Forum Archive

Clipboard.get_image Crash in Pythonista Keyboard

mieq

It seems Clipboard.get_image Can’t handle a large image (900kb in my case) in Pythonista Keyboard. Is there any workarounds?Thanks.

cvp

@mieq could you post your code please. 900 kB is not big

mieq

@cvp Just a simple Code like the following, and set as Pythonista Keyboard script. copy a 900kb image, run it in the Pythonista Keyboard and crash.the image under 500kb is all right.

import clipboard


clipboard.get_image()
print('Done')
cvp

@mieq it seems that you're right, no explanation. Doc says that we can use keyboard for simple things. Perhaps a big image is not so simple πŸ˜€

mieq

@cvp said:

Perhaps a big image is not so simple πŸ˜€

probably.Anyway,Thanks for your patience.

cvp

@mieq the problem comes from the clipboard.get_image because you can use and display a local image file a lot bigger than that (tested with a jpg of 6MB)

    v = ui.ImageView()
    #pil_image = clipboard.get_image()
    #v.image = pil2ui(pil_image)
    v.image = ui.Image.named('P1020104.JPG')    
    if keyboard.is_keyboard():
        keyboard.set_view(v)
    else:
        v.present('sheet') 
mieq

@cvp yes, So my first thought was whether this was a bug.πŸ˜‚

cvp

@mieq yes, you were right, I first thought it was a problem of small memory for keyboard script

stephen

im sure we have checked but has your device givin full acsess to pykeys?

stephen

@cvp @mieq

after some testing i suspect its access settings. i was able to get image using clipboard and display on keyboard

funny

using io to convert PIL img from clipboard.


#!python3
import keyboard
import clipboard
import ui
import io

def ConvertPIL(img):
    with io.BytesIO() as bIO:
        img.save(bIO, 'png')
        return ui.Image.from_data(bIO.getvalue())

if __name__ == '__main__':
    v = ui.ImageView()
    v.image= ConvertPIL(clipboard.get_image())

    if keyboard.is_keyboard():
        keyboard.set_view(v)
    else:
        v.present('sheet') 

cvp

@stephen I had full access, device = iPad mini 4

cvp

@stephen same crash with your code and no fault log...

cvp

@stephen said:

i was able to get image using clipboard and display on keyboard

Which image size?

stephen

@cvp my first was probably small. i ran couple more and as i expected, 8k image (7680 Γ— 4320) unknown filesize.. was too large for either PIL or ui. i have had this problem before.

with 4k (3840 x 2160) fileize 2.5MB. i know worked in PIL and ui, i usually deal with 4000x4000 up to 6000, 6000 for my game assets.

test image

test resault

if there is an issue on your side with image being too large i would sugest using resize with ANTI_ALIAS to resize image while maintaining quality. i have noticed that combining this method with scene power of rescaling you get really impressive quality after resizing.

super downscale

of course my scaling example wouldnt work in keyboard.. 6kx6k=88MB but i hope it helps in getting your image to display


def ConvertPIL():
    with io.BytesIO() as bIO:
        with Image.open('6k png test.png') as img:
            console.show_image('6k png test.png')

            resized = img.resize((2000, 2000), 1) # 1 == Anti-Alias
            resized.show()
            resized.save(bIO, 'png')
            resized.close()
        return ui.Image.from_data(bIO.getvalue(), 2) # 2 == scaling for retina can be 3 on newer models or 1 for no retina scaling
        bIO.close()

stephen

im using iPad Air2 (i believe this is same as iPad 5) with Retina screen (2x scale)

mieq

@stephen said:

im sure we have checked but has your device givin full acsess to pykeys?

I had full acsess to pykeys,Crash anyway.works
when Copying pictures below 500 kb.Running on my iPhone XR.Does this mean that different devices are different?

stephen

@mieq

ive heard iPhoneX has had alot of issues since launch with multiple apps.. never looked into this though.

aside from that i did a little research and your phone has:

