当前位置: 首页 > news >正文

Python截图轻量化工具

这是用Python做到截图工具,不过由于使用了ctypes调用了Windows的API, 同时访问了Windows中"C:/Windows/Cursors/"中的.cur光标样式文件, 这个工具只适用于Windows环境;

如果要提升其跨平台性的话,需要考虑替换ctypes的一些专属于Windows的API以及设置不同的.cur访问方式
 

该模块只使用了PIL这一个第三方库,和别的使用pygame\pyautogui等模块不同,该工具着重强调轻量化,无关或没必要使用的库尽可能不使用。

目前模块尚未完成,只包含了基本的截图时调整大小,保存,翻页查看不同截图这些简单功能;后续会继续完善,但当前模块的可用性已足够,且代码经过重构,具备一定的可读性以及可拓展性。

完整代码如下,单文件,自行安装PIL模块,在Windows环境上运行。

import ctypes
import tkinter as tk
from tkinter import messagebox, filedialog
from PIL import ImageGrab, ImageTkScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
ctypes.windll.shcore.SetProcessDpiAwareness(1)class FlatButton(tk.Label):def __init__(self, parent, command, enter_fg="#000000", click_color="#25C253", *args, **kwargs):super().__init__(parent, *args, **kwargs)self.__fg = fg = kwargs.get("fg", "#474747")self.__enter_fg = enter_fgself.__click_fg = click_colorself.command = commandself.config(cursor="hand2")self.bind("<Enter>", lambda _: self.config(fg=enter_fg))self.bind("<Leave>", lambda _: self.config(fg=fg))self.bind("<Button-1>", lambda _: self.config(fg=click_color))self.bind("<ButtonRelease-1>", self.__command)if fg == enter_fg:raise ValueError("enter_fg must be different from fg")def __command(self, event):try:if self.cget("fg") in (self.__enter_fg, self.__click_fg):self.command(event)self.config(fg=self.__fg)except tk.TclError:# Button have been destroy.passclass AdjustableRect(object):"""The judgement seq is so important that you must care about:(right, bottom), (left, top), (right, top), (left, bottom),(center_x, top), (center_x, bottom), (left, center_y, ), (right, center_y)"""ANCHOR_SIZE = 3ANCHOR_HOVER_DISTANCE = 20CURSOR_FILES_NAME = ["aero_nwse_l.cur", "aero_nesw_l.cur", "aero_ns_l.cur", "aero_ew_l.cur"]CURSOR_FILES = [f"@C:/Windows/Cursors/{cursor_file}" for cursor_file in CURSOR_FILES_NAME]CURSORS = [CURSOR_FILES[0], CURSOR_FILES[0], CURSOR_FILES[1], CURSOR_FILES[1],CURSOR_FILES[2], CURSOR_FILES[2], CURSOR_FILES[3], CURSOR_FILES[3],"fleur", "arrow"]def __init__(self, parent, screenshot):self.parent: tk.Canvas = parentself.screenshot: ScreenshotUtils = screenshotself.__rect: int = 0self.__anchors: list[int] = []self.anchor_id: int = 0def rect_coords(self) -> tuple[int, int, int, int]:try:return self.parent.coords(self.__rect)except tk.TclError:return [min(self.screenshot.start_x, self.screenshot.end_x),min(self.screenshot.start_y, self.screenshot.end_y),max(self.screenshot.start_x, self.screenshot.end_x),max(self.screenshot.start_y, self.screenshot.end_y)]def anchor_coords(self) -> tuple[int, int, int, int]:left, top, right, bottom = self.rect_coords()horizontal_middle = (left + right) // 2vertical_middle = (top + bottom) // 2return ((left, top), (horizontal_middle, top), (right, top), (right, vertical_middle),(right, bottom), (horizontal_middle, bottom), (left, bottom), (left, vertical_middle))def get_anchor(self, event) -> int:cls = self.__class__left, top, right, bottom = self.rect_coords()center_x, center_y = (left + right) // 2, (top + bottom) // 2def near(actual, target):return abs(actual - target) < cls.ANCHOR_HOVER_DISTANCE# 务必注意这个判断顺序,这与后面rect_adjust密切相关judgement_pos = ((right, bottom), (left, top), (right, top), (left, bottom),(center_x, top), (center_x, bottom), (left, center_y, ), (right, center_y))for index, pos in enumerate(judgement_pos):if near(event.x, pos[0]) and near(event.y, pos[1]):return indexif left < event.x < right and top < event.y < bottom:return 8return -1def create_anchors(self):cls = self.__class__for coord in self.anchor_coords():anchor = self.parent.create_rectangle(coord[0]-cls.ANCHOR_SIZE, coord[1]-cls.ANCHOR_SIZE,coord[0]+cls.ANCHOR_SIZE, coord[1]+cls.ANCHOR_SIZE,fill="#1AAE1A", outline="#1AAE1A")self.__anchors.append(anchor)def move_anchors(self):cls = self.__class__for anchor, coord in zip(self.__anchors, self.anchor_coords()):self.parent.coords(anchor, coord[0]-cls.ANCHOR_SIZE, coord[1]-2, coord[0]+cls.ANCHOR_SIZE, coord[1]+cls.ANCHOR_SIZE)def on_press(self, event):self.screenshot.start_x = event.xself.screenshot.start_y = event.yself.__rect= self.parent.create_rectangle(self.screenshot.start_x, self.screenshot.start_y, self.screenshot.start_x, self.screenshot.start_y, outline='#1AAE1A', width=2)self.create_anchors()def on_release(self, _):self.screenshot.start_x, self.screenshot.start_y,\self.screenshot.end_x, self.screenshot.end_y = self.rect_coords()def on_hover(self, event):self.anchor_id = self.get_anchor(event)cursor = self.CURSORS[self.anchor_id]self.parent.config(cursor=cursor)def move_rect(self, event):offset_x = event.x - self.screenshot.move_start_xoffset_y = event.y - self.screenshot.move_start_yself.screenshot.start_x += offset_xself.screenshot.start_y += offset_yself.screenshot.end_x += offset_xself.screenshot.end_y += offset_yself.screenshot.move_start_x = event.xself.screenshot.move_start_y = event.yself.parent.coords(self.__rect, self.screenshot.start_x, self.screenshot.start_y, self.screenshot.end_x, self.screenshot.end_y)self.move_anchors()def rect_adjust(self, event):if self.anchor_id == 8:return self.move_rect(event)if self.anchor_id == 0:self.screenshot.end_x, self.screenshot.end_y = event.x, event.yelif self.anchor_id == 1:self.screenshot.start_x, self.screenshot.start_y = event.x, event.yelif self.anchor_id == 2:self.screenshot.end_x, self.screenshot.start_y = event.x, event.yelif self.anchor_id == 3:self.screenshot.start_x, self.screenshot.end_y = event.x, event.yelif self.anchor_id == 4:self.screenshot.start_y = event.yelif self.anchor_id == 5:self.screenshot.end_y = event.yelif self.anchor_id == 6:self.screenshot.start_x = event.xelif self.anchor_id == 7:self.screenshot.end_x = event.xelse:returnself.parent.coords(self.__rect, self.screenshot.start_x, self.screenshot.start_y, self.screenshot.end_x, self.screenshot.end_y)self.move_anchors()class ScreenshotUtils(object):"""截图的关键是坐标;这个类管理着图片的引用和截图坐标;"""def TkS(value) -> int:return int(ScaleFactor/100*value)ZOOM: int = 4ZOOM_WIDTH: float = TkS(28.75)ZOOM_SCREEN_SIZE: int = int(ZOOM_WIDTH*ZOOM)MAGNIFIER_OFFSET: int = 36AJUST_BAR_WIDTH: int = TkS(100)# 截图相关变量def __init__(self):self.start_x = self.start_y = self.end_x = self.end_y = 0self.move_start_x = self.move_start_y = self.move_end_x = self.move_end_y = 0self.current_image = Noneself.pixel_reader = Noneself.final_images = list()# 这种是只移动但不改变大小和内容的控件,只需移动无需重绘self.screenshot_move_widget = list()# 这种是移动和改变大小的控件,需要实时重绘self.screenshot_redraw_widget = list()@staticmethoddef TkS(value) -> int:return int(ScaleFactor/100*value)@classmethoddef move_widget_coords(cls, x, y) -> list[tuple[int, int, int, int]]:# 按照主框架,水平线,垂直线的顺序返回坐标magnifier_x = x+cls.MAGNIFIER_OFFSETmagnifier_y = y+cls.MAGNIFIER_OFFSETmain_frame_coord  = (magnifier_x, magnifier_y,magnifier_x+cls.ZOOM_SCREEN_SIZE,magnifier_y+cls.ZOOM_SCREEN_SIZE )horrontal_line_coord = (magnifier_x, magnifier_y+cls.ZOOM_SCREEN_SIZE // 2,magnifier_x+cls.ZOOM_SCREEN_SIZE,magnifier_y+cls.ZOOM_SCREEN_SIZE // 2 )vertical_line_coord = (magnifier_x+cls.ZOOM_SCREEN_SIZE // 2, magnifier_y,magnifier_x+cls.ZOOM_SCREEN_SIZE // 2,magnifier_y+cls.ZOOM_SCREEN_SIZE )coords = [main_frame_coord, horrontal_line_coord, vertical_line_coord]return coordsdef redraw_widget_coords(self, x, y) -> list[tuple]:# 按照"长 × 宽"、"放大镜图像"、"POS标签"、"RGB标签"的顺序返回坐标offset = self.__class__.MAGNIFIER_OFFSETwidth_height_info = (min(self.start_x, self.end_x), min(self.start_y, self.end_y) - 40)magnifier_coord = (x + offset, y + offset)pos_info = (x + offset, y + offset+self.__class__.ZOOM_SCREEN_SIZE + 10)rgb_info = (x + offset, y + offset+self.__class__.ZOOM_SCREEN_SIZE + 47)coords = [width_height_info, magnifier_coord, pos_info, rgb_info]return coordsclass MainUI(tk.Tk):def __init__(self):super().__init__()self.screenshot = ScreenshotUtils()self.set_window()self.menu_bar: tk.Frame = self.set_menubar()self.cut_btn: tk.Label = self.set_cut_btn()self.save_btn: tk.Label = self.set_save_btn()self.turn_left_btn: tk.Label = self.set_turn_left_btn()self.turn_right_btn: tk.Label = self.set_turn_right_btn()self.show_image_canvas: tk.Canvas = self.set_show_image_canvas()self.adjust_rect: AdjustableRect = Noneself.pos_label: tk.Label = Noneself.rgb_label: tk.Label = Noneself.adjust_bar: tk.Frame = Nonedef set_window(self):self.title("截图工具")self.geometry(f"{self.screenshot.TkS(240)}x{self.screenshot.TkS(30)}")def set_menubar(self) -> tk.Frame:menubar = tk.Frame(self, bg="#FFFFFF", height=30)menubar.pack(fill=tk.X)return menubardef set_cut_btn(self) -> tk.Label:btn_cut = FlatButton(self.menu_bar, self.start_capture, text="✂", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)btn_cut.pack(side=tk.LEFT, padx=5)return btn_cutdef set_save_btn(self) -> tk.Label:btn_save = FlatButton(self.menu_bar, self.save_image, text="💾", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)btn_save.pack(side=tk.LEFT, padx=5)return btn_savedef set_turn_left_btn(self) -> tk.Label:turn_left_btn = FlatButton(self.menu_bar, self.turn_page, text="\u25C0", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)turn_left_btn.pack(side=tk.LEFT, padx=5)return turn_left_btndef set_turn_right_btn(self) -> tk.Label:turn_page_btn = FlatButton(self.menu_bar, self.turn_page, text="\u25B6", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)turn_page_btn.pack(side=tk.LEFT, padx=5)return turn_page_btndef set_cancel_btn(self, parent) -> tk.Label:cancel_btn = FlatButton(parent, self.clear_capture_info, text="×", bg="#FFFFFF",enter_fg="#DB1A21",fg="#CC181F", font=("微软雅黑", 18))cancel_btn.pack(side=tk.RIGHT, padx=5)return cancel_btndef set_confirm_btn(self, parent) -> tk.Label:confirm_btn = FlatButton(parent, self.confirm_capture, fg="#23B34C", text="√",enter_fg="#27C956", bg="#FFFFFF", font=("微软雅黑", 18))return confirm_btndef set_show_image_canvas(self) -> tk.Canvas:canvas = tk.Canvas(self, bg="white")return canvasdef set_adjust_bar(self) -> tk.Frame:self.adjust_bar = tk.Frame(self.full_screenshot_canvas, bg="#FFFFFF", height=50)cancel_btn = self.set_cancel_btn(self.adjust_bar)confirm_btn = self.set_confirm_btn(self.adjust_bar)cancel_btn.pack(side=tk.RIGHT, padx=5)confirm_btn.pack(side=tk.RIGHT, padx=10)def set_magnifier_frame(self, event) -> None:initial_coord = (0, 0, 0, 0)main_frame_id = self.full_screenshot_canvas.create_rectangle(*initial_coord, outline='#1AAE1A', width=1)horrontal_line = self.full_screenshot_canvas.create_line(*initial_coord, fill="#1AAE1A", width=2)vertical_line = self.full_screenshot_canvas.create_line(*initial_coord, fill="#1AAE1A", width=2)self.screenshot.screenshot_move_widget = [main_frame_id, horrontal_line, vertical_line]event.x = event.x + event.widget.winfo_rootx()event.y = event.y + event.widget.winfo_rooty()self.update_magnifier(event)def set_full_screenshot_canvas(self, parent) -> tk.Canvas:img = ImageGrab.grab()self.screenshot.current_image = imgself.screenshot.pixel_reader = img.convert("RGB")photo = ImageTk.PhotoImage(img)full_screenshot_canvas = tk.Canvas(parent, bg="white")full_screenshot_canvas.create_image(0, 0, anchor=tk.NW, image=photo)full_screenshot_canvas.image = photofull_screenshot_canvas.pack(fill=tk.BOTH, expand=True)return full_screenshot_canvasdef set_pos_rgb_label(self, parent) -> tk.Label:label = tk.Label(parent, bg="#000000", font=("微软雅黑", 8), fg="#ffffff")return labelclass ScreenshotTool(MainUI):def __init__(self):super().__init__()self.page_index = 0def start_capture(self, event):self.attributes('-alpha', 0)self.update()self.capture_win = tk.Toplevel()self.capture_win.geometry(f"{self.winfo_screenwidth()}x{self.winfo_screenheight()}+0+0")self.capture_win.overrideredirect(True)self.full_screenshot_canvas = self.set_full_screenshot_canvas(self.capture_win)self.pos_label = self.set_pos_rgb_label(self.full_screenshot_canvas)self.rgb_label = self.set_pos_rgb_label(self.full_screenshot_canvas)self.adjust_rect = AdjustableRect(self.full_screenshot_canvas, self.screenshot)self.set_magnifier_frame(event)self.set_adjust_bar()self.full_screenshot_canvas.bind("<Button-1>", self.on_press)self.full_screenshot_canvas.bind("<Motion>", self.update_magnifier)self.full_screenshot_canvas.bind("<ButtonRelease-1>", self.on_release)def on_press(self, event):self.adjust_rect.on_press(event)self.full_screenshot_canvas.unbind("<Motion>")self.full_screenshot_canvas.bind("<Motion>", self.on_drag)def on_drag(self, event):self.adjust_rect.rect_adjust(event)self.update_magnifier(event)def on_release(self, event, resize=True):self.unbind_all()self.adjust_rect.on_release(event)self.full_screenshot_canvas.config(cursor=self.adjust_rect.CURSOR_FILES[0])self.full_screenshot_canvas.bind("<Button-1>", self.start_move)self.full_screenshot_canvas.bind("<Motion>", self.adjust_rect.on_hover)self.adjust_bar.place(x=self.screenshot.end_x - 300, y=self.screenshot.end_y + 10, width=300)if resize:self.screenshot.end_x, self.screenshot.end_y = event.x, event.ydef unbind_all(self):events = ("<Button-1>", "<Motion>", "<ButtonRelease-1>")for event in events:self.full_screenshot_canvas.unbind(event)def update_magnifier(self, event):x, y = event.x, event.ysize = ScreenshotUtils.ZOOM_WIDTHimg = self.screenshot.current_image.crop((x - size//2, y - size//2, x + size//2, y + size//2))img = img.resize((ScreenshotUtils.ZOOM_SCREEN_SIZE, ScreenshotUtils.ZOOM_SCREEN_SIZE))photo = ImageTk.PhotoImage(img)self.full_screenshot_canvas.image2 = photow, h = abs(self.screenshot.end_x - self.screenshot.start_x), abs(self.screenshot.end_y - self.screenshot.start_y)for redraw_widget in self.screenshot.screenshot_redraw_widget:self.full_screenshot_canvas.delete(redraw_widget)self.screenshot.screenshot_redraw_widget.clear()redraw_widget_coords = self.screenshot.redraw_widget_coords(x, y)width_height_info, magnifier_coord, pos_info, rgb_info = redraw_widget_coordswh_info_widget = self.full_screenshot_canvas.create_text(*width_height_info, anchor=tk.NW, fill="white", text=f"{w} × {h}")zoom_img = self.full_screenshot_canvas.create_image(*magnifier_coord, anchor=tk.NW, image=photo)self.pos_label.config(text=f"POS: ({x}, {y})")self.rgb_label.config(text=f"RGB: {self.screenshot.pixel_reader.getpixel((x, y))}")self.pos_label.place(x=pos_info[0], y=pos_info[1])self.rgb_label.place(x=rgb_info[0], y=rgb_info[1])self.screenshot.screenshot_redraw_widget = [wh_info_widget, zoom_img]self.update_magnifier_frame(x, y)def update_magnifier_frame(self, x: int, y: int) -> None:coords = self.screenshot.move_widget_coords(x, y)for widget, coord in zip(self.screenshot.screenshot_move_widget, coords):self.full_screenshot_canvas.coords(widget, *coord)self.full_screenshot_canvas.tag_raise(widget)def start_move(self, event):self.screenshot.move_start_x = event.xself.screenshot.move_start_y = event.yself.adjust_bar.place_forget()self.pos_label.place_forget()self.rgb_label.place_forget()for widget in self.screenshot.screenshot_redraw_widget:self.full_screenshot_canvas.delete(widget)for widget in self.screenshot.screenshot_move_widget:self.full_screenshot_canvas.tag_lower(widget)self.full_screenshot_canvas.bind("<B1-Motion>", self.adjust_rect.rect_adjust)self.full_screenshot_canvas.bind("<ButtonRelease-1>", lambda e: self.on_release(e, False))def clear_capture_info(self, _):self.capture_win.destroy()self.full_screenshot_canvas.destroy()self.attributes('-alpha', 1)self.screenshot.screenshot_move_widget.clear()def confirm_capture(self, event):self.clear_capture_info(event)x1, y1, x2, y2 = self.adjust_rect.rect_coords()image = self.screenshot.current_image.crop((x1, y1, x2, y2))result = self.show_image(image)self.screenshot.final_images.append(result)self.page_index = len(self.screenshot.final_images) - 1self.attributes('-topmost', 1)def show_image(self, image):self.geometry(f"{max(image.width, ScreenshotUtils.TkS(240))}x{image.height+self.menu_bar.winfo_height()}")photo = ImageTk.PhotoImage(image)self.show_image_canvas.config(width=image.width, height=image.height)self.show_image_canvas.delete("all")self.show_image_canvas.create_image(0, 0, anchor=tk.NW, image=photo)self.show_image_canvas.image = photoself.show_image_canvas.pack()return imagedef save_image(self, _):try:image = self.screenshot.final_images[self.page_index]filename = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("PNG files", "*.png"), ("JPEG files", "*.jpg")],initialfile=f"{image.width}x{image.height}.png")if not filename:returnimage.save(filename)except IndexError:messagebox.showerror("保存失败", "未检测到截取图像")def turn_page(self, event):if len(self.screenshot.final_images) == 0:return messagebox.showinfo("提示", "暂无图片可切换!")if event.widget == self.turn_left_btn:if self.page_index == 0:return messagebox.showinfo("提示", "已经是第一张图片!")self.page_index -= 1else:if self.page_index == len(self.screenshot.final_images) - 1:return messagebox.showinfo("提示", "已经是最后一张图片!")self.page_index += 1self.show_image(self.screenshot.final_images[self.page_index])if __name__ == "__main__":app = ScreenshotTool()app.mainloop()

