Sticker Tool

剪贴板图片贴图工具 · 单文件发布

开源 · 轻量 · 桌面贴图

把剪贴板里的图片,直接贴到屏幕上

Sticker Tool 是一个面向日常使用的小工具。按下 F3,从剪贴板取图,图片会以置顶无边框窗口显示在桌面上;支持拖拽、滚轮缩放、Delete 删除,逻辑清楚,打开就能用。

F3一键贴图
5 张最多同时显示
单文件页面代码全内置
贴图效果预览
剪贴板图片
置顶显示
拖拽移动
滚轮缩放
右键或 Delete 删除

功能说明

这些功能都围绕“贴图”本身展开,不堆花哨东西,打开后马上能用。

快速贴图

按 F3 读取剪贴板中的图片,自动生成一个置顶贴图窗口,省去反复切换软件的步骤。

🖱

拖拽与缩放

鼠标按住图片即可拖动位置,滚轮可直接调整大小,适合临时查看、对照和标注。

🧹

随手删除

鼠标悬停后按 Delete / Backspace,或者右键点击即可删除,保持桌面干净利落。

使用方式

步骤很少,和这个工具本身一样,越简单越好。

1

准备一张图片

先把想贴出来的图片复制到剪贴板,常见方式是截图后复制,或者在浏览器里右键复制图片。

2

按下 F3

程序会从剪贴板读取图片,并在桌面最前方创建一个无边框贴图窗口。

3

移动和缩放

拖动图片移动位置,滚轮调整大小,窗口始终保持置顶,方便对照查看。

4

删除不用的图

鼠标悬停后按 Delete / Backspace,或者直接右键删除,最多同时显示 5 张。

源码展示

Python
import tkinter as tk
from PIL import Image, ImageTk, ImageGrab
from pynput import keyboard
import threading
import sys
from datetime import datetime

active_stickers = []          # 存储当前打开的贴图窗口实例
MAX_STICKERS = 5              # 最多同时显示5张图片
root = None                   # 主窗口句柄

def log_info(msg):
    print(f"[{datetime.now().strftime('%H:%M:%S')}] ℹ️ {msg}")

def log_success(msg):
    print(f"[{datetime.now().strftime('%H:%M:%S')}] ✅ {msg}")

def log_warning(msg):
    print(f"[{datetime.now().strftime('%H:%M:%S')}] ⚠️ {msg}")

def log_error(msg):
    print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ {msg}")

# ---------- 贴图窗口类 ----------
class StickerWindow:
    def __init__(self, pil_image):
        self.original_img = pil_image.copy()
        self.current_img = pil_image.copy()
        self.scale = 1.0

        # 创建无边框置顶窗口
        self.window = tk.Toplevel()
        self.window.overrideredirect(True)
        self.window.attributes('-topmost', True)
        self.window.attributes('-alpha', 0.95)       # 轻微半透明,更美观

        # 图片标签
        self.tk_img = None
        self.label = tk.Label(self.window, cursor="fleur")
        self.label.pack()

        # 事件绑定
        self.label.bind("", self.start_move)
        self.label.bind("", self.on_move)

        # 滚轮缩放(跨平台)
        self.window.bind("", self.on_zoom)        # Windows / macOS
        self.window.bind("", self.on_zoom_linux)   # Linux 向上
        self.window.bind("", self.on_zoom_linux)   # Linux 向下

        # 键盘删除(需窗口聚焦)
        self.window.bind("", self.delete)
        self.window.bind("", self.delete)
        self.window.bind("", lambda e: self.window.focus_set())  # 鼠标悬停自动聚焦
        self.label.bind("", self.delete)          # 右键删除

        self.update_display()
        self.center_window()
        self.window.protocol("WM_DELETE_WINDOW", self.delete)

    def update_display(self):
        """更新缩放后的图像尺寸"""
        new_w = max(10, int(self.original_img.width * self.scale))
        new_h = max(10, int(self.original_img.height * self.scale))
        resized = self.original_img.resize((new_w, new_h), Image.Resampling.LANCZOS)
        self.current_img = resized
        self.tk_img = ImageTk.PhotoImage(resized)
        self.label.config(image=self.tk_img)
        self.label.image = self.tk_img
        self.window.geometry(f"{new_w}x{new_h}")

    def center_window(self):
        """窗口居中显示"""
        self.window.update_idletasks()
        w = self.window.winfo_width()
        h = self.window.winfo_height()
        sw = self.window.winfo_screenwidth()
        sh = self.window.winfo_screenheight()
        x = (sw - w) // 2
        y = (sh - h) // 2
        self.window.geometry(f"+{x}+{y}")

    def on_zoom(self, event):
        """Windows / macOS 滚轮事件"""
        if event.delta > 0:
            self.scale *= 1.1
        else:
            self.scale *= 0.9
        self._apply_zoom()

    def on_zoom_linux(self, event):
        """Linux 滚轮事件(Button-4 向上,Button-5 向下)"""
        if event.num == 4:
            self.scale *= 1.1
        else:
            self.scale *= 0.9
        self._apply_zoom()

    def _apply_zoom(self):
        """统一缩放逻辑"""
        self.scale = max(0.1, min(10.0, self.scale))
        self.update_display()

    def start_move(self, event):
        self.drag_start_x = event.x
        self.drag_start_y = event.y

    def on_move(self, event):
        deltax = event.x - self.drag_start_x
        deltay = event.y - self.drag_start_y
        x = self.window.winfo_x() + deltax
        y = self.window.winfo_y() + deltay
        self.window.geometry(f"+{x}+{y}")

    def delete(self, event=None):
        """删除当前贴图窗口"""
        global active_stickers
        if self in active_stickers:
            active_stickers.remove(self)
        self.window.destroy()
        log_success(f"已删除一张图片,当前剩余 {len(active_stickers)} 张")

