用Qt写软件系列三:一个简单的系统工具之界面美化
前言
在上一篇中,我们基本上完成了主要功能的实现,剩下的一些导出、进程子模块信息等功能,留到后面再来慢慢实现。这一篇来讲述如何对主界面进行个性化的定制。Qt库提供的只是最基本的组件功能,使用这些组件开发出来的软件基本上个性可言。如果开发的产品只讲究实用性,那么UI体验尚可搁置一边。如果要面向客户推广部署,那么改善一下UI视觉效果对于产品的推广也会有莫大的帮助。闲话不多说。先来对比一下界面个性化定制前后的效果:


先不说界面美化之后,界面有多绚丽、震撼人心。但是,突出产品主题、彰显个性这块倒是不折不扣。UI设计毕竟是一门学问,不然也不会有视觉交互师这种职业了。那么,如何用Qt来对软件界面进行美化呢?
界面个性化定制
Qt开发中有两种方法来进行UI定制:Qt二维绘图(Qt 2D drawing and painting)以及Qt样式表(Qt Style Sheet)。通常这两种方法需要结合一起使用,以发挥其强大的作用。下面,我们就一起来看看,如何开始变身。
标题组件
首先对比一下标题栏前后的不同:

那么如何做到这样呢?Qt提供的窗口都自带了三个默认的按钮:放大、缩小、关闭。而我们只有两个按钮:缩小、关闭。显然,按钮的绘制需要我们手动干涉。那么,手动绘制的话绘制到哪里去呢?通过什么方法呢?怎么实现默认按钮的功能呢?看下一张图我们似乎神马都明白了:

整个一“窗中窗”啊!也就是说,我把默认的窗口边框给去掉了,什么标题啊,按钮啊都是自己手动绘制的。怎么绘制的呢?这其实也简单,通过窗口布局管理器啊。这么一规划,整个窗口就可以这样去实现了:

不过,我们得找到几张按钮状态背景图,分别对应不同的按钮状态(按下、悬停、正常)。然后重写鼠标事件(mouseMoveEvent, mousePressedEvent, enterEvent, leaveEvent等)来切换按钮的背景图,这样就实现了按钮的不同状态。当然,这些都需要Qt绘图类的参与。几个比较重要的绘图类:QPainter, QPixmap, QColor,……,尤其是QPainter类及QPainterPath等,恰当的使用能带来绘图质量的大幅提高。
窗口内容布局
由上面的规划图可以看出,内容布局由三个部分组成上方(top layout)的行编辑框、两个按钮,中间及下面的两个QTableView。那么就先看看上方的top layout怎么个实现。这倒简单,一个行编辑框(QLineEdit)、两个下推按钮(QPushButton),用水平布局管理器一拉就完成了。那么如何进行美化了? 我是这么做的,C++代码部分:
m_filterexp = new QLineEdit(this);
m_filterexp->setPlaceholderText(QStringLiteral("Filter expression"));
m_filterexp->setContentsMargins(, , , );
m_refreshBtn = new QPushButton(QStringLiteral("Refresh"), this);
m_exportBtn = new QPushButton(QStringLiteral("Export..."), this);
m_refreshBtn->setObjectName("refreshBtn");
m_exportBtn->setObjectName("exportBtn");
m_refreshBtn->setFixedSize(, );
m_exportBtn->setFixedSize(, );
m_filterexp->setFixedHeight();
余下的工作交给Qt Style Sheet来做吧。我们在上面设置了按钮的Object name,这里的QSS选择器就用#来选择,相当于CSS里面的ID选择器。
QPushButton#refreshBtn, QPushButton#exportBtn {
border-radius: 2px;
border: 1px solid rgb(, , );
background:transparent;
color: green;
}
QPushButton#refreshBtn:hover {
background: #86BA10;
}
QPushButton#exportBtn:hover {
background: #86BA10;
}
正常状态我们仅仅用淡绿色给他们描个边,背景色设置为透明,圆角2个像素,当鼠标悬停在按钮上面的时候,我们就用淡绿色绘制按钮背景。效果如下,就这样吧,简单大方。而中间部分的两个QTableView是重点。


