How to create MS Paint clone with Python and pygame

后端 未结 4 1373
花落未央
花落未央 2020-12-13 15:50

As I see it, there are two ways to handle mouse events to draw a picture.

The first is to detect when the mouse moves and draw a line to where the mouse is, shown he

相关标签:
4条回答
  • 2020-12-13 16:09

    Here's a simplified version of Matthew's example which is unfortunately not runnable.

    When the mouse is moved, pygame.MOUSEMOTION events are added to the event queue which contain the position and the relative movement. You can use these to calculate the previous position and then pass the two points to pygame.draw.line.

    pygame.MOUSEMOTION events also have a buttons attribute which you can use to check which mouse button is currently down.

    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()
    
    0 讨论(0)
  • 2020-12-13 16:18

    For the first problem, you need have a background, even if its just a color. I had the same problem with a replica pong game i made. Here is an example of a replica paint program I made, left click to draw, right click to erase, click on the color image to choose a color, and up button to clear screen:

    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)
    
    0 讨论(0)
  • 2020-12-13 16:19

    Why not do both?

    Draw a circle at each endpoint and a line between the two.

    EDIT rofl, just couldn't stop myself.

    Actually, you don't want to use pygame.draw.line because it cheats. It fills a 1 pixel wide row or column (depending on angle of attack) of pixels. If you do go at a roughly perpendicular angle, 0 deg or 90 deg, this isn't an issue, but at 45's, you'll notice a sort of string bean effect.

    The only solution is to draw a circle at every pixel's distance. Here...

    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()
    
    0 讨论(0)
  • 2020-12-13 16:24

    Not blitting at each loop step can improve the speed of the drawing (using this code adapted from the previous one allow to remove lag problem on my machine)

    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()
    
    0 讨论(0)
提交回复
热议问题