From 41fba5b1a78f9931b497bb466a25b26fe314fbc5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 22 Jun 2026 00:55:41 +0300 Subject: [PATCH] gh-151876: Add tkinter Canvas methods rotate and rchars Wrap the Tk canvas commands "rchars" (replace the text or coordinates of items, since Tk 8.6) and "rotate" (rotate the coordinates of items about an origin, new in Tk 9.0) as the methods Canvas.rchars() and Canvas.rotate(), complementing the existing dchars/insert and move/scale methods. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01XWevzas4XVpjzedzR9gKVo --- Doc/library/tkinter.rst | 20 +++++++++++++ Doc/whatsnew/3.16.rst | 5 ++++ Lib/test/test_tkinter/test_widgets.py | 28 +++++++++++++++++++ Lib/tkinter/__init__.py | 16 +++++++++++ ...-06-22-00-55-07.gh-issue-151876.sVe0xx.rst | 2 ++ 5 files changed, 71 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-22-00-55-07.gh-issue-151876.sVe0xx.rst diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 222a4d0128bd8d..650f19b45a2493 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -3648,6 +3648,15 @@ Widget classes *yOrigin* changes by a factor of *yScale* (a factor of ``1.0`` leaves the coordinate unchanged). + .. method:: rotate(tagOrId, xOrigin, yOrigin, angle, /) + + Rotate the coordinates of all items given by *tagOrId* in canvas + coordinate space about the origin (*xOrigin*, *yOrigin*) by *angle* + degrees anticlockwise. + Negative values of *angle* rotate clockwise. + + .. versionadded:: next + .. method:: delete(*tagOrIds) Delete each of the items given by the *tagOrIds* arguments. @@ -3667,6 +3676,17 @@ Widget classes For line and polygon items *string* must be a valid sequence of coordinates. + .. method:: rchars(tagOrId, first, last, string, /) + + Replace the characters (for text items) or coordinates (for line and + polygon items) in the range from *first* to *last* inclusive of each of + the items given by *tagOrId* with *string*. + For line and polygon items *string* must be a valid sequence of + coordinates. + Items that do not support indexing ignore this operation. + + .. versionadded:: next + .. method:: itemcget(tagOrId, option) Return the current value of the configuration option *option* for the diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index ec8e367d938ddb..5212916ce2a022 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -157,6 +157,11 @@ tkinter synchronization of the displayed view with the underlying text. (Contributed by Serhiy Storchaka in :gh:`151675`.) +* Added new :class:`!tkinter.Canvas` methods :meth:`~tkinter.Canvas.rchars` + which replaces the text or coordinates of canvas items, and + :meth:`~tkinter.Canvas.rotate` which rotates the coordinates of canvas items. + (Contributed by Serhiy Storchaka in :gh:`151876`.) + xml --- diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index 4b51d219d87e5b..a0a6615dd8f941 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -1390,6 +1390,34 @@ def test_scale(self): self.assertRaises(TclError, c.scale, rect, 0, 0, 'spam', 2) self.assertRaises(TclError, c.scale, rect, 0, 0) # missing factors + @requires_tk(8, 6) + def test_rchars(self): + c = self.create() + # On a line item, rchars replaces a range of the coordinate list. + line = c.create_line(0, 0, 10, 10, 20, 0) + c.rchars(line, 2, 5, (30, 30, 40, 40)) + self.assertEqual(c.coords(line), [0.0, 0.0, 30.0, 30.0, 40.0, 40.0]) + # On a text item, rchars replaces a range of characters. + text = c.create_text(10, 10, text='hello') + c.rchars(text, 0, 2, 'HE') + self.assertEqual(c.itemcget(text, 'text'), 'HElo') + self.assertRaises(TclError, c.rchars) + + @requires_tk(9, 0) + def test_rotate(self): + c = self.create() + line = c.create_line(10, 0, 20, 0) + # The canvas y-axis points down, so an anticlockwise rotation about + # the origin maps (x, y) to (y, -x). + c.rotate(line, 0, 0, 90) + for got, expected in zip(c.coords(line), [0, -10, 0, -20]): + self.assertAlmostEqual(got, expected, places=3) + # A negative angle rotates clockwise, restoring the original position. + c.rotate(line, 0, 0, -90) + for got, expected in zip(c.coords(line), [10, 0, 20, 0]): + self.assertAlmostEqual(got, expected, places=3) + self.assertRaises(TclError, c.rotate, line, 0, 0, 'spam') + def test_delete(self): c = self.create() r1 = c.create_rectangle(10, 10, 30, 30) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 8bdf7cc1e2d96b..4afe176baecaae 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -3226,6 +3226,22 @@ def tag_raise(self, *args): lift = tkraise = tag_raise + def rchars(self, *args): + """Replace the text or coordinates between indices FIRST and LAST of + the items identified by TAGORID with STRING. + + Text items replace their text; line and polygon items replace their + coordinates, in which case STRING is a list of coordinates. Other + items ignore this operation.""" + self.tk.call((self._w, 'rchars') + args) + + def rotate(self, *args): # new in Tk 9.0 + """Rotate the coordinates of the items identified by TAGORID about the + origin (XORIGIN, YORIGIN) by ANGLE degrees anticlockwise. + + Negative values of ANGLE rotate clockwise.""" + self.tk.call((self._w, 'rotate') + args) + def scale(self, *args): """Scale item TAGORID with XORIGIN, YORIGIN, XSCALE, YSCALE.""" self.tk.call((self._w, 'scale') + args) diff --git a/Misc/NEWS.d/next/Library/2026-06-22-00-55-07.gh-issue-151876.sVe0xx.rst b/Misc/NEWS.d/next/Library/2026-06-22-00-55-07.gh-issue-151876.sVe0xx.rst new file mode 100644 index 00000000000000..9dc5f2327cf415 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-00-55-07.gh-issue-151876.sVe0xx.rst @@ -0,0 +1,2 @@ +Add the :class:`tkinter.Canvas` methods :meth:`!rchars` and :meth:`!rotate`, +wrapping the ``rchars`` and ``rotate`` Tk canvas commands.