Home > Back-end >  create tkinter button with OOP on a same window: issue
create tkinter button with OOP on a same window: issue

Time:02-26

I am a beginner in Python. I created a GUI with nice buttons. To do this I did a change of images: when the mouse hovers the button and when the mouse leaves the button. I did this with this pretty ugly code, but it works:

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()

This is the visual result: visual result of the ugly code (it work)

BUT... The problem is that to make a single button, it takes 15 lines of code. If I want to create 10 buttons, it becomes incredibly repetitive and unpleasant. Being a beginner, I heard about object-oriented programming, and so I turned my code into a class that I called 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)

In the constructor of my class, I define the image used on hover, the image used on leave, the width of the button, its height, as well as the position of this button (horizontal position and vertical position). Still in the constructor, I placed my 2 functions which change the image according to the enter/leave state.

Then I create my buttons as NewButton objects and give the characteristics of the button. When I run my code, python creates the buttons for me, but in a different window. This is the visual result: result with POO code (not working)

What I want is to put all the buttons that I create on the same window, and that's not what I get with my code. Can you tell me what's wrong? Thank you (and sorry for my frenglish!)

SOLUTION by acw1668 that i try (it doesn't work): His suggestion: "You need to create root and canvas outside the class and pass canvas to the class instance instead." what I have done:

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)

CodePudding user response:

You need to create root and canvas outside the class and then pass canvas to the 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()
  • Related