You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:15:00