注:文中所列代码质量不高,但不影响演示我的思路
实现思路说明
-
实现DemoApplication
相当于MFC中CWinAppEx的派生类,暂时没加什么功能。
DemoApplication.h#pragma once#include <QtWidgets/QApplication>//相当于MFC中CWinAppEx的派生类, class DemoApplication : public QApplication {Q_OBJECTpublic:DemoApplication(int &argc, char **argv);~DemoApplication();};
DemoApplication.cpp
#include "DemoApplication.h"DemoApplication::DemoApplication(int &argc, char **argv): QApplication(argc, argv) { }DemoApplication::~DemoApplication() { }
-
实现DemoDocument
相当与MFC的CDocument。DemoDocument保存了当前所有视图的指针(此处实际是DeomChildWindow*,因为DeomChildWindow与DeomView时1对1关系,根据DeomChildWindow可以获得DemoView指针,简化设计,就这样处理了),实现了增加视图addView、移除视图removeView、获取视图数量getViewCount等函数。
DemoDocument.h#pragma once#include <QObject> #include <list>class DemoChildWindow;//相当与MFC的CDocument class DemoDocument : public QObject {Q_OBJECTpublic:DemoDocument(QObject *parent = 0);~DemoDocument();void addView(DemoChildWindow* pChildWindow);void removeView(DemoChildWindow* pChildWindow);int getViewCount() const;unsigned int getId() { return m_nId; }signals:void closedDocument(DemoDocument* pDocument);private:static unsigned int allocId();private:static unsigned int s_NextId;unsigned int m_nId = 0;std::list<DemoChildWindow*> m_viewList; //视图列表 };
DemoDocument.cpp
#include "DemoDocument.h"unsigned int DemoDocument::s_NextId = 0;DemoDocument::DemoDocument(QObject *parent): QObject(parent) {m_nId = allocId(); }DemoDocument::~DemoDocument() { }unsigned int DemoDocument::allocId() {if (DemoDocument::s_NextId == std::numeric_limits<unsigned int>::max()){DemoDocument::s_NextId = 0;}return ++DemoDocument::s_NextId; }void DemoDocument::addView(DemoChildWindow* pChildWindow) {m_viewList.push_back(pChildWindow); }void DemoDocument::removeView(DemoChildWindow* pChildWindow) {auto it = std::find(m_viewList.begin(), m_viewList.end(), pChildWindow);if (it != m_viewList.end() ){m_viewList.erase(it);}if (m_viewList.size() == 0){emit closedDocument(this);} }int DemoDocument::getViewCount() const {return int(m_viewList.size()); }
-
实现DemoMainWindow
相当于MFC中的CMainFrame,派生自CMDIFrameWndEx。此类new了一个QMdiArea对象,通过此对象实现多文档程序的通用功能,如切换窗口、层叠窗口、平铺窗口等。类DemoMainWindow直接管理了所有打开的文档,这点同MFC不一样,MFC是通过文档管理器、文档模版处理的,此处简化下,直接在DemoMainWindow中管理。此处我让DemoMainWindow负责新建、打开、关闭文档。
DemoMainWindow.h#pragma once#include <QtWidgets/QMainWindow> #include <QMdiArea> #include <list> #include <memory>class DemoDocument;//相当于MFC中的CMainFrame,派生自CMDIFrameWndEx class DemoMainWindow : public QMainWindow {Q_OBJECTpublic:DemoMainWindow(QWidget *parent = Q_NULLPTR);virtual ~DemoMainWindow();DemoDocument* getActiveDocument() const;protected slots:void onSlotNewDocument();void onSlotClosedDocument(DemoDocument* pDocument );void onSlotNewWindow();private://创建文档的一个视图(DemoChildWindow-DeomView)void createNewWindow(DemoDocument* pDocument );private:QMdiArea* m_pMDIArea = nullptr;std::list<DemoDocument*> m_DocList; //文档列表 };
DemoMainWindow.cpp
#include "DemoMainWindow.h" #include "DemoDocument.h" #include "DemoChildWindow.h"#include <QMdiSubWindow> #include <QMenuBar>DemoMainWindow::DemoMainWindow(QWidget *parent): QMainWindow(parent) {m_pMDIArea = new QMdiArea();this->setCentralWidget(m_pMDIArea);//void subWindowActivated(QMdiSubWindow * window)//菜单QMenu* pFileMenu = menuBar()->addMenu(QStringLiteral("文件"));QAction* pNewDocAction = new QAction(QStringLiteral("新建"), this);connect(pNewDocAction, SIGNAL(triggered()), this, SLOT(onSlotNewDocument()));pFileMenu->addAction(pNewDocAction);//窗口(实际需要动态添加到菜单栏中,即有视图窗口打开,就加入,否则就移除,此处暂未实现)QMenu* pWinMenu = menuBar()->addMenu(QStringLiteral("窗口"));QAction* pNewWinAction = new QAction(QStringLiteral("新建"), this);connect(pNewWinAction, SIGNAL(triggered()), this, SLOT(onSlotNewWindow()));pWinMenu->addAction(pNewWinAction);}DemoMainWindow::~DemoMainWindow() {for (auto pDoc : m_DocList){delete pDoc;}m_DocList.clear(); }void DemoMainWindow::onSlotNewDocument() {auto pNewDoc = new DemoDocument();m_DocList.push_back(pNewDoc);createNewWindow(pNewDoc);connect(pNewDoc, SIGNAL(closedDocument(DemoDocument*)), this, SLOT(onSlotClosedDocument(DemoDocument*))); }void DemoMainWindow::onSlotClosedDocument(DemoDocument* pDocument) {auto it = std::find(m_DocList.begin(), m_DocList.end(), pDocument);if (it != m_DocList.end()){auto pDoc = *it;delete pDoc;m_DocList.erase(it);} }void DemoMainWindow::onSlotNewWindow() {auto pDocument = getActiveDocument();createNewWindow(pDocument); }//创建文档的一个视图(DemoChildWindow-DeomView) void DemoMainWindow::createNewWindow(DemoDocument* pDocument) {if (!pDocument){Q_ASSERT(false);return;}auto pChildWnd = new DemoChildWindow(pDocument);//自己new QMdiSubWindow时,必须设置Qt::WA_DeleteOnClose,参看文档//When you create your own subwindow, you must set the Qt::WA_DeleteOnClose widget //attribute if you want the window to be deleted when closed in the MDI area. //If not, the window will be hidden and the MDI area will not activate the next subwindow.//添加方式如下: // QMdiSubWindow *pMdiSubWindow = new QMdiSubWindow; // pMdiSubWindow->setWidget(pChildWnd); // pMdiSubWindow->setAttribute(Qt::WA_DeleteOnClose); // m_pMDIArea->addSubWindow(pMdiSubWindow); // pMdiSubWindow->show();//这中方法更简单auto pMdiSubWindow = m_pMDIArea->addSubWindow(pChildWnd);pMdiSubWindow->setWindowTitle(QStringLiteral("文档%1:%2").arg(pDocument->getId()).arg(pDocument->getViewCount()));pMdiSubWindow->show(); }DemoDocument* DemoMainWindow::getActiveDocument() const {auto pCurMdiSubWindow = m_pMDIArea->currentSubWindow();if (!pCurMdiSubWindow){return nullptr;}auto pChildWnd = dynamic_cast<DemoChildWindow*>(pCurMdiSubWindow->widget());if (!pChildWnd){Q_ASSERT(false);return nullptr;}return pChildWnd->GetDocument(); }
-
实现DemoChildWindow
相当于MFC中的ChildFrm,派生自CMDIChildWndEx, 与文档是n-1关系, 与DemoView时1-1关系。此类负责创建DemoView,并且包含了文档对象的指针。MFC中创建ChildFrm以及CView没那么直接,通过消息触发创建了CView,具体参看MFC即可,我此处就简化处理创建的过程。
DemoChildWindow.h#pragma once#include <QWidget>class DemoDocument; class DemoView;//相当于MFC中的ChildFrm,派生自CMDIChildWndEx, 与文档是n-1关系, 与DemoView时1-1关系, class DemoChildWindow : public QWidget {Q_OBJECTpublic:DemoChildWindow(DemoDocument* pDoc, QWidget* parent = 0, Qt::WindowFlags f = 0);~DemoChildWindow();DemoDocument* GetDocument() const;protected:virtual void closeEvent(QCloseEvent * event) override;private:DemoDocument* m_pDoc;DemoView* m_pView; };
DemoChildWindow.cpp
#include "DemoChildWindow.h" #include "DemoView.h" #include "DemoDocument.h" #include <QVBoxLayout>DemoChildWindow::DemoChildWindow(DemoDocument* pDoc, QWidget *parent, Qt::WindowFlags flags): QWidget(parent, flags) {m_pDoc = pDoc;m_pView = new DemoView();m_pDoc->addView(this);auto pVBoxLayout = new QVBoxLayout();pVBoxLayout->addWidget(m_pView);this->setLayout(pVBoxLayout); }DemoChildWindow::~DemoChildWindow() { }DemoDocument* DemoChildWindow::GetDocument() const {return m_pDoc; }void DemoChildWindow::closeEvent(QCloseEvent * event) {m_pDoc->removeView(this);return QWidget::closeEvent(event); }
-
实现DemoView
相当与MFC中的CView,在DemoView中仅显示了硬编码的文本字符串。
DemoView.h#pragma once#include <QWidget>//相当与MFC中的CView class DemoView : public QWidget {Q_OBJECTpublic:DemoView(QWidget *parent = Q_NULLPTR);~DemoView();private://Ui::DemoView ui; };
DemoView.cpp
#include "DemoView.h" #include <QVBoxLayout> #include <QLabel>DemoView::DemoView(QWidget *parent): QWidget(parent) {auto pVBoxLayout = new QVBoxLayout();pVBoxLayout->addWidget(new QLabel(QStringLiteral("视图测试")));this->setLayout(pVBoxLayout);this->setMinimumSize(300, 200); }DemoView::~DemoView() { }
-
main函数
#include "DemoApplication.h" #include "DemoMainWindow.h"int main(int argc, char *argv[]) {DemoApplication a(argc, argv);DemoMainWindow w;w.show();return a.exec(); }