diff --git a/ChangeLog b/ChangeLog index 3b1a39f80..a671e0388 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2007-11-22 Benny Malengier + * src/plugins/relcalc.glade: don't do connect in glade, we need key + * src/plugins/Leak.py: use os.sep, not + '/' + * src/plugins/RelCalc.py: Don't keep recalculating relation map of the + active person, do it once. 50% faster. Make sure all objects can be + collected by the garbage collector + * src/Relationship.py: allow to connect to database. Map of first + person is stored, only to be removed if database changed, or it + concerns a different person. This reduces calculation with 50% + * src/DisplayState.py: don't recalculate home person every time, don't + call relationship calc on every click, only call it when the people + are different + 2007-11-21 Douglas S. Blank * src/gen/lib/date.py: added comparison operator for match() * src/Utils.py: uses new match comparison diff --git a/src/DisplayState.py b/src/DisplayState.py index decfb521b..4c70d13f6 100644 --- a/src/DisplayState.py +++ b/src/DisplayState.py @@ -314,6 +314,9 @@ class DisplayState(gen.utils.GrampsDBCallback): self.phistory = History() self.gwm = ManagedWindow.GrampsWindowManager(uimanager) self.widget = None + self.disprel_old = '' + self.disprel_defpers = None + self.disprel_active = None self.warnbtn = warnbtn self.last_bar = self.status.insert(min_width=15, ralign=True) self.set_relationship_class() @@ -341,13 +344,30 @@ class DisplayState(gen.utils.GrampsDBCallback): self.relationship = _PluginMgr.relationship_class() def display_relationship(self, dbstate): + ''' Construct the relationship in order to show it in the statusbar + This can be a time intensive calculation, so we only want to do + it if persons are different than before. + Eg: select a person, then double click, will result in calling + three times to construct build the statusbar. We only want + to obtain relationship once! + This means the relationship part of statusbar only changes on + change of row. + ''' + self.relationship.connect_db_signals(dbstate) default_person = dbstate.db.get_default_person() active = dbstate.get_active_person() if default_person == None or active == None: return u'' + if default_person.handle == self.disprel_defpers and \ + active.handle == self.disprel_active : + return self.disprel_old name = self.relationship.get_one_relationship( dbstate.db, default_person, active) + #store present call data + self.disprel_old = name + self.disprel_defpers = default_person.handle + self.disprel_active = active.handle if name: return name else: diff --git a/src/Relationship.py b/src/Relationship.py index c34eef0f1..6f7ae7469 100644 --- a/src/Relationship.py +++ b/src/Relationship.py @@ -378,13 +378,14 @@ class RelationshipCalculator: PARTNER_EX_UNKNOWN_REL = 8 def __init__(self): - pass - - def get_parents(self, level): - if level>len(_parents_level)-1: - return "distant ancestors (%d generations)" % level - else: - return _parents_level[level] + self.signal_keys = [] + self.state_signal_key = None + self.storemap = False + self.dirtymap = True + self.stored_map = None + self.map_handle = None + self.map_meta = None + self.__db_connected = False DIST_FATHER = "distant %(step)sancestor%(inlaw)s (%(level)d generations)" @@ -534,6 +535,12 @@ class RelationshipCalculator: # how the siblings are related: return self.UNKNOWN_SIB + def get_parents(self, level): + if level>len(_parents_level)-1: + return "distant ancestors (%d generations)" % level + else: + return _parents_level[level] + def _get_birth_parents(self, db, person): """ method that returns the birthparents of a person as tuple (mother handle, father handle), if no known birthparent, the @@ -749,13 +756,32 @@ class RelationshipCalculator: rank = 9999999 try: - self.__apply_filter(db, orig_person, '', [], firstMap) + if (self.storemap and self.stored_map is not None + and self.map_handle == orig_person.handle + and not self.dirtymap): + firstMap = self.stored_map + self.__maxDepthReached, self.__loopDetected, \ + self.__max_depth, self.__all_families,\ + self.__all_dist, self.__only_birth,\ + self.__crosslinks, self.__msg = self.map_meta + else: + self.__apply_filter(db, orig_person, '', [], firstMap) + self.map_meta = (self.__maxDepthReached, + self.__loopDetected, + self.__max_depth, self.__all_families, + self.__all_dist, self.__only_birth, + self.__crosslinks, self.__msg) self.__apply_filter(db, other_person, '', [], secondMap, stoprecursemap = firstMap) except RuntimeError: return (-1,None,-1,[],-1,[] ) , \ [_("Relationship loop detected")] + self.__msg + if self.storemap: + self.stored_map = firstMap + self.dirtymap = False + self.map_handle = orig_person.handle + for person_handle in secondMap.keys() : if firstMap.has_key(person_handle) : com = [] @@ -844,6 +870,7 @@ class RelationshipCalculator: ''' if person == None or not person.handle : return + if depth > self.__max_depth: self.__maxDepthReached = True print 'Maximum ancestor generations ('+str(depth)+') reached', \ @@ -1712,6 +1739,55 @@ class RelationshipCalculator: else: return _("gender unknown,unknown relation|former partner") + def connect_db_signals(self, dbstate): + """ We can save work by storing a map, however, if database changes + this map must be regenerated. + Before close, the calling app must call disconnect_db_signals + """ + if self.__db_connected: + return + assert(len(self.signal_keys)==0) + self.state_signal_key = dbstate.connect('database-changed', + self._dbchange_callback) + self.__connect_db_signals(dbstate.db) + + def __connect_db_signals(self, db): + signals = ['person-add', 'person-update', 'person-delete', + 'person-rebuild', 'family-add', 'family-update', + 'family-delete', 'family-rebuild', 'database-changed'] + for name in signals: + self.signal_keys.append(db.connect(name, + self._datachange_callback)) + self.storemap = True + self.__db_connected = True + + def disconnect_db_signals(self, dbstate): + """ Method to disconnect to all signals the relationship calculator is + subscribed + """ + dbstate.disconnect(self.state_signal_key) + for key in self.signal_keys: + dbstate.db.disconnect(key) + self.storemap = False + self.stored_map = None + + def _dbchange_callback(self, db): + """ When database changes, the map can no longer be used. + Connects must be remade + """ + self.dirtymap = True + #signals are disconnected on close of old database, connect to new + self.__connect_db_signals(db) + + def _datachange_callback(self, list=[]): + """ When data in database changes, the map can no longer be used. + As the map might be in use or might be generated at the moment, + this method sets a dirty flag. Before reusing the map, this flag + will be checked + """ + self.dirtymap = True + + def _test(rc, onlybirth, inlawa, inlawb, printrelstr): """ this is a generic test suite for the singular relationship TRANSLATORS: do NOT translate, use __main__ ! diff --git a/src/plugins/Leak.py b/src/plugins/Leak.py index e6c7c0fb2..4cbecbc29 100644 --- a/src/plugins/Leak.py +++ b/src/plugins/Leak.py @@ -63,7 +63,7 @@ class Leak(Tool.Tool,ManagedWindow.ManagedWindow): Tool.Tool.__init__(self,dbstate, options_class, name) ManagedWindow.ManagedWindow.__init__(self,uistate,[],self.__class__) - glade_file = "%s/%s" % (os.path.dirname(__file__),"leak.glade") + glade_file = os.path.dirname(__file__) + os.sep + "leak.glade" self.glade = gtk.glade.XML(glade_file,"top","gramps") window = self.glade.get_widget("top") diff --git a/src/plugins/RelCalc.py b/src/plugins/RelCalc.py index 6d2582065..c08acd3e5 100644 --- a/src/plugins/RelCalc.py +++ b/src/plugins/RelCalc.py @@ -36,6 +36,7 @@ from gettext import gettext as _ # #------------------------------------------------------------------------- import gtk.glade +from gtk import TextBuffer #------------------------------------------------------------------------- # @@ -85,23 +86,29 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow): _('You must select an active person for this ' 'tool to work properly.')) return - + + self.dbstate = dbstate self.relationship = relationship_class() + self.relationship.connect_db_signals(dbstate) base = os.path.dirname(__file__) glade_file = base + os.sep + "relcalc.glade" - self.glade = gtk.glade.XML(glade_file,"relcalc","gramps") + self.glade = gtk.glade.XML(glade_file, "relcalc", "gramps") name = name_displayer.display(self.person) self.title = _('Relationship calculator: %(person_name)s' ) % {'person_name' : name} window = self.glade.get_widget('relcalc') - self.set_window(window,self.glade.get_widget('title'), + self.titlelabel = self.glade.get_widget('title') + self.set_window(window, self.titlelabel, _('Relationship to %(person_name)s' ) % {'person_name' : name }, self.title) self.tree = self.glade.get_widget("peopleList") + self.text = self.glade.get_widget("text1") + self.textbuffer = TextBuffer() + self.text.set_buffer(self.textbuffer) self.model = PeopleModel(self.db,None) self.tree.set_model(self.model) @@ -111,6 +118,8 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow): column.set_min_width(225) column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) self.tree.append_column(column) + #keep reference of column so garbage collection works + self.columns = [column] index = 1 for pair in self.db.get_person_column_order(): @@ -123,16 +132,25 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow): column.set_min_width(60) column.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY) self.tree.append_column(column) + #keep reference of column so garbage collection works + self.columns.append(column) index += 1 - self.tree.get_selection().connect('changed',self.on_apply_clicked) - - self.glade.signal_autoconnect({ - "on_close_clicked" : self.close, - }) + self.sel = self.tree.get_selection() + self.changedkey = self.sel.connect('changed',self.on_apply_clicked) + self.closebtn = self.glade.get_widget("button5") + self.closebtn.connect('clicked', self.close) self.show() + def close(self, *obj): + ''' Close relcalc tool. Remove non-gtk connections so garbage + collection can do its magic. + ''' + self.relationship.disconnect_db_signals(self.dbstate) + self.sel.disconnect(self.changedkey) + ManagedWindow.ManagedWindow.close(self, *obj) + def build_menu_names(self,obj): return (_("Relationship Calculator tool"),None) @@ -142,12 +160,9 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow): return handle = model.get_value(node,len(PeopleModel.COLUMN_DEFS)-1) - other_person = self.db.get_person_from_handle(handle) - - text1 = self.glade.get_widget("text1").get_buffer() - + other_person = self.db.get_person_from_handle(handle) if other_person is None : - text1.set_text("") + self.textbuffer.set_text("") return #now determine the relation, and print it out @@ -205,7 +220,7 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow): textval = "" for val in text: textval += "%s %s\n" % (val[0], val[1]) - text1.set_text(textval) + self.textbuffer.set_text(textval) #------------------------------------------------------------------------ # diff --git a/src/plugins/relcalc.glade b/src/plugins/relcalc.glade index b7d04bc59..236b71d63 100644 --- a/src/plugins/relcalc.glade +++ b/src/plugins/relcalc.glade @@ -19,6 +19,7 @@ GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_NORTH_WEST True + False False @@ -43,7 +44,6 @@ GTK_RELIEF_NORMAL True 0 -