1. 程式人生 > >畫板(面向物件設計,外掛式程式設計)

畫板(面向物件設計,外掛式程式設計)

畫板

drawboard.png

設計上的主要特點

  • 多型: 使用虛擬函式,讓呼叫者不加區分地完成函式呼叫. 該特點體現在畫板主程式對Painter類方法的呼叫上,以此實現對多種圖形的操作.
  • 繼承: 每次實現一個新的畫圖選項(比如畫矩形)都需要子類化4個基類Shape,Painter,ShapeFactory,PainterFactory.
  • 設計模式: 外掛工程使用工廠方法模式,抽象工廠類為ShapeFactoryPainterFactory,對應的抽象產品類為ShapePainter.
  • 外掛式: 所有的畫圖選項都以外掛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 最終的產品

設計模式

外掛工程使用工廠方法模式,抽象工廠類為ShapeFactoryPainterFactory,對應的抽象產品類為ShapePainter.

畫板是如何執行的?

1、初始化過程

畫板啟動後加載外掛XxxPainterFactory.DLL,將XxxPainterFactory::createPainter()建立的Painter子類物件儲存在MainWindow::m_PainterList. Painter子類物件被建立時,建構函式呼叫ShapeFactory::createShape()建立該Painter對應的Shape

.

MainWindow::m_PainterList初始化後的情形如下:

m_PainterList.png

  • 藍色箭頭: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:

painter.png

每個橢圓表示一個Shape子類物件.

3、畫板的重繪策略

重繪時,畫板遍歷m_PainterList中的Painter,每個Painter遍歷自己的m_ShapeListm_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

  1. 在VS中新建資原始檔(.rc),並在資源檢視下右鍵Resource.rc-資源符號->新建,輸入IDI_APPICON. 新增Icon資源,匯入準備好的.ico檔案,再使用其他文字編輯器開啟Resource.rcresource.h,將預設的資源符號IDI_ICON1刪去或修改為IDI_APPICON.
  2. 選擇選單欄QT5->Create basic .pro file->不建立.pri檔案->Project tag選擇Windows resource file,完成.
  3. .pro檔案末尾新增RCC_FILE += Resource.rc,重新生成即可.

vs2013

第一步與vs2015相同,完成後只需開啟Resource Files下的.qrc檔案,選擇Add File,新增.ico檔案即可.

如何讓視窗中的各種圖示生效?

開啟Resource Files下的.qrc檔案,選擇Add File,新增相應的圖片檔案,即可讓程式碼中的setIcon()setWindowIcon()函式生效.