【最终版】PyQt5 自定义标题栏,实现无边框,最小化最大化关闭事件,窗口拖动移动,窗口改变大小,仿百度网盘色调美化,添加内容窗口

【最终版】PyQt5 自定义标题栏,实现无边框,最小化最大化关闭事件,窗口拖动移动,窗口改变大小,仿百度网盘色调美化,添加内容窗口



前言

此篇文章是在【PyQt5 自定义标题栏,实现无边框,最小化最大化关闭事件,窗口拖动移动,窗口改变大小,仿百度网盘色调美化】这篇文章的基础上改进的。

下面文章内容如果没有说明,就是和基础版的一样。如果说了【改】或【新增】就说明和基础版的不一致了。


1.文件架构【新增】


|

|-TitleTest.py 标题栏ui代码

|

|-CallTitleTest.py 标题栏逻辑代码

|- - - - -|

|-------- |-textedit_test.py内容窗口ui代码

|---------|-Call_textedit_test.py内容窗口逻辑代码


2.实现效果【增加】

没有内容的标题栏效果

在标题栏加入内容后效果

标题栏适应了内容窗口大小。

在这里插入图片描述


3. 标题栏Qtdesigner的ui设计(ui设计代码)

设计图:

在这里插入图片描述

布局

在这里插入图片描述

具体控件设置请看 ui的py文件

请留意 3 个pushButton和 2 个lable的大小设计

TitleTest.py

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

# Form implementation generated from reading ui file 'TitleTest.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(648, 432)
        self.verticalLayout = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.widget = QtWidgets.QWidget(Form)
        self.widget.setMaximumSize(QtCore.QSize(16777215, 35))
        self.widget.setObjectName("widget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.label = QtWidgets.QLabel(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(20)
        sizePolicy.setVerticalStretch(20)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setMaximumSize(QtCore.QSize(20, 20))
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        spacerItem1 = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.label_2 = QtWidgets.QLabel(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(30)
        sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
        self.label_2.setSizePolicy(sizePolicy)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem2)
        self.pushButton = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(20)
        sizePolicy.setVerticalStretch(20)
        sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
        self.pushButton.setSizePolicy(sizePolicy)
        self.pushButton.setMinimumSize(QtCore.QSize(0, 0))
        self.pushButton.setMaximumSize(QtCore.QSize(20, 20))
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        spacerItem3 = QtWidgets.QSpacerItem(8, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem3)
        self.pushButton_2 = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(20)
        sizePolicy.setVerticalStretch(20)
        sizePolicy.setHeightForWidth(self.pushButton_2.sizePolicy().hasHeightForWidth())
        self.pushButton_2.setSizePolicy(sizePolicy)
        self.pushButton_2.setMaximumSize(QtCore.QSize(20, 20))
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout.addWidget(self.pushButton_2)
        spacerItem4 = QtWidgets.QSpacerItem(8, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem4)
        self.pushButton_3 = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(20)
        sizePolicy.setVerticalStretch(20)
        sizePolicy.setHeightForWidth(self.pushButton_3.sizePolicy().hasHeightForWidth())
        self.pushButton_3.setSizePolicy(sizePolicy)
        self.pushButton_3.setMaximumSize(QtCore.QSize(20, 20))
        self.pushButton_3.setObjectName("pushButton_3")
        self.horizontalLayout.addWidget(self.pushButton_3)
        spacerItem5 = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem5)
        self.verticalLayout.addWidget(self.widget)
        self.widget_2 = QtWidgets.QWidget(Form)
        self.widget_2.setObjectName("widget_2")
        self.verticalLayout.addWidget(self.widget_2)

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

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "图标"))
        self.label_2.setText(_translate("Form", "TextLabel"))
        self.pushButton.setToolTip(_translate("Form", "<html><head/><body><p>最小化</p></body></html>"))
        self.pushButton.setText(_translate("Form", "-"))
        self.pushButton_2.setToolTip(_translate("Form", "<html><head/><body><p>最大化</p></body></html>"))
        self.pushButton_2.setText(_translate("Form", "O"))
        self.pushButton_3.setToolTip(_translate("Form", "<html><head/><body><p>关闭</p></body></html>"))
        self.pushButton_3.setText(_translate("Form", "X"))

