趁热打铁赶快把我们这个画板完成吧~

……鼠绘无能,不准笑!所有评论中“噗嗤”、“画的好搓啊”、“画的好棒啊”等,都会被我无情扑杀掉!但是能告诉我怎样画可以更漂亮的话,绝对欢迎。
上次讲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(你可以试着加上快捷键);窗口尺寸不能变,图片不能打开不能保存……不足一大堆啊,不说了,自己都要伤心了~ 但是只要你掌握了原理,所有的自己期望的功能都能慢慢实现。看着手中的程序慢慢成长,不是很有成就感么?它甚至有可能变的史无前例的强大,难道不是么?
下一个实战是什么?尽请期待~
# 另,非常欢迎有绘图高手用这个画个漂亮点的给我,我好把题头的图片换掉,太吓人了……
顶起 楼主厉害
刚开始学习python,想了解学习简单小游戏的编程。网页中看不到代码
您使用的是什么浏览器?居然看不到代码么,IE,FF,Chrome应该都没问题的,即使禁用了JavaScript也应该能看到,实在不行请查看源代码……
不用回复!
恩?
哈哈,博主画的比我好多了~
另外鼠标移出窗口很容易出现除0错误,得加上length == 0的处理
我是加了
if length == 0: return points
结果是好了,不过鼠标会自动跳回窗口内
呃,对了,还有draw.circle最后一个参数不接受float,是不是因为我用的pygame1.9.2pre
很有可能……
类型转换(float)报错的几处可以强制转换为int型
self.size.__int__()类似这样,可以正常运行。
请问pygame.mouse.get_pos()怎么还原回event.pos?就是将鼠标单击事件的坐标还原回鼠标单击事件本身。。。
请问pygame可以实现粒子效果吗
颜色这一块没看懂
self.brush.set_at((i, j),
color + (self.brush.get_at((i, j)).a,))
求解答!
最后一个a的逗号什么意思?
如果元组内只有一个元素 元组的创建是需要在单个元素后面加逗号的 我也是刚刚才知道
float出错是笔刷跟铅笔的绘制形式不同,笔刷是 blit,绘制起始点就是鼠标的位置,所以相对于你画的那个点是在左上角的,而铅笔则是画圆,起始点是圆心,所以相对于画出来的那个点就是在正中央
那应该怎么解决呢?
这个例子我估计又得啃一天了 ,蓝瘦,香菇。
Pingback: 用Python和Pygame写游戏-从入门到精通(实战一:涂鸦画板2)-演道网
画笔和刷子来回切换为什么会直接关闭画板的窗口
解决了,数据类型错误,把size改为整形数就行了!
在哪里改
xrange’ is not defined
这是咋回事阿?
python3.6
直接改成range即可
File “D:/6+1/SKet_2.py”, line 111, in __init__
self.colors_rect.append(rect)
AttributeError: ‘Menu’ object has no attribute ‘colors_rect’
看完你的教程后码了一遍
出现了这个问题。。。。
新人希望楼主能够帮助一下
不胜感激,谢谢。
兄弟你还在吗 我找不到一起学习的小伙伴 不知道愿不愿意加个好友 2256687517 我Q