Compare commits
76 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad74684272 | ||
|
|
031adf6054 | ||
|
|
f35be9c750 | ||
|
|
636b5a9750 | ||
|
|
c21089f97b | ||
|
|
f37c8b6ffc | ||
|
|
8af532f25e | ||
|
|
493a0e18d2 | ||
|
|
224f6b3503 | ||
|
|
c3244e19a6 | ||
|
|
269e052ba4 | ||
|
|
49272e0d95 | ||
|
|
1342503b54 | ||
|
|
0cbbfcdfe8 | ||
|
|
9d6edd3847 | ||
|
|
9bdd44a4e4 | ||
|
|
8376c4a238 | ||
|
|
54d4b74172 | ||
|
|
e3b5146977 | ||
|
|
f2ff0448c4 | ||
|
|
5f5757f575 | ||
|
|
a3f417c43e | ||
|
|
d6118b520b | ||
|
|
f5ff57ff48 | ||
|
|
d8d207438e | ||
|
|
27b22807bc | ||
|
|
891bc9b73f | ||
|
|
5553e7fabe | ||
|
|
9ea961e87c | ||
|
|
c040b63595 | ||
|
|
b829b6ac5f | ||
|
|
be7fe35ba9 | ||
|
|
915a360a09 | ||
|
|
06aad62cb6 | ||
|
|
fa94b546fb | ||
|
|
1ecff8b3d9 | ||
|
|
9ee9548d47 | ||
|
|
4e702d3f3a | ||
|
|
006256e08b | ||
|
|
117a1a36cc | ||
|
|
1e602b2a77 | ||
|
|
460161b6ee | ||
|
|
030684fe60 | ||
|
|
b2713d5485 | ||
|
|
95512644b6 | ||
|
|
fb1c463a03 | ||
|
|
d2c0a7905b | ||
|
|
13f43bfa2f | ||
|
|
f7531a1095 | ||
|
|
02a92463e5 | ||
|
|
c38cf92f25 | ||
|
|
26878908c0 | ||
|
|
42008757f2 | ||
|
|
8e415cb451 | ||
|
|
23c3243209 | ||
|
|
3a19f3cef8 | ||
|
|
59b3d8c94d | ||
|
|
573dcab3d2 | ||
|
|
d7a0f84f64 | ||
|
|
797cc433b3 | ||
|
|
461ae9fd57 | ||
|
|
648f78c139 | ||
|
|
352ba03d02 | ||
|
|
278ef269cf | ||
|
|
25836634af | ||
|
|
a24ef15ff5 | ||
|
|
063d2b8adb | ||
|
|
420c945f94 | ||
|
|
bc8c665d40 | ||
|
|
e71c22b519 | ||
|
|
69fb5c9c88 | ||
|
|
623922d12d | ||
|
|
a20f7a5beb | ||
|
|
a3d9735ef8 | ||
|
|
cd80b8c386 | ||
|
|
8eca530275 |
119
.gitignore
vendored
|
|
@ -1,5 +1,116 @@
|
||||||
*.pyc
|
# Byte-compiled / optimized / DLL files
|
||||||
MANIFEST
|
__pycache__/
|
||||||
dist/
|
*.py[cod]
|
||||||
build/
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
include COPYING MANIFEST MANIFEST.in README trimage
|
include COPYING MANIFEST MANIFEST.in README.md bin/trimage
|
||||||
recursive-include desktop *.svg *.desktop
|
recursive-include desktop *.svg *.desktop
|
||||||
recursive-include src/ *.py *.png
|
recursive-include trimage/ *.py *.png
|
||||||
|
|
||||||
|
|
|
||||||
15
README
|
|
@ -1,15 +0,0 @@
|
||||||
Trimage image compressor
|
|
||||||
A cross-platform tool for optimizing PNG and JPG files.
|
|
||||||
|
|
||||||
Trimage is a cross-platform GUI and command-line interface to optimize image
|
|
||||||
files via "optipng":http://optipng.sourceforge.net/,
|
|
||||||
"advpng":http://advancemame.sourceforge.net/comp-readme.html and
|
|
||||||
"jpegoptim":http://www.kokkonen.net/tjko/projects.html, depending on the
|
|
||||||
filetype (currently, PNG and JPG files are supported). It was inspired by
|
|
||||||
"imageoptim":http://imageoptim.pornel.net/. All image files are losslessly
|
|
||||||
compressed on the highest available compression levels. Trimage gives you
|
|
||||||
various input functions to fit your own workflow: A regular file dialog,
|
|
||||||
dragging and dropping and various command line options.
|
|
||||||
|
|
||||||
Visit "Trimage.org":http://trimage.org for more information
|
|
||||||
|
|
||||||
44
README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
### Made by [@kilianvalkhof](https://twitter.com/kilianvalkhof)
|
||||||
|
|
||||||
|
#### Other projects:
|
||||||
|
|
||||||
|
- 💻 [Polypane](https://polypane.app) - Develop responsive websites and apps twice as fast on multiple screens at once
|
||||||
|
- 🖌️ [Superposition](https://superposition.design) - Kickstart your design system by extracting design tokens from your website
|
||||||
|
- 🗒️ [FromScratch](https://fromscratch.rocks) - A smart but simple autosaving scratchpad
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Trimage image compressor
|
||||||
|
|
||||||
|
A cross-platform tool for optimizing PNG and JPG files.
|
||||||
|
|
||||||
|
Trimage is a cross-platform GUI and command-line interface to optimize image files via [advpng](http://advancemame.sourceforge.net/comp-readme.html), [jpegoptim](http://www.kokkonen.net/tjko/projects.html), [optipng](http://optipng.sourceforge.net) and [pngcrush](https://pmt.sourceforge.io/pngcrush) depending on the
|
||||||
|
filetype (currently, PNG and JPG files are supported).
|
||||||
|
It was inspired by
|
||||||
|
[imageoptim](http://imageoptim.pornel.net).
|
||||||
|
|
||||||
|
All image files are losslessly
|
||||||
|
compressed on the highest available compression levels. Trimage gives you
|
||||||
|
various input functions to fit your own workflow: a regular file dialog,
|
||||||
|
dragging and dropping and various command line options.
|
||||||
|
|
||||||
|
## Installation instructions
|
||||||
|
|
||||||
|
Visit [Trimage.org](http://trimage.org) to install Trimage as a package.
|
||||||
|
|
||||||
|
## Building instructions
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- PyQt5
|
||||||
|
- advpng
|
||||||
|
- jpegoptim
|
||||||
|
- optipng
|
||||||
|
- pngcrush
|
||||||
|
|
||||||
|
### Build from source
|
||||||
|
|
||||||
|
Build and install by running:
|
||||||
|
|
||||||
|
python setup.py build
|
||||||
|
sudo python setup.py install
|
||||||
17
TODO.md
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Todo
|
||||||
|
|
||||||
|
- general refactoring
|
||||||
|
- sys.exit(1) for errors -- how to handle? Not good to simply sys.exit() from any random part of code (can leave things in a mess)
|
||||||
|
- consider context managers for handling compression, so as to keep operations atomic and/or rollback-able
|
||||||
|
- add a recursive option on the command-line for use with -d
|
||||||
|
- make -f accept a list of files
|
||||||
|
- make the current verbose be "normal", and make -verbose print the commandline app prints as well
|
||||||
|
- find a way to specify the version once for everywhere
|
||||||
|
- notification area drag/drop widget -> probably need gtk for gnome
|
||||||
|
- figure out how to make mac and win versions (someone else :) <- via gui2exe
|
||||||
|
- animate compressing.gif
|
||||||
|
- allow selection/deletion of rows from table (and subsequently the imagelist)
|
||||||
|
- punypng api? http://www.gracepointafterfive.com/punypng/api
|
||||||
|
- imagemagick/graphicsmagick?
|
||||||
|
- always on top option
|
||||||
|
- intelligently recompress, i.e. go through the list of files, recompress each until no more gains are seen (and a sensible number-of-tries limit isn't exceeded), and flag that file as fully-optimised. Repeat for each file in the list, until all are done. Saves pointlessly trying to optimise files. Consider the case of a directory of 100 files, already optimised once. Recompressing maximally compresses 90. Recompressing again would currently try to recompress all 100, when only 10 would be worthy of trying to compress further
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
#coding: utf-8
|
|
||||||
#
|
#
|
||||||
#Copyright (c) 2010 Kilian Valkhof, Paul Chaplin, Tarnay Kálmán
|
#Copyright (c) 2010 Kilian Valkhof, Paul Chaplin, Tarnay Kálmán
|
||||||
#
|
#
|
||||||
9
debian/changelog
vendored
|
|
@ -1,3 +1,11 @@
|
||||||
|
trimage (1.0.6-0ubuntu1) bionic; urgency=low
|
||||||
|
|
||||||
|
* Migrate to python 3 and python-qt 5
|
||||||
|
* fix bug with hanging thread
|
||||||
|
* remove hurry.filesize package
|
||||||
|
|
||||||
|
-- Kilian Valkhof <kilian@kilianvalkhof.com> Tue, 12 Mar 2019 11:54:00 +0200
|
||||||
|
|
||||||
trimage (1.0.5-0ubuntu1) jaunty; urgency=low
|
trimage (1.0.5-0ubuntu1) jaunty; urgency=low
|
||||||
|
|
||||||
* prevent images from becoming larger after recompression
|
* prevent images from becoming larger after recompression
|
||||||
|
|
@ -70,4 +78,3 @@ trimage (1.0.0b-0ubuntu1) jaunty; urgency=low
|
||||||
|
|
||||||
* Trimage image compressor
|
* Trimage image compressor
|
||||||
-- Kilian Valkhof <help@trimage.org> Tue, 23 Mar 2010 20:18:17 +0100
|
-- Kilian Valkhof <help@trimage.org> Tue, 23 Mar 2010 20:18:17 +0100
|
||||||
|
|
||||||
|
|
|
||||||
2
debian/compat
vendored
|
|
@ -1 +1 @@
|
||||||
7
|
11
|
||||||
|
|
|
||||||
9
debian/control
vendored
|
|
@ -2,15 +2,13 @@ Source: trimage
|
||||||
Section: graphics
|
Section: graphics
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Kilian Valkhof <kilian@kilianvalkhof.com>
|
Maintainer: Kilian Valkhof <kilian@kilianvalkhof.com>
|
||||||
Build-Depends: debhelper (>=7), python-support (>=0.8.7), python
|
Build-Depends: debhelper (>=7), python3
|
||||||
XS-Python-Version: >=2.6
|
Standards-Version: 4.3.0
|
||||||
Standards-Version: 3.9.1
|
|
||||||
Homepage: http://trimage.org
|
Homepage: http://trimage.org
|
||||||
|
|
||||||
Package: trimage
|
Package: trimage
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${misc:Depends}, ${python:Depends}, python-qt4 (>=4.4), optipng (>=0.6.2.1), advancecomp (>=1.15), jpegoptim (>=1.2.2), pngcrush (>=1.6.7)
|
Depends: ${misc:Depends}, ${python:Depends}, python-pyqt5 (>=5.7), optipng (>=0.6.2.1), advancecomp (>=1.15), jpegoptim (>=1.2.2), pngcrush (>=1.6.7)
|
||||||
XB-Python-Version: ${python:Versions}
|
|
||||||
Description: GUI and command-line interface to optimize image files
|
Description: GUI and command-line interface to optimize image files
|
||||||
Trimage is a cross-platform GUI and command-line interface to optimize image
|
Trimage is a cross-platform GUI and command-line interface to optimize image
|
||||||
files via optipng, advpng, pngcrush and jpegoptim, depending on the filetype
|
files via optipng, advpng, pngcrush and jpegoptim, depending on the filetype
|
||||||
|
|
@ -18,4 +16,3 @@ Description: GUI and command-line interface to optimize image files
|
||||||
compressed on the highest available compression levels. Trimage gives you
|
compressed on the highest available compression levels. Trimage gives you
|
||||||
various input functions to fit your own workflow: A regular file dialog,
|
various input functions to fit your own workflow: A regular file dialog,
|
||||||
dragging and dropping and various command line options.
|
dragging and dropping and various command line options.
|
||||||
|
|
||||||
|
|
|
||||||
44
debian/copyright
vendored
|
|
@ -39,49 +39,6 @@ The Debian packaging is:
|
||||||
|
|
||||||
and is licensed under the MIT license, see above.
|
and is licensed under the MIT license, see above.
|
||||||
|
|
||||||
hurry.filesize is:
|
|
||||||
|
|
||||||
Copyright (C) Martijn Faassen, Startifact
|
|
||||||
|
|
||||||
License:
|
|
||||||
|
|
||||||
Zope Public License (ZPL) Version 2.1
|
|
||||||
A copyright notice accompanies this license document that identifies the
|
|
||||||
copyright holders. This license has been certified as open source. It has
|
|
||||||
also been designated as GPL compatible by the Free Software Foundation (FSF).
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions in source code must retain the accompanying copyright
|
|
||||||
notice, this list of conditions, and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the accompanying
|
|
||||||
copyright notice, this list of conditions, and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
3. Names of the copyright holders must not be used to endorse or promote
|
|
||||||
products derived from this software without prior written permission from
|
|
||||||
the copyright holders.
|
|
||||||
4. The right to distribute this software or to use it for any purpose does
|
|
||||||
not give you the right to use Servicemarks (sm) or Trademarks (tm) of the
|
|
||||||
copyright holders. Use of them is covered by separate agreement with the
|
|
||||||
copyright holders.
|
|
||||||
5. If any files are modified, you must cause the modified files to carry
|
|
||||||
prominent notices stating that you changed the files and the date of any
|
|
||||||
change.
|
|
||||||
|
|
||||||
Disclaimer
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
|
||||||
EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT,
|
|
||||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
ThreadPool is:
|
ThreadPool is:
|
||||||
|
|
||||||
Copyright (c) Morten Holdflod Moeller
|
Copyright (c) Morten Holdflod Moeller
|
||||||
|
|
@ -253,4 +210,3 @@ License:
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
permanent authorization for you to choose that version for the
|
permanent authorization for you to choose that version for the
|
||||||
Library.
|
Library.
|
||||||
|
|
||||||
|
|
|
||||||
1
debian/pycompat
vendored
|
|
@ -1 +0,0 @@
|
||||||
2
|
|
||||||
1
debian/rules
vendored
Normal file → Executable file
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
%:
|
%:
|
||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@ Type=Application
|
||||||
Exec=trimage
|
Exec=trimage
|
||||||
Categories=Application;Qt;Graphics;
|
Categories=Application;Qt;Graphics;
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
|
Keywords=compression;compressor;images;jpg;jpeg;png;web;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
.\" This manual page is distributed under the terms
|
.\" This manual page is distributed under the terms
|
||||||
.\" of the GNU Free Documentation License version 1.3.
|
.\" of the GNU Free Documentation License version 1.3.
|
||||||
.\"
|
.\"
|
||||||
.TH TRIMAGE "1" "2011-07-11" "trimage 1.0.5" "User Commands"
|
.TH TRIMAGE "1" "2019-03-12" "trimage 1.0.6" "User Commands"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
trimage \- losslessly optimizing png and jpeg liles
|
trimage \- losslessly optimizing png and jpeg liles
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
==========================================
|
|
||||||
todo app wise
|
|
||||||
- general refactoring
|
|
||||||
- sys.exit(1) for errors -- how to handle? Not good to simply sys.exit() from
|
|
||||||
any random part of code (can leave things in a mess)
|
|
||||||
- consider context managers for handling compression, so as to keep operations
|
|
||||||
atomic and/or rollback-able
|
|
||||||
|
|
||||||
- add a recursive option on the command-line for use with -d
|
|
||||||
- make -f accept a list of files
|
|
||||||
- make the current verbose be "normal", and make -verbose print the commandline
|
|
||||||
app prints as well
|
|
||||||
|
|
||||||
- find a way to specify the version once for everywhere
|
|
||||||
- notification area drag/drop widget
|
|
||||||
-> probably need gtk for gnome
|
|
||||||
|
|
||||||
todo else
|
|
||||||
- figure out how to make mac and win versions (someone else :) <- via gui2exe
|
|
||||||
|
|
||||||
|
|
||||||
===========================================
|
|
||||||
later versions:
|
|
||||||
animate compressing.gif
|
|
||||||
allow selection/deletion of rows from table (and subsequently the imagelist)
|
|
||||||
punypng api? http://www.gracepointafterfive.com/punypng/api
|
|
||||||
imagemagick/graphicsmagick?
|
|
||||||
always on top option
|
|
||||||
intelligently recompress, i.e. go through the list of files, recompress
|
|
||||||
each until no more gains are seen (and a sensible number-of-tries limit
|
|
||||||
isn't exceeded), and flag that file as fully-optimised. Repeat for each
|
|
||||||
file in the list, until all are done. Saves pointlessly trying to
|
|
||||||
optimise files. Consider the case of a directory of 100 files, already
|
|
||||||
optimised once. Recompressing maximally compresses 90. Recompressing
|
|
||||||
again would currently try to recompress all 100, when only 10 would be
|
|
||||||
worthy of trying to compress further.
|
|
||||||
|
|
||||||
33
setup.py
|
|
@ -1,41 +1,22 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
|
||||||
win=(sys.platform == "win32")
|
|
||||||
if win:
|
|
||||||
import py2exe
|
|
||||||
sys.path.append("src/trimage")
|
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
|
|
||||||
setup(name = "trimage",
|
setup(name = "trimage",
|
||||||
version = "1.0.2",
|
version = "1.0.6",
|
||||||
description = "Trimage image compressor - A cross-platform tool for optimizing PNG and JPG files",
|
description = "Trimage image compressor - A cross-platform tool for optimizing PNG and JPG files",
|
||||||
author = "Kilian Valkhof, Paul Chaplin",
|
author = "Kilian Valkhof, Paul Chaplin",
|
||||||
author_email = "help@trimage.org",
|
author_email = "help@trimage.org",
|
||||||
url = "http://trimage.org",
|
url = "http://trimage.org",
|
||||||
license = "MIT license",
|
license = "MIT license",
|
||||||
package_dir = {'trimage' : 'src/trimage'},
|
|
||||||
packages = ["trimage",
|
packages = ["trimage",
|
||||||
"trimage.filesize",
|
"trimage.ThreadPool"],
|
||||||
"trimage.ThreadPool",],
|
|
||||||
package_data = {"trimage" : ["pixmaps/*.*"] },
|
package_data = {"trimage" : ["pixmaps/*.*"] },
|
||||||
data_files=[('share/icons/hicolor/scalable/apps', ['desktop/trimage.svg']),
|
data_files=[('share/icons/hicolor/scalable/apps', ['desktop/trimage.svg']),
|
||||||
('share/applications', ['desktop/trimage.desktop']),
|
('share/applications', ['desktop/trimage.desktop']),
|
||||||
('share/man/man1', ['doc/trimage.1'])],
|
('share/man/man1', ['doc/trimage.1'])],
|
||||||
scripts = ["trimage"],
|
scripts = ["bin/trimage"],
|
||||||
long_description = """Trimage is a cross-platform GUI and command-line interface to optimize image files via optipng, advpng and jpegoptim, depending on the filetype (currently, PNG and JPG files are supported). It was inspired by imageoptim. All image files are losslessy compressed on the highest available compression levels. Trimage gives you various input functions to fit your own workflow: A regular file dialog, dragging and dropping and various command line options.""",
|
long_description = """Trimage is a cross-platform GUI and command-line interface to optimize image files via advpng, jpegoptim, optipng and pngcrush, depending on the filetype (currently, PNG and JPG files are supported). It was inspired by imageoptim. All image files are losslessy compressed on the highest available compression levels. Trimage gives you various input functions to fit your own workflow: A regular file dialog, dragging and dropping and various command line options.""",
|
||||||
requires = ["PyQt4 (>=4.4)"],
|
requires = ["PyQt5"]
|
||||||
|
|
||||||
#for py2exe
|
|
||||||
windows=[r'src\trimage\trimage.py'],
|
|
||||||
zipfile=None,
|
|
||||||
options={"py2exe":{
|
|
||||||
"optimize":2,
|
|
||||||
"compressed":1,
|
|
||||||
"bundle_files":1,
|
|
||||||
"includes":["sip",],
|
|
||||||
"excludes":['email'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
from ThreadPool import ThreadPool, ThreadPoolMixIn
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
Metadata-Version: 1.0
|
|
||||||
Name: hurry.filesize
|
|
||||||
Version: 0.9
|
|
||||||
Summary: A simple Python library for human readable file sizes (or anything sized in bytes).
|
|
||||||
Home-page: UNKNOWN
|
|
||||||
Author: Martijn Faassen, Startifact
|
|
||||||
Author-email: faassen@startifact.com
|
|
||||||
License: ZPL 2.1
|
|
||||||
Description: hurry.filesize
|
|
||||||
==============
|
|
||||||
|
|
||||||
hurry.filesize a simple Python library that can take a number of bytes and
|
|
||||||
returns a human-readable string with the size in it, in kilobytes (K),
|
|
||||||
megabytes (M), etc.
|
|
||||||
|
|
||||||
The default system it uses is "traditional", where multipliers of 1024
|
|
||||||
increase the unit size::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import size
|
|
||||||
>>> size(1024)
|
|
||||||
'1K'
|
|
||||||
|
|
||||||
An alternative, slightly more verbose system::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import alternative
|
|
||||||
>>> size(1, system=alternative)
|
|
||||||
'1 byte'
|
|
||||||
>>> size(10, system=alternative)
|
|
||||||
'10 bytes'
|
|
||||||
>>> size(1024, system=alternative)
|
|
||||||
'1 KB'
|
|
||||||
|
|
||||||
A verbose system::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import verbose
|
|
||||||
>>> size(10, system=verbose)
|
|
||||||
'10 bytes'
|
|
||||||
>>> size(1024, system=verbose)
|
|
||||||
'1 kilobyte'
|
|
||||||
>>> size(2000, system=verbose)
|
|
||||||
'1 kilobyte'
|
|
||||||
>>> size(3000, system=verbose)
|
|
||||||
'2 kilobytes'
|
|
||||||
>>> size(1024 * 1024, system=verbose)
|
|
||||||
'1 megabyte'
|
|
||||||
>>> size(1024 * 1024 * 3, system=verbose)
|
|
||||||
'3 megabytes'
|
|
||||||
|
|
||||||
You can also use the SI system, where multipliers of 1000 increase the unit
|
|
||||||
size::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import si
|
|
||||||
>>> size(1000, system=si)
|
|
||||||
'1K'
|
|
||||||
|
|
||||||
|
|
||||||
Changes
|
|
||||||
=======
|
|
||||||
|
|
||||||
0.9 (2009-03-11)
|
|
||||||
----------------
|
|
||||||
|
|
||||||
* Initial public release.
|
|
||||||
|
|
||||||
Download
|
|
||||||
========
|
|
||||||
|
|
||||||
Keywords: file size bytes
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
hurry.filesize
|
|
||||||
==============
|
|
||||||
|
|
||||||
hurry.filesize a simple Python library that can take a number of bytes and
|
|
||||||
returns a human-readable string with the size in it, in kilobytes (K),
|
|
||||||
megabytes (M), etc.
|
|
||||||
|
|
||||||
The default system it uses is "traditional", where multipliers of 1024
|
|
||||||
increase the unit size::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import size
|
|
||||||
>>> size(1024)
|
|
||||||
'1K'
|
|
||||||
|
|
||||||
An alternative, slightly more verbose system::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import alternative
|
|
||||||
>>> size(1, system=alternative)
|
|
||||||
'1 byte'
|
|
||||||
>>> size(10, system=alternative)
|
|
||||||
'10 bytes'
|
|
||||||
>>> size(1024, system=alternative)
|
|
||||||
'1 KB'
|
|
||||||
|
|
||||||
A verbose system::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import verbose
|
|
||||||
>>> size(10, system=verbose)
|
|
||||||
'10 bytes'
|
|
||||||
>>> size(1024, system=verbose)
|
|
||||||
'1 kilobyte'
|
|
||||||
>>> size(2000, system=verbose)
|
|
||||||
'1 kilobyte'
|
|
||||||
>>> size(3000, system=verbose)
|
|
||||||
'2 kilobytes'
|
|
||||||
>>> size(1024 * 1024, system=verbose)
|
|
||||||
'1 megabyte'
|
|
||||||
>>> size(1024 * 1024 * 3, system=verbose)
|
|
||||||
'3 megabytes'
|
|
||||||
|
|
||||||
You can also use the SI system, where multipliers of 1000 increase the unit
|
|
||||||
size::
|
|
||||||
|
|
||||||
>>> from hurry.filesize import si
|
|
||||||
>>> size(1000, system=si)
|
|
||||||
'1K'
|
|
||||||
|
|
||||||
|
|
||||||
http://pypi.python.org/pypi/hurry.filesize
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
from filesize import size
|
|
||||||
from filesize import traditional, alternative, verbose, iec, si
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
'''
|
|
||||||
hurry.filesize
|
|
||||||
|
|
||||||
@author: Martijn Faassen, Startifact
|
|
||||||
@license: ZPL 2.1
|
|
||||||
'''
|
|
||||||
|
|
||||||
traditional = [
|
|
||||||
(1024 ** 5, 'P'),
|
|
||||||
(1024 ** 4, 'T'),
|
|
||||||
(1024 ** 3, 'G'),
|
|
||||||
(1024 ** 2, 'M'),
|
|
||||||
(1024 ** 1, 'K'),
|
|
||||||
(1024 ** 0, 'B'),
|
|
||||||
]
|
|
||||||
|
|
||||||
alternative = [
|
|
||||||
(1024 ** 5, ' PB'),
|
|
||||||
(1024 ** 4, ' TB'),
|
|
||||||
(1024 ** 3, ' GB'),
|
|
||||||
(1024 ** 2, ' MB'),
|
|
||||||
(1024 ** 1, ' KB'),
|
|
||||||
(1024 ** 0, (' byte', ' bytes')),
|
|
||||||
]
|
|
||||||
|
|
||||||
verbose = [
|
|
||||||
(1024 ** 5, (' petabyte', ' petabytes')),
|
|
||||||
(1024 ** 4, (' terabyte', ' terabytes')),
|
|
||||||
(1024 ** 3, (' gigabyte', ' gigabytes')),
|
|
||||||
(1024 ** 2, (' megabyte', ' megabytes')),
|
|
||||||
(1024 ** 1, (' kilobyte', ' kilobytes')),
|
|
||||||
(1024 ** 0, (' byte', ' bytes')),
|
|
||||||
]
|
|
||||||
|
|
||||||
iec = [
|
|
||||||
(1024 ** 5, 'Pi'),
|
|
||||||
(1024 ** 4, 'Ti'),
|
|
||||||
(1024 ** 3, 'Gi'),
|
|
||||||
(1024 ** 2, 'Mi'),
|
|
||||||
(1024 ** 1, 'Ki'),
|
|
||||||
(1024 ** 0, ''),
|
|
||||||
]
|
|
||||||
|
|
||||||
si = [
|
|
||||||
(1000 ** 5, 'P'),
|
|
||||||
(1000 ** 4, 'T'),
|
|
||||||
(1000 ** 3, 'G'),
|
|
||||||
(1000 ** 2, 'M'),
|
|
||||||
(1000 ** 1, 'K'),
|
|
||||||
(1000 ** 0, 'B'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def size(bytes, system=traditional):
|
|
||||||
"""Human-readable file size.
|
|
||||||
|
|
||||||
Using the traditional system, where a factor of 1024 is used::
|
|
||||||
|
|
||||||
>>> size(10)
|
|
||||||
'10B'
|
|
||||||
>>> size(100)
|
|
||||||
'100B'
|
|
||||||
>>> size(1000)
|
|
||||||
'1000B'
|
|
||||||
>>> size(2000)
|
|
||||||
'1K'
|
|
||||||
>>> size(10000)
|
|
||||||
'9K'
|
|
||||||
>>> size(20000)
|
|
||||||
'19K'
|
|
||||||
>>> size(100000)
|
|
||||||
'97K'
|
|
||||||
>>> size(200000)
|
|
||||||
'195K'
|
|
||||||
>>> size(1000000)
|
|
||||||
'976K'
|
|
||||||
>>> size(2000000)
|
|
||||||
'1M'
|
|
||||||
|
|
||||||
Using the SI system, with a factor 1000::
|
|
||||||
|
|
||||||
>>> size(10, system=si)
|
|
||||||
'10B'
|
|
||||||
>>> size(100, system=si)
|
|
||||||
'100B'
|
|
||||||
>>> size(1000, system=si)
|
|
||||||
'1K'
|
|
||||||
>>> size(2000, system=si)
|
|
||||||
'2K'
|
|
||||||
>>> size(10000, system=si)
|
|
||||||
'10K'
|
|
||||||
>>> size(20000, system=si)
|
|
||||||
'20K'
|
|
||||||
>>> size(100000, system=si)
|
|
||||||
'100K'
|
|
||||||
>>> size(200000, system=si)
|
|
||||||
'200K'
|
|
||||||
>>> size(1000000, system=si)
|
|
||||||
'1M'
|
|
||||||
>>> size(2000000, system=si)
|
|
||||||
'2M'
|
|
||||||
|
|
||||||
"""
|
|
||||||
for factor, suffix in system:
|
|
||||||
if bytes >= factor:
|
|
||||||
break
|
|
||||||
amount = int(bytes/factor)
|
|
||||||
if isinstance(suffix, tuple):
|
|
||||||
singular, multiple = suffix
|
|
||||||
if amount == 1:
|
|
||||||
suffix = singular
|
|
||||||
else:
|
|
||||||
suffix = multiple
|
|
||||||
return str(amount) + suffix
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
'''
|
'''
|
||||||
ThreadPool Implementation
|
ThreadPool Implementation
|
||||||
|
|
||||||
|
|
@ -8,7 +10,7 @@ ThreadPool Implementation
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
from threading import Thread, RLock
|
from threading import Thread, RLock
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from Queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -28,9 +30,6 @@ class ThreadPoolMixIn:
|
||||||
def __init__(self, threadpool=None):
|
def __init__(self, threadpool=None):
|
||||||
if (threadpool == None):
|
if (threadpool == None):
|
||||||
threadpool = ThreadPool()
|
threadpool = ThreadPool()
|
||||||
self.__private_threadpool = True
|
|
||||||
else:
|
|
||||||
self.__private_threadpool = False
|
|
||||||
|
|
||||||
self.__threadpool = threadpool
|
self.__threadpool = threadpool
|
||||||
|
|
||||||
|
|
@ -50,10 +49,6 @@ class ThreadPoolMixIn:
|
||||||
def process_request(self, request, client_address):
|
def process_request(self, request, client_address):
|
||||||
self.__threadpool.add_job(self.process_request_thread, [request, client_address])
|
self.__threadpool.add_job(self.process_request_thread, [request, client_address])
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
if (self.__private_threadpool): self.__threadpool.shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
class AddJobException(Exception):
|
class AddJobException(Exception):
|
||||||
'''
|
'''
|
||||||
Exceptoion raised when a Job could not be added
|
Exceptoion raised when a Job could not be added
|
||||||
|
|
@ -86,16 +81,16 @@ class ThreadPool:
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return_value = self.callable(*self.arguments) #IGNORE:W0142
|
return_value = self.callable(*self.arguments) #IGNORE:W0142
|
||||||
except Exception, excep: #IGNORE:W0703
|
except Exception as excep: #IGNORE:W0703
|
||||||
logger = logging.getLogger("threadpool.worker")
|
logger = logging.getLogger("threadpool.worker")
|
||||||
logger.warning("A job in the ThreadPool raised an exception: " + excep)
|
logger.warning("A job in the ThreadPool raised an exception: ", excep)
|
||||||
#else do nothing cause we don't know what to do...
|
#else do nothing cause we don't know what to do...
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if (self.return_callback != None):
|
if (self.return_callback != None):
|
||||||
self.return_callback(return_value)
|
self.return_callback(return_value)
|
||||||
except Exception, _: #IGNORE:W0703 everything could go wrong...
|
except Exception as _: #IGNORE:W0703 everything could go wrong...
|
||||||
logger = logging.getLogger('threadpool')
|
logger = logging.getLogger('threadpool')
|
||||||
logger.warning('Error while delivering return value to callback function')
|
logger.warning('Error while delivering return value to callback function')
|
||||||
|
|
||||||
1
trimage/ThreadPool/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from .ThreadPool import ThreadPool, ThreadPoolMixIn
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 676 B After Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
45
trimage/tools.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import errno
|
||||||
|
from subprocess import call, PIPE
|
||||||
|
|
||||||
|
|
||||||
|
def check_dependencies():
|
||||||
|
"""Check if the required command line apps exist."""
|
||||||
|
status = True
|
||||||
|
dependencies = {
|
||||||
|
"jpegoptim": "--version",
|
||||||
|
"optipng": "-v",
|
||||||
|
"advpng": "--version",
|
||||||
|
"pngcrush": "-version"
|
||||||
|
}
|
||||||
|
|
||||||
|
for elt in dependencies:
|
||||||
|
retcode = safe_call(elt + " " + dependencies[elt])
|
||||||
|
if retcode != 0:
|
||||||
|
status = False
|
||||||
|
print("[error] please install {}".format(elt), file=sys.stderr)
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def safe_call(command):
|
||||||
|
"""Cross-platform command-line check."""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
return call(command, shell=True, stdout=PIPE)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.EINTR:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def human_readable_size(num, suffix="B"):
|
||||||
|
"""Bytes to a readable size format"""
|
||||||
|
for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
|
||||||
|
if abs(num) < 1024.0:
|
||||||
|
return "%3.1f%s%s" % (num, unit, suffix)
|
||||||
|
num /= 1024.0
|
||||||
|
return "%.1f%s%s" % (num, "Y", suffix)
|
||||||
|
|
@ -1,32 +1,27 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import errno
|
from os import listdir, path, remove, access, W_OK
|
||||||
from os import listdir
|
|
||||||
from os import path
|
|
||||||
from os import remove
|
|
||||||
from os import access
|
|
||||||
from os import W_OK as WRITEABLE
|
|
||||||
from shutil import copy
|
from shutil import copy
|
||||||
from subprocess import call, PIPE
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
from PyQt4.QtCore import *
|
|
||||||
from PyQt4.QtGui import *
|
|
||||||
from filesize import *
|
|
||||||
from imghdr import what as determinetype
|
|
||||||
|
|
||||||
from Queue import Queue
|
|
||||||
from ThreadPool import ThreadPool
|
|
||||||
from multiprocessing import cpu_count
|
from multiprocessing import cpu_count
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
from PyQt5.QtCore import *
|
||||||
|
from PyQt5.QtGui import *
|
||||||
|
from PyQt5.QtWidgets import *
|
||||||
|
|
||||||
|
from ThreadPool import ThreadPool
|
||||||
from ui import Ui_trimage
|
from ui import Ui_trimage
|
||||||
|
from tools import *
|
||||||
VERSION = "1.0.5"
|
|
||||||
|
|
||||||
|
|
||||||
class StartQT4(QMainWindow):
|
VERSION = "1.0.6"
|
||||||
|
|
||||||
|
|
||||||
|
class StartQt(QMainWindow):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.ui = Ui_trimage()
|
self.ui = Ui_trimage()
|
||||||
|
|
@ -40,13 +35,14 @@ class StartQT4(QMainWindow):
|
||||||
QCoreApplication.setOrganizationDomain("trimage.org")
|
QCoreApplication.setOrganizationDomain("trimage.org")
|
||||||
QCoreApplication.setApplicationName("Trimage")
|
QCoreApplication.setApplicationName("Trimage")
|
||||||
self.settings = QSettings()
|
self.settings = QSettings()
|
||||||
self.restoreGeometry(self.settings.value("geometry").toByteArray())
|
if self.settings.value("geometry"):
|
||||||
|
self.restoreGeometry(self.settings.value("geometry"))
|
||||||
|
|
||||||
# check if apps are installed
|
# check if dependencies are installed
|
||||||
if self.checkapps():
|
if not check_dependencies():
|
||||||
quit()
|
quit()
|
||||||
|
|
||||||
#add quit shortcut
|
# add quit shortcut
|
||||||
if hasattr(QKeySequence, "Quit"):
|
if hasattr(QKeySequence, "Quit"):
|
||||||
self.quit_shortcut = QShortcut(QKeySequence(QKeySequence.Quit),
|
self.quit_shortcut = QShortcut(QKeySequence(QKeySequence.Quit),
|
||||||
self)
|
self)
|
||||||
|
|
@ -55,23 +51,17 @@ class StartQT4(QMainWindow):
|
||||||
|
|
||||||
# disable recompress
|
# disable recompress
|
||||||
self.ui.recompress.setEnabled(False)
|
self.ui.recompress.setEnabled(False)
|
||||||
#self.ui.recompress.hide()
|
|
||||||
|
|
||||||
# make a worker thread
|
# make a worker thread
|
||||||
self.thread = Worker()
|
self.thread = Worker()
|
||||||
|
|
||||||
# connect signals with slots
|
# connect signals with slots
|
||||||
QObject.connect(self.ui.addfiles, SIGNAL("clicked()"),
|
self.ui.addfiles.clicked.connect(self.file_dialog)
|
||||||
self.file_dialog)
|
self.ui.recompress.clicked.connect(self.recompress_files)
|
||||||
QObject.connect(self.ui.recompress, SIGNAL("clicked()"),
|
self.quit_shortcut.activated.connect(self.close)
|
||||||
self.recompress_files)
|
self.ui.processedfiles.drop_event_signal.connect(self.file_drop)
|
||||||
QObject.connect(self.quit_shortcut, SIGNAL("activated()"),
|
self.thread.finished.connect(self.update_table)
|
||||||
qApp, SLOT('quit()'))
|
self.thread.update_ui_signal.connect(self.update_table)
|
||||||
QObject.connect(self.ui.processedfiles, SIGNAL("fileDropEvent"),
|
|
||||||
self.file_drop)
|
|
||||||
QObject.connect(self.thread, SIGNAL("finished()"), self.update_table)
|
|
||||||
QObject.connect(self.thread, SIGNAL("terminated()"), self.update_table)
|
|
||||||
QObject.connect(self.thread, SIGNAL("updateUi"), self.update_table)
|
|
||||||
|
|
||||||
self.compressing_icon = QIcon(QPixmap(self.ui.get_image("pixmaps/compressing.gif")))
|
self.compressing_icon = QIcon(QPixmap(self.ui.get_image("pixmaps/compressing.gif")))
|
||||||
|
|
||||||
|
|
@ -82,11 +72,11 @@ class StartQT4(QMainWindow):
|
||||||
self.systemtray = Systray(self)
|
self.systemtray = Systray(self)
|
||||||
|
|
||||||
def commandline_options(self):
|
def commandline_options(self):
|
||||||
self.cli = False
|
|
||||||
"""Set up the command line options."""
|
"""Set up the command line options."""
|
||||||
|
self.cli = False
|
||||||
parser = OptionParser(version="%prog " + VERSION,
|
parser = OptionParser(version="%prog " + VERSION,
|
||||||
description="GUI front-end to compress png and jpg images via "
|
description="GUI front-end to compress png and jpg images via "
|
||||||
"optipng, advpng and jpegoptim")
|
"advpng, jpegoptim, optipng and pngcrush")
|
||||||
|
|
||||||
parser.set_defaults(verbose=True)
|
parser.set_defaults(verbose=True)
|
||||||
parser.add_option("-v", "--verbose", action="store_true",
|
parser.add_option("-v", "--verbose", action="store_true",
|
||||||
|
|
@ -103,14 +93,14 @@ class StartQT4(QMainWindow):
|
||||||
|
|
||||||
# make sure we quit after processing finished if using cli
|
# make sure we quit after processing finished if using cli
|
||||||
if options.filename or options.directory:
|
if options.filename or options.directory:
|
||||||
QObject.connect(self.thread, SIGNAL("finished()"), quit)
|
self.thread.finished.connect(quit)
|
||||||
self.cli = True
|
self.cli = True
|
||||||
|
|
||||||
# send to correct function
|
# send to correct function
|
||||||
if options.filename:
|
if options.filename:
|
||||||
self.file_from_cmd(options.filename.decode("utf-8"))
|
self.file_from_cmd(options.filename)
|
||||||
if options.directory:
|
if options.directory:
|
||||||
self.dir_from_cmd(options.directory.decode("utf-8"))
|
self.dir_from_cmd(options.directory)
|
||||||
|
|
||||||
self.verbose = options.verbose
|
self.verbose = options.verbose
|
||||||
|
|
||||||
|
|
@ -143,11 +133,12 @@ class StartQT4(QMainWindow):
|
||||||
def file_dialog(self):
|
def file_dialog(self):
|
||||||
"""Open a file dialog and send the selected images to compress_file."""
|
"""Open a file dialog and send the selected images to compress_file."""
|
||||||
fd = QFileDialog(self)
|
fd = QFileDialog(self)
|
||||||
fd.restoreState(self.settings.value("fdstate").toByteArray())
|
if (self.settings.value("fdstate")):
|
||||||
directory = self.settings.value("directory", QVariant("")).toString()
|
fd.restoreState(self.settings.value("fdstate"))
|
||||||
|
directory = self.settings.value("directory", QVariant(""))
|
||||||
fd.setDirectory(directory)
|
fd.setDirectory(directory)
|
||||||
|
|
||||||
images = fd.getOpenFileNames(self,
|
images, _ = fd.getOpenFileNames(self,
|
||||||
"Select one or more image files to compress",
|
"Select one or more image files to compress",
|
||||||
directory,
|
directory,
|
||||||
# this is a fix for file dialog differentiating between cases
|
# this is a fix for file dialog differentiating between cases
|
||||||
|
|
@ -155,8 +146,8 @@ class StartQT4(QMainWindow):
|
||||||
|
|
||||||
self.settings.setValue("fdstate", QVariant(fd.saveState()))
|
self.settings.setValue("fdstate", QVariant(fd.saveState()))
|
||||||
if images:
|
if images:
|
||||||
self.settings.setValue("directory", QVariant(path.dirname(unicode(images[0]))))
|
self.settings.setValue("directory", QVariant(path.dirname(images[0])))
|
||||||
self.delegator([unicode(fullpath) for fullpath in images])
|
self.delegator([fullpath for fullpath in images])
|
||||||
|
|
||||||
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."""
|
||||||
|
|
@ -173,15 +164,15 @@ class StartQT4(QMainWindow):
|
||||||
delegatorlist = []
|
delegatorlist = []
|
||||||
for fullpath in images:
|
for fullpath in images:
|
||||||
try: # recompress images already in the list
|
try: # recompress images already in the list
|
||||||
image = (i.image for i in self.imagelist
|
image = next(i.image for i in self.imagelist
|
||||||
if i.image.fullpath == fullpath).next()
|
if i.image.fullpath == fullpath)
|
||||||
if image.compressed:
|
if image.compressed:
|
||||||
image.reset()
|
image.reset()
|
||||||
image.recompression = True
|
image.recompression = True
|
||||||
delegatorlist.append(image)
|
delegatorlist.append(image)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
if not path.isdir(fullpath):
|
if not path.isdir(fullpath):
|
||||||
self. add_image(fullpath, delegatorlist)
|
self.add_image(fullpath, delegatorlist)
|
||||||
else:
|
else:
|
||||||
self.walk(fullpath, delegatorlist)
|
self.walk(fullpath, delegatorlist)
|
||||||
|
|
||||||
|
|
@ -191,7 +182,7 @@ class StartQT4(QMainWindow):
|
||||||
|
|
||||||
def walk(self, dir, delegatorlist):
|
def walk(self, dir, delegatorlist):
|
||||||
"""
|
"""
|
||||||
Walks a directory, and executes a callback on each file
|
Walks a directory, and executes a callback on each file.
|
||||||
"""
|
"""
|
||||||
dir = path.abspath(dir)
|
dir = path.abspath(dir)
|
||||||
for file in [file for file in listdir(dir) if not file in [".","..",".svn",".git",".hg",".bzr",".cvs"]]:
|
for file in [file for file in listdir(dir) if not file in [".","..",".svn",".git",".hg",".bzr",".cvs"]]:
|
||||||
|
|
@ -204,7 +195,7 @@ class StartQT4(QMainWindow):
|
||||||
|
|
||||||
def add_image(self, fullpath, delegatorlist):
|
def add_image(self, fullpath, delegatorlist):
|
||||||
"""
|
"""
|
||||||
Adds an image file to the delegator list and update the tray and the title of the window
|
Adds an image file to the delegator list and update the tray and the title of the window.
|
||||||
"""
|
"""
|
||||||
image = Image(fullpath)
|
image = Image(fullpath)
|
||||||
if image.valid:
|
if image.valid:
|
||||||
|
|
@ -214,7 +205,7 @@ class StartQT4(QMainWindow):
|
||||||
self.systemtray.trayIcon.setToolTip("Trimage image compressor (" + str(len(self.imagelist)) + " files)")
|
self.systemtray.trayIcon.setToolTip("Trimage image compressor (" + str(len(self.imagelist)) + " files)")
|
||||||
self.setWindowTitle("Trimage image compressor (" + str(len(self.imagelist)) + " files)")
|
self.setWindowTitle("Trimage image compressor (" + str(len(self.imagelist)) + " files)")
|
||||||
else:
|
else:
|
||||||
print >> sys.stderr, u"[error] %s not a supported image file and/or not writeable" % image.fullpath
|
print("[error] {} not a supported image file and/or not writable".format(image.fullpath), file=sys.stderr)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
UI Functions
|
UI Functions
|
||||||
|
|
@ -247,52 +238,12 @@ class StartQT4(QMainWindow):
|
||||||
# enable recompress button
|
# enable recompress button
|
||||||
self.enable_recompress()
|
self.enable_recompress()
|
||||||
|
|
||||||
"""
|
|
||||||
Helper functions
|
|
||||||
"""
|
|
||||||
|
|
||||||
def enable_recompress(self):
|
def enable_recompress(self):
|
||||||
"""Enable the recompress button."""
|
"""Enable the recompress button."""
|
||||||
self.ui.recompress.setEnabled(True)
|
self.ui.recompress.setEnabled(True)
|
||||||
if QSystemTrayIcon.isSystemTrayAvailable() and not self.cli:
|
if QSystemTrayIcon.isSystemTrayAvailable() and not self.cli:
|
||||||
self.systemtray.recompress.setEnabled(True)
|
self.systemtray.recompress.setEnabled(True)
|
||||||
|
|
||||||
def checkapps(self):
|
|
||||||
"""Check if the required command line apps exist."""
|
|
||||||
exe = ".exe" if (sys.platform == "win32") else ""
|
|
||||||
status = False
|
|
||||||
retcode = self.safe_call("jpegoptim" + exe + " --version")
|
|
||||||
if retcode != 0:
|
|
||||||
status = True
|
|
||||||
sys.stderr.write("[error] please install jpegoptim")
|
|
||||||
|
|
||||||
retcode = self.safe_call("optipng" + exe + " -v")
|
|
||||||
if retcode != 0:
|
|
||||||
status = True
|
|
||||||
sys.stderr.write("[error] please install optipng")
|
|
||||||
|
|
||||||
retcode = self.safe_call("advpng" + exe + " --version")
|
|
||||||
if retcode != 0:
|
|
||||||
status = True
|
|
||||||
sys.stderr.write("[error] please install advancecomp")
|
|
||||||
|
|
||||||
retcode = self.safe_call("pngcrush" + exe + " -version")
|
|
||||||
if retcode != 0:
|
|
||||||
status = True
|
|
||||||
sys.stderr.write("[error] please install pngcrush")
|
|
||||||
return status
|
|
||||||
|
|
||||||
def safe_call(self, command):
|
|
||||||
""" cross-platform command-line check """
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
return call(command, shell=True, stdout=PIPE)
|
|
||||||
except OSError, e:
|
|
||||||
if e.errno == errno.EINTR:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def hide_main_window(self):
|
def hide_main_window(self):
|
||||||
if self.isVisible():
|
if self.isVisible():
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
@ -309,7 +260,6 @@ class StartQT4(QMainWindow):
|
||||||
|
|
||||||
|
|
||||||
class TriTableModel(QAbstractTableModel):
|
class TriTableModel(QAbstractTableModel):
|
||||||
|
|
||||||
def __init__(self, parent, imagelist, header, *args):
|
def __init__(self, parent, imagelist, header, *args):
|
||||||
"""
|
"""
|
||||||
@param parent Qt parent object.
|
@param parent Qt parent object.
|
||||||
|
|
@ -351,15 +301,15 @@ class TriTableModel(QAbstractTableModel):
|
||||||
|
|
||||||
|
|
||||||
class ImageRow:
|
class ImageRow:
|
||||||
|
|
||||||
def __init__(self, image, waitingIcon=None):
|
def __init__(self, image, waitingIcon=None):
|
||||||
""" Build the information visible in the table image row. """
|
"""Build the information visible in the table image row."""
|
||||||
self.image = image
|
self.image = image
|
||||||
|
|
||||||
d = {
|
d = {
|
||||||
'shortname': lambda i: self.statusStr() % i.shortname,
|
'filename_w_ext': lambda i: self.statusStr().format(i.filename_w_ext),
|
||||||
'oldfilesizestr': lambda i: size(i.oldfilesize, system=alternative)
|
'oldfilesizestr': lambda i: human_readable_size(i.oldfilesize)
|
||||||
if i.compressed else "",
|
if i.compressed else "",
|
||||||
'newfilesizestr': lambda i: size(i.newfilesize, system=alternative)
|
'newfilesizestr': lambda i: human_readable_size(i.newfilesize)
|
||||||
if i.compressed else "",
|
if i.compressed else "",
|
||||||
'ratiostr': lambda i:
|
'ratiostr': lambda i:
|
||||||
"%.1f%%" % (100 - (float(i.newfilesize) / i.oldfilesize * 100))
|
"%.1f%%" % (100 - (float(i.newfilesize) / i.oldfilesize * 100))
|
||||||
|
|
@ -367,7 +317,7 @@ class ImageRow:
|
||||||
'icon': lambda i: i.icon if i.compressed else waitingIcon,
|
'icon': lambda i: i.icon if i.compressed else waitingIcon,
|
||||||
'fullpath': lambda i: i.fullpath, #only used by cli
|
'fullpath': lambda i: i.fullpath, #only used by cli
|
||||||
}
|
}
|
||||||
names = ['shortname', 'oldfilesizestr', 'newfilesizestr',
|
names = ['filename_w_ext', 'oldfilesizestr', 'newfilesizestr',
|
||||||
'ratiostr', 'icon']
|
'ratiostr', 'icon']
|
||||||
for i, n in enumerate(names):
|
for i, n in enumerate(names):
|
||||||
d[i] = d[n]
|
d[i] = d[n]
|
||||||
|
|
@ -375,47 +325,40 @@ class ImageRow:
|
||||||
self.d = d
|
self.d = d
|
||||||
|
|
||||||
def statusStr(self):
|
def statusStr(self):
|
||||||
""" Set the status message. """
|
"""Set the status message."""
|
||||||
if self.image.failed:
|
if self.image.failed:
|
||||||
return "ERROR: %s"
|
return "ERROR: {0}"
|
||||||
if self.image.compressing:
|
if self.image.compressing:
|
||||||
message = "Compressing %s..."
|
message = "Compressing {0}..."
|
||||||
return message
|
return message
|
||||||
if not self.image.compressed and self.image.recompression:
|
if not self.image.compressed and self.image.recompression:
|
||||||
return "Queued for recompression..."
|
return "Queued for recompression {0}..."
|
||||||
if not self.image.compressed:
|
if not self.image.compressed:
|
||||||
return "Queued..."
|
return "Queued {0}..."
|
||||||
return "%s"
|
return "{0}"
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return self.d[key](self.image)
|
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.reset()
|
||||||
self.fullpath = fullpath
|
self.fullpath = fullpath
|
||||||
if path.isfile(self.fullpath) and access(self.fullpath, WRITEABLE):
|
self.filename_w_ext = path.basename(self.fullpath)
|
||||||
self.filetype = determinetype(self.fullpath)
|
self.filename, self.filetype = path.splitext(self.filename_w_ext)
|
||||||
|
if path.isfile(self.fullpath) and access(self.fullpath, W_OK):
|
||||||
|
self.filetype = self.filetype[1:].lower()
|
||||||
|
if self.filetype == "jpg":
|
||||||
|
self.filetype = "jpeg"
|
||||||
if self.filetype in ["jpeg", "png"]:
|
if self.filetype in ["jpeg", "png"]:
|
||||||
oldfile = QFileInfo(self.fullpath)
|
oldfile = QFileInfo(self.fullpath)
|
||||||
self.shortname = oldfile.fileName()
|
|
||||||
self.oldfilesize = oldfile.size()
|
self.oldfilesize = oldfile.size()
|
||||||
self.icon = QIcon(self.fullpath)
|
self.icon = QIcon(self.fullpath)
|
||||||
self.valid = True
|
self.valid = True
|
||||||
|
|
||||||
#def _determinetype(self):
|
|
||||||
# """ Determine the filetype of the file using imghdr. """
|
|
||||||
# filetype = determinetype(self.fullpath)
|
|
||||||
# if filetype in ["jpeg", "png"]:
|
|
||||||
# self.filetype = filetype
|
|
||||||
# else:
|
|
||||||
# self.filetype = None
|
|
||||||
# return self.filetype
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.failed = False
|
self.failed = False
|
||||||
self.compressed = False
|
self.compressed = False
|
||||||
|
|
@ -423,19 +366,19 @@ class Image:
|
||||||
self.recompression = 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 \
|
raise "Tried to compress invalid image (unsupported format or not \
|
||||||
file)"
|
file)"
|
||||||
self.reset()
|
self.reset()
|
||||||
self.compressing = True
|
self.compressing = True
|
||||||
exe = ".exe" if (sys.platform == "win32") else ""
|
|
||||||
runString = {
|
runString = {
|
||||||
"jpeg": u"jpegoptim" + exe + " -f --strip-all '%(file)s'",
|
"jpeg": "jpegoptim -f --strip-all '%(file)s'",
|
||||||
"png": u"optipng" + exe + " -force -o7 '%(file)s'&&advpng" + exe + " -z4 '%(file)s' && pngcrush -rem gAMA -rem alla -rem cHRM -rem iCCP -rem sRGB -rem time '%(file)s' '%(file)s.bak' && mv '%(file)s.bak' '%(file)s'"
|
"png": "optipng -force -o7 '%(file)s'&&advpng -z4 '%(file)s' && pngcrush -rem gAMA -rem alla -rem cHRM -rem iCCP -rem sRGB -rem time '%(file)s' '%(file)s.bak' && mv '%(file)s.bak' '%(file)s'"
|
||||||
}
|
}
|
||||||
# Create a backup file
|
# create a backup file
|
||||||
copy(self.fullpath, self.fullpath + '~')
|
backupfullpath = '/tmp/' + self.filename_w_ext
|
||||||
|
copy(self.fullpath, backupfullpath)
|
||||||
try:
|
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)
|
||||||
|
|
@ -445,13 +388,13 @@ class Image:
|
||||||
self.newfilesize = QFile(self.fullpath).size()
|
self.newfilesize = QFile(self.fullpath).size()
|
||||||
self.compressed = True
|
self.compressed = True
|
||||||
|
|
||||||
# Checks the new file and copy the backup
|
# checks the new file and copy the backup
|
||||||
if self.newfilesize >= self.oldfilesize:
|
if self.newfilesize >= self.oldfilesize:
|
||||||
copy(self.fullpath + '~', self.fullpath)
|
copy(backupfullpath, self.fullpath)
|
||||||
self.newfilesize = self.oldfilesize
|
self.newfilesize = self.oldfilesize
|
||||||
|
|
||||||
# Removes the backup file
|
# removes the backup file
|
||||||
remove(self.fullpath + '~')
|
remove(backupfullpath)
|
||||||
else:
|
else:
|
||||||
self.failed = True
|
self.failed = True
|
||||||
self.compressing = False
|
self.compressing = False
|
||||||
|
|
@ -460,15 +403,13 @@ class Image:
|
||||||
|
|
||||||
|
|
||||||
class Worker(QThread):
|
class Worker(QThread):
|
||||||
|
update_ui_signal = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QThread.__init__(self, parent)
|
QThread.__init__(self, parent)
|
||||||
self.toDisplay = Queue()
|
self.toDisplay = Queue()
|
||||||
self.threadpool = ThreadPool(max_workers=cpu_count())
|
self.threadpool = ThreadPool(max_workers=cpu_count())
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.threadpool.shutdown()
|
|
||||||
|
|
||||||
def compress_file(self, images, showapp, verbose, imagelist):
|
def compress_file(self, images, showapp, verbose, imagelist):
|
||||||
"""Start the worker thread."""
|
"""Start the worker thread."""
|
||||||
for image in images:
|
for image in images:
|
||||||
|
|
@ -488,7 +429,7 @@ class Worker(QThread):
|
||||||
tp._ThreadPool__jobs.empty()):
|
tp._ThreadPool__jobs.empty()):
|
||||||
image = self.toDisplay.get()
|
image = self.toDisplay.get()
|
||||||
|
|
||||||
self.emit(SIGNAL("updateUi"))
|
self.update_ui_signal.emit()
|
||||||
|
|
||||||
if not self.showapp and self.verbose: # we work via the commandline
|
if not self.showapp and self.verbose: # we work via the commandline
|
||||||
if image.retcode == 0:
|
if image.retcode == 0:
|
||||||
|
|
@ -497,11 +438,10 @@ class Worker(QThread):
|
||||||
+ ir['oldfilesizestr'] + ", New Size: "
|
+ ir['oldfilesizestr'] + ", New Size: "
|
||||||
+ ir['newfilesizestr'] + ", Ratio: " + ir['ratiostr'])
|
+ ir['newfilesizestr'] + ", Ratio: " + ir['ratiostr'])
|
||||||
else:
|
else:
|
||||||
print >> sys.stderr, u"[error] %s could not be compressed" % image.fullpath
|
print("[error] {} could not be compressed".format(image.fullpath), file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
class Systray(QWidget):
|
class Systray(QWidget):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QWidget.__init__(self)
|
QWidget.__init__(self)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
@ -511,15 +451,14 @@ class Systray(QWidget):
|
||||||
|
|
||||||
def createActions(self):
|
def createActions(self):
|
||||||
self.quitAction = QAction(self.tr("&Quit"), self)
|
self.quitAction = QAction(self.tr("&Quit"), self)
|
||||||
QObject.connect(self.quitAction, SIGNAL("triggered()"),
|
self.quitAction.triggered.connect(self.parent.close)
|
||||||
qApp, SLOT("quit()"))
|
|
||||||
|
|
||||||
self.addFiles = QAction(self.tr("&Add and compress"), self)
|
self.addFiles = QAction(self.tr("&Add and compress"), self)
|
||||||
icon = QIcon()
|
icon = QIcon()
|
||||||
icon.addPixmap(QPixmap(self.parent.ui.get_image(("pixmaps/list-add.png"))),
|
icon.addPixmap(QPixmap(self.parent.ui.get_image(("pixmaps/list-add.png"))),
|
||||||
QIcon.Normal, QIcon.Off)
|
QIcon.Normal, QIcon.Off)
|
||||||
self.addFiles.setIcon(icon)
|
self.addFiles.setIcon(icon)
|
||||||
QObject.connect(self.addFiles, SIGNAL("triggered()"), self.parent.file_dialog)
|
self.addFiles.triggered.connect(self.parent.file_dialog)
|
||||||
|
|
||||||
self.recompress = QAction(self.tr("&Recompress"), self)
|
self.recompress = QAction(self.tr("&Recompress"), self)
|
||||||
icon2 = QIcon()
|
icon2 = QIcon()
|
||||||
|
|
@ -527,10 +466,11 @@ class Systray(QWidget):
|
||||||
QIcon.Normal, QIcon.Off)
|
QIcon.Normal, QIcon.Off)
|
||||||
self.recompress.setIcon(icon2)
|
self.recompress.setIcon(icon2)
|
||||||
self.recompress.setDisabled(True)
|
self.recompress.setDisabled(True)
|
||||||
QObject.connect(self.addFiles, SIGNAL("triggered()"), self.parent.recompress_files)
|
|
||||||
|
self.addFiles.triggered.connect(self.parent.recompress_files)
|
||||||
|
|
||||||
self.hideMain = QAction(self.tr("&Hide window"), self)
|
self.hideMain = QAction(self.tr("&Hide window"), self)
|
||||||
QObject.connect(self.hideMain, SIGNAL("triggered()"), self.parent.hide_main_window)
|
self.hideMain.triggered.connect(self.parent.hide_main_window)
|
||||||
|
|
||||||
def createTrayIcon(self):
|
def createTrayIcon(self):
|
||||||
self.trayIconMenu = QMenu(self)
|
self.trayIconMenu = QMenu(self)
|
||||||
|
|
@ -550,7 +490,7 @@ class Systray(QWidget):
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
myapp = StartQT4()
|
myapp = StartQt()
|
||||||
|
|
||||||
if myapp.showapp:
|
if myapp.showapp:
|
||||||
myapp.show()
|
myapp.show()
|
||||||
|
|
@ -1,8 +1,16 @@
|
||||||
from PyQt4.QtCore import *
|
#!/usr/bin/env python3
|
||||||
from PyQt4.QtGui import *
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
|
from PyQt5.QtCore import *
|
||||||
|
from PyQt5.QtGui import *
|
||||||
|
from PyQt5.QtWidgets import *
|
||||||
|
|
||||||
|
|
||||||
class TrimageTableView(QTableView):
|
class TrimageTableView(QTableView):
|
||||||
|
|
||||||
|
drop_event_signal = pyqtSignal(list)
|
||||||
|
|
||||||
"""Init the table drop event."""
|
"""Init the table drop event."""
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(TrimageTableView, self).__init__(parent)
|
super(TrimageTableView, self).__init__(parent)
|
||||||
|
|
@ -21,19 +29,19 @@ class TrimageTableView(QTableView):
|
||||||
event.accept()
|
event.accept()
|
||||||
filelist = []
|
filelist = []
|
||||||
for url in event.mimeData().urls():
|
for url in event.mimeData().urls():
|
||||||
filelist.append(unicode(url.toLocalFile()))
|
filelist.append(url.toLocalFile())
|
||||||
|
|
||||||
self.emit(SIGNAL("fileDropEvent"), (filelist))
|
self.drop_event_signal.emit(filelist)
|
||||||
|
|
||||||
|
|
||||||
class Ui_trimage(object):
|
class Ui_trimage():
|
||||||
def get_image(self, image):
|
def get_image(self, image):
|
||||||
""" Get the correct link to the images used in the UI """
|
"""Get the correct link to the images used in the UI."""
|
||||||
imagelink = path.join(path.dirname(path.dirname(path.realpath(__file__))), "trimage/" + image)
|
imagelink = path.join(path.dirname(path.dirname(path.realpath(__file__))), "trimage/" + image)
|
||||||
return imagelink
|
return imagelink
|
||||||
|
|
||||||
def setupUi(self, trimage):
|
def setupUi(self, trimage):
|
||||||
""" Setup the entire UI """
|
"""Setup the entire UI."""
|
||||||
trimage.setObjectName("trimage")
|
trimage.setObjectName("trimage")
|
||||||
trimage.resize(600, 170)
|
trimage.resize(600, 170)
|
||||||
|
|
||||||
|
|
@ -44,7 +52,7 @@ class Ui_trimage(object):
|
||||||
self.centralwidget.setObjectName("centralwidget")
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
|
||||||
self.gridLayout_2 = QGridLayout(self.centralwidget)
|
self.gridLayout_2 = QGridLayout(self.centralwidget)
|
||||||
self.gridLayout_2.setMargin(0)
|
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout_2.setSpacing(0)
|
self.gridLayout_2.setSpacing(0)
|
||||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
|
||||||
|
|
@ -60,7 +68,7 @@ class Ui_trimage(object):
|
||||||
|
|
||||||
self.verticalLayout = QVBoxLayout(self.widget)
|
self.verticalLayout = QVBoxLayout(self.widget)
|
||||||
self.verticalLayout.setSpacing(0)
|
self.verticalLayout.setSpacing(0)
|
||||||
self.verticalLayout.setMargin(0)
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
|
||||||
self.frame = QFrame(self.widget)
|
self.frame = QFrame(self.widget)
|
||||||
|
|
@ -68,12 +76,12 @@ class Ui_trimage(object):
|
||||||
|
|
||||||
self.verticalLayout_2 = QVBoxLayout(self.frame)
|
self.verticalLayout_2 = QVBoxLayout(self.frame)
|
||||||
self.verticalLayout_2.setSpacing(0)
|
self.verticalLayout_2.setSpacing(0)
|
||||||
self.verticalLayout_2.setMargin(0)
|
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
|
|
||||||
self.horizontalLayout = QHBoxLayout()
|
self.horizontalLayout = QHBoxLayout()
|
||||||
self.horizontalLayout.setSpacing(0)
|
self.horizontalLayout.setSpacing(0)
|
||||||
self.horizontalLayout.setMargin(10)
|
self.horizontalLayout.setContentsMargins(10, 10, 10, 10)
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
|
||||||
self.addfiles = QPushButton(self.frame)
|
self.addfiles = QPushButton(self.frame)
|
||||||
|
|
@ -93,7 +101,7 @@ class Ui_trimage(object):
|
||||||
font.setPointSize(8)
|
font.setPointSize(8)
|
||||||
self.label.setFont(font)
|
self.label.setFont(font)
|
||||||
self.label.setFrameShadow(QFrame.Plain)
|
self.label.setFrameShadow(QFrame.Plain)
|
||||||
self.label.setMargin(1)
|
self.label.setContentsMargins(1, 1, 1, 1)
|
||||||
self.label.setIndent(10)
|
self.label.setIndent(10)
|
||||||
self.label.setObjectName("label")
|
self.label.setObjectName("label")
|
||||||
self.horizontalLayout.addWidget(self.label)
|
self.horizontalLayout.addWidget(self.label)
|
||||||
|
|
@ -141,26 +149,24 @@ class Ui_trimage(object):
|
||||||
QMetaObject.connectSlotsByName(trimage)
|
QMetaObject.connectSlotsByName(trimage)
|
||||||
|
|
||||||
def retranslateUi(self, trimage):
|
def retranslateUi(self, trimage):
|
||||||
""" Fill in the texts for all UI elements """
|
"""Fill in the texts for all UI elements."""
|
||||||
trimage.setWindowTitle(QApplication.translate("trimage",
|
trimage.setWindowTitle(QApplication.translate("trimage",
|
||||||
"Trimage image compressor", None, QApplication.UnicodeUTF8))
|
"Trimage image compressor", None))
|
||||||
self.addfiles.setToolTip(QApplication.translate("trimage",
|
self.addfiles.setToolTip(QApplication.translate("trimage",
|
||||||
"Add file to the compression list", None,
|
"Add file to the compression list", None))
|
||||||
QApplication.UnicodeUTF8))
|
|
||||||
self.addfiles.setText(QApplication.translate("trimage",
|
self.addfiles.setText(QApplication.translate("trimage",
|
||||||
"&Add and compress", None, QApplication.UnicodeUTF8))
|
"&Add and compress", None))
|
||||||
self.addfiles.setShortcut(QApplication.translate("trimage",
|
self.addfiles.setShortcut(QApplication.translate("trimage",
|
||||||
"Alt+A", None, QApplication.UnicodeUTF8))
|
"Alt+A", None))
|
||||||
self.label.setText(QApplication.translate("trimage",
|
self.label.setText(QApplication.translate("trimage",
|
||||||
"Drag and drop images onto the table", None,
|
"Drag and drop images onto the table", None))
|
||||||
QApplication.UnicodeUTF8))
|
|
||||||
self.recompress.setToolTip(QApplication.translate("trimage",
|
self.recompress.setToolTip(QApplication.translate("trimage",
|
||||||
"Recompress all images", None, QApplication.UnicodeUTF8))
|
"Recompress all images", None))
|
||||||
self.recompress.setText(QApplication.translate("trimage",
|
self.recompress.setText(QApplication.translate("trimage",
|
||||||
"&Recompress", None, QApplication.UnicodeUTF8))
|
"&Recompress", None))
|
||||||
self.recompress.setShortcut(QApplication.translate("trimage",
|
self.recompress.setShortcut(QApplication.translate("trimage",
|
||||||
"Alt+R", None, QApplication.UnicodeUTF8))
|
"Alt+R", None))
|
||||||
self.processedfiles.setToolTip(QApplication.translate("trimage",
|
self.processedfiles.setToolTip(QApplication.translate("trimage",
|
||||||
"Drag files in here", None, QApplication.UnicodeUTF8))
|
"Drag files in here", None))
|
||||||
self.processedfiles.setWhatsThis(QApplication.translate("trimage",
|
self.processedfiles.setWhatsThis(QApplication.translate("trimage",
|
||||||
"Drag files in here", None, QApplication.UnicodeUTF8))
|
"Drag files in here", None))
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf8">
|
<meta charset="utf8" />
|
||||||
<title>Trimage (lossless) image compressor</title>
|
<title>Trimage (lossless) image compressor</title>
|
||||||
<link href="https://raw.github.com/Kilian/sencss/master/source/sen.css" rel="stylesheet" type="text/css">
|
<link
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background:#fff
|
background:#fff
|
||||||
font-size:13px;
|
font-size:13px;
|
||||||
|
font-family:sans-serif;
|
||||||
}
|
}
|
||||||
#wrap {
|
#wrap {
|
||||||
position:relative;
|
position:relative;
|
||||||
|
|
@ -67,153 +72,155 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="wrap">
|
<div id="wrap">
|
||||||
<h1><img src="trimage-icon.png" alt=""> Trimage image compressor – 1.0.5</h1>
|
<h1>
|
||||||
<span class="subtitle">A cross-platform tool for losslessly optimizing PNG and JPG files.</span>
|
<img src="trimage-icon.png" alt="" /> Trimage image compressor –
|
||||||
<p class="tri">Trimage is a cross-platform GUI and command-line interface to optimize image
|
1.0.6
|
||||||
files for websites, using <a href="http://optipng.sourceforge.net/">optipng</a>, <a href="http://pmt.sourceforge.net/pngcrush/">pngcrush</a>,
|
</h1>
|
||||||
<a href="http://advancemame.sourceforge.net/comp-readme.html">advpng</a> and
|
<span class="subtitle"
|
||||||
<a href="http://www.kokkonen.net/tjko/projects.html">jpegoptim</a>, depending on the filetype
|
>A cross-platform tool for losslessly optimizing PNG and JPG files for
|
||||||
(currently, PNG and JPG files are supported). It was inspired by
|
web.</span
|
||||||
<a href="http://imageoptim.pornel.net/">imageoptim</a>. All image files are losslessy compressed
|
>
|
||||||
on the highest available compression levels, and EXIF and other metadata is removed. Trimage gives you various input functions to fit your
|
<p class="tri">
|
||||||
own workflow: A regular file dialog, dragging and dropping and various command line options.</p>
|
Trimage is a cross-platform GUI and command-line interface to optimize
|
||||||
|
image files for websites, using
|
||||||
|
<a href="http://optipng.sourceforge.net/">optipng</a>,
|
||||||
|
<a href="http://pmt.sourceforge.net/pngcrush/">pngcrush</a>,
|
||||||
|
<a href="http://advancemame.sourceforge.net/comp-readme.html">advpng</a>
|
||||||
|
and <a href="http://www.kokkonen.net/tjko/projects.html">jpegoptim</a>,
|
||||||
|
depending on the filetype (currently, PNG and JPG files are supported).
|
||||||
|
It was inspired by
|
||||||
|
<a href="http://imageoptim.pornel.net/">imageoptim</a>. All image files
|
||||||
|
are losslessy compressed on the highest available compression levels,
|
||||||
|
and EXIF and other metadata is removed. Trimage gives you various input
|
||||||
|
functions to fit your own workflow: A regular file dialog, dragging and
|
||||||
|
dropping and various command line options.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Trimage in action</h2>
|
<h2>Trimage in action</h2>
|
||||||
<img src="image.png" alt="a screenshot of Trimage">
|
<img src="image.png" alt="a screenshot of Trimage" />
|
||||||
|
|
||||||
<div class="block2">
|
<div class="block2">
|
||||||
<h2>Download</h2>
|
<h2>Download</h2>
|
||||||
|
|
||||||
<h3><img src="debian.png" alt=""> Debian (sid)</h3>
|
<h3><img src="debian.png" alt="" /> Debian (sid)</h3>
|
||||||
<p>Trimage is available in the official Debian Sid repositories:</p>
|
<p>Trimage is available in the official Debian Sid repositories:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li><code>sudo apt-get install trimage</code></li>
|
<li><code>sudo apt-get install trimage</code></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h3><img src="ubuntu.png" alt=""> Ubuntu</h3>
|
<h3><img src="ubuntu.png" alt="" /> Ubuntu</h3>
|
||||||
<p>If you're on Natty (11.04), Trimage is available in the official repositories:</p>
|
<p>Trimage is available in the official repositories:</p>
|
||||||
<a href="https://apps.ubuntu.com/cat/applications/trimage/"><img src="http://developer.ubuntu.com/wp-content/uploads/2012/06/downloadonubuntubutton.png" title="Download for Ubuntu" alt="Download for Ubuntu button" width="122" height="49" /></a>
|
<a href="https://apps.ubuntu.com/cat/applications/trimage/"
|
||||||
<p>Alternatively:</p>
|
>Download for Ubuntu</a
|
||||||
<ol>
|
>
|
||||||
<li><code>sudo apt-get install trimage</code></li>
|
<p>Alternatively:</p>
|
||||||
</ol>
|
<ol>
|
||||||
<p>If you're on Maveric (10.10), Karmic (9.10) or Lucid (10.04), type the following commands into your console:</p>
|
<li><code>sudo apt-get install trimage</code></li>
|
||||||
<ol>
|
</ol>
|
||||||
<li><code>sudo add-apt-repository ppa:kilian/trimage</code></li>
|
|
||||||
<li><code>sudo apt-get update</code></li>
|
|
||||||
<li><code>sudo apt-get install trimage</code></li>
|
|
||||||
</ol>
|
|
||||||
<p>If you are still on Jaunty, read <a href="https://launchpad.net/+help/soyuz/ppa-sources-list.html">this guide</a> on installing PPA's.</p>
|
|
||||||
|
|
||||||
<h3><img src="arch.png" alt=""> Arch Linux</h3>
|
<h3><img src="arch.png" alt="" /> Arch Linux</h3>
|
||||||
Trimage is available from AUR, to install, type:
|
Trimage is available from AUR, to install, type:
|
||||||
<ol>
|
<ol>
|
||||||
<li><code>yaourt -S trimage-git</code> </li>
|
<li><code>yaourt -S trimage</code></li>
|
||||||
</ol>
|
</ol>
|
||||||
<h3><img src="mandriva.png" alt=""> Mandriva Linux 2010.1</h3>
|
|
||||||
Trimage is available Via two RPMs provided by <a href="http://mib.pianetalinux.org/mib/quick/basic-rpms/mib-rpms/918-trimage-105">Mandriva international backports</a>
|
|
||||||
<ol>
|
|
||||||
<li><a href="http://mib.pianetalinux.org/2010.1/32/basic/trimage-1.0.5-69mib2010.1.noarch.rpm">Trimage 1.0.5 32bit</a></li>
|
|
||||||
<li><a href="http://mib.pianetalinux.org/2010.1/64/basic/trimage-1.0.5-69mib2010.1.noarch.rpm">Trimage 1.0.5 63bit</a></li>
|
|
||||||
</ol>
|
|
||||||
<h3><img src="opensuse.png" alt=""> OpenSuse</h3>
|
|
||||||
<p>Trimage is available for a variety of versions: 11.3, 11.4 and Factory and SLE. You can find them on <a href="http://software.opensuse.org/search?q=Trimage&include_home=true">software.opensuse.org</a>.</p>
|
|
||||||
|
|
||||||
<h3><img src="linux.png" alt=""> Other *nix</h3>
|
<h3><img src="macos.png" alt="" /> macOS</h3><!-- Attribution for the image: VICDJES21 / CC BY-SA (https://creativecommons.org/licenses/by-sa/4.0) -->
|
||||||
<ol>
|
Trimage is available from Homebrew, to install, type:
|
||||||
<li>Download the source via git or bzr (see repositories)</li>
|
<ol>
|
||||||
<li>Make sure you have all the requirements installed (see requirements)</li>
|
<li><code>brew install trimage</code></li>
|
||||||
<li>Enter <code>python setup.py install</code> into your console</li>
|
</ol>
|
||||||
<li>Launch by executing <code>trimage</code></li>
|
<p>Launch by executing <code>trimage</code> in Terminal.app</p>
|
||||||
</ol>
|
|
||||||
<p>Help us make .deb's, rpms's etc: <a href="mailto:help@trimage.org">contact us</a></p>
|
<h3><img src="linux.png" alt="" /> Other *nix</h3>
|
||||||
<h3><img src="mac.png" alt=""> Mac</h3>
|
<ol>
|
||||||
<p>Trimage should be able to run on Mac. <a href="mailto:help@trimage.org">Help us with this</a></p>
|
<li>Download the source via git or bzr (see repositories)</li>
|
||||||
<h3><img src="windows.png" alt=""> Windows</h3>
|
<li>
|
||||||
<p>Trimage should be able to run on Windows. <a href="mailto:help@trimage.org">Help us with this</a></p>
|
Make sure you have all the requirements installed (see requirements)
|
||||||
|
</li>
|
||||||
|
<li>Enter <code>python setup.py install</code> into your console</li>
|
||||||
|
<li>Launch by executing <code>trimage</code></li>
|
||||||
|
</ol>
|
||||||
|
<p>
|
||||||
|
Help us make .snaps, .appimages etc:
|
||||||
|
<a href="mailto:help@trimage.org">contact us</a> or
|
||||||
|
<a href="https://github.com/kilian/trimage/pulls"
|
||||||
|
>open a pull request</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<!--
|
||||||
|
<h3><img src="windows.png" alt=""> Windows</h3>
|
||||||
|
<p>Trimage should be able to run on Windows. <a href="mailto:help@trimage.org">Help us with this</a></p>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<h2>Repositories</h2>
|
||||||
|
<p>
|
||||||
|
<strong>Git:</strong> Trimage is primarily developed on
|
||||||
|
<a href="http://github.com/Kilian/Trimage">GitHub</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Bzr:</strong> Trimage is also available on
|
||||||
|
<a href="https://launchpad.net/trimage">Launchpad</a>.
|
||||||
|
</p>
|
||||||
|
<p>Trimage is MIT licenced. We encourage contributions via GitHub.</p>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<h2>Thanks</h2>
|
||||||
|
<p>The following people helped develop Trimage:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://hugo-posnic.fr/">Hugo Posnic</a></li>
|
||||||
|
<li>Neil Wallace</li>
|
||||||
|
<li>Jeroen Goudsmit</li>
|
||||||
|
<li>Tarnay Kálmán</li>
|
||||||
|
<li>Thomas Lété</li>
|
||||||
|
<li>Kyrill Detinov</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<h2>Requirements</h2>
|
||||||
|
<ul>
|
||||||
|
<li>python <em>3</em></li>
|
||||||
|
<li>python-qt5 <em>5</em></li>
|
||||||
|
<li>optipng <em>0.6.2.1</em></li>
|
||||||
|
<li>pngcrush <em>1.6.7</em></li>
|
||||||
|
<li>advancecomp <em>1.15</em></li>
|
||||||
|
<li>jpegoptim <em>1.2.2</em></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block2">
|
||||||
|
<h2>Command line options</h2>
|
||||||
|
<dl>
|
||||||
|
<dt>--version</dt>
|
||||||
|
<dd>show program's version number and exit</dd>
|
||||||
|
<dt>-h, --help</dt>
|
||||||
|
<dd>show this help message and exit</dd>
|
||||||
|
<dt>-v, --verbose</dt>
|
||||||
|
<dd>Verbose mode (default)</dd>
|
||||||
|
<dt>-q, --quiet</dt>
|
||||||
|
<dd>Quiet mode</dd>
|
||||||
|
<dt>-f FILENAME, --file=FILENAME</dt>
|
||||||
|
<dd>compresses image and exit</dd>
|
||||||
|
<dt>-d DIRECTORY, --directory=DIRECTORY</dt>
|
||||||
|
<dd>compresses images in directory and exit</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<h2>Help</h2>
|
||||||
|
<p>
|
||||||
|
Please use <a href="https://github.com/Kilian/Trimage">GitHub</a> for
|
||||||
|
reporting bugs and email
|
||||||
|
<a href="mailto:help@trimage.org">help@trimage.org</a> for general
|
||||||
|
questions (though we are not a helpdesk!)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p class="footer">
|
||||||
|
Trimage is © 2010–2019
|
||||||
|
<a href="http://kilianvalkhof.com">Kilian Valkhof</a>,
|
||||||
|
<a href="http://paulchaplin.com">Paul Chaplin</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="block">
|
|
||||||
<h2>Repositories</h2>
|
|
||||||
<p><strong>Git:</strong> Trimage is primarily developed on <a href="http://github.com/Kilian/Trimage">GitHub</a>.</p>
|
|
||||||
<p><strong>Bzr:</strong> Trimage is also available on <a href="https://launchpad.net/trimage">Launchpad</a>.</p>
|
|
||||||
<p>Trimage is MIT licenced. We encourage contributions via GitHub.</p>
|
|
||||||
</div>
|
|
||||||
<div class="block">
|
|
||||||
<h2>Thanks</h2>
|
|
||||||
<p>The following people helped develop Trimage:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Neil Wallace</li>
|
|
||||||
<li>Jeroen Goudsmit</li>
|
|
||||||
<li>Tarnay Kálmán</li>
|
|
||||||
<li>Thomas Lété</li>
|
|
||||||
<li>Kyrill Detinov</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="block">
|
|
||||||
<h2>Donate</h2>
|
|
||||||
<p>If you enjoy using Trimage, please buy us a coffee or a beer :)</p>
|
|
||||||
<a href='http://www.pledgie.com/campaigns/9607'><img alt='Click here to lend your support to: Trimage and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/9607.png?skin_name=chrome' border='0' /></a>
|
|
||||||
<script type="text/javascript">
|
|
||||||
var flattr_url = 'http://trimage.org';
|
|
||||||
var flattr_btn='compact';
|
|
||||||
</script>
|
|
||||||
<script src="http://api.flattr.com/button/load.js" type="text/javascript"></script>
|
|
||||||
</div>
|
|
||||||
<div class="block">
|
|
||||||
<h2>Requirements</h2>
|
|
||||||
<ul>
|
|
||||||
<li>python <em>2.6</em></li>
|
|
||||||
<li>python-qt4 <em>4.4</em></li>
|
|
||||||
<li>optipng <em>0.6.2.1</em></li>
|
|
||||||
<li>pngcrush <em>1.6.7</em></li>
|
|
||||||
<li>advancecomp <em>1.15</em></li>
|
|
||||||
<li>jpegoptim <em>1.2.2</em></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="block2">
|
|
||||||
<h2>Command line options</h2>
|
|
||||||
<dl>
|
|
||||||
<dt>--version</dt>
|
|
||||||
<dd>show program's version number and exit</dd>
|
|
||||||
<dt>-h, --help</dt>
|
|
||||||
<dd>show this help message and exit</dd>
|
|
||||||
<dt>-v, --verbose</dt>
|
|
||||||
<dd>Verbose mode (default)</dd>
|
|
||||||
<dt>-q, --quiet</dt>
|
|
||||||
<dd>Quiet mode</dd>
|
|
||||||
<dt>-f FILENAME, --file=FILENAME</dt>
|
|
||||||
<dd>compresses image and exit</dd>
|
|
||||||
<dt>-d DIRECTORY, --directory=DIRECTORY</dt>
|
|
||||||
<dd>compresses images in directory and exit</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div class="block">
|
|
||||||
<h2>Help</h2>
|
|
||||||
<p>Please use <a href="http://github.com/Kilian/Trimage">GitHub</a> for reporting bugs and email <a href="mailto:help@trimage.org">help@trimage.org</a> for general questions (though we are not a helpdesk!)</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Planned features</h2>
|
|
||||||
<p>Version 1.1.0 final:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Expand command line options</li>
|
|
||||||
<li>General refactoring</li>
|
|
||||||
</ul>
|
|
||||||
<p>Version 1.1.1 final:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Use multiprocessing instead of threads</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>Beyond that</p>
|
|
||||||
<ul>
|
|
||||||
<li>Deletion of rows in the table view</li>
|
|
||||||
<li>Integration with online services such as punypng</li>
|
|
||||||
<li><a href="mailto:help@trimage.org">Suggest something</a></li>
|
|
||||||
</ul>
|
|
||||||
<p class="footer">Trimage is © 2010 <a href="http://kilianvalkhof.com">Kilian Valkhof</a>, <a href="http://paulchaplin.com">Paul Chaplin</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
||||||
BIN
website/mac.png
|
Before Width: | Height: | Size: 891 B |
BIN
website/macos.png
Normal file
|
After Width: | Height: | Size: 835 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |