opencv实战项目 手势识别-手势控制鼠标

手势识别系列文章目录

手势识别是一种人机交互技术,通过识别人的手势动作,从而实现对计算机、智能手机、智能电视等设备的操作和控制。

1.  opencv实现手部追踪(定位手部关键点)

2.opencv实战项目 实现手势跟踪并返回位置信息(封装调用)

3.手势识别-手势音量控制(opencv)

4.opencv实战项目 手势识别-手势控制鼠标

未完待续

本专栏记录作者的学习之旅会一直更新下去,欢迎订阅一起学习进步


本项目是使用了谷歌开源的框架mediapipe,里面有非常多的模型提供给我们使用,例如面部检测,身体检测,手部检测等

在这里插入图片描述

 

代码需要用到opencv   HandTraqckModule模块   mediapipe模块和一个鼠标控制模块autopy


一、HandTraqckModule模块 

前面的文章中有封装手部检测模块的教程,这边简单的介绍一下

import cv2
import mediapipe as mp
import timeclass handDetector():def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):"""初始化手势检测器对象。Args:mode (bool): 是否检测多只手。默认为False,只检测单只手。maxHands (int): 最多检测的手的数量。默认为2。detectionCon (float): 手势检测的置信度阈值。默认为0.5。trackCon (float): 手势跟踪的置信度阈值。默认为0.5。"""self.mode = modeself.maxHands = maxHandsself.detectionCon = detectionConself.trackCon = trackCon# 创建 Mediapipe Hands 模块和绘制工具对象self.mpHands = mp.solutions.handsself.hands = self.mpHands.Hands(self.mode, self.maxHands,self.detectionCon, self.trackCon)self.mpDraw = mp.solutions.drawing_utilsself.tipIds = [4, 8, 12, 16, 20]def findHands(self, img, draw=True):"""检测手势并在图像上绘制关键点和连接线。Args:img (numpy.ndarray): 输入图像。draw (bool): 是否在图像上绘制标记。默认为True。Returns:numpy.ndarray: 绘制了关键点和连接线的图像。"""imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)self.results = self.hands.process(imgRGB)if self.results.multi_hand_landmarks:for handLms in self.results.multi_hand_landmarks:if draw:self.mpDraw.draw_landmarks(img, handLms,self.mpHands.HAND_CONNECTIONS)return img

 

这是第一个小模块,用于创建手势检测器对象以及进行手势检测并在图像上绘制关键点和连接线。下面是对第一个小模块的详细解释:

  1. handDetector 类:定义了手势检测器对象,它具有以下初始化参数和方法。

    • __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):初始化函数,创建手势检测器对象并设置相关参数。

      • mode:是否检测多只手,默认为False。
      • maxHands:最多检测的手的数量,默认为2。
      • detectionCon:手势检测的置信度阈值,默认为0.5。
      • trackCon:手势跟踪的置信度阈值,默认为0.5。
    • findHands(self, img, draw=True):检测手势并在图像上绘制关键点和连接线。

      • img:输入图像(numpy数组)。
      • draw:是否在图像上绘制标记,默认为True。
    • mpHands:Mediapipe Hands 模块。

    • hands:Hand 模型,用于手势检测。

    • mpDraw:Mediapipe 绘制工具。

    • tipIds:手指末端关键点的ID列表。

  2. findHands 方法:接收输入图像,检测手势,并在图像上绘制关键点和连接线。

    • img:输入图像(numpy数组)。
    • draw:是否在图像上绘制标记,默认为True。
  3. imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB):将BGR格式的图像转换为RGB格式,以便Mediapipe处理。

  4. self.results = self.hands.process(imgRGB):使用Mediapipe Hand模型处理图像,得到手势检测结果。

  5. if self.results.multi_hand_landmarks::如果检测到多只手。

  6. for handLms in self.results.multi_hand_landmarks::遍历每只检测到的手。

  7. self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS):在图像上绘制手势关键点和连接线。

  8. 返回绘制了关键点和连接线的图像。

    def findPosition(self, img, handNo=0, draw=True):"""获取手指关键点位置和包围框。Args:img (numpy.ndarray): 输入图像。handNo (int): 指定要分析的手的索引。默认为0,即第一只手。draw (bool): 是否在图像上绘制标记。默认为True。Returns:list: 手指关键点列表。tuple: 包围框坐标 (xmin, ymin, xmax, ymax)。"""xList = []yList = []bbox = []self.lmList = []if self.results.multi_hand_landmarks:myHand = self.results.multi_hand_landmarks[handNo]for id, lm in enumerate(myHand.landmark):# 获取关键点在图像中的坐标h, w, c = img.shapecx, cy = int(lm.x * w), int(lm.y * h)xList.append(cx)yList.append(cy)self.lmList.append([id, cx, cy])if draw:# 在图像上绘制关键点cv2.circle(img, (cx, cy), 5, (255, 0, 255), cv2.FILLED)xmin, xmax = min(xList), max(xList)ymin, ymax = min(yList), max(yList)bbox = xmin, ymin, xmax, ymaxif draw:# 在图像上绘制包围框cv2.rectangle(img, (xmin - 20, ymin - 20), (xmax + 20, ymax + 20),(0, 255, 0), 2)return self.lmList, bbox

 

这是第二个小模块,用于获取手指关键点位置和包围框。下面是对第二个小模块的详细解释:

  1. findPosition 方法:在图像上绘制手指关键点,并返回手指关键点的坐标列表以及手的包围框坐标。

    • img:输入图像(numpy数组)。
    • handNo:指定要分析的手的索引,默认为0,即第一只手。
    • draw:是否在图像上绘制标记,默认为True。
  2. xListyList:用于存储手指关键点的 x 和 y 坐标。

  3. bbox:包围框的坐标,用于确定手的位置。

  4. self.lmList:手指关键点的列表,格式为 [id, x, y]。

  5. myHand = self.results.multi_hand_landmarks[handNo]:获取指定索引的手势关键点信息。

  6. for id, lm in enumerate(myHand.landmark)::遍历手的关键点。

  7. h, w, c = img.shape:获取图像的高度、宽度和通道数。

  8. cx, cy = int(lm.x * w), int(lm.y * h):计算关键点在图像中的坐标。

  9. xList.append(cx)yList.append(cy):将坐标添加到列表中。

  10. self.lmList.append([id, cx, cy]):将关键点信息添加到关键点列表中。

  11. if draw::如果绘制标记为True。

  12. cv2.circle(img, (cx, cy), 5, (255, 0, 255), cv2.FILLED):在图像上绘制关键点。

  13. xmin, xmax = min(xList), max(xList)ymin, ymax = min(yList), max(yList):计算包围框的坐标。

  14. bbox = xmin, ymin, xmax, ymax:设置包围框的坐标。

  15. cv2.rectangle(img, (xmin - 20, ymin - 20), (xmax + 20, ymax + 20), (0, 255, 0), 2):在图像上绘制包围框。

  16. 返回手指关键点列表和包围框坐标。

    def fingersUp(self):"""判断手指是否伸展。Returns:list: 包含每个手指的状态,1表示伸展,0表示弯曲。"""fingers = []# 判断拇指是否伸展if self.lmList[self.tipIds[0]][1] > self.lmList[self.tipIds[0] - 1][1]:fingers.append(1)else:fingers.append(0)# 判断其他手指是否伸展for id in range(1, 5):if self.lmList[self.tipIds[id]][2] < self.lmList[self.tipIds[id] - 2][2]:fingers.append(1)else:fingers.append(0)return fingersdef findDistance(self, p1, p2, img, draw=True, r=15, t=3):"""计算两个关键点之间的距离。Args:p1 (int): 第一个关键点的索引。p2 (int): 第二个关键点的索引。img (numpy.ndarray): 输入图像。draw (bool): 是否在图像上绘制标记。默认为True。r (int): 圆的半径,用于标记关键点。默认为15。t (int): 绘制线条的粗细。默认为3。Returns:float: 两个关键点之间的距离。numpy.ndarray: 绘制了距离标记的图像。list: 包含关键点坐标的列表 [x1, y1, x2, y2, cx, cy]。"""x1, y1 = self.lmList[p1][1:]x2, y2 = self.lmList[p2][1:]cx, cy = (x1 + x2) // 2, (y1 + y2) // 2if draw:# 在图像上绘制线条和关键点cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), t)cv2.circle(img, (x1, y1), r, (255, 0, 255), cv2.FILLED)cv2.circle(img, (x2, y2), r, (255, 0, 255), cv2.FILLED)cv2.circle(img, (cx, cy), r, (0, 0, 255), cv2.FILLED)# 计算两个关键点之间的距离length = math.hypot(x2 - x1, y2 - y1)return length, img, [x1, y1, x2, y2, cx, cy]

 

