pygame实现的GUI画板

写在前面

这是将近一年前学校开设的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()

最终效果

pygame实现的GUI画板_第1张图片

你可能感兴趣的:(Python,pygame,python,开发语言)