如果在程序退出前使用 "paste",Tk 仅复制到剪贴板

Tk only copies to clipboard if "paste" is used before program exits

环境

问题

我使用 tk 方法 clipboard_append() 将字符串复制到剪贴板。

当我的程序 运行 来自 Python 解释器时,数据被正确复制到剪贴板。

然而,当 运行 使用 "C:\Python36.exe myprogram.py" 时,我得到了一些奇怪的行为。

  1. 如果我 粘贴 数据,而程序仍在 运行ning,它会按预期工作。
  2. 如果我粘贴数据时程序正在运行ning,然后关闭程序,我可以继续粘贴数据。
  3. 如果我在复制之后但在粘贴之前关闭程序,剪贴板是空的。

问题

如何将 tk 复制到剪贴板,而不管包含的 window 发生什么情况?

我的代码

from tkinter import *
from tkinter import messagebox

url = 'http://testServer/feature/'

def copyToClipboard():
    top.clipboard_clear()
    top.clipboard_append(fullURL.get())
    top.update()
    top.destroy()

def updateURL(event):
    fullURL.set(url + featureNumber.get())

def submit(event):
    copyToClipboard()

top = Tk()
top.geometry("400x75")
top.title = "Get Test URL"

topRow = Frame(top)
topRow.pack(side = TOP)

bottomRow = Frame(top)
bottomRow.pack(side = BOTTOM)

featureLabel = Label(topRow, text="Feature Number")
featureLabel.pack(side = LEFT)

featureNumber =  Entry(topRow)
featureNumber.pack(side = RIGHT)

fullURL = StringVar()
fullURL.set(url)

fullLine = Label(bottomRow, textvariable=fullURL)
fullLine.pack(side = TOP)

copyButton = Button(bottomRow, text = "Copy", command = copyToClipboard)
copyButton.pack(side = TOP)

featureNumber.focus_set()
featureNumber.bind("<KeyRelease>", updateURL)
featureNumber.bind("<Return>", submit)

top.mainloop()

计划的目的

我的公司有一台用于新功能的测试服务器。每次我们创建新功能时,我们都需要在测试服务器上 post 一个 url 到它。 url 除了功能编号外是相同的,所以我创建了这个 python 程序来为我生成 url 并将其复制到剪贴板。

如果我注释掉 "top.destroy" 并在手动关闭 window 之前粘贴 url,我可以使它正常工作,但我真的很想避免这种情况。在一个完美的世界里,我会按一个快捷方式,让 window 弹出,输入我的功能号码,然后只需按回车键关闭 window 并粘贴新的 url,所有这些都不需要我的手离开了键盘。

如果您在粘贴剪贴板之前关闭 tk 应用程序,则剪贴板为空的问题是由于 tkinter 本身的问题。这已被报告过几次,这必须归因于 tkinter 处理剪贴板的惰性方式。

如果某些内容已设置到 tkinter 剪贴板但未粘贴,则 tkinter 将不会在应用程序关闭前附加 windows 剪贴板。所以解决这个问题的一种方法是告诉 tkinter 附加到 windows 剪贴板。

我一直在测试一种方法来做到这一点,但是它会导致应用程序过程中出现一些延迟,因此它可能不是最好的解决方案,但却是一个开始。使用 import ossystem 方法查看代码的修改版本。

from tkinter import *
from tkinter import messagebox
import os

top = Tk()
top.geometry("400x75")
top.title = "Get Test URL"

url = 'http://testServer/feature/'
fullURL = StringVar()
fullURL.set(url)

def copyToClipboard():
    top.clipboard_clear()
    top.clipboard_append(fullURL.get())
    os.system('echo {}| clip'.format(fullURL.get()))
    top.update()
    top.destroy()

def updateURL(event):
    fullURL.set(url + featureNumber.get())

def submit(event):
    copyToClipboard()

topRow = Frame(top)
topRow.pack(side = TOP)
bottomRow = Frame(top)
bottomRow.pack(side = BOTTOM)
featureLabel = Label(topRow, text="Feature Number")
featureLabel.pack(side = LEFT)
featureNumber =  Entry(topRow)
featureNumber.pack(side = RIGHT)
fullLine = Label(bottomRow, textvariable=fullURL)
fullLine.pack(side = TOP)
copyButton = Button(bottomRow, text = "Copy", command = copyToClipboard)
copyButton.pack(side = TOP)
featureNumber.focus_set()
featureNumber.bind("<Return>", submit)

top.mainloop()

当您 运行 代码时,您会看到代码冻结了应用程序,但一旦它在几秒钟后完成处理,它将关闭应用程序,您仍然可以粘贴剪贴板内容。此服务器演示如果我们可以在关闭 tkinter 应用程序之前写入 windows 剪贴板,它将按预期工作。我会寻找更好的方法,但这应该是您的起点。

这是已报告给 tkinter 的同一问题的几个链接。

issue23760

1844034fffffffffffff

732662ffffffffffffff

822002ffffffffffffff

更新:

这是一个使用库 pyperclip

的干净解决方案

这也是跨平台的:)

from tkinter import *
from tkinter import messagebox
import pyperclip

top = Tk()
top.geometry("400x75")
top.title = "Get Test URL"

url = 'http://testServer/feature/'
fullURL = StringVar()
fullURL.set(url)

def copyToClipboard():
    top.clipboard_clear()
    pyperclip.copy(fullURL.get())
    pyperclip.paste()
    top.update()
    top.destroy()

def updateURL(event):
    fullURL.set(url + featureNumber.get())

def submit(event):
    copyToClipboard()

topRow = Frame(top)
topRow.pack(side = TOP)
bottomRow = Frame(top)
bottomRow.pack(side = BOTTOM)
featureLabel = Label(topRow, text="Feature Number")
featureLabel.pack(side = LEFT)
featureNumber =  Entry(topRow)
featureNumber.pack(side = RIGHT)
fullLine = Label(bottomRow, textvariable=fullURL)
fullLine.pack(side = TOP)
copyButton = Button(bottomRow, text = "Copy", command = copyToClipboard)
copyButton.pack(side = TOP)
featureNumber.focus_set()
featureNumber.bind("<Return>", submit)

top.mainloop()