2007-09-28 Tobias Gehrig <tobias@gehrignet.de>

* src/DataViews/_PedigreeView.py: use subsection thumbnails
	* src/DataViews/_RelationView.py: use subsection thumbnails
	* src/Editors/_EditPerson.py: use subsection thumbnails
	* src/Editors/_EditMediaRef.py: added subsection thumbnail preview and signal handlers
	to update subsection thumbnail on spinbutton changes
	* src/DisplayTabs/_GalleryTab.py: use subsection thumbnails
	* src/ThumbNails.py: added rectangle parameter to thumbnail functions to specify the subsection
	* src/GrampsWidgets.py: added class MonitoredSpinButton
	* src/glade/gramps.glade: added preview pixmap of subsection thumbnail to change_description



svn: r9027
This commit is contained in:
Benny Malengier 2007-09-28 18:33:22 +00:00
parent faaaf7e858
commit d502801584
9 changed files with 427 additions and 17 deletions

View File

@ -1,3 +1,14 @@
2007-09-28 Tobias Gehrig <tobias@gehrignet.de>
* src/DataViews/_PedigreeView.py: use subsection thumbnails
* src/DataViews/_RelationView.py: use subsection thumbnails
* src/Editors/_EditPerson.py: use subsection thumbnails
* src/Editors/_EditMediaRef.py: added subsection thumbnail preview and signal handlers
to update subsection thumbnail on spinbutton changes
* src/DisplayTabs/_GalleryTab.py: use subsection thumbnails
* src/ThumbNails.py: added rectangle parameter to thumbnail functions to specify the subsection
* src/GrampsWidgets.py: added class MonitoredSpinButton
* src/glade/gramps.glade: added preview pixmap of subsection thumbnail to change_description
2007-09-28 Zsolt Foldvari <zfoldvar@users.sourceforge.net>
* src/docgen/CairoDoc.py (fontstyle_to_fontdescription): Fix deprecated
warning.

View File

@ -853,7 +853,8 @@ class PedigreeView(PageView.PersonNavView):
if obj:
mtype = obj.get_mime_type()
if mtype and mtype[0:5] == "image":
image = ThumbNails.get_thumbnail_path(obj.get_path())
image = ThumbNails.get_thumbnail_path(obj.get_path(),
rectangle=ph.get_rectangle())
if cairo_available:
pw = PersonBoxWidget_cairo( self.format_helper, lst[i][0], lst[i][3], positions[i][0][3], image);
else:

View File

@ -559,7 +559,8 @@ class RelationshipView(PageView.PersonNavView):
if image_list:
mobj = self.dbstate.db.get_object_from_handle(image_list[0].ref)
if mobj.get_mime_type()[0:5] == "image":
pixbuf = ThumbNails.get_thumbnail_image(mobj.get_path())
pixbuf = ThumbNails.get_thumbnail_image(mobj.get_path(),
rectangle=image_list[0].get_rectangle())
image = gtk.Image()
image.set_from_pixbuf(pixbuf)
image.show()

View File

@ -191,7 +191,8 @@ class GalleryTab(ButtonTab):
handle = ref.get_reference_handle()
obj = self.dbstate.db.get_object_from_handle(handle)
pixbuf = ThumbNails.get_thumbnail_image(obj.get_path(),
obj.get_mime_type())
obj.get_mime_type(),
ref.get_rectangle())
self.iconmodel.append(row=[pixbuf, obj.get_description(), ref])
self._connect_icon_model()
self._set_label()

View File

