Giter VIP home page Giter VIP logo

tkpf's Introduction

tkpf is a library for building Tkinter GUIs in a paradigm influenced by WPF (Windows Presentation Foundation) and Angular.

Main features are:

  • Declarative view hierarchy and layout in XML or YAML
  • One-way and two-way data binding
  • Componentization support

tkpf x1 mac

Tutorial

The layout template

You specify the GUI in XML or YAML format. Here is a simple example, ExampleWindow.xml:

<Frame pack-anchor="nw" pack-padx="5">
     <LabelFrame text="Options" pack-anchor="w" pack-fill="x">
        <Radiobutton pack-anchor="w" variable="[(choice)]" value="option1">
            Option 1
        </Radiobutton>        
        <Radiobutton pack-anchor="w" variable="[(choice)]" value="option2">
            Option 2
        </Radiobutton>
        <Combobox pack-anchor="w" textvariable="(selected_suboption)" values="[available_suboptions]"
                  name="combobox"/>
        <Button command="do_stuff">Do stuff</Button>
    </LabelFrame>
</Frame>

As you can see, the XML tag names correspond to Tkinter widget class names, while XML attributes to their arguments. tkpf is opinionated in that it always uses the better looking ttk themed widgets when available.

Options such as pack-anchor="nw" or grid-row="0" specify the layout and will be passed to the appropriate Tkinter layout manager method, in this case .pack(anchor='nw').

On how to specify a GUI in YAML format, see example/ExampleWindow.yaml.

The view class

You display the GUI by creating a class derived from Window and showing it. You have to supply the viewmodel in the constructor.

class ExampleWindow(Window):
    template_path = 'ExampleWindow.xml'  # 'ExampleWindow.yaml' works too

ExampleWindow(ExampleModel()).show()

If you want to keep the layout XML in this file inline, you can do that too:

class ExampleWindow(Window):
    template = '<Label>Some text</Label>'

or

class ExampleWindow(Window):
    template_yaml = '''
    Label:
      text: Some text
    '''

Setting the window title:

    def __init__(self, model):
        super().__init__(model)
        self.title = 'My application'

In the view class you can write event handlers. Make that button work for example:

    def do_stuff(self):
        self.combobox.config(state='disabled')

This also shows how you can access widgets by name in methods of the view class. But if you prefer you can access them dynamically like this:

        self.named_widgets['combobox']

The viewmodel class

class ExampleModel(ViewModel):
    choice = Bindable(AutoProperty(1))
    available_suboptions = Bindable(AutoProperty())
    selected_suboption = Bindable(AutoProperty())
    
    def __init__(self):
        super().__init__()
        self.available_suboptions = ('suboption1', 'suboption2')

AutoProperty is similar to a C# autogenerated property. By default its datatype is str. You can supply either a default value or a type to its constructor.

Bindable is a decorator that you can use on any property to return a bindable property. It has to know the data type of the wrapped property, so please specify its return type with a type annotation:

@Bindable
@property
def foo() -> int:
    return 1

AutoProperty takes care of that for you.

Only int, bool, float and str types are supported for Tkinter bindings, though for the combobox values, you can assign a Python tuple.

If an event handler is not found on the view class, it will be looked up on the viewmodel as well.

Data binding syntax

In the XML you specify the direction of data binding with a syntax similar to that of Angular:

values="[available_suboptions]"

is a one-way binding from data source to view target,

textvariable="(selected_suboption)"

is a one-way binding from view target to data source, and

variable="[(choice)]"

is a two-way binding.

Using custom widgets

You can use custom widgets derived from Tkinter widget classes. The only thing you have to do is call

Directive.Registry.register(YourCustomWidgetClass)

before loading a template that uses it.

Components

tkpf supports breaking up your GUI into components. Here's an example of a progressbar component with its own viewmodel:

class ProgressbarModel(ViewModel):
    value = BindableProperty(0)
    target = BindableProperty(100)
    

class CustomProgressbar(Component):
    template = '<Progressbar name="progressbar" variable="[value]" maximum="[target]"/>'

and you can use it like this:

<CustomProgressbar tkpf-model="[progressbar_model]"/>

where progressbar_model is an attribute or property on your main viewmodel.

On Python 3.5 you have to register your component before using it. On Python 3.6+ that is automatic.

Directive.Registry.register(CustomProgressbar)

It is planned that you will be able to add add custom, bindable attributes to components, like this:

class ExampleComponent(Component):
    template = '<Label name="thelabel">Example component text</Label>'

    def config(self, **kwargs):
        self.thelabel.config(text=kwargs['custom-text'])

and then use them like this:

<ExampleComponent custom-text="Custom text"/>

The only requirement is that the attribute name contains a hyphen.

Caveats

tkpf only supports Python 3.5+.

This is a work in progress. Also my first attempt at creating a library. Look at the project issues to see what's not supported yet.

tkpf's People

Contributors

marczellm avatar tritium21 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tkpf's Issues

Support YAML, TOML, JSON

I like the idea, but not XML

All the attributes in the XML can be expressed in to YAML or JSON

Using xmltodict.unparse it is possible to transform YAML to dict then to XML.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.