Check for tkinter events globally (across OS) - python

I trying to make an application with a pop-up menu - when I type SPACE-R_ALT on my keyboard, globally across the OS (Windows in my case). When that happens, I want to pop-up a window (I know how to do that), and it is crucial that I can happen to be using Chrome or Word, then tap Space-Right Alt, then be able to open up this little menu.
Tkinter event bindings have two problems:
First, when I use an event binding for <Key> and then, in the
function, use evt.keysym, I can see that the program can't register
both at the same time. I could use a timer and then see if it works,
but I would prefer one line that fixes it all.
Second, I find that tkinter event bindings only work when the binded widget's window (or window itself) is FOCUSED. I will hide my root and TopLevel at all times, and so is not focused.
I would appreciate any help on this. If your suggestion uses another module, I don't really care, as long as it works on Windows 10 (not Mac OS X, not Linux, but Windows). I'm also using Python 3, but any version (aka 2) would also be okay, as I could either try to port YOUR suggestion to Py3, or port MY code to Py2. Thanks!

I figured it out with Furas's help - with Pyhook I can wait for events globally, and then tie in the event with tkinter events.

Related

Create a delay between mouse clicks using Autokey

I am about to switch from Windows to Ubuntu. Since my mouse keeps doing multiple clicks each time I press the middle mouse button, I used AutoHotkey under Windows to add a delay after each click. This worked fine. Now under Ubuntu I want to use AutoKey to do the same. Autokey uses Python for its scripts though.
Here is the AutoHotkey script:
MButton::
If (A_TimeSincePriorHotkey < 200)
Return
Send {MButton}
Return
Currently, (as of version 0.95.4), this is not possible from within AutoKey, because it can’t handle mouse buttons as hotkeys.
This stackoverflow question may be of help: Triggering AutoKey Script via Mouse Button - How To?

Python - How to make a daemon out of GUI Application on Mac OS X?

