#!/usr/bin/python
# -*- coding: Utf-8 -*-


import sys, os, Image, string, glob
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from gui_modules_lecture.affichage_image.afficheurImage import VisionneurImagePourEKD	
from gui_modules_image.image_base import Base

class FiltresAvecSpin(QFrame):
	"""Widget de réglage à 1, 2 ou 3 boites de spins"""
	def __init__(self, ligne1=('',('','','')), ligne2=None):
        	QFrame.__init__(self)
		
		grid = QGridLayout(self)
		
		if not ligne2: # 1 seul spin
			self.spin = QSpinBox()
			self.spin.setRange(ligne1[1][0], ligne1[1][1])
			self.spin.setValue(ligne1[1][2])
			self.curseur = QSlider(Qt.Horizontal)
			self.curseur.setRange(ligne1[1][0], ligne1[1][1])
			self.curseur.setValue(ligne1[1][2])
			grid.addWidget(self.spin,0,0)
			grid.addWidget(self.curseur,0,1)
			grid.addWidget(QLabel(ligne1[0]),0,2)
			self.connect(self.spin,SIGNAL("valueChanged(int)"), self.spinChange)
			self.connect(self.curseur,SIGNAL("sliderMoved(int)"), self.curseurBouge)
		else: # 2 spins
			self.spin1 = QSpinBox()
			self.spin2 = QSpinBox()
			self.spin1.setRange(ligne1[1][0], ligne1[1][1])
			self.spin1.setValue(ligne1[1][2])
			self.spin2.setRange(ligne2[1][0], ligne2[1][1])
			self.spin2.setValue(ligne2[1][2])
			self.curseur1 = QSlider(Qt.Horizontal)
			self.curseur1.setRange(ligne1[1][0], ligne1[1][1])
			self.curseur1.setValue(ligne1[1][2])
			self.curseur2 = QSlider(Qt.Horizontal)
			self.curseur2.setRange(ligne2[1][0], ligne2[1][1])
			self.curseur2.setValue(ligne2[1][2])
			grid.addWidget(self.spin1,0,0)
			grid.addWidget(self.curseur1,0,1)
			grid.addWidget(QLabel(ligne1[0]),0,2)
			grid.addWidget(self.spin2,1,0)
			grid.addWidget(self.curseur2,1,1)
			grid.addWidget(QLabel(ligne2[0]),1,2)
			self.connect(self.spin1,SIGNAL("valueChanged(int)"), self.spinChange1Bis)
			self.connect(self.curseur1,SIGNAL("sliderMoved(int)"), self.curseurBouge1Bis)
			self.connect(self.spin2,SIGNAL("valueChanged(int)"), self.spinChange2Bis)
			self.connect(self.curseur2,SIGNAL("sliderMoved(int)"), self.curseurBouge2Bis)
		
		grid.setAlignment(Qt.AlignCenter)
	
	def spinChange(self, i):
		"""modifie le curseur lorsque le curseur bouge"""
		self.curseur.setValue(i)
	
	def curseurBouge(self, i):
		"""Quand on change la position du curseur la valeur du spin est automatiquement modifiée"""
		self.spin.setValue(i)

	def spinChange1Bis(self, i):
		"""modifie le curseur lorsque le curseur bouge"""
		self.curseur1.setValue(i)
	
	def curseurBouge1Bis(self, i):
		"""Quand on change la position du curseur la valeur du spin est automatiquement modifiée"""
		self.spin1.setValue(i)

	def spinChange2Bis(self, i):
		"""modifie le curseur lorsque le curseur bouge"""
		self.curseur2.setValue(i)
	
	def curseurBouge2Bis(self, i):
		"""Quand on change la position du curseur la valeur du spin est automatiquement modifiée"""
		self.spin2.setValue(i)


