Tkinter destroying a Toplevel - python

I cannot destroy a Toplevel (Tkinter, python)
In my program
1) in the beginning user presses the button and the toplevel appears
2) inside the toplevel there are some more widgets and one more button
3) when user presses this (second) button, the function (name_of_toplevel.destroy()) start working
4) but then Terminal writes me "NameError: global name 'name_of_toplevel' is not defined"
5) but it really IS defined!
6) buttons are bound with functions with method "bind"
Text of the program:
from Tkinter import *
def Begin(event):
okno.destroy()
def QuitAll(event):
exit(0)
def OpenOkno(event):
#print "<ButtonRelease-1> really works! Horray!"
okno = Toplevel()
okno.title('Question')
okno.geometry('700x300')
Sign = Label(okno,text = 'Quit the program?', font = 'Arial 17')
Sign.grid(row = 2, column = 3)
OK = Button(okno, text = 'YES', bg = 'yellow', fg = 'blue', font = 'Arial 17')
OK.grid(row = 4, column = 2)
OK.bind("<ButtonRelease-1>",QuitAll)
NO = Button(okno, text = 'NO', bg = 'yellow', fg = 'blue', font = 'Arial 17')
NO.grid(row = 4, column = 4)
NO.bind("<ButtonRelease-1>",Begin)
root = Tk() # main window 'program_on_Python'
root.title('Program_on_Python')
root.geometry('400x600')
knpk = Button(root, text = 'click here!', width = 30, height = 5, bg = 'yellow', fg = 'blue', font = 'Arial 17')
knpk.grid(row = 2, column = 2)
knpk.bind("<ButtonRelease-1>",OpenOkno)
root.mainloop()
please, help me, if you can

okno doesn't exist outside of the OpenOkno function, so attempting to access it anywhere else will cause a NameError. One way to address this is to move Begin inside OpenOkno, where the okno object is visible.
def OpenOkno(event):
def Begin(event):
okno.destroy()
#print "<ButtonRelease-1> really works! Horray!"
okno = Toplevel()
#etc... Put rest of function here
You could also use a lambda expression in place of a full function, as an argument to Bind.
NO.bind("<ButtonRelease-1>", lambda event: okno.destroy())
You could also make okno a global variable, so it will be visible everywhere. You would then need to use the global okno statement anywhere you need to assign to okno.
okno = None
def QuitAll(event):
exit(0)
def Begin(event):
okno.destroy()
def OpenOkno(event):
#print "<ButtonRelease-1> really works! Horray!"
global okno
#etc... Put rest of function here

Related

Python Tkinter: object has no attribute tk

I am new to tkinter, python, and programming in general. I have made an example program of what I'm trying to do. I am trying to use tkinter GUI to receive user inputs for date and time, then convert these tk entries into strings, then check the format of the date and time strings, then if the format is good add the date and time to a list. My issue is with converting the tk entries into strings. When I try to do so I receive an error that says "Example object has no attribute tk". In my program, I have a tk window that is made in my UserInputWindow function, and I pass this window to PromptDateTime, which is where the user is prompted to enter a date and time. When I try to convert using "dateFromUser = tk.Entry(self)", this is the part that receives the error. I don't understand why the PromptDateTime function had no problem editing the window from UserInputWindow function, yet when tk is directly referenced there is an issue.
Also: I had some trouble with formatting my code below (new to stack overflow) so please note that the first section of code is part of "class Example()", and the second section of code is the main function.
Thank you for your help! Please be nice! I'm a newbie and open to critiques.
class Example():
#data members
__dateEntry = None
__timeEntry = None
exampleList = []
def UserInputWindow(self, windowName, instruction):
#Create new window to display fields and options
new_window = tk.Tk()
new_window.title(f'{windowName}')
new_window.geometry = ("500x500")
#Label to display instructions
label_instruction = Label(new_window, text = (f'{instruction}'), font = ("Courier", 10), justify = LEFT, fg = "black", bg = "light yellow")
label_instruction.grid(row = 0, column = 0)
return new_window
#this function checks to see if date string from user is in proper format, and if it is not an error window appears.
def VerifyDate(self, d):
#code deleted for simplicty for this example
#this function checks to see if time string from user is in proper format, and if it is not an error window appears.
def VerifyTime(self, t):
#code deleted for simplicty for this example
#this function prompts user for date and time
def PromptDateTime(self, new_window):
#Label to display instructions
label_instruction = Label(new_window, text = "Enter activity date and time: ",font = ("Courier", 10), justify = LEFT, fg = "black", bg = "light yellow")
label_instruction.grid(row = 0, column = 0)
#Create labels and entries for date and time
label_date = Label(new_window, text = "Enter date in MM/DD/YYYY format: ",fg = "black", bg = "white")
label_date.grid(row = 1, column = 0, padx = 5)
dateEntry = Entry(new_window, fg = 'black', bg = 'white', width = 10)
dateEntry.grid(row = 2, column = 0, padx = 5)
dateFromUser = tk.Entry(self)
str(dateFromUser)
label_time = Label(new_window, text = "Enter time in hh:mm format (military time): ",fg = "black", bg = "white")
label_time.grid(row = 3, column = 0, padx = 5)
timeEntry = Entry(new_window, fg = 'black', bg = 'white', width = 10)
timeEntry.grid(row = 4, column = 0, padx = 5)
self.VerifyDate(dateFromUser)
self.VerifyTime(timeEntry)
def SubmitButton(self, new_window, new_command):
button_submit = Button(new_window, fg = "black", bg = "light blue", text = "Submit", command = new_command)
button_submit.grid(row = 17, column = 10, pady = 5)
def PromptAndAddToList(self):
window = self.UserInputWindow('Date and Time', 'Enter date and time as specified below.')
self.PromptDateTime(window)
self.SubmitButton(window, lambda:exampleList.append(otherClass(dateEntry, timeEntry)))
#################################################
if __name__ == '__main__':
from tkinter import *
import tkinter as tk
import datetime
ex = Example()
ex.PromptAndAddToList()
root = tk.Tk()
root.withdraw()
root.mainloop()
As the error said, the parent of dateFromUser is Example:
dateFromUser = tk.Entry(self) # self is instance of class Example
but Example is not a tkinter widget.
Use new_window instead of self:
dateFromUser = tk.Entry(new_window)

Tkinter need a Button to change background color when pointer on and pointer off

I have a Python Tkinter Windows program with many buttons. I need a button to change its background color forth and back when the pointer is on it and off it. This issue has been dicussed here before, and I tried to use the code snippets given to solve my problem but did not succeed. The best solution for me would be such that the method would be on such a level that it is needed only once. In my program the user can define the background color for the buttons, however similar to all, and the pointer-on color should be able to be affected by the choice.
Below a minimal code where I have tried to use bind. randint simulates user choice. But, as said, it does not work. What changes do I require? I am new with Python and Tkinter, so please give your answer as clear changes to the code below.
import tkinter as tk
from random import randint
class PointerOnOff:
def __init__ (self, root):
root.geometry ("200x140+100+100")
color = randint (0, 2)
if color == 0:
off_color = "#aaffaa"
on_color = "#99ff99"
elif color == 1:
off_color = "#ffffaa"
on_color = "#ffff99"
else:
off_color = "#ffaaaa"
on_color = "#ff9999"
self.OK = tk.Button (root, text = "OK", bg = off_color, command = self.OKPush)
self.OK.place (x = 50, y = 20, width = 100, height = 30)
self.Cancel = tk.Button (root, text = "Cancel", bg = off_color, command = self.CancelPush)
self.Cancel.place (x = 50, y = 60, width = 100, height = 30)
self.PushedButton = tk.Label (root, text = "")
self.PushedButton.place (x = 20, y = 100, width = 160, height = 30)
def on_enter (anybutton):
anybutton.widget.config (bg = on_color)
def on_leave (anybutton):
anybutton.widget.config (bg = off_color)
self.OK.bind("<Enter>", on_enter)
self.OK.bind("<Leave>", on_leave)
self.Cancel.bind("<Enter>", on_enter)
self.Cancel.bind("<Leave>", on_leave)
def OKPush (self):
self.PushedButton.config (text = "You pushed OK button")
def CancelPush (self):
self.PushedButton.config (text = "You pushed Cancel button")
root = tk.Tk ()
master = PointerOnOff (root)
root.mainloop ()
I'm python beginner and I'm not sure of my answer but the code for changing the background color is this in my opinion:
from tkinter import *
from random import randint
t = Tk()
t.geometry('200x200')
def change_bg():
color = ("#" + str(randint(100000, 999999)))
f = Frame(t, bg=color)
f.place(x=0, y=0, width=200, height=200)
The issue is due to incorrect indentation of the two functions: on_enter() and on_leave(). They need to be inner functions inside __init__():
class PointerOnOff:
def __init__ (self, root):
...
self.PushedButton = tk.Label (root, text = "")
self.PushedButton.place (x = 20, y = 100, width = 160, height = 30)
def on_enter (anybutton):
anybutton.widget.config (bg = on_color)
def on_leave (anybutton):
anybutton.widget.config (bg = off_color)
self.OK.bind("<Enter>", on_enter)
self.OK.bind("<Leave>", on_leave)
self.Cancel.bind("<Enter>", on_enter)
self.Cancel.bind("<Leave>", on_leave)
If you don't want to call the two bindings for every button, you better create a custom button class to embed the hover feature:
class HoverButton(tk.Button):
_colors = [
# off # on
('#aaffaa', '#99ff99'),
('#ffffaa', '#ffff99'),
('#ffaaaa', '#ff9999'),
]
def __init__(self, master=None, *args, **kw):
# if "colors" option not provided, use random choice from internal colors
self._off_color, self._on_color = kw.pop("colors", self._colors[randint(0, 2)])
super().__init__(master, *args, **kw)
self["bg"] = self._off_color
self.bind("<Enter>", lambda e: self.config(bg=self._on_color))
self.bind("<Leave>", lambda e: self.config(bg=self._off_color))
Then use this custom button class for those buttons you want to have hover effect:
def class PointOnOff:
def __init___(self, root):
...
self.OK = HoverButton(root, text="OK", colors=("orange", "gold"), command=self.OKPush)
self.OK.place(x=50, y=20, width=100, height=30)
self.Cancel = HoverButton(root, text="Cancel", command=self.CancelPush)
self.Cancel.place(x=50, y=60, width=100, height=30)
...

