Почему этот скрипт Python работает в фоновом режиме и потребляет 100% ресурсов процессора?

22

Я хочу запустить простой Python-скрипт в фоновом режиме, который читает текст из буфера обмена и распечатывает его. Вот мой код

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

Это работает, как и ожидалось, но потребляет слишком много ЦП (100% ЦП).

Как я могу заставить его работать правильно, не потребляя так много?

DMX
источник
26
Если вообще поддерживается используемой платформой, используйте код на основе событий для обнаружения изменений в буфере обмена, а не в цикле. Есть разница между получением буфера обмена непрерывно до его изменения или прослушиванием системы, сообщающей вам, что она изменилась.
Стефан
6
@dessert Я никогда не делал этого на python, но здесь, похоже, есть решение с GTK: stackoverflow.com/a/25961646/985296 (не упоминается никакой зависимости от платформы).
Стефан
@ jpmc26 & десерт Выглядит как мета-обсуждение, не стесняйтесь обсуждать его. Определенно хорошая идея , чтобы получить это прояснилось для сферы .
Мачта
1
@dessert Откройте мета-ветку, если вы и JPMC хотите обсудить, является ли это темой включения / выключения. Пожалуйста, не используйте комментарии для этого аргумента. (Очистка комментария завершена, тема заблокирована на неделю в ожидании обсуждения в Мета-версии, но также и для прекращения аргумента комментария)
Томас Уорд

Ответы:

44

Вы забыли time.sleep()в своем whileцикле, согласно этому ответу SO спит в течение 0,2 с - это хороший компромисс между частотой опроса и нагрузкой на процессор:

import time

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

Проверка буфера обмена каждые 0,2 секунды кажется достаточно простой; если вы хотите уменьшить нагрузку на процессор, вы можете даже увеличить это значение - мало пользователей меняют содержимое буфера обмена с одной секунды на другую.

Обратите внимание, что в общем опрос в цикле так часто, как это не считается хорошим дизайном. Лучшим подходом было бы действовать в случае изменения содержимого буфера обмена, пример для GTK можно найти в этом ответе SO .

дальнейшее чтение

Десерт
источник
3
Вы можете сократить интервал ожидания, не влияя на используемое время процессора. Я обнаружил на своем Mac: 0,01 с: 69%, 0,02 с: 43%, 0,05 с: 25%, 0,1 с: 14%, 0,2 с: 7%. 0,5 с: 3%
Флорис
6
Опрос все еще отстой, потому что он продолжает пробуждать этот процесс, загрязняя кэш процессора и так далее. Как уже говорилось в комментариях, гораздо лучше дождаться уведомления об изменении буфера обмена.
Питер Кордес
@dessert: Если бы я знал ответ, я бы. Я просто предлагаю упомянуть в своем ответе, что пробуждение каждые 0,2 секунды все еще не считается хорошим дизайном, и поиск подхода без опроса был бы намного лучше. Но для одноразового взлома, который будет работать только на одном компьютере, конечно, это не ужасно, и, вероятно, достаточно хорошо.
Питер Кордес
26

Я наконец заставляю это работать без петли. Это код:

Мне пришлось установить несколько модулей: 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()

не стесняйтесь выбирать решение, которое подходит для вас.

DMX
источник
Оооо ... Почему вы запрашиваете clip.wait_for_text()дважды?
wizzwizz4
@ wizzwizz4 Вы правы, я редактировал ... танки
DMX
@ wizzwizz4 ли не каждый экземпляр дважды просто , чтобы быть уверенным?
Майкл Франк
16

Вы запускаете вещь в while True:цикле! Это означает, что процессор постоянно запускает ваш цикл. Просто добавьте небольшую паузу, и вы увидите, что загрузка процессора резко падает:

#!/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
источник
3

Я был заинтригован этим проектом, поэтому написал сценарий bash для тех, кто чувствует себя более комфортно в этой среде:

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

Требуется xclipпакет Xorg :

sudo apt install xclip

Он выводит содержимое буфера обмена на экран с помощью catкоманды. Если вы хотите твердую копию вместо замены catс lpи указать имя принтера, ориентацию и , возможно , «подходит к странице» вариант.

Вы увидите некоторое отставание от экрана, потому что я выбираю то, sleep 1.0что было бы незаметно для принтера и все же быстрее, чем люди могут выделить текст и использовать Ctrl+ C.

Если вы скопируете точно такой же выделенный текст в буфер обмена, это не вызовет разницы. Одна буква более или менее вызовет ответ.

WinEunuuchs2Unix
источник