1
0
mirror of https://github.com/gryf/pygtktalog.git synced 2025-12-18 20:10:24 +01:00

Adapt GUI legacy version accept new databases

This commit is contained in:
2015-01-08 21:08:13 +01:00
parent 1424ce9c86
commit 5c1ea2e590
7 changed files with 132 additions and 205 deletions

33
README
View File

@@ -4,15 +4,15 @@ pyGTKtalog 1.0
pyGTKtalog is Linux/FreeBSD program for indexing CD/DVD or directories on
filesystem. It is similar to gtktalog <http://www.nongnu.org/gtktalog/> or
gwhere <http://www.gwhere.org/home.php3>. There is no coincidence in name of
application, because it's ment to be replacement (in some way) for gtktalog,
application, because it's meant to be replacement (in some way) for gtktalog,
which seems to be dead project for years.
FEATURES
========
- scan for files in selected media
- get/generate thumbnails from exif and other images
- most important exif tags
- get/generate thumbnails from EXIF and other images
- most important EXIF tags
- add/edit description and notes
- fetch comments for images made in gThumb <http://gthumb.sourceforge.net>
- add/remove unlimited images to any file or directory
@@ -30,19 +30,20 @@ pyGTKtalog is written in python with following dependencies:
Optional modules:
- PIL <http://www.pythonware.com/products/pil/index.htm> for image manipulation
- PIL <http://www.pythonware.com/products/pil/index.htm> for image
manipulation
Additional pyGTKtalog uses pygtkmvc <http://pygtkmvc.sourceforge.net> by Roberto
Cavada and EXIF module by Gene Cash (slightly updatetd to EXIF 2.2 by me) which
are included in sources.
Additional pyGTKtalog uses pygtkmvc <http://pygtkmvc.sourceforge.net> by
Roberto Cavada and EXIF module by Gene Cash (slightly updatetd to EXIF 2.2 by
me) which are included in sources.
pyGTKtalog extensivly uses external programs in unix spirit, however there is
small possibility of using it Windows (probably with limitations) and quite big
possiblity to run it on other sofisticated unix-like systems (i.e.
pyGTKtalog extensively uses external programs in unix spirit, however there is
small possibility of using it Windows (probably with limitations) and quite
big possibility to run it on other sophisticated unix-like systems (i.e.
BeOS/ZETA/Haiku, QNX or MacOSX).
INSTALATION
===========
INSTALLATION
============
You don't have to install it if you don't want to. You can just change current
directory to pyGTKtalog and simply run:
@@ -80,11 +81,12 @@ For version 2.0:
- command line support: query, adding media to collection etc
- internationalization
- export to XLS
- user definied group of tags (represented by color in cloud tag)
- hiding specified files - configurable, like dot prefixed, cfg and manualy
selected
- user defined group of tags (represented by color in cloud tag)
- hiding specified files - configurable, like dot prefixed, config files and
manually selected
- tests
- warning about existing image in media directory
Removed:
- filetypes handling (movies, images, archives, documents etc). Now it have
common, unified external "plugin" system - simple text output from command
@@ -126,4 +128,3 @@ BUGS
====
All bugs please report to Roman 'gryf' Dobosz <roman.dobosz@gmail.com>

View File

@@ -27,7 +27,7 @@ __version__ = "1.0.1"
LICENCE = ''
import os.path
from os import popen
from utils import deviceHelper
from utils import device_helper
from gtkmvc import Controller
from c_config import ConfigController
@@ -526,9 +526,9 @@ class MainController(Controller):
def on_add_cd_activate(self, widget, label=None, current_id=None):
"""Add directory structure from cd/dvd disc"""
mount = deviceHelper.volmount(self.model.config.confd['cd'])
mount = device_helper.volmount(self.model.config.confd['cd'])
if mount == 'ok':
guessed_label = deviceHelper.volname(self.model.config.confd['cd'])
guessed_label = device_helper.volname(self.model.config.confd['cd'])
if not label:
label = Dialogs.InputDiskLabel(guessed_label).run()
if label:
@@ -541,7 +541,7 @@ class MainController(Controller):
self.model.unsaved_project = True
self.__set_title(filepath=self.model.filename, modified=True)
else:
deviceHelper.volumount(self.model.config.confd['cd'])
device_helper.volumount(self.model.config.confd['cd'])
return True
else:
Dialogs.Wrn("Error mounting device - pyGTKtalog",
@@ -1373,7 +1373,7 @@ class MainController(Controller):
# umount/eject cd
ejectapp = self.model.config.confd['ejectapp']
if self.model.config.confd['eject'] and ejectapp:
msg = deviceHelper.eject_cd(ejectapp,
msg = device_helper.eject_cd(ejectapp,
self.model.config.confd['cd'])
if msg != 'ok':
Dialogs.Wrn("error ejecting device - pyGTKtalog",
@@ -1381,7 +1381,7 @@ class MainController(Controller):
self.model.config.confd['cd'],
"Last eject message:\n%s" % msg)
else:
msg = deviceHelper.volumount(self.model.config.confd['cd'])
msg = device_helper.volumount(self.model.config.confd['cd'])
if msg != 'ok':
Dialogs.Wrn("error unmounting device - pyGTKtalog",
"Cannot unmount device pointed to %s" %

View File

@@ -34,7 +34,11 @@ import gobject
from gtkmvc.model_mt import ModelMT
from pysqlite2 import dbapi2 as sqlite
try:
import sqlite3 as sqlite
except ImportError:
from pysqlite2 import dbapi2 as sqlite
from datetime import datetime
import threading as _threading
@@ -50,6 +54,20 @@ from utils.gthumb import GthumbCommentParser
from utils.no_thumb import no_thumb as no_thumb_img
def mangle_date(date):
"""Return date object depending on the record type."""
if date:
try:
dateobj = datetime.fromtimestamp(date)
except TypeError:
dateobj = datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f")
else:
dateobj = datetime.fromtimestamp(0)
return dateobj
class MainModel(ModelMT):
"""Create, load, save, manipulate db file which is container for data"""
@@ -169,7 +187,6 @@ class MainModel(ModelMT):
else:
os.mkdir(path)
if os.path.exists(imgpath):
if not os.path.isdir(imgpath):
print "Warning:",
@@ -360,8 +377,6 @@ class MainModel(ModelMT):
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
@@ -556,8 +571,6 @@ class MainModel(ModelMT):
try:
os.unlink(self.db_tmp_path)
except:
if __debug__:
print "Exception in removing temporary db file!"
pass
#if self.internal_dirname != None:
@@ -573,6 +586,7 @@ class MainModel(ModelMT):
self.filename = None
self.__create_temporary_db_file()
self.__connect_to_db()
self._set_image_path()
self.__create_database()
self.__clear_trees()
self.clear_search_tree()
@@ -592,6 +606,14 @@ class MainModel(ModelMT):
return
if filename:
if not '.sqlite' in filename:
filename += '.sqlite'
else:
filename = filename[:filename.rindex('.sqlite')] + '.sqlite'
if self.config.confd['compress']:
filename += '.bz2'
self.filename = filename
val, err = self.__compress_and_save()
if not val:
@@ -683,6 +705,7 @@ class MainModel(ModelMT):
#tar.close()
self.__connect_to_db()
self._set_image_path()
self.__fetch_db_into_treestore()
self.config.add_recent(filename)
self.get_tags()
@@ -758,8 +781,9 @@ class MainModel(ModelMT):
self.search_list.set_value(myiter, 3,
self.__get_file_path(row[0]))
self.search_list.set_value(myiter, 4, row[2])
self.search_list.set_value(myiter, 5,
datetime.fromtimestamp(row[3]))
self.search_list.set_value(myiter, 5, mangle_date(row[3]))
self.search_list.set_value(myiter, 6, 1)
self.search_list.set_value(myiter, 7, gtk.STOCK_DIRECTORY)
@@ -794,8 +818,7 @@ class MainModel(ModelMT):
self.search_list.set_value(myiter, 3,
self.__get_file_path(row[0]))
self.search_list.set_value(myiter, 4, row[2])
self.search_list.set_value(myiter, 5,
datetime.fromtimestamp(row[3]))
self.search_list.set_value(myiter, 5, mangle_date(row[3]))
self.search_list.set_value(myiter, 6, row[4])
if row[4] == self.FIL:
self.search_list.set_value(myiter, 7, gtk.STOCK_FILE)
@@ -825,9 +848,7 @@ class MainModel(ModelMT):
#self.__fetch_db_into_treestore()
self.unsaved_project = True
else:
if __debug__:
print "m_main.py: rename(): no label defined"
return
def refresh_discs_tree(self):
@@ -879,8 +900,7 @@ class MainModel(ModelMT):
self.files_list.set_value(myiter, 2, row[1])
self.files_list.set_value(myiter, 3, self.__get_file_path(row[0]))
self.files_list.set_value(myiter, 4, row[2])
self.files_list.set_value(myiter, 5,
datetime.fromtimestamp(row[3]))
self.files_list.set_value(myiter, 5, mangle_date(row[3]))
self.files_list.set_value(myiter, 6, 1)
self.files_list.set_value(myiter, 7, gtk.STOCK_DIRECTORY)
@@ -923,8 +943,7 @@ class MainModel(ModelMT):
self.files_list.set_value(myiter, 2, row[1])
self.files_list.set_value(myiter, 3, self.__get_file_path(row[0]))
self.files_list.set_value(myiter, 4, row[2])
self.files_list.set_value(myiter, 5,
datetime.fromtimestamp(row[3]))
self.files_list.set_value(myiter, 5, mangle_date(row[3]))
self.files_list.set_value(myiter, 6, row[4])
if row[4] == self.FIL:
self.files_list.set_value(myiter, 7, gtk.STOCK_FILE)
@@ -954,11 +973,10 @@ class MainModel(ModelMT):
res = self.db_cursor.fetchone()
if res:
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)
'size': res[2],
'type': res[3],
'date': mangle_date(res[1]),
'disc': self.__get_file_root(file_id)}
retval['filename'] = res[0]
if res[4]:
@@ -969,9 +987,13 @@ class MainModel(ModelMT):
if res[6]:
thumbfile = os.path.join(self.image_path, res[6] + "_t")
thumb2 = os.path.join(self.image_path, res[6])
if os.path.exists(thumbfile):
pix = gtk.gdk.pixbuf_new_from_file(thumbfile)
retval['thumbnail'] = thumbfile
elif os.path.exists(thumb2):
pix = gtk.gdk.pixbuf_new_from_file(thumb2)
retval['thumbnail'] = thumb2
sql = """SELECT id, filename FROM images
WHERE file_id = ?"""
@@ -981,8 +1003,13 @@ class MainModel(ModelMT):
self.images_store = gtk.ListStore(gobject.TYPE_INT, gtk.gdk.Pixbuf)
for im_id, filename in res:
thumbfile = os.path.join(self.image_path, filename + "_t")
file_, ext_ = os.path.splitext(filename)
thumb2 = os.path.join(self.image_path,
"".join([file_, "_t", ext_]))
if os.path.exists(thumbfile):
pix = gtk.gdk.pixbuf_new_from_file(thumbfile)
elif os.path.exists(thumb2):
pix = gtk.gdk.pixbuf_new_from_file(thumb2)
else:
pix = gtk.gdk.pixbuf_new_from_inline(len(no_thumb_img),
no_thumb_img, False)
@@ -1083,13 +1110,10 @@ class MainModel(ModelMT):
sql = """DELETE FROM tags_files WHERE file_id = ?"""
db_cursor.executemany(sql, generator())
if __debug__:
print "m_main.py: delete(): deleting:", fids
if len(fids) == 1:
arg = "(%d)" % fids[0]
else:
arg = str(tuple(fids))
#if len(fids) == 1:
# arg = "(%d)" % fids[0]
#else:
# arg = str(tuple(fids))
# remove thumbnails
#sql = """SELECT filename FROM thumbnails WHERE file_id IN %s""" % arg
@@ -1258,12 +1282,15 @@ class MainModel(ModelMT):
path = os.path.join(self.image_path, res[0])
if os.path.exists(path):
return path
path = os.path.join('/home/gryf/.pygtktalog/imgs2/', res[0])
if os.path.exists(path):
return path
return None
def update_desc_and_note(self, file_id, desc='', note=''):
"""update note and description"""
sql = """UPDATE files SET description=?, note=? WHERE id=?"""
self.db_cursor.execute(sql, (desc, note, file_id))
self.db_cursor.execute(sql, (unicode(desc), unicode(note), file_id))
self.db_connection.commit()
return
@@ -1378,6 +1405,34 @@ class MainModel(ModelMT):
self.db_cursor = self.db_connection.cursor()
return
def _set_image_path(self):
"""hack, hack, hack!"""
if not self.filename:
return
sql = ("select name from sqlite_master where name='config' and "
"type='table'")
if not self.db_cursor.execute(sql).fetchone():
return
sql = "SELECT value FROM config WHERE key = ?"
res = self.db_cursor.execute(sql, ("image_path", )).fetchone()
if not res:
return
if res[0] == ":same_as_db:":
dir_, file_ = (os.path.dirname(self.filename),
os.path.basename(self.filename))
file_base, dummy = os.path.splitext(file_)
self.image_path = os.path.abspath(os.path.join(dir_, file_base +
"_images"))
else:
self.image_path = res[0]
if "~" in self.image_path:
self.images_dir = os.path.expanduser(self.image_path)
if "$" in self.image_path:
self.images_dir = os.path.expandvars(self.image_path)
def __close_db_connection(self):
"""close db conection"""
@@ -1407,8 +1462,6 @@ class MainModel(ModelMT):
output_file = bz2.BZ2File(self.filename, "w")
else:
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
@@ -1597,8 +1650,6 @@ class MainModel(ModelMT):
for root, dirs, files in os.walk(self.path):
count += len(files)
except:
if __debug__:
print 'm_main.py: os.walk in %s' % self.path
pass
if count > 0:
@@ -1630,14 +1681,14 @@ class MainModel(ModelMT):
files(parent_id, filename, filepath, date,
size, type, source)
VALUES(?,?,?,?,?,?,?)"""
db_cursor.execute(sql, (parent_id, name, path, date, size,
filetype, self.source))
db_cursor.execute(sql, (parent_id, name, path.decode("utf-8"),
date, size, filetype, self.source))
else:
self.discs_tree.set_value(myit, 2, gtk.STOCK_DIRECTORY)
sql = """INSERT INTO
files(parent_id, filename, filepath, date, size, type)
VALUES(?,?,?,?,?,?)"""
db_cursor.execute(sql, (parent_id, name, path,
db_cursor.execute(sql, (parent_id, name, path.decode("utf-8"),
date, size, filetype))
sql = """SELECT seq FROM sqlite_sequence WHERE name='files'"""
@@ -1645,14 +1696,13 @@ class MainModel(ModelMT):
currentid = db_cursor.fetchone()[0]
self.discs_tree.set_value(myit, 0, currentid)
self.discs_tree.set_value(myit, 1, name)
self.discs_tree.set_value(myit, 3, parent_id)
try:
root, dirs, files = os.walk(path).next()
except:
if __debug__:
print "m_main.py: cannot access ", path
#return -1
return 0
@@ -1721,8 +1771,12 @@ class MainModel(ModelMT):
sql = """INSERT INTO
files(parent_id, filename, filepath, date, size, type)
VALUES(?,?,?,?,?,?)"""
db_cursor.execute(sql, (currentid, j, current_file,
st_mtime, st_size, self.FIL))
try:
db_cursor.execute(sql, (currentid, unicode(j),
unicode(current_file),
st_mtime, st_size, self.FIL))
except:
raise
if self.count % 32 == 0:
update = True
@@ -1812,7 +1866,10 @@ class MainModel(ModelMT):
sql = """UPDATE files SET description=?
WHERE id=?"""
db_cursor.execute(sql, (desc, fileid))
db_cursor.execute(sql,
(unicode(desc.decode("utf8",
"ignore")),
fileid))
### end of scan
if update:
@@ -1827,18 +1884,11 @@ class MainModel(ModelMT):
return _size
if __recurse(1, self.label, self.path, 0, 0, self.DIR) == -1:
if __debug__:
print "m_main.py: __scan() __recurse()",
print "interrupted self.abort = True"
self.discs_tree.remove(self.fresh_disk_iter)
db_cursor.close()
db_connection.rollback()
else:
if __debug__:
print "m_main.py: __scan() __recurse() goes without interrupt"
if self.currentid:
if __debug__:
print "m_main.py: __scan() removing old branch"
self.statusmsg = "Removing old branch..."
self.delete(self.currentid, db_cursor, db_connection)
@@ -1847,8 +1897,6 @@ class MainModel(ModelMT):
db_cursor.close()
db_connection.commit()
db_connection.close()
if __debug__:
print "m_main.py: __scan() time: ", (datetime.now() - timestamp)
self.busy = False
@@ -1903,13 +1951,8 @@ class MainModel(ModelMT):
gtk.STOCK_DIRECTORY)
return
if __debug__:
start_date = datetime.now()
# launch scanning.
get_children()
if __debug__:
print "m_main.py: __fetch_db_into_treestore()",
print "tree generation time: ", (datetime.now() - start_date)
db_connection.close()
return
@@ -1944,13 +1987,8 @@ class MainModel(ModelMT):
gtk.STOCK_DIRECTORY)
return
if __debug__:
start_date = datetime.now()
# launch scanning.
get_children()
if __debug__:
print "m_main.py: __append_added_volume() tree generation time: ",
print datetime.now() - start_date
db_connection.close()
return

View File

@@ -1,112 +0,0 @@
# This Python file uses the following encoding: utf-8
#
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
#
# Copyright (C) 2007 by Roman 'gryf' Dobosz
#
# This file is part of pyGTKtalog.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# -------------------------------------------------------------------------
"""
device (cd, dvd) helper
"""
import os
def volname(mntp):
"""read volume name from cd/dvd"""
dev = mountpoint_to_dev(mntp)
if dev != None:
try:
a = open(dev,"rb")
a.seek(32808)
b = a.read(32).strip()
a.close()
except:
return None
return b
return None
def volmount(mntp):
"""mount device, return 'ok' or error message"""
_in,_out,_err = os.popen3("mount %s" % mntp)
inf = _err.readlines()
if len(inf) > 0:
for i in inf:
i.strip()
return i
else:
return 'ok'
def volumount(mntp):
"""mount device, return 'ok' or error message"""
_in,_out,_err = os.popen3("umount %s" % mntp)
inf = _err.readlines()
if len(inf) > 0:
for error in inf:
error.strip()
if error.strip()[:7] == 'umount:':
return error.strip()
return 'ok'
def check_mount(dev):
"""Refresh the entries from fstab or mount."""
mounts = os.popen('mount')
for line in mounts.readlines():
parts = line.split()
device, txt1, mount_point, txt2, filesystem, options = parts
if device == dev:
return True
return False
def mountpoint_to_dev(mntp):
"""guess mountpoint from fstab"""
fstab = open("/etc/fstab")
for line in fstab.readlines():
a = line.split()
try:
if a[1] == mntp and a[0][0] != '#':
fstab.close()
return a[0]
except:
pass
fstab.close()
return None
def eject_cd(eject_app, cd):
"""mount device, return 'ok' or error message"""
if len(eject_app) > 0:
_in,_out,_err = os.popen3("%s %s" % (eject_app, cd))
inf = _err.readlines()
error = ''
for error in inf:
error.strip()
if error !='':
return error
return 'ok'
return "Eject program not specified"

View File

@@ -26,7 +26,8 @@ from shutil import copy
from os import path
from hashlib import sha512
import Image
from PIL import Image
class Img(object):

View File

@@ -29,7 +29,7 @@ from os import path
import sys
from utils import EXIF
import Image
from PIL import Image
class Thumbnail(object):
"""Class for generate/extract thumbnail from image file"""
@@ -56,7 +56,7 @@ class Thumbnail(object):
# rotated 90 CW
8: Image.ROTATE_90} # Rotated 90 CCW
flips = {7: Image.FLIP_LEFT_RIGHT, 5: Image.FLIP_LEFT_RIGHT}
image_file = open(self.filename, 'rb')
try:
exif = EXIF.process_file(image_file)
@@ -71,7 +71,7 @@ class Thumbnail(object):
if __debug__:
print "file", self.filename, "with hash", self.sha512, "exists"
return self.sha512, exif
if 'JPEGThumbnail' in exif:
if __debug__:
print self.filename, "exif thumb"
@@ -79,15 +79,15 @@ class Thumbnail(object):
thumb_file = open(self.thumbnail_path, 'wb')
thumb_file.write(exif_thumbnail)
thumb_file.close()
if 'Image Orientation' in exif:
orient = exif['Image Orientation'].values[0]
if orient > 1 and orient in orientations:
temp_image_path = mkstemp()[1]
thumb_image = Image.open(self.thumbnail_path)
tmp_thumb_img = thumb_image.transpose(orientations[orient])
if orient in flips:
tmp_thumb_img = tmp_thumb_img.transpose(flips[orient])

View File

@@ -34,9 +34,8 @@ class ImageView(object):
image = gtk.Image()
image.set_from_file(image_filename)
pixbuf = image.get_pixbuf()
pic_width = pixbuf.get_width() + 23
pic_height = pixbuf.get_height() + 23
pic_width = image.size_request()[0] + 23
pic_height = image.size_request()[1] + 23
screen_width = gtk.gdk.screen_width()
screen_height = gtk.gdk.screen_height()