现在我们开始实现Spreadsheet程序的main()函数:
include <QApplication>
include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
return app.exec();
}
这个main()函数和以前实现的稍有不同:我们在堆栈上创建了MainWindow实例。在程序中止的时候,MainWindow自动销毁。
使用以上的main()函数,Spreadsheet程序提供一个主窗口,一次只能处理一个文档。如果我们希望在同时处理多个文档,我们就要同时启动多个Spreadsheet程序。这对用户来说很不方便,他们更喜欢在一个应用程序实例中打开多个窗口,就如同一个web浏览器可以同时打开多个窗口一样。
为了处理多文档,我们需要对Spreadsheet进行一点修改。首先File菜单要进行修改:
- File|New 创建一个带有空文档的新的主窗口,而不是重新使用已经存在的主窗口。
- File|Close 关闭当前主窗口。
- File|Exit 关闭所有窗口。
在原来的程序版本中没有Close菜单项,因为这个菜单项和Exit时一样。
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow *mainWin = new MainWindow;
mainWin->show();
return app.exec();
}
由于有了多个主窗口,我们需要用new创建MainWindow实例,这样当保存后就可以使用delete删除MainWindow实例。
槽函数MainWindow::newFile()要改成这样:
void MainWindow::newFile()
{
MainWindow *mainWin = new MainWindow;
mainWin->show();
}
我们只是简单的创建一个新的MainWindow实例。奇怪的是我们没有保存新窗口的指针,这是因为Qt会为我们记录所有窗口的地址。
在MainWindow::createActions()中,我们需要Close和Exit行为:
void MainWindow::createActions()
{
...
closeAction = new QAction(tr("&Close"), this);
closeAction->setShortcut(tr("Ctrl+W"));
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, SIGNAL(triggered()),
qApp, SLOT(closeAllWindows()));
...
}
函数QApplication::closeAllWindows()关闭所有应用程序的窗口,除非有些窗口拒绝了这个关闭事件。这个功能是很需要的。因为只要关闭一个窗口,MainWindow::closeEvent()就会调用,这样我们就不用担心有些文档没有存盘。
到现在,看起来我们的程序已经能够处理多文档窗口了。但是还有一个隐藏的问题:如果用户不停的创建关闭主窗口,那么机器最终会耗尽所有内存。因为我们在newFile()中不停的创建主窗口 但是却没有删除它。用户关闭一个窗口只是把它隐藏,窗口实例一致在内存中。在内存中不用的主窗口越来越多,这个问题就很严重了。
解决这个问题很简单,在构造函数中我们把窗口属性设置为Qt::WA_DeleteOnClose就可以了:
MainWindow::MainWindow()
{
...
setAttribute(Qt::WA_DeleteOnClose);
...
}
这样Qt就在窗口关闭的同时销毁它。QWidgets有许多可以影响行为的属性,Qt::WA_DeleteOnClose只是其中的一个。
内存泄漏只是我们需要处理的问题之一。在我们的原来程序设计中,我们假定只有一个主窗口。如果创建了多个窗口,那么每一个窗口都已个最近打开的文档和自己的设置。很明显,最近打开的文档应该是对程序全局有效的。我们可以声明recentFiles为静态变量,这样在整个程序运行期间就只有一份拷贝。这样我们就需要在调用updateRecentFilesActions()函数时,所有的主窗口都要调用,代码实现如下:
foreach (QWidget *win, QApplication::topLevelWidgets()) {
if (MainWindow *mainWin = qobject_cast<MainWindow *>(win))
mainWin->updateRecentFileActions();
}
上面的代码用到了Qt的foreach(这将在第11章介绍)遍历所有程序窗口,类型为MainWindow的窗口全部调用updateRecentFilesActions()。选项ShowGrid和AutoRecalculate也要这样处理进行同步,确保同一个文件不会调用两次。
一个主窗口只能处理一个文档的程序称为SDI(single document interface)程序。能处理多个文档的程序称之为MDI(Multiple document interface)程序。Qt能够在所有操作系统平台上支持SDI和MDI程序。