Skip to main content
 首页 » 编程设计

python之PyQt5 中带有框架的自定义标题栏

2025年05月04日25bhlsheji

我正在为 Windows/Linux 开发一个开源 markdown 支持的最小笔记记录应用程序。我正在尝试删除标题栏并添加我自己的按钮。我想要类似的东西,一个只有两个自定义按钮的标题栏,如图 所示。

目前我有这个:

我试过修改窗口标志:

  • 没有窗口标志,窗口既可以调整大小也可以移动。但没有自定义按钮。
  • 使用self.setWindowFlags(QtCore.Qt.FramelessWindowHint),窗口没有边框,但不能移动或调整窗口大小
  • 使用self.setWindowFlags(QtCore.Qt.CustomizeWindowHint),窗口可以调整大小但不能移动,也不能去掉窗口顶部的白色部分。

感谢任何帮助。您可以在 GitHub 上找到该项目 here .

谢谢..

这是我的 python 代码:

from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, uic 
import sys 
import os 
import markdown2 # https://github.com/trentm/python-markdown2 
from PyQt5.QtCore import QRect 
from PyQt5.QtGui import QFont 
 
simpleUiForm = uic.loadUiType("Simple.ui")[0] 
 
class SimpleWindow(QtWidgets.QMainWindow, simpleUiForm): 
    def __init__(self, parent=None): 
        QtWidgets.QMainWindow.__init__(self, parent) 
        self.setupUi(self) 
        self.markdown = markdown2.Markdown() 
        self.css = open(os.path.join("css", "default.css")).read() 
        self.editNote.setPlainText("") 
        #self.noteView = QtWebEngineWidgets.QWebEngineView(self) 
        self.installEventFilter(self) 
        self.displayNote.setContextMenuPolicy(QtCore.Qt.NoContextMenu) 
        #self.setWindowFlags(QtCore.Qt.FramelessWindowHint) 
 
    def eventFilter(self, object, event): 
        if event.type() == QtCore.QEvent.WindowActivate: 
            print("widget window has gained focus") 
            self.editNote.show() 
            self.displayNote.hide() 
        elif event.type() == QtCore.QEvent.WindowDeactivate: 
            print("widget window has lost focus") 
            note = self.editNote.toPlainText() 
            htmlNote = self.getStyledPage(note) 
            # print(note) 
            self.editNote.hide() 
            self.displayNote.show() 
            # print(htmlNote) 
            self.displayNote.setHtml(htmlNote) 
        elif event.type() == QtCore.QEvent.FocusIn: 
            print("widget has gained keyboard focus") 
        elif event.type() == QtCore.QEvent.FocusOut: 
            print("widget has lost keyboard focus") 
        return False 

UI 文件在以下层次结构中创建

请您参考如下方法:

以下是您必须遵循的步骤:

  1. 拥有您的 MainWindow,无论是 QMainWindow 还是 QWidget,或者您想要继承的任何 [widget]。
  2. 设置它的标志,self.setWindowFlags(Qt.FramelessWindowHint)
  3. 实现您自己的四处走动。
  4. 实现您自己的按钮(关闭、最大、最小)
  5. 实现您自己的调整大小。

这是一个实现了四处移动和按钮的小示例。您仍然应该使用相同的逻辑来实现调整大小。

import sys 
 
from PyQt5.QtCore import QPoint 
from PyQt5.QtCore import Qt 
from PyQt5.QtWidgets import QApplication 
from PyQt5.QtWidgets import QHBoxLayout 
from PyQt5.QtWidgets import QLabel 
from PyQt5.QtWidgets import QPushButton 
from PyQt5.QtWidgets import QVBoxLayout 
from PyQt5.QtWidgets import QWidget 
 
 
 
class MainWindow(QWidget): 
 
    def __init__(self): 
        super(MainWindow, self).__init__() 
        self.layout  = QVBoxLayout() 
        self.layout.addWidget(MyBar(self)) 
        self.setLayout(self.layout) 
        self.layout.setContentsMargins(0,0,0,0) 
        self.layout.addStretch(-1) 
        self.setMinimumSize(800,400) 
        self.setWindowFlags(Qt.FramelessWindowHint) 
        self.pressing = False 
 
 
