More work on progress monitor.

svn: r8142
This commit is contained in:
Richard Taylor 2007-02-17 19:59:21 +00:00
parent e197638418
commit 625fbee200
11 changed files with 144 additions and 66 deletions

View File

@ -1,3 +1,15 @@
2007-02-17 Richard Taylor <rjt-gramps@thegrindstone.me.uk>
* src/ViewManager.py: add progress monitor
* src/GrampsDb/_GrampsDbBase.py: add get_length to cursors
* src/GrampsDb/_LongOpStatus.py: add __del__ method
* src/GrampsDb/_ProgressMonitor.py: add class params
* src/GrampsDb/_GrampsInMemDB.py: add get_length to cursors
* src/GrampsDb/_CursorIterator.py: use get_length methods
* src/GrampsDb/_GrampsBSDDB.py: add get_length to cursors
* src/DisplayState.py: add progress monitor
* src/DisplayModels/_PeopleModel.py: use LongOpStatus
* src/ProgressDialog.py: fix show method
2007-02-17 Brian Matherly <brian@gramps-project.org> 2007-02-17 Brian Matherly <brian@gramps-project.org>
* src/ReportBase/_Report.py: remove unused progress bar functions * src/ReportBase/_Report.py: remove unused progress bar functions

View File

@ -65,6 +65,7 @@ import DateHandler
import ToolTips import ToolTips
import GrampsLocale import GrampsLocale
import Config import Config
from GrampsDb import LongOpStatus
from Filters import SearchFilter, ExactSearchFilter from Filters import SearchFilter, ExactSearchFilter
from Lru import LRU from Lru import LRU
@ -321,10 +322,11 @@ class PeopleModel(gtk.GenericTreeModel):
self.mapper.clear_sort_names() self.mapper.clear_sort_names()
cursor = self.db.get_person_cursor() #cursor = self.db.get_person_cursor()
node = cursor.first() #node = cursor.first()
while node: #while node:
for node in self.db.get_person_cursor_iter():
handle, d = node handle, d = node
if not (handle in skip or (dfilter and not dfilter.match(handle))): if not (handle in skip or (dfilter and not dfilter.match(handle))):
name_data = d[PeopleModel._NAME_COL] name_data = d[PeopleModel._NAME_COL]
@ -333,8 +335,8 @@ class PeopleModel(gtk.GenericTreeModel):
sorted_name = nsn(name_data) sorted_name = nsn(name_data)
self.mapper.assign_sort_name(handle, sorted_name, group_name) self.mapper.assign_sort_name(handle, sorted_name, group_name)
node = cursor.next() #node = cursor.next()
cursor.close() #cursor.close()
def _build_filter_sub(self,dfilter, skip): def _build_filter_sub(self,dfilter, skip):
@ -348,7 +350,13 @@ class PeopleModel(gtk.GenericTreeModel):
self.mapper.clear_sort_names() self.mapper.clear_sort_names()
status = LongOpStatus(msg="Loading People",
total_steps=len(handle_list),
interval=len(handle_list)/10)
self.db.emit('long-op-start', (status,))
for handle in handle_list: for handle in handle_list:
status.heartbeat()
d = self.db.get_raw_person_data(handle) d = self.db.get_raw_person_data(handle)
if not (handle in skip or (dfilter and not dfilter.match(handle))): if not (handle in skip or (dfilter and not dfilter.match(handle))):
name_data = d[PeopleModel._NAME_COL] name_data = d[PeopleModel._NAME_COL]
@ -358,6 +366,8 @@ class PeopleModel(gtk.GenericTreeModel):
self.mapper.assign_sort_name(handle, sorted_name, group_name) self.mapper.assign_sort_name(handle, sorted_name, group_name)
status.end()
def calculate_data(self, dfilter=None, skip=[]): def calculate_data(self, dfilter=None, skip=[]):
""" """
Calculates the new path to node values for the model. Calculates the new path to node values for the model.

View File

@ -260,10 +260,12 @@ class DisplayState(GrampsDb.GrampsDBCallback):
'plugins-reloaded' : (list,list), 'plugins-reloaded' : (list,list),
} }
def __init__(self, window, status, progress, warnbtn, uimanager): def __init__(self, window, status, progress, warnbtn, uimanager,
progress_monitor):
self.busy = False self.busy = False
self.uimanager = uimanager self.uimanager = uimanager
self.progress_monitor = progress_monitor
self.window = window self.window = window
GrampsDb.GrampsDBCallback.__init__(self) GrampsDb.GrampsDBCallback.__init__(self)
self.status = status self.status = status
@ -290,6 +292,7 @@ class DisplayState(GrampsDb.GrampsDBCallback):
def db_changed(self, db): def db_changed(self, db):
from PluginUtils import _PluginMgr from PluginUtils import _PluginMgr
self.relationship = _PluginMgr.relationship_class(db) self.relationship = _PluginMgr.relationship_class(db)
db.connect('long-op-start', self.progress_monitor.add_op)
def display_relationship(self,dbstate): def display_relationship(self,dbstate):
default_person = dbstate.db.get_default_person() default_person = dbstate.db.get_default_person()

View File

@ -2,41 +2,41 @@ from _LongOpStatus import LongOpStatus
class CursorIterator(object): class CursorIterator(object):
def __init__(self,db,cursor): def __init__(self, db, cursor, msg=""):
self._db = db self._db = db
self._cursor = cursor self._cursor = cursor
#self._status = LongOpStatus(total_steps=cursor.get_length(),interval=10) self._status = LongOpStatus(total_steps=cursor.get_length(), interval=10)
self._status = LongOpStatus() #self._status = LongOpStatus(msg=msg)
def __iter__(self): def __iter__(self):
try: try:
# Emit start signal # Emit start signal
self._db.emit('long-op-start',(self._status,)) self._db.emit('long-op-start', (self._status,))
first = self._cursor.first() first = self._cursor.first()
if first: if first:
yield first yield first
next = self._cursor.next() next = self._cursor.next()
while next: while next:
yield next yield next
# check for cancel # check for cancel
#if self._status.should_cancel(): #if self._status.should_cancel():
# raise GrampsDbUserCancel # raise GrampsDbUserCancel
# emit heartbeat # emit heartbeat
self._status.heartbeat() self._status.heartbeat()
next = self._cursor.next() next = self._cursor.next()
# emit stop signal # emit stop signal
self._status.end() self._status.end()
self._cursor.close() self._cursor.close()
raise StopIteration raise StopIteration
except: except:
# Not allowed to use 'finally' because we # Not allowed to use 'finally' because we
# yeild inside the try clause. # yeild inside the try clause.
self._cursor.close() self._cursor.close()
self._status_end() self._status.end()
raise raise

View File

@ -75,6 +75,7 @@ class GrampsBSDDBCursor(GrampsCursor):
def __init__(self,source,txn=None): def __init__(self,source,txn=None):
self.cursor = source.db.cursor(txn) self.cursor = source.db.cursor(txn)
self.source = source
def first(self): def first(self):
d = self.cursor.first() d = self.cursor.first()
@ -93,11 +94,15 @@ class GrampsBSDDBCursor(GrampsCursor):
def delete(self): def delete(self):
self.cursor.delete() self.cursor.delete()
def get_length(self):
return self.source.stat()['ndata']
class GrampsBSDDBAssocCursor(GrampsCursor): class GrampsBSDDBAssocCursor(GrampsCursor):
def __init__(self,source,txn=None): def __init__(self,source,txn=None):
self.cursor = source.cursor(txn) self.cursor = source.cursor(txn)
self.source = source
def first(self): def first(self):
d = self.cursor.first() d = self.cursor.first()
@ -116,6 +121,9 @@ class GrampsBSDDBAssocCursor(GrampsCursor):
def delete(self): def delete(self):
self.cursor.delete() self.cursor.delete()
def get_length(self):
return self.source.stat()['ndata']
class GrampsBSDDBDupCursor(GrampsBSDDBAssocCursor): class GrampsBSDDBDupCursor(GrampsBSDDBAssocCursor):
"""Cursor that includes handling for duplicate keys""" """Cursor that includes handling for duplicate keys"""

