我不确定我是否正确使用了课程。我正在尝试使用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做法是否有任何好文章。像如何构建代码等事情。?
希望以上是有道理的。
答案 0 :(得分: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
中的注释。基本上,您需要注意不要丢失数据处理......
答案 1 :(得分:0)
GUI的东西可以做得更好。
是的,请将您的控件包装在类中。
我建议试试这个。
首先,为您的控件定义逻辑接口。忘记一分钟的实现细节。可以点击任何控件;方法onClick(pos)
。复选框可以选中或取消选中;定义setChecked(bool)
。可以显示或隐藏Windows,定义setVisible(bool)
等等。
为可点击控件创建共同祖先。在其事件处理程序中,调用onClick(event.pos)
。默认实现不会执行任何操作。现在,当您想要模仿点击时,可以调用控件的onClick()
。您需要onMouseDown
和onMouseUp
执行诸如点击动画,onMouseIn
和onMouseOut
等悬停事件等操作。您唯一关心血腥细节的地方事件派遣将是你的共同祖先。
不要直接引用全球状态;它会导致各种不愉快的事情。相反,让每个控件都知道它的状态以及如何改变它(我们无论如何都要做OOP)。因此,复选框将获得isChecked()
方法等
现在,取消按钮只需覆盖其onClick
方法和构造函数。在构造函数中,传递CheckBox
实例;在cancel_button.onClick
中,只需致电Button.onClick
,然后致电self.check_box.setChecked(False)
。