День добрый. Как мы знаем в последних версиях, вектор развития библиотеки Qt был направлен на поддержку разработки для мобильных устройств. В связи с этим, Qt обзавелась парой важных портов - на Symbian и MeeGo. Кроме того, Qt/Embedded вполне успешно используется многими компаниями при разработке приложений для планшетных компьютеров, которые всё быстрее набирают популярность и входят в нашу жизнь. Эти обстоятельства побуждают разработчиков к разработке специфических вспомогательных классов и средств, используемых в собственных приложениях. Одним из таких специфических средств при разработке приложений под планшеты и мобильные телефоны, не имеющие аппаратной клавиатуры является разработка собственной виртуальной клавиатуры, позволяющей вводить данные в поля ввода программы.

Задача осложняется тем, что в случае Qt, где вроде бы можно глобально перехватывать все события приложения через функцию eventFilter(), существуют не просто поля вроде QInputText, QTextEdit и QPlaintTextEdit для которых относительно просто получить событие focusIn, но существуют также QWebView и QDeclarativeView, внутри которых умещаются либо веб-страница с html-полями ввода, либо набор QML-компонентов, а может и компонент QML-компонент браузера, со вставленными уже в него html-полями... В общем иерархия получается та ещё. На практике можно достучаться до каждого из этих элементов, обрабатывать различные события происходящие в них и можно даже написать вполне себе работоспособную клавиатурку... Вот только это будет громоздко, глючно и вообще состоять из чёткой системы костылей и подпорок =)

Что же можно поставить в противовес? В Qt 4, изначально весь ввод приложения был реализован через так называемый “контекст ввода” (Input Context). ЭФактически, это абстракция, позволяющая поставить между приложением и устройством ввода собственную прослойку, которая и будет преобразовывать сигналы ввода с “устройства” в события Qt-приложения. Начиная с версии Qt 4.6 и началом мобильной эры Qt, разработчики позаботились о возможности создания виртуальной клавиатуры (http://labs.qt.nokia.com/2009/08/31/new-api-for-input-panel-virtual-keyboards/)... Как устройства ввода со специфическим контекстом ввода! В Qt появились два новых события RequestSoftwareInputPanel и CloseSoftwareInputPanel, обрабатывая которые в самописаном плагине, реализующем специфический контекст ввода, можно с лёгкостью сделать виртаульную клавиатуру, имеющую прямой доступ к любым доступным полям ввода на форме приложения, так как для самого приложения она остаётся по сути чёрным ящиком и представляется как та же самая аппаратная клавиатура. Кроме того, все виджеты Qt в зависимости от того необходима им клавиатура или нет, могут самостоятельно высылать оба события, то есть кроме указания приложению нового контекста ввода нет необходимости дополнительно следить за открытием/закрытием клавиатуры при необходимости.

Итак. Опираясь на пример http://doc.trolltech.com/4.7/tools-inputpanel.html, изменив его и прокомментировав, постараемся создать собственную небольшую виртуальную клавиатуру.

Начнём с написания плагина. Он будет состоять из трёх классов:

SoftwareInputPanel - виджет на котором будут размещены кнопки нашей клавиатуры

SoftwareInputContext - собственно контекст ввода. позволяющий приложению вызывать виртуальную клавиатуру при необходимости

SoftwareInputContextPlugin - и наконец Qt-плагин, позволяющий вписать созданный контекст ввода в общую структуру плагинов Qt.

Первое. Виджет ввода.

softwareinputpanel.h

#ifndef SOFTWAREINPUTPANEL_H

#define SOFTWAREINPUTPANEL_H

#include <QWidget>

#include <QSignalMapper>

namespace Ui {

class SoftwareInputPanel;

}

class SoftwareInputPanel : public QWidget

{

Q_OBJECT

public:

explicit SoftwareInputPanel(QWidget *parent = 0);

~SoftwareInputPanel();

signals:

void characterGenerated(QChar character);

protected:

bool event(QEvent *e);

private slots:

void saveFocusWidget(QWidget *oldFocus, QWidget *newFocus);

void buttonClicked(QWidget *w);

private:

QWidget *lastFocusedWidget;

QSignalMapper signalMapper;

Ui::SoftwareInputPanel *ui;

};

#endif // SOFTWAREINPUTPANEL_H

Здесь всё просто. В данном классе для нас главное:

1 - возвращать потерянный приложение фокус ввода на тот виджет в который необходимо вводить даные.

2 - собственно ни коим образом не ловить фокус ввода нашей панелью

3 - всегда оставлять панель поверх других окон

4 - и наконец сделать маппинг всех кнопок находящихся на форме на функцию, которая будет обрабатывать их и отправлять сигнал, оповещающий контекст ввода о нажатиях на кнопки виртуальной клавиатуры.

softwareinputpanel.cpp

#include "softwareinputpanel.h"

#include "ui_softwareinputpanel.h"

SoftwareInputPanel::SoftwareInputPanel(QWidget *parent) :

Оставляем нашу панель поверх других окон

QWidget(0, Qt::Tool | Qt::WindowStaysOnTopHint),

lastFocusedWidget(0),

ui(new Ui::SoftwareInputPanel)

{

ui->setupUi(this);

Запрещаем ловить фокус и возвращаем его на предыдущий виджет.

connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),

        this, SLOT(saveFocusWidget(QWidget*,QWidget*)));