相关文章:

Python截图轻量化工具

这是用Python做到截图工具&#xff0c;不过由于使用了ctypes调用了Windows的API, 同时访问了Windows中"C:/Windows/Cursors/"中的.cur光标样式文件, 这个工具只适用于Windows环境&#xff1b; 如果要提升其跨平台性的话&#xff0c;需要考虑替换ctypes的一些专属于W…...

怎麼在Chrome中設置代理伺服器?

出於隱私、安全或者訪問特定網路資源的需求&#xff0c;設置代理伺服器顯得尤為重要。本文將詳細介紹如何在Chrome流覽器中進行代理伺服器的設置。 代理伺服器是介於電腦和互聯網之間的一道“中間牆”。幫助間接訪問互聯網資源&#xff0c;這樣實際的IP地址被代理伺服器的IP地…...

【数据结构中链表常用的方法实现过程】

线性表 线性表包括:顺序表、链表、栈&#xff0c;队列等&#xff0c;本节我们先学习顺序表。 顺序表 利用新的数据类型——顺序表&#xff0c;操作数组 顺序表的本质就是对数组的增删改查。 /*** 打印顺序表中的所有元素*/Overridepublic void display() {for (int i 0; i…...

语言月赛 202308【小粉兔处理题解审核志愿者轮换】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202308] 小粉兔处理题解审核志愿者轮换 题目背景 又到了一周的周日&#xff0c;扶苏又像催命一样开始催促小粉兔处理题解审核志愿者的轮换。但是懒狗小粉兔总是把这项工作拖到第二天凌晨。 【这里有一张内部群聊图&#xff0c;…...

