Pyside6 QWidget 控件之父(4)

十三、QWidget的外观与样式设置

这里所说QWidget的外观设置,是指其背景颜色、边框、字体等设置。为便于后面控件的学习,本小节仅简要介绍API的基本使用。深入的设置将放在QSS和QPalette章节学习,字体将在QFont章节学习。

API函数

参数说明

返回值

功能作用

setStyle(self, arg__1)

arg_1:QStyle

None

设置窗口风格

style(self)

None

QStyle

获取窗口风格

setStyleSheet(self, styleSheet)

styleSheet: str

None

设置窗口样式表

setFont(self, arg__1)

arg__1:

QFont, str, Sequence[str]

QWidget

设置字体

font(self)

None

QFont

获取字体

setPalette(self, arg__1

atg_1:QPalette

Qt.GlobalColor

QColor

None

设置调色板

palette(self)

None

QPalette

获取调色板

setAutoFillBackground(self, enabled)

enabled: bool

None

设置是否自动填充背景

autoFillBackground(self)

None

bool

获取是否自动填充背景

setAttribute(self, arg__1, on=True)

arg__1:

Qt.WidgetAttribute

on:bool

参数为Qt枚举类

None

设置控件或窗口属性,on为Ture时开启,on为Flase时关闭

testAttribute(self, arg__1)

arg__1:

Qt.WidgetAttribute

bool

如果控件设置了属性,开启时返回Ture,关闭时返回Flase

setBackgroundRole(self, arg__1)

arg__1:

QPalette.ColorRole

参数为QPalette的枚举类

None

设置控件的背景角色

backgroundRole(self)

None

QPalette.ColorRole

获取控件的背景角色

fontInfo(self)

None

QFontInfo

返回控件的字体信息。

fontMetrics(self)

None

QFontMetrics

返回控件的字体度量。

1.样式表的设置

PySide6的样式表使用使用setStyleSheet()方法进行设置,其参数是一个QSS标注的字符串或是QSS文件的文件路径。QSS标准类似于CSS,熟悉CSS的基本可以正常使用,不熟悉的我们将在后面样式美化章节学习。

example:

创建一个名为btnStyleSheet普通文本文件,这里没有创建QSS文件,QSS文件的创建及添加到.qrc资源文件将在后面学。

QPushButton {
    /* 设置前景色 */
    color:blue;

    /* 设置背景色 */
    background-color:rgb(30,75,10);

    /* 设置边框风格 */
    border-style:outset;

    /* 设置边框宽度 */
    border-width:3px;

    /* 设置边框颜色 */
    border-color:rgb(10,45,110);

    /* 设置边框倒角 */
    border-radius:15px;

    /* 设置字体 */
    font:bold 24px;

    /* 设置字体族 */
    font-family:隶书;

}

将其添加到按钮的样式表里:

# -*- codeing:utf-8 -*-
from PySide6.QtWidgets import QWidget,QApplication,QPushButton
import sys

class testWindow(QWidget):
    def __init__(self):
        super(testWindow, self).__init__()
        self.resize(500,500)
        self.setWindowTitle("QSS测试")
        self.setStyleSheet("background-color:green")

        btn = QPushButton("测试按钮",self)
        btn.move(200,200)
        btn.resize(100,50)
        with open("btnStyleSheet","r",encoding="utf-8") as f:
            btn.setStyleSheet(f.read())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = testWindow()
    win.show()
    sys.exit(app.exec())

运行结果:

2.QPalette和QFont类对控件外观与样式设置

Pyside6有两套系统对控件的外观进行颜色设置,一种是上面介绍的QSS,它的优点在于简单、通用,学过CSS的基本都不用再有学习成本,缺点在于针对具体控件,可以对颜色和外观进行部分设置,某些属性不支持。另一种是QPalette和QFont这些内置的外观设置,QPalette调色板类主要是对颜色类进行定义和设置,QFont主要对字体进行设置,配合QSize大小设置等,可以较好的完成控件的外观定义,不存在兼容性和不支持的情况,但是相对来说需要设置类型较多,需要多条语句才能实现,切QPalette主要是针对色彩类进行调整,控件的外型(如圆形按钮等)就需要配合QStyle类。

QPalette的设置较为复杂,将在高级专篇中介绍。QFont、QFontInfo、QFontDateBase以及QFontMetrics构成了Qt的字体类系统,我们将在后面的字体类章节专门介绍。但由于在接下来的学习中会经常用到,这里简要学习。

  API函数 

参数说明

返回值

功能作用

QFont(self,families,point,weight,italic)

families: Sequence[str], pointSize: int = -1, weight: int = -1, italic: bool = False

None

创建一个字体

QFont(self, family, pointSize, weight, italic)

family: str, pointSize: int = -1, weight: int = -1, italic: bool = False

None

创建一个字体

setBold(self, arg__1)

arg__1: bool

None

设置是否开启字体为粗体。

bold(self)

None

bool

获取是否开启字体为粗体。

setWeight(self, weight)

weight: PySide6.QtGui.QFont.Weight

None

使用Weight枚举值设置字体粗细

weight(self)

None

QFont.Weight

获取字体的粗细

setCapitalization(self, arg__1)

arg__1: PySide6.QtGui.QFont.Capitalization

None

设置字体的大小写模式

capitalization(self)

None

QFont.

Capitalization

获取字体的大小写模式

setFamily(self, arg__1)

arg__1: str

None

设置字体的族

famili(self)

None

str

获取字体的族

setItalic(self, b)

b: bool

None

设置是否开启斜体

italic(self)

None

bool

获取是否开启斜体

setPointSize(self, arg__1)

int

None

设置字体的点大小

pointSize(self)

None

int

获取字体的点大小

famliy属性是我们常规所说的字体的类型,比如“宋体、隶书、微软雅黑”等。但是,所设置的famlily需要是系统字体库中存在的字体。

setWeight方法是设置字体的粗细程度,他的参数是一个枚举类:

枚举类

枚举常量

枚举值

功能描述

QFont.Weight

Thin

100

ExtraLight

200

Light

300

Normal

400

Medium

500

DemiBold

600

Bold

700

ExtraBold

800

Black

900

setBold()方法的参数设置为True时,与setWeight(QFont.Weight.Bold)等价。

对于字体的颜色需要使用QPalette的前景色进行设置。

example:

# -*- codeing:utf-8 -*-
from PySide6.QtWidgets import QWidget,QApplication,QPushButton
from PySide6.QtGui import QFont,QPalette,QColor
import sys

class testWindow(QWidget):
    def __init__(self):
        super(testWindow, self).__init__()
        self.resize(500,500)
        self.setWindowTitle("QSS测试")

        btn = QPushButton("测试按钮",self)
        btn.move(200,200)
        btn.resize(100,50)
        font = btn.font()
        font.setFamily("宋体")
        font.setWeight(QFont.Weight.Bold)
        font.setItalic(True)
        font.setPointSize(15)
        btn.setFont(font)

        palette = btn.palette()
        palette.setColor(QPalette.ColorRole.ButtonText,QColor("red"))
        btn.setPalette(palette)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = testWindow()
    win.show()
    sys.exit(app.exec())

3.QStyle设置外观

除了上述的两种方法设置外观,针对自定义控件外形以及主题样式,Pyside6提供QStyle类可以对单个控件进行调整和自定义,也可以对整个QApplication进行调整和自定义。

Pyside6的内置窗口组件使用QStyle来完成几乎所有的绘图工作,确保它们看起来与原生窗口组件完全相同。下图显示了九种不同样式的QComboBox。

样式的设置,我们可以通过setStyle()方法进行操作。Pyside6提供了三种可以方便替换的内置系统样式类型“windows11”,“windows”,“windowsvista”和“Fusion”。我们可以通过app.style(QStyleFactory.creat("style"))方式更换系统样式。

from PySide6.QtWidgets import QApplication,QWidget,QComboBox,QStyleFactory
import sys

class testWindow(QWidget):
    def __init__(self):
        super(testWindow, self).__init__()
        self.setWindowTitle("style测试")
        self.resize(500,500)

        self.combobox = QComboBox(self)
        self.combobox.resize(200, 50)
        self.combobox.move((self.width()-self.combobox.width())/2,(self.height()-self.combobox.height())/2)

        # QStyleFactory.keys()获取内置系统样式的字符串列表
        styleList = QStyleFactory.keys()
        self.combobox.addItems(styleList)
        
        # 当combobox的当前条目文字改变时将系统风格样式改为条目内字体标识的样式
        self.combobox.currentTextChanged.connect(lambda text:app.setStyle(QStyleFactory.create(text)))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = testWindow()
    win.show()
    sys.exit(app.exec())

上面的代码只是简单的利用Pyside6提供的系统风格样式对控件外观进行改变。当然如果想自定义样式,搞得花里胡哨的,除了使用QSS和QPalette外,就需要自定义QStyle了。

QStyle相当复杂,对于使用Pyside6的我们比使用C++版本的Qt还难。区别在于,自定义QStyle经常需要查看控件源码,而源码只有C++版本的。首先,对于我这样对C++基本不太明白的,看起来非常吃力。同时,QStyle的自定义涉及大量的后续内容,包括基本控件的用法、QPalette的使用、字体系统的使用、绘制系统QPainter、QPen、QBrush的使用,QStyle的QProxyStyle、QCommonStyle的使用,QStyleOption的使用、控件绘制的层次等等。但是,这里面最难得还是看源码。。。。

所以,目前不对QStyle进行学习,只需要知道QWidget.setStyle()是干什么的就可以了。(就这个问题,自学的过程中花了两年多时间才知道他是干什么的)

下面的例子转载自:[Qt]自定义QStyle——实现QProgressBar自定义样式 - During丶 - 博客园 (cnblogs.com)

这个例子原是C++的Qt版本,我对其进行了Pyside6的改写,可以看看效果。

from PySide6.QtWidgets import QComboBox,QPushButton,QProxyStyle,QWidget,\
    QStyle,QStyleOption,QProgressBar,QApplication,QVBoxLayout,QStyleOptionProgressBar
from PySide6.QtGui import QPainter,QPalette,QColor,QBrush,QPen,QFont,QLinearGradient,QFontMetricsF
from PySide6.QtCore import Qt,QPointF,QRect,QTimer
import sys

class customStyle(QProxyStyle):
    ContentsRect = QRect(0,0,0,0)
    LabelRect = QRect(0,0,0,0)
    def drawControl(self, element, opt, p, w):
        super(customStyle, self).drawControl(element, opt, p, w)
        if element == QStyle.ControlElement.CE_ProgressBarContents:
            if isinstance(opt,QStyleOptionProgressBar):
                pb = opt
            else:
                pb = QStyleOptionProgressBar()
            rect = self.subElementRect(QStyle.SubElement.SE_ProgressBarContents,pb,w)
            customStyle.ContentsRect = rect
            minimum = pb.minimum
            maximum = pb.maximum
            progress = pb.progress
            pbBits = pb
            pbBits.rect = QRect(rect.x(), rect.y(), int(rect.width() * (progress) / (maximum-minimum)), rect.height())
            p.setBrush(QColor("#D3D3D3"))
            p.drawRoundedRect(rect,8,8)
            self.drawPrimitive(QStyle.PrimitiveElement.PE_IndicatorProgressChunk,pbBits,p,w)

        if element == QStyle.ControlElement.CE_ProgressBarGroove:
            p.setPen(Qt.GlobalColor.transparent)
            p.setBrush(Qt.BrushStyle.NoBrush)
            p.drawRect(opt.rect)

        if element == QStyle.ControlElement.CE_ProgressBarLabel:
            if isinstance(opt,QStyleOptionProgressBar):
                pBarOpt = opt
            else:
                pBarOpt = QStyleOptionProgressBar()
            pBarOpt.rect = self.subElementRect(QStyle.SubElement.SE_ProgressBarLabel,pBarOpt, w)
            progressNumber = pBarOpt.progress /(pBarOpt.maximum - pBarOpt.minimum)
            text = "已完成{}%".format(progressNumber * 100)
            font = p.font()
            font.setLetterSpacing(QFont.SpacingType.AbsoluteSpacing,2)
            p.setFont(font)
            if progressNumber > 0:
                mid = progressNumber
            else:
                mid = 0.001
            mid = 0.999 if mid >= 1 else mid
            textGradient = QLinearGradient(QPointF(pBarOpt.rect.left(), pBarOpt.rect.height()),
                                           QPointF(pBarOpt.rect.left(), pBarOpt.rect.top()))
            textGradient.setColorAt(0, Qt.GlobalColor.white);
            textGradient.setColorAt(mid, Qt.GlobalColor.white);
            textGradient.setColorAt(mid + 0.001, Qt.GlobalColor.darkGray);
            textGradient.setColorAt(1, Qt.GlobalColor.darkGray);
            p.setPen(QPen(QBrush(textGradient), 1))
            customStyle.LabelRect = QRect((customStyle.ContentsRect.width() - QFontMetricsF(p.font()).boundingRect("字").width() * len(text)) / 2, pBarOpt.rect.top(), QFontMetricsF(p.font()).boundingRect("字").width() * len(text), w.height())
            p.drawText(customStyle.LabelRect,Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextSingleLine,text)


    def subElementRect(self, element, option, widget):
        if element == QStyle.SubElement.SE_ProgressBarLabel:
            option.rect = customStyle.LabelRect
            return QProxyStyle().subElementRect(element, option, widget)
        elif element == QStyle.SubElement.SE_ProgressBarGroove:
            return widget.rect()

        else:
            return QProxyStyle().subElementRect(element, option, widget)

    def drawPrimitive(self, element, option, painter, widget):
        if element == QStyle.PrimitiveElement.PE_IndicatorProgressChunk:
            linear = QLinearGradient()

            linear.setStart(0, 0)
            linear.setFinalStop(widget.width(), widget.height())
            linear.setColorAt(0, QColor(255, 182, 193))
            linear.setColorAt(0.5, QColor(100, 149, 237))
            linear.setColorAt(1, QColor(255, 222, 173))
            painter.setPen(Qt.PenStyle.NoPen);
            painter.setBrush(linear);
            painter.drawRoundedRect(option.rect, 8, 8);

class testWindow(QWidget):
    def __init__(self):
        super(testWindow, self).__init__()
        self.resize(500,500)
        self.number = 0

        self.t = QProgressBar(self)
        self.t.setMaximum(100)
        self.t.setMinimum(0)

        self.btn = QPushButton("开始/暂停")
        self.btn.clicked.connect(self.control)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.progress)

        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.addWidget(self.t)
        layout.addWidget(self.btn)

    def progress(self):
        self.number += 10
        self.t.setValue(self.number)

    def control(self):
        if self.timer.isActive():
            self.timer.stop()
        else:
            self.timer.start(1000)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle(customStyle())
    win = testWindow()
    win.show()
    sys.exit(app.exec())