Регистрируем все события нажатий на клавиши...

signalMapper.setMapping(ui->panelButton_1, ui->panelButton_1);

signalMapper.setMapping(ui->panelButton_2, ui->panelButton_2);

signalMapper.setMapping(ui->panelButton_3, ui->panelButton_3);

signalMapper.setMapping(ui->panelButton_4, ui->panelButton_4);

signalMapper.setMapping(ui->panelButton_5, ui->panelButton_5);

signalMapper.setMapping(ui->panelButton_6, ui->panelButton_6);

signalMapper.setMapping(ui->panelButton_7, ui->panelButton_7);

signalMapper.setMapping(ui->panelButton_8, ui->panelButton_8);

signalMapper.setMapping(ui->panelButton_9, ui->panelButton_9);

signalMapper.setMapping(ui->panelButton_star, ui->panelButton_star);

signalMapper.setMapping(ui->panelButton_0, ui->panelButton_0);

signalMapper.setMapping(ui->panelButton_hash, ui->panelButton_hash);

connect(ui->panelButton_1, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_2, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_3, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_4, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_5, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_6, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_7, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_8, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_9, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_star, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_0, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

connect(ui->panelButton_hash, SIGNAL(clicked()),

        &signalMapper, SLOT(map()));

…и отправляем их все в функцию buttonClicked();

connect(&signalMapper, SIGNAL(mapped(QWidget*)),

        this, SLOT(buttonClicked(QWidget*)));

}

Так как панель по сути является отдельным окном, прежде чем вернуть фокус на предыдущий виджет, нам необходимо вернуть фокус на окно. в котором собтсенно и расположен виджет, в который необходимо производить ввод.

bool SoftwareInputPanel::event(QEvent *e)

{

switch (e->type()) {

case QEvent::WindowActivate:

    if (lastFocusedWidget)

        lastFocusedWidget->activateWindow();

    break;

default:

    break;

}

return QWidget::event(e);

}

void SoftwareInputPanel::saveFocusWidget(QWidget * /*oldFocus*/, QWidget *newFocus)

{

if (newFocus != 0 && !this->isAncestorOf(newFocus)) {

    lastFocusedWidget = newFocus;

}

}

void SoftwareInputPanel::buttonClicked(QWidget *w)

