python (on / off switch) [closed]

2

Hello.

I'm learning to program in python3, it's been some time. I am Ubuntu user.

I've been creating a script for some time, now I want to improve it, I've looked for some modules and some examples but I did not find anything. I need a script template that does the following: When I press Ctrl + Shift + I it performs a function with a loop. And when I press another sequence Ctrl + Shift + P the loop pauses, but when I press Ctrl + Shift + I it returns to do the function or it restarts. And finally when I press Ctrl + Shift + D it ends the program in general. I need it to do this without me having the keyboard in the terminal, because the script is analyzing images. Please help me by sending an example script, or something that can help me.

Thank you.

    
asked by anonymous 01.02.2017 / 19:41

1 answer

3

Unfolding your question

Python is a generic usage language - it fits for anything. But to do anything, it has to be able to connect to inputs and outputs with the data it wants to process.

A program in Python more easily accesses the arguments that were passed on the command line, the input and output of the terminal - divided into lines of text, and any file available in the environments it is running.

In order to process events such as "ctrl + shift + I" pressed in the windows environment (not the terminal where the program is running), mouse clicks, movements in front of the computer camera, etc ... - it has to interface with the Operating System in some way, so that the event can get to your program.

This is done through libraries and external modules (you can also do it directly if you know which 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 what options your operating system and its components are capable of capturing events like the ones you want, and how to pass those events to your Python code.

Which libraries to search

And on the other hand, a question like this is no longer a "simple" Python question, to be a question that requires someone who has a very specialized knowledge of one of these components.

Going forward then - as you are on Linux, you would have to interface with an X11 compatibility layer - but other components of your system already have some abstraction for you. In general, the components that are able to "observe" global events in the graphical interface will be the libraries with Widget 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 do not even know if what it installs again is using classic Gtk k or gtk3 + gobject introspection) - and it has a library that is distributed by default with the Python language, but the standard installation of Ubuntu does not include - that is the tkinter.

Tkinter would be simpler, and more universal - then, first thing - seek to understand if Tkinter can capture a global keyboard event - but it seems like no, it can only actually receive events that happen in the program window.

Repeating the same search with Python GTK3, an example in StackOverflow in English appears that is almost exactly what you need:

Then check that you have installed the "python3-gobject" package or similar (this is the name of the package in Fedora).

What needs to be different in your program

Note that the use of libraries that interact with the screen and the mouse in general require a different programming paradigm - in programs on 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 graphic window events, the "expecting something" becomes expected system events from outside your program. So in general the program is done in another way:

A part of the program does the "setup", declaring the classes, windows and controls that will be used - and most importantly - by declaring which parts of the program will respond to system events. It can be "close the main window", "click the Ok" button, "user press <Ctrl><alt>I anywhere on the screen" - this setup always indicates functions or methods that will be called when the event happens (they take the name of callback).

The second stage of the execution calls the "main loop" of the chosen graphics library. This effectively pauses your program, and returns control to the operating system. Then no code will run until a pre-registered event arrives, and one of its callback functions is executed.

So it's hard to adapt a program that interacts with "print" and "input" to use events of the graphical environment - even if it is ONLY a keyboard shortcut - the correct one is to rewrite it so that it works in this new paradigm, oriented to wait for events.

And an example to terminate:

Then your script can stay in the form:

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 did not work for me - look for Keybinder documentation, - do not write "I" - or maybe, the windows system consumes this type of shortcut, before passing it to the program.

    
03.02.2017 / 20:30