tkinter "scrolledtext" copy paste doesn't work reliably

1k Views Asked by At

I have found a behaviour which seems to be a bug in tkinter. If you run the following (minimal to reproduce the bug) code:

import tkinter, tkinter.simpledialog, tkinter.scrolledtext
root = tkinter.Tk('test')
text = tkinter.scrolledtext.ScrolledText(master=root, wrap='none')
text.pack(side="top", fill="both", expand=True, padx=0, pady=0)
text.insert(tkinter.END, 'abc\ndef\nghi\nijk')
root.mainloop()

then:

  • select one row in the scrolledtext widget, e.g. the row "ghi",
  • copy it with CTRL+C
  • do nothing else and close the app

Then paste it (CTRL+V) in any other Windows app: it won't work, nothing will be pasted. Why?

How to solve this?


Note: the expected behaviour is that text copied with CTRL+C should persist in the clipboard even if the app is closed. This is the default behaviour in many Windows software. Example here with notepad.exe: link to the animated screen capture: https://i.imgur.com/li7UvYw.mp4

enter image description here

Note: this is linked to

3

There are 3 best solutions below

4
On

You can also use pyperclip which supports Windows, Linux and Mac

import tkinter as tk
import pyperclip

def copy(event:tk.Event=None) -> str:
    try:
        text = text_widget.selection_get()
        pyperclip.copy(text)
    except tk.TclError:
        pass
    return "break"

root = tk.Tk()

text_widget = tk.Text(root)
text_widget.pack()
text_widget.bind("<Control-c>", copy)

root.mainloop()
4
On

Using pyperclip and root.bind_all() we can solve the problem.

import tkinter, tkinter.simpledialog, tkinter.scrolledtext 
import pyperclip as clip

root = tkinter.Tk('test')

text = tkinter.scrolledtext.ScrolledText(master=root, wrap='none')
def _copy(event):
   try:
      string = text.selection_get()
      clip.copy(string)
   except:pass

root.bind_all("<Control-c>",_copy)

text.pack(side="top", fill="both", expand=True, padx=0, pady=0)
text.insert(tkinter.END,'abc\ndef\nghi\njkl')
root.mainloop()

3
On

For a Windows only solution try this:

import tkinter as tk
import os

def copy(event:tk.Event=None) -> str:
    try:
        # Get the selected text
        # Taken from: https://stackoverflow.com/a/4073612/11106801
        text = text_widget.selection_get()
        # Copy the text
        # Inspired from: https://codegolf.stackexchange.com/a/111405
        os.system("echo.%s|clip" % text)
        print(f"{text!r} is in the clipboard")
    # No selection was made:
    except tk.TclError:
        pass
    # Stop tkinter's built in copy:
    return "break"

root = tk.Tk()

text_widget = tk.Text(root)
text_widget.pack()
text_widget.bind("<Control-c>", copy)

root.mainloop()

Basically I call my own copy function whenever the user presses Control-C. In that function I use the clip.exe program that is part of the OS to copy the text.

Note: my approach to copying data to the clipboard using os.system, isn't great as you can't copy | characters. I recommend looking here for better ways. You just need to replace that 1 line of code.