Merge branch 'master' of git://github.com/kalmi/Trimage into parallel

Conflicts:
	src/trimage/trimage.py
This commit is contained in:
Kilian Valkhof 2010-04-02 12:57:59 +02:00
commit 0f5092e10b
6 changed files with 83 additions and 51 deletions

View file

@ -15,10 +15,6 @@ todo app wise
todo else todo else
- figure out how to make mac and win versions (someone else :) <- via gui2exe - figure out how to make mac and win versions (someone else :) <- via gui2exe
todo later
- use multiprocessing lib to take advantage of multicore/multi-CPU to compress
multiple files simultaneously (threads have issues in Python; see "GIL")
=========================================== ===========================================
later versions: later versions:
animate compressing.gif animate compressing.gif

View file

@ -127,7 +127,7 @@
<cursorShape>PointingHandCursor</cursorShape> <cursorShape>PointingHandCursor</cursorShape>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Recompress selected images</string> <string>Recompress all images</string>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Recompress</string> <string>&amp;Recompress</string>

View file

@ -132,19 +132,11 @@ class StartQT4(QMainWindow):
# this is a fix for file dialog differentiating between cases # this is a fix for file dialog differentiating between cases
"Image files (*.png *.jpg *.jpeg *.PNG *.JPG *.JPEG)") "Image files (*.png *.jpg *.jpeg *.PNG *.JPG *.JPEG)")
imagelist = [] self.delegator([unicode(fullpath) for fullpath in images])
for i, image in enumerate(images):
imagelist.append(unicode(image))
self.delegator(imagelist)
def recompress_files(self): def recompress_files(self):
"""Send each file in the current file list to compress_file again.""" """Send each file in the current file list to compress_file again."""
newimagelist = [] self.delegator([row.image.fullpath for row in self.imagelist])
for image in self.imagelist:
newimagelist.append(image[4])
self.imagelist = []
self.delegator(newimagelist)
""" """
Compress functions Compress functions
@ -155,12 +147,19 @@ class StartQT4(QMainWindow):
Recieve all images, check them and send them to the worker thread. Recieve all images, check them and send them to the worker thread.
""" """
delegatorlist = [] delegatorlist = []
for image in images: for fullpath in images:
image=Image(image) try: # do not add already existing images again, recompress them instead
image=(i.image for i in self.imagelist
if i.image.fullpath == fullpath).next()
if image.compressed:
image.reset()
image.recompression=True
delegatorlist.append(image)
except StopIteration:
image=Image(fullpath)
if image.valid: if image.valid:
delegatorlist.append(image) delegatorlist.append(image)
self.imagelist.append(("Compressing...", "", "", "", image, self.imagelist.append(ImageRow(image,QIcon(QPixmap(self.ui.get_image("pixmaps/compressing.gif")))))
QIcon(QPixmap(self.ui.get_image("pixmaps/compressing.gif")))))
else: else:
print >>sys.stderr, u"[error] %s not a supported image file" % image.fullpath print >>sys.stderr, u"[error] %s not a supported image file" % image.fullpath
@ -267,7 +266,7 @@ class TriTableModel(QAbstractTableModel):
return QVariant(data) return QVariant(data)
elif index.column() == 0 and role == Qt.DecorationRole: elif index.column() == 0 and role == Qt.DecorationRole:
# decorate column 0 with an icon of the image itself # decorate column 0 with an icon of the image itself
f_icon = self.imagelist[index.row()][5] f_icon = self.imagelist[index.row()][4]
return QVariant(f_icon) return QVariant(f_icon)
else: else:
return QVariant() return QVariant()
@ -279,10 +278,43 @@ class TriTableModel(QAbstractTableModel):
return QVariant(self.header[col]) return QVariant(self.header[col])
return QVariant() return QVariant()
class ImageRow:
def __init__(self, image, waitingIcon=None):
self.image=image
d={
'shortname': lambda i: self.statusStr() % i.shortname,
'oldfilesizestr': lambda i: size(i.oldfilesize, system=alternative) if i.compressed else "",
'newfilesizestr': lambda i: size(i.newfilesize, system=alternative) if i.compressed else "",
'ratiostr': lambda i:
"%.1f%%" % (100 - (float(i.newfilesize) / i.oldfilesize * 100)) if i.compressed else "",
'icon': lambda i: i.icon if i.compressed else waitingIcon,
'fullpath': lambda i: i.fullpath, #only used by cli
}
for i,n in enumerate(['shortname','oldfilesizestr','newfilesizestr','ratiostr','icon']):
d[i]=d[n]
self.d = d
def statusStr(self):
if self.image.failed:
return "ERROR: %s"
if self.image.compressing:
return "In Progress..."
if not self.image.compressed and self.image.recompression:
return "Queued for recompression..."
if not self.image.compressed:
return "Queued..."
return "%s"
def __getitem__(self, key):
return self.d[key](self.image)
class Image: class Image:
def __init__(self, fullpath): def __init__(self, fullpath):
""" gather image information. """ """ gather image information. """
self.valid = False self.valid = False
self.reset()
self.fullpath = fullpath self.fullpath = fullpath
if path.isfile(self.fullpath): if path.isfile(self.fullpath):
self.filetype = determinetype(self.fullpath) self.filetype = determinetype(self.fullpath)
@ -302,20 +334,36 @@ class Image:
self.filetype=None self.filetype=None
return self.filetype return self.filetype
def reset(self):
self.failed = False
self.compressed = False
self.compressing = False
self.recompression= False
def compress(self): def compress(self):
""" Compress the image and return it to the thread. """ """ Compress the image and return it to the thread. """
if not self.valid: if not self.valid:
raise "Tried to compress invalid image (unsupported format or not file)" raise "Tried to compress invalid image (unsupported format or not file)"
self.reset()
self.compressing=True
runString = { runString = {
"jpeg": u"jpegoptim -f --strip-all '%(file)s'", "jpeg": u"jpegoptim -f --strip-all '%(file)s'",
"png" : u"optipng -force -o7 '%(file)s'&&advpng -z4 '%(file)s'"} "png" : u"optipng -force -o7 '%(file)s'&&advpng -z4 '%(file)s'"}
try:
retcode = call(runString[self.filetype] % {"file": self.fullpath}, retcode = call(runString[self.filetype] % {"file": self.fullpath},
shell = True, stdout=PIPE) shell = True, stdout=PIPE)
except:
retcode = -1
if retcode == 0: if retcode == 0:
self.newfilesize = QFile(self.fullpath).size() self.newfilesize = QFile(self.fullpath).size()
self.compressed=True
else:
self.failed=True
self.compressing=False
self.retcode=retcode self.retcode=retcode
return self return self
class Worker(QThread): class Worker(QThread):
def __init__(self, parent=None): def __init__(self, parent=None):
@ -339,29 +387,17 @@ class Worker(QThread):
def run(self): def run(self):
"""Compress the given file, get data from it and call update_table.""" """Compress the given file, get data from it and call update_table."""
tp = self.threadpool tp = self.threadpool
while self.showapp or not (tp.__active_workers==0 and tp.__jobs.empty()): while self.showapp or not (tp._ThreadPool__active_worker_count==0 and tp._ThreadPool__jobs.empty()):
image = self.toDisplay.get() image = self.toDisplay.get()
if image.retcode==0:
#calculate ratio and make a nice string
oldfilesizestr = size(image.oldfilesize, system=alternative)
newfilesizestr = size(image.newfilesize, system=alternative)
ratio = 100 - (float(image.newfilesize) / float(image.oldfilesize) * 100)
ratiostr = "%.1f%%" % ratio
# append current image to list
for i, listitem in enumerate(self.imagelist):
if listitem[4] == image:
self.imagelist.remove(listitem)
self.imagelist.insert(i, (image.shortname, oldfilesizestr,
newfilesizestr, ratiostr, image.fullpath, image.icon))
self.emit(SIGNAL("updateUi")) self.emit(SIGNAL("updateUi"))
if not self.showapp and self.verbose: if not self.showapp and self.verbose: # we work via the commandline
# we work via the commandline if image.retcode==0:
print("File: " + image.fullpath + ", Old Size: " ir=ImageRow(image)
+ oldfilesizestr + ", New Size: " + newfilesizestr print("File: " + ir['fullpath'] + ", Old Size: "
+ ", Ratio: " + ratiostr) + ir['oldfilesizestr'] + ", New Size: " + ir['newfilesizestr']
+ ", Ratio: " + ir['ratiostr'])
else: else:
print >>sys.stderr, u"[error] %s could not be compressed" % image.fullpath print >>sys.stderr, u"[error] %s could not be compressed" % image.fullpath

