QML 中自定义虚拟键盘

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

前言

我们知道 Qt 中虚拟键盘模块遵循的是 GPL 协议,是不可用于商业发布的。如果项目中使用了 Qt 自带的虚拟键盘,在正式发布项目时必须要开源才可以。因此为了避免使用此模块就需要自己来实现一个虚拟键盘功能。博主在网上也搜到了一些资源,基本上都是 widget 来实现的,用 qml 来做的很少,这里我们以官方的虚拟键盘为参照,用 qml 自己实现一个键盘。

功能展示

在这里插入图片描述

代码展示

1. main.qml

界面上的元素包括:两个自定义文本输入框 BQTextInput ,一个自定义虚拟键盘 BQVirtualKeyboard ,还有一个切换中英文的按钮。虚拟键盘显示的条件是当前焦点正在文本输入框中

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15Window {width: 1024; height: 768visible: truetitle: qsTr("Virtual Keyboard Demo")Rectangle {anchors.fill: parentcolor: "lightblue"BQTextInput {id: input1x: 10; y: 10width: 300; height: 40pixelSize: 16textFontFamily: "微软雅黑"placeholderText: "测试文本1"}BQTextInput {id: input2x: 10; y: 60width: 300; height: 40pixelSize: 16textFontFamily: "微软雅黑"placeholderText: "测试文本2"}Button {x: 10; y: 110width: 120; height: 40font.pixelSize: 16text: "切换键盘语言"onClicked: virtualKeyboard.languageType = virtualKeyboard.languageType == 1 ? 2 : 1}BQVirtualKeyboard {id: virtualKeyboardy: 180anchors.horizontalCenter: parent.horizontalCentervisible: input1.hasFocus || input2.hasFocus}}
}
2. BQTextInput.qml

自定义文本输入框,从其他项目中直接拷贝过来的,主要用于实现账号密码登录时的文本输入,如下图所示
在这里插入图片描述

import QtQuick 2.15// 自定义文本输入框
Rectangle {width: 200; height: 40color: "white"property int rightDis: 0                        // 右侧缩进property int pixelSize: 16                      // 字体大小property string textFontFamily: ""              // 字体样式property string placeholderText: ""             // 提示文本property alias textInput: input                 // 文本输入property bool isPassword: false                 // 密码输入property string imageSource: ""                 // 图像资源property bool hasFocus: input.focus             // 输入框焦点signal imagePressed()TextInput {id: inputx: 5width: parent.width - rightDis - 10height: parent.heightactiveFocusOnPress: truefont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: TextInput.AlignVCenterechoMode: isPassword ? TextInput.Password : TextInput.Normalclip: trueText {x: 5anchors.verticalCenter: parent.verticalCenterfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCentertext: placeholderTextvisible: input.text == ""}}Image {id: imagewidth: rightDis; height: rightDisanchors.verticalCenter: parent.verticalCenteranchors.right: parent.rightanchors.rightMargin: (parent.height - rightDis) / 2source: imageSourcevisible: rightDis != 0MouseArea {anchors.fill: parentcursorShape: Qt.PointingHandCursoronClicked: {image.focus = trueimagePressed()}}}
}
3. BQVirtualKeyboard.qml

自定义虚拟键盘,一共4行布局,符号可以按照需求自己改

import QtQuick 2.15// 自定义虚拟键盘
Rectangle {id: virtualKeyboardwidth: 710; height: 290radius: 5color: "black"property bool isUpper: false            // 是否大写property bool isEnglish: true           // 是否英文property int page: 1                    // 字符页面property int pixelSize: 16              // 字体大小property string textFontFamily: ""      // 字体样式property int languageType: 1            // 语言 1-中文 2-英文property var en_line1_lower: ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"]property var en_line1_upper: ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]property var en_line2_lower: ["a", "s", "d", "f", "g", "h", "j", "k", "l"]property var en_line2_upper: ["A", "S", "D", "F", "G", "H", "J", "K", "L"]property var en_line3_lower: ["z", "x", "c", "v", "b", "n", "m"]property var en_line3_upper: ["Z", "X", "C", "V", "B", "N", "M"]property var char_page1_line1: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]property var char_page1_line2: ["~", "-", "+", ";", ":", "_", "=", "|", "\\"]property var char_page1_line3: ["`", ",", ".", "<", ">", "/", "?"]property var char_page2_line1: ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"]property var char_page2_line2: ["[", "]", "{", "}", "'", "\"", "I", "II", "III"]property var char_page2_line3: ["IV", "V", "VI", "VII", "VIII", "IX", "X"]// 第一行Row {y: 10anchors.horizontalCenter: parent.horizontalCenterspacing: 10Repeater {model: {if (isEnglish) {isUpper ? en_line1_upper : en_line1_lower} else {page == 1 ? char_page1_line1 : char_page2_line1}}Rectangle {width: 60; height: 60radius: 5color: area1.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area1.pressed ? "#5D4B37" : "#FFFFFF"text: modelData}MouseArea {id: area1anchors.fill: parentfocus: falseonClicked: {var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)focusedItem.text = focusedItem.text + modelData}}}}}// 第二行Row {y: 80anchors.horizontalCenter: parent.horizontalCenterspacing: 10Repeater {model: {if (isEnglish) {isUpper ? en_line2_upper : en_line2_lower} else {page == 1 ? char_page1_line2 : char_page2_line2}}Rectangle {width: 60; height: 60radius: 5color: area2.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area2.pressed ? "#5D4B37" : "#FFFFFF"text: modelData}MouseArea {id: area2anchors.fill: parentfocus: falseonClicked: {var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)focusedItem.text = focusedItem.text + modelData}}}}}// 第三行Row {y: 150anchors.horizontalCenter: parent.horizontalCenterspacing: 10// shiftRectangle {width: 95; height: 60radius: 5color: area_shift.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area_shift.pressed ? "#5D4B37" : (isEnglish && isUpper ? "#239B56" : "#FFFFFF")text: isEnglish ? "Shift" : (page == 1 ? "1/2" : "2/2")}MouseArea {id: area_shiftanchors.fill: parentfocus: falseonClicked: {if (isEnglish) {isUpper = !isUpper} else {page == 1 ? (page = 2) : (page = 1)}}}}Repeater {model: {if (isEnglish) {isUpper ? en_line3_upper : en_line3_lower} else {page == 1 ? char_page1_line3 : char_page2_line3}}Rectangle {width: 60; height: 60radius: 5color: area3.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area3.pressed ? "#5D4B37" : "#FFFFFF"text: modelData}MouseArea {id: area3anchors.fill: parentfocus: falseonClicked: {var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)focusedItem.text = focusedItem.text + modelData}}}}// backspaceRectangle {width: 95; height: 60radius: 5color: area_backspace.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area_backspace.pressed ? "#5D4B37" : "#FFFFFF"text: languageType == 1 ? "回 退" : "Backspace"}MouseArea {id: area_backspaceanchors.fill: parentfocus: falseonClicked: {var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)focusedItem.text = focusedItem.text.slice(0, -1)}}}}// 第四行Row {y: 220anchors.horizontalCenter: parent.horizontalCenterspacing: 10// switchRectangle {width: 95; height: 60radius: 5color: area_switch.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area_switch.pressed ? "#5D4B37" : "#FFFFFF"text: isEnglish ? "&123" : "ABC"}MouseArea {id: area_switchanchors.fill: parentfocus: falseonClicked: isEnglish = !isEnglish}}// spaceRectangle {width: 375; height: 60radius: 5color: area_space.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area_space.pressed ? "#5D4B37" : "#FFFFFF"text: languageType == 1 ? "空 格" : "Space"}MouseArea {id: area_spaceanchors.fill: parentfocus: falseonClicked: {var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)focusedItem.text += " "}}}// clearRectangle {width: 95; height: 60radius: 5color: area_clear.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area_clear.pressed ? "#5D4B37" : "#FFFFFF"text: languageType == 1 ? "清 空" : "Clear"}MouseArea {id: area_clearanchors.fill: parentfocus: falseonClicked: {var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)focusedItem.text = ""}}}// hideRectangle {id: hidewidth: 95; height: 60radius: 5color: area_hide.pressed ? "#2A2826" : "#383533"Text {anchors.fill: parentfont.pixelSize: pixelSizefont.family: textFontFamilyverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentercolor: area_hide.pressed ? "#5D4B37" : "#FFFFFF"text: languageType == 1 ? "隐 藏" : "Hide"}MouseArea {id: area_hideanchors.fill: parentonClicked: hide.focus = true}}}
}
4. 获取当前拥有焦点的控件