第二十二章:游戏结缘与现实的相遇

“终于结束了……” 小冷的手微微颤抖&#xff0c;笔在纸上划过&#xff0c;签下放弃 985 高校研究生入学资格的名字&#xff0c;那一刻&#xff0c;数月来压在心头沉甸甸的巨石轰然崩塌&#xff0c;扬起的尘埃渐渐消散。曾经&#xff0c;学术巅峰犹如夜空中遥不可及却又熠熠生…...

(ICLR=2025)生成的表征对齐:训练扩散Transformer比你想象的更简单

生成的表征对齐&#xff1a;训练扩散Transformer比你想象的更简单 paper是KAIST发表在ICLR 2025的工作 paper title:REPRESENTATION ALIGNMENT FOR GENERATION: TRAINING DIFFUSION TRANSFORMERS IS EASIER THAN YOU THINK Code&#xff1a;链接 ABSTRACT 最近的研究表明&…...

保姆级教程--DeepSeek部署

以DeepSeek-R1或其他类似模型为例&#xff0c;涵盖环境配置、代码部署和运行测试的全流程&#xff1a; 准备工作 1. 注册 Cloud Studio - 访问 [Cloud Studio 官网](https://cloudstudio.net/)&#xff0c;使用腾讯云账号登录。 - 完成实名认证&#xff08;如需长期使用…...

