Skip to content

'cget' returns '_tkinter.Tcl_Obj' instead of 'str' when called on a class widget inheriting from ttk #98815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JRiggles opened this issue Oct 28, 2022 · 2 comments
Labels
topic-tkinter type-bug An unexpected behavior, bug, or error

Comments

@JRiggles
Copy link
Contributor

JRiggles commented Oct 28, 2022

Bug report

Calling cget on a widget typically returns a str value, but when called on a custom widget class that inherits from ttk, cget returns _tkinter.Tcl_Obj instead. This issue was demonstrated in this StackOverflow Question, where it was suggested that I submit this bug report.

EXAMPLE:

import tkinter as tk
from tkinter import ttk
# some tkinter boilerplate (i.e. 'root = tk.Tk()') has been left out for brevity

# tk Entry widget
tk_entry = tk.Entry()
print(type(tk_entry.cget('fg')))
# => <class 'str'>

# ttk Entry widget
ttk_entry = ttk.Entry()
print(type(ttk_entry.cget('foreground')))
# => <class 'str'>

# subclassed ttk Entry widget
ph_entry = PlaceholderEntry()
print(type(ph_entry.cget('foreground')))
# => <class '_tkinter.Tcl_Obj'> ... Why is this different?

# ultra basic subclass widget
basic_entry = BasicEntry()
print(type(basic_entry.cget('foreground')))
# => <class 'str'>


# demo class inheriting from ttk.Entry (as shown on StackOverflow)
class PlaceholderEntry(ttk.Entry):
    """Entry widget with focus-toggled placeholder text"""
    def __init__(
        self, parent, placeholder='', color='#828790', *args, **kwargs
    ):
        super().__init__(parent, *args, **kwargs)
        self.placeholder = placeholder
        self._ph_color = color
        self._default_fg = self.cget('foreground')  # default foreground color
        # focus bindings
        self.bind('<FocusOut>', self.set_placeholder)
        self.bind('<FocusIn>', self.clear_placeholder)
        # initialize placeholder
        self.set_placeholder()

    def set_placeholder(self, *args):  # on focus out
        if not self.get():  # if the entry has no text...
            self.insert(0, self.placeholder)
            self.configure(foreground=self._ph_color)

    def clear_placeholder(self, *args): # on focus in
        if str(self.cget('foreground')) == self._ph_color:  # str() is used here to make this work as intended
            self.delete(0, tk.END)
            self.configure(foreground=self._default_fg)


# minimal demo class inheriting from ttk.Entry that *doesn't* have this issue
class BasicEntry(ttk.Entry):
    def __init__(self, parent):
        super().__init__(parent)

Your environment

  • Running code directly from VSCode
  • CPython versions tested on: 3.11.0, 64-bit
  • Operating system and architecture: Windows 10
@JRiggles JRiggles added the type-bug An unexpected behavior, bug, or error label Oct 28, 2022
@daniilS
Copy link

daniilS commented Feb 10, 2023

I've encountered an issue that might be related. I was having trouble debugging a function that called cget and sometimes returned a _tkinter.Tcl_Obj, and discovered that the reason it felt impossible to debug was due to print statements affecting the outcome.

A minimal example that shows this behaviour:

import tkinter as tk
from tkinter import ttk


class Button(ttk.Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs, command=self.callback)

    def callback(self):
        print(type(self.cget("padding")[0]))

        # begin magic
        print(self.cget("padding"))
        # end magic

        print(type(self.cget("padding")[0]))


root = tk.Tk()
button = Button(root, text="Magic", padding=5)
button.pack()

button.invoke()

# <class '_tkinter.Tcl_Obj'>
# (<string object: '5'>,)
# <class 'str'>

@serhiy-storchaka
Copy link
Member

The difference between ttk.Entry and PlaceholderEntry is caused by setting non-empty foreground option for the latter. Simple example:

from tkinter import ttk
entry = ttk.Entry()
print(repr(entry.cget('foreground')))  # output: ''
entry.configure(foreground='#828790')
print(repr(entry.cget('foreground')))  # output: <color object: '#828790'>
entry.configure(foreground='')
print(repr(entry.cget('foreground')))  # output: ''

Tkinter converts some Tcl types (numbers, strings, etc) to corresponding Python types and use Tcl_Obj to wrap all other types. You can use the string attribute or call str() to get the string representation for them.

The second issue is a duplicate of #101830. It was just fixed, wait for the next bugfix release.

@serhiy-storchaka serhiy-storchaka closed this as not planned Won't fix, can't repro, duplicate, stale Jun 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-tkinter type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants