Python / Tkinter - Select and copy all Text contents to clipboard on Button click from another function

1.2k views Asked by At

I just started teaching myself Python in the last couple days to do some application programming and have previous experience developing websites with PHP. I've been building a program that will parse a list of information, build an array of collected variables, then load and populate an html template with those variables in a new Tkinter Toplevel window. The new window is created by a function that is called by a menubar command in the root window. All it contains is a text box with scrollbars and a few buttons that should allow the user to select all of the text, copy it to the clipboard, and close the window.

The issue I'm having, and I'm sure this will probably be a simple fix for somebody fluent in Python, is that I don't know how to properly reference everything when calling the select and copy functions from within other functions. If I strip down the code as if I'm only working out of one window, everything works as expected:

import tkinter as tk

def clipit():
    textpop.clipboard_clear()
    textpop.event_generate("<<TextModified>>")
    textpop.clipboard_append(textarea.get('1.0', 'end'))
    textpop.update()
    
def textselect():
    textpop.event_generate("<<TextModified>>")
    textarea.tag_add('sel', "1.0", 'end-1c')

textpop = tk.Tk()
textarea = tk.Text(textpop, wrap="none")
textarea.pack(side="left", fill="both", padx=20, pady=20)
textarea.insert("1.0", "This is a test - Try to select all and copy!")
exitbutton = tk.Button(textpop, text="Close", command = textpop.destroy)
exitbutton.pack(side="right", padx=(5,20), pady=(0,20))
copybutton = tk.Button(textpop, text="Copy", command = clipit)
copybutton.pack(side="right",padx=5, pady=(0,20))
selectbutton = tk.Button(textpop, text="Select All", command = textselect)
selectbutton.pack(side="right",padx=5, pady=(0,20))
textarea.focus()
textpop.mainloop()

If I try to do the same thing, but from within a function (where textpop = tk.Toplevel()), it doesn't work any longer. I've attempted passing various references to the functions (parent, widget, etc) and modifying the function code accordingly, but haven't had any luck getting it to work. For example:

import tkinter as tk

def clipit(parent,textwidget):
    parent.clipboard_clear()
    parent.event_generate("<<TextModified>>")
    parent.clipboard_append(textwidget.get('1.0', 'end'))
    parent.update()
    
def textselect(parent,textwidget):
    parent.event_generate("<<TextModified>>")
    parent.textwidget.tag_add('sel', "1.0", 'end-1c')

def textwindow(title,content):
    textpop = tk.Toplevel()
    textpop.title(title)
    textarea = tk.Text(textpop, wrap="none")
    textarea.pack(side="left", fill="both", padx=20, pady=20)
    textarea.insert("1.0", content)
    exitbutton = tk.Button(textpop, text="Close", command = textpop.destroy)
    exitbutton.pack(side="right", padx=(5,20), pady=(0,20))
    copybutton = tk.Button(textpop, text="Copy", command = lambda: clipit(textpop,textarea))
    copybutton.pack(side="right",padx=5, pady=(0,20))
    selectbutton = tk.Button(textpop, text="Select All", command = lambda: textselect(textpop,textarea))
    selectbutton.pack(side="right",padx=5, pady=(0,20))
    textarea.focus()
    textpop.mainloop()

window = tk.Tk()
window.title("Main Window")
launchbutton = tk.Button(window, text = "Launch Window", command = lambda: textwindow("Toplevel Popup", "Text Area Text"))
launchbutton.pack(padx=20,pady=20)
window.mainloop()

In my main script (and this example code), clicking the Select All button would result in the following error:

AttributeError: 'Toplevel' object has no attribute 'textwidget'

Is there something simple that I'm just missing because I'm new to the language?

Edit: Revised second example for clarity, based on Bryan's comment.

1

There are 1 answers

0
DJT On BEST ANSWER

In the process of building a functional example script to help people troubleshoot this for me, I think I found the culprit:

parent.textwidget.tag_add('sel', '1.0', 'end-1c')

Looks like I was maybe being a little too specific with my references, as removing the attempted parent reference fixed the problem with selecting the the contents of textwidget. I did also have to add a focus call for the textwidget to make it work, which I also threw into the function:

def textselect(parent,textwidget):
    parent.event_generate("<<TextModified>>")
    textwidget.focus()
    textwidget.tag_add('sel', '1.0', 'end')

Once I had it all working, I also realized that selecting the text is sort of redundant anyway and more of a visual thing, as the copy function will copy the entire contents of the text box regardless of whether it's highlighted.

Not 100% sure this is the best way to accomplish all this, but it works. If anyone has a better method, feel free to post it!