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



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





图 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 所示。




图 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. http://m.biancheng.net/view/1875.html

Qt MDI及其使用方法(详解版)的更多相关文章

  1. HTTP请求方法详解

    HTTP请求方法详解 请求方法:指定了客户端想对指定的资源/服务器作何种操作 下面我们介绍HTTP/1.1中可用的请求方法: [GET:获取资源]     GET方法用来请求已被URI识别的资源.指定 ...

  2. Markdown语法说明(详解版)

    ####date: 2016-05-26 20:38:58 tags: Markdown tags && Syntax ##Markdown语法说明(详解版)杨帆发表于 2011-11 ...

  3. Java中的main()方法详解

    在Java中,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法,这个方法和其他的方法有很大的不同,比如方法的名字必须是main,方法必须是 ...

  4. [转帖]Vim编辑器使用方法详解

    Vim编辑器使用方法详解 程序员小新人学习 2018-12-16 12:26:23 转载于https://www.cnblogs.com/libaoliang/articles/6961676.htm ...

  5. Bootstrap Table使用方法详解

    http://www.jb51.net/article/89573.htm bootstrap-table使用总结 bootstrap-table是在bootstrap-table的基础上写出来的,专 ...

  6. (转)shell中test命令方法详解

    test命令用法.功能:检查文件和比较值 shell中test命令方法详解 原文:https://www.cnblogs.com/guanyf/p/7553940.html 1)判断表达式 if te ...

  7. postman使用方法详解

    postman的使用方法详解   Collections:在Postman中,Collection类似文件夹,可以把同一个项目的请求放在一个Collection里方便管理和分享,Collection里 ...

  8. StringUtils类API及使用方法详解

    StringUtils类API及使用方法详解 StringUtils方法概览 判空函数 1)StringUtils.isEmpty(String str) 2)StringUtils.isNotEmp ...

  9. Java工程师 基础+实战 完整路线图(详解版)

    Java工程师 基础+实战 完整路线图(详解版)   Java 基础 Java 是一门纯粹的面向对象的编程语言,所以除了基础语法之外,必须得弄懂它的 oop 特性:封装.继承.多态.此外还有泛型.反射 ...

  10. SpringBoot系列(六)集成thymeleaf详解版

    SpringBoot系列(六)集成thymeleaf详解版 1. thymeleaf简介  1. Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎.  2. Thymeleaf ...

随机推荐

  1. 【Python从入门到精通】(十一)Python的函数的方方面面【收藏下来保证有用!!!】

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本文主要介绍Python的函数,函数的定义,使用,可变参数等等都有详细介绍. 干货满满,建议收藏,需要用到时常看看. 小伙伴们如有问题及需要,欢迎 ...

  2. 高性能内存图数据库RedisGraph(一)

    作为一种简单.通用的数据结构,图可以表示数据对象之间的复杂关系.生物信息学.计算机网络和社交媒体等领域中产生的大量数据,往往是相互连接.关系复杂且低结构化的,这类数据对传统数据库而言十分棘手,一个简单 ...

  3. Scala学习——模式匹配

    scala模式匹配 1.基础match case(类似java里switch case,但功能强大些) object MatchApp { def main(args: Array[String]): ...

  4. Python+API接口测试框架设计(pytest)

    1.测试框架简介 整个接口测试框架的设计图如下: base:存放的是请求方法二次封装 common:存放的是自定义工具,操作excel,yaml文件等 data:存放的是公共动态数据,如data.xl ...

  5. Redis中一个String类型引发的惨案

    ​      曾经看到这么一个案例,有一个团队需要开发一个图片存储系统,要求这个系统能快速记录图片ID和图片存储对象ID,同时还需要能够根据图片的ID快速找到图片存储对象ID.我们假设用10位数来表示 ...

  6. HCNA Routing&Switching之动态路由协议OSPF DR和BDR

    前文我们了解了OSPF建立邻居关系的条件,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15032907.html:今天我们来聊一聊OSPF中的DR和BDR: ...

  7. 导出数据在exlcel上

    1.前台写一个按钮跳到控制层 <a href="account.do?flag=out" >导出表格</a> 2.控制层导出数据方法 @RequestMap ...

  8. Python3.6安装protobuf模块+将proto文件转换成pb2.py文件

    Python对版本的对应即为苛刻,笔者第一次安装时遇到了很多坑,比如无法将proto文件转换成py文件,转换了之后文件无法使用,网上各种各样的解决办法都没有讲到重点.其实会出现各种各样的问题是由于版本 ...

  9. 什么是SpringBoot,微服务

    Spring是如何简化Java开发的 为了降低Java开发的复杂性,Spring采用了以下4种关键策略: 1.基于pojo的轻量级和最小侵入性编程:   2.通过IOC,依赖注入(DI)和面向接口实现 ...

  10. 福利!Python制作动态字符画(附源码)

    字符画,一种由字母.标点.汉字或其他字符组成的图画.简单的字符画是利用字符的形状代替图画的线条来构成简单的人物.事物等形象,它一般由人工制作而成:复杂的字符画通常利用占用不同数量像素的字符代替图画上不 ...