# QT学习笔记之八 MDIEditor

- URL: https://www.yidoo.xyz/qt-study-notes-eight-mdieditor
- 作者: 核动力蜗牛
- 发布时间: 2009-07-01
- 更新时间: 2009-07-01
- 分类: 技术笔记
- 标签: qt

这个程序由于期末考试搁置很久了，下午由于下大雨没出去，就把他完成了哈.....主要实现多文档编辑功能，测试了下，基本上没有 bug，由于整体设计思路参考书上的，大牛们不要 BS 我阿.....

等下次有时间将他改成简易的代码编辑器好了，貌似国外网站上有相关模块，有时间去看看。。。。

---

程序部分代码如下：

**editor.h**

```cpp
#ifndef EDITOR_H
#define EDITOR_H
#include <QTextEdit>
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_H
```

**editor.cpp**

```cpp
#include <QtGui>
#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);  //Makes Qt delete this widget when the widget has accepted the close event
}
/**
  新建文件
*/
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());  //Returns the font metrics for the widget's current font
}
/**
  打开
*/
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").arg(strippedName(curFile),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel));
        if(r == QMessageBox::Yes) {
            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();
}
```

_mainwindow.h_

```cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
class QAction;
class QActionGroup;
class QLabel;
class QMdiArea;
class QMenu;
class QToolBar;
class Editor;
namespace Ui
{
    class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~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:
    Ui::MainWindow *ui;
    void createActions();
    void createMenus();
    void createToolBars();
    void createStatusBar();
    void addEditor(Editor *editor);
    Editor *activeEditor();
    QMdiArea *mdiArea;    //The QMdiArea widget provides an area in which MDI windows are displayed
    QLabel *readyLabel;
    QWidgetList windows;
    //Menu
    QMenu *fileMenu;
    QMenu *editMenu;
    QMenu *windowMenu;
    QMenu *helpMenu;
    //ToolBar
    QToolBar *fileToolBar;
    QToolBar *editToolBar;
    //Actions
    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_H
```

**mainwindos.cpp**

```cpp
#include <QtGui>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "editor.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mdiArea = new QMdiArea;
    setCentralWidget(mdiArea);
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),this, SLOT(updateActions()));
    createActions();
    createMenus();
    createToolBars();
    createStatusBar();
    setWindowIcon(QPixmap(":/images/icon.png"));
    setWindowTitle("Killua MDI Editor");
    QTimer::singleShot(0,this,SLOT(loadFiles()));
}
MainWindow::~MainWindow()
{
    delete ui;
}
/**
   文件读取
  */
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 Killua MDI Editor"),
                       tr("Design by Killua"));
}
/**
  事件更新
  */
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->setEnabled(hasEditor);
    if(activeEditor())
        activeEditor()->windowMenuAction()->setChecked(true);  //菜单栏可点击
}
/**
  事件创建
  */
void MainWindow::createActions()
{
    //newAction
    newAction = new QAction(tr("&New"), this);
    newAction->setIcon(QIcon(":/images/new.png"));
    newAction->setShortcut(QKeySequence::New);
    connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
    //openAction
    openAction = new QAction(tr("&Open..."), this);
    openAction->setIcon(QIcon(":/images/open.png"));
    openAction->setShortcut(QKeySequence::Open);
    connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
    //saveAction
    saveAction = new QAction(tr("&Save"), this);
    saveAction->setIcon(QIcon(":/images/save.png"));
    saveAction->setShortcut(QKeySequence::Save);
    connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
    //saveAsAction
    saveAsAction = new QAction(tr("Save &As..."), this);
    connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs()));
    //exitAction
    exitAction = new QAction(tr("E&xit"), this);
    exitAction->setShortcut(tr("Ctrl+Q"));
    connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
    //cutAction
    cutAction = new QAction(tr("Cu&t"), this);
    cutAction->setIcon(QIcon(":/images/cut.png"));
    cutAction->setShortcut(QKeySequence::Cut);
    connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
    //copyAction
    copyAction = new QAction(tr("&Copy"), this);
    copyAction->setIcon(QIcon(":/images/copy.png"));
    copyAction->setShortcut(QKeySequence::Copy);
    connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
    //pasteAction
    pasteAction = new QAction(tr("&Paste"), this);
    pasteAction->setIcon(QIcon(":/images/paste.png"));
    pasteAction->setShortcut(QKeySequence::Paste);
    connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
    //closeAction
    closeAction = new QAction(tr("Cl&ose"), this);
    closeAction->setShortcut(QKeySequence::Close);
    connect(closeAction, SIGNAL(triggered()),mdiArea, SLOT(closeActiveSubWindow()));
    //closeAllAction
    closeAllAction = new QAction(tr("Close &All"), this);
    connect(closeAllAction, SIGNAL(triggered()), this, SLOT(close()));
    //tileAction
    tileAction = new QAction(tr("&Tile"), this);
    connect(tileAction, SIGNAL(triggered()),mdiArea, SLOT(tileSubWindows()));
    //cacadeAction
    cascadeAction = new QAction(tr("&Cascade"), this);
    connect(cascadeAction, SIGNAL(triggered()),mdiArea, SLOT(cascadeSubWindows()));
    //newAction
    nextAction = new QAction(tr("Ne&xt"), this);
    nextAction->setShortcut(QKeySequence::NextChild);
    connect(nextAction, SIGNAL(triggered()),mdiArea, SLOT(activateNextSubWindow()));
    //previousAction
    previousAction = new QAction(tr("Pre&vious"), this);
    previousAction->setShortcut(QKeySequence::PreviousChild);
    connect(previousAction, SIGNAL(triggered()),mdiArea, SLOT(activatePreviousSubWindow()));
    //separatorAction
    separatorAction = new QAction(this);
    separatorAction->setSeparator(true);
    //aboutAction
    aboutAction = new QAction(tr("&About"), this);
    aboutAction->setStatusTip(tr("Show the application's About box"));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
    //aboutQtAction
    aboutQtAction = new QAction(tr("About &Qt"), this);
    connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
    windowActionGroup = new QActionGroup(this);
}
/**
  菜单栏创建
  */
void MainWindow::createMenus()
{
    //fileMenu
    fileMenu = menuBar()->addMenu("&File");
    fileMenu->addAction(newAction);
    fileMenu->addAction(openAction);
    fileMenu->addAction(saveAction);
    fileMenu->addAction(saveAsAction);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAction);
    //editMenu
    editMenu = menuBar()->addMenu(tr("&Edit"));
    editMenu->addAction(cutAction);
    editMenu->addAction(copyAction);
    editMenu->addAction(pasteAction);
    windowMenu=menuBar()->addMenu(tr("&Windos"));
    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);
}
/**
  添加Editor
  */
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());
    subWindow->show();
}
/**
  激活窗口
  */
Editor *MainWindow::activeEditor()
{
    QMdiSubWindow *subWindow = mdiArea->activeSubWindow();
    if(subWindow)
        return qobject_cast<Editor *>(subWindow->widget());
    return 0;
}
```

_PS:完整源代码放到资源里面了，附上链接......_

http://download.csdn.net/source/1458294

---

放假了...无聊中....
