Skip to content

Commit 6c692ff

Browse files
gh-151890: Support more photo image options in tkinter.PhotoImage methods
Add parameters mapping to Tk photo image options that previously had no tkinter equivalent: * the format parameter of PhotoImage.put() (Tk 8.6), * the metadata parameter of PhotoImage.put(), read(), write() and data() (Tk 9.0), * the withalpha parameter of PhotoImage.get() (Tk 9.0). The new option parameters are keyword-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWevzas4XVpjzedzR9gKVo
1 parent f28ef85 commit 6c692ff

5 files changed

Lines changed: 137 additions & 14 deletions

File tree

Doc/library/tkinter.rst

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5927,7 +5927,7 @@ Image classes
59275927

59285928

59295929
.. method:: data(format=None, *, from_coords=None, background=None, \
5930-
grayscale=False)
5930+
grayscale=False, metadata=None)
59315931

59325932
Return the image data.
59335933

@@ -5951,16 +5951,27 @@ Image classes
59515951
If *grayscale* is true, the data does not contain color information; all
59525952
pixel data is transformed into grayscale.
59535953

5954+
*metadata* is a dictionary passed to the image format driver.
5955+
It requires Tcl/Tk 9.0 or newer.
5956+
59545957
.. versionadded:: 3.13
59555958

5959+
.. versionchanged:: next
5960+
Added the *metadata* parameter.
5961+
59565962

5957-
.. method:: get(x, y)
5963+
.. method:: get(x, y, *, withalpha=False)
59585964

59595965
Return the color of the pixel at coordinates (*x*, *y*) as an
59605966
``(r, g, b)`` tuple of three integers between 0 and 255, representing the
59615967
red, green and blue components respectively.
5968+
If *withalpha* is true, the returned tuple has a fourth element giving
5969+
the alpha (opacity) value of the pixel.
5970+
5971+
.. versionchanged:: next
5972+
Added the *withalpha* parameter, which requires Tcl/Tk 9.0 or newer.
59625973

5963-
.. method:: put(data, to=None)
5974+
.. method:: put(data, to=None, *, format=None, metadata=None)
59645975

59655976
Set pixels of the image to the colors given in *data*, which must be a
59665977
string or a nested sequence of horizontal rows of pixel colors (for
@@ -5973,13 +5984,25 @@ Image classes
59735984
bottom-right corner, of the region.
59745985
The default position is ``(0, 0)``.
59755986

5987+
*format* specifies the format of the image *data*, so that only image
5988+
file format handlers whose names begin with it are tried.
5989+
5990+
*metadata* is a dictionary passed to the image format driver.
5991+
It requires Tcl/Tk 9.0 or newer.
5992+
5993+
.. versionchanged:: next
5994+
Added the *format* and *metadata* parameters.
5995+
59765996
.. method:: read(filename, format=None, *, from_coords=None, to=None, \
5977-
shrink=False)
5997+
shrink=False, metadata=None)
59785998

59795999
Read image data from the file named *filename* into the image.
59806000

59816001
*format* specifies the format of the image data in the file.
59826002

6003+
*metadata* is a dictionary passed to the image format driver.
6004+
It requires Tcl/Tk 9.0 or newer.
6005+
59836006
*from_coords* specifies a rectangular sub-region of the image file data
59846007
to be copied to the destination image.
59856008
It must be a tuple or a list of 1 to 4 integers ``(x1, y1, x2, y2)``.
@@ -6000,6 +6023,9 @@ Image classes
60006023

60016024
.. versionadded:: 3.13
60026025

6026+
.. versionchanged:: next
6027+
Added the *metadata* parameter.
6028+
60036029

60046030
.. method:: subsample(x, y='', *, from_coords=None)
60056031

@@ -6032,7 +6058,7 @@ Image classes
60326058

60336059

60346060
.. method:: write(filename, format=None, from_coords=None, *, \
6035-
background=None, grayscale=False)
6061+
background=None, grayscale=False, metadata=None)
60366062

60376063
Write image data from the image to the file named *filename*.
60386064

@@ -6054,9 +6080,15 @@ Image classes
60546080
If *grayscale* is true, the data does not contain color information; all
60556081
pixel data is transformed into grayscale.
60566082

6083+
*metadata* is a dictionary passed to the image format driver.
6084+
It requires Tcl/Tk 9.0 or newer.
6085+
60576086
.. versionchanged:: 3.13
60586087
Added the *background* and *grayscale* parameters.
60596088

6089+
.. versionchanged:: next
6090+
Added the *metadata* parameter.
6091+
60606092

60616093
.. method:: zoom(x, y='', *, from_coords=None)
60626094

Doc/whatsnew/3.16.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ tkinter
157157
synchronization of the displayed view with the underlying text.
158158
(Contributed by Serhiy Storchaka in :gh:`151675`.)
159159

160+
* Added support for more options in :class:`!tkinter.PhotoImage` methods: the
161+
*format* parameter of :meth:`~tkinter.PhotoImage.put`, the *metadata*
162+
parameter of :meth:`~tkinter.PhotoImage.put`, :meth:`~tkinter.PhotoImage.read`,
163+
:meth:`~tkinter.PhotoImage.write` and :meth:`~tkinter.PhotoImage.data`, and
164+
the *withalpha* parameter of :meth:`~tkinter.PhotoImage.get`.
165+
(Contributed by Serhiy Storchaka in :gh:`151890`.)
166+
160167
xml
161168
---
162169

Lib/test/test_tkinter/test_images.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,16 @@ def test_put(self):
509509
self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255))
510510
self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0))
511511

512+
def test_put_format(self):
513+
image = self.create()
514+
with open(self.testfile, 'rb') as f:
515+
data = f.read()
516+
image2 = tkinter.PhotoImage(master=self.root)
517+
image2.put(data, format='gif')
518+
self.assertEqual(image2.width(), 16)
519+
self.assertEqual(image2.height(), 16)
520+
self.assertEqual(image2.get(4, 6), image.get(4, 6))
521+
512522
def test_get(self):
513523
image = self.create()
514524
self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162))
@@ -519,6 +529,29 @@ def test_get(self):
519529
self.assertRaises(tkinter.TclError, image.get, 16, 15)
520530
self.assertRaises(tkinter.TclError, image.get, 15, 16)
521531

532+
@requires_tk(9, 0)
533+
def test_get_withalpha(self):
534+
image = self.create()
535+
rgb = image.get(4, 6)
536+
rgba = image.get(4, 6, withalpha=True)
537+
if self.wantobjects:
538+
self.assertEqual(rgba[:3], rgb)
539+
self.assertEqual(len(rgba), 4)
540+
self.assertIn(rgba[3], (0, 255)) # GIF alpha is fully on or off
541+
else:
542+
self.assertTrue(rgba.startswith(rgb + ' '))
543+
544+
@requires_tk(9, 0)
545+
def test_metadata(self):
546+
image = self.create()
547+
# The -metadata configuration option holds the image's metadata.
548+
image.configure(metadata=('Comment', 'spam'))
549+
self.assertIn('Comment', str(image.cget('metadata')))
550+
# put() and data() accept a metadata dictionary, passed to the image
551+
# format driver. put() does not change the image's own metadata.
552+
image.put('{red green} {blue yellow}', metadata={'Comment': 'spam'})
553+
self.assertTrue(image.data(metadata={'Comment': 'spam'}))
554+
522555
def test_read(self):
523556
# Due to the Tk bug https://core.tcl-lang.org/tk/tktview/1576528
524557
# the -from option does not work correctly for GIF and PNG files.

Lib/tkinter/__init__.py

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4583,26 +4583,59 @@ def copy_replace(self, sourceImage, *, from_coords=None, to=None, shrink=False,
45834583
options.extend(('-compositingrule', compositingrule))
45844584
self.tk.call(self.name, 'copy', sourceImage, *options)
45854585

4586-
def get(self, x, y):
4587-
"""Return the color (red, green, blue) of the pixel at X,Y."""
4588-
return self.tk.call(self.name, 'get', x, y)
4586+
@staticmethod
4587+
def _metadata(metadata):
4588+
# A Tcl dict is a flat list of alternating keys and values. A Python
4589+
# dict passed directly would expand to its keys only, so flatten it.
4590+
flat = ()
4591+
for key, value in metadata.items():
4592+
flat += (key, value)
4593+
return flat
4594+
4595+
def get(self, x, y, *, withalpha=False):
4596+
"""Return the color of the pixel at X,Y as a tuple of its red, green
4597+
and blue components.
4598+
4599+
If WITHALPHA is true, the returned tuple has a fourth element giving
4600+
the alpha (opacity) value of the pixel. This requires Tcl/Tk 9.0 or
4601+
newer.
4602+
"""
4603+
args = (self.name, 'get', x, y)
4604+
if withalpha:
4605+
args += ('-withalpha',)
4606+
return self.tk.call(args)
45894607

4590-
def put(self, data, to=None):
4608+
def put(self, data, to=None, *, format=None, metadata=None):
45914609
"""Put row formatted colors to image starting from
4592-
position TO, e.g. image.put("{red green} {blue yellow}", to=(4,6))"""
4610+
position TO, e.g. image.put("{red green} {blue yellow}", to=(4,6))
4611+
4612+
The FORMAT option specifies the format of the image DATA, so that only
4613+
image file format handlers whose names begin with it are tried.
4614+
4615+
The METADATA option, a dictionary passed to the image format driver,
4616+
requires Tcl/Tk 9.0 or newer.
4617+
"""
45934618
args = (self.name, 'put', data)
4619+
if format is not None:
4620+
args += ('-format', format)
4621+
if metadata is not None:
4622+
args += ('-metadata', self._metadata(metadata))
45944623
if to:
45954624
if to[0] == '-to':
45964625
to = to[1:]
4597-
args = args + ('-to',) + tuple(to)
4626+
args += ('-to',) + tuple(to)
45984627
self.tk.call(args)
45994628

4600-
def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False):
4629+
def read(self, filename, format=None, *, from_coords=None, to=None,
4630+
shrink=False, metadata=None):
46014631
"""Reads image data from the file named FILENAME into the image.
46024632
46034633
The FORMAT option specifies the format of the image data in the
46044634
file.
46054635
4636+
The METADATA option, a dictionary passed to the image format driver,
4637+
requires Tcl/Tk 9.0 or newer.
4638+
46064639
The FROM_COORDS option specifies a rectangular sub-region of the image
46074640
file data to be copied to the destination image. It must be a tuple
46084641
or a list of 1 to 4 integers (x1, y1, x2, y2). (x1, y1) and
@@ -4622,6 +4655,8 @@ def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False
46224655
options = ()
46234656
if format is not None:
46244657
options += ('-format', format)
4658+
if metadata is not None:
4659+
options += ('-metadata', self._metadata(metadata))
46254660
if from_coords is not None:
46264661
options += ('-from', *from_coords)
46274662
if shrink:
@@ -4631,13 +4666,16 @@ def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False
46314666
self.tk.call(self.name, 'read', filename, *options)
46324667

46334668
def write(self, filename, format=None, from_coords=None, *,
4634-
background=None, grayscale=False):
4669+
background=None, grayscale=False, metadata=None):
46354670
"""Writes image data from the image to a file named FILENAME.
46364671
46374672
The FORMAT option specifies the name of the image file format
46384673
handler to be used to write the data to the file. If this option
46394674
is not given, the format is guessed from the file extension.
46404675
4676+
The METADATA option, a dictionary passed to the image format driver,
4677+
requires Tcl/Tk 9.0 or newer.
4678+
46414679
The FROM_COORDS option specifies a rectangular region of the image
46424680
to be written to the image file. It must be a tuple or a list of 1
46434681
to 4 integers (x1, y1, x2, y2). If only x1 and y1 are specified,
@@ -4656,6 +4694,8 @@ def write(self, filename, format=None, from_coords=None, *,
46564694
options = ()
46574695
if format is not None:
46584696
options += ('-format', format)
4697+
if metadata is not None:
4698+
options += ('-metadata', self._metadata(metadata))
46594699
if from_coords is not None:
46604700
options += ('-from', *from_coords)
46614701
if grayscale:
@@ -4665,9 +4705,12 @@ def write(self, filename, format=None, from_coords=None, *,
46654705
self.tk.call(self.name, 'write', filename, *options)
46664706

46674707
def data(self, format=None, *, from_coords=None,
4668-
background=None, grayscale=False):
4708+
background=None, grayscale=False, metadata=None):
46694709
"""Returns image data.
46704710
4711+
The METADATA option, a dictionary passed to the image format driver,
4712+
requires Tcl/Tk 9.0 or newer.
4713+
46714714
The FORMAT option specifies the name of the image file format
46724715
handler to be used. If this option is not given, this method uses
46734716
a format that consists of a tuple (one element per row) of strings
@@ -4694,6 +4737,8 @@ def data(self, format=None, *, from_coords=None,
46944737
options = ()
46954738
if format is not None:
46964739
options += ('-format', format)
4740+
if metadata is not None:
4741+
options += ('-metadata', self._metadata(metadata))
46974742
if from_coords is not None:
46984743
options += ('-from', *from_coords)
46994744
if grayscale:
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Add the *format* parameter to the :meth:`tkinter.PhotoImage.put` method, the
2+
*metadata* parameter to the :meth:`~tkinter.PhotoImage.put`,
3+
:meth:`~tkinter.PhotoImage.read`, :meth:`~tkinter.PhotoImage.write` and
4+
:meth:`~tkinter.PhotoImage.data` methods, and the *withalpha* parameter to the
5+
:meth:`~tkinter.PhotoImage.get` method. The *metadata* and *withalpha*
6+
parameters require Tcl/Tk 9.0 or newer.

0 commit comments

Comments
 (0)