Как создать клон MS Paint с помощью Python и pygame
Вопрос
Насколько я понимаю, существует два способа обработки событий мыши для рисования изображения.
Первый — определить, когда мышь движется, и провести линию туда, где находится мышь, как показано на рисунке. здесь.Однако проблема заключается в том, что при большом размере кисти между каждой «линией», которая не является прямой, появляется множество промежутков, поскольку для создания толстых линий используется размер штриха линии.
Другой способ — рисовать круги, когда мышь движется, как показано на рисунке. здесь.Проблема в том, что между каждым кругом появляются промежутки, если мышь движется быстрее, чем компьютер обнаруживает ввод мыши.
Вот скриншот с моими проблемами с обоими:
Как лучше всего реализовать кисть, подобную MS Paint, с прилично большим размером кисти без промежутков в обводке линии или без промежутков между каждым кругом?
Решение
Почему бы не сделать и то, и другое?
Нарисуйте круг в каждой конечной точке и линию между ними.
РЕДАКТИРОВАТЬ рофл, просто не смог остановиться.
На самом деле, вы не хотите использовать pygame.draw.line
потому что это обман.Он заполняет строку или столбец шириной 1 пиксель (в зависимости от угла атаки) пикселей.Если вы двигаетесь под примерно перпендикулярным углом, 0 или 90 градусов, это не проблема, но при угле 45 вы заметите своего рода фасоль эффект.
Единственное решение — нарисовать круг на расстоянии каждого пикселя.Здесь...
import pygame, random
screen = pygame.display.set_mode((800,600))
draw_on = False
last_pos = (0, 0)
color = (255, 128, 0)
radius = 10
def roundline(srf, color, start, end, radius=1):
dx = end[0]-start[0]
dy = end[1]-start[1]
distance = max(abs(dx), abs(dy))
for i in range(distance):
x = int( start[0]+float(i)/distance*dx)
y = int( start[1]+float(i)/distance*dy)
pygame.draw.circle(srf, color, (x, y), radius)
try:
while True:
e = pygame.event.wait()
if e.type == pygame.QUIT:
raise StopIteration
if e.type == pygame.MOUSEBUTTONDOWN:
color = (random.randrange(256), random.randrange(256), random.randrange(256))
pygame.draw.circle(screen, color, e.pos, radius)
draw_on = True
if e.type == pygame.MOUSEBUTTONUP:
draw_on = False
if e.type == pygame.MOUSEMOTION:
if draw_on:
pygame.draw.circle(screen, color, e.pos, radius)
roundline(screen, color, e.pos, last_pos, radius)
last_pos = e.pos
pygame.display.flip()
except StopIteration:
pass
pygame.quit()
Другие советы
Отсутствие дублирования на каждом шаге цикла может повысить скорость рисования (использование этого кода, адаптированного из предыдущего, позволяет устранить проблему с задержкой на моей машине)
import pygame, random
screen = pygame.display.set_mode((800,600))
draw_on = False
last_pos = (0, 0)
color = (255, 128, 0)
radius = 10
def roundline(srf, color, start, end, radius=1):
dx = end[0]-start[0]
dy = end[1]-start[1]
distance = max(abs(dx), abs(dy))
for i in range(distance):
x = int( start[0]+float(i)/distance*dx)
y = int( start[1]+float(i)/distance*dy)
pygame.display.update(pygame.draw.circle(srf, color, (x, y), radius))
try:
while True:
e = pygame.event.wait()
if e.type == pygame.QUIT:
raise StopIteration
if e.type == pygame.MOUSEBUTTONDOWN:
color = (random.randrange(256), random.randrange(256), random.randrange(256))
pygame.draw.circle(screen, color, e.pos, radius)
draw_on = True
if e.type == pygame.MOUSEBUTTONUP:
draw_on = False
if e.type == pygame.MOUSEMOTION:
if draw_on:
pygame.display.update(pygame.draw.circle(screen, color, e.pos, radius))
roundline(screen, color, e.pos, last_pos, radius)
last_pos = e.pos
#pygame.display.flip()
except StopIteration:
pass
pygame.quit()
Для решения первой проблемы вам понадобится фон, даже если это просто цвет.У меня была та же проблема с копией игры в понг, которую я сделал.Вот пример созданной мной копии программы рисования: щелкните левой кнопкой мыши, чтобы нарисовать, щелкните правой кнопкой мыши, чтобы стереть, щелкните цветное изображение, чтобы выбрать цвет, и кнопку «Вверх», чтобы очистить экран:
import os
os.environ['SDL_VIDEO_CENTERED'] = '1'
from pygamehelper import *
from pygame import *
from pygame.locals import *
from vec2d import *
from math import e, pi, cos, sin, sqrt
from random import uniform
class Starter(PygameHelper):
def __init__(self):
self.w, self.h = 800, 600
PygameHelper.__init__(self, size=(self.w, self.h), fill=((255,255,255)))
self.img= pygame.image.load("colors.png")
self.screen.blit(self.img, (0,0))
self.drawcolor= (0,0,0)
self.x= 0
def update(self):
pass
def keyUp(self, key):
if key==K_UP:
self.screen.fill((255,255,255))
self.screen.blit(self.img, (0,0))
def mouseUp(self, button, pos):
pass
def mouseMotion(self, buttons, pos, rel):
if pos[1]>=172:
if buttons[0]==1:
#pygame.draw.circle(self.screen, (0,0,0), pos, 5)
pygame.draw.line(self.screen, self.drawcolor, pos, (pos[0]-rel[0], pos[1]-rel[1]),5)
if buttons[2]==1:
pygame.draw.circle(self.screen, (255,255,255), pos, 30)
if buttons[1]==1:
#RAINBOW MODE
color= self.screen.get_at((self.x, 0))
pygame.draw.line(self.screen, color, pos, (pos[0]-rel[0], pos[1]-rel[1]), 5)
self.x+= 1
if self.x>172: self.x=0
else:
if pos[0]<172:
if buttons[0]==1:
self.drawcolor= self.screen.get_at(pos)
pygame.draw.circle(self.screen, self.drawcolor, (250, 100), 30)
def draw(self):
pass
#self.screen.fill((255,255,255))
#pygame.draw.circle(self.screen, (0,0,0), (50,100), 20)
s = Starter()
s.mainLoop(40)
Вот упрощенная версия Мэтью пример который, к сожалению, не работает.
При перемещении мыши pygame.MOUSEMOTION
события добавляются в очередь событий, содержащую положение и относительное движение.Вы можете использовать их для расчета предыдущей позиции, а затем передать две точки в pygame.draw.line
.
pygame.MOUSEMOTION
события также имеют buttons
атрибут, который вы можете использовать, чтобы проверить, какая кнопка мыши в данный момент нажата.
import os
import random
import pygame as pg
class App:
def __init__(self):
os.environ['SDL_VIDEO_CENTERED'] = '1'
pg.init()
self.w, self.h = 800, 600
self.screen = pg.display.set_mode((self.w, self.h))
self.screen.fill(pg.Color('white'))
self.clock = pg.time.Clock()
self.drawcolor = (0, 0, 0)
def mainloop(self):
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 2: # Color picker (middle mouse button).
self.drawcolor = self.screen.get_at(pos)
# Pick a random color.
# self.drawcolor = [random.randrange(256) for _ in range(3)]
elif event.type == pg.MOUSEMOTION:
pos, rel = event.pos, event.rel
if event.buttons[0]: # If the left mouse button is down.
# Draw a line from the pos to the previous pos.
pg.draw.line(self.screen, self.drawcolor, pos, (pos[0]-rel[0], pos[1]-rel[1]), 5)
elif event.buttons[2]: # If the right mouse button is down.
# Erase by drawing a circle.
pg.draw.circle(self.screen, (255, 255, 255), pos, 30)
pg.display.flip()
self.clock.tick(30)
if __name__ == '__main__':
app = App()
app.mainloop()
pg.quit()