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自制虚拟键盘(支持中文)
本方案已经能满足屏幕键盘输入的大部分需求了,还有不足的地方欢迎大家留言补充。

相关文章:
QT自制软键盘 最完美、最简单、支持中文输入(二)
目录 一、前言 二、本自制虚拟键盘特点 三、中文输入原理 四、组合键输入 五、键盘事件模拟 六、界面 七、代码 7.1 frmKeyBoard 头文件代码 7.2 frmKeyBoard 源文件代码 八、使用示例 九、效果 十、结语 一、前言 由于系统自带虚拟键盘不一定好用,也不一…...
SpringCloud_学习笔记_1
SpringCloud01 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构ÿ…...
容器算法迭代器初识
#include<iostream> using namespace std; #include<vector> //vetor容器存放内置数据类型 void test01() {//创建了一个vector容器,数组 vector<int> v;//向容器中插入数据v.push_back (10);//尾插 v.push_back (20);v.push_back (30);v.push_ba…...
瑞_力扣LeetCode_二叉搜索树相关题
文章目录 说明题目 450. 删除二叉搜索树中的节点题解递归实现 题目 701. 二叉搜索树中的插入操作题解递归实现 题目 700. 二叉搜索树中的搜索题解递归实现 题目 98. 验证二叉搜索树题解中序遍历非递归实现中序遍历递归实现上下限递归 题目 938. 二叉搜索树的范围和题解中序遍历…...
python爬虫爬取网站
流程: 1.指定url(获取网页的内容) 爬虫会向指定的URL发送HTTP请求,获取网页的HTML代码,然后解析HTML代码,提取出需要的信息,如文本、图片、链接等。爬虫请求URL的过程中,还可以设置请求头、请求参数、请求…...
c# Get方式调用WebAPI,WebService等接口
/// <summary> /// 利用WebRequest/WebResponse进行WebService调用的类 /// </summary> public class WebServiceHelper {//<webServices>// <protocols>// <add name"HttpGet"/>// <add name"HttpPost"/>// …...
银行数据仓库体系实践(11)--数据仓库开发管理系统及开发流程
数据仓库管理着整个银行或公司的数据,数据结构复杂,数据量庞大,任何一个数据字段的变化或错误都会引起数据错误,影响数据应用,同时业务的发展也带来系统不断升级,数据需求的不断增加,数据仓库需…...
微信小程序引导用户打开定位授权通用模版
在需要使用位置信息的页面(例如 onLoad 或 onShow 生命周期函数)中调用 wx.getSetting 方法检查用户是否已经授权地理位置权限: Page({onLoad: function() {wx.getSetting({success: res > {if (res.authSetting[scope.userLocation]) {/…...
JVM篇----第十篇
系列文章目录 文章目录 系列文章目录前言一、JAVA 强引用二、JAVA软引用三、JAVA弱引用四、JAVA虚引用五、分代收集算法前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧…...
DevSecOps 参考模型介绍
目录 一、参考模型概述 1.1 概述 二、参考模型分类 2.1 DevOps 组织型模型 2.1.1 DevOps 关键特性 2.1.1.1 模型特性图 2.1.1.2 特性讲解 2.1.1.2.1 自动化 2.1.1.2.2 多边协作 2.1.1.2.3 持续集成 2.1.1.2.4 配置管理 2.1.2 DevOps 生命周期 2.1.2.1 研发过程划分…...
什么是okhttp?
OkHttp简介: OkHttp 是一个开源的、高效的 HTTP 客户端库,由 Square 公司开发和维护。它为 Android 和 Java 应用程序提供了简单、强大、灵活的 HTTP 请求和响应的处理方式。OkHttp 的设计目标是使网络请求变得更加简单、快速、高效,并且支持…...
R语言基础学习-02 (此语言用途小众 用于数学 生物领域 基因分析)
变量 R 语言的有效的变量名称由字母,数字以及点号 . 或下划线 _ 组成。 变量名称以字母或点开头。 变量名是否正确原因var_name2.正确字符开头,并由字母、数字、下划线和点号组成var_name%错误% 是非法字符2var_name错误不能数字开头 .var_name, var.…...
CTF-WEB的入门真题讲解
EzLogin 第一眼看到这个题目我想着用SQL注入 但是我们先看看具体的情况 我们随便输入admin和密码发现他提升密码不正确 我们查看源代码 发现有二个不一样的第一个是base64 意思I hava no sql 第二个可以看出来是16进制转化为weak通过发现是个弱口令 canyouaccess 如果…...
【C项目】顺序表
简介:本系列博客为C项目系列内容,通过代码来具体实现某个经典简单项目 适宜人群:已大体了解C语法同学 作者留言:本博客相关内容如需转载请注明出处,本人学疏才浅,难免存在些许错误,望留言指正 作…...
【Docker】在Windows下使用Docker Desktop创建nginx容器并访问默认网站
欢迎来到《小5讲堂》,大家好,我是全栈小5。 这是《Docker容器》序列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对…...
详讲api网关之kong的基本概念及安装和使用(二)
consul的服务注册与发现 如果不知道consul的使用,可以点击上方链接,这是我写的关于consul的一篇文档。 upstreamconsul实现负载均衡 我们知道,配置upstream可以实现负载均衡,而consul实现了服务注册与发现,那么接下来…...
取消Vscode在输入符号时自动补全
取消Vscode在输入符号时自动补全 取消Vscode在输入符号时自动补全问题演示解决方法 取消Vscode在输入符号时自动补全 问题演示 在此状态下输入/会直接自动补全, 如下图 笔者想要达到的效果为可以正常输入/而不进行补全, 如下图 解决方法 在设置->文本编辑器->建议, 取消…...
ElementUI Form:Input 输入框
ElementUI安装与使用指南 Input 输入框 点击下载learnelementuispringboot项目源码 效果图 el-input.vue 页面效果图 项目里el-input.vue代码 <script> export default {name: el_input,data() {return {input: ,input1: ,input2: ,input3: ,input4: ,textarea: …...
Vue_Router_守卫
路由守卫:路由进行权限控制。 分为:全局守卫,独享守卫,组件内守卫。 全局守卫 //创建并暴露 路由器 const routernew Vrouter({mode:"hash"//"hash路径出现#但是兼容性强,history没有#兼容性差"…...
GDB调试技巧实战--自动化画出类关系图
1. 前言 上节我们在帖子《Modern C++利用工具快速理解std::tuple的实现原理》根据GDB的ptype命令快速的理解了std::tuple数据结构的实现,但是手动一个个打印,然后手动画出的UML图,这个过程明显可以自动化。 本文旨在写一个GDB python脚本把这个过程自动化。 本脚本也可以用…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
项目进度管理软件是什么?项目进度管理软件有哪些核心功能?
无论是建筑施工、软件开发,还是市场营销活动,项目往往涉及多个团队、大量资源和严格的时间表。如果没有一个系统化的工具来跟踪和管理这些元素,项目很容易陷入混乱,导致进度延误、成本超支,甚至失败。 项目进度管理软…...
