Tkinter Button image not showing - python

I am using tkinter to make a simple drawing tool. I am trying to put an image, fill_bucket.png, on the button. However, when I run the program, the button is incredibly small, like it has no content, and there is not image.
From looking around, it seems like the issue is probably Python's garbage collection, however I have tried many ways of fixing this and nothing seems to work. The simplest thing I have tried is:
self.fill_bucket_photo = PhotoImage(file='./img/fill_bucket.png')
self.fillButton = Button(self.button_layout, image=self.fill_bucket_photo, compound='top', font=('Arial', 25), width=1, height=1, command=lambda: self.selectTool('fill'))
However this does not work. I have also tried this solution, which I found here:
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
However when I do self.fillButton.image after assigning self.fillButton, I get an Attribute error for the image attribute.
Everything that I am doing is inside a class, and I can define normal buttons with text fine.
Additionally, doing
self.fill_bucket_photo = PhotoImage(file='./img/fill_bucket.png')
self.label = Label(image=self.fill_bucket_photo).grid(row=1, column=1)
shows the image normally.
Full example. On my computer, this code creates a window that contains one blank button with no image on it:
from tkinter import *
class App():
def __init__(self):
self.root = Tk()
self.root.resizable(False, False)
self.fill_bucket_photo = PhotoImage(file='./img/fill_bucket.png')
self.fillButton = Button(self.root, image=self.fill_bucket_photo, compound='top', font=('Arial', 25), width=40, height=40).pack()
self.root.mainloop()
app = App()
What are some possible solutions to this?

Related

How to remove error made when adding exit button?

from tkinter import*
import sqlite3
class login:
def __init__(self,root):
self.root=root
self.root.geometry("250x250")
self.root.title("Login")
self.root.resizable(False,False)
self.var_username=StringVar() ##variables
self.var_password=StringVar()
username=Label(self.root,text="Username",font=("Bahnschrift SemiBold",15),bg="white",fg="black").place(x=15,y=20)
username=Entry(self.root,textvariable=self.var_username,font=("Bahnschrift SemiBold",15),bg="white",fg="black").place(x=125,y=20,width=115)
password=Label(self.root,text="Password ",font=("Bahnschrift SemiBold",15),bg="white",fg="black").place(x=15,y=60)
password=Entry(self.root,textvariable=self.var_password,font=("Bahnschrift SemiBold",15),bg="white",fg="black").place(x=125,y=60,width=115)
_exit=Button(self.root,text="exit",command=self.destroy,font=("Bahnschrift SemiBold",15),bg="green",fg="white",cursor="hand2").place(x=125,y=100,width=55,height=28)
if __name__ == "__main__":
root=Tk()
obj=login(root)
root.mainloop()
it comes up with a attribute error and im not to sure how to fix it because it works in other pieces of code.
You might want to change the button command from self.destroy to self.root.destroy.
Button(self.root,
text="exit",
command=self.root.destroy,
font=("Bahnschrift SemiBold",15),
bg="green",
fg="white",
cursor="hand2").place(x=125, y=100, width=55, height=28)
As a side note, doing label = Label(root, ...).place(x=...) does not do anything (for any widget). The value of label will be stored as None and you wont be able to reference that later to change its properties. If this is the goal, then simply: Label(root, ...).place(x=...) would work. Otherwise, you'll have to create widgets in one line, and place them in the next lline
PS: It is a recommended practice to also include the error faced in order for ease of diagnosing. Please do refer How to ask for asking further questions.

Creating a new window in tkinter that has the same widgets from the root window

