{"id":17990126,"url":"https://github.com/rdbende/flake8-tkinter","last_synced_at":"2026-03-11T05:01:32.083Z","repository":{"id":36959739,"uuid":"505126112","full_name":"rdbende/flake8-tkinter","owner":"rdbende","description":"A flake8 plugin that helps you write better Tkinter code","archived":false,"fork":false,"pushed_at":"2023-06-27T20:26:42.000Z","size":99,"stargazers_count":7,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-20T20:48:22.037Z","etag":null,"topics":["code-quality","flake8","flake8-extension","flake8-plugin","linter","linter-plugin","tkinter"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rdbende.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-19T14:02:06.000Z","updated_at":"2025-08-27T12:31:12.000Z","dependencies_parsed_at":"2024-10-29T19:59:48.437Z","dependency_job_id":null,"html_url":"https://github.com/rdbende/flake8-tkinter","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/rdbende/flake8-tkinter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdbende%2Fflake8-tkinter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdbende%2Fflake8-tkinter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdbende%2Fflake8-tkinter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdbende%2Fflake8-tkinter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rdbende","download_url":"https://codeload.github.com/rdbende/flake8-tkinter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdbende%2Fflake8-tkinter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30372125,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"online","status_checked_at":"2026-03-11T02:00:07.027Z","response_time":84,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["code-quality","flake8","flake8-extension","flake8-plugin","linter","linter-plugin","tkinter"],"created_at":"2024-10-29T19:16:48.016Z","updated_at":"2026-03-11T05:01:32.052Z","avatar_url":"https://github.com/rdbende.png","language":"Python","readme":"# flake8-tkinter\n\nA [flake8](https://github.com/PyCQA/flake8) plugin that helps you write better Tkinter code\n\n_Project idea by [@insolor](https://github.com/insolor)_\n\n\n## Installation\n\n```\npip install flake8-tkinter\n```\n\n\n## List of warnings\n\nCommon mistakes\n- **`TK102`**: Using multiple mainloop calls is unnecessary. One call is perfectly enough. ([example](#tk102))\n- **`TK111`**: Calling `callback_handler()` instead of passing the reference for on-click or binding callback. ([example](#tk111))\n- **`TK112`**: Calling `callback_handler()` with arguments instead of passing the reference for on-click or binding callback. If you need to call `callback_handler` with arguments, use lambda or functools.partial. ([example](#tk112))\n- **`TK131`**: Assigning result of geometry manager call to a variable. ([example](#tk131))\n\nBest practices\n- **`TK141`**: Using bind without `add=True` will overwrite any existing bindings to this sequence on this widget. Either overwrite them explicitly with `add=False` or use `add=True` to keep existing bindings. ([example](#tk141))\n- **`TK142`**: Creating tag bindings in a loop can lead to memory leaks. Store the returned command names in a list to clean them up later. ([example](#tk142))\n- **`TK201`**: Using `from tkinter import *` is generally a bad practice and discouraged. Use `import tkinter as tk` or simply `import tkinter` instead. ([example](#tk201))\n- **`TK202`**: Using `from tkinter.ttk import *` is generally a bad practice and discouraged. Use `from tkinter import ttk` instead. ([example](#tk202))\n- **`TK211`**: Using `import tkinter.ttk as ttk` is pointless. Use `from tkinter import ttk` instead. ([example](#tk211))\n- **`TK221`**: Using tkinter.TRUE, tkinter.FALSE, etc. is pointless. Use an appropriate Python boolean instead. ([example](#tk221))\n- **`TK251`**: Using `tkinter.Message` widget. It's redundant since `tkinter.Label` provides the same functionality. ([example](#tk251))\n\nCode quality\n- **`TK304`**: Value for `add` in bind methods should be a boolean. ([example](#tk304))\n\nOpinionated warnings\n- **`TK504`**: Using a tkinter constant. Use a string literal instead (disabled by default). ([example](#tk504))\n\n## Examples\n\n### TK102\n```python\n# Bad\ndef foo():\n    top = tk.Toplevel()\n    ...\n    top.mainloop()\n\nroot.mainloop()\n\n# Good\ndef foo():\n    top = tk.Toplevel()\n    ...\n    \nroot.mainloop()\n```\n\n### TK111\n```python\n# Bad\ntk.Button(..., command=foo())\nbutton.config(command=bar())\nbutton.bind(\"\u003cButton-3\u003e\", baz())\n\n# Good\ntk.Button(..., command=foo)\nbutton.config(command=bar)\nbutton.bind(\"\u003cButton-3\u003e\", baz)\n```\n\n### TK112\n```python\n# Bad\ntk.Button(..., command=foo(arg, kwarg=...))\nbutton.config(command=bar(arg, kwarg=...))\nbutton.bind(\"\u003cButton-3\u003e\", baz(arg, kwarg=...))\n\n# Good\ntk.Button(..., command=lambda: foo(arg, kwarg=...))\nbutton.config(command=lambda: bar(arg, kwarg=...))\nbutton.bind(\"\u003cButton-3\u003e\", lambda e: baz(arg, kwarg=...))\n```\n\n### TK131\n```python\n# Bad\nbtn = tk.Button().grid()\n\n# Good\nbtn = tk.Button()\nbtn.grid()\n```\n\n### TK201\n```python\n# Bad\nfrom tkinter import *\n\n# Good\nimport tkinter\n# OR\nimport tkinter as tk\n```\n\n### TK202\n```python\n# Bad\nfrom tkinter.ttk import *\n\n# Good\nfrom tkinter import ttk\n```\n\n### TK211\n```python\n# Bad\nimport tkinter.ttk as ttk\n\n# Good\nfrom tkinter import ttk\n```\n\n### TK221\n```python\n# Bad\nw.pack(expand=tk.TRUE)\nw.pack(expand=tk.FALSE)\nw.pack(expand=tk.YES)\nw.pack(expand=tk.NO)\nw.pack(expand=tk.ON)\nw.pack(expand=tk.OFF)\n\n# Good\nw.pack(expand=True)\nw.pack(expand=False)\n```\n\n### TK141\n```python\n# Bad\nw.bind(\"\u003cButton-1\u003e\", foo)\n\n# Good\nw.bind(\"\u003cButton-1\u003e\", foo, add=True)\n# OR\nw.bind(\"\u003cButton-1\u003e\", foo, add=False)\n```\n\n### TK142\n```python\n# Bad\nfor index, foo in enumerate(foos):\n    w.tag_bind(f\"bar_{index}\", \"\u003cButton-1\u003e\", baz)\n    \n# Good\nfor index, foo in enumerate(foos):\n    tcl_command = w.tag_bind(f\"bar_{index}\", \"\u003cButton-1\u003e\", baz)\n    bindings.append(tcl_command)  # Clean them up later with `.deletecommand()`\n```\n\n### TK251\n_Yes, there's some minor diffrence in text wrapping, but that can be easily fixed\n```python\n# Bad\nw = tkinter.Message()\n\n# Good\nw = tkinter.Label()\n```\n\n### TK304\n```python\n# Bad\nw.bind(\"\u003cButton-1\u003e\", foo, add=\"+\")\n\n# Good\nw.bind(\"\u003cButton-1\u003e\", foo, add=True)\n```\n\n### TK504\n```python\n# Bad\nw.pack(side=tkinter.BOTTOM, fill=tkinter.BOTH)\n\n# Good\nw.pack(side=\"bottom\", fill=\"both\")\n```\n\n## Planned warnings\n\n- Common mistakes (TK101-TK179)\n    - `TK101`: Using multiple `tkinter.Tk` instances. Child windows must be created from `tkinter.Toplevel`.\n    - `TK103`: Suggest refactoring code that uses `.update()`, as it's usually pointless, [potentially harmful](https://wiki.tcl-lang.org/page/Update+considered+harmful), and considered a code smell.\n    - `TK113`: Callback handler should be a callable\n    - `TK121`: Using `time.sleep()` in tkinter code. Use `.after()` in some form instead.\n    - `TK122`: Using an infinite loop in callback handler. Propose to use recursive function with `.after()`.\n    - `TK???`: Suggest keeping reference of local `PhotoImage` instance to avoid GC.\n    - `TK151`: Don't use `w.setup()` directly. Use init args, or `w.configure()`.\n    - Extend `TK111` and `Tk112` to check in `w.after()` calls.\n\n- Cross platform (TK181-TK199)\n    - `TK181`: Using `\u003cMouseWheel\u003e` binding. It doesn't work on Linux with Tk 8.6 (use button4-5 instead)\n    - `TK182`: Using `\u003cShift-Tab\u003e` binding. It doesn't work on Linux (use `\u003cISO_Left_Tab\u003e` instead)\n    - `TK183`: Using `\u003cMenu\u003e` binding. It doesn't work on Windows (use `\u003cApp\u003e` instead)\n    - `TK184`: Binding to control or alt with constant values. It probably won't work on macOS.\n    - `TK191`: Not calling `wait_visibility` before `wm_attributes(\"-alpha\")`.\n    - `TK192`: Using `w.state(\"zoomed\")`. It throws an error on Linux (and on mac too?). Use `wm_attributes(\"-zoomed\", True)`\n\n- Best practices (TK201-TK299)\n    - `TK222`: Using `tk.N+tk.S+tk.E+tk.W` and combinations like that. Use `tk.NSEW`, or some other constant instead.\n    - `TK241`: Creating a widget without parent specified, and there is a container in the same scope.\n    - `TK252`: Using `tkinter.Menu` without `tearoff=False`\n    - `TK261`: Using subsequent `wm_attributes` calls. It can take value pairs.\n\n- Code quality (TK301-TK399)\n    - `TK301`: Suggest using more clear binding sequences, like `\u003cButton-1\u003e` instead of `\u003c1\u003e` and `\u003cKey-a\u003e` instead of `\u003ca\u003e`.\n    - `TK302`: Suggest using more clear `tkinter.Text` indexes, like `end - 1 chars` instead of `end-1c`.\n    - `TK303`: Using a float as `tkinter.Text` index. It works because how Tkinter translates Python objects to Tcl, but it shouldn't.\n\n- OO (TK401-TK499)\n    - `TK401`: Consider refactoring a huge app with OOP.\n    - `TK402`: Consider refactoring widget into separate class.\n    \n- Opinionated rules (TK501-TK599)\n    - `TK501`: Calling `mainloop()` on something other than the root window.\n    - `TK502`: Using things like `root.wm_title()`. Use `root.title()`. (But there should be exceptions, like `wm_attributes`, and instead warn on plain `attributes`)\n    - `TK503`: Using subscripting for widget cget and configure. Use `.cget()` and `.configure()` instead.\n\n\n## Development\n1. Clone the repo\n2. Set up a virtual environment, activate, and install `flake8` and `pytest` in it\n3. Run `pip install -e .` to install `flake8-tkinter` in editable format\n4. Run `python3 -m pytest` to test your changes\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdbende%2Fflake8-tkinter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frdbende%2Fflake8-tkinter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdbende%2Fflake8-tkinter/lists"}