QT自制软键盘 最完美、最简单、支持中文输入(二)

目录

一、前言

二、本自制虚拟键盘特点

三、中文输入原理

四、组合键输入

五、键盘事件模拟

六、界面

 七、代码

7.1 frmKeyBoard 头文件代码

7.2 frmKeyBoard 源文件代码

八、使用示例

九、效果

十、结语


一、前言

        由于系统自带虚拟键盘不一定好用,也不一定好看,有的按键太小,有的电脑上可能没有自带的软键盘,干脆直接自己设计一个。        

        在现代的用户界面设计中,屏幕键盘是一种重要的辅助工具,特别是在触摸屏设备上。本文将深入解析一个使用Qt框架自制的屏幕键盘,具有丰富的功能和用户友好的界面,支持中文输入、组合键操作等多种特性。

        之前写过一篇不带中文的屏幕键盘,本文在该基础上升级部分功能:QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样(一)

二、本自制虚拟键盘特点

1.支持中文输入。

2.支持组合键,例如“Ctrl+C”复制粘贴操作。

3.键盘界面保持在所有界面最上方。

4.点击键盘按钮不会改变底层文本输入框焦点。

5.通过模拟键盘点击事件完成键盘输入文本信息。

6.包含各种键盘自带符号输入。

7.长按按键可以持续重复输入键盘内容。

三、中文输入原理

        资源中包含了“PinYin_Chinese.txt”文本文件,文件内容为七千多个汉字以及对应的拼音,通过加载中文拼音数据,实现了基于拼音的中文输入。在用户输入拼音时,屏幕键盘匹配对应的汉字词组,并显示在候选列表中。

加载中文汉字代码:

void frmKeyBoard::loadChineseFontData()
{QFile pinyin(":/PinYin_Chinese.txt");if (!pinyin.open(QIODevice::ReadOnly)) {qDebug() << "Open pinyin file failed!";return;}while (!pinyin.atEnd()) {QString buf = QString::fromUtf8(pinyin.readLine()).trimmed();if (buf.isEmpty())continue;/* 去除#号后的注释内容 */if (buf.left(1) == "#")continue;buf=buf.replace("\t"," ");QString pinyin = buf.mid(1,buf.size() - 1);QString word = buf.mid(0,1);QString abb;QStringList pinyinList = pinyin.split(" ");for (int i = 0; i < pinyinList.count(); i++) {/* 获得拼音词组的首字母(用于缩写匹配) */abb += pinyinList.at(i).left(1);}QList<QPair<QString, QString>> &tmp = m_data[pinyin.left(1)];/* 将'拼音(缩写)'和'词组'写入匹配容器 */tmp.append(qMakePair(abb, word));/* 将'拼音(全拼)'和'词组'写入匹配容器 */tmp.append(qMakePair(pinyin.remove(" "), word));}qDebug() << m_data.size();pinyin.close();
}

点击列表选择汉字输入:

void frmKeyBoard::on_listWidget_itemClicked(QListWidgetItem *item)
{QKeyEvent keyPress(QEvent::KeyPress, 0, m_curModifier, item->text().right(1));QKeyEvent keyRelease(QEvent::KeyRelease, 0, m_curModifier, item->text().right(1));QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();
}

 另外,点击汉字对应的数字、点击空格都可以输入汉字,点击回车输入对应的英文字母,部分关键代码:


void frmKeyBoard::slotKeyButtonClicked()
{QPushButton* pbtn = (QPushButton*)sender();QString objectName = pbtn->objectName();if (pbtn->text().contains("Backspace")) {if(isChinese){if(recordLitterBuf.size() > 0){recordLitterBuf.remove(recordLitterBuf.size() - 1, 1);findChineseFontData(recordLitterBuf);if(!m_labelShowPinyin->text().isEmpty())m_labelShowPinyin->setText(recordLitterBuf);}}else{QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Backspace, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Backspace, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if(pbtn->text() == "Space") {if(isChinese){if(ui.listWidget->count() > 0){//按下空格输入列表第一汉字on_listWidget_itemClicked(ui.listWidget->item(0));}}else{QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Space, m_curModifier, " ");QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Space, m_curModifier, " ");QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if (pbtn->text().contains("Enter")) {        if(isChinese){if(!m_labelShowPinyin->text().isEmpty()){//按下回车输入拼音字母QKeyEvent keyPress(QEvent::KeyPress, 0, m_curModifier, m_labelShowPinyin->text());QKeyEvent keyRelease(QEvent::KeyRelease, 0,m_curModifier, m_labelShowPinyin->text());QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();}}else{QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Enter, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Enter, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if (pbtn->text().contains("Shift")) {if (pbtn->isChecked()) {isChinese = true;m_curModifier = Qt::ShiftModifier;for (auto pbtnKey : m_letterKeys) {pbtnKey->setText(pbtnKey->text().toUpper());}}else {isChinese = false;ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();m_curModifier = Qt::NoModifier;for (auto pbtnKey : m_letterKeys) {pbtnKey->setText(pbtnKey->text().toLower());}}QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Shift, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Shift, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}
//......else {QString symbol;if (ui.pushButton_shift->isChecked())symbol = pbtn->text().split("\n").at(0);elsesymbol = pbtn->text().split("\n").at(1);QKeyEvent keyPress(QEvent::KeyPress, m_mapSymbolKeys.value(symbol), m_curModifier, symbol);QKeyEvent keyRelease(QEvent::KeyRelease, m_mapSymbolKeys.value(symbol), m_curModifier, symbol);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}
}

四、组合键输入

       4.1 定义m_curModifier 记录当前键盘模式。

Qt::KeyboardModifier m_curModifier = Qt::NoModifier;

       4.2 特殊按键选中时切换对应的按键模式 

if (pbtn->text().contains("Ctrl")) {if(pbtn->isChecked())m_curModifier = Qt::ControlModifier;elsem_curModifier = Qt::NoModifier;QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Control, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Control, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}

五、键盘事件模拟

        通过模拟键盘事件,实现了对底层文本输入框的模拟输入。这样,用户可以在使用屏幕键盘的同时,保持对其他界面元素的操作。

QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Enter, m_curModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Enter, m_curModifier);
QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);
QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);

        另外,通过qApp->focusWidget()获取当前焦点所在的窗口,可以将键盘模拟输入到各个窗口的输入框中。

六、界面

界面新增了一个QListWidget用于显示候选汉字列表,列表方向从左至右。

 七、代码

7.1 frmKeyBoard 头文件代码

#pragma once
#pragma execution_character_set("utf-8")#include <QDialog>
#include "ui_frmKeyBoard.h"
#include <QPushButton>
#include <QKeyEvent>
#include <QDebug>
#include <QStyle>
#include <QListWidgetItem>
#include <QLabel>
#include <QVBoxLayout>
#include <QToolTip>class frmKeyBoard : public QDialog
{Q_OBJECTpublic:frmKeyBoard(QWidget *parent = nullptr);~frmKeyBoard();void initFocusWidget(QWidget*);private slots:void slotKeyButtonClicked();void slotKeyLetterButtonClicked();void slotKeyNumberButtonClicked();void on_listWidget_itemClicked(QListWidgetItem *item);void on_toolButton_lastPage_clicked();void on_toolButton_nextPage_clicked();private:Ui::frmKeyBoardClass ui;void initFrm();void initStyleSheet();void loadChineseFontData();void findChineseFontData(QString text);void addOneItem(QString text);QMap<QString, QList<QPair<QString, QString>>> m_data;bool isChinese = false;QString recordLitterBuf;QVector<QPushButton*> m_letterKeys;QVector<QPushButton*> m_NumberKeys;QMap<QString, Qt::Key> m_mapSymbolKeys;QStringList m_showTextList;int m_curPage = 0;QLabel* m_labelShowPinyin;Qt::KeyboardModifier m_curModifier = Qt::NoModifier;
};

7.2 frmKeyBoard 源文件代码

#include "frmKeyBoard.h"frmKeyBoard::frmKeyBoard(QWidget *parent): QDialog(parent)
{ui.setupUi(this);this->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus);this->setWindowTitle("屏幕键盘(源客V)");this->setWindowModality(Qt::WindowModal);this->setAttribute(Qt::WA_DeleteOnClose);this->initFrm();this->initStyleSheet();this->loadChineseFontData();
}frmKeyBoard::~frmKeyBoard()
{
}void frmKeyBoard::initFrm()
{m_letterKeys.clear();m_NumberKeys.clear();QList<QPushButton*> pbtns = this->findChildren<QPushButton*>();foreach(QPushButton * pbtn, pbtns) {pbtn->setAutoRepeat(true);    //允许自动重复pbtn->setAutoRepeatDelay(500);//设置重复操作的时延if (pbtn->text() >= 'a' && pbtn->text() <= 'z') {connect(pbtn, &QPushButton::clicked, this, &frmKeyBoard::slotKeyLetterButtonClicked);m_letterKeys.push_back(pbtn);}else if (pbtn->text().toInt() > 0 && pbtn->text().toInt() <= 9 || pbtn->text() == "0") {connect(pbtn, &QPushButton::clicked, this, &frmKeyBoard::slotKeyNumberButtonClicked);m_NumberKeys.push_back(pbtn);}else{connect(pbtn, &QPushButton::clicked, this, &frmKeyBoard::slotKeyButtonClicked);}}//添加label显示拼音m_labelShowPinyin = new QLabel();QVBoxLayout* vLayout = new QVBoxLayout();vLayout->addWidget(m_labelShowPinyin);vLayout->addStretch();ui.listWidget->setLayout(vLayout);m_mapSymbolKeys.insert("~", Qt::Key_AsciiTilde);m_mapSymbolKeys.insert("`", Qt::Key_nobreakspace);m_mapSymbolKeys.insert("-", Qt::Key_Minus);m_mapSymbolKeys.insert("_", Qt::Key_Underscore);m_mapSymbolKeys.insert("+", Qt::Key_Plus);m_mapSymbolKeys.insert("=", Qt::Key_Equal);m_mapSymbolKeys.insert(",", Qt::Key_Comma);m_mapSymbolKeys.insert(".", Qt::Key_Period);m_mapSymbolKeys.insert("/", Qt::Key_Slash);m_mapSymbolKeys.insert("<", Qt::Key_Less);m_mapSymbolKeys.insert(">", Qt::Key_Greater);m_mapSymbolKeys.insert("?", Qt::Key_Question);m_mapSymbolKeys.insert("[", Qt::Key_BracketLeft);m_mapSymbolKeys.insert("]", Qt::Key_BracketRight);m_mapSymbolKeys.insert("{", Qt::Key_BraceLeft);m_mapSymbolKeys.insert("}", Qt::Key_BraceRight);m_mapSymbolKeys.insert("|", Qt::Key_Bar);m_mapSymbolKeys.insert("\\", Qt::Key_Backslash);m_mapSymbolKeys.insert(":", Qt::Key_Colon);m_mapSymbolKeys.insert(";", Qt::Key_Semicolon);m_mapSymbolKeys.insert("\"", Qt::Key_QuoteLeft);m_mapSymbolKeys.insert("'", Qt::Key_Apostrophe);
}void frmKeyBoard::initStyleSheet()
{QString qss;qss += "QWidget{ background-color:rgb(26,26,26)}";    qss += "QToolButton{ color:white; font:bold 11pt;}";qss += "QLabel{ color:white; font:13pt;}";qss += "QListWidget{ color:white; border:none; padding-bottom:10px; }";qss += "QListWidget::item:hover,QListWidget::item:selected{font:bold; color:yellow; background-color:rgba(0,0,0,0)}";qss += "QPushButton{ color:white; background-color:rgb(51,51,51); height:50px; font-size:bold 15pt; border:1px solid rgb(26,26,26); border-radius: 0px; min-width:50px;}";qss += "QPushButton:hover{background-color:rgb(229,229,229); color:black;}";qss += "QPushButton:pressed,QPushButton:checked{background-color:rgb(0,118,215);}";qss += "#pushButton_closeKeyboard{background-color:rgba(0,0,0,0); border:0px}";qss += "#pushButton_closeKeyboard:hover{background-color:#b30220;}";qss += "#pushButton_space{min-width:500px;}";qss += "#pushButton_backspace,#pushButton_shift{min-width:100px;}";qss += "#pushButton_enter{min-width:120px;}";qss += "#pushButton_tab,#pushButton_ctrl{min-width:70px;}";qss += "#pushButton_capsLock{min-width:80px;}";qss += "#pushButton_up{min-width:150px;}";this->setStyleSheet(qss);
}void frmKeyBoard::loadChineseFontData()
{QFile pinyin(":/PinYin_Chinese.txt");if (!pinyin.open(QIODevice::ReadOnly)) {qDebug() << "Open pinyin file failed!";return;}while (!pinyin.atEnd()) {QString buf = QString::fromUtf8(pinyin.readLine()).trimmed();if (buf.isEmpty())continue;/* 去除#号后的注释内容 */if (buf.left(1) == "#")continue;buf=buf.replace("\t"," ");QString pinyin = buf.mid(1,buf.size() - 1);QString word = buf.mid(0,1);QString abb;QStringList pinyinList = pinyin.split(" ");for (int i = 0; i < pinyinList.count(); i++) {/* 获得拼音词组的首字母(用于缩写匹配) */abb += pinyinList.at(i).left(1);}QList<QPair<QString, QString>> &tmp = m_data[pinyin.left(1)];/* 将'拼音(缩写)'和'词组'写入匹配容器 */tmp.append(qMakePair(abb, word));/* 将'拼音(全拼)'和'词组'写入匹配容器 */tmp.append(qMakePair(pinyin.remove(" "), word));}qDebug() << m_data.size();pinyin.close();
}void frmKeyBoard::findChineseFontData(QString text)
{QString lowerText = text.toLower();m_labelShowPinyin->setText(lowerText);for (int i = 0; i < ui.listWidget->count(); i++) {QListWidgetItem *item = ui.listWidget->takeItem(i);delete item;item = NULL;}ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;if(lowerText.count() <= 0)return;const QList<QPair<QString, QString> > &tmp = m_data[lowerText.left(1)];bool fond = false;for (int i = 0; i < tmp.count(); i++) {const QPair<QString, QString> &each = tmp.at(i);if (each.first.left(lowerText.count()) != lowerText)continue;fond = true;addOneItem(each.second);m_showTextList.push_back(each.second);}if(!fond){if(recordLitterBuf.count() > 1){recordLitterBuf = recordLitterBuf.right(1);findChineseFontData(recordLitterBuf);}else{QKeyEvent keyPress(QEvent::KeyPress, int(recordLitterBuf.at(0).toLatin1()), m_curModifier, recordLitterBuf);QKeyEvent keyRelease(QEvent::KeyRelease, int(recordLitterBuf.at(0).toLatin1()), m_curModifier, recordLitterBuf);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}
}void frmKeyBoard::addOneItem(QString text)
{if(ui.listWidget->count() >= 9)return;QString itemText = QString("%1.%2").arg(ui.listWidget->count() + 1).arg(text);QListWidgetItem *item = new QListWidgetItem(itemText);QFont font;font.setPointSize(15);font.setBold(true);font.setWeight(50);item->setFont(font);/* 设置文字居中 */item->setTextAlignment(Qt::AlignBottom | Qt::AlignHCenter);bool isChineseFlag = QRegExp("^[\u4E00-\u9FA5]+").indexIn(text.left(1)) != -1;int width = font.pointSize();if (isChineseFlag)width += itemText.count()*font.pointSize()*1.5;elsewidth += itemText.count()*font.pointSize()*2/3;item->setSizeHint(QSize(width, 50));ui.listWidget->addItem(item);
}void frmKeyBoard::slotKeyButtonClicked()
{QPushButton* pbtn = (QPushButton*)sender();QString objectName = pbtn->objectName();if (pbtn->text().contains("Backspace")) {if(isChinese){if(recordLitterBuf.size() > 0){recordLitterBuf.remove(recordLitterBuf.size() - 1, 1);findChineseFontData(recordLitterBuf);if(!m_labelShowPinyin->text().isEmpty())m_labelShowPinyin->setText(recordLitterBuf);}}else{QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Backspace, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Backspace, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if (pbtn->text().contains("Caps")) {if (pbtn->isChecked()) {for (auto pbtnKey : m_letterKeys) {pbtnKey->setText(pbtnKey->text().toUpper());}}else {for (auto pbtnKey : m_letterKeys) {pbtnKey->setText(pbtnKey->text().toLower());}}}else if(pbtn->text() == "Space") {if(isChinese){if(ui.listWidget->count() > 0){on_listWidget_itemClicked(ui.listWidget->item(0));}}else{QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Space, m_curModifier, " ");QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Space, m_curModifier, " ");QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if (pbtn->text().contains("Tab")) {QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Tab, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Tab, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("Enter")) {        if(isChinese){if(!m_labelShowPinyin->text().isEmpty()){QKeyEvent keyPress(QEvent::KeyPress, 0, m_curModifier, m_labelShowPinyin->text());QKeyEvent keyRelease(QEvent::KeyRelease, 0,m_curModifier, m_labelShowPinyin->text());QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();}}else{QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Enter, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Enter, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if (pbtn->text().contains("Shift")) {if (pbtn->isChecked()) {isChinese = true;m_curModifier = Qt::ShiftModifier;for (auto pbtnKey : m_letterKeys) {pbtnKey->setText(pbtnKey->text().toUpper());}}else {isChinese = false;ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();m_curModifier = Qt::NoModifier;for (auto pbtnKey : m_letterKeys) {pbtnKey->setText(pbtnKey->text().toLower());}}QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Shift, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Shift, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("Ctrl")) {if(pbtn->isChecked())m_curModifier = Qt::ControlModifier;elsem_curModifier = Qt::NoModifier;QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Control, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Control, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("Win")) {QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Menu, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Menu, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("Alt")) {if(pbtn->isChecked())m_curModifier = Qt::AltModifier;elsem_curModifier = Qt::NoModifier;QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Alt, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Alt, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("↑")) {QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Up, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Up, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("↓")) {QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Down, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Down, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("←")) {QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Left, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Left, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else if (pbtn->text().contains("→")) {QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Right, m_curModifier);QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Right, m_curModifier);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}else {QString symbol;if (ui.pushButton_shift->isChecked())symbol = pbtn->text().split("\n").at(0);elsesymbol = pbtn->text().split("\n").at(1);QKeyEvent keyPress(QEvent::KeyPress, m_mapSymbolKeys.value(symbol), m_curModifier, symbol);QKeyEvent keyRelease(QEvent::KeyRelease, m_mapSymbolKeys.value(symbol), m_curModifier, symbol);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}
}void frmKeyBoard::slotKeyLetterButtonClicked()
{QPushButton* pbtn = (QPushButton*)sender();if (pbtn->text() >= 'a' && pbtn->text() <= 'z') {if(isChinese){recordLitterBuf+=pbtn->text().toLower();findChineseFontData(recordLitterBuf);}else{ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();QKeyEvent keyPress(QEvent::KeyPress, int(pbtn->text().at(0).toLatin1()) - 32, m_curModifier, pbtn->text());QKeyEvent keyRelease(QEvent::KeyRelease, int(pbtn->text().at(0).toLatin1()) - 32, m_curModifier, pbtn->text());QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}else if (pbtn->text() >= 'A' && pbtn->text() <= 'Z') {if(isChinese){recordLitterBuf+=pbtn->text().toLower();findChineseFontData(recordLitterBuf);}else{ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();QKeyEvent keyPress(QEvent::KeyPress, int(pbtn->text().at(0).toLatin1()), m_curModifier, pbtn->text());QKeyEvent keyRelease(QEvent::KeyRelease, int(pbtn->text().at(0).toLatin1()), m_curModifier, pbtn->text());QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);}}if (ui.pushButton_ctrl->isChecked())ui.pushButton_ctrl->setChecked(false);if (ui.pushButton_win->isChecked())ui.pushButton_win->setChecked(false);if (ui.pushButton_alt->isChecked())ui.pushButton_alt->setChecked(false);m_curModifier = Qt::NoModifier;
}void frmKeyBoard::slotKeyNumberButtonClicked()
{QPushButton* pbtn = (QPushButton*)sender();int num = pbtn->text().toInt();if(isChinese){if(num > 0 && num < 9 && ui.listWidget->count() >= num){on_listWidget_itemClicked(ui.listWidget->item(num - 1));}}else {QKeyEvent keyPress(QEvent::KeyPress, num + 48, m_curModifier, pbtn->text());QKeyEvent keyRelease(QEvent::KeyRelease, num + 48, m_curModifier, pbtn->text());QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);if (ui.pushButton_ctrl->isChecked())ui.pushButton_ctrl->setChecked(false);if (ui.pushButton_win->isChecked())ui.pushButton_win->setChecked(false);if (ui.pushButton_alt->isChecked())ui.pushButton_alt->setChecked(false);}m_curModifier = Qt::NoModifier;
}void frmKeyBoard::on_listWidget_itemClicked(QListWidgetItem *item)
{QKeyEvent keyPress(QEvent::KeyPress, 0, m_curModifier, item->text().right(1));QKeyEvent keyRelease(QEvent::KeyRelease, 0, m_curModifier, item->text().right(1));QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyPress);QApplication::sendEvent(qApp->focusWidget() == nullptr ? this : qApp->focusWidget(), &keyRelease);ui.listWidget->clear();m_showTextList.clear();m_curPage = 0;m_labelShowPinyin->clear();recordLitterBuf.clear();
}void frmKeyBoard::on_toolButton_lastPage_clicked()
{if(m_curPage <= 0)return;m_curPage--;ui.listWidget->clear();for (int i = m_curPage * 9; i < m_curPage * 9 + 9; i++) {if(i >= m_showTextList.count())break;addOneItem(m_showTextList.at(i));}
}void frmKeyBoard::on_toolButton_nextPage_clicked()
{if(m_curPage >= m_showTextList.count() / 9)return;m_curPage++;ui.listWidget->clear();for (int i = m_curPage * 9; i < m_curPage * 9 + 9; i++) {if(i >= m_showTextList.count())break;addOneItem(m_showTextList.at(i));}
}

八、使用示例

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("屏幕键盘测试(源客V)");QHBoxLayout* layout = new QHBoxLayout();layout->setContentsMargins(0, 0, 0, 0);layout->addWidget(ui->pushButton, 0, Qt::AlignRight);ui->lineEdit->setLayout(layout);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{frmKeyBoard* keyBoard = new frmKeyBoard();keyBoard->show();
}

九、效果

Qt自制虚拟键盘2(支持中文)

十、结语

        通过深度解析,我们更全面地了解了这个使用Qt框架自制的屏幕键盘。其设计理念、功能特点和实现原理展示了在用户界面开发中如何灵活运用Qt框架,为用户提供强大而友好的输入方式。对于需要在触摸屏设备上提供良好用户体验的应用,这个自制的屏幕键盘提供了一种出色的解决方案。

前序文章:QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样(一)

源码资源:Qt自制虚拟键盘(支持中文) 

本方案已经能满足屏幕键盘输入的大部分需求了,还有不足的地方欢迎大家留言补充。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/657691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客网-----------[NOIP2006]数列

题目描述 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列&#xff0c;例如&#xff0c;当k3时&#xff0c;这个序列是&#xff1a; 1&#xff0c;3&#xff0c;4&#xff0c;9&#xff0c;10&#xff0c;12&#xff0c;13&…

2024年重庆市公务员考试报名明天开始,招聘4530人!

2024年重庆公务员招录公告已出&#xff0c;招聘人数&#xff1a;4530人 ✅重庆市考重要时间节点 报名时间&#xff1a;2月1日9:00-2月6日9:00 缴费时间&#xff1a;2月8日 笔试时间&#xff1a;3月16日-17日 笔试查成绩时间&#xff1a;4月15日 面试时间&#xff1a;4月27日-2…

C++文件操作(1)

C文件操作 1.文本的写入及读取文本文件写入文本文件读取 2.二进制文件的写入及读取二进制文件写入二进制文件读取 3.小结 C也有处理文件的能力&#xff0c;其功能实现依赖文件流。文件流是C中用来处理文件输入输出的一种流类。文件流可以用于从文件中读取数据或将数据写入到文件…

《游戏-03_2D-开发》

基于《游戏-02_2D-开发》&#xff0c; 继续制作游戏&#xff1a; 首先要做的时切割人物Idle空闲状态下的动画&#xff0c; 在切割之前我们需要创建一个文件夹&#xff0c;用来存放动画控制器AnimatorContoller&#xff0c; 再创建一个人物控制器文件夹用来存放人物控制器&…

【Ubuntu 22.04.3 LTS】apt-get下载安装有关问题可能原因及解决方法

ubuntu 22.04.3 LTS unaccountably error 装啥啥没依赖 可能是用了不合适的源&#xff0c;换个就好了 Now, let’s take a look at the lsb_release output, with a special focus on the Codename, which could be a crucial piece of information. The lsb_release comm…

认识BPMN2.0

&#x1f496;专栏简介 ✔️本专栏将从Camunda(卡蒙达) 7中的关键概念到实现中国式工作流相关功能。 ✔️文章中只包含演示核心代码及测试数据&#xff0c;完整代码可查看作者的开源项目snail-camunda ✔️请给snail-camunda 点颗星吧&#x1f618; &#x1f496;说在前面 …

STM32单片机基本原理与应用(四)

直流电机驱动控制原理 1、电机正反转控制 在STM32中&#xff0c;直流电机的正反转控制主要通过改变电机输入电源的极性来实现。当电机的电压极性发生变化时&#xff0c;电机的旋转方向也会相应改变。在硬件电路中&#xff0c;可以通过继电器或晶体管等电子开关来切换电机的电源…

【TCP】重传与超时机制

前言 在网络通信的世界里&#xff0c;传输控制协议&#xff08;TCP&#xff09;扮演着一个至关重要的角色。它确保了数据的可靠传输&#xff0c;就像邮差确保每一封信都能准确无误地送达收件人手中一样。但是&#xff0c;网络环境充满了不确定性&#xff0c;数据包可能会因为各…

新书速览|Docker与Kubernetes容器运维实战

帮助读者用最短的时间掌握Docker与K8s运维技能 内容简介 随着云计算和容器技术的发展&#xff0c;Docker与Kubernetes已经成为各个企业首选的部署工具&#xff0c;使用它们可以提高系统的部署效率和运维能力&#xff0c;降低运维成本。本书是一本为初学者量身定制的Docker与Kub…

STM32F407移植OpenHarmony笔记4

上一篇写到make menuconfig报错&#xff0c;继续开整。 make menuconfig需要/device/soc/*下面有对应的Kconfig文件。 直接去gitee下载stm32的配置文件拿来参考用。 先提取Kconfig文件&#xff0c;后面再添加其它文件。https://gitee.com/openharmony/device_soc_st/tree/Open…

arcgis 如何将线路转为路面

在出外业的时候&#xff0c;用手机软件测出来的路&#xff08;线要素&#xff09;&#xff0c;需要转换成路面。具体操作如下&#xff1a; 1.打开线图层 2.菜单-地理处理-缓冲区 在缓冲区中&#xff0c;输入要转换的线要素&#xff0c;在线性单位下方填写要转换的面的宽度&am…

《Numpy 简易速速上手小册》第10章:Numpy案例研究和实践技巧(2024 最新版)

文章目录 10.1 实际案例分析10.1.1 基础知识10.1.2 完整案例&#xff1a;天气数据分析10.1.3 拓展案例 1&#xff1a;股票价格分析10.1.4 拓展案例 2&#xff1a;信号处理 10.2 Numpy 最佳实践10.2.1 基础知识10.2.2 完整案例&#xff1a;高效数组操作10.2.3 拓展案例 1&#x…

vue-cli初始化项目很慢?

第一种情况 大部分是由于npm的镜像源不是淘宝的 cmd输入npm config get registry查看是不是淘宝的&#xff0c;是的话看第二种情况试试不是的话输入npm config set registry https://registry.npm.taobao.org 第二种情况 vue-cli配置文件不是使用淘宝镜像源的 找到文件.vue…

已实现:vue、h5项目如何使用echarts实现雷达图、六边形图表

说实话&#xff0c;要说图表里&#xff0c;最强的应该属于echarts了&#xff0c;不管是接入难度上&#xff0c;还是样式多样性上&#xff0c;还有社区庞大程度上&#xff0c;都是首屈一指的&#xff0c;反观有的人习惯用chart.js了&#xff0c;这个无可厚非&#xff0c;但是如果…

微信小程序如何实现实时显示输入内容

如下所示&#xff0c;在许多场景中需要实时显示用户输入&#xff0c;具体实现见下文。 .wxml <input type"text" placeholder"请输入{{item.value}}(必填)" style"width:80%;" bindinput"get_required_value" data-info"{{it…

科技云报道:新趋势下,国产数据库或“春山可望”

科技云报道原创。 从540亿元到1286亿元——这是中国通信标准化协会大数据技术标准推进委员会针对中国数据库行业给出的一份预测报告。 报告指出&#xff0c;未来五年&#xff0c;中国数据库行业将从百亿级市场跨越成为千亿级市场。 最近两年&#xff0c;中国的数据库行业似乎…

Ubuntu 22.04 中文乱码解决方案

sudo apkg-reconfigure locales 按空格键选中

Jenkins自动化打包

Jenkins自动化打包 下载安装 我们直接从官网https://www.jenkins.io/download/ 下载所需的Jenkins文件 如上图所示, 选择Windows版本,下面就是一路安装即可,需要注意的是,选择作为系统服务选项, 不要自己设置账号密码登录. Web配置 安装完根据提示在浏览器打开 http://lo…

iZotope RX 10.4.2 mac激活版 音频修复和增强工具

iZotope RX 10 for Mac是一款专业的音频修复软件&#xff0c;旨在提供强大、精确的工具&#xff0c;让用户能够清晰、纯净地处理音频。以下是其主要功能和特点&#xff1a; 软件下载&#xff1a;iZotope RX 10.4.2 mac激活版下载 强大的降噪功能&#xff1a;iZotope RX 10采用了…

动态住宅IP可以用来注册亚马逊电商吗?

注册亚马逊店铺可以用动态IP&#xff0c;只要是独立且干净的网线就没问题&#xff0c;亚马逊规则要求一个IP地址只能出现一个亚马逊店铺&#xff0c;若使用不当会导致关联账户。所以现在非常多人使用指纹浏览器搭配代理IP 固定ip可以给我们的账户带来更多的安全&#xff0c;要知…