python编程绘图实例-使用Python编写简单的画图板程序的示例教程

从这次开始,我会由简单到困难(其实也不会困难到哪里去)讲几个例程,每一个例程都是我自己写(或者修改,那样的话我会提供原始出处)的,都具有一定的操作性和娱乐性。例程中汇尽量覆盖到以前所讲的pygame中方方面面,如果看到哪一步不明白,那就再回去复习复习,基本没有人会看一遍什么都记住什么都掌握的,重复是学习之母,实践是掌握一门技艺的最好手段!

这次就先从一个最简单的程序开始,说实话有些太简单我都不好意思拿出手了,不过从简单的开始,容易建立自信培养兴趣。兴趣是学习之母嘛。我们这次做一个画板,类似Windows里自带的画板,还记不记得第一次接触电脑用画板时的惊叹?现在想起来其实那个真的非常简陋,不过我们的比那个还要朴素,因为打算一篇讲完,就不追加很多功能了,等你把这一次讲解的都理解了,很容易可以自己给它增加新的机能。没准,你就开发出一个非常牛X的画图工具击败了Photoshop,然后日进斗金名垂千古(众:喂,别做梦了!)……

功能样式

做之前总要有个数,我们的程序做出来会是个什么样子。所谓从顶到底或者从底到顶啥的,咱就不研究了,这个小程序随你怎么弄了,而且我们主要是来熟悉pygame,高级的软件设计方法一概不谈~

因为是抄袭画图板,也就是鼠标按住了能在上面涂涂画画就是了,选区、放大镜、滴管功能啥的就统统不要了。画笔的话,基本的铅笔画笔总是要的,也可以考虑加一个刷子画笔,这样有一点变化;然后颜色应该是要的,否则太过单调了,不过调色板啥的就暂时免了,提供几个候选色就好了;然后橡皮……橡皮不就是白色的画笔么?免了免了!还有啥?似乎够了。。。 OK,开始吧!

框架

pygame程序的框架都是差不多的,考虑到我们这个程序的实际作用,大概建立这样的一个代码架子就可以了。

?

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

import pygame

from pygame.locals import *

class Brush():

def __init__(self):

pass

class Painter():

def __init__(self):

self.screen= pygame.display.set_mode((800,600))

pygame.display.set_caption("Painter")

self.clock= pygame.time.Clock()

def run(self):

self.screen.fill((255,255,255))

while True:

# max fps limit

self.clock.tick(30)

for eventin pygame.event.get():

if event.type == QUIT:

return

elif event.type == KEYDOWN:

pass

elif event.type == MOUSEBUTTONDOWN:

pass

elif event.type == MOUSEMOTION:

pass

elif event.type == MOUSEBUTTONUP:

pass

pygame.display.update()

if __name__== '__main__':

app= Painter()

app.run()

import pygame

from pygame.locals import *

class Brush():

def __init__(self):

pass

class Painter():

def __init__(self):

self.screen= pygame.display.set_mode((800,600))

pygame.display.set_caption("Painter")

self.clock= pygame.time.Clock()

def run(self):

self.screen.fill((255,255,255))

while True:

# max fps limit

self.clock.tick(30)

for eventin pygame.event.get():

if event.type == QUIT:

return

elif event.type == KEYDOWN:

pass

elif event.type == MOUSEBUTTONDOWN:

pass

elif event.type == MOUSEMOTION:

pass

elif event.type == MOUSEBUTTONUP:

pass

pygame.display.update()

if __name__== '__main__':

app= Painter()

app.run()

这个非常简单,准备好画板类,画笔类,暂时还都是空的,其实也就是做了一些pygame的初始化工作。如果这样还不能读懂的话,您需要把前面22篇从头再看看,有几句话不懂就看几遍:)

这里只有一点要注意一下,我们把帧率控制在了30,没有人希望在画画的时候,CPU风扇狂转的。而且只是画板,没有自动运动的物体,纯粹的交互驱动,我们也不需要很高的刷新率。

第一次的绘图代码

按住鼠标然后在上面移动就画东西,我们很容易可以想到这个流程:

按下左键 → 绘制flag开

移动鼠标 → flag开的时候,在移动坐标上留下痕迹