这是第三个小模块,用于判断手指是否伸展以及计算两个关键点之间的距离。下面是对第三个小模块的详细解释:

  1. fingersUp 方法:判断每个手指是否伸展,返回包含手指状态的列表。

    • 返回值:包含每个手指状态的列表,1表示伸展,0表示弯曲。
  2. findDistance 方法:计算两个关键点之间的距离,并在图像上绘制标记。

    • p1p2:两个关键点的索引。
    • img:输入图像(numpy数组)。
    • draw:是否在图像上绘制标记,默认为True。
    • r:圆的半径,用于标记关键点,默认为15。
    • t:绘制线条的粗细,默认为3。
  3. x1, y1 = self.lmList[p1][1:]x2, y2 = self.lmList[p2][1:]:获取两个关键点的坐标。

  4. cx, cy = (x1 + x2) // 2, (y1 + y2) // 2:计算两个关键点的中心坐标。

  5. if draw::如果绘制标记为True。

  6. cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), t):在图像上绘制连接两个关键点的线条。

  7. cv2.circle(img, (x1, y1), r, (255, 0, 255), cv2.FILLED)cv2.circle(img, (x2, y2), r, (255, 0, 255), cv2.FILLED):在两个关键点处绘制实心圆圈,用于标记关键点。

  8. cv2.circle(img, (cx, cy), r, (0, 0, 255), cv2.FILLED):在关键点中心绘制实心圆圈,用于标记距离的中心。

  9. length = math.hypot(x2 - x1, y2 - y1):使用勾股定理计算两个关键点之间的距离。

  10. 返回值:返回计算的距离、绘制了标记的图像和包含关键点坐标的列表 [x1, y1, x2, y2, cx, cy]。

 

完整代码

"""
Hand Tracking Module"""import cv2
import mediapipe as mp
import time
import math
import numpy as npclass handDetector():def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):self.mode = modeself.maxHands = maxHandsself.detectionCon = detectionConself.trackCon = trackConself.mpHands = mp.solutions.handsself.hands = self.mpHands.Hands(self.mode, self.maxHands,self.detectionCon, self.trackCon)self.mpDraw = mp.solutions.drawing_utilsself.tipIds = [4, 8, 12, 16, 20]def findHands(self, img, draw=True):imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)self.results = self.hands.process(imgRGB)# print(results.multi_hand_landmarks)if self.results.multi_hand_landmarks:for handLms in self.results.multi_hand_landmarks:if draw:self.mpDraw.draw_landmarks(img, handLms,self.mpHands.HAND_CONNECTIONS)return imgdef findPosition(self, img, handNo=0, draw=True):xList = []yList = []bbox = []self.lmList = []if self.results.multi_hand_landmarks:myHand = self.results.multi_hand_landmarks[handNo]for id, lm in enumerate(myHand.landmark):# print(id, lm)h, w, c = img.shapecx, cy = int(lm.x * w), int(lm.y * h)xList.append(cx)yList.append(cy)# print(id, cx, cy)self.lmList.append([id, cx, cy])if draw:cv2.circle(img, (cx, cy), 5, (255, 0, 255), cv2.FILLED)xmin, xmax = min(xList), max(xList)ymin, ymax = min(yList), max(yList)bbox = xmin, ymin, xmax, ymaxif draw:cv2.rectangle(img, (xmin - 20, ymin - 20), (xmax + 20, ymax + 20),(0, 255, 0), 2)return self.lmList, bboxdef fingersUp(self):fingers = []# Thumbif self.lmList[self.tipIds[0]][1] > self.lmList[self.tipIds[0] - 1][1]:fingers.append(1)else:fingers.append(0)# Fingersfor id in range(1, 5):if self.lmList[self.tipIds[id]][2] < self.lmList[self.tipIds[id] - 2][2]:fingers.append(1)else:fingers.append(0)# totalFingers = fingers.count(1)return fingersdef findDistance(self, p1, p2, img, draw=True,r=15, t=3):x1, y1 = self.lmList[p1][1:]x2, y2 = self.lmList[p2][1:]cx, cy = (x1 + x2) // 2, (y1 + y2) // 2if draw:cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), t)cv2.circle(img, (x1, y1), r, (255, 0, 255), cv2.FILLED)cv2.circle(img, (x2, y2), r, (255, 0, 255), cv2.FILLED)cv2.circle(img, (cx, cy), r, (0, 0, 255), cv2.FILLED)length = math.hypot(x2 - x1, y2 - y1)return length, img, [x1, y1, x2, y2, cx, cy]def main():pTime = 0cTime = 0cap = cv2.VideoCapture(1)detector = handDetector()while True:success, img = cap.read()img = detector.findHands(img)lmList, bbox = detector.findPosition(img)if len(lmList) != 0:print(lmList[4])cTime = time.time()fps = 1 / (cTime - pTime)pTime = cTimecv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,(255, 0, 255), 3)cv2.imshow("Image", img)cv2.waitKey(1)if __name__ == "__main__":main()

 

