Example #1
0
    def init(self, parent, params = {}):
        #connect buttons with icons
        self.zoom_fit_button.add(Utils.create_stock_button(gtk.STOCK_ZOOM_FIT))
        self.zoom_100_button.add(Utils.create_stock_button(gtk.STOCK_ZOOM_100))
        self.library_back_button.add(Utils.create_stock_button(gtk.STOCK_GO_BACK, "Back to Library"))
        self.play_slideshow_button.add(Utils.create_stock_button(gtk.STOCK_MEDIA_PLAY, "Play"))
        self.prev_button.add(Utils.create_stock_button(gtk.STOCK_MEDIA_PREVIOUS))
        self.next_button.add(Utils.create_stock_button(gtk.STOCK_MEDIA_NEXT))

        self.selection_add_button.add(Utils.create_stock_button(gtk.STOCK_ADD, icon_size=gtk.ICON_SIZE_MENU))
        self.selection_remove_button.add(Utils.create_stock_button(gtk.STOCK_REMOVE, icon_size=gtk.ICON_SIZE_MENU))
        self.selection_clear_button.add(Utils.create_stock_button(gtk.STOCK_CLEAR, icon_size=gtk.ICON_SIZE_MENU))

        #self.star_button.remove(self.star_button.get_child())
        #self.star_button.add(Utils.create_stock_button("unstarred"))

        #self.star_button.set_property('orientation', gtk.ORIENTATION_HORIZONTAL)
        self.rotate_left_button.add(Utils.create_stock_button("object-rotate-left"))
        self.rotate_right_button.add(Utils.create_stock_button("object-rotate-right"))
        self.add_tag_button.add(Utils.create_stock_button("tag"))

        self.image_viewport = GtkImageView()

        self.vbox2.pack_start(self.image_viewport, True, True, 0)
        self.vbox2.reorder_child(self.image_viewport, 0)
        self.image_viewport.show()
        self.thumbnails = [self.thumbnail1, self.thumbnail2, self.thumbnail3, self.thumbnail4, self.thumbnail5, self.thumbnail6, self.thumbnail7 ]
        self.image_viewport.modify_bg(gtk.STATE_NORMAL,  gtk.gdk.color_parse('#888A85'))

        #connect eventboxes to thumbnails
        dx = -3
        for eventbox in [self.thumbnail1event, self.thumbnail2event, self.thumbnail3event, self.thumbnail5event, self.thumbnail6event, self.thumbnail7event]:
            eventbox.set_events(gtk.gdk.BUTTON1_MASK)
            eventbox.connect("button_press_event", self.move_to_thumbnail, dx)
            dx+=1
            if dx==0: dx+=1

        #colorize status_line
        self.statusline.modify_fg(gtk.STATE_NORMAL,  gtk.gdk.color_parse('#eeeeec'))
        self.statusline_eventbox.modify_bg(gtk.STATE_NORMAL,  gtk.gdk.color_parse('#5590ba'))
        self.parent = parent

        #connect change-value signals to scrollbars of main image
        #self.scrolled_viewport.get_vscrollbar().connect('change-value', self.change_zoom_origin_x)

        label = gtk.Label("Hello World\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nHello")
        x = Utils.create_expander("QuickFix", label, False)
        self.plugins_box.pack_start(x, False, False)

        #self.image_viewport.connect("button_release_event", self.on_image_viewport_button_release_event)
        self.caption_text.get_buffer().connect_after('changed', self.on_caption_text_changed_event)
        self.caption_text.get_buffer().connect('insert-text', self.on_caption_text_insert_text_event)

        #ValueButton for rating
        self.star_value_button = ValueButton([
            (0, 'star-0of5', '0/5'), (1, 'star-1of5', '1/5'), (2, 'star-2of5', '2/5'),
            (3, 'star-3of5', '3/5'), (4, 'star-4of5', '4/5'), (5, 'star-5of5', '5/5')],
        initial_value = 0, icon_size = gtk.ICON_SIZE_MENU, show_values = True)

        self.star_value_button.show()
        self.star_value_button.connect('value-changed', self._set_image_rating)
        self.hbox8.pack_start(self.star_value_button, False, False)
        self.hbox8.reorder_child(self.star_value_button, 0)

        self.star_value_button.set_border_width(4)
