Key Event Handling using Tkinter in Python - python

check the following link out :
[PyObjC Key Event Handling Question] Key Events Handling using PyObjC in Mac OS X
This was my initial question. I somehow managed to find a built-in plugin to solve the Key Event Management, but using Python. It is called Tkinter.
from Tkinter import *
root = Tk()
def screenshot(*ignore): os.system("screencapture -s %s" % check_snapshot)
root.bind('<Return>', greet)
root.mainloop( )
On pressing return (enter) key, it would successfully call screenshot function, and it would work.
Now, what I am looking for is, whenever I press combination of keys, like Command+Shift+4, the above function should be call.
This should be done in the same manner for Command+Shift+3 and Command+Shift+5 as well.
This should be done by checking which combination of keys are pressed, and accordingly, their respective screenshot functions should be called.
Also, this app shortcuts shouldn't be just relied on this app's window or frame, the window / frame of this window shouldn't be visible, yet, the shortcuts should work and trigger their respective functions.
root.withdraw()
This is the built-in function which hides the Tkinter window, but then, I am unable to invoke any of the functions. These functions only work on Tkinter window, or else, keys shortcuts don't work.
Any help would be appreciated.

Tkinter events only work when the tkinter window has focus,and for it to have focus it must be visible. You cannot use tkinter to handle events while another program is in the foreground.
The format of an event is <modifier-modifier-event-detail>, with modifier and event being optional. Event is something like KeyPress, ButtonPress, ButtonRelease and so on. Detail gives more detail, such as which key, or which button. For example, <ButtonRelease-1> is for releasing mouse button 1 (one).
Modifier is where you specify control, alt, delete or shift, and you can have more than one. Shift is a bit special, because it is often interpreted by the OS before tkinter ever sees is. So, for example, "Shift-3" on an American English keyboard is "#". Thus, instead of <Shift-3> you would use <#>.
Putting that all together, command-shift-3 would be <Command-#>. However, if you do that on a Mac, it will intercept that event and do a screenshot, so the binding will only work on Windows and Linux. On each OS there are a few key bindings you cannot override.
The best description of the format to use for specifying events is the tcl/tk man page on bind. Even though you're asking about tkinter, the underlying engine is tcl/tk.

Related

Tkinter Listbox - How to disable arrow key selection?

I have a Python 3.7 tkinter GUI, and within the GUI I have implemented up-down arrow key controls for the main part of the application. Next to it I have a list box that also controls the application but in a different way, and by default AFTER a listbox selection has been made the listbox selection will scroll with up and down arrows. So, after I've used the list box in the app, the arrow key triggers an up arrow event in both the main part of the application and in the list box. This triggers my application to respond in the change in list box selection by loading new data into the main app. This is obviously unacceptable.
How can I disable the arrow key controls feature of tkinter's
ListBox?
I've tried configuring the listbox to not take focus, but this doesn't seem to disable the feature.
Edit:
I solved this by binding the list box's FocusIn event to a function that immediately focus's something else. This is far from ideal, as the code now focus's in then changes focus for no reason. If there is a way to disable focus on a widget completely or disable the list box key bindings that would be a preferred solution.
from tkinter import *
class App:
def __init__(self):
self.root = Tk()
self.dummy_widget = Label()
self.lb = ListBox(master=self.root)
self.lb.bind("<FocusIn>", lambda event: self.dummy_widget.focus())
# Additional setup and packing widgets...
if __name__ == '__main__':
mainloop()
This seems very "hacky", although it does the job perfectly.
How can I disable the arrow key controls feature of tkinter's ListBox?
Create your own bindings for the events you want to override, in the widget in which you want them overridden. Do anything you want in that function (including nothing), and then return the string break, which is the documented way to prevent events from being processed any further.
For a more extensive description of how bindings work, see this answer to the question Basic query regarding bindtags in tkinter. It describes how a character is inserted into an entry widget, but the mechanism is identical for all events and widgets in tkinter.

Tkinter session active whilst tabbed out?

The title may be a touch vague, but I'd like to make a Tkinter session in Python 3 be active whilst it is tabbed out.
Say I have a function bound to a keypress in my code, and this is built in Tkinter, is there a way for me to keep this keypress function active when I'm on other windows?
In short, no. Tkinter only processes keyboard events when it has the keyboard focus. If you want to capture all keypresses no matter which app has focus, you'll have to use something else, like pyhook if you're on windows. Other platforms will require other platform-specific solutions.

How to 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 - the tk window is not displayed nor is there a button on the screen 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.
Again, after entering this code nothing visible happens. I deleted the code relating to creating the button and still nothing happens so I am not sure where to start diagnosing the issue I have Googled for information and found nothing useful
Any insight would be appreciated.
I did ask this question on superuser but there is no Tkinter tag so . . .
humm do I need a
tk.pack()
statement - I will report back.
No, you do not need tk.pack(). What you do need is start the event loop. The event loop, as it's name suggests, is a loop that processes events. Everything in Tkinter happens as a response an event, including the actual drawing of a widget or window on the screen.
As the last line in your file, add the following:
tk.mainloop()
I encourage you to not do the import the way you are doing. I know a lot of tkinter tutorials do it that way, but it's a bad thing to do. Instead, do it like this:
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text='click me')
btn.pack()
root.mainloop()
It requires typing three extra characters for every widget, but in exchange you get code that is easier to maintain over time.
PEP8 is the official python style guide, and it explicitly recommends against wildcard imports:
Wildcard imports (from import *) should be avoided, as they
make it unclear which names are present in the namespace, confusing
both readers and many automated tools. There is one defensible use
case for a wildcard import, which is to republish an internal
interface as part of a public API (for example, overwriting a pure
Python implementation of an interface with the definitions from an
optional accelerator module and exactly which definitions will be
overwritten isn't known in advance).
See http://legacy.python.org/dev/peps/pep-0008/#imports

Qt - Temporarily disable all events or window functionality?

I have a Qt program with many buttons, user-interactable widgets, etc.
At one stage in the program, I would like all the widgets to temporarily 'stop working'; stop behaving to mouse clicks and instead pass the event on to one function.
(This is so the User can select a widget to perform meta operations. Part explanation here: Get variable name of Qt Widget (for use in Stylesheet)? )
The User would pick a widget (to do stuff with) by clicking it, and of course clicking a button must not cause the button's bound function to run.
What is the correct (most abstracted, sensible) method of doing this?
(which doesn't involve too much new code. ie; not subclassing every widget)
Is there anything in Qt designed for this?
So far, I am able to retrieve a list of all the widgets in the program (by calling
QObject.findChildren(QtGui.QWidget)
so the solution can incorporate this.
My current horrible ideas are;
Some how dealing with all the applications events all the time in one
function and not letting through the events when I need the
application to be dormant.
When I need dormancy, make a new transparent widget which recieves
mouse clicks and stretch it over the entire window. Take coordinates
of click and figure out the widget underneath.
Somehow create a new 'shell' instance of the window.
THANKS!
(Sorry for the terrible write-up; in a slight rush)
python 2.7.2
PyQt4
Windows 7
You can intercept events send to specific widgets with QObject::installEventFilter.
graphite answered this one first so give credit where credit is due.
For an actual example in PySide, here's an example you might draw some useful code from:
my_app.py
from KeyPressEater import KeyPressEater
if __name__ == "__main__":
app = QApplication(sys.argv)
eater = KeyPressEater()
app.installEventFilter(eater)
KeyPressEater.py
class KeyPressEater(QObject):
# subclassing for eventFilter
def eventFilter(self, obj, event):
if self.ignore_input:
# swallow events
pass
else:
# bubble events
return QObject.eventFilter(self,obj,event)

On-value-change type of event for widgets or use .trace_variable() technique?

Is there an on-value-change type of event for data input widgets like Entry, Text, Spinner, Checkbutton, Radiobutton? By on-value-change, I mean the ability to detect when the value of a widget has changed due to keyboard input or cut/delete/paste (and Text edit_undo/edit_redo) activity? I see no such event described in the Tkinter event documentation [1].
Is the proper technique to link Tkinter variables to widget values I want to monitor and use these variables' .trace_variable( 'w', ... ) methods to bind to value changes? This seems like the right approach, but I haven't seen a lot of trace_variable() use in the Tkinter application source code that I've studied ... leading me to be cautious about using this approach.
[1] http://infohost.nmt.edu/tcc/help/pubs/tkinter/events.html
Different widgets call for different solutions. For example, check buttons and radio buttons have a command option, and with an entry widget you can use the built-in validation features.
For all the widgets that can be tied to a variable, doing a variable trace is a common solution. The text widget is one exception since you can't associate it with a variable without a lot of effort.
In the tcl/tk world I associate all my widgets to a single array (tcl's name for a hash map / dictionary) and then put a single trace on the array. Unfortunately tkinter doesn't directly support tcl arrays. However, support is somewhat easy to hack in. For more information see my response to this question: How to run a code whenever a Tkinter widget value changes?

Categories

Resources