银河麒麟系统中Qt程序无法切换中文
1.环境
-
操作系统:银河麒麟
-
CPU:飞腾2000
-
开发环境:QT5.9.9
-
输入法:搜狗输入法
2.问题
在银河麒麟系统中,基于QT5.9.9版本,使用Qt Creator开发的桌面应用程序,启动后,在应用编辑框(QTextEdit)无法输入中文,切换输入法无效。
编译fcitx-qt5,cmake时报ECM 、XKBCommon、bison-3.0.4错误。
make时报error: no matching function for call to ‘QMetaObject::invokeMethod(QFcitxPlatformInputContext*, QFcitxPlatformInputContext::setFocusObject(QObject*)::<lambda()>, Qt::ConnectionType)’
304 | Qt::QueuedConnection)错误。
3.原因分析
linux内核系统,包含了银河麒麟系统(基于linux内核),QT程序需要切换输入法(中文),依赖于QT输入法插件。
window版本QT安装时会自动安装window版本的输入法插件,linux版本QT安装时不一定安装该系统的输入法插件,至少银河麒麟系统不会安装。
4.QT安装环境描述
在银河麒麟系统环境下,QT只能通过源码编译安装。
本人QT输入法插件安装路径:
安装路径:/usr/local/Qt-5.9.9-release/plugins/platforminputcontexts
本人QT输入法插件:
libcomposeplatforminputcontextplugin.so
libibusplatforminputcontextplugin.so
libqtvirtualkeyboardplugin.so
插件目录中缺少libfcitxplatforminputcontextplugin.so插件,该插件对银河麒麟系统中QT程序中文输入法切换提供了支撑,因此,我们要获取该插件。
5.中文输入法插件注意点
重点:版本要一致。
QT程序:如果QT程序是V5.9.9版本编译的,输入法插件编译安装也必须基于V5.9.9版本(输入法插件编译安装依赖于QT版本),两者保持一致。
Qt Creator开发工具:开发过程中需要切换输入法,也需libfcitxplatforminputcontextplugin.so插件支持,才能使用中文输入法,Qt Creator也是基于QT某版本,因此,Qt Creator和输入法插件依赖的QT版本保持一致。
6.操作系统环境
银河麒麟系统部分UI功能是依赖于QT的,因此银河麒麟系统本身安装有QT版本,本人操作系统本身安装有QT5.12.12版本,安装的Qt Creator基于QT5.12.12版本,系统安装的QT5.12.12版本中包含了libfcitxplatforminputcontextplugin.so插件(该插件基于QT5.12.12),因此本人Qt Creator可以输入中文,这也回答了上面的版本一致问题(Qt Creator和输入法插件依赖的QT保本保持一致),该插件位置在/usr/lib/aarch64-linux-gnu/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so。
7.回答网上部分解决方案
- 网上有部分技术人员说,将该/usr/lib/aarch64-linux-gnu/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so(每个人目录可能不一样,可以使用find / -name libfcitxplatforminputcontextplugin.so查找)插件拷贝到自己安装的QT插件目录、Qt Creator插件目录、QT程序插件目录中,修改一下插件执行权限就可以输入中文,里面有很大歧义,如果QT程序、Qt Creator和插件依赖的QT版本一致,则可以切换输入法,如果版本不一致,还是无法解决问题。
- 网上有部分技术人员说,执行sudo apt-get install fcitx-qt5,这个也只能解决QT程序、Qt Creator和插件依赖的QT版本一致,如果不一致,可能只能解决Qt Creator中文输入法问题,无法解决QT程序问题,因为sudo apt-get install fcitx-qt5命令会将一些依赖库安装到系统路径下,安装的最新版插件库,与实际使用QT版本不一致。
8.解决方案
基于QT5.9.9版本,手动编译fcitx-qt5,fcitx-qt5提供了对linux环境中文输入法支持,编译成功后会生成libfcitxplatforminputcontextplugin.so插件。
以下路径都是本人的,可以根据自己情况进行更改。
8.1下载fcitx-qt5源码
因为fcitx-qt5官网有源码,但是版本太多,不知道选择哪个,所以从git上下载最新源码。
新建一个文件夹/home/adminyw/soft/fcitx,在/fcitx/文件夹下使用终端执行以下语句,会将源码下载到当前文件夹下的fcitx-qt5文件夹中,有时候外网不稳定,可以多执行几次:
adminyw@adminyw-pc:~/soft/fcitx$ git clone https://github.com/fcitx/fcitx-qt5
下载内容如下:
adminyw@adminyw-pc:~/soft/fcitx/fcitx-qt5$ ll
总用量 76
drwxrwxr-x 7 adminyw adminyw 4096 2月 21 14:24 ./
drwxrwxr-x 3 adminyw adminyw 4096 2月 21 14:00 …/
-rw-rw-r-- 1 adminyw adminyw 1754 2月 21 14:01 .clang-format
-rwxrwxr-x 1 adminyw adminyw 150 2月 21 14:01 clang-format.sh*
drwxrwxr-x 2 adminyw adminyw 4096 2月 21 14:01 cmake/
-rw-rw-r-- 1 adminyw adminyw 1643 2月 21 14:01 CMakeLists.txt
-rw-rw-r-- 1 adminyw adminyw 17989 2月 21 14:01 COPYING
-rw-rw-r-- 1 adminyw adminyw 1488 2月 21 14:01 COPYING.BSD
-rw-rw-r-- 1 adminyw adminyw 158 2月 21 14:01 .formatignore
drwxrwxr-x 8 adminyw adminyw 4096 2月 21 14:01 .git/
-rw-rw-r-- 1 adminyw adminyw 80 2月 21 14:01 .gitignore
drwxrwxr-x 2 adminyw adminyw 4096 2月 21 14:01 po/
drwxrwxr-x 7 adminyw adminyw 4096 2月 21 14:01 qt5/
drwxrwxr-x 3 adminyw adminyw 4096 2月 21 14:01 qt6/
-rw-rw-r-- 1 adminyw adminyw 141 2月 21 14:01 README
8.2CMakeLists配置
- 要求cmake版本高于VERSION 3.1;
- 要求QT版本高于REQUIRED_QT_VERSION 5.1.0;
- 需要ECM 1.4.0模块、XKBCommon 0.5.0模块、bison-3.0.4模块;
- 关于编译的QT版本,option(ENABLE_QT5 “Enable Qt5” On)、option(ENABLE_QT6 “Enable Qt6 im module” Off),默认配置是按QT5版本进行编译,如果想用QT6版本编译,需将QT5的on设置为false,QT6的off设置为on。
以上是CMakeLists中需要注意的点,同时也是cmake和编译fcitx-qt5前提条件。
8.3安装必要依赖
-
该库是通过cmake构建的,需要安装cmake,执行sudo apt-get install cmake(会将cmake安装到系统中,不用自己设置环境变量),也可下载linux版本的cmake,解压后,将bin目录设置到PATH中,版本高于3.1;
-
安装extra-cmake-modules-1.4.0,即ECM 模块,下载该源码,解压后目录/home/adminyw/soft/fcitx/extra-cmake-modules-1.4.0,在该路径终端分别执行:mkdir build,cd build,cmake …,make,sudo make install,解决cmake时报缺失ECM 问题;
-
如果cmake和make时不报XKBCommon的错误,可以不安装XKBCommon和bison,如果报XKBCommon错误,先安装bison,再安装XKBCommon。下载bison-3.0.4和libxkbcommon-0.5.0源码,解压后,进入解压目录,在该路径终端分别执行./configure,make,sudo make install,解决cmake时缺失xkbcommon问题。
8.4执行cmake
在/home/adminyw/soft/fcitx/fcitx-qt5路径,打开终端,执行以下语句:
adminyw@adminyw-pc:~/soft/fcitx/fcitx-qt5$ mkdir build
adminyw@adminyw-pc:~/soft/fcitx/fcitx-qt5$ cd build
adminyw@adminyw-pc:~/soft/fcitx/fcitx-qt5$ cmake .. -DENABLE_LIBRARY=false -DQt5Concurrent_DIR=/usr/local/Qt-5.9.9-release/lib/cmake/Qt5Concurrent -DQt5Core_DIR=/usr/local/Qt-5.9.9-release/lib/cmake/Qt5Core -DQt5DBus_DIR=/usr/local/Qt-5.9.9-release/lib/cmake/Qt5DBus -DQt5Gui_DIR=/usr/local/Qt-5.9.9-release/lib/cmake/Qt5Gui -DQt5Widgets_DIR=/usr/local/Qt-5.9.9-release/lib/cmake/Qt5Widgets -DQt5_DIR=/usr/local/Qt-5.9.9-release/lib/cmake/Qt5
说明:-DENABLE_LIBRARY=false:解决Fcitx 4.2.8、LibIntl 、po报错问题;
-DQt5Concurrent_DIR、-DQt5Core_DIR、-DQt5DBus_DIR、-DQt5Gui_DIR、-DQt5Widgets_DIR、-DQt5_DIR:基于自己QT5.9.9版本进行编译,如果不设置,则以操作系统QT5.12.12版本进行编译,这样得到输入法插件将版本不一致,无法使用。网上有朋友说将自己安装的QT的bin目录设置到PATH路径下,但我设置了,还是无法解决编译时以QT5.12.12版本进行编译。
执行cmake后,显示以下信息,说明cmake成功。
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.1")
-- Found XKBCommon_XKBCommon: /usr/lib/aarch64-linux-gnu/libxkbcommon.so (found version "0.10.0")
-- Found XKBCommon: /usr/lib/aarch64-linux-gnu/libxkbcommon.so (found suitable version "0.10.0", minimum required is "0.5.0") found components: XKBCommon
-- The following OPTIONAL packages have been found:
* PkgConfig
-- The following REQUIRED packages have been found:
* ECM (required version >= 1.4.0)
* XKBCommon (required version >= 0.5.0), Keyboard handling library using XKB data, <http://xkbcommon.org>
* Qt5DBus
* Qt5Widgets
* Qt5Concurrent
* Qt5 (required version >= 5.1.0)
* Qt5Gui (required version >= 5.1.0)
* Qt5Core
-- Configuring done
-- Generating done
-- Build files have been written to: /home/adminyw/soft/fcitx/fcitx-qt5/build
8.5执行make
fcitx-qt5基于QT版本,我下载是最新fcitx-qt5源码,最新的fcitx-qt5代码发生改变,支持Qt5.9以上版本,Qt5.9.9版本中qobjectdefs.h的invokeMethod不支持lambda函数,Qt5.9以后版本支持lambda表达式,
fcitx-qt5中的QFcitxPlatformInputContext.cpp,在代码块中调用了
QMetaObject::invokeMethod(
this,
[this, window]() {
if (window != m_lastWindow) {
return;
}
if (validICByWindow(window.data())) {
cursorRectChanged();
}
},
Qt::QueuedConnection);
此方法中使用了lambda表达式,再使用Qt5.9.9版本编译fcitx-qt5时会出现问题,因为QT5.9.9中qobjectdefs.h不提供有lambda表达式参数的接口,应该QT5.9版本以后提供该接口,因此需要修改fcitx-qt5中的源码,去掉lambda表达式写法,换做slots槽函数。
修改qfcitxplatforminputcontext.h,修改如下:
private Q_SLOTS:
void processKeyEventFinished(QDBusPendingCallWatcher *);
//增加槽函数,使fcitx-qt5源码适配QT5.9.9版本
void cursorRectChangedForOldQtVersion(QPointer<QWindow> window);
修改qfcitxplatforminputcontext.cpp中的setFocusObject方法,如下:
void QFcitxPlatformInputContext::setFocusObject(QObject *object) {
Q_UNUSED(object);
FcitxInputContextProxy *proxy = validICByWindow(m_lastWindow);
commitPreedit(m_lastObject);
if (proxy) {
proxy->focusOut();
}
QWindow *window = qApp->focusWindow();
m_lastWindow = window;
m_lastObject = object;
// Always create IC Data for window.
if (window) {
proxy = validICByWindow(window);
if (!proxy) {
createICData(window);
}
}
if (!window || (!inputMethodAccepted() && !objectAcceptsInputMethod())) {
m_lastWindow = nullptr;
m_lastObject = nullptr;
return;
}
if (proxy) {
proxy->focusIn();
// We need to delegate this otherwise it may cause self-recursion in
// certain application like libreoffice.
auto window = m_lastWindow;
//注释该方法的调用,QT5.9.9不支持lambda表达式参数的接口,invokeMethod方法由qobjectdefs.h提供
// QMetaObject::invokeMethod(
// this,
// [this, window]() {
// if (window != m_lastWindow) {
// return;
// }
// if (validICByWindow(window.data())) {
// cursorRectChanged();
// }
// },
// Qt::QueuedConnection);
//修改为该方法调用,"cursorRectChangedForOldQtVersion"是qfcitxplatforminputcontext.h中新增函数名
QMetaObject::invokeMethod(
this,
"cursorRectChangedForOldQtVersion",
Qt::QueuedConnection,
Q_ARG(QPointer<QWindow>, window));
}
}
不按上面要求修改源码,make时会报错:
/home/adminyw/soft/fcitx/fcitx-qt5/qt5/platforminputcontext/qfcitxplatforminputcontext.cpp: In member function ‘virtual void QFcitxPlatformInputContext::setFocusObject(QObject*)’:
/home/adminyw/soft/fcitx/fcitx-qt5/qt5/platforminputcontext/qfcitxplatforminputcontext.cpp:304:33: error: no matching function for call to ‘QMetaObject::invokeMethod(QFcitxPlatformInputContext*, QFcitxPlatformInputContext::setFocusObject(QObject*)::<lambda()>, Qt::ConnectionType)’
304 | Qt::QueuedConnection);
| ^
In file included from /usr/local/Qt-5.9.9-release/include/QtCore/qmetatype.h:49,
from /usr/local/Qt-5.9.9-release/include/QtDBus/qtdbusglobal.h:44,
from /usr/local/Qt-5.9.9-release/include/QtDBus/qdbusconnection.h:44,
from /usr/local/Qt-5.9.9-release/include/QtDBus/QDBusConnection:1,
from /home/adminyw/soft/fcitx/fcitx-qt5/qt5/platforminputcontext/qfcitxplatforminputcontext.cpp:21:
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:406:17: note: candidate: ‘static bool QMetaObject::invokeMethod(QObject*, const char*, Qt::ConnectionType, QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument)’
406 | static bool invokeMethod(QObject *obj, const char *member,
| ^~~~~~~~~~~~
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:406:17: note: candidate expects 14 arguments, 3 provided
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:420:24: note: candidate: ‘static bool QMetaObject::invokeMethod(QObject*, const char*, QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument)’
420 | static inline bool invokeMethod(QObject *obj, const char *member,
| ^~~~~~~~~~~~
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:420:63: note: no known conversion for argument 2 from ‘QFcitxPlatformInputContext::setFocusObject(QObject*)::<lambda()>’ to ‘const char*’
420 | static inline bool invokeMethod(QObject *obj, const char *member,
| ~~~~~~~~~~~~^~~~~~
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:437:24: note: candidate: ‘static bool QMetaObject::invokeMethod(QObject*, const char*, Qt::ConnectionType, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument)’
437 | static inline bool invokeMethod(QObject *obj, const char *member,
| ^~~~~~~~~~~~
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:437:63: note: no known conversion for argument 2 from ‘QFcitxPlatformInputContext::setFocusObject(QObject*)::<lambda()>’ to ‘const char*’
437 | static inline bool invokeMethod(QObject *obj, const char *member,
| ~~~~~~~~~~~~^~~~~~
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:454:24: note: candidate: ‘static bool QMetaObject::invokeMethod(QObject*, const char*, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument)’
454 | static inline bool invokeMethod(QObject *obj, const char *member,
| ^~~~~~~~~~~~
/usr/local/Qt-5.9.9-release/include/QtCore/qobjectdefs.h:454:63: note: no known conversion for argument 2 from ‘QFcitxPlatformInputContext::setFocusObject(QObject*)::<lambda()>’ to ‘const char*’
454 | static inline bool invokeMethod(QObject *obj, const char *member,
| ~~~~~~~~~~~~^~~~~~
/usr/bin/cmake: /home/adminyw/soft/dminstall/bin/libcurl.so.4: no version information available (required by /usr/bin/cmake)
[ 85%] Building CXX object qt5/platforminputcontext/CMakeFiles/fcitxplatforminputcontextplugin.dir/inputcontext1proxy.cpp.o
/usr/bin/cmake: /home/adminyw/soft/dminstall/bin/libcurl.so.4: no version information available (required by /usr/bin/cmake)
[ 90%] Building CXX object qt5/platforminputcontext/CMakeFiles/fcitxplatforminputcontextplugin.dir/inputmethodproxy.cpp.o
make[2]: *** [qt5/platforminputcontext/CMakeFiles/fcitxplatforminputcontextplugin.dir/build.make:159:qt5/platforminputcontext/CMakeFiles/fcitxplatforminputcontextplugin.dir/qfcitxplatforminputcontext.cpp.o] 错误 1
make[2]: *** 正在等待未完成的任务....
make[1]: *** [CMakeFiles/Makefile2:113:qt5/platforminputcontext/CMakeFiles/fcitxplatforminputcontextplugin.dir/all] 错误 2
make: *** [Makefile:130:all] 错误 2
修改完成后,执行以下语句:
adminyw@adminyw-pc:~/soft/fcitx/fcitx-qt5/build$ make -j4
编译成功后,会在/home/adminyw/soft/fcitx/fcitx-qt5/build/qt5/platforminputcontext文件夹下生成libfcitxplatforminputcontextplugin.so输入法插件库,该库就是基于QT5.9.9版本编译后的插件库,然后将该库拷贝到自己安装的QT插件目录、Qt Creator插件目录、QT程序插件目录中,修改一下插件执行权限就可以切换输入法,使用中文进行输入。
8.6执行make install
无必要,可以不执行make install,因为make后会生成libfcitxplatforminputcontextplugin.so插件库,可以执行使用。
make install是将fcitx-qt5安装到/usr/local/目录下,也会生成插件库,会将相关信息添加环境变量中。
9.总结
fcitx-qt5编译安装,一定要注意QT版本问题。
以上总结都是个人编译过程遇到问题,如果为的解答没有解决您的问题,可以参考其他linux或银河麒麟系统无法输入中文的解决方案,和我的进行结合,解决个人问题。
如果还是没有解决,欢迎评论区留言,共同讨论,谢谢大家阅读交流。