Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorroot <root@ghunt-desktop.(none)>2010-07-06 10:53:22 (GMT)
committer root <root@ghunt-desktop.(none)>2010-07-06 10:53:22 (GMT)
commitfbaad3095b5a0ccf51fdf2a3dd7f3c0551edbe7e (patch)
tree08df3364b559b417bfaaa5c8e5c54cf4e88a49e9
parent12405ccda4ec4d2f2b35d01a72e20d445a010ae8 (diff)
progress bar during load to datastore
-rw-r--r--dbphoto.py57
-rw-r--r--display.py316
-rw-r--r--photo_toolbar.py10
-rw-r--r--sources.py102
-rw-r--r--startup_images/XoPhoto.svg54
-rw-r--r--startup_images/reuiideasmynewxo.zipbin57363 -> 0 bytes
-rw-r--r--startup_images/xophoto.sql18
-rw-r--r--xophoto.sqlitebin2486272 -> 2486272 bytes
-rw-r--r--xophotoactivity.py122
9 files changed, 433 insertions, 246 deletions
diff --git a/dbphoto.py b/dbphoto.py
index e35feb8..958c458 100644
--- a/dbphoto.py
+++ b/dbphoto.py
@@ -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
diff --git a/display.py b/display.py
index ea42cfd..cde41bd 100644
--- a/display.py
+++ b/display.py
@@ -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()
diff --git a/sources.py b/sources.py
index 7e4d1db..dad5311 100644
--- a/sources.py
+++ b/sources.py
@@ -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
deleted file mode 100644
index 3e19fa2..0000000
--- a/startup_images/reuiideasmynewxo.zip
+++ /dev/null
Binary files differ
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
index 31cd97b..c2a058b 100644
--- a/xophoto.sqlite
+++ b/xophoto.sqlite
Binary files differ
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