在 BQVirtualKeyboard.qml 文件中用到了下面这行代码,其目的是获取当前拥有焦点的控件,这个操作可以说是自己实现虚拟键盘的一个难点,只要获取到当前拥有焦点的控件,就可以根据虚拟键盘上按下的按键,对控件的内容进行修改

var focusedItem = commonFun.getFocusedItem(virtualKeyboard.parent)

创建一个 CommonFunction 类,实现 getFocusedItem 函数,通过父节点去寻找子控件中哪一个拥有焦点

QQuickItem* CommonFunction::getFocusedItem(QQuickItem* rootItem)
{if ( rootItem->hasActiveFocus() ) {return rootItem;}QList<QQuickItem*> childItems = rootItem->childItems();for (QQuickItem *childItem : childItems){QQuickItem *focusedItem = getFocusedItem(childItem);if (focusedItem) {return focusedItem;}}return nullptr;
}

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

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

相关文章

解决java接口使用spring-validation验证入参类型为List校验不生效的问题

由于 List 是接口,无法直接用于参数验证,但可以通过实现类重写 List 接口,把此实现类作为校验dto即可 dto标记: 一、直接使用 List 入参类型,不生效示例: @ApiOperation(value = "公共测试接口")@PostMapping("/testPublicApi")public CommonResult&…

vue +elementui 项目登录通过不同账号切换侧边栏菜单的颜色

前景提要&#xff1a;要求不同权限账号登录侧边栏颜色不一样。分为 theme&#xff1a;1代表默认样式&#xff0c;theme:2代表深色主题样式。 1.首先定义一个主题文件 theme.js&#xff0c;定义两个主题样式 // 主要是切换菜单栏和菜单头部主题的设计&#xff0c;整体主题样式切…

【7z压缩包】7z压缩包没密码怎么办

7z压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了7z压缩包的密码…

rime中州韵 输入字符透传 lua Translator

在 rime中州韵 help lua Translator 中我们分享了如何使用 lua 脚本定义一个 translator&#xff0c;并以 五笔・拼音 为例引用了该 translator&#xff0c;并且达到了预期的效果。 今天&#xff0c;我们继续通过 lua 脚本为 rime中州韵/小狼毫 输入法打造一个 translator&…

使用Spring Retry优雅的实现业务异常重试

在系统中经常遇到业务重试的逻辑&#xff0c;比如三方接口调用&#xff0c;timeout重试三遍&#xff0c;异常处理重试的兜底逻辑等。那你是不是还在用下面这种方式呢&#xff1a; 我想大家可能很多时候也会这么写&#xff0c;这是能想到的第一个方法&#xff0c;但是我们这段代…

Ant Design Vue 编译后的网页特点是什么,怎么确认他是用的前端 Ant Design Vue 技术栈的呢?

Ant Design Vue 是一个前端 UI 框架&#xff0c;使用 Vue.js 构建。它包含了大量的预设样式和组件&#xff0c;如按钮、表单、表格等&#xff0c;可以帮助开发者快速构建出优雅且功能丰富的网页。但是&#xff0c;要确定一个编译后的网页是否使用了 Ant Design Vue&#xff0c;…

【算法】利用分治思想解算法题:快排、归并、快速选择实战(C++)

1. 分治思想 介绍 分治法将问题划分成多个相互独立且相同或类似的子问题&#xff0c;然后递归地解决每个子问题&#xff0c;并将结果合并以得到原始问题的解。 分治思想通常包含以下三个步骤&#xff1a; 分解&#xff1a;将原始问题划分成多个规模较小、相互独立且类似的子…

单片机大小端模式

单片机大小端模式 参考链接 单片机干货-什么是大小端_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Ju4y1M7Tx/?spm_id_from333.337.search-card.all.click&vd_sourcee821a225c7ba4a7b85e5aa6d013ac92e 特此记录 anlog 2024年1月2日

文本到3D肖像最强生成方案!DiffusionGAN3D: 3D GANs和Diffusion先验强强联合!

本文介绍了一个新型框架DiffusionGAN3D&#xff0c;旨在改善文本引导的3D域适应和生成&#xff0c;以及解决现有方法在这些任务中存在的问题&#xff0c;如 inflexibility&#xff08;缺乏灵活性&#xff09;、instability&#xff08;不稳定性&#xff09;和low fidelity&…

