summaryrefslogtreecommitdiff
path: root/grc
diff options
context:
space:
mode:
Diffstat (limited to 'grc')
-rw-r--r--grc/CMakeLists.txt10
-rw-r--r--grc/__main__.py20
-rw-r--r--grc/base/Block.py542
-rw-r--r--grc/base/CMakeLists.txt43
-rw-r--r--grc/base/Constants.py50
-rw-r--r--grc/base/FlowGraph.py484
-rw-r--r--grc/base/Param.py205
-rw-r--r--grc/base/Platform.py271
-rw-r--r--grc/base/Port.py136
-rw-r--r--grc/base/__init__.py20
-rw-r--r--grc/blocks/blks2_error_rate.xml69
-rw-r--r--grc/blocks/blks2_packet_decoder.xml75
-rw-r--r--grc/blocks/blks2_packet_encoder.xml119
-rw-r--r--grc/blocks/blks2_selector.xml97
-rw-r--r--grc/blocks/blks2_tcp_sink.xml89
-rw-r--r--grc/blocks/blks2_tcp_source.xml89
-rw-r--r--grc/blocks/blks2_valve.xml72
-rw-r--r--grc/blocks/block_tree.xml17
-rw-r--r--grc/blocks/xmlrpc_client.xml42
-rw-r--r--grc/blocks/xmlrpc_server.xml41
-rwxr-xr-xgrc/checks.py80
-rw-r--r--grc/core/Block.py846
-rw-r--r--grc/core/CMakeLists.txt (renamed from grc/examples/CMakeLists.txt)24
-rw-r--r--grc/core/Config.py55
-rw-r--r--grc/core/Connection.py (renamed from grc/base/Connection.py)83
-rw-r--r--grc/core/Constants.py (renamed from grc/python/Constants.py)85
-rw-r--r--grc/core/Element.py (renamed from grc/base/Element.py)53
-rw-r--r--grc/core/FlowGraph.py594
-rw-r--r--grc/core/Messages.py (renamed from grc/gui/Messages.py)53
-rw-r--r--grc/core/Param.py740
-rw-r--r--grc/core/ParseXML.py (renamed from grc/base/ParseXML.py)22
-rw-r--r--grc/core/Platform.py319
-rw-r--r--grc/core/Port.py404
-rw-r--r--grc/core/__init__.py (renamed from grc/grc_gnuradio/__init__.py)0
-rw-r--r--grc/core/block.dtd (renamed from grc/python/block.dtd)0
-rw-r--r--grc/core/block_tree.dtd (renamed from grc/base/block_tree.dtd)0
-rw-r--r--grc/core/default_flow_graph.grc (renamed from grc/python/default_flow_graph.grc)0
-rw-r--r--grc/core/domain.dtd (renamed from grc/base/domain.dtd)0
-rw-r--r--grc/core/flow_graph.dtd (renamed from grc/base/flow_graph.dtd)0
-rw-r--r--grc/core/generator/CMakeLists.txt (renamed from grc/grc_gnuradio/CMakeLists.txt)17
-rw-r--r--grc/core/generator/FlowGraphProxy.py126
-rw-r--r--grc/core/generator/Generator.py (renamed from grc/python/Generator.py)195
-rw-r--r--grc/core/generator/__init__.py18
-rw-r--r--grc/core/generator/flow_graph.tmpl (renamed from grc/python/flow_graph.tmpl)9
-rw-r--r--grc/core/utils/CMakeLists.txt (renamed from grc/grc_gnuradio/blks2/__init__.py)16
-rw-r--r--grc/core/utils/__init__.py22
-rw-r--r--grc/core/utils/complexity.py49
-rw-r--r--grc/core/utils/epy_block_io.py (renamed from grc/python/epy_block_io.py)15
-rw-r--r--grc/core/utils/expr_utils.py (renamed from grc/python/expr_utils.py)57
-rw-r--r--grc/core/utils/extract_docs.py (renamed from grc/python/extract_docs.py)47
-rw-r--r--grc/core/utils/odict.py (renamed from grc/base/odict.py)18
-rw-r--r--grc/cpp/README5
-rw-r--r--grc/examples/simple/variable_config.grc561
-rw-r--r--grc/examples/xmlrpc/readme.txt18
-rw-r--r--grc/examples/xmlrpc/xmlrpc_client.grc428
-rw-r--r--grc/examples/xmlrpc/xmlrpc_client_script.py23
-rw-r--r--grc/examples/xmlrpc/xmlrpc_server.grc908
-rw-r--r--grc/grc_gnuradio/README11
-rw-r--r--grc/grc_gnuradio/blks2/error_rate.py140
-rw-r--r--grc/grc_gnuradio/blks2/packet.py257
-rw-r--r--grc/grc_gnuradio/blks2/selector.py142
-rw-r--r--grc/grc_gnuradio/blks2/tcp.py70
-rw-r--r--grc/gui/ActionHandler.py111
-rw-r--r--grc/gui/Actions.py8
-rw-r--r--grc/gui/Bars.py1
-rw-r--r--grc/gui/Block.py48
-rw-r--r--grc/gui/BlockTreeWindow.py58
-rw-r--r--grc/gui/CMakeLists.txt32
-rw-r--r--grc/gui/Colors.py3
-rw-r--r--grc/gui/Config.py74
-rw-r--r--grc/gui/Connection.py18
-rw-r--r--grc/gui/Constants.py53
-rw-r--r--grc/gui/Dialogs.py33
-rw-r--r--grc/gui/DrawingArea.py27
-rw-r--r--grc/gui/Element.py16
-rw-r--r--grc/gui/Executor.py123
-rw-r--r--grc/gui/FileDialogs.py15
-rw-r--r--grc/gui/FlowGraph.py83
-rw-r--r--grc/gui/MainWindow.py30
-rw-r--r--grc/gui/NotebookPage.py6
-rw-r--r--grc/gui/Param.py15
-rw-r--r--grc/gui/Platform.py53
-rw-r--r--grc/gui/Port.py43
-rw-r--r--grc/gui/Preferences.py6
-rw-r--r--grc/gui/PropsDialog.py38
-rwxr-xr-xgrc/main.py55
-rw-r--r--grc/python/Block.py323
-rw-r--r--grc/python/CMakeLists.txt44
-rw-r--r--grc/python/Connection.py45
-rw-r--r--grc/python/FlowGraph.py338
-rw-r--r--grc/python/Param.py433
-rw-r--r--grc/python/Platform.py174
-rw-r--r--grc/python/Port.py268
-rw-r--r--grc/python/__init__.py1
-rw-r--r--grc/scripts/CMakeLists.txt2
-rw-r--r--grc/scripts/freedesktop/CMakeLists.txt (renamed from grc/freedesktop/CMakeLists.txt)0
-rw-r--r--grc/scripts/freedesktop/README (renamed from grc/freedesktop/README)0
-rwxr-xr-xgrc/scripts/freedesktop/convert.sh (renamed from grc/freedesktop/convert.sh)0
-rw-r--r--grc/scripts/freedesktop/gnuradio-grc.desktop (renamed from grc/freedesktop/gnuradio-grc.desktop)0
-rw-r--r--grc/scripts/freedesktop/gnuradio-grc.xml (renamed from grc/freedesktop/gnuradio-grc.xml)0
-rw-r--r--grc/scripts/freedesktop/gnuradio_logo_icon-square.svg (renamed from grc/freedesktop/gnuradio_logo_icon-square.svg)0
-rw-r--r--grc/scripts/freedesktop/grc-icon-128.png (renamed from grc/freedesktop/grc-icon-128.png)bin4758 -> 4758 bytes
-rw-r--r--grc/scripts/freedesktop/grc-icon-16.png (renamed from grc/freedesktop/grc-icon-16.png)bin537 -> 537 bytes
-rw-r--r--grc/scripts/freedesktop/grc-icon-24.png (renamed from grc/freedesktop/grc-icon-24.png)bin840 -> 840 bytes
-rw-r--r--grc/scripts/freedesktop/grc-icon-256.png (renamed from grc/freedesktop/grc-icon-256.png)bin9762 -> 9762 bytes
-rw-r--r--grc/scripts/freedesktop/grc-icon-32.png (renamed from grc/freedesktop/grc-icon-32.png)bin1148 -> 1148 bytes
-rw-r--r--grc/scripts/freedesktop/grc-icon-48.png (renamed from grc/freedesktop/grc-icon-48.png)bin1796 -> 1796 bytes
-rw-r--r--grc/scripts/freedesktop/grc-icon-64.png (renamed from grc/freedesktop/grc-icon-64.png)bin2355 -> 2355 bytes
-rw-r--r--grc/scripts/freedesktop/grc_setup_freedesktop.in (renamed from grc/freedesktop/grc_setup_freedesktop.in)0
-rwxr-xr-xgrc/scripts/gnuradio-companion124
-rw-r--r--grc/todo.txt69
111 files changed, 4290 insertions, 7481 deletions
diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index 859b9e9045..4c782a7f7d 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -96,8 +96,10 @@ install(
COMPONENT "grc"
)
+file(GLOB py_files "*.py")
+
GR_PYTHON_INSTALL(
- FILES __init__.py
+ FILES ${py_files}
DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc
COMPONENT "grc"
)
@@ -133,13 +135,9 @@ endif(WIN32)
########################################################################
# Add subdirectories
########################################################################
-add_subdirectory(base)
add_subdirectory(blocks)
-add_subdirectory(freedesktop)
-add_subdirectory(grc_gnuradio)
add_subdirectory(gui)
-add_subdirectory(python)
+add_subdirectory(core)
add_subdirectory(scripts)
-add_subdirectory(examples)
endif(ENABLE_GRC)
diff --git a/grc/__main__.py b/grc/__main__.py
new file mode 100644
index 0000000000..899d0195d1
--- /dev/null
+++ b/grc/__main__.py
@@ -0,0 +1,20 @@
+# Copyright 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 .main import main
+
+main()
diff --git a/grc/base/Block.py b/grc/base/Block.py
deleted file mode 100644
index 77c3145173..0000000000
--- a/grc/base/Block.py
+++ /dev/null
@@ -1,542 +0,0 @@
-"""
-Copyright 2008-2011 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 odict
-from . Constants import ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB
-from . Constants import BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS
-from . Constants import BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED
-from Element import Element
-
-from Cheetah.Template import Template
-from UserDict import UserDict
-from itertools import imap
-
-
-class TemplateArg(UserDict):
- """
- A cheetah template argument created from a param.
- The str of this class evaluates to the param's to code method.
- The use of this class as a dictionary (enum only) will reveal the enum opts.
- The __call__ or () method can return the param evaluated to a raw python data type.
- """
-
- def __init__(self, param):
- UserDict.__init__(self)
- self._param = param
- if param.is_enum():
- for key in param.get_opt_keys():
- self[key] = str(param.get_opt(key))
-
- def __str__(self):
- return str(self._param.to_code())
-
- def __call__(self):
- return self._param.get_evaluated()
-
-
-def _get_keys(lst):
- return [elem.get_key() for elem in lst]
-
-
-def _get_elem(lst, key):
- try: return lst[_get_keys(lst).index(key)]
- except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst))
-
-
-class Block(Element):
-
- def __init__(self, flow_graph, n):
- """
- Make a new block from nested data.
-
- Args:
- flow: graph the parent element
- n: the nested odict
-
- Returns:
- block a new block
- """
- #build the block
- Element.__init__(self, flow_graph)
- #grab the data
- params = n.findall('param')
- sources = n.findall('source')
- sinks = n.findall('sink')
- self._name = n.find('name')
- self._key = n.find('key')
- self._category = n.find('category') or ''
- self._flags = n.find('flags') or ''
- # Backwards compatibility
- if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags:
- self._flags += BLOCK_FLAG_THROTTLE
- self._grc_source = n.find('grc_source') or ''
- self._block_wrapper_path = n.find('block_wrapper_path')
- self._bussify_sink = n.find('bus_sink')
- self._bussify_source = n.find('bus_source')
- self._var_value = n.find('var_value') or '$value'
-
- # get list of param tabs
- n_tabs = n.find('param_tab_order') or None
- self._param_tab_labels = n_tabs.findall('tab') if n_tabs is not None else [DEFAULT_PARAM_TAB]
-
- #create the param objects
- self._params = list()
- #add the id param
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({
- 'name': 'ID',
- 'key': 'id',
- 'type': 'id',
- })
- ))
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({
- 'name': 'Enabled',
- 'key': '_enabled',
- 'type': 'raw',
- 'value': 'True',
- 'hide': 'all',
- })
- ))
- for param in imap(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params):
- key = param.get_key()
- #test against repeated keys
- if key in self.get_param_keys():
- raise Exception, 'Key "%s" already exists in params'%key
- #store the param
- self.get_params().append(param)
- #create the source objects
- self._sources = list()
- for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources):
- key = source.get_key()
- #test against repeated keys
- if key in self.get_source_keys():
- raise Exception, 'Key "%s" already exists in sources'%key
- #store the port
- self.get_sources().append(source)
- self.back_ofthe_bus(self.get_sources())
- #create the sink objects
- self._sinks = list()
- for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks):
- key = sink.get_key()
- #test against repeated keys
- if key in self.get_sink_keys():
- raise Exception, 'Key "%s" already exists in sinks'%key
- #store the port
- self.get_sinks().append(sink)
- self.back_ofthe_bus(self.get_sinks())
- self.current_bus_structure = {'source':'','sink':''};
-
- # Virtual source/sink and pad source/sink blocks are
- # indistinguishable from normal GR blocks. Make explicit
- # checks for them here since they have no work function or
- # buffers to manage.
- is_virtual_or_pad = self._key in (
- "virtual_source", "virtual_sink", "pad_source", "pad_sink")
- is_variable = self._key.startswith('variable')
-
- # Disable blocks that are virtual/pads or variables
- if is_virtual_or_pad or is_variable:
- self._flags += BLOCK_FLAG_DISABLE_BYPASS
-
- if not (is_virtual_or_pad or is_variable or self._key == 'options'):
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({'name': 'Block Alias',
- 'key': 'alias',
- 'type': 'string',
- 'hide': 'part',
- 'tab': ADVANCED_PARAM_TAB
- })
- ))
-
- if (len(sources) or len(sinks)) and not is_virtual_or_pad:
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({'name': 'Core Affinity',
- 'key': 'affinity',
- 'type': 'int_vector',
- 'hide': 'part',
- 'tab': ADVANCED_PARAM_TAB
- })
- ))
- if len(sources) and not is_virtual_or_pad:
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({'name': 'Min Output Buffer',
- 'key': 'minoutbuf',
- 'type': 'int',
- 'hide': 'part',
- 'value': '0',
- 'tab': ADVANCED_PARAM_TAB
- })
- ))
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({'name': 'Max Output Buffer',
- 'key': 'maxoutbuf',
- 'type': 'int',
- 'hide': 'part',
- 'value': '0',
- 'tab': ADVANCED_PARAM_TAB
- })
- ))
-
- self.get_params().append(self.get_parent().get_parent().Param(
- block=self,
- n=odict({'name': 'Comment',
- 'key': 'comment',
- 'type': '_multiline',
- 'hide': 'part',
- 'value': '',
- 'tab': ADVANCED_PARAM_TAB
- })
- ))
-
- def back_ofthe_bus(self, portlist):
- portlist.sort(key=lambda p: p._type == 'bus')
-
- def filter_bus_port(self, ports):
- buslist = [p for p in ports if p._type == 'bus']
- return buslist or ports
-
- # Main functions to get and set the block state
- # Also kept get_enabled and set_enabled to keep compatibility
- def get_state(self):
- """
- Gets the block's current state.
-
- Returns:
- ENABLED - 0
- BYPASSED - 1
- DISABLED - 2
- """
- try: return int(eval(self.get_param('_enabled').get_value()))
- except: return BLOCK_ENABLED
-
- def set_state(self, state):
- """
- Sets the state for the block.
-
- Args:
- ENABLED - 0
- BYPASSED - 1
- DISABLED - 2
- """
- if state in [BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED]:
- self.get_param('_enabled').set_value(str(state))
- else:
- self.get_param('_enabled').set_value(str(BLOCK_ENABLED))
-
- # Enable/Disable Aliases
- def get_enabled(self):
- """
- Get the enabled state of the block.
-
- Returns:
- true for enabled
- """
- return not (self.get_state() == BLOCK_DISABLED)
-
- def set_enabled(self, enabled):
- """
- Set the enabled state of the block.
-
- Args:
- enabled: true for enabled
-
- Returns:
- True if block changed state
- """
- old_state = self.get_state()
- new_state = BLOCK_ENABLED if enabled else BLOCK_DISABLED
- self.set_state(new_state)
- return old_state != new_state
-
- # Block bypassing
- def get_bypassed(self):
- """
- Check if the block is bypassed
- """
- return self.get_state() == BLOCK_BYPASSED
-
- def set_bypassed(self):
- """
- Bypass the block
-
- Returns:
- True if block chagnes state
- """
- if self.get_state() != BLOCK_BYPASSED and self.can_bypass():
- self.set_state(BLOCK_BYPASSED)
- return True
- return False
-
- def can_bypass(self):
- """ Check the number of sinks and sources and see if this block can be bypassed """
- # Check to make sure this is a single path block
- # Could possibly support 1 to many blocks
- if len(self.get_sources()) != 1 or len(self.get_sinks()) != 1:
- return False
- if not (self.get_sources()[0].get_type() == self.get_sinks()[0].get_type()):
- return False
- if self.bypass_disabled():
- return False
- return True
-
- def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key())
-
- def get_id(self): return self.get_param('id').get_value()
- def is_block(self): return True
- def get_name(self): return self._name
- def get_key(self): return self._key
- def get_category(self): return self._category
- def set_category(self, cat): self._category = cat
- def get_doc(self): return ''
- def get_ports(self): return self.get_sources() + self.get_sinks()
- def get_ports_gui(self): return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks());
- def get_children(self): return self.get_ports() + self.get_params()
- def get_children_gui(self): return self.get_ports_gui() + self.get_params()
- def get_block_wrapper_path(self): return self._block_wrapper_path
- def get_comment(self): return self.get_param('comment').get_value()
-
- def get_flags(self): return self._flags
- def throtteling(self): return BLOCK_FLAG_THROTTLE in self._flags
- def bypass_disabled(self): return BLOCK_FLAG_DISABLE_BYPASS in self._flags
-
- ##############################################
- # Access Params
- ##############################################
- def get_param_tab_labels(self): return self._param_tab_labels
- def get_param_keys(self): return _get_keys(self._params)
- def get_param(self, key): return _get_elem(self._params, key)
- def get_params(self): return self._params
- def has_param(self, key):
- try:
- _get_elem(self._params, key);
- return True;
- except:
- return False;
-
- ##############################################
- # Access Sinks
- ##############################################
- def get_sink_keys(self): return _get_keys(self._sinks)
- def get_sink(self, key): return _get_elem(self._sinks, key)
- def get_sinks(self): return self._sinks
- def get_sinks_gui(self): return self.filter_bus_port(self.get_sinks())
-
- ##############################################
- # Access Sources
- ##############################################
- def get_source_keys(self): return _get_keys(self._sources)
- def get_source(self, key): return _get_elem(self._sources, key)
- def get_sources(self): return self._sources
- def get_sources_gui(self): return self.filter_bus_port(self.get_sources());
-
- def get_connections(self):
- return sum([port.get_connections() for port in self.get_ports()], [])
-
- def resolve_dependencies(self, tmpl):
- """
- Resolve a paramater dependency with cheetah templates.
-
- Args:
- tmpl: the string with dependencies
-
- Returns:
- the resolved value
- """
- tmpl = str(tmpl)
- if '$' not in tmpl: return tmpl
- n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params())
- try:
- return str(Template(tmpl, n))
- except Exception as err:
- return "Template error: %s\n %s" % (tmpl, err)
-
- ##############################################
- # Controller Modify
- ##############################################
- def type_controller_modify(self, direction):
- """
- Change the type controller.
-
- Args:
- direction: +1 or -1
-
- Returns:
- true for change
- """
- changed = False
- type_param = None
- for param in filter(lambda p: p.is_enum(), self.get_params()):
- children = self.get_ports() + self.get_params()
- #priority to the type controller
- if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param
- #use param if type param is unset
- if not type_param: type_param = param
- if type_param:
- #try to increment the enum by direction
- try:
- keys = type_param.get_option_keys()
- old_index = keys.index(type_param.get_value())
- new_index = (old_index + direction + len(keys))%len(keys)
- type_param.set_value(keys[new_index])
- changed = True
- except: pass
- return changed
-
- def port_controller_modify(self, direction):
- """
- Change the port controller.
-
- Args:
- direction: +1 or -1
-
- Returns:
- true for change
- """
- return False
-
- def form_bus_structure(self, direc):
- if direc == 'source':
- get_p = self.get_sources;
- get_p_gui = self.get_sources_gui;
- bus_structure = self.get_bus_structure('source');
- else:
- get_p = self.get_sinks;
- get_p_gui = self.get_sinks_gui
- bus_structure = self.get_bus_structure('sink');
-
- struct = [range(len(get_p()))];
- if True in map(lambda a: isinstance(a.get_nports(), int), get_p()):
-
-
- structlet = [];
- last = 0;
- for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]:
- structlet.extend(map(lambda a: a+last, range(j)));
- last = structlet[-1] + 1;
- struct = [structlet];
- if bus_structure:
-
- struct = bus_structure
-
- self.current_bus_structure[direc] = struct;
- return struct
-
- def bussify(self, n, direc):
- if direc == 'source':
- get_p = self.get_sources;
- get_p_gui = self.get_sources_gui;
- bus_structure = self.get_bus_structure('source');
- else:
- get_p = self.get_sinks;
- get_p_gui = self.get_sinks_gui
- bus_structure = self.get_bus_structure('sink');
-
-
- for elt in get_p():
- for connect in elt.get_connections():
- self.get_parent().remove_element(connect);
-
-
-
-
-
-
- if (not 'bus' in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0:
-
- struct = self.form_bus_structure(direc);
- self.current_bus_structure[direc] = struct;
- if get_p()[0].get_nports():
- n['nports'] = str(1);
-
- for i in range(len(struct)):
- n['key'] = str(len(get_p()));
- n = odict(n);
- port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc);
- get_p().append(port);
-
-
-
-
- elif 'bus' in map(lambda a: a.get_type(), get_p()):
- for elt in get_p_gui():
- get_p().remove(elt);
- self.current_bus_structure[direc] = ''
- ##############################################
- ## Import/Export Methods
- ##############################################
- def export_data(self):
- """
- Export this block's params to nested data.
-
- Returns:
- a nested data odict
- """
- n = odict()
- n['key'] = self.get_key()
- n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str))
- if 'bus' in map(lambda a: a.get_type(), self.get_sinks()):
- n['bus_sink'] = str(1);
- if 'bus' in map(lambda a: a.get_type(), self.get_sources()):
- n['bus_source'] = str(1);
- return n
-
- def import_data(self, n):
- """
- Import this block's params from nested data.
- Any param keys that do not exist will be ignored.
- Since params can be dynamically created based another param,
- call rewrite, and repeat the load until the params stick.
- This call to rewrite will also create any dynamic ports
- that are needed for the connections creation phase.
-
- Args:
- n: the nested data odict
- """
- get_hash = lambda: hash(tuple(map(hash, self.get_params())))
- my_hash = 0
- while get_hash() != my_hash:
- params_n = n.findall('param')
- for param_n in params_n:
- key = param_n.find('key')
- value = param_n.find('value')
- #the key must exist in this block's params
- if key in self.get_param_keys():
- self.get_param(key).set_value(value)
- #store hash and call rewrite
- my_hash = get_hash()
- self.rewrite()
- bussinks = n.findall('bus_sink');
- if len(bussinks) > 0 and not self._bussify_sink:
- self.bussify({'name':'bus','type':'bus'}, 'sink')
- elif len(bussinks) > 0:
- self.bussify({'name':'bus','type':'bus'}, 'sink')
- self.bussify({'name':'bus','type':'bus'}, 'sink')
- bussrcs = n.findall('bus_source');
- if len(bussrcs) > 0 and not self._bussify_source:
- self.bussify({'name':'bus','type':'bus'}, 'source')
- elif len(bussrcs) > 0:
- self.bussify({'name':'bus','type':'bus'}, 'source')
- self.bussify({'name':'bus','type':'bus'}, 'source')
diff --git a/grc/base/CMakeLists.txt b/grc/base/CMakeLists.txt
deleted file mode 100644
index bdc8a5006f..0000000000
--- a/grc/base/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2011 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio 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 3, or (at your option)
-# any later version.
-#
-# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-
-########################################################################
-GR_PYTHON_INSTALL(FILES
- odict.py
- ParseXML.py
- Block.py
- Connection.py
- Constants.py
- Element.py
- FlowGraph.py
- Param.py
- Platform.py
- Port.py
- __init__.py
- DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/base
- COMPONENT "grc"
-)
-
-install(FILES
- block_tree.dtd
- domain.dtd
- flow_graph.dtd
- DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/base
- COMPONENT "grc"
-)
diff --git a/grc/base/Constants.py b/grc/base/Constants.py
deleted file mode 100644
index 1e83de63b5..0000000000
--- a/grc/base/Constants.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-Copyright 2008, 2009 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
-"""
-
-import os
-
-#data files
-DATA_DIR = os.path.dirname(__file__)
-FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd')
-BLOCK_TREE_DTD = os.path.join(DATA_DIR, 'block_tree.dtd')
-
-# file format versions:
-# 0: undefined / legacy
-# 1: non-numeric message port keys (label is used instead)
-FLOW_GRAPH_FILE_FORMAT_VERSION = 1
-
-# Param tabs
-DEFAULT_PARAM_TAB = "General"
-ADVANCED_PARAM_TAB = "Advanced"
-
-# Port domains
-DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd')
-GR_STREAM_DOMAIN = "gr_stream"
-GR_MESSAGE_DOMAIN = "gr_message"
-DEFAULT_DOMAIN = GR_STREAM_DOMAIN
-
-BLOCK_FLAG_THROTTLE = 'throttle'
-BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass'
-BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui'
-BLOCK_FLAG_NEED_WX_GUI = 'need_ex_gui'
-
-# Block States
-BLOCK_DISABLED = 0
-BLOCK_ENABLED = 1
-BLOCK_BYPASSED = 2
diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py
deleted file mode 100644
index 0398dfd011..0000000000
--- a/grc/base/FlowGraph.py
+++ /dev/null
@@ -1,484 +0,0 @@
-"""
-Copyright 2008-2011 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
-"""
-
-import time
-from operator import methodcaller
-from itertools import ifilter
-
-from .. gui import Messages
-
-from . import odict
-from .Element import Element
-from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
-
-
-class FlowGraph(Element):
-
- def __init__(self, platform):
- """
- Make a flow graph from the arguments.
-
- Args:
- platform: a platforms with blocks and contrcutors
-
- Returns:
- the flow graph object
- """
- #initialize
- Element.__init__(self, platform)
- self._elements = []
- self._timestamp = time.ctime()
- #inital blank import
- self.import_data()
-
- def _get_unique_id(self, base_id=''):
- """
- Get a unique id starting with the base id.
-
- Args:
- base_id: the id starts with this and appends a count
-
- Returns:
- a unique id
- """
- index = 0
- while True:
- id = '%s_%d' % (base_id, index)
- index += 1
- #make sure that the id is not used by another block
- if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id
-
- def __str__(self):
- return 'FlowGraph - %s(%s)' % (self.get_option('title'), self.get_option('id'))
-
- def get_complexity(self):
- """
- Determines the complexity of a flowgraph
- """
- dbal = 0
- block_list = self.get_blocks()
- for block in block_list:
- # Skip options block
- if block.get_key() == 'options':
- continue
-
- # Don't worry about optional sinks?
- sink_list = filter(lambda c: not c.get_optional(), block.get_sinks())
- source_list = filter(lambda c: not c.get_optional(), block.get_sources())
- sinks = float(len(sink_list))
- sources = float(len(source_list))
- base = max(min(sinks, sources), 1)
-
- # Port ratio multiplier
- if min(sinks, sources) > 0:
- multi = sinks / sources
- multi = (1 / multi) if multi > 1 else multi
- else:
- multi = 1
-
- # Connection ratio multiplier
- sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0)
- source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0)
- dbal = dbal + (base * multi * sink_multi * source_multi)
-
- elements = float(len(self.get_elements()))
- connections = float(len(self.get_connections()))
- disabled_connections = len(filter(lambda c: not c.get_enabled(), self.get_connections()))
- blocks = float(len(block_list))
- variables = elements - blocks - connections
- enabled = float(len(self.get_enabled_blocks()))
-
- # Disabled multiplier
- if enabled > 0:
- disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05))
- else:
- disabled_multi = 1
-
- # Connection multiplier (How many connections )
- if (connections - disabled_connections) > 0:
- conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05))
- else:
- conn_multi = 1
-
- final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6)
- return final
-
- def rewrite(self):
- def refactor_bus_structure():
-
- for block in self.get_blocks():
- for direc in ['source', 'sink']:
- if direc == 'source':
- get_p = block.get_sources;
- get_p_gui = block.get_sources_gui;
- bus_structure = block.form_bus_structure('source');
- else:
- get_p = block.get_sinks;
- get_p_gui = block.get_sinks_gui
- bus_structure = block.form_bus_structure('sink');
-
- if 'bus' in map(lambda a: a.get_type(), get_p_gui()):
- if len(get_p_gui()) > len(bus_structure):
- times = range(len(bus_structure), len(get_p_gui()));
- for i in times:
- for connect in get_p_gui()[-1].get_connections():
- block.get_parent().remove_element(connect);
- get_p().remove(get_p_gui()[-1]);
- elif len(get_p_gui()) < len(bus_structure):
- n = {'name':'bus','type':'bus'};
- if True in map(lambda a: isinstance(a.get_nports(), int), get_p()):
- n['nports'] = str(1);
-
- times = range(len(get_p_gui()), len(bus_structure));
-
- for i in times:
- n['key'] = str(len(get_p()));
- n = odict(n);
- port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc);
- get_p().append(port);
-
- for child in self.get_children(): child.rewrite()
- refactor_bus_structure()
-
- def get_option(self, key):
- """
- Get the option for a given key.
- The option comes from the special options block.
-
- Args:
- key: the param key for the options block
-
- Returns:
- the value held by that param
- """
- return self._options_block.get_param(key).get_evaluated()
-
- def is_flow_graph(self): return True
-
- ##############################################
- ## Access Elements
- ##############################################
- def get_block(self, id):
- for block in self.iter_blocks():
- if block.get_id() == id:
- return block
- raise KeyError('No block with ID {0!r}'.format(id))
-
- def iter_blocks(self):
- return ifilter(methodcaller('is_block'), self.get_elements())
-
- def get_blocks(self):
- return list(self.iter_blocks())
-
- def iter_connections(self):
- return ifilter(methodcaller('is_connection'), self.get_elements())
-
- def get_connections(self):
- return list(self.iter_connections())
-
- def get_elements(self):
- """
- Get a list of all the elements.
- Always ensure that the options block is in the list (only once).
-
- Returns:
- the element list
- """
- options_block_count = self._elements.count(self._options_block)
- if not options_block_count:
- self._elements.append(self._options_block)
- for i in range(options_block_count-1):
- self._elements.remove(self._options_block)
- return self._elements
-
- get_children = get_elements
-
- def iter_enabled_blocks(self):
- """
- Get an iterator of all blocks that are enabled and not bypassed.
- """
- return ifilter(methodcaller('get_enabled'), self.iter_blocks())
-
- def get_enabled_blocks(self):
- """
- Get a list of all blocks that are enabled and not bypassed.
-
- Returns:
- a list of blocks
- """
- return list(self.iter_enabled_blocks())
-
- def get_bypassed_blocks(self):
- """
- Get a list of all blocks that are bypassed.
-
- Returns:
- a list of blocks
- """
- return filter(methodcaller('get_bypassed'), self.iter_blocks())
-
- def get_enabled_connections(self):
- """
- Get a list of all connections that are enabled.
-
- Returns:
- a list of connections
- """
- return filter(methodcaller('get_enabled'), self.get_connections())
-
- def get_new_block(self, key):
- """
- Get a new block of the specified key.
- Add the block to the list of elements.
-
- Args:
- key: the block key
-
- Returns:
- the new block or None if not found
- """
- if key not in self.get_parent().get_block_keys(): return None
- block = self.get_parent().get_new_block(self, key)
- self.get_elements().append(block);
- if block._bussify_sink:
- block.bussify({'name':'bus','type':'bus'}, 'sink')
- if block._bussify_source:
- block.bussify({'name':'bus','type':'bus'}, 'source')
- return block;
-
- def connect(self, porta, portb):
- """
- Create a connection between porta and portb.
-
- Args:
- porta: a port
- portb: another port
- @throw Exception bad connection
-
- Returns:
- the new connection
- """
- connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb)
- self.get_elements().append(connection)
- return connection
-
- def remove_element(self, element):
- """
- Remove the element from the list of elements.
- If the element is a port, remove the whole block.
- If the element is a block, remove its connections.
- If the element is a connection, just remove the connection.
- """
- if element not in self.get_elements(): return
- #found a port, set to parent signal block
- if element.is_port():
- element = element.get_parent()
- #remove block, remove all involved connections
- if element.is_block():
- for port in element.get_ports():
- map(self.remove_element, port.get_connections())
- if element.is_connection():
- if element.is_bus():
- cons_list = []
- for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()):
- cons_list.extend(i);
- map(self.remove_element, cons_list);
- self.get_elements().remove(element)
-
- def evaluate(self, expr):
- """
- Evaluate the expression.
-
- Args:
- expr: the string expression
- @throw NotImplementedError
- """
- raise NotImplementedError
-
- ##############################################
- ## Import/Export Methods
- ##############################################
- def export_data(self):
- """
- Export this flow graph to nested data.
- Export all block and connection data.
-
- Returns:
- a nested data odict
- """
- # sort blocks and connections for nicer diffs
- blocks = sorted(self.iter_blocks(), key=lambda b: (
- b.get_key() != 'options', # options to the front
- not b.get_key().startswith('variable'), # then vars
- str(b)
- ))
- connections = sorted(self.get_connections(), key=str)
- n = odict()
- n['timestamp'] = self._timestamp
- n['block'] = [b.export_data() for b in blocks]
- n['connection'] = [c.export_data() for c in connections]
- instructions = odict({
- 'created': self.get_parent().get_version_short(),
- 'format': FLOW_GRAPH_FILE_FORMAT_VERSION,
- })
- return odict({'flow_graph': n, '_instructions': instructions})
-
- def import_data(self, n=None):
- """
- Import blocks and connections into this flow graph.
- Clear this flowgraph of all previous blocks and connections.
- Any blocks or connections in error will be ignored.
-
- Args:
- n: the nested data odict
- """
- errors = False
- self._elements = list() # remove previous elements
- # set file format
- try:
- instructions = n.find('_instructions') or {}
- file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n)
- except:
- file_format = 0
-
- fg_n = n and n.find('flow_graph') or odict() # use blank data if none provided
- self._timestamp = fg_n.find('timestamp') or time.ctime()
-
- # build the blocks
- self._options_block = self.get_parent().get_new_block(self, 'options')
- for block_n in fg_n.findall('block'):
- key = block_n.find('key')
- block = self._options_block if key == 'options' else self.get_new_block(key)
-
- if not block: # looks like this block key cannot be found
- # create a dummy block instead
- block = self.get_new_block('dummy_block')
- # Ugly ugly ugly
- _initialize_dummy_block(block, block_n)
- Messages.send_error_msg_load('Block key "%s" not found' % key)
-
- block.import_data(block_n)
-
- self.rewrite() # evaluate stuff like nports before adding connections
-
- # build the connections
- def verify_and_get_port(key, block, dir):
- ports = block.get_sinks() if dir == 'sink' else block.get_sources()
- for port in ports:
- if key == port.get_key():
- break
- if not key.isdigit() and port.get_type() == '' and key == port.get_name():
- break
- else:
- if block.is_dummy_block():
- port = _dummy_block_add_port(block, key, dir)
- else:
- raise LookupError('%s key %r not in %s block keys' % (dir, key, dir))
- return port
-
- for connection_n in fg_n.findall('connection'):
- # get the block ids and port keys
- source_block_id = connection_n.find('source_block_id')
- sink_block_id = connection_n.find('sink_block_id')
- source_key = connection_n.find('source_key')
- sink_key = connection_n.find('sink_key')
- try:
- source_block = self.get_block(source_block_id)
- sink_block = self.get_block(sink_block_id)
-
- # fix old, numeric message ports keys
- if file_format < 1:
- source_key, sink_key = _update_old_message_port_keys(
- source_key, sink_key, source_block, sink_block)
-
- # build the connection
- source_port = verify_and_get_port(source_key, source_block, 'source')
- sink_port = verify_and_get_port(sink_key, sink_block, 'sink')
- self.connect(source_port, sink_port)
- except LookupError as e:
- Messages.send_error_load(
- 'Connection between %s(%s) and %s(%s) could not be made.\n\t%s' % (
- source_block_id, source_key, sink_block_id, sink_key, e))
- errors = True
-
- self.rewrite() # global rewrite
- return errors
-
-
-def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block):
- """Backward compatibility for message port keys
-
- Message ports use their names as key (like in the 'connect' method).
- Flowgraph files from former versions still have numeric keys stored for
- message connections. These have to be replaced by the name of the
- respective port. The correct message port is deduced from the integer
- value of the key (assuming the order has not changed).
-
- The connection ends are updated only if both ends translate into a
- message port.
- """
- try:
- # get ports using the "old way" (assuming liner indexed keys)
- source_port = source_block.get_sources()[int(source_key)]
- sink_port = sink_block.get_sinks()[int(sink_key)]
- if source_port.get_type() == "message" and sink_port.get_type() == "message":
- source_key, sink_key = source_port.get_key(), sink_port.get_key()
- except (ValueError, IndexError):
- pass
- return source_key, sink_key # do nothing
-
-
-def _guess_file_format_1(n):
- """Try to guess the file format for flow-graph files without version tag"""
- try:
- has_non_numeric_message_keys = any(not (
- connection_n.find('source_key').isdigit() and
- connection_n.find('sink_key').isdigit()
- ) for connection_n in n.find('flow_graph').findall('connection'))
- if has_non_numeric_message_keys:
- return 1
- except:
- pass
- return 0
-
-
-def _initialize_dummy_block(block, block_n):
- """This is so ugly... dummy-fy a block
-
- Modify block object to get the behaviour for a missing block
- """
- block._key = block_n.find('key')
- block.is_dummy_block = lambda: True
- block.is_valid = lambda: False
- block.get_enabled = lambda: False
- for param_n in block_n.findall('param'):
- if param_n['key'] not in block.get_param_keys():
- new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'})
- block.get_params().append(block.get_parent().get_parent().Param(block=block, n=new_param_n))
-
-
-def _dummy_block_add_port(block, key, dir):
- """This is so ugly... Add a port to a dummy-field block"""
- port_n = odict({'name': '?', 'key': key, 'type': ''})
- port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir)
- if port.is_source():
- block.get_sources().append(port)
- else:
- block.get_sinks().append(port)
- return port
diff --git a/grc/base/Param.py b/grc/base/Param.py
deleted file mode 100644
index 34dd36e790..0000000000
--- a/grc/base/Param.py
+++ /dev/null
@@ -1,205 +0,0 @@
-"""
-Copyright 2008-2011 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 odict
-from Element import Element
-
-def _get_keys(lst): return [elem.get_key() for elem in lst]
-def _get_elem(lst, key):
- try: return lst[_get_keys(lst).index(key)]
- except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst))
-
-class Option(Element):
-
- def __init__(self, param, n):
- Element.__init__(self, param)
- self._name = n.find('name')
- self._key = n.find('key')
- self._opts = dict()
- opts = n.findall('opt')
- #test against opts when non enum
- if not self.get_parent().is_enum() and opts:
- raise Exception, 'Options for non-enum types cannot have sub-options'
- #extract opts
- for opt in opts:
- #separate the key:value
- try: key, value = opt.split(':')
- except: raise Exception, 'Error separating "%s" into key:value'%opt
- #test against repeated keys
- if self._opts.has_key(key):
- raise Exception, 'Key "%s" already exists in option'%key
- #store the option
- self._opts[key] = value
-
- def __str__(self): return 'Option %s(%s)'%(self.get_name(), self.get_key())
- def get_name(self): return self._name
- def get_key(self): return self._key
-
- ##############################################
- # Access Opts
- ##############################################
- def get_opt_keys(self): return self._opts.keys()
- def get_opt(self, key): return self._opts[key]
- def get_opts(self): return self._opts.values()
-
-class Param(Element):
-
- def __init__(self, block, n):
- """
- Make a new param from nested data.
-
- Args:
- block: the parent element
- n: the nested odict
- """
- # if the base key is a valid param key, copy its data and overlay this params data
- base_key = n.find('base_key')
- if base_key and base_key in block.get_param_keys():
- n_expanded = block.get_param(base_key)._n.copy()
- n_expanded.update(n)
- n = n_expanded
- # save odict in case this param will be base for another
- self._n = n
- # parse the data
- self._name = n.find('name')
- self._key = n.find('key')
- value = n.find('value') or ''
- self._type = n.find('type') or 'raw'
- self._hide = n.find('hide') or ''
- self._tab_label = n.find('tab') or block.get_param_tab_labels()[0]
- if not self._tab_label in block.get_param_tab_labels():
- block.get_param_tab_labels().append(self._tab_label)
- #build the param
- Element.__init__(self, block)
- #create the Option objects from the n data
- self._options = list()
- for option in map(lambda o: Option(param=self, n=o), n.findall('option')):
- key = option.get_key()
- #test against repeated keys
- if key in self.get_option_keys():
- raise Exception, 'Key "%s" already exists in options'%key
- #store the option
- self.get_options().append(option)
- #test the enum options
- if self.is_enum():
- #test against options with identical keys
- if len(set(self.get_option_keys())) != len(self.get_options()):
- raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys()
- #test against inconsistent keys in options
- opt_keys = self.get_options()[0].get_opt_keys()
- for option in self.get_options():
- if set(opt_keys) != set(option.get_opt_keys()):
- raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys
- #if a value is specified, it must be in the options keys
- self._value = value if value or value in self.get_option_keys() else self.get_option_keys()[0]
- if self.get_value() not in self.get_option_keys():
- raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys())
- else: self._value = value or ''
- self._default = value
-
- def validate(self):
- """
- Validate the param.
- The value must be evaluated and type must a possible type.
- """
- Element.validate(self)
- if self.get_type() not in self.get_types():
- self.add_error_message('Type "%s" is not a possible type.'%self.get_type())
-
- def get_evaluated(self): raise NotImplementedError
-
- def to_code(self):
- """
- Convert the value to code.
- @throw NotImplementedError
- """
- raise NotImplementedError
-
- def get_types(self):
- """
- Get a list of all possible param types.
- @throw NotImplementedError
- """
- raise NotImplementedError
-
- def get_color(self): return '#FFFFFF'
- def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key())
- def is_param(self): return True
- def get_name(self): return self.get_parent().resolve_dependencies(self._name).strip()
- def get_key(self): return self._key
- def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip()
-
- def get_value(self):
- value = self._value
- if self.is_enum() and value not in self.get_option_keys():
- value = self.get_option_keys()[0]
- self.set_value(value)
- return value
-
- def set_value(self, value): self._value = str(value) #must be a string
-
- def set_default(self, value):
- if self._default == self._value:
- self.set_value(value)
- self._default = str(value)
-
- def get_type(self): return self.get_parent().resolve_dependencies(self._type)
- def get_tab_label(self): return self._tab_label
- def is_enum(self): return self._type == 'enum'
-
- def __repr__(self):
- """
- Get the repr (nice string format) for this param.
- Just return the value (special case enum).
- Derived classes can handle complex formatting.
-
- Returns:
- the string representation
- """
- if self.is_enum(): return self.get_option(self.get_value()).get_name()
- return self.get_value()
-
- ##############################################
- # Access Options
- ##############################################
- def get_option_keys(self): return _get_keys(self.get_options())
- def get_option(self, key): return _get_elem(self.get_options(), key)
- def get_options(self): return self._options
-
- ##############################################
- # Access Opts
- ##############################################
- def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys()
- def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key)
- def get_opts(self): return self.get_option(self.get_value()).get_opts()
-
- ##############################################
- ## Import/Export Methods
- ##############################################
- def export_data(self):
- """
- Export this param's key/value.
-
- Returns:
- a nested data odict
- """
- n = odict()
- n['key'] = self.get_key()
- n['value'] = self.get_value()
- return n
diff --git a/grc/base/Platform.py b/grc/base/Platform.py
deleted file mode 100644
index 0cc3fcf1dd..0000000000
--- a/grc/base/Platform.py
+++ /dev/null
@@ -1,271 +0,0 @@
-"""
-Copyright 2008-2011 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
-"""
-
-import os
-import sys
-from .. base import ParseXML, odict
-from Element import Element as _Element
-from FlowGraph import FlowGraph as _FlowGraph
-from Connection import Connection as _Connection
-from Block import Block as _Block
-from Port import Port as _Port
-from Param import Param as _Param
-from Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD, DOMAIN_DTD
-
-
-class Platform(_Element):
- def __init__(self, name, version, key,
- block_paths, block_dtd, default_flow_graph, generator,
- license='', website=None, colors=None):
- """
- Make a platform from the arguments.
-
- Args:
- name: the platform name
- version: the version string
- key: the unique platform key
- block_paths: the file paths to blocks in this platform
- block_dtd: the dtd validator for xml block wrappers
- default_flow_graph: the default flow graph file path
- generator: the generator class for this platform
- colors: a list of title, color_spec tuples
- license: a multi-line license (first line is copyright)
- website: the website url for this platform
-
- Returns:
- a platform object
- """
- _Element.__init__(self)
- self._name = name
- # Save the verion string to the first
- self._version = version[0]
- self._version_major = version[1]
- self._version_api = version[2]
- self._version_minor = version[3]
- self._version_short = version[1] + "." + version[2] + "." + version[3]
-
- self._key = key
- self._license = license
- self._website = website
- self._block_paths = list(set(block_paths))
- self._block_dtd = block_dtd
- self._default_flow_graph = default_flow_graph
- self._generator = generator
- self._colors = colors or []
- #create a dummy flow graph for the blocks
- self._flow_graph = _Element(self)
-
- self._blocks = None
- self._blocks_n = None
- self._category_trees_n = None
- self._domains = dict()
- self._connection_templates = dict()
- self.load_blocks()
-
- def load_blocks(self):
- """load the blocks and block tree from the search paths"""
- # reset
- self._blocks = odict()
- self._blocks_n = odict()
- self._category_trees_n = list()
- self._domains.clear()
- self._connection_templates.clear()
- ParseXML.xml_failures.clear()
- # try to parse and load blocks
- for xml_file in self.iter_xml_files():
- try:
- if xml_file.endswith("block_tree.xml"):
- self.load_category_tree_xml(xml_file)
- elif xml_file.endswith('domain.xml'):
- self.load_domain_xml(xml_file)
- else:
- self.load_block_xml(xml_file)
- except ParseXML.XMLSyntaxError as e:
- # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file)
- pass
- except Exception as e:
- print >> sys.stderr, 'Warning: XML parsing failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file)
-
- def iter_xml_files(self):
- """Iterator for block descriptions and category trees"""
- get_path = lambda x: os.path.abspath(os.path.expanduser(x))
- for block_path in map(get_path, self._block_paths):
- if os.path.isfile(block_path):
- yield block_path
- elif os.path.isdir(block_path):
- for dirpath, dirnames, filenames in os.walk(block_path):
- for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)):
- yield os.path.join(dirpath, filename)
-
- def load_block_xml(self, xml_file):
- """Load block description from xml file"""
- # validate and import
- ParseXML.validate_dtd(xml_file, self._block_dtd)
- n = ParseXML.from_file(xml_file).find('block')
- n['block_wrapper_path'] = xml_file # inject block wrapper path
- # get block instance and add it to the list of blocks
- block = self.Block(self._flow_graph, n)
- key = block.get_key()
- if key in self._blocks:
- print >> sys.stderr, 'Warning: Block with key "%s" already exists.\n\tIgnoring: %s' % (key, xml_file)
- else: # store the block
- self._blocks[key] = block
- self._blocks_n[key] = n
- return block
-
- def load_category_tree_xml(self, xml_file):
- """Validate and parse category tree file and add it to list"""
- ParseXML.validate_dtd(xml_file, BLOCK_TREE_DTD)
- n = ParseXML.from_file(xml_file).find('cat')
- self._category_trees_n.append(n)
-
- def load_domain_xml(self, xml_file):
- """Load a domain properties and connection templates from XML"""
- ParseXML.validate_dtd(xml_file, DOMAIN_DTD)
- n = ParseXML.from_file(xml_file).find('domain')
-
- key = n.find('key')
- if not key:
- print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: %s' % xml_file
- return
- if key in self.get_domains(): # test against repeated keys
- print >> sys.stderr, 'Warning: Domain with key "%s" already exists.\n\tIgnoring: %s' % (key, xml_file)
- return
-
- to_bool = lambda s, d: d if s is None else \
- s.lower() not in ('false', 'off', '0', '')
-
- color = n.find('color') or ''
- try:
- import gtk # ugly but handy
- gtk.gdk.color_parse(color)
- except (ValueError, ImportError):
- if color: # no color is okay, default set in GUI
- print >> sys.stderr, 'Warning: Can\'t parse color code "%s" for domain "%s" ' % (color, key)
- color = None
-
- self._domains[key] = dict(
- name=n.find('name') or key,
- multiple_sinks=to_bool(n.find('multiple_sinks'), True),
- multiple_sources=to_bool(n.find('multiple_sources'), False),
- color=color
- )
- for connection_n in n.findall('connection'):
- key = (connection_n.find('source_domain'), connection_n.find('sink_domain'))
- if not all(key):
- print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t%s' % xml_file
- elif key in self._connection_templates:
- print >> sys.stderr, 'Warning: Connection template "%s" already exists.\n\t%s' % (key, xml_file)
- else:
- self._connection_templates[key] = connection_n.find('make') or ''
-
- def parse_flow_graph(self, flow_graph_file):
- """
- Parse a saved flow graph file.
- Ensure that the file exists, and passes the dtd check.
-
- Args:
- flow_graph_file: the flow graph file
-
- Returns:
- nested data
- @throws exception if the validation fails
- """
- flow_graph_file = flow_graph_file or self._default_flow_graph
- open(flow_graph_file, 'r') # test open
- ParseXML.validate_dtd(flow_graph_file, FLOW_GRAPH_DTD)
- return ParseXML.from_file(flow_graph_file)
-
- 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.find('name')]
- block_tree.add_block(parent)
- #recursive call to load sub categories
- map(lambda c: load_category(c, parent), cat_n.findall('cat'))
- #add blocks in this category
- for block_key in cat_n.findall('block'):
- if block_key not in self.get_block_keys():
- print >> sys.stderr, 'Warning: Block key "%s" not found when loading category tree.' % (block_key)
- continue
- block = self.get_block(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 self.get_blocks():
- #blocks with empty categories are hidden
- if not block.get_category(): continue
- block_tree.add_block(block.get_category(), block)
-
- def __str__(self): return 'Platform - %s(%s)'%(self.get_key(), self.get_name())
-
- def is_platform(self): return True
-
- def get_new_flow_graph(self): return self.FlowGraph(platform=self)
-
- def get_generator(self): return self._generator
-
- ##############################################
- # Access Blocks
- ##############################################
- def get_block_keys(self): return self._blocks.keys()
- def get_block(self, key): return self._blocks[key]
- def get_blocks(self): return self._blocks.values()
- def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key])
-
- def get_domains(self): return self._domains
- def get_domain(self, key): return self._domains.get(key)
- def get_connection_templates(self): return self._connection_templates
-
- def get_name(self): return self._name
- def get_version(self): return self._version
- def get_version_major(self): return self._version_major
- def get_version_api(self): return self._version_api
- def get_version_minor(self): return self._version_minor
- def get_version_short(self): return self._version_short
-
- def get_key(self): return self._key
- def get_license(self): return self._license
- def get_website(self): return self._website
- def get_colors(self): return self._colors
- def get_block_paths(self): return self._block_paths
-
- ##############################################
- # Constructors
- ##############################################
- FlowGraph = _FlowGraph
- Connection = _Connection
- Block = _Block
- Port = _Port
- Param = _Param
diff --git a/grc/base/Port.py b/grc/base/Port.py
deleted file mode 100644
index d1c35163f5..0000000000
--- a/grc/base/Port.py
+++ /dev/null
@@ -1,136 +0,0 @@
-"""
-Copyright 2008-2011 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 Element import Element
-from . Constants import GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN
-
-class Port(Element):
-
- def __init__(self, block, n, dir):
- """
- Make a new port from nested data.
-
- Args:
- block: the parent element
- n: the nested odict
- dir: the direction source or sink
- """
- #build the port
- Element.__init__(self, block)
- #grab the data
- self._name = n['name']
- self._key = n['key']
- self._type = n['type'] or ''
- self._domain = n['domain']
- self._hide = n.find('hide') or ''
- self._dir = dir
- self._hide_evaluated = False # updated on rewrite()
-
- def validate(self):
- """
- Validate the port.
- The port must be non-empty and type must a possible type.
- """
- Element.validate(self)
- if self.get_type() not in self.get_types():
- self.add_error_message('Type "%s" is not a possible type.' % self.get_type())
- platform = self.get_parent().get_parent().get_parent()
- if self.get_domain() not in platform.get_domains():
- self.add_error_message('Domain key "%s" is not registered.' % self.get_domain())
-
- def rewrite(self):
- """resolve dependencies in for type and hide"""
- Element.rewrite(self)
- hide = self.get_parent().resolve_dependencies(self._hide).strip().lower()
- self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide)
- # update domain if was deduced from (dynamic) port type
- type_ = self.get_type()
- if self._domain == GR_STREAM_DOMAIN and type_ == "message":
- self._domain = GR_MESSAGE_DOMAIN
- self._key = self._name
- if self._domain == GR_MESSAGE_DOMAIN and type_ != "message":
- self._domain = GR_STREAM_DOMAIN
- self._key = '0' # is rectified in rewrite()
-
- def __str__(self):
- if self.is_source():
- return 'Source - %s(%s)'%(self.get_name(), self.get_key())
- if self.is_sink():
- return 'Sink - %s(%s)'%(self.get_name(), self.get_key())
-
- def get_types(self):
- """
- Get a list of all possible port types.
- @throw NotImplementedError
- """
- raise NotImplementedError
-
- def is_port(self): return True
- def get_color(self): return '#FFFFFF'
- def get_name(self):
- number = ''
- if self.get_type() == 'bus':
- busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui())
- number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports()))
- return self._name + number
-
- def get_key(self): return self._key
- def is_sink(self): return self._dir == 'sink'
- def is_source(self): return self._dir == 'source'
- def get_type(self): return self.get_parent().resolve_dependencies(self._type)
- def get_domain(self): return self._domain
- def get_hide(self): return self._hide_evaluated
-
- def get_connections(self):
- """
- Get all connections that use this port.
-
- Returns:
- a list of connection objects
- """
- connections = self.get_parent().get_parent().get_connections()
- connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections)
- return connections
-
- def get_enabled_connections(self):
- """
- Get all enabled connections that use this port.
-
- Returns:
- a list of connection objects
- """
- return filter(lambda c: c.get_enabled(), self.get_connections())
-
- def get_associated_ports(self):
- if not self.get_type() == 'bus':
- return [self]
- else:
- if self.is_source():
- get_ports = self.get_parent().get_sources
- bus_structure = self.get_parent().current_bus_structure['source']
- else:
- get_ports = self.get_parent().get_sinks
- bus_structure = self.get_parent().current_bus_structure['sink']
-
- ports = [i for i in get_ports() if not i.get_type() == 'bus']
- if bus_structure:
- busses = [i for i in get_ports() if i.get_type() == 'bus']
- bus_index = busses.index(self)
- ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports)
- return ports
diff --git a/grc/base/__init__.py b/grc/base/__init__.py
deleted file mode 100644
index 2682db8125..0000000000
--- a/grc/base/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-Copyright 2009 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 odict import odict
diff --git a/grc/blocks/blks2_error_rate.xml b/grc/blocks/blks2_error_rate.xml
deleted file mode 100644
index 91a303206d..0000000000
--- a/grc/blocks/blks2_error_rate.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Error Rate:
-## Custom blks2 block
-###################################################
- -->
-<block>
- <name>Error Rate</name>
- <key>blks2_error_rate</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.error_rate(
- type=$type,
- win_size=$win_size,
- bits_per_symbol=$bits_per_symbol,
-)</make>
- <param>
- <name>Type</name>
- <key>type</key>
- <type>enum</type>
- <option>
- <name>Bit Error Rate</name>
- <key>'BER'</key>
- <opt>hide_bps:</opt>
- </option>
- <option>
- <name>Symbol Error Rate</name>
- <key>'SER'</key>
- <opt>hide_bps:all</opt>
- </option>
- </param>
- <param>
- <name>Window Size</name>
- <key>win_size</key>
- <value>1000</value>
- <type>int</type>
- </param>
- <param>
- <name>Bits per Symbol</name>
- <key>bits_per_symbol</key>
- <value>2</value>
- <type>int</type>
- <hide>$type.hide_bps</hide>
- </param>
- <sink>
- <name>ref</name>
- <type>byte</type>
- </sink>
- <sink>
- <name>in</name>
- <type>byte</type>
- </sink>
- <source>
- <name>out</name>
- <type>float</type>
- </source>
- <doc>
-Calculate the bit error rate (BER) or the symbol error rate (SER) over a number of samples given by the window size. \
-The actual window size will start at size one and grow to the full window size as new samples arrive. \
-Once the window has reached full size, old samples are shifted out of the window and new samples shfited in.
-
-The error block compares the input byte stream to the reference byte stream. \
-For example, the reference byte stream could be the input to a modulator, \
-and the input byte stream could be the output of a modulator.
-
-Each byte in the incoming stream represents one symbol. \
-The bits per symbol parameter is only useful for calculating the BER.
- </doc>
-</block>
diff --git a/grc/blocks/blks2_packet_decoder.xml b/grc/blocks/blks2_packet_decoder.xml
deleted file mode 100644
index 07b0d1f2eb..0000000000
--- a/grc/blocks/blks2_packet_decoder.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Packet Decoder
-###################################################
- -->
-<block>
- <name>Packet Decoder</name>
- <key>blks2_packet_decoder</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.packet_demod_$(type.fcn)(grc_blks2.packet_decoder(
- access_code=$access_code,
- threshold=$threshold,
- callback=lambda ok, payload: self.$(id).recv_pkt(ok, payload),
- ),
-)</make>
- <param>
- <name>Output Type</name>
- <key>type</key>
- <value>float</value>
- <type>enum</type>
- <option>
- <name>Complex</name>
- <key>complex</key>
- <opt>fcn:c</opt>
- </option>
- <option>
- <name>Float</name>
- <key>float</key>
- <opt>fcn:f</opt>
- </option>
- <option>
- <name>Int</name>
- <key>int</key>
- <opt>fcn:i</opt>
- </option>
- <option>
- <name>Short</name>
- <key>short</key>
- <opt>fcn:s</opt>
- </option>
- <option>
- <name>Byte</name>
- <key>byte</key>
- <opt>fcn:b</opt>
- </option>
- </param>
- <param>
- <name>Access Code</name>
- <key>access_code</key>
- <value></value>
- <type>string</type>
- </param>
- <param>
- <name>Threshold</name>
- <key>threshold</key>
- <value>-1</value>
- <type>int</type>
- </param>
- <sink>
- <name>in</name>
- <type>byte</type>
- </sink>
- <source>
- <name>out</name>
- <type>$type</type>
- </source>
- <doc>
-Packet decoder block, for use with the gnuradio demodulator blocks: gmsk, psk, qam.
-
-Access Code: string of 1's and 0's, leave blank for automatic.
-
-Threshold: -1 for automatic.
- </doc>
-</block>
diff --git a/grc/blocks/blks2_packet_encoder.xml b/grc/blocks/blks2_packet_encoder.xml
deleted file mode 100644
index 88e1ba350c..0000000000
--- a/grc/blocks/blks2_packet_encoder.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Packet Encoder
-###################################################
- -->
-<block>
- <name>Packet Encoder</name>
- <key>blks2_packet_encoder</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.packet_mod_$(type.fcn)(grc_blks2.packet_encoder(
- samples_per_symbol=$samples_per_symbol,
- bits_per_symbol=$bits_per_symbol,
- preamble=$preamble,
- access_code=$access_code,
- pad_for_usrp=$pad_for_usrp,
- ),
- payload_length=$payload_length,
-)</make>
- <param>
- <name>Input Type</name>
- <key>type</key>
- <value>float</value>
- <type>enum</type>
- <option>
- <name>Complex</name>
- <key>complex</key>
- <opt>fcn:c</opt>
- </option>
- <option>
- <name>Float</name>
- <key>float</key>
- <opt>fcn:f</opt>
- </option>
- <option>
- <name>Int</name>
- <key>int</key>
- <opt>fcn:i</opt>
- </option>
- <option>
- <name>Short</name>
- <key>short</key>
- <opt>fcn:s</opt>
- </option>
- <option>
- <name>Byte</name>
- <key>byte</key>
- <opt>fcn:b</opt>
- </option>
- </param>
- <param>
- <name>Samples/Symbol</name>
- <key>samples_per_symbol</key>
- <type>int</type>
- </param>
- <param>
- <name>Bits/Symbol</name>
- <key>bits_per_symbol</key>
- <type>int</type>
- </param>
- <param>
- <name>Preamble</name>
- <key>preamble</key>
- <value></value>
- <type>string</type>
- </param>
- <param>
- <name>Access Code</name>
- <key>access_code</key>
- <value></value>
- <type>string</type>
- </param>
- <param>
- <name>Pad for USRP</name>
- <key>pad_for_usrp</key>
- <type>enum</type>
- <option>
- <name>Yes</name>
- <key>True</key>
- </option>
- <option>
- <name>No</name>
- <key>False</key>
- </option>
- </param>
- <param>
- <name>Payload Length</name>
- <key>payload_length</key>
- <value>0</value>
- <type>int</type>
- </param>
- <sink>
- <name>in</name>
- <type>$type</type>
- </sink>
- <source>
- <name>out</name>
- <type>byte</type>
- </source>
- <doc>
-Packet encoder block, for use with the gnuradio modulator blocks: gmsk, dpsk, qam.
-
-Preamble: string of 1's and 0's, leave blank for automatic.
-
-Access Code: string of 1's and 0's, leave blank for automatic.
-
-Payload Length: 0 for automatic.
-
-Bits/Symbol should be set accordingly:
- gmsk -> 1
- dbpsk -> 1
- dqpsk -> 2
- d8psk -> 3
- qam8 -> 3
- qam16 -> 4
- qam64 -> 6
- qam256 -> 8
- </doc>
-</block>
diff --git a/grc/blocks/blks2_selector.xml b/grc/blocks/blks2_selector.xml
deleted file mode 100644
index 2d89df1860..0000000000
--- a/grc/blocks/blks2_selector.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Selector:
-## Custom blks2 block
-###################################################
- -->
-<block>
- <name>Selector</name>
- <key>blks2_selector</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.selector(
- item_size=$type.size*$vlen,
- num_inputs=$num_inputs,
- num_outputs=$num_outputs,
- input_index=$input_index,
- output_index=$output_index,
-)</make>
- <callback>set_input_index(int($input_index))</callback>
- <callback>set_output_index(int($output_index))</callback>
- <param>
- <name>Type</name>
- <key>type</key>
- <type>enum</type>
- <option>
- <name>Complex</name>
- <key>complex</key>
- <opt>size:gr.sizeof_gr_complex</opt>
- </option>
- <option>
- <name>Float</name>
- <key>float</key>
- <opt>size:gr.sizeof_float</opt>
- </option>
- <option>
- <name>Int</name>
- <key>int</key>
- <opt>size:gr.sizeof_int</opt>
- </option>
- <option>
- <name>Short</name>
- <key>short</key>
- <opt>size:gr.sizeof_short</opt>
- </option>
- <option>
- <name>Byte</name>
- <key>byte</key>
- <opt>size:gr.sizeof_char</opt>
- </option>
- </param>
- <param>
- <name>Num Inputs</name>
- <key>num_inputs</key>
- <value>2</value>
- <type>int</type>
- </param>
- <param>
- <name>Num Outputs</name>
- <key>num_outputs</key>
- <value>2</value>
- <type>int</type>
- </param>
- <param>
- <name>Input Index</name>
- <key>input_index</key>
- <value>0</value>
- <type>int</type>
- </param>
- <param>
- <name>Output Index</name>
- <key>output_index</key>
- <value>0</value>
- <type>int</type>
- </param>
- <param>
- <name>Vec Length</name>
- <key>vlen</key>
- <value>1</value>
- <type>int</type>
- </param>
- <check>$vlen &gt; 0</check>
- <sink>
- <name>in</name>
- <type>$type</type>
- <vlen>$vlen</vlen>
- <nports>$num_inputs</nports>
- </sink>
- <source>
- <name>out</name>
- <type>$type</type>
- <vlen>$vlen</vlen>
- <nports>$num_outputs</nports>
- </source>
- <doc>
-Connect the sink at input index to the source at output index. Leave all other ports disconnected.
- </doc>
-</block>
diff --git a/grc/blocks/blks2_tcp_sink.xml b/grc/blocks/blks2_tcp_sink.xml
deleted file mode 100644
index cfe7b42d84..0000000000
--- a/grc/blocks/blks2_tcp_sink.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##TCP Sink: Custom blks2 block
-###################################################
- -->
-<block>
- <name>TCP Sink</name>
- <key>blks2_tcp_sink</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.tcp_sink(
- itemsize=$type.size*$vlen,
- addr=$addr,
- port=$port,
- server=$server,
-)</make>
- <param>
- <name>Input Type</name>
- <key>type</key>
- <type>enum</type>
- <option>
- <name>Complex</name>
- <key>complex</key>
- <opt>size:gr.sizeof_gr_complex</opt>
- </option>
- <option>
- <name>Float</name>
- <key>float</key>
- <opt>size:gr.sizeof_float</opt>
- </option>
- <option>
- <name>Int</name>
- <key>int</key>
- <opt>size:gr.sizeof_int</opt>
- </option>
- <option>
- <name>Short</name>
- <key>short</key>
- <opt>size:gr.sizeof_short</opt>
- </option>
- <option>
- <name>Byte</name>
- <key>byte</key>
- <opt>size:gr.sizeof_char</opt>
- </option>
- </param>
- <param>
- <name>Address</name>
- <key>addr</key>
- <value>127.0.0.1</value>
- <type>string</type>
- </param>
- <param>
- <name>Port</name>
- <key>port</key>
- <value>0</value>
- <type>int</type>
- </param>
- <param>
- <name>Mode</name>
- <key>server</key>
- <value>False</value>
- <type>enum</type>
- <option>
- <name>Server</name>
- <key>True</key>
- </option>
- <option>
- <name>Client</name>
- <key>False</key>
- </option>
- </param>
- <param>
- <name>Vec Length</name>
- <key>vlen</key>
- <value>1</value>
- <type>int</type>
- </param>
- <check>$vlen &gt; 0</check>
- <sink>
- <name>in</name>
- <type>$type</type>
- <vlen>$vlen</vlen>
- </sink>
- <doc>
-In client mode, we attempt to connect to a server at the given address and port. \
-In server mode, we bind a socket to the given address and port and accept the first client.
- </doc>
-</block>
diff --git a/grc/blocks/blks2_tcp_source.xml b/grc/blocks/blks2_tcp_source.xml
deleted file mode 100644
index 6bf742aa00..0000000000
--- a/grc/blocks/blks2_tcp_source.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##TCP Source: Custom blks2 block
-###################################################
- -->
-<block>
- <name>TCP Source</name>
- <key>blks2_tcp_source</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.tcp_source(
- itemsize=$type.size*$vlen,
- addr=$addr,
- port=$port,
- server=$server,
-)</make>
- <param>
- <name>Output Type</name>
- <key>type</key>
- <type>enum</type>
- <option>
- <name>Complex</name>
- <key>complex</key>
- <opt>size:gr.sizeof_gr_complex</opt>
- </option>
- <option>
- <name>Float</name>
- <key>float</key>
- <opt>size:gr.sizeof_float</opt>
- </option>
- <option>
- <name>Int</name>
- <key>int</key>
- <opt>size:gr.sizeof_int</opt>
- </option>
- <option>
- <name>Short</name>
- <key>short</key>
- <opt>size:gr.sizeof_short</opt>
- </option>
- <option>
- <name>Byte</name>
- <key>byte</key>
- <opt>size:gr.sizeof_char</opt>
- </option>
- </param>
- <param>
- <name>Address</name>
- <key>addr</key>
- <value>127.0.0.1</value>
- <type>string</type>
- </param>
- <param>
- <name>Port</name>
- <key>port</key>
- <value>0</value>
- <type>int</type>
- </param>
- <param>
- <name>Mode</name>
- <key>server</key>
- <value>True</value>
- <type>enum</type>
- <option>
- <name>Server</name>
- <key>True</key>
- </option>
- <option>
- <name>Client</name>
- <key>False</key>
- </option>
- </param>
- <param>
- <name>Vec Length</name>
- <key>vlen</key>
- <value>1</value>
- <type>int</type>
- </param>
- <check>$vlen &gt; 0</check>
- <source>
- <name>out</name>
- <type>$type</type>
- <vlen>$vlen</vlen>
- </source>
- <doc>
-In client mode, we attempt to connect to a server at the given address and port. \
-In server mode, we bind a socket to the given address and port and accept the first client.
- </doc>
-</block>
diff --git a/grc/blocks/blks2_valve.xml b/grc/blocks/blks2_valve.xml
deleted file mode 100644
index 47c553523f..0000000000
--- a/grc/blocks/blks2_valve.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Valve:
-## Custom blks2 block
-###################################################
- -->
-<block>
- <name>Valve</name>
- <key>blks2_valve</key>
- <import>from grc_gnuradio import blks2 as grc_blks2</import>
- <make>grc_blks2.valve(item_size=$type.size*$vlen, open=bool($open))</make>
- <callback>set_open(bool($open))</callback>
- <param>
- <name>Type</name>
- <key>type</key>
- <type>enum</type>
- <option>
- <name>Complex</name>
- <key>complex</key>
- <opt>size:gr.sizeof_gr_complex</opt>
- </option>
- <option>
- <name>Float</name>
- <key>float</key>
- <opt>size:gr.sizeof_float</opt>
- </option>
- <option>
- <name>Int</name>
- <key>int</key>
- <opt>size:gr.sizeof_int</opt>
- </option>
- <option>
- <name>Short</name>
- <key>short</key>
- <opt>size:gr.sizeof_short</opt>
- </option>
- <option>
- <name>Byte</name>
- <key>byte</key>
- <opt>size:gr.sizeof_char</opt>
- </option>
- </param>
- <param>
- <name>Open</name>
- <key>open</key>
- <value>0</value>
- <type>raw</type>
- </param>
- <param>
- <name>Vec Length</name>
- <key>vlen</key>
- <value>1</value>
- <type>int</type>
- </param>
- <check>$vlen &gt; 0</check>
- <sink>
- <name>in</name>
- <type>$type</type>
- <vlen>$vlen</vlen>
- <nports>$num_inputs</nports>
- </sink>
- <source>
- <name>out</name>
- <type>$type</type>
- <vlen>$vlen</vlen>
- <nports>$num_outputs</nports>
- </source>
- <doc>
-Connect output to input when valve is closed (not open).
- </doc>
-</block>
diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml
index d07c52e9c5..a8775d6872 100644
--- a/grc/blocks/block_tree.xml
+++ b/grc/blocks/block_tree.xml
@@ -20,23 +20,6 @@
<block>note</block>
<block>import</block>
-
- <block>blks2_selector</block>
- <block>blks2_valve</block>
- <block>blks2_error_rate</block>
-
- <block>xmlrpc_server</block>
- <block>xmlrpc_client</block>
- </cat>
- <cat>
- <name>Networking Tools</name>
- <block>blks2_tcp_source</block>
- <block>blks2_tcp_sink</block>
- </cat>
- <cat>
- <name>Packet Operators</name>
- <block>blks2_packet_decoder</block>
- <block>blks2_packet_encoder</block>
</cat>
<cat>
<name>Variables</name>
diff --git a/grc/blocks/xmlrpc_client.xml b/grc/blocks/xmlrpc_client.xml
deleted file mode 100644
index dc4d154d14..0000000000
--- a/grc/blocks/xmlrpc_client.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Simple XMLRPC Client
-###################################################
- -->
-<block>
- <name>XMLRPC Client</name>
- <key>xmlrpc_client</key>
- <import>import xmlrpclib</import>
- <make>xmlrpclib.Server('http://$(addr()):$(port)')</make>
- <callback>$(callback())($variable)</callback>
- <param>
- <name>Address</name>
- <key>addr</key>
- <value>localhost</value>
- <type>string</type>
- </param>
- <param>
- <name>Port</name>
- <key>port</key>
- <value>8080</value>
- <type>int</type>
- </param>
- <param>
- <name>Callback</name>
- <key>callback</key>
- <value>set_</value>
- <type>string</type>
- </param>
- <param>
- <name>Variable</name>
- <key>variable</key>
- <type>raw</type>
- </param>
- <doc>
-This block will create an XMLRPC client. \
-The client will execute the callback on the server when the variable is changed. \
-The callback should be a the name of a function registered on the server. \
-The variable should be an expression containing a the name of a variable in flow graph.
- </doc>
-</block>
diff --git a/grc/blocks/xmlrpc_server.xml b/grc/blocks/xmlrpc_server.xml
deleted file mode 100644
index 602d444161..0000000000
--- a/grc/blocks/xmlrpc_server.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Simple XMLRPC Server
-###################################################
- -->
-<block>
- <name>XMLRPC Server</name>
- <key>xmlrpc_server</key>
- <import>import SimpleXMLRPCServer</import>
- <import>import threading</import>
- <make>SimpleXMLRPCServer.SimpleXMLRPCServer(($addr, $port), allow_none=True)
-self.$(id).register_instance(self)
-self.$(id)_thread = threading.Thread(target=self.$(id).serve_forever)
-self.$(id)_thread.daemon = True
-self.$(id)_thread.start()</make>
- <param>
- <name>Address</name>
- <key>addr</key>
- <value>localhost</value>
- <type>string</type>
- </param>
- <param>
- <name>Port</name>
- <key>port</key>
- <value>8080</value>
- <type>int</type>
- </param>
- <doc>
-This block will start an XMLRPC server. \
-The server provides access to the run, start, stop, wait functions of the flow graph. \
-The server also provides access to the variable callbacks in the flow graph. \
-Ex: If the variable is called freq, the function provided by the server will be called set_freq(new_freq).
-
-Example client in python:
-
-import xmlrpclib
-s = xmlrpclib.Server('http://localhost:8080')
-s.set_freq(5000)
- </doc>
-</block>
diff --git a/grc/checks.py b/grc/checks.py
new file mode 100755
index 0000000000..fd0e5de06a
--- /dev/null
+++ b/grc/checks.py
@@ -0,0 +1,80 @@
+# Copyright 2009-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
+
+import os
+import warnings
+
+
+GR_IMPORT_ERROR_MESSAGE = """\
+Cannot import gnuradio.
+
+Is the model path environment variable set correctly?
+ All OS: PYTHONPATH
+
+Is the library path environment variable set correctly?
+ Linux: LD_LIBRARY_PATH
+ Windows: PATH
+ MacOSX: DYLD_LIBRARY_PATH
+"""
+
+
+def die(error, message):
+ msg = "{0}\n\n({1})".format(message, error)
+ try:
+ import gtk
+ d = gtk.MessageDialog(
+ type=gtk.MESSAGE_ERROR,
+ buttons=gtk.BUTTONS_CLOSE,
+ message_format=msg,
+ )
+ d.set_title(type(error).__name__)
+ d.run()
+ exit(1)
+ except ImportError:
+ exit(type(error).__name__ + '\n\n' + msg)
+
+
+def check_gtk():
+ try:
+ warnings.filterwarnings("error")
+ import pygtk
+ pygtk.require('2.0')
+ import gtk
+ gtk.init_check()
+ warnings.filterwarnings("always")
+ except Exception as err:
+ die(err, "Failed to initialize GTK. If you are running over ssh, "
+ "did you enable X forwarding and start ssh with -X?")
+
+
+def check_gnuradio_import():
+ try:
+ from gnuradio import gr
+ except ImportError as err:
+ die(err, GR_IMPORT_ERROR_MESSAGE)
+
+
+def check_blocks_path():
+ if 'GR_DONT_LOAD_PREFS' in os.environ and not os.environ.get('GRC_BLOCKS_PATH', ''):
+ die(EnvironmentError("No block definitions available"),
+ "Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH.")
+
+
+def do_all():
+ check_gnuradio_import()
+ check_gtk()
+ check_blocks_path()
diff --git a/grc/core/Block.py b/grc/core/Block.py
new file mode 100644
index 0000000000..cb4eb0db61
--- /dev/null
+++ b/grc/core/Block.py
@@ -0,0 +1,846 @@
+"""
+Copyright 2008-2015 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
+"""
+
+import collections
+import itertools
+
+from Cheetah.Template import Template
+
+from .utils import epy_block_io, odict
+from . Constants import (
+ BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI,
+ ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB,
+ BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS,
+ BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED
+)
+from . Element import Element
+from . FlowGraph import _variable_matcher
+
+
+def _get_keys(lst):
+ return [elem.get_key() for elem in lst]
+
+
+def _get_elem(lst, key):
+ try:
+ return lst[_get_keys(lst).index(key)]
+ except ValueError:
+ raise ValueError('Key "{}" not found in {}.'.format(key, _get_keys(lst)))
+
+
+class Block(Element):
+
+ is_block = True
+
+ def __init__(self, flow_graph, n):
+ """
+ Make a new block from nested data.
+
+ Args:
+ flow: graph the parent element
+ n: the nested odict
+
+ Returns:
+ block a new block
+ """
+ # Grab the data
+ self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '')
+ self._imports = map(lambda i: i.strip(), n.findall('import'))
+ self._make = n.find('make')
+ self._var_make = n.find('var_make')
+ self._checks = n.findall('check')
+ self._callbacks = n.findall('callback')
+ self._bus_structure_source = n.find('bus_structure_source') or ''
+ self._bus_structure_sink = n.find('bus_structure_sink') or ''
+ self.port_counters = [itertools.count(), itertools.count()]
+
+ # Build the block
+ Element.__init__(self, flow_graph)
+
+ # Grab the data
+ params = n.findall('param')
+ sources = n.findall('source')
+ sinks = n.findall('sink')
+ self._name = n.find('name')
+ self._key = n.find('key')
+ self._category = n.find('category') or ''
+ self._flags = n.find('flags') or ''
+ # Backwards compatibility
+ if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags:
+ self._flags += BLOCK_FLAG_THROTTLE
+ self._grc_source = n.find('grc_source') or ''
+ self._block_wrapper_path = n.find('block_wrapper_path')
+ self._bussify_sink = n.find('bus_sink')
+ self._bussify_source = n.find('bus_source')
+ self._var_value = n.find('var_value') or '$value'
+
+ # Get list of param tabs
+ n_tabs = n.find('param_tab_order') or None
+ self._param_tab_labels = n_tabs.findall('tab') if n_tabs is not None else [DEFAULT_PARAM_TAB]
+
+ # Create the param objects
+ self._params = list()
+
+ # Add the id param
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({
+ 'name': 'ID',
+ 'key': 'id',
+ 'type': 'id',
+ })
+ ))
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({
+ 'name': 'Enabled',
+ 'key': '_enabled',
+ 'type': 'raw',
+ 'value': 'True',
+ 'hide': 'all',
+ })
+ ))
+ for param in itertools.imap(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params):
+ key = param.get_key()
+ # Test against repeated keys
+ if key in self.get_param_keys():
+ raise Exception('Key "{}" already exists in params'.format(key))
+ # Store the param
+ self.get_params().append(param)
+ # Create the source objects
+ self._sources = list()
+ for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources):
+ key = source.get_key()
+ # Test against repeated keys
+ if key in self.get_source_keys():
+ raise Exception('Key "{}" already exists in sources'.format(key))
+ # Store the port
+ self.get_sources().append(source)
+ self.back_ofthe_bus(self.get_sources())
+ # Create the sink objects
+ self._sinks = list()
+ for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks):
+ key = sink.get_key()
+ # Test against repeated keys
+ if key in self.get_sink_keys():
+ raise Exception('Key "{}" already exists in sinks'.format(key))
+ # Store the port
+ self.get_sinks().append(sink)
+ self.back_ofthe_bus(self.get_sinks())
+ self.current_bus_structure = {'source': '', 'sink': ''}
+
+ # Virtual source/sink and pad source/sink blocks are
+ # indistinguishable from normal GR blocks. Make explicit
+ # checks for them here since they have no work function or
+ # buffers to manage.
+ is_virtual_or_pad = self._key in (
+ "virtual_source", "virtual_sink", "pad_source", "pad_sink")
+ is_variable = self._key.startswith('variable')
+
+ # Disable blocks that are virtual/pads or variables
+ if is_virtual_or_pad or is_variable:
+ self._flags += BLOCK_FLAG_DISABLE_BYPASS
+
+ if not (is_virtual_or_pad or is_variable or self._key == 'options'):
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({'name': 'Block Alias',
+ 'key': 'alias',
+ 'type': 'string',
+ 'hide': 'part',
+ 'tab': ADVANCED_PARAM_TAB
+ })
+ ))
+
+ if (len(sources) or len(sinks)) and not is_virtual_or_pad:
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({'name': 'Core Affinity',
+ 'key': 'affinity',
+ 'type': 'int_vector',
+ 'hide': 'part',
+ 'tab': ADVANCED_PARAM_TAB
+ })
+ ))
+ if len(sources) and not is_virtual_or_pad:
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({'name': 'Min Output Buffer',
+ 'key': 'minoutbuf',
+ 'type': 'int',
+ 'hide': 'part',
+ 'value': '0',
+ 'tab': ADVANCED_PARAM_TAB
+ })
+ ))
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({'name': 'Max Output Buffer',
+ 'key': 'maxoutbuf',
+ 'type': 'int',
+ 'hide': 'part',
+ 'value': '0',
+ 'tab': ADVANCED_PARAM_TAB
+ })
+ ))
+
+ self.get_params().append(self.get_parent().get_parent().Param(
+ block=self,
+ n=odict({'name': 'Comment',
+ 'key': 'comment',
+ 'type': '_multiline',
+ 'hide': 'part',
+ 'value': '',
+ 'tab': ADVANCED_PARAM_TAB
+ })
+ ))
+
+ self._epy_source_hash = -1 # for epy blocks
+ self._epy_reload_error = None
+
+ if self._bussify_sink:
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'sink')
+ if self._bussify_source:
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'source')
+
+ def get_bus_structure(self, direction):
+ if direction == 'source':
+ bus_structure = self._bus_structure_source
+ else:
+ bus_structure = self._bus_structure_sink
+
+ bus_structure = self.resolve_dependencies(bus_structure)
+
+ if not bus_structure:
+ return '' # TODO: Don't like empty strings. should change this to None eventually
+
+ try:
+ clean_bus_structure = self.get_parent().evaluate(bus_structure)
+ return clean_bus_structure
+ except:
+ return ''
+
+ def validate(self):
+ """
+ Validate this block.
+ Call the base class validate.
+ Evaluate the checks: each check must evaluate to True.
+ """
+ Element.validate(self)
+ # Evaluate the checks
+ for check in self._checks:
+ check_res = self.resolve_dependencies(check)
+ try:
+ if not self.get_parent().evaluate(check_res):
+ self.add_error_message('Check "{}" failed.'.format(check))
+ except:
+ self.add_error_message('Check "{}" did not evaluate.'.format(check))
+
+ # For variables check the value (only if var_value is used
+ if _variable_matcher.match(self.get_key()) and self._var_value != '$value':
+ value = self._var_value
+ try:
+ value = self.get_var_value()
+ self.get_parent().evaluate(value)
+ except Exception as err:
+ self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err))
+
+ # check if this is a GUI block and matches the selected generate option
+ current_generate_option = self.get_parent().get_option('generate_options')
+
+ def check_generate_mode(label, flag, valid_options):
+ block_requires_mode = (
+ flag in self.get_flags() or
+ self.get_name().upper().startswith(label)
+ )
+ if block_requires_mode and current_generate_option not in valid_options:
+ self.add_error_message("Can't generate this block in mode: {} ".format(
+ repr(current_generate_option)))
+
+ check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',))
+ check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
+ if self._epy_reload_error:
+ self.get_param('_source_code').add_error_message(str(self._epy_reload_error))
+
+ def rewrite(self):
+ """
+ Add and remove ports to adjust for the nports.
+ """
+ Element.rewrite(self)
+ # Check and run any custom rewrite function for this block
+ getattr(self, 'rewrite_' + self._key, lambda: None)()
+
+ # Adjust nports, disconnect hidden ports
+ for ports in (self.get_sources(), self.get_sinks()):
+ for i, master_port in enumerate(ports):
+ nports = master_port.get_nports() or 1
+ num_ports = 1 + len(master_port.get_clones())
+ if master_port.get_hide():
+ for connection in master_port.get_connections():
+ self.get_parent().remove_element(connection)
+ if not nports and num_ports == 1: # Not a master port and no left-over clones
+ continue
+ # Remove excess cloned ports
+ for port in master_port.get_clones()[nports-1:]:
+ # Remove excess connections
+ for connection in port.get_connections():
+ self.get_parent().remove_element(connection)
+ master_port.remove_clone(port)
+ ports.remove(port)
+ # Add more cloned ports
+ for j in range(num_ports, nports):
+ port = master_port.add_clone()
+ ports.insert(ports.index(master_port) + j, port)
+
+ self.back_ofthe_bus(ports)
+ # Renumber non-message/message ports
+ domain_specific_port_index = collections.defaultdict(int)
+ for port in filter(lambda p: p.get_key().isdigit(), ports):
+ domain = port.get_domain()
+ port._key = str(domain_specific_port_index[domain])
+ domain_specific_port_index[domain] += 1
+
+ def port_controller_modify(self, direction):
+ """
+ Change the port controller.
+
+ Args:
+ direction: +1 or -1
+
+ Returns:
+ true for change
+ """
+ changed = False
+ # Concat the nports string from the private nports settings of all ports
+ nports_str = ' '.join([port._nports for port in self.get_ports()])
+ # Modify all params whose keys appear in the nports string
+ for param in self.get_params():
+ if param.is_enum() or param.get_key() not in nports_str:
+ continue
+ # Try to increment the port controller by direction
+ try:
+ value = param.get_evaluated()
+ value = value + direction
+ if 0 < value:
+ param.set_value(value)
+ changed = True
+ except:
+ pass
+ return changed
+
+ def get_doc(self):
+ platform = self.get_parent().get_parent()
+ documentation = platform.block_docstrings.get(self._key, {})
+ from_xml = self._doc.strip()
+ if from_xml:
+ documentation[''] = from_xml
+ return documentation
+
+ def get_imports(self, raw=False):
+ """
+ Resolve all import statements.
+ Split each import statement at newlines.
+ Combine all import statments into a list.
+ Filter empty imports.
+
+ Returns:
+ a list of import statements
+ """
+ if raw:
+ return self._imports
+ return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), []))
+
+ def get_make(self, raw=False):
+ if raw:
+ return self._make
+ return self.resolve_dependencies(self._make)
+
+ def get_var_make(self):
+ return self.resolve_dependencies(self._var_make)
+
+ def get_var_value(self):
+ return self.resolve_dependencies(self._var_value)
+
+ def get_callbacks(self):
+ """
+ Get a list of function callbacks for this block.
+
+ Returns:
+ a list of strings
+ """
+ def make_callback(callback):
+ callback = self.resolve_dependencies(callback)
+ if 'self.' in callback:
+ return callback
+ return 'self.{}.{}'.format(self.get_id(), callback)
+ return map(make_callback, self._callbacks)
+
+ def is_virtual_sink(self):
+ return self.get_key() == 'virtual_sink'
+
+ def is_virtual_source(self):
+ return self.get_key() == 'virtual_source'
+
+ ###########################################################################
+ # Custom rewrite functions
+ ###########################################################################
+
+ def rewrite_epy_block(self):
+ flowgraph = self.get_parent()
+ platform = flowgraph.get_parent()
+ param_blk = self.get_param('_io_cache')
+ param_src = self.get_param('_source_code')
+
+ src = param_src.get_value()
+ src_hash = hash((self.get_id(), src))
+ if src_hash == self._epy_source_hash:
+ return
+
+ try:
+ blk_io = epy_block_io.extract(src)
+
+ except Exception as e:
+ self._epy_reload_error = ValueError(str(e))
+ try: # Load last working block io
+ blk_io = epy_block_io.BlockIO(*eval(param_blk.get_value()))
+ except:
+ return
+ else:
+ self._epy_reload_error = None # Clear previous errors
+ param_blk.set_value(repr(tuple(blk_io)))
+
+ # print "Rewriting embedded python block {!r}".format(self.get_id())
+
+ self._epy_source_hash = src_hash
+ self._name = blk_io.name or blk_io.cls
+ self._doc = blk_io.doc
+ self._imports[0] = 'import ' + self.get_id()
+ self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join(
+ '{0}=${0}'.format(key) for key, _ in blk_io.params))
+
+ params = {}
+ for param in list(self._params):
+ if hasattr(param, '__epy_param__'):
+ params[param.get_key()] = param
+ self._params.remove(param)
+
+ for key, value in blk_io.params:
+ try:
+ param = params[key]
+ param.set_default(value)
+ except KeyError: # need to make a new param
+ name = key.replace('_', ' ').title()
+ n = odict(dict(name=name, key=key, type='raw', value=value))
+ param = platform.Param(block=self, n=n)
+ setattr(param, '__epy_param__', True)
+ self._params.append(param)
+
+ def update_ports(label, ports, port_specs, direction):
+ ports_to_remove = list(ports)
+ iter_ports = iter(ports)
+ ports_new = []
+ port_current = next(iter_ports, None)
+ for key, port_type in port_specs:
+ reuse_port = (
+ port_current is not None and
+ port_current.get_type() == port_type and
+ (key.isdigit() or port_current.get_key() == key)
+ )
+ if reuse_port:
+ ports_to_remove.remove(port_current)
+ port, port_current = port_current, next(iter_ports, None)
+ else:
+ n = odict(dict(name=label + str(key), type=port_type, key=key))
+ if port_type == 'message':
+ n['name'] = key
+ n['optional'] = '1'
+ port = platform.Port(block=self, n=n, dir=direction)
+ ports_new.append(port)
+ # replace old port list with new one
+ del ports[:]
+ ports.extend(ports_new)
+ # remove excess port connections
+ for port in ports_to_remove:
+ for connection in port.get_connections():
+ flowgraph.remove_element(connection)
+
+ update_ports('in', self.get_sinks(), blk_io.sinks, 'sink')
+ update_ports('out', self.get_sources(), blk_io.sources, 'source')
+ self.rewrite()
+
+ def back_ofthe_bus(self, portlist):
+ portlist.sort(key=lambda p: p._type == 'bus')
+
+ def filter_bus_port(self, ports):
+ buslist = [p for p in ports if p._type == 'bus']
+ return buslist or ports
+
+ # Main functions to get and set the block state
+ # Also kept get_enabled and set_enabled to keep compatibility
+ def get_state(self):
+ """
+ Gets the block's current state.
+
+ Returns:
+ ENABLED - 0
+ BYPASSED - 1
+ DISABLED - 2
+ """
+ try:
+ return int(eval(self.get_param('_enabled').get_value()))
+ except:
+ return BLOCK_ENABLED
+
+ def set_state(self, state):
+ """
+ Sets the state for the block.
+
+ Args:
+ ENABLED - 0
+ BYPASSED - 1
+ DISABLED - 2
+ """
+ if state in [BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED]:
+ self.get_param('_enabled').set_value(str(state))
+ else:
+ self.get_param('_enabled').set_value(str(BLOCK_ENABLED))
+
+ # Enable/Disable Aliases
+ def get_enabled(self):
+ """
+ Get the enabled state of the block.
+
+ Returns:
+ true for enabled
+ """
+ return not (self.get_state() == BLOCK_DISABLED)
+
+ def set_enabled(self, enabled):
+ """
+ Set the enabled state of the block.
+
+ Args:
+ enabled: true for enabled
+
+ Returns:
+ True if block changed state
+ """
+ old_state = self.get_state()
+ new_state = BLOCK_ENABLED if enabled else BLOCK_DISABLED
+ self.set_state(new_state)
+ return old_state != new_state
+
+ # Block bypassing
+ def get_bypassed(self):
+ """
+ Check if the block is bypassed
+ """
+ return self.get_state() == BLOCK_BYPASSED
+
+ def set_bypassed(self):
+ """
+ Bypass the block
+
+ Returns:
+ True if block chagnes state
+ """
+ if self.get_state() != BLOCK_BYPASSED and self.can_bypass():
+ self.set_state(BLOCK_BYPASSED)
+ return True
+ return False
+
+ def can_bypass(self):
+ """ Check the number of sinks and sources and see if this block can be bypassed """
+ # Check to make sure this is a single path block
+ # Could possibly support 1 to many blocks
+ if len(self.get_sources()) != 1 or len(self.get_sinks()) != 1:
+ return False
+ if not (self.get_sources()[0].get_type() == self.get_sinks()[0].get_type()):
+ return False
+ if self.bypass_disabled():
+ return False
+ return True
+
+ def __str__(self):
+ return 'Block - {} - {}({})'.format(self.get_id(), self.get_name(), self.get_key())
+
+ def get_id(self):
+ return self.get_param('id').get_value()
+
+ def get_name(self):
+ return self._name
+
+ 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()
+
+ def get_ports_gui(self):
+ return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks())
+
+ def get_children(self):
+ return self.get_ports() + self.get_params()
+
+ def get_children_gui(self):
+ return self.get_ports_gui() + self.get_params()
+
+ def get_block_wrapper_path(self):
+ return self._block_wrapper_path
+
+ def get_comment(self):
+ return self.get_param('comment').get_value()
+
+ def get_flags(self):
+ return self._flags
+
+ def throtteling(self):
+ return BLOCK_FLAG_THROTTLE in self._flags
+
+ def bypass_disabled(self):
+ return BLOCK_FLAG_DISABLE_BYPASS in self._flags
+
+ ##############################################
+ # Access Params
+ ##############################################
+ def get_param_tab_labels(self):
+ return self._param_tab_labels
+
+ def get_param_keys(self):
+ return _get_keys(self._params)
+
+ def get_param(self, key):
+ return _get_elem(self._params, key)
+
+ def get_params(self):
+ return self._params
+
+ def has_param(self, key):
+ try:
+ _get_elem(self._params, key)
+ return True
+ except:
+ return False
+
+ ##############################################
+ # Access Sinks
+ ##############################################
+ def get_sink_keys(self):
+ return _get_keys(self._sinks)
+
+ def get_sink(self, key):
+ return _get_elem(self._sinks, key)
+
+ def get_sinks(self):
+ return self._sinks
+
+ def get_sinks_gui(self):
+ return self.filter_bus_port(self.get_sinks())
+
+ ##############################################
+ # Access Sources
+ ##############################################
+ def get_source_keys(self):
+ return _get_keys(self._sources)
+
+ def get_source(self, key):
+ return _get_elem(self._sources, key)
+
+ def get_sources(self):
+ return self._sources
+
+ def get_sources_gui(self):
+ return self.filter_bus_port(self.get_sources())
+
+ def get_connections(self):
+ return sum([port.get_connections() for port in self.get_ports()], [])
+
+ def resolve_dependencies(self, tmpl):
+ """
+ Resolve a paramater dependency with cheetah templates.
+
+ Args:
+ tmpl: the string with dependencies
+
+ Returns:
+ the resolved value
+ """
+ tmpl = str(tmpl)
+ if '$' not in tmpl:
+ return tmpl
+ n = dict((param.get_key(), param.template_arg)
+ for param in self.get_params()) # TODO: cache that
+ try:
+ return str(Template(tmpl, n))
+ except Exception as err:
+ return "Template error: {}\n {}".format(tmpl, err)
+
+ ##############################################
+ # Controller Modify
+ ##############################################
+ def type_controller_modify(self, direction):
+ """
+ Change the type controller.
+
+ Args:
+ direction: +1 or -1
+
+ Returns:
+ true for change
+ """
+ changed = False
+ type_param = None
+ for param in filter(lambda p: p.is_enum(), self.get_params()):
+ children = self.get_ports() + self.get_params()
+ # Priority to the type controller
+ if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param
+ # Use param if type param is unset
+ if not type_param:
+ type_param = param
+ if type_param:
+ # Try to increment the enum by direction
+ try:
+ keys = type_param.get_option_keys()
+ old_index = keys.index(type_param.get_value())
+ new_index = (old_index + direction + len(keys)) % len(keys)
+ type_param.set_value(keys[new_index])
+ changed = True
+ except:
+ pass
+ return changed
+
+ def form_bus_structure(self, direc):
+ if direc == 'source':
+ get_p = self.get_sources
+ get_p_gui = self.get_sources_gui
+ bus_structure = self.get_bus_structure('source')
+ else:
+ get_p = self.get_sinks
+ get_p_gui = self.get_sinks_gui
+ bus_structure = self.get_bus_structure('sink')
+
+ struct = [range(len(get_p()))]
+ if True in map(lambda a: isinstance(a.get_nports(), int), get_p()):
+ structlet = []
+ last = 0
+ for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]:
+ structlet.extend(map(lambda a: a+last, range(j)))
+ last = structlet[-1] + 1
+ struct = [structlet]
+ if bus_structure:
+
+ struct = bus_structure
+
+ self.current_bus_structure[direc] = struct
+ return struct
+
+ def bussify(self, n, direc):
+ if direc == 'source':
+ get_p = self.get_sources
+ get_p_gui = self.get_sources_gui
+ bus_structure = self.get_bus_structure('source')
+ else:
+ get_p = self.get_sinks
+ get_p_gui = self.get_sinks_gui
+ bus_structure = self.get_bus_structure('sink')
+
+ for elt in get_p():
+ for connect in elt.get_connections():
+ self.get_parent().remove_element(connect)
+
+ if ('bus' not in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0:
+ struct = self.form_bus_structure(direc)
+ self.current_bus_structure[direc] = struct
+ if get_p()[0].get_nports():
+ n['nports'] = str(1)
+
+ for i in range(len(struct)):
+ n['key'] = str(len(get_p()))
+ n = odict(n)
+ port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc)
+ get_p().append(port)
+ elif 'bus' in map(lambda a: a.get_type(), get_p()):
+ for elt in get_p_gui():
+ get_p().remove(elt)
+ self.current_bus_structure[direc] = ''
+
+ ##############################################
+ # Import/Export Methods
+ ##############################################
+ def export_data(self):
+ """
+ Export this block's params to nested data.
+
+ Returns:
+ a nested data odict
+ """
+ n = odict()
+ n['key'] = self.get_key()
+ n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str))
+ if 'bus' in map(lambda a: a.get_type(), self.get_sinks()):
+ n['bus_sink'] = str(1)
+ if 'bus' in map(lambda a: a.get_type(), self.get_sources()):
+ n['bus_source'] = str(1)
+ return n
+
+ def get_hash(self):
+ return hash(tuple(map(hash, self.get_params())))
+
+ def import_data(self, n):
+ """
+ Import this block's params from nested data.
+ Any param keys that do not exist will be ignored.
+ Since params can be dynamically created based another param,
+ call rewrite, and repeat the load until the params stick.
+ This call to rewrite will also create any dynamic ports
+ that are needed for the connections creation phase.
+
+ Args:
+ n: the nested data odict
+ """
+ my_hash = 0
+ while self.get_hash() != my_hash:
+ params_n = n.findall('param')
+ for param_n in params_n:
+ key = param_n.find('key')
+ value = param_n.find('value')
+ # The key must exist in this block's params
+ if key in self.get_param_keys():
+ self.get_param(key).set_value(value)
+ # Store hash and call rewrite
+ my_hash = self.get_hash()
+ self.rewrite()
+ bussinks = n.findall('bus_sink')
+ if len(bussinks) > 0 and not self._bussify_sink:
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'sink')
+ elif len(bussinks) > 0:
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'sink')
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'sink')
+ bussrcs = n.findall('bus_source')
+ if len(bussrcs) > 0 and not self._bussify_source:
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'source')
+ elif len(bussrcs) > 0:
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'source')
+ self.bussify({'name': 'bus', 'type': 'bus'}, 'source')
diff --git a/grc/examples/CMakeLists.txt b/grc/core/CMakeLists.txt
index a218dbe500..51b0dacba6 100644
--- a/grc/examples/CMakeLists.txt
+++ b/grc/core/CMakeLists.txt
@@ -17,21 +17,21 @@
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
-# SIMPLE
-install(
- FILES
- simple/variable_config.grc
- DESTINATION ${GR_PKG_DATA_DIR}/examples/grc/simple
+file(GLOB py_files "*.py")
+
+GR_PYTHON_INSTALL(
+ FILES ${py_files}
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core
COMPONENT "grc"
)
-# XMLRPC
+file(GLOB dtd_files "*.dtd")
+
install(
- FILES
- xmlrpc/readme.txt
- xmlrpc/xmlrpc_client.grc
- xmlrpc/xmlrpc_client_script.py
- xmlrpc/xmlrpc_server.grc
- DESTINATION ${GR_PKG_DATA_DIR}/examples/grc/xmlrpc
+ FILES ${dtd_files} default_flow_graph.grc
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core
COMPONENT "grc"
)
+
+add_subdirectory(generator)
+add_subdirectory(utils)
diff --git a/grc/core/Config.py b/grc/core/Config.py
new file mode 100644
index 0000000000..ac38d9978c
--- /dev/null
+++ b/grc/core/Config.py
@@ -0,0 +1,55 @@
+"""
+Copyright 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
+"""
+
+import os
+from os.path import expanduser, normpath, expandvars, exists
+
+
+class Config(object):
+
+ key = 'grc'
+ name = 'GNU Radio Companion (no gui)'
+ license = __doc__.strip()
+ website = 'http://gnuradio.org'
+
+ hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', expanduser('~/.grc_gnuradio'))
+
+ def __init__(self, prefs_file, version, version_parts=None):
+ self.prefs = prefs_file
+ self.version = version
+ self.version_parts = version_parts or version[1:].split('-', 1)[0].split('.')[:3]
+
+ @property
+ def block_paths(self):
+ path_list_sep = {'/': ':', '\\': ';'}[os.path.sep]
+
+ paths_sources = (
+ self.hier_block_lib_dir,
+ os.environ.get('GRC_BLOCKS_PATH', ''),
+ self.prefs.get_string('grc', 'local_blocks_path', ''),
+ self.prefs.get_string('grc', 'global_blocks_path', ''),
+ )
+
+ collected_paths = sum((paths.split(path_list_sep)
+ for paths in paths_sources), [])
+
+ valid_paths = [normpath(expanduser(expandvars(path)))
+ for path in collected_paths if exists(path)]
+
+ return valid_paths
diff --git a/grc/base/Connection.py b/grc/core/Connection.py
index bf3c75277c..3aa32ef183 100644
--- a/grc/base/Connection.py
+++ b/grc/core/Connection.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008-2011 Free Software Foundation, Inc.
+Copyright 2008-2015 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -17,11 +17,15 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Element import Element
-from . import odict
+from . import Constants
+from .Element import Element
+from .utils import odict
+
class Connection(Element):
+ is_connection = True
+
def __init__(self, flow_graph, porta, portb):
"""
Make a new connection given the parent and 2 ports.
@@ -37,72 +41,88 @@ class Connection(Element):
"""
Element.__init__(self, flow_graph)
source = sink = None
- #separate the source and sink
+ # Separate the source and sink
for port in (porta, portb):
- if port.is_source(): source = port
- if port.is_sink(): sink = port
- if not source: raise ValueError('Connection could not isolate source')
- if not sink: raise ValueError('Connection could not isolate sink')
- busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2
- if not busses == 0: raise ValueError('busses must get with busses')
+ if port.is_source:
+ source = port
+ else:
+ sink = port
+ if not source:
+ raise ValueError('Connection could not isolate source')
+ if not sink:
+ raise ValueError('Connection could not isolate sink')
+ busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink])) % 2
+ if not busses == 0:
+ raise ValueError('busses must get with busses')
if not len(source.get_associated_ports()) == len(sink.get_associated_ports()):
- raise ValueError('port connections must have same cardinality');
- #ensure that this connection (source -> sink) is unique
- for connection in self.get_parent().get_connections():
+ raise ValueError('port connections must have same cardinality')
+ # Ensure that this connection (source -> sink) is unique
+ for connection in flow_graph.connections:
if connection.get_source() is source and connection.get_sink() is sink:
raise LookupError('This connection between source and sink is not unique.')
self._source = source
self._sink = sink
if source.get_type() == 'bus':
- sources = source.get_associated_ports();
- sinks = sink.get_associated_ports();
+ sources = source.get_associated_ports()
+ sinks = sink.get_associated_ports()
for i in range(len(sources)):
try:
- flow_graph.connect(sources[i], sinks[i]);
+ flow_graph.connect(sources[i], sinks[i])
except:
pass
def __str__(self):
- return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%(
+ return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format(
self.get_source().get_parent(),
self.get_source(),
self.get_sink().get_parent(),
self.get_sink(),
)
- def is_connection(self): return True
+ def is_msg(self):
+ return self.get_source().get_type() == self.get_sink().get_type() == 'msg'
+
+ def is_bus(self):
+ return self.get_source().get_type() == self.get_sink().get_type() == 'bus'
def validate(self):
"""
Validate the connections.
+ The ports must match in io size.
+ """
+ """
+ Validate the connections.
The ports must match in type.
"""
Element.validate(self)
platform = self.get_parent().get_parent()
source_domain = self.get_source().get_domain()
sink_domain = self.get_sink().get_domain()
- if (source_domain, sink_domain) not in platform.get_connection_templates():
- self.add_error_message('No connection known for domains "%s", "%s"'
- % (source_domain, sink_domain))
+ if (source_domain, sink_domain) not in platform.connection_templates:
+ self.add_error_message('No connection known for domains "{}", "{}"'.format(
+ source_domain, sink_domain))
too_many_other_sinks = (
- source_domain in platform.get_domains() and
- not platform.get_domain(key=source_domain)['multiple_sinks'] and
+ not platform.domains.get(source_domain, []).get('multiple_sinks', False) and
len(self.get_source().get_enabled_connections()) > 1
)
too_many_other_sources = (
- sink_domain in platform.get_domains() and
- not platform.get_domain(key=sink_domain)['multiple_sources'] and
+ not platform.domains.get(sink_domain, []).get('multiple_sources', False) and
len(self.get_sink().get_enabled_connections()) > 1
)
if too_many_other_sinks:
self.add_error_message(
- 'Domain "%s" can have only one downstream block' % source_domain)
+ 'Domain "{}" can have only one downstream block'.format(source_domain))
if too_many_other_sources:
self.add_error_message(
- 'Domain "%s" can have only one upstream block' % sink_domain)
+ 'Domain "{}" can have only one upstream block'.format(sink_domain))
+
+ source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen()
+ sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen()
+ if source_size != sink_size:
+ self.add_error_message('Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size))
def get_enabled(self):
"""
@@ -117,11 +137,14 @@ class Connection(Element):
#############################
# Access Ports
#############################
- def get_sink(self): return self._sink
- def get_source(self): return self._source
+ def get_sink(self):
+ return self._sink
+
+ def get_source(self):
+ return self._source
##############################################
- ## Import/Export Methods
+ # Import/Export Methods
##############################################
def export_data(self):
"""
diff --git a/grc/python/Constants.py b/grc/core/Constants.py
index b7a370cad7..9fe805f854 100644
--- a/grc/python/Constants.py
+++ b/grc/core/Constants.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008-2011 Free Software Foundation, Inc.
+Copyright 2008-2015 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -18,49 +18,53 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
import os
-from os.path import expanduser
import numpy
import stat
-from gnuradio import gr
-_gr_prefs = gr.prefs()
-
-# setup paths
-PATH_SEP = {'/': ':', '\\': ';'}[os.path.sep]
-
-HIER_BLOCKS_LIB_DIR = os.environ.get('GRC_HIER_PATH', expanduser('~/.grc_gnuradio'))
-
-PREFS_FILE = os.environ.get('GRC_PREFS_PATH', expanduser('~/.gnuradio/grc.conf'))
-PREFS_FILE_OLD = os.environ.get('GRC_PREFS_PATH', expanduser('~/.grc'))
-
-BLOCKS_DIRS = filter( # filter blank strings
- lambda x: x, PATH_SEP.join([
- os.environ.get('GRC_BLOCKS_PATH', ''),
- _gr_prefs.get_string('grc', 'local_blocks_path', ''),
- _gr_prefs.get_string('grc', 'global_blocks_path', ''),
- ]).split(PATH_SEP),
-) + [HIER_BLOCKS_LIB_DIR]
-
-# user settings
-XTERM_EXECUTABLE = _gr_prefs.get_string('grc', 'xterm_executable', 'xterm')
-
-# file creation modes
-TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
-HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH
-
-# data files
+# Data files
DATA_DIR = os.path.dirname(__file__)
-FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl')
+FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd')
+BLOCK_TREE_DTD = os.path.join(DATA_DIR, 'block_tree.dtd')
BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd')
DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc')
+DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd')
+
+# File format versions:
+# 0: undefined / legacy
+# 1: non-numeric message port keys (label is used instead)
+FLOW_GRAPH_FILE_FORMAT_VERSION = 1
+
+# Param tabs
+DEFAULT_PARAM_TAB = "General"
+ADVANCED_PARAM_TAB = "Advanced"
+
+# Port domains
+GR_STREAM_DOMAIN = "gr_stream"
+GR_MESSAGE_DOMAIN = "gr_message"
+DEFAULT_DOMAIN = GR_STREAM_DOMAIN
+
+BLOCK_FLAG_THROTTLE = 'throttle'
+BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass'
+BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui'
+BLOCK_FLAG_NEED_WX_GUI = 'need_wx_gui'
+
+# Block States
+BLOCK_DISABLED = 0
+BLOCK_ENABLED = 1
+BLOCK_BYPASSED = 2
+
+# File creation modes
+TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | \
+ stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
+HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH
-#define types, native python + numpy
+# Define types, native python + numpy
VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
- numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
-#cast to tuple for isinstance, concat subtypes
+ numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
+# Cast to tuple for isinstance, concat subtypes
COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
INT_TYPES = tuple(INT_TYPES)
@@ -87,7 +91,6 @@ GRC_COLOR_DARK_GREY = '#72706F'
GRC_COLOR_GREY = '#BDBDBD'
GRC_COLOR_WHITE = '#FFFFFF'
-
CORE_TYPES = ( # name, key, sizeof, color
('Complex Float 64', 'fc64', 16, GRC_COLOR_BROWN),
('Complex Float 32', 'fc32', 8, GRC_COLOR_BLUE),
@@ -108,23 +111,25 @@ CORE_TYPES = ( # name, key, sizeof, color
)
ALIAS_TYPES = {
- 'complex' : (8, GRC_COLOR_BLUE),
- 'float' : (4, GRC_COLOR_ORANGE),
- 'int' : (4, GRC_COLOR_TEAL),
- 'short' : (2, GRC_COLOR_YELLOW),
- 'byte' : (1, GRC_COLOR_LIGHT_PURPLE),
+ 'complex': (8, GRC_COLOR_BLUE),
+ 'float': (4, GRC_COLOR_ORANGE),
+ 'int': (4, GRC_COLOR_TEAL),
+ 'short': (2, GRC_COLOR_YELLOW),
+ 'byte': (1, GRC_COLOR_LIGHT_PURPLE),
}
TYPE_TO_COLOR = dict()
TYPE_TO_SIZEOF = dict()
+
for name, key, sizeof, color in CORE_TYPES:
TYPE_TO_COLOR[key] = color
TYPE_TO_SIZEOF[key] = sizeof
+
for key, (sizeof, color) in ALIAS_TYPES.iteritems():
TYPE_TO_COLOR[key] = color
TYPE_TO_SIZEOF[key] = sizeof
-#coloring
+# Coloring
COMPLEX_COLOR_SPEC = '#3399FF'
FLOAT_COLOR_SPEC = '#FF8C69'
INT_COLOR_SPEC = '#00FF99'
diff --git a/grc/base/Element.py b/grc/core/Element.py
index b0f94d0183..b96edb0a72 100644
--- a/grc/base/Element.py
+++ b/grc/core/Element.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008, 2009 Free Software Foundation, Inc.
+Copyright 2008, 2009, 2015 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
+
class Element(object):
def __init__(self, parent=None):
@@ -32,7 +33,8 @@ class Element(object):
Call this base method before adding error messages in the subclass.
"""
del self._error_messages[:]
- for child in self.get_children(): child.validate()
+ for child in self.get_children():
+ child.validate()
def is_valid(self):
"""
@@ -61,10 +63,10 @@ class Element(object):
Returns:
a list of error message strings
"""
- error_messages = list(self._error_messages) #make a copy
+ error_messages = list(self._error_messages) # Make a copy
for child in filter(lambda c: c.get_enabled() and not c.get_bypassed(), self.get_children()):
for msg in child.get_error_messages():
- error_messages.append("%s:\n\t%s"%(child, msg.replace("\n", "\n\t")))
+ error_messages.append("{}:\n\t{}".format(child, msg.replace("\n", "\n\t")))
return error_messages
def rewrite(self):
@@ -72,27 +74,36 @@ class Element(object):
Rewrite this element and call rewrite on all children.
Call this base method before rewriting the element.
"""
- for child in self.get_children(): child.rewrite()
+ for child in self.get_children():
+ child.rewrite()
+
+ def get_enabled(self):
+ return True
- def get_enabled(self): return True
- def get_bypassed(self): return False
+ def get_bypassed(self):
+ return False
##############################################
- ## Tree-like API
+ # Tree-like API
##############################################
- def get_parent(self): return self._parent
- def get_children(self): return list()
+ def get_parent(self):
+ return self._parent
+
+ def get_children(self):
+ return list()
##############################################
- ## Type testing methods
+ # Type testing
##############################################
- def is_element(self): return True
- def is_platform(self): return False
- def is_flow_graph(self): return False
- def is_connection(self): return False
- def is_block(self): return False
- def is_dummy_block(self): return False
- def is_source(self): return False
- def is_sink(self): return False
- def is_port(self): return False
- def is_param(self): return False
+ is_platform = False
+
+ is_flow_graph = False
+
+ is_block = False
+ is_dummy_block = False
+
+ is_connection = False
+
+ is_port = False
+
+ is_param = False
diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py
new file mode 100644
index 0000000000..313af3107a
--- /dev/null
+++ b/grc/core/FlowGraph.py
@@ -0,0 +1,594 @@
+# Copyright 2008-2015 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
+
+import imp
+import time
+from itertools import ifilter, chain
+from operator import methodcaller
+
+import re
+
+from . import Messages
+from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
+from .Element import Element
+from .utils import odict, expr_utils
+
+_variable_matcher = re.compile('^(variable\w*)$')
+_parameter_matcher = re.compile('^(parameter)$')
+_monitors_searcher = re.compile('(ctrlport_monitor)')
+_bussink_searcher = re.compile('^(bus_sink)$')
+_bussrc_searcher = re.compile('^(bus_source)$')
+_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$')
+_bus_struct_src_searcher = re.compile('^(bus_structure_source)$')
+
+
+class FlowGraph(Element):
+
+ is_flow_graph = True
+
+ def __init__(self, platform):
+ """
+ Make a flow graph from the arguments.
+
+ Args:
+ platform: a platforms with blocks and contrcutors
+
+ Returns:
+ the flow graph object
+ """
+ Element.__init__(self, platform)
+ self._elements = []
+ self._timestamp = time.ctime()
+
+ self.platform = platform # todo: make this a lazy prop
+ self.blocks = []
+ self.connections = []
+
+ self._eval_cache = {}
+ self.namespace = {}
+
+ self.grc_file_path = ''
+ self._options_block = self.new_block('options')
+
+ def __str__(self):
+ return 'FlowGraph - {}({})'.format(self.get_option('title'), self.get_option('id'))
+
+ ##############################################
+ # TODO: Move these to new generator package
+ ##############################################
+ def get_imports(self):
+ """
+ Get a set of all import statments in this flow graph namespace.
+
+ Returns:
+ a set of import statements
+ """
+ imports = sum([block.get_imports() for block in self.get_enabled_blocks()], [])
+ imports = sorted(set(imports))
+ return imports
+
+ def get_variables(self):
+ """
+ Get a list of all variables in this flow graph namespace.
+ Exclude paramterized variables.
+
+ Returns:
+ a sorted list of variable blocks in order of dependency (indep -> dep)
+ """
+ variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.iter_enabled_blocks())
+ return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make'))
+
+ def get_parameters(self):
+ """
+ Get a list of all paramterized variables in this flow graph namespace.
+
+ Returns:
+ a list of paramterized variables
+ """
+ parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks())
+ return parameters
+
+ def get_monitors(self):
+ """
+ Get a list of all ControlPort monitors
+ """
+ monitors = filter(lambda b: _monitors_searcher.search(b.get_key()),
+ self.iter_enabled_blocks())
+ return monitors
+
+ def get_python_modules(self):
+ """Iterate over custom code block ID and Source"""
+ for block in self.iter_enabled_blocks():
+ if block.get_key() == 'epy_module':
+ yield block.get_id(), block.get_param('source_code').get_value()
+
+ def get_bussink(self):
+ bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks())
+
+ for i in bussink:
+ for j in i.get_params():
+ if j.get_name() == 'On/Off' and j.get_value() == 'on':
+ return True
+ return False
+
+ def get_bussrc(self):
+ bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks())
+
+ for i in bussrc:
+ for j in i.get_params():
+ if j.get_name() == 'On/Off' and j.get_value() == 'on':
+ return True
+ return False
+
+ def get_bus_structure_sink(self):
+ bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks())
+ return bussink
+
+ def get_bus_structure_src(self):
+ bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks())
+ return bussrc
+
+ def iter_enabled_blocks(self):
+ """
+ Get an iterator of all blocks that are enabled and not bypassed.
+ """
+ return ifilter(methodcaller('get_enabled'), self.blocks)
+
+ def get_enabled_blocks(self):
+ """
+ Get a list of all blocks that are enabled and not bypassed.
+
+ Returns:
+ a list of blocks
+ """
+ return list(self.iter_enabled_blocks())
+
+ def get_bypassed_blocks(self):
+ """
+ Get a list of all blocks that are bypassed.
+
+ Returns:
+ a list of blocks
+ """
+ return filter(methodcaller('get_bypassed'), self.blocks)
+
+ def get_enabled_connections(self):
+ """
+ Get a list of all connections that are enabled.
+
+ Returns:
+ a list of connections
+ """
+ return filter(methodcaller('get_enabled'), self.connections)
+
+ def get_option(self, key):
+ """
+ Get the option for a given key.
+ The option comes from the special options block.
+
+ Args:
+ key: the param key for the options block
+
+ Returns:
+ the value held by that param
+ """
+ return self._options_block.get_param(key).get_evaluated()
+
+ ##############################################
+ # Access Elements
+ ##############################################
+ def get_block(self, id):
+ for block in self.blocks:
+ if block.get_id() == id:
+ return block
+ raise KeyError('No block with ID {!r}'.format(id))
+
+ def get_elements(self):
+ """
+ Get a list of all the elements.
+ Always ensure that the options block is in the list (only once).
+
+ Returns:
+ the element list
+ """
+ options_block_count = self.blocks.count(self._options_block)
+ if not options_block_count:
+ self.blocks.append(self._options_block)
+ for i in range(options_block_count-1):
+ self.blocks.remove(self._options_block)
+
+ return self.blocks + self.connections
+
+ get_children = get_elements
+
+ def rewrite(self):
+ """
+ Flag the namespace to be renewed.
+ """
+
+ self.renew_namespace()
+ for child in chain(self.blocks, self.connections):
+ child.rewrite()
+
+ self.bus_ports_rewrite()
+
+ def renew_namespace(self):
+ namespace = {}
+ # Load imports
+ for expr in self.get_imports():
+ try:
+ exec expr in namespace
+ except:
+ pass
+
+ for id, expr in self.get_python_modules():
+ try:
+ module = imp.new_module(id)
+ exec expr in module.__dict__
+ namespace[id] = module
+ except:
+ pass
+
+ # Load parameters
+ np = {} # params don't know each other
+ for parameter in self.get_parameters():
+ try:
+ value = eval(parameter.get_param('value').to_code(), namespace)
+ np[parameter.get_id()] = value
+ except:
+ pass
+ namespace.update(np) # Merge param namespace
+
+ # Load variables
+ for variable in self.get_variables():
+ try:
+ value = eval(variable.get_var_value(), namespace)
+ namespace[variable.get_id()] = value
+ except:
+ pass
+
+ self.namespace.clear()
+ self._eval_cache.clear()
+ self.namespace.update(namespace)
+
+ def evaluate(self, expr):
+ """
+ Evaluate the expression.
+
+ Args:
+ expr: the string expression
+ @throw Exception bad expression
+
+ Returns:
+ the evaluated data
+ """
+ # Evaluate
+ if not expr:
+ raise Exception('Cannot evaluate empty statement.')
+ return self._eval_cache.setdefault(expr, eval(expr, self.namespace))
+
+ ##############################################
+ # Add/remove stuff
+ ##############################################
+
+ def new_block(self, key):
+ """
+ Get a new block of the specified key.
+ Add the block to the list of elements.
+
+ Args:
+ key: the block key
+
+ Returns:
+ the new block or None if not found
+ """
+ try:
+ block = self.platform.get_new_block(self, key)
+ self.blocks.append(block)
+ except KeyError:
+ block = None
+ return block
+
+ def connect(self, porta, portb):
+ """
+ Create a connection between porta and portb.
+
+ Args:
+ porta: a port
+ portb: another port
+ @throw Exception bad connection
+
+ Returns:
+ the new connection
+ """
+
+ connection = self.platform.Connection(
+ flow_graph=self, porta=porta, portb=portb)
+ self.connections.append(connection)
+ return connection
+
+ def remove_element(self, element):
+ """
+ Remove the element from the list of elements.
+ If the element is a port, remove the whole block.
+ If the element is a block, remove its connections.
+ If the element is a connection, just remove the connection.
+ """
+ if element.is_port:
+ # Found a port, set to parent signal block
+ element = element.get_parent()
+
+ if element in self.blocks:
+ # Remove block, remove all involved connections
+ for port in element.get_ports():
+ map(self.remove_element, port.get_connections())
+ self.blocks.remove(element)
+
+ elif element in self.connections:
+ if element.is_bus():
+ cons_list = []
+ for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()):
+ cons_list.extend(i)
+ map(self.remove_element, cons_list)
+ self.connections.remove(element)
+
+ ##############################################
+ # Import/Export Methods
+ ##############################################
+ def export_data(self):
+ """
+ Export this flow graph to nested data.
+ Export all block and connection data.
+
+ Returns:
+ a nested data odict
+ """
+ # sort blocks and connections for nicer diffs
+ blocks = sorted(self.blocks, key=lambda b: (
+ b.get_key() != 'options', # options to the front
+ not b.get_key().startswith('variable'), # then vars
+ str(b)
+ ))
+ connections = sorted(self.connections, key=str)
+ n = odict()
+ n['timestamp'] = self._timestamp
+ n['block'] = [b.export_data() for b in blocks]
+ n['connection'] = [c.export_data() for c in connections]
+ instructions = odict({
+ 'created': '.'.join(self.get_parent().config.version_parts),
+ 'format': FLOW_GRAPH_FILE_FORMAT_VERSION,
+ })
+ return odict({'flow_graph': n, '_instructions': instructions})
+
+ def import_data(self, n):
+ """
+ Import blocks and connections into this flow graph.
+ Clear this flowgraph of all previous blocks and connections.
+ Any blocks or connections in error will be ignored.
+
+ Args:
+ n: the nested data odict
+ """
+ # Remove previous elements
+ del self.blocks[:]
+ del self.connections[:]
+ # set file format
+ try:
+ instructions = n.find('_instructions') or {}
+ file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n)
+ except:
+ file_format = 0
+
+ fg_n = n and n.find('flow_graph') or odict() # use blank data if none provided
+ self._timestamp = fg_n.find('timestamp') or time.ctime()
+
+ # build the blocks
+ self._options_block = self.new_block('options')
+ for block_n in fg_n.findall('block'):
+ key = block_n.find('key')
+ block = self._options_block if key == 'options' else self.new_block(key)
+
+ if not block:
+ # we're before the initial fg update(), so no evaluated values!
+ # --> use raw value instead
+ path_param = self._options_block.get_param('hier_block_src_path')
+ file_path = self.platform.find_file_in_paths(
+ filename=key + '.grc',
+ paths=path_param.get_value(),
+ cwd=self.grc_file_path
+ )
+ if file_path: # grc file found. load and get block
+ self.platform.load_and_generate_flow_graph(file_path)
+ block = self.new_block(key) # can be None
+
+ if not block: # looks like this block key cannot be found
+ # create a dummy block instead
+ block = self.new_block('dummy_block')
+ # Ugly ugly ugly
+ _initialize_dummy_block(block, block_n)
+ print('Block key "%s" not found' % key)
+
+ block.import_data(block_n)
+
+ self.rewrite() # evaluate stuff like nports before adding connections
+
+ # build the connections
+ def verify_and_get_port(key, block, dir):
+ ports = block.get_sinks() if dir == 'sink' else block.get_sources()
+ for port in ports:
+ if key == port.get_key():
+ break
+ if not key.isdigit() and port.get_type() == '' and key == port.get_name():
+ break
+ else:
+ if block.is_dummy_block:
+ port = _dummy_block_add_port(block, key, dir)
+ else:
+ raise LookupError('%s key %r not in %s block keys' % (dir, key, dir))
+ return port
+
+ errors = False
+ for connection_n in fg_n.findall('connection'):
+ # get the block ids and port keys
+ source_block_id = connection_n.find('source_block_id')
+ sink_block_id = connection_n.find('sink_block_id')
+ source_key = connection_n.find('source_key')
+ sink_key = connection_n.find('sink_key')
+ try:
+ source_block = self.get_block(source_block_id)
+ sink_block = self.get_block(sink_block_id)
+
+ # fix old, numeric message ports keys
+ if file_format < 1:
+ source_key, sink_key = _update_old_message_port_keys(
+ source_key, sink_key, source_block, sink_block)
+
+ # build the connection
+ source_port = verify_and_get_port(source_key, source_block, 'source')
+ sink_port = verify_and_get_port(sink_key, sink_block, 'sink')
+ self.connect(source_port, sink_port)
+ except LookupError as e:
+ Messages.send_error_load(
+ 'Connection between {}({}) and {}({}) could not be made.\n\t{}'.format(
+ source_block_id, source_key, sink_block_id, sink_key, e))
+ errors = True
+
+ self.rewrite() # global rewrite
+ return errors
+
+ ##############################################
+ # Needs to go
+ ##############################################
+ def bus_ports_rewrite(self):
+ # todo: move to block.rewrite()
+ for block in self.blocks:
+ for direc in ['source', 'sink']:
+ if direc == 'source':
+ get_p = block.get_sources
+ get_p_gui = block.get_sources_gui
+ bus_structure = block.form_bus_structure('source')
+ else:
+ get_p = block.get_sinks
+ get_p_gui = block.get_sinks_gui
+ bus_structure = block.form_bus_structure('sink')
+
+ if 'bus' in map(lambda a: a.get_type(), get_p_gui()):
+ if len(get_p_gui()) > len(bus_structure):
+ times = range(len(bus_structure), len(get_p_gui()))
+ for i in times:
+ for connect in get_p_gui()[-1].get_connections():
+ block.get_parent().remove_element(connect)
+ get_p().remove(get_p_gui()[-1])
+ elif len(get_p_gui()) < len(bus_structure):
+ n = {'name': 'bus', 'type': 'bus'}
+ if True in map(
+ lambda a: isinstance(a.get_nports(), int),
+ get_p()):
+ n['nports'] = str(1)
+
+ times = range(len(get_p_gui()), len(bus_structure))
+
+ for i in times:
+ n['key'] = str(len(get_p()))
+ n = odict(n)
+ port = block.get_parent().get_parent().Port(
+ block=block, n=n, dir=direc)
+ get_p().append(port)
+
+ if 'bus' in map(lambda a: a.get_type(),
+ block.get_sources_gui()):
+ for i in range(len(block.get_sources_gui())):
+ if len(block.get_sources_gui()[
+ i].get_connections()) > 0:
+ source = block.get_sources_gui()[i]
+ sink = []
+
+ for j in range(len(source.get_connections())):
+ sink.append(
+ source.get_connections()[j].get_sink())
+ for elt in source.get_connections():
+ self.remove_element(elt)
+ for j in sink:
+ self.connect(source, j)
+
+
+def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block):
+ """
+ Backward compatibility for message port keys
+
+ Message ports use their names as key (like in the 'connect' method).
+ Flowgraph files from former versions still have numeric keys stored for
+ message connections. These have to be replaced by the name of the
+ respective port. The correct message port is deduced from the integer
+ value of the key (assuming the order has not changed).
+
+ The connection ends are updated only if both ends translate into a
+ message port.
+ """
+ try:
+ # get ports using the "old way" (assuming liner indexed keys)
+ source_port = source_block.get_sources()[int(source_key)]
+ sink_port = sink_block.get_sinks()[int(sink_key)]
+ if source_port.get_type() == "message" and sink_port.get_type() == "message":
+ source_key, sink_key = source_port.get_key(), sink_port.get_key()
+ except (ValueError, IndexError):
+ pass
+ return source_key, sink_key # do nothing
+
+
+def _guess_file_format_1(n):
+ """
+ Try to guess the file format for flow-graph files without version tag
+ """
+ try:
+ has_non_numeric_message_keys = any(not (
+ connection_n.find('source_key').isdigit() and
+ connection_n.find('sink_key').isdigit()
+ ) for connection_n in n.find('flow_graph').findall('connection'))
+ if has_non_numeric_message_keys:
+ return 1
+ except:
+ pass
+ return 0
+
+
+def _initialize_dummy_block(block, block_n):
+ """
+ This is so ugly... dummy-fy a block
+ Modify block object to get the behaviour for a missing block
+ """
+
+ block._key = block_n.find('key')
+ block.is_dummy_block = lambda: True
+ block.is_valid = lambda: False
+ block.get_enabled = lambda: False
+ for param_n in block_n.findall('param'):
+ if param_n['key'] not in block.get_param_keys():
+ new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'})
+ params = block.get_parent().get_parent().Param(block=block, n=new_param_n)
+ block.get_params().append(params)
+
+
+def _dummy_block_add_port(block, key, dir):
+ """ This is so ugly... Add a port to a dummy-field block """
+ port_n = odict({'name': '?', 'key': key, 'type': ''})
+ port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir)
+ if port.is_source():
+ block.get_sources().append(port)
+ else:
+ block.get_sinks().append(port)
+ return port
diff --git a/grc/gui/Messages.py b/grc/core/Messages.py
index 551a8ce753..da50487e5b 100644
--- a/grc/gui/Messages.py
+++ b/grc/core/Messages.py
@@ -1,21 +1,20 @@
-"""
-Copyright 2007 Free Software Foundation, Inc.
-This file is part of GNU Radio
+# Copyright 2007, 2015 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
-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
-"""
import traceback
import sys
@@ -59,20 +58,12 @@ register_messenger(sys.stdout.write)
# Special functions for specific program functionalities
###########################################################################
def send_init(platform):
- p = platform
-
- def get_paths(x):
- return os.path.abspath(os.path.expanduser(x)), x
-
- send('\n'.join([
- "<<< Welcome to %s %s >>>" % (p.get_name(), p.get_version()),
- "",
- "Preferences file: " + p.get_prefs_file(),
- "Block paths:"
- ] + [
- "\t%s" % path + (" (%s)" % opath if opath != path else "")
- for path, opath in map(get_paths, p.get_block_paths())
- ]) + "\n")
+ msg = "<<< Welcome to {config.name} {config.version} >>>\n\n" \
+ "Block paths:\n\t{paths}\n"
+ send(msg.format(
+ config=platform.config,
+ paths="\n\t".join(platform.config.block_paths))
+ )
def send_page_switch(file_path):
diff --git a/grc/core/Param.py b/grc/core/Param.py
new file mode 100644
index 0000000000..d155800c43
--- /dev/null
+++ b/grc/core/Param.py
@@ -0,0 +1,740 @@
+"""
+Copyright 2008-2015 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
+"""
+
+import ast
+import weakref
+import re
+
+from . import Constants
+from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
+from .Element import Element
+from .utils import odict
+
+# Blacklist certain ids, its not complete, but should help
+import __builtin__
+
+
+ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__)
+try:
+ from gnuradio import gr
+ ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
+except ImportError:
+ pass
+
+_check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
+_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$')
+
+
+def _get_keys(lst):
+ return [elem.get_key() for elem in lst]
+
+
+def _get_elem(lst, key):
+ try:
+ return lst[_get_keys(lst).index(key)]
+ except ValueError:
+ raise ValueError('Key "{}" not found in {}.'.format(key, _get_keys(lst)))
+
+
+def num_to_str(num):
+ """ Display logic for numbers """
+ def eng_notation(value, fmt='g'):
+ """Convert a number to a string in engineering notation. E.g., 5e-9 -> 5n"""
+ template = '{:' + fmt + '}{}'
+ magnitude = abs(value)
+ for exp, symbol in zip(range(9, -15-1, -3), 'GMk munpf'):
+ factor = 10 ** exp
+ if magnitude >= factor:
+ return template.format(value / factor, symbol.strip())
+ return template.format(value, '')
+
+ if isinstance(num, COMPLEX_TYPES):
+ num = complex(num) # Cast to python complex
+ if num == 0:
+ return '0'
+ output = eng_notation(num.real) if num.real else ''
+ output += eng_notation(num.imag, '+g' if output else 'g') + 'j' if num.imag else ''
+ return output
+ else:
+ return str(num)
+
+
+class Option(Element):
+
+ def __init__(self, param, n):
+ Element.__init__(self, param)
+ self._name = n.find('name')
+ self._key = n.find('key')
+ self._opts = dict()
+ opts = n.findall('opt')
+ # Test against opts when non enum
+ if not self.get_parent().is_enum() and opts:
+ raise Exception('Options for non-enum types cannot have sub-options')
+ # Extract opts
+ for opt in opts:
+ # Separate the key:value
+ try:
+ key, value = opt.split(':')
+ except:
+ raise Exception('Error separating "{}" into key:value'.format(opt))
+ # Test against repeated keys
+ if key in self._opts:
+ raise Exception('Key "{}" already exists in option'.format(key))
+ # Store the option
+ self._opts[key] = value
+
+ def __str__(self):
+ return 'Option {}({})'.format(self.get_name(), self.get_key())
+
+ def get_name(self):
+ return self._name
+
+ def get_key(self):
+ return self._key
+
+ ##############################################
+ # Access Opts
+ ##############################################
+ def get_opt_keys(self):
+ return self._opts.keys()
+
+ def get_opt(self, key):
+ return self._opts[key]
+
+ def get_opts(self):
+ return self._opts.values()
+
+
+class TemplateArg(object):
+ """
+ A cheetah template argument created from a param.
+ The str of this class evaluates to the param's to code method.
+ The use of this class as a dictionary (enum only) will reveal the enum opts.
+ The __call__ or () method can return the param evaluated to a raw python data type.
+ """
+
+ def __init__(self, param):
+ self._param = weakref.proxy(param)
+
+ def __getitem__(self, item):
+ return str(self._param.get_opt(item)) if self._param.is_enum() else NotImplemented
+
+ def __str__(self):
+ return str(self._param.to_code())
+
+ def __call__(self):
+ return self._param.get_evaluated()
+
+
+class Param(Element):
+
+ is_param = True
+
+ def __init__(self, block, n):
+ """
+ Make a new param from nested data.
+
+ Args:
+ block: the parent element
+ n: the nested odict
+ """
+ # If the base key is a valid param key, copy its data and overlay this params data
+ base_key = n.find('base_key')
+ if base_key and base_key in block.get_param_keys():
+ n_expanded = block.get_param(base_key)._n.copy()
+ n_expanded.update(n)
+ n = n_expanded
+ # Save odict in case this param will be base for another
+ self._n = n
+ # Parse the data
+ self._name = n.find('name')
+ self._key = n.find('key')
+ value = n.find('value') or ''
+ self._type = n.find('type') or 'raw'
+ self._hide = n.find('hide') or ''
+ self._tab_label = n.find('tab') or block.get_param_tab_labels()[0]
+ if self._tab_label not in block.get_param_tab_labels():
+ block.get_param_tab_labels().append(self._tab_label)
+ # Build the param
+ Element.__init__(self, block)
+ # Create the Option objects from the n data
+ self._options = list()
+ self._evaluated = None
+ for option in map(lambda o: Option(param=self, n=o), n.findall('option')):
+ key = option.get_key()
+ # Test against repeated keys
+ if key in self.get_option_keys():
+ raise Exception('Key "{}" already exists in options'.format(key))
+ # Store the option
+ self.get_options().append(option)
+ # Test the enum options
+ if self.is_enum():
+ # Test against options with identical keys
+ if len(set(self.get_option_keys())) != len(self.get_options()):
+ raise Exception('Options keys "{}" are not unique.'.format(self.get_option_keys()))
+ # Test against inconsistent keys in options
+ opt_keys = self.get_options()[0].get_opt_keys()
+ for option in self.get_options():
+ if set(opt_keys) != set(option.get_opt_keys()):
+ raise Exception('Opt keys "{}" are not identical across all options.'.format(opt_keys))
+ # If a value is specified, it must be in the options keys
+ if value or value in self.get_option_keys():
+ self._value = value
+ else:
+ self._value = self.get_option_keys()[0]
+ if self.get_value() not in self.get_option_keys():
+ raise Exception('The value "{}" is not in the possible values of "{}".'.format(self.get_value(), self.get_option_keys()))
+ else:
+ self._value = value or ''
+ self._default = value
+ self._init = False
+ self._hostage_cells = list()
+ self.template_arg = TemplateArg(self)
+
+ def get_types(self):
+ return (
+ 'raw', 'enum',
+ 'complex', 'real', 'float', 'int',
+ 'complex_vector', 'real_vector', 'float_vector', 'int_vector',
+ 'hex', 'string', 'bool',
+ 'file_open', 'file_save', '_multiline', '_multiline_python_external',
+ 'id', 'stream_id',
+ 'grid_pos', 'notebook', 'gui_hint',
+ 'import',
+ )
+
+ def __repr__(self):
+ """
+ Get the repr (nice string format) for this param.
+
+ Returns:
+ the string representation
+ """
+ ##################################################
+ # Truncate helper method
+ ##################################################
+ def _truncate(string, style=0):
+ max_len = max(27 - len(self.get_name()), 3)
+ if len(string) > max_len:
+ if style < 0: # Front truncate
+ string = '...' + string[3-max_len:]
+ elif style == 0: # Center truncate
+ string = string[:max_len/2 - 3] + '...' + string[-max_len/2:]
+ elif style > 0: # Rear truncate
+ string = string[:max_len-3] + '...'
+ return string
+
+ ##################################################
+ # Simple conditions
+ ##################################################
+ if not self.is_valid():
+ return _truncate(self.get_value())
+ if self.get_value() in self.get_option_keys():
+ return self.get_option(self.get_value()).get_name()
+
+ ##################################################
+ # Split up formatting by type
+ ##################################################
+ # Default center truncate
+ truncate = 0
+ e = self.get_evaluated()
+ t = self.get_type()
+ if isinstance(e, bool):
+ return str(e)
+ elif isinstance(e, COMPLEX_TYPES):
+ dt_str = num_to_str(e)
+ elif isinstance(e, VECTOR_TYPES):
+ # Vector types
+ if len(e) > 8:
+ # Large vectors use code
+ dt_str = self.get_value()
+ truncate = 1
+ else:
+ # Small vectors use eval
+ dt_str = ', '.join(map(num_to_str, e))
+ elif t in ('file_open', 'file_save'):
+ dt_str = self.get_value()
+ truncate = -1
+ else:
+ # Other types
+ dt_str = str(e)
+
+ # Done
+ return _truncate(dt_str, truncate)
+
+ def __repr2__(self):
+ """
+ Get the repr (nice string format) for this param.
+
+ Returns:
+ the string representation
+ """
+ if self.is_enum():
+ return self.get_option(self.get_value()).get_name()
+ return self.get_value()
+
+ def __str__(self):
+ return 'Param - {}({})'.format(self.get_name(), self.get_key())
+
+ def get_color(self):
+ """
+ Get the color that represents this param's type.
+
+ Returns:
+ a hex color code.
+ """
+ try:
+ return {
+ # Number types
+ 'complex': Constants.COMPLEX_COLOR_SPEC,
+ 'real': Constants.FLOAT_COLOR_SPEC,
+ 'float': Constants.FLOAT_COLOR_SPEC,
+ 'int': Constants.INT_COLOR_SPEC,
+ # Vector types
+ 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC,
+ 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
+ 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
+ 'int_vector': Constants.INT_VECTOR_COLOR_SPEC,
+ # Special
+ 'bool': Constants.INT_COLOR_SPEC,
+ 'hex': Constants.INT_COLOR_SPEC,
+ 'string': Constants.BYTE_VECTOR_COLOR_SPEC,
+ 'id': Constants.ID_COLOR_SPEC,
+ 'stream_id': Constants.ID_COLOR_SPEC,
+ 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
+ 'notebook': Constants.INT_VECTOR_COLOR_SPEC,
+ 'raw': Constants.WILDCARD_COLOR_SPEC,
+ }[self.get_type()]
+ except:
+ return '#FFFFFF'
+
+ def get_hide(self):
+ """
+ Get the hide value from the base class.
+ Hide the ID parameter for most blocks. Exceptions below.
+ If the parameter controls a port type, vlen, or nports, return part.
+ If the parameter is an empty grid position, return part.
+ These parameters are redundant to display in the flow graph view.
+
+ Returns:
+ hide the hide property string
+ """
+ hide = self.get_parent().resolve_dependencies(self._hide).strip()
+ if hide:
+ return hide
+ # Hide ID in non variable blocks
+ if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()):
+ return 'part'
+ # Hide port controllers for type and nports
+ if self.get_key() in ' '.join(map(lambda p: ' '.join([p._type, p._nports]),
+ self.get_parent().get_ports())):
+ return 'part'
+ # Hide port controllers for vlen, when == 1
+ if self.get_key() in ' '.join(map(
+ lambda p: p._vlen, self.get_parent().get_ports())
+ ):
+ try:
+ if int(self.get_evaluated()) == 1:
+ return 'part'
+ except:
+ pass
+ # Hide empty grid positions
+ if self.get_key() in ('grid_pos', 'notebook') and not self.get_value():
+ return 'part'
+ return hide
+
+ def validate(self):
+ """
+ Validate the param.
+ The value must be evaluated and type must a possible type.
+ """
+ Element.validate(self)
+ if self.get_type() not in self.get_types():
+ self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
+
+ self._evaluated = None
+ try:
+ self._evaluated = self.evaluate()
+ except Exception, e:
+ self.add_error_message(str(e))
+
+ def get_evaluated(self):
+ return self._evaluated
+
+ def evaluate(self):
+ """
+ Evaluate the value.
+
+ Returns:
+ evaluated type
+ """
+ self._init = True
+ self._lisitify_flag = False
+ self._stringify_flag = False
+ self._hostage_cells = list()
+ t = self.get_type()
+ v = self.get_value()
+
+ #########################
+ # Enum Type
+ #########################
+ if self.is_enum():
+ return v
+
+ #########################
+ # Numeric Types
+ #########################
+ elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'):
+ # Raise exception if python cannot evaluate this value
+ try:
+ e = self.get_parent().get_parent().evaluate(v)
+ except Exception, e:
+ raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
+ # Raise an exception if the data is invalid
+ if t == 'raw':
+ return e
+ elif t == 'complex':
+ if not isinstance(e, COMPLEX_TYPES):
+ raise Exception('Expression "{}" is invalid for type complex.'.format(str(e)))
+ return e
+ elif t == 'real' or t == 'float':
+ if not isinstance(e, REAL_TYPES):
+ raise Exception('Expression "{}" is invalid for type float.'.format(str(e)))
+ return e
+ elif t == 'int':
+ if not isinstance(e, INT_TYPES):
+ raise Exception('Expression "{}" is invalid for type integer.'.format(str(e)))
+ return e
+ elif t == 'hex':
+ return hex(e)
+ elif t == 'bool':
+ if not isinstance(e, bool):
+ raise Exception('Expression "{}" is invalid for type bool.'.format(str(e)))
+ return e
+ else:
+ raise TypeError('Type "{}" not handled'.format(t))
+ #########################
+ # Numeric Vector Types
+ #########################
+ elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
+ if not v:
+ # Turn a blank string into an empty list, so it will eval
+ v = '()'
+ # Raise exception if python cannot evaluate this value
+ try:
+ e = self.get_parent().get_parent().evaluate(v)
+ except Exception, e:
+ raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
+ # Raise an exception if the data is invalid
+ if t == 'complex_vector':
+ if not isinstance(e, VECTOR_TYPES):
+ self._lisitify_flag = True
+ e = [e]
+ if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]):
+ raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e)))
+ return e
+ elif t == 'real_vector' or t == 'float_vector':
+ if not isinstance(e, VECTOR_TYPES):
+ self._lisitify_flag = True
+ e = [e]
+ if not all([isinstance(ei, REAL_TYPES) for ei in e]):
+ raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e)))
+ return e
+ elif t == 'int_vector':
+ if not isinstance(e, VECTOR_TYPES):
+ self._lisitify_flag = True
+ e = [e]
+ if not all([isinstance(ei, INT_TYPES) for ei in e]):
+ raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e)))
+ return e
+ #########################
+ # String Types
+ #########################
+ elif t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
+ # Do not check if file/directory exists, that is a runtime issue
+ try:
+ e = self.get_parent().get_parent().evaluate(v)
+ if not isinstance(e, str):
+ raise Exception()
+ except:
+ self._stringify_flag = True
+ e = str(v)
+ if t == '_multiline_python_external':
+ ast.parse(e) # Raises SyntaxError
+ return e
+ #########################
+ # Unique ID Type
+ #########################
+ elif t == 'id':
+ # Can python use this as a variable?
+ if not _check_id_matcher.match(v):
+ raise Exception('ID "{}" must begin with a letter and may contain letters, numbers, and underscores.'.format(v))
+ ids = [param.get_value() for param in self.get_all_params(t)]
+
+ # Id should only appear once, or zero times if block is disabled
+ if ids.count(v) > 1:
+ raise Exception('ID "{}" is not unique.'.format(v))
+ if v in ID_BLACKLIST:
+ raise Exception('ID "{}" is blacklisted.'.format(v))
+ return v
+
+ #########################
+ # Stream ID Type
+ #########################
+ elif t == 'stream_id':
+ # Get a list of all stream ids used in the virtual sinks
+ ids = [param.get_value() for param in filter(
+ lambda p: p.get_parent().is_virtual_sink(),
+ self.get_all_params(t),
+ )]
+ # Check that the virtual sink's stream id is unique
+ if self.get_parent().is_virtual_sink():
+ # Id should only appear once, or zero times if block is disabled
+ if ids.count(v) > 1:
+ raise Exception('Stream ID "{}" is not unique.'.format(v))
+ # Check that the virtual source's steam id is found
+ if self.get_parent().is_virtual_source():
+ if v not in ids:
+ raise Exception('Stream ID "{}" is not found.'.format(v))
+ return v
+
+ #########################
+ # 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
+ return GuiHint(widget_str)
+ #########################
+ # Grid Position Type
+ #########################
+ elif t == 'grid_pos':
+ if not v:
+ # Allow for empty grid pos
+ return ''
+ e = self.get_parent().get_parent().evaluate(v)
+ if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]):
+ raise Exception('A grid position must be a list of 4 integers.')
+ row, col, row_span, col_span = e
+ # Check row, col
+ if row < 0 or col < 0:
+ raise Exception('Row and column must be non-negative.')
+ # Check row span, col span
+ if row_span <= 0 or col_span <= 0:
+ raise Exception('Row and column span must be greater than zero.')
+ # Get hostage cell parent
+ try:
+ my_parent = self.get_parent().get_param('notebook').evaluate()
+ except:
+ my_parent = ''
+ # 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)))
+ # 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:
+ raise Exception('Another graphical element is using parent "{}", cell "{}".'.format(str(parent), str(cell)))
+ return e
+ #########################
+ # Notebook Page Type
+ #########################
+ elif t == 'notebook':
+ if not v:
+ # Allow for empty notebook
+ return ''
+
+ # Get a list of all notebooks
+ notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks())
+ # Check for notebook param syntax
+ try:
+ notebook_id, page_index = map(str.strip, v.split(','))
+ except:
+ raise Exception('Bad notebook page format.')
+ # Check that the notebook id is valid
+ try:
+ notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0]
+ except:
+ raise Exception('Notebook id "{}" is not an existing notebook id.'.format(notebook_id))
+
+ # Check that page index exists
+ if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())):
+ raise Exception('Page index "{}" is not a valid index number.'.format(page_index))
+ return notebook_id, page_index
+
+ #########################
+ # Import Type
+ #########################
+ elif t == 'import':
+ # New namespace
+ n = dict()
+ try:
+ exec v in n
+ except ImportError:
+ raise Exception('Import "{}" failed.'.format(v))
+ except Exception:
+ raise Exception('Bad import syntax: "{}".'.format(v))
+ return filter(lambda k: str(k) != '__builtins__', n.keys())
+
+ #########################
+ else:
+ raise TypeError('Type "{}" not handled'.format(t))
+
+ def to_code(self):
+ """
+ Convert the value to code.
+ For string and list types, check the init flag, call evaluate().
+ This ensures that evaluate() was called to set the xxxify_flags.
+
+ Returns:
+ a string representing the code
+ """
+ v = self.get_value()
+ t = self.get_type()
+ # String types
+ if t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
+ if not self._init:
+ self.evaluate()
+ if self._stringify_flag:
+ return '"%s"' % v.replace('"', '\"')
+ else:
+ return v
+ # Vector types
+ elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
+ if not self._init:
+ self.evaluate()
+ if self._lisitify_flag:
+ return '(%s, )' % v
+ else:
+ return '(%s)' % v
+ else:
+ return v
+
+ def get_all_params(self, type):
+ """
+ Get all the params from the flowgraph that have the given type.
+
+ Args:
+ type: the specified type
+
+ Returns:
+ a list of params
+ """
+ return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])
+
+ def is_enum(self):
+ return self._type == 'enum'
+
+ def get_value(self):
+ value = self._value
+ if self.is_enum() and value not in self.get_option_keys():
+ value = self.get_option_keys()[0]
+ self.set_value(value)
+ return value
+
+ def set_value(self, value):
+ # Must be a string
+ self._value = str(value)
+
+ def set_default(self, value):
+ if self._default == self._value:
+ self.set_value(value)
+ self._default = str(value)
+
+ def get_type(self):
+ return self.get_parent().resolve_dependencies(self._type)
+
+ def get_tab_label(self):
+ return self._tab_label
+
+ def get_name(self):
+ return self.get_parent().resolve_dependencies(self._name).strip()
+
+ def get_key(self):
+ return self._key
+
+ ##############################################
+ # Access Options
+ ##############################################
+ def get_option_keys(self):
+ return _get_keys(self.get_options())
+
+ def get_option(self, key):
+ return _get_elem(self.get_options(), key)
+
+ def get_options(self):
+ return self._options
+
+ ##############################################
+ # Access Opts
+ ##############################################
+ def get_opt_keys(self):
+ return self.get_option(self.get_value()).get_opt_keys()
+
+ def get_opt(self, key):
+ return self.get_option(self.get_value()).get_opt(key)
+
+ def get_opts(self):
+ return self.get_option(self.get_value()).get_opts()
+
+ ##############################################
+ # Import/Export Methods
+ ##############################################
+ def export_data(self):
+ """
+ Export this param's key/value.
+
+ Returns:
+ a nested data odict
+ """
+ n = odict()
+ n['key'] = self.get_key()
+ n['value'] = self.get_value()
+ return n
diff --git a/grc/base/ParseXML.py b/grc/core/ParseXML.py
index e05fc1428d..c9f6541ee7 100644
--- a/grc/base/ParseXML.py
+++ b/grc/core/ParseXML.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008 Free Software Foundation, Inc.
+Copyright 2008, 2015 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -18,7 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
from lxml import etree
-from . import odict
+
+from .utils import odict
xml_failures = {}
etree.set_default_parser(etree.XMLParser(remove_comments=True))
@@ -42,7 +43,7 @@ def validate_dtd(xml_file, dtd_file=None):
dtd_file: the optional dtd file
@throws Exception validation fails
"""
- # perform parsing, use dtd validation if dtd file is not specified
+ # Perform parsing, use dtd validation if dtd file is not specified
parser = etree.XMLParser(dtd_validation=not dtd_file)
try:
xml = etree.parse(xml_file, parser=parser)
@@ -51,7 +52,7 @@ def validate_dtd(xml_file, dtd_file=None):
if parser.error_log:
raise XMLSyntaxError(parser.error_log)
- # perform dtd validation if the dtd file is specified
+ # Perform dtd validation if the dtd file is specified
if not dtd_file:
return
try:
@@ -100,9 +101,11 @@ def _from_file(xml):
nested_data = odict()
for elem in xml:
key, value = _from_file(elem).items()[0]
- if nested_data.has_key(key): nested_data[key].append(value)
- else: nested_data[key] = [value]
- # delistify if the length of values is 1
+ if key in nested_data:
+ nested_data[key].append(value)
+ else:
+ nested_data[key] = [value]
+ # Delistify if the length of values is 1
for key, values in nested_data.iteritems():
if len(values) == 1:
nested_data[key] = values[0]
@@ -120,7 +123,8 @@ def to_file(nested_data, xml_file):
"""
xml_data = ""
instructions = nested_data.pop('_instructions', None)
- if instructions: # create the processing instruction from the array
+ # Create the processing instruction from the array
+ if instructions:
xml_data += etree.tostring(etree.ProcessingInstruction(
'grc', ' '.join(
"{0}='{1}'".format(*item) for item in instructions.iteritems())
@@ -143,7 +147,7 @@ def _to_file(nested_data):
"""
nodes = list()
for key, values in nested_data.iteritems():
- # listify the values if not a list
+ # Listify the values if not a list
if not isinstance(values, (list, set, tuple)):
values = [values]
for value in values:
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
new file mode 100644
index 0000000000..4c1e6f471a
--- /dev/null
+++ b/grc/core/Platform.py
@@ -0,0 +1,319 @@
+"""
+Copyright 2008-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
+"""
+
+import os
+import sys
+
+from . import ParseXML, Messages, Constants
+
+from .Config import Config
+from .Element import Element
+from .generator import Generator
+from .FlowGraph import FlowGraph
+from .Connection import Connection
+from .Block import Block
+from .Port import Port
+from .Param import Param
+
+from .utils import odict, extract_docs
+
+
+class Platform(Element):
+
+ Config = Config
+ Generator = Generator
+ FlowGraph = FlowGraph
+ Connection = Connection
+ Block = Block
+ Port = Port
+ Param = Param
+
+ is_platform = True
+
+ def __init__(self, *args, **kwargs):
+ """ Make a platform for GNU Radio """
+ Element.__init__(self)
+
+ self.config = self.Config(*args, **kwargs)
+
+ self.block_docstrings = {}
+ self.block_docstrings_loaded_callback = lambda: None # dummy to be replaced by BlockTreeWindow
+
+ self._docstring_extractor = extract_docs.SubprocessLoader(
+ callback_query_result=self._save_docstring_extraction_result,
+ callback_finished=lambda: self.block_docstrings_loaded_callback()
+ )
+
+ self._block_dtd = Constants.BLOCK_DTD
+ self._default_flow_graph = Constants.DEFAULT_FLOW_GRAPH
+
+ # Create a dummy flow graph for the blocks
+ self._flow_graph = Element(self)
+ self._flow_graph.connections = []
+
+ self.blocks = None
+ self._blocks_n = None
+ self._category_trees_n = None
+ self.domains = {}
+ self.connection_templates = {}
+
+ self._auto_hier_block_generate_chain = set()
+
+ self.load_blocks()
+
+ def __str__(self):
+ return 'Platform - {}({})'.format(self.config.key, self.config.name)
+
+ @staticmethod
+ def find_file_in_paths(filename, paths, cwd):
+ """Checks the provided paths relative to cwd for a certain filename"""
+ if not os.path.isdir(cwd):
+ cwd = os.path.dirname(cwd)
+ if isinstance(paths, str):
+ paths = (p for p in paths.split(':') if p)
+
+ for path in paths:
+ path = os.path.expanduser(path)
+ if not os.path.isabs(path):
+ path = os.path.normpath(os.path.join(cwd, path))
+ file_path = os.path.join(path, filename)
+ if os.path.exists(os.path.normpath(file_path)):
+ return file_path
+
+ def load_and_generate_flow_graph(self, file_path):
+ """Loads a flow graph from file and generates it"""
+ Messages.set_indent(len(self._auto_hier_block_generate_chain))
+ Messages.send('>>> Loading: %r\n' % file_path)
+ if file_path in self._auto_hier_block_generate_chain:
+ Messages.send(' >>> Warning: cyclic hier_block dependency\n')
+ return False
+ self._auto_hier_block_generate_chain.add(file_path)
+ try:
+ flow_graph = self.get_new_flow_graph()
+ flow_graph.grc_file_path = file_path
+ # Other, nested higiter_blocks might be auto-loaded here
+ flow_graph.import_data(self.parse_flow_graph(file_path))
+ flow_graph.rewrite()
+ flow_graph.validate()
+ if not flow_graph.is_valid():
+ raise Exception('Flowgraph invalid')
+ except Exception as e:
+ Messages.send('>>> Load Error: {}: {}\n'.format(file_path, str(e)))
+ return False
+ finally:
+ self._auto_hier_block_generate_chain.discard(file_path)
+ Messages.set_indent(len(self._auto_hier_block_generate_chain))
+
+ try:
+ Messages.send('>>> Generating: {}\n'.format(file_path))
+ generator = self.Generator(flow_graph, file_path)
+ generator.write()
+ except Exception as e:
+ Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e)))
+ return False
+
+ self.load_block_xml(generator.get_file_path_xml())
+ return True
+
+ def load_blocks(self):
+ """load the blocks and block tree from the search paths"""
+ self._docstring_extractor.start()
+ # Reset
+ self.blocks = odict()
+ self._blocks_n = odict()
+ self._category_trees_n = list()
+ self.domains.clear()
+ self.connection_templates.clear()
+ ParseXML.xml_failures.clear()
+ # Try to parse and load blocks
+ for xml_file in self.iter_xml_files():
+ try:
+ if xml_file.endswith("block_tree.xml"):
+ self.load_category_tree_xml(xml_file)
+ elif xml_file.endswith('domain.xml'):
+ self.load_domain_xml(xml_file)
+ else:
+ self.load_block_xml(xml_file)
+ except ParseXML.XMLSyntaxError as e:
+ # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file)
+ pass
+ except Exception as e:
+ print >> sys.stderr, 'Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file)
+
+ self._docstring_extractor.finish()
+ # self._docstring_extractor.wait()
+
+ def iter_xml_files(self):
+ """Iterator for block descriptions and category trees"""
+ for block_path in self.config.block_paths:
+ if os.path.isfile(block_path):
+ yield block_path
+ elif os.path.isdir(block_path):
+ for dirpath, dirnames, filenames in os.walk(block_path):
+ for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)):
+ yield os.path.join(dirpath, filename)
+
+ def load_block_xml(self, xml_file):
+ """Load block description from xml file"""
+ # Validate and import
+ ParseXML.validate_dtd(xml_file, self._block_dtd)
+ n = ParseXML.from_file(xml_file).find('block')
+ n['block_wrapper_path'] = xml_file # inject block wrapper path
+ # Get block instance and add it to the list of blocks
+ block = self.Block(self._flow_graph, n)
+ key = block.get_key()
+ if key in self.blocks:
+ print >> sys.stderr, 'Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file)
+ else: # Store the block
+ self.blocks[key] = block
+ self._blocks_n[key] = n
+
+ self._docstring_extractor.query(
+ block.get_key(),
+ block.get_imports(raw=True),
+ block.get_make(raw=True)
+ )
+
+ 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).find('cat')
+ self._category_trees_n.append(n)
+
+ def load_domain_xml(self, xml_file):
+ """Load a domain properties and connection templates from XML"""
+ ParseXML.validate_dtd(xml_file, Constants.DOMAIN_DTD)
+ n = ParseXML.from_file(xml_file).find('domain')
+
+ key = n.find('key')
+ if not key:
+ print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file)
+ return
+ if key in self.domains: # test against repeated keys
+ print >> sys.stderr, 'Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file)
+ return
+
+ #to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '')
+ def to_bool(s, d):
+ if s is not None:
+ return s.lower() not in ('false', 'off', '0', '')
+ return d
+
+ color = n.find('color') or ''
+ try:
+ import gtk # ugly but handy
+ gtk.gdk.color_parse(color)
+ except (ValueError, ImportError):
+ if color: # no color is okay, default set in GUI
+ print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key)
+ color = None
+
+ self.domains[key] = dict(
+ name=n.find('name') or key,
+ multiple_sinks=to_bool(n.find('multiple_sinks'), True),
+ multiple_sources=to_bool(n.find('multiple_sources'), False),
+ color=color
+ )
+ for connection_n in n.findall('connection'):
+ key = (connection_n.find('source_domain'), connection_n.find('sink_domain'))
+ if not all(key):
+ print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file)
+ elif key in self.connection_templates:
+ print >> sys.stderr, 'Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file)
+ else:
+ self.connection_templates[key] = connection_n.find('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.find('name')]
+ block_tree.add_block(parent)
+ # Recursive call to load sub categories
+ map(lambda c: load_category(c, parent), cat_n.findall('cat'))
+ # Add blocks in this category
+ for block_key in cat_n.findall('block'):
+ if block_key not in self.blocks:
+ print >> sys.stderr, 'Warning: Block key "{}" not found when loading category tree.'.format(block_key)
+ 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 self.blocks.itervalues():
+ # 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 docstrings.iteritems():
+ if not docstring or match.endswith('_sptr'):
+ continue
+ docstring = docstring.replace('\n\n', '\n').strip()
+ docs[match] = docstring
+ self.block_docstrings[key] = docs
+
+ ##############################################
+ # Access
+ ##############################################
+
+ def parse_flow_graph(self, flow_graph_file):
+ """
+ Parse a saved flow graph file.
+ Ensure that the file exists, and passes the dtd check.
+
+ Args:
+ flow_graph_file: the flow graph file
+
+ Returns:
+ nested data
+ @throws exception if the validation fails
+ """
+ flow_graph_file = flow_graph_file or self._default_flow_graph
+ open(flow_graph_file, 'r') # Test open
+ ParseXML.validate_dtd(flow_graph_file, Constants.FLOW_GRAPH_DTD)
+ return ParseXML.from_file(flow_graph_file)
+
+ def get_new_flow_graph(self):
+ return self.FlowGraph(platform=self)
+
+ def get_blocks(self):
+ return self.blocks.values()
+
+ def get_new_block(self, flow_graph, key):
+ return self.Block(flow_graph, n=self._blocks_n[key])
+
+ def get_colors(self):
+ return [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES]
diff --git a/grc/core/Port.py b/grc/core/Port.py
new file mode 100644
index 0000000000..6a8f484082
--- /dev/null
+++ b/grc/core/Port.py
@@ -0,0 +1,404 @@
+"""
+Copyright 2008-2015 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 .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN
+from .Element import Element
+
+from . import Constants
+
+
+def _get_source_from_virtual_sink_port(vsp):
+ """
+ Resolve the source port that is connected to the given virtual sink port.
+ Use the get source from virtual source to recursively resolve subsequent ports.
+ """
+ try:
+ return _get_source_from_virtual_source_port(
+ vsp.get_enabled_connections()[0].get_source())
+ except:
+ raise Exception('Could not resolve source for virtual sink port {}'.format(vsp))
+
+
+def _get_source_from_virtual_source_port(vsp, traversed=[]):
+ """
+ Recursively resolve source ports over the virtual connections.
+ Keep track of traversed sources to avoid recursive loops.
+ """
+ if not vsp.get_parent().is_virtual_source():
+ return vsp
+ if vsp in traversed:
+ raise Exception('Loop found when resolving virtual source {}'.format(vsp))
+ try:
+ return _get_source_from_virtual_source_port(
+ _get_source_from_virtual_sink_port(
+ filter( # Get all virtual sinks with a matching stream id
+ lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
+ filter( # Get all enabled blocks that are also virtual sinks
+ lambda b: b.is_virtual_sink(),
+ vsp.get_parent().get_parent().get_enabled_blocks(),
+ ),
+ )[0].get_sinks()[0]
+ ), traversed + [vsp],
+ )
+ except:
+ raise Exception('Could not resolve source for virtual source port {}'.format(vsp))
+
+
+def _get_sink_from_virtual_source_port(vsp):
+ """
+ Resolve the sink port that is connected to the given virtual source port.
+ Use the get sink from virtual sink to recursively resolve subsequent ports.
+ """
+ try:
+ # Could have many connections, but use first
+ return _get_sink_from_virtual_sink_port(
+ vsp.get_enabled_connections()[0].get_sink())
+ except:
+ raise Exception('Could not resolve source for virtual source port {}'.format(vsp))
+
+
+def _get_sink_from_virtual_sink_port(vsp, traversed=[]):
+ """
+ Recursively resolve sink ports over the virtual connections.
+ Keep track of traversed sinks to avoid recursive loops.
+ """
+ if not vsp.get_parent().is_virtual_sink():
+ return vsp
+ if vsp in traversed:
+ raise Exception('Loop found when resolving virtual sink {}'.format(vsp))
+ try:
+ return _get_sink_from_virtual_sink_port(
+ _get_sink_from_virtual_source_port(
+ filter( # Get all virtual source with a matching stream id
+ lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
+ filter( # Get all enabled blocks that are also virtual sinks
+ lambda b: b.is_virtual_source(),
+ vsp.get_parent().get_parent().get_enabled_blocks(),
+ ),
+ )[0].get_sources()[0]
+ ), traversed + [vsp],
+ )
+ except:
+ raise Exception('Could not resolve source for virtual sink port {}'.format(vsp))
+
+
+class Port(Element):
+
+ is_port = True
+
+ def __init__(self, block, n, dir):
+ """
+ Make a new port from nested data.
+
+ Args:
+ block: the parent element
+ n: the nested odict
+ dir: the direction
+ """
+ self._n = n
+ if n['type'] == 'message':
+ n['domain'] = GR_MESSAGE_DOMAIN
+ if 'domain' not in n:
+ n['domain'] = DEFAULT_DOMAIN
+ elif n['domain'] == GR_MESSAGE_DOMAIN:
+ n['key'] = n['name']
+ n['type'] = 'message' # For port color
+ if n['type'] == 'msg':
+ n['key'] = 'msg'
+ if not n.find('key'):
+ n['key'] = str(next(block.port_counters[dir == 'source']))
+
+ # Build the port
+ Element.__init__(self, block)
+ # Grab the data
+ self._name = n['name']
+ self._key = n['key']
+ self._type = n['type'] or ''
+ self._domain = n['domain']
+ self._hide = n.find('hide') or ''
+ self._dir = dir
+ self._hide_evaluated = False # Updated on rewrite()
+
+ self._nports = n.find('nports') or ''
+ self._vlen = n.find('vlen') or ''
+ self._optional = bool(n.find('optional'))
+ self._clones = [] # References to cloned ports (for nports > 1)
+
+ def __str__(self):
+ if self.is_source:
+ return 'Source - {}({})'.format(self.get_name(), self.get_key())
+ if self.is_sink:
+ return 'Sink - {}({})'.format(self.get_name(), self.get_key())
+
+ def get_types(self):
+ return Constants.TYPE_TO_SIZEOF.keys()
+
+ def is_type_empty(self):
+ return not self._n['type']
+
+ def validate(self):
+ Element.validate(self)
+ if self.get_type() not in self.get_types():
+ self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
+ platform = self.get_parent().get_parent().get_parent()
+ if self.get_domain() not in platform.domains:
+ self.add_error_message('Domain key "{}" is not registered.'.format(self.get_domain()))
+ if not self.get_enabled_connections() and not self.get_optional():
+ self.add_error_message('Port is not connected.')
+ # Message port logic
+ if self.get_type() == 'msg':
+ if self.get_nports():
+ self.add_error_message('A port of type "msg" cannot have "nports" set.')
+ if self.get_vlen() != 1:
+ self.add_error_message('A port of type "msg" must have a "vlen" of 1.')
+
+ def rewrite(self):
+ """
+ Handle the port cloning for virtual blocks.
+ """
+ if self.is_type_empty():
+ try:
+ # Clone type and vlen
+ source = self.resolve_empty_type()
+ self._type = str(source.get_type())
+ self._vlen = str(source.get_vlen())
+ except:
+ # Reset type and vlen
+ self._type = ''
+ self._vlen = ''
+
+ Element.rewrite(self)
+ hide = self.get_parent().resolve_dependencies(self._hide).strip().lower()
+ self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide)
+
+ # Update domain if was deduced from (dynamic) port type
+ type_ = self.get_type()
+ if self._domain == GR_STREAM_DOMAIN and type_ == "message":
+ self._domain = GR_MESSAGE_DOMAIN
+ self._key = self._name
+ if self._domain == GR_MESSAGE_DOMAIN and type_ != "message":
+ self._domain = GR_STREAM_DOMAIN
+ self._key = '0' # Is rectified in rewrite()
+
+ def resolve_virtual_source(self):
+ if self.get_parent().is_virtual_sink():
+ return _get_source_from_virtual_sink_port(self)
+ if self.get_parent().is_virtual_source():
+ return _get_source_from_virtual_source_port(self)
+
+ def resolve_empty_type(self):
+ if self.is_sink:
+ try:
+ src = _get_source_from_virtual_sink_port(self)
+ if not src.is_type_empty():
+ return src
+ except:
+ pass
+ sink = _get_sink_from_virtual_sink_port(self)
+ if not sink.is_type_empty():
+ return sink
+ if self.is_source:
+ try:
+ src = _get_source_from_virtual_source_port(self)
+ if not src.is_type_empty():
+ return src
+ except:
+ pass
+ sink = _get_sink_from_virtual_source_port(self)
+ if not sink.is_type_empty():
+ return sink
+
+ def get_vlen(self):
+ """
+ Get the vector length.
+ If the evaluation of vlen cannot be cast to an integer, return 1.
+
+ Returns:
+ the vector length or 1
+ """
+ vlen = self.get_parent().resolve_dependencies(self._vlen)
+ try:
+ return int(self.get_parent().get_parent().evaluate(vlen))
+ except:
+ return 1
+
+ def get_nports(self):
+ """
+ Get the number of ports.
+ If already blank, return a blank
+ If the evaluation of nports cannot be cast to a positive integer, return 1.
+
+ Returns:
+ the number of ports or 1
+ """
+ if self._nports == '':
+ return ''
+
+ nports = self.get_parent().resolve_dependencies(self._nports)
+ try:
+ return max(1, int(self.get_parent().get_parent().evaluate(nports)))
+ except:
+ return 1
+
+ def get_optional(self):
+ return bool(self._optional)
+
+ def get_color(self):
+ """
+ Get the color that represents this port's type.
+ Codes differ for ports where the vec length is 1 or greater than 1.
+
+ Returns:
+ a hex color code.
+ """
+ try:
+ color = Constants.TYPE_TO_COLOR[self.get_type()]
+ vlen = self.get_vlen()
+ if vlen == 1:
+ return color
+ color_val = int(color[1:], 16)
+ r = (color_val >> 16) & 0xff
+ g = (color_val >> 8) & 0xff
+ b = (color_val >> 0) & 0xff
+ dark = (0, 0, 30, 50, 70)[min(4, vlen)]
+ r = max(r-dark, 0)
+ g = max(g-dark, 0)
+ b = max(b-dark, 0)
+ # TODO: Change this to .format()
+ return '#%.2x%.2x%.2x' % (r, g, b)
+ except:
+ return '#FFFFFF'
+
+ def get_clones(self):
+ """
+ Get the clones of this master port (nports > 1)
+
+ Returns:
+ a list of ports
+ """
+ return self._clones
+
+ def add_clone(self):
+ """
+ Create a clone of this (master) port and store a reference in self._clones.
+
+ The new port name (and key for message ports) will have index 1... appended.
+ If this is the first clone, this (master) port will get a 0 appended to its name (and key)
+
+ Returns:
+ the cloned port
+ """
+ # Add index to master port name if there are no clones yet
+ if not self._clones:
+ self._name = self._n['name'] + '0'
+ # Also update key for none stream ports
+ if not self._key.isdigit():
+ self._key = self._name
+
+ # Prepare a copy of the odict for the clone
+ n = self._n.copy()
+ # Remove nports from the key so the copy cannot be a duplicator
+ if 'nports' in n:
+ n.pop('nports')
+ n['name'] = self._n['name'] + str(len(self._clones) + 1)
+ # Dummy value 99999 will be fixed later
+ n['key'] = '99999' if self._key.isdigit() else n['name']
+
+ # Clone
+ port = self.__class__(self.get_parent(), n, self._dir)
+ self._clones.append(port)
+ return port
+
+ def remove_clone(self, port):
+ """
+ Remove a cloned port (from the list of clones only)
+ Remove the index 0 of the master port name (and key9 if there are no more clones left
+ """
+ self._clones.remove(port)
+ # Remove index from master port name if there are no more clones
+ if not self._clones:
+ self._name = self._n['name']
+ # Also update key for none stream ports
+ if not self._key.isdigit():
+ self._key = self._name
+
+ def get_name(self):
+ number = ''
+ if self.get_type() == 'bus':
+ busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui())
+ number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports()))
+ return self._name + number
+
+ def get_key(self):
+ return self._key
+
+ @property
+ def is_sink(self):
+ return self._dir == 'sink'
+
+ @property
+ def is_source(self):
+ return self._dir == 'source'
+
+ def get_type(self):
+ return self.get_parent().resolve_dependencies(self._type)
+
+ def get_domain(self):
+ return self._domain
+
+ def get_hide(self):
+ return self._hide_evaluated
+
+ def get_connections(self):
+ """
+ Get all connections that use this port.
+
+ Returns:
+ a list of connection objects
+ """
+ connections = self.get_parent().get_parent().connections
+ connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections)
+ return connections
+
+ def get_enabled_connections(self):
+ """
+ Get all enabled connections that use this port.
+
+ Returns:
+ a list of connection objects
+ """
+ return filter(lambda c: c.get_enabled(), self.get_connections())
+
+ def get_associated_ports(self):
+ if not self.get_type() == 'bus':
+ return [self]
+ else:
+ if self.is_source:
+ get_ports = self.get_parent().get_sources
+ bus_structure = self.get_parent().current_bus_structure['source']
+ else:
+ get_ports = self.get_parent().get_sinks
+ bus_structure = self.get_parent().current_bus_structure['sink']
+
+ ports = [i for i in get_ports() if not i.get_type() == 'bus']
+ if bus_structure:
+ busses = [i for i in get_ports() if i.get_type() == 'bus']
+ bus_index = busses.index(self)
+ ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports)
+ return ports
diff --git a/grc/grc_gnuradio/__init__.py b/grc/core/__init__.py
index 8b13789179..8b13789179 100644
--- a/grc/grc_gnuradio/__init__.py
+++ b/grc/core/__init__.py
diff --git a/grc/python/block.dtd b/grc/core/block.dtd
index 145f4d8610..145f4d8610 100644
--- a/grc/python/block.dtd
+++ b/grc/core/block.dtd
diff --git a/grc/base/block_tree.dtd b/grc/core/block_tree.dtd
index 9e23576477..9e23576477 100644
--- a/grc/base/block_tree.dtd
+++ b/grc/core/block_tree.dtd
diff --git a/grc/python/default_flow_graph.grc b/grc/core/default_flow_graph.grc
index 059509d34b..059509d34b 100644
--- a/grc/python/default_flow_graph.grc
+++ b/grc/core/default_flow_graph.grc
diff --git a/grc/base/domain.dtd b/grc/core/domain.dtd
index b5b0b8bf39..b5b0b8bf39 100644
--- a/grc/base/domain.dtd
+++ b/grc/core/domain.dtd
diff --git a/grc/base/flow_graph.dtd b/grc/core/flow_graph.dtd
index bdfe1dc059..bdfe1dc059 100644
--- a/grc/base/flow_graph.dtd
+++ b/grc/core/flow_graph.dtd
diff --git a/grc/grc_gnuradio/CMakeLists.txt b/grc/core/generator/CMakeLists.txt
index e992a60a39..4bdd59a7a2 100644
--- a/grc/grc_gnuradio/CMakeLists.txt
+++ b/grc/core/generator/CMakeLists.txt
@@ -17,19 +17,16 @@
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
-########################################################################
+file(GLOB py_files "*.py")
+
GR_PYTHON_INSTALL(
- FILES __init__.py
- DESTINATION ${GR_PYTHON_DIR}/grc_gnuradio
+ FILES ${py_files}
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/generator
COMPONENT "grc"
)
-GR_PYTHON_INSTALL(FILES
- blks2/__init__.py
- blks2/error_rate.py
- blks2/packet.py
- blks2/selector.py
- blks2/tcp.py
- DESTINATION ${GR_PYTHON_DIR}/grc_gnuradio/blks2
+install(FILES
+ flow_graph.tmpl
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/generator
COMPONENT "grc"
)
diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py
new file mode 100644
index 0000000000..3723005576
--- /dev/null
+++ b/grc/core/generator/FlowGraphProxy.py
@@ -0,0 +1,126 @@
+# Copyright 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
+
+
+class FlowGraphProxy(object):
+
+ def __init__(self, fg):
+ self._fg = fg
+
+ def __getattr__(self, item):
+ return getattr(self._fg, item)
+
+ def get_hier_block_stream_io(self, direction):
+ """
+ Get a list of stream io signatures for this flow graph.
+
+ Args:
+ direction: a string of 'in' or 'out'
+
+ Returns:
+ a list of dicts with: type, label, vlen, size, optional
+ """
+ return filter(lambda p: p['type'] != "message", self.get_hier_block_io(direction))
+
+ def get_hier_block_message_io(self, direction):
+ """
+ Get a list of message io signatures for this flow graph.
+
+ Args:
+ direction: a string of 'in' or 'out'
+
+ Returns:
+ a list of dicts with: type, label, vlen, size, optional
+ """
+ return filter(lambda p: p['type'] == "message", self.get_hier_block_io(direction))
+
+ def get_hier_block_io(self, direction):
+ """
+ Get a list of io ports for this flow graph.
+
+ Args:
+ direction: a string of 'in' or 'out'
+
+ Returns:
+ a list of dicts with: type, label, vlen, size, optional
+ """
+ pads = self.get_pad_sources() if direction in ('sink', 'in') else \
+ self.get_pad_sinks() if direction in ('source', 'out') else []
+ ports = []
+ for pad in pads:
+ master = {
+ 'label': str(pad.get_param('label').get_evaluated()),
+ 'type': str(pad.get_param('type').get_evaluated()),
+ 'vlen': str(pad.get_param('vlen').get_value()),
+ 'size': pad.get_param('type').get_opt('size'),
+ 'optional': bool(pad.get_param('optional').get_evaluated()),
+ }
+ num_ports = pad.get_param('num_streams').get_evaluated()
+ if num_ports > 1:
+ for i in xrange(num_ports):
+ clone = master.copy()
+ clone['label'] += str(i)
+ ports.append(clone)
+ else:
+ ports.append(master)
+ return ports
+
+ def get_pad_sources(self):
+ """
+ Get a list of pad source blocks sorted by id order.
+
+ Returns:
+ a list of pad source blocks in this flow graph
+ """
+ pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks())
+ return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
+
+ def get_pad_sinks(self):
+ """
+ Get a list of pad sink blocks sorted by id order.
+
+ Returns:
+ a list of pad sink blocks in this flow graph
+ """
+ pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks())
+ return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
+
+ def get_pad_port_global_key(self, port):
+ """
+ Get the key for a port of a pad source/sink to use in connect()
+ This takes into account that pad blocks may have multiple ports
+
+ Returns:
+ the key (str)
+ """
+ key_offset = 0
+ pads = self.get_pad_sources() if port.is_source else self.get_pad_sinks()
+ for pad in pads:
+ # using the block param 'type' instead of the port domain here
+ # to emphasize that hier block generation is domain agnostic
+ is_message_pad = pad.get_param('type').get_evaluated() == "message"
+ if port.get_parent() == pad:
+ if is_message_pad:
+ key = pad.get_param('label').get_value()
+ else:
+ key = str(key_offset + int(port.get_key()))
+ return key
+ else:
+ # assuming we have either only sources or sinks
+ if not is_message_pad:
+ key_offset += len(pad.get_ports())
+ return -1 \ No newline at end of file
diff --git a/grc/python/Generator.py b/grc/core/generator/Generator.py
index 56e3a6e78f..91671072d6 100644
--- a/grc/python/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -1,41 +1,37 @@
-"""
-Copyright 2008-2011 Free Software Foundation, Inc.
-This file is part of GNU Radio
+# Copyright 2008-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
-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
-"""
+import codecs
import os
-import sys
-import subprocess
import tempfile
-import shlex
-import codecs
-import re # for shlex_quote
-from distutils.spawn import find_executable
from Cheetah.Template import Template
-from .. gui import Messages
-from .. base import ParseXML
-from .. base import odict
-from .. base.Constants import BLOCK_FLAG_NEED_QT_GUI
+from .FlowGraphProxy import FlowGraphProxy
+from .. import ParseXML, Messages
+from ..Constants import (
+ TOP_BLOCK_FILE_MODE, BLOCK_FLAG_NEED_QT_GUI,
+ HIER_BLOCK_FILE_MODE, BLOCK_DTD
+)
+from ..utils import expr_utils, odict
-from . Constants import TOP_BLOCK_FILE_MODE, FLOW_GRAPH_TEMPLATE, \
- XTERM_EXECUTABLE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, BLOCK_DTD
-from . import expr_utils
+DATA_DIR = os.path.dirname(__file__)
+FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl')
class Generator(object):
@@ -50,19 +46,16 @@ class Generator(object):
flow_graph: the flow graph object
file_path: the path to the grc file
"""
- self._generate_options = flow_graph.get_option('generate_options')
- if self._generate_options == 'hb':
+ self.generate_options = flow_graph.get_option('generate_options')
+ if self.generate_options == 'hb':
generator_cls = HierBlockGenerator
- elif self._generate_options == 'hb_qt_gui':
+ elif self.generate_options == 'hb_qt_gui':
generator_cls = QtHierBlockGenerator
else:
generator_cls = TopBlockGenerator
self._generator = generator_cls(flow_graph, file_path)
- def get_generate_options(self):
- return self._generate_options
-
def __getattr__(self, item):
"""get all other attrib from actual generator object"""
return getattr(self._generator, item)
@@ -78,23 +71,23 @@ class TopBlockGenerator(object):
flow_graph: the flow graph object
file_path: the path to write the file to
"""
- self._flow_graph = flow_graph
+ self._flow_graph = FlowGraphProxy(flow_graph)
self._generate_options = self._flow_graph.get_option('generate_options')
self._mode = TOP_BLOCK_FILE_MODE
dirname = self._dirname = os.path.dirname(file_path)
- # handle the case where the directory is read-only
- # in this case, use the system's temp directory
+ # Handle the case where the directory is read-only
+ # In this case, use the system's temp directory
if not os.access(dirname, os.W_OK):
dirname = tempfile.gettempdir()
filename = self._flow_graph.get_option('id') + '.py'
- self._file_path = os.path.join(dirname, filename)
+ self.file_path = os.path.join(dirname, filename)
def get_file_path(self):
- return self._file_path
+ return self.file_path
def write(self):
"""generate output and write it to files"""
- # do throttle warning
+ # Do throttle warning
throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks())
if not throttling_blocks and not self._generate_options.startswith('hb'):
Messages.send_warning("This flow graph may not have flow control: "
@@ -109,48 +102,16 @@ class TopBlockGenerator(object):
"e.g. a hardware source or sink. "
"This is usually undesired. Consider "
"removing the throttle block.")
- # generate
+ # Generate
for filename, data in self._build_python_code_from_template():
with codecs.open(filename, 'w', encoding='utf-8') as fp:
fp.write(data)
- if filename == self.get_file_path():
+ if filename == self.file_path:
try:
os.chmod(filename, self._mode)
except:
pass
- def get_popen(self):
- """
- Execute this python flow graph.
-
- Returns:
- a popen object
- """
- run_command = self._flow_graph.get_option('run_command')
- try:
- run_command = run_command.format(
- python=shlex_quote(sys.executable),
- filename=shlex_quote(self.get_file_path()))
- run_command_args = shlex.split(run_command)
- except Exception as e:
- raise ValueError("Can't parse run command {!r}: {}".format(run_command, e))
-
- # when in no gui mode on linux, use a graphical terminal (looks nice)
- xterm_executable = find_executable(XTERM_EXECUTABLE)
- if self._generate_options == 'no_gui' and xterm_executable:
- run_command_args = [xterm_executable, '-e', run_command]
-
- # this does not reproduce a shell executable command string, if a graphical
- # terminal is used. Passing run_command though shlex_quote would do it but
- # it looks really ugly and confusing in the console panel.
- Messages.send_start_exec(' '.join(run_command_args))
-
- return subprocess.Popen(
- args=run_command_args,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
- shell=False, universal_newlines=True
- )
-
def _build_python_code_from_template(self):
"""
Convert the flow graph to python code.
@@ -167,21 +128,21 @@ class TopBlockGenerator(object):
parameters = fg.get_parameters()
monitors = fg.get_monitors()
- # list of blocks not including variables and imports and parameters and disabled
+ # List of blocks not including variables and imports and parameters and disabled
def _get_block_sort_text(block):
code = block.get_make().replace(block.get_id(), ' ')
try:
- code += block.get_param('notebook').get_value() # older gui markup w/ wxgui
+ code += block.get_param('notebook').get_value() # Older gui markup w/ wxgui
except:
pass
try:
- code += block.get_param('gui_hint').get_value() # newer gui markup w/ qtgui
+ code += block.get_param('gui_hint').get_value() # Newer gui markup w/ qtgui
except:
pass
return code
blocks = expr_utils.sort_objects(
- filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.iter_blocks()),
+ filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks),
lambda b: b.get_id(), _get_block_sort_text
)
# List of regular blocks (all blocks minus the special ones)
@@ -198,7 +159,8 @@ class TopBlockGenerator(object):
output.append((file_path, src))
# Filter out virtual sink connections
- cf = lambda c: not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink())
+ def cf(c):
+ return not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink())
connections = filter(cf, fg.get_enabled_connections())
# Get the virtual blocks and resolve their connections
@@ -220,8 +182,7 @@ class TopBlockGenerator(object):
for block in bypassed_blocks:
# Get the upstream connection (off of the sink ports)
# Use *connections* not get_connections()
- get_source_connection = lambda c: c.get_sink() == block.get_sinks()[0]
- source_connection = filter(get_source_connection, connections)
+ source_connection = filter(lambda c: c.get_sink() == block.get_sinks()[0], connections)
# The source connection should never have more than one element.
assert (len(source_connection) == 1)
@@ -229,8 +190,7 @@ class TopBlockGenerator(object):
source_port = source_connection[0].get_source()
# Loop through all the downstream connections
- get_sink_connections = lambda c: c.get_source() == block.get_sources()[0]
- for sink in filter(get_sink_connections, connections):
+ for sink in filter(lambda c: c.get_source() == block.get_sources()[0], connections):
if not sink.get_enabled():
# Ignore disabled connections
continue
@@ -248,23 +208,23 @@ class TopBlockGenerator(object):
c.get_source().get_parent().get_id(), c.get_sink().get_parent().get_id()
))
- connection_templates = fg.get_parent().get_connection_templates()
+ connection_templates = fg.get_parent().connection_templates
msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections())
- # list of variable names
+ # List of variable names
var_ids = [var.get_id() for var in parameters + variables]
- # prepend self.
+ # Prepend self.
replace_dict = dict([(var_id, 'self.%s' % var_id) for var_id in var_ids])
- # list of callbacks
+ # 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()], [])
- ]
- # map var id to callbacks
+ ]
+ # Map var id to callbacks
var_id2cbs = dict([
(var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks))
for var_id in var_ids
])
- # load the namespace
+ # Load the namespace
namespace = {
'title': title,
'imports': imports,
@@ -279,9 +239,9 @@ class TopBlockGenerator(object):
'generate_options': self._generate_options,
'var_id2cbs': var_id2cbs,
}
- # build the template
+ # Build the template
t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace)
- output.append((self.get_file_path(), str(t)))
+ output.append((self.file_path, str(t)))
return output
@@ -297,10 +257,15 @@ 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.get_parent()
+
+ 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)
+
self._mode = HIER_BLOCK_FILE_MODE
- self._file_path = os.path.join(HIER_BLOCKS_LIB_DIR,
- self._flow_graph.get_option('id') + '.py')
- self._file_path_xml = self._file_path + '.xml'
+ self.file_path = os.path.join(hier_block_lib_dir, self._flow_graph.get_option('id') + '.py')
+ self._file_path_xml = self.file_path + '.xml'
def get_file_path_xml(self):
return self._file_path_xml
@@ -322,7 +287,7 @@ class HierBlockGenerator(TopBlockGenerator):
Returns:
a xml node tree
"""
- # extract info from the flow graph
+ # Extract info from the flow graph
block_key = self._flow_graph.get_option('id')
parameters = self._flow_graph.get_parameters()
@@ -331,7 +296,7 @@ class HierBlockGenerator(TopBlockGenerator):
return "$"+name
return name
- # build the nested data
+ # Build the nested data
block_n = odict()
block_n['name'] = self._flow_graph.get_option('title') or \
self._flow_graph.get_option('id').replace('_', ' ').title()
@@ -339,7 +304,7 @@ class HierBlockGenerator(TopBlockGenerator):
block_n['category'] = self._flow_graph.get_option('category')
block_n['import'] = "from {0} import {0} # grc-generated hier_block".format(
self._flow_graph.get_option('id'))
- # make data
+ # Make data
if parameters:
block_n['make'] = '{cls}(\n {kwargs},\n)'.format(
cls=block_key,
@@ -349,7 +314,7 @@ class HierBlockGenerator(TopBlockGenerator):
)
else:
block_n['make'] = '{cls}()'.format(cls=block_key)
- # callback data
+ # Callback data
block_n['callback'] = [
'set_{key}(${key})'.format(key=param.get_id()) for param in parameters
]
@@ -364,13 +329,13 @@ class HierBlockGenerator(TopBlockGenerator):
param_n['type'] = 'raw'
block_n['param'].append(param_n)
- # bus stuff
+ # Bus stuff
if self._flow_graph.get_bussink():
block_n['bus_sink'] = '1'
if self._flow_graph.get_bussrc():
block_n['bus_source'] = '1'
- # sink/source ports
+ # Sink/source ports
for direction in ('sink', 'source'):
block_n[direction] = list()
for port in self._flow_graph.get_hier_block_io(direction):
@@ -383,7 +348,7 @@ class HierBlockGenerator(TopBlockGenerator):
port_n['optional'] = '1'
block_n[direction].append(port_n)
- # more bus stuff
+ # More bus stuff
bus_struct_sink = self._flow_graph.get_bus_structure_sink()
if bus_struct_sink:
block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value()
@@ -391,11 +356,11 @@ class HierBlockGenerator(TopBlockGenerator):
if bus_struct_src:
block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value()
- # documentation
+ # Documentation
block_n['doc'] = "\n".join(field for field in (
self._flow_graph.get_option('author'),
self._flow_graph.get_option('description'),
- self.get_file_path()
+ self.file_path
) if field)
block_n['grc_source'] = str(self._flow_graph.grc_file_path)
@@ -427,21 +392,3 @@ class QtHierBlockGenerator(HierBlockGenerator):
"\n${gui_hint()($win)}"
)
return n
-
-
-###########################################################
-# back-port from python3
-###########################################################
-_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
-
-
-def shlex_quote(s):
- """Return a shell-escaped version of the string *s*."""
- if not s:
- return "''"
- if _find_unsafe(s) is None:
- return s
-
- # use single quotes, and put single quotes into double quotes
- # the string $'b is then quoted as '$'"'"'b'
- return "'" + s.replace("'", "'\"'\"'") + "'"
diff --git a/grc/core/generator/__init__.py b/grc/core/generator/__init__.py
new file mode 100644
index 0000000000..f44b94a85d
--- /dev/null
+++ b/grc/core/generator/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2008-2015 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 Generator import Generator
diff --git a/grc/python/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl
index bd8025b676..ecdb89390e 100644
--- a/grc/python/flow_graph.tmpl
+++ b/grc/core/generator/flow_graph.tmpl
@@ -274,8 +274,8 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])
self.settings = Qt.QSettings("GNU Radio", "$class_name")
self.settings.setValue("geometry", self.saveGeometry())
event.accept()
-
#if $flow_graph.get_option('qt_qss_theme')
+
def setStyleSheetFromFile(self, filename):
try:
if not os.path.exists(filename):
@@ -336,7 +336,12 @@ $short_id#slurp
def argument_parser():
- parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
+ #set $desc_args = 'usage="%prog: [options]", option_class=eng_option'
+ #if $flow_graph.get_option('description')
+ #set $desc_args += ', description=description'
+ description = $repr($flow_graph.get_option('description'))
+ #end if
+ parser = OptionParser($desc_args)
#for $param in $parameters
#set $type = $param.get_param('type').get_value()
#if $type
diff --git a/grc/grc_gnuradio/blks2/__init__.py b/grc/core/utils/CMakeLists.txt
index e6941ab91b..2528fbc43c 100644
--- a/grc/grc_gnuradio/blks2/__init__.py
+++ b/grc/core/utils/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2008-2011 Free Software Foundation, Inc.
+# Copyright 2015 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -16,11 +16,11 @@
# along with GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
-#
-from selector import selector, valve
-from packet import options, packet_encoder, packet_decoder, \
- packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \
- packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c
-from error_rate import error_rate
-from tcp import tcp_source, tcp_sink
+file(GLOB py_files "*.py")
+
+GR_PYTHON_INSTALL(
+ FILES ${py_files}
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/utils
+ COMPONENT "grc"
+)
diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py
new file mode 100644
index 0000000000..6b23da2723
--- /dev/null
+++ b/grc/core/utils/__init__.py
@@ -0,0 +1,22 @@
+# Copyright 2008-2015 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
+
+import expr_utils
+import epy_block_io
+import extract_docs
+
+from odict import odict
diff --git a/grc/core/utils/complexity.py b/grc/core/utils/complexity.py
new file mode 100644
index 0000000000..baa8040db4
--- /dev/null
+++ b/grc/core/utils/complexity.py
@@ -0,0 +1,49 @@
+
+def calculate_flowgraph_complexity(flowgraph):
+ """ Determines the complexity of a flowgraph """
+ dbal = 0
+ for block in flowgraph.blocks:
+ # Skip options block
+ if block.get_key() == 'options':
+ continue
+
+ # Don't worry about optional sinks?
+ sink_list = filter(lambda c: not c.get_optional(), block.get_sinks())
+ source_list = filter(lambda c: not c.get_optional(), block.get_sources())
+ sinks = float(len(sink_list))
+ sources = float(len(source_list))
+ base = max(min(sinks, sources), 1)
+
+ # Port ratio multiplier
+ if min(sinks, sources) > 0:
+ multi = sinks / sources
+ multi = (1 / multi) if multi > 1 else multi
+ else:
+ multi = 1
+
+ # Connection ratio multiplier
+ sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0)
+ source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0)
+ dbal = dbal + (base * multi * sink_multi * source_multi)
+
+ blocks = float(len(flowgraph.blocks))
+ connections = float(len(flowgraph.connections))
+ elements = blocks + connections
+ disabled_connections = len(filter(lambda c: not c.get_enabled(), flowgraph.connections))
+ variables = elements - blocks - connections
+ enabled = float(len(flowgraph.get_enabled_blocks()))
+
+ # Disabled multiplier
+ if enabled > 0:
+ disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05))
+ else:
+ disabled_multi = 1
+
+ # Connection multiplier (How many connections )
+ if (connections - disabled_connections) > 0:
+ conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05))
+ else:
+ conn_multi = 1
+
+ final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6)
+ return final
diff --git a/grc/python/epy_block_io.py b/grc/core/utils/epy_block_io.py
index e089908a01..df3a4bbc3e 100644
--- a/grc/python/epy_block_io.py
+++ b/grc/core/utils/epy_block_io.py
@@ -2,9 +2,6 @@
import inspect
import collections
-from gnuradio import gr
-import pmt
-
TYPE_MAP = {
'complex64': 'complex', 'complex': 'complex',
@@ -31,21 +28,27 @@ def _ports(sigs, msgs):
return ports
-def _blk_class(source_code):
+def _find_block_class(source_code, cls):
ns = {}
try:
exec source_code in ns
except Exception as e:
raise ValueError("Can't interpret source code: " + str(e))
for var in ns.itervalues():
- if inspect.isclass(var)and issubclass(var, gr.gateway.gateway_block):
+ if inspect.isclass(var) and issubclass(var, cls):
return var
raise ValueError('No python block class found in code')
def extract(cls):
+ try:
+ from gnuradio import gr
+ import pmt
+ except ImportError:
+ raise EnvironmentError("Can't import GNU Radio")
+
if not inspect.isclass(cls):
- cls = _blk_class(cls)
+ cls = _find_block_class(cls, gr.gateway.gateway_block)
spec = inspect.getargspec(cls.__init__)
defaults = map(repr, spec.defaults or ())
diff --git a/grc/python/expr_utils.py b/grc/core/utils/expr_utils.py
index 9e0b2a4a0a..66911757d6 100644
--- a/grc/python/expr_utils.py
+++ b/grc/core/utils/expr_utils.py
@@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import string
VAR_CHARS = string.letters + string.digits + '_'
+
class graph(object):
"""
Simple graph structure held in a dictionary.
@@ -30,13 +31,16 @@ class graph(object):
def __str__(self): return str(self._graph)
def add_node(self, node_key):
- if self._graph.has_key(node_key): return
+ if node_key in self._graph:
+ return
self._graph[node_key] = set()
def remove_node(self, node_key):
- if not self._graph.has_key(node_key): return
+ if node_key not in self._graph:
+ return
for edges in self._graph.values():
- if node_key in edges: edges.remove(node_key)
+ if node_key in edges:
+ edges.remove(node_key)
self._graph.pop(node_key)
def add_edge(self, src_node_key, dest_node_key):
@@ -45,9 +49,12 @@ class graph(object):
def remove_edge(self, src_node_key, dest_node_key):
self._graph[src_node_key].remove(dest_node_key)
- def get_nodes(self): return self._graph.keys()
+ def get_nodes(self):
+ return self._graph.keys()
+
+ def get_edges(self, node_key):
+ return self._graph[node_key]
- def get_edges(self, node_key): return self._graph[node_key]
def expr_split(expr):
"""
@@ -66,7 +73,8 @@ def expr_split(expr):
quote = ''
for char in expr:
if quote or char in VAR_CHARS:
- if char == quote: quote = ''
+ if char == quote:
+ quote = ''
tok += char
elif char in ("'", '"'):
toks.append(tok)
@@ -79,6 +87,7 @@ def expr_split(expr):
toks.append(tok)
return filter(lambda t: t, toks)
+
def expr_replace(expr, replace_dict):
"""
Search for vars in the expression and add the prepend.
@@ -96,6 +105,7 @@ def expr_replace(expr, replace_dict):
expr_splits[i] = replace_dict[es]
return ''.join(expr_splits)
+
def get_variable_dependencies(expr, vars):
"""
Return a set of variables used in this expression.
@@ -110,6 +120,7 @@ def get_variable_dependencies(expr, vars):
expr_toks = expr_split(expr)
return set(filter(lambda v: v in expr_toks, vars))
+
def get_graph(exprs):
"""
Get a graph representing the variable dependencies
@@ -121,14 +132,17 @@ def get_graph(exprs):
a graph of variable deps
"""
vars = exprs.keys()
- #get dependencies for each expression, load into graph
+ # Get dependencies for each expression, load into graph
var_graph = graph()
- for var in vars: var_graph.add_node(var)
+ for var in vars:
+ var_graph.add_node(var)
for var, expr in exprs.iteritems():
for dep in get_variable_dependencies(expr, vars):
- if dep != var: var_graph.add_edge(dep, var)
+ if dep != var:
+ var_graph.add_edge(dep, var)
return var_graph
+
def sort_variables(exprs):
"""
Get a list of variables in order of dependencies.
@@ -142,17 +156,20 @@ def sort_variables(exprs):
"""
var_graph = get_graph(exprs)
sorted_vars = list()
- #determine dependency order
+ # Determine dependency order
while var_graph.get_nodes():
- #get a list of nodes with no edges
+ # Get a list of nodes with no edges
indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes())
- if not indep_vars: raise Exception('circular dependency caught in sort_variables')
- #add the indep vars to the end of the list
+ if not indep_vars:
+ raise Exception('circular dependency caught in sort_variables')
+ # Add the indep vars to the end of the list
sorted_vars.extend(sorted(indep_vars))
- #remove each edge-less node from the graph
- for var in indep_vars: var_graph.remove_node(var)
+ # Remove each edge-less node from the graph
+ for var in indep_vars:
+ var_graph.remove_node(var)
return reversed(sorted_vars)
+
def sort_objects(objects, get_id, get_expr):
"""
Sort a list of objects according to their expressions.
@@ -166,12 +183,14 @@ def sort_objects(objects, get_id, get_expr):
a list of sorted objects
"""
id2obj = dict([(get_id(obj), obj) for obj in objects])
- #map obj id to expression code
+ # Map obj id to expression code
id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects])
- #sort according to dependency
+ # Sort according to dependency
sorted_ids = sort_variables(id2expr)
- #return list of sorted objects
+ # 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
+ 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/python/extract_docs.py b/grc/core/utils/extract_docs.py
index d8dc4f4e8f..a6e0bc971e 100644
--- a/grc/python/extract_docs.py
+++ b/grc/core/utils/extract_docs.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008-2011 Free Software Foundation, Inc.
+Copyright 2008-2015 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -32,8 +32,8 @@ import itertools
###############################################################################
def docstring_guess_from_key(key):
- """Extract the documentation from the python __doc__ strings
-
+ """
+ Extract the documentation from the python __doc__ strings
By guessing module and constructor names from key
Args:
@@ -65,12 +65,10 @@ def docstring_guess_from_key(key):
else:
return doc_strings
- pattern = re.compile(
- '^' + init_name.replace('_', '_*').replace('x', r'\w') + r'\w*$'
- )
+ pattern = re.compile('^' + init_name.replace('_', '_*').replace('x', r'\w') + r'\w*$')
for match in filter(pattern.match, dir(module)):
try:
- doc_strings[match] = getattr(module, match).__doc__.strip()
+ doc_strings[match] = getattr(module, match).__doc__
except AttributeError:
continue
@@ -78,8 +76,8 @@ def docstring_guess_from_key(key):
def docstring_from_make(key, imports, make):
- """Extract the documentation from the python __doc__ strings
-
+ """
+ Extract the documentation from the python __doc__ strings
By importing it and checking a truncated make
Args:
@@ -95,12 +93,10 @@ def docstring_from_make(key, imports, make):
blk_cls = make.partition('(')[0].strip()
if '$' in blk_cls:
raise ValueError('Not an identifier')
-
ns = dict()
for _import in imports:
exec(_import.strip(), ns)
blk = eval(blk_cls, ns)
-
doc_strings = {key: blk.__doc__}
except (ImportError, AttributeError, SyntaxError, ValueError):
@@ -114,10 +110,11 @@ def docstring_from_make(key, imports, make):
###############################################################################
class SubprocessLoader(object):
- """Start and manage docstring extraction process
-
+ """
+ Start and manage docstring extraction process
Manages subprocess and handles RPC.
"""
+
BOOTSTRAP = "import runpy; runpy.run_path({!r}, run_name='__worker__')"
AUTH_CODE = random.random() # sort out unwanted output of worker process
RESTART = 5 # number of worker restarts before giving up
@@ -134,7 +131,7 @@ class SubprocessLoader(object):
self._last_cmd = None
def start(self):
- """Start the worker process handler thread"""
+ """ Start the worker process handler thread """
if self._thread is not None:
return
self._shutdown.clear()
@@ -143,7 +140,7 @@ class SubprocessLoader(object):
thread.start()
def run_worker(self):
- """Read docstring back from worker stdout and execute callback."""
+ """ Read docstring back from worker stdout and execute callback. """
for _ in range(self.RESTART):
if self._shutdown.is_set():
break
@@ -173,7 +170,7 @@ class SubprocessLoader(object):
self.callback_finished()
def _handle_worker(self):
- """Send commands and responses back from worker."""
+ """ Send commands and responses back from worker. """
assert '1' == self._worker.stdout.read(1)
for cmd, args in iter(self._queue.get, self.DONE):
self._last_cmd = cmd, args
@@ -182,13 +179,13 @@ class SubprocessLoader(object):
self._handle_response(cmd, args)
def _send(self, cmd, args):
- """send a command to worker's stdin"""
+ """ Send a command to worker's stdin """
fd = self._worker.stdin
json.dump((self.AUTH_CODE, cmd, args), fd)
fd.write('\n'.encode())
def _receive(self):
- """receive response from worker's stdout"""
+ """ Receive response from worker's stdout """
for line in iter(self._worker.stdout.readline, ''):
try:
key, cmd, args = json.loads(line, encoding='utf-8')
@@ -201,7 +198,7 @@ class SubprocessLoader(object):
raise IOError("Can't read worker response")
def _handle_response(self, cmd, args):
- """Handle response from worker, call the callback"""
+ """ Handle response from worker, call the callback """
if cmd == 'result':
key, docs = args
self.callback_query_result(key, docs)
@@ -211,7 +208,7 @@ class SubprocessLoader(object):
print >> sys.stderr, "Unknown response:", cmd, args
def query(self, key, imports=None, make=None):
- """request docstring extraction for a certain key"""
+ """ Request docstring extraction for a certain key """
if self._thread is None:
self.start()
if imports and make:
@@ -220,16 +217,16 @@ class SubprocessLoader(object):
self._queue.put(('query_key_only', (key,)))
def finish(self):
- """signal end of requests"""
+ """ Signal end of requests """
self._queue.put(self.DONE)
def wait(self):
- """Wait for the handler thread to die"""
+ """ Wait for the handler thread to die """
if self._thread:
self._thread.join()
def terminate(self):
- """Terminate the worker and wait"""
+ """ Terminate the worker and wait """
self._shutdown.set()
try:
self._worker.terminate()
@@ -243,8 +240,8 @@ class SubprocessLoader(object):
###############################################################################
def worker_main():
- """Main entry point for the docstring extraction process.
-
+ """
+ Main entry point for the docstring extraction process.
Manages RPC with main process through.
Runs a docstring extraction for each key it read on stdin.
"""
diff --git a/grc/base/odict.py b/grc/core/utils/odict.py
index 70ab67d053..20970e947c 100644
--- a/grc/base/odict.py
+++ b/grc/core/utils/odict.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008-2011 Free Software Foundation, Inc.
+Copyright 2008-2015 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from UserDict import DictMixin
+
class odict(DictMixin):
def __init__(self, d={}):
@@ -57,7 +58,8 @@ class odict(DictMixin):
val: the value for the new entry
"""
index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key)
- if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key))
+ if key in self._keys:
+ raise KeyError('Cannot insert, key "{}" already exists'.format(str(key)))
self._keys.insert(index+1, key)
self._data[key] = val
@@ -72,7 +74,8 @@ class odict(DictMixin):
val: the value for the new entry
"""
index = (pos_key is not None) and self._keys.index(pos_key) or 0
- if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key))
+ if key in self._keys:
+ raise KeyError('Cannot insert, key "{}" already exists'.format(str(key)))
self._keys.insert(index, key)
self._data[key] = val
@@ -86,7 +89,8 @@ class odict(DictMixin):
Returns:
the value or None
"""
- if self.has_key(key): return self[key]
+ if key in self:
+ return self[key]
return None
def findall(self, key):
@@ -100,6 +104,8 @@ class odict(DictMixin):
a list of values or empty list
"""
obj = self.find(key)
- if obj is None: obj = list()
- if isinstance(obj, list): return obj
+ if obj is None:
+ obj = list()
+ if isinstance(obj, list):
+ return obj
return [obj]
diff --git a/grc/cpp/README b/grc/cpp/README
deleted file mode 100644
index 3eccc5dbf7..0000000000
--- a/grc/cpp/README
+++ /dev/null
@@ -1,5 +0,0 @@
-GRC could be used to generate c++ based flowgraphs:
-
-* A few base and gui classes would be overridden.
-* Block info could be extracted from the doxygen xml.
-* New flowgraph templates would be designed.
diff --git a/grc/examples/simple/variable_config.grc b/grc/examples/simple/variable_config.grc
deleted file mode 100644
index 0b60abc813..0000000000
--- a/grc/examples/simple/variable_config.grc
+++ /dev/null
@@ -1,561 +0,0 @@
-<?xml version='1.0' encoding='ASCII'?>
-<flow_graph>
- <timestamp>Sat Jul 12 16:15:51 2014</timestamp>
- <block>
- <key>options</key>
- <param>
- <key>id</key>
- <value>variable_config_demo</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>title</key>
- <value>Variable Config Block Demonstration</value>
- </param>
- <param>
- <key>author</key>
- <value>Example</value>
- </param>
- <param>
- <key>description</key>
- <value>Save/Load freq from a config file.</value>
- </param>
- <param>
- <key>window_size</key>
- <value>1280, 1024</value>
- </param>
- <param>
- <key>generate_options</key>
- <value>qt_gui</value>
- </param>
- <param>
- <key>category</key>
- <value>Custom</value>
- </param>
- <param>
- <key>run_options</key>
- <value>prompt</value>
- </param>
- <param>
- <key>run</key>
- <value>True</value>
- </param>
- <param>
- <key>max_nouts</key>
- <value>0</value>
- </param>
- <param>
- <key>realtime_scheduling</key>
- <value></value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(-1, 2)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable</key>
- <param>
- <key>id</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>32000</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(-1, 125)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>analog_sig_source_x</key>
- <param>
- <key>id</key>
- <value>analog_sig_source_x_0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>complex</value>
- </param>
- <param>
- <key>samp_rate</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>waveform</key>
- <value>analog.GR_COS_WAVE</value>
- </param>
- <param>
- <key>freq</key>
- <value>freq</value>
- </param>
- <param>
- <key>amp</key>
- <value>1</value>
- </param>
- <param>
- <key>offset</key>
- <value>0</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>minoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>maxoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(173, 201)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>blocks_throttle</key>
- <param>
- <key>id</key>
- <value>blocks_throttle_0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>complex</value>
- </param>
- <param>
- <key>samples_per_second</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>vlen</key>
- <value>1</value>
- </param>
- <param>
- <key>ignoretag</key>
- <value>True</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>minoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>maxoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(392, 233)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>qtgui_freq_sink_x</key>
- <param>
- <key>id</key>
- <value>qtgui_freq_sink_x_0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>complex</value>
- </param>
- <param>
- <key>name</key>
- <value>QT GUI Plot</value>
- </param>
- <param>
- <key>fftsize</key>
- <value>1024</value>
- </param>
- <param>
- <key>wintype</key>
- <value>firdes.WIN_BLACKMAN_hARRIS</value>
- </param>
- <param>
- <key>fc</key>
- <value>0</value>
- </param>
- <param>
- <key>bw</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>autoscale</key>
- <value>False</value>
- </param>
- <param>
- <key>average</key>
- <value>1.0</value>
- </param>
- <param>
- <key>ymin</key>
- <value>-140</value>
- </param>
- <param>
- <key>ymax</key>
- <value>10</value>
- </param>
- <param>
- <key>nconnections</key>
- <value>1</value>
- </param>
- <param>
- <key>update_time</key>
- <value>0.10</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value></value>
- </param>
- <param>
- <key>label1</key>
- <value></value>
- </param>
- <param>
- <key>width1</key>
- <value>1</value>
- </param>
- <param>
- <key>color1</key>
- <value>"blue"</value>
- </param>
- <param>
- <key>alpha1</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label2</key>
- <value></value>
- </param>
- <param>
- <key>width2</key>
- <value>1</value>
- </param>
- <param>
- <key>color2</key>
- <value>"red"</value>
- </param>
- <param>
- <key>alpha2</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label3</key>
- <value></value>
- </param>
- <param>
- <key>width3</key>
- <value>1</value>
- </param>
- <param>
- <key>color3</key>
- <value>"green"</value>
- </param>
- <param>
- <key>alpha3</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label4</key>
- <value></value>
- </param>
- <param>
- <key>width4</key>
- <value>1</value>
- </param>
- <param>
- <key>color4</key>
- <value>"black"</value>
- </param>
- <param>
- <key>alpha4</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label5</key>
- <value></value>
- </param>
- <param>
- <key>width5</key>
- <value>1</value>
- </param>
- <param>
- <key>color5</key>
- <value>"cyan"</value>
- </param>
- <param>
- <key>alpha5</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label6</key>
- <value></value>
- </param>
- <param>
- <key>width6</key>
- <value>1</value>
- </param>
- <param>
- <key>color6</key>
- <value>"magenta"</value>
- </param>
- <param>
- <key>alpha6</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label7</key>
- <value></value>
- </param>
- <param>
- <key>width7</key>
- <value>1</value>
- </param>
- <param>
- <key>color7</key>
- <value>"yellow"</value>
- </param>
- <param>
- <key>alpha7</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label8</key>
- <value></value>
- </param>
- <param>
- <key>width8</key>
- <value>1</value>
- </param>
- <param>
- <key>color8</key>
- <value>"dark red"</value>
- </param>
- <param>
- <key>alpha8</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label9</key>
- <value></value>
- </param>
- <param>
- <key>width9</key>
- <value>1</value>
- </param>
- <param>
- <key>color9</key>
- <value>"dark green"</value>
- </param>
- <param>
- <key>alpha9</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label10</key>
- <value></value>
- </param>
- <param>
- <key>width10</key>
- <value>1</value>
- </param>
- <param>
- <key>color10</key>
- <value>"dark blue"</value>
- </param>
- <param>
- <key>alpha10</key>
- <value>1.0</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(643, 221)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable_qtgui_range</key>
- <param>
- <key>id</key>
- <value>freq</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>label</key>
- <value>Frequency (Hz)</value>
- </param>
- <param>
- <key>value</key>
- <value>1e3</value>
- </param>
- <param>
- <key>start</key>
- <value>-samp_rate/2</value>
- </param>
- <param>
- <key>stop</key>
- <value>samp_rate/2</value>
- </param>
- <param>
- <key>step</key>
- <value>1</value>
- </param>
- <param>
- <key>widget</key>
- <value>counter_slider</value>
- </param>
- <param>
- <key>orient</key>
- <value>Qt.Horizontal</value>
- </param>
- <param>
- <key>min_len</key>
- <value>200</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value></value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(339, 9)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable_config</key>
- <param>
- <key>id</key>
- <value>freq_init</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>1000</value>
- </param>
- <param>
- <key>type</key>
- <value>real</value>
- </param>
- <param>
- <key>config_file</key>
- <value>/home/mbant/.gnuradio/config.conf</value>
- </param>
- <param>
- <key>section</key>
- <value>main</value>
- </param>
- <param>
- <key>option</key>
- <value>freq</value>
- </param>
- <param>
- <key>writeback</key>
- <value>freq</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(168, 0)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <connection>
- <source_block_id>analog_sig_source_x_0</source_block_id>
- <sink_block_id>blocks_throttle_0</sink_block_id>
- <source_key>0</source_key>
- <sink_key>0</sink_key>
- </connection>
- <connection>
- <source_block_id>blocks_throttle_0</source_block_id>
- <sink_block_id>qtgui_freq_sink_x_0</sink_block_id>
- <source_key>0</source_key>
- <sink_key>0</sink_key>
- </connection>
-</flow_graph>
diff --git a/grc/examples/xmlrpc/readme.txt b/grc/examples/xmlrpc/readme.txt
deleted file mode 100644
index 056ad1e823..0000000000
--- a/grc/examples/xmlrpc/readme.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-##################################################
-# XMLRPC example
-##################################################
-
-XMLRPC allows software to make remote function calls over http.
-In the case of GRC, one can use XMLRPC to modify variables in a running flow graph.
-See http://www.xmlrpc.com/
-
---- Server Example ---
-Place an "XMLRPC Server" block inside of any flow graph.
-The server will provide set functions for every variable in the flow graph.
-If a variable is called "freq", the server will provide a function set_freq(new_freq).
-Run the server example and experiment with the example client script.
-
--- Client Example --
-The "XMLRPC Client" block will give a variable control over one remove function.
-In the example client, there is one client block and gui control per variable.
-This technique can be used to remotely control a flow graph, perhaps running on a non-gui machine.
diff --git a/grc/examples/xmlrpc/xmlrpc_client.grc b/grc/examples/xmlrpc/xmlrpc_client.grc
deleted file mode 100644
index 45d8af2824..0000000000
--- a/grc/examples/xmlrpc/xmlrpc_client.grc
+++ /dev/null
@@ -1,428 +0,0 @@
-<?xml version='1.0' encoding='ASCII'?>
-<flow_graph>
- <timestamp>Sat Jul 12 17:10:55 2014</timestamp>
- <block>
- <key>options</key>
- <param>
- <key>id</key>
- <value>client_block</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>title</key>
- <value>XMLRPC Client</value>
- </param>
- <param>
- <key>author</key>
- <value>Example</value>
- </param>
- <param>
- <key>description</key>
- <value>example flow graph</value>
- </param>
- <param>
- <key>window_size</key>
- <value>1280, 1024</value>
- </param>
- <param>
- <key>generate_options</key>
- <value>qt_gui</value>
- </param>
- <param>
- <key>category</key>
- <value>Custom</value>
- </param>
- <param>
- <key>run_options</key>
- <value>prompt</value>
- </param>
- <param>
- <key>run</key>
- <value>True</value>
- </param>
- <param>
- <key>max_nouts</key>
- <value>0</value>
- </param>
- <param>
- <key>realtime_scheduling</key>
- <value></value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(-2, 0)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable</key>
- <param>
- <key>id</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>32000</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(13, 172)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>xmlrpc_client</key>
- <param>
- <key>id</key>
- <value>xmlrpc_client</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>addr</key>
- <value>localhost</value>
- </param>
- <param>
- <key>port</key>
- <value>1234</value>
- </param>
- <param>
- <key>callback</key>
- <value>set_freq</value>
- </param>
- <param>
- <key>variable</key>
- <value>freq</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(177, 0)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>xmlrpc_client</key>
- <param>
- <key>id</key>
- <value>xmlrpc_client0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>addr</key>
- <value>localhost</value>
- </param>
- <param>
- <key>port</key>
- <value>1234</value>
- </param>
- <param>
- <key>callback</key>
- <value>set_ampl</value>
- </param>
- <param>
- <key>variable</key>
- <value>ampl</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(308, 0)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>xmlrpc_client</key>
- <param>
- <key>id</key>
- <value>xmlrpc_client1</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>addr</key>
- <value>localhost</value>
- </param>
- <param>
- <key>port</key>
- <value>1234</value>
- </param>
- <param>
- <key>callback</key>
- <value>set_offset</value>
- </param>
- <param>
- <key>variable</key>
- <value>offset*ampl</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(440, 4)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable_qtgui_range</key>
- <param>
- <key>id</key>
- <value>freq</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>label</key>
- <value>Frequency (Hz)</value>
- </param>
- <param>
- <key>value</key>
- <value>1000</value>
- </param>
- <param>
- <key>start</key>
- <value>0</value>
- </param>
- <param>
- <key>stop</key>
- <value>5e3</value>
- </param>
- <param>
- <key>step</key>
- <value>1</value>
- </param>
- <param>
- <key>widget</key>
- <value>counter_slider</value>
- </param>
- <param>
- <key>orient</key>
- <value>Qt.Horizontal</value>
- </param>
- <param>
- <key>min_len</key>
- <value>200</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value>0,0,1,2</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(209, 165)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable_qtgui_range</key>
- <param>
- <key>id</key>
- <value>ampl</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>label</key>
- <value>Amplitude</value>
- </param>
- <param>
- <key>value</key>
- <value>1</value>
- </param>
- <param>
- <key>start</key>
- <value>0</value>
- </param>
- <param>
- <key>stop</key>
- <value>2</value>
- </param>
- <param>
- <key>step</key>
- <value>.1</value>
- </param>
- <param>
- <key>widget</key>
- <value>counter_slider</value>
- </param>
- <param>
- <key>orient</key>
- <value>Qt.Horizontal</value>
- </param>
- <param>
- <key>min_len</key>
- <value>200</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value>1,0,1,2</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(367, 158)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable_qtgui_chooser</key>
- <param>
- <key>id</key>
- <value>offset</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>label</key>
- <value>Offset</value>
- </param>
- <param>
- <key>type</key>
- <value>int</value>
- </param>
- <param>
- <key>num_opts</key>
- <value>3</value>
- </param>
- <param>
- <key>value</key>
- <value>0</value>
- </param>
- <param>
- <key>options</key>
- <value>[0, 1, 2]</value>
- </param>
- <param>
- <key>labels</key>
- <value>[]</value>
- </param>
- <param>
- <key>option0</key>
- <value>-1</value>
- </param>
- <param>
- <key>label0</key>
- <value>neg</value>
- </param>
- <param>
- <key>option1</key>
- <value>0</value>
- </param>
- <param>
- <key>label1</key>
- <value>zero</value>
- </param>
- <param>
- <key>option2</key>
- <value>1</value>
- </param>
- <param>
- <key>label2</key>
- <value>pos</value>
- </param>
- <param>
- <key>option3</key>
- <value>3</value>
- </param>
- <param>
- <key>label3</key>
- <value></value>
- </param>
- <param>
- <key>option4</key>
- <value>4</value>
- </param>
- <param>
- <key>label4</key>
- <value></value>
- </param>
- <param>
- <key>widget</key>
- <value>combo_box</value>
- </param>
- <param>
- <key>orient</key>
- <value>Qt.QVBoxLayout</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value>2,0,1,2</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(531, 145)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
-</flow_graph>
diff --git a/grc/examples/xmlrpc/xmlrpc_client_script.py b/grc/examples/xmlrpc/xmlrpc_client_script.py
deleted file mode 100644
index e96c4cbf83..0000000000
--- a/grc/examples/xmlrpc/xmlrpc_client_script.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-
-import time
-import random
-import xmlrpclib
-
-#create server object
-s = xmlrpclib.Server("http://localhost:1234")
-
-#randomly change parameters of the sinusoid
-for i in range(10):
- #generate random values
- new_freq = random.uniform(0, 5000)
- new_ampl = random.uniform(0, 2)
- new_offset = random.uniform(-1, 1)
- #set new values
- time.sleep(1)
- s.set_freq(new_freq)
- time.sleep(1)
- s.set_ampl(new_ampl)
- time.sleep(1)
- s.set_offset(new_offset)
-
diff --git a/grc/examples/xmlrpc/xmlrpc_server.grc b/grc/examples/xmlrpc/xmlrpc_server.grc
deleted file mode 100644
index d210b2694e..0000000000
--- a/grc/examples/xmlrpc/xmlrpc_server.grc
+++ /dev/null
@@ -1,908 +0,0 @@
-<?xml version='1.0' encoding='ASCII'?>
-<flow_graph>
- <timestamp>Sat Jul 12 17:11:40 2014</timestamp>
- <block>
- <key>options</key>
- <param>
- <key>id</key>
- <value>server_block</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>title</key>
- <value>XMLRPC Server</value>
- </param>
- <param>
- <key>author</key>
- <value>Example</value>
- </param>
- <param>
- <key>description</key>
- <value>example flow graph</value>
- </param>
- <param>
- <key>window_size</key>
- <value>1280, 1024</value>
- </param>
- <param>
- <key>generate_options</key>
- <value>qt_gui</value>
- </param>
- <param>
- <key>category</key>
- <value>Custom</value>
- </param>
- <param>
- <key>run_options</key>
- <value>prompt</value>
- </param>
- <param>
- <key>run</key>
- <value>True</value>
- </param>
- <param>
- <key>max_nouts</key>
- <value>0</value>
- </param>
- <param>
- <key>realtime_scheduling</key>
- <value></value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(0, -1)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable</key>
- <param>
- <key>id</key>
- <value>ampl</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>1</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(4, 291)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable</key>
- <param>
- <key>id</key>
- <value>freq</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>1000</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(2, 213)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable</key>
- <param>
- <key>id</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>32000</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(2, 136)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>variable</key>
- <param>
- <key>id</key>
- <value>offset</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>value</key>
- <value>0</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(3, 366)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>analog_sig_source_x</key>
- <param>
- <key>id</key>
- <value>analog_sig_source_x_0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>float</value>
- </param>
- <param>
- <key>samp_rate</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>waveform</key>
- <value>analog.GR_COS_WAVE</value>
- </param>
- <param>
- <key>freq</key>
- <value>freq</value>
- </param>
- <param>
- <key>amp</key>
- <value>ampl</value>
- </param>
- <param>
- <key>offset</key>
- <value>offset</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>minoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>maxoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(175, 0)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>blocks_throttle</key>
- <param>
- <key>id</key>
- <value>blocks_throttle</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>float</value>
- </param>
- <param>
- <key>samples_per_second</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>vlen</key>
- <value>1</value>
- </param>
- <param>
- <key>ignoretag</key>
- <value>True</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>minoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>maxoutbuf</key>
- <value>0</value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(399, 35)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>xmlrpc_server</key>
- <param>
- <key>id</key>
- <value>xmlrpc_server</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>addr</key>
- <value>localhost</value>
- </param>
- <param>
- <key>port</key>
- <value>1234</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(129, 137)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>qtgui_time_sink_x</key>
- <param>
- <key>id</key>
- <value>qtgui_time_sink_x_0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>float</value>
- </param>
- <param>
- <key>name</key>
- <value>Scope Plot</value>
- </param>
- <param>
- <key>size</key>
- <value>1024</value>
- </param>
- <param>
- <key>srate</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>autoscale</key>
- <value>False</value>
- </param>
- <param>
- <key>ymin</key>
- <value>-1</value>
- </param>
- <param>
- <key>ymax</key>
- <value>1</value>
- </param>
- <param>
- <key>nconnections</key>
- <value>1</value>
- </param>
- <param>
- <key>update_time</key>
- <value>0.10</value>
- </param>
- <param>
- <key>entags</key>
- <value>True</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value>0, 0, 2, 4</value>
- </param>
- <param>
- <key>tr_mode</key>
- <value>qtgui.TRIG_MODE_FREE</value>
- </param>
- <param>
- <key>tr_slope</key>
- <value>qtgui.TRIG_SLOPE_POS</value>
- </param>
- <param>
- <key>tr_level</key>
- <value>0.0</value>
- </param>
- <param>
- <key>tr_delay</key>
- <value>0</value>
- </param>
- <param>
- <key>tr_chan</key>
- <value>0</value>
- </param>
- <param>
- <key>tr_tag</key>
- <value>""</value>
- </param>
- <param>
- <key>label1</key>
- <value></value>
- </param>
- <param>
- <key>width1</key>
- <value>1</value>
- </param>
- <param>
- <key>color1</key>
- <value>"blue"</value>
- </param>
- <param>
- <key>style1</key>
- <value>1</value>
- </param>
- <param>
- <key>marker1</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha1</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label2</key>
- <value></value>
- </param>
- <param>
- <key>width2</key>
- <value>1</value>
- </param>
- <param>
- <key>color2</key>
- <value>"red"</value>
- </param>
- <param>
- <key>style2</key>
- <value>1</value>
- </param>
- <param>
- <key>marker2</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha2</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label3</key>
- <value></value>
- </param>
- <param>
- <key>width3</key>
- <value>1</value>
- </param>
- <param>
- <key>color3</key>
- <value>"green"</value>
- </param>
- <param>
- <key>style3</key>
- <value>1</value>
- </param>
- <param>
- <key>marker3</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha3</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label4</key>
- <value></value>
- </param>
- <param>
- <key>width4</key>
- <value>1</value>
- </param>
- <param>
- <key>color4</key>
- <value>"black"</value>
- </param>
- <param>
- <key>style4</key>
- <value>1</value>
- </param>
- <param>
- <key>marker4</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha4</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label5</key>
- <value></value>
- </param>
- <param>
- <key>width5</key>
- <value>1</value>
- </param>
- <param>
- <key>color5</key>
- <value>"cyan"</value>
- </param>
- <param>
- <key>style5</key>
- <value>1</value>
- </param>
- <param>
- <key>marker5</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha5</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label6</key>
- <value></value>
- </param>
- <param>
- <key>width6</key>
- <value>1</value>
- </param>
- <param>
- <key>color6</key>
- <value>"magenta"</value>
- </param>
- <param>
- <key>style6</key>
- <value>1</value>
- </param>
- <param>
- <key>marker6</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha6</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label7</key>
- <value></value>
- </param>
- <param>
- <key>width7</key>
- <value>1</value>
- </param>
- <param>
- <key>color7</key>
- <value>"yellow"</value>
- </param>
- <param>
- <key>style7</key>
- <value>1</value>
- </param>
- <param>
- <key>marker7</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha7</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label8</key>
- <value></value>
- </param>
- <param>
- <key>width8</key>
- <value>1</value>
- </param>
- <param>
- <key>color8</key>
- <value>"dark red"</value>
- </param>
- <param>
- <key>style8</key>
- <value>1</value>
- </param>
- <param>
- <key>marker8</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha8</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label9</key>
- <value></value>
- </param>
- <param>
- <key>width9</key>
- <value>1</value>
- </param>
- <param>
- <key>color9</key>
- <value>"dark green"</value>
- </param>
- <param>
- <key>style9</key>
- <value>1</value>
- </param>
- <param>
- <key>marker9</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha9</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label10</key>
- <value></value>
- </param>
- <param>
- <key>width10</key>
- <value>1</value>
- </param>
- <param>
- <key>color10</key>
- <value>"blue"</value>
- </param>
- <param>
- <key>style10</key>
- <value>1</value>
- </param>
- <param>
- <key>marker10</key>
- <value>-1</value>
- </param>
- <param>
- <key>alpha10</key>
- <value>1.0</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(644, 13)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <block>
- <key>qtgui_freq_sink_x</key>
- <param>
- <key>id</key>
- <value>qtgui_freq_sink_x_0</value>
- </param>
- <param>
- <key>_enabled</key>
- <value>True</value>
- </param>
- <param>
- <key>type</key>
- <value>float</value>
- </param>
- <param>
- <key>name</key>
- <value>Spectrum Plot</value>
- </param>
- <param>
- <key>fftsize</key>
- <value>1024</value>
- </param>
- <param>
- <key>wintype</key>
- <value>firdes.WIN_BLACKMAN_hARRIS</value>
- </param>
- <param>
- <key>fc</key>
- <value>0</value>
- </param>
- <param>
- <key>bw</key>
- <value>samp_rate</value>
- </param>
- <param>
- <key>autoscale</key>
- <value>False</value>
- </param>
- <param>
- <key>average</key>
- <value>1.0</value>
- </param>
- <param>
- <key>ymin</key>
- <value>-140</value>
- </param>
- <param>
- <key>ymax</key>
- <value>10</value>
- </param>
- <param>
- <key>nconnections</key>
- <value>1</value>
- </param>
- <param>
- <key>update_time</key>
- <value>0.10</value>
- </param>
- <param>
- <key>gui_hint</key>
- <value>2, 0, 2, 4</value>
- </param>
- <param>
- <key>label1</key>
- <value></value>
- </param>
- <param>
- <key>width1</key>
- <value>1</value>
- </param>
- <param>
- <key>color1</key>
- <value>"blue"</value>
- </param>
- <param>
- <key>alpha1</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label2</key>
- <value></value>
- </param>
- <param>
- <key>width2</key>
- <value>1</value>
- </param>
- <param>
- <key>color2</key>
- <value>"red"</value>
- </param>
- <param>
- <key>alpha2</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label3</key>
- <value></value>
- </param>
- <param>
- <key>width3</key>
- <value>1</value>
- </param>
- <param>
- <key>color3</key>
- <value>"green"</value>
- </param>
- <param>
- <key>alpha3</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label4</key>
- <value></value>
- </param>
- <param>
- <key>width4</key>
- <value>1</value>
- </param>
- <param>
- <key>color4</key>
- <value>"black"</value>
- </param>
- <param>
- <key>alpha4</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label5</key>
- <value></value>
- </param>
- <param>
- <key>width5</key>
- <value>1</value>
- </param>
- <param>
- <key>color5</key>
- <value>"cyan"</value>
- </param>
- <param>
- <key>alpha5</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label6</key>
- <value></value>
- </param>
- <param>
- <key>width6</key>
- <value>1</value>
- </param>
- <param>
- <key>color6</key>
- <value>"magenta"</value>
- </param>
- <param>
- <key>alpha6</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label7</key>
- <value></value>
- </param>
- <param>
- <key>width7</key>
- <value>1</value>
- </param>
- <param>
- <key>color7</key>
- <value>"yellow"</value>
- </param>
- <param>
- <key>alpha7</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label8</key>
- <value></value>
- </param>
- <param>
- <key>width8</key>
- <value>1</value>
- </param>
- <param>
- <key>color8</key>
- <value>"dark red"</value>
- </param>
- <param>
- <key>alpha8</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label9</key>
- <value></value>
- </param>
- <param>
- <key>width9</key>
- <value>1</value>
- </param>
- <param>
- <key>color9</key>
- <value>"dark green"</value>
- </param>
- <param>
- <key>alpha9</key>
- <value>1.0</value>
- </param>
- <param>
- <key>label10</key>
- <value></value>
- </param>
- <param>
- <key>width10</key>
- <value>1</value>
- </param>
- <param>
- <key>color10</key>
- <value>"dark blue"</value>
- </param>
- <param>
- <key>alpha10</key>
- <value>1.0</value>
- </param>
- <param>
- <key>alias</key>
- <value></value>
- </param>
- <param>
- <key>affinity</key>
- <value></value>
- </param>
- <param>
- <key>_coordinate</key>
- <value>(644, 126)</value>
- </param>
- <param>
- <key>_rotation</key>
- <value>0</value>
- </param>
- </block>
- <connection>
- <source_block_id>analog_sig_source_x_0</source_block_id>
- <sink_block_id>blocks_throttle</sink_block_id>
- <source_key>0</source_key>
- <sink_key>0</sink_key>
- </connection>
- <connection>
- <source_block_id>blocks_throttle</source_block_id>
- <sink_block_id>qtgui_time_sink_x_0</sink_block_id>
- <source_key>0</source_key>
- <sink_key>0</sink_key>
- </connection>
- <connection>
- <source_block_id>blocks_throttle</source_block_id>
- <sink_block_id>qtgui_freq_sink_x_0</sink_block_id>
- <source_key>0</source_key>
- <sink_key>0</sink_key>
- </connection>
-</flow_graph>
diff --git a/grc/grc_gnuradio/README b/grc/grc_gnuradio/README
deleted file mode 100644
index 897eed65ca..0000000000
--- a/grc/grc_gnuradio/README
+++ /dev/null
@@ -1,11 +0,0 @@
-This is the grc_gnuradio module.
-It contains supplemental python modules that grc uses at runtime.
-The supplemental modules are meant to mimic modules in gnuradio.
-These will be phased-out as new functionaility is merged into gnuradio.
-
-The blk2s module wraps many blocks in blks2 and gives them streaming outputs.
-Will be phased-out by new message passing implementations.
-Other blks2 blocks will hopefully make their way into blks2impl.
-
-The wxgui module contains a top_block + wxgui frame.
-Will be phased-out by gui.py in wxgui and a new top block template.
diff --git a/grc/grc_gnuradio/blks2/error_rate.py b/grc/grc_gnuradio/blks2/error_rate.py
deleted file mode 100644
index 9bf387030a..0000000000
--- a/grc/grc_gnuradio/blks2/error_rate.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# Copyright 2008 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio 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 3, or (at your option)
-# any later version.
-#
-# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-#
-
-default_win_size = 1000
-
-from gnuradio import gr
-from gnuradio import blocks
-import gnuradio.gr.gr_threading as _threading
-import numpy
-
-#generate 1s counts array
-_1s_counts = [sum([1&(i>>j) for j in range(8)]) for i in range(2**8)]
-
-class input_watcher(_threading.Thread):
- """
- Read samples from the message queue and hand them to the callback.
- """
-
- def __init__(self, msgq, callback):
- self._msgq = msgq
- self._callback = callback
- _threading.Thread.__init__(self)
- self.setDaemon(1)
- self.keep_running = True
- self.start()
-
- def run(self):
- r = ''
- while True:
- msg = self._msgq.delete_head()
- itemsize = int(msg.arg1())
- nitems = int(msg.arg2())
- s = r + msg.to_string()
- i = (nitems-nitems%2)*itemsize
- r = s[i:]
- s = s[:i]
- samples = numpy.fromstring(s, numpy.int8)
- self._callback(samples)
-
-class error_rate(gr.hier_block2):
- """
- Sample the incoming data streams (byte) and calculate the bit or symbol error rate.
- Write the running rate to the output data stream (float).
- """
-
- def __init__(self, type='BER', win_size=default_win_size, bits_per_symbol=2):
- """
- Error rate constructor.
-
- Args:
- type: a string 'BER' or 'SER'
- win_size: the number of samples to calculate over
- bits_per_symbol: the number of information bits per symbol (BER only)
- """
- #init
- gr.hier_block2.__init__(
- self, 'error_rate',
- gr.io_signature(2, 2, gr.sizeof_char),
- gr.io_signature(1, 1, gr.sizeof_float),
- )
- assert type in ('BER', 'SER')
- self._max_samples = win_size
- self._bits_per_symbol = bits_per_symbol
- #setup message queue
- msg_source = blocks.message_source(gr.sizeof_float, 1)
- self._msgq_source = msg_source.msgq()
- msgq_sink = gr.msg_queue(2)
- msg_sink = blocks.message_sink(gr.sizeof_char, msgq_sink, False) #False -> blocking
- inter = blocks.interleave(gr.sizeof_char)
- #start thread
- self._num_errs = 0
- self._err_index = 0
- self._num_samps = 0
- self._err_array = numpy.zeros(self._max_samples, numpy.int8)
- if type == 'BER':
- input_watcher(msgq_sink, self._handler_ber)
- elif type == 'SER':
- input_watcher(msgq_sink, self._handler_ser)
- #connect
- self.connect(msg_source, self)
- self.connect((self, 0), (inter, 0))
- self.connect((self, 1), (inter, 1))
- self.connect(inter, msg_sink)
-
- def _handler_ber(self, samples):
- num = len(samples)/2
- arr = numpy.zeros(num, numpy.float32)
- for i in range(num):
- old_err = self._err_array[self._err_index]
- #record error
- self._err_array[self._err_index] = _1s_counts[samples[i*2] ^ samples[i*2 + 1]]
- self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err
- #increment index
- self._err_index = (self._err_index + 1)%self._max_samples
- self._num_samps = min(self._num_samps + 1, self._max_samples)
- #write sample
- arr[i] = float(self._num_errs)/float(self._num_samps*self._bits_per_symbol)
- #write message
- msg = gr.message_from_string(arr.tostring(), 0, gr.sizeof_float, num)
- self._msgq_source.insert_tail(msg)
-
- def _handler_ser(self, samples):
- num = len(samples)/2
- arr = numpy.zeros(num, numpy.float32)
- for i in range(num):
- old_err = self._err_array[self._err_index]
- #record error
- ref = samples[i*2]
- res = samples[i*2 + 1]
- if ref == res:
- self._err_array[self._err_index] = 0
- else:
- self._err_array[self._err_index] = 1
- #update number of errors
- self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err
- #increment index
- self._err_index = (self._err_index + 1)%self._max_samples
- self._num_samps = min(self._num_samps + 1, self._max_samples)
- #write sample
- arr[i] = float(self._num_errs)/float(self._num_samps)
- #write message
- msg = gr.message_from_string(arr.tostring(), 0, gr.sizeof_float, num)
- self._msgq_source.insert_tail(msg)
diff --git a/grc/grc_gnuradio/blks2/packet.py b/grc/grc_gnuradio/blks2/packet.py
deleted file mode 100644
index ef79afde64..0000000000
--- a/grc/grc_gnuradio/blks2/packet.py
+++ /dev/null
@@ -1,257 +0,0 @@
-# Copyright 2008,2009,2012-2013 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio 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 3, or (at your option)
-# any later version.
-#
-# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-#
-
-from gnuradio import gr, digital
-from gnuradio import blocks
-from gnuradio.digital import packet_utils
-import gnuradio.gr.gr_threading as _threading
-
-##payload length in bytes
-DEFAULT_PAYLOAD_LEN = 512
-
-##how many messages in a queue
-DEFAULT_MSGQ_LIMIT = 2
-
-##threshold for unmaking packets
-DEFAULT_THRESHOLD = 12
-
-##################################################
-## Options Class for OFDM
-##################################################
-class options(object):
- def __init__(self, **kwargs):
- for key, value in kwargs.iteritems(): setattr(self, key, value)
-
-##################################################
-## Packet Encoder
-##################################################
-class _packet_encoder_thread(_threading.Thread):
-
- def __init__(self, msgq, payload_length, send):
- self._msgq = msgq
- self._payload_length = payload_length
- self._send = send
- _threading.Thread.__init__(self)
- self.setDaemon(1)
- self.keep_running = True
- self.start()
-
- def run(self):
- sample = '' #residual sample
- while self.keep_running:
- msg = self._msgq.delete_head() #blocking read of message queue
- sample = sample + msg.to_string() #get the body of the msg as a string
- while len(sample) >= self._payload_length:
- payload = sample[:self._payload_length]
- sample = sample[self._payload_length:]
- self._send(payload)
-
-class packet_encoder(gr.hier_block2):
- """
- Hierarchical block for wrapping packet-based modulators.
- """
-
- def __init__(self, samples_per_symbol, bits_per_symbol, preamble='', access_code='', pad_for_usrp=True):
- """
- packet_mod constructor.
-
- Args:
- samples_per_symbol: number of samples per symbol
- bits_per_symbol: number of bits per symbol
- preamble: string of ascii 0's and 1's
- access_code: AKA sync vector
- pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples
- """
- #setup parameters
- self._samples_per_symbol = samples_per_symbol
- self._bits_per_symbol = bits_per_symbol
- self._pad_for_usrp = pad_for_usrp
- if not preamble: #get preamble
- preamble = packet_utils.default_preamble
- if not access_code: #get access code
- access_code = packet_utils.default_access_code
- if not packet_utils.is_1_0_string(preamble):
- raise ValueError, "Invalid preamble %r. Must be string of 1's and 0's" % (preamble,)
- if not packet_utils.is_1_0_string(access_code):
- raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
- self._preamble = preamble
- self._access_code = access_code
- self._pad_for_usrp = pad_for_usrp
- #create blocks
- msg_source = blocks.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT)
- self._msgq_out = msg_source.msgq()
- #initialize hier2
- gr.hier_block2.__init__(
- self,
- "packet_encoder",
- gr.io_signature(0, 0, 0), # Input signature
- gr.io_signature(1, 1, gr.sizeof_char) # Output signature
- )
- #connect
- self.connect(msg_source, self)
-
- def send_pkt(self, payload):
- """
- Wrap the payload in a packet and push onto the message queue.
-
- Args:
- payload: string, data to send
- """
- packet = packet_utils.make_packet(
- payload,
- self._samples_per_symbol,
- self._bits_per_symbol,
- self._preamble,
- self._access_code,
- self._pad_for_usrp
- )
- msg = gr.message_from_string(packet)
- self._msgq_out.insert_tail(msg)
-
-##################################################
-## Packet Decoder
-##################################################
-class _packet_decoder_thread(_threading.Thread):
-
- def __init__(self, msgq, callback):
- _threading.Thread.__init__(self)
- self.setDaemon(1)
- self._msgq = msgq
- self.callback = callback
- self.keep_running = True
- self.start()
-
- def run(self):
- while self.keep_running:
- msg = self._msgq.delete_head()
- ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1()))
- if self.callback:
- self.callback(ok, payload)
-
-class packet_decoder(gr.hier_block2):
- """
- Hierarchical block for wrapping packet-based demodulators.
- """
-
- def __init__(self, access_code='', threshold=-1, callback=None):
- """
- packet_demod constructor.
-
- Args:
- access_code: AKA sync vector
- threshold: detect access_code with up to threshold bits wrong (0 -> use default)
- callback: a function of args: ok, payload
- """
- #access code
- if not access_code: #get access code
- access_code = packet_utils.default_access_code
- if not packet_utils.is_1_0_string(access_code):
- raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
- self._access_code = access_code
- #threshold
- if threshold < 0: threshold = DEFAULT_THRESHOLD
- self._threshold = threshold
- #blocks
- msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY
- correlator = digital.correlate_access_code_bb(self._access_code, self._threshold)
- framer_sink = digital.framer_sink_1(msgq)
- #initialize hier2
- gr.hier_block2.__init__(
- self,
- "packet_decoder",
- gr.io_signature(1, 1, gr.sizeof_char), # Input signature
- gr.io_signature(0, 0, 0) # Output signature
- )
- #connect
- self.connect(self, correlator, framer_sink)
- #start thread
- _packet_decoder_thread(msgq, callback)
-
-##################################################
-## Packet Mod for OFDM Mod and Packet Encoder
-##################################################
-class packet_mod_base(gr.hier_block2):
- """
- Hierarchical block for wrapping packet source block.
- """
-
- def __init__(self, packet_source=None, payload_length=0):
- if not payload_length: #get payload length
- payload_length = DEFAULT_PAYLOAD_LEN
- if payload_length%self._item_size_in != 0: #verify that packet length is a multiple of the stream size
- raise ValueError, 'The payload length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in)
- #initialize hier2
- gr.hier_block2.__init__(
- self,
- "ofdm_mod",
- gr.io_signature(1, 1, self._item_size_in), # Input signature
- gr.io_signature(1, 1, packet_source.output_signature().sizeof_stream_item(0)) # Output signature
- )
- #create blocks
- msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT)
- msg_sink = blocks.message_sink(self._item_size_in, msgq, False) #False -> blocking
- #connect
- self.connect(self, msg_sink)
- self.connect(packet_source, self)
- #start thread
- _packet_encoder_thread(msgq, payload_length, packet_source.send_pkt)
-
-class packet_mod_b(packet_mod_base): _item_size_in = gr.sizeof_char
-class packet_mod_s(packet_mod_base): _item_size_in = gr.sizeof_short
-class packet_mod_i(packet_mod_base): _item_size_in = gr.sizeof_int
-class packet_mod_f(packet_mod_base): _item_size_in = gr.sizeof_float
-class packet_mod_c(packet_mod_base): _item_size_in = gr.sizeof_gr_complex
-
-##################################################
-## Packet Demod for OFDM Demod and Packet Decoder
-##################################################
-class packet_demod_base(gr.hier_block2):
- """
- Hierarchical block for wrapping packet sink block.
- """
-
- def __init__(self, packet_sink=None):
- #initialize hier2
- gr.hier_block2.__init__(
- self,
- "ofdm_mod",
- gr.io_signature(1, 1, packet_sink.input_signature().sizeof_stream_item(0)), # Input signature
- gr.io_signature(1, 1, self._item_size_out) # Output signature
- )
- #create blocks
- msg_source = blocks.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT)
- self._msgq_out = msg_source.msgq()
- #connect
- self.connect(self, packet_sink)
- self.connect(msg_source, self)
- if packet_sink.output_signature().sizeof_stream_item(0):
- self.connect(packet_sink,
- blocks.null_sink(packet_sink.output_signature().sizeof_stream_item(0)))
-
- def recv_pkt(self, ok, payload):
- msg = gr.message_from_string(payload, 0, self._item_size_out,
- len(payload)/self._item_size_out)
- if ok: self._msgq_out.insert_tail(msg)
-
-class packet_demod_b(packet_demod_base): _item_size_out = gr.sizeof_char
-class packet_demod_s(packet_demod_base): _item_size_out = gr.sizeof_short
-class packet_demod_i(packet_demod_base): _item_size_out = gr.sizeof_int
-class packet_demod_f(packet_demod_base): _item_size_out = gr.sizeof_float
-class packet_demod_c(packet_demod_base): _item_size_out = gr.sizeof_gr_complex
diff --git a/grc/grc_gnuradio/blks2/selector.py b/grc/grc_gnuradio/blks2/selector.py
deleted file mode 100644
index 24e3844658..0000000000
--- a/grc/grc_gnuradio/blks2/selector.py
+++ /dev/null
@@ -1,142 +0,0 @@
-#
-# Copyright 2008,2013 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio 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 3, or (at your option)
-# any later version.
-#
-# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-#
-
-from gnuradio import gr
-from gnuradio import blocks
-
-class selector(gr.hier_block2):
- """A hier2 block with N inputs and M outputs, where data is only forwarded through input n to output m."""
- def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index):
- """
- Selector constructor.
-
- Args:
- item_size: the size of the gr data stream in bytes
- num_inputs: the number of inputs (integer)
- num_outputs: the number of outputs (integer)
- input_index: the index for the source data
- output_index: the index for the destination data
- """
- gr.hier_block2.__init__(
- self, 'selector',
- gr.io_signature(num_inputs, num_inputs, item_size),
- gr.io_signature(num_outputs, num_outputs, item_size),
- )
- #terminator blocks for unused inputs and outputs
- self.input_terminators = [blocks.null_sink(item_size) for i in range(num_inputs)]
- self.output_terminators = [blocks.head(item_size, 0) for i in range(num_outputs)]
- self.copy = blocks.copy(item_size)
- #connections
- for i in range(num_inputs): self.connect((self, i), self.input_terminators[i])
- for i in range(num_outputs): self.connect(blocks.null_source(item_size),
- self.output_terminators[i], (self, i))
- self.item_size = item_size
- self.input_index = input_index
- self.output_index = output_index
- self.num_inputs = num_inputs
- self.num_outputs = num_outputs
- self._connect_current()
-
- def _indexes_valid(self):
- """
- Are the input and output indexes within range of the number of inputs and outputs?
-
- Returns:
- true if input index and output index are in range
- """
- return self.input_index in range(self.num_inputs) and self.output_index in range(self.num_outputs)
-
- def _connect_current(self):
- """If the input and output indexes are valid:
- disconnect the blocks at the input and output index from their terminators,
- and connect them to one another. Then connect the terminators to one another."""
- if self._indexes_valid():
- self.disconnect((self, self.input_index), self.input_terminators[self.input_index])
- self.disconnect(self.output_terminators[self.output_index], (self, self.output_index))
- self.connect((self, self.input_index), self.copy)
- self.connect(self.copy, (self, self.output_index))
- self.connect(self.output_terminators[self.output_index], self.input_terminators[self.input_index])
-
- def _disconnect_current(self):
- """If the input and output indexes are valid:
- disconnect the blocks at the input and output index from one another,
- and the terminators at the input and output index from one another.
- Reconnect the blocks to the terminators."""
- if self._indexes_valid():
- self.disconnect((self, self.input_index), self.copy)
- self.disconnect(self.copy, (self, self.output_index))
- self.disconnect(self.output_terminators[self.output_index], self.input_terminators[self.input_index])
- self.connect((self, self.input_index), self.input_terminators[self.input_index])
- self.connect(self.output_terminators[self.output_index], (self, self.output_index))
-
- def set_input_index(self, input_index):
- """
- Change the block to the new input index if the index changed.
-
- Args:
- input_index: the new input index
- """
- if self.input_index != input_index:
- self.lock()
- self._disconnect_current()
- self.input_index = input_index
- self._connect_current()
- self.unlock()
-
- def set_output_index(self, output_index):
- """
- Change the block to the new output index if the index changed.
-
- Args:
- output_index: the new output index
- """
- if self.output_index != output_index:
- self.lock()
- self._disconnect_current()
- self.output_index = output_index
- self._connect_current()
- self.unlock()
-
-class valve(selector):
- """Wrapper for selector with 1 input and 1 output."""
-
- def __init__(self, item_size, open):
- """
- Constructor for valve.
-
- Args:
- item_size: the size of the gr data stream in bytes
- open: true if initial valve state is open
- """
- if open: output_index = -1
- else: output_index = 0
- selector.__init__(self, item_size, 1, 1, 0, output_index)
-
- def set_open(self, open):
- """
- Callback to set open state.
-
- Args:
- open: true to set valve state to open
- """
- if open: output_index = -1
- else: output_index = 0
- self.set_output_index(output_index)
diff --git a/grc/grc_gnuradio/blks2/tcp.py b/grc/grc_gnuradio/blks2/tcp.py
deleted file mode 100644
index aee90fad2c..0000000000
--- a/grc/grc_gnuradio/blks2/tcp.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# Copyright 2009 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio 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 3, or (at your option)
-# any later version.
-#
-# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-#
-
-from gnuradio import gr, blocks
-import socket
-import os
-
-def _get_sock_fd(addr, port, server):
- """
- Get the file descriptor for the socket.
- As a client, block on connect, dup the socket descriptor.
- As a server, block on accept, dup the client descriptor.
-
- Args:
- addr: the ip address string
- port: the tcp port number
- server: true for server mode, false for client mode
-
- Returns:
- the file descriptor number
- """
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- if server:
- sock.bind((addr, port))
- sock.listen(1)
- clientsock, address = sock.accept()
- return os.dup(clientsock.fileno())
- else:
- sock.connect((addr, port))
- return os.dup(sock.fileno())
-
-class tcp_source(gr.hier_block2):
- def __init__(self, itemsize, addr, port, server=True):
- #init hier block
- gr.hier_block2.__init__(
- self, 'tcp_source',
- gr.io_signature(0, 0, 0),
- gr.io_signature(1, 1, itemsize),
- )
- fd = _get_sock_fd(addr, port, server)
- self.connect(blocks.file_descriptor_source(itemsize, fd), self)
-
-class tcp_sink(gr.hier_block2):
- def __init__(self, itemsize, addr, port, server=False):
- #init hier block
- gr.hier_block2.__init__(
- self, 'tcp_sink',
- gr.io_signature(1, 1, itemsize),
- gr.io_signature(0, 0, 0),
- )
- fd = _get_sock_fd(addr, port, server)
- self.connect(self, blocks.file_descriptor_sink(itemsize, fd))
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 7766a0a853..077786d4b4 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -17,26 +17,21 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import os
-import subprocess
-from threading import Thread
-import pygtk
-pygtk.require('2.0')
-import gtk
import gobject
+import gtk
+import os
+import subprocess
-from .. base import ParseXML, Constants
-from .. python.Constants import XTERM_EXECUTABLE
-
-from . import Dialogs, Messages, Preferences, Actions
-from .ParserErrorsDialog import ParserErrorsDialog
-from .MainWindow import MainWindow
-from .PropsDialog import PropsDialog
+from . import Dialogs, Preferences, Actions, Executor, Constants
from .FileDialogs import (OpenFlowGraphFileDialog, SaveFlowGraphFileDialog,
- SaveReportsFileDialog, SaveImageFileDialog,
+ SaveReportsFileDialog, SaveScreenShotDialog,
OpenQSSFileDialog)
-from .Constants import DEFAULT_CANVAS_SIZE, IMAGE_FILE_EXTENSION, GR_PREFIX
+from .MainWindow import MainWindow
+from .ParserErrorsDialog import ParserErrorsDialog
+from .PropsDialog import PropsDialog
+
+from ..core import ParseXML, Messages
gobject.threads_init()
@@ -74,8 +69,6 @@ class ActionHandler:
#initialize
self.init_file_paths = file_paths
Actions.APPLICATION_INITIALIZE()
- #enter the mainloop
- gtk.main()
def _handle_key_press(self, widget, event):
"""
@@ -143,6 +136,7 @@ class ActionHandler:
Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB,
Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY,
Actions.FLOW_GRAPH_OPEN_QSS_THEME,
+ Actions.SELECT_ALL,
):
action.set_sensitive(True)
if hasattr(action, 'load_from_preferences'):
@@ -162,6 +156,8 @@ class ActionHandler:
pass #do nothing, update routines below
elif action == Actions.NOTHING_SELECT:
self.get_flow_graph().unselect()
+ elif action == Actions.SELECT_ALL:
+ self.get_flow_graph().select_all()
##################################################
# Enable/Disable
##################################################
@@ -230,7 +226,7 @@ class ActionHandler:
if y < y_min:
y_min = y
- for connection in block.get_connections():
+ for connection in block.connections:
# Get id of connected blocks
source_id = connection.get_source().get_parent().get_id()
@@ -373,7 +369,8 @@ class ActionHandler:
# Window stuff
##################################################
elif action == Actions.ABOUT_WINDOW_DISPLAY:
- Dialogs.AboutDialog(self.get_flow_graph().get_parent())
+ platform = self.get_flow_graph().get_parent()
+ Dialogs.AboutDialog(platform.config)
elif action == Actions.HELP_WINDOW_DISPLAY:
Dialogs.HelpDialog()
elif action == Actions.TYPES_WINDOW_DISPLAY:
@@ -489,12 +486,13 @@ class ActionHandler:
self.main_window.menu_bar.refresh_submenus()
elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
- file_paths = OpenQSSFileDialog(GR_PREFIX + '/share/gnuradio/themes/').run()
+ file_paths = OpenQSSFileDialog(self.platform.config.install_prefix +
+ '/share/gnuradio/themes/').run()
if file_paths:
try:
- from gnuradio import gr
- gr.prefs().set_string("qtgui", "qss", file_paths[0])
- gr.prefs().save()
+ prefs = self.platform.config.prefs
+ prefs.set_string("qtgui", "qss", file_paths[0])
+ prefs.save()
except Exception as e:
Messages.send("Failed to save QSS preference: " + str(e))
elif action == Actions.FLOW_GRAPH_CLOSE:
@@ -521,10 +519,10 @@ class ActionHandler:
self.main_window.tool_bar.refresh_submenus()
self.main_window.menu_bar.refresh_submenus()
elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
- file_path = SaveImageFileDialog(self.get_page().get_file_path()).run()
+ file_path, background_transparent = SaveScreenShotDialog(self.get_page().get_file_path()).run()
if file_path is not None:
- pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf()
- pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:])
+ pixbuf = self.get_flow_graph().get_drawing_area().get_screenshot(background_transparent)
+ pixbuf.save(file_path, Constants.IMAGE_FILE_EXTENSION[1:])
##################################################
# Gen/Exec/Stop
##################################################
@@ -542,12 +540,13 @@ class ActionHandler:
elif action == Actions.FLOW_GRAPH_EXEC:
if not self.get_page().get_proc():
Actions.FLOW_GRAPH_GEN()
- if Preferences.xterm_missing() != XTERM_EXECUTABLE:
- if not os.path.exists(XTERM_EXECUTABLE):
- Dialogs.MissingXTermDialog(XTERM_EXECUTABLE)
- Preferences.xterm_missing(XTERM_EXECUTABLE)
+ xterm = self.platform.config.xterm_executable
+ if Preferences.xterm_missing() != xterm:
+ if not os.path.exists(xterm):
+ Dialogs.MissingXTermDialog(xterm)
+ Preferences.xterm_missing(xterm)
if self.get_page().get_saved() and self.get_page().get_file_path():
- ExecFlowGraphThread(self)
+ Executor.ExecFlowGraphThread(self)
elif action == Actions.FLOW_GRAPH_KILL:
if self.get_page().get_proc():
try:
@@ -560,7 +559,8 @@ class ActionHandler:
self.platform.load_blocks()
self.main_window.btwin.clear()
self.platform.load_block_tree(self.main_window.btwin)
- Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool(ParseXML.xml_failures))
+ Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool(
+ ParseXML.xml_failures))
Messages.send_xml_errors_if_any(ParseXML.xml_failures)
# Force a redraw of the graph, by getting the current state and re-importing it
self.main_window.update_pages()
@@ -635,7 +635,7 @@ class ActionHandler:
self.main_window.update()
try: #set the size of the flow graph area (if changed)
new_size = (self.get_flow_graph().get_option('window_size') or
- DEFAULT_CANVAS_SIZE)
+ self.platform.config.default_canvas_size)
if self.get_flow_graph().get_size() != tuple(new_size):
self.get_flow_graph().set_size(*new_size)
except: pass
@@ -653,48 +653,3 @@ class ActionHandler:
Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive)
Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive)
Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_proc() is not None)
-
-class ExecFlowGraphThread(Thread):
- """Execute the flow graph as a new process and wait on it to finish."""
-
- def __init__ (self, action_handler):
- """
- ExecFlowGraphThread constructor.
-
- Args:
- action_handler: an instance of an ActionHandler
- """
- Thread.__init__(self)
- self.update_exec_stop = action_handler.update_exec_stop
- self.flow_graph = action_handler.get_flow_graph()
- #store page and dont use main window calls in run
- self.page = action_handler.get_page()
- #get the popen
- try:
- self.p = self.page.get_generator().get_popen()
- self.page.set_proc(self.p)
- #update
- self.update_exec_stop()
- self.start()
- except Exception, e:
- Messages.send_verbose_exec(str(e))
- Messages.send_end_exec()
-
- def run(self):
- """
- Wait on the executing process by reading from its stdout.
- Use gobject.idle_add when calling functions that modify gtk objects.
- """
- #handle completion
- r = "\n"
- while r:
- gobject.idle_add(Messages.send_verbose_exec, r)
- r = os.read(self.p.stdout.fileno(), 1024)
- self.p.poll()
- gobject.idle_add(self.done)
-
- def done(self):
- """Perform end of execution tasks."""
- Messages.send_end_exec(self.p.returncode)
- self.page.set_proc(None)
- self.update_exec_stop()
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index 9b32b3e601..354e536a82 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -226,6 +226,12 @@ FLOW_GRAPH_REDO = Action(
keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK),
)
NOTHING_SELECT = Action()
+SELECT_ALL = Action(
+ label='Select _All',
+ tooltip='Select all blocks and connections in the flow graph',
+ stock_id=gtk.STOCK_SELECT_ALL,
+ keypresses=(gtk.keysyms.a, gtk.gdk.CONTROL_MASK),
+)
ELEMENT_SELECT = Action()
ELEMENT_CREATE = Action()
ELEMENT_DELETE = Action(
@@ -386,7 +392,7 @@ FLOW_GRAPH_KILL = Action(
keypresses=(gtk.keysyms.F7, NO_MODS_MASK),
)
FLOW_GRAPH_SCREEN_CAPTURE = Action(
- label='Sc_reen Capture',
+ label='Screen Ca_pture',
tooltip='Create a screen capture of the flow graph',
stock_id=gtk.STOCK_PRINT,
keypresses=(gtk.keysyms.Print, NO_MODS_MASK),
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index 19f041f508..259aa6ed8b 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -82,6 +82,7 @@ MENU_BAR_LIST = (
Actions.BLOCK_COPY,
Actions.BLOCK_PASTE,
Actions.ELEMENT_DELETE,
+ Actions.SELECT_ALL,
None,
Actions.BLOCK_ROTATE_CCW,
Actions.BLOCK_ROTATE_CW,
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 67b80695fa..95135310b8 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -17,22 +17,24 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Element import Element
-import Utils
-import Colors
-from .. base import odict
-from .. python.Param import num_to_str
-from Constants import BORDER_PROXIMITY_SENSITIVITY
-from Constants import (
- BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION,
- PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT
-)
-import Actions
import pygtk
pygtk.require('2.0')
import gtk
import pango
+from . import Actions, Colors, Utils
+
+from .Constants import (
+ BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION,
+ PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT,
+ BORDER_PROXIMITY_SENSITIVITY
+)
+from . Element import Element
+from ..core.Param import num_to_str
+from ..core.utils import odict
+from ..core.utils.complexity import calculate_flowgraph_complexity
+from ..core.Block import Block as _Block
+
BLOCK_MARKUP_TMPL="""\
#set $foreground = $block.is_valid() and 'black' or 'red'
<span foreground="$foreground" font_desc="$font"><b>$encode($block.get_name())</b></span>"""
@@ -52,15 +54,16 @@ COMMENT_COMPLEXITY_MARKUP_TMPL="""\
"""
-
-class Block(Element):
+class Block(Element, _Block):
"""The graphical signal block."""
- def __init__(self):
+ def __init__(self, flow_graph, n):
"""
Block contructor.
Add graphics related params to the block.
"""
+ _Block.__init__(self, flow_graph, n)
+
self.W = 0
self.H = 0
#add the position param
@@ -135,10 +138,10 @@ class Block(Element):
delta_coor: requested delta coordinate (dX, dY) to move
Returns:
- The delta coordinate possible to move while keeping the block on the canvas
+ The delta coordinate possible to move while keeping the block on the canvas
or the input (dX, dY) on failure
"""
- dX, dY = delta_coor
+ dX, dY = delta_coor
try:
fgW, fgH = self.get_parent().get_size()
@@ -147,7 +150,7 @@ class Block(Element):
sW, sH = self.W, self.H
else:
sW, sH = self.H, self.W
-
+
if x + dX < 0:
dX = -x
elif dX + x + sW >= fgW:
@@ -159,7 +162,7 @@ class Block(Element):
except:
pass
- return ( dX, dY )
+ return ( dX, dY )
def get_rotation(self):
"""
@@ -193,7 +196,7 @@ class Block(Element):
def create_labels(self):
"""Create the labels for the signal block."""
Element.create_labels(self)
- self._bg_color = self.is_dummy_block() and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \
+ self._bg_color = self.is_dummy_block and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \
self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \
self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR
@@ -204,7 +207,7 @@ class Block(Element):
layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=BLOCK_FONT))
self.label_width, self.label_height = layout.get_pixel_size()
#display the params
- if self.is_dummy_block():
+ if self.is_dummy_block:
markups = [
'<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>'.format(font=PARAM_FONT, key=self._key)
]
@@ -271,7 +274,8 @@ class Block(Element):
# Show the flowgraph complexity on the top block if enabled
if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.get_key() == "options":
- complexity = "Complexity: {}bal".format(num_to_str(self.get_parent().get_complexity()))
+ complexity = calculate_flowgraph_complexity(self.get_parent())
+ complexity = "Complexity: {}bal".format(num_to_str(complexity))
layout = gtk.DrawingArea().create_pango_layout('')
layout.set_markup(Utils.parse_template(COMMENT_COMPLEXITY_MARKUP_TMPL,
@@ -311,7 +315,7 @@ class Block(Element):
Element.draw(
self, gc, window, bg_color=self._bg_color,
border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or
- self.is_dummy_block() and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR,
+ self.is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR,
)
#draw label image
if self.is_horizontal():
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index 6b3ebf7807..4279e8c61d 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -30,12 +30,27 @@ KEY_INDEX = 1
DOC_INDEX = 2
DOC_MARKUP_TMPL = """\
-#if $doc
-#if len($doc) > 1000
-#set $doc = $doc[:1000] + '...'
+#set $docs = []
+#if $doc.get('')
+ #set $docs += $doc.pop('').splitlines() + ['']
#end if
-$encode($doc)#slurp
-#else
+#for b, d in $doc.iteritems()
+ #set $docs += ['--- {0} ---'.format(b)] + d.splitlines() + ['']
+#end for
+#set $len_out = 0
+#for $n, $line in $enumerate($docs[:-1])
+#if $n
+
+#end if
+$encode($line)#slurp
+#set $len_out += $len($line)
+#if $n > 10 or $len_out > 500
+
+...#slurp
+#break
+#end if
+#end for
+#if $len_out == 0
undocumented#slurp
#end if"""
@@ -129,8 +144,10 @@ class BlockTreeWindow(gtk.VBox):
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 treestore is None:
+ treestore = self.treestore
+ if categories is None:
+ categories = self._categories
if isinstance(category, (str, unicode)): category = category.split('/')
category = tuple(filter(lambda x: x, category)) # tuple is hashable
@@ -138,17 +155,18 @@ class BlockTreeWindow(gtk.VBox):
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, '[ %s ]'%cat_name)
- treestore.set_value(iter, KEY_INDEX, '')
- treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
- categories[sub_category] = iter
+ iter_ = treestore.insert_before(categories[sub_category[:-1]], None)
+ treestore.set_value(iter_, NAME_INDEX, cat_name)
+ treestore.set_value(iter_, KEY_INDEX, '')
+ treestore.set_value(iter_, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
+ categories[sub_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())
- treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
+ 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())
+ treestore.set_value(iter_, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
def update_docs(self):
"""Update the documentation column of every block"""
@@ -157,7 +175,7 @@ class BlockTreeWindow(gtk.VBox):
if model.iter_has_child(iter_):
return # category node, no doc string
key = model.get_value(iter_, KEY_INDEX)
- block = self.platform.get_block(key)
+ block = self.platform.blocks[key]
doc = Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())
model.set_value(iter_, DOC_INDEX, doc)
@@ -210,8 +228,8 @@ class BlockTreeWindow(gtk.VBox):
self.treeview.set_model(self.treestore)
self.treeview.collapse_all()
else:
- blocks = self.get_flow_graph().get_parent().get_blocks()
- matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(), blocks)
+ matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(),
+ self.platform.blocks.values())
self.treestore_search.clear()
self._categories_search = {tuple(): None}
diff --git a/grc/gui/CMakeLists.txt b/grc/gui/CMakeLists.txt
index 99140df7c4..aa9592b351 100644
--- a/grc/gui/CMakeLists.txt
+++ b/grc/gui/CMakeLists.txt
@@ -17,34 +17,10 @@
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
-########################################################################
-GR_PYTHON_INSTALL(FILES
- external_editor.py
- Block.py
- Colors.py
- Constants.py
- Connection.py
- Element.py
- FlowGraph.py
- Param.py
- Platform.py
- Port.py
- Utils.py
- ActionHandler.py
- Actions.py
- Bars.py
- BlockTreeWindow.py
- Dialogs.py
- DrawingArea.py
- FileDialogs.py
- MainWindow.py
- Messages.py
- NotebookPage.py
- ParserErrorsDialog.py
- PropsDialog.py
- Preferences.py
- StateCache.py
- __init__.py
+file(GLOB py_files "*.py")
+
+GR_PYTHON_INSTALL(
+ FILES ${py_files}
DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/gui
COMPONENT "grc"
)
diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py
index 52c95e8edf..050b363cdd 100644
--- a/grc/gui/Colors.py
+++ b/grc/gui/Colors.py
@@ -33,8 +33,9 @@ try:
PARAM_ENTRY_TEXT_COLOR = get_color('black')
ENTRYENUM_CUSTOM_COLOR = get_color('#EEEEEE')
#flow graph color constants
- FLOWGRAPH_BACKGROUND_COLOR = get_color('#FFF9FF')
+ FLOWGRAPH_BACKGROUND_COLOR = get_color('#FFFFFF')
COMMENT_BACKGROUND_COLOR = get_color('#F3F3F3')
+ FLOWGRAPH_EDGE_COLOR = COMMENT_BACKGROUND_COLOR
#block color constants
BLOCK_ENABLED_COLOR = get_color('#F1ECFF')
BLOCK_DISABLED_COLOR = get_color('#CCCCCC')
diff --git a/grc/gui/Config.py b/grc/gui/Config.py
new file mode 100644
index 0000000000..9b0c5d4afe
--- /dev/null
+++ b/grc/gui/Config.py
@@ -0,0 +1,74 @@
+"""
+Copyright 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
+"""
+
+import sys
+import os
+from ..core.Config import Config as _Config
+from . import Constants
+
+
+class Config(_Config):
+
+ name = 'GNU Radio Companion'
+
+ gui_prefs_file = os.environ.get(
+ 'GRC_PREFS_PATH', os.path.expanduser('~/.gnuradio/grc.conf'))
+
+ def __init__(self, install_prefix, *args, **kwargs):
+ _Config.__init__(self, *args, **kwargs)
+ self.install_prefix = install_prefix
+ Constants.update_font_size(self.font_size)
+
+ @property
+ def editor(self):
+ return self.prefs.get_string('grc', 'editor', '')
+
+ @editor.setter
+ def editor(self, value):
+ self.prefs.get_string('grc', 'editor', value)
+ self.prefs.save()
+
+ @property
+ def xterm_executable(self):
+ return self.prefs.get_string('grc', 'xterm_executable', 'xterm')
+
+ @property
+ def default_canvas_size(self):
+ try: # ugly, but matches current code style
+ raw = self.prefs.get_string('grc', 'canvas_default_size', '1280, 1024')
+ value = tuple(int(x.strip('() ')) for x in raw.split(','))
+ if len(value) != 2 or not all(300 < x < 4096 for x in value):
+ raise Exception()
+ return value
+ except:
+ print >> sys.stderr, "Error: invalid 'canvas_default_size' setting."
+ return Constants.DEFAULT_CANVAS_SIZE_DEFAULT
+
+ @property
+ def font_size(self):
+ try: # ugly, but matches current code style
+ font_size = self.prefs.get_long('grc', 'canvas_font_size',
+ Constants.DEFAULT_FONT_SIZE)
+ if font_size <= 0:
+ raise Exception()
+ except:
+ font_size = Constants.DEFAULT_FONT_SIZE
+ print >> sys.stderr, "Error: invalid 'canvas_font_size' setting."
+
+ return font_size
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index badf8e8a82..50361c19d0 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -17,15 +17,18 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import Utils
-from Element import Element
+import gtk
+
import Colors
+import Utils
from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
-import gtk
+from Element import Element
+
+from ..core.Constants import GR_MESSAGE_DOMAIN
+from ..core.Connection import Connection as _Connection
-from .. base.Constants import GR_MESSAGE_DOMAIN
-class Connection(Element):
+class Connection(Element, _Connection):
"""
A graphical connection for ports.
The connection has 2 parts, the arrow and the wire.
@@ -35,8 +38,9 @@ class Connection(Element):
The arrow coloring exposes the enabled and valid states.
"""
- def __init__(self):
+ def __init__(self, **kwargs):
Element.__init__(self)
+ _Connection.__init__(self, **kwargs)
# can't use Colors.CONNECTION_ENABLED_COLOR here, might not be defined (grcc)
self._bg_color = self._arrow_color = self._color = None
@@ -88,7 +92,7 @@ class Connection(Element):
if not source_domain == sink_domain == GR_MESSAGE_DOMAIN \
else gtk.gdk.LINE_ON_OFF_DASH
get_domain_color = lambda d: Colors.get_color((
- self.get_parent().get_parent().get_domain(d) or {}
+ self.get_parent().get_parent().domains.get(d, {})
).get('color') or Colors.DEFAULT_DOMAIN_COLOR_CODE)
self._color = get_domain_color(source_domain)
self._bg_color = get_domain_color(sink_domain)
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index 741c6fda95..e267c6ca02 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -17,18 +17,10 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import os
-import sys
-
-import pygtk
-pygtk.require('2.0')
import gtk
-from gnuradio import gr
+from ..core.Constants import *
-prefs = gr.prefs()
-GR_PREFIX = gr.prefix()
-EDITOR = prefs.get_string('grc', 'editor', '')
# default path for the open/save dialogs
DEFAULT_FILE_PATH = os.getcwd()
@@ -50,28 +42,12 @@ MIN_DIALOG_HEIGHT = 500
DEFAULT_BLOCKS_WINDOW_WIDTH = 100
DEFAULT_REPORTS_WINDOW_WIDTH = 100
-try: # ugly, but matches current code style
- raw = prefs.get_string('grc', 'canvas_default_size', '1280, 1024')
- DEFAULT_CANVAS_SIZE = tuple(int(x.strip('() ')) for x in raw.split(','))
- if len(DEFAULT_CANVAS_SIZE) != 2 or not all(300 < x < 4096 for x in DEFAULT_CANVAS_SIZE):
- raise Exception()
-except:
- DEFAULT_CANVAS_SIZE = 1280, 1024
- print >> sys.stderr, "Error: invalid 'canvas_default_size' setting."
-
-# flow-graph canvas fonts
-try: # ugly, but matches current code style
- FONT_SIZE = prefs.get_long('grc', 'canvas_font_size', 8)
- if FONT_SIZE <= 0:
- raise Exception()
-except:
- FONT_SIZE = 8
- print >> sys.stderr, "Error: invalid 'canvas_font_size' setting."
-FONT_FAMILY = "Sans"
-BLOCK_FONT = "%s %f" % (FONT_FAMILY, FONT_SIZE)
-PORT_FONT = BLOCK_FONT
-PARAM_FONT = "%s %f" % (FONT_FAMILY, FONT_SIZE - 0.5)
+DEFAULT_CANVAS_SIZE_DEFAULT = 1280, 1024
+FONT_SIZE = DEFAULT_FONT_SIZE = 8
+FONT_FAMILY = "Sans"
+BLOCK_FONT = PORT_FONT = "Sans 8"
+PARAM_FONT = "Sans 7.5"
# size of the state saving cache in the flow graph (undo/redo functionality)
STATE_CACHE_SIZE = 42
@@ -90,8 +66,7 @@ CANVAS_GRID_SIZE = 8
# port constraint dimensions
PORT_BORDER_SEPARATION = 8
PORT_SPACING = 2 * PORT_BORDER_SEPARATION
-PORT_SEPARATION = PORT_SPACING + 2 * PORT_LABEL_PADDING + int(1.5 * FONT_SIZE)
-PORT_SEPARATION += -PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple
+PORT_SEPARATION = 32
PORT_MIN_WIDTH = 20
PORT_LABEL_HIDDEN_WIDTH = 10
@@ -120,3 +95,17 @@ SCROLL_DISTANCE = 15
# How close the mouse click can be to a line and register a connection select.
LINE_SELECT_SENSITIVITY = 5
+
+
+def update_font_size(font_size):
+ global PORT_SEPARATION, BLOCK_FONT, PORT_FONT, PARAM_FONT, FONT_SIZE
+
+ FONT_SIZE = font_size
+ BLOCK_FONT = "%s %f" % (FONT_FAMILY, font_size)
+ PORT_FONT = BLOCK_FONT
+ PARAM_FONT = "%s %f" % (FONT_FAMILY, font_size - 0.5)
+
+ PORT_SEPARATION = PORT_SPACING + 2 * PORT_LABEL_PADDING + int(1.5 * font_size)
+ PORT_SEPARATION += -PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple
+
+update_font_size(DEFAULT_FONT_SIZE)
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index f2941250a8..6cfdd50a34 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -17,15 +17,13 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import pygtk
-pygtk.require('2.0')
import gtk
import sys
from distutils.spawn import find_executable
-
-from . import Utils, Actions, Constants, Messages
+from . import Utils, Actions
+from ..core import Messages
class SimpleTextDisplay(gtk.TextView):
@@ -177,14 +175,14 @@ def ErrorsDialog(flowgraph): MessageDialogHelper(
class AboutDialog(gtk.AboutDialog):
"""A cute little about dialog."""
- def __init__(self, platform):
+ def __init__(self, config):
"""AboutDialog constructor."""
gtk.AboutDialog.__init__(self)
- self.set_name(platform.get_name())
- self.set_version(platform.get_version())
- self.set_license(platform.get_license())
- self.set_copyright(platform.get_license().splitlines()[0])
- self.set_website(platform.get_website())
+ self.set_name(config.name)
+ self.set_version(config.version)
+ self.set_license(config.license)
+ self.set_copyright(config.license.splitlines()[0])
+ self.set_website(config.website)
self.run()
self.destroy()
@@ -240,7 +238,7 @@ def MissingXTermDialog(xterm):
)
-def ChooseEditorDialog():
+def ChooseEditorDialog(config):
# Give the option to either choose an editor or use the default
# Always return true/false so the caller knows it was successful
buttons = (
@@ -266,10 +264,7 @@ def ChooseEditorDialog():
file_dialog.set_current_folder('/usr/bin')
try:
if file_dialog.run() == gtk.RESPONSE_OK:
- file_path = file_dialog.get_filename()
- Constants.prefs.set_string('grc', 'editor', file_path)
- Constants.prefs.save()
- Constants.EDITOR = file_path
+ config.editor = file_path = file_dialog.get_filename()
file_dialog.destroy()
return file_path
finally:
@@ -287,16 +282,12 @@ def ChooseEditorDialog():
if process is None:
raise ValueError("Can't find default editor executable")
# Save
- Constants.prefs.set_string('grc', 'editor', process)
- Constants.prefs.save()
- Constants.EDITOR = process
+ config.editor = process
return process
except Exception:
Messages.send('>>> Unable to load the default editor. Please choose an editor.\n')
# Just reset of the constant and force the user to select an editor the next time
- Constants.prefs.set_string('grc', 'editor', '')
- Constants.prefs.save()
- Constants.EDITOR = ""
+ config.editor = ''
return
Messages.send('>>> No editor selected.\n')
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index 4412129809..6a1df27a8c 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -20,7 +20,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import pygtk
pygtk.require('2.0')
import gtk
+
from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, DND_TARGETS
+import Colors
+
class DrawingArea(gtk.DrawingArea):
"""
@@ -68,13 +71,21 @@ class DrawingArea(gtk.DrawingArea):
self.set_flags(gtk.CAN_FOCUS) # self.set_can_focus(True)
self.connect('focus-out-event', self._handle_focus_lost_event)
- def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1)
- def get_pixbuf(self):
- width, height = self._pixmap.get_size()
- pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height)
- pixbuf.get_from_drawable(self._pixmap, self._pixmap.get_colormap(), 0, 0, 0, 0, width, height)
+ def new_pixmap(self, width, height):
+ return gtk.gdk.Pixmap(self.window, width, height, -1)
+
+ def get_screenshot(self, transparent_bg=False):
+ pixmap = self._pixmap
+ W, H = pixmap.get_size()
+ pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, W, H)
+ pixbuf.fill(0xFF + Colors.FLOWGRAPH_BACKGROUND_COLOR.pixel << 8)
+ pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, W-1, H-1)
+ if transparent_bg:
+ bgc = Colors.FLOWGRAPH_BACKGROUND_COLOR
+ pixbuf = pixbuf.add_alpha(True, bgc.red, bgc.green, bgc.blue)
return pixbuf
+
##########################################################################
## Handlers
##########################################################################
@@ -149,6 +160,12 @@ class DrawingArea(gtk.DrawingArea):
gc = self.window.new_gc()
self._flow_graph.draw(gc, self._pixmap)
self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1)
+ # draw a light grey line on the bottom and right end of the canvas.
+ # this is useful when the theme uses the same panel bg color as the canvas
+ W, H = self._pixmap.get_size()
+ gc.set_foreground(Colors.FLOWGRAPH_EDGE_COLOR)
+ self.window.draw_line(gc, 0, H-1, W, H-1)
+ self.window.draw_line(gc, W-1, 0, W-1, H)
def _handle_focus_lost_event(self, widget, event):
# don't clear selection while context menu is active
diff --git a/grc/gui/Element.py b/grc/gui/Element.py
index 18fb321929..9385424772 100644
--- a/grc/gui/Element.py
+++ b/grc/gui/Element.py
@@ -132,14 +132,14 @@ class Element(object):
"""
self.coor = coor
- def get_parent(self):
- """
- Get the parent of this element.
-
- Returns:
- the parent
- """
- return self.parent
+ # def get_parent(self):
+ # """
+ # Get the parent of this element.
+ #
+ # Returns:
+ # the parent
+ # """
+ # return self.parent
def set_highlighted(self, highlighted):
"""
diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py
new file mode 100644
index 0000000000..f75f514cdb
--- /dev/null
+++ b/grc/gui/Executor.py
@@ -0,0 +1,123 @@
+# Copyright 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
+
+import gobject
+import os
+import threading
+import shlex
+import subprocess
+import sys
+import re
+from distutils.spawn import find_executable
+
+from ..core import Messages
+
+
+class ExecFlowGraphThread(threading.Thread):
+ """Execute the flow graph as a new process and wait on it to finish."""
+
+ def __init__(self, action_handler):
+ """
+ ExecFlowGraphThread constructor.
+
+ Args:
+ action_handler: an instance of an ActionHandler
+ """
+ threading.Thread.__init__(self)
+ self.update_exec_stop = action_handler.update_exec_stop
+ self.flow_graph = action_handler.get_flow_graph()
+ self.xterm_executable = action_handler.platform.config.xterm_executable
+ #store page and dont use main window calls in run
+ self.page = action_handler.get_page()
+ #get the popen
+ try:
+ self.p = self._popen()
+ self.page.set_proc(self.p)
+ #update
+ self.update_exec_stop()
+ self.start()
+ except Exception, e:
+ Messages.send_verbose_exec(str(e))
+ Messages.send_end_exec()
+
+ def _popen(self):
+ """
+ Execute this python flow graph.
+ """
+ run_command = self.flow_graph.get_option('run_command')
+ generator = self.page.get_generator()
+
+ try:
+ run_command = run_command.format(
+ python=shlex_quote(sys.executable),
+ filename=shlex_quote(generator.file_path))
+ run_command_args = shlex.split(run_command)
+ except Exception as e:
+ raise ValueError("Can't parse run command {!r}: {}".format(run_command, e))
+
+ # When in no gui mode on linux, use a graphical terminal (looks nice)
+ xterm_executable = find_executable(self.xterm_executable)
+ if generator.generate_options == 'no_gui' and xterm_executable:
+ run_command_args = [xterm_executable, '-e', run_command]
+
+ # this does not reproduce a shell executable command string, if a graphical
+ # terminal is used. Passing run_command though shlex_quote would do it but
+ # it looks really ugly and confusing in the console panel.
+ Messages.send_start_exec(' '.join(run_command_args))
+
+ return subprocess.Popen(
+ args=run_command_args,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ shell=False, universal_newlines=True
+ )
+
+ def run(self):
+ """
+ Wait on the executing process by reading from its stdout.
+ Use gobject.idle_add when calling functions that modify gtk objects.
+ """
+ #handle completion
+ r = "\n"
+ while r:
+ gobject.idle_add(Messages.send_verbose_exec, r)
+ r = os.read(self.p.stdout.fileno(), 1024)
+ self.p.poll()
+ gobject.idle_add(self.done)
+
+ def done(self):
+ """Perform end of execution tasks."""
+ Messages.send_end_exec(self.p.returncode)
+ self.page.set_proc(None)
+ self.update_exec_stop()
+
+
+###########################################################
+# back-port from python3
+###########################################################
+_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
+
+
+def shlex_quote(s):
+ """Return a shell-escaped version of the string *s*."""
+ if not s:
+ return "''"
+ if _find_unsafe(s) is None:
+ return s
+
+ # use single quotes, and put single quotes into double quotes
+ # the string $'b is then quoted as '$'"'"'b'
+ return "'" + s.replace("'", "'\"'\"'") + "'"
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index 730ac6fba0..4b5770ad21 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -210,3 +210,18 @@ class SaveFlowGraphFileDialog(FileDialog): type = SAVE_FLOW_GRAPH
class OpenQSSFileDialog(FileDialog): type = OPEN_QSS_THEME
class SaveReportsFileDialog(FileDialog): type = SAVE_REPORTS
class SaveImageFileDialog(FileDialog): type = SAVE_IMAGE
+
+
+class SaveScreenShotDialog(SaveImageFileDialog):
+
+ def __init__(self, current_file_path=''):
+ SaveImageFileDialog.__init__(self, current_file_path)
+ self._button = button = gtk.CheckButton('_Background transparent')
+ self._button.set_active(Preferences.screen_shot_background_transparent())
+ self.set_extra_widget(button)
+
+ def run(self):
+ filename = SaveImageFileDialog.run(self)
+ bg_transparent = self._button.get_active()
+ Preferences.screen_shot_background_transparent(bg_transparent)
+ return filename, bg_transparent
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index 2053e86454..357f87c894 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -17,39 +17,43 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import random
import functools
-from itertools import chain
-from operator import methodcaller
+import random
from distutils.spawn import find_executable
+from itertools import chain, count
+from operator import methodcaller
import gobject
-from . import Actions, Colors, Constants, Utils, Messages, Bars, Dialogs
-from . Element import Element
-from . Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
-from . external_editor import ExternalEditor
+from . import Actions, Colors, Constants, Utils, Bars, Dialogs
+from .Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
+from .Element import Element
+from .external_editor import ExternalEditor
+from ..core.FlowGraph import FlowGraph as _Flowgraph
+from ..core import Messages
-class FlowGraph(Element):
+
+class FlowGraph(Element, _Flowgraph):
"""
FlowGraph is the data structure to store graphical signal blocks,
graphical inputs and outputs,
and the connections between inputs and outputs.
"""
- def __init__(self):
+ def __init__(self, **kwargs):
"""
FlowGraph constructor.
Create a list for signal blocks and connections. Connect mouse handlers.
"""
Element.__init__(self)
+ _Flowgraph.__init__(self, **kwargs)
#when is the flow graph selected? (used by keyboard event handler)
self.is_selected = lambda: bool(self.get_selected_elements())
#important vars dealing with mouse event tracking
self.element_moved = False
self.mouse_pressed = False
- self.unselect()
+ self._selected_elements = []
self.press_coor = (0, 0)
#selected ports
self._old_selected_port = None
@@ -62,14 +66,31 @@ class FlowGraph(Element):
self._external_updaters = {}
+ def _get_unique_id(self, base_id=''):
+ """
+ Get a unique id starting with the base id.
+
+ Args:
+ base_id: the id starts with this and appends a count
+
+ Returns:
+ a unique id
+ """
+ for index in count():
+ block_id = '{}_{}'.format(base_id, index)
+ if block_id not in (b.get_id() for b in self.blocks):
+ break
+ return block_id
+
def install_external_editor(self, param):
target = (param.get_parent().get_id(), param.get_key())
if target in self._external_updaters:
editor = self._external_updaters[target]
else:
- editor = (find_executable(Constants.EDITOR) or
- Dialogs.ChooseEditorDialog())
+ config = self.get_parent().config
+ editor = (find_executable(config.editor) or
+ Dialogs.ChooseEditorDialog(config))
if not editor:
return
updater = functools.partial(
@@ -86,9 +107,7 @@ class FlowGraph(Element):
# Problem launching the editor. Need to select a new editor.
Messages.send('>>> Error opening an external editor. Please select a different editor.\n')
# Reset the editor to force the user to select a new one.
- Constants.prefs.set_string('grc', 'editor', '')
- Constants.prefs.save()
- Constants.EDITOR = ""
+ self.get_parent().config.editor = ''
def handle_external_editor_change(self, new_value, target):
try:
@@ -131,7 +150,7 @@ class FlowGraph(Element):
int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()),
)
#get the new block
- block = self.get_new_block(key)
+ block = self.new_block(key)
block.set_coordinate(coor)
block.set_rotation(0)
block.get_param('id').set_value(id)
@@ -160,7 +179,7 @@ class FlowGraph(Element):
#get connections between selected blocks
connections = filter(
lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks,
- self.get_connections(),
+ self.connections,
)
clipboard = (
(x_min, y_min),
@@ -190,7 +209,7 @@ class FlowGraph(Element):
for block_n in blocks_n:
block_key = block_n.find('key')
if block_key == 'options': continue
- block = self.get_new_block(block_key)
+ block = self.new_block(block_key)
if not block:
continue # unknown block was pasted (e.g. dummy block)
selected.add(block)
@@ -206,7 +225,7 @@ class FlowGraph(Element):
if param_key == 'id':
old_id2block[param_value] = block
#if the block id is not unique, get a new block id
- if param_value in [blk.get_id() for blk in self.get_blocks()]:
+ if param_value in (blk.get_id() for blk in self.blocks):
param_value = self._get_unique_id(param_value)
#set value to key
block.get_param(param_key).set_value(param_value)
@@ -350,7 +369,7 @@ class FlowGraph(Element):
# draw comments first
if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active():
- for block in self.iter_blocks():
+ for block in self.blocks:
if block.get_enabled():
block.draw_comment(gc, window)
#draw multi select rectangle
@@ -368,8 +387,8 @@ class FlowGraph(Element):
window.draw_rectangle(gc, False, x, y, w, h)
#draw blocks on top of connections
hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
- blocks = sorted(self.iter_blocks(), key=methodcaller('get_enabled'))
- for element in chain(self.iter_connections(), blocks):
+ blocks = sorted(self.blocks, key=methodcaller('get_enabled'))
+ for element in chain(self.connections, blocks):
if hide_disabled_blocks and not element.get_enabled():
continue # skip hidden disabled blocks and connections
element.draw(gc, window)
@@ -432,6 +451,10 @@ class FlowGraph(Element):
"""
self._selected_elements = []
+ def select_all(self):
+ """Select all blocks in the flow graph"""
+ self._selected_elements = list(self.get_elements())
+
def what_is_selected(self, coor, coor_m=None):
"""
What is selected?
@@ -456,13 +479,13 @@ class FlowGraph(Element):
if not selected_element: continue
# hidden disabled connections, blocks and their ports can not be selected
if Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() and (
- selected_element.is_block() and not selected_element.get_enabled() or
- selected_element.is_connection() and not selected_element.get_enabled() or
- selected_element.is_port() and not selected_element.get_parent().get_enabled()
+ selected_element.is_block and not selected_element.get_enabled() or
+ selected_element.is_connection and not selected_element.get_enabled() or
+ selected_element.is_port and not selected_element.get_parent().get_enabled()
):
continue
#update the selected port information
- if selected_element.is_port():
+ if selected_element.is_port:
if not coor_m: selected_port = selected_element
selected_element = selected_element.get_parent()
selected.add(selected_element)
@@ -486,7 +509,8 @@ class FlowGraph(Element):
"""
selected = set()
for selected_element in self.get_selected_elements():
- if selected_element.is_connection(): selected.add(selected_element)
+ if selected_element.is_connection:
+ selected.add(selected_element)
return list(selected)
def get_selected_blocks(self):
@@ -498,7 +522,8 @@ class FlowGraph(Element):
"""
selected = set()
for selected_element in self.get_selected_elements():
- if selected_element.is_block(): selected.add(selected_element)
+ if selected_element.is_block:
+ selected.add(selected_element)
return list(selected)
def get_selected_block(self):
@@ -673,7 +698,7 @@ class FlowGraph(Element):
adj.set_value(adj_val-SCROLL_DISTANCE)
adj.emit('changed')
#remove the connection if selected in drag event
- if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection():
+ if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection:
Actions.ELEMENT_DELETE()
#move the selected elements and record the new coordinate
if not self.get_ctrl_mask():
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index a340bcc817..6da240d85b 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -17,21 +17,19 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Constants import \
- NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH
-import Actions
-import pygtk
-pygtk.require('2.0')
-import gtk
-import Bars
-from BlockTreeWindow import BlockTreeWindow
-from Dialogs import TextDisplay, MessageDialogHelper
-from NotebookPage import NotebookPage
-import Preferences
-import Messages
-import Utils
import os
+import gtk
+
+from . import Bars, Actions, Preferences, Utils
+from .BlockTreeWindow import BlockTreeWindow
+from .Constants import \
+ NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH
+from .Dialogs import TextDisplay, MessageDialogHelper
+from .NotebookPage import NotebookPage
+
+from ..core import Messages
+
MAIN_WINDOW_TITLE_TMPL = """\
#if not $saved
*#slurp
@@ -71,11 +69,13 @@ class MainWindow(gtk.Window):
Setup the menu, toolbar, flowgraph editor notebook, block selection window...
"""
self._platform = platform
- gen_opts = platform.get_block('options').get_param('generate_options')
+
+ gen_opts = platform.blocks['options'].get_param('generate_options')
generate_mode_default = gen_opts.get_value()
generate_modes = [
(o.get_key(), o.get_name(), o.get_key() == generate_mode_default)
for o in gen_opts.get_options()]
+
# load preferences
Preferences.load(platform)
#setup window
@@ -283,7 +283,7 @@ class MainWindow(gtk.Window):
new_flowgraph_title=NEW_FLOGRAPH_TITLE,
read_only=self.get_page().get_read_only(),
saved=self.get_page().get_saved(),
- platform_name=self._platform.get_name(),
+ platform_name=self._platform.config.name,
)
)
#set tab titles
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index 4c112154af..6614649c89 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -102,10 +102,8 @@ class NotebookPage(gtk.HBox):
Returns:
generator
"""
- return self.get_flow_graph().get_parent().get_generator()(
- self.get_flow_graph(),
- self.get_file_path(),
- )
+ platform = self.get_flow_graph().get_parent()
+ return platform.Generator(self.get_flow_graph(), self.get_file_path())
def _handle_button(self, button):
"""
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index 6884d6530a..bf0a59b96b 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -23,8 +23,10 @@ import pygtk
pygtk.require('2.0')
import gtk
-from . import Colors, Utils, Constants, Dialogs
-from . Element import Element
+from . import Colors, Utils, Constants
+from .Element import Element
+
+from ..core.Param import Param as _Param
class InputParam(gtk.HBox):
@@ -317,7 +319,9 @@ class FileParam(EntryParam):
if self.param.get_key() == 'qt_qss_theme':
dirname = os.path.dirname(dirname) # trim filename
if not os.path.exists(dirname):
- dirname = os.path.join(Constants.GR_PREFIX, '/share/gnuradio/themes')
+ platform = self.param.get_parent().get_parent().get_parent()
+ dirname = os.path.join(platform.config.install_prefix,
+ '/share/gnuradio/themes')
if not os.path.exists(dirname):
dirname = os.getcwd() # fix bad paths
@@ -378,11 +382,12 @@ Error:
#end if"""
-class Param(Element):
+class Param(Element, _Param):
"""The graphical parameter."""
- def __init__(self):
+ def __init__(self, **kwargs):
Element.__init__(self)
+ _Param.__init__(self, **kwargs)
def get_input(self, *args, **kwargs):
"""
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index eda28a0e94..500df1cce4 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -17,12 +17,55 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Element import Element
+import os
+import sys
-class Platform(Element):
- def __init__(self, prefs_file):
+from ..core.Platform import Platform as _Platform
+
+from .Config import Config as _Config
+from .Block import Block as _Block
+from .Connection import Connection as _Connection
+from .Element import Element
+from .FlowGraph import FlowGraph as _FlowGraph
+from .Param import Param as _Param
+from .Port import Port as _Port
+
+
+class Platform(Element, _Platform):
+
+ def __init__(self, *args, **kwargs):
Element.__init__(self)
+ _Platform.__init__(self, *args, **kwargs)
+
+ # Ensure conf directories
+ gui_prefs_file = self.config.gui_prefs_file
+ if not os.path.exists(os.path.dirname(gui_prefs_file)):
+ os.mkdir(os.path.dirname(gui_prefs_file))
+
+ self._move_old_pref_file()
+
+ def get_prefs_file(self):
+ return self.config.gui_prefs_file
- self._prefs_file = prefs_file
+ def _move_old_pref_file(self):
+ gui_prefs_file = self.config.gui_prefs_file
+ old_gui_prefs_file = os.environ.get(
+ 'GRC_PREFS_PATH', os.path.expanduser('~/.grc'))
+ if gui_prefs_file == old_gui_prefs_file:
+ return # prefs file overridden with env var
+ if os.path.exists(old_gui_prefs_file) and not os.path.exists(gui_prefs_file):
+ try:
+ import shutil
+ shutil.move(old_gui_prefs_file, gui_prefs_file)
+ except Exception as e:
+ print >> sys.stderr, e
- def get_prefs_file(self): return self._prefs_file
+ ##############################################
+ # Constructors
+ ##############################################
+ FlowGraph = _FlowGraph
+ Connection = _Connection
+ Block = _Block
+ Port = _Port
+ Param = _Param
+ Config = _Config
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index ae1a1d223f..6314b7ede8 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -17,32 +17,33 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Element import Element
-from Constants import (
- PORT_SEPARATION, PORT_SPACING, CONNECTOR_EXTENSION_MINIMAL,
- CONNECTOR_EXTENSION_INCREMENT, CANVAS_GRID_SIZE,
- PORT_LABEL_PADDING, PORT_MIN_WIDTH, PORT_LABEL_HIDDEN_WIDTH, PORT_FONT
-)
-from .. base.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
-import Utils
-import Actions
-import Colors
import pygtk
pygtk.require('2.0')
import gtk
+from . import Actions, Colors, Utils
+from .Constants import (
+ PORT_SEPARATION, PORT_SPACING, CONNECTOR_EXTENSION_MINIMAL,
+ CONNECTOR_EXTENSION_INCREMENT, PORT_LABEL_PADDING, PORT_MIN_WIDTH, PORT_LABEL_HIDDEN_WIDTH, PORT_FONT
+)
+from .Element import Element
+from ..core.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
+
+from ..core.Port import Port as _Port
+
PORT_MARKUP_TMPL="""\
<span foreground="black" font_desc="$font">$encode($port.get_name())</span>"""
-class Port(Element):
+class Port(_Port, Element):
"""The graphical port."""
- def __init__(self):
+ def __init__(self, block, n, dir):
"""
Port contructor.
Create list of connector coordinates.
"""
+ _Port.__init__(self, block, n, dir)
Element.__init__(self)
self.W = self.H = self.w = self.h = 0
self._connector_coordinate = (0, 0)
@@ -63,7 +64,7 @@ class Port(Element):
rotation = self.get_rotation()
#get all sibling ports
ports = self.get_parent().get_sources_gui() \
- if self.is_source() else self.get_parent().get_sinks_gui()
+ if self.is_source else self.get_parent().get_sinks_gui()
ports = filter(lambda p: not p.get_hide(), ports)
#get the max width
self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH])
@@ -81,27 +82,27 @@ class Port(Element):
index = length-index-1
port_separation = PORT_SEPARATION \
- if not self.get_parent().has_busses[self.is_source()] \
+ if not self.get_parent().has_busses[self.is_source] \
else max([port.H for port in ports]) + PORT_SPACING
offset = (self.get_parent().H - (length-1)*port_separation - self.H)/2
#create areas and connector coordinates
- if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180):
+ if (self.is_sink and rotation == 0) or (self.is_source and rotation == 180):
x = -W
y = port_separation*index+offset
self.add_area((x, y), (W, self.H))
self._connector_coordinate = (x-1, y+self.H/2)
- elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180):
+ elif (self.is_source and rotation == 0) or (self.is_sink and rotation == 180):
x = self.get_parent().W
y = port_separation*index+offset
self.add_area((x, y), (W, self.H))
self._connector_coordinate = (x+1+W, y+self.H/2)
- elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270):
+ elif (self.is_source and rotation == 90) or (self.is_sink and rotation == 270):
y = -W
x = port_separation*index+offset
self.add_area((x, y), (self.H, W))
self._connector_coordinate = (x+self.H/2, y-1)
- elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270):
+ elif (self.is_sink and rotation == 90) or (self.is_source and rotation == 270):
y = self.get_parent().W
x = port_separation*index+offset
self.add_area((x, y), (self.H, W))
@@ -144,7 +145,7 @@ class Port(Element):
Element.draw(
self, gc, window, bg_color=self._bg_color,
border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or
- self.get_parent().is_dummy_block() and Colors.MISSING_BLOCK_BORDER_COLOR or
+ self.get_parent().is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or
Colors.BORDER_COLOR,
)
if not self._areas_list or self._label_hidden():
@@ -176,8 +177,8 @@ class Port(Element):
Returns:
the direction in degrees
"""
- if self.is_source(): return self.get_rotation()
- elif self.is_sink(): return (self.get_rotation() + 180)%360
+ if self.is_source: return self.get_rotation()
+ elif self.is_sink: return (self.get_rotation() + 180)%360
def get_connector_length(self):
"""
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
index 3ebee24345..1a194fd8c5 100644
--- a/grc/gui/Preferences.py
+++ b/grc/gui/Preferences.py
@@ -35,7 +35,7 @@ _config_parser = ConfigParser.SafeConfigParser()
def file_extension():
- return '.'+_platform.get_key()
+ return '.grc'
def load(platform):
@@ -150,3 +150,7 @@ def blocks_window_position(pos=None):
def xterm_missing(cmd=None):
return entry('xterm_missing', cmd, default='INVALID_XTERM_SETTING')
+
+
+def screen_shot_background_transparent(transparent=None):
+ return entry('screen_shot_background_transparent', transparent, default=False)
diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py
index bf7d31d391..7c66a77a54 100644
--- a/grc/gui/PropsDialog.py
+++ b/grc/gui/PropsDialog.py
@@ -97,7 +97,8 @@ class PropsDialog(gtk.Dialog):
self._params_boxes.append((tab, label, vbox))
# Docs for the block
- self._docs_text_display = SimpleTextDisplay()
+ self._docs_text_display = doc_view = SimpleTextDisplay()
+ doc_view.get_buffer().create_tag('b', weight=pango.WEIGHT_BOLD)
self._docs_box = gtk.ScrolledWindow()
self._docs_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self._docs_box.add_with_viewport(self._docs_text_display)
@@ -200,10 +201,43 @@ class PropsDialog(gtk.Dialog):
messages = '\n\n'.join(self._block.get_error_messages())
self._error_messages_text_display.set_text(messages)
# update the docs box
- self._docs_text_display.set_text(self._block.get_doc())
+ self._update_docs_page()
# update the generated code
self._update_generated_code_page()
+ def _update_docs_page(self):
+ """Show documentation from XML and try to display best matching docstring"""
+ buffer = self._docs_text_display.get_buffer()
+ buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
+ pos = buffer.get_end_iter()
+
+ docstrings = self._block.get_doc()
+ if not docstrings:
+ return
+
+ # show documentation string from block xml
+ from_xml = docstrings.pop('', '')
+ for line in from_xml.splitlines():
+ if line.lstrip() == line and line.endswith(':'):
+ buffer.insert_with_tags_by_name(pos, line + '\n', 'b')
+ else:
+ buffer.insert(pos, line + '\n')
+ if from_xml:
+ buffer.insert(pos, '\n')
+
+ # if given the current parameters an exact match can be made
+ block_constructor = self._block.get_make().rsplit('.', 2)[-1]
+ block_class = block_constructor.partition('(')[0].strip()
+ if block_class in docstrings:
+ docstrings = {block_class: docstrings[block_class]}
+
+ # show docstring(s) extracted from python sources
+ for cls_name, docstring in docstrings.iteritems():
+ buffer.insert_with_tags_by_name(pos, cls_name + '\n', 'b')
+ buffer.insert(pos, docstring + '\n\n')
+ pos.backward_chars(2)
+ buffer.delete(pos, buffer.get_end_iter())
+
def _update_generated_code_page(self):
if not self._code_text_display:
return # user disabled code preview
diff --git a/grc/main.py b/grc/main.py
new file mode 100755
index 0000000000..ae7a0ce115
--- /dev/null
+++ b/grc/main.py
@@ -0,0 +1,55 @@
+# Copyright 2009-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
+
+import optparse
+
+import gtk
+from gnuradio import gr
+
+from .gui.Platform import Platform
+from .gui.ActionHandler import ActionHandler
+
+
+VERSION_AND_DISCLAIMER_TEMPLATE = """\
+GNU Radio Companion %s
+
+This program is part of GNU Radio
+GRC comes with ABSOLUTELY NO WARRANTY.
+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()
+
+ try:
+ gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0))
+ except:
+ pass
+
+ platform = Platform(
+ prefs_file=gr.prefs(),
+ version=gr.version(),
+ version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()),
+ install_prefix=gr.prefix()
+ )
+ ActionHandler(args, platform)
+ gtk.main()
+
diff --git a/grc/python/Block.py b/grc/python/Block.py
deleted file mode 100644
index 782893fd8f..0000000000
--- a/grc/python/Block.py
+++ /dev/null
@@ -1,323 +0,0 @@
-"""
-Copyright 2008-2011 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
-"""
-
-import itertools
-import collections
-
-from .. base.Constants import BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI
-from .. base.odict import odict
-
-from .. base.Block import Block as _Block
-from .. gui.Block import Block as _GUIBlock
-
-from . FlowGraph import _variable_matcher
-from . import epy_block_io
-
-
-class Block(_Block, _GUIBlock):
-
- def __init__(self, flow_graph, n):
- """
- Make a new block from nested data.
-
- Args:
- flow: graph the parent element
- n: the nested odict
-
- Returns:
- block a new block
- """
- #grab the data
- self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '')
- self._imports = map(lambda i: i.strip(), n.findall('import'))
- self._make = n.find('make')
- self._var_make = n.find('var_make')
- self._checks = n.findall('check')
- self._callbacks = n.findall('callback')
- self._bus_structure_source = n.find('bus_structure_source') or ''
- self._bus_structure_sink = n.find('bus_structure_sink') or ''
- self.port_counters = [itertools.count(), itertools.count()]
- #build the block
- _Block.__init__(
- self,
- flow_graph=flow_graph,
- n=n,
- )
- _GUIBlock.__init__(self)
-
- self._epy_source_hash = -1 # for epy blocks
- self._epy_reload_error = None
-
- def get_bus_structure(self, direction):
- if direction == 'source':
- bus_structure = self._bus_structure_source;
- else:
- bus_structure = self._bus_structure_sink;
-
- bus_structure = self.resolve_dependencies(bus_structure);
-
- if not bus_structure: return ''
- try:
- clean_bus_structure = self.get_parent().evaluate(bus_structure)
- return clean_bus_structure
-
- except: return ''
-
- def validate(self):
- """
- Validate this block.
- Call the base class validate.
- Evaluate the checks: each check must evaluate to True.
- """
- _Block.validate(self)
- #evaluate the checks
- for check in self._checks:
- check_res = self.resolve_dependencies(check)
- try:
- if not self.get_parent().evaluate(check_res):
- self.add_error_message('Check "%s" failed.'%check)
- except: self.add_error_message('Check "%s" did not evaluate.'%check)
- # for variables check the value (only if var_value is used
- if _variable_matcher.match(self.get_key()) and self._var_value != '$value':
- value = self._var_value
- try:
- value = self.get_var_value()
- self.get_parent().evaluate(value)
- except Exception as err:
- self.add_error_message('Value "%s" cannot be evaluated:\n%s' % (value, err))
-
- # check if this is a GUI block and matches the selected generate option
- current_generate_option = self.get_parent().get_option('generate_options')
-
- def check_generate_mode(label, flag, valid_options):
- block_requires_mode = (
- flag in self.get_flags() or
- self.get_name().upper().startswith(label)
- )
- if block_requires_mode and current_generate_option not in valid_options:
- self.add_error_message("Can't generate this block in mode " +
- repr(current_generate_option))
-
- check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',))
- check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
- if self._epy_reload_error:
- self.get_param('_source_code').add_error_message(str(self._epy_reload_error))
-
- def rewrite(self):
- """
- Add and remove ports to adjust for the nports.
- """
- _Block.rewrite(self)
- # Check and run any custom rewrite function for this block
- getattr(self, 'rewrite_' + self._key, lambda: None)()
-
- # adjust nports, disconnect hidden ports
- for ports in (self.get_sources(), self.get_sinks()):
- for i, master_port in enumerate(ports):
- nports = master_port.get_nports() or 1
- num_ports = 1 + len(master_port.get_clones())
- if master_port.get_hide():
- for connection in master_port.get_connections():
- self.get_parent().remove_element(connection)
- if not nports and num_ports == 1: # not a master port and no left-over clones
- continue
- # remove excess cloned ports
- for port in master_port.get_clones()[nports-1:]:
- # remove excess connections
- for connection in port.get_connections():
- self.get_parent().remove_element(connection)
- master_port.remove_clone(port)
- ports.remove(port)
- # add more cloned ports
- for j in range(num_ports, nports):
- port = master_port.add_clone()
- ports.insert(ports.index(master_port) + j, port)
-
- self.back_ofthe_bus(ports)
- # renumber non-message/-msg ports
- domain_specific_port_index = collections.defaultdict(int)
- for port in filter(lambda p: p.get_key().isdigit(), ports):
- domain = port.get_domain()
- port._key = str(domain_specific_port_index[domain])
- domain_specific_port_index[domain] += 1
-
- def port_controller_modify(self, direction):
- """
- Change the port controller.
-
- Args:
- direction: +1 or -1
-
- Returns:
- true for change
- """
- changed = False
- #concat the nports string from the private nports settings of all ports
- nports_str = ' '.join([port._nports for port in self.get_ports()])
- #modify all params whose keys appear in the nports string
- for param in self.get_params():
- if param.is_enum() or param.get_key() not in nports_str: continue
- #try to increment the port controller by direction
- try:
- value = param.get_evaluated()
- value = value + direction
- if 0 < value:
- param.set_value(value)
- changed = True
- except: pass
- return changed
-
- def get_doc(self):
- platform = self.get_parent().get_parent()
- extracted_docs = platform.block_docstrings.get(self._key, '')
- return (self._doc + '\n\n' + extracted_docs).strip()
-
- def get_category(self):
- return _Block.get_category(self)
-
- def get_imports(self, raw=False):
- """
- Resolve all import statements.
- Split each import statement at newlines.
- Combine all import statments into a list.
- Filter empty imports.
-
- Returns:
- a list of import statements
- """
- if raw:
- return self._imports
- return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), []))
-
- def get_make(self, raw=False):
- if raw:
- return self._make
- return self.resolve_dependencies(self._make)
-
- def get_var_make(self):
- return self.resolve_dependencies(self._var_make)
-
- def get_var_value(self):
- return self.resolve_dependencies(self._var_value)
-
- def get_callbacks(self):
- """
- Get a list of function callbacks for this block.
-
- Returns:
- a list of strings
- """
- def make_callback(callback):
- callback = self.resolve_dependencies(callback)
- if 'self.' in callback: return callback
- return 'self.%s.%s'%(self.get_id(), callback)
- return map(make_callback, self._callbacks)
-
- def is_virtual_sink(self):
- return self.get_key() == 'virtual_sink'
-
- def is_virtual_source(self):
- return self.get_key() == 'virtual_source'
-
- ###########################################################################
- # Custom rewrite functions
- ###########################################################################
-
- def rewrite_epy_block(self):
- flowgraph = self.get_parent()
- platform = flowgraph.get_parent()
- param_blk = self.get_param('_io_cache')
- param_src = self.get_param('_source_code')
- doc_end_tag = 'Block Documentation:'
-
- src = param_src.get_value()
- src_hash = hash((self.get_id(), src))
- if src_hash == self._epy_source_hash:
- return
-
- try:
- blk_io = epy_block_io.extract(src)
-
- except Exception as e:
- self._epy_reload_error = ValueError(str(e))
- try: # load last working block io
- blk_io = epy_block_io.BlockIO(*eval(param_blk.get_value()))
- except:
- return
- else:
- self._epy_reload_error = None # clear previous errors
- param_blk.set_value(repr(tuple(blk_io)))
-
- # print "Rewriting embedded python block {!r}".format(self.get_id())
- self._epy_source_hash = src_hash
- self._name = blk_io.name or blk_io.cls
- self._doc = self._doc.split(doc_end_tag)[0] + doc_end_tag + '\n' + blk_io.doc
- self._imports[0] = 'import ' + self.get_id()
- self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join(
- '{0}=${0}'.format(key) for key, _ in blk_io.params))
-
- params = {}
- for param in list(self._params):
- if hasattr(param, '__epy_param__'):
- params[param.get_key()] = param
- self._params.remove(param)
-
- for key, value in blk_io.params:
- try:
- param = params[key]
- param.set_default(value)
- except KeyError: # need to make a new param
- name = key.replace('_', ' ').title()
- n = odict(dict(name=name, key=key, type='raw', value=value))
- param = platform.Param(block=self, n=n)
- setattr(param, '__epy_param__', True)
- self._params.append(param)
-
- def update_ports(label, ports, port_specs, direction):
- ports_to_remove = list(ports)
- iter_ports = iter(ports)
- ports_new = []
- port_current = next(iter_ports, None)
- for key, port_type in port_specs:
- reuse_port = (
- port_current is not None and
- port_current.get_type() == port_type and
- (key.isdigit() or port_current.get_key() == key)
- )
- if reuse_port:
- ports_to_remove.remove(port_current)
- port, port_current = port_current, next(iter_ports, None)
- else:
- n = odict(dict(name=label + str(key), type=port_type, key=key))
- if port_type == 'message':
- n['name'] = key
- n['optional'] = '1'
- port = platform.Port(block=self, n=n, dir=direction)
- ports_new.append(port)
- # replace old port list with new one
- del ports[:]
- ports.extend(ports_new)
- # remove excess port connections
- for port in ports_to_remove:
- for connection in port.get_connections():
- flowgraph.remove_element(connection)
-
- update_ports('in', self.get_sinks(), blk_io.sinks, 'sink')
- update_ports('out', self.get_sources(), blk_io.sources, 'source')
- _Block.rewrite(self)
diff --git a/grc/python/CMakeLists.txt b/grc/python/CMakeLists.txt
deleted file mode 100644
index 3f9e273146..0000000000
--- a/grc/python/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2011 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio 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 3, or (at your option)
-# any later version.
-#
-# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-
-########################################################################
-GR_PYTHON_INSTALL(FILES
- expr_utils.py
- extract_docs.py
- epy_block_io.py
- Block.py
- Connection.py
- Constants.py
- FlowGraph.py
- Generator.py
- Param.py
- Platform.py
- Port.py
- __init__.py
- DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/python
- COMPONENT "grc"
-)
-
-install(FILES
- block.dtd
- default_flow_graph.grc
- flow_graph.tmpl
- DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/python
- COMPONENT "grc"
-)
diff --git a/grc/python/Connection.py b/grc/python/Connection.py
deleted file mode 100644
index 822876a0ae..0000000000
--- a/grc/python/Connection.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-Copyright 2008-2011 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
-"""
-
-import Constants
-from .. base.Connection import Connection as _Connection
-from .. gui.Connection import Connection as _GUIConnection
-
-class Connection(_Connection, _GUIConnection):
-
- def __init__(self, **kwargs):
- _Connection.__init__(self, **kwargs)
- _GUIConnection.__init__(self)
-
- def is_msg(self):
- return self.get_source().get_type() == self.get_sink().get_type() == 'msg'
-
- def is_bus(self):
- return self.get_source().get_type() == self.get_sink().get_type() == 'bus'
-
- def validate(self):
- """
- Validate the connections.
- The ports must match in io size.
- """
- _Connection.validate(self)
- source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen()
- sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen()
- if source_size != sink_size:
- self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size))
diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py
deleted file mode 100644
index b2a1d27859..0000000000
--- a/grc/python/FlowGraph.py
+++ /dev/null
@@ -1,338 +0,0 @@
-"""
-Copyright 2008-2011 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
-"""
-import re
-import imp
-from operator import methodcaller
-
-from . import expr_utils
-from .. base.FlowGraph import FlowGraph as _FlowGraph
-from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph
-
-_variable_matcher = re.compile('^(variable\w*)$')
-_parameter_matcher = re.compile('^(parameter)$')
-_monitors_searcher = re.compile('(ctrlport_monitor)')
-_bussink_searcher = re.compile('^(bus_sink)$')
-_bussrc_searcher = re.compile('^(bus_source)$')
-_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$')
-_bus_struct_src_searcher = re.compile('^(bus_structure_source)$')
-
-
-class FlowGraph(_FlowGraph, _GUIFlowGraph):
-
- def __init__(self, **kwargs):
- self.grc_file_path = ''
- _FlowGraph.__init__(self, **kwargs)
- _GUIFlowGraph.__init__(self)
- self.n = {}
- self.n_hash = -1
- self._renew_eval_ns = True
- self._eval_cache = {}
-
- def _eval(self, code, namespace, namespace_hash):
- """
- Evaluate the code with the given namespace.
-
- Args:
- code: a string with python code
- namespace: a dict representing the namespace
- namespace_hash: a unique hash for the namespace
-
- Returns:
- the resultant object
- """
- if not code: raise Exception, 'Cannot evaluate empty statement.'
- my_hash = hash(code) ^ namespace_hash
- #cache if does not exist
- if not self._eval_cache.has_key(my_hash):
- self._eval_cache[my_hash] = eval(code, namespace, namespace)
- #return from cache
- return self._eval_cache[my_hash]
-
- def get_hier_block_stream_io(self, direction):
- """
- Get a list of stream io signatures for this flow graph.
-
- Args:
- direction: a string of 'in' or 'out'
-
- Returns:
- a list of dicts with: type, label, vlen, size, optional
- """
- return filter(lambda p: p['type'] != "message",
- self.get_hier_block_io(direction))
-
- def get_hier_block_message_io(self, direction):
- """
- Get a list of message io signatures for this flow graph.
-
- Args:
- direction: a string of 'in' or 'out'
-
- Returns:
- a list of dicts with: type, label, vlen, size, optional
- """
- return filter(lambda p: p['type'] == "message",
- self.get_hier_block_io(direction))
-
- def get_hier_block_io(self, direction):
- """
- Get a list of io ports for this flow graph.
-
- Args:
- direction: a string of 'in' or 'out'
-
- Returns:
- a list of dicts with: type, label, vlen, size, optional
- """
- pads = self.get_pad_sources() if direction in ('sink', 'in') else \
- self.get_pad_sinks() if direction in ('source', 'out') else []
- ports = []
- for pad in pads:
- master = {
- 'label': str(pad.get_param('label').get_evaluated()),
- 'type': str(pad.get_param('type').get_evaluated()),
- 'vlen': str(pad.get_param('vlen').get_value()),
- 'size': pad.get_param('type').get_opt('size'),
- 'optional': bool(pad.get_param('optional').get_evaluated()),
- }
- num_ports = pad.get_param('num_streams').get_evaluated()
- if num_ports > 1:
- for i in xrange(num_ports):
- clone = master.copy()
- clone['label'] += str(i)
- ports.append(clone)
- else:
- ports.append(master)
- return ports
-
- def get_pad_sources(self):
- """
- Get a list of pad source blocks sorted by id order.
-
- Returns:
- a list of pad source blocks in this flow graph
- """
- pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks())
- return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
-
- def get_pad_sinks(self):
- """
- Get a list of pad sink blocks sorted by id order.
-
- Returns:
- a list of pad sink blocks in this flow graph
- """
- pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks())
- return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
-
- def get_pad_port_global_key(self, port):
- """
- Get the key for a port of a pad source/sink to use in connect()
- This takes into account that pad blocks may have multiple ports
-
- Returns:
- the key (str)
- """
- key_offset = 0
- pads = self.get_pad_sources() if port.is_source() else self.get_pad_sinks()
- for pad in pads:
- # using the block param 'type' instead of the port domain here
- # to emphasize that hier block generation is domain agnostic
- is_message_pad = pad.get_param('type').get_evaluated() == "message"
- if port.get_parent() == pad:
- if is_message_pad:
- key = pad.get_param('label').get_value()
- else:
- key = str(key_offset + int(port.get_key()))
- return key
- else:
- # assuming we have either only sources or sinks
- if not is_message_pad:
- key_offset += len(pad.get_ports())
- return -1
-
- def get_imports(self):
- """
- Get a set of all import statments in this flow graph namespace.
-
- Returns:
- a set of import statements
- """
- imports = sum([block.get_imports() for block in self.get_enabled_blocks()], [])
- imports = sorted(set(imports))
- return imports
-
- def get_variables(self):
- """
- Get a list of all variables in this flow graph namespace.
- Exclude paramterized variables.
-
- Returns:
- a sorted list of variable blocks in order of dependency (indep -> dep)
- """
- variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.iter_enabled_blocks())
- return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make'))
-
- def get_parameters(self):
- """
- Get a list of all paramterized variables in this flow graph namespace.
-
- Returns:
- a list of paramterized variables
- """
- parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks())
- return parameters
-
- def get_monitors(self):
- """
- Get a list of all ControlPort monitors
- """
- monitors = filter(lambda b: _monitors_searcher.search(b.get_key()),
- self.iter_enabled_blocks())
- return monitors
-
- def get_python_modules(self):
- """Iterate over custom code block ID and Source"""
- for block in self.iter_enabled_blocks():
- if block.get_key() == 'epy_module':
- yield block.get_id(), block.get_param('source_code').get_value()
-
- def get_bussink(self):
- bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks())
-
- for i in bussink:
- for j in i.get_params():
- if j.get_name() == 'On/Off' and j.get_value() == 'on':
- return True;
-
- return False
-
- def get_bussrc(self):
- bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks())
-
- for i in bussrc:
- for j in i.get_params():
- if j.get_name() == 'On/Off' and j.get_value() == 'on':
- return True;
-
- return False
-
- def get_bus_structure_sink(self):
- bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks())
-
- return bussink
-
- def get_bus_structure_src(self):
- bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks())
-
- return bussrc
-
- def rewrite(self):
- """
- Flag the namespace to be renewed.
- """
- def reconnect_bus_blocks():
- for block in self.get_blocks():
-
- if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()):
-
-
- for i in range(len(block.get_sources_gui())):
- if len(block.get_sources_gui()[i].get_connections()) > 0:
- source = block.get_sources_gui()[i]
- sink = []
-
- for j in range(len(source.get_connections())):
- sink.append(source.get_connections()[j].get_sink());
-
-
- for elt in source.get_connections():
- self.remove_element(elt);
- for j in sink:
- self.connect(source, j);
- self._renew_eval_ns = True
- _FlowGraph.rewrite(self);
- reconnect_bus_blocks();
-
- def evaluate(self, expr):
- """
- Evaluate the expression.
-
- Args:
- expr: the string expression
- @throw Exception bad expression
-
- Returns:
- the evaluated data
- """
- if self._renew_eval_ns:
- self._renew_eval_ns = False
- #reload namespace
- n = dict()
- #load imports
- for code in self.get_imports():
- try: exec code in n
- except: pass
-
- for id, code in self.get_python_modules():
- try:
- module = imp.new_module(id)
- exec code in module.__dict__
- n[id] = module
- except:
- pass
-
- #load parameters
- np = dict()
- for parameter in self.get_parameters():
- try:
- e = eval(parameter.get_param('value').to_code(), n, n)
- np[parameter.get_id()] = e
- except: pass
- n.update(np) #merge param namespace
- #load variables
- for variable in self.get_variables():
- try:
- e = eval(variable.get_var_value(), n, n)
- n[variable.get_id()] = e
- except: pass
- #make namespace public
- self.n = n
- self.n_hash = hash(str(n))
- #evaluate
- e = self._eval(expr, self.n, self.n_hash)
- return e
-
- def get_new_block(self, key):
- """Try to auto-generate the block from file if missing"""
- block = _FlowGraph.get_new_block(self, key)
- if not block:
- platform = self.get_parent()
- # we're before the initial fg rewrite(), so no evaluated values!
- # --> use raw value instead
- path_param = self._options_block.get_param('hier_block_src_path')
- file_path = platform.find_file_in_paths(
- filename=key + '.' + platform.get_key(),
- paths=path_param.get_value(),
- cwd=self.grc_file_path
- )
- if file_path: # grc file found. load and get block
- platform.load_and_generate_flow_graph(file_path)
- block = _FlowGraph.get_new_block(self, key) # can be None
- return block
diff --git a/grc/python/Param.py b/grc/python/Param.py
deleted file mode 100644
index e60f613f00..0000000000
--- a/grc/python/Param.py
+++ /dev/null
@@ -1,433 +0,0 @@
-"""
-Copyright 2008-2011 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
-"""
-
-import ast
-import re
-
-from gnuradio import gr
-
-from .. base.Param import Param as _Param
-from .. gui.Param import Param as _GUIParam
-
-import Constants
-from Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
-
-from gnuradio import eng_notation
-
-_check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
-_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook|epy_module)$')
-
-
-#blacklist certain ids, its not complete, but should help
-import __builtin__
-ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + \
- filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__)
-
-
-def num_to_str(num):
- """ Display logic for numbers """
- if isinstance(num, COMPLEX_TYPES):
- num = complex(num) #cast to python complex
- if num == 0: return '0' #value is zero
- elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real
- elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary
- elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag)))
- else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag))
- else: return str(num)
-
-
-class Param(_Param, _GUIParam):
-
- def __init__(self, **kwargs):
- _Param.__init__(self, **kwargs)
- _GUIParam.__init__(self)
- self._init = False
- self._hostage_cells = list()
-
- def get_types(self): return (
- 'raw', 'enum',
- 'complex', 'real', 'float', 'int',
- 'complex_vector', 'real_vector', 'float_vector', 'int_vector',
- 'hex', 'string', 'bool',
- 'file_open', 'file_save', '_multiline', '_multiline_python_external',
- 'id', 'stream_id',
- 'grid_pos', 'notebook', 'gui_hint',
- 'import',
- )
-
- def __repr__(self):
- """
- Get the repr (nice string format) for this param.
-
- Returns:
- the string representation
- """
- ##################################################
- # truncate helper method
- ##################################################
- def _truncate(string, style=0):
- max_len = max(27 - len(self.get_name()), 3)
- if len(string) > max_len:
- if style < 0: #front truncate
- string = '...' + string[3-max_len:]
- elif style == 0: #center truncate
- string = string[:max_len/2 -3] + '...' + string[-max_len/2:]
- elif style > 0: #rear truncate
- string = string[:max_len-3] + '...'
- return string
- ##################################################
- # simple conditions
- ##################################################
- if not self.is_valid(): return _truncate(self.get_value())
- if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name()
-
- ##################################################
- # split up formatting by type
- ##################################################
- truncate = 0 #default center truncate
- e = self.get_evaluated()
- t = self.get_type()
- if isinstance(e, bool): return str(e)
- elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e)
- elif isinstance(e, VECTOR_TYPES): #vector types
- if len(e) > 8:
- dt_str = self.get_value() #large vectors use code
- truncate = 1
- else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval
- elif t in ('file_open', 'file_save'):
- dt_str = self.get_value()
- truncate = -1
- else: dt_str = str(e) #other types
- ##################################################
- # done
- ##################################################
- return _truncate(dt_str, truncate)
-
- def get_color(self):
- """
- Get the color that represents this param's type.
-
- Returns:
- a hex color code.
- """
- try:
- return {
- #number types
- 'complex': Constants.COMPLEX_COLOR_SPEC,
- 'real': Constants.FLOAT_COLOR_SPEC,
- 'float': Constants.FLOAT_COLOR_SPEC,
- 'int': Constants.INT_COLOR_SPEC,
- #vector types
- 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC,
- 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
- 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
- 'int_vector': Constants.INT_VECTOR_COLOR_SPEC,
- #special
- 'bool': Constants.INT_COLOR_SPEC,
- 'hex': Constants.INT_COLOR_SPEC,
- 'string': Constants.BYTE_VECTOR_COLOR_SPEC,
- 'id': Constants.ID_COLOR_SPEC,
- 'stream_id': Constants.ID_COLOR_SPEC,
- 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
- 'notebook': Constants.INT_VECTOR_COLOR_SPEC,
- 'raw': Constants.WILDCARD_COLOR_SPEC,
- }[self.get_type()]
- except: return _Param.get_color(self)
-
- def get_hide(self):
- """
- Get the hide value from the base class.
- Hide the ID parameter for most blocks. Exceptions below.
- If the parameter controls a port type, vlen, or nports, return part.
- If the parameter is an empty grid position, return part.
- These parameters are redundant to display in the flow graph view.
-
- Returns:
- hide the hide property string
- """
- hide = _Param.get_hide(self)
- if hide: return hide
- #hide ID in non variable blocks
- if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part'
- #hide port controllers for type and nports
- if self.get_key() in ' '.join(map(
- lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports())
- ): return 'part'
- #hide port controllers for vlen, when == 1
- if self.get_key() in ' '.join(map(
- lambda p: p._vlen, self.get_parent().get_ports())
- ):
- try:
- if int(self.get_evaluated()) == 1: return 'part'
- except: pass
- #hide empty grid positions
- if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part'
- return hide
-
- def validate(self):
- """
- Validate the param.
- A test evaluation is performed
- """
- _Param.validate(self) #checks type
- self._evaluated = None
- try: self._evaluated = self.evaluate()
- except Exception, e: self.add_error_message(str(e))
-
- def get_evaluated(self): return self._evaluated
-
- def evaluate(self):
- """
- Evaluate the value.
-
- Returns:
- evaluated type
- """
- self._init = True
- self._lisitify_flag = False
- self._stringify_flag = False
- self._hostage_cells = list()
- t = self.get_type()
- v = self.get_value()
- #########################
- # Enum Type
- #########################
- if self.is_enum(): return v
- #########################
- # Numeric Types
- #########################
- elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'):
- #raise exception if python cannot evaluate this value
- try: e = self.get_parent().get_parent().evaluate(v)
- except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e)
- #raise an exception if the data is invalid
- if t == 'raw': return e
- elif t == 'complex':
- if not isinstance(e, COMPLEX_TYPES):
- raise Exception, 'Expression "%s" is invalid for type complex.'%str(e)
- return e
- elif t == 'real' or t == 'float':
- if not isinstance(e, REAL_TYPES):
- raise Exception, 'Expression "%s" is invalid for type float.'%str(e)
- return e
- elif t == 'int':
- if not isinstance(e, INT_TYPES):
- raise Exception, 'Expression "%s" is invalid for type integer.'%str(e)
- return e
- elif t == 'hex': return hex(e)
- elif t == 'bool':
- if not isinstance(e, bool):
- raise Exception, 'Expression "%s" is invalid for type bool.'%str(e)
- return e
- else: raise TypeError, 'Type "%s" not handled'%t
- #########################
- # Numeric Vector Types
- #########################
- elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
- if not v: v = '()' #turn a blank string into an empty list, so it will eval
- #raise exception if python cannot evaluate this value
- try: e = self.get_parent().get_parent().evaluate(v)
- except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e)
- #raise an exception if the data is invalid
- if t == 'complex_vector':
- if not isinstance(e, VECTOR_TYPES):
- self._lisitify_flag = True
- e = [e]
- if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]):
- raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e)
- return e
- elif t == 'real_vector' or t == 'float_vector':
- if not isinstance(e, VECTOR_TYPES):
- self._lisitify_flag = True
- e = [e]
- if not all([isinstance(ei, REAL_TYPES) for ei in e]):
- raise Exception, 'Expression "%s" is invalid for type float vector.'%str(e)
- return e
- elif t == 'int_vector':
- if not isinstance(e, VECTOR_TYPES):
- self._lisitify_flag = True
- e = [e]
- if not all([isinstance(ei, INT_TYPES) for ei in e]):
- raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e)
- return e
- #########################
- # String Types
- #########################
- elif t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
- #do not check if file/directory exists, that is a runtime issue
- try:
- e = self.get_parent().get_parent().evaluate(v)
- if not isinstance(e, str):
- raise Exception()
- except:
- self._stringify_flag = True
- e = str(v)
- if t == '_multiline_python_external':
- ast.parse(e) # raises SyntaxError
- return e
- #########################
- # Unique ID Type
- #########################
- elif t == 'id':
- #can python use this as a variable?
- if not _check_id_matcher.match(v):
- raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v
- ids = [param.get_value() for param in self.get_all_params(t)]
- if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled
- raise Exception, 'ID "%s" is not unique.'%v
- if v in ID_BLACKLIST:
- raise Exception, 'ID "%s" is blacklisted.'%v
- return v
- #########################
- # Stream ID Type
- #########################
- elif t == 'stream_id':
- #get a list of all stream ids used in the virtual sinks
- ids = [param.get_value() for param in filter(
- lambda p: p.get_parent().is_virtual_sink(),
- self.get_all_params(t),
- )]
- #check that the virtual sink's stream id is unique
- if self.get_parent().is_virtual_sink():
- if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled
- raise Exception, 'Stream ID "%s" is not unique.'%v
- #check that the virtual source's steam id is found
- if self.get_parent().is_virtual_source():
- if v not in ids:
- raise Exception, 'Stream ID "%s" is not found.'%v
- return v
- #########################
- # 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 = '?'
-
- 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 and 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
- return GuiHint(widget_str)
- #########################
- # Grid Position Type
- #########################
- elif t == 'grid_pos':
- if not v: return '' #allow for empty grid pos
- e = self.get_parent().get_parent().evaluate(v)
- if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]):
- raise Exception, 'A grid position must be a list of 4 integers.'
- row, col, row_span, col_span = e
- #check row, col
- if row < 0 or col < 0:
- raise Exception, 'Row and column must be non-negative.'
- #check row span, col span
- if row_span <= 0 or col_span <= 0:
- raise Exception, 'Row and column span must be greater than zero.'
- #get hostage cell parent
- try: my_parent = self.get_parent().get_param('notebook').evaluate()
- except: my_parent = ''
- #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)))
- #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:
- raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell))
- return e
- #########################
- # Notebook Page Type
- #########################
- elif t == 'notebook':
- if not v: return '' #allow for empty notebook
- #get a list of all notebooks
- notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks())
- #check for notebook param syntax
- try: notebook_id, page_index = map(str.strip, v.split(','))
- except: raise Exception, 'Bad notebook page format.'
- #check that the notebook id is valid
- try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0]
- except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id
- #check that page index exists
- if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())):
- raise Exception, 'Page index "%s" is not a valid index number.'%page_index
- return notebook_id, page_index
- #########################
- # Import Type
- #########################
- elif t == 'import':
- n = dict() #new namespace
- try: exec v in n
- except ImportError: raise Exception, 'Import "%s" failed.'%v
- except Exception: raise Exception, 'Bad import syntax: "%s".'%v
- return filter(lambda k: str(k) != '__builtins__', n.keys())
- #########################
- else: raise TypeError, 'Type "%s" not handled'%t
-
- def to_code(self):
- """
- Convert the value to code.
- For string and list types, check the init flag, call evaluate().
- This ensures that evaluate() was called to set the xxxify_flags.
-
- Returns:
- a string representing the code
- """
- v = self.get_value()
- t = self.get_type()
- if t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'): # string types
- if not self._init: self.evaluate()
- if self._stringify_flag: return '"%s"'%v.replace('"', '\"')
- else: return v
- elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): #vector types
- if not self._init: self.evaluate()
- if self._lisitify_flag: return '(%s, )'%v
- else: return '(%s)'%v
- else: return v
-
- def get_all_params(self, type):
- """
- Get all the params from the flowgraph that have the given type.
-
- Args:
- type: the specified type
-
- Returns:
- a list of params
- """
- return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])
diff --git a/grc/python/Platform.py b/grc/python/Platform.py
deleted file mode 100644
index 2a0bbf9a9e..0000000000
--- a/grc/python/Platform.py
+++ /dev/null
@@ -1,174 +0,0 @@
-"""
-Copyright 2008-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
-"""
-
-import os
-import sys
-
-from gnuradio import gr
-
-from .. base.Platform import Platform as _Platform
-from .. gui.Platform import Platform as _GUIPlatform
-from .. gui import Messages
-
-from . import extract_docs
-from .FlowGraph import FlowGraph as _FlowGraph
-from .Connection import Connection as _Connection
-from .Block import Block as _Block
-from .Port import Port as _Port
-from .Param import Param as _Param
-from .Generator import Generator
-from .Constants import (
- HIER_BLOCKS_LIB_DIR, BLOCK_DTD, DEFAULT_FLOW_GRAPH, BLOCKS_DIRS,
- PREFS_FILE, PREFS_FILE_OLD, CORE_TYPES
-)
-
-COLORS = [(name, color) for name, key, sizeof, color in CORE_TYPES]
-
-
-class Platform(_Platform, _GUIPlatform):
- def __init__(self):
- """
- Make a platform for gnuradio.
- """
- # ensure hier and conf directories
- if not os.path.exists(HIER_BLOCKS_LIB_DIR):
- os.mkdir(HIER_BLOCKS_LIB_DIR)
- if not os.path.exists(os.path.dirname(PREFS_FILE)):
- os.mkdir(os.path.dirname(PREFS_FILE))
-
- self.block_docstrings = block_docstrings = dict()
- self.block_docstrings_loaded_callback = lambda: None
-
- def setter(key, docs):
- block_docstrings[key] = '\n\n'.join(
- '--- {0} ---\n{1}\n'.format(b, d.replace('\n\n', '\n'))
- for b, d in docs.iteritems() if d is not None
- )
-
- self._docstring_extractor = extract_docs.SubprocessLoader(
- callback_query_result=setter,
- callback_finished=lambda: self.block_docstrings_loaded_callback()
- )
-
- # init
- _Platform.__init__(
- self,
- name='GNU Radio Companion',
- version=(gr.version(), gr.major_version(), gr.api_version(), gr.minor_version()),
- key='grc',
- license=__doc__.strip(),
- website='http://gnuradio.org/',
- block_paths=BLOCKS_DIRS,
- block_dtd=BLOCK_DTD,
- default_flow_graph=DEFAULT_FLOW_GRAPH,
- generator=Generator,
- colors=COLORS,
- )
- self._move_old_pref_file()
- _GUIPlatform.__init__(
- self,
- prefs_file=PREFS_FILE
- )
- self._auto_hier_block_generate_chain = set()
-
- @staticmethod
- def _move_old_pref_file():
- if PREFS_FILE == PREFS_FILE_OLD:
- return # prefs file overridden with env var
- if os.path.exists(PREFS_FILE_OLD) and not os.path.exists(PREFS_FILE):
- try:
- import shutil
- shutil.move(PREFS_FILE_OLD, PREFS_FILE)
- except Exception as e:
- print >> sys.stderr, e
-
- def load_blocks(self):
- self._docstring_extractor.start()
- _Platform.load_blocks(self)
- self._docstring_extractor.finish()
- # self._docstring_extractor.wait()
-
- def load_block_xml(self, xml_file):
- block = _Platform.load_block_xml(self, xml_file)
- self._docstring_extractor.query(
- block.get_key(),
- block.get_imports(raw=True),
- block.get_make(raw=True)
- )
- return block
-
- @staticmethod
- def find_file_in_paths(filename, paths, cwd):
- """Checks the provided paths relative to cwd for a certain filename"""
- if not os.path.isdir(cwd):
- cwd = os.path.dirname(cwd)
- if isinstance(paths, str):
- paths = (p for p in paths.split(':') if p)
-
- for path in paths:
- path = os.path.expanduser(path)
- if not os.path.isabs(path):
- path = os.path.normpath(os.path.join(cwd, path))
- file_path = os.path.join(path, filename)
- if os.path.exists(os.path.normpath(file_path)):
- return file_path
-
- def load_and_generate_flow_graph(self, file_path):
- """Loads a flowgraph from file and generates it"""
- Messages.set_indent(len(self._auto_hier_block_generate_chain))
- Messages.send('>>> Loading: %r\n' % file_path)
- if file_path in self._auto_hier_block_generate_chain:
- Messages.send(' >>> Warning: cyclic hier_block dependency\n')
- return False
- self._auto_hier_block_generate_chain.add(file_path)
- try:
- flow_graph = self.get_new_flow_graph()
- flow_graph.grc_file_path = file_path
- # other, nested higiter_blocks might be auto-loaded here
- flow_graph.import_data(self.parse_flow_graph(file_path))
- flow_graph.rewrite()
- flow_graph.validate()
- if not flow_graph.is_valid():
- raise Exception('Flowgraph invalid')
- except Exception as e:
- Messages.send('>>> Load Error: %r: %s\n' % (file_path, str(e)))
- return False
- finally:
- self._auto_hier_block_generate_chain.discard(file_path)
- Messages.set_indent(len(self._auto_hier_block_generate_chain))
-
- try:
- Messages.send('>>> Generating: %r\n' % file_path)
- generator = self.get_generator()(flow_graph, file_path)
- generator.write()
- except Exception as e:
- Messages.send('>>> Generate Error: %r: %s\n' % (file_path, str(e)))
- return False
-
- self.load_block_xml(generator.get_file_path_xml())
- return True
-
- ##############################################
- # Constructors
- ##############################################
- FlowGraph = _FlowGraph
- Connection = _Connection
- Block = _Block
- Port = _Port
- Param = _Param
diff --git a/grc/python/Port.py b/grc/python/Port.py
deleted file mode 100644
index 249d7aed71..0000000000
--- a/grc/python/Port.py
+++ /dev/null
@@ -1,268 +0,0 @@
-"""
-Copyright 2008-2012 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 .. base.Port import Port as _Port
-from .. base.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
-from .. gui.Port import Port as _GUIPort
-import Constants
-
-
-def _get_source_from_virtual_sink_port(vsp):
- """
- Resolve the source port that is connected to the given virtual sink port.
- Use the get source from virtual source to recursively resolve subsequent ports.
- """
- try: return _get_source_from_virtual_source_port(
- vsp.get_enabled_connections()[0].get_source())
- except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp
-
-def _get_source_from_virtual_source_port(vsp, traversed=[]):
- """
- Recursively resolve source ports over the virtual connections.
- Keep track of traversed sources to avoid recursive loops.
- """
- if not vsp.get_parent().is_virtual_source(): return vsp
- if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp
- try: return _get_source_from_virtual_source_port(
- _get_source_from_virtual_sink_port(
- filter(#get all virtual sinks with a matching stream id
- lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
- filter(#get all enabled blocks that are also virtual sinks
- lambda b: b.is_virtual_sink(),
- vsp.get_parent().get_parent().get_enabled_blocks(),
- ),
- )[0].get_sinks()[0]
- ), traversed + [vsp],
- )
- except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp
-
-def _get_sink_from_virtual_source_port(vsp):
- """
- Resolve the sink port that is connected to the given virtual source port.
- Use the get sink from virtual sink to recursively resolve subsequent ports.
- """
- try: return _get_sink_from_virtual_sink_port(
- vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first
- except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp
-
-def _get_sink_from_virtual_sink_port(vsp, traversed=[]):
- """
- Recursively resolve sink ports over the virtual connections.
- Keep track of traversed sinks to avoid recursive loops.
- """
- if not vsp.get_parent().is_virtual_sink(): return vsp
- if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp
- try: return _get_sink_from_virtual_sink_port(
- _get_sink_from_virtual_source_port(
- filter(#get all virtual source with a matching stream id
- lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
- filter(#get all enabled blocks that are also virtual sinks
- lambda b: b.is_virtual_source(),
- vsp.get_parent().get_parent().get_enabled_blocks(),
- ),
- )[0].get_sources()[0]
- ), traversed + [vsp],
- )
- except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp
-
-class Port(_Port, _GUIPort):
-
- def __init__(self, block, n, dir):
- """
- Make a new port from nested data.
-
- Args:
- block: the parent element
- n: the nested odict
- dir: the direction
- """
- self._n = n
- if n['type'] == 'message':
- n['domain'] = GR_MESSAGE_DOMAIN
- if 'domain' not in n:
- n['domain'] = DEFAULT_DOMAIN
- elif n['domain'] == GR_MESSAGE_DOMAIN:
- n['key'] = n['name']
- n['type'] = 'message' # for port color
- if n['type'] == 'msg':
- n['key'] = 'msg'
- if not n.find('key'):
- n['key'] = str(next(block.port_counters[dir == 'source']))
- # build the port
- _Port.__init__(
- self,
- block=block,
- n=n,
- dir=dir,
- )
- _GUIPort.__init__(self)
- self._nports = n.find('nports') or ''
- self._vlen = n.find('vlen') or ''
- self._optional = bool(n.find('optional'))
- self._clones = [] # references to cloned ports (for nports > 1)
-
- def get_types(self): return Constants.TYPE_TO_SIZEOF.keys()
-
- def is_type_empty(self): return not self._n['type']
-
- def validate(self):
- _Port.validate(self)
- if not self.get_enabled_connections() and not self.get_optional():
- self.add_error_message('Port is not connected.')
- #message port logic
- if self.get_type() == 'msg':
- if self.get_nports():
- self.add_error_message('A port of type "msg" cannot have "nports" set.')
- if self.get_vlen() != 1:
- self.add_error_message('A port of type "msg" must have a "vlen" of 1.')
-
- def rewrite(self):
- """
- Handle the port cloning for virtual blocks.
- """
- if self.is_type_empty():
- try: #clone type and vlen
- source = self.resolve_empty_type()
- self._type = str(source.get_type())
- self._vlen = str(source.get_vlen())
- except: #reset type and vlen
- self._type = ''
- self._vlen = ''
- _Port.rewrite(self)
-
- def resolve_virtual_source(self):
- if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self)
- if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self)
-
- def resolve_empty_type(self):
- if self.is_sink():
- try:
- src = _get_source_from_virtual_sink_port(self)
- if not src.is_type_empty(): return src
- except: pass
- sink = _get_sink_from_virtual_sink_port(self)
- if not sink.is_type_empty(): return sink
- if self.is_source():
- try:
- src = _get_source_from_virtual_source_port(self)
- if not src.is_type_empty(): return src
- except: pass
- sink = _get_sink_from_virtual_source_port(self)
- if not sink.is_type_empty(): return sink
-
- def get_vlen(self):
- """
- Get the vector length.
- If the evaluation of vlen cannot be cast to an integer, return 1.
-
- Returns:
- the vector length or 1
- """
- vlen = self.get_parent().resolve_dependencies(self._vlen)
- try: return int(self.get_parent().get_parent().evaluate(vlen))
- except: return 1
-
- def get_nports(self):
- """
- Get the number of ports.
- If already blank, return a blank
- If the evaluation of nports cannot be cast to a positive integer, return 1.
-
- Returns:
- the number of ports or 1
- """
- if self._nports == '': return ''
-
- nports = self.get_parent().resolve_dependencies(self._nports)
- try:
- return max(1, int(self.get_parent().get_parent().evaluate(nports)))
- except:
- return 1
-
- def get_optional(self): return bool(self._optional)
-
- def get_color(self):
- """
- Get the color that represents this port's type.
- Codes differ for ports where the vec length is 1 or greater than 1.
-
- Returns:
- a hex color code.
- """
- try:
- color = Constants.TYPE_TO_COLOR[self.get_type()]
- vlen = self.get_vlen()
- if vlen == 1: return color
- color_val = int(color[1:], 16)
- r = (color_val >> 16) & 0xff
- g = (color_val >> 8) & 0xff
- b = (color_val >> 0) & 0xff
- dark = (0, 0, 30, 50, 70)[min(4, vlen)]
- r = max(r-dark, 0)
- g = max(g-dark, 0)
- b = max(b-dark, 0)
- return '#%.2x%.2x%.2x'%(r, g, b)
- except: return _Port.get_color(self)
-
- def get_clones(self):
- """
- Get the clones of this master port (nports > 1)
-
- Returns:
- a list of ports
- """
- return self._clones
-
- def add_clone(self):
- """
- Create a clone of this (master) port and store a reference in self._clones.
-
- The new port name (and key for message ports) will have index 1... appended.
- If this is the first clone, this (master) port will get a 0 appended to its name (and key)
-
- Returns:
- the cloned port
- """
- # add index to master port name if there are no clones yet
- if not self._clones:
- self._name = self._n['name'] + '0'
- if not self._key.isdigit(): # also update key for none stream ports
- self._key = self._name
-
- # Prepare a copy of the odict for the clone
- n = self._n.copy()
- if 'nports' in n: n.pop('nports') # remove nports from the key so the copy cannot be a duplicator
- n['name'] = self._n['name'] + str(len(self._clones) + 1)
- n['key'] = '99999' if self._key.isdigit() else n['name'] # dummy value 99999 will be fixed later
-
- port = self.__class__(self.get_parent(), n, self._dir) # clone
- self._clones.append(port)
- return port
-
- def remove_clone(self, port):
- """
- Remove a cloned port (from the list of clones only)
- Remove the index 0 of the master port name (and key9 if there are no more clones left
- """
- self._clones.remove(port)
- # remove index from master port name if there are no more clones
- if not self._clones:
- self._name = self._n['name']
- if not self._key.isdigit(): # also update key for none stream ports
- self._key = self._name
diff --git a/grc/python/__init__.py b/grc/python/__init__.py
deleted file mode 100644
index 8b13789179..0000000000
--- a/grc/python/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/grc/scripts/CMakeLists.txt b/grc/scripts/CMakeLists.txt
index e905892308..6cc78c3cf3 100644
--- a/grc/scripts/CMakeLists.txt
+++ b/grc/scripts/CMakeLists.txt
@@ -23,3 +23,5 @@ GR_PYTHON_INSTALL(
DESTINATION ${GR_RUNTIME_DIR}
COMPONENT "grc"
)
+
+add_subdirectory(freedesktop)
diff --git a/grc/freedesktop/CMakeLists.txt b/grc/scripts/freedesktop/CMakeLists.txt
index 47e836f697..47e836f697 100644
--- a/grc/freedesktop/CMakeLists.txt
+++ b/grc/scripts/freedesktop/CMakeLists.txt
diff --git a/grc/freedesktop/README b/grc/scripts/freedesktop/README
index 0857ecc224..0857ecc224 100644
--- a/grc/freedesktop/README
+++ b/grc/scripts/freedesktop/README
diff --git a/grc/freedesktop/convert.sh b/grc/scripts/freedesktop/convert.sh
index e2cba264a6..e2cba264a6 100755
--- a/grc/freedesktop/convert.sh
+++ b/grc/scripts/freedesktop/convert.sh
diff --git a/grc/freedesktop/gnuradio-grc.desktop b/grc/scripts/freedesktop/gnuradio-grc.desktop
index 39beeca1b8..39beeca1b8 100644
--- a/grc/freedesktop/gnuradio-grc.desktop
+++ b/grc/scripts/freedesktop/gnuradio-grc.desktop
diff --git a/grc/freedesktop/gnuradio-grc.xml b/grc/scripts/freedesktop/gnuradio-grc.xml
index a5cb95d9fd..a5cb95d9fd 100644
--- a/grc/freedesktop/gnuradio-grc.xml
+++ b/grc/scripts/freedesktop/gnuradio-grc.xml
diff --git a/grc/freedesktop/gnuradio_logo_icon-square.svg b/grc/scripts/freedesktop/gnuradio_logo_icon-square.svg
index 3b54bf4001..3b54bf4001 100644
--- a/grc/freedesktop/gnuradio_logo_icon-square.svg
+++ b/grc/scripts/freedesktop/gnuradio_logo_icon-square.svg
diff --git a/grc/freedesktop/grc-icon-128.png b/grc/scripts/freedesktop/grc-icon-128.png
index 13efe806ba..13efe806ba 100644
--- a/grc/freedesktop/grc-icon-128.png
+++ b/grc/scripts/freedesktop/grc-icon-128.png
Binary files differ
diff --git a/grc/freedesktop/grc-icon-16.png b/grc/scripts/freedesktop/grc-icon-16.png
index bdd1823b3d..bdd1823b3d 100644
--- a/grc/freedesktop/grc-icon-16.png
+++ b/grc/scripts/freedesktop/grc-icon-16.png
Binary files differ
diff --git a/grc/freedesktop/grc-icon-24.png b/grc/scripts/freedesktop/grc-icon-24.png
index a124768125..a124768125 100644
--- a/grc/freedesktop/grc-icon-24.png
+++ b/grc/scripts/freedesktop/grc-icon-24.png
Binary files differ
diff --git a/grc/freedesktop/grc-icon-256.png b/grc/scripts/freedesktop/grc-icon-256.png
index 077688eac5..077688eac5 100644
--- a/grc/freedesktop/grc-icon-256.png
+++ b/grc/scripts/freedesktop/grc-icon-256.png
Binary files differ
diff --git a/grc/freedesktop/grc-icon-32.png b/grc/scripts/freedesktop/grc-icon-32.png
index a345aace3c..a345aace3c 100644
--- a/grc/freedesktop/grc-icon-32.png
+++ b/grc/scripts/freedesktop/grc-icon-32.png
Binary files differ
diff --git a/grc/freedesktop/grc-icon-48.png b/grc/scripts/freedesktop/grc-icon-48.png
index c522a5d0ec..c522a5d0ec 100644
--- a/grc/freedesktop/grc-icon-48.png
+++ b/grc/scripts/freedesktop/grc-icon-48.png
Binary files differ
diff --git a/grc/freedesktop/grc-icon-64.png b/grc/scripts/freedesktop/grc-icon-64.png
index df4f6dc07b..df4f6dc07b 100644
--- a/grc/freedesktop/grc-icon-64.png
+++ b/grc/scripts/freedesktop/grc-icon-64.png
Binary files differ
diff --git a/grc/freedesktop/grc_setup_freedesktop.in b/grc/scripts/freedesktop/grc_setup_freedesktop.in
index 87a388e2ec..87a388e2ec 100644
--- a/grc/freedesktop/grc_setup_freedesktop.in
+++ b/grc/scripts/freedesktop/grc_setup_freedesktop.in
diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion
index 203a8c773d..04a1cb44e7 100755
--- a/grc/scripts/gnuradio-companion
+++ b/grc/scripts/gnuradio-companion
@@ -1,6 +1,6 @@
#!/usr/bin/env python
"""
-Copyright 2009-2015 Free Software Foundation, Inc.
+Copyright 2016 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@@ -20,111 +20,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import os
import sys
-import optparse
-import warnings
-
-GR_IMPORT_ERROR_MESSAGE = """\
-Cannot import gnuradio.
-
-Is the python path environment variable set correctly?
- All OS: PYTHONPATH
-
-Is the library path environment variable set correctly?
- Linux: LD_LIBRARY_PATH
- Windows: PATH
- MacOSX: DYLD_LIBRARY_PATH
-"""
-
-VERSION_AND_DISCLAIMER_TEMPLATE = """\
-GNU Radio Companion %s
-
-This program is part of GNU Radio
-GRC comes with ABSOLUTELY NO WARRANTY.
-This is free software, and you are welcome to redistribute it.
-"""
-
-
-def die(error, message):
- msg = "{0}\n\n({1})".format(message, error)
- try:
- import gtk
- d = gtk.MessageDialog(
- type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_CLOSE,
- message_format=msg,
- )
- d.set_title(type(error).__name__)
- d.run()
- exit(1)
- except ImportError:
- exit(type(error).__name__ + '\n\n' + msg)
-
-
-def check_gtk():
- try:
- warnings.filterwarnings("error")
- import pygtk
- pygtk.require('2.0')
- import gtk
- gtk.init_check()
- warnings.filterwarnings("always")
- except Exception as err:
- die(err, "Failed to initialize GTK. If you are running over ssh, "
- "did you enable X forwarding and start ssh with -X?")
-
-
-def check_gnuradio_import():
- try:
- from gnuradio import gr
- except ImportError as err:
- die(err, GR_IMPORT_ERROR_MESSAGE)
-
-
-def check_blocks_path():
- if 'GR_DONT_LOAD_PREFS' in os.environ and not os.environ.get('GRC_BLOCKS_PATH', ''):
- die(EnvironmentError("No block definitions available"),
- "Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH.")
-
-
-def get_source_tree_root():
- source_tree_subpath = "/grc/scripts"
- script_path = os.path.dirname(os.path.abspath(__file__))
- if script_path.endswith(source_tree_subpath):
- return script_path[:-len(source_tree_subpath)]
-
-
-def main():
- check_gnuradio_import()
-
- from gnuradio import gr
- parser = optparse.OptionParser(
- usage='usage: %prog [options] [saved flow graphs]',
- version=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version())
- options, args = parser.parse_args()
-
- check_gtk()
- check_blocks_path()
- source_tree_root = get_source_tree_root()
- if not source_tree_root:
- # run the installed version
- from gnuradio.grc.python.Platform import Platform
- from gnuradio.grc.gui.ActionHandler import ActionHandler
-
- else:
- print("Running from source tree")
- sys.path.insert(1, source_tree_root)
- from grc.python.Platform import Platform
- from grc.gui.ActionHandler import ActionHandler
-
- try:
- import gtk
- gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0))
- except:
- pass
-
- ActionHandler(args, Platform())
-
-
-if __name__ == '__main__':
- main()
+script_path = os.path.dirname(os.path.abspath(__file__))
+source_tree_subpath = "/grc/scripts"
+
+if not script_path.endswith(source_tree_subpath):
+ # run the installed version
+ from gnuradio.grc.main import main
+ from gnuradio.grc import checks
+else:
+ print("Running from source tree")
+ sys.path.insert(1, script_path[:-len(source_tree_subpath)])
+ from grc.main import main
+ from grc import checks
+
+checks.do_all()
+exit(main())
diff --git a/grc/todo.txt b/grc/todo.txt
deleted file mode 100644
index cedea72aa3..0000000000
--- a/grc/todo.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-##################################################
-# Examples
-##################################################
-* Push-to-Talk example
-* Start/Stop the flow graph
-
-##################################################
-# Blocks
-##################################################
-* probe: also non-float outputs
-* log slider gui control
-* packet mod: whitening offset
-* wx min window size in options block
-* gr_adaptive_fir_ccf
-* size params for the graphical sinks
-* callbacks for set average on fft, waterfall, number sinks
-* add units to params: Sps, Hz, dB...
-* add bool type to command line option store_true or store_false
-* messages for packet blocks and probe blocks
-
-##################################################
-# Features
-##################################################
-* extract category from doxygen
- * fix up block tree to mirror current doxygen group
- * remove blocks in block tree covered by doxygen
-* param editor, expand entry boxes in focus
-* change param dialog to panel within main window
-* gui grid editor for configuring grid params/placing wxgui plots and controls
-* drag from one port to another to connect
-* per parameter docs
- * extract individual param docs from doxygen
- * doc tag in param for handwritten notes
-* separate generated code into top block and gui class
- * use gui.py in gr-wxgui and remove custom top_block_gui
-* configuration option for adding block paths
-* orientations for ports (top, right, bottom, left)
- * source defaults to right, sink defaults to left
-* separation of variables and gui controls
-* speedup w/ background layer and animation layer
-* multiple doxygen directories (doc_dir becomes doc_path)
-* use pango markup in tooltips for params
-* use get_var_make to determine if it is a variable, not regexp
-* concept of a project, or project flow graph
- * collection of blocks, hier and top
- * system-wide, default/work, and user created
-* use templates/macros to generate the repetative stuff in the xml
-
-##################################################
-# Problems
-##################################################
-* msg ports dont work with virtual connections
- * dont fix this until pmts are used?
-* hier block generation
- * auto generate hier library on changes
- * auto clean hier library when block removed
- * add hier blocks to tree without restart
-* 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 properties dialog
-* weird grid params misbehaving
-* gr hier blocks have more diverse IO capabilities than we allow for
-
-##################################################
-# Future
-##################################################
-* require pygtk 2.12 for treeview tooltips
- * remove try/except in BlockTreeWindow.py