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

Python截图轻量化工具

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

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

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

目前模块基本完成,更多功能可能会考虑未来需要时加入。

目前纯截图功能已经高度接近微信的截图,不过不包含截图后的编辑操作;因为具体编辑个人认为直接打开图片编辑器编辑更方便,没必要写一个图片编辑功能;

基础功能演示:

主要功能包括:

①基本的截图后等待,可继续调整上下左右+对角位置(类似微信);

②打开图片文件,可以直接显示在面板上;

③选择保存文件。对截取过的图片进行翻页查看不同截图;

④删除图片。对于截取过的图片进行删除;当然只是删除内存中图片引用;

窗口属性设置了topmost=True, 即无论怎么打开其他窗口,该窗口都始终置于顶部。这是为了方便我们对截图后的图片放置到一侧,方便参照图片进行操作

代码经过重构,具备一定的可读性以及可拓展性。虽然还有很多不足,但已经相对优美了;

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

import ctypes
import tkinter as tk
from tkinter import messagebox, filedialog
from PIL import Image, ImageGrab, ImageTk, UnidentifiedImageErrorScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
ctypes.windll.shcore.SetProcessDpiAwareness(1)class FlatButton(tk.Label):def __init__(self, parent, command=None, enter_fg="#000000", click_color="#25C253", *args, **kwargs):super().__init__(parent, *args, **kwargs)self.__fg = fg = kwargs.get("fg", "#3B3B3B")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:passexcept TypeError:self.config(fg=self.__fg)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 = 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 = 36WIDTH_HEIGHT_OFFSET = 40POS_TAG_GAP = 10RGB_TAG_GAP = 47MAGNIFIER_ADJUST = 70AJUST_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]]:# 按照主框架,水平线,垂直线的顺序返回坐标main_frame_coord  = (x, y, x+cls.ZOOM_SCREEN_SIZE, y+cls.ZOOM_SCREEN_SIZE)horrontal_line_coord = (x, y+cls.ZOOM_SCREEN_SIZE // 2, x+cls.ZOOM_SCREEN_SIZE, y+cls.ZOOM_SCREEN_SIZE // 2)vertical_line_coord = (x+cls.ZOOM_SCREEN_SIZE // 2, y, x+cls.ZOOM_SCREEN_SIZE // 2, y+cls.ZOOM_SCREEN_SIZE)return [main_frame_coord, horrontal_line_coord, vertical_line_coord]def redraw_widget_coords(self, x, y) -> list[tuple]:# 按照"放大镜图像"、"长 × 宽"、"POS标签"、"RGB标签"的顺序返回坐标offset = self.__class__.MAGNIFIER_OFFSETzoom_size = self.__class__.ZOOM_SCREEN_SIZEif x + offset + zoom_size < SCREEN_WIDTH:x_offset = x + offsetelse:x_offset = x - offset - zoom_sizeif y + offset + zoom_size + self.__class__.MAGNIFIER_ADJUST < SCREEN_HEIGHT:y_offset = y + offsetelse:y_offset = y - offset - zoom_size - self.__class__.MAGNIFIER_ADJUSTwidth_height_y = max(min(self.start_y, self.end_y) - self.__class__.WIDTH_HEIGHT_OFFSET, 0)width_height_info = (max(min(self.start_x, self.end_x), 0), width_height_y)magnifier_coord = (x_offset, y_offset)pos_info = (x_offset, y_offset + zoom_size + self.__class__.POS_TAG_GAP)rgb_info = (x_offset, y_offset + zoom_size + self.__class__.RGB_TAG_GAP)return [magnifier_coord, width_height_info, pos_info, rgb_info]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: FlatButton = self.set_cut_btn()self.load_image_btn: FlatButton = self.set_load_image_btn()self.save_btn: FlatButton = self.set_save_btn()self.turn_left_btn: FlatButton = self.set_turn_left_btn()self.turn_right_btn: FlatButton = self.set_turn_right_btn()self.delete_btn: FlatButton = self.set_delete_btn()self.show_image_canvas: tk.Canvas = self.set_show_image_canvas()self.full_screenshot_canvas: tk.Canvas = Noneself.adjust_rect: AdjustableRect = Noneself.adjust_bar: tk.Frame = Nonedef set_window(self):self.title("截图工具")self.attributes("-topmost", True)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) -> FlatButton:btn_cut = FlatButton(self.menu_bar, text="✂", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)btn_cut.pack(side=tk.LEFT, padx=5)return btn_cutdef set_load_image_btn(self) -> FlatButton:btn_load = FlatButton(self.menu_bar, text="📄", bg="#FFFFFF", font=("Segoe UI Emoji", 18))btn_load.pack(side=tk.LEFT, padx=5)return btn_loaddef set_save_btn(self) -> FlatButton:btn_save = FlatButton(self.menu_bar, text="💾", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)btn_save.pack(side=tk.LEFT, padx=5)return btn_savedef set_turn_left_btn(self) -> FlatButton:turn_left_btn = FlatButton(self.menu_bar, 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) -> FlatButton:turn_page_btn = FlatButton(self.menu_bar, text="\u25B6", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)turn_page_btn.pack(side=tk.LEFT, padx=5)return turn_page_btndef set_delete_btn(self) -> FlatButton:delete_btn = FlatButton(self.menu_bar, text="🗑︎", bg="#FFFFFF", font=("Segoe UI Emoji", 18),)delete_btn.pack(side=tk.LEFT, padx=5)return delete_btndef set_cancel_btn(self, parent) -> FlatButton: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) -> FlatButton: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 config_pos_rgb_info(self, parent: tk.Canvas, pos: str, rgb: str, pos_coord: tuple[int, int], rgb_coord: tuple[int, int]) -> tuple[int]:style = {"anchor": tk.NW, "font": ("微软雅黑", 9), "fill": "#FFFFFF"}pos_info = parent.create_text(*pos_coord, text=pos, **style)rgb_info = parent.create_text(*rgb_coord, text=rgb, **style)pos_bg = parent.create_rectangle(*parent.bbox(pos_info), outline="#000000", fill="#000000")rgb_bg = parent.create_rectangle(*parent.bbox(rgb_info), outline="#000000", fill="#000000")parent.tag_raise(pos_info)parent.tag_raise(rgb_info)return pos_info, rgb_info, pos_bg, rgb_bgclass ScreenshotTool(MainUI):def __init__(self):super().__init__()self.add_command()self.page_index = 0def add_command(self):self.cut_btn.command = self.start_captureself.load_image_btn.command = self.load_imageself.save_btn.command = self.save_imageself.turn_left_btn.command = self.turn_pageself.turn_right_btn.command = self.turn_pageself.delete_btn.command = self.delete_imagedef start_capture(self, event):self.attributes('-alpha', 0)self.update()self.capture_win = tk.Toplevel()self.capture_win.geometry(f"{SCREEN_WIDTH}x{SCREEN_HEIGHT}+0+0")self.capture_win.overrideredirect(True)self.full_screenshot_canvas = self.set_full_screenshot_canvas(self.capture_win)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) -> None: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) -> None:self.adjust_rect.rect_adjust(event)self.update_magnifier(event)def on_release(self, event, resize=True) -> None:self.unbind_all()self.adjust_rect.on_release(event)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=min(self.screenshot.end_x - 300, SCREEN_WIDTH - 300), width=300,y=max(min(self.screenshot.end_y + 10, SCREEN_HEIGHT - ScreenshotUtils.TkS(40)), 0),)if resize:self.screenshot.end_x, self.screenshot.end_y = event.x, event.yself.update_width_height_info(event)def rect_adjust(self, event) -> None:self.adjust_rect.rect_adjust(event)if self.adjust_rect.anchor_id != 8 or self.adjust_rect.anchor_id == -1:self.update_magnifier(event)def unbind_all(self):events = ("<Button-1>", "<Motion>", "<ButtonRelease-1>")for event in events:self.full_screenshot_canvas.unbind(event)def clear_redraw_widget(self, redraw_widgets: list[int]):for redraw_widget in redraw_widgets:self.full_screenshot_canvas.delete(redraw_widget)self.screenshot.screenshot_redraw_widget.clear()def update_width_height_info(self, event) -> None:if self.rect_adjust.anchor_id == 8:self.clear_redraw_widget(self.screenshot.screenshot_redraw_widget)else:self.clear_redraw_widget(self.screenshot.screenshot_redraw_widget[1:])w, h = abs(self.screenshot.end_x - self.screenshot.start_x), abs(self.screenshot.end_y - self.screenshot.start_y)coord = self.screenshot.redraw_widget_coords(event.x, event.y)[1]wh_info_widget = self.full_screenshot_canvas.create_text(*coord, anchor=tk.NW, fill="white", text=f"{w:.0f} × {h:.0f}")self.screenshot.screenshot_redraw_widget = [wh_info_widget]def update_magnifier(self, event, ) -> None: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)self.clear_redraw_widget(self.screenshot.screenshot_redraw_widget)redraw_widget_coords = self.screenshot.redraw_widget_coords(x, y)magnifier_coord, width_height_info, pos_coord, rgb_coord = redraw_widget_coordszoom_img = self.full_screenshot_canvas.create_image(*magnifier_coord, anchor=tk.NW, image=photo)wh_info_widget = self.full_screenshot_canvas.create_text(*width_height_info, anchor=tk.NW, fill="white", text=f"{w:.0f} × {h:.0f}")pos_rgb_info = self.config_pos_rgb_info(self.full_screenshot_canvas, f"POS: ({x}, {y})", f"RGB: {self.screenshot.pixel_reader.getpixel((x, y))}", pos_coord, rgb_coord)self.screenshot.screenshot_redraw_widget = [zoom_img, wh_info_widget, *pos_rgb_info]self.update_magnifier_frame(*self.full_screenshot_canvas.coords(zoom_img))def update_magnifier_frame(self, x, y) -> 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) -> None:self.screenshot.move_start_x = event.xself.screenshot.move_start_y = event.yself.adjust_bar.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.rect_adjust)self.full_screenshot_canvas.bind("<ButtonRelease-1>", lambda e: self.on_release(e, False))def clear_capture_info(self, _) -> None:self.capture_win.destroy()self.full_screenshot_canvas.destroy()self.attributes('-alpha', 1)self.screenshot.screenshot_move_widget.clear()def confirm_capture(self, event) -> None: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: Image.Image) -> None: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 load_image(self, _) -> None:file_types = (("Image files", "*.jpg *.png *.jpeg"),)img_path = filedialog.askopenfilename(filetypes=file_types)if not img_path:returntry:image = Image.open(img_path)except UnidentifiedImageError:return messagebox.showerror("错误", "无法识别该图片文件!")self.show_image(image)self.screenshot.final_images.append(image)self.page_index = len(self.screenshot.final_images) - 1def save_image(self, _) -> None: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) -> None: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])def delete_image(self, _) -> None:if len(self.screenshot.final_images) == 0:return messagebox.showinfo("提示", "暂无图片可删除!")if not messagebox.askokcancel("提示", "确认删除当前图片?"):returnself.screenshot.final_images.pop(self.page_index)if len(self.screenshot.final_images) == 0:self.show_image_canvas.delete("all")self.geometry(f"{self.screenshot.TkS(240)}x{self.screenshot.TkS(30)}")else:self.show_image(self.screenshot.final_images[self.page_index])if __name__ == "__main__":app = ScreenshotTool()SCREEN_WIDTH = app.winfo_screenwidth()SCREEN_HEIGHT = app.winfo_screenheight()app.mainloop()


