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 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 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 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 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 appliquer(self) : """ Démarrage du processus d'encodage des images en diaporama vidéo""" format=self.vid_widg.data.getElementsByTagName('outputf')[0].childNodes[0].nodeValue filter=u"."+self.vid_widg.verifsformats[5][int(format)][2] print "DEBUG : Format du fichier : ", format, "Filter : ", filter # XXX Check that the output directory is correctly set. saveDialog = EkdSaveDialog(self, mode="video", suffix=filter, title=_(u"Sauver")) self.outputfile = saveDialog.getFile() if not self.outputfile : return self.vid_widg.updatexml(self.vid_widg.data,"outputfile",self.outputfile) self.vid_widg.process() self.connect(self.vid_widg.prc, SIGNAL("resProcess"), self.activideo)
def appliquer(self): """ appelle la boite de dialogue de sélection de fichier à sauver """ #=== Détermination des chemins d'entrée et sortie ===# # récupération du chemin du fichier audio de destination (à l'exception de l'extension) suffixSortie = u"."+self.selectionFile.getFileExt() saveDialog = EkdSaveDialog(self, mode="audio", suffix=suffixSortie, title=_(u"Sauver")) self.cheminAudioSorti = saveDialog.getFile() if not self.cheminAudioSorti: return # récupération du nombre de fichier audio à joindre nfile = self.selectionFile.getNumFile() # Encodage self.encodAudioWav(nfile, self.selectionFile.getListFile(), self.cheminAudioSorti)
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 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 appliquer(self): """Appliquer le renommage des images""" self.a1='###########################\n' self.b1=_('Fichiers avant renommage')+':\n' self.c1='###########################\n\n' # Récupération de la liste des fichiers chargés self.listeImgSource=self.afficheurImgSource.getFiles() # Nombre d'éléments présents dans la liste nbreElem=len(self.listeImgSource) # Copie par une boucle des images chargées par l'utilisateur dans le rep. # tampon --> shutil.copy(...) (on ne va pas utiliser # os.rename(source, destination) mais shutil.move(source, destination)) # car avec os.rename les fichiers source sont détruits, ce qui est une très # mauvaise chose. De plus si on utilise os.rename on obtient l'erreur: OSError: # [Errno 18] Lien croisé invalide # for n in range(nbreElem): shutil.copy(self.listeImgSource[n], self.repTampon) # On récupère le nom des fichiers chargés (seulement le nom, pas le chemin) # --> là il faut absolument garder les noms des fichiers exactement dans # l'ordre dans lequel ils ont été chargés. On ne peut pas utiliser glob, ni # os.listdir(...) car ils rangent dans un ordre qui n'est pas le bon # (certainement car ils utilisent à la base un dictionnaire). l_pre_imgSource = [os.path.basename(fich) for fich in self.listeImgSource] # On concatène le chemin (juste le chemin) du rep. tampon avec les fichiers l_imgSource = [self.repTampon+add for add in l_pre_imgSource] # Récup du classement choisi par l'utilisateur (par la QComboBox) i = self.comboClassement.currentIndex() classemt = self.comboClassement.itemData(i).toString() if classemt == 'ord_select': pass elif classemt == 'ord_apha_num': l_imgSource.sort() else: l_imgSource.sort() listeExif = [] for fichier in l_imgSource: # Sélection du chemin (sans extension) et de l'extension elle-même fich, ext = os.path.splitext(fichier) # Condition de sélection de l'extension pour EXIF et ouverture image try: if ext in [".jpg", ".JPG", ".jpeg", ".JPEG"]: imgExif = Image.open(fichier) else: # Si l'utilisateur charge des images avec des extensions autres que ... erreur_0 = QMessageBox.critical(self,_(u"Par ordre de prise de vue (données Exif)"),_(u"<p><b>Vous ne pouvez charger que des images avec des extensions jpg, JPG, jpeg ou JPEG (!).</b></p>"), QMessageBox.Yes) if erreur_0 == QMessageBox.Yes: return # Données brutes contenues dans le dictionnaire exifdata = imgExif._getexif() # Certaines images délivrent None à _getexif(), dans ce/ces cas # la boîte de dialogue erreur_1 est affichée et le traitement # est stoppé. Les 2 boîtes de dialogue (erreur_1 et erreur_2) # sont utilisées dans 2 cas différents (même si le message # d'erreur délivré est le même) if exifdata == None: erreur_1 = QMessageBox.critical(self,_(u"Par ordre de prise de vue (données Exif)"),_(u"<p>Une ou plusieurs de vos image(s) possède(nt) de fausses donnée(s) Exif.</p><p><b>Le traitement demandé ne pourra pas se dérouler avec succès.</b></p><p><b>Veuillez, s'il vous plaît (la prochaine fois), charger des photos avec des données Exif correctes.</b></p>"), QMessageBox.Yes) if erreur_1 == QMessageBox.Yes: return # Si on charge des images ne possédant pas de données Exif # ou de fausses images avec données Exif (comme par exemple # les copies d'écran faites par Gimp, qui elles, bizarre ! # possèdent des données Exif inexploitables) tout se ferme except: erreur_2 = QMessageBox.critical(self,_(u"Par ordre de prise de vue (données Exif)"),_(u"<p>Une ou plusieurs de vos image(s) possède(nt) de fausses donnée(s) Exif.</p><p><b>Le traitement demandé ne pourra pas se dérouler avec succès.</b></p><p><b>Veuillez, s'il vous plaît (la prochaine fois), charger des photos avec des données Exif correctes.</b></p>"), QMessageBox.Yes) if erreur_2 == QMessageBox.Yes: return # Récup des clés et valeurs du dictionnaire for keyExif, valueExif in zip(exifdata.keys(), exifdata.values()): try: # On ne récupère que le tag DateTime if ExifTags.TAGS[keyExif] == 'DateTimeOriginal': listeExif.append(valueExif.split(' ')) except KeyError: pass # Split pour récup des données de la sorte: [..., [année, mois, jour], [heure, min, sec], ...] listeSplit = [parc_2.split(':') for parc_1 in listeExif for parc_2 in parc_1] # Récup de la 1ère sous-liste uniquement index pair # [[année_1, mois_1, jour_1], [année_2, mois_2, jour_2], ...] listeIndexPair = [n for n in listeSplit if listeSplit.index(n) % 2 == 0] # Récup de la 2ème sous-liste uniquement index impair # [[heure_1, min_1, sec_1], [heure_2, min_2, sec_2], ...] listeIndexImpair = [n for n in listeSplit if listeSplit.index(n) % 2 != 0] # Fusion des 2 listes listeReunion = zip(listeIndexPair, listeIndexImpair) listeDate = [] for nb, group in enumerate(zip(listeReunion, l_imgSource)): # Les données de temps affichées ne sont pas directement exploitables # Il y a des choses à transformer ... surtout si on trouve des '00' # ------------------- annee = group[0][0][0] if annee[0] == '0': annee = annee[-1:] else: annee = annee # ------------------- mois = group[0][0][1] if mois[0] == '0': mois = mois[-1:] else: mois = mois # ------------------- jour = group[0][0][2] if jour[0] == '0': jour = jour[-1:] else: jour = jour # ------------------- heure = group[0][1][0] if heure[0] == '0': heure = heure[-1:] else: heure = heure # ------------------- minute = group[0][1][1] if minute[0] == '0': minute = minute[-1:] else: minute = minute # ------------------- seconde = group[0][1][2] if seconde[0] == '0': seconde = seconde[-1:] else: seconde = seconde # Liste remplie de la sorte: [..., [annee, mois, jour, # heure, minute, seconde, index, chemin_image], ...] listeDate.append([int(annee), int(mois), int(jour), int(heure), int(minute), int(seconde), nb, group[1]]) listeDate.sort() # Sélection de chaque élément à l'indice 7 dans les sous-listes, # c'est à dire le chemin de l'image (ordre croissant) listeOrdreDateCrois = [ordre for pre_ordre in listeDate for ordre in pre_ordre if pre_ordre.index(ordre) / 7 == 1] l_imgSource = listeOrdreDateCrois # Si l'utilisateur choisit l'ordre décroissant, le même # traitement est effectué, mais l'ordre de la liste finale # est inversé if classemt == 'ord_exif_dc': l_imgSource.reverse() # Demandé par Marc de la liste lprod # La liste liste_increment_2 contient les données comme ceci: # [(indice img 1, incrément img 1), (indice img 2, incrément img 2), # (indice img 3, incrément img 3), (indice img 4, incrément img 4), ...] liste_increment_1 = [c_pas_1*self.spin3.value() for c_pas_1 in range(nbreElem)] liste_increment_2 = [(inc, c_pas_2) for inc, c_pas_2 in enumerate(liste_increment_1)] # Récupération du chemin + vidéo chargée et de l'extension # (la première image de la liste) fich, ext=os.path.splitext(self.listeImgSource[0]) # Utilisation de la nouvelle boîte de dialogue de sauvegarde # Boîte de dialogue pour sauvegarder (nom du nouveau fichier) suffix="" sauver = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=True) sauver = sauver.getFile() # Pour la version windows # Autrement les fichiers seront (a la fin) renommes comme ceci: # a.jpg_000001.jpg, a.jpg_000002.jpg, ... alors que la ils seront renommes # comme ceci (c'est quand meme mieux !): a_000001.jpg, a_000002.jpg, ... # Uniquement pour windows if os.name == 'nt': sauver, ext_sauv = os.path.splitext(sauver) if not sauver: return listeSauve=[] if len(self.listeImgSource)==1: # Renommage et sauvegarde shutil.move(l_imgSource[0], sauver+ext) listeSauve.append(sauver+ext) elif len(self.listeImgSource)>1: # Barre de progression dans une fenêtre séparée . Attention la fenêtre # de la barre se ferme dès que la progression est terminée . En cas de # process très court, la fenêtre peut n'apparaître que très brièvement # (voire pas du tout si le temps est très très court) . self.progress=QProgressDialog(_(u"Opération en cours ..."), _(u"Annuler conversion"), 0, 100) self.progress.setWindowTitle(_(u'EnKoDeur-Mixeur . Fenêtre de progression')) # Attribution des nouvelles dimensions self.progress.setMinimumWidth(500) self.progress.setMinimumHeight(100) # setGeometry est utilisé ici uniquement pour le placement à la position 0, 0 # je ne suis pas un adepte de la position 0,0 :-P (Romain) #self.progress.setGeometry(QRect(0, 0, 500, 100)) self.progress.show() # Demandé par Marc de la liste lprod # Le parcours se fait maintenant par la liste liste_increment_2 (le contenu # de cette liste est précisé est précisé plus haut). Ceci pour mettre en # pratique l'icrémentation des images, c'est à dire de pouvoir les renommer # comme ceci: a_0001.jpg, a_0004.jpg, a_0007.jpg, a_0010.jpg, ... --> quand # l'utilisateur change la Valeur d'incrément (passage d'une image à l'autre) # à 3 (ce n'est qu'un exemple) for parc, increment in liste_increment_2: # Renommage et sauvegarde # Uniquement pour Linux et MacOSX if os.name in ['posix', 'mac']: shutil.move(l_imgSource[parc], sauver+'_'+string.zfill(increment+self.spin1.value(), self.spin2.value())+ext) # Uniquement pour windows elif os.name == 'nt': # --------------- # On avait prealablement (uniquement sous windows) l'erreur suivante: # WindowsError: [Error 32] Le processus ne peut pas acceder au fichier car ce # fichier est utilise par un autre processus: ... # --------------- # La solution pour la version windows a ete trouvee ici: # http://www.nabble.com/Windows-process-ownership-trouble-td18110819.html # --> try: ... except WindowsError: ... # --------------- try: shutil.move(l_imgSource[parc], sauver+'_'+string.zfill(increment+self.spin1.value(), self.spin2.value())+ext) except WindowsError: pass # Gestion du nombre d'images à traiter listeSauve.append(sauver+'_'+string.zfill(increment+self.spin1.value(), self.spin2.value())+ext) # -------------------------------------------- # Affichage de la progression (avec # QProgressDialog) ds une fenêtre séparée . val_pourc=((parc+1)*100)/nbreElem # Bouton Cancel pour arrêter la progression donc le process if (self.progress.wasCanceled()): break self.progress.setValue(val_pourc) QApplication.processEvents() # -------------------------------------------- self.a2='###########################\n' self.b2=_(u'Fichiers après renommage')+':\n' self.c2='###########################\n\n' # Affichage dans zoneTexte des fichiers avant et après renommage self.zoneAffichInfosImg.setText(self.a1+self.b1+self.c1+"\n".join(self.listeImgSource)+'\n\n'+self.a2+self.b2+self.c2+"\n".join(listeSauve)) self.framImg.setEnabled(True) self.tabwidget.setCurrentIndex(self.indexTabInfo)
def appliquer(self, nomSortie=None, ouvert=1): """Appel du moteur du cadre""" # quel est l'index du dernier item sélectionné de la boîte de combo? index = self.combo.currentIndex() if not nomSortie: ## On utilise la nouvelle interface de récupération des vidéos # Récupération du chemin source chemin = unicode(self.getFile()) # suffix du codec actif suffix = self.listeCombo[index][3] if suffix == "": suffix = os.path.splitext(chemin)[1] # ------------------------------------------------------------------- # # Gestion des fichiers mod (extension .mod). Ce sont (apparemment) # des fichiers mpeg avec une extension .mod. Les fichiers en question # ont juste besoin d'être renommés avec une extension .mpg avant le # traitement. # ------------------------------------------------------------------- # ext_chargee = os.path.splitext(chemin)[1] nom_fich_sans_ext = os.path.splitext(os.path.basename(chemin))[0] if ext_chargee in [".mod", ".MOD"]: # Copie du fichier concerné dans le rep tampon et renommage avec ext .mpg shutil.copy(chemin, self.repTempFichiersMod + nom_fich_sans_ext + ".mpg") chemin = unicode(self.repTempFichiersMod + nom_fich_sans_ext + ".mpg") # suffix du codec actif saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver")) cheminFichierEnregistrerVideo = saveDialog.getFile() ################################################################################################################### else: # module séquentiel chemin = cheminFichierEnregistrerVideo = nomSortie if not cheminFichierEnregistrerVideo: return # quel est l'index du dernier item sélectionné de la boîte de combo? # print 'index', index EkdPrint("index " + str(index)) # identifiant du codec actif idCodec = self.listeCombo[index][0] # print 'idCodec', idCodec EkdPrint("idCodec " + idCodec) # Condition pour détection windows if os.name == "nt": mencoder = WidgetMEncoder(idCodec, chemin, cheminFichierEnregistrerVideo, laisserOuvert=ouvert) # Condition pour détection Linux elif os.name in ["posix", "mac"]: mencoder = WidgetMEncoder(idCodec, chemin, cheminFichierEnregistrerVideo, laisserOuvert=ouvert) mencoder.setWindowTitle(_(u"Encodage vidéo - Pour le web")) mencoder.exec_() # Condition pour détection windows if os.name == "nt": self.lstFichiersSortie = cheminFichierEnregistrerVideo # pour la boite de dialogue de comparaison # Condition pour détection Linux elif os.name in ["posix", "mac"]: 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 cheminFichierEnregistrerVideo # module séquentiel
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
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')))
class Image_Divers_Compositing(QWidget): # ----------------------------------- # Cadre accueillant les widgets de : # Image >> Divers >> Compositing # ----------------------------------- def __init__(self, statusBar, geometry): QWidget.__init__(self) # ---------------------------- # Quelques paramètres de base # ---------------------------- # Paramètres de configuration self.config = EkdConfig # Fonctions communes à plusieurs cadres du module Image self.base = Base() self.idSection = "image_image_composite" # Log du terminal self.base.printSection(self.idSection) # Fonction appelant la fenêtre principale self.mainWindowFrameGeometry = geometry # Création des répertoires temporaires. Utilisation de EkdConfig self.repTampon = EkdConfig.getTempDir() + os.sep + "tampon" + os.sep + "temp_duplication" + 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) # Répertoire tampon dans lequel est crée le compositing qui sera crée par le # bouton 'Voir le résultat'. Utilisation de EkdConfig self.repTamponVisuVoirRes = self.repTampon + "visu_voir_res_compo" + os.sep if os.path.isdir(self.repTamponVisuVoirRes) is False: os.makedirs(self.repTamponVisuVoirRes) # Au cas où le répertoire existait déjà et qu'il n'était pas vide # -> purge (simple précausion) for toutRepCompoVisu in glob.glob(self.repTamponVisuVoirRes+'*.*'): os.remove(toutRepCompoVisu) # Répertoire temporaire 1 pour les redimensionnement des images if os.path.isdir(self.repTampon+'redim_1/') is False: os.makedirs(self.repTampon+'redim_1/') # Répertoire temporaire 2 pour les redimensionnement des images if os.path.isdir(self.repTampon+'redim_2/') is False: os.makedirs(self.repTampon+'redim_2/') # Au cas où le répertoire existait déjà et qu'il n'était pas vide # -> purge (simple précausion) for toutRepCompoRedim in glob.glob(self.repTampon+'redim_1/'+'*.*'): if len(toutRepCompoRedim)>0: os.remove(toutRepCompoRedim) # ... for toutRepCompoRedim in glob.glob(self.repTampon+'redim_2/'+'*.*'): if len(toutRepCompoRedim)>0: os.remove(toutRepCompoRedim) #=== 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 # Liste de chemins de fichiers avec et sans canal alpha et du dossier de sauvegarde self.listeChemAVcanAlph=[] self.listeChemSANScanAlph=[] self.listeImgDestin = [] # Boite d'alignement vertical vbox=QVBoxLayout(self) # -------------------------------------------------- # widgets du haut : titre + bouton de sélection # -------------------------------------------------- hbox = QHBoxLayout() # Ajout du titre de la page et de l'aperçu à la boite verticale vbox.addLayout(hbox, 0) #=== Bouton de sélection des images alpha et sans alpha ===# hbox = QHBoxLayout() self.framReglage=QFrame() vboxReglage=QVBoxLayout(self.framReglage) # Pour la 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=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() # ------------------------------------------------- # Onglets d'affichage image source et destination # ------------------------------------------------- # On peut sélectionner les extensions qui doivent être visibles comme ceci: #self.afficheurImgSource=SelectWidget(extensions=["*.jpg", "*.png"], geometrie = self.mainWindowFrameGeometry) # Là uniquement les fichiers png et gif apparaissent ds la fenêtre de chargement # Ne pas oublier de mettre * avant le point et l'extension # Là où s'afficheront les images # Avec canal alpha self.afficheurImgSourceAvecCanalAlpha=SelectWidget(extensions=["*.png", "*.gif"], geometrie = self.mainWindowFrameGeometry) # Sans canal alpha self.afficheurImgSourceSansCanalAlpha=SelectWidget(geometrie = self.mainWindowFrameGeometry) # Gestion de la configuration via EkdConfig # Résultat du compositing 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.afficheurImgSourceSansCanalAlpha # Fait le lien avec le sélecteur de fichier source. ### Remarque : Le choix a été fait de ne pas mettre la boîte de sélection des images alpha dans le tampon. # infos - logs self.zoneAffichInfosImg = QTextEdit("") if PYQT_VERSION_STR < "4.1.0": self.zoneAffichInfosImg.setText = self.zoneAffichInfosImg.setPlainText self.zoneAffichInfosImg.setReadOnly(True) self.fram=QFrame() vboxInfIm=QVBoxLayout(self.fram) vboxInfIm.addWidget(self.zoneAffichInfosImg) self.fram.setEnabled(False) self.tabwidget=QTabWidget() self.indexTabImgSourceAvCanAlph = self.tabwidget.addTab(self.afficheurImgSourceAvecCanalAlpha, _(u'Image(s) avec canal alpha')) self.indexTabImgSourceSansCanAlph = self.tabwidget.addTab(self.afficheurImgSourceSansCanalAlpha, _(u'Image(s) sans canal alpha')) 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.fram, _(u'Infos')) vbox.addWidget(self.tabwidget) # ------------------------------------------------------------------- # 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.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_derniere_img) self.boutAppliquer=QPushButton(_(u" Appliquer et sauver")) self.boutAppliquer.setIcon(QIcon("Icones/icone_appliquer_128.png")) 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) 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.boutAppliquer) vbox.addLayout(hbox) # 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.afficheurImgSourceSansCanalAlpha, SIGNAL("pictureChanged(int)"), self.modifBoutonsAction) 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.boutAppliquer.setEnabled(i) self.boutApPremImg.setEnabled(i) self.modifImageSource = 1 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 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 stat_dim_img_1(self): """Calcul statistique des dimensions des images les plus présentes dans le lot""" # Récupération de la liste des fichiers chargés (avec canal alpha) self.listeChemAVcanAlph=self.afficheurImgSourceAvecCanalAlpha.getFiles() # Ouverture et mise ds une liste des dimensions des images listePrepaRedim=[Image.open(aA).size for aA in self.listeChemAVcanAlph] # Merci beaucoup à Marc Keller de la liste: python at aful.org de m'avoir # aidé pour cette partie (les 4 lignes en dessous) dictSeq={}.fromkeys(listePrepaRedim, 0) for cle in listePrepaRedim: dictSeq[cle]+=1 self.lStatDimSeq=sorted(zip(dictSeq.itervalues(), dictSeq.iterkeys()), reverse=1) self.dimStatImg=self.lStatDimSeq[0][1] #print "Toutes les dimensions des images (avec le nbre d'images):", self.lStatDimSeq 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_1(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_1(): 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 ?. Dans la plupart des cas il faut répondre oui."), 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.listeChemAVcanAlph: obImg=Image.open(chemImg) if obImg.size!=self.dimStatImg: pass sRedim=obImg.resize(self.dimStatImg, Image.ANTIALIAS) chemSortie = self.repTampon+'redim_1'+os.sep+os.path.basename(chemImg) sRedim.save(chemSortie) self.listeChemAVcanAlph[index] = chemSortie index += 1 def stat_dim_img_2(self): """Calcul statistique des dimensions des images les plus présentes dans le lot""" # Récupération de la liste des fichiers chargés (sans canal alpha) self.listeChemSANScanAlph=self.afficheurImgSourceSansCanalAlpha.getFiles() ########################################################################### # Ouverture et mise ds une liste des dimensions des images listePrepaRedim=[Image.open(aA).size for aA in self.listeChemSANScanAlph] # Merci beaucoup à Marc Keller de la liste: python at aful.org de m'avoir # aidé pour cette partie (les 4 lignes en dessous) dictSeq={}.fromkeys(listePrepaRedim, 0) for cle in listePrepaRedim: dictSeq[cle]+=1 self.lStatDimSeq=sorted(zip(dictSeq.itervalues(), dictSeq.iterkeys()), reverse=1) self.dimStatImg=self.lStatDimSeq[0][1] #print self.dimStatImg EkdPrint(self.dimStatImg) #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_2(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_2(): 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 ?. Dans la plupart des cas il faut répondre oui."), 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.listeChemSANScanAlph: obImg=Image.open(chemImg) if obImg.size!=self.dimStatImg: pass sRedim=obImg.resize(self.dimStatImg, Image.ANTIALIAS) chemSortie = self.repTampon+'redim_2'+os.sep+os.path.basename(chemImg) sRedim.save(chemSortie) self.listeChemSANScanAlph[index] = chemSortie index += 1 def visu_1ere_derniere_img(self): """Visionnement du compositing avant application""" # Si l'utilisateur charge des images de taille différente, fait le traitement # ..., recharge de nouvelles images, il faut que les répertoires de redimen- # sionnement soient vidés listePresRedim_1=glob.glob(self.repTampon+'redim_1'+os.sep+'*.*') listePresRedim_1.sort() if len(listePresRedim_1)>0: for parcR_1 in listePresRedim_1: os.remove(parcR_1) listePresRedim_2=glob.glob(self.repTampon+'redim_2'+os.sep+'*.*') listePresRedim_2.sort() if len(listePresRedim_2)>0: for parcR_2 in listePresRedim_2: os.remove(parcR_2) # Récupération de la liste des fichiers chargés (avec canal alpha) self.listeChemAVcanAlph=self.afficheurImgSourceAvecCanalAlpha.getFiles() self.listeChemAVcanAlph.sort() # Récupération de la liste des fichiers chargés (sans canal alpha) self.listeChemSANScanAlph=self.afficheurImgSourceSansCanalAlpha.getFiles() self.listeChemSANScanAlph.sort() # Vérification du fait que les fichiers avec canal alpha chargés contiennent bien # un canal alpha (RGBA) ... sinon affichage d'une boîte de dialogue d'erreur et arrêt # du traitement des images for parcMode in self.listeChemAVcanAlph: imVerifCanAlph=Image.open(parcMode) if imVerifCanAlph.mode!='RGBA': messErr=QMessageBox(self) messErr.setText(_(u"<b>Vous avez chargé des images sans canal alpha</b> (c'est à dire sans transparence) et ce à partir de l'onglet <b>Image(s) avec canal alpha</b>. Sans transparence, vous ne pouvez, en aucun cas, appliquer un compositing (vos images doivent être en mode RGBA pour que cela réussisse) !.")) messErr.setWindowTitle(_(u"Erreur")) messErr.setIcon(QMessageBox.Critical) messErr.exec_() return # ----- TRAVAIL PREPARATOIRE --- Redimensionnement des images ----------------- try: nbreElem_1=len(self.listeChemAVcanAlph) nbreElem_2=len(self.listeChemSANScanAlph) # Appel des fonction de redimensionnement self.redim_img_1() self.redim_img_2() # Récup des listes contenant les fichiers repRedimTemp_1=glob.glob(self.repTampon+'redim_1'+os.sep+'*.*') repRedimTemp_1.sort() repRedimTemp_2=glob.glob(self.repTampon+'redim_2'+os.sep+'*.*') repRedimTemp_2.sort() except: messageErreur=QMessageBox(self) messageErreur.setText(_(u"<p>Vous n'avez pas chargé d'image(s) (bouton Ajouter) dans l'onglet <b>Image(s) avec canal aplpha</b>. Recommencez et chargez des images aussi bien dans <b>Image(s) avec canal aplpha</b>, que dans <b>Image(s) sans canal aplpha</b>.</p>")) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Critical) messageErreur.exec_() return # ----------------------------------------------------------------------------- try: # tImgAVcanAlph --> taille des images avec canal alpha # tImgSANScanAlph --> taille des images avec canal alpha # =/= --> différent # == --> strictement ègal # Si tImgAVcanAlph == entre elles et tImgSANScanAlph == entre elles # mais tImgAVcanAlph =/= tImgSANScanAlph (tImgAVcanAlph == tImgSANScanAlph # est aussi valable) if len(repRedimTemp_1)==0 and len(repRedimTemp_2)==0: im01=Image.open(self.listeChemAVcanAlph[nbreElem_1-1]) im02=Image.open(self.listeChemSANScanAlph[0]) # Redimensionnement à la tImgAVcanAlph im02=im02.resize(Image.open(self.listeChemAVcanAlph[nbreElem_1-1]).size, Image.ANTIALIAS) imgCompoUndeChaque=Image.composite(im01, im02, im01) # Si tImgAVcanAlph == entre elles et tImgSANScanAlph =/= entre elles elif len(repRedimTemp_1)==0 and len(repRedimTemp_2)>1: im01=Image.open(self.listeChemAVcanAlph[nbreElem_1-1]) im02=Image.open(repRedimTemp_2[0]) # Redimensionnement à la tImgAVcanAlph im02=im02.resize(Image.open(self.listeChemAVcanAlph[nbreElem_1-1]).size, Image.ANTIALIAS) imgCompoUndeChaque=Image.composite(im01, im02, im01) # Si tImgAVcanAlph =/= entre elles et tImgSANScanAlph =/= entre elles # Plusieurs images avec can alpha et plusieurs images sans can alpha elif len(repRedimTemp_1)>1 and len(repRedimTemp_2)>1: im01=Image.open(repRedimTemp_1[nbreElem_1-1]) im02=Image.open(repRedimTemp_2[0]) # Redimensionnement à la tImgAVcanAlph im02=im02.resize(Image.open(repRedimTemp_1[nbreElem_1-1]).size, Image.ANTIALIAS) imgCompoUndeChaque=Image.composite(im01, im02, im01) # Si tImgAVcanAlph =/= entre elles et tImgSANScanAlph == ... là on a # qu'une image sans canal alpha ce qui correspond à un arrière plan fixe elif len(repRedimTemp_1)>1 and len(repRedimTemp_2)==0: im01=Image.open(repRedimTemp_1[nbreElem_1-1]) im02=Image.open(self.listeChemSANScanAlph[0]) # Redimensionnement à la tImgAVcanAlph im02=im02.resize(Image.open(repRedimTemp_1[nbreElem_1-1]).size, Image.ANTIALIAS) imgCompoUndeChaque=Image.composite(im01, im02, im01) except: messageErreur=QMessageBox(self) messageErreur.setText(_(u"<p><b>Première situation d'erreur:</b> Vous n'avez pas chargé d'image(s) (bouton Ajouter) dans l'onglet <b>Image(s) avec canal aplpha</b>. Recommencez et chargez des images aussi bien dans <b>Image(s) avec canal aplpha</b>, que dans <b>Image(s) sans canal aplpha</b>.</p><p><b>Seconde situation d'erreur:</b> la visualisation de l'image ne peut pas avoir lieu car vous avez répondu non au moins une fois au moment du redimensionnement des images. Recommencez et répondez oui aux deux boîtes de dialogue.</p>")) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Critical) messageErreur.exec_() return # Sauvegarde des images resultant du Compositing self.cheminCourantSauv = self.repTamponVisuVoirRes+'visu_compositing_'+string.zfill((1), 6)+'.png' imgCompoUndeChaque.save(self.cheminCourantSauv, "PNG") # 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): """Appliquer le compositing""" # Si l'utilisateur charge des images de taille différente, fait le traitement # ..., recharge de nouvelles images, il faut que les répertoires de redimen- # sionnement soient vidés listePresRedim_1=glob.glob(self.repTampon+'redim_1'+os.sep+'*.*') listePresRedim_1.sort() if len(listePresRedim_1)>0: for parcR_1 in listePresRedim_1: os.remove(parcR_1) listePresRedim_2=glob.glob(self.repTampon+'redim_2'+os.sep+'*.*') listePresRedim_2.sort() if len(listePresRedim_2)>0: for parcR_2 in listePresRedim_2: os.remove(parcR_2) # 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=[] # Récupération de la liste des fichiers chargés (avec canal alpha) self.listeChemAVcanAlph=self.afficheurImgSourceAvecCanalAlpha.getFiles() self.listeChemAVcanAlph.sort() # Récupération de la liste des fichiers chargés (sans canal alpha) self.listeChemSANScanAlph=self.afficheurImgSourceSansCanalAlpha.getFiles() self.listeChemSANScanAlph.sort() # Vérification du fait que les fichiers avec canal alpha chargés contiennent bien # un canal alpha (RGBA) ... sinon affichage d'une boîte de dialogue d'erreur et arrêt # du traitement des images for parcMode in self.listeChemAVcanAlph: imVerifCanAlph=Image.open(parcMode) if imVerifCanAlph.mode!='RGBA': messErr=QMessageBox(self) messErr.setText(_(u"<b>Vous avez chargé des images sans canal alpha</b> (c'est à dire sans transparence) et ce à partir de l'onglet <b>Image(s) avec canal alpha</b>. Sans transparence, vous ne pouvez, en aucun cas, appliquer un compositing (vos images doivent être en mode RGBA pour que cela réussisse) !.")) messErr.setWindowTitle(_(u"Erreur")) messErr.setIcon(QMessageBox.Critical) messErr.exec_() return # ----- TRAVAIL PREPARATOIRE --- Redimensionnement des images ----------------- try: nbreElem_1=len(self.listeChemAVcanAlph) nbreElem_2=len(self.listeChemSANScanAlph) # Appel des fonction de redimensionnement self.redim_img_1() self.redim_img_2() # Récup des listes contenant les fichiers ### Le 18/09/09 ## ...+'redim_1/*.*' transformé en ...+'redim_1'+os.sep+'*.*' repRedimTemp_1=glob.glob(self.repTampon+'redim_1'+os.sep+'*.*') repRedimTemp_1.sort() ### Le 18/09/09 ## ...+'redim_2/*.*' transformé en ...+'redim_2'+os.sep+'*.*' repRedimTemp_2=glob.glob(self.repTampon+'redim_2'+os.sep+'*.*') repRedimTemp_2.sort() except: messageErreur=QMessageBox(self) messageErreur.setText(_(u"<p>Vous n'avez pas chargé d'image(s) (bouton Ajouter) dans l'onglet <b>Image(s) avec canal aplpha</b>. Recommencez et chargez des images aussi bien dans <b>Image(s) avec canal aplpha</b>, que dans <b>Image(s) sans canal aplpha</b>.</p>")) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Critical) messageErreur.exec_() return # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # Boîte de dialogue pour sauvegarder (nom du nouveau fichier) #------------------------------------------------------------------------------ # Utilisation de la nouvelle boîte de dialogue de sauvegarde suffix="" self.chemDossierSauv = EkdSaveDialog(self, mode="image", suffix=suffix, title=_(u"Sauver"), multiple=True) self.chemDossierSauv = self.chemDossierSauv.getFile() if not self.chemDossierSauv: return # Liste pour affichage (ds le tabwidget) listeAff_1=[] listeAff_2=[] listeAff_3=[] # Barre de progression dans une fenêtre séparée . Attention la fenêtre # de la barre se ferme dès que la progression est terminée . En cas de # process très court, la fenêtre peut n'apparaître que très brièvement # (voire pas du tout si le temps est très très court) . 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) # On implémente les chemins des fichiers dans une variable # pour préparer l'affichage des infos texte1=_(u" Image(s) avec canal alpha chargée(s)") texte2=_(u" Image(s) sans canal alpha chargée(s)") texte3=_(u" Résultat image(s) composite") a='#'*36 self.infosImgProv_1=a+'\n#'+texte1+'\n'+a self.infosImgProv_2=a+'\n#'+texte2+'\n'+a self.infosImgProv_3=a+'\n#'+texte3+'\n'+a # ------------------------------------------------------------------- # Si le nbre d'images chargées avec canal alpha est égal à 1 ... # en fait si l'utilisateur ne charge qu'une image avec et sans canal # alpha ... # ------------------------------------------------------------------- if nbreElem_1 == 1: try : # Ouverture des images (im01 --> avec canal alpha et # im02 --> sans canal alpha) im01=Image.open(self.listeChemAVcanAlph[0]) im02=Image.open(self.listeChemSANScanAlph[0]) # Récup de la dimension des l'image chargée avec canal alpha w_1, h_1=im01.size # Récup de la dimension des l'image chargée sans canal alpha w_2, h_2=im02.size # Redimensionnement de l'image sans canal alpha à la taille de l'image avec canal alpha if (int(w_1), int(h_1)) != (int(w_2), int(h_2)): im02=im02.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # Application du compositing imgCompoUndeChaque=Image.composite(im01, im02, im01) # Sauvegarde des images resultant du Compositing self.cheminCourantSauv = self.chemDossierSauv+'_'+string.zfill(self.spin1.value(), self.spin2.value())+'.png' imgCompoUndeChaque.save(self.cheminCourantSauv, "PNG") # Ajout des images par la variable self.cheminCourantSauv dans la liste self.listeChemin # Cette liste sert à récupérer les images pour l'affichage des images ds l'inteface self.listeImgDestin.append(self.cheminCourantSauv) # Affichage des images après traitement # # Changement d'onglet et fonctions associées self.conversionImg = 1 self.metaFctTab(self.indexTabImgDestin) # log listeAff_1.append(self.listeChemAVcanAlph[0]) listeAff_2.append(self.listeChemSANScanAlph[0]) listeAff_3.append(self.cheminCourantSauv) except: messageErreur=QMessageBox(self) messageErreur.setText(_(u"<p>Vous n'avez pas chargé d'image(s) (bouton Ajouter) dans l'onglet <b>Image(s) avec canal aplpha</b>. Recommencez et chargez des images aussi bien dans <b>Image(s) avec canal aplpha</b>, que dans <b>Image(s) sans canal aplpha</b>.</p>")) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Critical) messageErreur.exec_() # ------------------------------------------------------------------- # Si le nbre d'images chargées sans canal alpha est supérieur à 1 ... # ce qui correspond à un travail avec un fond animé # ------------------------------------------------------------------- elif nbreElem_2 > 1: self.progress.show() nbre=min(nbreElem_1, nbreElem_2) try : for parcoursComposit_un in range(nbre): # Ouverture des images (imm1 --> avec canal alpha et # imm2 --> sans canal alpha) imm1=Image.open(self.listeChemAVcanAlph[parcoursComposit_un]) imm2=Image.open(self.listeChemSANScanAlph[parcoursComposit_un]) # ------------ Images exactement de même taille --------------------- # Récup des dimensions des images chargées avec canal alpha w_1, h_1=imm1.size # Récup des dimensions des images chargées sans canal alpha w_2, h_2=imm2.size # Si l'utilisateur à chargé des images ayant strictement la même taille if w_1==w_2 and h_1==h_2: # Redimensionnement des images sans canal alpha à la taille # des images avec canal alpha imm2=imm2.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # ------------------------------------------------------------------- # ------------ Images de tailles différentes ------------------------ # Si l'utilisateur à chargé des images avec canal alpha ayant la même # taille entre elles mais n'ayant pas la même taille que les images # sans canal alpha (qui ont la même taille entre elles) if w_1!=w_2 or h_1!=h_2: # Redimensionnement des images sans canal alpha à la taille # des images avec canal alpha imm2=imm2.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # Si l'utilisateur charge des images avec avec canal alpha de tailles # différentes entre elles et des images sans canal alpha aussi différentes # entre elles, les images sans canal alpha sont redimensionnées à la dimension # des images avec canal alpha if len(repRedimTemp_1)>0 and len(repRedimTemp_2)>0: if (int(w_1), int(h_1)) != (int(w_2), int(h_2)): imm2=imm2.resize(Image.open(repRedimTemp_1[0]).size, Image.ANTIALIAS) # Si l'utilisateur charge des images avec canal alpha toutes de même taille mais # aussi charge des images sans canal alpha de tailles complètement différentes, # les images sans canal alpha sont redimensionnées à la dimension des images # avec canal alpha if len(repRedimTemp_2)>0 and len(repRedimTemp_1)==0: if (int(w_1), int(h_1)) != (int(w_2), int(h_2)): imm2=imm2.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # Si l'utilisateur charge des images sans canal alpha toutes de même taille mais # aussi charge des images avec canal alpha de tailles complètement différentes, # les images avec canal alpha sont redimensionnées à la dimension de la 1ère # image avec canal alpha if len(repRedimTemp_1)>0 and len(repRedimTemp_2)==0: if (int(w_1), int(h_1)) != (int(w_2), int(h_2)): imm1=imm1.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # Application du compositing imgCompo_1=Image.composite(imm1, imm2, imm1) # Sauvegarde des images resultant du Compositing self.cheminCourantSauv = self.chemDossierSauv+'_'+string.zfill(parcoursComposit_un+self.spin1.value(), self.spin2.value())+'.png' imgCompo_1.save(self.cheminCourantSauv, "PNG") # Ajout des images par la variable self.cheminCourantSauv dans la liste self.listeChemin # Cette liste sert à récupérer les images pour l'affichage des images ds l'inteface self.listeImgDestin.append(self.cheminCourantSauv) # Affichage des images après traitement # # Changement d'onglet et fonctions associées self.conversionImg = 1 self.metaFctTab(self.indexTabImgDestin) # log listeAff_1.append(self.listeChemAVcanAlph[parcoursComposit_un]) listeAff_2.append(self.listeChemSANScanAlph[parcoursComposit_un]) listeAff_3.append(self.cheminCourantSauv) # -------------------------------------------- # Affichage de la progression (avec # QProgressDialog) ds une fenêtre séparée val_pourc_1=((parcoursComposit_un+1)*100)/nbre # Bouton Cancel pour arrêter la progression donc le process if (self.progress.wasCanceled()): break self.progress.setValue(val_pourc_1) QApplication.processEvents() # -------------------------------------------- except: messageErreur=QMessageBox(self) messageErreur.setText(_(u"<p><b>Première situation d'erreur:</b> Vous n'avez pas chargé d'image(s) (bouton Ajouter) dans l'onglet <b>Image(s) avec canal aplpha</b>. Recommencez et chargez des images aussi bien dans <b>Image(s) avec canal alpha</b>, que dans <b>Image(s) sans canal aplpha</b>.</p><p><b>Seconde situation d'erreur:</b> la visualisation et/ou le traitement image(s) ne peut pas avoir lieu car vous avez répondu non au moins une fois au moment du redimensionnement des images. Recommencez et répondez oui aux deux boîtes de dialogue.</p>")) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Critical) messageErreur.exec_() # ------------------------------------------------------------------- # Si le nbre d'images chargées sans canal alpha est égal à 1 ... # ce qui correspond à un travail avec un fond fixe (caméra fixe) # ------------------------------------------------------------------- elif nbreElem_2 == 1: self.progress.show() listeTempCompo=[] try : for parcoursDupli in range(nbreElem_1): dupliString=self.listeChemSANScanAlph[0] # Ouverture de l'image reS=Image.open(dupliString) # Sauvegarde de l'image après multiplication reS.save(str(self.repTampon+'d_'+string.zfill(parcoursDupli+self.spin1.value(), self.spin2.value())+'.png'), 'PNG') # Remplissage de la liste tampon listeTempCompo.append(self.repTampon+'d_'+string.zfill(parcoursDupli+self.spin1.value(), self.spin2.value())+'.png') # Copie de l'ensemble des images (avec canal alpha) # chargees par l'utilisateur, dans le sous-repertoire # temporaire --> /EkdConfig.getTempDir()/ekd/tampon/temp_duplication/ . Il est # indispensable que ces images se retrouvent dans le meme # repertoire que les images multipliées (sans canal alpha) for parCop in self.listeChemSANScanAlph: cop=shutil.copy(parCop, self.repTampon) # Ouverture des images (imm3 --> avec canal alpha et # imm4 --> sans canal alpha) imm3=Image.open(self.listeChemAVcanAlph[parcoursDupli]) imm4=Image.open(listeTempCompo[parcoursDupli]) # ------------ Images de même taille -------------------------------- # Récup des dimensions des images chargées avec canal alpha w_1, h_1=imm3.size # Récup des dimensions des images chargées sans canal alpha w_2, h_2=imm4.size # Si l'utilisateur à chargé des images ayant strictement la même taille if w_1==w_2 and h_1==h_2: # Redimensionnement des images sans canal alpha à la taille # des images avec canal alpha imm4=imm4.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # ------------------------------------------------------------------- # ------------ Images de tailles différentes ------------------------ # Si le rep /EkdConfig.getTempDir()/ekd/tampon/temp_duplication/redim_1 contient des fichiers ... if len(repRedimTemp_1)>0: # Ouverture de la dernière image dans: # /EkdConfig.getTempDir()/ekd/tampon/temp_duplication/redim_1 imm3=Image.open(repRedimTemp_1[parcoursDupli]) # Dimension (w_1 --> largeur, h_1 --> hauteur) w_1, h_1=imm3.size # Si le rep /EkdConfig.getTempDir()/ekd/tampon/temp_duplication/redim_2 contient des fichiers ... if len(repRedimTemp_2)>0: # Ouverture de la première image dans: # /EkdConfig.getTempDir()/ekd/tampon/temp_duplication/redim_2 imm4=Image.open(repRedimTemp_2[parcoursDupli]) # Dimension (w_2 --> largeur, h_2 --> hauteur) w_2, h_2=imm4.size # Si l'utilisateur charge des images avec canal alpha de taille complètement différentes # (les images sont redimensionnées ds le rep tempo redim_1)de l'image sans canal alpha, # l'image sans canal alpha est redimensionnée à la taille des images avec canal alpha du # répertoire temporaire. Autrement l'image sans canal alpha est redimensionnée à la taille # de la 1ère image avec canal alpha chargée if len(repRedimTemp_1)>0: if (int(w_1), int(h_1)) != (int(w_2), int(h_2)): imm4=imm4.resize(Image.open(repRedimTemp_1[0]).size, Image.ANTIALIAS) else: imm4=imm4.resize(Image.open(self.listeChemAVcanAlph[0]).size, Image.ANTIALIAS) # Application du compositing . imgCompo_2=Image.composite(imm3, imm4, imm3) # Sauvegarde des images resultant du Compositing self.cheminCourantSauv = self.chemDossierSauv+'_'+string.zfill(parcoursDupli+self.spin1.value(), self.spin2.value())+'.png' # ---------------------------------------------------------------------- imgCompo_2.save(self.cheminCourantSauv, "PNG") # Ajout des images par la variable self.cheminCourantSauv dans la liste self.listeChemin # Cette liste sert à récupérer les images pour l'affichage des images ds l'inteface self.listeImgDestin.append(self.cheminCourantSauv) # Affichage des images après traitement # # Changement d'onglet et fonctions associées self.conversionImg = 1 self.metaFctTab(self.indexTabImgDestin) # log listeAff_1.append(self.listeChemAVcanAlph[parcoursDupli]) listeAff_2.append(listeTempCompo[parcoursDupli]) listeAff_3.append(self.cheminCourantSauv) # -------------------------------------------- # Affichage de la progression (avec # QProgressDialog) ds une fenêtre séparée . val_pourc_2=((parcoursDupli+1)*100)/nbreElem_1 # Bouton Cancel pour arrêter la progression donc le process if (self.progress.wasCanceled()): break self.progress.setValue(val_pourc_2) QApplication.processEvents() # -------------------------------------------- except: messageErreur=QMessageBox(self) messageErreur.setText(_(u"<p><b>Première situation d'erreur:</b> Vous n'avez pas chargé d'image(s) (bouton Ajouter) dans l'onglet <b>Image(s) avec canal aplpha</b>. Recommencez et chargez des images aussi bien dans <b>Image(s) avec canal alpha</b>, que dans <b>Image(s) sans canal aplpha</b>.</p><p><b>Seconde situation d'erreur:</b> la visualisation et/ou le traitement image(s) ne peut pas avoir lieu car vous avez répondu non au moins une fois au moment du redimensionnement des images. Recommencez et répondez oui aux deux boîtes de dialogue.</p>")) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Critical) messageErreur.exec_() # Images chargées avec canal alpha 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 # Compositing for parcStatRendu_3 in listeAff_3: self.infosImgProv_3=self.infosImgProv_3+'\n'+parcStatRendu_3 # affichage des infos dans l'onglet self.zoneAffichInfosImg.setText(self.infosImgProv_1+'\n\n'+self.infosImgProv_2+'\n\n'+self.infosImgProv_3+'\n\n') self.fram.setEnabled(True) def afficherAide(self): """Boîte de dialogue de l'aide""" # Utilisation de EkdAide messageAide=EkdAide(parent=self) messageAide.setText(tr(u"<p><b>Vous allez ici superposer des images avec un canal alpha (transparence) sur d'autres images sans canal alpha (ces dernières sont en quelque sorte l'arrière-plan). Vous avez aussi ici la possibilité de travailler avec un arrière-plan composé d'une seule image, en cas de travail en plan fixe (le programme duplique lui-même les images).</b></p><p><b>Voilà la définition que donne Wikipédia du terme compositing (c'est le terme exact): 'La composition (en anglais compositing) est un ensemble de méthodes numériques consistant à mélanger plusieurs sources d’images pour en faire un plan unique qui sera intégré dans le montage. Pour un film d'animation, il s'agit de l'étape finale de fabrication qui consiste à assembler toutes les couches des décors, des personnages et à réaliser les effets de caméra, à animer certains déplacements, et effets spéciaux. En cinéma de prise de vue réel, il consiste surtout à réaliser des effets spéciaux et à truquer des vidéos. C'est l'un des derniers maillons de la chaîne de l'image dans la réalisation d'un film.<br>Les sources peuvent être des images numérisées de cinéma, de dessin, de vidéo, des images numériques (dessin, 3D, effets spéciaux).'<br>Source: http://fr.wikipedia.org/wiki/Compositing</b></p><p>Dans l'onglet <b>'Image(s) avec canal alpha'</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). Passez maintenant dans l'onglet <b>'Image(s) sans canal alpha'</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).</p><p>Dans <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>. Cliquez sur le bouton <b>'Voir le résultat'</b> vous voyez à ce moment là, le résultat du compositing entre la dernière image du lot de votre premier groupe d'image (images avec transparence) et la première image du lot de votre second groupe d'image (images sans transparence), s'afficher dans une nouvelle fenêtre.</p>Pour finir cliquez sur le bouton <b>'Appliquer et sauver'</b>, entrez le titre de votre futur compositing (après <b>'Nom de fichier'</b>) dans cette dernière boîte (vous aurez évidemment pris soin de sélectionner le répertoire de destination de votre compositing). Cliquez sur le bouton <b>'Enregistrer'</b>.<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.afficheurImgSourceAvecCanalAlpha.saveFileLocation(self.idSection, u'sourcesa') self.afficheurImgSourceSansCanalAlpha.saveFileLocation(self.idSection, u'sourcessa') EkdConfig.set(self.idSection, u'spin1', unicode(self.spin1.value())) EkdConfig.set(self.idSection, u'spin2', unicode(self.spin2.value())) def load(self) : self.afficheurImgSourceAvecCanalAlpha.loadFileLocation(self.idSection, u'sourcesa') self.afficheurImgSourceSansCanalAlpha.loadFileLocation(self.idSection, u'sourcessa') self.spin1.setValue(int(EkdConfig.get(self.idSection, u'spin1'))) self.spin2.setValue(int(EkdConfig.get(self.idSection, u'spin2')))
def appliquer(self, nomSortie=None, ouvert=1): """appel du moteur de ekd -> encodage""" if not nomSortie: index=self.combo.currentIndex() chemin = unicode(self.getFile()) # suffix du codec actif suffix=self.listeCombo[index][4] if suffix=='': suffix=os.path.splitext(chemin)[1] ext_chargee = os.path.splitext(chemin)[1] codec_reglage = self.listeCombo[index][1] # ------------------------------------------------------------------- # # Gestion des fichiers mod (extension .mod). Ce sont (apparemment) # des fichiers mpeg avec une extension .mod. Les fichiers en question # ont juste besoin d'être renommés avec une extension .mpg avant le # traitement. # ------------------------------------------------------------------- # nom_fich_sans_ext = os.path.splitext(os.path.basename(chemin))[0] if ext_chargee in ['.mod', '.MOD']: # Copie du fichier concerné dans le rep tampon et renommage avec ext .mpg shutil.copy(chemin, self.repTempFichiersMod+nom_fich_sans_ext + '.mpg') chemin = unicode(self.repTempFichiersMod + nom_fich_sans_ext + '.mpg') saveDialog = EkdSaveDialog(self, mode="video", suffix=suffix, title=_(u"Sauver")) cheminFichierEnregistrerVideo = saveDialog.getFile() else: # module séquentiel chemin=cheminFichierEnregistrerVideo=nomSortie if not cheminFichierEnregistrerVideo: return # quel est l'index du dernier item sélectionné de la boîte de combo? i=self.combo.currentIndex() # identifiant du codec actif idCodec=str(self.combo.itemData(i).toStringList()[0]) # méthode de QVariant # pas de spin pour Copie (format original) - AVI-RAW Sans Compression # Codec XVID - Qualite SVCD - Qualite DVD - Codec H264 MPEG 4 spin=None # par défaut for j in self.listeCombo: # attribution de la valeur du spin s'il existe if j[0]==idCodec and j[5]==1: if j[0] in ['codecmotionjpeg', 'codecmpeg2', 'codech264mpeg4', 'codech264mpeg4_ext_h264', 'codecdivx4', 'codecmpeg1', 'macromediaflashvideo', 'codecwmv2']: # La valeur de compression vidéo va de 31 à 1 (31 --> mauvaise qualité # jusqu'à à 1 --> très bonne qualité). Ici on va calculer en mettant # en place un réglage qui partira de 1 à 100 (1 --> mauvaise qualité # et 100 --> très bonne qualité) calculValComp = (31 * j[3].spinCompression.value()) / 100 # Il faut soustraire le résultat à 31 pour # avoir une valeur reconnue par Mencoder calculValComp = 31 - calculValComp # Si la la valeur de réglage de la compression est réglée sur # 100, la valeur retournée sera 0 (ce qui va générer une erreur # dans Mencoder ... et ainsi stopper le process), la valeur # retournée sera donc de 1 (ce qui sera ok) if j[3].spinCompression.value() == 100: calculValComp = 1 # Dimension de la vidéo (preset). Exception pour contrôler # les cas où l'utilisateur laisse par défaut: # Pas de changement (vidéo avec la même taille que l'original) try: valeurSizeGeneral = str(j[3].idComboGeneral) except: valeurSizeGeneral = '' # La valeur est sous forme de tuple et contient au moins 3 valeurs spin = (str(j[3].spinBitrateVideo.value()), str(calculValComp), str(valeurSizeGeneral)) elif j[0] in ['codec_vob_ffmpeg']: # La valeur de compression vidéo va de 255 à 1 (255 --> mauvaise qualité # jusqu'à 1 --> très bonne qualité). Ici on va calculer en mettant # en place un réglage qui partira de 1 à 100 (1 --> mauvaise qualité # et 100 --> très bonne qualité) calculValComp = (255 * j[3].spinCompression.value()) / 100 # Il faut soustraire le résultat à 255 pour # avoir une valeur reconnue par Mencoder calculValComp = 255 - calculValComp # Si la la valeur de réglage de la compression est réglée sur # 100, la valeur retournée sera 0 (ce qui va générer une erreur # dans Mencoder ... et ainsi stopper le process), la valeur # retournée sera donc de 1 (ce qui sera ok) if j[3].spinCompression.value() == 100: calculValComp = 1 # On attribue la valeur spin = str(calculValComp) elif j[0] in ['codec_3GP_ffmpeg']: # On affecte la valeur de la résolution pour le 3GP # sélectionnée par l'utilisateur self.valeurSize3GP = j[3].resoSortie3gp.currentText() elif j[0] in ['codec_AMV_ffmpeg']: # On affecte la valeur de la résolution pour l'AMV # sélectionnée par l'utilisateur self.valeurSizeAMV = j[3].resoSortieAMV.currentText() elif j[0] in ['codec_mov_ffmpeg']: # La valeur de compression vidéo va de 31 à 1 (31 --> mauvaise qualité # jusqu'à à 1 --> très bonne qualité). Ici on va calculer en mettant # en place un réglage qui partira de 1 à 100 (1 --> mauvaise qualité # et 100 --> très bonne qualité) calculValComp = (31 * j[3].spinCompression.value()) / 100 # Il faut soustraire le résultat à 31 pour # avoir une valeur reconnue par Mencoder calculValComp = 31 - calculValComp # Si la la valeur de réglage de la compression est réglée sur # 100, la valeur retournée sera 0 (ce qui va générer une erreur # dans Mencoder ... et ainsi stopper le process), la valeur # retournée sera donc de 1 (ce qui sera ok) if j[3].spinCompression.value() == 100: calculValComp = 1 # Appel de la fonction dans laquelle sont définies les tailles # des vidéos pour un traitement pour un changement de syntaxe self.tailleVideoRetouche() # La valeur est sous forme de tuple et contient au moins 2 valeurs self.valeurCompSizeMOV = (str(calculValComp), str(self.valeurSizeGeneral)) elif j[0] in ['avirawsanscompression']: # Appel de la fonction dans laquelle sont définies les tailles # des vidéos pour un traitement pour un changement de syntaxe self.tailleVideoRetouche() self.valeurSizeAVI_RAW = str(self.valeurSizeGeneral) elif j[0] in ['codecxvid']: # Appel de la fonction dans laquelle sont définies les tailles # des vidéos pour un traitement pour un changement de syntaxe self.tailleVideoRetouche() valeurSizeXVID = str(self.valeurSizeGeneral) # La valeur est sous forme de tuple et contient au moins 2 valeurs spin = (str(j[3].spinBitrateVideo.value()), valeurSizeXVID) elif j[0] in ['codec_hfyu_ffmpeg']: # Appel de la fonction dans laquelle sont définies les tailles # des vidéos pour un traitement pour un changement de syntaxe self.tailleVideoRetouche() self.valeurSizeHFYU = str(self.valeurSizeGeneral) else: spin = str(j[3].spinCompression.value()) debug( "%s %s %s %s" % (idCodec, chemin, spin, type(spin))) # Collecte des infos codec audio infosCodecAudio = {'Fichier':chemin} getParamVideo(chemin, ['ID_AUDIO_CODEC'], infosCodecAudio) try: audioCodec = infosCodecAudio['ID_AUDIO_CODEC'] except: audioCodec = "codec audio non disponible" # Appel des classes après séparation des traitements gérés par Mencoder, FFmpeg # et FFmpeg2theora. # Appel du moteur -> animation encodage avec un codec particulier try: if idCodec in ['copie', 'codecdivx4', 'codecmotionjpeg', 'codecmpeg1', 'codecmpeg2', 'codecwmv2', 'codecxvid', 'macromediaflashvideo', 'codech264mpeg4', 'codech264mpeg4_ext_h264']: mencoder = WidgetMEncoder(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = spin, laisserOuvert=ouvert) mencoder.setWindowTitle(_(u"Transcodage vidéo")) mencoder.exec_() elif idCodec in ['avirawsanscompression']: mencoder = WidgetMEncoder(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = self.valeurSizeAVI_RAW, laisserOuvert=ouvert) mencoder.setWindowTitle(_(u"Transcodage vidéo")) mencoder.exec_() elif idCodec in ['codecxvid']: mencoder = WidgetMEncoder(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = spin, laisserOuvert=ouvert) mencoder.setWindowTitle(_(u"Transcodage vidéo")) mencoder.exec_() elif idCodec in ['codec_hfyu_ffmpeg']: ffmpeg = WidgetFFmpeg(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = self.valeurSizeHFYU, laisserOuvert=ouvert) ffmpeg.setWindowTitle(_(u"Transcodage vidéo")) ffmpeg.exec_() elif idCodec == 'codec_3GP_ffmpeg': # Pour le transcodage 3GP, si on charge une vidéo qui # comporte un canal audio, le transcodage n'a pas lieu if audioCodec == "codec audio non disponible": ffmpeg = WidgetFFmpeg(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = self.valeurSize3GP, laisserOuvert=ouvert) ffmpeg.setWindowTitle(_(u"Transcodage vidéo")) ffmpeg.exec_() else: messErr3GP=QMessageBox(self) messErr3GP.setText(_(u"<p>Le transcodage des vidéos comportant une piste audio en 3GP a momentanément été désactivé.</b></p>")) messErr3GP.setWindowTitle(_(u"Erreur")) messErr3GP.setIcon(QMessageBox.Warning) messErr3GP.exec_() return elif idCodec == 'codec_AMV_ffmpeg': ffmpeg = WidgetFFmpeg(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = self.valeurSizeAMV, laisserOuvert=ouvert) ffmpeg.setWindowTitle(_(u"Transcodage vidéo")) ffmpeg.exec_() elif idCodec == 'codec_mov_ffmpeg': ffmpeg = WidgetFFmpeg(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = self.valeurCompSizeMOV, laisserOuvert=ouvert) ffmpeg.setWindowTitle(_(u"Transcodage vidéo")) ffmpeg.exec_() elif idCodec == 'codecoggtheora': #### Gestion de l'extension .h264 #### # Si on charge une vidéo avec extension .h264, ffmpeg2theora ne peut # pas effectuer l'encodage if ext_chargee == '.h264': messErrExtH264ffmpeg2th=QMessageBox(self) messErrExtH264ffmpeg2th.setText(_(u"<p>Il n'est pas possible de donner suite au traitement <b>à partir d'une vidéo avec extension h264 et en ayant choisi: %s.</b></p><p><b>Veuillez choisir un autre codec dans la liste proposée.</b></p>" % codec_reglage)) messErrExtH264ffmpeg2th.setWindowTitle(_(u"Erreur")) messErrExtH264ffmpeg2th.setIcon(QMessageBox.Warning) messErrExtH264ffmpeg2th.exec_() return else: ffmpeg2theora = WidgetFFmpeg2theora(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = spin, laisserOuvert=ouvert) ffmpeg2theora.setWindowTitle(_(u"Transcodage vidéo")) ffmpeg2theora.exec_() else: #### Gestion de l'extension .h264 #### # Si on charge une vidéo avec extension .h264, FFmpeg ne peut # pas effectuer l'encodage if ext_chargee == '.h264': if codec_reglage in [u'Codec DV (.dv)', u'Codec QuickTime MOV (.mov)', u'Codec HFYU: Huffman Lossless YUV (yuv422p) (.avi)', u'Codec VOB (DVD-Video stream MPEG-2) (.vob)', u'Codec 3GP (3rd Generation Partnership Project) (.3gp)', u'Codec AMV: pour lecteurs mp4 (.avi)']: messErrExtH264ffmpeg=QMessageBox(self) messErrExtH264ffmpeg.setText(_(u"<p>Il n'est pas possible de donner suite au traitement <b>à partir d'une vidéo avec extension h264 et en ayant choisi: %s.</b></p><p><b>Veuillez choisir un autre codec dans la liste proposée.</b></p>" % codec_reglage)) messErrExtH264ffmpeg.setWindowTitle(_(u"Erreur")) messErrExtH264ffmpeg.setIcon(QMessageBox.Warning) messErrExtH264ffmpeg.exec_() return else: ffmpeg = WidgetFFmpeg(idCodec, chemin, cheminFichierEnregistrerVideo, valeurNum = spin, laisserOuvert=ouvert) ffmpeg.setWindowTitle(_(u"Transcodage vidéo")) ffmpeg.exec_() #except Exception, e: except None: messageErreur=QMessageBox(self) messageErreur.setText(_(u"Un problème est survenu.")+str(e)) messageErreur.setWindowTitle(_(u"Erreur")) messageErreur.setIcon(QMessageBox.Warning) messageErreur.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 cheminFichierEnregistrerVideo # module séquentiel
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')))
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 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 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 appliquer(self, nomSortie=None, ouvert=1): "Appel du moteur du cadre" if not nomSortie: chemin = self.getFile() self.chemins = chemin # suffix du codec actif (et collecte de l'extension en sortie) if self.codecSortie.currentText() == "MOV (.mov)": self.suffix_sortie = '.mov' elif self.codecSortie.currentText() == "VOB (.vob)": self.suffix_sortie = '.vob' elif self.codecSortie.currentText() == "MPEG4 (.mp4)": self.suffix_sortie = '.mp4' elif self.codecSortie.currentText() == "WMV2 (.wmv)": self.suffix_sortie = '.wmv' ### Ajouté le 13/08/2010 ################################################### elif self.codecSortie.currentText() == "Avid DNxHD (.mov)": self.suffix_sortie = '.mov' ############################################################################ elif self.codecSortie.currentText() in ["MPEG1 (.mpg)", "MPEG2 (.mpg)"]: self.suffix_sortie = '.mpg' else: self.suffix_sortie = '.avi' # Sélection du codec pour la sortie self.codec_sortie = str(self.codecSortie.currentText()) # Sélection de la résolution pour la vidéo en sortie (Largeur x Hauteur) resolution_sortie_texte = str(self.resoSortie.currentText()) resol_l_h = resolution_sortie_texte.split('x') self.reso_largeur_sortie = str(resol_l_h[0]) self.reso_hauteur_sortie = str(resol_l_h[1]) #print 'Largeur', self.reso_largeur_sortie, type(self.reso_largeur_sortie) EkdPrint(u'Largeur %s %s' % (self.reso_largeur_sortie, type(self.reso_largeur_sortie))) #print 'Hauteur', self.reso_hauteur_sortie, type(self.reso_hauteur_sortie) EkdPrint(u'Hauteur %s %s' % (self.reso_hauteur_sortie, type(self.reso_hauteur_sortie))) # Nombre d'images par secondes sélectionnées pour la sortie self.nbreImgSec_sortie = str(self.nbrImage.value()) # Valeur de la qualité de la vidéo pour la sortie self.qualite_sortie = str(self.qualite.value()) ### Ajouté le 13/08/2010 ################################################### # Spécifications Avid DNxHD (.mov) self.spec_sortie_DNxHD = str(self.specSortie_DNxHD.currentText()) # Choix du flux audio en sortie pour Avid DNxHD self.son_sortie_DNxHD = str(self.sonSortie_DNxHD.currentText()) ############################################################################ # suffix du codec actif saveDialog = EkdSaveDialog(self, mode="video", suffix=self.suffix_sortie, title=_(u"Sauver")) self.cheminFichierEnregistrerVideo = saveDialog.getFile() # A revoir mais là le fichier est nommé comme il faut (!) pre_cheminFichierEnregistrerVideo = self.cheminFichierEnregistrerVideo.split('.') self.cheminFichierEnregistrerVideo = pre_cheminFichierEnregistrerVideo[0] else: # module séquentiel self.cheminFichierEnregistrerVideo = nomSortie if not self.cheminFichierEnregistrerVideo: return # quel est l'index du dernier item sélectionné de la boîte de combo? #print 'index du codec sélectionné', self.codecSortie.currentIndex() EkdPrint(u'index du codec sélectionné %s' % self.codecSortie.currentIndex()) ### Rectification le 14/08/2010 ############################################ if self.codecSortie.currentText() == "Avid DNxHD (.mov)": # Affectation des valeurs de nouvelle largeur et nouvelle hauteur # sélectionnées par l'utilisateur pour le traitement final self.reso_largeur_sortie = "" self.reso_hauteur_sortie = "" # self.qualite_sortie = "" elif self.codecSortie.currentText() != "Avid DNxHD (.mov)": # Si l'utilisateur sélectionne une autre option que Avid DNxHD (.mov) # ces valeurs sont définies comme vides (ça fonctionne bien) self.spec_sortie_DNxHD = "" self.son_sortie_DNxHD = "" ############################################################################ # identifiant du codec actif # Appel de la classe try: ### Ajout (le 13/08/2010) de self.spec_sortie_DNxHD, self.son_sortie_DNxHD # wfa = WidgetFFmpegAvchd(self.chemins, self.cheminFichierEnregistrerVideo, self.codec_sortie, self.reso_largeur_sortie, self.reso_hauteur_sortie, self.nbreImgSec_sortie, self.qualite_sortie, self.spec_sortie_DNxHD, self.son_sortie_DNxHD, self) wfa.setWindowTitle(_(u"Traitement AVCHD (FFmpeg)")) wfa.exec_() ############################################################################ except: messageErrWfa=QMessageBox(self) messageErrWfa.setText(_(u"Problème lors de la conversion des fichiers (FFmpeg)\n")) messageErrWfa.setWindowTitle(_(u"Erreur")) messageErrWfa.setIcon(QMessageBox.Warning) messageErrWfa.exec_() return
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)