Tkinter bind copy event needs to work in background as well - python

I have a simple copy paste event, which should trigger a function while my program runs:
root = tkinter.Tk()
root.bind('<Control-c>', parse_item)
root.mainloop()
parse_item grabs the contents of the clipboard and makes some calculations.
The problem I have is:
It only works if my program is in focus. I need to be with the mouse in the program, then the bind event will trigger.
What I need:
The bind event should also trigger if the program is not in focus (for example the program is minimized). It should always trigger while the program runs.

It only works if my program is in focus. I need to be with the mouse in the program, then the bind event will trigger.
You cannot do that with tkinter. It can only operate on events in widgets that it controls, and only when it has focus. You would have to use a platform-specific tool to intercept events from other programs.

Related

How do I make a program listen for keyboard events in the background?

I'm making a program that will do multiple keyboard actions when I press my own shortcut (ctrl+q). How do I make my program listen to the binds when the program is in the background?
def pasteFun(event):
messagebox.showinfo("hey")
root.bind("<Control-q>", pasteFun)
This works fine when I am in the program, but when I minimize it, ctrl+q does nothing.
def test(event):
messagebox.showinfo("hey","hey")
root.bind_all("<Control-q>",test)
I have tried root.bind, frame.bind, and root.bind_all, but my bind does nothing when the program is in the background/minimized.
I need a function to run when I'm outside of the program and hit my binds/shortcuts.
I'm making a program that will do multiple keyboard actions when I press my own shortcut (ctrl+q). How do I make my program listen to the binds when the program is in the background?
You can't do that with tkinter. Tkinter will only see events when it has focus. That is a fundamental part of its design.

Key Press Connect at Start of Execution

I have a Python 3.6.3 script that incorporates PyQt5. The GUI has a button that toggles play and pause of a video file. Pressing the spacebar also toggles play and pause. The button works fine all the time. The spacebar also works as intended, except at the very start of executing the script. I need to click some other event first (such as a button or slider bar) before the spacebar will correctly triggers the signal to pause or play the video. I want to start playing the video with the spacebar when the script starts up. Here is how I create the connections in the same __init__(self) function . As you can see, clicking on the button and pressing the spacebar both connect the signal to the same function (self.pauseVideo).
self.btnPause = QtWidgets.QPushButton(self.centralwidget)
self.btnPause.clicked.connect(self.pauseVideo)
self.spacebar = QShortcut(QKeySequence(Qt.Key_Space), self)
self.spacebar.activated.connect(self.pauseVideo)
Suggestions on how to get the spacebar to trigger the signal when the script first starts?
After a lot more searching, reassigning keyboard shortcuts (without success), etc., I found this post PyQt widget keyboard focus. I inserted
self.setFocusPolicy(Qt.StrongFocus)
into __init__(self) and it seemed to solve my problem! Now I'm just hoping it doesn't cause other issues.

How to set tkinter window focus out?

I am trying to do make a virtual keyboard using tkinter. Is there any method that allow tkinter window focus out? For example in java we can have setFocusableWindowState(false)
Thank you very much for your help.
I believe you can accomplish what you want with tkinter, but it's not about not getting focus. I don't think, that other GUI tools will make it any easier.
It's part of operation system, or more precisely window manager to give focus to some window, when it is clicked. So, in case of virtual keyboard:
User has focus in text editor (for example).
User presses a button on your virtual keyboard.
OS/Window manager gives focus to your keyboard window and sends mouse click event to the GUI library (tkinter).
Here you need to identify where was the focus before your window got it, i.e. get the text editor window handler somehow.
Send the button press event to that window and return focus.
Repeat.
You'll use tkinter to draw the keyboard window and handle mouse clicks/touches on virtual keyboard keys. But you'll need to work with OS/Window manager to identify other windows by handlers and send keypress events to them. May be you will be able to prevent focus switch by working with OS/Window manager, but it's not tkinter or other GUI library functionality.

Python Tkinter and NetworkX freezes after moving mainloop to own Thread

I'm making a simple window to hold my NetworkX graph, and I want this GUI component to be separate from my logic. So what I'm trying to achieve is to make a function that makes the window, initializes the graph, starts the mainloop in a Thread and returns the gui reference. I've done this exact thing without networkX and canvas involved, and it works great, but now this doesn't work for some reason. What I have is:
def getNewGraphWindow():
root = Tk()
app = GraphUI(root)
root.mainloop()
#mainThread = Thread(target=root.mainloop)
#mainThread.start()
return app
So this code works perfectly, it creates the window, draws the graph and all that, but obviously I don't get the "app" reference cause its stuck in the mainloop. But if I replace that code with the 2 commented out lines, to simply run root.mainloop() in its own thread, all the code runs, but the window doesn't respond and the graph is not drawn. What's wrong?
You have to run the mainloop in the thread where you created the Tcl interpreter, according to the _tkinter source code:
The Tcl interpreter is only valid in the thread that created it, and
all Tk activity must happen in this thread, also. That means that the
mainloop must be invoked in the thread that created the interpreter.
So you'll need to run the mainloop in the main thread, and do whatever else you need to do in a background thread. You actually might be able to run the mainloop in a background thread, as long as you also create the Tk element in that same thread.

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