Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. How to structure this project?
QtWS25 Last Chance

How to structure this project?

Scheduled Pinned Locked Moved Solved Qt for Python
8 Posts 2 Posters 354 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    DieterV
    wrote on last edited by DieterV
    #1

    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 MainWindow and 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 -> SerialStatus Request to check if the serial port is still connected/ok and the list of available ports is still correct
    MainWindow <- SerialStatus The list of available serial ports has changed (with a list of ports)
    MainWindow -> SerialStatus Change connection (including which port and connect/disconnect)
    MainWindow <- SerialStatus The connection status has changed (connected or disconnected plus which port)
    MainWindow <- SerialStatus Error 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 MainWindow and the LEDController classes. But then, how do I connect between SerialStatus and LEDController?

    The LEDController class needs to be able to interface with the SerialStatus class that has the opened port stored as the attribute SerialStatus.port

    Specific 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 LEDController to the MainWindow which then has a signal/slot for Rx/Tx to the SerialStatus, but that seems awkward. Should I pass the SerialStatus class as an argument to the LEDController class and have that do all the communication directly? This would mean LEDController and SerialStatus are coupled together.

    Once again, I understand this might seem like very basic and intuitive.

    jsulmJ 1 Reply Last reply
    0
    • D DieterV

      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)
      ``
      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #7

      @DieterV said in How to structure this project?:

      is this what you mean?

      Yes

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • D DieterV

        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 MainWindow and 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 -> SerialStatus Request to check if the serial port is still connected/ok and the list of available ports is still correct
        MainWindow <- SerialStatus The list of available serial ports has changed (with a list of ports)
        MainWindow -> SerialStatus Change connection (including which port and connect/disconnect)
        MainWindow <- SerialStatus The connection status has changed (connected or disconnected plus which port)
        MainWindow <- SerialStatus Error 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 MainWindow and the LEDController classes. But then, how do I connect between SerialStatus and LEDController?

        The LEDController class needs to be able to interface with the SerialStatus class that has the opened port stored as the attribute SerialStatus.port

        Specific 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 LEDController to the MainWindow which then has a signal/slot for Rx/Tx to the SerialStatus, but that seems awkward. Should I pass the SerialStatus class as an argument to the LEDController class and have that do all the communication directly? This would mean LEDController and SerialStatus are coupled together.

        Once again, I understand this might seem like very basic and intuitive.

        jsulmJ Offline
        jsulmJ Offline
        jsulm
        Lifetime Qt Champion
        wrote on last edited by
        #2

        @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?

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        D 2 Replies Last reply
        1
        • jsulmJ jsulm

          @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?

          D Offline
          D Offline
          DieterV
          wrote on last edited by
          #3

          @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 LedController signal each outgoing message to the UI which connects this to a slot in the SerialHandler and vice versa.

          Hadn't thought about the fact that I could connect signals in SerialHandler to slots in LEDController and the other way around.

          That way, you do end up with a lot of connections through the MainWindow class (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.

          jsulmJ 1 Reply Last reply
          0
          • D DieterV

            @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 LedController signal each outgoing message to the UI which connects this to a slot in the SerialHandler and vice versa.

            Hadn't thought about the fact that I could connect signals in SerialHandler to slots in LEDController and the other way around.

            That way, you do end up with a lot of connections through the MainWindow class (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.

            jsulmJ Offline
            jsulmJ Offline
            jsulm
            Lifetime Qt Champion
            wrote on last edited by
            #4

            @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.

            https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            1
            • jsulmJ jsulm

              @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?

              D Offline
              D Offline
              DieterV
              wrote on last edited by
              #5

              @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 LEDController and SerialHandler as attributes in the MainWindow. That is what I meant with "connecting them in the GUI". But I'll post some code to make sure I'm correctly understanding.

              1 Reply Last reply
              0
              • D Offline
                D Offline
                DieterV
                wrote on last edited by DieterV
                #6

                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)
                ``
                jsulmJ 1 Reply Last reply
                0
                • D DieterV

                  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)
                  ``
                  jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on last edited by
                  #7

                  @DieterV said in How to structure this project?:

                  is this what you mean?

                  Yes

                  https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0
                  • D Offline
                    D Offline
                    DieterV
                    wrote on last edited by
                    #8

                    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.

                    1 Reply Last reply
                    0
                    • D DieterV has marked this topic as solved on

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved