Skip to content

Commit 7176908

Browse files
docs: Add QT MVC structure to style guide (#950)
* doc: first draft * doc: highlight key aspects of example * doc: address feedback * doc: add comment on making methods private by default * doc: fix typo * doc: fix typos
1 parent 29154ba commit 7176908

File tree

2 files changed

+83
-13
lines changed

2 files changed

+83
-13
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,7 @@ Mypy is also available as a VS Code [extension](https://marketplace.visualstudio
9393
- Run all tests by running `pytest tests/` in the repository root.
9494
9595
## Code Style
96-
97-
Most of the style guidelines can be checked, fixed, and enforced via Ruff. Older code may not be adhering to all of these guidelines, in which case _"do as I say, not as I do"..._
98-
99-
- Do your best to write clear, concise, and modular code.
100-
- Keep a maximum column with of no more than **100** characters.
101-
- Code comments should be used to help describe sections of code that can't speak for themselves.
102-
- Use [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) docstrings for any classes and functions you add.
103-
- If you're modifying an existing function that does _not_ have docstrings, you don't _have_ to add docstrings to it... but it would be pretty cool if you did ;)
104-
- Imports should be ordered alphabetically.
105-
- Lists of values should be ordered using their [natural sort order](https://en.wikipedia.org/wiki/Natural_sort_order).
106-
- Some files have their methods ordered alphabetically as well (i.e. [`thumb_renderer`](https://github.com/TagStudioDev/TagStudio/blob/main/src/tagstudio/qt/widgets/thumb_renderer.py)). If you're working in a file and notice this, please try and keep to the pattern.
107-
- When writing text for window titles or form titles, use "[Title Case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case)" capitalization. Your IDE may have a command to format this for you automatically, although some may incorrectly capitalize short prepositions. In a pinch you can use a website such as [capitalizemytitle.com](https://capitalizemytitle.com/) to check.
108-
- If it wasn't mentioned above, then stick to [**PEP-8**](https://peps.python.org/pep-0008/)!
96+
See the [Style Guide](/STYLE.md)
10997
11098
### Modules & Implementations
11199

STYLE.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Code Style
2+
3+
Most of the style guidelines can be checked, fixed, and enforced via Ruff. Older code may not be adhering to all of these guidelines, in which case _"do as I say, not as I do"..._
4+
5+
- Do your best to write clear, concise, and modular code.
6+
- This should include making methods private by default (e.g. `__method()`)
7+
- Methods should only be protected (e.g. `_method()`) or public (e.g. `method()`) when needed and warranted
8+
- Keep a maximum column width of no more than **100** characters.
9+
- Code comments should be used to help describe sections of code that can't speak for themselves.
10+
- Use [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) docstrings for any classes and functions you add.
11+
- If you're modifying an existing function that does _not_ have docstrings, you don't _have_ to add docstrings to it... but it would be pretty cool if you did ;)
12+
- Imports should be ordered alphabetically.
13+
- Lists of values should be ordered using their [natural sort order](https://en.wikipedia.org/wiki/Natural_sort_order).
14+
- Some files have their methods ordered alphabetically as well (i.e. [`thumb_renderer`](https://github.com/TagStudioDev/TagStudio/blob/main/src/tagstudio/qt/widgets/thumb_renderer.py)). If you're working in a file and notice this, please try and keep to the pattern.
15+
- When writing text for window titles or form titles, use "[Title Case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case)" capitalization. Your IDE may have a command to format this for you automatically, although some may incorrectly capitalize short prepositions. In a pinch you can use a website such as [capitalizemytitle.com](https://capitalizemytitle.com/) to check.
16+
- If it wasn't mentioned above, then stick to [**PEP-8**](https://peps.python.org/pep-0008/)!
17+
18+
## QT
19+
As of writing this section, the QT part of the code base is quite unstructured and the View and Controller parts are completely intermixed[^1]. This makes maintenance, fixes and general understanding of the code base quite challenging, because the interesting parts you are looking for are entangled in a bunch of repetitive UI setup code. To address this we are aiming to more strictly separate the view and controller aspects of the QT frontend.
20+
21+
The general structure of the QT code base should look like this:
22+
```
23+
qt
24+
├── controller
25+
│ ├── widgets
26+
│ │ └── preview_panel_controller.py
27+
│ └── main_window_controller.py
28+
├── view
29+
│ ├── widgets
30+
│ │ └── preview_panel_view.py
31+
│ └── main_window_view.py
32+
├── ts_qt.py
33+
└── mixed.py
34+
```
35+
36+
In this structure there are the `view` and `controller` sub-directories. They have the exact same structure and for every `<component>_view.py` there is a `<component>_controller.py` at the same location in the other subdirectory and vice versa.
37+
38+
Typically the classes should look like this:
39+
```py
40+
# my_cool_widget_view.py
41+
class MyCoolWidgetView(QWidget):
42+
def __init__(self):
43+
super().__init__()
44+
self.__button = QPushButton()
45+
self.__color_dropdown = QComboBox()
46+
# ...
47+
self.__connect_callbacks()
48+
49+
def __connect_callbacks(self):
50+
self.__button.clicked.connect(self._button_click_callback)
51+
self.__color_dropdown.currentIndexChanged.connect(
52+
lambda idx: self._color_dropdown_callback(self.__color_dropdown.itemData(idx))
53+
)
54+
55+
def _button_click_callback(self):
56+
raise NotImplementedError()
57+
```
58+
```py
59+
# my_cool_widget_controller.py
60+
class MyCoolWidget(MyCoolWidgetView):
61+
def __init__(self):
62+
super().__init__()
63+
64+
def _button_click_callback(self):
65+
print("Button was clicked!")
66+
67+
def _color_dropdown_callback(self, color: Color):
68+
print(f"The selected color is now: {color}")
69+
```
70+
71+
Observe the following key aspects of this example:
72+
- The Controller is just called `MyCoolWidget` instead of `MyCoolWidgetController` as it will be directly used by other code
73+
- The UI elements are in private variables
74+
- This enforces that the controller shouldn't directly access UI elements
75+
- Instead the view should provide a protected API (e.g. `_get_color()`) for things like setting/getting the value of a dropdown, etc.
76+
- Instead of `_get_color()` there could also be a `_color` method marked with `@property`
77+
- The callback methods are already defined as protected methods with NotImplementedErrors
78+
- Defines the interface the callbacks
79+
- Enforces that UI events be handled
80+
81+
82+
[^1]: For an explanation of the Model-View-Controller (MVC) Model, checkout this article: [MVC Framework Introduction](https://www.geeksforgeeks.org/mvc-framework-introduction/).

0 commit comments

Comments
 (0)