#!/usr/bin/env python # -*- coding: UTF8 -*- #******************************************************************************\ #* $Source$ #* $Id$ #* #* Copyright (C) 2006-2011, Jérôme Kieffer #* Conception : Jérôme KIEFFER, Mickael Profeta & Isabelle Letard #* Licence GPL v2 #* #* 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., 675 Mass Ave, Cambridge, MA 02139, USA. #* #*****************************************************************************/ # version 0.3 : 28/10/2004 # version 1.1 : 11/01/2005 # version 1.2 : 13/02/2006 : includes a configuration file # version 1.3 : correction d'un bug quant à l'allocation de mémoire dans libjpeg # version 1.4 : 04/2006 : passage du pre-processing dans un bibliotheque externe utilisant le design pattern # version 1.75: using some cache to speed-up display # # Liste des dépendances : python, PIL, Glade-2 # Exiftran existe en version windows maintenant ... nous utilisons une verison modifiée ...!!!! # #todo liste des fonctions a implemanter .... # - se passer de exiftran # - la version windows et la version mac # - faire une doc décente. # - proposer d'exporter toutes les photos dans un seul répertoire (pas de jour) """ Selector is the graphical (GUI) interface part of the Imagizer project. """ import os, locale, random, gc, time, shutil, sys, logging import os.path as op try: import ImageFile except: raise ImportError("Selector needs PIL: Python Imaging Library\n PIL is available from http://www.pythonware.com/products/pil/") import imagizer logger = logging.getLogger("imagizer.selector") from imagizer import installdir, listConfigurationFiles from imagizer.encoding import unicode2ascii from imagizer.config import Config from imagizer.imagizer import unifiedglade, CopySelected, ProcessSelected, RangeTout from imagizer.photo import Photo, RawImage from imagizer.fileutils import recursive_delete, smartSize, mkdir, findFiles from imagizer.parser import AttrFile from imagizer.dirchooser import WarningSc try: import pygtk ; pygtk.require('2.0') import gtk import gtk.glade as GTKglade except ImportError: raise ImportError("Selector needs pygtk and glade-2 available from http://www.pygtk.org/") config = Config() kpix = 3 * config.ScaledImages["Size"] ** 2 / 4000 if kpix > 64: ImageFile.MAXBLOCK = 1000 * kpix if config.ImageCache > 1: from imagizer.imagecache import ImageCache imageCache = ImageCache(maxSize=config.ImageCache) else: imageCache = None if os.getenv("LANGUAGE"): try: locale.setlocale(locale.LC_ALL, os.getenv("LANGUAGE")) except: locale.setlocale(locale.LC_ALL, config.Locale) else: locale.setlocale(locale.LC_ALL, config.Locale) ################################################################################ # ##### FullScreen Interface ##### ################################################################################ class FullScreenInterface(object): def __init__(self, AllJpegs=[], first=0, selected=[], mode="FullScreen"): self.image = None self.AllJpegs = AllJpegs self.Selected = [] self.RandomList = [] self.iCurrentImg = first self.timestamp = time.time() for i in selected: if i in self.AllJpegs: self.Selected.append(i) logger.info("Initialization of the fullscreen GUI") self.xml = GTKglade.XML(unifiedglade, root="FullScreen") self.ShowImage() self.flush_event_queue() self.xml.get_widget("FullScreen").maximize() self.flush_event_queue() self.ShowImage() self.flush_event_queue() self.xml.get_widget("FullScreen").connect("key_press_event", self.keypressed) self.xml.signal_connect('on_FullScreen_destroy', self.destroy) if mode == "SlideShow": self.SlideShow() self.QuitSlideShow = True gtk.main() def flush_event_queue(self): """Updates the GTK GUI before coming back to the gtk.main()""" while gtk.events_pending(): gtk.main_iteration() def ShowImage(self): """Show the image in the given GtkImage widget and set up the exif tags in the GUI""" if imageCache: self.image = imageCache.get(self.AllJpegs[self.iCurrentImg], Photo(self.AllJpegs[self.iCurrentImg])) else: self.image = Photo(self.AllJpegs[self.iCurrentImg]) X, Y = self.xml.get_widget("FullScreen").get_size() logger.debug("Size of image on screen: %sx%s" % (X, Y)) pixbuf = self.image.show(X, Y) self.xml.get_widget("image793").set_from_pixbuf(pixbuf) del pixbuf gc.collect() if self.AllJpegs[self.iCurrentImg] in self.Selected: sel = "[Selected]" else: sel = "" self.xml.get_widget("FullScreen").set_title("Selector : %s %s" % (self.AllJpegs[self.iCurrentImg], sel)) def keypressed(self, widget, event, *args): """keylogger""" key = gtk.gdk.keyval_name (event.keyval) #print "Keyval = %s\t Key = %s"%(event.keyval,key) if key == "Page_Up": self.previousJ() elif key == "Page_Down": self.nextJ() elif key == "d": self.SlideShow() elif key == "e": self.QuitSlideShow = True self.destroy() elif key == "Right": self.turnRight() elif key == "Left": self.turnLeft() elif key == "f": self.NormalScreen() elif key == "Down": self.next1() elif key == "Up": self.previous1() elif key == "s": self.select_Shortcut() elif key in ["Escape", "Q"]: self.QuitSlideShow = True elif key in ["KP_Add", "plus"]: config.SlideShowDelay += 1 elif key in ["KP_Subtract", "minus"]: config.SlideShowDelay -= 1 if config.SlideShowDelay < 0:config.SlideShowDelay = 0 def turnRight(self, *args): """rotate the current image clockwise""" self.image.Rotate(90) self.ShowImage() def turnLeft(self, *args): """rotate the current image clockwise""" self.image.Rotate(270) self.ShowImage() def destroy(self, *args): """destroy clicked by user""" SaveSelected(self.Selected) gtk.main_quit() config.GraphicMode = "Quit" def NormalScreen(self, *args): """Switch to Normal mode""" self.xml.get_widget("FullScreen").destroy() config.GraphicMode = "Normal" gtk.main_quit() def next1(self, *args): """Switch to the next image""" self.iCurrentImg = (self.iCurrentImg + 1) % len(self.AllJpegs) self.ShowImage() def previous1(self, *args): """Switch to the previous image""" self.iCurrentImg = (self.iCurrentImg - 1) % len(self.AllJpegs) self.ShowImage() def nextJ(self, *args): """Switch to the first image of the next day""" jour = op.dirname(self.AllJpegs[self.iCurrentImg]) for i in range(self.iCurrentImg, len(self.AllJpegs)): jc = op.dirname(self.AllJpegs[i]) if jc > jour: break self.iCurrentImg = i self.ShowImage() def previousJ(self, *args): """Switch to the first image of the previous day""" if self.iCurrentImg == 0: return jour = op.dirname(self.AllJpegs[self.iCurrentImg]) for i in range(self.iCurrentImg - 1, -1, -1): jc = op.dirname(self.AllJpegs[i]) jd = op.dirname(self.AllJpegs[i - 1]) if (jc < jour) and (jd < jc): break self.iCurrentImg = i self.ShowImage() def select_Shortcut(self, *args): """Select or unselect the image""" if self.AllJpegs[self.iCurrentImg] not in self.Selected: self.Selected.append(self.AllJpegs[self.iCurrentImg]) sel = "[Selected]" else: self.Selected.remove(self.AllJpegs[self.iCurrentImg]) sel = "" self.Selected.sort() self.xml.get_widget("FullScreen").set_title("Selector : %s %s" % (self.AllJpegs[self.iCurrentImg], sel)) def SlideShow(self): """Starts the slide show""" self.QuitSlideShow = False self.RandomList = [] while not self.QuitSlideShow: # print "Delai= %s; type = %s"%(config.SlideShowDelay,config.SlideShowType) if config.SlideShowType == "chronological": self.iCurrentImg = (self.iCurrentImg + 1) % len(self.AllJpegs) elif config.SlideShowType == "antichronological": self.iCurrentImg = (self.iCurrentImg - 1) % len(self.AllJpegs) elif config.SlideShowType == "random": if len(self.RandomList) == 0: self.RandomList = range(len(self.AllJpegs)) random.shuffle(self.RandomList) self.iCurrentImg = self.RandomList.pop() if imageCache: self.image = imageCache.get(self.AllJpegs[self.iCurrentImg], Photo(self.AllJpegs[self.iCurrentImg])) else: self.image = Photo(self.AllJpegs[self.iCurrentImg]) self.image.readExif() if self.image.metadata["Rate"] < config.SlideShowMinRating: self.flush_event_queue() continue now = time.time() if now - self.timestamp < config.SlideShowDelay: time.sleep(config.SlideShowDelay - now + self.timestamp) self.ShowImage() self.flush_event_queue() self.timestamp = time.time() ################################################################################ # ##### Normal Interface ##### ################################################################################ class Interface(object): """ class interface that manages the GUI using Glade-2 """ def __init__(self, AllJpegs=[], first=0, selected=[], mode="Default"): self.AllJpegs = AllJpegs self.Selected = [] for i in selected: if i in self.AllJpegs: self.Selected.append(i) self.iCurrentImg = first self.Xmin = 350 self.image = None self.strCurrentTitle = "" self.iCurrentRate = 0 logger.info("Initialization of the windowed graphical interface ...") self.xml = GTKglade.XML(unifiedglade, root="Principale") self.xml.get_widget("Principale").set_size_request(config.ScreenSize + self.Xmin, config.ScreenSize) self.xml.get_widget("Principale").resize(config.ScreenSize + self.Xmin, config.ScreenSize) self.xml.get_widget("Logo").set_from_pixbuf(gtk.gdk.pixbuf_new_from_file(op.join(installdir, "logo.png"))) self.flush_event_queue() if config.AutoRotate: i = 1 else: i = 0 self.xml.get_widget("Autorotate").set_active(i) if config.Filigrane: i = 1 else: i = 0 self.xml.get_widget("Filigrane").set_active(i) if config.ScreenSize == 300: self.xml.get_widget("t300").set_active(1) self.xml.get_widget("t600").set_active(0) self.xml.get_widget("t900").set_active(0) self.xml.get_widget("tauto").set_active(0) elif config.ScreenSize == 600: self.xml.get_widget("t300").set_active(0) self.xml.get_widget("t600").set_active(1) self.xml.get_widget("t900").set_active(0) self.xml.get_widget("tauto").set_active(0) elif config.ScreenSize == 900: self.xml.get_widget("t300").set_active(0) self.xml.get_widget("t600").set_active(0) self.xml.get_widget("t900").set_active(1) self.xml.get_widget("tauto").set_active(0) else: self.xml.get_widget("t300").set_active(0) self.xml.get_widget("t600").set_active(0) self.xml.get_widget("t900").set_active(0) self.xml.get_widget("tauto").set_active(1) if config.NbrPerPage == 9: self.xml.get_widget("9PerPage").set_active(1) self.xml.get_widget("12PerPage").set_active(0) self.xml.get_widget("16PerPage").set_active(0) self.xml.get_widget("20PerPage").set_active(0) self.xml.get_widget("25PerPage").set_active(0) self.xml.get_widget("30PerPage").set_active(0) elif config.NbrPerPage == 12: self.xml.get_widget("9PerPage").set_active(0) self.xml.get_widget("12PerPage").set_active(1) self.xml.get_widget("16PerPage").set_active(0) self.xml.get_widget("20PerPage").set_active(0) self.xml.get_widget("25PerPage").set_active(0) self.xml.get_widget("30PerPage").set_active(0) elif config.NbrPerPage == 16: self.xml.get_widget("9PerPage").set_active(0) self.xml.get_widget("12PerPage").set_active(0) self.xml.get_widget("16PerPage").set_active(1) self.xml.get_widget("20PerPage").set_active(0) self.xml.get_widget("25PerPage").set_active(0) self.xml.get_widget("30PerPage").set_active(0) elif config.NbrPerPage == 20: self.xml.get_widget("9PerPage").set_active(0) self.xml.get_widget("12PerPage").set_active(0) self.xml.get_widget("16PerPage").set_active(0) self.xml.get_widget("20PerPage").set_active(1) self.xml.get_widget("25PerPage").set_active(0) self.xml.get_widget("30PerPage").set_active(0) elif config.NbrPerPage == 25: self.xml.get_widget("9PerPage").set_active(0) self.xml.get_widget("12PerPage").set_active(0) self.xml.get_widget("16PerPage").set_active(0) self.xml.get_widget("20PerPage").set_active(0) self.xml.get_widget("25PerPage").set_active(1) self.xml.get_widget("30PerPage").set_active(0) elif config.NbrPerPage == 30: self.xml.get_widget("9PerPage").set_active(0) self.xml.get_widget("12PerPage").set_active(0) self.xml.get_widget("16PerPage").set_active(0) self.xml.get_widget("20PerPage").set_active(0) self.xml.get_widget("25PerPage").set_active(0) self.xml.get_widget("30PerPage").set_active(1) if config.Interpolation == 0: self.xml.get_widget("VLowQ").set_active(1) self.xml.get_widget("LowQ").set_active(0) self.xml.get_widget("HiQ").set_active(0) self.xml.get_widget("VHiQ").set_active(0) elif config.Interpolation == 1: self.xml.get_widget("VLowQ").set_active(0) self.xml.get_widget("LowQ").set_active(1) self.xml.get_widget("HiQ").set_active(0) self.xml.get_widget("VHiQ").set_active(0) elif config.Interpolation == 2: self.xml.get_widget("VLowQ").set_active(0) self.xml.get_widget("LowQ").set_active(0) self.xml.get_widget("HiQ").set_active(1) self.xml.get_widget("VHiQ").set_active(0) elif config.Interpolation == 3: self.xml.get_widget("VLowQ").set_active(0) self.xml.get_widget("LowQ").set_active(0) self.xml.get_widget("HiQ").set_active(0) self.xml.get_widget("VHiQ").set_active(1) self.ShowImage() self.flush_event_queue() dictHandlers = { 'on_Principale_destroy': self.destroy, 'on_arreter1_activate': self.destroy, 'on_suivant_clicked': self.next1, 'on_precedant_clicked': self.previous1, 'on_droite_clicked': self.turnRight, 'on_gauche_clicked': self.turnLeft, # 'on_Selection_toggled': self.select, # 'on_selectionner_activate': self.select_Shortcut, 'on_selectionner_activate': self.select_shortcut, 'on_Selection_toggled':self.select, 'on_photo_client_event': self.next1, 'on_About_activate': self.about, 'on_quitter1_activate': self.die, 'on_nommer1_activate': self.renameDay, 'on_executer1_activate': self.copyAndResize, 'on_Synchronize_activate': self.synchronize, 'on_copie_et_grave1_activate': self.burn, 'on_vers_page_web2_activate': self.toWeb, 'on_vide_selection1_activate': self.emptySelected, 'on_copie1_activate': self.copy, 'on_importer1_activate': self.importImages, 'on_poubelle_activate': self.trash, 'on_Poubelle_clicked': self.trash, 'on_Gimp_clicked': self.gimp, 'on_Reload_clicked': self.reload_img, 'on_Filter_clicked': self.filter_CM, 'on_enregistrerP_activate': self.savePref, 'on_Autorotate_activate': self.setAutoRotate, 'on_Filigrane_activate': self.setFiligrane, 'on_taille_media_activate': self.defineMediaSize, 'on_tauto_activate': self.sizeCurrent, 'on_t300_activate': self.setSize300, 'on_t600_activate': self.setSize600, 'on_t900_activate': self.setSize900, 'on_VLowQ_activate': self.setInterpolNearest, 'on_LowQ_activate': self.setInterpolTiles, 'on_HiQ_activate': self.setInterpolBilin, 'on_VHiQ_activate': self.setInterpolHyperbol, 'on_9PerPage_activate': self.set9PerPage, 'on_12PerPage_activate': self.set12PerPage, 'on_16PerPage_activate': self.set16PerPage, 'on_20PerPage_activate': self.set20PerPage, 'on_25PerPage_activate': self.set25PerPage, 'on_30PerPage_activate': self.set30PerPage, 'on_configurer_dioporama_activate': self.slideShowSetup, 'on_enregistrerS_activate': self.saveSelection, 'on_chargerS_activate': self.loadSelection, 'on_inverserS_activate': self.invertSelection, 'on_aucun1_activate': self.selectNone, 'on_TouS_activate': self.selectAll, 'on_taille_selection_activate': self.calculateSize, 'on_media_apres_activate': self.selectNewerMedia, 'on_media_avant_activate': self.SelectOlderMedia, 'on_precedentI_activate': self.previous1, 'on_suivantI_activate': self.next1, 'on_premierI_activate': self.first, 'on_dernierI_activate': self.last, 'on_plus_10_activate': self.next10, 'on_moins_10_activate': self.previous10, 'on_indexJ_activate': self.indexJ, 'on_precedentJ_activate': self.previousJ, 'on_suivantJ_activate': self.nextJ, 'on_premierJ_activate': self.firstJ, 'on_dernierJ_activate': self.lastJ, 'on_precedentS_activate': self.previousS, 'on_suivantS_activate': self.nextS, 'on_premierS_activate': self.firstS, 'on_dernierS_activate': self.lastS, 'on_precedentNS_activate': self.previousNS, 'on_suivantNS_activate': self.nextNS, 'on_premierNS_activate': self.firstNS, 'on_dernierNS_activate': self.lastNS, 'on_premierT_activate': self.firstT, 'on_precedentT_activate': self.previousT, 'on_suivantT_activate': self.nextT, 'on_dernierT_activate': self.lastT, 'on_premierNT_activate': self.firstNT, 'on_precedentNT_activate': self.previousNT, 'on_suivantNT_activate': self.nextNT, 'on_dernierNT_activate': self.lastNT, 'on_fullscreen_activate': self.FullScreen, 'on_lance_diaporama_activate': self.SlideShow, } self.xml.signal_autoconnect(dictHandlers) self.ShowImage() self.flush_event_queue() gtk.main() def flush_event_queue(self): """Updates the GTK GUI before coming back to the gtk.main()""" # self.xml.show_all() while gtk.events_pending(): gtk.main_iteration() def settitle(self): """Set the new title of the image""" logger.debug("Interface.settitle") newtitle = self.xml.get_widget("Titre").get_text() newRate = float(self.xml.get_widget("Rate").get_value()) if (newtitle != self.strCurrentTitle) or (newRate != self.iCurrentRate): self.image.name(newtitle, newRate) def ShowImage(self): """Show the image in the given GtkImage widget and set up the exif tags in the GUI""" logger.debug("Interface.showImage") if imageCache: self.image = imageCache.get(self.AllJpegs[self.iCurrentImg], Photo(self.AllJpegs[self.iCurrentImg])) else: self.image = Photo(self.AllJpegs[self.iCurrentImg]) X, Y = self.xml.get_widget("Principale").get_size() logger.debug("Size of the image on screen: %sx%s" % (X, Y)) if X <= self.Xmin : X = self.Xmin + config.ScreenSize pixbuf = self.image.show(X - self.Xmin, Y) self.xml.get_widget("photo").set_from_pixbuf(pixbuf) del pixbuf gc.collect() data = self.image.readExif() # if data.has_key("Orientation"): data.pop("Orientation") if "Rate" in data: self.iCurrentRate = int(float(data["Rate"])) self.xml.get_widget("Rate").set_value(self.iCurrentRate) data.pop("Rate") else: self.iCurrentRate = 0 for i in data: self.xml.get_widget(i).set_text(data[i]) self.xml.get_widget("Principale").set_title("Selector : %s" % self.AllJpegs[self.iCurrentImg]) self.xml.get_widget("Selection").set_active((self.AllJpegs[self.iCurrentImg] in self.Selected)) self.strCurrentTitle = data["Titre"] def next1(self, *args): """Switch to the next image""" logger.debug("Interface.next1") self.settitle() self.iCurrentImg = (self.iCurrentImg + 1) % len(self.AllJpegs) self.ShowImage() def next10(self, *args): """Switch forward of 10 images """ logger.debug("Interface.next10") self.settitle() self.iCurrentImg = self.iCurrentImg + 10 if self.iCurrentImg > len(self.AllJpegs):self.iCurrentImg = len(self.AllJpegs) - 1 self.ShowImage() def previous1(self, *args): """Switch to the previous image""" logger.debug("Interface.previous") self.settitle() self.iCurrentImg = (self.iCurrentImg - 1) % len(self.AllJpegs) self.ShowImage() def previous10(self, *args): """Switch 10 images backward""" logger.debug("Interface.previous10") self.settitle() self.iCurrentImg = self.iCurrentImg - 10 if self.iCurrentImg < 0: self.iCurrentImg = 0 self.ShowImage() def first(self, *args): """switch to the first image""" logger.debug("Interface.first") self.settitle() self.iCurrentImg = 0 self.ShowImage() def last(self, *args): """switch to the last image""" logger.debug("Interface.last") self.settitle() self.iCurrentImg = len(self.AllJpegs) - 1 self.ShowImage() def turnRight(self, *args): """rotate the current image clockwise""" logger.debug("Interface.turnRight") self.settitle() self.image.Rotate(90) self.ShowImage() def turnLeft(self, *args): """rotate the current image counterclockwise""" logger.debug("Interface.turnLeft") self.settitle() self.image.Rotate(270) self.ShowImage() def trash(self, *args): """Send the current file to the trash""" logger.debug("Interface.trash") self.settitle() if self.AllJpegs[self.iCurrentImg] in self.Selected:self.Selected.remove(self.AllJpegs[self.iCurrentImg]) self.AllJpegs.remove(self.AllJpegs[self.iCurrentImg]) self.image.Trash() self.iCurrentImg = self.iCurrentImg % len(self.AllJpegs) self.ShowImage() def gimp(self, *args): """Edit the current file with the Gimp""" logger.debug("Interface.gimp") self.settitle() filename = self.AllJpegs[self.iCurrentImg] base, ext = op.splitext(filename) newname = base + "-Gimp" + ext if not newname in self.AllJpegs: self.AllJpegs.append(newname) self.AllJpegs.sort() self.iCurrentImg = self.AllJpegs.index(newname) newnamefull = op.join(config.DefaultRepository, newname) shutil.copy(op.join(config.DefaultRepository, filename), newnamefull) os.chmod(newnamefull, config.DefaultFileMode) os.system("%s %s &" % (config.Gimp, newnamefull)) self.ShowImage() self.image.RemoveFromCache() def reload_img(self, *args): """Remove image from cache and reloads it""" logger.debug("Interface.gimp") self.settitle() filename = self.AllJpegs[self.iCurrentImg] self.image = Photo(filename) if imageCache: imageCache[filename] = self.image self.ShowImage() def filter_CM(self, *args): """Filter the current image with a contrast mask""" logger.debug("Interface.filter_CM") self.settitle() filename = self.AllJpegs[self.iCurrentImg] base, ext = op.splitext(filename) newname = base + "-Filtered" + ext if not newname in self.AllJpegs: self.AllJpegs.append(newname) self.AllJpegs.sort() self.iCurrentImg = self.AllJpegs.index(newname) if imageCache: myPhoto = imageCache.get(filename, Photo(filename)) else: myPhoto = Photo(filename) myPhoto.contrastMask(newname) self.ShowImage() def select_shortcut(self, *args): """Select or unselect the image (not directly clicked on the toggle button)""" logger.debug("Interface.select_shortcut") etat = not(self.xml.get_widget("Selection").get_active()) self.xml.get_widget("Selection").set_active(etat) def select(self, *args): """Select or unselect the image (directly clicked on the toggle button)""" logger.debug("Interface.select") # self.settitle() etat = self.xml.get_widget("Selection").get_active() if etat and (self.AllJpegs[self.iCurrentImg] not in self.Selected): self.Selected.append(self.AllJpegs[self.iCurrentImg]) if not(etat) and (self.AllJpegs[self.iCurrentImg] in self.Selected): self.Selected.remove(self.AllJpegs[self.iCurrentImg]) self.Selected.sort() self.xml.get_widget("Selection").set_active(etat) if (self.image.metadata["Rate"] == 0) and etat: self.image.metadata["Rate"] = config.DefaultRatingSelectedImage self.xml.get_widget("Rate").set_value(config.DefaultRatingSelectedImage) self.settitle() def destroy(self, *args): """destroy clicked by user""" logger.debug("Interface.destroy") self.settitle() gtk.main_quit() config.GraphicMode = "Quit" def FullScreen(self, *args): """Switch to fullscreen mode""" logger.debug("Interface.fullscreen") self.settitle() self.xml.get_widget("Principale").destroy() config.GraphicMode = "FullScreen" gtk.main_quit() def SlideShow(self, *args): """Switch to fullscreen mode and starts the SlideShow""" logger.debug("Interface.slideshow") self.settitle() self.xml.get_widget("Principale").destroy() config.GraphicMode = "SlideShow" gtk.main_quit() def copyAndResize(self, *args): """lauch the copy of all selected files then scale them to generate web pages""" logger.debug("Interface.copyAndResize") self.settitle() #TODO: go through MVC ProcessSelected(self.Selected) self.Selected = [] self.xml.get_widget("Selection").set_active((self.AllJpegs[self.iCurrentImg] in self.Selected)) logger.info("Interface.copyAndResize: Done") def toWeb(self, *args): """lauch the copy of all selected files then scale and finaly copy them to the generator-repository and generate web pages""" logger.debug("Interface.toWeb") self.settitle() ProcessSelected(self.Selected) self.Selected = [] self.xml.get_widget("Selection").set_active((self.AllJpegs[self.iCurrentImg] in self.Selected)) SelectedDir = op.join(config.DefaultRepository, config.SelectedDirectory) out = os.system(config.WebServer.replace("$WebRepository", config.WebRepository).replace("$Selected", SelectedDir)) if out != 0 : print "Error n° : %i" % out logger.info("Interface.toWeb: Done") def emptySelected(self, *args): """remove all the files in the "Selected" folder""" SelectedDir = op.join(config.DefaultRepository, config.SelectedDirectory) for dirs in os.listdir(SelectedDir): curfile = op.join(SelectedDir, dirs) if op.isdir(curfile): recursive_delete(curfile) else: os.remove(curfile) print "Done" def copy(self, *args): """lauch the copy of all selected files""" self.settitle() CopySelected(self.Selected) self.Selected = [] self.xml.get_widget("Selection").set_active((self.AllJpegs[self.iCurrentImg] in self.Selected)) print "Done" def burn(self, *args): """lauch the copy of all selected files then burn a CD according to the configuration file""" logger.debug("Interface.burn") self.settitle() CopySelected(self.Selected) self.Selected = [] #TODO : MVC self.xml.get_widget("Selection").set_active((self.AllJpegs[self.iCurrentImg] in self.Selected)) SelectedDir = op.join(config.DefaultRepository, config.SelectedDirectory) out = os.system(config.Burn.replace("$Selected", SelectedDir)) if out != 0 : print "Error n° : %i" % out logger.info("Interface.burn: Done") def die(self, *args): """you wanna leave the program ??""" logger.debug("Interface.die") self.settitle() dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, "Voulez vous vraiment quitter ce programme ?") result = dialog.run() dialog.destroy() if result == gtk.RESPONSE_OK: SaveSelected(self.Selected) gtk.main_quit() config.GraphicMode = "Quit" def saveSelection(self, *args): """Saves all the selection of photos """ logger.debug("Interface.saveSelection") self.settitle() SaveSelected(self.Selected) def loadSelection(self, *args): """Load a previously saved selection of photos """ logger.debug("Interface.loadSelection") self.settitle() self.Selected = LoadSelected() for i in self.Selected: if not(i in self.AllJpegs): self.Selected.remove(i) self.xml.get_widget("Selection").set_active(self.AllJpegs[self.iCurrentImg] in self.Selected) def selectAll(self, *args): """Select all photos for processing""" logger.debug("Interface.selectAll") self.settitle() self.Selected = self.AllJpegs self.xml.get_widget("Selection").set_active(True) def selectNone(self, *args): """Select NO photos and empty selection""" logger.debug("Interface.selectNone") self.settitle() self.Selected = [] self.xml.get_widget("Selection").set_active(False) def invertSelection(self, *args): """Invert the selection of photos """ logger.debug("Interface.invertSelection") self.settitle() temp = self.AllJpegs[:] for i in self.Selected: temp.remove(i) self.Selected = temp self.xml.get_widget("Selection").set_active(self.AllJpegs[self.iCurrentImg] in self.Selected) def about(self, *args): """display a copyright message""" logger.debug("Interface.about clicked") self.settitle() msg = "Selector vous permet de mélanger, de sélectionner et de tourner \ndes photos provenant de plusieurs sources.\nÉcrit par %s <%s>\nVersion %s" % (imagizer.__author__, imagizer.__contact__, imagizer.__version__) MessageError(msg.decode("UTF8"), Message=gtk.MESSAGE_INFO) def nextJ(self, *args): """Switch to the first image of the next day""" logger.debug("Interface.nextJ clicked") self.settitle() jour = op.dirname(self.AllJpegs[self.iCurrentImg]) for i in range(self.iCurrentImg, len(self.AllJpegs)): jc = op.dirname(self.AllJpegs[i]) if jc > jour: break self.iCurrentImg = i self.ShowImage() def previousJ(self, *args): """Switch to the first image of the previous day""" logger.debug("Interface.previousJ clicked") self.settitle() if self.iCurrentImg == 0: return jour = op.dirname(self.AllJpegs[self.iCurrentImg]) for i in range(self.iCurrentImg - 1, -1, -1): jc = op.dirname(self.AllJpegs[i]) jd = op.dirname(self.AllJpegs[i - 1]) if (jc < jour) and (jd < jc): break self.iCurrentImg = i self.ShowImage() def firstJ(self, *args): """switch to the first image of the first day""" logger.debug("Interface.firstJ clicked") self.settitle() self.iCurrentImg = 0 self.ShowImage() def lastJ(self, *args): """switch to the first image of the last day""" logger.debug("Interface.lastJ clicked") self.settitle() lastday = op.dirname(self.AllJpegs[-1]) for i in range(len(self.AllJpegs) - 1, -1, -1): jc = op.dirname(self.AllJpegs[i]) jd = op.dirname(self.AllJpegs[i - 1]) if (jc == lastday) and (jd < jc): break self.iCurrentImg = i self.ShowImage() def firstS(self, *args): """switch to the first image selected""" logger.debug("Interface.firstS clicked") self.settitle() if len(self.Selected) == 0:return self.iCurrentImg = self.AllJpegs.index(self.Selected[0]) self.ShowImage() def lastS(self, *args): """switch to the last image selected""" logger.debug("Interface.lastS clicked") self.settitle() if len(self.Selected) == 0:return self.iCurrentImg = self.AllJpegs.index(self.Selected[-1]) self.ShowImage() def nextS(self, *args): """switch to the next image selected""" logger.debug("Interface.nextS clicked") self.settitle() if len(self.Selected) == 0:return for i in self.AllJpegs[self.iCurrentImg + 1:]: if i in self.Selected: self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def previousS(self, *args): """switch to the previous image selected""" logger.debug("Interface.previousS clicked") self.settitle() if len(self.Selected) == 0:return temp = self.AllJpegs[:self.iCurrentImg] temp.reverse() for i in temp: if i in self.Selected: self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def firstNS(self, *args): """switch to the first image NOT selected""" logger.debug("Interface.firstNS clicked") self.settitle() for i in self.AllJpegs: if i not in self.Selected: self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def lastNS(self, *args): """switch to the last image NOT selected""" logger.debug("Interface.lastNS clicked") self.settitle() temp = self.AllJpegs[:] temp.reverse() for i in temp: if i not in self.Selected: self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def nextNS(self, *args): """switch to the next image NOT selected""" logger.debug("Interface.nextNS clicked") self.settitle() for i in self.AllJpegs[self.iCurrentImg + 1:]: if i not in self.Selected: self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def previousNS(self, *args): """switch to the previous image NOT selected""" logger.debug("Interface.previousNS clicked") self.settitle() temp = self.AllJpegs[:self.iCurrentImg] temp.reverse() for i in temp: if i not in self.Selected: self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def firstT(self, *args): """switch to the first entiteled image""" logger.debug("Interface.firstT clicked") self.settitle() for i in self.AllJpegs: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def previousT(self, *args): """switch to the previous titeled image""" logger.debug("Interface.previousT clicked") self.settitle() temp = self.AllJpegs[:self.iCurrentImg] temp.reverse() for i in temp: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def nextT(self, *args): """switch to the next titeled image""" logger.debug("Interface.nextT") self.settitle() for i in self.AllJpegs[self.iCurrentImg + 1:]: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def lastT(self, *args): """switch to the last titeled image""" logger.debug("Interface.lastT clicked") self.settitle() temp = self.AllJpegs[:] temp.reverse() for i in temp: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def firstNT(self, *args): """switch to the first non-titeled image""" logger.debug("Interface.firstNT clicked") self.settitle() for i in self.AllJpegs: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if not myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def previousNT(self, *args): """switch to the previous non-titeled image""" logger.debug("Interface.previousNT clicked") self.settitle() temp = self.AllJpegs[:self.iCurrentImg] temp.reverse() for i in temp: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if not myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def nextNT(self, *args): """switch to the next non-titeled image""" logger.debug("Interface.nextNT clicked") self.settitle() for i in self.AllJpegs[self.iCurrentImg + 1:]: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if not myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def lastNT(self, *args): """switch to the last non-titeled image""" logger.debug("Interface.lastNT clicked") self.settitle() temp = self.AllJpegs[:] temp.reverse() for i in temp: if imageCache: myPhoto = imageCache.get(i, Photo(i)) else: myPhoto = Photo(i) if not myPhoto.has_title(): self.iCurrentImg = self.AllJpegs.index(i) self.ShowImage() return def savePref(self, *args): """Preferences,save clicked. now we save the preferences in the file""" logger.debug("Interface.savePref clicked") config.save(listConfigurationFiles[-1]) def setAutoRotate(self, *args): """Set the autorotate flag""" logger.debug("Interface.setAutoRotate clicked") config.AutoRotate = self.xml.get_widget("Autorotate").get_active() def setFiligrane(self, *args): """Set the Signature/Filigrane flag""" logger.debug("Interface.setFiligrane clicked") config.Filigrane = self.xml.get_widget("Filigrane").get_active() def sizeCurrent(self, *args): """reads the current size of the image and defines it as default for next-time""" logger.debug("Interface.sizeCurrent clicked") X, Y = self.xml.get_widget("Principale").get_size() config.ScreenSize = max(X - self.Xmin, Y) def setSize300(self, *args): """reads the current size of the image and defines it as default for next-time""" logger.debug("Interface.setSize300 clicked") config.ScreenSize = 300 self.xml.get_widget("Principale").resize(config.ScreenSize + 323, config.ScreenSize) def setSize600(self, *args): """reads the current size of the image and defines it as default for next-time""" logger.debug("Interface.setSize600 clicked") config.ScreenSize = 600 self.xml.get_widget("Principale").resize(config.ScreenSize + 323, config.ScreenSize) def setSize900(self, *args): """reads the current size of the image and defines it as default for next-time""" logger.debug("Interface.setSize900 clicked") config.ScreenSize = 900 self.xml.get_widget("Principale").resize(config.ScreenSize + 323, config.ScreenSize) def setInterpolNearest(self, *args): """set interpolation level to nearest""" logger.debug("Interface.setInterpolNearest clicked (nearest)") config.Interpolation = 0 def setInterpolTiles(self, *args): """set interpolation level to tiles""" logger.debug("Interface.setInterpolTiles clicked (tiles)") config.Interpolation = 1 def setInterpolBilin(self, *args): """set interpolation level to bilinear""" logger.debug("Interface.setInterpolBilin clicked (bilinear)") config.Interpolation = 2 def setInterpolHyperbol(self, *args): """set interpolation level to hyperbolic""" logger.debug("Interface.setInterpolHyperbol clicked (hyperbolic)") config.Interpolation = 3 def set30PerPage(self, *args): """set 30 images per web-page""" logger.debug("Interface.set30PerPage clicked") config.NbrPerPage = 30 def set25PerPage(self, *args): """set 25 images per web-page""" logger.debug("Interface.set25PerPage clicked") config.NbrPerPage = 25 def set20PerPage(self, *args): """set 20 images per web-page""" logger.debug("Interface.set20PerPage clicked") config.NbrPerPage = 20 def set16PerPage(self, *args): """set 16 images per web-page""" logger.debug("Interface.set16PerPage clicked") config.NbrPerPage = 16 def set12PerPage(self, *args): """set 12 images per web-page""" logger.debug("Interface.set12PerPage clicked") config.NbrPerPage = 12 def set9PerPage(self, *args): """set 9 images per web-page""" logger.debug("Interface.set9PerPage clicked") config.NbrPerPage = 9 def renameDay(self, *args): """Launch a new window and ask for anew name for the current directory""" logger.debug("Interface.renameDay clicked") self.settitle() RenameDay(self.AllJpegs[self.iCurrentImg], self.AllJpegs, self.Selected, self.renameDayCallback) def renameDayCallback(self, renamdayinstance): logger.debug("Interface.renameDayCallback") self.AllJpegs = renamdayinstance.AllPhotos self.Selected = renamdayinstance.selected self.iCurrentImg = self.AllJpegs.index(renamdayinstance.newFilename) self.ShowImage() def importImages(self, *args): """Launch a filer window to select a directory from witch import all JPEG/RAW images""" logger.debug("Interface.importImages called") self.settitle() self.guiFiler = gtk.glade.XML(unifiedglade, root="filer") self.guiFiler.get_widget("filer").set_current_folder(config.DefaultRepository) self.guiFiler.signal_connect('on_Open_clicked', self.filerSelect) self.guiFiler.signal_connect('on_Cancel_clicked', self.filerDestroy) def filerSelect(self, *args): """Close the filer GUI and update the data""" logger.debug("dirchooser.filerSelect called") self.importImageCallBack(self.guiFiler.get_widget("filer").get_current_folder()) self.guiFiler.get_widget("filer").destroy() def filerDestroy(self, *args): """Close the filer GUI""" logger.debug("dirchooser.filerDestroy called") self.guiFiler.get_widget("filer").destroy() def importImageCallBack(self, path): """This is the call back method for launching the import of new images""" logger.debug("Interface.importImageCallBack with dirname= %s" % path) self.settitle() listNew = [] #TODO Use MVC here ... for oneRaw in findFiles(path, lstExtentions=config.RawExtensions + config.Extensions, bFromRoot=True): logger.debug("Importing: %s" % oneRaw) raw = RawImage(oneRaw) raw.extractJPEG() listNew.append(raw.getJpegPath()) if len(listNew) > 0: listNew.sort() first = listNew[0] self.AllJpegs += listNew self.AllJpegs.sort() self.iCurrentImg = self.AllJpegs.index(first) self.ShowImage() def defineMediaSize(self, *args): """lauch a new window and ask for the size of the backup media""" logger.debug("Interface.defineMediaSize clicked") self.settitle() AskMediaSize() def slideShowSetup(self, *args): """lauch a new window for seting up the slideshow""" logger.debug("Interface.slideShowSetup clicked") self.settitle() AskSlideShowSetup(self) if config.GraphicMode == "SlideShow": self.xml.get_widget("Principale").destroy() gtk.main_quit() def indexJ(self, *args): """lauch a new window for selecting the day of interest""" logger.debug("Interface.indexJ clicked") self.settitle() SelectDay(self) def synchronize(self, *args): """lauch the synchronization window""" logger.debug("Interface.synchronize clicked") self.settitle() Synchronize(self.iCurrentImg, self.AllJpegs, self.Selected) def selectNewerMedia(self, *args): """Calculate the size of the selected images then add newer images to complete the media (CD or DVD). Finally the last selected image is shown and the total size is printed""" logger.debug("Interface.selectNewerMedia clicked") self.settitle() size = SelectedSize(self.Selected) initsize = size maxsize = config.MediaSize * 1024 * 1024 init = len(self.Selected) for i in self.AllJpegs[self.iCurrentImg:]: if i in self.Selected: continue size += op.getsize(op.join(config.DefaultRepository, i)) if size >= maxsize: size -= op.getsize(op.join(config.DefaultRepository, i)) break else: self.Selected.append(i) self.Selected.sort() if len(self.Selected) == 0:return self.iCurrentImg = self.AllJpegs.index(self.Selected[-1]) self.ShowImage() t = smartSize(size) + (len(self.Selected),) + smartSize(initsize) + (init,) txt = "%.2f %s de données dans %i images sélectionnées dont\n%.2f %s de données dans %i images précédement sélectionnées " % t dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, txt.decode("UTF8")) dialog.run() dialog.destroy() # result = dialog.run() # dialog.destroy() def SelectOlderMedia(self, *args): """Calculate the size of the selected images then add older images to complete the media (CD or DVD). Finally the first selected image is shown and the total size is printed""" logger.debug("Interface.SelectOlderMedia clicked") self.settitle() size = SelectedSize(self.Selected) initsize = size maxsize = config.MediaSize * 1024 * 1024 init = len(self.Selected) tmplist = self.AllJpegs[:self.iCurrentImg] tmplist.reverse() for i in tmplist: if i in self.Selected: continue size += op.getsize(op.join(config.DefaultRepository, i)) if size >= maxsize: size -= op.getsize(op.join(config.DefaultRepository, i)) break else: self.Selected.append(i) self.Selected.sort() if len(self.Selected) == 0:return self.iCurrentImg = self.AllJpegs.index(self.Selected[0]) self.ShowImage() t = smartSize(size) + (len(self.Selected),) + smartSize(initsize) + (init,) txt = "%.2f %s de données dans %i images sélectionnées dont\n%.2f %s de données dans %i images précédement sélectionnées " % t dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, txt.decode("UTF8")) dialog.run() dialog.destroy() def calculateSize(self, *args): """Calculate the size of the selection and print it""" logger.debug("Interface.calculateSize clicked") self.settitle() size = SelectedSize(self.Selected) t = smartSize(size) + (len(self.Selected),) txt = "%.2f %s de données dans %i images sélectionnées" % t dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, txt.decode("UTF8")) dialog.run() dialog.destroy() ################################################################################ # # # # # # # fin de la classe interface graphique # # # # # # ################################################################################ def SelectedSize(List): """Return the size of the selection in byte (octets)""" size = 0 for File in List: size += op.getsize(op.join(config.DefaultRepository, File)) return size def SaveSelected(selected_list, filename=None): """ Save the list of selected files @param selected_list: list of filename selected """ if filename is None: selected_fn = op.join(config.DefaultRepository, config.Selected_save) else: selected_fn = str(filename) logger.debug("Saving selection into file %s" % selected_fn) os.remove(selected_fn) with open(selected_fn, "w") as select_file: select_file.write(os.linesep.join(selected_list)) os.chmod(selected_fn, config.DefaultFileMode) def LoadSelected(filename=None): """ Load the list of selected filenames @return: the list of selected images (filenames) """ if filename is None: selected_fn = op.join(config.DefaultRepository, config.Selected_save) else: selected_fn = str(filename) if op.exists(selected_fn): logger.debug("Loading selection from file %s" % selected_fn) else: logger.warning("No such selection file %s" % selected_fn) return [] try: select = [line.strip() for line in open(selected_fn, "r") if op.isfile(op.join(config.DefaultRepository, line.strip()))] except Exception as exc: logger.warning("Failed (%s) to load selection from file %s" % (exc, selected_fn)) return [] select.sort() return select def MessageError(text, Message=gtk.MESSAGE_ERROR): dialog = gtk.MessageDialog(None, 0, Message, gtk.BUTTONS_OK, text) dialog.set_default_response(gtk.BUTTONS_OK) dialog.run() dialog.destroy() class RenameDay(object): """prompt a windows and asks for a name for the day""" def __init__(self, filename, AllPhotos, selected, callback=None): """ @param filename: name of the currenty displayed image @param AllPhotos: list of all photos filenames @param selected: list of selected photos. """ self.initialFilename = filename self.newFilename = self.initialFilename self.dayname = op.dirname(filename) self.callback = callback self.commentfile = op.join(config.DefaultRepository, self.dayname, config.CommentFile) self.comment = AttrFile(self.commentfile) if op.isfile(self.commentfile): try: self.comment.read() except: pass try: self.timetuple = time.strptime(self.dayname[:10], "%Y-%m-%d") except: print "something is wrong with this name : " + self.dayname return self.comment["date"] = time.strftime("%A, %d %B %Y", self.timetuple).capitalize().decode(config.Coding) if self.comment.has_key("title"): self.name = self.comment["title"] elif len(self.dayname) > 10: self.name = self.dayname[11:].decode(config.Coding) self.comment["title"] = self.name.decode(config.Coding) else: self.name = u"" self.comment["title"] = self.name.decode(config.Coding) self.comment["image"] = op.split(filename)[1].decode(config.Coding) if not self.comment.has_key("comment"): self.comment["comment"] = u"" self.AllPhotos = AllPhotos self.selected = selected self.xml = GTKglade.XML(unifiedglade, root="Renommer") self.xml.signal_connect('on_Renommer_destroy', self.destroy) self.xml.signal_connect('on_cancel_clicked', self.destroy) self.xml.signal_connect('on_ok_clicked', self.continu) self.xml.get_widget("Date").set_text(self.comment["date"].encode("UTF-8")) self.xml.get_widget("Commentaire").set_text(self.comment["title"].encode("UTF-8")) self.DescObj = self.xml.get_widget("Description").get_buffer() # print self.comment["comment"] comment = self.comment["comment"].encode("UTF-8").strip().replace("
", "\n",) self.DescObj.set_text(comment) # print comment def continu(self, *args): """just distroy the window and goes on ....""" self.newname = self.xml.get_widget("Commentaire").get_text().strip().decode("UTF-8") self.comment["title"] = self.newname if self.newname == "": self.newdayname = time.strftime("%Y-%m-%d", self.timetuple) else: self.newdayname = time.strftime("%Y-%m-%d", self.timetuple) + "-" + unicode2ascii(self.newname.encode("latin1")).replace(" ", "_",) self.newFilename = op.join(self.newdayname, op.basename(self.initialFilename)) self.newcommentfile = op.join(config.DefaultRepository, self.newdayname, config.CommentFile) if not op.isdir(op.join(config.DefaultRepository, self.newdayname)): mkdir(op.join(config.DefaultRepository, self.newdayname)) if self.DescObj.get_modified(): self.comment["comment"] = self.DescObj.get_text(self.DescObj.get_start_iter(), self.DescObj.get_end_iter()).strip().decode("UTF-8").replace("\n", "
") self.comment.write() if self.newname != self.name: idx = 0 for photofile in self.AllPhotos: if op.dirname(photofile) == self.dayname: newphotofile = op.join(self.newdayname, op.split(photofile)[-1]) if op.isfile(op.join(config.DefaultRepository, newphotofile)): base = op.splitext(op.join(config.DefaultRepository, newphotofile)) count = 0 for i in os.listdir(op.join(config.DefaultRepository, self.newdayname)): if i.find(base) == 0:count += 1 newphotofile = op.splitext(newphotofile) + "-%i.jpg" % count print "%s -> %s" % (photofile, newphotofile) if imageCache: myPhoto = imageCache.get(photofile, Photo(photofile)) else: myPhoto = Photo(photofile) myPhoto.renameFile(newphotofile) self.AllPhotos[idx] = newphotofile # os.rename(op.join(config.DefaultRepository, photofile), op.join(config.DefaultRepository, newphotofile)) if photofile in self.selected: self.selected[self.selected.index(photofile)] = newphotofile idx += 1 #move or remove the comment file if necessary if op.isfile(self.commentfile):# and not op.isfile(self.newcommentfile): os.rename(self.commentfile, self.newcommentfile) # elif op.isfile(self.commentfile) and op.isfile(self.newcommentfile): # os.remove(self.commentfile) if len(os.listdir(op.join(config.DefaultRepository, self.dayname))) == 0: os.rmdir(op.join(config.DefaultRepository, self.dayname)) self.xml.get_widget("Renommer").destroy() if self.callback is not None: self.callback(self) def destroy(self, *args): """destroy clicked by user -> quit the program""" try: self.xml.get_widget("Renommer").destroy() except: pass while gtk.events_pending():gtk.main_iteration() class AskSlideShowSetup: """pop up a windows and asks for the setup of the SlideShow""" def __init__(self, upperIface): self.upperIface = upperIface self.xml = GTKglade.XML(unifiedglade, root="Diaporama") dict_sig = {'on_Diaporama_destroy': self.destroy, 'on_cancel_clicked': self.kill_window, 'on_apply_clicked': self.continu, 'on_Lauch_clicked': self.LauchSlideShow, } self.xml.signal_autoconnect(dict_sig) self.xml.get_widget("delai").set_value(config.SlideShowDelay) self.xml.get_widget("Rating").set_value(config.SlideShowMinRating) if config.SlideShowType.find("chrono") == 0: self.xml.get_widget("radio-chrono").set_active(1) elif config.SlideShowType.find("anti") == 0: self.xml.get_widget("radio-antichrono").set_active(1) else: self.xml.get_widget("radio-random").set_active(1) def LauchSlideShow(self, *args): """retrieves the data, destroy the window and lauch the slideshow""" config.SlideShowDelay = self.xml.get_widget("delai").get_value() config.SlideShowMinRating = self.xml.get_widget("Rating").get_value() if self.xml.get_widget("radio-antichrono").get_active(): config.SlideShowType = "antichronological" elif self.xml.get_widget("radio-chrono").get_active(): config.SlideShowType = "chronological" else: config.SlideShowType = "random" config.GraphicMode = "SlideShow" self.xml.get_widget("Diaporama").destroy() self.upperIface.xml.get_widget("lance_diaporama").activate() # self.xml.signal_connect('on_lance_diaporama_activate' def continu(self, *args): """retrieves the data, destroy the window and goes on ....""" config.SlideShowDelay = self.xml.get_widget("delai").get_value() config.SlideShowMinRating = self.xml.get_widget("Rating").get_value() if self.xml.get_widget("radio-antichrono").get_active(): config.SlideShowType = "antichronological" elif self.xml.get_widget("radio-chrono").get_active(): config.SlideShowType = "chronological" else: config.SlideShowType = "random" self.xml.get_widget("Diaporama").destroy() def destroy(self, *args): """destroy clicked by user -> close the window""" while gtk.events_pending(): gtk.main_iteration() def kill_window(self, *args): """ send the signal to close the window """ try: self.xml.get_widget("Diaporama").destroy() except: pass class AskMediaSize: """prompt a windows and asks for the size of the backup media""" def __init__(self): self.xml = GTKglade.XML(unifiedglade, root="TailleCD") self.xml.signal_connect('on_TailleCD_destroy', self.destroy) self.xml.signal_connect('on_cancel_clicked', self.destroy) self.xml.signal_connect('on_ok_clicked', self.continu) self.xml.get_widget("TailleMo").set_text(str(config.MediaSize)) def continu(self, *args): """just distroy the window and goes on ....""" txt = self.xml.get_widget("TailleMo").get_text().strip().decode("UTF-8").encode(config.Coding) try: config.MediaSize = abs(float(txt)) except: print "%s does not seem to be the size of a media" % txt self.xml.get_widget("TailleCD").destroy() def destroy(self, *args): """destroy clicked by user -> quit the program""" try: self.xml.get_widget("TailleCD").destroy() except: pass while gtk.events_pending():gtk.main_iteration() class Synchronize: """ Class for file synchronization between different repositories """ def __init__(self, current, AllPhotos, selected): """ """ logger.debug("Synchronize.init(%i,%i,%i)" % (current, len(AllPhotos), len(selected))) logger.debug("Recorded Synchronize type: %s" % config.SynchronizeType) self.current = current self.AllPhotos = AllPhotos self.Selected = selected self.initST = config.SynchronizeType self.xml = GTKglade.XML(unifiedglade, root="Synchroniser") self.xml.signal_connect('on_Synchroniser_destroy', self.destroy) self.xml.signal_connect('on_cancel4_clicked', self.destroy) self.xml.signal_connect('on_ok4_clicked', self.synchronize) self.xml.signal_connect('on_apply4_clicked', self.apply_conf) self.xml.get_widget("SyncCommand").set_text(config.SynchronizeRep.decode(config.Coding).encode("UTF-8")) if config.SynchronizeType.lower() == "newer": self.xml.get_widget("SyncOlder").set_active(0) self.xml.get_widget("SyncAll").set_active(0) self.xml.get_widget("SyncSelected").set_active(0) self.xml.get_widget("SyncNewer").set_active(1) elif config.SynchronizeType.lower() == "older" : self.xml.get_widget("SyncNewer").set_active(0) self.xml.get_widget("SyncAll").set_active(0) self.xml.get_widget("SyncSelected").set_active(0) self.xml.get_widget("SyncOlder").set_active(1) elif config.SynchronizeType.lower() == "all": self.xml.get_widget("SyncSelected").set_active(0) self.xml.get_widget("SyncOlder").set_active(0) self.xml.get_widget("SyncNewer").set_active(0) self.xml.get_widget("SyncAll").set_active(1) elif config.SynchronizeType.lower() == "selected": self.xml.get_widget("SyncOlder").set_active(0) self.xml.get_widget("SyncAll").set_active(0) self.xml.get_widget("SyncNewer").set_active(0) self.xml.get_widget("SyncSelected").set_active(1) else: self.xml.get_widget("SyncAll").set_active(0) self.xml.get_widget("SyncOlder").set_active(0) self.xml.get_widget("SyncNewer").set_active(0) self.xml.get_widget("SyncSelected").set_active(1) self.xml.signal_connect('on_SyncAll_toggled', self.SetSyncAll) self.xml.signal_connect('on_SyncNewer_toggled', self.SetSyncNewer) self.xml.signal_connect('on_SyncOlder_toggled', self.SetSyncOlder) self.xml.signal_connect('on_SyncSelected_toggled', self.SetSyncSelected) while gtk.events_pending():gtk.main_iteration() def SetSyncAll(self, *args): logger.debug("Synchronize.SetSyncAll activated") config.SynchronizeType = "All" def SetSyncOlder(self, *args): logger.debug("Synchronize.SetSyncOld activated") config.SynchronizeType = "Older" def SetSyncNewer(self, *args): logger.debug("Synchronize.SetSyncNew activated") config.SynchronizeType = "Newer" def SetSyncSelected(self, *args): logger.debug("Synchronize.SetSyncSel activated") config.SynchronizeType = "Selected" def read_conf_GUI(self): """read config from GUI""" logger.debug("Synchronize.Read activated") config.SynchronizeRep = self.xml.get_widget("SyncCommand").get_text().strip().decode("UTF-8").encode(config.Coding) self.initST = config.SynchronizeType def apply_conf(self, *args): self.read_conf_GUI() self.destroy() def synchronize(self, *args): self.read_conf_GUI() logger.debug("Synchronize.synchronize with mode %s" % config.SynchronizeType) synchrofile = op.join(config.DefaultRepository, ".synchro") synchro = [] if config.SynchronizeType.lower() == "selected": logger.debug("Synchronize.synchronize: exec selected") synchro = self.Selected elif config.SynchronizeType.lower() == "newer": logger.debug("Synchronize.synchronize: exec newer") synchro = self.AllPhotos[self.current:] elif config.SynchronizeType.lower() == "older": logger.debug("Synchronize.synchronize: exec older") synchro = self.AllPhotos[:self.current + 1] else: logger.debug("Synchronize.synchronize: exec all") synchro = self.AllPhotos synchro.append(config.Selected_save) days = [] for photo in synchro: day = op.split(photo)[0] if not day in days: days.append(day) if op.isfile(op.join(config.DefaultRepository, day, config.CommentFile)):synchro.append(op.join(day, config.CommentFile)) f = open(synchrofile, "w") for i in synchro: f.write(i + "\n") f.close() #TODO: MVC os.system("rsync -v --files-from=%s %s/ %s" % (synchrofile, config.DefaultRepository, config.SynchronizeRep)) self.destroy() def destroy(self, *args): """ destroy clicked by user -> quit the program """ config.SynchronizeType = self.initST try: self.xml.get_widget("Synchroniser").destroy() except: pass while gtk.events_pending():gtk.main_iteration() class SelectDay: def __init__(self, upperIface): self.upperIface = upperIface self.xml = GTKglade.XML(unifiedglade, root="ChangeDir") self.combobox = self.xml.get_widget("entry") self.xml.signal_connect('on_ChangeDir_destroy', self.destroy) self.xml.signal_connect('on_annuler_clicked', self.destroy) self.xml.signal_connect('on_Ouvrir_clicked', self.continu) self.days = [op.split(self.upperIface.AllJpegs[0])[0]] self.combobox.append_text(self.days[0]) for image in self.upperIface.AllJpegs[1:]: day = op.split(image)[0] if day != self.days[-1]: self.days.append(day) self.combobox.append_text(day) self.curday = self.days.index(op.split(self.upperIface.AllJpegs[self.upperIface.iCurrentImg])[0]) self.combobox.set_active(self.curday) def continu(self, *args): """just distroy the window and goes on ....""" day = self.days[self.combobox.get_active()] for i in range(len(self.upperIface.AllJpegs)): if op.split(self.upperIface.AllJpegs[i])[0] == day: break self.upperIface.iCurrentImg = i self.upperIface.ShowImage() self.xml.get_widget("ChangeDir").destroy() def destroy(self, *args): """destroy clicked by user -> quit the program""" try: self.xml.get_widget("ChangeDir").destroy() except: pass while gtk.events_pending(): gtk.main_iteration() ################################################################################ # Main program of selector ################################################################################ if __name__ == '__main__': printWarning = True if len(sys.argv) > 1: for arg in sys.argv[1:]: if arg[0] == "-": if arg[1].lower() == "h": print("Selector classe des photos,\nil prend comme paramettre le chemin début de recherche et comme option:\n -nowarning : évite le message d'avertissement au lancement\n -noautorotate : ne fait pas de tests de rotation automatique, accélèrer le tri.\n") sys.exit(0) elif arg.lower().find("-debug") >= 0: # logger.setLevel(logging.DEBUG) loggerImagizer = logging.getLogger("imagizer") loggerImagizer.setLevel(logging.DEBUG) logger.debug("We are in debug mode ...First Debug message") config.DEBUG = True elif arg.lower().find("-noautorotate") >= 0: logger.debug("Autorotate is disabled") config.AutoRotate = False elif arg.lower().find("-nowarning") >= 0: logger.debug("Initial warning about file renaming is disabled") printWarning = False elif op.isdir(arg): config.DefaultRepository = op.abspath(arg) config.printConfig() selected_fn = op.join(config.DefaultRepository, config.Selected_save) if (printWarning == True) and (not op.isfile(selected_fn)): W = WarningSc(config.DefaultRepository) config.DefaultRepository = W.directory del W selected_fn = op.join(config.DefaultRepository, config.Selected_save) if not op.isfile(selected_fn): os.remove(selected_fn) f = open(selected_fn, "w") f.close() AF, first = RangeTout(config.DefaultRepository) values = (AF, first, LoadSelected(), config.GraphicMode) while config.GraphicMode != "Quit": if config.GraphicMode == "Normal": ifc = Interface(*values) values = (ifc.AllJpegs, ifc.iCurrentImg, ifc.Selected, config.GraphicMode) del ifc gc.collect() elif config.GraphicMode in ["FullScreen", "SlideShow"]: ifc = FullScreenInterface(*values) values = (ifc.AllJpegs, ifc.iCurrentImg, ifc.Selected, config.GraphicMode) del ifc gc.collect() if config.DEBUG: print "Switching to mode %s" % config.GraphicMode