#!/usr/bin/env python
# -*- coding: utf-8 -*-
# License : GPLv3 : http://gplv3.fsf.org/
try:
import os, sys
import os.path
import subprocess
import shutil
import gobject
import time
import threading
import multiprocessing
import re
import ConfigParser
import operator
import cairo
import random
except:
print('An error occured. Python or one of its sub modules is absent...\nIt would be wise to check your python installation.')
sys.exit(1)
try:
import Image
from PIL.ExifTags import TAGS
except:
print('Python Imaging Library is missing.')
try:
import gtk
import gtk.glade
import pygtk
pygtk.require("2.0")
except:
print('gtk2, pygtk or libglade is missing.')
sys.exit(1)
# Bad, bad, really bad coder... Global variables...
global session_images_bak
session_images_bak=[]
global session_options_bak
session_options_bak=[]
APP = 'MacroFusion'
__VERSION__='0.7.2'
__LICENSE__='GPL'
__COPYRIGHT__='Dariusz Duma'
__WEBSITE__='http://sourceforge.net/p/macrofusion'
if os.path.exists('/usr/share/mfusion/ui/DOFuseInterface2.glade') \
and os.path.exists('/usr/share/mfusion/ui/Progress.glade') \
and os.path.exists('/usr/share/pixmaps/macrofusion.png') \
and os.path.exists('/usr/share/mfusion/images/logoSplash.png'):
# print ("System wide install!")
DIR = '/usr/share/locale/'
IMG = '/usr/share/pixmaps/'
IMG2 = '/usr/share/mfusion/images/'
UI = '/usr/share/mfusion/ui/'
elif os.path.exists(sys.path[0] + "/ui/DOFuseInterface2.glade"):
# print ("Local run!")
DIR = sys.path[0] + '/locale/'
IMG = sys.path[0] + '/images/'
IMG2 = sys.path[0] + '/images/'
UI = sys.path[0] + '/ui/'
else:
print ("That's me, your MacroFusion. Make your mind - local or system wide install?")
sys.exit(1)
import locale
import gettext
for module in (gettext, gtk.glade):
module.bindtextdomain(APP, DIR)
module.textdomain(APP)
locale.setlocale(locale.LC_ALL, '')
#gettext.bindtextdomain(APP, DIR)
#gettext.textdomain(APP)
#gettext.install(APP)
_ = gettext.gettext
gobject.threads_init() #Pour que les threads soient lancés au moment opportun.
def toggled_cb(cell, path, user_data):
model, column = user_data
model[path][column] = not model[path][column]
return
# PLEASE REAPAIR!! Python-imaging can't open .tiff (or some of them)
def creer_miniature(chemin,taille):
outfile=donnees.previs_dossier + '/' + os.path.split(chemin)[1]
try:
im = Image.open(chemin)
im.thumbnail(taille)
im.save(outfile, "JPEG")
except IOError:
print _("Generating %s thumbnail failed.") % chemin
return outfile
####################################################
########Classe des données##########################
####################################################
class Donnees:
"""Données utiles"""
def __init__(self):
self.install_dossier=sys.path[0] #On recupere le dossier d'install
self.home_dossier = (os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config')) + '/mfusion'
# self.home_dossier = os.environ['HOME'] #On créé les dossiers pour mettre les preview
self.enfuse_dossier = self.home_dossier
self.previs_dossier = self.enfuse_dossier + "/preview"
if not os.path.isdir(self.enfuse_dossier):
os.makedirs(self.enfuse_dossier)
if not os.path.isdir(self.previs_dossier):
os.makedirs(self.previs_dossier)
self.default_folder=os.path.expanduser('~/')
self.default_file=""
def check_install(self, name):
a=False
for dir in os.environ['PATH'].split(":"):
prog = os.path.join(dir, name)
if os.path.exists(prog):
a=True
return a
##############################################################
###########Classe de l'interface##############################
##############################################################
class Interface:
"""Interface pour le logiciel d'exposition-fusion enfuse"""
def __init__(self):
# Set default icon
gtk.window_set_default_icon_from_file(IMG + 'macrofusion.png')
self.cpus = multiprocessing.cpu_count()
if not donnees.check_install("enfuse"):
self.messageinthebottle(_("Can't find Enfuse.\nPlease check enblend/enfuse is installed.\nStopping..."))
sys.exit()
# Check cpus
if self.cpus>1 and donnees.check_install("enfuse-mp"):
print _("Will use all the powers of your CPU!")
self.enfuser = "enfuse-mp"
else:
self.enfuser = "enfuse"
#Set the Glade file
self.gui=gtk.glade.XML(fname=UI + "DOFuseInterface2.glade", domain=APP)
#Dans la foulee on chope la fenetre principale, ca sert a rien c'est pour
#montrer qu'on peut le faire c'est tout ^^
self.win=self.gui.get_widget("mainwindow")
self.win.set_title('MacroFusion')
#On chope le reste, et ca, ca va servir...
self.listeimages = self.gui.get_widget("listeimages")
self.buttonajoutfichiers = self.gui.get_widget("buttonajoutfichiers")
self.buttonenleverfichier = self.gui.get_widget("buttonenleverfichier")
self.statusbar = self.gui.get_widget("status1")
self.statusbar.push(1, _("CPU Cores: %s") % self.cpus)
self.hscaleexp = self.gui.get_widget("hscaleexp")
self.ajus_exp = gtk.Adjustment(value=1, lower=0, upper=1, step_incr=0.1, page_incr=0.1, page_size=0)
self.hscaleexp.set_adjustment(self.ajus_exp)
self.spinbuttonexp = self.gui.get_widget("spinbuttonexp")
self.spinbuttonexp.set_digits(1)
self.spinbuttonexp.set_value(1)
self.spinbuttonexp.set_adjustment(self.ajus_exp)
self.hscalecont = self.gui.get_widget("hscalecont")
self.ajus_cont = gtk.Adjustment(value=0, lower=0, upper=1, step_incr=0.1, page_incr=0.1, page_size=0)
self.hscalecont.set_adjustment(self.ajus_cont)
self.spinbuttoncont = self.gui.get_widget("spinbuttoncont")
self.spinbuttoncont.set_digits(1)
self.spinbuttoncont.set_value(0)
self.spinbuttoncont.set_adjustment(self.ajus_cont)
self.hscalesat = self.gui.get_widget("hscalesat")
self.ajus_sat = gtk.Adjustment(value=0.2, lower=0, upper=1, step_incr=0.1, page_incr=0.1, page_size=0)
self.hscalesat.set_adjustment(self.ajus_sat)
self.spinbuttonsat = self.gui.get_widget("spinbuttonsat")
self.spinbuttonsat.set_digits(1)
self.spinbuttonsat.set_value(0.2)
self.spinbuttonsat.set_adjustment(self.ajus_sat)
self.hscalemu = self.gui.get_widget("hscalemu")
self.ajus_mu = gtk.Adjustment(value=0.5, lower=0, upper=1, step_incr=0.01, page_incr=0.1, page_size=0)
self.hscalemu.set_adjustment(self.ajus_mu)
self.spinbuttonmu = self.gui.get_widget("spinbuttonmu")
self.spinbuttonmu.set_digits(2)
self.spinbuttonmu.set_value(0.5)
self.spinbuttonmu.set_adjustment(self.ajus_mu)
self.hscalesigma = self.gui.get_widget("hscalesigma")
self.ajus_sigma = gtk.Adjustment(value=0.2, lower=0, upper=1, step_incr=0.01, page_incr=0.1, page_size=0)
self.hscalesigma.set_adjustment(self.ajus_sigma)
self.spinbuttonsigma = self.gui.get_widget("spinbuttonsigma")
self.spinbuttonsigma.set_digits(2)
self.spinbuttonsigma.set_value(0.2)
self.spinbuttonsigma.set_adjustment(self.ajus_sigma)
self.buttonpreview = self.gui.get_widget("buttonpreview")
self.checkbuttontiff = self.gui.get_widget("checkbuttontiff")
self.checkbuttonjpeg = self.gui.get_widget("checkbuttonjpeg")
self.buttonfusion = self.gui.get_widget("buttonfusion")
self.buttonbeforeafter = self.gui.get_widget("buttonbeforeafter")
self.buttonedit = self.gui.get_widget("buttoneditw")
self.imagepreview = self.gui.get_widget("imagepreview")
self.imagepreview.set_from_file(IMG2 + "logoSplash.png")
self.progressbar = self.gui.get_widget("progressbar")
self.checkbuttonexif = self.gui.get_widget("checkbuttonexif")
#valeurs des options et configurations :
self.check_pyramidelevel = self.gui.get_widget("check_pyramidelevel")
self.spinbuttonlevel = self.gui.get_widget("spinbuttonlevel")
self.check_hardmask = self.gui.get_widget("check_hardmask")
self.check_contwin = self.gui.get_widget("check_contwin")
self.spinbuttoncontwin = self.gui.get_widget("spinbuttoncontwin")
self.check_courb = self.gui.get_widget("check_courb")
self.check_prctcourb = self.gui.get_widget("check_prctcourb")
self.spinbuttoncourb = self.gui.get_widget("spinbuttoncourb")
self.check_detecbord = self.gui.get_widget("check_detecbord")
self.spinbuttonEdge = self.gui.get_widget("spinbuttonEdge")
# self.spinbuttonEdge.set_value(self.conf.getint('prefs', 'w'))
self.spinbuttonLceS = self.gui.get_widget("spinbuttonLceS")
self.spinbuttonLceF = self.gui.get_widget("spinbuttonLceF")
self.check_lces = self.gui.get_widget("check_lces")
self.check_lcef = self.gui.get_widget("check_lcef")
self.check_ciecam = self.gui.get_widget("check_ciecam")
self.check_desatmeth = self.gui.get_widget("check_desatmeth")
self.combobox_desatmet = self.gui.get_widget("combobox_desatmet")
self.spinbuttonlargeurprev = self.gui.get_widget("spinbuttonlargeurprev")
self.spinbuttonhauteurprev = self.gui.get_widget("spinbuttonhauteurprev")
self.checkbuttoncache = self.gui.get_widget("checkbuttoncache")
self.spinbuttoncache = self.gui.get_widget("spinbuttoncache")
self.checkbuttonbloc = self.gui.get_widget("checkbuttonbloc")
self.spinbuttonbloc = self.gui.get_widget("spinbuttonbloc")
self.checkbuttontaillefinale = self.gui.get_widget("checkbuttontaillefinale")
self.spinbuttonlargeurfinale = self.gui.get_widget("spinbuttonlargeurfinale")
self.spinbuttonhauteurfinale = self.gui.get_widget("spinbuttonhauteurfinale")
self.spinbuttonxoff = self.gui.get_widget("spinbuttonxoff")
self.spinbuttonyoff = self.gui.get_widget("spinbuttonyoff")
self.checkbuttonjpegorig = self.gui.get_widget("checkbuttonjpegorig")
self.hscalecomprjpeg = self.gui.get_widget("hscalecomprjpeg")
self.combtiff = self.gui.get_widget("combtiff")
self.checkbutton_a5_align = self.gui.get_widget("checkbutton_a5_align")
self.checkbutton_a5_crop = self.gui.get_widget("checkbutton_a5_crop")
self.checkbutton_a5_shift = self.gui.get_widget("checkbutton_a5_shift")
self.checkbutton_a5_field = self.gui.get_widget("checkbutton_a5_field")
self.entryedit_field = self.gui.get_widget("entry_editor")
self.combobox_desatmet.set_active(0)
self.combtiff.set_active(0)
if not donnees.check_install('exiftool'):
self.checkbuttonexif.set_sensitive(False)
self.messageinthebottle(_("Exiftool is missing!\n\n Cannot copy exif info."))
if not donnees.check_install('align_image_stack'):
self.checkbutton_a5_align.set_sensitive(False)
self.checkbutton_a5_crop.set_sensitive(False)
self.checkbutton_a5_field.set_sensitive(False)
self.checkbutton_a5_shift.set_sensitive(False)
#self.checkbutton_a5_align.set_sensitive(False)
self.messageinthebottle(_("Hugin tools (align_image_stack) are missing !\n\n Cannot auto align images."))
# Read values from config
self.conf = ConfigParser.ConfigParser()
if os.path.isfile(donnees.enfuse_dossier + '/mfusion.cfg'):
self.conf.read(donnees.enfuse_dossier + '/mfusion.cfg')
if self.conf.has_option('prefs', 'pwidth'):
self.spinbuttonlargeurprev.set_value(self.conf.getint('prefs', 'pwidth'))
if self.conf.has_option('prefs', 'pheight'):
self.spinbuttonhauteurprev.set_value(self.conf.getint('prefs', 'pheight'))
if self.conf.has_option('prefs', 'cachebutton'):
self.checkbuttoncache.set_active(self.conf.getboolean('prefs', 'cachebutton'))
if self.conf.has_option('prefs', 'cachesize'):
self.spinbuttoncache.set_value(self.conf.getint('prefs', 'cachesize'))
if self.conf.has_option('prefs', 'blocbutton'):
self.checkbuttonbloc.set_active(self.conf.getboolean('prefs', 'blocbutton'))
if self.conf.has_option('prefs', 'blocsize'):
self.spinbuttonbloc.set_value(self.conf.getint('prefs', 'blocsize'))
if self.conf.has_option('prefs', 'outsize'):
self.checkbuttontaillefinale.set_active(self.conf.getboolean('prefs', 'outsize'))
if self.conf.has_option('prefs', 'outwidth'):
self.spinbuttonlargeurfinale.set_value(self.conf.getint('prefs', 'outwidth'))
if self.conf.has_option('prefs', 'outheight'):
self.spinbuttonhauteurfinale.set_value(self.conf.getint('prefs', 'outheight'))
if self.conf.has_option('prefs', 'xoff'):
self.spinbuttonxoff.set_value(self.conf.getint('prefs', 'xoff'))
if self.conf.has_option('prefs', 'yoff'):
self.spinbuttonyoff.set_value(self.conf.getint('prefs', 'yoff'))
if self.conf.has_option('prefs', 'jpegdef'):
self.checkbuttonjpegorig.set_active(self.conf.getboolean('prefs', 'jpegdef'))
if self.conf.has_option('prefs', 'jpegcompr'):
self.hscalecomprjpeg.set_value(self.conf.getfloat('prefs', 'jpegcompr'))
if self.conf.has_option('prefs', 'tiffcomp'):
self.combtiff.set_active(self.conf.getint('prefs', 'tiffcomp'))
if self.conf.has_option('prefs', 'exif'):
self.checkbuttonexif.set_active(self.conf.getboolean('prefs', 'exif'))
if self.conf.has_option('prefs', 'editor'):
self.entryedit_field.set_text(self.conf.get('prefs', 'editor'))
else:
self.entryedit_field.set_text("gimp")
#On relie les signaux (cliques sur boutons, cochage des cases, ...) aux fonctions appropriées
dic = { "on_mainwindow_destroy" : self.exit_app,
"on_buttonannuler_clicked" : self.exit_app,
"on_menufilequit_activate" : self.exit_app,
"on_menufileopen_activate" : self.ouverture,
"on_buttonajoutfichiers_clicked" : self.ajout,
"on_menufileadd_activate" : self.ajout,
"on_buttonenleverfichier_clicked" : self.ttenlever,
"on_menufileenlever_activate" : self.enlever,
"on_menufilettenlever_activate" : self.ttenlever,
"on_buttonpreview_clicked" : self.preview,
"on_menufilesave_activate" : self.fusion,
"on_buttonfusion_clicked" : self.fusion,
"on_buttoneditw_clicked" : self.sendto,
"on_buttonbeforeafter_pressed" : self.baswitch,
"on_buttonbeforeafter_released" : self.baswitch,
"on_entry_editor_activate" : self.check_editor,
"on_imagemenuitem10_activate" : self.apropos
}
#Auto-connection des signaux
self.gui.signal_autoconnect(dic)
#initialisation de la liste d'images a fusionner
self.inittreeview()
def exit_app(self, action):
# cancel = self.autosave_image()
# if cancel:
# return True
self.stop_now = True
self.closing_app = True
self.save_settings()
self.cleanup()
sys.exit(0)
def check_editor(self, action):
if not donnees.check_install(self.entryedit_field.get_text()):
Gui.messageinthebottle(_("No such application!\n\n Cannot find ") + self.entryedit_field.get_text() + _(".\n\n Revert to default value."))
self.entryedit_field.set_text("gimp")
return False
return True
def cleanup(self):
# os.remove(donnees.enfuse_dossier + "/session.sav")
for self.files in os.walk(donnees.previs_dossier):
for self.filename in self.files[2]:
os.remove(donnees.previs_dossier + "/" + self.filename)
def inittreeview(self):
"""initialisation de la liste d'images a importer"""
self.liststoreimport = gtk.ListStore(bool, str, str, gtk.gdk.Pixbuf, str) #création de la listestore qui contiendra les noms d'images
self.listeimages.set_model(self.liststoreimport) #on donne la liststore au l'afficheur treeview
self.listeimages.set_property('tooltip-column', 4)
self.colonneselect = gtk.TreeViewColumn('') #Premiere colonne :
self.listeimages.append_column(self.colonneselect) #on l'ajoute au TreeView
self.select=gtk.CellRendererToggle() #On creer le cellrender pour avoir des boutons toggle
self.colonneselect.pack_start(self.select, True) #on met le cellrender dans la colonne
self.colonneselect.add_attribute(self.select, 'active', 0) #on met les boutons actifs par défaut
self.colonneimages = gtk.TreeViewColumn(_('Image')) #deuxieme colonne, titre 'Image'
self.listeimages.append_column(self.colonneimages) #on rajoute la colonne dans le treeview
self.cell = gtk.CellRendererText() #Ce sera des cellules de texte
self.colonneimages.pack_start(self.cell, True) #que l'on met dans la colonne
self.colonneimages.add_attribute(self.cell, 'text', 1) #et on specifie que c'est du texte simple
self.colonneimages2 = gtk.TreeViewColumn(_("Thumbnail")) #deuxieme colonne, titre 'Image'
self.listeimages.append_column(self.colonneimages2) #on rajoute la colonne dans le treeview
self.cell2 = gtk.CellRendererPixbuf() #Ce sera des cellules de texte
self.colonneimages2.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
self.colonneimages2.pack_start(self.cell2, True) #que l'on met dans la colonne
self.colonneimages2.add_attribute(self.cell2, 'pixbuf', 3)
self.cell2.set_property('visible', 1)
self.listeimages.set_rules_hint(True)
self.select.connect("toggled", toggled_cb, (self.liststoreimport, 0)) #Pour que les boutons de selection marchent
def ouverture(self, widget):
FenOuv=Fenetre_Ouvrir(self.liststoreimport,0)
self.liststoreimport=FenOuv.get_model()
#self.raffraichissementlisteimages()
def ajout(self, widget):
FenOuv=Fenetre_Ouvrir(self.liststoreimport,1)
self.liststoreimport=FenOuv.get_model()
#self.raffraichissementlisteimages()
def raffraichissementlisteimages(self):
#self.listeimages.set_model(self.liststoreimport)
self.treeselectionsuppr=self.listeimages.get_selection() #pour récupérer quels fichiers sont selectionnés
self.treeselectionsuppr.set_mode(gtk.SELECTION_MULTIPLE) #Pour pouvoir en selectionner plusieurs
def enlever(self, widget):
self.treeselectionsuppr=self.listeimages.get_selection() #pour récupérer quels fichiers sont selectionnés
self.treeselectionsuppr.set_mode(gtk.SELECTION_MULTIPLE) #Pour pouvoir en selectionner plusieurs
(model, pathlist) = self.treeselectionsuppr.get_selected_rows()
for i in pathlist:
treeiter = model.get_iter(i)
self.liststoreimport.remove(treeiter)
def ttenlever(self, widget):
self.liststoreimport.clear()
def preview(self, widget):
self.taille=(self.spinbuttonlargeurprev.get_value(), self.spinbuttonhauteurprev.get_value())
self.name=donnees.previs_dossier + "/" + "preview.jpg"
item=0
if len(self.liststoreimport)>0:
self.ref=zip(*self.liststoreimport)[0]
for item2 in self.ref:
if item2:
item+=1
if item>1:
self.thread_preview = Thread_Preview(self.taille, self.get_options(), self.get_options_align(), self.liststoreimport)
self.thread_preview.start()
timer = gobject.timeout_add (100, self.pulsate)
break
if item<=1:
self.messageinthebottle(_("Please add or activate at least two images.\n\n Cannot do anything smart with the one or no image."))
def get_options_align(self):
self.options_align=[]
if self.checkbutton_a5_align.get_active():
if self.checkbutton_a5_crop.get_active():
self.options_align.append('-C')
if self.checkbutton_a5_shift.get_active():
self.options_align.append('-i')
if self.checkbutton_a5_field.get_active():
self.options_align.append('-m')
return self.options_align
def get_options(self):
options=["--exposure-weight=" + str(self.spinbuttonexp.get_value()),
"--exposure-mu=" + str(self.spinbuttonmu.get_value()),
"--exposure-sigma=" + str(self.spinbuttonsigma.get_value()),
"--saturation-weight=" + str(self.spinbuttonsat.get_value()),
"--contrast-weight=" + str(self.spinbuttoncont.get_value())]
if self.check_pyramidelevel.get_active():
options.append('-l ' + str(self.spinbuttonlevel.get_value_as_int()))
if self.check_hardmask.get_active():
options.append('--hard-mask')
if self.check_contwin.get_active():
options.append('--contrast-window-size=' + str(self.spinbuttoncontwin.get_value_as_int()))
if self.check_courb.get_active():
if self.check_prctcourb.get_active():
options.append('--contrast-min-curvature=' + str(self.spinbuttoncourb.get_value()) + "%")
else:
options.append('--contrast-min-curvature=' + str(self.spinbuttoncourb.get_value()))
if self.check_detecbord.get_active():
opts='--contrast-edge-scale=' + str(self.spinbuttonEdge.get_value()) + ':'
if self.check_lces.get_active():
opts+=str(self.spinbuttonLceS.get_value()) + '%:'
else:
opts+=str(self.spinbuttonLceS.get_value()) + ':'
if self.check_lcef.get_active():
opts+=str(self.spinbuttonLceF.get_value()) + '%'
else:
opts+=str(self.spinbuttonLceF.get_value()) + ''
options.append(opts)
# + str(self.spinbuttonLceF.get_value()) + '%')
if self.check_ciecam.get_active():
options.append('-c')
if self.check_desatmeth.get_active():
opt={-1:None, 0:"average", 1:'l-star', 2:'lightness', 3:'value', 4:'luminance'}
options.append('--gray-projector=' + opt[self.combobox_desatmet.get_active()])
if not self.checkbuttoncache.get_active():
options.append('-m ' + str(self.spinbuttoncache.get_value_as_int()))
if not self.checkbuttonbloc.get_active():
options.append('-b ' + str(self.spinbuttonbloc.get_value_as_int()))
if not self.checkbuttontaillefinale.get_active():
options.append('-f ' + str(self.spinbuttonlargeurfinale.get_value_as_int()) + 'x' + str(self.spinbuttonhauteurfinale.get_value_as_int()) + 'x' + str(self.spinbuttonxoff.get_value_as_int()) + 'x' + str(self.spinbuttonyoff.get_value_as_int()))
if self.name.endswith(('.tif', '.tiff', '.TIF', '.TIFF')):
tiffopt={0:"NONE", 1:"PACKBITS", 2:"LZW", 3:"DEFLATE"}
options.append("--compression=" + tiffopt[self.combtiff.get_active()])
if self.name.endswith(('.jpg', '.jpeg', '.JPG', '.JPEG')) and (not self.checkbuttonjpegorig.get_active()):
options.append("--compression=" + str(int(self.hscalecomprjpeg.get_value())))
return options
def pulsate(self):
if self.thread_preview.isAlive(): #Tant que le thread est en cours,
self.progressbar.set_text(_("Calculating preview..."))
self.progressbar.pulse() #on fait pulser la barre
return True #et on renvoie True pour que gobject.timeout recommence
else:
self.progressbar.set_fraction(1)
self.progressbar.set_text(_("Preview generated"))
self.imagepreview.set_from_file(donnees.previs_dossier + "/" + "preview.jpg")
return False
def baswitch(self, widget):
if (not int(self.buttonbeforeafter.get_relief())) and (os.path.exists(donnees.previs_dossier + "/preview_.jpg")):
self.buttonbeforeafter.props.relief = gtk.RELIEF_NONE
self.imagepreview.set_from_file(donnees.previs_dossier + "/preview_.jpg")
elif os.path.exists(donnees.previs_dossier + "/preview_.jpg"):
self.buttonbeforeafter.props.relief = gtk.RELIEF_NORMAL
self.imagepreview.set_from_file(donnees.previs_dossier + "/preview.jpg")
def fusion(self,widget):
FenPar=Fenetre_Parcourir()
self.name = FenPar.get_name()
if self.name:
if not re.search('\\.jpeg$|\\.jpg$|\\.tiff$|\\.tif$', self.name, flags=re.IGNORECASE):
self.name+=".jpg"
self.enroute('')
def sendto(self, widget):
self.name=(donnees.previs_dossier + "/sendto.tif")
if not self.check_editor(0):
return
if self.enroute(self.name) == -1:
self.messageinthebottle(_("No preview, no output, no edit.\n\n Game Over."))
return
def messageinthebottle(self, message):
self.messaga=gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=(message))
if self.messaga.run() == gtk.RESPONSE_OK:
self.messaga.destroy()
def get_exif(self, fichier):
tags2=''
tags={}
exifinfo=None
try:
im = Image.open(fichier)
if hasattr( im, '_getexif' ):
exifinfo = im._getexif()
if exifinfo != None:
for tag, value in exifinfo.items():
decoded = TAGS.get(tag, tag)
tags[decoded] = value
if tags.get('Model'): tags2=(_("Model: ") + str(tags['Model']) + "\n")
if tags.get('DateTimeOriginal'): tags2+=(_("Date: ") + str(tags['DateTimeOriginal']) + "\n")
if tags.get('FocalLength'): tags2+=(_("Focal length: ") + str(int(int(tags['FocalLength'][0])/int(tags['FocalLength'][1]))) + "mm \n")
if tags.get('FNumber'): tags2+=(_("Aperture: F/") + str(float(float(tags['FNumber'][0])/float(tags['FNumber'][1]))) + "\n")
if tags.get('ExposureTime'): tags2+=(_("Exposure Time: ") + str(str(tags['ExposureTime'][0]/10) + "/" + str(tags['ExposureTime'][1]/10) + " s. \n"))
except IOError:
print "failed to identify", file
return tags2
def enroute(self, issend):
self.issend=issend
self.liste_images=[]
self.liste_aligned=[]
index = 0
for item in self.liststoreimport:
if item[0]:
self.liste_images.append(item[2])
self.liste_aligned.append(donnees.previs_dossier + "/out" + format(index, "04d") + ".tif")
index += 1
if not Gui.checkbutton_a5_align.get_active():
self.liste_aligned=self.liste_images
if self.liste_images.count(self.name):
self.messageinthebottle(_("Can't overwrite input image!\n\n Please change the output filename."))
return -1
if len(self.liste_images) <= 1:
self.messageinthebottle(_("Please add or activate at least two images.\n\n Cannot do anything smart with the one or no image."))
return -1
command_a=['align_image_stack', '-a', donnees.previs_dossier + '/out'] + self.get_options_align() + self.liste_images
command=[Gui.enfuser, "-o", self.name] + self.get_options() + self.liste_aligned
ProFus=Progress_Fusion(command, command_a, self.liste_aligned, self.issend)
def apropos(self, widget):
self.fen=AproposFen()
def save_settings(self):
conf = ConfigParser.ConfigParser()
conf.add_section('prefs')
# conf.set('prefs', 'w', self.spinbuttonEdge.get_value_as_int())
conf.set('prefs', 'pwidth', self.spinbuttonlargeurprev.get_value_as_int())
conf.set('prefs', 'pheight', self.spinbuttonhauteurprev.get_value_as_int())
conf.set('prefs', 'cachebutton', self.checkbuttoncache.get_active())
conf.set('prefs', 'cachesize', self.spinbuttoncache.get_value_as_int())
conf.set('prefs', 'blocbutton', self.checkbuttonbloc.get_active())
conf.set('prefs', 'blocsize', self.spinbuttonbloc.get_value_as_int())
conf.set('prefs', 'outsize', self.checkbuttontaillefinale.get_active())
conf.set('prefs', 'outwidth', self.spinbuttonlargeurfinale.get_value_as_int())
conf.set('prefs', 'outheight', self.spinbuttonhauteurfinale.get_value_as_int())
conf.set('prefs', 'xoff', self.spinbuttonxoff.get_value_as_int())
conf.set('prefs', 'yoff', self.spinbuttonyoff.get_value_as_int())
conf.set('prefs', 'jpegdef', self.checkbuttonjpegorig.get_active())
conf.set('prefs', 'jpegcompr', int(self.hscalecomprjpeg.get_value()))
conf.set('prefs', 'tiffcomp', str(self.combtiff.get_active()))
conf.set('prefs', 'exif', str(self.checkbuttonexif.get_active()))
conf.set('prefs', 'editor', self.entryedit_field.get_text())
if not os.path.exists(donnees.enfuse_dossier):
os.makedirs(donnees.enfuse_dossier)
conf.write(file(donnees.enfuse_dossier + '/mfusion.cfg', 'w'))
# Also, save accel_map:
# gtk.accel_map_save(self.config_dir + '/accel_map')
return
def put_files_to_the_list(self, fichiers):
self.fichiers=fichiers
self.tags2=''
self.badfiles=[]
for fichier in self.fichiers:
if re.search('\\.jpg$|\\.jpeg$|\\.tiff$|\\.tif$', fichier, flags=re.IGNORECASE):
im = Image.open(fichier)
self.size=im.size
self.tags2 = Gui.get_exif(fichier)
if not self.tags2:
self.tags2=''
self.tooltip=("\n" + _("Filename: ") + os.path.basename(fichier) + "\n"+_("Resolution: ") + str(str(self.size[0]) + "x" + str(self.size[1])) + "\n" + self.tags2)
self.liststoreimport.append([1,os.path.basename(fichier), fichier, gtk.gdk.pixbuf_new_from_file_at_size(fichier, 128, 128), self.tooltip])
else:
self.badfiles.append(fichier)
if len(self.badfiles)>0:
messaga=_("Only JPEG and TIFF files are allowed.\n\nCannot open:\n")
for itz in self.badfiles:
messaga+=itz + "\n"
Gui.messageinthebottle(messaga)
return
####################################################################
###########Classe pour choisir les images a fusionner###############
####################################################################
class Fenetre_Ouvrir:
"""La classe qui ouvre la fenetre de choix de fichiers, et qui retourne le ListStore par la methode get_model"""
def __init__(self,model,bitajout):
"""Lance la fenetre de selection et créé la listsore a partir des fichiers selectionnés"""
self.filtre=gtk.FileFilter()
self.filtre.add_mime_type("image/jpeg")
self.filtre.add_mime_type("image/tiff")
self.liststoreimport=model #on repart de l'ancien modele
if bitajout:
self.fenetre_ouvrir = gtk.FileChooserDialog(_("Add images..."),
None,
gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
self.fenetre_ouvrir.set_select_multiple(True)
self.fenetre_ouvrir.set_current_folder(donnees.default_folder)
self.fenetre_ouvrir.set_filter(self.filtre)
self.fenetre_ouvrir.use_preview = True
self.previewidget = gtk.Image()
self.fenetre_ouvrir.set_preview_widget(self.previewidget)
self.fenetre_ouvrir.connect("update-preview", self.update_thumb_preview, self.previewidget)
else:
self.fenetre_ouvrir = gtk.FileChooserDialog(_("Open images..."),
None,
gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
self.fenetre_ouvrir.set_select_multiple(True)
self.fenetre_ouvrir.set_current_folder(donnees.default_folder)
self.fenetre_ouvrir.set_filter(self.filtre)
self.fenetre_ouvrir.use_preview = True
self.previewidget = gtk.Image()
self.fenetre_ouvrir.set_preview_widget(self.previewidget)
self.fenetre_ouvrir.connect("update-preview", self.update_thumb_preview, self.previewidget)
self.liststoreimport.clear() #On remet le model a 0 (oublie des anciennes images)
if (self.fenetre_ouvrir.run() == gtk.RESPONSE_OK):
self.fichiers = self.fenetre_ouvrir.get_filenames()
self.tags2=''
self.badfiles=[]
donnees.default_file = self.fichiers[0]
Gui.put_files_to_the_list(self.fichiers)
donnees.default_folder=self.fenetre_ouvrir.get_current_folder()
self.fenetre_ouvrir.destroy()
def update_thumb_preview(self, file_chooser, preview):
if not self.fenetre_ouvrir.use_preview:
return
filename = file_chooser.get_preview_filename()
try:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 320, 320)
self.previewidget.set_from_pixbuf(pixbuf)
self.have_preview = True
except:
self.have_preview = False
self.fenetre_ouvrir.set_preview_widget_active(self.have_preview)
return
def get_model(self):
""" Retourne la liststore """
if self.liststoreimport:
return self.liststoreimport
else:
return None
#####################################################################
#########Classe pour la fenetre pour choisir le fichier final########
#####################################################################
class Fenetre_Parcourir:
"""La classe qui ouvre la fenetre de choix pour enregistrer le fichier"""
def __init__(self):
self.fenetre_ouvrir = gtk.FileChooserDialog(_("Save file..."),
None,
gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK))
self.fenetre_ouvrir.set_current_folder(donnees.default_folder)
# self.fenetre_ouvrir.set_filename(donnees.default_file)
self.fenetre_ouvrir.set_current_name('output.jpg')
self.fenetre_ouvrir.set_do_overwrite_confirmation(True)
if (self.fenetre_ouvrir.run() == gtk.RESPONSE_OK):
self.resultat=self.fenetre_ouvrir.get_filename()
self.fenetre_ouvrir.destroy()
def get_name(self):
try:
return self.resultat
except AttributeError:
return ""
#####################################################################
#########Thread pour la prévisualisation#############################
#####################################################################
class Thread_Preview(threading.Thread):
def __init__(self, taille, options, options_align, liste):
threading.Thread.__init__ (self)
self.taille=taille
self.options=options
self.liste=liste
self.options_align=options_align
def run(self):
images_a_fusionner=[]
images_a_align=[]
index = 0
global session_images_bak
global session_options_bak
for item in self.liste:
if item[0]:
chemin_miniature=creer_miniature(item[2],(int(self.taille[0]), int(self.taille[1])))
images_a_align.append(chemin_miniature)
images_a_fusionner.append(donnees.previs_dossier + "/test" + format(index, "04d") + ".tif")
index += 1
if (len(images_a_fusionner))<=1:
Gui.messageinthebottle(_("Please add two or more images.\n\n Cannot do anything smart with the one image."))
return
if not Gui.checkbutton_a5_align.get_active():
images_a_fusionner=images_a_align
if os.path.exists(donnees.previs_dossier + "/preview.jpg"):
shutil.copy(donnees.previs_dossier + "/" + "preview.jpg", donnees.previs_dossier + "/" + "preview_.jpg")
if Gui.checkbutton_a5_align.get_active() and \
(len(images_a_align) != len(session_images_bak) \
or len(self.options_align) != len(session_options_bak) \
or len(list(axz for axz in images_a_align if axz not in session_images_bak)) \
or len(list(axz2 for axz2 in self.options_align if axz2 not in session_options_bak))):
command=["align_image_stack", "-a", donnees.previs_dossier + "/test"] + self.options_align + images_a_align
Gui.statusbar.push(15, _(":: Align photos..."))
preview_process=subprocess.Popen(command, stdout=subprocess.PIPE)
preview_process.wait()
session_options_bak=self.options_align
session_images_bak=images_a_align
Gui.statusbar.pop(15)
Gui.statusbar.push(15, _(":: Fusion photos..."))
command=[Gui.enfuser, "-o", donnees.previs_dossier + "/" + "preview.jpg"] + self.options + images_a_fusionner
preview_process=subprocess.Popen(command, stdout=subprocess.PIPE)
preview_process.wait()
Gui.statusbar.pop(15)
#######################################################################
#########Fenetre de progression lors de la fusion finale###############
#######################################################################
class Progress_Fusion:
def __init__(self, command, command_a, liste_aligned, issend):
self.progress = gtk.glade.XML(fname=UI + "Progress.glade", domain=APP)
self.progress_win = self.progress.get_widget("dialog1")
self.progress_label = self.progress.get_widget("progress_label")
self.info_label = self.progress.get_widget("info_label")
self.progress_bar = self.progress.get_widget("progressbar1")
self.progress_stop_button = self.progress.get_widget("stop_button")
self.dic1 = {"on_stop_button_clicked" : self.close_progress,
"on_dialog1_destroy" : self.close_progress}
self.progress.signal_autoconnect(self.dic1)
self.info_label.set_text(_('Fusion images...'))
self.thread_fusion = Thread_Fusion(command, command_a, liste_aligned, issend) #On prepare le thread qui va faire tout le boulot
self.thread_fusion.start() #On le lance
timer = gobject.timeout_add (100, self.pulsate)
def pulsate(self):
if self.thread_fusion.isAlive(): #Tant que le thread est en cours,
self.progress_bar.set_text(_("Fusion, please wait..."))
self.progress_bar.pulse() #on fait pulser la barre
return True #et on renvoie True pour que gobject.timeout recommence
else:
self.progress_bar.set_fraction(1)
self.progress_bar.set_text(_("Fused !"))
self.close_progress(self)
return False
def close_progress(self, widget):
self.progress_win.destroy()
##############################################################################
###########Thread de fusion des vraies images#################################
##############################################################################
class Thread_Fusion(threading.Thread):
def __init__(self, command, command_a, liste_aligned, issend):
threading.Thread.__init__ (self)
self.command=command
self.command_a=command_a
self.issend=issend
self.liste_aligned=liste_aligned
def run(self):
if Gui.checkbutton_a5_align.get_active():
align_process=subprocess.Popen(self.command_a, stdout=subprocess.PIPE)
align_process.wait()
fusion_process=subprocess.Popen(self.command, stdout=subprocess.PIPE)
fusion_process.wait()
# fusion_process=subprocess.call(self.command)
if Gui.checkbuttonexif.get_active():
exif_copy = subprocess.Popen(["exiftool", "-tagsFromFile", Gui.liste_images[0], "-overwrite_original", Gui.name])
exif_copy.wait()
if len(self.issend) > 0:
subprocess.Popen([Gui.entryedit_field.get_text(), self.issend], stdout=subprocess.PIPE)
########################################
#### Classe de la fenêtre a propos ####
########################################
class AproposFen:
def __init__(self):
# self.about = gtk.glade.XML(donnees.install_dossier + "/Apropos.glade", domain=APP)
# self.aboutdialog = self.about.get_widget("aboutdialog1")
self.aboutdialog = gtk.AboutDialog()
self.aboutdialog.set_name("MacroFusion")
self.aboutdialog.set_modal(True)
self.aboutdialog.set_position(gtk.WIN_POS_CENTER)
self.aboutdialog.set_version(__VERSION__)
self.aboutdialog.set_comments('A GTK Gui for the excellent Enfuse.\n Based on EnfuseGui by Chez Gholyo.\n\n2011 (c) Dariusz Duma\n')
# self.aboutdialog.set_copyright(__COPYRIGHT__)
self.aboutdialog.set_website(__WEBSITE__)
self.pixbuf=gtk.gdk.pixbuf_new_from_file(IMG + "macrofusion.png")
self.aboutdialog.set_logo(self.pixbuf)
self.aboutdialog.connect("response", self.close_about)
self.aboutdialog.show()
def close_about(self, widget, event):
self.aboutdialog.destroy()
###########################################################
#### Initialisation et appel de la classe principale ####
###########################################################
if __name__ == "__main__":
donnees=Donnees() #Variables
Gui = Interface() #Interface
if (len(sys.argv)>1): #Init with given files
fichiers=sys.argv[1:]
Gui.put_files_to_the_list(fichiers)
# if len(Gui.liststoreimport)==0:
# Gui.messageinthebottle(_("\nCan work only with JPEG or TIFF files."))
gtk.main() #The rest