[css] 黑白主题切换

link动态引入 类名切换 css滤镜 var 类名切换 v-bind css预处理器mixin类名切换 【前端知识分享】CSS主题切换方案...

C语言练习题

文章目录 1. 递归实现将字符串str中的元素逆序2. 对数组arr进行冒泡排序&#xff08;升序&#xff09;3. 对数组从下标low到下标hihg区间内的元素进行快速排序&#xff08;升序&#xff09;4. 在数组中利用二分查找(折半查找)目标关键字5. 求n的阶乘6. 判断year是否为闰年7. 求…...

Lambda 表达式

一、Lambda 表达式简介 Lambda 表达式是一种简洁的函数式编程方式&#xff0c;用于实现只有一个方法的接口&#xff08;例如函数式接口&#xff09;。 基本语法 (parameters) -> expression (parameters) -> { statements; } 参数&#xff1a;可以有零个或多个参数。…...

Vue 鼠标事件合集,关于鼠标右键的处理方法(改写鼠标右键方法、自定义鼠标右键)

鼠标事件使用 mousedown"canvasDown($event)"按下事件合集 click 点击某个对象时触发 mousedown 鼠标按钮被按下时触发 mouseup 鼠标按钮被松开时触发 mouseleave 当鼠标指针移出元素时触发 dblclick 双击时触发 mousemove 鼠标移动时触发&#xff0c…...

