5.14. NormalTUISpoke を使用したテキストインターフェイススポークの定義
Defining a Simple TUI Spoke の例では、メソッドが利用可能かつ提供されるデータの出力と処理に対処する TUI スポークを実装する方法が示されました。ただし、pyanaconda.ui.tui.spokes パッケージの NormalTUISpoke クラスを使用してこれを実現する別の方法があります。このクラスを継承することで、設定する必要のあるフィールドと属性を指定するだけで、一般的な TUI スポークを実装できます。以下の例で説明します。
前提条件
-
Anaconda アドオン構造 で説明されているように、
TUIディレクトリーの下に新しいサブパッケージのセットを追加しました。
手順
- 以下の例に従って、アドオン Text User Interface (TUI) のサポートを追加するために必要なすべての定義を含むモジュールを作成します。
例5.6 NormalTUISpoke を使用したテキストインターフェイススポークの定義
class HelloWorldEditSpoke(NormalTUISpoke):
"""Example class demonstrating usage of editing in TUI"""
category = HelloWorldCategory
def init(self, data, storage, payload):
"""
:see: simpleline.render.screen.UIScreen
:param data: data object passed to every spoke to load/store data
from/to it
:type data: pykickstart.base.BaseHandler
:param storage: object storing storage-related information
(disks, partitioning, boot loader, etc.)
:type storage: blivet.Blivet
:param payload: object storing packaging-related information
:type payload: pyanaconda.packaging.Payload
"""
super().init(self, *args, **Kwargs)
self.title = N_("Hello World Edit")
self._container = None
# values for user to set
self._checked = False
self._unconditional_input = ""
self._conditional_input = ""
def refresh(self, args=None):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of self.data.
:see: pyanaconda.ui.common.UIObject.refresh
:see: simpleline.render.screen.UIScreen.refresh
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
"""
super().refresh(args)
self._container = ListColumnContainer(columns=1)
# add ListColumnContainer to window (main window container)
# this will automatically add numbering and will call callbacks when required
self.window.add(self._container)
self._container.add(CheckboxWidget(title="Simple checkbox", completed=self._checked),
callback=self._checkbox_called)
self._container.add(EntryWidget(title="Unconditional text input",
value=self._unconditional_input),
callback=self._get_unconditional_input)
# show conditional input only if the checkbox is checked
if self._checked:
self._container.add(EntryWidget(title="Conditional password input",
value="Password set" if self._conditional_input
else ""),
callback=self._get_conditional_input)
self.window.add_with_separator(self._container)
def _checkbox_called(self, data): # pylint: disable=unused-argument
"""Callback when user wants to switch checkbox.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
self._checked = not self._checked
def _get_unconditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set unconditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = Dialog(
"Unconditional input",
conditions=[self._check_user_input]
)
self._unconditional_input = dialog.run()
def _get_conditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set conditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = PasswordDialog(
"Unconditional password input",
policy_name=PASSWORD_POLICY_ROOT
)
self._conditional_input = dialog.run()
def _check_user_input(self, user_input, report_func):
"""Check if the user has written a valid value.
:param user_input: user input for validation
:type user_input: str
:param report_func: function for reporting errors on user input
:type report_func: func with one param
"""
if re.match(r'^\w+$', user_input):
return True
else:
report_func("You must set at least one word")
return False
def input(self, args, key):
"""
The input method that is called by the main loop on user's input.
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
:param key: user's input
:type key: unicode
:return: if the input should not be handled here, return it, otherwise
return InputState.PROCESSED or InputState.DISCARDED if the input was
processed successfully or not respectively
:rtype: enum InputState
"""
if self._container.process_user_input(key):
return InputState.PROCESSED_AND_REDRAW
else:
return super().input(args, key)
@property
def completed(self):
# completed if user entered something non-empty to the Conditioned input
return bool(self._conditional_input)
@property
def status(self):
return "Hidden input %s" % ("entered" if self._conditional_input
else "not entered")
def apply(self):
# nothing needed here, values are set in the self.args tree
pass
class HelloWorldEditSpoke(NormalTUISpoke):
"""Example class demonstrating usage of editing in TUI"""
category = HelloWorldCategory
def init(self, data, storage, payload):
"""
:see: simpleline.render.screen.UIScreen
:param data: data object passed to every spoke to load/store data
from/to it
:type data: pykickstart.base.BaseHandler
:param storage: object storing storage-related information
(disks, partitioning, boot loader, etc.)
:type storage: blivet.Blivet
:param payload: object storing packaging-related information
:type payload: pyanaconda.packaging.Payload
"""
super().init(self, *args, **Kwargs)
self.title = N_("Hello World Edit")
self._container = None
# values for user to set
self._checked = False
self._unconditional_input = ""
self._conditional_input = ""
def refresh(self, args=None):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of self.data.
:see: pyanaconda.ui.common.UIObject.refresh
:see: simpleline.render.screen.UIScreen.refresh
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
"""
super().refresh(args)
self._container = ListColumnContainer(columns=1)
# add ListColumnContainer to window (main window container)
# this will automatically add numbering and will call callbacks when required
self.window.add(self._container)
self._container.add(CheckboxWidget(title="Simple checkbox", completed=self._checked),
callback=self._checkbox_called)
self._container.add(EntryWidget(title="Unconditional text input",
value=self._unconditional_input),
callback=self._get_unconditional_input)
# show conditional input only if the checkbox is checked
if self._checked:
self._container.add(EntryWidget(title="Conditional password input",
value="Password set" if self._conditional_input
else ""),
callback=self._get_conditional_input)
self.window.add_with_separator(self._container)
def _checkbox_called(self, data): # pylint: disable=unused-argument
"""Callback when user wants to switch checkbox.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
self._checked = not self._checked
def _get_unconditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set unconditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = Dialog(
"Unconditional input",
conditions=[self._check_user_input]
)
self._unconditional_input = dialog.run()
def _get_conditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set conditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = PasswordDialog(
"Unconditional password input",
policy_name=PASSWORD_POLICY_ROOT
)
self._conditional_input = dialog.run()
def _check_user_input(self, user_input, report_func):
"""Check if the user has written a valid value.
:param user_input: user input for validation
:type user_input: str
:param report_func: function for reporting errors on user input
:type report_func: func with one param
"""
if re.match(r'^\w+$', user_input):
return True
else:
report_func("You must set at least one word")
return False
def input(self, args, key):
"""
The input method that is called by the main loop on user's input.
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
:param key: user's input
:type key: unicode
:return: if the input should not be handled here, return it, otherwise
return InputState.PROCESSED or InputState.DISCARDED if the input was
processed successfully or not respectively
:rtype: enum InputState
"""
if self._container.process_user_input(key):
return InputState.PROCESSED_AND_REDRAW
else:
return super().input(args, key)
@property
def completed(self):
# completed if user entered something non-empty to the Conditioned input
return bool(self._conditional_input)
@property
def status(self):
return "Hidden input %s" % ("entered" if self._conditional_input
else "not entered")
def apply(self):
# nothing needed here, values are set in the self.args tree
pass
詳細と最新のコードは、Hello World NormalTUISpoke - GitHub Repository を参照してください。