二、主代码

import cv2
import numpy as np
import HandTrackingModule as htm
import time
import autopy##########################
wCam, hCam = 640, 480
frameR = 100  # Frame Reduction
smoothening = 7
#########################pTime = 0
plocX, plocY = 0, 0
clocX, clocY = 0, 0cap = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
detector = htm.handDetector(maxHands=1)
wScr, hScr = autopy.screen.size()
# print(wScr, hScr)while True:# 1. Find hand Landmarkssuccess, img = cap.read()img = detector.findHands(img)lmList, bbox = detector.findPosition(img)# 2. Get the tip of the index and middle fingersif len(lmList) != 0:x1, y1 = lmList[8][1:]x2, y2 = lmList[12][1:]# print(x1, y1, x2, y2)# 3. Check which fingers are upfingers = detector.fingersUp()# print(fingers)cv2.rectangle(img, (frameR, frameR), (wCam - frameR, hCam - frameR),(255, 0, 255), 2)# 4. Only Index Finger : Moving Modeif fingers[1] == 1 and fingers[2] == 0:# 5. Convert Coordinatesx3 = np.interp(x1, (frameR, wCam - frameR), (0, wScr))y3 = np.interp(y1, (frameR, hCam - frameR), (0, hScr))# 6. Smoothen ValuesclocX = plocX + (x3 - plocX) / smootheningclocY = plocY + (y3 - plocY) / smoothening# 7. Move Mouseautopy.mouse.move(wScr - clocX, clocY)cv2.circle(img, (x1, y1), 15, (255, 0, 255), cv2.FILLED)plocX, plocY = clocX, clocY# 8. Both Index and middle fingers are up : Clicking Modeif fingers[1] == 1 and fingers[2] == 1:# 9. Find distance between fingerslength, img, lineInfo = detector.findDistance(8, 12, img)print(length)# 10. Click mouse if distance shortif length < 40:cv2.circle(img, (lineInfo[4], lineInfo[5]),15, (0, 255, 0), cv2.FILLED)autopy.mouse.click()# 11. Frame RatecTime = time.time()fps = 1 / (cTime - pTime)pTime = cTimecv2.putText(img, str(int(fps)), (20, 50), cv2.FONT_HERSHEY_PLAIN, 3,(255, 0, 0), 3)# 12. Displaycv2.imshow("Image", img)cv2.waitKey(1)

 

下面是代码的主要功能和操作步骤的解释:

  1. 导入所需的库和模块(cv2numpyHandTrackingModuletimeautopy)以及一些配置参数。

  2. 创建摄像头对象 cap,设置摄像头的宽度和高度为 wCamhCam

  3. 创建一个 handDetector 对象 detector,用于检测手势。在这里,我们只使用一个手,所以设置 maxHands=1

  4. 获取屏幕的宽度和高度,以便后面的坐标转换。

  5. 进入一个无限循环,不断处理视频帧。

  6. 在循环中,首先从摄像头获取一帧图像,并调用 detector.findHands 方法来检测手势。然后,调用 detector.findPosition 方法获取手势的关键点坐标和边界框。

  7. 根据检测到的手势关键点,判断手指的状态(是否抬起)。

  8. 绘制一个矩形框作为活动区域,用于移动鼠标。

  9. 当食指抬起而中指不抬起时,进入移动模式。将手势坐标转换为屏幕上的坐标,然后进行平滑处理,最后使用 autopy.mouse.move 方法移动鼠标。

  10. 绘制一个实心圆表示食指的位置,并更新上一次的位置。

  11. 当食指和中指同时抬起时,进入点击模式。计算食指和中指之间的距离,如果距离小于阈值,执行鼠标点击操作。

  12. 计算并显示帧率。

  13. 将处理后的图像显示在窗口中,按下任意键退出循环。

