Reputation: 1166
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:
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
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