引言

在上一篇中讲述了主窗体的创建和设计。主窗体的无边框效果、阴影效果、拖动事件处理、窗体美化等工作在前面的博客中早就涉及,因此上篇博文中并未花费过多笔墨。这一篇继续讲述工具箱(Tool Button)的实现。另外,在实现的过程中还做了另外一个贴心的小功能:可伸缩的侧边栏。不过后来发现应用起来后效果不佳,于是就没在主窗体中加入这个功能了,单独做了一个demo作为示范。

工具箱的实现

工具箱是将若干的工具按钮组织在一起,为用户提供简便导航功能的一个组件。在Qt中实现这个功能不难,Qt库本身就提供了QToolButton和QToolBox两个类用于类似功能。在这里我们从QToolButton类派生一个子类自定义按钮动作。QToolButton类本身只提供了一些基本功能。因此我们需要实现一些事件处理器来自定义工具按钮的动作和外观。

看码说话:

CustomToolButton::CustomToolButton(const QString& path, QWidget *parent)
: QToolButton(parent), m_filePath(path)
{
// Get the widget's palette, we do have to change the color of the tool button.
QPalette text_palette = palette();
text_palette.setColor(QPalette::ButtonText, QColor(230, 230, 230));
setPalette(text_palette);
// set the style of QToolButton.
setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
// set the font style of tool buttons
// since the return value has the type of const, we have to remove the
// constness if we want to modify.
QFont text_font = const_cast<QFont&>(font());
text_font.setBold(true);
setFont(text_font);
// set the fixed size for tool buttons.
QPixmap background(m_filePath);
setIcon(background);
setIconSize(background.size());
setFixedSize(background.width()+25, background.height()+25); setAutoRaise(true);
m_mousePressed = false;
m_mouseHover = false;
}
/*
* Arguments topColor, centerColor, bottomColor are alpha values for QColor.
*/
void CustomToolButton::doPaintStuff(int topColor, int centerColor, int bottomColor)
{
QPainter painter(this);
QPen p(Qt::NoBrush, 1);
painter.setPen(p);
// create linear gradient brush to draw the widget
QLinearGradient linear(rect().topLeft(), rect().bottomLeft());
linear.setColorAt(0, QColor(230, 230, 230, topColor));
linear.setColorAt(0.5, QColor(230, 230, 230, centerColor));
linear.setColorAt(1, QColor(230, 230, 230, bottomColor)); // paint the widget.
painter.setBrush(linear);
painter.drawRect(rect());
} void CustomToolButton::setButtonPressed(bool isPressed)
{
m_mousePressed = isPressed;
update();
} void CustomToolButton::enterEvent(QEvent *)
{
m_mouseHover = true;
update();
} void CustomToolButton::leaveEvent(QEvent *)
{
m_mouseHover = false;
update();
} void CustomToolButton::paintEvent(QPaintEvent *event)
{
if (m_mouseHover)
{
doPaintStuff(0, 100, 150);
}
else
{
if (m_mousePressed)
{
doPaintStuff(0, 100, 150);
}
}
QToolButton::paintEvent(event);
} void CustomToolButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
emit clicked();
}
}

   在该子类中我们重写了enterEvent(),leaveEvent(), paintEvent(), mousePressEvent()这几个事件处理函数。分别对应鼠标进入、离开、点击按钮区域事件,paintEvent()则用于绘制按钮的外观。此外,还是用了几个状态变量,用于记录鼠标当前的移动状态。利用这些状态,我们就能顺利实现不同状态的外观绘制。值得注意的是doPaintStuff()这个函数。这个函数实际做的工作是给工具按钮添加垂直的渐变效果。使用了QLinearGradient这个类,可以实现线性的渐变效果,这在很多界面元素设计中都非常有用。

在主函数中怎么调用这个自定义的按钮类呢?

MainWin::MainWin(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this); setWindowTitle("Tool Button");
QStringList string_list;
string_list<<":/toolWidget/tiJian"<<":/toolWidget/muMa"<<":/toolWidget/repair"<<":/toolWidget/qingLi"
<<":/toolWidget/jiaSu"<<":/toolWidget/expert"<<":/toolWidget/menZhen"<<":/toolWidget/gongNeng"; QVBoxLayout *layout = new QVBoxLayout(this);
QHBoxLayout *button_layout = new QHBoxLayout(this); QSignalMapper *signal_mapper = new QSignalMapper(this);
for(int i=0; i< string_list.size(); i++)
{
CustomToolButton *tool_button = new CustomToolButton(string_list.at(i));
tool_button->setText("Test");
button_list.append(tool_button);
connect(tool_button, SIGNAL(clicked()), signal_mapper, SLOT(map()));
signal_mapper->setMapping(tool_button, QString::number(i, 10)); button_layout->addWidget(tool_button, 0, Qt::AlignBottom);
}
layout->addLayout(button_layout);
layout->addStretch();
setLayout(layout); }

   从代码中看,我们用了一个循环生成了若干个自定义按钮,然后全部放到水平布局管理器中进行管理。这个很容易理解,重点内容是QSignalMapper类的应用。QSignalMapper类是一个工具类,它主要的功能是将一组无参数信号集中管理,将信号用整型值或字符串值表示,然后再以一种统一的形式发送出去。其好处是,当有很多的信号需要统一管理的时候非常方便,不用手动调用connect()为信号绑定槽函数,因此代码结构也更为简练。在上面的代码中,我们将按钮点击信号转换为数值形式表示。这样也是很自然的做法,一方面形式简单,另一方面水平排列的工具按钮按序编号符合人类习惯。


