QT学习之路全.docx
《QT学习之路全.docx》由会员分享,可在线阅读,更多相关《QT学习之路全.docx(214页珍藏版)》请在冰豆网上搜索。
QT学习之路全
QT学习之路
二维画图中视口坐标与窗口坐标
视口坐标系是对物理坐标系而言,也就是我们平常画图使用的坐标系(通常相对屏幕或者父窗口部件);窗口坐标系是我们自己定义的逻辑坐标系,根据画图的方面而自定义的。
在默认情况下,视口坐标系与窗口坐标系是一致的。
在Qt中我们画图时坐标都是相对于窗口坐标系,而与视口坐标系无关。
比如:
比如我们要一个矩形(0,0,320,200)(视口坐标)内画图,而我们可以定义这个矩形的窗口坐标为(-50,-50,100,100);窗口坐标系统相对与视口坐标系做了平移与缩放。
在视口坐标系矩形的左上角坐标为(0,0),宽度为320,高度为200的长方形;而在窗口坐标系中这个矩形的左上角坐标为(-50,-50),宽度为100,高度为100的正方形。
坐标的转换公式:
(x0-m)*Rx=(X-0) (Y0-n)*Ry=Y-0
Rx为X轴的缩放因子,Ry为Y轴的缩放因子.
Qt学习之路
(1)
Qt是一个著名的C++库——或许并不能说这只是一个GUI库,因为Qt十分庞大,并不仅仅是GUI。
使用Qt,在一定程序上你获得的是一个“一站式”的服务:
不再需要研究STL,不再需要C++的,因为Qt有它自己的QString等等。
或许这样说很偏激,但Qt确实是一个“伟大的C++库”。
我们所使用的Qt,确切地说也就是它的GUI编程部分。
C++的GUI编程同Java不同:
GUI并不是C++标准的一部分。
所以,如果使用Java,那么你最好的选择就是AWT/Swing,或者也可以使SWT/JFace,但是,C++的GUI编程给了你更多的选择:
wxWidget,gtk++以及Qt。
这几个库我都有接触,但是接触都不是很多,只能靠一些资料和自己的一点粗浅的认识说一下它们之间的区别(PS:
更详尽的比较在前面的文章中有)。
首先说wxWidget,这是一个标准的C++库,和Qt一样庞大。
它的语法看上去和MFC类似,有大量的宏。
据说,一个MFC程序员可以很容易的转换到wxWidget上面来。
wxWidget有一个很大的优点,就是它的界面都是原生风格的。
这是其他的库所不能做到的。
wxWidget的运行效率很高,据说在Windows平台上比起微软自家的MFC也不相上下。
gtk++其实是一个C库,不过由于C++和C之间的关系,这点并没有很大的关系。
但是,gtk++是一个使用C语言很优雅的实现了面向对象程序设计的范例。
不过,这也同样带来了一个问题——它的里面带有大量的类型转换的宏来模拟多态,并且它的函数名“又臭又长(不过这点我倒是觉得无所谓,因为它的函数名虽然很长,但是同样很清晰)”,使用下划线分割单词,看上去和Linux如出一辙。
由于它是C语言实现,因此它的运行效率当然不在话下。
gtk++并不是模拟的原生界面,而有它自己的风格,所以有时候就会和操作系统的界面显得格格不入。
再来看Qt,和wxWidget一样,它也是一个标准的C++库。
但是它的语法很类似于Java的Swing,十分清晰,而且SIGNAL/SLOT机制使得程序看起来很明白——这也是我首先选择Qt的一个很重要的方面,因为我是学Java出身的。
不过,所谓“成也萧何,败也萧何”,这种机制虽然很清楚,但是它所带来的后果是你需要使用Qt的qmake对程序进行预处理,才能够再使用make或者nmake进行编译。
并且它的界面也不是原生风格的,尽管Qt使用style机制十分巧妙的模拟了本地界面。
另外值得一提的是,Qt不仅仅运行在桌面环境中,Qt已经被Nokia收购,它现在已经会成为Symbian系列的主要界面技术——Qt是能够运行于嵌入式平台的。
以往人们对Qt的授权多有诟病。
因为Qt的商业版本价格不菲,开源版本使用的是GPL协议。
但是现在Qt的开源协议已经变成LGPL。
这意味着,你可以将Qt作为一个库连接到一个闭源软件里面。
可以说,现在的Qt协议的争议已经不存在了——因为wxWidgets或者gtk+同样使用的是类似的协议发布的。
在本系列文章中,我们将使用Qt4进行C++GUI的开发。
我是参照着《C++GUIProgrammingwithQt4》一书进行学习的。
其实,我也只是初学Qt4,在这里将这个学习笔记记下来,希望能够方便更多的朋友学习Qt4。
我是一个Java程序员,感觉Qt4的一些命名规范以及约束同Java有异曲同工之妙,因而从Java迁移到Qt4似乎困难不大。
不过,这也主要是因为Qt4良好的设计等等。
闲话少说,还是尽快开始下面的学习吧!
Qt学习之路(2/3):
Hello,world!
任何编程技术的学习第一课基本上都会是Hello,world!
,我也不想故意打破这个惯例——照理说,应该首先回顾一下Qt的历史,不过即使不说这些也并无大碍。
或许有人总想知道,Qt这个单词是什么意思。
其实,这并不是一个缩写词,仅仅是因为它的发明者,TrollTech公司的CEO,HaarardNord和Trolltech公司的总裁EirikChambe-Eng在联合发明Qt的时候并没有一个很好的名字。
在这里,字母Q是Qt库中所有类的前缀——这仅仅是因为在Haarard的emacs的字体中,这个字母看起来特别的漂亮;而字母t则代表“toolkit”,这是在Xt(Xtoolkit)中得到的灵感。
顺便说句,Qt原始的公司就是上面提到的Trolltech,貌似有一个中文名字是奇趣科技——不过现在已经被Nokia收购了。
因此,一些比较旧的文章里面会提到Trolltech这个名字。
好了,闲话少说,先看看Qt的开发吧!
事先说明一下,我是一个比较懒的人,不喜欢配置很多的东西,而Qt已经提供了一个轻量级的IDE,并且它的网站上也有forEclipse和VS的开发插件,不过在这里我并不想用这些大块头。
Qt有两套协议——商业版本和开源的LGPL版本。
不同的是前者要收费,而后者免费,当然,后者还要遵循LGPL协议的规定,这是题外话。
安装完成后会有个QtCreator的东西,这就是官方提供的一个轻量级IDE,不过它的功能还是蛮强大的。
运行这个就会发现,其实Qt不仅仅是LinuxKDE桌面的底层实现库。
而且是这个IDE的实现的这个IDE就是用Qt完成的。
QtCreator左面从上到下依次是Welcome(欢迎页面,就是一开始出现的那个);Edit(我们的代码编辑窗口);Debug(调试窗口);Projects(工程窗口);Help(帮助,这个帮助完全整合的Qt的官方文档,相当有用);Output(输出窗口)。
下面我们来试试我们的Hello,world!
吧!
在Edit窗口空白处点右键,有Newproject...这里我们选第三项,QtGuiApplication。
然后点击OK,来到下一步,输入工程名字和保存的位置。
点击Next,来到选择库的界面。
这里我们系统默认为我们选择了Qtcore和GUI,还记得我们建的是GuiApplication吗?
嗯,就是这里啦,它会自动为我们加上gui这个库。
现在应该就能看出,Qt是多么庞大的一个库,它不仅仅有Gui,而且有Network,OpenGL,XML之类。
不过,现在在这里我们不作修改,直接Next。
编缉推荐阅读以下文章
∙Qt学习之路(10):
Meta-Object系统
∙Qt学习之路(9):
深入了解信号槽
∙Qt学习之路(8):
创建一个对话框(下)
∙Qt学习之路(7):
创建一个对话框(上)
∙Qt学习之路(6):
API文档的使用
∙Qt学习之路(5):
组件布局
∙Qt学习之路(4):
初探信号槽
∙Qt学习之路(3):
Hello,world!
(续)
∙Qt学习之路
(1):
前言
下一个界面需要我们定义文件名,我们不修改默认的名字,只是为了清楚起见,把generateform的那个勾去掉即可。
Next之后终于到了Finish了——漫长的一系列啊!
检查无误后Finish就好啦!
之后可以看到,IDE(即Qtcreator)自动生成了四个文件,一个.pro文件,两个.cpp和一个.h。
这里说明一下,.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件。
这里我们先不去管它。
main.cpp里面就是一个main函数,其他两个文件就是先前我们曾经指定的文件名的文件。
现在,我们把main.cpp中的代码修改一下:
#include
#include
intmain(intargc,char*argv[])
{
QApplicationapp(argc,argv);
QLabel*label=newQLabel("Hello,world!
");
label->show();
returnapp.exec();
}
修改完成后保存。
点击左下角的绿色三角键,Run。
一个小小的窗口出现了——
好了!
我们的第一个Qt程序已经完成了。
PS:
截了很多图,说得详细些,以后可就没这么详细的步骤啦,嘿嘿…相信很多朋友应该一下子就能看明白这个IDE应该怎么使用了的,无需我多费口舌。
呵呵。
下面来逐行解释一下前面的那个Hello,world!
程序,尽管很简单,但却可以对Qt程序的结构有一个清楚的认识。
现在再把代码贴过来:
#include
#include
intmain(intargc,char*argv[])
{
QApplicationapp(argc,argv);
QLabel*label=newQLabel("Hello,world!
");
label->show();
returnapp.exec();
}
第1行和第2行就是需要引入的头文件。
和普通的C++程序没有什么两样,如果要使用某个组件,就必须要引入相应的头文件,这类似于Java的import机制。
值得说明的是,Qt中头文件和类名是一致的。
也就是说,如果你要使用某个类的话,它的类名就是它的头文件名。
第3行是空行
第4行是main函数函数头。
这与普通的C++程序没有什么两样,学过C++的都明白。
因此你可以看到,实际上,Qt完全通过普通的main函数进入,这不同于wxWidgets,因为wxWidgets的Hello,world需要你继承它的一个wxApp类,并覆盖它的wxApp:
:
OnInit方法,系统会自动将OnInit编译成入口函数。
不过在Qt中,就不需要这些了。
第5行,噢噢,大括号…
第6行,创建一个QApplication对象。
这个对象用于管理应用程序级别的资源。
QApplication的构造函数要求两个参数,分别来自main的那两个参数,因此,Qt在一定程度上是支持命令行参数的。
第7行,创建一个QLabel对象,并且能够显示Hello,world!
字符串。
和其他库的Label控件一样,这是用来显示文本的。
在Qt中,这被称为一个widget(翻译出来是小东西,不过这个翻译并不好…),它等同于Windows技术里面的控件(controls)和容器(containers)。
也就是说,widget可以放置其他的widget,就像Swing的组件。
大多数Qt程序使用QMainWindow或者QDialog作为顶级组件,但Qt并不强制要求这点。
在这个例子中,顶级组件就是一个QLabel。
第8行,使这个label可见。
组件创建出来之后通常是不可见的,要求我们手动的使它们可见。
这样,在创建出组建之后我们就可以对它们进行各种定制,以避免出现之后在屏幕上面会有闪烁。
第9行,将应用程序的控制权移交给Qt。
这时,程序的事件循环就开始了,也就是说,这时可以相应你发出的各种事件了。
这类似于gtk+最后的一行gtk_main()。
第10行,大括号……程序结束了。
注意!
!
,我们并没有使用delete去删除创建的QLabel,因为在程序结束后操作系统会回收这个空间——这只是因为这个QLabel占用的内存比较小,但有时候这么做会引起麻烦的,特别是在大程序中,因此必须小心。
好了,程序解释完了。
按照正常的流程,下面应该编译。
前面也提过,Qt的编译不能使用普通的make,而必须先使用qmake进行预编译。
所以,第一步应该是在工程目录下使用
qmake-project
命令创建.pro文件(比如说是叫helloworld.pro)。
然后再在.pro文件目录下使用
qmakehelloworld.pro (make)
或者 qmake-tpvchelloworld.pro (nmake)
生成makefile,然后才能调用make或者是nmake进行编译。
不过因为我们使用的是IDE,所以这些步骤就不需要我们手动完成了。
值得说明一点的是,这个qmake能够生成标准的makefile文件,因此完全可以利用qmake自动生成makefile——这是题外话。
好了,下面修改一下源代码,把QLabel的创建一句改成
QLabel*label=newQLabel("
Hello,world!");
运行一下:
同Swing的JLabel一样,Qt也是支持HTML解析的。
好了,这个Hello,world就说到这里!
明确一下Qt的程序结构,在一个Qt源代码中,以下两条语句是必不可少的!
!
:
QApplicationapp(argc,argv);
//...
returnapp.exec();
Qt学习之路(4):
初探信号槽
看过了简单的Hello,world!
之后,下面来看看Qt最引以为豪的信号槽机制!
所谓信号槽,简单来说,就像是插销一样:
一个插头和一个插座。
怎么说呢?
当某种事件发生之后,比如,点击了一下鼠标,或者按了某个按键,这时,这个组件就会发出一个信号。
就像是广播一样,如果有了事件,它就漫天发声。
这时,如果有一个槽,正好对应上这个信号,那么,这个槽的函数就会执行,也就是回调。
就像广播发出了,如果你感兴趣,那么你就会对这个广播有反应。
干巴巴的解释很无力,还是看代码:
#include
#include
intmain(intargc,char*argv[])
{
QApplicationa(argc,argv);
QPushButton*button=newQPushButton("Quit");
QObject:
:
connect(button,SIGNAL(clicked()),&a,SLOT(quit()));
button->show();
returna.exec();
}
这是在QtCreator上面新建的文件,因为前面已经详细的说明怎么新建工程,所以这里就不再赘述了。
这个程序很简单,只有一个按钮,点击之后程序退出。
(顺便说一句,Qt里面的button被叫做QPushButton,真搞不明白为什么一个简单的button非得加上push呢?
呵呵)
主要是看这一句:
QObject:
:
connect(button,SIGNAL(clicked()),&a,SLOT(quit()));
QObject是所有类的根。
Qt使用这个QObject实现了一个单根继承的C++。
它里面有一个connect静态函数,用于连接信号槽。
当一个按钮被点击时,它会发出一个clicked信号,意思是,向周围的组件们声明:
我被点击啦!
当然,其它很多组件都懒得理他。
如果对它感兴趣,就告诉QObject说,你帮我盯着点,只要button发出clicked信号,你就告诉我——想了想之后,说,算了,你也别告诉我了,直接去执行我的某某某函数吧!
就这样,一个信号槽就形成了。
具体来说呢,这个例子就是QApplication的实例a说,如果button发出了clicked信号,你就去执行我的quit函数。
所以,当我们点击button的时候,a的quit函数被调用,程序退出了。
所以,在这里,clicked()就是一个信号,而quit()就是槽,形象地说就是把这个信号插进这个槽里面去。
Qt使用信号槽机制完成了事件监听操作。
这类似与Swing里面的listener机制,只是要比这个listener简单得多。
以后我们会看到,这种信号槽的定义也异常的简单。
值得注意的是,这个信号槽机制仅仅是使用的QObject的connect函数,其他并没有什么耦合——也就是说,完全可以利用这种机制实现你自己的信号监听!
不过,这就需要使用qmake预处理一下了!
细心的你或许发现,在QtCreator里面,SIGNAL和SLOT竟然变颜色了!
没错,Qt确实把它们当成了关键字!
实际上,Qt正是利用它们扩展了C++语言,因此才需要使用qmake进行预处理,比便使普通的C++编译器能够顺利编译。
另外,这里的signal和Unix系统里面的signal没有任何的关系!
哦哦,有一点关系,那就是名字是一样的!
信号槽机制是Qt关键部分之一,以后我们还会再仔细的探讨这个问题的。
Qt学习之路(5):
组件布局(布局:
Layout)
顾名思义,绝对定位就是使用最原始的定位方法,给出这个组件的坐标和长宽值。
这样,Qt就知道该把组件放在哪里,以及怎么设置组件的大小了。
但是这样做的一个问题是,如果用户改变了窗口大小,比如点击了最大化或者拖动窗口边缘,这时,你就要自己编写相应的函数来响应这些变化,以避免那些组件还只是静静地呆在一个角落。
或者,更简单的方法是直接禁止用户改变大小。
不过,Qt提供了另外的一种机制,就是布局,来解决这个问题。
你只要把组件放入某一种布局之中,当需要调整大小或者位置的时候,Qt就知道该怎样进行调整。
这类似于Swing的布局管理器,不过Qt的布局没有那么多,只有有限的几个。
来看一下下面的例子:
#include
#include
#include
#include
#include
intmain(intargc,char*argv[])
{
QApplicationapp(argc,argv);
QWidget*window=newQWidget;
window->setWindowTitle("Enteryourage");
QSpinBox*spinBox=newQSpinBox;
QSlider*slider=newQSlider(Qt:
:
Horizontal);
spinBox->setRange(0,130);
slider->setRange(0,130);
QObject:
:
connect(slider,SIGNAL(valueChanged(int)),spinBox,SLOT(setValue(int)));
QObject:
:
connect(spinBox,SIGNAL(valueChanged(int)),slider,SLOT(setValue(int)));
spinBox->setValue(35);
QHBoxLayout*layout=newQHBoxLayout;
layout->addWidget(spinBox);
layout->addWidget(slider);
window->setLayout(layout);
window->show();
returnapp.exec();
}
编缉推荐阅读以下文章
∙Qt学习之路(10):
Meta-Object系统
∙Qt学习之路(9):
深入了解信号槽
∙Qt学习之路(8):
创建一个对话框(下)
∙Qt学习之路(7):
创建一个对话框(上)
∙Qt学习之路(6):
API文档的使用
∙Qt学习之路(4):
初探信号槽
∙Qt学习之路(3):
Hello,world!
(续)
∙Qt学习之路
(2):
Hello,world!
∙Qt学习之路
(1):
前言
这里使用了两个新的组件:
QSpinBox和QSlider,以及一个新的顶级窗口QWidget。
QSpinBox是一个有上下箭头的微调器,QSlider是一个滑动杆,只要运行一下就会明白到底是什么东西了。
代码并不是那么难懂,还是来简单的看一下。
首先创建了一个QWidget的实例,调用setWindowTitle函数来设置窗口标题。
然后创建了一个QSpinBox和QSlider,分别设置了它们值的范围,使用的是setRange函数。
然后进行信号槽的链接。
这点后面再详细说明。
然后是一个QHBoxLayout,就是一个水平布局,按照从左到右的顺序进行添加,使用addWidget添加好组件后,调用QWidget的setLayout把QWidget的layout设置为我们定义的这个Layout,这样,程序就完成了!
编译运行一下,可以看到效果:
如果最大化的话:
虽然我并没有添加任何代码,但是那个layout就已经明白该怎样进行布局。
或许你发现,那两个信号槽的链接操作会不会产生无限递归?
因为steValue就会引发valueChanged信号!
答案是不会。
这两句语句实现了,当spinBox发出valueChanged信号的时候,会回调slider的setValue,以更新slider的值;而slider发出valueChanged信号的时候,又会回调slider的setValue。
但是,如果新的value和旧的value是一样的话,是不会发出这个信号的,因此避免了无限递归。
编缉推荐阅读以下文章
∙Qt学习之路(10):
Meta-Object系统
∙Qt学习之路(9):
深入了解信号槽
∙Qt学习之路(8):
创建一个对话框(下)
∙Qt学习之路(7):
创建一个对话框(上)
∙Qt学习之路(6):
API文档的使用
∙Qt学习之路(4):
初探信号槽
∙Qt学习之路(3):
Hello,worl