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

目录

一、界面演示

二、设备列表

三、抖动单元格

四、设备模型

五、设备编辑


本项目的交流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.mzph.cn/news/846340.shtml

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

相关文章

Java利用POI绘制表格

前提需求 最近公司要求写一些记录的表格&#xff0c;并且带有导出功能。再深入学习后&#xff0c;表格的底层其实就是list遍历塞值&#xff0c;导出功能的话可以由前端&#xff0c;后端实现&#xff0c;但技多不压身嘛&#xff0c;这里我自己就写了后端的导出功能&#xff0c;…

day-37 电话号码的字母组合

思路 设置一个char型的二维数组&#xff0c;每次从号码对应的子母中选出一个&#xff0c;拼接在一起即可 解题方法 注意&#xff1a;有的数字对应三个字母&#xff0c;有的对应四个字母 Code class Solution {public char arr[][]{{a,b,c, },{d,e,f, },{g,h,i, },{j,k,l, },…

虚拟现实环境下的远程教育和智能评估系统(三)

本周继续进行开发工具的选择与学习&#xff0c;基本了解了以下技术栈的部署应用&#xff1b; 一、Seata&#xff1a; Seata&#xff08;Simple Extensible Autonomous Transaction Architecture&#xff09;是一款开源的分布式事务解决方案&#xff0c;旨在提供高性能和简单易…

力扣575. 分糖果

题目&#xff1a; Alice 有 n 枚糖&#xff0c;其中第 i 枚糖的类型为 candyType[i] 。Alice 注意到她的体重正在增长&#xff0c;所以前去拜访了一位医生。 医生建议 Alice 要少摄入糖分&#xff0c;只吃掉她所有糖的 n / 2 即可&#xff08;n 是一个偶数&#xff09;。Alic…

智能监控技术助力山林生态养鸡:打造智慧安全的养殖新模式

随着现代科技的不断发展&#xff0c;智能化、自动化的养殖方式逐渐受到广大养殖户的青睐。特别是在山林生态养鸡领域&#xff0c;智能化监控方案的引入不仅提高了养殖效率&#xff0c;更有助于保障鸡只的健康与安全。视频监控系统EasyCVR视频汇聚/安防监控视频管理平台在山林生…

攻防世界---misc---心仪的公司

1、题目描述 2、下载附件是一个流量包 方法一&#xff1a; 1、用winhex分析&#xff0c;ctrlf搜索flag 2、尝试将搜索到的flag拿去提交&#xff0c;但是不对 3、担心flag不是长flag&#xff0c;做题多了你就会发现有些flag会是fl4g这种&#xff0c;为了可以稍微全面一点&…

6.2 休息日 背包问题总结

就目前所遇到的01背包与完全背包作总结。 01背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 二维dp数组01背包 动规五部曲 1.确定…

IO流(2)

缓冲流 字节缓冲流 利用字节缓冲区拷贝文件&#xff0c;一次读取一个字节&#xff1a; public class test {public static void main(String [] args) throws IOException {//利用字节缓冲区来拷贝文件BufferedInputStream bisnew BufferedInputStream(new FileInputStream(&…

STM32作业实现(四)光敏传感器

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

Java网络编程(上)

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:Java文件IO&#x1f649; &#x1f439;今日诗词:来如春梦几多时&#xff1f;去似朝云无觅处&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&a…

【Qt知识】disconnect

在Qt框架中&#xff0c;disconnect函数用于断开信号与槽之间的连接。当不再需要某个信号触发特定槽函数时&#xff0c;或者为了防止内存泄漏和重复执行问题&#xff0c;你可以使用disconnect来取消这种关联。disconnect函数的基本用法可以根据不同的需求采用多种形式&#xff0…

C++对C的增强

1、作用域运算符 ::解决归属问题&#xff08;谁是谁的谁&#xff09; 可以优先使用全局变量 2、命名空间 使用关键字namespace&#xff0c;控制标名称的作用域。 命名空间的本质&#xff1a;对符号常量、变量、函数、结构、枚举、类和对象等等进行封装 1、创建一个命名空间…

图解DSPy:Prompt的时代终结者?!

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调重新阅读。而最新科技&#xff08;Mamba&#xff0c;xLSTM,KAN&#xff09;则提供了大模…

多元联合分布建模 Copula python实例

多元联合分布建模 Copula python实例 目录 库安装 实例可视化代码 库安装 pip install copulas 实例可视化代码 import numpy as np import pandas as pd from copulas.multivariate import GaussianMultivariate# Generate some example data np.random.seed(42) data = …

ChatTTS:开源最强文本转真人语音工具

目录 1.前言 2.详细介绍 2.1 什么是ChatTTS 2.2 项目地址: 2.3 应用特点: 3.如何安装和使用 3.1.谷歌colab 3.1.1.点击链接 3.1.2 进行保存 3.1.3 按照流程依次点击运行 3.1.4 填写自己需要转的文字 3.2 本地运行 3.2.1 下载或克隆项目源码到本地 3.2.2 …

算法每日一题(python,2024.05.31)

题目来源&#xff08;力扣. - 力扣&#xff08;LeetCode&#xff09;&#xff0c;简单&#xff09; 解题思路&#xff1a; 二次遍历&#xff0c;第一次遍历用哈希表记录每个字母的出现次数&#xff0c;出现一次则将它的value值赋为True&#xff0c;将它的下标赋为key值&#x…

HTTPS加密

一.加密是什么 加密就是把明文(要传输的信息)进行一系列的变换,生成密文. 有加密就有解密,解密就是把密文进行一系列的变换,生成明文. 在这个加密和解密过程中,往往需要一个或多个中间数据,辅助进行这个过程,这样的数据称为密钥. 加密解密到如今已经发展成了一个独立的学科 : 密…

基于Springboot开发的外卖餐购项目(后台管理+消费者端)

免费获取方式↓↓↓ 项目介绍039&#xff1a; 系统运行 后端登录页: http://localhost:8081/backend/page/login/login.html 消费端请求:消费端主页: http://localhost:8081/front/index.html 管理员账号 admin 123456 消费者不需要登录 采用技术栈 前端&#xff1a;Eleme…

力扣20 有效的括号

给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括…

【智能算法】红嘴蓝喜鹊优化算法(RBMO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2024年&#xff0c;S Fu受到自然界中红嘴蓝喜鹊社会行为启发&#xff0c;提出了红嘴蓝喜鹊优化算法&#xff08;Red-billed Blue Magpie Optimizer, RBMO&#xff09;。 2.算法原理 2.1算…