ImageView.load_from_url() freezes app?
I'm new to Pythonista's ui module and I wonder if anybody else has seen this problem:
I have a small program with a simple .pyui view file. The program loads the view and tries to put an image into the imageView object in the view:
view = ui.load_view() imageView = view['imageview1'] imageView.load_from_url('http://i3.kym-cdn.com/photos/images/newsfeed/000/404/302/597.gif') view.present()
This works great the first time the program is run. However, when the program is run a second time, the entire Pythonista app freezes. I need to halt Pythonista and start it again to continue.
I thought it might be a network issue, even though it only happens every second attempt. So I saved the image to a (very long) data URL encoded with Base64. Again, the program works the first time, but not the second time.
If I comment out the call to
load_from_url(), the rest of the program will work fine.
I tried using an image in a much shorter URL, just in case size was the issue:
However, the program exhibited the same problem.
Should my program do some clean-up before it exits or before the image is loaded?
Update: Here's a simple single-file program that demonstrates the problem:
import ui view = ui.View() image = ui.ImageView() image.load_from_url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==') view.add_subview(image) view.present()
Slightly simpler but still exhibits the same hang up...
import ui image_view = ui.ImageView() image_view.load_from_url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==') image_view.present()
Nice. I didn't realize
View()wasn't necessary. I'm glad you're able to reproduce the problem, too.
load_from_urlasynchronously loads the image, according to the docs, which makes me worry about threading. Using ui.delay works without lockup.
import ui image_view = ui.ImageView() def load_image(): image_view.load_from_url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==') image_view.present() ui.delay(load_image,0.5)
Perfect solution! You can even change the
I'll try that. I had glossed over the fact that it's asynchronous. I still have some questions:
Even if it loads the image asynchronously, why would it freeze the whole app? It seems like maybe the image would fail to display when the UI is shown instead.
Asynchronously loaded or not, any idea why it was successful the first time, but always failed the second?
If I include images in my app encoded with Base64, they don't need to be loaded asynchronously because there's no waiting for the network. How should I put those images into the UI?
This is still a bug I think, but the asynchronous part maybe helps explain why it is a problem -- I suspect there is maybe a dedicated thread or resource used for the asynchronous load, that is not getting freed or something ... so the first time works, but the second time doesn't. omz would have to weigh in. In any case,
ui.delayruns the method in its own thread. I also tried
ui.in_background, which didn't work in my tests, which is strange because I always thought
in_backgroundwas equivalent to
An alternate way to load images, is to create
ui.Imageobjects. This can be done directly from files, using
ui.Image.named;or you can load from a string containing the data (such as you'd get from
These both return a
ui.Imageobject (not to be confused with a
PIL.Imageobject... there are a few threads here about conversion between these types).
I'd recommend just using files -- it is easier to change out the images if you want to later.
But, if you are intent on having a script which is a single .py file, for ease of distribution, then encoding images is a clever solution (though, keep the images small!).
IN that case, you'd store just the base64 file contents (not the extra url info), then when you want to load the image, decode the base64 and use the
ui.Image.from_datamethod. Either method could also be used in conjunction with something like
requeststo download files from the internet dynamically (either writing the file to disk first, or using the read string).
Finally, to load a
ui.Imageinto an ImageView, you'd use
image_view.image = ui.Image.from_data(image_binary)
(Random thought: zipping all required files, then base64 encoding that within a .py file might be a neat way to pack self-extracting py files for one-file distribution, similar to the way packui works)