网页版五子棋对战实现和自动化测试

文章目录

  • 前言
  • 一、项目描述
    • 项目演示链接
  • 二、实现的功能与操作
    • 1.登录注册
    • 2.游戏大厅
      • 线程安全问题
      • 多开处理
    • 3.五子棋对战
  • 三、项目测试
    • 1.测试用例
    • 2.测试技术点
    • 3.部分测试用例展示
      • (1)注册页面
      • (2)登录页面
      • (3)游戏大厅页面
      • (4)游戏对战页面
    • 4.项目测试的结果
      • 视频演示链接
  • 总结


前言

五子棋对战网页应用是一个基于Web技术的在线多人五子棋游戏。该应用提供了用户注册、登录、匹配对手、对战等功能,旨在为用户提供轻松愉快的游戏体验。本篇文章主要用来记录我的网页版五子棋项目,包括项目介绍、实现功能、测试用例、自动化测试等。


一、项目描述

该项目支持玩家随时随地与其他在线玩家进行五子棋对战,实现实时的棋局同步和即时通讯。利用 WebSocket 技术实现双向通信,保证游戏过程中的实时性和即时交互,提供更流畅的游戏体验。 提供游戏大厅,展示在线玩家信息、排名和当前对局状态。内置匹配系统,自动匹配合适的对手,确保玩家能够享受到公平有趣的游戏。

技术栈:
前端: 使用HTML5、CSS3、JavaScript以及现代前端框架(如React、Vue等)构建直观、响应式的用户界面。
后端: 基于Spring MVC、Spring Boot等后端框架,使用WebSocket实现实时通信,处理用户认证、游戏逻辑和数据存储。
数据库: 使用关系型数据库MySQL存储用户信息、对战历史等数据。
实时通信: 使用WebSocket技术,实现玩家之间的实时通信和对战同步。
webSocket:是一个应用层协议。四个核心点:建立连接之后、收到消息之后、出现连接异常、出现连接关闭。客户端通过webSocket.send()发送消息,服务器使用sendMessage()发送消息。

项目演示链接

二、实现的功能与操作

现在已实现注册登录功能、游戏匹配功能、下棋功能。

1.登录注册

登录主要是判定用户是不是合法登录;注册这一块比登录多一个确认密码,以及注册的这个人是不是在数据库中存在。
在这里插入图片描述后端代码如下(示例):

