Qt4_多文档界面

多文档界面

在主窗口的中央区域能够提供多个文档的那些应用程序就称
为多文档界面 (Multiple Document Interface, MDI) 应用程序,或者称为MDI 应用程序。在 Qt 中,通过把 QMdìArea类作为中央窗口部件,并且通过让每一个文挡窗口都成为这个 QMdìArea的子窗口部件,就可以创建一个多文档界面应用程序了。

对于多文挡界面应用程序有一个惯例,就是为它提供一个 Wìndow 菜单,这个菜单中包含一些管理这些窗口以及这些窗口列表的命令。激活窗口会使用一个选择标记标识出来。用户通过在Window 菜单中单击代表特定窗口的一项,就可以激活任何窗口。
Qt4_多文档界面_第1张图片
这个应用程序由两个类组成:MainWindow类和Editor类。在本书的例子程序中提供了它的代码,并且由于其中的绝大部分代码都与第一部分中的Spreadsheet应用程序相同或者相似,所以我们仅仅给出那些与多文档界面相关的代码。
Qt4_多文档界面_第2张图片

MainWindow.h

#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

MainWindow.cpp

#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中,并将其作为多文档界面窗口工作区中的一个子窗口。

Editor.h

#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

Editor.cpp

#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可以使用这个大小提示为窗口设定一个初始的尺寸大小值。

main.cpp

#include 

#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow mainWin;
    mainWin.show();
    return app.exec();
}

Qt4_多文档界面_第3张图片

你可能感兴趣的:(Qt)