仅当在主窗口中创建一个按钮时,具有图像功能的按钮大小调整才有效

Button resize with image function works only when one button is created in the main window

提问人:Paolo Sestini 提问时间:11/14/2023 更新时间:11/14/2023 访问量:42

问:

我创建了一个函数,用于在调整按钮大小时调整按钮上的图像大小(我想在按钮的左上角保留徽标图像,并将文本放在按钮的中间),当在根窗口中创建单个按钮时,它似乎可以正常工作,但是当创建第二个按钮时,第一个按钮会填充整个根窗口并隐藏第二个按钮。

下面是我的代码。

请问,任何人都可以告诉我我做错了什么或如何解决我的问题吗?

谢谢。

#!/usr/bin/env python
# coding: utf-8

import tkinter as tk
from PIL import Image, ImageTk, ImageColor, ImageFont


root = tk.Tk()
root.wm_state("zoomed")

screenWidth = root.winfo_screenwidth()
screenHeight = root.winfo_screenheight()
screenText = f'Screen Width: {screenWidth}\nScreen Height: {screenHeight}'


def OnEnter(event, color: str):
    event.widget['background'] = color

def OnLeave(event, color):
    event.widget['background'] = color


def OnButtonResize(event, logo: str, extraText: str = "") -> None:
    
    eventWidth = event.width
    eventHeight = event.height

    #print(f"Button resized to {eventWidth}x{eventHeight}")

    # create a larger transparent image (around the size of the button)
    img = Image.new("RGBA", (eventWidth, eventHeight), (0, 0, 0, 0))
    # paste the small image at the top-left corner of this large image
    img.paste(logo, (int(eventWidth/50), int(eventHeight/30)))
    
    #imgWidth, imgHeight = img.size
    buttonText = f"Button Width: {eventWidth}\nButton Height: {eventHeight}\n{extraText}"
    #print(buttonText)

    tkimg = ImageTk.PhotoImage(img)
    
    event.widget['text'] = buttonText
    event.widget['image'] = tkimg
    event.widget.image = tkimg    # This line is to make the image persistent and avoid errors due to garbage collector


def Rgb2Hex(rgbTuple: tuple) -> str:
    
    return (f"#{rgbTuple[0]:02x}{rgbTuple[1]:02x}{rgbTuple[2]:02x}")


def Hex2Rgb(hexcode: str) -> tuple:
    
    return(ImageColor.getcolor(hexcode, "RGB"))


def MinMax(x: int) -> int:
    return(max(0, min(x, 255)))


def LightenHexColor(hexColorCode: str , percent: int = 10) -> str:
    
    inputColorTuple = Hex2Rgb(hexColorCode)
    
    ligtherColorTuple = ()
    for i in inputColorTuple:
        ligtherColorTuple = ligtherColorTuple + (MinMax(int(i*(100+percent)/100)),)

    return(Rgb2Hex(ligtherColorTuple))


def GetZoneNameImage(zoneName: str = "Z1", fontColor: str = "#000000") -> str:
    
    imageFilename = f"{zoneName}.png"
    font_size = 128
    font_filepath = "C:/Windows/Fonts/calibrii.ttf"
    
    color = Hex2Rgb(LightenHexColor(fontColor, -25))

    font = ImageFont.truetype(font_filepath, size=font_size)
    mask_image = font.getmask(zoneName, "L")
    img = Image.new("RGBA", mask_image.size)
    img.im.paste(color, (0, 0) + mask_image.size, mask_image)  # need to use the inner `img.im.paste` due to `getmask` returning a core
    img.save(imageFilename, quality=100)
    
    return(imageFilename)


helv32 = ("Helvetica", 32, "bold")

logoWidth = 150
logoHeight = 110

bgColorCursorOut1 = "#38bab6"
bgColorCursorIn1 = LightenHexColor(bgColorCursorOut1)

logoFilename1 = GetZoneNameImage(zoneName="Z1", fontColor=bgColorCursorOut1)

# load the required image and resize it to the desired size
logo1 = Image.open(logoFilename1).resize((logoWidth, logoHeight))
tkimg1 = ImageTk.PhotoImage(logo1)