4. 设置标题和图标【改】

完整的菜单栏逻辑代码请看最下面的CallTitleTest.py文件代码

单独设置一个函数来整理【标题和图标】设置,如果全部放在__init__初始化函数里,会很混乱的。只需在__init__中初始化这个函数即可。

改:把设置无边框部分放在QSS美化部分讲。这部分就说【标题和图标】的设置函数。

如果看来基础版的代码,可以发现,我把无边框和【标题和图标】分成两个函数了。因为【标题和图标】设置部分做了改变。

首先在__init__函数中设置两个参数来接收图标和标题

# 接收图标和标题
self.icon_path = icon_path
self.title = title
    def icon_and_title(self):
        ########在测试时记得设置自己的图标地址
        self.label.setAlignment(Qt.AlignCenter)
        self.label_2.setStyleSheet('''
                                                font-family:"方正胖娃_GBK";
                                               font-size:11px;
                                               ''')
        if self.icon_path:
            # 设置图标
            self.pix = QPixmap(self.icon_path)  # 注意修改Windows路径问题
            self.label.setPixmap(self.pix)
            self.label.setScaledContents(True)

        else:
            self.label.setStyleSheet('''
                                                font-family:"Webdings";
                                               font-size:13px;
                                               color:blue;
                                               ''')
            self.label.setText('~')

        if self.title:
            # 设置标题
            self.label_2.setText(self.title)
        else:
            # 设置标题
            self.label_2.setText('我的APP')

我是用两个 QLable 标签控件来放置图标和标题的,用QSS进行一定美化

因为可能出现不想输入标题和图标的情况,所以为其设置了默认值。其中图标是使用了webdings特殊字体。

Webdings是特殊字体,想了解请看文章:PyQt5 使用 webdings,Wingdings 字体来替代某些常用图标

进行读写文件时,在字符串前加 r,u,b,f 的含义


5.最大化最小化关闭事件

最大化最小化关闭事件是通过三个按钮来实现

编写三个按钮的信号与槽函数

def _close_max_min_icon(self):
    self.pushButton_3.setText('r')
    self.pushButton_2.setText('1')
    self.pushButton.setText('0')

@pyqtSlot()
def on_pushButton_clicked(self):
    # 最小化
    self.showMinimized()

@pyqtSlot()
def on_pushButton_2_clicked(self):
    # 最大化与复原
    if self.isMaximized():
        self.showNormal()
        self.pushButton_2.setText('1')  # 切换放大按钮图标字体
        self.pushButton_2.setToolTip("<html><head/><body><p>最大化</p></body></html>")
    else:
        self.showMaximized()
        self.pushButton_2.setText('2')
        self.pushButton_2.setToolTip("<html><head/><body><p>恢复</p></body></html>")

@pyqtSlot()
def on_pushButton_3_clicked(self):
    # 关闭程序
    self.close()

def _close_max_min_icon(self):函数不是按钮的信号与槽函数,在这里说是因为它是设置三个按钮的图标关键字,使用了 webdings字体,需要在__init__初始化

PyQt5 使用 webdings,Wingdings 字体来替代某些常用图片

6.窗口拖动移动,窗口改变大小

__init__初始化函数里声明代码并初始化一些函数

self._initDrag()  # 设置鼠标跟踪判断扳机默认值

self.setMouseTracking(True)  # 设置widget鼠标跟踪
self.widget.installEventFilter(self)  # 初始化事件过滤器
self.widget_2.installEventFilter(self)

事件过滤器函数

def eventFilter(self, obj, event):
    # 事件过滤器,用于解决鼠标进入其它控件后还原为标准鼠标样式
    if isinstance(event, QEnterEvent):
        self.setCursor(Qt.ArrowCursor)
    return super(MyWindow, self).eventFilter(obj, event)  # 注意 ,MyWindow是所在类的名称
    # return QWidget.eventFilter(self, obj, event)  # 用这个也行,但要注意修改窗口类型