On Windows it is easy. Just run your program with pythonw instead with python and code will be executed in the background.
So, the thing I wish to achieve is easily arranged.
I have an application which is really a service doing underground stuff. But this service needs a control panel.
So, on Windows I use wxPython to create a GUI, even some wx stuff to provide needed service, and when user is done with adjustments she/he clicks Hide and Show(False) is called on main window.
Thus the GUI disappears and the service continues its work in the background. User can always bring it back using a hotkey.
The trouble is that on Mac OS X this strategy works only to some degree.
When wx.Frame.Show(False) is called, the window disappears along with its menu bar and service works fine, but the Application is still visible there.
You can switch to it regardless the fact that you cannot do anything with it. It is still present in the Dock etc. etc.
This happens when program is using python or pythonw or when it is bundled with Py2App.
No matter what I do, the icon stays there.
There must be some trick that allows a programmer to remove this naughty icon and thus stop bothering poor little user when she/he doesn't want to be bothered.
Hiding window is obviously not enough. Anyone knows the trick?
N.B.: I would really like to do it the way I described above and not mess with two separate processes and IPC.
Edit:
After much digging I found these:
How to hide application icon from Mac OS X dock
http://codesorcery.net/2008/02/06/feature-requests-versus-the-right-way-to-do-it
How to hide the Dock icon
According to last link the proper way to do it is to use:
[NSApp setActivationPolicy: NSApplicationActivationPolicyAccessory];
or
[NSApp setActivationPolicy: NSApplicationActivationPolicyProhibited];
So what I want (runtime switching from background to foreground and back) is possible.
But how to do it from Python???
Constants: NSApplicationActivationPolicyProhibited and NSApplicationActivationPolicyAccessory are present in AppKit, but I cannot find setApplicationActivationPolicy function anywhere.
NSApp() doesn't have it.
I know there is a way of doing it by loading objc dylib with ctypes, delegating to NSApp and sending "setApplicationActivationPolicy: <constant_value>", but I don't know how much will this mess with wx.App(). And it is a bit much work for something that should be available already.
In my experience, NSApp() and wx.App() active at the same time dislike eachother pretty much.
Perhaps we can get the NSApp() instance that wx is using somehow and use wx's delegate???
Remember please, already suggested solutions with starting as agent and switching to foreground or running multiple processes and doing IPC is very undesirable in my case.
So, ideally, using setApplicationActivationPolicy is my goal, but how? (Simple and easy and no messup to wx.App() please.)
Any ideas???
OK people, there is a good, nice and correct solution without any messing around.
Firstly, I want to explain why Windows GUI process goes to background when wx.Frame.Show(MyFrame, False) is called.
Very short explanation and skipping over details is that Windows consider the Window and an application the same thing.
I.e. The main element of the MS Windows application is your main GUI window.
So, when this window is hidden, an application has no more GUI and continues to run in background.
Mac OS X considers the application to be your application and any windows you choose to put into it are its children so to speak.
This allows you to have an application running while presenting no windows but a menu bar, from which you may choose an action which would then generate a needed window.
Very handy for editors where you may have more than one file opened at once, each in its own window and when you close the last one, you can still open a new one or create a blank one, etc. etc.
Therefore a main element of Mac OS X application is the application itself, and that is why it stays opened after last window is hidden, logically. Destroying its menu bar also will not help. The name of the app will stay present in Dock and in application switcher and in Force Quit. You will be able to switch to it and do nothing. :D
But, luckily, Mac provides us with function to put it to background though. And this function is already mentioned setApplicationActivationPolicy() from NSApp object.
The trouble was its naming in Python's AppKit, which is NSApp.setActivationPolicy_(). To complicate matters further, it is not available directly from Python's interactive shell but it has to be called at least from an imported module.
Why? I have no idea. Anyway here is a complete example for throwing an application into background that will work on Mac and Windows.
I didn't try it on Linux, which combines behaviour of Mac and Windows in matter of presenting an app, so, whether only hiding a window would be enough remains to be seen.
Feel free to try and submit an edit to make the example more cross-platform.
Example:
"""
This app will show you small window with the randomly generated code that will confirm that reopened window is still the same app returned from background,
and the button allowing you to send it to background.
After you send it to background, wait 8 seconds and application will return to foreground again.
Too prove that the application is continuing its work in the background, the app will call wx.Bell() every second.
You should hear the sound while app is in the foreground and when it is in background too.
Merry Christmas and a happy New Year!
"""
import wx
import random, sys
if sys.platform=="darwin":
from AppKit import NSBundle, NSApp, NSAutoreleasePool, NSApplicationActivationPolicyRegular, NSApplicationActivationPolicyProhibited
# Use Info.plist values to know whether our process started as daemon
# Also, change this dict in case anyone is later checking it (e.g. some module)
# Note: Changing this dict doesn't change Info.plist file
info = NSBundle.mainBundle().infoDictionary()
def SendToBackground ():
# Change info, just in case someone checks it later
info["LSUIElement"] = "1"
NSApp.setActivationPolicy_(NSApplicationActivationPolicyProhibited)
def ReturnToForeground ():
# Change info, just in case someone checks it later
info["LSUIElement"] = "0"
NSApp.setActivationPolicy_(NSApplicationActivationPolicyRegular)
else:
# Simulate Mac OS X App - Info.plist
info = {"LSUIElement": "0"} # Assume non background at startup
# If programmer chose not to display GUI at startup then she/he should change this before calling ReturnToForeground()
# To preserve consistency and allow correct IsDaemon() answer
def SendToBackground ():
info["LSUIElement"] = "1"
def ReturnToForeground ():
info["LSUIElement"] = "0"
def IsDaemon ():
return info["LSUIElement"]=="1"
class Interface (wx.Frame):
def __init__ (self):
wx.Frame.__init__(self, None, -1, "Test", pos=(100, 100), size=(100, 100))
wx.StaticText(self, -1, "Test code: "+str(random.randint(1000, 10000)), pos=(10, 10), size=(80, 20))
b = wx.Button(self, -1, "DAEMONIZE ME", size=(80, 20), pos=(10, 50))
wx.EVT_BUTTON(self, b.GetId(), self.OnDaemonize)
self.belltimer = wx.Timer(self)
wx.EVT_TIMER(self, self.belltimer.GetId(), self.OnBellTimer)
self.belltimer.Start(1000)
# On Mac OS X, you wouldn't be able to quit the app without the menu bar:
if sys.platform=="darwin":
self.SetMenuBar(wx.MenuBar())
self.Show()
def OnBellTimer (self, e):
wx.Bell()
def OnDaemonize (self, e):
self.Show(False)
SendToBackground()
self.timer = wx.Timer(self)
wx.EVT_TIMER(self, self.timer.GetId(), self.OnExorcize)
self.timer.Start(8000)
def OnExorcize (self, e):
self.timer.Stop()
ReturnToForeground()
self.Show()
self.Raise()
app = wx.App()
i = Interface()
app.MainLoop()
Of course, this example may be started from terminal or with CLI window. In this case the terminal control over your program will stay opened while app only will appear and disappear.
To complete your GUI daemon, you should start it with pythonw (on Windows) or launch it from daemontest.pyw file,
and on Mac you should use:
% nohup python daemontest.py &
or bundle it with py2app or use Python launcher that comes with python.org Python version to start daemontest.py without terminal.
Note: This example suffers from the same flaw on Mac OS X that is mentioned on links I supplied in my question. I refer to the problem of wrong focusing and menu bar not instantly appearing when app comes from background. User has to switch around and come back to newly returned app for it to work properly. I hope somebody will solve this too. And soon. It is quite annoying.
One more note: If you have threads running in your program, pause them while daemonizing and exorcizing. Especially if they are communicating with another app using Apple events. To be frank, something about wx.Timers should be done too. If you are not careful you may get leaking problems around non-existing NSAutoreleasePool and/or SegmentationFault upon program termination.
Ok. Here is the code to do what you want to do:
import AppKit
info = AppKit.NSBundle.mainBundle().infoDictionary()
info["LSUIElement"] = "1"
This the messier answer you do not want to do, but I will list it anyway. In the info.plist file add in this key:
<key>LSUIElement</key>
<string>1</string>
Another more daemonish solution but means it can't have a GUI, you add in this key to the info.plist file:
<key>LSBackgroundOnly</key>
<string>1</string>
Source

Tkinter activate window on Windows XP

I have a small GUI application that listens for network messages so a user can update some info and accept it. This is in a production factory environment and used for interacting with a specific piece of physical hardware (over serial in some cases). The workflow looks like this:
User is interacting with another program (5250 Green Screen)
They enter a certain keybinding that sends a UDP message to a Tkinter GUI
The Tkinter GUI does a deiconify()
User edits data, accepts (Enter) and it does an iconify()
My issue is that on windows XP, the GUI does not become active when I do the deiconify and conversely does not fall back to the prior window on iconify. I have tried some things I found in other questions such as:
Setting the Tk GUI as top.. self.wm_attributes("-topmost", 1)
Trying to set/force focus... self.focus_set() and self.focus_force()
Although the window is visible with the first, I can not seem to get it to be the active window so that the user can type in it without "clicking" on it to activate. The same is true for releasing the "focus" so that the active window becomes the one they were previously on (5250).
It seems like an issue that others also have had but I have not been able to find anything that works. Is there a programmatic way to get the window activated and release it when done?
Unfortunately, after a week there have been no answers and I was not able to find a direct way to do this with Tkinter. I did find a way to solve the problem though and it appears to work consistently. Here are the steps I took to make the screens activate:
Install pywin32.
Create a function that activates the tk app.
Create a function that activates the 5250.
Then each time I do a iconify/deiconify I also run the function to activate the appropriate screen. The code that activates the tk window looks like this:
def activate_self(self):
""" Activate this window. """
shell = win32com.client.Dispatch('WScript.Shell')
shell.AppActivate(str(self.title))
shell = None
The code that activates the caller is a little ugly since it has to guess the title but is the same basic concept.

Key commands in Tkinter

I made a GUI with Tkinter, now how do I make it so when a key command will execute a command even if the Tkinter window is not in focus? Basically I want it so everything is bound to that key command.
Example:
Say I was browsing the internet and the focus was on my browser, I then type Ctrl + U. An event would then run on my program.
Tkinter, on its own, cannot grab keystrokes that (from the OS's/WM's viewpoint) were directed to other, unrelated windows -- you'll need to instruct your window manager, desktop manager, or "operating system", to direct certain keystrokes differently than it usually does. So, what platform do you need to support for this functionality? Each platform has different ways to perform this kind of functionality, of course.

Set global hotkey with Python 2.6

I wanna setup a global hotkey in python 2.6 that listens to the keyboard shortcut ctrl + D or ctrl+ alt+ D on windows, please help me
Tim Golden's python/win32 site is a useful resource for win32 related programming in python. In particular, this example should help:
Catch system-wide hotkeys
I suggest pyhk. It allows for global wide hotkey registration in python and comes with examples and documentation. Pyhk builds on pyhook.
Hotkey registration is as simple as:
pyhk.addHotkey(SomeHotkey,SomeFunction)
The RegisterHotKey method of the wx.Window class is what you're looking for -- as the docs say,
Registers a system wide hotkey. Every
time the user presses the hotkey
registered here, this window will
receive a hotkey event. It will
receive the event even if the
application is in the background and
does not have the input focus because
the user is working with some other
application. To bind an event handler
function to this hotkey use EVT_HOTKEY
with an id equal to hotkeyId. Returns
True if the hotkey was registered
successfully.
So, make an instance of `wx.Window, register the hotkey you want with this method, and possibly do a PushEventHandler if ypu'd rather handle the event(s) in a separate event handler rather than in the window itself (the latter being the default).
Is there anything else in this procedure that is not entirely clear to you...? If so, please edit your question to add whatever further problems you may have!
If you want hotkeys in your wxPython program (which I assume you do because of the wxPython tag), then you should use a wx.AcceleratorTable.

Categories