Conception d'un Clavier Virtuel Qt avec Support IME Chinois pour Systèmes Embarqués

Architecture d'un système de saisie virtuel pour interfaces Qt embarquées

Dans les environnements embarqués dépourvus de clavier physique, la robustesse du clavier virtuel est critique. Cette solution se concentre sur l'intégration de la saisie chinoise (IME) au sein du framework Qt, en exploitant la gestion native des événements et les mécanismes de rendu personnalisés. L'objectif est de fournir une couche d'abstraction fiable pour les écrans tactiles industriels.

Mécanisme de propagation des événements dans Qt

Le cœur du système repose sur la boucle d'événements principale. Lorsqu'une interaction tactile survient, le système d'exploitation génère un signal brut que Qt transforme en objet QEvent. Ce dernier est ensuite dispatché vers le widget cible via la méthode notify().

Pour une supervision globale, il est possible de dériver la classe QApplication. Cela permet d'intercepter chaque événement avant sa distribution finale, utile pour le débogage ou la sécurité.

class ApplicationInstrumentée : public QApplication {
public:
    bool notify(QObject *destinataire, QEvent *événement) override {
        if (événement->type() == QEvent::KeyPress) {
            auto *touche = static_cast<QKeyEvent*>(événement);
            qInfo() << "Touche détectée:" << touche->text() 
                    << "Cible:" << destinataire->objectName();
        }
        return QApplication::notify(destinataire, événement);
    }
};

Filtres d'événements et interception

Avant qu'un événement n'atteigne son destinataire final, il traverse la chaîne des filtres installés. Un filtre retournant true stoppe la propagation. Ceci est idéal pour désactiver certaines touches dans des champs spécifiques.

class BloqueurTouches : public QObject {
protected:
    bool eventFilter(QObject *surveillé, QEvent *evt) override {
        if (surveillé == zoneSaisie && evt->type() == QEvent::KeyPress) {
            auto *k = static_cast<QKeyEvent*>(evt);
            if (k->key() == Qt::Key_Return) {
                return true; // Blocage de la validation
            }
        }
        return QObject::eventFilter(surveillé, evt);
    }
};

Simulation de saisie via QKeyEvent

Un clavier virtuel doit synthétiser des événements clavier. Il est crucial de respecter la séquence Press/Release et de gérer correctement les modificateurs (Shift, Ctrl) pour assurer la compatibilité avec les widgets standards.

void générerÉvénementTouche(QWidget *cible, int code, const QString &texte, 
                     Qt::KeyboardModifiers modifs = Qt::NoModifier) {
    // Émission de l'enfoncement
    auto *presse = new QKeyEvent(QEvent::KeyPress, code, modifs, texte);
    QCoreApplication::postEvent(cible, presse);

    // Émission du relâchement
    auto *relache = new QKeyEvent(QEvent::KeyRelease, code, modifs, texte);
    QCoreApplication::postEvent(cible, relache);
}

La gestion du focus est primordiale. Le widget destinataire doit avoir le focus clavier actif pour accepter les événements simulés. Une surveillance des événements FocusIn et FocusOut permet d'afficher ou masquer dynamiquement l'interface de saisie.

Intégration de la méthode de saisie (IME) pour le Chinois

La saisie de caractères chinois nécessite une gestion des états de composition. Qt utilise QInputMethodEvent pour transporter les chaînes pré-éditées (pinyin) et les candidats avant validation finale.

Contexte de saisie personnalisé

Dans les systèmes Linux embarqués sans environnement de bureau complet, les plugins IME systemiques (comme Fcitx) peuvent être absents. Une implémentation personnalisée de QPlatformInputContext permet d'intégrer directement un moteur comme libpinyin.

class AdaptateurPinyinEmbarqué : public QPlatformInputContext {
    MoteurPinyin *moteur;

public:
    bool filterEvent(const QEvent *evt) override {
        if (evt->type() == QEvent::KeyPress) {
            const auto *k = static_cast<const QKeyEvent*>(evt);
            char lettre = k->text().toLatin1().at(0);
            moteur->ajouterCaractère(lettre);

            QStringList candidats = moteur->obtenirCandidats(5);
            QString preedit = moteur->chaineComposition();

            QInputMethodEvent ime(preedit, candidats);
            QGuiApplication::focusObject()->inputMethod()->sendEvent(&ime);
            return true;
        }
        return false;
    }
};

Conception de l'interface utilisateur (UI)

L'architecture du clavier doit être modulaire pour faciliter la maintenance et l'adaptation aux différentes langues. L'utilisation de QStackedWidget permet de basculer entre les layouts alphabétiques, numériques et symboliques.

Stylisation avec QSS

Qt Style Sheets permet d'appliquer des règles CSS aux widgets. Pour un rendu industriel, des dégradés et des états visuels (pressé, survol) sont nécessaires.

KeyButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                stop:0 #f0f0f0, stop:1 #dcdcdc);
    border: 1px solid #b0b0b0;
    border-radius: 8px;
    font-weight: bold;
}

KeyButton:pressed {
    background: #c0c0c0;
    border-style: inset;
}

Pour des effets avancés comme les ombres portées, une surcharge de paintEvent reste nécessaire car QSS ne supporte pas nativement box-shadow.

Adaptabilité et Orientation

Les dispositifs embarqués peuvent changer d'orientation. Le layout doit répondre dynamiquement aux signaux de changement d'écran.

void ajusterPourOrientation(Qt::ScreenOrientation orient) {
    if (orient == Qt::LandscapeOrientation) {
        resize(800, 250);
        configurerLayoutHorizontal();
    } else {
        resize(400, 500);
        configurerLayoutVertical();
    }
}

Gestionnaire de clavier global

Pour orchestrer l'ensemble, un singleton KeyboardManager centralise la logique d'affichage. Il écoute les changements de focus sur l'ensemble de l'application et instancie le clavier uniquement lorsque nécessaire.

class ContrôleurClavierGlobal : public QObject {
    Q_OBJECT
    ClavierVirtuelPrincipal *vue;

public:
    static ContrôleurClavierGlobal& instance() {
        static ContrôleurClavierGlobal inst;
        return inst;
    }

    bool eventFilter(QObject *obj, QEvent *evt) override {
        if (evt->type() == QEvent::FocusIn) {
            if (estWidgetSaisie(obj)) {
                afficherPour(obj);
            }
        } else if (evt->type() == QEvent::FocusOut) {
            planifierMasquage();
        }
        return false;
    }

private:
    bool estWidgetSaisie(QObject *o) {
        return qobject_cast<QLineEdit*>(o) || 
               qobject_cast<QTextEdit*>(o);
    }
};

Cette approche garantit que le clavier virtuel reste non intrusif tout en étant immédiatement disponible pour tout champ de texte actif dans l'application embarquée.

Étiquettes: qt-framework embedded-linux input-method-engine cpp gui-architecture

Publié le 9 octobre à 08h21