class MyBar(QWidget): 
 
    def __init__(self, parent): 
        super(MyBar, self).__init__() 
        self.parent = parent 
        print(self.parent.width()) 
        self.layout = QHBoxLayout() 
        self.layout.setContentsMargins(0,0,0,0) 
        self.title = QLabel("My Own Bar") 
 
        btn_size = 35 
 
        self.btn_close = QPushButton("x") 
        self.btn_close.clicked.connect(self.btn_close_clicked) 
        self.btn_close.setFixedSize(btn_size,btn_size) 
        self.btn_close.setStyleSheet("background-color: red;") 
 
        self.btn_min = QPushButton("-") 
        self.btn_min.clicked.connect(self.btn_min_clicked) 
        self.btn_min.setFixedSize(btn_size, btn_size) 
        self.btn_min.setStyleSheet("background-color: gray;") 
 
        self.btn_max = QPushButton("+") 
        self.btn_max.clicked.connect(self.btn_max_clicked) 
        self.btn_max.setFixedSize(btn_size, btn_size) 
        self.btn_max.setStyleSheet("background-color: gray;") 
 
        self.title.setFixedHeight(35) 
        self.title.setAlignment(Qt.AlignCenter) 
        self.layout.addWidget(self.title) 
        self.layout.addWidget(self.btn_min) 
        self.layout.addWidget(self.btn_max) 
        self.layout.addWidget(self.btn_close) 
 
        self.title.setStyleSheet(""" 
            background-color: black; 
            color: white; 
        """) 
        self.setLayout(self.layout) 
 
        self.start = QPoint(0, 0) 
        self.pressing = False 
 
    def resizeEvent(self, QResizeEvent): 
        super(MyBar, self).resizeEvent(QResizeEvent) 
        self.title.setFixedWidth(self.parent.width()) 
 
    def mousePressEvent(self, event): 
        self.start = self.mapToGlobal(event.pos()) 
        self.pressing = True 
 
    def mouseMoveEvent(self, event): 
        if self.pressing: 
            self.end = self.mapToGlobal(event.pos()) 
            self.movement = self.end-self.start 
            self.parent.setGeometry(self.mapToGlobal(self.movement).x(), 
                                self.mapToGlobal(self.movement).y(), 
                                self.parent.width(), 
                                self.parent.height()) 
            self.start = self.end 
 
    def mouseReleaseEvent(self, QMouseEvent): 
        self.pressing = False 
 
 
    def btn_close_clicked(self): 
        self.parent.close() 
 
    def btn_max_clicked(self): 
        self.parent.showMaximized() 
 
    def btn_min_clicked(self): 
        self.parent.showMinimized() 
 
 
if __name__ == "__main__": 
    app = QApplication(sys.argv) 
    mw = MainWindow() 
    mw.show() 
    sys.exit(app.exec_()) 

这里有一些提示:

选项 1:

  1. 有一个 QGridLayout,每个角和边都有小部件(例如,左、左上、菜单栏、右上、右、右下、下和左下)
  2. 使用方法 (1),您会知道何时单击每个边框,您只需定义每个尺寸并将每个尺寸添加到它们的位置即可。
  3. 当你点击每一个时,以各自的方式对待它们,例如,如果你点击左边的一个并向左拖动,你必须将它的大小调整得更大,同时将它向左移动,这样它就会似乎停在了正确的位置并变宽了。
  4. 将此推理应用于每条边,每条边都以其必须的方式行事。

选项 2:

  1. 您可以通过点击位置检测您点击的位置,而不是使用 QGridLayout。

  2. 验证点击的 x 是否小于移动位置的 x,以了解它是向左移动还是向右移动以及它被点击的位置。

  3. 同Option1计算

选项 3:

  1. 可能还有其他方法,但这些是我刚刚想到的。例如使用 CustomizeWindowHint 你说你可以调整大小,所以你只需要实现我给你的例子。美丽!

提示:

  1. 注意 localPos(在自己的小部件内)、globalPos(与您的屏幕相关)。例如:如果您单击左侧小部件的最左侧,它的“x”将为零,如果您单击中间(内容)的最左侧,它也将为零,尽管如果您使用 mapToGlobal,您将具有不同的值根据屏幕的位置。
  2. 在调整大小时或移动时要注意,当您必须增加宽度或减少宽度,或者只是移动,或两者兼而有之时,我建议您在纸上画画并在实现之前弄清楚调整大小的逻辑是如何工作的蓝色。

祝你好运:D