因为设置了主窗口透明化,所以要专门用这个过滤器来恢复鼠标形状,两个子窗口widgetwidget_2都要经过这个过滤器

鼠标事件

def resizeEvent(self, QResizeEvent):
    # 自定义窗口调整大小事件
    # 改变窗口大小的三个坐标范围
    self._right_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 5)
                        for y in range(self.widget.height() + 20, self.height() - 5)]
    self._bottom_rect = [QPoint(x, y) for x in range(1, self.width() - 5)
                         for y in range(self.height() - 5, self.height() + 1)]
    self._corner_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 1)
                         for y in range(self.height() - 5, self.height() + 1)]

def mousePressEvent(self, event):
    # 重写鼠标点击的事件
    if (event.button() == Qt.LeftButton) and (event.pos() in self._corner_rect):
        # 鼠标左键点击右下角边界区域
        self._corner_drag = True
        event.accept()
    elif (event.button() == Qt.LeftButton) and (event.pos() in self._right_rect):
        # 鼠标左键点击右侧边界区域
        self._right_drag = True
        event.accept()
    elif (event.button() == Qt.LeftButton) and (event.pos() in self._bottom_rect):
        # 鼠标左键点击下侧边界区域
        self._bottom_drag = True
        event.accept()
    elif (event.button() == Qt.LeftButton) and (event.y() < self.widget.height()):
        # 鼠标左键点击标题栏区域
        self._move_drag = True
        self.move_DragPosition = event.globalPos() - self.pos()
        event.accept()

def mouseMoveEvent(self, QMouseEvent):
    # 判断鼠标位置切换鼠标手势
    if QMouseEvent.pos() in self._corner_rect:  # QMouseEvent.pos()获取相对位置
        self.setCursor(Qt.SizeFDiagCursor)
    elif QMouseEvent.pos() in self._bottom_rect:
        self.setCursor(Qt.SizeVerCursor)
    elif QMouseEvent.pos() in self._right_rect:
        self.setCursor(Qt.SizeHorCursor)

    # 当鼠标左键点击不放及满足点击区域的要求后,分别实现不同的窗口调整
    # 没有定义左方和上方相关的5个方向,主要是因为实现起来不难,但是效果很差,拖放的时候窗口闪烁,再研究研究是否有更好的实现
    if Qt.LeftButton and self._right_drag:
        # 右侧调整窗口宽度
        self.resize(QMouseEvent.pos().x(), self.height())
        QMouseEvent.accept()
    elif Qt.LeftButton and self._bottom_drag:
        # 下侧调整窗口高度
        self.resize(self.width(), QMouseEvent.pos().y())
        QMouseEvent.accept()
    elif Qt.LeftButton and self._corner_drag:
        #  由于我窗口设置了圆角,这个调整大小相当于没有用了
        # 右下角同时调整高度和宽度
        self.resize(QMouseEvent.pos().x(), QMouseEvent.pos().y())
        QMouseEvent.accept()
    elif Qt.LeftButton and self._move_drag:
        # 标题栏拖放窗口位置
        self.move(QMouseEvent.globalPos() - self.move_DragPosition)
        QMouseEvent.accept()

def mouseReleaseEvent(self, QMouseEvent):
    # 鼠标释放后,各扳机复位
    self._move_drag = False
    self._corner_drag = False
    self._bottom_drag = False
    self._right_drag = False

def resizeEvent(self, QResizeEvent)通过这个函数获取改变大小的坐标范围,只有鼠标处于这个范围,就可以改变程序窗口大小。

我只设置了三个方向可以改变窗口大小,其他的可以自行修改。

坐标问题:

self.width()获取主窗口的宽,self.height()获取主窗口的高

相对应的self.widget.width()是获取子窗口widget的宽

event.pos()QMouseEvent.pos()获取相对于软件窗口的像素坐标

event.globalPos()QMouseEvent.globalPos()获取相对于屏幕的像素坐标

后面的x,y分别是其的x坐标和y坐标


7.增加内容窗口事件【新增】

