Skip to content

Commit c7b28ca

Browse files
[tkinter.ttk] Added type annotations for Style, fix some other incomplete parts (#14348)
1 parent 4fa56fb commit c7b28ca

File tree

6 files changed

+165
-15
lines changed

6 files changed

+165
-15
lines changed

stdlib/@tests/stubtest_allowlists/py310.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ posixpath.join
2525
ntpath.join
2626
os.path.join
2727

28+
# this is implemented with *args having a minimum size so arguments before it must be positional (but stubtest doesn't see that)
29+
tkinter.ttk.Style.element_create
30+
2831
# typing.IO uses positional-or-keyword arguments, but in the stubs we prefer
2932
# to mark these as positional-only for compatibility with existing sub-classes.
3033
typing(_extensions)?\.BinaryIO\.write

stdlib/@tests/stubtest_allowlists/py311.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ posixpath.join
4545
ntpath.join
4646
os.path.join
4747

48+
# this is implemented with *args having a minimum size so arguments before it must be positional (but stubtest doesn't see that)
49+
tkinter.ttk.Style.element_create
50+
4851
# typing.IO uses positional-or-keyword arguments, but in the stubs we prefer
4952
# to mark these as positional-only for compatibility with existing sub-classes.
5053
typing(_extensions)?\.BinaryIO\.write

stdlib/@tests/stubtest_allowlists/py312.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ posixpath.join
6262
ntpath.join
6363
os.path.join
6464

65+
# this is implemented with *args having a minimum size so arguments before it must be positional (but stubtest doesn't see that)
66+
tkinter.ttk.Style.element_create
67+
6568
# typing.IO uses positional-or-keyword arguments, but in the stubs we prefer
6669
# to mark these as positional-only for compatibility with existing sub-classes.
6770
typing(_extensions)?\.BinaryIO\.write

stdlib/@tests/stubtest_allowlists/py313.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ posixpath.join
6262
ntpath.join
6363
os.path.join
6464

65+
# this is implemented with *args having a minimum size so arguments before it must be positional (but stubtest doesn't see that)
66+
tkinter.ttk.Style.element_create
67+
6568
# typing.IO uses positional-or-keyword arguments, but in the stubs we prefer
6669
# to mark these as positional-only for compatibility with existing sub-classes.
6770
typing(_extensions)?\.BinaryIO\.write

stdlib/@tests/stubtest_allowlists/py314.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ posixpath.join
8484
ntpath.join
8585
os.path.join
8686

87+
# this is implemented with *args having a minimum size so arguments before it must be positional (but stubtest doesn't see that)
88+
tkinter.ttk.Style.element_create
89+
8790
# typing.IO uses positional-or-keyword arguments, but in the stubs we prefer
8891
# to mark these as positional-only for compatibility with existing sub-classes.
8992
typing(_extensions)?\.BinaryIO\.write

stdlib/tkinter/ttk.pyi

Lines changed: 150 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import _tkinter
2+
import sys
23
import tkinter
3-
from _typeshed import Incomplete, MaybeNone
4-
from collections.abc import Callable
4+
from _typeshed import MaybeNone
5+
from collections.abc import Callable, Iterable
56
from tkinter.font import _FontDescription
67
from typing import Any, Literal, TypedDict, overload, type_check_only
7-
from typing_extensions import TypeAlias
8+
from typing_extensions import Never, TypeAlias, Unpack
89

910
__all__ = [
1011
"Button",
@@ -35,7 +36,7 @@ __all__ = [
3536
]
3637

3738
def tclobjs_to_py(adict: dict[Any, Any]) -> dict[Any, Any]: ...
38-
def setup_master(master=None): ...
39+
def setup_master(master: tkinter.Misc | None = None): ...
3940

4041
_Padding: TypeAlias = (
4142
tkinter._ScreenUnits
@@ -48,19 +49,153 @@ _Padding: TypeAlias = (
4849
# from ttk_widget (aka ttk::widget) manual page, differs from tkinter._Compound
4950
_TtkCompound: TypeAlias = Literal["", "text", "image", tkinter._Compound]
5051

52+
# Last item (option value to apply) varies between different options so use Any.
53+
# It could also be any iterable with items matching the tuple, but that case
54+
# hasn't been added here for consistency with _Padding above.
55+
_Statespec: TypeAlias = tuple[Unpack[tuple[str, ...]], Any]
56+
_ImageStatespec: TypeAlias = tuple[Unpack[tuple[str, ...]], tkinter._ImageSpec]
57+
_VsapiStatespec: TypeAlias = tuple[Unpack[tuple[str, ...]], int]
58+
59+
class _Layout(TypedDict, total=False):
60+
side: Literal["left", "right", "top", "bottom"]
61+
sticky: str # consists of letters 'n', 's', 'w', 'e', may contain repeats, may be empty
62+
unit: Literal[0, 1] | bool
63+
children: _LayoutSpec
64+
# Note: there seem to be some other undocumented keys sometimes
65+
66+
# This could be any sequence when passed as a parameter but will always be a list when returned.
67+
_LayoutSpec: TypeAlias = list[tuple[str, _Layout | None]]
68+
69+
# Keep these in sync with the appropriate methods in Style
70+
class _ElementCreateImageKwargs(TypedDict, total=False):
71+
border: _Padding
72+
height: tkinter._ScreenUnits
73+
padding: _Padding
74+
sticky: str
75+
width: tkinter._ScreenUnits
76+
77+
_ElementCreateArgsCrossPlatform: TypeAlias = (
78+
# Could be any sequence here but types are not homogenous so just type it as tuple
79+
tuple[Literal["image"], tkinter._ImageSpec, Unpack[tuple[_ImageStatespec, ...]], _ElementCreateImageKwargs]
80+
| tuple[Literal["from"], str, str]
81+
| tuple[Literal["from"], str] # (fromelement is optional)
82+
)
83+
if sys.platform == "win32" and sys.version_info >= (3, 13):
84+
class _ElementCreateVsapiKwargsPadding(TypedDict, total=False):
85+
padding: _Padding
86+
87+
class _ElementCreateVsapiKwargsMargin(TypedDict, total=False):
88+
padding: _Padding
89+
90+
class _ElementCreateVsapiKwargsSize(TypedDict):
91+
width: tkinter._ScreenUnits
92+
height: tkinter._ScreenUnits
93+
94+
_ElementCreateVsapiKwargsDict: TypeAlias = (
95+
_ElementCreateVsapiKwargsPadding | _ElementCreateVsapiKwargsMargin | _ElementCreateVsapiKwargsSize
96+
)
97+
_ElementCreateArgs: TypeAlias = ( # noqa: Y047 # It doesn't recognise the usage below for whatever reason
98+
_ElementCreateArgsCrossPlatform
99+
| tuple[Literal["vsapi"], str, int, _ElementCreateVsapiKwargsDict]
100+
| tuple[Literal["vsapi"], str, int, _VsapiStatespec, _ElementCreateVsapiKwargsDict]
101+
)
102+
else:
103+
_ElementCreateArgs: TypeAlias = _ElementCreateArgsCrossPlatform
104+
_ThemeSettingsValue = TypedDict(
105+
"_ThemeSettingsValue",
106+
{
107+
"configure": dict[str, Any],
108+
"map": dict[str, Iterable[_Statespec]],
109+
"layout": _LayoutSpec,
110+
"element create": _ElementCreateArgs,
111+
},
112+
total=False,
113+
)
114+
_ThemeSettings: TypeAlias = dict[str, _ThemeSettingsValue]
115+
51116
class Style:
52-
master: Incomplete
117+
master: tkinter.Misc
53118
tk: _tkinter.TkappType
54119
def __init__(self, master: tkinter.Misc | None = None) -> None: ...
55-
def configure(self, style, query_opt=None, **kw): ...
56-
def map(self, style, query_opt=None, **kw): ...
57-
def lookup(self, style, option, state=None, default=None): ...
58-
def layout(self, style, layoutspec=None): ...
59-
def element_create(self, elementname, etype, *args, **kw) -> None: ...
60-
def element_names(self): ...
61-
def element_options(self, elementname): ...
62-
def theme_create(self, themename, parent=None, settings=None) -> None: ...
63-
def theme_settings(self, themename, settings) -> None: ...
120+
# For these methods, values given vary between options. Returned values
121+
# seem to be str, but this might not always be the case.
122+
@overload
123+
def configure(self, style: str) -> dict[str, Any] | None: ... # Returns None if no configuration.
124+
@overload
125+
def configure(self, style: str, query_opt: str, **kw: Any) -> Any: ...
126+
@overload
127+
def configure(self, style: str, query_opt: None = None, **kw: Any) -> None: ...
128+
@overload
129+
def map(self, style: str, query_opt: str) -> _Statespec: ...
130+
@overload
131+
def map(self, style: str, query_opt: None = None, **kw: Iterable[_Statespec]) -> dict[str, _Statespec]: ...
132+
def lookup(self, style: str, option: str, state: Iterable[str] | None = None, default: Any | None = None) -> Any: ...
133+
@overload
134+
def layout(self, style: str, layoutspec: _LayoutSpec) -> list[Never]: ... # Always seems to return an empty list
135+
@overload
136+
def layout(self, style: str, layoutspec: None = None) -> _LayoutSpec: ...
137+
@overload
138+
def element_create(
139+
self,
140+
elementname: str,
141+
etype: Literal["image"],
142+
default_image: tkinter._ImageSpec,
143+
/,
144+
*imagespec: _ImageStatespec,
145+
border: _Padding = ...,
146+
height: tkinter._ScreenUnits = ...,
147+
padding: _Padding = ...,
148+
sticky: str = ...,
149+
width: tkinter._ScreenUnits = ...,
150+
) -> None: ...
151+
@overload
152+
def element_create(self, elementname: str, etype: Literal["from"], themename: str, fromelement: str = ..., /) -> None: ...
153+
if sys.platform == "win32" and sys.version_info >= (3, 13): # and tk version >= 8.6
154+
# margin, padding, and (width + height) are mutually exclusive. width
155+
# and height must either both be present or not present at all. Note:
156+
# There are other undocumented options if you look at ttk's source code.
157+
@overload
158+
def element_create(
159+
self,
160+
elementname: str,
161+
etype: Literal["vsapi"],
162+
class_: str,
163+
part: int,
164+
vs_statespec: _VsapiStatespec = ...,
165+
/,
166+
*,
167+
padding: _Padding = ...,
168+
) -> None: ...
169+
@overload
170+
def element_create(
171+
self,
172+
elementname: str,
173+
etype: Literal["vsapi"],
174+
class_: str,
175+
part: int,
176+
vs_statespec: _VsapiStatespec = ...,
177+
/,
178+
*,
179+
margin: _Padding = ...,
180+
) -> None: ...
181+
@overload
182+
def element_create(
183+
self,
184+
elementname: str,
185+
etype: Literal["vsapi"],
186+
class_: str,
187+
part: int,
188+
vs_statespec: _VsapiStatespec = ...,
189+
/,
190+
*,
191+
width: tkinter._ScreenUnits,
192+
height: tkinter._ScreenUnits,
193+
) -> None: ...
194+
195+
def element_names(self) -> tuple[str, ...]: ...
196+
def element_options(self, elementname: str) -> tuple[str, ...]: ...
197+
def theme_create(self, themename: str, parent: str | None = None, settings: _ThemeSettings | None = None) -> None: ...
198+
def theme_settings(self, themename: str, settings: _ThemeSettings) -> None: ...
64199
def theme_names(self) -> tuple[str, ...]: ...
65200
@overload
66201
def theme_use(self, themename: str) -> None: ...
@@ -615,7 +750,7 @@ class Panedwindow(Widget, tkinter.PanedWindow):
615750
) -> dict[str, tuple[str, str, str, Any, Any]] | None: ...
616751
@overload
617752
def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ...
618-
forget: Incomplete
753+
forget = tkinter.PanedWindow.forget
619754
def insert(self, pos, child, **kw) -> None: ...
620755
def pane(self, pane, option=None, **kw): ...
621756
def sashpos(self, index, newpos=None): ...

0 commit comments

Comments
 (0)