mirror of
https://github.com/Kilian/Trimage.git
synced 2026-01-26 10:08:40 -05:00
Update debian spec
This commit is contained in:
parent
cad49c6d71
commit
ddc6d2da9a
32 changed files with 1803 additions and 5 deletions
11
debian/trimage/usr/lib/python2.7/dist-packages/trimage-1.0.2.egg-info
vendored
Normal file
11
debian/trimage/usr/lib/python2.7/dist-packages/trimage-1.0.2.egg-info
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: trimage
|
||||
Version: 1.0.2
|
||||
Summary: Trimage image compressor - A cross-platform tool for optimizing PNG and JPG files
|
||||
Home-page: http://trimage.org
|
||||
Author: Kilian Valkhof, Paul Chaplin
|
||||
Author-email: help@trimage.org
|
||||
License: MIT license
|
||||
Description: Trimage is a cross-platform GUI and command-line interface to optimize image files via optipng, advpng and guetzli, 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.
|
||||
Platform: UNKNOWN
|
||||
Requires: PyQt4 (>=4.4)
|
||||
280
debian/trimage/usr/lib/python2.7/dist-packages/trimage/ThreadPool/ThreadPool.py
vendored
Normal file
280
debian/trimage/usr/lib/python2.7/dist-packages/trimage/ThreadPool/ThreadPool.py
vendored
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
'''
|
||||
ThreadPool Implementation
|
||||
|
||||
@author: Morten Holdflod Moeller - morten@holdflod.dk
|
||||
@license: LGPL v3
|
||||
'''
|
||||
|
||||
from __future__ import with_statement
|
||||
from threading import Thread, RLock
|
||||
from time import sleep
|
||||
from Queue import Queue, Empty
|
||||
import logging
|
||||
import sys
|
||||
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
|
||||
h = sys.stderr
|
||||
logging.getLogger('threadpool').addHandler(h)
|
||||
logging.getLogger('threadpool.worker').addHandler(h)
|
||||
|
||||
|
||||
|
||||
class ThreadPoolMixIn:
|
||||
"""Mix-in class to handle each request in a new thread from the ThreadPool."""
|
||||
|
||||
def __init__(self, threadpool=None):
|
||||
if (threadpool == None):
|
||||
threadpool = ThreadPool()
|
||||
self.__private_threadpool = True
|
||||
else:
|
||||
self.__private_threadpool = False
|
||||
|
||||
self.__threadpool = threadpool
|
||||
|
||||
def process_request_thread(self, request, client_address):
|
||||
"""Same as in BaseServer but as a thread.
|
||||
|
||||
In addition, exception handling is done here.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.finish_request(request, client_address)
|
||||
self.close_request(request)
|
||||
except:
|
||||
self.handle_error(request, client_address) #IGNORE:W0702
|
||||
self.close_request(request)
|
||||
|
||||
def process_request(self, 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):
|
||||
'''
|
||||
Exceptoion raised when a Job could not be added
|
||||
to the queue
|
||||
'''
|
||||
def __init__(self, msg):
|
||||
Exception.__init__(self, msg)
|
||||
|
||||
|
||||
class ThreadPool:
|
||||
'''
|
||||
The class implementing the ThreadPool.
|
||||
|
||||
Instantiate and add jobs using add_job(func, args_list)
|
||||
'''
|
||||
|
||||
class Job: #IGNORE:R0903
|
||||
'''
|
||||
Class encapsulating a job to be handled
|
||||
by ThreadPool workers
|
||||
'''
|
||||
def __init__(self, function, args, return_callback=None):
|
||||
self.callable = function
|
||||
self.arguments = args
|
||||
self.return_callback = return_callback
|
||||
|
||||
def execute(self):
|
||||
'''
|
||||
Called to execute the function
|
||||
'''
|
||||
try:
|
||||
return_value = self.callable(*self.arguments) #IGNORE:W0142
|
||||
except Exception, excep: #IGNORE:W0703
|
||||
logger = logging.getLogger("threadpool.worker")
|
||||
logger.warning("A job in the ThreadPool raised an exception: " + excep)
|
||||
#else do nothing cause we don't know what to do...
|
||||
return
|
||||
|
||||
try:
|
||||
if (self.return_callback != None):
|
||||
self.return_callback(return_value)
|
||||
except Exception, _: #IGNORE:W0703 everything could go wrong...
|
||||
logger = logging.getLogger('threadpool')
|
||||
logger.warning('Error while delivering return value to callback function')
|
||||
|
||||
class Worker(Thread):
|
||||
'''
|
||||
A worker thread handling jobs in the thread pool
|
||||
job queue
|
||||
'''
|
||||
|
||||
def __init__(self, pool):
|
||||
Thread.__init__(self)
|
||||
|
||||
if (not isinstance(pool, ThreadPool)):
|
||||
raise TypeError("pool is not a ThreadPool instance")
|
||||
|
||||
self.pool = pool
|
||||
|
||||
self.alive = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
'''
|
||||
The workers main-loop getting jobs from queue
|
||||
and executing them
|
||||
'''
|
||||
while self.alive:
|
||||
#print self.pool.__active_worker_count, self.pool.__worker_count
|
||||
job = self.pool.get_job()
|
||||
if (job != None):
|
||||
self.pool.worker_active()
|
||||
job.execute()
|
||||
self.pool.worker_inactive()
|
||||
else:
|
||||
self.alive = False
|
||||
|
||||
self.pool.punch_out()
|
||||
|
||||
def __init__(self, max_workers = 5, kill_workers_after = 3):
|
||||
if (not isinstance(max_workers, int)):
|
||||
raise TypeError("max_workers is not an int")
|
||||
if (max_workers < 1):
|
||||
raise ValueError('max_workers must be >= 1')
|
||||
|
||||
if (not isinstance(kill_workers_after, int)):
|
||||
raise TypeError("kill_workers_after is not an int")
|
||||
|
||||
self.__max_workers = max_workers
|
||||
self.__kill_workers_after = kill_workers_after
|
||||
|
||||
# This Queue is assumed Thread Safe
|
||||
self.__jobs = Queue()
|
||||
|
||||
self.__worker_count_lock = RLock()
|
||||
self.__worker_count = 0
|
||||
self.__active_worker_count = 0
|
||||
|
||||
self.__shutting_down = False
|
||||
logger = logging.getLogger('threadpool')
|
||||
logger.info('started')
|
||||
|
||||
def shutdown(self, wait_for_workers_period = 1, clean_shutdown_reties = 5):
|
||||
if (not isinstance(clean_shutdown_reties, int)):
|
||||
raise TypeError("clean_shutdown_reties is not an int")
|
||||
if (not clean_shutdown_reties >= 0):
|
||||
raise ValueError('clean_shutdown_reties must be >= 0')
|
||||
|
||||
if (not isinstance(wait_for_workers_period, int)):
|
||||
raise TypeError("wait_for_workers_period is not an int")
|
||||
if (not wait_for_workers_period >= 0):
|
||||
raise ValueError('wait_for_workers_period must be >= 0')
|
||||
|
||||
logger = logging.getLogger("threadpool")
|
||||
logger.info("shutting down")
|
||||
|
||||
with self.__worker_count_lock:
|
||||
self.__shutting_down = True
|
||||
self.__max_workers = 0
|
||||
self.__kill_workers_after = 0
|
||||
|
||||
retries_left = clean_shutdown_reties
|
||||
while (retries_left > 0):
|
||||
|
||||
with self.__worker_count_lock:
|
||||
logger.info("waiting for workers to shut down (%i), %i workers left"%(retries_left, self.__worker_count))
|
||||
if (self.__worker_count > 0):
|
||||
retries_left -= 1
|
||||
else:
|
||||
retries_left = 0
|
||||
|
||||
sleep(wait_for_workers_period)
|
||||
|
||||
|
||||
with self.__worker_count_lock:
|
||||
if (self.__worker_count > 0):
|
||||
logger.warning("shutdown stopped waiting. Still %i active workers"%self.__worker_count)
|
||||
clean_shutdown = False
|
||||
else:
|
||||
clean_shutdown = True
|
||||
|
||||
logger.info("shutdown complete")
|
||||
|
||||
return clean_shutdown
|
||||
|
||||
def punch_out(self):
|
||||
'''
|
||||
Called by worker to update worker count
|
||||
when the worker is shutting down
|
||||
'''
|
||||
with self.__worker_count_lock:
|
||||
self.__worker_count -= 1
|
||||
|
||||
def __new_worker(self):
|
||||
'''
|
||||
Adding a new worker thread to the thread pool
|
||||
'''
|
||||
with self.__worker_count_lock:
|
||||
ThreadPool.Worker(self)
|
||||
self.__worker_count += 1
|
||||
|
||||
def worker_active(self):
|
||||
with self.__worker_count_lock:
|
||||
self.__active_worker_count = self.__active_worker_count + 1
|
||||
|
||||
def worker_inactive(self):
|
||||
with self.__worker_count_lock:
|
||||
self.__active_worker_count = self.__active_worker_count - 1
|
||||
|
||||
def add_job(self, function, args = None, return_callback=None):
|
||||
'''
|
||||
Put new job into queue
|
||||
'''
|
||||
|
||||
if (not callable(function)):
|
||||
raise TypeError("function is not a callable")
|
||||
if (not ( args == None or isinstance(args, list))):
|
||||
raise TypeError("args is not a list")
|
||||
if (not (return_callback == None or callable(return_callback))):
|
||||
raise TypeError("return_callback is not a callable")
|
||||
|
||||
if (args == None):
|
||||
args = []
|
||||
|
||||
job = ThreadPool.Job(function, args, return_callback)
|
||||
|
||||
with self.__worker_count_lock:
|
||||
if (self.__shutting_down):
|
||||
raise AddJobException("ThreadPool is shutting down")
|
||||
|
||||
try:
|
||||
start_new_worker = False
|
||||
if (self.__worker_count < self.__max_workers):
|
||||
if (self.__active_worker_count == self.__worker_count):
|
||||
start_new_worker = True
|
||||
|
||||
self.__jobs.put(job)
|
||||
|
||||
if (start_new_worker):
|
||||
self.__new_worker()
|
||||
|
||||
except Exception:
|
||||
raise AddJobException("Could not add job")
|
||||
|
||||
|
||||
def get_job(self):
|
||||
'''
|
||||
Retrieve next job from queue
|
||||
workers die (and should) when
|
||||
returning None
|
||||
'''
|
||||
|
||||
job = None
|
||||
try:
|
||||
if (self.__kill_workers_after < 0):
|
||||
job = self.__jobs.get(True)
|
||||
elif (self.__kill_workers_after == 0):
|
||||
job = self.__jobs.get(False)
|
||||
else:
|
||||
job = self.__jobs.get(True, self.__kill_workers_after)
|
||||
except Empty:
|
||||
job = None
|
||||
|
||||
return job
|
||||
1
debian/trimage/usr/lib/python2.7/dist-packages/trimage/ThreadPool/__init__.py
vendored
Normal file
1
debian/trimage/usr/lib/python2.7/dist-packages/trimage/ThreadPool/__init__.py
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
from ThreadPool import ThreadPool, ThreadPoolMixIn
|
||||
0
debian/trimage/usr/lib/python2.7/dist-packages/trimage/__init__.py
vendored
Normal file
0
debian/trimage/usr/lib/python2.7/dist-packages/trimage/__init__.py
vendored
Normal file
2
debian/trimage/usr/lib/python2.7/dist-packages/trimage/filesize/__init__.py
vendored
Normal file
2
debian/trimage/usr/lib/python2.7/dist-packages/trimage/filesize/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from filesize import size
|
||||
from filesize import traditional, alternative, verbose, iec, si
|
||||
115
debian/trimage/usr/lib/python2.7/dist-packages/trimage/filesize/filesize.py
vendored
Normal file
115
debian/trimage/usr/lib/python2.7/dist-packages/trimage/filesize/filesize.py
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
'''
|
||||
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
|
||||
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/compressing.gif
vendored
Normal file
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/compressing.gif
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/list-add.png
vendored
Normal file
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/list-add.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 676 B |
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/trimage-icon.png
vendored
Normal file
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/trimage-icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/view-refresh.png
vendored
Normal file
BIN
debian/trimage/usr/lib/python2.7/dist-packages/trimage/pixmaps/view-refresh.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2 KiB |
570
debian/trimage/usr/lib/python2.7/dist-packages/trimage/trimage.py
vendored
Normal file
570
debian/trimage/usr/lib/python2.7/dist-packages/trimage/trimage.py
vendored
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
#!/usr/bin/python
|
||||
import time
|
||||
import sys
|
||||
import errno
|
||||
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 subprocess import call, PIPE
|
||||
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 ui import Ui_trimage
|
||||
|
||||
# change version to 1.1 after replacing jpegoptim with guetzli
|
||||
VERSION = "1.1"
|
||||
|
||||
|
||||
class StartQT4(QMainWindow):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.ui = Ui_trimage()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
self.showapp = True
|
||||
self.verbose = True
|
||||
self.imagelist = []
|
||||
|
||||
QCoreApplication.setOrganizationName("Kilian Valkhof")
|
||||
QCoreApplication.setOrganizationDomain("trimage.org")
|
||||
QCoreApplication.setApplicationName("Trimage")
|
||||
self.settings = QSettings()
|
||||
self.restoreGeometry(self.settings.value("geometry").toByteArray())
|
||||
|
||||
# check if apps are installed
|
||||
if self.checkapps():
|
||||
quit()
|
||||
|
||||
#add quit shortcut
|
||||
if hasattr(QKeySequence, "Quit"):
|
||||
self.quit_shortcut = QShortcut(QKeySequence(QKeySequence.Quit),
|
||||
self)
|
||||
else:
|
||||
self.quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
|
||||
|
||||
# disable recompress
|
||||
self.ui.recompress.setEnabled(False)
|
||||
#self.ui.recompress.hide()
|
||||
|
||||
# make a worker thread
|
||||
self.thread = Worker()
|
||||
|
||||
# connect signals with slots
|
||||
QObject.connect(self.ui.addfiles, SIGNAL("clicked()"),
|
||||
self.file_dialog)
|
||||
QObject.connect(self.ui.recompress, SIGNAL("clicked()"),
|
||||
self.recompress_files)
|
||||
QObject.connect(self.quit_shortcut, SIGNAL("activated()"),
|
||||
qApp, SLOT('quit()'))
|
||||
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")))
|
||||
|
||||
# activate command line options
|
||||
self.commandline_options()
|
||||
|
||||
if QSystemTrayIcon.isSystemTrayAvailable() and not self.cli:
|
||||
self.systemtray = Systray(self)
|
||||
|
||||
def commandline_options(self):
|
||||
self.cli = False
|
||||
"""Set up the command line options."""
|
||||
parser = OptionParser(version="%prog " + VERSION,
|
||||
description="GUI front-end to compress png and jpg images via "
|
||||
"optipng, advpng and guetzli")
|
||||
|
||||
parser.set_defaults(verbose=True)
|
||||
parser.add_option("-v", "--verbose", action="store_true",
|
||||
dest="verbose", help="Verbose mode (default)")
|
||||
parser.add_option("-q", "--quiet", action="store_false",
|
||||
dest="verbose", help="Quiet mode")
|
||||
|
||||
parser.add_option("-f", "--file", action="store", type="string",
|
||||
dest="filename", help="compresses image and exit")
|
||||
parser.add_option("-d", "--directory", action="store", type="string",
|
||||
dest="directory", help="compresses images in directory and exit")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
# make sure we quit after processing finished if using cli
|
||||
if options.filename or options.directory:
|
||||
QObject.connect(self.thread, SIGNAL("finished()"), quit)
|
||||
self.cli = True
|
||||
|
||||
# send to correct function
|
||||
if options.filename:
|
||||
self.file_from_cmd(options.filename.decode("utf-8"))
|
||||
if options.directory:
|
||||
self.dir_from_cmd(options.directory.decode("utf-8"))
|
||||
|
||||
self.verbose = options.verbose
|
||||
|
||||
"""
|
||||
Input functions
|
||||
"""
|
||||
|
||||
def dir_from_cmd(self, directory):
|
||||
"""
|
||||
Read the files in the directory and send all files to compress_file.
|
||||
"""
|
||||
self.showapp = False
|
||||
dirpath = path.abspath(directory)
|
||||
imagedir = listdir(directory)
|
||||
filelist = [path.join(dirpath, image) for image in imagedir]
|
||||
self.delegator(filelist)
|
||||
|
||||
def file_from_cmd(self, image):
|
||||
"""Get the file and send it to compress_file"""
|
||||
self.showapp = False
|
||||
filelist = [path.abspath(image)]
|
||||
self.delegator(filelist)
|
||||
|
||||
def file_drop(self, images):
|
||||
"""
|
||||
Get a file from the drag and drop handler and send it to compress_file.
|
||||
"""
|
||||
self.delegator(images)
|
||||
|
||||
def file_dialog(self):
|
||||
"""Open a file dialog and send the selected images to compress_file."""
|
||||
fd = QFileDialog(self)
|
||||
fd.restoreState(self.settings.value("fdstate").toByteArray())
|
||||
directory = self.settings.value("directory", QVariant("")).toString()
|
||||
fd.setDirectory(directory)
|
||||
|
||||
images = fd.getOpenFileNames(self,
|
||||
"Select one or more image files to compress",
|
||||
directory,
|
||||
# this is a fix for file dialog differentiating between cases
|
||||
"Image files (*.png *.jpg *.jpeg *.PNG *.JPG *.JPEG)")
|
||||
|
||||
self.settings.setValue("fdstate", QVariant(fd.saveState()))
|
||||
if images:
|
||||
self.settings.setValue("directory", QVariant(path.dirname(unicode(images[0]))))
|
||||
self.delegator([unicode(fullpath) for fullpath in images])
|
||||
|
||||
def recompress_files(self):
|
||||
"""Send each file in the current file list to compress_file again."""
|
||||
self.delegator([row.image.fullpath for row in self.imagelist])
|
||||
|
||||
"""
|
||||
Compress functions
|
||||
"""
|
||||
|
||||
def delegator(self, images):
|
||||
"""
|
||||
Receive all images, check them and send them to the worker thread.
|
||||
"""
|
||||
delegatorlist = []
|
||||
for fullpath in images:
|
||||
try: # recompress images already in the list
|
||||
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:
|
||||
if not path.isdir(fullpath):
|
||||
self. add_image(fullpath, delegatorlist)
|
||||
else:
|
||||
self.walk(fullpath, delegatorlist)
|
||||
|
||||
self.update_table()
|
||||
self.thread.compress_file(delegatorlist, self.showapp, self.verbose,
|
||||
self.imagelist)
|
||||
|
||||
def walk(self, dir, delegatorlist):
|
||||
"""
|
||||
Walks a directory, and executes a callback on each file
|
||||
"""
|
||||
dir = path.abspath(dir)
|
||||
for file in [file for file in listdir(dir) if not file in [".","..",".svn",".git",".hg",".bzr",".cvs"]]:
|
||||
nfile = path.join(dir, file)
|
||||
|
||||
if path.isdir(nfile):
|
||||
self.walk(nfile, delegatorlist)
|
||||
else:
|
||||
self.add_image(nfile, 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
|
||||
"""
|
||||
image = Image(fullpath)
|
||||
if image.valid:
|
||||
delegatorlist.append(image)
|
||||
self.imagelist.append(ImageRow(image, self.compressing_icon))
|
||||
if QSystemTrayIcon.isSystemTrayAvailable() and not self.cli:
|
||||
self.systemtray.trayIcon.setToolTip("Trimage image compressor (" + str(len(self.imagelist)) + " files)")
|
||||
self.setWindowTitle("Trimage image compressor (" + str(len(self.imagelist)) + " files)")
|
||||
else:
|
||||
print >> sys.stderr, u"[error] %s not a supported image file and/or not writeable" % image.fullpath
|
||||
|
||||
"""
|
||||
UI Functions
|
||||
"""
|
||||
|
||||
def update_table(self):
|
||||
"""Update the table view with the latest file data."""
|
||||
tview = self.ui.processedfiles
|
||||
# set table model
|
||||
tmodel = TriTableModel(self, self.imagelist,
|
||||
["Filename", "Old Size", "New Size", "Compressed"])
|
||||
tview.setModel(tmodel)
|
||||
|
||||
# set minimum size of table
|
||||
vh = tview.verticalHeader()
|
||||
vh.setVisible(False)
|
||||
|
||||
# set horizontal header properties
|
||||
hh = tview.horizontalHeader()
|
||||
hh.setStretchLastSection(True)
|
||||
|
||||
# set all row heights
|
||||
nrows = len(self.imagelist)
|
||||
for row in range(nrows):
|
||||
tview.setRowHeight(row, 25)
|
||||
|
||||
# set the second column to be longest
|
||||
tview.setColumnWidth(0, 300)
|
||||
|
||||
# enable recompress button
|
||||
self.enable_recompress()
|
||||
|
||||
"""
|
||||
Helper functions
|
||||
"""
|
||||
|
||||
def enable_recompress(self):
|
||||
"""Enable the recompress button."""
|
||||
self.ui.recompress.setEnabled(True)
|
||||
if QSystemTrayIcon.isSystemTrayAvailable() and not self.cli:
|
||||
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
|
||||
|
||||
# guetzli does the compression job much better than jpegoptim (and much slower)
|
||||
#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
|
||||
|
||||
# guetzli currently does not have neither --version not --help flags
|
||||
retcode = self.safe_call("guetzli" + exe)
|
||||
if retcode != 0:
|
||||
status = True
|
||||
sys.stderr.write("[error] please install guetzli")
|
||||
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):
|
||||
if self.isVisible():
|
||||
self.hide()
|
||||
if QSystemTrayIcon.isSystemTrayAvailable():
|
||||
self.systemtray.hideMain.setText("&Show window")
|
||||
else:
|
||||
self.show()
|
||||
if QSystemTrayIcon.isSystemTrayAvailable():
|
||||
self.systemtray.hideMain.setText("&Hide window")
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.settings.setValue("geometry", QVariant(self.saveGeometry()))
|
||||
event.accept()
|
||||
|
||||
|
||||
class TriTableModel(QAbstractTableModel):
|
||||
|
||||
def __init__(self, parent, imagelist, header, *args):
|
||||
"""
|
||||
@param parent Qt parent object.
|
||||
@param imagelist A list of tuples.
|
||||
@param header A list of strings.
|
||||
"""
|
||||
QAbstractTableModel.__init__(self, parent, *args)
|
||||
self.imagelist = imagelist
|
||||
self.header = header
|
||||
|
||||
def rowCount(self, parent):
|
||||
"""Count the number of rows."""
|
||||
return len(self.imagelist)
|
||||
|
||||
def columnCount(self, parent):
|
||||
"""Count the number of columns."""
|
||||
return len(self.header)
|
||||
|
||||
def data(self, index, role):
|
||||
"""Fill the table with data."""
|
||||
if not index.isValid():
|
||||
return QVariant()
|
||||
elif role == Qt.DisplayRole:
|
||||
data = self.imagelist[index.row()][index.column()]
|
||||
return QVariant(data)
|
||||
elif index.column() == 0 and role == Qt.DecorationRole:
|
||||
# decorate column 0 with an icon of the image itself
|
||||
f_icon = self.imagelist[index.row()][4]
|
||||
return QVariant(f_icon)
|
||||
else:
|
||||
return QVariant()
|
||||
|
||||
def headerData(self, col, orientation, role):
|
||||
"""Fill the table headers."""
|
||||
if orientation == Qt.Horizontal and (role == Qt.DisplayRole or
|
||||
role == Qt.DecorationRole):
|
||||
return QVariant(self.header[col])
|
||||
return QVariant()
|
||||
|
||||
|
||||
class ImageRow:
|
||||
|
||||
def __init__(self, image, waitingIcon=None):
|
||||
""" Build the information visible in the table image row. """
|
||||
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
|
||||
}
|
||||
names = ['shortname', 'oldfilesizestr', 'newfilesizestr',
|
||||
'ratiostr', 'icon']
|
||||
for i, n in enumerate(names):
|
||||
d[i] = d[n]
|
||||
|
||||
self.d = d
|
||||
|
||||
def statusStr(self):
|
||||
""" Set the status message. """
|
||||
if self.image.failed:
|
||||
return "ERROR: %s"
|
||||
if self.image.compressing:
|
||||
message = "Compressing %s..."
|
||||
return message
|
||||
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:
|
||||
|
||||
def __init__(self, fullpath):
|
||||
""" gather image information. """
|
||||
self.valid = False
|
||||
self.reset()
|
||||
self.fullpath = fullpath
|
||||
if path.isfile(self.fullpath) and access(self.fullpath, WRITEABLE):
|
||||
self.filetype = determinetype(self.fullpath)
|
||||
if self.filetype in ["jpeg", "png"]:
|
||||
oldfile = QFileInfo(self.fullpath)
|
||||
self.shortname = oldfile.fileName()
|
||||
self.oldfilesize = oldfile.size()
|
||||
self.icon = QIcon(self.fullpath)
|
||||
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):
|
||||
self.failed = False
|
||||
self.compressed = False
|
||||
self.compressing = False
|
||||
self.recompression = False
|
||||
|
||||
def compress(self):
|
||||
""" Compress the image and return it to the thread. """
|
||||
if not self.valid:
|
||||
raise "Tried to compress invalid image (unsupported format or not \
|
||||
file)"
|
||||
self.reset()
|
||||
self.compressing = True
|
||||
exe = ".exe" if (sys.platform == "win32") else ""
|
||||
runString = {
|
||||
# "jpeg": u"jpegoptim" + exe + " -f --strip-all '%(file)s'",
|
||||
# I commented out jpegoptim because guetzli compresses MUCH BETTER having a similar output quality
|
||||
# the default quality level for guetzli, if the parameter '--quality Q' is not passed, it 95.
|
||||
"jpeg": u"guetzli" + exe + " --quality 95 '%(file)s' '%(file)s.bak' && mv '%(file)s.bak' '%(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'"
|
||||
}
|
||||
# Create a backup file
|
||||
copy(self.fullpath, self.fullpath + '~')
|
||||
try:
|
||||
retcode = call(runString[self.filetype] % {"file": self.fullpath},
|
||||
shell=True, stdout=PIPE)
|
||||
except:
|
||||
retcode = -1
|
||||
if retcode == 0:
|
||||
self.newfilesize = QFile(self.fullpath).size()
|
||||
self.compressed = True
|
||||
|
||||
# Checks the new file and copy the backup
|
||||
if self.newfilesize >= self.oldfilesize:
|
||||
copy(self.fullpath + '~', self.fullpath)
|
||||
self.newfilesize = self.oldfilesize
|
||||
|
||||
# Removes the backup file
|
||||
remove(self.fullpath + '~')
|
||||
else:
|
||||
self.failed = True
|
||||
self.compressing = False
|
||||
self.retcode = retcode
|
||||
return self
|
||||
|
||||
|
||||
class Worker(QThread):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QThread.__init__(self, parent)
|
||||
self.toDisplay = Queue()
|
||||
self.threadpool = ThreadPool(max_workers=cpu_count())
|
||||
|
||||
def __del__(self):
|
||||
self.threadpool.shutdown()
|
||||
|
||||
def compress_file(self, images, showapp, verbose, imagelist):
|
||||
"""Start the worker thread."""
|
||||
for image in images:
|
||||
#FIXME:http://code.google.com/p/pythonthreadpool/issues/detail?id=5
|
||||
time.sleep(0.05)
|
||||
self.threadpool.add_job(image.compress, None,
|
||||
return_callback=self.toDisplay.put)
|
||||
self.showapp = showapp
|
||||
self.verbose = verbose
|
||||
self.imagelist = imagelist
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
"""Compress the given file, get data from it and call update_table."""
|
||||
tp = self.threadpool
|
||||
while self.showapp or not (tp._ThreadPool__active_worker_count == 0 and
|
||||
tp._ThreadPool__jobs.empty()):
|
||||
image = self.toDisplay.get()
|
||||
|
||||
self.emit(SIGNAL("updateUi"))
|
||||
|
||||
if not self.showapp and self.verbose: # we work via the commandline
|
||||
if image.retcode == 0:
|
||||
ir = ImageRow(image)
|
||||
print("File: " + ir['fullpath'] + ", Old Size: "
|
||||
+ ir['oldfilesizestr'] + ", New Size: "
|
||||
+ ir['newfilesizestr'] + ", Ratio: " + ir['ratiostr'])
|
||||
else:
|
||||
print >> sys.stderr, u"[error] %s could not be compressed" % image.fullpath
|
||||
|
||||
|
||||
class Systray(QWidget):
|
||||
|
||||
def __init__(self, parent):
|
||||
QWidget.__init__(self)
|
||||
self.parent = parent
|
||||
self.createActions()
|
||||
self.createTrayIcon()
|
||||
self.trayIcon.show()
|
||||
|
||||
def createActions(self):
|
||||
self.quitAction = QAction(self.tr("&Quit"), self)
|
||||
QObject.connect(self.quitAction, SIGNAL("triggered()"),
|
||||
qApp, SLOT("quit()"))
|
||||
|
||||
self.addFiles = QAction(self.tr("&Add and compress"), self)
|
||||
icon = QIcon()
|
||||
icon.addPixmap(QPixmap(self.parent.ui.get_image(("pixmaps/list-add.png"))),
|
||||
QIcon.Normal, QIcon.Off)
|
||||
self.addFiles.setIcon(icon)
|
||||
QObject.connect(self.addFiles, SIGNAL("triggered()"), self.parent.file_dialog)
|
||||
|
||||
self.recompress = QAction(self.tr("&Recompress"), self)
|
||||
icon2 = QIcon()
|
||||
icon2.addPixmap(QPixmap(self.parent.ui.get_image(("pixmaps/view-refresh.png"))),
|
||||
QIcon.Normal, QIcon.Off)
|
||||
self.recompress.setIcon(icon2)
|
||||
self.recompress.setDisabled(True)
|
||||
QObject.connect(self.addFiles, SIGNAL("triggered()"), self.parent.recompress_files)
|
||||
|
||||
self.hideMain = QAction(self.tr("&Hide window"), self)
|
||||
QObject.connect(self.hideMain, SIGNAL("triggered()"), self.parent.hide_main_window)
|
||||
|
||||
def createTrayIcon(self):
|
||||
self.trayIconMenu = QMenu(self)
|
||||
self.trayIconMenu.addAction(self.addFiles)
|
||||
self.trayIconMenu.addAction(self.recompress)
|
||||
self.trayIconMenu.addSeparator()
|
||||
self.trayIconMenu.addAction(self.hideMain)
|
||||
self.trayIconMenu.addSeparator()
|
||||
self.trayIconMenu.addAction(self.quitAction)
|
||||
|
||||
if QSystemTrayIcon.isSystemTrayAvailable():
|
||||
self.trayIcon = QSystemTrayIcon(self)
|
||||
self.trayIcon.setContextMenu(self.trayIconMenu)
|
||||
self.trayIcon.setToolTip("Trimage image compressor")
|
||||
self.trayIcon.setIcon(QIcon(self.parent.ui.get_image("pixmaps/trimage-icon.png")))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
myapp = StartQT4()
|
||||
|
||||
if myapp.showapp:
|
||||
myapp.show()
|
||||
sys.exit(app.exec_())
|
||||
166
debian/trimage/usr/lib/python2.7/dist-packages/trimage/ui.py
vendored
Normal file
166
debian/trimage/usr/lib/python2.7/dist-packages/trimage/ui.py
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from os import path
|
||||
|
||||
class TrimageTableView(QTableView):
|
||||
"""Init the table drop event."""
|
||||
def __init__(self, parent=None):
|
||||
super(TrimageTableView, self).__init__(parent)
|
||||
self.setAcceptDrops(True)
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if event.mimeData().hasUrls:
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
event.accept()
|
||||
|
||||
def dropEvent(self, event):
|
||||
event.accept()
|
||||
filelist = []
|
||||
for url in event.mimeData().urls():
|
||||
filelist.append(unicode(url.toLocalFile()))
|
||||
|
||||
self.emit(SIGNAL("fileDropEvent"), (filelist))
|
||||
|
||||
|
||||
class Ui_trimage(object):
|
||||
def get_image(self, image):
|
||||
""" Get the correct link to the images used in the UI """
|
||||
imagelink = path.join(path.dirname(path.dirname(path.realpath(__file__))), "trimage/" + image)
|
||||
return imagelink
|
||||
|
||||
def setupUi(self, trimage):
|
||||
""" Setup the entire UI """
|
||||
trimage.setObjectName("trimage")
|
||||
trimage.resize(600, 170)
|
||||
|
||||
trimageIcon = QIcon(self.get_image("pixmaps/trimage-icon.png"))
|
||||
trimage.setWindowIcon(trimageIcon)
|
||||
|
||||
self.centralwidget = QWidget(trimage)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
|
||||
self.gridLayout_2 = QGridLayout(self.centralwidget)
|
||||
self.gridLayout_2.setMargin(0)
|
||||
self.gridLayout_2.setSpacing(0)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
|
||||
self.widget = QWidget(self.centralwidget)
|
||||
self.widget.setEnabled(True)
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(1)
|
||||
sizePolicy.setVerticalStretch(1)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.widget.sizePolicy().hasHeightForWidth())
|
||||
self.widget.setSizePolicy(sizePolicy)
|
||||
self.widget.setObjectName("widget")
|
||||
|
||||
self.verticalLayout = QVBoxLayout(self.widget)
|
||||
self.verticalLayout.setSpacing(0)
|
||||
self.verticalLayout.setMargin(0)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
|
||||
self.frame = QFrame(self.widget)
|
||||
self.frame.setObjectName("frame")
|
||||
|
||||
self.verticalLayout_2 = QVBoxLayout(self.frame)
|
||||
self.verticalLayout_2.setSpacing(0)
|
||||
self.verticalLayout_2.setMargin(0)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.horizontalLayout.setSpacing(0)
|
||||
self.horizontalLayout.setMargin(10)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
|
||||
self.addfiles = QPushButton(self.frame)
|
||||
font = QFont()
|
||||
font.setPointSize(9)
|
||||
self.addfiles.setFont(font)
|
||||
self.addfiles.setCursor(Qt.PointingHandCursor)
|
||||
icon = QIcon()
|
||||
icon.addPixmap(QPixmap(self.get_image("pixmaps/list-add.png")), QIcon.Normal, QIcon.Off)
|
||||
self.addfiles.setIcon(icon)
|
||||
self.addfiles.setObjectName("addfiles")
|
||||
self.addfiles.setAcceptDrops(True)
|
||||
self.horizontalLayout.addWidget(self.addfiles)
|
||||
|
||||
self.label = QLabel(self.frame)
|
||||
font = QFont()
|
||||
font.setPointSize(8)
|
||||
self.label.setFont(font)
|
||||
self.label.setFrameShadow(QFrame.Plain)
|
||||
self.label.setMargin(1)
|
||||
self.label.setIndent(10)
|
||||
self.label.setObjectName("label")
|
||||
self.horizontalLayout.addWidget(self.label)
|
||||
|
||||
spacerItem = QSpacerItem(498, 20, QSizePolicy.Expanding,
|
||||
QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.recompress = QPushButton(self.frame)
|
||||
font = QFont()
|
||||
font.setPointSize(9)
|
||||
self.recompress.setFont(font)
|
||||
self.recompress.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
icon1 = QIcon()
|
||||
icon1.addPixmap(QPixmap(self.get_image("pixmaps/view-refresh.png")), QIcon.Normal, QIcon.Off)
|
||||
|
||||
self.recompress.setIcon(icon1)
|
||||
self.recompress.setCheckable(False)
|
||||
self.recompress.setObjectName("recompress")
|
||||
self.horizontalLayout.addWidget(self.recompress)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayout)
|
||||
|
||||
self.processedfiles = TrimageTableView(self.frame)
|
||||
self.processedfiles.setEnabled(True)
|
||||
self.processedfiles.setFrameShape(QFrame.NoFrame)
|
||||
self.processedfiles.setFrameShadow(QFrame.Plain)
|
||||
self.processedfiles.setLineWidth(0)
|
||||
self.processedfiles.setMidLineWidth(0)
|
||||
self.processedfiles.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.processedfiles.setTabKeyNavigation(True)
|
||||
self.processedfiles.setAlternatingRowColors(True)
|
||||
self.processedfiles.setTextElideMode(Qt.ElideRight)
|
||||
self.processedfiles.setShowGrid(True)
|
||||
self.processedfiles.setGridStyle(Qt.NoPen)
|
||||
self.processedfiles.setSortingEnabled(False)
|
||||
self.processedfiles.setObjectName("processedfiles")
|
||||
self.processedfiles.resizeColumnsToContents()
|
||||
self.processedfiles.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
self.verticalLayout_2.addWidget(self.processedfiles)
|
||||
self.verticalLayout.addWidget(self.frame)
|
||||
self.gridLayout_2.addWidget(self.widget, 0, 0, 1, 1)
|
||||
trimage.setCentralWidget(self.centralwidget)
|
||||
|
||||
self.retranslateUi(trimage)
|
||||
QMetaObject.connectSlotsByName(trimage)
|
||||
|
||||
def retranslateUi(self, trimage):
|
||||
""" Fill in the texts for all UI elements """
|
||||
trimage.setWindowTitle(QApplication.translate("trimage",
|
||||
"Trimage image compressor", None, QApplication.UnicodeUTF8))
|
||||
self.addfiles.setToolTip(QApplication.translate("trimage",
|
||||
"Add file to the compression list", None,
|
||||
QApplication.UnicodeUTF8))
|
||||
self.addfiles.setText(QApplication.translate("trimage",
|
||||
"&Add and compress", None, QApplication.UnicodeUTF8))
|
||||
self.addfiles.setShortcut(QApplication.translate("trimage",
|
||||
"Alt+A", None, QApplication.UnicodeUTF8))
|
||||
self.label.setText(QApplication.translate("trimage",
|
||||
"Drag and drop images onto the table", None,
|
||||
QApplication.UnicodeUTF8))
|
||||
self.recompress.setToolTip(QApplication.translate("trimage",
|
||||
"Recompress all images", None, QApplication.UnicodeUTF8))
|
||||
self.recompress.setText(QApplication.translate("trimage",
|
||||
"&Recompress", None, QApplication.UnicodeUTF8))
|
||||
self.recompress.setShortcut(QApplication.translate("trimage",
|
||||
"Alt+R", None, QApplication.UnicodeUTF8))
|
||||
self.processedfiles.setToolTip(QApplication.translate("trimage",
|
||||
"Drag files in here", None, QApplication.UnicodeUTF8))
|
||||
self.processedfiles.setWhatsThis(QApplication.translate("trimage",
|
||||
"Drag files in here", None, QApplication.UnicodeUTF8))
|
||||
Loading…
Add table
Add a link
Reference in a new issue