统的应用程序设计中有多文档界面(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

最新文章

  1. 一些比较实用的css片段
  2. BZOJ3083: 遥远的国度
  3. GirdView 追忆学生时代的百思不得解
  4. AT指令获取基站信息
  5. ORACLE10gRAC数据库迁移至10gRAC
  6. Charles中如何对https抓包
  7. 【js】将table的每个td的内容自动赋值给其title属性
  8. ExtractTablesFromSQL
  9. bzoj 3687 bitset的运用
  10. linux性能监控基础命令
  11. GLSL 中的光照计算
  12. DBUtils的使用
  13. URL重定向
  14. Git拉取、提交、迁出、合并、删除分之命令
  15. python基础(17)继承类和面向对象初识
  16. K 班1-7,alpha,beta 作业成绩汇总
  17. [ZJOI2015]地震后的幻想乡(期望+dp)
  18. 排序算法系列:选择排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)
  19. 黄聪:AngularJS中的$resource使用与Restful资源交互(转)
  20. 让linux中 history显示每条命令的操作时间及操作用户【转】

热门文章

  1. 前端009-vue框架
  2. 管理员的基本防范措施 Linux系统安全及应用
  3. File类与常用IO流第五章——IO字符流
  4. Day2基本数据类型 字节 和类型转换
  5. canvas实现任意正多边形的移动(点、线、面)
  6. 前端开发入门到进阶第三集【获取cookie然后单点登录后重定向】
  7. Requests 方法 -- post请求操作实践
  8. debain9显卡
  9. Ubuntu21.04 / Linux Mint20.2 安装 TradingView分析软件
  10. C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载