splitter->addWidget(conversationView); splitter->addWidget(chatEdit);
chatEdit->installEventFilter(this);//给chatEdit对象安装事件过滤器 setWindowTitle(tr(\
setTabOrder(chatEdit, conversationView);//设置焦点顺序 };
bool ChatWindow::eventFilter(QObject *watched, QEvent* e) {
if (watched == chatEdit && e->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast
if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) { submitChatText(); return true; } }
return QWidget::eventFilter(watched, e); }
void ChatWindow::submitChatText() {
// append text as new paragraph
conversationView->append(chatEdit->toPlainText()); // clear chat window chatEdit->setPlainText(\}
在ChatWindow的构造函数中,调用installEventFilter()函数,给chatEdit安装事件过滤器,并由this指针参数指出,将由ChatWindow自身对chatEdit对象的事件进行过滤。
在chatEdit对象中产生的事件,会先经由ChatWindow 的eventFilter()过滤,然后才实例的主函数代码入下所示: 会被传递给chatEdit对象自己。 // main.cpp
#include
QApplication app(argc, argv); ChatWindow win; win.show();
return app.exec();//事件循环 }
整个实例的事件处理流程为:
(1)在chatEdit控件中输入字符串,然后按“Return”或“Enter”键,触发按键事件。 该事件在chatEdit中产生,如无意外,将由chatEdit所属类的event()方法处理。 (2)主函数中的exec()事件循环中检测到了按键事件,按照程序设定,该事件被先传递给了ChatWindow的eventFilter()函数进行处理。
(3)在ChatWindow的eventFilter()函数中,根据条件判断,由于按下的是“Return”或“Enter”键,该按键行为导致submitChatText()函数被调用。然后返回true。 (4)由于ChatWindow的eventFilter()函数返回了true值,Qt事件系统将该次按键事件给过滤掉了,最终,chatEdit并没有接收到“Return”或“Enter”的按键事件。
如果在chatEdit控件中按下的并非“Return”或“Enter”键,那么按键事件流入ChatWindow的eventFilter()函数之后,将交由QWidget::eventFilter(watched, e)进行过滤,最终传递到chatEdit控件。 4.2 通用事件句柄的应用(event)
通过重载event()函数,可以在事件被特定的事件处理函数处理之前(像keyPressEvent())处理它。比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件。
下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )
bool CodeEditor::event(QEvent * event) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast
insertAtCurrentPosition('\\t'); return true; } }
return QWidget::event(event);//将其它事件交由父类的event()函数处理 }
4.3 特定事件处理函数--QTimerEvent的应用
Qt中的int QObject::startTimer ( int interval )函数可以开启一个定时器,其返回值为被创建的定时器的ID号(若开启定时器失败则返回0)。定时器开启后,每隔interval个毫秒就会产生一个timer事件。该timer事件会被事件循环捕获,然后被传递到接收对象,由接收对象的event()方法进行处理。下面以一个实例说明timer事件的应用。实例运行效果如图4-3-1所示:
图4-3-1 实例运行效果
QLCDNumber继承。其clockwidget.h文件内容如下所示: // clockwidget.h
#ifndef CLOCKWIDGET_H #define CLOCKWIDGET_H #include
class ClockWidget : public QLCDNumber
下面的代码实现一个数字时钟,构建一个名为ClockWidget的类,该类从
{
Q_OBJECT public:
ClockWidget(QWidget *parent = 0); protected:
void timerEvent(QTimerEvent *e);//负责处理接收到的timer事件 private:
int updateTimer, switchTimer;//存储创建的timer ID号 bool showClock;//用于切换时间与日期的显示 };
#endif // CLOCKWIDGET_H clockwidget.cpp文件内容如下所示: // clockwidget.cpp #include
ClockWidget::ClockWidget(QWidget *parent) : QLCDNumber(parent), showClock(true)
{//构造函数,在参数初始化类表中已经给showClock赋值为true setFrameShape(QFrame::NoFrame); setSegmentStyle(QLCDNumber::Flat);
updateTimer = startTimer(1000);//开启一个1秒间隔的定时器 switchTimer = startTimer(10000);//开启一个10秒间隔的定时器
QTimerEvent *e = new QTimerEvent(updateTimer);//创建一个QTimerEvent类型 //的事件对象,该对象以updateTimer作为时间间隔。及该事件每隔1秒发生一次 QCoreApplication::postEvent(this, e); }
void ClockWidget::timerEvent(QTimerEvent *e) {//处理timer事件的函数
if (!e) return;//检查指向事件的指针是否是安全有效 if (e->timerId() == switchTimer) showClock = !showClock; if (e->timerId() == updateTimer) { if (showClock) {
QTime time = QTime::currentTime();//获得当前系统时间
QString str = time.toString(Qt::LocalDate);//将时间转换成QString型字符串 setNumDigits(str.length());//设置LCD数字位数 display(str);//显示字符串 } else {
QDate date = QDate::currentDate();//获得当前系统日期
QString str = date.toString(Qt::LocalDate); //将日期转换成QString型字符串 setNumDigits(str.length());//设置LCD数字位数 display(str); //显示字符串 } } }
上述代码中,定时器用于触发QTimerEvent类型的事件。updateTimer与switchTimer存储了两个定时器的ID号,这两个ID号可用于在timerEvent()中对两个定时器加以区分。没有必要等上1秒钟之后再让控件显示,我们可以用updateTimer这个ID号手动发送一个timer事件,用QCoreApplication的postEvent()方法将事件发送出去。将目的对象指定为当前的widget。注意:手动创建QTimerEvent对象的目的就是要在updateTimer定时器自然到达触发事件之前,发送出一个事件。如果不是如此的话,是不需要手动创建QTimerEvent对象的。因为定时器到了所设定的事件间隔之后会自动触发一个QTimerEvent类型的事件的。
main函数实现代码如下所示: // main.cpp #include
QApplication app(argc, argv); ClockWidget w; w.show();
return app.exec();//事件循环 }
5 动态语言切换
Qt内部采用的全Unicode编码,这从根本上保证了多国语言界面实现的正确性和便捷性。Qt本身提供的linguist工具,就是来实现这个翻译过程的。实现多国语的步骤如下: 步骤1、在需要被翻译的字符串前面标识tr,如QString str=tr(“hello,world!”); ,这很重要,因为翻译工具会把源码中tr标识的字符串提取出来,翻译成其他语言,如果没有用tr标识的,不会被工具提取。在界面中输入的文字,默认已经是加上tr的了,所以在翻译时也能看见。建议:在程序中的字符串使用英文,汉语等通过多国语翻译来实现,而不要采取把汉字写在代码中。
在需要实现动态切换语言时,较好的方法是将所有需要动态切换的字符串集中在同一个函数中进行设定,如下:
// initGUI() 中会有大量的tr函数 void WizarDialog::initGUI() {
this->setWindowTitle(tr(\ Button->setText(tr(“Cancel”)); /* ...... */ }
步骤2、在工程文件***.pro中,添加一项
TRANSLATIONS += ***_CN.ts ****_EN.ts \\
***.ts
扩展名为.ts是翻译的源文件,表示生成这几个文件。一般我们会在命名中把区域加进去,更好的注释这些文件是用于什么语言的,比如中文大多会这样命名 myapp_zh_CN.ts, zh_CN表示的就是中国。
步骤3、使用lupdate工具提取翻译源文件, 命令是这样的 lupdate ***.pro
lupdate会解析***.pro即工程文件,生成TRANSLATIONS中的 ***.ts 几个文件,这些文件可以被linguist工具打开,按照提示一个一个的翻译成需要的文件,然后保存就OK。 (lupdate 是一个命令行工具,用于在指定的源文件、头文件及Qt Designer 接口文件中寻找可翻译的字符串,然后产生或更新.ts翻译文件。需要翻译的文件以及需要更新的文件可以在命令行下设置,也可以将一个.pro文件作为命令行参数传递给lupdate命令。由lupdate产生的.ts文件将交给翻译人员,翻译人员使用Qt Linguist读取文件,并插入翻译内容。.ts文件是xml格式的。Lupdate还可以处理XLIFF格式(Localization Interchange File
Format)的文件,这种文件以.xlf作为后缀名。可以使用lupdate –help 命令获取该命令所支持的操作列表。)
步骤4、用linguist翻译***.ts文件中的词条(每个tr项包含的内容)。打开刚才 的***.ts文件,linguist是在qt的bin的目录下的一个界面工具。。在linguist中用菜单栏file ->open 打开相应的.ts文件。打开后会看到左边是相应的类,右边的上半部是相应的类里面提取出来供翻译的内容,下半部是要翻译 的语言的相应的东西,即为需要输入中文的地方。在翻译中,要注意标点符号的翻译最好还是用英文输入状态下的标点符号。当翻译状态前出现绿色勾的提示为翻译 成功,叹号的表示没有翻译对,交叉的表示没有翻译。 步骤5、使用lrelease工具发布翻译文件的二进制文件, lrelease ***.pro
这样在程序运行时载入会大大的加快速度。这个工具会提示你多少语句被翻译,多少被忽略了等。生成的文件是 ***.qm,于同名的 ***.ts只是换了一个扩展名。而这才是我们程序需要使用到的文件。
(lrelease是一个命令行工具,用于从.ts文件中产生出.qm文件。.qm文件格式是压缩二进制格式,它将被本地应用程序使用。.qm文件为翻译器提供极其快速的查找。Lrelease处理的.ts文件可以在命令行指定,也可以由Qt的.pro工程文件间接给出。)
注:本步骤也可以在linguist中完成。用linguist界面工具里面菜单file里面的release... 步骤6、使用***.qm文件 切换语言分为两种情况:
1. 程序载入的时候,根据当前的区域设置,自动选择语言包(.qm),即可; 2. 要求在程序运行过程中动态切换语言;
第一种情况,一般在main函数中程序启动的部分加入如下代码:
QString locale = QLocale::system().name());// for example: zh_CN, en_US QTranslator *translator = new QTranslator(app);
translator->load(QString(\ // 会在当前目录下的
//language目录下寻找,可以不带\后缀名
app->installTranslator( translator ); // 安装翻译器
第二种情况,我们假设有一个QComboBox连接了changeLang的槽: connect(langCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(changeLang(int)) );
// 载入不同的语言包
void WizarDialog::changeLang( int langIndex ) {
QTranslator *translator = new QTranslator(qApp); switch( langCombo->currentIndex() ){ case 0:
translator->load(QString(\ break; case 1:
translator->load(QString(\ break; case 2:
translator->load(QString(\ default: break; }
qApp->installTranslator( translator ); //截止到该步骤只是加载安装了翻译器 this->initGUI(); //该函数中所包含的tr,将会调用之前加载安装的翻译器 }
// initGUI() 中会有大量的tr函数 void WizarDialog::initGUI() {
this->setWindowTitle(tr(\ /* ...... */ }
这两种情况,也可以复合起来用。
需要说明的是,一般我们使用设计器来设计界面UI,也就是程序源码中我们看到的 ***.ui文件,在载入翻译器后,我们应该调用 ui->retranslateUi() ,这个函数实际上就是把界面控件的text重新载入一遍,可以在 ui_***.cpp中看到该函数的实现。
注:QTranslator在load以后,并没有把qm文件中的数据拷贝一份,而是在需要的时候去查询字符串。如果qm在这期间被删除或修改,对程序都是有影响的。扩展开来,QTranslator必须保证要一直有效,如果在函数中定义的局部变量,函数结束后就自动释放掉了,那么翻译工作就不能正常进行。所以建议在private中定义个成员变量 QTranslator* app_translator;来确保整个翻译工作的正确性。
6 数据结构
7 硬件加速:OpenVG、OpenGL、DirectFB
Qt中提供的控件、动画框架、软件框架等资源,可以方便的实现UI中所设计的各种场景(动作、动画等)。对于同一种场景,在Qt中或许可以找到多种解决方案。当把Qt实现的代码运行在嵌入式系统中,由具体的电视芯片来运行时,代码的运行效率将变成最主要的问题。
8 编程技巧
8.1 借用。。。。生成.cpp与.h文件及代码 8.2 借用Qt Designer自动生成的代码
8.3 快捷键“F2”可以追查光标所指处item的定义
9 注意事项
9.1 如果child设定了parent,请不要自己删除child,因为会引起double delete,导致系统死掉 9.2