物联网实战--平台篇之(十二)设备管理前端

目录

一、界面演示

二、设备列表

三、抖动单元格

四、设备模型

五、设备编辑


本项目的交流QQ群:701889554

物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html

物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html

一、界面演示

设备管理演示

二、设备列表

        参考了几个现有的智能家居相关的APP,他们的设备管理模式基本差不多,都是这种单元格的方式,在QML里,就是GridView组件了,以下是设备列表相关的前端QML代码:

import QtQuick 2.7
import QtQuick.Controls 2.0
import "../base"Rectangle
{property var groupName: ""id:id_rootRectheight: parent.heightwidth: parent.width-20color: "transparent"anchors.horizontalCenter: parent.horizontalCenterDevDelDialog//设备删除对话框{id:id_devDelDialogonSiqOkClicked: {var dev_list=[]var ptr=0for(var i=0; i<id_cellModel.count; i++){if(id_cellModel.get(i).select_state)//选中{dev_list[ptr++]=id_cellModel.get(i).dev_sn}}theCenterMan.requestDelDevice(dev_list)funClose()}}DevRenameDialog//设备重命名对话框{id:id_devRenameDialogonSiqOkClicked: {theCenterMan.requestRenameDevice(devSn, text)funClose()}} GridView {id:id_gridViewproperty int dragItemIndex: -1property var dragActive: falseproperty int selectCnts: 0property var sortFlag: falseanchors.fill: parentclip: truecacheBuffer: 10000implicitWidth: 150implicitHeight: 150cellWidth: width*0.5cellHeight: cellWidth*0.8move:Transition {NumberAnimation { properties: "x,y"; duration: 200 }}moveDisplaced:Transition {NumberAnimation { properties: "x,y"; duration: 200 }}model: ListModel{id:id_cellModel}delegate: Rectangle{//单元格矩形id:id_cellRect
//            property var selectState: falsecolor: "transparent"
//            border.color: "blue"
//            border.width: 1width: id_gridView.cellWidthheight: id_gridView.cellHeightRectangle //拖动矩形{id:id_dragRectradius: 12width: id_cellRect.width-15height:id_cellRect.height-15anchors.centerIn:id_cellRectcolor: id_moveMouseArea.drag.active ? "transparent" : "white"onParentChanged: {if(parent!=null){theCenterMan.showSimpleView(dev_sn, id_dragRect)}         } Rectangle{  //勾选框id:id_selectRectwidth: 24height: widthradius: width/2border.width: 1border.color: "#D0D0D0"visible: id_gridView.dragActiveanchors{top:parent.toptopMargin:15right:parent.rightrightMargin:15}Image {id: id_selectImageanchors.fill: parentvisible: select_statemipmap: truesource: "qrc:/imagesRC/mainImages/check.png"}}MouseArea{id:id_moveMouseAreaanchors.fill: parent
//                    drag.target: id_dragRectdrag.axis: Drag.YAxis | Drag.XAxisdrag.onActiveChanged: {if (id_moveMouseArea.drag.active) {id_gridView.dragItemIndex = index;console.log("drag index=", index)}id_dragRect.Drag.drop();}onClicked: {if(id_gridView.dragActive){select_state=!select_stateif(select_state)id_gridView.selectCnts++elseid_gridView.selectCnts--
//                            console.log("selectCnts=", id_gridView.selectCnts)}}onPressAndHold: {id_gridView.dragActive=trueselect_state=trueid_gridView.selectCnts=1}onReleased: {
//                        drag.target=null
//                        id_gridView.dragActive=false
//                        id_dropAnimation.stop()}}states: [State {when: id_dragRect.Drag.activeParentChange {target: id_dragRectparent: id_rootRect}AnchorChanges {target: id_dragRectanchors.horizontalCenter: undefinedanchors.verticalCenter: undefined}}]Drag.active: id_moveMouseArea.drag.activeDrag.hotSpot.x: id_dragRect.width / 2Drag.hotSpot.y: id_dragRect.height / 2}DropArea {  //拖拽id: dropAreaanchors.fill: parentonDropped:{console.log("onDropped")var other_index = id_gridView.indexAt(id_moveMouseArea.mouseX + id_cellRect.x, id_moveMouseArea.mouseY + id_cellRect.y);console.log("other_index:",other_index,"id_gridView.dragItemIndex:",id_gridView.dragItemIndex);if(other_index!==id_gridView.dragItemIndex && other_index>=0){id_cellModel.move(id_gridView.dragItemIndex,other_index, 1);id_gridView.sortFlag=true//有排序动作}}Rectangle {id: dropRectangleanchors.centerIn:parentwidth: id_dragRect.widthheight: id_dragRect.heightcolor: "transparent"radius: 12states: [State {when: dropArea.containsDragPropertyChanges {target: dropRectanglecolor: "lightsteelblue"opacity:0.3}}]}//end Rectangle}//end dropPropertyAnimation { //抖动id:id_dropAnimationtarget: id_dragRectproperties: "rotation"from:-1.5to:1.5duration: 300easing.type: Easing.InOutExpoloops: 300 //循环次数onStopped: {target["rotation"] = 0 //显示归位}}Timer{interval: Math.random()*500; running: true; repeat: trueonTriggered: {if(id_gridView.dragActive){if(id_dropAnimation.stopped){id_dropAnimation.start()id_moveMouseArea.drag.target=id_dragRect         }}else{if(id_dropAnimation.started){id_dropAnimation.stop()id_moveMouseArea.drag.target=nullselect_state=falseid_gridView.selectCnts=0}}}}}//end delegate}//end GridViewRectangle  //设置栏{id:id_setRectwidth: parent.parent.widthheight: 60anchors{horizontalCenter:parent.horizontalCenterbottom:id_gridView.bottom}MouseArea{anchors.fill: parent //接收鼠标事件,避免选择背后的单元格}color: "#606060"
//        opacity: 0.5visible: id_gridView.dragActiveRow{height: id_setRect.heightwidth: id_setRect.width*0.9anchors{top:parent.toptopMargin:5horizontalCenter:id_setRect.horizontalCenter}spacing: (width-30*4)/3Repeater{model: ListModel{id:id_setModel}Rectangle{property var maskFlag: index===0 && id_gridView.selectCnts!==1height: 30width: heightradius: width/2color:  maskFlag ? "#808080" : "white"ImageButton01 {anchors.centerIn: parentwidth: parent.width*0.7height: widthmipmap: true source: img_srconSiqClickedLeft: {
//                            console.log("clicked index=", index)switch(index){case 0: //修改名称if(id_gridView.selectCnts===1){for(var i=0; i<id_cellModel.count; i++){if(id_cellModel.get(i).select_state)//选中{id_devRenameDialog.devSn=id_cellModel.get(i).dev_snid_devRenameDialog.oldName=theCenterMan.takeWorkDeviceName(id_devRenameDialog.devSn)//旧名称id_devRenameDialog.funOpen(id_devRenameDialog.oldName)break}}         }breakcase 1: //移动设备id_devMoveDialog.open()var dev_list=[]var ptr=0for(i=0; i<id_cellModel.count; i++){if(id_cellModel.get(i).select_state)//选中{dev_list[ptr++]=id_cellModel.get(i).dev_sn}}id_devMoveDialog.srcGroupName=groupNameid_devMoveDialog.moveDevList=dev_listbreakcase 2: //删除设备id_devDelDialog.funOpen()break                                    case 3:  //完成id_gridView.dragActive=false;if(id_gridView.sortFlag==true)//有排序动作{id_gridView.sortFlag=falsedev_list=[]for(i=0; i<id_cellModel.count; i++){dev_list[i]=id_cellModel.get(i).dev_sn}theCenterMan.requestSortDevice(groupName, dev_list)//排序}break}}}Text{height: 25anchors{horizontalCenter:parent.horizontalCentertop:parent.bottomtopMargin:3}text: namefont.pointSize: 10font.family: "宋体"color:  maskFlag ? "#808080" : "white"}}}}}Connections{target: theCenterManonSiqAddDevice2Group:{if(group_name===groupName){id_cellModel.append({"dev_sn":dev_sn, "select_state":false})}}onSiqDelDevice:{for(var i=0; i<id_cellModel.count; i++){if(dev_sn===id_cellModel.get(i).dev_sn){id_cellModel.remove(i)break}}if(flag>0)  //删除完成{var dev_list=[]var ptr=0for(i=0; i<id_cellModel.count; i++){dev_list[ptr++]=id_cellModel.get(i).dev_sn}theCenterMan.requestSortDevice(groupName, dev_list)//重新排序}}onSiqClearDevice:{if(group_name===groupName){id_cellModel.clear()}}}Component.onCompleted: {var img_src="qrc:/imagesRC/mainImages/home/rename.png"var name="修改名称"id_setModel.append({"img_src":img_src, "name":name})img_src="qrc:/imagesRC/mainImages/home/move.png"name="移动设备"id_setModel.append({"img_src":img_src, "name":name})  img_src="qrc:/imagesRC/mainImages/home/del02.png"name="删除设备"id_setModel.append({"img_src":img_src, "name":name})  img_src="qrc:/imagesRC/mainImages/home/finish.png"name="完成"id_setModel.append({"img_src":img_src, "name":name})  }}

        在GridView里的重点其实就是拖拽排序功能了,跟之前的分组排序类似的,只不过网格拖拽复杂点,首先方向是X和Y两个方向;然后是抖动效果,在长按某个设备单元格后整体就会抖动起来,让用户直观地感受到是在编辑状态;最后是选中效果,用户选中后就会增加一个打勾按钮,本质就是图片显示条件设置了。

三、抖动单元格

        抖动使用的是动画组件,其中需要改变的对象是拖拽矩形,改变的属性是角度,范围从-1.5~1.5角度,如果停止后角度重置为0。在这里我们加个定时器用来管理所有单元格的抖动状态,在有设备被长按激活拖拽后,网格里所有的设备都要抖动起来,在定时器里启动;如果退出编辑去激活后,同样是在定时器内检测停止。

           PropertyAnimation { //抖动id:id_dropAnimationtarget: id_dragRectproperties: "rotation"from:-1.5to:1.5duration: 300easing.type: Easing.InOutExpoloops: 300 //循环次数onStopped: {target["rotation"] = 0 //显示归位}}Timer{interval: Math.random()*500; running: true; repeat: trueonTriggered: {if(id_gridView.dragActive){if(id_dropAnimation.stopped){id_dropAnimation.start()id_moveMouseArea.drag.target=id_dragRect         }}else{if(id_dropAnimation.started){id_dropAnimation.stop()id_moveMouseArea.drag.target=nullselect_state=falseid_gridView.selectCnts=0}}}}

四、设备模型

        设备模型是我们后续开发新硬件产品所需要对应增加的内容,在其它平台上,很多称之为物模型,一般用json语句来描述设备的属性、状态和功能,这种模式的特点是通用性比较强,缺点是性能损耗大,深度定制使用体验不佳,所以只能定义一些较为简单的设备模型,复杂的调试起来很麻烦。那么,我们这边的思想还是以代码驱动为核心,物模型后端逻辑直接用C/C++实现,前端界面用QML实现,配套使用,下面举例说明。

        

        如上图所示,modelCpp存放的是物模型后端代码,modelQml存放的是物模型前端代码,由于各个物模型有一定相似性,所以都会定义一个基本的模型,具体设备模型再继承于这个基础模型,在这里我们以后面要实现的净化器为例,定义型号为AP01,那么他的ModelAp01,继承于BaseModel,具体的后面完善净化器项目的时候再说明。

        同样的,前端代码也是这种模式,SimpleAp01.qml继承于BaseSimpleView,带Simple说明是简易模型,就是每个单元格内显示的内容,对于每个物模型有两个界面,一个是这里所说的简易界面,另一个是详情界面,就是之前净化器项目那个可以具体控制操作的界面。

        对于模型的显示也是一个需要技巧的地方,首先要知道,每个界面都需要有一个父组件/窗口,这样才能显示,对于设备模型前端来讲,它的父窗口其实就是那个可以被拖拽的单元格了,所以我们要显示模型的时候,就是将这个单元格地址传到模型里去,然后模型内部自己主动显示在单元格上,下面通过代码了解这一流程。

        首先在网格单元格内,当单元格创建完成后,会触发onParentChanged信号,在这里就可以根据设备sn将单元格矩形最为父窗口,把指针传入模型后端。

        然后我们来看看控制中心的showSimpleView函数的内容,如下所示,就是根据dev_sn找到这个模型实例对象,然后调用模型的showSimple进行显示,注意,这里继续将父窗口指针继续传递。

        接下来是重头戏,如下图所示,是不是感觉似曾相识,跟主程序显示是一样的,每个模型内部都有一个QML显示引擎,通过这里的配置,就可以实现模型内容的前后端交互了,在这里有两个接口,theModelAp01和theCenterMan,其中通过theCenterMan可以调用CenterMan类内的相关功能。函数末尾就是加载SimpleAp01这个模型文件。

        到此为止,物模型并不能显示,因为上图中的父窗口指针只是保存在C++后端了,并没有跟前端的QML有什么关联,这就引出了最后一步,就是在具体的QML模型文件内调用takeModelParent(),将刚才保存在后端的父窗口指针赋值到当前模型的parent属性,这样就形成了闭环,完成了模型显示的功能。

五、设备编辑

        当用户长按某个单元格后,除了单元格会抖动起来以外,底部还会出现一个设置栏,目前定义的功能是修改名称、移动设备和删除设备,这里比较复杂的还是上一篇所写的后台管理,特别是移动设备功能;那么对于前端代码主要做个展示,具体内容自己阅读理解应该问题不大。

   Rectangle  //设置栏{id:id_setRectwidth: parent.parent.widthheight: 60anchors{horizontalCenter:parent.horizontalCenterbottom:id_gridView.bottom}MouseArea{anchors.fill: parent //接收鼠标事件,避免选择背后的单元格}color: "#606060"
//        opacity: 0.5visible: id_gridView.dragActiveRow{height: id_setRect.heightwidth: id_setRect.width*0.9anchors{top:parent.toptopMargin:5horizontalCenter:id_setRect.horizontalCenter}spacing: (width-30*4)/3Repeater{model: ListModel{id:id_setModel}Rectangle{property var maskFlag: index===0 && id_gridView.selectCnts!==1height: 30width: heightradius: width/2color:  maskFlag ? "#808080" : "white"ImageButton01 {anchors.centerIn: parentwidth: parent.width*0.7height: widthmipmap: true source: img_srconSiqClickedLeft: {
//                            console.log("clicked index=", index)switch(index){case 0: //修改名称if(id_gridView.selectCnts===1){for(var i=0; i<id_cellModel.count; i++){if(id_cellModel.get(i).select_state)//选中{id_devRenameDialog.devSn=id_cellModel.get(i).dev_snid_devRenameDialog.oldName=theCenterMan.takeWorkDeviceName(id_devRenameDialog.devSn)//旧名称id_devRenameDialog.funOpen(id_devRenameDialog.oldName)break}}         }breakcase 1: //移动设备id_devMoveDialog.open()var dev_list=[]var ptr=0for(i=0; i<id_cellModel.count; i++){if(id_cellModel.get(i).select_state)//选中{dev_list[ptr++]=id_cellModel.get(i).dev_sn}}id_devMoveDialog.srcGroupName=groupNameid_devMoveDialog.moveDevList=dev_listbreakcase 2: //删除设备id_devDelDialog.funOpen()break                                    case 3:  //完成id_gridView.dragActive=false;if(id_gridView.sortFlag==true)//有排序动作{id_gridView.sortFlag=falsedev_list=[]for(i=0; i<id_cellModel.count; i++){dev_list[i]=id_cellModel.get(i).dev_sn}theCenterMan.requestSortDevice(groupName, dev_list)//排序}break}}}Text{height: 25anchors{horizontalCenter:parent.horizontalCentertop:parent.bottomtopMargin:3}text: namefont.pointSize: 10font.family: "宋体"color:  maskFlag ? "#808080" : "white"}}}}}

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

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

相关文章

Python | 类的实现

权限控制 私有属性访问 装饰器property 和gender.setter 有个小错误&#xff1a;应该使用我的属性名称 多态&#xff1a; 编写对象 继承多个父类 子类&#xff1a; 面对对象的方法重写&#xff1a; 例如&#xff1a; 多态&#xff1a; object类 直接输出对象名相当于调用_str_…

QT系列教程(7) QLineEdit介绍

简介 QLineEdit属于输入插件&#xff0c;用来实现单行录入。支持几种录入模式。 Normal表示正常录入,录入的信息会显示在QLineEdit上。 Password表示密码录入的方式&#xff0c;录入的信息不显示QLineEdit&#xff0c;只是通过黑色圆点显示。 NoEcho 表示不显示录入信息&am…

爱德蒙得洛希尔:深耕亚洲市场,开启中国投资新篇章!

爱德蒙得洛希尔资产管理&#xff08;法国&#xff09;有限公司&#xff08;以下简称“爱德蒙得洛希尔”&#xff09;是一家具有悠久历史和全球业务网络的金融企业&#xff0c;由洛希尔家族于1953年在法国巴黎创立。作为一家主要从事私人银行和资产管理业务的金融集团&#xff0…

Mac专用投屏工具:AirServer 7 for Mac 激活版下载

AirServer 7 是一款在 Windows 和 macOS 平台上运行的强大的屏幕镜像和屏幕录制软件。它能够将 iOS 设备、Mac 以及其他 AirPlay、Google Cast 和 Miracast 兼容设备的屏幕镜像到电脑上&#xff0c;并支持高质量的录制功能。总的来说&#xff0c;AirServer 7 是一款功能全面的屏…

tcp链接中的三次挥手是什么原因

一、tcp链接中的正常四次挥手过程&#xff1f; 刚开始双方都处于 ESTABLISHED 状态&#xff0c;假如是客户端先发起关闭请求。四次挥手的过程如下&#xff1a; 1、客户端打算关闭连接&#xff0c;此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文&#xff0c;也即 FIN 报文…

mediasoup基础概览

提示&#xff1a;本文为之前mediasoup基础介绍的优化 mediasoup基础概览 架构&#xff1a;2.特性&#xff1a;优点缺点 3.mediasoup常见类介绍js部分c 4.mediasoup类图5.业务类图 Mediasoup 是一个构建在现代 Web 技术之上的实时通信&#xff08;RTC&#xff09;解决方案&#…

openssl 常用命令demo

RSA Private Key的结构&#xff08;ASN.1&#xff09; RSAPrivateKey :: SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- …

三丰云免费服务器

三丰云网址&#xff1a; https://www.sanfengyun.com 可申请免费云服务器&#xff0c;1核/1G内存/5M宽带/有公网IP/10G SSD硬盘/免备案。 收费云服务器&#xff0c;买2年送1年&#xff0c;有很多优惠

空调外机清洁机器人设计

现在的空调&#xff0c;有很多安装在高层&#xff0c;一旦安装使用后&#xff0c;外机几乎不可能再清洗。因为费用高&#xff0c;清洁工人的钱应该是好几百还不止&#xff1b;清洁风险高&#xff0c;空调师傅需要高空作业&#xff0c;如果发生意外业主难以承担。但空调运行几年…

欧科云链:Web3.0时代 具备链上数据分析能力的公司愈发凸显其价值

在当今激烈的市场竞争中&#xff0c;新兴互联网领域迅速崛起&#xff0c;Web2.0已相对成熟&#xff0c;用户创造数据&#xff0c;但不拥有数据。被视为新的价值互联网的Web3.0&#xff0c;赋予用户真正的数据自主权&#xff0c;它的到来被认为是打破Web2.0垄断的机遇。 在Web3…

kafka 发送文件二进制流及使用header发送附属信息

文章目录 背景案例发送方接收方 背景 需要使用kafka发送文件二进制以及附属信息 案例 发送方 import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord;import java.io.InputStream; import java.nio.charset.S…

Ubuntu20.04安装ffmpeg,并捕获视频流

工控机&#xff1a;幻影峡谷 系统&#xff1a;Ubuntu20.04 摄像头&#xff1a;杰瑞微通环星光USB摄像头记录一下使用ffmpeg拉取视频流的原因&#xff1a;刚开始用的是ubuntu系统自带的 茄子 软件&#xff0c;但是视频流很卡&#xff08;非常卡&#xff0c;基本上不能用&#xf…

开箱即用的Spring Boot 企业级开发平台【毕设项目推荐】

项目概述 基于 Spring 实现的通用权限管理平台&#xff08;RBAC模式&#xff09;。整合最新技术高效快速开发&#xff0c;前后端分离模式&#xff0c;开箱即用。 核心模块包括&#xff1a;用户、角色、职位、组织机构、菜单、字典、日志、多应用管理、文件管理、定时任务等功能…

【kubernetes】关于k8s集群如何将pod调度到指定node节点(亲和与反亲和等)

目录 一、调度约束 1.1K8S的 List-Watch 机制 ⭐⭐⭐⭐⭐ 1.1.1Pod 启动典型创建过程 二、调度过程 2.1Predicate&#xff08;预选策略&#xff09; 常见的算法 2.2priorities&#xff08;优选策略&#xff09;常见的算法 三、k8s将pod调度到指定node的方法 3.1指定…

css动态导航栏鼠标悬停特效

charset "utf-8"; /*科e互联特效基本框架CSS*/ body, ul, dl, dd, dt, ol, li, p, h1, h2, h3, h4, h5, h6, textarea, form, select, fieldset, table, td, div, input {margin:0;padding:0;-webkit-text-size-adjust: none} h1, h2, h3, h4, h5, h6{font-size:12px…

为什么GD32F303代码运行在flash比sram更快?

我们知道一般MCU的flash有等待周期&#xff0c;随主频提升需要插入flash读取的等待周期&#xff0c;以stm32f103为例&#xff0c;主频在72M时需要插入2个等待周期&#xff0c;故而代码效率无法达到最大时钟频率。 所以STM32F103将代码加载到sram运行速度更快。 但使用GD32F30…

20.Redis之缓存

1.什么是缓存&#xff1f; Redis 最主要的用途,三个方面:1.存储数据(内存数据库)2.缓存 【redis 最常用的场景】3.消息队列【很少见】 缓存 (cache) 是计算机中的⼀个经典的概念. 在很多场景中都会涉及到. 核⼼思路就是把⼀些常⽤的数据放到触⼿可及(访问速度更快)的地⽅, ⽅…

前端逆向之查看接口调用栈

一、来源 再分析前端请求接口数据的时候&#xff0c;其中有一个sid不知道是前端如何获取的&#xff0c;一般情况下只需要全局搜搜sid这个字符串或者请求接口的名称就可以了&#xff0c;基本都能找到sid的来源&#xff0c;但是今天这个不一样&#xff0c;搜什么都搜不到 接口地…

安装 ArchLinux 和 KDE Plasma 6 | 双系统/虚拟机

注&#xff1a;本文写于 2024/06/02 &#xff0c;ArchLinux 最新版为 2024.06.01 &#xff08;为什么用 Arch 懒得写了&#xff0c;给个别人写的链接&#xff1a;写在主力使用archlinux一年之后&#xff08;一&#xff09;Why Arch Linux? &#xff0c;总之就是pacman真香&…

【UnityShader入门精要学习笔记】第十六章 Unity中的渲染优化技术 (下)

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 减少需要处…