I'm building a console app, and would like to capture keystrokes in real time. The following code works perfectly until another window gets focus. From that point on, I'm not able to get back to a state where I can capture keystrokes and other events again with only the console visible.
import tkinter as tk
app = tk.Tk()
def handleKeypress(event):
key = event.char
if(key == 'q'):
app.destroy()
else:
print(key)
app.bind_all('<Key>', handleKeypress)
app.withdraw()
app.mainloop()
I've tried using various methods (grab and focus) to redirect the focus to my app. The best I was able to do was to get the Tkinter window visible and in focus with deiconify(), but I was not able to hide it again to make it as though the console is the only window.
Adding the following results in the Tkinter window appearing and disappearing repeatedly:
def lostFocus(event):
app.deiconify()
app.focus_force()
app.withdraw()
app.bind_all('<FocusOut>', lostFocus)
How can I go back to the state the application was in right after launch? Or even better, how can I force it to get all events without having to make the Tkinter window visible and in focus?
You can't do what you want. Tkinter is designed -- as are most GUI toolkits -- to only process events when it has the focus. That's the whole point of focus: for the OS to know where to send events.
The fact that it works initially is probably a bug in tkinter. Though, perhaps it can be explained by the fact that the window initially has focus, and when you withdraw the window the OS doesn't move the focus
The only way to restore focus is to make the window visible.
Related
Picture of my GUI for clarity
I made a tool that plays audio from youtube. I want it to pause when the spacebar is pressed. I do this by this piece of code:
win.bind('<space>',lambda event:funcPP(player, btnPP))
win is my window, funcPP is the play/pause function.
My problem is that I have also an Entry in my window (for searching videos) and of course it is impractical if the video pauses everytime when I search a new video and press Spacebar. And the real problem is that, once I have clicked the Entry field, the focus stays there. It doesn't go away after clicking somewhere else! This sabotages my Spacebar shortcut.
Any tips how I can solve this?
I found the solution.
With:
win.bind_all("<Button-1>", lambda event: event.widget.focus_set())
It is possible to put the focus whereever one clicks.
then:
win.bind('<space>',lambda event:funcPPTRANSFER(player))
To bind spacebar to the whole window
then:
def funcPPTRANSFER(player):
if str(win.focus_get()) != str(".!entry"):
funcPP(player)
Have the function check whether the spacebar was sent from the entry or from somwhere else in the window
EDIT: Another thing I found now:
https://stackoverflow.com/a/56519799/18664063
if isinstance(event.widget,tk.Tk): #check if event widget is Tk root window
very nice function that might help as well in this project.
I am trying to activate a function in python which will save a timestamp to a file. However, I wish to activate that function after the first mouse click that accurses outside the Tk window.
For example, after running the program I will minimize it and press on the chrome browser icon and while doing so my program will log the time stamp.
I tried to use the bind function but it works for clicks that accrue only in the Tk window
By the way, I am currently using Tkinter as my GUI platform however if there is a way to do it with another libraries please share and I will adjust my program
thanks :)
Use the <FocusOut> event:
import tkinter as tk
root = tk.Tk()
def focus_lost(event):
print("Clicked outside the window")
root.bind("<FocusOut>", focus_lost)
root.mainloop()
If you are focussed on the tkinter window (e.g. if you have clicked on it), then when you click elsewhere it will detect this as a <FocusOut> event.
So I have a script running inside another program (The Foundry's Hiero) and I'm just making a new QWidget object, and calling self.show()
Now, I can set it to self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint), so my window will stay on top of the main window, even if you click on something in the main window.
The problem is, this is a sort of popup window that you configure settings in, and it triggers other QWidget popups. If I set my window to WindowStaysOnTopHint, those subdialogs that my widget triggers end up beneath my widget.
Is there a way in PySide/PyQt to make a window stay on top/keep focus from the main application window in particular, but not everything?
You can use the QApplication.focusChanged signal to raise your widget up when Hiero's main window is selected. Then you would just need to remove the WindowStaysOnTopHint flag.
I'm not familiar with Hiero's API, but I'm guessing you could try something like:
def raiseMyWidget(old, new):
if new == hiero.ui.mainWindow():
myWidget.raise_()
QtWidgets.QApplication.instance().focusChanged.connect(raiseMyWidget)
Hope this helps! You can take advantage of the old parameter or some other means to make sure that your widget isn't raised above the others as well.
I am trying to make a window still visible after it has lost focus. An example of what I'm trying to achieve is a window like the windows on-screen keyboard, when you click on another window, it doesn't go ontop of the on-screen keyboard window. Can anyone give me a way to do this in tkinter?
I'm working on a graphical app, and at the start of the run I'd like to ask the user a single configuration question. The graphical framework (Panda3D) has ugly default dialog boxes, so I'd like to use something like tkInter to provide a modal dialog. I tried this:
import Tkinter
import tkMessageBox
root = Tkinter.Tk()
# hide the root window
root.withdraw()
config.PLAY_MUSIC = tkMessageBox.askyesno( "My App",
"Would you like this app to play music from your iTunes collection?" )
root.destroy()
This does what I want it to, but it appears to route all further keyboard events to tkInter rather than my Panda3D app. I don't need to do anything further with tk after this dialog.
I can put the tk dialog into a separate app that chains onto mine, I suppose, but I'm wondering if there's a way to kill tk and get the keyboard back without exiting my app entirely.
Update: Tried root.quit(), which does seem to get the keyboard back, but I get a "Fatal Python error: PyEval_RestoreThread: NULL tstate" crash on exit from my program, which isn't ideal.
Have you tried:
grab_release(self)
Which does: Release grab for this widget if currently set.
Where "A grab directs all events to this and descendant
widgets in the application."
as in:
root.grab_release()
Hope you haven't tried this one.