diff options
Diffstat (limited to 'grc')
-rw-r--r-- | grc/blocks/block_tree.xml | 11 | ||||
-rw-r--r-- | grc/blocks/epy_block.xml | 1 | ||||
-rw-r--r-- | grc/blocks/epy_module.xml | 1 | ||||
-rw-r--r-- | grc/blocks/options.xml | 6 | ||||
-rw-r--r-- | grc/blocks/parameter.xml | 6 | ||||
-rw-r--r-- | grc/blocks/variable_function_probe.xml | 18 | ||||
-rw-r--r-- | grc/blocks/variable_struct.xml.py | 1 | ||||
-rw-r--r-- | grc/core/Block.py | 8 | ||||
-rw-r--r-- | grc/core/Constants.py | 1 | ||||
-rw-r--r-- | grc/core/Element.pyi | 54 | ||||
-rw-r--r-- | grc/core/Platform.py | 76 | ||||
-rw-r--r-- | grc/core/generator/Generator.py | 35 | ||||
-rw-r--r-- | grc/core/generator/flow_graph.tmpl | 23 | ||||
-rw-r--r-- | grc/core/utils/expr_utils.py | 10 | ||||
-rw-r--r-- | grc/gui/ActionHandler.py | 5 | ||||
-rw-r--r-- | grc/gui/BlockTreeWindow.py | 82 | ||||
-rw-r--r-- | grc/gui/Constants.py | 7 | ||||
-rwxr-xr-x | grc/main.py | 13 | ||||
-rw-r--r-- | grc/test/.cache/v/cache/lastfailed | 3 |
19 files changed, 210 insertions, 151 deletions
diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml index a8775d6872..3125864d4d 100644 --- a/grc/blocks/block_tree.xml +++ b/grc/blocks/block_tree.xml @@ -1,11 +1,6 @@ <?xml version="1.0"?> -<!-- -################################################### -##Block Tree for platform gnuradio python. -################################################### - --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Misc</name> <block>pad_source</block> @@ -18,12 +13,16 @@ <block>bus_structure_sink</block> <block>bus_structure_source</block> + <block>epy_block</block> + <block>epy_module</block> + <block>note</block> <block>import</block> </cat> <cat> <name>Variables</name> <block>variable</block> + <block>variable_struct</block> <block>variable_config</block> <block>variable_function_probe</block> <block>parameter</block> diff --git a/grc/blocks/epy_block.xml b/grc/blocks/epy_block.xml index 3fd5aa84f1..65e78c4062 100644 --- a/grc/blocks/epy_block.xml +++ b/grc/blocks/epy_block.xml @@ -2,7 +2,6 @@ <block> <name>Python Block</name> <key>epy_block</key> - <category>Misc</category> <import></import> <make></make> <param><!-- Cache the last working block IO to keep FG sane --> diff --git a/grc/blocks/epy_module.xml b/grc/blocks/epy_module.xml index 6d6d71804c..fa3e5f91f4 100644 --- a/grc/blocks/epy_module.xml +++ b/grc/blocks/epy_module.xml @@ -2,7 +2,6 @@ <block> <name>Python Module</name> <key>epy_module</key> - <category>Misc</category> <import>import $id # embedded python module</import> <make></make> <param> diff --git a/grc/blocks/options.xml b/grc/blocks/options.xml index 937cfe82ea..55f411884d 100644 --- a/grc/blocks/options.xml +++ b/grc/blocks/options.xml @@ -20,8 +20,8 @@ from PyQt4 import Qt import sys #end if #if not $generate_options().startswith('hb') -from optparse import OptionParser -from gnuradio.eng_option import eng_option +from argparse import ArgumentParser +from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation #end if</import> <make></make> @@ -84,7 +84,7 @@ else: self.stop(); self.wait()</callback> <param> <name>Category</name> <key>category</key> - <value>Custom</value> + <value>[GRC Hier Blocks]</value> <type>string</type> <hide>#if $generate_options().startswith('hb') then 'none' else 'all'#</hide> </param> diff --git a/grc/blocks/parameter.xml b/grc/blocks/parameter.xml index e35b8f4d1d..b0713218fd 100644 --- a/grc/blocks/parameter.xml +++ b/grc/blocks/parameter.xml @@ -55,9 +55,11 @@ </option> <option> <name>String</name> - <key>string</key> + <key>str</key> <opt>type:string</opt> - </option> + </option> + <!-- Do not forget to add option value type handler import into + grc/python/flow_graph.tmpl for each new type. --> <!-- not supported yet in tmpl <option> <name>Boolean</name> diff --git a/grc/blocks/variable_function_probe.xml b/grc/blocks/variable_function_probe.xml index baa996c0ec..47c11b29fe 100644 --- a/grc/blocks/variable_function_probe.xml +++ b/grc/blocks/variable_function_probe.xml @@ -10,7 +10,7 @@ <import>import time</import> <import>import threading</import> <var_make>self.$(id) = $(id) = $value</var_make> - <make>#slurp + <make> def _$(id)_probe(): while True: #set $obj = 'self' + ('.' + $block_id() if $block_id() else '') @@ -22,15 +22,10 @@ def _$(id)_probe(): time.sleep(1.0 / ($poll_rate)) _$(id)_thread = threading.Thread(target=_$(id)_probe) _$(id)_thread.daemon = True -_$(id)_thread.start()</make> +_$(id)_thread.start() + </make> <callback>self.set_$(id)($value)</callback> <param> - <name>Value</name> - <key>value</key> - <value>0</value> - <type>raw</type> - </param> - <param> <name>Block ID</name> <key>block_id</key> <value>my_block_0</value> @@ -55,6 +50,13 @@ _$(id)_thread.start()</make> <value>10</value> <type>real</type> </param> + <param> + <name>Initial Value</name> + <key>value</key> + <value>0</value> + <type>raw</type> + <hide>part</hide> + </param> <doc> Periodically probe a function and set its value to this variable. diff --git a/grc/blocks/variable_struct.xml.py b/grc/blocks/variable_struct.xml.py index e43200828b..de4411e975 100644 --- a/grc/blocks/variable_struct.xml.py +++ b/grc/blocks/variable_struct.xml.py @@ -6,7 +6,6 @@ HEADER = """\ <block> <name>Struct Variable</name> <key>variable_struct</key> - <category>Variables</category> <import>def struct(data): return type('Struct', (object,), data)()</import> <var_make>self.$id = $id = struct({{#slurp #for $i in range({0}): diff --git a/grc/core/Block.py b/grc/core/Block.py index ff7f041dc0..9fff5afcb7 100644 --- a/grc/core/Block.py +++ b/grc/core/Block.py @@ -67,7 +67,7 @@ class Block(Element): self._name = n['name'] self._key = n['key'] - self._category = n.get('category', '') + self.category = [cat.strip() for cat in n.get('category', '').split('/') if cat.strip()] self._flags = n.get('flags', '') self._doc = n.get('doc', '').strip('\n').replace('\\\n', '') @@ -491,12 +491,6 @@ class Block(Element): def get_key(self): return self._key - def get_category(self): - return self._category - - def set_category(self, cat): - self._category = cat - def get_ports(self): return self.get_sources() + self.get_sinks() diff --git a/grc/core/Constants.py b/grc/core/Constants.py index 8a99f8b256..992d5e7d83 100644 --- a/grc/core/Constants.py +++ b/grc/core/Constants.py @@ -41,6 +41,7 @@ FLOW_GRAPH_FILE_FORMAT_VERSION = 1 # Param tabs DEFAULT_PARAM_TAB = "General" ADVANCED_PARAM_TAB = "Advanced" +DEFAULT_BLOCK_MODULE_NAME = '(no module specified)' # Port domains GR_STREAM_DOMAIN = "gr_stream" diff --git a/grc/core/Element.pyi b/grc/core/Element.pyi new file mode 100644 index 0000000000..c81180a33e --- /dev/null +++ b/grc/core/Element.pyi @@ -0,0 +1,54 @@ +# Copyright 2008, 2009, 2015, 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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. +# +# GNU Radio Companion 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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +from . import Platform, FlowGraph, Block + +def lazy_property(func): + return func + + +class Element(object): + + def __init__(self, parent=None): + ... + + @property + def parent(self): + ... + + def get_parent_by_type(self, cls): + parent = self.parent + if parent is None: + return None + elif isinstance(parent, cls): + return parent + else: + return parent.get_parent_by_type(cls) + + @lazy_property + def parent_platform(self): -> Platform.Platform + ... + + @lazy_property + def parent_flowgraph(self): -> FlowGraph.FlowGraph + ... + + @lazy_property + def parent_block(self): -> Block.Block + ... + + diff --git a/grc/core/Platform.py b/grc/core/Platform.py index 069870d389..be7b60ca59 100644 --- a/grc/core/Platform.py +++ b/grc/core/Platform.py @@ -69,7 +69,7 @@ class Platform(Element): self.blocks = {} self._blocks_n = {} - self._category_trees_n = [] + self._block_categories = {} self.domains = {} self.connection_templates = {} @@ -79,7 +79,8 @@ class Platform(Element): self._flow_graph = Element.__new__(FlowGraph) Element.__init__(self._flow_graph, self) self._flow_graph.connections = [] - self.load_blocks() + + self.build_block_library() def __str__(self): return 'Platform - {}({})'.format(self.config.key, self.config.name) @@ -137,14 +138,14 @@ class Platform(Element): self.load_block_xml(generator.file_path_xml) return True - def load_blocks(self): + def build_block_library(self): """load the blocks and block tree from the search paths""" self._docstring_extractor.start() # Reset self.blocks.clear() self._blocks_n.clear() - del self._category_trees_n[:] + self._block_categories.clear() self.domains.clear() self.connection_templates.clear() ParseXML.xml_failures.clear() @@ -162,9 +163,21 @@ class Platform(Element): # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file) pass except Exception as e: - raise print('Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file), file=sys.stderr) + # Add blocks to block tree + for key, block in six.iteritems(self.blocks): + category = self._block_categories.get(key, block.category) + # Blocks with empty categories are hidden + if not category: + continue + root = category[0] + if root.startswith('[') and root.endswith(']'): + category[0] = root[1:-1] + else: + category.insert(0, Constants.DEFAULT_BLOCK_MODULE_NAME) + block.category = category + self._docstring_extractor.finish() # self._docstring_extractor.wait() @@ -202,8 +215,19 @@ class Platform(Element): def load_category_tree_xml(self, xml_file): """Validate and parse category tree file and add it to list""" ParseXML.validate_dtd(xml_file, Constants.BLOCK_TREE_DTD) - n = ParseXML.from_file(xml_file).get('cat', {}) - self._category_trees_n.append(n) + xml = ParseXML.from_file(xml_file) + path = [] + + def load_category(cat_n): + path.append(cat_n.get('name').strip()) + for block_key in cat_n.get('block', []): + if block_key not in self._block_categories: + self._block_categories[block_key] = list(path) + for sub_cat_n in cat_n.get('cat', []): + load_category(sub_cat_n) + path.pop() + + load_category(xml.get('cat', {})) def load_domain_xml(self, xml_file): """Load a domain properties and connection templates from XML""" @@ -248,44 +272,6 @@ class Platform(Element): else: self.connection_templates[key] = connection_n.get('make') or '' - def load_block_tree(self, block_tree): - """ - Load a block tree with categories and blocks. - Step 1: Load all blocks from the xml specification. - Step 2: Load blocks with builtin category specifications. - - Args: - block_tree: the block tree object - """ - # Recursive function to load categories and blocks - def load_category(cat_n, parent=None): - # Add this category - parent = (parent or []) + [cat_n.get('name')] - block_tree.add_block(parent) - # Recursive call to load sub categories - for cat in cat_n.get('cat', []): - load_category(cat, parent) - # Add blocks in this category - for block_key in cat_n.get('block', []): - if block_key not in self.blocks: - print('Warning: Block key "{}" not found when loading category tree.'.format(block_key), file=sys.stderr) - continue - block = self.blocks[block_key] - # If it exists, the block's category shall not be overridden by the xml tree - if not block.get_category(): - block.set_category(parent) - - # Recursively load the category trees and update the categories for each block - for category_tree_n in self._category_trees_n: - load_category(category_tree_n) - - # Add blocks to block tree - for block in six.itervalues(self.blocks): - # Blocks with empty categories are hidden - if not block.get_category(): - continue - block_tree.add_block(block.get_category(), block) - def _save_docstring_extraction_result(self, key, docstrings): docs = {} for match, docstring in six.iteritems(docstrings): diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py index c3308d6c32..9c7d07e76b 100644 --- a/grc/core/generator/Generator.py +++ b/grc/core/generator/Generator.py @@ -144,16 +144,16 @@ class TopBlockGenerator(object): pass return code - blocks = expr_utils.sort_objects( + blocks_all = expr_utils.sort_objects( [b for b in fg.blocks if b.get_enabled() and not b.get_bypassed()], operator.methodcaller('get_id'), _get_block_sort_text ) - deprecated_block_keys = set(block.get_name() for block in blocks if block.is_deprecated) + deprecated_block_keys = set(b.get_name() for b in blocks_all if b.is_deprecated) for key in deprecated_block_keys: Messages.send_warning("The block {!r} is deprecated.".format(key)) # List of regular blocks (all blocks minus the special ones) - blocks = [b for b in blocks if b not in imports and b not in parameters] + blocks = [b for b in blocks_all if b not in imports and b not in parameters] for block in blocks: key = block.get_key() @@ -220,18 +220,20 @@ class TopBlockGenerator(object): # List of variable names var_ids = [var.get_id() for var in parameters + variables] - # Prepend self. - replace_dict = dict([(var_id, 'self.%s' % var_id) for var_id in var_ids]) - # List of callbacks - callbacks = [ - expr_utils.expr_replace(cb, replace_dict) - for cb in sum([block.get_callbacks() for block in fg.get_enabled_blocks()], []) - ] + replace_dict = dict((var_id, 'self.' + var_id) for var_id in var_ids) + callbacks_all = [] + for block in blocks_all: + callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_callbacks()) + # Map var id to callbacks - var_id2cbs = dict([ - (var_id, [c for c in callbacks if expr_utils.get_variable_dependencies(c, [var_id])]) - for var_id in var_ids - ]) + def uses_var_id(): + used = expr_utils.get_variable_dependencies(callback, [var_id]) + return used and 'self.' + var_id in callback # callback might contain var_id itself + + callbacks = {} + for var_id in var_ids: + callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id()] + # Load the namespace namespace = { 'title': title, @@ -245,7 +247,7 @@ class TopBlockGenerator(object): 'connection_templates': connection_templates, 'msgs': msgs, 'generate_options': self._generate_options, - 'var_id2cbs': var_id2cbs, + 'callbacks': callbacks, } # Build the template t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) @@ -265,8 +267,9 @@ class HierBlockGenerator(TopBlockGenerator): file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR) """ TopBlockGenerator.__init__(self, flow_graph, file_path) + platform = flow_graph.parent - hier_block_lib_dir = flow_graph.parent_platform.config.hier_block_lib_dir + hier_block_lib_dir = platform.config.hier_block_lib_dir if not os.path.exists(hier_block_lib_dir): os.mkdir(hier_block_lib_dir) diff --git a/grc/core/generator/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl index c86808455b..5eef3d2042 100644 --- a/grc/core/generator/flow_graph.tmpl +++ b/grc/core/generator/flow_graph.tmpl @@ -13,7 +13,7 @@ ##@param connections the connections ##@param msgs the msg type connections ##@param generate_options the type of flow graph -##@param var_id2cbs variable id map to callback strings +##@param callbacks variable id map to callback strings ######################################################## #def indent($code) #set $code = '\n '.join(str($code).splitlines()) @@ -301,12 +301,12 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) #if $flow_graph.get_option('thread_safe_setters') with self._lock: self.$id = $id - #for $callback in $var_id2cbs[$id] + #for $callback in $callbacks[$id] $indent($callback) #end for #else self.$id = $id - #for $callback in $var_id2cbs[$id] + #for $callback in $callbacks[$id] $indent($callback) #end for #end if @@ -336,19 +336,22 @@ $short_id#slurp def argument_parser(): - #set $desc_args = 'usage="%prog: [options]", option_class=eng_option' + #set $arg_parser_args = '' #if $flow_graph.get_option('description') - #set $desc_args += ', description=description' + #set $arg_parser_args = 'description=description' description = $repr($flow_graph.get_option('description')) #end if - parser = OptionParser($desc_args) + parser = ArgumentParser($arg_parser_args) #for $param in $parameters #set $type = $param.get_param('type').get_value() #if $type #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option( - "$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param), - help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") + parser.add_argument( + #if $make_short_id($param) + "$make_short_id($param)", #slurp + #end if + "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type=$type, default=$make_default($type, $param), + help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%(default)r]") #end if #end for return parser @@ -358,7 +361,7 @@ def argument_parser(): def main(top_block_cls=$(class_name), options=None): #if $parameters if options is None: - options, _ = argument_parser().parse_args() + options = argument_parser().parse_args() #end if #if $flow_graph.get_option('realtime_scheduling') if gr.enable_realtime_scheduling() != gr.RT_OK: diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py index 0577f06a75..555bd709b1 100644 --- a/grc/core/utils/expr_utils.py +++ b/grc/core/utils/expr_utils.py @@ -61,7 +61,7 @@ class graph(object): return self._graph[node_key] -def expr_split(expr): +def expr_split(expr, var_chars=VAR_CHARS): """ Split up an expression by non alphanumeric characters, including underscore. Leave strings in-tact. @@ -77,7 +77,7 @@ def expr_split(expr): tok = '' quote = '' for char in expr: - if quote or char in VAR_CHARS: + if quote or char in var_chars: if char == quote: quote = '' tok += char @@ -104,7 +104,7 @@ def expr_replace(expr, replace_dict): Returns: a new expression with the prepend """ - expr_splits = expr_split(expr) + expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.') for i, es in enumerate(expr_splits): if es in list(replace_dict.keys()): expr_splits[i] = replace_dict[es] @@ -195,7 +195,3 @@ def sort_objects(objects, get_id, get_expr): # Return list of sorted objects return [id2obj[id] for id in sorted_ids] - -if __name__ == '__main__': - for i in sort_variables({'x': '1', 'y': 'x+1', 'a': 'x+y', 'b': 'y+1', 'c': 'a+b+x+y'}): - print(i) diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index 8d4dc7841f..25c779b4d2 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -608,9 +608,8 @@ class ActionHandler: elif action == Actions.PAGE_CHANGE: # pass and run the global actions pass elif action == Actions.RELOAD_BLOCKS: - self.platform.load_blocks() - main.btwin.clear() - self.platform.load_block_tree(main.btwin) + self.platform.build_block_library() + main.btwin.repopulate() Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool( ParseXML.xml_failures)) Messages.send_xml_errors_if_any(ParseXML.xml_failures) diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py index 26086f58e9..89aac53a0e 100644 --- a/grc/gui/BlockTreeWindow.py +++ b/grc/gui/BlockTreeWindow.py @@ -27,7 +27,8 @@ from gi.repository import Gdk from gi.repository import GObject from . import Actions, Utils -from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH, DND_TARGETS +from . import Constants + NAME_INDEX, KEY_INDEX, DOC_INDEX = range(3) @@ -51,6 +52,18 @@ def _format_doc(doc): return out or 'undocumented' +def _format_cat_tooltip(category): + tooltip = '{}: {}'.format('Category' if len(category) > 1 else 'Module', category[-1]) + + if category == ('Core',): + tooltip += '\n\nThis subtree is meant for blocks included with GNU Radio (in-tree).' + + elif category == (Constants.DEFAULT_BLOCK_MODULE_NAME,): + tooltip += '\n\n' + Constants.DEFAULT_BLOCK_MODULE_TOOLTIP + + return tooltip + + class BlockTreeWindow(Gtk.VBox): """The block selection panel.""" @@ -100,66 +113,68 @@ class BlockTreeWindow(Gtk.VBox): renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX) self.treeview.append_column(column) - # try to enable the tooltips (available in pygtk 2.12 and above) - try: - self.treeview.set_tooltip_column(DOC_INDEX) - except: - pass + self.treeview.set_tooltip_column(DOC_INDEX) # setup sort order column.set_sort_column_id(0) self.treestore.set_sort_column_id(0, Gtk.SortType.ASCENDING) # setup drag and drop - self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, DND_TARGETS, Gdk.DragAction.COPY) + self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, Constants.DND_TARGETS, Gdk.DragAction.COPY) self.treeview.connect('drag-data-get', self._handle_drag_get_data) # make the scrolled window to hold the tree view scrolled_window = Gtk.ScrolledWindow() scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) scrolled_window.add_with_viewport(self.treeview) - scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1) + scrolled_window.set_size_request(Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1) self.pack_start(scrolled_window, True, True, 0) # map categories to iters, automatic mapping for root self._categories = {tuple(): None} self._categories_search = {tuple(): None} - # add blocks and categories - self.platform.load_block_tree(self) self.platform.block_docstrings_loaded_callback = self.update_docs + self.repopulate() def clear(self): - self.treestore.clear(); - self._categories = {tuple(): None} + self.treestore.clear() + self._categories = {(): None} + + def repopulate(self): + self.clear() + for block in six.itervalues(self.platform.blocks): + if block.category: + self.add_block(block) + self.expand_module_in_tree() + + def expand_module_in_tree(self, module_name='Core'): + self.treeview.collapse_all() + core_module_iter = self._categories.get((module_name,)) + if core_module_iter: + self.treeview.expand_row(self.treestore.get_path(core_module_iter), False) ############################################################ ## Block Tree Methods ############################################################ - def add_block(self, category, block=None, treestore=None, categories=None): + def add_block(self, block, treestore=None, categories=None): """ Add a block with category to this selection window. Add only the category when block is None. Args: - category: the category list or path string block: the block object or None """ - if treestore is None: - treestore = self.treestore - if categories is None: - categories = self._categories - - if isinstance(category, (str, six.text_type)): - category = category.split('/') - category = tuple(x for x in category if x) # tuple is hashable + treestore = treestore or self.treestore + categories = categories or self._categories + + category = tuple(filter(str, block.category)) # tuple is hashable, remove empty cats + # add category and all sub categories - for i, cat_name in enumerate(category): - sub_category = category[:i+1] - if sub_category not in categories: - iter_ = treestore.insert_before(categories[sub_category[:-1]], None) - treestore.set_value(iter_, NAME_INDEX, cat_name) + for level, parent_cat_name in enumerate(category, 1): + parent_category = category[:level] + if parent_category not in categories: + iter_ = treestore.insert_before(categories[parent_category[:-1]], None) + treestore.set_value(iter_, NAME_INDEX, parent_cat_name) treestore.set_value(iter_, KEY_INDEX, '') - treestore.set_value(iter_, DOC_INDEX, 'Category: ' + Utils.encode(cat_name)) - categories[sub_category] = iter_ + treestore.set_value(iter_, DOC_INDEX, _format_cat_tooltip(parent_cat_name)) + categories[parent_category] = iter_ # add block - if block is None: - return iter_ = treestore.insert_before(categories[category], None) treestore.set_value(iter_, NAME_INDEX, block.get_name()) treestore.set_value(iter_, KEY_INDEX, block.get_key()) @@ -212,7 +227,7 @@ class BlockTreeWindow(Gtk.VBox): key = widget.get_text().lower() if not key: self.treeview.set_model(self.treestore) - self.treeview.collapse_all() + self.expand_module_in_tree() else: matching_blocks = [b for b in list(self.platform.blocks.values()) if key in b.get_key().lower() or key in b.get_name().lower()] @@ -220,8 +235,7 @@ class BlockTreeWindow(Gtk.VBox): self.treestore_search.clear() self._categories_search = {tuple(): None} for block in matching_blocks: - self.add_block(block.get_category() or 'None', block, - self.treestore_search, self._categories_search) + self.add_block(block, self.treestore_search, self._categories_search) self.treeview.set_model(self.treestore_search) self.treeview.expand_all() diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py index 8bb15acc09..035a7f8ca9 100644 --- a/grc/gui/Constants.py +++ b/grc/gui/Constants.py @@ -96,6 +96,13 @@ SCROLL_DISTANCE = 15 # How close the mouse click can be to a line and register a connection select. LINE_SELECT_SENSITIVITY = 5 +DEFAULT_BLOCK_MODULE_TOOLTIP = """\ +This subtree holds all blocks (from OOT modules) that specify no module name. \ +The module name is the root category enclosed in square brackets. + +Please consider contacting OOT module maintainer for any block in here \ +and kindly ask to update their GRC Block Descriptions or Block Tree to include a module name.""" + def update_font_size(font_size): global PORT_SEPARATION, BLOCK_FONT, PORT_FONT, PARAM_FONT, FONT_SIZE diff --git a/grc/main.py b/grc/main.py index 810ac7c66f..ff0811e22a 100755 --- a/grc/main.py +++ b/grc/main.py @@ -15,8 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -from __future__ import absolute_import -import optparse +import argparse import gi gi.require_version('Gtk', '3.0') @@ -38,10 +37,10 @@ This is free software, and you are welcome to redistribute it. def main(): - parser = optparse.OptionParser( - usage='usage: %prog [options] [saved flow graphs]', - version=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version()) - options, args = parser.parse_args() + parser = argparse.ArgumentParser( + description=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version()) + parser.add_argument('flow_graphs', nargs='*') + args = parser.parse_args() try: Gtk.window_set_default_icon(Gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) @@ -54,6 +53,6 @@ def main(): version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()), install_prefix=gr.prefix() ) - ActionHandler(args, platform) + ActionHandler(args.flow_graphs, platform) Gtk.main() diff --git a/grc/test/.cache/v/cache/lastfailed b/grc/test/.cache/v/cache/lastfailed new file mode 100644 index 0000000000..9c73bf7055 --- /dev/null +++ b/grc/test/.cache/v/cache/lastfailed @@ -0,0 +1,3 @@ +{ + "test_no_re_cheetah_find.py::test_nested": true +}
\ No newline at end of file |