注:文中所列代码质量不高,但不影响演示我的思路
实现思路说明
-
实现DemoApplication
相当于MFC中CWinAppEx的派生类,暂时没加什么功能。
DemoApplication.h#pragma once #include <QtWidgets/QApplication> //相当于MFC中CWinAppEx的派生类, class DemoApplication : public QApplication { Q_OBJECT public: 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_OBJECT public: 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_OBJECT public: 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_OBJECT public: 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_OBJECT public: 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(); }