class Image_Divers_Redimensionner(QWidget):
	"""# -----------------------------------
	# Cadre accueillant les widgets de :
	# Image >> Divers >> Redimensionner
	# -----------------------------------"""
	def __init__(self, parent):
        	QWidget.__init__(self)
		
		# ----------------------------
		# Quelques paramètres de base
		# ----------------------------
		
		#=== Création des répertoires temporaires ===#
		self.repTampon = '/tmp/ekd/tampon/image_divers_redimensionner/'
		if os.path.isdir(self.repTampon) is False:
        		os.makedirs(self.repTampon)
		if os.path.isdir(self.repTampon+'redim/') is False:
        		os.makedirs(self.repTampon+'redim/')	
		# Si le répertoire /tmp/ekd/tampon/image_divers_redimensionner/redim
		# n'est pas vide, il est expressément vidé de tout son contenu
		tempr=glob.glob(self.repTampon+'redim/*.*')
		if len(tempr)>0:
			for parc in tempr: os.remove(parc)
		
		#=== Variable contenant les titres du log ===#
		self.infosImgTitre = []
		txt = _(u"Image(s) chargée(s)")
		a='#'*36
		b = a + '\n# ' + txt + '\n' + a + '\n'
		txt=_(u"Image(s) convertie(s)")
		c = a + '\n# ' + txt + '\n' + a + '\n'
		self.infosImgTitre.append(b)
		self.infosImgTitre.append(c)
		
		#=== Drapeaux ===#
		# Une conversion (même partielle) a-t-elle eu lieu après le chargement des images? (1: vrai)
		self.conversionImg = 0
		# Des images ont été chargées au moins une fois? (1: vrai)
		self.imgChargees = 0
		# Liste à 2 élements:
			# l'utilisateur vient d'afficher une nouvelle image <-> le curseur vient d'être déplacé?
			# indice de l'onglet qui vient d'afficher cette image
		self.nvImgAffichee = [0, 0] # le 2ème 0 n'a pas de sens pour l'instant
		# Forcer l'affichage de l'image dans le prochain onglet sélectionné (1: vrai)
		self.forcerAffichageImg = 0
		
		# Identifiant des filtres utilisant partiellement ou intégralement un module python durant 
		# la conversion
		self.filtresPython=['redim_avec_ratio', 'redim_sans_ratio']
		
		self.timer = QTimer()
		self.connect(self.timer, SIGNAL('timeout()'), self.sonderTempsActuel)
		
		self.process = QProcess()
		self.connect(self.process, SIGNAL('finished(int)'), self.finConversion)
		
		# Fonctions communes à plusieurs cadres du module Image
		self.base = Base()
		# Paramètres de configuration
		self.config = parent.config
		self.listeImgSource = []
		
		# Boite d'alignement vertical
		vbox=QVBoxLayout(self)
		
		# ----------------------------------------
		# Bouton de sélection des images sources
		# ----------------------------------------
		
		#=== Bouton de sélection des images ===#
		hbox = QHBoxLayout()
		self.boutOuvrirSource = QPushButton(_(u"Charger les images sources"))
		self.boutOuvrirSource.setFocusPolicy(Qt.NoFocus)
		hbox.addWidget(self.boutOuvrirSource)
		vbox.addLayout(hbox)
		
		self.connect(self.boutOuvrirSource, SIGNAL("clicked()"), self.ouvrirImages)
		
		self.tabwidget=QTabWidget()
		
		#------------------
		# Onglet Réglages
		#------------------
		
		self.framReglage=QFrame()
		vboxReglage=QVBoxLayout(self.framReglage)
		
		#=== Stacked ===#
		
		self.stacked = QStackedWidget()
		self.stacked.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed))
		
		#=== Instanciation des widgets du stacked  ===#
		
		# Widgets du stacked avec une seule boite de spin
		stacked_redim_avec_ratio = FiltresAvecSpin((_(u"nouvelle largeur"),(10,6000,640)))
		
		# Widgets du stacked avec 2 boites de spin
		stacked_redim_sans_ratio = FiltresAvecSpin((_(u"nouvelle largeur"),(10,6000,640)),(_(u"nouvelle hauteur"),(10,6000,480)))
		
		#=== Ajout de widgets au stacked ===#
		
		# Widgets du stacked avec une seule boite de spin
		indexStacked_redim_avec_ratio = self.stacked.addWidget(stacked_redim_avec_ratio)
		
		# Widgets du stacked avec 2 boites de spin
		indexStacked_redim_sans_ratio = self.stacked.addWidget(stacked_redim_sans_ratio)
		
		# boite de combo
		self.comboReglage=QComboBox()
		# Paramètres de la liste de combo: [(nom entrée, identifiant, index du stacked,
		# instance stacked),...]
		self.listeComboReglage=[\
		(_(u'Redimensionner avec ratio'), 'redim_avec_ratio', indexStacked_redim_avec_ratio, stacked_redim_avec_ratio),\
		(_(u'Redimensionner sans ratio'), 'redim_sans_ratio', indexStacked_redim_sans_ratio, stacked_redim_sans_ratio),\
		]

		# Insertion des codecs de compression dans la boite de combo
		for i in self.listeComboReglage:
                	self.comboReglage.addItem(i[0])
		self.connect(self.comboReglage, SIGNAL("activated(int)"), self.changerComboReglage)

		hbox = QHBoxLayout()
		hbox.addWidget(QLabel(_(u'Type')))
		hbox.addWidget(self.comboReglage)
		hbox.setAlignment(Qt.AlignHCenter)
		
		vboxReglage.addLayout(hbox)
		vboxReglage.addWidget(self.stacked)
		vboxReglage.addStretch()
		
		#----------------
		# Onglet de log
		#----------------
		
		self.zoneAffichInfosImg = QTextEdit("")
		self.zoneAffichInfosImg.setReadOnly(True)
		self.framInfos=QFrame()
		vboxInfIm=QVBoxLayout(self.framInfos)
		vboxInfIm.addWidget(self.zoneAffichInfosImg)
		
		# -------------------------------------------------
		# Onglets d'affichage image source et destination
		# -------------------------------------------------
		
		# Là où s'afficheront les images
		self.afficheurImgSource=VisionneurImagePourEKD()
		self.afficheurImgDestination=VisionneurImagePourEKD()
		
		self.connect(self.tabwidget, SIGNAL("currentChanged(int)"), self.fctTab)
		
		vbox.addWidget(self.tabwidget)
		
		self.indexTabImgReglage = self.tabwidget.addTab(self.framReglage, _(u'Réglages'))
		self.indexTabImgSource = self.tabwidget.addTab(self.afficheurImgSource, _(u'Images sources'))
		self.indexTabImgDestin = self.tabwidget.addTab(self.afficheurImgDestination, _(u'Images après traitement'))
		self.indexTabImgInfos = self.tabwidget.addTab(self.framInfos, _(u'Infos'))
		
		# -------------------------------------------
		# widgets du bas : curseur + ligne + boutons
		# -------------------------------------------
		
		# Curseur de parcours des images source ou destination
		self.curseur = QSlider(Qt.Horizontal)
		self.curseur.setEnabled(False)
		self.connect(self.curseur,SIGNAL("sliderReleased()"), self.curseurRelache)
		vbox.addWidget(self.curseur)
		
		# Boutons
		boutAide=QPushButton(_(u" Aide"))
		boutAide.setIcon(QIcon("Icones/icone_aide_128.png"))
		self.connect(boutAide, SIGNAL("clicked()"), self.afficherAide)
		
		self.boutAppliquer=QPushButton(_(u" Appliquer et sauver"))
		self.boutAppliquer.setIcon(QIcon("Icones/icone_appliquer_128.png"))
		# Bouton inactif au départ
		self.boutAppliquer.setEnabled(False)
		self.connect(self.boutAppliquer, SIGNAL("clicked()"), self.appliquer0)
		
		# Ligne de séparation juste au dessus des boutons
		ligne = QFrame()
		ligne.setFrameShape(QFrame.HLine)
		ligne.setFrameShadow(QFrame.Sunken)
		vbox.addWidget(ligne)
		vbox.addSpacing(-5)	# la ligne doit être plus près des boutons
		
		hbox=QHBoxLayout()
		hbox.addWidget(boutAide)
		hbox.addStretch()
		hbox.addWidget(self.boutAppliquer)
		vbox.addLayout(hbox)
		
		#-----------------------------
		# Barre de progression
		#-----------------------------
		
		self.progress=QProgressDialog(_(u"Conversion en cours..."), _(u"Arrêter"), 0, 100)
		self.progress.setWindowTitle(_(u'EnKoDeur-Mixeur. Fenêtre de progression'))
		self.progress.setMinimumWidth(500)
		self.progress.setMinimumHeight(100)
		self.connect(self.progress, SIGNAL("canceled()"), self.arretProgression)
		
		# affichage de la boîte principale
		self.setLayout(vbox)
		
	
	def curseurRelache(self):
		"""Affichage des images dans les 2 onglets prévus à cet effet"""
		i = self.curseur.value()
		print "Position %d du curseur principal" %i
		
		# Affichage d'une image dans l'onglet "images sources"
		if self.tabwidget.currentIndex() == self.indexTabImgSource:
			self.afficheurImgSource.setImage(self.listeImgSource[i])
			self.nvImgAffichee = [1, self.indexTabImgSource]
		
		# Affichage d'une image dans l'onglet "image après traitement" quand une conversion,
		# même partielle a eu lieu
		if self.tabwidget.currentIndex() == self.indexTabImgDestin and self.conversionImg:
			if self.progress.wasCanceled(): # ... elle n'est pas achevée...
				if i > len(self.listeImgDestin)-1: # ... on sort du domaine valide du curseur
					self.afficheurImgDestination.setImage() # image d'accueil
				else: # on affiche une image convertie
					self.afficheurImgDestination.setImage(self.listeImgDestin[i])
			else: # La conversion a été entièrement réalisée
					self.afficheurImgDestination.setImage(self.listeImgDestin[i])
			self.nvImgAffichee = [1, self.indexTabImgDestin]
		
		print "[Nouvelle Image affichée?, indice d'onglet] :", self.nvImgAffichee, '\n'
	
	
	def changerComboReglage(self, i):
		"""L'entrée sélectionnée de la boîte de combo modifie le QFrame de réglage du codec associée"""
		self.stacked.setCurrentIndex(self.listeComboReglage[i][2])
	
	
	def fctTab(self, i):
		"""Affichage d'une nouvelle image dans le QPixmap de l'onglet appelée si les conditions l'exigent. Accessoirement: rafraichissement du QPixmap pour que l'image soit affichée au milieu de la fenêtre et grisage/dégrisage du curseur principal"""
		
		# [bidouillage] Comme on n'arrive pas à afficher les images d'accueils
		# centrées de façon 'propre', en attendant on bidouille pour y
		# arriver: *.setTailleFenetre()
		
		print "Entrée : [Nouvelle Image affichée?, indice d'onglet] :", self.nvImgAffichee
		j = self.curseur.value()
		
		if i==self.indexTabImgReglage:
			self.curseur.setEnabled(False)
		
		elif i==self.indexTabImgSource:
			print "Vous êtes sur l'onglet \"Images Sources\""
			if self.imgChargees:
				print "Des images sources ont déjà été chargées"
				self.curseur.setEnabled(True)
				if self.forcerAffichageImg == 1:
					print "Des images viennent d'être chargées -> affichage de la 1ère image du lot source"
					self.afficheurImgSource.setImage(self.listeImgSource[0])
					self.forcerAffichageImg = 0
					self.nvImgAffichee[0] = 1 # pour avoir un nouvel affichage dans l'autre onglet
				elif self.nvImgAffichee == [1, self.indexTabImgDestin]:
					print "Le curseur a été déplacé dans l'onglet \"Images après traitement\" <-> une nouvelle image a été affichée dans l'onglet précédent.\n -> nouvelle image dans l'onglet \"Images Sources\""
					self.afficheurImgSource.setImage(self.listeImgSource[j])
					self.nvImgAffichee[0] = 0
				else: print "pas de nouvel affichage (pas utile)"
			else:
				print "Pas d'images sources chargées"
				self.afficheurImgSource.setTailleFenetre()
			self.nvImgAffichee[1]=self.indexTabImgSource # dernier onglet sélectionnée contenant 1 QPixMap
		
		elif i==self.indexTabImgDestin:
			print "Vous êtes sur l'onglet \"Images après traitement\""
			if not self.imgChargees or not self.conversionImg:
				print "Des images sources n'ont pas déjà été chargées ou la conversion n'a pas eu lieu après le chargement des dernières images sources"
				self.afficheurImgDestination.setTailleFenetre()
				self.curseur.setEnabled(False)
				self.nvImgAffichee[0] = 0
			else:
				print "Des images sources ont déjà été chargées et les dernières images chargées ont été converties"
				self.curseur.setEnabled(True)
				if self.forcerAffichageImg==1:
					print "La conversion vient d'avoir lieu -> affichage de la 1ère image du lot de destination"
					self.afficheurImgDestination.setImage(self.listeImgDestin[0])
					self.forcerAffichageImg = 0
					self.nvImgAffichee[0] = 1 # pour avoir un nouvel affichage dans l'autre onglet
				elif self.nvImgAffichee == [1, self.indexTabImgSource]:
					print "Le curseur a été déplacé dans l'onglet \"Images Sources\" <-> une nouvelle image a été affichée dans l'onglet précédent OU on force l'affichage de l'image"
					if j > len(self.listeImgDestin)-1:
						print "L'indice du curseur correspond à une image qui n'a pas pu être convertie car l'opération a été stoppée avant la fin\n  -> affichage de l'image d'accueil"
						self.afficheurImgDestination.setImage()
					else:
						print "-> Nouvelle image dans l'onglet \"Images après traitement\""
						self.afficheurImgDestination.setImage(self.listeImgDestin[j])
					self.nvImgAffichee[0] = 0
					self.forcerAffichageImg = 0
				else: print "pas de nouvel affichage (pas utile)"
			self.nvImgAffichee[1]=self.indexTabImgDestin # dernier onglet sélectionnée contenant 1 QPixMap
		
		elif i==self.indexTabImgInfos:
			self.curseur.setEnabled(False)
		
		print "Sortie : [Nouvelle Image affichée?, indice d'onglet] :", self.nvImgAffichee, '\n'
	
	
	def metaFctTab(self, i):
		"""Changement d'onglet (conçu pour sélectionner les onglets "Images Source" après le chargement de nouvelles images sources ou "Images Après Traitement" après la conversion). But: s'assurer que la fonction associée au QTabWidget (affichage d'images, grisage/dégrisage du curseur...) sera bien appliquée même si on est déjà sur le bon onglet"""
		if self.tabwidget.currentIndex()!=i:
			self.tabwidget.setCurrentIndex(i)
		else:
			self.fctTab(i)
	
	
	def logFinal(self, titreFiltre):
		"""Affichage des informations de conversion"""
		a='#'*36
		# On ne récupère pas l'ancien % car il est arrondi
		pourCent=round(float(len(self.listeImgDestin))/len(self.listeImgSource)*100)
		pourCent = "%.0f" %pourCent
		b = a + '\n# ' + _(u'Filtre utilisé: ') + titreFiltre + '\n' + a + '\n'
		c = _(u"nombre d'images converties / nombre d'images sources")+" = "+str(len(self.listeImgDestin))+" / "\
		+str(len(self.listeImgSource))+" = " + pourCent +" %\n\n"
		
		# Affichage information de la nouvelle résolution des images
		# Pour les commentaires voir dans la fonction appliquer
		if self.listeComboReglage[self.i][1]=='redim_avec_ratio':
			spin=self.listeComboReglage[self.i][3].spin.value() # nouvelle largeur
			obImg=Image.open(self.listeImgSource[0])
			w, h=obImg.size
			ratio=float(w)/float(h)
			calcHaut=float(spin)/ratio
			d = a+'\n# '+_(u"Nouvelle résolution image(s): ")+'\n'+a+'\n'+str(spin)+' x '+str(int(calcHaut))
			
		elif self.listeComboReglage[self.i][1]=='redim_sans_ratio':
			spin1=self.listeComboReglage[self.i][3].spin1.value() # nouvelle largeur
			spin2=self.listeComboReglage[self.i][3].spin2.value() # nouvelle hauteur
			obImg=Image.open(self.listeImgSource[0])
			d=a+'\n# '+_(u"Nouvelle résolution image(s): ")+'\n'+a+'\n'+str(spin1)+' x '+str(spin2)
		
		# Le dernier '\n' est parfois nécessaire pour voir la dernière ligne!
		#self.zoneAffichInfosImg.setText(b+c+self.infosImgTitre[0]+\
		#"\n".join(self.listeImgSource)+'\n\n'+self.infosImgTitre[1]+"\n".join(self.listeImgDestin)+'\n')
		
		# Le dernier '\n' est parfois nécessaire pour voir la dernière ligne!
		self.zoneAffichInfosImg.setText(b+c+self.infosImgTitre[0]+\
		"\n".join(self.listeImgSource)+'\n\n'+self.infosImgTitre[1]+"\n".join(self.listeImgDestin)+'\n\n'+d+'\n')
	
	
	def ouvrirImages(self):
		"""Récupération du chemin des images séquence A sélectionnées + quelques opérations supplémentaires"""
		
		# Ouvrir images séquence A
		rep = self.base.getRepSource(self.listeImgSource, self.config)
		chemin_chargement=QFileDialog.getOpenFileNames(None, _(u"Ouvrir images"), rep, ("JPEG (*.jpg *.JPG *.jpeg *.JPEG);;PNG (*.png *.PNG);;GIF (*.gif *.GIF);;BMP (*.bmp *.BMP);;SGI (*.sgi *.SGI);;TIF (*.tif *.TIF);;TARGA (*.tga *.TGA);;PNM (*.pnm *.PNM);;PPM (*.ppm *.PPM)"))
		
		# Si pas de fichier sélectionné -> sortir de la fonction
		if chemin_chargement.count()==0: return
		
		# Convertir les differents fichiers selectionnes
		# en une liste de chaines de caracteres
		self.listeImgSource=[str(chemin_chargement[i_chemCharg]) for i_chemCharg in range(chemin_chargement.count())]
		self.listeImgSource.sort()
		
		self.base.setRepSource(self.listeImgSource, self.config)
		
		# Condition à respecter pour qu'une redimension ait lieu
		self.imgChargees = 1
		self.conversionImg = 0
		self.forcerAffichageImg = 1
		
		# Définition du domaine de validité du curseur
		self.curseur.setRange(0, len(self.listeImgSource)-1)
		self.curseur.setValue(0)
		
		# Changement d'onglet et fonctions associées
		self.metaFctTab(self.indexTabImgSource)
		
		# Activer le bouton 'appliquer'
		self.boutAppliquer.setEnabled(True)
		
		# Onglet de log
		self.zoneAffichInfosImg.setText(self.infosImgTitre[0]+"\n".join(self.listeImgSource))
	
	
	def stat_dim_img(self):
		"""Calcul statistique des dimensions des images les plus présentes dans le lot"""
		
		# Ouverture et mise ds une liste des dimensions des images
		listePrepaRedim=[Image.open(aA).size for aA in self.listeImgSource]
		
		# Merci beaucoup à Marc Keller de la liste: python at aful.org de m'avoir
		# aidé pour cette partie (les 4 lignes en dessous)
		dictSeq={}.fromkeys(listePrepaRedim, 0)
		for cle in listePrepaRedim: dictSeq[cle]+=1
		self.lStatDimSeq=sorted(zip(dictSeq.itervalues(), dictSeq.iterkeys()), reverse=1)
		self.dimStatImg=self.lStatDimSeq[0][1]
		
		print "Toutes les dimensions des images (avec le nbre d'images):", self.lStatDimSeq
		print 'Dimension des images la plus presente dans la sequence:', self.dimStatImg
		print "Nombre de tailles d'images différentes dans le lot :", len(self.lStatDimSeq)
		
		if len(self.lStatDimSeq)>1:
			return 0
		else:
			return 1
	
	
	def redim_img(self):
		"""Si l'utilisateur charge des images avec des tailles complètement différentes --> les images de la séquence  peuvent être redimensionnées"""
		
		if not self.stat_dim_img():
			reply = QMessageBox.warning(self, 'Message',
			_(u"Vos images ne sont pas toutes de la même taille. Voulez-vous redimensionner les images de sortie à la taille la plus répandue dans la séquence?"), QMessageBox.Yes, QMessageBox.No)
			if reply == QMessageBox.No:
				return
			
			# Les images de tailles différentes à la plus répandue sont redimensionnées
			# dans un répertoire temporaire.
			# Les images redimensionnées voient leur chemin modifié dans la liste des
			# chemins des images sources. Les autres chemins ne changent pas.
			index=0
			for chemImg in self.listeImgSource:
				obImg=Image.open(chemImg)
				if obImg.size!=self.dimStatImg:
					pass
				sRedim=obImg.resize(self.dimStatImg)
				chemSortie = self.repTampon+'redim/'+os.path.basename(chemImg)
				sRedim.save(chemSortie)
				self.listeImgSource[index] = chemSortie
				index += 1

	
	def arretProgression(self):
		"""Si le filtre appliqué est 100 % shell, alors la conversion est immédiatement stoppée après un clic sur le bouton de la QProgessDialog. Pas la peine d'attendre la fin de la conversion de l'image en cours comme pour les autres types de filtres."""
		if self.listeComboReglage[self.i][1] in self.filtresPython: return
		#self.terminerFiltreShell = 1
		self.process.kill()
		if len(self.listeImgDestin)!=0:
			self.conversionImg = 1
			self.forcerAffichageImg = 1
			self.logFinal(self.listeComboReglage[self.i][0])
			self.tabwidget.setCurrentIndex(self.indexTabImgInfos)
		else: # Onglet de log -> on remet les infos de départ
			self.zoneAffichInfosImg.setText(self.infosImgTitre[0]+"\n".join(self.listeImgSource))
	
	
	def finConversion(self, statutDeSortie):
		"""Choses à faire à la fin de l'encodage d'une image quand le filtre sélectionné est constitué d'une seule commande shell"""
		
		if statutDeSortie==1:
			print "Problème lors de la conversion!"
			messageAide=QMessageBox(self)
			messageAide.setText(_(u"Une erreur s'est produite durant la conversion"))
			messageAide.setWindowTitle(_(u"Aide"))
			messageAide.setIcon(QMessageBox.Warning)
			messageAide.exec_()
			self.logFinal(self.listeComboReglage[self.i][0])
			self.tabwidget.setCurrentIndex(self.indexTabImgInfos)
			return
		
		# On ne continue pas si l'utilisateur l'a décidé
		#elif self.terminerFiltreShell: return
		
		# On passe à l'image suivante s'il en reste
		elif self.opReccurApresApp():
			self.appliquer()
	
	
	def opReccurApresApp(self):
		"""Opérations à effectuer après chaque appel du moteur."""
		if self.listeComboReglage[self.i][1] in self.filtresPython:
			modulePython = 1 # conversion python (+ shell pour certains filtres)
			
		#else: modulePython = 0 # conversion 100% shell
		
		self.listeImgDestin.append(self.cheminCourantSauv)
		pourCent=int((float(self.j+1)/self.nbrImg)*100)
		
		self.progress.setValue(pourCent)
		#!!!Rafraichissement indispensable pour la transmission immédiate du signal QProgressDialog().wasCanceled
		if modulePython: QApplication.processEvents()
		
		# Incrémentation de l'indice du fichier source pour une conversion 100% commande shell
		#if not modulePython: self.j += 1
		
		# Opérations de fin de conversion
		if pourCent==100:
			# Condition à respecter pour qu'un affichage d'une nouvelle image ait lieu dans l'onglet "Images après traitement"
			self.conversionImg = 1
			self.forcerAffichageImg = 1
			# Boite de dialogue d'information
			messageAide=QMessageBox(self)
			messageAide.setText(_(u"Le filtre a été appliqué avec succès!"))
			messageAide.setWindowTitle(self.listeComboReglage[self.i][0])
			messageAide.setIcon(QMessageBox.Information)
			messageAide.exec_()
			# Mise-à-jour du log
			self.logFinal(self.listeComboReglage[self.i][0])
			# Changement d'onglet et fonctions associées
			self.metaFctTab(self.indexTabImgDestin)
			return 0
		
		# Opérations à faire lors de l'arrêt de la conversion suite au clic sur le bouton annuler de la barre de progression
		elif self.progress.wasCanceled():
			if modulePython:
				# Condition à respecter pour qu'un affichage d'une nouvelle image ait lieu dans l'onglet "Images après traitement"
				self.conversionImg = 1
				self.forcerAffichageImg = 1
				# Mise-à-jour du log
				self.logFinal(self.listeComboReglage[self.i][0])
				# Affichage de l'onglet d'infos
				self.tabwidget.setCurrentIndex(self.indexTabImgInfos)
			return 0
		
		return 1
	
	
	def sonderTempsActuel(self):
		"""x ms après l'apparition de la boite de dialogue, on lance la conversion. But: faire en sorte que la boite de dialogue ait le temps de s'afficher correctement"""
		self.timer.stop()
		self.appliquer()
	
	
	def appliquer0(self):
		"""Préparation de la conversion"""
		
		# Redimensionner les images de tailles différentes?
		self.redim_img()
		
		rep = self.base.getRepSource(self.listeImgSource, self.config)
		self.cheminSauv=str(QFileDialog.getSaveFileName(None, _(u"Sauver"), rep, _(u"Tous les fichiers *.*")))
		
		if self.cheminSauv=='': return
		
		print 'Chemin+nom de sauvegarde:', self.cheminSauv
		
		# Liste des fichiers de destination
		self.listeImgDestin = []
		
		self.curseur.setValue(0)
		
		# Extension/format des images
		self.ext = os.path.splitext(self.listeImgSource[0])[1]
		
		# Liste des indices des images [0,1,2,...,nombre d'images-1]
		self.listeIndex = range(len(self.listeImgSource))
		
		# Nombre d'images sources
		self.nbrImg = len(self.listeImgSource)
		
		# Récupération de l'identifiant du codec
		self.i = self.comboReglage.currentIndex()
		
		# Indice de l'image à convertir
		self.j = 0
		
		# drapeau (réinitialisation): terminer le filtre avec commande 100% shell?
		# self.terminerFiltreShell = 0
		
		self.progress.reset() # wasCanceled est remis à 0 -> la conversion ne s'arrête pas à la 1ère img
		self.progress.show()
		self.progress.setValue(0)
		QApplication.processEvents()
		
		# Lancement de la conversion dans 250 ms
		self.timer.start(250)
	
	
	def appliquer(self):
		"""Conversion des images"""

		print "Indice de l'image à encoder :", self.j
		
		if self.listeComboReglage[self.i][1]=='redim_avec_ratio':
			""" Redimensionnement des images avec ratio """
			for self.j in self.listeIndex:
				# Récup réglage ci-dessous
				spin = self.listeComboReglage[self.i][3].spin.value() # nouvelle largeur
				# Chemin de sauvegarde
				self.cheminCourantSauv = self.cheminSauv+'_'+string.zfill((self.j+1), 6)+self.ext
				# Ouverture des images. Conversion et sauvegarde en niveaux de gris
				obImg = Image.open(self.listeImgSource[self.j])
				# Recup dimensions de l'image
				w, h = obImg.size
				# Calcul du ratio de chaque image chargee
				ratio=float(w)/float(h)
				# Calcul de future hauteur avec les dimensions donnees par l'utilisateur
				calcHaut=float(spin)/ratio
				# Redimensionnement et sauvegarde des images
				finRedimSansRatio=obImg.resize((int(spin), int(calcHaut))).save(self.cheminCourantSauv)
				if not self.opReccurApresApp(): return
				
		elif self.listeComboReglage[self.i][1]=='redim_sans_ratio':
			""" Redimensionnement des images sans ratio """
			for self.j in self.listeIndex:
				# Récup réglages ci-dessous
				spin1 = self.listeComboReglage[self.i][3].spin1.value() # nouvelle largeur
				spin2 = self.listeComboReglage[self.i][3].spin2.value() # nouvelle hauteur
				# Chemin de sauvegarde
				self.cheminCourantSauv = self.cheminSauv+'_'+string.zfill((self.j+1), 6)+self.ext
				# Ouverture des images. Conversion et sauvegarde en niveaux de gris
				obImg = Image.open(self.listeImgSource[self.j])
				# Redimensionnement et sauvegarde des images
				finRedimSansRatio=obImg.resize((int(spin1), int(spin2))).save(self.cheminCourantSauv)
				if not self.opReccurApresApp(): return
			
			
	def afficherAide(self):
		"""Boîte de dialogue de l'aide"""
		messageAide=QMessageBox(self)
		messageAide.setText(_(u"Blablablabla ... blabla."))
		messageAide.setWindowTitle(_(u"Aide"))
		messageAide.setIcon(QMessageBox.Information)
		messageAide.exec_()