GTKmm 練習筆記(三)Drawing Area實作動畫鬧鐘(cairo 向量繪圖)
来源:互联网 发布:unity3d开发地方麻将 编辑:程序博客网 时间:2024/05/23 23:08
這次的筆記一樣是來自GTKmm GitHub上的範例 範例連結
內容是利用GTKmm的Drawing Area來繪製能依據現在時間做變化的動畫鬧鐘。
我將會記下自己對源碼的理解以及函數的解析,以及實作過程。
如果有不知道如何架設GTKmm的朋友可以先看我之前的文章,GTKmm環境架設。
範例截圖(一)
範例截圖(二)
在進入源碼解說前,先來介紹一下GTKmm官網對於Drawing Area控件的基礎使用說明。來源連結
先介紹一下會使的我們接下來閱讀實現源碼時會輕鬆許多。
在GTKmm中,繪圖的功能主要藉由另一個開源的向量繪圖庫Cairo來實現(念法近似"開羅"),
所以學習DrawingArea這個控件的方法,其實就是學習Cairo繪圖庫的使用方式。
而基本的繪圖方式為:
(一)在畫布上先定義出想要繪製出來的圖線路徑(Path),注意,但定義完成後這個路徑是看不見的。
(二)如果要將其在畫布上顯現出來,則要利用路徑描繪(Stroking)或填滿他們(filling)的函數。
(三)堆疊的使用,一個Context代表一個圖形狀態,Cairo內建一個堆疊,使你可以使用Context.save()將現在狀態放入堆疊中,也可以
使用Context.restore()將堆疊最上面的的狀態覆寫回來;我們可以透過這個功能來實現圖片的儲存及回復,由於這是一個堆疊,所以可以反覆嵌套使用。
在開始繪圖之前,我們必須先創造Cairo::Context 這個類別物件,這個物件裡將包含許多參數來告訴畫布我們將如何將圖形顯現出來。
在GTKmm中我們將利用Gdk::Window::create_cairo_contex()這個函數來創Cairo::Contex,這個函數將返回
Cairo::RefPtr<cairo::contex>這個物件。
以下是一個簡單的利用DrawingArea控件及Cairo庫的使用範例,我們可以很輕易的在源碼中了解他們之間的關係。
Gtk::DrawingArea myArea; //創建DrawingArea控件Cairo::RefPtr<Cairo::Context> myContext = myArea.get_window()->create_cairo_context();//創建Cairo::ContextmyContext->set_source_rgb(1.0, 0.0, 0.0);//利用Context的繪圖函數將畫布染成紅色
在了解完大致的使用方法後,我們可以開始進入鬧鐘範例源碼閱讀的部分了。
首先來看一下鬧鐘類的架構。
clock.h
#ifndef GTKMM_EXAMPLE_CLOCK_H#define GTKMM_EXAMPLE_CLOCK_H#include <gtkmm/drawingarea.h>//在使用DrawingArea之前必須先將其引入class Clock : public Gtk::DrawingArea//這個類直接繼承DrawingArea{public: Clock(); virtual ~Clock();protected: //Override default signal handler://翻譯:覆寫原有在DrawingArea的on_draw()方法 bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override; bool on_timeout(); double m_radius; double m_line_width;};#endif // GTKMM_EXAMPLE_CLOCK_H
接下來是clock.cpp
#include <ctime>#include <cmath>#include <cairomm/context.h>#include <glibmm/main.h>#include "clock.h"Clock::Clock(): m_radius(0.42), m_line_width(0.05){ Glib::signal_timeout().connect( sigc::mem_fun(*this, &Clock::on_timeout), 1000 );}Clock::~Clock(){}bool Clock::on_draw(const Cairo::RefPtr<Cairo::Context>& cr){ Gtk::Allocation allocation = get_allocation();//獲取關於當前控件的參數 const int width = allocation.get_width();//獲取寬度 const int height = allocation.get_height();//獲取高度 // scale to unit square and translate (0, 0) to be (0.5, 0.5), i.e. // the center of the window//翻譯:將源點設於窗口中央 cr->scale(width, height); cr->translate(0.5, 0.5); cr->set_line_width(m_line_width); cr->save();//將圖形放入堆棧 cr->set_source_rgba(0.337, 0.612, 0.117, 0.9); // green//設置當前畫筆顏色為綠色 cr->paint();//將綠色畫筆畫成被景色 cr->restore();//將最上層參數pop回來 cr->arc(0, 0, m_radius, 0, 2 * M_PI);//將一個弧形放入路徑中,參數(圓心.x,圓心.y,畫多少弧度,起點,終點(弧度角度))//在這裡指的是以視窗中心為圓心,畫一個圓。arc cr->save();//將當前路徑保存進堆棧 cr->set_source_rgba(1.0, 1.0, 1.0, 0.8);//設置當前畫筆顏色 cr->fill_preserve();//依造當前填滿規則將當前元給填滿出來,並保留路徑 cr->restore();//恢復原本畫布(將路徑清空) cr->stroke_preserve();//劃出路徑,在這裡指的是圓周。 cr->clip();//創建一個路徑與當前區域相交的部分。(這裡表示我也暫時難理解) //clock ticks//翻譯:畫刻度 for (int i = 0; i < 12; i++)//畫12個刻度 { double inset = 0.05;//內圈與外圈半徑差 cr->save();//保存當前路徑 cr->set_line_cap(Cairo::LINE_CAP_ROUND);//將當前的畫筆參數的線條樣式更改為圓頭線條 if(i % 3 != 0)//從0~11 假設不是0,3,6,9則將其畫筆長度縮小 { inset *= 0.8;//將內外圈差縮小0.8 cr->set_line_width(0.03);//將粗細設為0.03,比0,3,6,9點的刻度更細。 }//以下開始畫刻度 cr->move_to( (m_radius - inset) * cos (i * M_PI / 6),//線條原點.x設為 半徑*cos(弧度))的值(不了解的可以自行百度一下斜邊長與座 //標關係) (m_radius - inset) * sin (i * M_PI / 6));//線條原點.y設為 半徑*sin(弧度)的值 cr->line_to (//畫線 m_radius * cos (i * M_PI / 6),//從內圈畫到外圈... m_radius * sin (i * M_PI / 6)); cr->stroke();//劃出路徑並拋棄路徑 cr->restore(); /* stack-pen-size *///恢復原本繪圖及畫筆參數 } // store the current time time_t rawtime;//計算當前時間 time(&rawtime); struct tm * timeinfo = localtime (&rawtime); // compute the angles of the indicators of our clock//計算各個指針當前的角度 double minutes = timeinfo->tm_min * M_PI / 30;//2PI弧度是一個圓呈上角度分割 double hours = timeinfo->tm_hour * M_PI / 6; double seconds= timeinfo->tm_sec * M_PI / 30; cr->save();//保存當前路徑(空白) cr->set_line_cap(Cairo::LINE_CAP_ROUND);//設定線條樣式為圓頭線條 // draw the seconds hand cr->save();//保存當前路徑及畫筆樣式(圓頭線條) cr->set_line_width(m_line_width / 3);//設置當前畫筆寬度 cr->set_source_rgba(0.7, 0.7, 0.7, 0.8); // gray//設置當前畫筆顏色為灰 cr->move_to(0, 0);//將筆移到原點 cr->line_to(sin(seconds) * (m_radius * 0.9),//劃到指定角度的位置 -cos(seconds) * (m_radius * 0.9)); cr->stroke();//繪製路徑 cr->restore();//回復到畫筆樣式為圓頭的階段 // draw the minutes hand//劃分針,原理相同 cr->set_source_rgba(0.117, 0.337, 0.612, 0.9); // blue cr->move_to(0, 0); cr->line_to(sin(minutes + seconds / 60) * (m_radius * 0.8), -cos(minutes + seconds / 60) * (m_radius * 0.8)); cr->stroke(); // draw the hours hand//畫時針,原理相同 cr->set_source_rgba(0.337, 0.612, 0.117, 0.9); // green cr->move_to(0, 0); cr->line_to(sin(hours + minutes / 12.0) * (m_radius * 0.5), -cos(hours + minutes / 12.0) * (m_radius * 0.5)); cr->stroke(); cr->restore(); // draw a little dot in the middle//在中間劃個小圓 cr->arc(0, 0, m_line_width / 3.0, 0, 2 * M_PI); cr->fill(); return true;}bool Clock::on_timeout(){ // force our program to redraw the entire clock.//翻譯:每當時間一到,發出通知使整個窗口重繪(註記:重要) auto win = get_window(); if (win) { Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height()); win->invalidate_rect(r, false); } return true;}
最後是main.cpp,因為一開始就把整個主窗口繼承自DrawingArea了。因此直接實現他就行。
#include "clock.h"#include <gtkmm/application.h>#include <gtkmm/window.h>int main(int argc, char** argv){ auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); Gtk::Window win; win.set_title("Cairomm Clock"); Clock c; win.add(c); c.show(); return app->run(win);}
打完收工!
- GTKmm 練習筆記(三)Drawing Area實作動畫鬧鐘(cairo 向量繪圖)
- The Cairo graphics tutorial -------Basic drawing in Cairo
- 学习Gtkmm系列之三
- cairo教程三
- Drawing Outside a Window's Client Area
- cairo
- GTKmm 練習筆記(二)buton控件及XPM圖檔文件初識
- pku 1654 Area(向量叉积算面积)
- Drawing
- GTKmm 練習筆記(一)關於BOX容器的應用
- gtkmm试用
- GTKmm環境架設筆記
- Light 1305 - Area of a Parallelogram【向量求解】
- LIGHT OJ-1305 - Area of a Parallelogram 【向量叉积】
- POJ 1265 Area(Pick定理、向量积求面积)
- Area pick原理+线段上格点数目+向量叉乘
- (三)字符串、向量、数组
- Area
- 单元测试的2种mark下
- JavaScript 中 当用live绑定多个同名事件时,如何移除事件。
- ScrollView的滑动监听
- java的动态绑定与静态绑定
- react native navigator禁用滑动返回
- GTKmm 練習筆記(三)Drawing Area實作動畫鬧鐘(cairo 向量繪圖)
- 文件格式大全
- C/C++中的multiple definition of“****”的问题
- $stateParams 获取参数失败
- UGUI提高<六> ToggleGroup勾选组和Dropdown下拉菜单
- DTFT和DFT
- c++赋值运算符
- 多线程、工厂模式、GUI+JAVA学习笔记-DAY25
- 你不知道的HashMap与HashSet