趁热打铁赶快把我们这个画板完成吧~
上次讲Brush的时候,因为觉得太简单把color设置跳过了,现在实际写的时候才发现,因为我们设置了颜色需要对刷子也有效,所以实际上set_color方法还有一点点收尾工作需要做:
1
2
3
4
5
6
|
def
set_color
(
self
,
color
)
:
self
.
color
=
color
for
i
in
xrange
(
self
.
brush
.
get_width
(
)
)
:
for
j
in
xrange
(
self
.
brush
.
get_height
(
)
)
:
self
.
brush
.
set_at
(
(
i
,
j
)
,
color
+
(
self
.
brush
.
get_at
(
(
i
,
j
)
)
.
a
,
)
)
|
也就是在设定color的时候,顺便把笔刷的颜色也改了,但是要保留原来的alpha值,其实也很简单就是了……
按钮菜单部分
上图可以看到,按钮部分分别为铅笔、毛笔、尺寸大小、(当前样式)、颜色选择者几个组成。我们只以笔刷选择为例讲解一下,其他的都是类似的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 初始化部分
self
.
sizes
=
[
pygame
.
image
.
load
(
"big.png"
)
.
convert_alpha
(
)
,
pygame
.
image
.
load
(
"small.png"
)
.
convert_alpha
(
)
]
self
.
sizes_rect
=
[
]
for
(
i
,
img
)
in
enumerate
(
self
.
sizes
)
:
rect
=
pygame
.
Rect
(
10
+
i
*
32
,
138
,
32
,
32
)
self
.
sizes_rect
.
append
(
rect
)
# 绘制部分
for
(
i
,
img
)
in
enumerate
(
self
.
pens
)
:
self
.
screen
.
blit
(
img
,
self
.
pens_rect
[
i
]
.
topleft
)
# 点击判断部分
for
(
i
,
rect
)
in
enumerate
(
self
.
pens_rect
)
:
if
rect
.
collidepoint
(
pos
)
:
self
.
brush
.
set_brush_style
(
bool
(
i
)
)
return
True
|
这些代码实际上是我这个例子最想给大家说明的地方,按钮式我们从未接触过的东西,然而游戏中按钮的应用我都不必说。
不过这代码也都不困难,基本都是我们学过的东西,只不过变换了一下组合而已,我稍微说明一下:
初始化部分:读入图标,并给每个图标一个Rect
绘制部分: 根据图表的Rect绘制图表
点击判断部分:根据点击的位置,依靠“碰撞”来判断这个按钮是否被点击,若点击了,则做相应的操作(这里是设置样式)后返回True。这里的collidepoint()是新内容,也就是Rect的“碰撞”函数,它接收一个坐标,如果在Rect内部,就返回True,否则False。
好像也就如此,有了一定的知识积累后,新东西的学习也变得易如反掌了。
在这个代码中,为了明晰,我把各个按钮按照功能都分成了好几组,在实际应用中按钮数量很多的时候可能并不合适,请自己斟酌。
完整代码
OK,这就结束了~ 下面把整个代码贴出来。不过,我是一边写代码一遍写文章,思路不是很连贯,而且python也好久不用了……如果有哪里写的有问题(没有就怪了),还请不吝指出!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
import
pygame
from
pygame
.
locals
import
*
import
math
# 2011/08/27 Version 1, first imported
class
Brush
(
)
:
def
__init__
(
self
,
screen
)
:
self
.
screen
=
screen
self
.
color
=
(
0
,
0
,
0
)
self
.
size
=
1
self
.
drawing
=
False
self
.
last_pos
=
None
self
.
space
=
1
# if style is True, normal solid brush
# if style is False, png brush
self
.
style
=
False
# load brush style png
self
.
brush
=
pygame
.
image
.
load
(
"brush.png"
)
.
convert_alpha
(
)
# set the current brush depends on size
self
.
brush_now
=
self
.
brush
.
subsurface
(
(
0
,
0
)
,
(
1
,
1
)
)
def
start_draw
(
self
,
pos
)
:
self
.
drawing
=
True
self
.
last_pos
=
pos
def
end_draw
(
self
)
:
self
.
drawing
=
False
def
set_brush_style
(
self
,
style
)
:
print
"* set brush style to"
,
style
self
.
style
=
style
def
get_brush_style
(
self
)
:
return
self
.
style
def
get_current_brush
(
self
)
:
return
self
.
brush_now
def
set_size
(
self
,
size
)
:
if
size
<
0.5
:
size
=
0.5
elif
size
>
32
:
size
=
32
print
"* set brush size to"
,
size
self
.
size
=
size
self
.
brush_now
=
self
.
brush
.
subsurface
(
(
0
,
0
)
,
(
size
*
2
,
size
*
2
)
)
def
get_size
(
self
)
:
return
self
.
size
def
set_color
(
self
,
color
)
:
self
.
color
=
color
for
i
in
xrange
(
self
.
brush
.
get_width
(
)
)
:
for
j
in
xrange
(
self
.
brush
.
get_height
(
)
)
:
self
.
brush
.
set_at
(
(
i
,
j
)
,
color
+
(
self
.
brush
.
get_at
(
(
i
,
j
)
)
.
a
,
)
)
def
get_color
(
self
)
:
return
self
.
color
def
draw
(
self
,
pos
)
:
if
self
.
drawing
:
for
p
in
self
.
_get_points
(
pos
)
:
# draw eveypoint between them
if
self
.
style
==
False
:
pygame
.
draw
.
circle
(
self
.
screen
,
self
.
color
,
p
,
self
.
size
)
else
:
self
.
screen
.
blit
(
self
.
brush_now
,
p
)
self
.
last_pos
=
pos
def
_get_points
(
self
,
pos
)
:
""" Get all points between last_point ~ now_point. """
points
=
[
(
self
.
last_pos
[
0
]
,
self
.
last_pos
[
1
]
)
]
len_x
=
pos
[
0
]
-
self
.
last_pos
[
0
]
len_y
=
pos
[
1
]
-
self
.
last_pos
[
1
]
length
=
math
.
sqrt
(
len_x
*
*
2
+
len_y
*
*
2
)
step_x
=
len_x
/
length
step_y
=
len_y
/
length
for
i
in
xrange
(
int
(
length
)
)
:
points
.
append
(
(
points
[
-
1
]
[
0
]
+
step_x
,
points
[
-
1
]
[
1
]
+
step_y
)
)
points
=
map
(
lambda
x
:
(
int
(
0.5
+
x
[
0
]
)
,
int
(
0.5
+
x
[
1
]
)
)
,
points
)
# return light-weight, uniq integer point list
return
list
(
set
(
points
)
)
class
Menu
(
)
:
def
__init__
(
self
,
screen
)
:
self
.
screen
=
screen
self
.
brush
=
None
self
.
colors
=
[
(
0xff
,
0x00
,
0xff
)
,
(
0x80
,
0x00
,
0x80
)
,
(
0x00
,
0x00
,
0xff
)
,
(
0x00
,
0x00
,
0x80
)
,
(
0x00
,
0xff
,
0xff
)
,
(
0x00
,
0x80
,
0x80
)
,
(
0x00
,
0xff
,
0x00
)
,
(
0x00
,
0x80
,
0x00
)
,
(
0xff
,
0xff
,
0x00
)
,
(
0x80
,
0x80
,
0x00
)
,
(
0xff
,
0x00
,
0x00
)
,
(
0x80
,
0x00
,
0x00
)
,
(
0xc0
,
0xc0
,
0xc0
)
,
(
0xff
,
0xff
,
0xff
)
,
(
0x00
,
0x00
,
0x00
)
,
(
0x80
,
0x80
,
0x80
)
,
]
self
.
colors_rect
=
[
]
for
(
i
,
rgb
)
in
enumerate
(
self
.
colors
)
:
rect
=
pygame
.
Rect
(
10
+
i
%
2
*
32
,
254
+
i
/
2
*
32
,
32
,
32
)
self
.
colors_rect
.
append
(
rect
)
self
.
pens
=
[
pygame
.
image
.
load
(
"pen1.png"
)
.
convert_alpha
(
)
,
pygame
.
image
.
load
(
"pen2.png"
)
.
convert_alpha
(
)
]
self
.
pens_rect
=
[
]
for
(
i
,
img
)
in
enumerate
(
self
.
pens
)
:
rect
=
pygame
.
Rect
(
10
,
10
+
i
*
64
,
64
,
64
)
self
.
pens_rect
.
append
(
rect
)
self
.
sizes
=
[
pygame
.
image
.
load
(
"big.png"
)
.
convert_alpha
(
)
,
pygame
.
image
.
load
(
"small.png"
)
.
convert_alpha
(
)
]
self
.
sizes_rect
=
[
]
for
(
i
,
img
)
in
enumerate
(
self
.
sizes
)
:
rect
=
pygame
.
Rect
(
10
+
i
*
32
,
138
,
32
,
32
)
self
.
sizes_rect
.
append
(
rect
)
def
set_brush
(
self
,
brush
)
:
self
.
brush
=
brush
def
draw
(
self
)
:
# draw pen style button
for
(
i
,
img
)
in
enumerate
(
self
.
pens
)
:
self
.
screen
.
blit
(
img
,
self
.
pens_rect
[
i
]
.
topleft
)
# draw < > buttons
for
(
i
,
img
)
in
enumerate
(
self
.
sizes
)
:
self
.
screen
.
blit
(
img
,
self
.
sizes_rect
[
i
]
.
topleft
)
# draw current pen / color
self
.
screen
.
fill
(
(
255
,
255
,
255
)
,
(
10
,
180
,
64
,
64
)
)
pygame
.
draw
.
rect
(
self
.
screen
,
(
0
,
0
,
0
)
,
(
10
,
180
,
64
,
64
)
,
1
)
size
=
self
.
brush
.
get_size
(
)
x
=
10
+
32
y
=
180
+
32
if
self
.
brush
.
get_brush_style
(
)
:
x
=
x
-
size
y
=
y
-
size
self
.
screen
.
blit
(
self
.
brush
.
get_current_brush
(
)
,
(
x
,
y
)
)
else
:
pygame
.
draw
.
circle
(
self
.
screen
,
self
.
brush
.
get_color
(
)
,
(
x
,
y
)
,
size
)
# draw colors panel
for
(
i
,
rgb
)
in
enumerate
(
self
.
colors
)
:
pygame
.
draw
.
rect
(
self
.
screen
,
rgb
,
self
.
colors_rect
[
i
]
)
def
click_button
(
self
,
pos
)
:
# pen buttons
for
(
i
,
rect
)
in
enumerate
(
self
.
pens_rect
)
:
if
rect
.
collidepoint
(
pos
)
:
self
.
brush
.
set_brush_style
(
bool
(
i
)
)
return
True
# size buttons
for
(
i
,
rect
)
in
enumerate
(
self
.
sizes_rect
)
:
if
rect
.
collidepoint
(
pos
)
:
if
i
:
# i == 1, size down
self
.
brush
.
set_size
(
self
.
brush
.
get_size
(
)
-
0.5
)
else
:
self
.
brush
.
set_size
(
self
.
brush
.
get_size
(
)
+
0.5
)
return
True
# color buttons
for
(
i
,
rect
)
in
enumerate
(
self
.
colors_rect
)
:
if
rect
.
collidepoint
(
pos
)
:
self
.
brush
.
set_color
(
self
.
colors
[
i
]
)
return
True
return
False
class
Painter
(
)
:
def
__init__
(
self
)
:
self
.
screen
=
pygame
.
display
.
set_mode
(
(
800
,
600
)
)
pygame
.
display
.
set_caption
(
"Painter"
)
self
.
clock
=
pygame
.
time
.
Clock
(
)
self
.
brush
=
Brush
(
self
.
screen
)
self
.
menu
=
Menu
(
self
.
screen
)
self
.
menu
.
set_brush
(
self
.
brush
)
def
run
(
self
)
:
self
.
screen
.
fill
(
(
255
,
255
,
255
)
)
while
True
:
# max fps limit
self
.
clock
.
tick
(
30
)
for
event
in
pygame
.
event
.
get
(
)
:
if
event
.
type
==
QUIT
:
return
elif
event
.
type
==
KEYDOWN
:
# press esc to clear screen
if
event
.
key
==
K_ESCAPE
:
self
.
screen
.
fill
(
(
255
,
255
,
255
)
)
elif
event
.
type
==
MOUSEBUTTONDOWN
:
# <= 74, coarse judge here can save much time
if
(
(
event
.
pos
)
[
0
]
<=
74
and
self
.
menu
.
click_button
(
event
.
pos
)
)
:
# if not click on a functional button, do drawing
pass
else
:
self
.
brush
.
start_draw
(
event
.
pos
)
elif
event
.
type
==
MOUSEMOTION
:
self
.
brush
.
draw
(
event
.
pos
)
elif
event
.
type
==
MOUSEBUTTONUP
:
self
.
brush
.
end_draw
(
)
self
.
menu
.
draw
(
)
pygame
.
display
.
update
(
)
if
__name__
==
'__main__'
:
app
=
Painter
(
)
app
.
run
(
)
|
200行左右,注释也不是很多,因为在这两篇文章里都讲了,有哪里不明白的请留言,我会根据实际情况再改改。
本次使用的资源文件打包
这次的pygame知识点:
- 屏幕Surface和图像Surface
- 图像绘制和图形绘制(是不是有人不明白“图像”和“图形”的区别?简单的说,图像指的是那些图片文件,图形指的是用命令画出来形状)
- 按钮的实现(新内容)
认真的朋友一定发现了本次没有涉及到动画和声音,毕竟这次只是简单的例子,太复杂了不免让人生畏。
实际用一下,会发现这个例子有很多不足,比如画错了不能撤消,只能用白色画掉(当然真正的艺术家都不用橡皮来着);调节画笔大小的时候太麻烦,点一下跳个0.5(你可以试着加上快捷键);窗口尺寸不能变,图片不能打开不能保存……不足一大堆啊,不说了,自己都要伤心了~ 但是只要你掌握了原理,所有的自己期望的功能都能慢慢实现。看着手中的程序慢慢成长,不是很有成就感么?它甚至有可能变的史无前例的强大,难道不是么?
下一个实战是什么?尽请期待~
# 另,非常欢迎有绘图高手用这个画个漂亮点的给我,我好把题头的图片换掉,太吓人了……