{

По сути здесь нам необходимы две вещи - получить символ из виджета и сгенерировать сигнал с даннм кодом. Обо всём остальном позаботится наш контекст ввода.

QChar chr = qvariant_cast<QChar>(w->property("buttonValue"));

emit characterGenerated(chr);

}

SoftwareInputPanel::~SoftwareInputPanel()

{

delete ui;

}

Кроме того, существует ui-файл с нашей клавиатурой, на которой для примера размещены цифровые клавиши. При необходимости любой разоаботчик сможет самостоятельно добавить остальные клавиши и сделать полноценную клавиатуру.

softwareinputpanel.ui

<?xml version="1.0" encoding="UTF-8"?>

<ui version="4.0">

<class>SoftwareInputPanel</class>

<widget class="QWidget" name="SoftwareInputPanel">

<property name="geometry">

<rect>

 <x>0</x>

 <y>0</y>

 <width>167</width>

 <height>205</height>

</rect>

</property>

<property name="windowTitle">

<string>Form</string>

</property>

<layout class="QVBoxLayout" name="verticalLayout">

<property name="spacing">

 <number>0</number>

</property>

<property name="margin">

 <number>0</number>

</property>

<item>

 <layout class="QGridLayout" name="gridLayout">

  <item row="0" column="0">

   <widget class="QPushButton" name="panelButton_1">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>1</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>49</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="0" column="1">

   <widget class="QPushButton" name="panelButton_2">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>2</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>50</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="0" column="2">

   <widget class="QPushButton" name="panelButton_3">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>3</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>51</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="1" column="0">

   <widget class="QPushButton" name="panelButton_4">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>4</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>52</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="1" column="1">

   <widget class="QPushButton" name="panelButton_5">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>5</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>53</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="1" column="2">

   <widget class="QPushButton" name="panelButton_6">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>6</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>54</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="2" column="0">

   <widget class="QPushButton" name="panelButton_7">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>7</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>55</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="2" column="1">

   <widget class="QPushButton" name="panelButton_8">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>8</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>56</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="2" column="2">

   <widget class="QPushButton" name="panelButton_9">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>9</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>57</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="3" column="0">

   <widget class="QPushButton" name="panelButton_star">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>*</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>42</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="3" column="1">

   <widget class="QPushButton" name="panelButton_0">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>0</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>48</unicode>

     </char>

    </property>

   </widget>

  </item>

  <item row="3" column="2">

   <widget class="QPushButton" name="panelButton_hash">

    <property name="sizePolicy">

     <sizepolicy hsizetype="Minimum" vsizetype="Minimum">

      <horstretch>0</horstretch>

      <verstretch>0</verstretch>

     </sizepolicy>

    </property>

    <property name="minimumSize">

     <size>

      <width>45</width>

      <height>40</height>

     </size>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>#</string>

    </property>

    <property name="buttonValue" stdset="0">

     <char>

      <unicode>35</unicode>

     </char>

    </property>

   </widget>

  </item>

 </layout>

</item>

<item>

 <layout class="QHBoxLayout" name="horizontalLayout">

  <item>

   <spacer name="horizontalSpacer">

    <property name="orientation">

     <enum>Qt::Horizontal</enum>

    </property>

    <property name="sizeHint" stdset="0">

     <size>

      <width>40</width>

      <height>20</height>

     </size>

    </property>

   </spacer>

  </item>

  <item>

   <widget class="QPushButton" name="closeButton">

    <property name="font">

     <font>

      <pointsize>8</pointsize>

     </font>

    </property>

    <property name="focusPolicy">

     <enum>Qt::NoFocus</enum>

    </property>

    <property name="text">

     <string>Close</string>

    </property>

   </widget>

  </item>

 </layout>

</item>

</layout>

</widget>

<resources/>

<connections>

<connection>

<sender>closeButton</sender>

<signal>clicked()</signal>

<receiver>SoftwareInputPanel</receiver>

<slot>hide()</slot>

