* refactor: basic split * fix: renaming and usage test didn't work for the tests * fix: tests * refactor: restructuring * refactor: further separation and lots of related changes * refactor: remove last reference to a widget from controller * refactor: address todo * fix: failing tests and mypy compaint * refactor: move control logic to controller * refactor: more readable button style * refactor: move existing code to view * refactor: move existing code to controller * fix: imports * refactor: make methods private by default * refactor: privatise fields * refactor: reduce code duplication * refactor: consolidate and sort display methods * refactor: remove needless setting of delete action text * refactor: extract control logic from _display_file * refactor: use MediaType for __switch_preview * fix: import in preview_panel_view.py * refactor: remove unnecessary wrapper on view side * refactor: move image data retrieval to control side * refactor: move audio / video specific code to the respective method * refactor: remove superfluos methods * refactor: this and that * refactor: use proper type instead of dict for file stats * refactor: extract gif parsing to controller * refactor: extract video size extraction to controller * doc: add rule of thumb to Qt MVC Style Guide * doc: change rule of thumb from note to tip --------- Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
4.8 KiB
Code Style
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"...
- Do your best to write clear, concise, and modular code.
- This should include making methods private by default (e.g.
__method()) - Methods should only be protected (e.g.
_method()) or public (e.g.method()) when needed and warranted
- This should include making methods private by default (e.g.
- Keep a maximum column width of no more than 100 characters.
- Code comments should be used to help describe sections of code that can't speak for themselves.
- Use Google style docstrings for any classes and functions you add.
- 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 ;)
- Imports should be ordered alphabetically.
- Lists of values should be ordered using their natural sort order.
- Some files have their methods ordered alphabetically as well (i.e.
thumb_renderer). If you're working in a file and notice this, please try and keep to the pattern.
- Some files have their methods ordered alphabetically as well (i.e.
- When writing text for window titles or form titles, use "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 to check.
- If it wasn't mentioned above, then stick to PEP-8!
QT
As of writing this section, the QT part of the code base is quite unstructured and the View and Controller parts are completely intermixed1 . 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.
The general structure of the QT code base should look like this:
qt
├── controller
│ ├── widgets
│ │ └── preview_panel_controller.py
│ └── main_window_controller.py
├── view
│ ├── widgets
│ │ └── preview_panel_view.py
│ └── main_window_view.py
├── ts_qt.py
└── mixed.py
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.
Typically the classes should look like this:
# my_cool_widget_view.py
class MyCoolWidgetView(QWidget):
def __init__(self):
super().__init__()
self.__button = QPushButton()
self.__color_dropdown = QComboBox()
# ...
self.__connect_callbacks()
def __connect_callbacks(self):
self.__button.clicked.connect(self._button_click_callback)
self.__color_dropdown.currentIndexChanged.connect(
lambda idx: self._color_dropdown_callback(self.__color_dropdown.itemData(idx))
)
def _button_click_callback(self):
raise NotImplementedError()
# my_cool_widget_controller.py
class MyCoolWidget(MyCoolWidgetView):
def __init__(self):
super().__init__()
def _button_click_callback(self):
print("Button was clicked!")
def _color_dropdown_callback(self, color: Color):
print(f"The selected color is now: {color}")
Observe the following key aspects of this example:
- The Controller is just called
MyCoolWidgetinstead ofMyCoolWidgetControlleras it will be directly used by other code - The UI elements are in private variables
- This enforces that the controller shouldn't directly access UI elements
- Instead the view should provide a protected API (e.g.
_get_color()) for things like setting/getting the value of a dropdown, etc. - Instead of
_get_color()there could also be a_colormethod marked with@property
- The callback methods are already defined as protected methods with NotImplementedErrors
- Defines the interface the callbacks
- Enforces that UI events be handled
Tip
A good (non-exhaustive) rule of thumb is: If it requires a non-UI import, then it doesn't belong in the
*_view.pyfile.
-
For an explanation of the Model-View-Controller (MVC) Model, checkout this article: MVC Framework Introduction. ↩︎