1

我不确定我是否正确使用类。我正在尝试使用 pygame 构建一个简单的菜单。这是我第一次涉足 gui 的东西。我不知道如何构建我的代码。

我发现我可以创建一个通用的 Button 类来处理所有鼠标悬停/鼠标单击的内容,然后我可以为每个按钮子类化它,并重写该do_action方法以给每个按钮一个特定的操作。

class Button(pygame.sprite.Sprite):
    global screen_flags
    def __init__(self, images, pos):
        pygame.sprite.Sprite.__init__(self)
        self.images = images
        self.image = images[0]
        self.rect  = self.image.get_rect()
        self.rect.move_ip(pos)


    def update(self, events, surface):
        # if not screen_flags['config']:
        for event in events:
            if event.type == MOUSEMOTION:
                if self.rect.collidepoint(event.pos):
                    self.image = self.images[1]
                else:
                    self.image = self.images[0]
            elif event.type == MOUSEBUTTONDOWN:
                if event.button == 1 and self.rect.collidepoint(event.pos):
                    self.image = self.images[-1]
                    screen_flags['config'] = 1
                    self.do_action()
                    self.set_flag()

            elif event.type == MOUSEBUTTONUP:
                self.image = self.images[0]


        screen.blit(self.image, self.rect)

    def do_action(self):
        pass
    def set_flag(self):
        pass


class CheckBox(Button):
    def __init__(self, images, pos):
        Button.__init__(self, images, pos)
        self.is_clicked = False

    def update(self, events, surface):
        for event in events:
            if event.type == MOUSEMOTION:
                if not self.is_clicked:
                    if self.rect.collidepoint(event.pos):
                        self.image = self.images[1]
                    else:
                        self.image = self.images[0]
            elif event.type == MOUSEBUTTONDOWN:
                if event.button == 1 and self.rect.collidepoint(event.pos):
                    if not self.is_clicked:
                        self.image = self.images[-1]
                        self.is_clicked = True
                    else:
                        self.image = self.images[0]
                        self.is_clicked = False
        screen.blit(self.image, self.rect)


class Cancel(Button):
    def do_action(self):
        screen_flags['config'] = 0

所以,正如你所看到的,他们还没有真正任何事情。我可以打开和关闭复选框,打开和关闭一个“配置”窗口,但这就是我所得到的。其余代码:

global count
global sTime



config_button_img = load_sliced_images(25, 25, c_buttons)
config_button = Button(config_button_img, (608,4))

input_bar = load_sliced_images(351,33, inpt_bar)
text_box = Textbox(input_bar, (144,155))

s_button = load_sliced_images(110,32, sbmt_bttn)
submit = Button(s_button, (241,301))

c_button = load_sliced_images(110,32, cncl_bttn)
cancel = Cancel(c_button, (385, 301))

c_boxes = load_sliced_images(20,19, chk_box)
check_box = CheckBox(c_boxes, (200,200))    

try:
    while True:
        # ****************************
        # Pygame section
        events = pygame.event.get()
        for event in events:
            if event.type == QUIT:
                screen_flags['alert'] = 1
                ding.play(0,1000)
            elif event.type == MOUSEBUTTONDOWN:
                text_box.set_focus(event.button, event.pos)





        screen.blit(background, (0,0))
        screen.blit(client_logo, (0,30))
        screen.blit(tag, (174,462))

        if screen_flags['config']:
            screen.blit(config_window_img, (0,0))

            submit.update(events, screen)
            cancel.update(events, screen)
            check_box.update(events, screen)
        if screen_flags['alert']:
            screen.blit(alert_dialog, (0,0))

        config_button.update(events, screen)
        pygame.display.update()   








except KeyboardInterrupt:
    try: 
        pygame.quit()
    except:
        pass

所以这有点像预期的那样。我不确定从这里去哪里。我是否继续在类中封装逻辑?例如,我尝试做的下一件事是,当单击“取消”按钮时,它会取消选中复选框。

我试图改变Cancel班级来做到这一点。

class Cancel(Button):
    def do_action(self):
        screen_flags['config'] = 0
        check_box.is_clicked=False

但是,这给了我一个 GlobalName 错误。如何从不同的类中定位实例方法?这甚至是正确的方法吗?或者只有一些逻辑,比如update()照顾鼠标的东西,然后通过将变量传入和传出不同的类来处理main()类的作用?我应该让所有的类都使用全局变量吗?

有没有关于gui实践的好文章。诸如如何构造代码等之类的事情?

希望以上是有道理的。

4

2 回答 2

2

就个人而言,我会这样做,以便我的每个类都被接受screen_flags为构造函数 ( __init__) 的参数。然后,您的每个类都会处理他们需要的“全局”数据。一个真正简单的方法是......

class Cancel(Button):
      def __init__(self,*args,**kwargs):
          self.screen_flags=kwargs.pop('screen_flags')
          Button.__init__(self,*args,**kwargs)  #Some might advise you to use super here...

      def do_action(self):
          self.screen_flags['config'] = 0

#now use the cancel class
cancel=Cancel(c_button, (385, 301),screen_flags=screen_flags)

当然,根据共享数据的样子(您有多少不同的变量等),您可能需要传递一个字典或其他对象来制作它,这样您就不需要传递 5000 条不同的共享数据大约。

处理此问题的另一种方法是将类中的“全局”数据定义为“类变量”,然后从该类继承。

class GlobalData(object):
   stuff=None

class FooButton(Button,GlobalData):
   def do_action(self):
       print self.stuff
       #If you do something in here, don't do:  self.stuff = blah
       #instead, you *should* do: GlobalData.stuff = blah
       #However, it is fine to change mutable objects in place.
       #         e.g. self.stuff["dict_key"]=blah

#Now we can assign to GlobalData and instances of 
#FooButton will see the differences immediately.
cancel=FooButton(c_button, (385, 301))
cancel.do_action()
GlobalData.stuff="Cows say Moo"
cancel.do_action()

我希望这有帮助。您的帖子内容很多,因此整理所有内容有点困难。

编辑

如果您不了解如何处理类变量,请参阅 中的注释do_action。基本上,您需要小心不要丢失对数据的处理...

于 2012-05-10T18:25:23.850 回答
0

GUI 的东西可以做得更好。

是的,请务必将您的控件包装在类中。

我建议试试这个。

首先,为您的控件定义逻辑接口。暂时忘记实施细节。任何控件都可以点击;定义方法onClick(pos)。复选框可以选中或不选中;定义setChecked(bool)。窗口可以显示或隐藏、定义setVisible(bool)等。

为可点击控件创建一个共同的祖先。在其事件处理程序中,调用onClick(event.pos). 默认实现什么都不做。onClick()现在,当您想模仿点击时,您可以调用控件。您将想要onMouseDownonMouseUp执行诸如单击动画onMouseInonMouseOut悬停事件等之类的事情。您关心事件调度的血腥细节的唯一地方将是您的共同祖先。

不要直接引用全局状态;它会导致各种不愉快的事情。相反,让每个控件都知道它的状态以及如何更改它(无论如何我们都在做 OOP)。因此,复选框获取isChecked()方法等。

现在取消按钮只需要覆盖它的onClick方法和构造函数。在构造函数中,传递CheckBox实例;在cancel_button.onClick只是打电话Button.onClick然后self.check_box.setChecked(False)

于 2012-05-10T19:03:52.993 回答