[hamster-applet] fighting memory leaks. fixes all the can't-open-window-second-time bugs
- From: Toms Baugis <tbaugis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [hamster-applet] fighting memory leaks. fixes all the can't-open-window-second-time bugs
- Date: Sat, 7 Apr 2012 15:05:35 +0000 (UTC)
commit 41787b195546c3b673e0c38681ed9e07555a554b
Author: Toms BauÄis <toms baugis gmail com>
Date: Sat Apr 7 18:05:09 2012 +0300
fighting memory leaks. fixes all the can't-open-window-second-time bugs
src/hamster/configuration.py | 14 ++++++-
src/hamster/edit_activity.py | 18 +++++++--
src/hamster/overview.py | 32 ++++++++++-----
src/hamster/preferences.py | 56 +++++++++++++++++++++------
src/hamster/stats.py | 25 ++++++++----
src/hamster/widgets/activityentry.py | 13 ++++++-
src/hamster/widgets/dateinput.py | 5 ++
src/hamster/widgets/facttree.py | 7 +++
src/hamster/widgets/rangepick.py | 5 ++
src/hamster/widgets/reportchooserdialog.py | 2 +
src/hamster/widgets/tags.py | 11 +++++-
src/hamster/widgets/timeinput.py | 4 ++
12 files changed, 151 insertions(+), 41 deletions(-)
---
diff --git a/src/hamster/configuration.py b/src/hamster/configuration.py
index 5d61682..f301ef5 100644
--- a/src/hamster/configuration.py
+++ b/src/hamster/configuration.py
@@ -83,6 +83,15 @@ class OneWindow(object):
def __init__(self, get_dialog_class):
self.dialogs = {}
self.get_dialog_class = get_dialog_class
+ self.dialog_close_handlers = {}
+
+ def on_close_window(self, dialog):
+ for key, assoc_dialog in list(self.dialogs.iteritems()):
+ if dialog == assoc_dialog:
+ del self.dialogs[key]
+
+ handler = self.dialog_close_handlers.pop(dialog)
+ dialog.disconnect(handler)
def show(self, parent = None, **kwargs):
@@ -90,8 +99,7 @@ class OneWindow(object):
if params in self.dialogs:
window = self.dialogs[params].window
- if not window.get_visible():
- self.dialogs[params].show()
+ self.dialogs[params].show()
window.present()
else:
if parent:
@@ -99,6 +107,8 @@ class OneWindow(object):
if isinstance(parent, gtk.Widget):
dialog.window.set_transient_for(parent.get_toplevel())
+
+ self.dialog_close_handlers[dialog] = dialog.connect("on-close", self.on_close_window)
else:
dialog = self.get_dialog_class()(**kwargs)
diff --git a/src/hamster/edit_activity.py b/src/hamster/edit_activity.py
index 1d65dd4..fac5854 100644
--- a/src/hamster/edit_activity.py
+++ b/src/hamster/edit_activity.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with Project Hamster. If not, see <http://www.gnu.org/licenses/>.
-import gtk
+import gtk, gobject
import time
import datetime as dt
@@ -28,8 +28,13 @@ import widgets
from configuration import runtime, conf, load_ui_file
from lib import stuff
-class CustomFactController:
+class CustomFactController(gtk.Object):
+ __gsignals__ = {
+ "on-close": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+ }
+
def __init__(self, parent = None, fact_date = None, fact_id = None):
+ gtk.Object.__init__(self)
self._gui = load_ui_file("edit_activity.ui")
self.window = self.get_widget('custom_fact_window')
@@ -307,5 +312,10 @@ class CustomFactController:
self.close_window()
def close_window(self):
- self.window.destroy()
- return False
+ if not self.parent:
+ gtk.main_quit()
+ else:
+ self.window.destroy()
+ self.window = None
+ self._gui = None
+ self.emit("on-close")
diff --git a/src/hamster/overview.py b/src/hamster/overview.py
index 6eeb472..9c1556a 100644
--- a/src/hamster/overview.py
+++ b/src/hamster/overview.py
@@ -38,8 +38,14 @@ from overview_activities import OverviewBox
from overview_totals import TotalsBox
-class Overview(object):
+class Overview(gtk.Object):
+ __gsignals__ = {
+ "on-close": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+ }
+
def __init__(self, parent = None):
+ gtk.Object.__init__(self)
+
self.parent = parent# determine if app should shut down on close
self._gui = load_ui_file("overview.ui")
self.report_chooser = None
@@ -66,10 +72,12 @@ class Overview(object):
self.get_widget("by_day_box").add(self.timechart)
self._gui.connect_signals(self)
- runtime.storage.connect('activities-changed',self.after_activity_update)
- runtime.storage.connect('facts-changed',self.after_activity_update)
- conf.connect('conf-changed', self.on_conf_change)
+ self.external_listeners = [
+ (runtime.storage, runtime.storage.connect('activities-changed',self.after_activity_update)),
+ (runtime.storage, runtime.storage.connect('facts-changed',self.after_activity_update)),
+ (conf, conf.connect('conf-changed', self.on_conf_change))
+ ]
self.show()
@@ -192,9 +200,6 @@ class Overview(object):
return True
def after_activity_update(self, widget):
- if not self.window.get_visible():
- return
-
self.search()
@@ -416,11 +421,16 @@ class Overview(object):
if not self.parent:
gtk.main_quit()
else:
- self.window.hide()
- self.facts = None
- return False
+ for obj, handler in self.external_listeners:
+ obj.disconnect(handler)
+ self._gui = None
+ self.window.destroy()
+ self.window = None
+
+ self.emit("on-close")
+
+
def on_delete_window(self, window, event):
self.close_window()
return True
-
diff --git a/src/hamster/preferences.py b/src/hamster/preferences.py
index a080146..5dfa4d4 100755
--- a/src/hamster/preferences.py
+++ b/src/hamster/preferences.py
@@ -91,14 +91,19 @@ from lib import stuff, trophies
-class PreferencesEditor:
+class PreferencesEditor(gtk.Object):
TARGETS = [
('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_APP, 0),
]
+ __gsignals__ = {
+ "on-close": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+ }
+
def __init__(self, parent = None):
+ gtk.Object.__init__(self)
self.parent = parent
self._gui = load_ui_file("preferences.ui")
self.window = self.get_widget('preferences_window')
@@ -120,10 +125,14 @@ class PreferencesEditor:
self.get_widget("activities_label").set_mnemonic_widget(self.activity_tree)
self.activity_store = ActivityStore()
+ self.external_listeners = []
+
self.activityColumn = gtk.TreeViewColumn(_("Name"))
self.activityColumn.set_expand(True)
self.activityCell = gtk.CellRendererText()
- self.activityCell.connect('edited', self.activity_name_edited_cb, self.activity_store)
+ self.external_listeners.extend([
+ (self.activityCell, self.activityCell.connect('edited', self.activity_name_edited_cb, self.activity_store))
+ ])
self.activityColumn.pack_start(self.activityCell, True)
self.activityColumn.set_attributes(self.activityCell, text=1)
self.activityColumn.set_sort_column_id(1)
@@ -132,7 +141,10 @@ class PreferencesEditor:
self.activity_tree.set_model(self.activity_store)
self.selection = self.activity_tree.get_selection()
- self.selection.connect('changed', self.activity_changed, self.activity_store)
+
+ self.external_listeners.extend([
+ (self.selection, self.selection.connect('changed', self.activity_changed, self.activity_store))
+ ])
# create and fill category tree
@@ -143,7 +155,10 @@ class PreferencesEditor:
self.categoryColumn = gtk.TreeViewColumn(_("Category"))
self.categoryColumn.set_expand(True)
self.categoryCell = gtk.CellRendererText()
- self.categoryCell.connect('edited', self.category_edited_cb, self.category_store)
+
+ self.external_listeners.extend([
+ (self.categoryCell, self.categoryCell.connect('edited', self.category_edited_cb, self.category_store))
+ ])
self.categoryColumn.pack_start(self.categoryCell, True)
self.categoryColumn.set_attributes(self.categoryCell, text=1)
@@ -155,11 +170,12 @@ class PreferencesEditor:
self.category_tree.set_model(self.category_store)
selection = self.category_tree.get_selection()
- selection.connect('changed', self.category_changed_cb, self.category_store)
+ self.external_listeners.extend([
+ (selection, selection.connect('changed', self.category_changed_cb, self.category_store))
+ ])
self.day_start = widgets.TimeInput(dt.time(5,30))
self.get_widget("day_start_placeholder").add(self.day_start)
- self.day_start.connect("time-entered", self.on_day_start_changed)
self.load_config()
@@ -175,8 +191,7 @@ class PreferencesEditor:
self.activity_tree.connect("drag_data_get", self.drag_data_get_data)
- self.category_tree.connect("drag_data_received",
- self.on_category_drop)
+ self.category_tree.connect("drag_data_received", self.on_category_drop)
#select first category
selection = self.category_tree.get_selection()
@@ -198,7 +213,10 @@ class PreferencesEditor:
self.wActivityColumn.set_expand(True)
self.wActivityCell = gtk.CellRendererText()
self.wActivityCell.set_property('editable', True)
- self.wActivityCell.connect('edited', self.on_workspace_activity_edited)
+
+ self.external_listeners.extend([
+ (self.wActivityCell, self.wActivityCell.connect('edited', self.on_workspace_activity_edited))
+ ])
self.wNameColumn.pack_start(self.wNameCell, True)
self.wNameColumn.set_attributes(self.wNameCell)
@@ -219,14 +237,20 @@ class PreferencesEditor:
self.get_widget("notification_preference_frame").hide()
+ self.external_listeners.extend([
+ (self.day_start, self.day_start.connect("time-entered", self.on_day_start_changed))
+ ])
+
# disable workspace tracking if wnck is not there
if wnck:
self.screen = wnck.screen_get_default()
for workspace in self.screen.get_workspaces():
self.on_workspace_created(self.screen, workspace)
- self.screen.workspace_add_handler = self.screen.connect("workspace-created", self.on_workspace_created)
- self.screen.workspace_del_handler = self.screen.connect("workspace-destroyed", self.on_workspace_deleted)
+ self.external_listeners.extend([
+ (self.screen, self.screen.connect("workspace-created", self.on_workspace_created)),
+ (self.screen, self.screen.connect("workspace-destroyed", self.on_workspace_deleted))
+ ])
else:
self.get_widget("workspace_tab").hide()
@@ -679,8 +703,14 @@ class PreferencesEditor:
if not self.parent:
gtk.main_quit()
else:
- self.window.hide()
- return False
+ for obj, handler in self.external_listeners:
+ obj.disconnect(handler)
+ self.window.destroy()
+ self.window = None
+ self._gui = None
+ self.wNameColumn = None
+ self.categoryColumn = None
+ self.emit("on-close")
def on_workspace_tracking_toggled(self, checkbox):
workspace_tracking = []
diff --git a/src/hamster/stats.py b/src/hamster/stats.py
index 073749f..1d1c880 100644
--- a/src/hamster/stats.py
+++ b/src/hamster/stats.py
@@ -38,8 +38,13 @@ from configuration import runtime, conf, load_ui_file
from lib.i18n import C_
-class Stats(object):
+class Stats(gtk.Object):
+ __gsignals__ = {
+ "on-close": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+ }
+
def __init__(self, parent = None):
+ gtk.Object.__init__(self)
self._gui = load_ui_file("stats.ui")
self.report_chooser = None
self.window = self.get_widget("stats_window")
@@ -52,10 +57,7 @@ class Stats(object):
self.get_widget("explore_everything").add(self.timechart)
self.get_widget("explore_everything").show_all()
- runtime.storage.connect('activities-changed',self.after_fact_update)
- runtime.storage.connect('facts-changed',self.after_fact_update)
self.window.set_position(gtk.WIN_POS_CENTER)
-
self.chart_category_totals = charting.Chart(value_format = "%.1f",
max_bar_width = 20,
legend_width = 70,
@@ -104,6 +106,10 @@ class Stats(object):
self.get_widget("explore_summary").add(self.explore_summary)
self.get_widget("explore_summary").show_all()
+ self.external_listeners = [
+ (runtime.storage, runtime.storage.connect('activities-changed',self.after_fact_update)),
+ (runtime.storage, runtime.storage.connect('facts-changed',self.after_fact_update))
+ ]
self._gui.connect_signals(self)
self.show()
@@ -423,9 +429,6 @@ than 15 minutes, you seem to be a busy bee.") % ("<b>%d</b>" % short_percent)
def after_fact_update(self, event):
- if not self.window.get_visible():
- return
-
self.stat_facts = runtime.storage.get_facts(dt.date(1970, 1, 1), dt.date.today())
self.stats()
@@ -447,8 +450,12 @@ than 15 minutes, you seem to be a busy bee.") % ("<b>%d</b>" % short_percent)
if not self.parent:
gtk.main_quit()
else:
- self.window.hide()
- return False
+ for obj, handler in self.external_listeners:
+ obj.disconnect(handler)
+ self.window.destroy()
+ self.window = None
+ self._gui = None
+ self.emit("on-close")
if __name__ == "__main__":
diff --git a/src/hamster/widgets/activityentry.py b/src/hamster/widgets/activityentry.py
index 7e0389b..0e82c3e 100644
--- a/src/hamster/widgets/activityentry.py
+++ b/src/hamster/widgets/activityentry.py
@@ -95,11 +95,22 @@ class ActivityEntry(gtk.Entry):
self.connect("changed", self._on_text_changed)
self._parent_click_watcher = None # bit lame but works
- runtime.storage.connect('activities-changed',self.after_activity_update)
+ self.external_listeners = [
+ (runtime.storage, runtime.storage.connect('activities-changed',self.after_activity_update)),
+ ]
self.show()
self.populate_suggestions()
+ self.connect("destroy", self.on_destroy)
+
+ def on_destroy(self, window):
+ for obj, handler in self.external_listeners:
+ obj.disconnect(handler)
+
+ self.popup.destroy()
+ self.popup = None
+
def get_value(self):
activity_name = self.get_text().decode("utf-8").strip()
if not activity_name:
diff --git a/src/hamster/widgets/dateinput.py b/src/hamster/widgets/dateinput.py
index 37cede8..f1512bf 100644
--- a/src/hamster/widgets/dateinput.py
+++ b/src/hamster/widgets/dateinput.py
@@ -61,6 +61,11 @@ class DateInput(gtk.Entry):
self.connect("changed", self._on_text_changed)
self.show()
+ self.connect("destroy", self.on_destroy)
+
+ def on_destroy(self, window):
+ self.popup.destroy()
+ self.popup = None
def set_date(self, date):
"""sets date to specified, using default format"""
diff --git a/src/hamster/widgets/facttree.py b/src/hamster/widgets/facttree.py
index 009b74b..02e7894 100644
--- a/src/hamster/widgets/facttree.py
+++ b/src/hamster/widgets/facttree.py
@@ -144,6 +144,13 @@ class FactTree(gtk.TreeView):
self.prev_rows = []
self.new_rows = []
+ self.connect("destroy", self.on_destroy)
+
+ def on_destroy(self, widget):
+ for col in self.get_columns():
+ col.destroy()
+ self.remove_column(col)
+
def fix_row_heights(self):
alloc = self.get_allocation()
diff --git a/src/hamster/widgets/rangepick.py b/src/hamster/widgets/rangepick.py
index a9abb74..22ce59f 100644
--- a/src/hamster/widgets/rangepick.py
+++ b/src/hamster/widgets/rangepick.py
@@ -54,7 +54,12 @@ class RangePick(gtk.ToggleButton):
self.connect("toggled", self.on_toggle)
self._gui.connect_signals(self)
+ self.connect("destroy", self.on_destroy)
+ def on_destroy(self, window):
+ self.popup.destroy()
+ self.popup = None
+ self._gui = None
def on_toggle(self, button):
if self.get_active():
diff --git a/src/hamster/widgets/reportchooserdialog.py b/src/hamster/widgets/reportchooserdialog.py
index 529edab..2e969df 100644
--- a/src/hamster/widgets/reportchooserdialog.py
+++ b/src/hamster/widgets/reportchooserdialog.py
@@ -109,6 +109,7 @@ class ReportChooserDialog(gtk.Dialog):
if response != gtk.RESPONSE_OK:
self.emit("report-chooser-closed")
self.dialog.destroy()
+ self.dialog = None
else:
self.on_save_button_clicked()
@@ -137,3 +138,4 @@ class ReportChooserDialog(gtk.Dialog):
# format, path, start_date, end_date
self.emit("report-chosen", format, path)
self.dialog.destroy()
+ self.dialog = None
diff --git a/src/hamster/widgets/tags.py b/src/hamster/widgets/tags.py
index cbb5938..224b005 100644
--- a/src/hamster/widgets/tags.py
+++ b/src/hamster/widgets/tags.py
@@ -58,9 +58,18 @@ class TagsEntry(gtk.Entry):
self._parent_click_watcher = None # bit lame but works
- runtime.storage.connect('tags-changed', self.refresh_tags)
+ self.external_listeners = [
+ (runtime.storage, runtime.storage.connect('tags-changed', self.refresh_tags))
+ ]
self.show()
self.populate_suggestions()
+ self.connect("destroy", self.on_destroy)
+
+ def on_destroy(self, window):
+ for obj, handler in self.external_listeners:
+ obj.disconnect(handler)
+ self.popup.destroy()
+ self.popup = None
def refresh_tags(self, event):
diff --git a/src/hamster/widgets/timeinput.py b/src/hamster/widgets/timeinput.py
index c931205..c1434e9 100644
--- a/src/hamster/widgets/timeinput.py
+++ b/src/hamster/widgets/timeinput.py
@@ -65,7 +65,11 @@ class TimeInput(gtk.Entry):
self.connect("changed", self._on_text_changed)
self.show()
+ self.connect("destroy", self.on_destroy)
+ def on_destroy(self, window):
+ self.popup.destroy()
+ self.popup = None
def set_time(self, time):
time = time or dt.time()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]