Python Forum
[Tkinter] createing a tkinter photoimage from array in python3 - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: [Tkinter] createing a tkinter photoimage from array in python3 (/thread-528.html)



createing a tkinter photoimage from array in python3 - pootle - Oct-17-2016

I started with this post and have tried to convert it to python3 - where the tkinter special in pilow has been 'replaced' by 'equivalent' functionality in tkinter.

After much searching and testing I have eventually resorted to writing a file and reading it into the tkinter photoimage object, which is really naff,  but is the only way I have found to make it work.

Surely there is away to pass the data direct to photoimage?

Interestingly my write / read a file approach is still faster than the original version in python 2.7!

Here is my python 3 version of the code:
import tkinter
from PIL import Image
import numpy
import time
import io
#python2 version (original) -> 120fps
#full physical file io and new image each cycle -> 130fps
#reuse PIL Image instead of create new each time -> 160fps

class mainWindow():
    times=1
    timestart=time.clock()
    data=numpy.array(numpy.random.random((400,500))*100,dtype=int)
    theimage = Image.frombytes('L', (data.shape[1],data.shape[0]), data.astype('b').tostring())
    
    def __init__(self):
        self.root = tkinter.Tk()
        self.frame = tkinter.Frame(self.root, width=500, height=400)
        self.frame.pack()
        self.canvas = tkinter.Canvas(self.frame, width=500,height=400)
        self.canvas.place(x=-2,y=-2)
        self.root.after(0,self.start) # INCREASE THE 0 TO SLOW IT DOWN
        self.root.mainloop()
    
    def start(self):
        global data
        global theimage
        self.theimage.frombytes(self.data.astype('b').tobytes())
        self.theimage.save('work.pgm')
        self.photo = tkinter.PhotoImage(file='work.pgm')
        self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW)
        self.root.update()
        self.times+=1
        if self.times%33==0:
            print("%.02f FPS"%(self.times/(time.clock()-self.timestart)))
        self.root.after(10,self.start)
        self.data=numpy.roll(self.data,-1,1)

if __name__ == '__main__':
    x=mainWindow()



RE: createing a tkinter photoimage from array in python3 - Larz60+ - Oct-17-2016

Hello,

This code is for python 3. It creates an image in a Label widget. It should work in Canvas with little modification
You will need to change the path to your image

from tkinter import *
from PIL import Image, ImageTk

class TryImage:
    def __init__(self, parent, image_path=None):
        self.image_path = image_path
        self.parent = parent

    def image_to_label(self):
        self.bar = Frame(self.parent, relief=RIDGE, borderwidth=5)
        self.bar.pack(fill=X, side=TOP)

        self.bar.columnconfigure(0, weight=1)
        self.bar.rowconfigure(0, weight=1)

        self.icon = ImageTk.PhotoImage(Image.open(self.image_path))
        self.icon_size = Label(self.bar)
        self.icon_size.image = self.icon
        self.icon_size.configure(image=self.icon)
        self.icon_size.pack(side=LEFT)

if __name__ == '__main__':
    root = Tk()
    ti = TryImage(root, 'images/showroom.png')
    ti.image_to_label()
    root.mainloop()
Larz60+


RE: createing a tkinter photoimage from array in python3 - pootle - Oct-18-2016

Well some more digging and I prised the answer out of a combination of several different places.

Larz60's approach is the way I was already doing it - loading a file, but I eventually found out that the data needs to be in ppm format which is pretty simple to generate. This approach goes twice as fast as writing /  reading a file and isn't really stressing the PC either:

import tkinter
import numpy
import time
#python2 version (original) -> 120fps
#full physical file io and new image each cycle -> 130fps
#reuse PIL Image instead of create new each time -> 160fps
#and... direct image into tkinter using ppm byte array -> 240 fps

class mainWindow():
    times=1
    timestart=time.clock()
    data=numpy.array(numpy.random.random((400,500))*900,dtype=numpy.uint16)
    
    def __init__(self):
        self.root = tkinter.Tk()
        self.frame = tkinter.Frame(self.root, width=500, height=400)
        self.frame.pack()
        self.canvas = tkinter.Canvas(self.frame, width=500,height=400)
        self.canvas.place(x=-2,y=-2)
        xdata = b'P5 500 400 255 ' + self.data.tobytes()
        self.photo = tkinter.PhotoImage(width=500, height=400, data=xdata, format='PPM')
        self.imid = self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW)
        self.root.after(1,self.start) # INCREASE THE 0 TO SLOW IT DOWN
        self.root.mainloop()
    
    def start(self):
        global data
        xdata = b'P5 500 400 255 ' + numpy.clip(self.data,0,255).tobytes()
        self.photo = tkinter.PhotoImage(width=500, height=400, data=xdata, format='PPM')
        if True:
            self.canvas.itemconfig(self.imid, image = self.photo)
        else:
            self.canvas.delete(self.imid)
            self.imid = self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW)
        self.times+=1
        if self.times%33==0:
            print("%.02f FPS"%(self.times/(time.clock()-self.timestart)))
        self.root.update()
        self.root.after(0,self.start)
        self.data=numpy.roll(self.data,-1,1)

if __name__ == '__main__':
    x=mainWindow()