目录
一、前言
本例主要实现QT4下的虚拟软键盘功能,使用了中文sqlite库,具有正常英文/中文/数字字符输入,支持一定的多中文拼写能力,支持自定义界面、移动虚拟键盘等功能。
二、效果展示
三、源码简析
源码分为两部分,一部分是虚拟键盘的相关交互类,另一部分是键盘的使用部分。
1. 虚拟键盘类
①先讲讲ui,ui的基础框架是在CVirtualKeyBoard.ui里设置的,样式是在keyboard.qss里设置,在CVirtualKeyBoard.cpp引用了keyboard.qss;
②CVirtualKeyBoard类是ui的交互类
CVirtualKeyBoard.h
#ifndef CVIRTUALKEYBOARD1_H
#define CVIRTUALKEYBOARD1_H
#include <QWidget>
#include <QMap>
#include <QSqlDatabase>
//最多显示6个待选汉字
#define MAX_VISIBLE (6)
class CVirtualKeyBoard_Btn;
namespace Ui {
class CVirtualKeyBoard;
}
class CVirtualKeyBoard : public QWidget
{
Q_OBJECT
public:
explicit CVirtualKeyBoard(QWidget *parent = 0);
~CVirtualKeyBoard();
void setAutoShow(QObject* widget);//eg. setAutoShow(new CLineEdit(this));//控件必须是自定义的
void setAutoHide(bool autoHide);//是否设置为Popup模式,默认有false,Popup有些地方有些问题,没去弄
protected:
void showEvent(QShowEvent *event);
void hideEvent(QHideEvent *event);
//拖拽窗口
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
void init(); //总初始化
void initBtn(); //初始化按键
void initDatabase(); //初始化数据库等
void clearBuffer(); //清除拼音、等缓存
void showChinese(const QString &skey, const QMap<int, QStringList> &mapChineseList); //第一栏的中文
QMap<int, QStringList> getChineseListMap(const QString &sCurText);//获取汉字列表
void switchLetterStatus();//切换大小写
void setSymbolPage(int num);//切换不同符号页
public slots:
void show();
private slots:
void stRecKeyClicked(const QString &str); //处理所有虚拟键盘的按钮
void stSelectChinese(const QString &str); //第一栏待选择的汉字
void stShowKeyBoard(const QString &curText, const QString &recObjName); //获取控件的信息,并显示虚拟键盘
signals:
void sgChangedText(const QString &text, const QString &objName);//表示当前外部输入源文本已经被改变
private:
enum
{
Is_ch_mode,
Is_capital_mode
};
//用于每行的按钮
enum
{
row_1,
row_2,
row_3,
row_4,
row_5
};
//不同模式下按钮的显示文本,0代表没切换符号时的样子,1,2,3代表不同符号页面,
//需要增加新符号则道init2()中去添加,
enum
{
en0,
en1,
en2,
ch0,
ch1,
ch2
};
QMap<int, QList<CVirtualKeyBoard_Btn*> > m_mapButtonList; //按键的信息
QMap<int, bool> m_mapModeFlag; //大小写的判断
QMap<int, QStringList> m_mapSymbolList; //存取所以按键的text
QSqlDatabase* m_pDatabase; //数据库
QString m_sCurPyText; //第一栏当前的中文text
int m_lCurPyPage; //第一栏中文的页数
QString m_sLastPy; //上一次保存的可寻找到汉字的拼音组合
QString m_sLastPyText; //用于确定退格按钮
QString m_sCurOutsideText; //第一栏直接输出当前的text
QString m_sObjName; //要输入字符的控件的名称
int m_lSymbalPage; //目前是中文还是英文的页面
//键盘拖动相关对象
bool m_bDrag;
QPoint m_mouseStartPoint;
QPoint m_windowTopLeftPoint;
private:
Ui::CVirtualKeyBoard *ui;
};
//#define g_VirtualKeyBoard (CVirtualKeyBoard::getInstance())
#endif // CVIRTUALKEYBOARD1_H
CVirtualKeyBoard.cpp
#include "CVirtualKeyBoard.h"
#include "ui_CVirtualKeyBoard.h"
#include "CVirtualKeyBoard_Btn.h"
#include <QFile>
#include <QDebug>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QString>
#include <QStringList>
#include <QMap>
#include <QList>
CVirtualKeyBoard::CVirtualKeyBoard(QWidget *parent) :
QWidget(parent),
ui(new Ui::CVirtualKeyBoard),m_lSymbalPage(en0)
{
//虚拟键盘的样式
QFile qss(":/keyboard.qss");
if(!qss.open(QFile::ReadOnly))
{
qDebug()<<"Error: void setStyleQss()"<<qss.errorString();
}
this->setStyleSheet(qss.readAll());
qss.close();
ui->setupUi(this);
init();
}
CVirtualKeyBoard::~CVirtualKeyBoard()
{
delete ui;
}
void CVirtualKeyBoard::init()
{
initBtn();
initDatabase();
}
void CVirtualKeyBoard::initBtn()
{
m_mapButtonList.clear();
m_mapModeFlag[Is_ch_mode]=false;
m_mapModeFlag[Is_capital_mode]=false;
//添加每行的按钮
QList<CVirtualKeyBoard_Btn*> btnList;
//汉字显示行
btnList <<(ui->btn0_left)
<<(ui->btn0_02)
<<(ui->btn0_03)
<<(ui->btn0_04)
<<(ui->btn0_05)
<<(ui->btn0_06)
<<(ui->btn0_07)
<<(ui->btn0_08)
<<(ui->btn0_09)
<<(ui->btn0_right);
m_mapButtonList[row_1] = btnList;
btnList.clear();
//键盘第一行
btnList <<(ui->btn1_01)
<<(ui->btn1_02)
<<(ui->btn1_03)
<<(ui->btn1_04)
<<(ui->btn1_05)
<<(ui->btn1_06)
<<(ui->btn1_07)
<<(ui->btn1_08)
<<(ui->btn1_09)
<<(ui->btn1_10)
<<(ui->btn1_backspace);
m_mapButtonList[row_2] = btnList;
btnList.clear();
//键盘第二行
btnList <<(ui->btn2_01)
<<(ui->btn2_02)
<<(ui->btn2_03)
<<(ui->btn2_04)
<<(ui->btn2_05)
<<(ui->btn2_06)
<<(ui->btn2_07)
<<(ui->btn2_08)
<<(ui->btn2_09)
<<(ui->btn2_return);
m_mapButtonList[row_3] = btnList;
btnList.clear();
//键盘第三行
btnList <<(ui->btn3_Lshirt)
<<(ui->btn3_02)
<<(ui->btn3_03)
<<(ui->btn3_04)
<<(ui->btn3_05)
<<(ui->btn3_06)
<<(ui->btn3_07)
<<(ui->btn3_08)
<<(ui->btn3_09)
<<(ui->btn3_10)
<<(ui->btn3_Rshirt);
m_mapButtonList[row_4] = btnList;
btnList.clear();
//键盘第四行
btnList <<(ui->btn4_symbol)
<<(ui->btn4_switch)
<<(ui->btn4_space)
<<(ui->btn4_01)
<<(ui->btn4_hide);
m_mapButtonList[row_5] = btnList;
btnList.clear();
//信号和槽
foreach(int key, m_mapButtonList.keys())
{
QList<CVirtualKeyBoard_Btn*> list(m_mapButtonList[key]);
for(int i = 0; i<list.size(); i++)
{
if(key==row_1 && i>0 && i<list.size()-1)//待选汉字
{
connect(list.at(i), SIGNAL(clicked(QString)), this, SLOT(stSelectChinese(QString)));
}
else
{
connect(list.at(i), SIGNAL(clicked(QString)), this, SLOT(stRecKeyClicked(QString)));
}
}
}
}
void CVirtualKeyBoard::initDatabase()
{
qDebug()<<QSqlDatabase::drivers();
m_pDatabase = new QSqlDatabase;
//添加数据库驱动
if(QSqlDatabase::contains("my_sqlite"))
{
*m_pDatabase = QSqlDatabase::database("my_sqlite");
}
else
{
*m_pDatabase = QSqlDatabase::addDatabase("QSQLITE", "my_sqlite");
}
//设置数据库名称/指定数据库
m_pDatabase->setDatabaseName(QCoreApplication::applicationDirPath()+"/ChinesePY.db");
//打开数据库
if(!m_pDatabase->open())
{
exit(-1);
}
设置汉字显示个数
QList<CVirtualKeyBoard_Btn*> list = m_mapButtonList[row_1];
for(int i = MAX_VISIBLE+1; i < list.size()-1; i++)//"<<" ">>"两个按钮
{
list.at(i)->hide();
}
clearBuffer();
//设置按键的值
QStringList sEn0List;
sEn0List <<"q"<<"w"<<"e"<<"r"<<"t"<<"y"<<"u"<<"i"<<"o"<<"p"<<"BS"
<<"a"<<"s"<<"d"<<"f"<<"g"<<"h"<<"j"<<"k"<<"l"<<"ok"//return -- OK
<<"Ls"<<"z"<<"x"<<"c"<<"v"<<"b"<<"n"<<"m"<<","<<"."<<"Rs"//Ls:Lshift, Rs:RLshift
<<"?123"<<"sw"<<"English"<<":"<<"hi";//sw:switch hi:hide
QStringList sEn1List;
sEn1List <<"1"<<"2"<<"3"<<"4"<<"5"<<"6"<<"7"<<"8"<<"9"<<"0"<<"BS"
<<"."<<"@"<<"~"<<"-"<<","<<":"<<"*"<<"?"<<"!"<<"_"
<<"Le"<<"#"<<"/"<<"="<<"+"<<"﹉"<<"&&"<<"^"<<";"<<"%"<<"Ri"
<<"ABC"<<"sw"<<"English"<<","<<"hi";//sp:space
QStringList sEn2List;
sEn2List <<"1"<<"2"<<"3"<<"4"<<"5"<<"6"<<"7"<<"8"<<"9"<<"0"<<"BS"
<<"…"<<"$"<<"("<<")"<<".."<<"<"<<">"<<"|"<<"."<<"¥"
<<"Le"<<"["<<"]"<<"\""<<"{"<<"}"<<"–"<<"'"<<"€"<<"\\"<<"Ri" //Le:left Ri:Right
<<"ABC"<<"sw"<<"English"<<"."<<"hi";
QStringList sCh0List;
sCh0List <<"q"<<"w"<<"e"<<"r"<<"t"<<"y"<<"u"<<"i"<<"o"<<"p"<<"BS"
<<"a"<<"s"<<"d"<<"f"<<"g"<<"h"<<"j"<<"k"<<"l"<<"ok"
<<"Ls"<<"z"<<"x"<<"c"<<"v"<<"b"<<"n"<<"m"<<","<<"。"<<"Rs"
<<"?123"<<"sw"<<"Chinese"<<"/"<<"hi";
QStringList sCh1List;
sCh1List <<"1"<<"2"<<"3"<<"4"<<"5"<<"6"<<"7"<<"8"<<"9"<<"0"<<"BS"
<<","<<"。"<<"!"<<"~"<<"、"<<":"<<"#"<<";"<<"%"<<"*"
<<"Le"<<"——"<<"……"<<"&"<<"·"<<"$"<<"("<<")"<<"‘"<<"’"<<"Ri"
<<"ABC"<<"sw"<<"Chinese"<<"‖"<<"hi";
QStringList sCh2List;
sCh2List <<"1"<<"2"<<"3"<<"4"<<"5"<<"6"<<"7"<<"8"<<"9"<<"0"<<"BS"
<<"“"<<"”"<<"["<<"]"<<"『"<<"』"<<"〔"<<"〕"<<"{"<<"}"
<<"Le"<<"【"<<"】"<<"£"<<"〖"<<"〗"<<"《"<<"》"<<"「"<<"」"<<"Ri"
<<"ABC"<<"sw"<<"Chinese"<<"¥"<<"hi";
m_mapSymbolList[en0]=sEn0List;
m_mapSymbolList[en1]=sEn1List;
m_mapSymbolList[en2]=sEn2List;
m_mapSymbolList[ch0]=sCh0List;
m_mapSymbolList[ch1]=sCh1List;
m_mapSymbolList[ch2]=sCh2List;
setAutoHide(false);
}
void CVirtualKeyBoard::clearBuffer()
{
m_sCurPyText.clear();
m_lCurPyPage = -1;
ui->lb_display_text->setText("");
m_sLastPy = "";
m_sLastPyText = "";
showChinese(">>", getChineseListMap(m_sCurPyText));
}
void CVirtualKeyBoard::showChinese(const QString &skey, const QMap<int, QStringList> &mapChineseList)
{
QStringList chlist;
if(mapChineseList.isEmpty())
{
}
else
{
int mapSize = mapChineseList.size();
if(skey == "<<" && m_lCurPyPage>0)
{
m_lCurPyPage--;
}
else if(skey == ">>" && (m_lCurPyPage<mapSize-1))
{
m_lCurPyPage++;
}
chlist = mapChineseList[m_lCurPyPage];
}
QList<CVirtualKeyBoard_Btn*> list(m_mapButtonList[row_1]);
for(int i = 0; (i<list.size()-2)|| i<MAX_VISIBLE; i++)//除去左右切换两个按钮
{
list.at(i+1)->setText("");
if(i<chlist.size())
{
list.at(i+1)->setText(chlist.at(i));
}
}
}
QMap<int, QStringList> CVirtualKeyBoard::getChineseListMap(const QString &sCurText)
{
QMap<int, QStringList> mapList;
int page = 0;
int index = -1;//计数是否达到最大显示数
if(sCurText.isEmpty())
{
return mapList;
}
if(QSqlDatabase::contains("my_sqlite"))
{
*m_pDatabase = QSqlDatabase::database("my_sqlite");
}
else
{
*m_pDatabase = QSqlDatabase::addDatabase("QSQLITE", "my_sqlite");
}
//打开数据库
if(!m_pDatabase->open())
{
return mapList;
}
QString strPy(sCurText);
QSqlQuery query(*m_pDatabase);
int count = 0;
if( sCurText.size() < m_sLastPyText.size() && !m_sLastPy.isEmpty())//按了退格键
{
m_sLastPy.remove(m_sLastPy.size()-3,1);
}
while(1)//根据拼音获取对因汉字/词组
{
const QString command = "select PinYin, Chinese from WordAndGroup where PinYin like ""'"+strPy+"%;'";
if(!query.exec(command))
{
goto error;
}
while(query.next())
{
if(index++ < MAX_VISIBLE-1)
{
mapList[page].append(query.value(1).toString());
}
else
{
page++;
index = -1;
}
}
if(mapList[page].size()>0)
{
m_sLastPy = strPy+"%,";
break;
}
else
{
QString str = m_sLastPy;
str.remove(QRegExp("%,"));
if(++count == 1)//当查询不匹配时,将新加入的词汇当作新词的开头(eg1:guoj--> guo%,j eg2:guoji -->guo%,j%,i)
{
strPy = m_sLastPy+sCurText.mid(str.size());
}
else if(count == 2)//若之前可以匹配现在不行,则将新加入的字母当作新词的一部分拼音(eg1:guoj-->guo%,j eg2:guoji -->guo%ji)
{
QString str2 = m_sLastPy;
str2.remove(str2.lastIndexOf("%,"), 2);
strPy=str2+sCurText.mid(str.size());
}
else if(count == 3)//将"m_sLastPy"最后3个字符替换成"%,",并补充当前拼音"arg1"尚未补充在"py"上的内容
{
QString str2 = m_sLastPy;
str2.remove(str2.lastIndexOf("%,"), str2.size()-str2.lastIndexOf("%,"));
str2.insert(str2.size()-1, "%,");
strPy = str2 + sCurText.mid(str.size());
}
else
{
break;
}
}
}
error:
m_pDatabase->close();
m_sLastPyText=sCurText;
return mapList;
}
void CVirtualKeyBoard::switchLetterStatus()
{
foreach(int key, m_mapButtonList.keys())
{
QList<CVirtualKeyBoard_Btn*> list(m_mapButtonList[key]);
int i = 0;
int listsize = list.size();
if(key == row_2)//除去删除按钮
{
listsize--;
}
else if(key == row_3)//除去确定按钮
{
listsize--;
}
else if(key == row_4)//除去两个shift及两个符号按钮
{
i++;
listsize -= 3;
}
else if(key == row_5)
{
break;
}
for(; i<listsize; i++)
{
if(m_mapModeFlag[Is_capital_mode])//如果原先是大写
{
list.at(i)->setText(list.at(i)->text().toLower());
}
else
{
list.at(i)->setText(list.at(i)->text().toUpper());
}
}
}
m_mapModeFlag[Is_capital_mode] = !m_mapModeFlag[Is_capital_mode];
}
void CVirtualKeyBoard::setSymbolPage(int num)
{
int count = 0;
foreach(int key, m_mapButtonList.keys())
{
QList<CVirtualKeyBoard_Btn*> list(m_mapButtonList[key]);
for(int i=0; i<list.size(); i++)
{
if(key == row_1)
{
continue;
}
list.at(i)->setText(m_mapSymbolList[num].at(count++));//en0例如
}
}
}
void CVirtualKeyBoard::show()
{
QWidget::show();
}
void CVirtualKeyBoard::stRecKeyClicked(const QString &str)
{
qDebug() << str;
if(str.size()==1)
{
if( m_mapModeFlag[Is_ch_mode] && (str.contains(QRegExp("([a-z])")) || (!m_sCurPyText.isEmpty())) )//正常输入拼音
{
m_sCurPyText+=str;
ui->lb_display_text->setText(m_sCurPyText);
m_lCurPyPage=-1;
showChinese(">>", getChineseListMap(m_sCurPyText));
}
else//直接输出
{
m_sCurOutsideText += str;
sgChangedText(m_sCurOutsideText, m_sObjName);
clearBuffer();
}
}
else//接收的是命令
{
if(str=="&&")//&为QT快捷键
{
return stRecKeyClicked("&");
}
else if(str=="sw")//中英文切换
{
m_mapModeFlag[Is_ch_mode] = !m_mapModeFlag[Is_ch_mode];
if(m_mapModeFlag[Is_ch_mode])
{
m_lSymbalPage=ch0;
}
else
{
m_lSymbalPage=en0;
}
setSymbolPage(m_lSymbalPage);
clearBuffer();
}
else if ((str=="<<" || str==">>") && m_mapModeFlag[Is_ch_mode])//待选汉字切换
{
showChinese(str, getChineseListMap(m_sCurPyText));
}
else if(str=="BS")//退格按钮
{
if(!m_sCurPyText.isEmpty())//若处于中文输入状态
{
m_sCurPyText.remove(m_sCurPyText.size()-1, 1);
ui->lb_display_text->setText(m_sCurPyText);
showChinese(">>", getChineseListMap(m_sCurPyText));
}else
{
m_sCurOutsideText.remove(m_sCurOutsideText.size()-1, 1);
sgChangedText(m_sCurOutsideText, m_sObjName);
}
if(m_sCurPyText.isEmpty())
{
clearBuffer();
}
}
else if(str == "English" || str == "Chinese")//空格按钮
{
if(!m_sCurPyText.isEmpty())//若处于中文输入状态
{
m_sCurPyText.append(" ");
ui->lb_display_text->setText(m_sCurPyText);
showChinese(">>", getChineseListMap(m_sCurPyText));
}
else
{
m_sCurOutsideText.append(" ");
sgChangedText(m_sCurOutsideText, m_sObjName);
clearBuffer();
}
}
else if(str=="Ls"||str=="Rs")//大小写切换
{
switchLetterStatus();
if(m_mapModeFlag[Is_ch_mode])
{
clearBuffer();
}
}
else if(str=="Le")
{
if( m_mapModeFlag[Is_ch_mode]&&(m_lSymbalPage>ch1) )
{
m_lSymbalPage--;
setSymbolPage(m_lSymbalPage);
}
else if ( (!m_mapModeFlag[Is_ch_mode])&&(m_lSymbalPage>en1) )
{
m_lSymbalPage--;
setSymbolPage(m_lSymbalPage);
}
}
else if(str=="Ri")
{
if( m_mapModeFlag[Is_ch_mode]&&(m_lSymbalPage<ch2) )
{
m_lSymbalPage++;
setSymbolPage(m_lSymbalPage);
}
else if ( (!m_mapModeFlag[Is_ch_mode])&&(m_lSymbalPage<en2) )
{
m_lSymbalPage++;
setSymbolPage(m_lSymbalPage);
}
}
else if(str=="?123" || str=="ABC")
{
stRecKeyClicked("ok");//除去拼音输入时遗留下来的拼音
if(str=="?123")
{
return stRecKeyClicked("Ri");
}
else if(str=="ABC")
{
m_mapModeFlag[Is_ch_mode] = !m_mapModeFlag[Is_ch_mode];
return stRecKeyClicked("sw");
}
}
else if(str == "ok")
{
if(!m_sCurPyText.isEmpty())
{
m_sCurOutsideText.append(m_sCurPyText);
sgChangedText(m_sCurOutsideText, m_sObjName);
clearBuffer();
}
}
else if(str == "hi")
{
this->hide();
}
}
}
void CVirtualKeyBoard::stSelectChinese(const QString &str)
{
m_sCurOutsideText.append(str);
sgChangedText(m_sCurOutsideText, m_sObjName);
clearBuffer();
}
void CVirtualKeyBoard::showEvent(QShowEvent *event)
{
setSymbolPage(en0);
return QWidget::showEvent(event);
}
void CVirtualKeyBoard::hideEvent(QHideEvent *event)
{
m_sCurOutsideText="";
m_mapModeFlag[Is_ch_mode]=false;
clearBuffer();
return QWidget::hideEvent(event);
}
void CVirtualKeyBoard::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
m_bDrag = true;
//获得鼠标的初始位置
m_mouseStartPoint = event->globalPos();
//获得窗口的初始位置
m_windowTopLeftPoint = this->frameGeometry().topLeft();
}
}
void CVirtualKeyBoard::mouseMoveEvent(QMouseEvent *event)
{
if(m_bDrag)
{
//获得鼠标移动的距离
QPoint distance = event->globalPos() - m_mouseStartPoint;
//改变窗口的位置
this->move(m_windowTopLeftPoint + distance);
}
}
void CVirtualKeyBoard::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
m_bDrag = false;
}
}
void CVirtualKeyBoard::setAutoHide(bool autoHide)
{
if(autoHide)
{
this->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
}
else
{
this->setWindowFlags(Qt::FramelessWindowHint);
}
}
void CVirtualKeyBoard::setAutoShow(QObject *widget)
{
if(widget == NULL)
{
qDebug() << "error: widget is null";
return;
}
//虚拟键盘与自定义控件的交互
connect(widget, SIGNAL(sgCurText(QString, QString)), this, SLOT(stShowKeyBoard(QString, QString)));
connect(this, SIGNAL(sgChangedText(QString, QString)), widget, SLOT(stSetText(QString, QString)));
}
void CVirtualKeyBoard::stShowKeyBoard(const QString &curText, const QString &recObjName)
{
m_sObjName = recObjName;
m_sCurOutsideText = curText;
QWidget::show();
}
③CVirtualKeyBoard_Btn类是ui里的button交互类,ui里的button继承CVirtualKeyBoard_Btn,定义了button的交互操作
CVirtualKeyBoard_Btn.h
#ifndef CVIRTUALKEYBOARD_BTN_H
#define CVIRTUALKEYBOARD_BTN_H
#include <QPushButton>
#include <QTimer>
#include <QMouseEvent>
class CVirtualKeyBoard_Btn : public QPushButton
{
Q_OBJECT
public:
CVirtualKeyBoard_Btn(QWidget *parent = 0);
~CVirtualKeyBoard_Btn();
void setText(const QString &text);
protected:
//按压事件
void mousePressEvent ( QMouseEvent * event );
void mouseReleaseEvent ( QMouseEvent * event );
private:
QTimer* m_pTimerClick; //用来限制点击的速度
QTimer* m_pTimerPressOn; //用来计时点击
bool m_bCanClick; //是否能再次点击
bool m_bIsPressOn; //是否按压
QPoint m_point;
signals:
void clicked(const QString&);
private slots:
void stUpdatePressStatus(); //限制点击速度
void stPressOnSlot();
};
#endif // CVIRTUALKEYBOARD_BTN_H
CVirtualKeyBoard_Btn.cpp
#include "CVirtualKeyBoard_Btn.h"
#include "CVirtualKeyBoard_ProxyStyle.h"
CVirtualKeyBoard_Btn::CVirtualKeyBoard_Btn(QWidget *parent)
: QPushButton(parent), m_bCanClick(true), m_bIsPressOn(false)
{
m_pTimerClick = new QTimer(this);
m_pTimerPressOn = new QTimer(this);
connect(m_pTimerClick, SIGNAL(timeout()), this, SLOT(stUpdatePressStatus()));
connect(m_pTimerPressOn, SIGNAL(timeout()), this, SLOT(stPressOnSlot()));
this->setStyle(new CVirtualKeyBoard_ProxyStyle);
this->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
}
CVirtualKeyBoard_Btn::~CVirtualKeyBoard_Btn()
{
if(m_pTimerPressOn != NULL)
{
if(m_pTimerPressOn->isActive())
{
m_pTimerPressOn->stop();
delete m_pTimerPressOn;
m_pTimerPressOn = NULL;
}
}
if(m_pTimerClick != NULL)
{
if(m_pTimerClick->isActive())
{
m_pTimerClick->stop();
delete m_pTimerClick;
m_pTimerClick = NULL;
}
}
}
void CVirtualKeyBoard_Btn::setText(const QString &text)
{
QPushButton::setText(text);
this->style()->unpolish(this);
this->style()->polish(this);
this->update();
}
void CVirtualKeyBoard_Btn::mousePressEvent(QMouseEvent *event)
{
m_pTimerPressOn->start(100); //开始计时,一直按压,0.1s输入一次
return QPushButton::mousePressEvent(event);
}
void CVirtualKeyBoard_Btn::mouseReleaseEvent(QMouseEvent *event)
{
//停止按压计时
m_bIsPressOn=false;
if(m_pTimerPressOn->isActive())
{
m_pTimerPressOn->stop();
}
m_point = event->pos();
if(this->rect().contains(m_point))
{
if(m_bCanClick)
{
m_bCanClick = false;
m_pTimerClick->start(100);//限制连续点击速度
emit clicked(this->text());
}
}
QPushButton::mouseReleaseEvent(event);
}
void CVirtualKeyBoard_Btn::stUpdatePressStatus()
{
m_bCanClick = true;
m_pTimerClick->stop();
}
void CVirtualKeyBoard_Btn::stPressOnSlot()
{
//连续点击
if(m_bIsPressOn)
{
emit clicked(this->text());
}
m_bIsPressOn=true;
}
④CVirtualKeyBoard_Label类是ui里的label交互类
CVirtualKeyBoard_Label.h
#ifndef CVIRTUALKEYBOARD_LABEL_H
#define CVIRTUALKEYBOARD_LABEL_H
#include <QLabel>
class CVirtualKeyBoard_Label : public QLabel
{
public:
explicit CVirtualKeyBoard_Label(QWidget *parent = 0);
void setText(const QString &text);
public slots:
void clear();
};
#endif // CVIRTUALKEYBOARD_LABEL_H
CVirtualKeyBoard_Label.cpp
#include "CVirtualKeyBoard_Label.h"
#include <QStyle>
CVirtualKeyBoard_Label::CVirtualKeyBoard_Label(QWidget *parent) : QLabel(parent)
{
}
void CVirtualKeyBoard_Label::setText(const QString &text)
{
QLabel::setText(text);
this->style()->unpolish(this);
this->style()->polish(this);
this->update();
}
void CVirtualKeyBoard_Label::clear()
{
QLabel::setText("");
this->style()->unpolish(this);
this->style()->polish(this);
this->update();
}
⑤CVirtualKeyBoard_ProxyStyle类是一些样式
CVirtualKeyBoard_ProxyStyle.h
#ifndef CVIRTUALKEYBOARD_PROXYSTYLE_H
#define CVIRTUALKEYBOARD_PROXYSTYLE_H
#include <QProxyStyle>
#include <QWidget>
class CVirtualKeyBoard_ProxyStyle : public QProxyStyle
{
public:
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption * option,
QPainter * painter, const QWidget * widget = 0) const;
virtual int pixelMetric(PixelMetric metric, const QStyleOption* option = 0, const QWidget* widget = 0) const;
};
#endif // CVIRTUALKEYBOARD_PROXYSTYLE_H
CVirtualKeyBoard_ProxyStyle.cpp
#include "CVirtualKeyBoard_ProxyStyle.h"
void CVirtualKeyBoard_ProxyStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
if (PE_FrameFocusRect == element)//当焦点集中时的策略
{
//这里不做任何操作,Qt默认是绘制矩形虚线框,以及焦点时的content背景色
}
else
{
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
int CVirtualKeyBoard_ProxyStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
{
if (metric == QStyle::PM_TextCursorWidth)//lineEdit光标不绘制
{
return 0;
}
return QProxyStyle::pixelMetric(metric, option, widget);
}
⑥CVirtualKeyBoardResource.qrc包含了样式图片/qss等资源
2. 使用方法类
本虚拟键盘需要自定义控件与其交互,才能使用,例如有一个LineEdit,需要重写其mousePressEvent等函数
CLineEdit.h
#ifndef CLINEEDIT_H
#define CLINEEDIT_H
#include<QLineEdit>
class CLineEdit:public QLineEdit
{
Q_OBJECT
public:
CLineEdit(QWidget *parent = 0);
protected:
void mousePressEvent(QMouseEvent *e);
signals:
void sgCurText(const QString &curText, const QString &curObjName);
public slots:
void stSetText(const QString& text, const QString &ObjName);
};
#endif // CLINEEDIT_H
CLineEdit.cpp
#include "CLineEdit.h"
#include <QMouseEvent>
CLineEdit::CLineEdit(QWidget *parent) :
QLineEdit(parent)
{
}
void CLineEdit::mousePressEvent(QMouseEvent *e)
{
sgCurText(this->text(), this->objectName());
return QLineEdit::mousePressEvent(e);
}
void CLineEdit::stSetText(const QString &text, const QString &ObjName)
{
if(ObjName==this->objectName())
{
this->setText(text);
}
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "./CVirtualKeyBoard.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pCVirtualKeyBoard = new CVirtualKeyBoard(this);
//定义控件大小,高度:宽度2:1好看一点,目前在底下
m_pCVirtualKeyBoard->setGeometry(this->width()/2-400,this->height()-400,800,400);
m_pCVirtualKeyBoard->hide();
//自定义控件与键盘的交互
m_pCVirtualKeyBoard->setAutoShow(ui->lineEdit);
m_pCVirtualKeyBoard->setAutoShow(ui->lineEdit_2);
}
MainWindow::~MainWindow()
{
if(m_pCVirtualKeyBoard != NULL)
{
delete m_pCVirtualKeyBoard;
m_pCVirtualKeyBoard = NULL;
}
delete ui;
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
m_pCVirtualKeyBoard->hide();
}
main.cpp
#include <QApplication>
#include <QTextCodec>
#include <QFontDatabase>
#include <QStringList>
#include "mainwindow.h"
void setFontPath(const QString &sPath, QApplication &app)
{
//解决qDebug中文乱码
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
int index = QFontDatabase::addApplicationFont(sPath);//添加字体文件(eg. ":/font.ttf" )
if(index != -1)
{
QStringList fontList(QFontDatabase::applicationFontFamilies(index));
// font
if(fontList.count() > 0)
{
QFont font_zh(fontList.at(0));
font_zh.setBold(false);
app.setFont(font_zh);
}
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
setFontPath(":/font.ttf", a);//解决中文乱码
w.show();
return a.exec();
}
四、源码/Demo
在本文CSDN下载链接中
注意:编译完成后,需把ChinesePY.db中文库,放入exe目录下,否则无法检索到中文。