Close a tkinter GUI after a period of time - python

I want to create a GUI that shows a message and it is automatically destroyed after some time. I saw this question in different posts but none of the solutions proposed worked out for my App. Here a small part of the code
class MessageShort(tkSimpleDialog.Dialog):
def __init__(self, parent, text, time):
self.top=Toplevel.__init__(self, parent)
self.transient(parent)
self.parent = parent
self.text=text
self.time=time
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=10, pady=10)
if not self.initial_focus:
self.initial_focus = self
self.geometry("+%d+%d" % (parent.winfo_rootx()+200,
parent.winfo_rooty()+75))
self.initial_focus.focus_set()
self.wait_window(self)
def body(self, master):
m=Message(master, text=self.text).grid(row=0,column=0,sticky=W)
master.after(self.time,master.destroy())
MessageShort(root,"Select date and decimal format",2000)#call from another part to the class to create the GUI message
root = Tk()
app = App(root) #main App
root.mainloop()
The App have different Menus and Tkinter classes to display the different tools
With the current code I close the App and I just want to close the message but not the App

Create a timer and set destroying the root as it's callback:
from threading import Timer
import time
def called_after_timer():
if(root != None):
root.destroy()
t = Timer(20 * 60, called_after_timeout)
t.start()
# do something else, such as
time.sleep(1500)

Finally I got something that seems to work
class MessageShort(tkSimpleDialog.Dialog):
def __init__(self, parent, text, time):
self.top=Toplevel.__init__(self, parent)
self.transient(parent)
self.parent = parent
self.text=text
self.time=time
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=10, pady=10)
if not self.initial_focus:
self.initial_focus = self
self.geometry("+%d+%d" % (parent.winfo_rootx()+200,
parent.winfo_rooty()+75))
self.initial_focus.focus_set()
self.wait_window(self)
def body(self, master):
m=Message(master, text=self.text).grid(row=0,column=0,sticky=W)
master.after(self.time,self.destroy)
MessageShort(root,"Select date and decimal format",2000)#call from another part to the class to create the GUI message
root = Tk()
app = App(root) #main App
root.mainloop()
In the last line self.master.destroy destroys m but not the container itself. So it has to call to self.destroy

Related

Open existing class with button in another class

