{"id":13501758,"url":"https://github.com/marczellm/tkpf","last_synced_at":"2025-03-29T09:31:19.065Z","repository":{"id":134381731,"uuid":"96512077","full_name":"marczellm/tkpf","owner":"marczellm","description":"A GUI library for python/tkinter with two-way data binding","archived":false,"fork":false,"pushed_at":"2018-11-05T16:59:20.000Z","size":67,"stargazers_count":59,"open_issues_count":5,"forks_count":5,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-31T20:41:04.364Z","etag":null,"topics":["components","databinding","gui","python","tkinter"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marczellm.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-07T07:31:46.000Z","updated_at":"2024-01-15T18:46:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"3ad8d2df-38f3-4340-a933-faec03ca5587","html_url":"https://github.com/marczellm/tkpf","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marczellm%2Ftkpf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marczellm%2Ftkpf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marczellm%2Ftkpf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marczellm%2Ftkpf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marczellm","download_url":"https://codeload.github.com/marczellm/tkpf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246167249,"owners_count":20734380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["components","databinding","gui","python","tkinter"],"created_at":"2024-07-31T22:01:49.388Z","updated_at":"2025-03-29T09:31:18.799Z","avatar_url":"https://github.com/marczellm.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"`tkpf` is a library for building Tkinter GUIs in a paradigm\ninfluenced by WPF (Windows Presentation Foundation) and Angular.\n\nMain features are:\n\n- Declarative view hierarchy and layout in XML or YAML\n- One-way and two-way data binding\n- Componentization support\n\n![tkpf](https://user-images.githubusercontent.com/6771275/28272256-3ddb29a4-6b0b-11e7-8072-c962235d58d8.png)\n![x1](https://user-images.githubusercontent.com/6771275/28181423-d9c4bfd4-6808-11e7-955a-c0e18219e609.PNG)\n![mac](https://user-images.githubusercontent.com/6771275/48013133-e82a6e80-e123-11e8-92c0-b0a13467dc7e.png)\n\n\n# Tutorial\n## The layout template\nYou specify the GUI in XML or YAML format. Here is a simple example, `ExampleWindow.xml`:\n\n```xml\n\u003cFrame pack-anchor=\"nw\" pack-padx=\"5\"\u003e\n     \u003cLabelFrame text=\"Options\" pack-anchor=\"w\" pack-fill=\"x\"\u003e\n        \u003cRadiobutton pack-anchor=\"w\" variable=\"[(choice)]\" value=\"option1\"\u003e\n            Option 1\n        \u003c/Radiobutton\u003e        \n        \u003cRadiobutton pack-anchor=\"w\" variable=\"[(choice)]\" value=\"option2\"\u003e\n            Option 2\n        \u003c/Radiobutton\u003e\n        \u003cCombobox pack-anchor=\"w\" textvariable=\"(selected_suboption)\" values=\"[available_suboptions]\"\n                  name=\"combobox\"/\u003e\n        \u003cButton command=\"do_stuff\"\u003eDo stuff\u003c/Button\u003e\n    \u003c/LabelFrame\u003e\n\u003c/Frame\u003e\n```\n\nAs you can see, the XML tag names correspond to Tkinter widget class names, \nwhile XML attributes to their arguments.\n`tkpf` is opinionated in that it always uses the better looking `ttk` themed widgets\nwhen available.\n\nOptions such as `pack-anchor=\"nw\"` or `grid-row=\"0\"` specify the layout and will be passed to the appropriate \nTkinter layout manager method, in this case `.pack(anchor='nw')`.\n\nOn how to specify a GUI in YAML format, see `example/ExampleWindow.yaml`.\n\n## The view class\nYou display the GUI by creating a class derived from `Window` and showing it.\nYou have to supply the viewmodel in the constructor.\n\n```python\nclass ExampleWindow(Window):\n    template_path = 'ExampleWindow.xml'  # 'ExampleWindow.yaml' works too\n\nExampleWindow(ExampleModel()).show()\n```\nIf you want to keep the layout XML in this file inline, you can do that too:\n\n```python\nclass ExampleWindow(Window):\n    template = '\u003cLabel\u003eSome text\u003c/Label\u003e'\n```\n\nor \n\n```python\nclass ExampleWindow(Window):\n    template_yaml = '''\n    Label:\n      text: Some text\n    '''\n```\n\n\n\nSetting the window title:\n\n```python\n    def __init__(self, model):\n        super().__init__(model)\n        self.title = 'My application'\n```\n\nIn the view class you can write event handlers. Make that button work for example:\n\n```python\n    def do_stuff(self):\n        self.combobox.config(state='disabled')\n```\n\nThis 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:\n\n```python\n        self.named_widgets['combobox']\n```\n\n## The viewmodel class\n```python\n\nclass ExampleModel(ViewModel):\n    choice = Bindable(AutoProperty(1))\n    available_suboptions = Bindable(AutoProperty())\n    selected_suboption = Bindable(AutoProperty())\n    \n    def __init__(self):\n        super().__init__()\n        self.available_suboptions = ('suboption1', 'suboption2')\n```\n\n`AutoProperty` is similar to a C# autogenerated property. By default its datatype is `str`.\nYou can supply either a default value or a type to its constructor.\n\n`Bindable` is a decorator that you can use on any property to return a bindable property.\nIt has to know the data type of the wrapped property, so please specify its return type with a type annotation:\n```python\n@Bindable\n@property\ndef foo() -\u003e int:\n    return 1\n```\n\n`AutoProperty` takes care of that for you.\n\nOnly `int`, `bool`, `float` and `str` types are supported for Tkinter bindings, though for the combobox\n values, you can assign a Python tuple.\n \nIf an event handler is not found on the view class, it will be looked up on the viewmodel as well.\n \n## Data binding syntax\nIn the XML you specify the direction of data binding with a syntax similar to that of Angular:\n\n```\nvalues=\"[available_suboptions]\"\n```\nis a one-way binding from data source to view target,\n```\ntextvariable=\"(selected_suboption)\"\n```\nis a one-way binding from view target to data source, and\n```\nvariable=\"[(choice)]\"\n```\nis a two-way binding.\n\n## Using custom widgets\nYou can use custom widgets derived from Tkinter widget classes.\nThe only thing you have to do is call \n\n```python\nDirective.Registry.register(YourCustomWidgetClass)\n```\n\nbefore loading a template that uses it.\n\n## Components\n`tkpf` supports breaking up your GUI into components.\nHere's an example of a progressbar component with its own viewmodel:\n\n```python\nclass ProgressbarModel(ViewModel):\n    value = BindableProperty(0)\n    target = BindableProperty(100)\n    \n\nclass CustomProgressbar(Component):\n    template = '\u003cProgressbar name=\"progressbar\" variable=\"[value]\" maximum=\"[target]\"/\u003e'\n```\n\nand you can use it like this:\n```xml\n\u003cCustomProgressbar tkpf-model=\"[progressbar_model]\"/\u003e\n```\n\nwhere `progressbar_model` is an attribute or property on your main viewmodel.\n\nOn Python 3.5 you have to register your component before using it. On Python 3.6+ that is automatic.\n\n```python\nDirective.Registry.register(CustomProgressbar)\n```\n\nIt is planned that you will be able to add add custom, bindable attributes to components, like this:\n\n```python\nclass ExampleComponent(Component):\n    template = '\u003cLabel name=\"thelabel\"\u003eExample component text\u003c/Label\u003e'\n\n    def config(self, **kwargs):\n        self.thelabel.config(text=kwargs['custom-text'])\n```\n\nand then use them like this:\n```xml\n\u003cExampleComponent custom-text=\"Custom text\"/\u003e\n```\nThe only requirement is that the attribute name contains a hyphen.\n## Caveats\n`tkpf` only supports Python 3.5+.\n\nThis 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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarczellm%2Ftkpf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarczellm%2Ftkpf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarczellm%2Ftkpf/lists"}