单单设计标题栏是没有用的,只有在标题栏放置了内容控件才能实现目的。

在本程序设计中是把内容控件放置在widget_2上,通过在widget_2上设置垂直布局,然后通过addwidget新增控件。

为了更加灵活,把标题栏设置得适应传进来的内容控件的大小。随其大小变化。通过resize()函数实现调整大小

初始化__init__函数部分代码

if widget_2_sub:
    self.widget_2_sub = widget_2_sub
    self.resize(self.widget_2_sub.width(), self.widget_2_sub.height() + self.widget.height())
    self.insert_widget()

首先判断是否有传进内容控件,如果没有,就不进行【界面大小】和【内容控件增添函数】了。

self.insert_widget()内容控件增添函数

def insert_widget(self):
    # widget_2控件初始化与创建垂直布局QVBoxLayout
    self.widget_2_vcroLayout = QVBoxLayout(self.widget_2)
    self.widget_2_vcroLayout.setContentsMargins(0, 0, 0, 0)  # 设置布局外部大小
    self.widget_2_vcroLayout.setSpacing(0)  # 设置布局控件间距
    self.widget_2_vcroLayout.setObjectName("widget_2_vcroLayout")  # 设置Object属性名称,QSS可以用到
    # 设置垂直布局对齐方式
    self.widget_2_vcroLayout.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
    self.widget_2.setLayout(self.widget_2_vcroLayout)  # 这一步暂时没有发现有什么用,

    self.widget_2_vcroLayout.addWidget(self.widget_2_sub)  # 增添内容控件

.


8.仿百度网盘色调QSS美化【改】

如果没有美化,效果如下图

在这里插入图片描述

可以看到,效果很不理想。还是要进行一定美化才好看。

【1】设置无边框

def _init_main_window(self):
    # 设置窗体无边框
    self.setWindowFlags(Qt.FramelessWindowHint)
    # 设置背景透明
    self.setAttribute(Qt.WA_TranslucentBackground)

【2】设置背景和按钮颜色

image-20200811111430871

百度网盘程序的标题栏的颜色便灰,内容窗口便白色,使用屏幕拾色器获取具体颜色编码。

两款屏幕取色拾色器(免安装,1MB左右大小)

.

my_Qss()函数

def my_Qss(self):
    # 给标题栏控件设置别的属性名,这样就不会在美化时和新加进来的内容栏窗口的控件有冲突了
    self.widget.setProperty('other_name', 'title_bar_widget')
    self.widget_2.setProperty('other_name', 'title_bar_widget_2')
    self.label.setProperty('other_name', 'title_bar_lable')
    self.label_2.setProperty('other_name', 'title_bar_lable_2')
    self.pushButton.setProperty('other_name', 'title_bar_pushButton')
    self.pushButton_2.setProperty('other_name', 'title_bar_pushButton_2')
    self.pushButton_3.setProperty('other_name', 'title_bar_pushButton_3')


    # Qss美化
    qssStyle = '''
               QWidget[other_name='title_bar_widget']{background-color:#eef0f6;
               border-left:0.5px solid lightgray;
               border-right:0.5px solid lightgray;
               border-top:0.5px solid lightgray;
               border-bottom:0.5px solid #e5e5e5;
               border-top-left-radius: 5px;
               border-top-right-radius: 5px;
               }
               
               QWidget[other_name='title_bar_widget_2']{background-color:#ffffff;
               border-left:0.5px solid lightgray;
                border-right:0.5px solid lightgray;
               border-bottom:0.5px solid #e5e5e5;
               border-bottom-left-radius: 5px;
               border-bottom-right-radius: 5px;
               }
               
               QPushButton[other_name='title_bar_pushButton']
               {
               font-family:"Webdings";
               text-align:top;
               background:#6DDF6D;border-radius:5px;
               border:none;
               font-size:13px;
               }
               QPushButton[other_name='title_bar_pushButton']:hover{background:green;}
               
               QPushButton[other_name='title_bar_pushButton_2']
               {
               font-family:"Webdings";
               background:#F7D674;border-radius:5px;
               border:none;
               font-size:13px;
               }
               QPushButton[other_name='title_bar_pushButton_2']:hover{background:yellow;}
               
               QPushButton[other_name='title_bar_pushButton_3']
               {
               font-family:"Webdings";
               background:#F76677;border-radius:5px;
               border:none;
               font-size:13px;
               }
               QPushButton[other_name='title_bar_pushButton_3']:hover{background:red;}
               '''
    self.setStyleSheet(qssStyle)