放开左键 → 绘制flag关

按下左键 → 绘制flag开

移动鼠标 → flag开的时候,在移动坐标上留下痕迹

放开左键 → 绘制flag关

立刻试一试吧:

?

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

class Brush():

def __init__(self, screen):

self.screen= screen

self.color= (0,0,0)

self.size= 1

self.drawing= False

def start_draw(self):

self.drawing= True

def end_draw(self):

self.drawing= False

def draw(self, pos):

if self.drawing:

pygame.draw.circle(self.screen,self.color, pos,self.size)

class Painter():

def __init__(self):

#*#*#*#*#

self.brush= Brush(self.screen)

def run(self):

#*#*#*#*#

elif event.type == KEYDOWN:

# press esc to clear screen

if event.key== K_ESCAPE:

self.screen.fill((255,255,255))

elif event.type == MOUSEBUTTONDOWN:

self.brush.start_draw()

elif event.type == MOUSEMOTION:

self.brush.draw(event.pos)

elif event.type == MOUSEBUTTONUP:

self.brush.end_draw()

class Brush():

def __init__(self, screen):

self.screen= screen

self.color= (0,0,0)

self.size= 1

self.drawing= False

def start_draw(self):

self.drawing= True

def end_draw(self):

self.drawing= False

def draw(self, pos):

if self.drawing:

pygame.draw.circle(self.screen,self.color, pos,self.size)

class Painter():

def __init__(self):

#*#*#*#*#

self.brush= Brush(self.screen)

def run(self):

#*#*#*#*#

elif event.type == KEYDOWN:

# press esc to clear screen

if event.key== K_ESCAPE:

self.screen.fill((255,255,255))

elif event.type == MOUSEBUTTONDOWN:

self.brush.start_draw()

elif event.type == MOUSEMOTION:

self.brush.draw(event.pos)

elif event.type == MOUSEBUTTONUP:

self.brush.end_draw()

框架中有的代码我就不贴了,用#*#*#*#*#代替,最后会给出完整代码的。

这里主要是给Brush类增加了一些功能,也就是上面我们提到的流程想对应的功能。留下痕迹,我们是使用了在坐标上画圆的方法,这也是最容易想到的方法。这样的效果好不好呢?我们试一试:

110IUZ7-0.jpg

哦,太糟糕了,再劣质的铅笔也不会留下这样断断续续的笔迹。上面是当我们鼠标移动的快一些的时候,点之间的间距很大;下面是移动慢一些的时候,勉勉强强显得比较连续。从这里我们也可以看到pygame事件响应的频度(这个距离和上面设置的最大帧率有关)。

怎么办?要修改帧率让pygame平滑的反应么?不,那样做得不偿失,换一个角度思考,如果有间隙,我们让pygame把这个间隙连接起来不好么?

第二次的绘图代码

思路还是很简单,当移动的时候,Brush在上一次和这一次的点之间连一条线就好了:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class Brush():

def __init__(self, screen):

self.screen= screen

self.color= (0,0,0)

self.size= 1

self.drawing= False

self.last_pos= None # <--

def start_draw(self, pos):

self.drawing= True

self.last_pos= pos# <--

def end_draw(self):

self.drawing= False

def draw(self, pos):

if self.drawing:

pygame.draw.line(self.screen,self.color,

self.last_pos, pos,self.size* 2)

self.last_pos= pos

110IQX8-1.jpg

在__init__和start_draw中各加了一句,用来存储上一个点的位置,然后draw也由刚刚的话圆变成画线,效果如何?我们来试试。嗯,好多了,如果你动作能温柔一些的话,线条已经很圆润了,至少没有断断续续的存在了。

满足了么?我希望你的回答是"NO”,为什么,如果你划线很快的话,你就能明显看出棱角来,就好像左图上半部分,还是能看出是由几个线段组合的。只有永不满足,我们才能不停进步。

不过对我们这个例程而言,差不多了,一般人在真正画东西的时候,也不会动那么快的:)

那么这个就是我们最终的绘图机制了么?回头看看我们的样式,好用还需要加一个笔刷……所谓笔刷,不仅仅是很粗,而且是由很多细小的毛组成,画出来的线是给人一种一缕一缕的感觉,用这个方法可以实现么?好像非常非常的困难。。。孜孜不倦的我们再次进入了沉思……

