Raphael
Raphael

Reputation: 1166

Proper implementation of access to ui elements for custom widgets in PySide or PyQT

I am building a program to view a video and do image processing on it. I want to know the proper way to give my individual classes access to the ui elements.

The way I implemented it now is the following:

I designed a GUI in QT Creator and save the video.ui file, then I generate the Ui_video.py file from it with pyside6-uic.

My main.py then looks like this:

import sys

from PySide6.QtWidgets import QMainWindow, QApplication
from PySide6.QtCore import QCoreApplication
from ui_video import Ui_Video

class VideoViewerMain(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui : Ui_Video = None

    def closeEvent(self, event):
        self.ui.video_view.closeEvent(event)
        return super().closeEvent(event)

class App(QApplication):
    def __init__(self, *args, **kwargs):
        super(App,self).__init__(*args, **kwargs)

        self.window = VideoViewerMain()
        self.ui = Ui_Video() # 
        self.window.ui = self.ui
        self.ui.setupUi(self.window)
        self.window.show()

if __name__ == "__main__":
    # init application
    app = App(sys.argv)
    app.processEvents()

    # execute qt main loop
    sys.exit(app.exec())

Then I implemented my video previewer, which inherits from QOpenGLWidget as a way to display the video. (I guess this could be any widget type that supports painting)

The Qt hierarchy looks like this:
Qt hierarchy

In the QT Creator I set the corresponding widget as a custom class. This means my class is instantiated when the main class calls setupUI.

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from ui_video import Ui_Video

class VideoPreview(QOpenGLWidget):
    """ 
    widget to display camera feed and overlay droplet approximations from opencv
    """
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui: Ui_Video = self.window().ui
        self.reader = VideoReader(r"some/video/path")
        # adjust the seekBar range with video length, this throws error
        self.ui.seekBar.setMaximum(self.reader.number_of_frames - 1)
        self.ui.seekBar.setMinimum(0)

        self._first_show = True

    def showEvent(self, event: QtGui.QShowEvent) -> None:
        if self._first_show:
            self._first_show = False
            self.ui.seekBar.setMaximum(self.reader.number_of_frames - 1)
            self.ui.seekBar.setMinimum(0)
        return super().showEvent(event)

When I try to initialize the ui seekBar in the init function, it throws an error

  File "src/VideoProcessor\videopreview.py", line 56, in __init__
    self.ui.seekBar.setMaximum(self.reader.number_of_frames - 1)
AttributeError: 'Ui_Video' object has no attribute 'seekBar'

This is because my custom class is instantiated before the seekBar in the setupUI function which I cannot change. Currently I use the workaround with the showEvent.

So my question is: How would a proper implementation of a custom widget look like in this context?
Should I divide the functionality of the video controls from the widget entirely?
How can I ensure that the classes have access to the ui elements?

Upvotes: 0

Views: 649

Answers (1)

PyHP3D
PyHP3D

Reputation: 88

The main thing I see is your use of colons in assignment statements.

In python colons are mainly used to for setting dictionary literal values...

my_dict = {"my_key1": 1, "my_key2": 2}

ending loops/condition statements...

for index in range(12):
    if index == 5:
        print("FIVE")

and the occasional lambda when necessary...

sorted_my_dict = sorted(my_dict.items(), key=lambda item: item[1])

In your case I see at least 2 places where you are using colons in ways I've never personally seen in python.

self.ui : Ui_Video = None
# and
self.ui: Ui_Video = self.window().ui

If you need to set a value to None just use

self.ui = None

when setting it to an existing object from another widget either utilize the parent argument or just pass the object in as a different argument...

self.ui = parent.ui
# or 
class VideoPreview(QOpenGLWidget):
    """ 
    widget to display camera feed and overlay droplet approximations from opencv
    """
    def __init__(self, ui_object, parent=None):
        super().__init__(parent)
        self.ui = ui_object.ui
        [...]

Upvotes: 1

Related Questions