这是将近一年前学校开设的Python高级语言程序课上的一次作业,最近太久没打代码所以又翻出来重新温习了一遍,希望对自己以后做项目开发软件设计方面有所帮助
橡皮擦视作颜色为白的笔即可
我们给画笔一个drawing开关记录现在是否在画画(也就是当我们在画布上按下鼠标左键时drawing变为True)同时记录笔刷的上一位置也即las_pos
当我们捕捉到鼠标移动时,我们开始绘制曲线,我们希望笔刷画出的轨迹相对圆润平滑,所以我们在las_pos以及当前pos的连线上等间距选取圆心,并以当前画笔大小(size)为半径作实心圆
最后当我们捕捉到鼠标左键松开时,drawing开关关闭即可
class Brush():
def __init__(self,screen):
self.color=(0,0,0)
self.screen=screen
self.drawing=False #绘画开关(是否按下鼠标左键)
self.size=1
self.laspos=None #记录上次事件笔刷位置
self.lascol=(0,0,0) #记录上次颜色(用于笔和橡皮之间的切换)
self.p_or_e=True #是笔还是橡皮
def start(self,pos):
self.drawing=True
self.laspos=pos
def close(self):
self.drawing=False
def set_size(self,size):
if size>50:
size=50
elif size<1:
size=1
self.size=size
def get_size(self):
return self.size
def getline(self,pos): #得到las_pos和now_pos之间直线上点的坐标(作为圆心)
lenx=pos[0]-self.laspos[0]
leny=pos[1]-self.laspos[1]
length=math.sqrt(lenx**2+leny**2)
cosx=lenx/length
sinx=leny/length
points=[]
for i in range(int(length)): #单位长度为 ‘一步’ ,得出每一步后的x,y坐标
points.append((self.laspos[0]+i*cosx,self.laspos[1]+i*sinx))
points.append((self.laspos[0]+length*cosx,self.laspos[1]+length*sinx))
return points
def Draw(self,pos):
if self.drawing==True:
for p in self.getline(pos):
pygame.draw.circle(self.screen,self.color,p,self.size) #用圆连接线条更顺滑,但是感觉还是有点毛毛刺刺的
self.laspos=pos
菜单栏上设计了吸色盘,笔刷变大变小按钮,选取橡皮或笔刷的按钮
pygame给了我们Rect也就是矩形这个非常方便的对象
rect =pygame.Rect(left,top,width,height)
同时也给了collidepoint 函数可以很便捷地判断某一位置是否在当前矩形范围内
吸色盘只需设计多个矩形(这里用了8*2)并在其中填充相应颜色就好,当鼠标左键按下时根据相应坐标就能知道选取了什么颜色
其余部分也都一样,各自设置一下对应的矩形位置就能判断点击了哪里
class Menu():
def __init__(self,screen,brush):
self.screen=screen
self.brush=brush
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), #颜色(RGB)
]
self.color_rec=[]
for (i,col) in enumerate(self.colors):
if i%2==0:
rect=pygame.Rect(10,10+i//2+32*(i//2),32,32)
else:
rect=pygame.Rect(43,10+i//2+32*(i//2),32,32)#10+7+32*7+32
self.color_rec.append(rect)
self.pen=pygame.image.load('./img/pen.png').convert_alpha()
self.plus=pygame.image.load('./img/plus.png').convert_alpha()
self.minus=pygame.image.load('./img/minus.png').convert_alpha()
self.eraser=pygame.image.load('./img/eraser.png').convert_alpha()
self.pen_rec=Rect(10,275,28,28)
self.plus_rec=Rect(10,320,28,28)
self.minus_rec=Rect(40,320,28,28)
self.eraser_rec=Rect(40,275,28,28) #设置每个矩形对象方便判断鼠标点击位置
def draw(self):
self.screen.blit(self.pen,self.pen_rec.topleft)
self.screen.blit(self.eraser,self.eraser_rec.topleft)
self.screen.blit(self.plus,self.plus_rec.topleft)
self.screen.blit(self.minus,self.minus_rec.topleft)
for (i,col) in enumerate(self.color_rec):
pygame.draw.rect(self.screen,self.colors[i],col)
def click_button(self,pos): #左键点击菜单
for (i,col) in enumerate(self.color_rec):
if col.collidepoint(pos):
self.brush.p_or_e=True
self.brush.color=self.colors[i]
self.brush.lascol=self.colors[i]
return True
if self.pen_rec.collidepoint(pos):
if self.brush.p_or_e==False:
self.brush.p_or_e=True
self.brush.color=self.brush.lascol
return True
if self.eraser_rec.collidepoint(pos):
if self.brush.p_or_e==True:
self.brush.p_or_e=False
self.brush.color=(255,255,255)
return True
if self.plus_rec.collidepoint(pos):
self.brush.set_size(self.brush.get_size()+1)
return True
if self.minus_rec.collidepoint(pos):
self.brush.set_size(self.brush.get_size()-1)
return True
return False
在这部分定义了快捷键,esc 清空屏幕,ctrl+z 撤销,ctrl+s 保存。
先使用key.get_pressed 捕获按下的键并保存在key_pressed中:
key_pressed = pygame.key.get_pressed()
判断组合键只需*keypressed[K_LCTRL]与keypressed[K_letter]*同时判定为true即可
esc即把画板全涂成白色,ctrl+s 即将当前图像按照 “Draw+tot”的格式保存即可
对于撤销操作,我们只需开一个栈:
las_screen=[] #用来保存之前的surface对象,便于撤回操作
然后依次保存每次动笔前的surface对象,按下ctrl+z 使用 blit 函数覆盖当前界面并弹栈
self.screen.blit(las_screen[len(las_screen)-1],(0,0))
las_screen.pop()
画板部分代码:
class Painter():
def __init__(self):
pygame.display.set_caption('Painter')
self.clock=pygame.time.Clock()
self.screen=pygame.display.set_mode(fbl,0,32)
self.brush=Brush(self.screen)
self.menu=Menu(self.screen,self.brush)
def run(self):
tot=1
self.screen.fill((255,255,255)) #背景涂白
while True:
self.clock.tick(60) #设置一下每秒循环次数,不占用太多cpu
for event in pygame.event.get():
if event.type==QUIT:
exit()
elif event.type==MOUSEBUTTONDOWN:
if self.menu.click_button(event.pos): #如果按在菜单栏里面
pass
elif event.pos[0]>=0 and event.pos[1]<=fbl[0] and event.pos[1]>=0 and event.pos[1]<=fbl[1]:
las_screen.append(self.screen.copy())
#print(las_screen)
self.brush.start(event.pos) #没按在菜单栏里,开始画画
elif event.type==MOUSEMOTION:
self.brush.Draw(event.pos)
elif event.type==MOUSEBUTTONUP:
self.brush.close()
elif event.type==KEYDOWN:
key_pressed = pygame.key.get_pressed()
if key_pressed[K_LCTRL] and key_pressed[K_s]: #按ctrl+S保存画的图,后保存的图会覆盖之前的
pygame.image.save(self.screen,'Draw'+str(tot)+'.png')
tot+=1
elif key_pressed[K_LCTRL] and key_pressed[K_z]:
if len(las_screen)==0:
pass
else:
self.screen.blit(las_screen[len(las_screen)-1],(0,0)) #ctrl+z撤回这个地方我弄了好久,但是最后还是没弄很明白(望解答qaq)我最开始并没有用blit函数往上填涂,直接将las_screen最后一个surface对象赋值给self.screen但是并没能实现撤回的功能?这是为什么(我本来觉得应该是可行的……在MOUSEBUTTONDOWN那里也捣鼓了半天,而且输出的las_screen列表里的surface对象样子都长一个样(只有分辨率和色深的信息……
#print(las_screen[len(las_screen)-1])
las_screen.pop()
elif event.key==K_ESCAPE:
las_screen.append(self.screen.copy())
self.screen.fill((255,255,255)) #按esc清空画板
self.menu.draw()
pygame.display.update()