class Animation_MontagVideoVidPlusAudio(Base):
	""" --------------------------------------------
	# Cadre accueillant les widgets de :
	# Animation >> Montage vidéo >> Vidéo et Audio
	# -------------------------------------------"""

	def __init__(self, statusBar, parent=None):
		vbox=QVBoxLayout()

		#=== Variable de configuration ===#
		self.config=EkdConfig

		# Identifiant de la classe
		self.idSection = "animation_montage_video_et_audio"

		super(Animation_MontagVideoVidPlusAudio, self).__init__(boite='vbox', titre=_(u"Montage: Vidéo et audio"))
		self.printSection()

		self.repTampon = EkdConfig.getTempDir() + os.sep

		if os.path.isdir(self.repTampon) is False:
        		os.makedirs(self.repTampon)

		# Au cas où le répertoire existait déjà et qu'il n'était pas vide -> purge (simple précaution)
		for toutRepCompo in glob.glob(self.repTampon+'*.*'):
			os.remove(toutRepCompo)

		# Liste de fichiers initiaux (contenu dans le fichier de configuration)
		self.lstFichiersSource = []
		self.lstFichiersSourceProv = [] # idem mais provisoire (sert à récupérer les chemins dans l'ordre de sélection)

		self.lstFichiersSourceAudio = []
		self.lstFichiersSourceProvAudio = []

		# Par soucis de lisibilité, on préfère ne pas utiliser des objets présent dans le parent de cette façon
		# mais plutôt le passer en paramètre (le code est plus facile à lire de cette façon)
		self.statusBar = statusBar

		#-------------------------------------------------------------------
		self.afficheurVideoSource=SelectWidget(extensions = ["*.avi", "*.mpg", "*.mpeg", "*.mjpeg", "*.flv", "*.mp4", "*.h264", "*.dv", "*.vob"], mode="texte", video = True)
		###################################################################################
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"),self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
		#--------------------------------------------------------------------


		# -------------------------------------------------------------------
		# Boîte de groupe : "Fichiers source"
		# -------------------------------------------------------------------
		extFormat=[]
		for fmt in parent.soxSuppFormat :
			extFormat.append("*."+fmt)

		self.afficheurAudioSource=SelectWidget(extensions=extFormat ,mode="texte", audio = True)
		# Onglets
		self.indexAudioSource = self.add(self.afficheurAudioSource, _(u'Audio(s) source'))
		self.connect(self.afficheurAudioSource,SIGNAL("fileSelected"), self.getFileA)
		self.connect(self.afficheurAudioSource, SIGNAL("pictureChanged(int)"),  self.getFileA)
		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = ["video","audio"] # Défini le type de fichier source.
		self.typeSortie = "video" # Défini le type de fichier de sortie.
		self.sourceEntrees = [self.afficheurVideoSource, self.afficheurAudioSource] # Fait le lien avec le sélecteur de fichier source.


		self.addReglage("vbox")
		#=== Widget qui seront inclus dans la boite de réglage ===#
		self.ordreVideo = selectJoinMultipleSound(0, self)
		self.ordreVideo.setTitleAndTips(_(u"Fichiers vidéos à joindre"), _(u"Liste des fichiers vidéo à joindre. <b>Pour information, vous pouvez monter et descendre les fichiers grâce aux flèches haut et bas (les fichiers apparaissant en haut de la liste sont ceux qui seront au début du montage)</b>"))
		self.ordreAudio = selectJoinMultipleSound(0, self)
		self.ordreAudio.setTitleAndTips(_(u"Fichiers audios à joindre"), _(u"Liste des fichiers audio à joindre. <b>Pour information, vous pouvez monter et descendre les fichiers grâce aux flèches haut et bas (les fichiers apparaissant en haut de la liste sont ceux qui seront au début du montage)</b>")) 

		self.layoutReglage.addWidget(self.ordreVideo)
		self.layoutReglage.addWidget(self.ordreAudio)

		# ---------------------------
		# Boite de groupe de mplayer
		# ---------------------------
		self.addPreview()
		self.addLog()

	def getFile(self):
		'''
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.lstFichiersSource = self.afficheurVideoSource.getFiles()
		self.ordreVideo.addSoundAction(self.lstFichiersSource)
		self.ordreVideo.delFile(self.lstFichiersSource)

		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])

		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(True)

		self.emit(SIGNAL("loaded"))
		return self.chemin

	def getFileA(self):
		'''
		# On utilise la nouvelle interface de récupération des fichiers audio
		'''
		chemin = self.afficheurAudioSource.getFile()
		self.lstFichiersSourceAudio = self.afficheurAudioSource.getFiles()
		self.ordreAudio.addSoundAction(self.lstFichiersSourceAudio)
		self.ordreAudio.delFile(self.lstFichiersSourceAudio)
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([chemin])

		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(True)

		self.emit(SIGNAL("loaded"))
		return chemin

	def fctRadioSource(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: 
			self.mplayer.listeVideos = self.lstFichiersSource
			try :
				self.radioConvert.setChecked(False)
			except : None

	def fctRadioAudio(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: self.mplayer.listeVideos = self.lstFichiersSourceAudio

	def fctRadioConvert(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: 
			self.mplayer.listeVideos = self.lstFichiersSortie
			try :
				self.radioSource.setChecked(False)
			except : None

	def listeChemins(self, ch):
		"""transforme une chaine de caractères en liste de chemins"""
		lst = ch.split("'")
		for i in lst: # i: élément de la ligne
			if (',' in i) or ('[' in i) or (']' in i):
				lst.remove(i)
		return lst


	def ouvrirSourceAudio(self, nomEntree=None):
		""" Récupération des chemins vidéo sélectionnée """

		chemin = self.recupSourcesAudio(nomEntree)
		if not chemin: return

		self.lstFichiersSourceAudio = chemin

		self.lstFichiersSourceProvAudio = self.lstFichiersSourceAudio[:] # ':' car sinon on créé un alias

		# Ordonner la liste si l'option a été sélectionnée et l'afficher dans la ligne d'édition
		self.ordonnerListeAudio()

		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.listeVideos = self.lstFichiersSourceAudio
		self.radioAudio.setChecked(True)
		self.radioConvert.setEnabled(False)

		if len(self.lstFichiersSource)!=0:
			self.radioSource.setEnabled(True)
			self.radioAudio.setEnabled(True)
		else:
			self.radioSource.setEnabled(False)
			self.radioAudio.setEnabled(False)

		self.statusBar.showMessage(_(u"La vidéo résultante ne pourra pas être lue avec tous les logiciels"))


	def ouvrirSourceVideo(self, nomEntree=None):
		""" Récupération des chemins vidéo sélectionnée """
		chemin = self.recupSources(nomEntree)

		if not chemin: return

		self.lstFichiersSource = chemin

		self.lstFichiersSourceProv = self.lstFichiersSource[:] # ':' car sinon on créé un alias

		# Ordonner la liste si l'option a été sélectionnée et l'afficher dans la ligne d'édition
		self.ordonnerListeVideo()

		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.listeVideos = self.lstFichiersSource
		self.radioSource.setChecked(True)
		self.radioConvert.setEnabled(False)

		if len(self.lstFichiersSourceAudio)!=0:
			self.radioSource.setEnabled(True)
			self.radioAudio.setEnabled(True)
		else:
			self.radioSource.setEnabled(False)
			self.radioAudio.setEnabled(False)

		self.statusBar.showMessage(_(u"La vidéo résultante ne pourra pas être lue avec tous les logiciels"))


	def afficherAide(self):
		""" Boîte de dialogue de l'aide """

		super(Animation_MontagVideoVidPlusAudio, self).afficherAide(_(u"<p><b>Sous le terme de montage vidéo, vous pouvez ici assembler des vidéos pour en constituer une seule, mais aussi des fichiers audio. EKD peut assembler des vidéos (mais aussi des fichiers audio) de différentes nature (extensions).</b></p><p><b>Il est à noter ici (pour le Montage vidéo) que vous bénéficiez d'un traitement par lot, c'est à dire que tous les fichiers que vous allez charger seront traités (et pas seulement le fichier sélectionné).</b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Passez maintenant dans l'onglet <b>'Audio(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher votre/vos fichier(s) audio. Si vous voulez sélectionner plusieurs fichiers audio d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos fichiers), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner un fichier audio dans la liste et l'écouter (par le bouton juste à la droite de cette liste). De même si vous le désirez, vous pouvez obtenir des informations complètes sur le fichier audio sélectionné, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>Réglages</b>, vous pouvez changer l'ordre de montage des fichiers et ce en remontant ou en redescendant (par les flèches haut et bas) dans les listes <b>'Fichiers vidéo à joindre'</b> et <b>'Fichiers audio à joindre'</b>.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion.</p><p>Dans l'onglet <b>'Visionner vidéo'</b> vous pouvez visionner le résultat (avant la concaténation) en sélectionnant <b>'vidéo(s) source(s)'</b>, après la concaténation <b>'vidéo convertie'</b>.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos et fichiers audio chargés (avec leurs chemins exacts) avant et après conversion.</p>"))


	def stat_dim_video(self):
		"""Calcul statistique des dimensions des vidéos les plus présentes dans le lot"""

		from gui_modules_animation.infoVideo import infovideo

		listePrepaRedim = []

		# Détection des dimensions différentes (résolutions)
		# dans les vidéos chargées par l'utilisateur
		for parcVideoResolution in self.lstFichiersSource:
			info = infovideo(parcVideoResolution)
			listePrepaRedim.append((info.videoLargeur, info.videoHauteur))

		# 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)
		#print self.lStatDimSeq[0][1]
		EkdPrint(u"%s" % str(self.lStatDimSeq[0][1]))
		self.dimStatVideo=self.lStatDimSeq[0][1]

		'''
		print
		print "Toutes les dimensions des vidéos (avec le nbre de vidéos):", self.lStatDimSeq
		print 'Dimension des vidéos la plus presente dans la sequence:', self.dimStatVideo
		print "Nombre de tailles de vidéos différentes dans le lot :", len(self.lStatDimSeq)
		print
		'''
		EkdPrint(u'')
		EkdPrint(u"Toutes les dimensions des vidéos (avec le nbre de vidéos): %s" % self.lStatDimSeq)
		EkdPrint(u'Dimension des vidéos la plus presente dans la sequence: %s' % str(self.dimStatVideo))
		EkdPrint(u"Nombre de tailles de vidéos différentes dans le lot: %s" % str(len(self.lStatDimSeq)))
		EkdPrint(u'')

		if len(self.lStatDimSeq)>1:
			return 0
		else:
			return 1
	#########################################################################################

	def recupOrdreAV(self) :
		self.lstFichiersSource = self.ordreVideo.getListFile()
		self.lstFichiersSourceAudio = self.ordreAudio.getListFile()

	def appliquer(self, nomSortie=None, ouvert=1):
		"""Fusion de vidéos"""

		self.recupOrdreAV()
		# ----- Vidéo ------ #
		self.stat_dim_video()
		resolution = self.dimStatVideo

		# Chemin du répertoire temporaire
		rep_video_ext_resol = self.repTampon  + 'video_extension_resol' + os.sep

		# Scan des extensions des vidéos chargées pour concaténation des vidéos
		lVideoExt = [parcVidExt.split('.') for parcVidExt in self.lstFichiersSource]
		lExtVideo = [parcExtVideo[1].lower() for parcExtVideo in lVideoExt]

		# On elimine les doublons s'il y en a. TTes les
		# extensions différentes sont mises en avant
		uniqExtVideo = list(set(lExtVideo))

		# ----- Audio ------ #
		# Chemin du répertoire temporaire
		rep_video_audio = self.repTampon  + 'concat_audio' + os.sep

		# Scan des extensions des fichiers audio chargés pour concaténation
		lAudioExt = [parcAudExt.split('.') for parcAudExt in self.lstFichiersSourceAudio]
		lExtAudio = [parcExtAudio[1].lower() for parcExtAudio in lAudioExt]

		# On elimine les doublons s'il y en a. TTes les
		# extensions différentes sont mises en avant
		uniqExtAudio = list(set(lExtAudio))

		#######################################################################

		#=== Récupération du fichier de sortie ===#
		chemin=unicode(self.ordreVideo.getListFile())

		if not nomSortie:
			# suffix du fichier actif
			#suffix=os.path.splitext(chemin)[1]
			suffix = u".avi"

			##############################################################################
			# Si la liste contient plus d'un élément, c'est à dire si elle contient des
			# extensions différentes et si la résolution des vidéos est différente.
			# ----------------------------------------------------------------------------
			# Ici l'extension à la sortie sera en avi car on encode en Motion JPEG.
			if len(uniqExtVideo) > 1 or len(self.lStatDimSeq) > 1: suffix = '.avi'
			##############################################################################
                        saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver"))
			self.cheminFichierEnregistrerVideo = saveDialog.getFile()
			####################################################################################################################

		else: # module séquentiel
			self.cheminFichierEnregistrerVideo=nomSortie

		if not self.cheminFichierEnregistrerVideo: return

		# Video
		# Chemin de sortie temporaire de la vidéo
		self.cheminVideoProv = self.repTampon+u"video"

		# Audio
		# Chemin de sortie temporaire de l'audio
		self.cheminAudioProv = self.repTampon+u"audio.wav"

		if os.path.isdir(rep_video_ext_resol) is False: os.makedirs(rep_video_ext_resol)

		#from moteur_modules_animation.mencoder_concat_video import WidgetMEncoderConcatExtResol
		from gui_modules_common.mencoder_concat_gui import WidgetMEncoderConcatExtResol

		self.sourceVideo = self.lstFichiersSource
		# Si la liste contient plus d'un élément, c'est à dire si elle contient des
		# extensions différentes et si la résolution des vidéos est différente
		if len(uniqExtVideo) > 1 or len(self.lStatDimSeq) > 1:
			wmcer = WidgetMEncoderConcatExtResol(self.lstFichiersSource, valeurNum=resolution)
			wmcer.setWindowTitle(_(u"Traitement vidéo (extension et résolution)"))
			wmcer.exec_()

			# Les vidéos chargées en vue de la concaténation sont maintenant ds
			# le rep tampon .../video_extension_resol
			self.lstFichiersSource = glob.glob(unicode(rep_video_ext_resol+'*.*'))
			if debug : 
				#print self.lstFichiersSource
				EkdPrint(u"%s" % self.lstFichiersSource)
			# Mise en ordre (numérotation) car cela peut
			# poser problème au moment de la concaténation
			self.lstFichiersSource.sort()
			if debug : 
				#print "sources tmp mise en ordre : ", self.lstFichiersSource
				EkdPrint(u"sources tmp mise en ordre : %s" % self.lstFichiersSource)
			# Comme l'encodage se fait ici en Motion JPEG
			# (AVI) l'extension doit être .avi
			suffix = '.avi'

		else:
			# Au cas où le répertoire existait déjà et qu'il n'était pas vide -> purge
			# ... ici dans le cas de vidéos avec la même extension et la même résolution
			if os.path.isdir(rep_video_ext_resol) is True:
				for toutRepCompoVideo in glob.glob(rep_video_ext_resol+'*.*'):
					os.remove(toutRepCompoVideo)
		##################################################################################################

		self.sourceAudio = self.lstFichiersSourceAudio
		self.process = soxProcessMulti(self.lstFichiersSourceAudio, self.cheminAudioProv, u"")
		self.process.show()
		self.process.run()
		self.connect(self.process,SIGNAL("endProcess"),self.endProcess)

	def endProcess(self, sortie) :
		# Suite du process

		self.process.close()
			# Les fichiers audio chargés en vue de la concaténation
			# sont maintenant ds le rep tampon .../concat_audio
		
		###################################################################################################
		### Concaténation élégante (à transformer en objet dans le package moteur) ###############
		# on écrit dans "output" (descripteur de fichier de cheminVideoProv)
		try:
			output = open(self.cheminVideoProv, 'wb+')
			for cheminFichier in self.lstFichiersSource:
				input = open(cheminFichier, 'rb')
				output.write(input.read())
				input.close()
			output.close()
		except Exception, details:
			#print "**** DEBUG: Erreur dans l'ouverture du fichier : " , details
			EkdPrint(u"**** DEBUG: Erreur dans l'ouverture du fichier : %s" % details)
		# Maintenant cheminVideoProv contient la concaténation de tous fichiers
		###################################################################################################

		#=== Commandes de concaténation finale ===#
		if debug : 
			#print self.cheminVideoProv, self.cheminAudioProv, self.cheminFichierEnregistrerVideo
			EkdPrint(u"%s %s %s" % (self.cheminVideoProv, self.cheminAudioProv, self.cheminFichierEnregistrerVideo))
		
		try:
			mencoder = WidgetMEncoder('fusion_audio_et_video_2', (self.cheminVideoProv, self.cheminAudioProv), self.cheminFichierEnregistrerVideo, laisserOuvert=1)
			mencoder.setWindowTitle(_(u"Fusion des fichiers vidéos et audios"))
			mencoder.exec_()
		except:
			messageErrAnEnc=QMessageBox(self)
			messageErrAnEnc.setText(_(u"Problème lors de l'étape de concaténation vidéo et audio (mencoder)"))
			messageErrAnEnc.setWindowTitle(_(u"Erreur"))
			messageErrAnEnc.setIcon(QMessageBox.Warning)
			messageErrAnEnc.exec_()
			os.remove(self.cheminVideoProv)
			os.remove(self.cheminAudioProv)
			return

		# Suppression des fichiers temporaires
		os.remove(self.cheminVideoProv)
		os.remove(self.cheminAudioProv)

		self.lstFichiersSortie = self.cheminFichierEnregistrerVideo # pour la boite de dialogue de comparaison
		self.radioConvert.setEnabled(True)
		self.radioConvert.setChecked(True)
		self.radioSource.setEnabled(True)
		self.radioSource.setChecked(False)
		#self.radioAudio.setEnabled(True)
		self.infoLog(None, self.sourceVideo, self.sourceAudio, self.lstFichiersSortie)
		return self.lstFichiersSortie # module séquentiel
Ejemplo n.º 2
0
class MusiqueSon_normalize(Base):
	# -----------------------------------
	# Cadre accueillant les widgets de :
	# Musique-Son >> Encodage
	# -----------------------------------
	def __init__(self, parent):
		# -------------------------------
		# Parametres généraux du widget
		# -------------------------------
		self.config=EkdConfig

		self.parent = parent
		#=== Identifiant de la classe ===#
		self.idSection = "son_normaliser_convertir_musique_ou_son"

		super(MusiqueSon_normalize, self).__init__(None, None, None, 'vbox') # Base module de animation
		self.setTitle(_(u"Normaliser et convertir un fichier audio"))
		self.printSection()

		#------------------------------------------------------------------------
		# TabWidget pour les réglages et pour l'écoute du résultat
		#------------------------------------------------------------------------
		extFormat=[]
		for fmt in self.parent.soxSuppFormat :
			extFormat.append("*."+fmt)

		# Widget standard de sélection de fichier audio dans le tab standard
		self.selectionAudioFile = SelectWidget(extensions = extFormat, mode="texte", audio = True)
		# Onglets
		self.tab.insertTab(0,self.selectionAudioFile, _(u'Son(s) source'))
		self.connect(self.selectionAudioFile,SIGNAL("fileSelected"),self.synchroFiles)
		self.connect(self.selectionAudioFile, SIGNAL("pictureChanged(int)"), self.synchroFiles)
		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "audio" # Défini le type de fichier source.
		self.typeSortie = "audio" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.selectionAudioFile # Fait le lien avec le sélecteur de fichier source.

		# -------------------------------------------------------------------
		# Sélection et affichage des fichiers à joindre : "Fichiers audio source"
		# -------------------------------------------------------------------
		reglage = QWidget()
		regVLayout = QVBoxLayout(reglage)
		self.selectionFile = choixSonSortieWidget(self.parent.soxSuppFormat, parent=self)
		regVLayout.addWidget(self.selectionFile)
		niveau = QGroupBox(_(u"Choix du niveau"))
		self.choix1 = QRadioButton(_(u"- 3 dB"))
		self.choix2 = QRadioButton(_(u"- 6 dB"))
		self.choix3 = QRadioButton(_(u"- 9 dB"))
		self.choix1.setChecked(True)
		nivLayout = QHBoxLayout()
		nivLayout.addWidget(self.choix1)
		nivLayout.addWidget(self.choix2)
		nivLayout.addWidget(self.choix3)
		niveau.setLayout(nivLayout)
		regVLayout.addWidget(niveau)

		mode = QGroupBox(_(u"Choix du mode de normalisation"))
		self.choixm1 = QRadioButton(_(u"Standard"))
		self.choixm2 = QRadioButton(_(u"Individuel"))
		self.choixm3 = QRadioButton(_(u"Balancé"))
		self.choixm1.setChecked(True)
		modeLayout = QHBoxLayout()
		modeLayout.addWidget(self.choixm1)
		modeLayout.addWidget(self.choixm2)
		modeLayout.addWidget(self.choixm3)
		mode.setLayout(modeLayout)
		regVLayout.addWidget(mode)

		regVLayout.addStretch()
		self.tab.addTab(reglage,_(u"Réglage"))

		#------------------------------------------------------------------------
		# Ecoute du résultat
		#------------------------------------------------------------------------
		#=== Widgets mplayer ===#
		widget = QWidget()
		vboxMplayer = QVBoxLayout(widget)
		vboxMplayer.addStretch()
		hbox = QHBoxLayout()
		vboxMplayer.addLayout(hbox)
		hbox.addStretch()

		self.mplayer=Mplayer(taille=(300,270), facteurLimitant=Mplayer.LARGEUR,
			choixWidget=(Mplayer.PAS_PRECEDENT_SUIVANT,Mplayer.CURSEUR_A_PART))
		self.mplayer.setAudio(True)
		hbox.addWidget(self.mplayer)
		hbox.addStretch()
		vboxMplayer.addStretch()

		self.mplayer.setEnabled(False)
		self.tab.addTab(widget, _(u"Son créé"))

		### pour les infos supplémentaires
		self.addLog()
		# -------------------------------------------------------------------
		# widgets du bas : ligne + boutons Aide et Appliquer
		# -------------------------------------------------------------------

	def synchroFiles(self):
		if len(self.selectionAudioFile.getFiles()) > 0 :
			self.boutApp.setEnabled(True)
		else :
			self.boutApp.setEnabled(False)

	def endProcess(self, sortie) :
		self.mplayer.setEnabled(True)
		self.mplayer.listeVideos = [sortie]
		self.tab.setCurrentIndex(1)
		self.infoLog(None, None, self.selectionAudioFile.getFile(), sortie)

	def afficherAide(self):
		""" Boîte de dialogue de l'aide """
		self.aide = EkdAide(550,400,self)
		self.aide.setText(_(u"<p><b>Ici vous pouvez normaliser des fichiers audio, c'est à dire ajuster leur volume sonore. Voici une définition selon Wikipédia de la normalisation audio:<br>http://fr.wikipedia.org/wiki/Normalisation_audio</b></p><p>Dans l'onglet <b>Son(s) source</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les fichiers(s) audio. Si vous voulez sélectionner plusieurs fichiers audio d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos fichiers audio), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner un fichier audio dans la liste et le lire (par le bouton juste à la droite de cette liste). De même si vous le désirez, vous pouvez obtenir des informations complètes sur le fichier audio sélectionné, et ce par le bouton <b>Infos</b> (en bas).</p><p>Dans l'onglet <b>Réglages</b> sélectionnez le <b>Format du fichier de sortie</b>, et s'il le faut <b>Réglage expert</b> et faites éventuellement le réglage de la <b>Qualité d'encodage</b>, puis réglez le <b>Choix du niveau</b> et le <b>Choix du mode de normalisation</b>.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>Appliquer</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>Nom de fichier</b>, cliquez sur le bouton <b>Enregistrer</b> et attendez le temps de la conversion. A la fin du traitement cliquez sur le bouton <b>Fermer</b> de la fenêtre <b>Affichage de la progression</b>.</p><p>Dans l'onglet <b>Son créé</b> vous pouvez lire le résultat.</p><p>L'onglet <b>Infos</b> vous permet de voir les fichiers audio chargés (avec leurs chemins exacts) avant et après conversion.</p>"))
		self.aide.show()

	def appliquer(self):
		""" appelle la boite de dialogue de sélection de fichier à sauver """

		suffixSortie = u"."+self.selectionFile.getFileExt()
		saveDialog = EkdSaveDialog(self, mode="audio", suffix=suffixSortie, title=_(u"Sauver"))
		cheminAudioSorti = saveDialog.getFile()


		if not cheminAudioSorti: return

		# récupération du chemin des fichiers audio source
		cheminAudioSource=self.selectionAudioFile.getFile()
		# suffix du fichier actif
		suffix=os.path.splitext(cheminAudioSource)[1]
		if suffix == u".mp3" or suffix == u".MP3" :
			chemin = u"-t mp3 \""+cheminAudioSource+u"\""
		elif suffix == u".ogg" or suffix == u".OGG" :
			chemin = u"-t ogg \""+cheminAudioSource+u"\""
		elif suffix == u".mp2" or suffix == u".MP2" :
			chemin = u"-t mp2 \""+cheminAudioSource+u"\""
		elif suffix == u".flac" or suffix == u".FLAC" :
			chemin = u"-t flac \""+cheminAudioSource+u"\""
		else :
			chemin = u"\""+cheminAudioSource+u"\""

		# Définition des options
		if self.choix1.isChecked() :
			db = " -3 "
		if self.choix2.isChecked() :
			db = " -6 "
		if self.choix3.isChecked() :
			db = " -9 "
		if self.choixm1.isChecked() :
			mde = ""
		if self.choixm2.isChecked() :
			mde = "-i"
		if self.choixm3.isChecked() :
			mde = "-b"
		option = "norm "+mde+db

		if self.selectionFile.reglageExp.getExpertState() :
			regExp = self.selectionFile.reglageExp.getC()
		else :
			regExp = u""

		# Encodage
		self.process = SoxProcess(chemin, cheminAudioSorti, 1, u"", u""+regExp, option, self.parent)
		self.process.setSignal(u"Input",u"In:")
		self.process.show()
		self.process.run()
		self.connect(self.process,SIGNAL("endProcess"),self.endProcess)

	def load(self) :
		self.selectionAudioFile.loadFileLocation(self.idSection)
		self.selectionFile.reglageExp.loadConfig(self.idSection)
		self.choix1.setChecked(int(EkdConfig.get(self.idSection, 'choix1')))
		self.choix2.setChecked(int(EkdConfig.get(self.idSection, 'choix2')))
		self.choix3.setChecked(int(EkdConfig.get(self.idSection, 'choix3')))
		self.choixm1.setChecked(int(EkdConfig.get(self.idSection, 'choixm1')))
		self.choixm2.setChecked(int(EkdConfig.get(self.idSection, 'choixm2')))
		self.choixm3.setChecked(int(EkdConfig.get(self.idSection, 'choixm3')))

	def save(self) :
		self.selectionAudioFile.saveFileLocation(self.idSection)
		self.selectionFile.reglageExp.saveConfig(self.idSection)
		EkdConfig.set(self.idSection, u'choix1', unicode(int(self.choix1.isChecked())))
		EkdConfig.set(self.idSection, u'choix2', unicode(int(self.choix2.isChecked())))
		EkdConfig.set(self.idSection, u'choix3', unicode(int(self.choix3.isChecked())))
		EkdConfig.set(self.idSection, u'choixm1', unicode(int(self.choixm1.isChecked())))
		EkdConfig.set(self.idSection, u'choixm2', unicode(int(self.choixm2.isChecked())))
		EkdConfig.set(self.idSection, u'choixm3', unicode(int(self.choixm3.isChecked())))
Ejemplo n.º 3
0
class Animation_ReglagesDivers(Base):
	# ----------------------------------------------------------------------------------------
	# Cadre accueillant les widgets de : Animation >> Séparer le flux vidéo et le flux audio
	# ----------------------------------------------------------------------------------------

	def __init__(self):
		# -------------------------------
		# Parametres généraux du widget
		# -------------------------------
		vbox=QVBoxLayout()

		#=== Variable de configuration ===#
		self.config=EkdConfig

		#=== Identifiant de la classe ===#
		self.idSection = "animation_reglages_divers"

		super(Animation_ReglagesDivers, self).__init__('hbox', titre=_(u"Nombre d'images par seconde"))
		self.printSection()

		# -------------------------------------------------------------------
		# Boîte de groupe : "Fichier vidéo source"
		# -------------------------------------------------------------------
		self.afficheurVideoSource=SelectWidget(extensions = ["*.avi", "*.dv"], mode="texte", video = True)
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"),self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
		#--------------------------------------------------------------------

		self.lstFichiersSortie = []
		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "video" # Défini le type de fichier source.
		self.typeSortie = "video" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurVideoSource # Fait le lien avec le sélecteur de fichier source.

		# -------------------------------------------------------------------
		# Boîte de groupe "Réglage de sortie de l'encodage"
		# -------------------------------------------------------------------
		groupReglage = QGroupBox()
		self.layoutReglage = QHBoxLayout(groupReglage)
		##=== Widget qui seront inclus dans la boite de réglage ===#

		# boite de spin
		self.spin=QSpinBox()		# self car on va récupérer la variable depuis le moteur
		self.spin.setRange(1,60) # valeur 22 par défaut
		self.connect(self.spin,SIGNAL("valueChanged(int)"),self.spinChange)

		# curseur
		self.curseur = QSlider(Qt.Horizontal)
		self.curseur.setRange(1,60)
		self.connect(self.curseur, SIGNAL("sliderMoved(int)"), self.curseurChange)

		#=== Chargement du paramètre de configuration ===#
		try:
			self.spin.setValue(int(self.config.get(self.idSection,'spin')))
		except:
			self.spin.setValue(22)

		# info
		txt2 = _(u"La vidéo source contient")
		txt3=_(u"img/s")
		self.info=QLabel("%s x %s" %(txt2,txt3))

		self.layoutReglage.addWidget(self.spin)
		self.layoutReglage.addWidget(self.curseur)
		self.layoutReglage.addWidget(self.info)

		self.add(groupReglage, _(u"Réglages"))
		self.addPreview()
		self.addLog()

	def getOutputFiles(self, typeSortie) :
		"""Fonction standard pour faire le lien avec le tampon EKD pour récupérer les fichier de sortie du type typeSortie"""
		if typeSortie == "video" :
			return self.lstFichiersSortie


	def getFile(self):
		'''
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])

		self.emit(SIGNAL("loaded"))
		return self.chemin


	def spinChange(self,i):
		"""conserver le spin dans le fichier de configuration et modifie le curseur"""
		#print "nbr img/s :", i
		EkdPrint(u"nbr img/s : %d" % i)
		# sauver spin
		self.config.set(self.idSection,'spin',i)
		# sauver curseur
		self.curseur.setValue(i)


	def curseurChange(self, i):
		"""Quand on change la position du curseur la valeur du spin est automatiquement modifiée"""
		self.spin.setValue(i)


	def fctRadioSource(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""

		if bool: self.mplayer.listeVideos = [[self.chemin]]


	def fctRadioConvert(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: self.mplayer.listeVideos = self.lstFichiersSortie


	def nbrImgSecIni(self):
		""" Calcule et affiche le nombre d'images par seconde de la vidéo source """
		chemin = self.getFile()
		fps = 0
		if chemin:
			fps = str(int(getVideoFPS(chemin)[0]))
		if fps:
			txt1 = _(u"La vidéo source contient")
			txt2=_(u"img/s")
			self.info.setText(u"%s %s %s" %(txt1,fps,txt2))

	####################################################################################


	def ouvrirSource(self, nomEntree=None):
		"""Récupération du chemin de la vidéo sélectionnée et activation de certains widgets"""

		chemin = self.getFile()

		if not chemin: return
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.listeVideos = [chemin]
		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(False)
		self.radioConvert.setEnabled(False)
		self.boutCompare.setEnabled(False)

		self.nbrImgSecIni()


	def getFile(self):
		'''
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])

		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(True)
		self.boutApp.setEnabled(True)
		if self.idSection == "animation_filtresvideo":
			self.boutApercu.setEnabled(True)
			self.filtreDecouper.setButtonEnabled(True)
		# On emet un signal quand le fichier est chargé
		self.emit(SIGNAL("loaded"))
		return self.chemin
	###################################################################################


	def afficherAide(self):
		""" Boîte de dialogue de l'aide """

		super(Animation_ReglagesDivers,self).afficherAide(_(u"<p><b>Vous pouvez ici changer le nombre d'images par seconde dans une vidéo.</b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>'Réglages'</b> réglez le nombre d'images par seconde.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion. A la fin cliquez sur le bouton <b>'Voir les informations d'encodage'</b> et fermez cette dernière fenêtre après avoir vu les informations en question.</p><p>Dans l'onglet <b>'Visionner vidéo'</b> vous pouvez visionner le résultat (avant la conversion) en sélectionnant <b>'vidéo(s) source(s)'</b>, après la conversion <b>'vidéo convertie'</b> ou bien encore les deux en même temps, en cliquant sur le bouton <b>'Comparateur de vidéos'</b>.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos chargées (avec leurs chemins exacts) avant et après conversion.</p>"))


	def appliquer(self, nomSortie=None, ouvert=1):
		""" appelle la boite de dialogue de sélection de fichier à sauver et appel de la fonction de changement du nombre d'images par seconde """

		#=== Détermination des chemins d'entrée et sortie ===#
		chemin=unicode(self.getFile())

		if not nomSortie:
			# suffix du fichier actif
			suffix=os.path.splitext(chemin)[1]
                        saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver"))
			cheminFichierEnregistrerVideo = saveDialog.getFile()

		else: # module séquentiel
			cheminFichierEnregistrerVideo = nomSortie

		if not cheminFichierEnregistrerVideo: return
		###################################################################################

		nbrImgSec = str(self.spin.value())

		try:
			#### le changement de framerate est maintenant géré par FFmpeg #####
			ffmpeg = WidgetFFmpeg('idx', chemin, cheminFichierEnregistrerVideo, valeurNum=nbrImgSec, laisserOuvert=ouvert)
			ffmpeg.setWindowTitle(_(u"Réglage divers"))
			ffmpeg.exec_()
			###########################################################################################

		except:
			messageErrAnEnc=QMessageBox(self)
			messageErrAnEnc.setText(_(u"Problème lors du changement du nombre d'images par seconde (mencoder)"))
			messageErrAnEnc.setWindowTitle(_(u"Erreur"))
			messageErrAnEnc.setIcon(QMessageBox.Warning)
			messageErrAnEnc.exec_()
			return

		self.lstFichiersSortie = cheminFichierEnregistrerVideo # chemin de la vidéo convertie pour le 2ème mplayer
		self.radioSource.setEnabled(True)
		self.radioConvert.setEnabled(True)
		self.radioSource.setChecked(False)
		self.radioConvert.setChecked(True)
		self.boutCompare.setEnabled(True)
		self.infoLog(None, chemin, None, cheminFichierEnregistrerVideo)
		return self.lstFichiersSortie # module séquentiel


	def sequentiel(self, entree, sortie, ouvert=0):
		"""Utile dans le module du même nom. Applique les opérations de la classe. Retourne le vrai nom du fichier de sortie"""
		self.ouvrirSource(entree)
		return self.appliquer(sortie, ouvert)


	def sequentielReglage(self):
		"""Utile dans le module du même nom. Récupère le widget de réglage associé à l'identifiant donné en 1er argument. Retourne l'instance du widget de réglage"""
		return self.groupReglage

	def saveFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.saveFileLocation(self.idSection)

	def loadFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.loadFileLocation(self.idSection)

	def load(self):
		'''
		Chargement de la configuration de tous les objets
		'''
		self.loadFiles()

	def save(self):
		'''
		Sauvegarde de la configuration de tous les objets
		'''
		self.saveFiles()
Ejemplo n.º 4
0
class Image_Divers_Info(QWidget):
	# -----------------------------------
	# Cadre accueillant les widgets de :
	# Image >> Divers >> Information
	# -----------------------------------
	def __init__(self, geometry):
        	QWidget.__init__(self)
		
		# Boite d'alignement vertical
		vbox=QVBoxLayout(self)
		
		# Est-ce que l'image a déjà été affichée une fois ?
		# -> gain de temps au niveau de l'affichage
		self.drapeauImage = 0 # 0: non ; 1: oui
		
		# Fonctions communes à plusieurs cadres du module Image
		self.base = Base()

		# Paramètres de configuration
		self.config = EkdConfig
		self.idSection = "image_divers_infos"
		# Log du terminal
		self.base.printSection(self.idSection)
		# Fonction appelant la fenêtre principale
		self.mainWindowFrameGeometry = geometry
		
		self.chemin = []

		# Là où s'afficheront les images
		self.afficheurImgSource=SelectWidget(geometrie = self.mainWindowFrameGeometry)
		
		# -----------
		# Onglets
		# -----------
		
		# Onglets
		self.tab = QTabWidget()
		
		#== onglet chargement d'images ===#
		
		self.indexTabImgSource = self.tab.addTab(self.afficheurImgSource, _(u'Image(s) source'))
		
		#== onglet d'information textuelle ===#
		
		self.zoneTexte = QTextEdit("")
		if PYQT_VERSION_STR < "4.1.0":
			self.zoneTexte.setText = self.zoneTexte.setPlainText
		self.zoneTexte.setReadOnly(True)
		
		frame = QFrame()
		hboxTab = QHBoxLayout(frame)
		hboxTab.addWidget(self.zoneTexte)
		
		self.tabIndexText = self.tab.addTab(frame, _("Infos Texte"))
		
		#== onglet d'information exif ===#
		
		self.table = QTableWidget()
		self.tabIndexExif = self.tab.addTab(self.table, _("Infos Exif"))

		vbox.addWidget(self.tab)
		
		# -------------------------------------------------------------------
		# widgets du bas : ligne + boutons
		# -------------------------------------------------------------------
		
		# boutons
		boutAide=QPushButton(_(u" Aide"))
		boutAide.setIcon(QIcon("Icones/icone_aide_128.png"))
		self.connect(boutAide, SIGNAL("clicked()"), self.afficherAide)
	
		self.boutSauverInfos=QPushButton(_(u" Sauver infos (.txt)"))
		# Show infos (.txt) ==> Sauver infos (.txt)
		self.boutSauverInfos.setIcon(QIcon("Icones/icone_appliquer_128.png"))
		self.boutSauverInfos.setEnabled(False)
		self.connect(self.boutSauverInfos, SIGNAL("clicked()"), self.sauverInfos)
		
		self.boutSauverExif=QPushButton(_(u" Sauver Exif (.html)"))
		# Save Exif (.html) ==> Sauver Exif (.html)
		self.boutSauverExif.setIcon(QIcon("Icones/icone_appliquer_128.png"))
		self.boutSauverExif.setEnabled(False)
		self.connect(self.boutSauverExif, SIGNAL("clicked()"), self.sauverExif)

		# 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()	# espace entre les 2 boutons
		hbox.addWidget(self.boutSauverInfos)
		hbox.addWidget(self.boutSauverExif)
		vbox.addLayout(hbox)
		
		# affichage de la boîte principale
		self.setLayout(vbox)
		
		#----------------------------------------------------------------------------------------------------
		# Signal de présence d'images dans ler widget de sélection -> appelle la fonction appliquer
		#----------------------------------------------------------------------------------------------------
		
		self.connect(self.afficheurImgSource, SIGNAL("pictureChanged(int)"), self.modifImgSource)
		self.connect(self.afficheurImgSource, SIGNAL("pictureSelected()"), self.appliquer)


	def modifImgSource(self):
		"""Les infos de la 1ère image de la liste sont affichées dans les onglets (soit les
		infos exif+texte, soit uniquement les infos texte"""
#		print "Images chargées"
		self.appliquer()
	
	
	def appliquer(self):
		"""Un chemin de fichier-image a été enregistré -> affichage des infos et possibilité de sauvegarder"""
		#print '\nfonction imAccepte'
		EkdPrint(u'\nfonction imAccepte')
		#print "chemin image d'entrée:", self.chemin, type(self.chemin)

		# Récupération de la liste des fichiers chargés
		self.chemin=self.afficheurImgSource.getFile()
		
		# Récupération du chemin + image chargée et de l'extension
		fich, self.ext=os.path.splitext(self.chemin)
		
		#=== remplissage des onglets avec les infos disponibles ===#
		
		#||| infos texte |||#
		self.afficherInfos()
		# Activation du bouton pour sauvegarder les infos texte
		self.boutSauverInfos.setEnabled(True)
		
		#||| infos exif et infos texte ... avec conditions |||#
		imgExif=Image.open(self.chemin)
		# Seulement si l'extension sélectionnée est en JPEG (et que l'image chargée
		# contient des données EXIF), la fonction self.afficherExif est exécutée
		if self.ext in [".jpg", ".JPG", ".jpeg", ".JPEG"] and 'Profile-exif:' in self.InfosTxtImage:
			exifdata=imgExif._getexif()
			self.afficherExif()
			# La page infos exif est sélectionnée
			self.tab.setCurrentIndex(self.tabIndexExif)
			# Activation du bouton pour sauvegarder les infos exif
			self.boutSauverExif.setEnabled(True)
		# Seulement si l'extension sélectionnée est en JPEG (mais que l'image chargée ne 
		# contient pas de données EXIF), la fonction self.afficherInfos est exécutée,
		# et rien n'est affiché dans la page Infos Exif
		elif self.ext in [".jpg", ".JPG", ".jpeg", ".JPEG"] and 'Profile-exif:' not in self.InfosTxtImage:
			# La page infos exif se vide complètement (lignes et colonnes)
			self.table.setRowCount(0)
			self.table.setColumnCount(0)
			self.afficherInfos()
			# La page infos texte est sélectionnée
			self.tab.setCurrentIndex(self.tabIndexText)
			# Non activation du bouton pour sauvegarder les infos exif
			self.boutSauverExif.setEnabled(False)
		# Seulement si l'extension sélectionnée est tout autre que JPEG (c'est à 
		# dire/et que l'image chargée ne contient pas de données EXIF), la fonction
		# self.afficherInfos est exécutée, et rien n'est affiché dans la page Infos Exif
		elif self.ext not in [".jpg", ".JPG", ".jpeg", ".JPEG"]:
			self.table.setRowCount(0)
			self.table.setColumnCount(0)
			self.afficherInfos()
			self.tab.setCurrentIndex(self.tabIndexText)
			# Activation du bouton pour sauvegarder les infos exif
			self.boutSauverExif.setEnabled(False)
		
		# Dégriser les onglets
		self.tab.setEnabled(True)
		
		# L'image n'a jamais été affichée
		self.drapeauImage = 0
	
	
	def afficherInfos(self):
		"""afficher les infos venant d'ImageMagick"""
		
		# Formats d'images acceptés (aussi ici en tout début) --> Rectifié le 25/12/08
		
		if self.ext in [".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".gif", ".GIF", ".tif", ".TIF", ".tiff", ".TIFF", ".ppm", ".PPM", ".bmp", ".BMP"]:
			
			self.listeInfo=[]
			
			chemin = self.chemin.encode("UTF8")
			
			#self.ImageInfos=os.popen('identify -verbose'+' '+"\""+chemin+"\"").readlines()
                        commande = 'identify -verbose \"%s\"' % chemin
                        processus = QProcess()
                        processus.start(commande)
                        if (not processus.waitForStarted()):
                            raise (u"Erreur de récupération des informations")
                        if ( not processus.waitForFinished()):
                            raise (u"Erreur de récupération des informations")
			# QString enlevé car pose de gros problèmes
                        #self.ImageInfos = QString(processus.readAllStandardOutput())
			self.ImageInfos = processus.readAllStandardOutput()
			
			# N'affichera que les infos humainement compréhensibles
			# (dans le cas d'une image avec données EXIF)
			self.listeInfo=[]
			for parcInfo in self.ImageInfos:
				# Ignore les données commençant par ...
				if parcInfo.startswith("0x0") or parcInfo.startswith("    Components Configuration:") or parcInfo.startswith("    Components Configuration:") or parcInfo.startswith("    Maker Note:") or parcInfo.startswith("    CFA Pattern:") or parcInfo.startswith("    User Comment:") or parcInfo.startswith("    File Source:") or parcInfo.startswith("    unknown:") or parcInfo.startswith("    Scene Type:") or parcInfo.startswith("    Print Image Matching:"):
					pass
				else:
					self.listeInfo.append(parcInfo)
			
			a=_(u"Vous ne pouvez sélectionner/afficher les infos que d'une image à la fois.\n==========================================================\nLes formats d'images acceptés sont: .jpg, .jpeg, .png, .gif, .tif, .tiff, .ppm, .bmp. Pour une image avec des informations EXIF, seulement le format .jpg (ou .jpeg, .JPG, .JPEG) est accepté.\n==========================================================\n")
			
			
			# Affichage dans zoneTexte
			self.InfosTxtImage=''.join(self.listeInfo)
			self.zoneTexte.setText(a+"\n"+self.InfosTxtImage)

		else:
			messageErreur=QMessageBox(self)
			messageErreur.setText(_(u"Une erreur est apparue! Cette extension (%s) n'est pas supportée" % self.ext))
			messageErreur.setWindowTitle(_(u"Erreur"))
			messageErreur.setIcon(QMessageBox.Critical)
			messageErreur.exec_()
		
		
	def sauverInfos(self):
		"""Sauver les infos (sous forme de fichier texte)"""
		
		try:

			# Utilisation de la nouvelle boîte de dialogue de sauvegarde
			suffix=""
			# Boîte de dialogue pour sauvegarder (nom du nouveau fichier)
                        sauver = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=False)
			sauver = sauver.getFile()
			
			if not sauver: return
			enrTxt=open(sauver+'.txt', 'wb')
			enrTxt.write(self.InfosTxtImage.encode("UTF8"))
			enrTxt.flush()
			enrTxt.close()
		
		except:
			messageErreur=QMessageBox(self)
			messageErreur.setText(_(u"Une erreur est apparue!"))
			messageErreur.setWindowTitle(_(u"Erreur"))
			messageErreur.setIcon(QMessageBox.Critical)
			messageErreur.exec_()
		
	
	def miseAJourTable(self, nbrCol, donnees):
		""" On met à jour la table exif """
		self.table.clear()
		self.table.setRowCount(len(donnees))
		self.table.setColumnCount(nbrCol)
		self.table.setHorizontalHeaderLabels([_(u"Paramètre"), _(u"Valeur")])
		self.table.setAlternatingRowColors(True)
		self.table.setEditTriggers(QTableWidget.NoEditTriggers)
		self.table.setSelectionMode(QTableWidget.ExtendedSelection)
		for indexL, ligne in enumerate(donnees):
			for indexC, element in enumerate(ligne):
				item = QTableWidgetItem(str(element))
				item.setTextAlignment(Qt.AlignCenter)
				self.table.setItem(indexL, indexC, item)
				#print indexL, indexC, element
				EkdPrint(u"%s %s %s" % (indexL, indexC, element))
        	self.table.resizeColumnsToContents()
	
	
	def afficherExif(self):
		"""Afficher les infos Exif"""
		
		try :
			# -----------------------------------------------------------------------
			# Extraction des donnees EXIF
			# -----------------------------------------------------------------------
			# Aide de Terry Carroll sur la liste de diffusion : 
			# image-sig at python.org .
			# Avec une petite adaptation personnelle. Merci à Terry Carroll.
			
			imgExif=Image.open(self.chemin.encode("UTF8"))
			
			# Recupération des infos EXIF
			exifdata=imgExif._getexif()
			
			listeExif=[]
	
			for keyExif, valueExif in zip(exifdata.keys(), exifdata.values()):
	
				try:
					if ExifTags.TAGS[keyExif] in ['UserComment', 'ComponentsConfiguration', 'CFAPattern', 'FileSource', 'SceneType', 'MakerNote', '\x01\x02\x03CompressedBitPerPixel']:
						pass
					
					else :
						ImageExif=(str(ExifTags.TAGS[keyExif]), valueExif)
						listeExif.append(ImageExif)
						
				except KeyError:
					pass
			
			# Changement de l'affichage pour l'enregistrement, et ce pour une 
			# meilleure lecture des données EXIF.
			affExif=[]
			for exifAff in listeExif:
				if exifAff[0].startswith("ApertureValue"):
					exifAffnouveau='f/'+str(round(float(exifAff[1][0])/float(exifAff[1][1]), 1))
					affExif.append(('ApertureValue', exifAffnouveau))
				elif exifAff[0].startswith("CompressedBitsPerPixel"):
					exifAffnouveau=str(int(exifAff[1][0])/int(exifAff[1][1]))+' bit/pixel'
					affExif.append(('CompressedBitsPerPixel', exifAffnouveau))
				elif exifAff[0].startswith("ExposureTime"): 
					exifAffnouveau=str(round(float(exifAff[1][0])/float(exifAff[1][1]), 3))+' ('+str(exifAff[1][0]/exifAff[1][0])+'/'+str(exifAff[1][1]/exifAff[1][0])+' sec)'
					affExif.append(('ExposureTime',exifAffnouveau))
				elif exifAff[0].startswith("FNumber"):
					exifAffnouveau='f/'+str(round(float(exifAff[1][0])/float(exifAff[1][1]), 1))+' (diaphragm value)'
					affExif.append(('FNumber', exifAffnouveau))
				elif exifAff[0].startswith("FocalLength"):
					exifAffnouveau=str(round(float(exifAff[1][0])/float(exifAff[1][1]), 2))+' mm'
					affExif.append(('FocalLength', exifAffnouveau))
				elif exifAff[0].startswith("FocalPlaneXResolution"):
					exifAffnouveau=str(round(float(exifAff[1][0])/float(exifAff[1][1]), 1))
					affExif.append(('FocalPlaneXResolution', exifAffnouveau))
				elif exifAff[0].startswith("FocalPlaneYResolution"):
					exifAffnouveau=str(round(float(exifAff[1][0])/float(exifAff[1][1]), 1))
					affExif.append(('FocalPlaneYResolution', exifAffnouveau))
				elif exifAff[0].startswith("MaxApertureValue"):
					exifAffnouveau='f/'+str(round(float(exifAff[1][0])/float(exifAff[1][1]), 1))
					affExif.append(('MaxApertureValue', exifAffnouveau))
				elif exifAff[0].startswith("XResolution"):
					exifAffnouveau=str(int(exifAff[1][0])/int(exifAff[1][1]))+' dpi'
					affExif.append(('XResolution', exifAffnouveau))
				elif exifAff[0].startswith("YResolution"):
					exifAffnouveau=str(int(exifAff[1][0])/int(exifAff[1][1]))+' dpi'
					affExif.append(('YResolution', exifAffnouveau))
				else:
					affExif.append(exifAff)
	
			# Ttes les infos Exif (tags Exif) obtenu(es) avec
			# intégration du code html pour affichage ds tableau.
			# Si on utilisait unicode pour les traductions, val[0] et val[1]
			# seraient à remplacer par _(unicode(val[0])) et _(unicode(val[1]))
			ExifHtml=['<tr>\n<td bgcolor="9dc3d9">'+str(val[0])+'</td>\n<td>'+str(val[1])+'</td>\n</tr>\n' for val in affExif]
			ExifHtml.sort()
			
			# ----------------------------------------------------
			# Conditions de dimensions de l'image pour l'affichage
			# ----------------------------------------------------
			
			# Recuperation de la valeur du poids de l'image 
			poidsDebutImgExif=int(os.stat(self.chemin).st_size)
			# Calcul du poids en kilo-octet de l'image
			self.poidsFinImgExif=int(poidsDebutImgExif)/1000
		
			# Recuperation du chemin de chargement de la photo
			chemEnrExif=os.path.dirname(self.chemin)
			# Recuperation du nom de la photo
			self.nomImageExif=os.path.basename(self.chemin).encode("UTF8")
			
			# On recupere la taille de l'image
			self.widthImgExif, self.heightImgExif=imgExif.size
			
			# Si la dimension la + grde est inférieure ou égale à 640
			if max(self.widthImgExif, self.heightImgExif)<=640 : self.widthImgExif, self.heightImgExif
			# Si la dimension la + grde est supérieure à 640
			if max(self.widthImgExif, self.heightImgExif)>640 :
				# Calcul du ratio de l'image
				ratioWHexif=float(max(self.widthImgExif, self.heightImgExif))/float(min(self.widthImgExif, self.heightImgExif))
				# La dimension la + grde
				MaxWHexif=max(self.widthImgExif, self.heightImgExif)
				# On assigne automatiquement la dimension la +
				# grde à 640 pour affichage ds le fichier html
				MaxWHexif=float(640)
				# Calcul de la dimension la + petite
				MinWHexif=MaxWHexif/ratioWHexif
			
				# Si La dimension la + grde est supérieure à 640 et 
				# la largeur est supérieure ou égale à la hauteur
				if MaxWHexif==float(640) and self.widthImgExif>=self.heightImgExif :
					self.widthImgExif=640
					self.heightImgExif=int(round(MinWHexif))
			
				# Si la hauteur est supérieure à la largeur
				if self.heightImgExif>self.widthImgExif :
					self.heightImgExif=640
					self.widthImgExif=int(round(MinWHexif))
			
			#----------------------------------------------------------
			# mise en forme des données dans un tableau QTableWidget()
			#----------------------------------------------------------
			self.miseAJourTable(2, listeExif)
			
			# -----------------------------------------------------
			# Affichage de l'exif ds le code et ds un tableau . 
			# -----------------------------------------------------
			# --> Quelques traductions :
			# --------------------------
			# NAME OF THE IMAGE --> NOM DE L'IMAGE 
			# WEIGHT OF THE IMAGE --> POIDS DE L'IMAGE
			# kb --> ko (kilobytes --> kilo-octets)
			# EXIF INFORMATIONS ABOUT THE IMAGE --> INFORMATIONS EXIF SUR L'IMAGE 
			# ExifVersion --> Version Exif
			# DateTime --> Date et heure
			# DateTimeOriginal --> Date et heure (d'origine)
			# DateTimeDigitized --> Date et heure (numerisation)
			# ExifImageWidth --> Dimension X en pixels
			# ExifImageHeight --> Dimension Y en pixels
			# XResolution --> Resolution X
			# YResolution --> Resolution Y
			# Make --> Constructeur
			# Model --> Modele
			# READ ATTENTIVELY WHAT IS JUST DOWN --> LISEZ ATTENTIVEMENT CE QUI 
			# SE TROUVE EN DESSOUS
			# Your image has to be exactly in the same directory as your html file,
			# otherwise the image in question will not appear in this page . -->
			# Votre image doit se trouver exactement dans le même répertoire que
			# votre fichier html, autrement l'image en question n'apparaîtra pas
			# dans cette page . 
			# This page html was generated by 
			# <a href='http://ekd.tolosano.info'>EKD</a> (post-production 
			# software for videos and images) . --> Cette page html a été générée
			# par <a href='http://ekd.tolosano.info'>EKD</a> (logiciel de
			# post-production pour les vidéos et les images) .
			
			#----------------------------------------------
			# Construction du tableau html avant affichage
			#----------------------------------------------
			
			self.infosExifPourHtml=u'<table border=1 bgcolor="f6d79a" width=100% align=center bordercolor="black">\n<tr>\n<tr>\n<td colspan="2" bgcolor="black" align="center"><font color="white">'+_(u"NOM DE L'IMAGE")+u'</td>\n</tr>\n<tr>\n<td colspan="2">'+self.nomImageExif+u'</td>\n</tr>\n<tr>\n<td colspan="2" bgcolor="black" align="center"><font color="white">'+_(u"POIDS DE L'IMAGE")+u'</td>\n</tr>\n<tr>\n<td colspan="2">'+str(self.poidsFinImgExif)+_(u' kb')+u'</td>\n</tr>\n<tr>\n<td colspan="2" bgcolor="black" align="center"><font color="white">'+_(u"INFORMATIONS EXIF SUR L'IMAGE")+u'</td>\n</tr>\n'+''.join(ExifHtml)+u'</tr>\n<tr>\n<td colspan="2" bgcolor="black" align="center"><font color="white">'+_(u"LISEZ ATTENTIVEMENT CE QUI SE TROUVE EN DESSOUS")+u'</td>\n</tr>\n<tr>\n<td colspan="2">'+_(u"Votre image doit se trouver exactement dans le m&ecirc;me r&eacute;pertoire que le fichier html, autrement l'image en question n'appara&icirc;tra pas dans la page.")+u'</td>\n</tr>\n<tr>\n<td colspan="2" bgcolor="black" align="center"><font color="white">'+_(u"Cette page html a &eacute;t&eacute; g&eacute;ner&eacute;e par <a href='http://ekd.tuxfamily.org'>EKD</a> (logiciel de post-production pour vid&eacute;os et images).")+u'</td>\n</tr>\n</table>'
			
		except :

			messageErreur=QMessageBox(self) 
			messageErreur.setText(_(u"Il s'est produit un problème !. Certainement que les photos de cet appareil ne contiennent pas des données importantes (comme par exemple 'Date et heure').")) 
			messageErreur.setWindowTitle(_(u"Erreur")) 
			messageErreur.setIcon(QMessageBox.Critical) 
			messageErreur.exec_()


	def sauverExif(self):
		"""Sauver les infos Exif (sous forme de fichier html)"""
	
		try:

			# Utilisation de la nouvelle boîte de dialogue de sauvegarde
			suffix=""
			# Boîte de dialogue pour sauvegarder (nom du nouveau fichier)
                        sauver = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=False)
			sauver = sauver.getFile()

			if not sauver: return
			enrHtml=open(sauver+'.html', 'wb')
			
			enrHtml.write('<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html>\n<head>\n<meta http-equiv=content-type content="text/html; charset=UTF-8">\n<title>EXIF infos</title>\n</head>\n<body bgcolor="d0cfcd" text="#000000" link="lightblue" vlink="orange">\n<center><IMG src="'+self.nomImageExif+'" '+ 'width="'+str(self.widthImgExif)+'" height="'+str(self.heightImgExif)+'" border="0"></center>\n<h4>'+self.infosExifPourHtml+'</h4>\n</body>\n</html>')
			enrHtml.flush()
			enrHtml.close()

			# Si on enregistre le fichier html dans le répertoire où la photo a été 
			# chargée, il faut vérifier si l'image chargée est déjà présente dans le rep.
			# de sauvegarde
			if os.path.exists(os.path.dirname(sauver)+os.sep+os.path.basename(self.chemin)) is False:
				# Copie de la 1ère image chargée dans le répertoire de sauvegarde
				# ... cela est utile pour que le fichier html généré s'affiche bien
				shutil.copy(self.chemin, os.path.dirname(sauver))
			else: pass

		except:

			messageErreur=QMessageBox(self)
			messageErreur.setText(_(u"Une erreur est apparue!"))
			messageErreur.setWindowTitle(_(u"Erreur"))
			messageErreur.setIcon(QMessageBox.Critical)
			messageErreur.exec_()


	def afficherAide(self):
		"""Boîte de dialogue de l'aide du cadre Image > Informations (txt ou Exif)"""

		# Gestion de l'aide via EkdAide
		messageAide=EkdAide(parent=self)
		messageAide.setText(_(u"<p><b>Vous pouvez ici afficher les informations (en format texte) d'images et/ou les informations EXIF (Exchangeable Image File Format) de prises de vues d'appareils photographiques numériques. Vous pouvez de même exporter ces informations sous forme de fichier html (en vue d'être publié sur internet, par exemple).</b></p><p><b>Voici une défintion de EXIF selon Wikipédia: 'L’Exchangeable image file format ou Exif est une spécification de format de fichier pour les images utilisées par les appareils photographiques numériques.</b></p><p><b>Les balises de métadonnées définies dans le format Exif standard couvrent un large éventail de données, dont:<br><ol><li>Information de la date et de l’heure. Les appareils numériques enregistrent la date et l’heure de la prise de vue et l’insèrent dans les métadonnées.</li><li>Les réglages de l’appareil. Cela comprend des informations statiques telles que la marque et le modèle de l’appareil et des informations variables telles que l’orientation, l’ouverture, la vitesse d’obturation, la longueur de focale, la sensibilité ... .</li><li>Description et information des droits d’auteur.'</b></li></ol></p><p><b><font color='green'>Attention: comme vous pouvez le constater, les boutons 'Sauver infos (.txt)' et 'Sauver Exif (.html)' sont dans un premier temps grisés (ils deviendront actifs dès lors que vous aurez chargé une image).</font></b></p><p>Dans l'onglet <b>'Image source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher votre/vos image(s). Si vous voulez sélectionner plusieurs images d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos images), cliquez sur <b>Ajouter</b>.</p><p>Vous avez maintenant la possibilité de consulter les informations sur l'image dans les onglets <b>'Infos Texte'</b> et/ou <b>'Infos Exif'</b>. <b><font color='red'>Il faut savoir que les informations délivrées sont celles de l'image que vous aurez sélectionné dans l'onglet Image(s) source</font></b></p><p>Vous pouvez sauvegarder les informations texte et/ou EXIF (suivant l'image/photo chargée), pour ce faire cliquez sur le bouton <b>'Sauver infos (.txt)'</b> ou <b>'Sauver Exif (.html)'</b>, dans la boîte de dialogue sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b>. <b><font color='red'>De même ici les informations qui seront enregistrées seront celles de l'image sélectionnée dans l'onglet Image(s) source.</font></b></p>"))
		messageAide.show()
	
	
	def save(self) :
		self.afficheurImgSource.saveFileLocation(self.idSection)


	def load(self) :
		self.afficheurImgSource.loadFileLocation(self.idSection)
		self.appliquer()
Ejemplo n.º 5
0
class Base_EncodageFiltre(Base):
	# -------------------------------------------------------------------
	# Cadre accueillant les widgets de : Animation >> Encodage
	# -------------------------------------------------------------------

	def __init__(self, titre=_(u"Titre")):
		# -------------------------------
		# Parametres généraux du widget
		# -------------------------------

		self.printSection()

		# tout sera mis dans une boîte verticale
		self.vbox=QVBoxLayout()

		super(Base_EncodageFiltre, self).__init__(boite='vbox', titre=titre)
		## -------------------------------------------------------------------
		## on utilise le selecteur d'image pour les vidéos
		from gui_modules_image.selectWidget import SelectWidget
		# Là où on naviguera entre les fichiers
		self.afficheurVideoSource=SelectWidget(mode="texte", video = True)
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"),self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)

		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "video" # Défini le type de fichier source.
		self.typeSortie = "video" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurVideoSource # Fait le lien avec le sélecteur de fichier source.

		#=== Widget qui seront inclus dans la boite de réglage ===#
		# Boite de combo
		self.combo=QComboBox() 	# Le self est nécessaire pour récupérer l'identifiant
					# de l'entrée de la boite de combo
		
		# Insertion des codecs de compression dans la combo box
		# Rq: listeCombo est de la forme: [(clé QVariant, texte à afficher,...), ...]
		for i in self.listeCombo: # self obligatoire pour le module séquentiel
                	self.combo.addItem(i[1], QVariant(i[0]))
		
		self.connect(self.combo,SIGNAL("currentIndexChanged(int)"),self.changerItemQStacked)
		
		# Utilisation de EkdConfig : modification des sections
		if self.idSection == "animation_filtresvideo":
			# On donne la référence de la boite de combo au widget de découpage
			# pour que ce dernier connaisse le type de découpage sélectionné (assisté ou libre)
			self.filtreDecouper.setComboParent(self.combo)
		
		# Widgets intégrés à la boîte de groupe "Réglage de sortie de l'encodage".
		# La boîte de combo définie ci-dessus 'sélectionne' ceux qui vont s'afficher
		# -> utilisation d'un stacked
		BoxReglage = QGroupBox()
		layoutReglage = QVBoxLayout(BoxReglage)
		layoutReglage.addWidget(self.combo)
		layoutReglage.addWidget(self.stacked)
		###

		self.loadOptions()
		
		self.add(BoxReglage, _(u"Réglages"))
		
		self.addPreview()

		self.addLog()

		# -------------------------------------------------------------------
		# widgets du bas : ligne + boutons Aide et Appliquer
		# -------------------------------------------------------------------

	
	def chargerItemCombo(self, idCombo):
		"""Charger un item particulier de la boite de combo"""
		try:
			indice = 0 # indice de la ligne de listeCombo correspondant au type d'ordre
			for i in self.listeCombo:
				if i[0]!=idCombo:
					indice += 1
				else:
					break
			self.combo.setCurrentIndex(indice)
		except:
			self.combo.setCurrentIndex(0)
	
	
	def changerItemQStacked(self, i):
		""" L'entrée sélectionnée de la boîte de combo modifie le QWidget de réglage du codec associée """
		#print "index", i
		EkdPrint(u"index %d" % i)
		idCodec=str(self.combo.itemData(i).toString())
		#print "idCodec:", idCodec, type(idCodec)
		EkdPrint(u"idCodec: %s %s" % (idCodec ,type(idCodec)))
		
		for k in self.listeCombo:
			if k[0]==idCodec:
				# Utilisation de EkdConfig : modification des sections
				if self.idSection == "animation_filtresvideo":
					if k[0] in ('decoupageassiste', 'decoupagelibre'):
						self.filtreDecouper.setStacked(k[0])
				if self.stacked:
					self.stacked.setCurrentIndex(k[2])
		
		EkdConfig.set(self.idSection,'codec', idCodec)
	
	def changerTypeDecoupe(self, mode):
		"Change le mode de découpe vidéo (assisté ou libre)"
		indexCombo = self.combo.findData(QVariant(mode))
		self.combo.setCurrentIndex(indexCombo)
	
	
	def getFile(self):
		'''
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.boutApp.setEnabled(True)
		
		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])
		
		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(True)
		#self.radioConvert.setEnabled(True)
		#self.boutCompare.setEnabled(True)
		self.boutApp.setEnabled(True)
		if self.idSection == "animation_filtresvideo":
			self.boutApercu.setEnabled(True)
			self.filtreDecouper.setButtonEnabled(True)
		# On emmet un signal quand le fichier est chargé
		self.emit(SIGNAL("loaded"))
		return self.chemin

	def saveFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.saveFileLocation(self.idSection)

 	def loadFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.loadFileLocation(self.idSection)

 	def loadOptions(self):
		'''
		# On charge les options
		'''
		idCodec = EkdConfig.get(self.idSection,'codec')
		self.chargerItemCombo(idCodec)

	def load(self):
		'''
		Chargement de la configuration de tous les objets
		'''
		self.loadFiles()
		self.loadOptions()

	def save(self):
		'''
		Sauvegarde de la configuration de tous les objets
		'''
		self.saveFiles()


	def sequentiel(self, entree, sortie, clef=None, ouvert=0):
		"""Utile dans le module du même nom. Applique les opérations associées à l'identifiant donné en 3ème argument. Retourne le vrai nom du fichier de sortie"""
		self.chargerItemCombo(clef)
		self.ouvrirSource(entree)
		return self.appliquer(sortie, ouvert)
	
	
	def sequentielReglage(self, clef):
		"""Utile dans le module du même nom. Récupère le widget de réglage associé à l'identifiant donné en 1er argument. Retourne l'instance du widget de réglage (avec quelques modifications)"""
		for k in self.listeCombo:
			if k[0]==clef:
				self.stacked.setCurrentIndex(k[2])
				titrePartielReglage = k[1]
		self.combo.hide()
		txt = _(u"Réglage")
		self.groupReglage.setTitle(txt+' : '+titrePartielReglage)
		return self.groupReglage
