PyQt5 拖动框架时移动 window
PyQt5 move window when dragging frame
希望你们在这些困难时期都过得很好。
我在 window 的顶部添加了一个框架,目的是让它看起来像大多数应用程序顶部的栏(请原谅我,我不知道我的行话),您可以在其中单击它并通过移动鼠标来移动屏幕。到目前为止,我只得到了顶部的框架,并研究了如何实现这个结果,我看过 this one 这样的例子,但它只适用于当你点击 [=] 上的任何地方时21=],而我正在寻找一种仅在您单击顶部框架时才移动屏幕的方法。
我的代码如下
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowModality(QtCore.Qt.NonModal)
MainWindow.resize(909, 544)
MainWindow.setAnimated(True)
MainWindow.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.centralwidget = QtWidgets.QWidget(MainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
self.centralwidget.setObjectName("centralwidget")
self.backgroundFrame = QtWidgets.QFrame(self.centralwidget)
self.backgroundFrame.setEnabled(True)
self.backgroundFrame.setGeometry(QtCore.QRect(-1, -1, 941, 551))
self.backgroundFrame.setStyleSheet("QLabel {\n"
"font-family: \"Microsoft Sans Serif\", sans-serif;\n"
"color: white;\n"
"}\n"
"\n"
"#backgroundFrame\n"
"{\n"
"background-color: #060321;\n"
"}\n"
"\n"
"#infoBar\n"
"{\n"
"background-color: #0F0334;\n"
"}")
self.backgroundFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.backgroundFrame.setFrameShadow(QtWidgets.QFrame.Raised)
self.backgroundFrame.setObjectName("backgroundFrame")
self.infoBar = QtWidgets.QFrame(self.backgroundFrame)
self.infoBar.setGeometry(QtCore.QRect(-10, -10, 921, 71))
self.infoBar.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.infoBar.setFrameShadow(QtWidgets.QFrame.Raised)
self.infoBar.setObjectName("infoBar")
self.exitButton = QtWidgets.QPushButton(self.infoBar)
self.exitButton.setGeometry(QtCore.QRect(874, 26, 31, 31))
self.exitButton.setText("")
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Resources/Exit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.exitButton.setIcon(icon)
self.exitButton.setIconSize(QtCore.QSize(32, 32))
self.exitButton.setObjectName("exitButton")
self.pushButton = QtWidgets.QPushButton(self.infoBar)
self.pushButton.setGeometry(QtCore.QRect(827, 30, 31, 31))
self.pushButton.setText("")
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap("Resources/Minimize.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton.setIcon(icon1)
self.pushButton.setIconSize(QtCore.QSize(32, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.infoBar)
self.pushButton_2.setGeometry(QtCore.QRect(26, 26, 34, 31))
self.pushButton_2.setText("")
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap("Resources/Settings.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_2.setIcon(icon2)
self.pushButton_2.setIconSize(QtCore.QSize(32, 32))
self.pushButton_2.setObjectName("pushButton_2")
self.label = QtWidgets.QLabel(self.infoBar)
self.label.setGeometry(QtCore.QRect(390, 23, 145, 40))
self.label.setText("")
self.label.setPixmap(QtGui.QPixmap("Resources/Titan Window.png"))
self.label.setObjectName("label")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Titan"))
def mousePressEvent(self, event):
self.offset = event.pos()
def mouseMoveEvent(self, event):
x=event.globalX()
y=event.globalY()
x_w = self.offset.x()
y_w = self.offset.y()
self.move(x-x_w, y-y_w)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
感谢您的帮助,希望您和您的朋友及家人在此期间保持安全<3
你的问题的 来源 是你可能正在编辑由 pyuic
生成的代码(或者你试图模仿它的行为),这是永远 不应该做的事情。这些文件只能作为模块导入,不得无故修改。要了解如何正确使用通过 Designer 创建的 ui
文件,请阅读 using Designer.
上的更多信息
采用这种方法的结果是 mousePressEvent
和 mouseMoveEvent
将 永远不会被调用 。
原因是,在您的代码中,那些方法属于 Ui_MainWindow
class 实例 (ui
创建接近尾声),这只不过是一个基本的 Python object
subclass:它几乎什么都不做,除了 build UI on 其 setupUi()
中的 QMainWindow 实例; 那个 QMainWindow 实例是应该接收那些事件的实例,那些方法应该在那里实现。
或者,至少,理论上。那是因为您要捕获的 mousePressEvent 和 mouseMoveEvent 实际上应该是从 infoBar
对象接收的,而不是主要的 window.
一个常见的解决方案是子class 将接收这些事件并在那里实现这些方法的小部件,但是由于您可能使用的是在 Designer 中创建的 ui,它不像似乎(稍后会详细介绍)。
最直接的方法,就是在watched widget上安装一个事件过滤器,这样我们就可以过滤那些事件并做出反应如果我们愿意的话给他们。
在下面的示例中,我假设您使用 pyuic
重新创建了文件,并将其命名为 ui_mainwindow.py
:
from PyQt5 import QtCore, QtGui, QtWidgets
from ui_mainwindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.offset = None
# install the event filter on the infoBar widget
self.infoBar.installEventFilter(self)
def eventFilter(self, source, event):
if source == self.infoBar:
if event.type() == QtCore.QEvent.MouseButtonPress:
self.offset = event.pos()
elif event.type() == QtCore.QEvent.MouseMove and self.offset is not None:
# no need for complex computations: just use the offset to compute
# "delta" position, and add that to the current one
self.move(self.pos() - self.offset + event.pos())
# return True to tell Qt that the event has been accepted and
# should not be processed any further
return True
elif event.type() == QtCore.QEvent.MouseButtonRelease:
self.offset = None
# let Qt process any other event
return super().eventFilter(source, event)
关于上面提到的设计器中子class的使用:可以在设计器中创建自定义子class和"use"。您必须在您的代码中创建一个 subclass,然后将其最接近的祖先(在您的情况下为 QFrame)添加到您的 ui,这将用作一种占位符,然后 推广 它。做一些关于 "promoted widgets" 的研究以了解更多信息。
一个重要的建议:使用固定大小和位置不是一个好的选择,layout managers 应该 always 是首选。这样做的原因是,您在计算机上看到的内容几乎总是与其他人的显示方式截然不同(最常见的原因之一是:不同的屏幕尺寸、默认设置、DPI、字体等),这可能会导致您的 GUI 变得不可用,因为小部件可能变得不可见、重叠或不可读。在非常非常少见的情况下可以使用固定的几何图形(但是当程序员意识到他们需要这些图形时,可能是因为 code/ui 设计不当),而这肯定不是你的情况。
例如,如果我尝试将 window 的大小调整为比原始大小更小,右上角的按钮将无法访问。此外,右侧靠近 "exit" 的按钮未正确对齐。如果您使用布局管理器(例如 infoBar
小部件上的水平布局),它会自动解决所有这些问题。
希望你们在这些困难时期都过得很好。
我在 window 的顶部添加了一个框架,目的是让它看起来像大多数应用程序顶部的栏(请原谅我,我不知道我的行话),您可以在其中单击它并通过移动鼠标来移动屏幕。到目前为止,我只得到了顶部的框架,并研究了如何实现这个结果,我看过 this one 这样的例子,但它只适用于当你点击 [=] 上的任何地方时21=],而我正在寻找一种仅在您单击顶部框架时才移动屏幕的方法。
我的代码如下
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowModality(QtCore.Qt.NonModal)
MainWindow.resize(909, 544)
MainWindow.setAnimated(True)
MainWindow.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.centralwidget = QtWidgets.QWidget(MainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
self.centralwidget.setObjectName("centralwidget")
self.backgroundFrame = QtWidgets.QFrame(self.centralwidget)
self.backgroundFrame.setEnabled(True)
self.backgroundFrame.setGeometry(QtCore.QRect(-1, -1, 941, 551))
self.backgroundFrame.setStyleSheet("QLabel {\n"
"font-family: \"Microsoft Sans Serif\", sans-serif;\n"
"color: white;\n"
"}\n"
"\n"
"#backgroundFrame\n"
"{\n"
"background-color: #060321;\n"
"}\n"
"\n"
"#infoBar\n"
"{\n"
"background-color: #0F0334;\n"
"}")
self.backgroundFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.backgroundFrame.setFrameShadow(QtWidgets.QFrame.Raised)
self.backgroundFrame.setObjectName("backgroundFrame")
self.infoBar = QtWidgets.QFrame(self.backgroundFrame)
self.infoBar.setGeometry(QtCore.QRect(-10, -10, 921, 71))
self.infoBar.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.infoBar.setFrameShadow(QtWidgets.QFrame.Raised)
self.infoBar.setObjectName("infoBar")
self.exitButton = QtWidgets.QPushButton(self.infoBar)
self.exitButton.setGeometry(QtCore.QRect(874, 26, 31, 31))
self.exitButton.setText("")
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Resources/Exit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.exitButton.setIcon(icon)
self.exitButton.setIconSize(QtCore.QSize(32, 32))
self.exitButton.setObjectName("exitButton")
self.pushButton = QtWidgets.QPushButton(self.infoBar)
self.pushButton.setGeometry(QtCore.QRect(827, 30, 31, 31))
self.pushButton.setText("")
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap("Resources/Minimize.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton.setIcon(icon1)
self.pushButton.setIconSize(QtCore.QSize(32, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.infoBar)
self.pushButton_2.setGeometry(QtCore.QRect(26, 26, 34, 31))
self.pushButton_2.setText("")
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap("Resources/Settings.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_2.setIcon(icon2)
self.pushButton_2.setIconSize(QtCore.QSize(32, 32))
self.pushButton_2.setObjectName("pushButton_2")
self.label = QtWidgets.QLabel(self.infoBar)
self.label.setGeometry(QtCore.QRect(390, 23, 145, 40))
self.label.setText("")
self.label.setPixmap(QtGui.QPixmap("Resources/Titan Window.png"))
self.label.setObjectName("label")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Titan"))
def mousePressEvent(self, event):
self.offset = event.pos()
def mouseMoveEvent(self, event):
x=event.globalX()
y=event.globalY()
x_w = self.offset.x()
y_w = self.offset.y()
self.move(x-x_w, y-y_w)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
感谢您的帮助,希望您和您的朋友及家人在此期间保持安全<3
你的问题的 来源 是你可能正在编辑由 pyuic
生成的代码(或者你试图模仿它的行为),这是永远 不应该做的事情。这些文件只能作为模块导入,不得无故修改。要了解如何正确使用通过 Designer 创建的 ui
文件,请阅读 using Designer.
采用这种方法的结果是 mousePressEvent
和 mouseMoveEvent
将 永远不会被调用 。
原因是,在您的代码中,那些方法属于 Ui_MainWindow
class 实例 (ui
创建接近尾声),这只不过是一个基本的 Python object
subclass:它几乎什么都不做,除了 build UI on 其 setupUi()
中的 QMainWindow 实例; 那个 QMainWindow 实例是应该接收那些事件的实例,那些方法应该在那里实现。
或者,至少,理论上。那是因为您要捕获的 mousePressEvent 和 mouseMoveEvent 实际上应该是从 infoBar
对象接收的,而不是主要的 window.
一个常见的解决方案是子class 将接收这些事件并在那里实现这些方法的小部件,但是由于您可能使用的是在 Designer 中创建的 ui,它不像似乎(稍后会详细介绍)。
最直接的方法,就是在watched widget上安装一个事件过滤器,这样我们就可以过滤那些事件并做出反应如果我们愿意的话给他们。
在下面的示例中,我假设您使用 pyuic
重新创建了文件,并将其命名为 ui_mainwindow.py
:
from PyQt5 import QtCore, QtGui, QtWidgets
from ui_mainwindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.offset = None
# install the event filter on the infoBar widget
self.infoBar.installEventFilter(self)
def eventFilter(self, source, event):
if source == self.infoBar:
if event.type() == QtCore.QEvent.MouseButtonPress:
self.offset = event.pos()
elif event.type() == QtCore.QEvent.MouseMove and self.offset is not None:
# no need for complex computations: just use the offset to compute
# "delta" position, and add that to the current one
self.move(self.pos() - self.offset + event.pos())
# return True to tell Qt that the event has been accepted and
# should not be processed any further
return True
elif event.type() == QtCore.QEvent.MouseButtonRelease:
self.offset = None
# let Qt process any other event
return super().eventFilter(source, event)
关于上面提到的设计器中子class的使用:可以在设计器中创建自定义子class和"use"。您必须在您的代码中创建一个 subclass,然后将其最接近的祖先(在您的情况下为 QFrame)添加到您的 ui,这将用作一种占位符,然后 推广 它。做一些关于 "promoted widgets" 的研究以了解更多信息。
一个重要的建议:使用固定大小和位置不是一个好的选择,layout managers 应该 always 是首选。这样做的原因是,您在计算机上看到的内容几乎总是与其他人的显示方式截然不同(最常见的原因之一是:不同的屏幕尺寸、默认设置、DPI、字体等),这可能会导致您的 GUI 变得不可用,因为小部件可能变得不可见、重叠或不可读。在非常非常少见的情况下可以使用固定的几何图形(但是当程序员意识到他们需要这些图形时,可能是因为 code/ui 设计不当),而这肯定不是你的情况。
例如,如果我尝试将 window 的大小调整为比原始大小更小,右上角的按钮将无法访问。此外,右侧靠近 "exit" 的按钮未正确对齐。如果您使用布局管理器(例如 infoBar
小部件上的水平布局),它会自动解决所有这些问题。