【问题标题】:How to create MS Paint clone with Python and pygame如何使用 Python 和 pygame 创建 MS Paint 克隆
【发布时间】:2009-02-28 02:13:08
【问题描述】:

在我看来,有两种方法可以处理鼠标事件来绘制图片。

第一个是检测鼠标何时移动并画一条线到鼠标所在的位置,显示为here。但是,这样做的问题是,对于较大的画笔,每条不直的“线”之间会出现许多间隙,因为它使用线的笔触大小来创建粗线。

另一种方法是在鼠标移动时绘制圆圈,如图here 所示。这样做的问题是,如果鼠标移动的速度快于计算机检测到鼠标输入的速度,每个圆圈之间就会出现间隙。

这是我的两个问题的屏幕截图:

实现像 MS Paint 这样的画笔的最佳方式是什么,画笔尺寸相当大,线条笔划中没有间隙或每个圆圈之间没有间隙?

【问题讨论】:

  • 我不明白你的线路问题。您是在谈论缺少端盖,还是...?
  • 只需将两者结合起来。粗线+圆圈。
  • 这是我的两个问题的截图:imgur.com/32DXN 同时使用粗线和圆圈似乎可以解决问题,但听起来不太优雅,所以我会考虑一下更多的,并将其用作最后的手段。
  • +1 因为我必须实现一个……哎呀!

标签: python mouse pygame draw paint


【解决方案1】:

为什么不两者都做?

在每个端点处画一个圆圈,并在两者之间画一条线。

EDIT rofl,我无法阻止自己。

实际上,您不想使用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()

【讨论】:

    【解决方案2】:

    在每个循环步骤中不进行 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()
    

    【讨论】:

      【解决方案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)
      

      【讨论】:

        【解决方案4】:

        这是 Matthew 的 example 的简化版本,很遗憾它无法运行。

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

        【讨论】:

          【解决方案5】:

          我制作了一个更高效的圆形函数。

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

          【讨论】:

            猜你喜欢
            • 2011-05-06
            • 1970-01-01
            • 1970-01-01
            • 2012-05-03
            • 1970-01-01
            • 2022-01-11
            • 1970-01-01
            • 1970-01-01
            • 2018-06-05
            相关资源
            最近更新 更多