动态更改 urwid.MainLoop 小部件的首选方法
preferred method to dynamically change the urwid.MainLoop widget
我正在查看一些植根于 urwid
:
的代码
import urwid
from functools import partial
from random import randint
class State(object):
def __init__(self, main_widget):
self.main_widget = main_widget
def handle_keystroke(app_state, key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
else:
loop.widget = urwid.Filler(urwid.Button('new rand int:' + str(randint(0, 100))))
app_state = State(urwid.Filler(urwid.Button('original widget')))
callback = partial(handle_keystroke, app_state)
loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback)
loop.run()
并注意到 loop
在定义之前在函数 unhandled_input
中被引用。此外,它不作为参数传递,只是按名称硬编码到函数中。 1)为什么这是可能的,并且:2)是否有更清晰的选择?否则很难做到,因为存在 loop
、app_state
和 callback
.
的循环依赖
当python编译函数时,作为赋值目标的left-hand-side个变量被视为local
,其余为全局变量。 loop
没有赋值,所以当 python 运行 loop.widget = urwid.Filler(...)
时,它知道 loop
不是局部变量,它会在模块的命名空间中查找名称。
模块命名空间是动态的,因此只要 loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback)
在查找之前运行,就会创建 loop
并且它可以工作。由于在 loop.run()
之前无法执行回调,因此将定义 loop
。
这是单例和全局状态的经典风险之一。确保资源在使用前创建并不总是那么容易。
我不确定您的示例代码中有多少代表了原始代码,但看起来您可能想通过一个示例小部件来熟悉 the technique of using urwid's custom widgets wrapping text widgets, as shown in the answer,该小部件在时间.
下面是一个编写类似于您提供的示例代码的示例,其设计更适合 urwid 并且 Python 更好一点:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function, absolute_import, division
import urwid
from random import randint
class RandomNumberWidget(urwid.WidgetWrap):
def __init__(self):
self.random_number = None
self.text_widget = urwid.Text(u'')
super(RandomNumberWidget, self).__init__(self.text_widget)
def roll(self):
self.random_number = randint(0, 100)
self.update()
def update(self):
"""Update UI
"""
if self.random_number is None:
self.text_widget.set_text('No number set')
else:
self.text_widget.set_text('Random number: %s' % self.random_number)
class App(object):
def __init__(self):
self.random_number_widget = RandomNumberWidget()
top_message = 'Press any key to get a random number, or q to quit\n\n\n'
widget = urwid.Pile([
urwid.Padding(urwid.Text(top_message),
'center', width=('relative', len(top_message))),
self.random_number_widget,
])
self.widget = urwid.Filler(widget, 'top')
def play(self):
self.random_number_widget.roll()
def play_or_exit(self, key):
if key in ('q', 'Q', 'esc'):
raise urwid.ExitMainLoop()
app.play()
if __name__ == '__main__':
app = App()
loop = urwid.MainLoop(app.widget, unhandled_input=app.play_or_exit)
loop.run()
还取决于您实际想要做什么,让自定义小部件响应键盘事件可能是有意义的,而不是在全局处理程序中完成所有操作(这对于简单的程序来说完全没问题,IMO) .
我正在查看一些植根于 urwid
:
import urwid
from functools import partial
from random import randint
class State(object):
def __init__(self, main_widget):
self.main_widget = main_widget
def handle_keystroke(app_state, key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
else:
loop.widget = urwid.Filler(urwid.Button('new rand int:' + str(randint(0, 100))))
app_state = State(urwid.Filler(urwid.Button('original widget')))
callback = partial(handle_keystroke, app_state)
loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback)
loop.run()
并注意到 loop
在定义之前在函数 unhandled_input
中被引用。此外,它不作为参数传递,只是按名称硬编码到函数中。 1)为什么这是可能的,并且:2)是否有更清晰的选择?否则很难做到,因为存在 loop
、app_state
和 callback
.
当python编译函数时,作为赋值目标的left-hand-side个变量被视为local
,其余为全局变量。 loop
没有赋值,所以当 python 运行 loop.widget = urwid.Filler(...)
时,它知道 loop
不是局部变量,它会在模块的命名空间中查找名称。
模块命名空间是动态的,因此只要 loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback)
在查找之前运行,就会创建 loop
并且它可以工作。由于在 loop.run()
之前无法执行回调,因此将定义 loop
。
这是单例和全局状态的经典风险之一。确保资源在使用前创建并不总是那么容易。
我不确定您的示例代码中有多少代表了原始代码,但看起来您可能想通过一个示例小部件来熟悉 the technique of using urwid's custom widgets wrapping text widgets, as shown in the answer,该小部件在时间.
下面是一个编写类似于您提供的示例代码的示例,其设计更适合 urwid 并且 Python 更好一点:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function, absolute_import, division
import urwid
from random import randint
class RandomNumberWidget(urwid.WidgetWrap):
def __init__(self):
self.random_number = None
self.text_widget = urwid.Text(u'')
super(RandomNumberWidget, self).__init__(self.text_widget)
def roll(self):
self.random_number = randint(0, 100)
self.update()
def update(self):
"""Update UI
"""
if self.random_number is None:
self.text_widget.set_text('No number set')
else:
self.text_widget.set_text('Random number: %s' % self.random_number)
class App(object):
def __init__(self):
self.random_number_widget = RandomNumberWidget()
top_message = 'Press any key to get a random number, or q to quit\n\n\n'
widget = urwid.Pile([
urwid.Padding(urwid.Text(top_message),
'center', width=('relative', len(top_message))),
self.random_number_widget,
])
self.widget = urwid.Filler(widget, 'top')
def play(self):
self.random_number_widget.roll()
def play_or_exit(self, key):
if key in ('q', 'Q', 'esc'):
raise urwid.ExitMainLoop()
app.play()
if __name__ == '__main__':
app = App()
loop = urwid.MainLoop(app.widget, unhandled_input=app.play_or_exit)
loop.run()
还取决于您实际想要做什么,让自定义小部件响应键盘事件可能是有意义的,而不是在全局处理程序中完成所有操作(这对于简单的程序来说完全没问题,IMO) .