当前位置: 首页 > news >正文

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 源文件代码 八、使用示例 九、效果 十、结语 一、前言 由于系统自带虚拟键盘不一定好用&#xff0c;也不一…...

SpringCloud_学习笔记_1

SpringCloud01 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff…...

容器算法迭代器初识

#include<iostream> using namespace std; #include<vector> //vetor容器存放内置数据类型 void test01() {//创建了一个vector容器&#xff0c;数组 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爬虫爬取网站

流程&#xff1a; 1.指定url(获取网页的内容) 爬虫会向指定的URL发送HTTP请求&#xff0c;获取网页的HTML代码&#xff0c;然后解析HTML代码&#xff0c;提取出需要的信息&#xff0c;如文本、图片、链接等。爬虫请求URL的过程中&#xff0c;还可以设置请求头、请求参数、请求…...

c# Get方式调用WebAPI,WebService等接口

/// <summary> /// 利用WebRequest/WebResponse进行WebService调用的类 /// </summary> public class WebServiceHelper {//<webServices>// <protocols>// <add name"HttpGet"/>// <add name"HttpPost"/>// …...

银行数据仓库体系实践(11)--数据仓库开发管理系统及开发流程

数据仓库管理着整个银行或公司的数据&#xff0c;数据结构复杂&#xff0c;数据量庞大&#xff0c;任何一个数据字段的变化或错误都会引起数据错误&#xff0c;影响数据应用&#xff0c;同时业务的发展也带来系统不断升级&#xff0c;数据需求的不断增加&#xff0c;数据仓库需…...

微信小程序引导用户打开定位授权通用模版

在需要使用位置信息的页面&#xff08;例如 onLoad 或 onShow 生命周期函数&#xff09;中调用 wx.getSetting 方法检查用户是否已经授权地理位置权限&#xff1a; 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简介&#xff1a; OkHttp 是一个开源的、高效的 HTTP 客户端库&#xff0c;由 Square 公司开发和维护。它为 Android 和 Java 应用程序提供了简单、强大、灵活的 HTTP 请求和响应的处理方式。OkHttp 的设计目标是使网络请求变得更加简单、快速、高效&#xff0c;并且支持…...

R语言基础学习-02 (此语言用途小众 用于数学 生物领域 基因分析)

变量 R 语言的有效的变量名称由字母&#xff0c;数字以及点号 . 或下划线 _ 组成。 变量名称以字母或点开头。 变量名是否正确原因var_name2.正确字符开头&#xff0c;并由字母、数字、下划线和点号组成var_name%错误% 是非法字符2var_name错误不能数字开头 .var_name, var.…...

CTF-WEB的入门真题讲解

EzLogin 第一眼看到这个题目我想着用SQL注入 但是我们先看看具体的情况 我们随便输入admin和密码发现他提升密码不正确 我们查看源代码 发现有二个不一样的第一个是base64 意思I hava no sql 第二个可以看出来是16进制转化为weak通过发现是个弱口令 canyouaccess 如果…...

【C项目】顺序表

简介&#xff1a;本系列博客为C项目系列内容&#xff0c;通过代码来具体实现某个经典简单项目 适宜人群&#xff1a;已大体了解C语法同学 作者留言&#xff1a;本博客相关内容如需转载请注明出处&#xff0c;本人学疏才浅&#xff0c;难免存在些许错误&#xff0c;望留言指正 作…...

【Docker】在Windows下使用Docker Desktop创建nginx容器并访问默认网站

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…...

详讲api网关之kong的基本概念及安装和使用(二)

consul的服务注册与发现 如果不知道consul的使用&#xff0c;可以点击上方链接&#xff0c;这是我写的关于consul的一篇文档。 upstreamconsul实现负载均衡 我们知道&#xff0c;配置upstream可以实现负载均衡&#xff0c;而consul实现了服务注册与发现&#xff0c;那么接下来…...

取消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_守卫

路由守卫&#xff1a;路由进行权限控制。 分为&#xff1a;全局守卫&#xff0c;独享守卫&#xff0c;组件内守卫。 全局守卫 //创建并暴露 路由器 const routernew Vrouter({mode:"hash"//"hash路径出现#但是兼容性强&#xff0c;history没有#兼容性差"…...

GDB调试技巧实战--自动化画出类关系图

1. 前言 上节我们在帖子《Modern C++利用工具快速理解std::tuple的实现原理》根据GDB的ptype命令快速的理解了std::tuple数据结构的实现,但是手动一个个打印,然后手动画出的UML图,这个过程明显可以自动化。 本文旨在写一个GDB python脚本把这个过程自动化。 本脚本也可以用…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

FTXUI::Dom 模块

DOM 模块定义了分层的 FTXUI::Element 树&#xff0c;可用于构建复杂的终端界面&#xff0c;支持响应终端尺寸变化。 namespace ftxui {...// 定义文档 定义布局盒子 Element document vbox({// 设置文本 设置加粗 设置文本颜色text("The window") | bold | color(…...

二维数组 行列混淆区分 js

二维数组定义 行 row&#xff1a;是“横着的一整行” 列 column&#xff1a;是“竖着的一整列” 在 JavaScript 里访问二维数组 grid[i][j] 表示 第i行第j列的元素 let grid [[1, 2, 3], // 第0行[4, 5, 6], // 第1行[7, 8, 9] // 第2行 ];// grid[i][j] 表示 第i行第j列的…...