このPythonスクリプトがバックグラウンドで実行されてCPUを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()

これは期待どおりに機能していますが、CPUを大量に消費しています(100%CPU)。

それほど消費せずに正しく動作させるにはどうすればよいですか?


26
使用しているフレームワークでサポートされている場合は、ループではなくイベントベースのコードを使用してクリップボードの変更を検出します。クリップボードが変化するまで継続的に取得するか、システムが変化したことを伝えるかを聞くことには違いがあります。
ステファン

6
@dessert私はPythonでそれをやったことがありませんが、ここにGTKでの解決策があるようです:stackoverflow.com/a/25961646/985296(プラットフォーム依存関係については言及していません)。
ステファン

@ jpmc26&dessertメタディスカッションのように見えるので、気軽に取り上げてください。これをスコープに対してクリアすることは間違いなく良い考えです。
マスト

1
@dessertあなたとJPMCがこれがオン/オフトピックかどうかを議論したい場合、メタスレッドを開きます。この引数にはコメントを使用しないでください。(コメントのクリーンアップが完了し、メタディスカッションまで1週間ロックされていますが、コメント引数も停止します)
Thomas Ward

回答:


44

SOを0.2秒間スリープするというこの回答によるとtime.sleep()whileループのことを忘れてしまったのは、ポーリング頻度とCPU負荷の間の適切な妥協点です。

import time

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

クリップボードを0.2秒ごとに確認することは、十分な頻度で十分に思えます。CPUの負荷を減らしたい場合は、この値を増やすこともできます。クリップボードのコンテンツを1秒から1秒に変更するユーザーはほとんどいません。

一般に、ループでのポーリングは、適切な設計とはみなされないことに注意してください。より良いアプローチは、クリップボードのコンテンツを変更するイベントに対応することです。GTKの例は、このSOの回答にあります。

参考文献


3
使用されるCPU時間に実際に影響を与えることなく、スリープ間隔を短くすることができます。Macで見つけたのは、0.01秒:69%、0.02秒:43%、0.05秒:25%、0.1秒:14%、0.2秒:7%です。0.5秒:3%
フローリス

6
ポーリングは、CPUキャッシュなどを汚染するこのプロセスを起動し続けるため、依然として無駄です。 コメントで説明したように、クリップボードの変更の通知を待つ方がずっと良いです。
ピーターコーデス

@dessert:答えがわかっていれば、そうでしょう。私はあなたの答えで、0.2秒ごとに目覚めることはまだ良い設計とは見なされず、非ポーリングアプローチを探す方がはるかに優れていると言及することをお勧めします。しかし、1台のコンピューターでのみ実行される1回限りのハックの場合、それが恐ろしくなく、おそらく十分であることを確認してください。
ピーターコーデス

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()

最適なソリューションを自由に選択してください。


うーん...なぜclip.wait_for_text()2回要求していますか?
wizzwizz4

@あなたは右の私が...タンクを編集しているwizzwizz4
DMX

@ wizzwizz4誰もが二度コピーしないだけで確認しますか?
マイケルフランク

16

あなたはwhile True:ループで事を実行しています!つまり、CPUは常にループを実行しています。そこに小さな一時停止を追加するだけで、CPU使用率が急激に低下します。

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

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

Xorgのxclipパッケージが必要です。

sudo apt install xclip

catコマンドを使用してクリップボードの内容を画面にダンプしています。代わりにハードコピーが必要な場合は、プリンタ名、向き、および「ページに合わせる」オプションに置き換えcatlp指定します。

sleep 1.0プリンターで気付かないものを選択し、ユーザーがテキストを強調表示してCtrl+ を使用するよりも速いため、画面に少し遅れが表示されますC

強調表示されたまったく同じテキストをクリップボードにコピーしても、違いは生じません。多かれ少なかれ1文字が応答をトリガーします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.