学习新的图形库时有个习惯,就是将罗云彬先生的一个闹钟小程序移植到这个图形库下。
原理也很简单,就是通过图片以第一个点的颜色为透明色创建窗体,但Gtk#创建就有些费劲了,主要是因为Gtk#的原生库Gtk+为C写的,有很多指针操作,在C#下有些麻烦,查了好多资料才写了下面几句代码。
然后用Cairo绘制时钟的时间,也是一张图片,然后按时、分、秒截取图形的一部分绘制到不透明窗体上。
在Windows 7、Ubunu 11.04下运行,效果相同。
见代码:
1
using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Cairo;
6 using Gdk;
7 using Gtk;
8 using Window = Gtk.Window;
9 using WindowType = Gtk.WindowType;
10
11 namespace GClock
12 {
13 public class MainWindow : Window
14 {
15 bool timer = true;
16 readonly Pixbuf _pixbufNumber; // 时钟时间图片,为0-9十个数字和:以及空白部分组成
17
18 public MainWindow()
19 : base(WindowType.Toplevel)
20 {
21 _pixbufNumber = new Pixbuf(GetType().Assembly, " GClock.Bmp.Number06.bmp ");
22
23 // 从嵌入资源中获取Pixbuf
24 var pixbufO = new Pixbuf(GetType().Assembly, " GClock.Bmp.Clock03.bmp ");
25 // 取Pixbuf第一个点的rgb值
26 byte r, g, b;
27 unsafe {
28 // Pixbuf的Pixels未记录图片每个点信息值
29 // 取(x,y)的rgb值,以下代码中x,y均为0
30 // var p = (byte*)pixbufO.Pixels.ToPointer() + y * pixbufO.Rowstride + x * pixbufO.NChannels;
31 var p = pixbufO.Pixels.ToPointer();
32 r = *( byte*)p;
33 g = *(( byte*)p + 1);
34 b = *(( byte*)p + 2);
35 }
36 // 依据第一个点的rgb值增加透明通道
37 var pixbuf = pixbufO.AddAlpha( true, r, g, b);
38
39 Decorated = false; // 不设置窗体边框
40 AppPaintable = true; // 应用程序绘制界面
41 SetDefaultIconFromFile( " Clock00.ico ");
42 // SetDefaultSize(pixbuf.Width, pixbuf.Height);
43 SetDefaultSize( 500, 500);
44 SetPosition(WindowPosition.Center);
45
46 Realize(); // 创建Window的资源,若不调用则this.GdkWindow为null
47
48 DeleteEvent += delegate { Application.Quit(); };
49 // 添加鼠标点击事件
50 AddEvents(( int)EventMask.ButtonPressMask);
51 GLib.Timeout.Add( 100, OnTimer); // 时间事件
52
53 Pixmap pixmap, pixmask;
54 // 依据Pixbuf创建Pixmap以及对应的蒙板Pixmap
55 pixbuf.RenderPixmapAndMask( out pixmap, out pixmask, 128);
56 ShapeCombineMask(pixmask, 0, 0);
57 GdkWindow.SetBackPixmap(pixmap, false);
58
59 ShowAll();
60 }
61
62 protected override bool OnButtonPressEvent(EventButton evnt)
63 {
64 // 鼠标左键点击移动窗体
65 if (evnt.Type == EventType.ButtonPress && evnt.Button == 1) {
66 BeginMoveDrag(( int)evnt.Button, ( int)evnt.XRoot, ( int)evnt.YRoot, evnt.Time);
67 }
68 return base.OnButtonPressEvent(evnt);
69 }
70
71 protected override bool OnExposeEvent(EventExpose evnt)
72 {
73 base.OnExposeEvent(evnt);
74
75 using (var cr = CairoHelper.Create(evnt.Window)) {
76 var curTime = DateTime.Now;
77 int tenDigit, entriesDigit;
78
79 var x = 25;
80 const int y = 80;
81 cr.Save();
82 // 绘制左侧空白
83 cr.Translate(x, y);
84 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -( 15 * 10 + 8), 0);
85 cr.Rectangle( 0, 0, 2, 25);
86 cr.Clip();
87 cr.Paint();
88
89
90 // HH
91 DecToBcd(curTime.Hour, out tenDigit, out entriesDigit);
92 x += 2;
93 cr.Restore();
94 cr.Save();
95 cr.Translate(x, y);
96 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * tenDigit, 0);
97 cr.Rectangle( 0, 0, 15, 25);
98 cr.Clip();
99 cr.Paint();
100
101 x += 15;
102 cr.Restore();
103 cr.Save();
104 cr.Translate(x, y);
105 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * entriesDigit, 0);
106 cr.Rectangle( 0, 0, 15, 25);
107 cr.Clip();
108 cr.Paint();
109
110 // :
111 x += 15;
112 cr.Restore();
113 cr.Save();
114 cr.Translate(x, y);
115 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * 10, 0);
116 cr.Rectangle( 0, 0, 8, 25);
117 cr.Clip();
118 cr.Paint();
119
120 // MM
121 DecToBcd(curTime.Minute, out tenDigit, out entriesDigit);
122 x += 8;
123 cr.Restore();
124 cr.Save();
125 cr.Translate(x, y);
126 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * tenDigit, 0);
127 cr.Rectangle( 0, 0, 15, 25);
128 cr.Clip();
129 cr.Paint();
130
131 x += 15;
132 cr.Restore();
133 cr.Save();
134 cr.Translate(x, y);
135 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * entriesDigit, 0);
136 cr.Rectangle( 0, 0, 15, 25);
137 cr.Clip();
138 cr.Paint();
139
140 // :
141 x += 15;
142 cr.Restore();
143 cr.Save();
144 cr.Translate(x, y);
145 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * 10, 0);
146 cr.Rectangle( 0, 0, 8, 25);
147 cr.Clip();
148 cr.Paint();
149
150 // SS
151 DecToBcd(curTime.Second, out tenDigit, out entriesDigit);
152 x += 8;
153 cr.Restore();
154 cr.Save();
155 cr.Translate(x, y);
156 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * tenDigit, 0);
157 cr.Rectangle( 0, 0, 15, 25);
158 cr.Clip();
159 cr.Paint();
160
161 x += 15;
162 cr.Restore();
163 cr.Save();
164 cr.Translate(x, y);
165 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * entriesDigit, 0);
166 cr.Rectangle( 0, 0, 15, 25);
167 cr.Clip();
168 cr.Paint();
169
170 // 绘制右侧空白
171 x += 15;
172 cr.Restore();
173 cr.Save();
174 cr.Translate(x, y);
175 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -( 15 * 10 + 8), 0);
176 cr.Rectangle( 0, 0, 2, 25);
177 cr.Clip();
178 cr.Paint();
179
180 cr.Restore();
181 }
182
183 return true;
184 }
185
186 bool OnTimer()
187 {
188 if (!timer) return false;
189
190 QueueDraw();
191 return true;
192 }
193
194 // 依据两位数获取该数字的十位、个位数
195 private static void DecToBcd( int decimalNum, out int tenDigit, out int entriesDigit)
196 {
197 tenDigit = decimalNum / 10;
198 entriesDigit = decimalNum % 10;
199 }
200 }
201 }
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Cairo;
6 using Gdk;
7 using Gtk;
8 using Window = Gtk.Window;
9 using WindowType = Gtk.WindowType;
10
11 namespace GClock
12 {
13 public class MainWindow : Window
14 {
15 bool timer = true;
16 readonly Pixbuf _pixbufNumber; // 时钟时间图片,为0-9十个数字和:以及空白部分组成
17
18 public MainWindow()
19 : base(WindowType.Toplevel)
20 {
21 _pixbufNumber = new Pixbuf(GetType().Assembly, " GClock.Bmp.Number06.bmp ");
22
23 // 从嵌入资源中获取Pixbuf
24 var pixbufO = new Pixbuf(GetType().Assembly, " GClock.Bmp.Clock03.bmp ");
25 // 取Pixbuf第一个点的rgb值
26 byte r, g, b;
27 unsafe {
28 // Pixbuf的Pixels未记录图片每个点信息值
29 // 取(x,y)的rgb值,以下代码中x,y均为0
30 // var p = (byte*)pixbufO.Pixels.ToPointer() + y * pixbufO.Rowstride + x * pixbufO.NChannels;
31 var p = pixbufO.Pixels.ToPointer();
32 r = *( byte*)p;
33 g = *(( byte*)p + 1);
34 b = *(( byte*)p + 2);
35 }
36 // 依据第一个点的rgb值增加透明通道
37 var pixbuf = pixbufO.AddAlpha( true, r, g, b);
38
39 Decorated = false; // 不设置窗体边框
40 AppPaintable = true; // 应用程序绘制界面
41 SetDefaultIconFromFile( " Clock00.ico ");
42 // SetDefaultSize(pixbuf.Width, pixbuf.Height);
43 SetDefaultSize( 500, 500);
44 SetPosition(WindowPosition.Center);
45
46 Realize(); // 创建Window的资源,若不调用则this.GdkWindow为null
47
48 DeleteEvent += delegate { Application.Quit(); };
49 // 添加鼠标点击事件
50 AddEvents(( int)EventMask.ButtonPressMask);
51 GLib.Timeout.Add( 100, OnTimer); // 时间事件
52
53 Pixmap pixmap, pixmask;
54 // 依据Pixbuf创建Pixmap以及对应的蒙板Pixmap
55 pixbuf.RenderPixmapAndMask( out pixmap, out pixmask, 128);
56 ShapeCombineMask(pixmask, 0, 0);
57 GdkWindow.SetBackPixmap(pixmap, false);
58
59 ShowAll();
60 }
61
62 protected override bool OnButtonPressEvent(EventButton evnt)
63 {
64 // 鼠标左键点击移动窗体
65 if (evnt.Type == EventType.ButtonPress && evnt.Button == 1) {
66 BeginMoveDrag(( int)evnt.Button, ( int)evnt.XRoot, ( int)evnt.YRoot, evnt.Time);
67 }
68 return base.OnButtonPressEvent(evnt);
69 }
70
71 protected override bool OnExposeEvent(EventExpose evnt)
72 {
73 base.OnExposeEvent(evnt);
74
75 using (var cr = CairoHelper.Create(evnt.Window)) {
76 var curTime = DateTime.Now;
77 int tenDigit, entriesDigit;
78
79 var x = 25;
80 const int y = 80;
81 cr.Save();
82 // 绘制左侧空白
83 cr.Translate(x, y);
84 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -( 15 * 10 + 8), 0);
85 cr.Rectangle( 0, 0, 2, 25);
86 cr.Clip();
87 cr.Paint();
88
89
90 // HH
91 DecToBcd(curTime.Hour, out tenDigit, out entriesDigit);
92 x += 2;
93 cr.Restore();
94 cr.Save();
95 cr.Translate(x, y);
96 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * tenDigit, 0);
97 cr.Rectangle( 0, 0, 15, 25);
98 cr.Clip();
99 cr.Paint();
100
101 x += 15;
102 cr.Restore();
103 cr.Save();
104 cr.Translate(x, y);
105 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * entriesDigit, 0);
106 cr.Rectangle( 0, 0, 15, 25);
107 cr.Clip();
108 cr.Paint();
109
110 // :
111 x += 15;
112 cr.Restore();
113 cr.Save();
114 cr.Translate(x, y);
115 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * 10, 0);
116 cr.Rectangle( 0, 0, 8, 25);
117 cr.Clip();
118 cr.Paint();
119
120 // MM
121 DecToBcd(curTime.Minute, out tenDigit, out entriesDigit);
122 x += 8;
123 cr.Restore();
124 cr.Save();
125 cr.Translate(x, y);
126 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * tenDigit, 0);
127 cr.Rectangle( 0, 0, 15, 25);
128 cr.Clip();
129 cr.Paint();
130
131 x += 15;
132 cr.Restore();
133 cr.Save();
134 cr.Translate(x, y);
135 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * entriesDigit, 0);
136 cr.Rectangle( 0, 0, 15, 25);
137 cr.Clip();
138 cr.Paint();
139
140 // :
141 x += 15;
142 cr.Restore();
143 cr.Save();
144 cr.Translate(x, y);
145 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * 10, 0);
146 cr.Rectangle( 0, 0, 8, 25);
147 cr.Clip();
148 cr.Paint();
149
150 // SS
151 DecToBcd(curTime.Second, out tenDigit, out entriesDigit);
152 x += 8;
153 cr.Restore();
154 cr.Save();
155 cr.Translate(x, y);
156 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * tenDigit, 0);
157 cr.Rectangle( 0, 0, 15, 25);
158 cr.Clip();
159 cr.Paint();
160
161 x += 15;
162 cr.Restore();
163 cr.Save();
164 cr.Translate(x, y);
165 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, - 15 * entriesDigit, 0);
166 cr.Rectangle( 0, 0, 15, 25);
167 cr.Clip();
168 cr.Paint();
169
170 // 绘制右侧空白
171 x += 15;
172 cr.Restore();
173 cr.Save();
174 cr.Translate(x, y);
175 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -( 15 * 10 + 8), 0);
176 cr.Rectangle( 0, 0, 2, 25);
177 cr.Clip();
178 cr.Paint();
179
180 cr.Restore();
181 }
182
183 return true;
184 }
185
186 bool OnTimer()
187 {
188 if (!timer) return false;
189
190 QueueDraw();
191 return true;
192 }
193
194 // 依据两位数获取该数字的十位、个位数
195 private static void DecToBcd( int decimalNum, out int tenDigit, out int entriesDigit)
196 {
197 tenDigit = decimalNum / 10;
198 entriesDigit = decimalNum % 10;
199 }
200 }
201 }
主窗体背景图片:
时钟数字图片:
程序运行图片: