如何使用Python和Pygame创建MS Paint克隆版

15

我认为,处理鼠标事件绘制图片有两种方法。

第一种是在检测到鼠标移动时,画一条线到鼠标所在位置,如此处所示:这里。然而,使用较大的画笔大小时,由于使用线条描边大小来创建粗线,每个不直线的“线”之间会出现许多间隙。

另一种方法是在鼠标移动时画圆形,如此处所示:这里。但是,如果鼠标移动速度超过计算机检测到的鼠标输入,则每个圆形之间会出现间隙。

以下是我的两种问题的屏幕截图:

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

如何实现像MS Paint中的画笔那样,使用相当大的画笔尺寸而没有线条笔画或每个圆圈之间的间隙?


我不理解你对于线条的问题。你是指缺少端头吗,还是其他方面? - Devin Jeanpierre
只要把这两个结合起来。粗线+圆形。 - Markus Jarderot
这是我遇到的问题的截图:http://imgur.com/32DXN。使用粗线和圆圈似乎可以解决问题,但这并不太优雅,所以我会再考虑一下,并将其作为最后的手段。 - Johnston
+1 因为我不得不实现一个... 哎! - SingleNegationElimination
5个回答

16
为什么不两者兼顾呢?在每个端点处画一个圆圈,并在两个圆圈之间画一条线。
编辑:哈哈,我就是忍不住。实际上,你不想使用pygame.draw.line因为它作弊了。 它填充了一行或一列(取决于攻击角度)的1像素宽的像素。 如果您近似垂直于0度或90度走,这不是问题,但在45度处,您会注意到一种 string 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()

3

不在每个循环步骤中进行blitting可以提高绘图的速度(使用从先前代码改编而来的此代码允许在我的机器上消除延迟问题)

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

2

对于第一个问题,您需要有一个背景,即使只是一种颜色。我曾经在制作复制版乒乓球游戏时遇到同样的问题。这里是我制作的复制版绘画程序的示例,左键绘制,右键擦除,单击颜色图像选择颜色,上按钮清除屏幕:

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)

1

这是一个简化版的Matthew的示例,但不幸的是无法运行。

当鼠标移动时,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()

0
我已经制作了一个更高效的roundline函数版本。
import pygame as pg,random,math


screen = pg.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):
    pg.display.update([drawline(srf,color,start,end,radius),
                       pg.draw.circle(srf, color, end, radius),
                       pg.draw.circle(srf, color, start, radius)])

def drawline(srf, color,start,end,width):
    v=(start[0]- end[0],start[1]- end[1])
    betrag=width/math.sqrt(v[0]*v[0]+v[1]*v[1])
    v_=(v[1]*betrag,-betrag*v[0])
    return pg.draw.polygon(srf, color,(
            (int(start[0]+v_[0]),int(start[1]+v_[1])),
            (int(start[0]-v_[0]),int(start[1]-v_[1])),
            (int(end[0]-v_[0]),int(end[1]-v_[1])),
            (int(end[0]+v_[0]),int(end[1]+v_[1]))
            ))

try:
    while True:
        e = pg.event.wait()
        if e.type == pg.QUIT:
            raise StopIteration
        elif e.type == pg.MOUSEBUTTONDOWN:
            color = (random.randrange(256), random.randrange(256), random.randrange(256))
            #pg.draw.circle(screen, color, e.pos, radius)
            draw_on = True
            start=e.pos
        elif e.type == pg.MOUSEBUTTONUP:
            draw_on = False
        elif e.type == pg.MOUSEMOTION:
            if draw_on:
                roundline(screen, color, e.pos, last_pos,  radius)
            last_pos = e.pos
        elif e.type == pg.MOUSEWHEEL:
            radius=max(1,radius+e.y)
finally:
    pg.quit()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接