在选择器上改变了, # 给标题栏控件设置别的属性名,这样就不会在美化时和新加进来的内容栏窗口的控件有冲突了。

如果不这样做,就会和新加进来的内容控件相冲突了。

注:只是参考使用了【百度网盘】的颜色,并不是和其一样。


9.完整标题栏逻辑代码 CallTitletest.py【改】

CallTitletest.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author:mgboy time:2020/8/5
import sys

from PyQt5.QtCore import pyqtSlot, Qt, QPoint
from PyQt5.QtGui import QFont, QEnterEvent, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDialog, QVBoxLayout, QPushButton
from PyQt5 import QtCore, QtWidgets

from TitleTest import Ui_Form


class TitleWindow(QWidget, Ui_Form):
    def __init__(self, widget_2_sub=None, icon_path=None, title=None, parent=None):
        super(TitleWindow, self).__init__(parent)
        self.setupUi(self)
        self._init_main_window()  # 主窗口初始化设置
        # 接收图标和标题
        self.icon_path = icon_path
        self.title = title
        self.icon_and_title()  # 设置图标与标题

        self._initDrag()  # 设置鼠标跟踪判断扳机默认值
        self.setMouseTracking(True)  # 设置widget鼠标跟踪
        self._close_max_min_icon()  # 设置 3 个按钮的图标字体
        self.my_Qss()  # 美化
        self.widget.installEventFilter(self)  # 初始化事件过滤器
        self.widget_2.installEventFilter(self)

        if widget_2_sub:
            self.widget_2_sub = widget_2_sub
            self.resize(self.widget_2_sub.width(), self.widget_2_sub.height() + self.widget.height())
            self.insert_widget()

    def insert_widget(self):
        # widget_2控件初始化与创建垂直布局QVBoxLayout
        self.widget_2_vcroLayout = QVBoxLayout(self.widget_2)
        self.widget_2_vcroLayout.setContentsMargins(0, 0, 0, 0)  # 设置布局外部大小
        self.widget_2_vcroLayout.setSpacing(0)  # 设置布局控件间距
        self.widget_2_vcroLayout.setObjectName("widget_2_vcroLayout")  # 设置Object属性名称,QSS可以用到
        # 设置垂直布局对齐方式
        self.widget_2_vcroLayout.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
        self.widget_2.setLayout(self.widget_2_vcroLayout)  # 这一步暂时没有发现有什么用,

        self.widget_2_vcroLayout.addWidget(self.widget_2_sub)  # 增添内容控件

    def _init_main_window(self):
        # 设置窗体无边框
        self.setWindowFlags(Qt.FramelessWindowHint)
        # 设置背景透明
        self.setAttribute(Qt.WA_TranslucentBackground)

    def icon_and_title(self):
        ########在测试时记得设置自己的图标地址
        self.label.setAlignment(Qt.AlignCenter)
        self.label_2.setStyleSheet('''
                                                font-family:"方正胖娃_GBK";
                                               font-size:11px;
                                               ''')
        if self.icon_path:
            # 设置图标
            self.pix = QPixmap(self.icon_path)  # 注意修改Windows路径问题
            self.label.setPixmap(self.pix)
            self.label.setScaledContents(True)

        else:
            self.label.setStyleSheet('''
                                                font-family:"Webdings";
                                               font-size:13px;
                                               color:blue;
                                               ''')
            self.label.setText('~')

        if self.title:
            # 设置标题
            self.label_2.setText(self.title)
        else:
            # 设置标题
            self.label_2.setText('我的APP')

    def _initDrag(self):
        # 设置鼠标跟踪判断扳机默认值
        self._move_drag = False
        self._corner_drag = False
        self._bottom_drag = False
        self._right_drag = False

    def _close_max_min_icon(self):
        self.pushButton_3.setText('r')
        self.pushButton_2.setText('1')
        self.pushButton.setText('0')

    @pyqtSlot()
    def on_pushButton_clicked(self):
        # 最小化
        self.showMinimized()

    @pyqtSlot()
    def on_pushButton_2_clicked(self):
        # 最大化与复原
        if self.isMaximized():
            self.showNormal()
            self.pushButton_2.setText('1')  # 切换放大按钮图标
            self.pushButton_2.setToolTip("<html><head/><body><p>最大化</p></body></html>")
        else:
            self.showMaximized()
            self.pushButton_2.setText('2')
            self.pushButton_2.setToolTip("<html><head/><body><p>恢复</p></body></html>")

    @pyqtSlot()
    def on_pushButton_3_clicked(self):
        # 关闭程序
        self.close()

    def eventFilter(self, obj, event):
        # 事件过滤器,用于解决鼠标进入其它控件后还原为标准鼠标样式
        if isinstance(event, QEnterEvent):
            self.setCursor(Qt.ArrowCursor)
        return super(TitleWindow, self).eventFilter(obj, event)  # 注意 ,MyWindow是所在类的名称
        # return QWidget.eventFilter(self, obj, event)  # 用这个也行,但要注意修改窗口类型

    def resizeEvent(self, QResizeEvent):
        # 自定义窗口调整大小事件
        # 改变窗口大小的三个坐标范围
        self._right_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 5)
                            for y in range(self.widget.height() + 20, self.height() - 5)]
        self._bottom_rect = [QPoint(x, y) for x in range(1, self.width() - 5)
                             for y in range(self.height() - 5, self.height() + 1)]
        self._corner_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 1)
                             for y in range(self.height() - 5, self.height() + 1)]

    def mousePressEvent(self, event):
        # 重写鼠标点击的事件
        if (event.button() == Qt.LeftButton) and (event.pos() in self._corner_rect):
            # 鼠标左键点击右下角边界区域
            self._corner_drag = True
            event.accept()
        elif (event.button() == Qt.LeftButton) and (event.pos() in self._right_rect):
            # 鼠标左键点击右侧边界区域
            self._right_drag = True
            event.accept()
        elif (event.button() == Qt.LeftButton) and (event.pos() in self._bottom_rect):
            # 鼠标左键点击下侧边界区域
            self._bottom_drag = True
            event.accept()
        elif (event.button() == Qt.LeftButton) and (event.y() < self.widget.height()):
            # 鼠标左键点击标题栏区域
            self._move_drag = True
            self.move_DragPosition = event.globalPos() - self.pos()
            event.accept()

    def mouseMoveEvent(self, QMouseEvent):
        # 判断鼠标位置切换鼠标手势
        if QMouseEvent.pos() in self._corner_rect:  # QMouseEvent.pos()获取相对位置
            self.setCursor(Qt.SizeFDiagCursor)
        elif QMouseEvent.pos() in self._bottom_rect:
            self.setCursor(Qt.SizeVerCursor)
        elif QMouseEvent.pos() in self._right_rect:
            self.setCursor(Qt.SizeHorCursor)

        # 当鼠标左键点击不放及满足点击区域的要求后,分别实现不同的窗口调整
        # 没有定义左方和上方相关的5个方向,主要是因为实现起来不难,但是效果很差,拖放的时候窗口闪烁,再研究研究是否有更好的实现
        if Qt.LeftButton and self._right_drag:
            # 右侧调整窗口宽度
            self.resize(QMouseEvent.pos().x(), self.height())
            QMouseEvent.accept()
        elif Qt.LeftButton and self._bottom_drag:
            # 下侧调整窗口高度
            self.resize(self.width(), QMouseEvent.pos().y())
            QMouseEvent.accept()
        elif Qt.LeftButton and self._corner_drag:
            #  由于我窗口设置了圆角,这个调整大小相当于没有用了
            # 右下角同时调整高度和宽度
            self.resize(QMouseEvent.pos().x(), QMouseEvent.pos().y())
            QMouseEvent.accept()
        elif Qt.LeftButton and self._move_drag:
            # 标题栏拖放窗口位置
            self.move(QMouseEvent.globalPos() - self.move_DragPosition)
            QMouseEvent.accept()

    def mouseReleaseEvent(self, QMouseEvent):
        # 鼠标释放后,各扳机复位
        self._move_drag = False
        self._corner_drag = False
        self._bottom_drag = False
        self._right_drag = False

    def my_Qss(self):
        # 给标题栏控件设置别的属性名,这样就不会在美化时和新加进来的内容栏窗口的控件有冲突了
        self.widget.setProperty('other_name', 'title_bar_widget')
        self.widget_2.setProperty('other_name', 'title_bar_widget_2')
        self.label.setProperty('other_name', 'title_bar_lable')
        self.label_2.setProperty('other_name', 'title_bar_lable_2')
        self.pushButton.setProperty('other_name', 'title_bar_pushButton')
        self.pushButton_2.setProperty('other_name', 'title_bar_pushButton_2')
        self.pushButton_3.setProperty('other_name', 'title_bar_pushButton_3')

        # Qss美化
        qssStyle = '''
                   QWidget[other_name='title_bar_widget']{background-color:#eef0f6;
                   border-left:0.5px solid lightgray;
                   border-right:0.5px solid lightgray;
                   border-top:0.5px solid lightgray;
                   border-bottom:0.5px solid #e5e5e5;
                   border-top-left-radius: 5px;
                   border-top-right-radius: 5px;
                   }
                   
                   QWidget[other_name='title_bar_widget_2']{background-color:#ffffff;
                   border-left:0.5px solid lightgray;
                    border-right:0.5px solid lightgray;
                   border-bottom:0.5px solid #e5e5e5;
                   border-bottom-left-radius: 5px;
                   border-bottom-right-radius: 5px;
                   }
                   
                   QPushButton[other_name='title_bar_pushButton']
                   {
                   font-family:"Webdings";
                   text-align:top;
                   background:#6DDF6D;border-radius:5px;
                   border:none;
                   font-size:13px;
                   }
                   QPushButton[other_name='title_bar_pushButton']:hover{background:green;}
                   
                   QPushButton[other_name='title_bar_pushButton_2']
                   {
                   font-family:"Webdings";
                   background:#F7D674;border-radius:5px;
                   border:none;
                   font-size:13px;
                   }
                   QPushButton[other_name='title_bar_pushButton_2']:hover{background:yellow;}
                   
                   QPushButton[other_name='title_bar_pushButton_3']
                   {
                   font-family:"Webdings";
                   background:#F76677;border-radius:5px;
                   border:none;
                   font-size:13px;
                   }
                   QPushButton[other_name='title_bar_pushButton_3']:hover{background:red;}
                   '''
        self.setStyleSheet(qssStyle)


