Flake8 plugin to detect (too) common mistakes and bad practices in Tkinter projects
Project description
flake8-tkinter
A flake8 plugin that helps you detect (too) common mistakes and bad practices in you Tkinter project
Project idea by @insolor
Installation
pip install flake8-tkinter
List of warnings
Common mistakes
TK102
: Using multiple mainloop calls is unnecessary. One call is perfectly enough. (example)TK111
: Callingcallback_handler()
instead of passing the reference for on-click or binding callback. (example)TK112
: Callingcallback_handler()
with arguments instead of passing the reference for on-click or binding callback. If you need to callcallback_handler
with arguments, use lambda or functools.partial. (example)TK131
: Assigning result of geometry manager call to a variable. (example)
Best practices
TK201
: Usingfrom tkinter import *
is generally a bad practice and discouraged. Useimport tkinter as tk
or simplyimport tkinter
instead. (example)TK202
: Usingfrom tkinter.ttk import *
is generally a bad practice and discouraged. Usefrom tkinter import ttk
instead. (example)TK211
: Usingimport tkinter.ttk as ttk
is pointless. Usefrom tkinter import ttk
instead. (example)TK221
: Using tkinter.TRUE, tkinter.FALSE, etc. is pointless. Use an appropriate Python boolean instead. (example)TK231
: Using bind withoutadd=True
will overwrite any existing bindings to this sequence on this widget. Either overwrite them explicitly withadd=False
or useadd=True
to keep existing bindings. (example)TK232
: 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)TK251
: Usingtkinter.Message
widget. It's redundant sincetkinter.Label
provides the same functionality.. (example
Code quality
TK304
: Value foradd
in bind methods should be a boolean. (example)
Opinionated warnings
TK504
: Using a tkinter constant. Use a string literal instead (disabled by default). (example)
Examples
TK102
# Bad
def foo():
top = tk.Toplevel()
...
top.mainloop()
root.mainloop()
# Good
def foo():
top = tk.Toplevel()
...
root.mainloop()
TK111
# Bad
tk.Button(..., command=foo())
button.config(command=bar())
button.bind("<Button-3>", baz())
# Good
tk.Button(..., command=foo)
button.config(command=bar)
button.bind("<Button-3>", baz)
TK112
# Bad
tk.Button(..., command=foo(arg, kwarg=...))
button.config(command=bar(arg, kwarg=...))
button.bind("<Button-3>", baz(arg, kwarg=...))
# Good
tk.Button(..., command=lambda: foo(arg, kwarg=...))
button.config(command=lambda: bar(arg, kwarg=...))
button.bind("<Button-3>", lambda e: baz(arg, kwarg=...))
TK131
# Bad
btn = tk.Button().grid()
# Good
btn = tk.Button()
btn.grid()
TK201
# Bad
from tkinter import *
# Good
import tkinter
# OR
import tkinter as tk
TK202
# Bad
from tkinter.ttk import *
# Good
from tkinter import ttk
TK211
# Bad
import tkinter.ttk as ttk
# Good
from tkinter import ttk
TK221
# Bad
w.pack(expand=tk.TRUE)
w.pack(expand=tk.FALSE)
w.pack(expand=tk.YES)
w.pack(expand=tk.NO)
w.pack(expand=tk.ON)
w.pack(expand=tk.OFF)
# Good
w.pack(expand=True)
w.pack(expand=False)
TK231
Will be renamed to TK141 in v1.0.0
# Bad
w.bind("<Button-1>", foo)
# Good
w.bind("<Button-1>", foo, add=True)
# OR
w.bind("<Button-1>", foo, add=False)
TK232
Will be renamed to TK142 in v1.0.0
# Bad
for index, foo in enumerate(foos):
w.tag_bind(f"bar_{index}", "<Button-1>", baz)
# Good
for index, foo in enumerate(foos):
tcl_command = w.tag_bind(f"bar_{index}", "<Button-1>", baz)
bindings.append(tcl_command) # Clean them up later with `.deletecommand()`
TK251
Yes, there's some minor diffrence in text wrapping difference, but that can be adjusted
# Bad
w = tkinter.Message()
# Good
w = tkinter.Label()
TK304
# Bad
w.bind("<Button-1>", foo, add="+")
# Good
w.bind("<Button-1>", foo, add=True)
TK504
# Bad
w.pack(side=tkinter.BOTTOM, fill=tkinter.BOTH)
# Good
w.pack(side="bottom", fill="both")
Planned warnings
-
Common mistakes (TK101-TK179)
TK101
: Using multipletkinter.Tk
instances. Child windows must be created fromtkinter.Toplevel
.TK103
: Suggest refactoring code that uses.update()
, as it's usually pointless, potentially harmful, and considered a code smell.TK113
: Callback handler should be a callable (lol)TK121
: Usingtime.sleep()
in tkinter code. Use.after()
in some form instead.TK122
: Using an infinite loop in callback handler. Propose to use recursive function with.after()
.TK141
: Suggest keeping reference of localPhotoImage
instance to avoid GC.
-
Cross platform (TK181-TK199)
TK181
: Using<Shift-Tab>
binding. It doesn't work on Linux.
-
Best practices (TK201-TK299)
TK222
: Usingtk.N+tk.S+tk.E+tk.W
and combinations like that. Usetk.NSEW
, or some other constant instead.TK241
: Creating a widget without parent specified, and there is a container in the same scope.TK261
: Using subsequentwm_attributes
calls. It can take value pairs.
-
Code quality (TK301-TK399)
TK301
: Suggest using more clear binding sequences, like<Button-1>
instead of<1>
and<Key-a>
instead of<a>
.TK302
: Suggest using more cleartkinter.Text
indexes, likeend - 1 chars
instead ofend-1c
.TK303
: Using a float astkinter.Text
index. It works because how Tkinter translates Python objects to Tcl, but it shouldn't.
-
OO (TK401-TK499)
TK401
: Consider refactoring a huge app with OOP.TK402
: Consider refactoring widget into separate class.
-
Opinionated rules (TK501-TK599)
TK501
: Callingmainloop()
on something other than the root window.TK502
: Using things likeroot.wm_title()
. Useroot.title()
. (But there should be exceptions, likewm_attributes
)TK503
: Using subscripting for widget cget and configure. Use.cget()
and.configure()
instead.
Development
- Clone the repo
- Set up a virtual environment, activate, and install
flake8
andpytest
in it - Run
pip install -e .
to installflake8-tkinter
in editable format - Run
python3 -m pytest
to test your changes
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file flake8_tkinter-0.5.3.tar.gz
.
File metadata
- Download URL: flake8_tkinter-0.5.3.tar.gz
- Upload date:
- Size: 10.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.9.13
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c6610e8c6a559f67fff9e751e5dd091ad5fd73d13a6b83006f9ddbf29e547fc1 |
|
MD5 | ab23a1f79e7fa7b727f00139429b4b92 |
|
BLAKE2b-256 | 4a5bff8209bd2cfc7c5da89f3a56234932c8d3f213716e2ba0a837902cd7ffdc |