pygame电子画板.py

"""pygame电子画板,简单的可以用来画画的电子画板,带保存功能"""

__author__ = "李兴球"
__date__ = "2019年1月"

import os
import time
import pygame,math
from pygame.locals import *

def make_random_filename():
    s0 = "saved"
    if not os.path.exists(os.getcwd() + os.sep + s0):os.mkdir(s0)
    s1 = time.ctime().replace(":","").replace(" ","")[-1:10:-1]
    s2 = str(time.time()).split(".")[-1]
    s3 = ".png"
    return  s0 + os.sep + "".join([s1,s2,s3])
    
class TipBubble():
    def __init__(self,image,font_file,position,screen):
        """font_file是ttf字体文件,position是保存按钮的坐标,screen是最底渲染面"""
        self.image = pygame.image.load(image)     # 字的背景图
        self.font = pygame.font.Font(font_file,18)# 字体对象
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.screen = screen                      # 可访问屏幕对象
        self.begin_time = 0
        self.draw_time = 5                        # 渲染时间
    def create_tip(self,filename):
        """单击保存按钮后,根据生成的文件名创建图像对象,渲染到self.image"""
        filename = "保存在:" + filename
        self.filename_image = self.font.render(filename,True,(0,0,0),( 255,255,255 ))
        self.image.blit(self.filename_image,(26,26))
        self.begin_time = time.time()
    def draw(self):
        if time.time() - self.begin_time < self.draw_time: # 在渲染时间内就画
            self.screen.blit(self.image,self.rect)       
        
def insert_point(a,b,step):
    """a:起点坐标二元组,b:终点坐标二元组,step:步长"""
    points = []
    x1,y1 = a       # 起点
    x2,y2 = b       # 终点
    dy = y2 - y1
    dx = x2 - x1
    angle = math.atan2(dy,dx)
    distance = int(math.sqrt(dx*dx+dy*dy))
    for d in range(1,distance,step):
        dx = d * math.cos(angle)
        dy = d * math.sin(angle)
        p = int(a[0] + dx),int(a[1] + dy)
        points.append(p)
    return points 

def draw_thickness(thickness_rect):
    """画笔触线条的函数,也就是那一横一横黑色的条,同时记录了它们的矩形对象"""
    left = thickness_rect.x
    top = thickness_rect.y
    thickness_image = pygame.Surface(thickness_rect.size)
    thickness_image.fill((255,255,255))
    dy = 8                           # 每个线条的间矩
    x,y = 10,5
    thickness_rect = []              # 记录笔宽的矩形范围左上角和右下角坐标
    for i in range(1,20):        
        width,height = 30,i
        thickness_rect.append(pygame.Rect(left+x,y+top,width,height))
        r = pygame.Rect(x,y,width,height)         
        pygame.draw.rect(thickness_image,((0,0,0)),r) # 画黑色矩形
        y = y + i + dy
    return thickness_image,thickness_rect
 
class Pen():
    def __init__(self,images,screen,board,mouse_pos=None):
        self.images = images         
        self.screen = screen
        self.board = board
        self.thickness = 10
        self.alt_pen(mouse_pos)
        
    def alt_pen(self,mouse_pos):
        """切换到笔模式"""
        self.shape = '笔'
        self.image = self.images[0]
        self.color = (0,0,0)
        self.rect = self.image.get_rect()        
        self.rect.bottomleft = mouse_pos
        
    def alt_erase(self,mouse_pos):
        """切换到橡皮模式"""
        self.shape = '橡'
        self.image = self.images[1]
        self.color = (255,255,255)
        self.rect = self.image.get_rect()
        self.rect.bottomleft = mouse_pos
        
    def update(self,mouse_pos):
        self.rect.bottomleft = mouse_pos
        
    def draw(self):
        self.screen.blit(self.image,self.rect)
        
    def paint(self,previous_point,mouse_pos,):
        """在画板上画圆"""
        boardx = self.board.rect.x
        boardy = self.board.rect.y
        
        dx = mouse_pos[0] - previous_point[0]   # 鼠标指针x和前一个点的x距离
        dy = mouse_pos[1] - previous_point[1]   # 鼠标指针y和前一个点的y距离
        distance = math.sqrt(dx*dx + dy*dy)

        "由于是在board上画,所以坐标要减去board相对于屏幕的坐标点"
        position  = mouse_pos[0] - boardx,mouse_pos[1] - boardy # 相对坐标
        pygame.draw.circle(self.board.image, self.color, position, self.thickness)

        if distance>=4:      # 如果距离大于等于2,则线性插入画点
            
            previous_point = previous_point[0] - boardx,previous_point[1] - boardy
            mouse_pos = mouse_pos[0] - boardx,mouse_pos[1] - boardy
            points = insert_point(previous_point,mouse_pos,1)
             
            for p in points:                 
                pygame.draw.circle(self.board.image, self.color, p, self.thickness)             
            
    def pickcolor(self,pos):
        if self.images.index ==1 :return
        x,y = pos
        if x>=114 and x <=750 and y >=28 and y <=42 and self.shape == '笔': 
           self.color = self.screen.get_at(pos)

    def pickthickness(self,pos,_rect):
 
        for i in range(len(_rect)):
            if _rect[i].collidepoint(pos):
                self.thickness = i+1
                break
            
    def clear(self,pos):        
        x,y = pos
        if x>=32 and x <=73 and y >=494 and y <=531: 
           self.board.image.fill((255,255,255))
        
        
