畫板(面向物件設計,外掛式程式設計)
畫板
設計上的主要特點
- 多型: 使用虛擬函式,讓呼叫者不加區分地完成函式呼叫. 該特點體現在畫板主程式對
Painter
類方法的呼叫上,以此實現對多種圖形的操作. - 繼承: 每次實現一個新的畫圖選項(比如畫矩形)都需要子類化4個基類
Shape
,Painter
,ShapeFactory
,PainterFactory
. - 設計模式: 外掛工程使用工廠方法模式,抽象工廠類為
ShapeFactory
和PainterFactory
,對應的抽象產品類為Shape
和Painter
. - 外掛式: 所有的畫圖選項都以外掛DLL的形式提供,由畫板主程式載入.
- 多邊形–增強畫圖功能的抽象性: 多邊形與其他基本幾何圖形不同,它的點集具有不確定性(the number of points is precarious),而矩形和圓形之類的圖形只需由2個點確定,如果要相容多變形就需要對各種圖形的處理策略進一步抽象.
功能特點
圖形繪製、拖動,儲存/開啟圖形資料檔案
開發環境
- Windows7
- Visual Studio 2015 Community (QT5 plugin installed)
- QtCreator (qt-opensource-windows-x86-msvc2015-5.8.0`)
最開始使用的是VS2015+Qt5.8.0,後來使用了VS2013+Qt5.5.1,除開發環境不同外,程式碼完全一致)
目錄說明
Drawboard.Main
畫板主程式工程檔案,使用VS 2015
建立PluginProj
外掛工程檔案,使用QtCreator
建立 (QT版本:qt-opensource-windows-x86-msvc2015-5.8.0
drawboard
最終的產品
設計模式
外掛工程使用工廠方法模式,抽象工廠類為ShapeFactory
和PainterFactory
,對應的抽象產品類為Shape
和Painter
.
畫板是如何執行的?
1、初始化過程
畫板啟動後加載外掛XxxPainterFactory.DLL
,將XxxPainterFactory::createPainter()
建立的Painter
子類物件儲存在MainWindow::m_PainterList
. Painter
子類物件被建立時,建構函式呼叫ShapeFactory::createShape()
建立該Painter
對應的Shape
MainWindow::m_PainterList
初始化後的情形如下:
- 藍色箭頭:
MainWindow::m_PainterList
存放的Painter*
- 橢圓:
XxxPainterFactory::createPainter()
建立的Painter
子類物件 - 橙色箭頭:每個
Painter
子類物件的m_pDrawingShape
指標 - 三角形:與每個
Painter
繫結的Shape
子類物件.
每個Painter
(橢圓)都有一個數據成員m_ShapeList
,儲存所有自己繪製的圖形.
選擇繪製不同的圖形,只需將MainWindow::m_PainterList
中的Painter*
賦給MainWindow::m_pCurrentPainter
,使得m_pCurrentPainter
指向不同的Painter
子類物件(橢圓).該操作由MainWindow::init()
完成.
2、繪製過程中的Painter
:
每個橢圓表示一個Shape
子類物件.
3、畫板的重繪策略
重繪時,畫板遍歷m_PainterList
中的Painter
,每個Painter
遍歷自己的m_ShapeList
,m_pDrawingShape
則被實時地動態顯示. 每次切換圖形繪製時,m_pCurrentPainter->m_pDrawingShape
已被儲存在m_ShapeList
中,m_pDrawingShape
又將指向新的Shape
,具體的處理策略如下:
4、繪製結束後的處理策略
法一:
MainWindow::m_pCurrentPainter
指向的Painter
先讓自己的ShapeFactory
建立一個新的Shape
,並將m_pDrawinigShape
指向的Shape
的關鍵資訊拷貝給這個新Shape
,再將指向新Shape
的指標儲存到m_ShapeList
,其後銷燬m_pDrawingShape
指向Shape
,最後再建立一個新的Shape
,令m_pDrawingShape
指向它.上述操作由Painter::save()
完成. 但是Painter::save()
的關鍵程式碼其實可以簡化為:
法二:
m_ShapeList.push_back(m_pDrawingShape);
m_pDrawingShape = m_pShapeFactory->createShape();
該方案保留了原有的Shape
,也不需要呼叫ShapeFactory::createShape(Shape*)
,但對於多邊形不可行(後續無法拖拽之). 注意到ShapeFactory::createShape(Shape*)
是通過Shape::setKeyPoints
完成關鍵資訊的拷貝的,而MyPolygon
重寫了該函式,因此在現有的繼承關係下,法二是較為合理的選擇(事實上,ShapeFactory::createShape(Shape*)
是多邊形專用的,其他圖形只需ShapeFactory::createShape()
,但為了統一,全部圖形繪製結束後的處理方式都採用法一).
5、拖動
畫板遍歷m_PainterList
,每個Painter
遍歷自己的m_ShapeList
,找到需要被拖動的Shape
後,畫板將m_pCurrentPainter
指向那個Painter
,並設定該Painter
的資料成員m_pDraggingShape
指向m_ShapeList
中需要被拖動的Shape
. 此後這個Painter
將根據滑鼠軌跡實時修改m_pDraggingShape
指向的Shape
,並重繪之.
拖動過程並未產生新的Shape
,而是由m_pCurrentPainter
修改自己的m_ShapeList
中的某個Shape
,重繪時會反映出來.
如何制定exe圖示?
vs2015
- 在VS中新建資原始檔(.rc),並在
資源檢視
下右鍵Resource.rc
-資源符號
->新建
,輸入IDI_APPICON
. 新增Icon
資源,匯入準備好的.ico
檔案,再使用其他文字編輯器開啟Resource.rc
和resource.h
,將預設的資源符號IDI_ICON1
刪去或修改為IDI_APPICON
. - 選擇選單欄
QT5
->Create basic .pro file
->不建立.pri
檔案->Project tag
選擇Windows resource file
,完成. - 在
.pro
檔案末尾新增RCC_FILE += Resource.rc
,重新生成即可.
vs2013
第一步與vs2015相同,完成後只需開啟Resource Files
下的.qrc
檔案,選擇Add File
,新增.ico
檔案即可.
如何讓視窗中的各種圖示生效?
開啟Resource Files
下的.qrc
檔案,選擇Add File
,新增相應的圖片檔案,即可讓程式碼中的setIcon()
、setWindowIcon()
函式生效.