首页 C++ GUI Programming with Qt 4_CH

C++ GUI Programming with Qt 4_CH

举报
开通vip

C++ GUI Programming with Qt 4_CH C++ GUI Programming with Qt 4 中文版 翻译:刘封天 e-mail:fengtian.WE@gmail.com Blog:http://www.linuxall.net Q Q :38407031 朋友们,下载翻译这本书经过一次由Open Office造成的数据丢失。以下的翻译由于时间仓促翻译不够精细,并且也没有整理,很粗糙,但是许多朋友强烈要求,决定先贡献出来头两章,供大家入门学习,这本书非常的不错,通过一些例子来讲解QT的种种机制,老少皆宜。 希望大家多多包涵,如...

C++ GUI Programming with Qt 4_CH
C++ GUI Programming with Qt 4 中文版 翻译:刘封天 e-mail:fengtian.WE@gmail.com Blog:http://www.linuxall.net Q Q :38407031 <注意> 朋友们,下载翻译这本书经过一次由Open Office造成的数据丢失。以下的翻译由于时间仓促翻译不够精细,并且也没有整理,很粗糙,但是许多朋友强烈要求,决定先贡献出来头两章,供大家入门学习,这本书非常的不错,通过一些例子来讲解QT的种种机制,老少皆宜。 希望大家多多包涵,如果发现了存在的一些问题,也请发送邮件告知在下。我的电子邮件:fengtian.WE@hotmail.com 之后我会做出一个专门的在线勘误 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf ,为打架提供一个“翻译错误”的提交和查看平台。 并且说明以下,以后我会根据重要性来单独的翻译本书的一些章节。会在http://www.linuxall.net上有所公告。 <注意> 译者注: 在继续之前,说一下,千万不要用OPEN OFFICE,因为当我已经把这本书翻译到一般的时候,由于OPEN OFFICE造成文件损坏,我只能现在从新开始写.OO 顶你的肺,我用MS OFFICE写. 前言和序章都是罗嗦的废话,我不翻译了,一些内容对LINUX,WINDOWS,MAC OS等做个各自简短的说明,很少的一点,我这里只翻译对LINUX的说明.(难道windows那些还用说吗?) 在学习之前你要清楚QT是做什么的. 如果你要在计算机上做软件,比如计算器,那就去用VB,那是不二的选择. 如果你要做一个网站,就去用PHP,ASP或者.NET. 但是如果你没有Windows,也没有前大硬件设备,但是你还想开发图形化话的程序(GUI),这时候,你就需要QT了. 目录 第一部分:QT基础 第一章.开始学习 第二章.创建对话框 第三章.创建主窗体 第四章.实现程序功能 第五章.创建自定义空间 第二部分:中级QT 第六章.布局管理器 第七章.事件处理 第八章.2D和3D绘图 第九章.拖拽和扔放操作 第十章. “项浏览”类 第十一章.容器类 第十二章.输入输出 第十三章.数据库 第十四章.网络 第十五章.XML 第十六章.在线帮助 第三部分:高级QT 第十七章:国际化 第十八章:多线程 第十九章:制作插件 第二十章:基于平台的特性 第二十一章:嵌入式程序 第一部分:QT基础 第一章.开始学习 第二章.创建对话框 第三章.创建主窗体 第四章.实现程序功能 第五章.创建自定义空间 第1章. 开始学习 Hello Qt //第一个QT程序 Making Connections //建立连接 Laying Out Widgets //布置控件 Using the Reference Documentation //使用参考文档 这一章指导我们结合C++基于QT做一些小的GUI程序,并且也介绍了QT的两个关键思想:”信号---信号槽”和布局,在第二章我们会更深入的介绍他们,在第三章哦们会编写更实际的程序. 如果你使用过C#或者JAVA,但是对C++还不是很了解,你可以阅读附录B的C++介绍. (编者注:附录B是一个C++的速成教程,对于有经验的程序员来说,是很好的学习教程.) 第一个QT程序 让我们通过一个非常简单的QT程序来开始学习.我们会一行一行的的解读他们,然后说明该如何编译运行这个程序. 1 #include 2 #include 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 QLabel *label = new QLabel("Hello Qt!"); 7 label->show(); 8 return app.exec(); 9 } 头两行我们包含了两个头文件, Qapplication类和Qlabel类.对于QT来说,与类同名的头文件中包含了该类的定义. 第5行创建了一个Qapplication对象用来管理整个程序的资源.QT支持命令行参数,可以通过Qapplication的构造函数来传递argc 和 argv参数给程序使用. 第6行创建了一个用来显示"Hello Qt!"的控件.在Unix方面的术语中,”挂件(widget)”是用户界面的是个可视元素,这个词来自于"window gadget",相当于我们在windows编程中常说的“控件”或“容器”,按钮,菜单,滚动条和框架都是控件.控件是可以在包含控件的,例如:一个程序窗体同城会再包含一个QmenuBar(菜单条),一些QtoolBars(工具条),一个QstatusBar(状态栏),和一些其他的控件.大多数的程序会使用QmainWindow或者Qdialog作为整个程序的主窗体.但是QT是非常灵活的,所以甚至可以使用任何控件作为一个窗体.在这个例子中, Qlabel就被作为这个程序的窗体. (编者注:随软关于widget叫法很多,不过在下文中我会同意称称之为”控件”,并且后面的翻译在遇到某个陌生控件的头几次我会给出中文的名字,不过为了让大家都习惯叫控件的英文名,所以再往后就都使用英文名了.) 第7行将这个label(标签)设置成可见的.控件生成后一般是隐藏的,所以我们要在顶只好后使其可见,这样可以避免控件的不正常显示. 第8行将程序的控制权交给QT,注意这时程序会进图事件循环(循环等待事件发生).这是一种等待模式,使程序等待用户的动作,比如点击鼠标或是按下键盘.用户的动作通常会产生一个事件(也叫"消息"),然后程序可以对期做出响应,一般是执行一个或多个函数,例如当用户点击控件,"mouse press" 和 a "mouse release"事件就会产生.要注意GUI程序完全不同于常规的命令行程序那样让你输入,然后处理结果,最后在非认为的情况下自动结束. 简单来说,我们不需要顾虑是否要在main()函数的最后删除Qlabel对象,因为对于这种小程序不用考虑内存遗漏问题,在程序结束后操作系统会自动回收内存. 图1.1. Hello on Linux 我们现在可以尝试在你的机器上运行这个程序了.首先你需要安装QT 4.1.1或者更新的版本,安装过程可以参考附录A.现在我们假设你已经正确的安装了QT4并且将它的bin目录加入了环境变量PATH中.你可以将程序代码拷贝到你的文件中起名叫hello.cpp,并且放在目录hello中,或者自己写进去也行.在本书的附带光盘中也有: /examples/chap01/hello/hello.cpp. (译者注:相信看这个的朋友都没有那个光盘吧,如果你有的话,希望能打包发给我一份,谢谢*@_@* 呵呵 ,所以以后关于这些废话我就不翻译了,而且后面说的编译部分也只翻译一次,所有的例子的编译方式都一样,并且这里建议大家为自己的每一个例子都建立一个文件夹来放置。) 进入命令行,进入hello目录,键入: qmake –project 这样就创建了一个与平台无关项目文件(hello.pro),然后键入: qmake hello.pro 会通过项目文件创建一个基于你当前平台的makefile 文件. ./hello 运行程序。 图1.2. A label with basic HTML formatting 在进行下一个例子之前,我们来做一个有趣的实验,替换下面的代码: 将:QLabel *label = new QLabel("Hello Qt!"); 替换为:QLabel *label = new QLabel("

