Rework the params/properties dialog and param gui class:
authorJosh Blum <josh@joshknows.com>
Sun, 6 Sep 2009 06:41:49 +0000 (23:41 -0700)
committerJosh Blum <josh@joshknows.com>
Sun, 6 Sep 2009 06:41:49 +0000 (23:41 -0700)
Better handles dynamic changes and subsequent code cleanup.

grc/gui/Param.py
grc/gui/PropsDialog.py
grc/todo.txt

index 5cc8d9c7f3c70f2d89527514ff2ea93f678074b2..3c5e99e9ebe10e68f94defd2e322a56c4a1334d8 100644 (file)
@@ -26,10 +26,10 @@ import gtk
 class InputParam(gtk.HBox):
        """The base class for an input parameter inside the input parameters dialog."""
 
-       def __init__(self, param, _handle_changed):
+       def __init__(self, param, callback=None):
                gtk.HBox.__init__(self)
                self.param = param
-               self._handle_changed = _handle_changed
+               self._callback = callback
                self.label = gtk.Label('') #no label, markup is added by set_markup
                self.label.set_size_request(150, -1)
                self.pack_start(self.label, False)
@@ -37,6 +37,34 @@ class InputParam(gtk.HBox):
                self.tp = None
        def set_color(self, color): pass
 
+       def update(self):
+               """
+               Set the markup, color, and tooltip.
+               """
+               #set the markup
+               has_cb = \
+                       hasattr(self.param.get_parent(), 'get_callbacks') and \
+                       filter(lambda c: self.param.get_key() in c, self.param.get_parent()._callbacks)
+               self.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self.param, has_cb=has_cb))
+               #set the color
+               self.set_color(self.param.get_color())
+               #set the tooltip
+               if self.tp: self.tp.set_tip(
+                       self.entry,
+                       Utils.parse_template(TIP_MARKUP_TMPL, param=self.param).strip(),
+               )
+
+       def _handle_changed(self, *args):
+               """
+               Handle a gui change by setting the new param value,
+               calling the callback (if applicable), and updating.
+               """
+               #set the new value
+               self.param.set_value(self.get_text())
+               #call the callback
+               if self._callback: self._callback()
+               #self.update() #dont update here, parent will update
+
 class EntryParam(InputParam):
        """Provide an entry box for strings and numbers."""
 
@@ -138,39 +166,10 @@ class Param(Element):
        def get_input_object(self, callback=None):
                """
                Get the graphical gtk object to represent this parameter.
-               Create the input object with this data type and the handle changed method.
-               @param callback a function of one argument(this param) to be called from the change handler
+               @param callback a function to be called from the input object. 
                @return gtk input object
                """
-               self._callback = callback
-               self._input = self.get_input_class()(self, self._handle_changed)
-               if not self._callback: self.update()
-               return self._input
-
-       def _handle_changed(self, widget=None):
-               """
-               When the input changes, write the inputs to the data type.
-               Finish by calling the exteral callback.
-               """
-               self.set_value(self._input.get_text())
-               self.validate()
-               #is param is involved in a callback? #FIXME: messy
-               has_cb = \
-                       hasattr(self.get_parent(), 'get_callbacks') and \
-                       filter(lambda c: self.get_key() in c, self.get_parent()._callbacks)
-               self._input.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self, has_cb=has_cb))
-               #hide/show
-               if self.get_hide() == 'all': self._input.hide_all()
-               else: self._input.show_all()
-               #set the color
-               self._input.set_color(self.get_color())
-               #set the tooltip
-               if self._input.tp: self._input.tp.set_tip(
-                       self._input.entry,
-                       Utils.parse_template(TIP_MARKUP_TMPL, param=self).strip(),
-               )
-               #execute the external callback
-               if self._callback: self._callback(self)
+               return self.get_input_class()(self, callback=callback)
 
        def get_layout(self):
                """
index 200cff1f53fdf5a2f11159a1e03ec12c1d09abf2..bd66b1178471c45abd597da61cc3d220467866b3 100644 (file)
@@ -38,13 +38,17 @@ def get_title_label(title):
        return hbox
 
 class PropsDialog(gtk.Dialog):
-       """A dialog box to set block parameters."""
+       """
+       A dialog to set block parameters, view errors, and view documentation.
+       """
 
        def __init__(self, block):
                """
