用tkinter监视剪贴板,我的代码哪里出错了?

1 投票
3 回答
2079 浏览
提问于 2025-04-17 20:04

关于这段代码的一些描述:

  1. 这段代码是我程序的一部分,我只放了相关的几行代码。
  2. 我希望这些代码能够监视我的剪贴板。如果我把"http:xxx"复制到剪贴板,它就会弹出一个窗口。如果剪贴板的内容没有变化,这个窗口就不会再出现。
  3. 在运行时,它可以正常弹出窗口一次,但当我复制另一个以'http:'开头的字符串到剪贴板时,它就不会再弹出窗口了。
  4. 我在after方法中尝试了不同的时间间隔,结果都是一样的。

代码:

from tkinter import *
import os
import tkinter.messagebox as messagebox
import threading
import re

def watch_clipboard(tk,pipeout):  
    content = '' 
    last_content = ''
    while True:
        try:
            content = tk.clipboard_get()
        except TclError:
            pass
        result = re.match('http:',content)
        if content != last_content:
            if result:
                last_content = content
                message = 'show'.encode()    
                os.write(pipeout,message)


class GUI:
    def __init__(self):
        self.tk = Tk()
        self.tk.resizable(0, 0)
        self.tk.title('watch clipboard')
        pipein,pipeout = os.pipe()
        threading.Thread(target=watch_clipboard,daemon=True,args=(self.tk,pipeout)).start()
        self.tk.after(5000,lambda:self.clipboard_confirm(pipein))
        self.tk.mainloop()

    def clipboard_confirm(self,pipein):
        message = os.read(pipein,16)
        if message == b'show':
            self.tk.clipboard_clear()
            messagebox.askokcancel('', 'add this in?', default='ok')
            self.tk.after(5000,clipboard_confirm(pipein))   #add this


if __name__ == '__main__':
    gui = GUI()

编辑:A. Rodas的代码可以正常工作。看起来是多线程导致了这个问题。具体原因还不清楚。

3 个回答

0

这个问题很可能和使用线程有关——在主线程以外的地方调用Tkinter的函数可能会引发问题。

你为什么要使用线程,而不是利用内置的无限循环(事件循环)呢?我建议你可以使用after这个方法,每隔一秒检查一次键盘。

1

@laike9m
你的代码出错是因为用了 os.pipe。
os.read() 是一个会阻塞的函数,也就是说一旦运行了 clipboard_confirm,os.read() 就会一直等着,导致界面卡住。
这个问题和 after 以及多线程没有关系。

2

我觉得多线程在你的情况下不是最好的选择,使用 after 可能就够了。与其使用一个忙碌等待的循环,不如用更短的时间间隔来调用 tk.after。然后你只需要把 watch_clipboard 的逻辑放到你的类里面,这样就不用担心线程之间的沟通问题了。

class GUI:
    def __init__(self):
        self.tk = Tk()
        self.tk.resizable(0, 0)
        self.tk.title('watch clipboard')
        self.last_content = ''
        self.tk.after(100, self.watch_clipboard)
        self.tk.mainloop()
    def watch_clipboard(self):
        try:
            content = self.tk.clipboard_get()
            if content != self.last_content and content.startswith('http:'):
                self.last_content = content
                self.tk.clipboard_clear()
                messagebox.askokcancel('', 'add this in?', default='ok')
        except TclError:
            pass
        self.tk.after(100, self.watch_clipboard)

撰写回答