@ -78,7 +78,7 @@ class EditMediaRef(EditReference):
def _setup_fields(self):
mtype = self.source.get_mime_type()
self.mtype = mtype
self.pix = ThumbNails.get_thumbnail_image(self.source.get_path(),mtype)
self.pixmap = self.top.get_widget("pixmap")
ebox = self.top.get_widget('eventbox')
@ -88,6 +88,14 @@ class EditMediaRef(EditReference):
coord = self.source_ref.get_rectangle()
self.rectangle = coord
self.subpixmap = self.top.get_widget("subpixmap")
self.subpix = ThumbNails.get_thumbnail_image(self.source.get_path(),
mtype,
coord)
self.subpixmap.set_from_pixbuf(self.subpix)
if coord and type(coord) == tuple:
self.top.get_widget("upperx").set_value(coord[0])
self.top.get_widget("uppery").set_value(coord[1])
@ -99,6 +107,30 @@ class EditMediaRef(EditReference):
self.top.get_widget("lowerx").set_sensitive(False)
self.top.get_widget("lowery").set_sensitive(False)
self.upperx_spinbutton = MonitoredSpinButton(
self.top.get_widget("upperx"),
self.set_upperx,
self.get_upperx,
self.db.readonly)
self.uppery_spinbutton = MonitoredSpinButton(
self.top.get_widget("uppery"),
self.set_uppery,
self.get_uppery,
self.db.readonly)
self.lowerx_spinbutton = MonitoredSpinButton(
self.top.get_widget("lowerx"),
self.set_lowerx,
self.get_lowerx,
self.db.readonly)
self.lowery_spinbutton = MonitoredSpinButton(
self.top.get_widget("lowery"),
self.set_lowery,
self.get_lowery,
self.db.readonly)
self.descr_window = MonitoredEntry(
self.top.get_widget("description"),
self.source.set_description,
@ -133,6 +165,153 @@ class EditMediaRef(EditReference):
else:
self.top.get_widget("type").set_text("")
def set_upperx(self, value):
"""
Callback for the signal handling of the spinbutton for the left x coordinate of the subsection.
Updates the subsection thumbnail using the given value for the left x coordinate.
@param value: the left x coordinate of the subsection
"""
if self.rectangle == None:
self.rectangle = (0,0,0,0)
self.rectangle = (value,
self.rectangle[1],
self.rectangle[2],
self.rectangle[3])
self.update_subpixmap()
def set_uppery(self, value):
"""
Callback for the signal handling of the spinbutton for the upper y coordinate of the subsection.
Updates the subsection thumbnail using the given value for the upper y coordinate.
@param value: the upper y coordinate of the subsection
"""
if self.rectangle == None:
self.rectangle = (0,0,0,0)
self.rectangle = (self.rectangle[0],
value,
self.rectangle[2],
self.rectangle[3])
self.update_subpixmap()
def set_lowerx(self, value):
"""
Callback for the signal handling of the spinbutton for the right x coordinate of the subsection.
Updates the subsection thumbnail using the given value for the right x coordinate.
@param value: the right x coordinate of the subsection
"""
if self.rectangle == None:
self.rectangle = (0,0,0,0)
self.rectangle = (self.rectangle[0],
self.rectangle[1],
value,
self.rectangle[3])
self.update_subpixmap()
def set_lowery(self, value):
"""
Callback for the signal handling of the spinbutton for the lower y coordinate of the subsection.
Updates the subsection thumbnail using the given value for the lower y coordinate.
@param value: the lower y coordinate of the subsection
"""
if self.rectangle == None:
self.rectangle = (0,0,0,0)
self.rectangle = (self.rectangle[0],
self.rectangle[1],
self.rectangle[2],
value)
self.update_subpixmap()
def get_upperx(self):
"""
Callback for the signal handling of the spinbutton for the left x coordinate of the subsection.
@returns: the left x coordinate of the subsection or 0 if there is no selection
"""
if self.rectangle != None:
return self.rectangle[0]
else:
return 0
def get_uppery(self):
"""
Callback for the signal handling of the spinbutton for the uppper y coordinate of the subsection.
@returns: the upper y coordinate of the subsection or 0 if there is no selection
"""
if self.rectangle != None:
return self.rectangle[1]
else:
return 0
def get_lowerx(self):
"""
Callback for the signal handling of the spinbutton for the right x coordinate of the subsection.
@returns: the right x coordinate of the subsection or 0 if there is no selection
"""
if self.rectangle != None:
return self.rectangle[2]
else:
return 0
def get_lowery(self):
"""
Callback for the signal handling of the spinbutton for the lower y coordinate of the subsection.
@returns: the lower y coordinate of the subsection or 0 if there is no selection
"""
if self.rectangle != None:
return self.rectangle[3]
else:
return 0
def update_subpixmap(self):
"""
Updates the thumbnail of the specified subsection
"""
path = self.source.get_path()
if path == None:
self.subpixmap.hide()
else:
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(path)
width = pixbuf.get_width()
height = pixbuf.get_height()
upper_x = min(self.rectangle[0], self.rectangle[2])/100.
lower_x = max(self.rectangle[0], self.rectangle[2])/100.
upper_y = min(self.rectangle[1], self.rectangle[3])/100.
lower_y = max(self.rectangle[1], self.rectangle[3])/100.
sub_x = int(upper_x * width)
sub_y = int(upper_y * height)
sub_width = int((lower_x - upper_x) * width)
sub_height = int((lower_y - upper_y) * height)
if sub_width > 0 and sub_height > 0:
pixbuf = pixbuf.subpixbuf(sub_x, sub_y, sub_width, sub_height)
width = sub_width
height = sub_height
ratio = float(max(height, width))
scale = const.THUMBSCALE / ratio
x = int(scale * width)
y = int(scale * height)
pixbuf = pixbuf.scale_simple(x, y, gtk.gdk.INTERP_BILINEAR)
self.subpixmap.set_from_pixbuf(pixbuf)
self.subpixmap.show()
except:
self.subpixmap.hide()
def build_menu_names(self, person):
"""
Provides the information needed by the base class to define the

View File

@ -390,7 +390,7 @@ class EditPerson(EditPrimary):
Called when a media reference had been edited. This allows fot
the updating image on the main form which has just been modified.
"""
self.load_photo(obj.get_path())
self.load_photo(obj.get_path(), ref.get_rectangle())
self.gallery_tab.edit_callback(ref, obj)
def _image_button_press(self, obj, event):
@ -525,7 +525,7 @@ class EditPerson(EditPrimary):
return False
return False
def load_photo(self, path):
def load_photo(self, path, rectangle=None):
"""loads, scales, and displays the person's main photo from the path"""
self.load_obj = path
if path == None:
@ -533,6 +533,21 @@ class EditPerson(EditPrimary):
else:
try:
i = gtk.gdk.pixbuf_new_from_file(path)
width = i.get_width()
height = i.get_height()
if rectangle != None:
upper_x = min(rectangle[0], rectangle[2])/100.
lower_x = max(rectangle[0], rectangle[2])/100.
upper_y = min(rectangle[1], rectangle[3])/100.
lower_y = max(rectangle[1], rectangle[3])/100.
sub_x = int(upper_x * width)
sub_y = int(upper_y * height)
sub_width = int((lower_x - upper_x) * width)
sub_height = int((lower_y - upper_y) * height)
if sub_width > 0 and sub_height > 0:
i = i.subpixbuf(sub_x, sub_y, sub_width, sub_height)
ratio = float(max(i.get_height(), i.get_width()))
scale = float(100.0)/ratio
x = int(scale*(i.get_width()))
@ -696,7 +711,7 @@ class EditPerson(EditPrimary):
if self.load_obj != obj.get_path():
mime_type = obj.get_mime_type()
if mime_type and mime_type.startswith("image"):
self.load_photo(obj.get_path())
self.load_photo(obj.get_path(), photo.get_rectangle())
else:
self.load_photo(None)
else:

View File

@ -414,6 +414,122 @@ class MonitoredEntry:
if self.get_val():
self.obj.set_text(self.get_val())
class MonitoredSpinButton:
"""
Class for signal handling of spinbuttons.
(Code is a modified copy of MonitoredEntry)
"""
def __init__(self, obj, set_val, get_val, read_only=False,
autolist=None, changed=None):
"""
@param obj: widget to be monitored
@type obj: gtk.SpinButton
@param set_val: callback to be called when obj is changed
@param get_val: callback to be called to retrieve value for obj
@param read_only: If SpinButton is read only.
"""
self.obj = obj
self.set_val = set_val
self.get_val = get_val
self.changed = changed
if get_val():
self.obj.set_value(get_val())
self.obj.connect('changed', self._on_change)
self.obj.set_editable(not read_only)
if autolist:
AutoComp.fill_entry(obj,autolist)
def reinit(self, set_val, get_val):
"""
Reinitialize class with the specified callback functions.
@param set_val: callback to be called when SpinButton is changed
@param get_val: callback to be called to retrieve value for SpinButton
"""
self.set_val = set_val
self.get_val = get_val
self.update()
def set_value(self, value):
"""
Sets the value of the monitored widget to the specified value.
@param value: Value to be set.
"""
self.obj.set_value(value)
def connect(self, signal, callback):
"""
Connect the signal of monitored widget to the specified callback.
@param signal: Signal prototype for which a connection should be set up.
@param callback: Callback function to be called when signal is emitted.
"""
self.obj.connect(signal, callback)
def _on_change(self, obj):
"""
Event handler to be called when the monitored widget is changed.
@param obj: Widget that has been changed.
@type obj: gtk.SpinButton
"""
self.set_val(obj.get_value())
if self.changed:
self.changed(obj)
def force_value(self, value):
"""
Sets the value of the monitored widget to the specified value.
@param value: Value to be set.
"""
self.obj.set_value(value)
def get_value(self):
"""
Gets the current value of the monitored widget.
@returns: Current value of monitored widget.
"""
return self.obj.get_value()
def enable(self, value):
"""
Changes the property editable and sensitive of the monitored widget to value.
@param value: If widget should be editable or deactivated.
@type value: bool
"""
self.obj.set_sensitive(value)
self.obj.set_editable(value)
def grab_focus(self):
"""
Assign the keyboard focus to the monitored widget.
"""
self.obj.grab_focus()
def update(self):
"""
Updates value of monitored SpinButton with the value returned by the get_val callback.
"""
if self.get_val():
self.obj.set_value(self.get_val())
class MonitoredText:
def __init__(self, obj, set_val, get_val, read_only=False):

View File

@ -106,7 +106,7 @@ def __get_gconf_bool(key):
# __build_thumb_path
#
#-------------------------------------------------------------------------
def __build_thumb_path(path):
def __build_thumb_path(path, rectangle=None):
"""
Converts the specified path into a corresponding path for the thumbnail
image. We do this by converting the original path into an MD5SUM value
@ -115,10 +115,15 @@ def __build_thumb_path(path):
@type path: unicode
@param path: filename of the source file
@type rectangle: tuple
@param rectangle: subsection rectangle
@rtype: unicode
@returns: full path name to the corresponding thumbnail file.
"""
md5_hash = md5.md5(path)
extra = ""
if rectangle != None:
extra = "?" + str(rectangle)
md5_hash = md5.md5(path+extra)
return os.path.join(const.THUMB_DIR, md5_hash.hexdigest()+'.png')
#-------------------------------------------------------------------------
@ -126,7 +131,7 @@ def __build_thumb_path(path):
# __create_thumbnail_image
#
#-------------------------------------------------------------------------
def __create_thumbnail_image(src_file, mtype=None):
def __create_thumbnail_image(src_file, mtype=None, rectangle=None):
"""
Generates the thumbnail image for a file. If the mime type is specified,
and is not an 'image', then we attempt to find and run a thumbnailer
@ -137,8 +142,10 @@ def __create_thumbnail_image(src_file, mtype=None):
@type src_file: unicode
@param mtype: mime type of the specified file (optional)
@type mtype: unicode
@param rectangle: subsection rectangle
@type rectangle: tuple
"""
filename = __build_thumb_path(src_file)
filename = __build_thumb_path(src_file, rectangle)
if mtype and not mtype.startswith('image/'):
# Not an image, so run the thumbnailer
@ -150,6 +157,21 @@ def __create_thumbnail_image(src_file, mtype=None):
pixbuf = gtk.gdk.pixbuf_new_from_file(src_file)
width = pixbuf.get_width()
height = pixbuf.get_height()
if rectangle != None:
upper_x = min(rectangle[0], rectangle[2])/100.
lower_x = max(rectangle[0], rectangle[2])/100.
upper_y = min(rectangle[1], rectangle[3])/100.
lower_y = max(rectangle[1], rectangle[3])/100.
sub_x = int(upper_x * width)
sub_y = int(upper_y * height)
sub_width = int((lower_x - upper_x) * width)
sub_height = int((lower_y - upper_y) * height)
if sub_width > 0 and sub_height > 0:
pixbuf = pixbuf.subpixbuf(sub_x, sub_y, sub_width, sub_height)
width = sub_width
height = sub_height
scale = const.THUMBSCALE / (float(max(width, height)))
scaled_width = int(width * scale)
@ -214,7 +236,7 @@ def run_thumbnailer(mime_type, src_file, dest_file, size=const.THUMBSCALE):
# get_thumbnail_image
#
#-------------------------------------------------------------------------
def get_thumbnail_image(src_file, mtype=None):
def get_thumbnail_image(src_file, mtype=None, rectangle=None):
"""
Returns the thumbnail image (in GTK Pixbuf format) associated with the
source file passed to the function. If no thumbnail could be found,
@ -228,11 +250,13 @@ def get_thumbnail_image(src_file, mtype=None):
@type src_file: unicode
@param mime_type: mime type of the source file
@type mime_type: unicode
@param rectangle: subsection rectangle
@type rectangle: tuple
@returns: thumbnail representing the source file
@rtype: gtk.gdk.Pixbuf
"""
try:
filename = get_thumbnail_path(src_file, mtype)
filename = get_thumbnail_path(src_file, mtype, rectangle)
return gtk.gdk.pixbuf_new_from_file(filename)
except (gobject.GError, OSError):
if mtype:
@ -246,7 +270,7 @@ def get_thumbnail_image(src_file, mtype=None):
# get_thumbnail_path
#
#-------------------------------------------------------------------------
def get_thumbnail_path(src_file, mtype=None):
def get_thumbnail_path(src_file, mtype=None, rectangle=None):
"""
Returns the path to the thumbnail image associated with the
source file passed to the function. If the thumbnail does not exist,
@ -256,16 +280,18 @@ def get_thumbnail_path(src_file, mtype=None):
@type src_file: unicode
@param mime_type: mime type of the source file
@type mime_type: unicode
@param rectangle: subsection rectangle
@type rectangle: tuple
@returns: thumbnail representing the source file
@rtype: gtk.gdk.Pixbuf
"""
filename = __build_thumb_path(src_file)
filename = __build_thumb_path(src_file, rectangle)
if not os.path.isfile(src_file):
return os.path.join(const.IMAGE_DIR, "image-missing.png")
else:
if not os.path.isfile(filename):
__create_thumbnail_image(src_file, mtype)
__create_thumbnail_image(src_file, mtype, rectangle)
elif os.path.getmtime(src_file) > os.path.getmtime(filename):
__create_thumbnail_image(src_file, mtype)
__create_thumbnail_image(src_file, mtype, rectangle)
return os.path.abspath(filename)

View File

@ -4870,6 +4870,66 @@
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame9">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0.5</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
<child>
<widget class="GtkEventBox" id="eventbox1">
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="above_child">False</property>
<child>
<widget class="GtkImage" id="subpixmap">
<property name="width_request">100</property>
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label707">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Preview&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">2</property>
<property name="bottom_attach">4</property>
<property name="x_options">fill</property>
<property name="y_options">fill</property>
</packing>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>