Linux驱动学习—设备树及设备树下的platform总线

1、什么是设备树&#xff1f; 设备树是一种描述硬件资源的数据结构。他通过bootloader将硬件资源传给内核&#xff0c;使得内核和硬件资源 描述相对独立。 2、设备树的由来 2.1 平台总线的由来 要想了解为什么会有设备树&#xff0c;设备树是怎么来的&#xff0c;我们就要先…

计算机毕业论文内容参考|基于Apriori算法的门诊药物推荐系统的设计与实现

文章目录 摘要:前言相关技术与方法介绍系统分析系统设计系统实现系统测试与优化总结与展望摘要: 本文详细介绍了一种基于Apriori算法的门诊药物推荐系统的设计与实现。该系统利用Apriori算法挖掘患者就诊记录中的药物关联规则,为医生提供药物推荐,从而优化治疗方案。文章首…

2023春季李宏毅机器学习笔记 01 :正确认识 ChatGPT

资料 课程主页&#xff1a;https://speech.ee.ntu.edu.tw/~hylee/ml/2023-spring.phpGithub&#xff1a;https://github.com/Fafa-DL/Lhy_Machine_LearningB站课程&#xff1a;https://space.bilibili.com/253734135/channel/collectiondetail?sid2014800 一、对Chatgpt的误解…

python旅游大数据分析可视化大屏 游客分析+商家分析+舆情分析 计算机毕业设计(附源码)Flask框架✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

[GKCTF 2020]ez三剑客-eztypecho

[GKCTF 2020]ez三剑客-eztypecho 考点&#xff1a;Typecho反序列化漏洞 打开题目&#xff0c;发现是typecho的CMS 尝试跟着创建数据库发现不行&#xff0c;那么就搜搜此版本的相关信息发现存在反序列化漏洞 参考文章 跟着该文章分析来&#xff0c;首先找到install.php&#xf…

grep -A -B -C 输出匹配行及相邻行

grep -A -B -C 输出匹配行及相邻行 grep --help 摘抄&#x1f447; 文件控制&#xff1a; -B, --before-context数值 打印前面 <数值> 行上下文-A, --after-context数值 打印后面 <数值> 行上下文-C, --context数值 打印前后 <数值> 行上下文 文件控制&#…

python小工具之弱密码检测工具

一、引用的python模块 Crypto&#xff1a; Python中一个强大的加密模块&#xff0c;提供了许多常见的加密算法和工具。它建立在pyc.ypodome或pyc.ypto等底层加密库之上&#xff0c;为Python程序员提供了简单易用的API&#xff0c;使其可以轻松地实现各种加密功能。 commands…

STM32MP157D-DK1 Qt程序交叉编译与运行测试

上篇文章介绍了STM32MP157D-DK1开发板Qt镜像的构建&#xff0c;通过在Ubuntu中重新编译带有Qt功能的系统来实现。 本篇在上篇的基础上&#xff0c;继续搭建Qt的交叉编译环境&#xff0c;实现Qt程序在Ubuntu中编译&#xff0c;在STM32MP157板子中运行。 1 编译安装SDK 在上篇…

计算机组成原理-总线的性能指标

文章目录 总览总线周期 总线时钟周期 总线工作频率 总线时钟频率总线宽度 总线带宽例题串行总线和并行总线的速度&#xff08;带宽&#xff09;比较总线复用 信号线数总结 总览 总线周期 总线时钟周期 总线工作频率 总线时钟频率 一个总线周期就是指利用总线传输一组数据需要的…

C#线程基础(线程启动和停止)

目录 一、关于线程 二、示例 三、生成效果 一、关于线程 在使用多线程前要先引用命名空间System.Threading&#xff0c;引用命名空间后就可以在需要的地方方便地创建并使用线程。 创建线程对象的构造方法中使用了ThreadStart()委托&#xff0c;当线程开始执行时&#xff0c…

LeetCode第32题 : 最长有效括号

题目介绍 给你一个只包含 ( 和 ) 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 示例 1&#xff1a; 输入&#xff1a;s "(()" 输出&#xff1a;2 解释&#xff1a;最长有效括号子串是 "()" 示例 2&#xf…