QT 虚拟键盘问题解决,dialog,模态窗口,无感知

目前各个帖子都没有完美解决QT下模态窗口的键盘无响应问题,此帖已解决此问题。

问题原因:原因主要是模态窗口卡住了虚拟键盘的事件响应,导致两者冲突,出现界面假死现象

目前主要解决方案是设置Dialog为非模态窗口,此法属于绕过虚拟键盘问题,模态窗口的作用就没有了,也就没有这个帖子的意义了。

那要怎么解决这个事件无响应,并且不能修改模态窗口的属性呢?

我思索了很久,偶然想起来模态窗口的子控件是完全可以响应事件的,那我们可以在创建虚拟键盘时将模态窗口设置为虚拟键盘的父对象,不就可以了吗?

//如果当前焦点控件的父控件存在模态窗口就将键盘窗口设置为模态窗口的子类

    if(!m_keyboard){
        if(b){
            m_keyboard = new KeyBoard(testParent);
        }else{
            m_keyboard = new KeyBoard();
        }
    }

那么现在又存在两个问题了

一、怎么知道当前控件的上层父控件是模态窗口呢?

二、一个控件的父对象可以一直向上有多个,怎么一直查找呢?

第一个解决方案很简单,通过 QWidget中的windowModality()方法获取窗口模式,判断是否为模态窗口。

第二个我们可以通过递归查找方式来判断,代码如下。

bool isDialogParent(QWidget* _widget)
{
    if(_widget == nullptr){
        return false;
    }
    if(_widget->windowModality() & Qt::ApplicationModal){
        testParent = _widget;
        return true;
    }
    if(_widget->parent() != nullptr){
        auto wid = qobject_cast<QWidget*>(_widget->parent());
        if(wid){
            return isDialogParent(wid);
        }
    }
    return false;
}

经过测试,目前这个没有任何问题。

详细代码如下:

键盘布局文件:

KeyBoard.h

#ifndef KEYBOARD_H
#define KEYBOARD_H

#include <QWidget>
#include <QPushButton>

class KeyBoard : public QWidget
{
    Q_OBJECT

public:
    explicit KeyBoard(QWidget *parent = nullptr);
    ~KeyBoard();
signals:
    void sendKeyToFocusItem(const QString &keytext);
    void _move(int x,int y);
    int _show(bool b);
    void _setInputType(int type);
private:
    QPushButton * btn1 = nullptr;
    QPushButton * btn2 = nullptr;
    QPushButton * btn3 = nullptr;
    QPushButton * btn4 = nullptr;
    QPushButton * btn5 = nullptr;
    QPushButton * btn6 = nullptr;
    QPushButton * btn7 = nullptr;
    QPushButton * btn8 = nullptr;
    QPushButton * btn9 = nullptr;
    QPushButton * btnA = nullptr;
    QPushButton * btnB = nullptr;
    QPushButton * btnC = nullptr;
    QPushButton * btnD = nullptr;
    QPushButton * btnE = nullptr;
    QPushButton * btnF = nullptr;
    QPushButton * btn0 = nullptr;
    QPushButton * btnDel = nullptr;
    QPushButton * btnPoint = nullptr;
};

#endif // KEYBOARD_H

KeyBoard.cpp

#include "KeyBoard.h"
#include <QtDebug>
#include <QGridLayout>

#define BTN_SIZE 60

