background window and main window tkinter - python

I'm learning Tkinter and just had a doubt.
Is there any method that prevents me from working with the window in the background until I close the main window? I saw that there is an argument about this here, but I ended up losing it and decided to ask.

Yes, it is possible (IIUC) and if I do understand correctly you simply want to block user for accessing other windows while your window is open. It has a slight flaw in that if user does a lot of alt-tabbing they may get keyboard focus on some other window but only the keyboard, as soon as they do anything with mouse they should get back to the main window. This can be handled using some module like keyboard to listen for global keyboard input but also perhaps it could be done using Windows API but for a tkinter only solution that is/should be effective enough you can use this:
import tkinter as tk
def check_focus():
if root.focus_get() is None:
root.focus_force()
root.after(100, check_focus)
def create_shield():
shield = tk.Toplevel(root)
shield.attributes('-topmost', True)
shield.attributes('-fullscreen', True)
shield.attributes('-alpha', 0.01)
shield.overrideredirect(True)
shield.bind('<FocusIn>', lambda _: root.focus_force())
root = tk.Tk()
root.attributes('-topmost', True)
create_shield()
check_focus()
root.mainloop()
First there is the after "loop" that checks for focus to see if the window has any, this prevents some attempts of trying to alt-tab away but enough attempts may cause some kind of timeout and the window will get out of focus.
The other part is a "shield" that simply is another fullscreen window that is almost completely invisible (on other OS the attribute name may be -splash instead of -alpha) and just keeps it in top to assist in focusing on the main window.
Or just make the window fullscreen and topmost (again the issues with keyboard focus on other windows but now user has no issues whatsoever with accessing the other windows except they wouldn't see what they are typing) and this requires to implement some button to close the window (currently doable by alt+f4 or shutting down the computer or killing the process via run without looking at what you are typing and perhaps some other way I haven't thought about, but for user convenience probably a button should be made that would simply destroy the window):
import tkinter as tk
root = tk.Tk()
root.attributes('-topmost', True)
root.attributes('-fullscreen', True)
root.mainloop()

Related

Tkinter set window focus and set to foreground after clicking on another window

This one has been puzzling me for too much time now, I don't find a way to ensure that a tkinter window is active and on top of the other windows (non-tkinter windows). I managed to recreate the problem with a very simple code. The idea is that during the sleeping time the user clicks on any other window and this seems to screw up the part of the code that should activate the window. If you run the following code and during the sleeping time you don't click anywhere it works fine, but if during that sleeping time you click on any other window the tkinter window appears in a non-active state (or non-foreground). Is there a way to ensures that the tkinter window is active when opened?
import tkinter as tk
import time
# Run code and click on any other window during the 3 seconds waiting time
time.sleep(3)
root = tk.Tk()
root.lift()
root.attributes("-topmost", True)
root.focus_force()
root.mainloop()

Tkinter intvar causing process to live on

I realize there are many examples of how to properly close a Tkinter GUI by calling the root.destroy() function. They work with my setup except I've determined that including a variable of type tkinter.intvar causes the gui process to live on even after I close the window. Here's a working example:
import mtTkinter as Tkinter #special threadsafe version of Tkinter module, required for message windows. You can use regular Tkinter if needed
root = Tkinter.Tk()
root.wm_title("KillmePlz")
nexusvar = Tkinter.IntVar()
def ClosebyX():
root.destroy()
closebutton = Tkinter.Button(root,text='Quit',command=ClosebyX)
closebutton.pack()
root.protocol('WM_DELETE_WINDOW', ClosebyX)
root.mainloop()
On my machine if I remove the creation of "nexusvar", the Tkinter.IntVar, when I close the GUI the process also stops. If this variable is included as shown above, I see the process linger after the gui is closed. I don't think mtTkinter makes any difference.
Does anyone know why this might be happening?
Windows 7 64 bit, Python 2.7.12
UPDATE 9/20/16:
mtTkinter is the source of this problem. The solution below is for regular Tkinter module use. For solving this problem using mtTkinter see the following post
nexusvar isn't a child of root, so when you destroy root it doesn't know to destroy nexusvar as well - the two things are separate. You can set an IntVar to have root as a parent by supplying root to the constructor. nexusvar should then be able to destroy itself when root dies.

Tkinter. How to destroy the root window while top level window is waiting for a variable

I have an issue whereby a user can open a top_level window which sits waiting for a variable via the wait_variable() method. But while the top_level window is open, the root window is still visible and the user is able to close the root window via usual methods (this is intentional). What I'd like (and sort of expect tkinter to do) is that calling .destroy() or .quit() on the root window will cause all of root's children to be terminated. But what appears to be happening is that the top_level window remains stuck in its local event loop and can only be killed via the task manager once its parent has gone.
So what am I doing wrong? How do I get the top_level window to listen out for it's parents destruction while its in a local event loop?
.
Below is some example code that demonstrates the issue. If you run the code and follow the steps below your IDE WILL CRASH!! probably, so save you work. Push the Click button in root. The top level window will appear with another button that says Test. Now close the root window. The IDE will hang.
import tkinter
root = tkinter.Tk()
def toplevel(event=None):
def set1(event=None):
vr.set(1)
tp = tkinter.Toplevel(root)
vr = tkinter.IntVar()
bt_ = tkinter.Button(tp,text='Test',command=set1)
bt_.grid()
tp.wait_variable(vr)
tp.destroy()
bt = tkinter.Button(root,text='Click',command=toplevel)
bt.grid()
root.mainloop()
Edit: My current solution is to repoint the WM_DELETE_WINDOW protocol when the top_level window starts, to a function that sets the variable that the local event loop is waiting for, and then destroys the root window.
The correct solution IMO is to not wait on the variable, but rather, wait on the window. Your button then must take on the responsibility of destroying the window instead of or in addition to setting the variable.
This is the right solution for another reason: as written your program will also hang if the user destroys the toplevel with the buttons in the titlebar. Since you're waiting on a variable, because the window is destroyed the variable will never be set.
(the solution you proposed in your edit is also valid -- register with WM_DELETE_WINDOW on the toplevel to set the variable when the window is destroyed. The effect will be the same).

