前面谈及了虚拟键盘的设计方法,其设计内容及思路如下:
(1) 新建对话框类,在该对话框中有文本框若干、虚拟输入键盘一个。
(2) 在对话框类中定义虚拟键盘子对象、用于文本输入响应的槽函数;定义事件过滤器,安装在对话框上,当对话框接收到event事件时,如果需要则接收(例如用于文本框上下切换的两个按键),如果不需要则转发给键盘(例如用于键盘左右移动的两个按键,用于选择字符的确认键),如此进行事件分发。
(3) 当用户按下确认键选择字符时,会发送一个clicked信号,此信号经过signalMapper转发,将按键上的文本信息传送到(2)中所述的槽函数中,由此设定文本框的输入值。
上述设计使用了 QSignalMapper类,这个类还是很好用的,但是存在一个缺点:按键类使用在新的对话框时,每次都要在新的对话框类中,定义一个 eventFilter,重写 keyPressEvent,很麻烦。下面的改进办法就很好用,该设计方法将按键统一放在VirtualKeyboard类中处理,且在VirtualKeyboard类中定义eventFilter,这样模块复用时就更加简单。
bool VirtualKeyboard::eventFilter(QObject *obj, QEvent *event)
{
int forwardKey = 0;
Qt::KeyboardModifier modifier = Qt::NoModifier; // 用于焦点切换时,定义是否按下了shift键
QString text;
if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch (keyEvent->key())
{
case GENERAL_PURPOSE_KNOB:
QApplication::sendEvent(this, event); // 旋动旋钮,则直接交给VirtualKeyboard的keypressEvent来处理
return true;
case GENERAL_PURPOSE_BUTTON:
forwardKey = button.at(currentChoice)->text().at(0).toLatin1();
text = button.at(currentChoice)->text(); // 取按键text,用于sendEvent
break;
case KEY_UP:
forwardKey = Qt::Key_Tab;
break;
case KEY_DOWN:
forwardKey = Qt::Key_Tab;
modifier = Qt::ShiftModifier; // shift键按下
break;
case KEY_LEFT_FORWARD:
forwardKey = Qt::Key_Left;
break;
case KEY_RIGHT_FORWARD:
forwardKey = Qt::Key_Right;
break;
case KEY_BACKSPACE:
forwardKey = Qt::Key_Backspace;
break;
default:
break;
}
}
if (forwardKey != 0)
{
QWidget *widget = QApplication::focusWidget();
if (widget)
{
QKeyEvent e(event->type(), forwardKey, modifier, text); // 定义keyevent事件
QApplication::sendEvent(widget, &e); // 发送给有焦点的QLineEdit,QLineEdit会自行处理这些事件
return true;
}
}
return QObject::eventFilter(obj, event);
}
这样用不得不说非常妙,因为QLineEdit可以自行处理上述的按键事件,就用不着再写槽函数了,且焦点可以自由的上下切换,对话框类也无需定义keypressEvent和eventFilter,该类的复用就非常方便。