• Qt MDI及其使用方法(详解版)


    统的应用程序设计中有多文档界面(Multi-document Interface,MDI)应用程序,Qt 为设计 MDI 应用程序提供了支持。

    本节的实例 samp6_4 是一个 MDI 应用程序,程序运行效果如图 1 所示。


    MDI 应用程序实例 samp6_4 的运行时界面
    图 1 MDI 应用程序实例 samp6_4 的运行时界面


    MDI 应用程序就是在主窗口里创建多个同类型的 MDI 子窗口,这些 MDI 子窗口在主窗口里显示,并共享主窗口上的工具栏和菜单等操作功能,主窗口上的操作都针对当前活动的 MDI 子窗口进行。

    设计 MDI 应用程序需要在主窗口工作区放置一个 QMdiArea 作为子窗体的容器。实例 samp6_4 主窗口的工作区使用一个 QMdiArea 组件,实例的子窗口类是 QFormDoc,是一个使用 QPlainTextEdit 进行简单文本显示和编辑的窗体。

    创建的 QFormDoc 窗体对象作为一个子窗口加入到 mdiArea 组件中。QMdiArea 组件类似于实例 samp6_3 中主窗口上的 tabWidget 组件,只是 QMdiArea 提供更加完备的功能。更改 MDI 的显示模式,可以得到与实例 samp6_3 相似的以多页组件管理的 MDI 界面效果。

    文档窗口类QFormDoc的设计

    以可视化方式创建一个基于 QWidget 的类 QFormDoc,设计可视化界面时,只放置一个 QPlainTextEdit 组件,并以水平布局填充满整个窗口。这里不再用可视化的方式设计 Action,因为 QFormDoc 窗口不需要创建自己的工具栏,而是使用主窗口上的工具栏按钮对 QFormDoc 窗体上的 QPlainTextEdit 组件进行操作。

    为 QFormDoc 添加一些用于文件打开和编辑操作的接口函数,QFormDoc 类的完整定义如下:

    1. class QFormDoc : public QWidget
    2. {
    3. Q_OBJECT
    4. private:
    5. QString mCurrentFile; //当前文件
    6. bool mFileOpened=false; //文件已打开
    7. public:
    8. explicit QFormDoc(QWidget *parent = 0);
    9. ~QFormDoc();
    10. void loadFromFile(QString& aFileName); //打开文件
    11. QString currentFileName();//返回当前文件名
    12. bool isFileOpened();//文件已经打开
    13. void setEditFont();//设置字体
    14. void textCut(); //cut
    15. void textCopy(); //copy
    16. void textPaste(); //paste
    17. private:
    18. Ui::QFormDoc *ui;
    19. };

    这些接口函数是为了在主窗口里调用,实现对 MDI 子窗口的操作。实现代码如下:

    1. QFormDoc::QFormDoc(QWidget *parent) :
    2. QWidget(parent),
    3. ui(new Ui::QFormDoc)
    4. {
    5. ui->setupUi(this);
    6. this->setWindowTitle("New Doc"); //窗口标题
    7. this->setAttribute(Qt::WA_DeleteOnClose); //关闭时自动删除
    8. }
    9. QFormDoc::~QFormDoc()
    10. {
    11. //QMessageBox::information(this,"信息","文档窗口被释放");
    12. delete ui;
    13. }
    14. void QFormDoc::loadFromFile(QString &aFileName)
    15. {//打开文件
    16. QFile aFile(aFileName); //以文件方式读出
    17. if (aFile.open(QIODevice::ReadOnly | QIODevice::Text)) //以只读文本方式打开文件
    18. {
    19. QTextStream aStream(&aFile); //用文本流读取文件
    20. ui->plainTextEdit->clear();//清空
    21. ui->plainTextEdit->setPlainText(aStream.readAll()); //读取文本文件
    22. aFile.close();//关闭文件
    23. mCurrentFile=aFileName;//保存当前文件名
    24. QFileInfo fileInfo(aFileName); //文件信息
    25. QString str=fileInfo.fileName(); //去除路径后的文件名
    26. this->setWindowTitle(str);
    27. mFileOpened=true;
    28. }
    29. }
    30. QString QFormDoc::currentFileName()
    31. {
    32. return mCurrentFile;
    33. }
    34. bool QFormDoc::isFileOpened()
    35. { //文件是否已打开
    36. return mFileOpened;
    37. }
    38. void QFormDoc::setEditFont()
    39. {
    40. QFont font;
    41. font=ui->plainTextEdit->font();
    42. bool ok;
    43. font=QFontDialog::getFont(&ok,font);
    44. ui->plainTextEdit->setFont(font);
    45. }
    46. void QFormDoc::textCut()
    47. {
    48. ui->plainTextEdit->cut();
    49. }
    50. void QFormDoc::textCopy()
    51. {
    52. ui->plainTextEdit->copy();
    53. }
    54. void QFormDoc::textPaste()
    55. {
    56. ui->plainTextEdit->paste();
    57. }

    注意作为 MDI 子窗口,不管其是否设置为关闭时删除,在主窗口里关闭一个 MDI 子窗口时,都会删除子窗口对象。

    MDI 主窗口设计与子窗口的使用

    主窗口界面设计

    要在主窗口实现 MDI 功能,只需在主窗口的工作区放置一个 QMdiArea 组件。图 2 是设计好的主窗口界面。


    设计时的主窗口
    图 2 设计时的主窗口


    在 UI 设计器里创建 Action,并应用 Action 设计主工具栏。在主窗口的工作区放置一个 QMdiArea 组件,然后在主窗口的构造函数里设置 mdiArea 填充满工作区。

    1. QWMainWindow::QWMainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::QWMainWindow)
    2. {
    3. ui->setupUi(this);
    4. this->setCentralWidget(ui->mdiArea);
    5. this->setWindowState(Qt::WindowMaximized);
    6. ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderlcon);
    7. }

    MDI 子窗口的创建与加入

    下面是主窗口上“新建文档”按钮的响应代码:

    1. void QWMainWindow::on_actDoc_New_triggered()
    2. { //新建文档
    3. QFormDoc *formDoc = new QFormDoc(this);
    4. ui->mdiArea->addSubWindow (formDoc) ; //文档窗口添力口到 MDI formDoc->show();
    5. }

    代码功能是新建一个 QFormDoc 类的窗口 formDoc,构造函数中传入了主窗口指针,所以主窗口是 formDoc 的父窗口,然后使用 QMdiArea 的 addSubWindow() 函数将 formDoc 加入到 mdiArea。

    下面是主窗口上“打开文档”按钮的响应代码:

    1. void QWMainWindow::on_actDoc_Open_triggered()
    2. {//打开文件
    3. //必须先获取当前MDI子窗口,再使用打开文件对话框,否则无法获得活动的子窗口
    4. bool needNew=false;// 是否需要新建子窗口
    5. QFormDoc *formDoc;
    6. if (ui->mdiArea->subWindowList().count()>0) //如果有打开的主窗口,获取活动窗口
    7. {
    8. formDoc=(QFormDoc*)ui->mdiArea->activeSubWindow()->widget();
    9. needNew=formDoc->isFileOpened();//文件已经打开,需要新建窗口
    10. }
    11. else
    12. needNew=true;
    13. QString curPath=QDir::currentPath();
    14. QString aFileName=QFileDialog::getOpenFileName(this,tr("打开一个文件"),curPath,
    15. "C程序文件(*.h *cpp);;文本文件(*.txt);;所有文件(*.*)");
    16. if (aFileName.isEmpty())
    17. return; //如果未选择文件,退出
    18. if (needNew) //需要新建子窗口
    19. {
    20. formDoc = new QFormDoc(this);//指定父窗口,必须在父窗口为Widget窗口提供一个显示区域
    21. ui->mdiArea->addSubWindow(formDoc);
    22. }
    23. formDoc->loadFromFile(aFileName); //打开文件
    24. formDoc->show();
    25. ui->actCut->setEnabled(true);
    26. ui->actCopy->setEnabled(true);
    27. ui->actPaste->setEnabled(true);
    28. ui->actFont->setEnabled(true);
    29. }

    通过 QMdiArea::subWindowList() 可以获得子窗口对象列表,从而可以判断子窗口的个数。如果没有一个MDI子窗口,就创建一个新的窗口并打开文件。

    如果有 MDI 子窗口,则总有一个活动窗口,通过 QMdiArea::activeSubWindow() 可以获得此活动的子窗口,通过子窗口的 isFileOpened() 函数判断是否打开了文件,如果没有打开过文件,就在这个活动窗口里打开文件,否则新建窗口打开文件。

    注意一定要先获取 MDI 子窗口,再使用 QFileDialog 选择需要打开的文件。如果顺序更换了,则无法获得正确的 MDI 活动子窗口。

    QMdiArea常用功能函数

    QMdiArea 提供了一些成员函数,可以进行一些操作,工具栏上的“关闭全部”、“MDI模式”、“级联展开”、“平铺展开”等按钮都是调用 QMdiArea 类的成员函数实现的。

    下面是这几个按钮功能的实现代码:

    1. void QWMainWindow::on_actCascade_triggered()
    2. { //窗口级联展开
    3. ui->mdiArea->cascadeSubWindows();
    4. }
    5. void QWMainWindow::on_actTile_triggered()
    6. {//平铺展开
    7. ui->mdiArea->tileSubWindows();
    8. }
    9. void QWMainWindow::on_actCloseALL_triggered()
    10. {//关闭全部子窗口
    11. ui->mdiArea->closeAllSubWindows();
    12. }
    13. void QWMainWindow::on_actViewMode_triggered(bool checked)
    14. {//MDI 显示模式
    15. if (checked) //Tab多页显示模式
    16. {
    17. ui->mdiArea->setViewMode(QMdiArea::TabbedView); //Tab多页显示模式
    18. ui->mdiArea->setTabsClosable(true); //页面可关闭
    19. ui->actCascade->setEnabled(false);
    20. ui->actTile->setEnabled(false);
    21. }
    22. else ////子窗口模式
    23. {
    24. ui->mdiArea->setViewMode(QMdiArea::SubWindowView); //子窗口模式
    25. ui->actCascade->setEnabled(true); //
    26. ui->actTile->setEnabled(true); //
    27. }
    28. }

    其中,设置 MDI 视图模式用 setViewMode() 函数,有两种模式可以选择:

    1. QMdiArea::Sub Window View 是传统的子窗口模式,显不效果如图 1 所示。
    2. QMdiArea::TabbedView 是多页的显示模式,显示效果如图 3 所示。

    多页模式下 MDI 界面
    图 3 多页模式下 MDI 界面

    MDI的信号

    QMdiArea 有一个信号 subWindowActivated(QMdiSubWindow *argl),在当前活动窗口切换时发射,利用此信号可以在活动窗口切换时进行一些处理,例如,在状态栏里显示活动 MDI 子窗口的文件名,在没有 MDI 子窗口时,将工具栏上的编辑功能按钮设置为禁用。

    下面是该信号的槽函数代码:

    1. void QWMainWindow::on_mdiArea_subWindowActivated(QMdiSubWindow *arg1)
    2. {//当前活动子窗口切换时
    3. if (ui->mdiArea->subWindowList().count()==0)
    4. { //若子窗口个数为零
    5. ui->actCut->setEnabled(false);
    6. ui->actCopy->setEnabled(false);
    7. ui->actPaste->setEnabled(false);
    8. ui->actFont->setEnabled(false);
    9. ui->statusBar->clearMessage();
    10. }
    11. else
    12. {
    13. QFormDoc *formDoc=static_cast<QFormDoc*>(ui->mdiArea->activeSubWindow()->widget());
    14. ui->statusBar->showMessage(formDoc->currentFileName()); //显示主窗口的文件名
    15. }
    16. }

    主窗口工具栏上的“剪切”、“复制”、“粘贴”、“字体设置”等按钮都是调用当前子窗口的相应函数,关键是获取当前 MDI 子窗体对象。

    例如,“剪切”和“字体设置”按钮的代码如下:

      1. void QWMainWindow::on_actCut_triggered()
      2. { //cut
      3. QFormDoc* formDoc=(QFormDoc*)ui->mdiArea->activeSubWindow()->widget();
      4. formDoc->textCut();
      5. }
      6. void QWMainWindow::on_actFont_triggered()
      7. {//设置字体
      8. QFormDoc* formDoc=(QFormDoc*)ui->mdiArea->activeSubWindow()->widget();
      9. formDoc->setEditFont();
      10. }
      11.  
      12. 参考网址
      13. http://m.biancheng.net/view/1875.html
  • 相关阅读:
    Count and Say
    Roman to Integer LeetCode Java
    白菜刷LeetCode记-121. Best Time to Buy and Sell Stock
    白菜刷LeetCode记-103. Binary Tree Zigzag Level Order Traversal
    白菜刷LeetCode记-102. Binary Tree Level Order Traversal
    白菜刷LeetCode记-350. Intersection of Two Arrays II
    白菜刷LeetCode记-268. Missing Number
    白菜刷LeetCode记-378. Kth Smallest Element in a Sorted Matrix
    白菜刷LeetCode记-328. Odd Even Linked List
    白菜刷LeetCode记-230. Kth Smallest Element in a BST
  • 原文地址:https://www.cnblogs.com/bruce1992/p/14489721.html
Copyright © 2020-2023  润新知