diff options
author | root <root@ghunt-desktop.(none)> | 2010-07-06 10:53:22 (GMT) |
---|---|---|
committer | root <root@ghunt-desktop.(none)> | 2010-07-06 10:53:22 (GMT) |
commit | fbaad3095b5a0ccf51fdf2a3dd7f3c0551edbe7e (patch) | |
tree | 08df3364b559b417bfaaa5c8e5c54cf4e88a49e9 | |
parent | 12405ccda4ec4d2f2b35d01a72e20d445a010ae8 (diff) |
progress bar during load to datastore
-rw-r--r-- | dbphoto.py | 57 | ||||
-rw-r--r-- | display.py | 316 | ||||
-rw-r--r-- | photo_toolbar.py | 10 | ||||
-rw-r--r-- | sources.py | 102 | ||||
-rw-r--r-- | startup_images/XoPhoto.svg | 54 | ||||
-rw-r--r-- | startup_images/reuiideasmynewxo.zip | bin | 57363 -> 0 bytes | |||
-rw-r--r-- | startup_images/xophoto.sql | 18 | ||||
-rw-r--r-- | xophoto.sqlite | bin | 2486272 -> 2486272 bytes | |||
-rw-r--r-- | xophotoactivity.py | 122 |
9 files changed, 433 insertions, 246 deletions
@@ -120,7 +120,7 @@ class DbAccess(): return cursor.fetchall() def get_album_thumbnails(self,album_id,is_journal=False): - if not is_journal: #want most recent first, need left join because picture may not exist yet + if is_journal: #is_journal: #want most recent first, need left join because picture may not exist yet #sql = """select groups.*, picture.* from groups left join picture \ #where groups.category = ? and groups.jobject_id = picture.jobject_id order by groups.seq desc""" sql = """select groups.* from groups where groups.category = ? order by groups.seq desc""" @@ -142,6 +142,31 @@ class DbAccess(): else: return -1 + def update_resequence(self,id,seq): + """ update the groups record at id, then reassign seq numbers for this album""" + conn = self.connection() + cur = conn.cursor() + cur.execute('select * from groups where id = ?',(id,)) + rows = cur.fetchall() + if len(rows) != 1: + _logger.debug('update_resequence did not fetch id=%s'%id) + return + album_id = rows[0]['category'] + cur.execute("update groups set seq = ? where id = ?",(seq,id,)) + conn.commit() + cur.execute ('select * from groups where category = ? order by seq',(album_id,)) + rows = cur.fetchall() + if len(rows) == 0: + _logger.debug('no group members for album_id = %s'%album_id) + return + #need another cursor + update_cursor = conn.cursor() + num = 0 + for row in rows: + update_cursor.execute("update groups set seq = ? where id = ?",(num,row['id'],)) + num += 20 + conn.commit() + def create_picture_record(self,object_id, fn): """create a record in picture pointing to unique pictures in the journal. Use md5 checksum to test for uniqueness @@ -204,8 +229,10 @@ class DbAccess(): def check_in_ds(self,fullpath,size): """returns true/false based upon identity of file path and image size""" sql = "select * from picture where mount_point = '%s' and orig_size = %s"%(fullpath,size,) - self.cur.execute(sql) - rows = self.cur.fetchall() + conn = self.connection() + cur = conn.cursor() + cur.execute(sql) + rows = cur.fetchall() if len(rows)>0: return True return False @@ -290,9 +317,29 @@ class DbAccess(): (str(album_id),'',str(jobject_id),old_seq + 20)) self.con.commit() - def change_sequence(self,rows,subject_index,new_index): - pass + def delete_image(self, album_id, jobject_id): + conn = self.connection() + cursor = conn.cursor() + cursor.execute("delete from groups where category = ? and jobject_id = ?",\ + (str(album_id), str(jobject_id),)) + conn.commit() + def delete_all_references_to(self,jobject_id): + conn = self.connection() + cursor = conn.cursor() + try: + cursor.execute('begin transaction') + cursor.execute("delete from groups where jobject_id = ?",\ + (str(jobject_id),)) + cursor.execute("delete from picture where jobject_id = ?",\ + (str(jobject_id),)) + cursor.execute("delete from data_cache.transforms where jobject_id = ?",\ + (str(jobject_id),)) + conn.commit() + except Exception,e: + cursor.execute('rollback transaction') + _logger.error('error deleting all references for object:%s. Error: ;%s'%(jobject_id,e,)) + def table_exists(self,table): try: sql = 'select * from %s'%table @@ -322,7 +322,7 @@ class OneAlbum(): _logger.debug('number of thumbnails found for %s was %s'%(self.album_id,len(self.rows))) #protect from an empty database - if len(self.rows) == 0: return + #if len(self.rows) == 0: return num_rows = int(len(self.rows) // self.pict_per_row) + 1 if self.thumbnail_world and num_rows == self.num_rows and not new_surface: self.repaint() @@ -349,18 +349,22 @@ class OneAlbum(): self.repaint() _logger.debug('paint thumbnail %s of %s in %s seconds'%(i,num_pict,(time.clock() - start_time))) start_time = time.clock() - #self.release_cycles() + self.release_cycles() self.repaint() - def repaint(self): - if not self.thumbnail_world: return + def repaint(self): + #if not self.thumbnail_world: return self.thumbnail_surface = self.thumbnail_panel(self.thumbnail_world) - #surf = self.thumbnail_panel(self.thumbnail_world) screen.blit(self.thumbnail_surface,(album_column_width,0)) - #screen.blit(self.thumbnail_world,(album_column_width,0)) - #self.select_pict(self.thumb_index) - pygame.display.flip + pygame.display.flip() + def release_cycles(self): + while gtk.events_pending(): + gtk.main_iteration() + pygame.event.pump() + pygame.event.get() + + def thumbnail_panel(self,world): #modeled after ezscrollbar example #following scrollRect definition changed to be surface rather than screen relative @@ -469,6 +473,17 @@ class OneAlbum(): def set_top_image(self,jobject_id): self.jobject_id = jobject_id + def insert_after(self,after_index, thumbnail_index): + #get the seq of the row[after_index] + if after_index < 0: + adder = -1 + else: + adder = +1 + seq = self.rows[after_index]['seq'] + id = self.rows[thumbnail_index]['id'] + self.db.update_resequence(id,seq+adder) + self.paint(True) + def click(self,x,y): #map from thumbnail_surface to thumbnail_world if self.sb: @@ -481,8 +496,9 @@ class OneAlbum(): if thumb_index < len(self.rows): self.thumb_index = thumb_index else: - self.thumb_index = len(self.rows) + self.thumb_index = len(self.rows) - 1 self.select_pict(self.thumb_index) + return self.thumb_index def get_jobject_id_at_xy(self,x,y): #x and y are relative to thumbnail_surface, figure out first mapping to the world @@ -554,11 +570,24 @@ class OneAlbum(): self.repaint() def scroll_up(self,num=3): + _logger.debug('scroll up') + #I started doing this when it wasn't my objective -- has not really been started + if self.sb: + x,y = self.sb.get_scrolled() + else: + x,y = [0,0] + self.sb.scroll(num) + return + + def scroll_down(self,num=-3): #I started doing this when it wasn't my objective -- has not really been started if self.sb: x,y = self.sb.get_scrolled() else: x,y = [0,0] + self.sb.scroll(num) + return + row = num // self.pict_per_row min_y = row * self.xy_size if min_y < y: @@ -660,7 +689,6 @@ class DisplayAlbums(): sql = """insert into groups (category,subcategory,jobject_id,seq) \ values ('%s','%s','%s',%s)"""%('albums',album_tup[0],album_tup[1],i,) self.db.dbtry(sql) - i += 20 self.db.commit() """this needs to be done whenever new pictures are added to journal so not now #then put the journal picutres into the journal album @@ -690,11 +718,11 @@ class DisplayAlbums(): to display thumbnails on the right side of screen""" self.selected_album_id = album_id #self.album_objects[self.selected_album_id].clear() - alb_object = self.album_objects.get(self.selected_album_id,new_surface) + alb_object = self.album_objects.get(self.selected_album_id) if alb_object: last_selected = alb_object start = time.clock() - alb_object.paint() + alb_object.paint(new_surface) alb_object.make_visible(alb_object.thumb_index) _logger.debug('took %s to display thumbnails'%(time.clock()-start)) else: @@ -769,6 +797,7 @@ class DisplayAlbums(): pygame.display.flip() def refresh_album_rows(self): + """ rows array, will need to change if albums become reorderable""" sql = "select * from groups where category = 'albums' order by id" rows,cur = self.db.dbdo(sql) self.number_of_albums = len(rows) @@ -781,11 +810,12 @@ class DisplayAlbums(): sb_x,sb_y = self.sb.get_scrolled() y_index = (y + sb_y) // self.album_height self.album_index = int(y_index) - self.accumulation_target = self.album_index self.refresh_album_rows() if self.album_index >= len(self.album_rows) : return None - + self.accumulation_target = self.album_index + self.paint_albums() + #now change the thumbnail side of the screen try: album_name = self.album_rows[int(self.album_index)]['subcategory'] @@ -795,6 +825,10 @@ class DisplayAlbums(): _logger.debug('exception fetching thumbnails %s'%e) return self.selected_album_id = album_name + if album_name == trash_id: + self._activity.activity_toolbar.empty_journal_button.show() + else: + self._activity.activity_toolbar.empty_journal_button.hide() _logger.debug('now display the thumbnails with the album identifier %s'%album_name) change_name = True for id,name in self.predefined_albums: @@ -802,7 +836,7 @@ class DisplayAlbums(): change_name = False if change_name: self._activity.activity_toolbar.title.set_text(album_title) - self.display_thumbnails(album_name) + self.display_thumbnails(album_name,new_surface=True) pygame.display.flip() def add_to_current_album(self,jobject_id,current_album_id=None,name=None): @@ -887,6 +921,11 @@ class DisplayAlbums(): def get_current_album_identifier(self): return str(self.album_rows[self.album_index]['subcategory']) + + def get_album_id_at_index(self,index): + if index >= len(self.album_rows): + return '' + return str(self.album_rows[index]['subcategory']) def get_current_album_name(self): return str(self.album_rows[self.album_index]['jobject_id']) @@ -909,35 +948,55 @@ class DisplayAlbums(): #change the cursor some way self._activity.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.TOP_LEFT_CORNER)) - def drop_image(self, img_x,img_y,album_x, album_y): + def drop_image(self, start_x,start_y,drop_x, drop_y): self._activity.window.set_cursor(None) - index = self.get_album_index_at_xy(album_x, album_y) - jobject_id = self.album_objects[self.selected_album_id].get_jobject_id_at_xy(img_x,img_y) - if not index or not jobject_id: return - #if index is larger than max, we want a new album - _logger.debug('index:%s length of rows:%s jobject_id %s'%(index,len(self.album_rows),jobject_id,)) - if index > len(self.album_rows)-1: - self.create_new_album(self.default_name) - self.album_objects[self.accumulation_target].set_top_image(jobject_id) - else: - self.db.add_image_to_album(self.album_rows[index]['subcategory'], jobject_id) - #self.display_thumbnails(self.accumulation_target) - self.refresh_album_rows() - self.paint_albums() - - def toggle(self,x,y): - """change the number of albums displayed""" - pass - - def roll_over(self,x,y, in_drag=False): - """indicate willingness to be selected""" - pass - - def do_drag_up(self,x,y): - """add the dragged item to the selected album""" - pass - + if drop_x < album_column_width: #we are dropping on album side of screen + jobject_id = self.album_objects[self.selected_album_id].get_jobject_id_at_xy(start_x,start_y) + index = self.get_album_index_at_xy(drop_x, drop_y) + if not index or not jobject_id: return + + #if dropped on the trash icon + if self.get_album_id_at_index(index) == trash_id: #a request to delete + current_album_id = self.get_current_album_identifier() + self.db.delete_image(current_album_id,jobject_id) + self.album_objects[current_album_id].paint(True) + if current_album_id == journal_id: + self.db.add_image_to_album(self.album_rows[index]['subcategory'], jobject_id) + self.refresh_album_rows() + self.paint_albums() + return + + #if index is larger than max, we want a new album + _logger.debug('index:%s length of rows:%s jobject_id %s'%(index,len(self.album_rows),jobject_id,)) + if index > len(self.album_rows)-1: + self.create_new_album(self.default_name) + self.album_objects[self.accumulation_target].set_top_image(jobject_id) + else: + self.accumulation_target = self.album_rows[index]['subcategory'] + self.db.add_image_to_album(self.accumulation_target, jobject_id) + self.refresh_album_rows() + self.paint_albums() + #guarantee that the next time the thumb nails are painted, the new one is included + self.album_objects[self.accumulation_target].thumbnail_world = None + + #the drop was on thumbnail side of screen, this is a reorder request + else: + #map from thumbnail_surface to thumbnail_world, splitting each image vertically in middle + start_index = self.album_objects[self.selected_album_id].click(start_x, start_y) + if self.album_objects[self.selected_album_id].sb: + (sb_x,sb_y) = self.album_objects[self.selected_album_id].sb.get_scrolled() + else: + (sb_x,sb_y) = [0,0] + xy_size = self.album_objects[self.selected_album_id].xy_size + pict_per_row = self.album_objects[self.selected_album_id].pict_per_row + thumb_index = int(((drop_y + sb_y) // xy_size) * pict_per_row + \ + math.floor(drop_x - album_column_width-(xy_size//2)) // xy_size) + if thumb_index > len(self.album_objects[self.selected_album_id].rows) - 1: + thumb_index =len(self.album_objects[self.selected_album_id].rows) - 1 + _logger.debug('insert after %s this image at index:%s'%(thumb_index,start_index,)) + self.album_objects[self.selected_album_id].insert_after(thumb_index,start_index) + ##################### ALERT ROUTINES ################################## @@ -983,6 +1042,31 @@ class Utilities(): #Do any work that is specific to the type of button clicked. if response_id is gtk.RESPONSE_OK and this_alert.callback_function != None: this_alert.callback_function (this_alert, response_id) + +class ProgressAlert(ConfirmationAlert): + def __init__(self, **kwargs): + self._parent = kwargs.get('parent') + ConfirmationAlert.__init__(self, **kwargs) + self.pb = gtk.ProgressBar() + #self.pb.set_text('test') + self.pb.show() + + self._hbox.pack_start(self.pb) + + self._timeout = 10 + #gobject.timeout_add(1000, self.__timeout) + + + def __timeout(self): + self._timeout -= 1 + self.done_percent((10.0-self._timeout)/10.0) + if self._timeout == 0: + self._response(gtk.RESPONSE_OK) + return False + return True + + def set_fraction(self,fraction): + self.pb.set_fraction(fraction) class Application(): @@ -994,13 +1078,15 @@ class Application(): self.in_grab = False self.file_tree = None self.util = Utilities(self._activity) + self.album_collection = None def first_run_setup(self): #scan the datastore and add new images as required source = os.path.join(os.environ['SUGAR_BUNDLE_PATH'],'startup_images') - self.file_tree = FileTree(self.db) + self.file_tree = FileTree(self.db,self._activity) self.file_tree.copy_tree_to_ds(source) - number_of_pictures = self.get_thumbnail_count(journal_id) + ds_count, added = self.ds_sql.check_for_recent_images() + number_of_pictures = self.db.get_thumbnail_count(journal_id) if number_of_pictures < 10: _logger.error('failed to initalize the datastore with at least 10 pictures') exit(0) @@ -1012,34 +1098,29 @@ class Application(): def pygame_display(self): pygame.display.flip() - def run(self): - global screen - global in_click_delay - global screen_w - global screen_h - global in_db_wait - global in_drag - if True: - """this may have been unnecessary --take it out and see - #moved the database functionality here because of sync problems with journal - if not self._activity.DbAccess_object: #we need to wait for the read-file to finish - Timer(5.0, self.end_db_delay, ()).start() - in_db_wait = True - while not self._activity.DbAccess_object and in_db_wait: - gtk.main_iteration() - if not self._activity.DbAccess_object: - _logger.error('db object not open after timeout in Appplication.run') - exit() - """ - self.db = self._activity.DbAccess_object - if not self.db.is_open(): - _logger.debug('failed to open "xophoto.sqlite" database') - exit() - self.ds_sql = Datastore_SQLite(self.db) - + def show_progress(self,button,id): + self.pa = ProgressAlert() + self._activity.add_alert(self.pa) + self.pa.connect('response',self._response_cb) + + def _response_cb(self,alert,response): + self._activity.remove_alert(self.pa) + + def do_startup(self): start = time.clock() - alert = self.util.alert(_('A quick check of the Journal for new images'),_('PLEASE BE PATIENT')) + + #for testing purposes, use the following to delete all pictures from the journal + if False: + self._activity.empty_trash_cb(None,gtk.RESPONSE_OK,journal_id) + conn = self.db.connection() + c = conn.cursor() + c.execute('vacuum') + conn.commit() + + #alert = self.util.alert(_('A quick check of the Journal for new images'),_('PLEASE BE PATIENT')) try: + #this step took 2.5 seconds to add 195 records to picture from datastore on 1.5XO + #and 1 second when no records were added ds_count, added = self.ds_sql.check_for_recent_images() except PhotoException,e: #This is a corrupted copy the sqlite database, start over @@ -1059,36 +1140,51 @@ class Application(): self.db = self.DbAccess_object _logger.debug('check for recent images took %f seconds'%(time.clock()-start)) - self.util.remove_alert(alert) - running = True - do_display = True - screen = pygame.display.get_surface() - info = pygame.display.Info() - screen_w = info.current_w - screen_h = info.current_h - _logger.debug('startup screen sizes w:%s h:%s '%(screen_w,screen_h,)) + #self.util.remove_alert(alert) - # Clear Display - screen.fill((album_background_color)) #255 for white - x = 0 - pygame.display.flip() #if the picture table is empty, populate it from the journal, and initialize if ds_count < 10: - self.first_run_setup() self.album_collection = DisplayAlbums(self.db, self._activity) self.album_collection.paint_albums() + _logger.debug('took %s to do startup and paint albums'%(time.clock()-start)) pygame.display.flip() + start = time.clock() self.album_collection.display_journal() + _logger.debug('took %s to display journal'%(time.clock()-start)) + def run(self): + global screen + global in_click_delay + global screen_w + global screen_h + global in_db_wait + global in_drag + if True: + self.db = self._activity.DbAccess_object + if not self.db.is_open(): + _logger.debug('failed to open "xophoto.sqlite" database') + exit() + self.ds_sql = Datastore_SQLite(self.db) + + screen = pygame.display.get_surface() + info = pygame.display.Info() + screen_w = info.current_w + screen_h = info.current_h + _logger.debug('startup screen sizes w:%s h:%s '%(screen_w,screen_h,)) + + # Clear Display + screen.fill((album_background_color)) + pygame.display.flip() + + self.do_startup() + # Flip Display pygame.display.flip() - # start processing any datastore images that don't have thumbnails - #gobject.idle_add(self.ds_sql.make_one_thumbnail) - - + running = True + x = 0 #initialize in case there is no mouse event while running: # Pump GTK messages. while gtk.events_pending(): @@ -1115,10 +1211,16 @@ class Application(): elif event.key == K_DOWN: self.album_collection.album_objects[self.album_collection.selected_album_id].next_row() pygame.display.flip() - + elif event.key == K_r and (pygame.key.get_mods() & KMOD_CTRL):# and (pygame.key.get_mods() & KMOD_ALT): + _logger.debug('restart database recognized') + self._activity.read(none,initialize=True) + self._activity.close() + running = False + pygame.quit() + #mouse events elif event.type == MOUSEBUTTONDOWN: - if self.mouse_timer_running(): #this is a double click + if event.button < 4 and self.mouse_timer_running(): #this is a double click self.process_mouse_double_click( event) in_click_delay = False else: #just a single click @@ -1184,24 +1286,32 @@ class Application(): print('drop at %s,%s'%(x,y,)) if in_drag and self.last_l: self.album_collection.drop_image(self.drag_start_x,self.drag_start_y,x,y) + in_drag = False pygame.display.flip() def process_mouse_click(self,event): x,y = event.pos - l,m,r = pygame.mouse.get_pressed() - print('mouse single click') - if x < album_column_width -thick: - scroll_x,scroll_y = self.album_collection.sb.get_scrolled() - rtn_val = self.album_collection.click(x,y + scroll_y) - if not rtn_val: - #create a new album - pass - elif x > album_column_width and x < (screen_w - thick): - if l: - self.album_collection.album_objects[self.album_collection.selected_album_id].click(x,y) - elif r: - self.in_grab = True - self.album_collection.start_grab(x,y) + butt = event.button + if butt == 4: + _logger.debug('button 4s') + self.album_collection.album_objects[self.album_collection.selected_album_id].scroll_up() + elif butt == 5: + self.album_collection.album_objects[self.album_collection.selected_album_id].scroll_down() + else: + l,m,r = pygame.mouse.get_pressed() + print('mouse single click') + if x < album_column_width -thick: + scroll_x,scroll_y = self.album_collection.sb.get_scrolled() + rtn_val = self.album_collection.click(x,y + scroll_y) + if not rtn_val: + #create a new album + pass + elif x > album_column_width and x < (screen_w - thick): + if l: + self.album_collection.album_objects[self.album_collection.selected_album_id].click(x,y) + elif r: + self.in_grab = True + self.album_collection.start_grab(x,y) pygame.display.flip() def process_mouse_double_click(self,event): diff --git a/photo_toolbar.py b/photo_toolbar.py index 3903c3d..cb92bae 100644 --- a/photo_toolbar.py +++ b/photo_toolbar.py @@ -72,6 +72,13 @@ class ActivityToolbar(gtk.Toolbar): self.delete_album.show() self.insert(self.delete_album,-1) + self.empty_journal_button = ToolButton() + self.empty_journal_button.set_stock_id('gtk-cancel') + self.empty_journal_button.set_tooltip(_("Empty Trash")) + self.empty_journal_button.hide() + self.empty_journal_button.connect('clicked',self.__empty_trash_clicked_cb) + self.insert(self.empty_journal_button,-1) + """ @@ -139,6 +146,9 @@ class ActivityToolbar(gtk.Toolbar): def __delete_album_clicked_cb (self,button): self._activity.activity_toolbar_delete_album_cb() + def __empty_trash_clicked_cb(self,button): + self._activity.activity_toolbar_empty_trash_cb() + def __traceback_changed_cb(self, combo): model = self.share.combo.get_model() it = self.share.combo.get_active_iter() @@ -145,10 +145,17 @@ class Datastore_SQLite(): ds_obj.destroy() return(fn) return None + + def delete_jobject_id_from_datastore(self,jobject_id): + try: + datastore.delete(jobject_id) + except Exception,e: + _logger.debug('delete_jobject_id_from_datastore error: %s'%e) class FileTree(): - def __init__(self,db): + def __init__(self,db,activity): self.db = db + self._activity = activity self.dialog = None def get_path(self): @@ -186,43 +193,66 @@ class FileTree(): self.dialog.destroy() self.dialog = None return fname + + def _response_cb(self,alert,response): + self._activity.remove_alert(alert) + if response == gtk.RESPONSE_CANCEL: + self.cancel = True + def copy_tree_to_ds(self,path): - added = 0 - for dirpath, dirnames, filenames in os.walk(path): - for filename in filenames: - start = time.clock() - abspath = os.path.join(dirpath, filename) - #print abs_path - mtype = '' - chunks = abspath.split('.') - if len(chunks)>1: - ext = chunks[-1] - if ext == 'jpg' or ext == 'jpeg': - mtype = 'image/jpg' - elif ext == 'gif': - mtype = 'image/gif' - elif ext == 'png': - mtype = 'image/png' - if mtype == '': continue - info = os.stat(abspath) - size = info.st_size - if self.db.check_in_ds(abspath,size): continue - ds = datastore.create() - ds.metadata['filename'] = abspath - ds.metadata['title'] = filename - ds.metadata['mime_type'] = mtype - dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'instance',filename) - shutil.copyfile(abspath,dest) - ds.set_file_path(dest) - datastore.write(ds,transfer_ownership=True) - self.db.create_picture_record(ds.object_id,abspath) - ds.destroy() - _logger('writing one image to datastore took %f seconds'%(time.clock-start)) - added += 1 - return added - return 0 - + added = 0 + self.cancel = False + proc_start = time.clock() + dirlist = os.listdir(path) + num = len(dirlist) + message = _('Number of images to copy to the XO Journal: ') + str(num) + pa_title = _('Please be patient') + alert = display.ProgressAlert(msg=message,title=pa_title) + self._activity.add_alert(alert) + alert.connect('response',self._response_cb) + for filename in dirlist: + start = time.clock() + abspath = os.path.join(path, filename) + #print abs_path + mtype = '' + chunks = abspath.split('.') + if len(chunks)>1: + ext = chunks[-1] + if ext == 'jpg' or ext == 'jpeg': + mtype = 'image/jpg' + elif ext == 'gif': + mtype = 'image/gif' + elif ext == 'png': + mtype = 'image/png' + if mtype == '': continue + info = os.stat(abspath) + size = info.st_size + + #if path and size are equal to image already loaded, abort + if self.db.check_in_ds(abspath,size): continue + ds = datastore.create() + ds.metadata['filename'] = abspath + ds.metadata['title'] = filename + ds.metadata['mime_type'] = mtype + dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'instance',filename) + shutil.copyfile(abspath,dest) + ds.set_file_path(dest) + datastore.write(ds,transfer_ownership=True) + self.db.create_picture_record(ds.object_id,abspath) + ds.destroy() + _logger.debug('writing one image to datastore took %f seconds'%(time.clock()-start)) + added += 1 + alert.set_fraction(float(added)/num) + while gtk.events_pending(): + gtk.main_iteration() + if self.cancel: + break + + _logger.debug('writing all images to datastore took %f seconds'%(time.clock()-proc_start)) + self._activity.remove_alert(alert) + return added + def fill_ds(self): path = self.get_path() if path: diff --git a/startup_images/XoPhoto.svg b/startup_images/XoPhoto.svg deleted file mode 100644 index d15c7e4..0000000 --- a/startup_images/XoPhoto.svg +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Generator: Adobe Illustrator 10, SVG Export Plug-In . SVG Version: 3.0.0 Build 76) --> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [ - <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/"> - <!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/"> - <!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/"> - <!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/"> - <!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/"> - <!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/"> - <!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/"> - <!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/"> - <!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/"> - <!ENTITY ns_svg "http://www.w3.org/2000/svg"> - <!ENTITY ns_xlink "http://www.w3.org/1999/xlink"> - <!ENTITY stroke_color "#666666"> - <!ENTITY fill_color "#CCCCCC"> -]> -<svg - xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;" i:viewOrigin="254.2856 419.2651" i:rulerOrigin="0 0" i:pageBounds="0 792 612 0" - xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" - width="48.758" height="48.736" viewBox="0 0 48.758 48.736" overflow="visible" enable-background="new 0 0 48.758 48.736" - xml:space="preserve"> - <metadata> - <variableSets xmlns="http://ns.adobe.com/Variables/1.0/"> - <variableSet varSetName="binding1" locked="none"> - <variables></variables> - <v:sampleDataSets xmlns="http://ns.adobe.com/GenericCustomNamespace/1.0/" xmlns:v="http://ns.adobe.com/Variables/1.0/"> - </v:sampleDataSets> - </variableSet> - </variableSets> - <sfw xmlns="&ns_sfw;"> - <slices></slices> - <sliceSourceBounds y="370.529" x="254.286" width="48.758" height="48.736" bottomLeftOrigin="true"></sliceSourceBounds> - </sfw> - </metadata> - <g id="Layer_1" i:knockout="Off" i:layer="yes" i:dimmedPercent="50" i:rgbTrio="#4F008000FFFF"> - - <path i:knockout="Off" fill="#FFFFFF" stroke="&stroke_color;" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" d=" - M43.07,8.245h4.139v31.833H17.866l-0.868,0.146L6.3,42.01L1.218,10.585l3.876-0.647l0.441-0.074l0.008-1.62h9.773l2.844-0.49 - l24.154-4.036l0.602,3.604L43.07,8.245z"/> - <path i:knockout="Off" fill="#FFFFFF" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" d="M46.627,39.161H6.21 - V9.245h40.417V39.161z"/> - <path i:knockout="Off" fill="&fill_color;" d="M43.627,11.578H8.96v24.75h6.433c0.092-1.765,0.24-3.677,0.48-4.591 - c0.447-1.695,4.26-4.017,8.416-4.562c-2.058-0.904-3.535-3.35-3.535-6.229c0-3.65,2.369-6.61,5.289-6.61s5.289,2.96,5.289,6.61 - c0,2.88-1.477,5.324-3.533,6.229c4.154,0.545,7.967,2.865,8.414,4.562c0.24,0.914,0.389,2.826,0.479,4.591h6.936L43.627,11.578z" - /> - <path i:knockout="Off" fill="none" stroke="&stroke_color;" stroke-width="0.5" stroke-linejoin="bevel" d="M15.392,36.328 - c0.092-1.765,0.24-3.677,0.48-4.591c0.447-1.695,4.26-4.017,8.417-4.562c-2.058-0.904-3.536-3.35-3.536-6.229 - c0-3.65,2.37-6.61,5.29-6.61s5.289,2.96,5.289,6.61c0,2.88-1.477,5.324-3.533,6.229c4.154,0.545,7.967,2.865,8.414,4.562 - c0.24,0.914,0.389,2.826,0.479,4.591"/> - <path i:knockout="Off" fill="none" stroke="&stroke_color;" stroke-width="0.5" d="M43.627,36.328H8.96v-24.75h34.667V36.328z"/> - <path i:knockout="Off" fill="none" d="M48.758,48.736H0.022V0h48.736V48.736z"/> - </g> -</svg> diff --git a/startup_images/reuiideasmynewxo.zip b/startup_images/reuiideasmynewxo.zip Binary files differdeleted file mode 100644 index 3e19fa2..0000000 --- a/startup_images/reuiideasmynewxo.zip +++ /dev/null diff --git a/startup_images/xophoto.sql b/startup_images/xophoto.sql deleted file mode 100644 index fc5189b..0000000 --- a/startup_images/xophoto.sql +++ /dev/null @@ -1,18 +0,0 @@ -DROP TABLE IF EXISTS "config"; -CREATE TABLE "config" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "name" TEXT NOT NULL , "value" TEXT, "int" INTEGER, "real" FLOAT, "blob" BLOB, "date" DATETIME); -INSERT INTO "config" VALUES(1,'mime_type','image/png',NULL,NULL,NULL,NULL); -INSERT INTO "config" VALUES(2,'mime_type','image/jpg',NULL,NULL,NULL,NULL); -INSERT INTO "config" VALUES(3,'mime_type','image/gif',NULL,NULL,NULL,NULL); -DROP TABLE IF EXISTS "groups"; -CREATE TABLE "groups" ("id" INTEGER PRIMARY KEY NOT NULL ,"category" TEXT,"subcategory" TEXT,"jobject_id" TEXT,"seq" INTEGER,"newseq" INTEGER); -DROP TABLE IF EXISTS "picture"; -CREATE TABLE "picture" ("jobject_id" TEXT NOT NULL ,"mount_point" TEXT,"orig_width" INTEGER,"orig_height" INTEGER,"mime_type" TEXT,"create_date" DATETIME,"title" TEXT,"comment" TEXT,"id" INTEGER PRIMARY KEY NOT NULL ,"seq" INTEGER,"orig_size" DOUBLE,"album" TEXT,"in_ds" INTEGER, "md5_sum" TEXT, "longitude" FLOAT, "latitude" FLOAT, "duplicate" INTEGER DEFAULT 0); -DROP TABLE IF EXISTS "sqlite_sequence"; -CREATE TABLE sqlite_sequence(name,seq); -INSERT INTO "sqlite_sequence" VALUES('config',3); -DROP TABLE IF EXISTS "transforms"; -CREATE TABLE "transforms" ("id" INTEGER PRIMARY KEY NOT NULL ,"jobject_id" TEXT,"original_x" INTEGER,"original_y" INTEGER,"scaled_x" INTEGER,"scaled_y" INTEGER,"thumb" blob); -CREATE INDEX "grp_order" ON "groups" ("category" ASC, "subcategory" ASC, "seq" ASC); -CREATE UNIQUE INDEX "jobject_id" ON "picture" ("jobject_id" ASC); -CREATE INDEX "name" ON "config" ("name" ASC, "int" ASC); -CREATE UNIQUE INDEX "path" ON "picture" ("mount_point" ASC, "orig_size" ASC); diff --git a/xophoto.sqlite b/xophoto.sqlite Binary files differindex 31cd97b..c2a058b 100644 --- a/xophoto.sqlite +++ b/xophoto.sqlite diff --git a/xophotoactivity.py b/xophotoactivity.py index 9c1b38e..c4b3f94 100644 --- a/xophotoactivity.py +++ b/xophotoactivity.py @@ -87,6 +87,7 @@ class XoPhotoActivity(activity.Activity): self.game = None self.kept_once = False self.util = Utilities(self) + self.db_sanity_check = False #there appears to be an initial save yourself, asynchronous, which # in my write_file closes the database and causes sporatic failures @@ -102,6 +103,11 @@ class XoPhotoActivity(activity.Activity): _logger.debug('At activity startup, handle.object_id is None. Making a new datastore entry') activity.Activity.__init__(self, handle, create_jobject = self.make_jobject) + #does activity init execute the read? check if dbobject is reliably open + if self.DbAccess_object: + _logger.debug('database object is_open:%s'%self.DbAccess_object.is_open()) + else: + _logger.debug('after activity init, read has not been called') self.make_jobject = False #following are essential for interface to Help @@ -154,6 +160,8 @@ class XoPhotoActivity(activity.Activity): self.toolbox.add_toolbar(_('Edit'), self.edit_toolbar) self.edit_toolbar.connect('do-import', self.edit_toolbar_doimport_cb) + self.edit_toolbar.connect('do-initialize', + self.edit_toolbar_doinitialize_cb) self.edit_toolbar.connect('do-stop', self.__stop_clicked_cb) self.edit_toolbar.show() @@ -217,6 +225,22 @@ class XoPhotoActivity(activity.Activity): if not response == gtk.RESPONSE_OK:return self.game.album_collection.delete_album(album_id) + def activity_toolbar_empty_trash_cb(self): + self.util.confirmation_alert(_('Are you sure you want to proceed?'),\ + _('Warning! you are about to completely remove these images from your XO.'),\ + self.empty_trash_cb) + + def empty_trash_cb(self,alert,response,album_id=trash_id): + if not response == gtk.RESPONSE_OK:return + rows = self.DbAccess_object.get_album_thumbnails(album_id) + for row in rows: + jobject_id = str(row['jobject_id']) + Datastore_SQLite(self.game.db).delete_jobject_id_from_datastore(jobject_id) + self.DbAccess_object.delete_all_references_to(jobject_id) + if self.game.album_collection: + self.game.album_collection.display_thumbnails(trash_id,new_surface=True) + self.game.album_collection.paint_albums() + def command_line(self,cmd, alert_error=False): _logger.debug('command_line cmd:%s'%cmd) p1 = Popen(cmd,stdout=PIPE, shell=True) @@ -267,36 +291,24 @@ class XoPhotoActivity(activity.Activity): datastore.write(ds,transfer_ownership=True) ds.destroy() - #check the thumbnails - cursor.execute('pragma data_cache.quick_check') - rows = cursor.fetchall() - if len(rows) == 1 and str(rows[0]) == 'ok': - #thumbnails database is ok - _logger.debug('thumbnail database passes quick_check') - else: - #need to start over with a new template and regenerate the thumbnails - _logger.debug('swapping in template for transforms (thumbnail) database') - - try: - source = os.path.join(os.getcwd(),'data_cache.sqlite.template') - local_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','data_cache.sqlite') - shutil.copy(source,local_path) - except Exception,e: - _logger.debug('data_cache template failed to copy error:%s'%e) - exit() def edit_toolbar_doimport_cb(self, view_toolbar): if not self.file_tree: - self.file_tree = FileTree(self.game.db) + self.file_tree = FileTree(self.game.db,self) path = self.file_tree.get_path() pygame.display.flip() if path: self.file_tree.copy_tree_to_ds(path) Datastore_SQLite(self.game.db).check_for_recent_images() + + def edit_toolbar_doinitialize_cb(self, view_toolbar): + self.empty_trash_cb(None,gtk.RESPONSE_OK,journal_id) + self.read_file(None,initialize=True) + def use_toolbar_doexport_cb(self,use_toolbar): if not self.file_tree: - self.file_tree = FileTree(self.game.db) + self.file_tree = FileTree(self.game.db,self) base_path = self.file_tree.get_path() #think about writing the whole journal, and the trash (would need to add these to the selectable paths) @@ -350,10 +362,12 @@ class XoPhotoActivity(activity.Activity): def use_toolbar_doslideshow_cb(self,use_toolbar): pass - def read_file(self, file_path): + def read_file(self, file_path, initialize=False): _logger.debug('started read_file %s. make_file flag %s'%(file_path,self.make_jobject)) - if self.make_jobject: #make jobject is flag signifying that we are not resuming activity + if self.make_jobject or initialize: #make jobject is flag signifying that we are not resuming activity _logger.debug(' copied template rather than resuming') + if self.DbAccess_object: + self.DbAccess_object.closedb() #This is a new invocation, copy the sqlite database to the data directory source = os.path.join(os.getcwd(),'xophoto.sqlite.template') @@ -367,7 +381,7 @@ class XoPhotoActivity(activity.Activity): #now do the same for the thumbnails if they don't already exist source = os.path.join(os.getcwd(),'data_cache.sqlite.template') dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','data_cache.sqlite') - if not os.path.isfile(dest): + if not os.path.isfile(dest) or initialize: try: shutil.copy(source,dest) except Exception,e: @@ -380,7 +394,7 @@ class XoPhotoActivity(activity.Activity): dict = self.get_metadata() _logger.debug('title was %s'%dict.get('title','no title given')) dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','xophoto.sqlite') - _logger.debug('reading from %s and writeing to %s'%(file_path,dest,)) + _logger.debug('reading from %s and writing to %s'%(file_path,dest,)) try: shutil.copy(file_path, dest) _logger.debug('completed writing the sqlite file') @@ -393,8 +407,49 @@ class XoPhotoActivity(activity.Activity): except Exception,e: _logger.debug('database failed to open in read file. error:%s'%e) exit() - _logger.debug('completed read_file. DbAccess_jobject is created') + + #if this is the first time the databases are to be used this invocation? + #if the databases are not well formed, rebuild from a template + if not self.db_sanity_check: + self.db_sanity_check = True + conn = self.DbAccess_object.connection() + c = conn.cursor() + c.execute('pragma quick_check') + rows = c.fetchall() + if len(rows) == 1 and str(rows[0][0]) == 'ok': + #main database is ok + _logger.debug('xophoto database passes quick_check') + else: + #need to start over with a new template and regenerate the thumbnails + _logger.debug('swapping in template for xophoto.sqlite database') + + try: + source = os.path.join(os.getcwd(),'xophoto.sqlite.template') + local_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','xophoto.sqlite') + shutil.copy(source,local_path) + except Exception,e: + _logger.debug('xophoto template failed to copy error:%s'%e) + exit() + + #check the thumbnails + c.execute('pragma data_cache.quick_check') + rows = c.fetchall() + if len(rows) == 1 and str(rows[0][0]) == 'ok': + #thumbnails database is ok + _logger.debug('thumbnail database passes quick_check') + else: + #need to start over with a new template and regenerate the thumbnails + _logger.debug('swapping in template for transforms (thumbnail) database') + + try: + source = os.path.join(os.getcwd(),'data_cache.sqlite.template') + local_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','data_cache.sqlite') + shutil.copy(source,local_path) + except Exception,e: + _logger.debug('data_cache template failed to copy error:%s'%e) + exit() + _logger.debug('completed read_file. DbAccess_jobject is created') def write_file(self, file_path): @@ -455,6 +510,9 @@ class EditToolbar(gtk.Toolbar): 'do-import': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'do-initialize': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([])), 'do-stop': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) @@ -471,8 +529,9 @@ class EditToolbar(gtk.Toolbar): self.doimport.show() self.delete_comment = ToolButton() - self.delete_comment.set_stock_id('gtk-stock-delete') - self.delete_comment.set_tooltip(_("Remove Picture")) + self.delete_comment.set_stock_id('gtk.stock-delete') + self.delete_comment.set_tooltip(_("Re-Inialize the Databass -- for startup testing")) + self.delete_comment.connect('clicked',self.do_initialize) self.delete_comment.show() self.insert(self.delete_comment,-1) @@ -489,11 +548,11 @@ class EditToolbar(gtk.Toolbar): tool_item.add(self.entry) self.entry.show() self.insert(tool_item, -1) - tool_item.show() + tool_item.hide() self.add_comment = ToolButton('list-add') self.add_comment.set_tooltip(_("Add Annotation")) - self.add_comment.show() + self.add_comment.hide() self.insert(self.add_comment,-1) separator = gtk.SeparatorToolItem() @@ -510,6 +569,9 @@ class EditToolbar(gtk.Toolbar): def doimport_cb(self, button): self.emit('do-import') + + def do_initialize(self, button): + self.emit('do-initialize') def dostop_cb(self, button): self.emit('do-stop') @@ -550,7 +612,7 @@ class UseToolbar(gtk.Toolbar): self.doupload.set_tooltip(_('Fullscreen')) self.doupload.connect('clicked', self.doupload_cb) self.insert(self.doupload, -1) - self.doupload.show() + self.doupload.hide() separator = gtk.SeparatorToolItem() separator.props.draw = False @@ -563,7 +625,7 @@ class UseToolbar(gtk.Toolbar): self.doslideshow.set_tooltip(_('SlideShow')) self.doslideshow.connect('clicked', self.doslideshow_cb) self.insert(self.doslideshow, -1) - self.doslideshow.show() + self.doslideshow.hide() separator = gtk.SeparatorToolItem() separator.props.draw = False |