How to structure this project?
-
Hi all,
I am looking for a bit of help structuring/(de)coupling/... the different parts of my program so that it is structured more cleanly.
I'm sorry if this is an overly long question for something that's possibly very intuitive for most of you. I'm trying to properly explain what I'm after.
I realise I might be (likely am?) overcomplicating things...
What am I doing/trying to do
My software is used to do basic control of a LED processor (driving a LED screen) over a serial port. Think
- setting brightness
- selecting input sources
- starting test patterns
I have a working version of my software but it was one of the first things I did in PyQt so I ended up putting almost everything together. I have the
MainWindowand a controller class that also has everything related to serial ports. I did not use signals/slots. Everything was messy but it works. I just don't think it's a good solution and wouldn't scale well.As an excercise, I am now trying to redo the project to disconnect separate parts. It makes sense to separate in to:
- the UI (
MainWindow) - the serial part listing available ports, managing status, ... (
SerialStatus) - the controller issuing commands and requesting data back (
LEDController)
Where I'm stuck
I can't figure out how to do this somewhat cleanly.
I have the serial class with signals/slots connected to the UI
Direction Function MainWindow->SerialStatusRequest to check if the serial port is still connected/ok and the list of available ports is still correct MainWindow<-SerialStatusThe list of available serial ports has changed (with a list of ports) MainWindow->SerialStatusChange connection (including which port and connect/disconnect) MainWindow<-SerialStatusThe connection status has changed (connected or disconnected plus which port) MainWindow<-SerialStatusError message or conditions This works for basic functionality and I think I got this covered.
I can make a similar list for communication between the
MainWindowand theLEDControllerclasses. But then, how do I connect betweenSerialStatusandLEDController?The
LEDControllerclass needs to be able to interface with theSerialStatusclass that has the opened port stored as the attributeSerialStatus.portSpecific example
If I press a button to change a test pattern, I need the GUI to talk to the LED controller class which needs to send commands to the serial port.
How could I do this? I could have a signal/slot for Rx/Tx request from the
LEDControllerto theMainWindowwhich then has a signal/slot for Rx/Tx to theSerialStatus, but that seems awkward. Should I pass theSerialStatusclass as an argument to theLEDControllerclass and have that do all the communication directly? This would meanLEDControllerandSerialStatusare coupled together.Once again, I understand this might seem like very basic and intuitive.
-
Below the (non functional, just to make sure I understand correctly) code.
Variable names are extremely verbose, just to make them clear.
@jsulm is this what you mean?from PySide6 import QtCore as qtc from PySide6 import QtWidgets as qtw from .ui_sources.mainwindow import Ui_MainWindow class MainWindow(Ui_MainWindow, qtw.QMainWindow): def __init__(self, *args, obj=None, **kwargs): super().__init__() self.setupUi(self) self.led_controller = LEDController() self.serial_handler = SerialHandler() self.btn_green = qtw.QPushButton("Green") self._setup_signals() def _setup_signals(self): self.led_controller.sgn_command_available.connect(self.serial_handler.send_cmd) self.btn_green.clicked.connect(self.led_controller.pattern_green) class SerialHandler: def __init__(self, *args): self.port = self._create_and_open_port(*args) # Open serial port @qtc.pyqtSlot() def send_cmd(self, cmd: bytearray): self.port.write(cmd) class LEDController: sgn_cmd_ready_for_tx = qtc.Signal(bytearray) def __init__(self): ... @qtc.pyqtSlot() def pattern_green(self) -> bytearray: #Would generate command for a green pattern here generated_cmd = bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) self.sgn_cmd_ready_for_tx.emit(generated_cmd) `` -
Hi all,
I am looking for a bit of help structuring/(de)coupling/... the different parts of my program so that it is structured more cleanly.
I'm sorry if this is an overly long question for something that's possibly very intuitive for most of you. I'm trying to properly explain what I'm after.
I realise I might be (likely am?) overcomplicating things...
What am I doing/trying to do
My software is used to do basic control of a LED processor (driving a LED screen) over a serial port. Think
- setting brightness
- selecting input sources
- starting test patterns
I have a working version of my software but it was one of the first things I did in PyQt so I ended up putting almost everything together. I have the
MainWindowand a controller class that also has everything related to serial ports. I did not use signals/slots. Everything was messy but it works. I just don't think it's a good solution and wouldn't scale well.As an excercise, I am now trying to redo the project to disconnect separate parts. It makes sense to separate in to:
- the UI (
MainWindow) - the serial part listing available ports, managing status, ... (
SerialStatus) - the controller issuing commands and requesting data back (
LEDController)
Where I'm stuck
I can't figure out how to do this somewhat cleanly.
I have the serial class with signals/slots connected to the UI
Direction Function MainWindow->SerialStatusRequest to check if the serial port is still connected/ok and the list of available ports is still correct MainWindow<-SerialStatusThe list of available serial ports has changed (with a list of ports) MainWindow->SerialStatusChange connection (including which port and connect/disconnect) MainWindow<-SerialStatusThe connection status has changed (connected or disconnected plus which port) MainWindow<-SerialStatusError message or conditions This works for basic functionality and I think I got this covered.
I can make a similar list for communication between the
MainWindowand theLEDControllerclasses. But then, how do I connect betweenSerialStatusandLEDController?The
LEDControllerclass needs to be able to interface with theSerialStatusclass that has the opened port stored as the attributeSerialStatus.portSpecific example
If I press a button to change a test pattern, I need the GUI to talk to the LED controller class which needs to send commands to the serial port.
How could I do this? I could have a signal/slot for Rx/Tx request from the
LEDControllerto theMainWindowwhich then has a signal/slot for Rx/Tx to theSerialStatus, but that seems awkward. Should I pass theSerialStatusclass as an argument to theLEDControllerclass and have that do all the communication directly? This would meanLEDControllerandSerialStatusare coupled together.Once again, I understand this might seem like very basic and intuitive.
@DieterV said in How to structure this project?:
SerialStatus class that has the opened port stored as the attribute SerialStatus.port
Calling a class which does the serial port communication SerialStatus sounds wrong. It is going to do more than just checking the status, right?
Connecting SerialStatus and LEDController can be done the same way you connected LEDController with your UI. I guess you create LEDController and SerialStatus instaces in MainWindow, right? So, what is the problem connect both there?
-
@DieterV said in How to structure this project?:
SerialStatus class that has the opened port stored as the attribute SerialStatus.port
Calling a class which does the serial port communication SerialStatus sounds wrong. It is going to do more than just checking the status, right?
Connecting SerialStatus and LEDController can be done the same way you connected LEDController with your UI. I guess you create LEDController and SerialStatus instaces in MainWindow, right? So, what is the problem connect both there?
@jsulm said in How to structure this project?:
@DieterV said in How to structure this project?:
SerialStatus class that has the opened port stored as the attribute SerialStatus.port
Calling a class which does the serial port communication SerialStatus sounds wrong. It is going to do more than just checking the status, right?
Correct. I'd better rename that
SerialHandler. Thanks.Connecting SerialStatus and LEDController can be done the same way you connected LEDController with your UI. I guess you create LEDController and SerialStatus instaces in MainWindow, right? So, what is the problem connect both there?
If I understand correctly, you're saying to have the
LedControllersignal each outgoing message to the UI which connects this to a slot in theSerialHandlerand vice versa.Hadn't thought about the fact that I could connect signals in
SerialHandlerto slots inLEDControllerand the other way around.That way, you do end up with a lot of connections through the
MainWindowclass (if you start having bigger programs), but I presume that's not really an issue.I'll whip up some pseudo code throughout the day and post it to verify I understand correctly.
-
@jsulm said in How to structure this project?:
@DieterV said in How to structure this project?:
SerialStatus class that has the opened port stored as the attribute SerialStatus.port
Calling a class which does the serial port communication SerialStatus sounds wrong. It is going to do more than just checking the status, right?
Correct. I'd better rename that
SerialHandler. Thanks.Connecting SerialStatus and LEDController can be done the same way you connected LEDController with your UI. I guess you create LEDController and SerialStatus instaces in MainWindow, right? So, what is the problem connect both there?
If I understand correctly, you're saying to have the
LedControllersignal each outgoing message to the UI which connects this to a slot in theSerialHandlerand vice versa.Hadn't thought about the fact that I could connect signals in
SerialHandlerto slots inLEDControllerand the other way around.That way, you do end up with a lot of connections through the
MainWindowclass (if you start having bigger programs), but I presume that's not really an issue.I'll whip up some pseudo code throughout the day and post it to verify I understand correctly.
@DieterV said in How to structure this project?:
If I understand correctly, you're saying to have the LedController signal each outgoing message to the UI which connects this to a slot in the SerialHandler and vice versa.
No. What I mean is: in a place where you have access to both instances (SerialHandler and LedController) you can connect their signals and slots, so that they can connect directly with each other. No need to go through UI.
-
@DieterV said in How to structure this project?:
SerialStatus class that has the opened port stored as the attribute SerialStatus.port
Calling a class which does the serial port communication SerialStatus sounds wrong. It is going to do more than just checking the status, right?
Connecting SerialStatus and LEDController can be done the same way you connected LEDController with your UI. I guess you create LEDController and SerialStatus instaces in MainWindow, right? So, what is the problem connect both there?
@jsulm said in How to structure this project?:
I guess you create LEDController and SerialStatus instaces in MainWindow, right? So, what is the problem connect both there?
Well, yeah. I create the
LEDControllerandSerialHandleras attributes in theMainWindow. That is what I meant with "connecting them in the GUI". But I'll post some code to make sure I'm correctly understanding. -
Below the (non functional, just to make sure I understand correctly) code.
Variable names are extremely verbose, just to make them clear.
@jsulm is this what you mean?from PySide6 import QtCore as qtc from PySide6 import QtWidgets as qtw from .ui_sources.mainwindow import Ui_MainWindow class MainWindow(Ui_MainWindow, qtw.QMainWindow): def __init__(self, *args, obj=None, **kwargs): super().__init__() self.setupUi(self) self.led_controller = LEDController() self.serial_handler = SerialHandler() self.btn_green = qtw.QPushButton("Green") self._setup_signals() def _setup_signals(self): self.led_controller.sgn_command_available.connect(self.serial_handler.send_cmd) self.btn_green.clicked.connect(self.led_controller.pattern_green) class SerialHandler: def __init__(self, *args): self.port = self._create_and_open_port(*args) # Open serial port @qtc.pyqtSlot() def send_cmd(self, cmd: bytearray): self.port.write(cmd) class LEDController: sgn_cmd_ready_for_tx = qtc.Signal(bytearray) def __init__(self): ... @qtc.pyqtSlot() def pattern_green(self) -> bytearray: #Would generate command for a green pattern here generated_cmd = bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) self.sgn_cmd_ready_for_tx.emit(generated_cmd) `` -
Below the (non functional, just to make sure I understand correctly) code.
Variable names are extremely verbose, just to make them clear.
@jsulm is this what you mean?from PySide6 import QtCore as qtc from PySide6 import QtWidgets as qtw from .ui_sources.mainwindow import Ui_MainWindow class MainWindow(Ui_MainWindow, qtw.QMainWindow): def __init__(self, *args, obj=None, **kwargs): super().__init__() self.setupUi(self) self.led_controller = LEDController() self.serial_handler = SerialHandler() self.btn_green = qtw.QPushButton("Green") self._setup_signals() def _setup_signals(self): self.led_controller.sgn_command_available.connect(self.serial_handler.send_cmd) self.btn_green.clicked.connect(self.led_controller.pattern_green) class SerialHandler: def __init__(self, *args): self.port = self._create_and_open_port(*args) # Open serial port @qtc.pyqtSlot() def send_cmd(self, cmd: bytearray): self.port.write(cmd) class LEDController: sgn_cmd_ready_for_tx = qtc.Signal(bytearray) def __init__(self): ... @qtc.pyqtSlot() def pattern_green(self) -> bytearray: #Would generate command for a green pattern here generated_cmd = bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) self.sgn_cmd_ready_for_tx.emit(generated_cmd) `` -
That is great. Thank you very much for your time and help.
Would you have any (open source) pyqt projects you would recommend to look at and learn from?
I've got a couple of good books but they usually have small examples focused on specific topics, not complete projects. I'm currently mostly looking at how https://github.com/dietervansteenwegen/serial-tool is set up and try to understand and copy (if it makes sense) that.
-
D DieterV has marked this topic as solved on