-               SignalBlockParamsDialog contructor.
-               @param block the signal block
+               Properties dialog contructor.
+               @param block a block instance
                """
+               self._hash = ''
+               LABEL_SPACING = 7
                gtk.Dialog.__init__(self,
                        title='Properties: %s'%block.get_name(),
                        buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE),
@@ -52,46 +56,81 @@ class PropsDialog(gtk.Dialog):
                self.block = block
                self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT)
                vbox = gtk.VBox()
-               #Add the title label
-               vbox.pack_start(get_title_label('Parameters'), False)
                #Create the scrolled window to hold all the parameters
                scrolled_window = gtk.ScrolledWindow()
                scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                scrolled_window.add_with_viewport(vbox)
                self.vbox.pack_start(scrolled_window, True)
+               #Params box for block parameters
+               self._params_box = gtk.VBox()
+               self._params_box.pack_start(get_title_label('Parameters'), False)
+               self._input_object_params = list()
                #Error Messages for the block
                self._error_box = gtk.VBox()
                self._error_messages_text_display = TextDisplay()
-               self._error_box.pack_start(gtk.Label(), False, False, 7) #spacing
+               self._error_box.pack_start(gtk.Label(), False, False, LABEL_SPACING)
                self._error_box.pack_start(get_title_label('Error Messages'), False)
                self._error_box.pack_start(self._error_messages_text_display, False)
                #Docs for the block
                self._docs_box = err_box = gtk.VBox()
                self._docs_text_display = TextDisplay()
-               self._docs_box.pack_start(gtk.Label(), False, False, 7) #spacing
+               self._docs_box.pack_start(gtk.Label(), False, False, LABEL_SPACING)
                self._docs_box.pack_start(get_title_label('Documentation'), False)
                self._docs_box.pack_start(self._docs_text_display, False)
-               #Add all the parameters
-               for param in self.block.get_params():
-                       vbox.pack_start(param.get_input_object(self._handle_changed), False)
-               #Add the error and docs box
+               #Add the boxes
+               vbox.pack_start(self._params_box, False)
                vbox.pack_start(self._error_box, False)
                vbox.pack_start(self._docs_box, False)
-               #connect and show
+               #connect key press event
                self.connect('key_press_event', self._handle_key_press)
+               #initial update to populate the params
                self.show_all()
-               #initial update
-               for param in self.block.get_params(): param.update()
                self._update()
 
+       def _params_changed(self):
+               """
+               Have the params in this dialog changed?
+               Ex: Added, removed, type change, hidden, shown?
+               Make a hash that uniquely represents the params state.
+               @return true if changed
+               """
+               old_hash = self._hash
+               str_accum = ''
+               for param in self.block.get_params():
+                       str_accum += param.get_key()
+                       str_accum += param.get_type()
+                       str_accum += param.get_hide()
+               self._hash = hash(str_accum)
+               return self._hash != old_hash
+
        def _update(self):
                """
+               Repopulate the parameters box (if changed).
+               Update all the input parameters.
                Update the error messages box.
                Hide the box if there are no errors.
                Update the documentation block.
                Hide the box if there are no docs.
                """
+               #update for the block
+               self.block.rewrite()
                self.block.validate()
+               #update the params box
+               if self._params_changed():
+                       #empty the params box
+                       for io_param in list(self._input_object_params):
+                               self._params_box.remove(io_param)
+                               self._input_object_params.remove(io_param)
+                               io_param.destroy()
+                       #repopulate the params box
+                       for param in self.block.get_params():
+                               if param.get_hide() == 'all': continue
+                               io_param = param.get_input_object(self._update)
+                               self._input_object_params.append(io_param)
+                               self._params_box.pack_start(io_param, False)
+                       self._params_box.show_all()
+               #update the gui inputs
+               for io_param in self._input_object_params: io_param.update()
                #update the errors box
                if self.block.is_valid(): self._error_box.hide()
                else: self._error_box.show()
@@ -112,23 +151,6 @@ class PropsDialog(gtk.Dialog):
                if keyname == 'Return': self.response(gtk.RESPONSE_OK)
                return False #forward the keypress
 
-       def _handle_changed(self, param):
-               """
-               A change occured, update any dependent parameters:
-               The enum inside the variable type may have changed and,
-               the variable param will need an external update.
-               @param param the graphical parameter that initiated the callback
-               """
-               #update dependent params
-               if param.is_enum():
-                       for other_param in param.get_parent().get_params():
-                               if param.get_key() is not other_param.get_key() and (
-                               param.get_key() in other_param._type or \
-                               param.get_key() in other_param._hide): other_param.update()
-               #update
-               self._update()
-               return True
-
        def run(self):
                """
                Call run().
index 8afd2f14580bfc11ff8e7528911bba89bf8614b3..99351a912f198bc09ffbb8dc84c05934ded727de 100644 (file)
 * dont generate py files in saved flowgraph dir
 * save/restore cwd
 * threads dont die on exit in probe and variable sink
-* align param titles in paramsdialog
+* align param titles in properties dialog
 * weird grid params misbehaving
-* params dialog needs to dynamically update for all params
-  * will not update for non-enum params
-  * needs to account for added or removed params
-  * example with grid params need update after notebook change
-  * idea: hash the current param keys list and types,
-    if changed, redo the whole dialog (params part)
+* properties dialog needs to show connection errors
 
 ##################################################
 # Future