Python / Pygame中如何通过迭代为形状设置不同的颜色?

3

我是新来的stackoverflow用户,希望从更高级的程序员那里获得一些见解。我下个学期将转专业学习计算机科学,并正在学习一些初级Python编程课程。我已经完成了以下程序(作业要求通过填写教授提供的代码,在窗口表面绘制椭圆形物体,实现随机颜色,不算太难),但我想添加一些额外的东西:正如您所看到的,我将椭圆的颜色设置为随机的,但它会保持相同的颜色,直到完全重新启动程序,也就是说,所有椭圆的颜色在程序运行期间都是相同的。以当前的代码编写方式,我无法找到改变每个椭圆的颜色的方法。请记住,这只是为了好玩,但如果有人感觉特别有帮助或有创意,我很想听听你们的建议。如果需要更多信息,请告诉我。谢谢!


import pygame, random, sys

WINDOWWIDTH = 700
WINDOWHEIGHT = 700
BACKGROUNDCOLOR = (150,160,100)
#A different color every run
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
                    random.randint (0,255))

pygame.init()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption("Mobile Ovals")

#The draw variable is used later to indicate the mouse is still pressed
ovals = []
completedOvals = []
finished = False
draw = False
startXY = (-1, -1)

while not finished:
    for event in pygame.event.get():
        if event.type == pygame.QUIT or (event.type == pygame.KEYUP and
                    event.key == pygame.K_ESCAPE):
            finished = True

        elif event.type == pygame.KEYDOWN:
            pressed = pygame.key.get_pressed()
            if pressed[pygame.K_F4] and (pressed[pygame.K_LALT] or
                        pressed[pygame.K_RALT]):
                finished  = True

        elif event.type == pygame.MOUSEBUTTONDOWN:
            startXY = event.pos
            draw = True

        elif event.type == pygame.MOUSEBUTTONUP:
            draw = False
            for oval in ovals:
                completedOvals.append (oval)

        if draw == True:
            del ovals [:]

            #The above function ensures only one oval is onscreen at any given time
            endXY = event.pos
            width = (abs(endXY[0]-startXY[0]))
            height = (abs(endXY[1]-startXY[1]))

            #The code below allows the user to drag any direction
            if endXY[0] < startXY[0]:
                left = endXY[0]
            else:
                left = startXY[0]
            if endXY[1] < startXY[1]:
                top = endXY[1]
            else:
                top = startXY[1]

            ovals.append (pygame.Rect (left, top, width, height))

            windowSurface.fill(BACKGROUNDCOLOR)

            for oval in ovals:
                pygame.draw.ellipse(windowSurface, OVAL_COLOR, oval)

            for completedOval in completedOvals:
                pygame.draw.ellipse(windowSurface, OVAL_COLOR, completedOval)

            pygame.display.update()

pygame.quit()
2个回答

2
你的问题很简单。你只设置了一次“OVAL_COLOR”。但是每次引用变量“OVAL_COLOR”时,你并没有创建新的随机颜色,而是重复使用了创建变量时生成的RGB颜色。
现在,按照你的程序结构,你要维护一个完整椭圆的列表,每次将“draw”变量设置为true时都会重新绘制。如果你把“OVAL_COLOR”变量放在for循环中,你将会在每次鼠标移动时更新颜色,改变正在绘制的椭圆的颜色以及所有旧椭圆被重新绘制的颜色。
要解决这个问题,需要在鼠标按钮按下时设置变量“OVAL_COLOR”。这样,在调整椭圆时,椭圆的颜色就不会改变。但是,考虑到程序的当前结构,你需要保存分配给完成椭圆的颜色,否则每次仍然会更改椭圆的颜色。
当鼠标按下时,我们希望为我们的圆形生成新的随机颜色。生成一个随机值,每次重新绘制圆形时都会使用它。
    elif event.type == pygame.MOUSEBUTTONDOWN:
        startXY = event.pos
        OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
                            random.randint (0,255))
        draw = True

释放鼠标按钮时,保存椭圆的坐标,以及它所绘制的颜色
    elif event.type == pygame.MOUSEBUTTONUP:
        draw = False
        # print len(ovals) # (always ==1)
        completedOvals.append ((ovals[-1], OVAL_COLOR)) 

