In my script I sometimes call my ErrorWindow class to show an error message. This creates an empty tkinter window and a messagebox error window. I either only want the messagebox window, or I want the tkinter window to close automatically when I close the error window.
I've tried two pieces of code:
class ErrorWindow:
def __init__(self,error_message):
self.error_window = tk.Tk()
messagebox.showerror("ERROR",error_message,command=self.close)
self.error_window.protocol("WM_DELETE_WINDOW", self.close)
self.error_window.mainloop()
def close(self):
self.error_window.destroy()
.
class ErrorWindow:
def __init__(self,error_message):
messagebox.showerror("ERROR",error_message) #automatically creates a tk window too
But even with the second one, the tkinter window remains after I close the messagebox.
How can I program the class so that I only have to press a button (either Ok or the X in the top right of a window) once to close all windows (whether that is one or two)?
You need to withdraw the main window:
class ErrorWindow:
def __init__(self,error_message):
if not tk._default_root: # check for existing Tk instance
error_window = tk.Tk()
error_window.withdraw()
messagebox.showerror("ERROR",error_message)
This has no business as a class. You should remake this a simple function.
You did not specify whether there is just one place or multiple places where you might want want an error message. If the latter, you can create and withdraw a tk window just once. I believe a wrapper function rather than class should be sufficient for your purposes.
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
# consider placing root to control where messagebox appears
root.withdraw()
def showerror(message):
messagebox.showerror('XYZ ERROR', message, parent=root)
To avoid possible problems, I always use an explicit master or parent for everything and never depend on _default_root.
The small function below will do the job. By setting the type you can choose for: info, warning or error message box, the default is 'Info'. You can set also the timeout, the default is 2.5 seconds.
def showMessage(message, type='info', timeout=2500):
import tkinter as tk
from tkinter import messagebox as msgb
root = tk.Tk()
root.withdraw()
try:
root.after(timeout, root.destroy)
if type == 'info':
msgb.showinfo('Info', message, master=root)
elif type == 'warning':
msgb.showwarning('Warning', message, master=root)
elif type == 'error':
msgb.showerror('Error', message, master=root)
except:
pass
Call the function as follow:
For message type 'Info' and timeout of 2.5 seconds:
showMessage('Your message')
Or by your own settings for type message 'Error' and timeout 4 seconds:
showMessage('Your message', type='error', timeout=4000)
Related
I have a program that needs to display graphical error messages to users. It is a tkinter GUI, so I am using tkinter.messagebox.showerror
When I call showerror, it shows the error, but also creates a blank "tk" window, the kind created when an instance of the Tk class is called, like root = Tk().
from tkinter.messagebox import showerror
showerror(title = "Error", message = "Something bad happened")
Produces
How can I make this blank window not appear?
from Tkinter import *
from tkMessageBox import showerror
Tk().withdraw()
showerror(title = "Error", message = "Something bad happened")
Calling Tk().withdraw() before showing the error message will hide the root window.
Note: from tkinter import * for Python 3.x
As explained in this answer, Tkinter requires a root window before we create any more widgets/dialogs. If there is no root window, tkinter creates one. So, to make the blank window disappear, first we need to create a root window ourselves, hide it and destroy it once your dialog action is complete. Sample code below
from tkinter import Tk
from tkinter.messagebox import showerror
root = Tk()
root.withdraw()
showerror(title = "Error", message = "Something bad happened")
root.destroy()
Note: This is applicable when you just have to display a dialog and no other window exists.
I'm using tkinter's "askokcancel" message box to warn the user, with a pop-up, of an irreversible action.
from tkinter import Tk
Tk().withdraw()
from tkinter.messagebox import askokcancel
askokcancel("Warning", "This will delete stuff")
I'd like to change the text of the 'OK' button (from 'OK') to something like 'Delete', to make it less benign-looking.
Is this possible?
If not, what is another way to achieve it? Preferably without introducing any dependancies...
Why not open a child window thus creating your own box with your own button like this:
from tkinter import *
def messageWindow():
win = Toplevel()
win.title('warning')
message = "This will delete stuff"
Label(win, text=message).pack()
Button(win, text='Delete', command=win.destroy).pack()
root = Tk()
Button(root, text='Bring up Message', command=messageWindow).pack()
root.mainloop()
No, there is no way to change the text of the buttons for the built-in dialogs.
Your best option is to create your own dialog. It's not very hard to do, and it gives you absolute control over what is in the dialog widget.
It is true that you cannot change names in Tkinter messageboxes, but you can make your own class of messagebox so that you can reuse it several times. The following code is the same as Tkinter messagebox, but it has args and kwargs as arguments. It is just for convenience.
I am also learning, so the code does not have flash and alarm.
class Message(object):
def __init__(self,parent, *args, **kwargs):
self.parent=parent
top=Toplevel(self.parent)
top.geometry("400x200")
top.transient(self.parent)
top.title(args[0])
f0=Frame(top)
top.l1=Label(f0, text=args[1])
top.l1.pack(pady=30)
f0.pack()
top.grab_set()
top.f1=Frame(top)
for key in kwargs:
key=Button(top.f1, text=f"{kwargs[key]}")
key.pack(side=LEFT)
top.f1.pack()
The message box will look like this:
I understand ScrolledText is constructed as a Text object (but has a scrollbar attached together in a frame). But the following code throws an error when the window is closed and the printText() method is called:
import Tkinter as tk
import ttk
import ScrolledText as st
class tkGui(object):
def printText(self, event):
print "It works!"
self.mText.get("1.0", 'end-1c')
def __init__(self, window):
# create widgets
self.frame=tk.Frame(window)
self.mText = st.ScrolledText(self.frame)
self.mText.bind('<Destroy>',self.printText)
# place widgets
self.frame.pack()
self.mText.pack()
window = tk.Tk()
app = tkGui(window)
window.mainloop()
The error:
[...]
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 3077, in get
return self.tk.call(self._w, 'get', index1, index2)
TclError: invalid command name ".140506094171344.140506094172280.140506094172496"
What am I doing wrong?
Once you destroy the main window, all its sub-widgets are destroyed, and you can't access them. (This is not technically correct, as Bryan Oakley's answer points out, but it's how I think about it. Trying to work with the widgets after destruction is problematical, at best.)
You need to use wm_protocol
http://nullege.com/codes/search/Tkinter.Tk.wm_protocol
import Tkinter as tk
import ttk
import ScrolledText as st
class tkGui(object):
def printText(self):
print "It works!"
self.mText.get("1.0", 'end-1c')
self.window.destroy()
def __init__(self, window):
# create widgets
self.window = window
self.frame=tk.Frame(window)
self.mText = st.ScrolledText(self.frame)
#self.mText.bind('<Destroy>',self.printText)
window.wm_protocol("WM_DELETE_WINDOW", self.printText)
# place widgets
self.frame.pack()
self.mText.pack()
window = tk.Tk()
app = tkGui(window)
window.mainloop()
There are a couple of changes here. I removed the event parameter from printText and added a call to self.window.destroy. Note that I had to add the self.window attribute to make this call. The basic change is using wm_protocol instead of binding to the event; the others are necessary consequences.
You should not assume you can get data out of a widget when handling the <Destroy> event for that widget.
From the official documentation:
When the Destroy event is delivered to a widget, it is in a
“half-dead” state: the widget still exists, but most operations on it
will fail.
I have a program that needs to display graphical error messages to users. It is a tkinter GUI, so I am using tkinter.messagebox.showerror
When I call showerror, it shows the error, but also creates a blank "tk" window, the kind created when an instance of the Tk class is called, like root = Tk().
from tkinter.messagebox import showerror
showerror(title = "Error", message = "Something bad happened")
Produces
How can I make this blank window not appear?
from Tkinter import *
from tkMessageBox import showerror
Tk().withdraw()
showerror(title = "Error", message = "Something bad happened")
Calling Tk().withdraw() before showing the error message will hide the root window.
Note: from tkinter import * for Python 3.x
As explained in this answer, Tkinter requires a root window before we create any more widgets/dialogs. If there is no root window, tkinter creates one. So, to make the blank window disappear, first we need to create a root window ourselves, hide it and destroy it once your dialog action is complete. Sample code below
from tkinter import Tk
from tkinter.messagebox import showerror
root = Tk()
root.withdraw()
showerror(title = "Error", message = "Something bad happened")
root.destroy()
Note: This is applicable when you just have to display a dialog and no other window exists.
I'm using tkinter's "askokcancel" message box to warn the user, with a pop-up, of an irreversible action.
from tkinter import Tk
Tk().withdraw()
from tkinter.messagebox import askokcancel
askokcancel("Warning", "This will delete stuff")
I'd like to change the text of the 'OK' button (from 'OK') to something like 'Delete', to make it less benign-looking.
Is this possible?
If not, what is another way to achieve it? Preferably without introducing any dependancies...
Why not open a child window thus creating your own box with your own button like this:
from tkinter import *
def messageWindow():
win = Toplevel()
win.title('warning')
message = "This will delete stuff"
Label(win, text=message).pack()
Button(win, text='Delete', command=win.destroy).pack()
root = Tk()
Button(root, text='Bring up Message', command=messageWindow).pack()
root.mainloop()
No, there is no way to change the text of the buttons for the built-in dialogs.
Your best option is to create your own dialog. It's not very hard to do, and it gives you absolute control over what is in the dialog widget.
It is true that you cannot change names in Tkinter messageboxes, but you can make your own class of messagebox so that you can reuse it several times. The following code is the same as Tkinter messagebox, but it has args and kwargs as arguments. It is just for convenience.
I am also learning, so the code does not have flash and alarm.
class Message(object):
def __init__(self,parent, *args, **kwargs):
self.parent=parent
top=Toplevel(self.parent)
top.geometry("400x200")
top.transient(self.parent)
top.title(args[0])
f0=Frame(top)
top.l1=Label(f0, text=args[1])
top.l1.pack(pady=30)
f0.pack()
top.grab_set()
top.f1=Frame(top)
for key in kwargs:
key=Button(top.f1, text=f"{kwargs[key]}")
key.pack(side=LEFT)
top.f1.pack()
The message box will look like this: