xkeysnail
xkeysnail copied to clipboard
Seeking working launch() and arbitrary (python) command examples
The example config.py
has this line, but it doesn't seem to work as expected:
K("C-o"): [K("C-a"), K("C-c"), launch(["gedit"]), sleep(0.5), K("C-v")]
I just noticed a strange phenomenon when I run xkeysnail
with a script in a terminal, this line in my config causes the startup to pause for 5 seconds (this needs import time
):
K("RC-Shift-M-l"): [launch(["gedit"]), time.sleep(5), K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")],
So it's like the time.sleep(5)
function in this example is actually running once during the startup of xkeysnail
, rather than running when I trigger the shortcut.
If I change it to this:
K("RC-Shift-M-l"): [time.sleep(5), K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")],
This will just instantly insert [Enter, "text", Enter]
into an app like Gedit. It's as if the time.sleep(5)
isn't even there inside the macro.
I also can't get anything to happen after a launch()
command, if I try to use it in a multi-keystroke macro, like in the config.py
example. The line below will just launch Gedit and then do nothing else. No insertion of characters.
K("RC-Shift-M-l"): [launch(["gedit"]), K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")],
Adding the time.sleep()
function, since it is being ignored (or executed in a different context?), doesn't do anything different. The only thing that happens is the Gedit launch:
K("RC-Shift-M-l"): [launch(["gedit"]), time.sleep(5), K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")],
Putting another launch()
command after the first one results in only the first one working and nothing else:
K("RC-Shift-M-l"): [launch(["gedit"]), launch(["transmission"]), time.sleep(5), K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")],
Anybody have working examples of other launch()
commands, especially in a macro with other things after it, or examples of running arbitrary python commands successfully?
@mooz @rbreaves @Lenbok
I use launch()
but not as part of a macro sequence.
Your config file is evaluated at startup, so those entries in your keymap get executed then. Note that launch()
(and sleep()
) return a function to be called to do the work, see https://github.com/mooz/xkeysnail/blob/master/xkeysnail/transform.py#L114 (whereas your time.sleep(5)
just executes immediately when your config is evaluated).
The actual triggered processing of keymap entries looks to be executed at https://github.com/mooz/xkeysnail/blob/master/xkeysnail/transform.py#L490 and there are a few of those that look like they can break out of the loop, including the processing of callables (such as that returned by launch
and sleep
) if they don't return something that can subsequently be processed itself by handle_commands
in a way that makes it return False/None. However it looks as though a callable command could just return a list of commands to execute, so you may be able to make custom launch and sleep functions in your config that take the next set of commands to do, e.g (untested):
def launch_and_then(command, next):
"""Launch command"""
def launcher():
from subprocess import Popen
Popen(command)
return next
return launcher
def sleep_and_then(sec, next):
"""Sleep sec in commands"""
def sleeper():
import time
time.sleep(sec)
return next
return sleeper
with a keymap entry like:
K("RC-Shift-M-l"): [launch_and_then(["gedit"], sleep_and_then(5, [K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")]))],
@Lenbok
Oh. Wow. I'm going to have to try this. Very interesting.
I barely understand what is happening here so if this is not already a working example I probably won't get very far getting it working. But I'll sure try it when I get a chance.
Thanks for the pointers to the code responsible for making this kind of thing work. That's also part of what I was looking for.
@Lenbok
Hey, guess what? It worked. No more pausing at xkeysnail
startup. The delay is part of the macro now.
Kind of awkward syntax, with everything being nested inside of the launch_and_then
, but it really works.
This still only allows, basically, launch()
and sleep()
as actual functions, if I understand. So if I want to do anything else, or even be able to use these in a different order, I'll have to figure out how to modify the def
without making anything crash.
Edit: Nevermind, they work in any order. This also works (wait 3 sec, open Gedit, wait 3 sec, insert text):
[sleep_and_then(3, launch_and_then(["gedit"], sleep_and_then(3, [K("Enter"), K("t"), K("e"), K("x"), K("t"), K("Enter")])))],
Yep, you just can't have anything after one of the callables in your binding. Agree on the syntax, but it's more a proof of concept, you could make a nicer "command sequence builder" function, or probably the handle_commands
should just be changed to work how it seems to be intended.
@Lenbok
So it's just not set up, in its current form, for launch()
and sleep()
to be used sequentially like all the individual K()
functions. But it could be. That would be a great improvement. Kind of beyond me.
But I can definitely work with this already. Hugely helpful.