正常情况创建一个窗体,窗口移动、阴影、最大化最小化关闭按钮等PyQt5都处理好了,但是如果隐藏了边框,则需要自己实现上述的功能,最大化最小化直接使用Qt Designer进行信号事件绑定即可。
对于标题栏拖拽窗口跟随移动,则需要重写鼠标事件。
具体步骤如下

1、找一个具有鼠标按下mousePressEvent、鼠标移动mouseMoveEvent和鼠标松开mouseReleaseEvent事件的控件作为标题栏,比如QLabel
2、重写鼠标按下方法mousePressEvent,按下时获取鼠标相对窗口的位置
3、重写鼠标移动方法mouseMoveEvent,如果是鼠标按下了,那么鼠标移动时,修改鼠标指针为手型、改变窗口位置
4、重写鼠标松开方法mouseReleaseEvent,如果是鼠标松开了,则恢复鼠标指针为箭头

UI代码示例,绘制一个带标题栏的窗口

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'frame_move.ui'
#
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Frame(object):
    def setupUi(self, Frame):
        Frame.setObjectName("Frame")
        Frame.resize(673, 490)
        self.frame = QtWidgets.QFrame(Frame)
        self.frame.setGeometry(QtCore.QRect(0, 0, 671, 481))
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.title_pr = QtWidgets.QLabel(self.frame)
        self.title_pr.setGeometry(QtCore.QRect(0, 0, 611, 31))
        font = QtGui.QFont()
        font.setPointSize(14)
        font.setBold(True)
        font.setItalic(False)
        font.setWeight(75)
        self.title_pr.setFont(font)
        self.title_pr.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.title_pr.setStyleSheet("background-color:#3375D6;\n"
"color:#FFF;")
        self.title_pr.setScaledContents(False)
        self.title_pr.setObjectName("title_pr")
        self.minimized_btn_2 = QtWidgets.QPushButton(self.frame)
        self.minimized_btn_2.setGeometry(QtCore.QRect(610, 0, 31, 31))
        self.minimized_btn_2.setStyleSheet("#minimized_btn_2{\n"
"border:none;\n"
"background-color:#FFF;\n"
"}\n"
"#minimized_btn_2::hover{\n"
"background-color:#F9F9F9;\n"
"}")
        self.minimized_btn_2.setObjectName("minimized_btn_2")
        self.closed_btn_2 = QtWidgets.QPushButton(self.frame)
        self.closed_btn_2.setGeometry(QtCore.QRect(640, 0, 31, 31))
        self.closed_btn_2.setStyleSheet("#closed_btn_2{\n"
"border:none;\n"
"background-color:#FFF;\n"
"}\n"
"#closed_btn_2::hover{\n"
"background-color:#C42B1C;\n"
"}")
        self.closed_btn_2.setObjectName("closed_btn_2")

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

    def retranslateUi(self, Frame):
        _translate = QtCore.QCoreApplication.translate
        Frame.setWindowTitle(_translate("Frame", "Frame"))
        self.title_pr.setText(_translate("Frame", " 我是标题栏"))
        self.minimized_btn_2.setText(_translate("Frame", "-"))
        self.closed_btn_2.setText(_translate("Frame", "X"))

UI绘制.png

在调用类中重写鼠标事件,仅监听标题栏

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QApplication, QFrame
# 导入UI
from frame_move import Ui_Frame

class frame_move_test(QFrame, Ui_Frame):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        # 设置窗口=无边框
        self.setWindowFlags(Qt.FramelessWindowHint)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            # 标记是否按下
            self.m_flag = True
            # 获取鼠标相对窗口的位置
            self.m_Position = event.globalPos() - self.pos()
            event.accept()

    def mouseMoveEvent(self, QMouseEvent):
        try:
            # 仅监听标题栏
            if Qt.LeftButton and self.m_flag and self.title_pr.underMouse():
                # 更改鼠标图标
                self.setCursor(QCursor(Qt.OpenHandCursor))
                # 更改窗口位置
                self.move(QMouseEvent.globalPos() - self.m_Position)
                QMouseEvent.accept()
        except Exception as e:
            print("报错信息=", e)

    def mouseReleaseEvent(self, QMouseEvent):
        self.m_flag = False
        # 恢复鼠标形状
        self.setCursor(QCursor(Qt.ArrowCursor))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    frame_move_test = frame_move_test()
    frame_move_test.show()
    sys.exit(app.exec_())

窗口移动示例.gif