<hints>

 <hint type="sourcelabel">

  <x>128</x>

  <y>192</y>

 </hint>

 <hint type="destinationlabel">

  <x>83</x>

  <y>102</y>

 </hint>

</hints>

</connection>

</connections>

</ui>

Так. С одним разобрались. Далее, основной класс - класс реализующий новый контекст ввода для приложения. Мы наследуем свой собственный класс от класса QInputContext и далее переопределям ряд его методов, о работе которых станет ясно при рассмотрении файла реализации.

softwareinputcontext.h

#ifndef SOFTWAREINPUTCONTEXT_H

#define SOFTWAREINPUTCONTEXT_H

#include <QInputContext>

class SoftwareInputPanel;

class SoftwareInputContext : public QInputContext

{

Q_OBJECT

public:

explicit SoftwareInputContext(QObject *parent = 0);

~SoftwareInputContext();

bool filterEvent(const QEvent* event);

QString identifierName();

QString language();

bool isComposing() const;

void reset();

private slots:

void sendCharacter(QChar character);

private:

void updatePosition();

private:

SoftwareInputPanel *inputPanel;

};

#endif // SOFTWAREINPUTCONTEXT_H

Реализация контекста ввода.

softwareinputcontext.cpp

#include "softwareinputcontext.h"

#include "softwareinputpanel.h"

#include <QPointer>

#include <QApplication>

Для начала в конструкторе создаём собственно наш виджет, являющийся виртуальной клавиатурой и подключаем его сигнал сообщающий о нажатии некой кнопки к нашему обработчику событий, слоту sendCharacter().

SoftwareInputContext::SoftwareInputContext(QObject *parent) :

QInputContext(parent)

{

inputPanel = new SoftwareInputPanel;

connect(inputPanel, SIGNAL(characterGenerated(QChar)), SLOT(sendCharacter(QChar)));

}

SoftwareInputContext::~SoftwareInputContext()

{

delete inputPanel;

}

Через данный метод мы фактически управляем показом/скрытием нашей клавиатуры, основываясь на событиях, генерируемых конкретным виджетом, получающим в приложении фокус в какой-либо момент времени.

bool SoftwareInputContext::filterEvent(const QEvent* event)

{

if (event->type() == QEvent::RequestSoftwareInputPanel) {

    updatePosition();

    inputPanel->show();

    return true;

} else if (event->type() == QEvent::CloseSoftwareInputPanel) {

    inputPanel->hide();

    return true;

}

return false;

}

Просто идентификатор нашего метода ввода

QString SoftwareInputContext::identifierName()

{

return "SoftwareInputPanel";

}

void SoftwareInputContext::reset()

{

}

bool SoftwareInputContext::isComposing() const

{

return false;

}

QString SoftwareInputContext::language()

{

return "en_US";

}

А вот здесь начинается магия ;-). Как мы помним, данная функция вызывается в тот момент, когда у нас уже открыта панель ввода, фокус силами панели возвращён на виджет требущий ввода и была нажата какая-либо клавиша.

void SoftwareInputContext::sendCharacter(QChar character)

{

С легкой руки Qt, получаем фокус ввода. Это может быть любое поле ввода, собственно умеющий принимать события типа QKeyEvent. Будь то виджет на форме, QML-элемент или форма ввода на веб-странице.

QPointer<QWidget> w = focusWidget();

if (!w)

    return;

Получив виджет - мы создаём два события - нажатие не кнопку и отпускание кнопки. В купе, это даёт нам события клика. И соответственно виджет имеющий фокус может его правильно обработать.

QKeyEvent keyPress(QEvent::KeyPress, character.unicode(), Qt::NoModifier, QString(character));

QApplication::sendEvent(w, &keyPress);

if (!w)

    return;

QKeyEvent keyRelease(QEvent::KeyPress, character.unicode(), Qt::NoModifier, QString());

QApplication::sendEvent(w, &keyRelease);

}

