QCoreApplication

        我们都知道,Qt是一个开源的C++库,主要用来开发GUI程序,但同时,它也支持控制台程序的开发。并且,这里的控制台程序又分为Qt控制台程序和纯C++控制台程序。其中,C++控制台程序就没什么好说的了,就是我们大学的入门程序了,使用cout、stl这样标准的C++组件;而Qt控制台程序是和Qt GUI相对的一种程序,它处理可以进行一般的打印输出外,也可以像GUI程序一样,支持事件循环、信号和槽的特性。并且,Qt库针对不同的应用程序类型,提供了不同的类来表示,比如,QCoreApplication表示Qt控制台程序,QApplication 和 QGuiApplication 表示GUI程序。它们之间的关系为QCoreApplication 继承自最顶层的QObject,QGuiApplication 又继承自QCoreApplication,QApplication又继承自QGuiApplication。今天,我们主要来学习一下QCoreApplication类。那么,我们就先新建一个Qt控制台程序。

启动Qt Creator,文件->新建文件或项目,选择Qt Console Application类型,如下:



建好后的工程如下,非常简单,只有一个工程文件和一个main函数源文件:



刚才,我们说了,QCoreApplication类表示Qt控制台程序,它承载了应用程序的事件循环,通过这个事件循环对所以来自操作系统和其他事件源,如网络,数据库,的事件进行处理和分发。同时,它还实现了应用程序的初始化和退出清理工作,以及系统级和应用级的常用设置。所以,Qt在生成main函数时就自动为我们定义了已应用程序对象,其在程序运行的过程中,就代表了整个应用程序本身。但由于该对象是main函数的局部对象,不方便我们在其他地方使用,特别是对于GUI程序来说,我们可能需要在某个窗口类中使用到这个应用程序对象,所以,鉴于此,Qt又在全局空间为我们提供了已应用程序指针,即qApp,该全局指针指向的就是main函数中定义的应用程序对象,我们可以在任何地方使用这个指针。当然,我们还可以使用QCoreApplication类中的一个静态成员函数instance(),来获得代表该应用程序的指针,该函数声明如下:

QCoreApplication *QCoreApplication::instance()


下面我们就分别看一下QCoreApplication所具有的功能和常用的程序函数。

事件循环和事件处理

在上面QtCreator为我们自动生成的代码中,它先定义了一个QCoreApplication类的对象,紧接着就调用了exec() 成员函数,而Qt的事件循环就是开始于此函数的调用。并且,exec() 会一直运行到事件循环结束才返回,比如quit()或exit() 函数被调用时,而exec() 的返回的值即为调用exit()时所传入的值,如果是quit() 函数,则相当于exit(0)。另外,我们上面说过,该对象代表 了成功应用程序本身,也是它开启了整个程序的事件循环,所以,一般情况下,我们推荐尽可能早的在main函数中创建出该对象。

QCoreApplication还为我们提供了几个方便的静态程序函数。比如我们上面已经说过的instance() 函数,它可以返回应用程序对象的指针;sendEvent()、postEvent() 和sendPostedEvent() 用于发送或寄送事件;事件队列中未处理的事件可以使用removePostedEvents() 或者 flush() 进行处理。

我们还可以将我们的某个操作连接到该类的quit()槽函数上,以此来实现应用程序的退出;也可以相应该类提供的aboutToQuit() 信号,在应用程序退出时做一些清理工作。

应用程序和库路径

我们可以使用applicationDirPath() 和 applicationFilePath() 函数来获得应用程序的执行路径;而应用程序中所使用的库所在的路径可以使用libraryPaths() 获得,使用setLibraryPaths() 、addLibraryPath()、和removeLibraryPath() 函数进行修改、添加和删除。

国际化和翻译

当我们的软件需要运行在不同的语言环境下时,我们要针对特定的语言环境来改变界面显示,这是通过Qt的翻译机制实现的。我们可以使用installTranslator() 和 removeTranslator()来为应用程序安装翻译文件,也可以使用translate() 或 QObject::tr() 和 QObject::trUtf8()对程序中的字符串字面量进行翻译。

