【学习笔记】C++ GUI Qt4 第三章 3.7 多文档和3.8程序启动画面
文章目录
3.7 多文档
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWin;
mainWin. show();
return app.exec();
}
这个main()函数和以前曾经写过的那些函数稍微有点不同:以变量的形式在堆栈里创建MainWindow实例,而不是使用new来创建它。当函数结束时, MainWindow实例会自动销毁。就像上面main()函数所显示的那样, Spreadsheet应用程序只提供了一个单一主窗口,并且在同一时间只能处理一个文档。如果想让它在同一时间具有处理多个文档的能力,就需要同时启动多个Spreadsheet应用程序实例。但是这对于用户来讲是很不方便的,用户需要的是一个可以处理多个文档的单应用程序实例,就像在一个网页浏览器实例中可以同时提供多个浏览器窗口一样。
下面将修改Spreadsheet应用程序,以使它可以处理多个文档。首先,需要对File菜单做一些简单改动:
● 利用File->New创建一个空文档主窗口,而不是再次使用已经存在的主窗口。
● 利用File->Close关闭当前主窗口。
● 利用File->Exit关闭所有窗口。。
在File菜单的最初版本中,并没有Close 选项,这只是因为当时它还和Exit一样具有相同的功能。
int main(int argc, char *argv[])
{
QApplication app(argC, argv);
MainWindow *mainWin = new MainWindow;
mainWin->show();
return app. exec();
}
我们只创建了一个新的MainWindow实例。这看起来有些奇怪,因为没有保留指向这个新窗口的任何指针;但实际上这并不是什么问题,因为Qt会对所有的窗口进行跟踪。
以下是用于Close和Exit的动作:
void MainWindow::createActions()
{
...
closeAction = new QAction(tr("&Close"), this);
ctoseAction->setShortcut(QKeySequence::Close);
closeAction->setStatusTip(tr("Close this window"));
connect (closeAction, SIGNAL(triggered()), this, SLOT(close()));
exitAction = new QAction(tr("E&xit"), this);
exitAction->setShortcut(tr("Ctrl+Q"));
exitAction-> setStatusTip(tr("Exit the application"));
connect (exitAction, SIGNAE(triggered()), qApp, SLOT(closeAllWindows()));
...
}
槽QApplication: :closeAllWindows()会关闭所有应用程序的窗口,除非其中一个应用程序拒绝了这个关闭事件。这正是在此所需的行为。我们不用再考虑那些有关是否保存的事情,因为就算关闭一个窗口,都会在MainWindow::closeEvent()中处理这些情况。看起来好像已经完成了应用程序对多窗口处理能力的工作。遗憾的是,这里还隐藏着一个潜在的问题:如果用户一直创建并且关闭主窗口,那么这台机器早晚会耗尽它的全部内存。这是因为我们保存了newFile()中创建的MainWindow窗口部件,但是从没有删除它们。当用户关闭一个主窗口时,默认行为是隐藏它,所以它还会保留在内存中。对于如此多的主窗口的确会造成一定的问题。解决办法是在构造函数中对Qt::WA_DeleteOnClose 的属性进行设置:
MainWindow::MainWindow()
{
...
setAttribute(Qt::WA_Delete0nClose);
...
}
这样做就会告诉Qt在关闭窗口时将其删除。Qt::WA _DeleteOnClose属性是可以在QWidget上进行设置并用来影响这个窗口部件的行为的诸多标记之一。内存泄漏并不是必须处理的唯一问题。 最初的应用程序设计包含了一个隐含的假设,也就是它仅有一个主窗口。对于多窗口,每一个主窗口都有它自己的最近打开文件列表和它自己的一些
选项。很明显,最近打开文件列表对于整个应用程序来说应该是全局的。通过把recentFiles变量声明为静态变量,可以相当容易地解决这个问题,这样对于整个应用程序来说,会只存在一个该列表的实例。 但随后必须确保的是无论何时调用updateRecentFileActions()函数来更新File菜单,都必须是在所有的主窗口上调用它。这是实现这一做法的代码:
foreach(QWidget *win, QApplication::topLevelWidgets())
{
if(MainWindow *mainWin = qobject_ cast<MainWindow *>(win))
mainWin->updateRecentFileActions();
}
这段代码使用了Qt的foreach结构体(将在第11章中对其进行说明)来遍历这个应用程序的所有窗口,并且对所有类型为MainWindow的窗口部件调用updateRecentFileActions()。可以使用类似的代码来同步Show Grid和Auto-Recalculate 选项,或者用于确保同一个文件不会被加载两次。
在每一个主窗口中只提供一个文档的应用程序称为单文档界面( single document interface , SDI)应用程序。在Windows系统下,一种常用的替代方法是多文档界面(multiple document interface,MDI),这种应用程序只有一个单一的主窗口,但可以对主窗口*区域的多个文档窗口进行管理。Qt可以在它支持的所有平台上创建SDI和MDI应用程序。图3.17给出了使用这两种方法的Spreadsheet应用程序。第6章将对MDI进行说明。
3.8 程序启动画面
许多应用程序都会在启动的时候显示一个程序启动画面(splashi screen) ,图3.18给出的就是这样的一个实例。一些程序员使用程序启动画面对缓慢的启动过程进行掩饰,而另外一些人则是用于满足市场部门的要求。使用QSplashScreen类,可以非常容易地为Qt应用程序添加一个程序启动画面。
类QSplashScreen会在应用程序的主窗口出现之前显示一个图片。它也可以在这个图片上显示一些消息,用来通知用户有关应用程序初始化的过程。通常,程序启动画面的代码会放在main( )函数中,位于QApplication: :exec( )调用之前。下面给出了一个main()函数的例子,在应用程序中,它使用QSplashScreen显示的程序启动画面表示启动时载入的一些模块和网络连接的建立。
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSplashScreen *splash = new QSplashScreen;
splash->setPixmap(QPixmap(":/images/splash.png"));
splash->show();
Qt::Alignment topRight = Qt::AlignRight | Qt::AlignTop;
splash->showMessage(QObject::tr("Setting up the main window..."),topRight,Qt::white);
MainWindow mainWin;
splash->showMessage(QObject::tr("Loading modules..."),topRight,Qt::white);
loadModules();
splash->showMessage(QObject::tr("Establishing connections..."),topRight,Qt::white);
establishConnections();
mainWin.show();
splash->finish(&mainWin);
delete splash;
return a.exec();
}
到此为止,我们已经创建了Spreadsheet 应用程序的用户界面。在下一章中,将会通过实现电子制表软件的核心功能来完成这个应用程序。