python / cpython

The Python programming language
https://www.python.org
Other
63.87k stars 30.57k forks source link

Tkinter ttk widgets cget() returning _tkinter.Tcl_Obj instead of str #126008

Open maj113 opened 1 month ago

maj113 commented 1 month ago

The docstring: """Return the resource value for a KEY given as string.""" claims it returns as a string however it returns a _tkinter.Tcl_Obj which seems to lack __eq__/the ability to compare it to a string so something like this:

is_normal = radio_button.cget("state") == tk.NORMAL

Will always return false no matter what unless its wrapped in a str()

I propose either clarifying the docstr or returning as a str() wrapped obj but i presume that's a breaking change

Linked PRs

Xiaokang2022 commented 1 month ago

Hello, @maj113

The result of my test is no problem, my environment is as follows:

The code I tested is as follows:

import tkinter

root = tkinter.Tk()

radio_button = tkinter.Radiobutton(root)
state = radio_button.cget("state")
is_normal = state == tkinter.NORMAL

print(f"{is_normal=}, {type(state)=}")

The output is as follows:

is_normal=True, type(state)=<class 'str'>

Obviously, it's not a _tkinter.Tcl_Obj. Therefore you should specifically point out the environment, as this may be a problem that only arises in certain environments. If there's a problem with the way I'm testing, feel free to point it out.

maj113 commented 1 month ago

Obviously, it's not a _tkinter.Tcl_Obj. Therefore you should specifically point out the environment, as this may be a problem that only arises in certain environments. If there's a problem with the way I'm testing, feel free to point it out.

I'm testing this on Windows 11 24H2 with the same behavior on (C)python 3.12.5 and 3.13.0

maj113 commented 1 month ago

okay the issue occurs ONLY with ttk widgets, not regular tk ones image

Xiaokang2022 commented 1 month ago

Ok, I'll try to create a PR later to fix this.

I don't think this should be solved just by modifying the docstring, _tkinter.Tcl_Obj should be converted to a string type.

brianzhouzc commented 1 month ago

I've encountered the same issue yesterday by accessing the state using the key directly. Took me way to long to figure out that the state isn't string type.

self.entry_box = ttk.Entry(self.root, width=40)

# This doesn't work!
if (self.entry_box['state'] == 'normal'):
    print('State is normal')

The weird thing is, if you access the state before hand by printing it as a string, then it becomes a string type.

import tkinter as tk
from tkinter import ttk, messagebox

class App:
    def __init__(self, root):
        self.root = root

        self.entry_box = ttk.Entry(self.root, width=40)
        self.entry_box.pack(padx=10, pady=10)

        messagebox.showinfo(message=f"Type: {type(self.entry_box['state'])}\nState: {self.entry_box['state']}\nType after access: {type(self.entry_box['state'])}")

if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()

Result:

Type: <class '_tkinter.Tcl_Obj'>
State: normal
Type after access: <class 'str'>
Xiaokang2022 commented 1 month ago

Hello, @brianzhouzc

I tested your code and it turned out to be different from yours:

Result:

Type: <class '_tkinter.Tcl_Obj'>
State: normal
Type after access: <class '_tkinter.Tcl_Obj'>

Perhaps you should point out your environment.

terryjreedy commented 1 month ago

@serhiy-storchaka We need a decision on what to do before too much effort is put into PRs.

serhiy-storchaka commented 1 month ago

The docstring is outdated. Perhaps it was written when all low-level Tkinter API returned only strings. "as string" should be removed.

brianzhouzc commented 1 month ago

Hello, @brianzhouzc

I tested your code and it turned out to be different from yours:

Result:

Type: <class '_tkinter.Tcl_Obj'>
State: normal
Type after access: <class '_tkinter.Tcl_Obj'>

Perhaps you should point out your environment.

Apologies, my environment is Windows 10 Business 22H2 19045.5011, Python 3.11.9.

Tried on Python 3.12.6 as well and the behaviours is indeed different, like what you have in your reply.

maj113 commented 1 month ago

The docstring is outdated. Perhaps it was written when all low-level Tkinter API returned only strings. "as string" should be removed.

I don't think just removing that would be enough to clarify the return value, it still might confuse people that the same key behaves differently comparing tk vs ttk, I haven't checked how tkinter.Tcl_Obj is implemented but maybe it can be extended with a eq so that at least string comparisons work as expected without the need to wrap it in a str

serhiy-storchaka commented 1 month ago

This is a Tk implementation detail. Depending on the key, the Tk version, the way of creating and modifying the widget, you can get a number, a boolean, a string, a tuple, a Tcl_Obj. If you get a Tcl_Obj and need a string, just call str().

Making Tcl_Obj comparable with str is an interesting idea, but this is a new feature which can only be added in a new Python version. It may require significant internal changes in Tkinter.

Xiaokang2022 commented 1 month ago

The docstring is outdated. Perhaps it was written when all low-level Tkinter API returned only strings. "as string" should be removed.

Is it possible that the "as string" in the comment refers to the KEY, not the return value? This can really be misleading.

But beyond that, it's also a problem that widgets of tk don't behave consistently with widgets of ttk.

picnixz commented 1 month ago

What I understood wrongly: the key is a string, the return value is a string as well because everything is stored as a string. But this was wrong! (I think?)

The key is a string, the return value is anything. Am I correct now?

Xiaokang2022 commented 1 month ago

The key is a string, the return value is anything. Am I correct now?

I think so.