如何在不阻塞MainThread的情况下在代码中等待用户在GUI(kivy)中的决策/输入



我想在代码中暂停或等待,直到用户在Gui中做出更改/决定(按下ToggleButton(我发现了asynckivy库,但我不知道如何在所有按钮上同时实现它,如果按下任何按钮,请继续。我试着通过Clock进行一些调度,但没有工作,或者我做错了

Python代码

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import *
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.behaviors import ToggleButtonBehavior

class FightScreen(Screen):
player_action_pick = StringProperty(rebind=True, allownone=True)
def __init__(self, **kwargs):
super(FightScreen, self).__init__(**kwargs)
self.af_init = Clock.create_trigger(self._init)
self.player_action_pick = None
self.af_init()
self.starting_hit = "Enemy"
def _init(self, dt):
self.app = App.get_running_app()
def fight(self, *args):
hero_alive = True
enemy_alive = True
def enemy_move():
# something
return hero_alive
def player_move():
# NEED SOLUTION HERE
# something like wait until any of ToggleButtonBehavior.get_widgets('player_action') is down
# than reset button state like button.state = 'normal'
# something
return enemy_alive
# while player.health > 0 and enemy.health > 0:
if self.starting_hit == "Enemy":
print("Enemy move")
if not enemy_move():
print("Player dead")
elif not player_move():
print("Enemy dead")
else:
print("Player move")
def on_enter(self, *args):
self.fight()
def player_action(self, stat):
self.player_action_pick = stat

class ScreenManagement(ScreenManager):
pass

class Design(App):
def __init__(self, **kwargs):
super(Design, self).__init__(**kwargs)
# Construct app
def build(self):
# design constructor
kv = Builder.load_file('AppDesign.kv')
return kv

if __name__ == "__main__":
Design().run()

kivy代码

ScreenManagement:
id: screen_manager
FightScreen:
name: 'FightScreen'
manager: screen_manager
id: fight_screen
<PlayerActionButton@ToggleButton>
size_hint: .3, .1
background_color: '#654321'
text: "Attack!"
<FightScreen>
FloatLayout:
GridLayout:
cols: 1
rows: 4
PlayerActionButton:
group: 'player_action'
on_state:
if self.state == 'down': 
root.player_action('programming_stat')
PlayerActionButton:
group: 'player_action'
on_state:
if self.state == 'down': 
root.player_action('design_stat')
PlayerActionButton:
group: 'player_action'
on_state:
if self.state == 'down': 
root.player_action('creativity_stat')
PlayerActionButton:
group: 'player_action'
on_state:
if self.state == 'down': 
root.player_action('heal')

如果使用asynckivy,则代码为:

import asynckivy as ak
class FightScreen(Screen):
async def fight(self, *args):
hero_alive = True
enemy_alive = True
def enemy_move():
# something
return hero_alive
async def player_move():
buttons = ToggleButtonBehavior.get_widgets('player_action')
await ak.or_from_iterable(
ak.event(button, 'state') for button in buttons)
for button in buttons:
button.state = 'normal'
return enemy_alive
# while player.health > 0 and enemy.health > 0:
if self.starting_hit == "Enemy":
print("Enemy move")
if not enemy_move():
print("Player dead")
elif not await player_move():
print("Enemy dead")
else:
print('AAAA')
else:
print("Player move")
def on_enter(self, *args):
ak.start_soon(self.fight())

您是否尝试创建一个asyncio.future,等待它,并让您的按钮能够设置未来的结果?你需要小心,确保未来只能设定一次。

可能值得拥有一个函数或类来创建未来,在所有按钮上设置它并等待它

(未经测试,天上掉馅饼,代码如下(

def which_button(group):
future = loop.create_future()
buttons = ToggleButtonBehavior.get_widgets(group):
def set_result(button):
for button in buttons:
button.unbind(set_result)
future.set_result(button) # or whatever you want from the button
for button in buttons:
button.bind(on_release=set_result)
return future

那么你可能会用类似的东西

def player_move():
button = await which_button('player_action')

最新更新