This is a simple test script I am attempting to write which will help me teach myself about tkinter...
from tkinter import *
def hello():
print("U pressed it lol")
global window1, window2
window2 = None
window1 = None
def setWindow(windowEnter):
global window
window = windowEnter
window = Tk()
window.attributes("-fullscreen", True)
def newScreen(newScreen, screenToDelete):
setWindow(newScreen)
print("New Window Created")
screenToDelete.destroy()
print("He ded lol")
setWindow(window1)
def setStuff():
button = Button(window1, text="hey", command=hello)
label = Label(window1, text="YoYoYo My dude")
button2 = Button(window1, text="Next Page", command = lambda: newScreen(window2, window1))
button.pack()
label.pack()
button2.pack()
setStuff()
When I run this code it returns an error?
File "C:\Users\026341\Desktop\test.py", line 19, in newScreen
screenToDelete.destroy()
AttributeError: 'NoneType' object has no attribute 'destroy'
Why doesn't this work & how do i fix it?
Thanks in advance :)
(Btw I'm using python 3.6)
You set
window2 = None
window1 = None
as global variables and then define the command function for button2 to be
lambda: newScreen(window2, window1)
Which calls newScreen with the values window2 and window1 which are both None, hence the error. The underlying issue here is your setWindow function:
def setWindow(windowEnter):
global window
window = windowEnter
window = Tk()
window.attributes("-fullscreen", True)
which doesn't work the way you are using it. When you call setWindow(window1), you pass the value of window1, what the function does with the variable cannot be seen on a global scope. A quick example would be this:
def increment(a):
a +=1
x = 1
print(x)
increment(x)
print(x)
which will print 1 twice.
To achieve what you want I suggest you use a dictionary to keep track of your windows.
from tkinter import *
def hello():
print("U pressed it lol")
global window1, window2
windows = {}
def setWindow(window_name):
windows[window_name] = Tk()
windows[window_name].attributes("-fullscreen", True)
def newScreen(newScreen_name, screenToDelete_name):
setWindow(newScreen_name)
print("New Window Created")
windows[screenToDelete_name].destroy()
del windows[screenToDelete_name] #delete invalid entry from dict
print("He ded lol")
setWindow("window1")
def setStuff():
button = Button(windows["window1"], text="hey", command=hello)
label = Label(windows["window1"], text="YoYoYo My dude")
button2 = Button(windows["window1"], text="Next Page", command = lambda: newScreen("window2", "window1"))
button.pack()
label.pack()
button2.pack()
setStuff()
Note on the side: previously your function was def newScreen(newScreen, screenToDelete), which is very confusing/bad style since both the function and its first argument share the same name. I changed it anyway to highlight that it now takes strings as arguments, but keep it in mind for the furture.
I'm not able to test it right now, but I've spotted a source of error:
lambda: newScreen(window2, window1)
This creates a lambda function that doesn't take any arguments, hence window2 and window1 will be None, and None doesn't have a destroy() method, hence the error. Instead try:
lambda window2, window1: newScreen(window2, window1)
Related
I'm using Tkinter to sometimes show a dialog in my application. I destroy and quit Tkinter (the toplevel widget) after the dialog got closed, since I don't know if another dialog will be opened later. However, if I call open_simple_value_dialog after open_file_dialog, open_simple_value_dialog does show an empty input field instead of the default value. If the value is changed and confirmed (click on Ok button), the result returned by the dialog is still the default value. open_simple_value_dialog does work as expected (shows default value and returns entered value) if open_file_dialog is not called before it. How to fix this issue?
def open_file_dialog():
from tkinter import Tk, filedialog
root = Tk()
root.withdraw()
file_name = filedialog.askopenfilename(title='Select input file')
root.quit()
return file_name
def open_simple_value_dialog(default_input_value):
from tkinter import Tk, IntVar, StringVar, Label, Entry, Button
root = Tk()
if isinstance(default_input_value, str):
input_variable = StringVar()
elif isinstance(default_input_value, int):
input_variable = IntVar()
else:
raise ValueError('expected string or integer as default input value')
input_variable.set(default_input_value)
result = None
def close_dialog():
nonlocal root
root.destroy()
root.quit()
def on_ok():
nonlocal result
result = input_variable.get()
close_dialog()
Label(root, text='enter something').pack()
Entry(root, textvariable=input_variable).pack()
Button(root, text='Ok', command=on_ok).pack()
Button(root, text='Cancel', command=close_dialog).pack()
root.mainloop()
return result
def main():
print(open_file_dialog())
print(open_simple_value_dialog('text'))
value = open_simple_value_dialog(0)
if isinstance(value, int):
print(value)
else:
print('Dialog has been canceled')
if __name__ == '__main__':
main()
Since you use root.quit() inside open_file_dialog(), it just terminates the tkinter .mainloop(), but not the underlying TCL interpreter and tkinter widgets (see explanation in this question). So there is still issue on multiple instances of Tk() when open_simple_value_dialog() is executed.
You need to use root.destroy() inside open_file_dialog() instead.
I've searched everywhere on the web but unfortunately no where did
I find an answer to this question:
after setting a tkinter Entry() widget's textvariable to a textvariable.
the text variable does not update after I have typed text into the entry.
code below:
def saveFileName(self):
if(self.save_file_name != ""):
self.window.destroy()
self.name_not_set = False
print("saving...")
else:
print("notsaving...entry empty")
pass
def cancel(self):
self.name_not_set = False
self.exit = True
self.window.destroy()
print("exiting...")
def askForFilename(self):
self.window = tk.Tk()
self.window.wm_title("enter a file name")
label = Label(self.window,text="please enter a file name:").pack(side="top")
entry = Entry(self.window,textvariable=self.save_file_name).pack()
save = Button(self.window,text="save",command=self.saveFileName).pack()
cancel = Button(self.window,text="cancel",command=self.cancel).pack()
self.window.mainloop()
The necessary variables have been defined and these methods are part
of a class which is a tk.TK() instance.
this problem is very bothersome
:( very sad :(
Thank you and merry christmas in advance!
A textvariable associated with an Entry should be a StringVar(). I don't se any such declaration in your code.
self.save_file_name = StringVar()
To set and get the value of a StringVar() you must use the set() or get() method, eg.
def saveFileName(self):
if(self.save_file_name.get() != ""):
self.window.destroy()
# etc, etc.
Also, don't create more than one instance of Tk() as in:
def askForFilename(self):
self.window = tk.Tk()
Use Toplevel() instead. Or even better: use the tkinter filedialogs.
I need to be able to change the button position when I click on it. The position changes by a random amount each time I click on it. But all i get is an error. Here's my code:
from tkinter import *
from random import randrange
class Window(Frame):
def position(self):
return randrange(0,400),randrange(0,300)
def __init__(self,master=None):
Frame.__init__(self,master)
self.master = master
self.__init__window()
def __init__window(self):
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
Button1 = Button(self, text="Click me if you can",command=self.Message)
Button1.place(*position())
menu=Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
file.add_command(label="Exit", command=self.client_exit)
menu.add_cascade(label="File",menu=file)
edit = Menu(menu)
edit.add_command(label="Show text", command=self.showText)
menu.add_cascade(label="Edit", menu=edit)
def Message(self):
print("Hello world")
def showText(self):
text = Label(self, text="Hey there!")
text.pack()
def client_exit(self):
exit()
root = Tk()
root.geometry("400x300")
app = Window(root)
root.mainloop()
"Button1.place" is the position of the button that needs to be changed but I am completely clueless how else to do this. I used variables as well.
It seems place() wants keyword arguments. You can let the function position() return a dict and unpack it in the place() statement:
def position(self):
return {'x':randrange(0,400),'y':randrange(0,300)}
Placing the button with:
self.Button1.place(**self.position())
You also need to prefix the button name with "self" to be able to access it from outside the function __init__window().
Then simply add a copy of the place() statement in the button callback function:
def Message(self):
print("Hello world")
self.Button1.place(**self.position())
That works fine for me at least (Python 3.6.5 under win10).
You'll have to reduce the random values generated for x and y or parts of the button will occationally be outside the window...
I want a button in my window to open a new window and close the previous one. Is it possible to have one button do both of these? I've tried in the following code, but it hasn't worked, just told me that window is not defined:
import tkinter
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = window2).pack()
window.mainloop()
def window2():
window.destroy() #This is where the error is
menu = tkinter.Tk()
etc, etc, etc
window1()
First, you need to return the window object from the first function:
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = lambda: window2(window)).pack()
window.mainloop()
return window
Then, you need to pass the window as an argument to your function:
def window2(window):
window.destroy()
menu = tkinter.Tk()
And then call window1 with:
window = window1()
and click the button to destroy it and do the rest
This is an example using Toplevels, which is usually a better choice than creating, destroying, re-creating Tk() instances. The unique Toplevel ID is passed to the close_it function using partial(). You would, of course, combine them or have the close function call the open function.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
class OpenToplevels():
""" open and close additional Toplevels with a button
"""
def __init__(self):
self.root = tk.Tk()
self.button_ctr=0
but=tk.Button(self.root, text="Open a Toplevel",
command=self.open_another)
but.grid(row=0, column=0)
tk.Button(self.root, text="Exit Tkinter", bg="red",
command=self.root.quit).grid(row=1, column=0, sticky="we")
self.root.mainloop()
def close_it(self, id):
id.destroy()
def open_another(self):
self.button_ctr += 1
id = tk.Toplevel(self.root)
id.title("Toplevel #%d" % (self.button_ctr))
tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
command=partial(self.close_it, id),
bg="orange", width=20).grid(row=1, column=0)
Ot=OpenToplevels()
Yes. Is possible. But you'll need to def that:
def window1:
blablabla
blablabla
def window2:
window2.destroy() <-- Here where the error was
How you noticed, put your name of window what you want Destroy and it will work!
using Python3
You could use a "global" such as:
root = Tk()
root.title('This is the root window')
def window_create():
global window_one
window_one = Tk()
window_one.title('This is window 1')
Then, from any function (or elsewhere) when you want to destroy window_one, do:
def window_destroyer():
window_one.destroy()
You could call your window_destroyer function from a button anywhere such as root which the example shows:
kill_window_btn = Button(root, text="Destroy", command=window_destroyer).pack()
Of course, follow your own naming conventions. :)
It seems to me, just 'global window_one' would solve it.
When I press the button, I want it to get the Entry and -for future things- use it in another function.
import tkinter
def ButtonAction():
MyEntry = ent.get() #this is the variable I wanna use in another function
den = tkinter.Tk()
den.title("Widget Example")
lbl = tkinter.Label(den, text="Write Something")
ent = tkinter.Entry(den)
btn = tkinter.Button(den, text="Get That Something", command = ButtonAction )
lbl.pack()
ent.pack()
btn.pack()
den.mainloop()
print MyEntry #something like this maybe. That's for just example
I will use this thing as a search tool. Entry window will appear, get that "entry" from there and search it in files like:
if MyEntry in files:
#do smth
I know I can handle the problem with using globals but from what I've read it's not recommended as a first solution.
Structure the program using class.
import tkinter
class Prompt:
def button_action(self):
self.my_entry = self.ent.get() #this is the variable I wanna use in another function
def __init__(self, den):
self.lbl = tkinter.Label(den, text="Write Something")
self.ent = tkinter.Entry(den)
self.btn = tkinter.Button(den, text="Get That Something", command=self.button_action)
self.lbl.pack()
self.ent.pack()
self.btn.pack()
den = tkinter.Tk()
den.title("Widget Example")
prompt = Prompt(den)
den.mainloop()
You can access the input using prompt.my_entry later.