Python截图轻量化工具

news/2025/2/9 11:23:53 标签: python, 开发语言

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

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

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

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

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

python">import ctypes
import tkinter as tk
from tkinter import messagebox, filedialog
from PIL import ImageGrab, ImageTk


ScaleFactor = 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_fg
        self.__click_fg = click_color
        self.command = command
        self.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.
            pass



class 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 = 3
    ANCHOR_HOVER_DISTANCE = 20
    CURSOR_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 = parent
        self.screenshot: ScreenshotUtils = screenshot
        self.__rect: int = 0
        self.__anchors: list[int] = []
        self.anchor_id: int = 0

    def 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) // 2
        vertical_middle = (top + bottom) // 2
        return (
            (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) // 2
        def 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 index
        if left < event.x < right and top < event.y < bottom:
            return 8
        return -1

    def 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.x
        self.screenshot.start_y = event.y
        self.__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_x
        offset_y = event.y - self.screenshot.move_start_y
        self.screenshot.start_x += offset_x
        self.screenshot.start_y += offset_y
        self.screenshot.end_x += offset_x
        self.screenshot.end_y += offset_y
        self.screenshot.move_start_x = event.x
        self.screenshot.move_start_y = event.y
        self.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.y
        elif self.anchor_id == 1:
            self.screenshot.start_x, self.screenshot.start_y = event.x, event.y
        elif self.anchor_id == 2:
            self.screenshot.end_x, self.screenshot.start_y = event.x, event.y
        elif self.anchor_id == 3:
            self.screenshot.start_x, self.screenshot.end_y = event.x, event.y
        elif self.anchor_id == 4:
            self.screenshot.start_y = event.y
        elif self.anchor_id == 5:
            self.screenshot.end_y = event.y
        elif self.anchor_id == 6:
            self.screenshot.start_x = event.x
        elif self.anchor_id == 7:
            self.screenshot.end_x = event.x
        else:
            return
        self.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 = 4
    ZOOM_WIDTH: float = TkS(28.75)
    ZOOM_SCREEN_SIZE: int = int(ZOOM_WIDTH*ZOOM)
    MAGNIFIER_OFFSET: int = 36
    AJUST_BAR_WIDTH: int = TkS(100)

    # 截图相关变量
    def __init__(self):
        self.start_x = self.start_y = self.end_x = self.end_y = 0
        self.move_start_x = self.move_start_y = self.move_end_x = self.move_end_y = 0
        self.current_image = None
        self.pixel_reader = None
        self.final_images = list()
        # 这种是只移动但不改变大小和内容的控件,只需移动无需重绘
        self.screenshot_move_widget = list()
        # 这种是移动和改变大小的控件,需要实时重绘
        self.screenshot_redraw_widget = list()

    @staticmethod
    def TkS(value) -> int:
        return int(ScaleFactor/100*value)
    
    @classmethod
    def move_widget_coords(cls, x, y) -> list[tuple[int, int, int, int]]:
        # 按照主框架,水平线,垂直线的顺序返回坐标
        magnifier_x = x+cls.MAGNIFIER_OFFSET
        magnifier_y = y+cls.MAGNIFIER_OFFSET
        main_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 coords

    def redraw_widget_coords(self, x, y) -> list[tuple]:
        # 按照"长 × 宽"、"放大镜图像"、"POS标签"、"RGB标签"的顺序返回坐标
        offset = self.__class__.MAGNIFIER_OFFSET
        width_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 coords



class 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 = None
        self.pos_label: tk.Label = None
        self.rgb_label: tk.Label = None
        self.adjust_bar: tk.Frame = None

    def 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 menubar

    def 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_cut
    
    def 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_save
    
    def 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_btn

    def 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_btn

    def 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_btn

    def 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_btn

    def set_show_image_canvas(self) -> tk.Canvas:
        canvas = tk.Canvas(self, bg="white")
        return canvas
    
    def 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 = img
        self.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 = photo
        full_screenshot_canvas.pack(fill=tk.BOTH, expand=True)
        return full_screenshot_canvas
    
    def set_pos_rgb_label(self, parent) -> tk.Label:
        label = tk.Label(parent, bg="#000000", font=("微软雅黑", 8), fg="#ffffff")
        return label


class ScreenshotTool(MainUI):
    def __init__(self):
        super().__init__()
        self.page_index = 0

    def 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.y

    def 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.y
        size = ScreenshotUtils.ZOOM_WIDTH
        img = 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 = photo
        w, 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_coords
        wh_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.x
        self.screenshot.move_start_y = event.y
        self.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) - 1
        self.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 = photo
        self.show_image_canvas.pack()
        return image

    def 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:
                return
            image.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 -= 1
        else:
            if self.page_index == len(self.screenshot.final_images) - 1:
                return messagebox.showinfo("提示", "已经是最后一张图片!")
            self.page_index += 1
        self.show_image(self.screenshot.final_images[self.page_index])

