当前位置:网站首页>QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样
QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样
2022-07-28 13:10:00 【源客V】
一、本自制虚拟键盘特点
1.键盘界面保持在所有界面最上方。
2.点击键盘按钮不会改变底层文本输入框焦点。
3.通过模拟键盘点击事件完成键盘输入文本信息。
4.包含各种键盘自带符号输入。
5.长按按键可以持续重复输入键盘内容。
6.支持win7、win10、Linux等各个系统。
7.界面好看。
总之跟真的虚拟键盘一样,不会有不爽的地方
二、windows打开系统自带软键盘
QDesktopServices::openUrl(QUrl("osk.exe", QUrl::TolerantMode));传统打开系统自带虚拟键盘的方法如上,一行代码即可,但是系统自带的虚拟键盘不一定好用,有的按键太小,有的电脑上可能没有自带的软键盘,干脆直接写一个。
三、让键盘界面保持在最上方、不改变底层界面焦点
很多自制键盘时不知道怎么使键盘保持在最顶层,发现点击键盘界面后系统焦点在键盘界面上了,光标不在底层的行输入框里,就通过输入完成后点击确定的方式将键盘内容传到底层界面,显得不nice,其实这两个问题都很简单能够解决,代码如下
this->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus);Qt::WindowStaysOnTopHint 设置窗口置顶 Qt::WindowDoesNotAcceptFocus 设置无焦点窗口
四、长按按键重复输入键盘内容
特别是我们点击退格删除键时,文本比较多是我们使用真实键盘就会长按退格键把十多个文本挨个删除,而使用虚拟键盘时要按十多次退格键,显得不nice。那么我们使用QPushButton的setAutoRepeat为true,就可以实现按键长按功能了,设置重复操作延时为500ms差不多了,按下的时长超过500ms后马上再次执行按键槽函数。
pbtn->setAutoRepeat(true); //允许自动重复
pbtn->setAutoRepeatDelay(500);//设置重复操作的时延五、模拟键盘点击事件完成虚拟键盘输入
一些自制虚拟键盘完成键盘输入的方式是同传递文本到输入框的方式,话不多说,就是不nice。点击虚拟按键直接发送对应的按键点击事件就很nice,这样其实就不会太死,甚至中文输入也可以,我们写的是键盘,不是写中文输入法,那些下载网上含有中文输入法的库的大可不必,输入法你系统用的什么就是什么。如果你想中文输入,系统下载的有搜狗输入法,你的键盘只需按下ctrl+shift切换输入法即可,而不是在键盘里实现中文输入法的功能。那样不nice,模拟发送按键点击事件代码如下。
QPushButton* pbtn = (QPushButton*)sender();
if (pbtn->text() >= 'a' && pbtn->text() <= 'z') {
QKeyEvent keyPress(QEvent::KeyPress, int(pbtn->text().at(0).toLatin1()) - 32, Qt::NoModifier, pbtn->text());
QKeyEvent keyRelease(QEvent::KeyRelease, int(pbtn->text().at(0).toLatin1()) - 32, Qt::NoModifier, pbtn->text());
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}通过QApplication::sendEvent发送一个按键按下和按键松开的事件就相当于模拟一个按键按下事件
QKeyEvent说明:
QKeyEvent::QKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text = QString(), bool autorep = false, ushort count = 1)
类型参数必须为QEvent::KeyPress、QEvent::KeyRelease或QEvent::ShortcutOverride。
Int key是事件循环应该监听的Qt:: key的代码。 如果key为0,则事件不是已知键的结果; 例如,它可能是组合序列或键盘宏的结果。 修饰符包含键盘修饰符,给定的文本是键生成的Unicode文本。 如果autorep为真,isAutoRepeat()将为真。 Count是事件中涉及的键的数量。
QKeyEvent在我们这里用4个参数就行了,第二个参数Qt:: key对应的按键,第四个参数是需要发送的文本,没有这个参数是无法输入任何内容的。
QApplication::sendEvent的第一个参数非常重要,是接收键盘输入的控件,比如是底层界面的lineEdit。
六、键盘符号输入
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);
QKeyEvent keyPress(QEvent::KeyPress, m_mapSymbolKeys.value(symbol), Qt::NoModifier, symbol);
QKeyEvent keyRelease(QEvent::KeyRelease, m_mapSymbolKeys.value(symbol), Qt::NoModifier, symbol);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);键盘输入特定的符号重要是找到QKeyEvent第二个参数对应的按键值即可。按键中的值被都在qnamespace.h的头文件中的enum Key枚举类型内。
七、界面

