From b2a294302e5cd72ec41b5471f3f8dc5da9abff66 Mon Sep 17 00:00:00 2001
From: Spencer Ross <brashendeavours@gmail.com>
Date: Thu, 17 Aug 2017 19:41:04 -0400
Subject: Fixes #1297, adds grc gui_hint collision detection

---
 grc/core/Param.py | 128 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 106 insertions(+), 22 deletions(-)

(limited to 'grc/core/Param.py')

diff --git a/grc/core/Param.py b/grc/core/Param.py
index 00c9e7d827..6c2c83fda8 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -533,37 +533,121 @@ class Param(Element):
         # GUI Position/Hint
         #########################
         elif t == 'gui_hint':
-            if ':' in v:
-                tab, pos = v.split(':')
-            elif '@' in v:
-                tab, pos = v, ''
-            else:
-                tab, pos = '', v
-
-            if '@' in tab:
-                tab, index = tab.split('@')
-            else:
-                index = '?'
-
-            # TODO: Problem with this code. Produces bad tabs
-            widget_str = ({
-                (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)',
-                (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)',
-                (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)',
-                (False, False): 'self.top_layout.addWidget(%(widget)s)',
-            }[bool(tab), bool(pos)]) % {'tab': tab, 'index': index, 'widget': '%s', 'pos': pos}
-
+        
             # FIXME: Move replace(...) into the make template of the qtgui blocks
             # Return a string here
             class GuiHint(object):
                 def __init__(self, ws):
                     self._ws = ws
-
+ 
                 def __call__(self, w):
                     return (self._ws.replace('addWidget', 'addLayout') if 'layout' in w else self._ws) % w
-
+ 
                 def __str__(self):
                     return self._ws
