Skip to content

Commit 34eca5a

Browse files
gh-151890: Support more photo image options in tkinter.PhotoImage methods (GH-151891)
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). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0bd1024 commit 34eca5a

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
@@ -6151,7 +6151,7 @@ Image classes
61516151

61526152

61536153
.. method:: data(format=None, *, from_coords=None, background=None, \
6154-
grayscale=False)
6154+
grayscale=False, metadata=None)
61556155

61566156
Return the image data.
61576157

@@ -6175,16 +6175,27 @@ Image classes
61756175
If *grayscale* is true, the data does not contain color information; all
61766176
pixel data is transformed into grayscale.
61776177

6178+
*metadata* is a dictionary passed to the image format driver.
6179+
It requires Tcl/Tk 9.0 or newer.
6180+
61786181
.. versionadded:: 3.13
61796182

6183+
.. versionchanged:: next
6184+
Added the *metadata* parameter.
6185+
61806186

6181-
.. method:: get(x, y)
6187+
.. method:: get(x, y, *, withalpha=False)
61826188

61836189
Return the color of the pixel at coordinates (*x*, *y*) as an
61846190
``(r, g, b)`` tuple of three integers between 0 and 255, representing the
61856191
red, green and blue components respectively.
6192+
If *withalpha* is true, the returned tuple has a fourth element giving
6193+
the alpha (opacity) value of the pixel.
6194+
6195+
.. versionchanged:: next
6196+
Added the *withalpha* parameter, which requires Tcl/Tk 9.0 or newer.
61866197

6187-
.. method:: put(data, to=None)
6198+
.. method:: put(data, to=None, *, format=None, metadata=None)
61886199

61896200
Set pixels of the image to the colors given in *data*, which must be a
61906201
string or a nested sequence of horizontal rows of pixel colors (for
@@ -6197,13 +6208,25 @@ Image classes
61976208
bottom-right corner, of the region.
61986209
The default position is ``(0, 0)``.
61996210

6211+
*format* specifies the format of the image *data*, so that only image
6212+
file format handlers whose names begin with it are tried.
6213+
6214+
*metadata* is a dictionary passed to the image format driver.
6215+
It requires Tcl/Tk 9.0 or newer.
6216+
6217+
.. versionchanged:: next
6218+
Added the *format* and *metadata* parameters.
6219+
62006220
.. method:: read(filename, format=None, *, from_coords=None, to=None, \
6201-
shrink=False)
6221+
shrink=False, metadata=None)
62026222

62036223
Read image data from the file named *filename* into the image.
62046224

62056225
*format* specifies the format of the image data in the file.
62066226

6227+
*metadata* is a dictionary passed to the image format driver.
6228+
It requires Tcl/Tk 9.0 or newer.
6229+
62076230
*from_coords* specifies a rectangular sub-region of the image file data
62086231
to be copied to the destination image.
62096232
It must be a tuple or a list of 1 to 4 integers ``(x1, y1, x2, y2)``.
@@ -6224,6 +6247,9 @@ Image classes
62246247

62256248
.. versionadded:: 3.13
62266249

6250+
.. versionchanged:: next
6251+
Added the *metadata* parameter.
6252+
62276253

62286254
.. method:: subsample(x, y='', *, from_coords=None)
62296255

@@ -6256,7 +6282,7 @@ Image classes
62566282

62576283

62586284
.. method:: write(filename, format=None, from_coords=None, *, \
6259-
background=None, grayscale=False)
6285+
background=None, grayscale=False, metadata=None)
62606286

62616287
Write image data from the image to the file named *filename*.
62626288

@@ -6278,9 +6304,15 @@ Image classes
62786304
If *grayscale* is true, the data does not contain color information; all
62796305
pixel data is transformed into grayscale.
62806306

6307+
*metadata* is a dictionary passed to the image format driver.
6308+
It requires Tcl/Tk 9.0 or newer.
6309+
62816310
.. versionchanged:: 3.13
62826311
Added the *background* and *grayscale* parameters.
62836312

6313+
.. versionchanged:: next
6314+
Added the *metadata* parameter.
6315+
62846316

62856317
.. method:: zoom(x, y='', *, from_coords=None)
62866318

Doc/whatsnew/3.16.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ tkinter
194194
badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order).
195195
(Contributed by Serhiy Storchaka in :gh:`151874`.)
196196

197+
* Added support for more options in :class:`!tkinter.PhotoImage` methods: the
198+
*format* parameter of :meth:`~tkinter.PhotoImage.put`, the *metadata*
199+
parameter of :meth:`~tkinter.PhotoImage.put`, :meth:`~tkinter.PhotoImage.read`,
200+
:meth:`~tkinter.PhotoImage.write` and :meth:`~tkinter.PhotoImage.data`, and
201+
the *withalpha* parameter of :meth:`~tkinter.PhotoImage.get`.
202+
(Contributed by Serhiy Storchaka in :gh:`151890`.)
203+
197204
* Added the :meth:`~tkinter.PhotoImage.redither` method which recalculates the
198205
dithered image when its data was supplied in pieces.
199206
(Contributed by Serhiy Storchaka in :gh:`151888`.)

Lib/test/test_tkinter/test_images.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,16 @@ def test_put(self):
515515
self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255))
516516
self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0))
517517

518+
def test_put_format(self):
519+
image = self.create()
520+
with open(self.testfile, 'rb') as f:
521+
data = f.read()
522+
image2 = tkinter.PhotoImage(master=self.root)
523+
image2.put(data, format='gif')
524+
self.assertEqual(image2.width(), 16)
525+
self.assertEqual(image2.height(), 16)
526+
self.assertEqual(image2.get(4, 6), image.get(4, 6))
527+
518528
def test_get(self):
519529
image = self.create()
520530
self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162))
@@ -525,6 +535,29 @@ def test_get(self):
525535
self.assertRaises(tkinter.TclError, image.get, 16, 15)
526536
self.assertRaises(tkinter.TclError, image.get, 15, 16)
527537

538+
@requires_tk(9, 0)
539+
def test_get_withalpha(self):
540+
image = self.create()
541+
rgb = image.get(4, 6)
542+
rgba = image.get(4, 6, withalpha=True)
543+
if self.wantobjects:
544+
self.assertEqual(rgba[:3], rgb)
545+
self.assertEqual(len(rgba), 4)
546+
self.assertIn(rgba[3], (0, 255)) # GIF alpha is fully on or off
547+
else:
548+
self.assertTrue(rgba.startswith(rgb + ' '))
549+
550+
@requires_tk(9, 0)
551+
def test_metadata(self):
552+
image = self.create()
553+
# The -metadata configuration option holds the image's metadata.
554+
image.configure(metadata=('Comment', 'spam'))
555+
self.assertIn('Comment', str(image.cget('metadata')))
556+
# put() and data() accept a metadata dictionary, passed to the image
557+
# format driver. put() does not change the image's own metadata.
558+
image.put('{red green} {blue yellow}', metadata={'Comment': 'spam'})
559+
self.assertTrue(image.data(metadata={'Comment': 'spam'}))
560+
528561
def test_read(self):
529562
# Due to the Tk bug https://core.tcl-lang.org/tk/tktview/1576528
530563
# 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
@@ -4692,26 +4692,59 @@ def copy_replace(self, sourceImage, *, from_coords=None, to=None, shrink=False,
46924692
options.extend(('-compositingrule', compositingrule))
46934693
self.tk.call(self.name, 'copy', sourceImage, *options)
46944694

4695-
def get(self, x, y):
4696-
"""Return the color (red, green, blue) of the pixel at X,Y."""
4697-
return self.tk.call(self.name, 'get', x, y)
4695+
@staticmethod
4696+
def _metadata(metadata):
4697+
# A Tcl dict is a flat list of alternating keys and values. A Python
4698+
# dict passed directly would expand to its keys only, so flatten it.
4699+
flat = ()
4700+
for key, value in metadata.items():
4701+
flat += (key, value)
4702+
return flat
4703+
4704+
def get(self, x, y, *, withalpha=False):
4705+
"""Return the color of the pixel at X,Y as a tuple of its red, green
4706+
and blue components.
4707+
4708+
If WITHALPHA is true, the returned tuple has a fourth element giving
4709+
the alpha (opacity) value of the pixel. This requires Tcl/Tk 9.0 or
4710+
newer.
4711+
"""
4712+
args = (self.name, 'get', x, y)
4713+
if withalpha:
4714+
args += ('-withalpha',)
4715+
return self.tk.call(args)
46984716

4699-
def put(self, data, to=None):
4717+
def put(self, data, to=None, *, format=None, metadata=None):
47004718
"""Put row formatted colors to image starting from
4701-
position TO, e.g. image.put("{red green} {blue yellow}", to=(4,6))"""
4719+
position TO, e.g. image.put("{red green} {blue yellow}", to=(4,6))
4720+
4721+
The FORMAT option specifies the format of the image DATA, so that only
4722+
image file format handlers whose names begin with it are tried.
4723+
4724+
The METADATA option, a dictionary passed to the image format driver,
4725+
requires Tcl/Tk 9.0 or newer.
4726+
"""
47024727
args = (self.name, 'put', data)
4728+
if format is not None:
4729+
args += ('-format', format)
4730+
if metadata is not None:
4731+
args += ('-metadata', self._metadata(metadata))
47034732
if to:
47044733
if to[0] == '-to':
47054734
to = to[1:]
4706-
args = args + ('-to',) + tuple(to)
4735+
args += ('-to',) + tuple(to)
47074736
self.tk.call(args)
47084737

4709-
def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False):
4738+
def read(self, filename, format=None, *, from_coords=None, to=None,
4739+
shrink=False, metadata=None):
47104740
"""Reads image data from the file named FILENAME into the image.
47114741
47124742
The FORMAT option specifies the format of the image data in the
47134743
file.
47144744
4745+
The METADATA option, a dictionary passed to the image format driver,
4746+
requires Tcl/Tk 9.0 or newer.
4747+
47154748
The FROM_COORDS option specifies a rectangular sub-region of the image
47164749
file data to be copied to the destination image. It must be a tuple
47174750
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
47314764
options = ()
47324765
if format is not None:
47334766
options += ('-format', format)
4767+
if metadata is not None:
4768+
options += ('-metadata', self._metadata(metadata))
47344769
if from_coords is not None:
47354770
options += ('-from', *from_coords)
47364771
if shrink:
@@ -4740,13 +4775,16 @@ def read(self, filename, format=None, *, from_coords=None, to=None, shrink=False
47404775
self.tk.call(self.name, 'read', filename, *options)
47414776

47424777
def write(self, filename, format=None, from_coords=None, *,
4743-
background=None, grayscale=False):
4778+
background=None, grayscale=False, metadata=None):
47444779
"""Writes image data from the image to a file named FILENAME.
47454780
47464781
The FORMAT option specifies the name of the image file format
47474782
handler to be used to write the data to the file. If this option
47484783
is not given, the format is guessed from the file extension.
47494784
4785+
The METADATA option, a dictionary passed to the image format driver,
4786+
requires Tcl/Tk 9.0 or newer.
4787+
47504788
The FROM_COORDS option specifies a rectangular region of the image
47514789
to be written to the image file. It must be a tuple or a list of 1
47524790
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, *,
47654803
options = ()
47664804
if format is not None:
47674805
options += ('-format', format)
4806+
if metadata is not None:
4807+
options += ('-metadata', self._metadata(metadata))
47684808
if from_coords is not None:
47694809
options += ('-from', *from_coords)
47704810
if grayscale:
@@ -4774,9 +4814,12 @@ def write(self, filename, format=None, from_coords=None, *,
47744814
self.tk.call(self.name, 'write', filename, *options)
47754815

47764816
def data(self, format=None, *, from_coords=None,
4777-
background=None, grayscale=False):
4817+
background=None, grayscale=False, metadata=None):
47784818
"""Returns image data.
47794819
4820+
The METADATA option, a dictionary passed to the image format driver,
4821+
requires Tcl/Tk 9.0 or newer.
4822+
47804823
The FORMAT option specifies the name of the image file format
47814824
handler to be used. If this option is not given, this method uses
47824825
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,
48034846
options = ()
48044847
if format is not None:
48054848
options += ('-format', format)
4849+
if metadata is not None:
4850+
options += ('-metadata', self._metadata(metadata))
48064851
if from_coords is not None:
48074852
options += ('-from', *from_coords)
48084853
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)