移动PyQt5应用程序 - QPushButton问题

时间:2016-04-02 14:53:53

标签: qt python-3.x pyqt5

我正在开发一个简单的应用程序,用于OSX上的Python3和PyQt5的茶具计时器。出于美观原因,我决定拆下窗框。为了保持窗口可移动,我重载了mousePressEvent()和mouseMoveEvent()处理程序。

但是当鼠标移动到其中一个按钮(QPushButton())上移动窗口时,我遇到了问题。然后光标跳转到最后一个单击位置,窗口从那里移动。有没有人知道为什么会这样?有趣的是,单击并移动QLabel()对象时,不会出现相同的问题......

如果你想尝试一下,这是一个示例代码:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

## ===============================================================
## IMPORTS

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *


## ===============================================================
## CONSTANTS

WINDOW_WIDTH = 690
WINDOW_HEIGHT = 435


## ===============================================================
## CLASSES


class Form(QWidget):
    def __init__(self, parent=None):
        super().__init__()

        self.windowPos = QPoint()       # Maybe not necessary when button click and window movement are seperated

        # Declare and specify UI elements
        self.timerLabel = QLabel("00:00")       # Might have to change data type here
        self.timerLabel.setObjectName("timerLabel")

        self.infoLabel = QLabel("No tea selected")
        self.infoLabel.setObjectName("infoLabel")

        self.teaOneButton = QPushButton("Tea One")
        self.teaOneButton.setObjectName("teaOneButton")

        self.teaTwoButton = QPushButton("Tea Two")
        self.teaTwoButton.setObjectName("teaTwoButton")

        # Arrange UI elements in a layout
        grid = QGridLayout()
        self.setLayout(grid)        # Set the QGridLayout as the window's main layout
        grid.setSpacing(0)      # Spacing between widgets - does not work if window is resized
        grid.setContentsMargins(4, 4, 4, 4)
        grid.addWidget(self.timerLabel, 0, 0, 1, -1, Qt.AlignHCenter)       # http://doc.qt.io/qt-5/qgridlayout.html#addWidget
        grid.addWidget(self.infoLabel, 1, 0, 1, -1, Qt.AlignHCenter)
        grid.addWidget(self.teaOneButton, 2, 0)
        grid.addWidget(self.teaTwoButton, 2, 1)

        self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)


    # Arranging window in center of the screen by overloading showEvent method
    def showEvent(self, QShowEvent):
        self.centerOnScreen()


    def centerOnScreen(self):
        screen = QDesktopWidget()
        screenGeom = QRect(screen.screenGeometry(self))

        screenCenterX = screenGeom.center().x()
        screenCenterY = screenGeom.center().y()

        self.move(screenCenterX - self.width() / 2,
                            screenCenterY - self.height() / 2)


    # Overload mouseEvent handlers to make window moveable
    def mousePressEvent(self, QMouseEvent):
        self.windowPos = QMouseEvent.pos()
        self.setCursor(QCursor(Qt.SizeAllCursor))


    def mouseReleaseEvent(self, QMouseEvent):
        self.setCursor(QCursor(Qt.ArrowCursor))


    def mouseMoveEvent(self, QMouseEvent):
        # print (self.childAt(QMouseEvent.pos()) == QLabel)
        pos = QPoint(QMouseEvent.globalPos())
        self.window().move(pos - self.windowPos)



## ===============================================================
## MAIN LOOP

if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)

    screen = Form()
    screen.setWindowFlags(Qt.FramelessWindowHint)
    screen.show()

    sys.exit(app.exec_())

2 个答案:

答案 0 :(得分:0)

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

您确定导入的成员没有命名空间冲突吗?

答案 1 :(得分:0)

好吧,我想我可能找到了解决问题的方法。

在我看来,问题可能是QPushButtons处理mouseMoveEvents的方式与例如QLabel对象不同。对我而言,直观上是有道理的,因为按钮对象可能需要与其他不可点击的对象不同的鼠标事件处理。

所以我所做的是尝试为QPushButton实现一个子类,重载mouseMoveEvent处理程序。起初我以为我可能会设置处理程序忽略该事件,以便将其委托给父窗口小部件。这并没有改变任何事情。它实际上可能是一直在发生的事情。因为当我在事件处理函数中实现某种代码时,该按钮不再用作移动窗口的源。亲眼看看:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

## ===============================================================
## IMPORTS

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *


## ===============================================================
## CONSTANTS

WINDOW_WIDTH = 690
WINDOW_HEIGHT = 435


## ===============================================================
## CLASSES

class UnmoveableButton(QPushButton):
    def __init__(self, text=""):
        super().__init__(text)

    # This event function is overloaded in order to avoid the widget from delegating the event up to the parent.
    # This way, the pre-existing functionality is skipped, i.e. the window can no longer be moved while hovering over a button.
    def mouseMoveEvent(self, QMouseEvent):
        pass

class Form(QWidget):
    def __init__(self, parent=None):
        super().__init__()

        self.windowPos = QPoint()       # Maybe not necessary when button click and window movement are seperated

        # Declare and specify UI elements
        self.timerLabel = QLabel("00:00")       # Might have to change data type here
        self.timerLabel.setObjectName("timerLabel")

        self.infoLabel = QLabel("No tea selected")
        self.infoLabel.setObjectName("infoLabel")

        self.teaOneButton = UnmoveableButton("Tea One")
        self.teaOneButton.setObjectName("teaOneButton")

        self.teaTwoButton = UnmoveableButton("Tea Two")
        self.teaTwoButton.setObjectName("teaTwoButton")

        # Arrange UI elements in a layout
        grid = QGridLayout()
        self.setLayout(grid)        # Set the QGridLayout as the window's main layout
        grid.setSpacing(0)      # Spacing between widgets - does not work if window is resized
        grid.setContentsMargins(4, 4, 4, 4)
        grid.addWidget(self.timerLabel, 0, 0, 1, -1, Qt.AlignHCenter)       # http://doc.qt.io/qt-5/qgridlayout.html#addWidget
        grid.addWidget(self.infoLabel, 1, 0, 1, -1, Qt.AlignHCenter)
        grid.addWidget(self.teaOneButton, 2, 0)
        grid.addWidget(self.teaTwoButton, 2, 1)

        self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)


    # Arranging window in center of the screen by overloading showEvent method
    def showEvent(self, QShowEvent):
        self.centerOnScreen()


    def centerOnScreen(self):
        screen = QDesktopWidget()
        screenGeom = QRect(screen.screenGeometry(self))

        screenCenterX = screenGeom.center().x()
        screenCenterY = screenGeom.center().y()

        self.move(screenCenterX - self.width() / 2,
                            screenCenterY - self.height() / 2)


    # Overload mouseEvent handlers to make window moveable
    def mousePressEvent(self, QMouseEvent):
        self.windowPos = QMouseEvent.pos()
        self.setCursor(QCursor(Qt.SizeAllCursor))


    def mouseReleaseEvent(self, QMouseEvent):
        self.setCursor(QCursor(Qt.ArrowCursor))


    def mouseMoveEvent(self, QMouseEvent):
        # print (self.childAt(QMouseEvent.pos()) == QLabel)
        pos = QPoint(QMouseEvent.globalPos())
        self.window().move(pos - self.windowPos)



## ===============================================================
## MAIN LOOP

if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)

    screen = Form()
    screen.setWindowFlags(Qt.FramelessWindowHint)
    screen.show()

    sys.exit(app.exec_())

我认为这可能不是最优雅或最正确的解决方案,但它现在适用于我。如果有人知道如何改进这一点,请告诉我:)。