想在一个QT应用中用软键盘进行输入,于是要先研究下软键盘输入原理,幸运的是Qt4.7.0库中带有一个InputPanel的示例程序,怀着激动的心情先打开运行一遍,但发现怎么点也输入不了,于是开始仔细阅读程序和帮助,想搞清楚软键盘输入的原理以及这里是否存在bug,如果有bug那它又在哪里,怎么修改等等。
现在解释程序代码如下:
myinputpanel.h
#ifndef MYINPUTPANEL_H
#define MYINPUTPANEL_H
#include <QtGui>
#include <QtCore>
#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 <QtGui/QInputContext>
#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 <QtGui/QApplication>
#include <QtGui/QWidget>
//! [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<QChar>(w->property("buttonValue"));
emit characterGenerated(chr);//发射出去
}
//! [3]
myinputpanel contetx.cpp
#include <QtCore>
#include "myinputpanelcontext.h"
//#include <QDebug>
//! [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<QWidget> 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 <QtGui>
#include <QtCore>
#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<QWidget> w = focusWidget();
QPointer<QWidget> 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);
}
下面是输入成功的图片:
转载自:http://xl028.blog.163.com/blog/static/19973024220127276110749/