How can I get a tkinter window to display in LINUX

I am trying to follow along in the book Python Programming for Kids. I am working with a group of neighborhood kids and to reduce the cost we are using the Raspberry Pi as our computer. I am a Windows guy and the GUI builder of choice for me is WxPython. I am trying to get ready for next weeks class and have run into a problem. I have entered the code below
from tkinter import *
tk = Tk()
btn = Button(tk,text = 'click me')
btn.pack()
according to the book the second line is supposed to create a window (frame I think in the Wx world) and the third line defines a button object and the fourth inserts it in the window.
However, this is not working and I have not been able to figure out why. tkinter is imported and the tk object has lots of methods/properties visible when I type dir(tk) so I know that we have tkinter on the Pi's.
Any insight would be appreciated.
You have to run the windows system event loop and process events. This means the last command in your program should be tk.mainloop(). The X Windows System operated in a similar manner to Windows. The system dispatches event messages whenever something happens like the mouse moving, a button being clicked or a window needs redrawing and so on. On Windows you would have to 'pump the message queue' using GetMessage() and DispatchMessage(). With Tkinter this is handled in the mainloop() function (for both Windows and X).

What is the difference between root.destroy() and root.quit()?

In Python using tkinter, what is the difference between root.destroy() and root.quit() when closing the root window?
Is one prefered over the other? Does one release resources that the other doesn't?
root.quit() causes mainloop to exit. The interpreter is still intact, as are all the widgets. If you call this function, you can have code that executes after the call to root.mainloop(), and that code can interact with the widgets (for example, get a value from an entry widget).
Calling root.destroy() will destroy all the widgets and exit mainloop. Any code after the call to root.mainloop() will run, but any attempt to access any widgets (for example, get a value from an entry widget) will fail because the widget no longer exists.
quit() stops the TCL interpreter. This is in most cases what you want, because your Tkinter-app will also stop. It can be a problem, if you e.g. call your app from idle. idle is itself a Tkinker-app, so if you call quit() in your app and the TCL interpreter gets terminated, idle will also terminate (or get confused ).
destroy() just terminates the mainloop and deletes all widgets. So it seems to be safer if you call your app from another Tkinter app, or if you have multiple mainloops."
taken from http://www.daniweb.com/forums/thread66698.html
The tkinter.Tk "quit" method exits the "mainloop" event-handler, and "destroy" destroys all the embedded widgets and only then exits the "mainloop". So is "destroy" the better of the two? Well, sometimes not. If "destroy" fails to destroy all the widgets for some reason, then "mainloop" is never exited and Python locks up. It can be better to just let Python shut things down in an orderly manner at the end of the script.
For example, if you embed a Matplotlib plot in a Tkinter window, that is useful because Matplotlib's own widgets are somewhat clunky to use. Unfortunately, if you then try to close the window by clicking the usual "X" in the title-bar, the window closes alright but leaves Python running. If the script had been started from a terminal, you'd have to mash Ctrl-C for a couple of minutes to get the prompt back. The reason being that the window-close event is bound to "destroy" which does not destroy the Matplotlib objects but leaves them orphaned.
The fix is to bind the window-close event to "quit" instead. But... if the script is started in a Tkinter-based IDE like IDLE, then that creates a new problem in that the window does not close because IDLE holds Tkinter running. So now "destroy" must be added after the mainloop. Finally, all is well.
Below is a minimal example of a Matplotlib plot that can be inverted with a press of a Tkinter button. Its window can be closed without problems. But if the windows-close event had been bound to "destroy" instead of "quit" then a locked-up Python process would remain.
#!/usr/bin/env python3
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
root = tk.Tk()
data, = plt.plot([0,5,3,4,-5,3])
canvas = FigureCanvasTkAgg(plt.gcf(), master=root)
invert = lambda: (data.set_ydata(-data.get_ydata()), canvas.draw())
tk.Button(master=root, text="Invert", command=invert).pack()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=1)
root.protocol("WM_DELETE_WINDOW", root.quit)
root.mainloop()
root.destroy()
Edit: I'll add that by binding the window-close event to both methods, you can avoid adding a line after the "mainloop", should that be desirable for some reason:
root.protocol("WM_DELETE_WINDOW", lambda: (root.quit(), root.destroy()))
My experience with root.quit() and root.destroy() ...
I have a dos python script, which calls a tkinter script (to choose from set of known values from combobox), then returns to the dos script to complete other things.
The TKinter script I've added onto the parent script. I may convert all to tkinter, but a combo works for the time being. It works in the following way:
To get rid of the windows box after selection was implemented, I needed to
1) root.quit() inside the callback function where all my keypresses were being processed.
2) root.destroy() after mainloop to destroy the windows box.
If I used root.destroy() inside the callback, I got an error message saying tkinter objects were no longer accessable.
Without the root.destroy() after mainloop, the windows box STAYED ONSCREEN until the whole parent script had completed.

Categories