HarmonyOS开发实例:【手势截屏】

 介绍

本篇Codelab基于手势处理和截屏能力,介绍了手势截屏的实现过程。样例主要包括以下功能:

  1. 根据下滑手势调用全屏截图功能。
  2. 全屏截图,同时右下角有弹窗提示截图成功。
  3. 根据双击手势调用区域截图功能。
  4. 区域截图,通过调整选择框大小完成。

相关概念

  • Canvas:画布组件,用于自定义绘制图形。
  • CanvasRenderingContext2D对象:使用RenderingContext在Canvas组件上进行绘制,绘制对象可以是矩形、文本、图片等。
  • 双击手势:手指双击屏幕回调事件。
  • 手指滑动手势:手指在屏幕滑动回调事件。

相关权限

  • 本篇Codelab用到屏幕截图的能力,需要在配置文件module.json5里添加屏幕截图的权限:ohos.permission.CAPTURE_SCREEN。
  • 本篇Codelab需要使用的screenshot为系统接口。需要使用Full SDK手动从镜像站点获取,并在DevEco Studio中替换。
  • 本篇Codelab使用的部分API仅系统应用可用,需要提升应用等级为system_core。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以3.2 Release版本为例:

  2. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  3. 搭建开发环境。

    1. 开始前请参考[工具准备]qr23.cn/AKFP8k,完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用[真机进行调测]。

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

├──entry/src/main/ets           // 代码区
│  ├──common
│  │  └──utils
│  │     ├──CommonConstants.ets	// 公共常量类
│  │     ├──DrawUtil.ets        // 画布相关工具类
│  │     └──Logger.ets          // 日志打印类
│  ├──entryability
│  │  └──EntryAbility.ets       // 程序入口类
│  ├──model
│  │  └──OffsetModel.ets        // 区域截图坐标相关工具类
│  ├──pages
│  │  └──GestureScreenshot.ets  // 主界面	
│  └──view
│     ├──AreaScreenshot.ets     // 自定义区域截屏组件类
│     └──ScreenshotDialog.ets   // 自定义截屏显示弹窗组件类
└──entry/src/main/resources     // 资源文件目录

构建截屏主页面

使用下滑手势,进行全屏截图并展示图片。效果如图所示:

主界面主要实现以下功能:

  1. 下滑手势绑定在主界面上,双击手势绑定在区域手势的最底层Stack组件上。
  2. 如果使用下滑手势,就进行全屏截图并展示图片。
  3. 如果使用双击手势,就唤起区域截图相关组件。

搜狗高速浏览器截图20240326151450.png

// GestureScreenshot.ets
// 区域截图最底层,当主页面缩放后会露出,设置为黑色
Stack() {
  // 主页面布局
  Column() {
    ...
  })
  // 添加滑动手势事件
  .gesture(
    // fingers:触发手指数 direction:触发方向 distance:触发滑动距离
    PanGesture({
      fingers: 1,
      direction: PanDirection.Down,
      distance: CommonConstants.MINIMUM_FINGER_DISTANCE
    })// 触发开始回调
      .onActionStart(() => {
        let screenshotOptions: screenshot.ScreenshotOptions = {
          rotation: 0
        };
        screenshot.save(screenshotOptions, (err: Error, data: image.PixelMap) => {
          if (err) {
            Logger.error(`Failed to save the screenshot. Error:${ JSON.stringify(err) }`);
          }
          if (this.pixelMap !== undefined) {
            this.pixelMap.release();
          }
          this.pixelMap = data;
          this.dialogController.open();
        });
      })
  )
  .scale(this.scaleNum)

  // 区域截图相关组件
  AreaScreenshot({ showScreen: this.showScreen, pixelMap: this.pixelMap, scaleNum: this.scaleNum })
}
.backgroundColor($r('app.color.black_area'))
// 添加双击手势事件
.gesture(
  TapGesture({ count: 2 })
    .onAction(() => {
      this.showScreen = true;
      this.scaleNum = {
        x: CommonConstants.X_SCALE_DOWN,
        y: CommonConstants.Y_SCALE_DOWN
      }
    })
)

构建区域截图组件

本章节将完成区域选择框的绘制并完成区域截图,效果如图所示:

在绘制区域选择框之前,首先需要在AreaScreenshot.ets的aboutToAppear方法中获取屏幕的宽和高,并初始化offsetModel和drawUtil对象(初始化参数为屏幕的宽高)。offsetModel对输入的坐标进行计算和更改,drawUtil使用offsetModel的坐标在屏幕上绘制区域选择框。

// AreaScreenshot.ets
aboutToAppear() {
  window.getLastWindow(getContext(this))
    .then((window) => {
      let property = window.getWindowProperties();
      this.systemBarHeight = property.windowRect.top;

      drawUtil.initDrawUtil(
        this.canvasRenderingContext,
        px2vp(property.windowRect.width),
        px2vp(property.windowRect.height)
      );

      offsetModel.initOffsetModel(
        px2vp(property.windowRect.width),
        px2vp(property.windowRect.height)
      );

      // 在展示截图的时候,用于计算图片大小
      this.screenAspectRatio = px2vp(property.windowRect.height) / px2vp(property.windowRect.width);
    })
    .catch((err: Error) => {
      Logger.error(`window loading has error: ${ JSON.stringify(err) }`);
    })
}

在AreaScreenshot.ets布局页面中添加Canvas组件,通过showScreen变量控制局部截屏页面的显示,并控制主页面的缩放。步骤如下:

  1. 根据手指按下的位置确定需要移动的边框。
  2. 手指移动后,更新offsetModel记录的坐标信息。
  3. 根据offsetModel提供的坐标,使用drawUtil绘制区域选择框。
  4. 点击保存按钮后,设置截屏区域坐标。
  5. 根据截屏区域坐标进行区域截屏。
// AreaScreenshot.ets
// 关闭区域截屏相关组件,并还原主页面
private resetParameter() {
  this.showScreen = false;
  this.scaleNum = {
    x: CommonConstants.NO_SCALE_DOWN,
    y: CommonConstants.NO_SCALE_DOWN
  };
  offsetModel.resetDefaultOffSet();
}