+                    
+            if (self.get_parent().get_state() == Constants.BLOCK_DISABLED):
+                return GuiHint('')
+                                    
+            # Parsing
+            if ':' in v:                
+                tab, pos = v.split(':')
+            elif '@' in v:              
+                tab, pos = v, ''        
+            else:
+                tab, pos = '', v        
+ 
+            if '@' in tab:
+                tab, index = tab.split('@')
+            else:
+                index = '0'
+               
+            position_set = bool(pos)
+            tab_set = bool(tab)
+            
+            # Validation
+            if position_set:
+                e = self.get_parent().get_parent().evaluate(pos)
+               
+                if len(e) == 2:
+                    e = e + (1,1)    
+                row, col, row_span, col_span = e
+               
+                if not isinstance(e, (list, tuple)) or not (len(e) == 2 or len(e) == 4) or not all([isinstance(ei, int) for ei in e]):
+                    raise Exception('Invalid GUI Hint entered: {e!s:s} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
+ 
+                if (row < 0) or (col < 0):
+                    raise Exception('Invalid GUI Hint entered: {e!s:s} (non-negative integers only).'.format(e=e))
+                   
+                if (row_span < 1) or (col_span < 1):
+                    raise Exception('Invalid GUI Hint entered: {e!s:s} (positive row/column span required).'.format(e=e))
+
+            if tab_set:
+                enabled_blocks = self.get_parent().get_parent().get_enabled_blocks()
+                tab_list = filter(lambda block: str(block.get_key()) == 'qtgui_tab_widget', enabled_blocks)
+                tab_match = filter(lambda t: t.get_id() == tab, tab_list) 
+                if not tab_match:
+                    raise Exception('Invalid tab name entered: {tab!s:s} (Tab name not found).'.format(tab=tab))
+                
+                tab_index_size = tab_match[0].get_param('num_tabs').get_value()
+                if index >= tab_index_size:
+                    raise Exception('Invalid tab index entered: {tab!s:s}@{index!s:s} (Index out of range).'.format(tab=tab,
+                                                                                                                    index=index))   
+                        
+            # Code Generation        
+            if position_set and tab_set:
+                widget_str = 'self.{tab:s}_grid_layout_{index:s}.addWidget({widget:s}, {e!s:s})'
+                widget_str += '\n[self.{tab:s}_grid_layout_{index:s}.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
+                widget_str += '\n[self.{tab:s}_grid_layout_{index:s}.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
+                widget_str = widget_str.format(tab=tab,
+                                               index=index,
+                                               widget='%s',
+                                               e=str(e)[1:-1],
+                                               fRow=row,
+                                               lRow=(row + row_span),
+                                               fCol=col,
+                                               lCol=(col + col_span))
+ 
+            elif position_set and not tab_set:
+                widget_str = 'self.top_grid_layout.addWidget({widget:s}, {e!s:s})'
+                widget_str += '\n[self.top_grid_layout.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
+                widget_str += '\n[self.top_grid_layout.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
+                widget_str = widget_str.format(widget='%s',
+                                               e=str(e)[1:-1],
+                                               fRow=row,
+                                               lRow=(row + row_span),
+                                               fCol=col,
+                                               lCol=(col + col_span))
+               
+            elif not position_set and tab_set:
+                widget_str = 'self.{tab:s}_layout_{index:s}.addWidget({widget:s})'
+                widget_str = widget_str.format(tab=tab,
+                                               index=index,
+                                               widget='%s')
+                 
+            elif not position_set and not tab_set:
+                widget_str = 'self.top_layout.addWidget({widget:s})'
+                widget_str = widget_str.format(widget='%s')
+
+            # Collision Detection
+            if position_set:
+                if tab_set:
+                    my_parent = '{tab:s}@{index:s}'.format(tab=tab,
+                                                           index=index)
+                else:
+                    my_parent = 'main'                            
+                # Calculate hostage cells
+                for r in range(row, row + row_span):
+                    for c in range(col, col + col_span):
+                        self._hostage_cells.append((my_parent, (r, c)))
+                # Avoid collisions
+                params = filter(lambda p: p is not self, self.get_all_params('gui_hint'))
+                for param in params:
+                    for parent, cell in param._hostage_cells:
+                        if (parent, cell) in self._hostage_cells:
+                            raise Exception('Another graphical element is using parent "{parent!s:s}", cell "{cell!s:s}".'.format(parent=parent,
+                                                                                                                                  cell=cell))
+ 
             return GuiHint(widget_str)
         #########################
         # Grid Position Type
-- 
cgit v1.2.3


From 2b35d7086f9585bb18fff3456b9d37d027d0f901 Mon Sep 17 00:00:00 2001
From: Spencer Ross <brashendeavours@gmail.com>
Date: Sat, 19 Aug 2017 23:16:38 -0400
Subject: moves gui_hint logic into parsing function, removes GuiHint class
 from Param.py, cleans up gr-qtgui blocks <make> functions

---
 gr-qtgui/grc/qtgui_ber_sink_b.xml       |   2 +-
 gr-qtgui/grc/qtgui_check_box.xml        |   3 +-
 gr-qtgui/grc/qtgui_chooser.xml          |   3 +-
 gr-qtgui/grc/qtgui_const_sink_x.xml     |   3 +-
 gr-qtgui/grc/qtgui_edit_box_msg.xml     |   2 +-
 gr-qtgui/grc/qtgui_entry.xml            |   5 +-
 gr-qtgui/grc/qtgui_freq_sink_x.xml      |   3 +-
 gr-qtgui/grc/qtgui_histogram_sink_x.xml |   4 +-
 gr-qtgui/grc/qtgui_label.xml            |   3 +-
 gr-qtgui/grc/qtgui_number_sink.xml      |   3 +-
 gr-qtgui/grc/qtgui_push_button.xml      |   4 +-
 gr-qtgui/grc/qtgui_range.xml            |   3 +-
 gr-qtgui/grc/qtgui_sink_x.xml           |   2 +-
 gr-qtgui/grc/qtgui_tab_widget.xml       |   3 +-
 gr-qtgui/grc/qtgui_time_raster_x.xml    |   3 +-
 gr-qtgui/grc/qtgui_time_sink_x.xml      |   3 +-
 gr-qtgui/grc/qtgui_vector_sink_f.xml    |   3 +-
 gr-qtgui/grc/qtgui_waterfall_sink_x.xml |   3 +-
 grc/core/Param.py                       | 232 ++++++++++++++++----------------
 grc/core/generator/Generator.py         |   2 +-
 20 files changed, 154 insertions(+), 135 deletions(-)

(limited to 'grc/core/Param.py')

diff --git a/gr-qtgui/grc/qtgui_ber_sink_b.xml b/gr-qtgui/grc/qtgui_ber_sink_b.xml
index da3fc6ebf8..851502a774 100644
--- a/gr-qtgui/grc/qtgui_ber_sink_b.xml
+++ b/gr-qtgui/grc/qtgui_ber_sink_b.xml
@@ -47,7 +47,7 @@ for i in xrange($num_curves):
     self.$(id).set_line_alpha(i, alphas[i])
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
 
   <param>
     <name>esno</name>
diff --git a/gr-qtgui/grc/qtgui_check_box.xml b/gr-qtgui/grc/qtgui_check_box.xml
index 67c8848a22..a02c83e3cf 100644
--- a/gr-qtgui/grc/qtgui_check_box.xml
+++ b/gr-qtgui/grc/qtgui_check_box.xml
@@ -20,7 +20,8 @@ self._$(id)_choices_inv = dict((v,k) for k,v in self._$(id)_choices.iteritems())
 self._$(id)_callback = lambda i: Qt.QMetaObject.invokeMethod($(win), "setChecked", Qt.Q_ARG("bool", self._$(id)_choices_inv[i]))
 self._$(id)_callback(self.$id)
 $(win).stateChanged.connect(lambda i: self.set_$(id)(self._$(id)_choices[bool(i)]))
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
 	<callback>self.set_$(id)($value)</callback>
 	<callback>self._$(id)_callback($id)</callback>
 	<param>
diff --git a/gr-qtgui/grc/qtgui_chooser.xml b/gr-qtgui/grc/qtgui_chooser.xml
index f018354dd4..b91566ae74 100644
--- a/gr-qtgui/grc/qtgui_chooser.xml
+++ b/gr-qtgui/grc/qtgui_chooser.xml
@@ -86,7 +86,8 @@ self._$(id)_callback(self.$id)
 self._$(id)_button_group.buttonClicked[int].connect(
 	lambda i: self.set_$(id)(self._$(id)_options[i]))
 #end if
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
 	<callback>self.set_$(id)($value)</callback>
 	<callback>self._$(id)_callback($id)</callback>
 	<param>
diff --git a/gr-qtgui/grc/qtgui_const_sink_x.xml b/gr-qtgui/grc/qtgui_const_sink_x.xml
index 755f12f964..6bf31d5190 100644
--- a/gr-qtgui/grc/qtgui_const_sink_x.xml
+++ b/gr-qtgui/grc/qtgui_const_sink_x.xml
@@ -51,7 +51,8 @@ for i in xrange(#if $type.t == 'message' then 1 else $nconnections#):
     self.$(id).set_line_alpha(i, alphas[i])
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_resize($width, $height)</callback>
   <callback>set_update_time($update_time)</callback>
   <callback>set_title($which, $title)</callback>
diff --git a/gr-qtgui/grc/qtgui_edit_box_msg.xml b/gr-qtgui/grc/qtgui_edit_box_msg.xml
index c7c758a612..276cfe9801 100644
--- a/gr-qtgui/grc/qtgui_edit_box_msg.xml
+++ b/gr-qtgui/grc/qtgui_edit_box_msg.xml
@@ -13,7 +13,7 @@
   <make>#set $win = 'self._%s_win'%$id
 qtgui.edit_box_msg($(type.t), $value, $label, $is_pair, $is_static, $key)
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
 
   <param>
     <name>Type</name>
diff --git a/gr-qtgui/grc/qtgui_entry.xml b/gr-qtgui/grc/qtgui_entry.xml
index cce2edb4b2..ab35aec858 100644
--- a/gr-qtgui/grc/qtgui_entry.xml
+++ b/gr-qtgui/grc/qtgui_entry.xml
@@ -21,9 +21,12 @@ self._$(id)_line_edit = Qt.QLineEdit(str(self.$id))
 self._$(id)_tool_bar.addWidget(self._$(id)_line_edit)
 self._$(id)_line_edit.returnPressed.connect(
 	lambda: self.set_$(id)($(type.conv)(str(self._$(id)_line_edit.text().toAscii()))))
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
 	<callback>self.set_$(id)($value)</callback>
+
 	<callback>Qt.QMetaObject.invokeMethod(self._$(id)_line_edit, "setText", Qt.Q_ARG("QString", $(type.str)($id)))</callback>
+
 	<param>
 		<name>Label</name>
 		<key>label</key>
diff --git a/gr-qtgui/grc/qtgui_freq_sink_x.xml b/gr-qtgui/grc/qtgui_freq_sink_x.xml
index 009a184327..5c6595cb33 100644
--- a/gr-qtgui/grc/qtgui_freq_sink_x.xml
+++ b/gr-qtgui/grc/qtgui_freq_sink_x.xml
@@ -54,7 +54,8 @@ for i in xrange(#if $type.t == 'message' then 1 else $nconnections#):
     self.$(id).set_line_alpha(i, alphas[i])
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_frequency_range($fc, $bw)</callback>
   <callback>set_update_time($update_time)</callback>
   <callback>set_title($which, $title)</callback>
diff --git a/gr-qtgui/grc/qtgui_histogram_sink_x.xml b/gr-qtgui/grc/qtgui_histogram_sink_x.xml
index a789d2e4fa..8bf9662b74 100644
--- a/gr-qtgui/grc/qtgui_histogram_sink_x.xml
+++ b/gr-qtgui/grc/qtgui_histogram_sink_x.xml
@@ -53,8 +53,8 @@ for i in xrange(#if $type.t == 'message' then 1 else $nconnections#):
     self.$(id).set_line_alpha(i, alphas[i])
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))
-  </make>
+$(gui_hint() % $win)</make>
+
   <callback>set_update_time($update_time)</callback>
   <callback>set_title($which, $title)</callback>
   <callback>set_color($which, $color)</callback>
diff --git a/gr-qtgui/grc/qtgui_label.xml b/gr-qtgui/grc/qtgui_label.xml
index d67f3d7500..90b77fa968 100644
--- a/gr-qtgui/grc/qtgui_label.xml
+++ b/gr-qtgui/grc/qtgui_label.xml
@@ -26,8 +26,7 @@ else:
 $(win).addWidget(Qt.QLabel($label+": "))
 self._$(id)_label = Qt.QLabel(str(self._$(id)_formatter(self.$id)))
 self._$(id)_tool_bar.addWidget(self._$(id)_label)
-$(gui_hint()($win))
-  </make>
+$(gui_hint() % $win)</make>
 
   <callback>self.set_$(id)(self._$(id)_formatter($value))</callback>
   <callback>Qt.QMetaObject.invokeMethod(self._$(id)_label, "setText", Qt.Q_ARG("QString", $id))</callback>
diff --git a/gr-qtgui/grc/qtgui_number_sink.xml b/gr-qtgui/grc/qtgui_number_sink.xml
index d10b7325c0..264baae667 100644
--- a/gr-qtgui/grc/qtgui_number_sink.xml
+++ b/gr-qtgui/grc/qtgui_number_sink.xml
@@ -41,7 +41,8 @@ for i in xrange($nconnections):
 
 self.$(id).enable_autoscale($autoscale)
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_update_time($update_time)</callback>
 
   <param_tab_order>
diff --git a/gr-qtgui/grc/qtgui_push_button.xml b/gr-qtgui/grc/qtgui_push_button.xml
index a535bb6473..e4f598e725 100644
--- a/gr-qtgui/grc/qtgui_push_button.xml
+++ b/gr-qtgui/grc/qtgui_push_button.xml
@@ -18,8 +18,10 @@ $win = Qt.QPushButton($label)
 self._$(id)_choices = {'Pressed': $pressed, 'Released': $released}
 $(win).pressed.connect(lambda: self.set_$(id)(self._$(id)_choices['Pressed']))
 $(win).released.connect(lambda: self.set_$(id)(self._$(id)_choices['Released']))
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
 	<callback>self.set_$(id)($value)</callback>
+
 	<param>
 		<name>Label</name>
 		<key>label</key>
diff --git a/gr-qtgui/grc/qtgui_range.xml b/gr-qtgui/grc/qtgui_range.xml
index 71b614cc5e..0999311a0b 100644
--- a/gr-qtgui/grc/qtgui_range.xml
+++ b/gr-qtgui/grc/qtgui_range.xml
@@ -17,7 +17,8 @@
 #end if
 $(range) = Range($start, $stop, $step, $value, $min_len)
 $(win) = RangeWidget($range, self.set_$(id), $label, "$widget", $rangeType)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>self.set_$(id)($value)</callback>
 
   <param>
diff --git a/gr-qtgui/grc/qtgui_sink_x.xml b/gr-qtgui/grc/qtgui_sink_x.xml
index 7488ea59d8..4dbf0e6375 100644
--- a/gr-qtgui/grc/qtgui_sink_x.xml
+++ b/gr-qtgui/grc/qtgui_sink_x.xml
@@ -25,7 +25,7 @@ qtgui.$(type.fcn)(
 )
 self.$(id).set_update_time(1.0/$rate)
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))
+$(gui_hint() % $win)
 
 self.$(id).enable_rf_freq($showrf)
 
