QT界面学习

想要先学习QT,必须掌握好C++中的类的相关概念。

QT学习(一)

还是和其他的语言学习一样,先来学习第一个程序,Hello QT!。

直接上代码:

[cpp]  view plain copy
  1. <span style="font-size:18px;">#include "helloqt.h"  
  2. #include <QtGui/QApplication>  
  3. #include <QLabel>  
  4.   
  5. int main(int argc, char *argv[])  
  6. {  
  7.     QApplication a(argc, argv);  
  8.     QLabel *label=new QLabel("Hello QT!");  
  9.     label->show();  
  10.     return a.exec();  
  11. }</span>  

很简单,记着要加载Label的库就行。

下面我们在这个上面稍微的做一点文章,理解QT中的布局管理器。

[cpp]  view plain copy
  1. <span style="font-size:18px;">#include "helloqt.h"  
  2. #include <QtGui/QApplication>  
  3. #include <QLabel>  
  4. #include <QHBoxLayout>  
  5.   
  6. int main(int argc, char *argv[])  
  7. {  
  8.     QApplication a(argc, argv);  
  9.         QWidget *window=new QWidget();  
  10.     window->setWindowTitle("你好!!");  
  11.     QHBoxLayout *layout=new QHBoxLayout();  
  12.     QLabel *label=new QLabel("Hello QT!");  
  13.     QLabel *label1=new QLabel("页面布局");  
  14.         layout->addWidget(label);  
  15.     layout->addWidget(label1);  
  16.     window->setLayout(layout);  
  17.     window->show();  
  18.     return a.exec();  
  19. }  
  20. </span>  

运行的结果汉字会出现乱码。

布局管理器有几种形式,QHBoxLayout:水平排列控件。QVBoxLayout:垂直排列控件。QGridLayout:按矩阵方式排列控件。

gridLayout->addWidget(colorPushBtn, 0, 0, 1, 1);
gridLayout->addWidget(errorPushBtn, 0, 1, 1, 1);
gridLayout->addWidget(filePushBtn, 0, 2, 1, 1);
gridLayout->addWidget(fontPushBtn, 1, 0, 1, 1);

矩阵布局器可以通过以上的形式进行布局。

一下是改变编码,显示汉字的代码。

[cpp]  view plain copy
  1. <span style="font-size:18px;">#include "helloqt.h"  
  2. #include <QtGui/QApplication>  
  3. #include <QLabel>  
  4. #include <QHBoxLayout>//头文件  
  5. #include <QtCore/QTextCodec>//头文件  
  6.   
  7. int main(int argc, char *argv[])  
  8. {  
  9.     QApplication a(argc, argv);  
  10.     QTextCodec::setCodecForTr(QTextCodec::codecForName("gb18030"));//系统默认unicode编码,必须进行编码的转换。  
  11.         QWidget *window=new QWidget();  
  12.     window->setWindowTitle(QObject::tr("你好!!"));  
  13.     QHBoxLayout *layout=new QHBoxLayout();  
  14.     QLabel *label=new QLabel(QObject::tr("Hello QT!"));  
  15.     QLabel *label1=new QLabel(QObject::tr("页面布局"));  
  16.         layout->addWidget(label);  
  17.     layout->addWidget(label1);  
  18.     window->setLayout(layout);  
  19.     window->show();  
  20.     return a.exec();  
  21. }  
  22. </span>  
以下提供QT的空间类型:

按钮控件:


单页容器控件


多页容器控件


反馈对话框


输入控件


文件和打印对话框


显示控件


向导对话框


项目查看控件


颜色和字体对话框


第一章就这些,下一章QT中的信号槽机制。


这一篇学习QT中最重要的也是最有特色的信号槽机制。

