Pregunta

Tal como lo veo, hay dos maneras de manejar los eventos del ratón para dibujar una imagen.

La primera es para detectar cuando el ratón se mueve y dibuja una línea a donde está el ratón, se muestra aquí . Sin embargo, el problema con esto es que con un tamaño cepillo grande, aparecen muchos vacíos entre cada "línea" que no es recta, ya que está utilizando tamaño de trazo de la línea para crear líneas gruesas.

La otra manera es dibujar círculos cuando el ratón se mueve como se muestra aquí . El problema con esto es que aparecen vacíos entre cada círculo si el ratón se mueve más rápido que el ordenador detecta la entrada del ratón.

Aquí hay una captura de pantalla con mis problemas con ambos:

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

¿Cuál es la mejor manera de implementar un cepillo como MS Paint, con un tamaño decente-cepillo grande y sin espacios en el trazo de la línea o sin espacios entre cada círculo?

¿Fue útil?

Solución

¿Por qué no hacer las dos cosas?

Dibuje un círculo en cada punto final y una línea entre los dos.

Editar rofl, simplemente no podía evitarlo.

En realidad, usted no desea utilizar pygame.draw.line porque hace trampa. Se llena una amplia fila 1 pixel o columna (en función del ángulo de ataque) de píxeles. Si tienes que ir a un ángulo de aproximadamente perpendicular, 0 grados o 90 grados, esto no es un problema, pero al 45 de, verás que hay una especie de habichuela efecto.

La única solución es dibujar un círculo en la distancia a cada píxel. Aquí ...

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

Otros consejos

No blitting en cada paso de bucle puede mejorar la velocidad del dibujo (usando el código adaptado de la anterior permitirá eliminar problema lag en mi máquina)

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

Para el primer problema, es necesario tener un fondo, incluso si es sólo un color. Yo tenía el mismo problema con un juego pong réplica que hice. Este es un ejemplo de un programa de dibujo que hice réplica, a la izquierda haga clic para dibujar, haga clic derecho de borrar, haga clic en la imagen en color para elegir un color, y hasta botón para borrar la pantalla:

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)

Aquí hay una versión simplificada de ejemplo el cual es, por desgracia, no ejecutable.

Cuando se mueve el ratón, eventos pygame.MOUSEMOTION se añaden a la cola de eventos que contienen la posición y el movimiento relativo. Usted puede utilizar éstos para calcular la posición anterior y luego pasar a los dos puntos a pygame.draw.line.

eventos pygame.MOUSEMOTION también tienen un atributo buttons que se puede utilizar para comprobar qué botón del ratón está actualmente abajo.

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()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top