I am wanting to create a tkinter window where when I click a button widget it opens a new window, showing all the widgets, exactly the same, from the root/original window. Essentially creating a second instance of the root window, where the application can have multiple users, using the same GUI, in different windows.
Any help is appreciated.
An example of one of my widgets:
summary_output = Text(
master=window,
height=8,
width=78,
bg="gray95",
borderwidth=2,
relief="groove",
font=("Arial", 12))
My window layout
window = Tk()
window.title("Data Viewer")
window.geometry("750x950")
window.configure(bg='white')
window.iconphoto(False, tk.PhotoImage(file='icon.png'))
I have this but cant seem to place the widgets from the root window:
def new_window():
newWindow = Toplevel(window)
newWindow.geometry("750x950")
newWindow.configure(bg='white')
newWindow.iconphoto(False, tk.PhotoImage(file='icon.png'))
upload_button.place(x=20, y=560)
mainloop()
Is their anyway to change the master to be any window?
Edit:
from tkinter import *
class StaticFrame(Frame):
def __init__(self,master,*args,**kwargs):
Frame.__init__(self,master,*args,**kwargs)
# All your widgets
Label(self,text='This is a reusable frame',font=(0,17)).place(x=0, y=0)
Button(self,text='Click me for nothing').pack()
Label(self,text='End of page').pack()
upload_button = Button(
self,
text="Edit Data",
fg="DodgerBlue4",
font=("Graph Type", 15),
height=1, width=12,
borderwidth=2,
relief="groove")
upload_button.place(x=20, y=50)
root = Tk() # First window
top = Toplevel(root) # Second window
root.geometry("750x968")
StaticFrame(root).pack() # Put the frame on the first window
StaticFrame(top).pack() # Put the frame on the second window
root.mainloop()
Result:
The concept used here is simple, create a "custom frame" that we will put onto these new windows, so that it will create the exact same frame, and widgets within it, inside different windows.
from tkinter import *
class StaticFrame(Frame):
def __init__(self,master,*args,**kwargs):
Frame.__init__(self,master,*args,**kwargs)
# All your widgets
Label(self,text='This is a reusable frame',font=(0,17)).pack()
Button(self,text='Click me for nothing').pack()
Label(self,text='End of page').pack()
root = Tk() # First window
top = Toplevel(root) # Second window
StaticFrame(root).pack() # Put the frame on the first window
StaticFrame(top).pack() # Put the frame on the second window
root.mainloop()
Very simple to code and has been explained with comments, if you do not know what classes and inheritance is then first do go through those. There are variety of other methods that come onto mind when I read this question, like even having an option database and storing the widgets in a list and recreating it based on its order, but this seems to be the easiest in a scratch.

grid_forget() still leads to error when using pack later on

I have a program I'm working on where, to keep it in the same window, I have my widgets in a frame. When I want to change the window, then I use either frame.pack_forget() or frame.grid_forget() and then frame.destroy() before simply adding a new frame to the new window. However, even having used grid_forget, if I use .pack(), I get an error saying I can't use pack when something is already managed by grid. Does anyone know how to circumvent this while still keeping everything within the same window?
.pack_forget seems to be working fine as I can transition from a frame using pack to a frame using grid without issue.
Here is a reproduction of the problem:
from tkinter import *
root = Tk()
def main_Menu (root):
frame = Frame(root)
frame.pack()
button = Button(frame, text="button ", command=lambda:[frame.pack_forget(), frame.destroy, function(root)])
button.pack()
def function(root):
frame = Frame(root)
frame.grid(row=0)
back_Button = Button(root, text="Back", command=lambda:[frame.grid_forget(), frame.destroy(), return_To_Menu(root)])
back_Button.grid(row=0)
def return_To_Menu(root):
main_Menu(root)
main_Menu (root)
Your packed Button is attached to the frame, while the grided button is attached to the root.
After changing
back_Button = Button(root, text="Back", command=lambda:[frame.grid_forget(), frame.destroy(), return_To_Menu(root)])
back_Button.grid(row=0)
to
back_Button = Button(frame, text="Back", command=lambda:[frame.grid_forget(), frame.destroy(), return_To_Menu(root)])
back_Button.grid(row=0)
it worked for me.

Remove border of tkinter button created with an image

