#AssessmentGUI
from Tkinter import *
window=Tk()
window.title('Troubleshooting')
def start():
wet()
def wet():
global wetlabel
wetlabel=Label(window, text="Has the phone got wet? Y/N")
wetsubmit()
def wetsubmit():
wetlabel.pack()
wetanswer=(entry.get())
if wetanswer=="Y":
print"ADD SOLUTION"
else:
dropped()
def dropped():
global droppedlabel
dropwindow.title('Troubleshooting')
dropwindow.mainloop()
droplabel=Label(dropwindow, text="Has the phone been dropped? Y/N")
droplabel.pack()
dropButton.pack()
dropsubmit()
def dropsubmit():
print "test"
window.geometry("300x100")
global wetsubmit
Button=Button(window, text="Submit Answer", activebackground="Green",command= wetsubmit , width=100)
dropwindow=Tk()
dropButton=Button(dropwindow, text="Submit Answer", activebackground="Green",command= dropsubmit , width=100)
entry=Entry(window, text="Test", width=100)
start()
entry.pack()
Button.pack()
window.mainloop()
Above is my code which isn't working due to the error. Basically what I want to happen is that each window opens another window after it for the next question on the troubleshooting program! If anyone has the task it would be nice if youy could suggest a better method if mine is unfixable.
The error message says:
Traceback (most recent call last):
File "H:\GCSE\Computing\GUI.py", line 36, in <module>
dropButton=Button(dropwindow, text="Submit Answer", activebackground="Green",command= dropsubmit , width=100)
AttributeError: Button instance has no __call__ method*
This is after a little bit of tweaking to the original code but I cannot fix this problem!
You have a class, named Button,and then you create a variable named Button. You have now destroyed the class, so the next time you try to create a button, you are calling your variable instead.
Lesson: don't use variable names that are the same as classes.
Related
So today I tried to use python classes for the first time, to remove the excessive use of global keyword. I am trying to create a tkinter window in which, when we click one button it removes the clicked button and replaces it with a new button. And when we click it again, it removes this button and replaces the old (first) button and this should cycle through out...
This is my code which I made:
# ================= Importing Modules ===================
from tkinter import *
import tkinter as tk
# ====================================================
class Test():
# ============= Play Button Click =============
def fun1(self):
self.hi.destroy()
self.he.place(x=350,y=340)
# ============ Pause Button Click =============
def fun2(self):
self.he.destroy()
self.hi.place(x=350,y=340)
# ============ Player Window ================
def __init__(self):
self.root = Tk()
self.root.geometry('700x400')
self.root.resizable(0,0)
self.root["bg"] = "black"
self.hi = tk.Button(self.root, text="button 1", bg="white", bd=0, command=lambda: self.fun1() , relief=RIDGE)
self.hi.place(x=350,y=340)
self.he = tk.Button(self.root, text="button 2", bg="white", bd=0, command=lambda: self.fun2() , relief=RIDGE)
self.root.mainloop()
# ============== Calling ===========
if __name__ == '__main__':
Test()
But Instead of the desired output, sadly, I got this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "C:/XXX/XXX/Desktop/test.py", line 29, in <lambda>
self.he = tk.Button(self.root, text="button 2", bg="white", bd=0, command=lambda: self.fun2() , relief=RIDGE)
File "C:/XXX/XXX/Desktop/test.py", line 16, in fun2
self.hi.place(x=350,y=340)
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 2477, in place_configure
self.tk.call(
_tkinter.TclError: bad window path name ".!button"
SO MY QUESTION IS:
doubt1 = Any idea what I am doing wrong?
doubt2 = Or isn't this possible?
if doubt1 or doubt2:
Please explain it...
elif:
Please tell me a better alternative or idea, how to do this efficiently.
else:
Note: I have researched so many questions. Nothing helped me out. Especially ---|
↓
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤthis one.
You're destroying self.hi and then later trying to call place on the destroyed button. Once a widget has been destroyed you can no longer use it.
If you want to keep cycling the buttons, don't destroy them. Since you are using place, you can use self.hi.place_forget() and self.he.place_forget() to remove the buttons from view without destroying them.
I would love some help, thanks!
I am trying to use lambdas to assign functionality to window events. It already worked for assigning the "enter" button to a function.
But, for some reason, it doesn't work for the default exit button on the window.
As you can see in the create_entry_window function, I used a lambda twice, and it worked only for the "Return".
The problem occurs with this line:
root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root)))
Here is the code:
from tkinter import Tk, Frame, BOTTOM, Entry, Text, END
def clear_and_get_entry(root, entry):
"""
"""
global entered_text
entry_text = entry.get()
entry.delete(0, END)
entry.insert(0, "")
entered_text = entry_text
root.destroy()
def exit_entry(root):
"""
"""
global entered_text
entered_text = False
print "here at exit"
root.destroy()
def create_entry_window(text):
"""
"""
root = Tk()
root.title("One picture is worth a thousand sounds!")
root.geometry("500x200")
root.resizable(width=False, height=False)
bottom_frame = Frame(root)
bottom_frame.pack(side=BOTTOM)
entry = Entry(root)
entry.pack(side=BOTTOM)
description_text = Text(root, height=50, width=100)
description_text.insert(END, text)
description_text.tag_configure("center", justify='center')
description_text.tag_add("center", "1.0", "end")
description_text.pack()
root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root)))
entry.bind("<Return>", (lambda event: clear_and_get_entry(root, entry)))
return root
if __name__ == '__main__':
root = create_entry_window("Some text")
root.mainloop()
When trying to exit the window, I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Heights\PortableApps\PortablePython2.7.6.1\App\lib\lib-tk\Tkinter.py", line 1470, in __call__
return self.func(*args)
TypeError: <lambda>() takes exactly 1 argument (0 given)
The quick-fix to your problem is quite easy:
Change the line reading root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root))) into
root.protocol("WM_DELETE_WINDOW", lambda: exit_entry(root))
The root.protocol does not provide an argument when triggered. But your lambda expects one. Removing the argument event fixes the problem for me.
Please note that there are some other issues with your code.
You are still using Python 2. Unless there are very compelling reasons to do so, I suggest you move forward to Python 3. I had not problems running your code with Python 3. All I needed to do was to add brackets to the print statement:
print "here at exit" -> print("here at exit")
Secondly: You are using global in order for your functions to communicate with each other. This is considered bad-practice. It leads to confusing code that is very hard to debug. I suggest that you have a closer look at some tkinter examples and how they are using an object oriented approach to deal with this issue. A possible starting point could be Introduction_to_GUI_Programming. The Calculator class looks like a good starting-point to me.
I'm a beginner in python and I'm creating an interface where I can click on a button to open a new window and then fill a form.
My problem here is about to get the value of the entry after the customer pressed the button print.
First as you can see I created a window.
This is example of what i want to do.
def return_nom(*args):
return ent1.get()
def save():
print("your name is", return_nom())
def new_win():
top = Toplevel(fen)
top.title("new window")
strvar = StringVar()
strvar.trace("w", return_nom)
ent1 = Entry(top, textvariable=strvar)
bouton1 = Button(top, text='print', command=save)
bouton1.pack()
ent1.pack()
top.mainloop()
fen = Tk()
lab = Label(fen)
lab.pack()
bouton = Button(fen, text='new window', command=new_win)
bouton.pack()
fen.mainloop()
If someone can tell me why it doesn't work and explain me why this technique works when I use this it for an entry on the main interface.
Thanks everybody ! ;)
The main issue you have here is scope, since you're trying to access the Entry and StringVar from functions that don't have access to it.
The name ent1 defined inside new_win() will not be accessible from return_nom().
Furthermore, it seems to me that what you actually want to query is the StringVar and not the Entry widget itself.
How you can pass the StringVar to the called functions, there are several ways:
Make the strvar a global variable, that way the other functions will have access to it. This is probably the worst possible solution to this problem, since the point of having functions is to avoid namespace pollution of a global namespace.
Make the return_nom() and save() functions inner functions of new_win(), which allows them to access the local variables in new_win() as a closure. This is slightly better.
Use an object-oriented interface, where your StringVar is an instance member and your save() is a method. State such as the StringVar is available to the whole instance. This is probably the best here, it's how Tkinter is actually intended to be used, it most naturally fits an object-oriented approach.
An example of using inner functions would be:
def new_win():
top = Toplevel(fen)
top.title("new window")
strvar = StringVar()
def return_nom(*args):
return strvar.get()
def save():
print("your name is", return_nom())
ent1 = Entry(top, textvariable=strvar)
bouton1 = Button(top, text='print', command=save)
bouton1.pack()
ent1.pack()
top.mainloop()
An example of an object-oriented approach would be:
class MyDialog:
def __init__(self, fen):
self.fen = fen
self.strvar = StringVar()
def return_nom(self, *args):
return self.strvar.get()
def save():
print("your name is", self.return_nom())
def new_win(self):
top = Toplevel(self.fen)
top.title("new window")
ent1 = Entry(top, textvariable=self.strvar)
bouton1 = Button(top, text='print', command=self.save)
bouton1.pack()
ent1.pack()
top.mainloop()
And then at the top-level:
my_dialog = MyDialog(fen)
bouton = Button(fen, text='new window', command=my_dialog.new_win)
But the above is still not the best approach, which would actually be to create subclasses of Tkinter.Frame for your windows, connect them together through methods and have your top-level code only instantiate a main Application class and call app.mainloop() on it, letting it drive the whole application flow through events connected to methods and other frames.
See a simple Hello World program in the Python tkinter documentation on the Python standard library to get a somewhat better idea of how tkinter is intended to be used.
That documentation also has pointers to many other resources on tkinter and on Tk itself that you can follow to get more in-depth knowledge of that toolkit library.
Check what the traceback is telling you when you start typing:
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "tktest.py", line 3, in return_nom
return ent1.get()
NameError: name 'ent1' is not defined
When you define a variable in a function, it's not automatically defined in other functions. That's called "scope of a variable". Either you define ent1 in the new_win() function with the global keyword or you make it a function attribute:
new_win.ent1 = Entry(top, textvariable=strvar)
...
new_win.ent1.pack()
and call it like that:
def return_nom(*args):
return new_win.ent1.get()
Happy programming!
I downloaded pycharm and I copied some code from a youtube tutorial into it which worked for the person making the video but when i tried running it it didnt work and this is what it said:
C:\Python27\python.exe C:/Python27/Lib/site-packages/wheel/test/test245425232.py
Traceback (most recent call last):
File "C:/Python27/Lib/site-packages/wheel/test/test245425232.py", line 9, in <module>
button1.bind("<button1>", printName)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1098, in bind
return self._bind(('bind', self._w), sequence, func, add)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1053, in _bind
self.tk.call(what + (sequence, cmd))
_tkinter.TclError: bad event type or keysym "button1"
Process finished with exit code 1
Here is the code:
from tkinter import *
root=Tk()
def printName():
print("hi stuff")
button1=Button(root, text="print my name")
button1.bind("<button1>", printName)
button1.pack()
root.mainloop()
It's better with :
button1.bind("<Button-1>", printName)
But you may want to plug your function directly to your button widget, a binding is not necessary here, it can be useful with a label widget for example :
button1=Button(root, text="print my name", command=printName)
("Button-1" is the name of the mouse left click event, not a widget variable name)
Otherwise you need to declare your function printName with a parameter : the event given by your binding.
def printName(event):
print("hi stuff")
button1=Button(root, text="print my name")
button1.bind("<Button-1>", printName)
Like i said, a binding like this could make sense with another widget :
from tkinter import *
root=Tk()
def printName(event):
print("hi stuff")
label1=Label(root, text="print my name")
label1.bind("<Button-1>", printName)
label1.pack()
root.mainloop()
I'm displaying two windows. The first one for saving my game with Entry widget. And the second one which is my game.
When I close the widget with .destroy() function it works. But then as I want to leave the game I do fenetre.destroy() but nothing happens. And I got a message error when I close the window manually:
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed
This is my code:
def game_quit():
global name
if askyesno("Quit game ?","Are you sure? :("):
if askyesno("Save ?","Do you want to save your game? "):
ask_name()
save_scoreG(grid,lenght)
fenetre.destroy()
def ask_name():
global entry, master
master = Toplevel()
master.title("Your Name")
button=Button(master, text='Input your name and click here', command = get_name, bg= "yellow" )
usertext= StringVar()
entry = Entry(master, textvariable=usertext)
entry.pack()
button.pack()
master.mainloop()
def get_name():
global name, entry, master
name = str(entry.get())
master.destroy()
def save_scoreG(grid,lenght):
global name
with open('score','a') as s:
s.write(str(lenght)+':' + name +':'+ str(score(grid,lenght))+'\n')
I can't simplify this to get the error in shell:
>>> from tkinter import Toplevel, Tk
>>> fenetre = Tk()
>>> w = Toplevel()
>>> w.destroy()
>>> fenetre.destroy()
>>> fenetre.destroy()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "Z:\py34-64\lib\tkinter\__init__.py", line 1842, in destroy
self.tk.call('destroy', self._w)
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed
I'm really not a fan of using global variables, especially in a GUI program, where everything should be in classes inheriting from the base widgets.
I would like to propose a function that will do what I think you are trying to achieve, in -in my opinion- a much clearer way.
So here is a function that opens a pop-up, asks a name, and returns it.
def ask_name():
toplevel = tk.Toplevel()
label = tk.Label(toplevel, text="What's your name?")
entry = tk.Entry(toplevel)
button = tk.Button(toplevel, text="OK", command=toplevel.quit)
toplevel.pack(label)
toplevel.pack(entry)
toplevel.pack(button)
toplevel.mainloop()
return entry.get()
This function allows you not to use global variables. Besides, it takes no parameter. I like this style, because this function could almost be integrated into a utils.py module, since it is absolutely independent of any context.
Though this might not directly solve your problem, this philosophy of avoiding global variables will help keep your code clean of weird dependencies, and much easier to understand and debug.