西餐冷菜十大沙拉:QT学习道路 - 陈百虎的日志 - 网易博客

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 12:32:27
网易 新闻 微博 邮箱 闪电邮 相册 有道 手机邮 印像派 梦幻人生  更多 博客 博客首页 博客话题 热点专题 博客油菜地 找朋友 博客圈子 博客风格 手机博客 短信写博 邮件写博 博客复制 摄影 主题展区 每日专题 摄影人物志 摄影师专访 摄影点评 随便看看  注册   登录    显示下一条  |  关闭

china_tiger 的博客

含泪播种的人,一定能含泪收获!

 

导航

  • 首页
  • 日志
  • 相册
  • 音乐
  • 收藏
  • 博友
  • 关于我
     

日志

   陈百虎做人简单,做事勤奋!  加博友   关注他

最新日志

  • Fw:类型问题(一)
  • 数据库
  • 8086/8088的指令系统--处理
  • Fw:Shell脚本调试技术
  • Fw:shell编程中的几种命令
  • Fw:shell编程技术

该作者的其他文章

    博主推荐

      相关日志

        随机阅读

          首页推荐

          • 微博直播小易游菲律宾
          • 户外猛男冰河前裸奔
          • 苏紫紫:我曾'私拍'过
          • 民政让抗日老兵去台湾
          • 易中天:得把儒学漂白
          • 机关幼儿园巨额公款养
          更多>>

            人生路上—— 决定一生的简单法则   Fw:Qt4编程环境的建立

          QT学习道路

          C++程序设计 2009-11-01 10:26:47 阅读792 评论0   字号: 订阅

          主题: QT学习道路

          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++ GUI Programming with Qt4》一书进行学习的。其实,我也只是初学Qt4,在这里将这个学习笔记记下来,希望能够方便更多的朋友学习Qt4。我是一个Java程序员,感觉 Qt4的一些命名规范以及约束同Java有异曲同工之妙,因而从Java迁移到Qt4似乎困难不大。不过,这也主要是因为Qt4良好的设计等等。

          闲话少说,还是尽快开始下面的学习吧!

          Qt学习之路(2):Hello, world!


          任何编程技术的学习第一课基本上都会是Hello, world!,我也不想故意打破这个惯例——照理说,应该首先回顾一下Qt的历史,不过即使不说这些也并无大碍。
          或许有人总想知道,Qt这个单词是什么意思。其实,这并不是一个缩写词,仅仅是因为它的发明者,TrollTech公司的 CEO,Haarard Nord和Trolltech公司的总裁Eirik Chambe-Eng在联合发明Qt的时候并没有一个很好的名字。在这里,字母Q是Qt库中所有类的前缀——这仅仅是因为在Haarard的emacs的字体中,这个字母看起来特别的漂亮;而字母t则代表“toolkit”,这是在Xt( X toolkit )中得到的灵感。
          顺便说句,Qt原始的公司就是上面提到的Trolltech,貌似有一个中文名字是奇趣科技——不过现在已经被Nokia收购了。因此,一些比较旧的文章里面会提到Trolltech这个名字。
          好了,闲话少说,先看看Qt的开发吧!事先说明一下,我是一个比较懒的人,不喜欢配置很多的东西,而Qt已经提供了一个轻量级的IDE,并且它的网站上也有for Eclipse 和 VS 的开发插件,不过在这里我并不想用这些大块头 :)
          Qt有两套协议——商业版本和开源的LGPL版本。不同的是前者要收费,而后者免费,当然,后者还要遵循LGPL协议的规定,这是题外话。
          Qt的网址是https://qt.nokia.com/downloads,不过我打开这个站点总是很慢,不知道为什么。你可以找到大大的 LGPL/Free 和 Commercial,好了,我选的是LGPL版本的,下载包蛮大,但是下载并不会很慢。下载完成后安装就可以了,其它不用管了。这样,整个Qt的开发环境就装好了——如果你需要的话,也可以把qmake所在的目录添加进环境变量,不过我就不做了。

          安装完成后会有个Qt Creator的东西,这就是官方提供的一个轻量级IDE,不过它的功能还是蛮强大的。运行这个就会发现,其实Qt不仅仅是Linux KDE桌面的底层实现库。而且是这个IDE的实现 :) 这个IDE就是用Qt完成的。
          Qt Creator左面从上到下依次是Welcome(欢迎页面,就是一开始出现的那个);Edit(我们的代码编辑窗口);Debug(调试窗口);Projects(工程窗口);Help(帮助,这个帮助完全整合的Qt的官方文档,相当有用);Output(输出窗口)。
          下面我们来试试我们的 Hello, world! 吧!
          在Edit窗口空白处点右键,有 New project... 这里我们选第三项,Qt Gui Application。
            然后点击OK,来到下一步,输入工程名字和保存的位置。     点击Next,来到选择库的界面。这里我们系统默认为我们选择了Qt core 和 GUI,还记得我们建的是Gui Application吗?嗯,就是这里啦,它会自动为我们加上gui这个库。现在应该就能看出,Qt是多么庞大的一个库,它不仅仅有Gui,而且有 Network,OpenGL,XML之类。不过,现在在这里我们不作修改,直接Next。     下一个界面需要我们定义文件名,我们不修改默认的名字,只是为了清除起见,把generate form的那个勾去掉即可。     Next之后终于到了Finish了——漫长的一系列啊!检查无误后Finish就好啦!     之后可以看到,IDE自动生成了四个文件,一个.pro文件,两个.cpp和一个.h。这里说明一下,.pro就是工程文件 (project),它是qmake自动生成的用于生产makefile的配置文件。这里我们先不去管它。main.cpp里面就是一个main函数,其他两个文件就是先前我们曾经指定的文件名的文件。     现在,我们把main.cpp中的代码修改一下:   #include
          #include

          int main(int argc, char *argv[])
          {
                  QApplication a(argc, argv);
                  QLabel *label = new QLabel("Hello, world!");
                  label->show();
                  return a.exec();
          }   修改完成后保存。点击左下角的绿色三角键,Run。一个小小的窗口出现了——     好了!我们的第一个Qt程序已经完成了。   PS:截了很多图,说得详细些,以后可就没这么详细的步骤啦,嘿嘿…相信很多朋友应该一下子就能看明白这个IDE应该怎么使用了的,无需我多费口舌。呵呵。   下一篇中,将会对这个Hello, world!做一番逐行解释!

          Qt学习之路(3):Hello, world!(续)


          下面来逐行解释一下前面的那个Hello, world!程序,尽管很简单,但却可以对Qt程序的结构有一个清楚的认识。现在再把代码贴过来:   #include
          #include

          int main(int argc, char *argv[])
          {
                  QApplication app(argc, argv);
                  QLabel *label = new QLabel("Hello, world!");
                  label->show();
                  return app.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文件目录下使用   qmake helloworld.pro  (make)   或者   qmake -tp vc helloworld.pro  (nmake)   生成makefile,然后才能调用make或者是nmake进行编译。不过因为我们使用的是IDE,所以这些步骤就不需要我们手动完成了。   值得说明一点的是,这个qmake能够生成标准的makefile文件,因此完全可以利用qmake自动生成makefile——这是题外话。   好了,下面修改一下源代码,把QLabel的创建一句改成   QLabel *label = new QLabel("

          Hello, world!

          ");   运行一下:     同Swing的JLabel一样,Qt也是支持HTML解析的。   好了,这个Hello, world就说到这里!明确一下Qt的程序结构,在一个Qt源代码中,一下两条语句是必不可少的: QApplication app(argc, argv);
          //...
          return app.exec();


          Qt学习之路(4):初探信号槽


          看过了简单的Hello, world! 之后,下面来看看Qt最引以为豪的信号槽机制!   所谓信号槽,简单来说,就像是插销一样:一个插头和一个插座。怎么说呢?当某种事件发生之后,比如,点击了一下鼠标,或者按了某个按键,这时,这个组件就会发出一个信号。就像是广播一样,如果有了事件,它就漫天发声。这时,如果有一个槽,正好对应上这个信号,那么,这个槽的函数就会执行,也就是回调。就像广播发出了,如果你感兴趣,那么你就会对这个广播有反应。干巴巴的解释很无力,还是看代码:   #include
          #include

          int main(int argc, char *argv[])
          {
                  QApplication a(argc, argv);
                  QPushButton *button = new QPushButton("Quit");
                  QObject::connect(button, SIGNAL(clicked()), &a, SLOT(quit()));
                  button->show();
                  return a.exec();
          }   这是在Qt Creator上面新建的文件,因为前面已经详细的说明怎么新建工程,所以这里就不再赘述了。这个程序很简单,只有一个按钮,点击之后程序退出。(顺便说一句,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预处理一下了!   细心的你或许发现,在Qt Creator里面,SIGNAL和SLOT竟然变颜色了!没错,Qt确实把它们当成了关键字!实际上,Qt正是利用它们扩展了C++语言,因此才需要使用qmake进行预处理,比便使普通的C++编译器能够顺利编译。另外,这里的signal和Unix系统里面的signal没有任何的关系!哦哦,有一点关系,那就是名字是一样的!   信号槽机制是Qt关键部分之一,以后我们还会再仔细的探讨这个问题的。



          Qt学习之路(5):组件布局


          同Swing类似,Qt也提供了几种组件定位的技术。其中就包括绝对定位和布局定位。   顾名思义,绝对定位就是使用最原始的定位方法,给出这个组件的坐标和长宽值。这样,Qt就知道该把组件放在哪里,以及怎么设置组件的大小了。但是这样做的一个问题是,如果用户改变了窗口大小,比如点击了最大化或者拖动窗口边缘,这时,你就要自己编写相应的函数来响应这些变化,以避免那些组件还只是静静地呆在一个角落。或者,更简单的方法是直接禁止用户改变大小。   不过,Qt提供了另外的一种机制,就是布局,来解决这个问题。你只要把组件放入某一种布局之中,当需要调整大小或者位置的时候,Qt就知道该怎样进行调整。这类似于Swing的布局管理器,不过Qt的布局没有那么多,只有有限的几个。   来看一下下面的例子:   #include
          #include
          #include
          #include
          #include

          int main(int argc, char *argv[])
          {
                  QApplication app(argc, argv);
                  QWidget *window = new QWidget;
                  window->setWindowTitle("Enter your age");

                  QSpinBox *spinBox = new QSpinBox;
                  QSlider *slider = new QSlider(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 = new QHBoxLayout;
                  layout->addWidget(spinBox);
                  layout->addWidget(slider);
                  window->setLayout(layout);

                  window->show();

                  return app.exec();
          }   这里使用了两个新的组件: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是一样的话,是不会发出这个信号的,因此避免了无限递归。   迷糊了吧?举个例子看。比如下面的spinBox->setValue(35)执行的时候,首先,spinBox会将自己的值设为35,这样,它的值与原来的不一样了(在没有setValue之前的时候,默认值是0),于是它发出了valueChanged信号。slider接收到这个信号,于是回调自己的setValue函数,将它的值也设置成35,它也发出了valueChanged信号。当然,此时spinBox又收到了,不过它发现,这个35和它本身的值是一样的,于是它就不发出信号,所以信号传递就停止了。   那么,你会问,它们是怎么知道值的呢?答案很简单,因为你的信号和槽都接受了一个int参数!新的值就是通过这个进行传递的。实际上,我们利用Qt的信号槽机制完成了一个数据绑定,使两个组件或者更多组件的状态能够同步变化。   Qt一共有三种主要的layout,分别是:   QHBoxLayout- 按照水平方向从左到右布局;   QVBoxLayout- 按照竖直方向从上到下布局;   QGridLayout- 在一个网格中进行布局,类似于HTML的table。   layout使用addWidget添加组件,使用addLayout可以添加子布局,因此,这就有了无穷无尽的组合方式。   我是在Windows上面进行编译的,如果你要是在其他平台上面,应用程序就会有不同的样子:     还记得前面曾经说过,Qt不是使用的原生组件,而是自己绘制模拟的本地组件的样子,不过看看这个截图,它模拟的不能说百分百一致,也可说是惟妙惟肖了… :)

          Qt学习之路(6): API文档的使用


          今天来说一下有关Qt API文档的使用。因为Qt有一个商业版本,因此它的文档十分健全,而且编写良好。对于开发者来说,查看文档时开发必修课之一——没有人能够记住那么多API的使用!   在Qt中查看文档是一件很简单的事情。如果你使用QtCreator,那么左侧的Help按钮就是文档查看入口。否则的话,你可以在Qt的安装目录下的bin里面的assistant.exe中看到Qt的文档。在早期版本中,Qt的文档曾以HTML格式发布,不过在2009.03版中我没有找到 HTML格式的文档,可能Qt已经把它全部换成二进制格式的了吧?——当然,如果你全部安装了Qt的组件,是可以在开始菜单中找到assistant的!   assistant里面的文档有很多项:     其中,第一个是帮助的帮助:-);第二个是Qt Designer的帮助;第三个是Qt Linguist的帮助;第四个是QMake的帮助;最后一个是Qt的API文档,在QtCreator中默认打开的就是这部分。   不过,关于文档的内容这里实在不好阐述,因为整个文档太大了,我也并没有看过多少,很多时候都是随用随查,就好像是字典一样——谁也不会天天没事抱着本字典去看不是?还有就是这里的文档都是英文的,不过如果是做开发的话,了解一些英文还是很有帮助的,不是吗?




          Qt学习之路(7): 创建一个对话框(上)


          首先说明一点,在C++ GUI Programming with Qt4, 2nd中,这一章连同以后的若干章一起,完成了一个比较完整的程序——一个模仿Excel的电子表格。不过这个程序挺大的,而且书中也没有给出完整的源代码,只是分段分段的——我不喜欢这个样子,我想要看到我写出来的是什么东西,这是最主要的,而不是慢慢的过上几章的内容才能看到自己的作品。所以,我打算换一种方式,每章只给出简单的知识,但是每章都能够运行出东西来。好了,扯完了,下面开始!   以前说的主要是一些基础知识,现在我们来真正做一个东西——一个查找对话框。什么?什么叫查找对话框?唉唉,先看看我们的最终作品吧!     好了,首先新建一个工程,就叫FindDialog吧!嗯,当然还是Qt Gui Application,然后最后一步注意,Base Dialog选择QDialog,而不是默认的QMainWindow,因为我们要学习建立对话框嘛!名字随便起,不过我就叫finddialog 啦!Ganarate form还是不要的。然后Finish就好了。   打开finddialog.h,开始编写头文件。   #ifndef FINDDIALOG_H
          #define FINDDIALOG_H

          #include

          class QCheckBox;
          class QLabel;
          class QLineEdit;
          class QPushButton;

          class FindDialog : public QDialog
          {
                  Q_OBJECT

          public:
                  FindDialog(QWidget *parent = 0);
                  ~FindDialog();
          signals:
                  void findNext(const QString &str, Qt::CaseSensitivity cs);
                  void findPrevious(const QString &str, Qt::CaseSensitivity cs);
          private slots:
                  void findClicked();
                  void enableFindButton(const QString &text);
          private:
                  QLabel *label;
                  QLineEdit *lineEdit;
                  QCheckBox *caseCheckBox;
                  QCheckBox *backwardCheckBox;
                  QPushButton *findButton;
                  QPushButton *closeButton;
          };

          #endif // FINDDIALOG_H   大家都是懂得C++的啊,所以什么#ifndef,#define和#endif的含义和用途就不再赘述了。   首先,声明四个用到的类。这里做的是前向声明,否则的话是编译不过的,因为编译器不知道这些类是否存在。简单来说,所谓前向声明就是告诉编译器,我要用这几个类,而且这几个类存在,你就不要担心它们存不存在的问题啦!   然后是我们的FindDialog,继承自QDialog。   下面是一个重要的东西:Q_OBJECT。这是一个宏。凡是定义信号槽的类都必须声明这个宏。至于为什么,我们以后再说。   然后是public的构造函数和析构函数声明。   然后是一个signal:,这是Qt的关键字——还记得前面说过的嘛?Qt扩展了C++语言,因此它有自己的关键字——这是对信号的定义,也就是说,FindDialog有两个public的信号,它可以在特定的时刻发出这两个信号,就这里来说,如果用户点击了Find按钮,并且选中了Search backward,就会发出findPrevious(),否则发出findNext()。   紧接着是private slots:的定义,和前面的signal一样,这是私有的槽的定义。也就是说,FindDialog具有两个槽,可以接收某些信号,不过这两个槽都是私有的。   为了slots的定义,我们需要访问FindDialog的组件,因此,我们把其中的组件定义为成员变量以便访问。正是因为需要定义这些组件,才需要对它们的类型进行前向声明。因为我们仅仅使用的是指针,并不涉及到这些类的函数,因此并不需要include它们的头文件——当然,你想直接引入头文件也可以,不过那样的话编译速度就会慢一些。   好了,头文件先说这些,下一篇再说源代码啦!休息,休息一下!



          Qt学习之路(8): 创建一个对话框(下)


          接着前一篇,下面是源代码部分:   #include
          #include "finddialog.h"

          FindDialog::FindDialog(QWidget *parent)
                  : QDialog(parent)
          {
                  label = new QLabel(tr("Find &what:"));
                  lineEdit = new QLineEdit;
                  label->setBuddy(lineEdit);

                  caseCheckBox = new QCheckBox(tr("Match &case"));
                  backwardCheckBox = new QCheckBox(tr("Search &backford"));

                  findButton = new QPushButton(tr("&Find"));
                  findButton->setDefault(true);
                  findButton->setEnabled(false);

                  closeButton = new QPushButton(tr("Close"));

                  connect(lineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(enableFindButton(const QString&)));
                  connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked()));
                  connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));

                  QHBoxLayout *topLeftLayout = new QHBoxLayout;
                  topLeftLayout->addWidget(label);
                  topLeftLayout->addWidget(lineEdit);

                  QVBoxLayout *leftLayout = new QVBoxLayout;
                  leftLayout->addLayout(topLeftLayout);
                  leftLayout->addWidget(caseCheckBox);
                  leftLayout->addWidget(backwardCheckBox);

                  QVBoxLayout *rightLayout = new QVBoxLayout;
                  rightLayout->addWidget(findButton);
                  rightLayout->addWidget(closeButton);
                  rightLayout->addStretch();

                  QHBoxLayout *mainLayout = new QHBoxLayout;
                  mainLayout->addLayout(leftLayout);
                  mainLayout->addLayout(rightLayout);
                  setLayout(mainLayout);

                  setWindowTitle(tr("Find"));
                  setFixedHeight(sizeHint().height());
          }

          FindDialog::~FindDialog()
          {

          }

          void FindDialog::findClicked()
          {
                  QString text = lineEdit->text();
                  Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseInsensitive : Qt::CaseSensitive;
                  if(backwardCheckBox->isChecked()) {
                          emit findPrevious(text, cs);
                  } else {
                          emit findNext(text, cs);
                  }
          }

          void FindDialog::enableFindButton(const QString &text)
          {
                  findButton->setEnabled(!text.isEmpty());
          }
            CPP文件要长一些哦——不过,它们的价钱也会更高,嘿嘿——嗯,来看代码,第一行include的是QtGui。Qt是分模块的,记得我们建工程的时候就会问你,使用哪些模块?QtCore?QtGui?QtXml?等等。这里,我们引入QtGui,它包括了QtCore和 QtGui模块。不过,这并不是最好的做法,因为QtGui文件很大,包括了GUI的所有组件,但是很多组件我们根本是用不到的——就像Swing的 import,你可以import到类,也可以使用*,不过都不会建议使用*,这里也是一样的。我们最好只引入需要的组件。不过,那样会把文件变长,现在就先用QtGui啦,只要记得正式开发时不能这么用就好啦!   构造函数有参数初始化列表,用来调用父类的构造函数,相当于Java里面的super()函数。这是C++的相关知识,不是Qt发明的,这里不再赘述。   然后新建一个QLabel。还记得前面的Hello, world!里面也使用过QLabel吗?那时候只是简单的传入一个字符串啊!这里怎么是一个函数tr()?函数tr()全名是 QObject::tr(),被它处理的字符串可以使用工具提取出来翻译成其他语言,也就是做国际化使用。这以后还会仔细讲解,只要记住,Qt的最佳实践:如果你想让你的程序国际化的话,那么,所有用户可见的字符串都要使用QObject::tr()!但是,为什么我们没有写QObject::tr(),而仅仅是tr()呢?原来,tr()函数是定义在Object里面的,所有使用了Q_OBJECT宏的类都自动具有tr()函数。   字符串中的&代表快捷键。注意看下面的findButton的&Find,它会生成Find字符串,当你按下 Alt+F的时候,这个按钮就相当于被点击——这么说很难受,相信大家都明白什么意思。同样,前面label里面也有一个&,因此它的快捷键就是 Alt+W。不过,这个label使用了setBuddy函数,它的意思是,当label获得焦点时,比如按下Alt+W,它的焦点会自动传给它的 buddy,也就是lineEdit。看,这就是伙伴的含义(buddy英文就是伙伴的意思)。   后面几行就比较简单了:创建了两个QCheckBox,把默认的按钮设为findButton,把findButton设为不可用——也就是变成灰色的了。   再下面是三个connect语句,用来连接信号槽。可以看到,当lineEdit发出textChanged(const QString&)信号时,FindDialog的enableFindButton(const QString&)函数会被调用——这就是回调,是有系统自动调用,而不是你去调用——当findButton发出clicked()信号时,FindDialog的findClicked()函数会被调用;当closeButton发出clicked()信号时,FindDialog的 close()函数会被调用。注意,connect()函数也是QObject的,因为我们继承了QObject,所以能够直接使用。   后面的很多行语句都是layout的使用,虽然很复杂,但是很清晰——编写layout布局最重要一点就是思路清楚,想清楚哪个套哪个,就会很好编写。这里我们的对话框实际上是这个样子的:     注意那个spacer是由rightLayout的addStretch()添加的,就像弹簧一样,把上面的组件“顶起来”。   最后的setWindowTitle()就是设置对话框的标题,而setFixedHeight()是设置成固定的高度,其参数值sizeHint()返回“最理想”的大小,这里我们使用的是height()函数去到“最理想”的高度。   好了,下面该编写槽了——虽然说是slot,但实际上它就是普通的函数,既可以和其他函数一样使用,又可以被系统回调。   先看findClicked()函数。首先取出lineEdit的输入值;然后判断caseCheckBox是不是选中,如果选中就返回 Qt::CaseInsensitive,否则返回Qt::CaseSensitive,用于判断是不是大小写敏感的查找;最后,如果 backwardCheckBox被选中,就emit(发出)信号findPrevious(),否则emit信号findNext。   enableFindButton()则根据lineEdit的内容是不是变化——这是我们的connect连接的——来设置findButton是不是可以使用,这个很简单,不再说了。   这样,FindDialog.cpp也就完成了。下面编写main.cpp——其实QtCreator已经替我们完成了——   #include

          #include "finddialog.h"

          int main(int argc, char *argv[])
          {
                  QApplication app(argc, argv);
                  FindDialog *dialog = new FindDialog;
                  dialog->show();
                  return app.exec();
          }   运行一下看看我们的成果吧!   虽然很简单,也没有什么实质性的功能,但是我们已经能够制作对话框了——Qt的组件成百上千,不可能全部介绍完,只能用到什么学什么,更重要的是,我们已经了解了其编写思路,否则的话,即便是你拿着全世界所有的砖瓦,没有设计图纸,你也不知道怎么把它们组合成高楼大厦啊!   嘿嘿,下回见!





          Qt学习之路(9):深入了解信号槽


          信号槽机制是Qt编程的基础。通过信号槽,能够使Qt各组件在不知道对方的情形下能够相互通讯。这就将类之间的关系做了最大程度的解耦。   槽函数和普通的C++成员函数没有很大的区别。它们也可以使virtual的;可以被重写;可以使public、protected或者 private的;可以由其它的C++函数调用;参数可以是任何类型的。如果要说区别,那就是,槽函数可以和一个信号相连接,当这个信号发生时,它可以被自动调用。   connect()语句的原型类似于:   connect(sender, SIGNAL(signal), receiver, SLOT(slot));   这里,sender和receiver都是QObject类型的,singal和slot都是没有参数名称的函数签名。SINGAL()和SLOT()宏用于把参数转换成字符串。   深入的说,信号槽还有更多可能的用法,如下所示。   一个信号可以和多个槽相连:   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()));   这种情况并不经常出现,因为当一个对象delete之后,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)));   这里,const QString &这个参数就会被槽忽略掉。   如果信号槽的参数不相容,或者是信号或槽有一个不存在,或者在信号槽的连接中出现了参数名字,在Debug模式下编译的时候,Qt都会很智能的给出警告。   在这之前,我们仅仅在widgets中使用到了信号槽,但是,注意到connect()函数其实是在QObject中实现的,并不局限于GUI,因此,只要我们继承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()调用的时候,就会发出salaryChanged()信号。注意这里的if判断,这是避免递归的方式!还记得前面提到的循环连接吗?如果没有if,当出现了循环连接的时候就会产生无限递归。



          Qt学习之路(10): Meta-Object系统


          前面说过,Qt使用的是自己的预编译器,它提供了对C++的一种扩展。利用Qt的信号槽机制,就可以把彼此独立的模块相互连接起来,不需要实现知道模块的任何细节。   为了达到这个目的,Qt提出了一个Meta-Object系统。它提供了两个关键的作用:信号槽和内省。   面向对象程序设计里面会讲到Smalltalk语言有一个元类系统。所谓元类,就是这里所说的Meta-Class。如果写过HTML,会知道 HTML标签里面也有一个,这是用于说明页面的某些属性的。同样,Qt的Meta-Object系统也是类似的作用。内省又称为反射,允许程序在运行时获得类的相关信息,也就是meta-information。什么是meta-information呢?举例来说,像这个类叫什么名字?它有什么属性?有什么方法?它的信号列表?它的槽列表?等等这些信息,就是这个类的meta-information,也就是“元信息”。这个机制还提供了对国际化的支持,是QSA(Qt Script for Application)的基础。   标准C++并没有Qt的meta-information所需要的动态meta-information。所以,Qt提供了一个独立的工具,moc,通过定义Q_OBJECT宏实现到标准C++函数的转变。moc使用纯C++实现的,因此可以再任何编译器中使用。

          这种机制工作过程是:   首先,Q_OBJECT宏声明了一些QObject子类必须实现的内省的函数,如metaObject(),tr(),qt_metacall()等;   第二,Qt的moc工具实现Q_OBJECT宏声明的函数和所有信号;   第三,QObject成员函数connect()和disconnect()使用这些内省函数实现信号槽的连接。
          以上这些过程是qmake,moc和QObject自动处理的,你不需要去考虑它们。如果实现好奇的话,可以通过查看QMetaObject的文档和moc的源代码来一睹芳容。




          Qt学习之路(11): MainWindow


          尽管Qt提供了很方便的快速开发工具QtDesigner用来拖放界面元素,但是现在我并不打算去介绍这个工具,原因之一在于我们的学习大体上是依靠手工编写代码,过早的接触设计工具并不能让我们对Qt的概念突飞猛进……   前面说过,本教程很大程度上依照的是《C++ GUI Programming with Qt4, 2nd Edition》这本书。但是,这本书中接下来的部分用了很大的篇幅完成了一个简单的类似Excel的程序。虽然最终效果看起来很不错,但我并不打算完全依照这个程序来,因为这个程序太大,以至于我们在开始之后会有很大的篇幅接触不到能够运行的东西,这无疑会严重打击学习的积极性——至少我是如此,看不到做的东西很难受——所以,我打算重新组织一下这个程序,请大家按照我的思路试试看吧!   闲话少说,下面开始新的篇章!   就像Swing的顶层窗口一般都是JFrame一样,Qt的GUI程序也有一个常用的顶层窗口,叫做MainWindow。好了,现在我们新建一个Gui Application项目MyApp,注意在后面选择的时候选择Base Class是QMainWindow。     然后确定即可。此时,QtCreator已经为我们生成了必要的代码,我们只需点击一下Run,看看运行出来的结果。     一个很简单的窗口,什么都没有,这就是我们的主窗口了。   MainWindow继承自QMainWindow。QMainWindow窗口分成几个主要的区域:     最上面是Window Title,用于显示标题和控制按钮,比如最大化、最小化和关闭等;下面一些是Menu Bar,用于显示菜单;再下面一点事Toolbar areas,用于显示工具条,注意,Qt的主窗口支持多个工具条显示,因此这里是ares,你可以把几个工具条并排显示在这里,就像Word2003一样;工具条下面是Dock window areas,这是停靠窗口的显示区域,所谓停靠窗口就是像Photoshop的工具箱一样,可以在主窗口的四周显示;再向下是Status Bar,就是状态栏;中间最大的Central widget就是主要的工作区了。   好了,今天的内容不多,我们以后的工作就是要对这个MainWindow进行修改,以满足我们的各种需要。



          Qt学习之路(12): 菜单和工具条


          在前面的QMainWindow的基础之上,我们开始着手建造我们的应用程序。虽然现在已经有一个框架,但是,确切地说我们还一行代码没有写呢!下面的工作就不那么简单了!在这一节里面,我们要为我们的框架添加菜单和工具条。   就像Swing里面的Action一样,Qt里面也有一个类似的类,叫做QAction。顾名思义,QAction类保存有关于这个动作,也就是action的信息,比如它的文本描述、图标、快捷键、回调函数(也就是信号槽),等等。神奇的是,QAction能够根据添加的位置来改变自己的样子 ——如果添加到菜单中,就会显示成一个菜单项;如果添加到工具条,就会显示成一个按钮。这也是为什么要把菜单和按钮放在一节里面。下面开始学习!   首先,我想添加一个打开命令。那么,就在头文件里面添加一个私有的QAction变量:   class QAcion;
          //...
          private:
                  QAction *openAction;
          //...   注意,不要忘记QAction类的前向声明哦!要不就会报错的!   然后我们要在cpp文件中添加QAction的定义。为了简单起见,我们直接把它定义在构造函数里面:           openAction = new QAction(tr("&Open"), this);
                  openAction->setShortcut(QKeySequence::Open);
                  openAction->setStatusTip(tr("Open a file."));   第一行代码创建一个QAction对象。QAction有几个重载的构造函数,我们使用的是   QAction(const QString &text, QObject* parent);   这一个。它有两个参数,第一个text是这个动作的文本描述,用来显示文本信息,比如在菜单中的文本;第二个是parent,一般而言,我们通常传入this指针就可以了。我们不需要去关心这个parent参数具体是什么,它的作用是指明这个QAction的父组件,当这个父组件被销毁时,比如 delete或者由系统自动销毁,与其相关联的这个QAction也会自动被销毁。   如果你还是不明白构造函数的参数是什么意思,或者说想要更加详细的了解QAction这个类,那么就需要自己翻阅一下它的API文档。前面说过有关API的使用方法,这里不再赘述。这也是学习Qt的一种方法,因为Qt是一个很大的库,我们不可能面面俱到,因此只为说道用到的东西,至于你自己想要实现的功能,就需要自己去查文档了。   第二句,我们使用了setShortcut函数。shortcut是这个动作的快捷键。Qt的QKeySequence已经为我们定义了很多内置的快捷键,比如我们使用的Open。你可以通过查阅API文档获得所有的快捷键列表,或者是在QtCreator中输入::后会有系统的自动补全功能显示出来。这个与我们自己定义的有什么区别呢?简单来说,我们完全可以自己定义一个tr("Ctrl+O")来实现快捷键。原因在于,这是Qt跨平台性的体现。比如PC键盘和Mac键盘是不一样的,一些键在PC键盘上有,而Max键盘上可能并不存在,或者反之,所以,推荐使用QKeySequence类来添加快捷键,这样,它会根据平台的不同来定义不同的快捷键。   第三句是setStatusTip函数。这是添加状态栏的提示语句。状态栏就是主窗口最下面的一条。现在我们的程序还没有添加状态栏,因此你是看不到有什么作用的。   下面要做的是把这个QAction添加到菜单和工具条:           QMenu *file = menuBar()->addMenu(tr("&File"));
                  file->addAction(openAction);

                  QToolBar *toolBar = addToolBar(tr("&File"));
                  toolBar->addAction(openAction);   QMainWindow有一个menuBar()函数,会返回菜单栏,也就是最上面的那一条。如果不存在会自动创建,如果已经存在就返回那个菜单栏的指针。直接使用返回值添加一个菜单,也就是addMenu,参数是一个QString,也就是显示的菜单名字。然后使用这个QMenu指针添加这个 QAction。类似的,使用addToolBar函数的返回值添加了一个工具条,并且把这个QAction添加到了上面。   好了,主要的代码已经写完了。不过,如果你只修改这些的话,是编译不过的哦!因为像menuBar()函数返回一个QMenuBar指针,但是你并没有include它的头文件哦!虽然没有明着写出QMenuBar这个类,但是实际上你已经用到了它的addMenu函数了,所以还是要注意的!   下面给出来全部的代码:   1. mainwindow.h #ifndef MAINWINDOW_H
          #define MAINWINDOW_H

          #include

          class QAction;

          class MainWindow : public QMainWindow
          {
                  Q_OBJECT

          public:
                  MainWindow(QWidget *parent = 0);
                  ~MainWindow();

          private:
                  QAction *openAction;
          };

          #endif // MAINWINDOW_H   2. mainwindow.cpp   #include
          #include
          #include
          #include
          #include
          #include "mainwindow.h"

          MainWindow::MainWindow(QWidget *parent)
                  : QMainWindow(parent)
          {
                  openAction = new QAction(tr("&Open"), this);
                  openAction->setShortcut(QKeySequence::Open);
                  openAction->setStatusTip(tr("Open a file."));

                  QMenu *file = menuBar()->addMenu(tr("&File"));
                  file->addAction(openAction);

                  QToolBar *toolBar = addToolBar(tr("&File"));
                  toolBar->addAction(openAction);
          }

          MainWindow::~MainWindow()
          {

          }   main.cpp没有修改,这里就不给出了。下面是运行结果:     很丑,是吧?不过我们已经添加上了菜单和工具条了哦!按一下键盘上的Alt+F,因为这是我们给它定义的快捷键。虽然目前挺难看,不过以后就会变得漂亮的!想想看,Linux的KDE桌面可是Qt实现的呢!




          Qt学习之路(13): 菜单和工具条(续)


          前面一节我们已经把QAction添加到菜单和工具条上面。现在我们要添加一些图片美化一下,然后把信号槽加上,这样,我们的action就可以相应啦!   首先来添加图标。QAction的图标会显示在菜单项的前面以及工具条按钮上面显示。   为了添加图标,我们首先要使用Qt的资源文件。在QtCreator的项目上右击,选择New File...,然后选择resource file。     然后点击next,选择好位置,Finish即可。为了使用方便,我就把这个文件建在根目录下,建议应该在仔细规划好文件之后,建在专门的 rsources文件夹下。完成之后,生成的是一个.qrc文件,qrc其实是Qt Recource Collection的缩写。它只是一个普通的XML文件,可以用记事本等打开。不过,这里我们不去深究它的结构,完全利用QtCreator操作这个文件,     点击Add按钮,首先选择Add prefix,然后把生成的/new/prefix改成/。这是prefix就是以后使用图标时需要提供的前缀,以/开头。添加过prefix之后,然后在工程文件中添加一个图标,再选择Add file,选择那个图标。这样完成之后保存qrc文件即可。   说明一下,QToolBar的图标大小默认是32*32,菜单默认是16*16。如果提供的图标小于要求的尺寸,则不做操作,Qt不会为你放大图片;反之,如果提供的图标文件大于相应的尺寸要求,比如是64*64,Qt会自动缩小尺寸。     图片的路径怎么看呢?可以看出,Qt的资源文件视图使用树状结构,根是/,叶子节点就是图片位置,连接在一起就是路径。比如这张图片的路径就是/Open.png。   注意,为了简单起见,我们没有把图标放在专门的文件夹中。正式的项目中应该单独有一个resources文件夹放资源文件的。   然后回到前面的mainwindow.cpp,在构造函数中修改代码:   openAction = new QAction(tr("&Open"), this);
          openAction->setShortcut(QKeySequence::Open);
          openAction->setStatusTip(tr("Open a file."));
          openAction->setIcon(QIcon(":/Open.png")); // Add code.   我们使用setIcon添加图标。添加的类是QIcon,构造函数需要一个参数,是一个字符串。由于我们要使用qrc中定义的图片,所以字符串以 : 开始,后面跟着prefix,因为我们先前定义的prefix是/,所以就需要一个/,然后后面是file的路径。这是在前面的qrc中定义的,打开 qrc看看那张图片的路径即可。   好了,图片添加完成,然后点击运行,看看效果吧!     瞧!我们只需要修改QAction,菜单和工具条就已经为我们做好了相应的处理,还是很方便的!   下一步,为QAction添加事件响应。还记得Qt的事件响应机制是基于信号槽吗?点击QAction会发出triggered()信号,所以,我们要做的是声名一个slot,然后connect这个信号。   mainwindow.h   class MainWindow : public QMainWindow
          {
                  Q_OBJECT

          public:
                  MainWindow(QWidget *parent = 0);
                  ~MainWindow();

          private slots:
                  void open();        

          private:
                  QAction *openAction;
          };   因为我们的open()目前只要在类的内部使用,因此定义成private slots即可。然后修改cpp文件:   MainWindow::MainWindow(QWidget *parent)
                  : QMainWindow(parent)
          {
                  openAction = new QAction(tr("&Open"), this);
                  openAction->setShortcut(QKeySequence::Open);
                  openAction->setStatusTip(tr("Open a file."));
                  openAction->setIcon(QIcon(":/Open.png"));
                  connect(openAction, SIGNAL(triggered()), this, SLOT(open()));

                  QMenu *file = menuBar()->addMenu(tr("&File"));
                  file->addAction(openAction);

                  QToolBar *toolBar = addToolBar(tr("&File"));
                  toolBar->addAction(openAction);
          }

          void MainWindow::open()
          {
                  QMessageBox::information(NULL, tr("Open"), tr("Open a file"));
          }   注意,我们在open()函数中简单的弹出一个标准对话框,并没有其他的操作。编译后运行,看看效果:     好了,关于QAction的动作也已经添加完毕了!   至此,QAction有关的问题先告一段落。最后说一下,如果你还不知道怎么添加子菜单的话,看一下QMenu的API,里面会有一个addMenu函数。也就是说,创建一个QMenu然后添加就可以的啦!





          Qt学习之路(14): 状态栏


          有段时间没有写过博客了。假期去上海旅游,所以一直没有能够上网。现在又来到这里,开始新的篇章吧!   今天的内容主要还是继续完善前面的那个程序。我们要为我们的程序加上一个状态栏。   状态栏位于主窗口的最下方,提供一个显示工具提示等信息的地方。一般地,当窗口不是最大化的时候,状态栏的右下角会有一个可以调节大小的控制点;当窗口最大化的时候,这个控制点会自动消失。Qt提供了一个QStatusBar类来实现状态栏。   Qt具有一个相当成熟的GUI框架的实现——这一点感觉比Swing要强一些——Qt似乎对GUI的开发做了很多设计,比如 QMainWindow类里面就有一个statusBar()函数,用于实现状态栏的调用。类似menuBar()函数,如果不存在状态栏,该函数会自动创建一个,如果已经创建则会返回这个状态栏的指针。如果你要替换掉已经存在的状态栏,需要使用QMainWindow的setStatusBar()函数。   在Qt里面,状态栏显示的信息有三种类型:临时信息、一般信息和永久信息。其中,临时信息指临时显示的信息,比如QAction的提示等,也可以设置自己的临时信息,比如程序启动之后显示Ready,一段时间后自动消失——这个功能可以使用QStatusBar的showMessage()函数来实现;一般信息可以用来显示页码之类的;永久信息是不会消失的信息,比如可以在状态栏提示用户Caps Lock键被按下之类。   QStatusBar继承自QWidget,因此它可以添加其他的QWidget。下面我们在QStatusBar上添加一个QLabel。   首先在class的声明中添加一个私有的QLabel属性:   private:
                  QAction *openAction;
                  QLabel *msgLabel;   然后在其构造函数中添加:           msgLabel = new QLabel;
                  msgLabel->setMinimumSize(msgLabel->sizeHint());
                  msgLabel->setAlignment(Qt::AlignHCenter);

                  statusBar()->addWidget(msgLabel);   这里,第一行创建一个QLabel的对象,然后设置最小大小为其本身的建议大小——注意,这样设置之后,这个最小大小可能是变化的——最后设置显示规则是水平居中(HCenter)。最后一行使用statusBar()函数将这个label添加到状态栏。编译运行,将鼠标移动到工具条或者菜单的 QAction上,状态栏就会有相应的提示:     看起来是不是很方便?只是,我们很快发现一个问题:当没有任何提示时,状态栏会有一个短短的竖线:     这是什么呢?其实,这是QLabel的边框。当没有内容显示时,QLabel只显示出自己的一个边框。但是,很多情况下我们并不希望有这条竖线,于是,我们对statusBar()进行如下设置:   statusBar()->setStyleSheet(QString("QStatusBar::item{border: 0px}"));   这里先不去深究这句代码是什么意思,简单来说,就是把QStatusBar的子组件的border设置为0,也就是没有边框。现在再编译试试吧!那个短线消失了!   QStatusBar右下角的大小控制点可以通过setSizeGripEnabled()函数来设置是否存在,详情参见API文档。   好了,现在,我们的状态栏已经初步完成了。由于QStatusBar可以添加多个QWidget,因此,我们可以构建出很复杂的状态栏。





          Qt学习之路(15): Qt标准对话框之QFileDialog 


          《Qt学习之路》已经写到了第15篇,然而现在再写下去却有点困难,原因是当初并没有想到会连续的写下去,因此并没有很好的计划这些内容究竟该怎样去写。虽然前面说过,本教程主要线路参考《C++ Gui Programming with Qt 4, 2nd Edition》,然而最近的章节由于原文是一个比较完整的项目而有所改变,因此现在不知道该从何写起。   我并不打算介绍很多组件的使用,因为Qt有很多组件,各种组件用法众多,根本不可能介绍完,只能把API放在手边,边用边查。所以,对于很多组件我只是简单的介绍一下,具体用法还请自行查找(确切地说,我知道的也并不多,很多时候还是要到API里面去找)。   下面还是按照我们的进度,从Qt的标准对话框开始说起。所谓标准对话框,其实就是Qt内置的一些对话框,比如文件选择、颜色选择等等。今天首先介绍一下QFileDialog。   QFileDialog是Qt中用于文件打开和保存的对话框,相当于Swing里面的JFileChooser。下面打开我们前面使用的工程。我们已经很有先见之明的写好了一个打开的action,还记得前面的代码吗?当时,我们只是弹出了一个消息对话框(这也是一种标准对话框哦~)用于告知这个信号槽已经联通,现在我们要写真正的打开代码了!   修改MainWindow的open函数:   void MainWindow::open()
          {
                  QString path = QFileDialog::getOpenFileName(this, tr("Open Image"), ".", tr("Image Files(*.jpg *.png)"));
                  if(path.length() == 0) {
                          QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files."));
                  } else {
                          QMessageBox::information(NULL, tr("Path"), tr("You selected ") + path);
                  }
          }   编译之前别忘记include QFileDialog哦!然后运行一下吧!点击打开按钮,就会弹出打开对话框,然后选择文件或者直接点击取消,会有相应的消息提示。   QFileDialog提供了很多静态函数,用于获取用户选择的文件。这里我们使用的是getOpenFileName(), 也就是“获取打开文件名”,你也可以查看API找到更多的函数使用。不过,这个函数的参数蛮长的,而且类型都是QString,并不好记。考虑到这种情况,Qt提供了另外的写法:           QFileDialog *fileDialog = new QFileDialog(this);
                  fileDialog->setWindowTitle(tr("Open Image"));
                  fileDialog->setDirectory(".");
                  fileDialog->setFilter(tr("Image Files(*.jpg *.png)"));
                  if(fileDialog->exec() == QDialog::Accepted) {
                          QString path = fileDialog->selectedFiles()[0];
                          QMessageBox::information(NULL, tr("Path"), tr("You selected ") + path);
                  } else {
                          QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files."));
                  }   不过,这两种写法虽然功能差别不大,但是弹出的对话框却并不一样。getOpenFileName()函数在Windows和MacOS X平台上提供的是本地的对话框,而QFileDialog提供的始终是Qt自己绘制的对话框(还记得前面说过,Qt的组件和Swing类似,也是自己绘制的,而不都是调用系统资源API)。   为了说明QFileDialog::getOpenFileName()函数的用法,还是先把函数签名放在这里:   QString QFileDialog::getOpenFileName (           QWidget * parent = 0,           const QString & caption = QString(),           const QString & dir = QString(),           const QString & filter = QString(),           QString * selectedFilter = 0,           Options options = 0 )   第一个参数parent,用于指定父组件。注意,很多Qt组件的构造函数都会有这么一个parent参数,并提供一个默认值0;   第二个参数caption,是对话框的标题;   第三个参数dir,是对话框显示时默认打开的目录,"." 代表程序运行目录,"/" 代表当前盘符的根目录(Windows,Linux下/就是根目录了),也可以是平台相关的,比如"C:\\"等;   第四个参数filter,是对话框的后缀名过滤器,比如我们使用"Image Files(*.jpg *.png)"就让它只能显示后缀名是jpg或者png的文件。如果需要使用多个过滤器,使用";;"分割,比如"JPEG Files(*.jpg);;PNG Files(*.png)";   第五个参数selectedFilter,是默认选择的过滤器;   第六个参数options,是对话框的一些参数设定,比如只显示文件夹等等,它的取值是enum QFileDialog::Option,每个选项可以使用 | 运算组合起来。   如果我要想选择多个文件怎么办呢?Qt提供了getOpenFileNames()函数,其返回值是一个QStringList。你可以把它理解成一个只能存放QString的List,也就是STL中的list。   好了,我们已经能够选择打开文件了。保存也是类似的,QFileDialog类也提供了保存对话框的函数getSaveFileName,具体使用还是请查阅API。





          Qt学习之路(16): Qt标准对话框之QColorDialog


          继续来说Qt的标准对话框,这次说说QColorDialog。这是Qt提供的颜色选择对话框。   使用QColorDialog也很简单,Qt提供了getColor()函数,类似于QFileDialog的getOpenFileName(),可以直接获得选择的颜色。我们还是使用前面的QAction来测试下这个函数:           QColor color = QColorDialog::getColor(Qt::white, this);
                  QString msg = QString("r: %1, g: %2, b: %3").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()));
                  QMessageBox::information(NULL, "Selected color", msg);   不要忘记include QColorDialog哦!这段代码虽然很少,但是内容并不少。   第一行QColorDialog::getColor()调用了QColorDialog的static函数getColor()。这个函数有两个参数,第一个是QColor类型,是对话框打开时默认选择的颜色,第二个是它的parent。   第二行比较长,涉及到QString的用法。如果我没记错的话,这些用法还没有提到过,本着“有用就说”的原则,尽管这些和 QColorDialog毫不相干,这里还是解释一下。QString("r: %1, g: %2, b: %3")创建了一个QString对象。我们使用了参数化字符串,也就是那些%1之类。在Java的properties文件中,字符参数是用{0}, {1}之类实现的。其实这都是一些占位符,也就是,后面会用别的字符串替换掉这些值。占位符的替换需要使用QString的arg()函数。这个函数会返回它的调用者,因此可以使用链式调用写法。它会按照顺序替换掉占位符。然后是QString::number()函数,这也是QString的一个 static函数,作用就是把int、double等值换成QString类型。这里是把QColor的R、G、B三个值输出了出来。关于QString 类,我们会在以后详细说明。   第三行就比较简单了,使用一个消息对话框把刚刚拼接的字符串输出。   现在就可以运行这个测试程序了。看上去很简单,不是吗?   QColorDialog还有一些其他的函数可以使用。   QColorDialog::setCustomColor()可以设置用户自定义颜色。这个函数有两个值,第一个是自定义颜色的索引,第二个是自定义颜色的RGB值,类型是QRgb,大家可以查阅API文档来看看这个类的使用,下面只给出一个简单的用发:   QColorDialog::setCustomColor(0, QRgb(0x0000FF));   getColor()还有一个重载的函数,签名如下:   QColorDialog::( const QColor & initial, QWidget * parent, const QString & title, ColorDialogOptions options = 0 )   第一个参数initial和前面一样,是对话框打开时的默认选中的颜色;   第二个参数parent,设置对话框的父组件;   第三个参数title,设置对话框的title;   第四个参数options,是QColorDialog::ColorDialogOptions类型的,可以设置对话框的一些属性,如是否显示Alpha值等,具体属性请查阅API文档。特别的,这些值是可以使用OR操作的。   QColorDialog相对简单一些,API文档也很详细,大家遇到问题可以查阅文档的哦!





          Qt学习之路(tip): parent参数


          这是一篇很简单的文章,仅仅是用来说明一下一个参数的作用,因此我把它写成了tip,而不是接下来的17.   程序写的多了,你会发现几乎所有的Qt类的构造函数都会有一个parent参数。这个参数通常是QObject* 或者是 QWidget* 类型的。很多情况下它都会有一个初始值0,因此,即便你不去给它复制也没有丝毫的问题。于是,稍微偷懒一下,就会不自觉的忽略了这个参数。那么,这个参数到底是干什么用的呢?   其实,这个参数有很多用处。就像它的名字一样,这个参数指定了组件的父组件。对于一个对话框来说,对话框一般是不作为顶层容器出现的,因此在任务栏上一般是没有对话框的位置的。怎么指定这个对话框不是顶层容器呢?有父组件的组件不就不是顶层容器了吗?因此,只要你指定对话框的parent属性,任务栏就不会出现它的身影。当然,如果你不指定,这个对话框就成为顶层容器了,任务栏会给它留个位置的——利用这个特性,就可以实现特殊对话框可以在任务栏出现的效果,比如“关于”对话框的出现。   另外比较通用,也是很重要的作用是,parent参数指明了组件的父组件,这样,当父组件delete时,Qt可以保证所有子组件——也就是 parent指针指向这个组件的所有组件——都会被正确的delete掉。这是Qt能够帮助我们管理一部分内存的原因所在。Qt是通过遍历parent属性来防止了这一部分内存泄漏的。因此,必要情况下还是不要忘记设置这个parent属性。当然,如果你不声明这个属性,当整个程序关闭时,操作系统会回收内存——因此我们所说的内存泄漏一般是指我们自己写的应用程序的内部,而不会影响到整个操作系统——当然,如果你实现太可恶,操作系统也会受不了自动关掉你的程序的:-)



          Qt学习之路(17): Qt标准对话框之QMessageBox


          好久没有更新博客,主要是公司里面还在验收一些东西,所以没有及时更新。而且也在写一个基于Qt的画图程序,基本上类似于PS的东西,主要用到的是Qt Graphics View Framework。好了,现在还是继续来说说Qt的标准对话框吧!   这次来说一下QMessageBox以及类似的几种对话框。其实,我们已经用过QMessageBox了,就在之前的几个程序中。不过,当时是大略的说了一下,现在专门来说说这几种对话框。   先来看一下最熟悉的QMessageBox::information。我们在以前的代码中这样使用过:   QMessageBox::information(NULL, "Title", "Content", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);   下面是一个简单的例子:     现在我们从API中看看它的函数签名:   static StandardButton QMessageBox::information ( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton );   首先,它是static的,所以我们能够使用类名直接访问到(怎么看都像废话…);然后看它那一堆参数,第一个参数parent,说明它的父组件;第二个参数title,也就是对话框的标题;第三个参数text,是对话框显示的内容;第四个参数buttons,声明对话框放置的按钮,默认是只放置一个OK按钮,这个参数可以使用或运算,例如我们希望有一个Yes和一个No的按钮,可以使用QMessageBox::Yes | QMessageBox::No,所有的按钮类型可以在QMessageBox声明的StandarButton枚举中找到;第五个参数defaultButton就是默认选中的按钮,默认值是NoButton,也就是哪个按钮都不选中。这么多参数,豆子也是记不住的啊!所以,我们在用QtCreator写的时候,可以在输入 QMessageBox::information之后输入(,稍等一下,QtCreator就会帮我们把函数签名显示在右上方了,还是挺方便的一个功能!   Qt提供了五个类似的接口,用于显示类似的窗口。具体代码这里就不做介绍,只是来看一下样子吧!   QMessageBox::critical(NULL, "critical", "Content", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);     QMessageBox::warning(NULL, "warning", "Content", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);     QMessageBox::question(NULL, "question", "Content", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);     QMessageBox::about(NULL, "About", "About this application");     请注意,最后一个about()函数是没有后两个关于button设置的按钮的!   QMessageBox对话框的文本信息时可以支持HTML标签的。例如:   QMessageBox::about(NULL, "About", "About this application");   运行效果如下:     如果我们想自定义图片的话,也是很简单的。这时候就不能使用这几个static的函数了,而是要我们自己定义一个QMessagebox来使用:   QMessageBox message(QMessageBox::NoIcon, "Title", "Content with icon.");
          message.setIconPixmap(QPixmap("icon.png"));
          message.show();   需要注意的是,同其他的程序类似,我们在程序中定义的相对路径都是要相对于运行时的.exe文件的地址的。比如我们写"icon.png",意思是是在.exe的当前目录下寻找一个"icon.png"的文件。这个程序的运行效果如下:     还有一点要注意,我们使用的是png格式的图片。因为Qt内置的处理图片格式是png,所以这不会引起很大的麻烦,如果你要使用jpeg格式的图片的话,Qt是以插件的形式支持的。在开发时没有什么问题,不过如果要部署的话,需要注意这一点。   最后再来说一下怎么处理对话框的交互。我们使用QMessageBox类的时候有两种方式,一是使用static函数,另外是使用构造函数。   首先来说一下static函数的方式。注意,static函数都是要返回一个StandardButton,我们就可以通过判断这个返回值来对用户的操作做出相应。   QMessageBox::StandardButton rb = QMessageBox::question(NULL, "Show Qt", "Do you want to show Qt dialog?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
          if(rb == QMessageBox::Yes)
          {
                  QMessageBox::aboutQt(NULL, "About Qt");
          }   如果要使用构造函数的方式,那么我们就要自己运行判断一下啦:   QMessageBox message(QMessageBox::NoIcon, "Show Qt", "Do you want to show Qt dialog?", QMessageBox::Yes | QMessageBox::No, NULL);
          if(message.exec() == QMessageBox::Yes)
          {
                  QMessageBox::aboutQt(NULL, "About Qt");
          }   其实道理上也是差不多的。




          Qt学习之路(18): Qt标准对话框之QInputDialog


          这是Qt标准对话框的最后一部分。正如同其名字显示的一样,QInputDialog用于接收用户的输入。QInputDialog提供了一些简单的static函数,用于快速的建立一个对话框,正像QColorDialog提供了getColor函数一样。   首先来看看getText函数:   bool isOK;
          QString text = QInputDialog::getText(NULL, "Input Dialog",
                                                             "Please input your comment",
                                                             QLineEdit::Normal,
                                                             "your comment",
                                                             &isOK);
          if(isOK) {
                 QMessageBox::information(NULL, "Information",
                                                     "Your comment is: " + text + "",
                                                     QMessageBox::Yes | QMessageBox::No,
                                                     QMessageBox::Yes);
          }   代码比较简单,使用getText函数就可以弹出一个可供用户输入的对话框:     下面来看一下这个函数的签名:   static QString QInputDialog::getText ( QWidget * parent,                                                       const QString & title,                                                       const QString & label,                                                       QLineEdit::EchoMode mode = QLineEdit::Normal,                                                       const QString & text = QString(),                                                       bool * ok = 0,                                                       Qt::WindowFlags flags = 0 )   第一个参数parent,也就是那个熟悉的父组件的指针;第二个参数title就是对话框的标题;第三个参数label是在输入框上面的提示语句;第四个参数mode用于指明这个QLineEdit的输入模式,取值范围是QLineEdit::EchoMode,默认是Normal,也就是正常显示,你也可以声明为password,这样就是密码的输入显示了,具体请查阅API;第五个参数text是QLineEdit的默认字符串;第六个参数 ok是可选的,如果非NLL,则当用户按下对话框的OK按钮时,这个bool变量会被置为true,可以由这个去判断用户是按下的OK还是Cancel,从而获知这个text是不是有意义;第七个参数flags用于指定对话框的样式。   虽然参数很多,但是每个参数的含义都比较明显,大家只要参照API就可以知道了。   函数的返回值是QString,也就是用户在QLineEdit里面输入的内容。至于这个内容有没有意义,那就要看那个ok参数是不是true了。   QInputDialog不仅提供了获取字符串的函数,还有getInteger,getDouble,getItem三个类似的函数,这里就不一一介绍。






          Qt 4下连接MySQL数据库

          前天刚装了个Qt4for windows,发现安装的时候MySQL等数据库插件都没有安装,像Qt X11版一样,都需要自己安装这些插件。
              在Qt的安装目录C:\Qt4.1.2\下,有个src目录,进入plugins\sqldrivers\mysql,可以看到两个文件,main.cpp和mysql.pro,很显然要用qmake来完成编译、生成目标。
              编译前,首先保证依赖的MySQL头文件和库文件正确。首先MySQL安装目录下的include中的所有文件拷到qmake时能找到的目录下,接着从 lib/opt目录下拷出libmysql.a,其实lib/opt下只有libmysql.lib和libmysql.dll,从网上搜了一下找到一个方法从libmysql.dll生成libmysql.a:

          1 、安装好MySQL (如果不愿意安装,找个libmySQL.dll文件也可以)

          2、下载Pexports工具

          3、转换操作: pexports libmysql.dll > libmysql.def

          4、使用MinGW的 dlltool转换成为libmysql.a文件。dlltool --input-def libmysql.def --dllname libmysql.dll --output-lib libmysql.a -k

          5、尝试是否成功:
          将生成的libmysql.a 拷到qmake的lib搜索路径下后在plugins\sqldrivers\mysql目录下做:
          qmake -project
          qmake
          make

          如果再出现象mysql_connect@xx (xx是数字)的错误提示。就执行第6步。

          6、修改libmysql.def文件,给mysql_connect加上@xx,即mysql_connect@xx

          7、重做第4步

          8、然后尝试第5步,如果还是出现错误提示。就作第6步。一直到没有错误为止。

          最后C:\Qt4.1.2\plugins\sqldrivers下将会生成需要的libqsqlmysql.a和qsqlmysql.dll。

          找了一个测试文件,通过。
          前天刚装了个Qt4for windows,发现安装的时候MySQL等数据库插件都没有安装,像Qt X11版一样,都需要自己安装这些插件。
              在Qt的安装目录C:\Qt4.1.2\下,有个src目录,进入plugins\sqldrivers\mysql,可以看到两个文件,main.cpp和mysql.pro,很显然要用qmake来完成编译、生成目标。
              编译前,首先保证依赖的MySQL头文件和库文件正确。首先MySQL安装目录下的include中的所有文件拷到qmake时能找到的目录下,接着从 lib/opt目录下拷出libmysql.a,其实lib/opt下只有libmysql.lib和libmysql.dll,从网上搜了一下找到一个方法从libmysql.dll生成libmysql.a:

          1 、安装好MySQL (如果不愿意安装,找个libmySQL.dll文件也可以)

          2、下载Pexports工具

          3、转换操作: pexports libmysql.dll > libmysql.def

          4、使用MinGW的 dlltool转换成为libmysql.a文件。dlltool --input-def libmysql.def --dllname libmysql.dll --output-lib libmysql.a -k

          5、尝试是否成功:
          将生成的libmysql.a 拷到qmake的lib搜索路径下后在plugins\sqldrivers\mysql目录下做:
          qmake -project
          qmake
          make

          如果再出现象mysql_connect@xx (xx是数字)的错误提示。就执行第6步。

          6、修改libmysql.def文件,给mysql_connect加上@xx,即mysql_connect@xx

          7、重做第4步

          8、然后尝试第5步,如果还是出现错误提示。就作第6步。一直到没有错误为止。

          最后C:\Qt4.1.2\plugins\sqldrivers下将会生成需要的libqsqlmysql.a和qsqlmysql.dll。

          找了一个测试文件,通过。
          #include  
          #include  


          bool createConnection(){ 
          QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); 
          db.setHostName("localhost"); 
          db.setDatabaseName("test"); 
          db.setUserName("test"); 
          db.setPassword(""); 

          if(!db.open()){ 
          QMessageBox::critical(0, QObject::tr("Database Error"), 
                                        db.lastError().text()); 

          return false; 


          QSqlQuery query; 
          //query.exec("insert into book values( 3, 'title', 'author') "); 
          query.exec("select title , author from book "); 

          while (query.next()) { 


              QString title = query.value(0).toString(); 
              QString author = query.value(1).toString(); 

          QMessageBox::critical(0, title,author); 
              



          return true; 




          int main(int argc,char** argv){ 

          QApplication app(argc, argv); 
              if (!createConnection()){ 

          return 1; 

                 
              return app.exec(); 

          }



          QT的Graphics View框架与坐标系

          Graphics View提供了一个界面,它既可以管理大数量的定制2D graphical items,又可与它们交互,有一个view widget可以把这些项绘制出来,并支持旋转与缩放。这个柜架也包含一个事件传播结构,对于在scene中的这些items,它具有双精度的交互能力。 Items能处理键盘事件,鼠标的按,移动、释放、双击事件,也可以跟踪鼠标移动。Graphics View使用BSP树来提供对item的快速查找,使用这种技术,它可以实时地绘制大规模场景,甚至以百万items计。Graphics View在Qt 4.2中被引用,它替代了它的前辈QCanvas。 Graphics View的体系结构

          Graphics View提供的是一种类似于Qt model-view的编程。多个views可以监视同一个场景,而场景包含多个具有多种几何外形的items。
          场景
          QGraphicsScene 表示Graphics View中的场景,它有以下职责:
          为管理大量的items提供一个快速的接口。
          传播事件到每个item。
          管理item的状态,例如选择,焦点处理。
          提供未经变换的渲染功能,主要用于打印。
          场景作为QGraphicsItem对象的容器。通过调用QgraphicsScene::addItem()把这些Items加入到场景中。可以使用众多的查找函数来获取特定的items。QGraphicsScene:items()与它的许多重载函数可获取那些与点、矩形,多边形,向量路径等相交或是有包含有关系的items。QGraphicsScene::itemAt()返回特定上最顶端的item。所有的item查找函数都以出栈序列返回(也就是说,第一个返回的是最顶端的,最后一个返回的是最底端的)。
          QGraphicsScene scene;
          QGraphicsRectItem *rect=scene.addRect(QRectF(0,0,100,100));
          QGraphicsItem *item=scene.itemAt(50,50);
          //item==rect;
          QGraphicsScene的事件传播结构会把场景事件投递到items,也管理多个items之间的传递。假如场景收到了鼠标在某个位置press事件,场景会把这个事件投递给处在那个位置的item。QGraphicsScene也管理某种item状态,像选择与焦点。你可以通过调用QGraphicsScene::setSelectionArea()来选择items,它需要提供一个任意的形状为参数。这个函数也作为在QGraphicsView实现橡皮筋选择功能的一个基础。为得到这些已经被选择的items,调用QGraphicsScene::selectedItem()。另一个状态处理是是否一个item拥有键盘输入焦点。你可以调用QGraphicsScene::setFocusItem()或QGraphics::setFocus()来设定焦点,也可用QGraphicsScene::focusItem()来得到当前拥有焦点的那个item。最后,QGraphicsScene允许你通过调用QGraphicsScene::render()函数把部分场景送到绘图设备进行渲染。
          视图
          QGraphicsView提供了视图部件,它可视化场景中的内容。你可以联结多个视图到同一个场景,对这个相同的数据集提供几个视口。视口部件是一个滚动区域,它提供了滚动条以对大场景进行浏览。为了使用OpenGL,你应该调用QGraphicsView::setViewport()来把一个QGLWidget设为视口。视图从键盘,鼠标接收输入事件,在发送这些事件到场景之前,会对这些事件进行适当的翻译(把事件坐标转换成对应的场景坐标)。
          利用转换矩阵,QGraphicsView::matrix(),视图可变换场景的坐标系统。这允许高级的导航特性,如缩放,旋转。为了方便,QGraphicsView也提供了在视图与场景之间进行坐标转换的函数:QGraphicsView::mapToScene(),QGraphicsView::mapForScene()。
          The Item
          QGraphicsItem 是场景中图形items的基类。Graphics View 提供了一些标准的、用于典型形状的items。像矩形(QGraphicsRectItem),椭圆(QGraphicsEllipseItem),文本 (QGraphicsTextItem),当你写定制的item时,那些最有用的一些QGraphicsItem特性也是有效的。除此这外,QGraphicsItem支持以下特性:
          *鼠标按、移动、释放、双击事件,鼠标悬停事件,滚轮事件,弹出菜单事件。
          *键盘输入焦点,键盘事件。
          *拖拽
          *组,包括父子关系,使用QGraphicsItemGroup
          *碰撞检测
          Items如同QGraphicsView一样,位于本地坐标系,它也为item与场景之间,item与item之间的坐标转换提供许多工具函数。而且,也像QGraphicsView一样,它使用矩阵来变换它的坐标系统:QGraphicsItem::matrix()。它对旋转与缩放单个的Item比较有用。
          Items可以包含别的items(孩子)。父items的转换被它的子孙所继承。然而,它的所有函数(也就是,QGraphicsItem::contains(),QGraphicsItem::boundingRect(),QGraphicsItem::collidesWith()),不会积累这些转换,依然在本地坐标下工作。
          QGraphicsItem通过QGraphicsItem::shape(),QGraphicsItem::collideWith())来支持碰撞检测。这两个都是虚函数。从shape()返回你的item的形状(以本地坐标QPainterPath表示),QGraphicsItem会为你处理所有的碰撞检测。假如你想提供自己的碰撞检测,你应该重新实现QGraphicsItem::collideWith()。
            Graphics View 坐标系统
          Graphics View基于笛卡尔坐标系。item在场景中的位置与几何形状通过x,y坐标表示。当使用未经变形的视图来观察场景时,场景中的一个单位等于屏幕上的一个像素。在Graphics View中有三个有效的坐标系统:Item坐标系,场景坐标系,视图坐标系。为了简化你的实现,Graphics View提供了方便的函数,允许三个坐标系之间相互映射。
          当渲染时,Graphics View的场景坐标对应于QPainter的逻辑坐标,视图坐标与设备坐标相同。



          Item坐标
          Items位于它们自己的坐标系中。它的坐标都以点(0,0)为中心点,这也是所有变换的中心点。在item坐标系中的几何图元,经常被称为item点,item线,item矩形。当创建一个定制的item,item坐标是所需要考虑的。QGraphicsScene与QGraphicsView可以为你执行所有转换,这使得实现定制的item变得容易。举例来说,假如你收到鼠标按或是拖进入事件,事件的位置以item坐标的形式给出。QGraphicsItem::contain()虚函数,当某个点的位置在你的item范围内时,返回true,否则返回false。这个点参数使用item坐标,相似地,item的包围矩形与形状也使用item坐标。
          Item位置指的是item的中心点在它父亲的坐标系中的坐标。以这种思想来看,场景指的就是那些祖先最少的item的“父亲”。最上级的Item位置就是在场景中的位置。
          子坐标与父坐标之间是相关的,假如孩子未经变换,子坐标与父坐标之间的差值等于在父坐标系下,父item与子item之间的距离。例如,假如一个未经变换的子item位置与其父item的中心重合,那么这两个item的坐标系统完全相同。如果孩子的位置是(10,0),那么孩子坐标系中的(0,10)点,对应于父坐标系中的(10,10)点。
          因为item的位置与变换是相对于父item的,子item的坐标不会被父亲的变换影响,尽管父item的变换隐含地对子item做了变换。在上面的例子中,即使父item旋转,缩放,子item的(0,10)点依然对应于父item的(10,10)点。然而,相对于场景来讲,子item会遵循父item的变换。假如父item被缩放(2X,2X),子item的位置在场景中的坐标是(20,0),它的(10,0)点则与场景中的(40,0)对应。除了QGraphicsItem::pos(),QGraphicsItem的函数以Item坐标工作,如一个item's包围矩形总是以item坐标的形式给出。 场景坐标
          场景坐标系统描述了每个最顶级item的位置,也是从视图向场景投递场景事件的基础。场景中的每个item有场景位置与包围矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect()), 另外,它有自己本地item位置与包围矩形。场景位置描述了item在场景坐标下的位置,它的场景包围矩形则用于QGraphicsScene决定场景中哪块区域发生了变化。场景中的变化通过QGraphicsScene::changed()信号来通知,它的参数是场景矩形列表。 视图坐标
          视图坐标是widget 的坐标,视图坐标中每个单位对应一个像素。这种坐标的特殊之处在于它是相对于widget或是视口的,不会被所观察的场景所影响。QGraphicsView 的视口的左上角总是(0,0),右下角总是(视口宽,视口高)。所有的鼠标事件与拖拽事件,最初以视图坐标表示,就应该把这些坐标映射到场景坐标以便与 item交互。 坐标映射
          经常,处理场景中item时,在场景与item之间,item与item之间,视图与场景之间进行坐标映射,形状映射是非常有用的。举例来讲,当你在QGraphicsView的视口中点击鼠标时,你应该通过调用QGraphicsView::mapToScence()与QGraphicsScene::itemAt()来获知光标下是场景中的哪个item。假如你想获知一个item位于视口中的什么位置,你应该先在item上调用QGraphicsItem::mapToScene(),然后调用QGraphicsView::mapFromScene()。最后,假如你想在一个视图椭圆中有哪些items,你应该把QPainterPath传递到mapToScene(),然后再把映射后的路径传递到QGraphicsScene::items()。
          你可以调用QGraphicsItem::mapToScene()与QGraphicsItem::mapFromScene()在item与场景之间进行坐标与形状的映射。也可以在item与其父item之间通过QGraphicsItem::mapToParent()与QGraphicsItem::mapFromItem()进行映射。所有映射函数可以包括点,矩形,多边形,路径。视图与场景之间的映射也与此类似。对于从视图与item之间的映射,你应该首先映射到场景,然后再从场景向item进行映射。   关键特性

          缩放与旋转
          QGraphicsView通过QGraphicsView::setMatrix()支持同QPainter一样的仿射变换,通过对一个视图应用变换,你可以很容易地支持普通的导航特性如缩放与旋转。下面是一个例子:
          class View:;public QGraphicsView
          {
          Q_OBJECT
          //.....
          public slots:
          void zoomIn() {scale(1.2,1.2);}
          void zoomOut() {scale(1/1.2,1/1.2);}
          void rotateLeft() {rotate(-10);}
          void rotateRight() {rotate(10);}
          };
          这些槽应与QToolButtons联接,并使autoRepeat有效。当对视图变换时,QGraphicsView会对视图中心进行校正。 拖拽
          因为 QGraphicsView继承自 QWidget,它也提供了像QWidget那样的拖拽功能,另处,为了方便,Graphics View柜架也为场景,每个item提供拖拽支持。当视图接收到拖拽事件,它可翻译为QGraphicsSceneDragDropEvent,再发送到场景。场景接管这个事件,把它发送到光标下接受拖拽的第一个item。
          从一个item开始拖拽时,创建一个QDrag对象,传递开始拖拽的那个 widget的指针。Items可以同时被多个视图观察,但只有一个视图可以开始拖拽。拖拽在多数情况下是从按下鼠标或是移动鼠标开始的,因此,在 mousePressEvent()或mouseMoveEvent()中,你可以从事件中得到那个原始的widget指针,例如:
          void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
          {
          QMimeData *data=new QMimeData;
          data->setColor(Qt::green);
          QDrag *drag=new QDrag(event->widget());
          drag->setMimeData(data);
          drag->start();
          }
          为了在场景中载取拖拽事件,你应重新实现QGraphicsScene::dragEnterEvent()和在QGraphicsItem的子类里任何与你特定场景需要的事件处理器。items也可以通过调用QGraphicsItem::setAcceptDrops()获得拖拽支持,为了处理将要进行的拖拽,你需要重新实现 QGraphicsItem::dragEnterEvent(),QGraphicsItem::dragMoveEvent(),QGraphicsItem::dragLeaveEvent() 和QGraphicsItem::dropEvent()。
          光标与工具提示
          像QWidget一样,QGraphicsItem也支持光标(QgraphicsItem::setCursor)与工具提示(QGraphicsItem::setToolTip())。当光标进入到 item的区域,光标与工具提示被QGraphicsView激活(通过调用QGraphicsItem::contains()检测)。你也可以直接在视图上设置一个缺省光标(QGraphicsView::setCursor)。
          动画
          Graphics View支持几种级别的动画。你可以很容易地通过把QGraphicsItemAnimatoin与你的item联结来
          装配出动画路径,这允许以时间线来控制动画,在所有平台上以稳定的速率运作。QGraphicsItemAnimation允许你为item的位置,旋转,缩放,剪切,变换等产生一条路径,动画可以用QSlider来控制,或更为普遍使用的QTimeLine。
          另一种是从QObject和QGraphicsItem继承,item可以设置自己的定时器,以在QObject::timeEvent()中增加步进的方式来控制动画。
          第三种,是通过调用QGraphicsScene::advance()来推进场景,它又依次调用QGraphicsItem::advance().
          OpenGL渲染
          为了使用OpenGL渲染,你要设置一个新的QGLWidget作为QGraphicsView的视口:QGraphicsView::setViewPort()。假如你让OpenGL提供反锯齿功能,你需要OpenGL采样缓冲支持。
          QGraphicsView view(&scene);
          view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
          Item组
          通过把一个item做为另一个item的孩子,你可以得到item组的大多数本质特性:这些items会一起移动,所有变换
          会从父到子传递。QGraphicsItem也可以为它的孩子处理所有的事件,这样就允许以父亲代表它所有的孩子,可以有效地把所有的items看作一个整体。
          另外,QGraphicsItemGroup是一个特殊的item,它既对孩子事件进行处理又有一个接口把items从一个组中增加和删除。把一个item加到
          QGraphicsItemGroup仍会保留item的原始位置与变换,而给一个item重新指定父item则会让item根据其新的父亲重新定位。可以用QGraphicsScene::createItemGroup()建组。



          Qt中文编码


          【Qt 编码简单实验】         首先,Qt中得QString 类对字符串进行了封装,其内部使用Unicode对传入的串进行编码。这样一来,QString就可以处理绝大多数的国际语言。将QString中的字符根据语言翻译的过程,也就是Qt 的Translater针对程序中使用含有的tr("XXXXX"),进行翻译的过程。由于QString的Unicode编码,和本地系统的编码不一定是一致的(比如系统采用的GB2312的编码)。这样的话,就不能直接使用类似QString str("汉字")这样的方法来存储本地的汉字,是有问题的。     <系统是使用GB2312编码的>     【试验1】        QString str("汉字");        std::cout << "Straight Output:" << str << endl;        std::cout << "Local Output:" << str.local8Bit() << endl;        std::cout << "Unicode Output:" << str.unicode() << endl;        结果如下:        汉字       (正确)        @#$%    (乱码)        @#$%    (乱码)     【试验2】        QString str = QString::fromLocal8Bit("汉字");        std::cout << "Straight Output:" << str << endl;        std::cout << "Local Output:" << str.local8Bit() << endl;        std::cout << "Unicode Output:" << str.unicode() << endl;              结果如下:              @#$%    (乱码)               汉字       (正确)        @#$%    (乱码)      首先说试验1,因为str采用Unicode编码,中文实际上没有经过任何的编码转换直接存到str中,所以存入的Unicode已经是错误的(GB编码的字符按照Unicode存的)。但是为什么第一个会正常显示呢?因为标准输入输出是不进行任何的编码解码工作的,字符串由本地系统读取时使用本地的字符集GB2312进行解码,因为存入的字符串“汉字”正好是GB2312编码的,正好得到了正确地结果!这有点负负得正的味道!QString只是充当了一个容器,里面存的是不正确的值。     对于试验2来说,使用fromLocal8Bit()函数,实现了从本地字符集GB到Unicode的转换,所以存在QString中的字符串是经过转换的正确编码。输出的时候,要正确显示,只能是再转为本地的字符编码,也就是使用local8Bit()转换。由于存入QString的是正确的值,就可以进行包括国际化在内的许多工作!( 注意本地LANGUAGE环境变量!) 【Qt国际化的问题】      在文本显示上,Qt 使用了Unicode 作为内部编码,为了程序的国际化,通常我们在文本显示的地方不直接输入本地字符,用英文代替,比如要编写一中文界面的 Qt 程序,应该在程序中使用英文,程序编写完成后,把文本提取出来翻译。对于需要翻译的地方,首先是在该文本处用tr()函数标识,同时制作出.qm信息文件,并在程序中加入QTranslator即可。      比如我们在某一程序中有如下语句: setCaption(tr(“main window”)) 为了显示中文,有两种方法: 方法一:         1. 修改工程文件,加上TRANSLATIONS = xxx.ts         2. lupdate 工程文件名         3. 用linguist编辑刚生成的xxx.ts文件并保存         4. lrelease 工程文件名 xxx.qm         5. 在main.cpp中加入QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);         6. qApp->setFont(font1);         7. QTranslator *translator = new QTranslator(0);         8. translator->load("xxx.qm",".");         9. qApp->installTranslator(translator); 方法二:         1. findtr 文件名(通常为CPP文件) > xxx.po         2. 编辑po文件,其中charset需由iso-8859-1改为GB2312,然后将“main window”翻译成“主窗口”         3. msg2qm –scope zh_CN.GB2312 xxx.po xxx.qm         4. 在main.cpp中加入QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);         5. qApp->setFont(font1);         6. QTranslator *translator = new QTranslator(0);         7. translator->load("xxx.qm",".");         8. qApp->installTranslator(translator); 方法三:      有时我们只是提供给本地用户使用,无需国际化,QT提供这一支持,在QT中有许多本地字符集同unicode的转换引擎,他们皆为QTextCodec的派生类,如QGbkCodec、QJisCodec, QHebrewCodec等。如:         QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);         qApp->setFont(font1);         QString caption=“主窗口“;         QTextCodec *gk_codec=QTextCodec::codecForName(“GBK”);         setCaption(gk_codec->toUnicode(caption));        从上面可以看出,使用转换引擎可以轻松实现中文显示,简要步骤如下:        1. 修改main.cpp文件,将字体改为unifont               QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);               qApp->setFont(font1);        2. 在想汉化的内的头文件中加入QTextCodec指针变量和转换函数QString mytr(char *)        #include        QTextCodec* gbk;        QString mytr(const char *);       3. 在想汉化的类的实现文件中,修改类构造函数,加入:               gbk=QTextCodec::codecForName(“GBK”);        4. 在想汉化的类的实现文件中,添加mytr函数代码               QString Form1::mytr(const char* chars) {                return gbk->toUnicode(chars,strlen(chars));               }        5. 在想汉化的类的实现文件中,用“mytr”替换“tr”    注:如果将codec成员变量改成QTextCodec派生类变量,编译将通不过,比如将QTextCodec* gbk;改成QGbkCodec* gbk;编译将报告此处有语法错误。 下面是相似的用法:         1. 修改***.cpp文件,在顶部加入codec头文件                  #include         2. 在***.h文件中,加入mytr()函数声明                  QString mytr(char* buffer,int size);         3. 在***.cpp文件中,加入mytr()定义                  QString mytr(char* buffer,int size) {                      QGbkCodec* gbk=QTextCodec::codeForName(“GBK”);                      return gbk->toUnicode(buffer,size);                   }         4. 在需要显示中文的地方,使用mytr函数即可         5. 修改main.cpp文件,将字体改为unifont                      QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);                      qApp->setFont(font1); 备注1:在翻译或转换之前必须将Unicode字体调入,否则显示不出中文,网上相关文章并未提及这一点,如果不显式装载该字体,系统默认的是Latin1,于是汉字显不出来。     备注2:在编译qt/embedded之前,必须修改qconfig-qpe.h配置文件的内容,将与TextCodec相关的宏定义给去掉,否则QTextCodec::codecForName(“GBK”)将返回NULL指针。 备注3:使用findtr命令时可同时查找多个文件的tr(),并将查找结果都放入一个文件内,源文件以空格隔开即可,另外,生成的.po和.qm文件的文件名最好与工程文件名相同!     备注4:如果要显示繁体中文,则需要使用QTextCodec::codecForName(“big5”)。获取本地的使用语言,用QTextCodec::locale(),它返回Qstring变量,通常如果是中文本地的话,通常其值为zh_CN.GB2312和zh_TW.Big5,根据这个返回字符串,可以加载相应的codec。如果程序只支持一种编码,也可以直接把整个应用程序的编码设置为一个默认的编码标准,比如系统只需要显示中文和英文,则可以直接设置应用程序的默认编码标准是GBK,如下使用方法:        qApp->setDefaultCodec( QTextCodec::codecForName("GBK") );        QLabel *label = new QLabel( tr("中文标签") );    备注5:如果使用本地的字符转换器,可以使用Qstring的静态函数Qstring::fromLocal8Bit(char* buffer,int size),将本地字符串转换成UNICODE字符串,不过要设置好LANGUAGE环境变量。 【QTOpia中文化 】 1) findtr 文件名 > xxx.po 2) 编辑xxx.po文件 3) msg2qm –scope zh_CN.GB2312 xxx.po xxx.qm 4) 拷贝可执行文件到QPEDIR/bin目录 5) 拷贝xxx.po和xxx.qm文件到QPEDIR/i18n/zh_CN目录 6) 进入QPEDIR/apps/Applications目录创建一新.desktop文件 7) iconv –f utf8 –t GB18030 xxx.desktop > xxx1.desktop 8) 编辑xxx1.desktop文件,主要是修改Exec、Icon、Name和Name[zh_CN]四项 9)  iconv –f GB18030 –t utf8 xxx1.desktop > xxx.desktop 10) rm –f xxx1.desktop 11) qvfb –depth 16 & 12) cd $QPEDIR/bin 13) ./qpe      备注1:如果你的系统中有多个qtopia版本,要特别注意QTDIR、QPEDIR、LD_LIBRARY_PATH环境变量      备注2:可按照此方法汉化qtopia自带的应用程序    备注3:po文件是中间文件,程序真正需要的是qm文件。iconv是系统自带的内码转换工具,它能将utf8编码的文件转换成gb18030编码的文件,反之也能,转换这一步必不可少,因为desktop文件缺省是utf8编码的,而我们的redhat linux 7。3中文操作系统用的却是gb18030,所以在编辑器打开前需转换。 Qt 目前的版本(2.2.4)对国际化的支持已经相当完善。 在文本显示上,Qt 使用了Unicode 作为内部编码,可以同时支持多种编码。 为 Qt 增加一种编码的支持也比较方便,只要 增加该编码和Unicode的转换编码便可以了。 Qt 目前支持ISO标准编码ISO 8859-1, ISO 8859-2,ISO 8859-3,ISO 8859-4,ISO 8859-5,ISO 8859-7,ISO 8859-9,和 ISO 8859-15(对于阿拉伯语和希伯来语的支持正在开发之中),中文GBK/Big5,日文 eucJP/JIS/ShiftJIS,韩文eucKR,俄文KOI8-R。 当然也可以直接使用UTF8编码。

          Qt 使用了自己定义的Locale机制,在编码支持和信息文件(Message File)的翻译上弥补了目前Unix上所普遍采用Locale和gettext的不足之处。 Qt 的这种机制可以使 Qt 的同一组件(QWidget)上同时显示不同编码的文本。 比如,Qt 的标签上可以同时使用中文简体 和中文繁体文本。

          在文本输入上,Qt 采用了XIM(X Input Method)标准协议,可以直接使用XIM输入服务器。由于目前的绝大多数输入服务器都是针对单一语言的,所以在 Qt 的标准输入组件( QLineEdit,QMultiLineEdit)中的输入受到单一编码的限制,Qt 还不支持动态切换编码 输入的支持,这是它的不足之处。

          1. Qt 的文本显示
          使用 Qt 编写国际化的程序,最好不要在程序中直接使用特殊编码的文本。 比如要 编写一中文界面的 Qt 程序,应该在程序中使用英文,程序编写完成后,把文本提取 出来翻译。 这样,程序还可以根据Locale的不同,支持多种语言。 下面介绍如何在 Qt 程序中标注字符串,如何提取并翻译文本。

          像普通的国际化过程一样,Qt 使用了类似GNU gettext一样的函数 QObject::tr(),它 用于从Qt的信息文件 .qm 中取出信息,这些信息是经过 Qt 的工具处理的。 Qt在处理 编码时还使用了 QTranslator 类,可用于指定整个应用软件的 的信息文件。

          下面是一段使用了 QObject::tr()的代码,它建立了一个弹出菜单,菜单项是"Quit", 它被放置在菜单条上,在菜单条上显示的是标签"File"。

          QPopupMenu* popup;
          popup = new QPopupMenu( this );
          popup->insertItem( tr("&Quit"),qApp,SLOT(quit()) );
          menubar->insertItem( tr("&File"),popup );

          对于绝大多数情况,可以用上述方法处理。不过有时在定义某些变量中使用的字符 串,不能使用上述方法,但是为了让Qt提取并翻译该字符串,必须用某种方法标志出 来。Qt 定义了 QT_TR_NOOP() 和 QT_TRANSLATE_NOOP() 来标志它们。前者用于单个字符串,后者用于多个字符串。比如,

          static const char* strings = {
          QT_TR_NOOP( "Hello" ),
          QT_TR_NOOP( "World" )
          };

          有时需要使用printf/sprintf之类的函数动态生成字符串,比如,

          QStings s;
          s.sprintf( "Button %d",i );
          but->setText( s );

          对这种使用方式的国际化是使用 arg() 函数。

          QString s = tr( "Button %1" ).arg(i);
          but->setText( s );

          提取上述信息的方法是使用 Qt 提供的工具 findtr 命令:

          findtr .cpp > i18n.po

          它类似于GNU的 xgettext,上述文件的提取信息文件内包含,

          ....
          "Content-Type: text/plain; charset=iso-8859-1\n"

          #: i18n.cpp:34
          msgid "ExampleWidget::&File"
          msgstr ""
          ...

          接下来是文本翻译过程。 在Qt中翻译信息文件时应该注意以下事项: (1) 提取的信息文件的编码是iso-8859-1,在翻译成某种语言(编码)时应该 注意改动它的 字符集,比如对中文GB2312和Big5编码,应该是, "Content-Type: text/plain; charset=gb2312\n"或者"Content-Type: text/plain; charset=big5\n"。 (2) 提取的信息有一个范围,比如上面的文件指定的范围是 ExampleWidget, 在翻译 前应该把它去掉,变成 msgid "::&File"。(3) 被翻译的字符串可能含有加速键符号,如 "&File"中的"F",如果翻译成中文最好保留该信息,它可以翻译成 "文件(&F)"。

          对于翻译后的文件(比如上面的翻译文件存为 i18n_gb.po),必须使用 Qt 提供的 工具 msg2qm 把它转换为 .qm 文件才能使用,

          > msg2qm i18n_gb.po i18n_gb.qm

          它类似于GNU的 msgfmt 命令。翻译后的文件可以用Qt程序直接调用。

          QTranslator *translator = new QTranslator(0);
          translator->load("i18n_gb.qm",".");
          qApp->installTranslator(translator);

          此外,Qt 还提供了类似于 msgmerge 的工具 mergetr,它用于把新提取的信息 文件和已经翻译过的信息文件融合起来,在此不再赘述。

          在 Qt 中也可以直接使用 QTextCodec 来转换字符串的编码,这为在Qt下开发纯 中文软件带来了便利条件,不过这种方法不符和国际化/本地化的习惯,

          char *string = "中文和English混和字符串!"
          QTextCodec* gbk_codec = QTextCodec::codecByName("GBK");
          QString gbk_string = codec->toUnicode(string);
          QLabel *label = new QLabel(gbk_string);

          如果使程序只支持一种编码,也可以直接把整个应用程序的编码设置为GBK编码, 然后在字符串之前 加tr(QObject::tr),

          qApp->setDefaultCodec( QTextCodec::codecForName("GBK") );
          QLabel *label = new QLabel( tr("中文标签") );

          如果使Qt根据Locale的环境变量取得字符集,可以使用

          QString::fromLocal8Bit(str)。

          本节的例子请参见 qt-i18n-example.tar.gz

          2. Qt 的文本输入
          在输入方面,Qt 的输入条(QLineEdit)和编辑区(QMultiLineEdit)都支持 XIM,只要配合相应的输入服务器,便可以输入中文/日文/韩文。目前有许多支持XIM的软件,比如 中文: Chinput/xcin/rfinput/q9,日文: kinput2/skkinput,韩文: ami/hanIM。

          Qt程序的缺省输入风格是OverTheSpot风格,它也支持 OffTheSpot风格和 Root风格。 用户可以在起动程序时在命令行指定输入风格,比如对程序app,


          ./app -inputstyle overthespot  #缺省风格,光标跟随
          ./app -inputstyle offthespot
          ./app -inputstyle root

          经过 MiziLinux 补丁的Qt-2.2.0 支持 OnTheSpot 输入风格,并且把它作为 缺省的输 入风格。请参见 http://www.mizi.com/ko/kde/doc/onthespot/onthespot.html。

          Qt 中的任何一个 Widget 都可以接受输入,只要它可以有键盘聚焦(Keyboard Focus)。所以对特殊 Widget 的输入处理只需要截获键盘输入,获取从XIM服务器 来的字符串。 对于OverTheSport风格的支持,刷新XIM输入服务器的位置即可。

          3. Qt 的打印
          在打印方面,XWindow下的 Qt 生成PostScript并使用lpr打印。 它含有QPrinter类, 可以方便地支持输出页面的控制。 对于中文打印,必须修正PostScript文件的输出 部分。本文出自 51CTO.COM技术博客





















          网易历六年耗亿资打造,3D国韵网游《天下贰》,免费领光盘


          看陆川杨幂新片《琴棋书画》,品网易3D国韵网游《天下贰》


          网易历六年耗亿资打造,3D国韵网游《天下贰》,免费领光盘 0人  |  分享到:          阅读(792)| 评论(0)| 引用 (0) |举报   人生路上—— 决定一生的简单法则   Fw:Qt4编程环境的建立

          历史上的今天

          相关文章

          • Qt四舍五入取模取余操作
          • 在ubuntu8.10下qt开发环境搭建
          • 关于Qt
          • qt杂记
          • QT(4)弹出菜单

          最近读者

          登录后,您可以在此留下足迹。 爱了就别 lhyww yinww200 carlward shdtjz20 niuhong3 echnit changwor

          评论

          点击登录|昵称:
          QT学习道路 - 陈百虎的日志 - 网易博客 道路驾驶考试技巧 - 猪不戒的日志 - 网易博客# 世界各地成为风景线的道路[40P] - AAA级私秘视觉馆的日志 - 网易博客 世界道路之“最”(令人惊叹) - 渴望美好的日志 - 网易博客 英语语法学习口诀 - 博元的日志 - 网易博客 高中生学习策略(高一高二高三) - 金雕的日志 - 网易博客 英语语法学习口诀 - 博元的日志 - 网易博客4 培养一点"领袖气质" - 职业经理学习的日志 - 网易博客 沉着应付办公室小人 - 职业经理学习的日志 - 网易博客 做人做事保存实力 - 职业经理学习的日志 - 网易博客 做人讲究外圆内方 - 职业经理学习的日志 - 网易博客 学习表格设置做图框 - 理睬的日志 - 网易博客 趣味学习 英语语法⑶ - 香儿的日志 - 网易博客 引用 如何学习盲打? - 兔子的日志 - 网易博客 英语拼音在线学习 - 香儿的日志 - 网易博客 高效学习十方法 - 1960sun的日志 - 网易博客 为人处世学习三十六计详解 - 真光的日志 - 网易博客 学习做个明白人 - 一统江山的日志 - 网易博客 学习生活在线工具 - 香儿的日志 - 网易博客 Excel 图解教程学习 - 闲人SGM的日志 - 网易博客 Latex学习 - 碧波蓝天的日志 - 网易博客 英语学习网址大全 - 博啦的日志 - 网易博客 学习制作眨眼睛GIF动画 - 梦中人 的日志 - 网易博客 IAR学习笔记(摘抄) - puppypuppy2005的日志 - 网易博客