@RequestMapping("/login")public Object login(HttpServletRequest request, String username, String password) {// 1.从数据库中找到匹配用户User user = userMapper.selectByName(username);System.out.println("当前登录用户:user=" + user);if (user == null || !user.getPassword().equals(password)) {// 登录失败System.out.println("登录失败!");return new User();}HttpSession httpSession = request.getSession(true);httpSession.setAttribute("user", user);return user;}@RequestMapping("/register")public Object register(String username, String password) {try {User user = new User();user.setUsername(username);user.setPassword(password);userMapper.insert(user);return user;} catch (org.springframework.dao.DuplicateKeyException e) {User user = new User();return user;}}

注册前端代码(示例):

    <script>function mysub() {var username = jQuery("#username");var password = jQuery("#password");var password2 = jQuery("#password2");if(username.val()=="") {alert("请先输入用户名!");username.focus();return;}if(password.val()=="") {alert("请先输入密码!");password.focus();return;}if(password2.val()=="") {alert("请确认密码!");password2.focus();return;}if(password2.val()!=password.val()) {alert("两次密码输入不一致!");password.focus();return;}jQuery.ajax({url:"/user/register",type:"POST",data:{"username":username.val(),"password":password.val()},success:function(result) {if(result && result.username) {if(confirm("恭喜:注册成功,是否跳转到登录页面?")) {location.href = "/login.html";}}else {alert("抱歉:注册失败,请重试!");}}});}</script>

2.游戏大厅

游戏大厅需要先实现其大厅页面,里面包括玩家信息、开始匹配按钮,而最重要的则是一旦玩家点击开始匹配之后,需要给该按钮添加一个点击事件,这里就涉及我们的核心:游戏匹配,我们创建了两个个优先级队列,玩家登录成功将该玩家加入进一个玩家在线的HashMap,玩家点击匹配按钮后,将其加入另一个表示游戏匹配的HashMap。这里的我们需要考虑线程安全问题和多开问题。实现思路如下图所示:
在这里插入图片描述

游戏匹配后端代码如下(示例):

    // 建立连接之后@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 玩家上线,加入到onlineUserManagertry {//1.先获取当前用户的身份信息// webSocketSession通过addInterceptors获取httpsession中的AttributesUser user = (User) session.getAttributes().get("user");//2.判定当前用户是否是已经在线状态if (onlineUserManager.getFromGameHall(user.getUserId()) != null|| onlineUserManager.getFromGameRoom(user.getUserId()) != null) {//说明当前用户已经登录!//告知客户端重复登录MatchResponse response = new MatchResponse();response.setOk(true);response.setReason("当前禁止多开!");response.setMessage("repeatConnection");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));// 直接关闭有些太激进//session.close();return;}//3.将玩家设置为在线状态onlineUserManager.enterGameHall(user.getUserId(), session);System.out.println("玩家:" + user.getUsername() + " 进入游戏大厅!");} catch (NullPointerException e) {System.out.println("[MatchAPI.afterConnectionEstablished]当前用户未登录");}}// 处理传输信息@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 处理开始匹配请求和处理停止匹配请求User user = (User) session.getAttributes().get("user");// 获取客户端发送给服务器的数据String payload = message.getPayload();// 将JSON搁置的字符串,转换为对象MatchRequest request = objectMapper.readValue(payload, MatchRequest.class);MatchResponse response = new MatchResponse();if (request.getMessage().equals("startMatch")) {//(1)进入匹配队列//(2)创建一个类表示匹配队列,把当前用户加进去matcher.add(user);response.setOk(true);response.setMessage("startMatch");} else if (request.getMessage().equals("stopMatch")) {// (1)退出匹配队列// (2)创建一个类表示匹配队列,把当前用户从队列中移除matcher.remove(user);// 移除之后,返回一个响应给客户端response.setOk(true);response.setMessage("stopMatch");} else {// 非法情况response.setOk(false);response.setReason("非法的匹配请求");}String jsonString = objectMapper.writeValueAsString(response);session.sendMessage(new TextMessage(jsonString));}// 处理传输异常@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {// 玩家下线,从 onlineUserManager 中删除try {User user = (User) session.getAttributes().get("user");WebSocketSession tmpSession = onlineUserManager.getFromGameHall(user.getUserId());// 保证退出的人和登录的人是一个人if (tmpSession == session) {onlineUserManager.exitGameHall(user.getUserId());System.out.println("玩家:" + user.getUsername() + " 退出游戏大厅!");}// 如果玩家正在匹配中,而 websocket 连接断开matcher.remove(user);} catch (NullPointerException e) {System.out.println("[MatchAPI.handleTransportError]当前用户未登录");}}// 处理传输关闭@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {try {// 玩家下线,从 onlineUserManager 中删除User user = (User) session.getAttributes().get("user");WebSocketSession tmpSession = onlineUserManager.getFromGameHall(user.getUserId());// 保证退出的人和登录的人是一个人if (tmpSession == session) {onlineUserManager.exitGameHall(user.getUserId());System.out.println("玩家:" + user.getUsername() + " 退出游戏大厅!");}// 如果玩家正在匹配中,而 websocket 连接断开matcher.remove(user);} catch (NullPointerException e) {System.out.println("[MatchAPI.afterConnectionClosed]当前用户未登录");}}

线程安全问题

我们对玩家进入游戏使用的是一个HashMap,玩家点击匹配按钮后,被加入另一个HashMap,而我们的系统涉及多个用户同时使用,则多线程访问同一个HashMap容易产生线程安全问题,这里我们将HashMap改为ConcurrentHashMap来处理线程安全问题,ConcurrentHashMap是线程安全的,它通过使用锁分段(segmented-locking)技术来提高并发访问性能。它允许多个修改操作并发进行,而不会导致数据不一致。
在这里插入图片描述

多开处理

(1)一个账户多个地方登录,那这种对于我们系统是不允许存在的,我们通过seesion判断当前用户是否存在,并且不允许一个客户端同时匹配两个用户。
在这里插入图片描述
(2)我的用户是按照分数划分为三个梯队,由于 handlerMatch 是在单独的线程中调用,因此要考虑到访问队列的线程安全问题.,需要加上锁。
在这里插入图片描述
在这里插入图片描述

3.五子棋对战

当游戏匹配已经有两个或两个以上的玩家,则会匹配成功进入同一个游戏房间,进行游戏对战。进入到游戏房间,这里的棋盘我们是用canvas画上去的,双方下棋的时候,使用websocket给双方用户通知提示当前对手信息,重写websocket四个核心方法,也要判断游戏输赢后通知给玩家。实现过程如下图所示:
在这里插入图片描述
重写websocket的四个核心方法,并实现通知玩家谁输谁赢的方法noticeThatUserWin。
游戏对战后端代码如下(示例):

Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {GameReadyResponse response = new GameReadyResponse();// 1.先获取到用户的身份信息,(从httpsession中拿到当前用户的对象)User user = (User) session.getAttributes().get("user");if (user == null) {response.setOk(false);response.setReason("用户尚未登录!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 2.判断当前用户是否已经进入房间Room room = roomManager.getRoomByUserId(user.getUserId());if (room == null) {// 该玩家还没有匹配到对手,不应该进入房间response.setOk(false);response.setReason("用户尚未匹配到!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 3.判断当前是不是多开用户if (onlineUserManager.getFromGameHall(user.getUserId()) != null|| onlineUserManager.getFromGameRoom(user.getUserId()) != null) {response.setOk(true);response.setReason("禁止多开游戏页面");response.setMessage("repeatConnection");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 4.设置当前玩家上线onlineUserManager.enterGameRoom(user.getUserId(), session);// 5.把两个玩家加入到游戏房间中synchronized (room) {if (room.getUser1() == null) {room.setUser1(user);// 把先连入房间的玩家作为先手方room.setWhiteUser(user.getUserId());System.out.println("玩家1:" + user.getUsername() + " 进入游戏房间,已经准备就绪!");return;}if (room.getUser2() == null) {// 进入到这里说明user1已经进入房间room.setUser2(user);System.out.println("玩家2:" + user.getUsername() + " 进入游戏房间,已经准备就绪!");// 两个玩家就绪后// 通知玩家1noticeGameReady(room, room.getUser1(), room.getUser2());// 通知玩家2noticeGameReady(room, room.getUser2(), room.getUser1());return;}}// 6.此处如果又有玩家尝试连接同一个房间(理论上是不存在的)response.setOk(false);response.setReason("当前房间已满,您不能加入房间!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));}private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {GameReadyResponse resp = new GameReadyResponse();resp.setOk(true);resp.setReason("");resp.setMessage("gameReady");resp.setReason(room.getRoomId());resp.setThisUserId(thisUser.getUserId());resp.setThatUserId(thatUser.getUserId());resp.setWhiteUser(room.getWhiteUser());// 把当前的响应数据传回给对应的玩家WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 1.从session里拿到当前用户信息User user = (User) session.getAttributes().get("user");if (user == null) {System.out.println("[handleTextMessage] 当前玩家尚未登录!");return;}// 2.根据玩家id获取到房间对象Room room = roomManager.getRoomByUserId(user.getUserId());// 3.通过room对象来处理这次具体的请求room.putChess(message.getPayload());}@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {User user = (User) session.getAttributes().get("user");if (user == null) {return;}WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());if (session == exitSession) {onlineUserManager.exitGameRoom(user.getUserId());}System.out.println("当前用户:" + user.getUsername() + " 游戏房间连接异常!");// 通知对手获胜了noticeThatUserWin(user);}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {User user = (User) session.getAttributes().get("user");if (user == null) {return;}WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());if (session == exitSession) {onlineUserManager.exitGameRoom(user.getUserId());}System.out.println("当前用户:" + user.getUsername() + " 离开游戏房间!");// 通知对手获胜了noticeThatUserWin(user);}// 判断输赢,并通知给玩家private void noticeThatUserWin(User user) throws IOException {// 1.根据当前玩家,找到玩家所在的房间Room room = roomManager.getRoomByUserId(user.getUserId());if (room == null) {System.out.println("当前房间已经释放,无需通知对手!");return;}// 2.根据房间找到对手User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();// 3.找到对手的在线状态WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());if (webSocketSession == null) {// 对手也掉线了System.out.println("对手已经掉线,无需通知!");return;}// 4.构造一个响应,通知对手,你是获胜方GameResponse resp = new GameResponse();resp.setMessage("putChess");resp.setUserId(thatUser.getUserId());resp.setWinner(thatUser.getUserId());webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));// 5.更新玩家分数信息int winUserId = thatUser.getUserId();int loseUserId = user.getUserId();userMapper.userWin(winUserId);userMapper.userLose(loseUserId);// 6.释放房间对象roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());}

三、项目测试

1.测试用例

在这里插入图片描述

2.测试技术点

测试工具:Selenium4+Junit5
(1)使用定位元素定位、元素操作(findElement、cssSelector、sendKeys)实现页面交互;
(2)获取页面相关元素并使用断言(Assertions)判断元素或者文本信息与预期是否符合;
(3)使用@BeforeEach、@BeforeAll、@AfterAll等注解降低代码冗余;
(4)使用参数化方法,@CsvSource批量测试多组数据;
(5)使用@Order注解,设置测试内部执行顺序;
(7)使用套件(suit)将测试用例连起来,以便批量运行测试用例。

3.部分测试用例展示

(1)注册页面

根据对注册页面的测试用例编写,分别对注册页面可否正常打开、注册失败、注册成功进行测试。
这里写出测试注册成功的逻辑:
1)首先,清空登录框,以防多组登录;
2)使用参数测试,将CsvSource准备好的多组测试数据,使用cssSelector选择器选中输入框,用sendKey输入准备好的数据;
3)输入完注册信息后,再选中登录按钮后用click点击注册按钮;
4)由于点击注册按钮后会alert出一个弹窗提示,所以此时将driver切换到alert;
5)切换到alert后,提取到弹窗信息,根据弹窗信息判断是否注册成功;
6)注册成功后跳转到登录页面,此时使用写好的截图方法,记录下此刻。

    @ParameterizedTest@CsvSource({"刘云,123,1234"})@Order(2)public void registerFail(String name, String password, String password2) throws IOException {//清空登录框,以防多组登录driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();driver.findElement(By.cssSelector("#password2")).clear();//进行注册driver.findElement(By.cssSelector("#username")).sendKeys(name);driver.findElement(By.cssSelector("#password")).sendKeys(password);driver.findElement(By.cssSelector("#password2")).sendKeys(password2);driver.findElement(By.cssSelector("#submit")).click();//driver转到alertAlert alert = driver.switchTo().alert();//获取弹窗中的提示信息String actual = alert.getText();if (actual.equals("请先输入用户名!")) {System.out.println("用户名为空!");} else if (actual.equals("请先输入密码!")){System.out.println("密码为空!");} else if (actual.equals("请确认密码!")) {System.out.println("再次输入密码为空!");} else if (actual.equals("两次密码输入不一致!")) {System.out.println("两次密码不一致!");} else if(actual.equals("抱歉:注册失败,请重试!")){System.out.println("已有该用户,请重试!");} else if(actual.equals("恭喜:注册成功,是否跳转到登录页面?")) {System.out.println("注册成功,跳转到登录页面!");} else {System.out.println("注册异常!");}alert.accept();getScreenShot(getClass().getName());}

(2)登录页面

根据测试用例,我们对登录页面是否正常、登录失败、登录成功分别测试。
这里写出测试登录失败的逻辑:
1)清空登录框。防止多组登录;
2)登录,使用cssSelector选择器和sendkeys输入文本,csvSource准备好测试数据;
3)获取弹窗和弹窗中的提示信息;
4)使用assertion判断实际信息和提示信息是否一致,一致则登录成功。

    @ParameterizedTest@CsvSource({"张三,1234"})@Order(2)public void loginFail(String name, String password) throws IOException {//登录失败try {// 清空登录框,以防多组登录driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();//登录driver.findElement(By.cssSelector("#username")).sendKeys(name);driver.findElement(By.cssSelector("#password")).sendKeys(password);getScreenShot(getClass().getName());driver.findElement(By.cssSelector("#submit")).click();//获取警告框文本String expect = "登录失败!";Alert alert = driver.switchTo().alert();String actual = alert.getText();System.out.println(actual);Assertions.assertEquals(expect, actual);alert.accept();} catch (TimeoutException e) {System.out.println("登录失败:未出现警告框");} catch (UnhandledAlertException e) {System.out.println("警告框文本与预期不符:" + e.getMessage());}}

(3)游戏大厅页面

根据游戏大厅的测试用例,编写页面加载正常、检测用户登录、匹配按钮的测试代码。
其匹配按钮的测试代码逻辑如下:
1)由于点击匹配按钮需要是登录的状态下,所以先进行用户的登录;
2)登录后点击匹配按钮;
3)查看点击后的匹配按钮的状态是否发生变化;
4)再次点击按钮;
5)查看按钮是否恢复为开始匹配状态。

    @Test@Order(3)public void isMatch() throws IOException, InterruptedException {// 1.先登录driver.get("http://127.0.0.1:8081/login.html");driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();driver.findElement(By.cssSelector("#username")).sendKeys("张三");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 2.进入到游戏大厅页面后,点击匹配按钮driver.findElement(By.cssSelector("#match-button")).click();// 3.获取点击匹配后的信息String actual = "匹配中...(点击停止)";getScreenShot(getClass().getName());String clickAfter = driver.findElement(By.cssSelector("#match-button")).getText();// 4.判断Assertions.assertEquals(clickAfter, actual);// 5.再次点击driver.findElement(By.cssSelector("#match-button")).click();String clickAgain = driver.findElement(By.cssSelector("#match-button")).getText();// 6.判断actual = "开始匹配";Assertions.assertEquals(clickAgain, actual);getScreenShot(getClass().getName());}

(4)游戏对战页面

根据测试用例对游戏匹配成功后游戏对战页面进行测试,主要测试点是页面上的棋盘和显示屏元素。
测试步骤:
1)防止多开,创建两个浏览器驱动(edgeDriver、chromeDriver);
2)分别在两个浏览器上登录不同的账户;
3)登录成功后进入游戏大厅页面,分别点击开始匹配按钮;
4)匹配成功后进入游戏对战页面,寻找棋盘和显示屏两个标志元素。

    // 创建驱动private static EdgeDriver edgeDriver = createEdgeDriver();private static ChromeDriver chromeDriver = createChromeDriver();// 将两个不同系统的用户匹配进同一游戏房间public void userLogin() {// user1edgeDriver.get("http://127.0.0.1:8081/login.html");edgeDriver.findElement(By.cssSelector("#username")).sendKeys("六1");edgeDriver.findElement(By.cssSelector("#password")).sendKeys("123");edgeDriver.findElement(By.cssSelector("#submit")).click();// user2chromeDriver.get("http://127.0.0.1:8081/login.html");chromeDriver.findElement(By.cssSelector("#username")).sendKeys("王五");chromeDriver.findElement(By.cssSelector("#password")).sendKeys("123");chromeDriver.findElement(By.cssSelector("#submit")).click();// user1和user2进同一游戏房间edgeDriver.findElement(By.cssSelector("#match-button")).click();chromeDriver.findElement(By.cssSelector("#match-button")).click();}/*** 测试游戏对战页面* 检查点:有棋盘、提示版元素*/@Test@Order(1)public void testGameBoard() {// 不同系统的两个用户匹配到同一个房间userLogin();// 测试游戏对战页面edgeDriver.findElement(By.cssSelector("#chess"));chromeDriver.findElement(By.cssSelector("#chess"));}