Liquid Retina HD display
6.1-inch (diagonal) all-screen LCD Multi-Touch display with IPS technology
1792-by-828-pixel resolution at 326 ppi
1400:1 contrast ratio (typical)
True Tone display
Wide color display (P3)
Haptic Touch
625 nits max brightness (typical)
Fingerprint-resistant oleophobic coating
Support for display of multiple languages and characters simultaneously
A12 Bionic chip
Second-generation Neural Engine

my iPhone 6s has

IPS LCD capacitive touchscreen, 16M colors
Size    4.7 inches, 60.9 cm2 (~65.6% screen-to-body ratio)
Resolution  750 x 1334 pixels, 16:9 ratio (~326 ppi density)
Protection  Ion-strengthened glass, oleophobic coating
3D Touch
PLATFORM    OS  iOS 9, upgradable to iOS 13.4
    PowerVR GT7600 (six-core graphics)

So our devices are very close only reasonable diference would be screen size(not resolution) and CPU Generation.

that said following is my test with same code on my iPhone 6s.

Phone actually performed much faster than my iPad air2

iPhone

im not sure what is creating this issue on your end .. could i posibly get a image you are using and the code? ad what format is the image ur trying to display? are cinverting the image from PIL (what clipboard.get_image() produces) to a bmp, png, jpeg or tiff? (only formats i have used myself..)
i really want to help get you back on track. πŸ™ƒπŸ€“

mieq

@stephen You are really nice,so much thanks.you can try this picture,
Just took with my phone.

cvp

@mieq your picture of 720kb crash my iPad mini 4 and one picture of 600 kB works...

mieq

@cvp Me,tooπŸ˜†

stephen

@mieq @cvp

not sure if good or bad news πŸ˜•πŸ˜•

turns out.. your devices, and im assuming its same case for @cvp , are using the P3 xyz colorspace.. this is awesome for image clearity and especially for illumination.. but apparently the pykeys keyboard doesnt support. its Apple mode and has a wider color range especially twards yellow band. but im assuming this is do to tbe limited memory givin by ios and ipad os.. i tried converting to tiff, jpeg (original format), png and bmp. i tried making a BytesIO object and i tried resizing. i even tried using UIimage and CGColor from Objc_utils. i ttied to find a solution that wouldnt make user change settings on their device but it seems you will need to change color from Display P3 to sRGB, RGBa or rgb at the least.

here is a great resource for conversions. folowing is img showing the Display P3 having been set.

here

*if anyone finds a way around this or finds my resault false please let me know its going to bug me till i fix it lol

mieq

@stephen
oh, Thanks for all of this.I was really surprised that you were able to explore such a deep field.and it really help me a lot,Thanks.

stephen

@mieq not a problem. i usually wonder around the game development realm so images feel very familier.. i hope changing color format on ur device helps.

cvp

@mieq with Objectivec, no crash πŸ˜€ with an image of 6MB 🍾

import keyboard
import ui
from objc_util import *

if __name__ == '__main__':
    v = ui.ImageView()
    UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()
    im = UIPasteboard.image()
    ObjCInstance(v).setImage_(im)

    if keyboard.is_keyboard():
        keyboard.set_view(v)
    else:
        v.present('sheet') 
mieq

@cvp wow, works perfectly.thank you! Actually, last night I was thinking whether Objectivec can be used to replace the function of cliboard.get_image. But it was too hard for me to do that.πŸ˜‚

Do you mind if I ask a few more questions about Objectivec? when I get the object of UIPasteboard.image(), how can I change it to a python object (like PIL image or binary data)? and can UIPasteboard.image() handle with gif files? so much Thanks!

cvp

@mieq said:

how can I change it to a python object

I think you can't but, often, tou can access to Objectivec object of a Python(ista) object by its ObjInstance.

cvp

@mieq said:

can UIPasteboard.image() handle with gif files?

Yes, you can paste a gif but up to you to display it as gif

stephen

@mieq

Pasteboard should produce a byted object i belive and using a context manager probably with BytesIO then u can convert that to ui.image with from_data. make sure you pass a 2.0 for second param to acount for your 2:1 retina.

---

@cvp outstanding!

i was so stuck on the image i didnt even think of UIPasteBoard good lookin out!

cvp

@stephen said:

Pasteboard should produce a byted object i belive