可伸缩的侧边栏

还是看看什么叫做可伸缩的侧边栏,这样的功能在QQ的聊天窗口就可以看见:

侧边栏的收缩可以在需要的时候隐藏部分组件,从而为其他组件提供更为广阔的视角。如上图中的侧边栏收缩为文本框组件提供更多的空间,整个界面上看起来也更为清爽。稍微一剖析:这个边栏要能点击,点击之后要切换图标,响应的组件要隐藏。如此一分析,代码可如下编写:

TestSideBar::TestSideBar(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
flag = false;
mainLayout = new QHBoxLayout(this); mainSplitter = new QSplitter(Qt::Horizontal, this);
mainSplitter->setFrameStyle(QFrame::NoFrame);
mainSplitter->setHandleWidth(1);
mainSplitter->setChildrenCollapsible(false); zoomButton = new QPushButton(this);
zoomButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
zoomButton->setIcon(QIcon(":/right"));
zoomButton->setFixedWidth(6);
zoomButton->setFocusPolicy(Qt::NoFocus);
zoomButton->setStyleSheet("background: #E8E8E8; border: none; padding: 0px;"); leftWidget = new QWidget(this);
leftWidget->setStyleSheet("background: yellow;");
rightWidget = new QWidget(this);
rightWidget->setStyleSheet("background: blue;"); mainSplitter->addWidget(leftWidget);
mainSplitter->addWidget(rightWidget); mainLayout->addWidget(zoomButton);
mainLayout->addWidget(mainSplitter);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0); QWidget* w = new QWidget(this);
w->setLayout(mainLayout);
setCentralWidget(w); connect(zoomButton, SIGNAL(clicked()), this, SLOT(onZoomClicked()));
} void TestSideBar::onZoomClicked()
{
if (flag) // 根据当前的展开状态,进行切换
{
flag = false;
zoomButton->setIcon(QIcon(":/left"));
leftWidget->setVisible(true);
}
else
{
flag = true;
zoomButton->setIcon(QIcon(":/right"));
leftWidget->setVisible(false);
}
}

  

可以发现这里的侧边栏果然一直固定在最左侧,要达到QQ聊天界面那要的效果呢,只需要改几行代码就好了:

TestSideBar::TestSideBar(QWidget *parent)
: QMainWindow(parent)
{
// 其他保持不变,省略…… mainSplitter->addWidget(leftWidget);
mainSplitter->addWidget(zoomButton);
mainSplitter->addWidget(rightWidget); mainLayout->addWidget(mainSplitter, 1);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0); // 其他保持不变,省略……
}

  

小结

这一篇主要讲了上篇遗留的一个功能,工具按钮组的开发。另外,实现了另外一个功能:侧边栏的伸缩。下一篇继续树形控件(tree widget)、堆栈式窗口布局(stacked layout)的讲解。

