Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Need Help with OOP/my project, trouble understanding how things "connect" and work together.
Qt 6.11 is out! See what's new in the release blog

Need Help with OOP/my project, trouble understanding how things "connect" and work together.

Scheduled Pinned Locked Moved Unsolved General and Desktop
12 Posts 3 Posters 3.3k Views 1 Watching
  • 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.
  • R Offline
    R Offline
    Romeoo
    wrote on last edited by
    #1

    Hey everyone,

    I'm very new to PyQt5 and not that good at Python either as you will see momentarily. Let me walk you through the idea and then post my code (and this code is just a simple "sketch" so to say.. to get familiar with the different things):

    You have a Main Menu. One of the buttons you can press there is the "Skill Tree"-button which opens up another window.
    In there you can allocate a total of 110 skill points.
    Each skill has its own button that you can press.. and by doing so, you invest 1 skill-point. So it knows which button has been pressed.

    It should also distinguish between left- and right-click (as left will spend points, right will subtract them) and allow shift+left/right-click to allocate or subtract 5 pts.

    I can get each of these to work by themselves.. but not together (with the buttons). And that's where my lack of OOP and PyQt5 knowledge comes into play. Not really grasping when to instantiate, what to pass as parameters, if my button (which is of class QPushButton/AbstractButton) can even use the members/functions(?) of other PyQt5 Classes etc.
    The Last class in the code below isn't called/instantiated btw, just to demonstrate what I'd like to do/connect.

    import sys
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout
    from PyQt5.QtWidgets import QWidget
    from functools import partial
    
    
    class MainWindow(QMainWindow):
    
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
            self.setObjectName("MainWindow")
            self.resize(1052, 804)
            self.setStyleSheet("background-color: rgb(24, 23, 24)")
    
    
            self.button1 = QPushButton(self)
            self.button1.setGeometry(100, 100, 100, 100)
            self.button1.setStyleSheet("background-color: white")
    
            
            self.button1.clicked.connect(self.is_clicked)
    
        def is_clicked(self):
            self.r = MainWindowTwo()
            self.r.show()
    
    
    
    
    
    
    
    
    class MainWindowTwo(QMainWindow):
        def __init__(self):
            super().__init__()
            self.initUIB()
    
        def initUIB(self):
            self.setGeometry(200, 200, 800, 600)
            self.label = QLabel(self)
            self.label.move(350, 80)
    
    
            self.button2 = QPushButton(self)
            self.button2.setGeometry(100, 100, 180, 180)
            self.button3 = QPushButton(self)
            self.button3.setGeometry(300, 300, 180, 180)
    
            self.button2.clicked.connect(partial(self.clicked, "Button 2"))
            self.button3.clicked.connect(partial(self.clicked, "Button 3"))
    
    
    
        def clicked(self, value):
            if value == "Button 2":
                self.button2.setText("I am Button 2")
            
            if value == "Button 3":
                self.button3.setText("I am Button 3")
    
    
    
    
        def mousePressEvent(self, e):
            if e.button() == Qt.LeftButton:
                self.label.setText("Left Button pressed.")
    
            if e.button() == Qt.RightButton:
                self.label.setText("Right Button pressed.")
    
    
    
    class MyWidget(QWidget):
        def __init__(self):
            super().__init__()
            self.intUIC()
    
        def intUIC(self):
            self.layout = QVBoxLayout(self)
            self.button2 = QPushButton(self)
            self.button2.clicked.connect(self.handleButton)
            self.button2.setText("Click me!")
            self.layout.addWidget(self.button2)
            self.setLayout(self.layout)
    
        def handleButton(self):
            self.modifiers = QApplication.keyboardModifiers()
    
            if self.modifiers == Qt.ShiftModifier:
                print("shift-click")
            
            if self.modifiers == Qt.ControlModifier:
                print("ctrl-click")
            if self.modifiers == (Qt.ShiftModifier | Qt.ControlModifier):
                print("shift+control-click")
            if self.modifiers != Qt.ShiftModifier and self.modifiers != Qt.ControlModifier and self.modifiers != (Qt.ShiftModifier | Qt.ControlModifier):
                print("mouseclick")
    
    
    
    if __name__ == "__main__":
        def run():
            app = QApplication(sys.argv)
            m = MainWindow()
            m.show()
            sys.exit(app.exec_())
        run()
    
    

    Sorry if this post sounds a bit whiny, just a bit frustrated and I desperately want to understand all of this better and continue working on my project because it's fun.. and I want to improve.

    1 Reply Last reply
    0
    • mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by mrjj
      #2

      Hi
      A signal and slot connection are always between instantiated objects.
      The sender and receiver must both be a pointer to an instance of the class.
      ( with the exception of lambdas )

      -if my button (which is of class QPushButton/AbstractButton) can even use the members/functions(?) of other PyQt5 Classes etc.
      well yes they can. but it's not actually the button that needs access, its more the function (slot) you connected to its signal.

      But here is Qt signal & slot concept very handy.

      You can connect it's signal to more than one slot. Also to a slot in another instance of another class.

      You can also easily add a new signal to a class. Like HitPointChanged and use that signal
      to connect other instances of classes that need to know this.
      Then you simply emit this new signal and others will know.

      This allows each class to know very little about others and still "talk".

      R 1 Reply Last reply
      1
      • mrjjM mrjj

        Hi
        A signal and slot connection are always between instantiated objects.
        The sender and receiver must both be a pointer to an instance of the class.
        ( with the exception of lambdas )

        -if my button (which is of class QPushButton/AbstractButton) can even use the members/functions(?) of other PyQt5 Classes etc.
        well yes they can. but it's not actually the button that needs access, its more the function (slot) you connected to its signal.

        But here is Qt signal & slot concept very handy.

        You can connect it's signal to more than one slot. Also to a slot in another instance of another class.

        You can also easily add a new signal to a class. Like HitPointChanged and use that signal
        to connect other instances of classes that need to know this.
        Then you simply emit this new signal and others will know.

        This allows each class to know very little about others and still "talk".

        R Offline
        R Offline
        Romeoo
        wrote on last edited by
        #3

        @mrjj Hey, first off I'd like to say thank you for typing all this out!

        Now.. I want to say I understand what you're telling me, but if you're asking me to now go ahead and implement this new knowledge and edit my code so it all works, I wouldn't be able to do it.

        That's not to say your explanation is bad in any way, it's just that (as you can probably tell) I'm still pretty new to OOP & PyQt5 and I have no idea how to even approach this.

        I watched several OOP-tutorials and can do the stuff they always show off in these (most of them show the Class Person example with multiple other Classes (Manager, Employee etc.) inheriting from Person.. all its methods and properties). Simple enough.

        But when I look at this and try to rewrite my code so it works... or think about how I'd code what you're telling me, I'm just lost & scratching my head, honestly. Sorry

        mrjjM 1 Reply Last reply
        0
        • R Romeoo

          @mrjj Hey, first off I'd like to say thank you for typing all this out!

          Now.. I want to say I understand what you're telling me, but if you're asking me to now go ahead and implement this new knowledge and edit my code so it all works, I wouldn't be able to do it.

          That's not to say your explanation is bad in any way, it's just that (as you can probably tell) I'm still pretty new to OOP & PyQt5 and I have no idea how to even approach this.

          I watched several OOP-tutorials and can do the stuff they always show off in these (most of them show the Class Person example with multiple other Classes (Manager, Employee etc.) inheriting from Person.. all its methods and properties). Simple enough.

          But when I look at this and try to rewrite my code so it works... or think about how I'd code what you're telling me, I'm just lost & scratching my head, honestly. Sorry

          mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @Romeoo

          Hi
          Thats ok.
          But what exactly do you want to happen?

          Which class has the skillpoint list ?

          For the case of add/reducing skill point assignment for a player, I assume you
          must read the current assignment from some class and add/reduce from it and the write it back.

          Im asking as the data class is the one that is normally shared amount classes/GUI or being hold by say
          MainWindow and other classes use its slots to manipulate its data.

          R 1 Reply Last reply
          0
          • mrjjM mrjj

            @Romeoo

            Hi
            Thats ok.
            But what exactly do you want to happen?

            Which class has the skillpoint list ?

            For the case of add/reducing skill point assignment for a player, I assume you
            must read the current assignment from some class and add/reduce from it and the write it back.

            Im asking as the data class is the one that is normally shared amount classes/GUI or being hold by say
            MainWindow and other classes use its slots to manipulate its data.

            R Offline
            R Offline
            Romeoo
            wrote on last edited by
            #5

            @mrjj Hey,
            I actually don't know how I'm going to structure/build all of this yet as I'm just experimenting with the different PyQt5 possibilities and learning about certain functionalities which I'm going to need for my project later on.

            Right now (for the purpose of learning this), I just want to keep the code as is and manipulate/connect the few methods and attributes the classes/instances have.

            So for example, after testing some more.. I figured out that if I make these changes to the above code:

            old:

            def clicked(self, value):
                    if value == "Button 2":
                        self.button2.setText("I am Button 2")
                    
                    if value == "Button 3":
                        self.button3.setText("I am Button 3")
            

            new:

            def clicked(self, value):
                    t = MyWidget()
                    if value == "Button 2":
                        t.handleButton()
                        self.button2.setText("I am Button 2")
                    
                    if value == "Button 3":
                        self.button3.setText("I am Button 3")
                        t.handleButton()
            

            it will correctly check if either of the 2 buttons in the MainWindowTwo class have been pressed with any of the defined modifiers in MyWidget's handlebutton() method (shift, ctrl, shift+ctrl) and no longer if only the Widget itself has been clicked like that.

            But if I try the same with mousePressEvent (whether it's in the MainWindowTwo Class or MyWidget Class), I get the Error: 'QPushButton' object has no attribute 'button'
            or
            'MainWindowTwo' object has no attribute 'button'

            depending on how I call it.

            I'm sure this seems very silly, I'm sorry. I just want it to also check if the two buttons which are defined in MainWindowTwo have been pressed with either left- or right-click.

            JonBJ 1 Reply Last reply
            0
            • R Romeoo

              @mrjj Hey,
              I actually don't know how I'm going to structure/build all of this yet as I'm just experimenting with the different PyQt5 possibilities and learning about certain functionalities which I'm going to need for my project later on.

              Right now (for the purpose of learning this), I just want to keep the code as is and manipulate/connect the few methods and attributes the classes/instances have.

              So for example, after testing some more.. I figured out that if I make these changes to the above code:

              old:

              def clicked(self, value):
                      if value == "Button 2":
                          self.button2.setText("I am Button 2")
                      
                      if value == "Button 3":
                          self.button3.setText("I am Button 3")
              

              new:

              def clicked(self, value):
                      t = MyWidget()
                      if value == "Button 2":
                          t.handleButton()
                          self.button2.setText("I am Button 2")
                      
                      if value == "Button 3":
                          self.button3.setText("I am Button 3")
                          t.handleButton()
              

              it will correctly check if either of the 2 buttons in the MainWindowTwo class have been pressed with any of the defined modifiers in MyWidget's handlebutton() method (shift, ctrl, shift+ctrl) and no longer if only the Widget itself has been clicked like that.

              But if I try the same with mousePressEvent (whether it's in the MainWindowTwo Class or MyWidget Class), I get the Error: 'QPushButton' object has no attribute 'button'
              or
              'MainWindowTwo' object has no attribute 'button'

              depending on how I call it.

              I'm sure this seems very silly, I'm sorry. I just want it to also check if the two buttons which are defined in MainWindowTwo have been pressed with either left- or right-click.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #6

              @Romeoo
              mousePressEvent() is an event, not a signal, and has to be coded quite differently. Show your code which errors if you want help.

              R 1 Reply Last reply
              1
              • JonBJ JonB

                @Romeoo
                mousePressEvent() is an event, not a signal, and has to be coded quite differently. Show your code which errors if you want help.

                R Offline
                R Offline
                Romeoo
                wrote on last edited by Romeoo
                #7

                @JonB Hey, thanks!

                I already posted the code in my first post though?
                The Errors I mentioned like "QPushButton' object has no attribute 'button'" was just me messing around with the code and trying stuff out.. just mentioning nonsense I tried.

                I have not found a way to do what I want. The code in my initial post is the code I have and it works/has no Error messages because I have no idea how to do what I'm asking for here (which is connecting it all to work together).

                That is why I made this post, so someone can hopefully show me how to do it and explain it.

                Again: I want mousePressEvent() to be able to determine if self.button2 and self.button3 have been pressed with a left-click or a right-click.
                Right now all it can do is determine that if I press anywhere in MainWindowTwo's Window, but not if I press on either of the buttons.

                mrjjM 1 Reply Last reply
                0
                • R Romeoo

                  @JonB Hey, thanks!

                  I already posted the code in my first post though?
                  The Errors I mentioned like "QPushButton' object has no attribute 'button'" was just me messing around with the code and trying stuff out.. just mentioning nonsense I tried.

                  I have not found a way to do what I want. The code in my initial post is the code I have and it works/has no Error messages because I have no idea how to do what I'm asking for here (which is connecting it all to work together).

                  That is why I made this post, so someone can hopefully show me how to do it and explain it.

                  Again: I want mousePressEvent() to be able to determine if self.button2 and self.button3 have been pressed with a left-click or a right-click.
                  Right now all it can do is determine that if I press anywhere in MainWindowTwo's Window, but not if I press on either of the buttons.

                  mrjjM Offline
                  mrjjM Offline
                  mrjj
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  @Romeoo

                  Hi
                  For MousePressEvent you must subclass the button.
                  something like. (disclaimer. c++ dude. don't know python :)

                  class MyButton(QtGui.QPushButton): 
                  
                      def mousePressEvent(self, event):
                          // use events button() == Qt::RightButton to know if left or right
                          QtGui.QPushButton.mousePressEvent(self, event) // call the base class which you must do or button stops to function
                  
                  R 1 Reply Last reply
                  1
                  • mrjjM mrjj

                    @Romeoo

                    Hi
                    For MousePressEvent you must subclass the button.
                    something like. (disclaimer. c++ dude. don't know python :)

                    class MyButton(QtGui.QPushButton): 
                    
                        def mousePressEvent(self, event):
                            // use events button() == Qt::RightButton to know if left or right
                            QtGui.QPushButton.mousePressEvent(self, event) // call the base class which you must do or button stops to function
                    
                    R Offline
                    R Offline
                    Romeoo
                    wrote on last edited by
                    #9

                    @mrjj ty for being so patient with me lol,

                    hmn, if I try to implement that and mess around with it, it just says
                    "AttributeError: module 'PyQt5.QtGui' has no attribute 'QPushButton'

                    mrjjM 1 Reply Last reply
                    0
                    • R Romeoo

                      @mrjj ty for being so patient with me lol,

                      hmn, if I try to implement that and mess around with it, it just says
                      "AttributeError: module 'PyQt5.QtGui' has no attribute 'QPushButton'

                      mrjjM Offline
                      mrjjM Offline
                      mrjj
                      Lifetime Qt Champion
                      wrote on last edited by mrjj
                      #10

                      "AttributeError: module 'PyQt5.QtGui' has no attribute 'QPushButton'

                      Then it lives in another module, would be my guess.

                      PyQt5 is not the official python binding so not sure where to look it up.

                      Hmm
                      I saw code doing
                      self.pushButton = QtWidgets.QPushButton(self.centralwidget)

                      so it lives in QtWidgets, I guess ?

                      so replace QtGui with QtWidgets and see if it eats it :)

                      R 1 Reply Last reply
                      0
                      • mrjjM mrjj

                        "AttributeError: module 'PyQt5.QtGui' has no attribute 'QPushButton'

                        Then it lives in another module, would be my guess.

                        PyQt5 is not the official python binding so not sure where to look it up.

                        Hmm
                        I saw code doing
                        self.pushButton = QtWidgets.QPushButton(self.centralwidget)

                        so it lives in QtWidgets, I guess ?

                        so replace QtGui with QtWidgets and see if it eats it :)

                        R Offline
                        R Offline
                        Romeoo
                        wrote on last edited by
                        #11

                        @mrjj thanks... been trying everything since your post, but I'm clearly way in over my head and seemingly have no idea what I'm doing.

                        The "best" I was able to do was create new buttons in self.button2 and self.button3 that would properly respond to the mouseclick?!

                        I eventually just ended up throwing lots of nonsense syntax at the wall and hope something would stick... that's how I ended up with:
                        self.button2.clicked.connect(MyButton(self.button2).mousePressEvent)
                        self.button3.clicked.connect(MyButton(self.button3).mousePressEvent)

                        which like I said simply resulted in creating 2 additional buttons inside of my other 2 (one in self.button1 and one in self.button2) which then determined if it was left or rightclick.

                        Everything else just resulted in:
                        AttributeError: 'QPushButton' object has no attribute 'button'
                        AttributeError: 'PyQt5.QtCore.pyqtBoundSignal' object has no attribute 'button'
                        AttributeError: 'MainWindowTwo' object has no attribute 'button'

                        I mean.. I understand these Errors. But I also have no clue how to fix this.
                        Anyway, sorry for wasting your time. thanks for trying to help me ; )

                        mrjjM 1 Reply Last reply
                        0
                        • R Romeoo

                          @mrjj thanks... been trying everything since your post, but I'm clearly way in over my head and seemingly have no idea what I'm doing.

                          The "best" I was able to do was create new buttons in self.button2 and self.button3 that would properly respond to the mouseclick?!

                          I eventually just ended up throwing lots of nonsense syntax at the wall and hope something would stick... that's how I ended up with:
                          self.button2.clicked.connect(MyButton(self.button2).mousePressEvent)
                          self.button3.clicked.connect(MyButton(self.button3).mousePressEvent)

                          which like I said simply resulted in creating 2 additional buttons inside of my other 2 (one in self.button1 and one in self.button2) which then determined if it was left or rightclick.

                          Everything else just resulted in:
                          AttributeError: 'QPushButton' object has no attribute 'button'
                          AttributeError: 'PyQt5.QtCore.pyqtBoundSignal' object has no attribute 'button'
                          AttributeError: 'MainWindowTwo' object has no attribute 'button'

                          I mean.. I understand these Errors. But I also have no clue how to fix this.
                          Anyway, sorry for wasting your time. thanks for trying to help me ; )

                          mrjjM Offline
                          mrjjM Offline
                          mrjj
                          Lifetime Qt Champion
                          wrote on last edited by SGaist
                          #12

                          @Romeoo
                          Hi
                          Ahh, i see where the confusion comes from.
                          https://doc.qt.io/qt-5/eventsandfilters.html

                          Events are not a signal. you cannot connect to them.
                          events are handled by classes in a virtual function.
                          and to customize that we create a sub class so its our own class.

                          so self.button2.clicked.connect(MyButton(self.button2).mousePressEvent)
                          will not work.

                          You should create instances of MyButton and use instead of the std Buttons.
                          Qt will then call your mousePressEvent directly in the sub class.

                          Often one does not wish to handle the processing directly in the mousepRessEvent but
                          tell someone else to do it. that we can do with a new signal to keep the MyButton clean and separate from rest of the app.
                          Disclaimer: I'm just grabbing code from the net. i cannot test it so will have errors:)

                          class MyButton(QtWidgets.QPushButton): 
                            leftclicked=pyqtSignal() // define new signal
                            rightclicked=pyqtSignal()
                          
                              def mousePressEvent(self, event):
                                  // use events button() == Qt.RightButton to know if left or right
                                 if ( event.button == Qt.RightButton )    self.rightclicked.emit() // send signal
                                 if ( event.button == Qt.LeftButton )    self.leftclicked.emit()
                                  QtWidgets.QPushButton.mousePressEvent(self, event) // call the base class which you must do or button stops to function
                          

                          [edit: fixed wrong module and syntax SGaist]

                          then from outside, you connect, your instance of MyButton and its new signal leftclicked and rightclicked to slots in main. there you then to the actual skill assignment.

                          1 Reply Last reply
                          1

                          • Login

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