¿Por qué esta secuencia de comandos de Python ejecutándose en segundo plano consume 100% de CPU?

22

Quiero ejecutar un script de python simple en segundo plano que lea el texto del portapapeles y lo imprima. Aquí está mi código.

#!/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()

Esto funciona como se esperaba pero consume demasiada CPU (100% CPU).

¿Cómo puedo hacer que funcione correctamente sin consumir tanto?

dmx
fuente
26
Si es compatible con el marco que está utilizando, use código basado en eventos para detectar cambios en el portapapeles en lugar de un bucle. Hay una diferencia entre obtener el portapapeles de forma continua hasta que cambie o escuchar al sistema que le dice que ha cambiado.
stefan
66
@dessert Nunca lo he hecho en Python, pero aquí parece ser una solución con GTK: stackoverflow.com/a/25961646/985296 (no menciona ninguna dependencia de la plataforma).
Stefan
@ jpmc26 y postre Parece una meta discusión, siéntase libre de llevarla allí. Definitivamente es una buena idea aclarar este alcance .
Mástil
1
@dessert Abra un metaproceso si usted y JPMC desean discutir si este es un tema activado / desactivado. Por favor no use comentarios para este argumento. (Limpieza de comentarios completa, tema bloqueado durante una semana en espera de su discusión Meta pero también para detener el argumento del comentario)
Thomas Ward

Respuestas:

44

Olvidó el time.sleep()en su whilebucle, de acuerdo con esta respuesta en SO, dormir durante 0.2s es un buen compromiso entre la frecuencia de sondeo y la carga de la CPU:

import time

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

Revisar el portapapeles cada 0.2 segundos parece bastante fácil; si desea menos carga de CPU, incluso puede aumentar este valor: pocos usuarios cambian el contenido del portapapeles de un segundo a otro.

Tenga en cuenta que, en general, el sondeo en bucle con tanta frecuencia como eso no se considera un buen diseño. Un mejor enfoque sería actuar en caso de cambiar el contenido del portapapeles, un ejemplo para GTK se puede encontrar en esta respuesta SO .

Otras lecturas

postre
fuente
3
Podría acortar el intervalo de suspensión sin afectar realmente el tiempo de CPU utilizado. Encontré en mi 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
66
El sondeo todavía apesta porque sigue despertando este proceso contaminando los cachés de la CPU, etc. Como se discutió en los comentarios, es mucho mejor esperar la notificación de un cambio en el portapapeles.
Peter Cordes
@dessert: si supiera la respuesta, lo haría. Simplemente sugiero mencionar en su respuesta que despertarse cada 0.2 segundos todavía no se considera un buen diseño, y buscar un enfoque sin encuestas sería mucho mejor. Pero para un truco único que solo se ejecutará en una computadora, seguro que no es horrible y probablemente sea lo suficientemente bueno.
Peter Cordes
26

Finalmente hago que funcione sin bucle. Este es el código:

Tuve que instalar algunos módulos: 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()

no dude en elegir la solución que más le convenga.

dmx
fuente
Oooh ... ¿Por qué estás solicitando clip.wait_for_text()dos veces?
wizzwizz4
@ wizzwizz4 tienes razón edité ... tanques
dmx
@ wizzwizz4 ¿No todos copian dos veces solo para estar seguros?
Michael Frank
16

¡Estás ejecutando la cosa en un while True:bucle! Eso significa que la CPU está ejecutando constantemente su ciclo. Simplemente agregue una pequeña pausa allí y debería ver que el uso de la CPU cae precipitadamente:

#!/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
fuente
3

Este proyecto me intrigó, así que escribí un script bash para aquellos que se sienten más cómodos en ese entorno:

#!/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

Requiere el xclippaquete de Xorg :

sudo apt install xclip

Está volcando el contenido del portapapeles a la pantalla usando el catcomando. Si desea una copia impresa en vez reemplazar catcon lpy especificar el nombre de la impresora, la orientación y posiblemente la opción "Ajustar a la página".

Verá un poco de retraso en la pantalla porque elijo sleep 1.0cuál sería imperceptible con una impresora y aún más rápido de lo que la gente puede resaltar el texto y usar Ctrl+ C.

Si copia exactamente el mismo texto resaltado en el portapapeles, no se activará la diferencia. Una letra más o menos activará una respuesta.

WinEunuuchs2Unix
fuente