Qt QInputContext

 想在一个QT应用中用软键盘进行输入,于是要先研究下软键盘输入原理,幸运的是Qt4.7.0库中带有一个InputPanel的示例程序,怀着激动的心情先打开运行一遍,但发现怎么点也输入不了,于是开始仔细阅读程序和帮助,想搞清楚软键盘输入的原理以及这里是否存在bug,如果有bug那它又在哪里,怎么修改等等。

Qt QInputContext_第1张图片

 现在解释程序代码如下:
 myinputpanel.h

#ifndef MYINPUTPANEL_H
#define MYINPUTPANEL_H
#include 
#include 
#include "ui_myinputpanelform.h"
//! [0]
class MyInputPanel : public QWidget
{
    Q_OBJECT
public:
    MyInputPanel();
signals:
    void characterGenerated(QChar character);
protected:
    bool event(QEvent *e);
private slots:
    void saveFocusWidget(QWidget *oldFocus, QWidget *newFocus);
    void buttonClicked(QWidget *w);
private:
    Ui::MyInputPanelForm form;//输入界面对象
    QWidget *lastFocusedWidget;
    QSignalMapper signalMapper;
};
//! [0]
#endif // MYINPUTPANEL_H 

myinputpanel contxet.h

#ifndef MYINPUTPANELCONTEXT_H
#define MYINPUTPANELCONTEXT_H
#include 
#include "myinputpanel.h"
class MyInputPanel;
//! [0]
class MyInputPanelContext : public QInputContext
{
    Q_OBJECT
public:
    MyInputPanelContext();
    ~MyInputPanelContext();
    bool filterEvent(const QEvent* event);
    QString identifierName();
    QString language();
    bool isComposing() const;
    void reset();
private slots:
    void sendCharacter(QChar character);
private:
    void updatePosition();
private:
    MyInputPanel *inputPanel;
};
//! [0]
#endif // MYINPUTPANELCONTEXT_H

main.cpp

 #include 
#include 
//! [main]
#include "myinputpanelcontext.h"
#include "ui_mainform.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    MyInputPanelContext *ic = new MyInputPanelContext;
    app.setInputContext(ic);//设置为应用程序范围内的输入上下文
    QWidget widget;
    Ui::MainForm form;
    form.setupUi(&widget);
    widget.show();
    return app.exec();
}
//! [main]

myinputpanel.cpp

#include "myinputpanel.h"

//! [0]
MyInputPanel::MyInputPanel()
    : QWidget(0, Qt::Tool | Qt::WindowStaysOnTopHint),
      lastFocusedWidget(0)
{
    form.setupUi(this);
    //光标事件改变就更新lastFocusedWidget
    connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
            this, SLOT(saveFocusWidget(QWidget*,QWidget*)));
        
    signalMapper.setMapping(form.panelButton_1, form.panelButton_1);
    signalMapper.setMapping(form.panelButton_2, form.panelButton_2);
    signalMapper.setMapping(form.panelButton_3, form.panelButton_3);
    signalMapper.setMapping(form.panelButton_4, form.panelButton_4);
    signalMapper.setMapping(form.panelButton_5, form.panelButton_5);
    signalMapper.setMapping(form.panelButton_6, form.panelButton_6);
    signalMapper.setMapping(form.panelButton_7, form.panelButton_7);
    signalMapper.setMapping(form.panelButton_8, form.panelButton_8);
    signalMapper.setMapping(form.panelButton_9, form.panelButton_9);
    signalMapper.setMapping(form.panelButton_star, form.panelButton_star);
    signalMapper.setMapping(form.panelButton_0, form.panelButton_0);
    signalMapper.setMapping(form.panelButton_hash, form.panelButton_hash);
    //按键到signalMapper的映射
    connect(form.panelButton_1, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_2, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_3, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_4, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_5, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_6, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_7, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_8, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_9, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_star, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_0, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    connect(form.panelButton_hash, SIGNAL(clicked()),
            &signalMapper, SLOT(map()));
    //已映射好的signalMapper和input panel的映射
    connect(&signalMapper, SIGNAL(mapped(QWidget*)),
            this, SLOT(buttonClicked(QWidget*)));
}
//! [0]
bool MyInputPanel::event(QEvent *e)
{
    switch (e->type()) {
//! [1]
    case QEvent::WindowActivate://窗口激活事件
        if (lastFocusedWidget)//如果上一次的非input panel窗口的焦点指针非零,就激动该指针的父窗口
            lastFocusedWidget->activateWindow();
        break;
//! [1]
    default:
        break;
    }
    return QWidget::event(e);//执行默认事件
}

//! [2]
void MyInputPanel::saveFocusWidget(QWidget * /*oldFocus*/, QWidget *newFocus)
{//判断是否是输入界面的子窗口指针,如果不是则表明是主界面的焦点指针,然后保存下来
    if (newFocus != 0 && !this->isAncestorOf(newFocus)) {
        lastFocusedWidget = newFocus;

    }
}
//! [2]
//! [3]
void MyInputPanel::buttonClicked(QWidget *w)
{//提取属性是buttonValue这种的值,这里是按键值,并转换为char类型
    QChar chr = qvariant_cast(w->property("buttonValue"));
    emit characterGenerated(chr);//发射出去
}
//! [3]

myinputpanel contetx.cpp

 #include 
