欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Python編寫(xiě)截圖輕量化工具

 更新時(shí)間:2025年02月09日 10:43:14   作者:Jo喬戈里  
這篇文章主要為大家詳細(xì)介紹了如何使用Python編寫(xiě)一個(gè)截圖輕量化工具,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下

這是用Python做到截圖工具,不過(guò)由于使用了ctypes調(diào)用了Windows的API, 同時(shí)訪問(wèn)了Windows中"C:/Windows/Cursors/"中的.cur光標(biāo)樣式文件, 這個(gè)工具只適用于Windows環(huán)境;

如果要提升其跨平臺(tái)性的話,需要考慮替換ctypes的一些專屬于Windows的API以及設(shè)置不同的.cur訪問(wèn)方式;

該模塊只使用了PIL這一個(gè)第三方庫(kù),和別的使用pygame\pyautogui等模塊不同,該工具著重強(qiáng)調(diào)輕量化,無(wú)關(guān)或沒(méi)必要使用的庫(kù)盡可能不使用。

目前模塊尚未完成,只包含了基本的截圖時(shí)調(diào)整大小,保存,翻頁(yè)查看不同截圖這些簡(jiǎn)單功能;后續(xù)會(huì)繼續(xù)完善,但當(dāng)前模塊的可用性已足夠,且代碼經(jīng)過(guò)重構(gòu),具備一定的可讀性以及可拓展性。

完整代碼如下,單文件,自行安裝PIL模塊,在Windows環(huán)境上運(yùn)行。

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
        # 務(wù)必注意這個(gè)判斷順序,這與后面rect_adjust密切相關(guān)
        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):
    """
    截圖的關(guān)鍵是坐標(biāo);這個(gè)類管理著圖片的引用和截圖坐標(biāo);
    """
    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)
 
    # 截圖相關(guān)變量
    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()
        # 這種是只移動(dòng)但不改變大小和內(nèi)容的控件,只需移動(dòng)無(wú)需重繪
        self.screenshot_move_widget = list()
        # 這種是移動(dòng)和改變大小的控件,需要實(shí)時(shí)重繪
        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]]:
        # 按照主框架,水平線,垂直線的順序返回坐標(biāo)
        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]:
        # 按照"長(zhǎng) × 寬"、"放大鏡圖像"、"POS標(biāo)簽"、"RGB標(biāo)簽"的順序返回坐標(biāo)
        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("保存失敗", "未檢測(cè)到截取圖像")
 
    def turn_page(self, event):
        if len(self.screenshot.final_images) == 0:
            return messagebox.showinfo("提示", "暫無(wú)圖片可切換!")
        if event.widget == self.turn_left_btn:
            if self.page_index == 0:
                return messagebox.showinfo("提示", "已經(jīng)是第一張圖片!")
            self.page_index -= 1
        else:
            if self.page_index == len(self.screenshot.final_images) - 1:
                return messagebox.showinfo("提示", "已經(jīng)是最后一張圖片!")
            self.page_index += 1
        self.show_image(self.screenshot.final_images[self.page_index])
 
if __name__ == "__main__":
    app = ScreenshotTool()
    app.mainloop()

到此這篇關(guān)于使用Python編寫(xiě)截圖輕量化工具的文章就介紹到這了,更多相關(guān)Python截圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論