# ---------- 剪贴板图片读取 ----------
def get_clipboard_image():
    """从系统剪贴板获取图像(PIL Image),失败返回 None"""
    try:
        img = ImageGrab.grabclipboard()
        if img is None:
            return None
        if isinstance(img, Image.Image):
            return img
        # 支持从文件路径列表读取(某些 Linux 环境)
        if isinstance(img, list) and len(img) > 0:
            from PIL import Image as PILImage
            return PILImage.open(img[0])
        return None
    except Exception as e:
        log_error(f"读取剪贴板失败: {e}")
        return None

def create_new_sticker():
    """F3 回调:从剪贴板创建新贴图(最多 MAX_STICKERS 张)"""
    global active_stickers
    if len(active_stickers) >= MAX_STICKERS:
        log_warning(f"最多只能贴 {MAX_STICKERS} 张图片,请先删除一些(鼠标悬停后按 Delete 或 Backspace)")
        return

    img = get_clipboard_image()
    if img is None:
        log_warning("剪贴板中没有图片,请先复制图片(例如:右键复制图片、截图工具复制后按 Ctrl+C)")
        return

    sticker = StickerWindow(img)
    active_stickers.append(sticker)
    log_success(f"已贴出新图片,当前共 {len(active_stickers)} / {MAX_STICKERS} 张")

def start_hotkey_listener(root):
    """在后台线程中监听 F3 热键"""
    def on_press(key):
        try:
            if key == keyboard.Key.f3:
                root.after(0, create_new_sticker)
        except Exception as e:
            log_error(f"热键监听异常: {e}")

    with keyboard.Listener(on_press=on_press) as listener:
        listener.join()

def on_exit():
    """关闭所有贴图窗口并退出程序"""
    for sticker in active_stickers[:]:
        sticker.delete()
    if root:
        root.quit()
        root.destroy()
    log_info("程序已退出,感谢使用!")
    sys.exit(0)

if __name__ == "__main__":
 
    root = tk.Tk()
    root.withdraw()

    import signal
    signal.signal(signal.SIGINT, lambda sig, frame: on_exit())

    # 启动热键监听线程
    listener_thread = threading.Thread(target=start_hotkey_listener, args=(root,), daemon=True)
    listener_thread.start()

    print("\n" + "=" * 50)
    print("     剪贴板图片贴图工具  v1.0")
    print("=" * 50)
    log_info("程序已启动,热键监听中...")
    print("\n操作指南:")
    print("  📌 按 [F3]        → 将剪贴板中的图片贴在屏幕最前")
    print("  🖱️ 鼠标拖拽图片   → 移动位置")
    print("  🔍 鼠标滚轮       → 缩放图片大小(0.1 ~ 10 倍)")
    print("  ❌ 鼠标悬停图片上 → 按 [Delete] 或 [Backspace] 删除该图片")
    print("  ❌ 鼠标右键点击   → 也可删除图片")
    print("  🚪 按 Ctrl+C      → 退出程序\n")
    log_info(f"最多支持 {MAX_STICKERS} 张图片同时显示,当前 0 张")

    try:
        root.mainloop()
    except KeyboardInterrupt:
        on_exit()

下载程序

下载路径已固定为 sticker_tool.exe,页面点击后即可获取桌面程序。

支持 F3 快捷键贴图
支持拖拽、缩放、删除
最多同时显示 5 张图片
开源、单文件、便于传播

常见问题

把容易卡住的点提前写清楚,省得来回问。

为什么按 F3 没反应?

先确认程序已经运行在后台,且剪贴板里确实有图片。没有图片时,程序不会生成贴图窗口。

为什么有时贴不上图?

通常是剪贴板里不是图片格式,或者当前系统对剪贴板权限有限。先复制一张真正的图片再试。

为什么最多只能显示 5 张?

这是为了避免桌面窗口过多,影响使用和管理。删掉旧图后还可以继续贴新图。