И конечно куда же мы без вспомогательной функции. Здесь мы лишь получаем позицию нашего виджета и размещаем панель таким образом, чтобы она находилась рядом с ним.

void SoftwareInputContext::updatePosition()

{

QWidget *widget = focusWidget();

if (!widget)

    return;

QRect widgetRect = widget->rect();

QPoint panelPos = QPoint(widgetRect.left(), widgetRect.bottom() + 2);

panelPos = widget->mapToGlobal(panelPos);

inputPanel->move(panelPos);

}

На данный момент мы имеем вполне себе готовый контекст ввода с рабочей виртуальной клавиатурой. Остаётся научиться подключать всё это хозяйство к любому приложению. Разработчики Qt естественно позаботились об этом заранее, создав класс QInputContextPlugin, с помощью которого можно создать подключаемый модуль Qt, который вписывается в общую систему плагинов и представляющий собой динамически подключаемую библиотеку.

softwareinputcontextplugin.h

#ifndef SOFTWAREINPUTCONTEXTPLUGIN_H

#define SOFTWAREINPUTCONTEXTPLUGIN_H

#include <QtGui/QInputContextPlugin>

#include "softwareinputcontext.h"

class SoftwareInputContextPlugin : public QInputContextPlugin {

Q_OBJECT

public:

SoftwareInputContextPlugin(QObject *parent = 0);

SoftwareInputContext *create ( const QString & key );

QString description ( const QString & key );

QString displayName ( const QString & key );

QStringList keys () const;

QStringList languages ( const QString & key );

};

#endif // SOFTWAREINPUTCONTEXTPLUGIN_H

Всё донельзя просто - наследуем QInputContextPlugin и переопределяем его методы.

softwareinputcontextplugin.cpp

#include "softwareinputcontextplugin.h"

SoftwareInputContextPlugin::SoftwareInputContextPlugin(QObject *parent) :

QInputContextPlugin(parent)

{

}

Создание нового контекста ввода

SoftwareInputContext *SoftwareInputContextPlugin::create ( const QString & key )

{

return new SoftwareInputContext(this);

}

Текстовое описание...

QString SoftwareInputContextPlugin::description ( const QString & key )

{

return "Example for Software Input Context";

}

...и имя показываемое... ну нупримерв даилоге выбора контекста ввода для приложения =)

QString SoftwareInputContextPlugin::displayName ( const QString & key )

{

return "Software Input Context";

}

О том какие контексты ввода предоставляет данный плагин...

QStringList SoftwareInputContextPlugin::keys () const

{

return QStringList() << "SoftwareInputPanel";

}

...и для каких языков... По сути, данный параметр не столь важен. потому как клавиатура сама может следить за тем какой язык показывать в определённый момент времени.

QStringList SoftwareInputContextPlugin::languages ( const QString & key )

{

return QStringList() << "en_US";

}

Q_EXPORT_PLUGIN2(SoftwareInputPanel, SoftwareInputContextPlugin)

Из всего этого мы сделаем Qt-проект, а именно подключаемую библиотеку. Смотрим:

#-------------------------------------------------

#

# Project created by QtCreator 2011-05-19T23:00:20

#

#-------------------------------------------------

QT       += core gui

TARGET = SoftwareInputPanel

TEMPLATE = lib

CONFIG += plugin

DESTDIR = $$[QT_INSTALL_PLUGINS]/inputmethods

SOURCES += softwareinputcontextplugin.cpp \

softwareinputcontext.cpp \

softwareinputpanel.cpp

HEADERS += softwareinputcontextplugin.h \

softwareinputcontext.h \

softwareinputpanel.h

unix:!symbian {

maemo5 {

    target.path = /opt/usr/lib

} else {

    target.path = /usr/local/lib

}

INSTALLS += target

}

FORMS += \

softwareinputpanel.ui

Как видно по DESTDIR - наш плагин сразу будет ставится в каталог по умолчанию для всех плагинов реализующих различные методы ввода.

