QT编程核心技术【事件机制】

原文出自:http://hi.baidu.com/yanxinxi/blog/item/3f04fc3e553281f014cecb22.html

3.4 事件过滤器

一个事件过滤器是一个能接收所有发送到这个对象上的事件的对象。这个过滤器能停止或转发到这个对象上的事件。事件过滤器通过 eventFilter()函数来接收事件,如果这个事件应该被过滤(如:停止事件等),eventFilter()函数返回TRUE, 否则,返回FALSE。如果多个事件过滤器被安装在一个对象上,最后安装的过滤器将被激活。

QObject类还提供了事件过滤器的安装,QObject类与事件过滤相关的几个成员函数说明如下:

bool QObject::event ( QEvent * e ) [virtual] 接收到一个对象的事件,如果事件被识别并处理时,返回TRUE。这个函数能被重载来定制一个对象的行为。

bool QObject::eventFilter ( QObject * watched, QEvent * e ) [virtual] 如果一个对象上已安装了事件过滤器,eventFilter函数将被用来过滤事件。在这个函数的重载中,如果你想过滤事件e(如:让它停止不再被处理), 就返回TRUE,否则返回FALSE。

void QObject::installEventFilter ( const QObject * filterObj ) 在对象filterObj上安装事件过滤器。

下面是一个事件过滤器的使用样例,MyMainWindow类在本对象上安装了事件过滤器,事件过滤处理函数eventFilter被重载 用来在textEdit对象上处理KeyPress事件。没处理的事件被传递到基类的eventFilter()函数中,因为基类也可能因为内部事件处理 的原因已重载了eventFilter()函数。如果你在这个函数中删除了接收者对象,确信返回TRUE,否则,Qt将向前传递事件到删除的对象中,程序 将崩溃

