Qt——自定义界面之QStyle

news2025/1/17 3:08:47

1. Qt控件结构简介

首先我们要来讲讲GUI控件结构,这里以QComboBox为例:

一个完整的控件由一种或多种GUI元素构成:

  • Complex Control Element。
  • Primitive Element。
  • Control Element。

1.1 Complex Control Element

Complex control elements contain sub controls. Complex controls behave differently depending on where the user handles them with the mouse and which keyboard keys are pressed.

Complex Control Elements(简称CC)包含子控件。根据用户对鼠标和键盘的不同处理,CC控件的表现也不同。上图中的QComboBox仅包含一个CC控件CC_ComboBox,该复杂控件又包含三个子控件(SC,Sub Control)SC_ComboBoxFrame、SC_ComboBoxArrow、SC_ComboBoxEditField。

1.2 Control Element

A control element performs an action or displays information to the user.

控件元素与用户交互相关,例如PushButton、CheckBox等等。QComboBox只有一个CE_ComboBoxLabel用以在ComboBox左侧展示当前选中或者正在编辑的文字。

1.3 Primitive Element

Primitive elements are GUI elements that are common and often used by several widgets.

主元素代表那些公共的GUI元素,主要用于GUI控件复用。例如PE_FrameFocusRect这个主元素就进场被多种控件用来绘制输入焦点。QComboBox包含两个主元素PE_IndicatorArrowDown、PE_FrameFocusRect。

2. QStyle、QProxyStyle、QStyleFactory简介

QStyle是一套抽象接口,它定义了实现界面控件外观的一系列api并且不能用来被实例化:

  • virtual void drawComplexControl(...) 绘制复杂元素。
  • virtual void drawControl(...) 绘制控件元素。
  • virtual void drawPrimitive(...) 绘制主元素。
  • ...
  • virtual QSize sizeFromContent(...) 获取控件大小。
  • virtual QRect subControlRect(...) 获取子控件位置及大小。
  • virtual QRect subElementRect(...) 获取子元素位置及大小。

QProxyStyle实现了QStyle所有的抽象接口,并且默认保持系统风格,在Linux、Windows、Mac系统上样式如下:

QStyleFactory类提供了当前可应用的所有QStyle风格实现,在Windows系统上我获得如下几种风格(具体结果见最后一小节):

  1. Windows
  2. WindowsXp
  3. WindowsVista
  4. Fusion

我们可以通过QStyleFactory::keys()和QStyleFactory::create()来获取这些可用的风格并且设置到需要的QWidget上用以改变GUI风格。

3. 自定义QComboBox Style

这里我们通过实现一个QStyle来自定义QComboBox的样式。

这个自定义的QComboBox样式分为两部分,箭头区域和非箭头区域。非箭头区域包含CE_ComboBoxLabel和SC_CombBoxListBoxPopup。由于QStyle不负责绘制下拉框(由delegate绘制),我们只能更改下拉框的位置和大小(这里我们不做改变)。 箭头区域包含背景区和PE_IndicatorArrowDown。

箭头区域我们用一个辐射渐变来绘制背景,并且在鼠标Hover或者按下的时候更改渐变的颜色来重绘,中间的下拉箭头我们复用QProxyStyle的实现来完成。

void CustomeStyle::drawArrowArea(const QStyleOptionComplex *option,
                             QPainter *painter,
                             const QWidget *widget) const
{
    QRect arrowBoxRect = option->rect;
    arrowBoxRect.adjust(option->rect.width() * 0.8, 0, 0, 0);

    auto arrowAreaColor = Qt::darkCyan;
    m_arrowAreaHovered = arrowBoxRect.contains(widget->mapFromGlobal(QCursor::pos()));
    if (option->state & State_MouseOver && m_arrowAreaHovered)
        arrowAreaColor = Qt::cyan;
    else if (option->state & State_On && m_arrowAreaHovered)
        arrowAreaColor = Qt::darkMagenta;

    QRadialGradient gradient(arrowBoxRect.center(),
                            arrowBoxRect.width());
    gradient.setColorAt(1.0, arrowAreaColor);
    painter->fillRect(arrowBoxRect, QBrush(gradient));


    auto arrowDownOption = *option;
    auto adjustPixel = arrowBoxRect.width() * 0.2;
    arrowDownOption.rect = arrowBoxRect.adjusted(adjustPixel,
                                                adjustPixel,
                                                -adjustPixel,
                                                -adjustPixel);
    drawPrimitive(PE_IndicatorArrowDown, &arrowDownOption, painter, widget);
}

非肩头区域即CE_ComboBoxLabel,我们用4种颜色的线性渐变来绘制,同箭头区域一样她也会根据当前的状态更改渐变颜色来增加交互效果:

auto comboBoxOption = qstyleoption_cast<const QStyleOptionComboBox*>(option);
if (comboBoxOption == nullptr)
    return;

