如何在不阻塞 MainThread 的情况下在 GUI (kivy) 中等待用户 decision/input 的代码?
How to wait in code for user decision/input in GUI (kivy) without blocking the MainThread?
我想在代码中暂停或等待,直到用户使change/decision(按下切换按钮) 在 Gui 中
我找到了库 asyncpivy,但我不知道如何同时在所有按钮上实现它,如果按下任何按钮,请继续。
我尝试通过 Clock 进行一些安排但没有工作或者我做错了
Python code
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 code
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')
您是否尝试过创建一个 asyncio.future、等待它并让您的按钮能够设置未来的结果?你需要小心确保未来只能设置一次。
创造未来的功能或class可能值得,将其设置在所有按钮上并等待它。
(未经测试,天上掉馅饼,代码如下)
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')
如果您使用 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())
我想在代码中暂停或等待,直到用户使change/decision(按下切换按钮) 在 Gui 中 我找到了库 asyncpivy,但我不知道如何同时在所有按钮上实现它,如果按下任何按钮,请继续。 我尝试通过 Clock 进行一些安排但没有工作或者我做错了
Python code
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 code
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')
您是否尝试过创建一个 asyncio.future、等待它并让您的按钮能够设置未来的结果?你需要小心确保未来只能设置一次。
创造未来的功能或class可能值得,将其设置在所有按钮上并等待它。
(未经测试,天上掉馅饼,代码如下)
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')
如果您使用 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())