5.10. Ajout de la prise en charge de l'interface utilisateur graphique (GUI) Add-on


Cette section décrit comment ajouter un support à l'interface utilisateur graphique (GUI) de votre module complémentaire en effectuant les étapes de haut niveau suivantes :

  1. Définir les attributs requis pour la classe Normalspoke
  2. Définir les méthodes __init__ et initialize
  3. Définir les méthodes refresh, apply et execute
  4. Définir les propriétés status et ready, completed et mandatory

Conditions préalables

  • Votre module complémentaire inclut la prise en charge de Kickstart. Voir la structure des modules complémentaires Anaconda.
  • Installez les paquets anaconda-widgets et anaconda-widgets-devel, qui contiennent des widgets Gtk spécifiques à Anaconda, tels que SpokeWindow.

Procédure

  • Créez les modules suivants avec toutes les définitions requises pour ajouter la prise en charge de l'interface utilisateur graphique (GUI) Add-on, conformément aux exemples suivants.

Exemple 5.4. Définition des attributs requis pour la classe 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")

L'attribut __all__ exporte la classe spoke, suivie des premières lignes de sa définition, y compris les définitions des attributs mentionnés précédemment dans les caractéristiques de base du module d'extension GUI. Les valeurs de ces attributs font référence aux widgets définis dans le fichier com_example_hello_world/gui/spokes/hello.glade. Deux autres attributs notables sont présents :

  • categorydont la valeur est importée de la classe HelloWorldCategory du module com_example_hello_world.gui.categories. L'attribut HelloWorldCategory indique que le chemin d'accès aux modules complémentaires se trouve dans sys.path, de sorte que les valeurs peuvent être importées à partir du paquet com_example_hello_world. L'attribut category fait partie du nom N_ function, qui marque la chaîne pour la traduction, mais renvoie la version non traduite de la chaîne, car la traduction a lieu à un stade ultérieur.
  • titlequi contient un trait de soulignement dans sa définition. Le trait de soulignement de l'attribut title marque le début du titre lui-même et permet d'atteindre le rayon en utilisant le raccourci clavier Alt H.

Ce qui suit généralement l'en-tête de la définition de la classe et les définitions de la classe attributes est le constructeur qui initialise une instance de la classe. Dans le cas des objets de l'interface graphique Anaconda, il existe deux méthodes d'initialisation d'une nouvelle instance : la méthode __init__ et la méthode initialize.

La raison pour laquelle il existe deux fonctions de ce type est que les objets GUI peuvent être créés en mémoire à un moment donné et entièrement initialisés à un autre moment, l'initialisation de spoke pouvant prendre beaucoup de temps. Par conséquent, la méthode __init__ ne doit appeler que la méthode __init__ du parent et, par exemple, initialiser les attributs non GUI. D'autre part, la méthode initialize qui est appelée lors de l'initialisation de l'interface graphique de l'installateur doit terminer l'initialisation complète des rayons.

Dans l'exemple Hello World add-on, définissez ces deux méthodes comme suit. Notez le nombre et la description des arguments transmis à la méthode __init__.

Exemple 5.5. Définition des méthodes __init__ et initialize :

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, bootloader, 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")

Le paramètre data transmis à la méthode __init__ est la représentation arborescente en mémoire du fichier Kickstart où toutes les données sont stockées. Dans l'une des méthodes __init__ des ancêtres, il est stocké dans l'attribut self.data, ce qui permet à toutes les autres méthodes de la classe de lire et de modifier la structure.

Note

Le module storage object n'est plus utilisable depuis RHEL9. Si votre module complémentaire doit interagir avec la configuration du stockage, utilisez le module Storage DBus.

La classe HelloWorldData ayant déjà été définie dans l'exemple du module complémentaire Hello World, il existe déjà un sous-arbre dans self.data pour ce module complémentaire. Sa racine, une instance de la classe, est disponible à l'adresse self.data.addons.com_example_hello_world.

Une autre action de l'ancêtre __init__ est d'initialiser une instance de GtkBuilder avec le fichier spoke’s .glade et de le stocker en tant que self.builder. La méthode initialize l'utilise pour obtenir le fichier GtkTextEntry utilisé pour afficher et modifier le texte de la section don du fichier kickstart.

