diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index db6834fbea53e4..0257de63067003 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -6151,7 +6151,7 @@ Image classes .. method:: data(format=None, *, from_coords=None, background=None, \ - grayscale=False) + grayscale=False, metadata=None) Return the image data. @@ -6175,16 +6175,27 @@ Image classes If *grayscale* is true, the data does not contain color information; all pixel data is transformed into grayscale. + *metadata* is a dictionary passed to the image format driver. + It requires Tcl/Tk 9.0 or newer. + .. versionadded:: 3.13 + .. versionchanged:: next + Added the *metadata* parameter. + - .. method:: get(x, y) + .. method:: get(x, y, *, withalpha=False) Return the color of the pixel at coordinates (*x*, *y*) as an ``(r, g, b)`` tuple of three integers between 0 and 255, representing the red, green and blue components respectively. + If *withalpha* is true, the returned tuple has a fourth element giving + the alpha (opacity) value of the pixel. + + .. versionchanged:: next + Added the *withalpha* parameter, which requires Tcl/Tk 9.0 or newer. - .. method:: put(data, to=None) + .. method:: put(data, to=None, *, format=None, metadata=None) Set pixels of the image to the colors given in *data*, which must be a string or a nested sequence of horizontal rows of pixel colors (for @@ -6197,13 +6208,25 @@ Image classes bottom-right corner, of the region. The default position is ``(0, 0)``. + *format* specifies the format of the image *data*, so that only image + file format handlers whose names begin with it are tried. + + *metadata* is a dictionary passed to the image format driver. + It requires Tcl/Tk 9.0 or newer. + + .. versionchanged:: next + Added the *format* and *metadata* parameters. + .. method:: read(filename, format=None, *, from_coords=None, to=None, \ - shrink=False) + shrink=False, metadata=None) Read image data from the file named *filename* into the image. *format* specifies the format of the image data in the file. + *metadata* is a dictionary passed to the image format driver. + It requires Tcl/Tk 9.0 or newer. + *from_coords* specifies a rectangular sub-region of the image file data to be copied to the destination image. It must be a tuple or a list of 1 to 4 integers ``(x1, y1, x2, y2)``. @@ -6224,6 +6247,9 @@ Image classes .. versionadded:: 3.13 + .. versionchanged:: next + Added the *metadata* parameter. + .. method:: subsample(x, y='', *, from_coords=None) @@ -6256,7 +6282,7 @@ Image classes .. method:: write(filename, format=None, from_coords=None, *, \ - background=None, grayscale=False) + background=None, grayscale=False, metadata=None) Write image data from the image to the file named *filename*. @@ -6278,9 +6304,15 @@ Image classes If *grayscale* is true, the data does not contain color information; all pixel data is transformed into grayscale. + *metadata* is a dictionary passed to the image format driver. + It requires Tcl/Tk 9.0 or newer. + .. versionchanged:: 3.13 Added the *background* and *grayscale* parameters. + .. versionchanged:: next + Added the *metadata* parameter. + .. method:: zoom(x, y='', *, from_coords=None) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index eb13c67ac6a5e8..b50a43f08b5dec 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -194,6 +194,13 @@ tkinter badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order). (Contributed by Serhiy Storchaka in :gh:`151874`.) +* Added support for more options in :class:`!tkinter.PhotoImage` methods: the + *format* parameter of :meth:`~tkinter.PhotoImage.put`, the *metadata* + parameter of :meth:`~tkinter.PhotoImage.put`, :meth:`~tkinter.PhotoImage.read`, + :meth:`~tkinter.PhotoImage.write` and :meth:`~tkinter.PhotoImage.data`, and + the *withalpha* parameter of :meth:`~tkinter.PhotoImage.get`. + (Contributed by Serhiy Storchaka in :gh:`151890`.) + * Added the :meth:`~tkinter.PhotoImage.redither` method which recalculates the dithered image when its data was supplied in pieces. (Contributed by Serhiy Storchaka in :gh:`151888`.) diff --git a/Lib/test/test_tkinter/test_images.py b/Lib/test/test_tkinter/test_images.py index 099996feb5654a..4ce66340620333 100644 --- a/Lib/test/test_tkinter/test_images.py +++ b/Lib/test/test_tkinter/test_images.py @@ -515,6 +515,16 @@ def test_put(self): self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255)) self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0)) + def test_put_format(self): + image = self.create() + with open(self.testfile, 'rb') as f: + data = f.read() + image2 = tkinter.PhotoImage(master=self.root) + image2.put(data, format='gif') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(4, 6), image.get(4, 6)) + def test_get(self): image = self.create() self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162)) @@ -525,6 +535,29 @@ def test_get(self): self.assertRaises(tkinter.TclError, image.get, 16, 15) self.assertRaises(tkinter.TclError, image.get, 15, 16) + @requires_tk(9, 0) + def test_get_withalpha(self): + image = self.create() + rgb = image.get(4, 6) + rgba = image.get(4, 6, withalpha=True) + if self.wantobjects: + self.assertEqual(rgba[:3], rgb) + self.assertEqual(len(rgba), 4) + self.assertIn(rgba[3], (0, 255)) # GIF alpha is fully on or off + else: + self.assertTrue(rgba.startswith(rgb + ' ')) + + @requires_tk(9, 0) + def test_metadata(self): + image = self.create() + # The -metadata configuration option holds the image's metadata. + image.configure(metadata=('Comment', 'spam')) + self.assertIn('Comment', str(image.cget('metadata'))) + # put() and data() accept a metadata dictionary, passed to the image + # format driver. put() does not change the image's own metadata. + image.put('{red green} {blue yellow}', metadata={'Comment': 'spam'}) + self.assertTrue(image.data(metadata={'Comment': 'spam'})) + def test_read(self): # Due to the Tk bug https://core.tcl-lang.org/tk/tktview/1576528 # the -from option does not work correctly for GIF and PNG files. diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index bf1a0290088fd2..dd7407176ddd8f 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -4692,26 +4692,59 @@ def copy_replace(self, sourceImage, *, from_coords=None, to=None, shrink=False, options.extend(('-compositingrule', compositingrule)) self.tk.call(self.name, 'copy', sourceImage, *options) - def get(self, x, y): - """Return the color (red, green, blue) of the pixel at X,Y.""" - return self.tk.call(self.name, 'get', x, y) + @staticmethod + def _metadata(metadata): + # A Tcl dict is a flat list of alternating keys and values. A Python + # dict passed directly would expand to its keys only, so flatten it. + flat = () + for key, value in metadata.items(): + flat += (key, value) + return flat + + def get(self, x, y, *, withalpha=False): + """Return the color of the pixel at X,Y as a tuple of its red, green + and blue components. + + If WITHALPHA is true, the returned tuple has a fourth element giving + the alpha (opacity) value of the pixel. This requires Tcl/Tk 9.0 or + newer. + """ + args = (self.name, 'get', x, y) + if withalpha: + args += ('-withalpha',) + return self.tk.call(args) - def put(self, data, to=None): + def put(self, data, to=None, *, format=None, metadata=None): """Put row formatted colors to image starting from - position TO, e.g. image.put("{red green} {blue yellow}", to=(4,6))""" + position TO, e.g. image.put("{red green} {blue yellow}", to=(4,6)) + + The FORMAT option specifies the format of the image DATA, so that only + image file format handlers whose names begin with it are tried. + + The METADATA option, a dictionary passed to the image format driver, + requires Tcl/Tk 9.0 or newer. + """ args = (self.name, 'put', data) + if format is not None: + args += ('-format', format) + if metadata is not None: + args += ('-metadata', self._metadata(metadata)) if to: if to[0] == '-to': to = to[1:] - args = args + ('-to',) + tuple(to) + args += ('-to',) + tuple(to) self.tk.call(args) - def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False): + def read(self, filename, format=None, *, from_coords=None, to=None, + shrink=False, metadata=None): """Reads image data from the file named FILENAME into the image. The FORMAT option specifies the format of the image data in the file. + The METADATA option, a dictionary passed to the image format driver, + requires Tcl/Tk 9.0 or newer. + The FROM_COORDS option specifies a rectangular sub-region of the image file data to be copied to the destination image. It must be a tuple or a list of 1 to 4 integers (x1, y1, x2, y2). (x1, y1) and @@ -4731,6 +4764,8 @@ def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False options = () if format is not None: options += ('-format', format) + if metadata is not None: + options += ('-metadata', self._metadata(metadata)) if from_coords is not None: options += ('-from', *from_coords) if shrink: @@ -4740,13 +4775,16 @@ def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False self.tk.call(self.name, 'read', filename, *options) def write(self, filename, format=None, from_coords=None, *, - background=None, grayscale=False): + background=None, grayscale=False, metadata=None): """Writes image data from the image to a file named FILENAME. The FORMAT option specifies the name of the image file format handler to be used to write the data to the file. If this option is not given, the format is guessed from the file extension. + The METADATA option, a dictionary passed to the image format driver, + requires Tcl/Tk 9.0 or newer. + The FROM_COORDS option specifies a rectangular region of the image to be written to the image file. It must be a tuple or a list of 1 to 4 integers (x1, y1, x2, y2). If only x1 and y1 are specified, @@ -4765,6 +4803,8 @@ def write(self, filename, format=None, from_coords=None, *, options = () if format is not None: options += ('-format', format) + if metadata is not None: + options += ('-metadata', self._metadata(metadata)) if from_coords is not None: options += ('-from', *from_coords) if grayscale: @@ -4774,9 +4814,12 @@ def write(self, filename, format=None, from_coords=None, *, self.tk.call(self.name, 'write', filename, *options) def data(self, format=None, *, from_coords=None, - background=None, grayscale=False): + background=None, grayscale=False, metadata=None): """Returns image data. + The METADATA option, a dictionary passed to the image format driver, + requires Tcl/Tk 9.0 or newer. + The FORMAT option specifies the name of the image file format handler to be used. If this option is not given, this method uses a format that consists of a tuple (one element per row) of strings @@ -4803,6 +4846,8 @@ def data(self, format=None, *, from_coords=None, options = () if format is not None: options += ('-format', format) + if metadata is not None: + options += ('-metadata', self._metadata(metadata)) if from_coords is not None: options += ('-from', *from_coords) if grayscale: diff --git a/Misc/NEWS.d/next/Library/2026-06-22-02-29-02.gh-issue-151890.gT6JwI.rst b/Misc/NEWS.d/next/Library/2026-06-22-02-29-02.gh-issue-151890.gT6JwI.rst new file mode 100644 index 00000000000000..3e064aeeba8131 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-02-29-02.gh-issue-151890.gT6JwI.rst @@ -0,0 +1,6 @@ +Add the *format* parameter to the :meth:`tkinter.PhotoImage.put` method, the +*metadata* parameter to the :meth:`~tkinter.PhotoImage.put`, +:meth:`~tkinter.PhotoImage.read`, :meth:`~tkinter.PhotoImage.write` and +:meth:`~tkinter.PhotoImage.data` methods, and the *withalpha* parameter to the +:meth:`~tkinter.PhotoImage.get` method. The *metadata* and *withalpha* +parameters require Tcl/Tk 9.0 or newer.