处理命令行参数

还是根据上面QtCreator自动生成的代码可以看出,在定义QCoreApplication类的对象时,将main函数表示命令行参数信息的argc、argv传给了QCoreApplication的构造函数。此后,我们就可以使用arguments() 成员函数来访问这些命令行参数。

其实,Qt库中还专门为命令行参数的解析提供了两个类,一个是QCommandLineOption,表示一个命令行参数选项和值,一个是QCommandLineParser,表示用来解析命令行参数的解析器。关于它们的具体使用,大家可以参考Qt帮助文档。

区域设置

我们知道,不同的国家和地区,在某些方面的表示是不同的,大家最常见的某过于货币的表示了,中国使用人民币¥,美国使用美元$,英国使用英镑£,等等。而对应到软件开发中,除了货币,我们面对的更多的是数字的表示,浮点数中的小数点的表示,日期的表示,这些在不同的地区都是不同的。比如,对Qt库来说,在Unix/Linux平台上,默认情况下是使用系统的区域设置。而这会在我们使用POSIX函数时发生一些冲突,例如,当在进行float和string的数据类型转换时。为了解决这个问题,我们可以通过在实例化QCoreApplication之后调用POSIX函数setlocale(LC_NUMERIC, "C")来重置区域设置。同理,QApplication和QGuiApplication也类似。

常用函数

[signal] void QCoreApplication::aboutToQuit()

该信号会在应用程序将要退出事件循环时发出。比如,软件在程序的其他地方调用了quit()函数,或者用户直接关掉了整个桌面回话。通常,我们可以使用这个信号还对应用程序最一些最后的清理工作。但注意,在这个状态时,不可能进行用户交互了。还有,这个信号是私有信号,即我们可以在应用程序中响应这个信号,但不能主动发出这个信号。

[static] void QCoreApplication::addLibraryPath(const QString &path)
[static] QStringList QCoreApplication::libraryPaths()
[static] void QCoreApplication::removeLibraryPath(const QString &path)
[static] void QCoreApplication::setLibraryPaths(const QStringList &paths)
这几个函数用来操作我们的应用程序所使用的库的位置所在。默认情况下,Qt会去搜索INSTALL/plugins,目录,其中INSTALL是Qt的安装目录。我们可以将我们自己开发的库放到一个独立的目录中,然后使用上面的函数将该目录设置为或添加到Qt搜索路径中。

当QCoreApplication的实例被析构时,库搜索路径会被重置为默认路径。


[static] QString QCoreApplication::applicationDirPath()
[static] QString QCoreApplication::applicationFilePath()
获得应用程序可执行文件所在的目录路径和可执行文件本身的路径。打印结果如下:



[static] int QCoreApplication::exec()
这个函数,应该是最重要的函数了,因为是他开启了主事件循环,带动了整个程序的运行。exec() 会一直运行,知道exit() 函数被调用,其返回值就是exit() 函数的参数,如果是通过quit() 函数返回的,其返回值是0。注意,在Windows平台上,当用户注销时,系统会在Qt关闭所有上层窗口之后总结该进程,因此,不能保证应用程序有时间退出事件循环并执行exec() 之后的代码。所以,一般我么推荐连接aboutToQuit() 信号去为应用程序做一些清理工作,而不是直接把与清理工作相关的代码放到exec() 后面,因为这样不一定会被执行到。

通常,我们通过使用0开启一个QTimer来进行空闲处理(即当事件循环没有事件要处理时,去执行一个特定的函数)。其实,更高级的空闲处理可以通过QCoreApplication的processEvents() 来实现。

[static] void QCoreApplication::flush()
该函数刷新平台特定的事件队列。

比如,如果你正在一个循环中进行图形变换操作,在循环结束之前不会返回到事件循环,而你有想立即看到对图形所做的改变,就可以调用这个函数。

[virtual] bool QCoreApplication::notify(QObject *receiver, QEvent *event)
这个函数和exec() 一样,也是一个很重要的函数,应用程序就是通过这个函数将接收到的事件分发给特定的事件接受者, receiver->event( event)。其返回值就是接受者事件处理器的返回值。还有,应用程序会对所以线程中的所有事件调用这个函数。

其实,说到事件处理,在Qt中共有5中事件处理的方式,重写该函数,只是其中之一。下面,我们简单看一下这5 中事件处理方式。

1. 第一种,也是最常用,最通用,最简单的方式,即在特定类中重写基类的事件处理函数,比如paintEvent()、mousePressEvent()、mouseMoveEvent()。。。。。。

2. 第二种就是实现notify() 函数,这种方式功能强大,可以让我们对程序中的事件处理进行完全的控制。但是,这种方式同一时间只能激活一个类,向它分发事件。
3. 在QCoreApplication的对象上安装事件过滤器。这种事件过滤器是一种全局事件过滤器,可以处理所有控件的事件,所以这种方式的能力和重写notify() 函数差不多;此外,我们可以在一个QCoreApplication应用程序对象上安装多个这样的事件过滤器。全局事件过滤器甚至能监视到发往被禁用的控件的鼠标事件。但是要注意,应用程序级别的事件过滤器只过滤哪些发往存活在主线程中的对象的事件。

4. 重写QObject::event() 函数。这种方式还能让我们拦截到tab键的按下事件。并且,这种方式的优先级要高于控件上的事件过滤器,即我们可以先与控件过滤器拦截到事件。

5. 在某个特定对象上安装事件过滤器。这种事件过滤器能拦截到所有的事件,包括Tab和Shift+Tab的press事件,前提是他们没有改变焦点控件。

最后要注意的是,如果重新了notify( )函数,就必须确保所有处理事件的线程在应用程序对象析构之前停止处理。这包括所使用的第三方库开启的线程,但不包括Qt自己的线程。


[static] bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
[static] void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
其中,postEvent()类似于win32的postMessage() ,它把事件对象event及其接受者receiver放到事件队列中,然后立即返回。注意,要投递的事件对象必须在堆上进行分配,因为事件队列或接手该对象的所有权,并在事件分发到接受者后自动释放该对象。所以,在事件对象被投递之后,再访问该对象也是不安全的。

当程序控制流再返回到主事件循环时,所有存储在事件队列中的事件会通过notify() 函数被发送出去。

另外,根据函数声明可以看出,在调用postEvent() 时,我们可以为所投递的事件指定一个优先级,事件队列中的事件就是按该优先级被排序的。也就是说,具有高优先级的事件会被排在低优先级事件的后面。事件优先级可以是INT_MIN ~ INT_MAX 之间的任何整数。该函数是线程安全的

同理,sendEvent() 类似于win32的sendMessage() 函数,它直接把事件event通过notify()发送给接受者receiver。返回值就是接受者的事件处理器的返回值。事件对象不会在发送事件完成后自动被释放,所以,一般的做法是在栈上定义该事件对象。例子如下:

  QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
  QApplication::sendEvent(mainWindow, &event);

[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents)
[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime)
在GUI程序开发中,如果遇到某个计算过程非常漫长,比如一个很大的循环,这会导致我们的界面在这段时间内无法正常处理用户数据而出现界面假死的现象。通常,对于这种情况,我们会把该计算过程放到一个单独的线程中。在Qt中,我们有了另一种方法,即在循环中调用上面的两个方法之一。

processEvent() 会去处理所有属于当前线程为处理的事件。从而,可以在漫长的计算过程中抽出事件处理用户的界面输入,防止界面假死。

同时,根据其重载形式,我们也可以为本次事件处理指定一个事件上限。当达到这个事件上限或者已经没有可处理的事件,就返回计算过程。

至于,剩下的函数,比如和库路径有关的函数、和字符串翻译即国际化有关的函数,就要大家根据需要自行研读Qt的帮助文档了,在此就不一一细说了。




  • 24
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值