diff --git a/gr-qtgui/grc/qtgui_tab_widget.xml b/gr-qtgui/grc/qtgui_tab_widget.xml
index 1bbadc8e51..fc8ad0553a 100644
--- a/gr-qtgui/grc/qtgui_tab_widget.xml
+++ b/gr-qtgui/grc/qtgui_tab_widget.xml
@@ -21,7 +21,8 @@ self.$(id)_grid_layout_$(i) = Qt.QGridLayout()
 self.$(id)_layout_$(i).addLayout(self.$(id)_grid_layout_$(i))
 $(win).addTab(self.$(id)_widget_$(i), $label)
 #end for
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
 	<param>
 		<name>Num Tabs</name>
 		<key>num_tabs</key>
diff --git a/gr-qtgui/grc/qtgui_time_raster_x.xml b/gr-qtgui/grc/qtgui_time_raster_x.xml
index 0359dc3b71..2d2f3cbff4 100644
--- a/gr-qtgui/grc/qtgui_time_raster_x.xml
+++ b/gr-qtgui/grc/qtgui_time_raster_x.xml
@@ -41,7 +41,8 @@ for i in xrange(#if $type.t == 'message' then 1 else $nconnections#):
     self.$(id).set_line_alpha(i, alphas[i])
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_num_rows($nrows)</callback>
   <callback>set_num_cols($ncols)</callback>
   <callback>set_multiplier($mult)</callback>
diff --git a/gr-qtgui/grc/qtgui_time_sink_x.xml b/gr-qtgui/grc/qtgui_time_sink_x.xml
index fbee4ca483..726e9fdf34 100644
--- a/gr-qtgui/grc/qtgui_time_sink_x.xml
+++ b/gr-qtgui/grc/qtgui_time_sink_x.xml
@@ -74,7 +74,8 @@ for i in xrange(#if $type.t == 'message' then 1 else $nconnections#):
 #end if
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_time_domain_axis($min, $max)</callback>
   <callback>set_update_time($update_time)</callback>
   <callback>set_title($which, $title)</callback>
diff --git a/gr-qtgui/grc/qtgui_vector_sink_f.xml b/gr-qtgui/grc/qtgui_vector_sink_f.xml
index 9f40d57729..1c17b8256a 100644
--- a/gr-qtgui/grc/qtgui_vector_sink_f.xml
+++ b/gr-qtgui/grc/qtgui_vector_sink_f.xml
@@ -46,7 +46,8 @@ for i in xrange($nconnections):
     self.$(id).set_line_alpha(i, alphas[i])
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_update_time($update_time)</callback>
   <callback>set_title($title)</callback>
   <callback>set_color($which, $color)</callback>
diff --git a/gr-qtgui/grc/qtgui_waterfall_sink_x.xml b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml
index cdecd5cce1..206318e30d 100644
--- a/gr-qtgui/grc/qtgui_waterfall_sink_x.xml
+++ b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml
@@ -47,7 +47,8 @@ for i in xrange(#if $type.t == 'message' then 1 else $nconnections#):
 self.$(id).set_intensity_range($int_min, $int_max)
 
 self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget)
-$(gui_hint()($win))</make>
+$(gui_hint() % $win)</make>
+
   <callback>set_frequency_range($fc, $bw)</callback>
   <callback>set_update_time($update_time)</callback>
   <callback>set_title($which, $title)</callback>
diff --git a/grc/core/Param.py b/grc/core/Param.py
index 6c2c83fda8..00336737b0 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -533,122 +533,10 @@ class Param(Element):
         # GUI Position/Hint
         #########################
         elif t == 'gui_hint':
-        
-            # FIXME: Move replace(...) into the make template of the qtgui blocks
-            # Return a string here
-            class GuiHint(object):
-                def __init__(self, ws):
-                    self._ws = ws
- 
-                def __call__(self, w):
-                    return (self._ws.replace('addWidget', 'addLayout') if 'layout' in w else self._ws) % w
- 
-                def __str__(self):
-                    return self._ws
-                    
             if (self.get_parent().get_state() == Constants.BLOCK_DISABLED):
-                return GuiHint('')
-                                    
-            # Parsing
-            if ':' in v:                
-                tab, pos = v.split(':')
-            elif '@' in v:              
-                tab, pos = v, ''        
-            else:
-                tab, pos = '', v        
- 
-            if '@' in tab:
-                tab, index = tab.split('@')
+                return ''
             else:
-                index = '0'
-               
-            position_set = bool(pos)
-            tab_set = bool(tab)
-            
-            # Validation
-            if position_set:
-                e = self.get_parent().get_parent().evaluate(pos)
-               
-                if len(e) == 2:
-                    e = e + (1,1)    
-                row, col, row_span, col_span = e
-               
-                if not isinstance(e, (list, tuple)) or not (len(e) == 2 or len(e) == 4) or not all([isinstance(ei, int) for ei in e]):
-                    raise Exception('Invalid GUI Hint entered: {e!s:s} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
- 
-                if (row < 0) or (col < 0):
-                    raise Exception('Invalid GUI Hint entered: {e!s:s} (non-negative integers only).'.format(e=e))
-                   
-                if (row_span < 1) or (col_span < 1):
-                    raise Exception('Invalid GUI Hint entered: {e!s:s} (positive row/column span required).'.format(e=e))
-
-            if tab_set:
-                enabled_blocks = self.get_parent().get_parent().get_enabled_blocks()
-                tab_list = filter(lambda block: str(block.get_key()) == 'qtgui_tab_widget', enabled_blocks)
-                tab_match = filter(lambda t: t.get_id() == tab, tab_list) 
-                if not tab_match:
-                    raise Exception('Invalid tab name entered: {tab!s:s} (Tab name not found).'.format(tab=tab))
-                
-                tab_index_size = tab_match[0].get_param('num_tabs').get_value()
-                if index >= tab_index_size:
-                    raise Exception('Invalid tab index entered: {tab!s:s}@{index!s:s} (Index out of range).'.format(tab=tab,
-                                                                                                                    index=index))   
-                        
-            # Code Generation        
-            if position_set and tab_set:
-                widget_str = 'self.{tab:s}_grid_layout_{index:s}.addWidget({widget:s}, {e!s:s})'
-                widget_str += '\n[self.{tab:s}_grid_layout_{index:s}.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
-                widget_str += '\n[self.{tab:s}_grid_layout_{index:s}.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
-                widget_str = widget_str.format(tab=tab,
-                                               index=index,
-                                               widget='%s',
-                                               e=str(e)[1:-1],
-                                               fRow=row,
-                                               lRow=(row + row_span),
-                                               fCol=col,
-                                               lCol=(col + col_span))
- 
-            elif position_set and not tab_set:
-                widget_str = 'self.top_grid_layout.addWidget({widget:s}, {e!s:s})'
-                widget_str += '\n[self.top_grid_layout.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
-                widget_str += '\n[self.top_grid_layout.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
-                widget_str = widget_str.format(widget='%s',
-                                               e=str(e)[1:-1],
-                                               fRow=row,
-                                               lRow=(row + row_span),
-                                               fCol=col,
-                                               lCol=(col + col_span))
-               
-            elif not position_set and tab_set:
-                widget_str = 'self.{tab:s}_layout_{index:s}.addWidget({widget:s})'
-                widget_str = widget_str.format(tab=tab,
-                                               index=index,
-                                               widget='%s')
-                 
-            elif not position_set and not tab_set:
-                widget_str = 'self.top_layout.addWidget({widget:s})'
-                widget_str = widget_str.format(widget='%s')
-
-            # Collision Detection
-            if position_set:
-                if tab_set:
-                    my_parent = '{tab:s}@{index:s}'.format(tab=tab,
-                                                           index=index)
-                else:
-                    my_parent = 'main'                            
-                # Calculate hostage cells
-                for r in range(row, row + row_span):
-                    for c in range(col, col + col_span):
-                        self._hostage_cells.append((my_parent, (r, c)))
-                # Avoid collisions
-                params = filter(lambda p: p is not self, self.get_all_params('gui_hint'))
-                for param in params:
-                    for parent, cell in param._hostage_cells:
-                        if (parent, cell) in self._hostage_cells:
-                            raise Exception('Another graphical element is using parent "{parent!s:s}", cell "{cell!s:s}".'.format(parent=parent,
-                                                                                                                                  cell=cell))
- 
-            return GuiHint(widget_str)
+                return self.parse_gui_hint(v)
         #########################
         # Grid Position Type
         #########################
@@ -837,3 +725,119 @@ class Param(Element):
         n['key'] = self.get_key()
         n['value'] = self.get_value()
         return n
+
+    ##############################################
+    # GUI Hint
+    ##############################################
+    def parse_gui_hint(self, v):
+        """
+        Parse/validate gui hint value.
+
+        Args:
+            v: gui_hint string from a block's 'gui_hint' param
+
+        Returns:
+            string of python code for positioning GUI elements in pyQT
+        """
+
+        # Parsing
+        if ':' in v:
+            tab, pos = v.split(':')
+        elif ',' in v:
+            tab, pos = '', v
+        else:
+            tab, pos = v, ''
+
+        if '@' in tab:
+            tab, index = tab.split('@')
+        else:
+            index = '0'
+        index = int(index)
+
+        position_set = bool(pos)
+        tab_set = bool(tab)
+
+        # Validation
+        if position_set:
+            e = self.get_parent().get_parent().evaluate(pos)
+
+            if len(e) == 2:
+                e = e + (1,1)
+            row, col, row_span, col_span = e
+
+            if not isinstance(e, (list, tuple)) or not (len(e) == 2 or len(e) == 4) or not all([isinstance(ei, int) for ei in e]):
+                raise Exception('Invalid GUI Hint entered: {e!s:s} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
+
+            if (row < 0) or (col < 0):
+                raise Exception('Invalid GUI Hint entered: {e!s:s} (non-negative integers only).'.format(e=e))
+
+            if (row_span < 1) or (col_span < 1):
+                raise Exception('Invalid GUI Hint entered: {e!s:s} (positive row/column span required).'.format(e=e))
+
+        if tab_set:
+            enabled_blocks = self.get_parent().get_parent().get_enabled_blocks()
+            tab_list = filter(lambda block: str(block.get_key()) == 'qtgui_tab_widget', enabled_blocks)
+            tab_match = filter(lambda t: t.get_id() == tab, tab_list)
+            if not tab_match:
+                raise Exception('Invalid tab name entered: {tab!s:s} (Tab name not found).'.format(tab=tab))
+
+            tab_index_size = int(tab_match[0].get_param('num_tabs').get_value())
+            if index >= tab_index_size:
+                raise Exception('Invalid tab index entered: {tab!s:s}@{index!s:s} (Index out of range).'.format(tab=tab,
+                                                                                                                index=index))
+
+        # Code Generation
+        if position_set and tab_set:
+            widget_str = 'self.{tab:s}_grid_layout_{index!s:s}.addWidget({widget:s}, {e!s:s})'
+            widget_str += '\n[self.{tab:s}_grid_layout_{index!s:s}.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
+            widget_str += '\n[self.{tab:s}_grid_layout_{index!s:s}.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
+            widget_str = widget_str.format(tab=tab,
+                                            index=index,
+                                            widget='%s',
+                                            e=str(e)[1:-1],
+                                            fRow=row,
+                                            lRow=(row + row_span),
+                                            fCol=col,
+                                            lCol=(col + col_span))
+
+        elif position_set and not tab_set:
+            widget_str = 'self.top_grid_layout.addWidget({widget:s}, {e!s:s})'
+            widget_str += '\n[self.top_grid_layout.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
+            widget_str += '\n[self.top_grid_layout.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
+            widget_str = widget_str.format(widget='%s',
+                                            e=str(e)[1:-1],
+                                            fRow=row,
+                                            lRow=(row + row_span),
+                                            fCol=col,
+                                            lCol=(col + col_span))
+
+        elif not position_set and tab_set:
+            widget_str = 'self.{tab:s}_layout_{index!s:s}.addWidget({widget:s})'
+            widget_str = widget_str.format(tab=tab,
+                                            index=index,
+                                            widget='%s')
+
+        elif not position_set and not tab_set:
+            widget_str = 'self.top_layout.addWidget({widget:s})'
+            widget_str = widget_str.format(widget='%s')
+
+        # Collision Detection
+        if position_set:
+            if tab_set:
+                my_parent = '{tab:s}@{index!s:s}'.format(tab=tab,
+                                                        index=index)
+            else:
+                my_parent = 'main'
+            # Calculate hostage cells
+            for r in range(row, row + row_span):
+                for c in range(col, col + col_span):
+                    self._hostage_cells.append((my_parent, (r, c)))
+            # Avoid collisions
+            params = filter(lambda p: p is not self, self.get_all_params('gui_hint'))
+            for param in params:
+                for parent, cell in param._hostage_cells:
+                    if (parent, cell) in self._hostage_cells:
+                        raise Exception('Another graphical element is using parent "{parent!s:s}", cell "{cell!s:s}".'.format(parent=parent,
+                                                                                                                                cell=cell))
+
+        return widget_str
\ No newline at end of file
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index 8c1cd9a6b0..2e639d4985 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -398,6 +398,6 @@ class QtHierBlockGenerator(HierBlockGenerator):
 
         block_n['make'] += (
             "\n#set $win = 'self.%s' % $id"
-            "\n${gui_hint()($win)}"
+            "\n${gui_hint() % $win}"
         )
         return n
-- 
cgit v1.2.3


From 5a8799f5bd477d5e8e520dd0dab1b8874db0e68e Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <sebastian.koslowski@gmail.com>
Date: Sat, 30 Sep 2017 21:11:09 +0200
Subject: [grc] refactor #1418

---
 grc/core/Param.py | 142 ++++++++++++++++++++++++++----------------------------
 1 file changed, 68 insertions(+), 74 deletions(-)

(limited to 'grc/core/Param.py')

diff --git a/grc/core/Param.py b/grc/core/Param.py
index 00336737b0..01697919d0 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 import ast
 import weakref
 import re
+import textwrap
 
 from . import Constants
 from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
@@ -212,7 +213,7 @@ class Param(Element):
             self._value = value or ''
         self._default = value
         self._init = False
-        self._hostage_cells = list()
+        self.hostage_cells = set()
         self.template_arg = TemplateArg(self)
 
     def get_types(self):
@@ -395,7 +396,6 @@ class Param(Element):
         self._init = True
         self._lisitify_flag = False
         self._stringify_flag = False
-        self._hostage_cells = list()
         t = self.get_type()
         v = self.get_value()
 
@@ -562,12 +562,12 @@ class Param(Element):
             # Calculate hostage cells
             for r in range(row_span):
                 for c in range(col_span):
-                    self._hostage_cells.append((my_parent, (row+r, col+c)))
+                    self.hostage_cells.append((my_parent, (row + r, col + c)))
             # Avoid collisions
             params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
             for param in params:
                 for parent, cell in param._hostage_cells:
-                    if (parent, cell) in self._hostage_cells:
+                    if (parent, cell) in self.hostage_cells:
                         raise Exception('Another graphical element is using parent "{0}", cell "{1}".'.format(str(parent), str(cell)))
             return e
         #########################
@@ -739,6 +739,7 @@ class Param(Element):
         Returns:
             string of python code for positioning GUI elements in pyQT
         """
+        self.hostage_cells.clear()
 
         # Parsing
         if ':' in v:
@@ -754,90 +755,83 @@ class Param(Element):
             index = '0'
         index = int(index)
 
-        position_set = bool(pos)
-        tab_set = bool(tab)
-
         # Validation
-        if position_set:
+        def parse_pos():
             e = self.get_parent().get_parent().evaluate(pos)
 
-            if len(e) == 2:
-                e = e + (1,1)
-            row, col, row_span, col_span = e
+            if not isinstance(e, (list, tuple)) or len(e) not in (2, 4) or not all(isinstance(ei, int) for ei in e):
+                raise Exception('Invalid GUI Hint entered: {e!r} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
 
-            if not isinstance(e, (list, tuple)) or not (len(e) == 2 or len(e) == 4) or not all([isinstance(ei, int) for ei in e]):
-                raise Exception('Invalid GUI Hint entered: {e!s:s} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
+            if len(e) == 2:
+                row, col = e
+                row_span = col_span = 1
+            else:
+                row, col, row_span, col_span = e
 
             if (row < 0) or (col < 0):
-                raise Exception('Invalid GUI Hint entered: {e!s:s} (non-negative integers only).'.format(e=e))
+                raise Exception('Invalid GUI Hint entered: {e!r} (non-negative integers only).'.format(e=e))
 
             if (row_span < 1) or (col_span < 1):
-                raise Exception('Invalid GUI Hint entered: {e!s:s} (positive row/column span required).'.format(e=e))
+                raise Exception('Invalid GUI Hint entered: {e!r} (positive row/column span required).'.format(e=e))
 
-        if tab_set:
-            enabled_blocks = self.get_parent().get_parent().get_enabled_blocks()
-            tab_list = filter(lambda block: str(block.get_key()) == 'qtgui_tab_widget', enabled_blocks)
-            tab_match = filter(lambda t: t.get_id() == tab, tab_list)
-            if not tab_match:
-                raise Exception('Invalid tab name entered: {tab!s:s} (Tab name not found).'.format(tab=tab))
+            return row, col, row_span, col_span
 
-            tab_index_size = int(tab_match[0].get_param('num_tabs').get_value())
-            if index >= tab_index_size:
-                raise Exception('Invalid tab index entered: {tab!s:s}@{index!s:s} (Index out of range).'.format(tab=tab,
-                                                                                                                index=index))
+        def validate_tab():
+            enabled_blocks = self.get_parent().get_parent().iter_enabled_blocks()
+            tabs = (block for block in enabled_blocks
+                    if block.get_key() == 'qtgui_tab_widget' and block.get_id() == tab)
+            tab_block = next(iter(tabs), None)
+            if not tab_block:
+                raise Exception('Invalid tab name entered: {tab} (Tab name not found).'.format(tab=tab))
 
-        # Code Generation
-        if position_set and tab_set:
-            widget_str = 'self.{tab:s}_grid_layout_{index!s:s}.addWidget({widget:s}, {e!s:s})'
-            widget_str += '\n[self.{tab:s}_grid_layout_{index!s:s}.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
-            widget_str += '\n[self.{tab:s}_grid_layout_{index!s:s}.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
-            widget_str = widget_str.format(tab=tab,
-                                            index=index,
-                                            widget='%s',
-                                            e=str(e)[1:-1],
-                                            fRow=row,
-                                            lRow=(row + row_span),
-                                            fCol=col,
-                                            lCol=(col + col_span))
-
-        elif position_set and not tab_set:
-            widget_str = 'self.top_grid_layout.addWidget({widget:s}, {e!s:s})'
-            widget_str += '\n[self.top_grid_layout.setRowStretch(r,1) for r in range({fRow:d},{lRow:d})]'
-            widget_str += '\n[self.top_grid_layout.setColumnStretch(c,1) for c in range({fCol:d},{lCol:d})]'
-            widget_str = widget_str.format(widget='%s',
-                                            e=str(e)[1:-1],
-                                            fRow=row,
-                                            lRow=(row + row_span),
-                                            fCol=col,
-                                            lCol=(col + col_span))
-
-        elif not position_set and tab_set:
-            widget_str = 'self.{tab:s}_layout_{index!s:s}.addWidget({widget:s})'
-            widget_str = widget_str.format(tab=tab,
-                                            index=index,
-                                            widget='%s')
-
-        elif not position_set and not tab_set:
-            widget_str = 'self.top_layout.addWidget({widget:s})'
-            widget_str = widget_str.format(widget='%s')
+            tab_index_size = int(tab_block.get_param('num_tabs').get_value())
+            if index >= tab_index_size:
+                raise Exception('Invalid tab index entered: {tab}@{index} (Index out of range).'.format(
+                    tab=tab, index=index))
 
         # Collision Detection
-        if position_set:
-            if tab_set:
-                my_parent = '{tab:s}@{index!s:s}'.format(tab=tab,
-                                                        index=index)
-            else:
-                my_parent = 'main'
+        def collision_detection(row, col, row_span, col_span):
+            my_parent = '{tab}@{index}'.format(tab=tab, index=index) if tab else 'main'
             # Calculate hostage cells
             for r in range(row, row + row_span):
                 for c in range(col, col + col_span):
-                    self._hostage_cells.append((my_parent, (r, c)))
-            # Avoid collisions
-            params = filter(lambda p: p is not self, self.get_all_params('gui_hint'))
-            for param in params:
-                for parent, cell in param._hostage_cells:
-                    if (parent, cell) in self._hostage_cells:
-                        raise Exception('Another graphical element is using parent "{parent!s:s}", cell "{cell!s:s}".'.format(parent=parent,
-                                                                                                                                cell=cell))
+                    self.hostage_cells.add((my_parent, (r, c)))
+
+            for other in self.get_all_params('gui_hint'):
+                if other is self:
+                    continue
+                collision = next(iter(self.hostage_cells & other.hostage_cells), None)
+                if collision:
+                    raise Exception('Block {block!r} is also using parent {parent!r}, cell {cell!r}.'.format(
+                        block=other.get_parent().get_id(), parent=collision[0], cell=collision[1]
+                    ))
+
+        # Code Generation
+        if tab:
+            validate_tab()
+            layout = '{tab}_grid_layout_{index}'.format(tab=tab, index=index)
+        else:
+            layout = 'top_grid_layout'
+
+        widget = '%s'  # to be fill-out in the mail template
+
+        if pos:
+            row, col, row_span, col_span = parse_pos()
+            collision_detection(row, col, row_span, col_span)
+
+            widget_str = textwrap.dedent("""
+                self.{layout}.addWidget({widget}, {row}, {col}, {row_span}, {col_span})
+                for r in range({row}, {row_end}):
+                    self.{layout}.setRowStretch(r, 1)
+                for c in range({col}, {col_end}):
+                    self.{layout}.setColumnStretch(c, 1)
+            """.strip('\n')).format(
+                layout=layout, widget=widget,
+                row=row, row_span=row_span, row_end=row+row_span,
+                col=col, col_span=col_span, col_end=col+col_span,
+            )
+
+        else:
+            widget_str = 'self.{layout}.addWidget({widget})'.format(layout=layout, widget=widget)
 
-        return widget_str
\ No newline at end of file
+        return widget_str
-- 
cgit v1.2.3