1# 2# Gramps - a GTK+/GNOME based genealogy program 3# 4# Copyright (C) 2003-2006 Donald N. Allingham 5# Copyright (C) 2008 Brian G. Matherly 6# Copyright (C) 2010 Jakim Friant 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 2 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program; if not, write to the Free Software 20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21# 22 23""" 24Show uncollected objects in a window. 25""" 26#------------------------------------------------------------------------ 27# 28# Python modules 29# 30#------------------------------------------------------------------------ 31import weakref 32import sys 33#------------------------------------------------------------------------ 34# 35# GNOME/GTK modules 36# 37#------------------------------------------------------------------------ 38from gi.repository import Gtk 39from gi.repository import Gdk 40import gc 41 42#------------------------------------------------------------------------ 43# 44# Gramps modules 45# 46#------------------------------------------------------------------------ 47from gramps.gen.plug import Gramplet 48from gramps.gui.dialog import InfoDialog 49from gramps.gui.utils import is_right_click, ProgressMeter 50from gramps.gen.const import GRAMPS_LOCALE as glocale 51_ = glocale.translation.gettext 52 53 54#------------------------------------------------------------------------- 55# 56# Leak 57# 58#------------------------------------------------------------------------- 59class Leak(Gramplet): 60 """ 61 Shows uncollected objects. 62 """ 63 def init(self): 64 self.gui.WIDGET = self.build_gui() 65 self.gui.get_container_widget().remove(self.gui.textview) 66 self.gui.get_container_widget().add(self.gui.WIDGET) 67 68 flags = gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_SAVEALL 69 if hasattr(gc, "DEBUG_OBJECTS"): 70 flags = flags | gc.DEBUG_OBJECTS 71 gc.set_debug(flags) 72 73 def build_gui(self): 74 """ 75 Build the GUI interface. 76 """ 77 self.top = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) 78 self.top.set_border_width(6) 79 80 self.label = Gtk.Label(halign=Gtk.Align.START) 81 self.top.pack_start(self.label, False, False, 6) 82 83 self.scroll = Gtk.ScrolledWindow() 84 # add a listview to the scrollable 85 self.list = Gtk.TreeView() 86 self.list.set_headers_visible(True) 87 self.list.connect('button-press-event', self._button_press) 88 self.scroll.add(self.list) 89 # make a model 90 self.model = Gtk.ListStore(int, str, str) 91 self.list.set_model(self.model) 92 93 # set the columns 94 self.renderer = Gtk.CellRendererText() 95 column = Gtk.TreeViewColumn(_('Number'), self.renderer, text=0) 96 column.set_resizable(True) 97 column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) 98 self.list.append_column(column) 99 column = Gtk.TreeViewColumn(_('Referrer'), self.renderer, text=1) 100 column.set_resizable(True) 101 column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) 102 self.list.append_column(column) 103 column = Gtk.TreeViewColumn(_('Uncollected object'), self.renderer, 104 text=2) 105 column.set_resizable(True) 106 column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) 107 self.list.append_column(column) 108 self.selection = self.list.get_selection() 109 self.top.pack_start(self.scroll, True, True, 6) 110 111 bbox = Gtk.ButtonBox() 112 apply_button = Gtk.Button(label=_("Refresh")) 113 apply_button.connect('clicked', self.apply_clicked) 114 bbox.pack_start(apply_button, False, False, 6) 115 self.top.pack_start(bbox, False, False, 6) 116 117 self.top.show_all() 118 119 return self.top 120 121 def main(self): 122 self.label.set_text(_('Press Refresh to see initial results')) 123 self.model.clear() 124 # self.display() # We should only run this on demand 125 126 def _button_press(self, obj, event): 127 if (event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS 128 and event.button == 1): 129 self.referenced_in() 130 return True 131 elif is_right_click(event): 132 self.refers_to() 133 return True 134 135 def referenced_in(self): 136 model, _iter = self.selection.get_selected() 137 if _iter is not None: 138 count = model.get_value(_iter, 0) 139 gc.collect(2) 140 referrers = gc.get_referrers(self.junk[count]) 141 text = "" 142 for referrer in referrers: 143 match = "" 144 try: 145 if referrer is not self.junk: 146 match = "**** " 147 for indx in range(len(self.junk)): 148 if referrer is self.junk[indx]: 149 match = str(indx) + ": " 150 break 151 match += str(referrer) + '\n' 152 except ReferenceError: 153 match += 'weakly-referenced object no longer exists %s'\ 154 % type(referrer) 155 except: 156 print(sys.exc_info()) 157 text += match 158 InfoDialog(_('Referrers of %d') % count, text, parent=self.parent) 159 160 def refers_to(self): 161 model, _iter = self.selection.get_selected() 162 if _iter is not None: 163 count = model.get_value(_iter, 0) 164 referents = gc.get_referents(self.junk[count]) 165 text = "" 166 for referent in referents: 167 match = "" 168 try: 169 match = "****: " 170 for indx in range(len(self.junk)): 171 if referent is self.junk[indx]: 172 match = str(indx) + ': ' 173 break 174 match += str(referent) + '\n' 175 except ReferenceError: 176 match += '%s weakly-referenced object no longer'\ 177 ' exists\n' % type(referent) 178 except: 179 print(sys.exc_info()) 180 text += match 181 InfoDialog(_('%d refers to') % count, text, parent=self.parent) 182 183 def display(self): 184 try: 185 from bsddb3.db import DBError 186 except: 187 class DBError(Exception): 188 """ 189 Dummy. 190 """ 191 self.parent = self.top.get_toplevel() 192 progress = ProgressMeter( 193 _('Updating display...'), '', parent=self.parent, can_cancel=True) 194 self.model.clear() 195 self.junk = [] 196 gc.collect(2) 197 self.junk = gc.garbage 198 self.label.set_text(_('Uncollected Objects: %s') % 199 str(len(self.junk))) 200 progress.set_pass(_('Updating display...'), len(self.junk)) 201 for count in range(0, len(self.junk)): 202 if progress.step(): 203 break 204 try: 205 refs = [] 206 referrers = gc.get_referrers(self.junk[count]) 207 for referrer in referrers: 208 try: 209 if referrer is not self.junk: 210 for indx in range(0, len(self.junk)): 211 if referrer is self.junk[indx]: 212 refs.append(str(indx) + ' ') 213 break 214 except: 215 print(sys.exc_info()) 216 if len(refs) > 3: 217 ref = ' '.join(refs[0:2]) + "..." 218 else: 219 ref = ' '.join(refs) 220 try: 221 self.model.append((count, ref, str(self.junk[count]))) 222 except DBError: 223 self.model.append((count, ref, 224 'db.DB instance at %s' % 225 id(self.junk[count]))) 226 except ReferenceError: 227 self.model.append(( 228 count, ref, 229 'weakly-referenced object no longer exists %s' 230 % type(self.junk[count]))) 231 except TypeError: 232 self.model.append(( 233 count, ref, 234 'Object cannot be displayed %s' 235 % type(self.junk[count]))) 236 except: 237 print(sys.exc_info()) 238 except ReferenceError: 239 InfoDialog(_('Reference Error'), "Refresh to correct", 240 parent=self.parent) 241 progress.close() 242 243 def apply_clicked(self, obj): 244 self.display() 245