想要在Qt写的程序中既可以使用qtvirtualkeyboard输入中文,又可以使用实体键盘和ibus输入法输入中文

想要在Qt写的程序中既可以使用qtvirtualkeyboard输入中文,又可以使用实体键盘和ibus输入法输入中文,能够接受子不同时使用,应该怎么办?Qt写的程序中要用输入法必须设置环境变量QT_IM_MODULE,但是这个环境变量已经被我设置为qtvirtualkeyboard了。
操作环境系统 Debian/Linux Qt 6.4
我尝试过
在代码中修改环境变量


```c++
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
qputenv("QT_IM_MODULE", QByteArray("ibus"));



但是并不生效。
后来发现只有在APP构造的之前设置才是有效的。这说明APP构造的时候回去加载输入法插件。我想做的就是让它重新加载一遍新的输入法插件。
也有尝试使用

bool useQtVirtualKeyboard = ...; // 根据需要动态设置
QByteArray imModule = useQtVirtualKeyboard ? QByteArrayLiteral("qtvirtualkeyboard") : QByteArrayLiteral("ibus");
qputenv("QT_IM_MODULE", imModule);

// 获取主窗口,并从中获取当前的输入方法
QInputMethod *inputMethod = qApp->inputMethod();
if (inputMethod) {
    // 重设当前输入方法
    inputMethod->reset();
    // 通知当前输入法重新加载
    inputMethod->update(Qt::ImEnabled);
}


也不生效。


该回答引用ChatGPT4与博主@晓码自在合作编写:

从你的描述来看,要实现在Qt程序中同时使用qtvirtualkeyboard和ibus输入法,目前遇到的主要问题是:

  1. QT_IM_MODULE这个环境变量只能在APP启动前设置,启动后设置无效。
  2. 重置当前输入法和更新输入法也无法重新加载输入法插件。

要解决这个问题,有几个思路:

  1. 在APP启动前根据需要设置QT_IM_MODULE为qtvirtualkeyboard或ibus,启动APP后无法切换。这个思路实现简单但功能较弱。
  2. 不使用QT_IM_MODULE环境变量,而是在代码中直接加载输入法插件。可以在运行时根据需要加载qtvirtualkeyboard或ibus插件,实现动态切换。
  3. 同时加载qtvirtualkeyboard和ibus两个输入法插件,并在两个输入法中实现切换逻辑。这样就无需改变环境变量和重载输入法了。

具体实现方案,你可以:

  1. 在主窗口创建时加载输入法插件:
cpp
void MainWindow::loadInputMethod() {
    if (useQtVirtualKeyboard) {  // 根据需要加载
        QInputMethod* im = new QQtVirtualKeyboardInputMethod;
        qApp->setInputMethod(im);
    } else {
        QInputMethod* im = new QIbusInputMethod;
        qApp->setInputMethod(im);
    }
}
  1. 同时加载两个输入法,并在输入法之间实现切换:
cpp
// 同时加载两个输入法
QQtVirtualKeyboardInputMethod* im1 = new QQtVirtualKeyboardInputMethod;
QIbusInputMethod* im2 = new QIbusInputMethod;

// 设置默认输入法和备用输入法
qApp->setInputMethod(im1);
qApp->setInputMethod(im2, Qt::ImPrefered);  

// 在两个输入法中添加切换逻辑
void QQtVirtualKeyboardInputMethod::changeInputMethod() {
    qApp->setInputMethod(im2);   // 切换到ibus输入法
}

void QIbusInputMethod::changeInputMethod() {
    qApp->setInputMethod(im1);   // 切换到qtvirtualkeyboard输入法
} 


这样你的APP就可以同时使用两个输入法,并在它们之间灵活切换了。


import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.VirtualKeyboard 2.2
InputPanel {
    id: inputPanel
    property int posX: 10
    property int posY: 10
    property int panelWidth: 800
    z: 99
    x: posX//更改posX,posY可更改键盘位置
    y: posY
    width: panelWidth//更改panelWidth可更改键盘大小
    visible: false
 
    states: State {
        name: "visible"
        when: inputPanel.active
        PropertyChanges {
            target: inputPanel
        }
    }
    transitions: Transition {
        from: ""
        to: "visible"
        reversible: true
        ParallelAnimation {
            NumberAnimation {
                properties: "y"
                duration: 250
                easing.type: Easing.InOutQuad
            }
        }
    }
}

调试Qt源码发现在QGuiApplication的构造的时候读取了环境变量QT_IM_MODULE,并依据该环境变量加载输入法插件。
最终在下面这个地方完成了环境变量的读取,加载输入法插件。

img

通过堆栈信息找到更上一层的调用

img

img

最终决定将eventDispatcherReady()方法进行封装,并设置为public方法。

img

img

然后编译Qt源码,使用自己编译的Qt重新写程序;切换输入法的过程代码如下

img

完美解决。

在Qt中同时支持qtvirtualkeyboard和实体键盘和ibus输入法输入中文,可以使用QInputMethod类来实现。QInputMethod类提供了与输入法交互的接口,可以获取当前输入法、设置输入法、发送输入内容等。

以下是一个可能的实现,可以根据需要动态选择输入法:

#include <QInputMethod>
#include <QGuiApplication>
#include <QByteArray>

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    bool useQtVirtualKeyboard = true; // 根据需要动态设置
    QByteArray imModule = useQtVirtualKeyboard ? QByteArrayLiteral("qtvirtualkeyboard") : QByteArrayLiteral("ibus");
    qputenv("QT_IM_MODULE", imModule);

    QInputMethod *inputMethod = qApp->inputMethod();
    if (inputMethod) {
        inputMethod->reset();
        inputMethod->update(Qt::ImEnabled);
    }

    // ... 程序主逻辑 ...

    return app.exec();
}

在代码中,首先根据需要动态设置QT_IM_MODULE环境变量,然后获取当前输入法并重新加载。在程序运行过程中,可以通过QInputMethod类的接口来发送输入内容、获取当前输入法、设置输入法等。

例如,可以在程序中使用QInputMethod::invokeAction方法来模拟键盘输入,或者使用QInputMethod::commit方法来发送输入文字。

另外,如果需要在特定的控件中使用不同的输入法,可以使用QWidget::inputMethodHints属性来设置输入法提示。例如,可以在一个QLineEdit控件中使用qtvirtualkeyboard输入法,而在另一个QTextEdit控件中使用ibus输入法。在设置完inputMethodHints属性之后,需要调用QWidget::inputMethod()方法来获取当前输入法并重新加载。

注意,在使用qtvirtualkeyboard输入法时,需要将QT_IM_MODULE环境变量设置为qtvirtualkeyboard,并且需要在程序中包含Qt5::VirtualKeyboard模块。在使用ibus输入法时,需要将QT_IM_MODULE环境变量设置为ibus,并且需要在系统中安装ibus输入法框架。

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

在Qt中同时支持qtvirtualkeyboard和实体键盘/ibus输入法输入中文的需求可以通过以下步骤实现:

  1. 在程序启动时,设置默认的输入法模块为qtvirtualkeyboard,并在环境变量QT_IM_MODULE中设置为"qtvirtualkeyboard"。这可以在应用程序的main函数中进行设置,确保在QApplication对象创建之前完成。例如:
int main(int argc, char *argv[])
{
    qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
    QApplication app(argc, argv);

    // ...其他初始化操作...

    return app.exec();
}
  1. 创建一个切换输入法的功能,允许用户在运行时切换输入法。可以通过在应用程序中添加一个按钮或菜单项来实现切换功能。
bool useQtVirtualKeyboard = ...; // 根据需要动态设置
QByteArray imModule = useQtVirtualKeyboard ? QByteArrayLiteral("qtvirtualkeyboard") : QByteArrayLiteral("ibus");
qputenv("QT_IM_MODULE", imModule);

// 获取主窗口,并从中获取当前的输入方法
QInputMethod *inputMethod = qApp->inputMethod();
if (inputMethod) {
    // 重设当前输入方法
    inputMethod->reset();
    // 通知当前输入法重新加载
    inputMethod->update(Qt::ImEnabled);
}
  1. 当用户切换输入法时,根据需要设置环境变量QT_IM_MODULE为"qtvirtualkeyboard"或"ibus",并通知当前输入法重新加载。

确保在切换输入法后调用reset()update(Qt::ImEnabled)来重新加载当前输入法。

请注意,这种方法依赖于Qt的输入法框架和输入法插件的支持。确保你已经正确安装了Qt Virtual Keyboard插件和ibus输入法,并配置了相应的输入法环境。

另外,根据Qt的文档,Qt Virtual Keyboard模块在Qt 6版本中已经作为单独的商业许可模块提供,可能需要购买相应的许可证才能使用。请确保你已经获得了适当的许可证,以便在使用Qt Virtual Keyboard模块时遵守相关许可条款。

最后,如果以上方法仍然无法实现你的需求,你可能需要进一步查阅Qt的文档、社区论坛或与Qt官方支持进行交流,以获取更详细的帮助和支持。

参考试试下面的代码:

bool useQtVirtualKeyboard = ...; // 根据需要动态设置
QByteArray imModule = useQtVirtualKeyboard ? QByteArrayLiteral("qtvirtualkeyboard") : QByteArrayLiteral("ibus");

// 设置环境变量
qputenv("QT_IM_MODULE", imModule);

// 获取主窗口对象
QWidget *mainWidget = qApp->activeWindow();
if (mainWidget) {
    // 获取输入法对象
    QInputMethod *inputMethod = qApp->inputMethod();
    if (inputMethod) {
        // 重设当前输入法
        inputMethod->reset();

        // 通知所有子窗口重新加载输入法
        QList<QWidget *> childWidgets = mainWidget->findChildren<QWidget *>();
        for (int i = 0; i < childWidgets.length(); i++) {
            QInputMethod *childInputMethod = childWidgets[i]->inputMethod();
            if (childInputMethod) {
                // 重设子窗口输入法并通知重新加载
                childInputMethod->reset();
                childInputMethod->update(Qt::ImEnabled);
            }
        }

        // 通知主窗口重新加载输入法
        inputMethod->update(Qt::ImEnabled);
    }
}

这里获取的 mainWidget 可能为 nullptr,这种情况可以在代码中进行判断处理。另外,使用 QWindow::find 获取子窗口更为准确,可以避免获取到控件等非窗口对象。