我怎样才能最好地创建一个按钮网格,每个按钮都包含一个图像,以便图像缩放到窗口变化?

问题描述

我的解决方案: 我扩展了我在 stackoverflow.com 中找到的一个非常好的示例: How to create a self resizing grid of buttons in tkinter? 如何在 tkinter 中创建一个自我调整大小的按钮网格

另外的问题是如何调整按钮网格的大小 如果它们包含您还想调整大小的图像? 我使用的技术需要改变图像大小 当窗口大小改变时成比例。

请注意,我将文本作为图像包含在内以演示该过程并避免检查中需要额外的图像文件

屏幕截图:

放大的网格显示

Screen shot of enlarged grid display

我的独立示例代码

# grid_resize_image.py
"""
I extended a very nice example I found in stackoverflow.com:
https://stackoverflow.com/questions/7591294/
how-to-create-a-self-resizing-grid-of-buttons-in-tkinter

The additional problem is how to resize the grid of buttons
if they contain images which you also want resized?
The technique I used requires changing the image sizes
proportionally when the window size changes.

The process goes as follows:
    1. Window size changes causing a <Configure> event,which is
    bound to win_size_event.

    2. If win_size_event determines that the button's image size
    has changed sufficiently,resize_grid function
    is called.
    
    3. resize_grid is responsible for altering the buttons
     A. resize_grid checks if the resizing of the buttons will
        exceed the target change in window size. If so,the size
        the changing is stopped to avoid runaway window resising.
     B. resize_grid reconfigures the buttons appropriately 

    The window size to button image size is calculated (roughly)
    by function get_btn_image_size.

"""
import os
import math 
from tkinter import *
from PIL import ImageTk,Image       # Expecting Pillow,# installed under same name
        
def sign(x):
    """ sign - from stackoverflow suggestion
    :x: value to take sign and return 1 for positive,-1 neg
    """
    return math.copysign(1,x)