@mieq first step

UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()
data = UIPasteboard.dataForPasteboardType_("com.compuserve.gif")
print(data) 

Next step: data -> pil then use my gif script here

stephen

@mieq @cvp

heres how i would of πŸ€“



import keyboard
import ui
import io
from objc_util import *

if __name__ == '__main__':
    v = ui.ImageView()
    UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()

    img_from_pasteboard = UIPasteboard.image()
    png_raw_bytes=uiimage_to_png(img_from_pasteboard)

    with io.BytesIO(png_raw_bytes) as bIO:
        new_ui_img = ui.Image.from_data(bIO.getvalue(), 2)
        v.image = new_ui_img



    if keyboard.is_keyboard():
        keyboard.set_view(v)
    else:
        v.present('sheet')
cvp

@stephen did you try with a gif, is it animated? I had read that with PasteBoard image, we get only the first frame of the gif, reason why I tried to get the entire file as data. And with my 3 lines, I get data with the length of the entire file.

mieq

@cvp @stephen
Thank you very much!πŸ‘ I think my problem has been solved with your help, just need some time to digest.(Too much knowledge for me.πŸ˜†

stephen

@cvp didnt see the gif request ill run a test right now and get back to u.

@mieq your very welcom!

cvp

@stephen Your

    UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()

    img_from_pasteboard = UIPasteboard.image()
    png_raw_bytes=uiimage_to_png(img_from_pasteboard)

    with io.BytesIO(png_raw_bytes) as bIO:
        new_ui_img = ui.Image.from_data(bIO.getvalue(), 2)
        v.image = new_ui_img

or mine

    UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()
    im = UIPasteboard.image()
    ObjCInstance(v).setImage_(im)
stephen

@cvp yours is beter not only cleaner but it is ALOT faster too. plus as u said the python ui.image only gets first frame πŸ€“πŸ˜… great job !

cvp

@mieq and @stephen this works for gif 🍾

import keyboard
import ui
import io
from objc_util import *
from   PIL import Image

class Gif(ui.View):
    def __init__(self,pil,duration):
        self.duration = duration
        self.ImageView = ui.ImageView()
        self.add_subview(self.ImageView)
        self.pil = pil
        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):j
        if self.update_interval == 0:
            self.update_interval = self.duration / self.pil.n_frames
        else:
            self.update_interval = 0


if __name__ == '__main__':
    UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()    
    data = UIPasteboard.dataForPasteboardType_("com.compuserve.gif")
    b = nsdata_to_bytes(ObjCInstance(data))
    pil = Image.open(io.BytesIO(b))
    v = Gif(pil,1)
    v.present('sheet')#,hide_title_bar=True) 
stephen

@cvp this should be added to examples or The Github collection πŸ•ΊπŸ»

cvp

@stephen said:

this should be added to examples or The Github collection

I don't even know what it is πŸ˜‚

stephen

@cvp collection of scripts for pythonista and this one i think is most popular public repo..

puplic collection

pythonista tools

cvp

@stephen you know, there are only 4 interesting lines:

    UIPasteboard = ObjCClass('UIPasteboard').generalPasteboard()    
    data = UIPasteboard.dataForPasteboardType_("com.compuserve.gif")
    b = nsdata_to_bytes(ObjCInstance(data))
    pil = Image.open(io.BytesIO(b)) 
cvp

@mieq I've tested, it is also ok for keyboard view.

stephen

@cvp yes but the amount of research i did earlier and still failed lol makes those few lines outstanding πŸ˜‚πŸ˜‚

mieq

@cvp Perfect, I'm not surprised anymore, I already feel that no problem can stump you.πŸ˜‚

cvp

@mieq said:

no problem can stump you

Wow, you have never seen my financial problems πŸ™„

mieq

@cvp said:

Wow, you have never seen my financial problems πŸ™„

πŸ˜†πŸ˜‚

stephen

@cvp said:

@mieq said:

no problem can stump you

Wow, you have never seen my financial problems πŸ™„

pretty sure this goes for most of us indie developers πŸ˜¬πŸ˜…πŸ˜‚

cvp

@stephen πŸ‘