1483 lines
53 KiB
Python
1483 lines
53 KiB
Python
# -*- coding: utf-8 -*-
|
||
#!/usr/bin/python
|
||
# -*- coding: utf-8 -*-
|
||
#
|
||
# Gramps - a GTK+/GNOME based genealogy program
|
||
#
|
||
# Copyright (C) 2009-2011 Rob G. Healey <robhealey1@gmail.com>
|
||
#
|
||
# 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; either version 2 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, write to the Free Software
|
||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
||
# $Id$
|
||
|
||
# *****************************************************************************
|
||
# Python Modules
|
||
# *****************************************************************************
|
||
import os
|
||
from datetime import datetime, date
|
||
import calendar, time
|
||
|
||
|
||
# abilty to escape certain characters from output...
|
||
from xml.sax.saxutils import escape as _html_escape
|
||
|
||
from itertools import chain
|
||
|
||
from decimal import Decimal, getcontext
|
||
getcontext().prec = 4
|
||
from fractions import Fraction
|
||
|
||
import subprocess
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# GTK modules
|
||
# -----------------------------------------------------------------------------
|
||
import gtk
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# GRAMPS modules
|
||
# -----------------------------------------------------------------------------
|
||
# -----------------------------------------------------------------------------
|
||
# GRAMPS modules
|
||
# -----------------------------------------------------------------------------
|
||
import GrampsDisplay
|
||
|
||
from gen.plug import Gramplet
|
||
from DateHandler import displayer as _dd
|
||
|
||
import gen.lib
|
||
import gen.mime
|
||
import Utils
|
||
from PlaceUtils import conv_lat_lon
|
||
|
||
from QuestionDialog import WarningDialog, QuestionDialog
|
||
|
||
from gen.ggettext import gettext as _
|
||
|
||
#####################################################################
|
||
# Check for pyexiv2 library...
|
||
#####################################################################
|
||
# pyexiv2 download page (C) Olivier Tilloy
|
||
_DOWNLOAD_LINK = "http://tilloy.net/dev/pyexiv2/download.html"
|
||
|
||
# make sure the pyexiv2 library is installed and at least a minimum version
|
||
software_version = False
|
||
Min_VERSION = (0, 1, 3)
|
||
Min_VERSION_str = "pyexiv2-%d.%d.%d" % Min_VERSION
|
||
Pref_VERSION_str = "pyexiv2-%d.%d.%d" % (0, 3, 0)
|
||
|
||
# to be able for people that have pyexiv2-0.1.3 to be able to use this addon also...
|
||
LesserVersion = False
|
||
|
||
try:
|
||
import pyexiv2
|
||
software_version = pyexiv2.version_info
|
||
|
||
except ImportError, msg:
|
||
WarningDialog(_("You need to install, %s or greater, for this addon to work...\n"
|
||
"I would recommend installing, %s, and it may be downloaded from here: \n%s") % (
|
||
Min_VERSION_str, Pref_VERSION_str, _DOWNLOAD_LINK), str(msg))
|
||
raise Exception(_("Failed to load 'Edit Image Exif Metadata'..."))
|
||
|
||
# This only happends if the user has pyexiv2-0.1.3 installed on their computer...
|
||
except AttributeError:
|
||
LesserVersion = True
|
||
|
||
# the library is either not installed or does not meet minimum required version for this addon....
|
||
if (software_version and (software_version < Min_VERSION)):
|
||
msg = _("The minimum required version for pyexiv2 must be %s \n"
|
||
"or greater. Or you do not have the python library installed yet. "
|
||
"You may download it from here: %s\n\n I recommend getting, %s") % (
|
||
Min_VERSION_str, _DOWNLOAD_LINK, Pref_VERSION_str)
|
||
WarningDialog(msg)
|
||
raise Exception(msg)
|
||
|
||
# *******************************************************************
|
||
# Determine if we have access to outside Programs
|
||
#
|
||
# The programs are ImageMagick, and jhead
|
||
# * ImageMagick -- Convert and Delete all Exif metadata...
|
||
# * jhead -- re-initialize a jpeg, and other features...
|
||
#********************************************************************
|
||
# Windows 32bit systems
|
||
system_platform = os.sys.platform
|
||
if system_platform == "win32":
|
||
_MAGICK_FOUND = "convert.exe" if Utils.search_for("convert.exe") else False
|
||
_JHEAD_FOUND = "jhead.exe" if Utils.search_for("jhead.exe") else False
|
||
|
||
elif system_platform == "linux2":
|
||
_MAGICK_FOUND = "convert" if Utils.search_for("convert") else False
|
||
_JHEAD_FOUND = "jhead" if Utils.search_for("jhead") else False
|
||
|
||
else:
|
||
_MAGICK_FOUND = "convert" if Utils.search_for("convert") else False
|
||
_JHEAD_FOUND = "jhead" if Utils.search_for("jhead") else False
|
||
|
||
# if external programs are not found, let the user know about the missing functionality?
|
||
if not _MAGICK_FOUND:
|
||
print(_("ImageMagick's convert program was not found on this computer.\n"
|
||
"You may download it from here: %s...") % (
|
||
"http://www.imagemagick.org/script/index.php"))
|
||
|
||
if not _JHEAD_FOUND:
|
||
print(_("Jhead program was not found on this computer.\n"
|
||
"You may download it from: %s...") % (
|
||
"http://www.sentex.net/~mwandel/jhead/"))
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Constants
|
||
# -----------------------------------------------------------------------------
|
||
# available image types for exiv2 and pyexiv2
|
||
# ["jpeg", "jpg", "exv", "tiff", "dng", "nef", "pef", "pgf", "png", "psd", "jp2"]
|
||
|
||
# define tooltips for all entries
|
||
_TOOLTIPS = {
|
||
|
||
# Description...
|
||
"Description" : _("Provide a short descripion for this image."),
|
||
|
||
# Artist
|
||
"Artist" : _("Enter the Artist/ Author of this image. The person's name or "
|
||
"the company who is responsible for the creation of this image."),
|
||
|
||
# Copyright
|
||
"Copyright" : _("Enter the copyright information for this image. \n"
|
||
"Example: (C) 2010 Smith and Wesson"),
|
||
|
||
# Calendar date select...
|
||
"Date:Select" : _("Allows you to select a date from a pop-up window calendar. \n"
|
||
"Warning: You will still need to edit the time..."),
|
||
|
||
# Original Date/ Time...
|
||
"DateTime" : _("Original Date/ Time of this image.\n"
|
||
"Example: 1826-Apr-12 14:30:00, 1826-April-12, 1998-01-31 13:30:00"),
|
||
|
||
# Convert to decimal button...
|
||
"GPSFormat:Decimal" : _("Converts Degree, Minutes, Seconds GPS coordinates to a "
|
||
"Decimal representation."),
|
||
|
||
# convert to degrees, minutes, seconds button...
|
||
"GPSFormat:DMS" : _("Converts Decimal GPS coordinates "
|
||
"to a Degrees, Minutes, Seconds representation."),
|
||
|
||
# GPS Latitude...
|
||
"Latitude" : _(u"Enter the GPS Latitude coordinates for your image,\n"
|
||
u"Example: 43.722965, 43 43 22 N, 38° 38′ 03″ N, 38 38 3"),
|
||
|
||
# GPS Longitude...
|
||
"Longitude" : _(u"Enter the GPS Longitude coordinates for your image,\n"
|
||
u"Example: 10.396378, 10 23 46 E, 105° 6′ 6″ W, -105 6 6") }.items()
|
||
|
||
# set up Exif keys for Image.exif_keys
|
||
_DATAMAP = {
|
||
"Exif.Image.ImageDescription" : "Description",
|
||
"Exif.Image.DateTime" : "Modified",
|
||
"Exif.Image.Artist" : "Artist",
|
||
"Exif.Image.Copyright" : "Copyright",
|
||
"Exif.Photo.DateTimeOriginal" : "DateTime",
|
||
"Exif.GPSInfo.GPSLatitudeRef" : "LatitudeRef",
|
||
"Exif.GPSInfo.GPSLatitude" : "Latitude",
|
||
"Exif.GPSInfo.GPSLongitudeRef" : "LongitudeRef",
|
||
"Exif.GPSInfo.GPSLongitude" : "Longitude"}
|
||
_DATAMAP = dict( (key, val) for key, val in _DATAMAP.items() )
|
||
_DATAMAP.update( (val, key) for key, val in _DATAMAP.items() )
|
||
|
||
# Toolt tips for the buttons in the gramplet...
|
||
_BUTTONTIPS = {
|
||
|
||
# CopyTo button...
|
||
"CopyTo" : _("Copies information from the Display area to the Edit area."),
|
||
|
||
# Clear Edit Area button...
|
||
"Clear" : _("Clears the Exif metadata from the Edit area."),
|
||
|
||
# Wiki Help button...
|
||
"Help" : _("Displays the Gramps Wiki Help page for 'Edit Image Exif Metadata' "
|
||
"in your web browser."),
|
||
|
||
# Save Exif Metadata button...
|
||
"Save" : _("Saves/ writes the Exif metadata to this image.\n"
|
||
"WARNING: Exif metadata will be erased if you save a blank entry field...") }
|
||
|
||
# if ImageMagick is installed on this computer then, add button tooltips for these two buttons...
|
||
if _MAGICK_FOUND:
|
||
_BUTTONTIPS.update( {
|
||
|
||
# Convert to .Jpeg button...
|
||
"Convert" : _("If your image is not a jpeg image, convert it to jpeg?"),
|
||
|
||
# Delete/ Erase/ Wipe Exif metadata button...
|
||
"Delete" : _("WARNING: This will completely erase all Exif metadata "
|
||
"from this image! Are you sure that you want to do this?") } )
|
||
|
||
def _help_page(obj):
|
||
"""
|
||
will bring up a Wiki help page.
|
||
"""
|
||
|
||
GrampsDisplay.help(webpage = "Edit Image Exif Metadata")
|
||
|
||
_allmonths = list([_dd.short_months[i], _dd.long_months[i], i] for i in range(1, 13))
|
||
|
||
def _return_month(month):
|
||
"""
|
||
returns either an integer of the month number or the abbreviated month name
|
||
|
||
@param: rmonth -- can be one of:
|
||
10, "10", "Oct", or "October"
|
||
"""
|
||
|
||
try:
|
||
month = int(month)
|
||
|
||
except ValueError:
|
||
for sm, lm, index in _allmonths:
|
||
if month == sm or month == lm:
|
||
month = int(index)
|
||
break
|
||
elif str(month) == index:
|
||
month = lm
|
||
break
|
||
return month
|
||
|
||
# ------------------------------------------------------------------------
|
||
# Gramplet class
|
||
# ------------------------------------------------------------------------
|
||
class EditExifMetadata(Gramplet):
|
||
|
||
def init(self):
|
||
|
||
self.exif_column_width = 12
|
||
self.exif_widgets = {}
|
||
|
||
# set all dirty variables to False to begin this addon...
|
||
self._dirty = False
|
||
|
||
self.orig_image = False
|
||
self.image_path = False
|
||
self.plugin_image = False
|
||
|
||
self.connect_signal("Media", self.update)
|
||
vbox = self.__build_gui()
|
||
|
||
self.gui.get_container_widget().remove(self.gui.textview)
|
||
self.gui.get_container_widget().add_with_viewport(vbox)
|
||
vbox.show_all()
|
||
|
||
# provide tooltips for all fields and buttons...
|
||
_setup_widget_tooltips(self.exif_widgets)
|
||
|
||
def __build_gui(self):
|
||
|
||
vbox = gtk.VBox()
|
||
|
||
medialabel = gtk.HBox(False)
|
||
self.exif_widgets["Media:Label"] = gtk.Label()
|
||
self.exif_widgets["Media:Label"].set_alignment(0.0, 0.0)
|
||
medialabel.pack_start(self.exif_widgets["Media:Label"], expand =False)
|
||
vbox.pack_start(medialabel, expand =False)
|
||
|
||
mimetype = gtk.HBox(False)
|
||
self.exif_widgets["Mime:Type"] = gtk.Label()
|
||
self.exif_widgets["Mime:Type"].set_alignment(0.0, 0.0)
|
||
mimetype.pack_start(self.exif_widgets["Mime:Type"], expand =False)
|
||
vbox.pack_start(mimetype, expand =False)
|
||
|
||
messagearea = gtk.HBox(False)
|
||
self.exif_widgets["Message:Area"] = gtk.Label(_("Click an image to begin..."))
|
||
self.exif_widgets["Message:Area"].set_alignment(0.5, 0.0)
|
||
messagearea.pack_start(self.exif_widgets["Message:Area"], expand =False)
|
||
vbox.pack_start(messagearea, expand =False)
|
||
|
||
self.model = gtk.ListStore(object, str, str)
|
||
view = gtk.TreeView(self.model)
|
||
|
||
# Key Column
|
||
view.append_column( self.__create_column(_("Key"), 1) )
|
||
|
||
# Value Column
|
||
view.append_column( self.__create_column(_("Value"), 2) )
|
||
|
||
# CopyTo, Clear, Convert horizontal box
|
||
ccc_box = gtk.HButtonBox()
|
||
ccc_box.set_layout(gtk.BUTTONBOX_START)
|
||
vbox.pack_start(ccc_box, expand =False, fill =False, padding =10)
|
||
|
||
# Copy To Edit Area button...
|
||
ccc_box.add( self.__create_button(
|
||
"CopyTo", False, [self.CopyTo], gtk.STOCK_COPY, False) )
|
||
|
||
# Clear button...
|
||
ccc_box.add( self.__create_button(
|
||
"Clear", False, [self.clear_metadata], gtk.STOCK_CLEAR, False) )
|
||
|
||
# is ImageMagick installed?
|
||
if _MAGICK_FOUND:
|
||
# Convert button...
|
||
ccc_box.add( self.__create_button(
|
||
"Convert", False, [self.__convert_dialog], gtk.STOCK_CONVERT, False) )
|
||
|
||
for items in [
|
||
|
||
# Image Description
|
||
("Description", _("Description"), None, False, [], True, 0),
|
||
|
||
# Last Modified Date/ Time
|
||
("Modified", _("Last Changed"), None, True, [], True, 0),
|
||
|
||
# Artist field
|
||
("Artist", _("Artist"), None, False, [], True, 0),
|
||
|
||
# copyright field
|
||
("Copyright", _("Copyright"), None, False, [], True, 0),
|
||
|
||
# calendar date clickable entry
|
||
("Date", "", None, True,
|
||
[("Select", _("Select Date"), "button", self.select_date)],
|
||
True, 0),
|
||
# Original Date/ Time Entry, 1826-April-12 14:06:00
|
||
("DateTime", _("Date/ Time"), None, False, [], True, 0),
|
||
|
||
# Convert GPS coordinates
|
||
("GPSFormat", _("Convert GPS"), None, True,
|
||
[("Decimal", _("Decimal"), "button", self.convert2decimal),
|
||
("DMS", _("Deg. Min. Sec."), "button", self.convert2dms)],
|
||
False, 0),
|
||
|
||
# Latitude and Longitude for this image
|
||
("Latitude", _("Latitude"), None, False, [], True, 0),
|
||
("Longitude", _("Longitude"), None, False, [], True, 0) ]:
|
||
|
||
pos, text, choices, readonly, callback, dirty, default = items
|
||
row = self.make_row(pos, text, choices, readonly, callback, dirty, default)
|
||
vbox.pack_start(row, False)
|
||
|
||
# Help, Save, Delete horizontal box
|
||
hsd_box = gtk.HButtonBox()
|
||
hsd_box.set_layout(gtk.BUTTONBOX_START)
|
||
vbox.pack_start(hsd_box, expand =False, fill =False, padding =10)
|
||
|
||
# Help button...
|
||
hsd_box.add( self.__create_button(
|
||
"Help", False, [_help_page], gtk.STOCK_HELP) )
|
||
|
||
# Save button...
|
||
hsd_box.add( self.__create_button(
|
||
"Save", False, [self.save_metadata, self.update, self.CopyTo, self.display_exif_tags],
|
||
gtk.STOCK_SAVE, False) )
|
||
|
||
if _MAGICK_FOUND:
|
||
# Delete All Metadata button...
|
||
hsd_box.add(self.__create_button(
|
||
"Delete", False, [self.__delete_dialog], gtk.STOCK_DELETE, False))
|
||
|
||
# adds Exif Metadata Viewing Area
|
||
vbox.pack_start(view, padding =10)
|
||
|
||
return vbox
|
||
|
||
def update_has_data(self):
|
||
active_handle = self.get_active('Media')
|
||
active = self.dbstate.db.get_object_from_handle(active_handle)
|
||
self.set_has_data(self.get_has_data(active))
|
||
|
||
def get_has_data(self, media):
|
||
"""
|
||
Return True if the gramplet has data, else return False.
|
||
"""
|
||
if media is None:
|
||
return False
|
||
|
||
full_path = Utils.media_path_full(self.dbstate.db, media.get_path() )
|
||
if not os.path.isfile(full_path):
|
||
return False
|
||
|
||
if LesserVersion: # prior to v0.2.0
|
||
metadata = pyexiv2.Image(full_path)
|
||
try:
|
||
metadata.readMetadata()
|
||
except (IOError, OSError):
|
||
return False
|
||
|
||
if metadata.exifKeys():
|
||
return True
|
||
|
||
else: # v0.2.0 and above
|
||
metadata = pyexiv2.ImageMetadata(full_path)
|
||
try:
|
||
metadata.read()
|
||
except (IOError, OSError):
|
||
return False
|
||
|
||
if metadata.exif_keys:
|
||
return True
|
||
|
||
return False
|
||
|
||
def activate_buttons(self, ButtonList):
|
||
"""
|
||
Enable/ activate the buttons that are in ButtonList
|
||
"""
|
||
|
||
for ButtonName in ButtonList:
|
||
self.exif_widgets[ButtonName].set_sensitive(True)
|
||
|
||
def deactivate_buttons(self, ButtonList):
|
||
"""
|
||
disable/ de-activate buttons in ButtonList
|
||
"""
|
||
for ButtonName in ButtonList:
|
||
self.exif_widgets[ButtonName].set_sensitive(False)
|
||
|
||
def main(self): # return false finishes
|
||
"""
|
||
get the active media, mime type, and reads the image metadata
|
||
"""
|
||
db = self.dbstate.db
|
||
|
||
# clear Display and Edit Areas
|
||
self.clear_metadata(self.orig_image)
|
||
self.model.clear()
|
||
|
||
# De-activate the buttons except for Help...
|
||
self.deactivate_buttons(["CopyTo", "Clear", "Save"])
|
||
|
||
if _MAGICK_FOUND:
|
||
self.deactivate_buttons(["Convert", "Delete"])
|
||
|
||
# Re-post initial image message...
|
||
self.exif_widgets["Message:Area"].set_text(_("Select an image to begin..."))
|
||
|
||
active_handle = self.get_active("Media")
|
||
if not active_handle:
|
||
self.set_has_data(False)
|
||
return
|
||
|
||
self.orig_image = db.get_object_from_handle(active_handle)
|
||
self.image_path = Utils.media_path_full(db, self.orig_image.get_path() )
|
||
if (not self.orig_image or not os.path.isfile(self.image_path)):
|
||
self.exif_widgets["Message:Area"].set_text(_("Image is either missing or deleted,\n"
|
||
"Choose a different image..."))
|
||
return
|
||
|
||
# check image read privileges...
|
||
_readable = os.access(self.image_path, os.R_OK)
|
||
if not _readable:
|
||
self.exif_widgets["Message:Area"].set_text(_("Image is NOT readable,\n"
|
||
"Choose a different image..."))
|
||
return
|
||
|
||
# check image write privileges...
|
||
_writable = os.access(self.image_path, os.W_OK)
|
||
if not _writable:
|
||
self.exif_widgets["Message:Area"].set_text(_("Image is NOT writable,\n"
|
||
"You will NOT be able to save Exif metadata...."))
|
||
|
||
# Mime type information...
|
||
mime_type = self.orig_image.get_mime_type()
|
||
_mtype = gen.mime.get_description(mime_type)
|
||
self.exif_widgets["Mime:Type"].set_text(_mtype)
|
||
|
||
# determine if it is a mime image object?
|
||
if mime_type:
|
||
if mime_type.startswith("image"):
|
||
self.activate_buttons(["Save"])
|
||
|
||
# display file description/ title...
|
||
self.exif_widgets["Media:Label"].set_text( _html_escape(
|
||
self.orig_image.get_description()))
|
||
|
||
# will create the image and read it...
|
||
self.setup_image(self.image_path, True)
|
||
|
||
# Checks to make sure that ImageMagick is installed on this computer and
|
||
# the image is NOT a jpeg image...
|
||
if _MAGICK_FOUND:
|
||
basename, extension = os.path.splitext(self.image_path)
|
||
if extension not in [".jpeg", ".jpg", ".jfif"]:
|
||
self.activate_buttons(["Convert"])
|
||
|
||
# displays the imge Exif metadata
|
||
self.display_exif_tags()
|
||
|
||
else:
|
||
self.exif_widgets["Message:Area"].set_text(_("Choose a different image..."))
|
||
return
|
||
|
||
else:
|
||
self.exif_widgets["Message:Area"].set_text(_("Choose a different image..."))
|
||
return
|
||
|
||
def __create_column(self, name, colnum, fixed =True):
|
||
"""
|
||
will create the column for the column row...
|
||
"""
|
||
|
||
renderer = gtk.CellRendererText()
|
||
column = gtk.TreeViewColumn(name, renderer, text =colnum)
|
||
|
||
if fixed:
|
||
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
|
||
column.set_expand(True)
|
||
|
||
else:
|
||
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
|
||
column.set_expand(False)
|
||
|
||
column.set_alignment(0.0)
|
||
column.set_sort_column_id(colnum)
|
||
|
||
return column
|
||
|
||
def __create_button(self, pos, text, callback =False , icon =False, sensitive = True):
|
||
"""
|
||
creates and returns a button for display
|
||
"""
|
||
|
||
if (icon and not text):
|
||
button = gtk.Button(stock=icon)
|
||
else:
|
||
button = gtk.Button(text)
|
||
|
||
if callback is not []:
|
||
for call_ in callback:
|
||
button.connect("clicked", call_)
|
||
|
||
if not sensitive:
|
||
button.set_sensitive(False)
|
||
self.exif_widgets[pos] = button
|
||
|
||
return button
|
||
|
||
def __convert_dialog(self, obj):
|
||
"""
|
||
Handles the Convert question Dialog...
|
||
"""
|
||
|
||
# is ImageMagick installled?
|
||
if _MAGICK_FOUND:
|
||
QuestionDialog(_("Edit Image Exif Metadata"), _("Convert this image to a .jpeg image?"),
|
||
_("Convert"), self.convert2Jpeg)
|
||
|
||
def __delete_dialog(self, obj):
|
||
"""
|
||
Handles the Delete Dialog...
|
||
"""
|
||
|
||
QuestionDialog(_("Edit Image Exif Metadata"), _("WARNING! You are about to completely "
|
||
"delete the Exif metadata from this image?"), _("Delete"),
|
||
self.strip_metadata)
|
||
|
||
def setup_image(self, full_path, createimage =False):
|
||
"""
|
||
will return an image instance and read the Exif metadata.
|
||
|
||
if createimage is True, it will create the pyexiv2 image instance...
|
||
|
||
LesserVersion -- prior to pyexiv2-0.2.0
|
||
-- pyexiv2-0.2.0 and above...
|
||
"""
|
||
|
||
if createimage:
|
||
if LesserVersion:
|
||
self.plugin_image = pyexiv2.Image(full_path)
|
||
else:
|
||
self.plugin_image = pyexiv2.ImageMetadata(full_path)
|
||
|
||
if LesserVersion:
|
||
try:
|
||
self.plugin_image.readMetadata()
|
||
except (IOError, OSError):
|
||
self.set_has_data(False)
|
||
return
|
||
|
||
else:
|
||
try:
|
||
self.plugin_image.read()
|
||
except (IOError, OSError):
|
||
self.set_has_data(False)
|
||
return
|
||
|
||
def make_row(self, pos, text, choices=None, readonly=False, callback_list =[],
|
||
mark_dirty=False, default=0):
|
||
|
||
# Edit Image Exif Metadata
|
||
row = gtk.HBox()
|
||
label = gtk.Label()
|
||
if readonly:
|
||
label.set_text("<b>%s</b>" % text)
|
||
label.set_width_chars(self.exif_column_width)
|
||
label.set_use_markup(True)
|
||
self.exif_widgets[pos] = gtk.Label()
|
||
self.exif_widgets[pos].set_alignment(0.0, 0.5)
|
||
self.exif_widgets[pos].set_use_markup(True)
|
||
label.set_alignment(0.0, 0.5)
|
||
row.pack_start(label, False)
|
||
row.pack_start(self.exif_widgets[pos], False)
|
||
else:
|
||
label.set_text("%s: " % text)
|
||
label.set_width_chars(self.exif_column_width)
|
||
label.set_alignment(1.0, 0.5)
|
||
if choices == None:
|
||
self.exif_widgets[pos] = gtk.Entry()
|
||
if mark_dirty:
|
||
self.exif_widgets[pos].connect("changed", self._mark_dirty)
|
||
row.pack_start(label, False)
|
||
row.pack_start(self.exif_widgets[pos], True)
|
||
else:
|
||
eventBox = gtk.EventBox()
|
||
self.exif_widgets[pos] = gtk.combo_box_new_text()
|
||
eventBox.add(self.exif_widgets[pos])
|
||
for add_type in choices:
|
||
self.exif_widgets[pos].append_text(add_type)
|
||
self.exif_widgets[pos].set_active(default)
|
||
if mark_dirty:
|
||
self.exif_widgets[pos].connect("changed", self._mark_dirty)
|
||
row.pack_start(label, False)
|
||
row.pack_start(eventBox, True)
|
||
for name, text, cbtype, callback in callback_list:
|
||
if cbtype == "button":
|
||
label = gtk.Label()
|
||
label.set_text(text)
|
||
self.exif_widgets[pos + ":" + name + ":Label"] = label
|
||
row.pack_start(label, False)
|
||
icon = gtk.STOCK_EDIT
|
||
size = gtk.ICON_SIZE_MENU
|
||
button = gtk.Button()
|
||
image = gtk.Image()
|
||
image.set_from_stock(icon, size)
|
||
button.add(image)
|
||
button.set_relief(gtk.RELIEF_NONE)
|
||
button.connect("clicked", callback)
|
||
self.exif_widgets[pos + ":" + name] = button
|
||
row.pack_start(button, False)
|
||
elif cbtype == "checkbox":
|
||
button = gtk.CheckButton(text)
|
||
button.set_active(True)
|
||
button.connect("clicked", callback)
|
||
self.exif_widgets[pos + ":" + name] = button
|
||
row.pack_start(button, False)
|
||
row.show_all()
|
||
return row
|
||
|
||
# -----------------------------------------------
|
||
# Error Checking functions
|
||
# -----------------------------------------------
|
||
def _mark_dirty(self, obj):
|
||
pass
|
||
|
||
def _get_value(self, keytag):
|
||
"""
|
||
gets the value from the Exif Key, and returns it...
|
||
|
||
@param: keytag -- image metadata key
|
||
"""
|
||
|
||
KeyValue = ""
|
||
if LesserVersion:
|
||
KeyValue = self.plugin_image[keytag]
|
||
else:
|
||
try:
|
||
KeyValue = self.plugin_image[keytag].value
|
||
|
||
except (KeyError, ValueError, AttributeError):
|
||
pass
|
||
|
||
return KeyValue
|
||
|
||
def display_exif_tags(self, mediadatatags_ =None):
|
||
"""
|
||
once the pyexiv2.Image has been created, we display
|
||
all of the image Exif metadata...
|
||
"""
|
||
|
||
mediadatatags_ = _get_exif_keypairs(self.plugin_image)
|
||
|
||
# set has data flag...
|
||
self.set_has_data(len(mediadatatags_) > 0)
|
||
|
||
# Activate Clear and Save buttons...
|
||
self.activate_buttons(["Clear", "Save"])
|
||
|
||
# check to see if we got metadata from the media object?
|
||
if mediadatatags_:
|
||
|
||
# activate CopyTo button...
|
||
self.activate_buttons(["CopyTo"])
|
||
|
||
# set Message Area to Display...
|
||
self.exif_widgets["Message:Area"].set_text(_("Displaying image Exif metadata..."))
|
||
|
||
for keytag in mediadatatags_:
|
||
|
||
if LesserVersion:
|
||
label = self.plugin_image.tagDetails(keytag)[0]
|
||
human_value = self.plugin_image.interpretedExifValue(keytag)
|
||
|
||
else:
|
||
try:
|
||
tag = self.plugin_image[keytag]
|
||
label = tag.label
|
||
human_value = tag.human_value
|
||
except AttributeError:
|
||
human_value = False
|
||
|
||
if keytag in ("Exif.Image.DateTime",
|
||
"Exif.Photo.DateTimeOriginal",
|
||
"Exif.Photo.DateTimeDigitized"):
|
||
human_value = _process_datetime(self._get_value(keytag))
|
||
|
||
if human_value is not False:
|
||
self.model.append((self.plugin_image, label, human_value))
|
||
|
||
def CopyTo(self, imagekeytags =None):
|
||
"""
|
||
reads the image metadata after the pyexiv2.Image has been created
|
||
"""
|
||
|
||
imagekeytags = _get_exif_keypairs(self.plugin_image)
|
||
if imagekeytags:
|
||
imagekeytags = [keytag for keytag in imagekeytags if keytag in _DATAMAP]
|
||
|
||
self.exif_widgets["Message:Area"].set_text(_("Copying Exif metadata to the Edit Area..."))
|
||
|
||
for keytag in imagekeytags:
|
||
|
||
# name for matching to exif_widgets
|
||
widgetsName = _DATAMAP[keytag]
|
||
|
||
tagValue = self._get_value(keytag)
|
||
if tagValue:
|
||
|
||
if widgetsName in ["Description", "Artist", "Copyright"]:
|
||
self.exif_widgets[widgetsName].set_text(tagValue)
|
||
|
||
# Original Date of the image...
|
||
elif widgetsName == "DateTime":
|
||
use_date = self._get_value(keytag)
|
||
use_date = _process_datetime(use_date) if use_date else False
|
||
if use_date is not False:
|
||
self.exif_widgets[widgetsName].set_text(use_date)
|
||
|
||
# latituderef, Latitude, longituderef, Longitude...
|
||
elif widgetsName == "Latitude":
|
||
|
||
latitude = self._get_value(keytag)
|
||
longitude = self._get_value(_DATAMAP["Longitude"] )
|
||
|
||
# if latitude and longitude exist, display them?
|
||
if (latitude and longitude):
|
||
|
||
# split latitude metadata into (degrees, minutes, and seconds) from Rational
|
||
latdeg, latmin, latsec = rational_to_dms(latitude)
|
||
|
||
# split longitude metadata into degrees, minutes, and seconds
|
||
longdeg, longmin, longsec = rational_to_dms(longitude)
|
||
|
||
# check to see if we have valid GPS coordinates?
|
||
latfail = any(coords == False for coords in [latdeg, latmin, latsec])
|
||
longfail = any(coords == False for coords in [longdeg, longmin, longsec])
|
||
if (not latfail and not longfail):
|
||
|
||
# Latitude Direction Reference
|
||
latituderef = self._get_value(_DATAMAP["LatitudeRef"] )
|
||
|
||
# Longitude Direction Reference
|
||
longituderef = self._get_value(_DATAMAP["LongitudeRef"] )
|
||
|
||
# set display for Latitude GPS coordinates
|
||
self.exif_widgets["Latitude"].set_text(
|
||
"""%s° %s′ %s″ %s""" % (latdeg, latmin, latsec, latituderef) )
|
||
|
||
# set display for Longitude GPS coordinates
|
||
self.exif_widgets["Longitude"].set_text(
|
||
"""%s° %s′ %s″ %s""" % (longdeg, longmin, longsec, longituderef) )
|
||
|
||
# enable Save button after metadata has been "Copied to Edit Area"...
|
||
self.activate_buttons(["Save"])
|
||
|
||
if _MAGICK_FOUND:
|
||
self.activate_buttons(["Delete"])
|
||
|
||
# Clear the Message Area...
|
||
self.exif_widgets["Message:Area"].set_text("")
|
||
|
||
def clear_metadata(self, obj, cleartype = "All"):
|
||
"""
|
||
clears all data fields to nothing
|
||
|
||
@param: cleartype --
|
||
"Date" = clears only Date entry fields
|
||
"All" = clears all data fields
|
||
"""
|
||
|
||
# set Message Area text...
|
||
self.exif_widgets["Message:Area"].set_text(_("Edit area has been cleared..."))
|
||
|
||
# clear all data fields
|
||
if cleartype == "All":
|
||
for widgetsName in ["Description", "Artist", "Copyright", "DateTime",
|
||
"Latitude", "Longitude"]:
|
||
self.exif_widgets[widgetsName].set_text("")
|
||
|
||
# clear only the date/ time field
|
||
else:
|
||
self.exif_widgets["DateTime"].set_text("")
|
||
|
||
def convert2Jpeg(self):
|
||
"""
|
||
Will attempt to convert an image to jpeg if it is not?
|
||
"""
|
||
|
||
# if ImageMagick's convert is installed...
|
||
if _MAGICK_FOUND:
|
||
|
||
filepath, basename = os.path.split(self.image_path)
|
||
basename, oldext = os.path.splitext(self.image_path)
|
||
newextension = ".jpeg"
|
||
|
||
convert = subprocess.check_call(["convert", self.image_path,
|
||
os.path.join(filepath, basename + newextension) ] )
|
||
if str(convert):
|
||
|
||
# set Message Area to Convert...
|
||
self.exif_widgets["Message:Area"].set_text(_("Converting image,\n"
|
||
"You will need to delete the original image file..."))
|
||
|
||
self.deactivate_buttons(["Convert"])
|
||
|
||
def _set_exif_keytag(self, keytag, KeyValue):
|
||
"""
|
||
sets the value for the metadata keytags
|
||
"""
|
||
|
||
if LesserVersion:
|
||
self.plugin_image[keytag] = KeyValue
|
||
|
||
else:
|
||
try: # tag is being modified...
|
||
self.plugin_image[keytag] = KeyValue
|
||
|
||
except KeyError: # tag has not been set...
|
||
self.plugin_image[keytag] = pyexiv2.ExifTag(keytag, KeyValue)
|
||
|
||
except (ValueError, AttributeError): # there is an issue with either keytag or KeyValue
|
||
pass
|
||
|
||
def write_metadata(self, imageinstance):
|
||
"""
|
||
writes the Exif metadata to the image.
|
||
|
||
LesserVersion -- prior to pyexiv2-0.2.0
|
||
-- pyexiv2-0.2.0 and above...
|
||
"""
|
||
if LesserVersion:
|
||
imageinstance.writeMetadata()
|
||
|
||
else:
|
||
imageinstance.write()
|
||
|
||
# -------------------------------------------------------------------
|
||
# GPS coordinates functions
|
||
# -------------------------------------------------------------------
|
||
def addsymbols2gps(self, latitude =False, longitude =False):
|
||
"""
|
||
converts a degrees, minutes, seconds representation of Latitude/ Longitude
|
||
without their symbols to having them...
|
||
|
||
@param: latitude -- Latitude GPS coordinates
|
||
@param: longitude -- Longitude GPS coordinates
|
||
"""
|
||
latituderef, longituderef = "N", "E"
|
||
|
||
# check to see if Latitude/ Longitude exits?
|
||
if (latitude and longitude):
|
||
|
||
if (latitude.count(".") == 1 and longitude.count(".") == 1):
|
||
self.convert2dms(self.plugin_image)
|
||
|
||
# get Latitude/ Longitude from data fields
|
||
# after the conversion
|
||
latitude = self.exif_widgets["Latitude"].get_text()
|
||
longitude = self.exif_widgets["Longitude"].get_text()
|
||
|
||
# add DMS symbols if necessary?
|
||
# the conversion to decimal format, require the DMS symbols
|
||
elif ( (latitude.count("°") == 0 and longitude.count("°") == 0) and
|
||
(latitude.count("′") == 0 and longitude.count("′") == 0) and
|
||
(latitude.count('″') == 0 and longitude.count('″') == 0) ):
|
||
|
||
# is there a direction element here?
|
||
if (latitude.count("N") == 1 or latitude.count("S") == 1):
|
||
latdeg, latmin, latsec, latituderef = latitude.split(" ", 3)
|
||
else:
|
||
atitudeRef = "N"
|
||
latdeg, latmin, latsec = latitude.split(" ", 2)
|
||
if latdeg[0] == "-":
|
||
latdeg = latdeg.replace("-", "")
|
||
latituderef = "S"
|
||
|
||
# is there a direction element here?
|
||
if (longitude.count("E") == 1 or longitude.count("W") == 1):
|
||
longdeg, longmin, longsec, longituderef = longitude.split(" ", 3)
|
||
else:
|
||
ongitudeRef = "E"
|
||
longdeg, longmin, longsec = longitude.split(" ", 2)
|
||
if longdeg[0] == "-":
|
||
longdeg = longdeg.replace("-", "")
|
||
longituderef = "W"
|
||
|
||
latitude = """%s° %s′ %s″ %s""" % (latdeg, latmin, latsec, latituderef)
|
||
longitude = """%s° %s′ %s″ %s""" % (longdeg, longmin, longsec, longituderef)
|
||
|
||
return latitude, longitude
|
||
|
||
def convert2decimal(self, obj):
|
||
"""
|
||
will convert a decimal GPS coordinates into decimal format.
|
||
|
||
@param: latitude -- GPS Latitude coordinates from data field...
|
||
@param: longitude -- GPS Longitude coordinates from data field...
|
||
"""
|
||
|
||
# get Latitude/ Longitude from the data fields
|
||
latitude = self.exif_widgets["Latitude"].get_text()
|
||
longitude = self.exif_widgets["Longitude"].get_text()
|
||
|
||
# if latitude and longitude exist?
|
||
if (latitude and longitude):
|
||
|
||
# is Latitude/ Longitude are in DMS format?
|
||
if (latitude.count(" ") >= 2 and longitude.count(" ") >= 2):
|
||
|
||
# add DMS symbols if necessary?
|
||
# the conversion to decimal format, require the DMS symbols
|
||
if ( (latitude.count("°") == 0 and longitude.count("°") == 0) and
|
||
(latitude.count("′") == 0 and longitude.count("′") == 0) and
|
||
(latitude.count('″') == 0 and longitude.count('″') == 0) ):
|
||
|
||
latitude, longitude = self.addsymbols2gps(latitude, longitude)
|
||
|
||
# convert degrees, minutes, seconds w/ symbols to an 8 point decimal
|
||
latitude, longitude = conv_lat_lon( unicode(latitude),
|
||
unicode(longitude), "D.D8")
|
||
|
||
self.exif_widgets["Latitude"].set_text(latitude)
|
||
self.exif_widgets["Longitude"].set_text(longitude)
|
||
|
||
def convert2dms(self, obj):
|
||
"""
|
||
will convert a decimal GPS coordinates into degrees, minutes, seconds
|
||
for display only
|
||
"""
|
||
|
||
# get Latitude/ Longitude from the data fields
|
||
latitude = self.exif_widgets["Latitude"].get_text()
|
||
longitude = self.exif_widgets["Longitude"].get_text()
|
||
|
||
# if Latitude/ Longitude exists?
|
||
if (latitude and longitude):
|
||
|
||
# if coordinates are in decimal format?
|
||
if (latitude.count(".") == 1 and longitude.count(".") == 1):
|
||
|
||
# convert latitude and longitude to a DMS with separator of ":"
|
||
latitude, longitude = conv_lat_lon(latitude, longitude, "DEG-:")
|
||
|
||
# remove negative symbol if there is one?
|
||
latituderef = "N"
|
||
if latitude[0] == "-":
|
||
latitude = latitude.replace("-", "")
|
||
latituderef = "S"
|
||
latdeg, latmin, latsec = latitude.split(":", 2)
|
||
|
||
# remove negative symbol if there is one?
|
||
longituderef = "E"
|
||
if longitude[0] == "-":
|
||
longitude = longitude.replace("-", "")
|
||
longituderef = "W"
|
||
longdeg, longmin, longsec = longitude.split(":", 2)
|
||
|
||
self.exif_widgets["Latitude"].set_text(
|
||
"""%s° %s′ %s″ %s""" % (latdeg, latmin, latsec, latituderef) )
|
||
|
||
self.exif_widgets["Longitude"].set_text(
|
||
"""%s° %s′ %s″ %s""" % (longdeg, longmin, longsec, longituderef) )
|
||
|
||
#------------------------------------------------
|
||
# Writes/ saves Exif metadata to image
|
||
#------------------------------------------------
|
||
def save_metadata(self, datatags =None):
|
||
"""
|
||
gets the information from the plugin data fields
|
||
and sets the keytag = keyvalue image metadata
|
||
"""
|
||
|
||
# determine if there has been something entered in the data fields?
|
||
datatags = (len(self.exif_widgets["Description"].get_text() ) +
|
||
len(self.exif_widgets["Artist"].get_text() ) +
|
||
len(self.exif_widgets["Copyright"].get_text() ) +
|
||
len(self.exif_widgets["DateTime"].get_text() )+
|
||
len(self.exif_widgets["Latitude"].get_text() ) +
|
||
len(self.exif_widgets["Longitude"].get_text() ) )
|
||
|
||
# Description data field
|
||
self._set_exif_keytag(_DATAMAP["Description"], self.exif_widgets["Description"].get_text() )
|
||
|
||
# Modify Date/ Time... not a data field, but saved anyway...
|
||
self._set_exif_keytag(_DATAMAP["Modified"], datetime.now() )
|
||
|
||
# display modified Date/ Time
|
||
self.exif_widgets["Modified"].set_text(_format_datetime(datetime.now() ) )
|
||
|
||
# Artist/ Author data field
|
||
self._set_exif_keytag(_DATAMAP["Artist"], self.exif_widgets["Artist"].get_text() )
|
||
|
||
# Copyright data field
|
||
self._set_exif_keytag(_DATAMAP["Copyright"], self.exif_widgets["Copyright"].get_text() )
|
||
|
||
# Original Date/ Time data field
|
||
datetime_ = self.exif_widgets["DateTime"].get_text()
|
||
if datetime_:
|
||
datetime_ = _process_datetime(datetime_, False)
|
||
if datetime_ is not False:
|
||
self._set_exif_keytag(_DATAMAP["DateTime"], datetime_)
|
||
|
||
# Latitude/ Longitude data fields
|
||
latitude = self.exif_widgets["Latitude"].get_text()
|
||
longitude = self.exif_widgets["Longitude"].get_text()
|
||
|
||
# check to see if Latitude/ Longitude exists?
|
||
if (latitude and longitude):
|
||
|
||
# complete some error checking to prevent crashes...
|
||
# if "?" character exist, remove it?
|
||
if "?" in latitude:
|
||
latitude = latitude.replace("?", "")
|
||
if "?" in longitude:
|
||
longitude = longitude.replace("?", "")
|
||
|
||
# if "," character exists, remove it?
|
||
if "," in latitude:
|
||
latitude = latitude.replace(",", "")
|
||
if "," in longitude:
|
||
longitude = longitude.replace(",", "")
|
||
|
||
# if it is in decimal format, convert it to DMS?
|
||
# if not, then do nothing?
|
||
self.convert2dms(self.plugin_image)
|
||
|
||
# get Latitude/ Longitude from the data fields
|
||
latitude = self.exif_widgets["Latitude"].get_text()
|
||
longitude = self.exif_widgets["Longitude"].get_text()
|
||
|
||
# will add (degrees, minutes, seconds) symbols if needed?
|
||
# if not, do nothing...
|
||
latitude, longitude = self.addsymbols2gps(latitude, longitude)
|
||
|
||
# set up display
|
||
self.exif_widgets["Latitude"].set_text(latitude)
|
||
self.exif_widgets["Longitude"].set_text(longitude)
|
||
|
||
latituderef = " N"
|
||
if "S" in latitude:
|
||
latituderef = " S"
|
||
latitude = latitude.replace(latituderef, "")
|
||
latituderef = latituderef.replace(" ", "")
|
||
|
||
longituderef = " E"
|
||
if "W" in longitude:
|
||
longituderef = " W"
|
||
longitude = longitude.replace(longituderef, "")
|
||
longituderef = longituderef.replace(" ", "")
|
||
|
||
# remove symbols for saving Latitude/ Longitude GPS coordinates
|
||
latitude, longitude = _removesymbols4saving(latitude, longitude)
|
||
|
||
# convert (degrees, minutes, seconds) to Rational for saving
|
||
self._set_exif_keytag(_DATAMAP["LatitudeRef"], latituderef)
|
||
self._set_exif_keytag(_DATAMAP["Latitude"], coords_to_rational(latitude))
|
||
|
||
# convert (degrees, minutes, seconds) to Rational for saving
|
||
self._set_exif_keytag(_DATAMAP["LongitudeRef"], longituderef)
|
||
self._set_exif_keytag(_DATAMAP["Longitude"], coords_to_rational(longitude))
|
||
|
||
if datatags:
|
||
# set Message Area to Saved...
|
||
self.exif_widgets["Message:Area"].set_text(_("Saving Exif metadata to the image..."))
|
||
else:
|
||
# set Message Area to Cleared...
|
||
self.exif_widgets["Message:Area"].set_text(_("Image Exif metadata has been cleared "
|
||
"from this image..."))
|
||
|
||
# writes all Exif Metadata to image even if the fields are all empty...
|
||
self.write_metadata(self.plugin_image)
|
||
|
||
# Activate Delete button...
|
||
if _MAGICK_FOUND:
|
||
self.activate_buttons(["Delete"])
|
||
|
||
def strip_metadata(self, erase_results =None):
|
||
"""
|
||
Will completely and irrevocably erase all Exif metadata from this image.
|
||
"""
|
||
|
||
if _MAGICK_FOUND:
|
||
erase = subprocess.check_call( ["convert", self.image_path, "-strip", self.image_path] )
|
||
erase_results = str(erase)
|
||
|
||
else:
|
||
mediadatatags_ = _get_exif_keypairs(self.plugin_image)
|
||
if mediadatatags_:
|
||
for keytag in mediadatatags_:
|
||
del self.plugin_image[keytag]
|
||
erase_results = True
|
||
|
||
# write wiped metadata to image...
|
||
self.write_metadata(self.plugin_image)
|
||
|
||
if erase_results:
|
||
|
||
# set Message Area for deleting...
|
||
self.exif_widgets["Message:Area"].set_text(_("Deleting all Exif metadata..."))
|
||
|
||
# Clear the Display and Edit Areas
|
||
self.clear_metadata(self.plugin_image)
|
||
self.model.clear()
|
||
|
||
# Notify the User...
|
||
self.exif_widgets["Message:Area"].set_text(_("All Exif metadata has been "
|
||
"deleted from this image..."))
|
||
self.update()
|
||
|
||
# re- initialize the image...
|
||
if _JHEAD_FOUND:
|
||
reinit = subprocess.check_call( ["jhead", "-purejpg", self.image_path] )
|
||
reinitialize = str(reinit)
|
||
if reinitialize:
|
||
self.exif_widgets["Message:Area"].set_text(_("Image has be re- initialized "
|
||
"for Exif metadata..."))
|
||
|
||
# -----------------------------------------------
|
||
# Date Calendar functions
|
||
# -----------------------------------------------
|
||
def select_date(self, obj):
|
||
"""
|
||
will allow you to choose a date from the calendar widget
|
||
"""
|
||
|
||
tip = _("Double click a day to return the date.")
|
||
|
||
self.app = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||
self.app.tooltip = tip
|
||
self.app.set_title(_("Select Date"))
|
||
self.app.set_default_size(450, 200)
|
||
self.app.set_border_width(10)
|
||
self.exif_widgets["Calendar"] = gtk.Calendar()
|
||
self.exif_widgets["Calendar"].connect('day-selected-double-click', self.double_click)
|
||
self.app.add(self.exif_widgets["Calendar"])
|
||
self.exif_widgets["Calendar"].show()
|
||
self.app.show()
|
||
|
||
def double_click(self, obj):
|
||
"""
|
||
receives double-clicked and returns the selected date
|
||
widget
|
||
"""
|
||
now = time.localtime()
|
||
|
||
year, month, day = self.exif_widgets["Calendar"].get_date()
|
||
self.exif_widgets["DateTime"].set_text( "%04d-%s-%02d %02d:%02d:%02d" % (
|
||
year, _dd.long_months[month + 1], day, now[3], now[4], now[5]) )
|
||
|
||
# close this window
|
||
self.app.destroy()
|
||
|
||
|
||
def string_to_rational(coordinate):
|
||
"""
|
||
convert string to rational variable for GPS
|
||
"""
|
||
|
||
if '.' in coordinate:
|
||
value1, value2 = coordinate.split('.')
|
||
return pyexiv2.Rational(int(float(value1 + value2)), 10**len(value2))
|
||
else:
|
||
return pyexiv2.Rational(int(coordinate), 1)
|
||
|
||
def _get_exif_keypairs(plugin_image):
|
||
"""
|
||
Will be used to retrieve and update the Exif metadata from the image.
|
||
"""
|
||
|
||
if not plugin_image:
|
||
return False
|
||
|
||
mediadatatags_ = False
|
||
if LesserVersion: # prior to pyexiv2-0.2.0
|
||
|
||
# get all keytags for this image for diplay only...
|
||
mediadatatags_ = [keytag for keytag in plugin_image.exifKeys() ]
|
||
|
||
else: # pyexiv2-0.2.0 and above
|
||
|
||
# get all keytags for this image for diplay only...
|
||
mediadatatags_ = [keytag for keytag in chain(
|
||
plugin_image.exif_keys, plugin_image.xmp_keys,
|
||
plugin_image.iptc_keys) ]
|
||
|
||
return mediadatatags_
|
||
|
||
def coords_to_rational(coordinates):
|
||
"""
|
||
returns the GPS coordinates to Latitude/ Longitude
|
||
"""
|
||
|
||
return [string_to_rational(coordinate) for coordinate in coordinates.split(" ")]
|
||
|
||
def convert_value(value):
|
||
"""
|
||
will take a value from the coordinates and return its value
|
||
"""
|
||
|
||
if isinstance(value, (Fraction, pyexiv2.Rational)):
|
||
|
||
return str( ( Decimal(value.numerator) / Decimal(value.denominator) ) )
|
||
|
||
def _removesymbols4saving(latitude, longitude):
|
||
"""
|
||
will recieve a DMS with symbols and return it without them
|
||
|
||
@param: latitude -- Latitude GPS coordinates
|
||
@param: longitude -- GPS Longitude coordinates
|
||
"""
|
||
|
||
# check to see if latitude/ longitude exist?
|
||
if (latitude and longitude):
|
||
|
||
# remove degrees symbol if it exist?
|
||
latitude = latitude.replace("°", "")
|
||
longitude = longitude.replace("°", "")
|
||
|
||
# remove minutes symbol if it exist?
|
||
latitude = latitude.replace("′", "")
|
||
longitude = longitude.replace("′", "")
|
||
|
||
# remove seconds symbol if it exist?
|
||
latitude = latitude.replace('″', "")
|
||
longitude = longitude.replace('″', "")
|
||
|
||
return latitude, longitude
|
||
|
||
def rational_to_dms(coords):
|
||
"""
|
||
takes a rational set of coordinates and returns (degrees, minutes, seconds)
|
||
|
||
[Fraction(40, 1), Fraction(0, 1), Fraction(1079, 20)]
|
||
"""
|
||
|
||
degrees, minutes, seconds = [False]*3
|
||
# coordinates look like:
|
||
# [Rational(38, 1), Rational(38, 1), Rational(150, 50)]
|
||
# or [Fraction(38, 1), Fraction(38, 1), Fraction(318, 100)]
|
||
if isinstance(coords, list):
|
||
|
||
if len(coords) == 3:
|
||
return [convert_value(coordinate) for coordinate in coords]
|
||
|
||
return degrees, minutes, seconds
|
||
|
||
def _format_datetime(exif_dt):
|
||
"""
|
||
Convert a python datetime object into a string for display, using the
|
||
standard Gramps date format.
|
||
"""
|
||
|
||
date_part = gen.lib.Date()
|
||
if isinstance(exif_dt, datetime):
|
||
date_part.set_yr_mon_day(exif_dt.year, exif_dt.month, exif_dt.day)
|
||
date_str = _dd.display(date_part)
|
||
time_str = _('%(hr)02d:%(min)02d:%(sec)02d') % {'hr': exif_dt.hour,
|
||
'min': exif_dt.minute,
|
||
'sec': exif_dt.second}
|
||
elif isinstance(exif_dt, str):
|
||
exif_dt = _get_date_format(exif_dt)
|
||
if exif_dt == False:
|
||
return False
|
||
|
||
date_part.set_yr_mon_day(exif_dt[0], exif_dt[1], exif_dt[2])
|
||
date_str = _dd.display(date_part)
|
||
time_str = _('%(hr)02d:%(min)02d:%(sec)02d') % {'hr' : exif_dt[3],
|
||
'min': exif_dt[4],
|
||
'sec': exif_dt[5]}
|
||
return _('%(date)s %(time)s') % {'date': date_str, 'time': time_str}
|
||
|
||
def _get_date_format(datestr):
|
||
"""
|
||
attempt to retrieve date format from date string
|
||
"""
|
||
|
||
# attempt to determine the dateformat of the variable passed to it...
|
||
tmpdate = False
|
||
for dateformat in ["%Y-%m-%d %H:%M:%S", "%Y %m %d %H:%M:%S",
|
||
"%Y-%b-%d %H:%M:%S", "%Y %b %d %H:%M:%S",
|
||
"%Y-%B-%d %H:%M:%S", "%Y %B %d %H:%M:%S",
|
||
"%d-%m-%Y %H:%M:%S", "%d %m %Y %H:%M:%S",
|
||
"%d-%b-%Y %H:%M:%S", "%d %b %Y %H:%M:%S",
|
||
"%d-%B-%Y %H:%M:%S", "%d %B %Y %H:%M:%S",
|
||
"%m-%d-%Y %H:%M:%S", "%m %d %Y %H:%M:%S",
|
||
"%b-%d-%Y %H:%M:%S", "%b %d %Y %H:%M:%S",
|
||
"%B-%d-%Y %H:%M:%S", "%B %d %Y %H:%M:%S"]:
|
||
|
||
# find date string format
|
||
try:
|
||
tmpdate = time.strptime(datestr, dateformat)
|
||
break
|
||
|
||
# datestring format not found...
|
||
except ValueError:
|
||
pass
|
||
|
||
return tmpdate
|
||
|
||
def _create_datetime(date_elements):
|
||
"""
|
||
will create and retrun a str or datetime from (
|
||
year, month, day, hour, minutes, and seconds) ...
|
||
|
||
if the year is less than 1900, then it will return a string representation...
|
||
"""
|
||
pyear, pmonth, day, hour, minutes, seconds = date_elements
|
||
|
||
# do some error trapping...
|
||
if pmonth > 12:
|
||
pmonth = 12
|
||
elif pmonth <= 0:
|
||
pmonth = 1
|
||
|
||
if hour >= 24:
|
||
hour = 23
|
||
elif hour < 0:
|
||
hour = 0
|
||
|
||
if minutes > 59:
|
||
minutes = 59
|
||
elif minutes < 0:
|
||
minutes = 0
|
||
|
||
if seconds > 59:
|
||
seconds = 59
|
||
elif seconds < 0:
|
||
seconds = 0
|
||
|
||
# get the number of days in year for all months
|
||
numdays = [0] + [calendar.monthrange(year, month)[1] for year in [pyear] for month in range(1, 13) ]
|
||
if day > numdays[pmonth]:
|
||
day = numdays[pmonth]
|
||
elif day <= 0:
|
||
day = 1
|
||
|
||
if pyear < 1900:
|
||
try:
|
||
tmpdate = "%04d-%s-%02d %02d:%02d:%02d" % (pyear, _dd.long_months[pmonth], day,
|
||
hour, minutes, seconds)
|
||
except ValueError:
|
||
tmpdate = False
|
||
|
||
else:
|
||
try:
|
||
tmpdate = datetime(pyear, pmonth, day, hour, minutes, seconds)
|
||
|
||
except ValueError:
|
||
tmpdate = False
|
||
|
||
if tmpdate is False:
|
||
tmpdate = ""
|
||
|
||
return tmpdate
|
||
|
||
def _process_datetime(tmpdate, exif_type =True):
|
||
"""
|
||
will attempt to parse the date/ time Exif metadata entry into its pieces...
|
||
(year, month, day, hour, minutes, seconds)
|
||
"""
|
||
|
||
if not tmpdate:
|
||
return False
|
||
|
||
datetype = type(tmpdate)
|
||
|
||
# if variable is already in datetime.datetime() format, return it?
|
||
if datetype == datetime:
|
||
pyear, pmonth, day = tmpdate.year, tmpdate.month, tmpdate.day
|
||
hour, minutes, seconds = tmpdate.hour, tmpdate.minute, tmpdate.second
|
||
|
||
elif any(datetype == value for value in [date, gen.lib.date.Date, list] ):
|
||
hour, minutes, seconds = time.localtime()[3:6]
|
||
|
||
# datetime.date format
|
||
if isinstance(tmpdate, date):
|
||
pyear, pmonth, day = tmpdate.year, tmpdate.month, tmpdate.day
|
||
|
||
# gen.lib.date.Date format
|
||
elif isinstance(tmpdate, gen.lib.date.Date):
|
||
pyear, pmonth, day = tmpdate.get_year(), tmpdate.get_month(), tmpdate.get_day()
|
||
|
||
# list format
|
||
else:
|
||
pyear, pmonth, day = tmpdate[0].year, tmpdate[0].month, tmpdate[0].day
|
||
|
||
# string format...
|
||
elif datetype == str:
|
||
|
||
datestr = _get_date_format(tmpdate)
|
||
if datestr is not False:
|
||
pyear, pmonth, day, hour, minutes, seconds = datestr[0:6]
|
||
|
||
else:
|
||
pyear, pmonth, day, hour, minutes, seconds = [False]*6
|
||
|
||
if (not pyear and not pmonth):
|
||
tmpdate = False
|
||
|
||
else:
|
||
|
||
# create datetime...
|
||
tmpdate = _create_datetime([pyear, pmonth, day, hour, minutes, seconds])
|
||
|
||
if tmpdate is not False:
|
||
|
||
if isinstance(tmpdate, datetime):
|
||
|
||
# for display only...
|
||
# make datetime displayed as user has set in preferences...
|
||
if exif_type:
|
||
tmpdate = _format_datetime(tmpdate)
|
||
|
||
return tmpdate
|
||
|
||
def _setup_widget_tooltips(exif_widgets):
|
||
"""
|
||
setup tooltips for each entry field and button.
|
||
"""
|
||
|
||
# add tooltips for the data entry fields...
|
||
for widget, tooltip in _TOOLTIPS:
|
||
exif_widgets[widget].set_tooltip_text(tooltip)
|
||
|
||
# add tooltips for the buttons...
|
||
for widget, tooltip in _BUTTONTIPS.items():
|
||
exif_widgets[widget].set_tooltip_text(tooltip)
|