class Animation_MontagVideoDecoupUneVideo(Base):
    # -------------------------------------------------------------------------------------
    # Cadre accueillant les widgets de : Animation >> Montage Vidéo >> Découper une Vidéo
    # -------------------------------------------------------------------------------------
    def __init__(self, statusBar):
        # -------------------------------
        # Parametres généraux du widget
        # -------------------------------
        # === tout sera mis dans une boîte verticale ===#
        self.vbox = QVBoxLayout()

        # === Variable de configuration ===#
        self.config = EkdConfig

        # === Identifiant de la classe ===#
        self.idSection = "animation_decouper_une_video"

        super(Animation_MontagVideoDecoupUneVideo, self).__init__(
            "vbox", titre=_(u"Montage: Découpage d'une vidéo")
        )  # module de animation

        self.printSection()

        # === Drapeaux ===#
        # drapeau pour savoir si la valeur de début de la sélection vidéo été enregistré
        self.debutEstSelect = False
        # drapeau pour savoir si la valeur de fin de la sélection vidéo été enregistré
        self.finEstSelect = False
        # drapeau d'extraction du son
        self.extraireSon = True

        # -------------------------------------------------------------------
        # Boîte de groupe : "Fichier vidéo source"
        # -------------------------------------------------------------------
        self.afficheurVideoSource = SelectWidget(
            extensions=["*.avi", "*.mpg", "*.mpeg", "*.mjpeg", "*.flv", "*.mp4", "*.dv", "*.vob"],
            mode="texte",
            video=True,
        )
        # Onglets
        self.indexVideoSource = self.add(self.afficheurVideoSource, _(u"Video(s) source"))
        self.connect(self.afficheurVideoSource, SIGNAL("fileSelected"), self.getFile)
        self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
        # --------------------------------------------------------------------

        ## ---------------------------------------------------------------------
        # Variables pour la fonction tampon
        ## ---------------------------------------------------------------------
        self.typeEntree = "video"  # Défini le type de fichier source.
        self.typeSortie = "video"  # Défini le type de fichier de sortie.
        self.sourceEntrees = self.afficheurVideoSource  # Fait le lien avec le sélecteur de fichier source.

        # --------------------------------------------------------------------------
        # Boîte de groupe de réglage: visualisation et de marquage de la sélection
        # --------------------------------------------------------------------------

        # Création de la boite de groupe de réglage (contenant la boite self.layoutReglage)
        reglageGroup = QGroupBox()
        self.layoutReglage = QVBoxLayout(reglageGroup)
        # === Widgets mplayer ===#
        vboxMplayer = QVBoxLayout()
        # Le facteur limitant est la largeur -> + simple à coder à cause de la largeur de la barre
        self.mplayer = Mplayer(
            taille=(300, 270),
            facteurLimitant=Mplayer.LARGEUR,
            choixWidget=(Mplayer.RATIO, Mplayer.PAS_PRECEDENT_SUIVANT, Mplayer.CURSEUR_A_PART),
        )

        self.mplayer.listeVideos = []
        self.mplayer.setToolTip(
            _(u"La lecture de la vidéo est nécessaire pour achever la sélection d'une zone de la vidéo")
        )
        self.mplayer.setEnabled(False)
        hbox = QHBoxLayout()
        hbox.addStretch()
        hbox.addWidget(self.mplayer)
        hbox.addStretch()
        vboxMplayer.addLayout(hbox)

        self.radioSource = QRadioButton(_(u"vidéo(s) source(s)"))
        self.radioSource.setChecked(True)
        self.radioSource.setEnabled(False)
        self.connect(self.radioSource, SIGNAL("clicked(bool)"), self.fctRadioSource)
        self.radioApercu = QRadioButton(_(u"aperçu"))
        self.radioApercu.setEnabled(False)
        self.connect(self.radioApercu, SIGNAL("clicked(bool)"), self.fctRadioApercu)
        self.radioConvert = QRadioButton(_(u"vidéo convertie"))
        self.radioConvert.setEnabled(False)
        self.connect(self.radioConvert, SIGNAL("clicked(bool)"), self.fctRadioConvert)

        self.layoutReglage.addLayout(vboxMplayer)

        self.add(reglageGroup, _(u"Réglages"))
        # Le widget-mplayer récupère des informations tous les 10ème
        # de seconde au lieu de toutes les secondes -> le marquage du
        # début et de la fin de la sélection seront plus précis
        self.mplayer.dureeTimer = 100

        self.connect(self.mplayer.bout_LectPause, SIGNAL("clicked()"), self.lectureMPlayer)

        # === Marquage et affichage des bornes de la sélection ===#

        # ||| Label de visualisation de la sélection |||#

        self.marques = (0, 0, 1)
        self.visuSelect = Label(self.marques, 300)
        self.visuSelect.setToolTip(_(u"La zone sélectionnée apparait en vert dans cette bande"))
        self.valeurDebut = 0
        self.valeurFin = 0

        hbox = QHBoxLayout()
        hbox.addWidget(self.visuSelect)
        self.layoutReglage.addLayout(hbox)

        # ||| boutons de marquage de la sélection |||#

        iconTaille = 28

        self.frameMarque = QFrame()
        boiteMarque = QHBoxLayout()
        boiteMarque.addStretch()

        boutMarqDebutSelect = QPushButton()
        boutMarqDebutSelect.setIcon(QIcon("Icones" + os.sep + "Tdebut.png"))
        boutMarqDebutSelect.setIconSize(QSize(iconTaille, iconTaille))
        boutMarqDebutSelect.setToolTip(_(u"Marquer le début de la sélection"))
        boiteMarque.addWidget(boutMarqDebutSelect)

        self.boutMarqFinSelect = QPushButton()
        self.boutMarqFinSelect.setIcon(QIcon("Icones" + os.sep + "Tfin.png"))
        self.boutMarqFinSelect.setIconSize(QSize(iconTaille, iconTaille))
        self.boutMarqFinSelect.setToolTip(_(u"Marquer la fin de la sélection"))
        self.boutMarqFinSelect.setEnabled(False)
        boiteMarque.addWidget(self.boutMarqFinSelect)

        boutMarqDebutSelect_min = QPushButton()
        boutMarqDebutSelect_min.setIcon(QIcon("Icones" + os.sep + "Tdebut2.png"))
        boutMarqDebutSelect_min.setIconSize(QSize(iconTaille, iconTaille))
        boutMarqDebutSelect_min.setToolTip(_(u"Marquer le début de la sélection au temps minimum (t=0)"))
        boiteMarque.addWidget(boutMarqDebutSelect_min)

        self.boutMarqFinSelect_max = QPushButton()
        self.boutMarqFinSelect_max.setIcon(QIcon("Icones" + os.sep + "Tfin2.png"))
        self.boutMarqFinSelect_max.setIconSize(QSize(iconTaille, iconTaille))
        self.boutMarqFinSelect_max.setToolTip(
            _(u'Marquer la fin de la sélection au temps maximum (t="la durée de la vidéo")')
        )
        self.boutMarqFinSelect_max.setEnabled(False)
        boiteMarque.addWidget(self.boutMarqFinSelect_max)

        boutMiseAZeroSelect = QPushButton()
        boutMiseAZeroSelect.setIcon(QIcon("Icones" + os.sep + "update.png"))
        boutMiseAZeroSelect.setIconSize(QSize(iconTaille, iconTaille))
        boutMiseAZeroSelect.setToolTip(_(u"Remettre à zéro les paramètres"))
        boiteMarque.addWidget(boutMiseAZeroSelect)

        self.boutExtractionSon = QPushButton()
        self.boutExtractionSon.setIcon(QIcon("Icones" + os.sep + "sound.png"))
        self.boutExtractionSon.setIconSize(QSize(iconTaille, iconTaille))
        self.boutExtractionSon.setToolTip(_(u"Pressez le bouton si vous voulez exclure le son de l'extraction vidéo"))
        boiteMarque.addWidget(self.boutExtractionSon)

        # -------------------------------------------------------------------------
        ## Bouton d'augmentation/réduction de la vitesse
        taille = QSize(15, 15)
        speedBox = QVBoxLayout()
        self.moinsvite = QPushButton("-")
        self.moinsvite.setFixedSize(taille)
        self.moinsvite.setToolTip(_(u"Pressez le bouton si vous réduire la vitesse de la vidéo"))
        speedBox.addWidget(self.moinsvite)

        self.initspeed = QPushButton("=")
        self.initspeed.setFixedSize(taille)
        self.initspeed.setToolTip(_(u"Pressez le bouton si vous réinitialiser la vitesse de la vidéo"))
        speedBox.addWidget(self.initspeed)

        self.plusvite = QPushButton("+")
        self.plusvite.setFixedSize(taille)
        self.plusvite.setToolTip(_(u"Pressez le bouton si vous augmenter la vitesse de la vidéo"))
        speedBox.addWidget(self.plusvite)
        boiteMarque.addLayout(speedBox)

        self.connect(self.moinsvite, SIGNAL("clicked()"), self.mplayer.speeddown)
        self.connect(self.initspeed, SIGNAL("clicked()"), self.mplayer.initspeed)
        self.connect(self.plusvite, SIGNAL("clicked()"), self.mplayer.speedup)
        # -------------------------------------------------------------------------

        self.connect(boutMarqDebutSelect, SIGNAL("clicked()"), self.marqDebutSelect)
        self.connect(self.boutMarqFinSelect, SIGNAL("clicked()"), self.marqFinSelect)
        self.connect(boutMarqDebutSelect_min, SIGNAL("clicked()"), self.marqDebutSelect_min)
        self.connect(self.boutMarqFinSelect_max, SIGNAL("clicked()"), self.marqFinSelect_max)
        self.connect(boutMiseAZeroSelect, SIGNAL("clicked()"), self.miseAZeroSelect)
        self.connect(self.boutExtractionSon, SIGNAL("clicked()"), self.reglageSon)

        self.frameMarque.setLayout(boiteMarque)
        # On grise le widget au début
        self.frameMarque.setEnabled(False)

        self.layoutReglage.addWidget(self.frameMarque)
        boiteMarque.addStretch()

        # ||| boutons radio de lecture de la source ou du fichier converti|||#
        hbox = QHBoxLayout()
        hbox.addWidget(self.radioSource)
        hbox.addWidget(self.radioApercu)
        hbox.addWidget(self.radioConvert)
        hbox.setAlignment(Qt.AlignHCenter)
        self.layoutReglage.addLayout(hbox)
        self.layoutReglage.addStretch(50)

        self.addLog()

    def getFile(self):
        """
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		"""
        self.chemin = self.afficheurVideoSource.getFile()
        self.boutApp.setEnabled(True)

        self.mplayer.setEnabled(True)
        self.mplayer.setVideos([self.chemin])

        # On active les réglage
        self.frameMarque.setEnabled(True)
        self.boutMarqFinSelect_max.setEnabled(True)
        self.boutMarqFinSelect.setEnabled(True)
        # Restauration de l'état initial de sélection
        self.radioSource.setEnabled(True)
        self.radioSource.setChecked(True)
        self.radioApercu.setEnabled(False)
        self.radioApercu.setChecked(False)
        self.radioConvert.setEnabled(False)
        self.radioConvert.setChecked(False)

        self.emit(SIGNAL("loaded"))
        self.miseAZeroSelect()

    def lectureMPlayer(self):
        """Dégriser les 2 boutons de sélection de fin si le début a déjà été sélectionné"""
        if self.debutEstSelect:
            self.boutMarqFinSelect.setEnabled(True)
            self.boutMarqFinSelect_max.setEnabled(True)

    def reglageSon(self):
        """Extraire ou pas le son de la vidéo lors de l'extraction vidéo"""
        if self.extraireSon:
            self.extraireSon = False
            self.boutExtractionSon.setIcon(QIcon("Icones" + os.sep + "nosound.png"))
            self.boutExtractionSon.setToolTip(
                _(u"Pressez le bouton si vous voulez ré-inclure le son de l'extraction vidéo")
            )
        else:
            self.extraireSon = True
            self.boutExtractionSon.setIcon(QIcon("Icones" + os.sep + "sound.png"))
            self.boutExtractionSon.setToolTip(
                _(u"Pressez le bouton si vous voulez exclure le son de l'extraction vidéo")
            )

    def miseAZeroSelect(self):
        """On remet à zéro les zones sélectionnées"""
        self.debutEstSelect = False
        self.finEstSelect = False
        self.boutApp.setEnabled(False)
        self.boutMarqFinSelect.setEnabled(False)
        self.boutMarqFinSelect_max.setEnabled(False)
        self.valeurDebut, self.valeurFin = 0, 0
        self.marques = (0, 0, 1)
        self.visuSelect.update_marques(self.marques)
        self.visuSelect.repaint()

    def marqDebutSelect(self):
        """Récupération de la valeur du marqueur du début de la sélection"""

        # Le début n'a pas été sélectionné ou il a été sélectionné mais pas la fin
        # -> un seul trait sur le QLabel
        if not self.debutEstSelect or (self.debutEstSelect and not self.finEstSelect):
            self.valeurDebut = self.mplayer.temps
            # print self.valeurDebut
            EkdPrint(u"%s" % self.valeurDebut)
            self.debutEstSelect = True
            # mplayer est en lecture
            if self.mplayer.estLue:
                self.boutMarqFinSelect.setEnabled(True)
                self.boutMarqFinSelect_max.setEnabled(True)
                # tracer d'un seul trait
            self.marques = (self.valeurDebut, self.valeurDebut, self.mplayer.dureeVideo)
            self.visuSelect.update_marques(self.marques)
            self.visuSelect.repaint()

            # Le début et la fin ont déjà été sélectionnés
        elif self.debutEstSelect and self.finEstSelect:
            self.valeurDebut = self.mplayer.temps
            # print self.valeurDebut
            EkdPrint(u"%s" % self.valeurDebut)
            self.mettreAJourLabel("debut")

    def marqFinSelect(self):
        """Récupération de la valeur du marqueur de la fin de la sélection"""
        self.valeurFin = self.mplayer.temps
        # print self.valeurFin
        EkdPrint(u"%s" % self.valeurFin)
        self.finEstSelect = True
        self.mettreAJourLabel("fin")

    def marqDebutSelect_min(self):
        """La valeur du marqueur du début de la sélection est placé au temps t=0 seconde"""

        # Le début n'a pas été sélectionné ou il a été sélectionné mais pas la fin
        # -> un seul trait sur le QLabel
        if not self.debutEstSelect or (self.debutEstSelect and not self.finEstSelect):
            self.valeurDebut = 0
            # print self.valeurDebut
            EkdPrint(u"%s" % self.valeurDebut)
            self.debutEstSelect = True
            # mplayer est en lecture
            if self.mplayer.estLue:
                self.boutMarqFinSelect.setEnabled(True)
                self.boutMarqFinSelect_max.setEnabled(True)
                # tracer d'un seul trait
            self.marques = (self.valeurDebut, self.valeurDebut, self.mplayer.dureeVideo)
            self.visuSelect.update_marques(self.marques)
            self.visuSelect.repaint()

            # Le début et la fin ont déjà été sélectionnés
        elif self.debutEstSelect and self.finEstSelect:
            self.valeurDebut = 0
            # print self.valeurDebut
            EkdPrint(u"%s" % self.valeurDebut)
            self.mettreAJourLabel()

    def marqFinSelect_max(self):
        """La valeur du marqueur de la fin de la sélection est placé à la fin de la vidéo"""
        self.valeurFin = self.mplayer.dureeVideo
        # print self.valeurFin
        EkdPrint(u"%s" % self.valeurFin)
        self.finEstSelect = True
        self.mettreAJourLabel()

    def mettreAJourLabel(self, debutOuFin=None):
        """Redessiner le QLabel"""

        # si les valeurs de début et de fin sont identiques (possible car notre précision est de un dixième de seconde) alors on remet tout à zéro
        if self.valeurDebut == self.valeurFin:
            self.finEstSelect = False
            self.boutApp.setEnabled(False)
            # tracer d'un seul trait
            self.marques = (self.valeurDebut, self.valeurFin, self.mplayer.dureeVideo)
            self.visuSelect.update_marques(self.marques)
            self.visuSelect.repaint()
            self.radioConvert.setEnabled(False)
            self.radioApercu.setEnabled(False)

            # Truisme: la valeur du début de la sélection doit être inférieure à la valeur de la fin
            # sinon les valeurs de début et de fin sont égalisées
        elif self.valeurDebut > self.valeurFin:
            self.radioSource.setEnabled(False)
            self.radioApercu.setEnabled(False)
            if debutOuFin == "debut":
                self.valeurFin = self.valeurDebut
                self.boutApp.setEnabled(False)
                self.marques = (self.valeurDebut, self.valeurFin, self.mplayer.dureeVideo)
                self.visuSelect.update_marques(self.marques)
                self.visuSelect.repaint()

            if debutOuFin == "fin":
                self.valeurDebut = self.valeurFin
                self.boutApp.setEnabled(False)
                self.marques = (self.valeurDebut, self.valeurFin, self.mplayer.dureeVideo)
                self.visuSelect.update_marques(self.marques)
                self.visuSelect.repaint()
                # Le cas normal
        else:
            self.marques = (self.valeurDebut, self.valeurFin, self.mplayer.dureeVideo)
            self.visuSelect.update_marques(self.marques)
            self.visuSelect.repaint()
            self.boutApp.setEnabled(True)
            self.radioSource.setEnabled(True)
            self.radioApercu.setEnabled(True)

            # print "Bornes de la sélection :", self.valeurDebut, self.valeurFin, type(self.valeurDebut), type(self.valeurFin)
        EkdPrint(
            u"Bornes de la sélection : %s %s %s" % (self.valeurDebut, self.valeurFin, type(self.valeurDebut)),
            type(self.valeurFin),
        )

    def fctRadioSource(self):
        """"Communique le fichier source à mplayer"""
        self.mplayer.listeVideos = [self.chemin]
        self.mplayer.debutFin = (0, 0)
        self.frameMarque.setEnabled(True)
        self.visuSelect.setEnabled(True)
        self.radioApercu.setChecked(False)
        self.radioConvert.setChecked(False)

    def fctRadioApercu(self):
        """"Communique le fichier aperçu à mplayer"""
        self.mplayer.listeVideos = [self.chemin]
        debut = float("%.1f" % self.valeurDebut)
        fin = float("%.1f" % self.valeurFin)
        self.mplayer.debutFin = (debut, fin)
        self.frameMarque.setEnabled(False)
        self.radioSource.setChecked(False)
        self.radioConvert.setChecked(False)

    def fctRadioConvert(self):
        """"Communique le fichier converti à mplayer"""
        self.mplayer.listeVideos = [self.fichierSortie]
        self.mplayer.debutFin = (0, 0)
        self.frameMarque.setEnabled(False)
        self.visuSelect.setEnabled(False)
        self.radioSource.setChecked(False)
        self.radioApercu.setChecked(False)

    def ouvrirSource(self, nomEntree=None):
        """Récupération du chemin de la vidéo sélectionnée et activation de certains widgets"""

        # Récupération du chemin de la vidéo
        chemin = self.recupSource(nomEntree)

        if not chemin:
            return

        # Affichage du chemin + nom de fichier dans la ligne d'édition
        self.ligneEditionSource.setText(chemin)

        self.mplayer.setEnabled(True)
        self.mplayer.listeVideos = [chemin]
        self.radioSource.setChecked(True)
        self.radioSource.setEnabled(False)
        self.radioConvert.setEnabled(False)

        # les boutons de marquage apparaissent
        self.frameMarque.setEnabled(True)

        # Utile lors de la sélection d'une 2ème vidéo et au-delà
        self.miseAZeroSelect()

    def afficherAide(self):
        """ Boîte de dialogue de l'aide du cadre Animation > Encodage """

        super(Animation_MontagVideoDecoupUneVideo, self).afficherAide(
            _(
                u"""<p><b>Vous pouvez ici découper une vidéo et ainsi en garder uniquement la partie qui vous intéresse.</b></p><p><font color='green'>Ce cadre est assez différent des autres, vous avez tout d'abord la zone de visualisation vidéo, puis en dessous les boutons marche, arrêt et le compteur, la glissière de défilement, la zone d'affichage de la découpe (les parties découpées seront affichées en vert), les boutons début/fin de sélection, remise à zéro (vous pouvez sélectionner ou non le son par le bouton haut parleur), à la doite de ce dernier bouton, vous avez trois minuscules boutons contenant +, = et - (ils servent à accélérer ou diminuer la vitesse de lecture de la vidéo), puis les choix de visualisation <b>'vidéo(s) source(s)'</b>, <b>'vidéo convertie'</b> et le bouton <b>'Comparateur de vidéos'</b>.</font></p><p><b>Tout ce qui vient de vous être décrit se trouve dans l'onglet Réglages</b>.</p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>'Réglages'</b>, lisez la vidéo (par le bouton avec la flèche orientée vers la droite <b>'La lecture de la vidéo est nécessaire ...'</b>), pour la vitesse de lecture, profitez des boutons + ou - pour augmenter ou diminuer la vitesse de lecture <b>(plus vous diminuez la vitesse de lecture, plus la découpe pourra se faire de façon précise)</b>, cliquez ensuite sur le bouton <b>'Marquer le début de la sélection'</b> ou <b>'Marquer le début de la sélection au temps minimum (t=0)'</b> (pour sélectionner la vidéo à son tout début), laissez jouer (regardez la vidéo défiler) et cliquez sur le bouton <b>'Marquer la fin de la sélection'</b> au moment propice (ou <b>'Marquer la fin de la sélection au temps maximum t="la durée de la vidéo"'</b> pour garder la dite vidéo jusqu'à la fin).</p><p><font color='blue'>Sachez que vous pouvez revenir aux paramètres par défaut en cliquant sur le bouton <b>'Remettre à zéro les paramètres'</b> (les deux flèches vertes inversées), vous devrez alors rejouer la vidéo et recommencer vos différentes sélections.</font></p><p>Cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde de votre vidéo, entrez le <b>'Nom de Fichier'</b> dans le champ de texte réservé à cet effet ... cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion. A la fin cliquez sur le bouton <b>'Voir les informations d'encodage'</b> et fermez cette dernière fenêtre après avoir vu les informations en question.</p><p>Vous pouvez visionner votre vidéo (avant la conversion) en sélectionnant <b>'vidéo(s) source(s)'</b>, après la conversion <b>'vidéo convertie'</b> ou bien encore les deux en même temps, en cliquant sur le bouton <b>'Comparateur de vidéos'</b>.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos et fichiers audio chargés (avec leurs chemins exacts) avant et après conversion.</p>"""
            )
        )

    def appliquer(self):
        """Découpage de la vidéo"""

        # Récupération du chemin source
        chemin = unicode(self.chemin)
        # suffix du fichier actif
        suffix = os.path.splitext(chemin)[1]
        # Modifié le 30/06/2009 : On joue avec l'éritage de Base
        saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver"))

        cheminFichierEnregistrerVideo = saveDialog.getFile()

        if not cheminFichierEnregistrerVideo:
            return
        ###########################################################################################################################

        tempsDebut = float("%.1f" % self.valeurDebut)
        tempsFin = float("%.1f" % self.valeurFin)
        dureeSelection = str(tempsFin - tempsDebut)
        #

        # Extension du fichier
        # print "extension :", suffix, type(suffix)
        EkdPrint(u"extension : %s %s" % (suffix, type(suffix)))

        try:
            mencoder = WidgetMEncoder(
                "decoupervideo",
                chemin,
                cheminFichierEnregistrerVideo,
                valeurNum=(str(tempsDebut), str(dureeSelection)),
                optionSpeciale=self.extraireSon,
                laisserOuvert=1,
            )
            mencoder.setWindowTitle(_(u"Découper une vidéo"))
            mencoder.exec_()
        except:
            messageErrAnEnc = QMessageBox(self)
            messageErrAnEnc.setText(_(u'Un problème est survenu lors de l\'exécution de "mencoder -ss ..."'))
            messageErrAnEnc.setWindowTitle(_(u"Error"))
            messageErrAnEnc.setIcon(QMessageBox.Warning)
            messageErrAnEnc.exec_()
            return

        self.fichierSortie = cheminFichierEnregistrerVideo
        self.radioSource.setEnabled(True)
        self.radioSource.setChecked(False)
        self.radioConvert.setEnabled(True)
        self.radioConvert.setChecked(True)
        ### Information à l'utilisateur
        self.infoLog(None, chemin, None, cheminFichierEnregistrerVideo)

    def saveFiles(self):
        """
		# On sauvegarde la liste des fichiers chargés
		"""
        self.afficheurVideoSource.saveFileLocation(self.idSection)
        # Ajout de la sauvegarde des positions début et fin
        EkdConfig.set(self.idSection, u"valeurDebut", unicode(self.valeurDebut))
        EkdConfig.set(self.idSection, u"valeurFin", unicode(self.valeurFin))

    def loadFiles(self):
        """
		# On sauvegarde la liste des fichiers chargés
		"""
        self.afficheurVideoSource.loadFileLocation(self.idSection)
        self.valeurDebut = float(EkdConfig.get(self.idSection, "valeurDebut"))
        self.valeurFin = float(EkdConfig.get(self.idSection, "valeurFin"))
        self.mettreAJourLabel("debut")
        self.mettreAJourLabel("fin")

    def load(self):
        """
		Chargement de la configuration de tous les objets
		"""
        self.loadFiles()

    def save(self):
        """
		Sauvegarde de la configuration de tous les objets
		"""
        self.saveFiles()