QColor gradientColors[] = {
    Qt::yellow,
    Qt::green,
    Qt::blue,
    Qt::red
};
QColor penColor = Qt::white;
if (option->state & State_MouseOver && !m_arrowAreaHovered) {
    for (auto& color : gradientColors)
        color.setAlpha(80);
    penColor.setAlpha(80);
} else if (option->state & State_On && !m_arrowAreaHovered) {
    for (auto& color : gradientColors)
        color = color.darker(300);
    penColor = penColor.darker(300);
}

QRect labelRect = comboBoxOption->rect;
labelRect.adjust(0, 0, -(labelRect.width() * 0.2), 0);

QLinearGradient linearGradient(labelRect.topLeft(), labelRect.bottomRight());
for (int i = 0; i < 4; ++i) {
    linearGradient.setColorAt(0.25 *i, gradientColors[i]);
}

painter->fillRect(labelRect, QBrush(linearGradient));

painter->setPen(QPen(penColor));
painter->drawText(labelRect, comboBoxOption->currentText, QTextOption(Qt::AlignCenter));

4. 实现效果

完整代码见链接。

5. 总结

QStyle优点:

  • 统一风格。特定类型的控件效果都统一,如果要多处用到同一种类型的控件,用QStyle会比较方便。

QStyle缺点:

  • 实现涉及Qt GUI控件结构细节,涉及知识面太多太杂。
  • 只有Qt控件使用了QStyle,系统及第三方实现的控件不保证有效。
  • 实现起来太复杂,不如重写QWidget的paintEvent配合其他事件来实现灵活。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/387301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言跬步】——指针数组和数组指针(指针进阶)

一.指针数组和数组指针的区别 1.指针数组是数组&#xff0c;是一种存放指针的数组&#xff1b; 例如&#xff1a; int* arr[10]; 2.数组指针是指针&#xff0c;是一种指向数组的指针&#xff0c;存放的是数组的地址&#xff1b; 例如&#xff1a; int arr[5]; int (p)[5]&a…

Redis学习(二):Redis安装测试

概述 Redis是什么 Redis, Remote Dictionary Server, 即远程字典服务。免费开源的数据库。 由C语言编写&#xff0c;支持网络&#xff0c;可基于内存亦可持久化的日志型、KV数据库&#xff0c;并提供所种语言的API。 Redis能干嘛 用于内存存储&#xff0c;持久化。rdb、ao…

[学习笔记]黑马程序员Spark全套视频教程,4天spark3.2快速入门到精通,基于Python语言的spark教程

文章目录视频资料&#xff1a;一、Spark基础入门&#xff08;环境搭建、入门概念&#xff09;第二章&#xff1a;Spark环境搭建-Local2.1 课程服务器环境2.2 Local模式基本原理2.3 安装包下载2.4 Spark Local模式部署第三章&#xff1a;Spark环境搭建-StandAlone3.1 StandAlone…

驾考笔记_2023

科目一1> 扣分制度1.1> 超速1.2> 超载1.3> 车牌1.4> 速记口诀2> 满分学习2.1> 消分学习2.2> 满分重考&#xff1b;3> 罚款制度3.1> 考证3.2> 审验教育3.3> 其他4> 车速规定4.1> 高速_最低限速4.2> 普路_最高限速5> 证件相关5.…

双指针法|位运算|离散化|区间合并

目录 双指针算法 位运算 离散化 序列合并 双指针算法 题目描述&#xff1a;1.输入n个单词&#xff0c;每个单词在输入的时候按空格隔开&#xff0c;之后打印出每个单词且换行 #include<iostream> #include <string>using namespace std; int main() {strin…

计算机网络中的原码、反码、补码

写在前面 原码、反码、补码是计算机组成原理中的概念&#xff0c;是计算机网络的基础知识之一。这些概念是为了处理二进制数的符号位而引入的&#xff0c;常用于计算机中的整数运算&#xff0c;也常用于数据存储和传输等领域。因此&#xff0c;了解和掌握这些概念对于理解计算机…

软测入门(五)接口测试Postman

Postman 一款Http接口收工测试工具。如果做自动化测试会使用jemter做。 安装 去官网下载即可。 https://www.postman.com/downloads/?utm_sourcepostman-home 功能介绍 页面上的单词基本上都能了解&#xff0c;不多介绍。 转代码&注释 可将接口的访问转为其他语言的…

一款小巧、开源免费、可观性强的流量监控软件——TrafficMonitor

名人说&#xff1a;君子生非异也&#xff0c;善假于物也。——荀子 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; o(‐&#xff3e;▽&#xff3e;‐)o很高兴你打开了这篇博客&#xff0c;跟着步骤一步步尝试安装吧。✧ 目录背景缘由一、…

自动驾驶目标检测项目实战(一)—基于深度学习框架yolov的交通标志检测