class MyMainWindow :public QMainWindow {public: MyMainWindow( QWidget *parent =0, constchar*name =0);   protected:bool eventFilter( QObject *obj, QEvent *ev );   private: QTextEdit *textEdit;};   MyMainWindow::MyMainWindow( QWidget *parent, constchar*name ): QMainWindow( parent, name ){ textEdit =new QTextEdit(this); setCentralWidget( textEdit ); textEdit->installEventFilter(this); //在本对象上安装事件过滤器}   bool MyMainWindow::eventFilter( QObject *obj, QEvent *ev ){if( obj == textEdit ){ //过滤的对象if( e->type()== QEvent::KeyPress){//过滤的事件 qDebug("Ate key press %d", k->key());return TRUE; //已对事件处理,必须返回TRUE,这样,系统不会对这个事件做第二次处理了}else{return FALSE;}}else{// 传递事件到父类return QMainWindow::eventFilter( obj, ev );}}

3.5 定时器

使用定时器有2种方法,一种是使用QTimer类,另一种是使用QObject类的定时器。定时器的精确度依赖于操作系统和硬件,大多数平台支持20ms的精确度。

(1)QObject类的定时器

Qobject是所有Qt对象的基类,它提供了一个基本的定时器。通过QObject::startTimer(),你可以把一个以毫秒为单位的时 间间隔作为参数来开始定时器。这个函数返回一个唯一的整数的定时器的标识符。这个定时器现在就会在每一个时间间隔"触发",直到你明确地使用这个定时器的 标识符来调用QObject::killTimer()结束。

应用程序在main函数中通过调用QApplication::exec()来开始进行事件循环。当定时器触发时,应用程序会发送一个 QTimerEvent,在事件循环中,处理器按照事件队列顺序来处理定时器事件,当处理器正忙于其它事件处理时,定时器就不能马上触发。

QObject类还提供定时器的功能,与定时器相关的成员函数有startTimer()、timerEvent()、killTimer()和killTimers()。QObject基类中的startTimer和timerEvent原型及说明如下:

int QObject::startTimer ( int interval )

开始一个定时器并返回定时器ID,如果不能开始一个定时器将返回0。定时器开始后,每隔 interval毫秒间隔将触发一次超时事件,直到killTimer()或killTimers()被调用来删除定时器。如果interval为0,那 么定时器事件每次发生时没有窗口系统事件处理。

void QObject::timerEvent ( QTimerEvent * ) [virtual protected]

虚拟函数timerEvent被重载来实现用户的超时事件处理函数。如果有多个定时器在运行,QTimerEvent::timerId()被用来查找是哪个定时器被激活。

当定时器事件发生时,虚函数timerEvent()随着QTimerEvent事件参数类一起被调用。重载这个函数可以获得定时器事件。

定时器的用法样例如下:

class MyObject :public QObject { Q_OBJECT public: MyObject( QObject *parent =0, constchar*name =0);   protected:void timerEvent( QTimerEvent *);};   MyObject::MyObject( QObject *parent, constchar*name ): QObject( parent, name ){ startTimer(50);// 50ms定时器 startTimer(1000);// 1s定时器 startTimer(60000);// 1分钟定时器}   void MyObject::timerEvent( QTimerEvent *e ) //重载timerEvent函数{ qDebug(" timer event, id %d", e->timerId());}   void MyObject::stopTimer(){ killTimer( showDateTimer ); showDateTimer=-1;}

(2)定时器类QTimer

定时器类QTimer提供当定时器触发的时候发射一个信号的定时器,QTimer提供只触发一次的超时事件。通常的使用方法如下:

QTimer * testtimer =new QTimer(this); //创建定时器  connect(testtimer, SIGNAL(timeout()), this, SLOT(updateCaption()));//将定时器超时信号与槽(功能函数)连接起来 testtimer ->start(1000);//开始运行定时器,定时时间间隔为1000ms

testtimer定时器被作为这个窗口部件的子类,这样当这个窗口部件被删除时,定时器也会被删除。

QTimer还提供了一个简单的只有一次定时的函数singleShot。例如:一个定时器在100ms后触发处理函数animateTimeout,并且只触发一次。代码如下:

QTimer::singleShot( 100, this, SLOT(animateTimeout( );

3.5 连接函数connect

QObject类还提供了信号与槽的连接函数connect()和断开连接函数disconnect()。这两个函数的用法,在信号与槽一节中详细说明。

3.6 字符串翻译函数

QObject类还提供了字符串的翻译函数tr()和trUtf8()。这两个函数说明如下:

QString QObject::tr ( const char * sourceText, const char * comment ) const

返回字符串sourceText的翻译,如果没有合适的翻译版本就返回原字符串sourceText。参数comment是翻译的上下文,是对字符串的补充说明,如:说明属于哪个类,可用来辅助标识字符串,这样可起到相同字符串要求不同的翻译。

QString QObject::trUtf8 ( const char * sourceText, const char * comment ) const

返回字符串sourceText的翻译,如果没有合适的翻译版本返回字符串QString::fromUtf8(sourceText)。其它与函数tr()相同。

Qt国际化

软件的国际化是指软件支持多国语言,能适应不同用户的语言、输入方法、字符编码甚至表达习惯等。软件的国际化使软件能被多个国家的人使用。

4.1 软件中字符串国际化方法

软件可以按下面的几个方法实现国际化:

(1)对用于Gui界面的字符串使用QString

QString内部使用了Unicode编码,Unicode编码包括了世界上的每种语言的字符编码。在Qt的基类中,有关字符串的函数大多数使用了QString作为参数。这样减小了char*到QString的转换的时间开销。

另外,还有QCString和QChar,它们的用法类似于传统C中的const char*和char。通过使用QString、QCString和QChar类,你不会注意到你在使用Unicode。

(2)对所有文字形式的文本使用tr()

程序需要显示字符串到用户时,要确保它被QApplication::translate()函数处理过。其实做到这一点只需要使用QObject::tr()。例如,假设LoginWidget是QWidget的一个子类:

LoginWidget::LoginWidget(){ QLabel *label =new QLabel( tr("Password:"), this); ... }

这样就解决了大部分你可能要写的用户可见的字符串。如果这些被引用的字符串不是在QObject子类的成员函数中,可以使用一个适当的类的tr()函数,或者直接使用QApplication::translate()函数,使用这两个函数的例子如下:

void some_global_function( LoginWidget *logwid ){ QLabel *label =new QLabel( LoginWidget::tr("Password:"), logwid );}   void same_global_function( LoginWidget *logwid ){ QLabel *label =new QLabel( qApp->translate("LoginWidget", "Password:"), logwid );}

lupdate工具会自动给每个源文本提供一个上下文。这个上下文是装有tr()调用的类的类名,这足够用来处理相同字符串需要不同的翻译的大多数情况。但有时候,需要更多的信息来惟一标识源文本。

示例:用上下文来惟一标识字符串

一个装有两个按钮对话框,每个按钮上有"Enabled"字符串,但翻译需要有差异,这时,需要用上下文来惟一标识字符串。这两个按钮字符翻译的上下文,分别是"Color frame"和"Hue frame"。这个例子列出如下:

rbc =new QRadioButton( tr("Enabled", "Color frame"), this); rbh =new QRadioButton( tr("Enabled", "Hue frame"), this);

(3)翻译函数外的文本

如果你需要翻译函数外的文本,有两个宏可以使用:QT_TR_NOOP()和QT_TRANSLATE_NOOP()。它们仅仅给文本作出标签,以便于被lupdate工具提取。使用QT_TR_NOOP()的例子如下:

QString FriendlyConversation::greeting(int greet_type ){staticconstchar* greeting_strings[]={  QT_TR_NOOP("Hello"),  QT_TR_NOOP("Goodbye")};return tr( greeting_strings[greet_type]);}

使用QT_TRANSLATE_NOOP()的例子如下:

staticconstchar* greeting_strings[]={  QT_TRANSLATE_NOOP("FriendlyConversation", "Hello"),  QT_TRANSLATE_NOOP("FriendlyConversation", "Goodbye")};   QString FriendlyConversation::greeting(int greet_type ){return tr( greeting_strings[greet_type]);}   QString global_greeting(int greet_type ){return qApp->translate("FriendlyConversation", greeting_strings[greet_type]);}

如果你使用了宏定义QT_NO_CAST_ASCII编译你的软件,就会关闭了从const char*到QString的自动转换,你很可能会得到错误的字符串。

如果你的源码语言使用Latin-1之外的字符集,你会发现QObject::trUtf8()比QObject::tr()更好用,因为tr()依赖于QApplication::defaultCodec(),这使它比QObject::trUtf8()更脆弱。

对于加速键值(Accelerator value),例如Ctrl+Q或者Alt+F,有时需要翻译者重载它,这时可使用使用QKeySequence()。如果你的应用给"Quit"直接编 码(hardcode)为CTRL+Key_Q,翻译者就不能重载它了。正确的用法如下:

QPopupMenu *file =new QPopupMenu(this); file->insertItem( tr("&Quit"), this, SLOT(quit()), QKeySequence(tr("Ctrl+Q", "File|Quit")));

(4)对字符串参数使用QString::arg()

对于国际化的文本,在字符串中使用类似printf()风格的插入参数不是好的选择,因为有时候有必要在翻译时改变参数的顺序。QString::arg()函数为参数替换提供了一种简单的途径,下面是使用QString::arg()的例子:

void FileCopier::showProgress(int done,int total,const QString& current_file ){ label.setText( tr("%1 of %2 files copied.\nCopying: %3") .arg(done) .arg(total) .arg(current_file));}

4.2 创建译本

程序代码中使用tr()之后,你就可以开始制作程序中用户可见的文本的译本了。Qt提供了Qt语言学家(Qt Linguist)工具、lupdate和lrelease来进行翻译处理。其中, Qt Linguist是图形界面工具,用户可以使用它,将字符串对应的翻译填入.ts文件中。

在你运行lupdate之前,你应该准备一个项目文件(.pro文件)。下面是一个项目文件名为myproject.pro的例子:

HEADERS = funnydialog.h \ wackywidget.h SOURCES = funnydialog.cpp \ main.cpp \ wackywidget.cpp FORMS = fancybox.ui TRANSLATIONS = superapp_dk.ts \ superapp_fi.ts \ superapp_no.ts \ superapp_se.ts

当你运行lupdate或者lrelease时,你必须以命令行参数给出项目文件的名称。例如:lupdate myproject.pro。在本例中,支持四种语言:Danish、Finnish、Norwegian和Swedish。如果你使用qmake(或者 tmake),你一般不需要给lupdate的附加项目文件;只要你加上TRANSLATIONS条目,你的qmake项目文件就会正常工作。

Qt应用的翻译过程分为三步:

第1步:运行lupdate,即使用命令:lupate project-file –ts ts-files。提取Qt应用的C++源代码中的可翻译文本,会产生一个翻译信息文件(.ts文件)。lupdate能识别出tr()结构和上面描述的 QT_*_NOOP宏。lupdate读一个.pro项目文件,并产生或更新列出在项目文件中的.ts翻译源文件。

第2步:使用Qt语言学家(Qt Linguist)翻译工具软件打开.ts文件,然后,人工翻译.ts文件中源文本的字符串,将字符串的译文使用Qt Linguist工具加到.ts文件中。.ts文件是XML格式,你也可以手工编辑它们。

第3步:运行lrelease,即使用命令lrelease ts-files –qm qm-file。从.ts文件中得到只适用于最后使用的轻量级的信息文件(.qm文件)。你可以把.ts文件看成"源文件",把.qm文件看成"目标文 件"。翻译者编辑的是.ts文件,可是你的应用的用户只需要.qm文件。这两种文件都是平台和地区(locale)无关的。你将对应用地每个发表版本重复 这几步。lupdate工具会尽力重用以前的发表版本的译文。

在应用中,你必须使用QTranslator::load()来装载对应用户语言的译文文件,再使用QApplication::installTranslator()来安装它们。

如果你一直使用以前的Qt工具(findtr、msg2qm和mergetr),可以使用qm2ts来转换你以前的.qm文件。

虽然这些工具提供了生成.qm文件的方便途径,可任何能编写.qm文件的系统也都够用。你可以制做一个应用,以利用 QTranslator::insert()把译文加入到QTranslator中,接着再利用QTranslator::save()写出一个.qm文 件。用这种办法可以从任何你选择的源文件中产生译文。

Qt本身包含有大约400个也需要翻译为目标语言的字符串。在$QTDIR/translations下,你会找到French和German的译文文件,也可以作为翻译为其他语言的模板。

示例3:装载译文文件

下面的例子说明了如何装载译文文件,在这个例子,应用程序会根据不同的本地语言来安装相应的译文文件。

int main(int argc, char**argv ){ QApplication app( argc, argv );   /*装载 Qt翻译文件*/ QTranslator  qt(0);//装载没有父类的QTranslator对象//根据本地语言选择译文文件(.qm文件)  qt.load( QString(" qt_")+ QTextCodec::locale(), "."); app.installTranslator(& qt );   //给应用 程序字符串装载翻译文件 QTranslator myapp(0); myapp.load( QString("myapp_")+ QTextCodec::locale(), "."); app.installTranslator(&myapp ); ... return app.exec();}

4.3 编码支持

当应用启动时,机器的地区(locale)决定了处理8-bit文本数据方法(如字体选择、文本显示、8-bit文本I/O和字符输入所用的8-bit编码)。

应用程序中有时会需要不同于缺省的本地8-bit编码。QTextCodec类和QTextStream类中函数,会支持很多对于用户数据 的输入输出编码。例如,一个Cyrillic KOI8-R locale(俄罗斯的事实标准locale)的应用可能需要以ISO 8859-5编码输出Cyrillic。这样的代码可能会是:

QString string = ...;// 一些Unicode编码的文本   QTextCodec* codec = QTextCodec::codecForName("ISO 8859-5"); //使用指定的编码 QCString encoded_string = codec->fromUnicode( string );//得到指定编码的字符串

对于把Unicode转换为本地8-bit编码,有一个快捷办法:QString的local8Bit()方法返回的就是这样的8-bit数据。另 一个有用的快捷办法是utf8()方法,它以8-bit的UTF-8编码返回文本,UTF-8编码的好处是:如果Unicode完全是US-ASCII的 话,它可以完全保留Unicode信息,而看起来又是一般的US-ASCII。

Qt提供完整的Unicode支持,包括输入法、字体、剪贴板、拖放和文件名。 文件I/O缺省为Latin-1,在QTextStream中带有Unicode选项。Unicode(UTF16或者UTF8)是包含了所有语言的字符 编码方法,使用Unicode编码的软件,可以最大地提高了各国用户之间的文本兼容性。然而,有时在同一语言的群体内,本地编码标准也许更为合适。这个编 码就是由QTextCodec::codecForLocale()所返回的编码方法。你还可以使用 QTextCodec::loadCharmapFile()函数以构造一个数据驱动的编码器。

Qt所支持的字符编码类说明如表1。

表1  Qt 中字符编码的类表
类名 功能说明
QEucJpCodec EUC到JP字符集之间的转换。
QEucKrCodec EUC到KR字符集之间的转换。
QGb18030Codec 中国GB18030/GBK/GB2312编码。
QGbkCodec 中国GBK编码。
QHebrewCodec Hebrew编码。
QJisCodec JIS字符集编码。
QSjisCodec Shift-JIS字符集编码。
QTextCodec 文本编码之间的转换。
QTextDecoder 基于状态(State-based)的解码器。
QTextEncoder 基于状态的编码器。
QTranslator 文本输出的国际化支持。
QTranslatorMessage Translator的信息及其属性。
QTsciiCodec Tscii编码。
5 QMetaObject元对象类

QMetaObject类装有有关Qt对象的元信息,包括运行时类型信息、属性系统。它负责信号与槽机制。每个在类声明中含有Q_OBJECT宏的类都有一个对应的元对象,即QMetaObject类对象,元对象是静态的,因此,只有一个元对象与这个类对应。

5.1 相关的数据结构

(1)QConnection、QConnectionList和QSignalVec类

QConnection类是内部使用的类,用于signal/slot机制中。不能直接用于应用程序。QObject中每个连接到外面的信 号siganl有一个QConnection链表。QConnection类存储并可使用成员函数得到连接到的对象指针、连接的成员名及其类型、参数个 数。QConnection类构造函数的声明列出如下:

QConnection::QConnection(const QObject *object, int member, constchar*memberName, int memberType )

QConnectionList类是一个QConnection对象链表,它从QPtrList<QConnection>继承,存储着各个QConnection对象指针。

QSignalVec类从QPtrVector<QConnectionList>继承,它是QConnection对象链表的数组,将每个信号的QConnection对象链表组织到一个数组中。

(2)元数据结构QMetaData及类属性结构QmetaEnum

结构QMetaData是信号与槽成员函数的元数据,用于描述信号或槽。结构QMetaData列出如下:

struct QMetaData {constchar*name;// 信号或槽的名称const QUMethod* method;// 信号或槽详细的方法描述enum Access { Private, Protected, Public }; Access access;//访问许可 };typedefconst QMetaData QconstMetaData;

类QmemberDict从QasciiDict<QconstMetaData>继承,是QmetaData的对象链表。

结构QmetaEnum是某类属性的描述,用于显示在Qt Designer等图形开发工具上。结构QmetaEnum说明如下:

{constchar*name;// 某类属性名 uint count;// 属性条目数总数struct Item // 一个属条目,name/value对{constchar*key;int value;};const Item *items;// 属性条目的数组 bool set;//这个类属性是否被看成一个集合};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值