Fichier .exe compilé disponible pour Windows.
Interface avec WX, traitement d'image avec PIL.
import os
import sys
import wx
import wx.animate
import wx.lib.platebtn as btnplat
import shutil
import random
import PIL
import struct as st
from PIL import Image as PILImage
from PIL import ImageTk
from PIL.ExifTags import TAGS
class DialogueInfos(wx.Dialog) :
FENETRE = None
exif = None
def dicoTexte(val) :
try : return unicode(val)
except UnicodeDecodeError : print val
return ""
def dicoCommentaire(val) :
return str(len(val)) + u" caractères à décoder."
def dicoDate(val) :
val = val.split(" ")
date = val[0].split(":")
heure = val[1].split(":")
retour = date[2] + "/" + date[1] + "/" + date[0] + " " + heure[0] + ":" + heure[1] + ":" + heure[2][0] + heure[2][1]
return retour
def dicoVide(val) :
return ""
dicoMeta = {
'36864' : ['ExifVersion', u'Version Exif', dicoTexte],
'37121' : ['ComponentsConfiguration', u'Composants de configuration', dicoVide],
'37122' : ['CompressedBitsPerPixel', u'Compression octet par pixel', dicoTexte],
'36867' : ['DateTimeOriginal', u'Date et heure d\'origine', dicoDate],
'36868' : ['DateTimeDigitized', u'Date et heure numérisés', dicoDate],
'37381' : ['MaxApertureValue', u'Ouverture max', dicoTexte],
'40960' : ['FlashPixVersion', u'Version de FlashPix', dicoTexte],
'37383' : ['MeteringMode', u'', dicoTexte],
'37384' : ['LightSource', u'Source de lumière', dicoTexte],
'37385' : ['Flash', u'Flash', dicoTexte],
'37386' : ['FocalLength', u'Distance focale', dicoTexte],
'41996' : ['41996', u'41996', dicoTexte],
'37378' : ['ApertureValue', u'Ouverture', dicoTexte],
'41486' : ['FocalPlaneXResolution', u'Hauteur du foyer', dicoTexte],
'271' : ['Make', u'Marque', dicoTexte],
'272' : ['Model', u'Modèle', dicoTexte],
'274' : ['Orientation', u'Orientation', dicoTexte],
'531' : ['YCbCrPositioning', u'', dicoTexte],
'41495' : ['SensingMethod', u'', dicoTexte],
'33432' : ['Copyright', u'Tous droits réservés', dicoTexte],
'282' : ['XResolution', u'Résolution X', dicoTexte],
'283' : ['YResolution', u'Résolution Y', dicoTexte],
'33434' : ['ExposureTime', u'Durée d\exposition', dicoTexte],
'41728' : ['FileSource', u'', dicoVide],
'40965' : ['ExifInteroperabilityOffset', u'', dicoTexte],
'34850' : ['ExposureProgram', u'Programme d\'exposition', dicoTexte],
'40961' : ['ColorSpace', u'Espace colorimétrique', dicoTexte],
'41990' : ['41990', u'', dicoTexte],
'34855' : ['ISOSpeedRatings', u'ISO', dicoTexte],
'296' : ['ResolutionUnit', u'Unité de la résolution', dicoTexte],
'41987' : ['41987', u'', dicoTexte],
'33437' : ['FNumber', u'Indice d\'ouverture (F)', dicoTexte],
'305' : ['Software', u'Logiciel', dicoTexte],
'306' : ['DateTime', u'Date et heure', dicoDate],
'41729' : ['SceneType', u'Type de scène', dicoVide],
'41994' : ['41994', u'', dicoTexte],
'40962' : ['ExifImageWidth', u'Largeur en pixels', dicoTexte],
'41985' : ['41985', u'', dicoTexte],
'41487' : ['FocalPlaneYResolution', u'Largeur du foyer', dicoTexte],
'40963' : ['ExifImageHeight', u'Hauteur en pixels', dicoTexte],
'41488' : ['FocalPlaneResolutionUnit', u'Résolution du foyer', dicoTexte],
'41986' : ['41986', u'', dicoTexte],
'34665' : ['ExifOffset', u'', dicoTexte],
'37500' : ['MakerNote', u'', dicoVide],
'37510' : ['UserComment', u'Commentaire', dicoCommentaire],
'40092' : ['40092', u'', dicoTexte],
'40091' : ['40091', u'', dicoTexte],
'270' : ['ImageDescription', u'Description', dicoTexte],
'41992' : ['41992', u'', dicoTexte],
'41988' : ['41988', u'', dicoTexte],
'41991' : ['41991', u'', dicoTexte],
'41993' : ['41993', u'', dicoTexte],
'41989' : ['41989', u'', dicoTexte],
'256' : ['ImageWidth', u'Largeur', dicoTexte],
'257' : ['ImageLength', u'Hauteur', dicoTexte],
'258' : ['BitsPerSample', u'Bits par échantillon', dicoTexte],
'259' : ['Compression', u'Compression', dicoTexte],
'37520' : ['SubsecTime', u'Sous-seconde', dicoTexte],
'37521' : ['SubsecTimeOriginal', u'Sous-seconde d\'origine', dicoTexte],
'262' : ['PhotometricInterpretation', u'Interprétation photométrique', dicoTexte],
'277' : ['SamplesPerPixel', u'Échantillons par pixel', dicoTexte],
'284' : ['PlanarConfiguration', u'Configuration planaire', dicoTexte],
'37382' : ['SubjectDistance', u'Distance du sujet', dicoTexte],
'37522' : ['SubsecTimeDigitized', u'Sous-seconde numérisée', dicoTexte],
'34853' : ['GPSInfo', u'Infos GPS', dicoTexte],
'315' : ['Artist', u'Artiste', dicoTexte],
'41730' : ['CFAPattern', u'', dicoTexte],
'42034' : ['42034', u'', dicoTexte],
'42033' : ['42033', u'', dicoTexte],
'50341' : ['50341', u'', dicoTexte],
'42032' : ['42032', u'', dicoTexte],
'42036' : ['Lens', u'Objectif', dicoTexte],
'34866' : ['34866', u'', dicoTexte],
'34864' : ['34864', u'', dicoTexte],
'42037' : ['42037', u'', dicoTexte],
'529' : ['YCbCrCoefficients', u'', dicoTexte],
'42240' : ['42240', u'', dicoTexte],
'318' : ['WhitePoint', u'Point blanc', dicoTexte],
'319' : ['PrimaryChromaticities', u'', dicoTexte],
'' : ['', u'', dicoTexte]
}
def __init__(self, parent, pos=(-1,-1)):
wx.Dialog.__init__(self, parent, pos=pos)
self.FENETRE = parent
self.exif = parent.exif
self.InitInterface()
self.SetSize((400, 670))
self.SetTitle(u"Informations")
self.style = wx.NO_BORDER
def InitBouton(self, txt="", gros=False) :
btn = btnplat.PlateButton(self.panneau, wx.ID_ANY, txt, None, style=btnplat.PB_STYLE_SQUARE)
if gros == 1 : btn.SetFont(wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD))
elif gros == 2 : btn.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD))
elif gros == -1 : btn.SetFont(wx.Font(9, wx.MODERN, wx.NORMAL, wx.BOLD))
else : btn.SetFont(wx.Font(8, wx.FONTFAMILY_SWISS, wx.NORMAL, wx.NORMAL))
btn.SetLabelColor(wx.Colour(230, 230, 230))
btn.SetPressColor(wx.Colour(40, 40, 40))
btn.SetBackgroundColour(wx.Colour(20, 20, 20))
return btn
def InitInterface(self):
self.panneau = wx.Panel(self)
self.boite = wx.GridBagSizer()
gros = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
self.panneau.SetBackgroundColour(wx.Colour(20, 20, 20))
btnGauche = self.InitBouton(u' ◄ ', 1)
self.boite.Add(btnGauche, (0,0), (1,1), wx.ALIGN_CENTER)
btnGauche.Bind(wx.EVT_BUTTON, self.eveBtnGauche)
self.texte = wx.TextCtrl(self.panneau, size=(-1,600), style=wx.NO_BORDER|wx.TE_MULTILINE)
self.boite.Add(self.texte, (1,0), (1,1), wx.EXPAND|wx.ALL, border=5)
self.texte.SetBackgroundColour(wx.Colour(40, 40, 40))
self.texte.SetForegroundColour(wx.Colour(230, 230, 230))
self.TraitementMeta()
self.boite.AddGrowableCol(0)
self.boite.AddGrowableCol(1)
# self.boite.AddGrowableRow(0)
wx.EVT_CLOSE(self, self.eveFerme)
self.panneau.SetSizer(self.boite)
self.Layout()
def TraitementMeta(self) :
exif = self.exif
if not exif :
self.texte.Value = 'Aucune information EXIF.'
return
liste = []
self.texte.Value = ""
i = 1
for clef, valeur in exif.items():
if not str(clef) in self.dicoMeta :
print clef, TAGS.get(clef, clef), valeur
continue
motdic = TAGS.get(clef, clef)
motdic = self.dicoMeta[str(clef)]
if motdic[1] == '' : mot = "***" + motdic[0]
else : mot = motdic[1]
fnc = motdic[2]
ti = str(i)
if i < 10 : ti = "0" + str(i)
entree = ti + ". " + mot + " : " + fnc(valeur)
liste.append(entree)
i += 1
imax = len(liste)
liste.sort()
liste = '\n'.join(liste)
self.texte.Value = liste + "\n" + str(imax)
# print ''.join(liste)
# return ''
def eveBtnGauche(self, eve) :
pass
def eveFerme(self, eve) :
self.Destroy()
class Dialogue(wx.Dialog) :
FENETRE = None
verrsuppr = False
plus = False
def __init__(self, parent, pos):
wx.Dialog.__init__(self, parent, pos=pos)
self.FENETRE = parent
self.InitInterface()
self.SetSize((208, 135))
self.SetTitle(u"Contrôle")
self.style = wx.NO_BORDER
def InitBouton(self, txt="", gros=False, coul=[0,0,0]) :
btn = btnplat.PlateButton(self.panneau, wx.ID_ANY, txt, None, style=btnplat.PB_STYLE_SQUARE)
if gros == 1 : btn.SetFont(wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD))
elif gros == 2 : btn.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD))
elif gros == -1 : btn.SetFont(wx.Font(9, wx.MODERN, wx.NORMAL, wx.BOLD))
else : btn.SetFont(wx.Font(8, wx.FONTFAMILY_SWISS, wx.NORMAL, wx.NORMAL))
if coul[0] == 0 : btn.SetLabelColor(wx.Colour(230, 230, 230))
if coul[1] == 0 : btn.SetPressColor(wx.Colour(40, 40, 40))
if coul[2] == 0 : btn.SetBackgroundColour(wx.Colour(20, 20, 20))
elif coul[2] == 1 : btn.SetBackgroundColour(wx.Colour(40, 40, 40))
elif coul[2] == 2: btn.SetBackgroundColour(wx.Colour(100, 20, 20))
return btn
def InitInterface(self):
self.panneau = wx.Panel(self)
self.boite = wx.GridBagSizer()
gros = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
self.panneau.SetBackgroundColour(wx.Colour(20, 20, 20))
btnGauche = self.InitBouton(u' ◄ ', 1)
self.boite.Add(btnGauche, (0,0), (1,1), wx.EXPAND)
btnGauche.Bind(wx.EVT_BUTTON, self.eveBtnGauche)
btnDroite = self.InitBouton(u' ► ', 1)
self.boite.Add(btnDroite, (0,1), (1,1), wx.EXPAND)
btnDroite.Bind(wx.EVT_BUTTON, self.eveBtnDroite)
btnZoomM = self.InitBouton(' - ', 2)
self.boite.Add(btnZoomM, (1,0), (1,1), wx.EXPAND)
btnZoomM.Bind(wx.EVT_BUTTON, self.eveBtnZoomM)
btnZoomP = self.InitBouton(' + ', 2)
self.boite.Add(btnZoomP, (1,1), (1,1), wx.EXPAND)
btnZoomP.Bind(wx.EVT_BUTTON, self.eveBtnZoomP)
btnTaille = self.InitBouton(u' Ajuster la taille ')
self.boite.Add(btnTaille, (2,0), (1,1), wx.EXPAND)
btnTaille.Bind(wx.EVT_BUTTON, self.eveBtnTaille)
btnPleinEcran = self.InitBouton(u' Plein écran ')
self.boite.Add(btnPleinEcran, (2,1), (1,1), wx.EXPAND)
btnPleinEcran.Bind(wx.EVT_BUTTON, self.eveBtnPleinEcran)
self.btnSupprimer = self.InitBouton(u' Supprimer ')
self.boite.Add(self.btnSupprimer, (3,0), (1,1), wx.EXPAND)
self.btnSupprimer.Bind(wx.EVT_BUTTON, self.eveBtnSuppr)
if self.verrsuppr : self.btnSupprimer.SetBackgroundColour(wx.Colour(100, 20, 20))
btnFermer = self.InitBouton(u' Quitter ')
self.boite.Add(btnFermer, (3,1), (1,1), wx.EXPAND)
btnFermer.Bind(wx.EVT_BUTTON, self.eveBtnFermer)
btnPlus = self.InitBouton(u' ... ', -1, [0,0,1])
self.boite.Add(btnPlus, (4,0), (1,2), wx.EXPAND)
btnPlus.Bind(wx.EVT_BUTTON, self.eveBtnPlus)
btnPlus.SetLabelColor(wx.Colour(100, 100, 100))
btnVerrSuppr = self.InitBouton(u' Suppr. directe ')
self.boite.Add(btnVerrSuppr, (5,0), (1,1), wx.EXPAND)
btnVerrSuppr.Bind(wx.EVT_BUTTON, self.eveBtnVerrSuppr)
btnInfos = self.InitBouton(u' Infos ')
self.boite.Add(btnInfos, (5,1), (1,1), wx.EXPAND)
btnInfos.Bind(wx.EVT_BUTTON, self.eveBtnInfos)
self.btnFond = self.InitBouton(u' Fond ')
self.boite.Add(self.btnFond, (6,0), (1,1), wx.EXPAND)
self.btnFond.Bind(wx.EVT_BUTTON, self.eveBtnFond)
self.eveBtnFond(None, False)
self.btnExif = self.InitBouton(u' Utiliser EXIF ')
self.boite.Add(self.btnExif, (6,1), (1,1), wx.EXPAND)
self.btnExif.Bind(wx.EVT_BUTTON, self.eveBtnExif)
if self.FENETRE.verrexif : self.btnExif.SetBackgroundColour(wx.Colour(100, 20, 20))
self.btnAleat = self.InitBouton(u' Aléatoire')
self.boite.Add(self.btnAleat, (7,0), (1,1), wx.EXPAND)
self.btnAleat.Bind(wx.EVT_BUTTON, self.eveBtnAleat)
if self.FENETRE.verraleat : self.btnAleat.SetBackgroundColour(wx.Colour(100, 20, 20))
self.btnFigzo = self.InitBouton(u' Figer le zoom')
self.boite.Add(self.btnFigzo, (7,1), (1,1), wx.EXPAND)
self.btnFigzo.Bind(wx.EVT_BUTTON, self.eveBtnFigzo)
if self.FENETRE.verrzoom : self.btnFigzo.SetBackgroundColour(wx.Colour(100, 20, 20))
btnEnr = self.InitBouton(u' Exporter')
self.boite.Add(btnEnr, (8,0), (1,1), wx.EXPAND)
btnEnr.Bind(wx.EVT_BUTTON, self.eveBtnEnr)
btnEnrLot = self.InitBouton(u' Export par lot')
self.boite.Add(btnEnrLot, (8,1), (1,1), wx.EXPAND)
btnEnrLot.Bind(wx.EVT_BUTTON, self.eveBtnEnrLot)
self.boite.AddGrowableCol(0)
self.boite.AddGrowableCol(1)
# self.boite.AddGrowableRow(0)
wx.EVT_CLOSE(self, self.eveFerme)
self.panneau.SetSizer(self.boite)
self.Layout()
def eveBtnEnrLot(self, eve) :
self.FENETRE.ExportLot()
def eveBtnEnr(self, eve) :
self.FENETRE.Exporter()
def eveBtnFigzo(self, eve) :
self.FENETRE.verrzoom = not self.FENETRE.verrzoom
if self.FENETRE.verrzoom : self.btnFigzo.SetBackgroundColour(wx.Colour(100, 20, 20))
else : self.btnFigzo.SetBackgroundColour(wx.Colour(20, 20, 20))
self.btnFigzo.Refresh()
def eveBtnAleat(self, eve) :
self.FENETRE.verraleat = not self.FENETRE.verraleat
if self.FENETRE.verraleat : self.btnAleat.SetBackgroundColour(wx.Colour(100, 20, 20))
else : self.btnAleat.SetBackgroundColour(wx.Colour(20, 20, 20))
self.btnAleat.Refresh()
def eveBtnFond(self, eve, affecter=True) :
coul = self.FENETRE.fond
if affecter :
if coul == 0 : coul = 20
elif coul == 20 : coul = 127
elif coul == 127 : coul = 255
elif coul == 255 : coul = 0
self.FENETRE.ModifFond(coul)
self.btnFond.SetBackgroundColour(wx.Colour(coul, coul, coul))
self.btnFond.SetPressColor(wx.Colour(coul, coul, coul))
def eveBtnExif(self, eve, forcer=False) :
if not forcer : self.FENETRE.verrexif = not self.FENETRE.verrexif
else : self.FENETRE.verrexif = True
if self.FENETRE.verrexif :
self.btnExif.SetBackgroundColour(wx.Colour(100, 20, 20))
if not self.FENETRE.exif : self.FENETRE.MetaDonnees()
else :
self.btnExif.SetBackgroundColour(wx.Colour(20, 20, 20))
self.FENETRE.exif = None
self.btnExif.Refresh()
def eveBtnInfos(self, eve) :
if self.FENETRE.DIALINFOS :
self.FENETRE.DIALINFOS.Destroy()
return
self.eveBtnExif(None, True)
pos = (wx.DisplaySize()[0] - 420, 20)
self.FENETRE.DIALINFOS = DialogueInfos(self.FENETRE, pos)
self.FENETRE.DIALINFOS.Show()
def eveBtnVerrSuppr(self, eve) :
self.verrsuppr = not self.verrsuppr
if self.verrsuppr : self.btnSupprimer.SetBackgroundColour(wx.Colour(100, 20, 20))
else : self.btnSupprimer.SetBackgroundColour(wx.Colour(20, 20, 20))
self.btnSupprimer.Refresh()
def eveBtnPlus(self, eve) :
self.plus = not self.plus
if self.plus : self.SetSize((208, 300))
else : self.SetSize((208, 135))
self.Layout()
def eveBtnFermer(self, eve) :
self.Destroy()
self.FENETRE.Destroy()
def eveBtnSuppr(self, eve) :
self.FENETRE.Supprimer(self.verrsuppr)
def eveBtnZoomP(self, eve) :
self.FENETRE.AjusteZoom(100)
def eveBtnZoomM(self, eve) :
self.FENETRE.AjusteZoom(-100)
def eveBtnPleinEcran(self, eve) :
self.FENETRE.ModePleinEcran()
def eveBtnTaille(self, eve) :
self.FENETRE.AjusteTaille()
def eveBtnGauche(self, eve) :
self.FENETRE.Naviguer(-1)
def eveBtnDroite(self, eve) :
self.FENETRE.Naviguer(1)
def eveFerme(self, eve) :
self.Destroy()
class Fenetre(wx.Frame) :
liste = []
listeLong = 0
num = 0
echelle = 1.0
echelleOpt = 1.0
exts = ['bmp', 'gif', 'jpg', 'jpeg', 'png', 'pcx', 'pnm', 'tif', 'xpm', 'ico', 'cur', 'ani', 'tga', 'pix']
bordures = (0, 0)
pleinecran = False
DIALOGUE = None
DIALINFOS = None
posImg = (0, 0)
posImgDer = (0, 0)
exif = None
verrexif = False
verraleat = False
verrzoom = False
orientation = 1
fond = 20
image = None
def __init__(self, parent, id, title) :
wx.Frame.__init__(self, parent, id, title, size=[300, 500])
icone = wx.Icon("icone.ico", wx.BITMAP_TYPE_ICO)
self.SetIcon(icone)
self.SetBackgroundColour(wx.Colour(self.fond, self.fond, self.fond))
self.panneau = wx.Panel(self, -1, style=0)
self.anim = None
self.bitmap = wx.EmptyBitmap(1, 1)
self.staticbitmap = wx.StaticBitmap(self, bitmap = self.bitmap)
self.boite = wx.GridSizer()
# self.boite = wx.BoxSizer()
self.boite.Add(self.staticbitmap, 0, wx.ALIGN_CENTER | wx.ALL | wx.ADJUST_MINSIZE, 0)
self.panneau.Bind(wx.EVT_KEY_DOWN, self.eveClavier)
self.panneau.Bind(wx.EVT_MOUSEWHEEL, self.eveSourisMolette)
self.panneau.Bind(wx.EVT_RIGHT_UP, self.eveSourisClicDroit)
self.panneau.Bind(wx.EVT_MOTION, self.eveSourisBouge)
self.panneau.Bind(wx.EVT_LEFT_DOWN, self.eveSourisClicGauche)
self.panneau.Bind(wx.EVT_LEFT_UP, self.eveSourisDeclicGauche)
self.panneau.Bind(wx.EVT_LEFT_DCLICK, self.eveSourisDoubleClicGauche)
self.panneau.Bind(wx.EVT_MIDDLE_DOWN, self.eveSourisClicMilieu)
self.SetSizerAndFit(self.boite)
# self.panneau.SetSizerAndFit(self.boite)
self.Listeur()
wx.EVT_CLOSE(self, self.eveFerme)
self.Bind(wx.EVT_SIZE, self.eveRecadre)
self.Show(True)
def eveSourisClicMilieu(self, eve) :
self.AjusteTaille()
def eveSourisDoubleClicGauche(self, eve) :
self.ModePleinEcran()
def eveSourisDeclicGauche(self, eve) :
self.Update()
def eveSourisClicGauche(self, eve) :
posImage = self.staticbitmap.GetPosition()
posSouris = eve.GetPosition()
self.posImgDer = (posSouris[0] - posImage[0], posSouris[1] - posImage[1])
def eveSourisBouge(self, eve) :
if eve.LeftIsDown() :
posAns = self.posImgDer
posSouris = eve.GetPosition()
pos = (posSouris[0] - posAns[0], posSouris[1] - posAns[1])
self.staticbitmap.SetPosition(pos)
self.Update()
def eveSourisClicDroit(self, eve) :
if self.DIALOGUE :
self.DIALOGUE.Destroy()
return
posSouris = eve.GetPosition() + self.GetPosition()
posSouris[0] -= 104
posSouris[1] += 10
self.DIALOGUE = Dialogue(self, pos=posSouris)
self.DIALOGUE.Show()
def eveSourisMolette(self, eve) :
val = eve.GetWheelRotation()
self.AjusteZoom(val)
def eveClavier(self, eve) :
touche = eve.GetKeyCode()
if touche == wx.WXK_LEFT :
self.Naviguer(-1)
elif touche == wx.WXK_RIGHT :
self.Naviguer(1)
elif touche == wx.WXK_DELETE :
self.Supprimer()
def eveRecadre(self, eve) :
taille = self.GetSize()
self.panneau.SetSize(taille)
self.panneau.Layout()
def eveFerme(self, eve) :
self.Destroy()
def ModifFond(self, coul) :
self.fond = coul
self.SetBackgroundColour(wx.Colour(coul, coul, coul))
self.Refresh()
def MetaDonnees(self, lien=None) :
if not lien :
lien = self.liste[self.num]
if not os.path.exists(lien) : return
image = PILImage.open(lien)
exifbrut = None
self.orientation = 1
try : exifbrut = image._getexif()
except : pass
if exifbrut :
try : self.orientation = exifbrut[274]
except KeyError : pass
self.exif = exifbrut
def ExportLot(self) :
if not self.image : return
if not self.liste : return
source = self.liste[self.num]
if not source : return
dossier = os.path.dirname(source)
dossier = os.path.abspath(dossier)
dest = wx.DirDialog(self, defaultPath = dossier)
retour = dest.ShowModal()
if retour == 5101 : return
chemin = dest.GetPath()
if not chemin : return
for i, f in enumerate(self.liste) :
self.num = i
self.Charger()
nom = os.path.basename(f)
nom = os.path.splitext(nom)[0] + ".png"
chem = os.path.join(chemin, nom)
self.image.SaveFile(chem, wx.BITMAP_TYPE_PNG)
def Exporter(self) :
if not self.image : return
if not self.liste : return
source = self.liste[self.num]
if not source : return
dossier = os.path.dirname(source)
nom = os.path.basename(source)
nom = os.path.splitext(nom)[0]
ext = "PNG (*.png)|*.png"
selection = wx.FileDialog(self, 'Exporter', defaultDir = dossier, defaultFile = nom, wildcard = ext, style = wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
retour = selection.ShowModal()
chemin = selection.GetPath()
if not chemin : return
self.image.SaveFile(chemin, wx.BITMAP_TYPE_PNG)
def Supprimer(self, confirme=False) :
if not confirme :
if not self.BoiteMessageSuppr() : return
poub = os.path.normpath(os.environ['SYSTEMDRIVE'] + r"\\$Recycle.Bin\\")
if not os.path.exists(poub) : return
corb = os.listdir(poub)
if not corb : return
corb = os.path.join(poub, corb[0])
if not self.liste : return
source = self.liste[self.num]
fichier = os.path.basename(source)
dest = os.path.join(poub, fichier) # corb, fichier
# print source
# print corb
# print dest
# os.system("del " + source)
# print os.system("move " + source + " " + corb)
shutil.move(source, dest)
self.liste.pop(self.num)
self.listeLong -= 1
if not self.listeLong : return
self.num = self.num % self.listeLong
self.Charger()
def ModePleinEcran(self) :
if not self.pleinecran :
self.ShowFullScreen(True)
self.pleinecran = True
else :
self.ShowFullScreen(False)
self.pleinecran = False
self.Recadre()
def AjusteZoom(self, val) :
self.echelle += round(val * 0.002 * self.echelle, 3)
if self.echelle < 0.012 : self.echelle = 0.012
if self.echelle < 1.1 and self.echelle > 0.9 : self.echelle = 1.0
if self.echelle < self.echelleOpt + 0.037 and self.echelle > self.echelleOpt - 0.037 : self.echelle = self.echelleOpt
self.Zoom()
def AjusteTaille(self) :
if self.echelle == self.echelleOpt : self.echelle = 1.0
else : self.echelle = self.echelleOpt
self.Zoom()
def Naviguer(self, n) :
if not self.listeLong : return
if self.verraleat : self.num = random.randint(0, self.listeLong-1)
else : self.num = (self.num + n) % self.listeLong
self.Charger()
def BoiteMessageSuppr(self):
retour = wx.MessageBox(u'Voulez-vous supprimer cette image ?', 'Confirmation', wx.YES_NO | wx.ICON_QUESTION)
if retour == wx.YES : return True
elif retour == wx.NO : return False
return False
def Recadre(self) :
if not self.pleinecran :
self.Fit()
self.Center()
self.Layout()
self.Refresh()
def Retaille(self) :
tailleImg = self.staticbitmap.GetSize()
tailleMax = wx.DisplaySize()
bord = (15, 60)
if self.pleinecran : bord = (0, 0)
ratio = 1.0
ratio2 = 1.0
if tailleMax[0] - bord[0] < tailleImg[0] :
ratio = float(tailleMax[0] - bord[0]) / float(tailleImg[0])
if tailleMax[1] - bord[1] < tailleImg[1] :
ratio2 = float(tailleMax[1] - bord[1]) / float(tailleImg[1])
if ratio2 < ratio : ratio = ratio2
if ratio != 1.0 :
self.echelle = ratio
self.echelleOpt = ratio
self.Zoom()
return
self.echelleOpt = 1.0
self.Recadre()
def Zoom(self) :
l = self.image.GetWidth()
h = self.image.GetHeight()
e = self.echelle
# image = self.image.Rescale(l * e, h * e, 1)
if (e > 1) :
image = self.image.Scale(l * e, h * e, wx.IMAGE_QUALITY_NORMAL)
else :
image = self.image.Scale(l * e, h * e, wx.IMAGE_QUALITY_HIGH)
self.bitmap = wx.BitmapFromImage(image)
self.staticbitmap.SetBitmap(self.bitmap)
self.Recadre()
def Listeur(self) :
if len(sys.argv) == 1 : return
lien = os.path.relpath(sys.argv[1])
# lien = "Images\\e.jpg"
dossier = os.path.split(lien)
dossier = dossier[0]
if dossier == "" :
dossier = "."
lien = '.\\' + lien
contenu = os.listdir(dossier)
liste = []
exts = self.exts
for f in contenu :
if os.path.isdir(f) : continue
fex = os.path.splitext(f)
if len(fex) == 1 : continue
ext = (fex[1][1:]).lower()
if ext in exts : liste.append(os.path.join(dossier, f))
self.num = liste.index(lien)
self.liste = liste
self.listeLong = len(liste)
self.Charger()
def Charger(self) :
lien = self.liste[self.num]
if not os.path.exists(lien) : return
if self.verrexif : self.MetaDonnees(lien)
if self.DIALINFOS :
self.DIALINFOS.exif = self.exif
self.DIALINFOS.TraitementMeta()
self.SetTitle(os.path.os.path.basename(lien))
type = os.path.splitext(lien)
type = (type[1][1:]).lower()
if self.anim : self.anim.Destroy()
if type == 'gif' :
self.anim = wx.animate.GIFAnimationCtrl(self, wx.ID_ANY, lien, pos=(0, 0))
self.anim.Play()
self.image = wx.Image(lien, wx.BITMAP_TYPE_ANY)
self.bitmap = wx.BitmapFromImage(self.image)
self.staticbitmap.SetBitmap(self.bitmap)
elif type == 'jpg' or type == 'jpeg' :
self.image = wx.Image(lien, wx.BITMAP_TYPE_ANY)
self.image.InitAlpha()
self.bitmap = wx.BitmapFromImage(self.image)
self.staticbitmap.SetBitmap(self.bitmap)
elif type == 'tga' :
self.image = self.LireTGA(lien)
self.bitmap = wx.BitmapFromImage(self.image)
self.staticbitmap.SetBitmap(self.bitmap)
elif type == 'pix' :
self.image = self.LirePIX(lien)
self.bitmap = wx.BitmapFromImage(self.image)
self.staticbitmap.SetBitmap(self.bitmap)
else :
self.image = wx.Image(lien, wx.BITMAP_TYPE_ANY)
if not self.image.HasAlpha() : self.image.InitAlpha()
self.bitmap = wx.BitmapFromImage(self.image)
self.staticbitmap.SetBitmap(self.bitmap)
if self.verrzoom :
self.Zoom()
else :
self.echelle = 1.0
self.Retaille()
def LirePIX(self, lien) : # Carmageddon 2
img = wx.EmptyImage()
f = open(lien, "rb")
if not f : return img
# En-tête
f.read(16)
f.read(8)
type = st.unpack(">B", f.read(1))[0] # 3 : 8 bits, 5 : 16 bits, 18 : 16 bits alpha
f.read(2)
largeur = st.unpack(">H", f.read(2))[0]
hauteur = st.unpack(">H", f.read(2))[0]
f.read(6)
o = st.unpack(">B", f.read(1))[0]
while o : o = st.unpack(">B", f.read(1))[0]
f.read(4)
taille = st.unpack(">L", f.read(4))[0] # Nombre d'octets des données de pixel
nbpix = st.unpack(">L", f.read(4))[0] # Nombre de pixels
format = st.unpack(">L", f.read(4))[0] # Nombre de bits pour un pixel : 1 = 8 bits, 2 = 16 bits
# Pixels
pixel = []
img.Create(largeur, hauteur)
img.InitAlpha()
for i in xrange(nbpix) :
x = i % largeur
y = i / largeur
if type == 5 : # 16 bits
val = st.unpack(">H", f.read(2))[0]
pixel = []
pixel.append(((val & 0xF800) >> 11) << 3)
pixel.append(((val & 0x07E0) >> 5) << 2)
pixel.append(((val & 0x001F) >> 0) << 3)
img.SetRGB(x, y, pixel[0], pixel[1], pixel[2])
elif type == 18 : # 16 bits alpha
val = st.unpack(">H", f.read(2))[0]
pixel = []
pixel.append(((val & 0x0F00) >> 8) << 4)
pixel.append(((val & 0x00F0) >> 4) << 4)
pixel.append(((val & 0x000F) >> 0) << 4)
pixel.append(((val ) >> 12) << 4)
img.SetRGB(x, y, pixel[0], pixel[1], pixel[2])
img.SetAlpha(x, y, pixel[3])
return img
def LireTGA(self, lien) :
img = wx.EmptyImage()
f = open(lien, "rb")
if not f : return img
palette = []
# En-tête
id_taille = st.unpack("B", f.read(1))[0]
apalette = st.unpack("B", f.read(1))[0]
imagetype = st.unpack("B", f.read(1))[0] # ?, 8 bits nc pal, 16 24 32 bits nc BRV, 8 16 bits nc gris, idem 1 compressé, idem 2 c, idem 3 c
pal_deb = st.unpack("h", f.read(2))[0] # Index de la première couleur dans la palette
pal_long = st.unpack("h", f.read(2))[0] # Nombre de couleurs de la palette
pal_tail = st.unpack("B", f.read(1))[0] # Nombre de bits par couleur de la palette. Il peut être de 15, 16, 24 ou 32.
f.read(4)
largeur = st.unpack("h", f.read(2))[0]
hauteur = st.unpack("h", f.read(2))[0]
format = st.unpack("B", f.read(1))[0] # Nombre de bits utilisés pour stocker un pixel de l'image
f.read(1)
f.read(id_taille)
pixel = []
#f.seek(id_taille, f.tell())
# Palette
if apalette :
p_tail = pal_tail >> 3
for p in xrange(pal_long) :
pixel = st.unpack("BBB", f.read(p_tail))
# pixel = f.read(p_tail)
palette.append(pixel)
# Image
img.Create(largeur, hauteur)
img.InitAlpha()
chn = ""
chna = ""
# brut = img.GetData()
for i in xrange(largeur * hauteur) :
x = i % largeur
if imagetype == 1 : # 8 bits non compressé avec palette
index = st.unpack("B", f.read(1))[0]
pixel = palette[index]
o = palette[index]
# if pixel == (242, 242, 242) : chn += '\xf2\xf2\xf2'
# else : chn += o[2] + o[1] + o[0]
y = (hauteur-1) - (i/largeur)
img.SetRGB(x, y, pixel[2], pixel[1], pixel[0])
elif imagetype == 2 : # 16 24 ou 32 bits non compressé BRV
if format == 16 :
o = st.unpack("BB", f.read(2))
# http://tfcduke.developpez.com/tutoriel/format/tga/
val = o[0] + (o[1] << 8)
pixel = []
pixel.append(((val & 0x001F) >> 0) << 3)
pixel.append(((val & 0x03E0) >> 5) << 3)
pixel.append(((val & 0x7C00) >> 10) << 3)
# o = chr(pixel[2]) + chr(pixel[1]) + chr(pixel[0])
y = (hauteur-1) - (i/largeur)
if format == 24 :
# o = f.read(3)
pixel = st.unpack("BBB", f.read(3))
y = (hauteur-1) - (i/largeur)
elif format == 32 :
# o = f.read(4)
pixel = st.unpack("BBBB", f.read(4))
y = i/largeur
img.SetAlpha(x, y, pixel[3])
# chna += o[3]
# chn += o[2] + o[1] + o[0]
# chn.extend([pixel[2], pixel[1], pixel[0]])
img.SetRGB(x, y, pixel[2], pixel[1], pixel[0])
elif imagetype == 3 : # 8 ou 16 bits non compressé gris
if format == 8 :
pixel = st.unpack("B", f.read(1))[0]
elif format == 16 :
pixel = st.unpack("h", f.read(2))[0]
y = (hauteur-1) - (i/largeur)
img.SetRGB(x, y, pixel, pixel, pixel)
# img.SetData(img.GetData())
# if chna : img.SetAlphaData(chna)
f.close()
return img
def main() :
lec = wx.App() # App() pour console wx, (False) sans
fenetre = Fenetre(None, -1, "ImageVonc")
lec.MainLoop()
if __name__ == "__main__" :
main()
keyboard_arrow_down