Ejemplo n.º 7
0
class Image_Divers_TxtSurImg(QWidget):
	# -----------------------------------
	# Cadre accueillant les widgets de :
	# Image >> Divers >> Texte sur images
	# -----------------------------------
	
	ZOOM_MOINS,ZOOM_PLUS,ZOOM_REEL,ZOOM_FIT = range(4)

	def __init__(self, statusBar, geometry):
		QWidget.__init__(self)
		
		# Fonctions communes à plusieurs cadres du module Image
		self.base = Base()

		# Gestion de la configuration via EkdConfig
		# Paramètres de configuration
		self.config = EkdConfig
		self.nProc = 0

		vbox = QVBoxLayout() # Layout principal
		# Modification de structure pour mettre ce module conforme au nouveau design.
		# 1.Ajout du tab principal
		self.tabwidget=QTabWidget()

		# 2. Création du tab de sélection de fichier source - basé sur le widget de sélection standard
		self.afficheurImgSource=SelectWidget(geometrie = geometry)
		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "image" # Défini le type de fichier source.
		self.typeSortie = "image" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurImgSource # Fait le lien avec le sélecteur de fichier source.
		
		self.connect(self.afficheurImgSource, SIGNAL("pictureChanged(int)"), self.modifImgSource)
		self.connect(self.afficheurImgSource, SIGNAL("pictureSelected()"), self.modifImgSource)
		self.tabwidget.addTab(self.afficheurImgSource, _(u'Image(s) source'))

		# 3. Création du tab réglages

		self.listFileNames = []
		self.z = 1 # "Hauteur" initiale des graphicsItems ajoutés
		self.baseImage = None
		self.copiedItem = QByteArray()
		self.pasteOffset = 5
		self.prevPoint = QPoint()
		self.addOffset = 5
		self.borders = []

		self.widReglage = QWidget()

		self.view = GraphicsView()
		self.scene = QGraphicsScene(self)
		self.view.setScene(self.scene)
		
		# Image de fond
		self.background = QGraphicsPixmapItem()
		
		buttonLayout = QVBoxLayout()
		# Liste qui servira à activer/désactiver les boutons
		self.buttonsList = []
		# Liste des boutons à éviter sous PyQt4.1.0 (ex.ubuntu feisty) à cause d'un bogue
		# sur les fonctions associées
		self.buttonListeNoireFeisty = []
		self.zoomPlus = partial(self.zoom, Image_Divers_TxtSurImg.ZOOM_PLUS)
		self.zoomMoins = partial(self.zoom, Image_Divers_TxtSurImg.ZOOM_MOINS)
		self.zoomReel = partial(self.zoom, Image_Divers_TxtSurImg.ZOOM_REEL)
		self.zoomFit = partial(self.zoom, Image_Divers_TxtSurImg.ZOOM_FIT)
		layoutEdition = QHBoxLayout()
		for text, slot, icon in (
			(_(u"Ajouter &Texte"), self.addText, "Icones/text_add.png"),
			(_(u"Ajouter &Boite"), self.addBox, "Icones/ajouter_shape.png"),
			(_(u"Ajouter Ima&ge"), self.addPixmap, "Icones/image.png"),
			(_(u"C&opier"), self.copy, "Icones/copy.png"),
			(_(u"Cou&per"), self.cut, "Icones/cut.png"),
			(_(u"Co&ller"), self.paste, "Icones/paste.png"),
			(_(u"&Effacer..."), self.delete, "Icones/bin.png"),
			(_(u"&Monter"), self.upItem, "Icones/up.png"),
			(_(u"Descen&dre"), self.downItem, "Icones/down.png"),
			(_(u"Pivoter &Droite"), self.rotateR, "Icones/rotationd.png"),
			(_(u"Pivoter &Gauche"), self.rotateL, "Icones/rotationg.png"),
			(_(u"Zoom &fit"), self.zoomFit, "Icones/fenetre.png"),
			(_(u"Zoom &avant"), self.zoomPlus, "Icones/zoomplus.png"),
			(_(u"Zoom a&rrière"), self.zoomMoins, "Icones/zoommoins.png"),
			(_(u"Zoom &100%"), self.zoomReel, "Icones/taillereelle.png"),
			):
			if text in [_(u"C&opier"),_(u"Cou&per"),_(u"Co&ller"),_(u"&Effacer...")]:
				button = QToolButton()
				button.setIcon(QIcon(icon))
				button.setIconSize(QSize(16, 16))
				button.setToolTip(text)
				layoutEdition.addWidget(button)
				self.buttonListeNoireFeisty.append(button)
			else :
				if icon != None :
					button = QPushButton(QIcon(icon), text)
				else :
					button = QPushButton(text)
				buttonLayout.addWidget(button)

			button.setEnabled(False)
			self.buttonsList.append(button)
			self.connect(button, SIGNAL("clicked()"), slot)

			if text == _(u"&Effacer...") : # provisoire...
				buttonLayout.addLayout(layoutEdition)
				buttonLayout.addStretch(0)
			if text == _(u"Pivoter &Gauche"): # prochainement...
				buttonLayout.addStretch(0)
		buttonLayout.addStretch()

		# ComboBox de choix du format de sortie
		self.choixSortie = QComboBox()
		self.listFormatSortie = []
		self.indexFormatSortie = 0
		for text, format, extension, compression in (
				(_(u"PNG"),"PNG",u".png", 1),
				(_(u"JPEG"),"JPEG",u".jpg", 2),
				(_(u"BMP"),"BMP",u".bmp", 0),
				(_(u"TIFF"),"TIFF",u".tiff", 0),
				(_(u"PPM"),"PPM",u".ppm", 0)) :
			self.choixSortie.addItem(text)
			self.listFormatSortie.append([format,extension,compression])
		self.connect(self.choixSortie, SIGNAL("currentIndexChanged(int)"), self.setFormatSortie)
		buttonLayout.addWidget(self.choixSortie)
		
		self.labelQualite = QLabel(_(u"Qualité de l'image"))
		self.cbQualite = QComboBox()
		self.cbQualite.addItems(["0","10","20","30","40","50","60","70","80","85","90","95","100"])
		self.cbQualite.setCurrentIndex(8)
		hboxQ = QHBoxLayout()
		hboxQ.addWidget(self.labelQualite)
		hboxQ.addWidget(self.cbQualite)
		buttonLayout.addLayout(hboxQ)
		self.labelQualite.hide()
		self.cbQualite.hide()

		hbox = QHBoxLayout()
		hbox.addWidget(self.view, 1)
		hbox.addLayout(buttonLayout)
		self.widReglage.setLayout(hbox)
		self.tabwidget.addTab(self.widReglage, _(u"Réglage"))
		
		# 4. Création du tab de visualisation du résultat
		self.afficheurImgDestination=Lecture_VisionImage(statusBar)
		self.tabwidget.addTab(self.afficheurImgDestination, _(u'Visualisation des images créées'))

		# 5. Création du tab Info
		self.Logs = QTextEdit() # Zone d'affichage des infos (Zone acceptant le formatage HTML et avec assenceur automatique si nécessaire)
		self.tabwidget.addTab(self.Logs, _(u'Infos'))
		
		#-----------------
		# widgets du bas
		#-----------------
		
		# 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"))
		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.appliquer)
		
		# Ligne de séparation juste au dessus des boutons
		ligne = QFrame()
		ligne.setFrameShape(QFrame.HLine)
		ligne.setFrameShadow(QFrame.Sunken)
		
		hbox=QHBoxLayout()
		hbox.addWidget(boutAide)
		hbox.addStretch()
		hbox.addWidget(self.boutAppliquer)

		vbox.addWidget(self.tabwidget)
		vbox.addWidget(ligne)
		vbox.addLayout(hbox)

		self.setLayout(vbox)


	def setFormatSortie(self, index) :
		self.indexFormatSortie = index
		if self.listFormatSortie[index][2] == 2 :
			self.labelQualite.show()
			self.cbQualite.show()
		else :
			self.labelQualite.hide()
			self.cbQualite.hide()


	def modifImgSource(self, i=True):
		"""On active ou désactive les boutons d'action et on recharge le pseudo-aperçu de planche-contact
		en fonction du nombre d'images présentes dans le widget de sélection"""
		###self.displayImg()
		self.boutAppliquer.setEnabled(i)
		selectedImg = self.afficheurImgSource.getFile()
		if self.setBackgroundImg(selectedImg) :
			if PYQT_VERSION_STR != "4.1.0":
				for button in self.buttonsList:
					button.setEnabled(True)
			else:
				# Pour empêcher le bogue de PyQt4.1.0 Feisty
				for button in self.buttonsList:
					if button not in self.buttonListeNoireFeisty:
						button.setEnabled(True)
			self.zoom(Image_Divers_TxtSurImg.ZOOM_FIT)


	def setBackgroundImg (self, selectedImg) :
		if selectedImg and selectedImg != self.baseImage :
			self.baseImage = selectedImg
			# Création de l'objet QImage de l'image de base
			pixmap = QPixmap(self.baseImage)
			# Suppression de la précédente image si elle existait
			if not self.background.pixmap().isNull():
				self.scene.removeItem(self.background)
		
			# Ajout de la nouvelle image
			self.scene.setSceneRect(0, 0, pixmap.width(), pixmap.height())
			self.background = self.scene.addPixmap(pixmap)
			self.background.setZValue(0)
			return True
		else : return False


	def position(self, pixmap=None):
		#print "QCursor.pos()", QCursor.pos().x(), QCursor.pos().y()
		EkdPrint(u"QCursor.pos() %s %s" % (QCursor.pos().x(), QCursor.pos().y()))
		point = self.mapFromGlobal(QCursor.pos())
		#print "self.mapFromGlobal(QCursor.pos()", point.x(), point.y()
		EkdPrint(u"self.mapFromGlobal(QCursor.pos() %s %s" % (point.x(), point.y()))
		if not self.view.geometry().contains(point) or pixmap:
			#print "if not self.view.geometry().contains(point) or pixmap"
			EkdPrint(u"if not self.view.geometry().contains(point) or pixmap")
			coord = random.randint(36, 144)
			point = QPoint(coord, coord)
		else:
			#print "else de if not self.view..."
			EkdPrint(u"else de if not self.view...")
			if point == self.prevPoint:
				#print "if point == self.prevPoint"
				EkdPrint(u"if point == self.prevPoint")
				point += QPoint(self.addOffset, self.addOffset)
				self.addOffset += 5
			else:
				#print "else de if point..."
				EkdPrint(u"else de if point...")
				self.addOffset = 5
				self.prevPoint = point
		#print "point", point.x(), point.y(), '\n'
		EkdPrint("upoint %s %s\n" % (point.x(), point.y()))
		# La position du point haut-gauche du rectangle encadrant le texte
		#print "self.view.mapToScene(point)", self.view.mapToScene(point).x(), self.view.mapToScene(point).y()
		EkdPrint(u"self.view.mapToScene(point) %s %s" % (self.view.mapToScene(point).x(), self.view.mapToScene(point).y()))
		return self.view.mapToScene(point)


	def addText(self):
		self.z += 1
		dialog = TextItemDlg(position=self.position(), scene=self.scene, parent=self, z=self.z)
		dialog.exec_()
	

	def addBox(self):
		self.z += 1
		BoxItem(self.position(), self.scene, z=self.z)
	

	def addPixmap(self):
		formats = ["*.%s" % unicode(format).lower() \
			for format in QImageReader.supportedImageFormats()]
		path = self.base.getRepSource(self.config)
		txt = _(u"Fichiers Image")
		fname = unicode(QFileDialog.getOpenFileName(self, "Ouvrir des images",
			path, "%s (%s);;%s" %(txt, " ".join(formats), FORMAT)))
		if not fname: return
		self.base.setRepSource(self.config, fname)
		self.createPixmapItem(QPixmap(fname), self.position(pixmap=True))
	

	def createPixmapItem(self, pixmap, position, rotation=0):
		self.z += 1
		item = GraphicsPixmapItem(pixmap, position, rotation, z=self.z)
		item.setFlags(QGraphicsItem.ItemIsSelectable|
			QGraphicsItem.ItemIsMovable)
		self.scene.clearSelection()
		self.scene.addItem(item)
		item.setZValue(self.z+1)
		self.z += 1
		item.setSelected(True)
	

	def selectedItem(self):
		items = self.scene.selectedItems()
		if len(items) == 1:
			return items[0]
		return None
	

	def copy(self):
		item = self.selectedItem()
		if item is None:
			return
		self.copiedItem.clear()
		self.pasteOffset = 5
		stream = QDataStream(self.copiedItem, QIODevice.WriteOnly)
		self.writeItemToStream(stream, item)
	

	def cut(self):
		item = self.selectedItem()
		if item is None:
			return
		self.copy()
		self.scene.removeItem(item)
		del item
	

	def paste(self):
		if self.copiedItem.isEmpty():
			return
		stream = QDataStream(self.copiedItem, QIODevice.ReadOnly)
		self.readItemFromStream(stream, self.pasteOffset)
		self.pasteOffset += 5


	def upItem(self) :
		'''Fonction pour relever le/les item(s) sélectionné(s))'''
		for item in self.scene.selectedItems():
			z = item.zValue()
			item.setZValue(z+1)


	def downItem(self) :
		'''Fonction pour descendre le/les item(s) sélectionné(s))'''
		for item in self.scene.selectedItems():
			z = item.zValue()
			if z>1 : # Pour éviter de placer un élément en dessous de l'image source qui est au niveau 0.
				item.setZValue(z-1)
	

	def rotateR(self):
		for item in self.scene.selectedItems():
			item.setRotateR(22.5)
	

	def rotateL(self):
		for item in self.scene.selectedItems():
			item.setRotateL(22.5)
	

	def delete(self):
		items = self.scene.selectedItems()
		#print "items sélectionnés", items ###
		EkdPrint(u"items sélectionnés %s" % items)
		txt1,txt2=_(u"Effacer"),_(u"élément")
		if len(items) != 1: s = "s"
		else: s = ""
		if len(items) and QMessageBox.question(self,
			_(u"Texte sur images - Effacer"),
			"%s %d %s%s?" % (txt1,len(items),txt2,s),
			QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes:
			while items:
				item = items.pop()
				#print "item?", item
				EkdPrint(u"item? %s" % item)
				self.scene.removeItem(item)
				del item
	
	
	def readItemFromStream(self, stream, offset=0):
		type_ = QString()
		position = QPointF()
		stream >> type_ >> position
		rotation = stream.readInt16()
		if offset:
			position += QPointF(offset, offset)
		if type_ == "Text":
			text = QString()
			font = QFont()
			color = QColor()
			stream >> text >> font >> color
			self.z += 1
			TextItem(text, position, self.scene, font, color, rotation, self.z)
		elif type_ == "Pixmap":
			pixmap = QPixmap()
			stream >> pixmap
			self.createPixmapItem(pixmap, position, rotation)
		elif type_ == "Box":
			rect = QRectF()
			stream >> rect
			style = Qt.PenStyle(stream.readInt16())
			borderWidth = stream.readInt16()
			boxColor = QColor()
			stream >> boxColor 
			self.z += 1
			BoxItem(position, self.scene, style, borderWidth, boxColor, rect, rotation, self.z)
	

	def writeItemToStream(self, stream, item):
		if isinstance(item, QGraphicsTextItem):
			stream << QString("Text") << item.pos()
			stream.writeInt16(item.getRotate())
			stream << item.toPlainText() << item.font() << item.defaultTextColor()
		elif isinstance(item, QGraphicsPixmapItem):
			stream << QString("Pixmap") << item.pos()
			stream.writeInt16(item.getRotate())
			stream << item.pixmap()
		elif isinstance(item, BoxItem):
			stream << QString("Box") << item.pos()
			stream.writeInt16(item.getRotate())
			stream << item.rect
			stream.writeInt16(item.style)
			stream.writeInt16(item.borderWidth)
			stream << item.boxColor
	

	def appliquer(self):
		"Conversion des images: ajout de textes, images et boites"
		
		# Utilisation de la nouvelle boîte de dialogue de sauvegarde
		suffix=""
                fname = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=True)
		fname = fname.getFile()

		if not fname: return

		# Gestion de l'extension
		if fname.endswith(self.listFormatSortie[self.indexFormatSortie][1]) :
			fname = fname[:-len(self.listFormatSortie[self.indexFormatSortie][1])]

		# Progression
		progress=QProgressDialog(_(u"Conversion en cours..."), _(u"Arrêter"), 0, 100)
		progress.setWindowTitle(_(u'EnKoDeur-Mixeur. Fenêtre de progression'))
		progress.show()
		progress.setValue(0)

		# Module traitement par lot
		lstImg = self.afficheurImgSource.getFiles()
		nbrImg = len(lstImg)
		lstFname = []
		k = 1
		for bimg in lstImg :
			self.setBackgroundImg(bimg)
			# Enregistrement de l'image composée
			imgDim = self.scene.sceneRect()
			imgFinal = QImage(imgDim.width(), imgDim.height(), QImage.Format_ARGB32)
			self.scene.clearSelection()
			self.scene.render(QPainter(imgFinal))
			if self.listFormatSortie[self.indexFormatSortie][2] == 2 : # Qualité = valeur de la combobox
				qu = int(self.cbQualite.currentText())
			elif self.listFormatSortie[self.indexFormatSortie][2] == 0 : # Qualité 100 pour les images sans compression
				qu = 100
			else : # Compression maximale pour les images PNG car format non destructif.
				qu = 0

			if imgFinal.save(fname+string.zfill(str(k), 5)+self.listFormatSortie[self.indexFormatSortie][1], self.listFormatSortie[self.indexFormatSortie][0], qu) :
				lstFname.append(fname+string.zfill(str(k), 5)+self.listFormatSortie[self.indexFormatSortie][1])
			else :
				#print "Erreur lors de la sauvegarde de l'image"
				EkdPrint(u"Erreur lors de la sauvegarde de l'image")
			progress.setValue(int(100*k/nbrImg))
			k += 1
		# Affichage du résultat
		self.afficheurImgDestination.cheminImage = u""
		self.afficheurImgDestination.updateImages(lstFname)
		# Mise à jour du log
		self.updateLog(lstImg, lstFname)


	def updateLog(self, images, sorties) :
		"""Fonction pour la mise à jour des informations de log données à l'utilisateur en fin de process sur les données entrées, et le résultat du process"""
		msgIm = _(u"<p>####################################<br># Image(s) chargée(s)<br>####################################</p>")
		for i in images :
			msgIm += unicode(i)+u"<br>"

		msgOut = _(u"<br><p>####################################<br># Fichier(s) de sortie<br>####################################</p>")
		for s in sorties :
			msgOut += unicode(s)+u"<br>"

		self.Logs.setHtml(QString(msgIm+msgOut))


	def zoom(self, val):
		if val==Image_Divers_TxtSurImg.ZOOM_PLUS:
			self.view.scale(1.2, 1.2)
		elif val==Image_Divers_TxtSurImg.ZOOM_MOINS:
			self.view.scale(0.8, 0.8)
		elif val==Image_Divers_TxtSurImg.ZOOM_REEL:
			self.view.resetTransform()
		elif val==Image_Divers_TxtSurImg.ZOOM_FIT :
			self.view.fitInView(self.background, Qt.KeepAspectRatio)
	

	def afficherAide(self):
		"Boîte de dialogue de l'aide"

		messageAide = EkdAide(parent=self)
		messageAide.setText(_(u"<p><b>Ici vous pouvez ajouter du texte, des cadres ou des petites images comme des logos sur des images. Cela peut être utile pour <i>signer</i> ou indiquer la provenance d'un lot d'images (avant par exemple de les diffuser sur internet).</b></p><p><b>De nombreux réglages vous sont proposés (dans l'onglet 'Réglage') afin d'inscrire et disposer comme bon vous semble du texte sur un lot d'images.</b></p>\
		\
		<p>Dans l'onglet <b>Image(s) source</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher vos image(s). Si vous voulez sélectionner plusieurs images d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos images), cliquez sur <b>Ajouter</b>. Toujours dans l'onglet <b>Image(s) source</b> sélectionnez une des miniatures.</p><p>Allez maintenant dans l'onglet <b>Réglages</b>, l'image de la miniature que vous venez de sélectionner est affichée. Vous pouvez zoomer ou dézoomer l'image avec le bouton du milieu de la souris. Dans cet onglet <b>Réglages</b> (et sur la droite), vous voyez toute une série de boutons (qui correspondent aux différentes actions que vous pouvez effectuer sur l'image ou sur le texte que vous allez écrire sur cette image). Nous allons maintenant, simplement ajouter un texte, pour ce faire cliquez sur le bouton <b>Ajouter texte</b>, la boîte de dialogue <b>Texte sur images - Ajouter texte</b> apparaît, avant d'écrire quoi que ce soit (dans le champ réservé à cet effet), faites le choix de la <b>Police</b>, de la <b>Taille</b> et de la <b>Couleur</b>, écrivez maintenant votre texte, une fois fait, cliquez sur le bouton <b>OK</b>. Vérifiez que votre texte ne dépasse pas de l'image (si cela est la cas, double-cliquez sur le texte en question, la boîte de dialogue de saisie va réapparaître) et faites ce qu'il faut ... . Vous pouvez positionner le texte sur l'image en bougeant le cadre où se trouve le texte avec la souris. Vous pouvez si vous le désirez ajouter une boîte (par le bouton <b>Ajouter Boîte</b>). Pour choisir le contour de la boîte, faites un clic droit dessus, un menu vous permettra de choisir le contour que vous désirez. En ce qui concerne la taille de la boîte, il est possible de régler la taille de celle-ci en pressant simultanément les touches <b>Shift</b> et <b>Flèche droite (ou gauche)</b> pour changer la taille en largeur, <b>Shift</b> et <b>Flèche haut (ou bas)</b> pour changer la taille en hauteur (pas besoin d'utiliser la souris pour changer la taille). Vous pouvez ajouter une image (par le bouton <b>Ajouter Image</b>), sélectionnez le chemin de votre image (puis l'image elle-même) dans la boîte de dialogue <b>Ouvrir des images</b>, cliquez sur le bouton <b>Ouvrir</b>. Certaines actions peuvent être effectuées (comme Copier, Couper, Coller, Effacer) par les boutons juste en dessous de <b>Ajouter Image</b> (positionnez votre souris sur ces boutons et il vous sera indiqué à quoi ils correspondent). Vous pouvez déplacer les éléments un à un dans la scène en cliquant dessus et en déplaçant la souris sans relacher le clic gauche (vu précédemment). Vous pouvez aussi déplacer plusieurs éléments en même temps comme expliqué précédemment mais en 'dessinant' au préalable un rectangle qui les englobera (bouton gauche de la souris maintenu en la déplaçant). L'ordre d'affichage des éléments peut-être modifié au moyen des boutons <b>Monter</b> et <b>Descendre</b>.</p><p>Sélectionnez votre format d'image (PNG, JPEG, BMP, TIFF ou PPM), c'est à dire votre extension d'image, dans la liste déroulante (tout en bas) prévue à cet effet. Dans le cas d'une sélection JPEG, n'oubliez pas de régler la <b>Qualité de l'image</b>.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>Appliquer</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>Nom de fichier</b>, cliquez sur le bouton <b>Enregistrer</b>.</p><p>Si vous faites un clic droit de la souris (sur l'image) dans l'onglet <b>Visualisation des images créées</b>, vous accédez à des paramètres vous permettant différents affichages de la dite image. De même dans cet onglet vous pouvez lancer la visualisation des images par le bouton <b>Lancer le diaporama</b> (le bouton violet avec une flèche blanche vers la droite).</p><p>L'onglet <b>Infos</b> vous permet de voir le filtre utilisé, les image(s) chargée(s) et les image(s) convertie(s).</p>"))
		messageAide.show()


	def save(self) :
		self.afficheurImgSource.saveFileLocation(self.idSection)


	def load(self) :
		self.afficheurImgSource.loadFileLocation(self.idSection)
