Tkinter滚动图片画廊行数设为1或2时显示异常求助
Tkinter图片画廊显示异常修复方案
问题描述
用Python Tkinter开发可滚动图片画廊,计划展示200张图片:
- 当每行图片数(
image_number)设为3~20时,所有图片正常显示 - 设为1时,展示到第38张后出现显示缺陷,无法加载全部图片
- 设为2时,展示到第14张后出现同样异常
原代码
import os import tkinter as tk from tkinter import filedialog, ttk import pyautogui import cv2 import h5py import numpy as np import pandas as pd from matplotlib import pyplot as plt from numpy.linalg import norm from PIL import Image, ImageTk import time from termcolor import colored from Ham_Img_Analyzer import featHA import threading import time import atexit from tkinter import messagebox from tkinter import PhotoImage class Timer: def __init__(self): self.seconds = 0 self.time_str = "" def run(self): """A function to show a timer that updates every second.""" while not self.stop_event.is_set(): m, s = divmod(self.seconds, 60) self.time_str = "{:02d}:{:02d}".format(m, s) print(colored("Elapsed time: {}\r".format(self.time_str),"green"), end="") time.sleep(1) self.seconds += 1 def start(self): self.stop_event = threading.Event() self.timer_thread = threading.Thread(target=self.run) self.timer_thread.start() def stop(self): self.stop_event.set() self.timer_thread.join() def get_elapsed_time(self): return self.time_str class ScrollableImageGallery(tk.Frame): def __init__(self, master=None, **kwargs): super().__init__(master, **kwargs) self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0) self.hscrollbar = ttk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.canvas.xview) self.vscrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.canvas.configure(xscrollcommand=self.hscrollbar.set, yscrollcommand=self.vscrollbar.set) self.canvas.pack(side="left", fill="both",expand=True) self.hscrollbar.pack(side="bottom", fill="x") self.vscrollbar.pack(side="right", fill="y") self.load_images(image_index) # Replace with the path to your image folder def load_images(self, folder): images = [] row_num = 0 col_num = 0 for image_path in image_index: image = Image.open(image_path) file=(image_path.split("\\")[-1]) images.append((file, image)) # Add file name to tuple for i, (file, image) in enumerate(images): image=image.resize((image_width,image_height)) photo = ImageTk.PhotoImage(image) label = tk.Label(self.scrollable_frame, image=photo) label.image = photo label.grid(row=row_num, column=col_num, padx=0, pady=3) # 动态设置标题字体大小 if 1<=image_number<=5: fontsize=10 elif 6<=image_number<=7: fontsize=6 elif 8<=image_number<=12: fontsize=4 elif 13<=image_number<=16: fontsize=3 elif 17<=image_number<=20: fontsize=2 title = tk.Label(self.scrollable_frame, text=str(i+1)+"."+file,font=("Arial",fontsize)) title.grid(row=row_num+1 , column=col_num) # Add title label below image label col_num +=1 if col_num == image_number: col_num = 0 row_num +=2 self.canvas.configure(scrollregion=self.canvas.bbox("all")) image_number=1 screen_width,screen_height=pyautogui.size() if image_number==None: image_number=5 image_width=((screen_width-120)//(image_number))-2 image_height=image_width*3//4 if image_height>=screen_height: image_height=screen_height*3//4 # 读取路径文件 f = open(r"C:\Users\VAIO\Desktop\DSC\Ham Model\Source_Image_paths_feather_path.txt", "r") bank_path=f.readline().rstrip() bank_path=bank_path[:-2] feath_path=f.readline().rstrip() f.close() # 获取图片列表 import glob image_index=glob.glob(r"C:\Users\VAIO\Desktop\DSC\TEST_HAM\Image Samples\sample\*") image_index=(image_index[:200]) # 启动主窗口 root = tk.Tk() root.geometry(str(screen_width)+"x"+str(screen_height)) phot=PhotoImage(file=r"C:\Users\VAIO\Desktop\DSC\Ham Model\turtle.png") root.iconphoto(False,phot) root.title("*HamNet* Gallery") scrollable_gallery = ScrollableImageGallery(root) scrollable_gallery.pack(fill="both", expand=True) root.mainloop()
修复方案
1. 修正布局索引计算逻辑
原代码通过row_num +=2累积行号,当图片数量大时容易出现计算偏移。改用基于图片索引的精确计算:
def load_images(self, folder): images = [] for image_path in image_index: image = Image.open(image_path) file=(image_path.split("\\")[-1]) images.append((file, image)) for i, (file, image) in enumerate(images): # 基于索引精确计算行号和列号 col_num = i % image_number row_num = (i // image_number) * 2 # 每行占2行(图片+标题) image=image.resize((image_width,image_height)) photo = ImageTk.PhotoImage(image) label = tk.Label(self.scrollable_frame, image=photo) label.image = photo label.grid(row=row_num, column=col_num, padx=0, pady=3) # 动态设置标题字体大小 if 1<=image_number<=5: fontsize=10 elif 6<=image_number<=7: fontsize=6 elif 8<=image_number<=12: fontsize=4 elif 13<=image_number<=16: fontsize=3 elif 17<=image_number<=20: fontsize=2 title = tk.Label(self.scrollable_frame, text=str(i+1)+"."+file,font=("Arial",fontsize)) title.grid(row=row_num+1 , column=col_num) # 强制更新界面后再计算滚动区域 self.scrollable_frame.update_idletasks() self.canvas.configure(scrollregion=self.canvas.bbox("all"))
2. 优化滚动区域更新
在所有图片加载完成后,先调用update_idletasks()让Tkinter渲染完所有控件,再更新Canvas的滚动区域,避免因控件未完全渲染导致的区域计算错误。
3. 可选:分批加载图片(针对大量图片)
一次性加载200张图片会占用较多内存,可实现滚动触发加载:
def __init__(self, master=None, **kwargs): super().__init__(master, **kwargs) # ... 原有代码 ... self.loaded_count = 0 self.batch_size = 20 # 每次加载20张 self.load_batch() # 绑定滚动事件,滚动到底部时加载下一批 self.canvas.bind("<MouseWheel>", self.on_scroll) self.canvas.bind("<Button-4>", self.on_scroll) # Linux向上滚动 self.canvas.bind("<Button-5>", self.on_scroll) # Linux向下滚动 def load_batch(self): end_idx = min(self.loaded_count + self.batch_size, len(image_index)) for i in range(self.loaded_count, end_idx): image_path = image_index[i] image = Image.open(image_path) file = image_path.split("\\")[-1] col_num = i % image_number row_num = (i // image_number) * 2 image=image.resize((image_width,image_height)) photo = ImageTk.PhotoImage(image) label = tk.Label(self.scrollable_frame, image=photo) label.image = photo label.grid(row=row_num, column=col_num, padx=0, pady=3) # 标题逻辑同上 if 1<=image_number<=5: fontsize=10 elif 6<=image_number<=7: fontsize=6 elif 8<=image_number<=12: fontsize=4 elif 13<=image_number<=16: fontsize=3 elif 17<=image_number<=20: fontsize=2 title = tk.Label(self.scrollable_frame, text=str(i+1)+"."+file,font=("Arial",fontsize)) title.grid(row=row_num+1 , column=col_num) self.loaded_count = end_idx self.scrollable_frame.update_idletasks() self.canvas.configure(scrollregion=self.canvas.bbox("all")) def on_scroll(self, event): # 滚动到底部时加载下一批 scroll_pos = self.canvas.yview()[1] if scroll_pos >= 0.95 and self.loaded_count < len(image_index): self.load_batch()
修复原理
- 精确的行列索引计算避免了累积误差,确保每张图片和标题都放在正确位置
update_idletasks()强制界面渲染完成后再计算滚动区域,保证区域范围准确- 分批加载减少了初始内存占用,避免因一次性加载大量控件导致的界面卡顿和布局异常
内容的提问来源于stack exchange,提问作者MJELVEH
相关产品推荐
相关产品推荐

