Search code examples
pythonqtpyqtpyqt5window

Problem with moving window using custom title bar in PyQt5


I'm trying to build a GUI with a custom title bar using along with setWindowFlags(FramelessWindowHint).

I have successfully created close/minimise buttons etc with their respective functions and they work perfectly (They are not included in my code below). However I haven't had the same success with making the window moveable using my custom title bar.

When I ran the code and attempted to drag the window, the cursor changed to the blue loading swirl then crashed. I also tried running it with debugger and when I tried to drag the window I got the error:

TypeError: unsupported operand type(s) for +: 'QPoint' and 'builtin_function_or_method'

Below is my a part of my code. (It may be a little messy as I cut it out of a larger project.)

Any help to solve my problem will be greatly appreciated, thank you.

from PyQt5 import QtCore, QtGui, QtWidgets, Qt
from PyQt5.QtCore import Qt

WINDOW_SIZE = 0

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1200, 700)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.top_bar = QtWidgets.QWidget(self.centralwidget)
        self.top_bar.setMaximumSize(QtCore.QSize(16777215, 60))
        self.top_bar.setStyleSheet("background-color: rgb(52, 56, 60)")
        self.top_bar.setObjectName("top_bar")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.top_bar)
        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_2.setSpacing(0)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.frame = QtWidgets.QFrame(self.top_bar)
        self.frame.setMinimumSize(QtCore.QSize(80, 60))
        self.frame.setMaximumSize(QtCore.QSize(80, 60))
        self.frame.setStyleSheet("background-color:rgb(30, 34, 38);\n"
                                                       "border: 0px")
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame)
        self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_4.setSpacing(0)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.horizontalLayout_2.addWidget(self.frame)
        self.header_bar = QtWidgets.QFrame(self.top_bar)
        self.header_bar.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.header_bar.setFrameShadow(QtWidgets.QFrame.Raised)
        self.header_bar.setObjectName("header_bar")
        self.horizontalLayout_2.addWidget(self.header_bar)
        self.verticalLayout.addWidget(self.top_bar)
        self.content = QtWidgets.QWidget(self.centralwidget)
        self.content.setStyleSheet("")
        self.content.setObjectName("content")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.content)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout.addWidget(self.content)
        MainWindow.setCentralWidget(self.centralwidget)

        self.header_bar.mouseMoveEvent = self.moveWindow

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

    def mousePressEvent(self, event):
        MainWindow.dragPos = event.globalPos()

    def moveWindow(self, event):
        if MainWindow.isMaximized():
            MainWindow.showNormal()
        else:
            if event.buttons() == Qt.LeftButton:
                MainWindow.move((MainWindow.pos() + event.globalPos() - MainWindow.dragPos))
                MainWindow.dragPos = event.globalPos()
                event.accept()


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_())

Solution

  • I marked the places I changed

    from PyQt5 import QtCore, QtGui, QtWidgets, Qt
    from PyQt5.QtCore import Qt
    
    WINDOW_SIZE = 0
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(1200, 700)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
            self.verticalLayout.setContentsMargins(0, 0, 0, 0)
            self.verticalLayout.setSpacing(0)
            self.verticalLayout.setObjectName("verticalLayout")
            self.top_bar = QtWidgets.QWidget(self.centralwidget)
            self.top_bar.setMaximumSize(QtCore.QSize(16777215, 60))
            self.top_bar.setStyleSheet("background-color: rgb(52, 56, 60)")
            self.top_bar.setObjectName("top_bar")
            self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.top_bar)
            self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
            self.horizontalLayout_2.setSpacing(0)
            self.horizontalLayout_2.setObjectName("horizontalLayout_2")
            self.frame = QtWidgets.QFrame(self.top_bar)
            self.frame.setMinimumSize(QtCore.QSize(80, 60))
            self.frame.setMaximumSize(QtCore.QSize(80, 60))
            self.frame.setStyleSheet("background-color:rgb(30, 34, 38);\n"
                                                           "border: 0px")
            self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
            self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
            self.frame.setObjectName("frame")
            self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame)
            self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
            self.horizontalLayout_4.setSpacing(0)
            self.horizontalLayout_4.setObjectName("horizontalLayout_4")
            self.horizontalLayout_2.addWidget(self.frame)
            self.header_bar = QtWidgets.QFrame(self.top_bar)
            self.header_bar.setFrameShape(QtWidgets.QFrame.StyledPanel)
            self.header_bar.setFrameShadow(QtWidgets.QFrame.Raised)
            self.header_bar.setObjectName("header_bar")
            self.horizontalLayout_2.addWidget(self.header_bar)
            self.verticalLayout.addWidget(self.top_bar)
            self.content = QtWidgets.QWidget(self.centralwidget)
            self.content.setStyleSheet("")
            self.content.setObjectName("content")
            self.horizontalLayout = QtWidgets.QHBoxLayout(self.content)
            self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
            self.horizontalLayout.setSpacing(0)
            self.horizontalLayout.setObjectName("horizontalLayout")
            self.verticalLayout.addWidget(self.content)
            MainWindow.setCentralWidget(self.centralwidget)
    
            
            self.header_bar.mouseMoveEvent = self.moveWindow
    
            #*************************************************************************************************************************************************************
            self.header_bar.mousePressEvent = self.mousePress
            #*************************************************************************************************************************************************************
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
    
        #*************************************************************************************************************************************************************
        def mousePress(self, event):
            MainWindow.dragPos = MainWindow.pos()
            self.mouse_original_pos = MainWindow.mapToGlobal(event.pos())
        #*************************************************************************************************************************************************************
    
        def moveWindow(self, event):
            if MainWindow.isMaximized():
                MainWindow.showNormal()
            else:
                if event.buttons() == Qt.LeftButton:
                    #******************************************************************************************************************************************************
                    MainWindow_last_pos = MainWindow.dragPos + MainWindow.mapToGlobal(event.pos()) - self.mouse_original_pos
                    MainWindow.move(MainWindow_last_pos)
                    event.accept()
                    #******************************************************************************************************************************************************
    
    
    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_())