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>
* src/ReportBase/_Report.py: remove unused progress bar functions

View File

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

View File

@ -260,10 +260,12 @@ class DisplayState(GrampsDb.GrampsDBCallback):
'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.uimanager = uimanager
self.progress_monitor = progress_monitor
self.window = window
GrampsDb.GrampsDBCallback.__init__(self)
self.status = status
@ -290,6 +292,7 @@ class DisplayState(GrampsDb.GrampsDBCallback):
def db_changed(self, db):
from PluginUtils import _PluginMgr
self.relationship = _PluginMgr.relationship_class(db)
db.connect('long-op-start', self.progress_monitor.add_op)
def display_relationship(self,dbstate):
default_person = dbstate.db.get_default_person()

View File

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

View File

@ -75,6 +75,7 @@ class GrampsBSDDBCursor(GrampsCursor):
def __init__(self,source,txn=None):
self.cursor = source.db.cursor(txn)
self.source = source
def first(self):
d = self.cursor.first()
@ -93,11 +94,15 @@ class GrampsBSDDBCursor(GrampsCursor):
def delete(self):
self.cursor.delete()
def get_length(self):
return self.source.stat()['ndata']
class GrampsBSDDBAssocCursor(GrampsCursor):
def __init__(self,source,txn=None):
self.cursor = source.cursor(txn)
self.source = source
def first(self):
d = self.cursor.first()
@ -116,6 +121,9 @@ class GrampsBSDDBAssocCursor(GrampsCursor):
def delete(self):
self.cursor.delete()
def get_length(self):
return self.source.stat()['ndata']
class GrampsBSDDBDupCursor(GrampsBSDDBAssocCursor):
"""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.
"""
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:
def __init__(self, default = []):
@ -346,44 +354,44 @@ class GrampsDbBase(GrampsDBCallback):
def get_person_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_person_cursor_iter(self):
return CursorIterator(self,self.get_person_cursor())
def get_person_cursor_iter(self, msg=_("Processing Person records")):
return CursorIterator(self, self.get_person_cursor(), msg)
def get_family_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_family_cursor_iter(self):
return CursorIterator(self,self.get_family_cursor())
def get_family_cursor_iter(self, msg=_("Processing Family records")):
return CursorIterator(self, self.get_family_cursor(), msg)
def get_event_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_event_cursor_iter(self):
return CursorIterator(self,self.get_event_cursor())
def get_event_cursor_iter(self, msg=_("Processing Event records")):
return CursorIterator(self, self.get_event_cursor(), msg)
def get_place_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_place_cursor_iter(self):
return CursorIterator(self,self.get_place_cursor())
def get_place_cursor_iter(self, msg=_("Processing Place records")):
return CursorIterator(self, self.get_place_cursor(), msg)
def get_source_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_source_cursor_iter(self):
return CursorIterator(self,self.get_source_cursor())
def get_source_cursor_iter(self, msg=_("Processing Source records")):
return CursorIterator(self, self.get_source_cursor(), msg)
def get_media_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_media_cursor_iter(self):
return CursorIterator(self,self.get_media_cursor())
def get_media_cursor_iter(self, msg=_("Processing Media records")):
return CursorIterator(self, self.get_media_cursor(), msg)
def get_repository_cursor(self):
assert False, "Needs to be overridden in the derived class"
def get_repository_cursor_iter(self):
return CursorIterator(self,self.get_repository_cursor())
def get_repository_cursor_iter(self, msg=_("Processing Repository records")):
return CursorIterator(self, self.get_repository_cursor(), msg)
def open_undodb(self):
if not self.readonly:

View File

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

View File

@ -91,7 +91,12 @@ class LongOpStatus(GrampsDBCallback):
self._countdown = interval
self._secs_left = 0
self._start = time.time()
self._running = True
def __del__(self):
if self._running:
self.emit('op-end')
def heartbeat(self):
"""This should be called for each step in the operation. It will
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.
"""
self.emit('op-end')
self._running = False
def should_cancel(self):
"""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
long running operations.
"""
import logging
log = logging.getLogger(".GrampsDb")
from gettext import gettext as _
class _StatusObjectFacade(object):
"""This provides a simple structure for recording the information
@ -36,15 +40,27 @@ class ProgressMonitor(object):
__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.
@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.
@type popup_time: int
"""
self._dialog_class = dialog_class
self._dialog_class_params = dialog_class_params
self._title = title
self._popup_time = popup_time
if self._popup_time == None:
@ -55,8 +71,10 @@ class ProgressMonitor(object):
def _get_dlg(self):
if self._dlg == None:
self._dlg = self._dialog_class("Long running operation.")
self._dlg.show()
self._dlg = self._dialog_class(self._dialog_class_params,
self._title)
self._dlg.show()
return self._dlg
@ -66,6 +84,8 @@ class ProgressMonitor(object):
@param op_status: the status object.
@type op_status: L{GrampsDb.LongOpStatus}
"""
log.debug("adding op to Progress Monitor")
facade = _StatusObjectFacade(op_status)
self._status_stack.append(facade)
idx = len(self._status_stack)-1
@ -84,6 +104,8 @@ class ProgressMonitor(object):
# check the estimated time to complete to see if we need
# to pop up a progress dialog.
log.debug("heartbeat in ProgressMonitor")
facade = self._status_stack[idx]
if facade.status_obj.estimated_secs_to_complete() > self._popup_time:
@ -100,6 +122,8 @@ class ProgressMonitor(object):
def _end(self, idx):
# hide any progress dialog
# remove the status object from the stack
log.debug("received end in ProgressMonitor")
facade = self._status_stack[idx]
if facade.active:
dlg = self._get_dlg()
@ -119,7 +143,7 @@ if __name__ == '__main__':
from GrampsDb import LongOpStatus
def test(a,b):
d = ProgressDialog(_GtkProgressDialog)
d = ProgressDialog(_GtkProgressDialog, "Test Progress")
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()/
long_op_status.get_interval())
self._pbar_index = 0.0
self._pbar.set_fraction((float(long_op_status.get_total_steps())/
(float(long_op_status.get_interval())))/
self._pbar.set_fraction(((100/float(long_op_status.get_total_steps())*
float(long_op_status.get_interval())))/
100.0)
if msg != '':
@ -70,15 +70,15 @@ class _GtkProgressBar(gtk.VBox):
self._pbar.set_fraction(val/100.0)
self._pbar.old_val = val
class _GtkProgressDialog(gtk.Dialog):
class GtkProgressDialog(gtk.Dialog):
"""A gtk window to display the status of a long running
process."""
def __init__(self, title):
def __init__(self, window_params, title):
"""@param title: The title to display on the top of the window.
@type title: string
"""
gtk.Dialog.__init__(self)
gtk.Dialog.__init__(self, *window_params)
self.connect('delete_event', self._warn)
self.set_has_separator(False)
self.set_title(title)
@ -162,7 +162,7 @@ if __name__ == '__main__':
from GrampsDb import LongOpStatus, ProgressMonitor
def test(a,b):
d = ProgressMonitor(_GtkProgressDialog)
d = ProgressMonitor(GtkProgressDialog)
s = LongOpStatus("Doing very long operation", 100, 10)

View File

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