马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
效果展示
界面展示:
截图效果:
OCR功能:
一、范围性
①兼容范围性
使用了ctypes调用了Windows的API, 同时访问了Windows中"C:/Windows/Cursors/"中的.cur光标样式文件, 这个工具只实用于Windows环境;
②图像编辑本领有限
该工具只写了一个最简单的画笔工具。(在截图钉在桌面后,按住Ctrl键可以唤出画笔,松开Ctrl键后画笔消散;)
③其他
性能一样平常,如贴图放大过快时会有卡顿;内存占用并不少,默认环境下在40MB;而且有一些快捷键上的Bug至今尚未修复。
二、上风
①轻量化
该模块只使用了PIL、Keyborad和win32clipboard这三个第三方库,和别的使用pygame\pyautogui等模块差异,该工具偏重夸大轻量化,无关或没须要使用的库尽大概倒霉用。整个步伐即便打包也只有10MB多一些的巨细。
②涵盖大部门根本功能
-- 涵盖底子设置,包罗了截图的各种快捷键设定,如自动生存,自动复制到剪切板,当截图数量到达上限时自动删除第一张截图等。
-- 在截图时还可以上下左右键微调选区;实现了截图框表里的亮暗色对比等等。截图时体现放大镜,体现POS和RGB值;
-- 截图后的图像体现:对于截图后的图像会立即体现在画布上。图像画布颠末了优化,其在视图上放大图像的性能刁悍,实现了类图形编辑器的放大效果。右键隐蔽菜单栏后可以很方便的检察截图内容。窗口是保持置顶的,无需担心窗口会被遮挡,方便用于左右对照。
-- 额外的OCR功能,其是基于微信OCR, 因此对于使用者而言必要本身安装微信才可以使用。代码会自动查找用户微信安装路径,并找到此中的OCR部门举行调用。无需启动微信即可使用到微信的OCR功能。固然,如果渴望完全离开微信,有额外的微信OCR离开版本可供下载使用,巨细在60MB;(这里思量过单独把OCR做成插件的情势,即用一个exe文件单独剖析图片输出笔墨的情势,步伐只负责调用这个exe文件。不外实际上由于懒照旧把代码写死了......)
三、最最古老的版本代码
由于展示空间有限,下面展示的代码仅仅是第一代,最最开始时的代码,功能还很大略。而且反面代码颠末了两三轮重构,尤其第三代代码和一代代码已经有了比力大的差异。
1.1版本的代码如下,单文件,仅使用的PIL模块。请自行安装PIL模块,确保在Windows环境上运行。该版本还相对大略。。。
1.2版本代码丢失,此处只剩下第三代代码,全部代码大概历时3周编写,4-5个文件,约1700多行代码。
完备代码已上传github: LightScreenshot/ at main · Just-A-Freshman/LightScreenshot
- import os
- import ctypes
- import subprocess as sp
- import tkinter as tk
- from threading import Thread
- from tkinter import messagebox, filedialog
- from PIL import Image, ImageGrab, ImageTk, UnidentifiedImageError
-
- ScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
- ctypes.windll.shcore.SetProcessDpiAwareness(1)
- TEMP_FILE_PATH = os.path.join(os.getenv("TEMP"), "tempScreenshot")
- os.makedirs(TEMP_FILE_PATH, exist_ok=True)
- 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_fg
- self.__click_fg = click_color
- self.command = command
- self.config(cursor="hand2", fg=fg)
- self.enable()
- if fg == enter_fg:
- raise ValueError("enter_fg must be different from fg")
- def enable(self):
- self.bind("<Enter>", lambda _: self.config(fg=self.__enter_fg))
- self.bind("<Leave>", lambda _: self.config(fg=self.__fg))
- self.bind("<Button-1>", lambda _: self.config(fg=self.__click_fg))
- self.bind("<ButtonRelease-1>", self.__command)
- def disable(self):
- for event in ("<Enter>", "<Leave>", "<Button-1>", "<ButtonRelease-1>"):
- self.unbind(event)
-
- 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:
- pass
- except 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 = 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]:
- return self.parent.coords(self.__rect)
-
- 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 rect_width_height(self) -> tuple[int, int]:
- left, top, right, bottom = self.rect_coords()
- return int(right - left), int(bottom - top)
-
- 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 create_rect(self) -> None:
- self.__rect= self.parent.create_rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, outline='#1AAE1A', width=2)
- self.create_anchors()
- 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
-
- 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
- if self.screenshot.start_x + offset_x > 0 and self.screenshot.end_x + offset_x < SCREEN_WIDTH:
- self.screenshot.start_x += offset_x
- self.screenshot.end_x += offset_x
- if self.screenshot.start_y + offset_y > 0 and self.screenshot.end_y + offset_y < SCREEN_HEIGHT:
- self.screenshot.start_y += offset_y
- 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
- WIDTH_HEIGHT_OFFSET = 40
- POS_TAG_GAP = 10
- RGB_TAG_GAP = 47
- MAGNIFIER_ADJUST = 70
- 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.page_index: int = 0
- self.current_image: Image.Image = None
- self.pixel_reader = None
- self.final_images: list[Image.Image] = 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]]:
- # 按照主框架,水平线,垂直线的顺序返回坐标
- 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_OFFSET
- zoom_size = self.__class__.ZOOM_SCREEN_SIZE
- if x + offset + zoom_size < SCREEN_WIDTH:
- x_offset = x + offset
- else:
- x_offset = x - offset - zoom_size
- if y + offset + zoom_size + self.__class__.MAGNIFIER_ADJUST < SCREEN_HEIGHT:
- y_offset = y + offset
- else:
- y_offset = y - offset - zoom_size - self.__class__.MAGNIFIER_ADJUST
- width_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.copy_btn: FlatButton = self.set_copy_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.capture_win: tk.Toplevel = None
- self.full_screenshot_canvas: tk.Canvas = None
- self.adjust_rect: AdjustableRect = None
- self.adjust_bar: tk.Frame = None
- def set_window(self):
- self.title("截图工具")
- width, height = self.screenshot.TkS(255), self.screenshot.TkS(30)
- self.minsize(width=width, height=height) # 这里可以根据需要调整最小宽高
- self.attributes("-topmost", True)
- self.geometry(f"{width}x{height}")
- def set_menubar(self) -> tk.Frame:
- menubar = tk.Frame(self, bg="#FFFFFF", height=ScreenshotUtils.TkS(30))
- menubar.pack(fill=tk.X)
- menubar.pack_propagate(False) # 阻止内部组件改变框架大小
- return menubar
- def 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, ipadx=0.1)
- return btn_cut
-
- def set_load_image_btn(self) -> FlatButton:
- btn_load = FlatButton(
- self.menu_bar, text="📄", bg="#FFFFFF",
- font=("Segoe UI Emoji", 18, "bold")
- )
- btn_load.pack(side=tk.LEFT, ipadx=0.1)
- return btn_load
-
- def set_copy_btn(self) -> FlatButton:
- btn_copy = FlatButton(
- self.menu_bar, text="⎘", bg="#FFFFFF", font=("Segoe UI Symbol", 26),
- )
- btn_copy.pack(side=tk.LEFT, ipadx=0.1)
- return btn_copy
- def 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, ipadx=0.1)
- return btn_save
- def 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, ipadx=0.1)
- return turn_left_btn
-
- def 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, ipadx=0.1)
- return turn_page_btn
-
- def set_delete_btn(self) -> FlatButton:
- delete_btn = FlatButton(
- self.menu_bar, text="🗑", bg="#FFFFFF", font=("Segoe UI Symbol", 26, "bold"),
- )
- delete_btn.pack(side=tk.LEFT, ipadx=0.1)
- return delete_btn
-
- def set_cancel_btn(self, parent) -> FlatButton:
- cancel_btn = FlatButton(
- parent, self.clear_capture_info, text="×", bg="#FFFFFF",
- enter_fg="#DB1A21",fg="#CC181F", font=("微软雅黑", 20)
- )
- cancel_btn.pack(side=tk.RIGHT, padx=5)
- return cancel_btn
- def set_confirm_btn(self, parent) -> FlatButton:
- confirm_btn = FlatButton(
- parent, self.confirm_capture, fg="#23B34C", text="√",
- enter_fg="#27C956", bg="#FFFFFF", font=("微软雅黑", 20)
- )
- confirm_btn.pack(side=tk.RIGHT, padx=10)
- 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()
- 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", highlightthickness=0)
- 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 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_bg
- class ScreenshotTool(MainUI):
- def __init__(self):
- super().__init__()
- self.add_command()
- def add_command(self):
- self.protocol("WM_DELETE_WINDOW", self.on_close)
- self.show_image_canvas.bind("<Configure>", self.auto_adjust_image_size)
- self.cut_btn.command = self.start_capture
- self.load_image_btn.command = self.load_image
- self.copy_btn.command = self.copy_image
- self.save_btn.command = self.save_image
- self.turn_left_btn.command = self.turn_page
- self.turn_right_btn.command = self.turn_page
- self.delete_btn.command = self.delete_image
-
- def initialize_screenshot_coords(self):
- self.is_drag = False
- self.screenshot.start_x = self.screenshot.start_y = 0
- self.screenshot.end_x = SCREEN_WIDTH
- self.screenshot.end_y = SCREEN_HEIGHT
- def 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.initialize_screenshot_coords()
- self.adjust_rect.create_rect()
- self.set_magnifier_frame(event)
- self.update_magnifier(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.enter_adjust_mode)
- 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.y
- self.conceal_move_widget()
- self.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) -> None:
- for redraw_widget in self.screenshot.screenshot_redraw_widget:
- self.full_screenshot_canvas.delete(redraw_widget)
- self.screenshot.screenshot_redraw_widget.clear()
- def conceal_move_widget(self):
- for widget in self.screenshot.screenshot_move_widget:
- self.full_screenshot_canvas.tag_lower(widget)
- def update_width_height_info(self, event) -> None:
- self.clear_redraw_widget()
- w, h = self.adjust_rect.rect_width_height()
- 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} × {h}")
- self.screenshot.screenshot_redraw_widget = [wh_info_widget]
- def update_magnifier(self, event, ) -> None:
- 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 = self.adjust_rect.rect_width_height()
- self.clear_redraw_widget()
- redraw_widget_coords = self.screenshot.redraw_widget_coords(x, y)
- magnifier_coord, width_height_info, pos_coord, rgb_coord = redraw_widget_coords
- zoom_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} × {h}"
- )
- 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 enter_adjust_mode(self, event) -> None:
- self.screenshot.move_start_x = event.x
- self.screenshot.move_start_y = event.y
- self.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 check_capture_screenshot(self) -> None:
- if self.screenshot.start_x == self.screenshot.end_x or \
- self.screenshot.start_y == self.screenshot.end_y:
- self.initialize_screenshot_coords()
-
- def confirm_capture(self, event) -> None:
- x1, y1, x2, y2 = self.adjust_rect.rect_coords()
- self.clear_capture_info(event)
- image = self.screenshot.current_image.crop((x1, y1, x2, y2))
- result = self.show_image(image)
- self.screenshot.final_images.append(result)
- self.screenshot.page_index = len(self.screenshot.final_images) - 1
- self.attributes('-topmost', 1)
-
- def show_image(self, image: Image.Image, window_resize=True) -> None:
- if window_resize:
- self.geometry(f"{image.width}x{image.height+self.menu_bar.winfo_height()}")
- photo = ImageTk.PhotoImage(image)
- 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(fill=tk.BOTH, expand=True)
- return image
- def current_limiting(self, canvas_w, canvas_h) -> bool:
- if not self.screenshot.final_images:
- return True
- try:
- coords = self.show_image_canvas.bbox("all")
- img_view_w, img_view_h = coords[2], coords[3]
- except TypeError:
- return True
- if (img_view_w == canvas_w and img_view_h < canvas_h) or \
- (img_view_h == canvas_h and img_view_w < canvas_w):
- return True
- return False
- def auto_adjust_image_size(self, _) -> None:
- canvas_w = self.show_image_canvas.winfo_width()
- canvas_h = self.show_image_canvas.winfo_height()
- if self.current_limiting(canvas_w, canvas_h):
- return
- image = self.screenshot.final_images[self.screenshot.page_index]
- width_ratio = canvas_w / image.width
- height_ratio = canvas_h / image.height
- ratio = min(width_ratio, height_ratio)
- new_width = int(image.width * ratio)
- new_height = int(image.height * ratio)
- resized_image = image.resize((new_width, new_height), Image.LANCZOS)
- self.show_image(resized_image, False)
- def load_image(self, _) -> None:
- file_types = (("Image files", "*.jpg *.png *.jpeg"),)
- img_path = filedialog.askopenfilename(filetypes=file_types)
- if not img_path:
- return
- try:
- image = Image.open(img_path)
- except UnidentifiedImageError:
- return messagebox.showerror("错误", "无法识别该图片文件!")
- self.show_image(image)
- self.screenshot.final_images.append(image)
- self.screenshot.page_index = len(self.screenshot.final_images) - 1
- def copy_image(self, _) -> None:
- def __copy_image():
- self.copy_btn.disable()
- self.copy_btn.config(fg="#25C253")
- image = self.screenshot.final_images[self.screenshot.page_index]
- temp_name = f"{int.from_bytes(os.urandom(4), byteorder='big')}.png"
- temp_file = os.path.join(TEMP_FILE_PATH, temp_name)
- image.save(temp_file)
- startupinfo = sp.STARTUPINFO()
- startupinfo.dwFlags |= sp.STARTF_USESHOWWINDOW
- args = ['powershell', f'Get-Item {temp_file} | Set-Clipboard']
- process = sp.Popen(args=args, startupinfo=startupinfo)
- process.wait()
- self.title("截图工具")
- self.copy_btn.enable()
- self.copy_btn.config(fg="#3B3B3B")
- if len(self.screenshot.final_images) == 0:
- return messagebox.showerror("复制失败", "未检测到截取图像")
- self.title("复制成功!")
- Thread(target=__copy_image, daemon=True).start()
- def save_image(self, _) -> None:
- try:
- image = self.screenshot.final_images[self.screenshot.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) -> None:
- if len(self.screenshot.final_images) == 0:
- return messagebox.showinfo("提示", "暂无图片可切换!")
- if event.widget == self.turn_left_btn:
- if self.screenshot.page_index == 0:
- return messagebox.showinfo("提示", "已经是第一张图片!")
- self.screenshot.page_index -= 1
- else:
- if self.screenshot.page_index == len(self.screenshot.final_images) - 1:
- return messagebox.showinfo("提示", "已经是最后一张图片!")
- self.screenshot.page_index += 1
- self.show_image(self.screenshot.final_images[self.screenshot.page_index])
- def delete_image(self, _) -> None:
- if len(self.screenshot.final_images) == 0:
- return messagebox.showinfo("提示", "暂无图片可删除!")
- if not messagebox.askokcancel("提示", "确认删除当前图片?"):
- return
- self.screenshot.final_images.pop(self.screenshot.page_index)
- if self.screenshot.page_index == len(self.screenshot.final_images):
- self.screenshot.page_index -= 1
- if len(self.screenshot.final_images) == 0:
- self.show_image_canvas.delete("all")
- self.geometry(f"{self.screenshot.TkS(255)}x{self.screenshot.TkS(30)}")
- else:
- self.show_image(self.screenshot.final_images[self.screenshot.page_index])
- def on_close(self):
- def delete_tmp_files():
- for file in os.scandir(TEMP_FILE_PATH):
- os.remove(file)
- self.destroy()
- Thread(target=delete_tmp_files).start()
- if __name__ == "__main__":
- app = ScreenshotTool()
- SCREEN_WIDTH = app.winfo_screenwidth()
- SCREEN_HEIGHT = app.winfo_screenheight()
- app.mainloop()
复制代码
四、打包步伐
如无Python环境,可以通过以下链接下载:
网盘链接中为老版本,新版本请自行前去github堆栈,链接:Just-A-Freshman/LightScreenshot: A tool use to make screenshot, contains some common funcitons.
通过百度网盘分享的文件:多版本截图工具.zip
链接:
提取码: h5yx
|