在主窗口的中央区域能够提供多个文档的那些应用程序就称
为多文档界面 (Multiple Document Interface, MDI) 应用程序,或者称为MDI 应用程序。在 Qt 中,通过把 QMdìArea类作为中央窗口部件,并且通过让每一个文挡窗口都成为这个 QMdìArea的子窗口部件,就可以创建一个多文档界面应用程序了。
对于多文挡界面应用程序有一个惯例,就是为它提供一个 Wìndow 菜单,这个菜单中包含一些管理这些窗口以及这些窗口列表的命令。激活窗口会使用一个选择标记标识出来。用户通过在Window 菜单中单击代表特定窗口的一项,就可以激活任何窗口。
这个应用程序由两个类组成:MainWindow类和Editor类。在本书的例子程序中提供了它的代码,并且由于其中的绝大部分代码都与第一部分中的Spreadsheet应用程序相同或者相似,所以我们仅仅给出那些与多文档界面相关的代码。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
class QAction;
class QActionGroup;
class QLabel;
class QMdiArea;
class QMenu;
class QToolBar;
class Editor;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
public slots:
void newFile();
void openFile(const QString &fileName);
protected:
void closeEvent(QCloseEvent *event);
private slots:
void open();
void save();
void saveAs();
void cut();
void copy();
void paste();
void about();
void updateActions();
void loadFiles();
private:
void createActions();
void createMenus();
void createToolBars();
void createStatusBar();
void addEditor(Editor *editor);
Editor *activeEditor();
QMdiArea *mdiArea;
QLabel *readyLabel;
QWidgetList windows;
QMenu *fileMenu;
QMenu *editMenu;
QMenu *windowMenu;
QMenu *helpMenu;
QToolBar *fileToolBar;
QToolBar *editToolBar;
QActionGroup *windowActionGroup;
QAction *newAction;
QAction *openAction;
QAction *saveAction;
QAction *saveAsAction;
QAction *exitAction;
QAction *cutAction;
QAction *copyAction;
QAction *pasteAction;
QAction *closeAction;
QAction *closeAllAction;
QAction *tileAction;
QAction *cascadeAction;
QAction *nextAction;
QAction *previousAction;
QAction *separatorAction;
QAction *aboutAction;
QAction *aboutQtAction;
};
#endif
#include
#include "editor.h"
#include "mainwindow.h"
MainWindow::MainWindow()
{
mdiArea = new QMdiArea;
setCentralWidget(mdiArea);
connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),
this, SLOT(updateActions()));
createActions();
createMenus();
createToolBars();
createStatusBar();
setWindowIcon(QPixmap(":/images/icon.png"));
setWindowTitle(tr("MDI Editor"));
QTimer::singleShot(0, this, SLOT(loadFiles()));
}
void MainWindow::loadFiles()
{
QStringList args = QApplication::arguments();
args.removeFirst();
if (!args.isEmpty()) {
foreach (QString arg, args)
openFile(arg);
mdiArea->cascadeSubWindows();
} else {
newFile();
}
mdiArea->activateNextSubWindow();
}
void MainWindow::newFile()
{
Editor *editor = new Editor;
editor->newFile();
addEditor(editor);
}
void MainWindow::openFile(const QString &fileName)
{
Editor *editor = Editor::openFile(fileName, this);
if (editor)
addEditor(editor);
}
void MainWindow::closeEvent(QCloseEvent *event)
{
mdiArea->closeAllSubWindows();
if (!mdiArea->subWindowList().isEmpty()) {
event->ignore();
} else {
event->accept();
}
}
void MainWindow::open()
{
Editor *editor = Editor::open(this);
if (editor)
addEditor(editor);
}
void MainWindow::save()
{
if (activeEditor())
activeEditor()->save();
}
void MainWindow::saveAs()
{
if (activeEditor())
activeEditor()->saveAs();
}
void MainWindow::cut()
{
if (activeEditor())
activeEditor()->cut();
}
void MainWindow::copy()
{
if (activeEditor())
activeEditor()->copy();
}
void MainWindow::paste()
{
if (activeEditor())
activeEditor()->paste();
}
void MainWindow::about()
{
QMessageBox::about(this, tr("About MDI Editor"),
tr("Editor 1.1
"
"Copyright © 2007 Software Inc."
"MDI Editor is a small application that demonstrates "
"QMdiArea."));
}
void MainWindow::updateActions()
{
bool hasEditor = (activeEditor() != 0);
bool hasSelection = activeEditor()
&& activeEditor()->textCursor().hasSelection();
saveAction->setEnabled(hasEditor);
saveAsAction->setEnabled(hasEditor);
cutAction->setEnabled(hasSelection);
copyAction->setEnabled(hasSelection);
pasteAction->setEnabled(hasEditor);
closeAction->setEnabled(hasEditor);
closeAllAction->setEnabled(hasEditor);
tileAction->setEnabled(hasEditor);
cascadeAction->setEnabled(hasEditor);
nextAction->setEnabled(hasEditor);
previousAction->setEnabled(hasEditor);
separatorAction->setVisible(hasEditor);
if (activeEditor())
activeEditor()->windowMenuAction()->setChecked(true);
}
void MainWindow::createActions()
{
newAction = new QAction(tr("&New"), this);
newAction->setIcon(QIcon(":/images/new.png"));
newAction->setShortcut(QKeySequence::New);
newAction->setStatusTip(tr("Create a new file"));
connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
openAction = new QAction(tr("&Open..."), this);
openAction->setIcon(QIcon(":/images/open.png"));
openAction->setShortcut(QKeySequence::Open);
openAction->setStatusTip(tr("Open an existing file"));
connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
saveAction = new QAction(tr("&Save"), this);
saveAction->setIcon(QIcon(":/images/save.png"));
saveAction->setShortcut(QKeySequence::Save);
saveAction->setStatusTip(tr("Save the file to disk"));
connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
saveAsAction = new QAction(tr("Save &As..."), this);
saveAsAction->setStatusTip(tr("Save the file under a new name"));
connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs()));
exitAction = new QAction(tr("E&xit"), this);
exitAction->setShortcut(tr("Ctrl+Q"));
exitAction->setStatusTip(tr("Exit the application"));
connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
cutAction = new QAction(tr("Cu&t"), this);
cutAction->setIcon(QIcon(":/images/cut.png"));
cutAction->setShortcut(QKeySequence::Cut);
cutAction->setStatusTip(tr("Cut the current selection to the "
"clipboard"));
connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
copyAction = new QAction(tr("&Copy"), this);
copyAction->setIcon(QIcon(":/images/copy.png"));
copyAction->setShortcut(QKeySequence::Copy);
copyAction->setStatusTip(tr("Copy the current selection to the "
"clipboard"));
connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
pasteAction = new QAction(tr("&Paste"), this);
pasteAction->setIcon(QIcon(":/images/paste.png"));
pasteAction->setShortcut(QKeySequence::Paste);
pasteAction->setStatusTip(tr("Paste the clipboard's contents at "
"the cursor position"));
connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
closeAction = new QAction(tr("Cl&ose"), this);
closeAction->setShortcut(QKeySequence::Close);
closeAction->setStatusTip(tr("Close the active window"));
connect(closeAction, SIGNAL(triggered()),
mdiArea, SLOT(closeActiveSubWindow()));
closeAllAction = new QAction(tr("Close &All"), this);
closeAllAction->setStatusTip(tr("Close all the windows"));
connect(closeAllAction, SIGNAL(triggered()), this, SLOT(close()));
tileAction = new QAction(tr("&Tile"), this);
tileAction->setStatusTip(tr("Tile the windows"));
connect(tileAction, SIGNAL(triggered()),
mdiArea, SLOT(tileSubWindows()));
cascadeAction = new QAction(tr("&Cascade"), this);
cascadeAction->setStatusTip(tr("Cascade the windows"));
connect(cascadeAction, SIGNAL(triggered()),
mdiArea, SLOT(cascadeSubWindows()));
nextAction = new QAction(tr("Ne&xt"), this);
nextAction->setShortcut(QKeySequence::NextChild);
nextAction->setStatusTip(tr("Move the focus to the next window"));
connect(nextAction, SIGNAL(triggered()),
mdiArea, SLOT(activateNextSubWindow()));
previousAction = new QAction(tr("Pre&vious"), this);
previousAction->setShortcut(QKeySequence::PreviousChild);
previousAction->setStatusTip(tr("Move the focus to the previous "
"window"));
connect(previousAction, SIGNAL(triggered()),
mdiArea, SLOT(activatePreviousSubWindow()));
separatorAction = new QAction(this);
separatorAction->setSeparator(true);
aboutAction = new QAction(tr("&About"), this);
aboutAction->setStatusTip(tr("Show the application's About box"));
connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
aboutQtAction = new QAction(tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show the Qt library's About box"));
connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
windowActionGroup = new QActionGroup(this);
}
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(newAction);
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);
fileMenu->addAction(saveAsAction);
fileMenu->addSeparator();
fileMenu->addAction(exitAction);
editMenu = menuBar()->addMenu(tr("&Edit"));
editMenu->addAction(cutAction);
editMenu->addAction(copyAction);
editMenu->addAction(pasteAction);
windowMenu = menuBar()->addMenu(tr("&Window"));
windowMenu->addAction(closeAction);
windowMenu->addAction(closeAllAction);
windowMenu->addSeparator();
windowMenu->addAction(tileAction);
windowMenu->addAction(cascadeAction);
windowMenu->addSeparator();
windowMenu->addAction(nextAction);
windowMenu->addAction(previousAction);
windowMenu->addAction(separatorAction);
menuBar()->addSeparator();
helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAction);
helpMenu->addAction(aboutQtAction);
}
void MainWindow::createToolBars()
{
fileToolBar = addToolBar(tr("File"));
fileToolBar->addAction(newAction);
fileToolBar->addAction(openAction);
fileToolBar->addAction(saveAction);
editToolBar = addToolBar(tr("Edit"));
editToolBar->addAction(cutAction);
editToolBar->addAction(copyAction);
editToolBar->addAction(pasteAction);
}
void MainWindow::createStatusBar()
{
readyLabel = new QLabel(tr(" Ready"));
statusBar()->addWidget(readyLabel, 1);
}
void MainWindow::addEditor(Editor *editor)
{
connect(editor, SIGNAL(copyAvailable(bool)),
cutAction, SLOT(setEnabled(bool)));
connect(editor, SIGNAL(copyAvailable(bool)),
copyAction, SLOT(setEnabled(bool)));
QMdiSubWindow *subWindow = mdiArea->addSubWindow(editor);
windowMenu->addAction(editor->windowMenuAction());
windowActionGroup->addAction(editor->windowMenuAction());
subWindow->show();
}
Editor *MainWindow::activeEditor()
{
QMdiSubWindow *subWindow = mdiArea->activeSubWindow();
if (subWindow)
return qobject_cast<Editor *>(subWindow->widget());
return 0;
}
MainWindow()
在MainWíndow构造函数中,创建一个QMdiArea窗口部件,并且让它成为中央窗口部件。我们把QMdiArea的subWindowActivated()信号与将要用来保持更新Window菜单的槽连接起来,并且会根据应用程序的状态启用或者禁用那些动作。
在构造函数的最后,我们把单触发定时器的时间间隔设置为0毫秒,以调用loadFiles()函数。对于这样的定时器,只要事件循环一空闲就会触发它。实际上,这意味着只要构造函数结束,同时在主窗口显示出来之后,它就会调用loadFiles()。如果不这样做,而且如果还需要加载许多大文件的话,那么该构造函数在文件加载完毕之前就无法结束。在此期间,用户极有可能在屏幕上看不到任何东西,这样他可能会认为是应用程序启动失败了。
loadFiles()
如果用户在命令行中启动该应用程序时使用了一个或者多个文件名,那么这个函数就会试图加载每一个文件,并且会按照逐级层叠的方式显示这些子窗口,以便用户可以轻松地看到它们。Qt的一些特殊命令行选项,比如-style和-font,QApplication 的构造函数会自动把它们从参数列表中剔除出去。因此,如果在命令行中写成:mdieditor -style motif readme.txt
QApplication::arguments()就会返回一个含有两个项(“mdieditor"和"readme.txt”)的QStringList,那么MDIEditor应用程序就会在启动的时候打开readme.txt。
如果在命令行中没有指定文件,就会创建一个空的编辑器子窗口,以使用户可以方便地直接开始输入。对于activateNextSubWindow()的调用意味着对编辑器窗口赋予焦点,并且可以确保调用updateActions()函数来更新Wíndow菜单,以及根据应用程序的状态来启用和禁用一些动作。
newFile()
newFile()槽对File->New菜单项做出响应。它创建一个Editor窗口部件,并且把它传递给addEditor()私有函数。
open()
open()函数对File Open菜单项做出响应。它调用 Editor::open()函数,该函数会弹出一个文件对话框。如果用户选择了一个文件,就会创建一个新的Editor,读入文件的文本内容,并且如果读取成功,就会返回一个指向Editor的指针。如果用户取消了该文件对话框,或者如果读取失败,就会返回一个空指针并通知用户出错。在Editor类中实现文件操作要比在MainWindow类中实现文件操作更有意义,因为每个Editor都需要维护它自己的独立状态。
addEditor()
addEditor()私有函数会从newFile()和open()中得到调用,以完成一个新的Editor窗口部件的初始化。它以创建两个信号-槽的连接开始。根据是否有选中的任意文本,这两个连接可以确保Edit->Cut和Edit->Copy菜单的启用或者禁用。
因为我们正在使用的是一个多文档界面,所以完全有可能同时使用多个Editor窗口部件。这一点之所以需要关注,是因为我们只对来自激活的Editor窗口的copyAvailable(bool)信号做出响应,而不会对其他的Editor窗口做出响应。但由于只能由激活的窗口发射这些信号;所以,实际上这并不算什么问题。
QMdiArea::addSubWindow()函数创建一个新的QMdiSubWindow,把作为参数传递的该窗口部件放进子窗口中,并且返回该子窗口。接下来,我们创建一个QAction,由其代表Window菜单中的一个窗口。该动作由Editor类提供,后面会说明这个类。我们还会在一个QActionGroup对象中添加该动作。 QActionGroup对象可以确保每个时刻只能选中Window菜单项中的一项。最后,对新的QMdiSubWindow调用show(),使其可见。
save()
如果存在激活的编辑器save()槽就会对它调用Editor::save()。同样,执行真正工作的代码就放在Editor类中。
activeEditor()
activeEditor()私有函敛返回一个类型为Editor的指针,它指向当前激活的子窗口;或者在没有激活的子窗口时,返回一个空指针。
cut()
cut()槽可以对激活的编辑器调用Editor::cut() 。由于copy()槽和paste()槽都与cut()槽遵循相同的模式,所以没有再给出它们的代码。
updateAction()
每当一个新的子窗口成为激活窗口,或者在关闭最后一个子窗口时,都会发射subWindowActivated()信号。在后一种情况下,它的参数就是一个空指针。这个信号会连接到updateActions()槽。
只有在存在激活窗口时,绝大多数的菜单项才会起作用。如果不存在激活窗口,就可以禁用它们。最后,我们在 QAction上调用setChecked(),表示这是一个激活窗口。由于有了QActíonGroup的帮助,就不必再明确地对前面的激活窗口进行解除选定操作了。
createMenus()
createMenus()私有函数刽主这些动作和 Window菜单一起配合工作。所有这些动作都是一些这种典型的菜单项,并且可以很容易地使用 QMidArea的closeActiveSubWindow()、closeAllSubWindows()、tileSubWindows()和cascadeSubWindows()槽来实现它们。每当打开一个新窗口时,都会把该动作添加到Window菜单的动作列表中。当用户关闭编辑器窗口时,就会删除它在Window菜单中的对应动作(因为该编辑器窗口拥有这个动作) ,因而就会自动从 Window 菜单中移除这个动作。
closeEvent()
重新实现closeEvent()函数来关闭所有的子窗口,从而使得每个子窗口都要接收一个关闭事件。如果这些子窗口的其中之一"忽略"了它的关问事件(可能是因为用户撤销了一个"未保存变化"的消息框) ,那么也就忽略对MainWindow的关闭事件;否则,我们就接受它,这样的结果就是Qt会关闭整个应用程序。如果没有在 MainWindow中重新实现它的closeEvent(),那么将不会给用户留下对那些"未保存变化"进行存储的任何机会。
现在已经完成了对MainWindow窗口的查看,因而就可以转到对Editor的实现上。Editor类表示一个子窗口,它继承了QTextEdit,而QTextEdit提供了文本编辑功能。在实际的应用程序中,如果需要一个代码编辑的组件,也许会考虑使用Scintilla,可以从http://www.riverbankcomputing.co.uk/qscintilla/中获取专门用于Qt的组件Scintilla。
就像任何Qt窗口部件都可以作为一个单独的窗口一样,任何Qt窗口部件都可以放进QMid-SubWindow中,并将其作为多文档界面窗口工作区中的一个子窗口。
#ifndef EDITOR_H
#define EDITOR_H
#include
class Editor : public QTextEdit
{
Q_OBJECT
public:
Editor(QWidget *parent = 0);
void newFile();
bool save();
bool saveAs();
QSize sizeHint() const;
QAction *windowMenuAction() const { return action; }
static Editor *open(QWidget *parent = 0);
static Editor *openFile(const QString &fileName,
QWidget *parent = 0);
protected:
void closeEvent(QCloseEvent *event);
private slots:
void documentWasModified();
private:
bool okToContinue();
bool saveFile(const QString &fileName);
void setCurrentFile(const QString &fileName);
bool readFile(const QString &fileName);
bool writeFile(const QString &fileName);
QString strippedName(const QString &fullFileName);
QString curFile;
bool isUntitled;
QAction *action;
};
#endif
#include
#include "editor.h"
Editor::Editor(QWidget *parent)
: QTextEdit(parent)
{
action = new QAction(this);
action->setCheckable(true);
connect(action, SIGNAL(triggered()), this, SLOT(show()));
connect(action, SIGNAL(triggered()), this, SLOT(setFocus()));
isUntitled = true;
connect(document(), SIGNAL(contentsChanged()),
this, SLOT(documentWasModified()));
setWindowIcon(QPixmap(":/images/document.png"));
setWindowTitle("[*]");
setAttribute(Qt::WA_DeleteOnClose);
}
void Editor::newFile()
{
static int documentNumber = 1;
curFile = tr("document%1.txt").arg(documentNumber);
setWindowTitle(curFile + "[*]");
action->setText(curFile);
isUntitled = true;
++documentNumber;
}
bool Editor::save()
{
if (isUntitled) {
return saveAs();
} else {
return saveFile(curFile);
}
}
bool Editor::saveAs()
{
QString fileName =
QFileDialog::getSaveFileName(this, tr("Save As"), curFile);
if (fileName.isEmpty())
return false;
return saveFile(fileName);
}
QSize Editor::sizeHint() const
{
return QSize(72 * fontMetrics().width('x'),
25 * fontMetrics().lineSpacing());
}
Editor *Editor::open(QWidget *parent)
{
QString fileName =
QFileDialog::getOpenFileName(parent, tr("Open"), ".");
if (fileName.isEmpty())
return 0;
return openFile(fileName, parent);
}
Editor *Editor::openFile(const QString &fileName, QWidget *parent)
{
Editor *editor = new Editor(parent);
if (editor->readFile(fileName)) {
editor->setCurrentFile(fileName);
return editor;
} else {
delete editor;
return 0;
}
}
void Editor::closeEvent(QCloseEvent *event)
{
if (okToContinue()) {
event->accept();
} else {
event->ignore();
}
}
void Editor::documentWasModified()
{
setWindowModified(true);
}
bool Editor::okToContinue()
{
if (document()->isModified()) {
int r = QMessageBox::warning(this, tr("MDI Editor"),
tr("File %1 has been modified.\n"
"Do you want to save your changes?")
.arg(strippedName(curFile)),
QMessageBox::Yes | QMessageBox::No
| QMessageBox::Cancel);
if (r == QMessageBox::Yes) {
return save();
} else if (r == QMessageBox::Cancel) {
return false;
}
}
return true;
}
bool Editor::saveFile(const QString &fileName)
{
if (writeFile(fileName)) {
setCurrentFile(fileName);
return true;
} else {
return false;
}
}
void Editor::setCurrentFile(const QString &fileName)
{
curFile = fileName;
isUntitled = false;
action->setText(strippedName(curFile));
document()->setModified(false);
setWindowTitle(strippedName(curFile) + "[*]");
setWindowModified(false);
}
bool Editor::readFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("MDI Editor"),
tr("Cannot read file %1:\n%2.")
.arg(file.fileName())
.arg(file.errorString()));
return false;
}
QTextStream in(&file);
QApplication::setOverrideCursor(Qt::WaitCursor);
setPlainText(in.readAll());
QApplication::restoreOverrideCursor();
return true;
}
bool Editor::writeFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("MDI Editor"),
tr("Cannot write file %1:\n%2.")
.arg(file.fileName())
.arg(file.errorString()));
return false;
}
QTextStream out(&file);
QApplication::setOverrideCursor(Qt::WaitCursor);
out << toPlainText();
QApplication::restoreOverrideCursor();
return true;
}
QString Editor::strippedName(const QString &fullFileName)
{
return QFileInfo(fullFileName).fileName();
}
Editor()
首先,创建一个QAction,用它表示应用程序Window菜单中的编辑器,并且把这个动作与show()和setFocus()槽连接起来。
由于允许用户创建任意数量的编辑器窗口,所以必须为它们预备一些名字,这样就可以在第一次保存这些窗口之前把它们区分开来。处理这种情况的一种常用方式是分配一个包含数字的名称(例如,document.txt) 。我们使用一个isUntitled变量来区分是用户提供的名称还是程序自动创建的名称。
我们把文本文档的contentsChanged()信号连接到documentWasModified()私有槽上。这个槽只会简单地调用 setWindowModified(true)。
最后,为了防止用户关闭Editor窗口时出现内存泄漏,需要设置Qt::WA_DeleteOnClose属性。
newFile()
newFile()函数会为新的文档产生一个像document.txt这样的名称。由于当在一个新创建的Editor中调用open()而打开一个已经存在的文档时,我们并不想白白浪费一个数字,因而把这段代码放在了newFile()中,而不是放在构造函数中。由于 documentNumber声明为静态变量,所以它可以被所有的Editor实例共享。
在窗口标题中的"[*]"标记是一种位置标记符,在除Mac OS X系统平台之外的文件中,当存在未保存的变化时,我们就希望能够让这个星号出现。而在Ma OS X系统中,未保存的文档会在它们的窗口关闭按钮中出现一个点。
除了创建一些新文件之外,用户通常希望打开一些已经存在的文件,即从文件对话框中打开已有文件,或者从文件列表中打开已有文件。比如,从最近打开文件列表中选择一些文件打开。为了支持这些用法,Qt 提供了两个静态函数:open()用于从文件系统中选择一个文件的名字,而openFile()用于创建一个Editor并且读入一个指定文件的内容。
open()
静态函数open()会弹出一个文件对话框,用户可以通过它选择一个文件。如果选择了文件,就会调用openFile()创建一个Editor并且读入该文件的内容。
openFile()
这个openFile()静态函数从创建新的Editor窗口部件开始,然后会试着读入指定的文件。如果读入成功,就会返回该Editor否则,用户就会得到出现问题的信息[在readFile()中],删除该编辑器,并且返回一个空指针。
save()
save()函数使用isUntitled变量来决定它是应该调用saveFile()还是应该调用saveAs()。
closeEvent()
这里重新实现了close::Event()函数,它允许用户保存那些未保存的变化。这部分逻辑判断代码放在okToContinue()函数中,该函数可以弹出一个消息框,询问 “Do you want to save your changes?”(你想保存你的改变吗?)。如果okToContinue()函数返回true,就接受这个关闭事件;否则,就"忽略"它,并且让这个窗口不受其影响。
setCurrentFile()
setCurrentFile()函数会在openFile()和saveFile()中得到调用,用来更新curFile和isUntitled变量,设置窗口的标题和动作的文本,以及用来把这个文挡的"modified" 标记设置成false 。一旦用户修改了编辑器中的文本,底层的QTextDocument就会发射contentsChanged ()信号,并且把它内部的"modified"标记设置成true。
sizeHint()
最后,根据字符"x" 的宽度和文本行的高度, sizeHint()函数可以返回一个大小提示。 QMdiArea可以使用这个大小提示为窗口设定一个初始的尺寸大小值。
#include
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
return app.exec();
}