http://www.mrgr.cn/news/89987.html

相关文章:

  • 相似性图相关性重构网络用于无监督跨模态哈希
  • 基于 Nginx 的 CDN 基础实现
  • (done) openMP学习 (Day10: Tasks 原语)
  • jvm 篇
  • Nginx配置 ngx_http_proxy_connect_module 模块及安装
  • 半导体制造工艺讲解
  • 激活函数篇 03 —— ReLU、LeakyReLU、RandomizedLeakkyReLU、PReLU、ELU
  • BiGRU双向门控循环单元多变量多步预测,光伏功率预测(Matlab完整源码和数据)
  • 2025.2.9机器学习笔记:PINN文献阅读
  • 机器学习基本概念(附代码)
  • windows通过网络向Ubuntu发送文件/目录
  • python循环
  • redis专栏解读
  • 外部中断实验 #STM32F407
  • 半导体制造工艺讲解
  • CNN卷积神经网络多变量多步预测,光伏功率预测(Matlab完整源码和数据)
  • Redis基础--常用数据结构的命令及底层编码
  • C++ Attribute 属性说明符
  • 人工智能图像分割之Mask2former源码解读
  • C语言练习题
  • burpsuite抓取html登陆和上传数据包
  • open3d将numpy数组可视化
  • 本地部署DeepSeek + Ragflow
  • python - 封装moondream(备份)
  • redis项目
  • git学习