输出结果:

20240926-013941

十四、窗口及控件的显示与隐藏

API函数

参数说明

返回值

功能作用

setHidden(self, hidden)

hidden:bool

None

设置控件是否隐藏

isHidden(self)

None

bool

查看控件是否隐藏

hide()

None

None

隐藏窗口或小部件

setVisible(self, visible)

bool

None

设置控件是否可见

isVisible(self)

None

bool

查看控件是否可见

isVisibleTo(w)

QWidget

bool

如果小部件的直接或间接父对象可见,小部件可见返回Ture;否则返回Flase。(通常没用)

show(self)

None

None

显示窗口及其小部件

close(self)

None

bool

关闭窗口或小部件,如果关闭成功则返回Ture,否则返回Flase

上面的方法都很简单,功能说的很明白,不再解释说明。

十五、QWidget的右键菜单设置

所谓的右键菜单,就是当我们在当前系统页面或控件面板上点击右键时,会弹出一个命令菜单的面板。如在windows系统桌面右键的时候,会出现“复制”、“粘贴”等命令的一个面板。

API函数

参数说明

返回值

功能作用

setConetextMenuPolicy(self,policy)

policy

:Qt.ContextMenuPolicy

枚举类型

None

设置控件的右键菜单策略

contextMenuPolicy(self)

