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.hqwc.cn/news/316374.html

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

相关文章

maven、springboot项目编译打包本地jar、第三方jar包

0. 引言 一般我们在maven项目中都是通过引入pom坐标的形式来引入第三方jar包&#xff0c;但某些场景下&#xff0c;第三方是直接提供的jar包文件&#xff0c;这就需要我们从本地引入第三方包并进行打包。所以我们今天来看下如何进行本地引入第三方包操作 1. 步骤 1、在项目下…

JVM是如何基于虚拟机栈运行的

众所周知&#xff1a;JVM执行Java代码是靠执行引擎实现的。执行引擎有两套解释器&#xff1a;字节码解释器、模板解释器。字节码解释器比较简单&#xff0c;不多说&#xff0c;看图。本篇文章咱们讨论模板解释器执行Java代码的底层原理。 早些年研究模板解释器看到R大用汇编写的…

你知道vue中key的原理吗?说说你对它的理解

一、Key是什么 开始之前&#xff0c;我们先还原两个实际工作场景 当我们在使用v-for时&#xff0c;需要给单元加上key <ul><li v-for"item in items" :key"item.id">...</li> </ul>用new Date()生成的时间戳作为key&#xff0c…

数据中心网络架构

参考&#xff1a; 一文读懂胖树 数据中心网络架构VL2详解 数据中心网络拓扑设计目标 总体目标 业务可以部署在任意的服务器上可以根据需要动态扩展或者缩小服务器规模 网络角度 均衡负载且高性能&#xff1a;服务器之间的性能仅受限于服务器网卡&#xff0c;而不是链路性能…

《Ensemble deep learning: A review》阅读笔记

论文标题 《Ensemble deep learning: A review》 集成深度学习&#xff1a; 综述 作者 M.A. Ganaie 和 Minghui Hu 来自印度理工学院印多尔分校数学系和南洋理工大学电气与电子工程学院 本文写的大而全。 初读 摘要 集成学习思想&#xff1a; 结合几个单独的模型以获得…

【GitHub】ssh: connect to host github.com port 22: Connection refused

本地使用git上传GitHub仓库时发现的一个报错&#xff0c;以为是本机连不上github了&#xff0c;ping过后发现能够正常访问&#xff0c;于是上网找到了一个很完美的解决方案 原因&#xff1a;22端口被占用或被防火墙屏蔽 解决方法&#xff1a;切换GitHub的443端口 1.首先找到…

【数据结构】手撕排序(排序的概念及意义、直接插入和希尔排序的实现及分析)

目录 一、排序的概念及其运用 1.1排序的概念 1.2排序运用 1.3 常见的排序算法 二、插入排序 2.1基本思想&#xff1a; 2.2直接插入排序&#xff1a; 2.3步骤&#xff1a; 2.4直接插入排序的实现 三、希尔排序( 缩小增量排序 ) 3.1希尔排序的发展历史 3.2 希尔…

跟着cherno手搓游戏引擎【2】:日志系统spdlog和premake的使用

配置&#xff1a; 日志库文件github&#xff1a; GitHub - gabime/spdlog: Fast C logging library. 新建vendor文件夹 将下载好的spdlog放入 配置YOTOEngine的附加包含目录&#xff1a; 配置Sandbox的附加包含目录&#xff1a; 包装spdlog&#xff1a; 在YOTO文件夹下创建…

研讨会分享 | 非遗文化的守正创新与数字化传播

12月17日&#xff0c;大势智慧受邀参与由南方科技大学创新创意设计学院&#xff08;以下简称“设计学院”&#xff09;举办的“非遗文化的守正创新与数字化传播”主题研讨会&#xff0c;与来自非遗、文化、设计、展陈、技术等领域的一众专家学者&#xff0c;共同探讨可持续视阈…

如何充值GPT会员账号?

详情点击链接&#xff1a;如何充值GPT会员账号&#xff1f; 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Claude2二定制自己的…

nginx 一、安装与conf浅析

文章目录 一、安装nginxdocker方式安装linux方式安装Ubuntu 或 Debian 系统&#xff1a;CentOS 或 RHEL 系统&#xff1a; macOS 系统&#xff08;使用 Homebrew&#xff09;&#xff1a;Windows 系统&#xff1a; 二、nginx.conf浅析 一、安装nginx docker方式安装 docker s…