用Qt写软件系列五:一个安全防护软件的制作(2)的更多相关文章

  1. 用Qt写软件系列五:一个安全防护软件的制作(1)

    引言 又有许久没有更新了.Qt,我心爱的Qt,为了找工作不得不抛弃一段时间,业余时间来学一学了.本来计划要写一系列关于Qt组件美化的博文,但是写了几篇之后就没坚持下去了.技术上倒是问题不大,主要是时间 ...

  2. 用Qt写软件系列五:一个安全防护软件的制作(3)

    引言 上一篇中讲述了工具箱的添加.通过一个水平布局管理器,我们将一系列的工具按钮组合到了一起,完成了工具箱的编写.本文在前面的基础上实现窗体分割效果.堆栈式窗口以及Tab选项卡. 窗体分割 窗体分割是 ...

  3. 系列五AnkhSvn

    原文:系列五AnkhSvn AnkhSvn介绍 AnkhSVN是一款在VS中管理Subversion的插件,您可以在VS中轻松的提交.更新.添加文件,而不用在命令行或资源管理器中提交.而且该插件属于开 ...

  4. 用Qt写软件系列三:一个简单的系统工具(上)

    导言 继上篇<用Qt写软件系列二:QIECookieViewer>之后,有一段时间没有更新博客了.这次要写的是一个简单的系统工具,需求来自一个内部项目.功能其实很简单,就是查看当前当前系统 ...

  5. 用Qt写软件系列一:QCacheViewer(浏览器缓存查看器)

    介绍 Cache技术广泛应用于计算机行业的软硬件领域.该技术既是人们对新技术探讨的结果,也是对当前软硬件计算能力的一种妥协.在浏览器中使用cache技术,可以大幅度提高web页面的响应速度,降低数据传 ...

  6. tensorflow笔记(五)之MNIST手写识别系列二

    tensorflow笔记(五)之MNIST手写识别系列二 版权声明:本文为博主原创文章,转载请指明转载地址 http://www.cnblogs.com/fydeblog/p/7455233.html ...

  7. Qt写的截图软件包含源代码和可执行程序

    http://blog.yundiantech.com/?log=blog&id=14 Qt写的截图软件包含源代码和可执行程序 http://download.csdn.net/downloa ...

  8. <p>在我们的实际软件项目中,管理团队事实上比写代码或者实现一个客户的需求更为的有挑战性。由于编程实际上是和机器打交道,而和机器打交道,仅仅要你符合机器预定的逻辑,</p>

    在我们的实际软件项目中,管理团队事实上比写代码或者实现一个客户的需求更为的有挑战性. 由于编程实际上是和机器打交道.而和机器打交道,仅仅要你符合机器预定的逻辑, 一步步迈向解决这个问题的道路上一点都不 ...

  9. SAP ECC6安装系列五:安装后 License 的处理

    原作者博客 http://www.cnblogs.com/Michael_z/ ======================================== 我发现我确实比较懒,先和各位说声抱歉了 ...

随机推荐

  1. Mvc利用淘宝Kissy uploader实现图片批量上传附带瀑布流的照片墙

    前言 KISSY 是由阿里集团前端工程师们发起创建的一个开源 JS 框架.它具备模块化.高扩展性.组件齐全,接口一致.自主开发.适合多种应用场景等特性.本人在一次项目中层使用这个uploader组件. ...

  2. 如何自行处理写好的eclipse插件安装不生效

    本帖最后由 anrainie 于 2013-7-23 11:31 编辑 对于eclipse插件开发的新手,经常会遇到插件写好了,拷贝到plugins或dropins文件下,但是没有生效.上网各种问,也 ...

  3. 团队项目——站立会议 DAY11

    团队项目--站立会议 DAY11        团队成员介绍(5人):张靖颜.何玥.钟灵毓秀.赵莹.王梓萱        今日(2016/5/20),站立会议已进行了两周时间,将这一周所遇到的问题和心 ...

  4. 【情人节来一发】网站添加QQ客服功能

    今年的元宵节遇到情人节,挺不自量力的,呵呵,开篇给各位讲个段子,早上一美女同学在空间发说说道:“开工大吉 起床啦,卖元宵,卖玫瑰,卖避孕套啦-有木有一起去发财的小伙伴?Let’s go…”,对于此种长 ...

  5. [nRF51822] 5、 霸屏了——详解nRF51 SDK中的GPIOTE(从GPIO电平变化到产生中断事件的流程详解)

    :由于在大多数情况下GPIO的状态变化都会触发应用程序执行一些动作.为了方便nRF51官方把该流程封装成了GPIOTE,全称:The GPIO Tasks and Events (GPIOTE) . ...

  6. IOS UIView 03- 自定义 Collection View 布局

    注:本人是翻译过来,并且加上本人的一点见解. 前言 UICollectionView 在 iOS6 中第一次被引入,也是 UIKit 视图类中的一颗新星.它和 UITableView 共享一套 API ...

  7. underscore源码阅读记录(二)

    引自underscore.js context参数用法 _.each(list, iteratee, [context]); context为上下文,如果传递了context参数,则把iterator ...

  8. Atiit 如何手写词法解析器

    Atiit 如何手写词法解析器 1.1. 通过编程直接从正则->nfa->dfa->表驱动词法解析一条龙自动生成.那是用程序自动生成是需要这样的,自己手写完全不必要这么复杂1 1.2 ...

  9. 每天一个linux命令(11):nl命令

    nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...

  10. Microsoft Visual Studio正忙解决办法

    问题描述 前段时间用vs2015进行开发.出现如下问题,关闭vs进程重启vs还是无法解决. 如何解决 进入本地项目.vs文件夹 这个文件夹下有个.suo文件,删除该文件,用任务管理器杀掉vs的进程,重 ...