Example #2
0
class PhotoView(GladeApp, object):
    class Size():
        def __init__(self, width, height):
            self.width = width
            self.height = height

    glade = os.path.join('..', os.path.dirname(os.path.dirname(__file__)), 'data', 'photoview.glade')
    window = "vbox1"

    _zoom = 0.0
    _zoom_min = log(0.01)
    _zoom_max = log(8.00)
    _zoom_fit = log(0.1)

    _zoom_origin_x = 0.5
    _zoom_origin_y = 0.5
    zoom_step = 0.1

    images_list = []
    _thumbnails_cache = {}
    parent = None
    thumbnails = []
    selected = []
    _allocation = None
    #image zoom
    def _get_zoom(self):
        return self._zoom

    def _set_zoom_with_callback(self, value, callback = True):
        if value == 'fit':
            value = self._zoom_fit

        if value < self._zoom_min:
            value = self._zoom_min
        if value > self._zoom_max:
            value = self._zoom_max

        if value != self._zoom:
            old = self._zoom
            self._zoom = value
            if value < self._zoom_fit:
                self._zoom_origin_x = 0.5
                self._zoom_origin_y = 0.5

            if callback:
                self.zoom_scale.set_value(value)

            self._apply_zoom_on_image(exp(-old+value))

    def _set_zoom(self, value):
        self._set_zoom_with_callback(value)

    zoom = property(_get_zoom, _set_zoom)

    #currently displayed image
    _image = None
    def _get_image(self):
        return self._image

    def _set_image(self, value):
        self._image = value
        self._compute_fit_zoom()
        self.zoom = 'fit'
        self.image_viewport.set_image(value)
        self.image_viewport.set_property('zoom', self.image_viewport.get_property('fit-zoom'))
        #self._apply_zoom_on_image() #show new image in widget

    image = property(_get_image, _set_image)

    #image index (now displayed) in images_list
    _index = 0
    def _get_index(self):
        return self._index

    def _set_index(self, value):
        if type(value) == int:
            if 0 <= value < len(self.images_list):# and self._index != value:
                self._index = value
                self.image = self.current.getImage()
                self._update_view()

    index = property(_get_index, _set_index)


    #current image node
    def _get_current(self):
        return self.images_list[self.index]
    current = property(_get_current)

    def init(self, parent, params = {}):
        #connect buttons with icons
        self.zoom_fit_button.add(Utils.create_stock_button(gtk.STOCK_ZOOM_FIT))
        self.zoom_100_button.add(Utils.create_stock_button(gtk.STOCK_ZOOM_100))
        self.library_back_button.add(Utils.create_stock_button(gtk.STOCK_GO_BACK, "Back to Library"))
        self.play_slideshow_button.add(Utils.create_stock_button(gtk.STOCK_MEDIA_PLAY, "Play"))
        self.prev_button.add(Utils.create_stock_button(gtk.STOCK_MEDIA_PREVIOUS))
        self.next_button.add(Utils.create_stock_button(gtk.STOCK_MEDIA_NEXT))

        self.selection_add_button.add(Utils.create_stock_button(gtk.STOCK_ADD, icon_size=gtk.ICON_SIZE_MENU))
        self.selection_remove_button.add(Utils.create_stock_button(gtk.STOCK_REMOVE, icon_size=gtk.ICON_SIZE_MENU))
        self.selection_clear_button.add(Utils.create_stock_button(gtk.STOCK_CLEAR, icon_size=gtk.ICON_SIZE_MENU))

        #self.star_button.remove(self.star_button.get_child())
        #self.star_button.add(Utils.create_stock_button("unstarred"))

        #self.star_button.set_property('orientation', gtk.ORIENTATION_HORIZONTAL)
        self.rotate_left_button.add(Utils.create_stock_button("object-rotate-left"))
        self.rotate_right_button.add(Utils.create_stock_button("object-rotate-right"))
        self.add_tag_button.add(Utils.create_stock_button("tag"))

        self.image_viewport = GtkImageView()

        self.vbox2.pack_start(self.image_viewport, True, True, 0)
        self.vbox2.reorder_child(self.image_viewport, 0)
        self.image_viewport.show()
        self.thumbnails = [self.thumbnail1, self.thumbnail2, self.thumbnail3, self.thumbnail4, self.thumbnail5, self.thumbnail6, self.thumbnail7 ]
        self.image_viewport.modify_bg(gtk.STATE_NORMAL,  gtk.gdk.color_parse('#888A85'))

        #connect eventboxes to thumbnails
        dx = -3
        for eventbox in [self.thumbnail1event, self.thumbnail2event, self.thumbnail3event, self.thumbnail5event, self.thumbnail6event, self.thumbnail7event]:
            eventbox.set_events(gtk.gdk.BUTTON1_MASK)
            eventbox.connect("button_press_event", self.move_to_thumbnail, dx)
            dx+=1
            if dx==0: dx+=1

        #colorize status_line
        self.statusline.modify_fg(gtk.STATE_NORMAL,  gtk.gdk.color_parse('#eeeeec'))
        self.statusline_eventbox.modify_bg(gtk.STATE_NORMAL,  gtk.gdk.color_parse('#5590ba'))
        self.parent = parent

        #connect change-value signals to scrollbars of main image
        #self.scrolled_viewport.get_vscrollbar().connect('change-value', self.change_zoom_origin_x)

        label = gtk.Label("Hello World\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nHello")
        x = Utils.create_expander("QuickFix", label, False)
        self.plugins_box.pack_start(x, False, False)

        #self.image_viewport.connect("button_release_event", self.on_image_viewport_button_release_event)
        self.caption_text.get_buffer().connect_after('changed', self.on_caption_text_changed_event)
        self.caption_text.get_buffer().connect('insert-text', self.on_caption_text_insert_text_event)

        #ValueButton for rating
        self.star_value_button = ValueButton([
            (0, 'star-0of5', '0/5'), (1, 'star-1of5', '1/5'), (2, 'star-2of5', '2/5'),
            (3, 'star-3of5', '3/5'), (4, 'star-4of5', '4/5'), (5, 'star-5of5', '5/5')],
        initial_value = 0, icon_size = gtk.ICON_SIZE_MENU, show_values = True)

        self.star_value_button.show()
        self.star_value_button.connect('value-changed', self._set_image_rating)
        self.hbox8.pack_start(self.star_value_button, False, False)
        self.hbox8.reorder_child(self.star_value_button, 0)

        self.star_value_button.set_border_width(4)


    def _set_image_rating(self, widget, value):
        if self.current != None:
            self.current.setRating(value)

    def move_to_thumbnail(self, widget, event, number):
        self.index += number



    def _get_thumbnail_from_node(self, node, refresh = False, size = 32):
        """
        Return square miniature of image (e.g. for thumbnail bar or for
        @param node:
        @return:
        """

        if not node.file in self._thumbnails_cache or refresh == True:
            pb = node.getOriginalThumbnail()
            w,h = (pb.get_width(), pb.get_height())

            if w > h:
                pb = pb.scale_simple(w*size/h, size, gtk.gdk.INTERP_HYPER)
            else:
                pb = pb.scale_simple(size, h*size/w, gtk.gdk.INTERP_HYPER)

            pb = pb.subpixbuf(0,0,size, size)
            #print pb.get_width(), pb.get_height()
            self._thumbnails_cache[node.file] = pb
        return self._thumbnails_cache[node.file]


    def _update_thumbnails_bar(self):
        if self.index is not None:
            for i in xrange(-3,4):
                if 0<= self.index+i < len(self.images_list):
                    self.thumbnails[i+3].set_from_pixbuf(self._get_thumbnail_from_node(self.images_list[self.index+i]))
                else:
                    self.thumbnails[i+3].set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_BUTTON)

    def _init_selection_view(self):
        model = gtk.ListStore(gtk.gdk.Pixbuf)
        for i in self.images_list:
            model.append((self._get_thumbnail_from_node(i, True,    size = 16), ))
        self.selection_icons.set_model(model)
        self.selection_icons.set_pixbuf_column(0)
        self.selection_icons.set_item_padding(1)
        self.selection_icons.set_spacing(0)
        self.selection_icons.set_row_spacing(0)
        self.selection_icons.set_column_spacing(0)
        #self.selection_icons.set_item_width(32)


    def init_data(self, images_list, index, selected=[]):
        self.images_list = images_list
        self.selected = selected
        self.index = index

        self._update_view()
        self._init_selection_view()


    def unload(self):
        self.images_list = None
        self.index = None
        self.selected = None



    def _update_status_line(self):
        d = self.current.getInfo()

        t = [
            os.path.split(self.current.file)[1],
            Utils.get_human_readable_date(d['exifdate']),
            str(d['resolution'])+' pixels',
            Utils.get_human_readable_byte_count(d['filesize'], False),
        ]

        #if photo has tags display it
        if len(self.current.tags)>0:
            t.append('tags: '+', '.join(self.current.tags))

        #add position of current photo (e.g 5/12)
        t.append('('+str(self.index+1)+'/'+str(len(self.images_list))+')')

        self.statusline.set_text('    '.join(t))

    def _update_main_image(self):
        self.image = self.current.getImage()

        if self.image is not None:
            self._apply_zoom_on_image(0.0)

    def _update_comment(self):
        if self.current != None:
            x = self.current.comment
            if x == '':
                x = 'Make a Caption!'
            self.caption_text.get_buffer().set_text(x)

    def _update_rating(self):
        if self.current != None:
            self.star_value_button.set_value(self.current.rating, True)

    def _update_view(self):
        self._update_thumbnails_bar()
        self._update_main_image()
        self._update_status_line()
        self._update_zoom_scale()
        self._update_comment()
        self._update_rating()

    def _update_zoom_scale(self):
        """
        Refresh zoom_scale (HScale) widget according to zoom properties
        """
        if self.image is not None:
            self.zoom_scale.clear_marks()
            self.zoom_scale.set_range(self._zoom_min, self._zoom_max)

            self.zoom_scale.add_mark(self._zoom_fit, gtk.POS_BOTTOM, "fit")
            self.zoom_scale.add_mark(0.0, gtk.POS_BOTTOM, "100%")
            self.zoom_scale.set_value(self.zoom)

    def _compute_fit_zoom(self):
        """
        Set _zoom_fit according to viewport size
        """
        """if self.image is None:
            return

        #TODO: change to _get_allocation_size()
        bounds = self._get_allocation_size()
        width, height = bounds.width, bounds.height

        #add some margin
        width-=16
        height-=16

        set_zoom_fit = True if self._zoom_fit == self._zoom else False
        x = width*self.image.get_height()/height

        if x >= self.image.get_width():
            self._zoom_fit = log(float(height)/float(self.image.get_height())) #fill height
        else:
            self._zoom_fit = log(float(width)/float(self.image.get_width())) #fill width

        #TODO: add better values
        self._zoom_min = min( 0.0, self._zoom_fit-2.0)
        self._zoom_max = max(self._zoom_fit+2.0, 0.0)

        #If zoom was fitted, then restore them to 'fit'
        if set_zoom_fit:
            self.zoom = 'fit'

        self._update_zoom_scale()"""

    def _get_zoomed_size(self):
        if self.image is not None:
            return PhotoView.Size(self.image.get_width()*exp(self.zoom), self.image.get_height()*exp(self.zoom))
        return PhotoView.Size(0,0)

    def _get_allocation_size(self):
        if self._allocation is None:
            self._allocation = self.image_viewport.get_allocation()
        bounds = self._allocation#self.image_viewport.get_allocation()
        return PhotoView.Size(bounds.width, bounds.height)

    def _update_adjustments(self):
        zoomed = self._get_zoomed_size()
        alloc = self._get_allocation_size()

        self.image_viewport.get_vadjustment().set_value(max(0.0, self._zoom_origin_y*zoomed.height-0.5*alloc.height))
        self.image_viewport.get_hadjustment().set_value(max(0.0, self._zoom_origin_x*zoomed.width-0.5*alloc.width))
        #self.scrolled_viewport.set_hadjustment(hadj)


    def _apply_zoom_on_image(self, ratio):
        """
        Set display_image with applying current zoom property
        """
        """if self.image is not None and ratio != 1.0:
            zoomed = self._get_zoomed_size()
            alloc = self._get_allocation_size()

            off_x = -self.image_viewport.get_bin_window().get_position()[0]
            x = alloc.width*self._zoom_origin_x
            dx = off_x+x

            off_y = -self.image_viewport.get_bin_window().get_position()[1]
            y = alloc.height*self._zoom_origin_y
            dy = off_y+y
            #print (dx, dy, ratio, self._zoom_origin_x, self._zoom_origin_y)

            self.image_viewport.get_bin_window().freeze_updates()
            self.display_image.set_from_pixbuf(
                self.image.scale_simple(
                    int(zoomed.width),
                    int(zoomed.height),
                    gtk.gdk.INTERP_NEAREST #gtk.gdk.INTERP_HYPER
                )
            )

            alloc = self._get_allocation_size()
            #print (dx*ratio-x, dy*ratio-y)
            self.image_viewport.get_hadjustment().set_value(dx*ratio-x)

            self.image_viewport.get_vadjustment().set_value(dy*ratio-y)


            self.image_viewport.get_bin_window().thaw_updates()
        """

    def on_prev_button_clicked(self, event):
        self.index -= 1

    def on_next_button_clicked(self, event):
        self.index += 1

    def on_library_back_button_clicked(self, event):
        #TODO
        self.parent.notebook2.set_current_page(0)
        self.unload()



    """def on_image_viewport_size_allocate(self, widget, allocation):
        alloc = self._get_allocation_size()

        if alloc.width != allocation.width or alloc.height != allocation.height:
            self._allocation = allocation

            if self.image is not None:
                self._compute_fit_zoom()

        return False"""


    def on_zoom_fit_button_clicked(self, event):
        self.image_viewport.set_property('horizontal-center', 0.5)
        self.image_viewport.set_property('vertical-center', 0.5)
        self.image_viewport.set_property('zoom', self.image_viewport.get_property('fit-zoom'))


    def on_zoom_100_button_clicked(self, event):
        self.image_viewport.set_property('horizontal-center', 0.5)
        self.image_viewport.set_property('vertical-center', 0.5)
        self.image_viewport.set_property('zoom', 1.0)

    def on_zoom_out_eventbox_button_press_event(self, widget, event):
        self._zoom_origin_x = self._zoom_origin_y = 0.5
        self.zoom -= self.zoom_step

    def on_zoom_in_eventbox_button_press_event(self, widget, event):
        self._zoom_origin_x = self._zoom_origin_y = 0.5
        self.zoom += self.zoom_step

    def on_zoom_scale_change_value(self, widget, scroll, value):
        self._zoom_origin_x = self._zoom_origin_y = 0.5
        self._set_zoom_with_callback(value, False)


    """def on_scrolled_viewport_scroll_event(self, widget, event):
        alloc = self._get_allocation_size()
        #zoomed = self._get_zoomed_size()



        orig_x = float(event.x)/float(alloc.width)
        orig_y = float(event.y)/float(alloc.height)

        #orig_x = float(event.x)/float(zoomed.width)
        #orig_y = float(event.y)/float(zoomed.height)

        # print (orig_x, orig_y)

        if event.direction == gtk.gdk.SCROLL_UP:
            self._zoom_origin_x = orig_x
            self._zoom_origin_y = orig_y
            self.zoom += self.zoom_step
        elif event.direction == gtk.gdk.SCROLL_DOWN:
            self._zoom_origin_x = orig_x
            self._zoom_origin_y = orig_y
            self.zoom -= self.zoom_step

        return True"""


    """def on_scrolled_viewport_motion_notify_event(self, widget, event):
        return False"""


    """def on_image_viewport_button_press_event(self, widget, event):
        #print (event.x, event.y, event.button, )
        return False

    def on_image_viewport_button_release_event(self, widget, event):
        #print (event.x, event.y, event.button, )
        return False"""

    def _save_comment(self, comment):
        if type(comment) == str:
            comment = unicode(comment)
        #print 'Setting comment: '+comment+' '+str(type(comment))
        self.current.setComment(comment)

    def on_caption_text_focus_in_event(self, widget, event):
        """
        On focus-in update caption
        @param widget:
        @param event:
        @return:
        """
        widget.get_buffer().set_text(self.current.comment)
        return False

    def on_caption_text_focus_out_event(self, widget, event):
        """
        On focus-out save caption as jpeg comment
        @param widget:
        @param event:
        @return:
        """
        buffer = widget.get_buffer()
        caption = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()).strip()
        self._save_comment(caption)
        if caption == '':
            buffer.set_text('Make a caption!')

        return False

    def on_caption_text_insert_text_event(self, buffer, iter, text, length):
        """
        If user pressed enter, leave focus
        @param buffer:
        @param iter:
        @param text:
        @param length:
        """
        if text == '\n':
            #print 'Entered: '+buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())

            self.caption_text.get_toplevel().child_focus(gtk.DIR_TAB_FORWARD)


    def on_caption_text_changed_event(self, buffer):#, iter, text, length):
        """
        Remove new lines "\n" and multiple spaces from caption text
        @param buffer:
        @return:
        """
        old_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()) #get caption text

        if old_text.find('\n') != -1 or old_text.find('  ') != -1: #is something to update?
            old_text = old_text.replace('\n', '') #replace newline

            import re
            old_text = re.compile('[ ]{2,}').subn(' ', old_text)[0] #replace multiple spaces

            #print old_text
            buffer.set_text(old_text) #update caption text

        return True

    def on_caption_remove_eventbox_button_press_event(self, widget, event):
        if event.button == 1: #detect left button press
            if self.current.comment != "":
                ans = InputQuestion(self.parent.main_widget,
                                    "Do you want to remove comment?",
                                    buttons = (gtk.STOCK_NO, gtk.RESPONSE_CANCEL, gtk.STOCK_YES, gtk.RESPONSE_OK))
                if ans:
                    self.current.setComment(u"")
                    self._update_comment()

    def on_add_tag_button_clicked(self, widget):
        tag_editor = TagEditorDialog(self.current)
        if tag_editor.run():
            self._update_status_line()

    def on_rotate_right_button_clicked(self, widget):
        self.current.rotate('R')
        self._get_thumbnail_from_node(self.current, True)
        self._update_view()

    def on_rotate_left_button_clicked(self, widget):
        self.current.rotate('L')
        self._get_thumbnail_from_node(self.current, True)
        self._update_view()