在 Qt 中使用鼠标中键单击并拖动整个 window

Move entire window with Middle Mouse Click & Drag in Qt

我有一个包含许多子部件的 Qt 程序。我希望用户能够通过单击鼠标中键并四处拖动来移动 window,当没有子项时它可以正常工作,如下所示:

class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.startPos = QtCore.QPoint()

    def mousePressEvent(self, event):
        self.startPos = event.pos()
        super(MainWindow, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.MiddleButton:
            delta = event.pos() - self.startPos
            self.move(self.pos() + delta)
            event.accept()
        super(MainWindow, self).mouseMoveEvent(event)

但是,当我用所有子部件及其子部件等填充我的部件时。如果我单击 margin/padding 区域,而不是小部件中的其他任何地方,我只会捕获该事件。

有没有办法使鼠标中键拖动事件可用于子控件?

当鼠标事件被与其关联的小部件拦截时,它们会被自动接受并且不会转发给父小部件。虽然大多数小部件会忽略中间点击,但有些小部件实际上会忽略,因此您的主 window 永远不会收到它们。

解决方案是在 QApplication 上安装事件过滤器,这样您感兴趣的任何鼠标事件在发送到任何可能的目标小部件之前都会被过滤:

class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        # just some test widgets
        layout = QtWidgets.QGridLayout(self)
        for row in range(4):
            for col in range(4):
                layout.addWidget(QtWidgets.QPushButton('Button'), row, col)
        layout.addWidget(QtWidgets.QTextEdit(), 
            layout.rowCount(), 0, 1, layout.columnCount())

        self.startPos = None
        # install the event filter on the application
        QtWidgets.QApplication.instance().installEventFilter(self)

    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.MouseButtonPress and 
            event.button() == QtCore.Qt.MiddleButton):
                self.startPos = event.pos()
                return True
        elif event.type() == QtCore.QEvent.MouseMove and self.startPos is not None:
            self.move(self.pos() + event.pos() - self.startPos)
            return True
        elif event.type() == QtCore.QEvent.MouseButtonRelease and self.startPos is not None:
            self.startPos = None
            return True
        return super(MainWindow, self).eventFilter(source, event)