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,一经查实,立即删除!

相关文章

【python】合理使用copy与deepcopy

合理使用**copy**与**deepcopy**对于dict和list等数据结构的对象&#xff0c;直接赋值使用的是引用的方式。而有些情况下需要复制整个对象&#xff0c;这时可以使用copy包里的copy和deepcopy&#xff0c;这两个函数的不同之处在于后者是递归复制的。效率也不一样&#xff1a;&a…

动态设置小程序IOS底部小黑条

创建setIOSAreaMixin.js文件 import {mapState,mapMutations } from vuexexport default {computed: {...mapState("ios_area", ["globalAreaClass", isSafeAreaCalculated])},mounted() {if (!this.isSafeAreaCalculated) {this.calculateSafeAreaClass(…

牛客网-----------[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;可以通过继电器或晶体管等电子开关来切换电机的电源…

查找二叉树(tree_a)

时间限制&#xff1a;1秒 内存限制&#xff1a;128M 题目描述 已知一棵二叉树用邻接表结构存储&#xff0c;中序查找二叉树中值为x的结点&#xff08;x在二叉树中是唯一的&#xff09;&#xff0c;并指出是第几个结点。 输入描述 第一行n为二叉树的结点个树&#xf…

【TCP】重传与超时机制

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

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

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

Android PMS——PMS服务启动流程(二)

PackageManagerService 既然是系统服务&#xff0c;那么肯定是通过 SystemServer 启动的&#xff0c;所以我们首先看一下 SystemServer 服务中启动 PackageManagerService 相关代码。 一、PMS启动 1、SystemServer 源码路径&#xff1a;/frameworks/base/services/java/com/…

iPhone手电筒不能工作的几种修复办法,总有一种适合你

这篇文章解释了为什么你的iPhone的手电筒不工作以及如何修复它。 手电筒不工作的原因 iPhone手电筒功能可能不起作用的原因有几个。通常&#xff0c;软件错误或小故障会导致该功能出现故障&#xff0c;但可能是你处于错误的电源模式或只需要充电。其他时候&#xff0c;确切的…

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…

[E模拟] lc2670. 找出不同元素数目差数组(哈希表+状态压缩)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接&#xff1a;2670. 找出不同元素数目差数组 2. 题目解析 哈希计数统计就行了&#xff0c;题解里有看到用 long 64 位进行状态压缩的&#xff0c;可以参考下。这类题就不纠结写法了。 思路&#xff1a; 两个哈希表&#x…

回响科技二面面试题解答

面试题 1、你们的数仓中DWD层为什么要划分数据域&#xff1f;划分数据域之后会对ADS层造成什么影响&#xff1f;是可以提效还是可扩展性强&#xff1f;你们是如何考虑的呢&#xff1f; 2、AZkaban和dolphinScheduler的区别是什么&#xff1f;如果选型会从哪几个方面来考虑呢&a…

《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…