]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-151876: Add tkinter Canvas methods rotate and rchars (GH-151877)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 22 Jun 2026 13:51:50 +0000 (16:51 +0300)
committerGitHub <noreply@github.com>
Mon, 22 Jun 2026 13:51:50 +0000 (13:51 +0000)
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 <noreply@anthropic.com>
Doc/library/tkinter.rst
Doc/whatsnew/3.16.rst
Lib/test/test_tkinter/test_widgets.py
Lib/tkinter/__init__.py
Misc/NEWS.d/next/Library/2026-06-22-00-55-07.gh-issue-151876.sVe0xx.rst [new file with mode: 0644]

index 584590176220670cb6973d5bcaa8e086940c63a9..a9659567eab5a799968de26deee048fb6a80f528 100644 (file)
@@ -3743,6 +3743,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.
@@ -3762,6 +3771,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
index 692b3cfdc067b8654ee9b0877749f1a8e2f6e57c..ec7329a3d852609f8efe0e28d34ad5db1d4e3f5e 100644 (file)
@@ -173,6 +173,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`.)
+
 * Added a :meth:`!validate` method to the :class:`!tkinter.Entry` and
   :class:`!tkinter.Spinbox` widgets, which forces an evaluation of the
   validation command.
index d0305562a0cb05bfcc7bb66eb7c180886685aa18..c2117981208dd5c062bcf58dead1c5f446b115d9 100644 (file)
@@ -1409,6 +1409,34 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
         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)
index 974e386be1bb492d56e59237dfb670e4b5827931..bf6a75875cfcb7ee8cebf90f15e6903e2f9a9c41 100644 (file)
@@ -3270,6 +3270,22 @@ class Canvas(Widget, XView, YView):
 
     lift = tkraise = tag_raise  # overrides Misc.tkraise
 
+    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 (file)
index 0000000..9dc5f23
--- /dev/null
@@ -0,0 +1,2 @@
+Add the :class:`tkinter.Canvas` methods :meth:`!rchars` and :meth:`!rotate`,
+wrapping the ``rchars`` and ``rotate`` Tk canvas commands.