how to use QtMenu or QtMoseEvent without going through a class

47 views Asked by At

Hello I am making a software in python I am not very good and so far I have not had to use any class in my program. I want to do events with the mouse without going through class and the codes I found all use class, it is difficult for me to use it without breaking my program.

Here is my code:

def init_tree(win):
    tw = QTreeWidget(win)
    tw.resize(500, 500)

    tw.setHeaderLabels(['TAGS'])
    tw.setAlternatingRowColors(True)

    tw = clear_tree(tw)
    fill_tree(tw)

    tw.show()

def main():
    app = QApplication.instance()
    if not app:
        app = QApplication(argv)

    window  = widgets()

    init_tree(window)

    mouse_press_event(window)

    exit(app.exec_())
main()

And here is the code i want to add

def contextMenuEvent(self, event):
        contextMenu = QMenu(self)
        newAct = contextMenu.addAction("New")
        openAct = contextMenu.addAction("Open")
        quitAct = contextMenu.addAction("Quit")
        action = contextMenu.exec_(self.mapToGlobal(event.pos()))
        if action == quitAct:
            self.close()

OR

class MyWidget(QWidget):


    def __init__(self):
        super(MyWidget, self).__init__()

    def mousePressEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            print("Left Button Clicked")
        elif QMouseEvent.button() == Qt.RightButton:
            #do what you want here
            print("Right Button Clicked")

For self I pass win as a parameter

What I'm having trouble with is the "event/ QMouseEvent" parameter

1

There are 1 answers

0
musicamante On

It can partially be done, and it's generally called monkey patching, which is a piece of Python code which extends or modifies other code.

You cannot do that for event management as you did with your init_tree function, though: those function are "static", as they just get called once, while event driven functions should be called whenever the related event is emitted.

The solution is then to "monkey patch" the class instance with the function that reacts to those events. This is a simple example:

def mouse_press_event(event):
    print('mouse pressed', event.pos())

some_widget.mousePressEvent = mouse_press_event

Now, there's a catch. ALL instance methods have a first argument which is a reference to the instance, that's what self is (note that "self" is just a convention, you could actually name it as you wish). That's because instance methods normally need to do something with or at the instance. This is clear in the contextMenuEvent code you provided: the self is important to set the parent of the menu and, most importantly, to use its mapToGlobal.

When you monkey patch with an anonymous function like the one above, you don't get the first instance argument (in fact, we only have the event argument). In order to get that, with "anonymous" monkey patching we have to provide the instance argument artificially, and we can use lambda for that:

def contextMenuEvent(self, event):
        contextMenu = QMenu(self)
        # ...
        action = contextMenu.exec_(event.globalPos())
        # ...

some_widget.contextMenuEvent = lambda event: contextMenuEvent(tw, event)

Note: I changed your code a bit, directly using event.globalPos(); the reason is that all QAbstractScrollArea subclasses (like all QAbstractItemView subclasses as QTreeWidget) have a child "viewport" that actually shows the content that are going to scroll, and that viewport is sometimes translated by some pixels, for example when a header is shown.

The original function should be changed to this:

action = contextMenu.exec_(self.viewport().mapToGlobal(event.pos()))

But we clearly don't need that, as all mouse events also have a globalPos() that is already mapped to global coordinates.

Finally, while there are some cases for which your approach is ideal (usually very simple reimplementation done on the fly with temporary instances), if you're going to do that for a whole program you should consider four aspects:

  • it's a bad idea;
  • you shouldn't do it like that, and you should use subclasses instead;
  • you really shouldn't do that unless you really know what you are doing and why;
  • it's a really bad idea;

If you believe that following this approach might ease things up, sorry but you're wrong: at some point your program would become too complex, and all those anonymous will only make your program very hard to read and debug.

Instead of finding complex and unorthodox ways to do something in a way it's not supposed to do, learn to do it in the correct way.

As a further note, monkey patching has an important issue with PyQt: Qt uses function caching to speed up things and use less resources, and if a function is called first with an already existing base method, monkey patching will not work at all, as that first original method will always be called afterwards.

So, take your time, learn how to create subclasses, and you'll not only find out that they're more easy than they seem, but will improve your coding dramatically.