当我们遍历这些完成的椭圆时,每次都用相同的颜色绘制它们。
        for (completedOval, color) in completedOvals:
            pygame.draw.ellipse(windowSurface, color, completedOval)

1
没错。问题是我不知道该如何解决这个问题。我尝试在循环中包含OVAL_COLOR常量,但这样会不断改变椭圆的颜色,足以引起癫痫发作。我无法确定在哪里放置颜色代码,以便它只在绘制每个椭圆时更改其颜色,而不是“对于所有已完成的椭圆”。 - user1333890
啊,你比我快。我试过了,但是它只是不断地改变颜色。嗯... - user1333890
这是你的问题,@JeffArcher:你正在使用while循环,它会无限期地重复,直到变量“finished”为真。因此,每次绘制时都会重新生成随机椭圆形颜色。这不是你的目标,我猜?编辑:实际上可能不是这样-你的缩进遮盖了循环的实际功能。 - David Cain
好的。所以如果我想改变颜色,就必须完全重新编写代码,使其不再运行在while循环上,而是运行在其他方法上。我只是想确保没有办法使用现有的代码来完成它。看起来我面临着一个很好的挑战 :-)。 - user1333890
我更新了我的回复-它应该做你想要的事情。给你一个未来的提示-下次发布时,请确保缩进正确,这样其他人就不需要纠正它了。如你所知,缩进在Python中非常重要;我不得不重新缩进你的代码才能测试和运行它。 - David Cain
显示剩余3条评论

1
创建一个简单的Oval()类,其中包含它的颜色和大小。
import pygame
from pygame.locals import * 

class Oval(object):
    """handle, and draw basic ovals. stores Rect() and Color()"""
    def __init__(self, startXY, endXY):
        self.color = Color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
        self.rect = Rect(0,0,1,1)
        self.coord_to_oval(startXY, endXY)

    def draw(self):
        pygame.draw.ellipse(windowSurface, self.color, self.rect)

    def coord_to_oval(self, startXY, endXY):
        width = (abs(endXY[0]-startXY[0]))
        height = (abs(endXY[1]-startXY[1]))

        #The code below allows the user to drag any direction
        if endXY[0] < startXY[0]:
            left = endXY[0]
        else:
            left = startXY[0]
        if endXY[1] < startXY[1]:
            top = endXY[1]
        else:
            top = startXY[1]

        self.rect = Rect(left, top, width, height)

# main loop
while not finished:
    for event in pygame.event.get():
       # events, and creation:
       # ... your other events here ...

       elif event.type == MOUSEBUTTONDOWN:
            startXY = event.pos
            draw = True

        elif event.type ==MOUSEBUTTONUP:
            # on mouseup, create instance.
            endXY = event.pos
            oval_new = Oval(startXY, endXY)
            completedOvals.append(oval_new)

        # draw them:
        for oval in ovals:
                oval.draw()
        for oval in completedOvals:
                oval.draw()

我主要省略了你未完成的椭圆。这是为了在点击之前展示尺寸吗?


你的代码有许多语法错误(例如,Oval类没有扩展自身类)。此外,这种方法只在释放鼠标按钮时绘制椭圆 - 最初的目的是能够在屏幕上缩放椭圆,然后再将其添加到画布中。 - David Cain
谢谢你提供的额外选项,Monkey!这也是一个有趣的想法,但我更喜欢David代码的简洁性。如果我必须重写代码,我可能会使用你的。 - user1333890
@David:编辑以修复Oval(object)。您可以通过仅具有一个椭圆实例来添加预览:在MOUSEDOWN上设置“topleft”,在MOUSEMOTION事件上设置宽度,高度,在MOUSEUP上删除/设置为无。 - ninMonkey
@JeffA.,我想指出我的方法是一个简单的解决方案,适用于你提供的代码(并且不依赖于任何你尚未涵盖的编程概念)。Monkey的方法是更好的设计 - 如果您想绘制许多不同类型的形状,或者以不同方式绘制对象会发生什么?我的代码很快就会变得难以阅读,而像Monkey这样的面向对象的方法则会更加清晰易读。 - David Cain

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