8

When manipulating PhotoImage objects with:

import tkinter as tk

img = tk.PhotoImage(file="myFile.gif")
for x in range(0,1000):
  for y in range(0,1000):
    img.put("{red}", (x, y))

The put operation takes a very long time. Is there a faster method of doing this?

martineau
  • 119,623
  • 25
  • 170
  • 301
HaskellElephant
  • 9,819
  • 4
  • 38
  • 67
  • I would say that its probably not just the `put()` call that is slow, but the fact that you are doing a nested for loop (1000^2) which is very slow. But @soulcheck has the right answer for you. – jdi May 02 '12 at 17:54

3 Answers3

7

Use a bounding box:

from Tkinter import *
root = Tk()
label = Label(root)
label.pack()
img = PhotoImage(width=300,height=300)
data = ("{red red red red blue blue blue blue}")
img.put(data, to=(20,20,280,280))
label.config(image=img)
root.mainloop()
noob oddy
  • 1,314
  • 8
  • 11
  • 2
    Realize this answer is old, but could you explain how using `"{red red red red blue blue blue blue}"` as an input for data creates the columns of colors? – Jet Blue Jun 07 '16 at 02:32
  • 2
    Figured it out, thanks! In case someone else comes across this and has a similar question [this article](http://tkinter.unpythonic.net/wiki/PhotoImage) explains it well. – Jet Blue Jun 07 '16 at 03:47
  • 2
    @JetBlue: Your link is dead, sadly :( – PhoemueX Oct 30 '19 at 12:51
  • 3
    @PhoemueX Here's an [archived version](http://web.archive.org/web/20170512214049/http://tkinter.unpythonic.net/wiki/PhotoImage) – Jet Blue Oct 30 '19 at 20:07
  • 1
    Here's a working link the [article](https://web.archive.org/web/20170512214049id_/http://tkinter.unpythonic.net/wiki/PhotoImage) @JetBlue was referring to. – martineau Jan 31 '22 at 18:17
6

Simply using the to optional parameter of the put() command is enough, no need to create a complex string:

import tkinter as tk
root = tk.Tk()

img = tk.PhotoImage(width=1000, height=1000)
data = 'red'
img.put(data, to=(0, 0, 1000, 1000))
label = tk.Label(root, image=img).pack()

root_window.mainloop()

Further observations

I couldn't find much in the way of documentation for PhotoImage, but the to parameter scales the data much more efficiently than a standard loop would. Here's some info I would have found helpful that doesn't seem to be properly documented well online.

The data parameter takes in a string of space-separated color values that are either named (official list), or an 8-bit color hex code. The string represents an array of colors to be repeated per pixel, where rows with more than one color are contained in curly braces and columns are just space separated. The rows must have the same number of columns/colors.

acceptable:
3 column 2 row: '{color color color} {color color color}'
1 column 2 row: 'color color', 'color {color}'
1 column 1 row: 'color', '{color}'

unacceptable:
{color color} {color}

If using a named color containing spaces, it must be encased in curly braces. ie. '{dodger blue}'

Here are a couple examples to illustrate the above in action, where a lengthy string would be required:

img = tk.PhotoImage(width=80, height=80)
data = ('{{{}{}}} '.format('{dodger blue} ' * 20, '#ff0000 ' * 20) * 20 +
        '{{{}{}}} '.format('LightGoldenrod ' * 20, 'green ' * 20) * 20)
img.put(data, to=(0, 0, 80, 80))

enter image description here

data = ('{{{}{}}} '.format('{dodger blue} ' * 20, '#ff0000 ' * 10) * 20 +
        '{{{}{}}} '.format('LightGoldenrod ' * 20, 'green ' * 10) * 10)

enter image description here

JCKE
  • 386
  • 5
  • 15
  • I love this answer. Saved me a lot of trouble. I did find the repetition a bit confusing, as I wanted to update every pixel in my image. I made [a gist](https://gist.github.com/samclane/7495aaad7edcc8fac29e8876baf0265e) showing how I update an image pixel-by-pixel using `put` and `to=`. – sawyermclane Nov 13 '18 at 21:29
3

Try constructing a 2d array of colors and call put with that array as parameter.

Like this:

import tkinter as tk

img = tk.PhotoImage(file="myFile.gif")
# "#%02x%02x%02x" % (255,0,0) means 'red'
line = '{' + ' '.join(["#%02x%02x%02x" % (255,0,0)] * 1000) + '}'
img.put(' '.join([line] * 1000))
soulcheck
  • 36,297
  • 6
  • 91
  • 90