KeyBoard::KeyBoard(QWidget *parent) :
    QWidget(parent)
{
    //设置主窗体样式
    //this->setAttribute(Qt::WA_TranslucentBackground);
    this->setWindowFlags(Qt::Tool | \
                         Qt::FramelessWindowHint | \
                         Qt::WindowStaysOnTopHint | \
                         Qt::WindowDoesNotAcceptFocus);
    //this->setFixedSize(150,200);
    this->setStyleSheet("QWidget{"
                        "background-color:rgb(25,25,25);"
                        "}"
                        "QPushButton{"
                        "     font:25px;"
                        "     background-color:rgb(49,49,49);"
                        "     color:white;"
                        "     border:2px solid rgb(25,25,25);"
                        "     border-radius:5px;"
                        " }"
                        "QPushButton:hover{"
                        "     background-color:rgb(36,177,213);"
                        "     color:white;"
                        "}"
                        "QPushButton:pressed{"
                        "     color:blue;"
                        "}");
    btn1 = new QPushButton("1",this);btn1->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn2 = new QPushButton("2",this);btn2->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn3 = new QPushButton("3",this);btn3->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn4 = new QPushButton("4",this);btn4->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn5 = new QPushButton("5",this);btn5->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn6 = new QPushButton("6",this);btn6->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn7 = new QPushButton("7",this);btn7->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn8 = new QPushButton("8",this);btn8->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn9 = new QPushButton("9",this);btn9->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnA = new QPushButton("A",this);btn7->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnB = new QPushButton("B",this);btn8->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnC = new QPushButton("C",this);btn9->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnD = new QPushButton("D",this);btn7->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnE = new QPushButton("E",this);btn8->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnF = new QPushButton("F",this);btn9->setFixedSize(BTN_SIZE,BTN_SIZE);
    btn0 = new QPushButton("0",this);btn0->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnDel = new QPushButton(u8"←",this);btnDel->setFixedSize(BTN_SIZE,BTN_SIZE);
    btnPoint = new QPushButton(".",this);btnPoint->setFixedSize(BTN_SIZE,BTN_SIZE);

    auto layout = new QGridLayout(this);
    layout->setSpacing(3);
    layout->setMargin(3);

    layout->addWidget(btn1,0,1);
    layout->addWidget(btn2,0,2);
    layout->addWidget(btn3,0,3);

    layout->addWidget(btn4,1,1);
    layout->addWidget(btn5,1,2);
    layout->addWidget(btn6,1,3);

    layout->addWidget(btn7,2,1);
    layout->addWidget(btn8,2,2);
    layout->addWidget(btn9,2,3);

    layout->addWidget(btnA,3,1);
    layout->addWidget(btnB,3,2);
    layout->addWidget(btnC,3,3);

    layout->addWidget(btnD,4,1);
    layout->addWidget(btnE,4,2);
    layout->addWidget(btnF,4,3);

    layout->addWidget(btnPoint,5,1);
    layout->addWidget(btn0,5,2);
    layout->addWidget(btnDel,5,3);

    connect(btn1,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("1");});
    connect(btn2,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("2");});
    connect(btn3,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("3");});
    connect(btn4,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("4");});
    connect(btn5,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("5");});
    connect(btn6,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("6");});
    connect(btn7,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("7");});
    connect(btn8,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("8");});
    connect(btn9,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("9");});
    connect(btnA,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("A");});
    connect(btnB,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("B");});
    connect(btnC,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("C");});
    connect(btnD,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("D");});
    connect(btnE,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("E");});
    connect(btnF,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("F");});
    connect(btn0,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("0");});
    connect(btnPoint,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem(".");});
    connect(btnDel,&QPushButton::clicked,this,[&](){emit sendKeyToFocusItem("\x7F");});

    connect(this,&KeyBoard::_move,this,[&](int x,int y){
        this->move(x,y);
    });
    connect(this,&KeyBoard::_show,this,[&](bool b){
        if(b){
            this->show();
        }else{
            this->hide();
        }
    });
    //在这里扩展用户输入类型
    connect(this,&KeyBoard::_setInputType,this,[&](int type){
        if(type == 0){
            btnA->setVisible(false);
            btnB->setVisible(false);
            btnC->setVisible(false);
            btnD->setVisible(false);
            btnE->setVisible(false);
            btnF->setVisible(false);
        }else{
            btnA->setVisible(true);
            btnB->setVisible(true);
            btnC->setVisible(true);
            btnD->setVisible(true);
            btnE->setVisible(true);
            btnF->setVisible(true);
        }
    });
}


KeyBoard::~KeyBoard()
{

}

虚拟键盘实现接口文件:

GZH_VirtualKeyBoard.h

#ifndef GZH_VIRTUALKEYBOARD_H
#define GZH_VIRTUALKEYBOARD_H
#include <qpa/qplatforminputcontext.h>
#include "KeyBoard.h"
#include <QThread>

class GZH_VirtualKeyBoard : public QPlatformInputContext
{
    Q_OBJECT
public:
    GZH_VirtualKeyBoard();
    ~GZH_VirtualKeyBoard();

    bool isValid() const Q_DECL_OVERRIDE;
    void setFocusObject(QObject *object) Q_DECL_OVERRIDE;
    void showInputPanel() Q_DECL_OVERRIDE;
    void hideInputPanel() Q_DECL_OVERRIDE;
    bool isInputPanelVisible() const Q_DECL_OVERRIDE;

private:
    void sendKeyToFocusItem(const QString &keytext);

    KeyBoard * m_keyboard = nullptr;
    QObject * m_focusitem = nullptr;
    QWidget * testParent = nullptr;
    bool isDialogParent(QWidget*);
};

