Skip to content
Browse files

Implemented packer class for archives, and changed archive type const…

…ants.
  • Loading branch information...
1 parent 1e32e51 commit 3f1a305bc5abe9e71f2331ceced28f2d57cbdd1a Pontus Ekberg committed Sep 25, 2008
Showing with 136 additions and 49 deletions.
  1. +2 −0 install.py
  2. +74 −19 src/archive.py
  3. +1 −1 src/bookmark.py
  4. +40 −0 src/edit.py
  5. +13 −20 src/filehandler.py
  6. +1 −1 src/librarybackend.py
  7. +1 −1 src/properties.py
  8. +1 −1 src/thumbbar.py
  9. +1 −1 src/thumbnail.py
  10. +2 −5 src/ui.py
View
2 install.py
@@ -45,6 +45,8 @@
("src/constants.pyc", "share/comix/src"),
("src/cursor.py", "share/comix/src"),
("src/cursor.pyc", "share/comix/src"),
+ ("src/edit.py", "share/comix/src"),
+ ("src/edit.pyc", "share/comix/src"),
("src/encoding.py", "share/comix/src"),
("src/encoding.pyc", "share/comix/src"),
("src/enhance.py", "share/comix/src"),
View
93 src/archive.py
@@ -9,6 +9,8 @@
import process
+ZIP, RAR, TAR, GZIP, BZIP2 = range(5)
+
# Determine if rar/unrar exists, and bind the executable path to _rar_exec
_rar_exec = None
for path in os.getenv('PATH', '').split(':') + [os.path.curdir]:
@@ -56,13 +58,13 @@ def setup(self, src, dst):
self._extract_thread = None
self._condition = threading.Condition()
- if self._type == 'zip':
+ if self._type == ZIP:
self._zfile = zipfile.ZipFile(src, 'r')
self._files = self._zfile.namelist()
- elif self._type in ('tar', 'gzip', 'bzip2'):
+ elif self._type in (TAR, GZIP, BZIP2):
self._tfile = tarfile.open(src, 'r')
self._files = self._tfile.getnames()
- elif self._type == 'rar' and _rar_exec:
+ elif self._type == RAR and _rar_exec:
proc = process.Process([_rar_exec, 'vb', src])
fobj = proc.spawn()
self._files = [name.rstrip('\n') for name in fobj.readlines()]
@@ -94,7 +96,7 @@ def set_files(self, files):
not be used for scanned comic books. So, we cheat and ignore the
ordering applied with this method on such archives.
"""
- if self._type in ('gzip', 'bzip2'):
+ if self._type in (GZIP, BZIP2):
self._files = filter(files.count, self._files)
else:
self._files = files
@@ -131,9 +133,9 @@ def close(self):
"""Close any open file objects, need only be called manually if the
extract() method isn't called.
"""
- if self._type == 'zip':
+ if self._type == ZIP:
self._zfile.close()
- elif self._type in ('tar', 'gzip', 'bzip2'):
+ elif self._type in (TAR, GZIP, BZIP2):
self._tfile.close()
def _thread_extract(self):
@@ -151,20 +153,20 @@ def _extract_file(self, name):
self.close()
sys.exit(0)
try:
- if self._type == 'zip':
+ if self._type == ZIP:
dst_path = os.path.join(self._dst, name)
if not os.path.exists(os.path.dirname(dst_path)):
os.makedirs(os.path.dirname(dst_path))
new = open(dst_path, 'w')
new.write(self._zfile.read(name))
new.close()
- elif self._type in ('tar', 'gzip', 'bzip2'):
+ elif self._type in (TAR, GZIP, BZIP2):
if os.path.normpath(os.path.join(self._dst, name)).startswith(
self._dst):
self._tfile.extract(name, self._dst)
else:
print '! archive.py: Non-local tar member:', name, '\n'
- elif self._type == 'rar':
+ elif self._type == RAR:
if _rar_exec:
proc = process.Process([_rar_exec, 'x', '-p-', '-o-',
'-inul', '--', self._src, name, self._dst])
@@ -184,37 +186,90 @@ def _extract_file(self, name):
self._condition.release()
+class Packer:
+
+ """Packer is a threaded class for packing files into Zip archives.
+
+ It would be straight-forward to add support for more archive types,
+ but basically all other types are less well fitted for this particular
+ task than Zip archives are (yes, really).
+ """
+
+ def __init__(self, image_files, other_files, archive_path):
+ """Setup a Packer object to create a Zip archive at <archive_path>.
+ All files pointed to by paths in the sequences <image_files> and
+ <other_files> will be included in the archive. The files in
+ <image_files> will be renamed so that the lexical ordering of their
+ filenames matches that of their order in the list. The files in
+ <other_files> will be included as they are.
+ """
+ self._image_files = image_files
+ self._other_files = other_files
+ self._archive_path = archive_path
+ self._pack_thread = None
+ self._lock = threading.Lock()
+
+ def pack(self):
+ """Pack all the files in the file lists into the archive."""
+ self._pack_thread = threading.Thread(target=self._thread_pack)
+ self._pack_thread.setDaemon(False)
+ self._lock.acquire()
+ self._pack_thread.start()
+
+ def wait(self):
+ """Block until the packer thread has finished."""
+ self._lock.acquire()
+ self._lock.release()
+
+ def _thread_pack(self):
+ zfile = zipfile.ZipFile(self._archive_path, 'w')
+ used_names = []
+ name = os.path.splitext(os.path.basename(self._archive_path))[0]
+ pattern = '%%0%dd - %s%%s' % (len(str(len(self._image_files))), name)
+ for i, path in enumerate(self._image_files):
+ filename = pattern % (i + 1, os.path.splitext(path)[1])
+ zfile.write(path, filename, zipfile.ZIP_STORED)
+ used_names.append(filename)
+ for path in self._other_files:
+ filename = os.path.basename(path)
+ while filename in used_names:
+ filename = '_%s' % filename
+ zfile.write(path, filename, zipfile.ZIP_DEFLATED)
+ used_names.append(filename)
+ zfile.close()
+ self._lock.release()
+
def archive_mime_type(path):
"""Return the archive type of <path> or None for non-archives."""
try:
if os.path.isfile(path):
if not os.access(path, os.R_OK):
return None
if zipfile.is_zipfile(path):
- return 'zip'
+ return ZIP
fd = open(path, 'rb')
magic = fd.read(4)
fd.close()
if tarfile.is_tarfile(path) and os.path.getsize(path) > 0:
if magic.startswith('BZh'):
- return 'bzip2'
+ return BZIP2
if magic.startswith('\037\213'):
- return 'gzip'
- return 'tar'
+ return GZIP
+ return TAR
if magic == 'Rar!':
- return 'rar'
+ return RAR
except Exception:
print '! archive.py: Error while reading', path
return None
def get_name(archive_type):
"""Return a text representation of an archive type."""
- return {'zip': _('ZIP archive'),
- 'tar': _('Tar archive'),
- 'gzip': _('Gzip compressed tar archive'),
- 'bzip2': _('Bzip2 compressed tar archive'),
- 'rar': _('RAR archive')}[archive_type]
+ return {ZIP: _('ZIP archive'),
+ TAR: _('Tar archive'),
+ GZIP: _('Gzip compressed tar archive'),
+ BZIP2: _('Bzip2 compressed tar archive'),
+ RAR: _('RAR archive')}[archive_type]
def get_archive_info(path):
View
2 src/bookmark.py
@@ -108,7 +108,7 @@ def __init__(self, file_handler, name, path, page, numpages, archive_type):
self._file_handler = file_handler
gtk.MenuItem.__init__(self, str(self), False)
- if self._archive_type:
+ if self._archive_type is not None:
im = gtk.image_new_from_stock('comix-archive', gtk.ICON_SIZE_MENU)
else:
im = gtk.image_new_from_stock('comix-image', gtk.ICON_SIZE_MENU)
View
40 src/edit.py
@@ -0,0 +1,40 @@
+"""edit.py - Archive editor."""
+
+import gtk
+
+_dialog = None
+
+
+class _EditArchiveDialog(gtk.Dialog):
+
+ def __init__(self):
+ gtk.Dialog.__init__(self, _('Edit archive'), None, 0,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK))
+ self.set_has_separator(False)
+ #self.set_resizable(False)
+ self.connect('response', self._response)
+ self.set_default_response(gtk.RESPONSE_OK)
+
+ self.show_all()
+
+ def _response(self, dialog, response):
+ if response == gtk.RESPONSE_OK:
+ print 'ok'
+ else:
+ print 'close'
+
+
+def open_dialog(*args):
+ global _dialog
+ if _dialog is None:
+ _dialog = _EditArchiveDialog()
+ else:
+ _dialog.present()
+
+
+def _close_dialog(*args):
+ global _dialog
+ if _dialog is not None:
+ _dialog.destroy()
+ _dialog = None
View
33 src/filehandler.py
@@ -115,7 +115,8 @@ def next_page(self):
old_page = self.get_current_page()
viewed = self._window.is_double_page and 2 or 1
if self.get_current_page() + viewed > self.get_number_of_pages():
- if prefs['auto open next archive'] and self.archive_type:
+ if (prefs['auto open next archive'] and
+ self.archive_type is not None):
self._open_next_archive()
return False
self._current_image_index += self._get_step_length()
@@ -128,7 +129,8 @@ def previous_page(self):
if not self.file_loaded:
return False
if self.get_current_page() == 1:
- if prefs['auto open next archive'] and self.archive_type:
+ if (prefs['auto open next archive'] and
+ self.archive_type is not None):
self._open_previous_archive()
return False
old_page = self.get_current_page()
@@ -183,9 +185,7 @@ def open_file(self, path, start_page=1):
Return True if the file is successfully loaded.
"""
- # --------------------------------------------------------------------
# If the given <path> is invalid we update the statusbar.
- # --------------------------------------------------------------------
if os.path.isdir(path):
self._window.statusbar.set_message(
_('%s: Target is a directory.') % path)
@@ -198,23 +198,22 @@ def open_file(self, path, start_page=1):
_('%s: Permission denied.') % path)
return False
self.archive_type = archive.archive_mime_type(path)
- if not self.archive_type and not is_image_file(path):
+ if self.archive_type is None and not is_image_file(path):
self._window.statusbar.set_message(
_('%s: Unsupported file type.') % path)
return False
-
+
+ # We close the previously opened file.
self._window.cursor_handler.set_cursor_type(cursor.WAIT)
if self.file_loaded:
self.close_file()
while gtk.events_pending():
gtk.main_iteration(False)
- # --------------------------------------------------------------------
# If <path> is an archive we create an Extractor for it and set the
# files in it with file endings indicating image files or comments
# as the ones to be extracted.
- # --------------------------------------------------------------------
- if self.archive_type:
+ if self.archive_type is not None:
self._base_path = path
self._condition = self._extractor.setup(path, self._tmp_dir)
files = self._extractor.get_files()
@@ -253,10 +252,7 @@ def open_file(self, path, start_page=1):
self._extractor.set_files(image_files + comment_files)
self._extractor.extract()
-
- # --------------------------------------------------------------------
# If <path> is an image we scan its directory for more images.
- # --------------------------------------------------------------------
else:
self._base_path = os.path.dirname(path)
for f in os.listdir(self._base_path):
@@ -266,9 +262,6 @@ def open_file(self, path, start_page=1):
self._image_files.sort(locale.strcoll)
self._current_image_index = self._image_files.index(path)
- # --------------------------------------------------------------------
- # If there are no viewable image files found.
- # --------------------------------------------------------------------
if not self._image_files:
self._window.statusbar.set_message(_('No images in "%s"') %
os.path.basename(path))
@@ -350,7 +343,7 @@ def get_pretty_current_filename(self):
"""Return a string with the name of the currently viewed file that is
suitable for printing.
"""
- if self.archive_type:
+ if self.archive_type is not None:
return os.path.basename(self._base_path)
return os.path.join(os.path.basename(self._base_path),
os.path.basename(self._image_files[self._current_image_index]))
@@ -374,7 +367,7 @@ def get_real_path(self):
full path to the archive or the full path to the currently
viewed image.
"""
- if self.archive_type:
+ if self.archive_type is not None:
return self.get_path_to_base()
return self._image_files[self._current_image_index]
@@ -453,7 +446,7 @@ def _open_next_archive(self):
return
for f in files[current_index + 1:]:
path = os.path.join(arch_dir, f)
- if archive.archive_mime_type(path):
+ if archive.archive_mime_type(path) is not None:
self.open_file(path)
return
@@ -470,7 +463,7 @@ def _open_previous_archive(self):
return
for f in reversed(files[:current_index]):
path = os.path.join(arch_dir, f)
- if archive.archive_mime_type(path):
+ if archive.archive_mime_type(path) is not None:
self.open_file(path, 0)
return
@@ -498,7 +491,7 @@ def _wait_on_file(self, path):
archive and has not yet been extracted. Return when the file is
ready.
"""
- if not self.archive_type:
+ if self.archive_type is None:
return
name = self._name_table[path]
self._condition.acquire()
View
2 src/librarybackend.py
@@ -305,7 +305,7 @@ def _create_table_book(self):
name string,
path string unique,
pages integer,
- format string,
+ format integer,
size integer,
added date default current_date)''')
View
2 src/properties.py
@@ -97,7 +97,7 @@ def __init__(self, window):
notebook.set_border_width(6)
self.vbox.pack_start(notebook, False, False, 0)
- if window.file_handler.archive_type:
+ if window.file_handler.archive_type is not None:
# ------------------------------------------------------------
# Archive tab
# ------------------------------------------------------------
View
2 src/thumbbar.py
@@ -106,7 +106,7 @@ def update_select(self):
self._vadjust.set_value(value)
def _load(self):
- if self._window.file_handler.archive_type:
+ if self._window.file_handler.archive_type is not None:
create = False
else:
create = prefs['create thumbnails']
View
2 src/thumbnail.py
@@ -72,7 +72,7 @@ def _get_new_thumbnail(path, create, dst_dir):
True we also save it to disk with <dst_dir> as the base thumbnail
directory.
"""
- if archive.archive_mime_type(path):
+ if archive.archive_mime_type(path) is not None:
if create:
return _get_new_archive_thumbnail(path, dst_dir)
return None
View
7 src/ui.py
@@ -9,6 +9,7 @@
import about
import bookmark
import comment
+import edit
import enhance
import filechooser
import filehandler
@@ -19,10 +20,6 @@
import thumbremover
-def bogus(*args):
- print 'Feature not implemented yet.'
-
-
class MainUI(gtk.UIManager):
def __init__(self, window):
@@ -54,7 +51,7 @@ def __init__(self, window):
_('_Thumbnail maintenance...'),
None, None, thumbremover.open_dialog),
('edit_archive', gtk.STOCK_EDIT, _('_Edit archive...'),
- None, None, bogus),
+ None, None, edit.open_dialog),
('close', gtk.STOCK_CLOSE, _('_Close'),
'<Control>w', None, window.file_handler.close_file),
('quit', gtk.STOCK_QUIT, _('_Quit'),

0 comments on commit 3f1a305

Please sign in to comment.
Something went wrong with that request. Please try again.