View File

@ -132,6 +132,14 @@ class GrampsCursor:
finished using the cursor, freeing up the cursor's resources. finished using the cursor, freeing up the cursor's resources.
""" """
pass pass
def get_length(self):
"""
Returns the number of records in the table referenced by the
cursor
"""
raise NotImplementedError, \
"get_length must be implemented by all subclasses of GrampsCursor"
class GrampsDbBookmarks: class GrampsDbBookmarks:
def __init__(self, default = []): def __init__(self, default = []):
@ -346,44 +354,44 @@ class GrampsDbBase(GrampsDBCallback):
def get_person_cursor(self): def get_person_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_person_cursor_iter(self): def get_person_cursor_iter(self, msg=_("Processing Person records")):
return CursorIterator(self,self.get_person_cursor()) return CursorIterator(self, self.get_person_cursor(), msg)
def get_family_cursor(self): def get_family_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_family_cursor_iter(self): def get_family_cursor_iter(self, msg=_("Processing Family records")):
return CursorIterator(self,self.get_family_cursor()) return CursorIterator(self, self.get_family_cursor(), msg)
def get_event_cursor(self): def get_event_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_event_cursor_iter(self): def get_event_cursor_iter(self, msg=_("Processing Event records")):
return CursorIterator(self,self.get_event_cursor()) return CursorIterator(self, self.get_event_cursor(), msg)
def get_place_cursor(self): def get_place_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_place_cursor_iter(self): def get_place_cursor_iter(self, msg=_("Processing Place records")):
return CursorIterator(self,self.get_place_cursor()) return CursorIterator(self, self.get_place_cursor(), msg)
def get_source_cursor(self): def get_source_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_source_cursor_iter(self): def get_source_cursor_iter(self, msg=_("Processing Source records")):
return CursorIterator(self,self.get_source_cursor()) return CursorIterator(self, self.get_source_cursor(), msg)
def get_media_cursor(self): def get_media_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_media_cursor_iter(self): def get_media_cursor_iter(self, msg=_("Processing Media records")):
return CursorIterator(self,self.get_media_cursor()) return CursorIterator(self, self.get_media_cursor(), msg)
def get_repository_cursor(self): def get_repository_cursor(self):
assert False, "Needs to be overridden in the derived class" assert False, "Needs to be overridden in the derived class"
def get_repository_cursor_iter(self): def get_repository_cursor_iter(self, msg=_("Processing Repository records")):
return CursorIterator(self,self.get_repository_cursor()) return CursorIterator(self, self.get_repository_cursor(), msg)
def open_undodb(self): def open_undodb(self):
if not self.readonly: if not self.readonly:

View File

@ -63,6 +63,9 @@ class GrampsInMemCursor(GrampsCursor):
def close(self): def close(self):
pass pass
def get_length(self):
return len(self.src_map)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #

View File

@ -91,7 +91,12 @@ class LongOpStatus(GrampsDBCallback):
self._countdown = interval self._countdown = interval
self._secs_left = 0 self._secs_left = 0
self._start = time.time() self._start = time.time()
self._running = True
def __del__(self):
if self._running:
self.emit('op-end')
def heartbeat(self): def heartbeat(self):
"""This should be called for each step in the operation. It will """This should be called for each step in the operation. It will
emit a 'op-heartbeat' every 'interval' steps. It recalcuates the emit a 'op-heartbeat' every 'interval' steps. It recalcuates the
@ -128,6 +133,7 @@ class LongOpStatus(GrampsDBCallback):
"""End the operation. Causes the 'op-end' signal to be emitted. """End the operation. Causes the 'op-end' signal to be emitted.
""" """
self.emit('op-end') self.emit('op-end')
self._running = False
def should_cancel(self): def should_cancel(self):
"""Returns true of the user has asked for the operation to be cancelled. """Returns true of the user has asked for the operation to be cancelled.

