4. Qt移植输入法

输入法的需求是来源于键盘的限制。 键盘原在打字机时代为英文字母而设计,但键盘只有一百来键, 由于不同语言、国家、或地区,文字语言系统多有不同,这个时候键盘就不能满足非英文字母的语言文字系统。 输入法是指为将各种符号输入计算机或其他设备(如手机)而采用的编码方法。 这样一来就解决上述问题。

通常我们提到的输入法是指输入法软件,如我们常用的搜狗输入法,百度输入法等,这是对编码方式的应用而开发出的汉字检索软件。

本篇涉及的涉及的内容为输入法软件的移植和使用。

在嵌入式设备中,由于硬件资源,应用场景的限制,很多情况下是没有鼠标键盘的, 这个时候用户输入就需要靠触摸屏,由于汉字系统非常庞大,就需要一个专门的软件来检索汉字。 Qt中提供了插件的形式来注入输入法软件,这给我们定制自己的输入法提供了方便。

4.1. 系统软键盘

在Window中,系统给我们提供系统软键盘,来满足我们的这一需求。

sysinput001

在程序代码中我们只需要使用下面的代码就能调用系统级别的软件盘。

embed_qt_develop_tutorial_code/QtInput/sysInput/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void MainWindow::openSysInput()
{
    //只能在win7下使用
    //QDesktopServices::openUrl(QUrl("osk.exe", QUrl::TolerantMode));
    //win8.1   win10 下使用
    PVOID value = nullptr;
    BOOL ok = Wow64DisableWow64FsRedirection(&value);
    QString cmd = "C:\\Windows\\System32\\osk.exe";
    QString arg="";
    ShellExecute(nullptr, L"open", (LPCWSTR)cmd.utf16(), (LPCWSTR)arg.utf16(), nullptr, SW_SHOWNORMAL);
    if (ok)
    {
        Wow64RevertWow64FsRedirection(value);
    }
}

而在Linux中,我们则需要根据不同的系统,以及安装的输入法软件而定,这里就不展开。

4.2. Qt输入法

通常输入法程序和应用程序是独立的,两个程序采用某种通信方式进行数据交换, QT5的输入法是通过插件的方式加载的,QT根据环境变量 QT_IM_MODULE 来加载不同的输入法插件。 输入法插件默认所在目录是 QT安装目录/plugins/platforminputcontext ,这个目录里可以看到有ibus等输入法插件, 如果 QT_IM_MODULE=ibus ,那qt就会在插件目录下找 libibusplatforminputcontextplugin.so 这个插件。 在程序中再通过 qputenv(“QT_IM_MODULE”, QByteArray(“ibus”)) 就可以调用 ibus 这个输入法。

4.2.1. 输入法插件

输入法插件通过继承 QPlatformInputContext 来实现。 在 QPlatformInputContext 中有如下内容:

/opt/qt-everywhere-src-5.11.3_lubancat/include/QtGui/5.11.3/QtGui/qpa/qplatforminputcontext.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <QtGui/qtguiglobal.h>
#include <QtGui/qinputmethod.h>

QT_BEGIN_NAMESPACE

class QPlatformInputContextPrivate;

class Q_GUI_EXPORT QPlatformInputContext : public QObject
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(QPlatformInputContext)

public:
    enum Capability {
        HiddenTextCapability = 0x1
    };

    QPlatformInputContext();
    virtual ~QPlatformInputContext();

    virtual bool isValid() const;
    virtual bool hasCapability(Capability capability) const;

    virtual void reset();
    virtual void commit();
    virtual void update(Qt::InputMethodQueries);
    virtual void invokeAction(QInputMethod::Action, int cursorPosition);
    virtual bool filterEvent(const QEvent *event);
    virtual QRectF keyboardRect() const;
    void emitKeyboardRectChanged();

    virtual bool isAnimating() const;
    void emitAnimatingChanged();

    virtual void showInputPanel();
    virtual void hideInputPanel();
    virtual bool isInputPanelVisible() const;
    void emitInputPanelVisibleChanged();

    virtual QLocale locale() const;
    void emitLocaleChanged();
    virtual Qt::LayoutDirection inputDirection() const;
    void emitInputDirectionChanged(Qt::LayoutDirection newDirection);

    virtual void setFocusObject(QObject *object);
    bool inputMethodAccepted() const;

    static void setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos);

private:
    friend class QGuiApplication;
    friend class QGuiApplicationPrivate;
    friend class QInputMethod;
};

QT_END_NAMESPACE

#endif // QPLATFORMINPUTCONTEXT_H

在创建输入法插件的时候,程序会通过 Q_PLUGIN_METADATA(IID QPlatformInputContextFactoryInterface_iid FILE “xxx.json”) 来设置输入法插件的名称,这个名称在xxx.json中声明。

{
    "Keys": [ "xxx" ]
}

创建输入法的时候,则会通过下面的方式判断。