class Board():
    """画板类"""
    def __init__(self,position,size,):
        self.image = pygame.Surface(size)
        self.image.fill((255,255,255))        
        self.rect = self.image.get_rect()
        self.rect.topleft = position
    

if __name__ == "__main__":
    
    game_title = "pygame电子画板_作者:李兴球,风火轮少儿编程,www.scratch8.net"
    width,height = 800,600
    background =  "background.png"
    topleft = (120,140)              # 画板左上角坐标
    dsize =  640,430                 # 画板宽度和高度
    pygame.init()
    screen = pygame.display.set_mode((width,height))
    pygame.display.set_caption(game_title)
    background = pygame.image.load(background)
    pen_image = pygame.image.load("pen_image.png")
    erase_image = pygame.image.load("erase_image.png")
    pe_images = [pen_image,erase_image]
    thickness_rect = pygame.Rect(30,20,60,350)
    thickness_image,t_rect = draw_thickness(thickness_rect)
    clock = pygame.time.Clock()
    running = True
    board = Board(topleft,dsize)      # 画板
    pos = pygame.mouse.get_pos()
    pen = Pen(pe_images,screen,board,pos) # 新建笔 
    start_draw = 0
    previous_point = (0,0)                # 上一个点
    tipbubble = TipBubble("dialog.png","msyh.ttf",(90,450),screen)                     
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
                break
            if event.type == MOUSEMOTION:
                title_string = game_title + " " + str(event.pos)
                pygame.display.set_caption(title_string)
                pen.update(event.pos)
                
                if start_draw == 1:
                    #print(event.pos)
                    pen.paint(previous_point,event.pos)
                    previous_point = event.pos
            if event.type == MOUSEBUTTONDOWN and start_draw == 0:
                previous_point = event.pos             # 记录先前的坐标
                x,y = event.pos
                start_draw = 1
                #draw_point_cors = [event.pos]          # 记录鼠标移动的每个坐标点
                pen.paint(previous_point,event.pos)
                pen.pickcolor(event.pos)               # 在某坐标范围,则取颜色
                pen.pickthickness(event.pos,t_rect)    # 在某坐标范围内选择粗细的画笔
                if x>=40 and x <=70 and y >=438 and y <=468:
                    pen.alt_pen(event.pos)
                if x>=40 and x <=70 and y >=386 and y <=420:
                    pen.alt_erase(event.pos)
                
                if x>=39 and x <=68 and y >=556 and y <=584:
                    filename = make_random_filename()  # 生成随机文件名
                    pygame.image.save(board.image,filename)
                    tipbubble.create_tip(filename) # 提示泡泡                   
                
                pen.clear(event.pos)          # 清除所画, 以白色填充board.image
                 
            if event.type == MOUSEBUTTONUP:
                start_draw = 0
                                
        screen.blit(background,(0,0))
        screen.blit(board.image,board.rect)        
        screen.blit(thickness_image,thickness_rect)       
        pen.draw()
        tipbubble.draw()        
        pygame.display.update()
        clock.tick(30)
    pygame.quit()