Apprendre à programmer avec les méthodes de saisies en Qt

Tobias Koenig

Traducteur

Guillaume Sigui

Dans ce tutoriel, vous allez apprendre de façon détaillée, à programmer avec les méthodes de saisies en Qt pour afficher des caractères spéciaux (par exemple, pour des textes en d'autres langues) indisponibles sur le clavier classique latin.

Commentez

- Titre : Apprendre à programmer avec les méthodes de saisies en Qt

- Auteur : Tobias Koenig

- Traducteur : Guillaume Sigui

- Parution : 20 mai 2017

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2017  Tobias Koenig. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Voir également :

- Tutoriel pour apprendre à programmer un clavier virtuel en Qt

Sommaire

- I. Qu'est-ce qu'une méthode de saisie et à quoi cela sert-il ?

- II. Les méthodes de saisie en Qt

- III. Suivre le flux

- Notes de la rédaction Developpez.com

I - Qu'est-ce qu'une méthode de saisie et à quoi cela sert-il ?

Pour répondre à cette question, voyons ce que nous dit Wikipédia :

Une méthode de saisie (ou méthode d'éditeur de saisie, communément abrégé en IME) est un composant d'un système d'exploitation ou d'un programme, qui permet à ce que toute donnée générée par un périphérique d'entrée, comme les saisies au clavier ou les mouvements de la souris, soit transmise comme un input. De cette façon, les utilisateurs peuvent transmettre des caractères et symboles qui ne sont pas disponibles sur leur périphérique d'entrée.

Et donc, une méthode de saisie peut vous permettre, par exemple, d'insérer du caractère chinois, japonais, coréen ou indien, dans une zone de texte d'une application, même si ce n'est qu'un clavier de caractères latins qui est connecté à l'ordinateur. Cela se fait sur analyse du texte, qui est saisi en alphabet latin, et on a un menu popup avec, par exemple, une présélection de caractères chinois à partir de cette saisie du clavier latin. L'utilisateur peut alors sélectionner l'un de ces caractères chinois, qui va remplacer la saisie du clavier latin dans cette zone de texte.

(cliquez ici pour aggrandir l'image)

Maintenant que les méthodes de saisie sont un important prérequis pour les kits d'outils d'interface utilisateur (UI), ce n'est pas une surprise que l'un de nos favoris (aka Qt), puisse en fournir. Mais comme Qt est multiplate-forme , il ne supporte pas qu'une seule méthode de saisie, mais toute une variété, selon la plate-forme cible.

Dans ce tutoriel, nous allons voir, de plus près, comment cela est implémenté, quelles classes sont concernées, et apprendre où les étendre et les adapter.

II - Les méthodes de saisie en Qt

Pour avoir un aperçu du traitement dans une méthode de saisie de Qt, regardons premièrement, de plus près, les différents composants impliqués :

(cliquez ici pour aggrandir l'image)

- le plugin QPA : depuis sa version 4.8, Qt contient une couche d'abstraction (Qt Platform Abstraction) pour simplifier le portage des parties d'UI (Interface utilisateur) de Qt, pour les nouveaux systèmes de fenêtrage. Cette couche consiste en un ensemble de classes abstraites d'interface (QPlatform*), qui sont réimplémentées pour le système de fenêtrage cible et sont compilées et réunies ensemble comme un plugin qui sera chargé à l'exécution. L'interface centrale est la QPlatformIntegration qui est instanciée depuis le plugin par QGuiApplication au démarrage, et donne accès à toutes les autres interfaces QPA ;

- QPlatformInputContext : la QplatformInputContext est une interface abstraite pour plusieurs systèmes de méthode de saisie qui sont disponibles pour différents systèmes de fenêtrage. Pour chaque système supporté, il y a une sous-classe de QplatformInputContext, qui implémente la communication avec l'actuelle méthode de saisie en backend (par exemple, QIBusPlatformInputContext pour Ibus). Ces sous-classes sont aussi disponibles comme des plugins autonomes, qui peuvent être chargés à l'exécution (Qt 5.4 embarque deux d'entre eux, « ibus » et « composer »), ou sont compilés dans le plugin QPA (par exemple, QQnxInputContext pour QNX et QwindowsInputContext pour Windows) ;
QPlatformInputContext est une partie de l'API (Application Programming Interface) privé Qt, donc en tant que développeur d'applications, vous ne pouvez pas y accéder directement, en lieu et place, l'instance globale de la classe publique QInputMethod est utilisée. Cette instance globale est retournée par QGuiApplication::inputMethod() et il transfère simplement les appels à ces méthodes à l'instance globale QPlatformInputContext. Ce dernier est fourni par le plugin QPA chargé (voir QPlatformIntegration::inputContext()), qui retourne l'une des classes mentionnées ci-dessus ;

- QGuiApplication : la QPlatformInputContext hérite de la classe QCoreApplication et donc contient la boucle de l'événement principal de l'application, qui sera responsable du transfert des événements entrant depuis le système de fenêtrage natif des objets appropriés dans l'application. Bien que QCoreApplication ne connaisse que les événements de l'interface utilisateur autonome, la QGuiApplication détient les informations sur les états liés à l'interface utilisateur, comme quel widget a le focus du clavier actif, quelle est la méthode de saisie en cours, etc. ;

- les widgets de saisie de texte : l'objectif des widgets de saisie de texte (par exemple QLineEdit ou QTextEdit) est de visualiser les entrées du clavier de l'utilisateur, dans une représentation textuelle. Bien que les événements liés aux touches de clavier, dans le système de fenêtrage natif, ne peuvent pas être mappés un à un avec les caractères de sortie, les widgets de saisie de texte ont besoin d'être supportés par la méthode de saisie du système pour effectuer la correspondance.

III - Suivre le flux

Maintenant que nous connaissons les acteurs majeurs du jeu et leur rôle, dans la prochaine étape, nous allons voir comment ils interagissent. Pour cela, nous allons suivre l'effet d'une touche du clavier à travers tout le système jusqu'à l'affichage du caractère dans un QLineEdit.

(cliquez ici pour aggrandir l'image)

La première chose dont nous avons besoin dans notre application est un widget de saisie, qui sera sensible aux touches du clavier. Cela se fait facilement avec une instanciation de QLineEdit. Si l'utilisateur veut saisir un caractère chinois, la première de ses actions sera de positionner le focus sur ce QLineEdit en cliquant dessus avec le pointeur de la souris ou en naviguant vers elle avec la chaîne de tabulations. Dès que la QLineEdit reçoit le focus, un slot privé de la QGuiApplication est appelé (_q_updateFocusObject), qui exécute les étapes suivantes :

1. Vérifier si la QPlatformIntegration fournit un objet QPlatformInputContext ;

2. Si c'est le cas, vérifie si l'objet qui a le focus d'input veut utiliser la méthode de saisie du système ou va manipuler de lui-même les événements liés à la touche du clavier ;

3. Informe la QPlatformInputContext du nouvel objet de focus.

La première vérification est facilement exécutée, puisque la QGuiApplication est chargée par le plugin QPA lui-même, il a accès à l'instance QPlatformIntegration et peut simplement appeler la méthode QplatformIntegration::inputContext() pour vérifier si un objet valide QPlatformInputContext est retourné.

La seconde vérification est un peu plus avancée. Pour dissocier la QGuiApplication de l'interface QWidget (et par exemple, supporter le focus manipulé pour QQuickItem), il ne peut pas juste appeler une méthode sur l'objet pour obtenir ses propriétés, puisque cela pourrait requérir la classe QGuiApplication pour connaître leurs API. Au lieu de cela, QGuiApplication envoie un événement synchrone (une invocation de blocage via QGuiApplication::sendEvent(QEvent*)) à l'objet du focus. Dès lors, l'objet de focus remplit l'objet d'événement avec l'information récupérée, et lorsque l'appel bloquant revient, la QGuiApplication extrait les informations de l'événement. Pour faire court : l'événement synchrone d'envoi est utilisé pour dissocier la classe QGuiApplication de l'interface publique des objets en focus.

Alors à quoi ressemble cet événement, et quelles informations peuvent être attendues ?

QInputMethodQueryEvent est l'événement réel utilisé pour obtenir les informations, et il permet à QGuiApplication d'obtenir des informations depuis l'objet de focus comme :

- la capacité d'accepter des entrées d'une méthode de saisie (Qt::ImEnabled) ;

- le texte autour de la zone de saisie (Qt::ImSurroundingText) ;

- la langue de saisie préférée (Qt::ImPreferredLanguage) et beaucoup, beaucoup plus (voir Qt::InputMethodQuery).

(cliquez ici pour aggrandir l'image)

Dans notre cas, QGuiApplication envoie une QInputMethodQueryEvent à QLineEdit et demande un drapeau (flag) pour le Qt::ImEnabled. QLineEdit répond à cet événement dans la méthode réimplémentée QWidget::inputMethodQuery(), en vérifiant si l'attribut de widget est Qt::WA_InputMethodEnabled configuré. Cet attribut doit être configuré pour tout widget qui doit utiliser la méthode de saisie du système, et il est paramétré par défaut sur les classes d'entrée de texte (QLineEdit, QTextEdit etc.) dans Qt.

La dernière étape exécutée par _q_updateFocusObject() est d'informer l'objet QPlatformInputContext du nouvel objet de focus, duquel il pourra tirer plus d'informations ultérieurement, au besoin, pendant le processus de saisie. Cela se fait simplement en invoquant QPlatformInputContext::setFocusObject(QObject *object).

Maintenant que la QLineEdit a le focus du clavier, l'utilisateur peut appuyer sur la touche du clavier, qui va déclencher un événement de saisie dans le système d'exploitation, qui sera transféré au système de fenêtrage, à partir duquel seront appelées certaines fonctions du plugin QPA chargé et en cours d'exécution. À ce niveau, le plugin QPA transforme l'événement natif lié à la touche en une QkeyEvent, et l'injecte dans la file des événements des applications en appelant QWindowSystemInterface::handleKeyEvent() ou QWindowSystemInterface::handleExtendedKeyEvent(). Si cependant, une méthode de saisie du système est active, (dans ce cas, QPlatformIntegration::inputContext() retourne un objet valide QPlatformInputContext), il va plutôt envoyer une donnée de saisie au format brut à l'objet QPlatformInputContext. Comment la donnée de saisie au format brut est envoyée à la QPlatformInputContext, dans ce cas, n'est défini dans aucune API, et donc toute implémentation du QPA est libre de choisir comment le faire. Le plugin QPA XCB, par exemple, s'attend à ce que la QPlatformInputContext lui fournisse un slot public « x11FilterEvent », qui est invoqué avec QMetaObject::invokeMethod() et passe les données xcb_keysym* comme paramètres. Cette invocation dynamique nous permet d'utiliser différentes implémentations de QPlatformInputContext dans les systèmes XCB, sans que le plugin QPA XCB ne connaisse l'interface exacte de la classe. D'autre part, le plugin QPA QNX intègre une version compilée de QQnxInputContext, et donc il a un pointeur sur cette instance, ce qui lui permet de simplement appeler une méthode pour lui transférer le format brut de données.

(cliquez ici pour aggrandir l'image)

La sous-classe QPlatformInputContext a maintenant reçu les données au format brut et peut les transférer, en backend, à la méthode de saisie spécifique de la plate-forme réelle (par exemple, envoyer au serveur IBus via DBus). À ce niveau, le backend de la méthode de saisie peut exiger des informations additionnelles pour traiter le format de données brut, en cours. Les informations additionnelles pourraient être, par exemple :

- le texte autour de la zone de saisie active, pour interpréter les nouvelles données de saisie dans un contexte ;

- la position du curseur, pour ouvrir le popup de menu de sélection de caractères, juste à côté de lui.

Pour obtenir ces informations, la QPlatformInputContext envoie, encore une fois, un QInputMethodQueryEvent synchrone à l'objet en focus actif, exactement comme QGuiApplication l'a fait précédemment. Une fois qu'il a récupéré les informations de l'objet en focus, et transféré au backend de la méthode de saisie, le backend va composer le caractère final (ou la séquence de caractères), qui sera affiché comme le nouveau texte dans la QLineEdit. Cette composition est également faite par programmation (en suivant certaines règles spécifiques écrites du système), ou la méthode de saisie du système ouvre le menu popup avec une présélection de caractères à partir de laquelle l'utilisateur va choisir le caractère approprié.

Alors comment le texte composé est-il retourné dans la QLineEdit ? Pour cela, un autre événement synchrone est exécuté. La QPlatformInputContext crée une instance de QInputMethodEvent et y appelle setCommitString(), avec le nouveau texte composé comme paramètre. Ensuite, il envoie l'événement à l'objet en focus.

Sur l'objet en focus, la QLineEdit dans notre cas, la méthode réimplémentée QWidget::inputMethodEvent(QInputMethodEvent*) est invoquée. Cette méthode va mettre à jour le texte de la QLineEdit avec le texte composé, repositionne le curseur, et peut aussi mettre à jour la sélection en cours.

À ce niveau, l'événement associé à la touche de clavier a atteint sa destination, et l'utilisateur est en train d'appuyer la touche suivante du clavier.

En supplément à la chaîne de validation, la QPlatformInputContext peut aussi créer un QInputMethodEvent avec une chaîne de préédition, et l'envoyer à l'objet en focus avant le début de la composition, ou pendant la phase de composition. Cette chaîne de préédition est alors présentée dans la QLineEdit comme un résultat intermédiaire. L'apparence visuelle de ce texte peut être influencée par certains attributs de la QInputMethodEvent.

Dans le prochain tutoriel, nous allons apprendre comment implémenter un clavier virtuel autonome, qui utilise le framework de la méthode de saisie de Qt, pour communiquer avec les applications programmées en Qt.

Notes de la rédaction Developpez.com

L'article original a été rédigé sous le titre Qt Input Method - In Depth, écrit par Tobias Koenig.

Nous remercions KDAB d'avoir autorisé la traduction et la publication sur Developpez.com.

KDAB propose :

- des formations ;

- des conseils ;

- des développements ;

- de l'expertise C++, Qt et OpenGL.

Nos remerciements également à Guillaume Sigui pour la traduction et Claude Leloup pour la relecture orthographique.

Retour à l'article

Retour à l'article

Retour à l'article

Retour à l'article

Retour à l'article