Bauble / bauble.classic

this is how Bauble and Ghini both started
GNU General Public License v2.0
10 stars 34 forks source link

display web-hosted pictures #247

Closed mfrasca closed 8 years ago

mfrasca commented 8 years ago

from comments in #245 @tmyersdn

3) pictures when you choose an image file, would it be possible to also link to a url image repository? This is not critical, but we are current not allowed to set up a formal in server folder for images (due to memory issues), so web storage is the next option.

@RoDuth

3) Funny isn't it, my IT dept. prefers I not use cloud based!! You may remember the old version had a Picasa system for pictures, never used it and was glad when Mario changed this. You could always store a url as a note or something?

yes quite interesting.

what do you expect Bauble to do if you put a note containing a URL of a picture? like you enter —sorry if initially this has to be totally manual— a note with category <picture> and as text a URL and Bauble grabs it from the net and shows it as if it was a locally stored picture.

mfrasca commented 8 years ago

pictures are now loaded using the gtk.gdk.pixbuf_new_from_file function, which takes one string parameter, the local name of the image file.

A gtk.gdk.PixbufLoader provides a way for applications to drive the process of loading an image [...] it should be used when reading an image from a (potentially) slow network connection [...]

http://www.pygtk.org/pygtk2reference/class-gdkpixbufloader.html

mfrasca commented 8 years ago

so, yes, it does get a bit complicated, but it also solves a problem I have been ignoring: I should be reading the files asynchronously, so that the interface does not lock (as it does now) while reading the pictures from file. I cannot yet estimate the impact on the code.

mfrasca commented 8 years ago

just some working thoughts...

#!/usr/bin/env python

import gtk
import gtk.glade
import gtk.gdk
import urllib
import contextlib
import threading
import gobject

def read_in_chunks(file_object, chunk_size=1024):
    """Lazy function (generator) to read a file piece by piece.
    Default chunk size: 1k."""
    while True:
        data = file_object.read(chunk_size)
        if not data:
            break
        yield data

class ImageLoader(threading.Thread):
    def __init__(self, box, url, *args, **kwargs):
        super(ImageLoader, self).__init__(*args, **kwargs)
        self.box = box
        self.url = url
        self.is_global = (url.startswith('http://') or
                          url.startswith('https://'))

    def callback(self):
        print 'in callback',
        self.image.set_from_pixbuf(self.loader.get_pixbuf())
        self.image.show()

    def loader_closed(self, pixbufloader):
        print 'done-loading',
        gobject.idle_add(self.callback)

    def loader_area_prepared(self, pixbufloader):
        print 'done-preparing',
        gobject.idle_add(self.callback)

    def run(self):
        self.loader = gtk.gdk.PixbufLoader()
        self.loader.connect("area-prepared", self.loader_area_prepared)
        self.loader.connect("closed", self.loader_closed)
        self.image = image = gtk.Image()
        self.box.pack_start(image)
        if self.is_global:
            self.read_global_url()
        else:
            self.read_local_url()
        self.loader.close()

    def read_global_url(self):
        with contextlib.closing(urllib.urlopen(self.url)) as f:
            for piece in read_in_chunks(f, 128):
                print '.',
                self.loader.write(piece)

    def read_local_url(self):
        with open(self.url) as f:
            for piece in read_in_chunks(f, 128):
                print '.',
                self.loader.write(piece)

class MainWindow:

    def __init__(self):
        xml = gtk.glade.XML('hw.glade')
        self.window = xml.get_widget('window')
        self.window.connect("delete_event", gtk.main_quit)
        button = xml.get_widget('button')
        button.connect("clicked", self.on_button_clicked)
        self.box = xml.get_widget('vbox1')
        self.poked = 0
        self.window.show_all()

    def on_button_clicked(self, w, *args):
        w.set_label("you poked me" + ("!" * self.poked))
        self.poked += 1
        w.connect("clicked", gtk.main_quit)

w = MainWindow()
ImageLoader(w.box, 'https://developer.gnome.org/skin/'
            'gnome-logo-devcenter.png').start()
ImageLoader(w.box, 'index.jpeg').start()

gtk.main()
mfrasca commented 8 years ago

todo: The pictures property works as it works now, that is it returns a list of gtk.Image objects. The difference that it also spawns a thread that will update the objects returned. the image loading thread updates the image on area-prepared and on closed signals from the PixbufLoader. I see a possible problem in the fact that an Image should be somewhere in the view before you can show it from the two above callbacks. but it really does not need look much more complicated than:

for n in self.notes:
    url = is_absolute(n.note) and n.note or os.path.join(pfolder, n.note)
    im = gtk.Image()
    ImageLoader(im, url).start()
    result.append(im)

where the ImageLoader could look like the thing in the previous comment.

RoDuth commented 8 years ago

I like this... I like how I can link in some images at species level from ALA or Wikipedia etc. that may be helpful for ID reasons (say a picture of the leaves and a picture of the bark, what have you) I don't need to host these locally and I can still have my local pictures at plant level. Great! Not sure how much I'll use it (time constraints wise). Might be useful for those I find hard to ID?