【问题标题】:Why is my airbrush function not working in pygame?为什么我的喷枪功能在 pygame 中不起作用?
【发布时间】:2019-02-19 02:22:01
【问题描述】:

我正在创建一个基本的绘画应用程序,我想创建一个喷枪功能,并为不同风格的画笔提供其他功能。基本上,用户单击一个图标来选择画笔的样式,然后当他们单击并按住画布时,相应的画笔将在画布上绘制。这是我的代码: 主循环:

def paintScreen():
    intro = True
    gameDisplay.fill(cyan)
    message_to_screen('Welcome to PyPaint', black, -300, 'large')
    cur = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    while intro:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        pygame.draw.rect(gameDisplay, white, (50, 120, displayWidth - 100, displayHeight - 240))

        button('X', 20, 20, 50, 50, red, lightRed, action = 'quit')
        icon(airbrushIcon, white, 50, displayHeight - 101, 51, 51, white, grey, 'airbrush')
        icon(pencilIcon, white, 140, displayHeight - 101, 51, 51, white, grey, 'pencil')
        icon(calligraphyIcon, white, 230, displayHeight - 101, 51, 51, white, grey, 'calligraphy')
        pygame.display.update()
        if cur[0] > 50 < displayWidth - 50 and cur [1] > 120 < displayHeight - 120:
            if airbrush == True:
                airbrush()

图标功能:

def icon(icon, colour, x, y, width, height, inactiveColour, activeColour, action = None):
    cur = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    if x + width > cur[0] > x and y + height > cur[1] > y:#if the cursor is over the button
        pygame.draw.rect(gameDisplay, activeColour, (x, y, width, height))
        gameDisplay.blit(icon, (x, y))
        if click[0] == 1 and action != None:
            if action == 'quit':
                pygame.quit()
                quit()
            elif action == 'pencil':
                pencil = True
                return pencil
            elif action == 'airbrush':
                airbrush = True
                return airbrush
            elif action == 'calligraphy':
                calligraphy = True
                return calligraphy
            elif action == 'erase':
                eraser = True
                return eraser
    else:
        pygame.draw.rect(gameDisplay, inactiveColour, (x, y, width, height))
        gameDisplay.blit(icon, (x, y))

喷枪功能:

def airbrush(brushSize = 3):
    airbrush = True
    cur = pygame.mouse.get_pos() #cur[0] is x location, cur[1] is y location
    click = pygame.mouse.get_pressed()
    while airbrush == True:
        if click[0] == True:
            if cur[0] > 50 < displayWidth - 50 and cur[1] > 120 < displayHeight - 120: #if the cursor is above the canvas
                #the area of the canvas is x(50, width-50) y(120, width-120)
                pygame.draw.circle(gameDisplay, black, (cur[0] + random.randrange(brushSize), cur[1] + random.randrange(brushSize)), random.randrange(1, 5))
                pygame.display.update()
            clock.tick(60)

如何使喷枪功能正常工作?运行应用程序时,它不会返回任何错误,只是根本不起作用

【问题讨论】:

  • 您确定airbrush() 函数被正确调用了吗?也许一些print() 语句将有助于识别逻辑流程中的任何错误。
  • 我确定它正在被调用。我什至将绘制画布的语句移到了 while 循环之外,这样它就不会从喷枪函数中绘制墨水。

标签: python python-3.x pygame


【解决方案1】:

您的代码有几个缺陷:

  • 您将业务逻辑和绘图混为一谈,这会使您的代码变得混乱。我们稍后会修复它
  • 在您的icon 函数中,您正在设置局部变量(如airbrushpencil 等),这不会影响同名的全局变量。此外,您在设置它们后返回它们的值,并且永远不要使用该值。
  • 如果您的 airbrush 函数将被调用,您将永远无法离开该函数,因为无法将 airbrush 设置为 false
  • 该函数也不起作用,因为您没有在其循环中处理事件,这将导致您的窗口在事件队列填满后不再响应

你应该使用多态性,像这样:

import pygame
import pygame.freetype
import random

def bresenham_line(start, end):
    x1, y1 = start
    x2, y2 = end
    dx = x2 - x1
    dy = y2 - y1
    is_steep = abs(dy) > abs(dx)
    if is_steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2
    swapped = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        swapped = True
    dx = x2 - x1
    dy = y2 - y1
    error = int(dx / 2.0)
    ystep = 1 if y1 < y2 else -1
    y = y1
    points = []
    for x in range(x1, x2 + 1):
        coord = (y, x) if is_steep else (x, y)
        points.append(coord)
        error -= abs(dy)
        if error < 0:
            y += ystep
            error += dx
    if swapped:
        points.reverse()
    return points

class Brush(pygame.sprite.Sprite):
    def __init__(self, pos, font, canvas, tmpcanvas, icon, brushes, offset):
        super().__init__(brushes)
        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color('grey'))
        font.render_to(self.image, (8, 7), icon)
        self.other_image = self.image.copy()
        pygame.draw.rect(self.other_image, pygame.Color('red'), self.other_image.get_rect(), 3)
        self.rect = self.image.get_rect(topleft=pos)
        self.active = False
        self.canvas = canvas
        self.tmpcanvas = tmpcanvas
        self.brushes = brushes
        self.offset = offset
        self.mouse_pos = None

    def translate(self, pos):
        return pos[0] - self.offset[0], pos[1] - self.offset[1]

    def draw_to_canvas(self):
        pass

    def flip(self):
        self.active = not self.active
        self.image, self.other_image = self.other_image, self.image        

    def update(self, events):
        for e in events:
            if e.type == pygame.MOUSEBUTTONDOWN and self.rect.collidepoint(e.pos):
                for brush in self.brushes:
                    if brush.active:
                        brush.flip()
                self.flip()

        self.mouse_pos = self.translate(pygame.mouse.get_pos())
        if self.active:
            self.draw_to_canvas()