go语言中的接口

接口简介 现实生活中的接口 现实生活中手机、相机、U 盘都可以和电脑的 USB 接口建立连接。我们不需要关注 usb 卡槽大小是否一样&#xff0c;因为所有的 USB 接口都是按照统一的标准来设计的。 Golang 中的接口&#xff08;interface&#xff09; Golang 中的接口是一种抽象…...

如何在Android Studio中开发一个简单的Android应用?

Android Studio是开发Android应用的官方集成开发环境&#xff08;IDE&#xff09;&#xff0c;它提供了许多强大的功能&#xff0c;使得开发者能够高效地创建Android应用。如果你是Android开发的初学者&#xff0c;本文将引导你如何在Android Studio中开发一个简单的Android应用…...

启明星辰发布MAF大模型应用防火墙产品,提升DeepSeek类企业用户安全

2月7日&#xff0c;启明星辰面向DeepSeek等企业级大模型业务服务者提供的安全防护产品——天清MAF&#xff08;Model Application Firewall&#xff09;大模型应用防火墙产品正式发布。 一个新赛道将被开启…… DeepSeek的低成本引爆赛道规模 随着DeepSeek成为当前最热的现象级…...

开箱即用的.NET MAUI组件库 V-Control 发布了!

之前写过挺多的MAUI Sample&#xff0c;其中有很多代码可以打包成组件&#xff0c;当组件完善到一定程度&#xff0c;我会把控件封装起来放到控件库中。 今天&#xff0c;在这个仓库建立一年零八个月后&#xff0c;我觉得可以考虑将其作为开源库发布。 有很多网友在观望.NET …...