#endif // GZH_VIRTUALKEYBOARD_H

GZH_VirtualKeyBoard.cpp

#include "GZH_VirtualKeyBoard.h"
#include <QCoreApplication>
#include <QKeyEvent>
#include <QApplication>
#include <QDesktopWidget>
#include "KeyBoard.h"
#include <QtDebug>


GZH_VirtualKeyBoard::GZH_VirtualKeyBoard()
{

}

GZH_VirtualKeyBoard::~GZH_VirtualKeyBoard()
{
    disconnect(m_keyboard, &KeyBoard::sendKeyToFocusItem, this, &GZH_VirtualKeyBoard::sendKeyToFocusItem);
    if(m_keyboard) delete m_keyboard;
}

void GZH_VirtualKeyBoard::sendKeyToFocusItem(const QString &keytext)
{
    if(!m_focusitem)return;
    
    if(keytext == QString("\x7F"))
    {
        QCoreApplication::sendEvent(m_focusitem, new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier));
        QCoreApplication::sendEvent(m_focusitem, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier));
    }else
    {
        QCoreApplication::sendEvent(m_focusitem, new QKeyEvent(QEvent::KeyPress, 0, Qt::NoModifier, keytext));
        QCoreApplication::sendEvent(m_focusitem, new QKeyEvent(QEvent::KeyRelease, 0, Qt::NoModifier, keytext));
    }
}

bool GZH_VirtualKeyBoard::isValid() const
{
    return true;
}

void GZH_VirtualKeyBoard::setFocusObject(QObject *object)
{
    m_focusitem = object;
}

void GZH_VirtualKeyBoard::showInputPanel()
{
//如果当前焦点控件的父控件存在模态窗口就将键盘窗口设置为模态窗口的子类
    bool b = isDialogParent(qobject_cast<QWidget*>(m_focusitem));
    if(!m_keyboard){
        if(b){
            m_keyboard = new KeyBoard(testParent);
        }else{
            m_keyboard = new KeyBoard();
        }
        connect(m_keyboard, &KeyBoard::sendKeyToFocusItem, this, &GZH_VirtualKeyBoard::sendKeyToFocusItem);
    }
    if(m_keyboard->isHidden())emit m_keyboard->_show(true);
    QWidget *widgetTmp = qobject_cast<QWidget*>(m_focusitem);

    if(widgetTmp){
        QPoint widgetGlobalPos = widgetTmp->mapToGlobal(QPoint(0, 0));
        if(widgetGlobalPos.x() < 0){
            widgetGlobalPos.setX(0);
        }
        if(widgetGlobalPos.y() < 0){
            widgetGlobalPos.setY(0);
        }
        if(qApp->desktop()->width() - widgetGlobalPos.x() < m_keyboard->width()){
            widgetGlobalPos.setX(qApp->desktop()->width() - m_keyboard->width());
        }
        if(qApp->desktop()->height() - widgetGlobalPos.y() - 30 < m_keyboard->height()){
            widgetGlobalPos.setY(widgetGlobalPos.y() - m_keyboard->height() - 10);
        }
        else{
            widgetGlobalPos = widgetGlobalPos + QPoint(0,30);
        }
        emit m_keyboard->_setInputType(widgetTmp->property("bdtype").toInt());
        emit m_keyboard->_move(widgetGlobalPos.x(),widgetGlobalPos.y());
    }
}

void GZH_VirtualKeyBoard::hideInputPanel()
{
    if(!m_keyboard){
        return;
    }
    delete m_keyboard;
    m_keyboard = nullptr;
}

bool GZH_VirtualKeyBoard::isInputPanelVisible() const
{
    return m_keyboard->isVisible();
}
//核心方法,查找当前焦点所在窗口是否为dialog窗口
bool GZH_VirtualKeyBoard::isDialogParent(QWidget* _widget)
{
    if(_widget == nullptr){
        return false;
    }
    if(_widget->windowModality() & Qt::ApplicationModal){
        testParent = _widget;
        return true;
    }
    if(_widget->parent() != nullptr){
        auto wid = qobject_cast<QWidget*>(_widget->parent());
        if(wid){
            return isDialogParent(wid);
        }
    }
    return false;
}

新手使用指导帖子:QT 虚拟键盘使用问题_悟情道长的博客-CSDN博客

完整项目地址:

https://download.csdn.net/download/qq_32854345/87173147?spm=1001.2014.3001.5501

如需要积分,可以联系球球654320149发送

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值