QTableView的美化
QTableView分成表头(Header)和表体(body)两部分。对于表头,我们需要做的不多,仅仅是换下背景色,去掉分节虚线,隐藏掉垂直表头。于是:
m_procssTableView->verticalHeader()->hide();
m_procssTableView->horizontalHeader()->setSectionsClickable(false);
m_procssTableView->horizontalHeader()->setStretchLastSection(true);
m_procssTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_procssTableView->setSelectionMode(QAbstractItemView::SingleSelection);
m_procssTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_procssTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
m_procssTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
m_procssTableView->setShowGrid(false); // disable the table grid.
m_procssTableView->verticalHeader()->setDefaultSectionSize(); // set row height.
m_procssTableView->horizontalHeader()->setHighlightSections(false);
m_procssTableView->setFrameShape(QFrame::NoFrame);
m_procssTableView->setItemDelegate(new NoFocusFrameDelegate());
表体部分,我们需要去掉网格线,这样看起来更加简洁。一格格的被网格线分开反而觉得被束缚了。其他的就是一些常见的设置选项,不必多说。另外要注意的是,我们总可以看到即便去掉了网格线,当我们鼠标点击某一行时,Qt仍然会在鼠标下的单元格周围画上一个选线框。这看起来就像白玉中的一点瑕疵,忍不住就要把它抠出去。网上对此的做法是,自定义一个条目委托(Item Delegate),并重写paint()方法:
void NoFocusFrameDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem itemOption(option);
// remove the focus state
if (itemOption.state & QStyle::State_HasFocus)
{
itemOption.state ^= QStyle::State_HasFocus;
}
QStyledItemDelegate::paint(painter, itemOption, index);
}
上面的代码很简单,仅仅是去掉了State_HasFocus这个状态,绘制工作仍然交由委托实现。
QTableView的上下文菜单,则需要重写contextMenuEvent()实现。上下文的菜单项背景色仍然可以用QSS进行控制。另外,QTableView还有一个单元格对齐的问题。QTableView的默认显示都是左对齐。这时,如果要想某一列都是居中对齐该怎么办那?答案是从QStandardItemModel类派生一个子类,重写虚函数data()。为什么不是从QTableView继承呢?因为我们使用了Qt中的MVC框架。View只管绘制Model中的数据,至于数据内容、格式设置什么的,都在Model里面设置。因此,使用MVC的时候我们大部分工作需要和Model打交道。
话又说回来。这个data()函数带两个参数,第一个参数可以控制那几列(行)怎么对齐。第二个参数是一个Role类型,用于区分不同的数据类型。因为Qt里面的数据分很多种:

我们得指明,当数据是用来显示在单元格中的时候,我们才设置对齐方式啊。不然的话就会乱套了。总之,QSS和2D绘图用好了,界面的效果也会慢慢炫起来。如果自己能够做出精美的界面素材,那么更加是锦上添花了。
遇到的问题
wchar_t的问题。由于底层使用了Windows API实现,免不了要和宽字符打交道。于是用上了QString类的两个静态方法:fromStdString(), fromStdWString()。用来将标准的string和wstring类型转换为QString类型。但是在链接的时候出错了:

fromStdWString无法解析的外部符号!解决方案如下:后面也有一些链接,至于为什么,我也一直没看懂。

截图及代码

