用python制作相册浏览小工具
近日,欢度国庆、中秋节双节,朋友们应拍了一些相片,故用python制作相册浏览小工具试用。
依赖库:tkinter(通常 Python 自带)和Pillow(图像处理库),后者,需安装。
安装命令:pip install pillow
程序支持两种视图模式,可通过顶部控制栏的【网格视图/单图视图】按钮切换:
单图视图:默认模式,一次显示一张图片,支持图片浏览、播放及图片旋转、翻转操作。
网格视图:以缩略图网格形式显示所有图片,便于快速查找;点击任意缩略图可切换到单图视图并显示该图片。
远行效果:


源码如下:
python
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image, ImageTk, ImageFilter
import os
import random
import math
from typing import List, Tuple, Optional, Dict
class AestheticPhotoAlbum:
"""相册浏览,支持单图查看和多图网格浏览模式"""
def __init__(self, root: tk.Tk):
# 初始化主窗口
self.root = root
self.root.title("相册浏览")
self.root.geometry("1300x800")
self.root.configure(bg="#f0f0f0")
# 图片相关变量
self.image_files: List[str] = []
self.current_index: int = 0
self.original_image: Optional[Image.Image] = None
self.processed_image: Optional[Image.Image] = None
self.displayed_image: Optional[Image.Image] = None
self.photo_directory: str = ""
self.transitioning: bool = False
self.rotation_angle: int = 0
self.cropping: bool = False
self.crop_start: Optional[Tuple[int, int]] = None
self.crop_rect: Optional[int] = None
self.thumbnail_cache: Dict[str, ImageTk.PhotoImage] = {}
self.view_mode: str = "single" # 视图模式:single(单图) 或 grid(网格)
self.slideshow_running: bool = False
# 布局常量
self.grid_spacing: int = 10 # 调整间距
self.thumb_size: Tuple[int, int] = (240,100) # 缩略图尺寸
self.control_padx: int = 10
self.control_pady: int = 5
# 创建UI组件
self._create_widgets()
# 绑定事件
self._bind_events()
def _font_config(self) -> None:
"""配置字体以支持中文显示"""
default_font = ('SimHei', 10)
self.root.option_add("*Font", default_font)
def _bind_events(self) -> None:
"""绑定各种事件处理"""
# 键盘事件
self.root.bind('<Left>', lambda e: self.prev_image() if self.view_mode == "single" else None)
self.root.bind('<Right>', lambda e: self.next_image() if self.view_mode == "single" else None)
self.root.bind('<Escape>', lambda e: self.root.quit())
self.root.bind('<r>', lambda e: self.rotate_clockwise() if self.view_mode == "single" else None)
self.root.bind('<R>', lambda e: self.rotate_counterclockwise() if self.view_mode == "single" else None)
# 窗口大小变化事件
self.root.bind('<Configure>', self._on_window_resize)
def _create_widgets(self) -> None:
"""创建所有UI组件"""
self._font_config()
self._create_top_controls()
self._create_display_area()
self._create_bottom_controls()
def _create_top_controls(self) -> None:
"""创建顶部控制栏"""
top_frame = tk.Frame(self.root, bg="#ffffff", height=60)
top_frame.pack(fill=tk.X, padx=10, pady=10)
top_frame.pack_propagate(False) # 固定高度
# 选择文件夹按钮
self.select_btn = tk.Button(
top_frame, text="图片文件夹",
command=self.select_directory,
bg="#4a90e2", fg="white",
padx=self.control_padx, pady=self.control_pady,
relief=tk.FLAT, cursor="hand2"
)
self.select_btn.pack(side=tk.LEFT, padx=self.control_padx)
# 视图切换按钮
view_frame = tk.Frame(top_frame, bg="#ffffff")
view_frame.pack(side=tk.LEFT, padx=self.control_padx)
self.view_mode_btn = tk.Button(
view_frame, text="换网格视图",
command=self.toggle_view_mode,
bg="#673ab7", fg="white",
padx=self.control_padx, pady=2,
relief=tk.FLAT, cursor="hand2",
state=tk.DISABLED
)
self.view_mode_btn.pack(side=tk.LEFT)
## # 工具框架
## self.edit_frame = tk.Frame(top_frame, bg="#ffffff")
## self.edit_frame.pack(side=tk.LEFT, padx=self.control_padx)
## self._create_edit_controls()
# 特效按钮
effects_frame = tk.Frame(top_frame, bg="#ffffff")
effects_frame.pack(side=tk.LEFT, padx=self.control_padx)
self.effect_var = tk.StringVar(value="原图")
effects = ["原图", "柔化", "黑白", "复古", "锐化"]
effect_menu = tk.OptionMenu(effects_frame, self.effect_var, *effects, command=self.apply_effect)
effect_menu.config(bg="#f8f9fa", relief=tk.FLAT)
effect_menu.pack()
# 工具框架
self.edit_frame = tk.Frame(top_frame, bg="#ffffff")
self.edit_frame.pack(side=tk.LEFT, padx=self.control_padx)
self._create_edit_controls()
# 重置按钮
self.reset_btn = tk.Button(
top_frame, text="重置图片",
command=self.reset_image,
bg="#f0ad4e", fg="white",
padx=self.control_padx, pady=self.control_pady,
relief=tk.FLAT, cursor="hand2",
state=tk.DISABLED
)
self.reset_btn.pack(side=tk.LEFT, padx=self.control_padx)
# 使用帮助
self.help_btn = tk.Button(
top_frame, text="帮助",
command=self.show_help,
bg="#7f8c8d", fg="white",
padx=self.control_padx, pady=self.control_pady,
relief=tk.FLAT, cursor="hand2"
)
self.help_btn.pack(side=tk.RIGHT, padx=self.control_padx)
# 状态标签
self.status_label = tk.Label(
top_frame, text="请选择图片文件夹",
bg="#ffffff", fg="#666666"
)
self.status_label.pack(side=tk.RIGHT, padx=20)
def _create_edit_controls(self) -> None:
"""创建控制按钮(旋转、翻转)"""
# 旋转按钮
rotate_frame = tk.Frame(self.edit_frame, bg="#ffffff")
rotate_frame.pack(side=tk.LEFT, padx=5)
tk.Label(rotate_frame, text="旋转:", bg="#ffffff").pack(side=tk.TOP)
self.rotate_cw_btn = tk.Button(
rotate_frame, text="顺时针",
command=self.rotate_clockwise,
bg="#f8f9fa", fg="#333333",
padx=5, pady=2, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.rotate_cw_btn.pack(side=tk.LEFT)
self.rotate_ccw_btn = tk.Button(
rotate_frame, text="逆时针",
command=self.rotate_counterclockwise,
bg="#f8f9fa", fg="#333333",
padx=5, pady=2, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.rotate_ccw_btn.pack(side=tk.LEFT)
# 翻转按钮
flip_frame = tk.Frame(self.edit_frame, bg="#ffffff")
flip_frame.pack(side=tk.LEFT, padx=5)
tk.Label(flip_frame, text="翻转:", bg="#ffffff").pack(side=tk.TOP)
self.flip_h_btn = tk.Button(
flip_frame, text="水平",
command=self.flip_horizontal,
bg="#f8f9fa", fg="#333333",
padx=5, pady=2, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.flip_h_btn.pack(side=tk.LEFT)
self.flip_v_btn = tk.Button(
flip_frame, text="垂直",
command=self.flip_vertical,
bg="#f8f9fa", fg="#333333",
padx=5, pady=2, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.flip_v_btn.pack(side=tk.LEFT)
def _create_display_area(self) -> None:
"""创建图片显示区域(支持滚动)"""
# 创建带滚动条的容器
self.scroll_frame = tk.Frame(self.root)
self.scroll_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
# 垂直滚动条
self.vscrollbar = ttk.Scrollbar(self.scroll_frame)
self.vscrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 水平滚动条
self.hscrollbar = ttk.Scrollbar(self.scroll_frame, orient=tk.HORIZONTAL)
self.hscrollbar.pack(side=tk.BOTTOM, fill=tk.X)
# 单图模式显示区域
self.display_frame = tk.Frame(
self.scroll_frame, bg="#e9ecef",
relief=tk.FLAT, bd=1
)
self.display_frame.pack(fill=tk.BOTH, expand=True)
# 网格视图容器
self.grid_frame = tk.Frame(self.scroll_frame, bg="#e9ecef")
# 单图模式画布
self.canvas = tk.Canvas(
self.display_frame, bg="#e9ecef",
highlightthickness=0, cursor="cross"
)
self.canvas.pack(fill=tk.BOTH, expand=True)
# 配置滚动条
self.canvas.configure(yscrollcommand=self.vscrollbar.set, xscrollcommand=self.hscrollbar.set)
self.vscrollbar.configure(command=self.canvas.yview)
self.hscrollbar.configure(command=self.canvas.xview)
def _create_bottom_controls(self) -> None:
"""创建底部控制栏"""
bottom_frame = tk.Frame(self.root, bg="#ffffff", height=60)
bottom_frame.pack(fill=tk.X, padx=10, pady=10)
bottom_frame.pack_propagate(False) # 固定高度
# 导航按钮
self.prev_btn = tk.Button(
bottom_frame, text="上一张",
command=self.prev_image,
bg="#f8f9fa", fg="#333333",
padx=15, pady=5, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.prev_btn.pack(side=tk.LEFT, padx=20)
# 图片计数器
self.counter_label = tk.Label(
bottom_frame, text="0/0",
bg="#ffffff", fg="#333333",
font=('SimHei', 12)
)
self.counter_label.pack(side=tk.LEFT, padx=20)
# 下一张按钮
self.next_btn = tk.Button(
bottom_frame, text="下一张",
command=self.next_image,
bg="#f8f9fa", fg="#333333",
padx=15, pady=5, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.next_btn.pack(side=tk.LEFT, padx=20)
# 幻灯片播放按钮
self.slideshow_btn = tk.Button(
bottom_frame, text="开始幻灯片",
command=self.toggle_slideshow,
bg="#5cb85c", fg="white",
padx=15, pady=5, relief=tk.FLAT,
cursor="hand2", state=tk.DISABLED
)
self.slideshow_btn.pack(side=tk.RIGHT, padx=20)
def toggle_view_mode(self) -> None:
"""切换视图模式(单图/网格)"""
if self.view_mode == "single":
self.view_mode = "grid"
self.view_mode_btn.config(text="换单图视图")
self.display_frame.pack_forget()
self.grid_frame.pack(fill=tk.BOTH, expand=True)
# 强制更新布局,使 winfo_width 能获取正确宽度
self.grid_frame.update_idletasks()
self._update_grid_view()
# 隐藏工具
self.edit_frame.pack_forget()
self.reset_btn.pack_forget()
# 更改鼠标样式
self.canvas.config(cursor="arrow")
else:
self.view_mode = "single"
self.view_mode_btn.config(text="换网格视图")
self.grid_frame.pack_forget()
self.display_frame.pack(fill=tk.BOTH, expand=True)
self._load_and_display_image()
# 显示工具
self.edit_frame.pack(side=tk.LEFT, padx=self.control_padx)
self.reset_btn.pack(side=tk.LEFT, padx=self.control_padx)
# 更新按钮状态
self._update_button_states()
def _update_grid_view(self) -> None:
"""更新网格视图,显示所有图片缩略图"""
# 清空现有内容
for widget in self.grid_frame.winfo_children():
widget.destroy()
if not self.image_files:
return
# 优先获取网格容器的实际宽度(若已渲染)
frame_width = self.grid_frame.winfo_width()
# 若宽度为0(容器未渲染完成),则使用窗口宽度作为默认
if frame_width == 0:
frame_width = self.root.winfo_width() - 100
#cols = max(1, frame_width // (self.thumb_size[0] + self.grid_spacing))
available_width = frame_width - 20 #将上句改为两句, 减去容器左右边缘的额外间距
cols = max(1, available_width // (self.thumb_size[0] + self.grid_spacing))
rows = math.ceil(len(self.image_files) / cols)
# 创建缩略图并添加到网格
for i, filename in enumerate(self.image_files):
row = i // cols
col = i % cols
# 创建缩略图容器
thumb_frame = tk.Frame(
self.grid_frame,
width=self.thumb_size[0],
height=self.thumb_size[1] + 30, # 额外空间显示文件名
bg="#ffffff",
relief=tk.RAISED,
bd=1
)
thumb_frame.grid(
row=row, column=col,
padx=self.grid_spacing//2,
pady=self.grid_spacing//2,
sticky="nsew"
)
thumb_frame.grid_propagate(False)
# 加载或获取缓存的缩略图
try:
if filename not in self.thumbnail_cache:
file_path = os.path.join(self.photo_directory, filename)
with Image.open(file_path) as img:
img.thumbnail(self.thumb_size, Image.Resampling.LANCZOS)
self.thumbnail_cache[filename] = ImageTk.PhotoImage(img)
# 显示缩略图
thumb_label = tk.Label(thumb_frame, image=self.thumbnail_cache[filename], bg="#ffffff")
thumb_label.pack(pady=5)
# 显示文件名(简短版本)
short_name = filename[:15] + "..." if len(filename) > 18 else filename
name_label = tk.Label(
thumb_frame,
text=short_name,
bg="#ffffff",
wraplength=self.thumb_size[0] - 10,
justify=tk.CENTER
)
name_label.pack(pady=2)
# 添加点击事件
def on_thumb_click(index: int) -> callable:
def handler(event=None) -> None:
self.current_index = index
self.toggle_view_mode()
self._update_counter()
return handler
thumb_frame.bind("<Button-1>", on_thumb_click(i))
thumb_label.bind("<Button-1>", on_thumb_click(i))
name_label.bind("<Button-1>", on_thumb_click(i))
# 设置鼠标样式为手型
thumb_frame.config(cursor="hand2")
thumb_label.config(cursor="hand2")
name_label.config(cursor="hand2")
except Exception as e:
# 出错时显示错误信息而非崩溃
error_label = tk.Label(
thumb_frame,
text=f"无法加载\n{filename[:10]}...",
bg="#ffebee",
fg="#b71c1c",
wraplength=self.thumb_size[0] - 10
)
error_label.pack(expand=True)
print(f"加载缩略图错误: {filename}, {str(e)}")
# 配置网格权重
for i in range(cols):
self.grid_frame.grid_columnconfigure(i, weight=1)
for i in range(rows):
self.grid_frame.grid_rowconfigure(i, weight=1)
def _on_window_resize(self, event: tk.Event) -> None:
"""窗口大小变化时的处理函数"""
# 避免初始化时的无效调用和非主窗口事件
if event.widget != self.root or self.transitioning:
return
# 根据当前视图模式刷新
if self.view_mode == "single" and self.processed_image:
self.apply_effect(self.effect_var.get())
elif self.view_mode == "grid" and self.image_files:
# 延迟更新网格,避免频繁触发
self.root.after(100, self._update_grid_view)
def select_directory(self) -> None:
"""选择图片文件夹"""
directory = filedialog.askdirectory(title="图片文件夹")
if directory:
self.photo_directory = directory
self._load_images()
self.status_label.config(text=f"当前文件夹: {os.path.basename(directory)}")
self.thumbnail_cache.clear() # 清除缓存的缩略图
def _load_images(self) -> None:
"""加载文件夹中的图片"""
supported_formats = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.jfif', '.webp')
self.image_files = [
f for f in os.listdir(self.photo_directory)
if f.lower().endswith(supported_formats)
]
if not self.image_files:
messagebox.showinfo("提示", "所选文件夹中没有图片文件")
return
# 启用所有按钮
self._enable_all_controls()
# 显示第一张图片
self.current_index = 0
self._update_counter()
self._load_and_display_image()
def _enable_all_controls(self) -> None:
"""启用所有控制按钮"""
self.prev_btn.config(state=tk.NORMAL)
self.next_btn.config(state=tk.NORMAL)
self.slideshow_btn.config(state=tk.NORMAL)
self.rotate_cw_btn.config(state=tk.NORMAL)
self.rotate_ccw_btn.config(state=tk.NORMAL)
self.flip_h_btn.config(state=tk.NORMAL)
self.flip_v_btn.config(state=tk.NORMAL)
self.reset_btn.config(state=tk.NORMAL)
self.view_mode_btn.config(state=tk.NORMAL)
def _update_button_states(self) -> None:
"""根据当前视图模式更新按钮状态"""
if self.view_mode == "grid":
# 网格模式下禁用单图操作按钮
self.prev_btn.config(state=tk.DISABLED)
self.next_btn.config(state=tk.DISABLED)
self.slideshow_btn.config(state=tk.DISABLED)
self.rotate_cw_btn.config(state=tk.DISABLED)
self.rotate_ccw_btn.config(state=tk.DISABLED)
self.flip_h_btn.config(state=tk.DISABLED)
self.flip_v_btn.config(state=tk.DISABLED)
self.reset_btn.config(state=tk.DISABLED)
else:
# 单图模式下启用相关按钮
self.prev_btn.config(state=tk.NORMAL)
self.next_btn.config(state=tk.NORMAL)
self.slideshow_btn.config(state=tk.NORMAL)
self.rotate_cw_btn.config(state=tk.NORMAL)
self.rotate_ccw_btn.config(state=tk.NORMAL)
self.flip_h_btn.config(state=tk.NORMAL)
self.flip_v_btn.config(state=tk.NORMAL)
self.reset_btn.config(state=tk.NORMAL)
def _load_and_display_image(self) -> None:
"""加载并显示当前图片"""
if not self.image_files:
return
# 获取当前图片路径
current_file = self.image_files[self.current_index]
file_path = os.path.join(self.photo_directory, current_file)
try:
# 打开图片,重置所有处理状态
self.original_image = Image.open(file_path)
self.processed_image = self.original_image.copy()
self.rotation_angle = 0
self.cropping = False
self.crop_rect = None
# 应用默认效果
self.apply_effect(self.effect_var.get())
except Exception as e:
messagebox.showerror("错误", f"无法打开图片: {str(e)}")
# 移除无法打开的图片
self.image_files.pop(self.current_index)
if not self.image_files:
self.status_label.config(text="没有可用的图片文件")
self._disable_all_controls()
return
# 调整索引
self.current_index = min(self.current_index, len(self.image_files) - 1)
self._update_counter()
self._load_and_display_image()
def _disable_all_controls(self) -> None:
"""禁用所有控制按钮"""
self.prev_btn.config(state=tk.DISABLED)
self.next_btn.config(state=tk.DISABLED)
self.slideshow_btn.config(state=tk.DISABLED)
self.rotate_cw_btn.config(state=tk.DISABLED)
self.rotate_ccw_btn.config(state=tk.DISABLED)
self.flip_h_btn.config(state=tk.DISABLED)
self.flip_v_btn.config(state=tk.DISABLED)
self.reset_btn.config(state=tk.DISABLED)
self.view_mode_btn.config(state=tk.DISABLED)
def apply_effect(self, effect: str) -> None:
"""应用图片效果"""
if not self.processed_image:
return
# 复制处理过的图片以便应用效果
img = self.processed_image.copy()
# 应用选定的效果
if effect == "柔化":
img = img.filter(ImageFilter.GaussianBlur(radius=2))
elif effect == "黑白":
img = img.convert("L")
elif effect == "复古":
# 复古效果
r, g, b = img.split()
r = r.point(lambda i: i * 0.9)
g = g.point(lambda i: i * 0.7)
b = b.point(lambda i: i * 0.5)
img = Image.merge("RGB", (r, g, b))
elif effect == "锐化":
img = img.filter(ImageFilter.SHARPEN)
# 调整图片大小以适应窗口,保持比例
self.displayed_image = self._resize_image(img)
# 在画布上显示图片
self.tk_image = ImageTk.PhotoImage(image=self.displayed_image)
self.canvas.delete("all")
# 计算居中位置
canvas_width = self.display_frame.winfo_width()
canvas_height = self.display_frame.winfo_height()
img_width = self.displayed_image.width
img_height = self.displayed_image.height
self.img_x = max(0, (canvas_width - img_width) // 2)
self.img_y = max(0, (canvas_height - img_height) // 2)
self.canvas.create_image(self.img_x, self.img_y, anchor=tk.NW, image=self.tk_image)
# 更新状态标签显示当前文件名
current_file = self.image_files[self.current_index]
self.status_label.config(text=f"当前图片: {current_file}")
def _resize_image(self, img: Image.Image) -> Image.Image:
"""调整图片大小以适应窗口,保持比例"""
# 获取窗口可用尺寸
max_width = self.display_frame.winfo_width() - 40 # 留出边距
max_height = self.display_frame.winfo_height() - 40
# 如果窗口还没初始化,使用默认尺寸
if max_width <= 0 or max_height <= 0:
max_width = 1100
max_height = 600
# 计算缩放比例
width_ratio = max_width / img.width
height_ratio = max_height / img.height
scale_ratio = min(width_ratio, height_ratio)
# 如果图片小于最大尺寸,则不缩放
if scale_ratio >= 1:
return img
# 计算新尺寸
new_width = int(img.width * scale_ratio)
new_height = int(img.height * scale_ratio)
# 调整大小,使用高质量缩放
return img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# 旋转和翻转功能
def rotate_clockwise(self) -> None:
"""顺时针旋转90度"""
if not self.processed_image or self.view_mode != "single":
return
self.rotation_angle = (self.rotation_angle + 90) % 360
self.processed_image = self.processed_image.rotate(-90, expand=True)
self.apply_effect(self.effect_var.get())
def rotate_counterclockwise(self) -> None:
"""逆时针旋转90度"""
if not self.processed_image or self.view_mode != "single":
return
self.rotation_angle = (self.rotation_angle - 90) % 360
self.processed_image = self.processed_image.rotate(90, expand=True)
self.apply_effect(self.effect_var.get())
def flip_horizontal(self) -> None:
"""水平翻转图片"""
if not self.processed_image or self.view_mode != "single":
return
self.processed_image = self.processed_image.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
self.apply_effect(self.effect_var.get())
def flip_vertical(self) -> None:
"""垂直翻转图片"""
if not self.processed_image or self.view_mode != "single":
return
self.processed_image = self.processed_image.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
self.apply_effect(self.effect_var.get())
def reset_image(self) -> None:
"""重置图片到原始状态"""
if self.original_image and self.view_mode == "single":
self.processed_image = self.original_image.copy()
self.rotation_angle = 0
self.cropping = False
self.crop_rect = None
self.canvas.config(cursor="arrow")
self.apply_effect(self.effect_var.get())
def prev_image(self) -> None:
"""显示上一张图片"""
if not self.image_files or self.transitioning or self.view_mode != "single":
return
self.transitioning = True
self.current_index = (self.current_index - 1) % len(self.image_files)
self._update_counter()
self._load_and_display_image()
self.transitioning = False
def next_image(self) -> None:
"""显示下一张图片"""
if not self.image_files or self.transitioning or self.view_mode != "single":
return
self.transitioning = True
self.current_index = (self.current_index + 1) % len(self.image_files)
self._update_counter()
self._load_and_display_image()
self.transitioning = False
def _update_counter(self) -> None:
"""更新图片计数器"""
self.counter_label.config(text=f"{self.current_index + 1}/{len(self.image_files)}")
def toggle_slideshow(self) -> None:
"""切换幻灯片播放状态"""
if self.view_mode != "single":
return
if self.slideshow_running:
self.slideshow_running = False
self.slideshow_btn.config(text="开始幻灯片", bg="#5cb85c")
else:
self.slideshow_running = True
self.slideshow_btn.config(text="停止幻灯片", bg="#d9534f")
self._run_slideshow()
def _run_slideshow(self) -> None:
"""运行幻灯片"""
if self.slideshow_running and self.image_files and self.view_mode == "single":
# 随机切换效果增加美感
effects = ["原图", "柔化", "黑白", "复古", "锐化"]
self.effect_var.set(random.choice(effects))
self.next_image()
# 3-5秒后切换到下一张
self.root.after(random.randint(3000, 5000), self._run_slideshow)
def show_help(self) -> None:
"""显示使用帮助窗口"""
# 创建帮助窗口
help_window = tk.Toplevel(self.root)
help_window.title("使用帮助")
help_window.geometry("800x600")
help_window.configure(bg="#f0f0f0")
help_window.resizable(True, True)
# 添加滚动条
scrollbar = ttk.Scrollbar(help_window)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 创建文本区域
help_text = tk.Text(
help_window,
wrap=tk.WORD,
bg="#ffffff",
padx=20,
pady=20,
font=('SimHei', 10),
yscrollcommand=scrollbar.set
)
help_text.pack(fill=tk.BOTH, expand=True)
scrollbar.config(command=help_text.yview)
# 帮助内容
help_content = """
# 相册浏览使用帮助
## 一、基本介绍
这是一款图片浏览工具,支持单图查看和网格缩略图浏览两种模式,可对图片进行旋转、翻转及特效处理。
## 二、核心功能
### 1. 文件夹操作
- 点击顶部【图片文件夹】按钮,选择存放图片的文件夹
- 支持格式:.jpg、.jpeg、.png、.gif、.bmp、.jfif、.webp
- 选择后自动加载所有图片,默认显示第一张
### 2. 视图模式切换
- 【网格视图】:以缩略图网格展示所有图片,便于快速查找
- 网格会根据窗口大小自动调整列数
- 点击任意缩略图可切换到单图模式并显示该图片
- 【单图视图】:显示单张高清图片,支持图片浏览、播放及图片旋转、翻转操作
### 3. 图片浏览(单图模式)
- 【上一张】/【下一张】按钮:切换图片(支持键盘←/→箭头)
- 底部计数器:显示当前位置(如"1/20")
- 【开始幻灯片】:自动播放图片,点击按钮可停止
- **特效**:通过下拉选项选择显示风格(原图/柔化/黑白/复古/锐化)
### 4. 图片显示方式(单图模式)
- **旋转**:
- 【顺时针】(快捷键r):顺时针旋转90°
- 【逆时针】(快捷键R):逆时针旋转90°
- **翻转**:
- 【水平】:水平镜像翻转
- 【垂直】:垂直镜像翻转
- **重置图片**:恢复当前图片到初始状态
## 三、快捷键
快捷键 功能
← 左箭头 上一张图片
→ 右箭头 下一张图片
Esc 退出程序
r 顺时针旋转
R 逆时针旋转
## 四、注意事项
1. 特效和旋转、翻转操作仅临时生效,不会修改原图文件
2. 首次加载大量图片时,网格视图可能需要短暂加载时间
3. 若图片加载失败,网格中会显示"无法加载"提示
"""
# 插入并格式化内容
help_text.insert(tk.END, help_content)
help_text.config(state=tk.DISABLED) # 设置为只读
# 居中显示窗口
help_window.update_idletasks()
width = help_window.winfo_width()
height = help_window.winfo_height()
x = (self.root.winfo_width() // 2) - (width // 2) + self.root.winfo_x()
y = (self.root.winfo_height() // 2) - (height // 2) + self.root.winfo_y()
help_window.geometry(f"{width}x{height}+{x}+{y}")
if __name__ == "__main__":
root = tk.Tk()
app = AestheticPhotoAlbum(root)
root.mainloop()