自动驾驶目标检测项目实战——基于深度学习框架yolov的交通标志检测 目前目标检测算法有很多&#xff0c;流行的就有faster-rnn和yolov&#xff0c;本文使用了几年前的yolov3框架进行训练&#xff0c;效果还是很好&#xff0c;当然也可以使用更高版本的Yolov进行实战。本代码使…

分布式事务之TCC与SAGA

分布式事务之TCC与SAGA 在《关于分布式事务的理解》&#xff0c;介绍了可靠消息队列的实现原理&#xff0c;虽然它也能保证最终的结果是相对可靠的&#xff0c;过程也足够简单&#xff08;相对于 TCC 来说&#xff09;&#xff0c;但现在你已经知道&#xff0c;可靠消息队列的…

从零起步认识XAML

新建WPF项目 在Visual Studio 2022中&#xff0c;选择"创建新项目" 选择“WPF Application” 点击下一步 点击创建。 执行DebugStart Debugging菜单命令&#xff0c;或者快捷键为F5&#xff0c;或者工具栏上的图标。 在Solution Explorer窗口&#xff08;ViewSoluti…

smardaten应用商超 | 邀您沉浸式体验企业级无代码开发

宝子们&#xff0c;还在发愁登录smardaten后缺少模板和数据吗&#xff1f;从0到1构建完整应用费时费力&#xff1f;别急&#xff0c;一大波模板资源已经装上车&#xff0c;这就给你安排上&#xff01;【smardaten应用商超】基于数睿数据长期服务软件企业、大型客户的经验&#…

Redis实现服务注册与服务发现源码阅读(Go语言)

Redis实现服务注册与服务发现源码阅读 背景 近期在看开源项目CloudWeGo中看到目前GoLang微服务框架Hertz中支持通过Redis实现服务注册与服务发现功能。便想着阅读下源码 源码阅读 gut clone了hertz-contrib后看到在一级目录下有目前各种主流的服务注册与发现的实现方案。为…

【数据结构初阶】二叉树顺序结构:堆的实现

前言 前边077带着大家学习了树与二叉树的相关概念&#xff0c;这篇文章我们来实现一个二叉树的顺序结构。 二叉树的顺序结构 普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉…

SQL注入基础入门篇 注入思路及常见的SQL注入类型总结

SQL注入基础入门篇1. SQL注入的概念1.1 什么是SQL注入&#xff1f;1.2 注入过程1.3 SQL注入的分类2. 注入思路3. 第一次注入3.1 寻找注入点3.2 构造攻击语句3.2.1 数据出在哪里&#xff1f;3.2.2 怎么有序的获取核心数据&#xff1f;3.2.2.1 基础信息查询3.2.2.2 表名&#xff…

TCP三次握手与四次挥手(一次明白)

TCP基本信息 默认端口号:80 LINUX中TIME_WAIT的默认时间是30s TCP三次握手 三次握手过程:每行代表发起握手到另一方刚刚收到数据包时的状态 客户端服务端客户端状态服务端状态握手前CLOSELISTEN客户端发送带有SYN标志的数据包到服务端一次握手SYN_SENDLISTEN二次握手服务端发送…

Java项目(一些注解、依赖

文章目录常用的几个注解DataAllArgsConstructorNoArgsConstructorSetterGetterEqualsAndHashCodeLog4j/Slf4jMYBatis-Plus常用注解TableNameTableIdTableFieldTableLogicMapperMapperMapperScanpom.xml中加入依赖创建项目常用的几个注解 Data 注在类上&#xff0c;提供类的ge…

cv2.addWeighted 操作 np.array 踩坑记录

cv2.addWeighted函数是把两张图片img1, img2达到融合的效果&#xff0c; 看官网的解释&#xff0c;下图中f0和f1代表两张图片&#xff0c; 用法是这样的 import cv2alpha 0.6 beta (1.0 - alpha)src1 cv2.imread("img1.jpg") src2 cv2.imread("img2.jpg&q…

MQ-2烟雾传感器模块功能实现(STM32)

认识MQ-2模块与其工作原理 MQ-2型烟雾传感器属于二氧化锡半导体气敏材料&#xff0c;属于表面离子式N型半导体。当处于200~300摄氏度时&#xff0c;二氧化锡吸附空气中的氧&#xff0c;形成氧的负离子吸附&#xff0c;使半导体中的电子密度减少&#xff0c;从而使其电阻值增加。…

【C语言复习】C语言中的数组与指针

数组与指针复习写在前面数组和指针指针基础概念进阶知识指针的分类指针和数组笔试题写在前面 数组和指针小节&#xff0c;主要分为以下关键点&#xff1a; 常见指针分类&#xff0c;如指针数组、数组指针、函数指针等。什么是数组/ 指针有关数组和指针的题目数组传参 我们也…