【问题标题】:how to use QtMenu or QtMoseEvent without going through a class如何在不通过类的情况下使用 QtMenu 或 QtMoseEvent
【发布时间】:2026-02-20 04:30:01
【问题描述】:

您好,我正在用 python 制作软件我不是很好,到目前为止我还没有在我的程序中使用任何类。 我想在不通过类的情况下用鼠标做事件,而我发现的代码都使用类,我很难在不破坏程序的情况下使用它。

这是我的代码:

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()

这是我要添加的代码

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()

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")

对于自我,我将 win 作为参数传递

我遇到的问题是“event/QMouseEvent”参数

【问题讨论】:

标签: python class pyqt pyqt5 python-class


【解决方案1】:

可以部分完成,一般称为monkey patching,是一段Python代码,扩展或修改其他代码。

您不能像使用 init_tree 函数那样进行事件管理:这些函数是“静态的”,因为它们只被调用一次,而事件驱动函数应该在相关事件发出时被调用。

然后解决方案是使用对这些事件做出反应的函数来“猴子补丁”类实例。这是一个简单的例子:

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

some_widget.mousePressEvent = mouse_press_event

现在,有一个问题。 ALL 实例方法的第一个参数是对 instance 的引用,这就是 self 的含义(请注意,“self”只是一个约定,您实际上可以命名如你所愿)。那是因为实例方法通常需要对实例withat 做一些事情。这在您提供的contextMenuEvent 代码中很清楚:self 对设置菜单的父级很重要,最重要的是使用它的mapToGlobal

当您使用上述匿名函数进行猴子补丁时,您不会获得第一个实例参数(实际上,我们只有 event 参数)。为了做到这一点,通过“匿名”猴子补丁,我们必须人为地提供实例参数,我们可以为此使用lambda

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

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

注意:我稍微修改了你的代码,直接使用event.globalPos();原因是所有 QAbstractScrollArea 子类(就像所有 QAbstractItemView 子类作为 QTreeWidget 一样)都有一个子“视口”,它实际上显示了将要滚动的内容,并且该视口有时会被某些像素转换,例如在显示标题时。

原来的功能应该改成这样:

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

但我们显然不需要,因为所有鼠标事件也有一个 globalPos() 已经映射到全局坐标。

最后,虽然在某些情况下您的方法是理想的(通常是非常简单的使用临时实例即时重新实现),但如果您要为一个整个程序这样做应该考虑四个方面:

  • 这是个坏主意;
  • 您不应该那样做,而应该使用子类;
  • 你真的不应该这样做,除非你真的知道你在做什么以及为什么;
  • 这是一个真的坏主意;

如果您认为遵循这种方法可能会使事情变得轻松,对不起,但您错了:在某些时候您的程序会变得过于复杂,所有这些匿名只会使您的程序非常困难阅读和调试。

与其寻找复杂和非正统的方法以不应该做的方式做某事,不如学会以正确的方式做。

进一步说明,monkey patching 与 PyQt 有一个重要问题:Qt 使用函数缓存来加快速度并使用更少的资源,如果首先使用已经存在的基本方法调用函数,monkey patching 将不起作用全部,因为第一个原始方法总是会在之后被调用。

所以,慢慢来,学习如何创建子类,你不仅会发现它们比看起来更容易,而且会显着改进你的编码。

【讨论】:

    最近更新 更多