I'm making a tkinter app and I need to know if my windows had focus, because I will send notifications only if the windows don't have focus. I check the root protocols but I didn't find something suitable.
This question has already been answered, but the accepted answer is more complicated than it should be.
I think the cleanest solution to this problem would be the following:
def has_focus(window):
return window.focus_displayof() # returns None if the window does not have focus
if not has_focus(root):
# do something
There can be several methods of doing this depending upon how you what the function to trigger.
Let's say you want the notification to go when the window loses focus then with the help of <FocusOut> bind we can do so
...
def send_notification(*args):
"""triggers when the window loses focus."""
...
root = tk.Tk()
root.bind('<FocusOut>', send_notification)
...
Or Let us the notification function trigger different times even if the window has focus or not then we can check in the function like so
def send_notification(*args):
"""triggers when the window loses focus."""
if not focus_check.get():
...
root = tk.Tk()
focus_check = tk.BooleanVar()
root.bind('<FocusIn>', lambda _: focus_check.set(True))
root.bind('<FocusOut>', lambda _: focus_check.set(False))
I found a StackOverflow question that seems fairly similar to yours. In essence, you need to add a binding to your Tkinter application. This will only function if your application has focus. You can also use the focus_get command to figure out if your obejct has focus if you apply it to the root object.
Here's the code the answer from 2009 used: e1.bind("<Return>", handleReturn)
Related
I am working on my gui with tkinter, and I have to place stuff in precise places, i can't just use pack. Currently i write almost random numbers for width and height in place(), then run, adjust the numbers, repeat the program and check the position until the button or label fits just perfectly.
Isn't there a simpler way? Is there an option of the python launcher that allows me to point somewhere in the window and find out the coordinates?
Or maybe an extention for vcscode or a setting for pycharm?
This is about the simplest way of getting mouse coordinates
from tkinter import Tk
root = Tk()
root.bind('<Button-1>', lambda e: print(e.x, e.y))
root.mainloop()
basically .bind() binds an event (as the name suggests) to the widget and when the event is triggered .bind() method calls the given function while also passing an event argument so that has to be dealt with but it can also be used as in this case (and in most cases actually)
P.S. e is just a preference of mine when using lambda in this case and if I were to define a function as usually I would use event as argument name
As #Matiiss stated in his answer, using can use mouse coordinates.
Also, there is another event called <Motion> which you can use.
from tkinter import *
root=Tk()
lbl=Label(root,text='')
lbl.pack()
lbl1=Label(root,text='')
lbl1.pack()
root.bind("<Motion>",lambda e: [lbl.config(text=f"X pixel: {e.x}"),lbl1.config(text=f"Y pixel: {e.y}")])
root.mainloop()
I have a class Duplicates that checks for duplicates within 40 words.
I have a class Window that creates and runs the main window where i post the result.
I have a class popWindow that creates a Toplevel window when asking user for what to do with a possible double.
My problem is closing the popWindow once a choice is submited.
the version I have that actualy runs and posts an aswer (the text with marked duplicates) uses quit to terminate the window (meaning the popup is still there in the way) or to simply have multiple popups till you are done.
class Duplicates:
def markWord(self):
self.appendMarkedWord(self.word)
self.checked.append(self.word)
self.pop.topLevel_exit()
return ""
class popUpWindow:
temp = Button( self, font = 8,
text = "Allowed this run only",
command = app.newFile.markWord
)
temp.place( x = 178,
y = 55
)
if I instead use .destroy() the window shuts but the program stops running and that is worse.
How do i work around this so it shuts the window but still continues to run the program?
Ok, after many many hours it seemed the real problem was destroy() was not stopping my popUpWindow.mainloop() so I now have altered my exit code to first do quit() and then do destroy(). This is not what i have seen as examples at all and it seems to me that destroy() on toplevel mainloop is not terminating it (destroy() works fine on my root.mainloop).
def topLevel_exit(self):
self.pop.quit()
self.pop.destroy()
If you call destroy() on a toplevel window, it will not stop the application from running. If your application stops, there must be more to your code that what you're telling us. Without question, the right way to get rid of the popup is to call destroy on the instance of the Toplevel.
A way to hide the window and keep the program running would be to use .withdraw() on the window, and .reiconify() to get it back (if needed). Or you could use .destroy() on a Toplevel window. If you need examples just ask, hope this helps you.
The solution for me was:
def topLevel_exit(self):
self.top.quit()
self.top.destroy()
I do not know if this is common praxis but is what I had to do since destroy was not stoping my top.mainloop()
If you use a topLevel window, self.pop.destroy() should still work as you are using mainloop()
Otherwise use quit() or both but in my opinion of all of these, I prefer destroy()
In Python, is there a way to bind every event generated by an object (perhaps a tkinter widget) to a single function, without explicitly naming them? The motivation here is for learning, debugging and development purposes.
(This question has arisen whilst trying to find a solution to this.)
You can bind to all of the keys like this:
def callback(event):
print(event.char, event.keysym, event.keycode)
root = Tk()
root.bind('<Key>', callback)
root.mainloop()
I'm running Python 3.3.3 (and right now I'm on Ubuntu but I also develop on Mac and Windows, which I haven't yet tested). I have a Treeview object that responds to right click on items and shows a context menu depending on what you click... but I've noticed that if you right click somewhere else while the original menu is up, it just opens another one.
In fact, normal clicking doesn't hide them either. Even when I close the window the menus still stay floating. The only way to get them to go away is to click one of the options.
The end result is this:
My code for the menu is as follows:
def rightclick_listitem(self, event):
rowitem = self.sources.identify('item', event.x, event.y)
if rowitem == '':
print('Right clicked an empty space.')
return
# user right clicked something.
self.sources.selection_set(rowitem)
rcmenu = Menu(self.root, tearoff=0)
plugin_disabled=self.sources.item(rowitem, 'values')[0] == 'Disabled'
if plugin_disabled:
rcmenu.add_command(label='Plugin is disabled...',
command=self.plugin_disabled_click)
rcmenu.add_command(label='Plugin options',state='disabled' if plugin_disabled else 'active')
rcmenu.add_command(label='Uninstall plugin')
rcmenu.post(event.x_root, event.y_root)
The code that calls this code is located here:
#RIGHTMOUSE is a variable that changes based on OS due to the way Mac OSX works
#sources is the treeview object
self.sources.bind(RIGHTMOUSE, self.rightclick_listitem)
I googled around and only got some people asking the same question with no answers. I'm still somewhat new to tkinter and python in general, and didn't see anything about this. I bind other actions to the treeview as well.
If you need more sourcecode my project is here: https://github.com/Mgamerz/Fresh-Set-of-Images (freshsetofimages.py)
Any help is appreciated.
And the plugins required to make this appear: https://github.com/Mgamerz/fsoi_plugins
Try calling the method tk_popup rather than post.
Also, your code has a memory leak, in that each time you right-click you're creating a new menu but never destroying the old one. You only ever need to create one, and the reconfigure it before popping it up.
To close the popup menu when click elsewhere, you can add
rcmenu.bind("<FocusOut>",popupFocusOut)
and call unpost in popupFocusOut.
def popupFocusOut(self,event=None):
rcmenu.unpost()
I'm writing a 'wizard' type Python Tkinter GUI that collects information from the user and then performs several actions based on the user's entries: file copying, DB updates, etc. The processing normally takes 30-60 seconds and during that time, I want to:
Provide the user with text updates on the activity and progress
Prevent the user from closing the app until it's finished what it's doing
I started on the route of having the text updates appear in a child window that's configured to be trainsient and using wait_window to pause the main loop until the activities are done. This worked fine for other custom dialog boxes I created which have OK/cancel buttons that call the window's destroy method. The basic approach is:
def myCustomDialog(parent,*args):
winCDLG = _cdlgWin(parent,*args)
winCDLG.showWin()
winCDLG.dlgWin.focus_set()
winCDLG.dlgWin.grab_set()
winCDLG.dlgWin.transient(parent)
winCDLG.dlgWin.wait_window(winCDLG.dlgWin)
return winCDLG.userResponse
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.userResponse = ''
def showWin(self):
#Tkinter widgets and geometry defined here
def _btnOKClick(self):
#self.userResponse assigned from user entry/entries on dialog
self.dlgWin.destroy()
def _btnCancelClick(self):
self.dlgWin.destroy()
However this approach isn't working for the new monitor-and-update dialog I want to create.
First, because there's no user-initiated action to trigger the copy/update activities and then the destroy, I have to put them either in showWin, or in another method. I've tried both ways but I'm stuck between a race condition (the code completes the copy/update stuff but then tries to destroy the window before it's there), and never executing the copy/update stuff in the first place because it hits the wait_window before I can activate the other method.
If I could figure out a way past that, then the secondary problem (preventing the user from closing the child window before the work's done) is covered by the answers below.
So... is there any kind of bandaid I could apply to make this approach work the way I want? Or do I need to just scrap this because it can't work? (And if it's the latter, is there any way I can accomplish the original goal?)
self.dlgWin.overrideredirect(1) will remove all of the buttons (make a borderless window). Is that what you're looking for?
As far as I know, window control buttons are implemented by the window manager, so I think it is not possible to just remove one of them with Tkinter (I am not 100% sure though). The common solution for this problem is to set a callback to the protocol WM_DELETE_WINDOW and use it to control the behaviour of the window:
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.dlgWin.protocol('WM_DELETE_WINDOW', self.close)
self.userResponse = ''
def close(self):
tkMessageBox.showwarning('Warning!',
'The pending action has not finished yet')
# ...