How do I make a tkinter button destroy itself?

I made this program in Tkinter in python where a small window pops up when the code is run and a start button would pop up and make the window full screen and show the content after. I want to make the button destroy itself after I press it so it makes a fullscreen and removes the button. I am still a beginner and would like the answer to be simple. The solution I am looking for is to maybe destroy the button completely(preferred) or move it way out of sight in the fullscreen window. Here is the code:
import Tkinter as w
from Tkinter import *
w = Tk()
w.geometry("150x50+680+350")
def w1():
w.attributes("-fullscreen", True)
l1 = Label(w, text = "Loaded!", height = 6, width = 8).pack()
global b1
b1.place(x = -10000, y = -10000)
b1 = Button(w, text = "Start", height = 3, width = 20, command = w1).place(x = 0, y = 10)
b2 = Button(w, text = "Exit", command = w.destroy).place(x = 1506, y = 0)
w.mainloop()
As you can see I want to make button one destroy itself.
Try this:
import tkinter as tk # Use `import Tkinter as tk` for Python 2
root = tk.Tk()
root.geometry("150x50+680+350")
def function():
global button_start
root.attributes("-fullscreen", True)
label = tk.Label(root, text="Loaded!", height=6, width=8)
label.pack()
button_start.place_forget() # You can also use `button_start.destroy()`
button_start = tk.Button(root, text="Start", height=3, width=20, command=function)
button_start.place(x = 0, y = 10)
button_exit = tk.Button(root, text="Exit", command=root.destroy)
button_exit.place(x=1506, y=0)
root.mainloop()
PS: Please read this.
Try:
b1.place_forget()
This will essentially "forget" about the button and hide it from view.
Edit:
If you are getting the error that b1 is None try:
b1 = Button(w, text = "Start", height = 3, width = 20, command = w1)
b1.place(x = 0, y = 10)
You need to add the b1.place() option at the bottom for this to work

How to save a data after closing a window?

I'm trying to make some GUI on Python3 with tkinter. So far I have Main Window and 'Test' button on it, which opens second window. Second window has entry, label, save and close buttons. When you type something in entry and press save button, label shows the text you typed in entry. But after closing this window and opening it again, label shows nothing. How do I make this label to show the text that were typed last time before closing? For example, I type 'Hi' in entry, press 'Save', then I press 'Close', then I open this window again and label shows 'Hi'
import tkinter as tk
def save_data(entry, t):
t.config(text = entry.get())
def close_action(current_window):
current_window.destroy()
def insertMainInfo():
new_window = tk.Tk()
new_window.geometry("307x131")
new_window.title("TestWindow")
test_entry = tk.Entry(new_window)
test_entry.place(relx = 0.283, rely = 0.1, height = 24, width = 127)
text = tk.Label(new_window)
text.place(relx = 0.283, rely = 0.25, height = 24, width = 127)
save_button = tk.Button(new_window, command = lambda: save_data(test_entry, text))
save_button.place(relx=0.283, rely=0.45, height=24, width=127)
save_button.configure(text = "Save")
close = tk.Button(new_window, command = lambda: close_action(new_window))
close.place(relx=0.283, rely=0.687, height=24, width=127)
close.configure(text = "Close")
new_window.mainloop()
if __name__ == '__main__':
top = tk.Tk()
top.geometry("307x131+557+330")
top.resizable(width=False, height=False)
top.title("MainWindow")
new_window_button = tk.Button(top, command = insertMainInfo)
new_window_button.place(relx=0.283, rely=0.687, height=24, width=127)
new_window_button.configure(text = "Test")
main_label = tk.Label(top)
main_label.place(relx=0.033, rely=0.153, height=41, width=284)
main_label.configure(text = "TestLabel")
top.mainloop()
I have to confess that your question heading is a bit ambiguity.
If you just want to update a label of last entry, here a simple way of modifying your code.
As advised, as a good practice we only have one Tk() in a program, other new windows or pop-up windows should use Toplevel() of tkinter class; So I use this in your insertMainInfo() function.
The point here is to define a variable, I called last_entry and initially is empty or ‘’. Use this as parameter in new_window button in main program (after if __name__ == '__main__': ) to this variable (I also add lambda function here).
Then we define it as global in save_data function, so as it can be known later by other functions or main program as the last entry before new_window is closed.
Here I modify your code as said above, and I have tested it, and it works as expected.
import tkinter as tk
def save_data(entry, t):
global last_entry
last_entry = entry.get()
t.config(text = last_entry)
def close_action(current_window):
current_window.destroy()
def insertMainInfo(last_entry):
new_window = tk.Toplevel()
new_window.geometry("307x131")
new_window.title("TestWindow")
test_entry = tk.Entry(new_window)
test_entry.place(relx = 0.283, rely = 0.1, height = 24, width = 127)
text = tk.Label(new_window, text=last_entry)
text.place(relx = 0.283, rely = 0.25, height = 24, width = 127)
save_button = tk.Button(new_window, command = lambda: save_data(test_entry, text))
save_button.place(relx=0.283, rely=0.45, height=24, width=127)
save_button.configure(text = "Save")
close = tk.Button(new_window, command = lambda: close_action(new_window))
close.place(relx=0.283, rely=0.687, height=24, width=127)
close.configure(text = "Close")
new_window.mainloop()
# --- A Simple Data Structure ---
last_entry = ''
if __name__ == '__main__':
top = tk.Tk()
top.geometry("307x131+557+330")
top.resizable(width=False, height=False)
top.title("MainWindow")
new_window_button = tk.Button(top, command = lambda: insertMainInfo(last_entry))
new_window_button.place(relx=0.283, rely=0.687, height=24, width=127)
new_window_button.configure(text = "Test")
main_label = tk.Label(top)
main_label.place(relx=0.033, rely=0.153, height=41, width=284)
main_label.configure(text = "TestLabel")
top.mainloop()

Python 3.4 GUI if statement with buttons

