From a36ca62b7329d2c7643ab0f5b655fbf57924351d Mon Sep 17 00:00:00 2001 From: gryf Date: Tue, 8 Apr 2008 13:25:13 +0000 Subject: [PATCH] * Added statisics. * Bug fix on files disapear in file view. * Code clean up. --- README | 8 +- resources/glade/config.glade | 4 +- resources/glade/dialogs.glade | 767 ++++++++++++++++++---------------- resources/glade/main.glade | 22 +- src/ctrls/c_main.py | 103 +++-- src/models/m_main.py | 308 ++++++++++---- src/views/v_dialogs.py | 44 +- 7 files changed, 758 insertions(+), 498 deletions(-) diff --git a/README b/README index ea1f820..12ffc97 100644 --- a/README +++ b/README @@ -60,13 +60,14 @@ For version 1.0 following aims have to be done: - file details: - files properties x thumbnail - - description - - file information (date, size, etc) (50%) + x description - exif information - keywords (tags) + - gthumb integration - adding images (60% done) x generating/saving thumbnails x moving hardcoded files extensions into config +x statistics Legend: [-] not done, [x] done. @@ -74,7 +75,6 @@ For version 2.0: - Icon grid in files view - command line support: query, adding media to collection etc - internationalization support -- statistics Removed: - filetypes handling (movies, images, archives, documents etc). Now it have @@ -92,7 +92,7 @@ Removed: - anidb link/imdb link Maybe in future versions. Now text file descriptions and tags have to be enough for good and fast information search. - +- file information (date, size, etc) (50%) (no need for?) NOTES ===== diff --git a/resources/glade/config.glade b/resources/glade/config.glade index c08eadd..21e7f0c 100644 --- a/resources/glade/config.glade +++ b/resources/glade/config.glade @@ -259,12 +259,14 @@ True True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Compress collection + True 0 True + False + False 3 diff --git a/resources/glade/dialogs.glade b/resources/glade/dialogs.glade index d570f03..1966cb1 100644 --- a/resources/glade/dialogs.glade +++ b/resources/glade/dialogs.glade @@ -1,373 +1,398 @@ - - - + + + - - - True - Disk label - pyGTKtalog - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_DIALOG - GDK_GRAVITY_NORTH_WEST - True - - - - True - False - 0 - - - - True - GTK_BUTTONBOX_END - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - -6 - - - - - - True - True - True - True - True - gtk-ok - True - GTK_RELIEF_NORMAL - True - -5 - - - - - 0 - False - True - GTK_PACK_END - - - - - - True - False - 0 - - - - True - Disk label - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 3 - 0 - - - 0 - False - False - - - - - - True - True - True - True - True - 0 - - True - * - True - - - - 0 - True - True - - - - - 0 - True - True - - - - - - - - True - dialog1 - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_DIALOG - GDK_GRAVITY_NORTH_WEST - True - - - - True - False - 0 - - - - True - GTK_BUTTONBOX_END - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - -6 - - - - - - True - True - True - gtk-ok - True - GTK_RELIEF_NORMAL - True - -5 - - - - - 0 - False - True - GTK_PACK_END - - - - - - 3 - True - False - 0 - - - - 3 - True - 0 - 0.5 - GTK_SHADOW_ETCHED_IN - - - - 3 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - 2 - 3 - False - 3 - 3 - - - - True - True - True - True - 0 - New - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 1 - 2 - - - - - - - True - Disk Label: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Select directory: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - True - Browse... - True - GTK_RELIEF_NORMAL - True - - - - 2 - 3 - 1 - 2 - fill - - - - - - - - - - - True - <b>Select directory and enter label</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - - - label_item - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - + + True + Disk label - pyGTKtalog + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + + + True + + + True + 3 + Disk label + + + False + False + + + + + True + True + True + True + + + + 1 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + True + True + True + True + gtk-ok + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + True + dialog1 + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + + + True + 3 + + + True + 3 + 0 + + + True + 3 + 12 + + + True + 2 + 3 + 3 + 3 + + + + + + True + True + Browse... + True + 0 + + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + Select directory: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Disk Label: + + + GTK_FILL + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + New + + + 1 + 2 + + + + + + + + + + True + <b>Select directory and enter label</b> + True + + + label_item + + + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + True + True + gtk-ok + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + pyGTKtalog - stats + GTK_WIN_POS_CENTER_ON_PARENT + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 4 + 2 + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Total size: + GTK_JUSTIFY_RIGHT + + + 3 + 4 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Number of files: + GTK_JUSTIFY_RIGHT + + + 2 + 3 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Number of directories: + GTK_JUSTIFY_RIGHT + + + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Number of discs: + GTK_JUSTIFY_RIGHT + + + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + GTK_SHADOW_ETCHED_IN + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + GTK_SHADOW_ETCHED_IN + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + GTK_SHADOW_ETCHED_IN + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + GTK_SHADOW_ETCHED_IN + + + 1 + 2 + GTK_FILL + + + + + False + 1 + + + + + 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-close + True + 0 + + + + + False + GTK_PACK_END + + + + + diff --git a/resources/glade/main.glade b/resources/glade/main.glade index 3f3029c..625c9bb 100644 --- a/resources/glade/main.glade +++ b/resources/glade/main.glade @@ -141,6 +141,7 @@ True True + @@ -171,7 +172,7 @@ True - Add CD/DVD + Add _CD/DVD True @@ -180,9 +181,10 @@ True - Add Directory + Add _Directory True + @@ -190,6 +192,20 @@ True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Catalog _statistics + True + + + + + + True + + True @@ -566,6 +582,7 @@ True True True + @@ -877,6 +894,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _Add tag True + diff --git a/src/ctrls/c_main.py b/src/ctrls/c_main.py index 2baa4fe..1caf3f6 100644 --- a/src/ctrls/c_main.py +++ b/src/ctrls/c_main.py @@ -50,13 +50,13 @@ class MainController(Controller): widgets = ( "discs","files", 'save1','save_as1','cut1','copy1','paste1','delete1','add_cd','add_directory1', - 'tb_save','tb_addcd','tb_find','nb_dirs','description', + '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', + 'nb_dirs','description','stat1', ) widgets_cancel = ('cancel','cancel1') @@ -146,22 +146,22 @@ class MainController(Controller): self.__do_quit() return True - def on_tb_quit_clicked(self,widget): + def on_tb_quit_clicked(self, widget): self.__do_quit() - def on_quit1_activate(self,widget): + def on_quit1_activate(self, widget): self.__do_quit() - def on_new1_activate(self,widget): + def on_new1_activate(self, widget): self.__new_db() - def on_tb_new_clicked(self,widget): + def on_tb_new_clicked(self, widget): self.__new_db() - def on_add_cd_activate(self,widget): + def on_add_cd_activate(self, widget): self.__add_cd() - def on_tb_addcd_clicked(self,widget): + def on_tb_addcd_clicked(self, widget): self.__add_cd() def on_add_directory1_activate(self, widget): @@ -169,7 +169,7 @@ class MainController(Controller): self.__add_directory() return - def on_about1_activate(self,widget): + def on_about1_activate(self, widget): """Show about dialog""" Dialogs.Abt("pyGTKtalog", __version__, "About", ["Roman 'gryf' Dobosz"], licence) @@ -207,6 +207,20 @@ class MainController(Controller): def on_save_as1_activate(self, widget): self.__save_as() + def on_stat1_activate(self, menu_item): + self.__show_stats() + + def on_statistics1_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) + 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() @@ -242,7 +256,7 @@ class MainController(Controller): return False if event.button == 3: - """show context menu""" + """Right mouse button. Show context menu.""" try: selection = treeview.get_selection() model, list_of_paths = selection.get_selected_rows() @@ -286,26 +300,44 @@ class MainController(Controller): self.view['discs'].collapse_all() return - def on_files_cursor_changed(self,treeview): + def on_files_button_press_event(self, tree, event): + try: + path, column, x, y = tree.get_path_at_pos(int(event.x), + int(event.y)) + 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 = [] + + self.__popup_files_menu(event) + return True + + def on_files_cursor_changed(self, treeview): """Show details of selected file""" 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: - #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) - #except: - # if __debug__: - # print "c_main.py: on_files_cursor_changed() insufficient iterator" + 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: + #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) + except: + if __debug__: + print "c_main.py: on_files_cursor_changed() insufficient iterator" return def on_files_key_release_event(self, a, event): @@ -376,6 +408,9 @@ class MainController(Controller): self.__open(path) return + def on_add_tag1_activate(self, menu_item): + print self.view['discs'].get_cursor() + def on_update1_activate(self, menu_item): """Update disc under cursor position""" @@ -411,7 +446,7 @@ class MainController(Controller): self.__set_title(filepath=self.model.filename, modified=True) return - def on_debugbtn_clicked(self,widget): + def on_debugbtn_clicked(self, widget): """Debug. To remove in stable version, including button in GUI""" if __debug__: print "\nc_main.py: on_debugbtn_clicked()" @@ -713,6 +748,12 @@ class MainController(Controller): 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() + return + def __generate_recent_menu(self): self.recent_menu = gtk.Menu() for i in self.model.config.recent: @@ -786,4 +827,8 @@ class MainController(Controller): buff.insert_with_tags(iter, cloud['name'], tag) self.view['tag_cloud_textview'].set_buffer(buff) + def __show_stats(self, selected_id=None): + data = self.model.get_stats(selected_id) + label = Dialogs.StatsDialog(data).run() + pass # end of class diff --git a/src/models/m_main.py b/src/models/m_main.py index c40550f..e7fbed4 100644 --- a/src/models/m_main.py +++ b/src/models/m_main.py @@ -327,7 +327,97 @@ class MainModel(ModelMT): self.discs_tree.remove(branch_iter) return + def get_stats(self, selected_id): + """get statistic information""" + retval = {} + if selected_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""" + 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=?""" + 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=?""" + 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""" + 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""" + 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""" + 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""" + self.db_cursor.execute(sql) + res = self.db_cursor.fetchone() + if res: + retval['size'] = self.__bytes_to_human(res[0]) + return retval + # 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) + + 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) + def __clear_trees(self): self.__clear_files_tree() self.__clear_discs_tree() @@ -396,23 +486,28 @@ class MainModel(ModelMT): filename TEXT, filepath TEXT, date datetime, - size integer, - type integer, - source integer, - size_x integer, - size_y integer, - filetype integer, + size INTEGER, + type INTEGER, + source INTEGER, + note TEXT, description TEXT);""") self.db_cursor.execute("""create table tags(id INTEGER PRIMARY KEY AUTOINCREMENT, - file_id INTEGER, + group_id INTEGER, tag TEXT);""") + self.db_cursor.execute("""create table + tags_files(file_id INTEGER, + tag_id INTEGER);""") + self.db_cursor.execute("""create table + groups(id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + color TEXT);""") self.db_cursor.execute("""create table 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("insert into files values(1, 1, 'root', null, 0, 0, 0, 0, null, null, null, null);") + self.db_cursor.execute("insert into groups values(1, 'default', 'black');") def __scan(self): """scan content of the given path""" @@ -496,36 +591,32 @@ class MainModel(ModelMT): #return -1 return 0 + ############# + # directories for i in dirs: - if self.fsenc: - j = i.decode(self.fsenc) - else: - j = i + j = self.__decode_filename(i) + current_dir = os.path.join(root, i) try: - st = os.stat(os.path.join(root,i)) + st = os.stat(current_dir) st_mtime = st.st_mtime except OSError: st_mtime = 0 # do NOT follow symbolic links - if os.path.islink(os.path.join(root,i)): - l = os.readlink(os.path.join(root,i)) - if self.fsenc: - l = l.decode(self.fsenc) - else: - l = l + 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(?,?,?,?,?,?) """ db_cursor.execute(sql, (currentid, j + " -> " + l, - os.path.join(path,i), st_mtime, 0, + ocurrent_dir, st_mtime, 0, self.LIN)) dirsize = 0 else: - dirsize = __recurse(currentid, j, os.path.join(path,i), + dirsize = __recurse(currentid, j, current_dir, st_mtime, 0, self.DIR, myit) if dirsize == -1: @@ -533,13 +624,17 @@ class MainModel(ModelMT): else: _size = _size + dirsize + ######## + # files: for i in files: if self.abort: break + self.count = self.count + 1 - current_path = os.path.join(root,i) + current_file = os.path.join(root, i) + try: - st = os.stat(current_path) + st = os.stat(current_file) st_mtime = st.st_mtime st_size = st.st_size except OSError: @@ -547,62 +642,70 @@ class MainModel(ModelMT): st_size = 0 _size = _size + st_size - j = i - if self.fsenc: - j = i.decode(self.fsenc) + j = self.__decode_filename(i) - sql = """ - insert into files(parent_id, filename, filepath, date, size, type) - values(?,?,?,?,?,?) - """ - db_cursor.execute(sql, (currentid, j, os.path.join(path,i), - st_mtime, st_size, self.FIL)) - - if self.count % 32 == 0: - update = True + # 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(?,?,?,?,?,?)""" + db_cursor.execute(sql, (currentid, j + " -> " + l, + current_file, st_mtime, 0, + self.LIN)) else: - update = False + 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)) - ########################### - # fetch details about files - if self.config.confd['retrive']: - update = True - 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: - path, exif, ret_code = Thumbnail(current_path, 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:])) - - if self.config.confd['exif']: - # TODO: exif implementation - pass - - # Extensions - user defined actions - if ext in self.config.confd['extensions'].keys(): - cmd = self.config.confd['extensions'][ext] - arg = string.replace(current_path, '"', '\\"') - output = os.popen(cmd % arg).readlines() - desc = '' - for line in output: - desc += line - #desc = string.replace(desc, "\n", "\\n") - sql = """update files set description=? where id=?""" - db_cursor.execute(sql, (desc, fileid)) + if self.count % 32 == 0: + update = True + else: + update = False - #if i.split('.').[-1].lower() in mov_ext: - # # video only - # info = filetypeHelper.guess_video(os.path.join(root,i)) - ### end of scan + ########################### + # fetch details about files + if self.config.confd['retrive']: + update = True + 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: + path, 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:])) + + if self.config.confd['exif']: + # TODO: exif implementation + pass + + # Extensions - user defined actions + if ext in self.config.confd['extensions'].keys(): + cmd = self.config.confd['extensions'][ext] + arg = string.replace(current_file, '"', '\\"') + output = os.popen(cmd % arg).readlines() + desc = '' + for line in output: + desc += line + #desc = string.replace(desc, "\n", "\\n") + 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_path + self.statusmsg = "Scannig: %s" % current_file self.progress = step * self.count sql = """update files set size=? where id=?""" @@ -695,34 +798,53 @@ class MainModel(ModelMT): return def __remove_branch_form_db(self, root_id): - parent_ids = [root_id,] - sql = """select id from files where parent_id = ? and type = 1""" - self.db_cursor.execute(sql, (root_id,)) - ids = self.db_cursor.fetchall() + """Remove subtree from main tree, remove tags from database + remove all possible data, like thumbnails""" + fids = [] def get_children(fid): - parent_ids.append(fid) - sql = """select id from files where parent_id = ? and type = 1""" + fids.append(fid) + sql = """select id from files where parent_id = ?""" self.db_cursor.execute(sql, (fid,)) res = self.db_cursor.fetchall() - for i in res: - get_children(i[0]) + if len(res)>0: + for i in res: + get_children(i[0]) - for i in ids: - get_children(i[0]) + get_children(root_id) def generator(): - for c in parent_ids: + for c in fids: yield (c,) - - sql = """delete from files where type = 1 and parent_id = ?""" - self.db_cursor.executemany(sql, generator()) + + # 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 @@ -762,4 +884,10 @@ class MainModel(ModelMT): db_connection.close() return + def __decode_filename(self, txt): + if self.fsenc: + return txt.decode(self.fsenc) + else: + return txt + pass # end of class diff --git a/src/views/v_dialogs.py b/src/views/v_dialogs.py index c5a0f96..fb20d98 100644 --- a/src/views/v_dialogs.py +++ b/src/views/v_dialogs.py @@ -109,7 +109,7 @@ class Abt(object): self.dialog.set_authors(authors) self.dialog.connect('response', lambda dialog, response: self.dialog.destroy()) self.dialog.show() - + class InputDiskLabel(object): """Sepcific dialog for quering user for a disc label""" def __init__(self, label=""): @@ -276,3 +276,45 @@ 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 StatsDialog(object): + """Sepcific dialog for display stats""" + def __init__(self, values={}): + self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade") + self.values = values + + def run(self): + gladexml = gtk.glade.XML(self.gladefile, "statDialog") + dialog = gladexml.get_widget("statDialog") + + if self.values.has_key('discs'): + entry = gladexml.get_widget("discs_entry") + entry.set_text(str(self.values['discs'])) + else: + label = gladexml.get_widget("discs_label") + entry = gladexml.get_widget("discs_entry") + label.hide() + entry.hide() + + if self.values.has_key('dirs'): + entry = gladexml.get_widget("dirs_entry") + entry.set_text(str(self.values['dirs'])) + else: + label = gladexml.get_widget("dirs_label") + entry = gladexml.get_widget("dirs_entry") + label.hide() + entry.hide() + + if self.values.has_key('files'): + entry = gladexml.get_widget("files_entry") + entry.set_text(str(self.values['files'])) + + if self.values.has_key('size'): + entry = gladexml.get_widget("size_entry") + entry.set_text(str(self.values['size'])) + + result = dialog.run() + dialog.destroy() + if result == gtk.RESPONSE_OK: + return entry.get_text() + return None