if __name__ == "__main__":
    # 适配2k等高分辨率屏幕,低分辨率屏幕可以缺省
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    app = QApplication(sys.argv)
    myWin = TitleWindow()
    myWin.show()
    sys.exit(app.exec_())


.


10.增添内容窗口控件测试【新增】

首先,用designer设置一个widget控件窗口,然后在其逻辑代码中引用标题栏的TitleWindow类,把内容窗口传进去即可,还可以传进图标地址和标题。

from CallTitleTest import TitleWindow
if __name__ == "__main__":
    #适配2k等高分辨率屏幕
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    app = QApplication(sys.argv)
    myWin = TitleWindow(widget_2_sub=MyWindow(),icon_path=None,title='OmyG')
    myWin.show()
    sys.exit(app.exec_())

注意第5行的引用方法,不要和其他程序混淆了

是将内容窗口MyWindow()类直接进去的。

注意如果想传图标,注意路径写法。

【1】内容窗口ui代码

textedit_test.py

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(320, 240)
        self.verticalLayout = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.lineEdit = QtWidgets.QLineEdit(Form)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.textEdit = QtWidgets.QTextEdit(Form)
        self.textEdit.setObjectName("textEdit")
        self.verticalLayout.addWidget(self.textEdit)

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

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton.setText(_translate("Form", "确定"))