这个时候,如果没有头绪,就得借鉴一下前辈的经验了。看看人家是如何实现的?

110IWH0-2.jpg

如果你的Photoshop不错,应该知道它里面复杂的笔刷设定,而Photoshop画出来的笔画,并不是真正一直线的,而是由无数细小的点组成的,这些点之间的间距是如此的密,以至于我们误会它是一直线……所以说,我们还得回到第一种方法上,把它发扬光大一下~ 这没有什么不好意思的,放弃第二种方法并不意味着我们是多么的愚蠢,而是说明我们从自己身上又学到了很多!

(公元前1800年)医生:来,试试吃点儿这种草根,感谢伟大的部落守护神赐与我们神药!

(公元900年)医生:别再吃那种草根,简直是野蛮不开化不尊重上帝,这是一篇祈祷词,每天虔诚地向上帝祈祷一次,不久就会治愈你的疾病。

(公元1650年)医生:祈祷?!封建迷信!!!来,只要喝下这种药水,什么病都能治好!

(公元1960年)医生:什么药水?早就不用了!别喝那骗人的”万灵药”,还是这种药片的疗效快!

(公元1995年)医生:哪个庸医给你开的处方?那种药片吃半瓶也抵不上这一粒,来来来,试试科技新成果—抗生素

(公元2003年)医生:据最新科学研究,抗生素副作用太强,毕竟是人造的东西呀……来,试试吃点儿这种草根!早在公元前1800年,文献就有记载了。

返璞归真,大抵如此了。

第三次的绘图代码

这次我们考虑的更多,希望在点与点之间充满我们的笔画,很自然的我们就需要一个循环来做这样的事情。我们的笔画有两种,普通的实心和刷子,实心的话,用circle来画也不失为一个好主意;刷子的话,我们可能需要一个刷子的图案来填充了。

下面是我们新的Brush类:

?

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

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 set_size(self, size):

if size <0.5: size= 0.5

elif size >50: size= 50

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 draw(self, pos):

if self.drawing:

for pin 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 iin 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 list

return list(set(points))

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 set_size(self, size):

if size <0.5: size= 0.5

elif size >50: size= 50

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 draw(self, pos):

if self.drawing:

for pin 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 iin 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 list

return list(set(points))

我们增加了几个方法,_get_points()返回上一个点到现在点之间所有的点(这话听着真别扭),draw根据这些点填充。

同时我们把get_size()、set_size()也加上了,用来设定当前笔刷的大小。

而变化最大的,则是set_style()和get_style(),我们现在载入一个PNG图片作为笔刷的样式,当style==True的时候,draw不再使用circle填充,而是使用这个PNG样式,当然,这个样式大小也是应该可调的,所有我们在set_size()中,会根据size大小实时的调整PNG笔刷。

当然,我们得在主循环中调用set方法,才能让这些东西工作起来~ 过一会儿再讲。再回顾下我们的样式,还有什么?颜色……我们马上把颜色设置代码也加进去吧,太简单了!我这里就先偷偷懒了~

控制代码

到现在,我们已经完成了绘图部分的所有功能了。现在已经可以在屏幕上自由发挥了,但是笔刷的颜色和大小好像不能改啊……我们有这样的接口你却不调用,浪费了。

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

110IQR2-3.jpg

现在实际写的时候才发现,因为我们设置了颜色需要对刷子也有效,所以实际上set_color方法还有一点点收尾工作需要做:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

def set_color(self, color):

self.color= color

for iin xrange(self.brush.get_width()):

for jin xrange(self.brush.get_height()):

self.brush.set_at((i, j),

color+ (self.brush.get_at((i, j)).a,))

def set_color(self, color):

self.color= color

for iin xrange(self.brush.get_width()):

for jin 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

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

# 初始化部分

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

# 初始化部分

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

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

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 iin xrange(self.brush.get_width()):

for jin 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 pin 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 iin 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 eventin 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()

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 iin xrange(self.brush.get_width()):

for jin 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 pin 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 iin 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):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值