⏱
快速贴图
按 F3 读取剪贴板中的图片,自动生成一个置顶贴图窗口,省去反复切换软件的步骤。
Sticker Tool 是一个面向日常使用的小工具。按下 F3,从剪贴板取图,图片会以置顶无边框窗口显示在桌面上;支持拖拽、滚轮缩放、Delete 删除,逻辑清楚,打开就能用。
这些功能都围绕“贴图”本身展开,不堆花哨东西,打开后马上能用。
按 F3 读取剪贴板中的图片,自动生成一个置顶贴图窗口,省去反复切换软件的步骤。
鼠标按住图片即可拖动位置,滚轮可直接调整大小,适合临时查看、对照和标注。
鼠标悬停后按 Delete / Backspace,或者右键点击即可删除,保持桌面干净利落。
步骤很少,和这个工具本身一样,越简单越好。
先把想贴出来的图片复制到剪贴板,常见方式是截图后复制,或者在浏览器里右键复制图片。
程序会从剪贴板读取图片,并在桌面最前方创建一个无边框贴图窗口。
拖动图片移动位置,滚轮调整大小,窗口始终保持置顶,方便对照查看。
鼠标悬停后按 Delete / Backspace,或者直接右键删除,最多同时显示 5 张。
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()
把容易卡住的点提前写清楚,省得来回问。
先确认程序已经运行在后台,且剪贴板里确实有图片。没有图片时,程序不会生成贴图窗口。
通常是剪贴板里不是图片格式,或者当前系统对剪贴板权限有限。先复制一张真正的图片再试。
这是为了避免桌面窗口过多,影响使用和管理。删掉旧图后还可以继续贴新图。