4.项目测试的结果

视频演示链接

在这里插入图片描述


完整项目代码可联系博主。

总结

这篇文章记录了五子棋项目现在已实现的功能及web页面自动化测试,后续将对该项目扩充聊天功能(同一个房间的用户可以发送消息)及测试方法。

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

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

相关文章

生成式AI:最有商业前景的人工智能技术,不再是改变分发关系,而是升级生产力

根据最新的报告可以看出ChatGPT到底有多厉害&#xff0c;多个方面实现从判别决策到创造生成 生成式AI VS Web 3.0 &#xff1a;不追求生产关系的重塑&#xff0c;但将大幅度提升和创造生产力 创造是生成式AI的核心&#xff0c;本质是对生产力的大幅度提升和创造。生成式AI通过…

2、java语法之循环、数组与方法(找工作版)

写在前面&#xff1a;整个系列文章是自己学习慕课相关视频&#xff0c;进行的一个总结。文章只是为了记录学习课程的整个过程&#xff0c;方便以后查漏补缺&#xff0c;找到对应章节。 文章目录 一、Java循环结构1、while循环2、do-while循环3、for循环4、嵌套循环5、break语句…

【面试题】如何在级别用户中检查用户名是否存在?

前言 不知道大家有没有留意过&#xff0c;在使用一些app或者网站注册的时候&#xff0c;提示你用户名已经被占用了&#xff0c;比如我们熟知的《英雄联盟》有些人不知道取啥名字&#xff0c;干脆就叫“不知道取啥名”。 但是有这样困惑的可不止他一个&#xff0c;于是就出现了…