I've created a couple of buttons using my program and I've made them include images. However, I wish to now remove the border that remains (see http://i.imgur.com/XRlmq39.png for screenshot).
The code for the "back" button as an example:
backbutton = ttk.Button(mainframe, command=homereturn)
backbuttonimage = PhotoImage(file="back.gif")
backbutton.config(image=backbuttonimage)
backbutton.pack()
backbutton.grid(column=0, row=1)
Any help would be greatly appreciated.
backbutton = tk.Button(..., highlightthickness = 0, bd = 0)
This works for Python 3.x, I tried...
icon = PhotoImage(file="lock.png")
self.company_lock_button = Button(self.control_lock_frame, image = icon, highlightthickness = 0, bd = 0)
self.day_lock_button = Button(self.control_lock_frame, image = icon)
self.hour_lock_button = Button(self.control_lock_frame, image = icon)
If you are using images to define a custom button, use the standard button class rather than the ttk button class. This will allow you to set the borderwidth attribute to zero:
import tkinter as tk
...
backbutton = tk.Button(..., borderwidth=0)
(unrelated: there's no point in calling backbutton.pack() and immediately follow it with backbutton.grid(...) - you can only use one for a particular widget, and the last one you call is the one that has any effect. In this case the call to pack is completely useless)
Using a new Mac I had the same issue and none of the other answers fully removed the border surrounding the button. I found that setting highlightbackground to the canvas's background color did the trick.
I had the same issue with imaged buttons. Neither bd=0 nor highlichtthickness=0 alone helped. I even suspected the relief-option to play it's part.
What finally helped (with me): pady=0, padx=0
So for the question asked: backbutton.config(image=backbuttonimage, highlichtthickness=0, pady=0, padx=0) could work.
You can use highlightthickness=0 and bd=0
None of the solutions provided so far worked in my case.
(I can only suspect that this is related to the new MacOS GUI style policy of recent versions (I'm using Big Sur). Also other style options don't work on MacOS, e.g. the relief option doesn't have any effect on the appearance of a button.)
Here is my solution: when you put an image to a button, the options width and height take pixels as unit. When you chose small values, i.e. 10x10, the border seems to be covered by the image. Voilá!
Here's one way you could do it..
Use ttk.Style() to set the button's background color to the color of mainframe.
root_color = "red" #Just an example, can't remember the default tk window color
mainframe = tk.Tk() #or whatever mainframe is
mainframe.configure(bg = root_color)
style = ttk.Style()
style.configure('TButton', background = root_color)
backbutton = ttk.Button(mainframe, command=homereturn)
backbuttonimage=PhotoImage(file="back.gif")
backbutton.config(image=backbuttonimage)
backbutton.pack()
backbutton.grid(column=0, row=1)
As a side note, you don't have to specify style = .. in your ttk button here because you are configuring the default TButton style ttk uses. If you defined a custom style for this button you would have to specify this in the keyword arguements for the button.
An example would be giving your button rounded edges instead of using images to achieve the desired effect.
If you are using the import function as:
from tkinter import *
Button(main_scr, text = "OK", bg = "yellow", bd = 0, command = delete1).pack()
The bd = 0 would not show the border.
Not all themes support changing borderwidth. Try using tkinter.tk instead of using ttk.Button.
Use keyword borderwidth=0 for Button's parameter.
You cannot use both backbutton.pack() and backbutton.grid(). You can select one of them.
Snippet:
from tkinter import ttk
import tkinter as tk
mainframe = tk.Tk()
def homereturn():
print('hello')
backbutton = tk.Button(mainframe, command=homereturn, borderwidth=0)
backbuttonimage = tk.PhotoImage(file="p1.png")
backbutton.config(image=backbuttonimage)
backbutton.pack()
mainframe.mainloop()
Screenshot w/out border:

I cannot change the text of a Label

I'm trying to source out code for a GUI program. I made a simple test and I cannot change the text value on the GUI, no error and nothing happens. Some issue with the mainloop of Tkinter?
serial.py:
import gui
gui.chiplabel.config(text="A.3f V" )
gui.py:
from Tkinter import *
root = Tk()
chiplabel = Label(root, relief=RIDGE, width = 9 , text ="Unknown",
padx=0, pady=0).grid(row = 0,column=5, sticky =W)
root.mainloop()
You have two main problems with your code. It needs to be restructured, and you're making a very common mistake with laying out your widgets.
Organizing your code
The way you have your code structured, your call to configure happens after mainloop exits, and after the widgets have been destroyed. You need to reorganize your code so that the call to mainloop is the last line of code that is executed.
In my opinion this is best accomplished by using classes and objects, but that's not strictly necessary. You simply need to not have any code after you call mainloop.
Laying out the widgets
The problem is this line:
chiplabel = Label( root, relief=RIDGE, width = 9 , text ="Unknown", padx=0, pady=0).grid(row = 0,column=5, sticky =W)
In python, when you do x=y().z(), x is given the value of z(). So, when you do chiplabel = Label(...).grid(...), chiplabel is given the value of grid(...). Grid always returns None, so chiplabel will always be None. Because of this, you can't reconfigure it because you've lost the reference to the widget.
The solution is to create the widget and lay out the widget in two steps.
One way to do this would be to create the UI in a class, e.g.:
import Tkinter as tk # note don't use wildcard imports
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.chiplabel = tk.Label(
self,
padx=0,
pady=0,
relief=tk.RIDGE,
text="Unknown",
width=9,
) # note alphabetical order and consistent spacing
self.chiplabel.grid(
column=5,
row=0,
sticky=tk.W,
) # note grid is separate step
and don't run it in-place, so that you can import the class without running anything. Then your serial.py looks more like:
from gui import GUI
interface = GUI()
interface.chiplabel.config(text="A.3f V")
interface.mainloop()
If you want multiple frames, you could do something like Switching between frames in tkinter menu.

Categories