(因为我学习过MFC,所以我觉得QT的信号槽机制和MFC中的消息响应机制是一一对应的。不过是MFC用的是宏来实现,而QT用的是消息和槽。相对来说QT可能会更灵活一点,但是也更容易出错吐舌头

QT学习(二)

首先应该明白为什么要去用信号和槽,它是为了实现什么而存在的。

因为QT是GUI编程,GUI编程最关注的就是用户和应用程序的交互(通过将某种用户事件
(比如按下鼠标)与程序事件(比如退出程序)联系起来,使用户能够在图形界面中只使用鼠标来控制程序。)

其他的程序使用回调函数来处理这类问题的(其他不是所有)

(所谓的回调函数是指:按照一定的形式由开发人员定义并编写实现内
容。使用回调函数,实际上就是在调用某个函数(通常是API函数)时,将自
己的一个函数(也就是回调函数)的地址作为参数传递给那个函数。而那个
函数在需要的时候,也就是某种事情发生的时候,利用传递的函数地址调
用回调函数,这时开发人员可以利用这个机会在回调函数中处理消息或完
成一定的操作。然而会点函数十分的复杂不容易理解,所以就有了信号和槽)

信号和槽是为了实现对象与对象之间(跨越类的!!!!)通信而存在的,然而面向对象程序中就是将应用程序对象化,所以这种机制就能够实现用户事件和程序事件的交互。

Qt的窗口部件有很多预定义的槽,当一个特定事件发生的时候,一个信号被发射,对信号感兴趣的槽就会调用对应的响应函数。

在QObject类或者其一个子类(比如:QWidget类)继承的所有类中,都存在了信号和槽。当对象改变其状态的时候,信号被发送,对象不关心有没有其他对象接收到这个信号。槽是类的正常成员函数,可以将信号和槽通过connect()函数任意连接。当一个信号被发射,它所连接的槽会被立即执行,如同一个普通函数调用一样。

当信号被发出时,会调用与之相连接的槽。槽是普通的C++函数,可以用普通的方式来调用。它唯一特殊的地方在于可以与信号相连接。槽的参数不能有默认值。同样,信号的参数也不能有默认值。在槽的参数中尽量不使用自定义的数据类型,因为这样将会使通用性降低。

public slot: 任何信号都可以与之相连接。这在窗口部件编程中非常有用,用于创建一些对彼此一无所知的对象,只有                   通过信号和槽来交换信息。public slot就像是标准的铁路一样。
protected slot: 只有该类及其子类所派生的对象的信号才可以与之相接。这类槽的目的通常是为了类的完善,而不是类与外界的接口。
private slot: 只有该类自己的信号才可以与之相连接。

信号和槽是相当高效的。当然,它们与“实时”的回调函数相比,在增加了灵活性的同时也损失了一些速度,正所谓有利必有弊,但是这种速度的损失相当微不足道。因此,信号/槽机制具有的简便性和灵活性的特性,使用信号和槽是用户交互的必然选择
信号
当某个信号对其客户或所有者发生的内部状态发生改变,信号被一个对象发射。只有定义过这个信号的类及其派生类能够发射这个信号。当一个信号被发射时,与其相关联的槽将被立刻执行,就象一个正常的函数调用一样。信号-槽机制完全独立于任何GUI事件循环。只有当所有的槽返回以后发射函数(emit)才返回。如果存在多个槽与某个信号相联,那么,当这个信号被发射时,这些槽将会一个接一个地执行,但是它们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪个后执行。

/*******************
元对象系统不但提供对象间通信的信号/槽机制,而且在QObject中的元
对象代码能够实现以下特性:
className()函数该函数在运行的时候,以字符串返回类的名称,不需
要C++编译器中的本地运行类型信息(RTTI)的支持。
interits()函数该函数返回本对象在QObject继承树中一个特定类的实例。
tr()和trUtf8()函数该函数用于国际化中的字符串的翻译。
setProperty()和property()函数该函数用来通过名称动态设置,并且获
得对象属性。
metaObject()函数该函数返回这个类所关联的元对象。
******************/(不是很理解!!!)

需要注意的事项:

1 多重继承把QObject的子类作为第一个父类
class SomeClass : public QObject, public OtherClass
{
……
}
2 函数指针不能作为信号和槽的参数,在你考虑使用函数指针作为信号/槽的参数的时候,继承是一个不错的
替代方法
3 不能把友元声明friend放在信号或者槽的声明部分
4 把继承的成员函数升级为公有状态,这个C++特征对信号和槽并不适

5 因为元对象编辑器不能展开#define,所以,在信号和槽中类型宏作为
一个参数不能工作
6 构造函数不能用于信号部分和槽部分
7 属性的声明应该放在含有相应的读写函数的公有部分之前

槽和信号的具体使用方法:

在类定义的第一行加入Q_OBJECT宏。类中的其他项都需要一个分号
终止符,而Q_OBJECT宏却不需要,但如果喜欢,也可以加上分号(是因
为编译器在处理时根本不考虑分号)。我们可以按照以下方式定义一个类:
class SenderClass
{
Q_OBJECT
……
值得注意的是:在一个类中可以定义任意个槽和信号,但是Q_OBJECT只需要一次。向类定义中加入信号的原型。比如:如果信号将要发送一个字符串作为该信号的参数,那么原型大概以如下的方式去编写:
……
signals:
void newName(QString &name);

使用发送语句来调用所有监听这个信号的方法。这一步使用的语法和用来调用一个局部方法的语法是一样的,只不过这时用emit关键字开头:
QString name;
emit newName(name);

与信号一样,槽需要在类定义的上部加入Q_OBJECT宏:
class ReceiverClass
{
Q_OBJECT
……
向类定义中加入槽方法的原型。这个原型必须与它将要接收的信号一样(也就是说,具有同样的一套参数)。由于槽是方法,所以,在作为槽使用的同时,也可以被直接调用。槽的方法可以设置成为公有的属性。
public slots:
void nameChanged(QString &name);

注:编写代码创建将要发送信号的类的实例。只有这个实例的存在,才能把槽和信号联系在一起。把槽和信号连接起来。这个工作通常在构造函数中完成,但是如果这个对象构造得比较晚,那么连接工作也可以晚点做。调用connect()方法把你的槽加入到方法列表中,每当指定的信号发出的时候,这个方法就会被调用。


必须小心不要创建死循环。如果一个槽方法发送一个信号,此信号直接或者间接地执行了发送一个信号的方法,而这个信号又被最开始的槽所接收,那么信号将连续不断的调用槽,你编写的程序就会崩溃。比如:如果名为firstfun()的方法发送了一个A信号,A信号被second()槽所接收,而second()槽发送了信号B,最后,名为firstfun()的方法接收了信号B,这样就产生了一个死循环。这种循环将一直执行,直到该程序崩溃为止(或者用户进入长时间的等待)。还需要小心槽和信号方法在连接语句中的参数是否匹配。当程序运行的时候,直到试着去解决一个问题时,才可能得到出错的信息。为了避免这个问题的出现,必须确定每次增加内容的时候,都要进行测试,或者改变槽和信号部分。唯一的出错信息是当connect()方法找不到匹配对象的时候,输出一个写入控制台上的字符串。此后,程序就忽略了这个信号的存在。只有从命令行运行程序的时候,才能够看到控制台输出的信息。
connect(sender,SIGNAL(newName(QString &)),this,
SLOT(nameChanged(QString &)));
:前两个参数指定信号的来源,后两个参数指定目标槽。宏SIGNAL()和SLOT()都需要完整的方法原型,原型必须遵循,用来调用一种方法的参数必须和该种方法可以使用的参数保持一致。无论任何使用emit发送信号,就好象是你编写的程序直接调用每一个槽方法一样。也就是说,直到槽方法返回,你编写的程序才能继续执行。因此,通常应当保持在槽方法内部的处理过程中尽可能的简单,这样才不会因此中止信号的发送。发送信号的可能是用户接口过程,操作过程表现得比较慢或者缓慢。

到这里QT最重要的特性已经OK,剩下的就是试练,敬仰,从下一章开始!!


首先整理一下编码的方法。对于一个待解决的问题,首先应该将大问题分解成小问题,将小问题划分为小小问题。。。

然后再进行类的抽象,将划分成的问题和类进行对应。然后再对划分的小。。问题进行具体的处理分析,划分出步骤进行接口的处理。最后进行编码。

编码是首先在.h文件中进行类的定义,类中函数的定义,该类中用到的变量的定义;然后再在.c的文件中进行类的具体实现。注意还要将一些宏的定义单独定义在一个头文件中。在构造函数中进行变量的初始化操作,界面初始化函数的调用和信号槽的连接(这里指的是QT这样用)。在析构函数中将系统不会自动释放的对象和资源进行释放。最后进行具体函数的实现。

QT学习(三)

上一章学习了信号槽机制,这一章来进行具体的应用。

创建一个简单的窗口。

test.h文件:

[cpp]  view plain copy
  1. #ifndef TEST_H  
  2. #define TEST_H  
  3.   
  4. #include <QtGui/QMainWindow>  
  5. #include "ui_test.h"  
  6. #include <QLabel>  
  7. #include <QPushButton>  
  8. #include <QHBoxLayout>  
  9.   
  10. class test : public QMainWindow  
  11. {  
  12.     Q_OBJECT  
  13.   
  14. public:  
  15.     test(QWidget *parent = 0, Qt::WFlags flags = 0);  
  16.     ~test();  
  17. private:  
  18.     void initWidget();  
  19.     void show();  
  20.     QLabel *label;  
  21.     QPushButton *pushBtn;  
  22.     QWidget *window;  
  23.     QHBoxLayout *layout;  
  24. };  
  25.   
  26. #endif // TEST_H  

test.cpp文件:

[cpp]  view plain copy
  1. #include "test.h"  
  2.   
  3. test::test(QWidget *parent, Qt::WFlags flags)  
  4.     : QMainWindow(parent, flags)  
  5. {  
  6.     label=new QLabel("nihao");  
  7.     pushBtn=new QPushButton("ok");  
  8.     window=new QWidget();  
  9.     layout=new QHBoxLayout();  
  10.     connect(pushBtn,SIGNAL(click()),this,SLOT(close()));  
  11.     initWidget();  
  12. }  
  13.   
  14. test::~test()  
  15. {  
  16.       
  17. }  
  18. void test::initWidget()  
  19. {  
  20.     window->setWindowTitle("aaaaaaaa");  
  21.     layout->addWidget(label);  
  22.     layout->addWidget(pushBtn);  
  23.     window->setLayout(layout);  
  24. }  
  25. void test::show()  
  26. {  
  27.     window->show();  
  28. }  
main.cpp中:

[cpp]  view plain copy
  1. #include "test.h"  
  2. #include <QtGui/QApplication>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     QApplication a(argc, argv);  
  7.     test w;  
  8.     return a.exec();  
  9. }  
以上的代码实现类一个简单的窗口。

其实所有的窗口都是基于这样一个简单的窗口的拓展,可能会因为功能的需要去继承原有的控件类进行新的开发,进行不一样的布局管理,单不管怎样的去改变,都是基于这样一个消息槽机制和布局方法来实现的,只要勤加练习,敢想敢做就一定会在QT的领域中有一番作为的。

以后全都是一些例子和实现。


QT学习(四)----360界面制作(1)


参照网上的资料,模仿了一份360新特效的界面。

源代码在:http://download.csdn.net/detail/zhangyang1990828/5238013

360真实效果:(最好自己打开360看看!!)




先上效果图。(纯UI)


(现在没有任何的功能,之后再陆续添加,这样做比较好理解)

首先需要绘制Frame,就是看到的整个窗口。它是由两个圆角矩形组合起来的。

绘制代码如下:

[cpp]  view plain copy
  1. void DataBrain::paintEvent(QPaintEvent *)  
  2. {  
  3.     QBitmap bitmap(this->size());//创建一个位图来存储这个组合体  
  4.     QPainter painter(&bitmap);//创建一个类似于绘画板的对象,通过它来在bitmap上绘图  
  5.     painter.fillRect(bitmap.rect(),Qt::white);  
  6.     painter.setBrush(QColor(0,0,0));  
  7.         painter.drawRoundRect(QRect(0,2,this->width(),this->height()-2),5,5);  
  8.     painter.drawRoundRect(QRect(20,0,100,2),2,2);  
  9.     setMask(bitmap);//将画好的bitmap遮挡在屏幕前方  
  10. }  
这样就绘制好了整个窗口的Frame,之后需要做的就是在这个Frame上贴图制作模糊透明效果的按钮。

背景其实就是将两张图片分别存储到两个label中,让后通过setGeometry将label放到指定的位置。两张是因为之后要实现动态的效果,所以要两张。(往后看就明白了)

上代码:

[cpp]  view plain copy
  1. void DataBrain::createFrame()  
  2. {  
  3.     this->setWindowTitle(tr("DataBrain"));  
  4.     this->resize(QSize(WINDOW_WIDTH,WINDOW_HEIGHT));  
  5.     setWindowFlags(Qt::FramelessWindowHint);//设置窗口的样式  
  6.       
  7.     m_pLabelBkTop=new QLabel(this);   
  8.     m_pLabelBkTop->setPixmap(QPixmap(":/images/images/bg_top.png"));  
  9.     m_pLabelBkTop->setGeometry(QRect(0,2,this->width(),this->height()-2));//在目标区域显示label  
  10.   
  11.     m_pLabelBkBottom=new QLabel(this);  
  12.     m_pLabelBkBottom->setPixmap(QPixmap(":/images/images/bg_bottom.png"));  
  13.     m_pLabelBkBottom->setGeometry(QRect(0,2,this->width(),this->height()-2));  
  14.   
  15. }  
为了实现最后的拖动效果,我们需要将前景的四张拖动出现的图合并成一张。

窗口上的显示“360安全桌面”“木马防火墙”..的按钮的实现,我们将这种按钮抽象成一个类,方便以后的调用。
之后只要将这类按钮的对象布局到这个窗口上就实现了前面实现的效果了(纯UI)。

按钮类里的函数:(不贴全部代码了,需要的去http://download.csdn.net/detail/zhangyang1990828/5238013下载

[cpp]  view plain copy
  1. void DataBrain::createWidget()//四张图片合并一个pixmap  
  2. {  
  3.     QPixmap pixmap(QSize(this->width()*WINDOW_PAGE_COUNT,WINDOW_HEIGHT-2));  
  4.     QPainter painter(&pixmap);  
  5.     for(int i=0;i<WINDOW_PAGE_COUNT;i++)  
  6.     {  
  7.         painter.drawImage(QRect(WINDOW_WIDTH*i,0,WINDOW_WIDTH,WINDOW_HEIGHT-2),QImage(tr(":/images/images/desktop_%1.jpg").arg(i)));  
  8.     }  
  9.   
  10.     m_pLabelFg=new QLabel(this);  
  11.     m_pLabelFg->resize(pixmap.size());  
  12.     m_pLabelFg->setPixmap(pixmap);  
  13.     m_pLabelFg->move(WINDOW_START_X,WINDOW_START_Y);  
  14.   
  15.     QStringList nameList;  
  16.     nameList << tr("360安全桌面 ")  
  17.              << tr("木马防火墙  ")  
  18.              << tr("360保镖     ")  
  19.              << tr("电脑门诊    ");  
  20.     for(int i=0;i<WINDOW_BUTTON_COUNT;i++)  
  21.     {  
  22.         CLabel *label=new CLabel(this);  
  23.         label->resize(QSize(155,45));  
  24.         label->setPixmap(QPixmap(tr(";/images/images/btn_%1.png").arg(i)));  
  25.         label->setText(nameList.at(i));  
  26.         label->move(8+i*170,319);  
  27.         m_pLabelBtnArray[i]=label;  
  28.           
  29.     }  
  30.     m_pCloseBtn=new QToolButton(this); //关闭按钮  
  31.     m_pCloseBtn->setFocusPolicy(Qt::NoFocus);  
  32.     m_pCloseBtn->setStyleSheet("background:transparent;border:0px;");  
  33.     setButtonIcon(m_pCloseBtn, EButtonMouseDefault);  
  34.     m_pCloseBtn->move(QPoint(this->width()-52,1));  
  35.   
  36.     //raise widget  
  37.     m_pLabelBkTop->raise();  
  38.     m_pCloseBtn->raise();  
  39.   
  40.     for (int i = 0; i < WINDOW_BUTTON_COUNT; i++)  
  41.     {  
  42.         m_pLabelBtnArray[i]->raise();  
  43.     }  
  44. }  
  45. void DataBrain::createFilter()  
  46. {  
  47.   
  48. }  
  49. void DataBrain::setButtonIcon(QToolButton *btn, EButtonMouseState state)  
  50. {  
  51.     QPixmap pixmap(":/images/images/btn_close.png");//自定义关闭按钮  
  52.     int nWidth = pixmap.width()/4;  
  53.     int nHeight = pixmap.height();  
  54.     btn->setIcon(QIcon(pixmap.copy(QRect(state * nWidth, 0, nWidth, nHeight))));  
  55.     btn->setIconSize(QSize(nWidth, nHeight));  
  56. }  

这样就完成了360新特性界面的纯UI实现。

下一章实现窗口的拖拽等功能。


继续上一章的360新特性界面。源代码:http://download.csdn.net/detail/zhangyang1990828/5241242

上一章中实现了整个界面的纯UI设计,这次我们让它生动起来。

QT学习(五)

首先让“360安全桌面”“木马防火墙”..这四个按钮“动”起来。

让这个按钮有几种状态:

①鼠标进入时变成低亮高透明

②鼠标离开时恢复原状

③鼠标点击后变成高亮低透明

为了实现这几种状态,需要三个事件和两个变量:

enterEvent(QEvent* e);

leaveEvent(QEvent* e);

mousePressEvent(QMouseEvent* e);

m_mousePressFlag;

m_mouseEnterFlag;

通过这三个事件和两个变量,就可以实现按钮的几种状态了。(还需要通过信号槽来实现只有一个按钮被选中)

代码:

[cpp]  view plain copy
  1. connect(label, SIGNAL(signalLabelPress(CLabel*)), this, SLOT(slotLabelButtonPress(CLabel*)));  
[cpp]  view plain copy
  1. void DataBrain::slotLabelButtonPress(CLabel *label)  
  2. {  
  3.     for (int i = 0; i < WINDOW_BUTTON_COUNT; i++)  
  4.     {  
  5.         if (label != m_pLabelBtnArray[i])  
  6.         {  
  7.             m_pLabelBtnArray[i]->setMousePressFlag(false);  
  8.         }  
  9.     }  
  10. }  


[cpp]  view plain copy
  1. //按钮特效  
  2. void CLabel::setMouseEnterFlag(bool flag)  
  3. {  
  4.     m_mouseEnterFlag=flag;  
  5.     this->update();  
  6. }  
  7. void CLabel::setMousePressFlag(bool flag)  
  8. {  
  9.     m_mousePressFlag=flag;  
  10.     this->update();  
  11. }  
  12. bool CLabel::getMouseEnterFlag()  
  13. {  
  14.     return m_mouseEnterFlag;  
  15. }  
  16. bool CLabel::getMousePressFlag()  
  17. {  
  18.     return m_mousePressFlag;  
  19. }  
  20. void CLabel::enterEvent(QEvent *e)  
  21. {  
  22.     if(!getMousePressFlag())  
  23.     {  
  24.         setMouseEnterFlag(true);  
  25.     }  
  26.     this->setCursor(Qt::PointingHandCursor);//设置鼠标的形状  
  27. }  
  28. void CLabel::leaveEvent(QEvent *e)  
  29. {  
  30.     setMouseEnterFlag(false);  
  31. }  
  32. void CLabel::mousePressEvent(QMouseEvent *e)  
  33. {  
  34.     if(e->button()==Qt::LeftButton)  
  35.     {  
  36.         setMousePressFlag(true);  
  37.         emit signalLabelPress(this);  
  38.     }  
  39. }  
效果图:


ok!我们继续来实现右上角关闭按钮的动态的效果。

和上面的相同,关闭的按钮也是需要三个状态:

①鼠标进入时

②鼠标离开时

③鼠标点击后

不同的是,关闭按钮不是对按钮设置样式,而是对它进行更换图片。

这里值得一提的是,这里用的eventFilter(事件过滤器),对某个控件install一个eventFiler后,可以在eventFilter的事件中通过传递进来的两个参数(QObject*,QEvent*)来对事件进行控制。

十分简单,上代码:

[cpp]  view plain copy
  1. //关闭按钮的效果  
  2. void DataBrain::createEventFilter()  
  3. {  
  4.     m_pCloseBtn->installEventFilter(this);  
  5. }  
  6. bool DataBrain::eventFilter(QObject *target , QEvent *event)  
  7. {  
  8.     EButtonMouseState state=EButtonMouseNone;  
  9.   
  10.     if(target==m_pCloseBtn)  
  11.     {  
  12.         if(event->type()==QEvent::Enter)  
  13.         {  
  14.             state=EButtonMouseEnter;  
  15.         }  
  16.         else if(event->type()==QEvent::Leave)  
  17.         {  
  18.             state=EButtonMouseDefault;  
  19.         }  
  20.         else if(((QMouseEvent*)event)->button()==Qt::LeftButton)  
  21.         {  
  22.             if(event->type()==QEvent::MouseButtonPress)  
  23.             {  
  24.                 state=EButtonMousePress;  
  25.             }  
  26.             else if (event->type()==QEvent::MouseButtonRelease)  
  27.             {  
  28.                 state = EButtonMouseEnter;  
  29.             }  
  30.         }  
  31.         if(state!=EButtonMouseNone)  
  32.         {  
  33.             setButtonIcon((QToolButton*)target,state);  
  34.         }  
  35.     }  
  36.     return QWidget::eventFilter(target, event);  
  37. }  
  38. void DataBrain::setButtonIcon(QToolButton *btn, EButtonMouseState state)  
  39. {  
  40.     QPixmap pixmap(":/images/images/btn_close.png");  
  41.     int nWidth = pixmap.width()/4;  
  42.     int nHeight = pixmap.height();  
  43.     btn->setIcon(QIcon(pixmap.copy(QRect(state*nWidth, 0, nWidth, nHeight))));  
  44.     btn->setIconSize(QSize(nWidth, nHeight));  
  45. }  
[cpp]  view plain copy
  1. connect(m_pCloseBtn,SIGNAL(clicked()),qApp,SLOT(quit()));//给按钮加上关闭程序的功能  
效果图:


最后来实现窗口的推拽功能(点击串口上的一部分内可以拖动窗口)和主页面选择页的拖拽(页面选择以及按钮选择的变换)

这里是整个设计中最复杂的地方!

首先需要先确定要完成窗口拖拽和主页面选择需要抽象出几个方法,怎样划分这些方法

①鼠标被按下事件mousePressEvent,在这里需要去判断鼠标的左键和右键,左键的话按下的区域是在窗口拖拽区还是在主页选择区,这里需要注意的是,关闭按钮也在窗口拖拽区,需要在关闭按钮的的点击事件将窗口拖拽的flag设置成false。完成这个类需要m_mouseMoveWindowFlag(窗口是否能被拖拽的flag),m_mousePressFlag(主窗口能否被拖拽的flag)两个flag;右键的话判断当前的主页面是否能向左滚动,需要setLabelMove和getLabelMove进行设置和读取m_labelMoveFlag来对主页面能被向左移还是向右移进行flag。m_labelMoveFlag=flase能向右移不能向左移,m_labelMoveFlag=true能向左移不能向右移。(这里的左移和右移指的是整页的移动)

②鼠标移动的事件mouseMoveEvent,在这里需要进行判断m_mouseMoveWindowFlag和m_mousePressFlag,通过这两个标识去完成是进行窗口的移动还是主页面的拖拽。(这里需要注意的是,这里的移动是拖拽,需要进行不断的事件响应,才能出现拖拽的效果。)

③鼠标的按键释放事件mouseReleaseEvent,在这里需要对m_mouseMoveWindowFlag和m_mousePressFlag进行判断,看刚结束的鼠标按键按下和释放过程中做了什么,如果m_mousePressFlag=true,则干才进行了主页面的拖拽,需要通过鼠标按键按下时鼠标的坐标和释放时鼠标的坐标来进行判断鼠标拖拽的方向,再通过moveCurrentPage(整页移动的函数)来完成页面的滚动;如果m_mousePressFlag=false,则刚才进行了窗口移动事件,需要在这里重新将m_mouseMoveWindowFlag设置成false。

③整页移动的函数moveCurrentPage,通过起始点的坐标和终点的坐标来判断滚动的方向。如果没有鼠标的拖动,而是通过鼠标右键或者快捷键来实现的页面滚动,则直接进行页面滚动。

④拖拽主页面到新的页面时,需要用改变当前选择按钮的函数changeCurrentButton,需要在这里调用CButton的setMousePressFlag函数重新对选中按钮进行设定。

⑤slotChangeCurrentPage函数,需要对点击按钮的事件进行响应,点击按钮则主页面进行响应的滚动

⑥快捷键的响应keyPressEvent,这里对快捷键进行设置。

OK!明白了以上的就可以了,代码:

[cpp]  view plain copy
  1. //窗口拖拽  
  2. void DataBrain::mousePressEvent(QMouseEvent *e)  
  3. {  
  4.     if(e->button()==Qt::LeftButton)  
  5.     {  
  6.         m_mouseSrcPos=e->pos();  
  7.         if(m_mouseSrcPos.y()<=40)  
  8.         {  
  9.             m_mouseMoveWindowFlag=true;  
  10.         }  
  11.         else  
  12.         {  
  13.             m_currentFgXpos=m_pLabelFg->x();  
  14.             m_mousePressFlag=true;  
  15.         }  
  16.     }  
  17.     //右键左移  
  18.     else if(e->button()==Qt::RightButton)  
  19.     {  
  20.         if(getLabelMove())  
  21.         {  
  22.             if(m_currentFgIndex>0)  
  23.             {  
  24.                 m_currentFgIndex--;  
  25.                 moveCurrentPage(false);  
  26.             }  
  27.         }  
  28.     }  
  29. }  
  30. inline void DataBrain::setLabelMove(bool enable)  
  31. {  
  32.     m_labelMoveFlag = enable;  
  33. }  
  34.   
  35. inline bool DataBrain::getLabelMove()  
  36. {  
  37.     return m_labelMoveFlag;  
  38. }  
  39. void DataBrain::moveCurrentPage(bool direction)  
  40. {  
  41.     int currentXpos = 0;//当前label的x坐标  
  42.     int destXpos = 0;//目标x坐标  
  43.   
  44.     //改变当前页面对应的按钮  
  45.     changeCurrentButton();  
  46.   
  47.     //图片的几个分割点  
  48.     //0-680, 680-1360, 1360-2040, 2040-2720  
  49.     //真:向左移;  假:向右移  
  50.     if (direction)  
  51.     {  
  52.         //左移的几种可能性,对于x坐标  
  53.         //index=0, 将label移动到-680*0  
  54.         //index=1, 将label移动到-680*1  
  55.         //index=2, 将label移动到-680*2  
  56.         //index=3, 将label移动到-680*3  
  57.         setLabelMove(false);  
  58.         currentXpos = m_pLabelFg->x();  
  59.         destXpos = -WINDOW_WIDTH * m_currentFgIndex;  
  60.         while(currentXpos > destXpos)//平滑滚动效果  
  61.         {  
  62.             m_pLabelFg->move(currentXpos-WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  63.             currentXpos = m_pLabelFg->x();  
  64.             qApp->processEvents(QEventLoop::AllEvents);//依然保持监听事件  
  65.         }  
  66.         m_pLabelFg->move(destXpos, WINDOW_START_Y);//确保最后移到指定的位置  
  67.         setLabelMove(true);  
  68.     }  
  69.     else  
  70.     {  
  71.         //右移的几种可能性,对于x坐标,与左移一致  
  72.         //index=0, 将label移动到-680*0  
  73.         //index=1, 将label移动到-680*1  
  74.         //index=2, 将label移动到-680*2  
  75.         //index=3, 将label移动到-680*3  
  76.         setLabelMove(false);  
  77.         currentXpos = m_pLabelFg->x();  
  78.         destXpos = -WINDOW_WIDTH * m_currentFgIndex;  
  79.         while(currentXpos < destXpos)  
  80.         {  
  81.             m_pLabelFg->move(currentXpos+WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  82.             currentXpos = m_pLabelFg->x();  
  83.             qApp->processEvents(QEventLoop::AllEvents);  
  84.         }  
  85.         m_pLabelFg->move(destXpos, WINDOW_START_Y);  
  86.         setLabelMove(true);  
  87.     }  
  88. }  
  89. void DataBrain::changeCurrentButton()  
  90. {  
  91.     for (int i = 0; i < WINDOW_BUTTON_COUNT; i++)  
  92.     {  
  93.         if (i != m_currentFgIndex)  
  94.         {  
  95.             m_pLabelBtnArray[i]->setMousePressFlag(false);  
  96.         }  
  97.         else  
  98.         {  
  99.             m_pLabelBtnArray[i]->setMousePressFlag(true);  
  100.         }  
  101.     }  
  102. }  
  103. void DataBrain::slotChangeCurrentPage(CLabel *label)  
  104. {  
  105.     int index = 0;  
  106.   
  107.     for (int i = 0; i < WINDOW_PAGE_COUNT; i++)  
  108.     {  
  109.         if (label == m_pLabelBtnArray[i])  
  110.         {  
  111.             index = i;  
  112.             break;  
  113.         }  
  114.     }  
  115.   
  116.     //移动的几种可能性,对于x坐标  
  117.     //index=0, 将label移动到-680*0  
  118.     //index=1, 将label移动到-680*1  
  119.     //index=2, 将label移动到-680*2  
  120.     //index=3, 将label移动到-680*3  
  121.     //点击左边的按钮 右移  
  122.     if (index < m_currentFgIndex)  
  123.     {  
  124.         while(index != m_currentFgIndex)  
  125.         {  
  126.             m_currentFgIndex--;  
  127.             moveCurrentPage(false);  
  128.         }  
  129.     }  
  130.     else if (index > m_currentFgIndex) //点击右边的按钮 左移  
  131.     {  
  132.         while(index != m_currentFgIndex)  
  133.         {  
  134.             m_currentFgIndex++;  
  135.             moveCurrentPage(true);  
  136.         }  
  137.     }  
  138. }  
  139. void DataBrain::mouseMoveEvent(QMouseEvent *e)  
  140. {  
  141.     int x = 0;  
  142.   
  143.     if (m_mousePressFlag)  
  144.     {  
  145.         if (getLabelMove())  
  146.         {  
  147.             m_mouseDstPos = e->pos();  
  148.             x = m_mouseDstPos.x() - m_mouseSrcPos.x();  
  149.   
  150.             setLabelMove(false);  
  151.             m_pLabelFg->move(m_currentFgXpos + x, WINDOW_START_Y);  
  152.             setLabelMove(true);  
  153.         }  
  154.     }  
  155.     else if (m_mouseMoveWindowFlag)  
  156.     {  
  157.         m_mouseDstPos = e->pos();  
  158.         this->move(this->pos() + m_mouseDstPos - m_mouseSrcPos);  
  159.     }  
  160. }  
  161. void DataBrain::mouseReleaseEvent(QMouseEvent *e)  
  162. {  
  163.     int xpos = 0;  
  164.   
  165.     if (m_mousePressFlag)  
  166.     {  
  167.         if (getLabelMove())  
  168.         {  
  169.             m_mouseDstPos = e->pos();  
  170.   
  171.             xpos = m_mouseDstPos.x() - m_mouseSrcPos.x();  
  172.   
  173.             if (xpos > 0)//右移  
  174.             {  
  175.                 if (xpos >= WINDOW_ONEBUTTON_WIDTH)  
  176.                 {  
  177.                     if (m_currentFgIndex > 0)  
  178.                     {  
  179.                         m_currentFgIndex--;  
  180.                         moveCurrentPage(false); //右移  
  181.                     }  
  182.                     else  
  183.                     {  
  184.                         moveCurrentPage(true); //左移  
  185.                     }  
  186.                 }  
  187.                 else  
  188.                 {  
  189.                     moveCurrentPage(true); //左移  
  190.                 }  
  191.             }  
  192.             else //左移  
  193.             {  
  194.                 if (xpos <= -WINDOW_ONEBUTTON_WIDTH)  
  195.                 {  
  196.                     if (m_currentFgIndex < WINDOW_PAGE_COUNT-1)  
  197.                     {  
  198.                         m_currentFgIndex++;  
  199.                         moveCurrentPage(true); //左移  
  200.                     }  
  201.                     else  
  202.                     {  
  203.                         moveCurrentPage(false); //右移  
  204.                     }  
  205.                 }  
  206.                 else  
  207.                 {  
  208.                     moveCurrentPage(false); //右移  
  209.                 }  
  210.             }  
  211.   
  212.             m_mousePressFlag = false;  
  213.         }  
  214.     }  
  215.     else if (m_mouseMoveWindowFlag)  
  216.     {  
  217.         m_mouseMoveWindowFlag = false;  
  218.     }  
  219. }  
  220. //快捷键  
  221. void DataBrain::keyPressEvent(QKeyEvent *e)  
  222. {  
  223.     if (getLabelMove())  
  224.     {  
  225.         switch(e->key())  
  226.         {  
  227.         case Qt::Key_Left:  
  228.         case Qt::Key_Up:  
  229.             if (m_currentFgIndex > 0)  
  230.             {  
  231.                 m_currentFgIndex--;  
  232.                 moveCurrentPage(false); //右移  
  233.             }  
  234.             break;  
  235.   
  236.         case Qt::Key_Right:  
  237.         case Qt::Key_Down:  
  238.             if (m_currentFgIndex < WINDOW_PAGE_COUNT-1)  
  239.             {  
  240.                 m_currentFgIndex++;  
  241.                 moveCurrentPage(true); //左移  
  242.             }  
  243.             break;  
  244.   
  245.         default:  
  246.             break;  
  247.         }  
  248.     }  
  249. }  
代码有点长,需要全部代码的可以去 http://download.csdn.net/detail/zhangyang1990828/5241242下载源代码。效果图就不上了,和360的新特性差不多。

通过这样一个例子,我想对于整个Qt的机制有了一定的了解吧,以后有好例子还会与大家分享的。


QT学习(六)----360界面实现(修改)


上次的360界面经过测试发现了一个bug,就是在窗口滚动的过程中再次点击按钮的话,页面会一直滚动到底层页面。

经过debug发现,在一个页面滚动没有结束的过程中再次点击会在以下代码处无限循环:

[cpp]  view plain copy
  1. while(currentXpos < destXpos)  
  2. {  
  3.     m_pLabelFg->move(currentXpos+WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  4.     currentXpos = m_pLabelFg->x();  
  5.     qApp->processEvents(QEventLoop::AllEvents);//通过事件延时实现滚动效果  
  6. }  
  7.         m_pLabelFg->move(destXpos, WINDOW_START_Y);  
以上代码本是通过 qApp->processEvents(QEventLoop::AllEvents);进行延时实现滚动效果,但是这样做会在滚动的过程中继续监听其他事件,这样在滚动过程中再次点击会继续处理点击的事件,从而无限循环。

经过调试,已经更改掉了bug,以下是实现代码:

[cpp]  view plain copy
  1. time1->start(10);//左移timer  
  2. time2->start(10);//右移timer  
  3. void DataBrain::onTimerOut1()  
  4. {  
  5.     if(WINDOW_PAGE_MOVE >=(currentXpos-destXpos))  
  6.     {  
  7.         m_pLabelFg->move(destXpos, WINDOW_START_Y);//确保最后移到指定的位置  
  8.         time1->stop();  
  9.     }  
  10.     m_pLabelFg->move(currentXpos-WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  11.     currentXpos = m_pLabelFg->x();  
  12.     m_thislabel->move(thisMoveX+WINDOW_PAGE_MOVE/4,122);  
  13.     thisMoveX=m_thislabel->x();  
  14. }  
  15. void DataBrain::onTimerOut2()  
  16. {  
  17.     if( WINDOW_PAGE_MOVE >=(destXpos-currentXpos))  
  18.     {  
  19.         m_pLabelFg->move(destXpos, WINDOW_START_Y);  
  20.         time2->stop();  
  21.     }  
  22.     m_pLabelFg->move(currentXpos+WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  23.     currentXpos = m_pLabelFg->x();  
  24.     m_thislabel->move(thisMoveX-WINDOW_PAGE_MOVE/4,122);  
  25.     thisMoveX=m_thislabel->x();  
  26. }  
这样用timer事件来替代 qApp->processEvents(QEventLoop::AllEvents);的延时效果,这样就不会出现以上的bug了。

而且经测试,上一个版本在连续点击中间两个label时,会将每一次的位移都移动完才停下,就会造成点击结束还会不断的滚动。改过之后的代码也取消掉了这个bug,在timer的延时过程中再次触发事件,系统会将时间片分配给新的事件而结束掉原来的事件。

改后的源代码在http://download.csdn.net/detail/zhangyang1990828/5258719地址中。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值