PaddleVideo:PP-TSM 视频分类

本文记录&#xff1a;使用Paddle框架训练TSM&#xff08;Temporal Shift Module&#xff09; 前提条件&#xff1a;已经安装Paddle和PadleVideo&#xff0c;具体可参考前一篇文章。 1-数据准备&#xff1a; 以UCF101为例&#xff1a;内含13320 个短视频&#xff0c;视频类别&…

2024年32款数据分析工具分五大类总览

数据分析工具在现代商业和科学中扮演着不可或缺的角色&#xff0c;为组织和个人提供了深入洞察和明智决策的能力。这些工具不仅能够处理大规模的数据集&#xff0c;还能通过强大的分析和可视化功能揭示隐藏在数据背后的模式和趋势。数据分析工具软件主要可以划分为以下五个类别…

YOLOv5标签值含义根据标签将检测框色块替换(马赛克)

以一个检测人脸的图片为例&#xff1a; 检测后生成的标签txt如下&#xff0c; 此时&#xff0c;如何根据标签值将检测到的人脸同色块替换呢&#xff1f; 关键是获取检测框的左上角坐标和右下角坐标。 img Image.open(D:/PythonWokspace/JINX/datasets_transform/dataset/im…

【鸿蒙开发】系统组件Text,Span

Text组件 Text显示一段文本 接口&#xff1a; Text(content?: string | Resource) 参数&#xff1a; 参数名 参数类型 必填 参数描述 content string | Resource 否 文本内容。包含子组件Span时不生效&#xff0c;显示Span内容&#xff0c;并且此时text组件的样式不…

