当前位置: 首页 > news >正文

Qt---自定义界面之QStyle

最近想学习下Qt的自定义界面,因此花了点时间看了下QStyle,,,,结果很难受,这一块涉及到一大块GUI的具体实现方式,看得我很头疼。想看第一手资料并且英语功底不错的可以直接上qt文档,下面我会以易懂的方式简单讲解下。

1. Qt控件结构简介

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

widget-structurecombobox-pic

一个完整的控件由一种或多种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系统上样式如下:

system-look

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. 实现效果

style1style2style3style4style5style6

完整代码见链接。

5. 总结

QStyle优点:

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

QStyle缺点:

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

下期我们介绍Qt的Style Sheet。

转载于:https://www.cnblogs.com/lgxZJ/p/8038839.html

相关文章:

  • 再学习之Spring(面向切面编程).
  • java:解决eclipse配置Tomcat时找不到server选项
  • Qt532.QSettings_默认分隔符
  • python 高阶函数三 filter()和sorted()
  • bzoj1036[ZJOI2008]树的统计Count 树链剖分+线段树
  • 自然语言处理入门学习(一)
  • 文件和目录权限chmod 更改所有者和所属组chown umask 隐藏权限lsattr/chattr
  • 面向对象java知识汇总题
  • PAT 1023.组个最小数
  • mongodb for windows安装
  • 【比赛】NOIP2017 宝藏
  • gdb调试多线程程序总结
  • Excel2016通过宏生成拼音码
  • Web离线应用解决方案——ServiceWorker
  • am335x SPI spi_d0, spi_d1 out, in 模式设定
  • [译] 怎样写一个基础的编译器
  • 【Leetcode】104. 二叉树的最大深度
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • android 一些 utils
  • Docker下部署自己的LNMP工作环境
  • Java Agent 学习笔记
  • Javascript 原型链
  • js继承的实现方法
  • leetcode讲解--894. All Possible Full Binary Trees
  • maya建模与骨骼动画快速实现人工鱼
  • PaddlePaddle-GitHub的正确打开姿势
  • ubuntu 下nginx安装 并支持https协议
  • Yii源码解读-服务定位器(Service Locator)
  • 从零开始学习部署
  • 分享一份非常强势的Android面试题
  • 基于web的全景—— Pannellum小试
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 一些css基础学习笔记
  • 用jquery写贪吃蛇
  • Semaphore
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • # centos7下FFmpeg环境部署记录
  • # 安徽锐锋科技IDMS系统简介
  • (1)STL算法之遍历容器
  • (pojstep1.1.2)2654(直叙式模拟)
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (十三)Java springcloud B2B2C o2o多用户商城 springcloud架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)...
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (转)平衡树
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转)一些感悟
  • ../depcomp: line 571: exec: g++: not found
  • .net mvc 获取url中controller和action
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • /etc/fstab 只读无法修改的解决办法
  • @font-face 用字体画图标
  • @vue/cli 3.x+引入jQuery
  • [ C++ ] STL---仿函数与priority_queue
  • []使用 Tortoise SVN 创建 Externals 外部引用目录