# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['guitk', 'guitk.widgets']

package_data = \
{'': ['*']}

setup_kwargs = {
    'name': 'guitk',
    'version': '0.2.0',
    'description': 'Python GUI Toolkit for Tk (guitk): simplify the layout and construction of tkinter graphical user interfaces in python.',
    'long_description': '<!-- DO NOT EDIT README.md, instead edit README.mdpp and process with MarkdownPP using build_readme.sh -->\n\n# Python GUI Toolkit for TK (guitk)\n\n## Synopsis\n\nguitk is an experiment to design a lightweight framework that simplifies creating simple GUIs with [tkinter](https://docs.python.org/3/library/tkinter.html).  This is very much early alpha stage but in constant development so check back frequently if this interests you or open an issue to start a conversation about what pain points this project could help you solve!\n\n## Code Example\n\n![hello.py example](examples/hello.py.png "Hello World example")\n\n```python\n"""Simple Hello World example using guitk """\n\nimport guitk\n\n\n# subclass guitk.Window as the starting point for your app\'s main window\nclass HelloWindow(guitk.Window):\n\n    def config(self):\n        # define a layout for the window\n        # you must have a class variable named `layout` or you\'ll get an empty window\n        self.layout = [\n            [guitk.Label("What\'s your name?")],\n            [guitk.Entry(key="name")],\n            [guitk.Button("Ok")],\n        ]\n        self.title = "Hello, World"\n\n    # define your event loop\n    # every guitk.Window will call self.handle_event to handle GUI events\n    # event is a guitk.Event object\n    def handle_event(self, event):\n        if event.key == "Ok":\n            print(f"Hello {self[\'name\'].value}")\n\n\n# run your event loop\nif __name__ == "__main__":\n    HelloWindow().run()\n```\n\n## Motivation\n\nI did not set out to create yet another python GUI framework -- there are already many of these, some of them quite good.  I wanted to create a simple GUI for [another python project](https://github.com/RhetTbull/osxphotos) and started down the path using [PySimpleGUI](https://github.com/PySimpleGUI/PySimpleGUI).  PySimpleGUI has an amazingly simple interface that allows creation of nice looking GUIs with just a few lines of code.  Unfortunately, after spending some time prototyping with PySimpleGUI, I discovered a few issues with PySimpleGUI (see below).  I evaluated several other GUI frameworks including [Toga](https://github.com/beeware/toga), [wxPython](https://www.wxpython.org/), [pyglet](https://github.com/pyglet/pyglet), [remi](https://github.com/dddomodossola/remi), and [tkinter](https://docs.python.org/3/library/tkinter.html).  None of these was as simple as PySimpleGUI and several had other issues, e.g. errors running under MacOS, steep learning curve, etc. \n\nI settled on using tkinter because it\'s included with python, well-supported on multiple platforms, and relatively light-weight.  However, I found tkinter took a bit too much boiler plate compared to PySimpleGUI and I missed the simplicity of PySimpleGUI\'s single event loop for quick prototyping.  \n\nguitk is my attempt to provide an event-loop interface to tkinter.  It is not intended to abstract away the tkinter interface and you\'ll need some knowledge of tkinter to use guitk.  I highly recommend Mark Roseman\'s excellent [Modern Tkinter for Busy Python Developers](https://tkdocs.com/book.html) book as a starting point.  guitk also provides a callback style interface if you prefer that over a single event-loop.\n\n## Installation\n\n* `git clone git@github.com:RhetTbull/guitk.git`\n* `cd guitk`\n* `python3 setup.py install`\n\nOnce this gets past the early alpha stage, I\'ll package for PyPI.\n\n## Anatomy of a guitk program \n\n![hello2.py example](examples/hello2.py.png "Hello World example")\n\n```python\n"""Hello World example using guitk """\n\nimport guitk\n\n\n# subclass guitk.Window as the starting point for your app\'s main window\nclass HelloWorld(guitk.Window):\n\n    # every Window class needs a config() method that\n    # defines the title and the layout (and optionally menu and other other settings)\n    def config(self):\n        # Your Window class needs to define a config() method that describes the layout, title, etc for your app\n        # config() is called by the Window class when the Window is created\n\n        # Title for the window\n        self.title = "Hello, World"\n\n        # Define the window\'s contents\n        # guitk.Label corresponds to a tkinter.ttk.Label, etc.\n        # optionally provide a unique key to each element to easily reference the element later\n        # layouts are lists of lists where each list corresponds to a row in the GUI\n        self.layout = [\n            [guitk.Label("What\'s your name?")],\n            [guitk.Entry(key="ENTRY_NAME", events=True)],\n            [guitk.Label("", width=40, key="OUTPUT", columnspan=2)],\n            [guitk.Button("Ok"), guitk.Button("Quit")],\n        ]\n\n    def setup(self):\n        # your setup() method is called by the Window class after config() just before the Window is displayed\n        # use this to initialize any internal state you need\n        # you do not need to provide a setup() method if no initialization is needed\n        print("setup")\n\n    def teardown(self):\n        # your teardown() method is called by the Window class after the Window is closed\n        # use this to clean up before the Window is destroyed\n        # you do not need to provide a teardown() method if no cleanup is needed\n        print("teardown")\n\n    # Interact with the Window using an event Loop\n    # every guitk.Window will call self.handle_event() to handle GUI events\n    # event is a guitk.Event object\n    def handle_event(self, event):\n        name = self["ENTRY_NAME"].value\n\n        if event.key == "Quit":\n            # a key wasn\'t supplied in `guitk.Button("Quit")` so guitk uses the name of the button\n            # value passed to quit will be returned by HelloWorld.run()\n            self.quit(name)\n\n        if event.key == "Ok":\n            # set the output Label to the value of the Entry box\n            # individual widgets can be accessed by their key; the window object acts as a dictionary of widgets\n            self["OUTPUT"].value = f"Hello {name}! Thanks for trying guitk."\n\n        if event.event_type == guitk.EventType.KeyRelease:\n            # events can be handled by event type as well as event key\n            print(event)\n\n\nif __name__ == "__main__":\n    # instantiate your Window class and run it\n    name = HelloWorld().run()\n    print(f"HelloWorld: {name}")\n```\n\nguitk supports both an event-loop style of app-development (very similar to how PySimpleGUI works) and also callbacks which are triggered by events.  The above example can be rewritten using a callback style:\n\n```python\n"""Hello World example using guitk, shows how to use callback style instead of event loop """\n\nimport guitk\n\n\n# subclass guitk.Window as the starting point for your app\'s main window\nclass HelloWorld(guitk.Window):\n\n    # every Window class needs a config() method that\n    # defines the title and the layout (and optionally menu and other other settings)\n    def config(self):\n        # Title for the window\n        self.title = "Hello, World"\n\n        # Define the window\'s contents\n        # guitk.Label corresponds to a tkinter.ttk.Label, etc.\n        # optionally provide a unique key to each element to easily reference the element later\n        # layouts are lists of lists where each list corresponds to a row in the GUI\n        # callbacks are functions that will be called when the user interact with the widget\n        # callbacks are specified with the `command` parameter\n        self.layout = [\n            [guitk.Label("What\'s your name?")],\n            [guitk.Entry(key="ENTRY_NAME", events=True, command=self.on_entry_changed)],\n            [guitk.Label("", width=40, key="OUTPUT", columnspan=2)],\n            [\n                guitk.Button("Ok", command=self.on_ok),\n                guitk.Button("Quit", command=self.on_quit),\n            ],\n        ]\n\n    def setup(self):\n        # this method is called after the window is created\n        # you can use it to set up any internal state you need\n\n        # bind_event_command() binds a callback command to a specific event,\n        # in this case, when user hits return in the entry field, the same command as hitting "Ok" will be called\n        # the widget objects can be accessed as self["KEY"] in setup() but not in config() as they aren\'t created until after config() is called\n        self["ENTRY_NAME"].bind_event("<Return>", command=self.on_ok)\n\n    def on_ok(self):\n        # the underlying guitk widgets are accessible as self["KEY"]\n        # the value of each widget is accessible as self["KEY"].value\n        name = self["ENTRY_NAME"].value\n        self["OUTPUT"].value = f"Hello {name}! Thanks for trying guitk."\n\n    def on_entry_changed(self):\n        print(self["ENTRY_NAME"].value)\n\n    def on_quit(self):\n        name = self["ENTRY_NAME"].value\n        # value passed to quit will be returned by HelloWorld.run()\n        self.quit(name)\n\n    def handle_event(self, event):\n        print(event)\n\n\nif __name__ == "__main__":\n    # instantiate your Window class and run it\n    name = HelloWorld().run()\n    print(f"HelloWorld: {name}")\n```\n\nguitk GUIs are created using a lists of lists where each element in the lists corresponds to a ttk or tk element.  This design pattern is borrowed from PySimpleGUI.\n\n![layout_lol.py example](examples/layouts_lol.py.png "Layout using lists of lists example")\n\n```python\n""" Example for guitk showing how to use lists of lists for creating GUI layout """\n\nimport guitk\n\n\nclass LayoutDemo(guitk.Window):\n    def config(self):\n        self.title = "Layouts are Lists of Lists"\n        self.layout = [\n            [guitk.Label("Row 1"), guitk.Label("What\'s your name?")],\n            [guitk.Label("Row 2"), guitk.Entry()],\n            [guitk.Label("Row 3"), guitk.Button("Ok")],\n        ]\n\n    def handle_event(self, event):\n        if event.key == "Ok":\n            print("Ok!")\n\n\nif __name__ == "__main__":\n    LayoutDemo().run()\n```\n\nBecause layouts are simply lists of lists, you can use python to create layouts programmatically, for example using list comprehensions.\n\n\n![layout2.py example](examples/layout2.py.png "Layout using list comprehensions, with tooltips!")\n\n```python\n""" Example for guitk showing how to use list comprehensions to create a GUI """\n\nimport guitk\n\n\nclass LayoutDemo(guitk.Window):\n    def config(self):\n        self.title = "List Comprehension"\n        # use list comprehension to generate 4x4 grid of buttons with tooltips\n        # use the tooltip named argument to add tooltip text to any element\n        self.layout = [\n            [\n                guitk.Button(\n                    f"{row}, {col}", padx=0, pady=0, tooltip=f"Tooltip: {row},{col}"\n                )\n                for col in range(4)\n            ]\n            for row in range(4)\n        ]\n\n    # Interact with the Window using an event Loop\n    def handle_event(self, event):\n        if event.event_type == guitk.EventType.ButtonPress:\n            # print the key for the button that was pressed\n            print(self[event.key].value)\n\n\nif __name__ == "__main__":\n    LayoutDemo().run()\n```\n\nA more complex example showing how to use the event handler to react to events and change the value of other GUI elements.\n\n![hello4.py example](examples/hello4.py.png "A more complex example showing how to use the event handler.")\n\n```python\n""" Another Hello World example for guitk showing how to use the event handler """\n\nimport guitk\nimport tkinter as tk\n\n\nclass HelloWorld(guitk.Window):\n\n    def config(self):\n        self.title = "Hello, World"\n\n        # Define the window\'s contents\n        # use variables to define rows to make your layout more readable\n        # use guitk.Frame to group sub-layouts into columns\n        label_frame = guitk.LabelFrame(\n            "Label Frame",\n            labelanchor=tk.N,\n            layout=[\n                [\n                    guitk.Frame(\n                        layout=[\n                            [guitk.Output(width=20, height=10)],\n                            [guitk.Label("Output", key="LABEL_OUTPUT", sticky=tk.S)],\n                        ]\n                    ),\n                    guitk.Frame(\n                        layout=[\n                            [None, guitk.Checkbutton("Upper case", key="CHECK_UPPER")],\n                            [None, guitk.Checkbutton("Green text", key="CHECK_GREEN")],\n                        ],\n                        sticky="n",\n                    ),\n                ]\n            ],\n        )\n\n        self.layout = [\n            [guitk.Label("What\'s your name?")],\n            [guitk.Entry(key="ENTRY_NAME")],\n            [guitk.Label("", width=40, key="OUTPUT")],\n            [label_frame],\n            [guitk.Button("Ok"), guitk.Button("Quit")],\n        ]\n\n        # you can define custom padding around widgets with padx, pady\n        # see https://tkdocs.com/tutorial/grid.html#padding\n        self.padx = 3\n        self.pady = 3\n\n    # Interact with the Window using an event Loop\n    def handle_event(self, event):\n        if event.key == "Quit":\n            self.quit()\n\n        if event.key == "Ok":\n            # set the output Label to the value of the Entry box\n            # the Window class acts like a dictionary for looking up guitk element objects by key\n            name = self["ENTRY_NAME"].value\n            print(f"Hello {name}")\n            self["OUTPUT"].value = f"Hello {name}! Thanks for trying guitk."\n\n        if event.key == "CHECK_UPPER" and self["CHECK_UPPER"].value:\n            # True if checked\n            # "Upper case" check button is checked, so make text upper case\n            self["OUTPUT"].value = self["OUTPUT"].value.upper()\n\n        if event.key == "CHECK_GREEN":\n            # change label text color to green if needed\n            # use .widget to access the underlying ttk element for each object\n            # tkinter is not abstracted -- you can easily use tkinter methods and properties if needed\n            if self["CHECK_GREEN"].value:\n                # checked\n                self["OUTPUT"].widget["foreground"] = "green"\n            else:\n                # not checked\n                self["OUTPUT"].widget["foreground"] = ""\n\n\nif __name__ == "__main__":\n    # add some padding around GUI elements to make it prettier\n    HelloWorld().run()\n```\n\nYou can create virtual events that fire after a time delay and these can be repeating.\n\n\n![bind_timer_event example](examples/bind_timer_event.py.png "Creating timed virtual events.")\n\n```python\n""" Example showing how to use bind_timer_event """\n\nimport time\nimport tkinter as tk\n\nimport guitk\n\n\nclass TimerWindow(guitk.Window):\n    def config(self):\n        self.title = "Timer Window"\n\n        self.layout = [\n            [guitk.Label("Press Start Timer to fire event after 2000 ms")],\n            [guitk.Label("", width=60, key="OUTPUT")],\n            [\n                guitk.Button("Start Timer"),\n                guitk.Button("Cancel Timer"),\n                guitk.Checkbutton("Repeat", key="REPEAT"),\n            ],\n        ]\n\n    def setup(self):\n        # store the id of the running timer so it can be cancelled\n        self.data = {"timer_id": None}\n\n    # Interact with the Window using an event Loop\n    def handle_event(self, event):\n        if event.key == "Quit":\n            self.quit()\n\n        if event.key == "Start Timer":\n            # this simple demo assumes only one timer running at a time\n            repeat = self["REPEAT"].value  # value of Repeat Checkbutton\n            self.data["timer_id"] = self.bind_timer_event(\n                2000, "<<MyTimer>>", repeat=repeat\n            )\n            self[\n                "OUTPUT"\n            ].value = f"Timer {self.data[\'timer_id\']} started at {time.time():.2f}"\n\n        if event.key == "<<MyTimer>>":\n            self["OUTPUT"].value = f"Timer went off at {time.time():.2f}!"\n\n        if event.key == "Cancel Timer":\n            self.cancel_timer_event(self.data["timer_id"])\n            self[\n                "OUTPUT"\n            ].value = f"Timer {self.data[\'timer_id\']} canceled at {time.time():.2f}"\n\n\nif __name__ == "__main__":\n    TimerWindow().run()\n```\n\nYou can access the underlying ttk widget, for example, to change style.  guitk also implements some additional widgets link `LinkLabel` which is a `ttk.Label()` that generates an event when clicked and changes mouse cursor to pointing hand (like a URL does).\n\n![LinkLabel example](examples/link.py.png "Using LinkLabel widget.")\n\n```python\n""" Demonstrates use of LinkLabel widget """\n\nimport guitk\nfrom tkinter import ttk\n\n\nclass ClickMe(guitk.Window):\n    def config(self):\n        self.title = "Click me!"\n\n        # you can pass tkinter.ttk options to the widgets\n        # e.g. width and anchor\n        self.layout = [\n            [\n                guitk.LinkLabel(\n                    "Click me!",\n                    width=20,\n                    anchor="center",\n                    key="CLICK_ME",\n                    underline_font=True,\n                )\n            ]\n        ]\n        self.padx = 20\n        self.pady = 20\n\n    def setup(self):\n        # setup gets called immediately before the window is shown\n\n        # configure the Click Me label to be blue\n        style = ttk.Style()\n        style.configure("Blue.TLabel", foreground="blue")\n\n        # use .widget to access the underlying tkinter ttk object, \n        # in this case, a tkinter.ttk.Label\n        self["CLICK_ME"].widget.configure(style="Blue.TLabel")\n\n    def handle_event(self, event):\n        print(event)\n\n\nif __name__ == "__main__":\n    ClickMe().run()\n```\n\n## Documentation\n\nNot much documentation at this point.  Take a look at the [examples](https://github.com/RhetTbull/guitk/tree/main/examples) directory for a number of self-documenting examples on use of various widgets.\n\n## Testing\n\nThere are currently no automated tests as I haven\'t figured out how to do these with tkinter.  You can run `python3 -m guitk` which opens a window with examples of all the widgets.  I currently use this for testing to ensure each widget still works but it\'s a manual process. \n\n## Contributors\n\nContributions welcome! If this project interests you, open an Issue or send a PR!\n\n## TODO\n\n- [x] Basic prototype\n- [x] Frame\n- [x] Label\n- [x] Entry\n- [x] Button\n- [x] Checkbutton\n- [x] Text\n- [x] ScrolledText\n- [ ] Other widgets\n- [x] Tooltips\n- [ ] Documentation\n- [ ] Add docstrings\n- [ ] Add typehints to public API\n- [ ] Tests\n\n## License\n\nMIT License with exception of `tooltips.py` which is licensed under the Python Software Foundation License Version 2.  Both are very permissive licenses.\n',
    'author': 'Rhet Turnbull',
    'author_email': 'rturnbull+git@gmail.com',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'None',
    'packages': packages,
    'package_data': package_data,
    'python_requires': '>=3.9,<4.0',
}


setup(**setup_kwargs)
