#!/usr/bin/env python
# Exile, an image labeler using exif fields.
# Copyright (C) 2009 Andrea Fazekas
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License, or
# (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
# Todo:
# Basic menus
# Attribute copy/paste
# Rotate
# Thumbnail size option
# Recursion getopt
# Larger image preview
# Sort manager
# +Menu items
# +Show/Hide stars
# +Next on star
# Toolbar icons
# +Show only High/Medium/Low
# Directory D'n'D
# Progressbar
# Quit warning if editing
# GPS
# Languages
# Label shortcuts
# Exif standardization
# Rename label
# File type support (jpg, pef, etc.)
# +Fill autocomplete list from fields
# Undo
# Jump to file
# +Move caption menu to a separate dialog (to be able to change several options with one reload)
# Use defines in tuple indexes
# Split preview page to pages per days (/hours? /N number of pictures?)
# +Pretty GPS caption
# Option to enable/disable thumbnail rotation
# Refresh selected files/reload all
# +/- zoom in Preview window
# +Load blank images with correct orientation to prevent flickering while thumbnail load
from exile_import import *
DEBUG = True
TARGET_TYPE_URI_LIST = 80
windows = []
class ExileException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class ExileWindow:
nolabel = "No label"
def __init__(self, dirname, recursive=False, convert_old_keywords_param=False):
global attributes
self.dirname = dirname
self.convert_old_keywords_param = convert_old_keywords_param
self.idle_id = -1
self.block_edit = False
self.attributes = ExileFields.attributes
self.metadata = ExileMetadata()
self.thumbnail_extractor = ExileThumbnailExtractor(self)
self.gps = ExileGPS()
self.gps_database = ExileGPSDatabase()
self.tools = ExileTools(self)
self.exile_preview = None
self.attribute_entries = {}
self.caption_menus = {}
self.hide_attributes = {}
self.load_all = False
self.unsaved_changes = False;
self.gladexml = gtk.glade.XML("exile.glade", "window_main")
self.window_main = self.gladexml.get_widget("window_main")
self.iconview = self.gladexml.get_widget("iconview_images")
self.scrolledwindow = self.gladexml.get_widget("scrolledwindow_images")
self.treeview_labels = self.gladexml.get_widget("treeview_labels")
self.menu_view = self.gladexml.get_widget("menu_view")
self.menu_hide = self.gladexml.get_widget("menu_hide")
self.menu_tools = self.gladexml.get_widget("menu_tools")
self.statusbar = self.gladexml.get_widget("statusbar")
self.context_id = self.statusbar.get_context_id("Images")
self.visible_range = None
self.gps_entries = [None, None, None]
self.set_clone_gps_icon()
self.create_stars()
self.show_stars = True
self.set_priority_toolbuttons()
self.create_iconview()
self.create_attributes()
self.create_recent_menu()
self.create_caption_menu()
self.create_hide_menu()
self.tools.create_menus(self.menu_tools)
self.create_labels()
#~ self.treeview.drag_dest_set(gtk.DEST_DEFAULT_DROP, [('text/uri-list', 0, TARGET_TYPE_URI_LIST)], gtk.gdk.ACTION_PRIVATE)
#~ self.treeview.unset_rows_drag_source()
self.window_main.connect("delete_event", self.delete_event)
self.window_main.connect("key-press-event", self.on_window_main_key_press_event)
if self.load_all == False: #FIXME: eleg lassu
self.scrolledwindow.get_vadjustment().connect_after("value-changed", self.on_scrolledwindow_event, None)
self.scrolledwindow.connect_after("size-allocate", self.on_scrolledwindow_event)
dict_connect = { "on_button_remove_clicked" : self.on_button_remove_clicked,
"on_button_add_clicked" : self.on_button_add_clicked,
"on_button_show_clicked" : self.on_button_show_clicked,
"on_menuitem_next_activate" : self.on_menuitem_next_activate,
"on_menuitem_prev_activate" : self.on_menuitem_prev_activate,
"on_menuitem_up_activate" : self.on_menuitem_up_activate,
"on_menuitem_down_activate" : self.on_menuitem_down_activate,
"on_quit_activate" : self.on_quit_activate,
"on_about_activate" : self.on_about_activate,
"on_open_activate" : self.on_open_activate,
"on_save_activate" : self.on_save_activate,
"on_export_activate" : self.on_export_activate,
"on_iconview_images_item_activated": self.on_iconview_images_item_activated,
"on_button_clonegps_clicked": self.on_button_clonegps_clicked,
"on_toggle_label_activate": self.on_toggle_label_activate,
"on_toggle_high_activate": self.on_toggle_high_activate,
"on_toggle_medium_activate": self.on_toggle_medium_activate,
"on_toggle_low_activate": self.on_toggle_low_activate,
"on_toggle_unset_activate": self.on_toggle_unset_activate,
"on_menuitem_stars_activate": self.on_menuitem_stars_activate,
"on_toolbutton_show_priority_activate": self.on_toolbutton_show_priority_activate,
"on_menuitem_noinstantsave_activate": self.on_menuitem_noinstantsave_activate,
"on_menuitem_andlabelvisibility_activate": self.on_menuitem_andlabelvisibility_activate,
#~ "on_options_activate" : self.on_options_activate,
#~ "on_treeview_drag_data_received" : self.on_treeview_drag_data_received,
#~ "on_treeview_drag_drop" : self.on_treeview_drag_drop
}
self.gladexml.signal_autoconnect(dict_connect)
self.load_completition_file()
self.load_dir(self.dirname, urlparse.urlunsplit(['file', '', os.path.abspath(self.dirname), '', '']), recursive)
self.window_main.show()
self.load_visible_range()
self.select_path((0,))
# Events
def delete_event(self, widget, event, data=None):
# FIXME: Warning if unsaved
global windows
windows.remove(self)
if (len(windows) == 0):
self.save_completition_file()
gtk.main_quit()
return False
def on_quit_activate(self, widget):
# FIXME: Warning if unsaved
self.save_completition_file()
gtk.main_quit()
def on_about_activate(self, widget):
gladexml_about = gtk.glade.XML("exile.glade", "aboutdialog_exile")
dialog_about = gladexml_about.get_widget("aboutdialog_exile")
dialog_about.set_transient_for(self.window_main)
fieldstr = ""
maxlen = max([len(attribute[1].replace("_", "")) for attribute in self.attributes])
for attribute in self.attributes + [["Iptc.Application2.Keywords", "Label"]]:
if "Iptc" not in attribute[0]:
tag = "Iptc.Application2.Keywords (" + attribute[0] + ":)"
else:
tag = attribute[0]
label = attribute[1].replace("_", "")
tab = ''.join(['\t' for num in xrange(1 + (maxlen - len(label))/4)])
fieldstr = fieldstr + label + tab + " : " + tag + "\n"
if USE_LIB:
uselibstr = "\nUsing exiv2 library."
else:
uselibstr = "\nUsing exiv2 command line."
dialog_about.set_translator_credits("Field - IPTC tag:\n" + fieldstr + uselibstr)
dialog_about.run()
dialog_about.destroy()
# File operations
def create_recent_menu(self):
self.menu_recent = gtk.RecentChooserMenu(gtk.recent_manager_get_default())
rf = gtk.RecentFilter()
rf.add_pattern("")
rf.add_custom(gtk.RECENT_FILTER_URI, self.recent_filter_func)
self.menu_recent.set_filter(rf)
self.menu_recent.set_show_numbers(True)
self.menu_recent.set_sort_type(gtk.RECENT_SORT_MRU)
self.menu_recent.connect("item-activated", self.on_menu_recent_item_activated)
self.gladexml.get_widget("recent").set_submenu(self.menu_recent)
def recent_filter_func(self,filterinfo):
return os.path.isdir(self.get_file_path_from_dnd_dropped_uri(filterinfo["uri"])) # is it directory?
def on_open_activate(self, widget):
gladexml_open = gtk.glade.XML("exile.glade", "filechooserdialog_exile")
dialog_open = gladexml_open.get_widget("filechooserdialog_exile")
dialog_open.set_filename(self.dirname)
response = dialog_open.run()
if response == 1:
self.dirname = dialog_open.get_filename()
self.reopen(gladexml_open.get_widget("checkbutton_recursive").get_active(), gladexml_open.get_widget("checkbutton_keep_labels").get_active())
dialog_open.destroy()
def get_file_path_from_dnd_dropped_uri(self, uri):
path = urllib.url2pathname(uri[5:]) # escape special chars, 5 is len 'file:'
path = path.strip('\r\n\x00') # remove \r\n and NULL
# get the path to file
if path.startswith('\\\\\\'): # windows
path = path[3:] # 3 is len('///')
elif path.startswith('file://'): # nautilus, rox
path = path[2:] # 2 is len('//')
return path
def load_file_from_uri(self, uri):
path = self.get_file_path_from_dnd_dropped_uri(uri)
if os.path.isdir(path): # is it directory?
self.dirname = path
self.reopen()
return True
return False
def on_menu_recent_item_activated(self, chooser):
self.load_file_from_uri(chooser.get_current_uri())
def reopen(self, recursive=False, keep_labels=False):
self.model_icon.clear()
self.load_dir(self.dirname, urlparse.urlunsplit(['file', '', os.path.abspath(self.dirname), '', '']), recursive, keep_labels)
self.load_visible_range()
self.select_path((0,))
def on_export_activate(self, widget):
dialog_export = gtk.FileChooserDialog("Export attributes", self.window_main, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK), None)
dialog_export.set_filename(self.dirname)
dialog_export.set_do_overwrite_confirmation(True)
response = dialog_export.run()
while response == gtk.RESPONSE_OK:
filename = dialog_export.get_filename()
text = ""
filenames = self.get_all_files()
basenames = self.get_all_files(False)
for file in filenames:
text = text + basenames[filenames.index(file)] + "\n"
metadata = self.metadata.get_data(file)
for field in self.metadata.iptc_attribute_keys + ["labels",] + self.metadata.additional_exif_keys:
if field in metadata and len(metadata[field].__str__()) > 0:
text = text + field + ":" + metadata[field].__str__() + "\n"
print text
efile = open(filename, "w")
efile.write(text)
efile.close()
break
dialog_export.destroy()
def on_save_activate(self, widget):
self.flush_all()
self.set_save_sensitive(False)
self.unsaved_changes = False
def on_menuitem_noinstantsave_activate(self, widget):
self.set_save_sensitive(widget.get_active())
if widget.get_active():
dialog = gtk.MessageDialog(self.window_main,
gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING,
gtk.BUTTONS_CLOSE, "USE WITH CAUTION!!!\nChanges will not be saved to the hard drive automatically!!!\nUse Save to save changes!")
dialog.run()
dialog.destroy()
else:
if self.unsaved_changes:
self.on_save_activate(widget)
def is_instant_save(self):
if self.gladexml.get_widget("menuitem_noinstantsave").get_active():
self.set_save_sensitive(True)
self.unsaved_changes = True
return False
return True
def flush_all(self):
for row in self.model_icon:
self.metadata.flush(row[2][1])
def flush_filenames(self, filenames):
for filename in filenames:
self.metadata.flush(filename)
def set_save_sensitive(self, active):
self.gladexml.get_widget("save").set_sensitive(active)
self.gladexml.get_widget("toolbutton_save").set_sensitive(active)
# Load images
def load_thumb(self, fromiter, force = False):
if self.model_icon[fromiter][3] == False or force:
thumbnail = self.thumbnail_extractor.get_thumbnail(self.metadata.get_image(self.model_icon[fromiter][2][1]), self.model_icon[fromiter][2][1])
angle = self.metadata.get_rotation(self.model_icon[fromiter][2][1])
if angle > 0:
thumbnail = thumbnail.rotate_simple(angle)
thumbnail = self.mark_image(thumbnail, self.metadata.get_priority(self.model_icon[fromiter][2][1]))
self.model_icon[fromiter][1] = thumbnail
#self.model_icon[fromiter][1] = gtk.gdk.pixbuf_new_from_file_at_size(self.model_icon[fromiter][2][1], -1, 100)
self.model_icon[fromiter][3] = True
def load_visible_range(self):
visrange = self.iconview.get_visible_range()
if (self.visible_range == visrange):
return
self.visible_range = visrange
if self.visible_range:
frompath = self.modelfilter_icon.convert_path_to_child_path(self.visible_range[0])
topath = self.modelfilter_icon.convert_path_to_child_path(self.visible_range[1])
fromiter = self.model_icon.get_iter(frompath)
while self.model_icon.get_path(fromiter)[0] <= topath[0]:
self.load_thumb(fromiter)
fromiter = self.model_icon.iter_next(fromiter)
if fromiter == None:
break
if self.idle_id > 0:
gobject.source_remove(self.idle_id)
self.first_unloaded = topath[0]
self.idle_id = gobject.idle_add(self.idle_load)
def load_dir(self, dirname=None, uri = None, recursive=False, keep_labels=False):
self.metadata.cache.clear()
self.model_icon.clear()
if keep_labels == False:
self.model_labels_clear()
filenames=[]
self.dirname = dirname
pixbuf_flat = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 160, 106)
pixbuf_flat.fill(0x00000000)
pixbuf_rotated = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 106, 160)
pixbuf_rotated.fill(0x00000000)
labels_to_sort = []
for root, dirs, files in os.walk(dirname):
for file in files:
if string.lower(file[-4:]) == ".jpg" or string.lower(file[-4:]) == ".pef":
mode = os.stat(os.path.join(root, file))[stat.ST_MODE]
if stat.S_ISREG(mode):
filenames.append([file, os.path.realpath(os.path.join(root, file))])
filenames.sort()
for filename in filenames:
if self.load_all:
pixbuf = self.thumbnail_extractor.get_thumbnail(self.metadata.get_image(filename[1]), filename[1])
pixbuf = self.mark_image(pixbuf, self.metadata.get_priority(filename[1]))
else:
if self.metadata.get_rotation(filename[1]) == 0:
pixbuf = pixbuf_flat
else:
pixbuf = pixbuf_rotated
self.model_icon.append([self.get_caption(filename), pixbuf, filename, self.load_all, self.get_tooltip(filename), True])
self.collect_labels(filename, labels_to_sort)
self.load_completition(filename)
if self.convert_old_keywords_param:
self.convert_old_keywords(filename[1])
if recursive == False:
break
filenames=[]
self.append_labels_to_model(labels_to_sort)
if uri:
gtk.recent_manager_get_default().add_item(uri)
self.statusbar_set()
def append_labels_to_model(self, labels_to_sort):
for label in sorted(labels_to_sort):
if label not in [row[1] for row in self.model_labels]:
self.model_labels.append([False, label, False, gtk.STOCK_DIALOG_INFO, True])
def collect_labels(self, filename, labels_to_sort):
labels = self.metadata.get_labels(filename[1])
for label in labels:
if len(label):
labels_to_sort.append(label)
def load_completition(self, filename):
map = self.metadata.get_data(filename[1])
for attribute in [attr_tuple[0] for attr_tuple in self.attributes]:
self.add_to_completion(self.attribute_entries[attribute], map[attribute])
# Captions
def caption_get_active(self, attribute):
return attribute in self.caption_menus and self.caption_menus[attribute][1]
def get_caption(self, filename):
caption = ""
map = self.metadata.get_data(filename[1])
for attribute in self.attributes:
if self.caption_get_active(attribute[0]):
if attribute[0] in map:
value = map[attribute[0]]
if len(value) > 0:
caption = caption + "<b>" + value + "</b>\n"
if self.caption_get_active("labels"):
labels = self.metadata.get_labels(filename[1])
if len(labels) > 0:
caption = caption + "<i>" + string.join(labels, ", ") + "</i>\n"
if self.caption_get_active("date") and "Exif.Image.DateTime" in map.keys():
caption = caption + "<small>" + map["Exif.Image.DateTime"] + "</small>" + "\n"
if self.caption_get_active("gps"):
value = self.gps.get_gps_text(map, True);
caption = caption + "<small>" + value+ "</small>" + "\n"
if self.caption_get_active("filename"):
if len(caption) == 0:
caption = filename[0]
else:
caption = caption + "<small>" + filename[0] + "</small>"
return string.strip(caption)
def refresh_selected_captions(self):
for path in [self.modelfilter_icon.convert_path_to_child_path(fpath) for fpath in self.iconview.get_selected_items()]:
self.model_icon[path][0] = self.get_caption(self.model_icon[path][2])
self.model_icon[path][4] = self.get_tooltip(self.model_icon[path][2])
def create_caption_menu(self):
menuitem = gtk.SeparatorMenuItem()
self.menu_view.append(menuitem)
menuitem = gtk.CheckMenuItem("_Filename", True)
sid = menuitem.connect('toggled', self.on_menuitem_caption_toggled, "filename")
menuitem.set_active(False)
self.caption_menus["filename"] = [menuitem, menuitem.get_active(), sid]
self.menu_view.append(menuitem)
menuitem = gtk.CheckMenuItem("_Date", True)
sid = menuitem.connect('toggled', self.on_menuitem_caption_toggled, "date")
menuitem.set_active(True)
self.caption_menus["date"] = [menuitem, menuitem.get_active(), sid]
self.menu_view.append(menuitem)
menuitem = gtk.CheckMenuItem("_GPS", True)
sid = menuitem.connect('toggled', self.on_menuitem_caption_toggled, "gps")
menuitem.set_active(False)
self.caption_menus["gps"] = [menuitem, menuitem.get_active(), sid]
self.menu_view.append(menuitem)
for attribute in self.attributes:
menuitem = gtk.CheckMenuItem(attribute[1], True)
sid = menuitem.connect('toggled', self.on_menuitem_caption_toggled, attribute[0])
menuitem.set_active(attribute[2])
self.caption_menus[attribute[0]] = [menuitem, menuitem.get_active(), sid]
self.menu_view.append(menuitem)
menuitem = gtk.CheckMenuItem("Labels", True)
sid = menuitem.connect('toggled', self.on_menuitem_caption_toggled, "labels")
menuitem.set_active(False)
self.caption_menus["labels"] = [menuitem, menuitem.get_active(), sid]
self.menu_view.append(menuitem)
menuitem = gtk.MenuItem("Edit all attributes visibility...", True)
menuitem.connect('activate', self.on_edit_captions_activated)
self.menu_view.append(menuitem)
self.menu_view.show_all()
def captions_refresh(self):
for row in self.model_icon:
row[0] = self.get_caption(row[2])
def set_caption(self, attribute, value):
if attribute not in self.caption_menus:
return
self.caption_menus[attribute][1] = value
def on_menuitem_caption_toggled(self, menuitem, attribute):
self.set_caption(attribute, menuitem.get_active())
self.captions_refresh()
def on_edit_captions_activated(self, menuitem):
dialog = gtk.Dialog("Edit all attributes visibility",
self.window_main,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
vbox = gtk.VBox(False, 12)
vbox.set_border_width(12)
label = gtk.Label()
label.set_markup("<b>Select attributes to display as caption</b>")
label.set_padding(12, 0)
vbox.pack_start(label, False, True, 12)
vbox.pack_start(gtk.HSeparator(), False, False, 0)
dialog.get_child().pack_start(vbox, False)
checkboxes = []
checkbox = gtk.CheckButton("Filename", True)
checkbox .set_name("filename")
checkbox.set_active(self.caption_get_active("filename"))
vbox.pack_start(checkbox, False)
checkboxes.append(checkbox)
checkbox = gtk.CheckButton("Date", True)
checkbox .set_name("date")
checkbox.set_active(self.caption_get_active("date"))
vbox.pack_start(checkbox, False)
checkboxes.append(checkbox)
checkbox = gtk.CheckButton("GPS", True)
checkbox .set_name("gps")
checkbox.set_active(self.caption_get_active("gps"))
vbox.pack_start(checkbox, False)
checkboxes.append(checkbox)
for attribute in self.attributes:
checkbox = gtk.CheckButton(attribute[1], True)
checkbox .set_name(attribute[0])
checkbox.set_active(self.caption_get_active(attribute[0]))
vbox.pack_start(checkbox, False)
checkboxes.append(checkbox)
checkbox = gtk.CheckButton("Labels", True)
checkbox .set_name("labels")
checkbox.set_active(self.caption_get_active("labels"))
vbox.pack_start(checkbox, False)
checkboxes.append(checkbox)
vbox.show_all()
if dialog.run() == gtk.RESPONSE_OK:
for checkbox in checkboxes:
if checkbox.get_name() in self.caption_menus:
menuitem = self.caption_menus[checkbox.get_name()][0]
menuitem.handler_block(self.caption_menus[checkbox.get_name()][2])
menuitem.set_active(checkbox.get_active())
menuitem.handler_unblock(self.caption_menus[checkbox.get_name()][2])
self.set_caption(checkbox.get_name(), checkbox.get_active())
self.captions_refresh()
dialog.destroy()
def get_tooltip(self, filename):
tooltip = ""
map = self.metadata.get_data(filename[1])
for attribute in self.attributes + [["Iptc.Application2.Headline", "Headline"]]:
if attribute[0] in map:
value = map[attribute[0]]
if len(value) > 0:
tooltip = tooltip + "<b>" + string.replace(attribute[1], "_", "") + ": " + value + "</b>\n"
if "labels" in map:
labels = self.metadata.get_labels(filename[1])
if len(labels) > 0:
tooltip = tooltip + "<b>Labels: " + string.join(labels, ", ") + "</b>\n"
value = self.gps.get_gps_text(map, False);
if len(value) > 0:
tooltip = tooltip + "GPS: " + value + "\n"
for exif_field in self.metadata.exif_fields:
if exif_field in map:
value = map[exif_field]
if len(value) > 0:
tooltip = tooltip + value + "\n"
if len(tooltip) == 0:
tooltip = filename[0]
else:
tooltip = tooltip + filename[0]
return tooltip
# Hide data
def create_hide_menu(self):
self.menuitem_hidedate = gtk.CheckMenuItem("Hide images with _Date", True)
self.menu_hide.append(self.menuitem_hidedate)
self.menuitem_hidedate.connect('toggled', self.on_menuitem_hide_toggled)
self.menuitem_hidegps = gtk.CheckMenuItem("Hide images with _GPS", True)
self.menu_hide.append(self.menuitem_hidegps)
self.menuitem_hidegps.connect('toggled', self.on_menuitem_hide_toggled)
self.menuitem_hideheadline = gtk.CheckMenuItem("Hide images with _Headline", True)
self.menu_hide.append(self.menuitem_hideheadline)
self.menuitem_hideheadline.connect('toggled', self.on_menuitem_hide_toggled)
for attribute in self.attributes:
menuitem = gtk.CheckMenuItem("Hide images with " + attribute[1], True)
self.hide_attributes[attribute[0]] = menuitem
self.menu_hide.append(menuitem)
menuitem.connect('toggled', self.on_menuitem_hide_toggled)
self.menuitem_hidelabels = gtk.CheckMenuItem("Hide images with L_abels", True)
self.menu_hide.append(self.menuitem_hidelabels)
self.menuitem_hidelabels.connect('toggled', self.on_menuitem_hide_toggled)
self.menu_hide.show_all()
def on_menuitem_hide_toggled(self, widget):
self.refresh_labels()
# Images
def create_iconview(self):
self.model_icon = gtk.ListStore(gobject.TYPE_STRING, gtk.gdk.Pixbuf, gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN) # Markup, Thumbnail, Filename tuple ([basename, full path]), Thumbnail is loaded, Tooltip, Visible
self.modelfilter_icon = self.model_icon.filter_new()
self.modelfilter_icon.set_visible_column(5)
self.iconview.set_model(self.modelfilter_icon)
self.iconview.set_markup_column(0)
self.iconview.set_pixbuf_column(1)
self.iconview.set_tooltip_column(4)
self.iconview.set_item_width(180)
self.iconview.grab_focus()
self.iconview.connect("selection_changed",self.on_iconview_selection_changed)
def get_selected_files(self):
filenames = [self.model_icon[self.modelfilter_icon.convert_path_to_child_path(path)][2][1] for path in self.iconview.get_selected_items()]
filenames.reverse()
return filenames
def get_all_files(self, fullnames = True):
if fullnames:
return [row[2][1] for row in self.modelfilter_icon]
else:
return [row[2][0] for row in self.modelfilter_icon]
def on_scrolledwindow_event(self, iconview, param):
self.load_visible_range()
def get_base_colour(self):
if len(self.iconview.get_selected_items()) > 1:
return gtk.gdk.color_parse("yellow")
return None
def on_iconview_selection_changed(self, iconview):
# Clear everything
self.block_edit = True
for attribute in [attr_tuple[0] for attr_tuple in self.attributes]:
self.attribute_entries[attribute].set_text("")
self.attribute_entries[attribute].modify_base(gtk.STATE_NORMAL, None)
for row in self.model_labels:
row[0] = False
row[2] = False
# Get the selected files and the first selected
filenames = self.get_selected_files()
if len(filenames) < 1:
self.last_filename = None
self.renderer_label.set_property('cell-background-gdk', None)
self.renderer_toggle.set_property('cell-background-gdk', None)
self.block_edit = False
return
if len(filenames) == 1:
self.last_filename = filenames[0]
if True and self.exile_preview: #follow selection will be an option
self.exile_preview.refresh(filenames[0])
if self.last_filename == None:
self.last_filename = filenames[0]
self.renderer_label.set_property('cell-background-gdk', self.get_base_colour())
self.renderer_toggle.set_property('cell-background-gdk', self.get_base_colour())
self.renderer_visibility.set_property('cell-background-gdk', self.get_base_colour())
# Set the entries
map = self.metadata.get_data(self.last_filename)
for attribute in [attr_tuple[0] for attr_tuple in self.attributes]:
self.attribute_entries[attribute].set_text(map[attribute])
self.attribute_entries[attribute].modify_base(gtk.STATE_NORMAL, self.get_base_colour())
self.label_gps_data.set_markup(self.gps.get_gps_text(self.metadata.get_data(self.last_filename), True))
self.set_labels_from_filenames(filenames)
self.block_edit = False
self.statusbar_set(False)
def set_labels_from_filenames(self, filenames):
# Set the labels
label_check_map = {}
for filename in filenames:
labels = self.metadata.get_labels(filename)
for label in labels:
if len(label):
if label not in label_check_map:
label_check_map[label] = 0
label_check_map[label] = label_check_map[label] + 1
rows = [row[1] for row in self.model_labels]
if label not in rows:
self.model_labels.append([True, label, True, gtk.STOCK_DIALOG_INFO, True])
else:
self.model_labels[rows.index(label)][0]=True
self.model_labels[rows.index(label)][2]=True
# Label inconsistency handling
rows = [row[1] for row in self.model_labels]
all_len = len(filenames)
for label in label_check_map.keys():
if label_check_map[label] == all_len:
self.model_labels[rows.index(label)][2]=False
else:
self.model_labels[rows.index(label)][0]=False
def on_iconview_images_item_activated(self, iconview, path):
filenames = self.get_selected_files()
for filename in filenames:
self.exile_preview = ExilePreview(self, filename)
# GPS
def set_clone_gps_icon(self):
pixbuf = gtk.gdk.pixbuf_new_from_file('clone_gps.png')
pixbuf = pixbuf.scale_simple(23, 23, gtk.gdk.INTERP_BILINEAR)
self.gladexml.get_widget("toolbutton_clonegps").set_icon_widget(gtk.image_new_from_pixbuf(pixbuf))
def create_label_gps_data(self, parent):
self.label_gps_data = gtk.Label();
self.label_gps_data.show()
parent.pack_start(self.label_gps_data, True, True)
self.label_gps_data.set_padding(12,0)
parent.show_all()
def create_buttons_gps_save(self, parent):
self.button_gps_save = gtk.Button();
image = gtk.Image()
image.set_from_stock(gtk.STOCK_SAVE, gtk.ICON_SIZE_BUTTON)
self.button_gps_save.add(image)
self.button_gps_save.show()
parent.pack_end(self.button_gps_save, True, False)
self.button_gps_save.connect("clicked", self.button_gps_save_clicked)
self.button_gps_load = gtk.Button();
image = gtk.Image()
image.set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_BUTTON)
self.button_gps_load.add(image)
self.button_gps_load.show()
parent.pack_end(self.button_gps_load, True, False)
self.button_gps_load.connect("clicked", self.button_gps_load_clicked)
parent.show_all()
def on_button_clonegps_clicked(self, button):
filenames = self.get_selected_files()
self.clone_gps_data(self.last_filename, filenames, self.gladexml.get_widget("entry_gpstimezone").get_text())
self.refresh_selected_captions()
def clone_gps_data(self, from_filename, filenames=[], timezone_text="") : #timezone_text=picture timezone #FIXME: refresh "meta" gps fields
map = self.metadata.get_data(from_filename)
timezonedelta = self.gps.get_timedelta(timezone_text)
image = self.metadata.get_image(from_filename)
for filename in filenames:
if not from_filename == filename:
self.__clone_gps_data_to_file(from_filename, filename, timezonedelta)
self.metadata.get_gps_data_all(filename)
def __clone_gps_data_to_file(self, from_filename, filename, timezonedelta):
for field in ExileFields.exif_gps_fields:
if field[1] == True:
self.metadata.clone_data(from_filename, filename, field[0])
if ExileFields.copy_date_time == ExileFields.DATETIME_EXIF:
self.__set_gps_datetime_from_exif_datetime(filename, timezonedelta)
def __set_gps_datetime_from_exif_datetime(self, filename, timezonedelta):
tomap = self.metadata.get_data(filename)
datetime = self.gps.get_datetime(tomap["Exif.Image.DateTime"])
filedatetime = datetime - timezonedelta
self.gps.to_gps_datetime_format(filedatetime.year, filedatetime.month, filedatetime.day)
self.gps.to_gps_datetime_format(filedatetime.hour, filedatetime.minute, filedatetime.second)
def paste_gps_clipboard(self, entry):
if gtk.clipboard_get().wait_is_text_available():
value = gtk.clipboard_get().wait_for_text()
split_data = value.split(",");
if len(split_data) > 1:
entry.stop_emission("paste-clipboard")
entry.set_text(split_data[0].strip())
self.gps_entries[1].set_text(split_data[1].strip())
if len(split_data) > 2:
self.gps_entries[2].set_text(split_data[2].strip())
return True
def button_gps_load_clicked(self, button):
metadata = self.metadata.get_data(self.last_filename)
if self.gps.has_gps_data(metadata):
return
(latitude, longitude, altitude) = self.gps_database.get_coordinates_from_location(metadata["Iptc.Application2.CountryName"],
metadata["Iptc.Application2.ProvinceState"],
metadata["Iptc.Application2.City"],
metadata["Iptc.Application2.SubLocation"])
if len(latitude): self.gps_entries[0].set_text(latitude)
if len(longitude): self.gps_entries[1].set_text(longitude)
if len(altitude): self.gps_entries[2].set_text(altitude)
def button_gps_save_clicked(self, button):
metadata = self.metadata.get_data(self.last_filename)
self.gps_database.set_coordinates_for_location(metadata["Iptc.Application2.CountryName"],
metadata["Iptc.Application2.ProvinceState"],
metadata["Iptc.Application2.City"],
metadata["Iptc.Application2.SubLocation"],
self.gps_entries[0].get_text(), self.gps_entries[1].get_text(), self.gps_entries[2].get_text())
self.gps_database.save()
# Attributes
def create_attribute(self, attribute, sg):
hbox = gtk.HBox(False, 6)
label = gtk.Label(attribute[1] + ":")
label.set_use_underline(True)
label.set_alignment(self.label_alignment, 0.5)
sg.add_widget(label)
entry = gtk.Entry()
self.attribute_entries[attribute[0]] = entry
entry.set_name(attribute[0])
entry.set_max_length(attribute[3])
label.set_mnemonic_widget(entry)
ec = gtk.EntryCompletion()
ec.set_model(gtk.ListStore(gobject.TYPE_STRING))
ec.set_text_column(0)
entry.set_completion(ec)
entry.connect("activate", self.on_entry_activated)
entry.connect("changed", self.on_entry_changed)
if self.attributes.index(attribute) != len(self.attributes) - 1:
entry.connect("activate", self.on_entry_activated_do_tab)
if attribute[0] == "Latitude": self.gps_entries[0] = entry
if attribute[0] == "Longitude": self.gps_entries[1] = entry
if attribute[0] == "Altitude": self.gps_entries[2] = entry
hbox.pack_start(label, False)
hbox.pack_start(entry)
hbox.show_all()
return hbox
def create_attributes(self):
sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
sg.add_widget(self.gladexml.get_widget("label_gps"))
self.box = self.gladexml.get_widget("vbox_attributes")
self.label_alignment = 0.0
for attribute in self.attributes:
if attribute[4] == "hc":
self.box = gtk.HBox(False, 12)
self.gladexml.get_widget("vbox_attributes").pack_start(self.box, False)
self.box.show_all()
self.label_alignment = 0.0
hbox = self.create_attribute(attribute, sg)
self.label_alignment = 1.0
else:
if attribute[4] == "vc":
self.box = self.gladexml.get_widget("vbox_attributes")
self.label_alignment = 0.0
hbox = self.create_attribute(attribute, sg)
self.box.pack_start(hbox, False)
if self.gps_entries[0] != None: self.gps_entries[0].connect("paste-clipboard", self.paste_gps_clipboard)
if self.gps_entries[2] != None:
self.create_label_gps_data(self.gps_entries[2].parent)
self.create_buttons_gps_save(self.gps_entries[2].parent)
def on_entry_activated(self, entry):
value = entry.get_text()
self.add_to_completion(entry, value)
filenames = self.get_selected_files()
if len(filenames) > 0:
entry.modify_base(gtk.STATE_NORMAL, self.get_base_colour())
for filename in filenames:
self.metadata.set_data(filename, entry.get_name(), value, self.is_instant_save())
self.refresh_selected_captions()
self.statusbar_set(False)
if entry.get_name() in ["Latitude", "Longitude", "Altitude"]:
self.label_gps_data.set_markup(self.gps.get_gps_text(self.metadata.get_data(filenames[-1]), True))
if entry.get_name() in ["Iptc.Application2.CountryName", "Iptc.Application2.ProvinceState", "Iptc.Application2.City", "Iptc.Application2.SubLocation"]:
self.button_gps_load.emit("clicked")
def on_entry_activated_do_tab(self, entry):
if self.gladexml.get_widget("menuitem_dotabonenter").get_active():
entry.get_toplevel().child_focus(gtk.DIR_TAB_FORWARD)
def on_entry_changed(self, entry):
if self.block_edit == False:
entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("red"))
self.statusbar_set(True)
# Completition
def add_to_completion(self, entry, value):
model = entry.get_completion().get_model()
if value not in [row[0] for row in model]:
model.append([value])
def save_completition_file(self):
if not self.gladexml.get_widget("menuitem_savecompletition").get_active():
return
completition_database = dict()
for attribute in [attr_tuple[0] for attr_tuple in self.attributes]:
model = self.attribute_entries[attribute].get_completion().get_model()
for row in model:
if len(row[0]) > 0 and not self.metadata.is_gps_meta_attribute(attribute):
if attribute in completition_database:
if row[0] in completition_database[attribute]:
continue
attrlist = completition_database[attribute] + [row[0], ]
else:
attrlist = [row[0], ]
completition_database[attribute] = attrlist
print completition_database
file = open("completition.dict", 'w')
file.write(str(completition_database))
def load_completition_file(self):
completition_database = dict()
if os.path.isfile("completition.dict"):
with open("completition.dict", 'r') as f:
s = f.read()
if s and len(s)>0:
completition_database = eval(s)
for attribute in completition_database.keys():
for value in completition_database[attribute]:
if attribute in self.attribute_entries:
self.add_to_completion(self.attribute_entries[attribute], value)
# Labels
def create_labels(self):
self.treeview_labels.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
self.model_labels = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN) # Toggled, Label name, Inconsistent, Visibility stock ID, Activatable
self.treeview_labels.set_model(self.model_labels)
self.renderer_toggle = gtk.CellRendererToggle()
self.renderer_toggle.connect( 'toggled', self.on_label_toggled)
self.treeview_labels.insert_column_with_attributes(-1, "", self.renderer_toggle, active=0, inconsistent=2, activatable=4)
image = gtk.Image()
image.set_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_MENU)
image.show()
self.renderer_visibility = gtk.CellRendererPixbuf()
self.column_visibility = self.treeview_labels.insert_column_with_attributes(-1, "", self.renderer_visibility, stock_id=3)
self.column_visibility.set_widget(image)
self.column_visibility.set_alignment(0.5)
self.column_visibility.set_clickable(True)
self.renderer_label = gtk.CellRendererText()
self.renderer_label.set_property('editable', True)
self.renderer_label.connect("edited", self.on_cell_edited)
self.column_label = self.treeview_labels.insert_column_with_attributes(-1, "Label", self.renderer_label, text=1)
self.column_label.set_expand(True)
self.treeview_labels.connect("button-press-event", self.on_button_press_event)
self.column_visibility.connect("clicked", self.on_column_visibility_clicked)
def model_labels_clear(self):
self.model_labels.clear()
iter = self.model_labels.append([False, self.nolabel, False, gtk.STOCK_DIALOG_INFO, False])
self.treeview_labels.get_selection().select_iter(iter)
def on_label_toggled(self, cell, path):
filenames = self.get_selected_files()
if len(filenames) == 0 or not self.model_labels[path][4]:
return
value = not self.model_labels[path][0]
label = self.model_labels[path][1]
self.model_labels[path][0] = value
self.model_labels[path][2] = False
#FIXME: len(filenames) > 1 eseten warning
for filename in filenames:
if value:
self.metadata.add_label(filename, label, self.is_instant_save())
else:
self.metadata.del_label(filename, label, self.is_instant_save())
self.refresh_selected_captions()
def on_cell_edited(self, cell, path, new_text):
self.model_labels[path][1] = new_text
def on_button_press_event(self, view, event):
coords = event.get_coords()
path, viewcol, cell_x, cell_y = view.get_path_at_pos(int(coords[0]), int(coords[1]))
if viewcol == self.column_visibility:
if event.button == 3:
# Show only the clicked one
for row in self.model_labels:
row[3] = None
self.model_labels[path][3] = gtk.STOCK_DIALOG_INFO
else:
# Toggle visibility
self.toggle_labels_on_paths([path])
self.refresh_labels()
def on_button_add_clicked(self, widget):
iter = self.model_labels.append([False, "", False, gtk.STOCK_DIALOG_INFO, True])
self.treeview_labels.get_selection().select_iter(iter)
self.treeview_labels.set_cursor(self.model_labels.get_path(iter), self.column_label, True)
def on_button_remove_clicked(self, widget): #fixme: esetleg az osszes keprol leszedni
model, rows = self.treeview_labels.get_selection().get_selected_rows()
rows.reverse()
for path in rows:
model.remove(model.get_iter(path))
def on_button_show_clicked(self, button):
model, paths = self.treeview_labels.get_selection().get_selected_rows()
self.toggle_labels_on_paths(paths)
self.refresh_labels()
def toggle_labels_on_paths(self, paths):
# Set the icons
for path in paths:
if self.model_labels[path][3] == None:
self.model_labels[path][3] = gtk.STOCK_DIALOG_INFO
else:
self.model_labels[path][3] = None
def refresh_labels(self):
# Get all visible labels
visible_labels = []
for row in self.model_labels:
if row[3] != None:
visible_labels.append(row[1])
hide_date = self.menuitem_hidedate.get_active()
hide_gps = self.menuitem_hidegps.get_active()
hide_headline = self.menuitem_hideheadline.get_active()
hide_labels = self.menuitem_hidelabels.get_active()
priorities = self.get_priorities_set(self.gladexml.get_widget("toolbutton_showhigh").get_active(),
self.gladexml.get_widget("toolbutton_showmedium").get_active(),
self.gladexml.get_widget("toolbutton_showlow").get_active(),
self.gladexml.get_widget("toolbutton_shownone").get_active())
# Refresh the iconview
for row in self.model_icon:
found = False
labels = self.metadata.get_labels(row[2][1])
visible_priority = self.get_priority_visibility(row[2][1], priorities)
if visible_priority and not self.is_hide_data(row[2][1], hide_date, hide_gps, hide_headline, hide_labels, labels):
if self.nolabel in visible_labels and self.has_valid_label(labels) == False: # images with no label or unknown labels
row[5] = True
found = True
else:
if self.gladexml.get_widget("menuitem_andlabelvisibility").get_active():
found = len(visible_labels) > 0
for label in visible_labels:
if label not in labels:
found = False
break
if found:
row[5] = True
else:
for label in labels:
if label in visible_labels:
row[5] = True
found = True
break
if found == False:
row[5] = False
self.load_visible_range()
self.statusbar_set()
def is_hide_data(self, filename, hide_date, hide_gps, hide_headline, hide_labels, labels):
if hide_labels and len(labels) > 0:
return True
map = self.metadata.get_data(filename)
if hide_date and "Exif.Image.DateTime" in map:
return True
if hide_gps and "Exif.GPSInfo.GPSLatitudeRef" in map:
return True
if hide_headline and "Iptc.Application2.Headline" in map:
return True
for attribute in self.attributes:
if self.hide_attributes[attribute[0]].get_active() and attribute[0] in map and len(map[attribute[0]]) > 0:
return True
return False
def has_valid_label(self, labels):
if len(labels) == 0 or len(labels[0]) == 0: # Has 0 labels
return False
all_labels = [row[1] for row in self.model_labels]
found = False
for label in labels: # Has only unknown
if label in all_labels:
found = True
break
return found
def on_toggle_label_activate(self, widget):
model, rows = self.treeview_labels.get_selection().get_selected_rows()
rows.reverse()
for path in rows:
self.renderer_toggle.emit("toggled", path[0])
def on_menuitem_andlabelvisibility_activate(self, widget):
self.refresh_labels()
if widget.get_active():
stock_icon = gtk.STOCK_ADD
else:
stock_icon = gtk.STOCK_INFO
self.column_visibility.get_widget().set_from_stock(stock_icon, self.column_visibility.get_widget().get_property("icon_size"))
def on_column_visibility_clicked(self, widget):
self.gladexml.get_widget("menuitem_andlabelvisibility").set_active(not self.gladexml.get_widget("menuitem_andlabelvisibility").get_active())
# Sorting
def create_stars(self):
self.pixbuf_high = gtk.gdk.pixbuf_new_from_file('star_full.png')
self.pixbuf_medium = gtk.gdk.pixbuf_new_from_file('star_half.png')
self.pixbuf_low = gtk.gdk.pixbuf_new_from_file('star_empty.png')
self.pixbuf_none = gtk.gdk.pixbuf_new_from_file('star_none.png')
self.pixbuf_high.saturate_and_pixelate(self.pixbuf_high, 15, False)
self.pixbuf_high=gtk.gdk.Pixbuf.add_alpha(self.pixbuf_high, 255, 255, 255, 255)
self.pixbuf_high=self.pixbuf_high.scale_simple(20, 20, gtk.gdk.INTERP_BILINEAR)
self.pixbuf_medium.saturate_and_pixelate(self.pixbuf_medium, 15, False)
self.pixbuf_medium=gtk.gdk.Pixbuf.add_alpha(self.pixbuf_medium, 255, 255, 255, 255)
self.pixbuf_medium=self.pixbuf_medium.scale_simple(20, 20, gtk.gdk.INTERP_BILINEAR)
self.pixbuf_low.saturate_and_pixelate(self.pixbuf_low, 15, False)
self.pixbuf_low=gtk.gdk.Pixbuf.add_alpha(self.pixbuf_low, 255, 255, 255, 255)
self.pixbuf_low=self.pixbuf_low.scale_simple(20, 20, gtk.gdk.INTERP_BILINEAR)
self.pixbuf_none=gtk.gdk.Pixbuf.add_alpha(self.pixbuf_none, 255, 255, 255, 255)
self.pixbuf_none=self.pixbuf_none.scale_simple(20, 20, gtk.gdk.INTERP_BILINEAR)
self.gladexml.get_widget("toggle_high").set_image(gtk.image_new_from_pixbuf(self.pixbuf_high))
self.gladexml.get_widget("toggle_medium").set_image(gtk.image_new_from_pixbuf(self.pixbuf_medium))
self.gladexml.get_widget("toggle_low").set_image(gtk.image_new_from_pixbuf(self.pixbuf_low))
def mark_image(self, pixbuf, priority):
if self.show_stars and priority:
if priority == "High": pixbuf_star = self.pixbuf_high
elif priority == "Medium": pixbuf_star = self.pixbuf_medium
elif priority == "Low": pixbuf_star = self.pixbuf_low
else:
return pixbuf
w,h = pixbuf.get_width(), pixbuf.get_height()
pixmap,mask = pixbuf.render_pixmap_and_mask() # Function call
cm = pixmap.get_colormap()
gc = pixmap.new_gc()
pixmap.draw_pixbuf(gc, pixbuf_star, 0, 0, 0, 0)
pixbuf.get_from_drawable(pixmap,cm,0,0,0,0,w,h)
return pixbuf
def toggle_priority(self, keyval):
if keyval in ExileFields.special_instructions:
iters = [self.modelfilter_icon.convert_path_to_child_path(path) for path in self.iconview.get_selected_items()]
for iter in iters:
self.metadata.set_priority(self.model_icon[iter][2][1], keyval, self.is_instant_save())
self.load_thumb(iter, True)
#self.model_icon[iter][1] = self.mark_image(self.model_icon[iter][1], ExileFields.special_instructions[keyval]);
if self.gladexml.get_widget("menuitem_donextonstar").get_active():
self.on_menuitem_next_activate(None)
def on_window_main_key_press_event(self, widget, event):
if self.iconview.is_focus() and not event.state & gtk.gdk.CONTROL_MASK:
self.toggle_priority(event.keyval)
def on_toggle_high_activate(self, widget):
self.toggle_priority(49)
def on_toggle_medium_activate(self, widget):
self.toggle_priority(50)
def on_toggle_low_activate(self, widget):
self.toggle_priority(51)
def on_toggle_unset_activate(self, widget):
self.toggle_priority(52)
def on_menuitem_stars_activate(self, widget):
self.show_stars = widget.get_active()
for row in self.model_icon:
self.load_thumb(row.iter, True)
def on_toolbutton_show_priority_activate(self,widget):
self.refresh_labels()
def get_priorities_set(self, is_high, is_medium, is_low, is_none):
priorities = []
if is_high: priorities = priorities + ["High"]
if is_medium: priorities = priorities + ["Medium"]
if is_low: priorities = priorities + ["Low"]
if is_none: priorities = priorities + ["None"]
return priorities;
def get_priority_visibility(self, filename, priorities):
priority = self.metadata.get_priority(filename)
if (priority == None or len(priority) == 0):
priority = "None"
return priority in priorities
def set_priority_toolbuttons(self):
image = gtk.image_new_from_pixbuf(self.pixbuf_high)
self.gladexml.get_widget("toolbutton_showhigh").set_icon_widget(image)
image = gtk.image_new_from_pixbuf(self.pixbuf_medium)
self.gladexml.get_widget("toolbutton_showmedium").set_icon_widget(image)
image = gtk.image_new_from_pixbuf(self.pixbuf_low)
self.gladexml.get_widget("toolbutton_showlow").set_icon_widget(image)
image = gtk.image_new_from_pixbuf(self.pixbuf_none)
self.gladexml.get_widget("toolbutton_shownone").set_icon_widget(image)
self.gladexml.get_widget("toolbar").show_all()
# Navigation
def select_path(self, path):
self.iconview.select_path(path)
self.iconview.scroll_to_path(path, 0, 0, 0)
self.iconview.set_cursor(path)
def on_menuitem_next_activate(self, menuitem):
paths = self.iconview.get_selected_items()
if len(paths) == 1:
nextiter = self.model_icon.iter_next(self.model_icon.get_iter(paths[0]))
if nextiter:
self.iconview.unselect_all()
path = self.model_icon.get_path(nextiter)
self.select_path(path)
def on_menuitem_prev_activate(self, menuitem):
paths = self.iconview.get_selected_items()
if len(paths) == 1:
if paths[0][0] > 0:
self.iconview.unselect_all()
path = (paths[0][0]-1,)
self.select_path(path)
def on_menuitem_up_activate(self, menuitem):
paths = self.iconview.get_selected_items()
if len(paths) == 1:
cols = self.get_columns()
if paths[0][0] - cols >= 0:
path = (paths[0][0] - cols,)
self.iconview.unselect_all()
self.select_path(path)
def on_menuitem_down_activate(self, menuitem):
paths = self.iconview.get_selected_items()
if len(paths) == 1:
cols = self.get_columns()
if paths[0][0] + cols < len(self.model_icon):
path = (paths[0][0] + cols,)
self.iconview.unselect_all()
self.select_path(path)
def get_columns(self):
return self.iconview.size_request()[0] / max([self.iconview.get_cells()[0].get_size(self.iconview)[2], self.iconview.get_cells()[1].get_size(self.iconview)[2]])
# Statusbar
def statusbar_set(self, editing=False):
status = str(len(self.modelfilter_icon)) + " images (" + str(len(self.iconview.get_selected_items())) + " selected)" + " in " + self.dirname
if editing == True:
status = "Press enter to apply modification!\t" + status
self.statusbar.push(self.context_id, status)
gtk.main_iteration()
# Idle
def check_idle_remove(self): #check if all thumbs are loaded
for row in self.model_icon:
if row[3] == False:
return False
return True
def idle_load(self):
self.load_thumb(self.model_icon.get_iter((self.first_unloaded,)))
self.first_unloaded = self.first_unloaded + 1
if self.first_unloaded >= len(self.model_icon):
self.first_unloaded = 0
if self.check_idle_remove() == True:
self.idle_id = -1
return False
return True
#TODO:
def on_treeview_drag_data_received(self, treeview, context, x, y, selection, target_type, timestamp):
if target_type == TARGET_TYPE_URI_LIST:
uri = selection.data.strip()
uri_splitted = uri.split() # we may have more than one file dropped
for uri in uri_splitted:
if self.load_file_from_uri(uri):
break #FIXME: multiple file handling (merge)
treeview.emit_stop_by_name("drag_data_received")
def on_treeview_drag_drop(self, treeview, context, x, y, timestamp):
pass
#~ treeview.emit_stop_by_name("drag_drop")
#~ return True
# Compatibility
def convert_old_keywords(self, filename):
image = pyexiv2.ImageMetadata(filename)
image.read()
if "Iptc.Application2.Keywords" in image.iptc_keys:
print "---"
print filename + ":"
print image["Iptc.Application2.Keywords"].value
keywords_to_delete = []
for keyword in image["Iptc.Application2.Keywords"].value:
if len(keyword) > 6 and keyword[0:7] == 'people:': #compatibility mode
keywords_to_delete = keywords_to_delete + [keyword]
if len(keyword) > 7:
self.metadata.set_data(filename, 'Xmp.iptcExt.PersonInImage', keyword[7:])
elif len(keyword) > 5 and keyword[0:6] == 'event:': #compatibility mode
keywords_to_delete = keywords_to_delete + [keyword]
if len(keyword) > 6:
self.metadata.set_data(filename, 'Xmp.iptcExt.Event', keyword[6:])
elif len(keyword) > 5 and keyword[0:6] == 'label:': #compatibility mode
keywords_to_delete = keywords_to_delete + [keyword]
if len(keyword) > 6:
self.metadata.add_label(filename, keyword[6:])
image = pyexiv2.ImageMetadata(filename)
image.read()
print image["Iptc.Application2.Keywords"].value
for keyword in keywords_to_delete:
if keyword in image["Iptc.Application2.Keywords"].value:
image["Iptc.Application2.Keywords"].value.remove(keyword)
image.write()
def print_dbg(str):
if DEBUG:
print str
def create_logviewer_window(dirname, recursive=False, convert_old_keywords_param=False):
windows.append(ExileWindow(dirname, recursive, convert_old_keywords_param))
def main():
gtk.gdk.threads_init()
gtk.gdk.threads_enter()
gtk.main()
gtk.gdk.threads_leave()
if __name__ == "__main__":
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) # hack for the .glade files
dirname = "."
convert_old_keywords_param = False
try:
opts, args = getopt.getopt(sys.argv[1:], "cd:", ["convert_old_keywords", "directory"])
except getopt.GetoptError:
sys.exit(2)
for opt, arg in opts:
if opt in ("-c", "--convert_old_keywords"):
convert_old_keywords_param = True
elif opt in ("-d", "--directory"):
dirname = arg
create_logviewer_window(dirname, False, convert_old_keywords_param)
main()