Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--addons/bubblemessage.py5
-rw-r--r--tests/addontests.py2
-rw-r--r--tutorius/actions.py2
-rw-r--r--tutorius/creator.py273
-rw-r--r--tutorius/properties.py16
-rw-r--r--tutorius/propwidgets.py489
6 files changed, 532 insertions, 255 deletions
diff --git a/addons/bubblemessage.py b/addons/bubblemessage.py
index 1ed1fe0..aaf086c 100644
--- a/addons/bubblemessage.py
+++ b/addons/bubblemessage.py
@@ -96,8 +96,9 @@ class BubbleMessage(Action):
self._drag = DragWrapper(self._bubble, self.position, True)
def exit_editmode(self, *args):
- x,y = self._drag.position
- self.position = (int(x), int(y))
+ if self._drag.moved:
+ x,y = self._drag.position
+ self.position = (int(x), int(y))
if self._drag:
self._drag.draggable = False
self._drag = None
diff --git a/tests/addontests.py b/tests/addontests.py
index 5fb4f61..ceaee2b 100644
--- a/tests/addontests.py
+++ b/tests/addontests.py
@@ -47,4 +47,4 @@ class AddonTest(unittest.TestCase):
def test_get_addon_meta(self):
addon._cache = None
meta = addon.get_addon_meta("BubbleMessage")
- assert meta.keys() == ['mandatory_props', 'class', 'display_name', 'name', 'icon',]
+ assert set(meta.keys()) == set(['type', 'mandatory_props', 'class', 'display_name', 'name', 'icon',])
diff --git a/tutorius/actions.py b/tutorius/actions.py
index bb15459..75c9c9b 100644
--- a/tutorius/actions.py
+++ b/tutorius/actions.py
@@ -43,6 +43,7 @@ class DragWrapper(object):
self._handles = [] # event handlers
self._dragging = False # whether a drag is in progress
self.position = position # position of the widget
+ self.moved = False
self.draggable = draggable
@@ -68,6 +69,7 @@ class DragWrapper(object):
xparent, yparent = widget.translate_coordinates(widget.parent,
xparent, yparent)
self.position = (xparent-xrel, yparent-yrel)
+ self.moved = True
self._widget.parent.move(self._eventbox, *self.position)
self._widget.parent.move(self._widget, *self.position)
self._widget.parent.queue_draw()
diff --git a/tutorius/creator.py b/tutorius/creator.py
index 906a04e..68c5fa6 100644
--- a/tutorius/creator.py
+++ b/tutorius/creator.py
@@ -36,6 +36,7 @@ from .services import ObjectStore
from .core import State
from .tutorial import Tutorial
from . import viewer
+from .propwidgets import TextInputDialog, StringPropWidget
class Creator(object):
"""
@@ -230,25 +231,7 @@ class Creator(object):
meta = addon.get_addon_meta(addonname)
for propname in meta['mandatory_props']:
prop = getattr(type(event), propname)
- if isinstance(prop, properties.TUAMProperty):
- selector = WidgetSelector(self._activity)
- setattr(event, propname, selector.select())
- elif isinstance(prop, properties.TEventType):
- try:
- dlg = SignalInputDialog(self._activity,
- text="Mandatory property",
- field=propname,
- addr=event.object_id)
- setattr(event, propname, dlg.pop())
- except AttributeError:
- pass
- elif isinstance(prop, properties.TStringProperty):
- dlg = TextInputDialog(self._activity,
- text="Mandatory property",
- field=propname)
- setattr(event, propname, dlg.pop())
- else:
- raise NotImplementedError()
+ prop.widget_class.run_dialog(self._activity, event, propname)
event_filters = self._tutorial.get_transition_dict(self._state)
@@ -360,6 +343,7 @@ class ToolBox(object):
self.tree = gtk.glade.XML(glade_file)
self.window = self.tree.get_widget('mainwindow')
self._propbox = self.tree.get_widget('propbox')
+ self._propedits = []
self.window.set_transient_for(parent)
@@ -402,74 +386,38 @@ class ToolBox(object):
"""Refresh property values from the selected action."""
if self._action is None:
return
- props = self._action._props.keys()
- for propnum in xrange(len(props)):
- row = self._propbox.get_children()[propnum]
- propname = props[propnum]
- prop = getattr(type(self._action), propname)
- propval = getattr(self._action, propname)
- if isinstance(prop, properties.TStringProperty):
- propwdg = row.get_children()[1]
- propwdg.get_buffer().set_text(propval)
- elif isinstance(prop, properties.TUAMProperty):
- propwdg = row.get_children()[1]
- propwdg.set_label(propval)
- elif isinstance(prop, properties.TIntProperty):
- propwdg = row.get_children()[1]
- propwdg.set_value(propval)
- elif isinstance(prop, properties.TArrayProperty):
- propwdg = row.get_children()[1]
- for i in xrange(len(propval)):
- entry = propwdg.get_children()[i]
- entry.set_text(str(propval[i]))
- else:
- propwdg = row.get_children()[1]
- propwdg.set_text(str(propval))
+
+ #Refresh the property editors
+ for prop in self._propedits:
+ prop.refresh_widget()
def set_action(self, action):
"""Setter for the action property."""
if self._action is action:
self.refresh_properties()
return
+
+ #Clear the prop box
for old_prop in self._propbox.get_children():
self._propbox.remove(old_prop)
+ self._propedits = []
+
self._action = action
if action is None:
return
for propname in action._props.keys():
row = gtk.HBox()
+ #Label
row.pack_start(gtk.Label(T(propname)), False, False, 10)
+
+ #Value field
prop = getattr(type(action), propname)
- propval = getattr(action, propname)
- if isinstance(prop, properties.TStringProperty):
- propwdg = gtk.TextView()
- propwdg.get_buffer().set_text(propval)
- propwdg.connect_after("focus-out-event", \
- self._str_prop_changed, action, propname)
- elif isinstance(prop, properties.TUAMProperty):
- propwdg = gtk.Button(propval)
- propwdg.connect_after("clicked", \
- self._uam_prop_changed, action, propname)
- elif isinstance(prop, properties.TIntProperty):
- adjustment = gtk.Adjustment(value=propval,
- lower=prop.lower_limit.limit,
- upper=prop.upper_limit.limit,
- step_incr=1)
- propwdg = gtk.SpinButton(adjustment=adjustment)
- propwdg.connect_after("focus-out-event", \
- self._int_prop_changed, action, prop)
- elif isinstance(prop, properties.TArrayProperty):
- propwdg = gtk.HBox()
- for i in xrange(len(propval)):
- entry = gtk.Entry()
- propwdg.pack_start(entry)
- entry.connect_after("focus-out-event", \
- self._list_prop_changed, action, propname, i)
- else:
- propwdg = gtk.Entry()
- propwdg.set_text(str(propval))
- row.pack_end(propwdg)
+ propedit = prop.widget_class(self.__parent, action, propname, self._refresh_action_cb)
+ self._propedits.append(propedit)
+ row.pack_end(propedit.widget)
+
+ #Add row
self._propbox.pack_start(row, expand=False)
self._propbox.show_all()
self.refresh_properties()
@@ -480,187 +428,10 @@ class ToolBox(object):
action = property(fset=set_action, fget=get_action, doc=\
"Action to be edited through introspection.")
- def _list_prop_changed(self, widget, evt, action, propname, idx):
- try:
- #Save props as tuples so that they can be hashed
- attr = list(getattr(action, propname))
- attr[idx] = int(widget.get_text())
- setattr(action, propname, tuple(attr))
- except ValueError:
- widget.set_text(str(getattr(action, propname)[idx]))
- self.__parent._creator._action_refresh_cb(None, None, action)
- def _uam_prop_changed(self, widget, action, propname):
- selector = WidgetSelector(self.__parent)
- selection = selector.select()
- setattr(action, propname, selection)
- self.__parent._creator._action_refresh_cb(None, None, action)
- def _str_prop_changed(self, widget, evt, action, propname):
- buf = widget.get_buffer()
- setattr(action, propname, buf.get_text(buf.get_start_iter(), buf.get_end_iter()))
- self.__parent._creator._action_refresh_cb(None, None, action)
- def _int_prop_changed(self, widget, evt, action, prop):
- setattr(action, propname, widget.get_value_as_int())
- self.__parent._creator._action_refresh_cb(None, None, action)
-
-
-class WidgetSelector(object):
- """
- Allow selecting a widget from within a window without interrupting the
- flow of the current call.
-
- The selector will run on the specified window until either a widget
- is selected or abort() gets called.
- """
- def __init__(self, window):
- super(WidgetSelector, self).__init__()
- self.window = window
- self._intro_mask = None
- self._intro_handle = None
- self._select_handle = None
- self._prelight = None
+ def _refresh_action_cb(self):
+ if self._action is not None:
+ self.__parent._creator._action_refresh_cb(None, None, self._action)
- def select(self):
- """
- Starts selecting a widget, by grabbing control of the mouse and
- highlighting hovered widgets until one is clicked.
- @returns: a widget address or None
- """
- if not self._intro_mask:
- self._prelight = None
- self._intro_mask = overlayer.Mask(catch_events=True)
- self._select_handle = self._intro_mask.connect_after(
- "button-press-event", self._end_introspect)
- self._intro_handle = self._intro_mask.connect_after(
- "motion-notify-event", self._intro_cb)
- self.window._overlayer.put(self._intro_mask, 0, 0)
- self.window._overlayer.queue_draw()
-
- while bool(self._intro_mask) and not gtk.main_iteration():
- pass
-
- return gtkutils.raddr_lookup(self._prelight)
-
- def _end_introspect(self, widget, evt):
- if evt.type == gtk.gdk.BUTTON_PRESS and self._prelight:
- self._intro_mask.catch_events = False
- self._intro_mask.disconnect(self._intro_handle)
- self._intro_handle = None
- self._intro_mask.disconnect(self._select_handle)
- self._select_handle = None
- self.window._overlayer.remove(self._intro_mask)
- self._intro_mask = None
- # for some reason, gtk may not redraw after this unless told to.
- self.window.queue_draw()
-
- def _intro_cb(self, widget, evt):
- """
- Callback for capture of widget events, when in introspect mode.
- """
- # widget has focus, let's hilight it
- win = gtk.gdk.display_get_default().get_window_at_pointer()
- if not win:
- return
- click_wdg = win[0].get_user_data()
- if not click_wdg.is_ancestor(self.window._overlayer):
- # as popups are not (yet) supported, it would break
- # badly if we were to play with a widget not in the
- # hierarchy.
- return
- for hole in self._intro_mask.pass_thru:
- self._intro_mask.mask(hole)
- self._intro_mask.unmask(click_wdg)
- self._prelight = click_wdg
-
- self.window.queue_draw()
-
- def abort(self):
- """
- Ends the selection. The control will return to the select() caller
- with a return value of None, as selection was aborted.
- """
- self._intro_mask.catch_events = False
- self._intro_mask.disconnect(self._intro_handle)
- self._intro_handle = None
- self._intro_mask.disconnect(self._select_handle)
- self._select_handle = None
- self.window._overlayer.remove(self._intro_mask)
- self._intro_mask = None
- self._prelight = None
-
-class SignalInputDialog(gtk.MessageDialog):
- def __init__(self, parent, text, field, addr):
- """
- Create a gtk signal selection dialog.
-
- @param parent: the parent window this dialog should stay over.
- @param text: the title of the dialog.
- @param field: the field description of the dialog.
- @param addr: the widget address from which to fetch signal list.
- """
- gtk.MessageDialog.__init__(self, parent,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK,
- None)
- self.set_markup(text)
- self.model = gtk.ListStore(str)
- widget = gtkutils.find_widget(parent, addr)
- for signal_name in gobject.signal_list_names(widget):
- self.model.append(row=(signal_name,))
- self.entry = gtk.ComboBox(self.model)
- cell = gtk.CellRendererText()
- self.entry.pack_start(cell)
- self.entry.add_attribute(cell, 'text', 0)
- hbox = gtk.HBox()
- lbl = gtk.Label(field)
- hbox.pack_start(lbl, False)
- hbox.pack_end(self.entry)
- self.vbox.pack_end(hbox, True, True)
- self.show_all()
-
- def pop(self):
- """
- Show the dialog. It will run in it's own loop and return control
- to the caller when a signal has been selected.
-
- @returns: a signal name or None if no signal was selected
- """
- self.run()
- self.hide()
- iter = self.entry.get_active_iter()
- if iter:
- text = self.model.get_value(iter, 0)
- return text
- return None
-
- def _dialog_done_cb(self, entry, response):
- self.response(response)
-
-class TextInputDialog(gtk.MessageDialog):
- def __init__(self, parent, text, field):
- gtk.MessageDialog.__init__(self, parent,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK,
- None)
- self.set_markup(text)
- self.entry = gtk.Entry()
- self.entry.connect("activate", self._dialog_done_cb, gtk.RESPONSE_OK)
- hbox = gtk.HBox()
- lbl = gtk.Label(field)
- hbox.pack_start(lbl, False)
- hbox.pack_end(self.entry)
- self.vbox.pack_end(hbox, True, True)
- self.show_all()
-
- def pop(self):
- self.run()
- self.hide()
- text = self.entry.get_text()
- return text
-
- def _dialog_done_cb(self, entry, response):
- self.response(response)
# The purpose of this function is to reformat text, as current IconView
# implentation does not insert carriage returns on long lines.
diff --git a/tutorius/properties.py b/tutorius/properties.py
index 5422532..aab6a0c 100644
--- a/tutorius/properties.py
+++ b/tutorius/properties.py
@@ -25,6 +25,13 @@ from .constraints import Constraint, \
UpperLimitConstraint, LowerLimitConstraint, \
MaxSizeConstraint, MinSizeConstraint, \
ColorConstraint, FileConstraint, BooleanConstraint, EnumConstraint
+from .propwidgets import PropWidget, \
+ StringPropWidget, \
+ UAMPropWidget, \
+ EventTypePropWidget, \
+ IntPropWidget, \
+ FloatPropWidget, \
+ IntArrayPropWidget
class TPropContainer(object):
"""
@@ -131,6 +138,7 @@ class TutoriusProperty(object):
get_contraints() : the constraints inserted on this property. They define
what is acceptable or not as values.
"""
+ widget_class = PropWidget
def __init__(self):
super(TutoriusProperty, self).__init__()
self.type = None
@@ -175,7 +183,7 @@ class TIntProperty(TutoriusProperty):
Represents an integer. Can have an upper value limit and/or a lower value
limit.
"""
-
+ widget_class = IntPropWidget
def __init__(self, value, lower_limit=None, upper_limit=None):
TutoriusProperty.__init__(self)
self.type = "int"
@@ -189,6 +197,7 @@ class TFloatProperty(TutoriusProperty):
Represents a floating point number. Can have an upper value limit and/or
a lower value limit.
"""
+ widget_class = FloatPropWidget
def __init__(self, value, lower_limit=None, upper_limit=None):
TutoriusProperty.__init__(self)
self.type = "float"
@@ -202,6 +211,7 @@ class TStringProperty(TutoriusProperty):
"""
Represents a string. Can have a maximum size limit.
"""
+ widget_class = StringPropWidget
def __init__(self, value, size_limit=None):
TutoriusProperty.__init__(self)
self.type = "string"
@@ -214,6 +224,7 @@ class TArrayProperty(TutoriusProperty):
Represents an array of properties. Can have a maximum number of element
limit, but there are no constraints on the content of the array.
"""
+ widget_class = IntArrayPropWidget
def __init__(self, value, min_size_limit=None, max_size_limit=None):
TutoriusProperty.__init__(self)
self.type = "array"
@@ -233,6 +244,7 @@ class TArrayProperty(TutoriusProperty):
min_size_limit=self.min_size_limit.limit,
value=self.value,
)
+
class TColorProperty(TutoriusProperty):
"""
Represents a RGB color with 3 8-bit integer values.
@@ -311,6 +323,7 @@ class TUAMProperty(TutoriusProperty):
"""
Represents a widget of the interface by storing its UAM.
"""
+ widget_class = UAMPropWidget
def __init__(self, value=None):
TutoriusProperty.__init__(self)
@@ -343,6 +356,7 @@ class TEventType(TutoriusProperty):
"""
Represents an GUI signal for a widget.
"""
+ widget_class = EventTypePropWidget
def __init__(self, value):
super(TEventType, self).__init__()
self.type = "gtk-signal"
diff --git a/tutorius/propwidgets.py b/tutorius/propwidgets.py
new file mode 100644
index 0000000..7e78ba4
--- /dev/null
+++ b/tutorius/propwidgets.py
@@ -0,0 +1,489 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+Property Widgets.
+
+Allows displaying properties cleanly.
+"""
+import gtk
+import gobject
+
+from . import gtkutils, overlayer
+###########################################################################
+# Dialog classes
+###########################################################################
+class TextInputDialog(gtk.MessageDialog):
+ def __init__(self, parent, text, field):
+ gtk.MessageDialog.__init__(self, parent,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_QUESTION,
+ gtk.BUTTONS_OK,
+ None)
+ self.set_markup(text)
+ self.entry = gtk.Entry()
+ self.entry.connect("activate", self._dialog_done_cb, gtk.RESPONSE_OK)
+ hbox = gtk.HBox()
+ lbl = gtk.Label(field)
+ hbox.pack_start(lbl, False)
+ hbox.pack_end(self.entry)
+ self.vbox.pack_end(hbox, True, True)
+ self.show_all()
+
+ def pop(self):
+ self.run()
+ self.hide()
+ text = self.entry.get_text()
+ return text
+
+ def _dialog_done_cb(self, entry, response):
+ self.response(response)
+
+class SignalInputDialog(gtk.MessageDialog):
+ def __init__(self, parent, text, field, addr):
+ """
+ Create a gtk signal selection dialog.
+
+ @param parent: the parent window this dialog should stay over.
+ @param text: the title of the dialog.
+ @param field: the field description of the dialog.
+ @param addr: the widget address from which to fetch signal list.
+ """
+ gtk.MessageDialog.__init__(self, parent,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_QUESTION,
+ gtk.BUTTONS_OK,
+ None)
+ self.set_markup(text)
+ self.model = gtk.ListStore(str)
+ widget = gtkutils.find_widget(parent, addr)
+ for signal_name in gobject.signal_list_names(widget):
+ self.model.append(row=(signal_name,))
+ self.entry = gtk.ComboBox(self.model)
+ cell = gtk.CellRendererText()
+ self.entry.pack_start(cell)
+ self.entry.add_attribute(cell, 'text', 0)
+ hbox = gtk.HBox()
+ lbl = gtk.Label(field)
+ hbox.pack_start(lbl, False)
+ hbox.pack_end(self.entry)
+ self.vbox.pack_end(hbox, True, True)
+ self.show_all()
+
+ def pop(self):
+ """
+ Show the dialog. It will run in it's own loop and return control
+ to the caller when a signal has been selected.
+
+ @returns: a signal name or None if no signal was selected
+ """
+ self.run()
+ self.hide()
+ iter = self.entry.get_active_iter()
+ if iter:
+ text = self.model.get_value(iter, 0)
+ return text
+ return None
+
+ def _dialog_done_cb(self, entry, response):
+ self.response(response)
+
+class WidgetSelector(object):
+ """
+ Allow selecting a widget from within a window without interrupting the
+ flow of the current call.
+
+ The selector will run on the specified window until either a widget
+ is selected or abort() gets called.
+ """
+ def __init__(self, window):
+ super(WidgetSelector, self).__init__()
+ self.window = window
+ self._intro_mask = None
+ self._intro_handle = None
+ self._select_handle = None
+ self._prelight = None
+
+ def select(self):
+ """
+ Starts selecting a widget, by grabbing control of the mouse and
+ highlighting hovered widgets until one is clicked.
+ @returns: a widget address or None
+ """
+ if not self._intro_mask:
+ self._prelight = None
+ self._intro_mask = overlayer.Mask(catch_events=True)
+ self._select_handle = self._intro_mask.connect_after(
+ "button-press-event", self._end_introspect)
+ self._intro_handle = self._intro_mask.connect_after(
+ "motion-notify-event", self._intro_cb)
+ self.window._overlayer.put(self._intro_mask, 0, 0)
+ self.window._overlayer.queue_draw()
+
+ while bool(self._intro_mask) and not gtk.main_iteration():
+ pass
+
+ return gtkutils.raddr_lookup(self._prelight)
+
+ def _end_introspect(self, widget, evt):
+ if evt.type == gtk.gdk.BUTTON_PRESS and self._prelight:
+ self._intro_mask.catch_events = False
+ self._intro_mask.disconnect(self._intro_handle)
+ self._intro_handle = None
+ self._intro_mask.disconnect(self._select_handle)
+ self._select_handle = None
+ self.window._overlayer.remove(self._intro_mask)
+ self._intro_mask = None
+ # for some reason, gtk may not redraw after this unless told to.
+ self.window.queue_draw()
+
+ def _intro_cb(self, widget, evt):
+ """
+ Callback for capture of widget events, when in introspect mode.
+ """
+ # widget has focus, let's hilight it
+ win = gtk.gdk.display_get_default().get_window_at_pointer()
+ if not win:
+ return
+ click_wdg = win[0].get_user_data()
+ if not click_wdg.is_ancestor(self.window._overlayer):
+ # as popups are not (yet) supported, it would break
+ # badly if we were to play with a widget not in the
+ # hierarchy.
+ return
+ for hole in self._intro_mask.pass_thru:
+ self._intro_mask.mask(hole)
+ self._intro_mask.unmask(click_wdg)
+ self._prelight = click_wdg
+
+ self.window.queue_draw()
+
+ def abort(self):
+ """
+ Ends the selection. The control will return to the select() caller
+ with a return value of None, as selection was aborted.
+ """
+ self._intro_mask.catch_events = False
+ self._intro_mask.disconnect(self._intro_handle)
+ self._intro_handle = None
+ self._intro_mask.disconnect(self._select_handle)
+ self._select_handle = None
+ self.window._overlayer.remove(self._intro_mask)
+ self._intro_mask = None
+ self._prelight = None
+
+###########################################################################
+# Property Widget Classes
+###########################################################################
+class PropWidget(object):
+ """
+ Base Class for property editing widgets.
+ Subclasses should implement create_widget, run_dialog and refresh_widget
+ """
+ def __init__(self, parent, edit_object, prop_name, changed_callback=None):
+ """Constructor
+ @param parent parent widget
+ @param edit_object TPropContainer being edited
+ @param prop_name name of property being edited
+ @param changed_callback optional callable to call on value changes
+ """
+ self._parent = parent
+ self._edit_object = edit_object
+ self._propname = prop_name
+ self._widget = None
+ self._changed_cb = changed_callback
+
+ ############################################################
+ # Begin Properties
+ ############################################################
+ def set_objprop(self, value):
+ """Setter for object property value"""
+ setattr(self._edit_object, self._propname, value)
+ def get_objprop(self):
+ """Getter for object property value"""
+ return getattr(self._edit_object, self._propname)
+ def _get_widget(self):
+ """Getter for widget. Creates the widget if necessary"""
+ if self._widget is None:
+ self._widget = self.create_widget(self.obj_prop)
+ return self._widget
+ def _get_prop_class(self):
+ """Getter for property type"""
+ return getattr(type(self._edit_object), self._propname)
+ def _get_parent(self):
+ """Getter for parent"""
+ return self._parent
+
+ obj_prop = property(get_objprop, set_objprop)
+ widget = property(_get_widget)
+ prop_class = property(_get_prop_class)
+ parent = property(_get_parent)
+
+ ############################################################
+ # End Properties
+ ############################################################
+
+ def notify(self):
+ """Notify a calling object that the property was changed"""
+ if self._changed_cb:
+ self._changed_cb()
+
+ ############################################################
+ # Public Interface -- Redefine those function in subclasses
+ ############################################################
+ def create_widget(self, init_value=None):
+ """
+ Create the Edit Widget for a property
+ @param init_value initial value
+ @return gtk.Widget
+ """
+ widget = gtk.Entry()
+ widget.set_text(str(init_value or ""))
+ return widget
+
+ @classmethod
+ def run_dialog(cls, parent, obj_prop, propname):
+ """
+ Class Method.
+ Prompts the user for changing an object's property
+ @param parent widget
+ @param obj_prop TPropContainer to edit
+ @param propname name of property to edit
+ """
+ raise NotImplementedError()
+
+ def refresh_widget(self):
+ """
+ Force the widget to update it's value in case the property has changed
+ """
+ pass
+
+class StringPropWidget(PropWidget):
+ """
+ Allows editing a str property
+ """
+ @classmethod
+ def _extract_value(cls, widget):
+ """
+ Class Method
+ extracts the value from the widget
+ """
+ buf = widget.get_buffer()
+ return cls._from_text(
+ buf.get_text(buf.get_start_iter(), buf.get_end_iter())
+ )
+
+ @classmethod
+ def _from_text(cls, text):
+ """
+ Class Method
+ transforms the text value into the correct type if required
+ """
+ return text
+
+ def _text_changed(self, widget, evt):
+ """callback for text change event in the edit box"""
+ self.obj_prop = self._extract_value(widget)
+ self.notify()
+
+ def create_widget(self, init_value=None):
+ """
+ Create the Edit Widget for a property
+ @param init_value initial value
+ @return gtk.Widget
+ """
+ propwdg = gtk.TextView()
+ propwdg.get_buffer().set_text(init_value or "")
+ propwdg.connect_after("focus-out-event", \
+ self._text_changed)
+
+ return propwdg
+
+ def refresh_widget(self):
+ """
+ Force the widget to update it's value in case the property has changed
+ """
+ self.widget.get_buffer().set_text(str(self.obj_prop)) #unicode() ?
+
+ @classmethod
+ def run_dialog(cls, parent, obj_prop, propname):
+ """
+ Class Method.
+ Prompts the user for changing an object's property
+ @param parent widget
+ @param obj_prop TPropContainer to edit
+ @param propname name of property to edit
+ """
+ dlg = TextInputDialog(parent,
+ text="Mandatory property",
+ field=propname)
+ setattr(obj_prop, propname, cls._from_text(dlg.pop()))
+
+class IntPropWidget(StringPropWidget):
+ """
+ Allows editing an int property with boundaries
+ """
+ @classmethod
+ def _extract_value(cls, widget):
+ """
+ Class Method
+ extracts the value from the widget
+ """
+ return widget.get_value_as_int()
+
+ @classmethod
+ def _from_text(cls, text):
+ """
+ Class Method
+ transforms the text value into the correct type if required
+ """
+ return int(text)
+
+ def create_widget(self, init_value=None):
+ """
+ Create the Edit Widget for a property
+ @param init_value initial value
+ @return gtk.Widget
+ """
+ prop = self.prop_class
+ adjustment = gtk.Adjustment(value=self.obj_prop,
+ lower=prop.lower_limit.limit,
+ upper=prop.upper_limit.limit,
+ step_incr=1)
+ propwdg = gtk.SpinButton(adjustment=adjustment)
+ propwdg.connect_after("focus-out-event", \
+ self._text_changed)
+
+class FloatPropWidget(StringPropWidget):
+ """Allows editing a float property"""
+ @classmethod
+ def _from_text(cls, text):
+ """
+ Class Method
+ transforms the text value into the correct type if required
+ """
+ return float(text)
+
+class UAMPropWidget(PropWidget):
+ """Allows editing an UAM property with a widget chooser"""
+ def _show_uam_chooser(self, widget):
+ """show the UAM chooser"""
+ selector = WidgetSelector(self.parent)
+ self.obj_prop = selector.select()
+ self.notify()
+
+ def create_widget(self, init_value=None):
+ """
+ Create the Edit Widget for a property
+ @param init_value initial value
+ @return gtk.Widget
+ """
+ propwdg = gtk.Button(self.obj_prop)
+ propwdg.connect_after("clicked", self._show_uam_chooser)
+ return propwdg
+
+ def refresh_widget(self):
+ """
+ Force the widget to update it's value in case the property has changed
+ """
+ self.widget.set_label(self.obj_prop)
+
+ @classmethod
+ def run_dialog(cls, parent, obj_prop, propname):
+ """
+ Class Method.
+ Prompts the user for changing an object's property
+ @param parent widget
+ @param obj_prop TPropContainer to edit
+ @param propname name of property to edit
+ """
+ selector = WidgetSelector(parent)
+ value = selector.select()
+ setattr(obj_prop, propname, selector.select())
+
+class EventTypePropWidget(PropWidget):
+ """Allows editing an EventType property"""
+ def refresh_widget(self):
+ """
+ Force the widget to update it's value in case the property has changed
+ """
+ self.widget.set_text(str(self.obj_prop))
+
+ @classmethod
+ def run_dialog(cls, parent, obj_prop, propname):
+ """
+ Class Method.
+ Prompts the user for changing an object's property
+ @param parent widget
+ @param obj_prop TPropContainer to edit
+ @param propname name of property to edit
+ """
+ try:
+ dlg = SignalInputDialog(parent,
+ text="Mandatory property",
+ field=propname,
+ addr=obj_prop.object_id)
+ setattr(obj_prop, propname, dlg.pop())
+ except AttributeError:
+ return
+
+class IntArrayPropWidget(PropWidget):
+ """Allows editing an array of ints property"""
+ def _item_changed(self, widget, evt, idx):
+ """callback for text changed in one of the entries"""
+ try:
+ #Save props as tuples so that they can be hashed
+ attr = list(self.obj_prop)
+ attr[idx] = int(widget.get_text())
+ self.obj_prop = tuple(attr)
+ except ValueError:
+ widget.set_text(str(self.obj_prop[idx]))
+ self.notify()
+
+ def create_widget(self, init_value=None):
+ """
+ Create the Edit Widget for a property
+ @param init_value initial value
+ @return gtk.Widget
+ """
+ value = self.obj_prop
+ propwdg = gtk.HBox()
+ for i in xrange(len(value)):
+ entry = gtk.Entry()
+ entry.set_text(str(value[i]))
+ propwdg.pack_start(entry)
+ entry.connect_after("focus-out-event", \
+ self._item_changed, i)
+ return propwdg
+
+ def refresh_widget(self):
+ """
+ Force the widget to update it's value in case the property has changed
+ """
+ children = self.widget.get_children()
+ value = self.obj_prop
+ for i in xrange(len(value)):
+ children[i].set_text(str(value[i]))
+
+ @classmethod
+ def run_dialog(cls, parent, obj_prop, propname):
+ """
+ Class Method.
+ Prompts the user for changing an object's property
+ @param parent widget
+ @param obj_prop TPropContainer to edit
+ @param propname name of property to edit
+ """
+ pass