八、头文件代码
#pragma once
#pragma execution_character_set("utf-8")
#include <QDialog>
#include "ui_frmKeyBoard.h"
#include "moveWidget.h"
#include <QPushButton>
#include <QKeyEvent>
#include <QDebug>
#include <QStyle>
class frmKeyBoard : public QDialog
{
Q_OBJECT
public:
frmKeyBoard(QWidget *parent = nullptr);
~frmKeyBoard();
void initFocusWidget(QWidget*);
private slots:
void slotKeyButtonClicked();
void slotKeyLetterButtonClicked();
void slotKeyNumberButtonClicked();
private:
Ui::frmKeyBoardClass ui;
void initFrm();
void initStyleSheet();
QWidget* m_focusWidget; //键盘输入主窗口
QVector<QPushButton*> m_letterKeys;
QVector<QPushButton*> m_NumberKeys;
QMap<QString, Qt::Key> m_mapSymbolKeys;
};
九、源文件代码
#include "frmKeyBoard.h"
frmKeyBoard::frmKeyBoard(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
this->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
this->setWindowTitle("屏幕键盘");
this->setWindowModality(Qt::WindowModal);
this->setAttribute(Qt::WA_DeleteOnClose);
MoveWidget* moveWidget = new MoveWidget();
moveWidget->setWidget(this);
this->initFrm();
this->initStyleSheet();
}
frmKeyBoard::~frmKeyBoard()
{
}
void frmKeyBoard::initFocusWidget(QWidget* widget)
{
m_focusWidget = widget;
}
void frmKeyBoard::initFrm()
{
ui.pushButton_closeKeyboard->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
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);
}
}
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 += "QPushButton{ color:white; background-color:rgb(51,51,51); height:60px; 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::slotKeyButtonClicked()
{
QPushButton* pbtn = (QPushButton*)sender();
QString objectName = pbtn->objectName();
if (objectName == "pushButton_closeKeyboard") {
this->close();
return;
}
if (pbtn->text().contains("Backspace")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->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") {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Space, Qt::NoModifier, " ");
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Space, Qt::NoModifier, " ");
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("Tab")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Tab, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("Enter")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("Shift")) {
if (pbtn->isChecked()) {
for (auto pbtnKey : m_letterKeys) {
pbtnKey->setText(pbtnKey->text().toUpper());
}
}
else {
for (auto pbtnKey : m_letterKeys) {
pbtnKey->setText(pbtnKey->text().toLower());
}
}
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Shift, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("Ctrl")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Control, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Control, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("Win")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Menu, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Menu, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("Alt")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Alt, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Alt, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("↑")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Up, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("↓")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Down, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("←")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text().contains("→")) {
QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else {
QString symbol;
if (ui.pushButton_shift->isChecked())
symbol = pbtn->text().split("\n").at(0);
else
symbol = pbtn->text().split("\n").at(1);
QKeyEvent keyPress(QEvent::KeyPress, m_mapSymbolKeys.value(symbol), Qt::NoModifier, symbol);
QKeyEvent keyRelease(QEvent::KeyRelease, m_mapSymbolKeys.value(symbol), Qt::NoModifier, symbol);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
//取消组合键按下
if (!pbtn->text().contains("Shift") && !pbtn->text().contains("Ctrl") && !pbtn->text().contains("Win") && !pbtn->text().contains("Alt")) {
if (ui.pushButton_shift->isChecked()) {
ui.pushButton_shift->setChecked(false);
for (auto pbtnKey : m_letterKeys) {
pbtnKey->setText(pbtnKey->text().toLower());
}
}
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);
}
}
void frmKeyBoard::slotKeyLetterButtonClicked()
{
QPushButton* pbtn = (QPushButton*)sender();
if (pbtn->text() >= 'a' && pbtn->text() <= 'z') {
QKeyEvent keyPress(QEvent::KeyPress, int(pbtn->text().at(0).toLatin1()) - 32, Qt::NoModifier, pbtn->text());
QKeyEvent keyRelease(QEvent::KeyRelease, int(pbtn->text().at(0).toLatin1()) - 32, Qt::NoModifier, pbtn->text());
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
else if (pbtn->text() >= 'A' && pbtn->text() <= 'Z') {
QKeyEvent keyPress(QEvent::KeyPress, int(pbtn->text().at(0).toLatin1()), Qt::NoModifier, pbtn->text());
QKeyEvent keyRelease(QEvent::KeyRelease, int(pbtn->text().at(0).toLatin1()), Qt::NoModifier, pbtn->text());
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
}
//取消组合键按下
if (ui.pushButton_shift->isChecked()) {
ui.pushButton_shift->setChecked(false);
for (auto pbtnKey : m_letterKeys) {
pbtnKey->setText(pbtnKey->text().toLower());
}
}
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);
}
void frmKeyBoard::slotKeyNumberButtonClicked()
{
QPushButton* pbtn = (QPushButton*)sender();
QKeyEvent keyPress(QEvent::KeyPress, pbtn->text().toInt() + 48, Qt::NoModifier, pbtn->text());
QKeyEvent keyRelease(QEvent::KeyRelease, pbtn->text().toInt() + 48, Qt::NoModifier, pbtn->text());
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyPress);
QApplication::sendEvent(m_focusWidget->focusWidget(), &keyRelease);
//取消组合键按下
if (ui.pushButton_shift->isChecked()) {
ui.pushButton_shift->setChecked(false);
for (auto pbtnKey : m_letterKeys) {
pbtnKey->setText(pbtnKey->text().toLower());
}
}
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);
}
十一、效果