class Pencil(Brush):
    def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
        super().__init__(pos, font, canvas, tmpcanvas, 'P', brushes, offset)
        self.prev_pos = None

    def draw_to_canvas(self):
        pressed = pygame.mouse.get_pressed()
        if pressed[0] and self.prev_pos:
            pygame.draw.line(self.canvas, pygame.Color('red'), self.prev_pos, self.mouse_pos)
        self.prev_pos = self.mouse_pos

class Calligraphy(Brush):
    def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
        super().__init__(pos, font, canvas, tmpcanvas, 'C', brushes, offset)
        self.prev_pos = None

    def draw_to_canvas(self):
        pressed = pygame.mouse.get_pressed()
        if pressed[0] and self.prev_pos:
            for x, y in bresenham_line(self.prev_pos, self.mouse_pos):
                pygame.draw.rect(self.canvas, pygame.Color('orange'), (x, y, 5, 15))
        self.prev_pos = self.mouse_pos 

class Airbrush(Brush):
    def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
        super().__init__(pos, font, canvas, tmpcanvas, 'A', brushes, offset)

    def draw_to_canvas(self):
        pressed = pygame.mouse.get_pressed()
        if pressed[0]:
            pygame.draw.circle(self.canvas, pygame.Color('green'), 
            (self.mouse_pos[0] + random.randrange(-13, 13), self.mouse_pos[1] + random.randrange(-13, 13)), 
            random.randrange(1, 5))

class LineTool(Brush):
    def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
        super().__init__(pos, font, canvas, tmpcanvas, 'L', brushes, offset)
        self.start = None

    def draw_to_canvas(self):
        pressed = pygame.mouse.get_pressed()
        if pressed[0]:
            if not self.start:
                self.start = self.mouse_pos
            pygame.draw.line(self.tmpcanvas, pygame.Color('yellow'), self.start, self.mouse_pos)
        else:
            if self.start:
                pygame.draw.line(self.canvas, pygame.Color('yellow'), self.start, self.mouse_pos)
                self.start = None

class Clear(Brush):
    def __init__(self, pos, font, canvas, tmpcanvas, brushes, offset):
        super().__init__(pos, font, canvas, tmpcanvas, '*', brushes, offset)

    def draw_to_canvas(self):
        pressed = pygame.mouse.get_pressed()
        if pressed[0]:
            self.canvas.fill((1, 1, 1))
            self.flip()

def main():
    pygame.init()
    screen = pygame.display.set_mode((500, 500))
    sprites = pygame.sprite.Group()
    clock = pygame.time.Clock()

    font = pygame.freetype.SysFont(None, 26)
    offset = 0, 50
    canvas = pygame.Surface((500, 450))
    canvas.set_colorkey((1,1,1))
    canvas.fill((1,1,1))
    tmpcanvas = canvas.copy()

    x=10
    for tool in (Pencil, Calligraphy, Airbrush, LineTool, Clear):
        tool((x, 10), font, canvas, tmpcanvas, sprites, offset)
        x+= 40

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        tmpcanvas.fill((1, 1, 1))
        sprites.update(events)
        screen.fill((30, 30, 30))
        screen.blit(canvas, offset)
        screen.blit(tmpcanvas, offset)
        sprites.draw(screen)
        pygame.display.update()
        clock.tick(60)

if __name__ == '__main__':
    main()

在这个例子中,我们有一个Brush 基类来处理图标的绘制并跟踪画笔的active 状态,而子类处理实际的绘制。

这样,我们可以通过创建一个新类并实现draw_to_canvas 函数来轻松添加新的画笔/工具。

【讨论】:

  • 所以我的代码目前应该像这样运行:在while循环中,它绘制所有按钮等,然后检查光标是否在画布上,然后检查是否设置了喷枪为真。如果喷枪设置为 true,它会检查鼠标是否按下。如果鼠标按下,它应该绘画。这一切都在单个 while 循环中。我确实对其进行了更改,以便 airbrush() 函数不再创建 while 循环,而是绘制一些东西,并且可以在初始 while 循环中重复。这种方法到底有什么缺陷?
  • 正如我在回答中所说,主要缺陷是:您将局部变量和全局变量混合在一起,因此通过在 icon 函数中将 airbrush 设置为 True(局部变量)您没有更改您在paintScreen 函数中检查的airbrush 变量(顺便说一句,它指向一个函数),并且您创建了第二个循环,您不能将其留在airbrush功能。
  • 但我确实从喷枪函数中删除了 while 循环,所以现在该函数只是绘制和更新显示。我还更改了我的代码,以便喷枪变量是全局的。与函数同名的变量会导致任何问题吗?
  • @NormyHaddad 是的;如果你创建一个函数foo 然后设置foo = True 你的函数就消失了,因为foo 现在指向True
猜你喜欢
  • 1970-01-01
  • 2014-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
相关资源
最近更新 更多