'PyQt5 draggable frameless window
I found an example to set borders on a frameless window, however it's not draggable. How can I make a frameless window draggable? Especially if I can see an example it'll be awesome. Here is my example code(normally the code is longer, that's why there are much libraries just don't mind them);
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_())
Solution 1:[1]
You need to handle the mouse events yourself.
- We will need to add an event on
mousePressEvent
, which will keep the place where we last clicked on the window - Then, we will add a
mouseMoveEvent
, which will calculate the distance between the last clicked point and the current mouse location. We will move the window according to this distance.
This is the fixed code:
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_())
Solution 2:[2]
here is dragable and resizable frameless 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()
Solution 3:[3]
Adding to Elad Joseph's answer, the following events need to be updated for 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()
Solution 4:[4]
I released a pyqt frameless window repo on GitHub, which is implemented by pywin32
on Windows system and xcffib
on Unix system.
Here is the repo link: https://github.com/zhiyiYo/PyQt-Frameless-Window
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | DRPK |
Solution 2 | |
Solution 3 | fastlan |
Solution 4 | zhiyi |