无法从PIL Image构造tkinter.PhotoImage

时间:2015-02-27 09:53:51

标签: python tkinter resize python-imaging-library resize-image

当我按下按钮时,我尝试在标签中显示图像,但图像太大而我试图调整图像大小。我创建了这个函数:

def image_resize(imageFile):
    width = 500
    height = 300
    image = Image.open(imageFile)
    im2 = image.resize((width, height), Image.ANTIALIAS)
    return im2

要显示我创建此功能的图像:

def show_image():
    label_originalimage ['image'] = image_tk

带有command=show_image的按钮:

filename = 'bild_1.jpg'
image_resize = image_resize(filename)
image_tk = PhotoImage(image_resize)
button_open = Button(frame_open, text='Open Image', command=show_image)

我只得到这个:

TypeError : __str__ returned non-string (type instance)

4 个答案:

答案 0 :(得分:2)

来自tkinter的{​​{3}}类将文件名作为参数,并且因为它无法将image转换为字符串,所以它会抱怨。而是使用PhotoImage模块中的PIL.ImageTk类。这对我有用:

from tkinter import *
from PIL import ImageTk, Image

def image_resize(imageFile):
    width = 500
    height = 300
    image = Image.open(imageFile)
    im2 = image.resize((width,height), Image.ANTIALIAS)
    return im2

def show_image():
    label_originalimage ['image'] = image_tk

root = Tk()
filename = './Pictures/Space/AP923487321702.jpg'
image_resize = image_resize(filename)
image_tk = ImageTk.PhotoImage(image_resize)

label_originalimage = Label(root)
label_originalimage.pack()

button_open = Button(root, text='Open Image', command=show_image)
button_open.pack()

root.mainloop()

请注意从image_tk = PhotoImage(image_resize)image_tk = ImageTk.PhotoImage(image_resize)的更改。

答案 1 :(得分:1)

当我尝试为其构建画布图像项时,我遇到了同样的问题 来自tkinter PhotoImage的tkinter。后者是由一些人构建的 内存中的图像数据(在我的例子中是opencv图像)。同样的例外 如果我只是尝试将PhotoImage转换为字符串,就会发生。

我想PhotoImage的转换方法__str__中存在错误, 使它只是返回图像源。如果是从文件名构造的 (见下文)这很好用。如果从某些图像数据构造,则不是 类型为string并产生异常。

不幸的是,使用PIL的兼容PhotoImage 像matsjoyce建议的ImageTk模块也没有帮助我,因为我遇到了更糟糕的问题,可能是平台或库版本依赖的bug(我使用OS X 10.11.6,python 3.5,tkinter 8.6,PIL 1.1.7):现在python脚本在构造带有“Bus Error”的画布图像项时崩溃。

我所知道的唯一解决方法是将图像数据存储到临时文件中,并使用从该文件名构造的tkinter PhotoImage。 (尝试使用PIL PhotoImage仍然会崩溃。)

#!/usr/bin/env python3

import tkinter
import tempfile
import cv2

def opencv2photoimg(opencv_img):
    """Convert OpenCV (numpy) image to tkinter photo image."""
    # ugly workaround: store as file & load file, because direct
    # construction leads to a crash on my platform 
    tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=True)
    # ^^^ I am using PNGs only, you might want to use another suffix
    cv2.imwrite(tmpfile.name, opencv_img)
    return tkinter.PhotoImage(file=tmpfile.name)

# load image
img = cv2.imread('test.png')
# do something w/ the image ...

# setup tk window w/ canvas containing an image
root = tkinter.Tk()
canvas = tkinter.Canvas(root, width=img.shape[1], height=img.shape[0])
canvas.pack()
# keep reference to PhotoImage to avoid it being garbage collected
# (well known tkinter bug for canvas image items)
photo_img = opencv2photoimg(img)
# create a canvas item 
img_item = canvas.create_image(0, 0, anchor=tkinter.NW, image=photo_img)

# display the window
tkinter.mainloop()

我认为它不优雅,但它有效。

答案 2 :(得分:0)

是的它有效,但yeeeucchh - 我必须要做的事情。

当然有更好的方法。

这是我从here ....

开始的测试代码
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()

答案 3 :(得分:0)

这是:我发现photoimage的输入数据可以是一个看起来像ppm文件的字节数组,虽然它似乎只适用于合法ppm的子集(例如16位值不起作用)< / p>

所以为了将来的参考.......

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()