I have a problem with opening existing class from button which is in another class.
I don't have any idea how to make it working because I know I should in function add (Toplevel) but it's also not working.
class na_dane(object):
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.pack()
# CODE ........
def close_windows(self):
self.master.destroy()
# this code opens another window (class) which I have defined but not included here...
def new_window(self):
self.newWindow = tk.Toplevel.iconify(self.master)
self.app = sinus(self.newWindow)
# here is the main class where when I click button it should open na_dane Class
class menu(na_dane):
def __init__(self, master):
self.master = master
przycisk_Jednostkowo_Liniowa = tk.Button(
text="Funkcja jednostkowo-liniowa",
width=250,
height=2,
bg="blue",
fg="yellow"
).pack()
# HERE IS THE PROBLEM WHERE I TRY TO BIND BUTTON TO CLASS
przycisk_Jednostkowo_Liniowa.bind("<Button>", lambda e: na_dane(master))
przycisk_Jednostkowo_Liniowa.pack()
def main():
root = tk.Tk()
root.geometry("250x200")
root.title("GRUPA 3")
root['bg'] = '#FFFFFF'
app = menu(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem seems to be that you attempt to pack przycisk_Jednostkowo_Liniowa in the same line as it is created. What this line of code is doing is creating a button with the characteristics described in your arguments, applying .pack() to it, then assigning the output of that .pack() execution to przycisk_Jednostkowo_Liniowa. Because .pack() doesn't have a return value, przycisk_Jednostkowo_Liniowa becomes None, so when you later attempt to bind a lambda to it, it doesn't recognize .bind() as a valid method. I tested the code with that .pack() removed and it worked for me.

Why does adding a custom menu break keyboard event binding in Python3/tkinter?

In the following code, when I press the button to add some secondary windows, and then try to close a window by using "Command-w" it does not always close the active window. But if I disable the menu creation by commenting the line self.gerar_menu(), windows are opened and closed as expected (I mean, by clicking the red 'x' button or by pressing Command-W in OS X). Any idea about what is wrong here?
Here is my current test code:
#!/usr/bin/env python3.6
# encoding: utf-8
import tkinter as tk
import tkinter.font
from tkinter import ttk
class baseApp(ttk.Frame):
"""
Parent classe for main app window (will include some aditional methods and properties).
"""
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.master = master
self.mainframe = ttk.Frame(master)
self.mainframe.pack()
class App(baseApp):
""" Base class for the main application window """
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.master = master
#self.gerar_menu() # This line breaks "Command-w" functionality
self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window")
self.lbl_text.pack()
self.btn = ttk.Button(self.mainframe, text="Open Second window",
command=lambda: self.create_detail_window(self, number=0))
self.btn.pack()
self.newDetailsWindow = {}
self.windows_count=0
def gerar_menu(self):
""" generate the application menu """
self.menu = tk.Menu(root)
root.config(menu=self.menu)
self.fileMenu = tk.Menu(self.menu)
self.menu.add_cascade(label="File", menu=self.fileMenu)
self.fileMenu.add_command(label="New Document", command=None, accelerator="Command+n")
def create_detail_window(self, *event, number=None):
self.windows_count += 1
self.newDetailsWindow[self.windows_count]=tk.Toplevel()
self.newDetailsWindow[self.windows_count].geometry('900x600+80+130')
self.newDetailsWindow[self.windows_count].title(f'Detail: {self.windows_count}')
self.newDetailsWindow[self.windows_count].wm_protocol("WM_DELETE_WINDOW", self.newDetailsWindow[self.windows_count].destroy)
self.newDetailsWindow[self.windows_count].bind("Command-w", lambda event: self.newDetailsWindow[-1].destroy())
self.detail_window = detailWindow(self.newDetailsWindow[self.windows_count], self.windows_count)
self.newDetailsWindow[self.windows_count].focus()
print(self.newDetailsWindow)
class detailWindow(ttk.Frame):
""" Base class for secondary windows """
def __init__(self, master, rep_num, *args,**kwargs):
super().__init__(master,*args,**kwargs)
self.num_rep = rep_num
self.master.minsize(900, 600)
self.master.maxsize(900, 600)
print(f"Showing details about nr. {self.num_rep}")
self.mainframe = ttk.Frame(master)
self.mainframe.pack()
self.lbl_text = ttk.Label(self.mainframe,
text=f"Showing details about nr. {self.num_rep}")
self.lbl_text.pack()
if __name__ == "__main__":
root = tk.Tk()
janela_principal = App(root)
root.title('Main Window')
root.bind_all("<Mod2-q>", exit)
root.mainloop()
If you want to create a binding for closing a window, you need the function to act upon the actual window that received the event. Your code is always deleting the last window that was opened no matter which window received the event.
The first step is to bind to a function rather than using lambda. While lambda has its uses, binding to a named function is much easier to debug and maintain.
Once the function is called, the event object can tell you which window got the event via the widget attribute of the event object. Given this window, you can get the toplevel window that this window is in (or itself, if it's a toplevel window) via the winfo_toplevel command.
For example:
window = tk.Toplevel(...)
...
window.bind("<Command-w>", self.destroy_window)
...
def destroy_window(self, event):
window = event.widget.winfo_toplevel()
window.destroy()

Receiveing input from Python's tkinter entry widget and displaying it in a label

The program I am working with currently requires input from the user to be displayed on the programs window. I have researched on both the internet and stackoverflow, discovering several solutions to my problem, but none seem to work. My goal is to receive input from the user via Python's tkinter entry widget and display the results in a new label, while taking out my initial one and the entry box, however, the program is rejecting my attempts at an answer.
What strategy, lines of code/library's, or pieces of advice do you have for me to accomplish my goal?
My existing solutions:
.get()
textvariable=self.entdat
Existing code is as follows:
from Tkinter import *
import time
class Input(Frame):
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, background="white")
self.parent = parent
self.initUI()
self.entdat = StringVar
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
self.ol = Label(text="Objective:")
self.ol.pack(side=TOP)
self.ew = Entry()
self.ew.pack(side=TOP)
self.b = Button(text="OK", command=self.clicked)
self.b.pack(side=TOP)
def clicked(self):
self.entdat = self.ew.get()
self.dat = Label(textvariable=self.ew.get())
self.dat.pack(side=TOP)
self.hide_Widget()
def hide_Widget(event):
event.ew.pack_forget()
event.ol.pack_forget()
event.b.pack_forget()
def main():
root = Tk()
root.geometry("240x135+25+50")
tm = Input(root)
tm.pack(side=TOP)
root.mainloop()
if __name__ == '__main__':
main()
I amended your code, so that it executes at least, and hopefully in a way you want.
from Tkinter import *
class Input(Frame):
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, background="white")
self.parent = parent
self.entdat = StringVar()
self.makeWidgets()
def makeWidgets(self):
self.ol = Label(text="Objective:")
self.ol.pack(side=TOP)
self.ew = Entry(textvariable=self.entdat)
self.ew.pack(side=TOP)
self.b = Button(text="OK", command=self.clicked)
self.b.pack(side=TOP)
def clicked(self):
self.dat = Label(self, textvariable=self.entdat )
self.dat.pack(side=TOP)
self.distroy_Widget()
def distroy_Widget(self):
self.ew.destroy()
self.ol.destroy()
self.b.destroy()
def main():
root = Tk()
root.geometry("240x135+25+50")
tm = Input(root)
tm.pack(side=TOP)
root.mainloop()
if __name__ == '__main__':
main()
Hope it helps.