if __name__ == "__main__":
    app = ScreenshotTool()
    app.mainloop()


http://www.niftyadmin.cn/n/5845987.html

相关文章

Unity-Mirror网络框架-从入门到精通之Discovery示例

文章目录 前言Discovery示例NetworkDiscoveryNetworkDiscoveryHUDServerRequestServerResponse最后前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题。Mirror是一个用于Un…

安装指定版本的pnpm

要安装指定版本的 pnpm&#xff0c;可以使用以下方法&#xff1a; 方法 1: 使用 pnpm 安装指定版本 你可以通过 pnpm 的 add 命令来安装指定版本&#xff1a; pnpm add -g pnpm<版本号>例如&#xff0c;安装 pnpm 的 7.0.0 版本&#xff1a; pnpm add -g pnpm7.0.0方法…

Vue(2)

一.指令修饰符 通过 “.” 指明一些指令的后缀&#xff0c;不同后缀封装了不同的处理操作 → 简化代码 ①按键修饰符 keyup.enter → 键盘回车监听 <body><div id"app"><h3>keyup.enter → 监听键盘回车事件</h3><input keyup.enter&…

CLion2024.3.2版中引入vector头文件报错

报错如下&#xff1a; 在MacBook端的CLion中引入#include <vector>报 vector file not found&#xff08;引入map、set等也看参考此方案&#xff09;&#xff0c;首先可以在Settings -> Build,Execution,Deployment -> Toolchains中修改C compiler和C compiler的路…

Flink 调用海豚调度器 SQL 脚本实现1份SQL流批一体化的方案和可运行的代码实例

目录 一、流批一体化概述 二、Flink 与海豚调度器结合实现流批一体化的好处 2.1 代码复用性增强 2.2 开发和维护成本降低 2.3 数据一致性保证 2.4 提高系统的灵活性和可扩展性 三、实现思路步骤 3.1 环境准备 3.2 编写 SQL 脚本并上传到海豚调度器 3.3 实现资源下载功…

MySQL InnoDB引擎 高度为3的B+树,可以存储的数据量

一、普通B树 1、B 树结构概述 B 树是一种平衡的多路搜索树&#xff0c;常用于数据库和文件系统中。在 B 树中&#xff0c;所有的数据记录都存储在叶子节点&#xff0c;非叶子节点只存储索引信息。B 树的高度从根节点开始计算&#xff0c;根节点高度为 1。 2、计算所需参数 …

元宇宙中的隐私与数据保护:Facebook 的挑战与机遇

随着数字技术的飞速发展&#xff0c;元宇宙&#xff08;Metaverse&#xff09;正逐渐成为未来互联网的新舞台。Meta&#xff0c;作为这一领域的先行者&#xff0c;正面临着隐私与数据保护的双重挑战。本文将探讨 Meta 在元宇宙中的隐私与数据保护问题&#xff0c;并分析其可能的…

HTML之CSS三大选择器

HTML之CSS三大选择器 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><st…