view it on Github:click me!
参考
- QSS
- http://stackoverflow.com/questions/4521252/qt-msvc-and-zcwchar-t-i-want-to-blow-up-the-worl
- http://www.qtcn.org/bbs/read-htm-tid-30828.html
- http://blog.csdn.net/dbzhang800/article/details/6707152
用Qt写软件系列三:一个简单的系统工具之界面美化的更多相关文章
- 用Qt写软件系列三:一个简单的系统工具(上)
导言 继上篇<用Qt写软件系列二:QIECookieViewer>之后,有一段时间没有更新博客了.这次要写的是一个简单的系统工具,需求来自一个内部项目.功能其实很简单,就是查看当前当前系统 ...
- 用Qt写软件系列四:定制个性化系统托盘菜单
导读 一款流行的软件,往往会在功能渐趋完善的时候,通过改善交互界面来提高用户体验.毕竟,就算再牛逼的产品,躲藏在糟糕的用户界面之后总会让用户心生不满.界面设计需综合考虑审美学.心理学.设计学等多因素, ...
- 用Qt写软件系列五:一个安全防护软件的制作(3)
引言 上一篇中讲述了工具箱的添加.通过一个水平布局管理器,我们将一系列的工具按钮组合到了一起,完成了工具箱的编写.本文在前面的基础上实现窗体分割效果.堆栈式窗口以及Tab选项卡. 窗体分割 窗体分割是 ...
- 用Qt写软件系列五:一个安全防护软件的制作(1)
引言 又有许久没有更新了.Qt,我心爱的Qt,为了找工作不得不抛弃一段时间,业余时间来学一学了.本来计划要写一系列关于Qt组件美化的博文,但是写了几篇之后就没坚持下去了.技术上倒是问题不大,主要是时间 ...
- 用Qt写软件系列五:一个安全防护软件的制作(2)
引言 在上一篇中讲述了主窗体的创建和设计.主窗体的无边框效果.阴影效果.拖动事件处理.窗体美化等工作在前面的博客中早就涉及,因此上篇博文中并未花费过多笔墨.这一篇继续讲述工具箱(Tool Button ...
- 用Qt写软件系列二:QCookieViewer(浏览器Cookie查看器)
预备 继上篇<浏览器缓存查看器QCacheViewer>之后,本篇开始QCookieViewer的编写.Cookie技术作为网站收集用户隐私信息.分析用户偏好的一种手段,广泛应用于各大网站 ...
- 用Qt写软件系列一:QCacheViewer(浏览器缓存查看器)
介绍 Cache技术广泛应用于计算机行业的软硬件领域.该技术既是人们对新技术探讨的结果,也是对当前软硬件计算能力的一种妥协.在浏览器中使用cache技术,可以大幅度提高web页面的响应速度,降低数据传 ...
- 造轮子系列(三): 一个简单快速的html虚拟语法树(AST)解析器
前言 虚拟语法树(Abstract Syntax Tree, AST)是解释器/编译器进行语法分析的基础, 也是众多前端编译工具的基础工具, 比如webpack, postcss, less等. 对于 ...
- ROS与Matlab系列:一个简单的运动控制
ROS与Matlab系列:一个简单的运动控制 转自:http://blog.exbot.net/archives/2594 Matlab拥有强大的数据处理.可视化绘图能力以及众多成熟的算法函数,非常适 ...
随机推荐
- http协议读书笔记1-概述
1.http协议在网络中的位置: http协议位于TCP协议的上层,http试用tcp来传输其报文数据,tcp在ip的上层. 2.浏览器发起连接的过程 上述图的过程是: 浏览器从url中解析出服务区的 ...
- 使用ClipboardUtils兼容API LEVEL 11以下实现复杂粘贴
实现功能:复杂粘贴内容 问题描述:android.content.ClipboardManager在APILevel 11之后才可以使用,而我们目前还要兼容API Level 10(2.3.3) 解决 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】连载导读
前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例 ...
- 解决mac os x下 tomcat启动报 java.net.BindException: Permission denied <null>:80 错误
我在mac os x上启动tomcat的时候,报 java.net.BindException: Permission denied <null>:80,java.net.BindExce ...
- 大型网站系统架构演化之路【mark】
前言 一 个成熟的大型网站(如淘宝.天猫.腾讯等)的系统架构并不是一开始设计时就具备完整的高性能.高可用.高伸缩等特性的,它是随着用户量的增加,业务功能的 扩展逐渐演变完善的,在这个过程中,开发模式. ...
- mac或linux下xampp的mysql配置
在mac 下安装好xampp后,需要在终端命令行操作时,比如输入:mysql -u root -p,未正确配置前不会出现想要的输入密码提示,而是会提示: command not found 原来当你输 ...
- [转]android开发之字节顺序
原文在此 android上C++程序为小端字节顺序,和windows上一样. 而android上的JAVA程序则使用的是大端字节顺序. 用NDK和java SDK 做android程序时发现的问题,记 ...
- SQLServer 获得所有表结构(包括表名及字段)
then d.name else null end) 表名, a.colorder 字段序号,a.name 字段名, ( then '√'else '' end) 标识, (case when (SE ...
- 魔兽争霸3 replay 格式
******************************************************************************* * WarCraft III Repla ...
- CI 框架去掉url 中index.php的方法
1 修改 apache 的 httpd.conf 文件 #LoadModule rewrite_module modules/mod_rewrite.so 去掉前面的# 2 找到 你程序目录下的 .h ...