View File

@ -2,6 +2,10 @@
This module provides a progess dialog for displaying the status of This module provides a progess dialog for displaying the status of
long running operations. long running operations.
""" """
import logging
log = logging.getLogger(".GrampsDb")
from gettext import gettext as _
class _StatusObjectFacade(object): class _StatusObjectFacade(object):
"""This provides a simple structure for recording the information """This provides a simple structure for recording the information
@ -36,15 +40,27 @@ class ProgressMonitor(object):
__default_popup_time = 5 # seconds __default_popup_time = 5 # seconds
def __init__(self, dialog_class, popup_time = None): def __init__(self, dialog_class, dialog_class_params=(),
title=_("Progress Information"),
popup_time = None):
""" """
@param dialog_class: A class used to display the progress dialog. @param dialog_class: A class used to display the progress dialog.
@type dialog_class: L{_GtkProgressDialog} or the same interface. @type dialog_class: L{_GtkProgressDialog} or the same interface.
@param dialog_class_params: A tuple that will be used as the initial
arguments to the dialog_class, this might be used for passing in
a parent window handle.
@type dialog_class_params: tuple
@param title: The title of the progress dialog
@type title: string
@param popup_time: number of seconds to wait before popup. @param popup_time: number of seconds to wait before popup.
@type popup_time: int @type popup_time: int
""" """
self._dialog_class = dialog_class self._dialog_class = dialog_class
self._dialog_class_params = dialog_class_params
self._title = title
self._popup_time = popup_time self._popup_time = popup_time
if self._popup_time == None: if self._popup_time == None:
@ -55,8 +71,10 @@ class ProgressMonitor(object):
def _get_dlg(self): def _get_dlg(self):
if self._dlg == None: if self._dlg == None:
self._dlg = self._dialog_class("Long running operation.") self._dlg = self._dialog_class(self._dialog_class_params,
self._dlg.show() self._title)
self._dlg.show()
return self._dlg return self._dlg
@ -66,6 +84,8 @@ class ProgressMonitor(object):
@param op_status: the status object. @param op_status: the status object.
@type op_status: L{GrampsDb.LongOpStatus} @type op_status: L{GrampsDb.LongOpStatus}
""" """
log.debug("adding op to Progress Monitor")
facade = _StatusObjectFacade(op_status) facade = _StatusObjectFacade(op_status)
self._status_stack.append(facade) self._status_stack.append(facade)
idx = len(self._status_stack)-1 idx = len(self._status_stack)-1
@ -84,6 +104,8 @@ class ProgressMonitor(object):
# check the estimated time to complete to see if we need # check the estimated time to complete to see if we need
# to pop up a progress dialog. # to pop up a progress dialog.
log.debug("heartbeat in ProgressMonitor")
facade = self._status_stack[idx] facade = self._status_stack[idx]
if facade.status_obj.estimated_secs_to_complete() > self._popup_time: if facade.status_obj.estimated_secs_to_complete() > self._popup_time:
@ -100,6 +122,8 @@ class ProgressMonitor(object):
def _end(self, idx): def _end(self, idx):
# hide any progress dialog # hide any progress dialog
# remove the status object from the stack # remove the status object from the stack
log.debug("received end in ProgressMonitor")
facade = self._status_stack[idx] facade = self._status_stack[idx]
if facade.active: if facade.active:
dlg = self._get_dlg() dlg = self._get_dlg()
@ -119,7 +143,7 @@ if __name__ == '__main__':
from GrampsDb import LongOpStatus from GrampsDb import LongOpStatus
def test(a,b): def test(a,b):
d = ProgressDialog(_GtkProgressDialog) d = ProgressDialog(_GtkProgressDialog, "Test Progress")
s = LongOpStatus("Doing very long operation", 100, 10) s = LongOpStatus("Doing very long operation", 100, 10)

