Skip to content

make tkinter.Event generic and add missing type hints to bind methods #4347

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

Merged
merged 5 commits into from
Jul 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 91 additions & 14 deletions stdlib/3/tkinter/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import sys
from enum import Enum
from tkinter.constants import * # noqa: F403
from types import TracebackType
from typing import Any, Callable, Dict, Optional, Tuple, Type, Union
from typing import Any, Callable, Dict, Generic, Optional, Tuple, Type, TypeVar, Union, overload
from typing_extensions import Literal

TclError: Any
wantobjects: Any
Expand Down Expand Up @@ -52,7 +53,10 @@ if sys.version_info >= (3, 6):
VirtualEvent: str = ...
Visibility: str = ...

class Event:
# Events considered covariant because you should never assign to event.widget.
_W = TypeVar("_W", covariant=True, bound="Misc")

class Event(Generic[_W]):
serial: int
num: int
focus: bool
Expand All @@ -73,7 +77,7 @@ class Event:
type: EventType
else:
type: str
widget: Misc
widget: _W
delta: int

def NoDefaultRoot(): ...
Expand Down Expand Up @@ -119,6 +123,8 @@ getdouble: Any

def getboolean(s): ...

# This class is the base class of all widgets. Don't use BaseWidget or Widget
# for that because Tk doesn't inherit from Widget or BaseWidget.
class Misc:
def destroy(self): ...
def deletecommand(self, name): ...
Expand Down Expand Up @@ -222,12 +228,47 @@ class Misc:
def update(self): ...
def update_idletasks(self): ...
def bindtags(self, tagList: Optional[Any] = ...): ...
def bind(self, sequence: Optional[Any] = ..., func: Optional[Any] = ..., add: Optional[Any] = ...): ...
def unbind(self, sequence, funcid: Optional[Any] = ...): ...
def bind_all(self, sequence: Optional[Any] = ..., func: Optional[Any] = ..., add: Optional[Any] = ...): ...
def unbind_all(self, sequence): ...
def bind_class(self, className, sequence: Optional[Any] = ..., func: Optional[Any] = ..., add: Optional[Any] = ...): ...
def unbind_class(self, className, sequence): ...
# bind with isinstance(func, str) doesn't return anything, but all other
# binds do. The default value of func is not str.
@overload
def bind(
self,
sequence: Optional[str] = ...,
func: Optional[Callable[[Event[Misc]], Optional[Literal["break"]]]] = ...,
add: Optional[bool] = ...,
) -> str: ...
@overload
def bind(self, sequence: Optional[str], func: str, add: Optional[bool] = ...) -> None: ...
@overload
def bind(self, *, func: str, add: Optional[bool] = ...) -> None: ...
# There's no way to know what type of widget bind_all and bind_class
# callbacks will get, so those are Misc.
@overload
def bind_all(
self,
sequence: Optional[str] = ...,
func: Optional[Callable[[Event[Misc]], Optional[Literal["break"]]]] = ...,
add: Optional[bool] = ...,
) -> str: ...
@overload
def bind_all(self, sequence: Optional[str], func: str, add: Optional[bool] = ...) -> None: ...
@overload
def bind_all(self, *, func: str, add: Optional[bool] = ...) -> None: ...
@overload
def bind_class(
self,
className: str,
sequence: Optional[str] = ...,
func: Optional[Callable[[Event[Misc]], Optional[Literal["break"]]]] = ...,
add: Optional[bool] = ...,
) -> str: ...
@overload
def bind_class(self, className: str, sequence: Optional[str], func: str, add: Optional[bool] = ...) -> None: ...
@overload
def bind_class(self, className: str, *, func: str, add: Optional[bool] = ...) -> None: ...
def unbind(self, sequence: str, funcid: Optional[str] = ...) -> None: ...
def unbind_all(self, sequence: str) -> None: ...
def unbind_class(self, className: str, sequence: str) -> None: ...
def mainloop(self, n: int = ...): ...
def quit(self): ...
def nametowidget(self, name): ...
Expand Down Expand Up @@ -419,7 +460,22 @@ class BaseWidget(Misc):
def __init__(self, master, widgetName, cnf=..., kw=..., extra=...): ...
def destroy(self): ...

class Widget(BaseWidget, Pack, Place, Grid): ...
# This class represents any widget except Toplevel or Tk.
class Widget(BaseWidget, Pack, Place, Grid):
# Allow bind callbacks to take e.g. Event[Label] instead of Event[Misc].
# Tk and Toplevel get notified for their child widgets' events, but other
# widgets don't.
@overload
def bind(
self: _W,
sequence: Optional[str] = ...,
func: Optional[Callable[[Event[_W]], Optional[Literal["break"]]]] = ...,
add: Optional[bool] = ...,
) -> str: ...
@overload
def bind(self, sequence: Optional[str], func: str, add: Optional[bool] = ...) -> None: ...
@overload
def bind(self, *, func: str, add: Optional[bool] = ...) -> None: ...

class Toplevel(BaseWidget, Wm):
def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ...
Expand All @@ -440,8 +496,19 @@ class Canvas(Widget, XView, YView):
def addtag_overlapping(self, newtag, x1, y1, x2, y2): ...
def addtag_withtag(self, newtag, tagOrId): ...
def bbox(self, *args): ...
def tag_unbind(self, tagOrId, sequence, funcid: Optional[Any] = ...): ...
def tag_bind(self, tagOrId, sequence: Optional[Any] = ..., func: Optional[Any] = ..., add: Optional[Any] = ...): ...
@overload
def tag_bind(
self,
tagOrId: Union[str, int],
sequence: Optional[str] = ...,
func: Optional[Callable[[Event[Canvas]], Optional[Literal["break"]]]] = ...,
add: Optional[bool] = ...,
) -> str: ...
@overload
def tag_bind(self, tagOrId: Union[str, int], sequence: Optional[str], func: str, add: Optional[bool] = ...) -> None: ...
@overload
def tag_bind(self, tagOrId: Union[str, int], *, func: str, add: Optional[bool] = ...) -> None: ...
def tag_unbind(self, tagOrId: Union[str, int], sequence: str, funcid: Optional[str] = ...) -> None: ...
def canvasx(self, screenx, gridspacing: Optional[Any] = ...): ...
def canvasy(self, screeny, gridspacing: Optional[Any] = ...): ...
def coords(self, *args): ...
Expand Down Expand Up @@ -660,8 +727,18 @@ class Text(Widget, XView, YView):
): ...
def see(self, index): ...
def tag_add(self, tagName, index1, *args): ...
def tag_unbind(self, tagName, sequence, funcid: Optional[Any] = ...): ...
def tag_bind(self, tagName, sequence, func, add: Optional[Any] = ...): ...
# tag_bind stuff is very similar to Canvas
@overload
def tag_bind(
self,
tagName: str,
sequence: Optional[str],
func: Optional[Callable[[Event[Text]], Optional[Literal["break"]]]],
add: Optional[bool] = ...,
) -> str: ...
@overload
def tag_bind(self, tagName: str, sequence: Optional[str], func: str, add: Optional[bool] = ...) -> None: ...
def tag_unbind(self, tagName: str, sequence: str, funcid: Optional[str] = ...) -> None: ...
def tag_cget(self, tagName, option): ...
def tag_configure(self, tagName, cnf: Optional[Any] = ..., **kw): ...
tag_config: Any
Expand Down
18 changes: 16 additions & 2 deletions stdlib/3/tkinter/ttk.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import sys
import tkinter
from typing import Any, List, Optional
from tkinter import Event
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Union, overload
from typing_extensions import Literal

def tclobjs_to_py(adict): ...
def setup_master(master: Optional[Any] = ...): ...
Expand Down Expand Up @@ -145,7 +147,19 @@ class Treeview(Widget, tkinter.XView, tkinter.YView):
def selection_remove(self, items): ...
def selection_toggle(self, items): ...
def set(self, item, column: Optional[Any] = ..., value: Optional[Any] = ...): ...
def tag_bind(self, tagname, sequence: Optional[Any] = ..., callback: Optional[Any] = ...): ...
# There's no tag_unbind() or 'add' argument for whatever reason.
# Also, it's 'callback' instead of 'func' here.
@overload
def tag_bind(
self,
tagname: str,
sequence: Optional[str] = ...,
callback: Optional[Callable[[Event[Treeview]], Optional[Literal["break"]]]] = ...,
) -> str: ...
@overload
def tag_bind(self, tagname: str, sequence: Optional[str], callback: str) -> None: ...
@overload
def tag_bind(self, tagname: str, *, callback: str) -> None: ...
def tag_configure(self, tagname, option: Optional[Any] = ..., **kw): ...
def tag_has(self, tagname, item: Optional[Any] = ...): ...

Expand Down