zoukankan      html  css  js  c++  java
  • PyQt:无边框自定义标题栏及最大化最小化窗体大小调整

    环境

      Python3.5.2

      PyQt5

    陈述

      隐藏掉系统的控制栏,实现了自定义的标题控制栏,以及关闭/最大化/最小化的功能,自由调整窗体大小的功能(跟随一个大佬学的),代码内有详细注释

      只要把MainWindow类自己实现就可以了,我把左侧栏的demo(可以看我这篇https://www.cnblogs.com/jyroy/p/9457882.html)搭载上了,效果如下

      标题栏的风格我和左侧栏的风格统一了,还是模仿网易云音乐的红色格调(我觉得网易云的红色很ok)

    代码

      1 #!/usr/bin/env python
      2 # -*- coding:utf-8 -*-
      3 # Author: jyroy
      4 import sys
      5 
      6 from PyQt5.QtCore import QSize
      7 from PyQt5.QtWidgets import QApplication
      8 from PyQt5.QtCore import Qt, pyqtSignal, QPoint
      9 from PyQt5.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen
     10 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel,QSpacerItem, QSizePolicy, QPushButton
     11 from PyQt5.QtGui import QIcon
     12 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit
     13 from LeftTabWidget import LeftTabWidget
     14 # 样式
     15 StyleSheet = """
     16 /*标题栏*/
     17 TitleBar {
     18     background-color: red;
     19 }
     20 /*最小化最大化关闭按钮通用默认背景*/
     21 #buttonMinimum,#buttonMaximum,#buttonClose {
     22     border: none;
     23     background-color: red;
     24 }
     25 /*悬停*/
     26 #buttonMinimum:hover,#buttonMaximum:hover {
     27     background-color: red;
     28     color: white;
     29 }
     30 #buttonClose:hover {
     31     color: white;
     32 }
     33 /*鼠标按下不放*/
     34 #buttonMinimum:pressed,#buttonMaximum:pressed {
     35     background-color: Firebrick;
     36 }
     37 #buttonClose:pressed {
     38     color: white;
     39     background-color: Firebrick;
     40 }
     41 """
     42 
     43 class TitleBar(QWidget):
     44 
     45     # 窗口最小化信号
     46     windowMinimumed = pyqtSignal()
     47     # 窗口最大化信号
     48     windowMaximumed = pyqtSignal()
     49     # 窗口还原信号
     50     windowNormaled = pyqtSignal()
     51     # 窗口关闭信号
     52     windowClosed = pyqtSignal()
     53     # 窗口移动
     54     windowMoved = pyqtSignal(QPoint)
     55 
     56     def __init__(self, *args, **kwargs):
     57         super(TitleBar, self).__init__(*args, **kwargs)
     58         # 支持qss设置背景
     59         self.setAttribute(Qt.WA_StyledBackground, True)
     60         self.mPos = None
     61         self.iconSize = 20  # 图标的默认大小
     62         # 设置默认背景颜色,否则由于受到父窗口的影响导致透明
     63         self.setAutoFillBackground(True)
     64         palette = self.palette()
     65         palette.setColor(palette.Window, QColor(240, 240, 240))
     66         self.setPalette(palette)
     67         # 布局
     68         layout = QHBoxLayout(self, spacing=0)
     69         layout.setContentsMargins(0, 0, 0, 0)
     70         # 窗口图标
     71         self.iconLabel = QLabel(self)
     72 #         self.iconLabel.setScaledContents(True)
     73         layout.addWidget(self.iconLabel)
     74         # 窗口标题
     75         self.titleLabel = QLabel(self)
     76         self.titleLabel.setMargin(2)
     77         layout.addWidget(self.titleLabel)
     78         # 中间伸缩条
     79         layout.addSpacerItem(QSpacerItem(
     80             40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
     81         # 利用Webdings字体来显示图标
     82         font = self.font() or QFont()
     83         font.setFamily('Webdings')
     84         # 最小化按钮
     85         self.buttonMinimum = QPushButton(
     86             '0', self, clicked=self.windowMinimumed.emit, font=font, objectName='buttonMinimum')
     87         layout.addWidget(self.buttonMinimum)
     88         # 最大化/还原按钮
     89         self.buttonMaximum = QPushButton(
     90             '1', self, clicked=self.showMaximized, font=font, objectName='buttonMaximum')
     91         layout.addWidget(self.buttonMaximum)
     92         # 关闭按钮
     93         self.buttonClose = QPushButton(
     94             'r', self, clicked=self.windowClosed.emit, font=font, objectName='buttonClose')
     95         layout.addWidget(self.buttonClose)
     96         # 初始高度
     97         self.setHeight()
     98 
     99     def showMaximized(self):
    100         if self.buttonMaximum.text() == '1':
    101             # 最大化
    102             self.buttonMaximum.setText('2')
    103             self.windowMaximumed.emit()
    104         else:  # 还原
    105             self.buttonMaximum.setText('1')
    106             self.windowNormaled.emit()
    107 
    108     def setHeight(self, height=38):
    109         """设置标题栏高度"""
    110         self.setMinimumHeight(height)
    111         self.setMaximumHeight(height)
    112         # 设置右边按钮的大小
    113         self.buttonMinimum.setMinimumSize(height, height)
    114         self.buttonMinimum.setMaximumSize(height, height)
    115         self.buttonMaximum.setMinimumSize(height, height)
    116         self.buttonMaximum.setMaximumSize(height, height)
    117         self.buttonClose.setMinimumSize(height, height)
    118         self.buttonClose.setMaximumSize(height, height)
    119 
    120     def setTitle(self, title):
    121         """设置标题"""
    122         self.titleLabel.setText(title)
    123 
    124     def setIcon(self, icon):
    125         """设置图标"""
    126         self.iconLabel.setPixmap(icon.pixmap(self.iconSize, self.iconSize))
    127 
    128     def setIconSize(self, size):
    129         """设置图标大小"""
    130         self.iconSize = size
    131 
    132     def enterEvent(self, event):
    133         self.setCursor(Qt.ArrowCursor)
    134         super(TitleBar, self).enterEvent(event)
    135 
    136     def mouseDoubleClickEvent(self, event):
    137         super(TitleBar, self).mouseDoubleClickEvent(event)
    138         self.showMaximized()
    139 
    140     def mousePressEvent(self, event):
    141         """鼠标点击事件"""
    142         if event.button() == Qt.LeftButton:
    143             self.mPos = event.pos()
    144         event.accept()
    145 
    146     def mouseReleaseEvent(self, event):
    147         '''鼠标弹起事件'''
    148         self.mPos = None
    149         event.accept()
    150 
    151     def mouseMoveEvent(self, event):
    152         if event.buttons() == Qt.LeftButton and self.mPos:
    153             self.windowMoved.emit(self.mapToGlobal(event.pos() - self.mPos))
    154         event.accept()
    155 
    156 # 枚举左上右下以及四个定点
    157 Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)
    158 
    159 class FramelessWindow(QWidget):
    160 
    161     # 四周边距
    162     Margins = 5
    163 
    164     def __init__(self, *args, **kwargs):
    165         super(FramelessWindow, self).__init__(*args, **kwargs)
    166 
    167         self._pressed = False
    168         self.Direction = None
    169         # 背景透明
    170         self.setAttribute(Qt.WA_TranslucentBackground, True)
    171         # 无边框
    172         self.setWindowFlags(Qt.FramelessWindowHint)  # 隐藏边框
    173         # 鼠标跟踪
    174         self.setMouseTracking(True)
    175         # 布局
    176         layout = QVBoxLayout(self, spacing=0)
    177         # 预留边界用于实现无边框窗口调整大小
    178         layout.setContentsMargins(
    179             self.Margins, self.Margins, self.Margins, self.Margins)
    180         # 标题栏
    181         self.titleBar = TitleBar(self)
    182         layout.addWidget(self.titleBar)
    183         # 信号槽
    184         self.titleBar.windowMinimumed.connect(self.showMinimized)
    185         self.titleBar.windowMaximumed.connect(self.showMaximized)
    186         self.titleBar.windowNormaled.connect(self.showNormal)
    187         self.titleBar.windowClosed.connect(self.close)
    188         self.titleBar.windowMoved.connect(self.move)
    189         self.windowTitleChanged.connect(self.titleBar.setTitle)
    190         self.windowIconChanged.connect(self.titleBar.setIcon)
    191 
    192     def setTitleBarHeight(self, height=38):
    193         """设置标题栏高度"""
    194         self.titleBar.setHeight(height)
    195 
    196     def setIconSize(self, size):
    197         """设置图标的大小"""
    198         self.titleBar.setIconSize(size)
    199 
    200     def setWidget(self, widget):
    201         """设置自己的控件"""
    202         if hasattr(self, '_widget'):
    203             return
    204         self._widget = widget
    205         # 设置默认背景颜色,否则由于受到父窗口的影响导致透明
    206         self._widget.setAutoFillBackground(True)
    207         palette = self._widget.palette()
    208         palette.setColor(palette.Window, QColor(240, 240, 240))
    209         self._widget.setPalette(palette)
    210         self._widget.installEventFilter(self)
    211         self.layout().addWidget(self._widget)
    212 
    213     def move(self, pos):
    214         if self.windowState() == Qt.WindowMaximized or self.windowState() == Qt.WindowFullScreen:
    215             # 最大化或者全屏则不允许移动
    216             return
    217         super(FramelessWindow, self).move(pos)
    218 
    219     def showMaximized(self):
    220         """最大化,要去除上下左右边界,如果不去除则边框地方会有空隙"""
    221         super(FramelessWindow, self).showMaximized()
    222         self.layout().setContentsMargins(0, 0, 0, 0)
    223 
    224     def showNormal(self):
    225         """还原,要保留上下左右边界,否则没有边框无法调整"""
    226         super(FramelessWindow, self).showNormal()
    227         self.layout().setContentsMargins(
    228             self.Margins, self.Margins, self.Margins, self.Margins)
    229 
    230     def eventFilter(self, obj, event):
    231         """事件过滤器,用于解决鼠标进入其它控件后还原为标准鼠标样式"""
    232         if isinstance(event, QEnterEvent):
    233             self.setCursor(Qt.ArrowCursor)
    234         return super(FramelessWindow, self).eventFilter(obj, event)
    235 
    236     def paintEvent(self, event):
    237         """由于是全透明背景窗口,重绘事件中绘制透明度为1的难以发现的边框,用于调整窗口大小"""
    238         super(FramelessWindow, self).paintEvent(event)
    239         painter = QPainter(self)
    240         painter.setPen(QPen(QColor(255, 255, 255, 1), 2 * self.Margins))
    241         painter.drawRect(self.rect())
    242 
    243     def mousePressEvent(self, event):
    244         """鼠标点击事件"""
    245         super(FramelessWindow, self).mousePressEvent(event)
    246         if event.button() == Qt.LeftButton:
    247             self._mpos = event.pos()
    248             self._pressed = True
    249 
    250     def mouseReleaseEvent(self, event):
    251         '''鼠标弹起事件'''
    252         super(FramelessWindow, self).mouseReleaseEvent(event)
    253         self._pressed = False
    254         self.Direction = None
    255 
    256     def mouseMoveEvent(self, event):
    257         """鼠标移动事件"""
    258         super(FramelessWindow, self).mouseMoveEvent(event)
    259         pos = event.pos()
    260         xPos, yPos = pos.x(), pos.y()
    261         wm, hm = self.width() - self.Margins, self.height() - self.Margins
    262         if self.isMaximized() or self.isFullScreen():
    263             self.Direction = None
    264             self.setCursor(Qt.ArrowCursor)
    265             return
    266         if event.buttons() == Qt.LeftButton and self._pressed:
    267             self._resizeWidget(pos)
    268             return
    269         if xPos <= self.Margins and yPos <= self.Margins:
    270             # 左上角
    271             self.Direction = LeftTop
    272             self.setCursor(Qt.SizeFDiagCursor)
    273         elif wm <= xPos <= self.width() and hm <= yPos <= self.height():
    274             # 右下角
    275             self.Direction = RightBottom
    276             self.setCursor(Qt.SizeFDiagCursor)
    277         elif wm <= xPos and yPos <= self.Margins:
    278             # 右上角
    279             self.Direction = RightTop
    280             self.setCursor(Qt.SizeBDiagCursor)
    281         elif xPos <= self.Margins and hm <= yPos:
    282             # 左下角
    283             self.Direction = LeftBottom
    284             self.setCursor(Qt.SizeBDiagCursor)
    285         elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm:
    286             # 左边
    287             self.Direction = Left
    288             self.setCursor(Qt.SizeHorCursor)
    289         elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm:
    290             # 右边
    291             self.Direction = Right
    292             self.setCursor(Qt.SizeHorCursor)
    293         elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins:
    294             # 上面
    295             self.Direction = Top
    296             self.setCursor(Qt.SizeVerCursor)
    297         elif self.Margins <= xPos <= wm and hm <= yPos <= self.height():
    298             # 下面
    299             self.Direction = Bottom
    300             self.setCursor(Qt.SizeVerCursor)
    301 
    302     def _resizeWidget(self, pos):
    303         """调整窗口大小"""
    304         if self.Direction == None:
    305             return
    306         mpos = pos - self._mpos
    307         xPos, yPos = mpos.x(), mpos.y()
    308         geometry = self.geometry()
    309         x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height()
    310         if self.Direction == LeftTop:  # 左上角
    311             if w - xPos > self.minimumWidth():
    312                 x += xPos
    313                 w -= xPos
    314             if h - yPos > self.minimumHeight():
    315                 y += yPos
    316                 h -= yPos
    317         elif self.Direction == RightBottom:  # 右下角
    318             if w + xPos > self.minimumWidth():
    319                 w += xPos
    320                 self._mpos = pos
    321             if h + yPos > self.minimumHeight():
    322                 h += yPos
    323                 self._mpos = pos
    324         elif self.Direction == RightTop:  # 右上角
    325             if h - yPos > self.minimumHeight():
    326                 y += yPos
    327                 h -= yPos
    328             if w + xPos > self.minimumWidth():
    329                 w += xPos
    330                 self._mpos.setX(pos.x())
    331         elif self.Direction == LeftBottom:  # 左下角
    332             if w - xPos > self.minimumWidth():
    333                 x += xPos
    334                 w -= xPos
    335             if h + yPos > self.minimumHeight():
    336                 h += yPos
    337                 self._mpos.setY(pos.y())
    338         elif self.Direction == Left:  # 左边
    339             if w - xPos > self.minimumWidth():
    340                 x += xPos
    341                 w -= xPos
    342             else:
    343                 return
    344         elif self.Direction == Right:  # 右边
    345             if w + xPos > self.minimumWidth():
    346                 w += xPos
    347                 self._mpos = pos
    348             else:
    349                 return
    350         elif self.Direction == Top:  # 上面
    351             if h - yPos > self.minimumHeight():
    352                 y += yPos
    353                 h -= yPos
    354             else:
    355                 return
    356         elif self.Direction == Bottom:  # 下面
    357             if h + yPos > self.minimumHeight():
    358                 h += yPos
    359                 self._mpos = pos
    360             else:
    361                 return
    362         self.setGeometry(x, y, w, h)
    363 
    364 class MainWindow(QWidget):
    365 
    366     def __init__(self, *args, **kwargs):
    367         super(MainWindow, self).__init__(*args, **kwargs)
    368         layout = QVBoxLayout(self, spacing=0)
    369         layout.setContentsMargins(0, 0, 0, 0)
    370         
    371         self.left_tag = LeftTabWidget()
    372         layout.addWidget(self.left_tag)
    373 
    374 
    375 if __name__ == '__main__':
    376 
    377     app = QApplication(sys.argv)
    378     app.setStyleSheet(StyleSheet)
    379     mainWnd = FramelessWindow()
    380     mainWnd.setWindowTitle('测试标题栏')
    381     mainWnd.setWindowIcon(QIcon('Qt.ico'))
    382     mainWnd.resize(QSize(1250,780))
    383     mainWnd.setWidget(MainWindow(mainWnd))  # 把自己的窗口添加进来
    384     mainWnd.show()
    385     sys.exit(app.exec_())

    效果展示

     

    拓展知识

    设置窗口尺寸的方法:
    1.设置宽度和高度。
      resize(int w,int h)
      resize(QSize s)
    2.设置窗口的位置、宽度和高度。
      setGeometry(int X,int Y,int W,int H)
      setGeometry(QRect r)
    3.设置窗口为固定值。
      setFixedSize(int w,int h)
      setFixedSize(QSize s)
      注意:窗口标题栏上的最大化按钮无效;用鼠标无法调整窗口尺寸。
    4.设置窗口为固定值。
      setFixedWidth(int w)
      窗口标题栏上的最大化按钮无效;用鼠标无法调整窗口的宽度。
    5.设置窗口为固定值。
      setFixedHeight(int h)
      窗口标题栏上的最大化按钮无效;用鼠标无法调整窗口的高度。
    5.设置窗口的最小尺寸。
      setMinimumSize(int w,int h)
      setMinimumSize(QSize s)
      用鼠标可以让窗口变宽、变高。
      设置窗口的最小宽度:
        setMinimumWidth(int w)
      设置窗口的最小高度:
        setMinimumHeight(int h)
    6.设置窗口的最大尺寸。
      setMaximumSize(int w,int h)
      setMaximumSize(QSize s)
      用鼠标可以让窗口变宽、变高。
      设置窗口的最小宽度:
        setMaximumWidth(int w)
      设置窗口的最小高度:
        setMaximumHeight(int h)
     

    说明

      因为只是自己写的简单的例子,在窗口方面都是利用的写死的大小。不同的电脑像素会有差别。我的是1920*1080的设备。在实际用的时候尽量加上判断,来适应不同的设备。

      

  • 相关阅读:
    day 13 闭包函数,装饰器,迭代器
    day12 可变长参数、函数对象、函数的嵌套、名称空间和作用域
    day11 文件的高级应用、文件修改的两种方式、函数的定义、函数的三种定义方式、函数的调用、函数的返回值、函数的参数
    数字类型内置方法
    基本语法之for循环
    基本语法之while循环
    python基本语法(3)
    python基本语法(2)
    python基本语法(1)
    编程及计算机组成
  • 原文地址:https://www.cnblogs.com/jyroy/p/9461317.html
Copyright © 2011-2022 走看看