TripleD
TripleD

Reputation: 339

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

Environment

The Problem

I use the tk method clipboard_append() to copy a string to the clipboard.

When my program is run from the Python interpreter data is copied to the clipboard correctly.

When run using "C:\Python36.exe myprogram.py" however, I get some weird behavior.

  1. If I paste the data while the program is still running, it works as expected.
  2. If I paste the data while the program is running, then close the program, I can continue to paste the data.
  3. If I close the program after copying but before pasting, the clipboard is empty.

Question

How do I make tk copy to the clipboard regardless of what happens to the containing window?

My Code

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

Purpose of the Program

My company has a test server we use for new features. Every time we create a new feature we need to post a url to it on the test server. The urls are identical save for the feature number, so I created this python program to generate the url for me and copy it to the clipboard.

I can get this working if I comment out "top.destroy" and paste the url before manually closing the window, but I would really like to avoid this. In a perfect world I would press a shortcut, have the window pop up, enter my feature number, then just press enter to close the window and paste the new url, all without taking my hands off the keyboard.

Upvotes: 5

Views: 2445

Answers (1)

Mike - SMT
Mike - SMT

Reputation: 15226

Your issue about the clipboard being empty if you close the tk app before pasting the clipboard is due to an issue in tkinter itself. This has been reported a few times and it has to due with the lazy way tkinter handles the clipboard.

If something is set to the tkinter clipboard but is not pasted then tkinter will not append the windows clipboard before the app is closed. So one way around that is to tell tkinter to append to the windows clipboard.

I have been testing a method to do that however it is causing some delay in the applications process so its probably not the best solution but is a start. Take a look at this modified version of your code using the import os with the system method.

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

When you run the code you will see that the code freezes the app but once its finishes processing after a few seconds it will have closed the app and you can still paste the clipboards content. this servers to demonstrate that if we can write to the windows clipboard before the tkinter app is closed it will work as intended. I will look for a better method but this should be a starting point for you.

Here is a few links of the same issue that has been reported to tkinter.

issue23760

1844034fffffffffffff

732662ffffffffffffff

822002ffffffffffffff

UPDATE:

Here is a clean solution that uses the library pyperclip

This is also cross platform :)

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

Upvotes: 4

Related Questions