当前位置: 首页 > news >正文

用python制作相册浏览小工具

用python制作相册浏览小工具

近日,欢度国庆、中秋节双节,朋友们应拍了一些相片,故用python制作相册浏览小工具试用。

依赖库:tkinter(通常 Python 自带)和Pillow(图像处理库),后者,需安装。

安装命令:pip install pillow

程序支持两种视图模式,可通过顶部控制栏的【网格视图/单图视图】按钮切换:

单图视图:默认模式,一次显示一张图片,支持图片浏览及图片显示方式操作。

网格视图:以缩略图网格形式显示所有图片,便于快速查找;点击任意缩略图可切换到单图视图并显示该图片。

远行效果:

源码如下:

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, Dictclass AestheticPhotoAlbum:"""相册浏览,支持单图查看和多图网格浏览模式"""def __init__(self, root: tk.Tk):# 初始化主窗口self.root = rootself.root.title("相册浏览")self.root.geometry("1300x800")self.root.configure(bg="#f0f0f0")# 图片相关变量self.image_files: List[str] = []self.current_index: int = 0self.original_image: Optional[Image.Image] = Noneself.processed_image: Optional[Image.Image] = Noneself.displayed_image: Optional[Image.Image] = Noneself.photo_directory: str = ""self.transitioning: bool = Falseself.rotation_angle: int = 0self.cropping: bool = Falseself.crop_start: Optional[Tuple[int, int]] = Noneself.crop_rect: Optional[int] = Noneself.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 = 10self.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.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 // colscol = 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 filenamename_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 = indexself.toggle_view_mode()self._update_counter()return handlerthumb_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 = directoryself._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 = 0self._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 = 0self.cropping = Falseself.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.widthimg_height = self.displayed_image.heightself.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 = 1100max_height = 600# 计算缩放比例width_ratio = max_width / img.widthheight_ratio = max_height / img.heightscale_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":returnself.rotation_angle = (self.rotation_angle + 90) % 360self.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":returnself.rotation_angle = (self.rotation_angle - 90) % 360self.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":returnself.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":returnself.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 = 0self.cropping = Falseself.crop_rect = Noneself.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":returnself.transitioning = Trueself.current_index = (self.current_index - 1) % len(self.image_files)self._update_counter()self._load_and_display_image()self.transitioning = Falsedef next_image(self) -> None:"""显示下一张图片"""if not self.image_files or self.transitioning or self.view_mode != "single":returnself.transitioning = Trueself.current_index = (self.current_index + 1) % len(self.image_files)self._update_counter()self._load_and_display_image()self.transitioning = Falsedef _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":returnif self.slideshow_running:self.slideshow_running = Falseself.slideshow_btn.config(text="开始幻灯片", bg="#5cb85c")else:self.slideshow_running = Trueself.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()

http://www.dtcms.com/a/461483.html

相关文章:

  • 字节跳动ByteDance前端考前总结
  • codex使用chrome-devtools-mcp最佳实践
  • 【Linux命令从入门到精通系列指南】export 命令详解:环境变量管理的核心利器
  • python 自动化采集 ChromeDriver 安装
  • 苏州招聘网站建设推广费
  • java8提取list中对象有相同属性值的对象或属性值
  • cuda编程笔记(26)-- 核函数使用任务队列
  • 存储芯片核心产业链研发实力:兆易创新、北京君正、澜起科技、江波龙、长电科技、佰维存储,6家龙头公司研发实力深度数据
  • 《Seq2Time: Sequential Knowledge Transfer for Video LLMTemporal Grounding》
  • 山东省建设部网站官网网站备案审核通过后
  • 浏览器兼容性问题处理
  • Day 09(下) B2a实例解说----exampleB2a.cc+ActionInitialization+PrimaryGeneratorAction
  • 分布式锁:Redisson的可重入锁
  • 计算机硬件相关(AI回答)
  • 网站设计中的用户体验大型网站需要什么样的团队
  • 淘宝网站开发方式网站托管 济南
  • 重庆网站seo案例网站推广用什么方法最好
  • sql报错:java.sql.SQLSyntaxErrorException: Unknown column ‘as0‘ in ‘where clause‘
  • 做网站是什么公司做陶瓷公司网站
  • CentOS 7上安装SonarQube8.9
  • 遗留系统微服务改造(二):数据迁移实战攻略与一致性保证
  • IO操作(Num22)
  • 领码方案|微服务与SOA的世纪对话(6):组织跃迁——智能架构下的团队与文化变革
  • 怎么什么软件可以吧做网站网站被百度收录很重要
  • C++ 单例模式(Singleton)详解
  • 面向未来的数据平台
  • C++5d
  • Transformer实战(21)——文本表示(Text Representation)
  • 网站空间商 权限梵克雅宝
  • 【Vue 3 】——setup、ref、watch