我已经尝试了很多,希望得到任何帮助。
我使用 pytransitions 构建了一个状态机仪表板,并使用 pywebio 构建了一个仪表板。现在我尝试有两个按钮:
def btn_click(btn_val):
global Lang
if btn_val=='English':
Lang=1
Dashboard.to_Start()
if btn_val=='German':
Lang=2
Dashboard.to_Start()
while True:
match Dashboard.state:
case 'Start':
clear()
put_buttons(['German', 'English'], onclick=btn_click)
match Lang:
case 1:
put_text("some english text")
case 2:
put_text("some german text")
Dashboard.ok()
case 'State2':
#do this and that
Dashboard.ok()
case 'State3':
#do this and that
match Lang:
case 1:
name = input("What's your name")
case 2:
name = input("Wie heisst du?")
它调用一个函数,可以在其中设置仪表板不同语言的变量。我还希望仪表板在语言更改时完全刷新,也就是我希望状态机将其状态更改为初始状态“开始”。当仪表板像状态 3 一样等待人工文本输入时,也应该发生这种情况。即使在状态 2 和状态 3 下,按钮始终保持在屏幕上。
不幸的是,当我尝试在函数中执行此操作(方法 to_Start())时,它不起作用。有趣的是,当我在函数中打印状态机状态时,它会在“开始”状态下打印它,但在我们离开该函数后就不再打印了。我理解这与函数中仅处理本地内容有关(定义全局变量时除外)。
我也尝试过 onclick=Dashboard.to_Start(),但它不起作用,我会遇到下一个问题,那就是我的变量 Lang 需要更改。
我想如果我可以从 onclick 调用函数 btn_click AND Dashboard.to_Start() 那就最好了。或者也许我的整个方法很糟糕,我很乐意听到一些建议。
有人可以解释一下我在这里缺少什么吗?
编辑:我根本没有收到错误消息,或者 pywebio 在仪表板上告诉我“发生了内部错误”。在评论中,我的代码示例受到批评,我尽力改进。
我对此很生气,请帮忙。干杯和问候
欢迎来到 Stack Overflow,Toben。
由于我无法直接测试您的代码,所以我自己创建了一个 MRE。但首先,一些评论:
状态机的一个论点是摆脱复杂的 switch/case 语句。如果您使用(轮询)switch/case 语句跟踪机器状态,则机器或 switch/case 可能不是正确的选择。
许多网络和 GUI 框架异步处理用户或网络输入(例如在线程中),并使用/阻止主线程来更改 GUI。此外,大多数状态机实现(包括转换)都不是线程安全的。转换功能
Machine
用于线程,LockedMachine
用于异步/等待用例。对于我的最小示例,我将忽略 B),因为线程或异步事件处理是否是更合适的方法取决于所选的框架。但现在让我们面对 A):我们不使用轮询机器的状态,而是使用事件驱动的方法,创建一个控制器来根据用户输入处理 GUI 更改,并将其作为状态模型传递给状态机。机器模型和有状态模型的分离是处理转换的推荐方法。机器可以被视为可用于多个有状态对象的规则手册。
AsyncMachine
当用户单击按钮时,
from pywebio.output import put_text, put_buttons, clear
from pywebio import start_server
from transitions import Machine
# Our GUI controller/stateful model contains all methods that will
# alter the GUI and process input events. Each method may receive
# input information.
class WebIOModel:
def on_enter_Start(self, inp=None):
# We bind the button's onclick callback to a yet
# to be defined "clicked" method.
put_buttons(["German", "English"], onclick=self.clicked)
put_text("Choose a language")
def on_enter_Clicked(self, inp=None):
put_text(f"You chose {inp}")
def clear_canvas(self, inp=None):
clear()
# Our simple machine has three states
# Pay attention to the state names and the method names of our
# WebIOModel: transitions will automatically bind methods to
# state events that match the pattern "on_enter_<state>"
# and "on_exit_<state>".
states = ["Init", "Start", "Clicked"]
# We create a transition event named "clicked".
# When the state machine is
# in state "Start" and "clicked" happens, the machine will transition
# to state "Clicked" and also execute callbacks associated with
# exiting "Start" and entering "Clicked".
transitions = [["clicked", "Start", "Clicked"]]
model = WebIOModel()
# We tell our machine that it should always execute the model's
# callback "clear_canvas" before a state transition.
# We use this callback to clear the GUI and reduce redundant code.
# Note that for "enter_<state>" callbacks to be called, a state
# must be actually entered. However, an "initial" state is not
# entered. We create a dummy "Init" state to deal with this.
machine = Machine(
model,
states=states,
transitions=transitions,
before_state_change="clear_canvas",
initial="Init",
)
def main():
# Note that events and trigger functions are bound to the model
# not the machine! 'to_Start' will enter this state and draw the
# buttons
model.to_Start()
start_server(main, 80)
回调将转发到我们模型的
onclick
事件。由于 clicked
返回一个值,我们的回调链需要能够处理输入。这样 onclick
中的每个回调都有一个可选的 WebIOModel
参数。传递给 input
的值将被转发到处理链中的所有回调。可以在转换/事件处理链的任何状态下调用回调。完整的列表及其执行顺序可以在文档中找到。 这不是生产代码,因为它忽略了前面提到的 B)。是否在主线程中对用户输入事件和处理状态更改进行排队,或者是否使用线程安全
clicked
或通过异步/等待对 GUI 事件进行排队,取决于您的架构和
LockedMachine
。