diff --git a/img.py b/img.py
new file mode 100644
index 0000000..6f59e2a
--- /dev/null
+++ b/img.py
@@ -0,0 +1,202 @@
+import pygtk; pygtk.require('2.0')
+import gtk
+
+import EXIF
+import Image
+import os
+import shutil
+from datetime import datetime
+
+class Thumbnail(object):
+ def __init__(self, filename=None, x=160, y=120, root='thumbnails', base=''):
+ self.root = root
+ self.x = x
+ self.y = y
+ self.filename = filename
+ self.base = base
+
+ def save(self, image_id):
+ """Save thumbnail into specific directory structure
+ return full path to the file and exif object or None"""
+ filepath = os.path.join(self.base, self.__get_and_make_path(image_id))
+ 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:
+ t = "/tmp/thumb%d.jpg" % datetime.now().microsecond
+ im_in = Image.open(filepath)
+ im_out = None
+ if orientation == 8:
+ im_out = im_in.transpose(Image.ROTATE_90)
+ elif orientation == 6:
+ im_out = im_in.transpose(Image.ROTATE_270)
+ if im_out:
+ im_out.save(t, 'JPEG')
+ shutil.move(t, filepath)
+ else:
+ f.close()
+ returncode = 0
+ else:
+ im = self.__scale_image(True)
+ if im:
+ im.save(filepath, "JPEG")
+ returncode = 1
+ except:
+ f.close()
+ im = self.__scale_image(True)
+ if im:
+ im.save(filepath, "JPEG")
+ returncode = 2
+ return filepath, exif, returncode
+
+ # private class functions
+ def __get_and_make_path(self, img_id):
+ """Make directory structure regards of id
+ and return filepath WITHOUT extension"""
+ try: os.mkdir(self.root)
+ except: pass
+ h = hex(img_id)
+ if len(h[2:])>6:
+ try: os.mkdir(os.path.join(self.root, h[2:4]))
+ except: pass
+ try: os.mkdir(os.path.join(self.root, h[2:4], h[4:6]))
+ except: pass
+ path = os.path.join(self.root, h[2:4], h[4:6], h[6:8])
+ try: os.mkdir(path)
+ except: pass
+ img = "%s.%s" % (h[8:], 'jpg')
+ elif len(h[2:])>4:
+ try: os.mkdir(os.path.join(self.root, h[2:4]))
+ except: pass
+ path = os.path.join(self.root, h[2:4], h[4:6])
+ try: os.mkdir(path)
+ except: pass
+ img = "%s.%s" % (h[6:], 'jpg')
+ elif len(h[2:])>2:
+ path = os.path.join(self.root, h[2:4])
+ try: os.mkdir(path)
+ except: pass
+ img = "%s.%s" %(h[4:], 'jpg')
+ else:
+ path = self.root
+ img = "%s.%s" %(h[2:], 'jpg')
+ return(os.path.join(self.root, img))
+
+ def __scale_image(self, factor=False):
+ """generate scaled Image object for given file
+ args:
+ factor - if False, adjust height into self.y
+ if True, use self.x for scale portrait pictures height.
+ returns Image object, or False
+ """
+ try:
+ im = Image.open(self.filename).convert('RGB')
+ except:
+ return False
+ x, y = im.size
+
+ if x > self.x or y > self.y:
+ if x==y:
+ # square
+ imt = im.resize((self.y, self.y), Image.ANTIALIAS)
+ elif x > y:
+ # landscape
+ if int(y/(x/float(self.x))) > self.y:
+ # landscape image: height is non standard
+ self.x1 = int(float(self.y) * self.y / self.x)
+ if float(self.y) * self.y / self.x - self.x1 > 0.49:
+ self.x1 += 1
+ imt = im.resize(((int(x/(y/float(self.y))),self.y)),Image.ANTIALIAS)
+ elif x/self.x==y/self.y:
+ # aspect ratio ok
+ imt = im.resize((self.x, self.y), Image.ANTIALIAS)
+ else:
+ imt = im.resize((self.x,int(y/(x/float(self.x)))), 1)
+ else:
+ # portrait
+ if factor:
+ if y>self.x:
+ imt = im.resize(((int(x/(y/float(self.x))),self.x)),Image.ANTIALIAS)
+ else:
+ imt = im
+ else:
+ self.x1 = int(float(self.y) * self.y / self.x)
+ if float(self.y) * self.y / self.x - self.x1 > 0.49:
+ self.x1 += 1
+
+ if x/self.x1==y/self.y:
+ # aspect ratio ok
+ imt = im.resize((self.x1,self.y),Image.ANTIALIAS)
+ else:
+ imt = im.resize(((int(x/(y/float(self.y))),self.y)),Image.ANTIALIAS)
+ return imt
+ else:
+ return im
+
+class Image_Example(object):
+
+ def pressButton(self, widget, data=None):
+ print "Pressed"
+
+ def delete_event(self, widget, event, data=None):
+ print "delete event occured"
+
+ return False
+
+ def destroy(self, widget, data=None):
+ gtk.main_quit()
+
+ def __init__(self):
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.connect("delete_event", self.delete_event)
+ self.window.connect("destroy", self.destroy)
+ self.window.set_border_width(10)
+
+ self.button = gtk.Button()
+ self.button.connect("clicked", self.pressButton, None)
+ self.button.connect_object("clicked", gtk.Widget.destroy, self.window)
+
+
+
+ root, dirs, files = os.walk('/home/gryf/t/t').next()
+ count = 0
+ for i in files:
+ count+=1
+ path, exif, success = Thumbnail(os.path.join(root, i), base='/home/gryf/t/t').save(count)
+ if exif:
+ print path, len(exif), success
+ if success != -1:
+ p = path
+
+ self.image = gtk.Image()
+ self.image.set_from_file(os.path.join(root, path))
+ self.image.show()
+
+ pb = self.image.get_pixbuf()
+ print pb.get_width(), pb.get_height()
+
+ self.button.add(self.image)
+ self.window.add(self.button)
+ self.button.show()
+ self.window.show()
+
+
+
+ def main(self):
+ gtk.main()
+
+
+if __name__ == '__main__':
+
+ Image_Example().main()
+
diff --git a/resources/glade/dialogs.glade b/resources/glade/dialogs.glade
index 1966cb1..a381fd9 100644
--- a/resources/glade/dialogs.glade
+++ b/resources/glade/dialogs.glade
@@ -395,4 +395,86 @@
+
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 5
+ pyGTKtalog - rename
+ GTK_WIN_POS_CENTER_ON_PARENT
+ GDK_WINDOW_TYPE_HINT_DIALOG
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 2
+
+
+ True
+
+
+ True
+ 3
+ Rename
+
+
+ False
+ False
+
+
+
+
+ True
+ True
+ True
+ True
+
+
+ 1
+
+
+
+
+ 2
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ GTK_BUTTONBOX_END
+
+
+ True
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ gtk-cancel
+ True
+ -6
+
+
+
+
+ True
+ True
+ True
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ gtk-ok
+ True
+ -5
+
+
+ 1
+
+
+
+
+ False
+ GTK_PACK_END
+
+
+
+
+
diff --git a/resources/glade/main.glade b/resources/glade/main.glade
index 625c9bb..1b287cb 100644
--- a/resources/glade/main.glade
+++ b/resources/glade/main.glade
@@ -650,98 +650,24 @@
-
+
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
-
+
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
- GTK_SHADOW_IN
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- False
- GTK_WRAP_WORD
- 2
- 2
- False
-
-
+ False
+ GTK_WRAP_WORD
+ 2
+ 2
+ False
-
- True
- True
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- True
-
-
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
- GTK_SHADOW_IN
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- True
-
-
-
-
- False
- True
-
-
-
-
-
-
-
- 1
-
-
-
-
-
-
-
- True
- True
-
@@ -761,6 +687,113 @@
False
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Double click to open image
+
+
+
+
+
+
+ 1
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Images
+
+
+ tab
+ 1
+ False
+
+
+
+
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ True
+
+
+
+
+ False
+ True
+
+
+
+
+
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ True
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ page 3
+
+
+ tab
+ 2
+ False
+
+
True
@@ -854,6 +887,7 @@
True
_Rename
True
+
@@ -890,7 +924,7 @@
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ pyGTKtalog - Image
+ True
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ gtk-missing-image
+
+
+
+
diff --git a/src/ctrls/c_main.py b/src/ctrls/c_main.py
index 1caf3f6..27b83f1 100644
--- a/src/ctrls/c_main.py
+++ b/src/ctrls/c_main.py
@@ -39,6 +39,8 @@ from models.m_config import ConfigModel
import views.v_dialogs as Dialogs
+from views.v_image import ImageView
+
import gtk
import pango
@@ -123,18 +125,57 @@ class MainController(Controller):
#########################################################################
# Connect signals from GUI, like menu objects, toolbar buttons and so on.
+ 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))
+
def on_rename1_activate(self, widget):
model, iter = self.view['discs'].get_selection().get_selected()
- label_old = model.get_value(iter, 1)
+ name = model.get_value(iter, 1)
id = model.get_value(iter, 0)
- label = Dialogs.InputDiskLabel(label_old).run()
+ new_name = Dialogs.InputNewName(name).run()
+
if __debug__:
- print "c_main.py: on_rename1_activate(): label:", label
- if label != None and label !=label_old:
- self.model.set_label(id, label)
- self.model.unsaved_project = True
+ print "c_main.py: on_rename1_activate(): label:", new_name
+
+ if new_name != None and new_name != name:
+ self.model.rename(id, new_name)
self.__set_title(filepath=self.model.filename, modified=True)
+ def on_rename2_activate(self, widget):
+ try:
+ selection = self.view['files'].get_selection()
+ model, list_of_paths = selection.get_selected_rows()
+ except TypeError:
+ return
+
+ if len(list_of_paths) != 1:
+ return
+
+ fid = model.get_value(model.get_iter(list_of_paths[0]),0)
+ 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 new_name != None and new_name != name:
+ self.model.rename(fid, new_name)
+ self.__set_title(filepath=self.model.filename, modified=True)
+
+ try:
+ path, column = self.view['discs'].get_cursor()
+ print path
+ iter = model.get_iter(path)
+ self.model.get_root_entries(model.get_value(iter, 0))
+ except TypeError:
+ print "zuo"
+ self.model.get_root_entries(1)
+ return
+ return
+
def on_tag_cloud_textview_motion_notify_event(self, widget):
if __debug__:
print "c_main.py: on_tag_cloud_textview_motion_notify_event():"
@@ -246,7 +287,42 @@ class MainController(Controller):
else:
treeview.expand_row(path,False)
return
-
+
+ def on_images_button_press_event(self, iconview, event):
+ try:
+ path_and_cell = iconview.get_item_at_pos(int(event.x), int(event.y))
+ except TypeError:
+ return False
+
+ if event.button == 3: # Right mouse button. Show context menu.
+ try:
+ iconview.select_path(path_and_cell[0])
+ except TypeError:
+ return False
+
+ self.__popup_menu(event, 'img_popup')
+ return True
+ return False
+
+ def on_img_delete_activate(self, menu_item):
+ list_of_paths = self.view['images'].get_selected_items()
+ model = self.view['images'].get_model()
+ iter = model.get_iter(list_of_paths[0])
+ id = model.get_value(iter, 0)
+ self.model.delete_image(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
+
+ def on_img_add_activate(self, menu_item):
+ self.on_add_image1_activate(menu_item)
+
def on_discs_button_press_event(self, treeview, event):
try:
path, column, x, y = treeview.get_path_at_pos(int(event.x),
@@ -271,11 +347,9 @@ class MainController(Controller):
if self.model.discs_tree.get_value(iter, 3) == 1:
# if ancestor is 'root', then activate "update" menu item
self.view['update1'].set_sensitive(True)
- self.view['rename1'].set_sensitive(True)
else:
self.view['update1'].set_sensitive(False)
- self.view['rename1'].set_sensitive(False)
- self.__popup_discs_menu(event)
+ self.__popup_menu(event)
# elif event.button == 1: # Left click
# """Show files on right treeview, after clicking the left disc treeview."""
@@ -314,8 +388,17 @@ class MainController(Controller):
model, list_of_paths = selection.get_selected_rows()
except TypeError:
list_of_paths = []
-
- self.__popup_files_menu(event)
+
+ if len(list_of_paths) == 0:
+ selection.select_path(path[0])
+
+ if len(list_of_paths) > 1:
+ self.view['add_image1'].set_sensitive(False)
+ self.view['rename2'].set_sensitive(False)
+ else:
+ self.view['add_image1'].set_sensitive(True)
+ self.view['rename2'].set_sensitive(True)
+ self.__popup_menu(event, 'files_popup')
return True
def on_files_cursor_changed(self, treeview):
@@ -323,18 +406,18 @@ class MainController(Controller):
model, paths = treeview.get_selection().get_selected_rows()
try:
itera = model.get_iter(paths[0])
- if model.get_value(itera,4) == 1:
- #directory, do nothin', just turn off view
- '''self.view['details'].hide()
- buf = self.view['details'].get_buffer()
- buf.set_text('')
- self.view['details'].set_buffer(buf)'''
- else:
+ #if model.get_value(itera,4) == 1:
+ # #directory, do nothin', just turn off view
+ # '''self.view['details'].hide()
+ # buf = self.view['details'].get_buffer()
+ # buf.set_text('')
+ # self.view['details'].set_buffer(buf)'''
+ #else:
#file, show what you got.
#self.details.get_top_widget()
- iter = model.get_iter(treeview.get_cursor()[0])
- selected_item = self.model.files_list.get_value(iter, 0)
- self.__get_item_info(selected_item)
+ iter = model.get_iter(treeview.get_cursor()[0])
+ 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"
@@ -409,39 +492,130 @@ class MainController(Controller):
return
def on_add_tag1_activate(self, menu_item):
- print self.view['discs'].get_cursor()
+ print self.view['files'].get_cursor()
+
+ def on_add_image1_activate(self, menu_item):
+ images = Dialogs.LoadImageFile().run()
+ if not images:
+ return
+ for image in images:
+ 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:
+ 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)
+ except:
+ return
+ self.model.add_image(image, id)
+ self.__get_item_info(id)
+ return
def on_update1_activate(self, menu_item):
"""Update disc under cursor position"""
+ path, column = self.view['discs'].get_cursor()
+ model = self.view['discs'].get_model()
# determine origin label and filepath
- path = self.view['discs'].get_cursor()
filepath, label = self.model.get_label_and_filepath(path)
+ fid = model.get_value(model.get_iter(path), 0)
+
if self.model.get_source(path) == self.model.CD:
- if self.__add_cd(label):
- self.model.delete(self.model.discs_tree.get_iter(path[0],0))
- pass
+ self.__add_cd(label, fid)
+
elif self.model.get_source(path) == self.model.DR:
- if self.__add_directory(filepath, label):
- self.model.delete(self.model.discs_tree.get_iter(path[0]))
- pass
+ self.__add_directory(filepath, label, fid)
+
return
def on_delete2_activate(self, menu_item):
- model = self.view['discs'].get_model()
try:
- path, column = self.view['discs'].get_cursor()
- selected_iter = self.model.discs_tree.get_iter(path)
+ #path, column = self.view['discs'].get_cursor()
+ #selected_iter = model.get_iter(path)
+ selection = self.view['discs'].get_selection()
+ model, selected_iter = selection.get_selected()
except:
return
+
if self.model.config.confd['delwarn']:
- name = self.model.discs_tree.get_value(selected_iter,1)
+ name = model.get_value(selected_iter, 1)
obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name,
'Object will be permanently removed.')
if not obj.run():
return
- self.model.delete(selected_iter)
+
+ # remove from model
+ path = model.get_path(selected_iter)
+ current_id = self.model.discs_tree.get_value(selected_iter, 0)
+ model.remove(selected_iter)
+ selection.select_path(path)
+
+ if not selection.path_is_selected(path):
+ row = path[0]-1
+ if row >= 0:
+ selection.select_path((row,))
+ path = (row, )
+
+ # delete from db
+ print current_id
+ self.model.delete(current_id)
+
+ # refresh files treeview
+ current_id = model.get_value(model.get_iter(path), 0)
+ self.model.get_root_entries(current_id)
+
+ # refresh file info view
+ self.__get_item_info(current_id)
+
+ self.model.unsaved_project = True
+ self.__set_title(filepath=self.model.filename, modified=True)
+ return
+
+ def on_delete3_activate(self, menu_item):
+ dmodel = self.model.discs_tree
+ try:
+ selection = self.view['files'].get_selection()
+ model, list_of_paths = selection.get_selected_rows()
+ except TypeError:
+ return
+
+ if self.model.config.confd['delwarn']:
+ obj = Dialogs.Qst('Delete elements', 'Delete items?',
+ 'Items will be permanently removed.')
+ if not obj.run():
+ return
+
+ def foreach_disctree(zmodel, zpath, ziter, d):
+ if d[0] == zmodel.get_value(ziter, 0):
+ d[1].append(zpath)
+ return False
+
+ for p in list_of_paths:
+ val = model.get_value(model.get_iter(p), 0)
+ if model.get_value(model.get_iter(p), 4) == self.model.DIR:
+ # remove from disctree model aswell
+ dpath = []
+ dmodel.foreach(foreach_disctree, (val, dpath))
+ for dp in dpath:
+ dmodel.remove(dmodel.get_iter(dp))
+
+ # delete from db
+ self.model.delete(val)
+
+ try:
+ selection = self.view['discs'].get_selection()
+ model, list_of_paths = selection.get_selected_rows()
+ if not list_of_paths:
+ list_of_paths = [1]
+ self.model.get_root_entries(model.get_value(model.get_iter(list_of_paths[0]),0))
+ except TypeError:
+ return
+
self.model.unsaved_project = True
self.__set_title(filepath=self.model.filename, modified=True)
return
@@ -505,8 +679,7 @@ class MainController(Controller):
"""Open catalog file"""
confirm = self.model.config.confd['confirmabandon']
if self.model.unsaved_project and confirm:
- obj = Dialogs.Qst('Unsaved data - pyGTKtalog','There is not saved \
- database','Pressing "Ok" will abandon catalog.')
+ obj = Dialogs.Qst('Unsaved data - pyGTKtalog','There is not saved database','Pressing "Ok" will abandon catalog.')
if not obj.run():
return
@@ -543,7 +716,7 @@ class MainController(Controller):
Dialogs.Err("Error writing file - pyGTKtalog","Cannot write \
file %s." % path, "%s" % err)
- def __add_cd(self, label=None):
+ def __add_cd(self, label=None, current_id=None):
"""Add directory structure from cd/dvd disc"""
mount = deviceHelper.volmount(self.model.config.confd['cd'])
if mount == 'ok':
@@ -555,7 +728,8 @@ class MainController(Controller):
for widget in self.widgets_all:
self.view[widget].set_sensitive(False)
self.model.source = self.model.CD
- self.model.scan(self.model.config.confd['cd'],label)
+ self.model.scan(self.model.config.confd['cd'], label,
+ current_id)
self.model.unsaved_project = True
self.__set_title(filepath=self.model.filename, modified=True)
return True
@@ -566,7 +740,7 @@ class MainController(Controller):
"Last mount message:\n%s" % mount)
return False
- def __add_directory(self, path=None, label=None):
+ def __add_directory(self, path=None, label=None, current_id=None):
if not label or not path:
res = Dialogs.PointDirectoryToAdd().run()
if res !=(None,None):
@@ -577,7 +751,7 @@ class MainController(Controller):
self.scan_cd = False
self.model.source = self.model.DR
- self.model.scan(path, label)
+ self.model.scan(path, label, current_id)
self.model.unsaved_project = True
self.__set_title(filepath=self.model.filename, modified=True)
return True
@@ -639,6 +813,12 @@ class MainController(Controller):
return
+ def __setup_iconview(self):
+ """Setup IconView images widget."""
+ self.view['images'].set_model(self.model.images_store)
+ self.view['images'].set_pixbuf_column(1)
+ return
+
def __setup_tags_treeview(self):
"""Setup TreeView discs widget as tree."""
self.view['tags'].set_model(self.model.tagsTree)
@@ -687,10 +867,10 @@ class MainController(Controller):
c.set_resizable(True)
self.view['files'].append_column(c)
- c = gtk.TreeViewColumn('Category',gtk.CellRendererText(), text=5)
- c.set_sort_column_id(5)
- c.set_resizable(True)
- self.view['files'].append_column(c)
+ #c = gtk.TreeViewColumn('Category',gtk.CellRendererText(), text=5)
+ #c.set_sort_column_id(5)
+ #c.set_resizable(True)
+ #self.view['files'].append_column(c)
# registration of treeview signals:
@@ -742,16 +922,10 @@ class MainController(Controller):
self.model.config.save()
return
- def __popup_discs_menu(self, event):
- self.view['discs_popup'].popup(None, None, None, event.button,
+ def __popup_menu(self, event, menu='discs_popup'):
+ self.view[menu].popup(None, None, None, event.button,
event.time)
- self.view['discs_popup'].show_all()
- return
-
- def __popup_files_menu(self, event):
- self.view['files_popup'].popup(None, None, None, event.button,
- event.time)
- self.view['files_popup'].show_all()
+ self.view[menu].show_all()
return
def __generate_recent_menu(self):
@@ -777,11 +951,17 @@ class MainController(Controller):
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('')
+
self.view['description'].set_buffer(buf)
+ if set.has_key('images'):
+ self.__setup_iconview()
+ self.view['img_container'].show()
+ else:
+ self.view['img_container'].hide()
+
if set.has_key('thumbnail'):
self.view['thumb'].set_from_file(set['thumbnail'])
self.view['thumb'].show()
diff --git a/src/models/m_main.py b/src/models/m_main.py
index e7fbed4..85545d3 100644
--- a/src/models/m_main.py
+++ b/src/models/m_main.py
@@ -46,6 +46,7 @@ except ImportError:
from m_config import ConfigModel
from m_details import DetailsModel
from utils.thumbnail import Thumbnail
+from utils.img import Img
class MainModel(ModelMT):
"""Create, load, save, manipulate db file which is container for data"""
@@ -79,7 +80,7 @@ class MainModel(ModelMT):
self.config.load()
self.details = DetailsModel()
- # Directory tree: id, nazwa, ikonka, typ
+ # Directory tree: id, name, icon, type
self.discs_tree = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING,
str, gobject.TYPE_INT)
# File list of selected directory: child_id(?), filename, size,
@@ -88,6 +89,8 @@ class MainModel(ModelMT):
gobject.TYPE_UINT64,
gobject.TYPE_STRING, gobject.TYPE_INT,
gobject.TYPE_STRING, str)
+ # iconview store - image id, pixbuffer
+ self.images_store = gtk.ListStore(gobject.TYPE_INT, gtk.gdk.Pixbuf)
# tag cloud array element is a dict with 4 keys:
# elem = {'id': str(id), 'name': tagname, 'size': size, 'color': color}
@@ -108,7 +111,25 @@ class MainModel(ModelMT):
{'id': str(10), 'name': "windows", 'size': 18, 'color': '#333'},
]'''
return
+ def add_image(self, image, id):
+ sql = """insert into images(file_id, thumbnail, filename)
+ values(?, null, null)"""
+ self.db_cursor.execute(sql, (id,))
+ 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, (id,))
+ res = self.db_cursor.fetchone()
+ if res:
+ tp, ip, rc = Img(image, self.internal_dirname).save(res[0])
+ if rc != -1:
+ sql = """update images set filename=?, thumbnail=? where id=?"""
+ self.db_cursor.execute(sql,
+ (ip.split(self.internal_dirname)[1][1:],
+ tp.split(self.internal_dirname)[1][1:],
+ res[0]))
+ self.db_connection.commit()
+
def cleanup(self):
self.__close_db_connection()
if self.internal_dirname != None:
@@ -201,7 +222,7 @@ class MainModel(ModelMT):
return True
- def scan(self, path, label):
+ def scan(self, path, label, currentid):
"""scan files in separated thread"""
# flush buffer to release db lock.
@@ -209,6 +230,7 @@ class MainModel(ModelMT):
self.path = path
self.label = label
+ self.currentid = currentid
if self.busy:
return
@@ -216,17 +238,21 @@ class MainModel(ModelMT):
self.thread.start()
return
- def set_label(self, id, label=None):
- if label:
+ def rename(self, id, new_name=None):
+ if new_name:
self.db_cursor.execute("update files set filename=? \
- where id=? and parent_id=1", (label, id))
+ where id=?", (new_name, id))
self.db_connection.commit()
self.__fetch_db_into_treestore()
+ self.unsaved_project = True
else:
if __debug__:
- print "m_main.py: set_label(): no label defined"
+ print "m_main.py: rename(): no label defined"
return
+ def refresh_discs_tree(self):
+ self.__fetch_db_into_treestore()
+
def get_root_entries(self, id=None):
"""Get all children down from sepcified root"""
try:
@@ -281,11 +307,12 @@ class MainModel(ModelMT):
def get_file_info(self, id):
"""get file info from database"""
retval = {}
- self.db_cursor.execute("SELECT f.filename, f.date, f.size, f.type, \
- t.filename, f.description \
- FROM files f \
- LEFT JOIN thumbnails t ON t.file_id = f.id \
- WHERE f.id = ?", (id,))
+ sql = """SELECT f.filename, f.date, f.size, f.type,
+ t.filename, f.description
+ 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" % \
@@ -296,6 +323,17 @@ class MainModel(ModelMT):
if set[4]:
retval['thumbnail'] = os.path.join(self.internal_dirname, set[4])
+
+ sql = """SELECT id, filename, thumbnail from images WHERE file_id = ?"""
+ self.db_cursor.execute(sql, (id,))
+ set = self.db_cursor.fetchall()
+ if set:
+ self.images_store = gtk.ListStore(gobject.TYPE_INT, gtk.gdk.Pixbuf)
+ for id, img, thb in set:
+ im = os.path.join(self.internal_dirname,thb)
+ pix = gtk.gdk.pixbuf_new_from_file(im)
+ self.images_store.append([id, pix])
+ retval['images'] = True
return retval
def get_source(self, path):
@@ -311,7 +349,7 @@ class MainModel(ModelMT):
def get_label_and_filepath(self, path):
"""get source of top level directory"""
- bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]),
+ bid = self.discs_tree.get_value(self.discs_tree.get_iter(path),
0)
self.db_cursor.execute("select filepath, filename from files \
where id = ? and parent_id = 1", (bid,))
@@ -320,11 +358,92 @@ class MainModel(ModelMT):
return None, None
return res[0], res[1]
- def delete(self, branch_iter):
- if not branch_iter:
- return
- self.__remove_branch_form_db(self.discs_tree.get_value(branch_iter,0))
- self.discs_tree.remove(branch_iter)
+ 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"""
+
+ # TODO: opanowac syf zwiazany z tym, ze katalogi teraz przechowuja dane nieprawdziwe
+
+ fids = []
+
+ if not db_cursor:
+ db_cursor = self.db_cursor
+
+ if not db_connection:
+ db_connection = self.db_connection
+
+ def get_children(fid):
+ fids.append(fid)
+ sql = """select id from files where parent_id = ?"""
+ db_cursor.execute(sql, (fid,))
+ res = db_cursor.fetchall()
+ if len(res)>0:
+ for i in res:
+ get_children(i[0])
+
+ get_children(root_id)
+
+ def generator():
+ for c in fids:
+ yield (c,)
+
+ # remove files records
+ sql = """delete from files where id = ?"""
+ db_cursor.executemany(sql, generator())
+
+ # remove tags records
+ sql = """delete from tags_files where file_id = ?"""
+ db_cursor.executemany(sql, generator())
+
+ # 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()
+ if len(res) > 0:
+ for fn in res:
+ os.unlink(os.path.join(self.internal_dirname, fn[0]))
+
+ # remove images
+ sql = """select filename, thumbnail from images where file_id in (%s)""" % arg
+ db_cursor.execute(sql)
+ res = db_cursor.fetchall()
+ if res[0][0]:
+ for fn in res:
+ os.unlink(os.path.join(self.internal_dirname, fn[0]))
+
+ # remove thumbs records
+ sql = """delete from thumbnails where file_id = ?"""
+ db_cursor.executemany(sql, generator())
+
+ # remove images records
+ sql = """delete from images where file_id = ?"""
+ db_cursor.executemany(sql, generator())
+
+ db_connection.commit()
return
def get_stats(self, selected_id):
@@ -398,25 +517,33 @@ class MainModel(ModelMT):
retval['size'] = self.__bytes_to_human(res[0])
return retval
+ def get_image_path(self, img_id):
+ """return image location"""
+ sql = """select filename from images where id=?"""
+ self.db_cursor.execute(sql, (img_id,))
+ res = self.db_cursor.fetchone()
+ if res:
+ return res[0]
+ return None
+
# private class functions
def __bytes_to_human(self, integer):
if integer <= 0 or integer < 1024:
return "%d bytes" % integer
- t = integer /1024.0
- if t < 1 or t < 1024:
- return "%d bytes (%d kB)" % (integer, t)
-
- t = t /1024.0
- if t < 1 or t < 1024:
- return "%d bytes (%d MB)" % (integer, t)
+ ## convert integer into string with thousands' separator
+ #for i in range(len(str(integer))/3+1):
+ # if i == 0:
+ # s_int = str(integer)[-3:]
+ # else:
+ # s_int = str(integer)[-(3*int(i)+3):-(3*int(i))] + " " + s_int
- t = t /1024.0
- if t < 1 or t < 1024:
- return "%d bytes (%d GB)" % (integer, t)
-
- t = t /1024.0
- return "%d bytes (%d TB)" % (integer, t)
+ t = integer
+ for power in ['kB', 'MB', 'GB', 'TB']:
+ t = t /1024.0
+ if t < 1 or t < 1024:
+ break
+ return "%0.2f %s (%d bytes)" % (t, power, integer)
def __clear_trees(self):
self.__clear_files_tree()
@@ -506,7 +633,12 @@ class MainModel(ModelMT):
thumbnails(id INTEGER PRIMARY KEY AUTOINCREMENT,
file_id INTEGER,
filename TEXT);""")
- self.db_cursor.execute("insert into files values(1, 1, 'root', null, 0, 0, 0, 0, null, null, null, null);")
+ self.db_cursor.execute("""create table
+ images(id INTEGER PRIMARY KEY AUTOINCREMENT,
+ file_id INTEGER,
+ thumbnail TEXT,
+ filename TEXT);""")
+ 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');")
def __scan(self):
@@ -678,11 +810,11 @@ class MainModel(ModelMT):
# Images - thumbnails and exif data
if self.config.confd['thumbs'] and ext in self.IMG:
- path, exif, ret_code = Thumbnail(current_file, base=self.internal_dirname).save(fileid)
+ tpath, exif, ret_code = Thumbnail(current_file, base=self.internal_dirname).save(fileid)
if ret_code != -1:
sql = """insert into thumbnails(file_id, filename) values (?, ?)"""
db_cursor.execute(sql, (fileid,
- path.split(self.internal_dirname)[1][1:]))
+ tpath.split(self.internal_dirname)[1][1:]))
if self.config.confd['exif']:
# TODO: exif implementation
@@ -717,14 +849,21 @@ class MainModel(ModelMT):
if __recurse(1, self.label, self.path, 0, 0, self.DIR) == -1:
if __debug__:
- print "m_main.py: __scan() __recurse() \
- interrupted self.abort = True"
+ 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.delete(self.currentid, db_cursor, db_connection)
+ self.currentid = None
+ else:
+ print "new directory/cd"
db_cursor.close()
db_connection.commit()
db_connection.close()
@@ -733,6 +872,8 @@ class MainModel(ModelMT):
self.busy = False
+ # refresh discs tree
+ self.__fetch_db_into_treestore()
self.statusmsg = "Idle"
self.progress = 0
self.abort = False
@@ -797,54 +938,6 @@ class MainModel(ModelMT):
db_connection.close()
return
- def __remove_branch_form_db(self, root_id):
- """Remove subtree from main tree, remove tags from database
- remove all possible data, like thumbnails"""
- fids = []
-
- def get_children(fid):
- fids.append(fid)
- sql = """select id from files where parent_id = ?"""
- self.db_cursor.execute(sql, (fid,))
- res = self.db_cursor.fetchall()
- if len(res)>0:
- for i in res:
- get_children(i[0])
-
- get_children(root_id)
-
- def generator():
- for c in fids:
- yield (c,)
-
- # remove files records
- sql = """delete from files where id = ?"""
- self.db_cursor.executemany(sql, generator())
-
- # remove tags records
- sql = """delete from tags_files where file_id = ?"""
- self.db_cursor.executemany(sql, generator())
-
- # 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
- self.db_cursor.execute(sql)
- res = self.db_cursor.fetchall()
- if len(res) > 0:
- for fn in res:
- os.unlink(os.path.join(self.internal_dirname, fn[0]))
-
- # remove thumbs records
- sql = """delete from thumbnails where file_id = ?"""
- self.db_cursor.executemany(sql, generator())
- self.db_connection.commit()
- return
-
def __append_added_volume(self):
"""append branch from DB to existing tree model"""
#connect
diff --git a/src/utils/img.py b/src/utils/img.py
new file mode 100644
index 0000000..12cfc4b
--- /dev/null
+++ b/src/utils/img.py
@@ -0,0 +1,156 @@
+# 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 tempfile import gettempdir
+from shutil import move, copy
+from os import path, mkdir
+from datetime import datetime
+
+import EXIF
+import Image
+
+class Img(object):
+ def __init__(self, filename=None, base=''):
+ self.root = 'images'
+ self.x = 160
+ self.y = 160
+ self.filename = filename
+ self.base = base
+
+ def save(self, image_id):
+ """Save image and asociated thumbnail into specific directory structure
+ return full path to the file and thumbnail 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)
+
+ # 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
+
+ if returncode != -1:
+ # copy image
+ copy(self.filename, image_filename)
+ return filepath, image_filename, returncode
+
+ # private class functions
+ def __get_and_make_path(self, img_id):
+ """Make directory structure regards of id
+ and return filepath and img filename WITHOUT extension"""
+ t = path.join(self.base, self.root)
+ try: mkdir(t)
+ except: pass
+
+ h = hex(img_id)
+ if len(h[2:])>6:
+ try: mkdir(path.join(t, h[2:4]))
+ except: pass
+ try: mkdir(path.join(t, h[2:4], h[4:6]))
+ except: pass
+ fpath = path.join(t, h[2:4], h[4:6], h[6:8])
+ try: mkdir(fpath)
+ except: pass
+ img = "%s" % h[8:]
+ elif len(h[2:])>4:
+ try: mkdir(path.join(t, h[2:4]))
+ except: pass
+ fpath = path.join(t, h[2:4], h[4:6])
+ try: mkdir(fpath)
+ except: pass
+ img = "%s" % h[6:]
+ elif len(h[2:])>2:
+ fpath = path.join(t, h[2:4])
+ try: mkdir(fpath)
+ except: pass
+ img = "%s" % h[4:]
+ else:
+ fpath = ''
+ img = "%s" % h[2:]
+ return(path.join(t, fpath, img))
+
+ def __scale_image(self, factor=True):
+ """create thumbnail. returns image object or None"""
+ try:
+ im = Image.open(self.filename).convert('RGB')
+ except:
+ return None
+ im.thumbnail((self.x, self.y), Image.ANTIALIAS)
+ return im
diff --git a/src/views/v_dialogs.py b/src/views/v_dialogs.py
index fb20d98..fdc66d5 100644
--- a/src/views/v_dialogs.py
+++ b/src/views/v_dialogs.py
@@ -128,6 +128,24 @@ class InputDiskLabel(object):
if result == gtk.RESPONSE_OK:
return entry.get_text()
return None
+
+class InputNewName(object):
+ """Sepcific dialog for quering user for a disc label"""
+ def __init__(self, name=""):
+ self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
+ self.label = ""
+ self.name = name
+
+ def run(self):
+ gladexml = gtk.glade.XML(self.gladefile, "renameDialog")
+ dialog = gladexml.get_widget("renameDialog")
+ entry = gladexml.get_widget("name")
+ entry.set_text(self.name)
+ result = dialog.run()
+ dialog.destroy()
+ if result == gtk.RESPONSE_OK:
+ return entry.get_text()
+ return None
class PointDirectoryToAdd(object):
"""Sepcific dialog for quering user for selecting directory to add"""
@@ -276,7 +294,47 @@ class LoadDBFile(object):
a = Err("Error - pyGTKtalog","File doesn't exist.","The file that you choose does not exist. Choose another one, or cancel operation.")
ch = True
res,filename = self.show_dialog()
+
+
+class LoadImageFile(object):
+ """class for displaying openFile dialog. It have possibility of multiple
+ selection."""
+ def __init__(self):
+ self.dialog = gtk.FileChooserDialog(
+ title="Select image",
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(
+ gtk.STOCK_CANCEL,
+ gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OPEN,
+ gtk.RESPONSE_OK
+ )
+ )
+ self.dialog.set_select_multiple(True)
+ self.dialog.set_default_response(gtk.RESPONSE_OK)
+ f = gtk.FileFilter()
+ f.set_name("All Images")
+ for i in ['*.jpg', '*.jpeg', '*.gif', '*.png', '*.tif', '*.tiff', '*.tga', '*.pcx', '*.bmp', '*.xbm', '*.xpm', '*.jp2', '*.jpx', '*.pnm']:
+ f.add_pattern(i)
+ self.dialog.add_filter(f)
+ f = gtk.FileFilter()
+ f.set_name("All files")
+ f.add_pattern("*.*")
+ self.dialog.add_filter(f)
+
+ def run(self):
+ response = self.dialog.run()
+ filenames = None
+
+ if response == gtk.RESPONSE_OK:
+ try:
+ filenames = self.dialog.get_filenames()
+ except:
+ pass
+ self.dialog.destroy()
+ return filenames
+
class StatsDialog(object):
"""Sepcific dialog for display stats"""
def __init__(self, values={}):
diff --git a/src/views/v_image.py b/src/views/v_image.py
new file mode 100644
index 0000000..c792c2c
--- /dev/null
+++ b/src/views/v_image.py
@@ -0,0 +1,43 @@
+# 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
+
+# -------------------------------------------------------------------------
+
+import os.path
+
+import gtk
+
+import utils.globals
+
+class ImageView(object):
+ """simple image viewer. no scaling, no zooming, no rotating.
+ simply show stupid image"""
+ def __init__(self, image_filename):
+ window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ image = gtk.Image()
+ image.set_from_file(image_filename)
+ window.add(image)
+ image.show()
+ window.show()
+ return
+
+ pass # end of class