class ImageGrid:
    def __init__(self,master=None,image_infos=None,nrow=5,ncol=5,btn_pad_x=1,btn_pad_y=1,btn_bd=1):
        """ display a grid of images
        :master: master window/frame default: Tk()
        :image_infos: list of images or (text,image) tuples
                required
        :nrow: number of rows
        :ncol: number of columns
        """
        if master is None:
            master = Tk()
        self.master = master
        self.image_infos = image_infos
        self.nrow = nrow
        self.ncol = ncol
        self.btn_pad_x = btn_pad_x  # Adjustments
        self.btn_pad_y = btn_pad_y
        btn_size_x = btn_size_y = 40    # Initial button size
        self.btn_bd = btn_bd
        self.btn_infos = {}             # Storage [irow,icol}
                                        #    of (btn,base_image)
        self.img_change_target = None   # Target image (width_chg,height_chg)
        self.prev_img_size = None       # (img_width,img_height)
        self.master.rowconfigure(0,weight=1)
        self.master.columnconfigure(0,weight=1)
        
        self.grid_frame=Frame(master)        #Create & Configure frame
        self.grid_frame.grid(row=0,column=0,sticky=N+S+E+W)
        btn_font=('arial',12,'bold')
        self.btn_images = {}            # References(by label) to btn photo images
        i_image = -1        # Index through image_infos
        for row_index in range(nrow):
            self.grid_frame.rowconfigure(row_index,weight=1)
            for col_index in range(ncol):
                self.grid_frame.columnconfigure(col_index,weight=1)
                i_image += 1
                if i_image >= len(image_infos):
                    break         # stop at end of images
                image_info = image_infos[i_image]
                if isinstance(image_info,tuple):
                    btn_label = image_info[0]       # (label,image) list
                    base_image = image_info[1]
                else:
                    btn_label = f"({row_index},{col_index})"    # image list
                    base_image = image_info 
                scaled_image = base_image.resize((int(btn_size_x),int(btn_size_y)))
                btn_image = ImageTk.PhotoImage(master=self.master,image=scaled_image)
                self.btn_images[btn_label] = btn_image  # save reference
                self.grid_frame.columnconfigure(col_index,weight=1)
                display_text = f"{btn_label}"   # for command display
                btn = Button(self.grid_frame,text = btn_label,font = btn_font,image = btn_image,compound = BottOM,# image below text                    
                    command = lambda txt = display_text : self.cmd(txt))
                btn.grid(row=row_index,column=col_index,sticky=N+S+E+W)
                self.btn_infos[row_index,col_index] = (btn,base_image)
                self.master.update() 
        self.grid_frame.bind('<Configure>',self.win_size_event)

    def cmd(self,text):
        print(f"{text} button clicked")

    def resize_grid(self):
        """ Change image sizes so that new grid size
        approximates the new window size
        Lots of the debugging print calls are left in
        for educational purposes.
        """
        win_width,win_height = self.win_width,self.win_height
        img_width,img_height = self.get_btn_image_size(
                            win_width,win_height)
        print(f"resize_grid: win:{win_width}x{win_height} img:{img_width}x{img_height}")
        if self.prev_img_size is None:
            self.prev_img_size = (img_width,img_height)
        prev_width,prev_height = self.prev_img_size
        width_change = img_width - prev_width
        height_change = img_height - prev_height
        if self.img_change_target is None:
            print(f"   new change_target: {width_change}x{height_change}")
            self.img_change_target = (width_change,height_change)
        width_change_target,height_change_target = self.img_change_target
        print(f"   WxH target:{width_change_target}x{height_change_target}")
        print(f"   WxH change:{width_change}x{height_change}")
        if (sign(width_change) == sign(width_change_target)
                and abs(width_change) > abs(width_change_target)
                and sign(height_change) == sign(height_change_target)
                and abs(height_change) > abs(height_change_target)):
            self.prev_img_size = (img_width,img_height)
            print(f" target made - stop resize")
            print(f" new={img_width}x{img_height} prev={prev_width}x{prev_height}\n")
            
            return      # Met our goal - no more resizing
             
        print(f"     resize to: btn_w={img_width} btn_h={img_height}")
        font_height = int(max(12,min(15,img_height/5)))
        btn_font=('arial',int(-font_height),'bold')

        for row_index in range(self.nrow):
            for col_index in range(ncol):
                imo_key = (row_index,col_index)
                if not imo_key in self.btn_infos:
                    continue
                btn,base_image = self.btn_infos[imo_key]
                label = btn.cget('text')
                scaled_image = base_image.resize((img_width,img_height))
                btn_image = ImageTk.PhotoImage(master=self.master,image=scaled_image)
                self.btn_images[label] = btn_image
                btn.config(text=label,font=btn_font,image=btn_image) # store new image
        self.img_change_target = (width_change,height_change)
        print(f"   new change_target: {width_change}x{height_change}")
            
    def win_size_event(self,event):        
        """ Window sizing event
        """
        min_chg = 0    # Minimum change recognizable
        
        self.win_width = win_width = self.master.winfo_width()
        self.win_height = win_height = self.master.winfo_height()
        img_width,win_height)
        print(f"win_size_event: win:{win_width}x{win_height} img:{img_width}x{img_height}")
        
        if self.prev_img_size is None:
            self.prev_img_size = (img_width,prev_height = self.prev_img_size
        width_change = img_width - prev_width
        height_change = img_height - prev_height
        
        if (abs(width_change) >= min_chg
            or abs(height_change) >= min_chg):
            self.resize_grid()
        print("        win_size_event - End")

    def get_btn_image_size(self,win_width=None,win_height=None):
        """ Calculate button's image size based on
        window size and number of button rows,columns
        :win_width: window width (pixels)
                    default: get from window
        :win_height: window height
                    default: get from window
        :returns: (x_size,y_size) in pixels
        """
        text_height = 10
        if win_width is None:
            win_width = self.win_width
        if win_height is None:
            win_height = self.win_height
        btn_size_x_raw = win_width/self.ncol - self.btn_bd
        btn_size_x = btn_size_x_raw - self.btn_pad_x
        btn_image_size_x = btn_size_x 
        
        btn_size_y_raw = win_height/self.nrow - self.btn_bd
        btn_size_y = btn_size_y_raw
        btn_image_size_y = btn_size_y - text_height
        return (int(btn_image_size_x),int(btn_image_size_y)) 

    def mainloop(self):
        self.master.mainloop()

if __name__ == "__main__":
    """ Create some example images without resorting to 
    to other files.  I use text images because they are
    recognizable.
    """
    from PIL import ImageTk,Image
    from PIL import ImageDraw,ImageFont
    
    def pixel2point(px):
        """ Convert pixels to point size
        """
        pt = int(72./96.*px)
        return pt

    
    def make_text_image(text,text_color="blue"):
        """ make image of text
        :text: text string
        :returns: image (Image)
        """
        text_size = 100
        font_size = pixel2point(text_size)
        text_font = ImageFont.truetype("arial.ttf",size=font_size)
        
        image = Image.new("RGB",(text_size,text_size),(255,255,255))
        draw = ImageDraw.Draw(image)      # Setup ImageDraw access
        draw.text((0,0),text,fill=text_color,font=text_font)
        #text_images[text] = image
        return image
    
    nrow = 5
    ncol = 6
    image_infos = []
    for i in range(nrow*ncol):
        ch = chr(ord("A")+i)
        image = make_text_image(ch)
        image_infos.append((ch,image))
    
    ig = ImageGrid(image_infos=image_infos,nrow=nrow,ncol=ncol)
    ig.mainloop()

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...