why is the `destroy` method executed without any order? Even if you display it as a separate function - destroy

```
def repeat_game(self):
def restart_program():
"""Restarts the current program.
Note: this function does not return. Any cleanup action (like
saving data) must be done before calling this function."""
python = sys.executable
os.execl(python, python, * sys.argv)
time.sleep(3)
root = Tk()
Label(root, text="Хотите сыграть еще?").pack()
Button(root, text="Да!", command=restart_program).pack()
Button(root, text="НЕТ!", command=root.destroy()).pack()
root.mainloop()

Related

Adding notebook tabs in tkinter - how do I do it with a class-based structure? (Python 2)

I want each tab to come from it's own class (classes are in their own files - I am just testing the first one for now).
Here is what I tried:
tab1.py
from Tkinter import *
import Tkinter as tk
class Tab(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
fr = Frame(self).pack()
Label(fr, text="one", bg='red', bd=2).pack()
Label(fr, text="two", bg='yellow', bd=2).pack()
if __name__ == '__main__':
root = Tk()
frame = Frame(root).pack()
Tab(frame)
Button(frame, text='only if class', command=root.destroy).pack()
mainloop()
noteBook.py
from Tkinter import *
from ttk import *
from tab1 import Tab
root = Tk()
note = Notebook(root)
main_frame = Frame(note)
button1 = Button(main_frame, text='test').pack()
#tab1 = Tab(note)
tab1 = Frame(note)
tab2 = Frame(note)
tab3 = Frame(note)
Tab(tab1)
Button(tab1, text='Exit', command=root.destroy).pack()
note.add(tab1, text = "Tab One", compound=TOP)
note.add(tab2, text = "Tab Two")
note.add(tab3, text = "Tab Three")
note.pack()
root.mainloop()
exit()
run with:
python2.7 noteBook.py
The problem is that the content of tab1.py does not appear within the first tab, it instead appears within the frame that contains the whole noteBook.
Also when running tab1.py directly with python2.7 noteBook.py I need it to behave properly meaning from what it has now it should show just the tab with an extra button from the if __name___... part.
I have come accros multiple examples but only found one that was what I want but it had no working solution and it was for python3 - I would like python2. python3 question with no working answer Thanks.
The problem is this line of code:
fr = Frame(self).pack()
When you do the above, fr is None because .pack() returns None (because x().y() returns the value of y()). Later, you do this:
Label(fr, text="one", bg='red', bd=2).pack()
Since fr is None, the label is created in the root window.
Unrelated to the problem, here's some advice: you are creating too many frames. You don't need fr inside of Tab, and you don't need tab1, tab2, or tab3
Here's all you need for Tab:
class Tab(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master, background="pink")
Label(self, text="one", bg='red', bd=2).pack()
Label(self, text="two", bg='yellow', bd=2).pack()
To add it to the notebook, you just need two lines:
tab1 = Tab(note)
note.add(tab1, text = "Tab One", compound=TOP)
This works perfectly and just for fun I've illustrated the populating of tabs 2 and 3 althought I just reused the same class for simplicity here. The goal was to be able to run the tabs directly to view them alone during developpement without having to run the whole thing every time.
noteBook.py
from Tkinter import *
from ttk import *
from tab1 import Tab
root = Tk()
note = Notebook(root)
main_frame = Frame(note)
button1 = Button(main_frame, text='test').pack()
tab1 = Frame(note)
tab2 = Frame(note)
tab3 = Frame(note)
Tab(tab1)
Tab(tab2)
Tab(tab3)
Button(tab1, text='Exit', command=root.destroy).pack()
note.add(tab1, text = "Tab One", compound=TOP)
note.add(tab2, text = "Tab Two")
note.add(tab3, text = "Tab Three")
note.pack()
root.mainloop()
exit()
tab1.py
import Tkinter as tk
class Tab(tk.Frame):
def __init__(self, parent_widget):
tk.Frame.__init__(self, parent_widget)
self.fr = tk.Frame(parent_widget, width=200, height=200, bg='pink', bd=2)
tk.Label(self.fr, text="one", bg='red', bd=2).pack()
tk.Label(self.fr, text="two", bg='yellow', bd=2).pack()
self.fr.pack() # this packing must be done after 2 above packings
if __name__ == '__main__':
root = tk.Tk() # the app window
main_frame = tk.Frame(root, height=200, width=200, bg='blue', bd=2) # main frame
Tab(main_frame) # instatiate Tab(), sending main_frame as the parent_widget
tk.Button(main_frame, text='only if class', command=root.destroy).pack()
main_frame.pack() # display main frame on window
tk.mainloop()

Python 3 Tkinter - How to call a function from another class

I am trying to get my save button to call a function from another class. I would like to click on the save button as much as I want and it should print "hello people" every time. Though, I am having trouble in getting the save button to work.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.createWidgets()
def createWidgets(self):
#save button
self.saveLabel = tk.Label(self.parent, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
#When I click the button save, I would like it to call the test function in the documentMaker class
self.saveButton = tk.Button(self.parent, text = "Save", command = documentMaker.test(self))
self.saveButton.pack()
class documentMaker():
def test(self):
print ("hello people")
root = tk.Tk()
app = Application(root)
app.master.title('Sample application')
object = documentMaker()
object.test()
app.mainloop()
In your documentMaker class, change the test method to a #staticmethod:
class documentMaker():
#staticmethod
def test(cls):
print ("hello people")
Then your saveButton's command can be:
command = documentMaker.test
A staticmethod is bound to the class, not to an instance of the class like an instance method. So, we can call it from the class's name directly. If you did not want it to be a staticmethod, you could keep it an instance method and have the command line change to:
command = documentMaker().test

tk button changes appearance

So I was doing this program and noticed that both my buttons look initially like this
And after I run my program for some time the second button changes it appearance to this
When does this happen?
Here's my code :/ in case I am doing something that should not be done. I am doing this in python 2.7.8 in IDLE.
import time
import Tkinter as tk
from Tkinter import StringVar
import threading
global root
root = tk.Tk()
x = tk.StringVar()
x.set('false')
def xval(*args):
try:
for i in range(0,9):
global x
print x.get()
if x.get()== 'false' :
print "x=false %d time"%i
time.sleep(1)
else:
print "waiting"
root.update()
except:
pass
def stop(event):
resume_btn.configure(state="normal")
global x
x.set('true')
print "execution stopped:%s"%x
def start(event):
global x
x.set('false')
print "execution started:%s"%x
xval()
root.title("GUI-Data Retrieval")
th = threading.Event()
t = threading.Thread(target=xval,args=(th,))
t.deamon=True
t.start()
x_btn = tk.Button(root, text="Stop", background="Snow", width=20, relief="raised")
x_btn.grid(row=0, column=4, sticky="W", padx=20, pady=5)
x_btn.bind('<Button-1>',stop)
resume_btn = tk.Button(root, text="Start", background="Snow", width=20, relief="raised")
resume_btn.configure(state="disabled")
resume_btn.grid(row=0, column=6, sticky="W", padx=20, pady=5)
resume_btn.bind('<Button-1>',start)
root.mainloop()
The problem is that your binding is handled before the default bindings. It is the default bindings that change the appearance of the button when it is clicked on. You are disabling the button on a click, preventing the default behavior from resetting the appearance of the button when you release the mouse button.
Unless there's a specific reason to do otherwise, you should use the command attribute of the button widget rather than try to create your own bindings.

Invoking destroy() method on window after mainloop in tkinter

I'm relatively new to Python and Tkinter and I am striving to get my head over how mainloop and the after method work.
More specifically, I want to create a splashScreen, which goes away after a timeframe, and then the actual mainWindow is shown.
My code looks like this:
class SplashScreen:
# some code here
def destroyMe(self):
self.destroy()
def destroySplashScreen(self, timeToSleep=0):
if timeToSleep > 0:
self.master.after(timeToSleep*1000, self.destroyMe())
# some other code here
if __name__ == '__main__':
root = Toplevel()
mySP = SplashScreen(root)
mySP.populateSplashScreen()
mySP.destroySplashScreen(5)
root.mainloop()
However, what the code does is to create the whole window after the timeframe given (5 sec.) without any content. Instead, it should create it, wait 5 sec. and then destroy it.
Working example
after expects only function name (without ()).
destroy() needs self.master
from Tkinter import *
class SplashScreen:
# some code here
def __init__(self, master):
self.master = master
self.master.title("SplashScreen")
def destroyMe(self):
self.master.destroy()
def destroySplashScreen(self, timeToSleep=0):
if timeToSleep > 0:
self.master.after(timeToSleep*1000, self.destroyMe)
# some other code here
if __name__ == '__main__':
root = Toplevel()
mySP = SplashScreen(root)
#mySP.populateSplashScreen()
mySP.destroySplashScreen(3)
root.mainloop()
BTW: Toplevel is used to create "child" window so (in example) it create automaticly "parent" window - so I add title("SplashScreen")
BTW: if you will use command= in widget - it also expects function name without ().
If you use (in command or after) function with () you run that function and result is assigned to command= or use as parameter for after.

Python Tkinter instance has no attribute 'tk'

AttributeError: MyGUI instance has no attribute 'tk'
Also, how do I make the created window have a fixed size and not be able to resize with the mouse? Or after changing label value by clicking on button.
My code:
from Tkinter import*
class MyGUI(Frame):
def __init__(self):
self.__mainWindow = Tk()
#lbl
self.labelText = 'label message'
self.depositLabel = Label(self.__mainWindow, text = self.labelText)
#buttons
self.hi_there = Button(self.__mainWindow)
self.hi_there["text"] = "Hello",
self.hi_there["command"] = self.testeo
self.QUIT = Button(self.__mainWindow)
self.QUIT["text"] = "QUIT"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
#place on view
self.depositLabel.pack()
self.hi_there.pack() #placed in order!
self.QUIT.pack()
#What does it do?
mainloop()
def testeo(self):
self.depositLabel['text'] = 'c2value'
print "testeo"
def depositCallBack(self,event):
self.labelText = 'change the value'
print(self.labelText)
self.depositLabel['text'] = 'change the value'
myGUI = MyGUI()
What's wrong?
Thanks
You should invoke the super constructor for Frame. Not sure, but I guess this will set the tk attribute that the quit command relies on. After that, there's no need to create your own Tk() instance.
def __init__(self):
Frame.__init__(self)
# self.__mainWindow = Tk()
Of course, you will also have to change the constructor calls for your widgets accordingly, e.g.,
self.hi_there = Button(self) # self, not self.__mainWindow
or better (or at least shorter): set all the attributes directly in the constructors:
self.hi_there = Button(self, text="Hello", command=self.testeo)
Also add self.pack() to your constructor.
(Alternatively, you could change the quit command to self.__mainWindow.quit, but I think the above is better style for creating Frames, see e.g. here.)
This error typically means you are calling SomeTKSuperClass.__init__ and forgetting the first parameter, which must be self. Remember that __init__ is a class method (static function) in this context, not an instance method, which means that you must explicitly pass it self.