bgColorCursorOut1 = "#38bab6"
bgColorCursorIn1 = LightenHexColor(bgColorCursorOut1)

button1 = tk.Button(root, text=screenText, image=tkimg1, compound="c", bg=bgColorCursorOut1, font=helv32)
button1.pack(fill=tk.BOTH, expand=tk.TRUE)

button1.bind("<Configure>", lambda event: OnButtonResize(event, logo1, screenText))
button1.bind("<Enter>", lambda event: OnEnter(event, bgColorCursorIn1))
button1.bind("<Leave>", lambda event: OnLeave(event, bgColorCursorOut1))

bgColorCursorOut2 = "#FCBA03"
bgColorCursorIn2 = LightenHexColor(bgColorCursorOut2)

logoFilename2 = GetZoneNameImage(zoneName="Z2", fontColor=bgColorCursorOut2)

# load the required image and resize it to the desired size
logo2 = Image.open(logoFilename2).resize((logoWidth, logoHeight))
tkimg2 = ImageTk.PhotoImage(logo2)

button2 = tk.Button(root, text=screenText, image=tkimg2, compound="c", bg=bgColorCursorOut2, font=helv32)
button2.pack(fill=tk.BOTH, expand=tk.TRUE)

button2.bind("<Configure>", lambda event: OnButtonResize(event, logo2))
button2.bind("<Enter>", lambda event: OnEnter(event, bgColorCursorIn2))
button2.bind("<Leave>", lambda event: OnLeave(event, bgColorCursorOut2))


root.mainloop()
python-3.x tkinter 按钮

评论

0赞 acw1668 11/14/2023
不应将图像大小调整为与按钮大小相同的大小。图像的可用空间小于按钮的大小,因为按钮有 (1px)、(2px) 和 (1px) 占用空间。因此,图像的最大大小应为 x 。padxborderwidthhighlightthicknesseventWidth-8eventHeight-8
0赞 Karl Knechtel 11/14/2023
@acw1668请找到合适的副本或将其写下来作为答案。
0赞 Paolo Sestini 11/14/2023
@acw1668,谢谢。我已经应用了您的建议,情况正在改善很多,但是现在,当我调整根窗口的大小时,两个按钮的大小不会以相同的高度调整,最终,第二个按钮消失了。

答:

2赞 acw1668 11/14/2023 #1

由于您在两个按钮上使用,因此它们将尝试占用可用空间。并且将图像的大小设置为与按钮大小相同的大小,并且按钮内的可用空间不足以容纳图像(由于 和 options 占用了一些空间)。所以会发光,因为它是打包到窗口中的第一个按钮,它会再次触发事件。pack(fill=tk.BOTH, expand=tk.TRUE)padxborderwidthhighlightthicknessbutton1<Configure>

对于您的情况,您可以使用而不是将按钮的大小限制在窗口的一半:place()pack()

...
# button1 occupies the upper half of the window
button1.place(x=0, y=0, relwidth=1, relheight=0.5)
...
# button2 occupies the low half of the window
button2.place(x=0, rely=0.5, relwidth=1, relheight=0.5)

您可以正确使用并获得相同的结果。grid().rowconfigure().columnconfigure()

评论

0赞 Paolo Sestini 11/14/2023
您建议使用 place 的解决方案会引发错误:_tkinter。TclError:图像“pyimage727”不存在,但使用 grid() 似乎解决了所有问题。谢谢。
0赞 acw1668 11/14/2023
@PaoloSestini我不知道你是如何实现我的解决方案的,但它对我有用。
0赞 Paolo Sestini 11/14/2023
我只是替换了两行:......button1.pack(fill=tk.两者,expand=tk。没错)......button2.pack(fill=tk.两者,expand=tk。TRUE) 替换为您建议的解决方案。
0赞 acw1668 11/14/2023
@PaoloSestini 尝试在创建两个按钮时设置初始和选项。widthheight
0赞 Paolo Sestini 11/14/2023
是的,设置按钮的初始宽度和高度解决了_tkinter。TclError:图像“pyimage727”错误。谢谢。