Ваш код имеет несколько недостатков:
- Вы смешиваете бизнес-логику и чертеж, который загромождает ваш код.Мы исправим это позже
- В вашей функции
icon
вы устанавливаете локальные переменные (например, airbrush
и pencil
и т. Д.), Что не влияет на глобальные переменные с тем же именем.Также вы возвращаете их значение после их установки и никогда не используете это значение. - Если ваша функция
airbrush
будет вызвана, вы никогда не сможете оставить эту функцию, потому что нет способа установить airbrush
в значение false - Функция также не будет работать, потому что вы не обрабатываете события в ее цикле, что приведет к тому, что ваше окно перестанет отвечать на запросы после заполнения очереди событий
Вы должны использоватьполиморфизм, что-то вроде этого:
import pygame
import pygame.freetype
import random
def bresenham_line(start, end):
x1, y1 = start
x2, y2 = end
dx = x2 - x1
dy = y2 - y1
is_steep = abs(dy) > abs(dx)
if is_steep:
x1, y1 = y1, x1
x2, y2 = y2, x2
swapped = False
if x1 > x2:
x1, x2 = x2, x1
y1, y2 = y2, y1
swapped = True
dx = x2 - x1
dy = y2 - y1
error = int(dx / 2.0)
ystep = 1 if y1 < y2 else -1
y = y1
points = []
for x in range(x1, x2 + 1):
coord = (y, x) if is_steep else (x, y)
points.append(coord)
error -= abs(dy)
if error < 0:
y += ystep
error += dx
if swapped:
points.reverse()
return points
class Brush(pygame.sprite.Sprite):
def __init__(self, pos, font, canvas, tmpcanvas, icon, brushes, offset):
super().__init__(brushes)
self.image = pygame.Surface((32, 32))
self.image.fill(pygame.Color('grey'))
font.render_to(self.image, (8, 7), icon)
self.other_image = self.image.copy()
pygame.draw.rect(self.other_image, pygame.Color('red'), self.other_image.get_rect(), 3)
self.rect = self.image.get_rect(topleft=pos)
self.active = False
self.canvas = canvas
self.tmpcanvas = tmpcanvas
self.brushes = brushes
self.offset = offset
self.mouse_pos = None
def translate(self, pos):
return pos[0] - self.offset[0], pos[1] - self.offset[1]
def draw_to_canvas(self):
pass
def flip(self):
self.active = not self.active
self.image, self.other_image = self.other_image, self.image
def update(self, events):
for e in events:
if e.type == pygame.MOUSEBUTTONDOWN and self.rect.collidepoint(e.pos):
for brush in self.brushes:
if brush.active:
brush.flip()
self.flip()
self.mouse_pos = self.translate(pygame.mouse.get_pos())
if self.active:
self.draw_to_canvas()
class Pencil(Brush):
def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
super().__init__(pos, font, canvas, tmpcanvas, 'P', brushes, offset)
self.prev_pos = None
def draw_to_canvas(self):
pressed = pygame.mouse.get_pressed()
if pressed[0] and self.prev_pos:
pygame.draw.line(self.canvas, pygame.Color('red'), self.prev_pos, self.mouse_pos)
self.prev_pos = self.mouse_pos
class Calligraphy(Brush):
def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
super().__init__(pos, font, canvas, tmpcanvas, 'C', brushes, offset)
self.prev_pos = None
def draw_to_canvas(self):
pressed = pygame.mouse.get_pressed()
if pressed[0] and self.prev_pos:
for x, y in bresenham_line(self.prev_pos, self.mouse_pos):
pygame.draw.rect(self.canvas, pygame.Color('orange'), (x, y, 5, 15))
self.prev_pos = self.mouse_pos
class Airbrush(Brush):
def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
super().__init__(pos, font, canvas, tmpcanvas, 'A', brushes, offset)
def draw_to_canvas(self):
pressed = pygame.mouse.get_pressed()
if pressed[0]:
pygame.draw.circle(self.canvas, pygame.Color('green'),
(self.mouse_pos[0] + random.randrange(-13, 13), self.mouse_pos[1] + random.randrange(-13, 13)),
random.randrange(1, 5))
class LineTool(Brush):
def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
super().__init__(pos, font, canvas, tmpcanvas, 'L', brushes, offset)
self.start = None
def draw_to_canvas(self):
pressed = pygame.mouse.get_pressed()
if pressed[0]:
if not self.start:
self.start = self.mouse_pos
pygame.draw.line(self.tmpcanvas, pygame.Color('yellow'), self.start, self.mouse_pos)
else:
if self.start:
pygame.draw.line(self.canvas, pygame.Color('yellow'), self.start, self.mouse_pos)
self.start = None
class Clear(Brush):
def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
super().__init__(pos, font, canvas, tmpcanvas, '*', brushes, offset)
def draw_to_canvas(self):
pressed = pygame.mouse.get_pressed()
if pressed[0]:
self.canvas.fill((1, 1, 1))
self.flip()
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
sprites = pygame.sprite.Group()
clock = pygame.time.Clock()
font = pygame.freetype.SysFont(None, 26)
offset = 0, 50
canvas = pygame.Surface((500, 450))
canvas.set_colorkey((1,1,1))
canvas.fill((1,1,1))
tmpcanvas = canvas.copy()
x=10
for tool in (Pencil, Calligraphy, Airbrush, LineTool, Clear):
tool((x, 10), font, canvas, tmpcanvas, sprites, offset)
x+= 40
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
tmpcanvas.fill((1, 1, 1))
sprites.update(events)
screen.fill((30, 30, 30))
screen.blit(canvas, offset)
screen.blit(tmpcanvas, offset)
sprites.draw(screen)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
![enter image description here](https://i.stack.imgur.com/iYzYD.gif)
В этом примере у нас есть базовый класс Brush
, который обрабатывает рисование значка и сохранениеотслеживание состояния active
кисти, и подклассы обрабатывают фактический чертеж.
Таким образом, мы можем легко добавить новые кисти / инструменты, создав новый класс и реализовав draw_to_canvas
функция.