View File

@ -43,8 +43,8 @@ class _GtkProgressBar(gtk.VBox):
self._pbar_max = (long_op_status.get_total_steps()/ self._pbar_max = (long_op_status.get_total_steps()/
long_op_status.get_interval()) long_op_status.get_interval())
self._pbar_index = 0.0 self._pbar_index = 0.0
self._pbar.set_fraction((float(long_op_status.get_total_steps())/ self._pbar.set_fraction(((100/float(long_op_status.get_total_steps())*
(float(long_op_status.get_interval())))/ float(long_op_status.get_interval())))/
100.0) 100.0)
if msg != '': if msg != '':
@ -70,15 +70,15 @@ class _GtkProgressBar(gtk.VBox):
self._pbar.set_fraction(val/100.0) self._pbar.set_fraction(val/100.0)
self._pbar.old_val = val self._pbar.old_val = val
class _GtkProgressDialog(gtk.Dialog): class GtkProgressDialog(gtk.Dialog):
"""A gtk window to display the status of a long running """A gtk window to display the status of a long running
process.""" process."""
def __init__(self, title): def __init__(self, window_params, title):
"""@param title: The title to display on the top of the window. """@param title: The title to display on the top of the window.
@type title: string @type title: string
""" """
gtk.Dialog.__init__(self) gtk.Dialog.__init__(self, *window_params)
self.connect('delete_event', self._warn) self.connect('delete_event', self._warn)
self.set_has_separator(False) self.set_has_separator(False)
self.set_title(title) self.set_title(title)
@ -162,7 +162,7 @@ if __name__ == '__main__':
from GrampsDb import LongOpStatus, ProgressMonitor from GrampsDb import LongOpStatus, ProgressMonitor
def test(a,b): def test(a,b):
d = ProgressMonitor(_GtkProgressDialog) d = ProgressMonitor(GtkProgressDialog)
s = LongOpStatus("Doing very long operation", 100, 10) s = LongOpStatus("Doing very long operation", 100, 10)

View File

@ -78,6 +78,8 @@ import GrampsWidgets
import UndoHistory import UndoHistory
from DbLoader import DbLoader from DbLoader import DbLoader
import GrampsDisplay import GrampsDisplay
from GrampsDb import ProgressMonitor
import ProgressDialog
def show_url(dialog,link,user_data): def show_url(dialog,link,user_data):
GrampsDisplay.url(link) GrampsDisplay.url(link)
@ -245,6 +247,8 @@ class ViewManager:
vbox.pack_start(self.menubar, False) vbox.pack_start(self.menubar, False)
vbox.pack_start(self.toolbar, False) vbox.pack_start(self.toolbar, False)
vbox.add(hbox) vbox.add(hbox)
self.progress_monitor = ProgressMonitor(ProgressDialog.GtkProgressDialog,
("",self.window))
self.progress = gtk.ProgressBar() self.progress = gtk.ProgressBar()
self.progress.set_size_request(100, -1) self.progress.set_size_request(100, -1)
self.progress.hide() self.progress.hide()
@ -262,7 +266,7 @@ class ViewManager:
self.uistate = DisplayState.DisplayState( self.uistate = DisplayState.DisplayState(
self.window, self.statusbar, self.progress, self.warnbtn, self.window, self.statusbar, self.progress, self.warnbtn,
self.uimanager) self.uimanager, self.progress_monitor)
self.state.connect('database-changed', self.uistate.db_changed) self.state.connect('database-changed', self.uistate.db_changed)
toolbar = self.uimanager.get_widget('/ToolBar') toolbar = self.uimanager.get_widget('/ToolBar')