Как создать клонирование MS Paint с помощью Python и pygame

Как я вижу, есть два способа обработки событий мыши для рисования изображения.

Во-первых, чтобы обнаружить, когда мышь перемещается и рисует линию, где находится мышь, показано здесь. Однако проблема заключается в том, что при большом размере кисти между каждыми "прямыми", которые не являются прямыми, появляется много пробелов, так как он использует размер штриха линии для создания толстых линий.

Другой способ - нарисовать круги, когда мышь перемещается, как показано здесь. Проблема заключается в том, что между каждым кружком появляются промежутки, если мышь движется быстрее, чем компьютер обнаруживает ввод мыши.

Вот скриншот с моими проблемами с обоими:

http://imgur.com/32DXN.jpg

Каков наилучший способ реализации кисти, такой как MS Paint, с прилично большим размером кисти без пробелов в штрихе линии или без пробелов между каждым кругом?

Ответ 1

Почему бы не сделать оба?

Нарисуйте круг на каждой конечной точке и линию между ними.

РЕДАКТИРОВАТЬ rofl, просто не мог остановиться.

Собственно, вы не хотите использовать pygame.draw.line, потому что он обманывает. Он заполняет строку или столбец шириной 1 пиксель (в зависимости от угла атаки) пикселей. Если вы переходите на грубо перпендикулярный угол, 0 градусов или 90 градусов, это не проблема, но в 45 секунд вы заметите какой-то эффект строки bean.

Единственное решение - нарисовать круг на каждом расстоянии. Здесь...

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()

Ответ 2

Не биение на каждом шаге цикла может улучшить скорость рисования (использование этого кода, адаптированного из предыдущего, позволяет устранить проблему с задержкой на моей машине)

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()

Ответ 3

Для первой проблемы вам нужен фон, даже если это только цвет. У меня была та же проблема с репликой, что я сделал. Вот пример программы рисования реплик, которую я сделал, щелкните левой кнопкой мыши для рисования, щелкните правой кнопкой мыши, чтобы удалить, щелкните по цветному изображению, чтобы выбрать цвет, и кнопку "вверх" для очистки экрана:

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)