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

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配合其他事件来实现灵活。

下期我们介绍Qt的Style Sheet。

Qt---自定义界面之QStyle的更多相关文章

  1. Qt自定义标题栏

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt自定义标题栏     本文地址:http://techieliang.com/2017/1 ...

  2. paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制

    源地址:http://blog.csdn.net/attilax/article/details/12343625 paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制 效果图: ...

  3. QSet使用及Qt自定义类型使用QHash等算法

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QSet使用及Qt自定义类型使用QHash等算法     本文地址:http://techie ...

  4. Qt自定义插件编程小结

    qt自定义组件开发步骤演示.以下所有步骤的前提是自己先编译Qtcreator源码,最好生成release版的QtCreator,否则自定义的插件嵌入QtCreator会失败!!!(这个网上教程很多) ...

  5. Qt 自定义事件(三种方法:继承QEvent,然后Send Post就都可以了,也可以覆盖customEvent函数,也可覆盖event()函数)

    Qt 自定义事件很简单,同其它类库的使用很相似,都是要继承一个类进行扩展.在 Qt 中,你需要继承的类是 QEvent. 继承QEvent类,你需要提供一个QEvent::Type类型的参数,作为自定 ...

  6. Qt 自定义事件

    Qt 自定义事件很简单,同其它类库的使用很相似,都是要继承一个类进行扩展.在 Qt 中,你需要继承的类是 QEvent. 继承QEvent类,你需要提供一个QEvent::Type类型的参数,作为自定 ...

  7. Qt中如果通过QStyle自定义能够跨平台的界面控件

    我们经常会碰到需要定制界面控件的要求.如果只是在一个平台上,比如说你的控件只需要在Windows上显示,那很好办,Hard code 你的look and feel就可以了.但是如果界面需要在不同平台 ...

  8. 44.Qt通过子类化qstyle实现自定义外观

    main.cpp #include <QtGui> #include "brozedialog.h" #include "bronzestyle.h" ...

  9. Qt自定义滚动条(不使用样式表)

    前面使用Qt 样式表实现滚动条,在实际工作中,发现存在一些瑕疵,例如如果在主窗口中绘制背景,则有可能给滚动条染色,还有如果想实现特殊的效果,则必须使用自定义风格,即从QStyle的子类派生出新的类型. ...

随机推荐

  1. js判断是否使用的是微信浏览器

    代码如下: function is_weixin() { var ua = navigator.userAgent.toLowerCase(); return ua.match(/MicroMesse ...

  2. ELK 完整部署和使用 - 每天5分钟玩转 Docker 容器技术(90)

    上一节已经部署了容器化的 ELK,本节讨论如何将日志导入 ELK 并进行图形化展示. 几乎所有的软件和应用都有自己的日志文件,容器也不例外.前面我们已经知道 Docker 会将容器日志记录到 /var ...

  3. sass学习--安装ruby

    1.下载ruby:https://rubyinstaller.org/downloads/ 2.安装完ruby之后,在开始菜单中,找到刚才我们安装的ruby,打开Start Command Promp ...

  4. HTML学习笔记 域元素(form表单、textarea文本域、fieldset域集合、input使用) 案例 第四节 (原创)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 6. ZooKeeper访问控制列表

    ZooKeeper的数据模型提供了ACL机制来控制访问znode. 在创建znode时,ACL将确定你可以在znode上执行的各种操作的权限. ZooKeeper ACL模型与Unix / Linux ...

  6. 原生JS—实现图片循环切换的两种方法

    今天我们主要讲讲如何使用原生JS实现图片的循环切换的方法.多余的话我们就不多说了,我们一个一个开始讲吧. 1  原生JS实现图片循环切换 -- 方法一 在上栗子之前我们先简单介绍一下所用的一些知识点. ...

  7. nginx-http-concat资源文件合并模块

    网页中引入多个CSS和JS的时候,浏览器会发出很多(css个数+js个数)次网络请求,甚至有的网页中有数十个以上的CSS或JS文件,用户体验特别不好,正好可以利用nginx-http-concat n ...

  8. Winform界面中实现菜单列表的动态个性化配置管理

    在我们一般的应用系统里面,由于系统是面向不同类型的用户,我们所看到的菜单会越来越多,多一点的甚至上百个,但是我们实际工作接触的菜单可能就是那么几个,那么对于这种庞大的菜单体系,寻找起来非常不便.因此对 ...

  9. Centos6.9安装vsftpd并配置多用户的方法

    本文介绍了Centos6.9安装vsftpd并配置多用户的方法,分享给大家,具体如下: 一.安装vsftpd ? 1 2 3 4 5 6 7 8 #安装vsftpd yum -y install vs ...

  10. keras 修仙笔记一

    对于牛逼的程序员,人家都喜欢叫他大神:因为大神很牛逼,人家需要一个小时完成的技术问题,他就20分钟就搞定.Keras框架是一个高度集成的框架,学好它,就犹如掌握一个法宝,可以呼风唤雨.所以学keras ...