// 使用if渲染,控制区域截图相关组件的显隐
if (this.showScreen) {
  Stack() {
    Canvas(this.canvasRenderingContext)
      ...
      .onReady(() => {
        // 通过draw方法绘制选择框和非高亮区域
        drawUtil.draw();
      })
    // 截图的工具栏
    Row() {
      ...
      // 区域截图并展示图像
      Image($r('app.media.ic_save'))
        .onClick(() => {
          let screenshotOptions: screenshot.ScreenshotOptions = {
            // 截屏区域Rect参数
            screenRect: {
              left: vp2px(offsetModel.getXLeft()),
              top: vp2px(offsetModel.getYTop()) + this.systemBarHeight,
              width: vp2px(offsetModel.getWidth()),
              height: vp2px(offsetModel.getHeight())
            } as screenshot.Rect,
            // 截图的大小
            imageSize: {
              width: vp2px(offsetModel.getWidth()),
              height: vp2px(offsetModel.getHeight())
            } as screenshot.Size,
            rotation: 0,
            displayId: 0
          };
          screenshot.save(screenshotOptions, (err: Error, data: image.PixelMap) => {
            if (err) {
              Logger.error(`Failed to save the screenshot. Error:${JSON.stringify(err)}`);
            }
            if (this.pixelMap !== undefined) {
              this.pixelMap.release();
            }
            this.pixelMap = data;
            // 使用弹窗组件展示截完的图片
            this.dialogController.open();
          });
          this.resetParameter();
        })
    }
    ...
    // 根据手指位置调整选择框大小和位置
    .onTouch((event: TouchEvent) => {
      switch(event.type) {
        case TouchType.Down:
          // 根据手指位置,判断移动哪个坐标
          offsetModel.setXLocationType(event.touches[0].screenX);
          offsetModel.setYLocationType(event.touches[0].screenY);
          break;
        case TouchType.Move:
          // 更新坐标信息,并保证坐标值合法
          offsetModel.resetOffsetXY(event.touches[0].screenX, event.touches[0].screenY);
          drawUtil.draw();
          break;
        default:
          break;
      }
    })
}

区域选择框工具类的实现

在构建区域截图组件中介绍了OffsetModel和DrawUtil两个工具类,本章节介绍一下具体的实现步骤。

使用OffsetModel校验坐标的范围,并保存坐标相关信息。

  1. 在初始化对象的时候,根据屏幕的缩放比例计算出黑色区域的宽高。
  2. 使用setXLocationType方法和setYLocationType方法,判断需要移动的x、y坐标位置。
  3. 根据传入的x、y坐标值,更改offset对应的坐标值,并保证选择框的宽高大于等于预设的选择框的最小值。
  4. 再次校验offset坐标值,是否超出可截屏区域。
// OffsetModel.ets
public initOffsetModel(width: number, height: number) {
  ...
  this.blackAreaWidth = this.screenWidth * (1 - CommonConstant.X_SCALE_DOWN);
  this.blackAreaWidth = this.blackAreaWidth / CommonConstant.BLACK_AREA_NUM;
  this.blackAreaHeight = this.screenHeight * (1 - CommonConstant.Y_SCALE_DOWN);
  this.blackAreaHeight = this.blackAreaHeight / CommonConstant.BLACK_AREA_NUM;
}

// 判断x坐标位置
public setXLocationType(offsetX: number) {
  if (offsetX > this.offsetXRight - CommonConstant.OFFSET_RANGE &&
    offsetX < this.offsetXRight + CommonConstant.OFFSET_RANGE) {
    this.xLocationType = XLocationEnum.XRight;
  } else if (offsetX > this.offsetXLeft - CommonConstant.OFFSET_RANGE &&
    offsetX < this.offsetXLeft + CommonConstant.OFFSET_RANGE) {
    this.xLocationType = XLocationEnum.XLeft;
  } else {
    this.xLocationType = XLocationEnum.noChange;
  }
}

// 判断y坐标位置
public setYLocationType(offsetY: number) {
  ...
}

// 根据参数改变坐标值
public resetOffsetXY(offsetX: number, offsetY: number) {
  if (this.xLocationType === XLocationEnum.XLeft) {
    this.offsetXLeft = this.offsetXRight - offsetX < CommonConstant.OFFSET_RANGE * 2 ?
      this.offsetXLeft : offsetX;
  }
  ...

  this.checkOffsetXY();
}

// 再次校验坐标值,是否超出可截屏区域
private checkOffsetXY() {
  this.offsetXLeft = this.offsetXLeft < this.blackAreaWidth ? this.blackAreaWidth : this.offsetXLeft;
  this.offsetXRight = this.offsetXRight > this.screenWidth - this.blackAreaWidth ?
    this.screenWidth - this.blackAreaWidth : this.offsetXRight;
  this.offsetYTop = this.offsetYTop < this.blackAreaHeight ? this.blackAreaHeight : this.offsetYTop;
  this.offsetYBottom = this.offsetYBottom > this.screenHeight - this.blackAreaHeight ?
    this.screenHeight - this.blackAreaHeight : this.offsetYBottom;
}

DrawUtil主要提供绘制方法,用于绘制区域选择框。

// DrawUtil.ets
// 绘制整个区域选择框
public draw() {
  this.offsetXLeft = offsetModel.getXLeft();
  this.offsetXRight = offsetModel.getXRight();
  this.offsetYTop = offsetModel.getYTop();
  this.offsetYBottom = offsetModel.getYBottom();
  
  // 填充非高亮区域
  this.drawScreenSelection();
  // 绘制框选线
  this.drawLines();
}

// 填充非高亮区域,设置回形区域并填充颜色
private drawScreenSelection() {
  this.canvasContext.clearRect(0, 0, this.screenWidth, this.screenHeight)
  this.canvasContext.beginPath();

  this.canvasContext.moveTo(0, 0);
  this.canvasContext.lineTo(this.screenWidth, 0);
  this.canvasContext.lineTo(this.screenWidth, this.screenHeight);
  this.canvasContext.lineTo(0, this.screenHeight);
  this.canvasContext.closePath();

  this.canvasContext.moveTo(this.offsetXRight, this.offsetYTop);
  this.canvasContext.lineTo(this.offsetXLeft, this.offsetYTop);
  this.canvasContext.lineTo(this.offsetXLeft, this.offsetYBottom);
  this.canvasContext.lineTo(this.offsetXRight, this.offsetYBottom);

  this.canvasContext.globalAlpha = Constants.UNSELECT_AREA_ALPHA;
  this.canvasContext.fillStyle = Constants.UNSELECT_AREA_COLOR;
  this.canvasContext.closePath();
  this.canvasContext.fill();
}

// 绘制框选线
private drawLines() {
  this.canvasContext.beginPath();
  ...
  this.canvasContext.moveTo(
    (this.offsetXLeft + Constants.LINES_MAX_LENGTH),
    (this.offsetYTop - Constants.GAP_WIDTH)
  );
  this.canvasContext.lineTo(
    (this.offsetXLeft - Constants.GAP_WIDTH),
    (this.offsetYTop - Constants.GAP_WIDTH)
  );
  this.canvasContext.lineTo(
    (this.offsetXLeft - Constants.GAP_WIDTH),
    (this.offsetYTop + Constants.LINES_MAX_LENGTH)
  );
  ...
  this.canvasContext.stroke();
}

展示截图

采用弹窗组件展示截屏,需要在aboutToAppear方法中计算对应的宽度:

  1. 截图长宽比小于或者等于屏幕长宽比:此截图展示时和全屏截图展示时等宽。
  2. 截图长宽比大于屏幕长宽比:此截图展示时和全屏截图展示时等长,通过计算对应的宽来实现。
// ScreenshotDialog.ets
aboutToAppear() {
  this.getDialogWidth();
}
...
private async getDialogWidth() {
  if (this.pixelMap !== undefined) {
    let info = await this.pixelMap.getImageInfo();
    let pixelMapAspectRatio = info.size.height / info.size.width;

    if ((this.screenAspectRatio !== -1) && (pixelMapAspectRatio > this.screenAspectRatio)) {
      let width = CommonConstants.HEIGHT_FIRST / pixelMapAspectRatio * this.screenAspectRatio;
      this.dialogWidth = width + '%';
    } else {
      this.dialogWidth = CommonConstants.WIDTH_FIRST;
    }
  }
}

鸿蒙开发岗位需要掌握那些核心要领?

目前还有很多小伙伴不知道要学习哪些鸿蒙技术?不知道重点掌握哪些?为了避免学习时频繁踩坑,最终浪费大量时间的。

自己学习时必须要有一份实用的鸿蒙(Harmony NEXT)资料非常有必要。 这里我推荐,根据鸿蒙开发官网梳理与华为内部人员的分享总结出的开发文档。内容包含了:【ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战】等技术知识点。

废话就不多说了,接下来好好看下这份资料。

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。鸿蒙OpenHarmony知识←前往。下面是鸿蒙开发的学习路线图。

针对鸿蒙成长路线打造的鸿蒙学习文档。鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

其中内容包含:

《鸿蒙开发基础》鸿蒙OpenHarmony知识←前往

  1. ArkTS语言
  2. 安装DevEco Studio
  3. 运用你的第一个ArkTS应用
  4. ArkUI声明式UI开发
  5. .……

《鸿蒙开发进阶》鸿蒙OpenHarmony知识←前往

  1. Stage模型入门
  2. 网络管理
  3. 数据管理
  4. 电话服务
  5. 分布式应用开发
  6. 通知与窗口管理
  7. 多媒体技术
  8. 安全技能
  9. 任务管理
  10. WebGL
  11. 国际化开发
  12. 应用测试
  13. DFX面向未来设计
  14. 鸿蒙系统移植和裁剪定制
  15. ……

《鸿蒙开发实战》鸿蒙OpenHarmony知识←前往

  1. ArkTS实践
  2. UIAbility应用
  3. 网络案例
  4. ……

最后

鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Excel 记录单 快速录入数据

一. 调出记录单 ⏹记录单功能默认是隐藏的&#xff0c;通过如下如图所示的方式&#xff0c;将记录单功能显示出来。 二. 录入数据 ⏹先在表格中录入一行数据&#xff0c;给记录单一个参考 ⏹将光标至于表格右上角&#xff0c;然后点击记录单按钮&#xff0c;调出记录单 然后点…

百元不入耳运动耳机哪个品牌好?五款业内顶尖品牌推荐

在追求舒适与健康的运动中&#xff0c;不入耳式&#xff08;开放式耳机&#xff09;运动耳机逐渐成为了许多运动爱好者的首选&#xff0c;它们不仅避免了长时间佩戴耳机带来的不适&#xff0c;还能在享受音乐的同时保持对环境的警觉&#xff0c;确保运动安全&#xff0c;市场上…

Python中同时调用多个列表

如果你有多个列表&#xff0c;想要同时迭代它们&#xff0c;可以使用zip()函数。zip()函数可以将多个可迭代对象合并成一个元组的迭代器&#xff0c;然后你可以在循环中使用它。 问题背景 当需要在Python脚本中避免重复相同任务时&#xff0c;可以使用for循环来遍历列表。但是…

Volatility-内存取证案例1-writeup--xx大赛

题目提示&#xff1a;flag{中文} 按部就班 &#xff08;1&#xff09;获取内存镜像版本信息 volatility -f 文件名 imageinfo 通过上述可知&#xff0c;镜像版本为Win7SP1X64。 &#xff08;2&#xff09;获取进程信息&#xff1a; volatility -f 镜像名 --profile第一步获取…

面壁智能完成新一轮数亿元融资,继续面向AGI的高效大模型征程

近日&#xff0c;面壁智能完成新一轮数亿元融资&#xff0c;由春华创投、华为哈勃领投&#xff0c;北京市人工智能产业投资基金等跟投&#xff0c;知乎作为战略股东持续跟投支持。本轮融资完成后&#xff0c;面壁智能将进一步推进优秀人才引入&#xff0c;加固大模型发展的底层…

6.12物联网RK3399项目开发实录-驱动开发之UART 串口的使用(wulianjishu666)

嵌入式实战开发例程【珍贵收藏&#xff0c;开发必备】&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1tkDBNH9R3iAaHOG1Zj9q1Q?pwdt41u UART 使用 简介 AIO-3399J 支持 SPI 桥接/扩展 4 个增强功能串口&#xff08;UART&#xff09;的功能&#xff0c;分别为 UA…

LeetCode 面试题 02.07.链表相交(判断两个结点是否相同)

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&#x…

Qt中的网络通信

C没有封装专门的网络套接字的类&#xff0c;因此C只能调用C对应的API&#xff0c;而在Linux和Windows环境下的API都是不一样的 Qt作为一个C框架提供了相关封装好的套接字通信类 在Qt中需要用到两个类&#xff0c;两个类都属于network且都是属于IO操作&#xff0c;只不过这两个类…

ArcGIS Desktop使用入门(三)图层右键工具——缩放至图层、缩放至可见

系列文章目录 ArcGIS Desktop使用入门&#xff08;一&#xff09;软件初认识 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——标准工具 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——编辑器 ArcGIS Desktop使用入门&#xff08;二&#x…

Java 怎么捕捉 Windows 中前台窗口的改变?

在Java中捕捉Windows中前台窗口的改变通常需要使用JNI&#xff08;Java Native Interface&#xff09;来调用Windows API。Windows API提供了一系列函数来获取有关窗口和进程的信息&#xff0c;通过使用这些函数&#xff0c;我们可以实现在Java程序中监视和捕捉Windows前台窗口…

抖音爬虫——点赞量

该爬虫模拟了一个get请求来得到返回json里面的点赞量信息 下面介绍如何使用&#xff1a; 首先&#xff0c;我们找一个浏览器打开抖音搜索具体的关键词 接着我们点击键盘的F12建 就会出现如下的界面&#xff0c;接着我们点击网络&#xff08;可能再一些浏览器是叫network&…

JavaSE:this关键字(代码和内存图讲解)

this的含义 this代表当前对象&#xff0c;谁调用this所在的方法&#xff0c;this就代表谁 这句话非常重要 demo 以这段代码为例&#xff0c;setNum方法内部的this&#xff0c;setStr方法内部的this&#xff0c;还有构造方法ThisKeyword(int num, String str)内部的两个this…

软件库V1.2版本开源-首页UI优化

iAppV3源码&#xff0c;首页的分类更换成了标签布局&#xff0c;各位可以参考学习&#xff0c;界面名称已经中文标注&#xff01; 老版本和现在的版本还是有较大的区别的&#xff0c;建议更新一下&#xff01; 新版本改动界面如下&#xff1a; 1、首页.iyu&#xff1a;分类按…

基于javassm实现的幼儿教育管理系统

开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclip…

晶核职业选择:六大角色技能揭秘,成为战斗高手!

在晶核的世界中&#xff0c;每一位玩家都扮演着不同角色&#xff0c;组成多样的团队&#xff0c;共同踏上探索未知的征程。而每个角色都有其独特的技能和特点&#xff0c;下面将为你详细介绍每个角色的技能搭配和操作技巧&#xff0c;让你在战斗中游刃有余&#xff0c;一展自己…

MPT - 原理及应用

前文回顾 Merkle原理及应用Merkle代码实现Patricia原理及应用Patricia代码实现 什么是MPT&#xff08;Merkle Patricia Tree&#xff09;树 MPT树是一种数据结构&#xff0c;用于在以太坊区块链中高效地存储和检索账户状态、交易历史和其他重要数据。MPT树的设计旨在结合Merk…

python之文件操作与管理

1、文件操作 通过open&#xff08;&#xff09;操作&#xff0c;来创建文件对象&#xff0c;下面是open&#xff08;&#xff09;函数语法如下&#xff1a; open&#xff08;file,mode r,buffering -1 , encoding None ,errors None , newline None,closefd True,opener …

分布式向量数据库-安装部署

下载 GitHub - pgvector/pgvector: Open-source vector similarity search for Postgres 源码编译 ##文件解压缩 unzip pgvector-0.6.2.zip ##编译 make && make install 功能验证 #安装扩展CREATE EXTENSION vector;#创建测试表CREATE TABLE items (id bigseri…

互联网需要做安全防护吗?

互联网需要做安全防护&#xff0c;因为网络攻击的风险随时存在。一旦遭受大规模攻击&#xff0c;企业很可能会受到严重影响&#xff0c;甚至会造成巨大的经济损失和品牌声誉受损。因此&#xff0c;建议企业在安全防护方面做好以下几点&#xff1a; 加强网络安全意识教育&#x…

libVLC 视频窗口上叠加透明窗口

很多时候&#xff0c;我们需要在界面上画一些三角形、文字等之类的东西&#xff0c;我们之需要重写paintEvent方法&#xff0c;比如像这样 void Widget::paintEvent(QPaintEvent *event) 以下就是重写的代码。 void Widget::paintEvent(QPaintEvent *event) {//创建QPainte…