Hello ""Qt!

"); 重新编译运行这个程序看看,你会发现,使用简单的HTML代码我们可以轻松美化QT程序的界面。 建立连接 第二个例子演示如何让程序响应用户的动作,这个程序由一个按钮组成,当用户点击它时会退出程序。程序代码和Hello相似,只不过这里会用QpushButton代替Qlabel作为主控件,并且我们将会八用户的动作和一些程序联系起来。 1 #include 2 #include 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 QPushButton *button = new QPushButton("Quit"); 7 QObject::connect(button, SIGNAL(clicked()), 8 &app, SLOT(quit())); 9 button->show(); 10 return app.exec(); 11 } QT控件会通过发射一个信号(signal)来表明用户做个一个操作或控件的状态发生了改变.例如:当用户点击按钮时, QpushButton就会发射clicked()信号.信号可以连接到一个函数上(一般叫做槽(slot)(译者注:国内一般称之为信号槽)),当信号发射后,与之相两的信号槽就会被执行.例如:我们将按钮的clicked()信号和Qapplication对象的quit()信号槽连接起来,我们会在下一章中更深入的讲解SIGNAL()和SLOT()机制. 图1.3. The Quit application 编译并运行程序,当点击Quit后,会退出程序. 布置控件 在这一部分,我们通过一个小例子来演示如何布置控件在窗体中的几何位置,还有如何使用信号和信号槽让两个控件同步工作.程序会询问用户年龄,用户可以通过翻动框或滑动条来操作. 这个程序包含三个控件: QspinBox()(翻动框), Qslider(滑动条), Qwidget.Qwidget作为程序的主窗体, QspinBox和Qslider都是放置在Qwidget内部的的.他们是Qwidget的子对象,也可以说Qwidget是他们的父对象, Qwidget没有父对象,因为它被用做于顶级窗体. Qwidget和它的子类都可以用带有QWidget *参数的构造函数来制定期父对象. 图1.4. The Age application 程序代码: 1 #include 2 #include 3 #include 4 #include 5 int main(int argc, char *argv[]) 6 { 7 QApplication app(argc, argv); 8 QWidget *window = new QWidget; 9 window->setWindowTitle("Enter Your Age"); 10 QSpinBox *spinBox = new QSpinBox; 11 QSlider *slider = new QSlider(Qt::Horizontal); 12 spinBox->setRange(0, 130); 13 slider->setRange(0, 130); 14 QObject::connect(spinBox, SIGNAL(valueChanged(int)), 15 slider, SLOT(setValue(int))); 16 QObject::connect(slider, SIGNAL(valueChanged(int)), 17 spinBox, SLOT(setValue(int))); 18 spinBox->setValue(35); 19 QHBoxLayout *layout = new QHBoxLayout; 20 layout->addWidget(spinBox); 21 layout->addWidget(slider); 22 window->setLayout(layout); 23 window->show(); 24 return app.exec(); 25 } 第8,9行将Qwidget设置为程序的主窗体,调用setWindowTitle()方法设置窗体的标题. 第10,11行创建QspinBox对象和Qslider对象, 我们假设用户的年龄最多不超过130岁,并在12,13行为这两个控件设置有效范围.我们可以通过QspinBox和Qslider构造函数来指定他们的父对象,但是没有必要这么做,因为我们之后就会发现布局系统会自动设定他们的父子关系. 下面的14到17行调用了两个QObject::connect()方法会使得QspinBox和Qslider的状态能够同步起来,显示同样的数值.当他们其中一个的值发生改变时,它就会发射valueChanged(int)信号,然后另一个控件的setValue(int)信号槽就会被调用,并且用新的数值作为参数. 18行将QspinBox的值设置为35,这样的话,QspinBox就会发射一个valueChanged(int)信号,并且参数int的值会是35.这个参数会被传递到Qslider的setValue(int)信号槽,将Qslider的值设置为35,因为Qslider的值发生了改变所以Qslider也会发射一个valueChanged(int)信号,发射到QspinBox的setValue(int)信号槽,但是要注意这里的setValue(int)不会再发射任何信号,因为QspinBox的值已经是35了.这样是为了预防无限循环. (译者注:以前在做事件处理的时候,我都是自己写判断来放置无限循环的,现在好像QT已经在信号里面做了判断处理,省事多了,不用再自己写了,HOHO 值得表扬.)图1.5说明了这种情况. 图1.5. Changing one widget's value changes both 19行到22行我们用“布局管理器”来布置两个控件.布局管理器是通过控件的功能来设置控件的尺寸和位置的对象.一共有三种布局管理器. QHBoxLayout 在水平方向上从左到右或者从左到右布置控件. QVBoxLayout 在竖直方向从上到下布置控件. QGridLayout 以网格形式布局控件. 22行调用setLayout()方法来为窗体设置布局管理器.在设置布局管理器的同时,控件QSpinBox 和 QSlider 会在后台重新设置各自的父对象,所以说我们没有必要在创建控件是通过构造函数指定它的父对象,完全可以交给布局管理器来完成. 图The Age application's widgets 虽然我们并没有对控件的尺寸和位置做任何的详细设定,登时他们仍然被适当的紧凑的并排放置在一起,这是因为布局QHBox-Layout根据控件的功能和需要自动的分配位置和尺寸.布局管理器使得我们不再手动去为控件的位置和尺寸去进行硬编码,也使得控件能够自动的根据窗体尺寸的改变而跟着适应. QT是非常易学易用高灵活性的GUI编程工具.通常QT程序在创建控件后都有必要设置他们的属性,将控件加入”布局”,自动的为控件非配尺寸和位置,用型号和信号槽机制处理用户对已经做了连接的控件所做的操作. 使用参考文档 QT参考文复函了所有的QT类和函数的说明信息,对于任何QT开发开说,QT参考文档都是优秀的工具.文档中虽然包含了许都的类和函数,但并未包括所有的,也并不是对所提及到的所有的类和函数全部做出深入的讲解.如果想更好的使用QT,你应该使自己能够尽可能熟练的使用QT参考文档. (译者注:相信”写程序,查文档”对于老程序员来说,不会陌生,要高速有效的写出好的程序,考的不是你的记忆,而是对帮助文档的灵活使用.) HTML格式的文档在QT目录下的doc/html目录下,可以使用任意浏览器打开.除了HTML文档之外也可以使用Qt Assistant(QT 帮助浏览器),Qt Assistant是一个比浏览器更加方便的文档查看工具,他的搜索,索引等功能可以更加快随便捷的查看你要查阅的文档.在LINUX下使用命令assistant来打开Qt Assistant(编者注:我安装完QT就在开始里有Qt Assistant 呵呵). 在”API区”首页的连接用来提供各种方式来查阅QT类。页面"All Classes"列出所有的QT类,"Main Classes"会列出最常用的QT类,你可以查看本章用到的类来作为练习。 图1.7Qt's documentation in Qt Assistant on Mac OS X 注意,通过继承得到的方法会被写在父类的文档中。例如:QpushButton的参考文档没有方法show()的说明,但是可以在他的父类Qwidget的文档中找到这个方法的说明。图1.8会说明类之间的继承关系。 图1.8. Inheritance tree for the Qt classes seen so far 在线文档在:http://doc.trolltech.com/ 上面的网站还有《QT季刊》的一些经典文章。 这一章介绍了信号―信号槽和布局的关键思想。也展现出用面向对象概念创建控件的特点。如果你浏览了QT文档的话,可能会发现,可以去直接学习一个新控件加以使用。而且QT对方法,参数,枚举等等的命名也都让人感觉很自然。 第二章:创建对话框 Subclassing Qdialog //Qdialog类 Signals and Slots in Depth //进入了解信号和信号槽 Rapid Dialog Design //快速 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 窗体 Shape-Changing Dialogs //变形对话框 Dynamic Dialogs //动态对话框 Built-in Widget and Dialog Classes //对话框类和内置控件 这一章指导你使用QT创建一个对话框.对话框的功能是为用户提供可选择的选项,并且允许用户设定一些选项.这个叫做对话框或者简单会话,它为用户和程序提供了交流的功能. 大多数GUI程序会由一个菜单和工具条,还有一些对话框窗体来组成主窗体.程序可能会创建一个对话框来接受用户的动作,比如计算器程序. 我们用纯手工编码的方式来演示如何编写第一个对话框.然后再看看如何使用Qt Designer的建立对话框.Qt Designer是一种可视化的QT设计器,可以比手工编写代码更加快速便捷的编写,修改和测试你设计的界面. Qdialog类 这个例子是完全用C++编写的“查找对话框”。我们在一个类中来实现,从而使他能够独立使用,并且使用自己的信号和信号槽。 图2.1. The Find dialog 程序代码分别写在finddialog.h和finddialog.cpp中,我们从finddialog.h来看: 1 #ifndef FINDDIALOG_H 2 #define FINDDIALOG_H 3 #include 4 class QCheckBox; 5 class QLabel; 6 class QLineEdit; 7 class QPushButton; 第1,2,27行保证头文件不被多次包含。 第三行包含了Qdialog类,他会作为我们对话框类的父类,Qdialog继承自Qwidget类。 4到7行在实现对话框之前提前声明一些类,提前声明会通知C++编译器虽然没有给出这些类的详细定义,但是这些类是存在的(通常会在头文件里有详细的定义)。关于这一点我们会在后面讲解更多。 接下来我们定义FindDialog类,并且继承于Qdialog: 8 class FindDialog : public QDialog 9 { 10 Q_OBJECT 11 public: 12 FindDialog(QWidget *parent = 0); 如果要定义信号或信号槽的话,在定义类的开始一定要加入宏指令Q_OBJECT。 FindDialog有典型的QT控件类的构造函数,参数parent指定此控件的父对象,默认是空指针,也就是没有父对象。 13 signals: 14 void findNext(const QString &str, Qt::CaseSensitivity cs); 15 void findPrevious(const QString &str, Qt::CaseSensitivity cs); 信号代码段声明了两个当用户点击查找按钮时会发射的信号。如果向后搜索选项被开启,对话框就会发射findPrevious()和findNext()信号。 关键字signals是一个宏指令,C++预处理器会在编译器得到它之前对期进行处理。Qt::CaseSensitivity是枚举类型,值分别可以为Qt::CaseSensitive或Qt::CaseInsensitive。 16 private slots: 17 void findClicked(); 18 void enableFindButton(const QString &text); 19 private: 20 QLabel *label; 21 QLineEdit *lineEdit; 22 QCheckBox *caseCheckBox; 23 QCheckBox *backwardCheckBox; 24 QPushButton *findButton; 25 QPushButton *closeButton; 26 }; 27 #endif 在私有代码段,我们声明两个信号槽,我们通过指针指向这些控件,可以在实现信号槽的时候更好的访问他们,关键字slots也是一个宏指令,会在处理后才交给C++编译器。 我们已经提前定义了这些私有变量的类。因为我们我能所有的指针都不会在头文件中做访问,所以编译器不需要类的详细定义。我们可以包含相关的头文件如QcheckBox,Qlabel等。但是提前定义可能会使编译更快一些。 下面看看finddialog.cpp,它包含的FindDialog的实现部分。 1 #include 2 #include "finddialog.h" 首先我们包含了QtGui,这个头文件包括了QT GUI类的定义。QT包括几个单独的模块,存放在他们各自的目录里。比较重要的模块有QtCore,QtGui, QtNetwork, QtOpenGL, QtSql, QtSvg, 和 QtXml。头文件QtGui中包含着QtCore和QtGui模块的所有类的定义。包含这个头文件后我们就不用再逐个包含所有类了。 在头文件filedialog.h中,没有包含Qdialog头文件,并且提前声明了QCheckBox, QLabel, QLineEdit,和QpushButton,但是包含这样一个大的头文件文件不是一个好主意,尤其是在比较大的程序中。 3 FindDialog::FindDialog(QWidget *parent) 4 : QDialog(parent) 5 { 6 label = new QLabel(tr("Find &what:")); 7 lineEdit = new QLineEdit; 8 label->setBuddy(lineEdit); 9 caseCheckBox = new QCheckBox(tr("Match &case")); 10 backwardCheckBox = new QCheckBox(tr("Search &backward")); 11 findButton = new QPushButton(tr("&Find")); 12 findButton->setDefault(true); 13 findButton->setEnabled(false); 14 closeButton = new QPushButton(tr("Close")); 第4行将参数parent传递给父类的构造函数。然后开始创建子对象,通过调用方法tr()可以使得字符创自动转换成其他的语言,它定义在Qobject类中,并且QObject所有的子类都包含宏指令Q_OBJECT。建议为所有用户可以看到的字符串都使用TR(),即使不打算将你的程序翻译成其他语言,也可以将此作为一个好习惯。QT程序的多语言化将在17章讲解。 我们可以在字符串中使用连接符“&”来指定快捷键。例如11行创建的查找按可以使用Alt+F来激活。连接符也可以用来控制焦点。在第6行我们创建了一个快捷键为Alt+W的标签,并且在第8行我们设定了这个标签的友好控件为文本框。所谓友好控件,就是说当标签的快捷键被按下时,该标签的友好控件就会得到焦点。所以当用户按下标签的快捷键Alt+W时,焦点就会交给标签的友好控件,也就是这个文本框。 在12行我们将查找按钮设置为窗体的默认按键,默认按键是指当回车按下后被激活的控件。13将禁用查找按钮,被禁用的按钮将显示诶灰色并且不对用户的操作做任何响应处理。 当文本框中的内容发生改变时私有信号槽enableFindButton(const QString &)会被调用。当用户点击查找按钮时私有信号槽findClicked()会被调用,当用户点击Close按钮后对话框窗体会关闭。信号槽close()继承自Qwidget,默认的功能是将控件隐藏(而不是删除),我们稍后再看enableFindButton()和findClicked()信号槽。 因为查找对话框继承自Qobject类,所以我们可以在调用connect()方式时忽略QObject::前缀。 21 QHBoxLayout *topLeftLayout = new QHBoxLayout; 22 topLeftLayout->addWidget(label); 23 topLeftLayout->addWidget(lineEdit); 24 QVBoxLayout *leftLayout = new QVBoxLayout; 25 leftLayout->addLayout(topLeftLayout); 26 leftLayout->addWidget(caseCheckBox); 27 leftLayout->addWidget(backwardCheckBox); 28 QVBoxLayout *rightLayout = new QVBoxLayout; 29 rightLayout->addWidget(findButton); 30 rightLayout->addWidget(closeButton); 31 rightLayout->addStretch(); 32 QHBoxLayout *mainLayout = new QHBoxLayout; 33 mainLayout->addLayout(leftLayout); 34 mainLayout->addLayout(rightLayout); 35 setLayout(mainLayout); 接下来用布局管理器来布置这些子对象,布局不仅可以包含控件,还可以包含其他布局。结合QHBoxLayouts, QVBoxLayouts,和QgridLayouts来嵌套使用,可以布置出非常复杂的程序界面。 在我们的查找对话框中,我们使用了两个QHBoxLayouts和两个QVBoxLayouts两个布局,如图2.2,在最外面的是主布局,通过第35行非配给FindDialog,用来负责整个窗体的布局,其他三个作为子布局。在图2.2右下角的像小弹簧一样的东西是一个spacer item(伸缩器),用来使右边两个按钮紧紧相连,更加紧凑,确保按钮靠都向上面对齐。 图2.2. The Find dialog's layouts 严格的说,布局管理器类不是控件,它继承自Qobject类的子类:Qlayout。在例图上控件使用实线绘制,布局使用虚线绘制,但在程序运行时,布局是隐藏的,不会被显示出来。当在25,33,34行将自布局加载到父布局中,自布局会自动的改变自己的父布局。然后到主布局被设置给对话框后,它会变成对话框的子对象,并且所有的控件也都会变成对话框的子对象。图2.3说明了最后的父子关系。 图2.3. The Find dialog's parentchild relationships 36 setWindowTitle(tr("Find")); 37 setFixedHeight(sizeHint().height()); 38 } 最后我们为对话框的窗体设置标题和准确的高度,使得所有控件都不会占据额外控件,这样会更加紧凑。QWidget::sizeHint()方法会返回一个控件的理想尺寸。 这样以来我们就完成了FindDialog类的构造函数。因为我么会使用new关键字来创建控件和布局,你可能会觉得应该编写一个叫做delete的方法来销毁他们。但其实没有必要这么做,因为当父对象销毁时QT会自动的删除子对象,并且子对象和布局都是FindDialog的子集。 现在我们来看对话框的信号槽: 39 void FindDialog::findClicked() 40 { 41 QString text = lineEdit->text(); 42 Qt::CaseSensitivity cs = 43 caseCheckBox->isChecked() ? Qt::CaseSensitive 44 : Qt::CaseInsensitive; 45 if (backwardCheckBox->isChecked()) { 46 emit findPrevious(text, cs); 47 } else { 48 emit findNext(text, cs); 49 } 50 } 51 void FindDialog::enableFindButton(const QString &text) 52 { 53 findButton->setEnabled(!text.isEmpty()); 54 } 当用户点击查找按钮时,信号槽findClicked()就会被调用,它会根据是否选中向前查找选项发射findPrevious()或者findNext()信号。QT的关键字emit会被C++预处理更换成便准的C++表达式。 当用户改变文本框中的内容时信号槽enableFindButton()会被调用。如果文本框中有内容的话就它就会将查找按钮变为可用的,如果没有任何内容的话就会禁用查找按钮。 现在对话框的两个信号槽已经完成了,我们来创建main.cpp文件测试对话框控件: 1 #include 2 #include "finddialog.h" 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 FindDialog *dialog = new FindDialog; 7 dialog->show(); 8 return app.exec(); 9 } 使用qmake编译程序,因为FindDialog类的定义中含有宏指令Q_OBJECT,所以qmake会通过特定规则运行moc工具来产生一个makefile。(moc:QT的元对象编译器。元对象系统会在下面进行讲解。) 为了让moc正常工作,我们必须在头文件中对类进行定义,将它和执行文件分隔开来。Moc会结合C++来处理头文件。 使用了宏指令Q_OBJECT的类都需要使用moc来处理。不用担心,qmake会自动的添加所需要的指令来生成makefile。但如果你忘使用qmake和moc来生成makefile,连接器会对一些报出一些问题,并且不实现。不过这个信息比较模糊,往往不能说明问题。GCC会产生一些这样的警告: finddialog.o: In function 'FindDialog::tr(char const*, char const*)': /usr/lib/qt/src/corelib/global/qglobal.h:1430: undefined reference to 'FindDialog::staticMetaObject' 如果经常出现这种情况,可以运行qmake更新makefile,然后重新编译程序。 现在运行程序,如果你的平台支持快捷键,测试一下Alt+W, Alt+C, Alt+B, 和 Alt+F是否能出发正确的动作。按下TAB键选择控件,默认情况下tab键得到控件焦点的顺序与控件被创建的顺序一致。可以使用QWidget::setTabOrder()方法来改变顺序。 合理的TAB顺序可以使得用户在不能或者不像使用鼠标的情况下,很好的完全使用键盘进行操作。完全的合理的键盘控制也可以提高软件操作人员的工作效率。 在第三章我们会结合查找对话框来做一个真实的程序。并且我们会连接findPrevious() 和 findNext()信号到一些信号槽。 深入了解信号和信号槽 “信号-信号槽”机制是QT程序的基本原理,它使得程序员可以绑定一些根本不相干的控件。我们已经将我们自己声明实现的信号和信号槽连接在一起了。现在我们来更深入的研究这一机制。 信号槽相当于C++成员函数,可以是虚函数,也可以被重载,可以是私有(private)的,共有(public)的或是受保护(protected)的,可以像普通的方法那样被调用,并且可有以任何类型的数据作为参数。信号槽也可以连接到信号,也就是每次都自动的调用信号使其发射。 connect()表达式如下: connect(sender, SIGNAL(signal), receiver, SLOT(slot)); 这里的sender和receiver是指向Qobjects类的指针,这里的信号和信号槽是没有参数名的函数。 迄今为止我们看到的例子中,我们总是将不同的信号连接不同的信号槽,但也要考虑其他的情况: 一个信号连接到多个信号槽: connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int))); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateStatusBarIndicator(int))); 当信号发射时,信号槽会被一个一个调用(没有固定的顺序), 多个信号连接到同一个信号槽: connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError())); connect(calculator, SIGNAL(divisionByZero()), this, SLOT(handleMathError())); 当任意一个信号发射时,这个信号槽都会被调用。 一个信号连接到另一个信号: connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(updateRecord(const QString &))); 当第一个信号发射后第二个信号也会随之发射,另外,信号和信号的连接无异于信号和信号槽的连接. 删除连接: disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError())); 这种做法比较少见。因为当对象被删除时,其中的连接也会被QT自动的删除. 要使信号能够连接到信号槽或则其他信号,他们必须拥有同样类型和同样顺序的参数: connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(processReply(int, const QString &))); 另外,如果信号的参数比与之相连的信号槽的参数多,可以完全忽视多出的参数. connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(checkErrorCode(int))); 当以debug模式编译程序后,在运行时如果发现参数的类型不匹配或者信号,信号槽不存在的话QT会发出警告. 至今为止,我们我们只是使用了控件的信号和信号槽.但是这一机制还可以使用于所有的Qobject子类: class Employee : public QObject { Q_OBJECT public: Employee() { mySalary = 0; } int salary() const { return mySalary; } public slots: void setSalary(int newSalary); signals: void salaryChanged(int newSalary); private: int mySalary; }; void Employee::setSalary(int newSalary) { if (newSalary != mySalary) { mySalary = newSalary; emit salaryChanged(mySalary); } } 注意信号槽setSalary()是如何实现的.如果newSalary != mySalary那么就发射salary-Changed()信号,以确保不会发生无限循环. (译者注:关于信号的无限循环,前面提到过,看来在自定义的槽中,需要自己判断,而QT自身的槽已经帮我们写好的判断来防止循环.) Qt's Meta-Object System(QT元对象系统) Qt的一个重要产物是它通过C++建立了独立的软件组件,不受其他的组件约束和影响。 这种机制叫做meta-object system(元对象系统),它提供两个关键的服务:"信号-信号槽"和"自我检查".如果你要实现信号或信号槽,那么一定会用到"自我检查"功能.它还使得程序员在运行时可以得到Qobject子类的一些信息,比如类名或对象的信号和信号槽的列表.这一机制也支持一些工具(如Qt Designer),和一些文字的翻译(如国际化),并且为QT的程序脚本奠定了基础. 因为C++不为meta-object system提供所需的元信息,所以QT会通过一个独立工具moc来解决这一问题,它可以通过C++的函数来分析定义了Q_OBJECT的类,然后生成一些有用的信息.因为moc是完全通过C++来实现的,所以meta-object system可以基于任何C++编译器来使用. 关于这一机制所做的工作: 宏命令Q_OBJECT声明了一些自我检查函数如metaObject(), TR(), qt_metacall()等等,所以在所有的Qobject子类中用到这些函数的都要包含宏命令Q_OBJECT. Moc工具为所有的声明了Q_OBJECT的函数和所有的信号做出处理. 对象的成员函数(比如connect() 或 disconnect())也会通过见我检查函数来实现. qmake, moc,和 Qobject会自动的来操作这些,所以你不需要太多的考虑这些.但如果你对此感兴趣,你可以通过文档来查看QmetaObject类,或者产看moc产生的源代码来了解它是如何工作的. 快速设计对话框 可以使用手工编写代码来设计QT程序,并且许多程序员都完全使用C++来开发完成的程序,但是还有很多程序员更喜欢使用可视化手段来设计窗体,因为它比手动编写代码更快捷更轻松,并且能够更快捷方便的测试或修改窗体的设计。 Qt Designer使得程序员可以进行可视化的设计。Qt Designer可以进行整个程序的开发,也可以用来制作一个程序中单独的一些窗体。Qt Designer会为所设计出的窗体生成C++代码,所以Qt Designer系列工具可以在任何的C++编译器环境中使用。 在这一部分,我们使用Qt Designer和代码来制作一个Go-to-Cell对话框(如图2.4),不管是使用Qt Designer还是手写代码,都会以一下步骤制作这个程序: 创建并初始化子对象; 将控件放如布局(用布局管理器布置控件); 设置TAB键序; 建立信号-信号槽连接; 实现对话框的自定义信号槽; 图2.4. The Go-to-Cell dialog 启动Qt Designer,(译者注:我安装万在开始里就有。对于其他系统不做详细说明。)启动后会出现模版列表,选择“Widget”模版,然后点OK。(也许Dialog with Buttons Bottom模版看上去更好,但是在我们这个例子中需要需要一个OK按钮和和一个Cancel按钮来完成。)现在应该有了一个叫做Untitled的窗口。默认情况下Qt Designer的界面是由几个单独的顶级窗体组成的,但如果你更喜欢MDI风格的界面,也就是一个顶级窗体中包含几个子窗体,可以点击Edit|User Interface Mode|Docked Window来更改界面。 第一步是将子窗体放置在窗体上。创建一个label(标签),一个line editor(单行文本框),一个horizontal spacer(水平伸缩器),和两个push buttons(按钮)。你可以从Qt Designer的控件箱中拖出控件并将其放在窗体上,spacer控件不会在最终的窗体中显示,只会在Qt Designer中显示出一个蓝色的弹簧。 图2.5. Qt Designer in docked window mode on Windows 现在从窗体下面拖拽将其更短点。结果应该和图2.6相似。不需要话太多的时间在布置窗体上的控件,稍后,Qt的布局管理器将会使他们的位置更合适。 图2.6. The form with some widgets 通过QT的property editor(属性编辑器)来设置所有控件的属性: 1. 点击选中text label,将属性objectName(对象名)设置为“label”并且设置其属性text为“&Cell Location:”。 2. 点击选中line editor(单行文本框),objectName设为“lineEdit”。 3. 点击选中第一个按钮。ObjectName设为“okButton”,enabled设为“false”,text设为“OK”,default设为“true”。 4. 点击选中第二个按钮,objectName设为“cancelButton”,text设置为“Cancel”。 5. 通过点击窗体的背景选中窗体,设置objectName为“GoToCellDialog”,windowTitle为“Go to Cell”。 除了text label显示为“&Cell Location”外,其他所有控件都看上去不错了。点击Edit|Edit Buddies进入特殊的友好控件编辑模式。点在label上并且将红色箭头拖拽至line editor再放开。这样label就应该显示为“Cell Location”了,并且line editor也成为了它的友好控件。现在点击Edit|Edit Widgets退出友好控件编辑模式。 图2.7. The form with properties set 下一步来布置窗体上的控件: 1. 选中Cell Location label,按下Shift点选line editor来将他们两个一起选中,选择Form|Lay Out Horizontally。 2. 选中spacer,按Shift追加按钮OK和Cancel。选择Form|Lay Out Horizontally。 3. 点击窗体背景,并且也取消的对其他控件的选择,选择 Form|Lay Out Vertically。 4. 选择Form|Adjust Size来调节窗体尺寸。 红线会在布局创建后出现,但是在运行时是不会显示出来的。 图2.8. The form with the layouts 选择Edit|Edit Tab Order,所有可以得到焦点的控件边上会出现一个蓝底数字。点击控件将设置他们的得到焦点的顺序,再点击Edit|Edit Widgets退出TAB排序模式。 图2.9. Setting the form's tab order 选择Form|Preview menu来预览对话框,可以反复按下TAB键测试焦点顺序。点击标题栏上的关闭按钮来关闭窗口。 将对话框保存为gotocelldialog.ui放在目录gotocell,并在此目录下创建main.cpp文件以文本方式编辑: #include #include #include "ui_gotocelldialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Ui::GoToCellDialog ui; QDialog *dialog = new QDialog; ui.setupUi(dialog); dialog->show(); return app.exec(); } 用qmake创建a .pro和makefile(qmake -project; qmake goto-cell.pro), Now run qmake to create a .pro file and a makefile (qmake -project; qmake goto-cell.pro). qmake可以巧妙的检测用户界面文件goto-celldialog.ui,然后产生可以调用uic的makefile规则,uic工具可以将gotocelldialog.ui转成称C++程序保存在ui_gotocelldialog.h中。 产生的ui_gotocelldialog.h文件包含了Ui::GoToCellDialog类的定义,相当于gotocelldialog.ui文件,类定义了成员变量来保存窗体的控件和布局,setupUi()方法用来产生窗体,所产生的类如下: class Ui::GoToCellDialog { public: QLabel *label; QLineEdit *lineEdit; QSpacerItem *spacerItem; QPushButton *okButton; QPushBu
本文档为【C++ GUI Programming with Qt 4_CH】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_741994
暂无简介~
格式:doc
大小:2MB
软件:Word
页数:42
分类:互联网
上传时间:2010-08-29
浏览量:13