在同一个 window: 问题上使用 OOP 创建 tkinter 按钮

create tkinter button with OOP on a same window: issue

我是 Python 的初学者。我创建了一个带有漂亮按钮的 GUI。为此,我更改了图像:当鼠标悬停在按钮上时以及当鼠标离开按钮时。我用这个非常丑陋的代码做到了这一点,但它有效:

from tkinter import *
from PIL import Image, ImageTk

root = Tk()
root.title("My first Python GUI")
root.geometry("1130x800")

canvas = Canvas(root, bg="#a9dfbf")
canvas.pack(fill=BOTH, expand=True)

button_1_onHover = Image.open("Buttons/button1_hover.png")
button_1_onLeave = Image.open("Buttons/button1_leave.png")
button_2_onHover = Image.open("Buttons/button2_hover.png")
button_2_onLeave = Image.open("Buttons/button2_leave.png")

root.button_1_onLeave = ImageTk.PhotoImage(button_1_onLeave)
root.button_1_onHover = ImageTk.PhotoImage(button_1_onHover)
root.button_2_onLeave = ImageTk.PhotoImage(button_2_onLeave)
root.button_2_onHover = ImageTk.PhotoImage(button_2_onHover)

def on_enter(event):
    button1.config(image=root.button_1_onHover)
def on_leave(leave):
    button1.config(image=root.button_1_onLeave)
    
def on_enter2(event):
    button2.config(image=root.button_2_onHover)
def on_leave2(leave):
    button2.config(image=root.button_2_onLeave)


button1 = Button(root, image=root.button_1_onLeave, bg="#a9dfbf", width=400, height=150, bd=0, relief="sunken", activebackground="#a9dfbf")

button2 = Button(root, image=root.button_2_onLeave, bg="#a9dfbf", width=400, height=150, bd=0, relief="sunken", activebackground="#a9dfbf")

canvas.create_window(300, 150, window=button1)
canvas.create_window(300, 350, window=button2)

button1.bind("<Enter>", on_enter)
button1.bind("<Leave>", on_leave)
button2.bind("<Enter>", on_enter2)
button2.bind("<Leave>", on_leave2)

root.mainloop()

这是视觉结果: visual result of the ugly code (it work)

但是... 问题是制作一个按钮需要 15 行代码。 如果我想创建 10 个按钮,它会变得非常重复和令人不快。 作为初学者,我听说过面向对象编程,所以我将我的代码变成了一个 class 我称之为 NewButton:

from tkinter import *
from PIL import Image, ImageTk


class NewButton:
    def __init__(self, imageHover, imageLeave, width, height, hposition, vposition):
        
        self.root = Tk()
        self.root.title("My first Python GUI")
        self.root.geometry("1130x800")
        
        canvas = Canvas(self.root, bg="#a9dfbf")
        canvas.pack(fill=BOTH, expand=True)
        
        self.width = width
        self.height = height
        self.hposition = hposition
        self.vposition = vposition
        
        self.imageHover = Image.open(f"Buttons/{imageHover}.png")
        self.imageLeave = Image.open(f"Buttons/{imageLeave}.png")

        self.root.imageLeave = ImageTk.PhotoImage(self.imageLeave)
        self.root.imageHover = ImageTk.PhotoImage(self.imageHover)
        
        self.button = Button(self.root, image=self.root.imageLeave, bg="#a9dfbf", width=self.width, height=self.height, bd=0, relief="sunken", activebackground="#a9dfbf")

        canvas.create_window(self.hposition, self.vposition, window=self.button)
        
        def on_enter(event):
            self.button.config(image=self.root.imageHover)
        def on_leave(leave):
            self.button.config(image=self.root.imageLeave)

        self.button.bind("<Enter>", on_enter)
        self.button.bind("<Leave>", on_leave)

        self.root.mainloop()
    

NewButton("button1_hover","button1_leave",400,150,300,150)
NewButton("button2_hover","button2_leave",400,150,300,350)

在我的 class 的构造函数中,我定义了悬停时使用的图像、离开时使用的图像、按钮的宽度、高度以及该按钮的位置(水平位置)和垂直位置)。 仍然在构造函数中,我放置了 2 个根据 enter/leave 状态更改图像的函数。

然后我将按钮创建为 NewButton 对象并赋予按钮的特征。 当我 运行 我的代码时,python 为我创建按钮,但在另一个 window 中。 这是视觉结果: result with POO code (not working)

我想要的是将我创建的所有按钮放在同一个 window 上,而这不是我的代码所得到的。 你能告诉我怎么了吗? 谢谢(对不起我的英语!)

我尝试的 acw1668 解决方案(它不起作用): 他的建议是:“您需要在 class 之外创建 root 和 canvas,然后将 canvas 传递给 class 实例。” 我做了什么:

from tkinter import *
from PIL import Image, ImageTk


root = Tk()
root.title("My first Python GUI")
root.geometry("1130x800")
canvas = Canvas(root, bg="#a9dfbf")
canvas.pack(fill=BOTH, expand=True)


class NewButton:
    def __init__(self, canvas, imageHover, imageLeave, width, height, hposition, vposition):

        self.canvas = canvas
        self.width = width
        self.height = height
        self.hposition = hposition
        self.vposition = vposition
        
        self.imageHover = Image.open(f"Buttons/{imageHover}.png")
        self.imageLeave = Image.open(f"Buttons/{imageLeave}.png")

        self.canvas.imageLeave = ImageTk.PhotoImage(self.imageLeave)
        self.canvas.imageHover = ImageTk.PhotoImage(self.imageHover)
        
        self.button = Button(self.canvas, image=self.canvas.imageLeave, bg="#a9dfbf", width=self.width, height=self.height, bd=0, relief="sunken", activebackground="#a9dfbf")

        canvas.create_window(self.hposition, self.vposition, window=self.button)
        
        def on_enter(event):
            self.button.config(image=self.canvas.imageHover)
        def on_leave(leave):
            self.button.config(image=self.canvas.imageLeave)

        self.button.bind("<Enter>", on_enter)
        self.button.bind("<Leave>", on_leave)

        self.canvas.mainloop()
    

NewButton(canvas,"button1_hover","button1_leave",400,150,300,150)
NewButton(canvas,"button2_hover","button2_leave",400,150,300,350)

您需要在 class 之外创建 rootcanvas,然后将 canvas 传递给 class:

from tkinter import *
from PIL import Image, ImageTk


class NewButton:
    def __init__(self, canvas, imageHover, imageLeave, width, height, hposition, vposition):

        self.canvas = canvas
        self.width = width
        self.height = height
        self.hposition = hposition
        self.vposition = vposition

        imageHover = Image.open(f"Buttons/{imageHover}.png")
        imageLeave = Image.open(f"Buttons/{imageLeave}.png")

        self.imageLeave = ImageTk.PhotoImage(imageLeave)
        self.imageHover = ImageTk.PhotoImage(imageHover)

        self.button = Button(canvas, image=self.imageLeave, bg="#a9dfbf", width=self.width, height=self.height, bd=0, relief="sunken", activebackground="#a9dfbf")
        self.button.bind("<Enter>", self.on_enter)
        self.button.bind("<Leave>", self.on_leave)

        self.item_id = canvas.create_window(self.hposition, self.vposition, window=self.button)

    def on_enter(self, event):
        self.button.config(image=self.imageHover)

    def on_leave(self, event):
        self.button.config(image=self.imageLeave)


root = Tk()
root.title("My first Python GUI")
root.geometry("1130x800")

canvas = Canvas(root, bg="#a9dfbf")
canvas.pack(fill=BOTH, expand=True)

NewButton(canvas,"button1_hover","button1_leave",400,150,300,150)
NewButton(canvas,"button2_hover","button2_leave",400,150,300,350)

root.mainloop()