None

None

获取控件的右键菜单策略

QWidget默认是不显示右键菜单的,但是我们可以自己创建菜单再通过setConetextMenuPolicy()更改其菜单策略从而显示自定义的菜单。

policy参数是一个枚举类,如下:

枚举类

枚举常量

枚举值

功能描述

Qt.ContextMenuPolicy

NoContextMenu

0

控件没有自己特有的快捷菜单,使用控件父窗口或父容器的快捷菜单

DefaultConetextMenu

1

鼠标右键事件交给控件的conetextMenuEvent()函数处理

ActionsContextMenu

2

右键快捷菜单是控件或窗口的actions()方法获取的动作

CustomConetxtMenu

3

用户自定义的快捷菜单,右键鼠标时,发射cunstomContextMenuRequested(QPoint)信号,其中QPoint是鼠标右击是光标位置

PreventContextMenu

4

鼠标右键菜单交给控件的MousePressEvent()函数处理

上面所说的QWidget默认不显示菜单并非其菜单策略是NoContextMenu,而是由于其默认菜单策略为DefaultConetextMenu,但其内置的conetextMenuEvent()方法并没有实现菜单的内容。我们可以通过重写其conetextMenuEvent()方法进行实现。

当然,我们如果选择PreventContextMenu策略,就在MousePressEvent()中实现。而大部分情况下,我们会使用CustomConetxtMenu自己定义菜单,这是由于这个方法可以让我们调整菜单的弹出位置。

