Por que esse script python sendo executado em segundo plano consome 100% da CPU?

22

Eu quero executar um script python simples em segundo plano que lê o texto da área de transferência e o imprime. Aqui está o meu 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()

Isso está funcionando conforme o esperado, mas consome muita CPU (100% da CPU).

Como posso fazê-lo funcionar corretamente sem consumir muito?

dmx
fonte
26
Se houver suporte da estrutura que você está usando, use o código baseado em eventos para detectar alterações na área de transferência, em vez de um loop. Há uma diferença entre obter a área de transferência continuamente até que ela mude ou ouvir o sistema informando que ela mudou.
stefan
6
@dessert Eu nunca fiz isso em python, mas aqui parece haver uma solução com o GTK: stackoverflow.com/a/25961646/985296 (não menciona nenhuma dependência de plataforma).
stefan
@ jpmc26 & dessert Parece uma meta-discussão, fique à vontade para levá-la até lá. Definitivamente, é uma boa idéia esclarecer esse escopo .
Mastro
1
@dessert Abra um meta thread se você e o JPMC quiserem discutir se este é um tópico ativado / desativado. Por favor, não use comentários para este argumento. (Limpeza de comentários concluída, tópico bloqueado por uma semana enquanto aguarda sua discussão sobre o Meta, mas também para interromper o argumento do comentário)
Thomas Ward

Respostas:

44

Você esqueceu a time.sleep()sua whileloop, de acordo com esta resposta em SO dormir por 0.2s é um bom compromisso entre a frequência de consulta e carga da CPU:

import time

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

Verificar a área de transferência a cada 0,2 segundos parece ser bastante fácil; se você deseja menos carga de CPU, pode aumentar esse valor - poucos usuários alteram o conteúdo da área de transferência de um segundo para outro.

Observe que, em geral, a sondagem em um loop sempre que isso não é considerado um bom design. Uma abordagem melhor seria agir no caso de alterar o conteúdo da área de transferência; um exemplo para o GTK pode ser encontrado nesta resposta do SO .

Leitura adicional

sobremesa
fonte
3
Você pode reduzir o intervalo de sono sem afetar o tempo de CPU usado. Encontrei no meu 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
A pesquisa ainda é uma porcaria porque continua acordando esse processo poluindo os caches da CPU e assim por diante. Como discutido nos comentários, é melhor esperar pela notificação de uma alteração na área de transferência.
Peter Cordes
@dessert: Se eu soubesse a resposta, saberia. Eu apenas sugiro mencionar na sua resposta que acordar a cada 0,2 segundos ainda não é considerado um bom design, e procurar uma abordagem sem pesquisa seria muito melhor. Mas para um hack único que só será executado em um computador, com certeza não é horrível e provavelmente é bom o suficiente.
Peter Cordes
26

Eu finalmente faço funcionar sem loop. Este é o código:

Eu tive que instalar alguns 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()

fique à vontade para escolher a solução mais adequada para você.

dmx
fonte
Oooh ... Por que você está solicitando clip.wait_for_text()duas vezes?
wizzwizz4
@ wizzwizz4 você está certo eu editei ... tanks
dmx
@ wizzwizz4 Todo mundo não copia duas vezes para ter certeza?
Michael Frank
16

Você está executando a coisa em um while True:loop! Isso significa que a CPU está constantemente executando seu loop. Basta adicionar uma pequena pausa lá e você verá o uso da CPU cair vertiginosamente:

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

Fiquei intrigado com este projeto, por isso escrevi um script bash para aqueles mais confortáveis ​​naquele ambiente:

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

Requer o xclippacote do Xorg :

sudo apt install xclip

Está despejando o conteúdo da área de transferência na tela usando o catcomando Se desejar cópia impressa, substitua catpor lpe especifique o nome da impressora, a orientação e, possivelmente, a opção "ajustar à página".

Você verá um pouco de atraso na tela porque eu escolho o sleep 1.0que seria imperceptível em uma impressora e ainda mais rápido do que as pessoas podem destacar texto e usar Ctrl+ C.

Se você copiar exatamente o mesmo texto destacado para a área de transferência, isso não causará diferença. Uma letra mais ou menos acionará uma resposta.

WinEunuuchs2Unix
fonte