在剪贴板内容改变时触发事件

20 投票
4 回答
22283 浏览
提问于 2025-04-17 14:41

我想在我的Mac Lion上用Python脚本获取剪贴板的内容。

我在找一个事件或者类似的东西,因为如果我用循环的话,我的程序就会一直在盯着剪贴板,浪费很多时间。

有没有什么好主意?

4 个回答

1

简单!

import os
def macSetClipboard(text):
    outf = os.popen('pbcopy', 'w')
    outf.write(text)
    outf.close()

def macGetClipboard():
    outf = os.popen('pbpaste', 'r')
    content = outf.read()
    outf.close()
    return content
current_clipboard = macGetClipboard()
while True:
   clipboard = macGetClipboard()
   
   if clipboard != current_clipboard:
       print(clipboard)
       macSetClipboard("my new string")
       print(macGetClipboard())
       break
2

在Macosx上,pyperclip的核心内容是:

import os
def macSetClipboard(text):
    outf = os.popen('pbcopy', 'w')
    outf.write(text)
    outf.close()

def macGetClipboard():
    outf = os.popen('pbpaste', 'r')
    content = outf.read()
    outf.close()
    return content

这些对我有效,你觉得怎么样?

我不太明白你说的在循环中是什么意思。


编辑 添加了一个糟糕的轮询示例,展示了每次copy到剪贴板时,changeCount()是如何增加的。不过这仍然不是提问者想要的,因为似乎没有事件或通知可以用来监测NSPasteboard的修改。

from LaunchServices import *
from AppKit import *
import os

from threading import Timer

def poll_clipboard():
    pasteboard = NSPasteboard.generalPasteboard()
    print pasteboard.changeCount()

def main():
    while True:
        t = Timer(1, poll_clipboard)
        t.start()
        t.join()

if __name__ == "__main__":
    main()
24

你有没有想过用一个无限循环,并在每次尝试之间“休眠”一下呢?我用过pyperclip这个库来做一个简单的演示,结果在Windows和Linux上都很好用。

import time
import sys
import os

import pyperclip

recent_value = ""
while True:
    tmp_value = pyperclip.paste()
    if tmp_value != recent_value:
        recent_value = tmp_value
        print("Value changed: %s" % str(recent_value)[:20])
    time.sleep(0.1)

在这里,你可以用你想做的事情来替代print


下面是一个完整的多线程示例。

import time
import threading

import pyperclip

def is_url_but_not_bitly(url):
    if url.startswith("http://") and not "bit.ly" in url:
        return True
    return False

def print_to_stdout(clipboard_content):
    print ("Found url: %s" % str(clipboard_content))

class ClipboardWatcher(threading.Thread):
    def __init__(self, predicate, callback, pause=5.):
        super(ClipboardWatcher, self).__init__()
        self._predicate = predicate
        self._callback = callback
        self._pause = pause
        self._stopping = False
            
    def run(self):       
        recent_value = ""
        while not self._stopping:
            tmp_value = pyperclip.paste()
            if tmp_value != recent_value:
                recent_value = tmp_value
                if self._predicate(recent_value):
                    self._callback(recent_value)
            time.sleep(self._pause)
    
    def stop(self):
        self._stopping = True

def main():
    watcher = ClipboardWatcher(is_url_but_not_bitly, 
                               print_to_stdout,
                               5.)
    watcher.start()
    while True:
        try:
            print("Waiting for changed clipboard...")
            time.sleep(10)
        except KeyboardInterrupt:
            watcher.stop()
            break
    

if __name__ == "__main__":
    main()

我创建了一个线程的子类,重写了run__init__这两个方法,然后创建了这个类的一个实例。通过调用watcher.start()(而不是run()!),你就可以启动这个线程。

为了安全地停止这个线程,我会等到按下-C(键盘中断),然后告诉线程自己停止。

在这个类的初始化中,你还有一个pause参数,可以用来控制每次尝试之间等待多久。

像我示例中那样使用ClipboardWatcher类,把回调函数替换成你想做的,比如lambda x: bitly(x, username, password)

撰写回答