DefaultConetextMenu、CustomConetxtMenu、PreventContextMenu的实现都需要自定义菜单QMenu并设置子菜单或QAction,关于菜单会在后面学习。

这里举个例子,用最简单的方法,ActionsContextMenu来实现右键菜单。这其中最重要的是我们需要使用QWidget的addAction()或addActions()为其添加动作。

API函数

参数说明

返回值

功能作用

addAction(self, action)

action:QAction

None

添加动作

addActions(self, actions)
actions: Sequence[QAction]

None

添加动作列表

example:

from PySide6.QtWidgets import QApplication,QWidget
from PySide6.QtGui import QAction
from PySide6.QtCore import Qt
import sys

class testWindow(QWidget):
    def __init__(self):
        super(testWindow, self).__init__()
        self.setWindowTitle("右键菜单测试")
        self.resize(500,500)

        self.addActions([QAction("复制",self),QAction("粘贴",self),QAction("剪切",self)])
        self.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = testWindow()
    win.show()
    sys.exit(app.exec())

十六、QWidget的内容更新

API函数

参数说明

返回值

功能作用

update()

None

静态函数

None

刷新窗口

update(self,arg1)

arg1:union[QRegion,QBitmap,QRect]

None

刷新窗口指定区域。

update(self,x,y,w,h)