I'm trying to learn coding with python 3.4. I built this mini GUI and I'm having trouble getting it to work. It prints out 2 every time even though I press the button 1 . All I want to do is to get 1 printed out in the label when I click the button on and 2 with button two with an if statement.
from tkinter import *
root = Tk()
root.geometry("500x500")
root.title("Test")
def button_press():
if one:
x = 1
display.configure(text=x)
if two:
x = 2
display.configure(text=x)
display = LabelFrame(root, bg="red", width="462", height="60")
display.pack()
one = Button(root, text="1", width="15", height="5",command=button_press)
one.pack(side="left")
two = Button(root, text="2", width="15", height="5", command=button_press)
two.pack(side="left")
root.mainloop()
You have two ways:
Or using a different function for each button
Or pass a lambda with one parameter.
bt … command = fct1
bt … command = fct2
Or use a lambda…
from tkinter import *
root = Tk()
root.geometry("500x500")
root.title("Test")
def button_press(var):
display.configure(text=var)
display = LabelFrame(root, bg="red", width="462", height="60")
display.pack()
one = Button(root, text="1", width="15", height="5",command=lambda : button_press(1))
one.pack(side="left")
two = Button(root, text="2", width="15", height="5", command=lambda : button_press(2))
two.pack(side="left")
root.mainloop()
I haven't done any Tkinter to be honest. That said the code snippet below
if one:
x = 1
display.configure(text=x)
if two:
x = 2
display.configure(text=x)
looks like a weird thing to do. Basically you are checking if one and than two are equal or not to None. This doesn't make sense since an if VARIABLE is always True for VARIABLE that is unequal 0, False or None.
Well, they are most certainly not equal to None, False or 0 since both variables represent an instance of a Button. So what you are doing is:
Check if one != None
Set label if 1. is True - this happens and you set the label to "1" but then...
Check if two != None
Set label if 3. is True - this also happens and it also overwrites the previous value of the label set by 2 which was "1" to "2". All this happens instantly so you are unable to see the transition and are left with the impression that only "2" is displayed.
We can go further and rewrite your code in order to expose this error:
def button_press():
if one:
x = 1
display.configure(text=x)
elif two:
x = 2
display.configure(text=x)
or even
def button_press():
if one:
x = 1
display.configure(text=x)
else:
x = 2
display.configure(text=x)
In both of the above cases the first statement will be executed and you will get 1 all the time and never 2. Once you check for the first logical expression in your if-else block and it is True, you go in there and then leave the whole block not touching the elif or else at all.
There are various ways how to fix this. You can assign a name to the button (so that you can distinguish each button based on its name) in case you still want to handle both buttons with a single function and then do a comparison whether the name of the object that called your event handling function is == "one" or == "two". However in most (all?) UI frameworks it is a common practice to have a separate function for each UI component that handles an event that the component supports (multiple functions if the component supports multiple event).
one = Button(root, text="1", width="15", height="5",command=button_press1)
one.pack(side="left")
two = Button(root, text="1", width="15", height="5",command=button_press2)
two.pack(side="left")
This solution is similar to #Clodion's but without the lambdas since I'm not sure if you are familiar with the concept and throwing that in might be a bit confusing.
I would suggest following some basic principles when working with an UI. If your component has support for - let's say - value changes, resized, clicked, pressed down, pressed up etc. (don't know the name of these in Tkinter but I would guess it supports at at least some of them) - you will create a separate function for each event. This not only makes your code more readable but also forces you to learn a way of doing things that is de facto standard no matter if you write your UI with Tkinter, Qt, .NET, Swing, AngularJS and so on.
This is what you want. May be new syntax but its the kinda thing you will get used to seeing. Dont be daunted.
from tkinter import *
root = Tk()
class BuildGui():
def __init__(self, parent):
self.parent = parent
self.parent.geometry("500x500")
self.parent.title("Building a Gui")
self.parent.configure(background="steel blue")
self.parent.resizable(0,0)
self.parent.grid()
self.CreateFrames()
self.AddButtons()
def CreateFrames(self):
"""Create The Frames"""
self.Frm = Frame(self.parent, bg = "steel blue")
self.Frm.grid(row = 0, column = 0)
def AddButtons(self):
self.Btn1 = Button(self.Frm, text = "Button1", height = 2, width = 10, fg = "black", activebackground = "yellow", bg = "light blue", command = self.Button1Pressed)
self.Btn2 = Button(self.Frm, text = "Button2", height = 2, width = 10, fg = "black", activebackground = "yellow", bg = "light blue", command = self.Button2Pressed)
self.Btn1.grid(row = 0, column = 0, padx = 40, pady = 40)
self.Btn2.grid(row = 0, column = 1, padx = 40, pady = 40)
def Button1Pressed(self):
self.Lbl1 = Label(self.Frm, text = "Button1pressed!", font = ("Calibri", 12))
self.Lbl1.grid(row = 1, column = 0, padx = 20, pady = 20)
def Button2Pressed(self):
self.Lbl2 = Label(self.Frm, text = "Button2pressed!", font = ("Calibri", 12))
self.Lbl2.grid(row = 1, column = 1, padx = 20, pady = 20)
buildgui = BuildGui(root)
root.mainloop()

Categories