mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 11:30:19 +01:00
241 lines
9.8 KiB
Python
241 lines
9.8 KiB
Python
# Author: Roberto Cavada <cavada@fbk.eu>
|
|
#
|
|
# 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., 51 Franklin Street, Fifth Floor,
|
|
# Boston, MA 02110, USA.
|
|
#
|
|
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
|
|
# or email to the author Roberto Cavada <cavada@fbk.eu>.
|
|
# Please report bugs to <cavada@fbk.eu>.
|
|
|
|
|
|
import new
|
|
import re
|
|
import types
|
|
|
|
import gtkmvc.support.wrappers as wrappers
|
|
from gtkmvc.support.utils import get_function_from_source
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
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 associated 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 overridden 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)
|
|
func = get_function_from_source(src)
|
|
setattr(cls, getter_name, func)
|
|
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)
|
|
func = get_function_from_source(src)
|
|
setattr(cls, setter_name, func)
|
|
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 overrids 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 check_value_change(cls, old, new):
|
|
"""Checks whether the value of the property changed in type
|
|
or if the instance has been changed to a different instance.
|
|
If true, a call to model._reset_property_notification should
|
|
be called in order to re-register the new property instance
|
|
or type"""
|
|
return type(old) != type(new) or \
|
|
isinstance(old, wrappers.ObsWrapperBase) and (old != new)
|
|
|
|
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
|
|
# ------------------------------------------------------------
|
|
|
|
# Override these:
|
|
def get_getter_source(cls, getter_name, prop_name):
|
|
"""This must be overridden 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 overridden 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
|
|
# ----------------------------------------------------------------------
|