From a98ba36a23fb95214027881505dda26086917030 Mon Sep 17 00:00:00 2001 From: gryf Date: Wed, 16 Apr 2008 14:07:18 +0000 Subject: [PATCH] * Added drag and drop support from files TreeView to tags cloud. * Added tags add. * Code clean up. * Removed unnecessary imports. * Adapted to PEP8. --- README | 4 +- src/ctrls/c_main.py | 695 ++++++++++++++++++++++++------------------- src/models/m_main.py | 671 ++++++++++++++++++++++------------------- src/views/v_main.py | 6 +- 4 files changed, 764 insertions(+), 612 deletions(-) diff --git a/README b/README index d01e8e9..5bd7f21 100644 --- a/README +++ b/README @@ -60,8 +60,8 @@ TODO 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) +- tagging files (40%) + - user definied group of tags (represented by color in cloud tag) (10%) x file details: x files properties x thumbnail diff --git a/src/ctrls/c_main.py b/src/ctrls/c_main.py index d889837..406ed0c 100644 --- a/src/ctrls/c_main.py +++ b/src/ctrls/c_main.py @@ -23,7 +23,7 @@ # ------------------------------------------------------------------------- __version__ = "0.8" -licence = \ +LICENCE = \ """ GPL v2 http://www.gnu.org/licenses/gpl.txt @@ -34,9 +34,12 @@ from os import popen from utils import deviceHelper from gtkmvc import Controller +from time import time, ctime + from c_config import ConfigController from views.v_config import ConfigView -from models.m_config import ConfigModel +from c_tags import TagsController +from views.v_tags import TagsView import views.v_dialogs as Dialogs @@ -45,51 +48,47 @@ from views.v_image import ImageView import gtk import pango -import datetime - class MainController(Controller): """Controller for main application window""" scan_cd = False - widgets = ( - "discs","files", - 'save1','save_as1','cut1','copy1','paste1','delete1','add_cd','add_directory1', - 'tb_save','tb_addcd','tb_find','nb_dirs','description','stat1', - ) - widgets_all = ( - "discs","files", - 'file1','edit1','add_cd','add_directory1','help1', - 'tb_save','tb_addcd','tb_find','tb_new','tb_open','tb_quit', - 'nb_dirs','description','stat1', - ) - + widgets = ("discs", "files", 'save1', 'save_as1', 'cut1', 'copy1', + 'paste1', 'delete1', 'add_cd', 'add_directory1', 'tb_save', + 'tb_addcd', 'tb_find', 'nb_dirs', 'description', 'stat1') + widgets_all = ("discs", "files", 'file1', 'edit1', 'add_cd', + 'add_directory1', 'help1', 'tb_save', 'tb_addcd', 'tb_find', + 'tb_new', 'tb_open', 'tb_quit', 'nb_dirs', 'description', + 'stat1') + widgets_cancel = ('cancel','cancel1') - + def __init__(self, model): """Initialize controller""" + self.DND_TARGETS = [('files_tags', 0, 69)] Controller.__init__(self, model) return def register_view(self, view): Controller.register_view(self, view) - + # Make widget set non active for widget in self.widgets: self.view[widget].set_sensitive(False) - + # Make not active "Cancel" button and menuitem for widget in self.widgets_cancel: self.view[widget].set_sensitive(False) - + # hide "debug" button, if production # (i.e. python OT running with -OO option) if __debug__: self.view['debugbtn'].show() else: self.view['debugbtn'].hide() - + # load configuration/defaults and set it to properties - self.view['toolbar1'].set_active(self.model.config.confd['showtoolbar']) - if self.model.config.confd['showtoolbar']: + bo = self.model.config.confd['showtoolbar'] + self.view['toolbar1'].set_active(bo) + if bo: self.view['maintoolbar'].show() else: self.view['maintoolbar'].hide() @@ -103,44 +102,87 @@ class MainController(Controller): self.view['vpaned1'].set_position(self.model.config.confd['v']) self.view['main'].resize(self.model.config.confd['wx'], self.model.config.confd['wy']) - + # initialize statusbar - self.context_id = self.view['mainStatus'].get_context_id('detailed res') + context = self.view['mainStatus'].get_context_id('detailed res') + self.context_id = context self.statusbar_id = self.view['mainStatus'].push(self.context_id, "Idle") + # make tag_cloud_textview recive dnd signals + self.view['tag_cloud_textview'].drag_dest_set(gtk.DEST_DEFAULT_ALL, + self.DND_TARGETS, + gtk.gdk.ACTION_COPY) + #ttv.connect('drag_motion', self.on_tag_cloud_textview_drag_motion) + #ttv.connect('drag_drop', self.on_tag_cloud_textview_drag_drop) + # initialize treeviews self.__setup_disc_treeview() self.__setup_files_treeview() self.__setup_exif_treeview() + # in case passing catalog filename in command line, unlock gui if self.model.filename != None: self.__activate_ui(self.model.filename) - + # generate recent menu self.__generate_recent_menu() + # initialoze tag cloud + self.__tag_cloud() + # Show main window self.view['main'].show(); + self.view['main'].drag_dest_set(0, [], 0) return - + ######################################################################### # Connect signals from GUI, like menu objects, toolbar buttons and so on. + def on_tag_cloud_textview_drag_drop(self, wid, context, x, y, time): + #print '\n'.join([str(t) for t in context.targets]) + #print context.drag_get_selection() + #print context.get_source_widget() + #print x, y + #print wid + context.finish(True, False, time) + + return True + + def on_tag_cloud_textview_drag_motion(self, wid, context, x, y, time): + context.drag_status(gtk.gdk.ACTION_COPY, time) + return True + + def on_files_drag_data_get(self, widget, context, selection, + targetType, eventTime): + # get selection, and send it to the client + if targetType == self.DND_TARGETS[0][2]: + str = "1,2,3,4,57,9,0," + selection.set(selection.target, 8, str) + + def on_tag_cloud_textview_drag_data_received(self, widget, context, x, y, + selection, targetType, time): + if targetType == self.DND_TARGETS[0][2]: + print selection.data + print "kupa" + 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" + if __debug__: + print "c_main.py: on_edit2_activate(): 0", + print "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.model.update_desc_and_note(id, + ret['description'], ret['note']) self.__get_item_info(id) self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) @@ -158,15 +200,18 @@ class MainController(Controller): self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) except: - if __debug__: print "c_main.py: on_add_thumb1_activate(): error on getting selected items or creating thumbnails" + if __debug__: + print "c_main.py: on_add_thumb1_activate(): error on getting", + print "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.') + "Thumbnails for selected items will be \ + permanently removed from catalog.") if not obj.run(): return try: @@ -176,18 +221,21 @@ class MainController(Controller): 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" + if __debug__: + print "c_main.py: on_remove_thumb1_activate(): error on", + print "getting selected items or removing thumbnails" return - + self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) 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.') + 'All images for selected items will be \ + permanently removed from catalog.') if not obj.run(): return try: @@ -197,41 +245,46 @@ class MainController(Controller): 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" + if __debug__: + print "c_main.py: on_remove_thumb1_activate(): error on", + print "getting selected items or removing thumbnails" return - + self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) 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) img = self.model.get_image_path(id) if img: - if self.model.config.confd['imgview'] and len(self.model.config.confd['imgprog'])>0: + 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) else: - Dialogs.Inf("Image view", "No Image", "This item have no real image, only thumbnail.") - + Dialogs.Inf("Image view", "No Image", + "This item have no real image, only thumbnail.") + def on_rename1_activate(self, widget): model, iter = self.view['discs'].get_selection().get_selected() name = model.get_value(iter, 1) 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) self.__set_title(filepath=self.model.filename, modified=True) self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) - + def on_rename2_activate(self, widget): try: selection = self.view['files'].get_selection() @@ -241,17 +294,18 @@ class MainController(Controller): 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 __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() iter = model.get_iter(path) @@ -259,55 +313,92 @@ class MainController(Controller): except TypeError: self.model.get_root_entries(1) return - + self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) 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) - + def on_main_destroy_event(self, window, event): - self.__do_quit() + self.on_quit1_activate(widget) return True - + def on_tb_quit_clicked(self, widget): - self.__do_quit() - + self.on_quit1_activate(widget) + def on_quit1_activate(self, widget): - self.__do_quit() - + """Quit and save window parameters to config file""" + # check if any unsaved project is on go. + if self.model.unsaved_project and \ + self.model.config.confd['confirmquit']: + if not Dialogs.Qst('Quit application - pyGTKtalog', + 'Do you really want to quit?', + "Current database is not saved, any changes \ + will be lost.").run(): + return + self.__store_settings() + self.model.cleanup() + gtk.main_quit() + return False + + def on_new1_activate(self, widget): self.__new_db() - + def on_tb_new_clicked(self, widget): self.__new_db() - - def on_add_cd_activate(self, widget): - self.__add_cd() - + + 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']) + if mount == 'ok': + guessed_label = deviceHelper.volname(self.model.config.confd['cd']) + if not label: + label = Dialogs.InputDiskLabel(guessed_label).run() + if label != None: + self.scan_cd = True + 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, + current_id) + self.model.unsaved_project = True + self.__set_title(filepath=self.model.filename, modified=True) + else: + deviceHelper.volumount(self.model.config.confd['cd']) + return True + else: + Dialogs.Wrn("Error mounting device - pyGTKtalog", + "Cannot mount device pointed to %s" % + self.model.config.confd['cd'], + "Last mount message:\n%s" % mount) + return False + def on_tb_addcd_clicked(self, widget): - self.__add_cd() - + self.on_add_cd_activate(widget) + def on_add_directory1_activate(self, widget): """Show dialog for choose drectory to add from filesystem.""" self.__add_directory() return - + def on_about1_activate(self, widget): """Show about dialog""" Dialogs.Abt("pyGTKtalog", __version__, "About", - ["Roman 'gryf' Dobosz"], licence) + ["Roman 'gryf' Dobosz"], LICENCE) return - + def on_preferences_activate(self, widget): c = ConfigController(self.model.config) v = ConfigView(c) return - + def on_status_bar1_activate(self, widget): """Toggle visibility of statusbat and progress bar.""" activity = self.view['status_bar1'].get_active() @@ -316,7 +407,7 @@ class MainController(Controller): self.view['statusprogress'].show() else: self.view['statusprogress'].hide() - + def on_toolbar1_activate(self, widget): """Toggle visibility of toolbar bar.""" activity = self.view['toolbar1'].get_active() @@ -325,19 +416,33 @@ class MainController(Controller): self.view['maintoolbar'].show() else: self.view['maintoolbar'].hide() - + def on_save1_activate(self, widget): - self.__save() - + """Save catalog to file""" + if self.model.filename: + self.model.save() + self.__set_title(filepath=self.model.filename) + else: + self.on_save_as1_activate(widget) + def on_tb_save_clicked(self, widget): - self.__save() - + self.on_save1_activate(widget) + def on_save_as1_activate(self, widget): - self.__save_as() - + """Save database to file under different filename.""" + path = Dialogs.ChooseDBFilename().run() + if path: + ret, err = self.model.save(path) + if ret: + self.model.config.add_recent(path) + self.__set_title(filepath=path) + else: + Dialogs.Err("Error writing file - pyGTKtalog", + "Cannot write file %s." % path, "%s" % err) + def on_stat1_activate(self, menu_item): self.__show_stats() - + def on_statistics1_activate(self, menu_item): model = self.view['discs'].get_model() try: @@ -345,16 +450,36 @@ class MainController(Controller): selected_iter = self.model.discs_tree.get_iter(path) except: return - + selected_id = self.model.discs_tree.get_value(selected_iter, 0) self.__show_stats(selected_id) - + def on_tb_open_clicked(self, widget): - self.__open() - - def on_open1_activate(self, widget): - self.__open() - + self.on_open1_activate(widget) + return + + def on_open1_activate(self, widget, path=None): + """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.') + if not obj.run(): + return + + if not path: + path = Dialogs.LoadDBFile().run() + + if path: + if not self.model.open(path): + Dialogs.Err("Error opening file - pyGTKtalog", + "Cannot open file %s." % path) + else: + self.__generate_recent_menu() + self.__activate_ui(path) + return + def on_discs_cursor_changed(self, widget): """Show files on right treeview, after clicking the left disc treeview.""" @@ -363,10 +488,10 @@ class MainController(Controller): iter = self.model.discs_tree.get_iter(path) selected_item = self.model.discs_tree.get_value(iter,0) self.model.get_root_entries(selected_item) - + self.__get_item_info(selected_item) return - + def on_discs_row_activated(self, treeview, path, treecolumn): """If possible, expand or collapse branch of discs tree""" if treeview.row_expanded(path): @@ -374,27 +499,28 @@ 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)) + 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): if self.model.config.confd['delwarn']: obj = Dialogs.Qst('Delete image', 'Delete image?', - 'Selected image will be permanently removed from catalog.') + 'Selected image will be permanently removed from catalog.') if not obj.run(): return list_of_paths = self.view['images'].get_selected_items() @@ -402,7 +528,7 @@ class MainController(Controller): 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() @@ -411,18 +537,18 @@ class MainController(Controller): self.__get_item_info(id) except: pass - + self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) return - + 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), @@ -430,7 +556,7 @@ class MainController(Controller): except TypeError: treeview.get_selection().unselect_all() return False - + if event.button == 3: """Right mouse button. Show context menu.""" try: @@ -438,11 +564,11 @@ class MainController(Controller): model, list_of_paths = selection.get_selected_rows() except TypeError: list_of_paths = [] - + if path not in list_of_paths: treeview.get_selection().unselect_all() treeview.get_selection().select_path(path) - + iter = self.model.discs_tree.get_iter(path) if self.model.discs_tree.get_value(iter, 3) == 1: # if ancestor is 'root', then activate "update" menu item @@ -450,15 +576,15 @@ class MainController(Controller): else: self.view['update1'].set_sensitive(False) self.__popup_menu(event) - + def on_expand_all1_activate(self, menuitem): self.view['discs'].expand_all() return - + def on_collapse_all1_activate(self, menuitem): self.view['discs'].collapse_all() return - + def on_files_button_press_event(self, tree, event): try: path, column, x, y = tree.get_path_at_pos(int(event.x), @@ -466,17 +592,17 @@ class MainController(Controller): except TypeError: tree.get_selection().unselect_all() return False - + if event.button == 3: # Right mouse button. Show context menu. try: selection = tree.get_selection() model, list_of_paths = selection.get_selected_rows() except TypeError: list_of_paths = [] - + 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) @@ -487,7 +613,9 @@ class MainController(Controller): self.view['edit2'].set_sensitive(True) self.__popup_menu(event, 'files_popup') return True - + if event.button == 1: + return False + def on_files_cursor_changed(self, treeview): """Show details of selected file/directory""" model, paths = treeview.get_selection().get_selected_rows() @@ -497,9 +625,11 @@ 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", + print "iterator" return - + def on_files_key_release_event(self, a, event): if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': d_path, d_column = self.view['discs'].get_cursor() @@ -516,8 +646,8 @@ class MainController(Controller): first_iter = f_model.get_iter_first() first_child_value = f_model.get_value(first_iter, 0) # get two steps up - value = self.model.get_parent_discs_value(first_child_value) - parent_value = self.model.get_parent_discs_value(value) + val = self.model.get_parent_discs_value(first_child_value) + parent_value = self.model.get_parent_discs_value(val) iter = self.model.discs_tree.get_iter_first() while iter: current_value = self.model.discs_tree.get_value(iter,0) @@ -527,22 +657,22 @@ class MainController(Controller): iter = None else: iter = self.model.discs_tree.iter_next() - + def on_files_row_activated(self, files_obj, row, column): """On directory doubleclick in files listview dive into desired branch.""" f_iter = self.model.files_list.get_iter(row) current_id = self.model.files_list.get_value(f_iter,0) - + if self.model.files_list.get_value(f_iter,4) == 1: # ONLY directories. files are omitted. self.model.get_root_entries(current_id) - + d_path, d_column = self.view['discs'].get_cursor() if d_path!=None: if not self.view['discs'].row_expanded(d_path): self.view['discs'].expand_row(d_path,False) - + iter = self.model.discs_tree.get_iter(d_path) new_iter = self.model.discs_tree.iter_children(iter) if new_iter: @@ -553,34 +683,55 @@ class MainController(Controller): self.view['discs'].set_cursor(path) new_iter = self.model.discs_tree.iter_next(new_iter) return - + def on_cancel1_activate(self, widget): self.__abort() - + def on_cancel_clicked(self, widget): self.__abort() - + def on_tb_find_clicked(self, widget): # TODO: implement searcher return - + def recent_item_response(self, path): - self.__open(path) + self.on_open1_activate(widget) return - + def on_add_tag1_activate(self, menu_item): - print self.view['files'].get_cursor() + #try: + selection = self.view['files'].get_selection() + model, list_of_paths = selection.get_selected_rows() + tags = Dialogs.TagsDialog().run() + if not tags: + return + + for path in list_of_paths: + id = model.get_value(model.get_iter(path),0) + self.model.add_tags(id, tags) + #except: + # if __debug__: + # print "c_main.py: on_remove_thumb1_activate(): error on", + # print "getting selected items or removing thumbnails" + # return + self.__tag_cloud() + self.model.unsaved_project = True + self.__set_title(filepath=self.model.filename, modified=True) + self.__get_item_info(id) + return + def on_add_image1_activate(self, menu_item): dialog = Dialogs.LoadImageFile(True) - toggle = gtk.CheckButton("Don't copy images. Generate only thumbnails.") + toggle = gtk.CheckButton("Don't copy images. \ + Generate only thumbnails.") toggle.show() dialog.dialog.set_extra_widget(toggle) - + images, only_thumbs = dialog.run() if not images: return - + for image in images: try: selection = self.view['files'].get_selection() @@ -595,74 +746,73 @@ class MainController(Controller): except: return self.model.add_image(image, id, only_thumbs) - + self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) - + 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 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: - self.__add_cd(label, fid) - + self.on_add_cd_activate(widget, label, fid) elif self.model.get_source(path) == self.model.DR: self.__add_directory(filepath, label, fid) - + return - + def on_delete2_activate(self, menu_item): try: - selection = self.view['discs'].get_selection() + selection = self.view['discs'].get_selection() model, selected_iter = selection.get_selected() except: return - + if self.model.config.confd['delwarn']: 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 - + # 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 self.model.delete(current_id) - + # refresh files treeview try: current_id = model.get_value(model.get_iter(path), 0) except: current_id = model.get_value(model.get_iter_first(), 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: @@ -672,16 +822,17 @@ class MainController(Controller): return if self.model.config.confd['delwarn']: - obj = Dialogs.Qst('Delete elements', 'Delete items?', - 'Items will be permanently removed from catalog.') + obj = Dialogs.Qst("Delete elements", "Delete items?", + "Items will be permanently \ + removed from catalog.") 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: @@ -690,33 +841,35 @@ class MainController(Controller): 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)) + iter = model.get_iter(list_of_paths[0]) + self.model.get_root_entries(model.get_value(iter,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): if self.model.config.confd['delwarn']: obj = Dialogs.Qst('Delete thumbnail', 'Delete thumbnail?', - 'Current thumbnail will be permanently removed from catalog.') + "Current thumbnail will be permanently removed\ + from catalog.") if not obj.run(): return path, column = self.view['files'].get_cursor() @@ -729,7 +882,7 @@ class MainController(Controller): self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) return - + def on_debugbtn_clicked(self, widget): """Debug. To remove in stable version, including button in GUI""" if __debug__: @@ -742,7 +895,7 @@ class MainController(Controller): print "abort = %s" % self.model.abort print "self.model.config.recent = %s" % self.model.config.recent print "source: %s" % self.model.source - + ##################### # observed properetis def property_statusmsg_value_change(self, model, old, new): @@ -751,7 +904,7 @@ class MainController(Controller): self.statusbar_id = self.view['mainStatus'].push(self.context_id, "%s" % new) return - + def property_busy_value_change(self, model, old, new): if new != old: for w in self.widgets_all: @@ -778,80 +931,13 @@ class MainController(Controller): self.model.config.confd['cd'], "Last umount message:\n%s" % msg) return - + def property_progress_value_change(self, model, old, new): self.view['progressbar1'].set_fraction(new) return - + ######################### # private class functions - def __open(self, path=None): - """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.') - if not obj.run(): - return - - if not path: - path = Dialogs.LoadDBFile().run() - - if path: - if not self.model.open(path): - Dialogs.Err("Error opening file - pyGTKtalog","Cannot open \ - file %s." % path) - else: - self.__generate_recent_menu() - self.__activate_ui(path) - return - - def __save(self): - """Save catalog to file""" - if self.model.filename: - self.model.save() - self.__set_title(filepath=self.model.filename) - else: - self.__save_as() - pass - - def __save_as(self): - """Save database to file under different filename.""" - path = Dialogs.ChooseDBFilename().run() - if path: - ret, err = self.model.save(path) - if ret: - self.model.config.add_recent(path) - self.__set_title(filepath=path) - else: - Dialogs.Err("Error writing file - pyGTKtalog","Cannot write \ - file %s." % path, "%s" % err) - - 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': - guessed_label = deviceHelper.volname(self.model.config.confd['cd']) - if not label: - label = Dialogs.InputDiskLabel(guessed_label).run() - if label != None: - self.scan_cd = True - 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, - current_id) - self.model.unsaved_project = True - self.__set_title(filepath=self.model.filename, modified=True) - else: - deviceHelper.volumount(self.model.config.confd['cd']) - return True - else: - Dialogs.Wrn("Error mounting device - pyGTKtalog", - "Cannot mount device pointed to %s" % - self.model.config.confd['cd'], - "Last mount message:\n%s" % mount) - return False - def __add_directory(self, path=None, label=None, current_id=None): if not label or not path: res = Dialogs.PointDirectoryToAdd().run() @@ -860,55 +946,38 @@ class MainController(Controller): label = res[0] else: return False - + self.scan_cd = False self.model.source = self.model.DR self.model.scan(path, label, current_id) self.model.unsaved_project = True self.__set_title(filepath=self.model.filename, modified=True) return True - - def __do_quit(self): - """Quit and save window parameters to config file""" - # check if any unsaved project is on go. - if self.model.unsaved_project and \ - self.model.config.confd['confirmquit']: - if not Dialogs.Qst('Quit application - pyGTKtalog', - 'Do you really want to quit?', - "Current database is not saved, any changes will be lost.").run(): - return - - self.__store_settings() - self.model.cleanup() - gtk.main_quit() - return False def __new_db(self): - - self.__tag_cloud() - """Create new database file""" if self.model.unsaved_project: if not Dialogs.Qst('Unsaved data - pyGTKtalog', "Current database isn't saved", - 'All changes will be lost. Do you really want to abandon it?').run(): + "All changes will be lost. Do you really \ + want to abandon it?").run(): return self.model.new() - + # clear "details" buffer buf = self.view['description'].get_buffer() buf.set_text("") self.view['description'].set_buffer(buf) self.__activate_ui() - + return - + def __setup_disc_treeview(self): """Setup TreeView discs widget as tree.""" self.view['discs'].set_model(self.model.discs_tree) - + c = gtk.TreeViewColumn('Filename') - + # one row contains image and text cellpb = gtk.CellRendererPixbuf() cell = gtk.CellRendererText() @@ -916,25 +985,26 @@ class MainController(Controller): c.pack_start(cell, True) c.set_attributes(cellpb, stock_id=2) c.set_attributes(cell, text=1) - + self.view['discs'].append_column(c) - + # registration of treeview signals: - + 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_files_treeview(self): """Setup TreeView files widget, as columned list.""" - self.view['files'].set_model(self.model.files_list) - - self.view['files'].get_selection().set_mode(gtk.SELECTION_MULTIPLE) - + v = self.view['files'] + v.set_model(self.model.files_list) + + v.get_selection().set_mode(gtk.SELECTION_MULTIPLE) + c = gtk.TreeViewColumn('Filename') cellpb = gtk.CellRendererPixbuf() cell = gtk.CellRendererText() @@ -942,42 +1012,48 @@ class MainController(Controller): c.pack_start(cell, True) c.set_attributes(cellpb, stock_id=6) c.set_attributes(cell, text=1) - + c.set_sort_column_id(1) c.set_resizable(True) self.view['files'].append_column(c) - + c = gtk.TreeViewColumn('Size',gtk.CellRendererText(), text=2) c.set_sort_column_id(2) c.set_resizable(True) self.view['files'].append_column(c) - + c = gtk.TreeViewColumn('Date',gtk.CellRendererText(), text=3) c.set_sort_column_id(3) c.set_resizable(True) self.view['files'].append_column(c) - return + #v.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, + # self.DND_TARGETS, + # gtk.gdk.ACTION_DEFAULT) + v.drag_source_set(gtk.gdk.BUTTON1_MASK, self.DND_TARGETS, + gtk.gdk.ACTION_COPY) + return + def __setup_exif_treeview(self): self.view['exif_tree'].set_model(self.model.exif_list) - + c = gtk.TreeViewColumn('EXIF key',gtk.CellRendererText(), text=0) c.set_sort_column_id(0) c.set_resizable(True) self.view['exif_tree'].append_column(c) - + c = gtk.TreeViewColumn('EXIF value',gtk.CellRendererText(), text=1) c.set_sort_column_id(1) c.set_resizable(True) self.view['exif_tree'].append_column(c) return - + def __abort(self): """When scanning thread is runing and user push the cancel button, models abort attribute trigger cancelation for scan operation""" self.model.abort = True return - + def __activate_ui(self, name=None): """Make UI active, and set title""" self.model.unsaved_project = False @@ -991,21 +1067,21 @@ class MainController(Controller): while gtk.events_pending(): gtk.main_iteration() return - + def __set_title(self, filepath=None, modified=False): """Set main window title""" if modified: mod = " *" else: mod = "" - + if filepath: self.view['main'].set_title("%s - pyGTKtalog%s" % (os.path.basename(filepath), mod)) else: self.view['main'].set_title("untitled - pyGTKtalog%s" % mod) return - + def __store_settings(self): """Store window size and pane position in config file (using config object from model)""" @@ -1017,13 +1093,13 @@ class MainController(Controller): self.model.config.confd['v'] = self.view['vpaned1'].get_position() self.model.config.save() return - + def __popup_menu(self, event, menu='discs_popup'): self.view[menu].popup(None, None, None, event.button, event.time) self.view[menu].show_all() return - + def __generate_recent_menu(self): self.recent_menu = gtk.Menu() for i in self.model.config.recent: @@ -1034,13 +1110,13 @@ class MainController(Controller): item.show() self.view['recent_files1'].set_submenu(self.recent_menu) return - + def __get_item_info(self, item): self.view['description'].show() set = self.model.get_file_info(item) buf = gtk.TextBuffer() - - if __debug__ and set.has_key('debug'): + + if __debug__ and 'debug' in set: tag = buf.create_tag() tag.set_property('weight', pango.WEIGHT_BOLD) buf.insert_with_tags(buf.get_end_iter(), "ID: ", tag) @@ -1053,8 +1129,8 @@ class MainController(Controller): 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'): + + if 'gthumb' in set: tag = buf.create_tag() tag.set_property('weight', pango.WEIGHT_BOLD) buf.insert_with_tags(buf.get_end_iter(), "gThumb comment:\n", tag) @@ -1065,61 +1141,62 @@ class MainController(Controller): 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'): + + if 'description' in set: 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'): + + if 'note' in set: 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'): + + if 'images' in set: self.__setup_iconview() self.view['img_container'].show() else: self.view['img_container'].hide() - - if set.has_key('exif'): + + if 'exif' in set: self.view['exif_tree'].set_model(self.model.exif_list) self.view['exifinfo'].show() else: self.view['exifinfo'].hide() - - if set.has_key('thumbnail'): + + if 'thumbnail' in set: self.view['thumb'].set_from_file(set['thumbnail']) self.view['thumb_box'].show() - #self.view['thumb'].show() else: - #self.view['thumb'].hide() self.view['thumb_box'].hide() return - + ''' + def on_tag_cloud_textview_drag_motion(self, widget, context, x, y, time): + context.drag_status(gtk.gdk.ACTION_COPY, time) + print "motion", x, y +''' def __tag_cloud(self): """generate tag cloud""" # TODO: checkit! + v = self.view['tag_cloud_textview'] def tag_cloud_click(tag, textview, event, iter, e): """react on click on connected tag items""" if event.type == gtk.gdk.BUTTON_RELEASE: print tag.get_property('name') elif event.type == gtk.gdk.MOTION_NOTIFY: - w = \ - self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT) + w = v.get_window(gtk.TEXT_WINDOW_TEXT) if w: w.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) else: - w = \ - self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT) + w = v.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(): return iter @@ -1127,20 +1204,24 @@ class MainController(Controller): b.insert(iter, " ") iter = b.get_end_iter() return iter - + if len(self.model.tag_cloud) > 0: - buff = gtk.TextBuffer() + buff = v.get_buffer() + buff.set_text('') for cloud in self.model.tag_cloud: iter = insert_blank(buff, buff.get_end_iter()) - tag = buff.create_tag(cloud['id']) + tag = buff.create_tag(str(cloud['id'])) tag.set_property('size-points', cloud['size']) tag.set_property('foreground', cloud['color']) tag.connect('event', tag_cloud_click, tag) buff.insert_with_tags(iter, cloud['name'], tag) - self.view['tag_cloud_textview'].set_buffer(buff) - + v.set_buffer(buff) + def __show_stats(self, selected_id=None): data = self.model.get_stats(selected_id) label = Dialogs.StatsDialog(data).run() + def __find_tag_in_textview(self, widget, x, y): + pass + pass # end of class diff --git a/src/models/m_main.py b/src/models/m_main.py index ecc0a07..dd0a13e 100644 --- a/src/models/m_main.py +++ b/src/models/m_main.py @@ -24,11 +24,10 @@ import os import sys -import base64 import shutil import tarfile -import tempfile import string +import math import gtk import gobject @@ -38,10 +37,7 @@ from gtkmvc.model_mt import ModelMT from pysqlite2 import dbapi2 as sqlite from datetime import datetime -try: - import threading as _threading -except ImportError: - import dummy_threading as _threading +import threading as _threading from m_config import ConfigModel try: @@ -54,19 +50,19 @@ from utils.gthumb import GthumbCommentParser class MainModel(ModelMT): """Create, load, save, manipulate db file which is container for data""" - + __properties__ = {'busy': False, 'statusmsg': '', 'progress': 0} - + # constants instead of dictionary tables # type of files LAB = 0 # root of the tree - label/collection name DIR = 1 # directory FIL = 2 # file LIN = 3 # symbolic link - + CD = 1 # sorce: cd/dvd DR = 2 # source: filesystem - + EXIF_DICT= {0: 'Camera', 1: 'Date', 2: 'Aperture', @@ -80,11 +76,11 @@ class MainModel(ModelMT): 10: 'Light source', 11: 'Resolution', 12: 'Orientation'} - + # images extensions - only for PIL and EXIF IMG = ['jpg', 'jpeg', 'gif', 'png', 'tif', 'tiff', 'tga', 'pcx', 'bmp', 'xbm', 'xpm', 'jp2', 'jpx', 'pnm'] - + def __init__(self): ModelMT.__init__(self) self.config = ConfigModel() @@ -96,7 +92,7 @@ class MainModel(ModelMT): self.abort = False self.source = self.CD self.config.load() - + # Directory tree: id, name, icon, type self.discs_tree = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING, str, gobject.TYPE_INT) @@ -108,11 +104,11 @@ class MainModel(ModelMT): gobject.TYPE_STRING, str) # iconview store - id, pixbuffer self.images_store = gtk.ListStore(gobject.TYPE_INT, gtk.gdk.Pixbuf) - + # exif liststore - id, exif key, exif value self.exif_list = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING) - + # tag cloud array element is a dict with 4 keys: # elem = {'id': str(id), 'name': tagname, 'size': size, 'color': color} # where color is in one of format: @@ -120,33 +116,91 @@ class MainModel(ModelMT): # - #rgb # - #rrggbb self.tag_cloud = [] - '''{'id': str(1), 'name': "bezpieczeństwo", 'size': 10, 'color': '#666'}, - {'id': str(2), 'name': "bsd", 'size': 14, 'color': '#333'}, - {'id': str(3), 'name': "debian", 'size': 18, 'color': '#333'}, - {'id': str(4), 'name': "fedora", 'size': 12, 'color': '#666'}, - {'id': str(5), 'name': "firefox", 'size': 40, 'color': '#666'}, - {'id': str(6), 'name': "gnome", 'size': 26, 'color': '#333'}, - {'id': str(7), 'name': "gentoo", 'size': 30, 'color': '#000'}, - {'id': str(8), 'name': "kde", 'size': 20, 'color': '#333'}, - {'id': str(9), 'name': "kernel", 'size': 10, 'color': '#666'}, - {'id': str(10), 'name': "windows", 'size': 18, 'color': '#333'}, - ]''' return + def add_tags(self, id, tags): + for tag in tags.split(','): + tag = tag.strip() + + # first, checkerap if we already have tag in tags table + sql = """SELECT id FROM tags WHERE tag = ?""" + self.db_cursor.execute(sql, (tag, )) + res = self.db_cursor.fetchone() + if not res: + # insert new tag + sql = """INSERT INTO tags(tag, group_id) + VALUES(?, ?)""" + self.db_cursor.execute(sql, (tag, 1)) + self.db_connection.commit() + sql = """SELECT id FROM tags WHERE tag = ?""" + self.db_cursor.execute(sql, (tag, )) + res = self.db_cursor.fetchone() + + tag_id = res[0] + + # then checkout if file have already tag assigned + sql = """SELECT file_id FROM tags_files + WHERE file_id = ? AND tag_id = ?""" + self.db_cursor.execute(sql, (id, tag_id)) + res = self.db_cursor.fetchone() + if not res: + sql = """INSERT INTO tags_files(file_id, tag_id) + VALUES(?, ?)""" + self.db_cursor.execute(sql, (id, tag_id)) + self.db_connection.commit() + self.get_tags() + return + + def get_tags(self): + sql = """SELECT COUNT(f.file_id), t.id, t.tag FROM tags_files f + LEFT JOIN tags t ON f.tag_id = t.id + GROUP BY f.tag_id + ORDER BY t.tag""" + self.db_cursor.execute(sql) + res = self.db_cursor.fetchall() + + if len(res) > 0: + self.tag_cloud = [] + for row in res: + self.tag_cloud.append({'id': row[1], + 'name': row[2], + 'size': row[0], + 'color':'black'}) + + def tag_weight(x): + """Calculate 'weight' of tag. + Tags can have sizes between 9 to ~40. Upper size is calculated with + logarythm and can take in extereme situation around value 55 like + for 1 milion tags.""" + if x==None or x==0: + x = 1 + return 4 * math.log(x, math.e) + + # correct font sizes with tag_weight function. + count = 0 + for ta in self.tag_cloud: + tmp = int(tag_weight(ta['size'])) + if tmp == 0: + tmp = 1 + self.tag_cloud[count]['size'] = tmp + 8 + count+=1 + def add_image(self, image, id, only_thumbs=False): """add single image to file/directory""" - sql = """insert into images(file_id, thumbnail, filename) - values(?, null, null)""" + 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=?""" + + 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=?""" + sql = """UPDATE images SET filename=?, + thumbnail=? WHERE id=?""" if only_thumbs: img = None else: @@ -156,11 +210,11 @@ class MainModel(ModelMT): tp.split(self.internal_dirname)[1][1:], res[0])) 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 =?""" + sql = """SELECT filename, thumbnail FROM images WHERE file_id =?""" self.db_cursor.execute(sql, (id,)) res = self.db_cursor.fetchall() if len(res) > 0: @@ -168,31 +222,31 @@ class MainModel(ModelMT): if fn[0]: 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 = ?""" + 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=?""" + sql = """SELECT filename, thumbnail FROM images WHERE id=?""" self.db_cursor.execute(sql, (id,)) res = self.db_cursor.fetchone() if res: if res[0]: os.unlink(os.path.join(self.internal_dirname, res[0])) os.unlink(os.path.join(self.internal_dirname, res[1])) - + if __debug__: print "m_main.py: delete_image(): removed images:" print res[0] print res[1] # remove images records - sql = """delete from images where id = ?""" + 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']: @@ -200,29 +254,30 @@ class MainModel(ModelMT): 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 (?, ?)""" + 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=?""" + 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=?""" + 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: @@ -231,37 +286,37 @@ class MainModel(ModelMT): except: pass return - + def new(self): self.unsaved_project = False self.__create_internal_dirname() self.__connect_to_db() self.__create_database() - + self.__clear_trees() - + return - + def save(self, filename=None): """save tared directory at given catalog fielname""" if not filename and not self.filename: if __debug__: return False, "no filename detected!" return - + if filename: self.filename = filename val, err = self.__compress_and_save() if not val: self.filename = None return val, err - + def open(self, filename=None): """try to open db file""" self.unsaved_project = False self.__create_internal_dirname() self.filename = filename - + try: tar = tarfile.open(filename, "r:gz") except: @@ -271,12 +326,13 @@ class MainModel(ModelMT): self.filename = None self.internal_dirname = None return - + os.chdir(self.internal_dirname) try: tar.extractall() if __debug__: - print "m_main.py: extracted tarfile into", self.internal_dirname + print "m_main.py: extracted tarfile into", + print self.internal_dirname except AttributeError: # python's 2.4 tarfile module lacks of method extractall() directories = [] @@ -305,36 +361,37 @@ class MainModel(ModelMT): os.chmod(tarinfo, '.') except: if __debug__: - print "m_main.py: open(): setting corrext owner, mtime etc" + print "m_main.py: open(): setting corrext owner,", + print "mtime etc" pass tar.close() - + self.__connect_to_db() self.__fetch_db_into_treestore() self.config.add_recent(filename) - + self.get_tags() return True - + def scan(self, path, label, currentid): """scan files in separated thread""" - + # flush buffer to release db lock. self.db_connection.commit() - + self.path = path self.label = label self.currentid = currentid - + if self.busy: return self.thread = _threading.Thread(target=self.__scan) self.thread.start() return - + def rename(self, id, new_name=None): if new_name: self.db_cursor.execute("update files set filename=? \ - where id=?", (new_name, id)) + WHERE id=?", (new_name, id)) self.db_connection.commit() self.__fetch_db_into_treestore() self.unsaved_project = True @@ -342,21 +399,22 @@ class MainModel(ModelMT): if __debug__: 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: self.files_list.clear() except: pass - + # directories first - self.db_cursor.execute("SELECT id, filename, size, date FROM files \ - WHERE parent_id=? AND type=1 \ - ORDER BY filename", (id,)) + sql = """SELECT id, filename, size, date FROM files + WHERE parent_id=? AND type=1 + ORDER BY filename""" + self.db_cursor.execute(sql, (id,)) data = self.db_cursor.fetchall() for ch in data: myiter = self.files_list.insert_before(None, None) @@ -368,12 +426,13 @@ class MainModel(ModelMT): self.files_list.set_value(myiter, 4, 1) self.files_list.set_value(myiter, 5, 'direktorja') self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY) - + # all the rest - self.db_cursor.execute("SELECT f.id, f.filename, f.size, f.date, \ - f.type FROM files f \ - WHERE f.parent_id=? AND f.type!=1 \ - ORDER BY f.filename", (id,)) + sql = """SELECT f.id, f.filename, f.size, f.date, f.type + FROM files f + WHERE f.parent_id=? AND f.type!=1 + ORDER BY f.filename""" + self.db_cursor.execute(sql, (id,)) data = self.db_cursor.fetchall() for ch in data: myiter = self.files_list.insert_before(None, None) @@ -388,15 +447,16 @@ class MainModel(ModelMT): elif ch[4] == self.LIN: self.files_list.set_value(myiter, 6, gtk.STOCK_INDEX) return - + def get_parent_discs_value(self, child_id): if child_id: - self.db_cursor.execute("SELECT parent_id FROM files where id=?", (child_id,)) + sql = """SELECT parent_id FROM files WHERE id=?""" + self.db_cursor.execute(sql, (child_id,)) set = self.db_cursor.fetchone() if set: return set[0] return None - + def get_file_info(self, id): """get file info from database""" retval = {} @@ -411,19 +471,21 @@ class MainModel(ModelMT): 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]) - - sql = """SELECT id, filename, thumbnail from images WHERE file_id = ?""" + pa = os.path.join(self.internal_dirname, set[4]) + retval['thumbnail'] = pa + + sql = """SELECT id, filename, thumbnail FROM images + WHERE file_id = ?""" self.db_cursor.execute(sql, (id,)) set = self.db_cursor.fetchall() if set: @@ -433,11 +495,11 @@ class MainModel(ModelMT): pix = gtk.gdk.pixbuf_new_from_file(im) self.images_store.append([idi, pix]) retval['images'] = True - + sql = """SELECT camera, date, aperture, exposure_program, exposure_bias, iso, focal_length, subject_distance, metering_mode, flash, light_source, resolution, orientation - from exif + FROM exif WHERE file_id = ?""" self.db_cursor.execute(sql, (id,)) set = self.db_cursor.fetchone() @@ -450,92 +512,95 @@ class MainModel(ModelMT): 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 = ?""" + 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]} - + retval['gthumb'] = {'note': set[0], + 'place': set[1], + 'date': set[2]} + return retval - + def get_source(self, path): """get source of top level directory""" - bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]), - 0) - self.db_cursor.execute("select source from files where id = ?", + bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]), 0) + sql = """SELECT source FROM files WHERE id = ?""" + self.db_cursor.execute(sql, (bid,)) res = self.db_cursor.fetchone() if res == None: return False return int(res[0]) - + 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) - self.db_cursor.execute("select filepath, filename from files \ - where id = ? and parent_id = 1", (bid,)) + bid = self.discs_tree.get_value(self.discs_tree.get_iter(path), 0) + sql = """SELECT filepath, filename FROM files + WHERE id = ? AND parent_id = 1""" + self.db_cursor.execute(sql, (bid,)) res = self.db_cursor.fetchone() if res == None: return None, None return res[0], res[1] - + 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, images, gthumb info, exif etc""" - + fids = [] - + if not db_cursor: db_cursor = self.db_cursor - + if not db_connection: db_connection = self.db_connection - - sql = """select parent_id from files where id = ?""" + + sql = """SELECT parent_id FROM files WHERE id = ?""" db_cursor.execute(sql, (root_id,)) res = db_cursor.fetchone() parent_id = res[0] - + def get_children(fid): fids.append(fid) - sql = """select id from files where parent_id = ?""" + 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 = ?""" + sql = """DELETE FROM files WHERE id = ?""" db_cursor.executemany(sql, generator()) - + # remove tags records - sql = """delete from tags_files where file_id = ?""" + sql = """DELETE FROM tags_files WHERE file_id = ?""" db_cursor.executemany(sql, generator()) - - arg = self.__list_to_string(fids) + + arg = str(tuple(fids)) # remove thumbnails - sql = """select filename from thumbnails where file_id in (%s)""" % arg + 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 + sql = """SELECT filename, thumbnail FROM images + WHERE file_id IN %s""" % arg db_cursor.execute(sql) res = db_cursor.fetchall() if len(res) > 0: @@ -543,173 +608,182 @@ class MainModel(ModelMT): if res[0]: 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 = ?""" + sql = """DELETE FROM thumbnails WHERE file_id = ?""" db_cursor.executemany(sql, generator()) - + # remove images records - sql = """delete from images where file_id = ?""" + sql = """DELETE FROM images WHERE file_id = ?""" db_cursor.executemany(sql, generator()) - + # remove gthumb info - sql = """delete from gthumb where file_id = ?""" + 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: - sql = """update files set size = - (select case when - sum(size) is null - then + sql = """UPDATE files SET size = + (SELECT CASE WHEN + SUM(size) IS null + THEN 0 - else - sum(size) - end - from files where parent_id=?) where id=?""" + ELSE + SUM(size) + END + 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""" + + sql = """SELECT parent_id FROM files + WHERE id = ? AND parent_id!=id""" db_cursor.execute(sql, (parent_id,)) res = db_cursor.fetchone() if res: parent_id = res[0] else: parent_id = False - + db_connection.commit() return - + def get_stats(self, selected_id): """get statistic information""" retval = {} if selected_id: - sql = """select id, type, parent_id from files where id=?""" + sql = """SELECT id, type, parent_id FROM files WHERE id=?""" self.db_cursor.execute(sql, (selected_id,)) res = self.db_cursor.fetchone() if not res: return retval - + selected_item = {'id':res[0], 'type':res[1], 'parent': res[2]} - + # collect all parent_id's parents = [] + def _recurse(fid): parents.append(fid) - sql = """select id from files where type=? and parent_id=? and parent_id!=1""" + sql = """SELECT id FROM files + WHERE type=? AND parent_id=? AND parent_id!=1""" self.db_cursor.execute(sql, (self.DIR, fid)) res = self.db_cursor.fetchall() if res: for row in res: _recurse(row[0]) _recurse(selected_id) - + if selected_item['parent'] == 1: parents.pop(0) retval['discs'] = 1 retval['dirs'] = len(parents) - + parents.append(selected_id) - + files_count = 0 - + for p in parents: - sql = """select count(id) from files where type!=0 and type!=1 and parent_id=?""" + sql = """SELECT count(id) FROM files + WHERE type!=0 AND type!=1 AND parent_id=?""" self.db_cursor.execute(sql, (p,)) res = self.db_cursor.fetchone() if res: files_count+=res[0] retval['files'] = files_count - sql = """select size from files where id=?""" + sql = """SELECT size FROM files WHERE id=?""" self.db_cursor.execute(sql, (selected_id,)) res = self.db_cursor.fetchone() if res: retval['size'] = self.__bytes_to_human(res[0]) else: - sql = """select count(id) from files where parent_id=1 and type=1""" + sql = """SELECT count(id) FROM files + WHERE parent_id=1 AND type=1""" self.db_cursor.execute(sql) res = self.db_cursor.fetchone() if res: retval['discs'] = res[0] - - sql = """select count(id) from files where parent_id!=1 and type=1""" + + sql = """SELECT count(id) FROM files + WHERE parent_id!=1 AND type=1""" self.db_cursor.execute(sql) res = self.db_cursor.fetchone() if res: retval['dirs'] = res[0] - - sql = """select count(id) from files where parent_id!=1 and type!=1""" + + sql = """SELECT count(id) FROM files + WHERE parent_id!=1 AND type!=1""" self.db_cursor.execute(sql) res = self.db_cursor.fetchone() if res: retval['files'] = res[0] - - sql = """select sum(size) from files where parent_id=1 and type=1""" + + sql = """SELECT sum(size) FROM files + WHERE parent_id=1 AND type=1""" self.db_cursor.execute(sql) res = self.db_cursor.fetchone() if res: 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=?""" + sql = """SELECT filename FROM images WHERE id=?""" self.db_cursor.execute(sql, (img_id,)) res = self.db_cursor.fetchone() if res: if res[0]: return os.path.join(self.internal_dirname, res[0]) 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: return "%d bytes" % integer - + ## 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 = 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() self.__clear_discs_tree() - + def __clear_discs_tree(self): try: self.discs_tree.clear() except: pass - + def __clear_files_tree(self): try: self.files_list.clear() except: pass - + def __connect_to_db(self): self.db_connection = sqlite.connect("%s" % \ (self.internal_dirname + '/db.sqlite'), detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES) self.db_cursor = self.db_connection.cursor() return - + def __close_db_connection(self): if self.db_cursor != None: self.db_cursor.close() @@ -718,16 +792,17 @@ class MainModel(ModelMT): self.db_connection.close() self.db_connection = None return - + def __create_internal_dirname(self): self.cleanup() - self.internal_dirname = "/tmp/pygtktalog%d" % datetime.now().microsecond + self.internal_dirname = "/tmp/pygtktalog%d" % \ + datetime.now().microsecond try: os.mkdir(self.internal_dirname) except IOError, (errno, strerror): print "m_main.py: __create_internal_dirname(): ", strerror return - + def __compress_and_save(self): try: if self.config.confd['compress']: @@ -736,20 +811,20 @@ class MainModel(ModelMT): tar = tarfile.open(self.filename, "w") if __debug__: print "m_main.py: __compress_and_save(): tar open successed" - + except IOError, (errno, strerror): return False, strerror - + os.chdir(self.internal_dirname) tar.add('.') tar.close() - + self.unsaved_project = False return True, None - + def __create_database(self): """make all necessary tables in db file""" - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table files(id INTEGER PRIMARY KEY AUTOINCREMENT, parent_id INTEGER, filename TEXT, @@ -760,27 +835,27 @@ class MainModel(ModelMT): source INTEGER, note TEXT, description TEXT);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table tags(id INTEGER PRIMARY KEY AUTOINCREMENT, group_id INTEGER, tag TEXT);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table tags_files(file_id INTEGER, tag_id INTEGER);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table groups(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, color TEXT);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table thumbnails(id INTEGER PRIMARY KEY AUTOINCREMENT, file_id INTEGER, filename TEXT);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table images(id INTEGER PRIMARY KEY AUTOINCREMENT, file_id INTEGER, thumbnail TEXT, filename TEXT);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table exif(id INTEGER PRIMARY KEY AUTOINCREMENT, file_id INTEGER, camera TEXT, @@ -796,33 +871,36 @@ class MainModel(ModelMT): light_source TEXT, resolution TEXT, orientation TEXT);""") - self.db_cursor.execute("""create table + self.db_cursor.execute("""create table gthumb(id INTEGER PRIMARY KEY AUTOINCREMENT, file_id INTEGER, note TEXT, place TEXT, 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');") + sql = """INSERT INTO files + VALUES(1, 1, 'root', null, 0, 0, 0, 0, null, null)""" + self.db_cursor.execute(sql) + sql = """INSERT INSERT groups VALUES(1, 'default', 'black')""" + self.db_cursor.execute(sql) self.db_connection.commit() - + def __scan(self): """scan content of the given path""" self.busy = True - + # new conection for this task, because it's running in separate thread db_connection = sqlite.connect("%s" % \ (self.internal_dirname + '/db.sqlite'), detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES, isolation_level="EXCLUSIVE") db_cursor = db_connection.cursor() - + timestamp = datetime.now() - + # count files in directory tree count = 0 self.statusmsg = "Calculating number of files in directory tree..." - + count = 0 try: for root, dirs, files in os.walk(self.path): @@ -831,55 +909,54 @@ class MainModel(ModelMT): if __debug__: print 'm_main.py: os.walk in %s' % self.path pass - + if count > 0: step = 1.0/count else: step = 1.0 - + self.count = 0 - + # guess filesystem encoding self.fsenc = sys.getfilesystemencoding() - + self.fresh_disk_iter = None - + def __recurse(parent_id, name, path, date, size, filetype, discs_tree_iter=None): """recursive scans given path""" if self.abort: return -1 - + _size = size - + myit = self.discs_tree.append(discs_tree_iter, None) - + if parent_id == 1: self.fresh_disk_iter = myit self.discs_tree.set_value(myit,2,gtk.STOCK_CDROM) - sql = """insert into - files(parent_id, filename, filepath, date, size, type, source) - values(?,?,?,?,?,?,?)""" + sql = """INSERT INTO + files(parent_id, filename, filepath, date, + size, type, source) + VALUES(?,?,?,?,?,?,?)""" db_cursor.execute(sql, (parent_id, name, path, date, size, filetype, self.source)) else: self.discs_tree.set_value(myit,2,gtk.STOCK_DIRECTORY) - sql = """ - insert into + sql = """INSERT INTO files(parent_id, filename, filepath, date, size, type) - values(?,?,?,?,?,?) - """ - db_cursor.execute(sql, - (parent_id, name, path, date, size, filetype)) - - sql = """select seq FROM sqlite_sequence WHERE name='files'""" + VALUES(?,?,?,?,?,?)""" + db_cursor.execute(sql, (parent_id, name, path, + date, size, filetype)) + + sql = """SELECT seq FROM sqlite_sequence WHERE name='files'""" db_cursor.execute(sql) 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: @@ -887,49 +964,48 @@ class MainModel(ModelMT): print "m_main.py: cannot access ", path #return -1 return 0 - + ############# # directories for i in dirs: j = self.__decode_filename(i) current_dir = os.path.join(root, i) - + try: st = os.stat(current_dir) st_mtime = st.st_mtime except OSError: st_mtime = 0 - + # do NOT follow symbolic links if os.path.islink(current_dir): l = self.__decode_filename(os.readlink(current_dir)) - - sql = """ - insert into files(parent_id, filename, filepath, date, size, type) - values(?,?,?,?,?,?) - """ + + sql = """INSERT INTO + files(parent_id, filename, filepath, date, size, type) + VALUES(?,?,?,?,?,?)""" db_cursor.execute(sql, (currentid, j + " -> " + l, - ocurrent_dir, st_mtime, 0, + current_dir, st_mtime, 0, self.LIN)) dirsize = 0 else: dirsize = __recurse(currentid, j, current_dir, st_mtime, 0, self.DIR, myit) - + if dirsize == -1: break else: _size = _size + dirsize - + ######## # files: for i in files: if self.abort: break - + self.count = self.count + 1 current_file = os.path.join(root, i) - + try: st = os.stat(current_file) st_mtime = st.st_mtime @@ -937,54 +1013,59 @@ class MainModel(ModelMT): except OSError: st_mtime = 0 st_size = 0 - + _size = _size + st_size j = self.__decode_filename(i) - + # do NOT follow symbolic links if os.path.islink(current_file): l = self.__decode_filename(os.readlink(current_file)) - sql = """insert into files(parent_id, filename, filepath, - date, size, type) - values(?,?,?,?,?,?)""" + sql = """INSERT INTO + files(parent_id, filename, filepath, date, size, type) + VALUES(?,?,?,?,?,?)""" db_cursor.execute(sql, (currentid, j + " -> " + l, current_file, st_mtime, 0, self.LIN)) else: - sql = """ - insert into files(parent_id, filename, filepath, date, size, type) - values(?,?,?,?,?,?) - """ + 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)) - + if self.count % 32 == 0: update = True else: update = False - + ########################### # fetch details about files if self.config.confd['retrive']: update = True exif = None - - sql = """select seq FROM sqlite_sequence WHERE name='files'""" + + sql = """SELECT seq FROM sqlite_sequence + WHERE name='files'""" db_cursor.execute(sql) fileid = db_cursor.fetchone()[0] - + ext = i.split('.')[-1].lower() - + # Images - thumbnails and exif data if self.config.confd['thumbs'] and ext in self.IMG: - tpath, exif, ret_code = Thumbnail(current_file, base=self.internal_dirname).save(fileid) + t = Thumbnail(current_file, + base=self.internal_dirname) + tpath, exif, ret_code = t.save(fileid) if ret_code != -1: - sql = """insert into thumbnails(file_id, filename) values (?, ?)""" - db_cursor.execute(sql, (fileid, - tpath.split(self.internal_dirname)[1][1:])) - - # exif - stroe data in exif table - if self.config.confd['exif'] and ext in ['jpg', 'jpeg']: + sql = """INSERT INTO + thumbnails(file_id, filename) + VALUES(?, ?)""" + t = tpath.split(self.internal_dirname)[1][1:] + db_cursor.execute(sql, (fileid, t)) + + # exif - store data in exif table + jpg = ['jpg', 'jpeg'] + if self.config.confd['exif'] and ext in jpg: p = None if self.config.confd['thumbs'] and exif: p = ParseExif(exif_dict=exif) @@ -1012,7 +1093,7 @@ class MainModel(ModelMT): orientation) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)""" db_cursor.execute(sql, (tuple(p))) - + # gthumb - save comments from gThumb program if self.config.confd['gthumb']: gt = GthumbCommentParser(root, i) @@ -1026,13 +1107,11 @@ class MainModel(ModelMT): db_cursor.execute(sql, (fileid, cmnts['note'], cmnts['place'], - cmnts['date'] - )) - if cmnts.has_key('keywords'): - # TODO: add gthumb keywords to tags and group 'gthumb' + cmnts['date'])) + if 'keywords' in cmnts: + # TODO: add gthumb keywords to tags pass - - + # Extensions - user defined actions if ext in self.config.confd['extensions'].keys(): cmd = self.config.confd['extensions'][ext] @@ -1041,25 +1120,23 @@ class MainModel(ModelMT): desc = '' for line in output: desc += line - #desc = string.replace(desc, "\n", "\\n") - sql = """update files set description=? where id=?""" + + sql = """UPDATE files SET description=? + WHERE id=?""" db_cursor.execute(sql, (desc, fileid)) - - #if i.split('.').[-1].lower() in mov_ext: - # # video only - # info = filetypeHelper.guess_video(os.path.join(root,i)) + ### end of scan if update: self.statusmsg = "Scannig: %s" % current_file self.progress = step * self.count - - sql = """update files set size=? where id=?""" + + sql = """UPDATE files SET size=? WHERE id=?""" db_cursor.execute(sql,(_size, currentid)) if self.abort: return -1 else: return _size - + if __recurse(1, self.label, self.path, 0, 0, self.DIR) == -1: if __debug__: print "m_main.py: __scan() __recurse()", @@ -1075,46 +1152,43 @@ class MainModel(ModelMT): print "m_main.py: __scan() removing old branch" self.statusmsg = "Removing old branch..." self.delete(self.currentid, db_cursor, db_connection) - + self.currentid = None - + db_cursor.close() db_connection.commit() db_connection.close() if __debug__: print "m_main.py: __scan() time: ", (datetime.now() - timestamp) - + self.busy = False - + # refresh discs tree self.__fetch_db_into_treestore() self.statusmsg = "Idle" self.progress = 0 self.abort = False - + def __fetch_db_into_treestore(self): """load data from DB to tree model""" # cleanup treeStore self.__clear_discs_tree() - + #connect + detect_types = sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES db_connection = sqlite.connect("%s" % \ (self.internal_dirname + '/db.sqlite'), - detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES) + detect_types = detect_types) db_cursor = db_connection.cursor() - + # fetch all the directories - sql = """ - SELECT id, parent_id, filename FROM files - WHERE type=1 ORDER BY parent_id, filename - """ + sql = """SELECT id, parent_id, filename FROM files + WHERE type=1 ORDER BY parent_id, filename""" db_cursor.execute(sql) data = db_cursor.fetchall() try: - sql = """ - SELECT id, parent_id, filename FROM files - WHERE type=1 ORDER BY parent_id, filename - """ + sql = """SELECT id, parent_id, filename FROM files + WHERE type=1 ORDER BY parent_id, filename""" db_cursor.execute(sql) data = db_cursor.fetchall() except: @@ -1124,7 +1198,7 @@ class MainModel(ModelMT): self.internal_dirname = None print "%s: Wrong database format!" % self.filename return - + def get_children(parent_id = 1, iterator = None): """fetch all children and place them in model""" for row in data: @@ -1134,7 +1208,7 @@ class MainModel(ModelMT): self.discs_tree.set_value(myiter, 1, row[2]) # name self.discs_tree.set_value(myiter, 3, row[1]) # parent_id get_children(row[0], myiter) - + # isroot? if iterator == None: self.discs_tree.set_value(myiter, 2, gtk.STOCK_CDROM) @@ -1142,28 +1216,31 @@ class MainModel(ModelMT): self.discs_tree.set_value(myiter, 2, gtk.STOCK_DIRECTORY) return - + if __debug__: start_date = datetime.now() # launch scanning. get_children() if __debug__: - print "m_main.py: __fetch_db_into_treestore() tree generation time: ", (datetime.now() - start_date) + print "m_main.py: __fetch_db_into_treestore()", + print "tree generation time: ", (datetime.now() - start_date) db_connection.close() return - + def __append_added_volume(self): """append branch from DB to existing tree model""" #connect + detect_types = sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES db_connection = sqlite.connect("%s" % (self.internal_dirname + '/db.sqlite'), - detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES) + detect_types = detect_types) db_cursor = db_connection.cursor() - - sql = """SELECT id, parent_id, filename FROM files WHERE type=1 ORDER BY parent_id, filename""" + + sql = """SELECT id, parent_id, filename FROM files + WHERE type=1 ORDER BY parent_id, filename""" db_cursor.execute(sql) data = db_cursor.fetchall() - + def get_children(parent_id = 1, iterator = None): """fetch all children and place them in model""" for row in data: @@ -1173,7 +1250,7 @@ class MainModel(ModelMT): self.discs_tree.set_value(myiter, 1, row[2]) self.discs_tree.set_value(myiter, 3, row[1]) get_children(row[0], myiter) - + # isroot? if iterator == None: self.discs_tree.set_value(myiter, 2, gtk.STOCK_CDROM) @@ -1181,28 +1258,20 @@ class MainModel(ModelMT): self.discs_tree.set_value(myiter, 2, 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: ", (datetime.now() - start_date) + print "m_main.py: __append_added_volume() tree generation time: ", + print datetime.now() - start_date db_connection.close() return - + def __decode_filename(self, txt): if self.fsenc: return txt.decode(self.fsenc) 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 + diff --git a/src/views/v_main.py b/src/views/v_main.py index d88a0db..0ad941b 100644 --- a/src/views/v_main.py +++ b/src/views/v_main.py @@ -29,15 +29,17 @@ from gtkmvc import View class MainView(View): """This handles only the graphical representation of the application. The widgets set is loaded from glade file""" - + GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade") + def __init__(self, ctrl): View.__init__(self, ctrl, self.GLADE) - + # hide v2.0 features self['separatormenuitem4'].hide() self['list1'].hide() self['thumbnails1'].hide() + #self['tag_cloud_textview'].drag_dest_set(0, [], 0) return pass # end of class