From 92016fe5c1c52ecaccc5336ef409bd1b48c0963f Mon Sep 17 00:00:00 2001 From: gryf Date: Sun, 2 Nov 2008 19:48:41 +0000 Subject: [PATCH] * Finally, version 1.0. --- README | 10 +- resources/glade/main.glade | 29 +- src/ctrls/c_main.py | 73 ++--- src/models/m_config.py | 20 +- src/models/m_main.py | 551 ++++++++++++++++++++++--------------- src/views/v_dialogs.py | 22 +- 6 files changed, 390 insertions(+), 315 deletions(-) diff --git a/README b/README index e582fc4..017128d 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -pyGTKtalog 1.0 RC3 +pyGTKtalog 1.0 ================== pyGTKtalog is Linux/FreeBSD program for indexing CD/DVD or directories on @@ -107,6 +107,14 @@ Catalog file is tared and gziped sqlite database and directories with images and thumbnails. If there are more images, the size of catalog file will grow. So be carefull with adding big images in your catalog file! +There is also converter form old database to new. In fact no image are stored in +archive with katalog. All thumnails will be lost. All images without big image +will be lost. There ar serious changes with application design, and I decided, +that is better to keep media unpacked on disk, instead of pack it every time +with save and unpack with open methods. New design prevent from deleting eny +file from media directory (placed in ~/.pygtktalog/images). Functionality for +exporting images and corresponding db file is planned. + BUGS ==== diff --git a/resources/glade/main.glade b/resources/glade/main.glade index 54d9b2a..0e8b74f 100644 --- a/resources/glade/main.glade +++ b/resources/glade/main.glade @@ -179,17 +179,6 @@ - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Deletes all images from files in current colection. -Thumbnails from image tabs will be keep. - Delete all images without thumbnals - True - - - True @@ -199,6 +188,14 @@ Thumbnails from image tabs will be keep. + + + True + Save all images... + True + + + True @@ -1007,20 +1004,18 @@ thumbnail from first image will be generated. True - GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _Delete images True - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Delete selected images, but leave thumbnails - De_lete images (keep thumbs) + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Set as _thumbnail True - + diff --git a/src/ctrls/c_main.py b/src/ctrls/c_main.py index 4963315..874831a 100644 --- a/src/ctrls/c_main.py +++ b/src/ctrls/c_main.py @@ -49,7 +49,7 @@ class MainController(Controller): widgets_all = ('tag_path_box', 'hpaned1', 'file1', 'edit1', 'view1', 'help1', 'add_cd', 'add_directory1', 'del_all_images', - 'del_all_images_thumb', 'del_all_thumb', 'stat1', + 'del_all_thumb', 'stat1', 'tb_new','tb_open', 'tb_save', 'tb_addcd', 'tb_adddir', 'tb_find', 'tb_quit') widgets_cancel = ('cancel','cancel1') @@ -120,7 +120,7 @@ class MainController(Controller): self.view['tag_cloud_textview'].connect("populate-popup", self.on_tag_cloud_textview_popup) # in case model has opened file, register tags - if self.model.internal_dirname: + if self.model.db_tmp_path: self.__tag_cloud() else: self.model.new() @@ -345,7 +345,7 @@ class MainController(Controller): ImageView(img) else: Dialogs.Inf("Image view", "No Image", - "This item have no real image, only thumbnail.") + "Image file does not exist.") def on_rename1_activate(self, widget): model, iter = self.view['discs'].get_selection().get_selected() @@ -749,7 +749,7 @@ class MainController(Controller): for path in list_of_paths: iterator = model.get_iter(path) ids.append(model.get_value(iterator, 0)) - + for fid in ids: self.model.delete_image(fid) @@ -805,37 +805,23 @@ class MainController(Controller): description) return - def on_img_delete2_activate(self, menu_item): - """remove images, but keep thumbnails""" + def on_img_thumbset_activate(self, menu_item): + """set selected image as thumbnail""" list_of_paths = self.view['images'].get_selected_items() if not list_of_paths: - Dialogs.Inf("Delete images", "No images selected", - "You have to select at least one image to delete.") + Dialogs.Inf("Set thumbnail", "No image selected", + "You have to select one image to set as thumbnail.") + return + if len(list_of_paths) >1: + Dialogs.Inf("Set thumbnail", "To many images selected", + "You have to select one image to set as thumbnail.") return - if self.model.config.confd['delwarn']: - description = 'Selected images will be permanently removed from ' - description += 'catalog,\nthumbnails will be keeped.' - obj = Dialogs.Qst('Delete images', 'Delete selected images?', - description) - if not obj.run(): - return - model = self.view['images'].get_model() - for path in list_of_paths: - iter = model.get_iter(path) - id = model.get_value(iter, 0) - self.model.delete_images_wth_thumbs(id) - - try: - path, column = self.view['files'].get_cursor() - model = self.view['files'].get_model() - iter = model.get_iter(path) - id = model.get_value(iter, 0) - self.__get_item_info(id) - except: - pass + iter = model.get_iter(list_of_paths[0]) + id = model.get_value(iter, 0) + self.model.set_image_as_thumbnail(id) self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) @@ -1049,7 +1035,7 @@ class MainController(Controller): ids = self.__get_tv_selection_ids(self.view['files']) for id in ids: self.model.add_tags(id, tags) - + self.__tag_cloud() self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) @@ -1193,7 +1179,7 @@ class MainController(Controller): for p in list_of_paths: val = model.get_value(model.get_iter(p), 0) ids.append(val) - + for fid in ids: # delete from db self.model.delete(fid) @@ -1263,31 +1249,6 @@ class MainController(Controller): pass return - def on_del_all_images_thumb_activate(self, menu_item): - if self.model.config.confd['delwarn']: - title = 'Delete images' - question = 'Delete all images?' - dsc = "All images without thumbnails will be permanently removed" - dsc += " from catalog." - obj = Dialogs.Qst(title, question, dsc) - if not obj.run(): - return - - self.model.delete_all_images_wth_thumbs() - self.model.unsaved_project = True - self.__set_title(filepath=self.model.filename, modified=True) - - try: - path, column = self.view['files'].get_cursor() - model = self.view['files'].get_model() - fiter = model.get_iter(path) - fid = model.get_value(fiter, 0) - if fid: - self.__get_item_info(fid) - except: - pass - return - def on_del_all_thumb_activate(self, menu_item): if self.model.config.confd['delwarn']: title = 'Delete images' diff --git a/src/models/m_config.py b/src/models/m_config.py index dca615f..5e1c41d 100644 --- a/src/models/m_config.py +++ b/src/models/m_config.py @@ -157,9 +157,11 @@ class ConfigModel(Model): dstring = ('cd','ejectapp','imgprog') try: - path = os.environ['HOME'] - except: - path = "/tmp" + path = os.path.join(os.environ['HOME'], ".pygtktalog") + except KeyError: + raise KeyError, "Cannot stat path for current user home!" + + path = os.path.join(path, "config.ini") def __init__(self): Model.__init__(self) @@ -179,12 +181,12 @@ class ConfigModel(Model): def save(self): try: - os.lstat("%s/.pygtktalog" % self.path) + os.lstat(self.path) except: - print "Saving preferences to %s/.pygtktalog" % self.path + print "Saving preferences to %s." % self.path if __debug__: print "m_config.py: save() Saving preferences to", - print "%s/.pygtktalog" % self.path + print "%s" % self.path newIni = Ini() # main section @@ -224,12 +226,12 @@ class ConfigModel(Model): # write config try: - f = open("%s/.pygtktalog" % self.path,"w") + f = open(self.path, "w") success = True except: if __debug__: print "m_config.py: save() Cannot open config file", - print "%s for writing." % (self.path, "/.pygtktalog") + print "%s for writing." % self.path success = False f.write(newIni.show()) f.close() @@ -239,7 +241,7 @@ class ConfigModel(Model): try: # try to read config file parser = ConfigParser() - parser.read("%s/.pygtktalog" % self.path) + parser.read(self.path) r = {} h = {} self.recent = [] diff --git a/src/models/m_main.py b/src/models/m_main.py index 3bb254b..dce1ca5 100644 --- a/src/models/m_main.py +++ b/src/models/m_main.py @@ -25,8 +25,9 @@ import os import sys import shutil -import tarfile +import bz2 import math +from tempfile import mkstemp import gtk import gobject @@ -47,6 +48,8 @@ except: from utils.parse_exif import ParseExif from utils.gthumb import GthumbCommentParser +from utils.no_thumb import no_thumb as no_thumb_img + class MainModel(ModelMT): """Create, load, save, manipulate db file which is container for data""" @@ -91,6 +94,7 @@ class MainModel(ModelMT): self.unsaved_project = False self.filename = None # catalog saved/opened filename self.internal_dirname = None + self.image_path = None self.db_connection = None self.db_cursor = None self.abort = False @@ -105,6 +109,7 @@ class MainModel(ModelMT): self.statusmsg = "Idle" self.selected_tags = {} self.search_created = False + self.db_tmp_path = False # Directory tree: id, name, icon, type self.discs_tree = gtk.TreeStore(gobject.TYPE_INT, @@ -150,6 +155,30 @@ class MainModel(ModelMT): # - #rgb # - #rrggbb self.tag_cloud = [] + + try: + path = os.path.join(os.environ['HOME'], ".pygtktalog") + imgpath = os.path.join(path, "images") + except KeyError: + raise KeyError, "Cannot stat path for current user home!" + + if os.path.exists(path): + if not os.path.isdir(path): + raise RuntimeError, "There is regular file \"%s\" on the way. Please remove it." % \ + path + else: + os.mkdir(path) + + + if os.path.exists(imgpath): + if not os.path.isdir(imgpath): + print "Warning:", + "There is regular file \"%s\" on the way. Please remove it, otherwise images cannot be used" % imgpath + else: + os.mkdir(imgpath) + + self.image_path = imgpath + self.new() return @@ -323,49 +352,57 @@ class MainModel(ModelMT): def add_image(self, image, file_id, only_thumbs=False): """add single image to file/directory""" - sql = """INSERT INTO images(file_id, thumbnail, filename) - VALUES(?, null, null)""" - self.db_cursor.execute(sql, (file_id,)) - self.db_connection.commit() + imp = Img(image, self.image_path).save() + if imp: + # check if there is that image already + sql = """SELECT filename FROM images WHERE file_id=? and filename=?""" + self.db_cursor.execute(sql, (file_id, imp)) + res = self.db_cursor.fetchone() + if res and res[0]: + # there is such an image. going back. + if __debug__: + print res[0] + return - sql = """SELECT id FROM images WHERE thumbnail is null - AND filename IS null AND file_id=?""" - self.db_cursor.execute(sql, (file_id,)) - res = self.db_cursor.fetchone() - if res: - thp, imp, rec = Img(image, self.internal_dirname).save(res[0]) - if rec != -1: - sql = """UPDATE images SET filename=?, - thumbnail=? WHERE id=?""" - if only_thumbs: - os.unlink(imp) - img = None - else: - img = imp.split(self.internal_dirname)[1][1:] - self.db_cursor.execute(sql, - (img, - thp.split(self.internal_dirname)[1][1:], - res[0])) - - # check if file have have thumbnail. if not, make it with image - sql = """SELECT id from thumbnails where file_id=?""" + # check if file have have thumbnail. if not, make it with first + # image + sql = """SELECT id FROM thumbnails WHERE file_id=?""" self.db_cursor.execute(sql, (file_id,)) res = self.db_cursor.fetchone() - if not res: - self.add_thumbnail(image, file_id) - self.db_connection.commit() + thumb = 1 + if not(res and res[0]): + sql = """INSERT INTO thumbnails(filename, file_id) VALUES(?, ?)""" + self.db_cursor.execute(sql, (imp, file_id)) + + # insert picture into db + sql = """INSERT INTO images(file_id, filename) + VALUES(?, ?)""" + self.db_cursor.execute(sql, (file_id, imp)) + self.db_connection.commit() + + ## check if file have have thumbnail. if not, make it with image + #sql = """SELECT id from thumbnails where file_id=?""" + #self.db_cursor.execute(sql, (file_id,)) + #res = self.db_cursor.fetchone() + #if not res: + # sql = """insert into thumbnails(file_id, filename) + # values (?, ?)""" + # self.db_cursor.execute(sql, (file_id, thp)) + # self.db_connection.commit() + # + self.db_connection.commit() def del_images(self, file_id): """removes images and their thumbnails from selected file/dir""" - # remove images - sql = """SELECT filename, thumbnail FROM images WHERE file_id =?""" - self.db_cursor.execute(sql, (file_id,)) - res = self.db_cursor.fetchall() - if len(res) > 0: - for filen in res: - if filen[0]: - os.unlink(os.path.join(self.internal_dirname, filen[0])) - os.unlink(os.path.join(self.internal_dirname, filen[1])) + ## remove images + #sql = """SELECT filename, thumbnail FROM images WHERE file_id =?""" + #self.db_cursor.execute(sql, (file_id,)) + #res = self.db_cursor.fetchall() + #if len(res) > 0: + # for filen in res: + # if filen[0]: + # os.unlink(os.path.join(self.internal_dirname, filen[0])) + # os.unlink(os.path.join(self.internal_dirname, filen[1])) # remove images records sql = """DELETE FROM images WHERE file_id = ?""" @@ -380,109 +417,122 @@ class MainModel(ModelMT): self.db_cursor.execute(sql, (image_id,)) res = self.db_cursor.fetchone() if res and res[0]: - source = os.path.join(self.internal_dirname, res[0]) + source = os.path.join(self.image_path, res[0]) count = 1 - dest = os.path.join(file_path, res[1] + "_%d." % count + \ - res[0].split('.')[-1]) + dest = os.path.join(file_path, res[1] + "_%04d." % count + 'jpg') while os.path.exists(dest): count += 1 - dest = os.path.join(file_path, res[1] + "_%d." % count + \ - res[0].split('.')[-1]) - + dest = os.path.join(file_path, res[1] + "_%04d." %\ + count + 'jpg') + if not os.path.exists(source): + return False shutil.copy(source, dest) return True - #os.unlink() else: return False def delete_images_wth_thumbs(self, image_id): """removes image (without thumbnail) on specified image id""" - sql = """SELECT filename FROM images WHERE id=?""" - self.db_cursor.execute(sql, (image_id,)) - res = self.db_cursor.fetchone() - if res: - if res[0]: - os.unlink(os.path.join(self.internal_dirname, res[0])) + print "method removed" + #sql = """SELECT filename FROM images WHERE id=?""" + #self.db_cursor.execute(sql, (image_id,)) + #res = self.db_cursor.fetchone() + #if res: + # if res[0]: + # os.unlink(os.path.join(self.internal_dirname, res[0])) + # + # if __debug__: + # print "m_main.py: delete_image(): removed images:" + # print res[0] - if __debug__: - print "m_main.py: delete_image(): removed images:" - print res[0] # remove images records - sql = """UPDATE images set filename=NULL WHERE id = ?""" - self.db_cursor.execute(sql, (image_id,)) - self.db_connection.commit() + #sql = """UPDATE images set filename=NULL WHERE id = ?""" + #self.db_cursor.execute(sql, (image_id,)) + #self.db_connection.commit() def delete_all_images_wth_thumbs(self): """removes all images (without thumbnails) from collection""" - sql = """SELECT filename FROM images""" - self.db_cursor.execute(sql) - res = self.db_cursor.fetchall() - for row in res: - if row[0]: - os.unlink(os.path.join(self.internal_dirname, row[0])) - if __debug__: - print "m_main.py: delete_all_images(): removed image:", - print row[0] + print "method removed" + #sql = """SELECT filename FROM images""" + #self.db_cursor.execute(sql) + #res = self.db_cursor.fetchall() + #for row in res: + # if row[0]: + # os.unlink(os.path.join(self.internal_dirname, row[0])) + # if __debug__: + # print "m_main.py: delete_all_images(): removed image:", + # print row[0] + # remove images records - sql = """UPDATE images set filename=NULL""" - self.db_cursor.execute(sql) - self.db_connection.commit() + #sql = """UPDATE images set filename=NULL""" + #self.db_cursor.execute(sql) + #self.db_connection.commit() def delete_image(self, image_id): """removes image on specified image id""" - sql = """SELECT filename, thumbnail FROM images WHERE id=?""" - self.db_cursor.execute(sql, (image_id,)) - res = self.db_cursor.fetchone() - if res: - if res[0]: - os.unlink(os.path.join(self.internal_dirname, res[0])) - os.unlink(os.path.join(self.internal_dirname, res[1])) + #sql = """SELECT filename, thumbnail FROM images WHERE id=?""" + #self.db_cursor.execute(sql, (image_id,)) + #res = self.db_cursor.fetchone() + #if res: + # if res[0]: + # os.unlink(os.path.join(self.internal_dirname, res[0])) + # os.unlink(os.path.join(self.internal_dirname, res[1])) + # + # if __debug__: + # print "m_main.py: delete_image(): removed images:" + # print res[0] + # print res[1] - if __debug__: - print "m_main.py: delete_image(): removed images:" - print res[0] - print res[1] # remove images records sql = """DELETE FROM images WHERE id = ?""" self.db_cursor.execute(sql, (image_id,)) self.db_connection.commit() - + + def set_image_as_thumbnail(self, image_id): + """set image as file thumbnail""" + sql = """SELECT file_id, filename FROM images WHERE id=?""" + self.db_cursor.execute(sql, (image_id,)) + res = self.db_cursor.fetchone() + if res and res[0]: + sql = """DELETE FROM thumbnails WHERE file_id=?""" + self.db_cursor.execute(sql, (res[0],)) + sql = """INSERT INTO thumbnails(file_id, filename) VALUES(?, ?)""" + self.db_cursor.execute(sql, (res[0], res[1])) + return True + return False + def delete_all_images(self): - """removes all images (with thumbnails) from collection""" + """removes all images from collection""" # remove images records sql = """DELETE FROM images""" self.db_cursor.execute(sql) self.db_connection.commit() - try: - shutil.rmtree(os.path.join(self.internal_dirname, 'images')) - except: - pass + #try: + # shutil.rmtree(os.path.join(self.internal_dirname, 'images')) + #except: + # pass def add_thumbnail(self, img_fn, file_id): """generate and add thumbnail to selected file/dir""" if self.config.confd['thumbs']: self.del_thumbnail(file_id) - thumb = Thumbnail(img_fn, base=self.internal_dirname) - retval = thumb.save(file_id) - if retval[2] != -1: - path = retval[0].split(self.internal_dirname)[1][1:] - sql = """insert into thumbnails(file_id, filename) - values (?, ?)""" - self.db_cursor.execute(sql, (file_id, path)) - self.db_connection.commit() - return True + im, exif = Thumbnail(img_fn, self.image_path).save() + + sql = """INSERT INTO thumbnails(file_id, filename) values (?, ?)""" + self.db_cursor.execute(sql, (file_id, im)) + self.db_connection.commit() + return True return False def del_thumbnail(self, file_id): """removes thumbnail from selected file/dir""" - # remove thumbnail files - sql = """SELECT filename FROM thumbnails WHERE file_id=?""" - self.db_cursor.execute(sql, (file_id,)) - res = self.db_cursor.fetchone() - if res: - os.unlink(os.path.join(self.internal_dirname, res[0])) + #sql = """SELECT filename FROM thumbnails WHERE file_id=?""" + #self.db_cursor.execute(sql, (file_id,)) + #res = self.db_cursor.fetchone() + #if res: + # os.unlink(os.path.join(self.internal_dirname, res[0])) # remove thumbs records sql = """DELETE FROM thumbnails WHERE file_id=?""" @@ -495,26 +545,33 @@ class MainModel(ModelMT): sql = """DELETE FROM thumbnails""" self.db_cursor.execute(sql) self.db_connection.commit() - try: - shutil.rmtree(os.path.join(self.internal_dirname, 'thumbnails')) - except: - pass + #try: + # shutil.rmtree(os.path.join(self.internal_dirname, 'thumbnails')) + #except: + # pass def cleanup(self): """remove temporary directory tree from filesystem""" self.__close_db_connection() - if self.internal_dirname != None: - try: - shutil.rmtree(self.internal_dirname) - except OSError: - pass + try: + os.unlink(self.db_tmp_path) + except: + if __debug__: + print "Exception in removing temporary db file!" + pass + + #if self.internal_dirname != None: + # try: + # shutil.rmtree(self.internal_dirname) + # except OSError: + # pass return def new(self): """create new project""" self.unsaved_project = False self.filename = None - self.__create_internal_dirname() + self.__create_temporary_db_file() self.__connect_to_db() self.__create_database() self.__clear_trees() @@ -525,6 +582,10 @@ class MainModel(ModelMT): def save(self, filename=None): """save tared directory at given catalog fielname""" + + # flush all changes + self.db_connection.commit() + if not filename and not self.filename: if __debug__: return False, "no filename detected!" @@ -540,59 +601,86 @@ class MainModel(ModelMT): def open(self, filename=None): """try to open db file""" self.unsaved_project = False - self.__create_internal_dirname() + self.__create_temporary_db_file() self.filename = filename self.tag_cloud = [] self.selected_tags = {} self.clear_search_tree() try: - tar = tarfile.open(filename, "r:gz") - except: + test_file = open(filename).read(15) + except IOError: + self.filename = None + self.internal_dirname = None + return False + + if test_file == "SQLite format 3": + db_tmp = open(self.db_tmp_path, "wb") + db_tmp.write(open(filename).read()) + db_tmp.close() + elif test_file[0:10] == "BZh91AY&SY": + open_file = bz2.BZ2File(filename) try: - tar = tarfile.open(filename, "r") - except: + curdb = open(self.db_tmp_path, "w") + curdb.write(open_file.read()) + curdb.close() + open_file.close() + except IOError: + # file is not bz2 self.filename = None self.internal_dirname = None - return - - os.chdir(self.internal_dirname) - try: - tar.extractall() - if __debug__: - print "m_main.py: extracted tarfile into", - print self.internal_dirname - except AttributeError: - # python 2.4 tarfile module lacks of method extractall() - directories = [] - for tarinfo in tar: - if tarinfo.isdir(): - # Extract directory with a safe mode, so that - # all files below can be extracted as well. - try: - os.makedirs(os.path.join('.', tarinfo.name), 0700) - except EnvironmentError: - pass - directories.append(tarinfo) - else: - tar.extract(tarinfo, '.') - - # Reverse sort directories. - directories.sort(lambda a, b: cmp(a.name, b.name)) - directories.reverse() - - # Set correct owner, mtime and filemode on directories. - for tarinfo in directories: - try: - os.chown(os.path.join('.', tarinfo.name), - tarinfo.uid, tarinfo.gid) - os.utime(os.path.join('.', tarinfo.name), - (0, tarinfo.mtime)) - except OSError: - if __debug__: - print "m_main.py: open(): setting corrext owner,", - print "mtime etc" - tar.close() + return False + else: + self.filename = None + self.internal_dirname = None + return False + #try: + # tar = tarfile.open(filename, "r:gz") + #except: + # try: + # tar = tarfile.open(filename, "r") + # except: + # self.filename = None + # self.internal_dirname = None + # return + # + #os.chdir(self.internal_dirname) + #try: + # tar.extractall() + # if __debug__: + # print "m_main.py: extracted tarfile into", + # print self.internal_dirname + #except AttributeError: + # # python 2.4 tarfile module lacks of method extractall() + # directories = [] + # for tarinfo in tar: + # if tarinfo.isdir(): + # # Extract directory with a safe mode, so that + # # all files below can be extracted as well. + # try: + # os.makedirs(os.path.join('.', tarinfo.name), 0700) + # except EnvironmentError: + # pass + # directories.append(tarinfo) + # else: + # tar.extract(tarinfo, '.') + # + # # Reverse sort directories. + # directories.sort(lambda a, b: cmp(a.name, b.name)) + # directories.reverse() + # + # # Set correct owner, mtime and filemode on directories. + # for tarinfo in directories: + # try: + # os.chown(os.path.join('.', tarinfo.name), + # tarinfo.uid, tarinfo.gid) + # os.utime(os.path.join('.', tarinfo.name), + # (0, tarinfo.mtime)) + # except OSError: + # if __debug__: + # print "m_main.py: open(): setting corrext owner,", + # print "mtime etc" + #tar.close() self.__connect_to_db() self.__fetch_db_into_treestore() @@ -858,9 +946,9 @@ class MainModel(ModelMT): """get file info from database""" retval = {} sql = """SELECT f.filename, f.date, f.size, f.type, - t.filename, f.description, f.note + f.description, f.note, t.filename FROM files f - LEFT JOIN thumbnails t ON t.file_id = f.id + LEFT JOIN thumbnails t on f.id = t.file_id WHERE f.id = ?""" self.db_cursor.execute(sql, (file_id,)) res = self.db_cursor.fetchone() @@ -868,31 +956,37 @@ class MainModel(ModelMT): retval['fileinfo'] = {'id': file_id, 'date': datetime.fromtimestamp(res[1]), 'size': res[2], 'type': res[3]} - + retval['fileinfo']['disc'] = self.__get_file_root(file_id) retval['filename'] = res[0] + if res[4]: + retval['description'] = res[4] + if res[5]: - retval['description'] = res[5] + retval['note'] = res[5] if res[6]: - retval['note'] = res[6] + thumbfile = os.path.join(self.image_path, res[6] + "_t") + if os.path.exists(thumbfile): + pix = gtk.gdk.pixbuf_new_from_file(thumbfile) + retval['thumbnail'] = thumbfile - if res[4]: - path = os.path.join(self.internal_dirname, res[4]) - retval['thumbnail'] = path - - sql = """SELECT id, thumbnail FROM images + sql = """SELECT id, filename FROM images WHERE file_id = ?""" self.db_cursor.execute(sql, (file_id,)) res = self.db_cursor.fetchall() if res: self.images_store = gtk.ListStore(gobject.TYPE_INT, gtk.gdk.Pixbuf) - for idi, thb in res: - img = os.path.join(self.internal_dirname, thb) - pix = gtk.gdk.pixbuf_new_from_file(img) - self.images_store.append([idi, pix]) + for im_id, filename in res: + thumbfile = os.path.join(self.image_path, filename + "_t") + if os.path.exists(thumbfile): + pix = gtk.gdk.pixbuf_new_from_file(thumbfile) + else: + pix = gtk.gdk.pixbuf_new_from_inline(len(no_thumb_img), + no_thumb_img, False) + self.images_store.append([im_id, pix]) retval['images'] = True sql = """SELECT camera, date, aperture, exposure_program, @@ -998,23 +1092,23 @@ class MainModel(ModelMT): arg = str(tuple(fids)) # remove thumbnails - sql = """SELECT filename FROM thumbnails WHERE file_id IN %s""" % arg - db_cursor.execute(sql) - res = db_cursor.fetchall() - if len(res) > 0: - for row in res: - os.unlink(os.path.join(self.internal_dirname, row[0])) + #sql = """SELECT filename FROM thumbnails WHERE file_id IN %s""" % arg + #db_cursor.execute(sql) + #res = db_cursor.fetchall() + #if len(res) > 0: + # for row in res: + # os.unlink(os.path.join(self.image_path, row[0])) # remove images - sql = """SELECT filename, thumbnail FROM images - WHERE file_id IN %s""" % arg - db_cursor.execute(sql) - res = db_cursor.fetchall() - if len(res) > 0: - for row in res: - if row[0]: - os.unlink(os.path.join(self.internal_dirname, row[0])) - os.unlink(os.path.join(self.internal_dirname, row[1])) + #sql = """SELECT filename, thumbnail FROM images + # WHERE file_id IN %s""" % arg + #db_cursor.execute(sql) + #res = db_cursor.fetchall() + #if len(res) > 0: + # for row in res: + # if row[0]: + # os.unlink(os.path.join(self.internal_dirname, row[0])) + # os.unlink(os.path.join(self.internal_dirname, row[1])) # remove thumbs records sql = """DELETE FROM thumbnails WHERE file_id = ?""" @@ -1053,7 +1147,7 @@ class MainModel(ModelMT): parent_id = False db_connection.commit() - + # part two: remove items from treestore/liststores def foreach_treestore(model, path, iterator, d): if d[0] == model.get_value(iterator, 0): @@ -1160,9 +1254,10 @@ class MainModel(ModelMT): sql = """SELECT filename FROM images WHERE id=?""" self.db_cursor.execute(sql, (img_id,)) res = self.db_cursor.fetchone() - if res: - if res[0]: - return os.path.join(self.internal_dirname, res[0]) + if res and res[0]: + path = os.path.join(self.image_path, res[0]) + if os.path.exists(path): + return path return None def update_desc_and_note(self, file_id, desc='', note=''): @@ -1278,14 +1373,14 @@ class MainModel(ModelMT): def __connect_to_db(self): """initialize db connection and store it in class attributes""" - self.db_connection = sqlite.connect("%s" % \ - (self.internal_dirname + '/db.sqlite'), + self.db_connection = sqlite.connect(self.db_tmp_path, detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES) self.db_cursor = self.db_connection.cursor() return def __close_db_connection(self): """close db conection""" + if self.db_cursor != None: self.db_cursor.close() self.db_cursor = None @@ -1294,42 +1389,66 @@ class MainModel(ModelMT): self.db_connection = None return - def __create_internal_dirname(self): - """create temporary directory for working thumb/image files and - database""" - # TODO: change this stupid rutine into tempfile mkdtemp method + def __create_temporary_db_file(self): + """create temporary db file""" self.cleanup() - self.internal_dirname = "/tmp/pygtktalog%d" % \ - datetime.now().microsecond - try: - os.mkdir(self.internal_dirname) - except IOError, (errno, strerror): - print "m_main.py: __create_internal_dirname(): ", strerror + self.db_tmp_path = mkstemp()[1] return def __compress_and_save(self): """create (and optionaly compress) tar archive from working directory and write it to specified file""" + + # flush all changes + self.db_connection.commit() + try: if self.config.confd['compress']: - tar = tarfile.open(self.filename, "w:gz") + output_file = bz2.BZ2File(self.filename, "w") else: - tar = tarfile.open(self.filename, "w") + output_file = open(self.filename, "w") if __debug__: print "m_main.py: __compress_and_save(): tar open successed" except IOError, (errno, strerror): return False, strerror - os.chdir(self.internal_dirname) - tar.add('.') - tar.close() + dbpath = open(self.db_tmp_path) + output_file.write(dbpath.read()) + dbpath.close() + output_file.close() self.unsaved_project = False return True, None def __create_database(self): - """make all necessary tables in db file""" + """make all necessary tables in db file + + + ,------------. ,------------. + |files | |tags | + +------------+ +------------+ + |→|pk id | |pk id | + |_|fk parent_id| |fk group_id | + |filename | |tag | + |filepath | +------------+ + |date | + |size | ,------------ + |source | |tags_files + |note | | + |description | | + +------------+ | + + + + + + + + + + + """ self.db_cursor.execute("""create table files(id INTEGER PRIMARY KEY AUTOINCREMENT, parent_id INTEGER, @@ -1359,7 +1478,6 @@ class MainModel(ModelMT): self.db_cursor.execute("""create table images(id INTEGER PRIMARY KEY AUTOINCREMENT, file_id INTEGER, - thumbnail TEXT, filename TEXT);""") self.db_cursor.execute("""create table exif(id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -1463,8 +1581,7 @@ class MainModel(ModelMT): self.busy = True # new conection for this task, because it's running in separate thread - db_connection = sqlite.connect("%s" % \ - (self.internal_dirname + '/db.sqlite'), + db_connection = sqlite.connect(self.db_tmp_path, detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES, isolation_level="EXCLUSIVE") db_cursor = db_connection.cursor() @@ -1627,15 +1744,13 @@ class MainModel(ModelMT): # Images - thumbnails and exif data if self.config.confd['thumbs'] and ext in self.IMG: - t = Thumbnail(current_file, - base=self.internal_dirname) - tpath, exif, ret_code = t.save(fileid) - if ret_code != -1: + thumb = Thumbnail(current_file, self.image_path) + th, exif = thumb.save() + if th: sql = """INSERT INTO thumbnails(file_id, filename) VALUES(?, ?)""" - t = tpath.split(self.internal_dirname)[1][1:] - db_cursor.execute(sql, (fileid, t)) + db_cursor.execute(sql, (fileid, th)) # exif - store data in exif table jpg = ['jpg', 'jpeg'] @@ -1750,8 +1865,7 @@ class MainModel(ModelMT): #connect detect_types = sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES - db_connection = sqlite.connect("%s" % \ - (self.internal_dirname + '/db.sqlite'), + db_connection = sqlite.connect(self.db_tmp_path, detect_types = detect_types) db_cursor = db_connection.cursor() @@ -1803,8 +1917,7 @@ class MainModel(ModelMT): """append branch from DB to existing tree model""" #connect detect_types = sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES - db_connection = sqlite.connect("%s" % - (self.internal_dirname + '/db.sqlite'), + db_connection = sqlite.connect(self.db_tmp_path, detect_types = detect_types) db_cursor = db_connection.cursor() diff --git a/src/views/v_dialogs.py b/src/views/v_dialogs.py index 99ab18d..54f1817 100644 --- a/src/views/v_dialogs.py +++ b/src/views/v_dialogs.py @@ -280,8 +280,8 @@ class ChooseDBFilename(object): f = gtk.FileFilter() f.set_name("Catalog files") - f.add_pattern("*.pgt") - f.add_pattern("*.pgt.tgz") + f.add_pattern("*.sqlite") + f.add_pattern("*.sqlite.bz2") self.dialog.add_filter(f) f = gtk.FileFilter() f.set_name("All files") @@ -299,13 +299,9 @@ class ChooseDBFilename(object): if response == gtk.RESPONSE_OK: filename = self.dialog.get_filename() print filename, ' do ', - if filename[-8:].lower() != '.pgt.tgz' and \ - filename[-4:].lower() != '.pgt': - filename = filename + '.pgt.tgz' - elif filename[-4:].lower() == '.pgt': - filename = filename[:-4] + '.pgt.tgz' - else: - filename = filename[:-8] + '.pgt.tgz' + if filename[-11:].lower() != '.sqlite.bz2' and \ + filename[-7:].lower() != '.sqlite': + filename = filename + '.sqlite.bz2' print filename self.__class__.URI = self.dialog.get_current_folder_uri() self.dialog.destroy() @@ -323,7 +319,7 @@ class LoadDBFile(object): def __init__(self, path=None): self.path = path - + self.dialog = gtk.FileChooserDialog( title="Open catalog", action=gtk.FILE_CHOOSER_ACTION_OPEN, @@ -334,11 +330,11 @@ class LoadDBFile(object): gtk.RESPONSE_OK)) self.dialog.set_default_response(gtk.RESPONSE_OK) - + f = gtk.FileFilter() f.set_name("Catalog files") - f.add_pattern("*.pgt") - f.add_pattern("*.pgt.tgz") + f.add_pattern("*.sqlite") + f.add_pattern("*.sqlite.bz2") self.dialog.add_filter(f) f = gtk.FileFilter() f.set_name("All files")