x:QPoint,y:QPoint,w:width,h:heigth

None

刷新窗口指定区域。

repaint()

None

静态函数

None

重绘窗口

repaint(self,arg1)

arg1:union[QRegion,QBitmap,QRect]

None

重绘窗口指定区域。

repaint(self,x,y,w,h)

x:QPoint,y:QPoint,w:width,h:heigth

None

重绘窗口指定区域。

Pyside6的应用程序在初始化或者改变内容(外观、样式、字体等)及绘图的时候,都会调用QWidget的paintEvent()事件函数。一般有两种方法来重绘widget。repaint()被调用之后,立即执行重绘,因此repaint是最快的,紧急情况下需要立刻重绘的可以使用repaint()。但是调用repaint的函数不能放到paintEvent中调用,会造成死循环。update()跟repaint()比较,update则更加有优越性。update()调用之后并不是立即重绘,而是将重绘事件放入主消息循环中,由main的event loop来统一调度的(其实也是比较快的)。update在调用paintEvent之前,还做了很多优化,如果update被调用了很多次,最后这些update会合并到一个大的重绘事件加入到消息队列,最后只有这个大的update被执行一次。同时也避免了repaint()中所提到的死循环。因此,一般情况下,我们调用update就够了,跟repaint()比起来,update是推荐使用的。

