5.10. アドオングラフィカルユーザーインターフェイス (GUI) のサポートの追加
このセクションでは、以下の大まかな手順を実行することで、アドオンのグラフィカルユーザーインターフェイス (GUI) にサポートを追加する方法を説明します。
- Normalspoke クラスに必要な属性を定義します。
-
__init__とinitializeメソッドを定義します。 -
refresh、apply、およびexecuteメソッドを定義します。 -
statusおよびready、completedおよびmandatoryのプロパティーを定義します。
前提条件
- アドオンには、キックスタートのサポートが含まれています。Anaconda アドオンの構造 を参照してください。
-
Anaconda固有の Gtk ウィジェット (SpokeWindowなど) が含まれる anaconda-widgets および anaconda-widgets-devel パッケージをインストールします。
手順
- 以下の例に従って、アドオングラフィカルユーザーインターフェイス (GUI) のサポートを追加するために必要なすべての定義で以下のモジュールを作成します。
例5.4 Normalspoke クラスに必要な属性の定義
# will never be translated
_ = lambda x: x
N_ = lambda x: x
# the path to addons is in sys.path so we can import things from org_fedora_hello_world
from org_fedora_hello_world.gui.categories.hello_world import HelloWorldCategory
from pyanaconda.ui.gui.spokes import NormalSpoke
# export only the spoke, no helper functions, classes or constants
all = ["HelloWorldSpoke"]
class HelloWorldSpoke(FirstbootSpokeMixIn, NormalSpoke):
"""
Class for the Hello world spoke. This spoke will be in the Hello world
category and thus on the Summary hub. It is a very simple example of a unit
for the Anaconda's graphical user interface. Since it is also inherited form
the FirstbootSpokeMixIn, it will also appear in the Initial Setup (successor
of the Firstboot tool).
:see: pyanaconda.ui.common.UIObject
:see: pyanaconda.ui.common.Spoke
:see: pyanaconda.ui.gui.GUIObject
:see: pyanaconda.ui.common.FirstbootSpokeMixIn
:see: pyanaconda.ui.gui.spokes.NormalSpoke
"""
# class attributes defined by API #
# list all top-level objects from the .glade file that should be exposed
# to the spoke or leave empty to extract everything
builderObjects = ["helloWorldSpokeWindow", "buttonImage"]
# the name of the main window widget
mainWidgetName = "helloWorldSpokeWindow"
# name of the .glade file in the same directory as this source
uiFile = "hello_world.glade"
# category this spoke belongs to
category = HelloWorldCategory
# spoke icon (will be displayed on the hub)
# preferred are the -symbolic icons as these are used in Anaconda's spokes
icon = "face-cool-symbolic"
# title of the spoke (will be displayed on the hub)
title = N_("_HELLO WORLD")
__all__ 属性は spoke クラスをエクスポートします。この後に、以前 GUI アドオンの基本機能 で言及した、属性の定義を含む定義の最初の行が続きます。これらの属性値は、com_example_hello_world/gui/spokes/hello.glade ファイルで定義されるウィジェットを参照します。この他に、以下の 2 つの重要な属性があります。
-
category。この値は、com_example_hello_world.gui.categoriesモジュールのHelloWorldCategoryクラスからインポートされます。アドオンへのパスがsys.pathにあるHelloWorldCategory。これにより、com_example_hello_worldパッケージから値をインポートできます。category属性はN_ function名の一部で、変換用の文字列をマークしますが、変換は後の段階で行われるため、変換されていない文字列のバージョンを返します。 -
タイトル。定義内にアンダースコアが 1 つ含まれています。title属性アンダースコアは、タイトル自体の先頭をマークし、Alt+Hキーボードショートカットを使用してスポークに到達できるようにします。
通常、クラス定義のヘッダーとクラス attributes の定義に続くのは、クラスのインスタンスを初期化するコンストラクターです。Anaconda グラフィカルインターフェイスオブジェクトの場合、新しいインスタンスの初期化には __init__ メソッドおよび initialize メソッドの 2 つのメソッドがあります。
このようなメソッドが 2 つある理由は、spoke の初期化に時間がかかる可能性があるため、あるタイミングで GUI オブジェクトがメモリーに作成され、別のタイミングで完全に初期化される可能性があるためです。したがって、__init__ メソッドは親の _init__ メソッドのみを呼び出し、たとえば、GUI 以外の属性を初期化する必要があります。一方、インストーラーのグラフィカルユーザーインターフェイスの初期化時に呼び出される initialize メソッドは、スポークの完全な初期化を完了する必要があります。
Hello World add-on の例で、以下のようにこの 2 つのメソッドを定義します。__init__ メソッドに渡される引数の数および説明をメモしてください。
例5.5 __init__ と初期化メソッドの定義
def __init__(self, data, storage, payload):
"""
:see: pyanaconda.ui.common.Spoke.init
: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
"""
NormalSpoke.init(self, data, storage, payload)
self._hello_world_module = HELLO_WORLD.get_proxy()
def initialize(self):
"""
The initialize method that is called after the instance is created.
The difference between init and this method is that this may take
a long time and thus could be called in a separate thread.
:see: pyanaconda.ui.common.UIObject.initialize
"""
NormalSpoke.initialize(self)
self._entry = self.builder.get_object("textLines")
self._reverse = self.builder.get_object("reverseCheckButton")
__init__ メソッドに渡されるデータパラメーターは、すべてのデータが保存されるキックスタートファイルのメモリー内ツリーのような表示になります。ancestor の __init__ メソッドのいずれかで、self.data 属性に格納されます。これにより、クラス内の他のすべてのメソッドで構造の読み取りおよび修正が可能になります。
storage object は RHEL9 以降利用できなくなりました。アドオンがストレージ設定と対話する必要がある場合は、Storage DBus モジュールを使用します。
HelloWorldData クラスは Hello World アドオンの例 ですでに定義されているため、このアドオンの self.data にはすでにサブツリーがあります。クラスのインスタンスである root は self.data.addons.com_example_hello_world として利用できます。
ancestor の __init__ が実行するもう 1 つのアクションは、spoke's .glade で GtkBuilder のインスタンスを初期化し、これを self.builder として保存することです。initialize メソッドはこれを使用して、キックスタートファイルの %addon セクションにあるテキストを表示し、変更するために使用される GtkTextEntry を取得します。
__init__ および initialize メソッドは両方とも、スポークの作成時に重要となります。ただし、スポークの主なロールは、スポークの値の表示と設定を変更または確認したいユーザーがアクセスすることです。これを有効にするには、その他の 3 つの方法を使用できます。
-
refresh: スポークがアクセスされようとするときに呼び出されます。このメソッドは、スポーク (主に UI 要素) の状態を更新し、表示されるデータが内部データ構造と一致するようにします。これにより、self.data 構造に保存されている現在の値が表示されるようにします。 -
apply: スポークが残っている場合に呼び出され、UI 要素の値をself.data構造に戻す際に使用されます。 -
execute: ユーザーがスポークを離れる場合に呼び出され、スポークの新しい状態に基づいてランタイムの変更を実行する際に使用されます。
これらの関数は、以下のように Hello World アドオンのサンプルに実装されます。
例5.6 更新、適用、および実行メソッドの定義
def refresh(self):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of
internal data structures.
:see: pyanaconda.ui.common.UIObject.refresh
"""
lines = self._hello_world_module.Lines
self._entry.get_buffer().set_text("".join(lines))
reverse = self._hello_world_module.Reverse
self._reverse.set_active(reverse)
def apply(self):
"""
The apply method that is called when user leaves the spoke. It should
update the D-Bus service with values set in the GUI elements.
"""
buf = self._entry.get_buffer()
text = buf.get_text(buf.get_start_iter(),
buf.get_end_iter(),
True)
lines = text.splitlines(True)
self._hello_world_module.SetLines(lines)
self._hello_world_module.SetReverse(self._reverse.get_active())
def execute(self):
"""
The execute method that is called when the spoke is exited. It is
supposed to do all changes to the runtime environment according to
the values set in the GUI elements.
"""
# nothing to do here
pass
複数の追加のメソッドを使用して、スポークの状態を制御できます。
-
ready: スポークへアクセスできるかどうかを判断します。値が "False" の場合はspokeへアクセスできません。たとえば、パッケージソースを設定する前にPackage Selectionスポークにアクセスできません。 -
completed- スポークが完了しているかどうかを確認します。 -
mandatory: スポークが必須かどうかを判別します。たとえば、自動パーティションを使用する場合でも、常にアクセスする必要があるInstallation Destinationスポークが必須かどうか判別します。
これらの属性はすべて、インストールプロセスの現在の状態に基づいて動的に決定する必要があります。
以下は、Hello World アドオンでのこれらのメソッドの実装例です。これには、HelloWorldData クラスの text 属性に特定の値を設定する必要があります。
例5.7 準備完了、完了、および必須メソッドの定義
@property
def ready(self):
"""
The ready property reports whether the spoke is ready, that is, can be visited
or not. The spoke is made (in)sensitive based on the returned value of the ready
property.
:rtype: bool
"""
# this spoke is always ready
return True
@property
def mandatory(self):
"""
The mandatory property that tells whether the spoke is mandatory to be
completed to continue in the installation process.
:rtype: bool
"""
# this is an optional spoke that is not mandatory to be completed
return False
これらのプロパティーが定義された後、スポークはそのアクセス可能性と完全性を制御できますが、内部で設定された値のサマリーを提供することはできません。スポークにアクセスして、スポークがどのように設定されているかを確認する必要がありますが、これは望ましくない場合があります。このため、status という追加のプロパティーが存在します。このプロパティーには、設定された値の短いサマリーを含む 1 行のテキストが含まれ、スポークタイトルの下のハブに表示することができます。
status プロパティーは、以下のように Hello World の例のアドオンで定義されます。
例5.8 status プロパティーの定義
@property
def status(self):
"""
The status property that is a brief string describing the state of the
spoke. It should describe whether all values are set and if possible
also the values themselves. The returned value will appear on the hub
below the spoke's title.
:rtype: str
"""
lines = self._hello_world_module.Lines
if not lines:
return _("No text added")
elif self._hello_world_module.Reverse:
return _("Text set with {} lines to reverse").format(len(lines))
else:
return _("Text set with {} lines").format(len(lines))
例で説明しているプロパティーをすべて定義した後に、アドオンには、グラフィカルユーザーインターフェイス (GUI) とキックスタートを示す完全なサポートがあります。
ここで示した例は非常にシンプルで、制御を含むものはありません。GUI で機能的かつインタラクティブなスポークを開発するには、Python Gtk プログラミングに関する知識が必要です。
主な制限の 1 つとして、それぞれのスポークに独自のメインウィンドウ (SpokeWindow ウィジェットのインスタンス) が必要である点が挙げられます。このウィジェットは、Anaconda 固有の他のウィジェットとともに、anaconda-widgets パッケージにあります。Glade など、GUI サポートでアドオンの開発に必要な他のファイルは、anaconda-widgets-devel パッケージで見つけることができます。
グラフィカルインターフェイスのサポートモジュールに、必要な方法をすべて含むと、以下のセクションへ進んでテキストベースのユーザーインターフェイスのサポートを追加するか、Anaconda アドオンのデプロイおよびテスト へ進んでアドオンをテストすることができます。