View file

@ -153,7 +153,7 @@ class Ui_trimage(object):
"Drag and drop images onto the table", None, "Drag and drop images onto the table", None,
QApplication.UnicodeUTF8)) QApplication.UnicodeUTF8))
self.recompress.setToolTip(QApplication.translate("trimage", self.recompress.setToolTip(QApplication.translate("trimage",
"Recompress selected images", None, QApplication.UnicodeUTF8)) "Recompress all images", None, QApplication.UnicodeUTF8))
self.recompress.setText(QApplication.translate("trimage", self.recompress.setText(QApplication.translate("trimage",
"&Recompress", None, QApplication.UnicodeUTF8)) "&Recompress", None, QApplication.UnicodeUTF8))
self.recompress.setShortcut(QApplication.translate("trimage", self.recompress.setShortcut(QApplication.translate("trimage",

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
#Copyright (c) 2010 Kilian Valkhof, Paul Chaplin #Copyright (c) 2010 Kilian Valkhof, Paul Chaplin, Tarnay Kálmán
# #
#Permission is hereby granted, free of charge, to any person #Permission is hereby granted, free of charge, to any person
#obtaining a copy of this software and associated documentation #obtaining a copy of this software and associated documentation

View file

@ -123,7 +123,7 @@
<ul> <ul>
<li>Neil Wallace</li> <li>Neil Wallace</li>
<li>Jeroen Goudsmit</li> <li>Jeroen Goudsmit</li>
<li>Kálmán Tarnay</li> <li>Tarnay Kálmán</li>
</ul> </ul>
</div> </div>
<div class="block"> <div class="block">