图片管理系统:原理、设计与实践

title: 图片管理系统&#xff1a;原理、设计与实践 date: 2024/4/9 20:04:25 updated: 2024/4/9 20:04:25 tags: 图片管理存储组织上传采集处理编辑搜索检索展示分享AI应用 第一章&#xff1a;图片管理系统概述 1.1 图片管理系统简介 图片管理系统是一种用于存储、组织、处理…

跨地域分布的企业,SD-WAN帮助企业实现统一管理

全球化进程的加速&#xff0c;越来越多的企业在全球范围内展开业务&#xff0c;跨地域分布的企业网络管理面临着诸多挑战。SD-WAN作为一种新兴的网络技术&#xff0c;为跨地域分布的企业提供了一种有效的解决方案&#xff0c;帮助企业实现统一管理和集中控制。本文将探讨SD-WAN…

【负载均衡——一致性哈希算法】

1.一致性哈希是什么 一致性哈希算法就很好地解决了分布式系统在扩容或者缩容时&#xff0c;发生过多的数据迁移的问题。 一致哈希算法也用了取模运算&#xff0c;但与哈希算法不同的是&#xff0c;哈希算法是对节点的数量进行取模运算&#xff0c;而一致哈希算法是对 2^32 进…

摩尔信使MThings之数据网关:Modbus转MQTT

由于现场设备和物联网云平台采用了不同的通信协议&#xff0c;而为了实现它们之间的互操作性和数据交换&#xff0c;需要进行协议转换。 MQTT作为一种轻量级的、基于发布/订阅模式的通信协议&#xff0c;适用于连接分布式设备和传感器网络&#xff0c;而MODBUS协议则常用于工业…

Java之枚举详细总结

枚举是一种特殊类。 枚举类的格式&#xff1a; 修饰符 enum 枚举类名{名称1&#xff0c;名称2&#xff0c;...&#xff1b;其他成员 } 例如&#xff1a; public enum A {//枚举类中的第一行必须枚举对象的名字X,Y,Z; ​private String name; ​public String getName() {retu…

Linux初学(十七)防火墙

一、防火墙简介 1.1 防火墙的类别 安全产品 杀毒&#xff1a; 针对病毒&#xff0c;特征篡改系统中的文件杀毒软件针对处理病毒程序防火墙&#xff1a; 针对木马&#xff0c;特征系统窃取防火墙针对处理木马 防火墙分为两种 硬件防火墙软件防火墙 硬件防火墙 各个网络安全…

el-table实现表格内部横向拖拽效果

2024.4.2今天我学习了如何对el-table表格组件实现内部横向拖拽的效果&#xff0c;效果&#xff1a; 代码如下&#xff1a; 一、创建utils/底下文件 const crosswise_drag_table function (Vue){// 全局添加table左右拖动效果的指令Vue.directive(tableMove, {bind: function…

