Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente |
issue165:python [2021/02/01 15:18] – d52fr | issue165:python [2021/02/02 11:41] (Version actuelle) – auntiee |
---|
pip3 install pynput** | pip3 install pynput** |
| |
Une chose dont j'avais besoin était de capturer les frappes et de les gérer sans avoir à appuyer sur [Entrée] tout le temps. Python ne supporte pas cela nativement. J'avais également besoin de gérer plusieurs fils de traitement, et je n'ai pas « directement » touché à des « threads » depuis longtemps. C'est pourquoi j'ai décidé de créer un petit programme de démonstration pour me mettre à niveau. Après avoir cherché sur Internet, j'ai choisi une bibliothèque Python tierce pour gérer les événements du clavier. Elle s'appelle pynput. Vous pouvez trouver le dépôt github à l'adresse https://github.com/moses-palmer/pynput. Elle gère la saisie et le contrôle de la souris ainsi que du clavier. Dans le cadre de cet article, nous ne traiterons que les capacités de contrôle du clavier. | Une chose dont j'avais besoin était de capturer les frappes et de les gérer sans devoir appuyer sur [Entrée] tout le temps. Python ne supporte pas cela nativement. J'avais également besoin de gérer plusieurs fils de traitement, et je n'ai pas « directement » touché à des « threads » depuis longtemps. C'est pourquoi j'ai décidé de créer un petit programme de démonstration pour me mettre à niveau. Après avoir cherché sur Internet, j'ai choisi une bibliothèque Python tierce pour gérer les événements du clavier. Elle s'appelle pynput. Vous pouvez trouver le dépôt github à l'adresse https://github.com/moses-palmer/pynput. Elle gère la saisie et le contrôle de la souris ainsi que du clavier. Dans le cadre de cet article, nous ne traiterons que les capacités de contrôle du clavier. |
| |
Bien sûr, comme il s'agit d'un paquet tiers, vous devez l'installer. Vous pouvez facilement le faire via pip... | Bien sûr, comme il s'agit d'un paquet tiers, vous devez l'installer. Vous pouvez facilement le faire via pip... |
Since pynput uses threading to do its magic, we should have at least a basic understanding of what threads are and how they work.** | Since pynput uses threading to do its magic, we should have at least a basic understanding of what threads are and how they work.** |
| |
La documentation sur pynput peut être consultée à l'adresse https://pynput.readthedocs.io/en/lates/, et cela vaut la peine de la parcourir. J'utiliserai certains de ses exemples de code dans le programme de démonstration que je vais présenter ici. J'ai également utilisé du code provenant d'un tutoriel de Real Python sur les threads, mais je l'ai modifié assez profondément. | La documentation sur pynput peut être consultée à l'adresse https://pynput.readthedocs.io/en/lates/ et il vaut la peine de la parcourir. J'utiliserai certains de ses exemples de code dans le programme de démonstration que je vais présenter ici. J'ai également utilisé du code provenant d'un tutoriel de Real Python sur les threads, mais je l'ai modifié assez profondément. |
| |
Puisque pynput utilise les threads pour opérer, nous devrons avoir au moins une compréhension de base de ce que sont les threads et de leur fonctionnement. | Puisque pynput utilise les threads pour opérer, nous devrons avoir au moins une compréhension de base de ce que sont les threads et de leur fonctionnement. |
Fils de traitement - threads | Fils de traitement - threads |
| |
J'ai trouvé deux sites web qui font vraiment un bon travail sur les threads : ce qu'ils sont et comment les utiliser. Vous pouvez les trouver sur https://www.techbeamers.com/python-multithreading-concepts/ et https://realpython.com/intro-to-python-threading/. | J'ai trouvé deux sites Web qui sont vraiment bien sur les threads : ce qu'ils sont et comment les utiliser. Vous pouvez les trouver sur https://www.techbeamers.com/python-multithreading-concepts/ et https://realpython.com/intro-to-python-threading/. |
| |
Je vais essayer de décomposer les informations fournies sur ces deux sites en un simple aperçu général des fils de discussion. | Je vais essayer de décomposer les informations fournies sur ces deux sites en un simple aperçu général des threads. |
| |
Lorsque vous exécutez un programme CLI (Command-Line Interface) « normal » écrit en Python, il ne fonctionne probablement que dans ce que l'on appelle le fil principal. Tout cela signifie que l'exécution du programme commence au début et exécute chaque instruction, une à la fois, jusqu'à la fin du programme, sans essayer de faire autre chose en même temps. Un assez bon exemple qui démontre la nécessité des threads (dans certains cas) est la fonction time.sleep(). Lorsque vous appelez cette fonction, tout le traitement du programme s'arrête jusqu'à ce que la durée spécifiée soit écoulée. Ainsi, si vous appelez time.sleep(5), votre programme s'arrête pendant 5 secondes. C'est l'un des plus grands obstacles qu'un programmeur (qui commence à peine à travailler avec des programmes d'interface graphique - GUI) doit surmonter. Dans la programmation GUI, vous traitez des événements. Vous ne voulez pas appeler une fonction de mise en veille, car l'interface graphique devient insensible lorsque cette fonction de blocage est appelée. | Lorsque vous exécutez un programme CLI (Command-Line Interface) « normal » écrit en Python, il ne fonctionne probablement que dans ce que l'on appelle le fil principal. Tout ce que cela signifie est que l'exécution du programme commence au début et exécute chaque instruction, une à la fois, jusqu'à la fin du programme, sans essayer de faire autre chose en même temps. Un assez bon exemple qui démontre la nécessité des threads (dans certains cas) est la fonction time.sleep(). Lorsque vous appelez cette fonction, tout le traitement du programme s'arrête jusqu'à ce que la durée spécifiée soit écoulée. Ainsi, si vous appelez time.sleep(5), votre programme s'arrête pendant 5 secondes. C'est l'un des plus grands obstacles qu'un programmeur (qui commence à peine à travailler avec des programmes d'interface graphique - GUI) doit surmonter. Dans la programmation GUI, vous traitez des événements. Vous ne voulez pas appeler une fonction de mise en veille, car l'interface graphique devient insensible lorsque cette fonction de blocage est appelée. |
| |
**In CLI programming, you can get around this issue by using a thread. Python and the computer cooperate when running on modern multi-core processors in such a way that threads can be handled in different cores almost concurrently. | **In CLI programming, you can get around this issue by using a thread. Python and the computer cooperate when running on modern multi-core processors in such a way that threads can be handled in different cores almost concurrently. |
When pynput starts (the way we will use it), it creates a thread that continuously “listens” for any mouse or keyboard messages. Once it “hears” a keyboard message or mouse message, it responds by calling a function that we define. This function is called a callback function. In that function, we can handle whatever key was pressed or released, or, in the case of the mouse, a movement or button click. Once it has called the callback function, it goes back to listening until the next keyboard or mouse message. We really don’t have to understand the deep internals of pynput, only the callback functions and how to start the listening process.** | When pynput starts (the way we will use it), it creates a thread that continuously “listens” for any mouse or keyboard messages. Once it “hears” a keyboard message or mouse message, it responds by calling a function that we define. This function is called a callback function. In that function, we can handle whatever key was pressed or released, or, in the case of the mouse, a movement or button click. Once it has called the callback function, it goes back to listening until the next keyboard or mouse message. We really don’t have to understand the deep internals of pynput, only the callback functions and how to start the listening process.** |
| |
Dans la programmation par CLI, vous pouvez contourner cette question en utilisant un thread. Python et l'ordinateur coopèrent lorsqu'ils fonctionnent sur des processeurs modernes à plusieurs cœurs de telle sorte que les threads peuvent être traités dans différents cœurs presque simultanément. | Dans la programmation par CLI, vous pouvez contourner ce problème en utilisant un thread. Python et l'ordinateur coopèrent lorsqu'ils fonctionnent sur des processeurs modernes à plusieurs cœurs de telle sorte que les threads peuvent être traités dans différents cœurs presque simultanément. |
| |
Lorsque pynput démarre (comme nous l'utiliserons), il crée un thread qui « écoute » en permanence les messages de la souris ou du clavier. Une fois qu'il « entend » un message du clavier ou de la souris, il répond en appelant une fonction que nous définissons. Cette fonction s'appelle une fonction de rappel (callback). Dans cette fonction, nous pouvons gérer n'importe quelle touche qui a été enfoncée ou relâchée ou, dans le cas de la souris, un mouvement ou un clic de bouton. Une fois qu'elle a appelé la fonction de rappel, elle retourne à l'écoute jusqu'au prochain message du clavier ou de la souris. Nous n'avons pas vraiment besoin de comprendre les mécanismes internes de pynput, mais seulement les fonctions de rappel et la façon de lancer le processus d'écoute. | Lorsque pynput démarre (comme nous l'utiliserons), il crée un thread qui « écoute » en permanence les messages de la souris ou du clavier. Une fois qu'il « entend » un message du clavier ou de la souris, il répond en appelant une fonction que nous définissons. Cette fonction s'appelle une fonction de rappel (callback). Dans cette fonction, nous pouvons gérer n'importe quelle touche qui a été enfoncée ou relâchée ou, dans le cas de la souris, un mouvement ou un clic de bouton. Une fois qu'elle a appelé la fonction de rappel, elle retourne à l'écoute jusqu'au prochain message du clavier ou de la souris. Nous n'avons pas vraiment besoin de comprendre les mécanismes internes de pynput, mais seulement les fonctions de rappel et la façon de lancer le processus d'écoute. |
Pour créer le rappel, nous allons d'abord nous occuper du rappel par appui sur une touche. Nous utiliserons pour cela le code du tutoriel (en haut à droite). | Pour créer le rappel, nous allons d'abord nous occuper du rappel par appui sur une touche. Nous utiliserons pour cela le code du tutoriel (en haut à droite). |
| |
Lorsqu'une touche est pressée sur le clavier, cette fonction est appelée. L'objet de la touche est fourni en paramètre. Si nous appuyons sur la touche "a" du clavier, le rappel s'imprimera dans le terminal... | Lorsqu'on appuie sur une touche du clavier, cette fonction est appelée. L'objet de la touche est fourni en paramètre. Si nous appuyons sur la touche « a » du clavier, le rappel s'imprimera dans le terminal : |
| |
alphanumeric key a pressed #touche alphanumérique a appuyée | alphanumeric key a pressed # appuie sur touche alphanumérique a |
| |
Cependant, si nous appuyons sur une "touche spéciale" comme [Shift] ou [Ctrl], le rappel s'imprimera... | Cependant, si nous appuyons sur une « touche spéciale » comme [Shift] ou [Ctrl], le rappel imprimera : |
| |
special key Key.shift pressed #touche spéciale Touche.shift appuyée | special key Key.shift pressed #appuie sur touche spéciale Touche.shift |
| |
ou | ou |
| |
special key Key.ctrl pressed #touche spéciale Key.ctrl appuyée | special key Key.ctrl pressed #appuie sur touche spéciale Key.ctrl |
| |
**This way, we can monitor for any type of keypress. We can also monitor for key-release as well. Sometimes, monitoring for the release of a key is a better option, since sometimes we can get multiple events when a key is pressed because the keyboard is internally dirty. Here is the author’s key-release callback function (bottom right). | **This way, we can monitor for any type of keypress. We can also monitor for key-release as well. Sometimes, monitoring for the release of a key is a better option, since sometimes we can get multiple events when a key is pressed because the keyboard is internally dirty. Here is the author’s key-release callback function (bottom right). |
Now, we’ll create our demo program that will contain the main thread, as well as two secondary threads and the listener thread.** | Now, we’ll create our demo program that will contain the main thread, as well as two secondary threads and the listener thread.** |
| |
De cette façon, nous pouvons surveiller tout type d'appui sur les touches. Nous pouvons également surveiller le relâchement des touches. Dans certains cas, la surveillance du relâchement d'une touche est une meilleure option, car nous pouvons parfois obtenir plusieurs événements lorsqu'une touche est pressée parce que le clavier est sale à l'intérieur. | De cette façon, nous pouvons surveiller tout type d'appui sur les touches. Nous pouvons également surveiller le relâchement des touches. Dans certains cas, la surveillance du relâchement d'une touche est une meilleure option, car nous pouvons parfois obtenir plusieurs événements lors de l'appui sur une touche parce que le clavier est sale à l'intérieur. Voici, en bas à droite, la fonction de rappel du relâchement d'une touche de l'auteur. |
| |
Dans cette fonction, le rappel ne cherche qu'une seule chose : la touche [Esc], qui arrête le processus d'écoute. | Dans cette fonction, le rappel ne cherche qu'une seule chose : la touche [Esc], qui arrête le processus d'écoute. |
Le code | Le code |
| |
La première chose à faire, comme toujours, est d'importer nos bibliothèques... | La première chose à faire, comme toujours, est d'importer nos bibliothèques : |
| |
import logging | import logging |
doloop2 = False** | doloop2 = False** |
| |
Il y aura deux threads qui tourneront chacun en boucle continue, pour obtenir l'heure actuelle, l'imprimer, dormir pendant une durée déterminée (chaque fil aura un temps de sommeil différent), puis tout recommencer. Comme j'utilise une simple boucle de temps, la ou les boucles tournent jusqu'à ce que la condition de test soit False (fausse). Nous réglons la condition sur « True » (vraie) avant de commencer la boucle. Par exemple, dans le premier fil, nous utilisons une variable globale appelée doloop1, que nous mettons à True avant la boucle. La boucle continuera à tourner jusqu'à ce que, et si, doloop1 devient False. Lorsqu'elle devient False, on quitte la boucle et le fil se termine. Pour ce faire, j'ai créé une fonction appelée stop_threads() qui met les deux fonctions globales à False. Cette fonction sera appelée après avoir appuyé sur la touche [Esc] pour mettre fin au programme. | Il y aura deux threads qui tourneront chacun en boucle continue, pour obtenir l'heure actuelle, l'imprimer, dormir pendant une durée déterminée (chaque fil aura un temps de sommeil différent), puis tout recommencer. Comme j'utilise une simple boucle while, la ou les boucles tournent jusqu'à ce que la condition de test soit False (fausse). Nous réglons la condition sur « True » (vraie) avant de commencer la boucle. Par exemple, dans le premier fil, nous utilisons une variable globale appelée doloop1, que nous mettons à True avant la boucle. La boucle continuera à tourner jusqu'à ce que, et si, doloop1 devient False. Lorsqu'elle devient False, on quitte la boucle et le fil se termine. Pour ce faire, j'ai créé une fonction appelée stop_threads() qui met les deux fonctions globales à False. Cette fonction sera appelée après un appui sur la touche [Esc] pour mettre fin au programme : |
| |
| |
Le fil numéro 2 (en bas à droite) est presque exactement le même que le fil numéro 1, à la seule différence que le temps de sommeil du fil 2 est de 10 secondes. | Le fil numéro 2 (en bas à droite) est presque exactement le même que le fil numéro 1, à la seule différence que le temps de sommeil du fil 2 est de 10 secondes. |
| |
La boucle principale démarre tous les fils et reste ensuite inactive jusqu'à ce que le fil listener nous informe que la touche [Esc] a été enfoncée, puis arrête proprement les deux fils "travailleurs". | La boucle principale démarre tous les fils et puis reste inactive jusqu'à ce que le fil listener nous informe que la touche [Esc] a été enfoncée, puis arrête proprement les deux fils "travailleurs". |
| |
Les lignes suivantes démarrent les deux fils d'affichage du temps et l'auditeur du clavier. | Les lignes suivantes démarrent les deux fils d'affichage du temps et le listener du clavier : |
| |
t1.start() | t1.start() |
When the program is run, we’ll get the following output in the terminal.** | When the program is run, we’ll get the following output in the terminal.** |
| |
À ce stade, lorsque l'appel de listener.join() est effectué, le programme attend simplement. Dans un vrai programme, vous auriez normalement d'autres choses à faire, mais ceci n'est qu'une simple démo. Une fois que la touche [Esc] est relâchée (rappelez-vous que nous surveillons cela dans la fonction de rappel on_release()), le reste du code est exécuté. | À ce stade, lorsque l'appel de listener.join() est effectué, le programme attend simplement. Dans un vrai programme, vous auriez normalement d'autres choses à faire, mais ceci n'est qu'une simple démo. Une fois que la touche [Esc] est relâchée (rappelez-vous que nous surveillons cela dans la fonction de rappel on_release()), le reste du code est exécuté : |
| |
logging.info("Main : wait for the thread to finish") | logging.info("Main : wait for the thread to finish") |
logging.info("Ending Program!") | logging.info("Ending Program!") |
| |
Enfin, nous utilisons le code suivant comme point d'entrée dans notre programme, en appelant la fonction mainloop(). | Enfin, nous utilisons le code suivant comme point d'entrée dans notre programme, en appelant la fonction mainloop() : |
| |
if __name__ == "__main__" : | if __name__ == "__main__" : |
mainloop() | mainloop() |
| |
Lorsque le programme est exécuté, nous obtenons la sortie suivante dans le terminal. | Lorsque le programme est exécuté, nous obtenons la sortie suivante dans le terminal : |
| |
**07:24:18: Main : before creating thread | **07:24:18: Main : before creating thread |