mirror of
https://github.com/Kilian/Trimage.git
synced 2026-01-26 10:08:40 -05:00
Merge branch 'master' of git://github.com/kalmi/Trimage into parallel
Conflicts: src/trimage/trimage.py
This commit is contained in:
commit
0f5092e10b
6 changed files with 83 additions and 51 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>&Recompress</string>
|
<string>&Recompress</string>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
2
trimage
2
trimage
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue