Forum Archive

I need help displaying a python PIL image within Pythonista UI

PeterDaGrape

Hi there, I am completely new to Pythonista UI and all I need to do is show an image generated within PIL, I have found https://forum.omz-software.com/topic/1935/how-can-i-convert-a-pil-image-to-a-ui-image However I can only get it to work on a button, as I have no idea how to get it to work.
The reason I need it to displayed outside of a button is that I need to be able to tap anywhere on the image and get the x and y coordinates of the tap, which I don’t think you can do with the button.
Any help; would be great thanks!!!

cvp

@PeterDaGrape try this little script (tap anywhere) and tell me if I had correctly understood your request

import ui
from PIL import Image
import io

def pil2ui(imgIn):
    with io.BytesIO() as bIO:
        imgIn.save(bIO, 'PNG')
        imgOut = ui.Image.from_data(bIO.getvalue())
    return imgOut

class MyView(ui.View):
    def __init__(self, pil, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        iv = ui.ImageView()
        iv.touch_enabled = True
        iv.frame = self.frame
        self.add_subview(iv)
        iv.image = pil2ui(pil)
    def touch_began(self, touch):
        x,y = touch.location
        self.name = f'x={x} y={y}'

pil = Image.open('test:Lenna')
wi,hi = pil.size
w = 400
h = w * hi/wi
mv = MyView(pil,frame=(0,0,w,h))
mv.present('sheet') 
PeterDaGrape

@cvp

You my friend are a genius, I had sort of figured out how to display the image within a window but only as a button, and I couldn’t find anywhere how you would just show a simple image within a window, and it works just as I needed, thanks as well for including the tap location part, that will also be extremely useful thank you!!!

PeterDaGrape

@cvp this code is working perfectly, I have been playing around, and I can’t figure out how to refresh the image, so it will use the same image, and will refresh the view to show an updated version of the image, is this possible?

cvp

@PeterDaGrape It will depend how/when you want to change the image. Here a quick and dirty script to show how to change the image by tapping a button. It does not foresee the case where your images do not have the same proportion

import ui
from PIL import Image
import io

def pil2ui(imgIn):
    with io.BytesIO() as bIO:
        imgIn.save(bIO, 'PNG')
        imgOut = ui.Image.from_data(bIO.getvalue())
    return imgOut

class MyView(ui.View):
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        self.images = ['test:Lenna', 'test:Mandrill', 'test:Peppers', 'test:Sailboat']
        self.idx = 0
        pil = Image.open(self.images[self.idx])
        wi,hi = pil.size
        self.width = 400
        self.height = self.width * hi/wi
        iv = ui.ImageView()
        iv.touch_enabled = True
        iv.frame = self.frame
        self.add_subview(iv)
        iv.image = pil2ui(pil)
        iv.name = 'iv'
        b = ui.ButtonItem()
        b.title = 'new image'
        b.action = self.b_action
        self.right_button_items = (b,)
    def touch_began(self, touch):
        x,y = touch.location
        self.name = f'x={x} y={y}'
    def b_action(self,sender):
        self.idx += 1
        if self.idx == len(self.images):
            self.idx = 0
        pil = Image.open(self.images[self.idx])
        self['iv'].image = pil2ui(pil)

mv = MyView()
mv.present('sheet') 
cvp

@PeterDaGrape another example with a gif (which contains a set of images) where the updating of images is automatically done by the standard update method of your ui.View. Try it

from PIL import Image
import ui
import io 

class Gif(ui.View):
    def __init__(self,gif_file,duration):
        self.frame = (0,0,400,400)
        self.duration = duration
        self.ImageView = ui.ImageView()
        self.ImageView.frame = self.frame
        self.add_subview(self.ImageView)
        self.pil = Image.open(gif_file)
        self.update_interval = self.duration / self.pil.n_frames
        self.frame_id = 0
    def pil2ui(self,imgIn):
        with io.BytesIO() as bIO:
            imgIn.save(bIO, 'PNG')
            imgOut = ui.Image.from_data(bIO.getvalue())
        del bIO
        return imgOut
    def update(self):
        # Display individual frames from the loaded animated GIF file
        self.pil.seek(self.frame_id)
        self.ImageView.image = self.pil2ui(self.pil)
        self.frame_id = self.frame_id + 1
        if self.frame_id >= self.pil.n_frames:
            self.frame_id = 0
    def touch_ended(self,touch):
        if self.update_interval == 0:
            self.update_interval = self.duration / self.pil.n_frames
        else:
            self.update_interval = 0

if __name__ == '__main__':
    v = Gif('../Examples/Misc/animation.gif',1)
    v.present('sheet')#,hide_title_bar=True)
PeterDaGrape

Ah, thank you very much, Ill have a play around with this, currently it would be creating a new window for every image generated which isn’t ideal, ill let you know if it does as needed!!