Compose Multiplatform+Kotlin Multiplatfrom 第五弹跨平台 截图
截图功能
Compose Multiplatform+Kotlin Multiplatfrom下实现桌面端的截图功能,起码搞了两星期,最后终于做出来了,操作都很流畅,截取的文件大小也正常,可参考支持讨论!
功能效果
代码实现
//在jvmMain下创建TestCapture11.kt,完整的截图核心功能代码package com.hwj.ai.captureimport androidx.compose.foundation.Canvas
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposeWindow
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.isSecondaryPressed
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.rememberWindowState
import com.hwj.ai.global.printD
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.awt.GraphicsEnvironment
import java.awt.Rectangle
import java.awt.Robot
import java.awt.Toolkit
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO//UI 拖拽得到的坐标 * scaleFactor ≠ 实际屏幕坐标
//macOS 多屏/高DPI 下 Robot 截图区域
//效果suc,windows/macOS 主屏幕
class ScreenshotState11 {var startOffset by mutableStateOf(Offset.Zero)var endOffset by mutableStateOf(Offset.Zero)val selectionRect: Rectget() = Rect(startOffset, endOffset)var isSelecting by mutableStateOf(false)
}val LocalMainWindow = staticCompositionLocalOf<ComposeWindow> { error("No Window provided") }@Composable
fun ScreenshotOverlay11(mainWindow: ComposeWindow,onCapture: (BufferedImage) -> Unit,onCancel: () -> Unit
) {var showActBtn by remember { mutableStateOf(false) }var capturedRect by remember { mutableStateOf<Rect?>(null) }val state = remember { ScreenshotState11() }val screenSize = Toolkit.getDefaultToolkit().screenSizeval windowState = rememberWindowState(position = WindowPosition(0.dp, 0.dp),size = DpSize(screenSize.width.dp, screenSize.height.dp))val subScope = rememberCoroutineScope()val focusReq = remember { FocusRequester() }LaunchedEffect(Unit) {mainWindow.isVisible = false}Window(onCloseRequest = {onCancel()mainWindow.isVisible = true},state = windowState,transparent = true,undecorated = true,alwaysOnTop = true,focusable = true,resizable = false) {LaunchedEffect(Unit) {window.requestFocus() //触发快捷键focusReq.requestFocus()}Box(modifier = Modifier.fillMaxSize().focusRequester(focusReq).focusable().pointerInput(Unit) { //识别鼠标右键取消awaitPointerEventScope {while (true) {val event = awaitPointerEvent()val pressed = event.buttons.isSecondaryPressedif (event.type == PointerEventType.Press && pressed) {onCancel()mainWindow.isVisible = true}}}}.onPreviewKeyEvent { keyEvent ->
// printD("keyEvent>${keyEvent.key} ${Key.Escape}")if (keyEvent.key == Key.Escape) { //快捷键取消onCancel()mainWindow.isVisible = truetrue} else {false}}.pointerInput(Unit) {detectDragGestures(onDragStart = { offset ->state.isSelecting = truestate.startOffset = offsetstate.endOffset = offset},onDrag = { change, _ ->state.endOffset = change.position},onDragEnd = {capturedRect = state.selectionRect.normalize()//如果只是点了两下,宽高都很少,不足以被认定为截图!showActBtn = true})}) {Canvas(modifier = Modifier.fillMaxSize()) {// 背景遮罩drawRect(Color.Black.copy(alpha = 0.3f))// 选区范围和尺寸if (state.isSelecting) {val rect = state.selectionRect.normalize()// 半透明填充drawRect(color = Color.White.copy(alpha = 0.05f),topLeft = rect.topLeft,size = rect.size)// 白色描边drawRect(color = Color.White,topLeft = rect.topLeft,size = rect.size,style = Stroke(width = 2.dp.toPx()))}}//在截图框下放按钮if (showActBtn && capturedRect != null) {val isFullCapture =capturedRect!!.width > screenSize.width * 0.8f && capturedRect!!.height > screenSize.height * 0.8fval myModifier: Modifierif (isFullCapture) {myModifier = Modifier.align(Alignment.TopEnd).padding(23.dp)} else {val offx = with(LocalDensity.current) { capturedRect!!.right.toDp() - 180.dp }val offy = with(LocalDensity.current) { capturedRect!!.bottom.toDp() + 0.dp }myModifier = Modifier.offset(x = offx, y = offy)}Box(modifier = myModifier) {Row(modifier = Modifier.align(Alignment.BottomCenter).padding(13.dp)) {Button(onClick = {showActBtn = falsemainWindow.isVisible = truestate.isSelecting = false //有必要吗capturedRect = null //点击取消没有退出onCancel() //关闭}) {Text("取消", color = Color.White)}Spacer(modifier = Modifier.width(10.dp))Button(onClick = {if (null != capturedRect) {subScope.launch {captureSelectedArea(capturedRect!!) { pic ->// val thumbnail =
// BufferedImage(
// 200,
// 200,
// BufferedImage.TYPE_INT_ARGB
// ).apply {
// createGraphics().drawImage(
// pic.getScaledInstance(
// 200,
// 200,
// java.awt.Image.SCALE_SMOOTH
// ),
// 0, 0, null
// )
// }
// onCapture(thumbnail) //传缩略图onCapture(pic)}withContext(Dispatchers.Main) {showActBtn = falsemainWindow.isVisible = trueonCancel()}}} else {showActBtn = falsemainWindow.isVisible = trueonCancel()}}) {Text("确定", color = Color.White)}}}}}}
}private suspend fun captureSelectedArea(rect: Rect, onSuccess: (BufferedImage) -> Unit) {val normalizedRect = rect.normalize()val screenDevices = GraphicsEnvironment.getLocalGraphicsEnvironment().screenDevicesvar targetDevice: java.awt.GraphicsDevice? = null// // 找到选区落在哪块屏幕上for (device in screenDevices) {val bounds = device.defaultConfiguration.boundsif (bounds.contains(normalizedRect.left.toInt(), normalizedRect.top.toInt())) {targetDevice = devicebreak}}//多屏不让用
// if (screenDevices.size>1){
// NotificationsManager().showNotification("不支持多屏截图!","不支持多屏截图")
// return
// }// targetDevice=screenDevices[0]if (targetDevice == null) {targetDevice =java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice}val config = targetDevice!!.defaultConfigurationval screenBounds = config.bounds // 屏幕偏移(多屏布局下重要)val transform = config.defaultTransformval scaleX = transform.scaleXval scaleY = transform.scaleY
// printD("屏幕 bounds: $screenBounds, scaleX: $scaleX, scaleY: $scaleY")// 关键:Compose 逻辑坐标 → 物理像素坐标 ,超级大坑,用chatgpt写代码一直反馈是乘scaleX,实际是除以,不然容易黑屏val captureX = (normalizedRect.left / scaleX).toInt()val captureY = (normalizedRect.top / scaleY).toInt()val captureW = (normalizedRect.width / scaleX).toInt()val captureH = (normalizedRect.height / scaleY).toInt()// printD("最终截图区域 (物理像素): x=$captureX, y=$captureY, w=$captureW, h=$captureH")if (captureW <= 0 || captureH <= 0) returntry {// 隐藏截图窗口,防止蒙层被截进去val awtWindow = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().activeWindowawtWindow?.isVisible = falseThread.sleep(100) // 等隐藏生效val robot = Robot(targetDevice)val screenRect = Rectangle(captureX, captureY, captureW, captureH)val image = robot.createScreenCapture(screenRect)onSuccess(image)} catch (e: Exception) {e.printStackTrace()}
}fun saveToFile11(image: BufferedImage): String? {// val desktopPath = System.getProperty("user.home") + File.separator + "Desktop"
// val file = File(desktopPath, "screenshot_${System.currentTimeMillis()}.png")val cacheDir = getPlatformCacheImgDir()if (!cacheDir.exists()) cacheDir.mkdirs()val file = File(cacheDir, "screenshot_${System.currentTimeMillis()}.png")ImageIO.write(image, "PNG", file)printD("截图已保存到:${file.absolutePath}")ImageIO.write(image, "PNG", file).also {if (it) return file.absolutePath}return null
}//截图已保存到缓存目录:/Users/你的用户名/Library/Caches/com.hwj.ai.capture/screenshot_1710918988888.png
//截图已保存到缓存目录:C:\Users\你的用户名\AppData\Local\com.hwj.ai.capture\cache\screenshot_1710918988888.png
//截图已保存到缓存目录:/home/你的用户名/.cache/com.hwj.ai.capture/screenshot_1710918988888.png
private fun getPlatformCacheImgDir(): File {val osName = System.getProperty("os.name").lowercase()return if (osName.contains("mac")) {File(System.getProperty("user.home"), "Library/Caches/com.hwj.ai.capture")} else if (osName.contains("win")) {File(System.getenv("LOCALAPPDATA"), "com.hwj.ai.capture/cache")} else {File(System.getProperty("user.home"), ".cache/com.hwj.ai.capture")}
}/*** 扩展方法:统一 start/end,避免负数尺寸*/
private fun Rect.normalize(): Rect {val left = minOf(this.left, this.right)val top = minOf(this.top, this.bottom)val right = maxOf(this.left, this.right)val bottom = maxOf(this.top, this.bottom)return Rect(left, top, right, bottom)
}
//commonMain 下声明接口
@Composable
expect fun ScreenShotPlatform(onSave: (String?) -> Unit)//在jvmMain实现接口内容,其他Android直接空就行了
@Composable
actual fun ScreenShotPlatform(onSave: (String?) -> Unit) {val mainWindow = LocalMainWindow.currentval chatViewModel = koinViewModel(ChatViewModel::class)val isShotState = chatViewModel.isShotState.collectAsState().valueif (isShotState && onlyDesktop()) {ScreenshotOverlay11(mainWindow = mainWindow, onCapture = { pic ->val file = saveToFile11(pic)onSave(file)}, onCancel = {chatViewModel.shotScreen(false)})}
}//调用方式
//在ChatViewModel.kt文件声明截图操作状态,方便全局拉起功能
//是否触发截图private val _isShotObs = MutableStateFlow(false)val isShotState = _isShotObs.asStateFlow()//在composable函数使用val isShotState = chatViewModel.isShotState.collectAsState().value
if (isShotState) {ScreenShotPlatform(onSave = { filePath ->filePath?.let {subScope.launch {conversationViewModel.addCameraImage(PlatformFile(filePath))}}})return}
技术讨论
1.整个思路概述,截图就是新建一个window,在新window进行手势操作,画布绘制,完成截图再把放在单例viewModel的状态变量重置,关闭新window显示主window,不是多window一 开始做手势操作都在应用内,没法在应用外截图。
2.我的截图需求分析是基于java原生api实现的,目前只考虑单显示器,即Windows,Macbook的Retina高分屏,触发截图功能时应用隐藏,主屏幕一层带黑色透明的背景,可多次使用鼠标拖拽绘制选择框,选择框由白色的边框和带白色透明的背景组成,拖拽结束矩形框下方出现操作菜单取消或保存,如果是全屏截图那么菜单按钮则内嵌到选择框的右上角。截图完成可压缩后再保存,但是我看截图文件都不大就注释了,图片路径getPlatformCacheImgDir()注释说明 了。点击保存应用显示,截图功能参数重置,缓存图片并回调图片路径。
3.看预览图windows/mac其实都是共用一套代码,但是菜单按钮显示位置不同时我代码还没同步,修改了下边距,逻辑没问题,然后还有优化点 是某些状态值不是很准确,导致菜单按钮多次取消,后面再修复,大家也可提意见修。
4.实现了快捷键的识别,对Esc可取消截图,鼠标右键也可取消。
5.多屏扩展问题,因为我没有多个显示器,不方便处理这个功能,要注意一个很恶心的问题,mac电脑是高分屏Retina,它的逻辑像素和物理像素有个2倍关系,但是windows是1倍,单屏其实不考虑屏幕偏移量,我在chatgpt示例代码都是把逻辑坐标乘缩放因子,导致我Mac截图一直错位,截黑屏,当时所有AI模型写的都是乘,后来是自己全部截图和测试缩放因子的实际图片发现的问题。
6.macOs系统截图触发会弹权限请求,用户要在系统设置-》隐私与安全性-》屏幕与系统音频录制-》添加应用即可,不然截图就是空白也不报错,调试时发现启用了权限但是线刷程序一样没权限,所以有时我是打包再测,有时又没问题。
7.截图api调用前要等待下线程,不然会把白色的蒙层也截取进去,还有就是注意线程的切换,不然容易造成卡顿 。
总结
此次是实现java虚拟机的截图功能,很多是思路选择问题,这网上查了没人用compose multiplatform实现截图桌面端,可参考下此文,最近实现豆包的划词工具,也是在Compose Multiplatform下实现,但是目前只实现了剪切板获取用户鼠标选中文字,但是没法恢复用户之前的复制文件,后面解决了再出新文章。也实现了微软的自动化Automataion,不太行,也实现了系统钩子但是只能做到win32的notepad,其他应用不行,有大牛有思路可提意见。
第四弹指达
相关文章:
Compose Multiplatform+Kotlin Multiplatfrom 第五弹跨平台 截图
截图功能 Compose MultiplatformKotlin Multiplatfrom下实现桌面端的截图功能,起码搞了两星期,最后终于做出来了,操作都很流畅,截取的文件大小也正常,可参考支持讨论! 功能效果 代码实现 //在jvmMain下创…...
算法题(119):高精度减法
审题: 本题高精度减法主要是要区分正负号,然后进行模拟 思路: 方法一:模拟法 首先本题需要我们利用字符串进行大数相减 第一步:区分s1和s2谁更大 先从数的位数进行判断,然后再从高到低的位数进行判断 第二步…...
使用成员函数指针数组简化C++类中的操作
使用成员函数指针数组简化C类中的操作 在C编程中,我们常常会遇到需要对一组相似的操作进行处理的情况。例如,在一个游戏引擎中,你可能希望角色能够执行一系列的动作,如行走、跳跃或攻击等。为了简化这些操作的管理和调用…...
WebGL数学手记:矩阵基础
一、矩阵的定义 矩阵,数学术语。在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合。 1.英文发音(Matrix) Matrix的发音类似于中文的[美吹克斯],知道它的发音。方便后期看教程时…...
Python爬取数据(二)
一.example2包下的 1.re模块的compile函数使用 import repatternre.compile(r\d) print(pattern) 2.match的方法使用 import re patternre.compile(r\d) # m1pattern.match(one123twothree345four) #参数2:指定起始位置(包含),参数3:终止位置(包含),…...
我的NISP二级之路-01
目录 一.SSE-CMM系统安全工程-能力成熟度模型(Systems Security Engineering - Capability Maturity Model) 二.ISMS 即信息安全管理体系(Information Security Management System),是一种基于风险管理的、系统化的管理体系 三.Kerberos协议 1. 用户登录与 AS 请求 2…...
自制简易 Shell:像搭建积木小屋一样打造命令交互小天地
目录 准备工作:搭建小屋的材料 打造小屋的 “身份牌” 接收指令:小屋的 “对讲机” 拆解指令:把大任务拆成小积木 执行指令:小屋的 “行动队” 特殊指令:小屋的 “特色功能” 小屋的日常运转 完整代码 啥是 …...
WEB安全--内网渗透--利用Net-NTLMv2 Hash
一、前言 在前两篇文章中分析了NTLM协议中Net-NTLMv2 Hash的生成、如何捕获Net-NTLMv2 Hash,现在就来探讨一下在内网环境中,如何利用Net-NTLMv2 Hash进行渗透。 二、Net-NTLM Hash的破解 工具:hashcat 原理:利用其内部的字典对…...
MySQL 数据库操作指南:从数据库创建到数据操作
关键词:MySQL;数据库操作;DDL;DML 一、引言 MySQL 作为广泛应用的关系型数据库管理系统,对于开发人员和数据库管理员而言,熟练掌握其操作至关重要。本文章通过一系列 SQL 示例,详细阐述 MySQL…...
从传递函数到PID控制器
在过程控制中,按偏差的比例(P,Proportional)、积分(I,Integral)和微分(D,Differential)进行控制的PID控制器(亦称PID调节器)是应用最为…...
抓wifi无线空口包之Ubuntu抓包(二)
一、设置网卡信道和频段,并抓包 1、使用iwconfig查看自己机器的无线网卡名称 wangwang-ThinkCentre-M930t-N000:~$ iwconfig lo no wireless extensions. eno1 no wireless extensions. enxc8a3624ab329 no wireless extensions. wlx90de80d1b5b1 IE…...
使用protobuf编译提示无法打开包括文件: ‘absl/log/absl_log.h’: No such file or directory
问题原因 Protobuf 依赖 Abseil: Protobuf 3.20 版本开始依赖 Abseil,但你的系统未正确安装或配置 Abseil。 头文件路径未包含: 编译器找不到 absl/log/absl_log.h,可能是因为 Abseil 未正确安装或未在项目中设置包含路径。 …...
深入浅出Java 锁 | 源码剖析 | 万字解析
目录 硬件内存结构&Java内存模型 硬件内存结构 Java内存模型(JMM) JMM中三大特性:原子性、有序性、可见性 Java中有哪些锁? Java中锁可以分成悲观锁和乐观锁的实现。 乐观锁和悲观锁的区别,乐观锁一定好嘛&…...
java流程控制12:流程控制练习
流程控制练习 打印三角型 package com.zheng.struct;public class TestDemo {public static void main(String[] args) {//打印三角形 5行for(int i1;i<5;i){for(int j5;j>i;j--){System.out.print(" ");}for(int j1;j<i;j){System.out.print("*&quo…...
JAVA:ByteBuddy 动态字节码操作库的技术指南
1、简述 ByteBuddy 是一个功能强大的 Java 字节码操作库,可以帮助开发者在运行时动态生成和修改类,而无需直接接触复杂的 ASM API。它被广泛应用于框架开发、AOP(面向切面编程)、代理类生成、性能监控等领域。 2、ByteBuddy 的优…...
C语言学习记录(13)自定义类型:结构体
一、结构体变量的声明、创建和初始化 1.结构体变量的声明 结构体变量我们学操作符的时候就顺带讲了一点了,因为当时讲了结构体成员变量访问操作符.。 结构体变量不像int、float这种内置类型的,一旦创建,系统就知道这是干啥的,结…...
rtthread 软件SPI驱动, 支持mode0~3,MSB,LSB
rtthread的软件模拟SPI用的上层PIN驱动写,由于经过层层封装,时钟频率并不会太高,200MHz的MCU跑不到1MHz的时钟频率。所以最好是在底层就模拟好,给上层用。 头文件 struct io_poSOFT {gpio_type *port;uint16_t pin; }; typedef …...
C++自学笔记——动态创建对象
动态创建对象 1. 什么是动态创建对象? 在学习之前的知识点时,我们知道有静态存储期和自动存储期。 静态存储期的对象在程序的整个生命周期内都存在,全局变量和static修饰的局部变量都属于这一类。自动存储期的对象,这些对象在函…...
35.[前端开发-JavaScript基础]Day12-for循环中变量-华为商城-商品列表-轮播图
for循环中监听函数中打印变量 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wi…...
详细描述以太坊的gas、gaslimit、gasPrice
目录 一、Gas 是什么? ✅ 简要定义: 🧠 举例理解: 二、Gas Limit 是什么? ✅ 简要定义: 分两种: 举例说明: 三、Gas Price 是什么? ✅ 简要定义: 为什么它重要? 示例: 四、 EIP-1559 后的新机制(伦敦升级) 三个要素: 五、额外技巧(开发实用) 本文…...
【Java】Maven
一、概念 是一个项目管理和构建工具,它基于项目对象模型(POM)的概念,通过一小段描述信息来管理项目的构建。 二、Maven坐标 <groupId>com.itheima</groupId><artifactId>maven-project01</artifactId>&…...
PageCache
目录 一、PageCache的具体过程 二、具体实现代码 一、PageCache的具体过程 页缓存主要解决的是内存外碎片问题,并且直接和系统调用打交道。 申请过程如下: 当中心缓存中没有内存时,会去页缓存申请一个span结构,要经过下面几步: (1…...
Vue3实战五、面包屑,收缩菜单,高亮暗黑主题切换,全屏功能实现
目录 面包屑,收缩菜单,黑夜白夜样式,全屏功能实现收缩菜单按钮结合pinia功能实现第一步、定义布局配置的数据类型第二步、创建布局状态管理文件第三步、使用布局配置状态第四步、进行展开/收起左侧菜单逻辑第五步、动态切换左侧菜单宽度样式第六步、动态…...
Linux内核设计——(二)进程调度
目录 一、进程调度简介 二、多任务 三、调度器 3.1 I/O消耗型和处理器消耗型进程 3.2 进程优先级 3.3 CFS算法 3.4 实时调度策略 3.5 SCHED_FIFO 3.6 SCHED_RR 3.7 调度器入口 四、上下文切换 4.1 睡眠和唤醒 4.2 need_resched标志 4.3 用户抢占 4.4 内核抢占 一…...
【C++初阶】--- string类模拟实现
1.基础函数 1.1成员函数 成员函数主要是_str、_size、_capacity这三个。npos是size_t 的最大值,用于当作后续成员函数的参数的缺省值。 class string { private:char* _str nullptr;//指向字符串的指针size_t _size 0;//字符串长度size_t _capacity 0;//空间大小static c…...
Pythia 使用说明
Pythia 是一个由非营利研究组织 EleutherAI 开发的开源语言模型套件,专注于透明性和可复现性。它是为了推动自然语言处理(NLP)领域的开放研究而设计,尤其在模型训练过程和性能分析方面提供了详尽的文档和数据。 Pythia 的核心特点…...
python:获取某路径下所有图片的名称
可以使用 Python 的 os 模块或者 pathlib 模块来获取指定路径下所有图片的名称。以下是使用这两种方法实现的代码示例: 使用 os 模块 import osdef get_image_names_os(path):image_extensions (.jpg, .jpeg, .png, .gif, .bmp)image_names []for root, dirs, f…...
一个开源的 VS Code 大模型聊天插件:Light-at
这篇文章是一个开发杂谈。对于有经验的开发者来说,可能这个项目并不算特别复杂或者高技术,只是对我个人来说算一个里程碑,因此写篇杂谈文章记录一下。也许也能给起步者一些参考。 项目地址:https://github.com/HiMeditator/light-…...
图论学习笔记2
请先阅读图论学习笔记 1。 在这篇文章里,我们将继续以前 tarjan 求解的强连通分量和双连通分量,讲解其缩点相关内容。 也会讲解一些特殊的图:基环树与仙人掌图、最小树形图。 缩点 我们知道,将强连通分量、双连通分量缩点之后…...
蓝桥杯备赛---真题训练之15届省赛产品360度展示
题目 介绍 在电子商务网站中,用户可以通过鼠标或手势交互实现 360 度全方位查看产品,提升用户体验。现在需要你设计一个 Pipeline 管道函数,用于控制 360 度展示产品的动画序列,通过管道连接各个动画步骤,使产品以流畅…...
图论:单源最短路(BF算法+迪杰斯特拉算法+spfa算法)
单源最短路 概念 dijkstra实现(解决不了负权值) P3371 【模板】单源最短路径(弱化版) - 洛谷 #include<iostream> #include<vector> #include<cstring> using namespace std;typedef pair<int, int> PII…...
嵌入式学习(35)-TTS语音模块FT-VBM-OS支持ModbusRTU
一、概述 FT-TTS-R-01 (下简简“模块”)是一款可将串口传入的文本信息转成语音播报的控制器。块”可下接收任意字 符或者汉字,并通过 TTS 语音合成功能,清晰、准确、自然的合成并播放音频。该块”还带有 1 路继电器输出࿰…...
【Vue-组件】学习笔记
目录 <<回到导览组件1.项目1.1.Vue Cli1.2.项目目录1.3.运行流程1.4.组件的组成1.5.注意事项 2.组件2.1.组件注册2.2.scoped样式冲突2.3.data是一个函数2.4.props详解2.5.data和prop的区别 3.组件通信3.1.父子通信3.1.1.父传子(props)3.1.2.子传父…...
Github上一些使用技巧(缩写、Issue的Highlight)自用
1. GIthub中的一些缩写 LGTM ! 最近经常看到一些迷之缩写,感觉挺有意思的,但是有时候看到一些没见过的缩写还是有点懵逼,不过缩写确实也是很方便去review,这里就记录汇总一下;顺便加了一些git的基操单词(加…...
【团体程序涉及天梯赛】L1~L2实战反思合集(C++)
实战反思汇总记录 仔细审题,想好再写 L1-104 九宫格 - 团体程序设计天梯赛-练习集 易忽略的错误:开始习惯性地看到n就以为是n*n数组了,实际上应该是9*9的固定大小数组,查了半天没查出来 L1-101 别再来这么多猫娘了!…...
ubuntu下的node.js的安装
安装 node-v22.14.0-linux-x64.tar.xz 的步骤如下: 1. 下载和解压 如果尚未下载文件,可以通过 wget 下载(替换为实际下载链接): wget https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.xz解压文件&…...
VMware-workstation-full-12.5.2 install OS X 10.11.1(15B42).cdr
手把手虚拟机安装苹果操作系统 VMware_workstation_full_12.5.2 unlocker208 Apple Max OS X(M)-CSDN博客 vcpu-0:VERIFY vmcore/vmm/main/physMem_monitor.c:1180 FILE: FileCreateDirectoryRetry: Non-retriable error encountered (C:\ProgramData\VMware): Cann…...
Linux下创建svn库 和 svn安装与操作
1.介绍 SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS、CVS,它采用了分支管理系统,它的设计目标就是取代CVS。适合中小公司的开发人员不多的项目使用,相比git管理工具更简单. 2.安装svn 2.1 国际惯例 首先看…...
React-04React组件状态(state),构造器初始化state以及数据读取,添加点击事件并更改state状态值
1.React组件状态(state) 组件可以拥有状态(state),它是组件数据的私有部分,可以用来管理动态数据。状态仅适用于类组件,或者使用 React 的 Hook 时可以在函数组件中使用。 注意 组件中render方…...
第3课:MCP协议接口定义与开发实践
MCP协议接口开发实战:从标准化设计到跨语言SDK落地 一、引言:为什么接口标准化是多智能体协作的“刚需” 在多智能体系统中,不同语言开发的智能体、异构服务之间的通信效率往往受制于接口兼容性问题。MCP(Model Context Protoco…...
Perl语言的WebAssembly
Perl语言的WebAssembly:将古老的语言带入新世纪 引言 在编程语言发展的历史长河中,Perl作为一门早期广泛使用的脚本语言,以其灵活性和丰富的文本处理能力而闻名。然而,随着互联网和Web技术的迅猛发展,许多开发者开始…...
[ISP] ISP 中的 GTM 与 LTM:原理、算法与与 Gamma 校正的对比详解
在现代图像信号处理(ISP)流水线中,图像增强是提升视觉质量的核心手段之一。尤其是在高动态范围(HDR)内容、弱光环境或复杂光照条件下,Tone Mapping(色调映射)技术的引入成为关键。To…...
健身管理小程序|基于java微信开发健身管理小程序的系统设计与实现(源码+数据库+文档)
健身管理小程序目录 基于微信开发健身管理小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 小程序端: 后台 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码…...
批量将文本合并成单个文件,支持按文件夹合并文本文档
我们的文件夹中有零零碎碎的多个小的文本文件,这对我们存档记录是非常不方便,不友好的。如果我们能够将多个小的文本文件合并成一个完整的大的文本文件,那不管是在共享还是在存档起来都更加的方便。今天给大家介绍一下如何批量将多个文本文件…...
ROS云课三分钟-差动移动机器人巡逻报告如何撰写-中等报告
评语: 成绩中等(70/100),具体如下: 1. 摘要部分 问题描述: 内容空洞:摘要过于简短,仅简要概述了研究内容和实现方法,未突出研究的创新点或重要性。缺乏细节࿱…...
forms实现推箱子小游戏
说明: forms实现推箱子小游戏 效果图: step0:游戏规则 # 推箱子游戏规则说明## 🎯 游戏目标 - 通过控制角色移动,将所有**棕色箱子(3)**推到**红色目标点(4)**上 - 当所有箱子都变为**绿色(7)**时,即完成当前关卡 - 完成全部关…...
图的储存+图的遍历
图的存储 邻接矩阵 #include <iostream>#include <cstring>using namespace std;const int N 1010;int n, m;int edges[N][N];int main() {memset(edges, -1, sizeof edges);cin >> n >> m; // 读⼊结点个数以及边的个数 for(int i 1; i < m; i)…...
蓝桥杯—数字接龙(dfs+减枝)
一.题目 二.思路 一看就是迷宫问题的变种,从左上角到达右下角,要解决 1.8个方向的方向向量,用dx,dy数组代表方向向量 2.要按照一个规律的数值串进行搜索0,1,2,k-1,0,1…...
Solidity智能合约漏洞类型与解题思路指南
一、常见漏洞类型与通俗解释 1. 重入攻击(Reentrancy) 🌀 通俗解释:就像你去银行取钱,柜台人员先给你钱,然后再记账。你拿到钱后立即又要求取钱,由于账还没记,柜台又给你一次钱,这样循环下去你就能拿走银行所有的钱。 漏洞原理:合约在更新状态前调用外部合约,允许…...
临床 不等于 医学-《分析模式》漫谈52
DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 “Analysis Patterns”的第4章“企业财务观察”有这么一句话: An important point about this model——a reflection of its clinical background 2004(机械…...