summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2025-12-23 21:24:30 +0100
committerSimeon Simeonov2025-12-23 21:24:30 +0100
commit3faee283f3e56f046e106fff5330a756a949618d (patch)
tree3e5fbe91f70030f577b5012dd85c5a1a2ba4ccb5
parenta29c4509a316c0c77450b6fa74b96f036b0f0821 (diff)
Improve layout and add ruff configuration
-rw-r--r--.ruff.toml98
-rw-r--r--src/scorecounter/app.py100
2 files changed, 164 insertions, 34 deletions
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 0000000..956737c
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,98 @@
1cache-dir = "~/.cache/ruff"
2indent-width = 4
3line-length = 79
4target-version = "py314"
5
6[lint]
7select = ["ALL", "D101", "D102", "D103", "D104"]
8ignore = [
9 "ANN",
10 "BLE001",
11 "COM812",
12 "D",
13 "EM101", # Exception must not use a string literal, assign to variable first
14 "EM102", # Exception must not use an f-string literal, assign to variable first
15 "ERA001",
16 #"FBT001",
17 #"FBT002",
18 #"INP001",
19 #"ISC001",
20 #"N802",
21 #"N806",
22 #"PLR2004",
23 # "PTH111",
24 # "PTH123",
25 "RUF012",
26 #"RUF013",
27 #"S101",
28 "S308",
29 #"T201",
30 "TRY003",
31 "TRY300",
32 #"UP020"
33]
34
35# C901 - `outside_temperature` is too complex (12 > 10)
36# D101 - Missing docstring in public class
37# D102 - Missing docstring in public method
38# D200 - One-line docstring should fit on one line
39# D203 - 1 blank line required before class docstring
40# D205 - 1 blank line required between summary line and description
41# D403 - First word of the first line should be capitalized: `str` -> `Str`
42# ERA001 - Found commented-out code
43# FBT001 - Boolean-typed positional argument in function definition
44# FBT002 - Boolean default positional argument in function definition
45# INP001 - File `beinc_weechat.py` is part of an implicit namespace package. Add an `__init__.py`
46# N802 - Function name `do_GET` should be lowercase
47# N806 - Variable `POST_data` in function should be lowercase
48# PTH111 - `os.path.expanduser()` should be replaced by `Path.expanduser()`
49# PTH123 - `open()` should be replaced by `Path.open()`
50# PLR0912 - Too many branches (13 > 12)
51# PLR0913 - Too many arguments in function definition (6 > 5)
52# PLR2004 - Magic value used in comparison, consider replacing `200` with a constant variable
53# RUF012 - Mutable class attributes should be annotated with `typing.ClassVar`
54# RUF013 - PEP 484 prohibits implicit `Optional`
55# S101 - Use of `assert` detected
56# S308 - Use of `mark_safe` may expose cross-site scripting vulnerabilities
57# S603 - `subprocess` call: check for execution of untrusted input
58# T201 - `print` found
59# TRY003 - Avoid specifying long messages outside the exception class
60# TRY300 - Consider moving this statement to an `else` block
61# UP020 - Use builtin `open`
62
63# Allow fix for all enabled rules (when `--fix`) is provided.
64fixable = ["ALL"]
65unfixable = []
66
67[format]
68# Like Black, use double quotes for strings.
69quote-style = "single"
70
71# Like Black, indent with spaces, rather than tabs.
72indent-style = "space"
73
74# Like Black, respect magic trailing commas.
75skip-magic-trailing-comma = true
76
77# Like Black, automatically detect the appropriate line ending.
78line-ending = "lf"
79
80# Enable auto-formatting of code examples in docstrings. Markdown,
81# reStructuredText code/literal blocks and doctests are all supported.
82#
83# This is currently disabled by default, but it is planned for this
84# to be opt-out in the future.
85docstring-code-format = false
86
87# Set the line length limit used when formatting code snippets in
88# docstrings.
89#
90# This only has an effect when the `docstring-code-format` setting is
91# enabled.
92docstring-code-line-length = "dynamic"
93
94[lint.flake8-quotes]
95inline-quotes = "single"
96
97[lint.isort]
98split-on-trailing-comma = false
diff --git a/src/scorecounter/app.py b/src/scorecounter/app.py
index cf4bc1d..26a0c74 100644
--- a/src/scorecounter/app.py
+++ b/src/scorecounter/app.py
@@ -1,7 +1,7 @@
1"""Simple score counter for two teams""" 1"""Simple score counter for two teams"""
2 2
3import toga 3import toga
4from toga.style.pack import COLUMN, ROW 4from toga.constants import COLUMN, ROW
5 5
6 6
7class ScoreCounter(toga.App): 7class ScoreCounter(toga.App):
@@ -9,65 +9,97 @@ class ScoreCounter(toga.App):
9 9
10 def startup(self): 10 def startup(self):
11 """startup entry point""" 11 """startup entry point"""
12 self.score_team1 = 0 12 self._score_team1 = 0
13 self.score_team2 = 0 13 self._score_team2 = 0
14 14
15 main_box = toga.Box(direction=COLUMN) 15 self.main_window = toga.MainWindow(
16 title=self.formal_name, on_resize=self.cb_main_window_resize
17 )
16 18
17 self.score_label1 = toga.Label( 19 self._score_label1 = toga.Label(
18 f'Team1: {self.score_team1}', margin=(0, 5), color='red' 20 f'Team1: {self._score_team1}',
21 margin=(0, 5),
22 color='red',
23 font_weight='bold',
24 font_size=24,
25 )
26 self._score_label2 = toga.Label(
27 f'Team2: {self._score_team2}',
28 margin=(0, 5),
29 color='blue',
30 font_weight='bold',
31 font_size=24,
19 ) 32 )
20 self.score_label2 = toga.Label( 33 separator_label = toga.Label('', flex=1)
21 f'Team2: {self.score_team2}', margin=(0, 5), color='blue' 34 score_box = toga.Box(
35 direction=ROW,
36 margin=5,
37 children=[self._score_label1, separator_label, self._score_label2],
22 ) 38 )
23 self.separator_label = toga.Label(' - ', margin=(0, 10))
24 39
25 score_box = toga.Box(direction=ROW, margin=5) 40 # empty box for consuming vertical space
26 score_box.add(self.score_label1) 41 empty_box = toga.Box(flex=1)
27 score_box.add(self.separator_label)
28 score_box.add(self.score_label2)
29 42
30 button_clear = toga.Button( 43 self._button_team1 = toga.Button(
31 'Clear', on_press=self.cb_clear, margin=(20, 5, 5) 44 'Team 1 (+1)',
45 on_press=self.cb_score_team1,
46 background_color='red',
47 flex=1,
48 font_size=18,
49 color='white',
32 ) 50 )
33 button_team1 = toga.Button( 51 self._button_team2 = toga.Button(
34 'Team 1 (+1)', on_press=self.cb_score_team1, margin=(20, 5, 5) 52 'Team 2 (+1)',
53 on_press=self.cb_score_team2,
54 background_color='blue',
55 flex=1,
56 font_size=18,
57 color='white',
35 ) 58 )
36 button_team1.style.background_color = 'red' 59 button_box = toga.Box(
37 button_team2 = toga.Button( 60 direction=COLUMN,
38 'Team 2 (+1)', on_press=self.cb_score_team2, margin=5 61 children=[self._button_team1, self._button_team2],
62 margin=(20, 5, 0),
63 )
64
65 self.main_window.content = toga.Box(
66 direction=COLUMN, children=[score_box, empty_box, button_box]
39 ) 67 )
40 button_team2.style.background_color = 'blue' 68 self._button_team1.style.height = int(self.main_window.size.height / 5)
69 self._button_team2.style.height = int(self.main_window.size.height / 5)
70
71 command_clear = toga.Command(
72 self.cb_clear, text='Clear', group=toga.Group('Scoreboard')
73 )
74 self.commands.add(command_clear)
41 75
42 main_box.add(score_box)
43 main_box.add(button_team1)
44 main_box.add(button_team2)
45 main_box.add(button_clear)
46
47 self.main_window = toga.MainWindow(title=self.formal_name)
48 self.main_window.content = main_box
49 self.main_window.show() 76 self.main_window.show()
50 77
51 def cb_clear(self, _widget): 78 def cb_clear(self, _widget):
52 """Clear on press callback""" 79 """Clear on press callback"""
53 self.score_team1 = 0 80 self._score_team1 = 0
54 self.score_team2 = 0 81 self._score_team2 = 0
55 self.refresh() 82 self.refresh()
56 83
57 def cb_score_team1(self, _widget): 84 def cb_score_team1(self, _widget):
58 """Team1 on press callback""" 85 """Team1 on press callback"""
59 self.score_team1 += 1 86 self._score_team1 += 1
60 self.refresh() 87 self.refresh()
61 88
62 def cb_score_team2(self, _widget): 89 def cb_score_team2(self, _widget):
63 """Team 2 on press callback""" 90 """Team 2 on press callback"""
64 self.score_team2 += 1 91 self._score_team2 += 1
65 self.refresh() 92 self.refresh()
66 93
94 def cb_main_window_resize(self, widget) -> None:
95 """On main window resize callback"""
96 self._button_team1.style.height = int(widget.size.height / 5)
97 self._button_team2.style.height = int(widget.size.height / 5)
98
67 def refresh(self): 99 def refresh(self):
68 """Refreshes the score labels""" 100 """Refreshes the score labels"""
69 self.score_label1.text = f'Team1: {self.score_team1}' 101 self._score_label1.text = f'Team1: {self._score_team1}'
70 self.score_label2.text = f'Team2: {self.score_team2}' 102 self._score_label2.text = f'Team2: {self._score_team2}'
71 103
72 104
73def main(): 105def main():