android skia渲染介绍

Android AOSP 的渲染系统主要使用 Skia 图形库。Skia 是一个开源的 2D 图形库&#xff0c;它被广泛应用于 Android 的图形渲染中&#xff0c;负责绘制 UI 元素、文本、图像以及其他 2D 图形内容。 以下是 Android AOSP 中 Skia 的作用和它在渲染系统中的位置&#xff1a; 1. 什…...

MySQL的底层原理与架构

前言 了解MySQL的架构和原理对于很多的后续很多的操作会有很大的帮助与理解。并且很多知识都与底层架构相关联。 了解MySQL架构 通过上面的架构图可以得知&#xff0c;Server层中主要由 连接器、查询缓存、解析器/分析器、优化器、执行器 几部分组成的&#xff0c;下面将主要…...

GB/T 43698-2024 《网络安全技术 软件供应链安全要求》标准解读

一、43698-2024标准图解 https://mmbiz.qpic.cn/sz_mmbiz_png/rwcfRwCticvgeBPR8TWIPywUP8nGp4IMFwwrxAHMZ9Enfp3wibNxnfichT5zs7rh2FxTZWMxz0je9TZSqQ0lNZ7lQ/640?wx_fmtpng&fromappmsg 标准在线预览&#xff1a; 国家标准|GB/T 43698-2024 相关标准&#xff1a; &a…...

Vue(4)

一.组件的三大组成部分-注意点说明 &#xff08;1&#xff09;scoped样式冲突 默认情况&#xff1a;写在组件中的样式会全局生效 → 因此很容易造成多个组件之间的样式冲突 ①全局样式&#xff1a;默认组件中的样式会作用到全局 ②局部样式&#xff1a;可以给组件加上scoped属…...

实验3 词法分析(二)

实验3 词法分析(二) [实验目的]&#xff1a; 1 . 熟悉给定的词法分析程序&#xff1b; 2 . 改进词法分析程序。 [实验内容]&#xff1a; 1.尝试多方面改进TEST语言的文法&#xff0c;参考教材附录B词法分析程序TESTscan.c&#xff0c;在此词法分析程序的基础上改进程序&#x…...

MYSQL第四次

目录 题目分析 代码实现 一、修改 Student 表中年龄&#xff08;sage&#xff09;字段属性&#xff0c;数据类型由 int 改变为 smallint 二、为 Course 表中 Cno 字段设置索引&#xff0c;并查看索引 三、为 SC 表建立按学号&#xff08;sno&#xff09;和课程号&#xff…...

CentOS 7配置samba服务设置文件共享

CentOS 7配置samba服务设置文件共享 一、生成另一个Linux系统&#xff0c;名为Linux-client&#xff0c;作为测试系统。 [rootliunx-client ~]# hostnamectl set-hostname Liunx-client二、如果没有则安装Samba服务&#xff0c;如果已经安装则省略此步。 yum install samba…...

分布式微服务系统架构第91集:系统性能指标总结

加群联系作者vx&#xff1a;xiaoda0423 仓库地址&#xff1a;https://webvueblog.github.io/JavaPlusDoc/ 系统性能指标总结 系统性能指标包括哪些&#xff1f; 业务指标、资源指标、中间件指标、数据库指标、前端指标、稳定性指标、批量处理指标、可扩展性指标、可靠性指标。 …...

【算法】动态规划专题⑦ —— 多重背包问题 + 二进制分解优化 python

目录 前置知识进入正题优化方法&#xff1a;二进制分解实战演练 前置知识 【算法】动态规划专题⑤ —— 0-1背包问题 滚动数组优化 python 【算法】动态规划专题⑥ —— 完全背包问题 python 进入正题 多重背包问题I https://www.acwing.com/problem/content/4/ 题目描述 有…...

Node.js学习指南

