1.43k likes | 1.59k Views
視窗程式設計. 第六章 進階繪圖與編輯控制. 大 綱. 簡介 建立繪圖師專案-可捲動展視窗 建立抽象圖形類別 建立特定圖形類別-由抽象而入具體 圖形編輯 繪圖師 之 文件管理 繪圖師 之 編輯控制 圖形選取-圖形調整與位移 展示窗變動處理 其它進階功能-思考與練習 結論. 第六章 進階繪圖與編輯控制. 簡介. 各種視窗程式設計之優劣比較. 高階發展工具 學習容易 無法隨心所欲,低階行為不易控制,執行效率偏低 Win32 執行檔小,效率高 隨心所欲,但學習困難,發展費時 MFC( 相較於 Win32) 執行檔稍大,效率稍差
E N D
視窗程式設計 第六章 進階繪圖與編輯控制
大 綱 • 簡介 • 建立繪圖師專案-可捲動展視窗 • 建立抽象圖形類別 • 建立特定圖形類別-由抽象而入具體 • 圖形編輯 • 繪圖師之文件管理 • 繪圖師之編輯控制 • 圖形選取-圖形調整與位移 • 展示窗變動處理 • 其它進階功能-思考與練習 • 結論
第六章進階繪圖與編輯控制 簡介
各種視窗程式設計之優劣比較 • 高階發展工具 • 學習容易 • 無法隨心所欲,低階行為不易控制,執行效率偏低 • Win32 • 執行檔小,效率高 • 隨心所欲,但學習困難,發展費時 • MFC(相較於Win32) • 執行檔稍大,效率稍差 • 隨心所欲,且學習容易,發展省時
定義C++類別的重要諸元 • 定義適當的外部成員資料/函式 • 定義適當的虛擬函式 • 將功能抽象化 (abstract class) • 可改寫(overridable) • 定義適當的輔助函式(helper functions) • 內部經常被呼叫的功能 • 妥善的掌握 • 自動狀態機觀念
程式製作原則 • 減少鋸齒層次 • 以歪至多一次為原則 • 歪一次以上請檢討程式製作方式或規劃完善的演算法後進行實作 • 每一實作單元在一畫面上能完整呈現
本章範例—繪圖師專案 檢視完成後之結果
第六章進階繪圖與編輯控制 建立繪圖師專案-可捲動展視窗
專案建立選項 • 專案名稱為Painter • “File Extention”欄位鍵入“pnt” • CPainterView類別,將其Base class選為CScrollView 編譯執行觀察捲軸變化
sizeTotal.cx sizeTotal.cy 文件的尺寸與座標系
文件的尺寸與座標系 void CPainterView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); CSize sizeTotal; // TODO: calculate the total size of this view sizeTotal.cx = sizeTotal.cy = 100; SetScrollSizes(MM_TEXT, sizeTotal); }
文件的尺寸與座標系 voidSetScrollSizes( intnMapMode, //座標系別 SIZEsizeTotal, //文件尺寸 constSIZE&sizePage=sizeDefault, //頁尺寸 constSIZE&sizeLine=sizeDefault //行尺寸 );
設定座標系之相關函式 • CDC::SetMapMode() • CDC::SetWindowExt() • CDC::SetWindowOrg() • CDC::SetViewportExt() • CDC::SetViewportOrg()
文件尺寸文件管理與展視窗類別各司其職 • 文件管理類別 • 根據資料決定文件尺寸 • 展視窗類別 • 根據文件尺寸相關資料,設定相關模式後呈現文件內容
文件管理類別的尺寸管理 class CPainterDoc : public CDocument { . . . . . . . . . . . . . . . . . . // Attributes public: CSize m_sizePage; // Operations CSize GetPageSize() {return m_sizePage;} . . . . . . . . . . . . . . . . . . };
文件管理類別的尺寸管理 BOOL CPainterDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // use MM_HIMETRIC // A4 size paper 210mm x 297 mm m_sizePage.cx=21000; //x-軸向右為正 m_sizePage.cy=29700; //y-軸向下為正 return TRUE; }
展現窗相關諸元 void CPainterView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); SetScrollSizes(MM_HIMETRIC, GetDocument()->GetPageSize()); }
展現窗功能測試 void CPainterView::OnDraw(CDC* pDC) { . . . . . . . . . . . . . . //以下程式碼測試完成後移去 CRect rect(0, 0, 21000, -29700); //矩形繪圖位置 rect.NormalizeRect(); //矩形座標正規化 rect.InflateRect(-1500,-1500); //壓縮矩形 pDC->Rectangle(&rect); //繪製矩形 }
展視窗之比例配合 • 捲軸視窗之比例配合功能 CScrollView::SetScrollToFitSize()
功能表增加檢視方法選項 • 於IDR_MAINFRAME功能表中加入 ﹝檢視(V)﹞|﹝整頁模式顯示(F)﹞選項,其ID設為ID_VIEW_FULLPAGE。 • 於CPainterDoc定義中宣告屬性為public,型態為BOOL之成員變數m_bViewFullPage。 • 於CPainterDoc::OnNewDocument()中將成員變數m_bViewFullPage設為FALSE。
變更功能表顯示內容 • 於PainterDoc.h檔前端定義以下巨集: // 呼叫 UpdateAllViews() 之提示 #define UV_NONE 0 #define UV_SCALING 1 // 功能表文字 #define STR_VIEW_FULLPAGE _T("整頁模式顯示") #define STR_VIEW_NOSCALING _T("原比例顯示")
ID_VIEW_FULLPAGE命令處理與介面更新 void CPainterDoc::OnViewFullpage() { m_bViewFullPage = !m_bViewFullPage; SetModifiedFlag(); UpdateAllViews(NULL, UV_SCALING); } void CPainterDoc::OnUpdateViewFullpage(CCmdUI* pCmdUI) { pCmdUI->SetText(m_bViewFullPage ? STR_VIEW_NOSCALING : STR_VIEW_FULLPAGE); }
void CDocument::UpdateAllViews( CView* pSender, //更改文件內容之展視窗LPARAM lHint = 0L, //長整數重繪提示CObject* pHint = NULL //物件指標重繪提示); 文件變動引發之重繪與提示命令
void CView::OnUpdate( CView* pSender, //更改文件內容之展視窗LPARAM lHint, //長整數重繪提示CObject* pHint //物件指標重繪提示); 文件變動引發之重繪與提示命令處理
展視窗文件更新之重繪處理 void CPainterView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { CPainterDoc* pDoc=GetDocument(); CSize sizeDoc=pDoc->GetPageSize(); switch(lHint){ case UV_SCALING: if(pDoc->m_bViewFullPage) SetScaleToFitSize(sizeDoc); else SetScrollSizes(MM_HIMETRIC, sizeDoc); break; default: CScrollView::OnUpdate(pSender, lHint, pHint); break; } }
展視窗為何不能正常工作? void CPainterView::OnDraw(CDC* pDC) { . . . . . . . . . . . . . . //以下程式碼測試完成後移去 CRect rect(0, 0, 21000, -29700); //矩形繪圖位置 rect.NormalizeRect(); //矩形座標正規化 rect.InflateRect(-1500,-1500); //壓縮矩形 pDC->Rectangle(&rect); //繪製矩形 } DC被動了什麼手腳?哪裡有錯?
CXxxView之WM_PAINT訊息處理函式 void CView::OnPaint() { // standard paint routine CPaintDC dc(this); //建立繪圖DC OnPrepareDC(&dc);//打點繪圖DC OnDraw(&dc); //呼叫OnDraw()虛擬函式 }
打點畫布(DC) • CView::OnPrepareDC()啥事都沒作 。 • CScrollView::OnPrepareDC()您認為為我們做了些什麼 ? • 您能自力找出CScrollView::SetScrollToFitSize()無法正常工作的原因嗎?
斧底抽薪檢視原始碼 void CScrollView::SetScaleToFitSize(SIZE sizeTotal) { // Note: It is possible to set sizeTotal members to negative //values to effectively invert either the X or Y axis. ASSERT(m_hWnd != NULL); m_nMapMode = MM_SCALETOFIT; // special internal value (-1) m_totalLog = sizeTotal; // 作者加註:記錄文件大小(邏輯單位) . . . . . . . . . . . . . } 線上說明居然有誤
請再試試 恍然大悟後 void CPainterView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { CPainterDoc* pDoc=GetDocument(); CSize sizeDoc=pDoc->GetPageSize(); switch(lHint){ case UV_SCALING: if(pDoc->m_bViewFullPage){ sizeDoc.cy = -sizeDoc.cy; SetScaleToFitSize(sizeDoc); } else SetScrollSizes(MM_HIMETRIC, sizeDoc); break; . . . . . . . . . . . . . . . . . . . . } }
效果當不理想怎麼辦? • CScrollView::SetScrollToFitSize()無等比例縮放功能? • 未做置中處理 • 該如何補救?
檢視原始碼 void CScrollView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { . . . . . . . . . . . . . . . . . switch (m_nMapMode) { case MM_SCALETOFIT: pDC->SetMapMode(MM_ANISOTROPIC); //實際模式 //以下為設定xy-軸顯示比例之原始碼 . . . . . . . . . . . . . . . . break; default: ASSERT(m_nMapMode > 0); pDC->SetMapMode(m_nMapMode); break; } //以下為設定原點之原始碼 . . . . . . . . . . . . . . . . . } 無法等比例縮放的原因
改造(OnPrepareDC) #define MM_SCALETOFIT (-1) void CPainterView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { if(m_nMapMode!=MM_SCALETOFIT){ CScrollView::OnPrepareDC(pDC, pInfo); //呼叫原函式 return; } . . . . . . . . . . . . . . . . . }
改造(OnPrepareDC) pDC->SetMapMode(MM_ISOTROPIC); //原為MM_ANISOTROPIC //設定文件邏輯單位尺寸與圖素單位尺寸比 pDC->SetWindowExt(m_totalLog); //設定視窗座標範圍 CRect rect; GetClientRect(&rect); //得取文件所在視窗大小 pDC->SetViewportExt(rect); //設定viewport座標範圍
改造(OnPrepareDC) //設定原點 //計算文件以圖素為單位的尺寸 m_totalDev=m_totalLog; m_totalDev.cx=abs(m_totalDev.cx);//取正值 m_totalDev.cy=abs(m_totalDev.cy);//取正值 pDC->LPtoDP(&m_totalDev); //將尺寸轉為圖素單位 CPoint ptVpOrg(0, 0); if (m_bCenter) //新發現(線上說明未說明此成員資料用途) {//計算新原點位置,將文件置於視窗中間 ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2; ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2; } pDC->SetViewportOrg(ptVpOrg); //設定viewport原點 CView::OnPrepareDC(pDC, pInfo); }
Wext.cx VPext.cx VPext.cy Wext.cy 縮放比設定 Viewport Extension Window Extension
縮放比設定 • CDC::SetWindowExt() • CDC::SetViewportExt()
Wext.cx VPext.cx VPext.cy Wext.cy 原點設定 CDC::SetWindowOrg() CDC::SetViewportOrg()
邏輯座標與設備座標間之轉換 • CDC::DPtoLP() • CDC::LPtoDP()
再試試看 展視窗起始設定 void CPainterView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); m_bCenter=TRUE;//文件置中 CPainterDoc* pDoc=GetDocument(); CSize sizeDoc=GetDocument()->GetPageSize(); if(pDoc->m_bViewFullPage){ sizeDoc.cy=-sizeDoc.cy; SetScaleToFitSize(sizeDoc); } else SetScrollSizes(MM_HIMETRIC, sizeDoc); }
練 習 • 練習6.1 • 利用CDC::SetWindowOrg()達成整頁模式時文件置中的效果。 • 於功能表中設計一文件置中選項,撰寫相關處理程式。
文件區與非文件區辨識 BOOL CPainterView::OnEraseBkgnd(CDC* pDC) { CScrollView::OnEraseBkgnd(pDC); //忽叫原函式 int nOldDC=pDC->SaveDC(); //儲存DC狀態 OnPrepareDC(pDC); //將DC打點成目前模式 CRect rectClient; GetClientRect(&rectClient); //得取視窗矩形(圖素座標) pDC->DPtoLP(&rectClient); //圖素座標->邏輯座標 //取得文件區,將之設為繪圖保護區 CSize sizePage=GetDocument()->GetPageSize(); CRect rectPage(0, 0, sizePage.cx, -sizePage.cy); pDC->ExcludeClipRect(&rectPage); //文件區設為保護區
文件區與非文件區辨識 CBrush brush(GetSysColor(COLOR_APPWORKSPACE)); //以繪圖刷填充視窗用戶區,但保護區無法被繪入 pDC->FillRect(&rectClient, &brush); pDC->RestoreDC(nOldDC); //還原DC狀態 return TRUE; }
第六章進階繪圖與編輯控制 建立抽象圖形類別
C++之虛擬函式 • 達成類別可被改造之功能 • 達成類別抽象化的目的
虛擬函式之宣告 class CMyClass1 { void fun1(); void fun2(); virtual void vfun1(); virtual void vfun2(); virtual void vfun3(); }; class CMyClass2 : public CMyclass1 { void fun1(); void vfun1(); virtual void vfun2(); }; class CMyClass3 : public CMyclass2 { void fun1(); void fun2(); virtual void vfun2(); virtual void vfun3(); };
class CMyClass1 { void fun1(); void fun2(); virtual void vfun1(); virtual void vfun2(); virtual void vfun3(); }; class CMyClass2 : public CMyclass1 { void fun1(); void vfun1(); virtual void vfun2(); }; 虛擬函式之宣告 class CMyClass3 : public CMyclass2 { void fun1(); void fun2(); virtual void vfun2(); virtual void vfun3(); }; main() { CMyClass1 obj1; CMyClass2 obj2; CMyClass3 obj3; CMyClass1* p; p = &obj1; p->fun1(); p->fun2(); p->vfun1(); p->vfun2(); p->vfun3(); p = &obj2; p->fun1(); p->fun2(); p->vfun1(); p->vfun2(); p->vfun3(); p = &obj3; p->fun1(); p->fun2(); p->vfun1(); p->vfun2(); p->vfun3(); }
抽象類別 class CMyClass1 { void fun1(); void fun2(); virtual void vfun1() = 0; virtual void vfun2() = 0; virtual void vfun3(); }; 抽象類別不得有類別物件實體 CMyClass1 obj1;//不合法 CMyClass1* pObj1 = new CMyClass1;//不合法
抽象類別 class CMyClass1 { void fun1(); void fun2(); virtual void vfun1() = 0; virtual void vfun2() = 0; virtual void vfun3(); }; class CMyClass2 : public CMyClass1 { virtual void vfun1(); }; 一類別含任一抽象函式即為抽象類別 CMyClass1 obj1;//不合法 CMyClass1* pObj1 = new CMyClass1;//不合法 CMyClass2 obj2;//不合法 CMyClass2* pObj2 = new CMyClass2;//不合法