Qt自制虚拟键盘
十二、资源下载
边栏推荐
- The strongest distributed locking tool: redisson
- 【LVGL事件(Events)】事件代码
- url相关知识点
- [Architecture] reading notes of three micro service books with high scores
- Operator3 - design an operator
- Rust from introduction to mastery 01 introduction
- Security assurance is based on software life cycle -istio authorization mechanism
- Istio IV fault injection and link tracking
- Several efficient APIs commonly used in inventory operation URL
- leetcode(442)数组中重复的数据
猜你喜欢

离散对数问题(DLP) && Diffie-Hellman问题(DHP)

一文读懂如何部署具有外部数据库的高可用 K3s

30 day question brushing plan (III)

Record a fake login of cookie

Qt5 development from introduction to mastery -- the first overview

After finishing, help autumn move, I wish you call it an offer harvester

Operator3 - design an operator

DXF读写:对齐尺寸标注文字居中、上方的位置计算

30天刷题训练(一)

安全保障基于软件全生命周期-PSP应用
随机推荐
【飞控开发基础教程7】疯壳·开源编队无人机-SPI(气压计数据获取)
Socket class understanding and learning about TCP character stream programming
Operator3 - design an operator
30天刷题计划(三)
Security assurance is based on software life cycle -istio authorization mechanism
Understand the principle behind the virtual list, and easily realize the virtual list
TS扫盲大法-基础篇
Dojnoip201708 cheese solution
第六章 支持向量机
创建线程池的四种方式
MySQL开发技巧——视图
83.(cesium之家)cesium示例如何运行
No swagger, what do I use?
Istio四之故障注入和链路追踪
什么是自旋锁 自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。 /** * 为什么用自旋锁:多个线程对同一个变量
111. The sap ui5 fileuploader control realizes local file upload and encounters a cross domain access error when receiving the response from the server
Thoroughly master binary search
Remember to use pdfbox once to parse PDF and obtain the key data of PDF
How to play a data mining game entry Edition
Record a fake login of cookie