Les méthodes __init__ et initialize sont toutes deux importantes lors de la création du rayon. Cependant, le rôle principal du rayon est d'être visité par un utilisateur qui souhaite modifier ou revoir les valeurs du rayon. Pour ce faire, trois autres méthodes sont disponibles :

  • refresh - appelée lorsque le rayon est sur le point d'être visité ; cette méthode rafraîchit l'état du rayon, principalement ses éléments d'interface utilisateur, pour s'assurer que les données affichées correspondent aux structures de données internes et, par conséquent, pour s'assurer que les valeurs actuelles stockées dans la structure self.data sont affichées.
  • apply - appelée lorsque le rayon est quitté et utilisée pour stocker les valeurs des éléments de l'interface utilisateur dans la structure self.data.
  • execute - appelé lorsque les utilisateurs quittent le rayon et utilisé pour effectuer tout changement d'exécution basé sur le nouvel état du rayon.

Ces fonctions sont mises en œuvre dans l'exemple de module complémentaire Hello World de la manière suivante :

Exemple 5.6. Définition des méthodes d'actualisation, d'application et d'exécution

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

Vous pouvez utiliser plusieurs méthodes supplémentaires pour contrôler l'état des rayons :

  • ready - détermine si le rayon est prêt à être visité ; si la valeur est "False", le rayon spoke n'est pas accessible, par exemple, le rayon Package Selection avant qu'une source de paquets ne soit configurée.
  • completed - détermine si le rayon a été achevé.
  • mandatory - détermine si le rayon est obligatoire ou non, par exemple le rayon Installation Destination, qui doit toujours être visité, même si vous voulez utiliser le partitionnement automatique.

Tous ces attributs doivent être déterminés de manière dynamique en fonction de l'état actuel du processus d'installation.

Vous trouverez ci-dessous un exemple de mise en œuvre de ces méthodes dans le module complémentaire Hello World, qui exige qu'une certaine valeur soit définie dans l'attribut text de la classe HelloWorldData:

Exemple 5.7. Définir les méthodes "prêt", "complété" et "obligatoire

@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

Une fois ces propriétés définies, le rayon peut contrôler son accessibilité et son exhaustivité, mais il ne peut pas fournir un résumé des valeurs configurées à l'intérieur - vous devez visiter le rayon pour voir comment il est configuré, ce qui peut ne pas être souhaité. C'est pourquoi il existe une propriété supplémentaire appelée status. Cette propriété contient une seule ligne de texte avec un bref résumé des valeurs configurées, qui peut ensuite être affiché dans le hub sous le titre du rayon.

La propriété "status" est définie comme suit dans l'exemple de module complémentaire Hello World:

Exemple 5.8. Définition de la propriété 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))

Après avoir défini toutes les propriétés décrites dans les exemples, le module complémentaire permet d'afficher une interface utilisateur graphique (GUI) ainsi que Kickstart.

Note

L'exemple présenté ici est très simple et ne contient aucun contrôle ; des connaissances en programmation Python Gtk sont nécessaires pour développer un rayon fonctionnel et interactif dans l'interface graphique.

Une restriction notable est que chaque rayon doit avoir sa propre fenêtre principale - une instance du widget SpokeWindow. Ce widget, ainsi que d'autres widgets spécifiques à Anaconda, se trouvent dans le paquetage anaconda-widgets. Vous trouverez d'autres fichiers nécessaires au développement de modules complémentaires avec prise en charge de l'interface graphique, tels que les définitions de Glade, dans le paquetage anaconda-widgets-devel.

Une fois que votre module de support de l'interface graphique contient toutes les méthodes nécessaires, vous pouvez continuer avec la section suivante pour ajouter le support de l'interface utilisateur textuelle, ou vous pouvez continuer avec Déployer et tester un module complémentaire Anaconda et tester le module complémentaire.

Red Hat logoGithubRedditYoutubeTwitter

Apprendre

Essayez, achetez et vendez

Communautés

À propos de la documentation Red Hat

Nous aidons les utilisateurs de Red Hat à innover et à atteindre leurs objectifs grâce à nos produits et services avec un contenu auquel ils peuvent faire confiance.

Rendre l’open source plus inclusif

Red Hat s'engage à remplacer le langage problématique dans notre code, notre documentation et nos propriétés Web. Pour plus de détails, consultez leBlog Red Hat.

À propos de Red Hat

Nous proposons des solutions renforcées qui facilitent le travail des entreprises sur plusieurs plates-formes et environnements, du centre de données central à la périphérie du réseau.

© 2024 Red Hat, Inc.