Наконец стоит остаётся вопрос, как подключить данный плагин к своему приложению? Посмотрим на примере...

SoftwareInputExample.pro

SOURCES += main.cpp

HEADERS +=

FORMS   += mainform.ui

# install

target.path = $$[QT_INSTALL_EXAMPLES]/tools/inputpanel

sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS inputpanel.pro

sources.path = $$[QT_INSTALL_EXAMPLES]/tools/inputpanel

INSTALLS += target sources

symbian: include($$PWD/../../symbianpkgrules.pri)

maemo5: include($$PWD/../../maemo5pkgrules.pri)

symbian: warning(This example might not fully work on Symbian platform)

maemo5: warning(This example might not fully work on Maemo platform)

simulator: warning(This example might not fully work on Simulator platform)

По сути, в приложении всё сводится к двум строчкам кода:

#include <QInputContextFactory>

и

app.setInputContext(QInputContextFactory::create("SoftwareInputPanel",&app));

А если говорить человеческими словами, то мы всего лишь подключаем заголовок Factory-класса. позволяющего динамически создать новый контекст ввода из имющегося плагина, по его текстовому идентификатору. А стало быть нам нет нужды знать о внутреннем устройстве созданной динамической библиотеки и совсем не нужно таскать с приложением её заголовочные файлы.

main.cpp

#include <QtGui/QApplication>

#include <QtGui/QWidget>

#include "ui_mainform.h"

#include <QInputContextFactory>

int main(int argc, char **argv)

{

QApplication app(argc, argv);

Подключаем новый контекст.

app.setInputContext(QInputContextFactory::create("SoftwareInputPanel",&app));

Инициализируем форму приложения...

QWidget widget;

Ui::MainForm form;

form.setupUi(&widget);

И показываем её...

widget.show();

return app.exec();

}

mainform.ui

<?xml version="1.0" encoding="UTF-8"?>

<ui version="4.0">

<class>MainForm</class>

<widget class="QWidget" name="MainForm">

<property name="geometry">

<rect>

 <x>0</x>

 <y>0</y>

 <width>140</width>

 <height>200</height>

</rect>

</property>

<property name="windowTitle">

<string>Input Panel Example</string>

</property>

<layout class="QVBoxLayout" name="verticalLayout_2">

<item>

 <widget class="QLabel" name="label">

  <property name="text">

   <string>My age:</string>

  </property>

  <property name="alignment">

   <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>

  </property>

  <property name="buddy">

   <cstring>lineEdit</cstring>

  </property>

 </widget>

</item>

<item>

 <widget class="QLineEdit" name="lineEdit"/>

</item>

<item>

 <widget class="QLabel" name="label_2">

  <property name="text">

   <string>My phone number:</string>

  </property>

  <property name="alignment">

   <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>

  </property>

  <property name="buddy">

   <cstring>lineEdit_2</cstring>

  </property>

 </widget>

</item>

<item>

 <widget class="QLineEdit" name="lineEdit_2"/>

</item>

<item>

 <widget class="QGroupBox" name="groupBox">

  <property name="title">

   <string>My gender:</string>

  </property>

  <layout class="QVBoxLayout" name="verticalLayout">

   <item>

    <widget class="QRadioButton" name="radioButton">

     <property name="text">

      <string>Male</string>

     </property>

    </widget>

   </item>

   <item>

    <widget class="QRadioButton" name="radioButton_2">

     <property name="text">

      <string>Female</string>

     </property>

    </widget>

   </item>

  </layout>

 </widget>

</item>

</layout>

</widget>

<resources/>

<connections/>

</ui>

В сухом остатке мы получаем работоспособную виртуальную клавиатуру, которая не использует каких-бы то ни было костылей для доступа к текущему виджету и работающую налюбой платформе - MeeGo, Symbian, QWS и прочих системах где нет нормальной аппаратной клавиатуры...