这部分内容不用太多理解,在后面事件的QPaintEvent中会中重点学习。

十七、QWidget父子控件及电脑屏幕坐标关系

在第4节中介绍了坐标点类并在鼠标的实例中进行了使用。而我们在实际开发过程中不管是创建控件还是绘图都会遇到父子控件的坐标转换及与屏幕之间的相对转换。比如播放器软件的音量调节键。当我们在主控件中点击声音按钮时会弹出音量调节框,那么音量调节框位置的确定过程就会涉及到子控件的位置相对于父控件的位置,以及弹出控件根据子控件位置定位到屏幕的位置。因为,我们的音量调节面板是通过其父控件进行定位的,而鼠标则是则是通过桌面进行定位的,那么我们就要统一坐标系(将二者统一到音量部件的父控件坐标系下或者屏幕坐标系下都可以),而同一坐标系就需要用到以下的方法。

API函数

参数说明

返回值

功能作用

mapFrom(self, arg__1, arg__2)

arg_1:QWidget

arg_2:QPoint/QPointF

QPoint/QPointF

将父容器的点映射成控件坐标系下的点

mapFromGlobal(self, arg__1)

arg_1:QPoint/QPointF

QPoint/QPointF

将屏幕坐标系下的点映射成控件坐标系下的点

mapFrompParent(self, arg__1)

arg_1:QPoint/QPointF

QPoint/QPointF

将父容器的点映射成控件坐标系下的点

mapTo( self, arg__1, arg__2)

arg_1:QWidget

arg_2:QPoint/QPointF

QPoint/QPointF

将控件坐标系下的点映射成父控件坐标系下的点

mapToGlobal(self, arg__1)

arg_1:QPoint/QPointF

QPoint/QPointF

将控件坐标系下的点映射成p屏幕坐标系下的点

mapToParent(self, arg__1)

arg_1:QPoint/QPointF

QPoint/QPointF

将控件坐标系下的点映射成父控件坐标系下的点

十八、拖拽与放置

实际开发的过程中经常跟会用鼠标拖放动作来完成一些操作,例如把一个docx文档拖到word中直接打开。拖放动作需要触发鼠标移动、鼠标释放及拖放的事件。这里只做简单叙述,具体将在事件章节的拖放事件小节进行学习。

十九、QWidget的属性设置

QWidget的属性主要通过setAttribute(arg__1[, on=true])进行设置,其参数为Qt.WidgetAttribute枚举类,这个没办法说。一些设置如:WA_AcceptDrops是接收鼠标拖放的数据,它与

setAcceptDrops(True)作用一样。其他很多方法,需要在具体学习或者工程中学习,例如WA_Hover就经常用于前面所述的QStyle自定义样式时使用。

二十、QWidget的信号

信号

参数说明

返回值

功能作用

objectNameChanged(str)

Str-传递新对象名给槽函数

None

窗口名称改变发射信号

destroyed(obj

Obj-被销毁的对象传递给槽函数

None

窗口被销毁发射信号

windowIconChanged(QIcon)

QIcon传递新图标给槽函数

None

窗口图标改变发射信号

windowTextChanged(str)

str—传递新图标文字给槽函数

None

窗口图标文字改变发射信号

windowTitleChanged(str)

str—传递新图标文字给槽函数

None

窗口标题改变发射信号

customContextMenuRequested(QPoint)

QPoint—右键菜单弹出点坐标

None

通过setContextMenuPolicy(Qt.CustomContextMenu)方法设置快捷菜单是自定义菜单,此时右击鼠标时发送信号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值