Python Tkinter, how to disable a button in a class?

I have seen many explanations of how to turn an enabled button disabled but not when classes are involved. The error here is in the line 'button_1.config...' and the error message is that button_1 is not defined. I think this is because it is in a different method but im not sure how to disable a button from a different method. any help is appreciated.
from tkinter import *
class menu:
def __init__(self, master):
self.master = master
button_1 = Button(self.master, text = 'test', command = self.correct).pack()
def correct(self):
button_1.config(state = DISABLED)
def window():
root = Tk()
menu(root)
root.mainloop()
if __name__ == '__main__':
window()
The button needs to be an instance variable, if you're accessing it between methods in the class. Just add self. in front of it. It's also going to need to be packed on a separate line, otherwise the instance variable self.button_1 will return None:
class menu:
def __init__(self, master):
self.master = master
self.button_1 = Button(self.master, text = 'test', command = self.correct)
self.button_1.pack()
def correct(self):
self.button_1.config(state = DISABLED)

tkinter app adding a right click context menu?

I have a python-tkinter gui app that I've been trying to find some way to add in some functionality. I was hoping there would be a way to right-click on an item in the app's listbox area and bring up a context menu. Is tkinter able to accomplish this? Would I be better off looking into gtk or some other gui-toolkit?
You would create a Menu instance and write a function that calls
its post() or tk_popup() method.
The tkinter documentation doesn't currently have any information about tk_popup().
Read the Tk documentation for a description, or the source:
library/menu.tcl in the Tcl/Tk source:
::tk_popup --
This procedure pops up a menu and sets things up for traversing
the menu and its submenus.
Arguments:
menu - Name of the menu to be popped up.
x, y - Root coordinates at which to pop up the menu.
entry - Index of a menu entry to center over (x,y).
If omitted or specified as {}, then menu's
upper-left corner goes at (x,y).
tkinter/__init__.py in the Python source:
def tk_popup(self, x, y, entry=""):
"""Post the menu at position X,Y with entry ENTRY."""
self.tk.call('tk_popup', self._w, x, y, entry)
You associate your context menu invoking function with right-click via:
the_widget_clicked_on.bind("<Button-3>", your_function).
However, the number associated with right-click is not the same on every platform.
library/tk.tcl in the Tcl/Tk source:
On Darwin/Aqua, buttons from left to right are 1,3,2.
On Darwin/X11 with recent XQuartz as the X server, they are 1,2,3;
other X servers may differ.
Here is an example I wrote that adds a context menu to a Listbox:
import tkinter # Tkinter -> tkinter in Python 3
class FancyListbox(tkinter.Listbox):
def __init__(self, parent, *args, **kwargs):
tkinter.Listbox.__init__(self, parent, *args, **kwargs)
self.popup_menu = tkinter.Menu(self, tearoff=0)
self.popup_menu.add_command(label="Delete",
command=self.delete_selected)
self.popup_menu.add_command(label="Select All",
command=self.select_all)
self.bind("<Button-3>", self.popup) # Button-2 on Aqua
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def delete_selected(self):
for i in self.curselection()[::-1]:
self.delete(i)
def select_all(self):
self.selection_set(0, 'end')
root = tkinter.Tk()
flb = FancyListbox(root, selectmode='multiple')
for n in range(10):
flb.insert('end', n)
flb.pack()
root.mainloop()
The use of grab_release() was observed in an example on effbot.
Its effect might not be the same on all systems.
I made some changes to the conext menu code above in order to adjust my demand and I think it would be useful to share:
Version 1:
import tkinter as tk
from tkinter import ttk
class Main(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
master.geometry('500x350')
self.master = master
self.tree = ttk.Treeview(self.master, height=15)
self.tree.pack(fill='x')
self.btn = tk.Button(master, text='click', command=self.clickbtn)
self.btn.pack()
self.aMenu = tk.Menu(master, tearoff=0)
self.aMenu.add_command(label='Delete', command=self.delete)
self.aMenu.add_command(label='Say Hello', command=self.hello)
self.num = 0
# attach popup to treeview widget
self.tree.bind("<Button-3>", self.popup)
def clickbtn(self):
text = 'Hello ' + str(self.num)
self.tree.insert('', 'end', text=text)
self.num += 1
def delete(self):
print(self.tree.focus())
if self.iid:
self.tree.delete(self.iid)
def hello(self):
print ('hello!')
def popup(self, event):
self.iid = self.tree.identify_row(event.y)
if self.iid:
# mouse pointer over item
self.tree.selection_set(self.iid)
self.aMenu.post(event.x_root, event.y_root)
else:
pass
root = tk.Tk()
app=Main(root)
root.mainloop()
Version 2:
import tkinter as tk
from tkinter import ttk
class Main(tk.Frame):
def __init__(self, master):
master.geometry('500x350')
self.master = master
tk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self.master, height=15)
self.tree.pack(fill='x')
self.btn = tk.Button(master, text='click', command=self.clickbtn)
self.btn.pack()
self.rclick = RightClick(self.master)
self.num = 0
# attach popup to treeview widget
self.tree.bind('<Button-3>', self.rclick.popup)
def clickbtn(self):
text = 'Hello ' + str(self.num)
self.tree.insert('', 'end', text=text)
self.num += 1
class RightClick:
def __init__(self, master):
# create a popup menu
self.aMenu = tk.Menu(master, tearoff=0)
self.aMenu.add_command(label='Delete', command=self.delete)
self.aMenu.add_command(label='Say Hello', command=self.hello)
self.tree_item = ''
def delete(self):
if self.tree_item:
app.tree.delete(self.tree_item)
def hello(self):
print ('hello!')
def popup(self, event):
self.aMenu.post(event.x_root, event.y_root)
self.tree_item = app.tree.focus()
root = tk.Tk()
app=Main(root)
root.mainloop()
from tkinter import *
root=Tk()
root.geometry("500x400+200+100")
class Menu_Entry(Entry):
def __init__(self,perant,*args,**kwargs):
Entry.__init__(self,perant,*args,**kwargs)
self.popup_menu=Menu(self,tearoff=0,background='#1c1b1a',fg='white',
activebackground='#534c5c',
activeforeground='Yellow')
self.popup_menu.add_command(label="Cut ",command=self.Cut,
accelerator='Ctrl+V')
self.popup_menu.add_command(label="Copy ",command=self.Copy,compound=LEFT,
accelerator='Ctrl+C')
self.popup_menu.add_command(label="Paste ",command=self.Paste,accelerator='Ctrl+V')
self.popup_menu.add_separator()
self.popup_menu.add_command(label="Select all",command=self.select_all,accelerator="Ctrl+A")
self.popup_menu.add_command(label="Delete",command=self.delete_only,accelerator=" Delete")
self.popup_menu.add_command(label="Delete all",command=self.delete_selected,accelerator="Ctrl+D")
self.bind('<Button-3>',self.popup)
self.bind("<Control-d>",self.delete_selected_with_e1)
self.bind('<App>',self.popup)
self.context_menu = Menu(self, tearoff=0)
self.context_menu.add_command(label="Cut")
self.context_menu.add_command(label="Copy")
self.context_menu.add_command(label="Paste")
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def Copy(self):
self.event_generate('<<Copy>>')
def Paste(self):
self.event_generate('<<Paste>>')
def Cut(self):
self.event_generate('<<Cut>>')
def delete_selected_with_e1(self,event):
self.select_range(0, END)
self.focus()
self.event_generate("<Delete>")
def delete_selected(self):
self.select_range(0, END)
self.focus()
self.event_generate("<Delete>")
def delete_only(self):
self.event_generate("<BackSpace>")
def select_all(self):
self.select_range(0, END)
self.focus()
ent=Menu_Entry(root)
ent.pack()
root.mainloop()
Important Caveat:
(Assuming the event argument that contains the coordinates is called "event"): Nothing will happen or be visible when you call tk_popup(...) unless you use "event.x_root" and "event.y_root" as arguments. If you do the obvious of using "event.x" and "event.y", it won't work, even though the names of the coordinates are "x" and "y" and there is no mention of "x_root" and "y_root" anywhere within it.
As for the grab_release(..), it's not necessary, anywhere. "tearoff=0" also isn't necessary, setting it to 1 (which is default), simply adds a dotted line entry to the context menu. If you click on it, it detaches the context menu and makes it its own top-level window with window decorators. tearoff=0 will hide this entry. Moreover, it doesn't matter if you set the menu's master to any specific widget or root, or anything at all.

Categories

Resources