#include "myinputpanelcontext.h"
//#include 
//! [0]
MyInputPanelContext::MyInputPanelContext()
{
    inputPanel = new MyInputPanel;
    connect(inputPanel, SIGNAL(characterGenerated(QChar)), SLOT(sendCharacter(QChar)));
}
//! [0]
MyInputPanelContext::~MyInputPanelContext()
{
    delete inputPanel;
}
//! [1]
//事件过滤:只有面板打开和面板关闭两个函数
bool MyInputPanelContext::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;
}
//! [1]
//返回标识符名字
QString MyInputPanelContext::identifierName()
{
    return "MyInputPanelContext";
}
//复位
void MyInputPanelContext::reset()
{
}
//输入事件发送成功与否
bool MyInputPanelContext::isComposing() const
{
    return true;
}
//编码方式
QString MyInputPanelContext::language()
{
    return "en_US";
}
//! [2]
//发送输入字符串
void MyInputPanelContext::sendCharacter(QChar character)
{   //保护指针 /*Returns the widget that has an input focus for this input context.*/
    QPointer w = focusWidget();//这个焦点应该还是有光标的LineEdit
    if (!w)
        return;
    //按下事件
    QKeyEvent keyPress(QEvent::KeyPress, character.unicode(), Qt::NoModifier, QString(character));
    QApplication::sendEvent(w, &keyPress);//w为接收者,发送按下字符
    if (!w)
        return;//
    //按键释放
    QKeyEvent keyRelease(QEvent::KeyPress, character.unicode(), Qt::NoModifier, QString());
    QApplication::sendEvent(w, &keyRelease);//发送释放信号
}
//! [2]
//! [3]
void MyInputPanelContext::updatePosition()
{
    QWidget *widget = focusWidget();//返回有光标的窗口部件的指针,这里是两个QLineEdit窗口部件的指针
    if (!widget)
        return;

    QRect widgetRect = widget->rect();//返回窗口部件的矩形对象
    QPoint panelPos = QPoint(widgetRect.left()+10, widgetRect.bottom() + 12);//以输入光标点处的x,y坐标,来确定输入面板的放置位置信息
    panelPos = widget->mapToGlobal(panelPos);//转换窗口部件中的坐标到全屏坐标中去
    inputPanel->move(panelPos);//移到设置点处
}
//! [3]

 好了,代码解释就到这里,但是为什么,按了软件盘,怎么没输入呢?于是我加入qDebug进行调试,发现按键时确实发出了信号啊,然后继续,调试,在void MyInputPanelContext::sendCharacter(QChar character)发现了,错误,当我在keyPress前面加入一条qDebug打印语句时,尽然没输出,那么肯定是这里有问题,那么怎么会没执行呢,于是又在if(!w)加了句,qDebug<<"ceshi"发现就有输出了,说明这里是因为,w=0使得直接return了。好,终于找到了错误了,但是为啥会w=0呢,原来QT帮助说明了,在设计键盘界面的时候,将所有键的focusPolicy设为了Nofocus(没有焦点),那么在调用focusWidget函数时,返回的就肯定是0了。其实,我们原本想干什么呢,我们想在光标点击的窗口中(比如这里的LineEdit)点击光标弹出输入软件键盘了,然后点击输入,那么好,输入肯定是要输入到光标所点击的窗口当中去了,那么这里返回inputpanel面板中的光标所在的窗口就没意义了,所以这里,应该改变那个指针。怎么改了,我们看inputpanel类中刚好有一个QWidget *lastFocusedWidget指针,那么应该想法,调用这个指针,怎么办呢?这里是private在inputpanel类外inputpanelcontext类中不能访问啊。所以这里在myinputpanel.h中的加入公有函数QWidget *getFocusedWidget();在myinputpanel.cpp中定义如下,返回光标要输入窗口的指针即可。


 #ifndef MYINPUTPANEL_H
#define MYINPUTPANEL_H
#include 
#include 
#include "ui_myinputpanelform.h"
//! [0]
class MyInputPanel : public QWidget
{
    Q_OBJECT
public:
    MyInputPanel();
    QWidget *getFocusedWidget();
signals:
    void characterGenerated(QChar character);
protected:
    bool event(QEvent *e);
private slots:
    void saveFocusWidget(QWidget *oldFocus, QWidget *newFocus);
    void buttonClicked(QWidget *w);
private:
    Ui::MyInputPanelForm form;
    QWidget *lastFocusedWidget;
    QSignalMapper signalMapper;
};

//返回主界面焦点处窗口指针 myinputpanel.cpp
QWidget * MyInputPanel::getFocusedWidget()
{
    return lastFocusedWidget;
}

最后,修改void MyInputPanelContext::sendCharacter(QChar character)函数,将w指针的产生方式改为w=inputpanel->getFocusedWidget(),这样就能正确获得光标输入处的指针了。修改后的函数如下:

void MyInputPanelContext::sendCharacter(QChar character)
{
   // QPointer w = focusWidget();
     QPointer w = inputPanel->getFocusedWidget();
    if (!w)
        return;
    QKeyEvent keyPress(QEvent::KeyPress, character.unicode(), Qt::NoModifier, QString(character));
    QApplication::sendEvent(w, &keyPress);//w为事件接收者
    if (!w)
        return;//
    QKeyEvent keyRelease(QEvent::KeyPress, character.unicode(), Qt::NoModifier, QString());
    QApplication::sendEvent(w, &keyRelease);
}

下面是输入成功的图片:

Qt QInputContext_第2张图片


转载自:http://xl028.blog.163.com/blog/static/19973024220127276110749/

你可能感兴趣的:(C++,Qt)