QPlatformInputContext *xxx::create(const QString &system, const QStringList &paramList)
{
    if (system.compare(QLatin1String("xxx"), Qt::CaseInsensitive) == 0) {
        return new XYPlatformInputContext();
    }
    return nullptr;
}

4.2.2. Qt自带的输入法插件

qt自5.7.0版本后自带了qtvirtualkeyboard模块,支持各国语言的输入法,中文输入法集成的google智能拼音,支持模糊拼音输入。 默认安装位置在 /opt/Qt/Qt5.11.3/5.11.3/gcc_64/plugins/platforminputcontexts/libqtvirtualkeyboardplugin.so

调用方式如下:

int main(int argc, char *argv[])
{
    qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

4.3. 开源输入法

如果说上面的介绍仍然有些不明所以,这里我在Git社区找到了两个开源的输入法。 按照其说明文档是可以跑起来的。

我也将QtInputMethod_GooglePinyin放在了我们的例程里面,工程位置:embed_qt_develop_tutorial_code/QtInput/。

工程里面有三个文件夹 googlepinyin、plugin和testWindow。

  • googlepinyin为Google拼音核心源码

  • plugin为输入法插件源码

  • testWindow是对输入法的简单测试窗口

针对工程,编译步骤如下:

  • 编译 googlepinyin (注意选择release)生成 libgooglepinyin.a ,相关文件在 plugin/googlepinyin 文件夹下;

  • 接着编译插件,生成 libtgtsmlInputContextPlugin.so 和相关链接文件,同时目录下面会产生 plugin/dict;

  • 使用这个输入法的时候,需将 libtgtsmlInputContextPlugin.so 拷贝到Qt库的插件目录(默认为 QT安装目录/plugins/platforminputcontext );

  • 同时需要将dict拷贝到APP的同一目录下;

  • 在应用程序的main.cpp的主函数中添加 qputenv(“QT_IM_MODULE”, QByteArray(“tgtsml”)) ,注意在QApplication a(argc, argv)之前。运行APP,点击文本框,就会弹出输入法。

embed_qt_develop_tutorial_code/QtInput/testWindow/main.cpp
1
2
3
4
5
6
7
8
int main(int argc, char *argv[])
{
    qputenv("QT_IM_MODULE", QByteArray("tgtsml"));
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
opensoftkeyboard001.png

4.4. 野火输入法

在我们的野火demo中,有单独提供输入法,暂时不开源,但是可以通过其他程序调用。

使用方法如下:

qputenv("QT_IM_MODULE", QByteArray("xyinput"));

插件和dict目录分别如下,arm和gcc_64两个版本。

embed_qt_develop_tutorial_code/script/fire-input

4.5. 例程说明

野火提供的Qt Demo已经开源,仓库地址在:

文档所涉及的示例代码也提供下载,仓库地址在:

例程为野火demo记事本调用野火的输入法。

4.5.1. 编程思路

4.5.2. 代码讲解

4.5.3. 编译构建

build001
  • Ubuntu 选择 Desktop Qt 5.11.3 GCC 64bit 套件,编译运行

  • LubanCat 选择 ebf_lubancat,只编译

注意 当两种构建套件进行切换时,请重新构建项目或清除之前项目。

build002

4.5.4. 运行结果

4.5.4.1. PC实验

直接点击编译,将 embed_qt_develop_tutorial_code/script/fire-input/ubuntu/libSoft-keyboard.so 拷贝到Qt库的插件目录(默认为 QT安装目录/plugins/platforminputcontext ) 将 embed_qt_develop_tutorial_code/script/fire-input/ubuntu/dict/ 目录拷贝到程序所在的目录。

运行程序,效果如下:

qtinput

4.5.4.2. LubanCat实验

通过SCP或者NFS将编译好的程序拷贝到LubanCat上

NFS环境搭建 参考这里

SCP命令如下:

# scp传输文件
# scp 文件名 服务器上的某个用户@服务器ip:/文件保存路径
scp filename server_user_name@192.168.0.205:server_file_path
# 从服务器拉取文件
# scp 服务器上的某个用户@服务器ip:/服务器文件存放路径 拉取文件保存路径
scp server_user_name@192.168.0.229:server_file_path local_path

编译好的程序在 embed_qt_develop_tutorial_code/app_bin/input 目录中,通过scp命令将编译好的程序拉到LubanCat。

scp root@192.168.0.174:/home/embed_qt_develop_tutorial_code/app_bin/input/NotePad /usr/local/qt-app/

在LubanCat运行程序,使用run_myapp.sh配置好环境,并执行NotePad。

sudo /usr/local/qt-app/run_myapp.sh /usr/local/qt-app/NotePad

这里不用拷贝 embed_qt_develop_tutorial_code/script/fire-input/arm/ 下面的文件,开发板环境已经配置好了。

输入法插件和dict分别在:

/usr/local/qt-app/dict
/usr/lib/plugins/platforminputcontexts/libSoft-keyboard.so
result002