前言

使用 qss 可以很方便地改变 QSlider 的样式,但是有些情况下 qss 无法满足我们的需求。比如下图所示样式:

如果直接使用 qss 将 handle 的内圆设置为透明背景,会看到 handle 下面的 groove ,而且画出来的圆环还不圆,如下图所示:

这时候就需要使用 QStyle 来重绘 QSlider,关于 QStyle 的介绍可以参见 《QStyle设置界面的外观和QCommonStyle继承关系图讲解和使用》,这里不过多赘述(才不是因为我自己也说不清楚)。

实现过程

对于 QSlider 这种比较复杂小部件,需要重写 QProxyStyledrawComplexControl()subControlRect(),前者决定了 QSlider 的样式,后者用来获取各个子控件所在的矩形区域。为了演示的方便,代码中只重绘了水平滑动条的样式。

  1. 首先绘制 groove 子控件,从动图中可以看到,要想防止看到 handle 下面的 groove,需将 groove 拆分成两段来绘制:一段是 sub-page 部分,另一段是 add-page 部分;
  2. 接着绘制 handle 子控件,handle 子控件有三个部分:透明内圆、不透明圆环以及透明外边距。绘制圆环可以实例化 QPainterPathaddEllipse() 添加两个不同半径的同心圆之后再 painter.drawPath(path)。为了响应 hoverpressed,需要分别在 opt.activeSubControls == QProxyStyle.SC_SliderHandle 以及 widget.isSliderDown() 时更新滑块样式。

下面是具体代码:

