OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
欢迎关注『OpenCV-PyQT项目实战 @ Youcans』系列,持续更新中
OpenCV-PyQT项目实战(1)安装与环境配置
OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门
OpenCV-PyQT项目实战(3)信号与槽机制
OpenCV-PyQT项目实战(4)OpenCV 与PyQt的图像转换
OpenCV-PyQT项目实战(5)项目案例01:图像模糊
OpenCV-PyQT项目实战(6)项目案例02:滚动条应用
OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
文章目录
- OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
- 1. OpenCV实现鼠标框选
- 2. PyQt实现鼠标框选
- 2.1 支持鼠标事件的自定义 Label 类
- 2.2 例程7-2:支持鼠标事件的自定义 MyLabel 类
- 3. 项目实战:PyQt 鼠标框选
- 3.1 使用 QtDesigner 开发 PyQt5 图形界面
- 3.2. 项目主程序的开发
- 3.2.1 实例化 MyLabel 类
- 3.2.2 框选图像槽函数
- 3.2.3 信号与槽的连接
- 3.3 完整例程 OpenCVPyqt08.py
OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
本节介绍OpenCV和PyQt 实现鼠标框选的方法和案例,通过案例学习PyQt中的鼠标动作。
1. OpenCV实现鼠标框选
OpenCV中的函数 cv.selectROI 可以通过鼠标在图像上选择感兴趣的矩形区域(ROI,region of interest)。
函数原型:
cv.selectROI(windowName, img[, showCrosshair=true, fromCenter=false]) → retval
函数cv.selectROI创建一个显示窗口,允许用户使用鼠标选择ROI,按Space或Enter键完成选择,按c键取消选择。
参数说明:
● img:选择矩形区域的图像
● windowName:图像显示窗口的名称
● showCrosshair:默认值true,显示选择矩形的中心十字线
● fromCenter:默认值false,表示鼠标初始位置作为矩形的角点;true表示鼠标初始位置作为矩形的中心点
● retval:返回值为Rect矩形类,格式为元组 (x , y, w, h)
注意问题:
⒈函数的返回值是Rect矩形类,元组 (x , y, w, h) 分别表示矩形左上角顶点坐标 (x,y)、矩形的宽度w和高度h。
⒉函数创建一个窗口设置自己的鼠标回调,完成后将为使用的窗口设置一个空回调。
例程7-1:OpenCV鼠标框选
# 1.17 图像的裁剪 (ROI)img1 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 读取彩色图像(BGR)roi = cv2.selectROI(img1, showCrosshair=True, fromCenter=False)xmin, ymin, w, h = roi # 矩形裁剪区域 (ymin:ymin+h, xmin:xmin+w) 的位置参数imgROI = img1[ymin:ymin+h, xmin:xmin+w].copy() # 切片获得裁剪后保留的图像区域cv2.imshow("DemoRIO", imgROI)cv2.waitKey(0)
总结:OpenCV实现鼠标框选非常简单,但无法与 PyQt5 的 GUI 集成,只能在 OpenCV GUI 进行简单的操作。
2. PyQt实现鼠标框选
PyQt 中实现鼠标框选,本质上是鼠标动作的响应。
2.1 支持鼠标事件的自定义 Label 类
基本的 QLabel 类并不支持鼠标动作,因此需要自定义一个支持鼠标动作的 Label 类。
PyQt中,每个事件类型都被封装成相应的事件类,如鼠标事件为QMouseEvent,键盘事件为QKeyEvent等。而它们的基类是QEvent。
QMouseEvent 鼠标事件:
mousePressEvent (self, event):鼠标按下事件
mouseReleaseEvent (self, event):鼠标释放事件
mouseDoubieCiickEvent (self, event):双击鼠标事件
mouseMoveEvent(self,event):鼠标移动事件
enterEvent (self, event):鼠标进入控件事件
leaveEvent (self, event):鼠标离开控件事件
wheelEvent (self, event):滚轮滚动事件
QMouseEvent 鼠标方法:
ignore():让父控件继续收到鼠标事件
accept():不让父控件继续收到鼠标事件
x()、y():返回相对于控件空间的鼠标坐标值
pos():返回相对于控件空间的QPoint对象
localPos():返回相对于控件空间的QPointF对象
globalX()、globalY():返回相对于屏幕的x,y 坐标值
globalPos():返回相对于屏幕的QPoint对象
windowPos():返回相对于窗口的QPointF对象
screenPos():返回相对于屏幕的QPointF对象
timestamp():返回事件发生的时间;
QMouseEvent 鼠标事件的具体内容:
按下并释放鼠标按钮时,将调用以下方法:
mousePressEvent (self, event) - 鼠标键按下时调用;
mouseReleaseEvent (self, event) - 鼠标键公开时调用;
mouseDoubieCiickEvent (self, event) - 双击鼠标时调用。必须注意,在双击之前的其他事件。双击时的事件顺序如下:
- MouseButtonPress
- MouseButtonRelease
- MouseButtonDblClick
- MouseButtonPress
- MouseButtonRelease
2.2 例程7-2:支持鼠标事件的自定义 MyLabel 类
class MyLabel(QLabel):def __init__(self,parent=None):super(MyLabel, self).__init__(parent)self.x0 = 0self.y0 = 0self.x1 = 1self.y1 = 1self.flag = False# 鼠标点击事件def mousePressEvent(self, event):self.flag = True # 鼠标点击状态self.x0 = event.x()self.y0 = event.y()# 鼠标释放事件def mouseReleaseEvent(self, event):self.flag = False # 鼠标释放状态self.x1 = event.x()self.y1 = event.y()# 鼠标移动事件def mouseMoveEvent(self, event):if self.flag:self.x1 = event.x()self.y1 = event.y()self.update()# 绘制事件def paintEvent(self, event):super().paintEvent(event)rect = QRect(self.x0, self.y0, abs(self.x1 - self.x0), abs(self.y1 - self.y0))painter = QPainter(self)painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))painter.drawRect(rect)
3. 项目实战:PyQt 鼠标框选
本项目基于 PyQt5 GUI,使用鼠标框选 ROI 区域,在窗口中显示 ROI 区域,并对 ROI 进行处理。
3.1 使用 QtDesigner 开发 PyQt5 图形界面
本例的 UI 集自 uiDemo4.ui :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SzG6cP8y-1676360319630)(D:\OpenCVPyQt\images\OpenCV_20.png)]
于是,我们就完成了本项目的图形界面设计,将其保存为 uiDemo7.ui文件。
在 PyCharm中,使用 PyUIC 将选中的 uiDemo7.ui 文件转换为 .py 文件,就得到了 uiDemo7.py 文件。
3.2. 项目主程序的开发
3.2.1 实例化 MyLabel 类
自定义的 MyLabel 类不能在 QtDesigner 中创建,要在主程序中定义如下。
self.label_1 = MyLabel(self.centralwidget)self.label_1.setGeometry(QRect(20, 20, 400, 320))self.label_1.setAlignment(Qt.AlignCenter)self.label_1.setObjectName("label_1")
3.2.2 框选图像槽函数
click_pushButton槽函数,由 pushButton_3.clicked 按钮信号触发。
def click_pushButton_3(self): # 点击 pushButton_3 触发 框选图像print("pushButton_3")self.label_1.setGeometry(QRect(20, 20, 400, 320))hImg, wImg = self.img1.shape[:2]wLabel = self.label_1.width()hLabel = self.label_1.height()x0 = self.label_1.x0 * wImg//wLabely0 = self.label_1.y0 * hImg//hLabelx1 = self.label_1.x1 * wImg//wLabely1 = self.label_1.y1 * hImg//hLabelprint("hImg,wImg=({},{}), x1,y1=({},{})".format(hImg, wImg, hLabel, wLabel))print("x0,y0=({},{}), x1,y1=({},{})".format(x0, y0, x1, y1))self.img2 = np.zeros((self.img1.shape), np.uint8)self.img2[y0:y1, x0:x1, :] = self.img1[y0:y1, x0:x1, :]self.refreshShow(self.img2, self.label_2) # 刷新显示return
3.2.3 信号与槽的连接
# 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButtonself.pushButton_1.clicked.connect(self.click_pushButton_1) # 按钮触发:导入图像self.pushButton_2.clicked.connect(self.click_pushButton_2) # # 按钮触发:灰度显示self.pushButton_3.clicked.connect(self.click_pushButton_3) # # 按钮触发:框选图像self.pushButton_4.clicked.connect(self.trigger_actHelp) # # 按钮触发:调整色阶self.pushButton_5.clicked.connect(self.close) # 点击 # 按钮触发:关闭
3.3 完整例程 OpenCVPyqt08.py
# OpenCVPyqt08.py
# Demo07 of GUI by PyQt5
# Copyright 2023 Youcans, XUPT
# Crated:2023-02-12import sys
import cv2 as cv
import numpy as np
from PyQt5.QtCore import QObject, pyqtSignal, QPoint, QRect, qDebug, Qt
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from uiDemo8 import Ui_MainWindow # 导入 uiDemo8.py 中的 Ui_MainWindow 界面类class MyLabel(QLabel):def __init__(self,parent=None):super(MyLabel, self).__init__(parent)self.x0 = 0self.y0 = 0self.x1 = 1self.y1 = 1self.flag = False# 鼠标点击事件def mousePressEvent(self, event):self.flag = True # 鼠标点击状态self.x0 = event.x()self.y0 = event.y()# 鼠标释放事件def mouseReleaseEvent(self, event):self.flag = False # 鼠标释放状态self.x1 = event.x()self.y1 = event.y()# 鼠标移动事件def mouseMoveEvent(self, event):if self.flag:self.x1 = event.x()self.y1 = event.y()self.update()# 绘制事件def paintEvent(self, event):super().paintEvent(event)rect = QRect(self.x0, self.y0, abs(self.x1 - self.x0), abs(self.y1 - self.y0))painter = QPainter(self)painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))painter.drawRect(rect)class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类def __init__(self, parent=None):super(MyMainWindow, self).__init__(parent) # 初始化父类self.setupUi(self) # 继承 Ui_MainWindow 界面类self.label_1 = MyLabel(self.centralwidget)self.label_1.setGeometry(QRect(20, 20, 400, 320))self.label_1.setAlignment(Qt.AlignCenter)self.label_1.setObjectName("label_1")# 菜单栏self.actionOpen.triggered.connect(self.openSlot) # 连接并执行 openSlot 子程序self.actionSave.triggered.connect(self.saveSlot) # 连接并执行 saveSlot 子程序self.actionHelp.triggered.connect(self.trigger_actHelp) # 连接并执行 trigger_actHelp 子程序self.actionQuit.triggered.connect(self.close) # 连接并执行 trigger_actHelp 子程序# 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButtonself.pushButton_1.clicked.connect(self.click_pushButton_1) # 按钮触发:导入图像self.pushButton_2.clicked.connect(self.click_pushButton_2) # # 按钮触发:灰度显示self.pushButton_3.clicked.connect(self.click_pushButton_3) # # 按钮触发:框选图像self.pushButton_4.clicked.connect(self.trigger_actHelp) # # 按钮触发:调整色阶self.pushButton_5.clicked.connect(self.close) # 点击 # 按钮触发:关闭# 初始化self.img1 = np.ndarray(()) # 初始化图像 ndarry,用于存储图像self.img2 = np.ndarray(()) # 初始化图像 ndarry,用于存储图像self.img1 = cv.imread("../images/Lena.tif") # OpenCV 读取图像self.refreshShow(self.img1, self.label_1)# self.refreshShow(self.img1, self.label_2)returndef click_pushButton_1(self): # 点击 pushButton_1 触发self.img1 = self.openSlot() # 读取图像self.img2 = self.img1.copy()print("click_pushButton_1", self.img1.shape)self.refreshShow(self.img1, self.label_1) # 刷新显示returndef click_pushButton_2(self): # 点击 pushButton_2 触发print("pushButton_2")self.img2 = cv.cvtColor(self.img2, cv.COLOR_BGR2GRAY) # 图片格式转换:BGR -> Grayself.refreshShow(self.img2, self.label_2) # 刷新显示returndef click_pushButton_3(self): # 点击 pushButton_3 触发 框选图像print("pushButton_3")self.label_1.setGeometry(QRect(20, 20, 400, 320))hImg, wImg = self.img1.shape[:2]wLabel = self.label_1.width()hLabel = self.label_1.height()x0 = self.label_1.x0 * wImg//wLabely0 = self.label_1.y0 * hImg//hLabelx1 = self.label_1.x1 * wImg//wLabely1 = self.label_1.y1 * hImg//hLabelprint("hImg,wImg=({},{}), x1,y1=({},{})".format(hImg, wImg, hLabel, wLabel))print("x0,y0=({},{}), x1,y1=({},{})".format(x0, y0, x1, y1))self.img2 = np.zeros((self.img1.shape), np.uint8)self.img2[y0:y1, x0:x1, :] = self.img1[y0:y1, x0:x1, :]print(self.img2.shape)# cv.imshow("Demo", self.img2)# key = cv.waitKey(0) # 等待下一个按键命令# self.gRightLayout.removeWidget(self.label_2) # 删除原有 labelCover 控件及显示图表# sip.delete(self.labelCover) # 删除控件 labelCover# self.img2 = np.zeros(self.img1.shape, np.int8)self.refreshShow(self.img2, self.label_2) # 刷新显示returndef refreshShow(self, img, label):print(img.shape, label)qImg = self.cvToQImage(img) # OpenCV 转为 PyQt 图像格式# label.setScaledContents(False) # 需要在图片显示之前进行设置label.setPixmap((QPixmap.fromImage(qImg))) # 加载 PyQt 图像returndef openSlot(self, flag=1): # 读取图像文件# OpenCV 读取图像文件fileName, _ = QFileDialog.getOpenFileName(self, "Open Image", "../images/", "*.png *.jpg *.tif")if flag==0 or flag=="gray":img = cv.imread(fileName, cv.IMREAD_GRAYSCALE) # 读取灰度图像else:img = cv.imread(fileName, cv.IMREAD_COLOR) # 读取彩色图像print(fileName, img.shape)return imgdef saveSlot(self): # 保存图像文件# 选择存储文件 dialogfileName, tmp = QFileDialog.getSaveFileName(self, "Save Image", "../images/", '*.png; *.jpg; *.tif')if self.img1.size == 1:return# OpenCV 写入图像文件ret = cv.imwrite(fileName, self.img1)if ret:print(fileName, self.img.shape)returndef cvToQImage(self, image):# 8-bits unsigned, NO. OF CHANNELS=1if image.dtype == np.uint8:channels = 1 if len(image.shape) == 2 else image.shape[2]if channels == 3: # CV_8UC3# Create QImage with same dimensions as input MatqImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)return qImg.rgbSwapped()elif channels == 1:# Create QImage with same dimensions as input MatqImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)return qImgelse:qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])return QImage()def qPixmapToCV(self, qPixmap): # PyQt图像 转换为 OpenCV图像qImg = qPixmap.toImage() # QPixmap 转换为 QImageshape = (qImg.height(), qImg.bytesPerLine() * 8 // qImg.depth())shape += (4,)ptr = qImg.bits()ptr.setsize(qImg.byteCount())image = np.array(ptr, dtype=np.uint8).reshape(shape) # 定义 OpenCV 图像image = image[..., :3]return imagedef trigger_actHelp(self): # 动作 actHelp 触发QMessageBox.about(self, "About","""数字图像处理工具箱 v1.0\nCopyright YouCans, XUPT 2023""")returnif __name__ == '__main__':app = QApplication(sys.argv) # 在 QApplication 方法中使用,创建应用程序对象myWin = MyMainWindow() # 实例化 MyMainWindow 类,创建主窗口myWin.show() # 在桌面显示控件 myWinsys.exit(app.exec_()) # 结束进程,退出程序
运行结果:
【本节完】
版权声明:
Copyright 2023 youcans, XUPT
Crated:2023-2-14
相关文章:

OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
欢迎关注『OpenCV-PyQT项目实战 Youcans』系列,持续更新中 OpenCV-PyQT项目实战(1)安装与环境配置 OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战(3)信号与槽机制 …...
vue2版本《后台管理模式》(上)
后台管理模式项目开发经验总结如下,希望对你们有些帮助: 文章目录一、app 出口位置二 、 index.js 路由配置三、package.json 文件四、 main.js 既然安装插件那就需要引入五、 跨域问题总结首先需要一个完整的v2版本的项目 vue2版本思路:首先…...
C++与C基础重叠部分
Cmake CPP程序开发过程 计算机硬件—>机器语言—>汇编—>cppcpp—>机器(gcc)Make(makefile)—>本地智能批处理翻译机制Cmake—>跨平台生成不同设备上的makefile进行执行 Cpp基础学习 基本知识 基本格式 #include<iostream> using namespace std;…...

神经网络基础部件-卷积层详解
前言 在全连接层构成的多层感知机网络中,我们要通过将图像数据展平成一维向量来送入模型,但这会忽略了每个图像的空间结构信息。理想的策略应该是要利用相近像素之间的相互关联性,将图像数据二维矩阵送给模型中学习。 卷积神经网络(convolu…...

【计算机网络】HTTPS协议原理
文章目录一、认识HTTPS协议二、为什么要发明HTTPS三、HTTP与HTTPS的区别四、常见的加密方式1. 对称加密2. 非对称加密3. 数据摘要4. 数字签名五、HTTPS的原理探究方案1:只使用对称加密方案2:只使用非对称加密方案3:双方都使用非对称加密方案4…...

21岁,华科博士在读,我的赛事Top经验
Datawhale干货 作者:vaew,华中科技大学,博士二年级在读简介笔者vaew,21岁,现为华中科技大学机械科学与工程学院陶波教授课题组博士二年级学生。主要研究方向是基于视触融合的机器人灵巧操作。学业之余的研究兴趣包括图…...

基于ThinkPHP6.0+Vue+uni-app的多商户商城系统好用吗?
likeshop多商户商城系统适用于B2B2C、多商户、商家入驻、平台商城场景。完美契合平台自营联营加盟等多种经营方式使用,系统拥有丰富的营销玩法,强大的分销能力,支持官方旗舰店,商家入驻,平台抽佣商家独立结算ÿ…...
Linux中断
文章目录 前言一、Linux 中断介绍二、中断上文和中断下文三、中断相关函数1 获取中断号相关函数2.申请中断3.释放中断4.中断处理函数四.中断下文之 tasklet1.概念2.Linux 内核中的 tasklet 结构体:3.使用步骤4.相关函数a.初始化 tasklet结构体b.调度 taskletc.杀死 tasklet总结…...

Excel+SQL实战项目 - 餐饮业日销售情况分析仪
目录1、要完成的任务2、认识数据3、SQL数据加工4、excel形成分析仪1、要完成的任务 目标:结合SQL和excel实现餐饮业日销售情况分析仪,如下表: 认识分析仪: 切片器:店面 分为四部分:KPI 、组合图、饼图、数…...

电商导购CPS,京东联盟如何跟单实现用户和订单绑定
前言 大家好,我是小悟 做过自媒体的小伙伴都知道,不管是发图文还是发短视频,直播也好,可以带货。在你的内容里面挂上商品,你自己都不需要囤货,如果用户通过这个商品下单成交了,自媒体平台就会…...

Redis学习【6】之BitMap、HyperLogLog、Geospatial操作命令 (1)
文章目录前言BitMap 操作命令1.1 BitMap 简介1.2 setbit1.3 getbit1.4 bitcount1.5 bitpos[pos:position]1.6 bitop1.7 应用场景二 HyperLogLog 操作命令2.1 HyperLogLog 简介2.2 pfadd2.3 pfcount2.4 pfmerge2.5 应用场景三 Geospatial【地理空间】操作命令3. 1 Geospatial 简…...

JAVA实现心跳检测【长连接】
文章目录1、心跳机制简介2、心跳机制实现方式3、客户端4 、服务端5、代码实现5.1 KeepAlive.java5.2 MyClient.java5.3 MyServer5.4 测试结果1、心跳机制简介 在分布式系统中,分布在不同主机上的节点需要检测其他节点的状态,如服务器节点需要检测从节点…...
python3.9安装和pandas安装踩坑处理
0、先决条件:系统内最好先安装有gcc、libffi-devel等 1、安装包下载 https://www.python.org/downloads/source/ 2、解压安装包并上传到/usr/local/python3.9 3、打开shell cd /usr/local/python3.9要先把python3.9的所有文件复制到/usr/local/python3.9才会成功…...

2023.2.15每日一题——867. 转置矩阵
每日一题题目描述解题核心解法一:二维表示 模拟解法二:一维表示 模拟题目描述 题目链接:867. 转置矩阵 给你一个二维整数数组 matrix, 返回 matrix 的 转置矩阵 。 矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵…...

【人脸识别】Partial-FC:让你在一台机器上训练1000万个id人脸数据集成为可能!
论文题目:”Killing Two Birds with One Stone: Efficient and Robust Training of Face Recognition CNNs by Partial FC“ -CVPR 2022 代码地址:https://arxiv.org/pdf/2203.15565.pdf 代码地址:https://github.com/deepinsight/insightfac…...
递归方法读取任意深度的 JSON 对象的键值
有以下json字符串 {"name":"John","age":30,"address":{"city":"New York","state":"NY","zip":"10001","coordinates":{"latitude":40.712776,&q…...

黑马redis学习记录:分布式锁
一、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行…...
对React-Fiber的理解,它解决了什么问题?
对React-Fiber的理解,它解决了什么问题?Fiber用来解决什么问题?Fiber是什么?Fiber是如何解决问题的?Fiber用来解决什么问题? JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行…...

【Linux】初学Linux你需要掌握这些基本指令(二)
目录 1.man指令 2.cp指令 3.mv指令 4.tree指令 5.echo指令 6.more指令 7.less指令(重要) 8.head与tail指令 9.date指令 显示时间常用参数: 设置时间常用参数: 10.cal指令 11.find & whereis & which指令 …...

Linux中VI/VIM 编辑器
1、概述所有Linux系统都会内置vi文本编辑器vim是vi的升级版,可以主动以字体颜色分辨语法的正确性,代码补完和编译,错误跳转等功能。2、vi和vim的三种模式基本上 vi/vim 共分为三种模式,分别是一般模式、编辑模式、命令模式2.1、一…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...
CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx
“Reactive Stream Processing in Industrial IoT using DDS and Rx” 是指在工业物联网(IIoT)场景中,结合 DDS(Data Distribution Service) 和 Rx(Reactive Extensions) 技术,实现 …...