Unity类银河恶魔城学习记录12-8 p130 Skill Tree UI源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI.cs using UnityEngine;public class UI : MonoBehaviour {[SerializeFi…

抖音电商小店短视频直播年度运营规划方案

【干货资料持续更新&#xff0c;以防走丢】 抖音电商小店短视频直播年度运营规划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 PPT可编辑&#xff08;完整资料包含以下内容&#xff09; 目录 年度运维方案的详细整理和规划。 一、行业分析洞察 - 市场增…

【linux基础】bash脚本的学习:定义变量及引用变量、统计目标目录下所有文件行数、列数

假设目的&#xff1a;统计并输出指定文件夹下所有文件行数 单个文件可以用 wc -l &#xff1b;多个文件&#xff0c;可以用通配符 / 借助bash脚本 1.定义变量名&#xff0c;使用引号 a"bestqc.com.map" b"Anno.variant_function" c"enrichment/GOe…

访问网站时你的电脑都做了什么

电脑在访问百度时 首先在本地hosts文件里面查看本地有无域名对应的IP地址&#xff0c;若有就直接返回。若无&#xff0c;则本地DNS服务器当DNS的客户&#xff0c;向其它根域服务器发送报文查询IP地址&#xff0c;简单来说就是帮助主机查找IP&#xff0c;所以递归查询就在客户端…

【Ubuntu】远程连接乌班图的方式-命令行界面、图形界面

环境&#xff1a;ubuntu-22.04.2-amd64.iso连接工具&#xff1a;MobaXterm、windows自带远程桌面mstsc.exe重置root密码&#xff1a;Ubuntu默认root密码是随机的&#xff0c;需要使用命令sudo passwd 进行重置。 一、命令行界面-SSH连接 1.1 SSH远程环境搭建 # 安装ssh服务&a…

无需训练,这个新方法实现了生成图像尺寸、分辨率自由

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 近日&#xff0c;来自香港中文大学 - 商汤科技联合实验室等机构的研究者们提出了FouriScale&…