# coding:utf-8
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QPoint, QRectF
from PyQt5.QtGui import QColor, QMouseEvent, QPainter, QPainterPath
from PyQt5.QtWidgets import (QProxyStyle, QSlider, QStyle, QStyleOptionSlider,
QWidget) class HollowHandleStyle(QProxyStyle):
""" 滑块中空样式 """ def __init__(self, config: dict = None):
"""
Parameters
----------
config: dict
样式配置
"""
super().__init__()
self.config = {
"groove.height": 3,
"sub-page.color": QColor(255, 255, 255),
"add-page.color": QColor(255, 255, 255, 64),
"handle.color": QColor(255, 255, 255),
"handle.ring-width": 4,
"handle.hollow-radius": 6,
"handle.margin": 4
}
config = config if config else {}
self.config.update(config) # 计算 handle 的大小
w = self.config["handle.margin"]+self.config["handle.ring-width"] + \
self.config["handle.hollow-radius"]
self.config["handle.size"] = QSize(2*w, 2*w) def subControlRect(self, cc: QStyle.ComplexControl, opt: QStyleOptionSlider, sc: QStyle.SubControl, widget: QWidget):
""" 返回子控件所占的矩形区域 """
if cc != self.CC_Slider or opt.orientation != Qt.Horizontal or sc == self.SC_SliderTickmarks:
return super().subControlRect(cc, opt, sc, widget) rect = opt.rect if sc == self.SC_SliderGroove:
h = self.config["groove.height"]
grooveRect = QRectF(0, (rect.height()-h)//2, rect.width(), h)
return grooveRect.toRect() elif sc == self.SC_SliderHandle:
size = self.config["handle.size"]
x = self.sliderPositionFromValue(
opt.minimum, opt.maximum, opt.sliderPosition, rect.width())
# 解决滑块跑出滑动条的情况
x *= (rect.width()-size.width())/rect.width()
sliderRect = QRectF(x, 0, size.width(), size.height())
return sliderRect.toRect() def drawComplexControl(self, cc: QStyle.ComplexControl, opt: QStyleOptionSlider, painter: QPainter, widget: QWidget):
""" 绘制子控件 """
if cc != self.CC_Slider or opt.orientation != Qt.Horizontal:
return super().drawComplexControl(cc, opt, painter, widget) grooveRect = self.subControlRect(cc, opt, self.SC_SliderGroove, widget)
handleRect = self.subControlRect(cc, opt, self.SC_SliderHandle, widget)
painter.setRenderHints(QPainter.Antialiasing)
painter.setPen(Qt.NoPen) # 绘制滑槽
painter.save()
painter.translate(grooveRect.topLeft()) # 绘制划过的部分
w = handleRect.x()-grooveRect.x()
h = self.config['groove.height']
painter.setBrush(self.config["sub-page.color"])
painter.drawRect(0, 0, w, h) # 绘制未划过的部分
x = w+self.config['handle.size'].width()
painter.setBrush(self.config["add-page.color"])
painter.drawRect(x, 0, grooveRect.width()-w, h)
painter.restore() # 绘制滑块
ringWidth = self.config["handle.ring-width"]
hollowRadius = self.config["handle.hollow-radius"]
radius = ringWidth + hollowRadius path = QPainterPath()
path.moveTo(0, 0)
center = handleRect.center() + QPoint(1, 1)
path.addEllipse(center, radius, radius)
path.addEllipse(center, hollowRadius, hollowRadius) handleColor = self.config["handle.color"] # type:QColor
handleColor.setAlpha(255 if opt.activeSubControls !=
self.SC_SliderHandle else 153)
painter.setBrush(handleColor)
painter.drawPath(path) # 滑块按下
if widget.isSliderDown():
handleColor.setAlpha(255)
painter.setBrush(handleColor)
painter.drawEllipse(handleRect)

测试

# coding:utf-8
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QApplication, QWidget, QSlider class Demo(QWidget): def __init__(self):
super().__init__()
self.resize(300, 150)
self.setStyleSheet("Demo{background: rgb(184, 106, 106)}") # 改变默认样式
style = {
"sub-page.color": QColor(70, 23, 180)
}
self.slider = QSlider(Qt.Horizontal, self)
self.slider.setStyle(HollowHandleStyle(style)) # 需要调整高度
self.slider.resize(200, 28)
self.slider.move(50, 61) if __name__ == '__main__':
app = QApplication(sys.argv)
w = Demo()
w.show()
sys.exit(app.exec_())

如何在pyqt中使用 QStyle 重绘 QSlider的更多相关文章

  1. 如何在pyqt中自定义无边框窗口

    前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ...

  2. 如何在pyqt中实现窗口磨砂效果

    磨砂效果的实现思路 这两周一直在思考怎么在pyqt上实现窗口磨砂效果,网上搜了一圈,全都是 C++ 的实现方法.正好今天查python的官方文档的时候看到了 ctypes 里面的 HWND,想想倒不如 ...

  3. 如何在pyqt中实现win10亚克力效果

    亚克力效果的实现思路 上一篇博客<如何在pyqt中实现窗口磨砂效果> 中实现了win7中的Aero效果,但是和win10的亚克力效果相比,Aero还是差了点内味.所以今天早上又在网上搜了一 ...

  4. 如何在pyqt中通过调用 SetWindowCompositionAttribute 实现Win10亚克力效果

    亚克力效果 在<如何在pyqt中实现窗口磨砂效果>和<如何在pyqt中实现win10亚克力效果>中,我们调用C++ dll来实现窗口效果,这种方法要求电脑上必须装有MSVC.V ...

  5. 如何在pyqt中在实现无边框窗口的同时保留Windows窗口动画效果(一)

    无边框窗体的实现思路 在pyqt中只要 self.setWindowFlags(Qt.FramelessWindowHint) 就可以实现边框的去除,但是没了标题栏也意味着窗口大小无法改变.窗口无法拖 ...

  6. 如何在pyqt中给无边框窗口添加DWM环绕阴影

    前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...

  7. 如何在pyqt中实现带动画的动态QMenu

    弹出菜单的视觉效果 QLineEdit 原生的菜单弹出效果十分生硬,而且样式很丑.所以照着Groove中单行输入框弹出菜单的样式和动画效果写了一个可以实现动态变化Item的弹出菜单,根据剪贴板的内容是 ...

  8. 如何在 pyqt 中捕获并处理 Alt+F4 快捷键

    前言 如果在 Windows 系统的任意一个窗口中按下 Alt+F4,默认行为是关闭窗口(或者最小化到托盘).对于使用了亚克力效果的窗口,使用 Alt+F4 最小化到托盘,再次弹出窗口的时候可能出现亚 ...

  9. 『转载』C# winform 中dataGridView的重绘(进度条,虚线,单元格合并等)

    原文转载自:http://hi.baidu.com/suming/item/81e45b1ab9b4585f2a3e2243 最近比较浅的研究了一下dataGridView的重绘,发现里面还是有很多东 ...

随机推荐

  1. 1067 - Combinations

    1067 - Combinations   PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB Giv ...

  2. Java 8 的内存结构

    Java8内存结构图 虚拟机内存与本地内存的区别 Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用 ...

  3. 第十个知识点:RSA和强RSA问题有什么区别?

    第十个知识点:RSA和强RSA问题有什么区别 这个密码学52件事数学知识的第一篇,也是整个系列的第10篇.这篇介绍了RSA问题和Strong-RSA问题,指出了这两种问题的不同之处. 密码学严重依赖于 ...

  4. Direct and Indirect Effects

    目录 概 主要内容 CDE NDE NIE TDE, TIE, PDE, PIE Judea Pearl. Direct and indirect effects. In Proceedings of ...

  5. Chapter 15 Outcome Regression and Propensity Scores

    目录 15.1 Outcome regression 15.2 Propensity scores 15.3 Propensity stratification and standardization ...

  6. 计算机系统2->从芯片说起 | 芯片怎样诞生

    这部分数字逻辑课上老师在讲CMOS部分时有讲过,当时在课堂上放了一个全英的视频,没怎么看懂,现在在研究计算机系统,自底层说起,也得从这讲起. 主要参考: <嵌入式C语言自我素养> b站相关 ...

  7. Linux进程管理之基本指令

    目录 基本介绍 显示系统执行的进程 指令 ps - aux 常用选项 每行栏目的含义 查看父进程 终止进程 相关指令 实用案例 踢掉某个非法登录用户 终止远程登录服务sshd,在适当的时候再次重启ss ...

  8. 使用 Eclipse 创建一个静态的登录页面

    要求: 使用 Eclipse 创建一个静态的登录页面 实现步骤: 在 Eclipse 中,点击"File",显示菜单,选择"New" "Other&q ...

  9. 使用 jQuery 操作页面元素的方法,实现浏览大图片的效果,在页面上插入一幅小图片,当鼠标悬停到小图片上时,在小图片的右侧出现与之相对应的大图片

    查看本章节 查看作业目录 需求说明: 使用 jQuery 操作页面元素的方法,实现浏览大图片的效果,在页面上插入一幅小图片,当鼠标悬停到小图片上时,在小图片的右侧出现与之相对应的大图片 实现思路: 在 ...

  10. 论文翻译:2021_Semi-Blind Source Separation for Nonlinear Acoustic Echo Cancellation

    论文地址:https://ieeexplore.ieee.org/abstract/document/9357975/ 基于半盲源分离的非线性回声消除 摘要: 当使用非线性自适应滤波器时,数值模型与实 ...