.

【2】内容窗口逻辑代码

Call_textedit_test.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author:mgboy time:2020/7/26
import sys

from PyQt5.QtWidgets import QApplication, QMainWindow,QWidget,QDialog
from PyQt5 import QtCore

from textedit_test import Ui_Form
from CallTitleTest import TitleWindow

class MyWindow(QWidget, Ui_Form):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)
        self.setupUi(self)
        self.pushButton.clicked.connect(self.show_text)
        self.lineEdit.returnPressed.connect(self.show_text)
        self.lineEdit.setPlaceholderText('请输入字符串')
        self.lineEdit.setClearButtonEnabled(True)
        qss = '''
        QPushButton#pushButton{
        width:30px;
        height:15px;
        border-radius:5px;
        background-color:gray;
        }
        '''
        self.setStyleSheet(qss)

    def show_text(self):
        text = '输入了:' + self.lineEdit.text()
        self.textEdit.insertPlainText(text)

    def returnPressed_func(self):
        current_text = self.lineEdit.text()
        print("文本框回车键信号", current_text)
        self.textBrowser.append("文本框回车键信号" + current_text + '\n'+ '\n' + '回车键')

if __name__ == "__main__":
    #适配2k等高分辨率屏幕
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    app = QApplication(sys.argv)
    myWin = TitleWindow(widget_2_sub=MyWindow(),icon_path=None,title='OmyG')
    myWin.show()
    sys.exit(app.exec_())

