{ "info": { "author": "Akuli", "author_email": "akuviljanen17@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "# Teek\n\n[![Build Status](https://travis-ci.org/Akuli/teek.svg?branch=master)](https://travis-ci.org/Akuli/teek)\n[![Documentation Status](https://readthedocs.org/projects/teek/badge/?version=latest)](https://teek.readthedocs.io/en/latest/?badge=latest)\n[![Coverage Status](https://coveralls.io/repos/github/Akuli/teek/badge.svg?branch=master)](https://coveralls.io/github/Akuli/teek?branch=master)\n\nTeek is a pythonic and user-friendly alternative to tkinter. It doesn't come\nwith Python so you need to install it yourself, but it's nice and light-weight.\n\nDocumentation: https://teek.rtfd.org/\n\n\n## Teek is Pythonic\n\nIf you have worked with tkinter a lot, you know that it's kind of annoying.\nAlmost everything is represented as strings in Tcl. Tkinter is dumb and it\ndoesn't try to do things like they would be usually done in Python; instead,\ntkinter users need to deal with many inconveniences themselves. On the other\nhand, Teek is *pythonic*; it does things like they are best done in Python,\nnot how they are done in Tcl.\n\n\n### Ttk\n\nNever heard of Ttk before? Shame on you. Ttk is the new way to write GUIs in\nTk, and you should be already using it in tkinter. Ttk GUIs look a *lot* better\nthan non-Ttk GUIs on most platforms. For example, this GUI has a Ttk button and\na non-Ttk button. Guess which is which:\n\n[comment]: # (this must be a full url to make it work in pypi description)\n\n![good and bad button](https://github.com/Akuli/teek/raw/master/tk-ttk.png)\n\nThe problem is that Tk's windows (in tkinter, `tkinter.Toplevel` and\n`tkinter.Tk`) are *not* Ttk widgets. If you add Ttk widgets into them, the GUI\nlooks messy on some systems, like my linux system with MATE desktop. The\nsolution is to add a big Ttk frame that fills the window, and then add all\nwidgets into that frame.\n\n**Tkinter:**\n\n```python3\n# this is a well-done hello world, and tbh, most people don't use tkinter \"well\"\nimport tkinter\nfrom tkinter import ttk\n\nroot = tkinter.Tk()\nbig_frame = ttk.Frame(root)\nbig_frame.pack(fill='both', expand=True) # make sure it fills the root window\nttk.Label(big_frame, text=\"Hello World!\").pack()\nroot.mainloop()\n```\n\n**Teek:**\n\n```python3\nimport teek\n\nwindow = teek.Window(\"Hello\")\nteek.Label(window, \"Hello World!\").pack()\nwindow.on_delete_window.connect(teek.quit)\nteek.run()\n```\n\nAll teek widgets are Ttk, so you don't need to do a separate import to use\nttk widgets. Also, when you create a teek `Window`, the big ttk frame is\ncreated and packed automatically for you, and you don't need to think about it\nat all; you just create a `Window` and add stuff into it.\n\n\n### Threads\n\nHere `time.sleep`s represent blocking things. In real life you could e.g. do\nnetwork requests, run a subprocess or perform CPU-sensitive computations in the\nthread.\n\n**Tkinter:**\n\n```python3\nimport queue\nimport threading\nimport time\nimport tkinter\n\nroot = tkinter.Tk()\nroot.title(\"Thread Demo\")\ntext = tkinter.Text(root)\ntext.pack()\n\nmessage_queue = queue.Queue()\n\ndef queue_poller():\n while True:\n try:\n message = message_queue.get(block=False)\n except queue.Empty:\n break\n text.insert('end', message)\n\n root.after(50, queue_poller)\n\ndef thread_target():\n message_queue.put('doing things...\\n')\n time.sleep(1)\n message_queue.put('doing more things...\\n')\n time.sleep(2)\n message_queue.put('done')\n\nthreading.Thread(target=thread_target).start()\nqueue_poller()\nroot.mainloop()\n```\n\n**Teek:**\n\n```python3\nimport threading\nimport time\nimport teek\n\ntext = teek.Text(teek.Window(\"Thread Demo\"))\ntext.pack()\n\ndef thread_target():\n text.insert(text.end, 'doing things...\\n')\n time.sleep(1)\n text.insert(text.end, 'doing more things...\\n')\n time.sleep(2)\n text.insert(text.end, 'done')\n\nteek.init_threads()\nthreading.Thread(target=thread_target).start()\nwindow.on_delete_window.connect(teek.quit)\nteek.run()\n```\n\nThis is not a joke. Using threads with tkinter is a horrible mess, but teek\nworks with threads nicely. All you need is `teek.init_threads()`, and then you\ncan do teek things from threads. See [concurrency docs] for details.\n\n[concurrency docs]: https://teek.readthedocs.io/en/latest/concurrency.html\n\n\n### Debuggability\n\n**Tkinter:**\n\n```python3\nlabel = ttk.Label(some_widget, text=\"hello world\")\nprint(label) # prints something like '.140269016152776', which is confusing\nprint(repr(label)) # somewhat better: \n```\n\n**Teek:**\n\n```python3\nlabel = teek.Label(some_widget, \"hello world\")\nprint(label) # \n```\n\n\n### Text Widget Indexes\n\nThe 4th character of the 3rd line of a text widget is the string `'3.4'` in\nTcl and tkinter. This is not only confusing because `3.4` looks like a float\neven though treating it as a float messes things up, but this makes code look\nmessy.\n\n**Tkinter:**\n\n```python3\n# figure out where the cursor is\nline, column = textwidget.index('insert').split('.')\nline = int(line)\ncolumn = int(column)\n\n# same thing, more concise\nline, column = map(int, textwidget.index('insert').split('.'))\n```\n\n**Teek:**\n\n```python3\nline, column = textwidget.marks['insert']\n```\n\n`textwidget.marks` is a dictionary-like object with mark names as keys and text\nindex namedtuples as values. Teek represents text indexes as namedtuples\nthat have `line` and `column` attributes, which is useful if you only need the\nline. In tkinter, you need to parse the `'line.column'` string with\n`.split('.')` and take the first element of the split result.\n\n**Tkinter:**\n\n```python3\ncursor_lineno = int(textwidget.index('insert').split('.')[0])\n```\n\n**Teek:**\n\n```python3\ncursor_lineno = textwidget.marks['insert'].line\n```\n\nIn tkinter you also need to construct the `'line.column'` strings yourself, but\nin teek you can use `(line, column)` tuples.\n\n**Tkinter:**\n\n```python3\ntextwidget.mark_set('insert', '{}.{}'.format(new_cursor_line, new_cursor_column))\n```\n\n**Teek:**\n\n```python3\ntextwidget.marks['insert'] = (new_cursor_line, new_cursor_column)\n```\n\nTcl uses strings like `3.4 + 5 chars` to denote the position that is 5\ncharacters after the position `3.4`. Teek's text position namedtuples have a\npythonic `forward()` method that returns a new text position.\n\n**Tkinter:**\n\n```python3\n# textwidget.index always returns the position as 'line.column'\nnew_position = textwidget.index('{}.{} + 5 chars - 1 line'.format(line, column))\n# now new_position is a string, and you may need to parse it back to\n# separate line and column\n```\n\n**Teek:**\n\n```python3\nnew_position = textwidget.index(line, column).forward(chars=5).back(lines=1)\n# new_position is now a text position namedtuple\n```\n\n\n### Timeouts\n\nIn tkinter, `any_widget.after(1000, func)` runs `func()` after 1 second, and\nthe `any_widget` can be any tkinter widget. That's right, you need a widget for\nscheduling timeouts. This can be a problem in library code. But what if during\nthat 1 second of waiting time, you decide that you don't want to run the\ntimeout after all? You can cancel the timeout, but as usual, teek makes it\neasier.\n\n**Tkinter:**\n\n```python3\nwidget = get_some_widget_from_somewhere()\ntimeout_id = widget.after(1000, my_function)\n...\n# debugging\nprint(timeout_id) # prints 'after#0'... very useful, eh??\n...\nif we_actually_dont_want_to_timeout():\n widget.after_cancel(timeout_id)\n print(timeout_id) # still prints 'after#0'\n```\n\n**Teek:**\n\n```python3\ntimeout_object = teek.after(1000, my_function)\n...\n# debugging\nprint(timeout_object) # prints \n...\nif we_actually_dont_want_to_timeout():\n timeout.cancel()\n print(timeout) # prints \n```\n\n\n## Developing teek\n\nThis section contains the commands I use when working on teek. If you\nuse windows, replace `python3` with `py`.\n\n- `python3 -m pip install --user sphinx pytest pytest-cov flit` installs\n everything you need for developing teek.\n- `python3 -m pytest` runs tests (they are in the `tests` subdirectory). It is\n normal to get lots of tiny windows on the screen while running the tests. I\n use these pytest options:\n - `--skipslow` makes the tests run faster by skipping tests that are\n decorated with `@pytest.mark.slow`.\n - `--durations=10` prints the list of 10 slowest tests at the end of the\n test run. This is a good way to figure out which tests to mark slow.\n - `--cov=teek` runs the tests under coverage. Run\n `python3 -m coverage html` and open `htmlcov/index.html` to view the\n results. Coverage results from travis builds go to [coveralls].\n- `cd docs` followed by `py -m sphinx . _build` builds documentation locally.\n You can view it by opening `docs/_build/index.html` in your browser.\n [readthedocs builds the docs] when you push to master, but it's best to make\n sure that everything's fine first.\n- Sphinx seems to only build parts of the documentation if you change some of\n it, but sometimes it doesn't detect your changes. Run `cd docs` followed by\n `rm -r _build` to make it build everything next time.\n- I don't usually lint the files on my system. I push to GitHub (to any\n branch), and if [the travis build] fails, I know I did something badly. If\n you want to lint things yourself, find the correct command from\n `.travis.yml`.\n- `flit publish` uploads to PyPI. You can ask me to run this after I have\n merged something to master.\n\n[readthedocs builds the docs]: https://readthedocs.org/projects/teek/builds/\n[the travis build]: https://travis-ci.org/Akuli/teek\n[coveralls]: https://coveralls.io/github/Akuli/teek\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Akuli/teek", "keywords": "pythonic tk tcl tkinter gui beginner", "license": "", "maintainer": "", "maintainer_email": "", "name": "teek", "package_url": "https://pypi.org/project/teek/", "platform": "", "project_url": "https://pypi.org/project/teek/", "project_urls": { "Homepage": "https://github.com/Akuli/teek" }, "release_url": "https://pypi.org/project/teek/0.5/", "requires_dist": [ "pillow; extra == \"image_loader\"", "reportlab; extra == \"image_loader\"", "svglib; extra == \"image_loader\"", "lxml; extra == \"image_loader\"", "beautifulsoup4; extra == \"soup_viewer\"", "lxml; extra == \"soup_viewer\"" ], "requires_python": ">=3.4", "summary": "Teek is a pythonic alternative to tkinter.", "version": "0.5" }, "last_serial": 5262571, "releases": { "0.3": [ { "comment_text": "", "digests": { "md5": "08eebdca0640de5f3db3de2504cc53c9", "sha256": "dc77fa6e6fdfa1fb9c43d199de58166d0c3b1f39b96c6f2b5a3595ad7ee61198" }, "downloads": -1, "filename": "teek-0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "08eebdca0640de5f3db3de2504cc53c9", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 181174, "upload_time": "2019-01-13T19:53:06", "url": "https://files.pythonhosted.org/packages/d5/33/d67e6e652f7ffae84f9900ed3954fd61748a225e25a352b426b98fda3a78/teek-0.3-py3-none-any.whl" } ], "0.4": [ { "comment_text": "", "digests": { "md5": "cc43e755a188b291122738bec783c02c", "sha256": "80d5074d793504482167d42f91137d3d434edbaba22cabdd659dbfb6b32eb110" }, "downloads": -1, "filename": "teek-0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "cc43e755a188b291122738bec783c02c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 198947, "upload_time": "2019-03-11T21:00:44", "url": "https://files.pythonhosted.org/packages/09/f7/7a5a851188590f0786aa48c62cf2107d0a1e2f7a4ed738f471b1f9b3a49a/teek-0.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a5728ce78f6d41df6367d00934812214", "sha256": "ecbf8eb329c03be2f8f04653454821e0b935b3c474adcbbfaba13a9cb147c3c9" }, "downloads": -1, "filename": "teek-0.4.tar.gz", "has_sig": false, "md5_digest": "a5728ce78f6d41df6367d00934812214", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 154350, "upload_time": "2019-03-11T21:01:26", "url": "https://files.pythonhosted.org/packages/2f/83/f236703c23858b82e2552cc18b325604f2a63f9335d00a1e8de8d6767c48/teek-0.4.tar.gz" } ], "0.5": [ { "comment_text": "", "digests": { "md5": "49fd38821a94fe6260985f443522ffc0", "sha256": "273cb18d6cc4cddd448bfd680a7cc3482fb08725325d0bb7339f0c101ec87daf" }, "downloads": -1, "filename": "teek-0.5-py3-none-any.whl", "has_sig": false, "md5_digest": "49fd38821a94fe6260985f443522ffc0", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 207481, "upload_time": "2019-05-13T14:26:42", "url": "https://files.pythonhosted.org/packages/83/0a/7598d39a5b21b19f3f5ba37b1d79bf603676107bf6f4836a0cf8ae797a65/teek-0.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "344248b7af74b18dc378cd0d320ed6ba", "sha256": "8750f0cc875065648f1fafb96bc21b97bdacab90c8777b21165c6f0a91074dfe" }, "downloads": -1, "filename": "teek-0.5.tar.gz", "has_sig": false, "md5_digest": "344248b7af74b18dc378cd0d320ed6ba", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 158558, "upload_time": "2019-05-13T14:26:55", "url": "https://files.pythonhosted.org/packages/73/47/d91d56ca961ce70f7ebe39ea9d3395bc6b8f9a7af56795b0cab62bbb7b53/teek-0.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "49fd38821a94fe6260985f443522ffc0", "sha256": "273cb18d6cc4cddd448bfd680a7cc3482fb08725325d0bb7339f0c101ec87daf" }, "downloads": -1, "filename": "teek-0.5-py3-none-any.whl", "has_sig": false, "md5_digest": "49fd38821a94fe6260985f443522ffc0", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 207481, "upload_time": "2019-05-13T14:26:42", "url": "https://files.pythonhosted.org/packages/83/0a/7598d39a5b21b19f3f5ba37b1d79bf603676107bf6f4836a0cf8ae797a65/teek-0.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "344248b7af74b18dc378cd0d320ed6ba", "sha256": "8750f0cc875065648f1fafb96bc21b97bdacab90c8777b21165c6f0a91074dfe" }, "downloads": -1, "filename": "teek-0.5.tar.gz", "has_sig": false, "md5_digest": "344248b7af74b18dc378cd0d320ed6ba", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 158558, "upload_time": "2019-05-13T14:26:55", "url": "https://files.pythonhosted.org/packages/73/47/d91d56ca961ce70f7ebe39ea9d3395bc6b8f9a7af56795b0cab62bbb7b53/teek-0.5.tar.gz" } ] }