Unfolding your question
Python is a generic language - it’s good for anything. Only to do anything, she has to be able to connect to inputs and outputs with the data she wants to process.
A Python program more easily accesses arguments that have been passed on the command line, terminal input and output - divided into lines of text, and any file available in the environments it is running.
In order to be able to process events such as "Ctrl + shift + I" pressed in the window environment (and not in the terminal where the program is running), mouse clicks, movements in front of the computer’s camera, etc... - it has to interface with the Operating System, somehow, so that the event can reach your program.
This is done through external libraries and modules (you can also do it directly if you know what calls to make to the operating system - without using a third-party package - but this can get VERY difficult, VERY fast).
So, you have to understand which options of your operating system and components of it are able to capture events like the ones you want, and how to pass these events to your Python code.
Which libraries to search for
And on the other hand, a question like this is no longer a "simple" Python question, but a question that requires someone who has very specialized knowledge of one of these components.
In advance then - as you are on Linux, you would have to interface with a X11 compatibility layer - but other components of your system already abstract some of this for you. In general, the components capable of "observing" global events in the graphical interface will be the libraries with Widgets toolkits - Python can interface with Qt, GTK+ and Tkinter, among others.
Of these, Ubuntu pre-installs the interfaces for GTK (but at this point I don’t even know if what it re-installs is using Gtk classic k or gtk3 + gobject introspection) - and has a library that is distributed by default with the Python language, but the default installation of Ubuntu does not include - which is Tkinter.
The Tkinter would be simpler, and more universal - so first thing - try to understand if Tkinter can capture a global keyboard event - but it seems not, it can only really receive events that happen in the program window.
Repeating the same search with Python GTK3, an example appears in Stackoverflow in English which is almost exactly what you need:
https://stackoverflow.com/questions/16613435/how-to-make-global-keyboard-shortcuts-with-python-and-gtk3
Then check if you have installed there the "python3-gobject" or similar package (this is the package name in Fedora).
What needs to be different in your program
Note that the use of libraries that interact with the screen and with the mouse, in general, require a different programming paradigm - in programs in the terminal where we interact with "print" and "input" - the program is always running something, or is waiting for the user. When the interaction is with graphics window events, the "expecting something" becomes expect system events from outside of your program. So in general the program is done differently:
A part of the program makes the "setup", declaring the classes, windows and controls that will be used - and most importantly - declaring what parts of the program will respond to system events. They may be events of "close main window", "click on Ok button", "user presses <Ctrl><alt>I
at any point on the screen" - this setup always indicates functions or methods that will be called when the event happens (they are named callback).
The second step of the execution calls the "main loop" of the chosen graphical library. This effectively pauses your program, and returns control to the operating system. Then no code your wheel, until you arrive at a pre-registered event, and one of its callback functions is executed.
So it’s hard to adapt a program that interacts with "print" and "input" to use graphical environment events - even if it’s ONLY a keyboard shortcut - the correct thing is to rewrite it so that it works in this new paradigm, oriented to wait events.
And an example to close:
Then your script can get in shape:
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Keybinder', '3.0')
from gi.repository import Gtk
from gi.repository import Keybinder
from gi.repository import GObject
KEY_START = "<Ctrl><ALt>I"
KEY_STOP = "<Ctrl><Alt>P"
KEY_TERMINATE = "<Ctrl><Alt>D"
def init(*args, **kwargs):
Keybinder.init()
m = Main(*args, **kwargs)
print("Iniciando aplicação - pressione {} para agir, {} para sair".format(KEY_START, KEY_TERMINATE))
Gtk.main()
class Main:
def __init__(self, *args, **kwargs):
# Salva os parametros para seu método principal
# Bind callback keys:
Keybinder.bind(KEY_START, self.start, None)
Keybinder.bind(KEY_STOP, self.stop, None)
Keybinder.bind(KEY_TERMINATE, Gtk.main_quit)
# A partir dos parametros passados, configure
# todos as variáveis que sua função principal
# vai precisar como atributos do objeto
# self.inicio = 0; self.dir="/tmp/", etc...
# Deixe todo o código até o ponto em que você teria um
# "while True" ou equivalente no seu codigo original
# Este binding faz o runtime do gtk chamar sua funcao principal
# quando o programa estiver rodando
self.running = False
def start(self, *args):
self.running = True
self.execute_once()
def stop(self, *args):
self.running = False
def execute_once(self, *args):
if not self.running:
return
# Aqui ponha o corpo de um loop da sua função
# como se fosse o conteúdo do "while True"
# guarde todas as variáveis que precisam ser
# preservadas em atributos da instância
print("Executando uma vez")
...
# e agende a próxima execução desse loop principal
# isso é necessário para que o sistema continue responsivo.
# o tempo minimo de agendamento é 1ms -
# se precisar repetir o corpo da função antes disso,
#use um "for" ou "while" interno.
GObject.timeout_add(200, self.execute_once)
if __name__ == "__main__":
init()
PS - note that this script does exactly what you asked for - except that I use "Ctrl + alt" instead of "Ctrl + shift": this combination didn’t work for me - look for Keybinder documentation,- it may not be written "I" - or perhaps, mu window system consumes this type of shortcut, before passing it to the program.
Hi Leonardo. Welcome to SOPT. First of all, this site is not a forum. Do the [tour] and read [Ask], okay? Your question is a little wide, why don’t you divide it into parts (you can open several questions if you need to)? For example, it seems that the central part of your difficulty is to know if it is possible to capture a key combination, executed on the operating system (since it will not have terminal open) via Python. Is that it? If so, I suggest editing this question and focusing on that doubt.
– Luiz Vieira
Ah, I’d say it’s unlikely you can do that with just Python. Maybe you need to use some feature of your Ubuntu to run Python scripts, and in these scripts use some shared resource (a disk file, a shared memory, etc.) to keep the state of your "key" on/off. An example of shell script execution (which you can transpose to run Python): https:/ubuntuforums.org/showthread.php?t=1540961
– Luiz Vieira
Hi Luiz - I think I covered what you need "besides Python" and how to connect this with Python in my answer. But if the option was to use a standard program from the scirpt shell that "wait Ctrl + shift + i" the same call to an external program can be made from Python. The question you point to, however, talks about running any program - it doesn’t matter if it’s in Python or not. It’s just configuring a system shortcut to start a program - and has nothing to do with shell.
– jsbueno
@jsbueno Yes, it’s true. I was just trying to help the AP, but I didn’t dig deep like you. Nice answer, by the way. :)
– Luiz Vieira