From f695f82c1f2a55eb34eece142aed4fecc986ce62 Mon Sep 17 00:00:00 2001 From: gryf Date: Tue, 1 May 2007 19:30:22 +0000 Subject: [PATCH] * New version using pygtkmvc framework. --- mvc/glade/foo.glade | 130 ++++++ mvc/glade/main.glade | 588 +++++++++++++++++++++++++++ mvc/gtkmvc/__init__.py | 48 +++ mvc/gtkmvc/controller.py | 46 +++ mvc/gtkmvc/model.py | 316 ++++++++++++++ mvc/gtkmvc/model_mt.py | 119 ++++++ mvc/gtkmvc/observable.py | 68 ++++ mvc/gtkmvc/observer.py | 56 +++ mvc/gtkmvc/support/__init__.py | 24 ++ mvc/gtkmvc/support/decorators.py | 43 ++ mvc/gtkmvc/support/metaclass_base.py | 244 +++++++++++ mvc/gtkmvc/support/metaclasses.py | 54 +++ mvc/gtkmvc/support/wrappers.py | 152 +++++++ mvc/gtkmvc/view.py | 158 +++++++ mvc/img/notavail.gif | Bin 0 -> 1917 bytes mvc/main_window.py | 24 ++ mvc/pixmaps/mainicon.png | Bin 0 -> 3762 bytes mvc/pixmaps/mainicon.xcf | Bin 0 -> 14161 bytes mvc/pixmaps/mainicon2.xcf | Bin 0 -> 21915 bytes mvc/pyGTKtalog | 6 + mvc/src/__init__.py | 3 + mvc/src/controllers/__init__.py | 0 mvc/src/controllers/c_main.py | 38 ++ mvc/src/models/__init__.py | 0 mvc/src/models/m_main.py | 18 + mvc/src/utils/__init__.py | 3 + mvc/src/utils/_importer.py | 44 ++ mvc/src/utils/globals.py | 33 ++ mvc/src/views/__init__.py | 0 mvc/src/views/v_main.py | 17 + 30 files changed, 2232 insertions(+) create mode 100644 mvc/glade/foo.glade create mode 100644 mvc/glade/main.glade create mode 100644 mvc/gtkmvc/__init__.py create mode 100644 mvc/gtkmvc/controller.py create mode 100644 mvc/gtkmvc/model.py create mode 100644 mvc/gtkmvc/model_mt.py create mode 100644 mvc/gtkmvc/observable.py create mode 100644 mvc/gtkmvc/observer.py create mode 100644 mvc/gtkmvc/support/__init__.py create mode 100644 mvc/gtkmvc/support/decorators.py create mode 100644 mvc/gtkmvc/support/metaclass_base.py create mode 100644 mvc/gtkmvc/support/metaclasses.py create mode 100644 mvc/gtkmvc/support/wrappers.py create mode 100644 mvc/gtkmvc/view.py create mode 100644 mvc/img/notavail.gif create mode 100644 mvc/main_window.py create mode 100644 mvc/pixmaps/mainicon.png create mode 100644 mvc/pixmaps/mainicon.xcf create mode 100644 mvc/pixmaps/mainicon2.xcf create mode 100755 mvc/pyGTKtalog create mode 100644 mvc/src/__init__.py create mode 100644 mvc/src/controllers/__init__.py create mode 100644 mvc/src/controllers/c_main.py create mode 100644 mvc/src/models/__init__.py create mode 100644 mvc/src/models/m_main.py create mode 100644 mvc/src/utils/__init__.py create mode 100644 mvc/src/utils/_importer.py create mode 100644 mvc/src/utils/globals.py create mode 100644 mvc/src/views/__init__.py create mode 100644 mvc/src/views/v_main.py 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 + + + + True + _File + True + + + + + + + True + gtk-new + True + + + + + + + True + gtk-open + True + + + + + + + True + gtk-save + True + + + + + + + True + gtk-save-as + True + + + + + + + True + + + + + + True + Recent files + True + + + + + + + True + + + + + + True + gtk-quit + True + + + + + + + + + + + True + _Edit + True + + + + + + + True + gtk-cut + True + + + + + + + True + gtk-copy + True + + + + + + + True + gtk-paste + True + + + + + + + True + gtk-delete + True + + + + + + + True + + + + + + True + gtk-preferences + True + + + + + + + + + + + True + _Catalog + True + + + + + + + True + Add CD/DVD + True + + + + + + + + True + Add Directory + True + + + + + + + + + + + True + _View + True + + + + + + + True + Toolbar + True + False + + + + + + + True + Status bar + True + False + + + + + + + + + + + True + _Help + True + + + + + + + True + gtk-about + True + + + + + + + + + + 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 0000000000000000000000000000000000000000..e60111e9c993442ad1a4ee199ca85b523ce6a857 GIT binary patch literal 1917 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 literal 0 HcmV?d00001 diff --git a/mvc/pixmaps/mainicon.xcf b/mvc/pixmaps/mainicon.xcf new file mode 100644 index 0000000000000000000000000000000000000000..06a6164ea85d7fbabc8147112c0caf377eb0ca66 GIT binary patch literal 14161 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 HN~rz^uoQ7> literal 0 HcmV?d00001 diff --git a/mvc/pixmaps/mainicon2.xcf b/mvc/pixmaps/mainicon2.xcf new file mode 100644 index 0000000000000000000000000000000000000000..7e9949d4249bfe0c74e2667d3b0d25bdeafd0eab GIT binary patch literal 21915 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 +# +# 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