PyQt5 可拖动无框 window
PyQt5 draggable frameless window
我找到了一个在无框 window 上设置边框的示例,但是它不可拖动。如何使无框 window 可拖动?特别是如果我能看到一个例子,那就太棒了。这是我的示例代码(通常代码较长,这就是为什么有很多库只是不介意它们);
from PyQt5.QtWidgets import (QMessageBox,QApplication, QWidget, QToolTip, QPushButton,
QDesktopWidget, QMainWindow, QAction, qApp, QToolBar, QVBoxLayout,
QComboBox,QLabel,QLineEdit,QGridLayout,QMenuBar,QMenu,QStatusBar,
QTextEdit,QDialog,QFrame,QProgressBar
)
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtGui import QIcon,QFont,QPixmap,QPalette
from PyQt5.QtCore import QCoreApplication, Qt,QBasicTimer
import sys
class cssden(QMainWindow):
def __init__(self):
super().__init__()
self.mwidget = QMainWindow(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
#size
self.setFixedSize(320, 450)
self.center
#label
self.lbl = QLabel(self)
self.lbl.setText("test")
self.lbl.setStyleSheet("background-color: rgb(0,0,0);"
"border: 1px solid red;"
"color: rgb(255,255,255);"
"font: bold italic 20pt 'Times New Roman';")
self.lbl.setGeometry(5,5,60,40)
self.show()
#center
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
app = QApplication(sys.argv)
app.setStyleSheet("QMainWindow{background-color: darkgray;border: 1px solid black}")
ex = cssden()
sys.exit(app.exec_())
您需要自己处理鼠标事件。
- 我们需要在
mousePressEvent
上添加一个事件,它将保留我们上次单击 window 的位置
- 然后,我们将添加一个
mouseMoveEvent
,它将计算最后一次单击的点与当前鼠标位置之间的距离。我们将根据这个距离移动window。
这是固定码:
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
class cssden(QMainWindow):
def __init__(self):
super().__init__()
# <MainWindow Properties>
self.setFixedSize(320, 450)
self.setStyleSheet("QMainWindow{background-color: darkgray;border: 1px solid black}")
self.setWindowFlags(Qt.FramelessWindowHint)
self.center()
# </MainWindow Properties>
# <Label Properties>
self.lbl = QLabel(self)
self.lbl.setText("test")
self.lbl.setStyleSheet("QLabel{background-color: rgb(0,0,0); border: 1px solid red; color: rgb(255,255,255); font: bold italic 20pt 'Times New Roman';}")
self.lbl.setGeometry(5, 5, 60, 40)
# </Label Properties>
self.oldPos = self.pos()
self.show()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def mousePressEvent(self, event):
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
delta = QPoint (event.globalPos() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPos()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = cssden()
sys.exit(app.exec_())
这里是可拖动且可调整大小的无框window
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class movable_label(QLabel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.setStyleSheet("background-color: #ccc")
self.setMinimumHeight(30)
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
if self.parent.press_control == 0:
self.pos = e.pos()
self.main_pos = self.parent.pos()
super().mousePressEvent(e)
def mouseMoveEvent(self, e):
if self.parent.cursor().shape() == Qt.ArrowCursor:
self.last_pos = e.pos() - self.pos
self.main_pos += self.last_pos
self.parent.move(self.main_pos)
super(movable_label, self).mouseMoveEvent(e)
class main(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.FramelessWindowHint)
self.central = QWidget()
self.vbox = QVBoxLayout(self.central)
self.vbox.addWidget(movable_label(self))
self.vbox.addWidget(QPushButton("Click"))
self.vbox.setAlignment(Qt.AlignTop)
self.vbox.setSpacing(0)
self.vbox.setContentsMargins(0,0,0,0)
self.press_control = 0
self.setCentralWidget(self.central)
self.resize(800,500)
self.show()
def eventFilter(self, obj, e):
#hovermoveevent
if e.type() == 129:
if self.press_control == 0:
self.pos_control(e)#cursor position control for cursor shape setup
#mousepressevent
if e.type() == 2:
self.press_control = 1
self.origin = self.mapToGlobal(e.pos())
self.ori_geo = self.geometry()
#mousereleaseevent
if e.type() == 3:
self.press_control = 0
self.pos_control(e)
#mosuemoveevent
if e.type() == 5:
if self.cursor().shape() != Qt.ArrowCursor:
self.resizing(self.origin, e, self.ori_geo, self.value)
return True
def pos_control(self, e):
rect = self.rect()
top_left = rect.topLeft()
top_right = rect.topRight()
bottom_left = rect.bottomLeft()
bottom_right = rect.bottomRight()
pos = e.pos()
#top catch
if pos in QRect(QPoint(top_left.x()+5,top_left.y()), QPoint(top_right.x()-5,top_right.y()+5)):
self.setCursor(Qt.SizeVerCursor)
self.value = 1
#bottom catch
elif pos in QRect(QPoint(bottom_left.x()+5,bottom_left.y()), QPoint(bottom_right.x()-5,bottom_right.y()-5)):
self.setCursor(Qt.SizeVerCursor)
self.value = 2
#right catch
elif pos in QRect(QPoint(top_right.x()-5,top_right.y()+5), QPoint(bottom_right.x(),bottom_right.y()-5)):
self.setCursor(Qt.SizeHorCursor)
self.value = 3
#left catch
elif pos in QRect(QPoint(top_left.x()+5,top_left.y()+5), QPoint(bottom_left.x(),bottom_left.y()-5)):
self.setCursor(Qt.SizeHorCursor)
self.value = 4
#top_right catch
elif pos in QRect(QPoint(top_right.x(),top_right.y()), QPoint(top_right.x()-5,top_right.y()+5)):
self.setCursor(Qt.SizeBDiagCursor)
self.value = 5
#botom_left catch
elif pos in QRect(QPoint(bottom_left.x(),bottom_left.y()), QPoint(bottom_left.x()+5,bottom_left.y()-5)):
self.setCursor(Qt.SizeBDiagCursor)
self.value = 6
#top_left catch
elif pos in QRect(QPoint(top_left.x(),top_left.y()), QPoint(top_left.x()+5,top_left.y()+5)):
self.setCursor(Qt.SizeFDiagCursor)
self.value = 7
#bottom_right catch
elif pos in QRect(QPoint(bottom_right.x(),bottom_right.y()), QPoint(bottom_right.x()-5,bottom_right.y()-5)):
self.setCursor(Qt.SizeFDiagCursor)
self.value = 8
#default
else:
self.setCursor(Qt.ArrowCursor)
def resizing(self, ori, e, geo, value):
#top_resize
if self.value == 1:
last = self.mapToGlobal(e.pos())-ori
first = geo.height()
first -= last.y()
Y = geo.y()
Y += last.y()
if first > self.minimumHeight():
self.setGeometry(geo.x(), Y, geo.width(), first)
#bottom_resize
if self.value == 2:
last = self.mapToGlobal(e.pos())-ori
first = geo.height()
first += last.y()
self.resize(geo.width(), first)
#right_resize
if self.value == 3:
last = self.mapToGlobal(e.pos())-ori
first = geo.width()
first += last.x()
self.resize(first, geo.height())
#left_resize
if self.value == 4:
last = self.mapToGlobal(e.pos())-ori
first = geo.width()
first -= last.x()
X = geo.x()
X += last.x()
if first > self.minimumWidth():
self.setGeometry(X, geo.y(), first, geo.height())
#top_right_resize
if self.value == 5:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_Y = geo.y()
first_width += last.x()
first_height -= last.y()
first_Y += last.y()
if first_height > self.minimumHeight():
self.setGeometry(geo.x(), first_Y, first_width, first_height)
#bottom_right_resize
if self.value == 6:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_X = geo.x()
first_width -= last.x()
first_height += last.y()
first_X += last.x()
if first_width > self.minimumWidth():
self.setGeometry(first_X, geo.y(), first_width, first_height)
#top_left_resize
if self.value == 7:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_X = geo.x()
first_Y = geo.y()
first_width -= last.x()
first_height -= last.y()
first_X += last.x()
first_Y += last.y()
if first_height > self.minimumHeight() and first_width > self.minimumWidth():
self.setGeometry(first_X, first_Y, first_width, first_height)
#bottom_right_resize
if self.value == 8:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_width += last.x()
first_height += last.y()
self.setGeometry(geo.x(), geo.y(), first_width, first_height)
app = QApplication([])
window = main()
window.installEventFilter(window)
app.exec()
添加到 Elad Joseph 的回答中,需要为 PyQt6 更新以下事件:
def mousePressEvent(self, event):
self.oldPos = event.globalPosition().toPoint()
def mouseMoveEvent(self, event):
delta = QPoint (event.globalPosition().toPoint() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPosition().toPoint()
我在GitHub上发布了一个pyqt frameless window repo,在Windows系统上由pywin32
实现,在Unix系统上由xcffib
实现。
我找到了一个在无框 window 上设置边框的示例,但是它不可拖动。如何使无框 window 可拖动?特别是如果我能看到一个例子,那就太棒了。这是我的示例代码(通常代码较长,这就是为什么有很多库只是不介意它们);
from PyQt5.QtWidgets import (QMessageBox,QApplication, QWidget, QToolTip, QPushButton,
QDesktopWidget, QMainWindow, QAction, qApp, QToolBar, QVBoxLayout,
QComboBox,QLabel,QLineEdit,QGridLayout,QMenuBar,QMenu,QStatusBar,
QTextEdit,QDialog,QFrame,QProgressBar
)
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtGui import QIcon,QFont,QPixmap,QPalette
from PyQt5.QtCore import QCoreApplication, Qt,QBasicTimer
import sys
class cssden(QMainWindow):
def __init__(self):
super().__init__()
self.mwidget = QMainWindow(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
#size
self.setFixedSize(320, 450)
self.center
#label
self.lbl = QLabel(self)
self.lbl.setText("test")
self.lbl.setStyleSheet("background-color: rgb(0,0,0);"
"border: 1px solid red;"
"color: rgb(255,255,255);"
"font: bold italic 20pt 'Times New Roman';")
self.lbl.setGeometry(5,5,60,40)
self.show()
#center
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
app = QApplication(sys.argv)
app.setStyleSheet("QMainWindow{background-color: darkgray;border: 1px solid black}")
ex = cssden()
sys.exit(app.exec_())
您需要自己处理鼠标事件。
- 我们需要在
mousePressEvent
上添加一个事件,它将保留我们上次单击 window 的位置
- 然后,我们将添加一个
mouseMoveEvent
,它将计算最后一次单击的点与当前鼠标位置之间的距离。我们将根据这个距离移动window。
这是固定码:
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
class cssden(QMainWindow):
def __init__(self):
super().__init__()
# <MainWindow Properties>
self.setFixedSize(320, 450)
self.setStyleSheet("QMainWindow{background-color: darkgray;border: 1px solid black}")
self.setWindowFlags(Qt.FramelessWindowHint)
self.center()
# </MainWindow Properties>
# <Label Properties>
self.lbl = QLabel(self)
self.lbl.setText("test")
self.lbl.setStyleSheet("QLabel{background-color: rgb(0,0,0); border: 1px solid red; color: rgb(255,255,255); font: bold italic 20pt 'Times New Roman';}")
self.lbl.setGeometry(5, 5, 60, 40)
# </Label Properties>
self.oldPos = self.pos()
self.show()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def mousePressEvent(self, event):
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
delta = QPoint (event.globalPos() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPos()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = cssden()
sys.exit(app.exec_())
这里是可拖动且可调整大小的无框window
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class movable_label(QLabel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.setStyleSheet("background-color: #ccc")
self.setMinimumHeight(30)
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
if self.parent.press_control == 0:
self.pos = e.pos()
self.main_pos = self.parent.pos()
super().mousePressEvent(e)
def mouseMoveEvent(self, e):
if self.parent.cursor().shape() == Qt.ArrowCursor:
self.last_pos = e.pos() - self.pos
self.main_pos += self.last_pos
self.parent.move(self.main_pos)
super(movable_label, self).mouseMoveEvent(e)
class main(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.FramelessWindowHint)
self.central = QWidget()
self.vbox = QVBoxLayout(self.central)
self.vbox.addWidget(movable_label(self))
self.vbox.addWidget(QPushButton("Click"))
self.vbox.setAlignment(Qt.AlignTop)
self.vbox.setSpacing(0)
self.vbox.setContentsMargins(0,0,0,0)
self.press_control = 0
self.setCentralWidget(self.central)
self.resize(800,500)
self.show()
def eventFilter(self, obj, e):
#hovermoveevent
if e.type() == 129:
if self.press_control == 0:
self.pos_control(e)#cursor position control for cursor shape setup
#mousepressevent
if e.type() == 2:
self.press_control = 1
self.origin = self.mapToGlobal(e.pos())
self.ori_geo = self.geometry()
#mousereleaseevent
if e.type() == 3:
self.press_control = 0
self.pos_control(e)
#mosuemoveevent
if e.type() == 5:
if self.cursor().shape() != Qt.ArrowCursor:
self.resizing(self.origin, e, self.ori_geo, self.value)
return True
def pos_control(self, e):
rect = self.rect()
top_left = rect.topLeft()
top_right = rect.topRight()
bottom_left = rect.bottomLeft()
bottom_right = rect.bottomRight()
pos = e.pos()
#top catch
if pos in QRect(QPoint(top_left.x()+5,top_left.y()), QPoint(top_right.x()-5,top_right.y()+5)):
self.setCursor(Qt.SizeVerCursor)
self.value = 1
#bottom catch
elif pos in QRect(QPoint(bottom_left.x()+5,bottom_left.y()), QPoint(bottom_right.x()-5,bottom_right.y()-5)):
self.setCursor(Qt.SizeVerCursor)
self.value = 2
#right catch
elif pos in QRect(QPoint(top_right.x()-5,top_right.y()+5), QPoint(bottom_right.x(),bottom_right.y()-5)):
self.setCursor(Qt.SizeHorCursor)
self.value = 3
#left catch
elif pos in QRect(QPoint(top_left.x()+5,top_left.y()+5), QPoint(bottom_left.x(),bottom_left.y()-5)):
self.setCursor(Qt.SizeHorCursor)
self.value = 4
#top_right catch
elif pos in QRect(QPoint(top_right.x(),top_right.y()), QPoint(top_right.x()-5,top_right.y()+5)):
self.setCursor(Qt.SizeBDiagCursor)
self.value = 5
#botom_left catch
elif pos in QRect(QPoint(bottom_left.x(),bottom_left.y()), QPoint(bottom_left.x()+5,bottom_left.y()-5)):
self.setCursor(Qt.SizeBDiagCursor)
self.value = 6
#top_left catch
elif pos in QRect(QPoint(top_left.x(),top_left.y()), QPoint(top_left.x()+5,top_left.y()+5)):
self.setCursor(Qt.SizeFDiagCursor)
self.value = 7
#bottom_right catch
elif pos in QRect(QPoint(bottom_right.x(),bottom_right.y()), QPoint(bottom_right.x()-5,bottom_right.y()-5)):
self.setCursor(Qt.SizeFDiagCursor)
self.value = 8
#default
else:
self.setCursor(Qt.ArrowCursor)
def resizing(self, ori, e, geo, value):
#top_resize
if self.value == 1:
last = self.mapToGlobal(e.pos())-ori
first = geo.height()
first -= last.y()
Y = geo.y()
Y += last.y()
if first > self.minimumHeight():
self.setGeometry(geo.x(), Y, geo.width(), first)
#bottom_resize
if self.value == 2:
last = self.mapToGlobal(e.pos())-ori
first = geo.height()
first += last.y()
self.resize(geo.width(), first)
#right_resize
if self.value == 3:
last = self.mapToGlobal(e.pos())-ori
first = geo.width()
first += last.x()
self.resize(first, geo.height())
#left_resize
if self.value == 4:
last = self.mapToGlobal(e.pos())-ori
first = geo.width()
first -= last.x()
X = geo.x()
X += last.x()
if first > self.minimumWidth():
self.setGeometry(X, geo.y(), first, geo.height())
#top_right_resize
if self.value == 5:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_Y = geo.y()
first_width += last.x()
first_height -= last.y()
first_Y += last.y()
if first_height > self.minimumHeight():
self.setGeometry(geo.x(), first_Y, first_width, first_height)
#bottom_right_resize
if self.value == 6:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_X = geo.x()
first_width -= last.x()
first_height += last.y()
first_X += last.x()
if first_width > self.minimumWidth():
self.setGeometry(first_X, geo.y(), first_width, first_height)
#top_left_resize
if self.value == 7:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_X = geo.x()
first_Y = geo.y()
first_width -= last.x()
first_height -= last.y()
first_X += last.x()
first_Y += last.y()
if first_height > self.minimumHeight() and first_width > self.minimumWidth():
self.setGeometry(first_X, first_Y, first_width, first_height)
#bottom_right_resize
if self.value == 8:
last = self.mapToGlobal(e.pos())-ori
first_width = geo.width()
first_height = geo.height()
first_width += last.x()
first_height += last.y()
self.setGeometry(geo.x(), geo.y(), first_width, first_height)
app = QApplication([])
window = main()
window.installEventFilter(window)
app.exec()
添加到 Elad Joseph 的回答中,需要为 PyQt6 更新以下事件:
def mousePressEvent(self, event):
self.oldPos = event.globalPosition().toPoint()
def mouseMoveEvent(self, event):
delta = QPoint (event.globalPosition().toPoint() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPosition().toPoint()
我在GitHub上发布了一个pyqt frameless window repo,在Windows系统上由pywin32
实现,在Unix系统上由xcffib
实现。