[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] [gnuradio] 03/18: grc-refactor: Moved code from grc.mo
From: |
git |
Subject: |
[Commit-gnuradio] [gnuradio] 03/18: grc-refactor: Moved code from grc.model.base to grc.model |
Date: |
Sun, 24 Apr 2016 19:19:36 +0000 (UTC) |
This is an automated email from the git hooks/post-receive script.
jcorgan pushed a commit to branch master
in repository gnuradio.
commit 0d5bd456adb7e4299ca6f77f9f3880426c0d0905
Author: Seth Hitefield <address@hidden>
Date: Fri Nov 20 15:43:52 2015 -0500
grc-refactor: Moved code from grc.model.base to grc.model
---
grc/gui/ActionHandler.py | 3 +-
grc/gui/Block.py | 2 +-
grc/gui/Connection.py | 2 +-
grc/gui/Port.py | 2 +-
grc/model/Block.py | 523 ++++++++++++++++++++++++++++++++--
grc/model/Connection.py | 124 ++++++++-
grc/model/Constants.py | 34 ++-
grc/model/{base => }/Element.py | 55 ++--
grc/model/FlowGraph.py | 462 +++++++++++++++++++++++++++++-
grc/model/Generator.py | 5 +-
grc/model/Param.py | 199 +++++++++++--
grc/model/{base => }/ParseXML.py | 2 +-
grc/model/Platform.py | 252 +++++++++++++++--
grc/model/Port.py | 109 +++++++-
grc/model/base/Block.py | 542 ------------------------------------
grc/model/base/CMakeLists.txt | 43 ---
grc/model/base/Connection.py | 139 ---------
grc/model/base/Constants.py | 50 ----
grc/model/base/FlowGraph.py | 482 --------------------------------
grc/model/base/Param.py | 203 --------------
grc/model/base/Platform.py | 274 ------------------
grc/model/base/Port.py | 136 ---------
grc/model/base/__init__.py | 20 --
grc/model/{base => }/block_tree.dtd | 0
grc/model/{base => }/domain.dtd | 0
grc/model/{base => }/flow_graph.dtd | 0
grc/model/{base => }/odict.py | 0
27 files changed, 1664 insertions(+), 1999 deletions(-)
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index a5081da..1702475 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -27,7 +27,8 @@ pygtk.require('2.0')
import gtk
import gobject
-from ..model.base import Constants, ParseXML
+
+from ..model import Constants, ParseXML
from .. model.Constants import XTERM_EXECUTABLE
from . import Dialogs, Messages, Preferences, Actions
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index aab1cab..97b4e96 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -30,7 +30,7 @@ from .Constants import (
BORDER_PROXIMITY_SENSITIVITY
)
from . Element import Element
-from ..model.base import odict
+from ..model.odict import odict
from ..model.Param import num_to_str
from ..model.Block import Block as _Block
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index 241ada8..d248543 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -24,7 +24,7 @@ import Utils
from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
from Element import Element
-from ..model.base.Constants import GR_MESSAGE_DOMAIN
+from ..model.Constants import GR_MESSAGE_DOMAIN
from ..model.Connection import Connection as _Connection
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index 23d41da..28d3b4d 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -27,7 +27,7 @@ from .Constants import (
CONNECTOR_EXTENSION_INCREMENT, PORT_LABEL_PADDING, PORT_MIN_WIDTH,
PORT_LABEL_HIDDEN_WIDTH, PORT_FONT
)
from .Element import Element
-from ..model.base.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
+from ..model.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
from ..model.Port import Port as _Port
diff --git a/grc/model/Block.py b/grc/model/Block.py
index aaf65fb..9cd740c 100644
--- a/grc/model/Block.py
+++ b/grc/model/Block.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
@@ -20,15 +20,54 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA
import collections
import itertools
-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 UserDict import UserDict
+
+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 . import epy_block_io
-from .FlowGraph import _variable_matcher
+from . odict import odict
+from . FlowGraph import _variable_matcher
+from . Element import Element
+from Cheetah.Template import Template
+
+
+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 model
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]
-class Block(_Block):
+
+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):
"""
@@ -41,7 +80,7 @@ class Block(_Block):
Returns:
block a new block
"""
- #grab the data
+ # 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')
@@ -51,12 +90,147 @@ class Block(_Block):
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,
- )
+
+ # 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 "%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
+ })
+ ))
+
self._epy_source_hash = -1 # for epy blocks
self._epy_reload_error = None
@@ -82,7 +256,7 @@ class Block(_Block):
Call the base class validate.
Evaluate the checks: each check must evaluate to True.
"""
- _Block.validate(self)
+ Element.validate(self)
#evaluate the checks
for check in self._checks:
check_res = self.resolve_dependencies(check)
@@ -120,7 +294,6 @@ class Block(_Block):
"""
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)()
@@ -188,9 +361,6 @@ class Block(_Block):
documentation[''] = from_xml
return documentation
- def get_category(self):
- return _Block.get_category(self)
-
def get_imports(self, raw=False):
"""
Resolve all import statements.
@@ -321,4 +491,319 @@ class Block(_Block):
update_ports('in', self.get_sinks(), blk_io.sinks, 'sink')
update_ports('out', self.get_sources(), blk_io.sources, 'source')
- _Block.rewrite(self)
+ 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 - %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 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/model/Connection.py b/grc/model/Connection.py
index e5b4c25..4404308 100644
--- a/grc/model/Connection.py
+++ b/grc/model/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
@@ -19,13 +19,64 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA
from . import Constants
-from .base.Connection import Connection as _Connection
+from .Element import Element
+from .odict import odict
-
-class Connection(_Connection):
+class Connection(Element):
def __init__(self, flow_graph, porta, portb):
- _Connection.__init__(self, flow_graph, porta, portb)
+ """
+ Make a new connection given the parent and 2 ports.
+
+ Args:
+ flow_graph: the parent of this element
+ porta: a port (any direction)
+ portb: a port (any direction)
+ @throws Error cannot make connection
+
+ Returns:
+ a new connection
+ """
+ Element.__init__(self, flow_graph)
+ source = sink = None
+ #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 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():
+ 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();
+
+ for i in range(len(sources)):
+ try:
+ 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)'%(
+ 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'
@@ -38,8 +89,69 @@ class Connection(_Connection):
Validate the connections.
The ports must match in io size.
"""
- _Connection.validate(self)
+ """
+ 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))
+ too_many_other_sinks = (
+ source_domain in platform.get_domains() and
+ not platform.get_domain(key=source_domain)['multiple_sinks'] 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
+ 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)
+ if too_many_other_sources:
+ self.add_error_message(
+ 'Domain "%s" can have only one upstream block' % 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 "%s" does not match sink IO
size "%s".'%(source_size, sink_size))
+
+
+ def get_enabled(self):
+ """
+ Get the enabled state of this connection.
+
+ Returns:
+ true if source and sink blocks are enabled
+ """
+ return self.get_source().get_parent().get_enabled() and \
+ self.get_sink().get_parent().get_enabled()
+
+ #############################
+ # Access Ports
+ #############################
+ def get_sink(self): return self._sink
+ def get_source(self): return self._source
+
+ ##############################################
+ ## Import/Export Methods
+ ##############################################
+ def export_data(self):
+ """
+ Export this connection's info.
+
+ Returns:
+ a nested data odict
+ """
+ n = odict()
+ n['source_block_id'] = self.get_source().get_parent().get_id()
+ n['sink_block_id'] = self.get_sink().get_parent().get_id()
+ n['source_key'] = self.get_source().get_key()
+ n['sink_key'] = self.get_sink().get_key()
+ return n
diff --git a/grc/model/Constants.py b/grc/model/Constants.py
index b7a370c..9336a9f 100644
--- a/grc/model/Constants.py
+++ b/grc/model/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
@@ -41,6 +41,38 @@ BLOCKS_DIRS = filter( # filter blank strings
]).split(PATH_SEP),
) + [HIER_BLOCKS_LIB_DIR]
+
+#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
+
+
# user settings
XTERM_EXECUTABLE = _gr_prefs.get_string('grc', 'xterm_executable', 'xterm')
diff --git a/grc/model/base/Element.py b/grc/model/Element.py
similarity index 76%
rename from grc/model/base/Element.py
rename to grc/model/Element.py
index 3b604a5..351445f 100644
--- a/grc/model/base/Element.py
+++ b/grc/model/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
@@ -33,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):
"""
@@ -73,26 +74,48 @@ 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
##############################################
## 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
##############################################
- 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
+ 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
diff --git a/grc/model/FlowGraph.py b/grc/model/FlowGraph.py
index ccd60e1..484c3fd 100644
--- a/grc/model/FlowGraph.py
+++ b/grc/model/FlowGraph.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,11 +18,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA
"""
import re
import imp
+import time
from operator import methodcaller
+from itertools import ifilter
-from . import expr_utils
-from .base.FlowGraph import FlowGraph as _FlowGraph
+from ..gui import Messages
+from . import expr_utils
+from .odict import odict
+from .Element import Element
+from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
_variable_matcher = re.compile('^(variable\w*)$')
_parameter_matcher = re.compile('^(parameter)$')
@@ -33,16 +38,129 @@ _bus_struct_sink_searcher =
re.compile('^(bus_structure_sink)$')
_bus_struct_src_searcher = re.compile('^(bus_structure_source)$')
-class FlowGraph(_FlowGraph):
+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
+
- def __init__(self, **kwargs):
+class FlowGraph(Element):
+
+ def __init__(self, platform):
self.grc_file_path = ''
- _FlowGraph.__init__(self, **kwargs)
+ """
+ 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()
+
self.n = {}
self.n_hash = -1
self._renew_eval_ns = True
self._eval_cache = {}
+ 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 _eval(self, code, namespace, namespace_hash):
"""
Evaluate the code with the given namespace.
@@ -242,6 +360,152 @@ class FlowGraph(_FlowGraph):
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.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 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
+
+ ### TODO >>> THIS SUCKS ###
def rewrite(self):
"""
Flag the namespace to be renewed.
@@ -266,7 +530,43 @@ class FlowGraph(_FlowGraph):
for j in sink:
self.connect(source, j);
self._renew_eval_ns = True
- _FlowGraph.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()
reconnect_bus_blocks();
def evaluate(self, expr):
@@ -320,7 +620,7 @@ class FlowGraph(_FlowGraph):
def get_new_block(self, key):
"""Try to auto-generate the block from file if missing"""
- block = _FlowGraph.get_new_block(self, key)
+ block = self._get_new_block(key)
if not block:
platform = self.get_parent()
# we're before the initial fg rewrite(), so no evaluated values!
@@ -333,5 +633,149 @@ class FlowGraph(_FlowGraph):
)
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
+ block = self._get_new_block(key) # can be None
return block
+
+ ##############################################
+ ## 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
self._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)
+ print('Block key "%s" not found' % key)
+
+ block.import_data(block_n)
+
+ # 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 = self._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
+
+ @staticmethod
+ 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
+
+ @staticmethod
+ 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
diff --git a/grc/model/Generator.py b/grc/model/Generator.py
index 2e9553f..c61900e 100644
--- a/grc/model/Generator.py
+++ b/grc/model/Generator.py
@@ -28,8 +28,9 @@ from distutils.spawn import find_executable
from Cheetah.Template import Template
-from .base import odict, ParseXML
-from .base.Constants import BLOCK_FLAG_NEED_QT_GUI
+from . import ParseXML
+from .odict import odict
+from .Constants import BLOCK_FLAG_NEED_QT_GUI
from . import expr_utils
from . Constants import (
diff --git a/grc/model/Param.py b/grc/model/Param.py
index b627e5e..c8da6a5 100644
--- a/grc/model/Param.py
+++ b/grc/model/Param.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
@@ -25,17 +25,23 @@ from gnuradio import gr
import Constants
from Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
-from .base.Param import Param as _Param
+
_check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
_show_id_matcher =
re.compile('^(variable\w*|parameter|options|notebook|epy_module)$')
+from .odict import odict
+from .Element import Element
#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 _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))
def num_to_str(num):
""" Display logic for numbers """
@@ -48,24 +54,110 @@ def num_to_str(num):
else: return '%s+%sj'%(eng_notation.num_to_str(num.real),
eng_notation.num_to_str(num.imag))
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 "%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.
-class Param(_Param):
-
- def __init__(self, **kwargs):
- _Param.__init__(self, **kwargs)
+ 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()
+ 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 "%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
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 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):
"""
@@ -115,6 +207,19 @@ class Param(_Param):
##################################################
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 - %s(%s)'%(self.get_name(),
self.get_key())
+
def get_color(self):
"""
Get the color that represents this param's type.
@@ -144,7 +249,8 @@ class Param(_Param):
'notebook': Constants.INT_VECTOR_COLOR_SPEC,
'raw': Constants.WILDCARD_COLOR_SPEC,
}[self.get_type()]
- except: return _Param.get_color(self)
+ except:
+ return '#FFFFFF'
def get_hide(self):
"""
@@ -157,7 +263,7 @@ class Param(_Param):
Returns:
hide the hide property string
"""
- hide = _Param.get_hide(self)
+ 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'
@@ -179,9 +285,12 @@ class Param(_Param):
def validate(self):
"""
Validate the param.
- A test evaluation is performed
+ The value must be evaluated and type must a possible type.
"""
- _Param.validate(self) #checks 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())
+
self._evaluated = None
try: self._evaluated = self.evaluate()
except Exception, e: self.add_error_message(str(e))
@@ -427,3 +536,53 @@ class Param(_Param):
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): self._value = str(value) #must be a string
+
+ def value_is_default(self):
+ return self._default == self._value
+
+ def get_type(self): return
self.get_parent().resolve_dependencies(self._type)
+ def get_tab_label(self): return self._tab_label
+
+ 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
+
+ ##############################################
+ # 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/model/base/ParseXML.py b/grc/model/ParseXML.py
similarity index 99%
rename from grc/model/base/ParseXML.py
rename to grc/model/ParseXML.py
index 2d5fed0..cd65fa1 100644
--- a/grc/model/base/ParseXML.py
+++ b/grc/model/ParseXML.py
@@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
"""
from lxml import etree
-from . import odict
+from .odict import odict
xml_failures = {}
diff --git a/grc/model/Platform.py b/grc/model/Platform.py
index e6b17fe..0ac301f 100644
--- a/grc/model/Platform.py
+++ b/grc/model/Platform.py
@@ -19,9 +19,14 @@ 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 .Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD, DOMAIN_DTD
+from .Element import Element
+
+from . import ParseXML
+from .odict import odict
from . import extract_docs
from .Constants import (
@@ -32,11 +37,24 @@ from .Generator import Generator
from .. gui import Messages
-class Platform(_Platform):
+class Platform(Element):
def __init__(self):
"""
Make a platform for gnuradio.
+
+ 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
"""
+
# ensure hier and conf directories
if not os.path.exists(HIER_BLOCKS_LIB_DIR):
os.mkdir(HIER_BLOCKS_LIB_DIR)
@@ -51,20 +69,33 @@ class Platform(_Platform):
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=[(name, color) for name, key, sizeof, color in CORE_TYPES],
- )
+ Element.__init__(self)
+ self._name = 'GNU Radio Companion'
+ # Save the verion string to the first
+ version = (gr.version(), gr.major_version(), gr.api_version(),
gr.minor_version())
+ 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 = 'grc'
+ self._license = __doc__.strip()
+ self._website = 'http://gnuradio.org'
+ self._block_paths = list(set(BLOCKS_DIRS))
+ self._block_dtd = BLOCK_DTD
+ self._default_flow_graph = DEFAULT_FLOW_GRAPH
+ self._generator = Generator
+ self._colors = [(name, color) for name, key, sizeof, color in
CORE_TYPES]
+ #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()
self._auto_hier_block_generate_chain = set()
@@ -90,12 +121,12 @@ class Platform(_Platform):
def load_blocks(self):
self._docstring_extractor.start()
- _Platform.load_blocks(self)
+ self._load_blocks()
self._docstring_extractor.finish()
# self._docstring_extractor.wait()
def load_block_xml(self, xml_file):
- block = _Platform.load_block_xml(self, xml_file)
+ block = self._load_block_xml(self, xml_file)
self._docstring_extractor.query(
block.get_key(),
block.get_imports(raw=True),
@@ -153,3 +184,188 @@ class Platform(_Platform):
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"""
+ # 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)
+
+ 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
diff --git a/grc/model/Port.py b/grc/model/Port.py
index 8466f4f..7197f75 100644
--- a/grc/model/Port.py
+++ b/grc/model/Port.py
@@ -1,5 +1,5 @@
"""
-Copyright 2008-2012 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,8 +17,8 @@ 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.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
-from .base.Port import Port as _Port
+from .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN
+from .Element import Element
from . import Constants
@@ -85,7 +85,7 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]):
except: raise Exception, 'Could not resolve source for virtual sink port
%s'%vsp
-class Port(_Port):
+class Port(Element):
def __init__(self, block, n, dir):
"""
@@ -108,24 +108,41 @@ class Port(_Port):
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,
- )
+
+ #build the port
+ Element.__init__(self, block)
+ #grab the data
+ self._name = n['name']
+ self._key = n['key']
+ self._type = n['type']
+ 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 - %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): return Constants.TYPE_TO_SIZEOF.keys()
def is_type_empty(self): return not self._n['type']
def validate(self):
- _Port.validate(self)
+ 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())
if not self.get_enabled_connections() and not self.get_optional():
self.add_error_message('Port is not connected.')
#message port logic
@@ -147,7 +164,17 @@ class Port(_Port):
except: #reset type and vlen
self._type = ''
self._vlen = ''
- _Port.rewrite(self)
+ 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)
@@ -221,7 +248,8 @@ class Port(_Port):
g = max(g-dark, 0)
b = max(b-dark, 0)
return '#%.2x%.2x%.2x'%(r, g, b)
- except: return _Port.get_color(self)
+ except:
+ return '#FFFFFF'
def get_clones(self):
"""
@@ -269,3 +297,56 @@ class Port(_Port):
self._name = self._n['name']
if not self._key.isdigit(): # also update key for none stream
ports
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
+ def is_sink(self): return self._dir == 'sink'
+ def is_source(self): return self._dir == 'source'
+ def is_port(self): return True
+ 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/model/base/Block.py b/grc/model/base/Block.py
deleted file mode 100644
index 32aac84..0000000
--- a/grc/model/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 model
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/model/base/CMakeLists.txt b/grc/model/base/CMakeLists.txt
deleted file mode 100644
index bdc8a50..0000000
--- a/grc/model/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/model/base/Connection.py b/grc/model/base/Connection.py
deleted file mode 100644
index 8df0f5a..0000000
--- a/grc/model/base/Connection.py
+++ /dev/null
@@ -1,139 +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 . import odict
-
-
-class Connection(Element):
-
- def __init__(self, flow_graph, porta, portb):
- """
- Make a new connection given the parent and 2 ports.
-
- Args:
- flow_graph: the parent of this element
- porta: a port (any direction)
- portb: a port (any direction)
- @throws Error cannot make connection
-
- Returns:
- a new connection
- """
- Element.__init__(self, flow_graph)
- source = sink = None
- #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 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():
- 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();
-
- for i in range(len(sources)):
- try:
- 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)'%(
- self.get_source().get_parent(),
- self.get_source(),
- self.get_sink().get_parent(),
- self.get_sink(),
- )
-
- def is_connection(self): return True
-
- def validate(self):
- """
- 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))
- too_many_other_sinks = (
- source_domain in platform.get_domains() and
- not platform.get_domain(key=source_domain)['multiple_sinks'] 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
- 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)
- if too_many_other_sources:
- self.add_error_message(
- 'Domain "%s" can have only one upstream block' % sink_domain)
-
- def get_enabled(self):
- """
- Get the enabled state of this connection.
-
- Returns:
- true if source and sink blocks are enabled
- """
- return self.get_source().get_parent().get_enabled() and \
- self.get_sink().get_parent().get_enabled()
-
- #############################
- # Access Ports
- #############################
- def get_sink(self): return self._sink
- def get_source(self): return self._source
-
- ##############################################
- ## Import/Export Methods
- ##############################################
- def export_data(self):
- """
- Export this connection's info.
-
- Returns:
- a nested data odict
- """
- n = odict()
- n['source_block_id'] = self.get_source().get_parent().get_id()
- n['sink_block_id'] = self.get_sink().get_parent().get_id()
- n['source_key'] = self.get_source().get_key()
- n['sink_key'] = self.get_sink().get_key()
- return n
diff --git a/grc/model/base/Constants.py b/grc/model/base/Constants.py
deleted file mode 100644
index 1e83de6..0000000
--- a/grc/model/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/model/base/FlowGraph.py b/grc/model/base/FlowGraph.py
deleted file mode 100644
index 40b4e37..0000000
--- a/grc/model/base/FlowGraph.py
+++ /dev/null
@@ -1,482 +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 ..FlowGraph 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)
- print('Block key "%s" not found' % key)
-
- block.import_data(block_n)
-
- # 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/model/base/Param.py b/grc/model/base/Param.py
deleted file mode 100644
index b246d9f..0000000
--- a/grc/model/base/Param.py
+++ /dev/null
@@ -1,203 +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 value_is_default(self):
- return self._default == self._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/model/base/Platform.py b/grc/model/base/Platform.py
deleted file mode 100644
index 367140f..0000000
--- a/grc/model/base/Platform.py
+++ /dev/null
@@ -1,274 +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 sys
-
-import os
-
-from .Block import Block as _Block
-from .Connection import Connection as _Connection
-from .Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD, DOMAIN_DTD
-from .Element import Element as _Element
-from .FlowGraph import FlowGraph as _FlowGraph
-from .Param import Param as _Param
-from .Port import Port as _Port
-from . import ParseXML, odict
-
-
-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%r\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/model/base/Port.py b/grc/model/base/Port.py
deleted file mode 100644
index 39166d1..0000000
--- a/grc/model/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']
- 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/model/base/__init__.py b/grc/model/base/__init__.py
deleted file mode 100644
index 2682db8..0000000
--- a/grc/model/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/model/base/block_tree.dtd b/grc/model/block_tree.dtd
similarity index 100%
rename from grc/model/base/block_tree.dtd
rename to grc/model/block_tree.dtd
diff --git a/grc/model/base/domain.dtd b/grc/model/domain.dtd
similarity index 100%
rename from grc/model/base/domain.dtd
rename to grc/model/domain.dtd
diff --git a/grc/model/base/flow_graph.dtd b/grc/model/flow_graph.dtd
similarity index 100%
rename from grc/model/base/flow_graph.dtd
rename to grc/model/flow_graph.dtd
diff --git a/grc/model/base/odict.py b/grc/model/odict.py
similarity index 100%
rename from grc/model/base/odict.py
rename to grc/model/odict.py
- [Commit-gnuradio] [gnuradio] 14/18: grc-refactor: fix fg load, (continued)
- [Commit-gnuradio] [gnuradio] 14/18: grc-refactor: fix fg load, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 12/18: grc-refactor: remove (hopefully) all deps to GR in core/ and gui/, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 18/18: Merge branch 'maint_grcwg' into refactoring, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 11/18: grc-refactor: move gui prefs to gui, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 05/18: grc-refactor: fixes, type-testing-flags, FlowGraph.py, (more), git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 01/18: grc-refactor: move grc.base to grc.python.base, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 10/18: grc-refactor: Platform.py, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 17/18: grc-refactor: CMake fixes, start-up script cleaned up, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 16/18: Merge remote-tracking branch 'upstream/master' into refactoring, git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 04/18: grc-refactor: Cleaning up code style to match PEP8., git, 2016/04/24
- [Commit-gnuradio] [gnuradio] 03/18: grc-refactor: Moved code from grc.model.base to grc.model,
git <=