QT4 虚拟软键盘

目录

一、前言

二、效果展示

三、源码简析

1. 虚拟键盘类

2. 使用方法类

四、源码/Demo


一、前言

本例主要实现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目录下,否则无法检索到中文。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值