5.10. 对附加图形用户界面(GUI)添加支持
这部分描述了如何通过执行以下高级步骤来为您的附加组件的图形用户界面(GUI)添加支持:
- 定义 Normalspoke 类所需的属性
-
定义
__init__
和initialize
方法 -
定义
refresh
、apply
和execute
方法 -
定义
status
、ready
、completed
和mandatory
属性
先决条件
- 您的附加组件包括对 Kickstart 的支持。请参阅 Anaconda 附加组件结构。
-
安装 anaconda-widgets 和 anaconda-widgets-devel 软件包,其中包含特定于
Anaconda
的 Gtk 小部件,如SpokeWindow
。
步骤
- 根据以下示例,创建带有所有必要的定义的以下模块,来添加对 Add-on 图形用户界面(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
文件中定义的小部件。还有两个值得注意的属性:
-
category
,它的值从com_example_hello_world.gui.gui.categories
模块的HelloWorldCategory
类导入。附加组件路径HelloWorldCategory
位于sys.path
中,因此值可以从com_example_hello_world
软件包导入。category
属性是N_function
名称的一部分,用于标记要转换的字符串;但会返回字符串的非转换版本,因为转换发生在后续阶段。 -
title
,其定义中包含一个下划线。title
属性下划线标记标题本身的开头,并使用Alt+H
键盘快捷键使 spoke 可访问。
通常在类定义标头和类 属性
定义后面是初始化类实例的构造器。如果是 Anaconda 图形界面对象,有两初始化新实例的种方法:__init__
方法和 initialize
方法。
这两个函数背后的原因是,GUI 对象可以一次在内存中创建 ,并在不同时间完全初始化,而 spoke
初始化可能会很耗时。因此,__init__
方法应只调用父类的 __init__
方法,例如初始化非 GUI 属性。另一方面,安装程序图形用户界面初始化时调用的 initialize
方法应该完成 spoke 的整个初始化过程。
在 Hello World add-on
示例中,定义了如下两种方法:注意传给 __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__
方法的数据参数是存储所有数据的 Kickstart 文件的内存树状表示。在祖先的一个 __init__
方法中,它存储在 self.data
属性中,这个属性允许类中的所有其他方法读取和修改结构。
存储对象
不再可用于 RHEL9。如果您的附加组件需要与存储配置进行交互,请使用 Storage DBus
模块。
由于 HelloWorldData 类已在 Hello World 附加组件示例 中定义,因此此附加组件的 self.data 中已有一个子树。它的根(一个类的实例)作为 self.data.addons.com_example_hello_world
提供。
祖先的 __init__
的所做的另一个操作是使用 spoke 的 .glade
文件初始化 GtkBuilder 的实例,并将它存储为 self.builder
。initialize
方法使用这个来获取用于显示和修改 Kickstart 文件的 %addon 部分中文本的 GtkTextEntry
。
在创建 spoke 时,__init__
和 initialize
方法都很重要。但是,spoke 的主要作用是通过希望更改或查看 spoke 的值显示和设置的用户访问。要启用此功能,可以使用其他三种方法:
-
refresh
- 在要访问 spoke 时调用 ;此方法会刷新 spoke 的状态,主要是它的 UI 元素,以确保显示的数据与内部数据结构相匹配,并通过它来确保显示 self.data 结构中存储的当前值。 -
apply
- 当 spoke 离开时调用,用于将 UI 元素的值存储回self.data
结构。 -
execute
- 当用户离开 spoke 时调用,用于根据 spoke 的新状态来执行任何运行时更改。
这些功能在 Hello World 附加组件示例中以以下方式实现:
例 5.6. 定义 refresh 、apply 和 execute 方法
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
您可以使用几个额外的方法来控制 spoke 的状态:
-
ready
- 确定 spoke 是否准备好被访问;如果值为"False",则不能访问spoke
,例如,在配置软件包源之前的Package Selection
spoke。 -
completed
- 确定 spoke 是否已完成。 -
mandatory
- 确定 spoke 是强制还是非强制的,例如,Installation Destination
spoke,其必须一直被访问,即使您想使用自动分区。
所有这些属性都需要根据安装过程的当前状态动态确定。
以下是在 Hello World 附加组件中实现这些方法的示例,这需要在 HelloWorldData
类的文本属性中设置一个特定的值:
例 5.7. 定义 ready 、completed 和 mandatory 方法
@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
在定义了这些属性后,spoke 可以控制其可访问性和完整性,但不能提供其中配置的值的摘要 - 您必须访问 spoke 以查看它是如何配置的,这可能不是必需的。因此,存在名为 status
的额外属性。此属性包含一文本行,并带有已配置值的简短摘要,然后其可显示在 spoke 标题下的 hub 中。
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)以及 Kickstart。
此处演示的示例非常简单,不包含任何控制;需要掌握 Python Gtk 编程知识才能在 GUI 中开发功能性的、交互式 spoke 。
一个值得注意的限制是每个 spoke 都必须有自己的主窗口,即 SpokeWindow
小部件的一个实例。此小部件以及其他特定于 Anaconda 的小部件可在 anaconda-widgets
软件包中找到。您可以在 anaconda-widgets-devel
软件包中找到使用 GUI 支持开发附加组件所需的其他文件,如 Glade
定义。
一旦图形界面支持模块包含所有必要的方法,您可以继续使用以下部分来添加对基于文本的用户界面的支持,或者您可以继续 部署和测试 Anaconda 附加组件 ,并测试附加组件。