.

四个代码文件放在同目录下。

直接运行Call_textedit_test.py文件即可

不是运行标题栏文件了。因为是通过from CallTitleTest import TitleWindow引用标题栏文件的。

在这里插入图片描述

.

.

.

.

  • 21
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是使用PyQt实现自定义标题栏的示例代码: ```python from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QPushButton from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor class CustomTitleBar(QWidget): def __init__(self, parent): super().__init__(parent) self.setFixedHeight(30) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.title_label = QLabel("Custom Title Bar") self.title_label.setAlignment(Qt.AlignCenter) layout.addWidget(self.title_label) self.minimize_button = QPushButton("-") self.minimize_button.setFixedSize(30, 30) self.minimize_button.clicked.connect(self.parent().showMinimized) layout.addWidget(self.minimize_button) self.maximize_button = QPushButton("□") self.maximize_button.setFixedSize(30, 30) self.maximize_button.clicked.connect(self.toggleMaximize) layout.addWidget(self.maximize_button) self.close_button = QPushButton("×") self.close_button.setFixedSize(30, 30) self.close_button.clicked.connect(self.parent().close) layout.addWidget(self.close_button) layout.addStretch() self.setStyleSheet(""" background-color: #333333; color: white; font-size: 14px; font-weight: bold; padding-left: 5px; padding-right: 5px; """) def toggleMaximize(self): if self.parent().isMaximized(): self.parent().showNormal() self.maximize_button.setText("□") else: self.parent().showMaximized() self.maximize_button.setText("◻") class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Custom Title Bar Example") self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.title_bar = CustomTitleBar(self) self.setCentralWidget(QWidget(self)) self.centralWidget().setLayout(QVBoxLayout()) self.centralWidget().layout().addWidget(QLabel("Content Area")) self.setStyleSheet(""" QMainWindow { background-color: white; } """) self.show() app = QApplication([]) window = MainWindow() app.exec_() ``` 这段代码创建了一个自定义标题栏,其中包含了一个标题标签、最小化按钮、最大化/还原按钮和关闭按钮。通过设置窗口的`setWindowFlags(Qt.FramelessWindowHint)`和`setAttribute(Qt.WA_TranslucentBackground)`属性,使窗口边框并且背景透明。然后,将自定义标题栏添加窗口的顶部,并在窗口的中央添加一个内容区域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值