mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 11:30:19 +01:00
* Added exteranl image viewer.
* Added edit for descriptions and notes for files. * Added possibility for make thumbnail for any directory/file. * More improvements and bufixes.
This commit is contained in:
32
README
32
README
@@ -10,9 +10,14 @@ which seems to be dead project for years.
|
||||
FEATURES
|
||||
========
|
||||
|
||||
- scanning for files in selected media
|
||||
- generating thumbnails
|
||||
- scan for files in selected media
|
||||
- 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
|
||||
- tagging files <http://en.wikipedia.org/wiki/Tag_%28metadata%29>
|
||||
- and more :)
|
||||
|
||||
REQUIREMENTS
|
||||
============
|
||||
@@ -57,13 +62,13 @@ For version 1.0 following aims have to be done:
|
||||
- searching database
|
||||
- tagging files
|
||||
- user definied group of tags (represented by color in cloud tag)
|
||||
- file details:
|
||||
- files properties
|
||||
x file details:
|
||||
x files properties
|
||||
x thumbnail
|
||||
x description
|
||||
x edit note and description
|
||||
x exif information
|
||||
- keywords (tags)
|
||||
- gthumb integration
|
||||
x gthumb integration
|
||||
x adding images
|
||||
x generating/saving thumbnails
|
||||
x moving hardcoded files extensions into config
|
||||
@@ -75,11 +80,12 @@ For version 2.0:
|
||||
- Icon grid in files view
|
||||
- command line support: query, adding media to collection etc
|
||||
- internationalization support
|
||||
- export to XLS
|
||||
|
||||
Removed:
|
||||
- filetypes handling (movies, images, archives, documents etc). Now it have
|
||||
common, unified external "plugin" system - simple output from command line
|
||||
programs.
|
||||
common, unified external "plugin" system - simple text output from command
|
||||
line programs.
|
||||
- anime/movie
|
||||
- title
|
||||
- alt title
|
||||
@@ -90,16 +96,16 @@ Removed:
|
||||
- sub lang
|
||||
- release date (from - to)
|
||||
- anidb link/imdb link
|
||||
Maybe in future versions. Now text file descriptions and tags have to be
|
||||
enough for good and fast information search.
|
||||
Maybe in future versions. Now text file descriptions/notes and tags have to
|
||||
be enough for good and fast information search.
|
||||
- file information (date, size, etc) (50%) (no need for?)
|
||||
|
||||
NOTES
|
||||
=====
|
||||
|
||||
Catalog file is tared and optionaly compressed sqlite database and directory
|
||||
with thumbnails. If there are more images, the size of catalog file will grow.
|
||||
So be carefull with adding big images in your catalog file!
|
||||
Catalog file is tared and optionaly compressed 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!
|
||||
|
||||
BUGS
|
||||
====
|
||||
|
||||
@@ -59,6 +59,8 @@ class ConfigController(Controller):
|
||||
self.view['ch_gthumb'].set_active(self.model.confd['gthumb'])
|
||||
self.view['ch_compress'].set_active(self.model.confd['compress'])
|
||||
self.view['ch_retrive'].set_active(self.model.confd['retrive'])
|
||||
self.view['ch_imageviewer'].set_active(self.model.confd['imgview'])
|
||||
self.view['entry_imv'].set_text(self.model.confd['imgprog'])
|
||||
|
||||
self.__toggle_scan_group()
|
||||
|
||||
@@ -118,21 +120,30 @@ class ConfigController(Controller):
|
||||
self.model.confd['gthumb'] = self.view['ch_gthumb'].get_active()
|
||||
self.model.confd['compress'] = self.view['ch_compress'].get_active()
|
||||
self.model.confd['retrive'] = self.view['ch_retrive'].get_active()
|
||||
self.model.confd['imgview'] = self.view['ch_imageviewer'].get_active()
|
||||
self.model.confd['imgprog'] = self.view['entry_imv'].get_text()
|
||||
self.model.save()
|
||||
self.view['config'].destroy()
|
||||
return
|
||||
|
||||
def on_button_ejt_clicked(self, button):
|
||||
self.__show_filechooser()
|
||||
return
|
||||
fn = self.__show_filechooser("Choose eject program")
|
||||
self.view['ejt_entryentry_imv'].set_text(fn)
|
||||
|
||||
def on_button_mnt_clicked(self, button):
|
||||
self.__show_dirchooser()
|
||||
return
|
||||
fn = self.__show_filechooser("Choose mount point")
|
||||
self.view['mnt_entry'].set_text(fn)
|
||||
|
||||
def on_ch_retrive_toggled(self, widget):
|
||||
self.__toggle_scan_group()
|
||||
return
|
||||
|
||||
def on_ch_imageviewer_toggled(self, checkbox):
|
||||
state = self.view['ch_imageviewer'].get_active()
|
||||
for i in ['label_imv', 'entry_imv', 'button_imv']:
|
||||
self.view[i].set_sensitive(state)
|
||||
|
||||
def on_button_imv_clicked(self, widget):
|
||||
fn = self.__show_filechooser("Choose image viewer")
|
||||
self.view['entry_imv'].set_text(fn)
|
||||
|
||||
def on_ext_add_clicked(self, widget):
|
||||
ext = self.view['ext_entry'].get_text().lower()
|
||||
@@ -220,10 +231,11 @@ class ConfigController(Controller):
|
||||
column.set_resizable(True)
|
||||
category_tree.append_column(column)
|
||||
|
||||
def __show_filechooser(self):
|
||||
def __show_filechooser(self, title):
|
||||
"""dialog for choose eject"""
|
||||
fn = None
|
||||
dialog = gtk.FileChooserDialog(
|
||||
title="Choose eject program",
|
||||
title=title,
|
||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
buttons=(
|
||||
gtk.STOCK_CANCEL,
|
||||
@@ -239,9 +251,9 @@ class ConfigController(Controller):
|
||||
if response == gtk.RESPONSE_OK:
|
||||
if __debug__:
|
||||
print "c_config.py: __show_filechooser()", dialog.get_filename()
|
||||
self.view['ejt_entry'].set_text(dialog.get_filename())
|
||||
|
||||
fn = dialog.get_filename()
|
||||
dialog.destroy()
|
||||
return fn
|
||||
|
||||
def __show_dirchooser(self):
|
||||
"""dialog for point the mountpoint"""
|
||||
|
||||
@@ -30,6 +30,7 @@ http://www.gnu.org/licenses/gpl.txt
|
||||
"""
|
||||
|
||||
import os.path
|
||||
from os import popen
|
||||
from utils import deviceHelper
|
||||
from gtkmvc import Controller
|
||||
|
||||
@@ -126,11 +127,83 @@ class MainController(Controller):
|
||||
|
||||
#########################################################################
|
||||
# Connect signals from GUI, like menu objects, toolbar buttons and so on.
|
||||
def on_edit2_activate(self, menu_item):
|
||||
try:
|
||||
selection = self.view['files'].get_selection()
|
||||
model, list_of_paths = selection.get_selected_rows()
|
||||
id = model.get_value(model.get_iter(list_of_paths[0]), 0)
|
||||
except TypeError:
|
||||
if __debug__: print "c_main.py: on_edit2_activate(): 0 zaznaczonych wierszy"
|
||||
return
|
||||
|
||||
val = self.model.get_file_info(id)
|
||||
ret = Dialogs.EditDialog(val).run()
|
||||
if ret:
|
||||
self.model.rename(id, ret['filename'])
|
||||
self.model.update_desc_and_note(id, ret['description'], ret['note'])
|
||||
self.__get_item_info(id)
|
||||
|
||||
def on_add_thumb1_activate(self, menu_item):
|
||||
image = Dialogs.LoadImageFile().run()
|
||||
if not image:
|
||||
return
|
||||
try:
|
||||
selection = self.view['files'].get_selection()
|
||||
model, list_of_paths = selection.get_selected_rows()
|
||||
for path in list_of_paths:
|
||||
id = model.get_value(model.get_iter(path),0)
|
||||
self.model.add_thumbnail(image, id)
|
||||
except:
|
||||
if __debug__: print "c_main.py: on_add_thumb1_activate(): error on getting selected items or creating thumbnails"
|
||||
return
|
||||
self.__get_item_info(id)
|
||||
return
|
||||
|
||||
def on_remove_thumb1_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
obj = Dialogs.Qst('Delete thumbnails', 'Delete thumbnails?',
|
||||
'Thumbnails for selected items will be permanently removed from catalog.')
|
||||
if not obj.run():
|
||||
return
|
||||
try:
|
||||
selection = self.view['files'].get_selection()
|
||||
model, list_of_paths = selection.get_selected_rows()
|
||||
for path in list_of_paths:
|
||||
id = model.get_value(model.get_iter(path),0)
|
||||
self.model.del_thumbnail(id)
|
||||
except:
|
||||
if __debug__: print "c_main.py: on_remove_thumb1_activate(): error on getting selected items or removing thumbnails"
|
||||
return
|
||||
self.__get_item_info(id)
|
||||
return
|
||||
|
||||
def on_remove_image1_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
obj = Dialogs.Qst('Delete images', 'Delete all images?',
|
||||
'All images for selected items will be permanently removed from catalog.')
|
||||
if not obj.run():
|
||||
return
|
||||
try:
|
||||
selection = self.view['files'].get_selection()
|
||||
model, list_of_paths = selection.get_selected_rows()
|
||||
for path in list_of_paths:
|
||||
id = model.get_value(model.get_iter(path),0)
|
||||
self.model.del_images(id)
|
||||
except:
|
||||
if __debug__: print "c_main.py: on_remove_thumb1_activate(): error on getting selected items or removing thumbnails"
|
||||
return
|
||||
self.__get_item_info(id)
|
||||
return
|
||||
|
||||
def on_images_item_activated(self, iconview, path):
|
||||
model = iconview.get_model()
|
||||
iter = model.get_iter(path)
|
||||
id = model.get_value(iter, 0)
|
||||
ImageView(self.model.get_image_path(id))
|
||||
img = self.model.get_image_path(id)
|
||||
if self.model.config.confd['imgview'] and len(self.model.config.confd['imgprog'])>0:
|
||||
popen("%s %s" % (self.model.config.confd['imgprog'], img))
|
||||
else:
|
||||
ImageView(img)
|
||||
|
||||
def on_rename1_activate(self, widget):
|
||||
model, iter = self.view['discs'].get_selection().get_selected()
|
||||
@@ -138,8 +211,7 @@ class MainController(Controller):
|
||||
id = model.get_value(iter, 0)
|
||||
new_name = Dialogs.InputNewName(name).run()
|
||||
|
||||
if __debug__:
|
||||
print "c_main.py: on_rename1_activate(): label:", new_name
|
||||
if __debug__: print "c_main.py: on_rename1_activate(): label:", new_name
|
||||
|
||||
if new_name != None and new_name != name:
|
||||
self.model.rename(id, new_name)
|
||||
@@ -159,8 +231,7 @@ class MainController(Controller):
|
||||
name = model.get_value(model.get_iter(list_of_paths[0]),1)
|
||||
|
||||
new_name = Dialogs.InputNewName(name).run()
|
||||
if __debug__:
|
||||
print "c_main.py: on_rename1_activate(): label:", new_name
|
||||
if __debug__: print "c_main.py: on_rename1_activate(): label:", new_name
|
||||
|
||||
if new_name != None and new_name != name:
|
||||
self.model.rename(fid, new_name)
|
||||
@@ -176,8 +247,7 @@ class MainController(Controller):
|
||||
return
|
||||
|
||||
def on_tag_cloud_textview_motion_notify_event(self, widget):
|
||||
if __debug__:
|
||||
print "c_main.py: on_tag_cloud_textview_motion_notify_event():"
|
||||
if __debug__: print "c_main.py: on_tag_cloud_textview_motion_notify_event():"
|
||||
w = self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT)
|
||||
if w:
|
||||
w.set_cursor(None)
|
||||
@@ -322,6 +392,10 @@ class MainController(Controller):
|
||||
def on_img_add_activate(self, menu_item):
|
||||
self.on_add_image1_activate(menu_item)
|
||||
|
||||
def on_thumb_box_button_press_event(self, widget, event):
|
||||
if event.button == 3:
|
||||
self.__popup_menu(event, 'th_popup')
|
||||
|
||||
def on_discs_button_press_event(self, treeview, event):
|
||||
try:
|
||||
path, column, x, y = treeview.get_path_at_pos(int(event.x),
|
||||
@@ -379,9 +453,11 @@ class MainController(Controller):
|
||||
if len(list_of_paths) > 1:
|
||||
self.view['add_image1'].set_sensitive(False)
|
||||
self.view['rename2'].set_sensitive(False)
|
||||
self.view['edit2'].set_sensitive(False)
|
||||
else:
|
||||
self.view['add_image1'].set_sensitive(True)
|
||||
self.view['rename2'].set_sensitive(True)
|
||||
self.view['edit2'].set_sensitive(True)
|
||||
self.__popup_menu(event, 'files_popup')
|
||||
return True
|
||||
|
||||
@@ -394,8 +470,7 @@ class MainController(Controller):
|
||||
selected_item = self.model.files_list.get_value(iter, 0)
|
||||
self.__get_item_info(selected_item)
|
||||
except:
|
||||
if __debug__:
|
||||
print "c_main.py: on_files_cursor_changed() insufficient iterator"
|
||||
if __debug__: print "c_main.py: on_files_cursor_changed() insufficient iterator"
|
||||
return
|
||||
|
||||
def on_files_key_release_event(self, a, event):
|
||||
@@ -470,9 +545,10 @@ class MainController(Controller):
|
||||
print self.view['files'].get_cursor()
|
||||
|
||||
def on_add_image1_activate(self, menu_item):
|
||||
images = Dialogs.LoadImageFile().run()
|
||||
images = Dialogs.LoadImageFile(True).run()
|
||||
if not images:
|
||||
return
|
||||
|
||||
for image in images:
|
||||
try:
|
||||
selection = self.view['files'].get_selection()
|
||||
@@ -561,7 +637,7 @@ class MainController(Controller):
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
obj = Dialogs.Qst('Delete elements', 'Delete items?',
|
||||
'Items will be permanently removed.')
|
||||
'Items will be permanently removed from catalog.')
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -590,11 +666,27 @@ class MainController(Controller):
|
||||
self.model.get_root_entries(model.get_value(model.get_iter(list_of_paths[0]),0))
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
buf = gtk.TextBuffer()
|
||||
self.view['description'].set_buffer(buf)
|
||||
self.view['thumb_box'].hide()
|
||||
self.view['exifinfo'].hide()
|
||||
self.view['img_container'].hide()
|
||||
|
||||
self.model.unsaved_project = True
|
||||
self.__set_title(filepath=self.model.filename, modified=True)
|
||||
return
|
||||
|
||||
|
||||
def on_th_delete_activate(self, menu_item):
|
||||
path, column = self.view['files'].get_cursor()
|
||||
model = self.view['files'].get_model()
|
||||
iter = model.get_iter(path)
|
||||
id = model.get_value(iter, 0)
|
||||
if id:
|
||||
self.model.del_thumbnail(id)
|
||||
self.__get_item_info(id)
|
||||
return
|
||||
|
||||
def on_debugbtn_clicked(self, widget):
|
||||
"""Debug. To remove in stable version, including button in GUI"""
|
||||
if __debug__:
|
||||
@@ -762,8 +854,6 @@ class MainController(Controller):
|
||||
buf = self.view['description'].get_buffer()
|
||||
buf.set_text("")
|
||||
self.view['description'].set_buffer(buf)
|
||||
self.view['thumb'].hide()
|
||||
|
||||
self.__activate_ui()
|
||||
|
||||
return
|
||||
@@ -903,18 +993,47 @@ class MainController(Controller):
|
||||
def __get_item_info(self, item):
|
||||
self.view['description'].show()
|
||||
set = self.model.get_file_info(item)
|
||||
buf = self.view['description'].get_buffer()
|
||||
buf = gtk.TextBuffer()
|
||||
|
||||
if set.has_key('file_info'):
|
||||
buf.set_text(set['file_info'])
|
||||
if set.has_key('description'):
|
||||
tag = buf.create_tag()
|
||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||
buf.insert_with_tags(buf.get_end_iter(), "\nDetails:\n", tag)
|
||||
buf.insert(buf.get_end_iter(), set['description'])
|
||||
else:
|
||||
buf.set_text('')
|
||||
if __debug__ and set.has_key('debug'):
|
||||
tag = buf.create_tag()
|
||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||
buf.insert_with_tags(buf.get_end_iter(), "ID: ", tag)
|
||||
buf.insert(buf.get_end_iter(), str(set['debug']['id']) + "\n")
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Filename: ", tag)
|
||||
buf.insert(buf.get_end_iter(), set['filename'] + "\n")
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Date: ", tag)
|
||||
buf.insert(buf.get_end_iter(), str(set['debug']['date']) + "\n")
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Size: ", tag)
|
||||
buf.insert(buf.get_end_iter(), str(set['debug']['size']) + "\n")
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Type: ", tag)
|
||||
buf.insert(buf.get_end_iter(), str(set['debug']['type']) + "\n\n")
|
||||
|
||||
if set.has_key('gthumb'):
|
||||
tag = buf.create_tag()
|
||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||
buf.insert_with_tags(buf.get_end_iter(), "gThumb comment:\n", tag)
|
||||
if set['gthumb']['note']:
|
||||
buf.insert(buf.get_end_iter(), set['gthumb']['note'] + "\n")
|
||||
if set['gthumb']['place']:
|
||||
buf.insert(buf.get_end_iter(), set['gthumb']['place'] + "\n")
|
||||
if set['gthumb']['date']:
|
||||
buf.insert(buf.get_end_iter(), set['gthumb']['date'] + "\n")
|
||||
buf.insert(buf.get_end_iter(), "\n")
|
||||
|
||||
if set.has_key('description'):
|
||||
tag = buf.create_tag()
|
||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Details:\n", tag)
|
||||
buf.insert(buf.get_end_iter(), set['description'])
|
||||
buf.insert(buf.get_end_iter(), "\n")
|
||||
|
||||
if set.has_key('note'):
|
||||
tag = buf.create_tag()
|
||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Note:\n", tag)
|
||||
buf.insert(buf.get_end_iter(), set['note'])
|
||||
|
||||
self.view['description'].set_buffer(buf)
|
||||
|
||||
if set.has_key('images'):
|
||||
@@ -931,9 +1050,11 @@ class MainController(Controller):
|
||||
|
||||
if set.has_key('thumbnail'):
|
||||
self.view['thumb'].set_from_file(set['thumbnail'])
|
||||
self.view['thumb'].show()
|
||||
self.view['thumb_box'].show()
|
||||
#self.view['thumb'].show()
|
||||
else:
|
||||
self.view['thumb'].hide()
|
||||
#self.view['thumb'].hide()
|
||||
self.view['thumb_box'].hide()
|
||||
return
|
||||
|
||||
def __tag_cloud(self):
|
||||
@@ -953,7 +1074,6 @@ class MainController(Controller):
|
||||
self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT)
|
||||
if w:
|
||||
w.set_cursor(None)
|
||||
|
||||
|
||||
def insert_blank(b, iter):
|
||||
if iter.is_end() and iter.is_start():
|
||||
|
||||
@@ -78,6 +78,9 @@ class ConfigModel(Model):
|
||||
'cd': '/mnt/cdrom',
|
||||
'ejectapp': 'eject -r',
|
||||
|
||||
'imgview': False,
|
||||
'imgprog': 'gqview',
|
||||
|
||||
'retrive': False,
|
||||
|
||||
'thumbs': True,
|
||||
@@ -122,6 +125,8 @@ class ConfigModel(Model):
|
||||
'retrive extra informatin':'retrive',
|
||||
'scan exif data':'exif',
|
||||
'include gthumb image description':'gthumb',
|
||||
'use external image viewer':'imgview',
|
||||
'external image viewer program':'imgprog',
|
||||
}
|
||||
|
||||
dbool = (
|
||||
@@ -141,12 +146,13 @@ class ConfigModel(Model):
|
||||
'delwarn',
|
||||
'compress',
|
||||
'retrive',
|
||||
'imgview',
|
||||
)
|
||||
|
||||
recent = []
|
||||
RECENT_MAX = 10
|
||||
|
||||
dstring = ('cd','ejectapp')
|
||||
dstring = ('cd','ejectapp','imgprog')
|
||||
|
||||
try:
|
||||
path = os.environ['HOME']
|
||||
|
||||
@@ -45,8 +45,11 @@ except ImportError:
|
||||
|
||||
from m_config import ConfigModel
|
||||
from m_details import DetailsModel
|
||||
from utils.thumbnail import Thumbnail
|
||||
from utils.img import Img
|
||||
try:
|
||||
from utils.thumbnail import Thumbnail
|
||||
from utils.img import Img
|
||||
except:
|
||||
pass
|
||||
from utils.parse_exif import ParseExif
|
||||
from utils.gthumb import GthumbCommentParser
|
||||
|
||||
@@ -131,7 +134,9 @@ class MainModel(ModelMT):
|
||||
{'id': str(10), 'name': "windows", 'size': 18, 'color': '#333'},
|
||||
]'''
|
||||
return
|
||||
|
||||
def add_image(self, image, id):
|
||||
"""add single image to file/directory"""
|
||||
sql = """insert into images(file_id, thumbnail, filename)
|
||||
values(?, null, null)"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
@@ -148,8 +153,72 @@ class MainModel(ModelMT):
|
||||
(ip.split(self.internal_dirname)[1][1:],
|
||||
tp.split(self.internal_dirname)[1][1:],
|
||||
res[0]))
|
||||
self.db_connection.commit()
|
||||
self.db_connection.commit()
|
||||
|
||||
def del_images(self, 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, (id,))
|
||||
res = self.db_cursor.fetchall()
|
||||
if len(res) > 0:
|
||||
for fn in res:
|
||||
os.unlink(os.path.join(self.internal_dirname, fn[0]))
|
||||
os.unlink(os.path.join(self.internal_dirname, fn[1]))
|
||||
|
||||
# remove images records
|
||||
sql = """delete from images where file_id = ?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
self.db_connection.commit()
|
||||
|
||||
def delete_image(self, id):
|
||||
"""removes image on specified image id"""
|
||||
sql = """select filename, thumbnail from images where id=?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
res = self.db_cursor.fetchone()
|
||||
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]
|
||||
# remove images records
|
||||
sql = """delete from images where id = ?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
self.db_connection.commit()
|
||||
|
||||
def add_thumbnail(self, img_fn, id):
|
||||
"""generate and add thumbnail to selected file/dir"""
|
||||
if self.config.confd['thumbs']:
|
||||
self.del_thumbnail(id)
|
||||
p, e, ret_code = Thumbnail(img_fn,
|
||||
base=self.internal_dirname).save(id)
|
||||
if ret_code != -1:
|
||||
sql = """insert into thumbnails(file_id, filename) values (?, ?)"""
|
||||
self.db_cursor.execute(sql,
|
||||
(id,
|
||||
p.split(self.internal_dirname)[1][1:]))
|
||||
self.db_connection.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
def del_thumbnail(self, id):
|
||||
"""removes thumbnail from selected file/dir"""
|
||||
|
||||
# remove thumbnail files
|
||||
sql = """select filename from thumbnails where file_id=?"""
|
||||
self.db_cursor.execute(sql, (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=?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
self.db_connection.commit()
|
||||
|
||||
def cleanup(self):
|
||||
self.__close_db_connection()
|
||||
if self.internal_dirname != None:
|
||||
@@ -202,7 +271,8 @@ class MainModel(ModelMT):
|
||||
os.chdir(self.internal_dirname)
|
||||
try:
|
||||
tar.extractall()
|
||||
print "m_main.py: extracted tarfile into", self.internal_dirname
|
||||
if __debug__:
|
||||
print "m_main.py: extracted tarfile into", self.internal_dirname
|
||||
except AttributeError:
|
||||
# python's 2.4 tarfile module lacks of method extractall()
|
||||
directories = []
|
||||
@@ -328,18 +398,24 @@ class MainModel(ModelMT):
|
||||
"""get file info from database"""
|
||||
retval = {}
|
||||
sql = """SELECT f.filename, f.date, f.size, f.type,
|
||||
t.filename, f.description
|
||||
t.filename, f.description, f.note
|
||||
FROM files f
|
||||
LEFT JOIN thumbnails t ON t.file_id = f.id
|
||||
WHERE f.id = ?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
set = self.db_cursor.fetchone()
|
||||
if set:
|
||||
string = "ID: %d\nFilename: %s\nDate: %s\nSize: %s\ntype: %s" % \
|
||||
(id, set[0], datetime.fromtimestamp(set[1]), set[2], set[3])
|
||||
retval['file_info'] = string
|
||||
retval['debug'] = {'id': id,
|
||||
'date': datetime.fromtimestamp(set[1]),
|
||||
'size': set[2], 'type': set[3]}
|
||||
|
||||
retval['filename'] = set[0]
|
||||
|
||||
if set[5]:
|
||||
retval['description'] = set[5]
|
||||
|
||||
if set[6]:
|
||||
retval['note'] = set[6]
|
||||
|
||||
if set[4]:
|
||||
retval['thumbnail'] = os.path.join(self.internal_dirname, set[4])
|
||||
@@ -370,8 +446,16 @@ class MainModel(ModelMT):
|
||||
myiter = self.exif_list.insert_before(None, None)
|
||||
self.exif_list.set_value(myiter, 0, self.EXIF_DICT[key])
|
||||
self.exif_list.set_value(myiter, 1, set[key])
|
||||
|
||||
retval['exif'] = True
|
||||
|
||||
# gthumb
|
||||
sql = """SELECT note, place, date from gthumb WHERE file_id = ?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
set = self.db_cursor.fetchone()
|
||||
|
||||
if set:
|
||||
retval['gthumb'] = {'note': set[0], 'place': set[1], 'date': set[2]}
|
||||
|
||||
return retval
|
||||
|
||||
def get_source(self, path):
|
||||
@@ -396,27 +480,10 @@ class MainModel(ModelMT):
|
||||
return None, None
|
||||
return res[0], res[1]
|
||||
|
||||
def delete_image(self, id):
|
||||
"""removes image on specified id"""
|
||||
sql = """select filename, thumbnail from images where id=?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
res = self.db_cursor.fetchone()
|
||||
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]
|
||||
# remove images records
|
||||
sql = """delete from images where id = ?"""
|
||||
self.db_cursor.execute(sql, (id,))
|
||||
self.db_connection.commit()
|
||||
|
||||
def delete(self, root_id, db_cursor=None, db_connection=None):
|
||||
"""Remove subtree from main tree, remove tags from database
|
||||
remove all possible data, like thumbnails"""
|
||||
remove all possible data, like thumbnails, images, gthumb info, exif
|
||||
etc"""
|
||||
|
||||
fids = []
|
||||
|
||||
@@ -429,9 +496,7 @@ class MainModel(ModelMT):
|
||||
sql = """select parent_id from files where id = ?"""
|
||||
db_cursor.execute(sql, (root_id,))
|
||||
res = db_cursor.fetchone()
|
||||
if res:
|
||||
parent_id = res[0]
|
||||
|
||||
parent_id = res[0]
|
||||
|
||||
def get_children(fid):
|
||||
fids.append(fid)
|
||||
@@ -456,13 +521,9 @@ class MainModel(ModelMT):
|
||||
sql = """delete from tags_files where file_id = ?"""
|
||||
db_cursor.executemany(sql, generator())
|
||||
|
||||
arg = self.__list_to_string(fids)
|
||||
|
||||
# remove thumbnails
|
||||
arg =''
|
||||
for c in fids:
|
||||
if len(arg) > 0:
|
||||
arg+=", %d" % c
|
||||
else:
|
||||
arg = "%d" % c
|
||||
sql = """select filename from thumbnails where file_id in (%s)""" % arg
|
||||
db_cursor.execute(sql)
|
||||
res = db_cursor.fetchall()
|
||||
@@ -477,6 +538,7 @@ class MainModel(ModelMT):
|
||||
if len(res) > 0:
|
||||
for fn in res:
|
||||
os.unlink(os.path.join(self.internal_dirname, fn[0]))
|
||||
os.unlink(os.path.join(self.internal_dirname, fn[1]))
|
||||
|
||||
# remove thumbs records
|
||||
sql = """delete from thumbnails where file_id = ?"""
|
||||
@@ -486,6 +548,10 @@ class MainModel(ModelMT):
|
||||
sql = """delete from images where file_id = ?"""
|
||||
db_cursor.executemany(sql, generator())
|
||||
|
||||
# remove gthumb info
|
||||
sql = """delete from gthumb where file_id = ?"""
|
||||
db_cursor.executemany(sql, generator())
|
||||
|
||||
# correct parent direcotry sizes
|
||||
# get size and parent of deleting object
|
||||
while parent_id:
|
||||
@@ -500,8 +566,6 @@ class MainModel(ModelMT):
|
||||
from files where parent_id=?) where id=?"""
|
||||
db_cursor.execute(sql, (parent_id, parent_id))
|
||||
|
||||
|
||||
|
||||
sql = """select parent_id from files where id = ? and parent_id!=id"""
|
||||
db_cursor.execute(sql, (parent_id,))
|
||||
res = db_cursor.fetchone()
|
||||
@@ -590,9 +654,15 @@ class MainModel(ModelMT):
|
||||
self.db_cursor.execute(sql, (img_id,))
|
||||
res = self.db_cursor.fetchone()
|
||||
if res:
|
||||
return res[0]
|
||||
return os.path.join(self.internal_dirname, res[0])
|
||||
return None
|
||||
|
||||
|
||||
def update_desc_and_note(self, id, desc='', note=''):
|
||||
"""update note and description"""
|
||||
sql = """UPDATE files SET description=?, note=? WHERE id=?"""
|
||||
self.db_cursor.execute(sql, (desc, note, id))
|
||||
return
|
||||
|
||||
# private class functions
|
||||
def __bytes_to_human(self, integer):
|
||||
if integer <= 0 or integer < 1024:
|
||||
@@ -729,6 +799,7 @@ class MainModel(ModelMT):
|
||||
date datetime);""")
|
||||
self.db_cursor.execute("insert into files values(1, 1, 'root', null, 0, 0, 0, 0, null, null);")
|
||||
self.db_cursor.execute("insert into groups values(1, 'default', 'black');")
|
||||
self.db_connection.commit()
|
||||
|
||||
def __scan(self):
|
||||
"""scan content of the given path"""
|
||||
@@ -952,7 +1023,7 @@ class MainModel(ModelMT):
|
||||
cmnts['place'],
|
||||
cmnts['date']
|
||||
))
|
||||
if cmnts['keywords']:
|
||||
if cmnts.has_key('keywords'):
|
||||
# TODO: add gthumb keywords to tags and group 'gthumb'
|
||||
pass
|
||||
|
||||
@@ -997,7 +1068,9 @@ class MainModel(ModelMT):
|
||||
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)
|
||||
|
||||
self.currentid = None
|
||||
else:
|
||||
print "new directory/cd"
|
||||
@@ -1120,4 +1193,12 @@ class MainModel(ModelMT):
|
||||
else:
|
||||
return txt
|
||||
|
||||
def __list_to_string(self, array):
|
||||
arg =''
|
||||
for c in array:
|
||||
if len(arg) > 0:
|
||||
arg+=", %d" % c
|
||||
else:
|
||||
arg = "%d" % c
|
||||
return arg
|
||||
pass # end of class
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
# 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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
from xml.dom.minidom import Node
|
||||
from xml.dom import minidom
|
||||
import gzip
|
||||
|
||||
@@ -27,7 +27,6 @@ from shutil import move, copy
|
||||
from os import path, mkdir
|
||||
from datetime import datetime
|
||||
|
||||
from utils import EXIF
|
||||
import Image
|
||||
|
||||
class Img(object):
|
||||
@@ -40,76 +39,26 @@ class Img(object):
|
||||
|
||||
def save(self, image_id):
|
||||
"""Save image and asociated thumbnail into specific directory structure
|
||||
return full path to the file and thumbnail None"""
|
||||
return full path to the file and thumbnail or None"""
|
||||
|
||||
base_path = self.__get_and_make_path(image_id)
|
||||
ext = self.filename.split('.')[-1].lower()
|
||||
image_filename = path.join(self.base, base_path + "_im." + ext)
|
||||
image_filename = path.join(self.base, base_path + "." + ext)
|
||||
|
||||
thumbnail = path.join(self.base, base_path + "_t.jpg")
|
||||
|
||||
# make and save image
|
||||
filepath = path.join(self.base, base_path + ".jpg")
|
||||
f = open(self.filename, 'rb')
|
||||
exif = None
|
||||
returncode = -1
|
||||
try:
|
||||
exif = EXIF.process_file(f)
|
||||
f.close()
|
||||
if exif.has_key('JPEGThumbnail'):
|
||||
thumbnail = exif['JPEGThumbnail']
|
||||
f = open(filepath,'wb')
|
||||
f.write(thumbnail)
|
||||
f.close()
|
||||
if exif.has_key('Image Orientation'):
|
||||
orientation = exif['Image Orientation'].values[0]
|
||||
if orientation > 1:
|
||||
# TODO: replace silly datetime function with tempfile
|
||||
t = path.join(gettempdir(), "thumb%d.jpg" % datetime.now().microsecond)
|
||||
im_in = Image.open(filepath)
|
||||
im_out = None
|
||||
if orientation == 8:
|
||||
# Rotated 90 CCW
|
||||
im_out = im_in.transpose(Image.ROTATE_90)
|
||||
elif orientation == 6:
|
||||
# Rotated 90 CW
|
||||
im_out = im_in.transpose(Image.ROTATE_270)
|
||||
elif orientation == 3:
|
||||
# Rotated 180
|
||||
im_out = im_in.transpose(Image.ROTATE_180)
|
||||
elif orientation == 2:
|
||||
# Mirrored horizontal
|
||||
im_out = im_in.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
elif orientation == 4:
|
||||
# Mirrored vertical
|
||||
im_out = im_in.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
elif orientation == 5:
|
||||
# Mirrored horizontal then rotated 90 CCW
|
||||
im_out = im_in.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
|
||||
elif orientation == 7:
|
||||
# Mirrored horizontal then rotated 90 CW
|
||||
im_out = im_in.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
|
||||
|
||||
if im_out:
|
||||
im_out.save(t, 'JPEG')
|
||||
move(t, filepath)
|
||||
else:
|
||||
f.close()
|
||||
returncode = 0
|
||||
else:
|
||||
im = self.__scale_image()
|
||||
if im:
|
||||
im.save(filepath, "JPEG")
|
||||
returncode = 1
|
||||
except:
|
||||
f.close()
|
||||
im = self.__scale_image()
|
||||
if im:
|
||||
im.save(filepath, "JPEG")
|
||||
returncode = 2
|
||||
|
||||
im = self.__scale_image()
|
||||
if im:
|
||||
im.save(thumbnail, "JPEG")
|
||||
returncode = 1
|
||||
|
||||
if returncode != -1:
|
||||
# copy image
|
||||
copy(self.filename, image_filename)
|
||||
return filepath, image_filename, returncode
|
||||
|
||||
return thumbnail, image_filename, returncode
|
||||
|
||||
# private class functions
|
||||
def __get_and_make_path(self, img_id):
|
||||
@@ -146,7 +95,7 @@ class Img(object):
|
||||
img = "%s" % h[2:]
|
||||
return(path.join(t, fpath, img))
|
||||
|
||||
def __scale_image(self, factor=True):
|
||||
def __scale_image(self):
|
||||
"""create thumbnail. returns image object or None"""
|
||||
try:
|
||||
im = Image.open(self.filename).convert('RGB')
|
||||
|
||||
@@ -177,14 +177,15 @@ class PointDirectoryToAdd(object):
|
||||
dialog.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
|
||||
dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
if self.URI:
|
||||
dialog.set_current_folder_uri(self.URI)
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
self.directory.set_text(dialog.get_filename())
|
||||
self.__class__.URI = dialog.get_current_folder_uri()
|
||||
dialog.destroy()
|
||||
|
||||
def run(self):
|
||||
if self.URI:
|
||||
self.dialog.set_current_folder_uri(self.URI)
|
||||
dialog = self.gladexml.get_widget("addDirDialog")
|
||||
ch = True
|
||||
result = dialog.run()
|
||||
@@ -195,7 +196,6 @@ class PointDirectoryToAdd(object):
|
||||
result = dialog.run()
|
||||
else:
|
||||
ch = False
|
||||
self.__class__.URI = self.dialog.get_current_folder_uri()
|
||||
dialog.destroy()
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return self.volname.get_text(),self.directory.get_text()
|
||||
@@ -321,7 +321,7 @@ class LoadImageFile(object):
|
||||
|
||||
URI="file://"+os.path.abspath(os.path.curdir)
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, multiple=False):
|
||||
self.dialog = gtk.FileChooserDialog(
|
||||
title="Select image",
|
||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
@@ -332,7 +332,7 @@ class LoadImageFile(object):
|
||||
gtk.RESPONSE_OK
|
||||
)
|
||||
)
|
||||
self.dialog.set_select_multiple(True)
|
||||
self.dialog.set_select_multiple(multiple)
|
||||
self.dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
f = gtk.FileFilter()
|
||||
@@ -357,7 +357,10 @@ class LoadImageFile(object):
|
||||
|
||||
if response == gtk.RESPONSE_OK:
|
||||
try:
|
||||
filenames = self.dialog.get_filenames()
|
||||
if self.dialog.get_select_multiple():
|
||||
filenames = self.dialog.get_filenames()
|
||||
else:
|
||||
filenames = self.dialog.get_filename()
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -417,3 +420,41 @@ class StatsDialog(object):
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return entry.get_text()
|
||||
return None
|
||||
|
||||
class EditDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
def __init__(self, values={}):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.values = values
|
||||
|
||||
def run(self):
|
||||
gladexml = gtk.glade.XML(self.gladefile, "file_editDialog")
|
||||
dialog = gladexml.get_widget("file_editDialog")
|
||||
|
||||
filename = gladexml.get_widget("filename_entry")
|
||||
filename.set_text(str(self.values['filename']))
|
||||
description = gladexml.get_widget("description_text")
|
||||
note = gladexml.get_widget("note_text")
|
||||
|
||||
if self.values.has_key('description'):
|
||||
buff = gtk.TextBuffer()
|
||||
buff.set_text(str(self.values['description']))
|
||||
description.set_buffer(buff)
|
||||
|
||||
if self.values.has_key('note'):
|
||||
buff = gtk.TextBuffer()
|
||||
buff.set_text(str(self.values['note']))
|
||||
note.set_buffer(buff)
|
||||
|
||||
result = dialog.run()
|
||||
if result == gtk.RESPONSE_OK:
|
||||
d = description.get_buffer()
|
||||
n = note.get_buffer()
|
||||
retval = {'filename': filename.get_text(),
|
||||
'description': d.get_text(d.get_start_iter(),
|
||||
d.get_end_iter()),
|
||||
'note': n.get_text(n.get_start_iter(), n.get_end_iter())}
|
||||
dialog.destroy()
|
||||
return retval
|
||||
dialog.destroy()
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user