Ejemplo n.º 8
0
class Animation_SeparVideoEtAudio(Base):
	# ----------------------------------------------------------------------------------------
	# Cadre accueillant les widgets de : Animation >> Séparer le flux vidéo et le flux audio
	# ----------------------------------------------------------------------------------------

	def __init__(self):
		vbox=QVBoxLayout()

		#=== Variable de configuration ===#
		self.config=EkdConfig

		#=== Identifiant de la classe ===#
		self.idSection = "animation_separer_audio_et_video"
		super(Animation_SeparVideoEtAudio, self).__init__('vbox', titre=_(u"Séparation audio/vidéo")) # module de animation
		self.printSection()

		## On utilise le selecteur d'image pour les vidéos
		from gui_modules_image.selectWidget import SelectWidget
		# Là où on naviguera entre les fichiers
		self.afficheurVideoSource=SelectWidget(extensions = ["*.avi", "*.mpg", "*.mpeg", "*.mjpeg", "*.flv", "*.mp4", "*.dv", "*.vob"], mode="texte", video = True)
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"),self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
		## -------------------------------------------------------------------

		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "video" # Défini le type de fichier source.
		self.typeSortie = ["video","audio"] # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurVideoSource # Fait le lien avec le sélecteur de fichier source.

		# -------------------------------------------------------------------
		# Boîte de groupe "Réglage de sortie de l'encodage"
		# -------------------------------------------------------------------
		groupBox = QGroupBox("")
		self.layoutReglage = QHBoxLayout(groupBox)
		#=== Widget qui seront inclus dans la boite de réglage ===#
		# boite de combo
		self.combo=QComboBox() # le self sert à afficher des informations sur les items à partir de la fonction
		# listeCombo=[(texte, clé QVariant)...]
		self.listeCombo=[        (_(u'Vidéo et audio') ,'video&audio'),\
					 (_(u'Vidéo seulement') ,'video'),\
					 (_(u'Audio seulement') , 'audio'),]

		# Insertion des codecs de compression dans la combo box
		for i in self.listeCombo:
                	self.combo.addItem(i[0],QVariant(i[1]))

		self.connect(self.combo, SIGNAL("activated(int)"), self.sauverTypeExtraction)

		self.layoutReglage.addWidget(self.combo, 0, Qt.AlignHCenter)

		# On tient-compte du paramètre de configuration
		try:
			typ = self.config.get(self.idSection,'type_extraction')
			indice = 0 # indice de la ligne de self.listeCombo correspondant au type d'ordre
			for i in listeCombo:
				if i[1]!=typ:
					indice += 1
				else:
					break
			self.combo.setCurrentIndex(indice)
		except:
			self.combo.setCurrentIndex(0)

		self.add(groupBox, _(u"Réglages"))
		self.addPreview(nomPreview = u"Visualiser / écouter - Vidéo / Audio", light = False, mode = "Video+Audio")
		self.addLog()

	def getFile(self):
		'''
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])

		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(True)
		self.boutApp.setEnabled(True)
		if self.idSection == "animation_filtresvideo":
			self.boutApercu.setEnabled(True)
			self.filtreDecouper.setButtonEnabled(True)
		self.emit(SIGNAL("loaded"))
		return self.chemin


	def sauverTypeExtraction(self,i):
		""" Sauvegarde du mode d'extraction """
		idCombo= str(self.combo.itemData(i).toStringList()[0])
		self.config.set(self.idSection,'type_extraction',idCombo)


	def afficherAide(self):
		""" Boîte de dialogue de l'aide """
		super(Animation_SeparVideoEtAudio,self).afficherAide(_(u"<p><b>Ici vous pouvez séparer le canal vidéo et le canal audio dans une vidéo (bien entendu une vidéo qui contient du son).</b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>'Réglages'</b> sélectionnez le mode d'extraction (<b>'Vidéo et audio'</b>, <b>'Vidéo seulement'</b> ou <b>'Audio seulement'</b>).</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion. A la fin cliquez sur le bouton <b>'Voir les informations d'encodage'</b> et fermez cette dernière fenêtre après avoir vu les informations en question.</p><p>Dans l'onglet <b>'Visualiser / écouter - Vidéo / Audio'</b>, dans le cas d'une extaction <b>'Vidéo et audio'</b> vous pouvez à la fois visionner la vidéo d'un côté et écouter l'audio de l'autre, dans le cas d'une extraction de la <b>'Vidéo seulement'</b>, vous pouvez lire la vidéo résultante (le bouton de lecture audio reste grisé), et dans le dernier cas résultat de l'extraction de l'<b>'Audio seulement'</b>, vous pourrez écouter la piste audio extraite (dans ce cas la zone de lecture vidéo est cette fois-ci grisée). Dans les deux cas (extraction vidéo et audio, et vidéo seulement) le visionnement de la vidéo d'origine se fait en sélectionnant <b>'vidéo(s) source(s)'</b>, et <b>'vidéo convertie'</b> pour la vidéo résultante de l'extraction. Vous pouvez lire les deux en même temps, en cliquant sur le bouton <b>'Comparateur de vidéos'</b></p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos chargées (avec leurs chemins exacts) avant et après conversion.</p>"))


	def ouvrirSource(self, nomEntree=None):
		"""Récupération du chemin de la vidéo sélectionnée et activation de certains widgets"""

		# Récupération du chemin de la vidéo
		chemin = self.recupSource(nomEntree)

		if not chemin: return
		# Affichage du chemin + nom de fichier dans la ligne d'édition
		self.ligneEditionSource.setText(chemin)

		self.mplayer.listeVideos = [chemin]
		self.mplayer.setEnabled(True)
		self.boutApp.setEnabled(True)


	def extraireVideo(self, cheminVideoEntre, SortieVideoSFA, laisserOuvert=1):
		"""extraction de la vidéo du fichier"""

		try:
			mencoder = WidgetMEncoder("extractionvideo", cheminVideoEntre, SortieVideoSFA, laisserOuvert=laisserOuvert)
			mencoder.setWindowTitle(_(u"Extraction vidéo"))
			mencoder.exec_()
		except:
			messageErrAnEnc=QMessageBox(self)
			messageErrAnEnc.setText(_(u"Problème d'extraction vidéo (mencoder)"))
			messageErrAnEnc.setWindowTitle(_(u"Error"))
			messageErrAnEnc.setIcon(QMessageBox.Warning)
			messageErrAnEnc.exec_()
			return


	def extraireAudio(self, cheminVideoEntre, SortieAudioSFA, laisserOuvert=1):
		"""extraction de l'audio du fichier"""

		ffmpeg = WidgetFFmpeg("extractionaudio", cheminVideoEntre, SortieAudioSFA, laisserOuvert=laisserOuvert)
		ffmpeg.setWindowTitle(_(u"Extraction audio"))
		ffmpeg.exec_()

	def appliquer(self, nomSortie=None, ouvert=1):
		""" appelle la boite de dialogue de sélection de fichier à sauver et appel de la fonction de séparation audio-vidéo """

		# quel est l'index du dernier item sélectionné de la boîte de combo?
		index=self.combo.currentIndex()

		## On utilise la nouvelle interface de récupération des vidéos
		# Récupération du chemin source
		chemin = self.getFile()

		if not nomSortie:
			# suffix du fichier actif
			if self.combo.currentIndex() == 2 : suffix = ".wav"
			else : suffix=os.path.splitext(chemin)[1]
                        saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver"))
			cheminFichierEnregistrerVideo = saveDialog.getFile()

		else: # module séquentiel
			cheminFichierEnregistrerVideo = nomSortie

		if not cheminFichierEnregistrerVideo: return
		###########################################################################################################################


		# chemins complets de sortie vidéo et audio
		SortieVideoSFA = cheminFichierEnregistrerVideo
		# Vérification de l'extension du nom de fichier indiqué.
		if cheminFichierEnregistrerVideo.endswith(".wav") : SortieAudioSFA = cheminFichierEnregistrerVideo
		else : SortieAudioSFA = cheminFichierEnregistrerVideo + ".wav"
		##########################################################################################################################

		#=== Separation du fichier video ===#
		idCombo = self.listeCombo[index][1]
		#print "Combo :", idCombo
		EkdPrint(u"Combo : %s" % idCombo)

		# extraction
		self.radioSource.setEnabled(True)
		self.radioSource.setChecked(False)
		self.radioConvert.setEnabled(True)
		self.boutCompare.setEnabled(True)

		# extraction audio et vidéo
		if idCombo == 'video&audio':
			# le 3ème argument sert à fermer automatiquement la fenêtre d'encodage
			#print "Extraction Vidéo"
			EkdPrint(u"Extraction Vidéo")
			self.extraireVideo(chemin, SortieVideoSFA, 0)
			#print "Extraction Audio"
			EkdPrint(u"Extraction Audio")
			self.extraireAudio(chemin, SortieAudioSFA, ouvert)
			self.lstFichiersSortie = [SortieVideoSFA]
			self.mplayerA.setEnabled(True)
			self.mplayerA.setVideos([SortieAudioSFA])
			self.infoLog(None, chemin, None, [SortieVideoSFA,SortieAudioSFA])
			self.radioConvert.setChecked(True)

		# extraction vidéo
		elif idCombo == 'video':
			self.extraireVideo(chemin, SortieVideoSFA, ouvert)
			self.lstFichiersSortie = [SortieVideoSFA]
			self.mplayerA.setEnabled(False)
			self.infoLog(None, chemin, None, SortieVideoSFA)
			self.radioConvert.setChecked(True)

		# extraction audio
		elif idCombo == 'audio':
			self.extraireAudio(chemin, SortieAudioSFA, ouvert)
			self.lstFichiersSortie = None
			self.mplayerA.setEnabled(True)
			self.mplayerA.setVideos([SortieAudioSFA])
			self.infoLog(None, chemin, None, [SortieVideoSFA,SortieAudioSFA])
			self.infoLog(None, chemin, None, SortieAudioSFA)
			self.radioSource.setChecked(True)
			self.radioConvert.setEnabled(False)
			self.boutCompare.setEnabled(False)

		return SortieVideoSFA


	def sequentiel(self, entree, sortie, ouvert=0):
		"""Utile dans le module du même nom. Applique les opérations de la classe. Retourne le vrai nom du fichier de sortie"""
		self.ouvrirSource(entree)
		return self.appliquer(sortie, ouvert)


	def sequentielReglage(self):
		"""Utile dans le module du même nom. Récupère le widget de réglage associé à l'identifiant donné en 1er argument. Retourne l'instance du widget de réglage"""
		return self.groupReglage

	def saveFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.saveFileLocation(self.idSection)

	def loadFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.loadFileLocation(self.idSection)

	def load(self):
		'''
		Chargement de la configuration de tous les objets
		'''
		self.loadFiles()

	def save(self):
		'''
		Sauvegarde de la configuration de tous les objets
		'''
		self.saveFiles()
Ejemplo n.º 9
0
class Animation_MontagVideoVidSeul(Base):
	""" -----------------------------------------
	# Cadre accueillant les widgets de :
	# Animation >> Montage vidéo >> Vidéo seulement
	# -----------------------------------------"""


	def __init__(self, statusBar):

		# -------------------------------
		# Parametres généraux du widget
		# -------------------------------

		#=== tout sera mis dans une boîte verticale ===#
		vbox=QVBoxLayout()

		#=== Variable de configuration ===#
		self.config=EkdConfig

		# Identifiant de la classe
		self.idSection = "animation_montage_video_seul"

		super(Animation_MontagVideoVidSeul, self).__init__(boite='vbox', titre=_(u"Montage: Vidéo seulement"))

		self.printSection()

		# Création des répertoires temporaires
		self.repTampon = EkdConfig.getTempDir() + os.sep

		if os.path.isdir(self.repTampon) is False:
        		os.makedirs(self.repTampon)

		# Au cas où le répertoire existait déjà et qu'il n'était pas vide -> purge (simple précausion)
		for toutRepCompo in glob.glob(self.repTampon+'*.*'):
			os.remove(toutRepCompo)

		# Liste de fichiers initiaux (contenu dans le fichier de configuration)
		self.lstFichiersSource = []
		self.lstFichiersSourceProv = [] # idem mais provisoire (sert à récupérer les chemins dans l'ordre de sélection)

		self.statusBar = statusBar

		#-------------------------------------------------------------------
		self.afficheurVideoSource=SelectWidget(extensions = ["*.avi", "*.mpg", "*.mpeg", "*.mjpeg", "*.flv", "*.mp4", "*.h264", "*.dv", "*.vob"], mode="texte", video = True)

		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "video" # Défini le type de fichier source.
		self.typeSortie = "video" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurVideoSource # Fait le lien avec le sélecteur de fichier source.

		#####################################################################
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"),self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
		#--------------------------------------------------------------------
		self.addReglage(boite="vbox")
		#--------------------------------------------------------------------
		#=== Widget qui seront inclus dans la boite de réglage ===#
		self.ordreVideo = selectJoinMultipleSound(0, self)
		self.ordreVideo.setTitleAndTips(_(u"Fichiers vidéos à joindre"), _(u"Liste des fichiers vidéo à joindre. <b>Pour information, vous pouvez monter et descendre les fichiers grâce aux flèches haut et bas (les fichiers apparaissant en haut de la liste sont ceux qui seront au début du montage)</b>"))
		self.layoutReglage.addWidget(self.ordreVideo)

		# ----------------------------
		# Boite de groupe de mplayer
		# ----------------------------
		self.addPreview()
		self.addLog()


	def getFile(self):
		'''
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.lstFichiersSource = self.afficheurVideoSource.getFiles()
		self.ordreVideo.addSoundAction(self.lstFichiersSource)
		self.ordreVideo.delFile(self.lstFichiersSource)
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])

		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(True)

		self.emit(SIGNAL("loaded"))
		return self.chemin


	def listeChemins(self, ch):
		"""transforme une chaine de caractères en liste de chemins"""
		lst = ch.split("'")
		for i in lst: # i: élément de la ligne
			if (',' in i) or ('[' in i) or (']' in i):
				lst.remove(i)
		return lst


	def fctRadioSource(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: 
			self.mplayer.listeVideos = self.lstFichiersSource
			try :
				self.radioConvert.setChecked(False)
			except : None


	def fctRadioConvert(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: 
			self.mplayer.listeVideos = self.lstFichiersSortie
			try :
				self.radioSource.setChecked(False)
			except : None


	def ouvrirSource(self, nomEntree=None):
		""" Récupération des chemins vidéo sélectionnée """
		chemin = self.recupSources(nomEntree)
		if not chemin: return
		self.lstFichiersSource = []

		for fichier in chemin:
			self.lstFichiersSource.append(fichier)

		self.lstFichiersSourceProv = self.lstFichiersSource[:] # ':' car sinon on créé un alias

		if len(self.lstFichiersSourceProv)>1:
			self.boutApp.setEnabled(True)
			self.boutApp.setToolTip("")
		else:
			self.boutApp.setEnabled(False)
			self.boutApp.setToolTip(_(u"Veuillez sélectionner au moins 2 vidéos"))

		self.mplayer.setEnabled(True)
		self.mplayer.listeVideos = self.lstFichiersSource
		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(False)
		self.radioConvert.setEnabled(False)

		self.statusBar.showMessage(_(u"La vidéo résultante ne pourra pas être lue avec tous les logiciels"))


	def afficherAide(self):
		""" Boîte de dialogue de l'aide """
		
		super(Animation_MontagVideoVidSeul, self).afficherAide(_(u"<p><b>Sous le terme de montage vidéo, vous pouvez ici assembler des vidéos pour en constituer une seule. EKD peut assembler des vidéos de diférentes nature (extensions).</b></p><p><b>Il est à noter ici (pour le Montage vidéo) que vous bénéficiez d'un traitement par lot, c'est à dire que tous les fichiers que vous allez charger seront traités (et pas seulement le fichier sélectionné).</b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>Réglages</b>, vous pouvez changer l'ordre de montage des fichiers et ce en remontant ou en redéscendant (par les flèches haut et bas) dans la liste des <b>'Fichiers vidéo à joindre'</b>.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion.</p><p>Dans l'onglet <b>'Visionner vidéo'</b> vous pouvez visionner le résultat (avant la concaténation) en sélectionnant <b>'vidéo(s) source(s)'</b>, après la concaténation <b>'vidéo convertie'</b>.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos chargées (avec leurs chemins exacts) avant et après conversion.</p>"))


	def stat_dim_video(self):
		"""Calcul statistique des dimensions des vidéos les plus présentes dans le lot"""

		from gui_modules_animation.infoVideo import infovideo

		listePrepaRedim = []

		# Détection des dimensions différentes (résolutions)
		# dans les vidéos chargées par l'utilisateur
		for parcVideoResolution in self.lstFichiersSource:
			info = infovideo(parcVideoResolution)
			listePrepaRedim.append((info.videoLargeur, info.videoHauteur))

		# 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.dimStatVideo=self.lStatDimSeq[0][1]
		
		'''
		print
		print "Toutes les dimensions des vidéos (avec le nbre de vidéos):", self.lStatDimSeq
		print 'Dimension des vidéos la plus presente dans la sequence:', self.dimStatVideo
		print "Nombre de tailles de vidéos différentes dans le lot :", len(self.lStatDimSeq)
		print
		'''
		EkdPrint(u'')
		EkdPrint(u"Toutes les dimensions des vidéos (avec le nbre de vidéos): %s" % self.lStatDimSeq)
		EkdPrint(u'Dimension des vidéos la plus presente dans la sequence: %s' % str(self.dimStatVideo))
		EkdPrint(u"Nombre de tailles de vidéos différentes dans le lot: %s" % len(self.lStatDimSeq))
		EkdPrint(u'')

		if len(self.lStatDimSeq)>1:
			return 0
		else:
			return 1
	#########################################################################################

	def stat_codec_video(self):
		"""Calcul detecte si une vidéo a un codec différent dans le lot"""
		from gui_modules_animation.infoVideo import infovideo
		listeCodec = {}
		for video in self.lstFichiersSource:
			info = infovideo(video)
			try:
				listeCodec[info.video_codec] = listeCodec[info.video_codec] + 1
			except KeyError:
				listeCodec[info.video_codec] = 1
		#print "Ensemble des codecs detectés : %s " % listeCodec.keys()
		EkdPrint(u"Ensemble des codecs détectés : %s " % listeCodec.keys())
		self.codecStatVideo = listeCodec
		
	def appliquer(self, nomSortie=None, ouvert=1):
		"""Fusion de vidéos"""

		self.lstFichiersSource = self.ordreVideo.getListFile()
		self.stat_dim_video()
		resolution = self.dimStatVideo

		self.stat_codec_video()
		
		# Chemin du répertoire temporaire
		rep_video_ext_resol = self.repTampon  + 'video_extension_resol' + os.sep

		chemin=unicode(self.ordreVideo.getListFile())

		if not nomSortie:
			suffix = '.avi'
			##############################################################################
                        saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver"))
			cheminFichierEnregistrerVideo = saveDialog.getFile()
			###################################################################################################################

		else: # module séquentiel
			cheminFichierEnregistrerVideo=nomSortie

		if not cheminFichierEnregistrerVideo: return

		fichiersSource=self.lstFichiersSource

		if os.path.isdir(rep_video_ext_resol) is False: os.makedirs(rep_video_ext_resol)


		## Dans tous les cas on change le format (mjpeg/avi)
		## # Si la liste contient plus d'un élément, c'est à dire si elle contient des
		## # extensions différentes et si la résolution des vidéos est différente
		###########################################################################################################
		wmcer = WidgetMEncoderConcatExtResol(self.lstFichiersSource, valeurNum=resolution)
		wmcer.setWindowTitle(_(u"Traitement vidéo (extension et résolution)"))
		wmcer.exec_()

		# Les vidéos chargées en vue de la concaténation sont maintenant ds
		# le rep tampon .../video_extension_resol
		self.lstFichiersSource = glob.glob(unicode(rep_video_ext_resol+'*.*'))
		# Mise en ordre (numérotation) car cela peut
		# poser problème au moment de la concaténation
		#print "Debug:: Files to mix : ", self.lstFichiersSource
		EkdPrint(u"Debug:: Files to mix : %s" % self.lstFichiersSource)
		self.lstFichiersSource.sort()

		### Concaténation élégante #########################################################
		cheminVideoProv = self.repTampon + "video" + suffix
		# on écrit dans "output" (descripteur de fichier de cheminVideoProv)

		try:
			output = open(cheminVideoProv, 'wb+')
			for cheminFichier in self.lstFichiersSource:
				input = open(cheminFichier, 'rb')
				output.write(input.read())
				input.close()
			output.close()
		except Exception, details:
			#print "**** DEBUG: Erreur dans l'ouverture du fichier : " , details
			EkdPrint(u"**** DEBUG: Erreur dans l'ouverture du fichier : %s" % details)
		# Maintenant cheminVideoProv contient la concaténation de tous fichiers
		###########################################################################################################

		#=== Commande de concaténation finale ===#
		try:
			mencoder = WidgetMEncoder('fusion_video', cheminVideoProv, cheminFichierEnregistrerVideo, laisserOuvert=ouvert)
			mencoder.setWindowTitle(_(u"Fusion des fichiers vidéos"))
			mencoder.exec_()
		except:

			messageErrAnEnc=QMessageBox(self)
			messageErrAnEnc.setText(_(u"Problème lors de l'étape finale de concaténation vidéo (mencoder)\n"))
			messageErrAnEnc.setWindowTitle(_(u"Erreur"))
			messageErrAnEnc.setIcon(QMessageBox.Warning)
			messageErrAnEnc.exec_()
			os.remove(cheminVideoProv)
			return

		# Suppression du fichier temporaire
		os.remove(cheminVideoProv)

		self.lstFichiersSortie = cheminFichierEnregistrerVideo # pour la boite de dialogue de comparaison
		self.radioConvert.setChecked(True)
		self.radioSource.setEnabled(True)
		self.radioSource.setChecked(False)
		self.radioConvert.setEnabled(True)
		### Ajouté le 04/09/2009 pour donner les infos à l'utilisateur sur ce qui a été traité.
		self.infoLog(None, fichiersSource, None, cheminFichierEnregistrerVideo)
		return self.lstFichiersSortie # module séquentiel
Ejemplo n.º 10
0
class Image_Divers_ChangFormat(QWidget):
	"""# -----------------------------------
	# Cadre accueillant les widgets de :
	# Image >> Divers >> Changer format
	# Gestion de 16 formats de fichiers
	# -----------------------------------"""
	def __init__(self, statusBar, geometry):
        	QWidget.__init__(self)

		# -------------------------------
		# Parametres généraux du widget
		# -------------------------------
		#=== tout sera mis dans une boîte verticale ===#
		vbox=QVBoxLayout(self)

		#=== Création des répertoires temporaires ===#
		# Utilisation de EkdConfig
		self.repTampon = EkdConfig.getTempDir() + os.sep

		if os.path.isdir(self.repTampon) is False:
        		os.makedirs(self.repTampon)

		# Au cas où le répertoire existait déjà et qu'il n'était pas vide
		# -> purge (simple précausion)
		for toutRepCompo in glob.glob(self.repTampon+'*.*'):
			os.remove(toutRepCompo)

		#=== Drapeaux ===#
		# Une conversion (même partielle) a-t-elle eu lieu après le chargement des images? (1: vrai)

		# Est-ce que des images ont été converties et qu'elles n'ont pas encore été montrées?
		# Marche aussi quand la conversion a été arrêté avant la fin de la 1ère image
		self.conversionImg = 0

		# Est-ce qu'une prévisualisation a été appelée?
		self.previsualImg = 0
		# Est-ce que des images sources ont été modifiées? (c'est-à-dire ajoutées ou supprimées)
		self.modifImageSource = 0

		# Délai avant conversion
		self.timer = QTimer()
		self.connect(self.timer, SIGNAL('timeout()'), self.sonderTempsActuel)

		# Fonctions communes à plusieurs cadres du module Image
		self.base = Base()
		# Gestion de la configuration via EkdConfig
		# Paramètres de configuration
		self.config = EkdConfig
		# Identifiant du cadre
		self.idSection = "image_changer_format"
		# Log du terminal
		self.base.printSection(self.idSection)
		# Fonction appelant la fenêtre principale
		self.mainWindowFrameGeometry = geometry

		self.listeImgSource = []
		self.listeImgDestin = []

		#------------------------
		# Onglets et stacked
		#------------------------
		self.tabwidget=QTabWidget()

		#=== 1er onglet ===#
		self.framReglage=QFrame()
		vboxReglage=QVBoxLayout(self.framReglage)

		# boite de combo
		self.comboReglage=QComboBox()
		self.listeComboReglage=[(_(u'JPEG (.jpg)'), '.jpg'),\
					(_(u'JPEG (.jpeg)'), '.jpeg'),\
					(_(u'PNG (.png)'), '.png'),\
					(_(u'GIF (.gif)'), '.gif'),\
					(_(u'BMP (.bmp)'), '.bmp'),\
					(_(u'PPM (.ppm)'), '.ppm'),\
					(_(u'TIFF (.tiff)'), '.tiff'),\
					(_(u'TIF (.tif)'), '.tif')]

		# Se trouve directement dans l'onglet Réglages
		self.grid = QGridLayout()
		self.grid.addWidget(QLabel(_(u"Traitement à partir de l'image (numéro)")), 0, 0)
		self.spin1=SpinSlider(1, 100000, 1, '', self)
		self.grid.addWidget(self.spin1, 0, 1)
		self.connect(self.spin1, SIGNAL("valueChanged(int)"), self.changeValNbreImg_1)
		self.grid.addWidget(QLabel(_(u"Nombre de chiffres après le nom de l'image")), 1, 0)
		self.spin2=SpinSlider(3, 18, 6, '', self)
		self.grid.addWidget(self.spin2, 1, 1)
		self.connect(self.spin2, SIGNAL("valueChanged(int)"), self.changeValNbreImg_1)

		self.grid.setAlignment(Qt.AlignHCenter)
		vboxReglage.addLayout(self.grid)
		vboxReglage.addStretch()

		# Insertion des formats dans la combo box
		for i in self.listeComboReglage:
                	self.comboReglage.addItem(i[0],QVariant(i[1]))
		self.connect(self.comboReglage, SIGNAL("currentIndexChanged(int)"), self.changerComboReglage)
		# Affiche l'entrée de la boite de combo inscrite dans un fichier de configuration
		self.base.valeurComboIni(self.comboReglage, self.config, self.idSection, 'format')

		self.grid2 = QGridLayout()

		# Label qualité pour la qualité (compression) lors de la sauvegarde en JPEG
		self.labQualite=QLabel(_(u"Qualité"))
		self.labQualite.hide()

		self.grid2.addWidget(QLabel(_(u'Type de format après traitement')), 0, 0)
		self.grid2.addWidget(self.comboReglage, 0, 1)
		self.grid2.addWidget(self.labQualite, 2, 0)
		# Réglage de la qualité pour la qualité (compression) lors de la sauvegarde en JPEG
		self.spin3=SpinSlider(1, 100, 75, '', self)
		self.spin3.hide()

		i = self.comboReglage.currentIndex()
		idCombo=str(self.comboReglage.itemData(i).toStringList()[0])
		if idCombo in ['.jpg', '.jpeg']:
			self.labQualite.show()
			self.spin3.show()
		else:
			self.labQualite.hide()
			self.spin3.hide()

		self.grid2.addWidget(self.spin3, 2, 1)
		self.connect(self.spin3, SIGNAL("valueChanged(int)"), self.changeQualitePourJPEG)

		self.grid2.setAlignment(Qt.AlignHCenter)
		vboxReglage.addLayout(self.grid2)
		vboxReglage.addStretch()

		#=== 2ème onglet ===#
		# infos - logs
		self.zoneAffichInfosImg = QTextEdit("")
		if PYQT_VERSION_STR < "4.1.0":
			self.zoneAffichInfosImg.setText = self.zoneAffichInfosImg.setPlainText
		self.zoneAffichInfosImg.setReadOnly(True)
		self.framImg=QFrame()
		vboxReglage=QVBoxLayout(self.framImg)
		vboxReglage.addWidget(self.zoneAffichInfosImg)
		self.framImg.setEnabled(False)

		# -------------------------------------------------
		# Onglets d'affichage image source et destination
		# -------------------------------------------------

		# Là où s'afficheront les images
		self.afficheurImgSource=SelectWidget(geometrie = geometry)
 		self.afficheurImgDestination=Lecture_VisionImage(statusBar)

		self.indexTabImgSource = self.tabwidget.addTab(self.afficheurImgSource, _(u'Image(s) source'))
		self.indexTabReglage=self.tabwidget.addTab(self.framReglage, _(u'Réglages'))
		self.indexTabImgDestin=self.tabwidget.addTab(self.afficheurImgDestination, _(u'Image(s) après traitement'))
		self.indexTabInfo=self.tabwidget.addTab(self.framImg, _(u'Infos'))

		vbox.addWidget(self.tabwidget)
		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "image" # Défini le type de fichier source.
		self.typeSortie = "image" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurImgSource # Fait le lien avec le sélecteur de fichier source.

		#------------------
		# Widgets du bas
		#------------------

		# boutons
		boutAide=QPushButton(_(u" Aide"))
		boutAide.setIcon(QIcon("Icones/icone_aide_128.png"))
		boutAide.setFocusPolicy(Qt.NoFocus)
		self.connect(boutAide, SIGNAL("clicked()"), self.afficherAide)
		self.boutApPremImg = QPushButton(_(u" Voir le résultat"))
		self.boutApPremImg.setIcon(QIcon("Icones/icone_visionner_128.png"))
		self.boutApPremImg.setFocusPolicy(Qt.NoFocus)
		self.boutApPremImg.setEnabled(False)
		self.connect(self.boutApPremImg, SIGNAL("clicked()"), self.visu_1ere_img)
		self.boutApp=QPushButton(_(u" Appliquer"))
		self.boutApp.setIcon(QIcon("Icones/icone_appliquer_128.png"))
		self.boutApp.setFocusPolicy(Qt.NoFocus)
		self.boutApp.setEnabled(False)
		self.connect(self.boutApp, 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()	# espace entre les 2 boutons
		hbox.addWidget(self.boutApPremImg)
		hbox.addStretch()
		hbox.addWidget(self.boutApp)
		vbox.addLayout(hbox)

		self.setLayout(vbox)

		#------------------------------------------------
		# Barre de progression dans une fenêtre séparée
		#------------------------------------------------

		self.progress=QProgressDialog(_(u"Progression ..."), _(u"Arrêter le processus"), 0, 100)
		self.progress.setWindowTitle(_(u'EnKoDeur-Mixeur. Fenêtre de progression'))
		# Attribution des nouvelles dimensions
		self.progress.setMinimumWidth(500)
		self.progress.setMinimumHeight(100)

		self.connect(self.tabwidget, SIGNAL("currentChanged(int)"), self.fctTab)

		#----------------------------------------------------------------------------------------------------
		# Signal de présence d'images dans ler widget de sélection -> modifie le statut des boutons d'action
		#----------------------------------------------------------------------------------------------------

		self.connect(self.afficheurImgSource, SIGNAL("pictureChanged(int)"), self.modifBoutonsAction)

		#----------------------------------------------------------------------------------------------------
		# Signal pour afficher ou ne pas afficher les widgets de changement de qualité pour les images
		#----------------------------------------------------------------------------------------------------

		self.connect(self.comboReglage, SIGNAL("currentIndexChanged(int)"), self.changerQualJPEG)


	def modifBoutonsAction(self, i):
		"On active ou désactive les boutons d'action selon s'il y a des images ou pas dans le widget de sélection"
		self.boutApp.setEnabled(i)
		self.boutApPremImg.setEnabled(i)
		self.modifImageSource = 1


	def changerComboReglage(self, i):
		"""Récup/affichage ds le terminal de l'index de self.comboReglage"""
		#print self.comboReglage.currentText()
		EkdPrint(u"%s" % self.comboReglage.currentText())
		self.config.set(self.idSection, 'format', self.listeComboReglage[i][1])


	def changerQualJPEG(self):
		''' Changement de la qualité pour les images jpeg à l'enregistrement '''

		# Si on sélectionne le format JPEG (avec extension .jpg ou .jpeg) dans la liste
		# déroulante, on peut régler la qualité du JPEG pour la sauvegarde
		if self.comboReglage.currentIndex() in [0, 1]:
			self.labQualite.show()
			self.spin3.show()
		# Si on sélectionne tous les autres formats, les widgets n'apparaissent pas
		else:
			self.labQualite.hide()
			self.spin3.hide()


	def changeValNbreImg_1(self):
		"""Gestion du nombre d'images à traiter"""
		#print "Traitement a partir de l'image (numero):", self.spin1.value()
		EkdPrint(u"Traitement a partir de l'image (numero): %s" % self.spin1.value())
		#print "Nombre de chiffres apres le nom de l'image:", self.spin2.value()
		EkdPrint(u"Nombre de chiffres apres le nom de l'image: %s" % self.spin2.value())


	def changeQualitePourJPEG(self):
		#print "Compression JPEG, qualité:", self.spin3.value()
		EkdPrint(u"Compression JPEG, qualité: %s" % self.spin3.value())


	def fctTab(self, i):
		"Affichage d'une ou plusieurs images converties"

		# Cela ne concerne que l'onglet de visualisation des images après leur conversion
		if i == self.indexTabImgDestin:
			if self.conversionImg:
				# Affichage si on sauvegarde par le bouton Appliquer et sauver
				#print "La conversion vient d'avoir lieu -> affichage des images du lot de destination"
				EkdPrint(u"La conversion vient d'avoir lieu -> affichage des images du lot de destination")
				cheminImages = os.path.dirname(self.listeImgDestin[0])
				liste = []
				for fichier in self.listeImgDestin:
					liste.append(os.path.basename(fichier))
				self.afficheurImgDestination.updateImages(liste, cheminImages)
			elif not self.boutApp.isEnabled() or self.modifImageSource:
				# Si le bouton de conversion n'est pas actif, c'est qu'il n'y a plus d'image source
				# -> on n'a plus de raison de maintenir des images dans l'afficheur de résultat
				# Si les images sources ont été modifiées, on purge aussi l'afficheur de résultat
				self.afficheurImgDestination.updateImages([])
			self.conversionImg = 0
			self.modifImageSource = 0


	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 visu_1ere_img(self):
		"""Fonction pour faire une simulation de rendu (avec les réglages opérés dans l'onglet Réglages)
		et ce à partir du bouton Aperçu à partir de la première image, toujours dans l'onglet Réglages.
		Pour les commentaires, se référer à la fonction chang_format juste en dessous"""

		# Récupération du fichier sélectionné par l'utilisateur (si pas de fichier
		# sélectionné par l'utilisateur, la 1ère image de la liste est prise)
		file = self.afficheurImgSource.getFile()
		if not file: return
		self.listeImgSource = [file]

		i = self.comboReglage.currentIndex()
		ext=self.comboReglage.itemData(i).toString()
		ext=str(ext).lower()

		# Formats (extensions) supportées: .bmp, .gif, .jpeg, .jpg, .mng, .pbm, .pgm,
		# .png, .ppm, .svg, .tif, .tiff, .xbm, .xpm
		formats = [".%s" % unicode(format).lower() \
			for format in QImageReader.supportedImageFormats()]

		# Chemin+nom d'image pour la sauvegarde
		self.cheminCourantSauv = self.repTampon+'0_image_visu_'+string.zfill(1, 6)+ext

		# CONVERSION

		# Uniquement pour Linux et MacOSX
		if os.name in ['posix', 'mac']:
			# On sélectionne le 'Type de format après traitement' à JPEG (.jpg)
			# ou JPEG (.jpeg) le traitement se fait par Python Imaging Library
			if i in [0, 1]:
				im = Image.open(self.listeImgSource[0]).save(self.cheminCourantSauv, quality=self.spin3.value())
			# Si on sélectionne les autres entrées, le traitement se fait par ImageMagick
			else:
				import locale
				# Conversion immédiate dans le rep tampon
				os.system(("convert "+"\""+self.listeImgSource[0]+"\""+' '+"\""+self.cheminCourantSauv+"\"").encode(locale.getdefaultlocale()[1]))
				
		# Uniquement pour windows
		elif os.name == 'nt':
			# Dans la version windows les autres entrees ne sont pas traitees 
			# par ImageMagik mais directement par Python Imaging Library (car 
			# par le traitement avec ImageMagick rien ne s'affiche, bizarre !!!)
			if i in [0, 1, 2, 3, 4, 5, 6, 7]:
				im = Image.open(self.listeImgSource[0]).save(self.cheminCourantSauv, quality=self.spin3.value())

		# AFFICHAGE

		# Récup de l'extension chargée
		ext_chargee=os.path.splitext(self.listeImgSource[0])[1]

		# Si le format (l'extension) chargé et le format sélectionné pour la sortie
		# dans Réglages sont des formats supportés, l'image avec ce format est
		# simplement affichéé
		# ----------------------------------------------------------------------
		# Aussi bizarre que cela puisse paraître (et .xpm est un format reconnu)
		# la conversion en xpm se fait bien mais l'image n'est pas lue dans le
		# lecteur --> alors conversion en jpeg ... et l'image est lue
		# ----------------------------------------------------------------------
		if ext in formats:
			# Récupération de la liste contenant le chemin+fichier contenus
			# contenu dans le répertoire temporaire
			listeImgDestinVisuTmp_0=glob.glob(self.repTampon+'*.*')
			listeImgDestinVisuTmp_0.sort()
			# Elimination des fichiers parasites si multiples conversions
			# --> seulement l'extension de sortie sélectionnée est gardée
			for parctemp_0 in listeImgDestinVisuTmp_0:
				if os.path.splitext(parctemp_0)[1]!=ext:
					os.remove(parctemp_0)

		# Affichage de l'image temporaire
		# Ouverture d'une boite de dialogue affichant l'aperçu.
		#
		# Affichage par le bouton Voir le résultat
		visio = VisionneurEvolue(self.cheminCourantSauv)
		visio.redimenFenetre(self.mainWindowFrameGeometry, 1., 0.7)
		visio.exec_()

		return 0


	def chang_format(self):
		""" Changer format (gestion de différents formats de fichiers) """

		# Récupération de la liste des fichiers chargés
		self.listeChemin=self.afficheurImgSource.getFiles()

		# Récup du format sélectionné par l'utilisateur
		i = self.comboReglage.currentIndex()
		ext=self.comboReglage.itemData(i).toString()
		ext=str(ext).lower()
		#print "format:", ext
		EkdPrint(u"format: %s" % ext)

		nbreElem=len(self.listeChemin)

		# Liste pour affichage des images chargées (ds le tabwidget)
		listeAff_1=[]
		# Liste pour affichage des pages sauvegardées (ds le tabwidget)
		listeAff_2=[]

		# La page Image résultat devient visible
		#self.tabwidget.setCurrentIndex(self.indexImageResultat)

		# Liste des formats supportés pour l'affichage
		formats = [".%s" % unicode(format).lower() \
			for format in QImageReader.supportedImageFormats()]

		#print "formats:", formats
		EkdPrint(u"formats: %s" % formats)

		process = QProcess(self)

		# Boucle principale
		for parc in range(nbreElem):

			# Chemin de sauvegarde
			vraiCheminSauv = self.chemDossierSauv+'_'+string.zfill(parc+self.spin1.value(), self.spin2.value())+ext

			# On sélectionne le 'Type de format après traitement' à JPEG (.jpg) ou JPEG (.jpeg)
			
			# Uniquement pour Linux et MacOSX
			if os.name in ['posix', 'mac']:
				# le traitement se fait par Python Imaging Library
				if i in [0, 1]:

					im = Image.open(self.listeChemin[parc]).save(vraiCheminSauv, quality=self.spin3.value())
					
			# Uniquement pour windows
			elif os.name == 'nt':
				
				if i in [0, 1, 2, 3, 4, 5, 6, 7]:
					
					im = Image.open(self.listeChemin[parc]).save(vraiCheminSauv, quality=self.spin3.value())

			# Bouton Cancel pour arrêter la progression donc le process
			if (self.progress.wasCanceled()): break


			# Si on sélectionne les autres entrées, le traitement se fait par ImageMagick
			else:

				# Enregistrement/conversion des formats sélectionnés
				process.start("convert "+"\""+self.listeChemin[parc]+"\" "+"\""+vraiCheminSauv+"\"")

				# Ajout des images par la variable vraiCheminSauv dans la liste
				self.listeImgDestin.append(vraiCheminSauv)

				listeAff_1.append(self.listeChemin[parc])
				listeAff_2.append(vraiCheminSauv)

				# ================================================================== #
				# Calcule le pourcentage effectue a chaque passage et ce pour la
				# barre de progression .
				# ---------------------------------------------
				val_pourc=((parc+1)*100)/nbreElem

				# --------------------------------------------
				# Affichage de la progression (avec
				# QProgressDialog) ds une fenêtre séparée
				self.progress.setValue(val_pourc)
				QApplication.processEvents()
				# Bouton Cancel pour arrêter la progression donc le process
				if (self.progress.wasCanceled()): break
				# --------------------------------------------

				if not process.waitForStarted(3000):
					QMessageBox.warning(None, _(u"Erreur"), _(u"Bogue au lancement de la commande"))
				process.waitForFinished(-1)

		# Conditions d'affichage des images dans l'interface
		# Si le format est supporté pour l'affichage ...
		if ext in formats:

			# Affichage des images après traitement
			#
			# Changement d'onglet et fonctions associées
			self.conversionImg = 1
			self.metaFctTab(self.indexTabImgDestin)


		# La liste pour l'affichage des images ds l'interface est
		# vidée pour que les images affichées ne s'amoncellent pas
		# si plusieurs rendus à la suite
		self.listeImgDestin=[]

		# Affichage des infos sur l'image -------------------------
		# On implémente les chemins des fichiers dans une variable
		# pour préparer l'affichage des infos
		texte1=_(u" Image(s) chargée(s)")
		texte2=_(u" Image(s) convertie(s)")
		a='#'*36

		self.infosImgProv_1=a+'\n#'+texte1+'\n'+a
		self.infosImgProv_2=a+'\n#'+texte2+'\n'+a

		# Images chargées
		for parcStatRendu_1 in listeAff_1:
			self.infosImgProv_1=self.infosImgProv_1+'\n'+parcStatRendu_1

		# Pages sauvegardées
		for parcStatRendu_2 in listeAff_2:
			self.infosImgProv_2=self.infosImgProv_2+'\n'+parcStatRendu_2

		# affichage des infos dans l'onglet
		self.zoneAffichInfosImg.setText(self.infosImgProv_1+'\n\n'+self.infosImgProv_2+'\n\n')
		self.framImg.setEnabled(True)

		# remise à 0 de la variable provisoire de log
		self.infosImgProv=''
		# ---------------------------------------------------------


	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 appliquer(self):
		"""Lancement de la fonction chang_format"""
		self.chang_format()


	def appliquer0(self):
		"""Préparation de la conversion"""

		suffix=""
		# Boîte de dialogue pour sauvegarder (nom du nouveau fichier)
                self.chemDossierSauv = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=True)
		self.chemDossierSauv = self.chemDossierSauv.getFile()

		if not self.chemDossierSauv: return

		self.progress.reset()
		self.progress.show()
		self.progress.setValue(0)
		QApplication.processEvents()

		# Lancement de la conversion dans 250 ms (seule solution trouvée pour éviter
		# le grisage au début)
		self.timer.start(250)


	def afficherAide(self):
		"""Boîte de dialogue de l'aide"""

		# ATTENTION _ a été mis à la place de tr car le script de mise à jour gettext (mise_a_jour_gettext.py)
		# ne fonctionne pas pour ekdDoc.pot avec les clés --keyword donc les nouvelles lignes vides encore
		# non traduites de la doc se retrouveront dans ekd.pot au lieu de ekdDoc.pot

		# Utilisation de EkdAide
		messageAide=EkdAide(parent=self)
		messageAide.setText(_(u"<p><b>Vous pouvez ici changer/transformer le format des images (et par là même en changer leur extension). En ce qui concerne le JPEG, vous pourrez sélectionner deux types d'extension (jpeg ou jpg), toujours pour le JPEG, vous aurez aussi la possiblité de régler la qualité (c'est à dire la compression).</b></p><p><b>Les formats pris en compte sont: JPEG, PNG, GIF, BMP, PPM, TIFF et TIF.</b></p><p>Dans l'onglet <b>'Images sources'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher vos image(s). Si vous voulez sélectionner plusieurs images d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos images), cliquez sur <b>Ajouter</b>.</p><p>Dans l'onglet <b>'Réglages'</b> faites les réglages du <b>'Traitement à partir de l'image (numéro)'</b> et du <b>'Nombre de chiffres après le nom de l'image' <font color='red'>(la plupart du temps les valeurs par défaut suffisent)</b></font>, ensuite choisissez votre <b>'Type de format après traitement'</b> et réglez la <b>'Qualité'</b> (disponible uniquement si vous avez sélectionné une image JPEG en sortie). Cliquez sur le bouton <b>'Voir le résultat'</b> (vous voyez à ce moment le résultat de vos réglages sur la première image du lot s'afficher dans une nouvelle fenêtre).</p></p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b>.</p><p>Si vous faites un clic droit de la souris (sur l'image) dans l'onglet <b>Image(s) après traitement</b>, vous accédez à des paramètres vous permettant différents affichages de la dite image. De même dans cet onglet vous pouvez lancer la visualisation des images par le bouton <b>Lancer le diaporama</b> (le bouton violet avec une flèche blanche vers la droite).</p><p>L'onglet <b>'Infos'</b> vous permet de voir le filtre utilisé, les image(s) chargée(s) et les image(s) convertie(s).</p>"))
		messageAide.show()


	def save(self) :
		self.afficheurImgSource.saveFileLocation(self.idSection)
		EkdConfig.set(self.idSection, u'choixReglage', unicode(self.comboReglage.currentIndex()))
		EkdConfig.set(self.idSection, u'spin1', unicode(self.spin1.value()))
		EkdConfig.set(self.idSection, u'spin2', unicode(self.spin2.value()))
		EkdConfig.set(self.idSection, u'spin3', unicode(self.spin3.value()))


	def load(self) :
		self.afficheurImgSource.loadFileLocation(self.idSection)
		self.comboReglage.setCurrentIndex(int(EkdConfig.get(self.idSection, u'choixReglage')))
		self.spin1.setValue(int(EkdConfig.get(self.idSection, u'spin1')))
		self.spin2.setValue(int(EkdConfig.get(self.idSection, u'spin2')))	
		self.spin3.setValue(int(EkdConfig.get(self.idSection, u'spin3')))
Ejemplo n.º 11
0
class Image_Divers_Redimensionner(QWidget):
	"""# -----------------------------------
	# Cadre accueillant les widgets de :
	# Image >> Divers >> Redimensionner
	# -----------------------------------"""
	def __init__(self, statusBar, geometry):

        	QWidget.__init__(self)
		
		# ----------------------------
		# Quelques paramètres de base
		# ----------------------------
		
		#=== Création des répertoires temporaires ===#
		# Gestion du repertoire tmp avec EkdConfig 
		self.repTampon = EkdConfig.getTempDir() + os.sep + "tampon" + os.sep + "image_divers_redimensionner" + os.sep


		if os.path.isdir(self.repTampon) is False:
        		os.makedirs(self.repTampon)
		if os.path.isdir(self.repTampon+'redim'+os.sep) is False:
        		os.makedirs(self.repTampon+'redim'+os.sep)	
		# 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'+os.sep+'*.*')
		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)
		
		# Est-ce que des images ont été converties et qu'elles n'ont pas encore été montrées?
		# Marche aussi quand la conversion a été arrêté avant la fin de la 1ère image
		self.conversionImg = 0
		
		# Est-ce qu'une prévisualisation a été appelée?
		self.previsualImg = 0
		# Est-ce que des images sources ont été modifiées? (c'est-à-dire ajoutées ou supprimées)
		self.modifImageSource = 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()

		# Gestion de la configuration via EkdConfig

		# Paramètres de configuration
		self.config = EkdConfig
		# Identifiant du cadre
		self.idSection = "image_redimensionner"
		# Log du terminal
		self.base.printSection(self.idSection)
		# Fonction appelant la fenêtre principale
		self.mainWindowFrameGeometry = geometry

		self.listeImgSource = []
		self.listeImgDestin = []
		
		# Boite d'alignement vertical
		vbox=QVBoxLayout(self)
		
		# ----------------------------------------
		# Bouton de sélection des images sources
		# ----------------------------------------
		
		self.tabwidget=QTabWidget()
		# Rq: le signal est placé à la fin de __init__ à cause d'une bizarrerie sous qt4.4
		
		#------------------
		# Onglet Réglages
		#------------------
		
		self.framReglage=QFrame()
		vboxReglage=QVBoxLayout(self.framReglage)
		
		# Gestion du nombre d'images à traiter
		self.grid = QGridLayout()
		self.grid.addWidget(QLabel(_(u"Traitement à partir de l'image (numéro)")), 0, 0)
		self.spin1_ImTrait=SpinSlider(1, 100000, 1, '', self)
		self.grid.addWidget(self.spin1_ImTrait, 0, 1)
		self.connect(self.spin1_ImTrait, SIGNAL("valueChanged(int)"), self.changeValNbreImg_1)
		self.grid.addWidget(QLabel(_(u"Nombre de chiffres après le nom de l'image")), 1, 0)
		self.spin2_ImTrait=SpinSlider(3, 18, 6, '', self)
		self.grid.addWidget(self.spin2_ImTrait, 1, 1)
		self.connect(self.spin2_ImTrait, SIGNAL("valueChanged(int)"), self.changeValNbreImg_1)
		
		self.grid.setAlignment(Qt.AlignHCenter)
		vboxReglage.addLayout(self.grid)
		vboxReglage.addStretch()
		
		#=== 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
		self.stacked_redim_avec_ratio = SpinSliders(self, 10,6000,640, _(u"nouvelle largeur"), 'largeur_ratio')
		
		# Widgets du stacked avec 2 boites de spin
		self.stacked_redim_sans_ratio = SpinSliders(self, 10,6000,640,_(u"nouvelle largeur"),'largeur_sans_ratio',
					10,6000,480, _(u"nouvelle hauteur"), 'longueur_sans_ratio')
		
		# Ajout des widgets aux stacked
		indexStacked_redim_avec_ratio = self.stacked.addWidget(self.stacked_redim_avec_ratio)
		indexStacked_redim_sans_ratio = self.stacked.addWidget(self.stacked_redim_sans_ratio)
		
		#=== Autres widgets de l'onglet réglagle ===#
		
		#---- 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 en tenant compte des proportions'), 'redim_avec_ratio',
				indexStacked_redim_avec_ratio, self.stacked_redim_avec_ratio),
			(_(u'Redimensionner sans tenir compte des proportions'), 'redim_sans_ratio',
				indexStacked_redim_sans_ratio, self.stacked_redim_sans_ratio)]
		
		# Insertion des codecs de compression dans la boite de combo
		for i in self.listeComboReglage:
                	self.comboReglage.addItem(i[0], QVariant(i[1]))
		self.connect(self.comboReglage, SIGNAL("currentIndexChanged(int)"), self.changerComboReglage)
		# Affiche l'entrée de la boite de combo inscrite dans un fichier de configuration
		self.base.valeurComboIni(self.comboReglage, self.config, self.idSection, 'methode')
		self.connect(self.comboReglage, SIGNAL("currentIndexChanged(int)"),
				self.changerApercu)
		
		#---- Pseudo-aperçu de la redimension
		self.apercu = Apercu((400, 300), statusBar)
		
		self.connect(self.stacked_redim_avec_ratio.spin, SIGNAL("valueChanged(int)"),
				self.changerApercu)
		self.connect(self.stacked_redim_sans_ratio.spin1, SIGNAL("valueChanged(int)"),
				self.changerApercu)
		self.connect(self.stacked_redim_sans_ratio.spin2, SIGNAL("valueChanged(int)"),
				self.changerApercu)
		# On donne les valeurs de largeur et hauteur des images converties à l'aperçu
		self.changerApercu()
		
		# Légende de l'aperçu
		coulSource, coulConvert = self.apercu.couleurContourSourceConvert()
		self.legendeSource = ElementLegende(": " +\
			_(u"Contours des images sources (taille la plus fréquente en foncé)"), coulSource)
		self.legendeConvert = ElementLegende(": "+ _(u"Contours des images à convertir"), coulConvert)
		self.legendeSource.hide()
		self.legendeConvert.hide()
		
		#=== Mise-en-page ===#
		
		hbox = QHBoxLayout()
		hbox.addWidget(QLabel(_(u'Type')))
		hbox.addWidget(self.comboReglage)
		hbox.setAlignment(Qt.AlignHCenter)
		
		vboxReglage.addLayout(hbox)
		vboxReglage.addWidget(self.stacked)
		hboxApercu = QHBoxLayout()
		hboxApercu.addStretch()
		hboxApercu.addWidget(self.apercu)
		hboxApercu.addStretch()
		vboxReglage.addLayout(hboxApercu)
		vboxReglage.addWidget(self.legendeSource)
		vboxReglage.addWidget(self.legendeConvert)
		vboxReglage.addStretch()
		
		#----------------
		# Onglet de log
		#----------------
		
		self.zoneAffichInfosImg = QTextEdit("")
		if PYQT_VERSION_STR < "4.1.0":
			self.zoneAffichInfosImg.setText = self.zoneAffichInfosImg.setPlainText
		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=SelectWidget(geometrie = self.mainWindowFrameGeometry)
		
		# Gestion de la configuration via EkdConfig
		self.afficheurImgDestination=Lecture_VisionImage(statusBar)
		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "image" # Défini le type de fichier source.
		self.typeSortie = "image" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurImgSource # Fait le lien avec le sélecteur de fichier source.
		
		vbox.addWidget(self.tabwidget)

		self.indexTabImgSource = self.tabwidget.addTab(self.afficheurImgSource, _(u'Image(s) source'))
		self.indexTabReglage=self.tabwidget.addTab(self.framReglage, _(u'Réglages'))
		self.indexTabImgDestin = self.tabwidget.addTab(self.afficheurImgDestination, _(u'Image(s) après traitement'))
		self.indexTabInfo=self.tabwidget.addTab(self.framInfos, _(u'Infos'))
		
		# -------------------------------------------
		# widgets du bas : curseur + ligne + boutons
		# -------------------------------------------
		
		# Boutons
		boutAide=QPushButton(_(u" Aide"))
		boutAide.setIcon(QIcon("Icones/icone_aide_128.png"))
		self.connect(boutAide, SIGNAL("clicked()"), self.afficherAide)
		self.boutApPremImg = QPushButton(_(u" Voir le résultat"))
		self.boutApPremImg.setIcon(QIcon("Icones/icone_visionner_128.png"))
		self.boutApPremImg.setFocusPolicy(Qt.NoFocus)
		# Bouton inactif au départ
		self.boutApPremImg.setEnabled(False)
		self.connect(self.boutApPremImg, SIGNAL("clicked()"), self.visu_1ere_derniere_img)
		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.boutApPremImg)
		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)

		self.connect(self.tabwidget, SIGNAL("currentChanged(int)"), self.fctTab)

		#----------------------------------------------------------------------------------------------------
		# Signal de présence d'images dans ler widget de sélection -> modifie le statut des boutons d'action
		#----------------------------------------------------------------------------------------------------
		
		self.connect(self.afficheurImgSource, SIGNAL("pictureChanged(int)"), self.modifImgSource)
		
		
	def changeValNbreImg_1(self):
		"""gestion du nombre d'images à traiter"""
		#print "Traitement a partir de l'image (numero):", self.spin1_ImTrait.value()
		EkdPrint(u"Traitement a partir de l'image (numero): %s" % self.spin1_ImTrait.value())
		#print "Nombre de chiffres apres le nom de l'image:", self.spin2_ImTrait.value()
		EkdPrint(u"Nombre de chiffres apres le nom de l'image: %s" % self.spin2_ImTrait.value())


	def modifImgSource(self, i):
		"""On active ou désactive les boutons d'action et on recharge le pseudo-aperçu de planche-contact
		en fonction du nombre d'images présentes dans le widget de sélection"""
		self.boutAppliquer.setEnabled(i)
		self.boutApPremImg.setEnabled(i)
		self.modifImageSource = 1
		if i :
			# Redessinage du canevas de peudo-aperçu
			self.stat_dim_img()
			liste = [i[1] for i in self.lStatDimSeq]
			self.apercu.setImageTaillePlusFrequente(self.imageTaillePlusFrequente)
			self.apercu.setListeTailleImage(liste)
			# Affichage de la légende
			self.legendeSource.show()
			self.legendeConvert.show()

	
	def fctTab(self, i):
		"Affichage d'une ou plusieurs images converties"
		
		# Cela ne concerne que l'onglet de visualisation des images après leur conversion
		if i == self.indexTabImgDestin:
			if self.conversionImg:
				# Affichage si on sauvegarde par le bouton Appliquer et sauver
				#print "La conversion vient d'avoir lieu -> affichage des images du lot de destination"
				EkdPrint(u"La conversion vient d'avoir lieu -> affichage des images du lot de destination")
				cheminImages = os.path.dirname(self.listeImgDestin[0])
				liste = []
				for fichier in self.listeImgDestin:
					liste.append(os.path.basename(fichier))
				self.afficheurImgDestination.updateImages(liste, cheminImages)
			elif not self.boutAppliquer.isEnabled() or self.modifImageSource:
				# Si le bouton de conversion n'est pas actif, c'est qu'il n'y a plus d'image source
				# -> on n'a plus de raison de maintenir des images dans l'afficheur de résultat
				# Si les images sources ont été modifiées, on purge aussi l'afficheur de résultat
				self.afficheurImgDestination.updateImages([])
			self.conversionImg = 0
			self.modifImageSource = 0
	
	
	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 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])
		self.config.set(self.idSection, 'methode', self.listeComboReglage[i][1])
	
	
	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\n'+d+'\n')
	

	def changerApercu(self, i=None):
		"Modification de l'aperçu quand une valeur de la nouvelle largeur ou hauteur est donnée"
		
		index = self.comboReglage.currentIndex()
		if self.comboReglage.itemData(index).toString() == "redim_sans_ratio":
			largeur = self.stacked_redim_sans_ratio.spin1.value()
			hauteur = self.stacked_redim_sans_ratio.spin2.value()
		elif self.comboReglage.itemData(index).toString() == "redim_avec_ratio":
			largeur = self.stacked_redim_avec_ratio.spin.value()
			hauteur = None
		
		self.apercu.setSizeConvertImages(largeur, hauteur)
	
	
	def stat_dim_img(self):
		"""Calcul statistique des dimensions des images les plus présentes dans le lot
		et récupération de l'adresse d'une image de la taille la plus fréquente du lot"""

		# Récupération de la liste des fichiers chargés
		self.listeImgSource=self.afficheurImgSource.getFiles()
		
		# 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]
		
		# On récupère l'adresse d'une image de la taille la plus fréquente du lot
		for index, taille in enumerate(listePrepaRedim):
			if taille == self.lStatDimSeq[0][1]:
				self.imageTaillePlusFrequente = self.listeImgSource[index]
				break
		
		#print "Toutes les dimensions des images (avec le nbre d'images):", self.lStatDimSeq
		EkdPrint(u"Toutes les dimensions des images (avec le nbre d'images): " + str(self.lStatDimSeq))
		#print 'Dimension des images la plus presente dans la sequence:', self.dimStatImg
		EkdPrint(u'Dimension des images la plus presente dans la sequence: ' + str(self.dimStatImg))
		#print "Nombre de tailles d'images différentes dans le lot :", len(self.lStatDimSeq)
		EkdPrint(u"Nombre de tailles d'images différentes dans le lot: " + str(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, Image.ANTIALIAS)
				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.process.kill()
		if len(self.listeImgDestin)!=0:
			self.conversionImg = 1
			self.logFinal(self.listeComboReglage[self.i][0])
			self.tabwidget.setCurrentIndex(self.indexTabInfo)
		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!"
			EkdPrint(u"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.indexTabInfo)
			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)
		
		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()
		
		# 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
			# Boite de dialogue d'information
			messageAide=QMessageBox(self)
			messageAide.setText(_(u"Le traitement 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
				# Mise-à-jour du log
				self.logFinal(self.listeComboReglage[self.i][0])
				# Affichage de l'onglet d'infos
				self.tabwidget.setCurrentIndex(self.indexTabInfo)
			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()

		# Récupération de la liste des fichiers chargés
		self.listeChemin=self.afficheurImgSource.getFiles()
		
		# Utilisation de la nouvelle boîte de dialogue de sauvegarde
		suffix=""
                self.cheminSauv = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=True)
		self.cheminSauv = self.cheminSauv.getFile()
		
		if not self.cheminSauv: return
		
		#print 'Chemin+nom de sauvegarde:', self.cheminSauv

		# Extension/format des images
		self.ext = os.path.splitext(self.listeChemin[0])[1]
		
		# Liste des indices des images [0,1,2,...,nombre d'images-1]
		self.listeIndex = range(len(self.listeChemin))
		
		# Nombre d'images sources
		self.nbrImg = len(self.listeChemin)
		
		# Récupération de l'identifiant du codec
		self.i = self.comboReglage.currentIndex()
		
		# Indice de l'image à convertir
		self.j = 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 visu_1ere_derniere_img(self):
		"""Conversion des images"""
		
		# Récupération de l'identifiant du codec
		i = self.comboReglage.currentIndex()
		
		# Récupération du fichier sélectionné par l'utilisateur (si pas de fichier
		# sélectionné par l'utilisateur, la 1ère image de la liste est prise)
		file = self.afficheurImgSource.getFile()
		if not file: return
		self.listeChemin = [file]
		
		if self.listeComboReglage[i][1]=='redim_avec_ratio':
			""" Redimensionnement des images avec ratio """

			# Récup réglage ci-dessous
			spin = self.listeComboReglage[i][3].spin.value() # nouvelle largeur
				
			# Chemin de sauvegarde
			self.cheminCourantSauv = self.repTampon+'image_visu_redim_'+string.zfill(1, 6)+'.jpg'
				
			# Ouverture des images. Conversion et sauvegarde
			obImg = Image.open(self.listeChemin[0])
			
			# 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)), Image.ANTIALIAS).save(self.cheminCourantSauv)
			
			# Affichage de l'image temporaire 
			# Ouverture d'une boite de dialogue affichant l'aperçu.
			#
			# Affichage par le bouton Voir le résultat
			visio = VisionneurEvolue(self.cheminCourantSauv)
			visio.redimenFenetre(self.mainWindowFrameGeometry, 1., 0.7)
			visio.exec_()
		
			return 0
			
		elif self.listeComboReglage[i][1]=='redim_sans_ratio':
			""" Redimensionnement des images sans ratio """

			# Récup réglages ci-dessous
			spin1 = self.listeComboReglage[i][3].spin1.value() # nouvelle largeur
			spin2 = self.listeComboReglage[i][3].spin2.value() # nouvelle hauteur
				
			# Chemin de sauvegarde
			self.cheminCourantSauv = self.repTampon+'image_visu_redim_'+string.zfill(1, 6)+'.jpg'
				
			# Ouverture des images. Conversion et sauvegarde
			obImg = Image.open(self.listeChemin[0])
			
			# Redimensionnement et sauvegarde des images
			finRedimSansRatio=obImg.resize((int(spin1), int(spin2)), Image.ANTIALIAS).save(self.cheminCourantSauv)
			
			# Affichage de l'image temporaire 
			# Ouverture d'une boite de dialogue affichant l'aperçu.
			#
			# Affichage par le bouton Voir le résultat
			visio = VisionneurEvolue(self.cheminCourantSauv)
			visio.redimenFenetre(self.mainWindowFrameGeometry, 1., 0.7)
			visio.exec_()
		
			return 0
		
	
	def appliquer(self):
		"""Conversion des images"""
		
		# La liste pour l'affichage des images ds l'interface est
		# vidée pour que les images affichées ne s'amoncellent pas
		# si plusieurs rendus à la suite
		self.listeImgDestin=[]

		#print "Indice de l'image à encoder :", self.j
		EkdPrint(u"Indice de l'image à encoder: %s" % 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+self.spin1_ImTrait.value(), self.spin2_ImTrait.value())+self.ext
				
				# Ouverture des images. Conversion et sauvegarde
				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+self.spin1_ImTrait.value(), self.spin2_ImTrait.value())+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
				
		# Affichage des images après traitement
		#
		# Changement d'onglet et fonctions associées
		self.conversionImg = 1
		self.metaFctTab(self.indexTabImgDestin)
			
			
	def afficherAide(self):
		"""Boîte de dialogue de l'aide"""
		
		# Nouvelle fenêtre d'aide
		messageAide=EkdAide(parent=self)
		messageAide.setText(tr(u"<p><b>Vous pouvez ici changer la résolution (c'est à dire la taille, plus précisément la largeur et la hauteur) des images.</b></p><p><b>Dans l'onglet 'Réglages' il est possible de redimensionner les images en tenant compte du ratio, c'est à dire des proportions de chaque image (là on ne donne que la largeur, EKD se changeant de calculer la hauteur ; cela permet de ne pas déformer les images lors du changement de résolution), mais aussi de définir soi-même la taille de chaque image (en changeant à la fois la largeur et la hauteur).</b></p><p>Dans l'onglet <b>'Image(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher vos image(s). Si vous voulez sélectionner plusieurs images d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos images), cliquez sur <b>Ajouter</b>.</p><p>Dans l'onglet <b>'Réglages'</b> faites les réglages du <b>'Traitement à partir de l'image (numéro)'</b> et du <b>'Nombre de chiffres après le nom de l'image' <font color='red'>(la plupart du temps les valeurs par défaut suffisent)</font></b>, ensuite sélectionnez votre <b>'Type'</b> (<b>Redimensionner en tenant compte des proportions</b> ou <b>Redimensionner sans tenir compte des proportions</b>), faites les réglages par rapport au <b>'Type'</b> choisi (vous pouvez voir en temps réel la taille de la première image du lot changer à mesure que vous réglez la <b>nouvelle largeur</b> et/ou la <b>nouvelle hauteur</b>).</p><p>En complément, vous pouvez cliquer sur le bouton <b>'Voir le résultat'</b>, vous voyez à ce moment là le résultat de vos réglages s'afficher dans une nouvelle fenêtre (particulièrement utile pour voir les déformations dues au redimensionnement pour <b>Redimensionner sans tenir compte des proportions</b>).</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer et sauver'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b>.</p><p>Si vous faites un clic droit de la souris (sur l'image) dans l'onglet <b>'Images après traitement'</b>, vous accédez à des paramètres vous permettant différents affichages de la dite image. De même dans cet onglet vous pouvez lancer la visualisation des images par le bouton <b>Lancer le diaporama</b> (le bouton violet avec une flèche blanche vers la droite).</p><p>L'onglet <b>'Infos'</b> vous permet de voir les image(s) chargée(s), les image(s) convertie(s) et la nouvelle résolution des images.</p>"))
		messageAide.show()


	def save(self) :
		self.afficheurImgSource.saveFileLocation(self.idSection)
		EkdConfig.set(self.idSection, u'spin1_ImTrait', unicode(self.spin1_ImTrait.value()))
		EkdConfig.set(self.idSection, u'spin2_ImTrait', unicode(self.spin2_ImTrait.value()))
		EkdConfig.set(self.idSection, u'stacked_redim_avec_ratio', unicode(self.stacked_redim_avec_ratio.spin.value()))
		EkdConfig.set(self.idSection, u'stacked_redim_sans_ratio1', unicode(self.stacked_redim_sans_ratio.spin.value()))
		EkdConfig.set(self.idSection, u'stacked_redim_sans_ratio2', unicode(self.stacked_redim_sans_ratio.spin2.value()))
		EkdConfig.set(self.idSection, u'type_redim', unicode(self.comboReglage.currentIndex()))


	def load(self) :
		self.afficheurImgSource.loadFileLocation(self.idSection)
		self.spin1_ImTrait.setValue(int(EkdConfig.get(self.idSection, 'spin1_ImTrait')))
		self.spin2_ImTrait.setValue(int(EkdConfig.get(self.idSection, 'spin2_ImTrait')))
		self.stacked_redim_avec_ratio.spin.setValue(int(EkdConfig.get(self.idSection, 'stacked_redim_avec_ratio')))	
		self.stacked_redim_sans_ratio.spin.setValue(int(EkdConfig.get(self.idSection, 'stacked_redim_sans_ratio1')))	
		self.stacked_redim_sans_ratio.spin2.setValue(int(EkdConfig.get(self.idSection, 'stacked_redim_sans_ratio2')))	
		self.comboReglage.setCurrentIndex(int(EkdConfig.get(self.idSection, 'type_redim')))
Ejemplo n.º 12
0
class Animation_TagsVideo(Base) :
	"Cadre de Animation -> Encodage -> Gestion AVCHD. But: encodages spécifiques au départ du format AVCHD haute définition (Caméra digitale HD)"

	def __init__(self):

		#=== Variable de configuration ===#
		self.config=EkdConfig
		# Identifiant de la classe
		self.idSection = "animation_tag_video" # idSection à modifier lorsqu'il y aura des données dans la config.

		super(Animation_TagsVideo, self).__init__('hbox', titre=_(u"Tags Vidéo"))
		
		self.printSection()

		# Customisation de l'interface
		# 1. Onglet sources
		self.afficheurVideoSource=SelectWidget(extensions = ["*.avi", "*.mp4", "*.mpg", "*.mpeg", "*.flv"], mode="texte", video = True)
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"), self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)

		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "video" # Défini le type de fichier source.
		self.typeSortie = "video" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurVideoSource # Fait le lien avec le sélecteur de fichier source.


		# 2. Onglet réglages
		
		groupReglage = QGroupBox()
		self.layoutReglage = QVBoxLayout(groupReglage)
		
		self.ligne = {}
                # Présentation ------------------------------------
		layoutPres = QHBoxLayout()
                titre = QLabel("<h2>" + _(u"Tags contenus dans la vidéo :") + "</h2>")
		layoutPres.addWidget(titre)
		self.layoutReglage.addLayout(layoutPres)
                # Les Tags ----------------------------------------
		self.layoutTags = QVBoxLayout()
                # Un bouton d'ajout et de suppression de tags
                self.layoutButton = QHBoxLayout()
                self.buttonAdd = QPushButton(_(u"Ajouter un tag"))
                self.buttonDel = QPushButton(_(u"Supprimer un tag"))
                self.labelWarn = QLabel(_(u"Veuillez charger au moins un fichier."))
                # Menu déroulant pour chaque pour chaque bouton
                self.menuDel = QMenu()
                self.buttonDel.setMenu(self.menuDel)
                self.menuAdd = QMenu()
                self.buttonAdd.setMenu(self.menuAdd)

		self.connect(self.buttonAdd, SIGNAL("clicked()"), self.buttonAdd.showMenu)
		#self.connect(self.buttonAdd, SIGNAL("clicked()"), self.showAddMenu)
		self.connect(self.buttonDel, SIGNAL("clicked()"), self.buttonDel.showMenu)
		#self.connect(self.buttonDel, SIGNAL("clicked()"), self.showDelMenu)
		self.connect(self.menuAdd, SIGNAL("aboutToHide()"), self.setToAdd)
		self.connect(self.menuDel, SIGNAL("aboutToHide()"), self.setToDel)

                self.layoutButton.addWidget(self.buttonAdd)
                self.layoutButton.addWidget(self.buttonDel)
                self.layoutButton.addWidget(self.labelWarn)
                ## Par défaut, on ne voit pas les boutons, ils seront activé si un fichier est chargé
                self.buttonAdd.hide()
                self.buttonDel.hide()
                self.labelWarn.show()
                self.layoutButton.addStretch()

		self.layoutReglage.addLayout(self.layoutTags)
		self.layoutReglage.addLayout(self.layoutButton)
		self.layoutReglage.addStretch()

		## On charge les options depuis EkdConfig
		self.loadOptions()
		
		##
		self.add(groupReglage, _(u"Réglages"))

		# 4 Onglet info.
		self.addLog()
		
        def setToAdd(self):
                self.toAdd = self.menuAdd.activeAction()
        def setToDel(self):
                self.toDel = self.menuDel.activeAction()

        def addTagsEdit(self, label, value):
                """Fonction d'ajout d'un champ de modification d'un tag"""
		layoutTag = QHBoxLayout()
		#
		lab = QLabel(_(u"%s :"% label))
                lab.setFixedWidth(90)
		layoutTag.addWidget(lab)
		#
                num = len(self.ligne)
		self.ligne[label] = QLineEdit()
		self.ligne[label].setMaxLength(80)
		self.ligne[label].setText(value)
		self.ligne[label].setToolTip(_(u"Vous n'avez droit ici qu'à maximum 80 caractères. <b>Au delà de cette taille, plus aucun caractère ne s'affichera !</b>"))
		layoutTag.addWidget(self.ligne[label])
		# 
		self.layoutTags.addLayout(layoutTag)
		#
		#self.connect(self.ligne[label], SIGNAL("textChanged(const QString&)"), self.updateLigne)
		self.connect(self.ligne[label], SIGNAL("editingFinished()"), self.updateLigne)
		
        def updateLigne(self):
                for k in self.tags.get_tags().keys() :
                    self.tags.setTag(k, self.ligne[k].text())


        def clearTags(self):
                """Supprimme tous les Tags"""
                def deleteItems(layout):
                    if layout is not None:
                        while layout.count():
                            item = layout.takeAt(0)
                            widget = item.widget()
                            if widget is not None:
                                widget.deleteLater()
                            else:
                                deleteItems(item.layout())
                deleteItems(self.layoutTags)

        def showAddMenu(self):
                #print "Visu du menu Add"
                self.buttonAdd.showMenu()

        def showDelMenu(self):
                #print "Visu du menu Del"
                self.buttonDel.showMenu()

        def tagDel(self):
                #print "Deleting %s" % self.toDel.text()
                self.clearTags()
                self.tags.delTag("%s" % self.toDel.text())
                self.refreshTags()

        def tagAdd(self):
                #print "Adding %s" % self.toAdd.text()
                self.clearTags()
                self.tags.addTag("%s" % self.toAdd.text(), "Exemple")
                self.refreshTags()

        def refreshTags(self):
                self.menuDel.clear()
                self.menuAdd.clear()
                for tag in self.tags.get_tags().keys() :
                    ## On ajoute cette condition si on veut restreindre
                    ## la visualisation des tags à seulement ceux gérés
                    ## par mencoder.
                    ## Sans cette condition, tous les tags sont affichés
                    if tag in Tags.DEFAULT_TAGS:
                        self.addTagsEdit(tag, self.tags[tag])
                        self.menuDel.addAction("%s" % tag, self.tagDel)
                for t in Tags.DEFAULT_TAGS :
                    if not t in self.tags.get_tags().keys() :
                        self.menuAdd.addAction("%s" % t, self.tagAdd)

	def getFile(self):
	
		# Choisir getFile à la place de getFiles
		#self.chemin = self.afficheurVideoSource.getFiles()
		
		# Chemin de la vidéo sélectionnée
		self.chemin = self.afficheurVideoSource.getFile()

                if self.chemin :
                    self.buttonAdd.show()
                    self.buttonDel.show()
                    self.labelWarn.hide()
                else:
                    ## Par défaut, on ne voit pas les boutons, ils seront activé si un fichier est chargé
                    self.buttonAdd.hide()
                    self.buttonDel.hide()
                    self.labelWarn.show()

		#print 'Chemin', self.chemin
		EkdPrint('Chemin ' + self.chemin)
		
                # Clear all TagsEdit
                self.clearTags()

		# A partir du moment où au moins une vidéo a 
		# été chargée, le bouton Appliquer devient actif
		self.boutApp.setEnabled(True)

                self.moteurInfosTags()
                #print 'Tags : %s' % self.tags
                self.refreshTags()

		#self.emit(SIGNAL("loaded"))
		return self.chemin
		
	
	def moteurInfosTags(self):
	
		# Chemin de la vidéo sélectionnée
		self.chemin = self.afficheurVideoSource.getFile()
		
                self.tags = Tags(self.chemin)

		

	def appliquer(self, nomSortie=None, ouvert=1):
		"Appel du moteur du cadre"
		
		if not nomSortie:
			#chemin = self.getFile()
			chemin = self.chemin
			
			self.suffix_sortie = os.path.splitext(chemin)[1]
			
			# Suffix du codec actif
                        saveDialog = EkdSaveDialog(self, mode="video", suffix=self.suffix_sortie, title=_(u"Sauver"))
			self.cheminFichierEnregistrerVideo = saveDialog.getFile()
			
		else: # module séquentiel
			self.cheminFichierEnregistrerVideo = nomSortie

		if not self.cheminFichierEnregistrerVideo:
			return
		
		# Appel de la classe
#		try:
                mencoder = WidgetMEncoder('tag_video', chemin, self.cheminFichierEnregistrerVideo, valeurNum = self.tags.get_tags(), laisserOuvert=ouvert)
                mencoder.setWindowTitle(_(u"Créer des tags vidéo"))
                mencoder.exec_()
#		except:
#			messageErrTagVid=QMessageBox(self)
#			messageErrTagVid.setText(_(u"Un problème est survenu lors de l'exécution de Mencoder"))
#			messageErrTagVid.setWindowTitle(_(u"Erreur"))
#			messageErrTagVid.setIcon(QMessageBox.Warning)
#			messageErrTagVid.exec_()
#			return

		### Information à l'utilisateur
		self.lstFichiersSortie = self.cheminFichierEnregistrerVideo
		self.infoLog(None, chemin, None, self.cheminFichierEnregistrerVideo)
	

	def afficherAide(self):
		"Boîte de dialogue de l'aide du cadre"

		super( Animation_TagsVideo,self).afficherAide(_(u"<p><b>Vous pouvez ici apposer des tags vidéo. Imaginez vous ayez une création vidéo et que vous voulez la mettre sur internet et en même temps vous voulez bien spécifier (et non par une signature ou par un logo en bas de la vidéo) que c'est bien la votre (par exemple par un copyright), ... et bien c'est possible, c'est là qu'interviennent les tags vidéo.</b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>'Réglages'</b> sélectionnez les entrées (c'est à dire les tags) dans la liste déroulante <b>'Ajouter un tag'</b> (c'est à dire Title, Artist, Genre, Subject, Copyright et Comments), une fois les entrées ajoutées renseignez les différents tags dans les champs de texte correspondants.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps du traitement.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos chargées (avec leurs chemins exacts) avant et après conversion.</p>"))


	def saveFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.saveFileLocation(self.idSection)


	def loadFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.loadFileLocation(self.idSection)


	def loadOptions(self):
		'''
		# On charge les différentes variables necessaire au widget

		# Sert maintenant à imprimer les infos des tags
		'''
		#print "Pas d'options à afficher"
		EkdPrint(u"Pas d'option à afficher")


	def load(self):
		'''
		Chargement de la configuration de tous les objets
		'''
		self.loadFiles()


	def save(self):
		'''
		Sauvegarde de la configuration de tous les objets
		'''
		self.saveFiles()
Ejemplo n.º 13
0
class Animation_ConvertirAnimEn_16_9_Ou_4_3(Base):
	""" -----------------------------------------
	# Cadre accueillant les widgets de :
	# Animation >> Montage vidéo >> Vidéo seulement
	# -----------------------------------------"""

	def __init__(self, statusBar):

		# On joue avec l'héritage de Base
		vbox=QVBoxLayout()

		# Identifiant de la classe
		self.idSection = "animation_conversion_video_16_9_4_3"
		super(Animation_ConvertirAnimEn_16_9_Ou_4_3, self).__init__('vbox', titre=_(u"Conversion 16/9 - 4/3"))
		self.printSection()

		# Liste de fichiers initiaux (contenu dans le fichier de configuration)
		self.lstFichiersSource = []
		self.lstFichiersSourceProv = [] # idem mais provisoire (sert à récupérer les chemins dans l'ordre de sélection)

		self.statusBar = statusBar
		# -------------------------------------------------------------------
		# Boîte de groupe : "Fichier vidéo source"
		# -------------------------------------------------------------------
		self.afficheurVideoSource=SelectWidget(extensions = ["*.avi", "*.mpg", "*.mpeg", "*.vob"], mode="texte", video = True)
		###################################################################################################
		# Onglets
		self.indexVideoSource = self.add(self.afficheurVideoSource, _(u'Video(s) source'))
		self.connect(self.afficheurVideoSource,SIGNAL("fileSelected"),self.getFile)
		self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
		#--------------------------------------------------------------------

		groupReglage=QGroupBox()
		self.layoutReglage=QVBoxLayout(groupReglage)

		## ---------------------------------------------------------------------
		# Variables pour la fonction tampon
		## ---------------------------------------------------------------------
		self.typeEntree = "video" # Défini le type de fichier source.
		self.typeSortie = "video" # Défini le type de fichier de sortie.
		self.sourceEntrees = self.afficheurVideoSource # Fait le lien avec le sélecteur de fichier source.


		#=== Widget qui seront inclus dans la boite de réglage ===#
		# boite de combo
		self.combo=QComboBox() # le self sert à afficher des informations sur les items à partir de la fonction
		listeCombo=[	(_(u'16/9 ème --> rapport 1.77') ,'16_9'),\
				(_(u'16/10 ème --> rapport 1.60') ,'16_10'),\
				(_(u'4/3 --> rapport 1.33') ,'4_3'),\
				(_(u'5/3 --> rapport 1.66') ,'5_3'),\
				(_(u'Panoramique 1,85:1 --> rapport 1.85') ,'1_85'),\
				(_(u'Cinemascope Panavision 2,35:1 --> rapport 2.35') ,'2_35'),\
				(_(u'CinemaScope optique 2,39:1 --> rapport 2.39') ,'2_39'),\
				(_(u'CinemaScope magnétique 2,55:1 --> rapport 2.55') ,'2_55'),]
		##############################################################################################
		# Insertion des codecs de compression dans la combo box
		for i in listeCombo:
                	self.combo.addItem(i[0],QVariant(i[1]))

		self.connect(self.combo, SIGNAL("activated(int)"), self.nouvelleResolution)

		self.layoutReglage.addWidget(self.combo, 0, Qt.AlignHCenter)

		# Chargement du paramètre de configuration
		try:
			typ = EkdConfig.get(self.idSection,'type')
			indice = 0 # indice de la ligne de self.listeCombo correspondant au type d'ordre
			for i in listeCombo:
				if i[1]!=typ:
					indice += 1
				else:
					break
			self.combo.setCurrentIndex(indice)
		except:
			self.combo.setCurrentIndex(0)

		self.nouvelleResolution()
		self.add(groupReglage, _(u"Réglages"))
		self.addPreview()
		self.addLog()

	def getFile(self):
		'''
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		'''
		self.chemin = self.afficheurVideoSource.getFile()
		self.boutApp.setEnabled(True)

		self.mplayer.setEnabled(True)
		self.mplayer.setVideos([self.chemin])

		self.emit(SIGNAL("loaded"))
		return self.chemin


	def nouvelleResolution(self):
		"""Mets une liste de chemins vidéos dans l'ordre alpha-numérique si tel est le choix de l'utilisateur"""
		i=self.combo.currentIndex()
		idCombo = str(self.combo.itemData(i).toStringList()[0])
		#print "Combo :", idCombo
		EkdPrint(u"Combo : %s" % idCombo)
		# Si ordre alpha-numérique choisi, alors on ordonne la liste
		if idCombo == '16_9':
			self.idCombo=1.77777
		elif idCombo == '16_10':
			self.idCombo=1.60
		elif idCombo == '4_3':
			self.idCombo=1.33333
		elif idCombo == '5_3':
			self.idCombo=1.66666
		elif idCombo == '1_85':
			self.idCombo=1.85
		elif idCombo == '2_35':
			self.idCombo=2.35
		elif idCombo == '2_39':
			self.idCombo=2.39
		elif idCombo == '2_55':
			self.idCombo=2.55
		######################################################################################
		EkdConfig.set(self.idSection,'type',idCombo)
		######################################################################################


	def fctRadioSource(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: self.mplayer.listeVideos = self.lstFichiersSource


	def fctRadioConvert(self, bool=None):
		""""Communique la vidéo appropriée à mplayer"""
		if bool: self.mplayer.listeVideos = self.lstFichiersSortie


	def ouvrirSource(self, nomEntree=None):
		""" Récupération des chemins vidéo sélectionnée """

		# On n'utilise pas le fonction recupSources du fichier animation_base.py contenu dans
		# le répertoire gui_modules_animation car pour cette fonction uniquement les fichiers
		# AVI (donc extension .avi) sont autorisés
		try:
			repEntree = EkdConfig.get('general', 'video_input_path')
		except:
			repEntree = os.path.expanduser('~')
		####################################################################################

		txt = _(u"Fichiers vidéos")

		if not nomEntree:
			liste=QFileDialog.getOpenFileNames(None, _(u"Ouvrir"), repEntree,
				"%s (*.avi *.mpg *.mpeg *.vob)\n" %txt)

			self.liste = [unicode(i) for i in liste]
		####################################################################################

		chemin=self.liste
		if not chemin: return

		## On a récupérer la vidéo, on défini le nouveau chemin par défaut des vidéos
		EkdConfig.set('general', 'video_input_path', os.path.dirname(chemin[0]))

		self.lstFichiersSource = []

		for fichier in chemin:
			self.lstFichiersSource.append(fichier)

		# Appel de la fonction nouvelleResolution pour application 16/9 ou 4/3
		self.nouvelleResolution()
		#print 'self.idCombo', self.idCombo
		EkdPrint(u'self.idCombo %s' % self.idCombo)

		# Le bouton appliquer devient utilisable
		self.boutApp.setEnabled(True)
		self.boutApp.setToolTip("")

		self.mplayer.setEnabled(True)
		self.mplayer.listeVideos = self.lstFichiersSource
		self.radioSource.setChecked(True)
		self.radioSource.setEnabled(False)
		self.radioConvert.setEnabled(False)

		# Affichage du chemin + nom de fichier dans la ligne d'édition
		self.ligneEditionSource.setText(liste[0])

		self.statusBar.showMessage(_(u"La vidéo résultante ne pourra pas être lue avec tous les logiciels"))


	def afficherAide(self):
		""" Boîte de dialogue de l'aide """

		super(Animation_ConvertirAnimEn_16_9_Ou_4_3,self).afficherAide(_(u"<p><b>Vous pouvez ici changer les proportions des images dans une vidéo (en quelque sorte les dimensions d'ensemble de la vidéo). Selon la définition de Wikipédia: 'Le format de projection cinématographique définit le rapport entre la largeur et la hauteur de l'image projetée'. Source: http://fr.wikipedia.org/wiki/Format_de_projection<b></p><p><b>Les options proposées ici sont: 16/9ème, 16/10ème, 4/3, 5/3, 1:85, 2:35, 2:39 et 2:55.<b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Dans l'onglet <b>'Réglages'</b> faites votre choix dans la liste déroulante.</p><p>Une fois tout ceci fait, cliquez sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion. A la fin cliquez sur le bouton <b>'Voir les informations d'encodage'</b> et fermez cette dernière fenêtre après avoir vu les informations en question.</p><p>Dans l'onglet <b>'Visionner vidéo'</b> vous pouvez visionner le résultat (avant la conversion) en sélectionnant <b>'vidéo(s) source(s)'</b>, après la conversion <b>'vidéo convertie'</b> ou bien encore les deux en même temps, en cliquant sur le bouton <b>'Comparateur de vidéos'</b>.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos chargées (avec leurs chemins exacts) avant et après conversion.</p>"))


	def appliquer(self, nomSortie=None, ouvert=1):
		"""Fusion de vidéos"""

		#=== Récupération de la liste de fichiers ===#
		chemin=unicode(self.getFile())
		if not nomSortie:
			# suffix du fichier actif
			suffix=os.path.splitext(chemin)[1]
			# suffix du codec actif
                        saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver"))

			cheminFichierEnregistrerVideo = saveDialog.getFile()
		else: # module séquentiel
			cheminFichierEnregistrerVideo = nomSortie
		if not cheminFichierEnregistrerVideo:
			return
		#####################################################################################################
		from moteur_modules_animation.mplayer import getVideoSize
		(videoLargeur, videoHauteur) = getVideoSize(chemin)
		#print videoLargeur, videoHauteur
		EkdPrint(u"%s %s" % (videoLargeur, videoHauteur))
		# ---------------------------------------------------------------------------------------------
		#####################################################################################################

		#######Détection/redimensionnement de la taille de la vidéo #################
		# --> Car FFmpeg ne peut traitre que des vidéo de résolution paire
		#videoLargeur = float(videoLargeur)
		videoHauteur = float(videoLargeur) / float(self.idCombo) # Changement fait le 25/03/11
		videoLargeur = int(videoLargeur)
		videoHauteur = int(videoHauteur) # Changement fait le 25/03/11

		# Si dimension largeur impaire
		if int(videoLargeur) % 2 == 1 and int(videoHauteur) % 2 == 0:
			videoLargeur = videoLargeur - 1
			videoHauteur = videoHauteur
			tailleVideo = [str(videoLargeur), str(videoHauteur)]
			#print tailleVideo, type(tailleVideo)
			EkdPrint(u"%s %s" % (tailleVideo, type(tailleVideo)))
		# Si dimension hauteur impaire
		if int(videoHauteur) % 2 == 1 and int(videoLargeur) % 2 == 0:
			videoLargeur = videoLargeur
			videoHauteur = videoHauteur - 1
			tailleVideo = [str(videoLargeur), str(videoHauteur)]
			#print tailleVideo, type(tailleVideo)
			EkdPrint(u"%s %s" % (tailleVideo, type(tailleVideo)))
		# Si les deux dimensions (largeur et hauteur) sont impaires
		if int(videoLargeur) % 2 == 1 and int(videoHauteur) % 2 == 1:
			videoLargeur = videoLargeur - 1
			videoHauteur = videoHauteur - 1
			tailleVideo = [str(videoLargeur), str(videoHauteur)]
			#print tailleVideo, type(tailleVideo)
			EkdPrint(u"%s %s" % (tailleVideo, type(tailleVideo)))
		# Si les deux dimensions sont paires
		if int(videoLargeur) % 2 == 0 and int(videoHauteur) % 2 == 0:
			videoLargeur = videoLargeur
			videoHauteur = videoHauteur
			tailleVideo = [str(videoLargeur), str(videoHauteur)]
			#print tailleVideo, type(tailleVideo)
			EkdPrint(u"%s %s" % (tailleVideo, type(tailleVideo)))
			
		#print 'self.idCombo', self.idCombo
		EkdPrint(u'self.idCombo %s' % self.idCombo)
		#####################################################################################################

		try:
			ffmpeg = WidgetFFmpeg('conv_en_16_9_ou_4_3', chemin, cheminFichierEnregistrerVideo, self.idCombo, laisserOuvert=ouvert, tailleVideo=tailleVideo)
			ffmpeg.setWindowTitle(_(u"Conversion de vidéos en 16/9 ou 4/3"))
			ffmpeg.exec_()
			#############################################################################################

		except None:
			messageErrAnEnc=QMessageBox(self)
			messageErrAnEnc.setText(_(u"Problème lors de la conversion (FFmpeg)"))
			messageErrAnEnc.setWindowTitle(_(u"Erreur"))
			messageErrAnEnc.setIcon(QMessageBox.Warning)
			messageErrAnEnc.exec_()
			return

		self.lstFichiersSortie = cheminFichierEnregistrerVideo # pour la boite de dialogue de comparaison
		self.radioConvert.setChecked(True)
		self.radioSource.setEnabled(True)
		self.radioSource.setChecked(False)
		self.radioConvert.setEnabled(True)
		self.boutCompare.setEnabled(True)
		#########################################
		### Information à l'utilisateur
		self.infoLog(None, chemin, None, cheminFichierEnregistrerVideo)

		return self.lstFichiersSortie # module séquentiel
		###############################################################


	def sequentiel(self, entree, sortie, ouvert=0):
		"""Utile dans le module du même nom. Applique les opérations de la classe. Retourne le vrai nom du fichier de sortie"""
		self.ouvrirSource(entree)
		return self.appliquer(sortie, ouvert)


	def sequentielReglage(self):
		"""Utile dans le module du même nom. Récupère le widget de réglage associé à l'identifiant donné en 1er argument. Retourne l'instance du widget de réglage"""
		return self.groupReglage

	def saveFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.saveFileLocation(self.idSection)

	def loadFiles(self):
		'''
		# On sauvegarde la liste des fichiers chargés
		'''
		self.afficheurVideoSource.loadFileLocation(self.idSection)

	def load(self):
		'''
		Chargement de la configuration de tous les objets
		'''
		self.loadFiles()

	def save(self):
		'''
		Sauvegarde de la configuration de tous les objets
		'''
		self.saveFiles()
Ejemplo n.º 14
0
class Animation_ConvertirUneAnimEnImg(Base):
    # ----------------------------------------------------------------------------------------
    # Cadre accueillant les widgets de : Animation >> Séparer le flux vidéo et le flux audio
    # ----------------------------------------------------------------------------------------

    def __init__(self, statusBar, geometry):
        # -------------------------------
        # Parametres généraux du widget
        # -------------------------------

        # === Variable de configuration ===#
        self.config = EkdConfig

        self.baseImg = BaseImg()  # module de image
        # Fonction appelant la fenêtre principale
        self.mainWindowFrameGeometry = geometry

        # === tout sera mis dans une boîte verticale ===#
        vbox = QVBoxLayout()

        # === Identifiant de la classe ===#
        self.idSection = "animation_convertir_une_video_en_images"

        super(Animation_ConvertirUneAnimEnImg, self).__init__(titre=_(u"Conversion d'une vidéo en images"))
        self.printSection()

        ######## Gestion de la nouvelle interface de chargement #######
        ## -------------------------------------------------------------------
        ## on utilise le selecteur d'image pour les vidéos
        from gui_modules_image.selectWidget import SelectWidget

        # Là où on naviguera entre les fichiers
        self.afficheurVideoSource = SelectWidget(
            extensions=["*.avi", "*.mpg", "*.mpeg", "*.mjpeg", "*.flv", "*.mp4", "*.h264", "*.dv", "*.vob"],
            mode="texte",
            video=True,
        )
        ###################################################################################
        # Onglets
        self.indexVideoSource = self.add(self.afficheurVideoSource, _(u"Video(s) source"))
        self.connect(self.afficheurVideoSource, SIGNAL("fileSelected"), self.getFile)
        self.connect(self.afficheurVideoSource, SIGNAL("pictureChanged(int)"), self.getFile)
        ## -------------------------------------------------------------------

        ## ---------------------------------------------------------------------
        # Variables pour la fonction tampon
        ## ---------------------------------------------------------------------
        self.typeEntree = "video"  # Défini le type de fichier source.
        self.typeSortie = "image"  # Défini le type de fichier de sortie.
        self.sourceEntrees = self.afficheurVideoSource  # Fait le lien avec le sélecteur de fichier source.

        # === Drapeaux ===#
        # Une conversion (même partielle) a-t-elle eu lieu après le chargement des images? (1: vrai)

        # Est-ce que des images ont été converties et qu'elles n'ont pas encore été montrées?
        # Marche aussi quand la conversion a été arrêté avant la fin de la 1ère image
        self.conversionImg = 0

        # === Accès à la barre des tâches ===#
        # On ne passe pas tout le parent, mais juste ce dont on a besoin
        self.statusBar = statusBar

        # -------------------------------------------------------------------
        # Boîte de groupe : "Fichier vidéo source"
        # -------------------------------------------------------------------
        self.afficheurImgDestination = Lecture_VisionImage(self.statusBar)
        #####################################################################################################

        self.listeImgDestin = []

        # ----------
        # Onglets
        # ----------
        self.dicoTab = {}

        self.add(self.afficheurImgDestination, _(u"Visualisation"))

        self.addLog()

    def fctTab(self):
        "Affichage d'une ou plusieurs images converties"

        # Cela ne concerne que l'onglet de visualisation des images après leur conversion
        # Affichage si on sauvegarde par le bouton Appliquer et sauver
        # print "La conversion vient d'avoir lieu -> affichage des images du lot de destination"
        EkdPrint(u"La conversion vient d'avoir lieu -> affichage des images du lot de destination")
        cheminImages = os.path.dirname(self.listeImgDestin[0])
        liste = []
        for fichier in self.listeImgDestin:
            liste.append(os.path.basename(fichier))
        self.afficheurImgDestination.updateImages(liste, cheminImages)

        self.conversionImg = 0

        ####################################################################
        # Car erreur: Traceback (most recent call last): ... KeyError: 'images'
        # Affichage (après conversion) de l'onglet contenant les images
        ####################################################################

        # On libere la memoire
        del cheminImages, liste, fichier

    def ouvrirSource(self, nomEntree=None):
        """Récupération du chemin de la vidéo sélectionnée et activation de certains widgets"""

        chemin = self.recupSource(nomEntree)

        if not chemin:
            return
        self.ligneEditionSource.setText(chemin)

        self.boutApp.setEnabled(True)

        self.mplayer.setEnabled(True)
        self.mplayer.listeVideos = [chemin]

        self.tab.setCurrentIndex(self.dicoTab["video"])

        # On libere la memoire
        del chemin

    def getFile(self):
        """
		# On utilise la nouvelle interface de récupération des vidéos
		Récupération de la vidéo source selectionnée
		"""
        self.chemin = self.afficheurVideoSource.getFile()
        self.boutApp.setEnabled(True)

        self.boutApp.setEnabled(True)
        if self.idSection == "animation_filtresvideo":
            self.boutApercu.setEnabled(True)
            self.filtreDecouper.setButtonEnabled(True)
            # On emet un signal quand le fichier est chargé
        self.emit(SIGNAL("loaded"))
        return self.chemin
        ###################################################################################

    def appliquer(self, nomSortie=None, ouvert=1):
        """ Appelle la boite de dialogue de sélection de fichier à sauver et appel de la fonction de conversion d'une vidéo en images """

        # === Détermination des chemins d'entrée et sortie ===#
        chemin = self.getFile()

        if not nomSortie:
            suffix = ""
            saveDialog = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=True)
            cheminFichierEnregistrerVideo = saveDialog.getFile()

        else:  # module séquentiel
            cheminFichierEnregistrerVideo = nomSortie

        if not cheminFichierEnregistrerVideo:
            return
        ###################################################################################

        # Préfixe des images
        prefixeImg = os.path.basename(cheminFichierEnregistrerVideo)

        # Chemin du répertoire de sortie des images
        cheminRepDestination = (
            os.path.dirname(cheminFichierEnregistrerVideo) + os.sep + os.path.basename(cheminFichierEnregistrerVideo)
        )

        cheminVideoSource = unicode(self.getFile())
        ###################################################################################
        try:
            #### la conversion de vidéo en images est maintenant gérée par FFmpeg #####
            ffmpeg = WidgetFFmpeg("jpeg", cheminVideoSource, cheminRepDestination, laisserOuvert=ouvert)
            ffmpeg.setWindowTitle(_(u"Convertir une animation en images"))
            ffmpeg.exec_()
            ##################################################################################################

        except:
            messageErreur = QMessageBox(self)
            messageErreur.setText(_(u"Problème lors de la conversion d'une vidéo en images (ffmpeg) %s") % e)
            messageErreur.setWindowTitle(_(u"Erreur"))
            messageErreur.setIcon(QMessageBox.Warning)
            messageErreur.exec_()
            return

        imgDep = glob.glob(cheminRepDestination + "*.png")

        imgDep.sort()

        # Nettoyage ... insertion des images dans la liste de visualisation
        self.listeImgDestin = []
        for parc_1 in imgDep:
            # Condition pour détection windows
            if os.name == "nt":
                if "\\" in parc_1:
                    self.listeImgDestin.append(parc_1.replace("\\", "/"))
            # Condition pour détection Linux ou MacOSX
            elif os.name in ["posix", "mac"]:
                self.listeImgDestin.append(parc_1)

                # Affichage des images après traitement (appel de la fonction fctTab)
        self.conversionImg = 1
        self.fctTab()
        ### Information à l'utilisateur
        self.infoLog(None, cheminVideoSource, None, self.listeImgDestin)

        return self.listeImgDestin  # module séquentiel

        # ATTENTION IL FAUT AJOUTER CECI NE PAS OUBLIER !!!
        # La liste pour l'affichage des images ds l'interface est
        # vidée pour que les images affichées ne s'amoncellent pas
        # si plusieurs rendus à la suite
        self.listeImgDestin = []

        # On libere la memoire
        del nomSortie, rep, chemin, prefixeImg, cheminRepDestination, cheminRepDestination, cheminVideoSource, commande, mencoder, listeFichiers, nomFichier

    def sequentiel(self, entree, sortie, ouvert=0):
        """Utile dans le module du même nom. Applique les opérations de la classe. Retourne le vrai nom du fichier de sortie"""
        self.ouvrirSource(entree)
        return self.appliquer(sortie, ouvert)

    def sequentielReglage(self):
        """Utile dans le module du même nom. Récupère le widget de réglage associé à l'identifiant donné en 1er argument. Retourne l'instance du widget de réglage"""
        groupReglage = QGroupBox(_(u"Réglages: convertir une animation en images"))
        hbox = QHBoxLayout(groupReglage)
        hbox.addWidget(QLabel(_(u"<center>Pas de réglages ici</center>")))
        return groupReglage

    def afficherAide(self):
        """ Boîte de dialogue de l'aide """
        super(Animation_ConvertirUneAnimEnImg, self).afficherAide(
            _(
                u"<p><b>Qu’est-ce qu’une vidéo en fin de compte ?. La réponse est des images se succédant à une certaine fréquence (24 images par seconde, par exemple), que nous interprétons comme un mouvement, c’est-à-dire une image animée. Traiter une vidéo dans EKD, c’est donner à cette vidéo la possibilité de se séparer en images (le nombre d'images contenues ici sera la durée de la vidéo en secondes multiplié par son nombre d'images par seconde). C'est ce que se propose de faire cette fonction.</b></p><p>Dans l'onglet <b>'Vidéo(s) source'</b> cliquez sur le bouton <b>Ajouter</b>, une boîte de dialogue apparaît, sur la partie gauche sélectionnez le répertoire (au besoin dépliez les sous-répertoires), allez chercher la/les vidéo(s). Si vous voulez sélectionner plusieurs vidéos d'un coup, maintenez la touche <b>CTRL</b> (ou <b>SHIFT</b>) du clavier enfoncée (tout en sélectionnant vos vidéos), cliquez sur <b>Ajouter</b>.</p><p>Vous pouvez dès lors sélectionner une vidéo dans la liste et la visionner (par le bouton juste à la droite de cette liste), vous noterez que vous pouvez visionner la vidéo en quatre tiers, en seize neuvième ou avec les proportions d'origine de la vidéo (w;h). De même si vous le désirez, vous pouvez obtenir des informations complètes sur la vidéo sélectionnée, et ce par le bouton <b>'Infos'</b> (en bas).</p><p>Cliquez ensuite sur le bouton <b>'Appliquer'</b>, sélectionnez le répertoire de sauvegarde, indiquez votre <b>'Nom de fichier'</b>, cliquez sur le bouton <b>'Enregistrer'</b> et attendez le temps de la conversion. A la fin cliquez sur le bouton <b>'Voir les informations d'encodage'</b> et fermez cette dernière fenêtre après avoir vu les informations en question.</p><p>Vous pouvez voir le résultat de la conversion en images dans l'onglet <b>Visualisation</b>. Dans cet onglet vous pouvez faire défiler les images par le bouton <b>Lancer le diaporama</b> (le bouton violet avec une flèche blanche vers la droite). Si vous faites un clic droit de la souris sur une des images résultantes, vous accédez à des paramètres vous permettant différents affichages de la dite image.</p><p>L'onglet <b>'Infos'</b> vous permet de voir les vidéos chargées (avec leurs chemins exacts) avant et après conversion.</p>"
            )
        )

    def saveFiles(self):
        """
		# On sauvegarde la liste des fichiers chargés
		"""
        self.afficheurVideoSource.saveFileLocation(self.idSection)

    def loadFiles(self):
        """
		# On sauvegarde la liste des fichiers chargés
		"""
        self.afficheurVideoSource.loadFileLocation(self.idSection)

    def load(self):
        """
		Chargement de la configuration de tous les objets
		"""
        self.loadFiles()

    def save(self):
        """
		Sauvegarde de la configuration de tous les objets
		"""
        self.saveFiles()