« Compresse et dimensionne image (Python) » : différence entre les versions
Aller à la navigation
Aller à la recherche
(Page créée avec « En Python, script qui redimensionne et compresse des images au format {{fichier|jpg}}. <source lang="python"> #! /usr/bin/env python #-*- coding: iso-8859-15 -*- import... ») |
mAucun résumé des modifications |
||
| Ligne 4 : | Ligne 4 : | ||
#! /usr/bin/env python | #! /usr/bin/env python | ||
#-*- coding: iso-8859-15 -*- | #-*- coding: iso-8859-15 -*- | ||
# | |||
# Ph. Page | |||
# modification 2013-05-03 | |||
# * compression à qualité 85/100 | |||
# * date de modification fichier inchangée | |||
# modification 2013-05-29 | |||
# * ne traiter que les images dont la date est inférieure à une date donnée : ligne 170 | |||
import os | import os | ||
| Ligne 10 : | Ligne 18 : | ||
import thread | import thread | ||
import Image | import Image | ||
import datetime | |||
import calendar | |||
try: | try: | ||
| Ligne 20 : | Ligne 30 : | ||
import wx.lib.newevent | import wx.lib.newevent | ||
# --------------------------------------------- | |||
# Variables | |||
# --------------------------------------------- | |||
taillefinale=1024 # taille maximum (en hauteur ou en largeur) | |||
quality_val = 85 # qualité de compression JPG, 85/100 | |||
datelimiteannee=2016 | |||
datelimitemois=12 | |||
datelimitejour=31 | |||
# --------------------------------------------- | # --------------------------------------------- | ||
| Ligne 157 : | Ligne 176 : | ||
original_size = os.path.getsize(name) | original_size = os.path.getsize(name) | ||
im = Image.open(name) | im = Image.open(name) | ||
im_dateorigine = os.stat(name) | |||
im_dateorigineformatee =im_dateorigine.st_mtime | |||
# traitement de la taille de l'image | |||
# date limite pour les images à traiter ; si date supérieur, pas de traitement | |||
datelimite=calendar.timegm(datetime.datetime(datelimiteannee, datelimitemois, datelimitejour).timetuple()) | |||
imageatraiter=False | |||
# début traitement de la taille de l'image | |||
hauteur, largeur=im.size | hauteur, largeur=im.size | ||
dimmax=max(hauteur, largeur) | dimmax=max(hauteur, largeur) | ||
| Ligne 164 : | Ligne 190 : | ||
rapportdim=float(dimmax)/float(dimmin) | rapportdim=float(dimmax)/float(dimmin) | ||
if largeur==dimmax: | if largeur==dimmax: | ||
nouvhauteur=int( | nouvhauteur=int(taillefinale/rapportdim) | ||
nouvlargeur= | nouvlargeur=taillefinale | ||
else: | else: | ||
nouvhauteur= | nouvhauteur=taillefinale | ||
nouvlargeur=int( | nouvlargeur=int(taillefinale/rapportdim) | ||
im2 = im.resize((nouvhauteur, nouvlargeur), Image.NEAREST) # use nearest neighbour | im2 = im.resize((nouvhauteur, nouvlargeur), Image.NEAREST) # use nearest neighbour | ||
# fin traitement de la taille de l'image | # fin traitement de la taille de l'image | ||
im2.save(name) | |||
# php-28/05/2013 : ne traiter que les images dont la date de modification est inférieure à | |||
if im_dateorigineformatee < datelimite: | |||
imageatraiter=True | |||
# php-13/11/2012 : ne pas traiter si dimension inférieure à 1024 | |||
if dimmax > taillefinale and imageatraiter: | |||
im2.save(name, 'JPEG', quality=quality_val) | |||
# php-02/05/2013 : date de modification fichier inchangée | |||
os.utime(name, (time.time(), im_dateorigine.st_mtime)) | |||
if not imageatraiter: | |||
im.save(name, 'JPEG', quality=quality_val) | |||
# php-02/05/2013 : date de modification fichier inchangée | |||
os.utime(name, (time.time(), im_dateorigine.st_mtime)) | |||
compress_size = os.path.getsize(name) | compress_size = os.path.getsize(name) | ||
except WindowsError: | |||
evt = ErrorCompressEvent( | |||
message=u"Erreur 3 lors du traitement du fichier '%s'" % name) | |||
wx.PostEvent(self.win, evt) | |||
except IOError: | except IOError: | ||
evt = ErrorCompressEvent( | evt = ErrorCompressEvent( | ||
| Ligne 185 : | Ligne 232 : | ||
if not self.keepGoing: | if not self.keepGoing: | ||
# interruption du traitement | |||
evt = InterruptCompressEvent() | evt = InterruptCompressEvent() | ||
wx.PostEvent(self.win, evt) | wx.PostEvent(self.win, evt) | ||
| Ligne 196 : | Ligne 243 : | ||
class MainFrame(wx.Frame): | class MainFrame(wx.Frame): | ||
def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||
wx.Frame.__init__(self, *args, **kwargs) | wx.Frame.__init__(self, *args, **kwargs) | ||
self.CreateWidgets() | self.CreateWidgets() | ||
self.DoLayout() | self.DoLayout() | ||
| Ligne 204 : | Ligne 254 : | ||
def CreateWidgets(self): | def CreateWidgets(self): | ||
panel = self.panel = wx.Panel(self) | panel = self.panel = wx.Panel(self) | ||
self.staticText_dir = wx.StaticText(panel, -1, u"Répertoire à recompresser :") | |||
self.staticText_dir = wx.StaticText(panel, -1, | |||
u"Répertoire à recompresser :") | |||
self.textCtrl_dir = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY) | self.textCtrl_dir = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY) | ||
self.btn_dir = wx.Button(panel, -1, "...", size=(30, -1)) | self.btn_dir = wx.Button(panel, -1, "...", size=(30, -1)) | ||
self.btn_action = wx.Button(panel, -1) | self.btn_action = wx.Button(panel, -1) | ||
font = wx.Font(family=wx.FONTFAMILY_DEFAULT, style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL, pointSize=18) | font = wx.Font(family=wx.FONTFAMILY_DEFAULT, style=wx.FONTSTYLE_NORMAL, | ||
weight=wx.FONTWEIGHT_NORMAL, pointSize=18) | |||
self.btn_action.SetFont(font) | self.btn_action.SetFont(font) | ||
self.btn_action.SetLabel(START_ACTION) | self.btn_action.SetLabel(START_ACTION) | ||
self.statusBar = wx.StatusBar(self) | self.statusBar = wx.StatusBar(self) | ||
self.statusBar.SetFieldsCount(4) | self.statusBar.SetFieldsCount(4) | ||
| Ligne 218 : | Ligne 274 : | ||
def DoLayout(self): | def DoLayout(self): | ||
szr = wx.GridBagSizer(5, 5) | szr = wx.GridBagSizer(5, 5) | ||
szr.Add(self.staticText_dir, (0, 0), flag=wx.ALIGN_CENTRE) | szr.Add(self.staticText_dir, (0, 0), flag=wx.ALIGN_CENTRE) | ||
szr.Add(self.textCtrl_dir, (0, 1), flag=wx.EXPAND | wx.ALIGN_CENTRE_HORIZONTAL) | szr.Add(self.textCtrl_dir, (0, 1), | ||
flag=wx.EXPAND | wx.ALIGN_CENTRE_HORIZONTAL) | |||
szr.Add(self.btn_dir, (0, 2), flag=wx.ALIGN_CENTRE_HORIZONTAL) | szr.Add(self.btn_dir, (0, 2), flag=wx.ALIGN_CENTRE_HORIZONTAL) | ||
szr.AddGrowableCol(1) | szr.AddGrowableCol(1) | ||
main_szr = wx.GridBagSizer(5, 5) | main_szr = wx.GridBagSizer(5, 5) | ||
main_szr.Add((0, 0), (0, 0)) | main_szr.Add((0, 0), (0, 0)) | ||
| Ligne 228 : | Ligne 289 : | ||
main_szr.Add(self.btn_action, (2, 1), flag=wx.EXPAND) | main_szr.Add(self.btn_action, (2, 1), flag=wx.EXPAND) | ||
main_szr.Add((0, 0), (3, 2)) | main_szr.Add((0, 0), (3, 2)) | ||
main_szr.AddGrowableCol(1) | main_szr.AddGrowableCol(1) | ||
main_szr.AddGrowableRow(2) | main_szr.AddGrowableRow(2) | ||
self.panel.SetSizerAndFit(main_szr) | self.panel.SetSizerAndFit(main_szr) | ||
self.SetSize((500, 200)) | self.SetSize((500, 200)) | ||
self.SetMinSize((500, 200)) | self.SetMinSize((500, 200)) | ||
def DoBinding(self): | def DoBinding(self): | ||
| Ligne 245 : | Ligne 310 : | ||
self.Bind(EVT_ERROR_COMPRESS, self.OnErrorCompress) | self.Bind(EVT_ERROR_COMPRESS, self.OnErrorCompress) | ||
self.Bind(wx.EVT_CLOSE, self.OnClose) | self.Bind(wx.EVT_CLOSE, self.OnClose) | ||
def SetInitialValues(self): | def SetInitialValues(self): | ||
self.count_thread = None | self.count_thread = None | ||
self.compress_thread = None | self.compress_thread = None | ||
self.original_size = 0 | self.original_size = 0 | ||
self.compress_size = 0 | self.compress_size = 0 | ||
self.compressed_photo = 0 | self.compressed_photo = 0 | ||
self.error = 0 | self.error = 0 | ||
for i in range(self.StatusBar.GetFieldsCount()): | for i in range(self.StatusBar.GetFieldsCount()): | ||
self.StatusBar.SetStatusText('', i) | self.StatusBar.SetStatusText('', i) | ||
| Ligne 266 : | Ligne 334 : | ||
else: | else: | ||
defaultPath = "" | defaultPath = "" | ||
dlg = wx.DirDialog(self, u"Choissisez le répertoire à recompresser", defaultPath=defaultPath) | dlg = wx.DirDialog(self, u"Choissisez le répertoire à recompresser", | ||
defaultPath=defaultPath) | |||
if dlg.ShowModal() == wx.ID_OK: | if dlg.ShowModal() == wx.ID_OK: | ||
self.textCtrl_dir.SetValue(dlg.GetPath()) | self.textCtrl_dir.SetValue(dlg.GetPath()) | ||
dlg.Destroy() | dlg.Destroy() | ||
evt.Skip() | evt.Skip() | ||
| Ligne 275 : | Ligne 346 : | ||
action = self.btn_action.GetLabel() | action = self.btn_action.GetLabel() | ||
if action == START_ACTION: | if action == START_ACTION: | ||
photo_dir = self.textCtrl_dir.GetValue() | photo_dir = self.textCtrl_dir.GetValue() | ||
evt.Skip() | evt.Skip() | ||
if not os.path.isdir(photo_dir): | if not os.path.isdir(photo_dir): | ||
dlg = wx.MessageDialog(self, u'Répertoire invalide', 'Erreur', wx.OK | wx.ICON_ERROR) | dlg = wx.MessageDialog(self, u'Répertoire invalide', | ||
'Erreur', wx.OK | wx.ICON_ERROR) | |||
dlg.ShowModal() | dlg.ShowModal() | ||
dlg.Destroy() | dlg.Destroy() | ||
return False | return False | ||
self.StartAction(photo_dir) | self.StartAction(photo_dir) | ||
elif action == STOP_ACTION: | elif action == STOP_ACTION: | ||
self.StopThreads() | self.StopThreads() | ||
def StopThreads(self): | def StopThreads(self): | ||
busy = wx.BusyInfo(u'Arrêt en cours... Veuillez patienter') | busy = wx.BusyInfo(u'Arrêt en cours... Veuillez patienter') | ||
wx.Yield() | wx.Yield() | ||
for thrd in self.count_thread, self.compress_thread: | for thrd in self.count_thread, self.compress_thread: | ||
if thrd and thrd.IsRunning(): | if thrd and thrd.IsRunning(): | ||
| Ligne 294 : | Ligne 373 : | ||
while thrd.IsRunning(): | while thrd.IsRunning(): | ||
time.sleep(0.1) | time.sleep(0.1) | ||
self.SetInitialValues() | self.SetInitialValues() | ||
self.btn_action.SetLabel(START_ACTION) | self.btn_action.SetLabel(START_ACTION) | ||
def StartAction(self, photo_dir): | def StartAction(self, photo_dir): | ||
| Ligne 306 : | Ligne 388 : | ||
def OnUpdateCount(self, evt): | def OnUpdateCount(self, evt): | ||
photos_count, dirs_count = evt.photos_count, evt.dirs_count | photos_count, dirs_count = evt.photos_count, evt.dirs_count | ||
self.statusBar.SetStatusText(u"Trouvé %i photo%s dans %i répertoire%s" % | self.statusBar.SetStatusText(u"Trouvé %i photo%s dans %i répertoire%s" % | ||
| Ligne 313 : | Ligne 396 : | ||
pluralize(dirs_count))) | pluralize(dirs_count))) | ||
def OnStopCount(self, evt): | def OnStopCount(self, evt): | ||
self.count_thread = None | self.count_thread = None | ||
| Ligne 326 : | Ligne 410 : | ||
def OnStopCompress(self, evt): | def OnStopCompress(self, evt): | ||
if self.photos_count: | if self.photos_count: | ||
dlg = wx.MessageDialog(self, u"""traitement terminée! | dlg = wx.MessageDialog(self, | ||
u"""traitement terminée! | |||
%i image%s compressée%s | %i image%s compressée%s | ||
%i erreur%s | %i erreur%s | ||
| Ligne 338 : | Ligne 424 : | ||
o_normalize(self.compress_size), | o_normalize(self.compress_size), | ||
normalize(self.original_size - self.compress_size), | normalize(self.original_size - self.compress_size), | ||
((self.original_size - self.compress_size) / float(self.original_size) * 100)), | ((self.original_size - self.compress_size) / | ||
float(self.original_size) * 100)), | |||
'fin du traitement', wx.OK | wx.ICON_INFORMATION) | 'fin du traitement', wx.OK | wx.ICON_INFORMATION) | ||
else: | else: | ||
| Ligne 347 : | Ligne 434 : | ||
dlg.ShowModal() | dlg.ShowModal() | ||
dlg.Destroy() | dlg.Destroy() | ||
self.SetInitialValues() | self.SetInitialValues() | ||
self.statusBar.SetStatusText(u'Compression terminée') | self.statusBar.SetStatusText(u'Compression terminée') | ||
| Ligne 354 : | Ligne 442 : | ||
def OnUpdateCompress(self, evt): | def OnUpdateCompress(self, evt): | ||
self.compressed_photo += 1 | self.compressed_photo += 1 | ||
self.original_size += evt.original_size | self.original_size += evt.original_size | ||
self.compress_size += evt.compress_size | self.compress_size += evt.compress_size | ||
self.statusBar.SetStatusText("%i/%i" % | self.statusBar.SetStatusText("%i/%i" % | ||
(self.compressed_photo, self.photos_count), 2) | (self.compressed_photo, self.photos_count), 2) | ||
| Ligne 367 : | Ligne 457 : | ||
print evt.message | print evt.message | ||
self.error += 1 | self.error += 1 | ||
if __name__ == "__main__": | if __name__ == "__main__": | ||
app = wx.PySimpleApp(redirect=False) | app = wx.PySimpleApp(redirect=False) | ||
fr = MainFrame(None, -1, "Recompresseur de photos (taille max 1024 px)") | fr = MainFrame(None, -1, "Recompresseur de photos (taille max 1024 px)") | ||
| Ligne 376 : | Ligne 468 : | ||
[[Catégorie:Python]] | [[Catégorie:Python]] | ||
[[Catégorie:Script]] | |||
Version du 2 mars 2021 à 10:48
En Python, script qui redimensionne et compresse des images au format jpg.
#! /usr/bin/env python
#-*- coding: iso-8859-15 -*-
#
# Ph. Page
# modification 2013-05-03
# * compression à qualité 85/100
# * date de modification fichier inchangée
# modification 2013-05-29
# * ne traiter que les images dont la date est inférieure à une date donnée : ligne 170
import os
import sys
import time
import thread
import Image
import datetime
import calendar
try:
import wx
except ImportError:
print u"""
Installer wxPython pour utiliser ce programme
À télecharger sur www.wxpython.org"""
sys.exit(0)
import wx.lib.newevent
# ---------------------------------------------
# Variables
# ---------------------------------------------
taillefinale=1024 # taille maximum (en hauteur ou en largeur)
quality_val = 85 # qualité de compression JPG, 85/100
datelimiteannee=2016
datelimitemois=12
datelimitejour=31
# ---------------------------------------------
# labels utilisées sur le bouton 'btn_action'
# ---------------------------------------------
START_ACTION = u"Compresser"
STOP_ACTION = u"Arrêter"
# ---------------------------------------------
# ---------------------------------------------
#creation d'evenement personnalisées
# ---------------------------------------------
(UpdateCountEvent, EVT_UPDATE_COUNT) = wx.lib.newevent.NewEvent()
(UpdateCompressEvent, EVT_UPDATE_COMPRESS) = wx.lib.newevent.NewEvent()
(StopCountEvent, EVT_STOP_COUNT) = wx.lib.newevent.NewEvent()
(InterruptCountEvent, EVT_INTERRUPT_COUNT) = wx.lib.newevent.NewEvent()
(StopCompressEvent, EVT_STOP_COMPRESS) = wx.lib.newevent.NewEvent()
(InterruptCompressEvent, EVT_INTERRUPT_COMPRESS) = wx.lib.newevent.NewEvent()
(ErrorCompressEvent, EVT_ERROR_COMPRESS) = wx.lib.newevent.NewEvent()
# ---------------------------------------------
def pluralize(num):
"""
fonction qui renvoie un 's' si le nombre passé en
argument est superieur à 1
"""
return num>1 and "s" or ""
def normalize(size):
"""
fonction qui prend en argument un nombre d'octets
et renvoie la taille la plus adapté
"""
seuil_Kio = 1024
seuil_Mio = 1024 * 1024
seuil_Gio = 1024 * 1024 * 1024
if size > seuil_Gio:
return "%.2fGio" % (size/float(seuil_Gio))
elif size > seuil_Mio:
return "%.2fMio" % (size/float(seuil_Mio))
elif size > seuil_Kio:
return "%.2fKio" % (size/float(seuil_Kio))
else:
return "%io" % size
def o_normalize(nb):
"""
fonction qui permet de separer un nombre par tranche de trois:
o_normalize(1234567890) => "1 234 567 890"
"""
nb = str(nb)[::-1] # 12345 => "54321"
l = []
for i in range(0,len(nb),3):
l.append(nb[i:i+3][::-1]) # ["345", "12"]
return " ".join(l[::-1]) # "12 345"
def get_jpegs(filenames):
# liste comprehension pour filtrer les fichiers jpeg (.jpg, .jpeg, .JPEG, ..)
return [f for f in filenames if os.path.splitext(f)[-1].lower() in ['.jpg', '.jpeg']]
class CountThread(object):
"""
Classe qui permet de compter le nombre de fichier jpeg
dans un ensemble de dossier.
Prend en parametre le dossier racine de la recherche
et la fenetre (wx) a laquelle renvoyer les evenements
"""
def __init__(self, win, root_dir):
self.win = win
self.root_dir = root_dir
def Start(self):
self.keepGoing = self.running = True
thread.start_new_thread(self.Run, ())
def Stop(self):
self.keepGoing = False
def IsRunning(self):
return self.running
def Run(self):
photos_count, dirs_count = 0, 0
for dirpath, dirnames, filenames in os.walk(self.root_dir):
photos = get_jpegs(filenames)
if photos:
photos_count += len(photos)
dirs_count += 1
evt = UpdateCountEvent(photos_count = photos_count,
dirs_count = dirs_count)
wx.PostEvent(self.win, evt)
if not self.keepGoing:
# en cas d'interruption du thread
evt = InterruptCountEvent()
wx.PostEvent(self.win, evt)
self.running = False
return
# le thread se termine normalement
self.running = False
evt = StopCountEvent(photos_count=photos_count, photo_dir=self.root_dir)
wx.PostEvent(self.win, evt)
class CompressThread(object):
"""
Classe qui permet de recompresser les fichiers jpeg
dans un ensemble de dossier.
Prend en parametre le dossier racine de la recherche
et la fenetre (wx) a laquelle renvoyer les evenements
"""
def __init__(self, win, root_dir):
self.win = win
self.root_dir = root_dir
def Start(self):
self.keepGoing = self.running = True
thread.start_new_thread(self.Run, ())
def Stop(self):
self.keepGoing = False
def IsRunning(self):
return self.running
def Run(self):
photos_count, dirs_count = 0, 0
for dirpath, dirnames, filenames in os.walk(self.root_dir):
photos = get_jpegs(filenames)
if photos:
for f in photos:
if not self.keepGoing:
break
try:
name = os.path.join(dirpath, f)
original_size = os.path.getsize(name)
im = Image.open(name)
im_dateorigine = os.stat(name)
im_dateorigineformatee =im_dateorigine.st_mtime
# date limite pour les images à traiter ; si date supérieur, pas de traitement
datelimite=calendar.timegm(datetime.datetime(datelimiteannee, datelimitemois, datelimitejour).timetuple())
imageatraiter=False
# début traitement de la taille de l'image
hauteur, largeur=im.size
dimmax=max(hauteur, largeur)
dimmin=min(hauteur, largeur)
rapportdim=float(dimmax)/float(dimmin)
if largeur==dimmax:
nouvhauteur=int(taillefinale/rapportdim)
nouvlargeur=taillefinale
else:
nouvhauteur=taillefinale
nouvlargeur=int(taillefinale/rapportdim)
im2 = im.resize((nouvhauteur, nouvlargeur), Image.NEAREST) # use nearest neighbour
# fin traitement de la taille de l'image
# php-28/05/2013 : ne traiter que les images dont la date de modification est inférieure à
if im_dateorigineformatee < datelimite:
imageatraiter=True
# php-13/11/2012 : ne pas traiter si dimension inférieure à 1024
if dimmax > taillefinale and imageatraiter:
im2.save(name, 'JPEG', quality=quality_val)
# php-02/05/2013 : date de modification fichier inchangée
os.utime(name, (time.time(), im_dateorigine.st_mtime))
if not imageatraiter:
im.save(name, 'JPEG', quality=quality_val)
# php-02/05/2013 : date de modification fichier inchangée
os.utime(name, (time.time(), im_dateorigine.st_mtime))
compress_size = os.path.getsize(name)
except WindowsError:
evt = ErrorCompressEvent(
message=u"Erreur 3 lors du traitement du fichier '%s'" % name)
wx.PostEvent(self.win, evt)
except IOError:
evt = ErrorCompressEvent(
message=u"Erreur lors du traitement du fichier '%s'" % name)
wx.PostEvent(self.win, evt)
else:
evt = UpdateCompressEvent(
original_size = original_size,
compress_size = compress_size)
wx.PostEvent(self.win, evt)
if not self.keepGoing:
# interruption du traitement
evt = InterruptCompressEvent()
wx.PostEvent(self.win, evt)
self.running = False
return
self.running = False
evt = StopCompressEvent()
wx.PostEvent(self.win, evt)
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.CreateWidgets()
self.DoLayout()
self.DoBinding()
self.SetInitialValues()
def CreateWidgets(self):
panel = self.panel = wx.Panel(self)
self.staticText_dir = wx.StaticText(panel, -1,
u"Répertoire à recompresser :")
self.textCtrl_dir = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY)
self.btn_dir = wx.Button(panel, -1, "...", size=(30, -1))
self.btn_action = wx.Button(panel, -1)
font = wx.Font(family=wx.FONTFAMILY_DEFAULT, style=wx.FONTSTYLE_NORMAL,
weight=wx.FONTWEIGHT_NORMAL, pointSize=18)
self.btn_action.SetFont(font)
self.btn_action.SetLabel(START_ACTION)
self.statusBar = wx.StatusBar(self)
self.statusBar.SetFieldsCount(4)
self.statusBar.SetStatusWidths([-3, -2, -1, -1])
self.SetStatusBar(self.statusBar)
def DoLayout(self):
szr = wx.GridBagSizer(5, 5)
szr.Add(self.staticText_dir, (0, 0), flag=wx.ALIGN_CENTRE)
szr.Add(self.textCtrl_dir, (0, 1),
flag=wx.EXPAND | wx.ALIGN_CENTRE_HORIZONTAL)
szr.Add(self.btn_dir, (0, 2), flag=wx.ALIGN_CENTRE_HORIZONTAL)
szr.AddGrowableCol(1)
main_szr = wx.GridBagSizer(5, 5)
main_szr.Add((0, 0), (0, 0))
main_szr.Add(szr, (1,1), flag=wx.EXPAND)
main_szr.Add(self.btn_action, (2, 1), flag=wx.EXPAND)
main_szr.Add((0, 0), (3, 2))
main_szr.AddGrowableCol(1)
main_szr.AddGrowableRow(2)
self.panel.SetSizerAndFit(main_szr)
self.SetSize((500, 200))
self.SetMinSize((500, 200))
def DoBinding(self):
self.Bind(wx.EVT_BUTTON, self.OnBtnDir, self.btn_dir)
self.Bind(wx.EVT_BUTTON, self.OnBtnAction, self.btn_action)
self.Bind(EVT_UPDATE_COUNT, self.OnUpdateCount)
self.Bind(EVT_UPDATE_COMPRESS, self.OnUpdateCompress)
self.Bind(EVT_STOP_COUNT, self.OnStopCount)
self.Bind(EVT_STOP_COMPRESS, self.OnStopCompress)
self.Bind(EVT_INTERRUPT_COUNT, self.OnInterrupt)
self.Bind(EVT_INTERRUPT_COMPRESS, self.OnInterrupt)
self.Bind(EVT_ERROR_COMPRESS, self.OnErrorCompress)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def SetInitialValues(self):
self.count_thread = None
self.compress_thread = None
self.original_size = 0
self.compress_size = 0
self.compressed_photo = 0
self.error = 0
for i in range(self.StatusBar.GetFieldsCount()):
self.StatusBar.SetStatusText('', i)
def OnClose(self, evt):
self.StopThreads()
evt.Skip()
def OnBtnDir(self, evt):
photo_dir = self.textCtrl_dir.GetValue()
if os.path.isdir(photo_dir):
defaultPath = photo_dir
else:
defaultPath = ""
dlg = wx.DirDialog(self, u"Choissisez le répertoire à recompresser",
defaultPath=defaultPath)
if dlg.ShowModal() == wx.ID_OK:
self.textCtrl_dir.SetValue(dlg.GetPath())
dlg.Destroy()
evt.Skip()
def OnBtnAction(self, evt):
action = self.btn_action.GetLabel()
if action == START_ACTION:
photo_dir = self.textCtrl_dir.GetValue()
evt.Skip()
if not os.path.isdir(photo_dir):
dlg = wx.MessageDialog(self, u'Répertoire invalide',
'Erreur', wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return False
self.StartAction(photo_dir)
elif action == STOP_ACTION:
self.StopThreads()
def StopThreads(self):
busy = wx.BusyInfo(u'Arrêt en cours... Veuillez patienter')
wx.Yield()
for thrd in self.count_thread, self.compress_thread:
if thrd and thrd.IsRunning():
thrd.Stop()
while thrd.IsRunning():
time.sleep(0.1)
self.SetInitialValues()
self.btn_action.SetLabel(START_ACTION)
def StartAction(self, photo_dir):
self.SetInitialValues()
self.statusBar.SetStatusText(u"Trouvé 0 photo dans 0 répertoire")
self.btn_dir.Enable(False)
self.count_thread = CountThread(self, photo_dir)
self.count_thread.Start()
self.btn_action.SetLabel(STOP_ACTION)
def OnUpdateCount(self, evt):
photos_count, dirs_count = evt.photos_count, evt.dirs_count
self.statusBar.SetStatusText(u"Trouvé %i photo%s dans %i répertoire%s" %
(photos_count,
pluralize(photos_count),
dirs_count,
pluralize(dirs_count)))
def OnStopCount(self, evt):
self.count_thread = None
self.photos_count = evt.photos_count
self.compress_thread = CompressThread(self, evt.photo_dir)
self.compress_thread.Start()
def OnInterrupt(self, evt):
self.SetInitialValues()
self.statusBar.SetStatusText('Action interrompue !')
self.btn_action.SetLabel(START_ACTION)
self.btn_dir.Enable(True)
def OnStopCompress(self, evt):
if self.photos_count:
dlg = wx.MessageDialog(self,
u"""traitement terminée!
%i image%s compressée%s
%i erreur%s
taille originale: %s octets
taille finale: %s octets
gain: %s soit %i%%""" % (self.photos_count, pluralize(self.photos_count),
pluralize(self.photos_count),
self.error, pluralize(self.error),
o_normalize(self.original_size),
o_normalize(self.compress_size),
normalize(self.original_size - self.compress_size),
((self.original_size - self.compress_size) /
float(self.original_size) * 100)),
'fin du traitement', wx.OK | wx.ICON_INFORMATION)
else:
dlg = wx.MessageDialog(self,
u"Aucune photo à recompresser.",
'Fin du traitement',
wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
self.SetInitialValues()
self.statusBar.SetStatusText(u'Compression terminée')
self.btn_action.SetLabel(START_ACTION)
self.btn_dir.Enable(True)
def OnUpdateCompress(self, evt):
self.compressed_photo += 1
self.original_size += evt.original_size
self.compress_size += evt.compress_size
self.statusBar.SetStatusText("%i/%i" %
(self.compressed_photo, self.photos_count), 2)
self.statusBar.SetStatusText("%s => %s" %
(normalize(self.original_size), normalize(self.compress_size)), 1)
self.statusBar.SetStatusText("%i%%" %
(int(round(100*self.compressed_photo//self.photos_count))), 3)
def OnErrorCompress(self, evt):
print evt.message
self.error += 1
if __name__ == "__main__":
app = wx.PySimpleApp(redirect=False)
fr = MainFrame(None, -1, "Recompresseur de photos (taille max 1024 px)")
fr.Show(True)
app.MainLoop()