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.
Related
Is there a simple way to get the right click menu to open on texty only and not the whole window?
This was a quick mashup to illustrate my question. Inheriting from texty on line 25 was a shot in the dark, which didnt work, but it's close to a simple solution, like I am seeking. I was hoping to avoid programming a whole class each time I want to set a right click menu.
from tkinter import *
from tkinter import ttk
def menu_popup(event):
try:
popup.tk_popup(event.x_root, event.y_root, 0)
finally:
popup.grab_release()
win = Tk()
win.geometry("600x550+125+125")
e = Entry(win, width=50, font=('Helvetica', 11))
e.pack()
e.insert(0, "Some text....")
label = Label(win, text="Right-click to see a menu", font= ('Helvetica 18'))
label.pack(pady= 40)
texty=Text(win, height=10)
texty.pack()
popup = Menu(texty, tearoff=0)
popup.add_command(label="New")
popup.add_separator()
popup.add_command(label="Open")
popup.add_separator()
popup.add_command(label="Close")
win.bind("<Button-3>", menu_popup)
button = ttk.Button(win, text="Quit", command=win.destroy)
button.pack()
mainloop()
The widget on which the callback should be executed for the respective event is determined by the widget you call bind on(and the level of bind too*). So if you want the event to be identified within texty, then apply binding to it.
texty.bind("<Button-3>", menu_popup)
* There is bind_all which executes no matter which widget has focus or is called upon. Read 54.1. Levels of binding for more info.
I am exploring GUI's at the moment. What I want is to have a GUI with a 2 buttons and I want each of the buttons to run a separate python script when clicked.
I have outlined my code below (the first button runs just fine but I am having issues with the second button.
Error Message when I choose the second button:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
Code:
from tkinter import *
import tkinter as tk
master = Tk()
def First_Scriptcallback():
exec(open(r'Desktop\Automation\First_Script.py').read())
def second_Scriptcallback():
exec(open(r'Desktop\Automation\Second_Script.py').read())
#master.title("Test GUI")
#canvas = tk.Canvas(master, height=300, width = 400)
#canvas.pack()
firstButton = Button(master, text="Run first script", command=First_Scriptcallback)
firstButton.pack()
secondButton = Button(master, text="Run second script", command=second_Scriptcallback)
secondButton.pack()
mainloop()
Thanks
As #matiiss suggested importing the other scripts into your program can help and it can be done like this,
import First_Script as first
import Second_Script as second
from tkinter import *
import tkinter as tk
master = Tk()
def First_Scriptcallback():
first.function_name()#here you must create functions in first_script to call in this main script
def second_Scriptcallback():
second.function_name()
#master.title("Test GUI")
#canvas = tk.Canvas(master, height=300, width = 400)
#canvas.pack()
firstButton = Button(master, text="Run first script", command=First_Scriptcallback)
#command=first.function_name
#we can also directly call an function using above command,but sometimes there are problems related to this approch
firstButton.pack()
secondButton = Button(master, text="Run second script", command=second_Scriptcallback)
#command=second.function_name
secondButton.pack()
mainloop()
here for this example the scripts and the program must be in same directory.
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()
#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.
I have this problem in Tkinter. I wish to import a file (*.txt) with Open Button and save some values with Save Button. I wish to show a Error message when no file is open.
I am wrong becuse i get always this message:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1470, in __call__
return self.func(*args)
File "C:/Users/samsung/PycharmProjects/test_FirstDensity/openANDread.py", line 30, in save
if not self.filename.name:
AttributeError: MainWindow instance has no attribute 'filename'
This is a very simplify example
from Tkinter import *
import tkMessageBox
import Tkinter, Tkconstants, tkFileDialog
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("input")
self.master.minsize(250, 150)
self.grid(sticky=E+W+N+S)
top=self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
for i in range(2):self.rowconfigure(i, weight=1)
self.columnconfigure(1, weight=1)
self.button0 = Button(self, text="open", command=self.askopenfilename, activeforeground="red")
self.button0.grid(row=0, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S)
self.button1 = Button(self, text="save", command=self.save, activeforeground="red")
self.button1.grid(row=1, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S)
def askopenfilename(self):
self.filename = tkFileDialog.askopenfilename(filetypes=[("Text Files",'.txt')])
return self.filename.name
def save(self):
if not self.filename.name:
tkMessageBox.showerror("Error", message="None file to save")
return
It seems like you are calling the save() method before calling the askopenfilename() method. That's why, you get the AttributeError. Make sure the flow of your code reflects this change.
You might also want to include proper error handling in your askopenfilename() method itself to include situations where no file is opened.
You could do something on these lines. First initialize the self.filename = None in your constructor for the MainWindow class. Then you could modify your method as such:
def askopenfilename(self):
self.filename = tkFileDialog.askopenfilename(filetypes=[("Text Files",'.txt')])
if not self.filename: return None
return self.filename.name
However this is just to give you an idea, I have not myself worked with Tkinter much. All in all, it would depend on what exactly you are trying to achieve.