mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 11:30:19 +01:00
* Finally, version 1.0.
This commit is contained in:
10
README
10
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
|
||||
====
|
||||
|
||||
|
||||
@@ -179,17 +179,6 @@
|
||||
<signal name="activate" handler="on_del_all_images_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="del_all_images_thumb">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="tooltip" translatable="yes">Deletes all images from files in current colection.
|
||||
Thumbnails from image tabs will be keep.</property>
|
||||
<property name="label" translatable="yes">Delete all images without thumbnals</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_del_all_images_thumb_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="del_all_thumb">
|
||||
<property name="visible">True</property>
|
||||
@@ -199,6 +188,14 @@ Thumbnails from image tabs will be keep.</property>
|
||||
<signal name="activate" handler="on_del_all_thumb_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="save_all_img">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Save all images...</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_save_all_img_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separator12">
|
||||
<property name="visible">True</property>
|
||||
@@ -1007,20 +1004,18 @@ thumbnail from first image will be generated.</property>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="img_delete">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="label" translatable="yes">_Delete images</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_img_delete_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="img_delete2">
|
||||
<widget class="GtkMenuItem" id="img_thumbset">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="tooltip" translatable="yes">Delete selected images, but leave thumbnails</property>
|
||||
<property name="label" translatable="yes">De_lete images (keep thumbs)</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="label" translatable="yes">Set as _thumbnail</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_img_delete2_activate"/>
|
||||
<signal name="activate" handler="on_img_thumbset_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
|
||||
@@ -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()
|
||||
@@ -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 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():
|
||||
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
|
||||
|
||||
model = self.view['images'].get_model()
|
||||
for path in list_of_paths:
|
||||
iter = model.get_iter(path)
|
||||
iter = model.get_iter(list_of_paths[0])
|
||||
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
|
||||
self.model.set_image_as_thumbnail(id)
|
||||
|
||||
self.model.unsaved_project = True
|
||||
self.__set_title(filepath=self.model.filename, modified=True)
|
||||
@@ -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'
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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)"""
|
||||
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
|
||||
|
||||
# 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()
|
||||
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()
|
||||
|
||||
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=?"""
|
||||
self.db_cursor.execute(sql, (file_id,))
|
||||
res = self.db_cursor.fetchone()
|
||||
if not res:
|
||||
self.add_thumbnail(image, file_id)
|
||||
## 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))
|
||||
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:
|
||||
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:
|
||||
try:
|
||||
tar = tarfile.open(filename, "r")
|
||||
except:
|
||||
test_file = open(filename).read(15)
|
||||
except IOError:
|
||||
self.filename = None
|
||||
self.internal_dirname = None
|
||||
return
|
||||
return False
|
||||
|
||||
os.chdir(self.internal_dirname)
|
||||
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.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)
|
||||
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 False
|
||||
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.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()
|
||||
@@ -873,26 +961,32 @@ class MainModel(ModelMT):
|
||||
|
||||
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 = ?"""
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -337,8 +333,8 @@ class LoadDBFile(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")
|
||||
|
||||
Reference in New Issue
Block a user