利用摄像头捕获的图像,检测手势,然后根据手指状态来模拟鼠标操作。移动模式下,用食指位置移动鼠标;点击模式下,用食指和中指之间的距离来模拟鼠标点击。这样可以实现通过手势来控制鼠标的功能。

 

需要注意的是,运行代码前,需要将食指放在摄像头前。否则会报错

遇到其他错误,可以评论区留言,大家一起解决

 

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

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

相关文章

AAAI论文阅读

文章目录 Open-Vocabulary Multi-Label Classifcation via Multi-Modal Knowledge Transfer——知识蒸馏的范畴Med-EASi: Finely Annotated Dataset and Models for Controllable Simplifcation of Medical Texts——医学领域数据集构建“Nothing Abnormal”: Disambiguating M…

UE 5 GAS 在项目中处理AttributeSet相关

这一篇文章是个人的实战经验记录&#xff0c;如果对基础性的内容不了解的&#xff0c;可以看我前面一篇文章对基础的概念以及内容的讲解。 设置AttributeSet 使用GAS之前&#xff0c;首先需要设置参数集AS&#xff0c;这个是用于同步的一些参数&#xff0c;至于如何设置GAS&a…

Apollo让自动驾驶如此简单

前言&#xff1a; 最近被新能源的电价闹的不行&#xff0c;买了电车的直呼上当了、不香了。但电车吸引人不只是公里油耗低&#xff0c;还有良好的驾车使用感。比如辅助驾驶、甚至是自动驾驶。今天来介绍一个头部自动驾驶平台Apollo&#xff0c;Apollo是一个开源的、自动驾驶的软…

【论文阅读】基于深度学习的时序预测——FEDformer

系列文章链接 论文一&#xff1a;2020 Informer&#xff1a;长时序数据预测 论文二&#xff1a;2021 Autoformer&#xff1a;长序列数据预测 论文三&#xff1a;2022 FEDformer&#xff1a;长序列数据预测 论文四&#xff1a;2022 Non-Stationary Transformers&#xff1a;非平…

3.6 Spring MVC文件上传

1. 文件上传到本地 实现方式 Spring MVC使用commons-fileupload实现文件上传&#xff0c;注意事项如下&#xff1a; l HTTP请求方法是POST。 l HTTP请求头的Content-Type是multipart/form-data。 SpringMVC配置 配置commons-fileupload插件的文件上传解析器CommonsMultip…

SpringBoot复习:(34)@EnableWebMvc注解为什么让@WebMvcAutoconfiguration失效?

它导入了DelegatingWebMvcConfiguration 它会把容器中的类型为WebMvcConfigurer的bean注入到类型为WebMvcConfigurerComposite的成员变量configurers中。 可以看到它继承了WebMvcConfigurerSupport类 而WebMvcConfigureAutoConfiguration类定义如下 可以看到一个Conditional…

Flink源码之RPC

Flink是一个典型的Master/Slave分布式实时处理系统&#xff0c;分布式系统组件之间必然涉及通信&#xff0c;也即RPC&#xff0c;以下图展示Flink组件之间的关系&#xff1a; RPCGateWay 一般RPC框架可根据用户业务类生成客户端和服务器端通信底层代码&#xff0c;此时只需定…

基于机器学习进行降雨预测 -- 机器学习项目基础篇(13)

在本文中&#xff0c;我们将学习如何构建一个机器学习模型&#xff0c;该模型可以根据一些大气因素预测今天是否会有降雨。这个问题与使用机器学习的降雨预测有关&#xff0c;因为机器学习模型往往在以前已知的任务上表现得更好&#xff0c;而这些任务需要高技能的个人来完成。…

mysql正则表达式

mysql正则表达式 : REGEXP 规则基本和java正则规则一样,有些特殊字符有差异. 举例: -- 匹配以英文开头,4位英文字母8位数字/4位数字-4位数字 select * from tableName where code REGEXP ^[A-Z]{4}[0-9]{8}/[0-9]{3}-[0-9]{3}$ ;将code中的3位数字改成4位.高位补0. -- 数据…

Jmeter快捷方式和应用图标设置

很多人在安装Jmeter,安装到本机却没有icon&#xff0c;每次使用的时候&#xff0c;每次打开应用都要找目录&#xff0c;不太方便。 【解决问题】 使用bin路径下的一个.bat文件&#xff0c;创建快捷方式。 【操作步骤】 Step1、将Jmeter 安装bin路径下的jmeter.bat 发送快捷方…

Linux常见面试题,应对面试分享

操作系统基础 1.cpu占⽤率太⾼了怎么办? 排查思路是什么&#xff0c;怎么定位这个问题&#xff0c;处理流程 其他程序: 1.通过top命令按照CPU使⽤率排序找出占⽤资源最⾼的进程 2.lsof查看这个进程在使⽤什么⽂件或者有哪些线程 3.询问开发或者⽼⼤,是什么业务在使⽤这个进程…

实例036 使窗体标题栏文字右对齐

实例说明 窗口标题栏中的文字是窗口的重要说明&#xff0c;该文字可以标示窗口的功能、状态或名称等信息&#xff0c;一般该文字是居左显示的&#xff0c;在本例中设计一个标题栏文字右对齐的窗口。本实例运行结果如图1.36所示。 技术要点 在C# 2.0中实现这一功能非常容易&am…

题目大解析(3)

题目 字符串中的第一个唯一字符 字符串中的第一个唯一字符 原题链接&#xff1a;字符串中的第一个唯一字符 计数法&#xff1a; class Solution { public:int firstUniqChar(string s) {int arr[130] {0};for(auto x : s){arr[x-0];}int i 0;for(auto x : s){if(arr[x-0] …

centos修改DNS方法

如何修复dns服务器&#xff1f;dns服务器由解析器和域名服务器组成&#xff0c;主要存储网络中所有主机的域名和相应的ip地址。关于dns服务器有很多问题&#xff0c;我们将在这里给出一个具体的答案。 1、什么是dns&#xff1f; dns是指&#xff1a;域名服务器&#xff08;域…

Android高通8.1 Selinux问题

1、最近客户提了一个需求&#xff0c;说要在user版本上面切分辨率&#xff0c;默认屏幕分辨率是2.5 k 执行adb shell指令之后变成 4k 然后adb shell wm size可以查看 2、一开始我能想到就是在文件节点添加权限&#xff0c;这里不管是mtk还是qcom&#xff08;高通平台&#xff…

将.doc文档的默认打开方式从WPS修改为word office打开方式的具体方法(以win 10 操作系统为例)

将.doc文档的默认打开方式从WPS修改为word office打开方式的具体方法&#xff08;以win 10 操作系统为例&#xff09; 随着近几年WPS软件的不断完善和丰富&#xff0c;在某些方面取得了具有特色的优势。在平时编辑.doc文档时候也常常用到wps软件&#xff0c;不过WPS文献也存在…

记录第一篇被”华为开发者联盟鸿蒙专区 “收录的文章

记录第一篇被”华为开发者联盟鸿蒙专区 “社区收录的文章。 坚持写作的动力是什么&#xff1f; 是记录、分享&#xff0c;以及更好的思考 。

培训报名小程序报名确认开发

目录 1 创建页面2 创建URL参数3 信息展示4 消息订阅5 页面传参6 程序预览总结 我们上一篇介绍了报名功能的开发&#xff0c;在用户报名成功后需要展示报名的确认信息&#xff0c;如果信息无误提示用户支付&#xff0c;在支付之前需要让用户进行授权&#xff0c;允许小程序给用户…

从零开始搭建个人博客网站(hexo框架)

1.工具及环境搭建 1&#xff09;注册GitHub并且新建一个repositories 2&#xff09;下载node.js以及Git 下载链接&#xff1a; 检验安装是否成功&#xff1a; 【注】&#xff1a;MacOS自带Git&#xff0c;可以直接在终端输入git --version进行检验 3&#xff09;新建一个…

Springboot04--vue前端部分+element-ui

注意点&#xff1a; 这边v-model和value的区别&#xff1a;v-model是双向绑定的&#xff0c;value是单向绑定 li的key的问题 vue的组件化开发&#xff1a; 1. NPM&#xff08;类似maven&#xff0c;是管理前段代码的工具&#xff09; 安装完之后可以在cmd里面使用以下指令 2.…