diff --git a/mvc/glade/foo.glade b/mvc/glade/foo.glade
new file mode 100644
index 0000000..89979e6
--- /dev/null
+++ b/mvc/glade/foo.glade
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+ True
+ window1
+ GTK_WINDOW_TOPLEVEL
+ GTK_WIN_POS_NONE
+ False
+ True
+ False
+ True
+ False
+ False
+ GDK_WINDOW_TYPE_HINT_NORMAL
+ GDK_GRAVITY_NORTH_WEST
+ True
+ False
+
+
+
+
+ True
+ 0
+ 0.5
+ GTK_SHADOW_NONE
+
+
+
+ True
+ 0.5
+ 0.5
+ 1
+ 1
+ 0
+ 0
+ 12
+ 0
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ label2
+ False
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ True
+ button1
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+ 0
+ False
+ False
+
+
+
+
+
+
+
+
+
+ True
+ <b>frame1</b>
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ label_item
+
+
+
+
+
+
+
diff --git a/mvc/glade/main.glade b/mvc/glade/main.glade
new file mode 100644
index 0000000..87e1358
--- /dev/null
+++ b/mvc/glade/main.glade
@@ -0,0 +1,588 @@
+
+
+
+
+
+
+ mainW
+ GTK_WINDOW_TOPLEVEL
+ GTK_WIN_POS_NONE
+ False
+ True
+ False
+ True
+ False
+ False
+ GDK_WINDOW_TYPE_HINT_NORMAL
+ GDK_GRAVITY_NORTH_WEST
+ True
+ False
+
+
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ GTK_PACK_DIRECTION_LTR
+ GTK_PACK_DIRECTION_LTR
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ GTK_ORIENTATION_HORIZONTAL
+ GTK_TOOLBAR_BOTH
+ True
+ True
+
+
+
+ True
+ Create new catalog
+ gtk-new
+ True
+ True
+ False
+
+
+
+ False
+ True
+
+
+
+
+
+ True
+ Open catalog file
+ gtk-open
+ True
+ True
+ False
+
+
+
+ False
+ True
+
+
+
+
+
+ True
+ Save catalog
+ gtk-save
+ True
+ True
+ False
+
+
+
+ False
+ True
+
+
+
+
+
+ True
+ True
+ True
+ False
+
+
+
+ True
+
+
+
+
+ False
+ False
+
+
+
+
+
+ True
+ Add CD/DVD to catalog
+ Add CD
+ True
+ gtk-cdrom
+ True
+ True
+ False
+
+
+
+ False
+ True
+
+
+
+
+
+ True
+ Find file
+ gtk-find
+ True
+ True
+ False
+
+
+
+ False
+ True
+
+
+
+
+
+ True
+ True
+ True
+ False
+
+
+
+ True
+
+
+
+
+ False
+ False
+
+
+
+
+
+ True
+ Quit pyGTKtalog
+ gtk-quit
+ True
+ True
+ False
+
+
+
+ False
+ True
+
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ True
+ True
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ False
+ True
+ False
+ True
+ False
+ False
+ False
+
+
+
+
+
+
+ True
+ False
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ True
+ True
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ True
+ True
+ False
+ True
+ False
+ False
+ False
+
+
+
+
+
+
+ True
+ False
+
+
+
+
+
+ True
+ True
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ False
+ False
+ False
+ GTK_JUSTIFY_LEFT
+ GTK_WRAP_NONE
+ False
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ True
+ True
+
+
+
+
+ False
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ False
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ GTK_PROGRESS_LEFT_TO_RIGHT
+ 0
+ 0.10000000149
+ PANGO_ELLIPSIZE_NONE
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ False
+ True
+
+
+
+
+
+
+
diff --git a/mvc/gtkmvc/__init__.py b/mvc/gtkmvc/__init__.py
new file mode 100644
index 0000000..3e4e252
--- /dev/null
+++ b/mvc/gtkmvc/__init__.py
@@ -0,0 +1,48 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+
+__all__ = ["model", "view", "controller", "observable", "observer"]
+
+__version = (1,0,0)
+
+from model import Model, TreeStoreModel, ListStoreModel, TextBufferModel
+from model_mt import ModelMT
+from controller import Controller
+from view import View
+from observer import Observer
+import observable
+
+
+def get_version(): return __version
+
+def require(ver):
+ if isinstance(ver, str): ver = ver.split(".")
+ ver = tuple(map(int, ver))
+
+ if get_version() < ver:
+ raise AssertionError("gtkmvc required version '%s', found '%s'"\
+ % (ver, get_version()))
+ pass
+ return
+
+
diff --git a/mvc/gtkmvc/controller.py b/mvc/gtkmvc/controller.py
new file mode 100644
index 0000000..68b481f
--- /dev/null
+++ b/mvc/gtkmvc/controller.py
@@ -0,0 +1,46 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+
+from gtkmvc.observer import Observer
+
+class Controller (Observer):
+ """We put all of our gtk signal handlers into a class. This lets us bind
+ all of them at once, because their names are in the class dict.
+ This class automatically register its instances as observers into the
+ corresponding model.
+ Also, when a view is created, the view calls method register_view,
+ which can be oveloaded in order to connect signals and perform other
+ specific operation"""
+
+ def __init__(self, model):
+ Observer.__init__(self, model)
+
+ self.view = None
+ return
+
+ def register_view(self, view):
+ assert(self.view is None)
+ self.view = view
+ return
+
+ pass # end of class Controller
diff --git a/mvc/gtkmvc/model.py b/mvc/gtkmvc/model.py
new file mode 100644
index 0000000..54434bd
--- /dev/null
+++ b/mvc/gtkmvc/model.py
@@ -0,0 +1,316 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+import support.metaclasses
+from support.wrappers import ObsWrapperBase
+from observable import Signal
+
+
+class Model (object):
+ """This class is the application model base class.
+ It handles a set of observable properties which you are interested
+ in showing by one ore more view - via one or more observers of course.
+ The mechanism is the following:
+ 1. You are interested in showing a set of model property, that you can
+ declare in the __properties__ member map.
+ 2. You define one or more observers that observe one or more properties
+ you registered. When someone changes a property value the model notifies
+ the changing to each listening controller. The property-observer[s]
+ association is given by the implicit rule in observers method names: if
+ you want the model notified the changing event of the property 'p'
+ you must define the method called 'property_p_change_notification' in
+ each listening observer class.
+ Notice that tipically 'controllers' implement the observer pattern.
+ The notification method gets the
+ emitting model, the old value for the property and the new one.
+ Properties functionalities are automatically provided by the
+ ObservablePropertyMeta meta-class."""
+
+ __metaclass__ = support.metaclasses.ObservablePropertyMeta
+ __properties__ = {} # override this
+
+ def __init__(self):
+ object.__init__(self)
+ self.__observers = []
+ # keys are properties names, values are methods inside the observer:
+ self.__value_notifications = {}
+ self.__instance_notif_before = {}
+ self.__instance_notif_after = {}
+ self.__signal_notif = {}
+
+ for key in (self.__properties__.keys() + self.__derived_properties__.keys()):
+ self.register_property(key)
+ pass
+
+ return
+
+ def register_property(self, name):
+ """Registers an existing property to be monitored, and sets up
+ notifiers for notifications"""
+ if not self.__value_notifications.has_key(name):
+ self.__value_notifications[name] = []
+ pass
+
+ # registers observable wrappers
+ prop = getattr(self, "_prop_%s" % name)
+
+ if isinstance(prop, ObsWrapperBase):
+ prop.__set_model__(self, name)
+
+ if isinstance(prop, Signal):
+ if not self.__signal_notif.has_key(name):
+ self.__signal_notif[name] = []
+ pass
+ pass
+ else:
+ if not self.__instance_notif_before.has_key(name):
+ self.__instance_notif_before[name] = []
+ pass
+ if not self.__instance_notif_after.has_key(name):
+ self.__instance_notif_after[name] = []
+ pass
+ pass
+ pass
+
+ return
+
+
+ def register_observer(self, observer):
+ if observer in self.__observers: return # not already registered
+
+ self.__observers.append(observer)
+ for key in (self.__properties__.keys() + self.__derived_properties__.keys()):
+ self.__add_observer_notification(observer, key)
+ pass
+
+ return
+
+
+ def unregister_observer(self, observer):
+ if observer not in self.__observers: return
+
+ for key in (self.__properties__.keys() + self.__derived_properties__.keys()):
+ self.__remove_observer_notification(observer, key)
+ pass
+
+ self.__observers.remove(observer)
+ return
+
+
+ def _reset_property_notification(self, prop_name):
+ """Called when it has done an assignment that changes the type
+ of a property, so it must be unregistered and registered again"""
+
+ self.register_property(prop_name)
+
+ for observer in self.__observers:
+ self.__remove_observer_notification(observer, prop_name)
+ self.__add_observer_notification(observer, prop_name)
+ pass
+ return
+
+
+ def __add_observer_notification(self, observer, prop_name):
+ """Searches in the observer for any possible listener, and
+ stores the notification methods to be called later"""
+
+ method_name = "property_%s_value_change" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method not in self.__value_notifications[prop_name]:
+ list.append(self.__value_notifications[prop_name], method)
+ pass
+ pass
+
+ # is it a signal?
+ orig_prop = getattr(self, "_prop_%s" % prop_name)
+ if isinstance(orig_prop, Signal):
+ method_name = "property_%s_signal_emit" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method not in self.__signal_notif[prop_name]:
+ list.append(self.__signal_notif[prop_name], method)
+ pass
+ pass
+ pass
+
+ # is it an instance change notification type?
+ elif isinstance(orig_prop, ObsWrapperBase):
+ method_name = "property_%s_before_change" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method not in self.__instance_notif_before[prop_name]:
+ list.append(self.__instance_notif_before[prop_name], method)
+ pass
+ pass
+
+ method_name = "property_%s_after_change" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method not in self.__instance_notif_after[prop_name]:
+ list.append(self.__instance_notif_after[prop_name], method)
+ pass
+ pass
+ pass
+
+ return
+
+
+ def __remove_observer_notification(self, observer, prop_name):
+ if self.__value_notifications.has_key(prop_name):
+ method_name = "property_%s_value_change" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method in self.__value_notifications[prop_name]:
+ self.__value_notifications[prop_name].remove(method)
+ pass
+ pass
+ pass
+
+
+ orig_prop = getattr(self, "_prop_%s" % prop_name)
+ # is it a signal?
+ if isinstance(orig_prop, Signal):
+ method_name = "property_%s_signal_emit" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method in self.__signal_notif[prop_name]:
+ self.__signal_notif[prop_name].remove(method)
+ pass
+ pass
+ pass
+
+ # is it an instance change notification type?
+ elif isinstance(orig_prop, ObsWrapperBase):
+ if self.__instance_notif_before.has_key(prop_name):
+ method_name = "property_%s_before_change" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method in self.__instance_notif_before[prop_name]:
+ self.__instance_notif_before[prop_name].remove(method)
+ pass
+ pass
+ pass
+
+ if self.__instance_notif_after.has_key(prop_name):
+ method_name = "property_%s_after_change" % prop_name
+ if hasattr(observer, method_name):
+ method = getattr(observer, method_name)
+ if method in self.__instance_notif_after[prop_name]:
+ self.__instance_notif_after[prop_name].remove(method)
+ pass
+ pass
+ pass
+ pass
+
+ return
+
+
+ def __notify_observer__(self, observer, method, *args, **kwargs):
+ """This can be overridden by derived class in order to call
+ the method in a different manner (for example, in
+ multithreading, or a rpc, etc.) This implementation simply
+ calls the given method with the given arguments"""
+ return method(*args, **kwargs)
+
+
+ # ---------- Notifiers:
+
+ def notify_property_value_change(self, prop_name, old, new):
+ assert(self.__value_notifications.has_key(prop_name))
+ for method in self.__value_notifications[prop_name] :
+ self.__notify_observer__(method.im_self, method,
+ self, old, new) # notifies the change
+ pass
+ return
+
+ def notify_method_before_change(self, prop_name, instance, meth_name,
+ args, kwargs):
+ assert(self.__instance_notif_before.has_key(prop_name))
+ for method in self.__instance_notif_before[prop_name] :
+ self.__notify_observer__(method.im_self, method, self, instance,
+ meth_name, args, kwargs) # notifies the change
+ pass
+ return
+
+ def notify_method_after_change(self, prop_name, instance, meth_name,
+ res, args, kwargs):
+ assert(self.__instance_notif_after.has_key(prop_name))
+ for method in self.__instance_notif_after[prop_name] :
+ self.__notify_observer__(method.im_self, method, self, instance,
+ meth_name, res, args, kwargs) # notifies the change
+ pass
+ return
+
+ def notify_signal_emit(self, prop_name, args, kwargs):
+ assert(self.__signal_notif.has_key(prop_name))
+ for method in self.__signal_notif[prop_name] :
+ self.__notify_observer__(method.im_self, method, self,
+ args, kwargs) # notifies the signal emit
+ pass
+ return
+
+
+ pass # end of class Model
+# ----------------------------------------------------------------------
+
+
+
+import gtk
+# ----------------------------------------------------------------------
+class TreeStoreModel (Model, gtk.TreeStore):
+ """Use this class as base class for your model derived by
+ gtk.TreeStore"""
+ __metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
+
+ def __init__(self, column_type, *args):
+ Model.__init__(self)
+ gtk.TreeStore.__init__(self, column_type, *args)
+ return
+ pass
+
+
+# ----------------------------------------------------------------------
+class ListStoreModel (Model, gtk.ListStore):
+ """Use this class as base class for your model derived by
+ gtk.ListStore"""
+ __metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
+
+ def __init__(self, column_type, *args):
+ Model.__init__(self)
+ gtk.ListStore.__init__(self, column_type, *args)
+ return
+ pass
+
+
+# ----------------------------------------------------------------------
+class TextBufferModel (Model, gtk.TextBuffer):
+ """Use this class as base class for your model derived by
+ gtk.TextBuffer"""
+ __metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
+
+ def __init__(self, table=None):
+ Model.__init__(self)
+ gtk.TextBuffer.__init__(self, table)
+ return
+ pass
+
diff --git a/mvc/gtkmvc/model_mt.py b/mvc/gtkmvc/model_mt.py
new file mode 100644
index 0000000..07ac6ca
--- /dev/null
+++ b/mvc/gtkmvc/model_mt.py
@@ -0,0 +1,119 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+
+from gtkmvc.model import Model
+import support.metaclasses
+
+try: import threading as _threading
+except ImportError: import dummy_threading as _threading
+
+import gobject
+if hasattr(gobject, "threads_init"): gobject.threads_init()
+else: import gtk; gtk.threads_init()
+
+
+class ModelMT (Model):
+ """A base class for models whose observable properties can be
+ changed by threads different than gtk main thread. Notification is
+ performed by exploiting the gtk idle loop only if needed,
+ otherwise the standard notification system (direct method call) is
+ used."""
+
+ def __init__(self):
+ Model.__init__(self)
+ self.__observer_threads = {}
+ return
+
+ def register_observer(self, observer):
+ Model.register_observer(self, observer)
+ self.__observer_threads[observer] = _threading.currentThread()
+ return
+
+ def unregister_observer(self, observer):
+ Model.unregister_observer(self, observer)
+ del self.__observer_threads[observer]
+ return
+
+ # ---------- Notifiers:
+
+ def __notify_observer__(self, observer, method, *args, **kwargs):
+ """This makes a call either through the gtk.idle list or a
+ direct method call depending whether the caller's thread is
+ different from the observer's thread"""
+
+ assert self.__observer_threads.has_key(observer)
+ if _threading.currentThread() == self.__observer_threads[observer]:
+ # standard call
+ return Model.__notify_observer__(self, observer, method,
+ args, kwargs)
+
+ # multi-threading call
+ gobject.idle_add(self.__idle_callback, observer, method, args, kwargs)
+ return
+
+ def __idle_callback(self, observer, method, args, kwargs):
+ method(*args, **kwargs)
+ return False
+
+
+ pass # end of class
+
+
+import gtk
+# ----------------------------------------------------------------------
+class TreeStoreModelMT (ModelMT, gtk.TreeStore):
+ """Use this class as base class for your model derived by
+ gtk.TreeStore"""
+ __metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
+
+ def __init__(self, column_type, *args):
+ ModelMT.__init__(self)
+ gtk.TreeStore.__init__(self, column_type, *args)
+ return
+ pass
+
+
+# ----------------------------------------------------------------------
+class ListStoreModelMT (ModelMT, gtk.ListStore):
+ """Use this class as base class for your model derived by
+ gtk.ListStore"""
+ __metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
+
+ def __init__(self, column_type, *args):
+ ModelMT.__init__(self)
+ gtk.ListStore.__init__(self, column_type, *args)
+ return
+ pass
+
+
+# ----------------------------------------------------------------------
+class TextBufferModelMT (ModelMT, gtk.TextBuffer):
+ """Use this class as base class for your model derived by
+ gtk.TextBuffer"""
+ __metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
+
+ def __init__(self, table=None):
+ ModelMT.__init__(self)
+ gtk.TextBuffer.__init__(self, table)
+ return
+ pass
diff --git a/mvc/gtkmvc/observable.py b/mvc/gtkmvc/observable.py
new file mode 100644
index 0000000..f6ac190
--- /dev/null
+++ b/mvc/gtkmvc/observable.py
@@ -0,0 +1,68 @@
+# -------------------------------------------------------------------------
+# Author: Roberto Cavada
+#
+# Copyright (C) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author .
+# -------------------------------------------------------------------------
+
+
+from support import decorators
+from support.wrappers import ObsWrapperBase
+
+# ----------------------------------------------------------------------
+class Observable (ObsWrapperBase):
+ def __init__(self):
+ ObsWrapperBase.__init__(self)
+ return
+ pass # end of class
+
+
+@decorators.good_decorator
+def observed(func):
+ """Use this decorator to make your class methods observable.
+
+ Your observer will receive at most two notifications:
+ - property__before_change
+ - property__after_change
+
+ """
+
+ def wrapper(*args, **kwargs):
+ self = args[0]
+ assert(isinstance(self, Observable))
+
+ self._notify_method_before(self, func.__name__, args, kwargs)
+ res = func(*args, **kwargs)
+ self._notify_method_after(self, func.__name__, res, args, kwargs)
+ return res
+ return wrapper
+
+
+# ----------------------------------------------------------------------
+class Signal (Observable):
+ """Base class for signals properties"""
+ def __init__(self):
+ Observable.__init__(self)
+ return
+
+ def emit(self, *args, **kwargs):
+ return self.__get_model__().notify_signal_emit(
+ self.__get_prop_name__(), args, kwargs)
+ pass # end of class
+
diff --git a/mvc/gtkmvc/observer.py b/mvc/gtkmvc/observer.py
new file mode 100644
index 0000000..ac35f16
--- /dev/null
+++ b/mvc/gtkmvc/observer.py
@@ -0,0 +1,56 @@
+# -------------------------------------------------------------------------
+# Author: Roberto Cavada
+#
+# Copyright (C) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+# -------------------------------------------------------------------------
+
+
+class Observer (object):
+ """Use this class as base class of all observers"""
+
+ def __init__(self, model=None):
+ self.model = None
+ self.register_model(model)
+ return
+
+ def register_model(self, model):
+ self.unregister_model()
+ self.model = model
+ if self.model: self.model.register_observer(self)
+ return
+
+ def unregister_model(self):
+ if self.model:
+ self.model.unregister_observer(self)
+ self.model = None
+ pass
+ return
+
+ def __del__(self):
+ self.unregister_model()
+ return
+
+ def get_model(self): return self.model
+
+ pass # end of class
+
+
+
diff --git a/mvc/gtkmvc/support/__init__.py b/mvc/gtkmvc/support/__init__.py
new file mode 100644
index 0000000..86ec566
--- /dev/null
+++ b/mvc/gtkmvc/support/__init__.py
@@ -0,0 +1,24 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+
+__all__ = ["metaclass_base", "metaclasses", "wrappers", "decorators"]
diff --git a/mvc/gtkmvc/support/decorators.py b/mvc/gtkmvc/support/decorators.py
new file mode 100644
index 0000000..fef7184
--- /dev/null
+++ b/mvc/gtkmvc/support/decorators.py
@@ -0,0 +1,43 @@
+# -------------------------------------------------------------------------
+# Author: Roberto Cavada
+#
+# Copyright (C) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+# -------------------------------------------------------------------------
+
+
+
+# This file contains decorators to be used (privately) by other parts
+# of the framework
+
+def good_decorator(decorator):
+ """This decorator makes decorators behave well wrt to decorated
+ functions names, doc, etc."""
+ def new_decorator(f):
+ g = decorator(f)
+ g.__name__ = f.__name__
+ g.__doc__ = f.__doc__
+ g.__dict__.update(f.__dict__)
+ return g
+
+ new_decorator.__name__ = decorator.__name__
+ new_decorator.__doc__ = decorator.__doc__
+ new_decorator.__dict__.update(decorator.__dict__)
+ return new_decorator
diff --git a/mvc/gtkmvc/support/metaclass_base.py b/mvc/gtkmvc/support/metaclass_base.py
new file mode 100644
index 0000000..c9a2435
--- /dev/null
+++ b/mvc/gtkmvc/support/metaclass_base.py
@@ -0,0 +1,244 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+
+import new
+import re
+import types
+
+import gtkmvc.support.wrappers as wrappers
+
+
+# ----------------------------------------------------------------------
+
+VERBOSE_LEVEL = 5
+
+class PropertyMeta (type):
+ """This is a meta-class that provides auto-property support.
+ The idea is to allow programmers to define some properties which
+ will be automatically connected to auto-generated code which handles
+ access to those properties.
+ How can you use this meta-class?
+ First, '__metaclass__ = PropertyMeta' must be class member of the class
+ you want to make the automatic properties handling.
+ Second, '__properties__' must be a map containing the properties names
+ as keys, values will be initial values for properties.
+ That's all: after the instantiation, your class will contain all properties
+ you named inside '__properties__'. Each of them will be also associated
+ to a couple of automatically-generated functions which get and set the
+ property value inside a generated member variable.
+ About names: suppose the property is called 'x'. The generated variable
+ (which keeps the real value of the property x) is called _prop_x.
+ The getter is called get_prop_x(self), and the setter is called
+ 'set_prop_x(self, value)'.
+
+ Customization:
+ The base implementation of getter is to return the value stored in the
+ variable associate to the property. The setter simply sets its value.
+ Programmers can override basic behaviour for getters or setters simply by
+ defining their getters and setters (see at the names convention above).
+ The customized function can lie everywhere in the user classes hierarchy.
+ Every overrided function will not be generated by the metaclass.
+
+ To supply your own methods is good for few methods, but can result in a
+ very unconfortable way for many methods. In this case you can extend
+ the meta-class, and override methods get_[gs]etter_source with your
+ implementation (this can be probably made better).
+ An example is provided in meta-class PropertyMetaVerbose below.
+ """
+
+ def __init__(cls, name, bases, dict):
+ """class constructor"""
+ properties = {}
+ type.__init__(cls, name, bases, dict)
+
+ props = getattr(cls, '__properties__', {})
+ setattr(cls, '__derived_properties__', {})
+ der_props = getattr(cls, '__derived_properties__')
+
+ # Calculates derived properties:
+ for base in bases:
+ maps = ( getattr(base, '__properties__', {}),
+ getattr(base, '__derived_properties__', {}) )
+ for map in maps:
+ for p in map.keys():
+ if not props.has_key(p) and not der_props.has_key(p):
+ der_props[p] = map[p]
+ pass
+ pass
+ pass
+ pass
+
+ # Generates code for all properties (but not for derived props):
+ props = getattr(cls, '__properties__', {})
+ for prop in props.keys():
+ type(cls).__create_prop_accessors__(cls, prop, props[prop])
+ pass
+
+ return
+
+
+ def __msg__(cls, msg, level):
+ """if level is less or equal to VERBOSE_LEVEL, ths message will
+ be printed"""
+ if level <= VERBOSE_LEVEL: print msg
+ return
+
+ def __create_prop_accessors__(cls, prop_name, default_val):
+ """Private method that creates getter and setter, and the
+ corresponding property"""
+ getter_name = "get_prop_%s" % prop_name
+ setter_name = "set_prop_%s" % prop_name
+
+ members_names = cls.__dict__.keys()
+
+ # checks if accessors are already defined:
+ if getter_name not in members_names:
+ src = type(cls).get_getter_source(cls, getter_name, prop_name)
+ code = type(cls).get_func_code_from_func_src(cls, src)
+ type(cls).add_method_from_func_code(cls, getter_name, code)
+ else:
+ cls.__msg__("Warning: Custom member '%s' overloads generated accessor of property '%s'" \
+ % (getter_name, prop_name), 2)
+ pass
+
+ if setter_name not in members_names:
+ src = type(cls).get_setter_source(cls, setter_name, prop_name)
+ code = type(cls).get_func_code_from_func_src(cls, src)
+ type(cls).add_method_from_func_code(cls, setter_name, code)
+ else:
+ cls.__msg__("Warning: Custom member '%s' overloads generated accessor of property '%s'" \
+ % (setter_name, prop_name), 2)
+ pass
+
+ prop = property(getattr(cls, getter_name), getattr(cls, setter_name))
+
+ if prop_name in members_names:
+ cls.__msg__("Warning: automatic property builder is overriding property %s in class %s" \
+ % (prop_name, cls.__name__), 2)
+ pass
+ setattr(cls, prop_name, prop)
+
+ varname = "_prop_%s" % prop_name
+ if not varname in members_names: cls.__create_property(varname, default_val)
+ else: cls.__msg__("Warning: automatic property builder found a possible clashing for variable %s inside class %s" \
+ % (varname, cls.__name__), 2)
+ return
+
+ def __create_property(cls, name, default_val):
+ setattr(cls, name, cls.create_value(name, default_val))
+ return
+
+ def create_value(cls, prop_name, val, model=None):
+ """This is used to create a value to be assigned to a
+ property. Depending on the type of the value, different values
+ are created and returned. For example, for a list, a
+ ListWrapper is created to wrap it, and returned for the
+ assignment. model is different from model when the value is
+ changed (a model exists). Otherwise, during property creation
+ model is None"""
+
+ if isinstance(val, tuple):
+ # this might be a class instance to be wrapped
+ if len(val) == 3 and \
+ isinstance(val[1], val[0]) and \
+ (isinstance(val[2], tuple) or isinstance(val[2], list)):
+ res = wrappers.ObsUserClassWrapper(val[1], val[2])
+ if model: res.__set_model__(model, prop_name)
+ return res
+ pass
+
+ elif isinstance(val, list):
+ res = wrappers.ObsListWrapper(val)
+ if model: res.__set_model__(model, prop_name)
+ return res
+
+ elif isinstance(val, dict):
+ res = wrappers.ObsMapWrapper(val)
+ if model: res.__set_model__(model, prop_name)
+ return res
+
+ return val
+
+ # Services:
+ def add_method_from_func_code(cls, meth_name, code):
+ """Use this to add a code that is a new method for the class"""
+
+ func = new.function(code, globals(), meth_name)
+ meth = new.instancemethod(func, cls, cls.__name__)
+ setattr(cls, meth_name, func)
+ return
+
+ def get_func_code_from_func_src(cls, source):
+ """Public service that provided code object from function source"""
+ m = re.compile("def\s+(\w+)\s*\(.*\):").match(source)
+ if m is None: raise BadFuncSource(source)
+
+ func_name = m.group(1)
+ exec source
+ code = eval("%s.func_code" % func_name)
+ return code
+
+
+ # Override these:
+ def get_getter_source(cls, getter_name, prop_name):
+ """This must be overrided if you need a different implementation.
+ Simply the generated implementation returns the variable name
+ _prop_name"""
+
+ return "def %s(self): return self._prop_%s" % (getter_name, prop_name)
+
+ def get_setter_source(cls, setter_name, prop_name):
+ """This must be overrided if you need a different implementation.
+ Simply the generated implementation sets the variable _prop_name"""
+ return "def %s(self, val): self._prop_%s = val" \
+ % (setter_name, prop_name)
+
+ pass # end of class
+# ----------------------------------------------------------------------
+
+
+# What follows underneath is a set of examples of usage
+
+## class PropertyMetaVerbose (PropertyMeta):
+## """An example of customization"""
+## def get_getter_source(cls, getter_name, prop_name):
+## return "def %s(self): print 'Calling %s!'; return self._prop_%s" \
+## % (getter_name, getter_name, prop_name)
+
+## def get_setter_source(cls, setter_name, prop_name):
+## return "def %s(self, val): print 'Calling %s!'; self._prop_%s = val;" \
+## % (setter_name, setter_name, prop_name)
+## pass #end of class
+# ----------------------------------------------------------------------
+
+## class User:
+## """An example of usage"""
+## __metaclass__ = PropertyMetaVerbose
+## __properties__ = {'x':10, 'y':20}
+
+## def __init__(self):
+## print self.x # x is 10
+## self.x = self.y + 10 # x is now 30
+## return
+## pass
+# ----------------------------------------------------------------------
diff --git a/mvc/gtkmvc/support/metaclasses.py b/mvc/gtkmvc/support/metaclasses.py
new file mode 100644
index 0000000..e594dac
--- /dev/null
+++ b/mvc/gtkmvc/support/metaclasses.py
@@ -0,0 +1,54 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+from metaclass_base import PropertyMeta
+import types
+
+
+class ObservablePropertyMeta (PropertyMeta):
+ """Classes instantiated by this meta-class must provide a method named
+ notify_property_change(self, prop_name, old, new)"""
+ def __init__(cls, name, bases, dict):
+ PropertyMeta.__init__(cls, name, bases, dict)
+ return
+
+ def get_setter_source(cls, setter_name, prop_name):
+ return """def %(setter)s(self, val):
+ old = self._prop_%(prop)s
+ self._prop_%(prop)s = type(self).create_value('%(prop)s', val, self)
+ if type(old) != type(self._prop_%(prop)s): self._reset_property_notification('%(prop)s')
+ self.notify_property_value_change('%(prop)s', old, val)
+ return
+""" % {'setter':setter_name, 'prop':prop_name}
+
+ pass #end of class
+
+
+
+try:
+ from gobject import GObjectMeta
+ class ObservablePropertyGObjectMeta (ObservablePropertyMeta, GObjectMeta): pass
+except:
+ class ObservablePropertyGObjectMeta (ObservablePropertyMeta): pass
+ pass
+
+
diff --git a/mvc/gtkmvc/support/wrappers.py b/mvc/gtkmvc/support/wrappers.py
new file mode 100644
index 0000000..0f362ee
--- /dev/null
+++ b/mvc/gtkmvc/support/wrappers.py
@@ -0,0 +1,152 @@
+# -------------------------------------------------------------------------
+# Author: Roberto Cavada
+#
+# Copyright (C) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+# -------------------------------------------------------------------------
+
+
+import new
+
+
+# ----------------------------------------------------------------------
+class ObsWrapperBase (object):
+ def __init__(self):
+ self.__prop_name = None
+ self.__gtkmvc_model = None
+ return
+
+ def __set_model__(self, model, prop_name):
+ self.__prop_name = prop_name
+ self.__gtkmvc_model = model
+ return
+
+ def __get_prop_name__(self): return self.__prop_name
+ def __get_model__(self): return self.__gtkmvc_model
+
+ def _notify_method_before(self, instance, name, args, kwargs):
+ self.__get_model__().notify_method_before_change(self.__prop_name,
+ instance,
+ name, args, kwargs)
+ return
+
+ def _notify_method_after(self, instance, name, res_val, args, kwargs):
+ self.__get_model__().notify_method_after_change(self.__prop_name,
+ instance,
+ name, res_val, args, kwargs)
+ return
+
+ pass
+
+
+# ----------------------------------------------------------------------
+class ObsWrapper (ObsWrapperBase):
+
+ def __init__(self, obj, method_names):
+ ObsWrapperBase.__init__(self)
+
+ self._obj = obj
+ self.__doc__ = obj.__doc__
+
+ for name in method_names:
+ if hasattr(self._obj, name) and not hasattr(self, name):
+ src = self.__get_wrapper_code(name)
+ exec src
+
+ code = eval("%s.func_code" % name)
+ func = new.function(code, globals())
+ meth = new.instancemethod(func, self, type(self).__name__)
+ setattr(self, name, meth)
+ pass
+ pass
+
+ return
+
+ def __get_wrapper_code(self, name):
+ return """def %(name)s(self, *args, **kwargs):
+ self._notify_method_before(self._obj, "%(name)s", args, kwargs)
+ res = self._obj.%(name)s(*args, **kwargs)
+ self._notify_method_after(self._obj, "%(name)s", res, args, kwargs)
+ return res""" % {'name' : name}
+
+ # For all fall backs
+ def __getattr__(self, name): return self._obj.__getattr__(name)
+ def __repr__(self): return self._obj.__repr__()
+ def __str__(self): return self._obj.__str__()
+
+ pass #end of class
+
+
+# ----------------------------------------------------------------------
+class ObsSeqWrapper (ObsWrapper):
+ def __init__(self, obj, method_names):
+ ObsWrapper.__init__(self, obj, method_names)
+ return
+
+ def __setitem__(self, key, val):
+
+ self._notify_method_before(self._obj, "__setitem__", (key,val), {})
+ res = self._obj.__setitem__(key, val)
+ self._notify_method_after(self._obj, res, "__setitem__", (key,val), {})
+ return res
+
+ def __delitem__(self, key):
+ self._notify_method_before(self._obj, "__delitem__", (key,), {})
+ res = self._obj.__delitem__(key)
+ self._notify_method_after(self._obj, res, "__delitem__", (key,), {})
+ return res
+
+
+ def __getitem__(self, key):
+ return self._obj.__getitem__(key)
+
+ pass #end of class
+
+
+# ----------------------------------------------------------------------
+class ObsMapWrapper (ObsSeqWrapper):
+ def __init__(self, m):
+ methods = ("clear", "pop", "popitem", "update",
+ "setdefault")
+ ObsSeqWrapper.__init__(self, m, methods)
+ return
+ pass #end of class
+
+
+# ----------------------------------------------------------------------
+class ObsListWrapper (ObsSeqWrapper):
+ def __init__(self, l):
+ methods = ("append", "extend", "insert",
+ "pop", "remove", "reverse", "sort")
+ ObsSeqWrapper.__init__(self, l, methods)
+ return
+ pass #end of class
+
+
+
+# ----------------------------------------------------------------------
+class ObsUserClassWrapper (ObsWrapper):
+ def __init__(self, user_class_instance, obs_method_names):
+ ObsWrapper.__init__(self, user_class_instance, obs_method_names)
+ return
+ pass #end of class
+
+
+
diff --git a/mvc/gtkmvc/view.py b/mvc/gtkmvc/view.py
new file mode 100644
index 0000000..94147b1
--- /dev/null
+++ b/mvc/gtkmvc/view.py
@@ -0,0 +1,158 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2005 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author Roberto Cavada .
+# Please report bugs to .
+
+
+import gtk.glade
+from controller import Controller
+import types
+
+class View (object):
+
+ def __init__(self, controller, glade_filename=None,
+ glade_top_widget_name=None, parent_view=None, register=True):
+ """If register is False you *must* call 'controller.register_view(self)'
+ from the derived class constructor (i.e. registration is delayed)
+ If filename is not given (or None) all following parameters must be
+ not given (or None). In that case widgets must be connected manually.
+ glade_top_widget_name can be either a string name or list of names."""
+ self.manualWidgets = {}
+ self.xmlWidgets = []
+
+ if (( type(glade_top_widget_name) == types.StringType)
+ or (glade_top_widget_name is None) ):
+ wids = (glade_top_widget_name,)
+ else: wids = glade_top_widget_name # Already a list or tuple
+
+ if (glade_filename is not None):
+ for i in range(0,len(wids)):
+ self.xmlWidgets.append(gtk.glade.XML(glade_filename, wids[i]))
+ pass
+ pass
+
+ # top widget list or singleton:
+ if (glade_top_widget_name is not None):
+ if len(wids) > 1:
+ self.m_topWidget = []
+ for i in range(0, len(wids)):
+ self.m_topWidget.append(self[wids[i]])
+ pass
+ else: self.m_topWidget = self[wids[0]]
+ else: self.m_topWidget = None
+
+ if (glade_filename is not None): self.autoconnect_signals(controller)
+ if (register): controller.register_view(self)
+ if (not parent_view is None): self.set_parent_view(parent_view)
+ return
+
+ # Gives us the ability to do: view['widget_name'].action()
+ # Returns None if no widget name has been found.
+ def __getitem__(self, key):
+ wid = None
+ for xml in self.xmlWidgets:
+ wid = xml.get_widget(key)
+ if wid is not None: break
+ pass
+
+ if (wid is None):
+ # try with manually-added widgets:
+ if (self.manualWidgets.has_key(key)):
+ wid = self.manualWidgets[key]
+ pass
+ pass
+ return wid
+
+ # You can also add a single widget:
+ def __setitem__(self, key, wid):
+ self.manualWidgets[key] = wid
+ if (self.m_topWidget is None): self.m_topWidget = wid
+ return
+
+ # performs Controller's signals auto-connection:
+ def autoconnect_signals(self, controller):
+ dict = {}
+ member_names = dir(controller)
+ for name in member_names:
+ method = getattr(controller, name)
+ if (not callable(method)): continue
+ assert(not dict.has_key(name)) # not already connected!
+ dict[name] = method
+ pass
+
+ for xml in self.xmlWidgets: xml.signal_autoconnect(dict)
+ return
+
+ def show(self):
+ ret = True
+ top = self.get_top_widget()
+ if type(top) in (types.ListType, types.TupleType):
+ for t in top:
+ if t is not None: ret = ret and t.show()
+ pass
+ elif (top is not None): ret = top.show_all()
+ else: ret = False
+ return ret
+
+
+ def hide(self):
+ top = self.get_top_widget()
+ if type(top) in (types.ListType, types.TupleType):
+ for t in top:
+ if t is not None: t.hide_all()
+ pass
+ elif top is not None: top.hide_all()
+ return
+
+ # Returns the top-level widget, or a list of top widgets
+ def get_top_widget(self):
+ return self.m_topWidget
+
+
+ # Set parent view:
+ def set_parent_view(self, parent_view):
+ top = self.get_top_widget()
+ if type(top) in (types.ListType, types.TupleType):
+ for t in top:
+ if t is not None:
+ t.set_transient_for(parent_view.get_top_widget())
+ pass
+ pass
+ elif (top is not None):
+ top.set_transient_for(parent_view.get_top_widget())
+ pass
+
+ return
+
+ # Set the transient for the view:
+ def set_transient(self, transient_view):
+ top = self.get_top_widget()
+ if type(top) in (types.ListType, types.TupleType):
+ for t in top:
+ if t is not None:
+ transient_view.get_top_widget().set_transient_for(t)
+ pass
+ pass
+ elif (top is not None):
+ transient_view.get_top_widget().set_transient_for(top)
+ pass
+ return
+
+ pass # end of class View
diff --git a/mvc/img/notavail.gif b/mvc/img/notavail.gif
new file mode 100644
index 0000000..e60111e
Binary files /dev/null and b/mvc/img/notavail.gif differ
diff --git a/mvc/main_window.py b/mvc/main_window.py
new file mode 100644
index 0000000..41dbef9
--- /dev/null
+++ b/mvc/main_window.py
@@ -0,0 +1,24 @@
+# This Python file uses the following encoding: utf-8
+def setup_path():
+ """Sets up the python include paths to include src"""
+ import os.path; import sys
+
+ if sys.argv[0]:
+ top_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+ sys.path = [os.path.join(top_dir, "src")] + sys.path
+ pass
+ return
+
+if __name__ == "__main__":
+ setup_path()
+
+ from models.m_main import MainModel
+ from controllers.c_main import MainController
+ from views.v_main import MainView
+ m = MainModel()
+ c = MainController(m)
+ v = MainView(c)
+
+ import gtk
+ gtk.main()
+ pass
diff --git a/mvc/pixmaps/mainicon.png b/mvc/pixmaps/mainicon.png
new file mode 100644
index 0000000..55a22b1
Binary files /dev/null and b/mvc/pixmaps/mainicon.png differ
diff --git a/mvc/pixmaps/mainicon.xcf b/mvc/pixmaps/mainicon.xcf
new file mode 100644
index 0000000..06a6164
Binary files /dev/null and b/mvc/pixmaps/mainicon.xcf differ
diff --git a/mvc/pixmaps/mainicon2.xcf b/mvc/pixmaps/mainicon2.xcf
new file mode 100644
index 0000000..7e9949d
Binary files /dev/null and b/mvc/pixmaps/mainicon2.xcf differ
diff --git a/mvc/pyGTKtalog b/mvc/pyGTKtalog
new file mode 100755
index 0000000..736f295
--- /dev/null
+++ b/mvc/pyGTKtalog
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Simple wraper to launch python application from desired place.
+cd ~/Python/pyGTKtalog/mvc
+#export PYTHONPATH="$PYTHONPATH:/usr/lib/gajim"
+#exec python -OO pyGTKtalog.py $@
+exec python main_window.py $@
diff --git a/mvc/src/__init__.py b/mvc/src/__init__.py
new file mode 100644
index 0000000..8349638
--- /dev/null
+++ b/mvc/src/__init__.py
@@ -0,0 +1,3 @@
+
+
+__all__ = ["models", "views", "controllers", "utils"]
diff --git a/mvc/src/controllers/__init__.py b/mvc/src/controllers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mvc/src/controllers/c_main.py b/mvc/src/controllers/c_main.py
new file mode 100644
index 0000000..b4afd2f
--- /dev/null
+++ b/mvc/src/controllers/c_main.py
@@ -0,0 +1,38 @@
+# This Python file uses the following encoding: utf-8
+
+import utils._importer
+import utils.globals
+from gtkmvc import Controller
+import gtk
+
+class MainController(Controller):
+ """Kontroler głównego okna aplikacji"""
+ def __init__(self, model):
+ Controller.__init__(self, model)
+ return
+
+ def register_view(self, view):
+ Controller.register_view(self, view)
+
+ # ustaw domyślne wartości dla poszczególnych widżetów
+ self.view['main'].set_title('pyGTKtalog');
+ self.view['main'].set_icon_list(gtk.gdk.pixbuf_new_from_file("pixmaps/mainicon.png"))
+
+ # pokaż główne okno
+ self.view['main'].show();
+ return
+
+ # Podłącz sygnały:
+ def on_main_destroy_event(self, window, event):
+ gtk.main_quit()
+ return True
+
+ def on_tb_quit_clicked(self,toolbutton):
+ gtk.main_quit()
+
+ def on_quit1_activate(self,button):
+ gtk.main_quit()
+
+ # Obserwowalne właściwości
+
+ pass # end of class
diff --git a/mvc/src/models/__init__.py b/mvc/src/models/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mvc/src/models/m_main.py b/mvc/src/models/m_main.py
new file mode 100644
index 0000000..45c9aeb
--- /dev/null
+++ b/mvc/src/models/m_main.py
@@ -0,0 +1,18 @@
+# This Python file uses the following encoding: utf-8
+
+import utils._importer
+import utils.globals
+from gtkmvc import Model
+
+class MainModel(Model):
+ """Our model contains a numeric counter and a numeric value that
+ holds the value that the counter must be assigned to when we the
+ model is reset"""
+
+ __properties__ = {}
+
+ def __init__(self):
+ Model.__init__(self)
+ return
+
+ pass # end of class
diff --git a/mvc/src/utils/__init__.py b/mvc/src/utils/__init__.py
new file mode 100644
index 0000000..11d1287
--- /dev/null
+++ b/mvc/src/utils/__init__.py
@@ -0,0 +1,3 @@
+
+
+__all__ = ["globals", "_importer"]
diff --git a/mvc/src/utils/_importer.py b/mvc/src/utils/_importer.py
new file mode 100644
index 0000000..8a92aac
--- /dev/null
+++ b/mvc/src/utils/_importer.py
@@ -0,0 +1,44 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author .
+# Please report bugs to .
+
+
+
+# ======================================================================
+# This module is used only as a utility to import gtkmvc when not
+# installed.
+
+import utils.globals
+
+if __name__ != "__main__":
+ try: import gtkmvc
+ except:
+ import os.path; import sys
+
+ rel_path = os.path.join(utils.globals.TOP_DIR, "..")
+ top_dir = os.path.dirname(os.path.abspath(rel_path))
+ sys.path = [top_dir] + sys.path
+ import gtkmvc
+ pass
+
+ gtkmvc.require("1.0.0")
+pass
+
diff --git a/mvc/src/utils/globals.py b/mvc/src/utils/globals.py
new file mode 100644
index 0000000..38291ff
--- /dev/null
+++ b/mvc/src/utils/globals.py
@@ -0,0 +1,33 @@
+# Author: Roberto Cavada
+#
+# Copyright (c) 2006 by Roberto Cavada
+#
+# pygtkmvc is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# pygtkmvc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# For more information on pygtkmvc see
+# or email to the author .
+# Please report bugs to .
+
+
+import os.path
+import sys
+
+if sys.argv[0]: top_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+else: top_dir = "."
+
+
+# A set of global vars
+TOP_DIR = top_dir
+GLADE_DIR = os.path.join(TOP_DIR, "glade")
diff --git a/mvc/src/views/__init__.py b/mvc/src/views/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mvc/src/views/v_main.py b/mvc/src/views/v_main.py
new file mode 100644
index 0000000..45125fa
--- /dev/null
+++ b/mvc/src/views/v_main.py
@@ -0,0 +1,17 @@
+# This Python file uses the following encoding: utf-8
+
+import utils._importer
+import utils.globals
+from gtkmvc import View
+import os.path
+
+class MainView(View):
+ """This handles only the graphical representation of the
+ application. The widgets set is loaded from glade file"""
+
+ GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
+ def __init__(self, ctrl):
+ View.__init__(self, ctrl, self.GLADE)
+ return
+
+ pass # end of class