一、模块化规范 nodejs使用的模块化规范 叫做 common.js 规范: 每一个模块都有独立的作用域 代码在各自模块中执行 不会造成全局污染 每一个模块都是一个独立的文件&#xff08;module对象&#xff09; 模块可以被多次加载&#xff08;module.exports 属性&#xff09; 但是仅…...

Mybatis篇

1&#xff0c;什么是Mybatis &#xff08; 1 &#xff09;Mybatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁…...

微信小程序案例1——制作猫眼电影底部标签导航栏

文章目录 一、项目步骤1 新建一个无AppID的movie项目2将准备好的底部标签导航图标拷贝到movie项目下面(将图标文件夹image放到项目文件夹里&#xff09;3 打开App.json配置文件&#xff0c;在pages数组里添加4个页面路径:电影“pages/movie/movie”、影院“pages/cinema/cinema…...

Docker 数据卷(Volume)详细介绍

Docker 数据卷&#xff08;Volume&#xff09;详细介绍 1. 什么是 Docker 数据卷&#xff1f; Docker 数据卷&#xff08;Volume&#xff09;是一种用于 持久化数据 和 容器间数据共享 的机制。由于容器的存储是临时的&#xff0c;容器删除后其中的数据会丢失&#xff0c;因此…...

Redis --- 使用Feed流实现社交平台的新闻流

要实现一个 Feed 流&#xff08;类似于社交媒体中的新闻流&#xff09;&#xff0c;通常涉及以下几个要素&#xff1a; 内容发布&#xff1a;用户发布内容&#xff08;例如文章、状态更新、图片等&#xff09;。内容订阅&#xff1a;用户可以订阅其他用户的内容&#xff0c;获…...

WPF 在后台使TextBox失去焦点的方法

在软件设计开发的时候&#xff0c;偶尔会遇到在后台xaml.cs后台中&#xff0c;要将TextBox控件的焦点取消或者使TextBox控件获取焦点&#xff0c;下面介绍讲述一种简单的“只让特定的 TextBox 失去焦点”方法: 前端xaml代码示例&#xff1a; <StackPanel Orientation"…...

自动化测试、压力测试、持续集成

因为项目的原因&#xff0c;前段时间研究并使用了 SoapUI 测试工具进行自测开发的 api。下面将研究的成果展示给大家&#xff0c;希望对需要的人有所帮助。 SoapUI 是什么&#xff1f; SoapUI 是一个开源测试工具&#xff0c;通过 soap/http 来检查、调用、实现 Web Service 的…...

Mac电脑上好用的压缩软件

在Mac电脑上&#xff0c;有许多优秀的压缩软件可供选择&#xff0c;这些软件不仅支持多种压缩格式&#xff0c;还提供了便捷的操作体验和强大的功能。以下是几款被广泛推荐的压缩软件&#xff1a; BetterZip 功能特点&#xff1a;BetterZip 是一款功能强大的压缩和解压缩工具&a…...

C++ 使用CURL开源库实现Http/Https的get/post请求进行字串和文件传输

CURL开源库介绍 CURL 是一个功能强大的开源库&#xff0c;用于在各种平台上进行网络数据传输。它支持众多的网络协议&#xff0c;像 HTTP、HTTPS、FTP、SMTP 等&#xff0c;能让开发者方便地在程序里实现与远程服务器的通信。 CURL 可以在 Windows、Linux、macOS 等多种操作系…...

[创业之路-276]:从燃油汽车到智能汽车:工业革命下的价值变迁

目录 前言&#xff1a; 从燃油汽车到智能汽车&#xff1a;工业革命下的价值变迁 前言&#xff1a; 燃油汽车&#xff0c;第一次、第二次工业革命&#xff0c;机械化、电气化时代的产物&#xff0c;以机械和电气自动化为核心价值。 智能汽车&#xff0c;第三次、第四次工业革…...

DeepSeek服务器繁忙问题的原因分析与解决方案

一、引言 随着人工智能技术的飞速发展&#xff0c;DeepSeek 等语言模型在众多领域得到了广泛应用。然而&#xff0c;在春节这段时间的使用过程中&#xff0c;用户常常遭遇服务器繁忙的问题&#xff0c;这不仅影响了用户的使用体验&#xff0c;也在一定程度上限制了模型的推广和…...

【python】简单的flask做页面。一组字母组成的所有单词。这里的输入是一组字母,而输出是所有可能得字母组成的单词列表

目录结构如下&#xff1a; . ├── static │ ├── css │ │ └── styles.css │ └── js │ └── scripts.js ├── templates │ ├── base.html │ ├── case_converter.html │ ├── index.html │ └── word_finder.html ├── app.py ├── tree.py…...

【华为OD-E卷 - 114 找最小数 100分(python、java、c++、js、c)】

【华为OD-E卷 - 找最小数 100分&#xff08;python、java、c、js、c&#xff09;】 题目 给一个正整数NUM1&#xff0c;计算出新正整数NUM2&#xff0c;NUM2为NUM1中移除N位数字后的结果&#xff0c;需要使得NUM2的值最小 输入描述 输入的第一行为一个字符串&#xff0c;字…...

Maven 依赖管理全面解析

目录 1. Maven 简介 2. 依赖的基本概念 依赖坐标 依赖范围 3. Maven 仓库 本地仓库 远程仓库 4. 依赖传递 5. 依赖冲突 6. 在 pom.xml 文件中管理依赖 基本依赖配置 依赖范围配置 依赖排除配置 依赖管理配置&#xff08;&#xff09; 1. Maven 简介 Maven 是一个…...

基于C#语言的海康威视智能车牌识别系统

基于C#语言的海康威视智能车牌识别系统&#xff0c;通过布控对来往车辆进行检测抓拍 资源文件列表 AICar.sln , 984 AICar.sln.GhostDoc.xml , 1306 AICar.v12.suo , 72704 AICar/AICar.csproj , 3573 AICar/AICar.csproj.user , 228 AICar/bin/Debug/AICar.exe , 220672 AIC…...

RabbitMQ 从入门到精通:从工作模式到集群部署实战(五)

#作者&#xff1a;闫乾苓 系列前几篇&#xff1a; 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》&#xff1a;link 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;二&#xff09;》&#xff1a; lin…...

清理服务器/docker容器

清理服务器 服务器或docker容器清理空间。 清理conda环境 删除不用的conda虚拟环境&#xff1a; conda env remove --name python38 conda env remove --name python310清理临时目录&#xff1a;/tmp du -sh /tmp # 查看/tmp目录的大小/tmp 目录下的文件通常是可以直接删除…...

【专题】2024-2025人工智能代理深度剖析:GenAI 前沿、LangChain 现状及演进影响与发展趋势报告汇总PDF洞察(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p39630 在科技飞速发展的当下&#xff0c;人工智能代理正经历着深刻的变革&#xff0c;其能力演变已然成为重塑各行业格局的关键力量。从早期简单的规则执行&#xff0c;到如今复杂的自主决策与多智能体协作&#xff0c;人工智能代理…...

广度优先搜索(BFS)算法详解——以走迷宫问题为例

引言&#xff1a;当算法遇见迷宫 想象你置身于一个复杂的迷宫&#xff0c;如何在最短时间内找到出口&#xff1f;这个问题不仅存在于童话故事中&#xff0c;更是计算机科学中经典的路径搜索问题。本文将带你通过走迷宫问题&#xff0c;深入理解广度优先搜索&#xff08;BFS&am…...

【大数据技术】用户行为日志分析(python+hadoop+mapreduce+yarn+hive)

用户行为日志分析(python+hadoop+mapreduce+yarn+hive) 搭建完全分布式高可用大数据集群(VMware+CentOS+FinalShell) 搭建完全分布式高可用大数据集群(Hadoop+MapReduce+Yarn) 本机PyCharm远程连接虚拟机Python 搭建完全分布式高可用大数据集群(MySQL+Hive)...

C语言基础之【数组和字符串】(上)

C语言基础之【数组和字符串】&#xff08;上&#xff09; 概述一维数组一维数组的定义一维数组的初始化一维数组的访问一维数组的遍历数组名一维数组的常用数据强化训练一维数组的最值一维数组的逆置一维数组的排序&#xff08;冒泡排序&#xff09; 二维数组二维数组的定义二维…...

Maven插件—flatten-maven-plugin:工程模块统一版本依赖

文章目录 前言一、认识flatten-maven-plugin插件二、如何使用flatten-maven-plugin插件&#xff1f;未使用flatten-maven-plugin插件之前的情况描述配置flatten-maven-plugin插件步骤1&#xff1a;最外层父模块安装插件&配置版本变量步骤2&#xff1a;各个自模块使用版本使…...

Linux系统 环境变量

环境变量 写在前面概念查看环境变量main函数的参数argc & argvenv bash环境变量 写在前面 对于环境变量&#xff0c;本篇主要介绍基本概念及三四个环境变量 —— PATH、HOME、PWD。其中 PATH 作为 “ 敲门砖 ”&#xff0c;我们会更详细讲解&#xff1b;理解环境变量的全局…...

TAPEX:通过神经SQL执行器学习的表格预训练

摘要 近年来&#xff0c;语言模型预训练的进展通过利用大规模非结构化文本数据取得了巨大成功。然而&#xff0c;由于缺乏大规模高质量的表格数据&#xff0c;在结构化表格数据上应用预训练仍然是一个挑战。本文提出了TAPEX&#xff0c;通过在一个合成语料库上学习神经SQL执行…...

Ruby:从宝石到编程语言的奇妙联系(中英双语)

Ruby&#xff1a;从宝石到编程语言的奇妙联系 在珠宝世界中&#xff0c;红宝石&#xff08;Ruby&#xff09;是一种象征热情、力量和高贵的珍贵宝石&#xff1b;而在编程世界中&#xff0c;Ruby则是一门灵活、优雅且富有创造力的编程语言。那么&#xff0c;这两者究竟有何联系…...

RLHF中的on-policy和off-policy的区别

在LLM&#xff08;大语言模型&#xff09;和RLHF&#xff08;基于人类反馈的强化学习&#xff09;中&#xff0c;on-policy和off-policy的主要区别在于数据的来源和策略更新的方式。以下是两者的详细对比以及各自的典型算法&#xff1a; On-policy 和 Off-policy 的区别 特性…...