我正在开发一个简单的应用程序,用于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_())
答案 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_())
我认为这可能不是最优雅或最正确的解决方案,但它现在适用于我。如果有人知道如何改进这一点,请告诉我:)。