Pourquoi ce script python s'exécute en arrière-plan et consomme 100% de CPU?

22

Je veux exécuter un script python simple en arrière-plan qui lit le texte du presse-papiers et l'imprime. Voici mon code.

#!/usr/bin/env python

import Tkinter

last_clipboard = ""

def get_clipboard():
  global last_clipboard
  root = Tkinter.Tk()
  root.withdraw() # Hide the main window (optional)
  text_in_clipboard = root.clipboard_get()
  if text_in_clipboard != last_clipboard:
    last_clipboard = text_in_clipboard
    print last_clipboard


while True:
  get_clipboard()

Cela fonctionne comme prévu mais consomme trop de CPU (100% CPU).

Comment puis-je le faire fonctionner correctement sans consommer autant?

dmx
la source
26
Si tout est pris en charge par le framework que vous utilisez, utilisez du code basé sur les événements pour détecter les modifications dans le presse-papiers plutôt qu'une boucle. Il y a une différence entre obtenir le presse-papiers en continu jusqu'à ce qu'il change ou écouter le système vous dire qu'il a changé.
Stefan
6
@dessert Je ne l'ai jamais fait en python, mais voici une solution avec GTK: stackoverflow.com/a/25961646/985296 (ne mentionne aucune dépendance de plateforme).
Stefan
@ jpmc26 & dessert On dirait une méta-discussion, n'hésitez pas à y aller. Certainement une bonne idée pour clarifier la portée .
mât
1
@dessert Ouvrez un fil de discussion méta si vous et JPMC souhaitez discuter si ce sujet est activé / désactivé. Veuillez ne pas utiliser de commentaires pour cet argument. (Nettoyage des commentaires terminé, sujet verrouillé pendant une semaine en attendant votre discussion Meta mais pour arrêter également l'argument des commentaires)
Thomas Ward

Réponses:

44

Vous avez oublié le time.sleep()dans votre whileboucle, selon cette réponse sur SO dormir pendant 0,2 s est un bon compromis entre la fréquence d'interrogation et la charge CPU:

import time

while True:
  get_clipboard()
  time.sleep(0.2) # sleep for 0.2 seconds

Vérifier le presse-papiers toutes les 0,2 secondes semble assez souvent; si vous voulez moins de charge CPU, vous pouvez même augmenter cette valeur - peu d'utilisateurs modifient le contenu du presse-papiers d'une seconde à l'autre.

Notez qu'en général, l'interrogation dans une boucle aussi souvent que cela n'est pas considérée comme une bonne conception. Une meilleure approche serait d'agir en cas de changement du contenu du presse-papiers, un exemple pour GTK peut être trouvé dans cette réponse SO .

Lectures complémentaires

dessert
la source
3
Vous pouvez raccourcir l'intervalle de sommeil sans vraiment affecter le temps processeur utilisé. J'ai trouvé sur mon Mac: 0,01 s: 69%, 0,02 s: 43%, 0,05 s: 25%, 0,1 s: 14%, 0,2 s: 7%. 0,5 s: 3%
Floris
6
L'interrogation est toujours nulle car elle continue de réveiller ce processus polluant les caches CPU et ainsi de suite. Comme discuté dans les commentaires, il vaut mieux attendre la notification d'une modification du presse-papiers.
Peter Cordes
@dessert: Si je connaissais la réponse, je le ferais. Je suggère simplement de mentionner dans votre réponse que se réveiller toutes les 0,2 secondes n'est toujours pas considéré comme une bonne conception, et rechercher une approche non-polling serait beaucoup mieux. Mais pour un piratage unique qui ne fonctionnera que sur un seul ordinateur, bien sûr ce n'est pas horrible et est probablement assez bon.
Peter Cordes
26

Je l'ai finalement fait fonctionner sans boucle. Voici le code:

J'ai dû installer quelques modules: sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0

#!/usr/bin/env python3
import gi, sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

last_clipboard = ""

def callBack(*args):
  global last_clipboard
  new_clipboard = clip.wait_for_text()
  if new_clipboard != last_clipboard:
    last_clipboard = new_clipboard
    print("new Clipboard")
    print(new_clipboard)

clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clip.connect('owner-change',callBack)
Gtk.main()

n'hésitez pas à choisir la solution qui vous convient.

dmx
la source
Oooh… Pourquoi demandez-vous clip.wait_for_text()deux fois?
wizzwizz4
@ wizzwizz4 vous avez raison j'ai édité ... tanks
dmx
@ wizzwizz4 Est-ce que tout le monde ne copie pas deux fois juste pour être sûr?
Michael Frank
16

Vous exécutez la chose en while True:boucle! Cela signifie que le CPU exécute constamment votre boucle. Ajoutez simplement une petite pause à cet endroit et vous devriez voir l'utilisation du processeur diminuer brutalement:

#!/usr/bin/env python

import Tkinter
import time

last_clipboard = ""

def get_clipboard():
  global last_clipboard
  root = Tkinter.Tk()
  root.withdraw() # Hide the main window (optional)
  text_in_clipboard = root.clipboard_get()
  if text_in_clipboard != last_clipboard:
    last_clipboard = text_in_clipboard
    print last_clipboard

while True:
  get_clipboard()
  time.sleep(1)
terdon
la source
3

J'ai été intrigué par ce projet, j'ai donc écrit un script bash pour ceux qui sont plus à l'aise dans cet environnement:

#!/bin/bash

xclip -o -sel clip > /tmp/LastClip

while true ; do 

    xclip -o -sel clip > /tmp/NewClip
    diff -q /tmp/LastClip /tmp/NewClip > /tmp/DiffClip
    if [[ -s /tmp/DiffClip ]] ; then
        cat /tmp/NewClip    # For testing dump to screen instead of printing
        cp -a /tmp/NewClip /tmp/LastClip
    fi
    sleep 1.0

done

Il nécessite le xclippackage de Xorg :

sudo apt install xclip

Il vide le contenu du presse-papiers sur l'écran à l'aide de la catcommande. Si vous souhaitez une copie papier à la place, remplacez-la catpar lpet spécifiez le nom de votre imprimante, son orientation et éventuellement l'option "Ajuster à la page".

Vous verrez un peu de décalage à l'écran parce que je choisis sleep 1.0ce qui serait imperceptible avec une imprimante et encore plus rapide que les gens peuvent mettre du texte en surbrillance et utiliser Ctrl+ C.

Si vous copiez exactement le même texte en surbrillance dans le presse-papiers, cela ne déclenchera pas de différence. Plus ou moins une lettre déclenchera une réponse.

WinEunuuchs2Unix
la source