I wanted to write a simple Tk application that would assist my students in running unit tests for their exercises in an introductory Python course.
I choose Tk, because it can be accessed from the Python standard library with the
tkinter module, which would allow my students to run the app without installing additional Python packages.
Inside the app, I needed a
Text widget to display the standard output and standard error streams of the unit test process.
The students should be able to copy text from this widget into a search engine to learn about the meaning of Python error messages, but they should not be able to alter the content of the widget accidentally by typing something into the widget.
Like most GUI toolkits, Tk allows to “disable” widgets so that they do not react to user input.
However, from there on it got a little more complicated.
insert() method used for setting the content of the text widget programmatically also becomes disabled when the widget state is set to
This can be fixed by wrapping calls to
insert() with an activation and deactivation call as follows:
text_widget.config(state="normal") text_widget.delete("1.0", "end") text_widget.insert("1.0", res) text_widget.config(state="disabled")
Once this was fixed, however, my tutors noticed that copying text with the usual shortcut CTRL-C did not work in Ubuntu. The curious thing: It did work on my Manjaro machine. What could be the reason of this platform dependent behavior?
Things I learned along the way
Before I tell you about the solution, I want to share a few discoveries with you that I made along the way:
My first instinct was check whether removing the lines containing
.config(state="disabled") would remove the problem.
According to my tutor it did not, so I searched for the problem online learning that…
- … there might be an issue related to language settings,
- … there is an issue involving
- … there are a lot of solutions for building a read-only
Textwidget, but all of them somehow feel wrong.
It seemed like disabling all keybindings manually by binding them to a function that returns
'break' might be an easy solution, but this seemed a little too hacky for me, so I went on to do some research on the interna of Tk.
The first interesting thing that I found was a mention of a file called
tk.tcl, which apparently is responsible for setting up the key bindings for copy-paste commands.
I did find the file at
/usr/lib/tk8.6/tk.tcl in Manjaro and
/usr/share/tcltk/tk8.6/tk.tcl in Ubuntu and by looking at the contents I could confirm that…
- … the only variable that controls how the “virtual event”
<<Copy>>is set up, is
tk windowingsystem, which can be
- … if you put
puts "<insert frustrated slur here>"somewhere into the file, you are indeed insulted by every Tk app you start afterwards, including
To find an easier way to check the value of
tk windowingsystem than meddling with the
tk.tcl file, I then searched for a way to execute Tcl/Tk commands.
It turns out that there are two shells
wish, the former being a REPL for plain Tcl and the latter already including the Tk library.
wish, you can then simply type
tk windowingsystem and as suspected it returned
x11 for both Linux systems, ruling out the
tk.tcl file as possible culprit.
Since the next possible offender was
ScrolledText, I looked at the source code for the
tkinter.scrolledtext module and found out that the relevant class is only a little more than 20 lines of code, which in passing alleviated my concerns how hard it would be to write a version of
ScrolledText that used themed
After a lot of back and forth changing search terms, I finally arrived at a StackOverflow post that mentioned the following with regard to using the disabled state for a read-only widget.
On some platforms, you also need to add a binding on <1> to give the focus to the widget, otherwise the highlighting for copy doesn’t appear:
text_widget.bind("<1>", lambda event: text_widget.focus_set())
Having set up a VM with a vanilla Ubuntu installation in the meantime, this led me to re-investigate the issue regarding the disabled state again myself.
Sure enough, the copy-paste problem did vanish when I disabled the two relevant lines that set the disabled state. So there is another lesson in passing here: If you can only communicate via text, make absolutely sure that what you think is being tested is actually what is being tested. Back to the problem, I re-enabled the code lines that set the disabled state and used the suggestion from Bryan Oakley from StackOverflow instead and it did also work.
So the problem was solved, but I was still not satisfied, because I wanted to know the cause of this difference. Who is responsible here? Is it Tk, is it Linux? Are there really such fundamental differences between Ubuntu and Manjaro? My journey took me to the release notes of Tcl/Tk and I found the following bullet point in the notes for version 8.6.11:
- Allow for select/copy from disabled text widget on all platforms
So it was just a bug in Tk after all and the suggested fix to set the focus when the mouse button is clicked is the correct solution. Whew!
Sure enough, I checked the package versions and my Manjaro used version
188.8.131.52-1 of the
tk package while Ubuntu used version
As a last bit of information, if you want to find out your Tk version from within a Tk app, you can query
root.tk.call("info", "patchlevel") where
root is your root-level
I hope you enjoyed this little murder mystery as much as I did, even if it was just the copy-paste function that was murdered and at the end of the day it turned out that it only did not find the way home, since it was lacking focus. 😆