pythontkintertcl

Python Tkinter "tag mechanism" broken - _tkinter.TclError: invalid boolean operator in tag search expression


The following [mre]:

import tkinter as tk

root = tk.Tk()
cnvs = tk.Canvas(root)
cnvs.pack()
labl = tk.Label(cnvs, text='hello world')
laid = cnvs.create_window(0,0,window=labl,anchor='nw', tags=str(labl))
tags = cnvs.gettags(laid)
cnvs.find_withtag(tags)
root.mainloop()

produces this error stack:

Traceback (most recent call last):
  File "C:\Users\PC\Desktop\mre.py", line 9, in <module>
    cnvs.find_withtag(tags)
  File "C:\Users\PC\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2922, in find_withtag
    return self.find('withtag', tagOrId)
  File "C:\Users\PC\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2889, in find
    self.tk.call((self._w, 'find') + args)) or ()
_tkinter.TclError: invalid boolean operator in tag search expression

EDIT I edited the tag tcl since I'm unsure if the same mistake can happen in tcl as well. Please feel free to remove the tag again, if this is not the case. However, please leave a comment why this can't happen in tcl.

The approximate script would seem like this

package require Tk

pack [canvas .!canvas]
lable .!canvas.!label -text {hello world}
set laid [.!canvas create window 0 0 -anchor nw -window .!canvas.!label]
.!canvas addtag .!canvas.!label withtag $laid
puts .!canvas find withtag .!canvas.!label

Solution

  • The root of the problem is that tkinter by default adds a ! to the front of the name of each widget. The ! is interpreted by the find command as a boolean operator. When passed to find, the string .!canvas.!label is treated as an expression. It is an invalid expression which is why you get the error "_tkinter.TclError: invalid boolean operator in tag search expression"

    These are all defined behaviors, but are an unfortunate consequence of the tkinter team choosing to add a ! in front of dynamically created widget names.

    One answer to this question suggests removing the ! from the widget name. That is a reasonable solution. I'll offer a separate solution, which is to give your widgets explicit names without the !. You can do this with the name parameter when creating the canvas and the label.

    This will create a tag with the text ".canvas.label", which will work with your find command.

    import tkinter as tk
    
    root = tk.Tk()
    cnvs = tk.Canvas(root, name="canvas")
    cnvs.pack()
    labl = tk.Label(cnvs, text='hello world', name="label")
    laid = cnvs.create_window(0,0,window=labl,anchor='nw', tags=str(labl))
    tags = cnvs.gettags(laid)
    cnvs.find_withtag(tags)
    root.mainloop()