From 498715deea12451bd271d20b51c02f01530acd14 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Fri, 27 May 2016 15:11:29 +0200
Subject: grc-refactor: move param entry colors spec in gui package

---
 grc/core/Constants.py | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)

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

diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 462049cc73..4f278bb22d 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -70,8 +70,8 @@ COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
 REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
 INT_TYPES = tuple(INT_TYPES)
 
-# Updating colors. Using the standard color pallette from:
-#  http://www.google.com/design/spec/style/color.html#color-color-palette
+# Updating colors. Using the standard color palette from:
+#   http://www.google.com/design/spec/style/color.html#color-color-palette
 # Most are based on the main, primary color standard. Some are within
 # that color's spectrum when it was deemed necessary.
 GRC_COLOR_BROWN = '#795548'
@@ -132,18 +132,3 @@ for name, key, sizeof, color in CORE_TYPES:
 for key, (sizeof, color) in ALIAS_TYPES.iteritems():
     TYPE_TO_COLOR[key] = color
     TYPE_TO_SIZEOF[key] = sizeof
-
-# Coloring
-COMPLEX_COLOR_SPEC = '#3399FF'
-FLOAT_COLOR_SPEC = '#FF8C69'
-INT_COLOR_SPEC = '#00FF99'
-SHORT_COLOR_SPEC = '#FFFF66'
-BYTE_COLOR_SPEC = '#FF66FF'
-COMPLEX_VECTOR_COLOR_SPEC = '#3399AA'
-FLOAT_VECTOR_COLOR_SPEC = '#CC8C69'
-INT_VECTOR_COLOR_SPEC = '#00CC99'
-SHORT_VECTOR_COLOR_SPEC = '#CCCC33'
-BYTE_VECTOR_COLOR_SPEC = '#CC66CC'
-ID_COLOR_SPEC = '#DDDDDD'
-WILDCARD_COLOR_SPEC = '#FFFFFF'
-MSG_COLOR_SPEC = '#777777'
-- 
cgit v1.2.3


From 94c4606edd30dc8b1278580782f2809b69f04641 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Fri, 3 Jun 2016 10:02:36 +0200
Subject: grc: py3k compat using python-modernize

---
 grc/checks.py                        |  1 +
 grc/core/Block.py                    | 33 +++++++++--------
 grc/core/Config.py                   |  1 +
 grc/core/Connection.py               |  8 +++--
 grc/core/Constants.py                | 10 ++++--
 grc/core/Element.py                  |  6 ++--
 grc/core/FlowGraph.py                | 57 +++++++++++++++--------------
 grc/core/Messages.py                 |  3 +-
 grc/core/Param.py                    | 69 ++++++++++++++++++------------------
 grc/core/ParseXML.py                 | 21 +++++++----
 grc/core/Platform.py                 | 34 ++++++++++--------
 grc/core/Port.py                     | 41 +++++++++++----------
 grc/core/generator/FlowGraphProxy.py | 16 +++++----
 grc/core/generator/Generator.py      | 30 +++++++++-------
 grc/core/generator/__init__.py       |  3 +-
 grc/core/utils/__init__.py           | 10 +++---
 grc/core/utils/complexity.py         | 13 +++----
 grc/core/utils/epy_block_io.py       | 11 ++++--
 grc/core/utils/expr_utils.py         | 21 ++++++-----
 grc/core/utils/extract_docs.py       | 28 ++++++++-------
 grc/core/utils/odict.py              |  2 ++
 grc/gui/ActionHandler.py             |  8 +++--
 grc/gui/Actions.py                   | 14 +++++---
 grc/gui/Bars.py                      |  3 +-
 grc/gui/Block.py                     |  3 +-
 grc/gui/BlockTreeWindow.py           | 14 +++++---
 grc/gui/Colors.py                    |  1 +
 grc/gui/Config.py                    |  7 ++--
 grc/gui/Connection.py                | 16 ++++-----
 grc/gui/Constants.py                 |  1 +
 grc/gui/Dialogs.py                   |  1 +
 grc/gui/DrawingArea.py               |  1 +
 grc/gui/Element.py                   |  6 ++--
 grc/gui/Executor.py                  |  1 +
 grc/gui/FileDialogs.py               |  9 ++---
 grc/gui/FlowGraph.py                 | 14 +++++---
 grc/gui/MainWindow.py                |  6 ++--
 grc/gui/NotebookPage.py              |  1 +
 grc/gui/Param.py                     |  1 +
 grc/gui/ParamWidgets.py              |  1 +
 grc/gui/ParserErrorsDialog.py        |  8 +++--
 grc/gui/Platform.py                  |  4 ++-
 grc/gui/Port.py                      |  8 ++---
 grc/gui/Preferences.py               | 21 ++++++-----
 grc/gui/PropsDialog.py               |  6 ++--
 grc/gui/StateCache.py                |  5 +--
 grc/gui/Utils.py                     |  8 +++--
 grc/gui/VariableEditor.py            |  6 ++--
 grc/gui/external_editor.py           |  6 ++--
 grc/main.py                          |  1 +
 50 files changed, 356 insertions(+), 243 deletions(-)

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

diff --git a/grc/checks.py b/grc/checks.py
index 66c114d723..40a0b2b270 100755
--- a/grc/checks.py
+++ b/grc/checks.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import os
 import warnings
 
diff --git a/grc/core/Block.py b/grc/core/Block.py
index aafc5db6f1..062598e9d1 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -17,9 +17,13 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import collections
 import itertools
 
+from six.moves import map, range
+
 from Cheetah.Template import Template
 
 from .utils import epy_block_io, odict
@@ -70,7 +74,7 @@ class Block(Element):
             self._flags += BLOCK_FLAG_THROTTLE
 
         self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '')
-        self._imports = map(lambda i: i.strip(), n.findall('import'))
+        self._imports = [i.strip() for i in n.findall('import')]
         self._make = n.find('make')
         self._var_make = n.find('var_make')
         self._var_value = n.find('var_value') or '$value'
@@ -250,7 +254,7 @@ class Block(Element):
             self.back_ofthe_bus(ports)
             # Renumber non-message/message ports
             domain_specific_port_index = collections.defaultdict(int)
-            for port in filter(lambda p: p.get_key().isdigit(), ports):
+            for port in [p for p in ports if p.get_key().isdigit()]:
                 domain = port.get_domain()
                 port._key = str(domain_specific_port_index[domain])
                 domain_specific_port_index[domain] += 1
@@ -275,7 +279,8 @@ class Block(Element):
         """
         if raw:
             return self._imports
-        return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), []))
+        return [i for i in sum([self.resolve_dependencies(i).split('\n')
+                                for i in self._imports], []) if i]
 
     def get_make(self, raw=False):
         if raw:
@@ -300,7 +305,7 @@ class Block(Element):
             if 'self.' in callback:
                 return callback
             return 'self.{}.{}'.format(self.get_id(), callback)
-        return map(make_callback, self._callbacks)
+        return [make_callback(c) for c in self._callbacks]
 
     def is_virtual_sink(self):
         return self.get_key() == 'virtual_sink'
@@ -622,10 +627,10 @@ class Block(Element):
         """
         changed = False
         type_param = None
-        for param in filter(lambda p: p.is_enum(), self.get_params()):
+        for param in [p for p in self.get_params() if p.is_enum()]:
             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
+            if param.get_key() in ' '.join([p._type for p in children]): type_param = param
             # Use param if type param is unset
             if not type_param:
                 type_param = param
@@ -681,10 +686,10 @@ class Block(Element):
         """
         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['param'] = [p.export_data() for p in sorted(self.get_params(), key=str)]
+        if 'bus' in [a.get_type() for a in self.get_sinks()]:
             n['bus_sink'] = str(1)
-        if 'bus' in map(lambda a: a.get_type(), self.get_sources()):
+        if 'bus' in [a.get_type() for a in self.get_sources()]:
             n['bus_source'] = str(1)
         return n
 
@@ -773,12 +778,12 @@ class Block(Element):
             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()):
+        struct = [list(range(len(get_p())))]
+        if True in [isinstance(a.get_nports(), int) for a in 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)))
+                structlet.extend([a+last for a in range(j)])
                 last = structlet[-1] + 1
                 struct = [structlet]
         if bus_structure:
@@ -802,7 +807,7 @@ class Block(Element):
             for connect in elt.get_connections():
                 self.get_parent().remove_element(connect)
 
-        if ('bus' not in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0:
+        if ('bus' not in [a.get_type() for a in 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():
@@ -813,7 +818,7 @@ class Block(Element):
                 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()):
+        elif 'bus' in [a.get_type() for a in get_p()]:
             for elt in get_p_gui():
                 get_p().remove(elt)
             self.current_bus_structure[direc] = ''
diff --git a/grc/core/Config.py b/grc/core/Config.py
index ac38d9978c..400d5d365f 100644
--- a/grc/core/Config.py
+++ b/grc/core/Config.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import os
 from os.path import expanduser, normpath, expandvars, exists
 
diff --git a/grc/core/Connection.py b/grc/core/Connection.py
index 3aa32ef183..ddc6c0256f 100644
--- a/grc/core/Connection.py
+++ b/grc/core/Connection.py
@@ -17,6 +17,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
+from six.moves import range
+
 from . import Constants
 from .Element import Element
 from .utils import odict
@@ -51,8 +55,8 @@ class Connection(Element):
             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:
+
+        if (source.get_type() == 'bus') != (sink.get_type() == 'bus'):
             raise ValueError('busses must get with busses')
 
         if not len(source.get_associated_ports()) == len(sink.get_associated_ports()):
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 4f278bb22d..8a99f8b256 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -17,10 +17,14 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import os
-import numpy
 import stat
 
+import numpy
+import six
+
 # Data files
 DATA_DIR = os.path.dirname(__file__)
 FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd')
@@ -63,7 +67,7 @@ HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP
 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
 COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
 REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
-INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
+INT_TYPES = [int, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
              numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
 # Cast to tuple for isinstance, concat subtypes
 COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
@@ -129,6 +133,6 @@ for name, key, sizeof, color in CORE_TYPES:
     TYPE_TO_COLOR[key] = color
     TYPE_TO_SIZEOF[key] = sizeof
 
-for key, (sizeof, color) in ALIAS_TYPES.iteritems():
+for key, (sizeof, color) in six.iteritems(ALIAS_TYPES):
     TYPE_TO_COLOR[key] = color
     TYPE_TO_SIZEOF[key] = sizeof
diff --git a/grc/core/Element.py b/grc/core/Element.py
index 67c36e12b4..e697d293fb 100644
--- a/grc/core/Element.py
+++ b/grc/core/Element.py
@@ -22,7 +22,7 @@ class Element(object):
 
     def __init__(self, parent=None):
         self._parent = parent
-        self._error_messages = list()
+        self._error_messages = []
 
     ##################################################
     # Element Validation API
@@ -64,7 +64,9 @@ class Element(object):
             a list of error message strings
         """
         error_messages = list(self._error_messages)  # Make a copy
-        for child in filter(lambda c: c.get_enabled() and not c.get_bypassed(), self.get_children()):
+        for child in self.get_children():
+            if not child.get_enabled() or child.get_bypassed():
+                continue
             for msg in child.get_error_messages():
                 error_messages.append("{}:\n\t{}".format(child, msg.replace("\n", "\n\t")))
         return error_messages
diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py
index 949eecaa71..9edd4f24d8 100644
--- a/grc/core/FlowGraph.py
+++ b/grc/core/FlowGraph.py
@@ -15,12 +15,15 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import, print_function
+
 import imp
 import time
-from itertools import ifilter, chain
+import re
+from itertools import chain
 from operator import methodcaller, attrgetter
 
-import re
+from six.moves import filter
 
 from . import Messages
 from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
@@ -87,7 +90,7 @@ class FlowGraph(Element):
         Returns:
             a sorted list of variable blocks in order of dependency (indep -> dep)
         """
-        variables = filter(attrgetter('is_variable'), self.iter_enabled_blocks())
+        variables = list(filter(attrgetter('is_variable'), self.iter_enabled_blocks()))
         return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make'))
 
     def get_parameters(self):
@@ -97,15 +100,14 @@ class FlowGraph(Element):
         Returns:
             a list of parameterized variables
         """
-        parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks())
+        parameters = [b for b in self.iter_enabled_blocks() if _parameter_matcher.match(b.get_key())]
         return parameters
 
     def get_monitors(self):
         """
         Get a list of all ControlPort monitors
         """
-        monitors = filter(lambda b: _monitors_searcher.search(b.get_key()),
-                          self.iter_enabled_blocks())
+        monitors = [b for b in self.iter_enabled_blocks() if _monitors_searcher.search(b.get_key())]
         return monitors
 
     def get_python_modules(self):
@@ -115,7 +117,7 @@ class FlowGraph(Element):
                 yield block.get_id(), block.get_param('source_code').get_value()
 
     def get_bussink(self):
-        bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussink = [b for b in self.get_enabled_blocks() if _bussink_searcher.search(b.get_key())]
 
         for i in bussink:
             for j in i.get_params():
@@ -124,7 +126,7 @@ class FlowGraph(Element):
         return False
 
     def get_bussrc(self):
-        bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussrc = [b for b in self.get_enabled_blocks() if _bussrc_searcher.search(b.get_key())]
 
         for i in bussrc:
             for j in i.get_params():
@@ -133,18 +135,18 @@ class FlowGraph(Element):
         return False
 
     def get_bus_structure_sink(self):
-        bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussink = [b for b in self.get_enabled_blocks() if _bus_struct_sink_searcher.search(b.get_key())]
         return bussink
 
     def get_bus_structure_src(self):
-        bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussrc = [b for b in self.get_enabled_blocks() if _bus_struct_src_searcher.search(b.get_key())]
         return bussrc
 
     def iter_enabled_blocks(self):
         """
         Get an iterator of all blocks that are enabled and not bypassed.
         """
-        return ifilter(methodcaller('get_enabled'), self.blocks)
+        return filter(methodcaller('get_enabled'), self.blocks)
 
     def get_enabled_blocks(self):
         """
@@ -162,7 +164,7 @@ class FlowGraph(Element):
         Returns:
             a list of blocks
         """
-        return filter(methodcaller('get_bypassed'), self.blocks)
+        return list(filter(methodcaller('get_bypassed'), self.blocks))
 
     def get_enabled_connections(self):
         """
@@ -171,7 +173,7 @@ class FlowGraph(Element):
         Returns:
             a list of connections
         """
-        return filter(methodcaller('get_enabled'), self.connections)
+        return list(filter(methodcaller('get_enabled'), self.connections))
 
     def get_option(self, key):
         """
@@ -206,7 +208,7 @@ class FlowGraph(Element):
         options_block_count = self.blocks.count(self._options_block)
         if not options_block_count:
             self.blocks.append(self._options_block)
-        for i in range(options_block_count-1):
+        for _ in range(options_block_count-1):
             self.blocks.remove(self._options_block)
 
         return self.blocks + self.connections
@@ -229,14 +231,14 @@ class FlowGraph(Element):
         # Load imports
         for expr in self.get_imports():
             try:
-                exec expr in namespace
+                exec(expr, namespace)
             except:
                 pass
 
         for id, expr in self.get_python_modules():
             try:
                 module = imp.new_module(id)
-                exec expr in module.__dict__
+                exec(expr, module.__dict__)
                 namespace[id] = module
             except:
                 pass
@@ -333,15 +335,15 @@ class FlowGraph(Element):
         if element in self.blocks:
             # Remove block, remove all involved connections
             for port in element.get_ports():
-                map(self.remove_element, port.get_connections())
+                for connection in port.get_connections():
+                    self.remove_element(connection)
             self.blocks.remove(element)
 
         elif element in self.connections:
             if element.is_bus():
-                cons_list = []
-                for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()):
-                    cons_list.extend(i)
-                map(self.remove_element, cons_list)
+                for port in element.get_source().get_associated_ports():
+                    for connection in port.get_connections():
+                        self.remove_element(connection)
             self.connections.remove(element)
 
     ##############################################
@@ -484,21 +486,19 @@ class FlowGraph(Element):
                     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 'bus' in [a.get_type() for a in get_p_gui()]:
                     if len(get_p_gui()) > len(bus_structure):
-                        times = range(len(bus_structure), len(get_p_gui()))
+                        times = list(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()):
+                        if any(isinstance(a.get_nports(), int) for a in get_p()):
                             n['nports'] = str(1)
 
-                        times = range(len(get_p_gui()), len(bus_structure))
+                        times = list(range(len(get_p_gui()), len(bus_structure)))
 
                         for i in times:
                             n['key'] = str(len(get_p()))
@@ -507,8 +507,7 @@ class FlowGraph(Element):
                                 block=block, n=n, dir=direc)
                             get_p().append(port)
 
-                if 'bus' in map(lambda a: a.get_type(),
-                                block.get_sources_gui()):
+                if 'bus' in [a.get_type() for a in block.get_sources_gui()]:
                     for i in range(len(block.get_sources_gui())):
                         if len(block.get_sources_gui()[
                                    i].get_connections()) > 0:
diff --git a/grc/core/Messages.py b/grc/core/Messages.py
index 8daa12c33f..596b6197d8 100644
--- a/grc/core/Messages.py
+++ b/grc/core/Messages.py
@@ -16,9 +16,10 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 
+from __future__ import absolute_import
+
 import traceback
 import sys
-import os
 
 #  A list of functions that can receive a message.
 MESSENGERS_LIST = list()
diff --git a/grc/core/Param.py b/grc/core/Param.py
index 73d54b6aff..45f0187d27 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -17,20 +17,20 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import ast
 import weakref
 import re
 
+from six.moves import builtins, filter, map, range, zip
+
 from . import Constants
-from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
 from .Element import Element
 from .utils import odict
 
 # Blacklist certain ids, its not complete, but should help
-import __builtin__
-
-
-ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__)
+ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(builtins)
 try:
     from gnuradio import gr
     ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
@@ -64,7 +64,7 @@ def num_to_str(num):
                 return template.format(value / factor, symbol.strip())
         return template.format(value, '')
 
-    if isinstance(num, COMPLEX_TYPES):
+    if isinstance(num, Constants.COMPLEX_TYPES):
         num = complex(num)  # Cast to python complex
         if num == 0:
             return '0'
@@ -112,13 +112,13 @@ class Option(Element):
     # Access Opts
     ##############################################
     def get_opt_keys(self):
-        return self._opts.keys()
+        return list(self._opts.keys())
 
     def get_opt(self, key):
         return self._opts[key]
 
     def get_opts(self):
-        return self._opts.values()
+        return list(self._opts.values())
 
 
 class TemplateArg(object):
@@ -176,7 +176,8 @@ class Param(Element):
         # 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')):
+        for o_n in n.findall('option'):
+            option = Option(param=self, n=o_n)
             key = option.get_key()
             # Test against repeated keys
             if key in self.get_option_keys():
@@ -257,9 +258,9 @@ class Param(Element):
         t = self.get_type()
         if isinstance(e, bool):
             return str(e)
-        elif isinstance(e, COMPLEX_TYPES):
+        elif isinstance(e, Constants.COMPLEX_TYPES):
             dt_str = num_to_str(e)
-        elif isinstance(e, VECTOR_TYPES):
+        elif isinstance(e, Constants.VECTOR_TYPES):
             # Vector types
             if len(e) > 8:
                 # Large vectors use code
@@ -310,13 +311,10 @@ class Param(Element):
         if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()):
             return 'part'
         # Hide port controllers for type and nports
-        if self.get_key() in ' '.join(map(lambda p: ' '.join([p._type, p._nports]),
-                                          self.get_parent().get_ports())):
+        if self.get_key() in ' '.join([' '.join([p._type, p._nports]) for p in self.get_parent().get_ports()]):
             return 'part'
         # Hide port controllers for vlen, when == 1
-        if self.get_key() in ' '.join(map(
-            lambda p: p._vlen, self.get_parent().get_ports())
-        ):
+        if self.get_key() in ' '.join(p._vlen for p in self.get_parent().get_ports()):
             try:
                 if int(self.get_evaluated()) == 1:
                     return 'part'
@@ -339,7 +337,7 @@ class Param(Element):
         self._evaluated = None
         try:
             self._evaluated = self.evaluate()
-        except Exception, e:
+        except Exception as e:
             self.add_error_message(str(e))
 
     def get_evaluated(self):
@@ -372,21 +370,21 @@ class Param(Element):
             # Raise exception if python cannot evaluate this value
             try:
                 e = self.get_parent().get_parent().evaluate(v)
-            except Exception, e:
+            except Exception as e:
                 raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
             # Raise an exception if the data is invalid
             if t == 'raw':
                 return e
             elif t == 'complex':
-                if not isinstance(e, COMPLEX_TYPES):
+                if not isinstance(e, Constants.COMPLEX_TYPES):
                     raise Exception('Expression "{}" is invalid for type complex.'.format(str(e)))
                 return e
             elif t == 'real' or t == 'float':
-                if not isinstance(e, REAL_TYPES):
+                if not isinstance(e, Constants.REAL_TYPES):
                     raise Exception('Expression "{}" is invalid for type float.'.format(str(e)))
                 return e
             elif t == 'int':
-                if not isinstance(e, INT_TYPES):
+                if not isinstance(e, Constants.INT_TYPES):
                     raise Exception('Expression "{}" is invalid for type integer.'.format(str(e)))
                 return e
             elif t == 'hex':
@@ -407,28 +405,28 @@ class Param(Element):
             # Raise exception if python cannot evaluate this value
             try:
                 e = self.get_parent().get_parent().evaluate(v)
-            except Exception, e:
+            except Exception as e:
                 raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
             # Raise an exception if the data is invalid
             if t == 'complex_vector':
-                if not isinstance(e, VECTOR_TYPES):
+                if not isinstance(e, Constants.VECTOR_TYPES):
                     self._lisitify_flag = True
                     e = [e]
-                if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]):
+                if not all([isinstance(ei, Constants.COMPLEX_TYPES) for ei in e]):
                     raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e)))
                 return e
             elif t == 'real_vector' or t == 'float_vector':
-                if not isinstance(e, VECTOR_TYPES):
+                if not isinstance(e, Constants.VECTOR_TYPES):
                     self._lisitify_flag = True
                     e = [e]
-                if not all([isinstance(ei, REAL_TYPES) for ei in e]):
+                if not all([isinstance(ei, Constants.REAL_TYPES) for ei in e]):
                     raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e)))
                 return e
             elif t == 'int_vector':
-                if not isinstance(e, VECTOR_TYPES):
+                if not isinstance(e, Constants.VECTOR_TYPES):
                     self._lisitify_flag = True
                     e = [e]
-                if not all([isinstance(ei, INT_TYPES) for ei in e]):
+                if not all([isinstance(ei, Constants.INT_TYPES) for ei in e]):
                     raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e)))
                 return e
         #########################
@@ -545,7 +543,7 @@ class Param(Element):
                 for c in range(col_span):
                     self._hostage_cells.append((my_parent, (row+r, col+c)))
             # Avoid collisions
-            params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
+            params = [p for p in self.get_all_params('grid_pos') if p is not self]
             for param in params:
                 for parent, cell in param._hostage_cells:
                     if (parent, cell) in self._hostage_cells:
@@ -560,7 +558,7 @@ class Param(Element):
                 return ''
 
             # Get a list of all notebooks
-            notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks())
+            notebook_blocks = [b for b in self.get_parent().get_parent().get_enabled_blocks() if b.get_key() == 'notebook']
             # Check for notebook param syntax
             try:
                 notebook_id, page_index = map(str.strip, v.split(','))
@@ -568,7 +566,7 @@ class Param(Element):
                 raise Exception('Bad notebook page format.')
             # Check that the notebook id is valid
             try:
-                notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0]
+                notebook_block = [b for b in notebook_blocks if b.get_id() == notebook_id][0]
             except:
                 raise Exception('Notebook id "{}" is not an existing notebook id.'.format(notebook_id))
 
@@ -584,12 +582,12 @@ class Param(Element):
             # New namespace
             n = dict()
             try:
-                exec v in n
+                exec(v, n)
             except ImportError:
                 raise Exception('Import "{}" failed.'.format(v))
             except Exception:
                 raise Exception('Bad import syntax: "{}".'.format(v))
-            return filter(lambda k: str(k) != '__builtins__', n.keys())
+            return [k for k in list(n.keys()) if str(k) != '__builtins__']
 
         #########################
         else:
@@ -635,7 +633,10 @@ class Param(Element):
         Returns:
             a list of params
         """
-        return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])
+        params = []
+        for block in self.get_parent().get_parent().get_enabled_blocks():
+            params.extend(p for p in block.get_params() if p.get_type() == type)
+        return params
 
     def is_enum(self):
         return self._type == 'enum'
diff --git a/grc/core/ParseXML.py b/grc/core/ParseXML.py
index c9f6541ee7..d1306fcab4 100644
--- a/grc/core/ParseXML.py
+++ b/grc/core/ParseXML.py
@@ -17,8 +17,13 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 from lxml import etree
 
+import six
+from six.moves import map
+
 from .utils import odict
 
 xml_failures = {}
@@ -80,7 +85,9 @@ def from_file(xml_file):
     # Get the embedded instructions and build a dictionary item
     nested_data['_instructions'] = {}
     xml_instructions = xml.xpath('/processing-instruction()')
-    for inst in filter(lambda i: i.target == 'grc', xml_instructions):
+    for inst in xml_instructions:
+        if inst.target != 'grc':
+            continue
         nested_data['_instructions'] = odict(inst.attrib)
     return nested_data
 
@@ -100,13 +107,13 @@ def _from_file(xml):
         return odict({tag: xml.text or ''})  # store empty tags (text is None) as empty string
     nested_data = odict()
     for elem in xml:
-        key, value = _from_file(elem).items()[0]
+        key, value = list(_from_file(elem).items())[0]
         if key in nested_data:
             nested_data[key].append(value)
         else:
             nested_data[key] = [value]
     # Delistify if the length of values is 1
-    for key, values in nested_data.iteritems():
+    for key, values in six.iteritems(nested_data):
         if len(values) == 1:
             nested_data[key] = values[0]
 
@@ -127,7 +134,7 @@ def to_file(nested_data, xml_file):
     if instructions:
         xml_data += etree.tostring(etree.ProcessingInstruction(
             'grc', ' '.join(
-                "{0}='{1}'".format(*item) for item in instructions.iteritems())
+                "{0}='{1}'".format(*item) for item in six.iteritems(instructions))
         ), xml_declaration=True, pretty_print=True, encoding='utf-8')
     xml_data += etree.tostring(_to_file(nested_data)[0],
                                pretty_print=True, encoding='utf-8')
@@ -146,14 +153,14 @@ def _to_file(nested_data):
         the xml tree filled with child nodes
     """
     nodes = list()
-    for key, values in nested_data.iteritems():
+    for key, values in six.iteritems(nested_data):
         # Listify the values if not a list
         if not isinstance(values, (list, set, tuple)):
             values = [values]
         for value in values:
             node = etree.Element(key)
-            if isinstance(value, (str, unicode)):
-                node.text = unicode(value)
+            if isinstance(value, (str, six.text_type)):
+                node.text = six.text_type(value)
             else:
                 node.extend(_to_file(value))
             nodes.append(node)
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
index 02a625bbf4..25f415639a 100644
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@ -17,6 +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 __future__ import absolute_import
+from __future__ import print_function
 import os
 import sys
 
@@ -32,6 +34,9 @@ from .Port import Port
 from .Param import Param
 
 from .utils import odict, extract_docs
+import six
+from six.moves import map
+from six.moves import range
 
 
 class Platform(Element):
@@ -156,7 +161,7 @@ class Platform(Element):
                 # 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)
+                print('Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file), file=sys.stderr)
 
         self._docstring_extractor.finish()
         # self._docstring_extractor.wait()
@@ -168,7 +173,7 @@ class Platform(Element):
                 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)):
+                    for filename in sorted(f for f in filenames if f.endswith('.xml')):
                         yield os.path.join(dirpath, filename)
 
     def load_block_xml(self, xml_file):
@@ -181,7 +186,7 @@ class Platform(Element):
         block = self.Block(self._flow_graph, n)
         key = block.get_key()
         if key in self.blocks:
-            print >> sys.stderr, 'Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file)
+            print('Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
         else:  # Store the block
             self.blocks[key] = block
             self._blocks_n[key] = n
@@ -205,13 +210,13 @@ class Platform(Element):
 
         key = n.find('key')
         if not key:
-            print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file)
+            print('Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file), file=sys.stderr)
             return
         if key in self.domains:  # test against repeated keys
-            print >> sys.stderr, 'Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file)
+            print('Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
             return
 
-        #to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '')
+        # to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '')
         def to_bool(s, d):
             if s is not None:
                 return s.lower() not in ('false', 'off', '0', '')
@@ -223,7 +228,7 @@ class Platform(Element):
             tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 3 * chars_per_color, chars_per_color))
         except ValueError:
             if color:  # no color is okay, default set in GUI
-                print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key)
+                print('Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key), file=sys.stderr)
                 color = None
 
         self.domains[key] = dict(
@@ -235,9 +240,9 @@ class Platform(Element):
         for connection_n in n.findall('connection'):
             key = (connection_n.find('source_domain'), connection_n.find('sink_domain'))
             if not all(key):
-                print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file)
+                print('Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file), file=sys.stderr)
             elif key in self.connection_templates:
-                print >> sys.stderr, 'Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file)
+                print('Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file), file=sys.stderr)
             else:
                 self.connection_templates[key] = connection_n.find('make') or ''
 
@@ -256,11 +261,12 @@ class Platform(Element):
             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'))
+            for cat in cat_n.findall('cat'):
+                load_category(cat, parent)
             # Add blocks in this category
             for block_key in cat_n.findall('block'):
                 if block_key not in self.blocks:
-                    print >> sys.stderr, 'Warning: Block key "{}" not found when loading category tree.'.format(block_key)
+                    print('Warning: Block key "{}" not found when loading category tree.'.format(block_key), file=sys.stderr)
                     continue
                 block = self.blocks[block_key]
                 # If it exists, the block's category shall not be overridden by the xml tree
@@ -272,7 +278,7 @@ class Platform(Element):
             load_category(category_tree_n)
 
         # Add blocks to block tree
-        for block in self.blocks.itervalues():
+        for block in six.itervalues(self.blocks):
             # Blocks with empty categories are hidden
             if not block.get_category():
                 continue
@@ -280,7 +286,7 @@ class Platform(Element):
 
     def _save_docstring_extraction_result(self, key, docstrings):
         docs = {}
-        for match, docstring in docstrings.iteritems():
+        for match, docstring in six.iteritems(docstrings):
             if not docstring or match.endswith('_sptr'):
                 continue
             docstring = docstring.replace('\n\n', '\n').strip()
@@ -312,7 +318,7 @@ class Platform(Element):
         return self.FlowGraph(platform=self)
 
     def get_blocks(self):
-        return self.blocks.values()
+        return list(self.blocks.values())
 
     def get_new_block(self, flow_graph, key):
         return self.Block(flow_graph, n=self._blocks_n[key])
diff --git a/grc/core/Port.py b/grc/core/Port.py
index 6a8f484082..a24262da6b 100644
--- a/grc/core/Port.py
+++ b/grc/core/Port.py
@@ -17,7 +17,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-from .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN
+from __future__ import absolute_import
+
+from six.moves import filter
+
 from .Element import Element
 
 from . import Constants
@@ -47,13 +50,13 @@ def _get_source_from_virtual_source_port(vsp, traversed=[]):
     try:
         return _get_source_from_virtual_source_port(
             _get_source_from_virtual_sink_port(
-                filter(  # Get all virtual sinks with a matching stream id
+                list(filter(  # Get all virtual sinks with a matching stream id
                     lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
-                    filter(  # Get all enabled blocks that are also virtual sinks
+                    list(filter(  # Get all enabled blocks that are also virtual sinks
                         lambda b: b.is_virtual_sink(),
                         vsp.get_parent().get_parent().get_enabled_blocks(),
-                    ),
-                )[0].get_sinks()[0]
+                    )),
+                ))[0].get_sinks()[0]
             ), traversed + [vsp],
         )
     except:
@@ -87,10 +90,10 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]):
             _get_sink_from_virtual_source_port(
                 filter(  # Get all virtual source with a matching stream id
                     lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
-                    filter(  # Get all enabled blocks that are also virtual sinks
+                    list(filter(  # Get all enabled blocks that are also virtual sinks
                         lambda b: b.is_virtual_source(),
                         vsp.get_parent().get_parent().get_enabled_blocks(),
-                    ),
+                    )),
                 )[0].get_sources()[0]
             ), traversed + [vsp],
         )
@@ -113,10 +116,10 @@ class Port(Element):
         """
         self._n = n
         if n['type'] == 'message':
-            n['domain'] = GR_MESSAGE_DOMAIN
+            n['domain'] = Constants.GR_MESSAGE_DOMAIN
         if 'domain' not in n:
-            n['domain'] = DEFAULT_DOMAIN
-        elif n['domain'] == GR_MESSAGE_DOMAIN:
+            n['domain'] = Constants.DEFAULT_DOMAIN
+        elif n['domain'] == Constants.GR_MESSAGE_DOMAIN:
             n['key'] = n['name']
             n['type'] = 'message'  # For port color
         if n['type'] == 'msg':
@@ -147,7 +150,7 @@ class Port(Element):
             return 'Sink - {}({})'.format(self.get_name(), self.get_key())
 
     def get_types(self):
-        return Constants.TYPE_TO_SIZEOF.keys()
+        return list(Constants.TYPE_TO_SIZEOF.keys())
 
     def is_type_empty(self):
         return not self._n['type']
@@ -189,11 +192,11 @@ class Port(Element):
 
         # 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
+        if self._domain == Constants.GR_STREAM_DOMAIN and type_ == "message":
+            self._domain = Constants.GR_MESSAGE_DOMAIN
             self._key = self._name
-        if self._domain == GR_MESSAGE_DOMAIN and type_ != "message":
-            self._domain = GR_STREAM_DOMAIN
+        if self._domain == Constants.GR_MESSAGE_DOMAIN and type_ != "message":
+            self._domain = Constants.GR_STREAM_DOMAIN
             self._key = '0'  # Is rectified in rewrite()
 
     def resolve_virtual_source(self):
@@ -341,7 +344,7 @@ class Port(Element):
     def get_name(self):
         number = ''
         if self.get_type() == 'bus':
-            busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui())
+            busses = [a for a in self.get_parent().get_ports_gui() if a._dir == self._dir]
             number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports()))
         return self._name + number
 
@@ -373,7 +376,7 @@ class Port(Element):
             a list of connection objects
         """
         connections = self.get_parent().get_parent().connections
-        connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections)
+        connections = [c for c in connections if c.get_source() is self or c.get_sink() is self]
         return connections
 
     def get_enabled_connections(self):
@@ -383,7 +386,7 @@ class Port(Element):
         Returns:
             a list of connection objects
         """
-        return filter(lambda c: c.get_enabled(), self.get_connections())
+        return [c for c in self.get_connections() if c.get_enabled()]
 
     def get_associated_ports(self):
         if not self.get_type() == 'bus':
@@ -400,5 +403,5 @@ class Port(Element):
             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)
+                ports = [a for a in ports if ports.index(a) in bus_structure[bus_index]]
             return ports
diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py
index 3723005576..c673c5b005 100644
--- a/grc/core/generator/FlowGraphProxy.py
+++ b/grc/core/generator/FlowGraphProxy.py
@@ -16,6 +16,10 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 
+from __future__ import absolute_import
+from six.moves import range
+
+
 class FlowGraphProxy(object):
 
     def __init__(self, fg):
@@ -34,7 +38,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of dicts with: type, label, vlen, size, optional
         """
-        return filter(lambda p: p['type'] != "message", self.get_hier_block_io(direction))
+        return [p for p in self.get_hier_block_io(direction) if p['type'] != "message"]
 
     def get_hier_block_message_io(self, direction):
         """
@@ -46,7 +50,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of dicts with: type, label, vlen, size, optional
         """
-        return filter(lambda p: p['type'] == "message", self.get_hier_block_io(direction))
+        return [p for p in self.get_hier_block_io(direction) if p['type'] == "message"]
 
     def get_hier_block_io(self, direction):
         """
@@ -71,7 +75,7 @@ class FlowGraphProxy(object):
             }
             num_ports = pad.get_param('num_streams').get_evaluated()
             if num_ports > 1:
-                for i in xrange(num_ports):
+                for i in range(num_ports):
                     clone = master.copy()
                     clone['label'] += str(i)
                     ports.append(clone)
@@ -86,7 +90,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of pad source blocks in this flow graph
         """
-        pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks())
+        pads = [b for b in self.get_enabled_blocks() if b.get_key() == 'pad_source']
         return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
 
     def get_pad_sinks(self):
@@ -96,7 +100,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of pad sink blocks in this flow graph
         """
-        pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks())
+        pads = [b for b in self.get_enabled_blocks() if b.get_key() == 'pad_sink']
         return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
 
     def get_pad_port_global_key(self, port):
@@ -123,4 +127,4 @@ class FlowGraphProxy(object):
                 # assuming we have either only sources or sinks
                 if not is_message_pad:
                     key_offset += len(pad.get_ports())
-        return -1
\ No newline at end of file
+        return -1
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index 0d0ca6f55f..c9b065372d 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -16,9 +16,11 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 
+from __future__ import absolute_import
 import codecs
 import os
 import tempfile
+import operator
 
 from Cheetah.Template import Template
 
@@ -85,14 +87,15 @@ class TopBlockGenerator(object):
     def write(self):
         """generate output and write it to files"""
         # Do throttle warning
-        throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks())
+        throttling_blocks = [b for b in self._flow_graph.get_enabled_blocks()
+                             if b.throtteling()]
         if not throttling_blocks and not self._generate_options.startswith('hb'):
             Messages.send_warning("This flow graph may not have flow control: "
                                   "no audio or RF hardware blocks found. "
                                   "Add a Misc->Throttle block to your flow "
                                   "graph to avoid CPU congestion.")
         if len(throttling_blocks) > 1:
-            keys = set(map(lambda b: b.get_key(), throttling_blocks))
+            keys = set([b.get_key() for b in throttling_blocks])
             if len(keys) > 1 and 'blocks_throttle' in keys:
                 Messages.send_warning("This flow graph contains a throttle "
                                       "block and another rate limiting block, "
@@ -139,15 +142,15 @@ class TopBlockGenerator(object):
             return code
 
         blocks = expr_utils.sort_objects(
-            filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks),
-            lambda b: b.get_id(), _get_block_sort_text
+            [b for b in fg.blocks if b.get_enabled() and not b.get_bypassed()],
+            operator.methodcaller('get_id'), _get_block_sort_text
         )
         deprecated_block_keys = set(block.get_name() for block in blocks if block.is_deprecated)
         for key in deprecated_block_keys:
             Messages.send_warning("The block {!r} is deprecated.".format(key))
 
         # List of regular blocks (all blocks minus the special ones)
-        blocks = filter(lambda b: b not in (imports + parameters), blocks)
+        blocks = [b for b in blocks if b not in imports and b not in parameters]
 
         for block in blocks:
             key = block.get_key()
@@ -162,10 +165,10 @@ class TopBlockGenerator(object):
         # Filter out virtual sink connections
         def cf(c):
             return not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink())
-        connections = filter(cf, fg.get_enabled_connections())
+        connections = [con for con in fg.get_enabled_connections() if cf(con)]
 
         # Get the virtual blocks and resolve their connections
-        virtual = filter(lambda c: c.get_source().get_parent().is_virtual_source(), connections)
+        virtual = [c for c in connections if c.get_source().get_parent().is_virtual_source()]
         for connection in virtual:
             source = connection.get_source().resolve_virtual_source()
             sink = connection.get_sink()
@@ -183,7 +186,7 @@ class TopBlockGenerator(object):
         for block in bypassed_blocks:
             # Get the upstream connection (off of the sink ports)
             # Use *connections* not get_connections()
-            source_connection = filter(lambda c: c.get_sink() == block.get_sinks()[0], connections)
+            source_connection = [c for c in connections if c.get_sink() == block.get_sinks()[0]]
             # The source connection should never have more than one element.
             assert (len(source_connection) == 1)
 
@@ -191,7 +194,7 @@ class TopBlockGenerator(object):
             source_port = source_connection[0].get_source()
 
             # Loop through all the downstream connections
-            for sink in filter(lambda c: c.get_source() == block.get_sources()[0], connections):
+            for sink in (c for c in connections if c.get_source() == block.get_sources()[0]):
                 if not sink.get_enabled():
                     # Ignore disabled connections
                     continue
@@ -210,7 +213,8 @@ class TopBlockGenerator(object):
         ))
 
         connection_templates = fg.get_parent().connection_templates
-        msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections())
+        msgs = [c for c in fg.get_enabled_connections() if c.is_msg()]
+
         # List of variable names
         var_ids = [var.get_id() for var in parameters + variables]
         # Prepend self.
@@ -222,7 +226,7 @@ class TopBlockGenerator(object):
             ]
         # Map var id to callbacks
         var_id2cbs = dict([
-            (var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks))
+            (var_id, [c for c in callbacks if expr_utils.get_variable_dependencies(c, [var_id])])
             for var_id in var_ids
         ])
         # Load the namespace
@@ -290,8 +294,8 @@ class HierBlockGenerator(TopBlockGenerator):
         parameters = self._flow_graph.get_parameters()
 
         def var_or_value(name):
-            if name in map(lambda p: p.get_id(), parameters):
-                return "$"+name
+            if name in (p.get_id() for p in parameters):
+                return "$" + name
             return name
 
         # Build the nested data
diff --git a/grc/core/generator/__init__.py b/grc/core/generator/__init__.py
index f44b94a85d..98f410c8d4 100644
--- a/grc/core/generator/__init__.py
+++ b/grc/core/generator/__init__.py
@@ -15,4 +15,5 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
-from Generator import Generator
+from __future__ import absolute_import
+from .Generator import Generator
diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py
index 6b23da2723..0d84f7131d 100644
--- a/grc/core/utils/__init__.py
+++ b/grc/core/utils/__init__.py
@@ -15,8 +15,10 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
-import expr_utils
-import epy_block_io
-import extract_docs
+from __future__ import absolute_import
 
-from odict import odict
+from . import expr_utils
+from . import epy_block_io
+from . import extract_docs
+
+from .odict import odict
diff --git a/grc/core/utils/complexity.py b/grc/core/utils/complexity.py
index baa8040db4..d72db9b3dd 100644
--- a/grc/core/utils/complexity.py
+++ b/grc/core/utils/complexity.py
@@ -8,8 +8,8 @@ def calculate_flowgraph_complexity(flowgraph):
             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())
+        sink_list = [c for c in block.get_sinks() if not c.get_optional()]
+        source_list = [c for c in block.get_sources() if not c.get_optional()]
         sinks = float(len(sink_list))
         sources = float(len(source_list))
         base = max(min(sinks, sources), 1)
@@ -22,14 +22,15 @@ def calculate_flowgraph_complexity(flowgraph):
             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)
+        sink_multi = max(float(sum(len(c.get_connections()) for c in sink_list) / max(sinks, 1.0)), 1.0)
+        source_multi = max(float(sum(len(c.get_connections()) for c in source_list) / max(sources, 1.0)), 1.0)
+        dbal += base * multi * sink_multi * source_multi
 
     blocks = float(len(flowgraph.blocks))
     connections = float(len(flowgraph.connections))
     elements = blocks + connections
-    disabled_connections = len(filter(lambda c: not c.get_enabled(), flowgraph.connections))
+    disabled_connections = sum(not c.get_enabled() for c in flowgraph.connections)
+
     variables = elements - blocks - connections
     enabled = float(len(flowgraph.get_enabled_blocks()))
 
diff --git a/grc/core/utils/epy_block_io.py b/grc/core/utils/epy_block_io.py
index 76b50051db..7a2006a833 100644
--- a/grc/core/utils/epy_block_io.py
+++ b/grc/core/utils/epy_block_io.py
@@ -1,7 +1,12 @@
 
+from __future__ import absolute_import
+
 import inspect
 import collections
 
+import six
+from six.moves import zip
+
 
 TYPE_MAP = {
     'complex64': 'complex', 'complex': 'complex',
@@ -31,10 +36,10 @@ def _ports(sigs, msgs):
 def _find_block_class(source_code, cls):
     ns = {}
     try:
-        exec source_code in ns
+        exec(source_code, ns)
     except Exception as e:
         raise ValueError("Can't interpret source code: " + str(e))
-    for var in ns.itervalues():
+    for var in six.itervalues(ns):
         if inspect.isclass(var) and issubclass(var, cls):
             return var
     raise ValueError('No python block class found in code')
@@ -52,7 +57,7 @@ def extract(cls):
 
     spec = inspect.getargspec(cls.__init__)
     init_args = spec.args[1:]
-    defaults = map(repr, spec.defaults or ())
+    defaults = [repr(arg) for arg in (spec.defaults or ())]
     doc = cls.__doc__ or cls.__init__.__doc__ or ''
     cls_name = cls.__name__
 
diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py
index 66911757d6..0577f06a75 100644
--- a/grc/core/utils/expr_utils.py
+++ b/grc/core/utils/expr_utils.py
@@ -17,7 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import string
+
+import six
+
 VAR_CHARS = string.letters + string.digits + '_'
 
 
@@ -50,7 +55,7 @@ class graph(object):
         self._graph[src_node_key].remove(dest_node_key)
 
     def get_nodes(self):
-        return self._graph.keys()
+        return list(self._graph.keys())
 
     def get_edges(self, node_key):
         return self._graph[node_key]
@@ -85,7 +90,7 @@ def expr_split(expr):
             toks.append(char)
             tok = ''
     toks.append(tok)
-    return filter(lambda t: t, toks)
+    return [t for t in toks if t]
 
 
 def expr_replace(expr, replace_dict):
@@ -101,7 +106,7 @@ def expr_replace(expr, replace_dict):
     """
     expr_splits = expr_split(expr)
     for i, es in enumerate(expr_splits):
-        if es in replace_dict.keys():
+        if es in list(replace_dict.keys()):
             expr_splits[i] = replace_dict[es]
     return ''.join(expr_splits)
 
@@ -118,7 +123,7 @@ def get_variable_dependencies(expr, vars):
         a subset of vars used in the expression
     """
     expr_toks = expr_split(expr)
-    return set(filter(lambda v: v in expr_toks, vars))
+    return set(v for v in vars if v in expr_toks)
 
 
 def get_graph(exprs):
@@ -131,12 +136,12 @@ def get_graph(exprs):
     Returns:
         a graph of variable deps
     """
-    vars = exprs.keys()
+    vars = list(exprs.keys())
     # Get dependencies for each expression, load into graph
     var_graph = graph()
     for var in vars:
         var_graph.add_node(var)
-    for var, expr in exprs.iteritems():
+    for var, expr in six.iteritems(exprs):
         for dep in get_variable_dependencies(expr, vars):
             if dep != var:
                 var_graph.add_edge(dep, var)
@@ -159,7 +164,7 @@ def sort_variables(exprs):
     # Determine dependency order
     while var_graph.get_nodes():
         # Get a list of nodes with no edges
-        indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes())
+        indep_vars = [var for var in var_graph.get_nodes() if not var_graph.get_edges(var)]
         if not indep_vars:
             raise Exception('circular dependency caught in sort_variables')
         # Add the indep vars to the end of the list
@@ -193,4 +198,4 @@ def sort_objects(objects, get_id, get_expr):
 
 if __name__ == '__main__':
     for i in sort_variables({'x': '1', 'y': 'x+1', 'a': 'x+y', 'b': 'y+1', 'c': 'a+b+x+y'}):
-        print i
+        print(i)
diff --git a/grc/core/utils/extract_docs.py b/grc/core/utils/extract_docs.py
index a6e0bc971e..cff8a81099 100644
--- a/grc/core/utils/extract_docs.py
+++ b/grc/core/utils/extract_docs.py
@@ -17,15 +17,19 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import sys
 import re
 import subprocess
 import threading
 import json
-import Queue
 import random
 import itertools
 
+import six
+from six.moves import queue, filter, range
+
 
 ###############################################################################
 # The docstring extraction
@@ -124,7 +128,7 @@ class SubprocessLoader(object):
         self.callback_query_result = callback_query_result
         self.callback_finished = callback_finished or (lambda: None)
 
-        self._queue = Queue.Queue()
+        self._queue = queue.Queue()
         self._thread = None
         self._worker = None
         self._shutdown = threading.Event()
@@ -157,14 +161,14 @@ class SubprocessLoader(object):
                 cmd, args = self._last_cmd
                 if cmd == 'query':
                     msg += " (crashed while loading {0!r})".format(args[0])
-                print >> sys.stderr, msg
+                print(msg, file=sys.stderr)
                 continue  # restart
             else:
                 break  # normal termination, return
             finally:
                 self._worker.terminate()
         else:
-            print >> sys.stderr, "Warning: docstring loader crashed too often"
+            print("Warning: docstring loader crashed too often", file=sys.stderr)
         self._thread = None
         self._worker = None
         self.callback_finished()
@@ -203,9 +207,9 @@ class SubprocessLoader(object):
             key, docs = args
             self.callback_query_result(key, docs)
         elif cmd == 'error':
-            print args
+            print(args)
         else:
-            print >> sys.stderr, "Unknown response:", cmd, args
+            print("Unknown response:", cmd, args, file=sys.stderr)
 
     def query(self, key, imports=None, make=None):
         """ Request docstring extraction for a certain key """
@@ -270,12 +274,12 @@ if __name__ == '__worker__':
 
 elif __name__ == '__main__':
     def callback(key, docs):
-        print key
-        for match, doc in docs.iteritems():
-            print '-->', match
-            print doc.strip()
-            print
-        print
+        print(key)
+        for match, doc in six.iteritems(docs):
+            print('-->', match)
+            print(doc.strip())
+            print()
+        print()
 
     r = SubprocessLoader(callback)
 
diff --git a/grc/core/utils/odict.py b/grc/core/utils/odict.py
index 20970e947c..38f898a97f 100644
--- a/grc/core/utils/odict.py
+++ b/grc/core/utils/odict.py
@@ -17,6 +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 __future__ import absolute_import
+
 from UserDict import DictMixin
 
 
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 492bf8de9c..9c3e9246d5 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -18,6 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
 
+from __future__ import absolute_import, print_function
+
 import os
 import subprocess
 
@@ -114,7 +116,7 @@ class ActionHandler:
         ##################################################
         if action == Actions.APPLICATION_INITIALIZE:
             if not self.init_file_paths:
-                self.init_file_paths = filter(os.path.exists, Preferences.get_open_files())
+                self.init_file_paths = list(filter(os.path.exists, Preferences.get_open_files()))
             if not self.init_file_paths: self.init_file_paths = ['']
             for file_path in self.init_file_paths:
                 if file_path: main.new_page(file_path) #load pages from file paths
@@ -603,7 +605,7 @@ class ActionHandler:
                 try:
                     page.process.kill()
                 except:
-                    print "could not kill process: %d" % page.process.pid
+                    print("could not kill process: %d" % page.process.pid)
         elif action == Actions.PAGE_CHANGE:  # pass and run the global actions
             pass
         elif action == Actions.RELOAD_BLOCKS:
@@ -645,7 +647,7 @@ class ActionHandler:
                              shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
         else:
-            print '!!! Action "%s" not handled !!!' % action
+            print('!!! Action "%s" not handled !!!' % action)
         ##################################################
         # Global Actions for all States
         ##################################################
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index d0e114293f..3a51e80918 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -17,13 +17,17 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
+import six
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import Gdk
 from gi.repository import GObject
 
-import Preferences
+from . import Preferences
 
 NO_MODS_MASK = 0
 
@@ -47,7 +51,6 @@ def handle_key_press(event):
     Returns:
         true if handled
     """
-    _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK)
     # extract the key value and the consumed modifiers
     _unused, keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
         event.hardware_keycode, event.get_state(), event.group)
@@ -80,13 +83,16 @@ class _ActionBase(object):
     Register actions and keypresses with this module.
     """
     def __init__(self, label, keypresses):
+        global _used_mods_mask
+
         _all_actions_list.append(self)
         for i in range(len(keypresses)/2):
             keyval, mod_mask = keypresses[i*2:(i+1)*2]
             # register this keypress
-            if _actions_keypress_dict.has_key((keyval, mod_mask)):
+            if (keyval, mod_mask) in _actions_keypress_dict:
                 raise KeyError('keyval/mod_mask pair already registered "%s"' % str((keyval, mod_mask)))
             _actions_keypress_dict[(keyval, mod_mask)] = self
+            _used_mods_mask |= mod_mask
             # set the accelerator group, and accelerator path
             # register the key name and mod mask with the accelerator path
             if label is None:
@@ -102,7 +108,7 @@ class _ActionBase(object):
         The string representation should be the name of the action id.
         Try to find the action id for this action by searching this module.
         """
-        for name, value in globals().iteritems():
+        for name, value in six.iteritems(globals()):
             if value == self:
                 return name
         return self.get_name()
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index c8631aa298..0c18836c4e 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
@@ -206,7 +207,7 @@ class SubMenuCreator(object):
 
     def _fill_flow_graph_recent_submenu(self, action):
         """menu showing recent flow-graphs"""
-        import Preferences
+        from . import Preferences
         menu = Gtk.Menu()
         recent_files = Preferences.get_recent_files()
         if len(recent_files) > 0:
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 1b90cf69a0..a6c31cd473 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import math
 import gi
 gi.require_version('Gtk', '3.0')
@@ -150,7 +151,7 @@ class Block(Element, _Block):
         W = self.label_width + 2 * BLOCK_LABEL_PADDING
 
         def get_min_height_for_ports():
-            visible_ports = filter(lambda p: not p.get_hide(), ports)
+            visible_ports = [p for p in ports if not p.get_hide()]
             min_height = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION
             if visible_ports:
                 min_height -= ports[0].H
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index 829ddfed68..26086f58e9 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -17,6 +17,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+import six
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
@@ -33,7 +36,7 @@ def _format_doc(doc):
     docs = []
     if doc.get(''):
         docs += doc.pop('').splitlines() + ['']
-    for block_name, docstring in doc.iteritems():
+    for block_name, docstring in six.iteritems(doc):
         docs.append('--- {0} ---'.format(block_name))
         docs += docstring.splitlines()
         docs.append('')
@@ -142,8 +145,9 @@ class BlockTreeWindow(Gtk.VBox):
         if categories is None:
             categories = self._categories
 
-        if isinstance(category, (str, unicode)): category = category.split('/')
-        category = tuple(filter(lambda x: x, category))  # tuple is hashable
+        if isinstance(category, (str, six.text_type)):
+            category = category.split('/')
+        category = tuple(x for x in category if x)  # tuple is hashable
         # add category and all sub categories
         for i, cat_name in enumerate(category):
             sub_category = category[:i+1]
@@ -210,8 +214,8 @@ class BlockTreeWindow(Gtk.VBox):
             self.treeview.set_model(self.treestore)
             self.treeview.collapse_all()
         else:
-            matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(),
-                                     self.platform.blocks.values())
+            matching_blocks = [b for b in list(self.platform.blocks.values())
+                               if key in b.get_key().lower() or key in b.get_name().lower()]
 
             self.treestore_search.clear()
             self._categories_search = {tuple(): None}
diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py
index a03a7bcade..b2ed55b711 100644
--- a/grc/gui/Colors.py
+++ b/grc/gui/Colors.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 
 
 def get_color(color_code):
diff --git a/grc/gui/Config.py b/grc/gui/Config.py
index 9b0c5d4afe..b6556ad724 100644
--- a/grc/gui/Config.py
+++ b/grc/gui/Config.py
@@ -17,8 +17,11 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import sys
 import os
+
 from ..core.Config import Config as _Config
 from . import Constants
 
@@ -57,7 +60,7 @@ class Config(_Config):
                 raise Exception()
             return value
         except:
-            print >> sys.stderr, "Error: invalid 'canvas_default_size' setting."
+            print("Error: invalid 'canvas_default_size' setting.", file=sys.stderr)
             return Constants.DEFAULT_CANVAS_SIZE_DEFAULT
 
     @property
@@ -69,6 +72,6 @@ class Config(_Config):
                 raise Exception()
         except:
             font_size = Constants.DEFAULT_FONT_SIZE
-            print >> sys.stderr, "Error: invalid 'canvas_font_size' setting."
+            print("Error: invalid 'canvas_font_size' setting.", file=sys.stderr)
 
         return font_size
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index 46414c94c8..8953ca0183 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -17,16 +17,14 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
+from __future__ import absolute_import
 
+from six.moves import map
 
-import Colors
-import Utils
-from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
-from Element import Element
+from . import Colors
+from . import Utils
+from .Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
+from .Element import Element
 
 from ..core.Constants import GR_MESSAGE_DOMAIN
 from ..core.Connection import Connection as _Connection
@@ -130,7 +128,7 @@ class Connection(Element, _Connection):
             #points[0][0] -> source connector should not be in the direction of source
             if Utils.get_angle_from_coordinates(points[0][0], (x1, y1)) == source.get_connector_direction(): points.reverse()
             #create 3-line connector
-            p1, p2 = map(int, points[0][0]), map(int, points[0][1])
+            p1, p2 = list(map(int, points[0][0])), list(map(int, points[0][1]))
             self.add_line((x1, y1), p1)
             self.add_line(p1, p2)
             self.add_line((x2, y2), p2)
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index 55739a7c03..8bb15acc09 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index 953373ee24..8f0f60d764 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index d1e0e78634..33c669c99f 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 from gi.repository import Gtk, Gdk, GObject
 
 
diff --git a/grc/gui/Element.py b/grc/gui/Element.py
index 30c0f5dba7..4e88df375f 100644
--- a/grc/gui/Element.py
+++ b/grc/gui/Element.py
@@ -17,10 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-from Constants import LINE_SELECT_SENSITIVITY
-from Constants import POSSIBLE_ROTATIONS
+from __future__ import absolute_import
+from .Constants import LINE_SELECT_SENSITIVITY
+from .Constants import POSSIBLE_ROTATIONS
 
 import gi
+from six.moves import zip
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import Gdk
diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py
index 13a1cfd942..a8c67986e5 100644
--- a/grc/gui/Executor.py
+++ b/grc/gui/Executor.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import os
 import threading
 import shlex
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index 63d4a397a8..3ee715dac6 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -17,18 +17,19 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import GObject
 
-from Dialogs import MessageDialogHelper
-from Constants import \
+from .Dialogs import MessageDialogHelper
+from .Constants import \
     DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, TEXT_FILE_EXTENSION, \
     NEW_FLOGRAPH_TITLE
-import Preferences
+from . import Preferences
 from os import path
-import Utils
+from . import Utils
 
 ##################################################
 # Constants
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index 802c54f7a7..50e146b4db 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -17,16 +17,20 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import functools
 import random
 from distutils.spawn import find_executable
 from itertools import chain, count
 from operator import methodcaller
 
+import six
+from six.moves import filter
+
 from gi.repository import GObject
 
-from . import Actions, Colors, Constants, Utils, Bars, Dialogs
-from .Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
+from . import Actions, Colors, Utils, Bars, Dialogs
 from .Element import Element
 from .external_editor import ExternalEditor
 
@@ -179,10 +183,10 @@ class FlowGraph(Element, _Flowgraph):
             x_min = min(x, x_min)
             y_min = min(y, y_min)
         #get connections between selected blocks
-        connections = filter(
+        connections = list(filter(
             lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks,
             self.connections,
-        )
+        ))
         clipboard = (
             (x_min, y_min),
             [block.export_data() for block in blocks],
@@ -222,7 +226,7 @@ class FlowGraph(Element, _Flowgraph):
                 block.get_param('_io_cache').set_value(params.pop('_io_cache'))
                 block.get_param('_source_code').set_value(params.pop('_source_code'))
                 block.rewrite()  # this creates the other params
-            for param_key, param_value in params.iteritems():
+            for param_key, param_value in six.iteritems(params):
                 #setup id parameter
                 if param_key == 'id':
                     old_id2block[param_value] = block
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index ce16074c81..3236768969 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -17,6 +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 __future__ import absolute_import
+
 import os
 
 import gi
@@ -270,7 +272,7 @@ class MainWindow(Gtk.Window):
         Returns:
             true if all closed
         """
-        open_files = filter(lambda file: file, self._get_files()) #filter blank files
+        open_files = [file for file in self._get_files() if file] #filter blank files
         open_file = self.current_page.file_path
         #close each page
         for page in sorted(self.get_pages(), key=lambda p: p.saved):
@@ -416,7 +418,7 @@ class MainWindow(Gtk.Window):
         Returns:
             list of file paths
         """
-        return map(lambda page: page.file_path, self.get_pages())
+        return [page.file_path for page in self.get_pages()]
 
     def get_pages(self):
         """
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index bcfb4d87fe..757dcbc0f8 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import os
 
 from gi.repository import Gtk, Gdk, GObject
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index 0f88015256..137c5e057b 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 from . import Utils, Constants
 
 from . import ParamWidgets
diff --git a/grc/gui/ParamWidgets.py b/grc/gui/ParamWidgets.py
index 2fd6ccd1dc..e0979e19f3 100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import os
 
 from gi.repository import Gtk, Gdk
diff --git a/grc/gui/ParserErrorsDialog.py b/grc/gui/ParserErrorsDialog.py
index f49e6923e5..28cc8ece0c 100644
--- a/grc/gui/ParserErrorsDialog.py
+++ b/grc/gui/ParserErrorsDialog.py
@@ -17,12 +17,16 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
+import six
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import GObject
 
-from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT
+from .Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT
 
 
 class ParserErrorsDialog(Gtk.Dialog):
@@ -72,7 +76,7 @@ class ParserErrorsDialog(Gtk.Dialog):
         """set up data model"""
         self.tree_store.clear()
         self._error_logs = error_logs
-        for filename, errors in error_logs.iteritems():
+        for filename, errors in six.iteritems(error_logs):
             parent = self.tree_store.append(None, [str(filename)])
             try:
                 with open(filename, 'r') as fp:
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index 500df1cce4..997e96ab59 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -17,6 +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 __future__ import absolute_import, print_function
+
 import os
 import sys
 
@@ -58,7 +60,7 @@ class Platform(Element, _Platform):
                 import shutil
                 shutil.move(old_gui_prefs_file, gui_prefs_file)
             except Exception as e:
-                print >> sys.stderr, e
+                print(e, file=sys.stderr)
 
     ##############################################
     # Constructors
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index fb1cd678cd..0fa35573c1 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import math
 import gi
 gi.require_version('Gtk', '3.0')
@@ -68,7 +69,7 @@ class Port(_Port, Element):
         #get all sibling ports
         ports = self.get_parent().get_sources_gui() \
             if self.is_source else self.get_parent().get_sinks_gui()
-        ports = filter(lambda p: not p.get_hide(), ports)
+        ports = [p for p in ports if not p.get_hide()]
         #get the max width
         self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH])
         W = self.W if not self._label_hidden() else PORT_LABEL_HIDDEN_WIDTH
@@ -79,16 +80,15 @@ class Port(_Port, Element):
             if hasattr(self, '_connector_length'):
                 del self._connector_length
             return
-        length = len(filter(lambda p: not p.get_hide(), ports))
         #reverse the order of ports for these rotations
         if rotation in (180, 270):
-            index = length-index-1
+            index = len(ports)-index-1
 
         port_separation = PORT_SEPARATION \
             if not self.get_parent().has_busses[self.is_source] \
             else max([port.H for port in ports]) + PORT_SPACING
 
-        offset = (self.get_parent().H - (length-1)*port_separation - self.H)/2
+        offset = (self.get_parent().H - (len(ports)-1)*port_separation - self.H)/2
         #create areas and connector coordinates
         if (self.is_sink and rotation == 0) or (self.is_source and rotation == 180):
             x = -W
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
index 5fbdfe927a..8756a7ab23 100644
--- a/grc/gui/Preferences.py
+++ b/grc/gui/Preferences.py
@@ -17,9 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import os
 import sys
-import ConfigParser
+
+from six.moves import configparser
 
 
 HEADER = """\
@@ -31,7 +34,7 @@ HEADER = """\
 """
 
 _platform = None
-_config_parser = ConfigParser.SafeConfigParser()
+_config_parser = configparser.SafeConfigParser()
 
 
 def file_extension():
@@ -45,12 +48,12 @@ def load(platform):
     for section in ['main', 'files_open', 'files_recent']:
         try:
             _config_parser.add_section(section)
-        except Exception, e:
-             print e
+        except Exception as e:
+            print(e)
     try:
         _config_parser.read(_platform.get_prefs_file())
     except Exception as err:
-        print >> sys.stderr, err
+        print(err, file=sys.stderr)
 
 
 def save():
@@ -59,7 +62,7 @@ def save():
             fp.write(HEADER)
             _config_parser.write(fp)
     except Exception as err:
-        print >> sys.stderr, err
+        print(err, file=sys.stderr)
 
 
 def entry(key, value=None, default=None):
@@ -74,7 +77,7 @@ def entry(key, value=None, default=None):
         }.get(_type, _config_parser.get)
         try:
             result = getter('main', key)
-        except ConfigParser.Error:
+        except configparser.Error:
             result = _type() if default is None else default
     return result
 
@@ -106,7 +109,7 @@ def get_file_list(key):
     try:
         files = [value for name, value in _config_parser.items(key)
                  if name.startswith('%s_' % key)]
-    except ConfigParser.Error:
+    except configparser.Error:
         files = []
     return files
 
@@ -121,7 +124,7 @@ def set_open_files(files):
 
 def get_recent_files():
     """ Gets recent files, removes any that do not exist and re-saves it """
-    files = filter(os.path.exists, get_file_list('files_recent'))
+    files = list(filter(os.path.exists, get_file_list('files_recent')))
     set_recent_files(files)
     return files
 
diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py
index d6b64944cc..f87ca6e7c1 100644
--- a/grc/gui/PropsDialog.py
+++ b/grc/gui/PropsDialog.py
@@ -17,10 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 from gi.repository import Gtk, Gdk, GObject, Pango
 
 from . import Actions, Utils, Constants
 from .Dialogs import SimpleTextDisplay
+import six
 
 
 class PropsDialog(Gtk.Dialog):
@@ -160,7 +162,7 @@ class PropsDialog(Gtk.Dialog):
                     # child.destroy()   # disabled because it throw errors...
                 # repopulate the params box
                 box_all_valid = True
-                for param in filter(lambda p: p.get_tab_label() == tab, self._block.get_params()):
+                for param in [p for p in self._block.get_params() if p.get_tab_label() == tab]:
                     # fixme: why do we even rebuild instead of really hiding params?
                     if param.get_hide() == 'all':
                         continue
@@ -212,7 +214,7 @@ class PropsDialog(Gtk.Dialog):
             docstrings = {block_class: docstrings[block_class]}
 
         # show docstring(s) extracted from python sources
-        for cls_name, docstring in docstrings.iteritems():
+        for cls_name, docstring in six.iteritems(docstrings):
             buf.insert_with_tags_by_name(pos, cls_name + '\n', 'b')
             buf.insert(pos, docstring + '\n\n')
         pos.backward_chars(2)
diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py
index 3cdb5f30ce..b109a1281b 100644
--- a/grc/gui/StateCache.py
+++ b/grc/gui/StateCache.py
@@ -17,8 +17,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-import Actions
-from Constants import STATE_CACHE_SIZE
+from __future__ import absolute_import
+from . import Actions
+from .Constants import STATE_CACHE_SIZE
 
 class StateCache(object):
     """
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index 311b37f468..e5d4ccaa35 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -17,6 +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 __future__ import absolute_import
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import GLib
@@ -47,7 +49,7 @@ def get_rotated_coordinate(coor, rotation):
     return x * cos_r + y * sin_r, -x * sin_r + y * cos_r
 
 
-def get_angle_from_coordinates((x1, y1), (x2, y2)):
+def get_angle_from_coordinates(p1, p2):
     """
     Given two points, calculate the vector direction from point1 to point2, directions are multiples of 90 degrees.
 
@@ -58,6 +60,8 @@ def get_angle_from_coordinates((x1, y1), (x2, y2)):
     Returns:
         the direction in degrees
     """
+    (x1, y1) = p1
+    (x2, y2) = p2
     if y1 == y2:  # 0 or 180
         return 0 if x2 > x1 else 180
     else:  # 90 or 270
@@ -78,7 +82,7 @@ def align_to_grid(coor, mode=round):
     def align(value):
         return int(mode(value / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
     try:
-        return map(align, coor)
+        return [align(c) for c in coor]
     except TypeError:
         x = coor
         return align(coor)
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index d34903e241..399e4ec475 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-from operator import attrgetter
+from __future__ import absolute_import
 
 import gi
 gi.require_version('Gtk', '3.0')
@@ -221,8 +221,8 @@ class VariableEditor(Gtk.VBox):
                 sp('foreground', 'red')
 
     def update_gui(self, blocks):
-        self._imports = filter(attrgetter('is_import'), blocks)
-        self._variables = filter(attrgetter('is_variable'), blocks)
+        self._imports = [block for block in blocks if block.is_import]
+        self._variables = [block for block in blocks if block.is_variable]
         self._rebuild()
         self.treeview.expand_all()
 
diff --git a/grc/gui/external_editor.py b/grc/gui/external_editor.py
index 76f21412b0..11d6fd7ebb 100644
--- a/grc/gui/external_editor.py
+++ b/grc/gui/external_editor.py
@@ -17,6 +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 __future__ import absolute_import, print_function
+
 import os
 import sys
 import time
@@ -71,7 +73,7 @@ class ExternalEditor(threading.Thread):
                 time.sleep(1)
 
         except Exception as e:
-            print >> sys.stderr, "file monitor crashed:", str(e)
+            print("file monitor crashed:", str(e), file=sys.stderr)
         else:
             # print "file monitor: done with", filename
             pass
@@ -79,7 +81,7 @@ class ExternalEditor(threading.Thread):
 
 if __name__ == '__main__':
     def p(data):
-        print data
+        print(data)
 
     e = ExternalEditor('/usr/bin/gedit', "test", "content", p)
     e.open_editor()
diff --git a/grc/main.py b/grc/main.py
index cd9863739f..810ac7c66f 100755
--- a/grc/main.py
+++ b/grc/main.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import optparse
 
 import gi
-- 
cgit v1.2.3


From c39aa154ff7a7787b91e9c09862016afb9874254 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Wed, 15 Jun 2016 11:11:37 -0700
Subject: grc-refactor: move more port type color stuff to gui

---
 grc/core/Constants.py | 56 +++++++++++++++++++-------------------------
 grc/core/Platform.py  |  3 ---
 grc/core/Port.py      | 26 ---------------------
 grc/gui/Colors.py     | 39 +++++++++++++++++++++++++------
 grc/gui/Connection.py | 20 +++++++++-------
 grc/gui/Dialogs.py    |  4 ++--
 grc/gui/Port.py       | 65 +++++++++++++++++++++++++++------------------------
 7 files changed, 104 insertions(+), 109 deletions(-)

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

diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 992d5e7d83..99531487fd 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -99,41 +99,33 @@ GRC_COLOR_GREY = '#BDBDBD'
 GRC_COLOR_WHITE = '#FFFFFF'
 
 CORE_TYPES = (  # name, key, sizeof, color
-    ('Complex Float 64', 'fc64', 16, GRC_COLOR_BROWN),
-    ('Complex Float 32', 'fc32', 8, GRC_COLOR_BLUE),
-    ('Complex Integer 64', 'sc64', 16, GRC_COLOR_LIGHT_GREEN),
-    ('Complex Integer 32', 'sc32', 8, GRC_COLOR_GREEN),
-    ('Complex Integer 16', 'sc16', 4, GRC_COLOR_AMBER),
-    ('Complex Integer 8', 'sc8', 2, GRC_COLOR_PURPLE),
-    ('Float 64', 'f64', 8, GRC_COLOR_CYAN),
-    ('Float 32', 'f32', 4, GRC_COLOR_ORANGE),
-    ('Integer 64', 's64', 8, GRC_COLOR_LIME),
-    ('Integer 32', 's32', 4, GRC_COLOR_TEAL),
-    ('Integer 16', 's16', 2, GRC_COLOR_YELLOW),
-    ('Integer 8', 's8', 1, GRC_COLOR_PURPLE_A400),
-    ('Bits (unpacked byte)', 'bit', 1, GRC_COLOR_PURPLE_A100),
-    ('Message Queue', 'msg', 0, GRC_COLOR_DARK_GREY),
-    ('Async Message', 'message', 0, GRC_COLOR_GREY),
-    ('Bus Connection', 'bus', 0, GRC_COLOR_WHITE),
-    ('Wildcard', '', 0, GRC_COLOR_WHITE),
+    ('Complex Float 64',    'fc64', 16, GRC_COLOR_BROWN),
+    ('Complex Float 32',    'fc32',  8, GRC_COLOR_BLUE),
+    ('Complex Integer 64',  'sc64', 16, GRC_COLOR_LIGHT_GREEN),
+    ('Complex Integer 32',  'sc32',  8, GRC_COLOR_GREEN),
+    ('Complex Integer 16',  'sc16',  4, GRC_COLOR_AMBER),
+    ('Complex Integer 8',    'sc8',  2, GRC_COLOR_PURPLE),
+    ('Float 64',             'f64',  8, GRC_COLOR_CYAN),
+    ('Float 32',             'f32',  4, GRC_COLOR_ORANGE),
+    ('Integer 64',           's64',  8, GRC_COLOR_LIME),
+    ('Integer 32',           's32',  4, GRC_COLOR_TEAL),
+    ('Integer 16',           's16',  2, GRC_COLOR_YELLOW),
+    ('Integer 8',             's8',  1, GRC_COLOR_PURPLE_A400),
+    ('Bits (unpacked byte)', 'bit',  1, GRC_COLOR_PURPLE_A100),
+    ('Message Queue',        'msg',  0, GRC_COLOR_DARK_GREY),
+    ('Async Message',    'message',  0, GRC_COLOR_GREY),
+    ('Bus Connection',       'bus',  0, GRC_COLOR_WHITE),
+    ('Wildcard',                '',  0, GRC_COLOR_WHITE),
 )
 
 ALIAS_TYPES = {
     'complex': (8, GRC_COLOR_BLUE),
-    'float': (4, GRC_COLOR_ORANGE),
-    'int': (4, GRC_COLOR_TEAL),
-    'short': (2, GRC_COLOR_YELLOW),
-    'byte': (1, GRC_COLOR_PURPLE_A400),
-    'bits': (1, GRC_COLOR_PURPLE_A100),
+    'float':   (4, GRC_COLOR_ORANGE),
+    'int':     (4, GRC_COLOR_TEAL),
+    'short':   (2, GRC_COLOR_YELLOW),
+    'byte':    (1, GRC_COLOR_PURPLE_A400),
+    'bits':    (1, GRC_COLOR_PURPLE_A100),
 }
 
-TYPE_TO_COLOR = dict()
-TYPE_TO_SIZEOF = dict()
-
-for name, key, sizeof, color in CORE_TYPES:
-    TYPE_TO_COLOR[key] = color
-    TYPE_TO_SIZEOF[key] = sizeof
-
-for key, (sizeof, color) in six.iteritems(ALIAS_TYPES):
-    TYPE_TO_COLOR[key] = color
-    TYPE_TO_SIZEOF[key] = sizeof
+TYPE_TO_SIZEOF = {key: sizeof for name, key, sizeof, color in CORE_TYPES}
+TYPE_TO_SIZEOF.update((key, sizeof) for key, (sizeof, _) in ALIAS_TYPES.items())
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
index be7b60ca59..844693d14b 100644
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@ -310,6 +310,3 @@ class Platform(Element):
 
     def get_new_block(self, flow_graph, key):
         return self.Block(flow_graph, n=self._blocks_n[key])
-
-    def get_colors(self):
-        return [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES]
diff --git a/grc/core/Port.py b/grc/core/Port.py
index 9a33c5c506..4ba516d5fe 100644
--- a/grc/core/Port.py
+++ b/grc/core/Port.py
@@ -263,32 +263,6 @@ class Port(Element):
     def get_optional(self):
         return bool(self._optional)
 
-    def get_color(self):
-        """
-        Get the color that represents this port's type.
-        Codes differ for ports where the vec length is 1 or greater than 1.
-
-        Returns:
-            a hex color code.
-        """
-        try:
-            color = Constants.TYPE_TO_COLOR[self.get_type()]
-            vlen = self.get_vlen()
-            if vlen == 1:
-                return color
-            color_val = int(color[1:], 16)
-            r = (color_val >> 16) & 0xff
-            g = (color_val >> 8) & 0xff
-            b = (color_val >> 0) & 0xff
-            dark = (0, 0, 30, 50, 70)[min(4, vlen)]
-            r = max(r-dark, 0)
-            g = max(g-dark, 0)
-            b = max(b-dark, 0)
-            # TODO: Change this to .format()
-            return '#%.2x%.2x%.2x' % (r, g, b)
-        except:
-            return '#FFFFFF'
-
     def get_clones(self):
         """
         Get the clones of this master port (nports > 1)
diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py
index 6ee31e5e18..157c07ea35 100644
--- a/grc/gui/Colors.py
+++ b/grc/gui/Colors.py
@@ -19,12 +19,37 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import
 
+from gi.repository import Gdk, cairo
+# import pycairo
+
+from . import Constants
+
+
+def _color_parse(color_code):
+    color = Gdk.RGBA()
+    color.parse(color_code)
+    return color
+
 
 def get_color(color_code):
+    # color = _color_parse(color_code)
+    # print(dir(cairo.SolidPattern))
+    # cairo_pattern = cairo.SolidPattern(
+    #     red=color.red,
+    #     green=color.green,
+    #     blue=color.blue,
+    #     alpha=color.alpha
+    # )
+    # return cairo_pattern
+
     chars_per_color = 2 if len(color_code) > 4 else 1
     offsets = range(1, 3 * chars_per_color + 1, chars_per_color)
     return tuple(int(color_code[o:o + 2], 16) / 255.0 for o in offsets)
 
+#################################################################################
+# fg colors
+#################################################################################
+
 HIGHLIGHT_COLOR = get_color('#00FFFF')
 BORDER_COLOR = get_color('#444444')
 
@@ -49,18 +74,18 @@ CONNECTION_ERROR_COLOR = get_color('#FF0000')
 
 DEFAULT_DOMAIN_COLOR = get_color('#777777')
 
+
 #################################################################################
-# param box colors
+# port colors
 #################################################################################
 
-from gi.repository import Gdk
+PORT_TYPE_TO_COLOR = {key: get_color(color) for name, key, sizeof, color in Constants.CORE_TYPES}
+PORT_TYPE_TO_COLOR.update((key, get_color(color)) for key, (_, color) in Constants.ALIAS_TYPES.items())
 
 
-def _color_parse(color_code):
-    color = Gdk.RGBA()
-    color.parse(color_code)
-    return color
-
+#################################################################################
+# param box colors
+#################################################################################
 COMPLEX_COLOR_SPEC = _color_parse('#3399FF')
 FLOAT_COLOR_SPEC = _color_parse('#FF8C69')
 INT_COLOR_SPEC = _color_parse('#00FF99')
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index d893060aa6..b1a22e3949 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -45,6 +45,9 @@ class Connection(Element, _Connection):
         # can't use Colors.CONNECTION_ENABLED_COLOR here, might not be defined (grcc)
         self._bg_color = self._arrow_color = self._color = None
 
+        self._sink_rot = self._source_rot = None
+        self._sink_coor = self._source_coor = None
+
     def get_coordinate(self):
         """
         Get the 0,0 coordinate.
@@ -74,12 +77,12 @@ class Connection(Element, _Connection):
         self._source_coor = None
         #get the source coordinate
         try:
-            connector_length = self.source_port.get_connector_length()
+            connector_length = self.source_port.connector_length
         except:
-            return
+            return  # todo: why?
         self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.source_port.get_rotation())
         #get the sink coordinate
-        connector_length = self.sink_port.get_connector_length() + CONNECTOR_ARROW_HEIGHT
+        connector_length = self.sink_port.connector_length + CONNECTOR_ARROW_HEIGHT
         self.x2, self.y2 = Utils.get_rotated_coordinate((-connector_length, 0), self.sink_port.get_rotation())
         #build the arrow
         self.arrow = [(0, 0),
@@ -106,7 +109,7 @@ class Connection(Element, _Connection):
 
     def _update_after_move(self):
         """Calculate coordinates."""
-        self.clear() #FIXME do i want this here?
+        self.clear()
         #source connector
         source = self.source_port
         X, Y = source.get_connector_coordinate()
@@ -118,11 +121,11 @@ class Connection(Element, _Connection):
         x2, y2 = self.x2 + X, self.y2 + Y
         self.add_line((x2, y2), (X, Y))
         #adjust arrow
-        self._arrow = [(x+X, y+Y) for x,y in self.arrow]
+        self._arrow = [(x+X, y+Y) for x, y in self.arrow]
         #add the horizontal and vertical lines in this connection
         if abs(source.get_connector_direction() - sink.get_connector_direction()) == 180:
             #2 possible point sets to create a 3-line connector
-            mid_x, mid_y = (x1 + x2)/2.0, (y1 + y2)/2.0
+            mid_x, mid_y = (x1 + x2) / 2.0, (y1 + y2) / 2.0
             points = [((mid_x, y1), (mid_x, y2)), ((x1, mid_y), (x2, mid_y))]
             #source connector -> points[0][0] should be in the direction of source (if possible)
             if Utils.get_angle_from_coordinates((x1, y1), points[0][0]) != source.get_connector_direction(): points.reverse()
@@ -155,12 +158,13 @@ class Connection(Element, _Connection):
         sink = self.sink_port
         source = self.source_port
         #check for changes
-        if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes()
+        if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation():
+            self.create_shapes()
         elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate():
             try:
                 self._update_after_move()
             except:
-                return
+                return  # todo: why?
         #cache values
         self._sink_rot = sink.get_rotation()
         self._source_rot = source.get_rotation()
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index 8f0f60d764..da271fe46e 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -27,7 +27,7 @@ from gi.repository import GObject
 import sys
 from distutils.spawn import find_executable
 
-from . import Utils, Actions
+from . import Utils, Actions, Constants
 from ..core import Messages
 
 
@@ -207,7 +207,7 @@ def HelpDialog(): MessageDialogHelper(
 
 
 def TypesDialog(platform):
-    colors = platform.get_colors()
+    colors = [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES]
     max_len = 10 + max(len(name) for name, code in colors)
 
     message = '\n'.join(
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index 8c4500f960..62086c76e4 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -23,15 +23,10 @@ import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk, PangoCairo
 
-from . import Actions, Colors, Utils
-from .Constants import (
-    PORT_SEPARATION, PORT_SPACING, CONNECTOR_EXTENSION_MINIMAL,
-    CONNECTOR_EXTENSION_INCREMENT, PORT_LABEL_PADDING, PORT_MIN_WIDTH,
-    PORT_LABEL_HIDDEN_WIDTH, PORT_FONT
-)
+from . import Actions, Colors, Utils, Constants
 from .Element import Element
-from ..core.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
 
+from ..core.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN
 from ..core.Port import Port as _Port
 
 
@@ -45,24 +40,42 @@ class Port(_Port, Element):
         """
         _Port.__init__(self, block, n, dir)
         Element.__init__(self)
-        self.W = self.w = self.h = 0
-        self.H = 20  # todo: fix
         self._connector_coordinate = (0, 0)
-        self._connector_length = 0
         self._hovering = True
         self._force_label_unhidden = False
+        self._bg_color = (0, 0, 0)
+
+        self.W = self.w = self.h = 0
+        self.H = 20  # todo: fix
+        self.connector_length = 0
+
         self.layout = Gtk.DrawingArea().create_pango_layout('')
-        self._bg_color = Colors.get_color(self.get_color())
+
+    def _get_color(self):
+        """
+        Get the color that represents this port's type.
+        Codes differ for ports where the vec length is 1 or greater than 1.
+
+        Returns:
+            a hex color code.
+        """
+        color = Colors.PORT_TYPE_TO_COLOR[self.get_type()]
+        vlen = self.get_vlen()
+        if vlen > 1:
+            dark = (0, 0, 30 / 255.0, 50 / 255.0, 70 / 255.0)[min(4, vlen)]
+            color = tuple(max(c - dark, 0) for c in color)
+        return color
 
     def create_shapes(self):
         """Create new areas and labels for the port."""
         Element.create_shapes(self)
-        self._bg_color = Colors.get_color(self.get_color())
+        self._bg_color = self._get_color()
+
         if self.get_hide():
             return  # this port is hidden, no need to create shapes
-        if self.get_domain() == GR_MESSAGE_DOMAIN:
+        if self.get_domain() == Constants.GR_MESSAGE_DOMAIN:
             pass
-        elif self.get_domain() != DEFAULT_DOMAIN:
+        elif self.get_domain() != Constants.DEFAULT_DOMAIN:
             self.line_attributes[0] = 2
         #get current rotation
         rotation = self.get_rotation()
@@ -71,22 +84,22 @@ class Port(_Port, Element):
             if self.is_source else self.parent.get_sinks_gui()
         ports = [p for p in ports if not p.get_hide()]
         #get the max width
-        self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH])
-        W = self.W if not self._label_hidden() else PORT_LABEL_HIDDEN_WIDTH
+        self.W = max([port.W for port in ports] + [Constants.PORT_MIN_WIDTH])
+        W = self.W if not self._label_hidden() else Constants.PORT_LABEL_HIDDEN_WIDTH
         #get a numeric index for this port relative to its sibling ports
         try:
             index = ports.index(self)
         except:
             if hasattr(self, '_connector_length'):
-                del self._connector_length
+                del self.connector_length
             return
         #reverse the order of ports for these rotations
         if rotation in (180, 270):
             index = len(ports)-index-1
 
-        port_separation = PORT_SEPARATION \
+        port_separation = Constants.PORT_SEPARATION \
             if not self.parent.has_busses[self.is_source] \
-            else max([port.H for port in ports]) + PORT_SPACING
+            else max([port.H for port in ports]) + Constants.PORT_SPACING
 
         offset = (self.parent.H - (len(ports)-1)*port_separation - self.H)/2
         #create areas and connector coordinates
@@ -111,12 +124,12 @@ class Port(_Port, Element):
             self.add_area((x, y), (self.H, W))
             self._connector_coordinate = (x+self.H/2, y+1+W)
         #the connector length
-        self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index
+        self.connector_length = Constants.CONNECTOR_EXTENSION_MINIMAL + Constants.CONNECTOR_EXTENSION_INCREMENT * index
 
     def create_labels(self):
         """Create the labels for the socket."""
         self.layout.set_markup("""<span foreground="black" font_desc="{font}">{name}</span>""".format(
-            name=Utils.encode(self.get_name()), font=PORT_FONT
+            name=Utils.encode(self.get_name()), font=Constants.PORT_FONT
         ))
 
     def draw(self, widget, cr):
@@ -169,16 +182,6 @@ class Port(_Port, Element):
         if self.is_source: return self.get_rotation()
         elif self.is_sink: return (self.get_rotation() + 180)%360
 
-    def get_connector_length(self):
-        """
-        Get the length of the connector.
-        The connector length increases as the port index changes.
-
-        Returns:
-            the length in pixels
-        """
-        return self._connector_length
-
     def get_rotation(self):
         """
         Get the parent's rotation rather than self.
-- 
cgit v1.2.3


From 310af9cd32b42e9ad5324fb1dca9bff25ccaca96 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Wed, 15 Jun 2016 11:25:31 -0700
Subject: grc-refactor: block state handling

---
 grc/core/Block.py        | 44 ++++++++++++++++++++++++--------------------
 grc/core/Constants.py    |  5 -----
 grc/gui/ActionHandler.py | 10 ++++++----
 grc/gui/Element.py       |  1 -
 4 files changed, 30 insertions(+), 30 deletions(-)

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

diff --git a/grc/core/Block.py b/grc/core/Block.py
index 9fff5afcb7..829665954b 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -26,13 +26,13 @@ from six.moves import map, range
 
 from Cheetah.Template import Template
 
-from .utils import epy_block_io
+from . import utils
+
 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_FLAG_DEPRECATED,
-    BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED
 )
 from . Element import Element
 
@@ -52,6 +52,9 @@ class Block(Element):
 
     is_block = True
 
+    # block states
+    DISABLED, ENABLED, BYPASSED = range(3)
+
     def __init__(self, flow_graph, n):
         """
         Make a new block from nested data.
@@ -318,7 +321,7 @@ class Block(Element):
             return
 
         try:
-            blk_io = epy_block_io.extract(src)
+            blk_io = utils.epy_block_io.extract(src)
 
         except Exception as e:
             self._epy_reload_error = ValueError(str(e))
@@ -326,7 +329,7 @@ class Block(Element):
                 blk_io_args = eval(param_blk.get_value())
                 if len(blk_io_args) == 6:
                     blk_io_args += ([],)  # add empty callbacks
-                blk_io = epy_block_io.BlockIO(*blk_io_args)
+                blk_io = utils.epy_block_io.BlockIO(*blk_io_args)
             except Exception:
                 return
         else:
@@ -395,7 +398,8 @@ class Block(Element):
 
     # Main functions to get and set the block state
     # Also kept get_enabled and set_enabled to keep compatibility
-    def get_state(self):
+    @property
+    def state(self):
         """
         Gets the block's current state.
 
@@ -405,11 +409,12 @@ class Block(Element):
             DISABLED - 2
         """
         try:
-            return int(eval(self.get_param('_enabled').get_value()))
-        except:
-            return BLOCK_ENABLED
+            return int(self.get_param('_enabled').get_value())
+        except ValueError:
+            return self.ENABLED
 
-    def set_state(self, state):
+    @state.setter
+    def state(self, value):
         """
         Sets the state for the block.
 
@@ -418,10 +423,9 @@ class Block(Element):
             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))
+        if value not in [self.ENABLED, self.BYPASSED, self.DISABLED]:
+            value = self.ENABLED
+        self.get_param('_enabled').set_value(str(value))
 
     # Enable/Disable Aliases
     def get_enabled(self):
@@ -431,7 +435,7 @@ class Block(Element):
         Returns:
             true for enabled
         """
-        return not (self.get_state() == BLOCK_DISABLED)
+        return self.state != self.DISABLED
 
     def set_enabled(self, enabled):
         """
@@ -443,9 +447,9 @@ class Block(Element):
         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)
+        old_state = self.state
+        new_state = self.ENABLED if enabled else self.DISABLED
+        self.state = new_state
         return old_state != new_state
 
     # Block bypassing
@@ -453,7 +457,7 @@ class Block(Element):
         """
         Check if the block is bypassed
         """
-        return self.get_state() == BLOCK_BYPASSED
+        return self.state == self.BYPASSED
 
     def set_bypassed(self):
         """
@@ -462,8 +466,8 @@ class Block(Element):
         Returns:
             True if block chagnes state
         """
-        if self.get_state() != BLOCK_BYPASSED and self.can_bypass():
-            self.set_state(BLOCK_BYPASSED)
+        if self.state != self.BYPASSED and self.can_bypass():
+            self.state = self.BYPASSED
             return True
         return False
 
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 99531487fd..40fe69dd02 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -54,11 +54,6 @@ BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui'
 BLOCK_FLAG_NEED_WX_GUI = 'need_wx_gui'
 BLOCK_FLAG_DEPRECATED = 'deprecated'
 
-# Block States
-BLOCK_DISABLED = 0
-BLOCK_ENABLED = 1
-BLOCK_BYPASSED = 2
-
 # File creation modes
 TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | \
                       stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 25c779b4d2..d188030c62 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -670,12 +670,14 @@ class ActionHandler:
         Actions.BLOCK_COPY.set_sensitive(bool(selected_blocks))
         Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard))
         #update enable/disable/bypass
-        can_enable = any(block.get_state() != Constants.BLOCK_ENABLED
+        can_enable = any(block.state != block.ENABLED
                          for block in selected_blocks)
-        can_disable = any(block.get_state() != Constants.BLOCK_DISABLED
+        can_disable = any(block.state != block.DISABLED
                           for block in selected_blocks)
-        can_bypass_all = all(block.can_bypass() for block in selected_blocks) \
-                         and any(not block.get_bypassed() for block in selected_blocks)
+        can_bypass_all = (
+            all(block.can_bypass() for block in selected_blocks) and
+            any(not block.get_bypassed() for block in selected_blocks)
+        )
         Actions.BLOCK_ENABLE.set_sensitive(can_enable)
         Actions.BLOCK_DISABLE.set_sensitive(can_disable)
         Actions.BLOCK_BYPASS.set_sensitive(can_bypass_all)
diff --git a/grc/gui/Element.py b/grc/gui/Element.py
index 4e88df375f..48fdf62543 100644
--- a/grc/gui/Element.py
+++ b/grc/gui/Element.py
@@ -48,7 +48,6 @@ class Element(object):
            0, Gdk.LINE_SOLID, Gdk.CAP_BUTT, Gdk.JOIN_MITER
         ]"""
 
-
     def is_horizontal(self, rotation=None):
         """
         Is this element horizontal?
-- 
cgit v1.2.3


From 93ce3961a572da6ec3dbef1f24a22f4153acaa61 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Fri, 15 Jul 2016 23:25:54 +0200
Subject: grc: refactor: Port, Param, Options init clean-up

---
 grc/core/Block.py                    | 300 +++++++++++++++++------------------
 grc/core/Constants.py                |  11 ++
 grc/core/Element.py                  |   9 +-
 grc/core/Element.pyi                 |  16 +-
 grc/core/Param.py                    | 272 +++++++------------------------
 grc/core/Platform.py                 |  73 ++++++---
 grc/core/Port.py                     |  13 +-
 grc/core/generator/FlowGraphProxy.py |   2 +-
 grc/core/utils/__init__.py           |   1 -
 grc/core/utils/_num_to_str.py        |  30 ----
 grc/gui/Block.py                     |   4 +-
 grc/gui/Connection.py                |   8 +-
 grc/gui/MainWindow.py                |  10 +-
 grc/gui/Param.py                     |  79 ++++++++-
 grc/gui/ParamWidgets.py              |  16 +-
 grc/gui/Platform.py                  |   8 +-
 grc/gui/Port.py                      |   9 +-
 grc/gui/Utils.py                     |  45 ++++--
 18 files changed, 411 insertions(+), 495 deletions(-)
 delete mode 100644 grc/core/utils/_num_to_str.py

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

diff --git a/grc/core/Block.py b/grc/core/Block.py
index 8ffb99b5bd..d37909b4b8 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -36,7 +36,7 @@ from . Constants import (
     BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS,
     BLOCK_FLAG_DEPRECATED,
 )
-from . Element import Element
+from . Element import Element, lazy_property
 
 
 def _get_elem(iterable, key):
@@ -53,21 +53,12 @@ class Block(Element):
 
     STATE_LABELS = ['disabled', 'enabled', 'bypassed']
 
-    def __init__(self, flow_graph, n):
-        """
-        Make a new block from nested data.
-
-        Args:
-            flow_graph: the parent element
-            n: the nested odict
+    def __init__(self, parent, key, name, **n):
+        """Make a new block from nested data."""
+        super(Block, self).__init__(parent)
 
-        Returns:
-            block a new block
-        """
-        Element.__init__(self, parent=flow_graph)
-
-        self.name = n['name']
-        self.key = n['key']
+        self.key = key
+        self.name = name
         self.category = [cat.strip() for cat in n.get('category', '').split('/') if cat.strip()]
         self.flags = n.get('flags', '')
         self._doc = n.get('doc', '').strip('\n').replace('\\\n', '')
@@ -98,7 +89,6 @@ class Block(Element):
             has_sources=len(sources_n)
         )
 
-        self.port_counters = [itertools.count(), itertools.count()]
         self.sources = self._init_ports(sources_n, direction='source')
         self.sinks = self._init_ports(sinks_n, direction='sink')
         self.active_sources = []  # on rewrite
@@ -106,18 +96,15 @@ class Block(Element):
 
         self.states = {'_enabled': True}
 
-        self._epy_source_hash = -1  # for epy blocks
-        self._epy_reload_error = None
-
         self._init_bus_ports(n)
 
-    def _add_param(self, key, name, value='', type='raw', **kwargs):
-        n = {'key': key, 'name': name, 'value': value, 'type': type}
-        n.update(kwargs)
-        self.params[key] = self.parent_platform.Param(block=self, n=n)
-
     def _init_params(self, params_n, has_sources, has_sinks):
-        self._add_param(key='id', name='ID', type='id')
+        param_factory = self.parent_platform.get_new_param
+
+        def add_param(key, **kwargs):
+            self.params[key] = param_factory(self, key=key, **kwargs)
+
+        add_param(key='id', name='ID', type='id')
 
         # Virtual source/sink and pad source/sink blocks are
         # indistinguishable from normal GR blocks. Make explicit
@@ -133,35 +120,40 @@ class Block(Element):
             self.flags += BLOCK_FLAG_DISABLE_BYPASS
 
         if not (is_virtual_or_pad or is_variable or self.key == 'options'):
-            self._add_param(key='alias', name='Block Alias', type='string',
-                            hide='part', tab=ADVANCED_PARAM_TAB)
+            add_param(key='alias', name='Block Alias', type='string',
+                      hide='part', tab=ADVANCED_PARAM_TAB)
 
         if not is_virtual_or_pad and (has_sources or has_sinks):
-            self._add_param(key='affinity', name='Core Affinity', type='int_vector',
-                            hide='part', tab=ADVANCED_PARAM_TAB)
+            add_param(key='affinity', name='Core Affinity', type='int_vector',
+                      hide='part', tab=ADVANCED_PARAM_TAB)
 
         if not is_virtual_or_pad and has_sources:
-            self._add_param(key='minoutbuf', name='Min Output Buffer', type='int',
-                            hide='part', value='0', tab=ADVANCED_PARAM_TAB)
-            self._add_param(key='maxoutbuf', name='Max Output Buffer', type='int',
-                            hide='part', value='0', tab=ADVANCED_PARAM_TAB)
+            add_param(key='minoutbuf', name='Min Output Buffer', type='int',
+                      hide='part', value='0', tab=ADVANCED_PARAM_TAB)
+            add_param(key='maxoutbuf', name='Max Output Buffer', type='int',
+                      hide='part', value='0', tab=ADVANCED_PARAM_TAB)
 
+        base_params_n = {n['key']: n for n in params_n}
         for param_n in params_n:
-            param = self.parent_platform.Param(block=self, n=param_n)
-            key = param.key
+            key = param_n['key']
             if key in self.params:
                 raise Exception('Key "{}" already exists in params'.format(key))
-            self.params[key] = param
 
-        self._add_param(key='comment', name='Comment', type='_multiline', hide='part',
-                        value='', tab=ADVANCED_PARAM_TAB)
+            extended_param_n = base_params_n.get(param_n.pop('base_key', None), {})
+            extended_param_n.update(param_n)
+            self.params[key] = param_factory(self, **extended_param_n)
+
+        add_param(key='comment', name='Comment', type='_multiline', hide='part',
+                  value='', tab=ADVANCED_PARAM_TAB)
 
     def _init_ports(self, ports_n, direction):
-        port_cls = self.parent_platform.Port
+        port_factory = self.parent_platform.get_new_port
         ports = []
         port_keys = set()
-        for port_n in ports_n:
-            port = port_cls(block=self, n=port_n, dir=direction)
+        stream_port_keys = itertools.count()
+        for i, port_n in enumerate(ports_n):
+            port_n.setdefault('key', str(next(stream_port_keys)))
+            port = port_factory(parent=self, direction=direction, **port_n)
             key = port.key
             if key in port_keys:
                 raise Exception('Key "{}" already exists in {}'.format(key, direction))
@@ -169,61 +161,14 @@ class Block(Element):
             ports.append(port)
         return ports
 
-    def _run_checks(self):
-        """Evaluate the checks"""
-        for check in self._checks:
-            check_res = self.resolve_dependencies(check)
-            try:
-                if not self.parent.evaluate(check_res):
-                    self.add_error_message('Check "{}" failed.'.format(check))
-            except:
-                self.add_error_message('Check "{}" did not evaluate.'.format(check))
-
-    def _validate_generate_mode_compat(self):
-        """check if this is a GUI block and matches the selected generate option"""
-        current_generate_option = self.parent.get_option('generate_options')
-
-        def check_generate_mode(label, flag, valid_options):
-            block_requires_mode = (
-                flag in self.flags or self.name.upper().startswith(label)
-            )
-            if block_requires_mode and current_generate_option not in valid_options:
-                self.add_error_message("Can't generate this block in mode: {} ".format(
-                                       repr(current_generate_option)))
-
-        check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',))
-        check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
-
-    def _validate_var_value(self):
-        """or variables check the value (only if var_value is used)"""
-        if self.is_variable and self._var_value != '$value':
-            value = self._var_value
-            try:
-                value = self.get_var_value()
-                self.parent.evaluate(value)
-            except Exception as err:
-                self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err))
-
-    def validate(self):
-        """
-        Validate this block.
-        Call the base class validate.
-        Evaluate the checks: each check must evaluate to True.
-        """
-        Element.validate(self)
-        self._run_checks()
-        self._validate_generate_mode_compat()
-        self._validate_var_value()
-        if self._epy_reload_error:
-            self.params['_source_code'].add_error_message(str(self._epy_reload_error))
-
+    ##############################################
+    # validation and rewrite
+    ##############################################
     def rewrite(self):
         """
         Add and remove ports to adjust for the nports.
         """
         Element.rewrite(self)
-        # Check and run any custom rewrite function for this block
-        getattr(self, 'rewrite_' + self.key, lambda: None)()
 
         # Adjust nports, disconnect hidden ports
         for ports in (self.sources, self.sinks):
@@ -258,6 +203,55 @@ class Block(Element):
         self.active_sources = [p for p in self.get_sources_gui() if not p.get_hide()]
         self.active_sinks = [p for p in self.get_sinks_gui() if not p.get_hide()]
 
+    def validate(self):
+        """
+        Validate this block.
+        Call the base class validate.
+        Evaluate the checks: each check must evaluate to True.
+        """
+        Element.validate(self)
+        self._run_checks()
+        self._validate_generate_mode_compat()
+        self._validate_var_value()
+
+    def _run_checks(self):
+        """Evaluate the checks"""
+        for check in self._checks:
+            check_res = self.resolve_dependencies(check)
+            try:
+                if not self.parent.evaluate(check_res):
+                    self.add_error_message('Check "{}" failed.'.format(check))
+            except:
+                self.add_error_message('Check "{}" did not evaluate.'.format(check))
+
+    def _validate_generate_mode_compat(self):
+        """check if this is a GUI block and matches the selected generate option"""
+        current_generate_option = self.parent.get_option('generate_options')
+
+        def check_generate_mode(label, flag, valid_options):
+            block_requires_mode = (
+                flag in self.flags or self.name.upper().startswith(label)
+            )
+            if block_requires_mode and current_generate_option not in valid_options:
+                self.add_error_message("Can't generate this block in mode: {} ".format(
+                                       repr(current_generate_option)))
+
+        check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',))
+        check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
+
+    def _validate_var_value(self):
+        """or variables check the value (only if var_value is used)"""
+        if self.is_variable and self._var_value != '$value':
+            value = self._var_value
+            try:
+                value = self.get_var_value()
+                self.parent.evaluate(value)
+            except Exception as err:
+                self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err))
+
+    ##############################################
+    # Getters
+    ##############################################
     def get_imports(self, raw=False):
         """
         Resolve all import statements.
@@ -316,14 +310,7 @@ class Block(Element):
     # Also kept get_enabled and set_enabled to keep compatibility
     @property
     def state(self):
-        """
-        Gets the block's current state.
-
-        Returns:
-            ENABLED - 0
-            BYPASSED - 1
-            DISABLED - 2
-        """
+        """Gets the block's current state."""
         try:
             return self.STATE_LABELS[int(self.states['_enabled'])]
         except ValueError:
@@ -331,14 +318,7 @@ class Block(Element):
 
     @state.setter
     def state(self, value):
-        """
-        Sets the state for the block.
-
-        Args:
-            ENABLED - 0
-            BYPASSED - 1
-            DISABLED - 2
-        """
+        """Sets the state for the block."""
         try:
             encoded = self.STATE_LABELS.index(value)
         except ValueError:
@@ -425,11 +405,11 @@ class Block(Element):
     def get_comment(self):
         return self.params['comment'].get_value()
 
-    @property
+    @lazy_property
     def is_throtteling(self):
         return BLOCK_FLAG_THROTTLE in self.flags
 
-    @property
+    @lazy_property
     def is_deprecated(self):
         return BLOCK_FLAG_DEPRECATED in self.flags
 
@@ -459,12 +439,11 @@ class Block(Element):
         return self.filter_bus_port(self.sources)
 
     def get_connections(self):
-        return sum([port.get_connections() for port in self.get_ports()], [])
+        return sum((port.get_connections() for port in self.get_ports()), [])
 
     ##############################################
     # Resolve
     ##############################################
-
     def resolve_dependencies(self, tmpl):
         """
         Resolve a paramater dependency with cheetah templates.
@@ -515,7 +494,7 @@ class Block(Element):
 
         # Try to increment the enum by direction
         try:
-            keys = type_param.get_option_keys()
+            keys = list(type_param.options.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])
@@ -693,7 +672,7 @@ class Block(Element):
             for i in range(len(struct)):
                 n['key'] = str(len(ports))
                 n = dict(n)
-                port = self.parent.parent.Port(block=self, n=n, dir=direc)
+                port = self.parent_platform.get_new_port(self, direction=direc, **n)
                 ports.append(port)
         elif any('bus' == p.get_type() for p in ports):
             for elt in get_p_gui():
@@ -716,15 +695,14 @@ class Block(Element):
 
 class EPyBlock(Block):
 
-    def __init__(self, flow_graph, n):
-        super(EPyBlock, self).__init__(flow_graph, n)
+    def __init__(self, flow_graph, **n):
+        super(EPyBlock, self).__init__(flow_graph, **n)
         self._epy_source_hash = -1  # for epy blocks
         self._epy_reload_error = None
 
+    def rewrite(self):
+        Element.rewrite(self)
 
-    def rewrite_epy_block(self):
-        flowgraph = self.parent_flowgraph
-        platform = self.parent_platform
         param_blk = self.params['_io_cache']
         param_src = self.params['_source_code']
 
@@ -758,53 +736,65 @@ class EPyBlock(Block):
         self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join(
             '{0}=${{ {0} }}'.format(key) for key, _ in blk_io.params))
         self._callbacks = ['{0} = ${{ {0} }}'.format(attr) for attr in blk_io.callbacks]
+        self._update_params(blk_io.params)
+        self._update_ports('in', self.sinks, blk_io.sinks, 'sink')
+        self._update_ports('out', self.sources, blk_io.sources, 'source')
+
+        super(EPyBlock, self).rewrite()
 
+    def _update_params(self, params_in_src):
+        param_factory = self.parent_platform.get_new_param
         params = {}
         for param in list(self.params):
             if hasattr(param, '__epy_param__'):
                 params[param.key] = param
                 del self.params[param.key]
 
-        for key, value in blk_io.params:
+        for key, value in params_in_src:
             try:
                 param = params[key]
-                param.set_default(value)
+                if param.default == param.value:
+                    param.set_value(value)
+                param.default = str(value)
             except KeyError:  # need to make a new param
-                name = key.replace('_', ' ').title()
-                n = dict(name=name, key=key, type='raw', value=value)
-                param = platform.Param(block=self, n=n)
+                param = param_factory(
+                    parent=self,  key=key, type='raw', value=value,
+                    name=key.replace('_', ' ').title(),
+                )
                 setattr(param, '__epy_param__', True)
             self.params[key] = param
 
-        def update_ports(label, ports, port_specs, direction):
-            ports_to_remove = list(ports)
-            iter_ports = iter(ports)
-            ports_new = []
-            port_current = next(iter_ports, None)
-            for key, port_type in port_specs:
-                reuse_port = (
-                    port_current is not None and
-                    port_current.get_type() == port_type and
-                    (key.isdigit() or port_current.key == key)
-                )
-                if reuse_port:
-                    ports_to_remove.remove(port_current)
-                    port, port_current = port_current, next(iter_ports, None)
-                else:
-                    n = dict(name=label + str(key), type=port_type, key=key)
-                    if port_type == 'message':
-                        n['name'] = key
-                        n['optional'] = '1'
-                    port = platform.Port(block=self, n=n, dir=direction)
-                ports_new.append(port)
-            # replace old port list with new one
-            del ports[:]
-            ports.extend(ports_new)
-            # remove excess port connections
-            for port in ports_to_remove:
-                for connection in port.get_connections():
-                    flowgraph.remove_element(connection)
-
-        update_ports('in', self.sinks, blk_io.sinks, 'sink')
-        update_ports('out', self.sources, blk_io.sources, 'source')
-        self.rewrite()
+    def _update_ports(self, label, ports, port_specs, direction):
+        port_factory = self.parent_platform.get_new_port
+        ports_to_remove = list(ports)
+        iter_ports = iter(ports)
+        ports_new = []
+        port_current = next(iter_ports, None)
+        for key, port_type in port_specs:
+            reuse_port = (
+                port_current is not None and
+                port_current.get_type() == port_type and
+                (key.isdigit() or port_current.key == key)
+            )
+            if reuse_port:
+                ports_to_remove.remove(port_current)
+                port, port_current = port_current, next(iter_ports, None)
+            else:
+                n = dict(name=label + str(key), type=port_type, key=key)
+                if port_type == 'message':
+                    n['name'] = key
+                    n['optional'] = '1'
+                port = port_factory(self, direction=direction, **n)
+            ports_new.append(port)
+        # replace old port list with new one
+        del ports[:]
+        ports.extend(ports_new)
+        # remove excess port connections
+        for port in ports_to_remove:
+            for connection in port.get_connections():
+                self.parent_flowgraph.remove_element(connection)
+
+    def validate(self):
+        super(EPyBlock, self).validate()
+        if self._epy_reload_error:
+            self.params['_source_code'].add_error_message(str(self._epy_reload_error))
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 40fe69dd02..bc387a4837 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -59,6 +59,17 @@ TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP
                       stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
 HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH
 
+PARAM_TYPE_NAMES = (
+    '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',
+)
+
 # Define types, native python + numpy
 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
 COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
diff --git a/grc/core/Element.py b/grc/core/Element.py
index cd0514beb6..415b086402 100644
--- a/grc/core/Element.py
+++ b/grc/core/Element.py
@@ -29,16 +29,15 @@ class lazy_property(object):
         if instance is None:
             return self
         value = self.func(instance)
-        weak_value = weakref.proxy(value) if not weakref.ProxyType else value
-        setattr(instance, self.func.__name__, weak_value)
-        return weak_value
+        setattr(instance, self.func.__name__, value)
+        return value
 
 
-def property_nop_write(func):
+def nop_write(prop):
     """Make this a property with a nop setter"""
     def nop(self, value):
         pass
-    return property(fget=func, fset=nop)
+    return prop.setter(nop)
 
 
 class Element(object):
diff --git a/grc/core/Element.pyi b/grc/core/Element.pyi
index c81180a33e..46c6d3480d 100644
--- a/grc/core/Element.pyi
+++ b/grc/core/Element.pyi
@@ -1,4 +1,4 @@
-# Copyright 2008, 2009, 2015, 2016 Free Software Foundation, Inc.
+# Copyright 2016 Free Software Foundation, Inc.
 # This file is part of GNU Radio
 #
 # GNU Radio Companion is free software; you can redistribute it and/or
@@ -27,17 +27,11 @@ class Element(object):
         ...
 
     @property
-    def parent(self):
+    def parent(self): -> 'Element'
         ...
 
-    def get_parent_by_type(self, cls):
-        parent = self.parent
-        if parent is None:
-            return None
-        elif isinstance(parent, cls):
-            return parent
-        else:
-            return parent.get_parent_by_type(cls)
+    def get_parent_by_type(self, cls): -> 'Element'
+        ...
 
     @lazy_property
     def parent_platform(self): -> Platform.Platform
@@ -50,5 +44,3 @@ class Element(object):
     @lazy_property
     def parent_block(self): -> Block.Block
         ...
-
-
diff --git a/grc/core/Param.py b/grc/core/Param.py
index 841f85ec86..a2effad2f9 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -20,13 +20,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import absolute_import
 
 import ast
-import weakref
 import re
 import collections
 
 from six.moves import builtins, filter, map, range, zip
 
-from . import Constants, utils
+from . import Constants
 from .Element import Element
 
 # Blacklist certain ids, its not complete, but should help
@@ -41,52 +40,6 @@ _check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
 _show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$')
 
 
-class Option(Element):
-
-    def __init__(self, param, n):
-        Element.__init__(self, param)
-        self._name = n.get('name')
-        self.key = n.get('key')
-        self._opts = dict()
-        opts = n.get('opt', [])
-        # Test against opts when non enum
-        if not self.parent.is_enum() and opts:
-            raise Exception('Options for non-enum types cannot have sub-options')
-        # Extract opts
-        for opt in opts:
-            # Separate the key:value
-            try:
-                key, value = opt.split(':')
-            except:
-                raise Exception('Error separating "{}" into key:value'.format(opt))
-            # Test against repeated keys
-            if key in self._opts:
-                raise Exception('Key "{}" already exists in option'.format(key))
-            # Store the option
-            self._opts[key] = value
-
-    def __str__(self):
-        return 'Option {}({})'.format(self.get_name(), self.key)
-
-    def get_name(self):
-        return self._name
-
-    def get_key(self):
-        return self.key
-
-    ##############################################
-    # Access Opts
-    ##############################################
-    def get_opt_keys(self):
-        return list(self._opts.keys())
-
-    def get_opt(self, key):
-        return self._opts[key]
-
-    def get_opts(self):
-        return list(self._opts.values())
-
-
 class TemplateArg(object):
     """
     A cheetah template argument created from a param.
@@ -96,10 +49,12 @@ class TemplateArg(object):
     """
 
     def __init__(self, param):
-        self._param = weakref.proxy(param)
+        self._param = param
 
     def __getitem__(self, item):
-        return str(self._param.get_opt(item)) if self._param.is_enum() else NotImplemented
+        param = self._param
+        opts = param.options_opts[param.get_value()]
+        return str(opts[item]) if param.is_enum() else NotImplemented
 
     def __str__(self):
         return str(self._param.to_code())
@@ -112,147 +67,63 @@ class Param(Element):
 
     is_param = True
 
-    def __init__(self, block, n):
-        """
-        Make a new param from nested data.
+    def __init__(self, parent, key, name, type='raw', value='', **n):
+        """Make a new param from nested data"""
+        super(Param, self).__init__(parent)
+        self.key = key
+        self._name = name
+        self.value = self.default = value
+        self._type = type
 
-        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.get('base_key')
-        if base_key and base_key in block.params:
-            n_expanded = block.params[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['name']
-        self.key = n['key']
-        value = n.get('value', '')
-        self._type = n.get('type', 'raw')
         self._hide = n.get('hide', '')
         self.tab_label = n.get('tab', Constants.DEFAULT_PARAM_TAB)
-        # Build the param
-        Element.__init__(self, parent=block)
-        # Create the Option objects from the n data
-        self._options = list()
         self._evaluated = None
-        for o_n in n.get('option', []):
-            option = Option(param=self, n=o_n)
-            key = option.key
-            # Test against repeated keys
-            if key in self.get_option_keys():
-                raise Exception('Key "{}" already exists in options'.format(key))
-            # Store the option
-            self.get_options().append(option)
-        # Test the enum options
-        if self.is_enum():
-            # Test against options with identical keys
-            if len(set(self.get_option_keys())) != len(self.get_options()):
-                raise Exception('Options keys "{}" are not unique.'.format(self.get_option_keys()))
-            # Test against inconsistent keys in options
-            opt_keys = self.get_options()[0].get_opt_keys()
-            for option in self.get_options():
-                if set(opt_keys) != set(option.get_opt_keys()):
-                    raise Exception('Opt keys "{}" are not identical across all options.'.format(opt_keys))
-            # If a value is specified, it must be in the options keys
-            if value or value in self.get_option_keys():
-                self._value = value
-            else:
-                self._value = self.get_option_keys()[0]
-            if self.get_value() not in self.get_option_keys():
-                raise Exception('The value "{}" is not in the possible values of "{}".'.format(self.get_value(), self.get_option_keys()))
-        else:
-            self._value = value or ''
-        self._default = value
+
+        self.options = []
+        self.options_names = []
+        self.options_opts = {}
+        self._init_options(options_n=n.get('option', []))
+
         self._init = False
         self._hostage_cells = list()
         self.template_arg = TemplateArg(self)
 
-    def get_types(self):
-        return (
-            'raw', 'enum',
-            'complex', 'real', 'float', 'int',
-            'complex_vector', 'real_vector', 'float_vector', 'int_vector',
-            'hex', 'string', 'bool',
-            'file_open', 'file_save', '_multiline', '_multiline_python_external',
-            'id', 'stream_id',
-            'grid_pos', 'notebook', 'gui_hint',
-            'import',
-        )
-
-    def __repr__(self):
-        """
-        Get the repr (nice string format) for this param.
-
-        Returns:
-            the string representation
-        """
-        ##################################################
-        # Truncate helper method
-        ##################################################
-        def _truncate(string, style=0):
-            max_len = max(27 - len(self.get_name()), 3)
-            if len(string) > max_len:
-                if style < 0:  # Front truncate
-                    string = '...' + string[3-max_len:]
-                elif style == 0:  # Center truncate
-                    string = string[:max_len/2 - 3] + '...' + string[-max_len/2:]
-                elif style > 0:  # Rear truncate
-                    string = string[:max_len-3] + '...'
-            return string
-
-        ##################################################
-        # Simple conditions
-        ##################################################
-        if not self.is_valid():
-            return _truncate(self.get_value())
-        if self.get_value() in self.get_option_keys():
-            return self.get_option(self.get_value()).get_name()
-
-        ##################################################
-        # Split up formatting by type
-        ##################################################
-        # Default center truncate
-        truncate = 0
-        e = self.get_evaluated()
-        t = self.get_type()
-        if isinstance(e, bool):
-            return str(e)
-        elif isinstance(e, Constants.COMPLEX_TYPES):
-            dt_str = utils.num_to_str(e)
-        elif isinstance(e, Constants.VECTOR_TYPES):
-            # Vector types
-            if len(e) > 8:
-                # Large vectors use code
-                dt_str = self.get_value()
-                truncate = 1
-            else:
-                # Small vectors use eval
-                dt_str = ', '.join(map(utils.num_to_str, e))
-        elif t in ('file_open', 'file_save'):
-            dt_str = self.get_value()
-            truncate = -1
-        else:
-            # Other types
-            dt_str = str(e)
-
-        # Done
-        return _truncate(dt_str, truncate)
-
-    def __repr2__(self):
-        """
-        Get the repr (nice string format) for this param.
+    def _init_options(self, options_n):
+        """Create the Option objects from the n data"""
+        option_keys = set()
+        for option_n in options_n:
+            key, name = option_n['key'], option_n['name']
+            # Test against repeated keys
+            if key in option_keys:
+                raise KeyError('Key "{}" already exists in options'.format(key))
+            option_keys.add(key)
+            # Store the option
+            self.options.append(key)
+            self.options_names.append(name)
 
-        Returns:
-            the string representation
-        """
         if self.is_enum():
-            return self.get_option(self.get_value()).get_name()
-        return self.get_value()
+            self._init_enum(options_n)
+
+    def _init_enum(self, options_n):
+        opt_ref = None
+        for option_n in options_n:
+            key, opts_raw = option_n['key'], option_n.get('opt', [])
+            try:
+                self.options_opts[key] = opts = dict(opt.split(':') for opt in opts_raw)
+            except TypeError:
+                raise ValueError('Error separating opts into key:value')
+
+            if opt_ref is None:
+                opt_ref = set(opts.keys())
+            elif opt_ref != set(opts):
+                raise ValueError('Opt keys ({}) are not identical across all options.'
+                                 ''.format(', '.join(opt_ref)))
+        if not self.value:
+            self.value = self.default = self.options[0]
+        elif self.value not in self.options:
+            self.value = self.default = self.options[0]  # TODO: warn
+            # raise ValueError('The value {!r} is not in the possible values of {}.'
+            #                  ''.format(self.get_value(), ', '.join(self.options)))
 
     def __str__(self):
         return 'Param - {}({})'.format(self.get_name(), self.key)
@@ -295,7 +166,7 @@ class Param(Element):
         The value must be evaluated and type must a possible type.
         """
         Element.validate(self)
-        if self.get_type() not in self.get_types():
+        if self.get_type() not in Constants.PARAM_TYPE_NAMES:
             self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
 
         self._evaluated = None
@@ -606,20 +477,20 @@ class Param(Element):
         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]
+        value = self.value
+        if self.is_enum() and value not in self.options:
+            value = self.options[0]
             self.set_value(value)
         return value
 
     def set_value(self, value):
         # Must be a string
-        self._value = str(value)
+        self.value = str(value)
 
     def set_default(self, value):
-        if self._default == self._value:
+        if self.default == self.value:
             self.set_value(value)
-        self._default = str(value)
+        self.default = str(value)
 
     def get_type(self):
         return self.parent.resolve_dependencies(self._type)
@@ -633,29 +504,8 @@ class Param(Element):
     ##############################################
     # Access Options
     ##############################################
-    def get_option_keys(self):
-        return [elem.key for elem in self._options]
-
-    def get_option(self, key):
-        for option in self._options:
-            if option.key == key:
-                return option
-        return ValueError('Key "{}" not found in {}.'.format(key, self.get_option_keys()))
-
-    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()
+    def opt_value(self, key):
+        return self.options_opts[self.get_value()][key]
 
     ##############################################
     # Import/Export Methods
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
index e32bd9198a..696281380e 100644
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@ -41,17 +41,6 @@ from .utils import extract_docs
 
 class Platform(Element):
 
-    Config = Config
-    Generator = Generator
-    FlowGraph = FlowGraph
-    Connection = Connection
-    block_classes = {
-        None: Block,  # default
-        'epy_block': EPyBlock,
-    }
-    Port = Port
-    Param = Param
-
     is_platform = True
 
     def __init__(self, *args, **kwargs):
@@ -166,6 +155,7 @@ class Platform(Element):
                 # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file)
                 pass
             except Exception as e:
+                raise
                 print('Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file), file=sys.stderr)
 
         # Add blocks to block tree
@@ -200,17 +190,18 @@ class Platform(Element):
         ParseXML.validate_dtd(xml_file, self._block_dtd)
         n = ParseXML.from_file(xml_file).get('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_classes[None](self._flow_graph, n)
-        key = block.key
+        key = n.pop('key')
+
         if key in self.blocks:
-            print('Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
-        else:  # Store the block
-            self.blocks[key] = block
-            self._blocks_n[key] = n
+            print('Warning: Block with key "{}" already exists.\n'
+                  '\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
+            return
 
+        # Store the block
+        self.blocks[key] = block = self.get_new_block(self._flow_graph, key, **n)
+        self._blocks_n[key] = n
         self._docstring_extractor.query(
-            block.key,
+            key,
             block.get_imports(raw=True),
             block.get_make(raw=True)
         )
@@ -305,12 +296,46 @@ class Platform(Element):
         ParseXML.validate_dtd(flow_graph_file, Constants.FLOW_GRAPH_DTD)
         return ParseXML.from_file(flow_graph_file)
 
-    def get_new_flow_graph(self):
-        return self.FlowGraph(platform=self)
-
     def get_blocks(self):
         return list(self.blocks.values())
 
-    def get_new_block(self, flow_graph, key):
+    def get_generate_options(self):
+        gen_opts = self.blocks['options'].get_param('generate_options')
+        generate_mode_default = gen_opts.get_value()
+        return [(key, name, key == generate_mode_default)
+                for key, name in zip(gen_opts.options, gen_opts.options_names)]
+
+    ##############################################
+    # Factories
+    ##############################################
+    Config = Config
+    Generator = Generator
+    FlowGraph = FlowGraph
+    Connection = Connection
+    block_classes = {
+        None: Block,  # default
+        'epy_block': EPyBlock,
+    }
+    port_classes = {
+        None: Port,  # default
+    }
+    param_classes = {
+        None: Param,  # default
+    }
+
+    def get_new_flow_graph(self):
+        return self.FlowGraph(platform=self)
+
+    def get_new_block(self, parent, key, **kwargs):
         cls = self.block_classes.get(key, self.block_classes[None])
-        return cls(flow_graph, n=self._blocks_n[key])
+        if not kwargs:
+            kwargs = self._blocks_n[key]
+        return cls(parent, key=key, **kwargs)
+
+    def get_new_param(self, parent, **kwargs):
+        cls = self.param_classes[None]
+        return cls(parent, **kwargs)
+
+    def get_new_port(self, parent, **kwargs):
+        cls = self.port_classes[None]
+        return cls(parent, **kwargs)
\ No newline at end of file
diff --git a/grc/core/Port.py b/grc/core/Port.py
index ef6a92c7b1..9030bef375 100644
--- a/grc/core/Port.py
+++ b/grc/core/Port.py
@@ -105,7 +105,7 @@ class Port(Element):
 
     is_port = True
 
-    def __init__(self, block, n, dir):
+    def __init__(self, block, direction, **n):
         """
         Make a new port from nested data.
 
@@ -117,6 +117,7 @@ class Port(Element):
         self._n = n
         if n['type'] == 'message':
             n['domain'] = Constants.GR_MESSAGE_DOMAIN
+
         if 'domain' not in n:
             n['domain'] = Constants.DEFAULT_DOMAIN
         elif n['domain'] == Constants.GR_MESSAGE_DOMAIN:
@@ -125,8 +126,6 @@ class Port(Element):
         if n['type'] == 'msg':
             n['key'] = 'msg'
 
-        n.setdefault('key', str(next(block.port_counters[dir == 'source'])))
-
         # Build the port
         Element.__init__(self, parent=block)
         # Grab the data
@@ -135,13 +134,12 @@ class Port(Element):
         self._type = n.get('type', '')
         self.domain = n.get('domain')
         self._hide = n.get('hide', '')
-        self._dir = dir
+        self._dir = direction
         self._hide_evaluated = False  # Updated on rewrite()
 
         self._nports = n.get('nports', '')
         self._vlen = n.get('vlen', '')
         self._optional = bool(n.get('optional'))
-        self.di_optional = bool(n.get('optional'))
         self._clones = []  # References to cloned ports (for nports > 1)
 
     def __str__(self):
@@ -150,15 +148,12 @@ class Port(Element):
         if self.is_sink:
             return 'Sink - {}({})'.format(self.get_name(), self.key)
 
-    def get_types(self):
-        return list(Constants.TYPE_TO_SIZEOF.keys())
-
     def is_type_empty(self):
         return not self._n['type']
 
     def validate(self):
         Element.validate(self)
-        if self.get_type() not in self.get_types():
+        if self.get_type() not in Constants.TYPE_TO_SIZEOF.keys():
             self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
         platform = self.parent.parent.parent
         if self.domain not in platform.domains:
diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py
index d5d31c0dce..678be4433e 100644
--- a/grc/core/generator/FlowGraphProxy.py
+++ b/grc/core/generator/FlowGraphProxy.py
@@ -70,7 +70,7 @@ class FlowGraphProxy(object):
                 'label': str(pad.get_param('label').get_evaluated()),
                 'type': str(pad.get_param('type').get_evaluated()),
                 'vlen': str(pad.get_param('vlen').get_value()),
-                'size': pad.get_param('type').get_opt('size'),
+                'size': pad.get_param('type').opt_value('size'),
                 'optional': bool(pad.get_param('optional').get_evaluated()),
             }
             num_ports = pad.get_param('num_streams').get_evaluated()
diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py
index 4b1717b930..d095179a10 100644
--- a/grc/core/utils/__init__.py
+++ b/grc/core/utils/__init__.py
@@ -22,4 +22,3 @@ from . import epy_block_io
 from . import extract_docs
 
 from ._complexity import calculate_flowgraph_complexity
-from ._num_to_str import num_to_str
diff --git a/grc/core/utils/_num_to_str.py b/grc/core/utils/_num_to_str.py
deleted file mode 100644
index 92b2167bb5..0000000000
--- a/grc/core/utils/_num_to_str.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-"""${FILE_NAME}"""
-from grc.core import Constants
-
-__author__ = "Sebastian Koslowski"
-__email__ = "sebastian.koslowski@gmail.com"
-__copyright__ = "Copyright 2016, Sebastian Koslowski"
-
-
-def num_to_str(num):
-    """ Display logic for numbers """
-    def eng_notation(value, fmt='g'):
-        """Convert a number to a string in engineering notation.  E.g., 5e-9 -> 5n"""
-        template = '{:' + fmt + '}{}'
-        magnitude = abs(value)
-        for exp, symbol in zip(range(9, -15-1, -3), 'GMk munpf'):
-            factor = 10 ** exp
-            if magnitude >= factor:
-                return template.format(value / factor, symbol.strip())
-        return template.format(value, '')
-
-    if isinstance(num, Constants.COMPLEX_TYPES):
-        num = complex(num)  # Cast to python complex
-        if num == 0:
-            return '0'
-        output = eng_notation(num.real) if num.real else ''
-        output += eng_notation(num.imag, '+g' if output else 'g') + 'j' if num.imag else ''
-        return output
-    else:
-        return str(num)
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index b55e471e37..c8611933f0 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -36,12 +36,12 @@ from ..core.Block import Block as CoreBlock
 class Block(CoreBlock, Element):
     """The graphical signal block."""
 
-    def __init__(self, flow_graph, n):
+    def __init__(self, parent, **n):
         """
         Block constructor.
         Add graphics related params to the block.
         """
-        super(self.__class__, self).__init__(flow_graph, n)
+        super(self.__class__, self).__init__(parent, **n)
 
         self.states.update(_coordinate=(0, 0), _rotation=0)
         self.width = self.height = 0
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index 6122ec13b4..87dd97a520 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -23,7 +23,7 @@ from . import Colors, Utils
 from .Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
 from .Element import Element
 
-from ..core.Element import property_nop_write
+from ..core.Element import nop_write
 from ..core.Connection import Connection as _Connection
 
 
@@ -46,11 +46,13 @@ class Connection(Element, _Connection):
         self._sink_rot = self._source_rot = None
         self._sink_coor = self._source_coor = None
 
-    @property_nop_write
+    @nop_write
+    @property
     def coordinate(self):
         return self.source_port.get_connector_coordinate()
 
-    @property_nop_write
+    @nop_write
+    @property
     def rotation(self):
         """
         Get the 0 degree rotation.
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index bd07a667d4..97f9033974 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -41,7 +41,6 @@ from ..core import Messages
 ############################################################
 # Main window
 ############################################################
-
 class MainWindow(Gtk.Window):
     """The topmost window with menus, the tool bar, and other major windows."""
 
@@ -56,14 +55,6 @@ class MainWindow(Gtk.Window):
         Setup the menu, toolbar, flow graph editor notebook, block selection window...
         """
         self._platform = platform
-
-        gen_opts = platform.blocks['options'].get_param('generate_options')
-        generate_mode_default = gen_opts.get_value()
-        generate_modes = [
-            (o.key, o.get_name(), o.key == generate_mode_default)
-            for o in gen_opts.get_options()]
-
-        # Load preferences
         Preferences.load(platform)
 
         # Setup window
@@ -72,6 +63,7 @@ class MainWindow(Gtk.Window):
         self.add(vbox)
 
         # Create the menu bar and toolbar
+        generate_modes = platform.get_generate_options()
         self.add_accel_group(Actions.get_accel_group())
         self.menu_bar = Bars.MenuBar(generate_modes, action_handler_callback)
         vbox.pack_start(self.menu_bar, False, False, 0)
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index 9d5b55f339..f688d2aad4 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -19,6 +19,7 @@ from __future__ import absolute_import
 from . import Utils, Constants
 
 from . import ParamWidgets
+from .Element import Element
 
 from ..core.Param import Param as _Param
 
@@ -26,6 +27,8 @@ from ..core.Param import Param as _Param
 class Param(_Param):
     """The graphical parameter."""
 
+    make_cls_with_base = classmethod(Element.make_cls_with_base.__func__)
+
     def get_input(self, *args, **kwargs):
         """
         Get the graphical gtk class to represent this parameter.
@@ -36,19 +39,20 @@ class Param(_Param):
         Returns:
             gtk input class
         """
-        if self.get_type() in ('file_open', 'file_save'):
+        type_ = self.get_type()
+        if type_ in ('file_open', 'file_save'):
             input_widget_cls = ParamWidgets.FileParam
 
         elif self.is_enum():
             input_widget_cls = ParamWidgets.EnumParam
 
-        elif self.get_options():
+        elif self.options:
             input_widget_cls = ParamWidgets.EnumEntryParam
 
-        elif self.get_type() == '_multiline':
+        elif type_ == '_multiline':
             input_widget_cls = ParamWidgets.MultiLineEntryParam
 
-        elif self.get_type() == '_multiline_python_external':
+        elif type_ == '_multiline_python_external':
             input_widget_cls = ParamWidgets.PythonEditorParam
 
         else:
@@ -66,8 +70,8 @@ class Param(_Param):
         return '<span underline="{line}" foreground="{color}" font_desc="Sans 9">{label}</span>'.format(
             line='low' if has_callback else 'none',
             color='blue' if have_pending_changes else
-            'black' if self.is_valid() else
-            'red',
+                  'black' if self.is_valid() else
+                  'red',
             label=Utils.encode(self.get_name())
         )
 
@@ -86,6 +90,66 @@ class Param(_Param):
             tooltip_lines.extend(' * ' + msg for msg in errors)
         return '\n'.join(tooltip_lines)
 
+    def pretty_print(self):
+        """
+        Get the repr (nice string format) for this param.
+
+        Returns:
+            the string representation
+        """
+        ##################################################
+        # Truncate helper method
+        ##################################################
+        def _truncate(string, style=0):
+            max_len = max(27 - len(self.get_name()), 3)
+            if len(string) > max_len:
+                if style < 0:  # Front truncate
+                    string = '...' + string[3-max_len:]
+                elif style == 0:  # Center truncate
+                    string = string[:max_len/2 - 3] + '...' + string[-max_len/2:]
+                elif style > 0:  # Rear truncate
+                    string = string[:max_len-3] + '...'
+            return string
+
+        ##################################################
+        # Simple conditions
+        ##################################################
+        value = self.get_value()
+        if not self.is_valid():
+            return _truncate(value)
+        if value in self.options:
+            return self.options_names[self.options.index(value)]
+
+        ##################################################
+        # Split up formatting by type
+        ##################################################
+        # Default center truncate
+        truncate = 0
+        e = self.get_evaluated()
+        t = self.get_type()
+        if isinstance(e, bool):
+            return str(e)
+        elif isinstance(e, Constants.COMPLEX_TYPES):
+            dt_str = Utils.num_to_str(e)
+        elif isinstance(e, Constants.VECTOR_TYPES):
+            # Vector types
+            if len(e) > 8:
+                # Large vectors use code
+                dt_str = self.get_value()
+                truncate = 1
+            else:
+                # Small vectors use eval
+                dt_str = ', '.join(map(Utils.num_to_str, e))
+        elif t in ('file_open', 'file_save'):
+            dt_str = self.get_value()
+            truncate = -1
+        else:
+            # Other types
+            dt_str = str(e)
+
+        # Done
+        return _truncate(dt_str, truncate)
+
     def format_block_surface_markup(self):
         """
         Get the markup for this param.
@@ -95,5 +159,6 @@ class Param(_Param):
         """
         return '<span foreground="{color}" font_desc="{font}"><b>{label}:</b> {value}</span>'.format(
             color='black' if self.is_valid() else 'red', font=Constants.PARAM_FONT,
-            label=Utils.encode(self.get_name()), value=Utils.encode(repr(self).replace('\n', ' '))
+            label=Utils.encode(self.get_name()),
+            value=Utils.encode(self.pretty_print().replace('\n', ' '))
         )
diff --git a/grc/gui/ParamWidgets.py b/grc/gui/ParamWidgets.py
index 0c5728feed..4e6ba27ac9 100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@ -188,11 +188,11 @@ class EnumParam(InputParam):
     def __init__(self, *args, **kwargs):
         InputParam.__init__(self, *args, **kwargs)
         self._input = Gtk.ComboBoxText()
-        for option in self.param.get_options():
-            self._input.append_text(option.get_name())
+        for option_name in self.param.options_names:
+            self._input.append_text(option_name)
 
         value = self.param.get_value()
-        active_index = self.param.get_option_keys().index(value)
+        active_index = self.param.options.index(value)
         self._input.set_active(active_index)
 
         self._input.connect('changed', self._editing_callback)
@@ -200,7 +200,7 @@ class EnumParam(InputParam):
         self.pack_start(self._input, False, False, 0)
 
     def get_text(self):
-        return self.param.get_option_keys()[self._input.get_active()]
+        return self.param.options[self._input.get_active()]
 
     def set_tooltip_text(self, text):
         self._input.set_tooltip_text(text)
@@ -212,12 +212,12 @@ class EnumEntryParam(InputParam):
     def __init__(self, *args, **kwargs):
         InputParam.__init__(self, *args, **kwargs)
         self._input = Gtk.ComboBoxText.new_with_entry()
-        for option in self.param.get_options():
-            self._input.append_text(option.get_name())
+        for option_name in self.param.options_names:
+            self._input.append_text(option_name)
 
         value = self.param.get_value()
         try:
-            active_index = self.param.get_option_keys().index(value)
+            active_index = self.param.options.index(value)
             self._input.set_active(active_index)
         except ValueError:
             self._input.set_active(-1)
@@ -237,7 +237,7 @@ class EnumEntryParam(InputParam):
         if self.has_custom_value:
             return self._input.get_child().get_text()
         else:
-            return self.param.get_option_keys()[self._input.get_active()]
+            return self.param.options[self._input.get_active()]
 
     def set_tooltip_text(self, text):
         if self.has_custom_value:  # custom entry
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index b8dd6aa074..6a2a13f644 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -61,12 +61,14 @@ class Platform(CorePlatform):
                 print(e, file=sys.stderr)
 
     ##############################################
-    # Constructors
+    # Factories
     ##############################################
     Config = Config
     FlowGraph = FlowGraph
     Connection = Connection
     block_classes = {key: Block.make_cls_with_base(cls)
                      for key, cls in CorePlatform.block_classes.items()}
-    Port = Port
-    Param = Param
+    port_classes = {key: Port.make_cls_with_base(cls)
+                    for key, cls in CorePlatform.port_classes.items()}
+    param_classes = {key: Param.make_cls_with_base(cls)
+                     for key, cls in CorePlatform.param_classes.items()}
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index 4b3f6edb81..db3ab9da23 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -25,19 +25,19 @@ from gi.repository import Gtk, PangoCairo, Pango
 from . import Actions, Colors, Utils, Constants
 from .Element import Element
 
-from ..core.Element import property_nop_write
+from ..core.Element import nop_write
 from ..core.Port import Port as _Port
 
 
 class Port(_Port, Element):
     """The graphical port."""
 
-    def __init__(self, block, n, dir):
+    def __init__(self, parent, direction, **n):
         """
         Port constructor.
         Create list of connector coordinates.
         """
-        super(Port, self).__init__(block, n, dir)
+        super(self.__class__, self).__init__(parent, direction, **n)
         Element.__init__(self)
         self._connector_coordinate = (0, 0)
         self._hovering = True
@@ -155,7 +155,8 @@ class Port(_Port, Element):
         elif self.is_sink:
             return (self.rotation + 180) % 360
 
-    @property_nop_write
+    @nop_write
+    @property
     def rotation(self):
         return self.parent_block.rotation
 
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index 304c8b8411..d474c66f19 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -21,7 +21,7 @@ from __future__ import absolute_import
 
 from gi.repository import GLib
 
-from .Constants import POSSIBLE_ROTATIONS, CANVAS_GRID_SIZE
+from .Constants import POSSIBLE_ROTATIONS, CANVAS_GRID_SIZE, COMPLEX_TYPES
 
 
 def get_rotated_coordinate(coor, rotation):
@@ -66,16 +66,6 @@ def get_angle_from_coordinates(p1, p2):
         return 270 if y2 > y1 else 90
 
 
-def encode(value):
-    """Make sure that we pass only valid utf-8 strings into markup_escape_text.
-
-    Older versions of glib seg fault if the last byte starts a multi-byte
-    character.
-    """
-    valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8')
-    return GLib.markup_escape_text(valid_utf8)
-
-
 def align_to_grid(coor, mode=round):
     def align(value):
         return int(mode(value / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
@@ -84,3 +74,36 @@ def align_to_grid(coor, mode=round):
     except TypeError:
         x = coor
         return align(coor)
+
+
+def num_to_str(num):
+    """ Display logic for numbers """
+    def eng_notation(value, fmt='g'):
+        """Convert a number to a string in engineering notation.  E.g., 5e-9 -> 5n"""
+        template = '{:' + fmt + '}{}'
+        magnitude = abs(value)
+        for exp, symbol in zip(range(9, -15-1, -3), 'GMk munpf'):
+            factor = 10 ** exp
+            if magnitude >= factor:
+                return template.format(value / factor, symbol.strip())
+        return template.format(value, '')
+
+    if isinstance(num, COMPLEX_TYPES):
+        num = complex(num)  # Cast to python complex
+        if num == 0:
+            return '0'
+        output = eng_notation(num.real) if num.real else ''
+        output += eng_notation(num.imag, '+g' if output else 'g') + 'j' if num.imag else ''
+        return output
+    else:
+        return str(num)
+
+
+def encode(value):
+    """Make sure that we pass only valid utf-8 strings into markup_escape_text.
+
+    Older versions of glib seg fault if the last byte starts a multi-byte
+    character.
+    """
+    valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8')
+    return GLib.markup_escape_text(valid_utf8)
-- 
cgit v1.2.3


From 5d4baf530af8ca843dcfe310af3bd34d4cf7c3ad Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Wed, 27 Jul 2016 22:24:15 +0200
Subject: grc: remove support for old msg queues

---
 grc/core/Connection.py             |  3 ---
 grc/core/Constants.py              |  1 -
 grc/core/Port.py                   |  8 --------
 grc/core/generator/Generator.py    |  9 +++------
 grc/core/generator/flow_graph.tmpl | 13 -------------
 grc/gui/Port.py                    |  2 +-
 6 files changed, 4 insertions(+), 32 deletions(-)

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

diff --git a/grc/core/Connection.py b/grc/core/Connection.py
index 63c6a94571..066532149b 100644
--- a/grc/core/Connection.py
+++ b/grc/core/Connection.py
@@ -101,9 +101,6 @@ class Connection(Element):
             self.source_block, self.source_port, self.sink_block, self.sink_port,
         )
 
-    def is_msg(self):
-        return self.source_port.get_type() == self.sink_port.get_type() == 'msg'
-
     def is_bus(self):
         return self.source_port.get_type() == 'bus'
 
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index bc387a4837..13e29cb533 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -118,7 +118,6 @@ CORE_TYPES = (  # name, key, sizeof, color
     ('Integer 16',           's16',  2, GRC_COLOR_YELLOW),
     ('Integer 8',             's8',  1, GRC_COLOR_PURPLE_A400),
     ('Bits (unpacked byte)', 'bit',  1, GRC_COLOR_PURPLE_A100),
-    ('Message Queue',        'msg',  0, GRC_COLOR_DARK_GREY),
     ('Async Message',    'message',  0, GRC_COLOR_GREY),
     ('Bus Connection',       'bus',  0, GRC_COLOR_WHITE),
     ('Wildcard',                '',  0, GRC_COLOR_WHITE),
diff --git a/grc/core/Port.py b/grc/core/Port.py
index 0d9298fb05..41388df409 100644
--- a/grc/core/Port.py
+++ b/grc/core/Port.py
@@ -124,8 +124,6 @@ class Port(Element):
         elif n['domain'] == Constants.GR_MESSAGE_DOMAIN:
             n['key'] = n['name']
             n['type'] = 'message'  # For port color
-        if n['type'] == 'msg':
-            n['key'] = 'msg'
 
         # Build the port
         Element.__init__(self, parent)
@@ -159,12 +157,6 @@ class Port(Element):
             self.add_error_message('Domain key "{}" is not registered.'.format(self.domain))
         if not self.get_enabled_connections() and not self.get_optional():
             self.add_error_message('Port is not connected.')
-        # Message port logic
-        if self.get_type() == 'msg':
-            if self.get_nports():
-                self.add_error_message('A port of type "msg" cannot have "nports" set.')
-            if self.get_vlen() != 1:
-                self.add_error_message('A port of type "msg" must have a "vlen" of 1.')
 
     def rewrite(self):
         """
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index a3b0c8af43..a9ce933aeb 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -165,10 +165,9 @@ class TopBlockGenerator(object):
                 src = block.get_param('source_code').get_value()
                 output.append((file_path, src))
 
-        # Filter out virtual sink connections
-        def cf(c):
-            return not (c.is_bus() or c.is_msg() or c.sink_block.is_virtual_sink())
-        connections = [con for con in fg.get_enabled_connections() if cf(con)]
+        # Filter out bus and virtual sink connections
+        connections = [con for con in fg.get_enabled_connections()
+                       if not (con.is_bus() or con.sink_block.is_virtual_sink())]
 
         # Get the virtual blocks and resolve their connections
         connection_factory = fg.parent_platform.Connection
@@ -216,7 +215,6 @@ class TopBlockGenerator(object):
         ))
 
         connection_templates = fg.parent.connection_templates
-        msgs = [c for c in fg.get_enabled_connections() if c.is_msg()]
 
         # List of variable names
         var_ids = [var.get_id() for var in parameters + variables]
@@ -245,7 +243,6 @@ class TopBlockGenerator(object):
             'blocks': blocks,
             'connections': connections,
             'connection_templates': connection_templates,
-            'msgs': msgs,
             'generate_options': self._generate_options,
             'callbacks': callbacks,
         }
diff --git a/grc/core/generator/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl
index 4ce2a513c3..fd5546e459 100644
--- a/grc/core/generator/flow_graph.tmpl
+++ b/grc/core/generator/flow_graph.tmpl
@@ -11,7 +11,6 @@
 ##@param parameters the parameter blocks
 ##@param blocks the signal blocks
 ##@param connections the connections
-##@param msgs the msg type connections
 ##@param generate_options the type of flow graph
 ##@param callbacks variable id map to callback strings
 ########################################################
@@ -200,18 +199,6 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])
         $indent($var.get_var_make())
 #end for
 ########################################################
-##Create Message Queues
-########################################################
-#if $msgs
-
-        $DIVIDER
-        # Message Queues
-        $DIVIDER
-#end if
-#for $msg in $msgs
-        $(msg.source_block.get_id())_msgq_out = $(msg.sink_block.get_id())_msgq_in = gr.msg_queue(2)
-#end for
-########################################################
 ##Create Blocks
 ########################################################
 #if $blocks
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index 0880856b00..21271cc4d6 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -71,7 +71,7 @@ class Port(_Port, Element):
         Returns:
             a hex color code.
         """
-        color = Colors.PORT_TYPE_TO_COLOR[self.get_type()]
+        color = Colors.PORT_TYPE_TO_COLOR.get(self.get_type()) or Colors.PORT_TYPE_TO_COLOR.get('')
         vlen = self.get_vlen()
         if vlen > 1:
             dark = (0, 0, 30 / 255.0, 50 / 255.0, 70 / 255.0)[min(4, vlen)]
-- 
cgit v1.2.3


From 7f7fa2f91467fdb2b11312be8562e7b51fdeb199 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <sebastian.koslowski@gmail.com>
Date: Tue, 3 May 2016 17:13:08 +0200
Subject: grc: added yaml/mako support

Includes basic converter from XML/Cheetah to YAML/Mako based block format.
---
 gr-digital/examples/demod/pam_timing.grc | 2629 ++++++++++++++++--------------
 grc/CMakeLists.txt                       |    9 +-
 grc/blocks/block_tree.xml                |    6 -
 grc/blocks/bus_sink.xml                  |   27 -
 grc/blocks/bus_source.xml                |   27 -
 grc/blocks/bus_structure_sink.xml        |   18 -
 grc/blocks/bus_structure_source.xml      |   18 -
 grc/blocks/epy_block.xml                 |   58 -
 grc/blocks/epy_module.xml                |   32 -
 grc/blocks/gr_message_domain.xml         |   19 -
 grc/blocks/gr_stream_domain.xml          |   18 -
 grc/blocks/import.xml                    |    4 +-
 grc/blocks/message.domain.yml            |   10 +
 grc/blocks/options.xml                   |    4 +-
 grc/blocks/stream.domain.yml             |   10 +
 grc/compiler.py                          |    4 +-
 grc/converter/__init__.py                |   20 +
 grc/converter/__main__.py                |   21 +
 grc/converter/block.dtd                  |   69 +
 grc/converter/block.py                   |  219 +++
 grc/converter/block_tree.dtd             |   26 +
 grc/converter/block_tree.py              |   56 +
 grc/converter/cheetah_converter.py       |  277 ++++
 grc/converter/flow_graph.dtd             |   38 +
 grc/converter/flow_graph.py              |  131 ++
 grc/converter/main.py                    |  163 ++
 grc/converter/xml.py                     |   82 +
 grc/core/Block.py                        |  784 ---------
 grc/core/Config.py                       |    9 +-
 grc/core/Connection.py                   |  113 +-
 grc/core/Constants.py                    |   15 +-
 grc/core/Element.py                      |  180 --
 grc/core/Element.pyi                     |   41 -
 grc/core/FlowGraph.py                    |  289 ++--
 grc/core/Param.py                        |  481 +++---
 grc/core/Platform.py                     |  341 ----
 grc/core/Port.py                         |  391 -----
 grc/core/base.py                         |  164 ++
 grc/core/block.dtd                       |   69 -
 grc/core/block_tree.dtd                  |   26 -
 grc/core/blocks/__init__.py              |   37 +
 grc/core/blocks/_build.py                |   69 +
 grc/core/blocks/_flags.py                |   39 +
 grc/core/blocks/_templates.py            |   77 +
 grc/core/blocks/block.py                 |  416 +++++
 grc/core/blocks/dummy.py                 |   54 +
 grc/core/blocks/embedded_python.py       |  242 +++
 grc/core/blocks/virtual.py               |   76 +
 grc/core/domain.dtd                      |   35 -
 grc/core/errors.py                       |   30 +
 grc/core/generator/FlowGraphProxy.py     |   54 +-
 grc/core/generator/Generator.py          |  354 +---
 grc/core/generator/flow_graph.py.mako    |  352 ++++
 grc/core/generator/flow_graph.tmpl       |  420 -----
 grc/core/generator/hier_block.py         |  196 +++
 grc/core/generator/top_block.py          |  285 ++++
 grc/core/io/__init__.py                  |   16 +
 grc/core/io/yaml.py                      |   91 ++
 grc/core/platform.py                     |  430 +++++
 grc/core/ports/__init__.py               |   23 +
 grc/core/ports/_virtual_connections.py   |  126 ++
 grc/core/ports/clone.py                  |   38 +
 grc/core/ports/port.py                   |  207 +++
 grc/core/schema_checker/__init__.py      |    5 +
 grc/core/schema_checker/block.py         |   57 +
 grc/core/schema_checker/domain.py        |   16 +
 grc/core/schema_checker/flow_graph.py    |   23 +
 grc/core/schema_checker/utils.py         |   27 +
 grc/core/schema_checker/validator.py     |  102 ++
 grc/core/utils/__init__.py               |    8 +-
 grc/core/utils/_complexity.py            |   50 -
 grc/core/utils/backports/__init__.py     |   25 +
 grc/core/utils/backports/chainmap.py     |  106 ++
 grc/core/utils/backports/shlex.py        |   47 +
 grc/core/utils/descriptors/__init__.py   |   26 +
 grc/core/utils/descriptors/_lazy.py      |   39 +
 grc/core/utils/descriptors/evaluated.py  |  112 ++
 grc/core/utils/expr_utils.py             |  158 +-
 grc/core/utils/extract_docs.py           |    8 +-
 grc/core/utils/flow_graph_complexity.py  |   50 +
 grc/core/utils/shlex.py                  |   47 -
 grc/gui/Application.py                   |   71 +-
 grc/gui/BlockTreeWindow.py               |    4 +-
 grc/gui/Dialogs.py                       |   10 +-
 grc/gui/MainWindow.py                    |    2 +-
 grc/gui/ParamWidgets.py                  |   28 +-
 grc/gui/Platform.py                      |   14 +-
 grc/gui/PropsDialog.py                   |   44 +-
 grc/gui/VariableEditor.py                |   22 +-
 grc/gui/canvas/block.py                  |   54 +-
 grc/gui/canvas/connection.py             |    9 +-
 grc/gui/canvas/flowgraph.py              |   24 +-
 grc/gui/canvas/param.py                  |   20 +-
 grc/gui/canvas/port.py                   |   22 +-
 grc/main.py                              |    1 +
 grc/tests/__init__.py                    |    0
 grc/tests/resources/file1.xml            |   58 +
 grc/tests/resources/file2.xml            |   80 +
 grc/tests/resources/file3.xml            |  100 ++
 grc/tests/test_block_flags.py            |   26 +
 grc/tests/test_block_templates.py        |   45 +
 grc/tests/test_cheetah_converter.py      |  132 ++
 grc/tests/test_evaled_property.py        |  104 ++
 grc/tests/test_expr_utils.py             |   41 +
 grc/tests/test_generator.py              |   46 +
 grc/tests/test_xml_parser.py             |   39 +
 grc/tests/test_yaml_checker.py           |   84 +
 107 files changed, 7589 insertions(+), 4962 deletions(-)
 delete mode 100644 grc/blocks/bus_sink.xml
 delete mode 100644 grc/blocks/bus_source.xml
 delete mode 100644 grc/blocks/bus_structure_sink.xml
 delete mode 100644 grc/blocks/bus_structure_source.xml
 delete mode 100644 grc/blocks/epy_block.xml
 delete mode 100644 grc/blocks/epy_module.xml
 delete mode 100644 grc/blocks/gr_message_domain.xml
 delete mode 100644 grc/blocks/gr_stream_domain.xml
 create mode 100644 grc/blocks/message.domain.yml
 create mode 100644 grc/blocks/stream.domain.yml
 create mode 100644 grc/converter/__init__.py
 create mode 100644 grc/converter/__main__.py
 create mode 100644 grc/converter/block.dtd
 create mode 100644 grc/converter/block.py
 create mode 100644 grc/converter/block_tree.dtd
 create mode 100644 grc/converter/block_tree.py
 create mode 100644 grc/converter/cheetah_converter.py
 create mode 100644 grc/converter/flow_graph.dtd
 create mode 100644 grc/converter/flow_graph.py
 create mode 100644 grc/converter/main.py
 create mode 100644 grc/converter/xml.py
 delete mode 100644 grc/core/Block.py
 delete mode 100644 grc/core/Element.py
 delete mode 100644 grc/core/Element.pyi
 delete mode 100644 grc/core/Platform.py
 delete mode 100644 grc/core/Port.py
 create mode 100644 grc/core/base.py
 delete mode 100644 grc/core/block.dtd
 delete mode 100644 grc/core/block_tree.dtd
 create mode 100644 grc/core/blocks/__init__.py
 create mode 100644 grc/core/blocks/_build.py
 create mode 100644 grc/core/blocks/_flags.py
 create mode 100644 grc/core/blocks/_templates.py
 create mode 100644 grc/core/blocks/block.py
 create mode 100644 grc/core/blocks/dummy.py
 create mode 100644 grc/core/blocks/embedded_python.py
 create mode 100644 grc/core/blocks/virtual.py
 delete mode 100644 grc/core/domain.dtd
 create mode 100644 grc/core/errors.py
 create mode 100644 grc/core/generator/flow_graph.py.mako
 delete mode 100644 grc/core/generator/flow_graph.tmpl
 create mode 100644 grc/core/generator/hier_block.py
 create mode 100644 grc/core/generator/top_block.py
 create mode 100644 grc/core/io/__init__.py
 create mode 100644 grc/core/io/yaml.py
 create mode 100644 grc/core/platform.py
 create mode 100644 grc/core/ports/__init__.py
 create mode 100644 grc/core/ports/_virtual_connections.py
 create mode 100644 grc/core/ports/clone.py
 create mode 100644 grc/core/ports/port.py
 create mode 100644 grc/core/schema_checker/__init__.py
 create mode 100644 grc/core/schema_checker/block.py
 create mode 100644 grc/core/schema_checker/domain.py
 create mode 100644 grc/core/schema_checker/flow_graph.py
 create mode 100644 grc/core/schema_checker/utils.py
 create mode 100644 grc/core/schema_checker/validator.py
 delete mode 100644 grc/core/utils/_complexity.py
 create mode 100644 grc/core/utils/backports/__init__.py
 create mode 100644 grc/core/utils/backports/chainmap.py
 create mode 100644 grc/core/utils/backports/shlex.py
 create mode 100644 grc/core/utils/descriptors/__init__.py
 create mode 100644 grc/core/utils/descriptors/_lazy.py
 create mode 100644 grc/core/utils/descriptors/evaluated.py
 create mode 100644 grc/core/utils/flow_graph_complexity.py
 delete mode 100644 grc/core/utils/shlex.py
 create mode 100644 grc/tests/__init__.py
 create mode 100644 grc/tests/resources/file1.xml
 create mode 100644 grc/tests/resources/file2.xml
 create mode 100644 grc/tests/resources/file3.xml
 create mode 100644 grc/tests/test_block_flags.py
 create mode 100644 grc/tests/test_block_templates.py
 create mode 100644 grc/tests/test_cheetah_converter.py
 create mode 100644 grc/tests/test_evaled_property.py
 create mode 100644 grc/tests/test_expr_utils.py
 create mode 100644 grc/tests/test_generator.py
 create mode 100644 grc/tests/test_xml_parser.py
 create mode 100644 grc/tests/test_yaml_checker.py

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

diff --git a/gr-digital/examples/demod/pam_timing.grc b/gr-digital/examples/demod/pam_timing.grc
index cb1f1ad981..3c0fcfeda0 100644
--- a/gr-digital/examples/demod/pam_timing.grc
+++ b/gr-digital/examples/demod/pam_timing.grc
@@ -1,22 +1,23 @@
-<?xml version='1.0' encoding='ASCII'?>
+<?xml version='1.0' encoding='utf-8'?>
+<?grc format='1' created='3.7.10'?>
 <flow_graph>
   <timestamp>Sat Jul 12 13:50:56 2014</timestamp>
   <block>
     <key>options</key>
     <param>
-      <key>id</key>
-      <value>pam_timing</value>
+      <key>author</key>
+      <value></value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>window_size</key>
+      <value>1280, 1024</value>
     </param>
     <param>
-      <key>title</key>
-      <value></value>
+      <key>category</key>
+      <value>Custom</value>
     </param>
     <param>
-      <key>author</key>
+      <key>comment</key>
       <value></value>
     </param>
     <param>
@@ -24,64 +25,72 @@
       <value></value>
     </param>
     <param>
-      <key>window_size</key>
-      <value>1280, 1024</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>generate_options</key>
-      <value>qt_gui</value>
+      <key>_coordinate</key>
+      <value>(-1, 0)</value>
     </param>
     <param>
-      <key>category</key>
-      <value>Custom</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>run_options</key>
-      <value>prompt</value>
+      <key>generate_options</key>
+      <value>qt_gui</value>
     </param>
     <param>
-      <key>run</key>
-      <value>True</value>
+      <key>hier_block_src_path</key>
+      <value>.:</value>
+    </param>
+    <param>
+      <key>id</key>
+      <value>pam_timing</value>
     </param>
     <param>
       <key>max_nouts</key>
       <value>0</value>
     </param>
     <param>
-      <key>realtime_scheduling</key>
+      <key>qt_qss_theme</key>
       <value></value>
     </param>
     <param>
-      <key>alias</key>
+      <key>realtime_scheduling</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(-1, 0)</value>
+      <key>run_command</key>
+      <value>{python} -u {filename}</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>run_options</key>
+      <value>prompt</value>
     </param>
-  </block>
-  <block>
-    <key>variable</key>
     <param>
-      <key>id</key>
-      <value>const</value>
+      <key>run</key>
+      <value>True</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>thread_safe_setters</key>
+      <value></value>
     </param>
     <param>
-      <key>value</key>
-      <value>digital.qpsk_constellation()</value>
+      <key>title</key>
+      <value></value>
     </param>
+  </block>
+  <block>
+    <key>variable</key>
     <param>
-      <key>alias</key>
+      <key>comment</key>
       <value></value>
     </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
     <param>
       <key>_coordinate</key>
       <value>(206, 116)</value>
@@ -90,133 +99,151 @@
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>variable</key>
     <param>
       <key>id</key>
-      <value>sig_amp</value>
+      <value>const</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>value</key>
+      <value>digital.qpsk_constellation()</value>
+    </param>
+  </block>
+  <block>
+    <key>variable_qtgui_range</key>
+    <param>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
       <key>value</key>
-      <value>1</value>
+      <value>0</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
       <key>_coordinate</key>
-      <value>(791, 46)</value>
+      <value>(268, 527)</value>
+    </param>
+    <param>
+      <key>gui_hint</key>
+      <value>4,2,1,1</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>variable</key>
     <param>
       <key>id</key>
-      <value>samp_rate</value>
+      <value>freq_offset</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>label</key>
+      <value>Frequency Offset</value>
     </param>
     <param>
-      <key>value</key>
-      <value>32000</value>
+      <key>min_len</key>
+      <value>200</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>orient</key>
+      <value>Qt.Horizontal</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(267, 357)</value>
+      <key>start</key>
+      <value>-.5</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>step</key>
+      <value>.01</value>
     </param>
-  </block>
-  <block>
-    <key>variable</key>
     <param>
-      <key>id</key>
-      <value>spb</value>
+      <key>stop</key>
+      <value>.5</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>rangeType</key>
+      <value>float</value>
     </param>
     <param>
-      <key>value</key>
-      <value>4.2563</value>
+      <key>widget</key>
+      <value>counter_slider</value>
     </param>
+  </block>
+  <block>
+    <key>variable_qtgui_range</key>
     <param>
-      <key>alias</key>
+      <key>comment</key>
       <value></value>
     </param>
+    <param>
+      <key>value</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
     <param>
       <key>_coordinate</key>
-      <value>(300, 0)</value>
+      <value>(6, 526)</value>
+    </param>
+    <param>
+      <key>gui_hint</key>
+      <value>2,2,1,1</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>variable</key>
     <param>
       <key>id</key>
-      <value>rolloff</value>
+      <value>interpratio</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>label</key>
+      <value>Timing Offset</value>
     </param>
     <param>
-      <key>value</key>
-      <value>.35</value>
+      <key>min_len</key>
+      <value>200</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>orient</key>
+      <value>Qt.Horizontal</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(482, 335)</value>
+      <key>start</key>
+      <value>.99</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>step</key>
+      <value>0.0001</value>
     </param>
-  </block>
-  <block>
-    <key>variable</key>
     <param>
-      <key>id</key>
-      <value>nfilts</value>
+      <key>stop</key>
+      <value>1.01</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>rangeType</key>
+      <value>float</value>
     </param>
     <param>
-      <key>value</key>
-      <value>32</value>
+      <key>widget</key>
+      <value>counter_slider</value>
     </param>
+  </block>
+  <block>
+    <key>variable</key>
     <param>
-      <key>alias</key>
+      <key>comment</key>
       <value></value>
     </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
     <param>
       <key>_coordinate</key>
       <value>(562, 334)</value>
@@ -225,952 +252,1161 @@
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>analog_random_source_x</key>
     <param>
       <key>id</key>
-      <value>analog_random_source_x</value>
+      <value>nfilts</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>value</key>
+      <value>32</value>
     </param>
+  </block>
+  <block>
+    <key>variable_qtgui_range</key>
     <param>
-      <key>type</key>
-      <value>byte</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>min</key>
+      <key>value</key>
       <value>0</value>
     </param>
     <param>
-      <key>max</key>
-      <value>const.arity()</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>num_samps</key>
-      <value>10000000</value>
+      <key>_coordinate</key>
+      <value>(130, 528)</value>
     </param>
     <param>
-      <key>repeat</key>
-      <value>True</value>
+      <key>gui_hint</key>
+      <value>3,2,1,1</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>affinity</key>
-      <value></value>
+      <key>id</key>
+      <value>noise_amp</value>
     </param>
     <param>
-      <key>minoutbuf</key>
-      <value>0</value>
+      <key>label</key>
+      <value>Channel Noise</value>
     </param>
     <param>
-      <key>maxoutbuf</key>
-      <value>0</value>
+      <key>min_len</key>
+      <value>200</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(-1, 163)</value>
+      <key>orient</key>
+      <value>Qt.Horizontal</value>
     </param>
     <param>
-      <key>_rotation</key>
+      <key>start</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>digital_chunks_to_symbols_xx</key>
     <param>
-      <key>id</key>
-      <value>digital_chunks_to_symbols_xx</value>
+      <key>step</key>
+      <value>.001</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>stop</key>
+      <value>1</value>
     </param>
     <param>
-      <key>in_type</key>
-      <value>byte</value>
+      <key>rangeType</key>
+      <value>float</value>
     </param>
     <param>
-      <key>out_type</key>
-      <value>complex</value>
+      <key>widget</key>
+      <value>counter_slider</value>
     </param>
+  </block>
+  <block>
+    <key>variable</key>
     <param>
-      <key>symbol_table</key>
-      <value>const.points()</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>dimension</key>
-      <value>1</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>num_ports</key>
-      <value>1</value>
+      <key>_coordinate</key>
+      <value>(482, 335)</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>affinity</key>
-      <value></value>
+      <key>id</key>
+      <value>rolloff</value>
     </param>
     <param>
-      <key>minoutbuf</key>
-      <value>0</value>
+      <key>value</key>
+      <value>.35</value>
     </param>
+  </block>
+  <block>
+    <key>variable</key>
     <param>
-      <key>maxoutbuf</key>
-      <value>0</value>
+      <key>comment</key>
+      <value></value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
       <key>_coordinate</key>
-      <value>(203, 178)</value>
+      <value>(267, 357)</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
+    <param>
+      <key>id</key>
+      <value>samp_rate</value>
+    </param>
+    <param>
+      <key>value</key>
+      <value>32000</value>
+    </param>
   </block>
   <block>
-    <key>blocks_throttle</key>
+    <key>variable</key>
     <param>
-      <key>id</key>
-      <value>blocks_throttle_0</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
       <key>_enabled</key>
       <value>True</value>
     </param>
     <param>
-      <key>type</key>
-      <value>complex</value>
+      <key>_coordinate</key>
+      <value>(791, 46)</value>
     </param>
     <param>
-      <key>samples_per_second</key>
-      <value>samp_rate</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>vlen</key>
-      <value>1</value>
+      <key>id</key>
+      <value>sig_amp</value>
     </param>
     <param>
-      <key>ignoretag</key>
-      <value>True</value>
+      <key>value</key>
+      <value>1</value>
     </param>
+  </block>
+  <block>
+    <key>variable</key>
     <param>
-      <key>alias</key>
+      <key>comment</key>
       <value></value>
     </param>
     <param>
-      <key>affinity</key>
-      <value></value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>minoutbuf</key>
-      <value>0</value>
+      <key>_coordinate</key>
+      <value>(300, 0)</value>
     </param>
     <param>
-      <key>maxoutbuf</key>
+      <key>_rotation</key>
       <value>0</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(258, 431)</value>
+      <key>id</key>
+      <value>spb</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>value</key>
+      <value>4.2563</value>
     </param>
   </block>
   <block>
-    <key>channels_channel_model</key>
+    <key>variable_qtgui_range</key>
     <param>
-      <key>id</key>
-      <value>channels_channel_model_0</value>
+      <key>comment</key>
+      <value></value>
+    </param>
+    <param>
+      <key>value</key>
+      <value>0</value>
     </param>
     <param>
       <key>_enabled</key>
       <value>True</value>
     </param>
     <param>
-      <key>noise_voltage</key>
-      <value>noise_amp</value>
+      <key>_coordinate</key>
+      <value>(451, 0)</value>
     </param>
     <param>
-      <key>freq_offset</key>
-      <value>freq_offset</value>
+      <key>gui_hint</key>
+      <value>1,2,1,1</value>
     </param>
     <param>
-      <key>epsilon</key>
-      <value>interpratio</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>taps</key>
-      <value>1.0</value>
+      <key>id</key>
+      <value>time_bw</value>
     </param>
     <param>
-      <key>seed</key>
-      <value>42</value>
+      <key>label</key>
+      <value>Timing Loop BW</value>
     </param>
     <param>
-      <key>block_tags</key>
-      <value>False</value>
+      <key>min_len</key>
+      <value>200</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>orient</key>
+      <value>Qt.Horizontal</value>
     </param>
     <param>
-      <key>affinity</key>
-      <value></value>
+      <key>start</key>
+      <value>0</value>
     </param>
     <param>
-      <key>minoutbuf</key>
-      <value>0</value>
+      <key>step</key>
+      <value>.001</value>
     </param>
     <param>
-      <key>maxoutbuf</key>
-      <value>0</value>
+      <key>stop</key>
+      <value>.1</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(57, 401)</value>
+      <key>rangeType</key>
+      <value>float</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>widget</key>
+      <value>counter_slider</value>
     </param>
   </block>
   <block>
-    <key>pfb_arb_resampler_xxx</key>
+    <key>analog_random_source_x</key>
     <param>
-      <key>id</key>
-      <value>pfb_arb_resampler_xxx_0</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>type</key>
-      <value>ccf</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>rrate</key>
-      <value>spb</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>taps</key>
-      <value>firdes.root_raised_cosine(nfilts, nfilts, 1.0, rolloff, 44*nfilts)</value>
+      <key>_coordinate</key>
+      <value>(-1, 163)</value>
     </param>
     <param>
-      <key>nfilts</key>
-      <value>32</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>atten</key>
-      <value>100</value>
+      <key>id</key>
+      <value>analog_random_source_x</value>
     </param>
     <param>
-      <key>samp_delay</key>
+      <key>maxoutbuf</key>
       <value>0</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
-    </param>
-    <param>
-      <key>affinity</key>
-      <value></value>
+      <key>max</key>
+      <value>const.arity()</value>
     </param>
     <param>
       <key>minoutbuf</key>
       <value>0</value>
     </param>
     <param>
-      <key>maxoutbuf</key>
+      <key>min</key>
       <value>0</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(458, 179)</value>
+      <key>num_samps</key>
+      <value>10000000</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>type</key>
+      <value>byte</value>
+    </param>
+    <param>
+      <key>repeat</key>
+      <value>True</value>
     </param>
   </block>
   <block>
-    <key>import</key>
+    <key>blocks_multiply_const_vxx</key>
     <param>
-      <key>id</key>
-      <value>import_0</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>import</key>
-      <value>from gnuradio import digital</value>
+      <key>const</key>
+      <value>sig_amp</value>
     </param>
     <param>
-      <key>alias</key>
+      <key>affinity</key>
       <value></value>
     </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
     <param>
       <key>_coordinate</key>
-      <value>(-1, 61)</value>
+      <value>(760, 159)</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>blocks_multiply_const_vxx</key>
     <param>
       <key>id</key>
       <value>blocks_multiply_const_vxx_0</value>
     </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
     <param>
       <key>type</key>
       <value>complex</value>
     </param>
     <param>
-      <key>const</key>
-      <value>sig_amp</value>
+      <key>maxoutbuf</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>minoutbuf</key>
+      <value>0</value>
     </param>
     <param>
       <key>vlen</key>
       <value>1</value>
     </param>
+  </block>
+  <block>
+    <key>blocks_throttle</key>
     <param>
       <key>alias</key>
       <value></value>
     </param>
     <param>
-      <key>affinity</key>
+      <key>comment</key>
       <value></value>
     </param>
     <param>
-      <key>minoutbuf</key>
-      <value>0</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>maxoutbuf</key>
-      <value>0</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
       <key>_coordinate</key>
-      <value>(760, 159)</value>
+      <value>(258, 431)</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>qtgui_time_sink_x</key>
     <param>
       <key>id</key>
-      <value>qtgui_time_sink_x_0</value>
+      <value>blocks_throttle_0</value>
     </param>
     <param>
-      <key>_enabled</key>
+      <key>ignoretag</key>
       <value>True</value>
     </param>
     <param>
-      <key>type</key>
-      <value>complex</value>
-    </param>
-    <param>
-      <key>name</key>
-      <value>Error</value>
-    </param>
-    <param>
-      <key>ylabel</key>
-      <value>Amplitude</value>
-    </param>
-    <param>
-      <key>yunit</key>
-      <value>""</value>
+      <key>maxoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>size</key>
-      <value>1024</value>
+      <key>minoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>srate</key>
+      <key>samples_per_second</key>
       <value>samp_rate</value>
     </param>
     <param>
-      <key>grid</key>
-      <value>False</value>
+      <key>type</key>
+      <value>complex</value>
     </param>
     <param>
-      <key>autoscale</key>
-      <value>False</value>
+      <key>vlen</key>
+      <value>1</value>
     </param>
+  </block>
+  <block>
+    <key>channels_channel_model</key>
     <param>
-      <key>ymin</key>
-      <value>-1</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>ymax</key>
-      <value>1</value>
+      <key>block_tags</key>
+      <value>False</value>
     </param>
     <param>
-      <key>nconnections</key>
-      <value>1</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>update_time</key>
-      <value>0.10</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>entags</key>
+      <key>_enabled</key>
       <value>True</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>notebook@3</value>
-    </param>
-    <param>
-      <key>tr_mode</key>
-      <value>qtgui.TRIG_MODE_FREE</value>
+      <key>epsilon</key>
+      <value>interpratio</value>
     </param>
     <param>
-      <key>tr_slope</key>
-      <value>qtgui.TRIG_SLOPE_POS</value>
+      <key>freq_offset</key>
+      <value>freq_offset</value>
     </param>
     <param>
-      <key>tr_level</key>
-      <value>0.0</value>
+      <key>_coordinate</key>
+      <value>(57, 401)</value>
     </param>
     <param>
-      <key>tr_delay</key>
+      <key>_rotation</key>
       <value>0</value>
     </param>
     <param>
-      <key>tr_chan</key>
-      <value>0</value>
+      <key>id</key>
+      <value>channels_channel_model_0</value>
     </param>
     <param>
-      <key>tr_tag</key>
-      <value>""</value>
+      <key>maxoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>label1</key>
-      <value></value>
+      <key>minoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>width1</key>
-      <value>1</value>
+      <key>noise_voltage</key>
+      <value>noise_amp</value>
     </param>
     <param>
-      <key>color1</key>
-      <value>"blue"</value>
+      <key>seed</key>
+      <value>42</value>
     </param>
     <param>
-      <key>style1</key>
-      <value>1</value>
+      <key>taps</key>
+      <value>1.0</value>
     </param>
+  </block>
+  <block>
+    <key>digital_chunks_to_symbols_xx</key>
     <param>
-      <key>marker1</key>
-      <value>-1</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>alpha1</key>
-      <value>1.0</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>label2</key>
+      <key>affinity</key>
       <value></value>
     </param>
     <param>
-      <key>width2</key>
+      <key>dimension</key>
       <value>1</value>
     </param>
     <param>
-      <key>color2</key>
-      <value>"red"</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>style2</key>
-      <value>1</value>
+      <key>_coordinate</key>
+      <value>(203, 178)</value>
     </param>
     <param>
-      <key>marker2</key>
-      <value>-1</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>alpha2</key>
-      <value>1.0</value>
+      <key>id</key>
+      <value>digital_chunks_to_symbols_xx</value>
     </param>
     <param>
-      <key>label3</key>
-      <value></value>
+      <key>in_type</key>
+      <value>byte</value>
     </param>
     <param>
-      <key>width3</key>
-      <value>1</value>
+      <key>maxoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>color3</key>
-      <value>"green"</value>
+      <key>minoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>style3</key>
+      <key>num_ports</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker3</key>
-      <value>-1</value>
+      <key>out_type</key>
+      <value>complex</value>
     </param>
     <param>
-      <key>alpha3</key>
-      <value>1.0</value>
+      <key>symbol_table</key>
+      <value>const.points()</value>
     </param>
+  </block>
+  <block>
+    <key>digital_pfb_clock_sync_xxx</key>
     <param>
-      <key>label4</key>
+      <key>alias</key>
       <value></value>
     </param>
     <param>
-      <key>width4</key>
-      <value>1</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>color4</key>
-      <value>"black"</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>style4</key>
-      <value>1</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>marker4</key>
-      <value>-1</value>
+      <key>filter_size</key>
+      <value>nfilts</value>
     </param>
     <param>
-      <key>alpha4</key>
-      <value>1.0</value>
+      <key>_coordinate</key>
+      <value>(467, 403)</value>
     </param>
     <param>
-      <key>label5</key>
-      <value></value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>width5</key>
-      <value>1</value>
+      <key>id</key>
+      <value>digital_pfb_clock_sync_xxx_0</value>
     </param>
     <param>
-      <key>color5</key>
-      <value>"cyan"</value>
+      <key>init_phase</key>
+      <value>0</value>
     </param>
     <param>
-      <key>style5</key>
-      <value>1</value>
+      <key>loop_bw</key>
+      <value>time_bw</value>
     </param>
     <param>
-      <key>marker5</key>
-      <value>-1</value>
+      <key>maxoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>alpha5</key>
-      <value>1.0</value>
+      <key>max_dev</key>
+      <value>1.5</value>
     </param>
     <param>
-      <key>label6</key>
-      <value></value>
+      <key>minoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>width6</key>
+      <key>osps</key>
       <value>1</value>
     </param>
     <param>
-      <key>color6</key>
-      <value>"magenta"</value>
+      <key>sps</key>
+      <value>spb</value>
     </param>
     <param>
-      <key>style6</key>
-      <value>1</value>
+      <key>taps</key>
+      <value>firdes.root_raised_cosine(nfilts, nfilts*spb, 1.0, rolloff, 44*nfilts)</value>
     </param>
     <param>
-      <key>marker6</key>
-      <value>-1</value>
+      <key>type</key>
+      <value>ccf</value>
     </param>
+  </block>
+  <block>
+    <key>import</key>
     <param>
-      <key>alpha6</key>
-      <value>1.0</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>label7</key>
+      <key>comment</key>
       <value></value>
     </param>
     <param>
-      <key>width7</key>
-      <value>1</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>color7</key>
-      <value>"yellow"</value>
+      <key>_coordinate</key>
+      <value>(-1, 61)</value>
     </param>
     <param>
-      <key>style7</key>
-      <value>1</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>marker7</key>
-      <value>-1</value>
+      <key>id</key>
+      <value>import_0</value>
     </param>
     <param>
-      <key>alpha7</key>
-      <value>1.0</value>
+      <key>import</key>
+      <value>from gnuradio import digital</value>
     </param>
+  </block>
+  <block>
+    <key>qtgui_tab_widget</key>
     <param>
-      <key>label8</key>
+      <key>alias</key>
       <value></value>
     </param>
     <param>
-      <key>width8</key>
-      <value>1</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>color8</key>
-      <value>"dark red"</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>style8</key>
-      <value>1</value>
+      <key>_coordinate</key>
+      <value>(485, 554)</value>
     </param>
     <param>
-      <key>marker8</key>
-      <value>-1</value>
+      <key>gui_hint</key>
+      <value>1,1,5,1</value>
     </param>
     <param>
-      <key>alpha8</key>
-      <value>1.0</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>label9</key>
-      <value></value>
+      <key>id</key>
+      <value>notebook</value>
     </param>
     <param>
-      <key>width9</key>
-      <value>1</value>
+      <key>label0</key>
+      <value>Error</value>
     </param>
     <param>
-      <key>color9</key>
-      <value>"dark green"</value>
+      <key>label1</key>
+      <value>Phase</value>
     </param>
     <param>
-      <key>style9</key>
-      <value>1</value>
+      <key>label10</key>
+      <value>Tab 10</value>
     </param>
     <param>
-      <key>marker9</key>
-      <value>-1</value>
+      <key>label11</key>
+      <value>Tab 11</value>
     </param>
     <param>
-      <key>alpha9</key>
-      <value>1.0</value>
+      <key>label12</key>
+      <value>Tab 12</value>
     </param>
     <param>
-      <key>label10</key>
-      <value></value>
+      <key>label13</key>
+      <value>Tab 13</value>
     </param>
     <param>
-      <key>width10</key>
-      <value>1</value>
+      <key>label14</key>
+      <value>Tab 14</value>
     </param>
     <param>
-      <key>color10</key>
-      <value>"blue"</value>
+      <key>label15</key>
+      <value>Tab 15</value>
     </param>
     <param>
-      <key>style10</key>
-      <value>1</value>
+      <key>label16</key>
+      <value>Tab 16</value>
     </param>
     <param>
-      <key>marker10</key>
-      <value>-1</value>
+      <key>label17</key>
+      <value>Tab 17</value>
     </param>
     <param>
-      <key>alpha10</key>
-      <value>1.0</value>
+      <key>label18</key>
+      <value>Tab 18</value>
+    </param>
+    <param>
+      <key>label19</key>
+      <value>Tab 19</value>
+    </param>
+    <param>
+      <key>label2</key>
+      <value>Freq</value>
+    </param>
+    <param>
+      <key>label3</key>
+      <value>Resampled Signal</value>
+    </param>
+    <param>
+      <key>label4</key>
+      <value>Tab 4</value>
+    </param>
+    <param>
+      <key>label5</key>
+      <value>Tab 5</value>
     </param>
+    <param>
+      <key>label6</key>
+      <value>Tab 6</value>
+    </param>
+    <param>
+      <key>label7</key>
+      <value>Tab 7</value>
+    </param>
+    <param>
+      <key>label8</key>
+      <value>Tab 8</value>
+    </param>
+    <param>
+      <key>label9</key>
+      <value>Tab 9</value>
+    </param>
+    <param>
+      <key>num_tabs</key>
+      <value>4</value>
+    </param>
+  </block>
+  <block>
+    <key>pfb_arb_resampler_xxx</key>
     <param>
       <key>alias</key>
       <value></value>
     </param>
+    <param>
+      <key>comment</key>
+      <value></value>
+    </param>
     <param>
       <key>affinity</key>
       <value></value>
     </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
     <param>
       <key>_coordinate</key>
-      <value>(1020, 128)</value>
+      <value>(458, 179)</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>qtgui_tab_widget</key>
     <param>
       <key>id</key>
-      <value>notebook</value>
+      <value>pfb_arb_resampler_xxx_0</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>maxoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>num_tabs</key>
-      <value>4</value>
+      <key>minoutbuf</key>
+      <value>0</value>
     </param>
     <param>
-      <key>label0</key>
-      <value>Error</value>
+      <key>nfilts</key>
+      <value>32</value>
     </param>
     <param>
-      <key>label1</key>
-      <value>Phase</value>
+      <key>rrate</key>
+      <value>spb</value>
     </param>
     <param>
-      <key>label2</key>
-      <value>Freq</value>
+      <key>samp_delay</key>
+      <value>0</value>
     </param>
     <param>
-      <key>label3</key>
-      <value>Resampled Signal</value>
+      <key>atten</key>
+      <value>100</value>
     </param>
     <param>
-      <key>label4</key>
-      <value>Tab 4</value>
+      <key>taps</key>
+      <value>firdes.root_raised_cosine(nfilts, nfilts, 1.0, rolloff, 44*nfilts)</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>1,1,5,1</value>
+      <key>type</key>
+      <value>ccf</value>
+    </param>
+  </block>
+  <block>
+    <key>qtgui_time_sink_x</key>
+    <param>
+      <key>autoscale</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>axislabels</key>
+      <value>True</value>
     </param>
     <param>
       <key>alias</key>
       <value></value>
     </param>
+    <param>
+      <key>comment</key>
+      <value></value>
+    </param>
+    <param>
+      <key>ctrlpanel</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>affinity</key>
+      <value></value>
+    </param>
+    <param>
+      <key>entags</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
     <param>
       <key>_coordinate</key>
-      <value>(485, 554)</value>
+      <value>(1020, 128)</value>
+    </param>
+    <param>
+      <key>gui_hint</key>
+      <value>notebook@3</value>
     </param>
     <param>
       <key>_rotation</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>variable_qtgui_range</key>
+    <param>
+      <key>grid</key>
+      <value>False</value>
+    </param>
     <param>
       <key>id</key>
-      <value>interpratio</value>
+      <value>qtgui_time_sink_x_0</value>
     </param>
     <param>
-      <key>_enabled</key>
+      <key>legend</key>
       <value>True</value>
     </param>
     <param>
-      <key>label</key>
-      <value>Timing Offset</value>
+      <key>alpha1</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>value</key>
+      <key>color1</key>
+      <value>"blue"</value>
+    </param>
+    <param>
+      <key>label1</key>
+      <value></value>
+    </param>
+    <param>
+      <key>marker1</key>
+      <value>-1</value>
+    </param>
+    <param>
+      <key>style1</key>
       <value>1</value>
     </param>
     <param>
-      <key>start</key>
-      <value>.99</value>
+      <key>width1</key>
+      <value>1</value>
     </param>
     <param>
-      <key>stop</key>
-      <value>1.01</value>
+      <key>alpha10</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>step</key>
-      <value>0.0001</value>
+      <key>color10</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>widget</key>
-      <value>counter_slider</value>
+      <key>label10</key>
+      <value></value>
     </param>
     <param>
-      <key>orient</key>
-      <value>Qt.Horizontal</value>
+      <key>marker10</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>min_len</key>
-      <value>200</value>
+      <key>style10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>2,2,1,1</value>
+      <key>width10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alias</key>
+      <key>alpha2</key>
+      <value>1.0</value>
+    </param>
+    <param>
+      <key>color2</key>
+      <value>"red"</value>
+    </param>
+    <param>
+      <key>label2</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(6, 526)</value>
+      <key>marker2</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>style2</key>
+      <value>1</value>
     </param>
-  </block>
-  <block>
-    <key>variable_qtgui_range</key>
     <param>
-      <key>id</key>
-      <value>noise_amp</value>
+      <key>width2</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>alpha3</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>label</key>
-      <value>Channel Noise</value>
+      <key>color3</key>
+      <value>"green"</value>
     </param>
     <param>
-      <key>value</key>
-      <value>0</value>
+      <key>label3</key>
+      <value></value>
     </param>
     <param>
-      <key>start</key>
-      <value>0</value>
+      <key>marker3</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>stop</key>
+      <key>style3</key>
       <value>1</value>
     </param>
     <param>
-      <key>step</key>
-      <value>.001</value>
+      <key>width3</key>
+      <value>1</value>
     </param>
     <param>
-      <key>widget</key>
-      <value>counter_slider</value>
+      <key>alpha4</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>orient</key>
-      <value>Qt.Horizontal</value>
+      <key>color4</key>
+      <value>"black"</value>
     </param>
     <param>
-      <key>min_len</key>
-      <value>200</value>
+      <key>label4</key>
+      <value></value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>3,2,1,1</value>
+      <key>marker4</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>alias</key>
+      <key>style4</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>width4</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>alpha5</key>
+      <value>1.0</value>
+    </param>
+    <param>
+      <key>color5</key>
+      <value>"cyan"</value>
+    </param>
+    <param>
+      <key>label5</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(130, 528)</value>
+      <key>marker5</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>style5</key>
+      <value>1</value>
     </param>
-  </block>
-  <block>
-    <key>variable_qtgui_range</key>
     <param>
-      <key>id</key>
-      <value>freq_offset</value>
+      <key>width5</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>alpha6</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>label</key>
-      <value>Frequency Offset</value>
+      <key>color6</key>
+      <value>"magenta"</value>
     </param>
     <param>
-      <key>value</key>
-      <value>0</value>
+      <key>label6</key>
+      <value></value>
     </param>
     <param>
-      <key>start</key>
-      <value>-.5</value>
+      <key>marker6</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>stop</key>
-      <value>.5</value>
+      <key>style6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>step</key>
-      <value>.01</value>
+      <key>width6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>widget</key>
-      <value>counter_slider</value>
+      <key>alpha7</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>orient</key>
-      <value>Qt.Horizontal</value>
+      <key>color7</key>
+      <value>"yellow"</value>
     </param>
     <param>
-      <key>min_len</key>
-      <value>200</value>
+      <key>label7</key>
+      <value></value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>4,2,1,1</value>
+      <key>marker7</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>alias</key>
+      <key>style7</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>width7</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>alpha8</key>
+      <value>1.0</value>
+    </param>
+    <param>
+      <key>color8</key>
+      <value>"dark red"</value>
+    </param>
+    <param>
+      <key>label8</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(268, 527)</value>
+      <key>marker8</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>_rotation</key>
+      <key>style8</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>width8</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>alpha9</key>
+      <value>1.0</value>
+    </param>
+    <param>
+      <key>color9</key>
+      <value>"dark green"</value>
+    </param>
+    <param>
+      <key>label9</key>
+      <value></value>
+    </param>
+    <param>
+      <key>marker9</key>
+      <value>-1</value>
+    </param>
+    <param>
+      <key>style9</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>width9</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>name</key>
+      <value>Error</value>
+    </param>
+    <param>
+      <key>nconnections</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>size</key>
+      <value>1024</value>
+    </param>
+    <param>
+      <key>srate</key>
+      <value>samp_rate</value>
+    </param>
+    <param>
+      <key>tr_chan</key>
       <value>0</value>
     </param>
-  </block>
-  <block>
-    <key>qtgui_time_sink_x</key>
     <param>
-      <key>id</key>
-      <value>qtgui_time_sink_x_1_1</value>
+      <key>tr_delay</key>
+      <value>0</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>tr_level</key>
+      <value>0.0</value>
+    </param>
+    <param>
+      <key>tr_mode</key>
+      <value>qtgui.TRIG_MODE_FREE</value>
+    </param>
+    <param>
+      <key>tr_slope</key>
+      <value>qtgui.TRIG_SLOPE_POS</value>
+    </param>
+    <param>
+      <key>tr_tag</key>
+      <value>""</value>
     </param>
     <param>
       <key>type</key>
-      <value>float</value>
+      <value>complex</value>
     </param>
     <param>
-      <key>name</key>
-      <value>Phase</value>
+      <key>update_time</key>
+      <value>0.10</value>
     </param>
     <param>
       <key>ylabel</key>
@@ -1181,351 +1417,319 @@
       <value>""</value>
     </param>
     <param>
-      <key>size</key>
-      <value>1024</value>
+      <key>ymax</key>
+      <value>1</value>
     </param>
     <param>
-      <key>srate</key>
-      <value>samp_rate</value>
+      <key>ymin</key>
+      <value>-1</value>
     </param>
+  </block>
+  <block>
+    <key>qtgui_time_sink_x</key>
     <param>
-      <key>grid</key>
+      <key>autoscale</key>
       <value>False</value>
     </param>
     <param>
-      <key>autoscale</key>
+      <key>axislabels</key>
       <value>True</value>
     </param>
     <param>
-      <key>ymin</key>
-      <value>-1</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>ymax</key>
-      <value>1</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>nconnections</key>
-      <value>1</value>
+      <key>ctrlpanel</key>
+      <value>False</value>
     </param>
     <param>
-      <key>update_time</key>
-      <value>0.10</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
       <key>entags</key>
       <value>True</value>
     </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>_coordinate</key>
+      <value>(1029, 281)</value>
+    </param>
     <param>
       <key>gui_hint</key>
-      <value>notebook@1</value>
+      <value></value>
     </param>
     <param>
-      <key>tr_mode</key>
-      <value>qtgui.TRIG_MODE_FREE</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>tr_slope</key>
-      <value>qtgui.TRIG_SLOPE_POS</value>
+      <key>grid</key>
+      <value>False</value>
     </param>
     <param>
-      <key>tr_level</key>
-      <value>0.0</value>
+      <key>id</key>
+      <value>qtgui_time_sink_x_0_0</value>
     </param>
     <param>
-      <key>tr_delay</key>
-      <value>0</value>
+      <key>legend</key>
+      <value>True</value>
     </param>
     <param>
-      <key>tr_chan</key>
-      <value>0</value>
+      <key>alpha1</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>tr_tag</key>
-      <value>""</value>
+      <key>color1</key>
+      <value>"blue"</value>
     </param>
     <param>
       <key>label1</key>
       <value></value>
     </param>
+    <param>
+      <key>marker1</key>
+      <value>-1</value>
+    </param>
+    <param>
+      <key>style1</key>
+      <value>1</value>
+    </param>
     <param>
       <key>width1</key>
       <value>1</value>
     </param>
     <param>
-      <key>color1</key>
+      <key>alpha10</key>
+      <value>1.0</value>
+    </param>
+    <param>
+      <key>color10</key>
       <value>"blue"</value>
     </param>
     <param>
-      <key>style1</key>
-      <value>1</value>
+      <key>label10</key>
+      <value></value>
     </param>
     <param>
-      <key>marker1</key>
+      <key>marker10</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha1</key>
-      <value>1.0</value>
+      <key>style10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label2</key>
-      <value></value>
+      <key>width10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width2</key>
-      <value>1</value>
+      <key>alpha2</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color2</key>
       <value>"red"</value>
     </param>
     <param>
-      <key>style2</key>
-      <value>1</value>
+      <key>label2</key>
+      <value></value>
     </param>
     <param>
       <key>marker2</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha2</key>
-      <value>1.0</value>
+      <key>style2</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label3</key>
-      <value></value>
+      <key>width2</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width3</key>
-      <value>1</value>
+      <key>alpha3</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color3</key>
       <value>"green"</value>
     </param>
     <param>
-      <key>style3</key>
-      <value>1</value>
+      <key>label3</key>
+      <value></value>
     </param>
     <param>
       <key>marker3</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha3</key>
-      <value>1.0</value>
+      <key>style3</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label4</key>
-      <value></value>
+      <key>width3</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width4</key>
-      <value>1</value>
+      <key>alpha4</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color4</key>
       <value>"black"</value>
     </param>
     <param>
-      <key>style4</key>
-      <value>1</value>
+      <key>label4</key>
+      <value></value>
     </param>
     <param>
       <key>marker4</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha4</key>
-      <value>1.0</value>
+      <key>style4</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label5</key>
-      <value></value>
+      <key>width4</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width5</key>
-      <value>1</value>
+      <key>alpha5</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color5</key>
       <value>"cyan"</value>
     </param>
     <param>
-      <key>style5</key>
-      <value>1</value>
+      <key>label5</key>
+      <value></value>
     </param>
     <param>
       <key>marker5</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha5</key>
-      <value>1.0</value>
+      <key>style5</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label6</key>
-      <value></value>
+      <key>width5</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width6</key>
-      <value>1</value>
+      <key>alpha6</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color6</key>
       <value>"magenta"</value>
     </param>
     <param>
-      <key>style6</key>
-      <value>1</value>
+      <key>label6</key>
+      <value></value>
     </param>
     <param>
       <key>marker6</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha6</key>
-      <value>1.0</value>
+      <key>style6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label7</key>
-      <value></value>
+      <key>width6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width7</key>
-      <value>1</value>
+      <key>alpha7</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color7</key>
       <value>"yellow"</value>
     </param>
     <param>
-      <key>style7</key>
-      <value>1</value>
+      <key>label7</key>
+      <value></value>
     </param>
     <param>
       <key>marker7</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha7</key>
-      <value>1.0</value>
+      <key>style7</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label8</key>
-      <value></value>
+      <key>width7</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width8</key>
-      <value>1</value>
+      <key>alpha8</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color8</key>
       <value>"dark red"</value>
     </param>
     <param>
-      <key>style8</key>
-      <value>1</value>
+      <key>label8</key>
+      <value></value>
     </param>
     <param>
       <key>marker8</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha8</key>
-      <value>1.0</value>
+      <key>style8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label9</key>
-      <value></value>
+      <key>width8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>width9</key>
-      <value>1</value>
+      <key>alpha9</key>
+      <value>1.0</value>
     </param>
     <param>
       <key>color9</key>
       <value>"dark green"</value>
     </param>
     <param>
-      <key>style9</key>
-      <value>1</value>
+      <key>label9</key>
+      <value></value>
     </param>
     <param>
       <key>marker9</key>
       <value>-1</value>
     </param>
     <param>
-      <key>alpha9</key>
-      <value>1.0</value>
-    </param>
-    <param>
-      <key>label10</key>
-      <value></value>
-    </param>
-    <param>
-      <key>width10</key>
+      <key>style9</key>
       <value>1</value>
     </param>
     <param>
-      <key>color10</key>
-      <value>"blue"</value>
-    </param>
-    <param>
-      <key>style10</key>
+      <key>width9</key>
       <value>1</value>
     </param>
-    <param>
-      <key>marker10</key>
-      <value>-1</value>
-    </param>
-    <param>
-      <key>alpha10</key>
-      <value>1.0</value>
-    </param>
-    <param>
-      <key>alias</key>
-      <value></value>
-    </param>
-    <param>
-      <key>affinity</key>
-      <value></value>
-    </param>
-    <param>
-      <key>_coordinate</key>
-      <value>(1014, 622)</value>
-    </param>
-    <param>
-      <key>_rotation</key>
-      <value>0</value>
-    </param>
-  </block>
-  <block>
-    <key>qtgui_time_sink_x</key>
-    <param>
-      <key>id</key>
-      <value>qtgui_time_sink_x_1_0</value>
-    </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>type</key>
-      <value>float</value>
-    </param>
     <param>
       <key>name</key>
-      <value>Rate</value>
-    </param>
-    <param>
-      <key>ylabel</key>
-      <value>Amplitude</value>
+      <value>Scope Plot</value>
     </param>
     <param>
-      <key>yunit</key>
-      <value>""</value>
+      <key>nconnections</key>
+      <value>1</value>
     </param>
     <param>
       <key>size</key>
@@ -1536,36 +1740,16 @@
       <value>samp_rate</value>
     </param>
     <param>
-      <key>grid</key>
-      <value>False</value>
-    </param>
-    <param>
-      <key>autoscale</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>ymin</key>
-      <value>-1</value>
-    </param>
-    <param>
-      <key>ymax</key>
-      <value>1</value>
-    </param>
-    <param>
-      <key>nconnections</key>
-      <value>1</value>
-    </param>
-    <param>
-      <key>update_time</key>
-      <value>0.10</value>
+      <key>tr_chan</key>
+      <value>0</value>
     </param>
     <param>
-      <key>entags</key>
-      <value>True</value>
+      <key>tr_delay</key>
+      <value>0</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>notebook@2</value>
+      <key>tr_level</key>
+      <value>0.0</value>
     </param>
     <param>
       <key>tr_mode</key>
@@ -1576,343 +1760,359 @@
       <value>qtgui.TRIG_SLOPE_POS</value>
     </param>
     <param>
-      <key>tr_level</key>
-      <value>0.0</value>
+      <key>tr_tag</key>
+      <value>""</value>
     </param>
     <param>
-      <key>tr_delay</key>
-      <value>0</value>
+      <key>type</key>
+      <value>complex</value>
     </param>
     <param>
-      <key>tr_chan</key>
-      <value>0</value>
+      <key>update_time</key>
+      <value>0.10</value>
     </param>
     <param>
-      <key>tr_tag</key>
-      <value>""</value>
+      <key>ylabel</key>
+      <value>Amplitude</value>
     </param>
     <param>
-      <key>label1</key>
-      <value></value>
+      <key>yunit</key>
+      <value>""</value>
     </param>
     <param>
-      <key>width1</key>
+      <key>ymax</key>
       <value>1</value>
     </param>
     <param>
-      <key>color1</key>
-      <value>"blue"</value>
+      <key>ymin</key>
+      <value>-1</value>
     </param>
+  </block>
+  <block>
+    <key>qtgui_time_sink_x</key>
     <param>
-      <key>style1</key>
-      <value>1</value>
+      <key>autoscale</key>
+      <value>True</value>
     </param>
     <param>
-      <key>marker1</key>
-      <value>-1</value>
+      <key>axislabels</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha1</key>
-      <value>1.0</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>label2</key>
+      <key>comment</key>
       <value></value>
     </param>
     <param>
-      <key>width2</key>
-      <value>1</value>
+      <key>ctrlpanel</key>
+      <value>False</value>
     </param>
     <param>
-      <key>color2</key>
-      <value>"red"</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>style2</key>
-      <value>1</value>
+      <key>entags</key>
+      <value>True</value>
     </param>
     <param>
-      <key>marker2</key>
-      <value>-1</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha2</key>
-      <value>1.0</value>
+      <key>_coordinate</key>
+      <value>(1024, 400)</value>
     </param>
     <param>
-      <key>label3</key>
-      <value></value>
+      <key>gui_hint</key>
+      <value>notebook@0</value>
     </param>
     <param>
-      <key>width3</key>
-      <value>1</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>color3</key>
-      <value>"green"</value>
+      <key>grid</key>
+      <value>False</value>
     </param>
     <param>
-      <key>style3</key>
-      <value>1</value>
+      <key>id</key>
+      <value>qtgui_time_sink_x_1</value>
     </param>
     <param>
-      <key>marker3</key>
-      <value>-1</value>
+      <key>legend</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha3</key>
+      <key>alpha1</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label4</key>
-      <value></value>
+      <key>color1</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>width4</key>
-      <value>1</value>
+      <key>label1</key>
+      <value></value>
     </param>
     <param>
-      <key>color4</key>
-      <value>"black"</value>
+      <key>marker1</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style4</key>
+      <key>style1</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker4</key>
-      <value>-1</value>
+      <key>width1</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha4</key>
+      <key>alpha10</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label5</key>
-      <value></value>
+      <key>color10</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>width5</key>
-      <value>1</value>
+      <key>label10</key>
+      <value></value>
     </param>
     <param>
-      <key>color5</key>
-      <value>"cyan"</value>
+      <key>marker10</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style5</key>
+      <key>style10</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker5</key>
-      <value>-1</value>
+      <key>width10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha5</key>
+      <key>alpha2</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label6</key>
-      <value></value>
+      <key>color2</key>
+      <value>"red"</value>
     </param>
     <param>
-      <key>width6</key>
-      <value>1</value>
+      <key>label2</key>
+      <value></value>
     </param>
     <param>
-      <key>color6</key>
-      <value>"magenta"</value>
+      <key>marker2</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style6</key>
+      <key>style2</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker6</key>
-      <value>-1</value>
+      <key>width2</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha6</key>
+      <key>alpha3</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label7</key>
-      <value></value>
+      <key>color3</key>
+      <value>"green"</value>
     </param>
     <param>
-      <key>width7</key>
-      <value>1</value>
+      <key>label3</key>
+      <value></value>
     </param>
     <param>
-      <key>color7</key>
-      <value>"yellow"</value>
+      <key>marker3</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style7</key>
+      <key>style3</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker7</key>
-      <value>-1</value>
+      <key>width3</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha7</key>
+      <key>alpha4</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label8</key>
-      <value></value>
+      <key>color4</key>
+      <value>"black"</value>
     </param>
     <param>
-      <key>width8</key>
-      <value>1</value>
+      <key>label4</key>
+      <value></value>
     </param>
     <param>
-      <key>color8</key>
-      <value>"dark red"</value>
+      <key>marker4</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style8</key>
+      <key>style4</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker8</key>
-      <value>-1</value>
+      <key>width4</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha8</key>
+      <key>alpha5</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label9</key>
-      <value></value>
+      <key>color5</key>
+      <value>"cyan"</value>
     </param>
     <param>
-      <key>width9</key>
-      <value>1</value>
+      <key>label5</key>
+      <value></value>
     </param>
     <param>
-      <key>color9</key>
-      <value>"dark green"</value>
+      <key>marker5</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style9</key>
+      <key>style5</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker9</key>
-      <value>-1</value>
+      <key>width5</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha9</key>
+      <key>alpha6</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label10</key>
-      <value></value>
+      <key>color6</key>
+      <value>"magenta"</value>
     </param>
     <param>
-      <key>width10</key>
-      <value>1</value>
+      <key>label6</key>
+      <value></value>
     </param>
     <param>
-      <key>color10</key>
-      <value>"blue"</value>
+      <key>marker6</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style10</key>
+      <key>style6</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker10</key>
-      <value>-1</value>
+      <key>width6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha10</key>
+      <key>alpha7</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>color7</key>
+      <value>"yellow"</value>
     </param>
     <param>
-      <key>affinity</key>
+      <key>label7</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(1018, 503)</value>
+      <key>marker7</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>style7</key>
+      <value>1</value>
     </param>
-  </block>
-  <block>
-    <key>qtgui_time_sink_x</key>
     <param>
-      <key>id</key>
-      <value>qtgui_time_sink_x_1</value>
+      <key>width7</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>alpha8</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>type</key>
-      <value>float</value>
+      <key>color8</key>
+      <value>"dark red"</value>
+    </param>
+    <param>
+      <key>label8</key>
+      <value></value>
     </param>
     <param>
-      <key>name</key>
-      <value>Error</value>
+      <key>marker8</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>ylabel</key>
-      <value>Amplitude</value>
+      <key>style8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>yunit</key>
-      <value>""</value>
+      <key>width8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>size</key>
-      <value>1024</value>
+      <key>alpha9</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>srate</key>
-      <value>samp_rate</value>
+      <key>color9</key>
+      <value>"dark green"</value>
     </param>
     <param>
-      <key>grid</key>
-      <value>False</value>
+      <key>label9</key>
+      <value></value>
     </param>
     <param>
-      <key>autoscale</key>
-      <value>True</value>
+      <key>marker9</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>ymin</key>
-      <value>-1</value>
+      <key>style9</key>
+      <value>1</value>
     </param>
     <param>
-      <key>ymax</key>
+      <key>width9</key>
       <value>1</value>
     </param>
+    <param>
+      <key>name</key>
+      <value>Error</value>
+    </param>
     <param>
       <key>nconnections</key>
       <value>1</value>
     </param>
     <param>
-      <key>update_time</key>
-      <value>0.10</value>
+      <key>size</key>
+      <value>1024</value>
     </param>
     <param>
-      <key>entags</key>
-      <value>True</value>
+      <key>srate</key>
+      <value>samp_rate</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>notebook@0</value>
+      <key>tr_chan</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>tr_delay</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>tr_level</key>
+      <value>0.0</value>
     </param>
     <param>
       <key>tr_mode</key>
@@ -1923,772 +2123,781 @@
       <value>qtgui.TRIG_SLOPE_POS</value>
     </param>
     <param>
-      <key>tr_level</key>
-      <value>0.0</value>
+      <key>tr_tag</key>
+      <value>""</value>
     </param>
     <param>
-      <key>tr_delay</key>
-      <value>0</value>
+      <key>type</key>
+      <value>float</value>
     </param>
     <param>
-      <key>tr_chan</key>
-      <value>0</value>
+      <key>update_time</key>
+      <value>0.10</value>
     </param>
     <param>
-      <key>tr_tag</key>
-      <value>""</value>
+      <key>ylabel</key>
+      <value>Amplitude</value>
     </param>
     <param>
-      <key>label1</key>
-      <value></value>
+      <key>yunit</key>
+      <value>""</value>
     </param>
     <param>
-      <key>width1</key>
+      <key>ymax</key>
       <value>1</value>
     </param>
     <param>
-      <key>color1</key>
-      <value>"blue"</value>
+      <key>ymin</key>
+      <value>-1</value>
     </param>
+  </block>
+  <block>
+    <key>qtgui_time_sink_x</key>
     <param>
-      <key>style1</key>
-      <value>1</value>
+      <key>autoscale</key>
+      <value>True</value>
     </param>
     <param>
-      <key>marker1</key>
-      <value>-1</value>
+      <key>axislabels</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha1</key>
-      <value>1.0</value>
+      <key>alias</key>
+      <value></value>
     </param>
     <param>
-      <key>label2</key>
+      <key>comment</key>
       <value></value>
     </param>
     <param>
-      <key>width2</key>
-      <value>1</value>
+      <key>ctrlpanel</key>
+      <value>False</value>
     </param>
     <param>
-      <key>color2</key>
-      <value>"red"</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>style2</key>
-      <value>1</value>
+      <key>entags</key>
+      <value>True</value>
     </param>
     <param>
-      <key>marker2</key>
-      <value>-1</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha2</key>
-      <value>1.0</value>
+      <key>_coordinate</key>
+      <value>(1018, 503)</value>
     </param>
     <param>
-      <key>label3</key>
-      <value></value>
+      <key>gui_hint</key>
+      <value>notebook@2</value>
     </param>
     <param>
-      <key>width3</key>
-      <value>1</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>color3</key>
-      <value>"green"</value>
+      <key>grid</key>
+      <value>False</value>
     </param>
     <param>
-      <key>style3</key>
-      <value>1</value>
+      <key>id</key>
+      <value>qtgui_time_sink_x_1_0</value>
     </param>
     <param>
-      <key>marker3</key>
-      <value>-1</value>
+      <key>legend</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha3</key>
+      <key>alpha1</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label4</key>
-      <value></value>
+      <key>color1</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>width4</key>
-      <value>1</value>
+      <key>label1</key>
+      <value></value>
     </param>
     <param>
-      <key>color4</key>
-      <value>"black"</value>
+      <key>marker1</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style4</key>
+      <key>style1</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker4</key>
-      <value>-1</value>
+      <key>width1</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha4</key>
+      <key>alpha10</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label5</key>
-      <value></value>
+      <key>color10</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>width5</key>
-      <value>1</value>
+      <key>label10</key>
+      <value></value>
     </param>
     <param>
-      <key>color5</key>
-      <value>"cyan"</value>
+      <key>marker10</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style5</key>
+      <key>style10</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker5</key>
-      <value>-1</value>
+      <key>width10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha5</key>
+      <key>alpha2</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label6</key>
-      <value></value>
+      <key>color2</key>
+      <value>"red"</value>
     </param>
     <param>
-      <key>width6</key>
-      <value>1</value>
+      <key>label2</key>
+      <value></value>
     </param>
     <param>
-      <key>color6</key>
-      <value>"magenta"</value>
+      <key>marker2</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style6</key>
+      <key>style2</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker6</key>
-      <value>-1</value>
+      <key>width2</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha6</key>
+      <key>alpha3</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label7</key>
-      <value></value>
+      <key>color3</key>
+      <value>"green"</value>
     </param>
     <param>
-      <key>width7</key>
-      <value>1</value>
+      <key>label3</key>
+      <value></value>
     </param>
     <param>
-      <key>color7</key>
-      <value>"yellow"</value>
+      <key>marker3</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style7</key>
+      <key>style3</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker7</key>
-      <value>-1</value>
+      <key>width3</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha7</key>
+      <key>alpha4</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label8</key>
-      <value></value>
+      <key>color4</key>
+      <value>"black"</value>
     </param>
     <param>
-      <key>width8</key>
-      <value>1</value>
+      <key>label4</key>
+      <value></value>
     </param>
     <param>
-      <key>color8</key>
-      <value>"dark red"</value>
+      <key>marker4</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style8</key>
+      <key>style4</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker8</key>
-      <value>-1</value>
+      <key>width4</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha8</key>
+      <key>alpha5</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label9</key>
-      <value></value>
+      <key>color5</key>
+      <value>"cyan"</value>
     </param>
     <param>
-      <key>width9</key>
-      <value>1</value>
+      <key>label5</key>
+      <value></value>
     </param>
     <param>
-      <key>color9</key>
-      <value>"dark green"</value>
+      <key>marker5</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style9</key>
+      <key>style5</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker9</key>
-      <value>-1</value>
+      <key>width5</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha9</key>
+      <key>alpha6</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label10</key>
-      <value></value>
+      <key>color6</key>
+      <value>"magenta"</value>
     </param>
     <param>
-      <key>width10</key>
-      <value>1</value>
+      <key>label6</key>
+      <value></value>
     </param>
     <param>
-      <key>color10</key>
-      <value>"blue"</value>
+      <key>marker6</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style10</key>
+      <key>style6</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker10</key>
-      <value>-1</value>
+      <key>width6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha10</key>
+      <key>alpha7</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>color7</key>
+      <value>"yellow"</value>
     </param>
     <param>
-      <key>affinity</key>
+      <key>label7</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(1017, 378)</value>
+      <key>marker7</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>style7</key>
+      <value>1</value>
     </param>
-  </block>
-  <block>
-    <key>qtgui_time_sink_x</key>
     <param>
-      <key>id</key>
-      <value>qtgui_time_sink_x_0_0</value>
+      <key>width7</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>alpha8</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>type</key>
-      <value>complex</value>
+      <key>color8</key>
+      <value>"dark red"</value>
     </param>
     <param>
-      <key>name</key>
-      <value>Scope Plot</value>
+      <key>label8</key>
+      <value></value>
     </param>
     <param>
-      <key>ylabel</key>
-      <value>Amplitude</value>
+      <key>marker8</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>yunit</key>
-      <value>""</value>
+      <key>style8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>size</key>
-      <value>1024</value>
+      <key>width8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>srate</key>
-      <value>samp_rate</value>
+      <key>alpha9</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>grid</key>
-      <value>False</value>
+      <key>color9</key>
+      <value>"dark green"</value>
     </param>
     <param>
-      <key>autoscale</key>
-      <value>False</value>
+      <key>label9</key>
+      <value></value>
     </param>
     <param>
-      <key>ymin</key>
+      <key>marker9</key>
       <value>-1</value>
     </param>
     <param>
-      <key>ymax</key>
+      <key>style9</key>
       <value>1</value>
     </param>
     <param>
-      <key>nconnections</key>
+      <key>width9</key>
       <value>1</value>
     </param>
     <param>
-      <key>update_time</key>
-      <value>0.10</value>
+      <key>name</key>
+      <value>Rate</value>
     </param>
     <param>
-      <key>entags</key>
-      <value>True</value>
+      <key>nconnections</key>
+      <value>1</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value></value>
+      <key>size</key>
+      <value>1024</value>
     </param>
     <param>
-      <key>tr_mode</key>
-      <value>qtgui.TRIG_MODE_FREE</value>
+      <key>srate</key>
+      <value>samp_rate</value>
     </param>
     <param>
-      <key>tr_slope</key>
-      <value>qtgui.TRIG_SLOPE_POS</value>
+      <key>tr_chan</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>tr_delay</key>
+      <value>0</value>
     </param>
     <param>
       <key>tr_level</key>
       <value>0.0</value>
     </param>
     <param>
-      <key>tr_delay</key>
-      <value>0</value>
+      <key>tr_mode</key>
+      <value>qtgui.TRIG_MODE_FREE</value>
     </param>
     <param>
-      <key>tr_chan</key>
-      <value>0</value>
+      <key>tr_slope</key>
+      <value>qtgui.TRIG_SLOPE_POS</value>
     </param>
     <param>
       <key>tr_tag</key>
       <value>""</value>
     </param>
     <param>
-      <key>label1</key>
-      <value></value>
+      <key>type</key>
+      <value>float</value>
     </param>
     <param>
-      <key>width1</key>
-      <value>1</value>
+      <key>update_time</key>
+      <value>0.10</value>
     </param>
     <param>
-      <key>color1</key>
-      <value>"blue"</value>
+      <key>ylabel</key>
+      <value>Amplitude</value>
     </param>
     <param>
-      <key>style1</key>
+      <key>yunit</key>
+      <value>""</value>
+    </param>
+    <param>
+      <key>ymax</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker1</key>
+      <key>ymin</key>
       <value>-1</value>
     </param>
+  </block>
+  <block>
+    <key>qtgui_time_sink_x</key>
     <param>
-      <key>alpha1</key>
-      <value>1.0</value>
+      <key>autoscale</key>
+      <value>True</value>
     </param>
     <param>
-      <key>label2</key>
+      <key>axislabels</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>alias</key>
       <value></value>
     </param>
     <param>
-      <key>width2</key>
-      <value>1</value>
+      <key>comment</key>
+      <value></value>
     </param>
     <param>
-      <key>color2</key>
-      <value>"red"</value>
+      <key>ctrlpanel</key>
+      <value>False</value>
     </param>
     <param>
-      <key>style2</key>
-      <value>1</value>
+      <key>affinity</key>
+      <value></value>
     </param>
     <param>
-      <key>marker2</key>
-      <value>-1</value>
+      <key>entags</key>
+      <value>True</value>
     </param>
     <param>
-      <key>alpha2</key>
-      <value>1.0</value>
+      <key>_enabled</key>
+      <value>True</value>
     </param>
     <param>
-      <key>label3</key>
-      <value></value>
+      <key>_coordinate</key>
+      <value>(1014, 622)</value>
     </param>
     <param>
-      <key>width3</key>
-      <value>1</value>
+      <key>gui_hint</key>
+      <value>notebook@1</value>
     </param>
     <param>
-      <key>color3</key>
-      <value>"green"</value>
+      <key>_rotation</key>
+      <value>0</value>
     </param>
     <param>
-      <key>style3</key>
-      <value>1</value>
+      <key>grid</key>
+      <value>False</value>
     </param>
     <param>
-      <key>marker3</key>
-      <value>-1</value>
+      <key>id</key>
+      <value>qtgui_time_sink_x_1_1</value>
     </param>
     <param>
-      <key>alpha3</key>
+      <key>legend</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>alpha1</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label4</key>
-      <value></value>
+      <key>color1</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>width4</key>
-      <value>1</value>
+      <key>label1</key>
+      <value></value>
     </param>
     <param>
-      <key>color4</key>
-      <value>"black"</value>
+      <key>marker1</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style4</key>
+      <key>style1</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker4</key>
-      <value>-1</value>
+      <key>width1</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha4</key>
+      <key>alpha10</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label5</key>
-      <value></value>
+      <key>color10</key>
+      <value>"blue"</value>
     </param>
     <param>
-      <key>width5</key>
-      <value>1</value>
+      <key>label10</key>
+      <value></value>
     </param>
     <param>
-      <key>color5</key>
-      <value>"cyan"</value>
+      <key>marker10</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style5</key>
+      <key>style10</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker5</key>
-      <value>-1</value>
+      <key>width10</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha5</key>
+      <key>alpha2</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label6</key>
-      <value></value>
+      <key>color2</key>
+      <value>"red"</value>
     </param>
     <param>
-      <key>width6</key>
-      <value>1</value>
+      <key>label2</key>
+      <value></value>
     </param>
     <param>
-      <key>color6</key>
-      <value>"magenta"</value>
+      <key>marker2</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style6</key>
+      <key>style2</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker6</key>
-      <value>-1</value>
+      <key>width2</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha6</key>
+      <key>alpha3</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label7</key>
+      <key>color3</key>
+      <value>"green"</value>
+    </param>
+    <param>
+      <key>label3</key>
       <value></value>
     </param>
     <param>
-      <key>width7</key>
-      <value>1</value>
-    </param>
-    <param>
-      <key>color7</key>
-      <value>"yellow"</value>
+      <key>marker3</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style7</key>
+      <key>style3</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker7</key>
-      <value>-1</value>
+      <key>width3</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha7</key>
+      <key>alpha4</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label8</key>
-      <value></value>
+      <key>color4</key>
+      <value>"black"</value>
     </param>
     <param>
-      <key>width8</key>
-      <value>1</value>
+      <key>label4</key>
+      <value></value>
     </param>
     <param>
-      <key>color8</key>
-      <value>"dark red"</value>
+      <key>marker4</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style8</key>
+      <key>style4</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker8</key>
-      <value>-1</value>
+      <key>width4</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha8</key>
+      <key>alpha5</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label9</key>
-      <value></value>
+      <key>color5</key>
+      <value>"cyan"</value>
     </param>
     <param>
-      <key>width9</key>
-      <value>1</value>
+      <key>label5</key>
+      <value></value>
     </param>
     <param>
-      <key>color9</key>
-      <value>"dark green"</value>
+      <key>marker5</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style9</key>
+      <key>style5</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker9</key>
-      <value>-1</value>
+      <key>width5</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha9</key>
+      <key>alpha6</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>label10</key>
-      <value></value>
+      <key>color6</key>
+      <value>"magenta"</value>
     </param>
     <param>
-      <key>width10</key>
-      <value>1</value>
+      <key>label6</key>
+      <value></value>
     </param>
     <param>
-      <key>color10</key>
-      <value>"blue"</value>
+      <key>marker6</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>style10</key>
+      <key>style6</key>
       <value>1</value>
     </param>
     <param>
-      <key>marker10</key>
-      <value>-1</value>
+      <key>width6</key>
+      <value>1</value>
     </param>
     <param>
-      <key>alpha10</key>
+      <key>alpha7</key>
       <value>1.0</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>color7</key>
+      <value>"yellow"</value>
     </param>
     <param>
-      <key>affinity</key>
+      <key>label7</key>
       <value></value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(1029, 281)</value>
-    </param>
-    <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>marker7</key>
+      <value>-1</value>
     </param>
-  </block>
-  <block>
-    <key>variable_qtgui_range</key>
     <param>
-      <key>id</key>
-      <value>time_bw</value>
+      <key>style7</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>width7</key>
+      <value>1</value>
     </param>
     <param>
-      <key>label</key>
-      <value>Timing Loop BW</value>
+      <key>alpha8</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>value</key>
-      <value>0</value>
+      <key>color8</key>
+      <value>"dark red"</value>
     </param>
     <param>
-      <key>start</key>
-      <value>0</value>
+      <key>label8</key>
+      <value></value>
     </param>
     <param>
-      <key>stop</key>
-      <value>.1</value>
+      <key>marker8</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>step</key>
-      <value>.001</value>
+      <key>style8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>widget</key>
-      <value>counter_slider</value>
+      <key>width8</key>
+      <value>1</value>
     </param>
     <param>
-      <key>orient</key>
-      <value>Qt.Horizontal</value>
+      <key>alpha9</key>
+      <value>1.0</value>
     </param>
     <param>
-      <key>min_len</key>
-      <value>200</value>
+      <key>color9</key>
+      <value>"dark green"</value>
     </param>
     <param>
-      <key>gui_hint</key>
-      <value>1,2,1,1</value>
+      <key>label9</key>
+      <value></value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>marker9</key>
+      <value>-1</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(451, 0)</value>
+      <key>style9</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>width9</key>
+      <value>1</value>
     </param>
-  </block>
-  <block>
-    <key>digital_pfb_clock_sync_xxx</key>
     <param>
-      <key>id</key>
-      <value>digital_pfb_clock_sync_xxx_0</value>
+      <key>name</key>
+      <value>Phase</value>
     </param>
     <param>
-      <key>_enabled</key>
-      <value>True</value>
+      <key>nconnections</key>
+      <value>1</value>
     </param>
     <param>
-      <key>type</key>
-      <value>ccf</value>
+      <key>size</key>
+      <value>1024</value>
     </param>
     <param>
-      <key>sps</key>
-      <value>spb</value>
+      <key>srate</key>
+      <value>samp_rate</value>
     </param>
     <param>
-      <key>loop_bw</key>
-      <value>time_bw</value>
+      <key>tr_chan</key>
+      <value>0</value>
     </param>
     <param>
-      <key>taps</key>
-      <value>firdes.root_raised_cosine(nfilts, nfilts*spb, 1.0, rolloff, 44*nfilts)</value>
+      <key>tr_delay</key>
+      <value>0</value>
     </param>
     <param>
-      <key>filter_size</key>
-      <value>nfilts</value>
+      <key>tr_level</key>
+      <value>0.0</value>
     </param>
     <param>
-      <key>init_phase</key>
-      <value>16</value>
+      <key>tr_mode</key>
+      <value>qtgui.TRIG_MODE_FREE</value>
     </param>
     <param>
-      <key>max_dev</key>
-      <value>1.5</value>
+      <key>tr_slope</key>
+      <value>qtgui.TRIG_SLOPE_POS</value>
     </param>
     <param>
-      <key>osps</key>
-      <value>1</value>
+      <key>tr_tag</key>
+      <value>""</value>
     </param>
     <param>
-      <key>alias</key>
-      <value></value>
+      <key>type</key>
+      <value>float</value>
     </param>
     <param>
-      <key>affinity</key>
-      <value></value>
+      <key>update_time</key>
+      <value>0.10</value>
     </param>
     <param>
-      <key>minoutbuf</key>
-      <value>0</value>
+      <key>ylabel</key>
+      <value>Amplitude</value>
     </param>
     <param>
-      <key>maxoutbuf</key>
-      <value>0</value>
+      <key>yunit</key>
+      <value>""</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(467, 403)</value>
+      <key>ymax</key>
+      <value>1</value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
+      <key>ymin</key>
+      <value>-1</value>
     </param>
   </block>
   <connection>
-    <source_block_id>blocks_throttle_0</source_block_id>
-    <sink_block_id>digital_pfb_clock_sync_xxx_0</sink_block_id>
+    <source_block_id>analog_random_source_x</source_block_id>
+    <sink_block_id>digital_chunks_to_symbols_xx</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
-    <source_block_id>pfb_arb_resampler_xxx_0</source_block_id>
-    <sink_block_id>blocks_multiply_const_vxx_0</sink_block_id>
+    <source_block_id>blocks_multiply_const_vxx_0</source_block_id>
+    <sink_block_id>channels_channel_model_0</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
-    <source_block_id>digital_chunks_to_symbols_xx</source_block_id>
-    <sink_block_id>pfb_arb_resampler_xxx_0</sink_block_id>
+    <source_block_id>blocks_multiply_const_vxx_0</source_block_id>
+    <sink_block_id>qtgui_time_sink_x_0</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
-    <source_block_id>analog_random_source_x</source_block_id>
-    <sink_block_id>digital_chunks_to_symbols_xx</sink_block_id>
+    <source_block_id>blocks_throttle_0</source_block_id>
+    <sink_block_id>digital_pfb_clock_sync_xxx_0</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
@@ -2699,15 +2908,15 @@
     <sink_key>0</sink_key>
   </connection>
   <connection>
-    <source_block_id>blocks_multiply_const_vxx_0</source_block_id>
-    <sink_block_id>channels_channel_model_0</sink_block_id>
+    <source_block_id>digital_chunks_to_symbols_xx</source_block_id>
+    <sink_block_id>pfb_arb_resampler_xxx_0</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
-    <source_block_id>blocks_multiply_const_vxx_0</source_block_id>
-    <sink_block_id>qtgui_time_sink_x_0</sink_block_id>
-    <source_key>0</source_key>
+    <source_block_id>digital_pfb_clock_sync_xxx_0</source_block_id>
+    <sink_block_id>qtgui_time_sink_x_1</sink_block_id>
+    <source_key>1</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
@@ -2716,12 +2925,6 @@
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
-  <connection>
-    <source_block_id>digital_pfb_clock_sync_xxx_0</source_block_id>
-    <sink_block_id>qtgui_time_sink_x_1</sink_block_id>
-    <source_key>1</source_key>
-    <sink_key>0</sink_key>
-  </connection>
   <connection>
     <source_block_id>digital_pfb_clock_sync_xxx_0</source_block_id>
     <sink_block_id>qtgui_time_sink_x_1_1</sink_block_id>
@@ -2734,4 +2937,10 @@
     <source_key>2</source_key>
     <sink_key>0</sink_key>
   </connection>
+  <connection>
+    <source_block_id>pfb_arb_resampler_xxx_0</source_block_id>
+    <sink_block_id>blocks_multiply_const_vxx_0</sink_block_id>
+    <source_key>0</source_key>
+    <sink_key>0</sink_key>
+  </connection>
 </flow_graph>
diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index 5a32c7cd70..d0002325b6 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -32,12 +32,6 @@ GR_PYTHON_CHECK_MODULE_RAW(
     PYTHON_MIN_VER_FOUND
 )
 
-GR_PYTHON_CHECK_MODULE_RAW(
-    "Cheetah >= 2.0.0"
-    "import Cheetah; assert Cheetah.Version >= '2.0.0'"
-    CHEETAH_FOUND
-)
-
 GR_PYTHON_CHECK_MODULE_RAW(
     "PyYAML >= 3.10"
     "import yaml; assert yaml.__version__ >= '3.11'"
@@ -94,7 +88,8 @@ include(GrComponent)
 if(NOT CMAKE_CROSSCOMPILING)
     set(grc_python_deps
         PYTHON_MIN_VER_FOUND
-        CHEETAH_FOUND
+        PYYAML_FOUND
+        MAKO_FOUND
         LXML_FOUND
         PYGI_FOUND
         GTK_GI_FOUND
diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml
index 3125864d4d..2a063f1f60 100644
--- a/grc/blocks/block_tree.xml
+++ b/grc/blocks/block_tree.xml
@@ -8,12 +8,6 @@
       <block>virtual_source</block>
       <block>virtual_sink</block>
 
-      <block>bus_sink</block>
-      <block>bus_source</block>
-      <block>bus_structure_sink</block>
-      <block>bus_structure_source</block>
-
-      <block>epy_block</block>
       <block>epy_module</block>
 
       <block>note</block>
diff --git a/grc/blocks/bus_sink.xml b/grc/blocks/bus_sink.xml
deleted file mode 100644
index 029820dc2c..0000000000
--- a/grc/blocks/bus_sink.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Bus Sink
-###################################################
- -->
-<block>
-	<name>Bus Sink</name>
-	<key>bus_sink</key>
-	<make>$yesno.yesno</make>
-
-	<param>
-		<name>On/Off</name>
-		<key>yesno</key>
-		<type>enum</type>
-		<option>
-			<name>On</name>
-			<key>on</key>
-			<opt>yesno:True</opt>
-		</option>
-		<option>
-			<name>Off</name>
-			<key>off</key>
-			<opt>yesno:False</opt>
-		</option>
-	</param>
-</block>
diff --git a/grc/blocks/bus_source.xml b/grc/blocks/bus_source.xml
deleted file mode 100644
index e5b5c2b9bf..0000000000
--- a/grc/blocks/bus_source.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Bus Sink
-###################################################
- -->
-<block>
-	<name>Bus Source</name>
-	<key>bus_source</key>
-	<make>$yesno.yesno</make>
-
-	<param>
-		<name>On/Off</name>
-		<key>yesno</key>
-		<type>enum</type>
-		<option>
-			<name>On</name>
-			<key>on</key>
-			<opt>yesno:True</opt>
-		</option>
-		<option>
-			<name>Off</name>
-			<key>off</key>
-			<opt>yesno:False</opt>
-		</option>
-	</param>
-</block>
diff --git a/grc/blocks/bus_structure_sink.xml b/grc/blocks/bus_structure_sink.xml
deleted file mode 100644
index 3adac92810..0000000000
--- a/grc/blocks/bus_structure_sink.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Bus Sink
-###################################################
- -->
-<block>
-	<name>Bus Sink Structure</name>
-	<key>bus_structure_sink</key>
-	<make>None</make>
-
-	<param>
-		<name>Structure</name>
-		<key>struct</key>
-		<value></value>
-		<type>raw</type>
-	</param>
-</block>
diff --git a/grc/blocks/bus_structure_source.xml b/grc/blocks/bus_structure_source.xml
deleted file mode 100644
index 34e7c049a2..0000000000
--- a/grc/blocks/bus_structure_source.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##Bus Sink
-###################################################
- -->
-<block>
-	<name>Bus Source Structure</name>
-	<key>bus_structure_source</key>
-	<make>None</make>
-
-	<param>
-		<name>Structure</name>
-		<key>struct</key>
-		<value></value>
-		<type>raw</type>
-	</param>
-</block>
diff --git a/grc/blocks/epy_block.xml b/grc/blocks/epy_block.xml
deleted file mode 100644
index 65e78c4062..0000000000
--- a/grc/blocks/epy_block.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0"?>
-<block>
-    <name>Python Block</name>
-    <key>epy_block</key>
-    <import></import>
-    <make></make>
-    <param><!-- Cache the last working block IO to keep FG sane -->
-        <name>Block Io</name>
-        <key>_io_cache</key>
-        <type>string</type>
-    <hide>all</hide>
-    </param>
-    <param>
-        <name>Code</name>
-        <key>_source_code</key>
-    <value>"""
-Embedded Python Blocks:
-
-Each time this file is saved, GRC will instantiate the first class it finds
-to get ports and parameters of your block. The arguments to __init__  will
-be the parameters. All of them are required to have default values!
-"""
-
-import numpy as np
-from gnuradio import gr
-
-
-class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
-    """Embedded Python Block example - a simple multiply const"""
-
-    def __init__(self, example_param=1.0):  # only default arguments here
-        """arguments to this function show up as parameters in GRC"""
-        gr.sync_block.__init__(
-            self,
-            name='Embedded Python Block',   # will show up in GRC
-            in_sig=[np.complex64],
-            out_sig=[np.complex64]
-        )
-        # if an attribute with the same name as a parameter is found,
-        # a callback is registered (properties work, too).
-        self.example_param = example_param
-
-    def work(self, input_items, output_items):
-        """example: multiply with constant"""
-        output_items[0][:] = input_items[0] * self.example_param
-        return len(output_items[0])
-</value>
-    <type>_multiline_python_external</type>
-    <hide>part</hide>
-    </param>
-    <doc>This block represents an arbitrary GNU Radio Python Block.
-
-Its source code can be accessed through the parameter 'Code' which opens your editor. Each time you save changes in the editor, GRC will update the block. This includes the number, names and defaults of the parameters, the ports (stream and message) and the block name and documentation.
-
-Block Documentation:
-(will be replaced the docstring of your block class)
-</doc>
-</block>
diff --git a/grc/blocks/epy_module.xml b/grc/blocks/epy_module.xml
deleted file mode 100644
index fa3e5f91f4..0000000000
--- a/grc/blocks/epy_module.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0"?>
-<block>
-	<name>Python Module</name>
-	<key>epy_module</key>
-	<import>import $id  # embedded python module</import>
-	<make></make>
-	<param>
-		<name>Code</name>
-		<key>source_code</key>
-		<value># this module will be imported in the into your flowgraph</value>
-		<type>_multiline_python_external</type>
-		<hide>part</hide>
-	</param>
-	<doc>This block lets you embed a python module in your flowgraph.
-
-Code you put in this module is accessible in other blocks using the ID of this
-block. Example:
-
-If you put
-
-    a = 2
-
-    def double(arg):
-        return 2 * arg
-
-in a Python Module Block with the ID 'stuff' you can use code like
-
-    stuff.a  # evals to 2
-    stuff.double(3)  # evals to 6
-
-to set parameters of other blocks in your flowgraph.</doc>
-</block>
diff --git a/grc/blocks/gr_message_domain.xml b/grc/blocks/gr_message_domain.xml
deleted file mode 100644
index bc8add99ab..0000000000
--- a/grc/blocks/gr_message_domain.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##GNU Radio default domain 'gr_message'
-###################################################
- -->
- <domain>
-  <name>GR Message</name>
-  <key>gr_message</key>
-  <color>#000</color>
-  <multiple_sources>True</multiple_sources>
-  <connection>
-    <source_domain>gr_message</source_domain>
-    <sink_domain>gr_message</sink_domain>
-    <make>#slurp
-        self.msg_connect($make_port_sig($source), $make_port_sig($sink))#slurp
-    </make>
-  </connection>
-</domain>
diff --git a/grc/blocks/gr_stream_domain.xml b/grc/blocks/gr_stream_domain.xml
deleted file mode 100644
index 66026448ca..0000000000
--- a/grc/blocks/gr_stream_domain.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0"?>
-<!--
-###################################################
-##GNU Radio default domain 'gr_stream'
-###################################################
- -->
- <domain>
-  <name>GR Stream</name>
-  <key>gr_stream</key>
-  <color>#000</color>
-  <connection>
-    <source_domain>gr_stream</source_domain>
-    <sink_domain>gr_stream</sink_domain>
-    <make>#slurp
-        self.connect($make_port_sig($source), $make_port_sig($sink))#slurp
-    </make>
-  </connection>
-</domain>
diff --git a/grc/blocks/import.xml b/grc/blocks/import.xml
index 59f807bacb..58e99a2d01 100644
--- a/grc/blocks/import.xml
+++ b/grc/blocks/import.xml
@@ -7,11 +7,11 @@
 <block>
 	<name>Import</name>
 	<key>import</key>
-	<import>$import</import>
+	<import>$imports</import>
 	<make></make>
 	<param>
 		<name>Import</name>
-		<key>import</key>
+		<key>imports</key>
 		<value></value>
 		<type>import</type>
 	</param>
diff --git a/grc/blocks/message.domain.yml b/grc/blocks/message.domain.yml
new file mode 100644
index 0000000000..7e6cc529d9
--- /dev/null
+++ b/grc/blocks/message.domain.yml
@@ -0,0 +1,10 @@
+id: message
+label: Message
+color: "#FFFFFF"
+
+multiple_connections_per_input: true
+multiple_connections_per_output: true
+
+templates:
+-   type: [message, message]
+    connect: self.msg_connect(${ make_port_sig(source) }, ${ make_port_sig(sink) })
diff --git a/grc/blocks/options.xml b/grc/blocks/options.xml
index 252a0b2e2d..c349cd956a 100644
--- a/grc/blocks/options.xml
+++ b/grc/blocks/options.xml
@@ -100,7 +100,7 @@ else: self.stop(); self.wait()</callback>
 		<key>run</key>
 		<value>True</value>
 		<type>bool</type>
-		<hide>#if $generate_options() == 'qt_gui' then ('part' if $run() else 'none') else 'all'#</hide>
+		<hide>#if $generate_options() != 'qt_gui' then 'all' else ('part' if $run() else 'none')#</hide>
 		<option>
 			<name>Autostart</name>
 			<key>True</key>
@@ -137,7 +137,7 @@ else: self.stop(); self.wait()</callback>
 		<key>qt_qss_theme</key>
 		<value></value>
 		<type>file_open</type>
-		<hide>#if $generate_options() == 'qt_gui' then ('none' if $qt_qss_theme() else 'part') else 'all'#</hide>
+		<hide>#if $generate_options() != 'qt_gui' then 'all' else ('none' if $qt_qss_theme() else 'part')#</hide>
 	</param>
 	<param>
 		<name>Thread-safe setters</name>
diff --git a/grc/blocks/stream.domain.yml b/grc/blocks/stream.domain.yml
new file mode 100644
index 0000000000..a4d786f8b4
--- /dev/null
+++ b/grc/blocks/stream.domain.yml
@@ -0,0 +1,10 @@
+id: stream
+label: Stream
+color: "#000000"
+
+multiple_connections_per_input: false
+multiple_connections_per_output: true
+
+templates:
+-   type: [stream, stream]
+    connect: self.connect(${ make_port_sig(source) }, ${ make_port_sig(sink) })
diff --git a/grc/compiler.py b/grc/compiler.py
index b2361b86eb..a5f6c3edcd 100755
--- a/grc/compiler.py
+++ b/grc/compiler.py
@@ -26,7 +26,7 @@ import subprocess
 from gnuradio import gr
 
 from .core import Messages
-from .core.Platform import Platform
+from .core.platform import Platform
 
 
 def argument_parser():
@@ -53,6 +53,8 @@ def main(args=None):
         version=gr.version(),
         version_parts=(gr.major_version(), gr.api_version(), gr.minor_version())
     )
+    platform.build_library()
+
     out_dir = args.output if not args.user_lib_dir else platform.config.hier_block_lib_dir
     if os.path.exists(out_dir):
         pass  # all is well
diff --git a/grc/converter/__init__.py b/grc/converter/__init__.py
new file mode 100644
index 0000000000..224f2e9afc
--- /dev/null
+++ b/grc/converter/__init__.py
@@ -0,0 +1,20 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from .main import Converter
diff --git a/grc/converter/__main__.py b/grc/converter/__main__.py
new file mode 100644
index 0000000000..6efc2d7c59
--- /dev/null
+++ b/grc/converter/__main__.py
@@ -0,0 +1,21 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+# TODO: implement cli
+
diff --git a/grc/converter/block.dtd b/grc/converter/block.dtd
new file mode 100644
index 0000000000..145f4d8610
--- /dev/null
+++ b/grc/converter/block.dtd
@@ -0,0 +1,69 @@
+<!--
+Copyright 2008 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
+-->
+<!--
+    gnuradio_python.blocks.dtd
+    Josh Blum
+    The document type definition for blocks.
+ -->
+<!--
+    Top level element.
+    A block contains a name, ...parameters list, and list of IO ports.
+ -->
+<!ELEMENT block (name, key, category?, throttle?, flags?, import*, var_make?, var_value?,
+        make, callback*, param_tab_order?, param*, bus_sink?, bus_source?, check*,
+        sink*, source*, bus_structure_sink?, bus_structure_source?, doc?,  grc_source?)>
+<!--
+    Sub level elements.
+ -->
+<!ELEMENT param_tab_order (tab+)>
+<!ELEMENT param (base_key?, name, key, value?, type?, hide?, option*, tab?)>
+<!ELEMENT option (name, key, opt*)>
+<!ELEMENT sink (name, type, vlen?, domain?, nports?, optional?, hide?)>
+<!ELEMENT source (name, type, vlen?, domain?, nports?, optional?, hide?)>
+<!--
+    Bottom level elements.
+    Character data only.
+ -->
+<!ELEMENT category (#PCDATA)>
+<!ELEMENT import (#PCDATA)>
+<!ELEMENT doc (#PCDATA)>
+<!ELEMENT grc_source (#PCDATA)>
+<!ELEMENT tab (#PCDATA)>
+<!ELEMENT name (#PCDATA)>
+<!ELEMENT base_key (#PCDATA)>
+<!ELEMENT key (#PCDATA)>
+<!ELEMENT check (#PCDATA)>
+<!ELEMENT bus_sink (#PCDATA)>
+<!ELEMENT bus_source (#PCDATA)>
+<!ELEMENT opt (#PCDATA)>
+<!ELEMENT type (#PCDATA)>
+<!ELEMENT hide (#PCDATA)>
+<!ELEMENT vlen (#PCDATA)>
+<!ELEMENT domain (#PCDATA)>
+<!ELEMENT nports (#PCDATA)>
+<!ELEMENT bus_structure_sink (#PCDATA)>
+<!ELEMENT bus_structure_source (#PCDATA)>
+<!ELEMENT var_make (#PCDATA)>
+<!ELEMENT var_value (#PCDATA)>
+<!ELEMENT make (#PCDATA)>
+<!ELEMENT value (#PCDATA)>
+<!ELEMENT callback (#PCDATA)>
+<!ELEMENT optional (#PCDATA)>
+<!ELEMENT throttle (#PCDATA)>
+<!ELEMENT flags (#PCDATA)>
diff --git a/grc/converter/block.py b/grc/converter/block.py
new file mode 100644
index 0000000000..04e5c905a0
--- /dev/null
+++ b/grc/converter/block.py
@@ -0,0 +1,219 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+"""
+Converter for legacy block definitions in XML format
+
+- Cheetah expressions that can not be converted are passed to Cheetah for now
+- Instead of generating a Block subclass directly a string representation is
+  used and evaluated. This is slower / lamer but allows us to show the user
+  how a converted definition would look like
+"""
+
+from __future__ import absolute_import, division, print_function
+
+from collections import OrderedDict, defaultdict
+from itertools import chain
+
+from ..core.io import yaml
+from . import cheetah_converter, xml
+
+current_file_format = 1
+reserved_block_keys = ('import', )  # todo: add more keys
+
+
+def from_xml(filename):
+    """Load block description from xml file"""
+    element, version_info = xml.load(filename, 'block.dtd')
+
+    try:
+        data = convert_block_xml(element)
+    except NameError:
+        raise ValueError('Conversion failed', filename)
+
+    return data
+
+
+def dump(data, stream):
+    out = yaml.dump(data)
+
+    replace = [
+        ('parameters:', '\nparameters:'),
+        ('inputs:', '\ninputs:'),
+        ('outputs:', '\noutputs:'),
+        ('templates:', '\ntemplates:'),
+        ('documentation:', '\ndocumentation:'),
+        ('file_format:', '\nfile_format:'),
+    ]
+    for r in replace:
+        out = out.replace(*r)
+    prefix = '# auto-generated by grc.converter\n\n'
+    stream.write(prefix + out)
+
+
+no_value = object()
+dummy = cheetah_converter.DummyConverter()
+
+
+def convert_block_xml(node):
+    converter = cheetah_converter.Converter(names={
+        param_node.findtext('key'): {
+            opt_node.text.split(':')[0]
+            for opt_node in next(param_node.iterfind('option'), param_node).iterfind('opt')
+        } for param_node in node.iterfind('param')
+    })
+
+    block_id = node.findtext('key')
+    if block_id in reserved_block_keys:
+        block_id += '_'
+
+    data = OrderedDict()
+    data['id'] = block_id
+    data['label'] = node.findtext('name') or no_value
+    data['category'] = node.findtext('category') or no_value
+    data['flags'] = node.findtext('flags') or no_value
+
+    data['parameters'] = [convert_param_xml(param_node, converter.to_python_dec)
+                          for param_node in node.iterfind('param')] or no_value
+    # data['params'] = {p.pop('key'): p for p in data['params']}
+
+    data['inputs'] = [convert_port_xml(port_node, converter.to_python_dec)
+                      for port_node in node.iterfind('sink')] or no_value
+
+    data['outputs'] = [convert_port_xml(port_node, converter.to_python_dec)
+                       for port_node in node.iterfind('source')] or no_value
+
+    data['checks'] = [converter.to_python_dec(check_node.text)
+                      for check_node in node.iterfind('checks')] or no_value
+    data['value'] = (
+        converter.to_python_dec(node.findtext('var_value')) or
+        ('${ value }' if block_id.startswith('variable') else no_value)
+    )
+
+    data['templates'] = convert_templates(node, converter.to_mako, block_id) or no_value
+
+    docs = node.findtext('doc')
+    if docs:
+        docs = docs.strip().replace('\\\n', '')
+        data['documentation'] = yaml.MultiLineString(docs)
+
+    data['file_format'] = current_file_format
+
+    data = OrderedDict((key, value) for key, value in data.items() if value is not no_value)
+    auto_hide_params_for_item_sizes(data)
+
+    return data
+
+
+def auto_hide_params_for_item_sizes(data):
+    item_size_templates = []
+    vlen_templates = []
+    for port in chain(*[data.get(direction, []) for direction in ['inputs', 'outputs']]):
+        for key in ['dtype', 'multiplicity']:
+            item_size_templates.append(str(port.get(key, '')))
+        vlen_templates.append(str(port.get('vlen', '')))
+    item_size_templates = ' '.join(value for value in item_size_templates if '${' in value)
+    vlen_templates = ' '.join(value for value in vlen_templates if '${' in value)
+
+    for param in data.get('parameters', []):
+        if param['id'] in item_size_templates:
+            param.setdefault('hide', 'part')
+        if param['id'] in vlen_templates:
+            param.setdefault('hide', "${ 'part' if vlen == 1 else 'none' }")
+
+
+def convert_templates(node, convert, block_id=''):
+    templates = OrderedDict()
+
+    imports = '\n'.join(convert(import_node.text)
+                        for import_node in node.iterfind('import'))
+    if '\n' in imports:
+        imports = yaml.MultiLineString(imports)
+    templates['imports'] = imports or no_value
+
+    templates['var_make'] = convert(node.findtext('var_make') or '') or no_value
+
+    make = convert(node.findtext('make') or '')
+    if make:
+        check_mako_template(block_id, make)
+    if '\n' in make:
+        make = yaml.MultiLineString(make)
+    templates['make'] = make or no_value
+
+    templates['callbacks'] = [
+         convert(cb_node.text) for cb_node in node.iterfind('callback')
+    ] or no_value
+
+    return OrderedDict((key, value) for key, value in templates.items() if value is not no_value)
+
+
+def convert_param_xml(node, convert):
+    param = OrderedDict()
+    param['id'] = node.findtext('key').strip()
+    param['label'] = node.findtext('name').strip()
+    param['category'] = node.findtext('tab') or no_value
+
+    param['dtype'] = convert(node.findtext('type') or '')
+    param['default'] = node.findtext('value') or no_value
+
+    options = yaml.ListFlowing(on.findtext('key') for on in node.iterfind('option'))
+    option_labels = yaml.ListFlowing(on.findtext('name') for on in node.iterfind('option'))
+    param['options'] = options or no_value
+    if not all(str(o).title() == l for o, l in zip(options, option_labels)):
+        param['option_labels'] = option_labels
+
+    attributes = defaultdict(yaml.ListFlowing)
+    for option_n in node.iterfind('option'):
+        for opt_n in option_n.iterfind('opt'):
+            key, value = opt_n.text.split(':', 2)
+            attributes[key].append(value)
+    param['option_attributes'] = dict(attributes) or no_value
+
+    param['hide'] = convert(node.findtext('hide')) or no_value
+
+    return OrderedDict((key, value) for key, value in param.items() if value is not no_value)
+
+
+def convert_port_xml(node, convert):
+    port = OrderedDict()
+    label = node.findtext('name')
+    # default values:
+    port['label'] = label if label not in ('in', 'out') else no_value
+
+    dtype = convert(node.findtext('type'))
+    # TODO: detect dyn message ports
+    port['domain'] = domain = 'message' if dtype == 'message' else 'stream'
+    if domain == 'message':
+        port['id'], port['label'] = label, no_value
+    else:
+        port['dtype'] = dtype
+        vlen = node.findtext('vlen')
+        port['vlen'] = int(vlen) if vlen and vlen.isdigit() else convert(vlen) or no_value
+
+    port['multiplicity'] = convert(node.findtext('nports')) or no_value
+    port['optional'] = bool(node.findtext('optional')) or no_value
+    port['hide'] = convert(node.findtext('hide')) or no_value
+
+    return OrderedDict((key, value) for key, value in port.items() if value is not no_value)
+
+
+def check_mako_template(block_id, expr):
+    import sys
+    from mako.template import Template
+    try:
+        Template(expr)
+    except Exception as error:
+        print(block_id, expr, type(error), error, '', sep='\n', file=sys.stderr)
diff --git a/grc/converter/block_tree.dtd b/grc/converter/block_tree.dtd
new file mode 100644
index 0000000000..9e23576477
--- /dev/null
+++ b/grc/converter/block_tree.dtd
@@ -0,0 +1,26 @@
+<!--
+Copyright 2008 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
+-->
+<!--
+    block_tree.dtd
+    Josh Blum
+    The document type definition for a block tree category listing.
+ -->
+<!ELEMENT cat (name, cat*, block*)>
+<!ELEMENT name (#PCDATA)>
+<!ELEMENT block (#PCDATA)>
diff --git a/grc/converter/block_tree.py b/grc/converter/block_tree.py
new file mode 100644
index 0000000000..dee9adba49
--- /dev/null
+++ b/grc/converter/block_tree.py
@@ -0,0 +1,56 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+"""
+Converter for legacy block tree definitions in XML format
+"""
+
+from __future__ import absolute_import, print_function
+
+from ..core.io import yaml
+from . import xml
+
+
+def from_xml(filename):
+    """Load block tree description from xml file"""
+    element, version_info = xml.load(filename, 'block_tree.dtd')
+
+    try:
+        data = convert_category_node(element)
+    except NameError:
+        raise ValueError('Conversion failed', filename)
+
+    return data
+
+
+def dump(data, stream):
+    out = yaml.dump(data, indent=2)
+    prefix = '# auto-generated by grc.converter\n\n'
+    stream.write(prefix + out)
+
+
+def convert_category_node(node):
+    """convert nested <cat> tags to nested lists dicts"""
+    assert node.tag == 'cat'
+    name, elements = '', []
+    for child in node:
+        if child.tag == 'name':
+            name = child.text.strip()
+        elif child.tag == 'block':
+            elements.append(child.text.strip())
+        elif child.tag == 'cat':
+            elements.append(convert_category_node(child))
+    return {name: elements}
diff --git a/grc/converter/cheetah_converter.py b/grc/converter/cheetah_converter.py
new file mode 100644
index 0000000000..16fea32c99
--- /dev/null
+++ b/grc/converter/cheetah_converter.py
@@ -0,0 +1,277 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import, print_function
+
+import collections
+import re
+import string
+
+delims = {'(': ')', '[': ']', '{': '}', '': ', #\\*:'}
+identifier_start = '_' + string.ascii_letters + ''.join(delims.keys())
+string_delims = '"\''
+
+cheetah_substitution = re.compile(
+    r'^\$((?P<d1>\()|(?P<d2>\{)|(?P<d3>\[)|)'
+    r'(?P<arg>[_a-zA-Z][_a-zA-Z0-9]*(?:\.[_a-zA-Z][_a-zA-Z0-9]*)?)(?P<eval>\(\))?'
+    r'(?(d1)\)|(?(d2)\}|(?(d3)\]|)))$'
+)
+cheetah_inline_if = re.compile(r'#if (?P<cond>.*) then (?P<then>.*?) ?else (?P<else>.*?) ?(#|$)')
+
+
+class Python(object):
+    start = ''
+    end = ''
+    nested_start = ''
+    nested_end = ''
+    eval = ''
+    type = str  # yaml_output.Eval
+
+
+class FormatString(Python):
+    start = '{'
+    end = '}'
+    nested_start = '{'
+    nested_end = '}'
+    eval = ':eval'
+    type = str
+
+
+class Mako(Python):
+    start = '${'
+    end = '}'
+    nested_start = ''
+    nested_end = ''
+    type = str
+
+
+class Converter(object):
+
+    def __init__(self, names):
+        self.stats = collections.defaultdict(int)
+        self.names = set(names)
+        self.extended = set(self._iter_identifiers(names))
+
+    @staticmethod
+    def _iter_identifiers(names):
+        if not isinstance(names, dict):
+            names = {name: {} for name in names}
+        for key, sub_keys in names.items():
+            yield key
+            for sub_key in sub_keys:
+                yield '{}.{}'.format(key, sub_key)
+
+    def to_python(self, expr):
+        return self.convert(expr=expr, spec=Python)
+
+    def to_python_dec(self, expr):
+        converted = self.convert(expr=expr, spec=Python)
+        if converted and converted != expr:
+            converted = '${ ' + converted.strip() + ' }'
+        return converted
+
+    def to_format_string(self, expr):
+        return self.convert(expr=expr, spec=FormatString)
+
+    def to_mako(self, expr):
+        return self.convert(expr=expr, spec=Mako)
+
+    def convert(self, expr, spec=Python):
+        if not expr:
+            return ''
+
+        elif '$' not in expr:
+            return expr
+
+        try:
+            return self.convert_simple(expr, spec)
+        except ValueError:
+            pass
+
+        try:
+            if '#if' in expr and '\n' not in expr:
+                expr = self.convert_inline_conditional(expr, spec)
+            return self.convert_hard(expr, spec)
+        except ValueError:
+            return 'Cheetah! ' + expr
+
+    def convert_simple(self, expr, spec=Python):
+        match = cheetah_substitution.match(expr)
+        if not match:
+            raise ValueError('Not a simple substitution: ' + expr)
+
+        identifier = match.group('arg')
+        if identifier not in self.extended:
+            raise NameError('Unknown substitution {!r}'.format(identifier))
+        if match.group('eval'):
+            identifier += spec.eval
+
+        out = spec.start + identifier + spec.end
+        if '$' in out or '#' in out:
+            raise ValueError('Failed to convert: ' + expr)
+
+        self.stats['simple'] += 1
+        return spec.type(out)
+
+    def convert_hard(self, expr, spec=Python):
+        lines = '\n'.join(self.convert_hard_line(line, spec) for line in expr.split('\n'))
+        if spec == Mako:
+            # no line-continuation before a mako control structure
+            lines = re.sub(r'\\\n(\s*%)', r'\n\1', lines)
+        return lines
+
+    def convert_hard_line(self, expr, spec=Python):
+        if spec == Mako:
+            if '#set' in expr:
+                ws, set_, statement = expr.partition('#set ')
+                return ws + '<% ' + self.to_python(statement) + ' %>'
+
+            if '#if' in expr:
+                ws, if_, condition = expr.partition('#if ')
+                return ws + '% if ' + self.to_python(condition) + ':'
+            if '#else if' in expr:
+                ws, elif_, condition = expr.partition('#else if ')
+                return ws + '% elif ' + self.to_python(condition) + ':'
+            if '#else' in expr:
+                return expr.replace('#else', '% else:')
+            if '#end if' in expr:
+                return expr.replace('#end if', '% endif')
+
+            if '#slurp' in expr:
+                expr = expr.split('#slurp', 1)[0] + '\\'
+        return self.convert_hard_replace(expr, spec)
+
+    def convert_hard_replace(self, expr, spec=Python):
+        counts = collections.Counter()
+
+        def all_delims_closed():
+            for opener_, closer_ in delims.items():
+                if counts[opener_] != counts[closer_]:
+                    return False
+            return True
+
+        def extra_close():
+            for opener_, closer_ in delims.items():
+                if counts[opener_] < counts[closer_]:
+                    return True
+            return False
+
+        out = []
+        delim_to_find = False
+
+        pos = 0
+        char = ''
+        in_string = None
+        while pos < len(expr):
+            prev, char = char, expr[pos]
+            counts.update(char)
+
+            if char in string_delims:
+                if not in_string:
+                    in_string = char
+                elif char == in_string:
+                    in_string = None
+                    out.append(char)
+                    pos += 1
+                    continue
+            if in_string:
+                out.append(char)
+                pos += 1
+                continue
+
+            if char == '$':
+                pass  # no output
+
+            elif prev == '$':
+                if char not in identifier_start:  # not a substitution
+                    out.append('$' + char)  # now print the $ we skipped over
+
+                elif not delim_to_find:  # start of a substitution
+                    try:
+                        delim_to_find = delims[char]
+                        out.append(spec.start)
+                    except KeyError:
+                        if char in identifier_start:
+                            delim_to_find = delims['']
+                            out.append(spec.start)
+                            out.append(char)
+
+                    counts.clear()
+                    counts.update(char)
+
+                else:  # nested substitution: simply match known variable names
+                    found = False
+                    for known_identifier in self.names:
+                        if expr[pos:].startswith(known_identifier):
+                            found = True
+                            break
+                    if found:
+                        out.append(spec.nested_start)
+                        out.append(known_identifier)
+                        out.append(spec.nested_end)
+                        pos += len(known_identifier)
+                        continue
+
+            elif delim_to_find and char in delim_to_find and all_delims_closed():  # end of substitution
+                out.append(spec.end)
+                if char in delims['']:
+                    out.append(char)
+                delim_to_find = False
+
+            elif delim_to_find and char in ')]}' and extra_close():  # end of substitution
+                out.append(spec.end)
+                out.append(char)
+                delim_to_find = False
+
+            else:
+                out.append(char)
+
+            pos += 1
+
+        if delim_to_find == delims['']:
+            out.append(spec.end)
+
+        out = ''.join(out)
+        # fix: eval stuff
+        out = re.sub(r'(?P<arg>' + r'|'.join(self.extended) + r')\(\)', '\g<arg>', out)
+
+        self.stats['hard'] += 1
+        return spec.type(out)
+
+    def convert_inline_conditional(self, expr, spec=Python):
+        if spec == FormatString:
+            raise ValueError('No conditionals in format strings: ' + expr)
+        matcher = r'\g<then> if \g<cond> else \g<else>'
+        if spec == Python:
+            matcher = '(' + matcher + ')'
+        expr = cheetah_inline_if.sub(matcher, expr)
+        return spec.type(self.convert_hard(expr, spec))
+
+
+class DummyConverter(object):
+
+    def __init__(self, names={}):
+        pass
+
+    def to_python(self, expr):
+        return expr
+
+    def to_format_string(self, expr):
+        return expr
+
+    def to_mako(self, expr):
+        return expr
diff --git a/grc/converter/flow_graph.dtd b/grc/converter/flow_graph.dtd
new file mode 100644
index 0000000000..bdfe1dc059
--- /dev/null
+++ b/grc/converter/flow_graph.dtd
@@ -0,0 +1,38 @@
+<!--
+Copyright 2008 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
+-->
+<!--
+    flow_graph.dtd
+    Josh Blum
+    The document type definition for flow graph xml files.
+ -->
+<!ELEMENT flow_graph (timestamp?, block*, connection*)> <!-- optional timestamp -->
+<!ELEMENT timestamp (#PCDATA)>
+<!-- Block -->
+<!ELEMENT block (key, param*, bus_sink?, bus_source?)>
+<!ELEMENT param (key, value)>
+<!ELEMENT key (#PCDATA)>
+<!ELEMENT value (#PCDATA)>
+<!ELEMENT bus_sink (#PCDATA)>
+<!ELEMENT bus_source (#PCDATA)>
+<!-- Connection -->
+<!ELEMENT connection (source_block_id, sink_block_id, source_key, sink_key)>
+<!ELEMENT source_block_id (#PCDATA)>
+<!ELEMENT sink_block_id (#PCDATA)>
+<!ELEMENT source_key (#PCDATA)>
+<!ELEMENT sink_key (#PCDATA)>
diff --git a/grc/converter/flow_graph.py b/grc/converter/flow_graph.py
new file mode 100644
index 0000000000..d20c67703c
--- /dev/null
+++ b/grc/converter/flow_graph.py
@@ -0,0 +1,131 @@
+# Copyright 2017 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 __future__ import absolute_import, division
+
+import ast
+from collections import OrderedDict
+
+from ..core.io import yaml
+from . import xml
+
+
+def from_xml(filename):
+    """Load flow graph from xml file"""
+    element, version_info = xml.load(filename, 'flow_graph.dtd')
+
+    data = convert_flow_graph_xml(element)
+    try:
+        file_format = int(version_info['format'])
+    except KeyError:
+        file_format = _guess_file_format_1(data)
+
+    data['metadata'] = {'file_format': file_format}
+
+    return data
+
+
+def dump(data, stream):
+    out = yaml.dump(data, indent=2)
+
+    replace = [
+        ('blocks:', '\nblocks:'),
+        ('connections:', '\nconnections:'),
+        ('metadata:', '\nmetadata:'),
+    ]
+    for r in replace:
+        out = out.replace(*r)
+    prefix = '# auto-generated by grc.converter\n\n'
+    stream.write(prefix + out)
+
+
+def convert_flow_graph_xml(node):
+    blocks = [
+        convert_block(block_data)
+        for block_data in node.findall('block')
+    ]
+
+    options = next(b for b in blocks if b['id'] == 'options')
+    blocks.remove(options)
+    options.pop('id')
+
+    connections = [
+        convert_connection(connection)
+        for connection in node.findall('connection')
+        ]
+
+    flow_graph = OrderedDict()
+    flow_graph['options'] = options
+    flow_graph['blocks'] = blocks
+    flow_graph['connections'] = connections
+    return flow_graph
+
+
+def convert_block(data):
+    block_id = data.findtext('key')
+
+    params = OrderedDict(sorted(
+        (param.findtext('key'), param.findtext('value'))
+        for param in data.findall('param')
+    ))
+    states = OrderedDict()
+    x, y = ast.literal_eval(params.pop('_coordinate', '(10, 10)'))
+    states['coordinate'] = yaml.ListFlowing([x, y])
+    states['rotation'] = int(params.pop('_rotation', '0'))
+    enabled = params.pop('_enabled', 'True')
+    states['state'] = (
+        'enabled' if enabled in ('1', 'True') else
+        'bypassed' if enabled == '2' else
+        'disabled'
+    )
+
+    block = OrderedDict()
+    if block_id != 'options':
+        block['name'] = params.pop('id')
+    block['id'] = block_id
+    block['parameters'] = params
+    block['states'] = states
+
+    return block
+
+
+def convert_connection(data):
+    src_blk_id = data.findtext('source_block_id')
+    src_port_id = data.findtext('source_key')
+    snk_blk_id = data.findtext('sink_block_id')
+    snk_port_id = data.findtext('sink_key')
+
+    if src_port_id.isdigit():
+        src_port_id = 'out' + src_port_id
+    if snk_port_id.isdigit():
+        snk_port_id = 'in' + snk_port_id
+
+    return yaml.ListFlowing([src_blk_id, src_port_id, snk_blk_id, snk_port_id])
+
+
+def _guess_file_format_1(data):
+    """Try to guess the file format for flow-graph files without version tag"""
+
+    def has_numeric_port_ids(src_id, src_port_id, snk_id, snk_port_id):
+        return src_port_id.isdigit() and snk_port_id.is_digit()
+
+    try:
+        if any(not has_numeric_port_ids(*con) for con in data['connections']):
+            return 1
+    except:
+        pass
+    return 0
diff --git a/grc/converter/main.py b/grc/converter/main.py
new file mode 100644
index 0000000000..f979cc0281
--- /dev/null
+++ b/grc/converter/main.py
@@ -0,0 +1,163 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from codecs import open
+import json
+import logging
+import os
+
+import six
+
+from . import block_tree, block
+
+path = os.path
+logger = logging.getLogger(__name__)
+
+excludes = [
+    'qtgui_',
+    '.grc_gnuradio/',
+    'blks2',
+    'wxgui',
+    'epy_block.xml',
+    'virtual_sink.xml',
+    'virtual_source.xml',
+    'dummy.xml',
+    'variable_struct.xml',  # todo: re-implement as class
+    'digital_constellation',  # todo: fix template
+]
+
+
+class Converter(object):
+
+    def __init__(self, search_path, output_dir='~/.cache/grc_gnuradio'):
+        self.search_path = search_path
+        self.output_dir = output_dir
+
+        self._force = False
+
+        converter_module_path = path.dirname(__file__)
+        self._converter_mtime = max(path.getmtime(path.join(converter_module_path, module))
+                                    for module in os.listdir(converter_module_path)
+                                    if not module.endswith('flow_graph.py'))
+
+        self.cache_file = os.path.join(self.output_dir, '_cache.json')
+        self.cache = {}
+
+    def run(self, force=False):
+        self._force = force
+
+        try:
+            with open(self.cache_file, encoding='utf-8') as cache_file:
+                self.cache = byteify(json.load(cache_file))
+        except (IOError, ValueError):
+            self.cache = {}
+            self._force = True
+        need_cache_write = False
+
+        if not path.isdir(self.output_dir):
+            os.makedirs(self.output_dir)
+        if self._force:
+            for name in os.listdir(self.output_dir):
+                os.remove(os.path.join(self.output_dir, name))
+
+        for xml_file in self.iter_files_in_block_path():
+            if xml_file.endswith("block_tree.xml"):
+                changed = self.load_category_tree_xml(xml_file)
+            elif xml_file.endswith('domain.xml'):
+                continue
+            else:
+                changed = self.load_block_xml(xml_file)
+
+            if changed:
+                need_cache_write = True
+
+        if need_cache_write:
+            logger.info('Saving %d entries to json cache', len(self.cache))
+            with open(self.cache_file, 'w', encoding='utf-8') as cache_file:
+                json.dump(self.cache, cache_file)
+
+    def load_block_xml(self, xml_file):
+        """Load block description from xml file"""
+        if any(part in xml_file for part in excludes):
+            return
+
+        block_id_from_xml = path.basename(xml_file)[:-4]
+        yml_file = path.join(self.output_dir, block_id_from_xml + '.block.yml')
+
+        if not self.needs_conversion(xml_file, yml_file):
+            return  # yml file up-to-date
+
+        logger.info('Converting block %s', path.basename(xml_file))
+        data = block.from_xml(xml_file)
+        if block_id_from_xml != data['id']:
+            logger.warning('block_id and filename differ')
+        self.cache[yml_file] = data
+
+        with open(yml_file, 'w', encoding='utf-8') as yml_file:
+            block.dump(data, yml_file)
+        return True
+
+    def load_category_tree_xml(self, xml_file):
+        """Validate and parse category tree file and add it to list"""
+        module_name = path.basename(xml_file)[:-len('block_tree.xml')].rstrip('._-')
+        yml_file = path.join(self.output_dir, module_name + '.tree.yml')
+
+        if not self.needs_conversion(xml_file, yml_file):
+            return  # yml file up-to-date
+
+        logger.info('Converting module %s', path.basename(xml_file))
+        data = block_tree.from_xml(xml_file)
+        self.cache[yml_file] = data
+
+        with open(yml_file, 'w', encoding='utf-8') as yml_file:
+            block_tree.dump(data, yml_file)
+        return True
+
+    def needs_conversion(self, source, destination):
+        """Check if source has already been converted and destination is up-to-date"""
+        if self._force or not path.exists(destination):
+            return True
+        xml_time = path.getmtime(source)
+        yml_time = path.getmtime(destination)
+
+        return yml_time < xml_time or yml_time < self._converter_mtime
+
+    def iter_files_in_block_path(self, suffix='.xml'):
+        """Iterator for block descriptions and category trees"""
+        for block_path in self.search_path:
+            if path.isfile(block_path):
+                yield block_path
+            elif path.isdir(block_path):
+                for root, _, files in os.walk(block_path, followlinks=True):
+                    for name in files:
+                        if name.endswith(suffix):
+                            yield path.join(root, name)
+            else:
+                logger.warning('Invalid entry in search path: {}'.format(block_path))
+
+
+def byteify(data):
+    if isinstance(data, dict):
+        return {byteify(key): byteify(value) for key, value in six.iteritems(data)}
+    elif isinstance(data, list):
+        return [byteify(element) for element in data]
+    elif isinstance(data, unicode):
+        return data.encode('utf-8')
+    else:
+        return data
diff --git a/grc/converter/xml.py b/grc/converter/xml.py
new file mode 100644
index 0000000000..2eda786c0f
--- /dev/null
+++ b/grc/converter/xml.py
@@ -0,0 +1,82 @@
+# Copyright 2017 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 __future__ import absolute_import, division
+
+import re
+from os import path
+
+try:
+    # raise ImportError()
+    from lxml import etree
+    HAVE_LXML = True
+except ImportError:
+    import xml.etree.ElementTree as etree
+    HAVE_LXML = False
+
+
+_validator_cache = {None: lambda xml: True}
+
+
+def _get_validator(dtd=None):
+    validator = _validator_cache.get(dtd)
+    if not validator:
+        if not path.isabs(dtd):
+            dtd = path.join(path.dirname(__file__), dtd)
+        validator = _validator_cache[dtd] = etree.DTD(dtd).validate
+    return validator
+
+
+def load_lxml(filename, document_type_def=None):
+    """Load block description from xml file"""
+
+    try:
+        xml_tree = etree.parse(filename)
+        _get_validator(document_type_def)
+        element = xml_tree.getroot()
+    except etree.LxmlError:
+        raise ValueError("Failed to parse or validate {}".format(filename))
+
+    version_info = {}
+    for inst in xml_tree.xpath('/processing-instruction()'):
+        if inst.target == 'grc':
+            version_info.update(inst.attrib)
+
+    return element, version_info
+
+
+def load_stdlib(filename, document_type_def=None):
+    """Load block description from xml file"""
+
+    with open(filename, 'rb') as xml_file:
+        data = xml_file.read().decode('utf-8')
+
+    try:
+        element = etree.fromstring(data)
+    except etree.ParseError:
+        raise ValueError("Failed to parse {}".format(filename))
+
+    version_info = {}
+    for body in re.findall(r'<\?(.*?)\?>', data):
+        element = etree.fromstring('<' + body + '/>')
+        if element.tag == 'grc':
+            version_info.update(element.attrib)
+
+    return element, version_info
+
+
+load = load_lxml if HAVE_LXML else load_stdlib
diff --git a/grc/core/Block.py b/grc/core/Block.py
deleted file mode 100644
index 087815b941..0000000000
--- a/grc/core/Block.py
+++ /dev/null
@@ -1,784 +0,0 @@
-"""
-Copyright 2008-2015 Free Software Foundation, Inc.
-This file is part of GNU Radio
-
-GNU Radio Companion is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-GNU Radio Companion is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-"""
-
-from __future__ import absolute_import
-
-import collections
-import itertools
-import ast
-
-import six
-from six.moves import map, range
-
-from Cheetah.Template import Template
-
-from . import utils
-
-from . Constants import (
-    BLOCK_FLAG_NEED_QT_GUI,
-    ADVANCED_PARAM_TAB,
-    BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS,
-    BLOCK_FLAG_DEPRECATED,
-)
-from . Element import Element, lazy_property
-
-
-def _get_elem(iterable, key):
-    items = list(iterable)
-    for item in items:
-        if item.key == key:
-            return item
-    return ValueError('Key "{}" not found in {}.'.format(key, items))
-
-
-class Block(Element):
-
-    is_block = True
-
-    STATE_LABELS = ['disabled', 'enabled', 'bypassed']
-
-    def __init__(self, parent, key, name, **n):
-        """Make a new block from nested data."""
-        super(Block, self).__init__(parent)
-
-        self.key = key
-        self.name = name
-        self.category = [cat.strip() for cat in n.get('category', '').split('/') if cat.strip()]
-        self.flags = n.get('flags', '')
-        self._doc = n.get('doc', '').strip('\n').replace('\\\n', '')
-
-        # Backwards compatibility
-        if n.get('throttle') and BLOCK_FLAG_THROTTLE not in self.flags:
-            self.flags += BLOCK_FLAG_THROTTLE
-
-        self._imports = [i.strip() for i in n.get('import', [])]
-        self._make = n.get('make')
-        self._var_make = n.get('var_make')
-        self._var_value = n.get('var_value', '$value')
-        self._checks = n.get('check', [])
-        self._callbacks = n.get('callback', [])
-
-        self._grc_source = n.get('grc_source', '')
-        self.block_wrapper_path = n.get('block_wrapper_path')
-
-        # 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.
-        self.is_virtual_or_pad = self.key in (
-            "virtual_source", "virtual_sink", "pad_source", "pad_sink")
-        self.is_variable = self.key.startswith('variable')
-        self.is_import = (self.key == 'import')
-
-        # Disable blocks that are virtual/pads or variables
-        if self.is_virtual_or_pad or self.is_variable:
-            self.flags += BLOCK_FLAG_DISABLE_BYPASS
-
-        params_n = n.get('param', [])
-        sources_n = n.get('source', [])
-        sinks_n = n.get('sink', [])
-
-        # Get list of param tabs
-        self.params = collections.OrderedDict()
-        self._init_params(
-            params_n=params_n,
-            has_sinks=len(sinks_n),
-            has_sources=len(sources_n)
-        )
-
-        self.sources = self._init_ports(sources_n, direction='source')
-        self.sinks = self._init_ports(sinks_n, direction='sink')
-        self.active_sources = []  # on rewrite
-        self.active_sinks = []  # on rewrite
-
-        self.states = {'_enabled': True}
-
-        self._init_bus_ports(n)
-
-    def _init_params(self, params_n, has_sources, has_sinks):
-        param_factory = self.parent_platform.get_new_param
-
-        def add_param(key, **kwargs):
-            self.params[key] = param_factory(self, key=key, **kwargs)
-
-        add_param(key='id', name='ID', type='id')
-
-        if not (self.is_virtual_or_pad or self.is_variable or self.key == 'options'):
-            add_param(key='alias', name='Block Alias', type='string',
-                      hide='part', tab=ADVANCED_PARAM_TAB)
-
-        if not self.is_virtual_or_pad and (has_sources or has_sinks):
-            add_param(key='affinity', name='Core Affinity', type='int_vector',
-                      hide='part', tab=ADVANCED_PARAM_TAB)
-
-        if not self.is_virtual_or_pad and has_sources:
-            add_param(key='minoutbuf', name='Min Output Buffer', type='int',
-                      hide='part', value='0', tab=ADVANCED_PARAM_TAB)
-            add_param(key='maxoutbuf', name='Max Output Buffer', type='int',
-                      hide='part', value='0', tab=ADVANCED_PARAM_TAB)
-
-        base_params_n = {}
-        for param_n in params_n:
-            key = param_n['key']
-            if key in self.params:
-                raise Exception('Key "{}" already exists in params'.format(key))
-
-            base_key = param_n.get('base_key', None)
-            param_n_ext = base_params_n.get(base_key, {}).copy()
-            param_n_ext.update(param_n)
-            self.params[key] = param_factory(self, **param_n_ext)
-            base_params_n[key] = param_n_ext
-
-        add_param(key='comment', name='Comment', type='_multiline', hide='part',
-                  value='', tab=ADVANCED_PARAM_TAB)
-
-    def _init_ports(self, ports_n, direction):
-        port_factory = self.parent_platform.get_new_port
-        ports = []
-        port_keys = set()
-        stream_port_keys = itertools.count()
-        for i, port_n in enumerate(ports_n):
-            port_n.setdefault('key', str(next(stream_port_keys)))
-            port = port_factory(parent=self, direction=direction, **port_n)
-            key = port.key
-            if key in port_keys:
-                raise Exception('Key "{}" already exists in {}'.format(key, direction))
-            port_keys.add(key)
-            ports.append(port)
-        return ports
-
-    ##############################################
-    # validation and rewrite
-    ##############################################
-    def rewrite(self):
-        """
-        Add and remove ports to adjust for the nports.
-        """
-        Element.rewrite(self)
-
-        def rekey(ports):
-            """Renumber non-message/message ports"""
-            domain_specific_port_index = collections.defaultdict(int)
-            for port in [p for p in ports if p.key.isdigit()]:
-                domain = port.domain
-                port.key = str(domain_specific_port_index[domain])
-                domain_specific_port_index[domain] += 1
-
-        # Adjust nports
-        for ports in (self.sources, self.sinks):
-            self._rewrite_nports(ports)
-            self.back_ofthe_bus(ports)
-            rekey(ports)
-
-        self._rewrite_bus_ports()
-
-        # disconnect hidden ports
-        for port in itertools.chain(self.sources, self.sinks):
-            if port.get_hide():
-                for connection in port.get_connections():
-                    self.parent_flowgraph.remove_element(connection)
-
-
-        self.active_sources = [p for p in self.get_sources_gui() if not p.get_hide()]
-        self.active_sinks = [p for p in self.get_sinks_gui() if not p.get_hide()]
-
-    def _rewrite_nports(self, ports):
-        for port in ports:
-            if port.is_clone:  # Not a master port and no left-over clones
-                continue
-            nports = port.get_nports() or 1
-            for clone in port.clones[nports-1:]:
-                # Remove excess connections
-                for connection in clone.get_connections():
-                    self.parent_flowgraph.remove_element(connection)
-                port.remove_clone(clone)
-                ports.remove(clone)
-            # Add more cloned ports
-            for j in range(1 + len(port.clones), nports):
-                clone = port.add_clone()
-                ports.insert(ports.index(port) + j, clone)
-
-    def validate(self):
-        """
-        Validate this block.
-        Call the base class validate.
-        Evaluate the checks: each check must evaluate to True.
-        """
-        Element.validate(self)
-        self._run_checks()
-        self._validate_generate_mode_compat()
-        self._validate_var_value()
-
-    def _run_checks(self):
-        """Evaluate the checks"""
-        for check in self._checks:
-            check_res = self.resolve_dependencies(check)
-            try:
-                if not self.parent.evaluate(check_res):
-                    self.add_error_message('Check "{}" failed.'.format(check))
-            except:
-                self.add_error_message('Check "{}" did not evaluate.'.format(check))
-
-    def _validate_generate_mode_compat(self):
-        """check if this is a GUI block and matches the selected generate option"""
-        current_generate_option = self.parent.get_option('generate_options')
-
-        def check_generate_mode(label, flag, valid_options):
-            block_requires_mode = (
-                flag in self.flags or self.name.upper().startswith(label)
-            )
-            if block_requires_mode and current_generate_option not in valid_options:
-                self.add_error_message("Can't generate this block in mode: {} ".format(
-                                       repr(current_generate_option)))
-
-        check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
-
-    def _validate_var_value(self):
-        """or variables check the value (only if var_value is used)"""
-        if self.is_variable and self._var_value != '$value':
-            value = self._var_value
-            try:
-                value = self.get_var_value()
-                self.parent.evaluate(value)
-            except Exception as err:
-                self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err))
-
-    ##############################################
-    # props
-    ##############################################
-
-    @lazy_property
-    def is_throtteling(self):
-        return BLOCK_FLAG_THROTTLE in self.flags
-
-    @lazy_property
-    def is_deprecated(self):
-        return BLOCK_FLAG_DEPRECATED in self.flags
-
-    @property
-    def documentation(self):
-        documentation = self.parent_platform.block_docstrings.get(self.key, {})
-        from_xml = self._doc.strip()
-        if from_xml:
-            documentation[''] = from_xml
-        return documentation
-
-    @property
-    def comment(self):
-        return self.params['comment'].get_value()
-
-    @property
-    def state(self):
-        """Gets the block's current state."""
-        try:
-            return self.STATE_LABELS[int(self.states['_enabled'])]
-        except ValueError:
-            return 'enabled'
-
-    @state.setter
-    def state(self, value):
-        """Sets the state for the block."""
-        try:
-            encoded = self.STATE_LABELS.index(value)
-        except ValueError:
-            encoded = 1
-        self.states['_enabled'] = encoded
-
-    # Enable/Disable Aliases
-    @property
-    def enabled(self):
-        """Get the enabled state of the block"""
-        return self.state != 'disabled'
-
-    ##############################################
-    # Getters (old)
-    ##############################################
-
-    def get_imports(self, raw=False):
-        """
-        Resolve all import statements.
-        Split each import statement at newlines.
-        Combine all import statements into a list.
-        Filter empty imports.
-
-        Returns:
-            a list of import statements
-        """
-        if raw:
-            return self._imports
-        return [i for i in sum((self.resolve_dependencies(i).split('\n')
-                                for i in self._imports), []) if i]
-
-    def get_make(self, raw=False):
-        if raw:
-            return self._make
-        return self.resolve_dependencies(self._make)
-
-    def get_var_make(self):
-        return self.resolve_dependencies(self._var_make)
-
-    def get_var_value(self):
-        return self.resolve_dependencies(self._var_value)
-
-    def get_callbacks(self):
-        """
-        Get a list of function callbacks for this block.
-
-        Returns:
-            a list of strings
-        """
-        def make_callback(callback):
-            callback = self.resolve_dependencies(callback)
-            if 'self.' in callback:
-                return callback
-            return 'self.{}.{}'.format(self.get_id(), callback)
-        return [make_callback(c) for c in self._callbacks]
-
-    def is_virtual_sink(self):
-        return self.key == 'virtual_sink'
-
-    def is_virtual_source(self):
-        return self.key == 'virtual_source'
-
-    # Block bypassing
-    def get_bypassed(self):
-        """
-        Check if the block is bypassed
-        """
-        return self.state == 'bypassed'
-
-    def set_bypassed(self):
-        """
-        Bypass the block
-
-        Returns:
-            True if block chagnes state
-        """
-        if self.state != 'bypassed' and self.can_bypass():
-            self.state = '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.sources) != 1 or len(self.sinks) != 1:
-            return False
-        if not (self.sources[0].get_type() == self.sinks[0].get_type()):
-            return False
-        if BLOCK_FLAG_DISABLE_BYPASS in self.flags:
-            return False
-        return True
-
-    def __str__(self):
-        return 'Block - {} - {}({})'.format(self.get_id(), self.name, self.key)
-
-    def get_id(self):
-        return self.params['id'].get_value()
-
-    def get_ports(self):
-        return self.sources + self.sinks
-
-    def get_ports_gui(self):
-        return self.get_sources_gui() + self.get_sinks_gui()
-
-    def active_ports(self):
-        return itertools.chain(self.active_sources, self.active_sinks)
-
-    def get_children(self):
-        return self.get_ports() + list(self.params.values())
-
-    def get_children_gui(self):
-        return self.get_ports_gui() + self.params.values()
-
-    ##############################################
-    # Access
-    ##############################################
-
-    def get_param(self, key):
-        return self.params[key]
-
-    def get_sink(self, key):
-        return _get_elem(self.sinks, key)
-
-    def get_sinks_gui(self):
-        return self.filter_bus_port(self.sinks)
-
-    def get_source(self, key):
-        return _get_elem(self.sources, key)
-
-    def get_sources_gui(self):
-        return self.filter_bus_port(self.sources)
-
-    def get_connections(self):
-        return sum((port.get_connections() for port in self.get_ports()), [])
-
-    ##############################################
-    # Resolve
-    ##############################################
-    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
-        # TODO: cache that
-        n = {key: param.template_arg for key, param in six.iteritems(self.params)}
-        try:
-            return str(Template(tmpl, n))
-        except Exception as err:
-            return "Template error: {}\n    {}".format(tmpl, err)
-
-    ##############################################
-    # Import/Export Methods
-    ##############################################
-    def export_data(self):
-        """
-        Export this block's params to nested data.
-
-        Returns:
-            a nested data odict
-        """
-        n = collections.OrderedDict()
-        n['key'] = self.key
-
-        params = (param.export_data() for param in six.itervalues(self.params))
-        states = (collections.OrderedDict([('key', key), ('value', repr(value))])
-                  for key, value in six.iteritems(self.states))
-        n['param'] = sorted(itertools.chain(states, params), key=lambda p: p['key'])
-
-        if any('bus' in a.get_type() for a in self.sinks):
-            n['bus_sink'] = '1'
-        if any('bus' in a.get_type() for a in self.sources):
-            n['bus_source'] = '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
-        """
-        param_data = {p['key']: p['value'] for p in n.get('param', [])}
-
-        for key in self.states:
-            try:
-                self.states[key] = ast.literal_eval(param_data.pop(key))
-            except (KeyError, SyntaxError, ValueError):
-                pass
-
-        def get_hash():
-            return hash(tuple(hash(v) for v in self.params.values()))
-
-        pre_rewrite_hash = -1
-        while pre_rewrite_hash != get_hash():
-            for key, value in six.iteritems(param_data):
-                try:
-                    self.params[key].set_value(value)
-                except KeyError:
-                    continue
-            # Store hash and call rewrite
-            pre_rewrite_hash = get_hash()
-            self.rewrite()
-
-        self._import_bus_stuff(n)
-
-    ##############################################
-    # Bus ports stuff
-    ##############################################
-
-    def get_bus_structure(self, direction):
-        bus_structure = self.resolve_dependencies(self._bus_structure[direction])
-        if not bus_structure:
-            return
-        try:
-            return self.parent_flowgraph.evaluate(bus_structure)
-        except:
-            return
-
-    @staticmethod
-    def back_ofthe_bus(portlist):
-        portlist.sort(key=lambda p: p._type == 'bus')
-
-    @staticmethod
-    def filter_bus_port(ports):
-        buslist = [p for p in ports if p._type == 'bus']
-        return buslist or ports
-
-    def _import_bus_stuff(self, n):
-        bus_sinks = n.get('bus_sink', [])
-        if len(bus_sinks) > 0 and not self._bussify_sink:
-            self.bussify('sink')
-        elif len(bus_sinks) > 0:
-            self.bussify('sink')
-            self.bussify('sink')
-        bus_sources = n.get('bus_source', [])
-        if len(bus_sources) > 0 and not self._bussify_source:
-            self.bussify('source')
-        elif len(bus_sources) > 0:
-            self.bussify('source')
-            self.bussify('source')
-
-    def form_bus_structure(self, direc):
-        ports = self.sources if direc == 'source' else self.sinks
-        struct = self.get_bus_structure(direc)
-
-        if not struct:
-            struct = [list(range(len(ports)))]
-
-        elif any(isinstance(p.get_nports(), int) for p in ports):
-            last = 0
-            structlet = []
-            for port in ports:
-                nports = port.get_nports()
-                if not isinstance(nports, int):
-                    continue
-                structlet.extend(a + last for a in range(nports))
-                last += nports
-            struct = [structlet]
-
-        self.current_bus_structure[direc] = struct
-        return struct
-
-    def bussify(self, direc):
-        ports = self.sources if direc == 'source' else self.sinks
-
-        for elt in ports:
-            for connect in elt.get_connections():
-                self.parent.remove_element(connect)
-
-        if ports and all('bus' != p.get_type() for p in ports):
-            struct = self.current_bus_structure[direc] = self.form_bus_structure(direc)
-            n = {'type': 'bus'}
-            if ports[0].get_nports():
-                n['nports'] = '1'
-
-            for i, structlet in enumerate(struct):
-                name = 'bus{}#{}'.format(i, len(structlet))
-                port = self.parent_platform.get_new_port(
-                    self, direction=direc, key=str(len(ports)), name=name, **n)
-                ports.append(port)
-        elif any('bus' == p.get_type() for p in ports):
-            get_p_gui = self.get_sources_gui if direc == 'source' else self.get_sinks_gui
-            for elt in get_p_gui():
-                ports.remove(elt)
-            self.current_bus_structure[direc] = ''
-
-    def _init_bus_ports(self, n):
-        self.current_bus_structure = {'source': '', 'sink': ''}
-        self._bus_structure = {'source': n.get('bus_structure_source', ''),
-                               'sink': n.get('bus_structure_sink', '')}
-        self._bussify_sink = n.get('bus_sink')
-        self._bussify_source = n.get('bus_source')
-        if self._bussify_sink:
-            self.bussify('sink')
-        if self._bussify_source:
-            self.bussify('source')
-
-    def _rewrite_bus_ports(self):
-        return  # fixme: probably broken
-
-        def doit(ports, ports_gui, direc):
-            if not self.current_bus_structure[direc]:
-                return
-
-            bus_structure = self.form_bus_structure(direc)
-            for port in ports_gui[len(bus_structure):]:
-                for connect in port.get_connections():
-                    self.parent_flowgraph.remove_element(connect)
-                ports.remove(port)
-
-            port_factory = self.parent_platform.get_new_port
-
-            if len(ports_gui) < len(bus_structure):
-                for i in range(len(ports_gui), len(bus_structure)):
-                    port = port_factory(self, direction=direc, key=str(1 + i),
-                                        name='bus', type='bus')
-                    ports.append(port)
-
-        doit(self.sources, self.get_sources_gui(), 'source')
-        doit(self.sinks, self.get_sinks_gui(), 'sink')
-
-        if 'bus' in [a.get_type() for a in self.get_sources_gui()]:
-            for i in range(len(self.get_sources_gui())):
-                if not self.get_sources_gui()[i].get_connections():
-                    continue
-                source = self.get_sources_gui()[i]
-                sink = []
-
-                for j in range(len(source.get_connections())):
-                    sink.append(source.get_connections()[j].sink_port)
-                for elt in source.get_connections():
-                    self.parent_flowgraph.remove_element(elt)
-                for j in sink:
-                    self.parent_flowgraph.connect(source, j)
-
-
-class EPyBlock(Block):
-
-    def __init__(self, flow_graph, **n):
-        super(EPyBlock, self).__init__(flow_graph, **n)
-        self._epy_source_hash = -1  # for epy blocks
-        self._epy_reload_error = None
-
-    def rewrite(self):
-        Element.rewrite(self)
-
-        param_blk = self.params['_io_cache']
-        param_src = self.params['_source_code']
-
-        src = param_src.get_value()
-        src_hash = hash((self.get_id(), src))
-        if src_hash == self._epy_source_hash:
-            return
-
-        try:
-            blk_io = utils.epy_block_io.extract(src)
-
-        except Exception as e:
-            self._epy_reload_error = ValueError(str(e))
-            try:  # Load last working block io
-                blk_io_args = eval(param_blk.get_value())
-                if len(blk_io_args) == 6:
-                    blk_io_args += ([],)  # add empty callbacks
-                blk_io = utils.epy_block_io.BlockIO(*blk_io_args)
-            except Exception:
-                return
-        else:
-            self._epy_reload_error = None  # Clear previous errors
-            param_blk.set_value(repr(tuple(blk_io)))
-
-        # print "Rewriting embedded python block {!r}".format(self.get_id())
-
-        self._epy_source_hash = src_hash
-        self.name = blk_io.name or blk_io.cls
-        self._doc = blk_io.doc
-        self._imports[0] = 'import ' + self.get_id()
-        self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join(
-            '{0}=${{ {0} }}'.format(key) for key, _ in blk_io.params))
-        self._callbacks = ['{0} = ${{ {0} }}'.format(attr) for attr in blk_io.callbacks]
-        self._update_params(blk_io.params)
-        self._update_ports('in', self.sinks, blk_io.sinks, 'sink')
-        self._update_ports('out', self.sources, blk_io.sources, 'source')
-
-        super(EPyBlock, self).rewrite()
-
-    def _update_params(self, params_in_src):
-        param_factory = self.parent_platform.get_new_param
-        params = {}
-        for param in list(self.params):
-            if hasattr(param, '__epy_param__'):
-                params[param.key] = param
-                del self.params[param.key]
-
-        for key, value in params_in_src:
-            try:
-                param = params[key]
-                if param.default == param.value:
-                    param.set_value(value)
-                param.default = str(value)
-            except KeyError:  # need to make a new param
-                param = param_factory(
-                    parent=self,  key=key, type='raw', value=value,
-                    name=key.replace('_', ' ').title(),
-                )
-                setattr(param, '__epy_param__', True)
-            self.params[key] = param
-
-    def _update_ports(self, label, ports, port_specs, direction):
-        port_factory = self.parent_platform.get_new_port
-        ports_to_remove = list(ports)
-        iter_ports = iter(ports)
-        ports_new = []
-        port_current = next(iter_ports, None)
-        for key, port_type, vlen in port_specs:
-            reuse_port = (
-                port_current is not None and
-                port_current.get_type() == port_type and
-                port_current.get_vlen() == vlen and
-                (key.isdigit() or port_current.key == key)
-            )
-            if reuse_port:
-                ports_to_remove.remove(port_current)
-                port, port_current = port_current, next(iter_ports, None)
-            else:
-                n = dict(name=label + str(key), type=port_type, key=key)
-                if port_type == 'message':
-                    n['name'] = key
-                    n['optional'] = '1'
-                if vlen > 1:
-                    n['vlen'] = str(vlen)
-                port = port_factory(self, direction=direction, **n)
-            ports_new.append(port)
-        # replace old port list with new one
-        del ports[:]
-        ports.extend(ports_new)
-        # remove excess port connections
-        for port in ports_to_remove:
-            for connection in port.get_connections():
-                self.parent_flowgraph.remove_element(connection)
-
-    def validate(self):
-        super(EPyBlock, self).validate()
-        if self._epy_reload_error:
-            self.params['_source_code'].add_error_message(str(self._epy_reload_error))
-
-
-class DummyBlock(Block):
-
-    is_dummy_block = True
-    build_in_param_keys = 'id alias affinity minoutbuf maxoutbuf comment'
-
-    def __init__(self, parent, key, missing_key, params_n):
-        super(DummyBlock, self).__init__(parent=parent, key=missing_key, name='Missing Block')
-        param_factory = self.parent_platform.get_new_param
-        for param_n in params_n:
-            key = param_n['key']
-            self.params.setdefault(key, param_factory(self, key=key, name=key, type='string'))
-
-    def is_valid(self):
-        return False
-
-    @property
-    def enabled(self):
-        return False
-
-    def add_missing_port(self, key, dir):
-        port = self.parent_platform.get_new_port(
-            parent=self, direction=dir, key=key, name='?', type='',
-        )
-        if port.is_source:
-            self.sources.append(port)
-        else:
-            self.sinks.append(port)
-        return port
diff --git a/grc/core/Config.py b/grc/core/Config.py
index cc199a348f..eb53e1751d 100644
--- a/grc/core/Config.py
+++ b/grc/core/Config.py
@@ -1,5 +1,4 @@
-"""
-Copyright 2016 Free Software Foundation, Inc.
+"""Copyright 2016 Free Software Foundation, Inc.
 This file is part of GNU Radio
 
 GNU Radio Companion is free software; you can redistribute it and/or
@@ -32,6 +31,8 @@ class Config(object):
 
     hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR)
 
+    yml_block_cache = os.path.expanduser('~/.cache/grc_gnuradio')  # FIXME: remove this as soon as converter is stable
+
     def __init__(self, version, version_parts=None, name=None, prefs=None):
         self._gr_prefs = prefs if prefs else DummyPrefs()
         self.version = version
@@ -39,6 +40,9 @@ class Config(object):
         if name:
             self.name = name
 
+        if not os.path.exists(self.yml_block_cache):
+            os.mkdir(self.yml_block_cache)
+
     @property
     def block_paths(self):
         path_list_sep = {'/': ':', '\\': ';'}[os.path.sep]
@@ -46,6 +50,7 @@ class Config(object):
         paths_sources = (
             self.hier_block_lib_dir,
             os.environ.get('GRC_BLOCKS_PATH', ''),
+            self.yml_block_cache,
             self._gr_prefs.get_string('grc', 'local_blocks_path', ''),
             self._gr_prefs.get_string('grc', 'global_blocks_path', ''),
         )
diff --git a/grc/core/Connection.py b/grc/core/Connection.py
index 066532149b..01baaaf8fc 100644
--- a/grc/core/Connection.py
+++ b/grc/core/Connection.py
@@ -19,26 +19,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import
 
-import collections
-
-from six.moves import range
-
-from . import Constants
-from .Element import Element, lazy_property
+from .base import Element
+from .utils.descriptors import lazy_property
 
 
 class Connection(Element):
 
     is_connection = True
 
-    def __init__(self, parent, porta, portb):
+    def __init__(self, parent, source, sink):
         """
         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)
+            source: a port (any direction)
+            sink: a port (any direction)
         @throws Error cannot make connection
 
         Returns:
@@ -46,37 +42,31 @@ class Connection(Element):
         """
         Element.__init__(self, parent)
 
-        source, sink = self._get_sink_source(porta, portb)
+        if not source.is_source:
+            source, sink = sink, source
+        if not source.is_source:
+            raise ValueError('Connection could not isolate source')
+        if not sink.is_sink:
+            raise ValueError('Connection could not isolate sink')
 
         self.source_port = source
         self.sink_port = sink
 
-        # Ensure that this connection (source -> sink) is unique
-        if self in self.parent_flowgraph.connections:
-            raise LookupError('This connection between source and sink is not unique.')
-
-        if self.is_bus():
-            self._make_bus_connect()
+    def __str__(self):
+        return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format(
+            self.source_block, self.source_port, self.sink_block, self.sink_port,
+        )
 
     def __eq__(self, other):
         if not isinstance(other, self.__class__):
             return NotImplemented
         return self.source_port == other.source_port and self.sink_port == other.sink_port
 
-    @staticmethod
-    def _get_sink_source(porta, portb):
-        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')
-        return source, sink
+    def __hash__(self):
+        return hash((self.source_port, self.sink_port))
+
+    def __iter__(self):
+        return iter((self.source_port, self.sink_port))
 
     @lazy_property
     def source_block(self):
@@ -86,6 +76,10 @@ class Connection(Element):
     def sink_block(self):
         return self.sink_port.parent_block
 
+    @lazy_property
+    def type(self):
+        return self.source_port.domain, self.sink_port.domain
+
     @property
     def enabled(self):
         """
@@ -96,14 +90,6 @@ class Connection(Element):
         """
         return self.source_block.enabled and self.sink_block.enabled
 
-    def __str__(self):
-        return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format(
-            self.source_block, self.source_port, self.sink_block, self.sink_port,
-        )
-
-    def is_bus(self):
-        return self.source_port.get_type() == 'bus'
-
     def validate(self):
         """
         Validate the connections.
@@ -112,29 +98,12 @@ class Connection(Element):
         Element.validate(self)
         platform = self.parent_platform
 
-        source_domain = self.source_port.domain
-        sink_domain = self.sink_port.domain
+        if self.type not in platform.connection_templates:
+            self.add_error_message('No connection known between domains "{}" and "{}"'
+                                   ''.format(*self.type))
 
-        if (source_domain, sink_domain) not in platform.connection_templates:
-            self.add_error_message('No connection known for domains "{}", "{}"'.format(
-                source_domain, sink_domain))
-        too_many_other_sinks = (
-            not platform.domains.get(source_domain, []).get('multiple_sinks', False) and
-            len(self.source_port.get_enabled_connections()) > 1
-        )
-        too_many_other_sources = (
-            not platform.domains.get(sink_domain, []).get('multiple_sources', False) and
-            len(self.sink_port.get_enabled_connections()) > 1
-        )
-        if too_many_other_sinks:
-            self.add_error_message(
-                'Domain "{}" can have only one downstream block'.format(source_domain))
-        if too_many_other_sources:
-            self.add_error_message(
-                'Domain "{}" can have only one upstream block'.format(sink_domain))
-
-        source_size = Constants.TYPE_TO_SIZEOF[self.source_port.get_type()] * self.source_port.get_vlen()
-        sink_size = Constants.TYPE_TO_SIZEOF[self.sink_port.get_type()] * self.sink_port.get_vlen()
+        source_size = self.source_port.item_size
+        sink_size = self.sink_port.item_size
         if source_size != sink_size:
             self.add_error_message('Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size))
 
@@ -148,23 +117,7 @@ class Connection(Element):
         Returns:
             a nested data odict
         """
-        n = collections.OrderedDict()
-        n['source_block_id'] = self.source_block.get_id()
-        n['sink_block_id'] = self.sink_block.get_id()
-        n['source_key'] = self.source_port.key
-        n['sink_key'] = self.sink_port.key
-        return n
-
-    def _make_bus_connect(self):
-        source, sink = self.source_port, self.sink_port
-
-        if source.get_type() == sink.get_type() == 'bus':
-            raise ValueError('busses must get with busses')
-
-        sources = source.get_associated_ports()
-        sinks = sink.get_associated_ports()
-        if len(sources) != len(sinks):
-            raise ValueError('port connections must have same cardinality')
-
-        for ports in zip(sources, sinks):
-            self.parent_flowgraph.connect(*ports)
+        return (
+            self.source_block.name, self.source_port.key,
+            self.sink_block.name, self.sink_port.key
+        )
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index caf301be60..fc5383378c 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -23,17 +23,15 @@ import os
 import stat
 
 import numpy
-import six
+
 
 # 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')
 BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd')
 DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc')
 DEFAULT_HIER_BLOCK_LIB_DIR = os.path.expanduser('~/.grc_gnuradio')
-DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd')
 
+BLOCK_DESCRIPTION_FILE_FORMAT_VERSION = 1
 # File format versions:
 #  0: undefined / legacy
 #  1: non-numeric message port keys (label is used instead)
@@ -45,15 +43,10 @@ ADVANCED_PARAM_TAB = "Advanced"
 DEFAULT_BLOCK_MODULE_NAME = '(no module specified)'
 
 # Port domains
-GR_STREAM_DOMAIN = "gr_stream"
-GR_MESSAGE_DOMAIN = "gr_message"
+GR_STREAM_DOMAIN = "stream"
+GR_MESSAGE_DOMAIN = "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_DEPRECATED = 'deprecated'
-
 # File creation modes
 TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | \
                       stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
diff --git a/grc/core/Element.py b/grc/core/Element.py
deleted file mode 100644
index 86e0746655..0000000000
--- a/grc/core/Element.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# Copyright 2008, 2009, 2015, 2016 Free Software Foundation, Inc.
-# This file is part of GNU Radio
-#
-# GNU Radio Companion is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# GNU Radio Companion is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-
-import weakref
-import functools
-
-
-class lazy_property(object):
-
-    def __init__(self, func):
-        self.func = func
-        functools.update_wrapper(self, func)
-
-    def __get__(self, instance, cls):
-        if instance is None:
-            return self
-        value = self.func(instance)
-        setattr(instance, self.func.__name__, value)
-        return value
-
-
-def nop_write(prop):
-    """Make this a property with a nop setter"""
-    def nop(self, value):
-        pass
-    return prop.setter(nop)
-
-
-class Element(object):
-
-    def __init__(self, parent=None):
-        self._parent = weakref.ref(parent) if parent else lambda: None
-        self._error_messages = []
-
-    ##################################################
-    # Element Validation API
-    ##################################################
-    def validate(self):
-        """
-        Validate this element and call validate on all children.
-        Call this base method before adding error messages in the subclass.
-        """
-        del self._error_messages[:]
-
-        for child in self.get_children():
-            child.validate()
-
-    def is_valid(self):
-        """
-        Is this element valid?
-
-        Returns:
-            true when the element is enabled and has no error messages or is bypassed
-        """
-        if not self.enabled or self.get_bypassed():
-            return True
-        return not next(self.iter_error_messages(), False)
-
-    def add_error_message(self, msg):
-        """
-        Add an error message to the list of errors.
-
-        Args:
-            msg: the error message string
-        """
-        self._error_messages.append(msg)
-
-    def get_error_messages(self):
-        """
-        Get the list of error messages from this element and all of its children.
-        Do not include the error messages from disabled or bypassed children.
-        Cleverly indent the children error messages for printing purposes.
-
-        Returns:
-            a list of error message strings
-        """
-        return [msg if elem is self else "{}:\n\t{}".format(elem, msg.replace("\n", "\n\t"))
-                for elem, msg in self.iter_error_messages()]
-
-    def iter_error_messages(self):
-        """
-        Iterate over error messages. Yields tuples of (element, message)
-        """
-        for msg in self._error_messages:
-            yield self, msg
-        for child in self.get_children():
-            if not child.enabled or child.get_bypassed():
-                continue
-            for element_msg in child.iter_error_messages():
-                yield element_msg
-
-    def rewrite(self):
-        """
-        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()
-
-    @property
-    def enabled(self):
-        return True
-
-    def get_bypassed(self):
-        return False
-
-    ##############################################
-    # Tree-like API
-    ##############################################
-    @property
-    def parent(self):
-        return self._parent()
-
-    def get_parent_by_type(self, cls):
-        parent = self.parent
-        if parent is None:
-            return None
-        elif isinstance(parent, cls):
-            return parent
-        else:
-            return parent.get_parent_by_type(cls)
-
-    @lazy_property
-    def parent_platform(self):
-        from .Platform import Platform
-        return self.get_parent_by_type(Platform)
-
-    @lazy_property
-    def parent_flowgraph(self):
-        from .FlowGraph import FlowGraph
-        return self.get_parent_by_type(FlowGraph)
-
-    @lazy_property
-    def parent_block(self):
-        from .Block import Block
-        return self.get_parent_by_type(Block)
-
-    def reset_parents_by_type(self):
-        """Reset all lazy properties"""
-        for name, obj in vars(Element):  # explicitly only in Element, not subclasses
-            if isinstance(obj, lazy_property):
-                delattr(self, name)
-
-    def get_children(self):
-        return list()
-
-    ##############################################
-    # Type testing
-    ##############################################
-    is_platform = False
-
-    is_flow_graph = False
-
-    is_block = False
-
-    is_dummy_block = False
-
-    is_connection = False
-
-    is_port = False
-
-    is_param = False
-
-    is_variable = False
-
-    is_import = False
diff --git a/grc/core/Element.pyi b/grc/core/Element.pyi
deleted file mode 100644
index 2a2aed401c..0000000000
--- a/grc/core/Element.pyi
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2016 Free Software Foundation, Inc.
-# This file is part of GNU Radio
-#
-# GNU Radio Companion is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# GNU Radio Companion is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-
-from typing import Union
-
-from . import Platform, FlowGraph, Block
-
-lazy_property = property  # fixme: descriptors don't seems to be supported
-
-
-class Element(object):
-
-    def __init__(self, parent: Union[None, 'Element'] = None): ...
-
-    @lazy_property
-    def parent(self) -> 'Element': ...
-
-    def get_parent_by_type(self, cls) -> Union[None, 'Element']: ...
-
-    @lazy_property
-    def parent_platform(self) -> Platform.Platform: ...
-
-    @lazy_property
-    def parent_flowgraph(self) -> FlowGraph.FlowGraph: ...
-
-    @lazy_property
-    def parent_block(self) -> Block.Block: ...
diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py
index bf5bf6d93e..3ad95eb207 100644
--- a/grc/core/FlowGraph.py
+++ b/grc/core/FlowGraph.py
@@ -17,24 +17,17 @@
 
 from __future__ import absolute_import, print_function
 
-import imp
-import time
-import re
-from operator import methodcaller
 import collections
+import imp
+import itertools
 import sys
+from operator import methodcaller, attrgetter
 
-from . import Messages
+from . import Messages, blocks
 from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
-from .Element import Element
-from .utils import expr_utils, shlex
-
-_parameter_matcher = re.compile('^(parameter)$')
-_monitors_searcher = re.compile('(ctrlport_monitor)')
-_bussink_searcher = re.compile('^(bus_sink)$')
-_bussrc_searcher = re.compile('^(bus_source)$')
-_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$')
-_bus_struct_src_searcher = re.compile('^(bus_structure_source)$')
+from .base import Element
+from .utils import expr_utils
+from .utils.backports import shlex
 
 
 class FlowGraph(Element):
@@ -52,11 +45,10 @@ class FlowGraph(Element):
             the flow graph object
         """
         Element.__init__(self, parent)
-        self._timestamp = time.ctime()
-        self._options_block = self.parent_platform.get_new_block(self, 'options')
+        self._options_block = self.parent_platform.make_block(self, 'options')
 
         self.blocks = [self._options_block]
-        self.connections = []
+        self.connections = set()
 
         self._eval_cache = {}
         self.namespace = {}
@@ -66,15 +58,14 @@ class FlowGraph(Element):
     def __str__(self):
         return 'FlowGraph - {}({})'.format(self.get_option('title'), self.get_option('id'))
 
-    def get_imports(self):
+    def imports(self):
         """
         Get a set of all import statements in this flow graph namespace.
 
         Returns:
-            a set of import statements
+            a list of import statements
         """
-        imports = sum([block.get_imports() for block in self.iter_enabled_blocks()], [])
-        return sorted(set(imports))
+        return [block.templates.render('imports') for block in self.iter_enabled_blocks()]
 
     def get_variables(self):
         """
@@ -85,7 +76,7 @@ class FlowGraph(Element):
             a sorted list of variable blocks in order of dependency (indep -> dep)
         """
         variables = [block for block in self.iter_enabled_blocks() if block.is_variable]
-        return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make'))
+        return expr_utils.sort_objects(variables, attrgetter('name'), methodcaller('get_var_make'))
 
     def get_parameters(self):
         """
@@ -94,47 +85,21 @@ class FlowGraph(Element):
         Returns:
             a list of parameterized variables
         """
-        parameters = [b for b in self.iter_enabled_blocks() if _parameter_matcher.match(b.key)]
+        parameters = [b for b in self.iter_enabled_blocks() if b.key == 'parameter']
         return parameters
 
     def get_monitors(self):
         """
         Get a list of all ControlPort monitors
         """
-        monitors = [b for b in self.iter_enabled_blocks() if _monitors_searcher.search(b.key)]
+        monitors = [b for b in self.iter_enabled_blocks() if 'ctrlport_monitor' in b.key]
         return monitors
 
     def get_python_modules(self):
         """Iterate over custom code block ID and Source"""
         for block in self.iter_enabled_blocks():
             if block.key == 'epy_module':
-                yield block.get_id(), block.get_param('source_code').get_value()
-
-    def get_bussink(self):
-        bussink = [b for b in self.get_enabled_blocks() if _bussink_searcher.search(b.key)]
-
-        for i in bussink:
-            for j in i.params.values():
-                if j.name == 'On/Off' and j.get_value() == 'on':
-                    return True
-        return False
-
-    def get_bussrc(self):
-        bussrc = [b for b in self.get_enabled_blocks() if _bussrc_searcher.search(b.key)]
-
-        for i in bussrc:
-            for j in i.params.values():
-                if j.name == 'On/Off' and j.get_value() == 'on':
-                    return True
-        return False
-
-    def get_bus_structure_sink(self):
-        bussink = [b for b in self.get_enabled_blocks() if _bus_struct_sink_searcher.search(b.key)]
-        return bussink
-
-    def get_bus_structure_src(self):
-        bussrc = [b for b in self.get_enabled_blocks() if _bus_struct_src_searcher.search(b.key)]
-        return bussrc
+                yield block.name, block.params[1].get_value()
 
     def iter_enabled_blocks(self):
         """
@@ -180,7 +145,7 @@ class FlowGraph(Element):
         Returns:
             the value held by that param
         """
-        return self._options_block.get_param(key).get_evaluated()
+        return self._options_block.params[key].get_evaluated()
 
     def get_run_command(self, file_path, split=False):
         run_command = self.get_option('run_command')
@@ -195,16 +160,19 @@ class FlowGraph(Element):
     ##############################################
     # Access Elements
     ##############################################
-    def get_block(self, id):
+    def get_block(self, name):
         for block in self.blocks:
-            if block.get_id() == id:
+            if block.name == name:
                 return block
-        raise KeyError('No block with ID {!r}'.format(id))
+        raise KeyError('No block with name {!r}'.format(name))
 
     def get_elements(self):
-        return self.blocks + self.connections
+        elements = list(self.blocks)
+        elements.extend(self.connections)
+        return elements
 
-    get_children = get_elements
+    def children(self):
+        return itertools.chain(self.blocks, self.connections)
 
     def rewrite(self):
         """
@@ -216,7 +184,7 @@ class FlowGraph(Element):
     def renew_namespace(self):
         namespace = {}
         # Load imports
-        for expr in self.get_imports():
+        for expr in self.imports():
             try:
                 exec(expr, namespace)
             except:
@@ -232,19 +200,19 @@ class FlowGraph(Element):
 
         # Load parameters
         np = {}  # params don't know each other
-        for parameter in self.get_parameters():
+        for parameter_block in self.get_parameters():
             try:
-                value = eval(parameter.get_param('value').to_code(), namespace)
-                np[parameter.get_id()] = value
+                value = eval(parameter_block.params['value'].to_code(), namespace)
+                np[parameter_block.name] = value
             except:
                 pass
         namespace.update(np)  # Merge param namespace
 
         # Load variables
-        for variable in self.get_variables():
+        for variable_block in self.get_variables():
             try:
-                value = eval(variable.get_var_value(), namespace)
-                namespace[variable.get_id()] = value
+                value = eval(variable_block.value, namespace, variable_block.namespace)
+                namespace[variable_block.name] = value
             except:
                 pass
 
@@ -252,41 +220,37 @@ class FlowGraph(Element):
         self._eval_cache.clear()
         self.namespace.update(namespace)
 
-    def evaluate(self, expr):
+    def evaluate(self, expr, namespace=None, local_namespace=None):
         """
         Evaluate the expression.
-
-        Args:
-            expr: the string expression
-        @throw Exception bad expression
-
-        Returns:
-            the evaluated data
         """
         # Evaluate
         if not expr:
             raise Exception('Cannot evaluate empty statement.')
-        return self._eval_cache.setdefault(expr, eval(expr, self.namespace))
+        if namespace is not None:
+            return eval(expr, namespace, local_namespace)
+        else:
+            return self._eval_cache.setdefault(expr, eval(expr, self.namespace))
 
     ##############################################
     # Add/remove stuff
     ##############################################
 
-    def new_block(self, key, **kwargs):
+    def new_block(self, block_id, **kwargs):
         """
         Get a new block of the specified key.
         Add the block to the list of elements.
 
         Args:
-            key: the block key
+            block_id: the block key
 
         Returns:
             the new block or None if not found
         """
-        if key == 'options':
+        if block_id == 'options':
             return self._options_block
         try:
-            block = self.parent_platform.get_new_block(self, key, **kwargs)
+            block = self.parent_platform.make_block(self, block_id, **kwargs)
             self.blocks.append(block)
         except KeyError:
             block = None
@@ -304,12 +268,17 @@ class FlowGraph(Element):
         Returns:
             the new connection
         """
-
         connection = self.parent_platform.Connection(
-            parent=self, porta=porta, portb=portb)
-        self.connections.append(connection)
+            parent=self, source=porta, sink=portb)
+        self.connections.add(connection)
         return connection
 
+    def disconnect(self, *ports):
+        to_be_removed = [con for con in self.connections
+                         if any(port in con for port in ports)]
+        for con in to_be_removed:
+            self.remove_element(con)
+
     def remove_element(self, element):
         """
         Remove the element from the list of elements.
@@ -321,21 +290,14 @@ class FlowGraph(Element):
             return
 
         if element.is_port:
-            # Found a port, set to parent signal block
-            element = element.parent
+            element = element.parent_block  # remove parent block
 
         if element in self.blocks:
             # Remove block, remove all involved connections
-            for port in element.get_ports():
-                for connection in port.get_connections():
-                    self.remove_element(connection)
+            self.disconnect(*element.ports())
             self.blocks.remove(element)
 
         elif element in self.connections:
-            if element.is_bus():
-                for port in element.source_port.get_associated_ports():
-                    for connection in port.get_connections():
-                        self.remove_element(connection)
             self.connections.remove(element)
 
     ##############################################
@@ -349,70 +311,61 @@ class FlowGraph(Element):
         Returns:
             a nested data odict
         """
-        # sort blocks and connections for nicer diffs
-        blocks = sorted(self.blocks, key=lambda b: (
-            b.key != 'options',  # options to the front
-            not b.key.startswith('variable'),  # then vars
-            str(b)
-        ))
-        connections = sorted(self.connections, key=str)
-        n = collections.OrderedDict()
-        n['timestamp'] = self._timestamp
-        n['block'] = [b.export_data() for b in blocks]
-        n['connection'] = [c.export_data() for c in connections]
-        instructions = collections.OrderedDict()
-        instructions['created'] = '.'.join(self.parent.config.version_parts)
-        instructions['format'] = FLOW_GRAPH_FILE_FORMAT_VERSION
-        return {'flow_graph': n, '_instructions': instructions}
-
-    def import_data(self, n):
+        def block_order(b):
+            return not b.key.startswith('variable'), b.name  # todo: vars still first ?!?
+
+        data = collections.OrderedDict()
+        data['options'] = self._options_block.export_data()
+        data['blocks'] = [b.export_data() for b in sorted(self.blocks, key=block_order)
+                          if b is not self._options_block]
+        data['connections'] = sorted(c.export_data() for c in self.connections)
+        data['metadata'] = {'file_format': FLOW_GRAPH_FILE_FORMAT_VERSION}
+        return data
+
+    def _build_depending_hier_block(self, block_id):
+        # we're before the initial fg update(), so no evaluated values!
+        # --> use raw value instead
+        path_param = self._options_block.params['hier_block_src_path']
+        file_path = self.parent_platform.find_file_in_paths(
+            filename=block_id + '.grc',
+            paths=path_param.get_value(),
+            cwd=self.grc_file_path
+        )
+        if file_path:  # grc file found. load and get block
+            self.parent_platform.load_and_generate_flow_graph(file_path, hier_only=True)
+            return self.new_block(block_id)  # can be None
+
+    def import_data(self, data):
         """
         Import blocks and connections into this flow graph.
         Clear this flow graph of all previous blocks and connections.
         Any blocks or connections in error will be ignored.
 
         Args:
-            n: the nested data odict
+            data: the nested data odict
         """
         # Remove previous elements
         del self.blocks[:]
-        del self.connections[:]
-        # set file format
-        try:
-            instructions = n.get('_instructions', {})
-            file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n)
-        except:
-            file_format = 0
+        self.connections.clear()
 
-        fg_n = n and n.get('flow_graph', {})  # use blank data if none provided
-        self._timestamp = fg_n.get('timestamp', time.ctime())
+        file_format = data['metadata']['file_format']
 
         # build the blocks
+        self._options_block.import_data(name='', **data.get('options', {}))
         self.blocks.append(self._options_block)
-        for block_n in fg_n.get('block', []):
-            key = block_n['key']
-            block = self.new_block(key)
-
-            if not block:
-                # we're before the initial fg update(), so no evaluated values!
-                # --> use raw value instead
-                path_param = self._options_block.get_param('hier_block_src_path')
-                file_path = self.parent_platform.find_file_in_paths(
-                    filename=key + '.grc',
-                    paths=path_param.get_value(),
-                    cwd=self.grc_file_path
-                )
-                if file_path:  # grc file found. load and get block
-                    self.parent_platform.load_and_generate_flow_graph(file_path, hier_only=True)
-                    block = self.new_block(key)  # can be None
-
-            if not block:  # looks like this block key cannot be found
-                # create a dummy block instead
-                block = self.new_block('_dummy', missing_key=key,
-                                       params_n=block_n.get('param', []))
-                print('Block key "%s" not found' % key)
-
-            block.import_data(block_n)
+
+        for block_data in data.get('blocks', []):
+            block_id = block_data['id']
+            block = (
+                self.new_block(block_id) or
+                self._build_depending_hier_block(block_id) or
+                self.new_block(block_id='_dummy', missing_block_id=block_id, **block_data)
+            )
+
+            if isinstance(block, blocks.DummyBlock):
+                print('Block id "%s" not found' % block_id)
+
+            block.import_data(**block_data)
 
         self.rewrite()  # evaluate stuff like nports before adding connections
 
@@ -420,9 +373,9 @@ class FlowGraph(Element):
         def verify_and_get_port(key, block, dir):
             ports = block.sinks if dir == 'sink' else block.sources
             for port in ports:
-                if key == port.key:
+                if key == port.key or key + '0' == port.key:
                     break
-                if not key.isdigit() and port.get_type() == '' and key == port.name:
+                if not key.isdigit() and port.dtype == '' and key == port.name:
                     break
             else:
                 if block.is_dummy_block:
@@ -431,34 +384,32 @@ class FlowGraph(Element):
                     raise LookupError('%s key %r not in %s block keys' % (dir, key, dir))
             return port
 
-        errors = False
-        for connection_n in fg_n.get('connection', []):
-            # get the block ids and port keys
-            source_block_id = connection_n.get('source_block_id')
-            sink_block_id = connection_n.get('sink_block_id')
-            source_key = connection_n.get('source_key')
-            sink_key = connection_n.get('sink_key')
+        had_connect_errors = False
+        _blocks = {block.name: block for block in self.blocks}
+        for src_blk_id, src_port_id, snk_blk_id, snk_port_id in data.get('connections', []):
             try:
-                source_block = self.get_block(source_block_id)
-                sink_block = self.get_block(sink_block_id)
+                source_block = _blocks[src_blk_id]
+                sink_block = _blocks[snk_blk_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)
+                    src_port_id, snk_port_id = _update_old_message_port_keys(
+                        src_port_id, snk_port_id, 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')
+                source_port = verify_and_get_port(src_port_id, source_block, 'source')
+                sink_port = verify_and_get_port(snk_port_id, sink_block, 'sink')
+
                 self.connect(source_port, sink_port)
-            except LookupError as e:
+
+            except (KeyError, LookupError) as e:
                 Messages.send_error_load(
                     'Connection between {}({}) and {}({}) could not be made.\n\t{}'.format(
-                        source_block_id, source_key, sink_block_id, sink_key, e))
-                errors = True
+                        src_blk_id, src_port_id, snk_blk_id, snk_port_id, e))
+                had_connect_errors = True
 
         self.rewrite()  # global rewrite
-        return errors
+        return had_connect_errors
 
 
 def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block):
@@ -475,27 +426,11 @@ def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block
     message port.
     """
     try:
-        # get ports using the "old way" (assuming liner indexed keys)
+        # get ports using the "old way" (assuming linear indexed keys)
         source_port = source_block.sources[int(source_key)]
         sink_port = sink_block.sinks[int(sink_key)]
-        if source_port.get_type() == "message" and sink_port.get_type() == "message":
+        if source_port.dtype == "message" and sink_port.dtype == "message":
             source_key, sink_key = source_port.key, sink_port.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.get('source_key', '').isdigit() and
-            connection_n.get('sink_key', '').isdigit()
-        ) for connection_n in n.get('flow_graph', []).get('connection', []))
-        if has_non_numeric_message_keys:
-            return 1
-    except:
-        pass
-    return 0
diff --git a/grc/core/Param.py b/grc/core/Param.py
index be86f0aecb..e8c81383f3 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -20,27 +20,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import absolute_import
 
 import ast
+import numbers
 import re
 import collections
 
+import six
 from six.moves import builtins, filter, map, range, zip
 
-from . import Constants
-from .Element import Element, nop_write
+from . import Constants, blocks
+from .base import Element
+from .utils.descriptors import Evaluated, EvaluatedEnum, setup_names
 
 # Blacklist certain ids, its not complete, but should help
 ID_BLACKLIST = ['self', 'options', 'gr', 'math', 'firdes'] + dir(builtins)
 try:
     from gnuradio import gr
     ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
-except ImportError:
+except (ImportError, AttributeError):
     pass
 
-_check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
-_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$')
 
-
-class TemplateArg(object):
+class TemplateArg(str):
     """
     A cheetah template argument created from a param.
     The str of this class evaluates to the param's to code method.
@@ -48,123 +48,119 @@ class TemplateArg(object):
     The __call__ or () method can return the param evaluated to a raw python data type.
     """
 
-    def __init__(self, param):
-        self._param = param
+    def __new__(cls, param):
+        value = param.to_code()
+        instance = str.__new__(cls, value)
+        setattr(instance, '_param', param)
+        return instance
 
-    def __getitem__(self, item):
+    def __getattr__(self, item):
         param = self._param
-        opts = param.options_opts[param.get_value()]
-        return str(opts[item]) if param.is_enum() else NotImplemented
-
-    def __str__(self):
-        return str(self._param.to_code())
+        attributes = param.options.attributes[param.get_value()]
+        return str(attributes.get(item)) or NotImplemented
 
     def __call__(self):
         return self._param.get_evaluated()
 
 
+@setup_names
 class Param(Element):
 
     is_param = True
 
-    def __init__(self, parent, key, name, type='raw', value='', **n):
+    name = Evaluated(str, default='no name')
+    dtype = EvaluatedEnum(Constants.PARAM_TYPE_NAMES, default='raw')
+    hide = EvaluatedEnum('none all part')
+
+    # region init
+    def __init__(self, parent, id, label='', dtype='raw', default='',
+                 options=None, option_labels=None, option_attributes=None,
+                 category='', hide='none', **_):
         """Make a new param from nested data"""
         super(Param, self).__init__(parent)
-        self.key = key
-        self._name = name
-        self.value = self.default = value
-        self._type = type
+        self.key = id
+        self.name = label.strip() or id.title()
+        self.category = category or Constants.DEFAULT_PARAM_TAB
 
-        self._hide = n.get('hide', '')
-        self.tab_label = n.get('tab', Constants.DEFAULT_PARAM_TAB)
-        self._evaluated = None
+        self.dtype = dtype
+        self.value = self.default = str(default)
 
-        self.options = []
-        self.options_names = []
-        self.options_opts = {}
-        self._init_options(options_n=n.get('option', []))
+        self.options = self._init_options(options or [], option_labels or [],
+                                          option_attributes or {})
+        self.hide = hide or 'none'
+        # end of args ########################################################
 
+        self._evaluated = None
+        self._stringify_flag = False
+        self._lisitify_flag = False
         self._init = False
-        self._hostage_cells = list()
-        self.template_arg = TemplateArg(self)
-
-    def _init_options(self, options_n):
-        """Create the Option objects from the n data"""
-        option_keys = set()
-        for option_n in options_n:
-            key, name = option_n['key'], option_n['name']
-            # Test against repeated keys
-            if key in option_keys:
-                raise KeyError('Key "{}" already exists in options'.format(key))
-            option_keys.add(key)
-            # Store the option
-            self.options.append(key)
-            self.options_names.append(name)
 
-        if self.is_enum():
-            self._init_enum(options_n)
+    @property
+    def template_arg(self):
+        return TemplateArg(self)
+
+    def _init_options(self, values, labels, attributes):
+        """parse option and option attributes"""
+        options = collections.OrderedDict()
+        options.attributes = collections.defaultdict(dict)
 
-    def _init_enum(self, options_n):
-        opt_ref = None
-        for option_n in options_n:
-            key, opts_raw = option_n['key'], option_n.get('opt', [])
+        padding = [''] * max(len(values), len(labels))
+        attributes = {key: value + padding for key, value in six.iteritems(attributes)}
+
+        for i, option in enumerate(values):
+            # Test against repeated keys
+            if option in options:
+                raise KeyError('Value "{}" already exists in options'.format(option))
+            # get label
             try:
-                self.options_opts[key] = opts = dict(opt.split(':') for opt in opts_raw)
-            except TypeError:
-                raise ValueError('Error separating opts into key:value')
-
-            if opt_ref is None:
-                opt_ref = set(opts.keys())
-            elif opt_ref != set(opts):
-                raise ValueError('Opt keys ({}) are not identical across all options.'
-                                 ''.format(', '.join(opt_ref)))
+                label = str(labels[i])
+            except IndexError:
+                label = str(option)
+            # Store the option
+            options[option] = label
+            options.attributes[option] = {attrib: values[i] for attrib, values in six.iteritems(attributes)}
+
+        default = next(iter(options)) if options else ''
         if not self.value:
-            self.value = self.default = self.options[0]
-        elif self.value not in self.options:
-            self.value = self.default = self.options[0]  # TODO: warn
+            self.value = self.default = default
+
+        if self.is_enum() and self.value not in options:
+            self.value = self.default = default  # TODO: warn
             # raise ValueError('The value {!r} is not in the possible values of {}.'
             #                  ''.format(self.get_value(), ', '.join(self.options)))
+        return options
+    # endregion
 
     def __str__(self):
         return 'Param - {}({})'.format(self.name, self.key)
 
-    def get_hide(self):
-        """
-        Get the hide value from the base class.
-        Hide the ID parameter for most blocks. Exceptions below.
-        If the parameter controls a port type, vlen, or nports, return part.
-        If the parameter is an empty grid position, return part.
-        These parameters are redundant to display in the flow graph view.
+    def __repr__(self):
+        return '{!r}.param[{}]'.format(self.parent, self.key)
 
-        Returns:
-            hide the hide property string
-        """
-        hide = self.parent.resolve_dependencies(self._hide).strip()
-        if hide:
-            return hide
-        # Hide ID in non variable blocks
-        if self.key == 'id' and not _show_id_matcher.match(self.parent.key):
-            return 'part'
-        # Hide port controllers for type and nports
-        if self.key in ' '.join([' '.join([p._type, p._nports]) for p in self.parent.get_ports()]):
-            return 'part'
-        # Hide port controllers for vlen, when == 1
-        if self.key in ' '.join(p._vlen for p in self.parent.get_ports()):
-            try:
-                if int(self.get_evaluated()) == 1:
-                    return 'part'
-            except:
-                pass
-        return hide
+    def is_enum(self):
+        return self.get_raw('dtype') == 'enum'
 
-    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 Constants.PARAM_TYPE_NAMES:
-            self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
+    def get_value(self):
+        value = self.value
+        if self.is_enum() and value not in self.options:
+            value = self.default
+            self.set_value(value)
+        return value
+
+    def set_value(self, value):
+        # Must be a string
+        self.value = str(value)
+
+    def set_default(self, value):
+        if self.default == self.value:
+            self.set_value(value)
+        self.default = str(value)
+
+    def rewrite(self):
+        Element.rewrite(self)
+        del self.name
+        del self.dtype
+        del self.hide
 
         self._evaluated = None
         try:
@@ -172,6 +168,15 @@ class Param(Element):
         except Exception as e:
             self.add_error_message(str(e))
 
+    def validate(self):
+        """
+        Validate the param.
+        The value must be evaluated and type must a possible type.
+        """
+        Element.validate(self)
+        if self.dtype not in Constants.PARAM_TYPE_NAMES:
+            self.add_error_message('Type "{}" is not a possible type.'.format(self.dtype))
+
     def get_evaluated(self):
         return self._evaluated
 
@@ -185,150 +190,112 @@ class Param(Element):
         self._init = True
         self._lisitify_flag = False
         self._stringify_flag = False
-        self._hostage_cells = list()
-        t = self.get_type()
-        v = self.get_value()
+        dtype = self.dtype
+        expr = self.get_value()
 
         #########################
         # Enum Type
         #########################
         if self.is_enum():
-            return v
+            return expr
 
         #########################
         # Numeric Types
         #########################
-        elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'):
+        elif dtype in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'):
             # Raise exception if python cannot evaluate this value
             try:
-                e = self.parent_flowgraph.evaluate(v)
-            except Exception as e:
-                raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
+                value = self.parent_flowgraph.evaluate(expr)
+            except Exception as value:
+                raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, value))
             # Raise an exception if the data is invalid
-            if t == 'raw':
-                return e
-            elif t == 'complex':
-                if not isinstance(e, Constants.COMPLEX_TYPES):
-                    raise Exception('Expression "{}" is invalid for type complex.'.format(str(e)))
-                return e
-            elif t == 'real' or t == 'float':
-                if not isinstance(e, Constants.REAL_TYPES):
-                    raise Exception('Expression "{}" is invalid for type float.'.format(str(e)))
-                return e
-            elif t == 'int':
-                if not isinstance(e, Constants.INT_TYPES):
-                    raise Exception('Expression "{}" is invalid for type integer.'.format(str(e)))
-                return e
-            elif t == 'hex':
-                return hex(e)
-            elif t == 'bool':
-                if not isinstance(e, bool):
-                    raise Exception('Expression "{}" is invalid for type bool.'.format(str(e)))
-                return e
+            if dtype == 'raw':
+                return value
+            elif dtype == 'complex':
+                if not isinstance(value, Constants.COMPLEX_TYPES):
+                    raise Exception('Expression "{}" is invalid for type complex.'.format(str(value)))
+                return value
+            elif dtype in ('real', 'float'):
+                if not isinstance(value, Constants.REAL_TYPES):
+                    raise Exception('Expression "{}" is invalid for type float.'.format(str(value)))
+                return value
+            elif dtype == 'int':
+                if not isinstance(value, Constants.INT_TYPES):
+                    raise Exception('Expression "{}" is invalid for type integer.'.format(str(value)))
+                return value
+            elif dtype == 'hex':
+                return hex(value)
+            elif dtype == 'bool':
+                if not isinstance(value, bool):
+                    raise Exception('Expression "{}" is invalid for type bool.'.format(str(value)))
+                return value
             else:
-                raise TypeError('Type "{}" not handled'.format(t))
+                raise TypeError('Type "{}" not handled'.format(dtype))
         #########################
         # Numeric Vector Types
         #########################
-        elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
-            if not v:
-                # Turn a blank string into an empty list, so it will eval
-                v = '()'
-            # Raise exception if python cannot evaluate this value
+        elif dtype in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
+            default = []
+
+            if not expr:
+                return default   # Turn a blank string into an empty list, so it will eval
+
             try:
-                e = self.parent.parent.evaluate(v)
-            except Exception as e:
-                raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
+                value = self.parent.parent.evaluate(expr)
+            except Exception as value:
+                raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, value))
+
+            if not isinstance(value, Constants.VECTOR_TYPES):
+                self._lisitify_flag = True
+                value = [value]
+
             # Raise an exception if the data is invalid
-            if t == 'complex_vector':
-                if not isinstance(e, Constants.VECTOR_TYPES):
-                    self._lisitify_flag = True
-                    e = [e]
-                if not all([isinstance(ei, Constants.COMPLEX_TYPES) for ei in e]):
-                    raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e)))
-                return e
-            elif t == 'real_vector' or t == 'float_vector':
-                if not isinstance(e, Constants.VECTOR_TYPES):
-                    self._lisitify_flag = True
-                    e = [e]
-                if not all([isinstance(ei, Constants.REAL_TYPES) for ei in e]):
-                    raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e)))
-                return e
-            elif t == 'int_vector':
-                if not isinstance(e, Constants.VECTOR_TYPES):
-                    self._lisitify_flag = True
-                    e = [e]
-                if not all([isinstance(ei, Constants.INT_TYPES) for ei in e]):
-                    raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e)))
-                return e
+            if dtype == 'complex_vector' and not all(isinstance(item, numbers.Complex) for item in value):
+                raise Exception('Expression "{}" is invalid for type complex vector.'.format(value))
+            elif dtype in ('real_vector', 'float_vector') and not all(isinstance(item, numbers.Real) for item in value):
+                raise Exception('Expression "{}" is invalid for type float vector.'.format(value))
+            elif dtype == 'int_vector' and not all(isinstance(item, Constants.INT_TYPES) for item in value):
+                raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(value)))
+            return value
         #########################
         # String Types
         #########################
-        elif t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
+        elif dtype in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
             # Do not check if file/directory exists, that is a runtime issue
             try:
-                e = self.parent.parent.evaluate(v)
-                if not isinstance(e, str):
+                value = self.parent.parent.evaluate(expr)
+                if not isinstance(value, str):
                     raise Exception()
             except:
                 self._stringify_flag = True
-                e = str(v)
-            if t == '_multiline_python_external':
-                ast.parse(e)  # Raises SyntaxError
-            return e
+                value = str(expr)
+            if dtype == '_multiline_python_external':
+                ast.parse(value)  # Raises SyntaxError
+            return value
         #########################
         # Unique ID Type
         #########################
-        elif t == 'id':
-            # Can python use this as a variable?
-            if not _check_id_matcher.match(v):
-                raise Exception('ID "{}" must begin with a letter and may contain letters, numbers, and underscores.'.format(v))
-            ids = [param.get_value() for param in self.get_all_params(t, 'id')]
-
-            if v in ID_BLACKLIST:
-                raise Exception('ID "{}" is blacklisted.'.format(v))
-
-            if self.key == 'id':
-                # Id should only appear once, or zero times if block is disabled
-                if ids.count(v) > 1:
-                    raise Exception('ID "{}" is not unique.'.format(v))
-            else:
-                # Id should exist to be a reference
-                if ids.count(v) < 1:
-                    raise Exception('ID "{}" does not exist.'.format(v))
-
-            return v
+        elif dtype == 'id':
+            self.validate_block_id()
+            return expr
 
         #########################
         # Stream ID Type
         #########################
-        elif t == 'stream_id':
-            # Get a list of all stream ids used in the virtual sinks
-            ids = [param.get_value() for param in filter(
-                lambda p: p.parent.is_virtual_sink(),
-                self.get_all_params(t),
-            )]
-            # Check that the virtual sink's stream id is unique
-            if self.parent.is_virtual_sink():
-                # Id should only appear once, or zero times if block is disabled
-                if ids.count(v) > 1:
-                    raise Exception('Stream ID "{}" is not unique.'.format(v))
-            # Check that the virtual source's steam id is found
-            if self.parent.is_virtual_source():
-                if v not in ids:
-                    raise Exception('Stream ID "{}" is not found.'.format(v))
-            return v
+        elif dtype == 'stream_id':
+            self.validate_stream_id()
+            return expr
 
         #########################
         # GUI Position/Hint
         #########################
-        elif t == 'gui_hint':
-            if ':' in v:
-                tab, pos = v.split(':')
-            elif '@' in v:
-                tab, pos = v, ''
+        elif dtype == 'gui_hint':
+            if ':' in expr:
+                tab, pos = expr.split(':')
+            elif '@' in expr:
+                tab, pos = expr, ''
             else:
-                tab, pos = '', v
+                tab, pos = '', expr
 
             if '@' in tab:
                 tab, index = tab.split('@')
@@ -358,20 +325,51 @@ class Param(Element):
         #########################
         # Import Type
         #########################
-        elif t == 'import':
+        elif dtype == 'import':
             # New namespace
             n = dict()
             try:
-                exec(v, n)
+                exec(expr, n)
             except ImportError:
-                raise Exception('Import "{}" failed.'.format(v))
+                raise Exception('Import "{}" failed.'.format(expr))
             except Exception:
-                raise Exception('Bad import syntax: "{}".'.format(v))
+                raise Exception('Bad import syntax: "{}".'.format(expr))
             return [k for k in list(n.keys()) if str(k) != '__builtins__']
 
         #########################
         else:
-            raise TypeError('Type "{}" not handled'.format(t))
+            raise TypeError('Type "{}" not handled'.format(dtype))
+
+    def validate_block_id(self):
+        value = self.value
+        # Can python use this as a variable?
+        if not re.match(r'^[a-z|A-Z]\w*$', value):
+            raise Exception('ID "{}" must begin with a letter and may contain letters, numbers, '
+                            'and underscores.'.format(value))
+        if value in ID_BLACKLIST:
+            raise Exception('ID "{}" is blacklisted.'.format(value))
+        block_names = [block.name for block in self.parent_flowgraph.iter_enabled_blocks()]
+        # Id should only appear once, or zero times if block is disabled
+        if self.key == 'id' and block_names.count(value) > 1:
+            raise Exception('ID "{}" is not unique.'.format(value))
+        elif value not in block_names:
+            raise Exception('ID "{}" does not exist.'.format(value))
+        return value
+
+    def validate_stream_id(self):
+        value = self.value
+        stream_ids = [
+            block.params['stream_id'].value
+            for block in self.parent_flowgraph.iter_enabled_blocks()
+            if isinstance(block, blocks.VirtualSink)
+            ]
+        # Check that the virtual sink's stream id is unique
+        if isinstance(self.parent_block, blocks.VirtualSink) and stream_ids.count(value) >= 2:
+            # Id should only appear once, or zero times if block is disabled
+            raise Exception('Stream ID "{}" is not unique.'.format(value))
+        # Check that the virtual source's steam id is found
+        elif isinstance(self.parent_block, blocks.VirtualSource) and value not in stream_ids:
+            raise Exception('Stream ID "{}" is not found.'.format(value))
 
     def to_code(self):
         """
@@ -382,8 +380,9 @@ class Param(Element):
         Returns:
             a string representing the code
         """
+        self._init = True
         v = self.get_value()
-        t = self.get_type()
+        t = self.dtype
         # String types
         if t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
             if not self._init:
@@ -400,71 +399,3 @@ class Param(Element):
                 return '(%s)' % v
         else:
             return v
-
-    def get_all_params(self, type, key=None):
-        """
-        Get all the params from the flowgraph that have the given type and
-        optionally a given key
-
-        Args:
-            type: the specified type
-            key: the key to match against
-
-        Returns:
-            a list of params
-        """
-        params = []
-        for block in self.parent_flowgraph.get_enabled_blocks():
-            params.extend(p for k, p in block.params.items() if p.get_type() == type and (key is None or key == k))
-        return params
-
-    def is_enum(self):
-        return self._type == 'enum'
-
-    def get_value(self):
-        value = self.value
-        if self.is_enum() and value not in self.options:
-            value = self.options[0]
-            self.set_value(value)
-        return value
-
-    def set_value(self, value):
-        # Must be a string
-        self.value = str(value)
-
-    def set_default(self, value):
-        if self.default == self.value:
-            self.set_value(value)
-        self.default = str(value)
-
-    def get_type(self):
-        return self.parent.resolve_dependencies(self._type)
-
-    def get_tab_label(self):
-        return self.tab_label
-
-    @nop_write
-    @property
-    def name(self):
-        return self.parent.resolve_dependencies(self._name).strip()
-
-    ##############################################
-    # Access Options
-    ##############################################
-    def opt_value(self, key):
-        return self.options_opts[self.get_value()][key]
-
-    ##############################################
-    # Import/Export Methods
-    ##############################################
-    def export_data(self):
-        """
-        Export this param's key/value.
-
-        Returns:
-            a nested data odict
-        """
-        n = collections.OrderedDict()
-        n['key'] = self.key
-        n['value'] = self.get_value()
-        return n
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
deleted file mode 100644
index 73937f1299..0000000000
--- a/grc/core/Platform.py
+++ /dev/null
@@ -1,341 +0,0 @@
-"""
-Copyright 2008-2016 Free Software Foundation, Inc.
-This file is part of GNU Radio
-
-GNU Radio Companion is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-GNU Radio Companion is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-"""
-
-from __future__ import absolute_import, print_function
-
-import os
-import sys
-
-import six
-from six.moves import range
-
-from . import ParseXML, Messages, Constants
-
-from .Config import Config
-from .Element import Element
-from .generator import Generator
-from .FlowGraph import FlowGraph
-from .Connection import Connection
-from . import Block
-from .Port import Port, PortClone
-from .Param import Param
-
-from .utils import extract_docs
-
-
-class Platform(Element):
-
-    is_platform = True
-
-    def __init__(self, *args, **kwargs):
-        """ Make a platform for GNU Radio """
-        Element.__init__(self, parent=None)
-
-        self.config = self.Config(*args, **kwargs)
-        self.block_docstrings = {}
-        self.block_docstrings_loaded_callback = lambda: None  # dummy to be replaced by BlockTreeWindow
-
-        self._docstring_extractor = extract_docs.SubprocessLoader(
-            callback_query_result=self._save_docstring_extraction_result,
-            callback_finished=lambda: self.block_docstrings_loaded_callback()
-        )
-
-        self.blocks = {}
-        self._blocks_n = {}
-        self._block_categories = {}
-        self.domains = {}
-        self.connection_templates = {}
-
-        self._auto_hier_block_generate_chain = set()
-
-        # Create a dummy flow graph for the blocks
-        self._flow_graph = Element.__new__(FlowGraph)
-        Element.__init__(self._flow_graph, self)
-        self._flow_graph.connections = []
-
-        self.build_block_library()
-
-    def __str__(self):
-        return 'Platform - {}'.format(self.config.name)
-
-    @staticmethod
-    def find_file_in_paths(filename, paths, cwd):
-        """Checks the provided paths relative to cwd for a certain filename"""
-        if not os.path.isdir(cwd):
-            cwd = os.path.dirname(cwd)
-        if isinstance(paths, str):
-            paths = (p for p in paths.split(':') if p)
-
-        for path in paths:
-            path = os.path.expanduser(path)
-            if not os.path.isabs(path):
-                path = os.path.normpath(os.path.join(cwd, path))
-            file_path = os.path.join(path, filename)
-            if os.path.exists(os.path.normpath(file_path)):
-                return file_path
-
-    def load_and_generate_flow_graph(self, file_path, out_path=None, hier_only=False):
-        """Loads a flow graph from file and generates it"""
-        Messages.set_indent(len(self._auto_hier_block_generate_chain))
-        Messages.send('>>> Loading: {}\n'.format(file_path))
-        if file_path in self._auto_hier_block_generate_chain:
-            Messages.send('    >>> Warning: cyclic hier_block dependency\n')
-            return None, None
-        self._auto_hier_block_generate_chain.add(file_path)
-        try:
-            flow_graph = self.get_new_flow_graph()
-            flow_graph.grc_file_path = file_path
-            # Other, nested hier_blocks might be auto-loaded here
-            flow_graph.import_data(self.parse_flow_graph(file_path))
-            flow_graph.rewrite()
-            flow_graph.validate()
-            if not flow_graph.is_valid():
-                raise Exception('Flowgraph invalid')
-            if hier_only and not flow_graph.get_option('generate_options').startswith('hb'):
-                raise Exception('Not a hier block')
-        except Exception as e:
-            Messages.send('>>> Load Error: {}: {}\n'.format(file_path, str(e)))
-            return None, None
-        finally:
-            self._auto_hier_block_generate_chain.discard(file_path)
-            Messages.set_indent(len(self._auto_hier_block_generate_chain))
-
-        try:
-            generator = self.Generator(flow_graph, out_path or file_path)
-            Messages.send('>>> Generating: {}\n'.format(generator.file_path))
-            generator.write()
-        except Exception as e:
-            Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e)))
-            return None, None
-
-        if flow_graph.get_option('generate_options').startswith('hb'):
-            self.load_block_xml(generator.file_path_xml)
-        return flow_graph, generator.file_path
-
-    def build_block_library(self):
-        """load the blocks and block tree from the search paths"""
-        self._docstring_extractor.start()
-
-        # Reset
-        self.blocks.clear()
-        self._blocks_n.clear()
-        self._block_categories.clear()
-        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:
-                raise
-                print('Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file), file=sys.stderr)
-
-        # Add blocks to block tree
-        for key, block in six.iteritems(self.blocks):
-            category = self._block_categories.get(key, block.category)
-            # Blocks with empty categories are hidden
-            if not category:
-                continue
-            root = category[0]
-            if root.startswith('[') and root.endswith(']'):
-                category[0] = root[1:-1]
-            else:
-                category.insert(0, Constants.DEFAULT_BLOCK_MODULE_NAME)
-            block.category = category
-
-        self._docstring_extractor.finish()
-        # self._docstring_extractor.wait()
-
-    def iter_xml_files(self):
-        """Iterator for block descriptions and category trees"""
-        for block_path in self.config.block_paths:
-            if os.path.isfile(block_path):
-                yield block_path
-            elif os.path.isdir(block_path):
-                for dirpath, dirnames, filenames in os.walk(block_path):
-                    for filename in sorted(f for f in filenames if f.endswith('.xml')):
-                        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, Constants.BLOCK_DTD)
-        n = ParseXML.from_file(xml_file).get('block', {})
-        n['block_wrapper_path'] = xml_file  # inject block wrapper path
-        key = n.pop('key')
-
-        if key in self.blocks:
-            print('Warning: Block with key "{}" already exists.\n'
-                  '\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
-            return
-
-        # Store the block
-        self.blocks[key] = block = self.get_new_block(self._flow_graph, key, **n)
-        self._blocks_n[key] = n
-        self._docstring_extractor.query(
-            key,
-            block.get_imports(raw=True),
-            block.get_make(raw=True)
-        )
-
-    def load_category_tree_xml(self, xml_file):
-        """Validate and parse category tree file and add it to list"""
-        ParseXML.validate_dtd(xml_file, Constants.BLOCK_TREE_DTD)
-        xml = ParseXML.from_file(xml_file)
-        path = []
-
-        def load_category(cat_n):
-            path.append(cat_n.get('name').strip())
-            for block_key in cat_n.get('block', []):
-                if block_key not in self._block_categories:
-                    self._block_categories[block_key] = list(path)
-            for sub_cat_n in cat_n.get('cat', []):
-                load_category(sub_cat_n)
-            path.pop()
-
-        load_category(xml.get('cat', {}))
-
-    def load_domain_xml(self, xml_file):
-        """Load a domain properties and connection templates from XML"""
-        ParseXML.validate_dtd(xml_file, Constants.DOMAIN_DTD)
-        n = ParseXML.from_file(xml_file).get('domain')
-
-        key = n.get('key')
-        if not key:
-            print('Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file), file=sys.stderr)
-            return
-        if key in self.domains:  # test against repeated keys
-            print('Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
-            return
-
-        # to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '')
-        def to_bool(s, d):
-            if s is not None:
-                return s.lower() not in ('false', 'off', '0', '')
-            return d
-
-        color = n.get('color') or ''
-        try:
-            chars_per_color = 2 if len(color) > 4 else 1
-            tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 3 * chars_per_color, chars_per_color))
-        except ValueError:
-            if color:  # no color is okay, default set in GUI
-                print('Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key), file=sys.stderr)
-                color = None
-
-        self.domains[key] = dict(
-            name=n.get('name') or key,
-            multiple_sinks=to_bool(n.get('multiple_sinks'), True),
-            multiple_sources=to_bool(n.get('multiple_sources'), False),
-            color=color
-        )
-        for connection_n in n.get('connection', []):
-            key = (connection_n.get('source_domain'), connection_n.get('sink_domain'))
-            if not all(key):
-                print('Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file), file=sys.stderr)
-            elif key in self.connection_templates:
-                print('Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file), file=sys.stderr)
-            else:
-                self.connection_templates[key] = connection_n.get('make') or ''
-
-    def _save_docstring_extraction_result(self, key, docstrings):
-        docs = {}
-        for match, docstring in six.iteritems(docstrings):
-            if not docstring or match.endswith('_sptr'):
-                continue
-            docstring = docstring.replace('\n\n', '\n').strip()
-            docs[match] = docstring
-        self.block_docstrings[key] = docs
-
-    ##############################################
-    # Access
-    ##############################################
-
-    def parse_flow_graph(self, flow_graph_file):
-        """
-        Parse a saved flow graph file.
-        Ensure that the file exists, and passes the dtd check.
-
-        Args:
-            flow_graph_file: the flow graph file
-
-        Returns:
-            nested data
-        @throws exception if the validation fails
-        """
-        flow_graph_file = flow_graph_file or self.config.default_flow_graph
-        open(flow_graph_file, 'r').close()  # Test open
-        ParseXML.validate_dtd(flow_graph_file, Constants.FLOW_GRAPH_DTD)
-        return ParseXML.from_file(flow_graph_file)
-
-    def get_blocks(self):
-        return list(self.blocks.values())
-
-    def get_generate_options(self):
-        gen_opts = self.blocks['options'].get_param('generate_options')
-        generate_mode_default = gen_opts.get_value()
-        return [(key, name, key == generate_mode_default)
-                for key, name in zip(gen_opts.options, gen_opts.options_names)]
-
-    ##############################################
-    # Factories
-    ##############################################
-    Config = Config
-    Generator = Generator
-    FlowGraph = FlowGraph
-    Connection = Connection
-    block_classes = {
-        None: Block.Block,  # default
-        'epy_block': Block.EPyBlock,
-        '_dummy': Block.DummyBlock,
-    }
-    port_classes = {
-        None: Port,  # default
-        'clone': PortClone,  # default
-    }
-    param_classes = {
-        None: Param,  # default
-    }
-
-    def get_new_flow_graph(self):
-        return self.FlowGraph(parent=self)
-
-    def get_new_block(self, parent, key, **kwargs):
-        cls = self.block_classes.get(key, self.block_classes[None])
-        if not kwargs:
-            kwargs = self._blocks_n[key]
-        return cls(parent, key=key, **kwargs)
-
-    def get_new_param(self, parent, **kwargs):
-        cls = self.param_classes[kwargs.pop('cls_key', None)]
-        return cls(parent, **kwargs)
-
-    def get_new_port(self, parent, **kwargs):
-        cls = self.port_classes[kwargs.pop('cls_key', None)]
-        return cls(parent, **kwargs)
diff --git a/grc/core/Port.py b/grc/core/Port.py
deleted file mode 100644
index 9ca443efa1..0000000000
--- a/grc/core/Port.py
+++ /dev/null
@@ -1,391 +0,0 @@
-"""
-Copyright 2008-2017 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 __future__ import absolute_import
-
-from itertools import chain
-
-from six.moves import filter
-
-from .Element import Element, lazy_property
-from . import Constants
-
-
-class LoopError(Exception):
-    pass
-
-
-def _upstream_ports(port):
-    if port.is_sink:
-        return _sources_from_virtual_sink_port(port)
-    else:
-        return _sources_from_virtual_source_port(port)
-
-
-def _sources_from_virtual_sink_port(sink_port, _traversed=None):
-    """
-    Resolve the source port that is connected to the given virtual sink port.
-    Use the get source from virtual source to recursively resolve subsequent ports.
-    """
-    source_ports_per_virtual_connection = (
-        # there can be multiple ports per virtual connection
-        _sources_from_virtual_source_port(c.source_port, _traversed)  # type: list
-        for c in sink_port.get_enabled_connections()
-    )
-    return list(chain(*source_ports_per_virtual_connection))  # concatenate generated lists of ports
-
-
-def _sources_from_virtual_source_port(source_port, _traversed=None):
-    """
-    Recursively resolve source ports over the virtual connections.
-    Keep track of traversed sources to avoid recursive loops.
-    """
-    _traversed = set(_traversed or [])  # a new set!
-    if source_port in _traversed:
-        raise LoopError('Loop found when resolving port type')
-    _traversed.add(source_port)
-
-    block = source_port.parent_block
-    flow_graph = source_port.parent_flow_graph
-
-    if not block.is_virtual_source():
-        return [source_port]  # nothing to resolve, we're done
-
-    stream_id = block.get_param('stream_id').get_value()
-
-    # currently the validation does not allow multiple virtual sinks and one virtual source
-    # but in the future it may...
-    connected_virtual_sink_blocks = (
-        b for b in flow_graph.iter_enabled_blocks()
-        if b.is_virtual_sink() and b.get_param('stream_id').get_value() == stream_id
-    )
-    source_ports_per_virtual_connection = (
-        _sources_from_virtual_sink_port(b.sinks[0], _traversed)  # type: list
-        for b in connected_virtual_sink_blocks
-    )
-    return list(chain(*source_ports_per_virtual_connection))  # concatenate generated lists of ports
-
-
-def _downstream_ports(port):
-    if port.is_source:
-        return _sinks_from_virtual_source_port(port)
-    else:
-        return _sinks_from_virtual_sink_port(port)
-
-
-def _sinks_from_virtual_source_port(source_port, _traversed=None):
-    """
-    Resolve the sink port that is connected to the given virtual source port.
-    Use the get sink from virtual sink to recursively resolve subsequent ports.
-    """
-    sink_ports_per_virtual_connection = (
-        # there can be multiple ports per virtual connection
-        _sinks_from_virtual_sink_port(c.sink_port, _traversed)  # type: list
-        for c in source_port.get_enabled_connections()
-    )
-    return list(chain(*sink_ports_per_virtual_connection))  # concatenate generated lists of ports
-
-
-def _sinks_from_virtual_sink_port(sink_port, _traversed=None):
-    """
-    Recursively resolve sink ports over the virtual connections.
-    Keep track of traversed sinks to avoid recursive loops.
-    """
-    _traversed = set(_traversed or [])  # a new set!
-    if sink_port in _traversed:
-        raise LoopError('Loop found when resolving port type')
-    _traversed.add(sink_port)
-
-    block = sink_port.parent_block
-    flow_graph = sink_port.parent_flow_graph
-
-    if not block.is_virtual_sink():
-        return [sink_port]
-
-    stream_id = block.get_param('stream_id').get_value()
-
-    connected_virtual_source_blocks = (
-        b for b in flow_graph.iter_enabled_blocks()
-        if b.is_virtual_source() and b.get_param('stream_id').get_value() == stream_id
-    )
-    sink_ports_per_virtual_connection = (
-        _sinks_from_virtual_source_port(b.sources[0], _traversed)  # type: list
-        for b in connected_virtual_source_blocks
-    )
-    return list(chain(*sink_ports_per_virtual_connection))  # concatenate generated lists of ports
-
-
-class Port(Element):
-
-    is_port = True
-    is_clone = False
-
-    def __init__(self, parent, direction, **n):
-        """
-        Make a new port from nested data.
-
-        Args:
-            block: the parent element
-            n: the nested odict
-            dir: the direction
-        """
-        self._n = n
-        if n['type'] == 'message':
-            n['domain'] = Constants.GR_MESSAGE_DOMAIN
-
-        if 'domain' not in n:
-            n['domain'] = Constants.DEFAULT_DOMAIN
-        elif n['domain'] == Constants.GR_MESSAGE_DOMAIN:
-            n['key'] = n['name']
-            n['type'] = 'message'  # For port color
-
-        # Build the port
-        Element.__init__(self, parent)
-        # Grab the data
-        self.name = n['name']
-        self.key = n['key']
-        self.domain = n.get('domain')
-        self._type = n.get('type', '')
-        self.inherit_type = not self._type
-        self._hide = n.get('hide', '')
-        self._dir = direction
-        self._hide_evaluated = False  # Updated on rewrite()
-
-        self._nports = n.get('nports', '')
-        self._vlen = n.get('vlen', '')
-        self._optional = bool(n.get('optional'))
-        self._optional_evaluated = False  # Updated on rewrite()
-        self.clones = []  # References to cloned ports (for nports > 1)
-
-    def __str__(self):
-        if self.is_source:
-            return 'Source - {}({})'.format(self.name, self.key)
-        if self.is_sink:
-            return 'Sink - {}({})'.format(self.name, self.key)
-
-    def validate(self):
-        Element.validate(self)
-        if self.get_type() not in Constants.TYPE_TO_SIZEOF.keys():
-            self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
-        if self.domain not in self.parent_platform.domains:
-            self.add_error_message('Domain key "{}" is not registered.'.format(self.domain))
-        if not self.get_enabled_connections() and not self.get_optional():
-            self.add_error_message('Port is not connected.')
-
-    def rewrite(self):
-        """
-        Handle the port cloning for virtual blocks.
-        """
-        del self._error_messages[:]
-        if self.inherit_type:
-            self.resolve_empty_type()
-
-        hide = self.parent_block.resolve_dependencies(self._hide).strip().lower()
-        self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide)
-        optional = self.parent_block.resolve_dependencies(self._optional).strip().lower()
-        self._optional_evaluated = False if optional in ('false', 'off', '0') else bool(optional)
-
-        # Update domain if was deduced from (dynamic) port type
-        type_ = self.get_type()
-        if self.domain == Constants.GR_STREAM_DOMAIN and type_ == "message":
-            self.domain = Constants.GR_MESSAGE_DOMAIN
-            self.key = self.name
-        if self.domain == Constants.GR_MESSAGE_DOMAIN and type_ != "message":
-            self.domain = Constants.GR_STREAM_DOMAIN
-            self.key = '0'  # Is rectified in rewrite()
-
-    def resolve_virtual_source(self):
-        """Only used by Generator after validation is passed"""
-        return _upstream_ports(self)
-
-    def resolve_empty_type(self):
-        def find_port(finder):
-            try:
-                return next((p for p in finder(self) if not p.inherit_type), None)
-            except LoopError as error:
-                self.add_error_message(str(error))
-            except (StopIteration, Exception) as error:
-                pass
-
-        try:
-            port = find_port(_upstream_ports) or find_port(_downstream_ports)
-            self._type = str(port.get_type())
-            self._vlen = str(port.get_vlen())
-        except Exception:
-            # Reset type and vlen
-            self._type = self._vlen = ''
-
-    def get_vlen(self):
-        """
-        Get the vector length.
-        If the evaluation of vlen cannot be cast to an integer, return 1.
-
-        Returns:
-            the vector length or 1
-        """
-        vlen = self.parent_block.resolve_dependencies(self._vlen)
-        try:
-            return max(1, int(self.parent_flowgraph.evaluate(vlen)))
-        except:
-            return 1
-
-    def get_nports(self):
-        """
-        Get the number of ports.
-        If already blank, return a blank
-        If the evaluation of nports cannot be cast to a positive integer, return 1.
-
-        Returns:
-            the number of ports or 1
-        """
-        if self._nports == '':
-            return 1
-
-        nports = self.parent_block.resolve_dependencies(self._nports)
-        try:
-            return max(1, int(self.parent_flowgraph.evaluate(nports)))
-        except:
-            return 1
-
-    def get_optional(self):
-        return self._optional_evaluated
-
-    def add_clone(self):
-        """
-        Create a clone of this (master) port and store a reference in self._clones.
-
-        The new port name (and key for message ports) will have index 1... appended.
-        If this is the first clone, this (master) port will get a 0 appended to its name (and key)
-
-        Returns:
-            the cloned port
-        """
-        # Add index to master port name if there are no clones yet
-        if not self.clones:
-            self.name = self._n['name'] + '0'
-            # Also update key for none stream ports
-            if not self.key.isdigit():
-                self.key = self.name
-
-        name = self._n['name'] + str(len(self.clones) + 1)
-        # Dummy value 99999 will be fixed later
-        key = '99999' if self.key.isdigit() else name
-
-        # Clone
-        port_factory = self.parent_platform.get_new_port
-        port = port_factory(self.parent, direction=self._dir,
-                            name=name, key=key,
-                            master=self, cls_key='clone')
-
-        self.clones.append(port)
-        return port
-
-    def remove_clone(self, port):
-        """
-        Remove a cloned port (from the list of clones only)
-        Remove the index 0 of the master port name (and key9 if there are no more clones left
-        """
-        self.clones.remove(port)
-        # Remove index from master port name if there are no more clones
-        if not self.clones:
-            self.name = self._n['name']
-            # Also update key for none stream ports
-            if not self.key.isdigit():
-                self.key = self.name
-
-    @lazy_property
-    def is_sink(self):
-        return self._dir == 'sink'
-
-    @lazy_property
-    def is_source(self):
-        return self._dir == 'source'
-
-    def get_type(self):
-        return self.parent_block.resolve_dependencies(self._type)
-
-    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.parent_flowgraph.connections
-        connections = [c for c in connections if c.source_port is self or c.sink_port is self]
-        return connections
-
-    def get_enabled_connections(self):
-        """
-        Get all enabled connections that use this port.
-
-        Returns:
-            a list of connection objects
-        """
-        return [c for c in self.get_connections() if c.enabled]
-
-    def get_associated_ports(self):
-        if not self.get_type() == 'bus':
-            return [self]
-
-        block = self.parent_block
-        if self.is_source:
-            block_ports = block.sources
-            bus_structure = block.current_bus_structure['source']
-        else:
-            block_ports = block.sinks
-            bus_structure = block.current_bus_structure['sink']
-
-        ports = [i for i in block_ports if not i.get_type() == 'bus']
-        if bus_structure:
-            bus_index = [i for i in block_ports if i.get_type() == 'bus'].index(self)
-            ports = [p for i, p in enumerate(ports) if i in bus_structure[bus_index]]
-        return ports
-
-
-class PortClone(Port):
-
-    is_clone = True
-
-    def __init__(self, parent, direction, master, name, key):
-        """
-        Make a new port from nested data.
-
-        Args:
-            block: the parent element
-            n: the nested odict
-            dir: the direction
-        """
-        Element.__init__(self, parent)
-        self.master = master
-        self.name = name
-        self._key = key
-        self._nports = '1'
-
-    def __getattr__(self, item):
-        return getattr(self.master, item)
-
-    def add_clone(self):
-        raise NotImplementedError()
-
-    def remove_clone(self, port):
-        raise NotImplementedError()
diff --git a/grc/core/base.py b/grc/core/base.py
new file mode 100644
index 0000000000..e5ff657d85
--- /dev/null
+++ b/grc/core/base.py
@@ -0,0 +1,164 @@
+# Copyright 2008, 2009, 2015, 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+import weakref
+
+from .utils.descriptors import lazy_property
+
+
+class Element(object):
+
+    def __init__(self, parent=None):
+        self._parent = weakref.ref(parent) if parent else lambda: None
+        self._error_messages = []
+
+    ##################################################
+    # Element Validation API
+    ##################################################
+    def validate(self):
+        """
+        Validate this element and call validate on all children.
+        Call this base method before adding error messages in the subclass.
+        """
+        del self._error_messages[:]
+
+        for child in self.children():
+            child.validate()
+
+    def is_valid(self):
+        """
+        Is this element valid?
+
+        Returns:
+            true when the element is enabled and has no error messages or is bypassed
+        """
+        if not self.enabled or self.get_bypassed():
+            return True
+        return not next(self.iter_error_messages(), False)
+
+    def add_error_message(self, msg):
+        """
+        Add an error message to the list of errors.
+
+        Args:
+            msg: the error message string
+        """
+        self._error_messages.append(msg)
+
+    def get_error_messages(self):
+        """
+        Get the list of error messages from this element and all of its children.
+        Do not include the error messages from disabled or bypassed children.
+        Cleverly indent the children error messages for printing purposes.
+
+        Returns:
+            a list of error message strings
+        """
+        return [msg if elem is self else "{}:\n\t{}".format(elem, msg.replace("\n", "\n\t"))
+                for elem, msg in self.iter_error_messages()]
+
+    def iter_error_messages(self):
+        """
+        Iterate over error messages. Yields tuples of (element, message)
+        """
+        for msg in self._error_messages:
+            yield self, msg
+        for child in self.children():
+            if not child.enabled or child.get_bypassed():
+                continue
+            for element_msg in child.iter_error_messages():
+                yield element_msg
+
+    def rewrite(self):
+        """
+        Rewrite this element and call rewrite on all children.
+        Call this base method before rewriting the element.
+        """
+        for child in self.children():
+            child.rewrite()
+
+    @property
+    def enabled(self):
+        return True
+
+    def get_bypassed(self):
+        return False
+
+    ##############################################
+    # Tree-like API
+    ##############################################
+    @property
+    def parent(self):
+        return self._parent()
+
+    def get_parent_by_type(self, cls):
+        parent = self.parent
+        if parent is None:
+            return None
+        elif isinstance(parent, cls):
+            return parent
+        else:
+            return parent.get_parent_by_type(cls)
+
+    @lazy_property
+    def parent_platform(self):
+        from .platform import Platform
+        return self.get_parent_by_type(Platform)
+
+    @lazy_property
+    def parent_flowgraph(self):
+        from .FlowGraph import FlowGraph
+        return self.get_parent_by_type(FlowGraph)
+
+    @lazy_property
+    def parent_block(self):
+        from .blocks import Block
+        return self.get_parent_by_type(Block)
+
+    def reset_parents_by_type(self):
+        """Reset all lazy properties"""
+        for name, obj in vars(Element):  # explicitly only in Element, not subclasses
+            if isinstance(obj, lazy_property):
+                delattr(self, name)
+
+    def children(self):
+        return
+        yield  # empty generator
+
+    ##############################################
+    # Type testing
+    ##############################################
+    is_flow_graph = False
+    is_block = False
+    is_dummy_block = False
+    is_connection = False
+    is_port = False
+    is_param = False
+    is_variable = False
+    is_import = False
+
+    def get_raw(self, name):
+        descriptor = getattr(self.__class__, name, None)
+        if not descriptor:
+            raise ValueError("No evaluated property '{}' found".format(name))
+        return getattr(self, descriptor.name_raw, None) or getattr(self, descriptor.name, None)
+
+    def set_evaluated(self, name, value):
+        descriptor = getattr(self.__class__, name, None)
+        if not descriptor:
+            raise ValueError("No evaluated property '{}' found".format(name))
+        self.__dict__[descriptor.name] = value
diff --git a/grc/core/block.dtd b/grc/core/block.dtd
deleted file mode 100644
index 145f4d8610..0000000000
--- a/grc/core/block.dtd
+++ /dev/null
@@ -1,69 +0,0 @@
-<!--
-Copyright 2008 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
--->
-<!--
-    gnuradio_python.blocks.dtd
-    Josh Blum
-    The document type definition for blocks.
- -->
-<!--
-    Top level element.
-    A block contains a name, ...parameters list, and list of IO ports.
- -->
-<!ELEMENT block (name, key, category?, throttle?, flags?, import*, var_make?, var_value?,
-        make, callback*, param_tab_order?, param*, bus_sink?, bus_source?, check*,
-        sink*, source*, bus_structure_sink?, bus_structure_source?, doc?,  grc_source?)>
-<!--
-    Sub level elements.
- -->
-<!ELEMENT param_tab_order (tab+)>
-<!ELEMENT param (base_key?, name, key, value?, type?, hide?, option*, tab?)>
-<!ELEMENT option (name, key, opt*)>
-<!ELEMENT sink (name, type, vlen?, domain?, nports?, optional?, hide?)>
-<!ELEMENT source (name, type, vlen?, domain?, nports?, optional?, hide?)>
-<!--
-    Bottom level elements.
-    Character data only.
- -->
-<!ELEMENT category (#PCDATA)>
-<!ELEMENT import (#PCDATA)>
-<!ELEMENT doc (#PCDATA)>
-<!ELEMENT grc_source (#PCDATA)>
-<!ELEMENT tab (#PCDATA)>
-<!ELEMENT name (#PCDATA)>
-<!ELEMENT base_key (#PCDATA)>
-<!ELEMENT key (#PCDATA)>
-<!ELEMENT check (#PCDATA)>
-<!ELEMENT bus_sink (#PCDATA)>
-<!ELEMENT bus_source (#PCDATA)>
-<!ELEMENT opt (#PCDATA)>
-<!ELEMENT type (#PCDATA)>
-<!ELEMENT hide (#PCDATA)>
-<!ELEMENT vlen (#PCDATA)>
-<!ELEMENT domain (#PCDATA)>
-<!ELEMENT nports (#PCDATA)>
-<!ELEMENT bus_structure_sink (#PCDATA)>
-<!ELEMENT bus_structure_source (#PCDATA)>
-<!ELEMENT var_make (#PCDATA)>
-<!ELEMENT var_value (#PCDATA)>
-<!ELEMENT make (#PCDATA)>
-<!ELEMENT value (#PCDATA)>
-<!ELEMENT callback (#PCDATA)>
-<!ELEMENT optional (#PCDATA)>
-<!ELEMENT throttle (#PCDATA)>
-<!ELEMENT flags (#PCDATA)>
diff --git a/grc/core/block_tree.dtd b/grc/core/block_tree.dtd
deleted file mode 100644
index 9e23576477..0000000000
--- a/grc/core/block_tree.dtd
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Copyright 2008 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
--->
-<!--
-    block_tree.dtd
-    Josh Blum
-    The document type definition for a block tree category listing.
- -->
-<!ELEMENT cat (name, cat*, block*)>
-<!ELEMENT name (#PCDATA)>
-<!ELEMENT block (#PCDATA)>
diff --git a/grc/core/blocks/__init__.py b/grc/core/blocks/__init__.py
new file mode 100644
index 0000000000..e4a085d477
--- /dev/null
+++ b/grc/core/blocks/__init__.py
@@ -0,0 +1,37 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from ._flags import Flags
+from ._templates import MakoTemplates
+
+from .block import Block
+
+from ._build import build
+
+
+build_ins = {}
+
+
+def register_build_in(cls):
+    build_ins[cls.key] = cls
+    return cls
+
+from .dummy import DummyBlock
+from .embedded_python import EPyBlock, EPyModule
+from .virtual import VirtualSink, VirtualSource
diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py
new file mode 100644
index 0000000000..9a50086cea
--- /dev/null
+++ b/grc/core/blocks/_build.py
@@ -0,0 +1,69 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+import re
+
+from .block import Block
+from ._flags import Flags
+from ._templates import MakoTemplates
+
+
+def build(id, label='', category='', flags='', documentation='',
+          checks=None, value=None,
+          parameters=None, inputs=None, outputs=None, templates=None, **kwargs):
+    block_id = id
+
+    cls = type(block_id, (Block,), {})
+    cls.key = block_id
+
+    cls.label = label or block_id.title()
+    cls.category = [cat.strip() for cat in category.split('/') if cat.strip()]
+
+    cls.flags = Flags(flags)
+    if re.match(r'options$|variable|virtual', block_id):
+        cls.flags += Flags.NOT_DSP + Flags.DISABLE_BYPASS
+
+    cls.documentation = {'': documentation.strip('\n\t ').replace('\\\n', '')}
+
+    cls.checks = [_single_mako_expr(check, block_id) for check in (checks or [])]
+
+    cls.parameters_data = parameters or []
+    cls.inputs_data = inputs or []
+    cls.outputs_data = outputs or []
+    cls.extra_data = kwargs
+
+    templates = templates or {}
+    cls.templates = MakoTemplates(
+        imports=templates.get('imports', ''),
+        make=templates.get('make', ''),
+        callbacks=templates.get('callbacks', []),
+        var_make=templates.get('var_make', ''),
+    )
+    # todo: MakoTemplates.compile() to check for errors
+
+    cls.value = _single_mako_expr(value, block_id)
+
+    return cls
+
+
+def _single_mako_expr(value, block_id):
+    match = re.match(r'\s*\$\{\s*(.*?)\s*\}\s*', str(value))
+    if value and not match:
+        raise ValueError('{} is not a mako substitution in {}'.format(value, block_id))
+    return match.group(1) if match else None
diff --git a/grc/core/blocks/_flags.py b/grc/core/blocks/_flags.py
new file mode 100644
index 0000000000..ffea2ad569
--- /dev/null
+++ b/grc/core/blocks/_flags.py
@@ -0,0 +1,39 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+import six
+
+
+class Flags(six.text_type):
+
+    THROTTLE = 'throttle'
+    DISABLE_BYPASS = 'disable_bypass'
+    NEED_QT_GUI = 'need_qt_gui'
+    DEPRECATED = 'deprecated'
+    NOT_DSP = 'not_dsp'
+
+    def __getattr__(self, item):
+        return item in self
+
+    def __add__(self, other):
+        if not isinstance(other, six.string_types):
+            return NotImplemented
+        return self.__class__(str(self) + other)
+
+    __iadd__ = __add__
diff --git a/grc/core/blocks/_templates.py b/grc/core/blocks/_templates.py
new file mode 100644
index 0000000000..0b15166423
--- /dev/null
+++ b/grc/core/blocks/_templates.py
@@ -0,0 +1,77 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+"""
+This dict class holds a (shared) cache of compiled mako templates.
+These
+
+"""
+from __future__ import absolute_import, print_function
+
+from mako.template import Template
+from mako.exceptions import SyntaxException
+
+from ..errors import TemplateError
+
+
+class MakoTemplates(dict):
+
+    _template_cache = {}
+
+    def __init__(self, _bind_to=None, *args, **kwargs):
+        self.instance = _bind_to
+        dict.__init__(self, *args, **kwargs)
+
+    def __get__(self, instance, owner):
+        if instance is None or self.instance is not None:
+            return self
+        copy = self.__class__(_bind_to=instance, **self)
+        if getattr(instance.__class__, 'templates', None) is self:
+            setattr(instance, 'templates', copy)
+        return copy
+
+    @classmethod
+    def compile(cls, text):
+        text = str(text)
+        try:
+            template = Template(text)
+        except SyntaxException as error:
+            raise TemplateError(text, *error.args)
+
+        cls._template_cache[text] = template
+        return template
+
+    def _get_template(self, text):
+        try:
+            return self._template_cache[str(text)]
+        except KeyError:
+            return self.compile(text)
+
+    def render(self, item):
+        text = self.get(item)
+        if not text:
+            return ''
+        namespace = self.instance.namespace_templates
+
+        try:
+            if isinstance(text, list):
+                templates = (self._get_template(t) for t in text)
+                return [template.render(**namespace) for template in templates]
+            else:
+                template = self._get_template(text)
+                return template.render(**namespace)
+        except Exception as error:
+            raise TemplateError(error, text)
diff --git a/grc/core/blocks/block.py b/grc/core/blocks/block.py
new file mode 100644
index 0000000000..e6695083a1
--- /dev/null
+++ b/grc/core/blocks/block.py
@@ -0,0 +1,416 @@
+"""
+Copyright 2008-2015 Free Software Foundation, Inc.
+This file is part of GNU Radio
+
+GNU Radio Companion is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+GNU Radio Companion is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+"""
+
+from __future__ import absolute_import
+
+import ast
+import collections
+import itertools
+
+import six
+from six.moves import range
+
+from ._templates import MakoTemplates
+from ._flags import Flags
+
+from ..Constants import ADVANCED_PARAM_TAB
+from ..base import Element
+from ..utils.descriptors import lazy_property
+
+
+def _get_elem(iterable, key):
+    items = list(iterable)
+    for item in items:
+        if item.key == key:
+            return item
+    return ValueError('Key "{}" not found in {}.'.format(key, items))
+
+
+class Block(Element):
+
+    is_block = True
+
+    STATE_LABELS = ['disabled', 'enabled', 'bypassed']
+
+    key = ''
+    label = ''
+    category = ''
+    flags = Flags('')
+    documentation = {'': ''}
+
+    value = None
+    checks = []
+
+    templates = MakoTemplates()
+    parameters_data = []
+    inputs_data = []
+    outputs_data = []
+
+    extra_data = {}
+
+    # region Init
+    def __init__(self, parent):
+        """Make a new block from nested data."""
+        super(Block, self).__init__(parent)
+        self.params = self._init_params()
+        self.sinks = self._init_ports(self.inputs_data, direction='sink')
+        self.sources = self._init_ports(self.outputs_data, direction='source')
+
+        self.active_sources = []  # on rewrite
+        self.active_sinks = []  # on rewrite
+
+        self.states = {'state': True}
+
+    def _init_params(self):
+        is_dsp_block = not self.flags.not_dsp
+        has_inputs = bool(self.inputs_data)
+        has_outputs = bool(self.outputs_data)
+
+        params = collections.OrderedDict()
+        param_factory = self.parent_platform.make_param
+
+        def add_param(id, **kwargs):
+            params[id] = param_factory(self, id=id, **kwargs)
+
+        add_param(id='id', name='ID', dtype='id',
+                  hide='none' if (self.key == 'options' or self.is_variable) else 'part')
+
+        if is_dsp_block:
+            add_param(id='alias', name='Block Alias', dtype='string',
+                      hide='part', category=ADVANCED_PARAM_TAB)
+
+            if has_outputs or has_inputs:
+                add_param(id='affinity', name='Core Affinity', dtype='int_vector',
+                          hide='part', category=ADVANCED_PARAM_TAB)
+
+            if has_outputs:
+                add_param(id='minoutbuf', name='Min Output Buffer', dtype='int',
+                          hide='part', value='0', category=ADVANCED_PARAM_TAB)
+                add_param(id='maxoutbuf', name='Max Output Buffer', dtype='int',
+                          hide='part', value='0', category=ADVANCED_PARAM_TAB)
+
+        base_params_n = {}
+        for param_data in self.parameters_data:
+            param_id = param_data['id']
+            if param_id in params:
+                raise Exception('Param id "{}" is not unique'.format(param_id))
+
+            base_key = param_data.get('base_key', None)
+            param_data_ext = base_params_n.get(base_key, {}).copy()
+            param_data_ext.update(param_data)
+
+            add_param(**param_data_ext)
+            base_params_n[param_id] = param_data_ext
+
+        add_param(id='comment', name='Comment', dtype='_multiline', hide='part',
+                  value='', category=ADVANCED_PARAM_TAB)
+        return params
+
+    def _init_ports(self, ports_n, direction):
+        ports = []
+        port_factory = self.parent_platform.make_port
+        port_ids = set()
+
+        def make_stream_port_id(_pool=itertools.count()):
+            return {'sink': 'in', 'source': 'out'}[direction] + str(next(_pool))
+
+        for i, port_data in enumerate(ports_n):
+            port_id = port_data.setdefault('id', make_stream_port_id())
+            if port_id in port_ids:
+                raise Exception('Port id "{}" already exists in {}s'.format(port_id, direction))
+            port_ids.add(port_id)
+
+            port = port_factory(parent=self, direction=direction, **port_data)
+            ports.append(port)
+        return ports
+    # endregion
+
+    # region Rewrite_and_Validation
+    def rewrite(self):
+        """
+        Add and remove ports to adjust for the nports.
+        """
+        Element.rewrite(self)
+
+        def rekey(ports):
+            """Renumber non-message/message ports"""
+            domain_specific_port_index = collections.defaultdict(int)
+            for port in ports:
+                if not port.key.isdigit():
+                    continue
+                domain = port.domain
+                port.key = str(domain_specific_port_index[domain])
+                domain_specific_port_index[domain] += 1
+
+        # Adjust nports
+        for ports in (self.sources, self.sinks):
+            self._rewrite_nports(ports)
+            rekey(ports)
+
+        # disconnect hidden ports
+        self.parent_flowgraph.disconnect(*[p for p in self.ports() if p.hidden])
+
+        self.active_sources = [p for p in self.sources if not p.hidden]
+        self.active_sinks = [p for p in self.sinks if not p.hidden]
+
+    def _rewrite_nports(self, ports):
+        for port in ports:
+            if hasattr(port, 'master_port'):  # Not a master port and no left-over clones
+                continue
+            nports = port.multiplicity
+            for clone in port.clones[nports-1:]:
+                # Remove excess connections
+                self.parent_flowgraph.disconnect(clone)
+                port.remove_clone(clone)
+                ports.remove(clone)
+            # Add more cloned ports
+            for j in range(1 + len(port.clones), nports):
+                clone = port.add_clone()
+                ports.insert(ports.index(port) + j, clone)
+
+    def validate(self):
+        """
+        Validate this block.
+        Call the base class validate.
+        Evaluate the checks: each check must evaluate to True.
+        """
+        Element.validate(self)
+        self._run_checks()
+        self._validate_generate_mode_compat()
+        self._validate_var_value()
+
+    def _run_checks(self):
+        """Evaluate the checks"""
+        for check in self.checks:
+            try:
+                if not self.evaluate(check):
+                    self.add_error_message('Check "{}" failed.'.format(check))
+            except:
+                self.add_error_message('Check "{}" did not evaluate.'.format(check))
+
+    def _validate_generate_mode_compat(self):
+        """check if this is a GUI block and matches the selected generate option"""
+        current_generate_option = self.parent.get_option('generate_options')
+
+        def check_generate_mode(label, flag, valid_options):
+            block_requires_mode = (
+                flag in self.flags or self.label.upper().startswith(label)
+            )
+            if block_requires_mode and current_generate_option not in valid_options:
+                self.add_error_message("Can't generate this block in mode: {} ".format(
+                                       repr(current_generate_option)))
+
+        check_generate_mode('QT GUI', Flags.NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
+
+    def _validate_var_value(self):
+        """or variables check the value (only if var_value is used)"""
+        if self.is_variable and self.value != 'value':
+            try:
+                self.parent_flowgraph.evaluate(self.value, local_namespace=self.namespace)
+            except Exception as err:
+                self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(self.value, err))
+    # endregion
+
+    # region Properties
+
+    def __str__(self):
+        return 'Block - {} - {}({})'.format(self.name, self.label, self.key)
+
+    def __repr__(self):
+        try:
+            name = self.name
+        except Exception:
+            name = self.key
+        return 'block[' + name + ']'
+
+    @property
+    def name(self):
+        return self.params['id'].value
+
+    @lazy_property
+    def is_virtual_or_pad(self):
+        return self.key in ("virtual_source", "virtual_sink", "pad_source", "pad_sink")
+
+    @lazy_property
+    def is_variable(self):
+        return bool(self.value)
+
+    @lazy_property
+    def is_import(self):
+        return self.key == 'import'
+
+    @property
+    def comment(self):
+        return self.params['comment'].value
+
+    @property
+    def state(self):
+        """Gets the block's current state."""
+        state = self.states['state']
+        return state if state in self.STATE_LABELS else 'enabled'
+
+    @state.setter
+    def state(self, value):
+        """Sets the state for the block."""
+        self.states['state'] = value
+
+    # Enable/Disable Aliases
+    @property
+    def enabled(self):
+        """Get the enabled state of the block"""
+        return self.state != 'disabled'
+
+    # endregion
+
+    ##############################################
+    # Getters (old)
+    ##############################################
+    def get_var_make(self):
+        return self.templates.render('var_make')
+
+    def get_var_value(self):
+        return self.templates.render('var_value')
+
+    def get_callbacks(self):
+        """
+        Get a list of function callbacks for this block.
+
+        Returns:
+            a list of strings
+        """
+        def make_callback(callback):
+            if 'self.' in callback:
+                return callback
+            return 'self.{}.{}'.format(self.name, callback)
+        return [make_callback(c) for c in self.templates.render('callbacks')]
+
+    def is_virtual_sink(self):
+        return self.key == 'virtual_sink'
+
+    def is_virtual_source(self):
+        return self.key == 'virtual_source'
+
+    # Block bypassing
+    def get_bypassed(self):
+        """
+        Check if the block is bypassed
+        """
+        return self.state == 'bypassed'
+
+    def set_bypassed(self):
+        """
+        Bypass the block
+
+        Returns:
+            True if block chagnes state
+        """
+        if self.state != 'bypassed' and self.can_bypass():
+            self.state = '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.sources) != 1 or len(self.sinks) != 1:
+            return False
+        if not (self.sources[0].dtype == self.sinks[0].dtype):
+            return False
+        if self.flags.disable_bypass:
+            return False
+        return True
+
+    def ports(self):
+        return itertools.chain(self.sources, self.sinks)
+
+    def active_ports(self):
+        return itertools.chain(self.active_sources, self.active_sinks)
+
+    def children(self):
+        return itertools.chain(six.itervalues(self.params), self.ports())
+
+    ##############################################
+    # Access
+    ##############################################
+
+    def get_sink(self, key):
+        return _get_elem(self.sinks, key)
+
+    def get_source(self, key):
+        return _get_elem(self.sources, key)
+
+    ##############################################
+    # Resolve
+    ##############################################
+    @property
+    def namespace(self):
+        return {key: param.get_evaluated() for key, param in six.iteritems(self.params)}
+
+    @property
+    def namespace_templates(self):
+        return {key: param.template_arg for key, param in six.iteritems(self.params)}
+
+    def evaluate(self, expr):
+        return self.parent_flowgraph.evaluate(expr, self.namespace)
+
+    ##############################################
+    # Import/Export Methods
+    ##############################################
+    def export_data(self):
+        """
+        Export this block's params to nested data.
+
+        Returns:
+            a nested data odict
+        """
+        data = collections.OrderedDict()
+        if self.key != 'options':
+            data['name'] = self.name
+            data['id'] = self.key
+        data['parameters'] = collections.OrderedDict(sorted(
+            (param_id, param.value) for param_id, param in self.params.items()
+            if param_id != 'id'
+        ))
+        data['states'] = collections.OrderedDict(sorted(self.states.items()))
+        return data
+
+    def import_data(self, name, states, parameters, **_):
+        """
+        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.
+        """
+        self.params['id'].value = name
+        self.states.update(states)
+
+        def get_hash():
+            return hash(tuple(hash(v) for v in self.params.values()))
+
+        pre_rewrite_hash = -1
+        while pre_rewrite_hash != get_hash():
+            for key, value in six.iteritems(parameters):
+                try:
+                    self.params[key].set_value(value)
+                except KeyError:
+                    continue
+            # Store hash and call rewrite
+            pre_rewrite_hash = get_hash()
+            self.rewrite()
diff --git a/grc/core/blocks/dummy.py b/grc/core/blocks/dummy.py
new file mode 100644
index 0000000000..6a5ec2fa72
--- /dev/null
+++ b/grc/core/blocks/dummy.py
@@ -0,0 +1,54 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from . import Block, register_build_in
+
+
+@register_build_in
+class DummyBlock(Block):
+
+    is_dummy_block = True
+
+    label = 'Missing Block'
+    key = '_dummy'
+
+    def __init__(self, parent, missing_block_id, parameters, **_):
+        self.key = missing_block_id
+        super(DummyBlock, self).__init__(parent=parent)
+
+        param_factory = self.parent_platform.make_param
+        for param_id in parameters:
+            self.params.setdefault(param_id, param_factory(parent=self, id=param_id, dtype='string'))
+
+    def is_valid(self):
+        return False
+
+    @property
+    def enabled(self):
+        return False
+
+    def add_missing_port(self, port_id, direction):
+        port = self.parent_platform.make_port(
+            parent=self, direction=direction, id=port_id, name='?', dtype='',
+        )
+        if port.is_source:
+            self.sources.append(port)
+        else:
+            self.sinks.append(port)
+        return port
diff --git a/grc/core/blocks/embedded_python.py b/grc/core/blocks/embedded_python.py
new file mode 100644
index 0000000000..0b5a7a21c5
--- /dev/null
+++ b/grc/core/blocks/embedded_python.py
@@ -0,0 +1,242 @@
+# Copyright 2015-16 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 __future__ import absolute_import
+
+from ast import literal_eval
+from textwrap import dedent
+
+from . import Block, register_build_in
+from ._templates import MakoTemplates
+
+from .. import utils
+from ..base import Element
+
+
+DEFAULT_CODE = '''\
+"""
+Embedded Python Blocks:
+
+Each time this file is saved, GRC will instantiate the first class it finds
+to get ports and parameters of your block. The arguments to __init__  will
+be the parameters. All of them are required to have default values!
+"""
+
+import numpy as np
+from gnuradio import gr
+
+
+class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
+    """Embedded Python Block example - a simple multiply const"""
+
+    def __init__(self, example_param=1.0):  # only default arguments here
+        """arguments to this function show up as parameters in GRC"""
+        gr.sync_block.__init__(
+            self,
+            name='Embedded Python Block',   # will show up in GRC
+            in_sig=[np.complex64],
+            out_sig=[np.complex64]
+        )
+        # if an attribute with the same name as a parameter is found,
+        # a callback is registered (properties work, too).
+        self.example_param = example_param
+
+    def work(self, input_items, output_items):
+        """example: multiply with constant"""
+        output_items[0][:] = input_items[0] * self.example_param
+        return len(output_items[0])
+'''
+
+DOC = """
+This block represents an arbitrary GNU Radio Python Block.
+
+Its source code can be accessed through the parameter 'Code' which opens your editor. \
+Each time you save changes in the editor, GRC will update the block. \
+This includes the number, names and defaults of the parameters, \
+the ports (stream and message) and the block name and documentation.
+
+Block Documentation:
+(will be replaced the docstring of your block class)
+"""
+
+
+@register_build_in
+class EPyBlock(Block):
+
+    key = 'epy_block'
+    label = 'Python Block'
+    documentation = {'': DOC}
+
+    parameters_data = [dict(
+        label='Code',
+        id='_source_code',
+        dtype='_multiline_python_external',
+        value=DEFAULT_CODE,
+        hide='part',
+    )]
+    inputs_data = []
+    outputs_data = []
+
+    def __init__(self, flow_graph, **kwargs):
+        super(EPyBlock, self).__init__(flow_graph, **kwargs)
+        self.states['_io_cache'] = ''
+
+        self._epy_source_hash = -1
+        self._epy_reload_error = None
+
+    def rewrite(self):
+        Element.rewrite(self)
+
+        param_src = self.params['_source_code']
+
+        src = param_src.get_value()
+        src_hash = hash((self.name, src))
+        if src_hash == self._epy_source_hash:
+            return
+
+        try:
+            blk_io = utils.epy_block_io.extract(src)
+
+        except Exception as e:
+            self._epy_reload_error = ValueError(str(e))
+            try:  # Load last working block io
+                blk_io_args = literal_eval(self.states['_io_cache'])
+                if len(blk_io_args) == 6:
+                    blk_io_args += ([],)  # add empty callbacks
+                blk_io = utils.epy_block_io.BlockIO(*blk_io_args)
+            except Exception:
+                return
+        else:
+            self._epy_reload_error = None  # Clear previous errors
+            self.states['_io_cache'] = repr(tuple(blk_io))
+
+        # print "Rewriting embedded python block {!r}".format(self.name)
+        self._epy_source_hash = src_hash
+
+        self.label = blk_io.name or blk_io.cls
+        self.documentation = {'': blk_io.doc}
+
+        self.templates['imports'] = 'import ' + self.name
+        self.templates['make'] = '{mod}.{cls}({args})'.format(
+            mod=self.name,
+            cls=blk_io.cls,
+            args=', '.join('{0}=${{ {0} }}'.format(key) for key, _ in blk_io.params))
+        self.templates['callbacks'] = [
+            '{0} = ${{ {0} }}'.format(attr) for attr in blk_io.callbacks
+            ]
+
+        self._update_params(blk_io.params)
+        self._update_ports('in', self.sinks, blk_io.sinks, 'sink')
+        self._update_ports('out', self.sources, blk_io.sources, 'source')
+
+        super(EPyBlock, self).rewrite()
+
+    def _update_params(self, params_in_src):
+        param_factory = self.parent_platform.make_param
+        params = {}
+        for param in list(self.params):
+            if hasattr(param, '__epy_param__'):
+                params[param.key] = param
+                del self.params[param.key]
+
+        for id_, value in params_in_src:
+            try:
+                param = params[id_]
+                if param.default == param.value:
+                    param.set_value(value)
+                param.default = str(value)
+            except KeyError:  # need to make a new param
+                param = param_factory(
+                    parent=self,  id=id_, dtype='raw', value=value,
+                    name=id_.replace('_', ' ').title(),
+                )
+                setattr(param, '__epy_param__', True)
+            self.params[id_] = param
+
+    def _update_ports(self, label, ports, port_specs, direction):
+        port_factory = self.parent_platform.make_port
+        ports_to_remove = list(ports)
+        iter_ports = iter(ports)
+        ports_new = []
+        port_current = next(iter_ports, None)
+        for key, port_type, vlen in port_specs:
+            reuse_port = (
+                port_current is not None and
+                port_current.dtype == port_type and
+                port_current.vlen == vlen and
+                (key.isdigit() or port_current.key == key)
+            )
+            if reuse_port:
+                ports_to_remove.remove(port_current)
+                port, port_current = port_current, next(iter_ports, None)
+            else:
+                n = dict(name=label + str(key), dtype=port_type, id=key)
+                if port_type == 'message':
+                    n['name'] = key
+                    n['optional'] = '1'
+                if vlen > 1:
+                    n['vlen'] = str(vlen)
+                port = port_factory(self, direction=direction, **n)
+            ports_new.append(port)
+        # replace old port list with new one
+        del ports[:]
+        ports.extend(ports_new)
+        # remove excess port connections
+        self.parent_flowgraph.disconnect(*ports_to_remove)
+
+    def validate(self):
+        super(EPyBlock, self).validate()
+        if self._epy_reload_error:
+            self.params['_source_code'].add_error_message(str(self._epy_reload_error))
+
+
+@register_build_in
+class EPyModule(Block):
+    key = 'epy_module'
+    label = 'Python Module'
+    documentation = {'': dedent("""
+        This block lets you embed a python module in your flowgraph.
+
+        Code you put in this module is accessible in other blocks using the ID of this
+        block. Example:
+
+        If you put
+
+            a = 2
+
+            def double(arg):
+                return 2 * arg
+
+        in a Python Module Block with the ID 'stuff' you can use code like
+
+            stuff.a  # evals to 2
+            stuff.double(3)  # evals to 6
+
+        to set parameters of other blocks in your flowgraph.
+    """)}
+
+    parameters_data = [dict(
+        label='Code',
+        id='source_code',
+        dtype='_multiline_python_external',
+        value='# this module will be imported in the into your flowgraph',
+        hide='part',
+    )]
+
+    templates = MakoTemplates(
+        imports='import ${ id }  # embedded python module',
+    )
diff --git a/grc/core/blocks/virtual.py b/grc/core/blocks/virtual.py
new file mode 100644
index 0000000000..a10853ad1b
--- /dev/null
+++ b/grc/core/blocks/virtual.py
@@ -0,0 +1,76 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+import itertools
+
+from . import Block, register_build_in
+
+
+@register_build_in
+class VirtualSink(Block):
+    count = itertools.count()
+
+    key = 'virtual_sink'
+    label = 'Virtual Sink'
+    documentation = {'': ''}
+
+    parameters_data = [dict(
+        label='Stream ID',
+        id='stream_id',
+        dtype='stream_id',
+    )]
+    inputs_data = [dict(
+        domain='stream',
+        dtype=''
+    )]
+
+    def __init__(self, parent, **kwargs):
+        super(VirtualSink, self).__init__(parent, **kwargs)
+        self.params['id'].hide = 'all'
+
+    @property
+    def stream_id(self):
+        return self.params['stream_id'].value
+
+
+@register_build_in
+class VirtualSource(Block):
+    count = itertools.count()
+
+    key = 'virtual_source'
+    label = 'Virtual Source'
+    documentation = {'': ''}
+
+    parameters_data = [dict(
+        label='Stream ID',
+        id='stream_id',
+        dtype='stream_id',
+    )]
+    outputs_data = [dict(
+        domain='stream',
+        dtype=''
+    )]
+
+    def __init__(self, parent, **kwargs):
+        super(VirtualSource, self).__init__(parent, **kwargs)
+        self.params['id'].hide = 'all'
+
+    @property
+    def stream_id(self):
+        return self.params['stream_id'].value
diff --git a/grc/core/domain.dtd b/grc/core/domain.dtd
deleted file mode 100644
index b5b0b8bf39..0000000000
--- a/grc/core/domain.dtd
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
-Copyright 2014 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
--->
-<!ELEMENT domain (name, key, color?, multiple_sinks?, multiple_sources?, connection*)>
-<!--
-    Sub level elements.
- -->
-<!ELEMENT connection (source_domain, sink_domain, make)>
-<!--
-    Bottom level elements.
-    Character data only.
- -->
-<!ELEMENT name (#PCDATA)>
-<!ELEMENT key (#PCDATA)>
-<!ELEMENT multiple_sinks (#PCDATA)>
-<!ELEMENT multiple_sources (#PCDATA)>
-<!ELEMENT color (#PCDATA)>
-<!ELEMENT make (#PCDATA)>
-<!ELEMENT source_domain (#PCDATA)>
-<!ELEMENT sink_domain (#PCDATA)>
diff --git a/grc/core/errors.py b/grc/core/errors.py
new file mode 100644
index 0000000000..6437cc4fa1
--- /dev/null
+++ b/grc/core/errors.py
@@ -0,0 +1,30 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import, print_function
+
+
+class GRCError(Exception):
+    """Generic error class"""
+
+
+class BlockLoadError(GRCError):
+    """Error during block loading"""
+
+
+class TemplateError(BlockLoadError):
+    """Mako Template Error"""
diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py
index 23ccf95c4b..f438fa0d39 100644
--- a/grc/core/generator/FlowGraphProxy.py
+++ b/grc/core/generator/FlowGraphProxy.py
@@ -66,14 +66,15 @@ class FlowGraphProxy(object):  # TODO: move this in a refactored Generator
             self.get_pad_sinks() if direction in ('source', 'out') else []
         ports = []
         for pad in pads:
+            type_param = pad.params['type']
             master = {
-                'label': str(pad.get_param('label').get_evaluated()),
-                'type': str(pad.get_param('type').get_evaluated()),
-                'vlen': str(pad.get_param('vlen').get_value()),
-                'size': pad.get_param('type').opt_value('size'),
-                'optional': bool(pad.get_param('optional').get_evaluated()),
+                'label': str(pad.params['label'].get_evaluated()),
+                'type': str(pad.params['type'].get_evaluated()),
+                'vlen': str(pad.params['vlen'].get_value()),
+                'size':  type_param.options.attributes[type_param.get_value()]['size'],
+                'optional': bool(pad.params['optional'].get_evaluated()),
             }
-            num_ports = pad.get_param('num_streams').get_evaluated()
+            num_ports = pad.params['num_streams'].get_evaluated()
             if num_ports > 1:
                 for i in range(num_ports):
                     clone = master.copy()
@@ -91,7 +92,7 @@ class FlowGraphProxy(object):  # TODO: move this in a refactored Generator
             a list of pad source blocks in this flow graph
         """
         pads = [b for b in self.get_enabled_blocks() if b.key == 'pad_source']
-        return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
+        return sorted(pads, key=lambda x: x.name)
 
     def get_pad_sinks(self):
         """
@@ -101,7 +102,7 @@ class FlowGraphProxy(object):  # TODO: move this in a refactored Generator
             a list of pad sink blocks in this flow graph
         """
         pads = [b for b in self.get_enabled_blocks() if b.key == 'pad_sink']
-        return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
+        return sorted(pads, key=lambda x: x.name)
 
     def get_pad_port_global_key(self, port):
         """
@@ -116,15 +117,46 @@ class FlowGraphProxy(object):  # TODO: move this in a refactored Generator
         for pad in pads:
             # using the block param 'type' instead of the port domain here
             # to emphasize that hier block generation is domain agnostic
-            is_message_pad = pad.get_param('type').get_evaluated() == "message"
+            is_message_pad = pad.params['type'].get_evaluated() == "message"
             if port.parent == pad:
                 if is_message_pad:
-                    key = pad.get_param('label').get_value()
+                    key = pad.params['label'].get_value()
                 else:
                     key = str(key_offset + int(port.key))
                 return key
             else:
                 # assuming we have either only sources or sinks
                 if not is_message_pad:
-                    key_offset += len(pad.get_ports())
+                    key_offset += len(pad.sinks) + len(pad.sources)
         return -1
+
+
+def get_hier_block_io(flow_graph, direction, domain=None):
+    """
+    Get a list of io ports for this flow graph.
+
+    Returns a list of dicts with: type, label, vlen, size, optional
+    """
+    pads = flow_graph.get_pad_sources() if direction in ('sink', 'in') else \
+        flow_graph.get_pad_sinks() if direction in ('source', 'out') else []
+    ports = []
+    for pad in pads:
+        type_param = pad.params['type']
+        master = {
+            'label': str(pad.params['label'].get_evaluated()),
+            'type': str(pad.params['type'].get_evaluated()),
+            'vlen': str(pad.params['vlen'].get_value()),
+            'size':  type_param.options.attributes[type_param.get_value()]['size'],
+            'optional': bool(pad.params['optional'].get_evaluated()),
+        }
+        num_ports = pad.params['num_streams'].get_evaluated()
+        if num_ports > 1:
+            for i in range(num_ports):
+                clone = master.copy()
+                clone['label'] += str(i)
+                ports.append(clone)
+        else:
+            ports.append(master)
+    if domain is not None:
+        ports = [p for p in ports if p.domain == domain]
+    return ports
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index 316ed5014d..62dc26b8a8 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -18,25 +18,16 @@
 
 from __future__ import absolute_import
 
-import codecs
 import os
-import tempfile
-import operator
-import collections
 
-from Cheetah.Template import Template
-import six
+from mako.template import Template
 
-from .FlowGraphProxy import FlowGraphProxy
-from .. import ParseXML, Messages
-from ..Constants import (
-    TOP_BLOCK_FILE_MODE, BLOCK_FLAG_NEED_QT_GUI,
-    HIER_BLOCK_FILE_MODE, BLOCK_DTD
-)
-from ..utils import expr_utils
+from .hier_block import HierBlockGenerator, QtHierBlockGenerator
+from .top_block import TopBlockGenerator
 
 DATA_DIR = os.path.dirname(__file__)
-FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl')
+FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.py.mako')
+flow_graph_template = Template(filename=FLOW_GRAPH_TEMPLATE)
 
 
 class Generator(object):
@@ -64,338 +55,3 @@ class Generator(object):
     def __getattr__(self, item):
         """get all other attrib from actual generator object"""
         return getattr(self._generator, item)
-
-
-class TopBlockGenerator(object):
-
-    def __init__(self, flow_graph, file_path):
-        """
-        Initialize the top block generator object.
-
-        Args:
-            flow_graph: the flow graph object
-            file_path: the path to write the file to
-        """
-        self._flow_graph = FlowGraphProxy(flow_graph)
-        self._generate_options = self._flow_graph.get_option('generate_options')
-        self._mode = TOP_BLOCK_FILE_MODE
-        dirname = os.path.dirname(file_path)
-        # Handle the case where the directory is read-only
-        # In this case, use the system's temp directory
-        if not os.access(dirname, os.W_OK):
-            dirname = tempfile.gettempdir()
-        filename = self._flow_graph.get_option('id') + '.py'
-        self.file_path = os.path.join(dirname, filename)
-        self._dirname = dirname
-
-    def write(self):
-        """generate output and write it to files"""
-        # Do throttle warning
-        throttling_blocks = [b for b in self._flow_graph.get_enabled_blocks()
-                             if b.is_throtteling]
-        if not throttling_blocks and not self._generate_options.startswith('hb'):
-            Messages.send_warning("This flow graph may not have flow control: "
-                                  "no audio or RF hardware blocks found. "
-                                  "Add a Misc->Throttle block to your flow "
-                                  "graph to avoid CPU congestion.")
-        if len(throttling_blocks) > 1:
-            keys = set([b.key for b in throttling_blocks])
-            if len(keys) > 1 and 'blocks_throttle' in keys:
-                Messages.send_warning("This flow graph contains a throttle "
-                                      "block and another rate limiting block, "
-                                      "e.g. a hardware source or sink. "
-                                      "This is usually undesired. Consider "
-                                      "removing the throttle block.")
-        # Generate
-        for filename, data in self._build_python_code_from_template():
-            with codecs.open(filename, 'w', encoding='utf-8') as fp:
-                fp.write(data)
-            if filename == self.file_path:
-                try:
-                    os.chmod(filename, self._mode)
-                except:
-                    pass
-
-    def _build_python_code_from_template(self):
-        """
-        Convert the flow graph to python code.
-
-        Returns:
-            a string of python code
-        """
-        output = list()
-
-        fg = self._flow_graph
-        title = fg.get_option('title') or fg.get_option('id').replace('_', ' ').title()
-        imports = fg.get_imports()
-        variables = fg.get_variables()
-        parameters = fg.get_parameters()
-        monitors = fg.get_monitors()
-
-        # List of blocks not including variables and imports and parameters and disabled
-        def _get_block_sort_text(block):
-            code = block.get_make().replace(block.get_id(), ' ')
-            try:
-                code += block.get_param('gui_hint').get_value()  # Newer gui markup w/ qtgui
-            except:
-                pass
-            return code
-
-        blocks_all = expr_utils.sort_objects(
-            [b for b in fg.blocks if b.enabled and not b.get_bypassed()],
-            operator.methodcaller('get_id'), _get_block_sort_text
-        )
-        deprecated_block_keys = set(b.name for b in blocks_all if b.is_deprecated)
-        for key in deprecated_block_keys:
-            Messages.send_warning("The block {!r} is deprecated.".format(key))
-
-        # List of regular blocks (all blocks minus the special ones)
-        blocks = [b for b in blocks_all if b not in imports and b not in parameters]
-
-        for block in blocks:
-            key = block.key
-            file_path = os.path.join(self._dirname, block.get_id() + '.py')
-            if key == 'epy_block':
-                src = block.get_param('_source_code').get_value()
-                output.append((file_path, src))
-            elif key == 'epy_module':
-                src = block.get_param('source_code').get_value()
-                output.append((file_path, src))
-
-        # Filter out bus and virtual sink connections
-        connections = [con for con in fg.get_enabled_connections()
-                       if not (con.is_bus() or con.sink_block.is_virtual_sink())]
-
-        # Get the virtual blocks and resolve their connections
-        connection_factory = fg.parent_platform.Connection
-        virtual = [c for c in connections if c.source_block.is_virtual_source()]
-        for connection in virtual:
-            sink = connection.sink_port
-            for source in connection.source_port.resolve_virtual_source():
-                resolved = connection_factory(fg.orignal_flowgraph, source, sink)
-                connections.append(resolved)
-            # Remove the virtual connection
-            connections.remove(connection)
-
-        # Bypassing blocks: Need to find all the enabled connections for the block using
-        # the *connections* object rather than get_connections(). Create new connections
-        # that bypass the selected block and remove the existing ones. This allows adjacent
-        # bypassed blocks to see the newly created connections to downstream blocks,
-        # allowing them to correctly construct bypass connections.
-        bypassed_blocks = fg.get_bypassed_blocks()
-        for block in bypassed_blocks:
-            # Get the upstream connection (off of the sink ports)
-            # Use *connections* not get_connections()
-            source_connection = [c for c in connections if c.sink_port == block.sinks[0]]
-            # The source connection should never have more than one element.
-            assert (len(source_connection) == 1)
-
-            # Get the source of the connection.
-            source_port = source_connection[0].source_port
-
-            # Loop through all the downstream connections
-            for sink in (c for c in connections if c.source_port == block.sources[0]):
-                if not sink.enabled:
-                    # Ignore disabled connections
-                    continue
-                connection = connection_factory(fg.orignal_flowgraph, source_port, sink.sink_port)
-                connections.append(connection)
-                # Remove this sink connection
-                connections.remove(sink)
-            # Remove the source connection
-            connections.remove(source_connection[0])
-
-        # List of connections where each endpoint is enabled (sorted by domains, block names)
-        connections.sort(key=lambda c: (
-            c.source_port.domain, c.sink_port.domain,
-            c.source_block.get_id(), c.sink_block.get_id()
-        ))
-
-        connection_templates = fg.parent.connection_templates
-
-        # List of variable names
-        var_ids = [var.get_id() for var in parameters + variables]
-        replace_dict = dict((var_id, 'self.' + var_id) for var_id in var_ids)
-        callbacks_all = []
-        for block in blocks_all:
-            callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_callbacks())
-
-        # Map var id to callbacks
-        def uses_var_id():
-            used = expr_utils.get_variable_dependencies(callback, [var_id])
-            return used and 'self.' + var_id in callback  # callback might contain var_id itself
-
-        callbacks = {}
-        for var_id in var_ids:
-            callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id()]
-
-        # Load the namespace
-        namespace = {
-            'title': title,
-            'imports': imports,
-            'flow_graph': fg,
-            'variables': variables,
-            'parameters': parameters,
-            'monitors': monitors,
-            'blocks': blocks,
-            'connections': connections,
-            'connection_templates': connection_templates,
-            'generate_options': self._generate_options,
-            'callbacks': callbacks,
-        }
-        # Build the template
-        t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace)
-        output.append((self.file_path, "\n".join(line.rstrip() for line in str(t).split("\n"))))
-        return output
-
-
-class HierBlockGenerator(TopBlockGenerator):
-    """Extends the top block generator to also generate a block XML file"""
-
-    def __init__(self, flow_graph, file_path):
-        """
-        Initialize the hier block generator object.
-
-        Args:
-            flow_graph: the flow graph object
-            file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR)
-        """
-        TopBlockGenerator.__init__(self, flow_graph, file_path)
-        platform = flow_graph.parent
-
-        hier_block_lib_dir = platform.config.hier_block_lib_dir
-        if not os.path.exists(hier_block_lib_dir):
-            os.mkdir(hier_block_lib_dir)
-
-        self._mode = HIER_BLOCK_FILE_MODE
-        self.file_path = os.path.join(hier_block_lib_dir, self._flow_graph.get_option('id') + '.py')
-        self.file_path_xml = self.file_path + '.xml'
-
-    def write(self):
-        """generate output and write it to files"""
-        TopBlockGenerator.write(self)
-        ParseXML.to_file(self._build_block_n_from_flow_graph_io(), self.file_path_xml)
-        ParseXML.validate_dtd(self.file_path_xml, BLOCK_DTD)
-        try:
-            os.chmod(self.file_path_xml, self._mode)
-        except:
-            pass
-
-    def _build_block_n_from_flow_graph_io(self):
-        """
-        Generate a block XML nested data from the flow graph IO
-
-        Returns:
-            a xml node tree
-        """
-        # Extract info from the flow graph
-        block_key = self._flow_graph.get_option('id')
-        parameters = self._flow_graph.get_parameters()
-
-        def var_or_value(name):
-            if name in (p.get_id() for p in parameters):
-                return "$" + name
-            return name
-
-        # Build the nested data
-        block_n = collections.OrderedDict()
-        block_n['name'] = self._flow_graph.get_option('title') or \
-            self._flow_graph.get_option('id').replace('_', ' ').title()
-        block_n['key'] = block_key
-        block_n['category'] = self._flow_graph.get_option('category')
-        block_n['import'] = "from {0} import {0}  # grc-generated hier_block".format(
-            self._flow_graph.get_option('id'))
-        # Make data
-        if parameters:
-            block_n['make'] = '{cls}(\n    {kwargs},\n)'.format(
-                cls=block_key,
-                kwargs=',\n    '.join(
-                    '{key}=${key}'.format(key=param.get_id()) for param in parameters
-                ),
-            )
-        else:
-            block_n['make'] = '{cls}()'.format(cls=block_key)
-        # Callback data
-        block_n['callback'] = [
-            'set_{key}(${key})'.format(key=param.get_id()) for param in parameters
-        ]
-
-        # Parameters
-        block_n['param'] = list()
-        for param in parameters:
-            param_n = collections.OrderedDict()
-            param_n['name'] = param.get_param('label').get_value() or param.get_id()
-            param_n['key'] = param.get_id()
-            param_n['value'] = param.get_param('value').get_value()
-            param_n['type'] = 'raw'
-            param_n['hide'] = param.get_param('hide').get_value()
-            block_n['param'].append(param_n)
-
-        # Bus stuff
-        if self._flow_graph.get_bussink():
-            block_n['bus_sink'] = '1'
-        if self._flow_graph.get_bussrc():
-            block_n['bus_source'] = '1'
-
-        # Sink/source ports
-        for direction in ('sink', 'source'):
-            block_n[direction] = list()
-            for port in self._flow_graph.get_hier_block_io(direction):
-                port_n = collections.OrderedDict()
-                port_n['name'] = port['label']
-                port_n['type'] = port['type']
-                if port['type'] != "message":
-                    port_n['vlen'] = var_or_value(port['vlen'])
-                if port['optional']:
-                    port_n['optional'] = '1'
-                block_n[direction].append(port_n)
-
-        # More bus stuff
-        bus_struct_sink = self._flow_graph.get_bus_structure_sink()
-        if bus_struct_sink:
-            block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value()
-        bus_struct_src = self._flow_graph.get_bus_structure_src()
-        if bus_struct_src:
-            block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value()
-
-        # Documentation
-        block_n['doc'] = "\n".join(field for field in (
-            self._flow_graph.get_option('author'),
-            self._flow_graph.get_option('description'),
-            self.file_path
-        ) if field)
-        block_n['grc_source'] = str(self._flow_graph.grc_file_path)
-
-        n = {'block': block_n}
-        return n
-
-
-class QtHierBlockGenerator(HierBlockGenerator):
-
-    def _build_block_n_from_flow_graph_io(self):
-        n = HierBlockGenerator._build_block_n_from_flow_graph_io(self)
-        block_n = collections.OrderedDict()
-
-        # insert flags after category
-        for key, value in six.iteritems(n['block']):
-            block_n[key] = value
-            if key == 'category':
-                block_n['flags'] = BLOCK_FLAG_NEED_QT_GUI
-
-        if not block_n['name'].upper().startswith('QT GUI'):
-            block_n['name'] = 'QT GUI ' + block_n['name']
-
-        gui_hint_param = collections.OrderedDict()
-        gui_hint_param['name'] = 'GUI Hint'
-        gui_hint_param['key'] = 'gui_hint'
-        gui_hint_param['value'] = ''
-        gui_hint_param['type'] = 'gui_hint'
-        gui_hint_param['hide'] = 'part'
-        block_n['param'].append(gui_hint_param)
-
-        block_n['make'] += (
-            "\n#set $win = 'self.%s' % $id"
-            "\n${gui_hint()($win)}"
-        )
-
-        return {'block': block_n}
diff --git a/grc/core/generator/flow_graph.py.mako b/grc/core/generator/flow_graph.py.mako
new file mode 100644
index 0000000000..484441f00f
--- /dev/null
+++ b/grc/core/generator/flow_graph.py.mako
@@ -0,0 +1,352 @@
+% if not generate_options.startswith('hb'):
+#!/usr/bin/env python2
+% endif
+# -*- coding: utf-8 -*-
+<%def name="indent(code)">${ '\n        '.join(str(code).splitlines()) }</%def>
+"""
+GNU Radio Python Flow Graph
+
+Title: ${title}
+% if flow_graph.get_option('author'):
+Author: ${flow_graph.get_option('author')}
+% endif
+% if flow_graph.get_option('description'):
+Description: ${flow_graph.get_option('description')}
+% endif
+Generated: ${ generated_time }
+"""
+
+% if generate_options == 'qt_gui':
+from distutils.version import StrictVersion
+
+if __name__ == '__main__':
+    import ctypes
+    import sys
+    if sys.platform.startswith('linux'):
+        try:
+            x11 = ctypes.cdll.LoadLibrary('libX11.so')
+            x11.XInitThreads()
+        except:
+            print "Warning: failed to XInitThreads()"
+
+% endif
+########################################################
+##Create Imports
+########################################################
+% for imp in imports:
+##${imp.replace("  # grc-generated hier_block", "")}
+${imp}
+% endfor
+########################################################
+##Create Class
+##  Write the class declaration for a top or hier block.
+##  The parameter names are the arguments to __init__.
+##  Setup the IO signature (hier block only).
+########################################################
+<%
+    class_name = flow_graph.get_option('id')
+    param_str = ', '.join(['self'] + ['%s=%s'%(param.name, param.templates.render('make')) for param in parameters])
+%>\
+% if generate_options == 'qt_gui':
+from gnuradio import qtgui
+
+class ${class_name}(gr.top_block, Qt.QWidget):
+
+    def __init__(${param_str}):
+        gr.top_block.__init__(self, "${title}")
+        Qt.QWidget.__init__(self)
+        self.setWindowTitle("${title}")
+        qtgui.util.check_set_qss()
+        try:
+            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
+        except:
+            pass
+        self.top_scroll_layout = Qt.QVBoxLayout()
+        self.setLayout(self.top_scroll_layout)
+        self.top_scroll = Qt.QScrollArea()
+        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
+        self.top_scroll_layout.addWidget(self.top_scroll)
+        self.top_scroll.setWidgetResizable(True)
+        self.top_widget = Qt.QWidget()
+        self.top_scroll.setWidget(self.top_widget)
+        self.top_layout = Qt.QVBoxLayout(self.top_widget)
+        self.top_grid_layout = Qt.QGridLayout()
+        self.top_layout.addLayout(self.top_grid_layout)
+
+        self.settings = Qt.QSettings("GNU Radio", "${class_name}")
+
+        try:
+            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
+                self.restoreGeometry(self.settings.value("geometry").toByteArray())
+            else:
+                self.restoreGeometry(self.settings.value("geometry"))
+        except:
+            pass
+% elif generate_options == 'no_gui':
+
+class ${class_name}(gr.top_block):
+
+    def __init__(${param_str}):
+        gr.top_block.__init__(self, "${title}")
+% elif generate_options.startswith('hb'):
+    <% in_sigs = flow_graph.get_hier_block_stream_io('in') %>
+    <% out_sigs = flow_graph.get_hier_block_stream_io('out') %>
+
+
+% if generate_options == 'hb_qt_gui':
+class ${class_name}(gr.hier_block2, Qt.QWidget):
+% else:
+class ${class_name}(gr.hier_block2):
+% endif
+<%def name="make_io_sig(io_sigs)">
+    <% size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in io_sigs] %>
+    % if len(io_sigs) == 0:
+gr.io_signature(0, 0, 0)\
+    #elif len(${io_sigs}) == 1
+gr.io_signature(1, 1, ${size_strs[0]})
+    % else:
+gr.io_signaturev(${len(io_sigs)}, ${len(io_sigs)}, [${', '.join(ize_strs)}])
+    % endif
+</%def>
+
+    def __init__(${param_str}):
+        gr.hier_block2.__init__(
+            self, "${ title }",
+            ${make_io_sig(in_sigs)},
+            ${make_io_sig(out_sigs)},
+        )
+    % for pad in flow_graph.get_hier_block_message_io('in'):
+        self.message_port_register_hier_in("${ pad['label'] }")
+    % endfor
+    % for pad in flow_graph.get_hier_block_message_io('out'):
+        self.message_port_register_hier_out("${ pad['label'] }")
+    % endfor
+    % if generate_options == 'hb_qt_gui':
+
+        Qt.QWidget.__init__(self)
+        self.top_layout = Qt.QVBoxLayout()
+        self.top_grid_layout = Qt.QGridLayout()
+        self.top_layout.addLayout(self.top_grid_layout)
+        self.setLayout(self.top_layout)
+    % endif
+% endif
+% if flow_graph.get_option('thread_safe_setters'):
+
+        self._lock = threading.RLock()
+% endif
+########################################################
+##Create Parameters
+##  Set the parameter to a property of self.
+########################################################
+% if parameters:
+
+        ${'##################################################'}
+        # Parameters
+        ${'##################################################'}
+% endif
+% for param in parameters:
+        ${indent(param.get_var_make())}
+% endfor
+########################################################
+##Create Variables
+########################################################
+% if variables:
+
+        ${'##################################################'}
+        # Variables
+        ${'##################################################'}
+% endif
+% for var in variables:
+        ${indent(var.templates.render('var_make'))}
+% endfor
+        % if blocks:
+
+        ${'##################################################'}
+        # Blocks
+        ${'##################################################'}
+        % endif
+        % for blk, blk_make in blocks:
+        ${ indent(blk_make.strip('\n')) }
+##         % if 'alias' in blk.params and blk.params['alias'].get_evaluated():
+##         (self.${blk.name}).set_block_alias("${blk.params['alias'].get_evaluated()}")
+##         % endif
+##         % if 'affinity' in blk.params and blk.params['affinity'].get_evaluated():
+##         (self.${blk.name}).set_processor_affinity(${blk.params['affinity'].get_evaluated()})
+##         % endif
+##         % if len(blk.sources) > 0 and 'minoutbuf' in blk.params and int(blk.params['minoutbuf'].get_evaluated()) > 0:
+##         (self.${blk.name}).set_min_output_buffer(${blk.params['minoutbuf'].get_evaluated()})
+##         % endif
+##         % if len(blk.sources) > 0 and 'maxoutbuf' in blk.params and int(blk.params['maxoutbuf'].get_evaluated()) > 0:
+##         (self.${blk.name}).set_max_output_buffer(${blk.params['maxoutbuf'].get_evaluated()})
+##         % endif
+        % endfor
+        % if connections:
+
+        ${'##################################################'}
+        # Connections
+        ${'##################################################'}
+        % for connection in connections:
+        ${ connection.rstrip() }
+        % endfor
+        % endif
+########################################################
+## QT sink close method reimplementation
+########################################################
+% if generate_options == 'qt_gui':
+
+    def closeEvent(self, event):
+        self.settings = Qt.QSettings("GNU Radio", "${class_name}")
+        self.settings.setValue("geometry", self.saveGeometry())
+        event.accept()
+    % if flow_graph.get_option('qt_qss_theme'):
+
+    def setStyleSheetFromFile(self, filename):
+        try:
+            if not os.path.exists(filename):
+                filename = os.path.join(
+                    gr.prefix(), "share", "gnuradio", "themes", filename)
+            with open(filename) as ss:
+                self.setStyleSheet(ss.read())
+        except Exception as e:
+            print >> sys.stderr, e
+    % endif
+% endif
+##
+##
+##
+## Create Callbacks
+## Write a set method for this variable that calls the callbacks
+########################################################
+    % for var in parameters + variables:
+
+    def get_${ var.name }(self):
+        return self.${ var.name }
+
+    def set_${ var.name }(self, ${ var.name }):
+        % if flow_graph.get_option('thread_safe_setters'):
+        with self._lock:
+            self.${ var.name } = ${ var.name }
+            % for callback in callbacks[var.name]:
+            ${ indent(callback) }
+            % endfor
+        % else:
+        self.${ var.name } = ${ var.name }
+        % for callback in callbacks[var.name]:
+        ${ indent(callback) }
+        % endfor
+        % endif
+    % endfor
+########################################################
+##Create Main
+##  For top block code, generate a main routine.
+##  Instantiate the top block and run as gui or cli.
+########################################################
+<%def name="make_default(type_, param)">
+    % if type_ == 'eng_float':
+eng_notation.num_to_str(${param.templates.render('make')})
+    % else:
+${param.templates.render('make')}
+    % endif
+</%def>\
+% if not generate_options.startswith('hb'):
+<% params_eq_list = list() %>
+% if parameters:
+
+<% arg_parser_args = '' %>\
+def argument_parser():
+    % if flow_graph.get_option('description'):
+    <%
+        arg_parser_args = 'description=description'
+    %>description = ${repr(flow_graph.get_option('description'))}
+    % endif
+    parser = ArgumentParser(${arg_parser_args})
+    % for param in parameters:
+<%
+        switches = ['"--{}"'.format(param.name.replace('_', '-'))]
+        short_id = param.params['short_id'].get_value()
+        if short_id:
+            switches.insert(0, '"-{}"'.format(short_id))
+
+        type_ = param.params['type'].get_value()
+        if type_:
+            params_eq_list.append('%s=options.%s' % (param.name, param.name))
+
+        default = param.templates.render('make')
+        if type_ == 'eng_float':
+            default = eng_notation.num_to_str(default)
+        # FIXME:
+        if type_ == 'string':
+            type_ = 'str'
+    %>\
+    % if type_:
+    parser.add_argument(
+        ${ ', '.join(switches) }, dest="${param.name}", type=${type_}, default=${ default },
+        help="Set ${param.params['label'].get_evaluated() or param.name} [default=%(default)r]")
+    % endif
+    % endfor
+    return parser
+% endif
+
+
+def main(top_block_cls=${class_name}, options=None):
+    % if parameters:
+    if options is None:
+        options = argument_parser().parse_args()
+    % endif
+    % if flow_graph.get_option('realtime_scheduling'):
+    if gr.enable_realtime_scheduling() != gr.RT_OK:
+        print "Error: failed to enable real-time scheduling."
+    % endif
+    % if generate_options == 'qt_gui':
+
+    if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
+        style = gr.prefs().get_string('qtgui', 'style', 'raster')
+        Qt.QApplication.setGraphicsSystem(style)
+    qapp = Qt.QApplication(sys.argv)
+
+    tb = top_block_cls(${ ', '.join(params_eq_list) })
+    % if flow_graph.get_option('run'):
+    tb.start(${flow_graph.get_option('max_nouts') or ''})
+    % endif
+    % if flow_graph.get_option('qt_qss_theme'):
+    tb.setStyleSheetFromFile(${ flow_graph.get_option('qt_qss_theme') })
+    % endif
+    tb.show()
+
+    def quitting():
+        tb.stop()
+        tb.wait()
+    qapp.aboutToQuit.connect(quitting)
+    % for m in monitors:
+    if 'en' in m.params:
+        if m.params['en'].get_value():
+            (tb.${m.name}).start()
+    else:
+        sys.stderr.write("Monitor '{0}' does not have an enable ('en') parameter.".format("tb.${m.name}"))
+    % endfor
+    qapp.exec_()
+    % elif generate_options == 'no_gui':
+    tb = top_block_cls(${ ', '.join(params_eq_list) })
+    % if flow_graph.get_option('run_options') == 'prompt':
+    tb.start(${ flow_graph.get_option('max_nouts') or '' })
+    % for m in monitors:
+    (tb.${m.name}).start()
+    % endfor
+    try:
+        raw_input('Press Enter to quit: ')
+    except EOFError:
+        pass
+    tb.stop()
+    % elif flow_graph.get_option('run_options') == 'run':
+    tb.start(${flow_graph.get_option('max_nouts') or ''})
+    % endif
+    % for m in monitors:
+    (tb.${m.name}).start()
+    % endfor
+    tb.wait()
+    % endif
+
+
+if __name__ == '__main__':
+    main()
+% endif
diff --git a/grc/core/generator/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl
deleted file mode 100644
index 202362c925..0000000000
--- a/grc/core/generator/flow_graph.tmpl
+++ /dev/null
@@ -1,420 +0,0 @@
-#if not $generate_options.startswith('hb')
-#!/usr/bin/env python2
-#end if
-# -*- coding: utf-8 -*-
-########################################################
-##Cheetah template - gnuradio_python
-##
-##@param imports the import statements
-##@param flow_graph the flow_graph
-##@param variables the variable blocks
-##@param parameters the parameter blocks
-##@param blocks the signal blocks
-##@param connections the connections
-##@param generate_options the type of flow graph
-##@param callbacks variable id map to callback strings
-########################################################
-#def indent($code)
-#set $code = '\n        '.join(str($code).splitlines())
-$code#slurp
-#end def
-#import time
-#set $DIVIDER = '#'*50
-$DIVIDER
-# GNU Radio Python Flow Graph
-# Title: $title
-#if $flow_graph.get_option('author')
-# Author: $flow_graph.get_option('author')
-#end if
-#if $flow_graph.get_option('description')
-# Description: $flow_graph.get_option('description')
-#end if
-# Generated: $time.ctime()
-$DIVIDER
-#if $flow_graph.get_option('thread_safe_setters')
-import threading
-#end if
-
-#if $generate_options == 'qt_gui'
-from distutils.version import StrictVersion
-#end if
-
-## Call XInitThreads as the _very_ first thing.
-## After some Qt import, it's too late
-#if $generate_options == 'qt_gui'
-if __name__ == '__main__':
-    import ctypes
-    import sys
-    if sys.platform.startswith('linux'):
-        try:
-            x11 = ctypes.cdll.LoadLibrary('libX11.so')
-            x11.XInitThreads()
-        except:
-            print "Warning: failed to XInitThreads()"
-
-#end if
-#
-########################################################
-##Create Imports
-########################################################
-#if $flow_graph.get_option('qt_qss_theme')
-#set imports = $sorted(set($imports + ["import os", "import sys"]))
-#end if
-#if any(imp.endswith("# grc-generated hier_block") for imp in $imports)
-import os
-import sys
-#set imports = $filter(lambda i: i not in ("import os", "import sys"), $imports)
-sys.path.append(os.environ.get('GRC_HIER_PATH', os.path.expanduser('~/.grc_gnuradio')))
-
-#end if
-#for $imp in $imports
-##$(imp.replace("  # grc-generated hier_block", ""))
-$imp
-#end for
-########################################################
-##Create Class
-##  Write the class declaration for a top or hier block.
-##  The parameter names are the arguments to __init__.
-##  Setup the IO signature (hier block only).
-########################################################
-#set $class_name = $flow_graph.get_option('id')
-#set $param_str = ', '.join(['self'] + ['%s=%s'%(param.get_id(), param.get_make()) for param in $parameters])
-#if $generate_options == 'qt_gui'
-from gnuradio import qtgui
-
-class $(class_name)(gr.top_block, Qt.QWidget):
-
-    def __init__($param_str):
-        gr.top_block.__init__(self, "$title")
-        Qt.QWidget.__init__(self)
-        self.setWindowTitle("$title")
-        qtgui.util.check_set_qss()
-        try:
-            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
-        except:
-            pass
-        self.top_scroll_layout = Qt.QVBoxLayout()
-        self.setLayout(self.top_scroll_layout)
-        self.top_scroll = Qt.QScrollArea()
-        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
-        self.top_scroll_layout.addWidget(self.top_scroll)
-        self.top_scroll.setWidgetResizable(True)
-        self.top_widget = Qt.QWidget()
-        self.top_scroll.setWidget(self.top_widget)
-        self.top_layout = Qt.QVBoxLayout(self.top_widget)
-        self.top_grid_layout = Qt.QGridLayout()
-        self.top_layout.addLayout(self.top_grid_layout)
-
-        self.settings = Qt.QSettings("GNU Radio", "$class_name")
-
-        try:
-            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
-                self.restoreGeometry(self.settings.value("geometry").toByteArray())
-            else:
-                self.restoreGeometry(self.settings.value("geometry"))
-        except:
-            pass
-#elif $generate_options == 'no_gui'
-
-
-class $(class_name)(gr.top_block):
-
-    def __init__($param_str):
-        gr.top_block.__init__(self, "$title")
-#elif $generate_options.startswith('hb')
-    #set $in_sigs = $flow_graph.get_hier_block_stream_io('in')
-    #set $out_sigs = $flow_graph.get_hier_block_stream_io('out')
-
-
-#if $generate_options == 'hb_qt_gui'
-class $(class_name)(gr.hier_block2, Qt.QWidget):
-#else
-class $(class_name)(gr.hier_block2):
-#end if
-#def make_io_sig($io_sigs)
-    #set $size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in $io_sigs]
-    #if len($io_sigs) == 0
-gr.io_signature(0, 0, 0)#slurp
-    #elif len($io_sigs) == 1
-gr.io_signature(1, 1, $size_strs[0])#slurp
-    #else
-gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])#slurp
-    #end if
-#end def
-
-    def __init__($param_str):
-        gr.hier_block2.__init__(
-            self, "$title",
-            $make_io_sig($in_sigs),
-            $make_io_sig($out_sigs),
-        )
-    #for $pad in $flow_graph.get_hier_block_message_io('in')
-        self.message_port_register_hier_in("$pad['label']")
-    #end for
-    #for $pad in $flow_graph.get_hier_block_message_io('out')
-        self.message_port_register_hier_out("$pad['label']")
-    #end for
-    #if $generate_options == 'hb_qt_gui'
-
-        Qt.QWidget.__init__(self)
-        self.top_layout = Qt.QVBoxLayout()
-        self.top_grid_layout = Qt.QGridLayout()
-        self.top_layout.addLayout(self.top_grid_layout)
-        self.setLayout(self.top_layout)
-    #end if
-#end if
-#if $flow_graph.get_option('thread_safe_setters')
-
-        self._lock = threading.RLock()
-#end if
-########################################################
-##Create Parameters
-##  Set the parameter to a property of self.
-########################################################
-#if $parameters
-
-        $DIVIDER
-        # Parameters
-        $DIVIDER
-#end if
-#for $param in $parameters
-        $indent($param.get_var_make())
-#end for
-########################################################
-##Create Variables
-########################################################
-#if $variables
-
-        $DIVIDER
-        # Variables
-        $DIVIDER
-#end if
-#for $var in $variables
-        $indent($var.get_var_make())
-#end for
-########################################################
-##Create Blocks
-########################################################
-#if $blocks
-
-        $DIVIDER
-        # Blocks
-        $DIVIDER
-#end if
-#for $blk in filter(lambda b: b.get_make(), $blocks)
-    #if $blk in $variables
-        $indent($blk.get_make())
-    #else
-        self.$blk.get_id() = $indent($blk.get_make())
-        #if 'alias' in $blk.params and $blk.params['alias'].get_evaluated()
-        (self.$blk.get_id()).set_block_alias("$blk.params['alias'].get_evaluated()")
-        #end if
-        #if 'affinity' in $blk.params and $blk.params['affinity'].get_evaluated()
-        (self.$blk.get_id()).set_processor_affinity($blk.params['affinity'].get_evaluated())
-        #end if
-        #if len($blk.sources) > 0 and 'minoutbuf' in $blk.params and int($blk.params['minoutbuf'].get_evaluated()) > 0
-        (self.$blk.get_id()).set_min_output_buffer($blk.params['minoutbuf'].get_evaluated())
-        #end if
-        #if len($blk.sources) > 0 and 'maxoutbuf' in $blk.params and int($blk.params['maxoutbuf'].get_evaluated()) > 0
-        (self.$blk.get_id()).set_max_output_buffer($blk.params['maxoutbuf'].get_evaluated())
-        #end if
-    #end if
-#end for
-########################################################
-##Create Connections
-##  The port name should be the id of the parent block.
-##  However, port names for IO pads should be self.
-########################################################
-#def make_port_sig($port)
-    #if $port.parent.key in ('pad_source', 'pad_sink')
-        #set block = 'self'
-        #set key = $flow_graph.get_pad_port_global_key($port)
-    #else
-        #set block = 'self.' + $port.parent.get_id()
-        #set key = $port.key
-    #end if
-    #if not $key.isdigit()
-        #set key = repr($key)
-    #end if
-($block, $key)#slurp
-#end def
-#if $connections
-
-        $DIVIDER
-        # Connections
-        $DIVIDER
-#end if
-#for $con in $connections
-    #set global $source = $con.source_port
-    #set global $sink = $con.sink_port
-    #include source=$connection_templates[($source.domain, $sink.domain)]
-
-#end for
-########################################################
-## QT sink close method reimplementation
-########################################################
-#if $generate_options == 'qt_gui'
-
-    def closeEvent(self, event):
-        self.settings = Qt.QSettings("GNU Radio", "$class_name")
-        self.settings.setValue("geometry", self.saveGeometry())
-        event.accept()
-    #if $flow_graph.get_option('qt_qss_theme')
-
-    def setStyleSheetFromFile(self, filename):
-        try:
-            if not os.path.exists(filename):
-                filename = os.path.join(
-                    gr.prefix(), "share", "gnuradio", "themes", filename)
-            with open(filename) as ss:
-                self.setStyleSheet(ss.read())
-        except Exception as e:
-            print >> sys.stderr, e
-    #end if
-#end if
-########################################################
-##Create Callbacks
-##  Write a set method for this variable that calls the callbacks
-########################################################
-#for $var in $parameters + $variables
-
-    #set $id = $var.get_id()
-    def get_$(id)(self):
-        return self.$id
-
-    def set_$(id)(self, $id):
-    #if $flow_graph.get_option('thread_safe_setters')
-        with self._lock:
-            self.$id = $id
-        #for $callback in $callbacks[$id]
-            $indent($callback)
-        #end for
-    #else
-        self.$id = $id
-        #for $callback in $callbacks[$id]
-        $indent($callback)
-        #end for
-    #end if
-#end for
-########################################################
-##Create Main
-##  For top block code, generate a main routine.
-##  Instantiate the top block and run as gui or cli.
-########################################################
-#def make_default($type, $param)
-    #if $type == 'eng_float'
-eng_notation.num_to_str($param.get_make())#slurp
-    #else
-$param.get_make()#slurp
-    #end if
-#end def
-#def make_short_id($param)
-    #set $short_id = $param.get_param('short_id').get_evaluated()
-    #if $short_id
-        #set $short_id = '-' + $short_id
-    #end if
-$short_id#slurp
-#end def
-#if not $generate_options.startswith('hb')
-#set $params_eq_list = list()
-#if $parameters
-
-
-def argument_parser():
-    #set $arg_parser_args = ''
-    #if $flow_graph.get_option('description')
-        #set $arg_parser_args = 'description=description'
-    description = $repr($flow_graph.get_option('description'))
-    #end if
-    parser = ArgumentParser($arg_parser_args)
-    #for $param in $parameters
-        #set $type = $param.get_param('type').get_value()
-        #if $type
-            #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id()))
-    parser.add_argument(
-        #if $make_short_id($param)
-        "$make_short_id($param)", #slurp
-        #end if
-        "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type=$type, default=$make_default($type, $param),
-        help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%(default)r]")
-        #end if
-    #end for
-    return parser
-#end if
-
-
-def main(top_block_cls=$(class_name), options=None):
-    #if $parameters
-    if options is None:
-        options = argument_parser().parse_args()
-    #end if
-    #if $flow_graph.get_option('realtime_scheduling')
-    if gr.enable_realtime_scheduling() != gr.RT_OK:
-        print "Error: failed to enable real-time scheduling."
-    #end if
-
-    #if $generate_options == 'qt_gui'
-    if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
-        style = gr.prefs().get_string('qtgui', 'style', 'raster')
-        Qt.QApplication.setGraphicsSystem(style)
-    qapp = Qt.QApplication(sys.argv)
-
-    tb = top_block_cls($(', '.join($params_eq_list)))
-    #if $flow_graph.get_option('run')
-        #if $flow_graph.get_option('max_nouts')
-    tb.start($flow_graph.get_option('max_nouts'))
-        #else
-    tb.start()
-        #end if
-    #end if
-    #if $flow_graph.get_option('qt_qss_theme')
-    tb.setStyleSheetFromFile($repr($flow_graph.get_option('qt_qss_theme')))
-    #end if
-    tb.show()
-
-    def quitting():
-        tb.stop()
-        tb.wait()
-    qapp.aboutToQuit.connect(quitting)
-        #for $m in $monitors
-    if 'en' in $m.params:
-        if $m.params['en'].get_value():
-            (tb.$m.get_id()).start()
-    else:
-        sys.stderr.write("Monitor '{0}' does not have an enable ('en') parameter.".format("tb.$m.get_id()"))
-        #end for
-    qapp.exec_()
-    #elif $generate_options == 'no_gui'
-    tb = top_block_cls($(', '.join($params_eq_list)))
-        #set $run_options = $flow_graph.get_option('run_options')
-        #if $run_options == 'prompt'
-            #if $flow_graph.get_option('max_nouts')
-    tb.start($flow_graph.get_option('max_nouts'))
-            #else
-    tb.start()
-            #end if
-        #for $m in $monitors
-    (tb.$m.get_id()).start()
-        #end for
-    try:
-        raw_input('Press Enter to quit: ')
-    except EOFError:
-        pass
-    tb.stop()
-        #elif $run_options == 'run'
-            #if $flow_graph.get_option('max_nouts')
-    tb.start($flow_graph.get_option('max_nouts'))
-            #else
-    tb.start()
-            #end if
-        #end if
-        #for $m in $monitors
-    (tb.$m.get_id()).start()
-        #end for
-    tb.wait()
-    #end if
-
-
-if __name__ == '__main__':
-    main()
-#end if
diff --git a/grc/core/generator/hier_block.py b/grc/core/generator/hier_block.py
new file mode 100644
index 0000000000..ab362e0663
--- /dev/null
+++ b/grc/core/generator/hier_block.py
@@ -0,0 +1,196 @@
+import collections
+import os
+
+import six
+
+from .top_block import TopBlockGenerator
+
+from .. import ParseXML, Constants
+
+
+class HierBlockGenerator(TopBlockGenerator):
+    """Extends the top block generator to also generate a block XML file"""
+
+    def __init__(self, flow_graph, file_path):
+        """
+        Initialize the hier block generator object.
+
+        Args:
+            flow_graph: the flow graph object
+            file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR)
+        """
+        TopBlockGenerator.__init__(self, flow_graph, file_path)
+        platform = flow_graph.parent
+
+        hier_block_lib_dir = platform.config.hier_block_lib_dir
+        if not os.path.exists(hier_block_lib_dir):
+            os.mkdir(hier_block_lib_dir)
+
+        self._mode = Constants.HIER_BLOCK_FILE_MODE
+        self.file_path = os.path.join(hier_block_lib_dir, self._flow_graph.get_option('id') + '.py')
+        self.file_path_xml = self.file_path + '.xml'
+
+    def write(self):
+        """generate output and write it to files"""
+        TopBlockGenerator.write(self)
+        ParseXML.to_file(self._build_block_n_from_flow_graph_io(), self.file_path_xml)
+        ParseXML.validate_dtd(self.file_path_xml, Constants.BLOCK_DTD)
+        try:
+            os.chmod(self.file_path_xml, self._mode)
+        except:
+            pass
+
+    def _build_block_n_from_flow_graph_io(self):
+        """
+        Generate a block XML nested data from the flow graph IO
+
+        Returns:
+            a xml node tree
+        """
+        # Extract info from the flow graph
+        block_id = self._flow_graph.get_option('id')
+        parameters = self._flow_graph.get_parameters()
+
+        def var_or_value(name):
+            if name in (p.name for p in parameters):
+                return "${" + name + " }"
+            return name
+
+        # Build the nested data
+        data = collections.OrderedDict()
+        data['id'] = block_id
+        data['label'] = (
+            self._flow_graph.get_option('title') or
+            self._flow_graph.get_option('id').replace('_', ' ').title()
+        )
+        data['category'] = self._flow_graph.get_option('category')
+
+        # Parameters
+        data['parameters'] = []
+        for param_block in parameters:
+            p = collections.OrderedDict()
+            p['id'] = param_block.name
+            p['label'] = param_block.params['label'].get_value() or param_block.name
+            p['dtype'] = 'raw'
+            p['value'] = param_block.params['value'].get_value()
+            p['hide'] = param_block.params['hide'].get_value()
+            data['param'].append(p)
+
+        # Ports
+        for direction in ('inputs', 'outputs'):
+            data[direction] = []
+            for port in get_hier_block_io(self._flow_graph, direction):
+                p = collections.OrderedDict()
+                if port.domain == Constants.GR_MESSAGE_DOMAIN:
+                    p['id'] = port.id
+                p['label'] = port.label
+                if port.domain != Constants.DEFAULT_DOMAIN:
+                    p['domain'] = port.domain
+                p['dtype'] = port.dtype
+                if port.domain != Constants.GR_MESSAGE_DOMAIN:
+                    p['vlen'] = var_or_value(port.vlen)
+                if port.optional:
+                    p['optional'] = True
+                data[direction].append(p)
+
+        t = data['templates'] = collections.OrderedDict()
+
+        t['import'] = "from {0} import {0}  # grc-generated hier_block".format(
+            self._flow_graph.get_option('id'))
+        # Make data
+        if parameters:
+            t['make'] = '{cls}(\n    {kwargs},\n)'.format(
+                cls=block_id,
+                kwargs=',\n    '.join(
+                    '{key}=${key}'.format(key=param.name) for param in parameters
+                ),
+            )
+        else:
+            t['make'] = '{cls}()'.format(cls=block_id)
+        # Callback data
+        t['callback'] = [
+            'set_{key}(${key})'.format(key=param_block.name) for param_block in parameters
+        ]
+
+
+        # Documentation
+        data['doc'] = "\n".join(field for field in (
+            self._flow_graph.get_option('author'),
+            self._flow_graph.get_option('description'),
+            self.file_path
+        ) if field)
+        data['grc_source'] = str(self._flow_graph.grc_file_path)
+
+        n = {'block': data}
+        return n
+
+
+class QtHierBlockGenerator(HierBlockGenerator):
+
+    def _build_block_n_from_flow_graph_io(self):
+        n = HierBlockGenerator._build_block_n_from_flow_graph_io(self)
+        block_n = collections.OrderedDict()
+
+        # insert flags after category
+        for key, value in six.iteritems(n['block']):
+            block_n[key] = value
+            if key == 'category':
+                block_n['flags'] = 'need_qt_gui'
+
+        if not block_n['name'].upper().startswith('QT GUI'):
+            block_n['name'] = 'QT GUI ' + block_n['name']
+
+        gui_hint_param = collections.OrderedDict()
+        gui_hint_param['name'] = 'GUI Hint'
+        gui_hint_param['key'] = 'gui_hint'
+        gui_hint_param['value'] = ''
+        gui_hint_param['type'] = 'gui_hint'
+        gui_hint_param['hide'] = 'part'
+        block_n['param'].append(gui_hint_param)
+
+        block_n['make'] += (
+            "\n#set $win = 'self.%s' % $id"
+            "\n${gui_hint()($win)}"
+        )
+
+        return {'block': block_n}
+
+
+def get_hier_block_io(flow_graph, direction, domain=None):
+    """
+    Get a list of io ports for this flow graph.
+
+    Returns a list of dicts with: type, label, vlen, size, optional
+    """
+    pads = flow_graph.get_pad_sources() if direction == 'inputs' else flow_graph.get_pad_sinks()
+
+    ports = []
+    for pad in pads:
+        for port in (pad.sources if direction == 'inputs' else pad.sinks):
+            if domain and port.domain != domain:
+                continue
+            yield port
+
+        type_param = pad.params['type']
+        master = {
+            'label': str(pad.params['label'].get_evaluated()),
+            'type': str(pad.params['type'].get_evaluated()),
+            'vlen': str(pad.params['vlen'].get_value()),
+            'size':  type_param.options.attributes[type_param.get_value()]['size'],
+            'optional': bool(pad.params['optional'].get_evaluated()),
+        }
+        if domain and pad.
+
+        num_ports = pad.params['num_streams'].get_evaluated()
+        if num_ports <= 1:
+            yield master
+        else:
+            for i in range(num_ports):
+                clone = master.copy()
+                clone['label'] += str(i)
+                ports.append(clone)
+        else:
+            ports.append(master)
+    if domain is not None:
+        ports = [p for p in ports if p.domain == domain]
+    return ports
diff --git a/grc/core/generator/top_block.py b/grc/core/generator/top_block.py
new file mode 100644
index 0000000000..e4fed838ae
--- /dev/null
+++ b/grc/core/generator/top_block.py
@@ -0,0 +1,285 @@
+import codecs
+import operator
+import os
+import tempfile
+import textwrap
+import time
+
+from mako.template import Template
+
+from .. import Messages, blocks
+from ..Constants import TOP_BLOCK_FILE_MODE
+from .FlowGraphProxy import FlowGraphProxy
+from ..utils import expr_utils
+
+DATA_DIR = os.path.dirname(__file__)
+FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.py.mako')
+flow_graph_template = Template(filename=FLOW_GRAPH_TEMPLATE)
+
+
+class TopBlockGenerator(object):
+
+    def __init__(self, flow_graph, file_path):
+        """
+        Initialize the top block generator object.
+
+        Args:
+            flow_graph: the flow graph object
+            file_path: the path to write the file to
+        """
+
+        self._flow_graph = FlowGraphProxy(flow_graph)
+        self._generate_options = self._flow_graph.get_option('generate_options')
+
+        self._mode = TOP_BLOCK_FILE_MODE
+        dirname = os.path.dirname(file_path)
+        # Handle the case where the directory is read-only
+        # In this case, use the system's temp directory
+        if not os.access(dirname, os.W_OK):
+            dirname = tempfile.gettempdir()
+        filename = self._flow_graph.get_option('id') + '.py'
+        self.file_path = os.path.join(dirname, filename)
+        self._dirname = dirname
+
+    def _warnings(self):
+        throttling_blocks = [b for b in self._flow_graph.get_enabled_blocks()
+                             if b.flags.throttle]
+        if not throttling_blocks and not self._generate_options.startswith('hb'):
+            Messages.send_warning("This flow graph may not have flow control: "
+                                  "no audio or RF hardware blocks found. "
+                                  "Add a Misc->Throttle block to your flow "
+                                  "graph to avoid CPU congestion.")
+        if len(throttling_blocks) > 1:
+            keys = set([b.key for b in throttling_blocks])
+            if len(keys) > 1 and 'blocks_throttle' in keys:
+                Messages.send_warning("This flow graph contains a throttle "
+                                      "block and another rate limiting block, "
+                                      "e.g. a hardware source or sink. "
+                                      "This is usually undesired. Consider "
+                                      "removing the throttle block.")
+
+        deprecated_block_keys = {b.name for b in self._flow_graph.get_enabled_blocks() if b.flags.deprecated}
+        for key in deprecated_block_keys:
+            Messages.send_warning("The block {!r} is deprecated.".format(key))
+
+    def write(self):
+        """generate output and write it to files"""
+        self._warnings()
+
+        for filename, data in self._build_python_code_from_template():
+            with codecs.open(filename, 'w', encoding='utf-8') as fp:
+                fp.write(data)
+            if filename == self.file_path:
+                try:
+                    os.chmod(filename, self._mode)
+                except:
+                    pass
+
+    def _build_python_code_from_template(self):
+        """
+        Convert the flow graph to python code.
+
+        Returns:
+            a string of python code
+        """
+        output = []
+
+        fg = self._flow_graph
+        title = fg.get_option('title') or fg.get_option('id').replace('_', ' ').title()
+        variables = fg.get_variables()
+        parameters = fg.get_parameters()
+        monitors = fg.get_monitors()
+
+        for block in fg.iter_enabled_blocks():
+            key = block.key
+            file_path = os.path.join(self._dirname, block.name + '.py')
+            if key == 'epy_block':
+                src = block.params['_source_code'].get_value()
+                output.append((file_path, src))
+            elif key == 'epy_module':
+                src = block.params['source_code'].get_value()
+                output.append((file_path, src))
+
+        namespace = {
+            'flow_graph': fg,
+            'variables': variables,
+            'parameters': parameters,
+            'monitors': monitors,
+            'generate_options': self._generate_options,
+            'generated_time': time.ctime(),
+        }
+        flow_graph_code = flow_graph_template.render(
+            title=title,
+            imports=self._imports(),
+            blocks=self._blocks(),
+            callbacks=self._callbacks(),
+            connections=self._connections(),
+            **namespace
+        )
+        # strip trailing white-space
+        flow_graph_code = "\n".join(line.rstrip() for line in flow_graph_code.split("\n"))
+        output.append((self.file_path, flow_graph_code))
+
+        return output
+
+    def _imports(self):
+        fg = self._flow_graph
+        imports = fg.imports()
+        seen = set()
+        output = []
+
+        need_path_hack = any(imp.endswith("# grc-generated hier_block") for imp in imports)
+        if need_path_hack:
+            output.insert(0, textwrap.dedent("""\
+                import os
+                import sys
+                sys.path.append(os.environ.get('GRC_HIER_PATH', os.path.expanduser('~/.grc_gnuradio')))
+            """))
+            seen.add('import os')
+            seen.add('import sys')
+
+        if fg.get_option('qt_qss_theme'):
+            imports.append('import os')
+            imports.append('import sys')
+
+        if fg.get_option('thread_safe_setters'):
+            imports.append('import threading')
+
+        def is_duplicate(l):
+            if l.startswith('import') or l.startswith('from') and l in seen:
+                return True
+            seen.add(line)
+            return False
+
+        for import_ in sorted(imports):
+            lines = import_.strip().split('\n')
+            if not lines[0]:
+                continue
+            for line in lines:
+                line = line.rstrip()
+                if not is_duplicate(line):
+                    output.append(line)
+
+        return output
+
+    def _blocks(self):
+        fg = self._flow_graph
+        parameters = fg.get_parameters()
+
+        # List of blocks not including variables and imports and parameters and disabled
+        def _get_block_sort_text(block):
+            code = block.templates.render('make').replace(block.name, ' ')
+            try:
+                code += block.params['gui_hint'].get_value()  # Newer gui markup w/ qtgui
+            except:
+                pass
+            return code
+
+        blocks = [
+            b for b in fg.blocks
+            if b.enabled and not (b.get_bypassed() or b.is_import or b in parameters or b.key == 'options')
+        ]
+
+        blocks = expr_utils.sort_objects(blocks, operator.attrgetter('name'), _get_block_sort_text)
+        blocks_make = []
+        for block in blocks:
+            make = block.templates.render('make')
+            if not block.is_variable:
+                make = 'self.' + block.name + ' = ' + make
+            if make:
+                blocks_make.append((block, make))
+        return blocks_make
+
+    def _callbacks(self):
+        fg = self._flow_graph
+        variables = fg.get_variables()
+        parameters = fg.get_parameters()
+
+        # List of variable names
+        var_ids = [var.name for var in parameters + variables]
+        replace_dict = dict((var_id, 'self.' + var_id) for var_id in var_ids)
+        callbacks_all = []
+        for block in fg.iter_enabled_blocks():
+            callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_callbacks())
+
+        # Map var id to callbacks
+        def uses_var_id():
+            used = expr_utils.get_variable_dependencies(callback, [var_id])
+            return used and 'self.' + var_id in callback  # callback might contain var_id itself
+
+        callbacks = {}
+        for var_id in var_ids:
+            callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id()]
+
+        return callbacks
+
+    def _connections(self):
+        fg = self._flow_graph
+        templates = {key: Template(text)
+                     for key, text in fg.parent_platform.connection_templates.items()}
+
+        def make_port_sig(port):
+            if port.parent.key in ('pad_source', 'pad_sink'):
+                block = 'self'
+                key = fg.get_pad_port_global_key(port)
+            else:
+                block = 'self.' + port.parent_block.name
+                key = port.key
+
+            if not key.isdigit():
+                key = repr(key)
+
+            return '({block}, {key})'.format(block=block, key=key)
+
+        connections = fg.get_enabled_connections()
+
+        # Get the virtual blocks and resolve their connections
+        connection_factory = fg.parent_platform.Connection
+        virtual = [c for c in connections if isinstance(c.source_block, blocks.VirtualSource)]
+        for connection in virtual:
+            sink = connection.sink_port
+            for source in connection.source_port.resolve_virtual_source():
+                resolved = connection_factory(fg.orignal_flowgraph, source, sink)
+                connections.append(resolved)
+            # Remove the virtual connection
+            connections.remove(connection)
+
+        # Bypassing blocks: Need to find all the enabled connections for the block using
+        # the *connections* object rather than get_connections(). Create new connections
+        # that bypass the selected block and remove the existing ones. This allows adjacent
+        # bypassed blocks to see the newly created connections to downstream blocks,
+        # allowing them to correctly construct bypass connections.
+        bypassed_blocks = fg.get_bypassed_blocks()
+        for block in bypassed_blocks:
+            # Get the upstream connection (off of the sink ports)
+            # Use *connections* not get_connections()
+            source_connection = [c for c in connections if c.sink_port == block.sinks[0]]
+            # The source connection should never have more than one element.
+            assert (len(source_connection) == 1)
+
+            # Get the source of the connection.
+            source_port = source_connection[0].source_port
+
+            # Loop through all the downstream connections
+            for sink in (c for c in connections if c.source_port == block.sources[0]):
+                if not sink.enabled:
+                    # Ignore disabled connections
+                    continue
+                connection = connection_factory(fg.orignal_flowgraph, source_port, sink.sink_port)
+                connections.append(connection)
+                # Remove this sink connection
+                connections.remove(sink)
+            # Remove the source connection
+            connections.remove(source_connection[0])
+
+        # List of connections where each endpoint is enabled (sorted by domains, block names)
+        def by_domain_and_blocks(c):
+            return c.type, c.source_block.name, c.sink_block.name
+
+        rendered = []
+        for con in sorted(connections, key=by_domain_and_blocks):
+            template = templates[con.type]
+            code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port)
+            rendered.append(code)
+
+        return rendered
diff --git a/grc/core/io/__init__.py b/grc/core/io/__init__.py
new file mode 100644
index 0000000000..f77f1a6704
--- /dev/null
+++ b/grc/core/io/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2017 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
diff --git a/grc/core/io/yaml.py b/grc/core/io/yaml.py
new file mode 100644
index 0000000000..29b4cb81d6
--- /dev/null
+++ b/grc/core/io/yaml.py
@@ -0,0 +1,91 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from collections import OrderedDict
+
+import six
+import yaml
+
+
+class GRCDumper(yaml.SafeDumper):
+    @classmethod
+    def add(cls, data_type):
+        def decorator(func):
+            cls.add_representer(data_type, func)
+            return func
+        return decorator
+
+    def represent_ordered_mapping(self, data):
+        value = []
+        node = yaml.MappingNode(u'tag:yaml.org,2002:map', value, flow_style=False)
+
+        if self.alias_key is not None:
+            self.represented_objects[self.alias_key] = node
+
+        for item_key, item_value in six.iteritems(data):
+            node_key = self.represent_data(item_key)
+            node_value = self.represent_data(item_value)
+            value.append((node_key, node_value))
+
+        return node
+
+    def represent_ordered_mapping_flowing(self, data):
+        node = self.represent_ordered_mapping(data)
+        node.flow_style = True
+        return node
+
+    def represent_list_flowing(self, data):
+        node = self.represent_list(data)
+        node.flow_style = True
+        return node
+
+    def represent_ml_string(self, data):
+        node = self.represent_str(data)
+        node.style = '|'
+        return node
+
+
+class OrderedDictFlowing(OrderedDict):
+    pass
+
+
+class ListFlowing(list):
+    pass
+
+
+class MultiLineString(str):
+    pass
+
+
+GRCDumper.add_representer(OrderedDict, GRCDumper.represent_ordered_mapping)
+GRCDumper.add_representer(OrderedDictFlowing, GRCDumper.represent_ordered_mapping_flowing)
+GRCDumper.add_representer(ListFlowing, GRCDumper.represent_list_flowing)
+GRCDumper.add_representer(tuple, GRCDumper.represent_list)
+GRCDumper.add_representer(MultiLineString, GRCDumper.represent_ml_string)
+GRCDumper.add_representer(yaml.nodes.ScalarNode, lambda r, n: n)
+
+
+def dump(data, stream=None, **kwargs):
+    config = dict(stream=stream, default_flow_style=False, indent=4, Dumper=GRCDumper)
+    config.update(kwargs)
+    return yaml.dump_all([data], **config)
+
+
+safe_load = yaml.safe_load
+__with_libyaml__ = yaml.__with_libyaml__
diff --git a/grc/core/platform.py b/grc/core/platform.py
new file mode 100644
index 0000000000..02203942f9
--- /dev/null
+++ b/grc/core/platform.py
@@ -0,0 +1,430 @@
+# Copyright 2008-2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import, print_function
+
+from codecs import open
+from collections import namedtuple
+import glob
+import os
+import logging
+from itertools import chain
+
+import six
+from six.moves import range
+
+from . import (
+    Messages, Constants,
+    blocks, ports, errors, utils, schema_checker
+)
+
+from .Config import Config
+from .base import Element
+from .io import yaml
+from .generator import Generator
+from .FlowGraph import FlowGraph
+from .Connection import Connection
+from .Param import Param
+
+logger = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+
+class Platform(Element):
+
+    def __init__(self, *args, **kwargs):
+        """ Make a platform for GNU Radio """
+        Element.__init__(self, parent=None)
+
+        self.config = self.Config(*args, **kwargs)
+        self.block_docstrings = {}
+        self.block_docstrings_loaded_callback = lambda: None  # dummy to be replaced by BlockTreeWindow
+
+        self._docstring_extractor = utils.extract_docs.SubprocessLoader(
+            callback_query_result=self._save_docstring_extraction_result,
+            callback_finished=lambda: self.block_docstrings_loaded_callback()
+        )
+
+        self.blocks = self.block_classes
+        self.domains = {}
+        self.connection_templates = {}
+
+        self._block_categories = {}
+        self._auto_hier_block_generate_chain = set()
+
+        if not yaml.__with_libyaml__:
+            logger.warning("Slow YAML loading (libyaml not available)")
+
+    def __str__(self):
+        return 'Platform - {}'.format(self.config.name)
+
+    @staticmethod
+    def find_file_in_paths(filename, paths, cwd):
+        """Checks the provided paths relative to cwd for a certain filename"""
+        if not os.path.isdir(cwd):
+            cwd = os.path.dirname(cwd)
+        if isinstance(paths, str):
+            paths = (p for p in paths.split(':') if p)
+
+        for path in paths:
+            path = os.path.expanduser(path)
+            if not os.path.isabs(path):
+                path = os.path.normpath(os.path.join(cwd, path))
+            file_path = os.path.join(path, filename)
+            if os.path.exists(os.path.normpath(file_path)):
+                return file_path
+
+    def load_and_generate_flow_graph(self, file_path, out_path=None, hier_only=False):
+        """Loads a flow graph from file and generates it"""
+        Messages.set_indent(len(self._auto_hier_block_generate_chain))
+        Messages.send('>>> Loading: {}\n'.format(file_path))
+        if file_path in self._auto_hier_block_generate_chain:
+            Messages.send('    >>> Warning: cyclic hier_block dependency\n')
+            return None, None
+        self._auto_hier_block_generate_chain.add(file_path)
+        try:
+            flow_graph = self.make_flow_graph()
+            flow_graph.grc_file_path = file_path
+            # Other, nested hier_blocks might be auto-loaded here
+            flow_graph.import_data(self.parse_flow_graph(file_path))
+            flow_graph.rewrite()
+            flow_graph.validate()
+            if not flow_graph.is_valid():
+                raise Exception('Flowgraph invalid')
+            if hier_only and not flow_graph.get_option('generate_options').startswith('hb'):
+                raise Exception('Not a hier block')
+        except Exception as e:
+            Messages.send('>>> Load Error: {}: {}\n'.format(file_path, str(e)))
+            return None, None
+        finally:
+            self._auto_hier_block_generate_chain.discard(file_path)
+            Messages.set_indent(len(self._auto_hier_block_generate_chain))
+
+        try:
+            generator = self.Generator(flow_graph, out_path or file_path)
+            Messages.send('>>> Generating: {}\n'.format(generator.file_path))
+            generator.write()
+        except Exception as e:
+            Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e)))
+            return None, None
+
+        if flow_graph.get_option('generate_options').startswith('hb'):
+            # self.load_block_xml(generator.file_path_xml)
+            # TODO: implement yml output for hier blocks
+            pass
+        return flow_graph, generator.file_path
+
+    def build_library(self, path=None):
+        """load the blocks and block tree from the search paths
+
+        path: a list of paths/files to search in or load (defaults to config)
+        """
+        self._docstring_extractor.start()
+
+        # Reset
+        self.blocks.clear()
+        self.domains.clear()
+        self.connection_templates.clear()
+        self._block_categories.clear()
+
+        # FIXME: remove this as soon as converter is stable
+        from ..converter import Converter
+        converter = Converter(self.config.block_paths, self.config.yml_block_cache)
+        converter.run()
+        logging.info('XML converter done.')
+
+        for file_path in self._iter_files_in_block_path(path):
+            try:
+                data = converter.cache[file_path]
+            except KeyError:
+                with open(file_path, encoding='utf-8') as fp:
+                    data = yaml.safe_load(fp)
+
+            if file_path.endswith('.block.yml'):
+                loader = self.load_block_description
+                scheme = schema_checker.BLOCK_SCHEME
+            elif file_path.endswith('.domain.yml'):
+                loader = self.load_domain_description
+                scheme = schema_checker.DOMAIN_SCHEME
+            elif file_path.endswith('.tree.yml'):
+                loader = self.load_category_tree_description
+                scheme = None
+            else:
+                continue
+
+            try:
+                checker = schema_checker.Validator(scheme)
+                passed = checker.run(data)
+                for msg in checker.messages:
+                    logger.warning('{:<40s} {}'.format(os.path.basename(file_path), msg))
+                if not passed:
+                    logger.info('YAML schema check failed for: ' + file_path)
+
+                loader(data, file_path)
+            except Exception as error:
+                logger.exception('Error while loading %s', file_path)
+                logger.exception(error)
+                raise
+
+        for key, block in six.iteritems(self.blocks):
+            category = self._block_categories.get(key, block.category)
+            if not category:
+                continue
+            root = category[0]
+            if root.startswith('[') and root.endswith(']'):
+                category[0] = root[1:-1]
+            else:
+                category.insert(0, Constants.DEFAULT_BLOCK_MODULE_NAME)
+            block.category = category
+
+        self._docstring_extractor.finish()
+        # self._docstring_extractor.wait()
+
+    def _iter_files_in_block_path(self, path=None, ext='yml'):
+        """Iterator for block descriptions and category trees"""
+        for entry in (path or self.config.block_paths):
+            if os.path.isfile(entry):
+                yield entry
+            elif os.path.isdir(entry):
+                pattern = os.path.join(entry, '**.' + ext)
+                yield_from = glob.iglob(pattern)
+                for file_path in yield_from:
+                    yield file_path
+            else:
+                logger.debug('Ignoring invalid path entry %r', entry)
+
+    def _save_docstring_extraction_result(self, block_id, docstrings):
+        docs = {}
+        for match, docstring in six.iteritems(docstrings):
+            if not docstring or match.endswith('_sptr'):
+                continue
+            docs[match] = docstring.replace('\n\n', '\n').strip()
+        try:
+            self.blocks[block_id].documentation.update(docs)
+        except KeyError:
+            pass  # in tests platform might be gone...
+
+    ##############################################
+    # Description File Loaders
+    ##############################################
+    # region loaders
+    def load_block_description(self, data, file_path):
+        log = logger.getChild('block_loader')
+
+        # don't load future block format versions
+        file_format = data['file_format']
+        if file_format < 1 or file_format > Constants.BLOCK_DESCRIPTION_FILE_FORMAT_VERSION:
+            log.error('Unknown format version %d in %s', file_format, file_path)
+            return
+
+        block_id = data.pop('id').rstrip('_')
+
+        if block_id in self.block_classes_build_in:
+            log.warning('Not overwriting build-in block %s with %s', block_id, file_path)
+            return
+        if block_id in self.blocks:
+            log.warning('Block with id "%s" overwritten by %s', block_id, file_path)
+
+        try:
+            block_cls = self.blocks[block_id] = self.new_block_class(block_id, **data)
+        except errors.BlockLoadError as error:
+            log.error('Unable to load block %s', block_id)
+            log.exception(error)
+            return
+
+        self._docstring_extractor.query(
+            block_id, block_cls.templates['imports'], block_cls.templates['make'],
+        )
+
+    def load_domain_description(self, data, file_path):
+        log = logger.getChild('domain_loader')
+        domain_id = data['id']
+        if domain_id in self.domains:  # test against repeated keys
+            log.debug('Domain "{}" already exists. Ignoring: %s', file_path)
+            return
+
+        color = data.get('color', '')
+        if color.startswith('#'):
+            try:
+                tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 6, 2))
+            except ValueError:
+                log.warning('Cannot parse color code "%s" in %s', color, file_path)
+                return
+
+        self.domains[domain_id] = self.Domain(
+            name=data.get('label', domain_id),
+            multi_in=data.get('multiple_connections_per_input', True),
+            multi_out=data.get('multiple_connections_per_output', False),
+            color=color
+        )
+        for connection in data.get('templates', []):
+            try:
+                source_id, sink_id = connection.get('type', [])
+            except ValueError:
+                log.warn('Invalid connection template.')
+                continue
+            connection_id = str(source_id), str(sink_id)
+            self.connection_templates[connection_id] = connection.get('connect', '')
+
+    def load_category_tree_description(self, data, file_path):
+        """Parse category tree file and add it to list"""
+        log = logger.getChild('tree_loader')
+        log.debug('Loading %s', file_path)
+        path = []
+
+        def load_category(name, elements):
+            if not isinstance(name, str):
+                log.debug('invalid name %r', name)
+                return
+            if isinstance(elements, list):
+                pass
+            elif isinstance(elements, str):
+                elements = [elements]
+            else:
+                log.debug('Ignoring elements of %s', name)
+                return
+            path.append(name)
+            for element in elements:
+                if isinstance(element, str):
+                    block_id = element
+                    self._block_categories[block_id] = list(path)
+                elif isinstance(element, dict):
+                    load_category(*next(six.iteritems(element)))
+                else:
+                    log.debug('Ignoring some elements of %s', name)
+            path.pop()
+
+        try:
+            module_name, categories = next(six.iteritems(data))
+        except (AttributeError, StopIteration):
+            log.warning('no valid data found')
+        else:
+            load_category(module_name, categories)
+
+    ##############################################
+    # Access
+    ##############################################
+    def parse_flow_graph(self, filename):
+        """
+        Parse a saved flow graph file.
+        Ensure that the file exists, and passes the dtd check.
+
+        Args:
+            filename: the flow graph file
+
+        Returns:
+            nested data
+        @throws exception if the validation fails
+        """
+        filename = filename or self.config.default_flow_graph
+        with open(filename, encoding='utf-8') as fp:
+            is_xml = '<flow_graph>' in fp.read(100)
+            fp.seek(0)
+            # todo: try
+            if not is_xml:
+                data = yaml.safe_load(fp)
+                validator = schema_checker.Validator(schema_checker.FLOW_GRAPH_SCHEME)
+                validator.run(data)
+            else:
+                Messages.send('>>> Converting from XML\n')
+                from ..converter.flow_graph import from_xml
+                data = from_xml(fp)
+
+        return data
+
+    def save_flow_graph(self, filename, flow_graph):
+        data = flow_graph.export_data()
+
+        try:
+            data['connections'] = [yaml.ListFlowing(i) for i in data['connections']]
+        except KeyError:
+            pass
+
+        try:
+            for d in chain([data['options']], data['blocks']):
+                d['states']['coordinate'] = yaml.ListFlowing(d['states']['coordinate'])
+                for param_id, value in list(d['parameters'].items()):
+                    if value == '':
+                        d['parameters'].pop(param_id)
+        except KeyError:
+            pass
+
+        out = yaml.dump(data, indent=2)
+
+        replace = [
+            ('blocks:', '\nblocks:'),
+            ('connections:', '\nconnections:'),
+            ('metadata:', '\nmetadata:'),
+        ]
+        for r in replace:
+            out = out.replace(*r)
+
+        with open(filename, 'w', encoding='utf-8') as fp:
+            fp.write(out)
+
+    def get_generate_options(self):
+        for param in self.block_classes['options'].parameters_data:
+            if param.get('id') == 'generate_options':
+                break
+        else:
+            return []
+        generate_mode_default = param.get('default')
+        return [(value, name, value == generate_mode_default)
+                for value, name in zip(param['options'], param['option_labels'])]
+
+    ##############################################
+    # Factories
+    ##############################################
+    Config = Config
+    Domain = namedtuple('Domain', 'name multi_in multi_out color')
+    Generator = Generator
+    FlowGraph = FlowGraph
+    Connection = Connection
+
+    block_classes_build_in = blocks.build_ins
+    block_classes = utils.backports.ChainMap({}, block_classes_build_in)  # separates build-in from loaded blocks)
+
+    port_classes = {
+        None: ports.Port,  # default
+        'clone': ports.PortClone,  # clone of ports with multiplicity > 1
+    }
+    param_classes = {
+        None: Param,  # default
+    }
+
+    def make_flow_graph(self, from_filename=None):
+        fg = self.FlowGraph(parent=self)
+        if from_filename:
+            data = self.parse_flow_graph(from_filename)
+            fg.grc_file_path = from_filename
+            fg.import_data(data)
+        return fg
+
+    def new_block_class(self, block_id, **data):
+        return blocks.build(block_id, **data)
+
+    def make_block(self, parent, block_id, **kwargs):
+        cls = self.block_classes[block_id]
+        return cls(parent, **kwargs)
+
+    def make_param(self, parent, **kwargs):
+        cls = self.param_classes[kwargs.pop('cls_key', None)]
+        return cls(parent, **kwargs)
+
+    def make_port(self, parent, **kwargs):
+        cls = self.port_classes[kwargs.pop('cls_key', None)]
+        return cls(parent, **kwargs)
diff --git a/grc/core/ports/__init__.py b/grc/core/ports/__init__.py
new file mode 100644
index 0000000000..375b5d63e3
--- /dev/null
+++ b/grc/core/ports/__init__.py
@@ -0,0 +1,23 @@
+"""
+Copyright 2008-2015 Free Software Foundation, Inc.
+This file is part of GNU Radio
+
+GNU Radio Companion is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+GNU Radio Companion is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+"""
+
+from __future__ import absolute_import
+
+from .port import Port
+from .clone import PortClone
diff --git a/grc/core/ports/_virtual_connections.py b/grc/core/ports/_virtual_connections.py
new file mode 100644
index 0000000000..45f4a247fd
--- /dev/null
+++ b/grc/core/ports/_virtual_connections.py
@@ -0,0 +1,126 @@
+# Copyright 2008-2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from itertools import chain
+
+from .. import blocks
+
+
+class LoopError(Exception):
+    pass
+
+
+def upstream_ports(port):
+    if port.is_sink:
+        return _sources_from_virtual_sink_port(port)
+    else:
+        return _sources_from_virtual_source_port(port)
+
+
+def _sources_from_virtual_sink_port(sink_port, _traversed=None):
+    """
+    Resolve the source port that is connected to the given virtual sink port.
+    Use the get source from virtual source to recursively resolve subsequent ports.
+    """
+    source_ports_per_virtual_connection = (
+        # there can be multiple ports per virtual connection
+        _sources_from_virtual_source_port(c.source_port, _traversed)  # type: list
+        for c in sink_port.connections(enabled=True)
+    )
+    return list(chain(*source_ports_per_virtual_connection))  # concatenate generated lists of ports
+
+
+def _sources_from_virtual_source_port(source_port, _traversed=None):
+    """
+    Recursively resolve source ports over the virtual connections.
+    Keep track of traversed sources to avoid recursive loops.
+    """
+    _traversed = set(_traversed or [])  # a new set!
+    if source_port in _traversed:
+        raise LoopError('Loop found when resolving port type')
+    _traversed.add(source_port)
+
+    block = source_port.parent_block
+    flow_graph = source_port.parent_flowgraph
+
+    if not isinstance(block, blocks.VirtualSource):
+        return [source_port]  # nothing to resolve, we're done
+
+    stream_id = block.params['stream_id'].value
+
+    # currently the validation does not allow multiple virtual sinks and one virtual source
+    # but in the future it may...
+    connected_virtual_sink_blocks = (
+        b for b in flow_graph.iter_enabled_blocks()
+        if isinstance(b, blocks.VirtualSink) and b.params['stream_id'].value == stream_id
+    )
+    source_ports_per_virtual_connection = (
+        _sources_from_virtual_sink_port(b.sinks[0], _traversed)  # type: list
+        for b in connected_virtual_sink_blocks
+    )
+    return list(chain(*source_ports_per_virtual_connection))  # concatenate generated lists of ports
+
+
+def downstream_ports(port):
+    if port.is_source:
+        return _sinks_from_virtual_source_port(port)
+    else:
+        return _sinks_from_virtual_sink_port(port)
+
+
+def _sinks_from_virtual_source_port(source_port, _traversed=None):
+    """
+    Resolve the sink port that is connected to the given virtual source port.
+    Use the get sink from virtual sink to recursively resolve subsequent ports.
+    """
+    sink_ports_per_virtual_connection = (
+        # there can be multiple ports per virtual connection
+        _sinks_from_virtual_sink_port(c.sink_port, _traversed)  # type: list
+        for c in source_port.connections(enabled=True)
+    )
+    return list(chain(*sink_ports_per_virtual_connection))  # concatenate generated lists of ports
+
+
+def _sinks_from_virtual_sink_port(sink_port, _traversed=None):
+    """
+    Recursively resolve sink ports over the virtual connections.
+    Keep track of traversed sinks to avoid recursive loops.
+    """
+    _traversed = set(_traversed or [])  # a new set!
+    if sink_port in _traversed:
+        raise LoopError('Loop found when resolving port type')
+    _traversed.add(sink_port)
+
+    block = sink_port.parent_block
+    flow_graph = sink_port.parent_flowgraph
+
+    if not isinstance(block, blocks.VirtualSink):
+        return [sink_port]
+
+    stream_id = block.params['stream_id'].value
+
+    connected_virtual_source_blocks = (
+        b for b in flow_graph.iter_enabled_blocks()
+        if isinstance(b, blocks.VirtualSource) and b.params['stream_id'].value == stream_id
+    )
+    sink_ports_per_virtual_connection = (
+        _sinks_from_virtual_source_port(b.sources[0], _traversed)  # type: list
+        for b in connected_virtual_source_blocks
+    )
+    return list(chain(*sink_ports_per_virtual_connection))  # concatenate generated lists of ports
diff --git a/grc/core/ports/clone.py b/grc/core/ports/clone.py
new file mode 100644
index 0000000000..4e1320f81d
--- /dev/null
+++ b/grc/core/ports/clone.py
@@ -0,0 +1,38 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from .port import Port, Element
+
+
+class PortClone(Port):
+
+    def __init__(self, parent, direction, master, name, key):
+        Element.__init__(self, parent)
+        self.master_port = master
+
+        self.name = name
+        self.key = key
+        self.multiplicity = 1
+
+    def __getattr__(self, item):
+        return getattr(self.master_port, item)
+
+    def add_clone(self):
+        raise NotImplementedError()
+
+    def remove_clone(self, port):
+        raise NotImplementedError()
diff --git a/grc/core/ports/port.py b/grc/core/ports/port.py
new file mode 100644
index 0000000000..139aae0ccc
--- /dev/null
+++ b/grc/core/ports/port.py
@@ -0,0 +1,207 @@
+# Copyright 2008-2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from . import _virtual_connections
+
+from .. import Constants
+from ..base import Element
+from ..utils.descriptors import (
+    EvaluatedFlag, EvaluatedEnum, EvaluatedPInt,
+    setup_names, lazy_property
+)
+
+
+@setup_names
+class Port(Element):
+
+    is_port = True
+
+    dtype = EvaluatedEnum(list(Constants.TYPE_TO_SIZEOF.keys()), default='')
+    vlen = EvaluatedPInt()
+    multiplicity = EvaluatedPInt()
+    hidden = EvaluatedFlag()
+    optional = EvaluatedFlag()
+
+    def __init__(self, parent, direction, id, label='', domain=Constants.DEFAULT_DOMAIN, dtype='',
+                 vlen='', multiplicity=1, optional=False, hide='', **_):
+        """Make a new port from nested data."""
+        Element.__init__(self, parent)
+
+        self._dir = direction
+        self.key = id
+        if not label:
+            label = id if not id.isdigit() else {'sink': 'in', 'source': 'out'}[direction] + id
+        self.name = self._base_name = label
+
+        self.domain = domain
+        self.dtype = dtype
+        self.vlen = vlen
+
+        if domain == Constants.GR_MESSAGE_DOMAIN:  # ToDo: message port class
+            self.key = self.name
+            self.dtype = 'message'
+
+        self.multiplicity = multiplicity
+        self.optional = optional
+        self.hidden = hide
+        # end of args ########################################################
+        self.clones = []  # References to cloned ports (for nports > 1)
+
+    def __str__(self):
+        if self.is_source:
+            return 'Source - {}({})'.format(self.name, self.key)
+        if self.is_sink:
+            return 'Sink - {}({})'.format(self.name, self.key)
+
+    def __repr__(self):
+        return '{!r}.{}[{}]'.format(self.parent, 'sinks' if self.is_sink else 'sources', self.key)
+
+    @property
+    def item_size(self):
+        return Constants.TYPE_TO_SIZEOF[self.dtype] * self.vlen
+
+    @lazy_property
+    def is_sink(self):
+        return self._dir == 'sink'
+
+    @lazy_property
+    def is_source(self):
+        return self._dir == 'source'
+
+    @property
+    def inherit_type(self):
+        """always empty for e.g. virtual blocks, may eval to empty for 'Wildcard'"""
+        return not self.dtype
+
+    def validate(self):
+        Element.validate(self)
+        platform = self.parent_platform
+
+        num_connections = len(list(self.connections(enabled=True)))
+        need_connection = not self.optional and not self.hidden
+        if need_connection and num_connections == 0:
+            self.add_error_message('Port is not connected.')
+
+        if self.dtype not in Constants.TYPE_TO_SIZEOF.keys():
+            self.add_error_message('Type "{}" is not a possible type.'.format(self.dtype))
+
+        try:
+            domain = platform.domains[self.domain]
+            if self.is_sink and not domain.multi_in and num_connections > 1:
+                self.add_error_message('Domain "{}" can have only one upstream block'
+                                       ''.format(self.domain))
+            if self.is_source and not domain.multi_out and num_connections > 1:
+                self.add_error_message('Domain "{}" can have only one downstream block'
+                                       ''.format(self.domain))
+        except KeyError:
+            self.add_error_message('Domain key "{}" is not registered.'.format(self.domain))
+
+    def rewrite(self):
+        del self.vlen
+        del self.multiplicity
+        del self.hidden
+        del self.optional
+        del self.dtype
+
+        if self.inherit_type:
+            self.resolve_empty_type()
+
+        Element.rewrite(self)
+
+        # Update domain if was deduced from (dynamic) port type
+        if self.domain == Constants.GR_STREAM_DOMAIN and self.dtype == "message":
+            self.domain = Constants.GR_MESSAGE_DOMAIN
+            self.key = self.name
+        if self.domain == Constants.GR_MESSAGE_DOMAIN and self.dtype != "message":
+            self.domain = Constants.GR_STREAM_DOMAIN
+            self.key = '0'  # Is rectified in rewrite()
+
+    def resolve_virtual_source(self):
+        """Only used by Generator after validation is passed"""
+        return _virtual_connections.upstream_ports(self)
+
+    def resolve_empty_type(self):
+        def find_port(finder):
+            try:
+                return next((p for p in finder(self) if not p.inherit_type), None)
+            except _virtual_connections.LoopError as error:
+                self.add_error_message(str(error))
+            except (StopIteration, Exception):
+                pass
+
+        try:
+            port = find_port(_virtual_connections.upstream_ports) or \
+                   find_port(_virtual_connections.downstream_ports)
+            self.set_evaluated('dtype', port.dtype)  # we don't want to override the template
+            self.set_evaluated('vlen', port.vlen)  # we don't want to override the template
+            self.domain = port.domain
+        except AttributeError:
+            self.domain = Constants.DEFAULT_DOMAIN
+
+    def add_clone(self):
+        """
+        Create a clone of this (master) port and store a reference in self._clones.
+
+        The new port name (and key for message ports) will have index 1... appended.
+        If this is the first clone, this (master) port will get a 0 appended to its name (and key)
+
+        Returns:
+            the cloned port
+        """
+        # Add index to master port name if there are no clones yet
+        if not self.clones:
+            self.name = self._base_name + '0'
+            # Also update key for none stream ports
+            if not self.key.isdigit():
+                self.key = self.name
+
+        name = self._base_name + str(len(self.clones) + 1)
+        # Dummy value 99999 will be fixed later
+        key = '99999' if self.key.isdigit() else name
+
+        # Clone
+        port_factory = self.parent_platform.make_port
+        port = port_factory(self.parent, direction=self._dir,
+                            name=name, key=key,
+                            master=self, cls_key='clone')
+
+        self.clones.append(port)
+        return port
+
+    def remove_clone(self, port):
+        """
+        Remove a cloned port (from the list of clones only)
+        Remove the index 0 of the master port name (and key9 if there are no more clones left
+        """
+        self.clones.remove(port)
+        # Remove index from master port name if there are no more clones
+        if not self.clones:
+            self.name = self._base_name
+            # Also update key for none stream ports
+            if not self.key.isdigit():
+                self.key = self.name
+
+    def connections(self, enabled=None):
+        """Iterator over all connections to/from this port
+
+        enabled: None for all, True for enabled only, False for disabled only
+        """
+        for con in self.parent_flowgraph.connections:
+            if self in con and (enabled is None or enabled == con.enabled):
+                yield con
diff --git a/grc/core/schema_checker/__init__.py b/grc/core/schema_checker/__init__.py
new file mode 100644
index 0000000000..e92500ed4a
--- /dev/null
+++ b/grc/core/schema_checker/__init__.py
@@ -0,0 +1,5 @@
+from .validator import Validator
+
+from .block import BLOCK_SCHEME
+from .domain import DOMAIN_SCHEME
+from .flow_graph import FLOW_GRAPH_SCHEME
diff --git a/grc/core/schema_checker/block.py b/grc/core/schema_checker/block.py
new file mode 100644
index 0000000000..db8830fddf
--- /dev/null
+++ b/grc/core/schema_checker/block.py
@@ -0,0 +1,57 @@
+from .utils import Spec, expand, str_
+
+PARAM_SCHEME = expand(
+    base_key=str_,   # todo: rename/remove
+
+    id=str_,
+    label=str_,
+    category=str_,
+
+    dtype=str_,
+    default=object,
+
+    options=list,
+    option_labels=list,
+    option_attributes=Spec(types=dict, required=False, item_scheme=(str_, list)),
+
+    hide=str_,
+)
+PORT_SCHEME = expand(
+    label=str_,
+    domain=str_,
+
+    id=str_,
+    dtype=str_,
+    vlen=(int, str_),
+
+    multiplicity=(int, str_),
+    optional=(bool, int, str_),
+    hide=(bool, str_),
+)
+TEMPLATES_SCHEME = expand(
+    imports=str_,
+    var_make=str_,
+    make=str_,
+    callbacks=list,
+)
+BLOCK_SCHEME = expand(
+    id=Spec(types=str_, required=True, item_scheme=None),
+    label=str_,
+    category=(list, str_),
+    flags=(list, str_),
+
+    parameters=Spec(types=list, required=False, item_scheme=PARAM_SCHEME),
+    inputs=Spec(types=list, required=False, item_scheme=PORT_SCHEME),
+    outputs=Spec(types=list, required=False, item_scheme=PORT_SCHEME),
+
+    checks=(list, str_),
+    value=str_,
+
+    templates=Spec(types=dict, required=False, item_scheme=TEMPLATES_SCHEME),
+
+    documentation=str_,
+
+    file_format=Spec(types=int, required=True, item_scheme=None),
+
+    block_wrapper_path=str_,  # todo: rename/remove
+)
diff --git a/grc/core/schema_checker/domain.py b/grc/core/schema_checker/domain.py
new file mode 100644
index 0000000000..86c29ed3c6
--- /dev/null
+++ b/grc/core/schema_checker/domain.py
@@ -0,0 +1,16 @@
+from .utils import Spec, expand, str_
+
+DOMAIN_CONNECTION = expand(
+    type=Spec(types=list, required=True, item_scheme=None),
+    connect=str_,
+)
+
+DOMAIN_SCHEME = expand(
+    id=Spec(types=str_, required=True, item_scheme=None),
+    label=str_,
+    color=str_,
+    multiple_connections_per_input=bool,
+    multiple_connections_per_output=bool,
+
+    templates=Spec(types=list, required=False, item_scheme=DOMAIN_CONNECTION)
+)
\ No newline at end of file
diff --git a/grc/core/schema_checker/flow_graph.py b/grc/core/schema_checker/flow_graph.py
new file mode 100644
index 0000000000..746fbf4aa7
--- /dev/null
+++ b/grc/core/schema_checker/flow_graph.py
@@ -0,0 +1,23 @@
+from .utils import Spec, expand, str_
+
+OPTIONS_SCHEME = expand(
+    parameters=Spec(types=dict, required=False, item_scheme=(str_, str_)),
+    states=Spec(types=dict, required=False, item_scheme=(str_, str_)),
+)
+
+BLOCK_SCHEME = expand(
+    name=str_,
+    id=str_,
+    **OPTIONS_SCHEME
+)
+
+FLOW_GRAPH_SCHEME = expand(
+    options=Spec(types=dict, required=False, item_scheme=OPTIONS_SCHEME),
+    blocks=Spec(types=dict, required=False, item_scheme=BLOCK_SCHEME),
+    connections=list,
+
+    metadata=Spec(types=dict, required=True, item_scheme=expand(
+        file_format=Spec(types=int, required=True, item_scheme=None),
+    ))
+
+)
diff --git a/grc/core/schema_checker/utils.py b/grc/core/schema_checker/utils.py
new file mode 100644
index 0000000000..a9cf4c0175
--- /dev/null
+++ b/grc/core/schema_checker/utils.py
@@ -0,0 +1,27 @@
+import collections
+
+import six
+
+Spec = collections.namedtuple('Spec', 'types required item_scheme')
+
+
+def expand(**kwargs):
+    def expand_spec(spec):
+        if not isinstance(spec, Spec):
+            types_ = spec if isinstance(spec, tuple) else (spec,)
+            spec = Spec(types=types_, required=False, item_scheme=None)
+        elif not isinstance(spec.types, tuple):
+            spec = Spec(types=(spec.types,), required=spec.required,
+                        item_scheme=spec.item_scheme)
+        return spec
+    return {key: expand_spec(value) for key, value in kwargs.items()}
+
+
+str_ = six.string_types
+
+
+class Message(collections.namedtuple('Message', 'path type message')):
+    fmt = '{path}: {type}: {message}'
+
+    def __str__(self):
+        return self.fmt.format(**self._asdict())
diff --git a/grc/core/schema_checker/validator.py b/grc/core/schema_checker/validator.py
new file mode 100644
index 0000000000..ab4d43bc67
--- /dev/null
+++ b/grc/core/schema_checker/validator.py
@@ -0,0 +1,102 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import print_function
+
+import six
+
+from .utils import Message, Spec
+
+
+class Validator(object):
+
+    def __init__(self, scheme=None):
+        self._path = []
+        self.scheme = scheme
+        self.messages = []
+        self.passed = False
+
+    def run(self, data):
+        if not self.scheme:
+            return True
+        self._reset()
+        self._path.append('block')
+        self._check(data, self.scheme)
+        self._path.pop()
+        return self.passed
+
+    def _reset(self):
+        del self.messages[:]
+        del self._path[:]
+        self.passed = True
+
+    def _check(self, data, scheme):
+        if not data or not isinstance(data, dict):
+            self._error('Empty data or not a dict')
+            return
+        if isinstance(scheme, dict):
+            self._check_dict(data, scheme)
+        else:
+            self._check_var_key_dict(data, *scheme)
+
+    def _check_var_key_dict(self, data, key_type, value_scheme):
+        for key, value in six.iteritems(data):
+            if not isinstance(key, key_type):
+                self._error('Key type {!r} for {!r} not in valid types'.format(
+                    type(value).__name__, key))
+            if isinstance(value_scheme, Spec):
+                self._check_dict(value, value_scheme)
+            elif not isinstance(value, value_scheme):
+                self._error('Value type {!r} for {!r} not in valid types'.format(
+                    type(value).__name__, key))
+
+    def _check_dict(self, data, scheme):
+        for key, (types_, required, item_scheme) in six.iteritems(scheme):
+            try:
+                value = data[key]
+            except KeyError:
+                if required:
+                    self._error('Missing required entry {!r}'.format(key))
+                continue
+
+            self._check_value(value, types_, item_scheme, label=key)
+
+        for key in set(data).difference(scheme):
+            self._warn('Ignoring extra key {!r}'.format(key))
+
+    def _check_list(self, data, scheme, label):
+        for i, item in enumerate(data):
+            self._path.append('{}[{}]'.format(label, i))
+            self._check(item, scheme)
+            self._path.pop()
+
+    def _check_value(self, value, types_, item_scheme, label):
+        if not isinstance(value, types_):
+            self._error('Value type {!r} for {!r} not in valid types'.format(
+                type(value).__name__, label))
+        if item_scheme:
+            if isinstance(value, list):
+                self._check_list(value, item_scheme, label)
+            elif isinstance(value, dict):
+                self._check(value, item_scheme)
+
+    def _error(self, msg):
+        self.messages.append(Message('.'.join(self._path), 'error', msg))
+        self.passed = False
+
+    def _warn(self, msg):
+        self.messages.append(Message('.'.join(self._path), 'warn', msg))
diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py
index d095179a10..2d12e280b5 100644
--- a/grc/core/utils/__init__.py
+++ b/grc/core/utils/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2008-2015 Free Software Foundation, Inc.
+# Copyright 2016 Free Software Foundation, Inc.
 # This file is part of GNU Radio
 #
 # GNU Radio Companion is free software; you can redistribute it and/or
@@ -17,8 +17,4 @@
 
 from __future__ import absolute_import
 
-from . import expr_utils
-from . import epy_block_io
-from . import extract_docs
-
-from ._complexity import calculate_flowgraph_complexity
+from . import epy_block_io, expr_utils, extract_docs, flow_graph_complexity
diff --git a/grc/core/utils/_complexity.py b/grc/core/utils/_complexity.py
deleted file mode 100644
index c0f3ae9de4..0000000000
--- a/grc/core/utils/_complexity.py
+++ /dev/null
@@ -1,50 +0,0 @@
-
-def calculate_flowgraph_complexity(flowgraph):
-    """ Determines the complexity of a flowgraph """
-    dbal = 0
-    for block in flowgraph.blocks:
-        # Skip options block
-        if block.key == 'options':
-            continue
-
-        # Don't worry about optional sinks?
-        sink_list = [c for c in block.sinks if not c.get_optional()]
-        source_list = [c for c in block.sources if not c.get_optional()]
-        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(len(c.get_connections()) for c in sink_list) / max(sinks, 1.0)), 1.0)
-        source_multi = max(float(sum(len(c.get_connections()) for c in source_list) / max(sources, 1.0)), 1.0)
-        dbal += base * multi * sink_multi * source_multi
-
-    blocks = float(len(flowgraph.blocks))
-    connections = float(len(flowgraph.connections))
-    elements = blocks + connections
-    disabled_connections = sum(not c.enabled for c in flowgraph.connections)
-
-    variables = elements - blocks - connections
-    enabled = float(len(flowgraph.get_enabled_blocks()))
-
-    # Disabled multiplier
-    if enabled > 0:
-        disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05))
-    else:
-        disabled_multi = 1
-
-    # Connection multiplier (How many connections )
-    if (connections - disabled_connections) > 0:
-        conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05))
-    else:
-        conn_multi = 1
-
-    final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6)
-    return final
diff --git a/grc/core/utils/backports/__init__.py b/grc/core/utils/backports/__init__.py
new file mode 100644
index 0000000000..a24ee3ae01
--- /dev/null
+++ b/grc/core/utils/backports/__init__.py
@@ -0,0 +1,25 @@
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+from __future__ import absolute_import
+
+try:
+    from collections import ChainMap
+except ImportError:
+    from .chainmap import ChainMap
diff --git a/grc/core/utils/backports/chainmap.py b/grc/core/utils/backports/chainmap.py
new file mode 100644
index 0000000000..1f4f4a96fb
--- /dev/null
+++ b/grc/core/utils/backports/chainmap.py
@@ -0,0 +1,106 @@
+# from https://hg.python.org/cpython/file/default/Lib/collections/__init__.py
+
+from collections import MutableMapping
+
+
+class ChainMap(MutableMapping):
+    """ A ChainMap groups multiple dicts (or other mappings) together
+    to create a single, updateable view.
+
+    The underlying mappings are stored in a list.  That list is public and can
+    be accessed or updated using the *maps* attribute.  There is no other
+    state.
+
+    Lookups search the underlying mappings successively until a key is found.
+    In contrast, writes, updates, and deletions only operate on the first
+    mapping.
+
+    """
+
+    def __init__(self, *maps):
+        """Initialize a ChainMap by setting *maps* to the given mappings.
+        If no mappings are provided, a single empty dictionary is used.
+
+        """
+        self.maps = list(maps) or [{}]          # always at least one map
+
+    def __missing__(self, key):
+        raise KeyError(key)
+
+    def __getitem__(self, key):
+        for mapping in self.maps:
+            try:
+                return mapping[key]             # can't use 'key in mapping' with defaultdict
+            except KeyError:
+                pass
+        return self.__missing__(key)            # support subclasses that define __missing__
+
+    def get(self, key, default=None):
+        return self[key] if key in self else default
+
+    def __len__(self):
+        return len(set().union(*self.maps))     # reuses stored hash values if possible
+
+    def __iter__(self):
+        return iter(set().union(*self.maps))
+
+    def __contains__(self, key):
+        return any(key in m for m in self.maps)
+
+    def __bool__(self):
+        return any(self.maps)
+
+    def __repr__(self):
+        return '{0.__class__.__name__}({1})'.format(
+            self, ', '.join(map(repr, self.maps)))
+
+    @classmethod
+    def fromkeys(cls, iterable, *args):
+        """Create a ChainMap with a single dict created from the iterable."""
+        return cls(dict.fromkeys(iterable, *args))
+
+    def copy(self):
+        """New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]"""
+        return self.__class__(self.maps[0].copy(), *self.maps[1:])
+
+    __copy__ = copy
+
+    def new_child(self, m=None):                # like Django's Context.push()
+        """New ChainMap with a new map followed by all previous maps.
+        If no map is provided, an empty dict is used.
+        """
+        if m is None:
+            m = {}
+        return self.__class__(m, *self.maps)
+
+    @property
+    def parents(self):                          # like Django's Context.pop()
+        """New ChainMap from maps[1:]."""
+        return self.__class__(*self.maps[1:])
+
+    def __setitem__(self, key, value):
+        self.maps[0][key] = value
+
+    def __delitem__(self, key):
+        try:
+            del self.maps[0][key]
+        except KeyError:
+            raise KeyError('Key not found in the first mapping: {!r}'.format(key))
+
+    def popitem(self):
+        """Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty."""
+        try:
+            return self.maps[0].popitem()
+        except KeyError:
+            raise KeyError('No keys found in the first mapping.')
+
+    def pop(self, key, *args):
+        """Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0]."""
+        try:
+            return self.maps[0].pop(key, *args)
+        except KeyError:
+            raise KeyError('Key not found in the first mapping: {!r}'.format(key))
+
+    def clear(self):
+        """Clear maps[0], leaving maps[1:] intact."""
+        self.maps[0].clear()
diff --git a/grc/core/utils/backports/shlex.py b/grc/core/utils/backports/shlex.py
new file mode 100644
index 0000000000..6b620fa396
--- /dev/null
+++ b/grc/core/utils/backports/shlex.py
@@ -0,0 +1,47 @@
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+from __future__ import absolute_import
+
+import re
+import shlex
+
+# back port from python3
+
+_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
+
+
+def _shlex_quote(s):
+    """Return a shell-escaped version of the string *s*."""
+    if not s:
+        return "''"
+    if _find_unsafe(s) is None:
+        return s
+
+    # use single quotes, and put single quotes into double quotes
+    # the string $'b is then quoted as '$'"'"'b'
+    return "'" + s.replace("'", "'\"'\"'") + "'"
+
+
+if not hasattr(shlex, 'quote'):
+    quote = _shlex_quote
+else:
+    quote = shlex.quote
+
+split = shlex.split
diff --git a/grc/core/utils/descriptors/__init__.py b/grc/core/utils/descriptors/__init__.py
new file mode 100644
index 0000000000..80c5259230
--- /dev/null
+++ b/grc/core/utils/descriptors/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from ._lazy import lazy_property, nop_write
+
+from .evaluated import (
+    Evaluated,
+    EvaluatedEnum,
+    EvaluatedPInt,
+    EvaluatedFlag,
+    setup_names,
+)
diff --git a/grc/core/utils/descriptors/_lazy.py b/grc/core/utils/descriptors/_lazy.py
new file mode 100644
index 0000000000..a0cb126932
--- /dev/null
+++ b/grc/core/utils/descriptors/_lazy.py
@@ -0,0 +1,39 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+import functools
+
+
+class lazy_property(object):
+
+    def __init__(self, func):
+        self.func = func
+        functools.update_wrapper(self, func)
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        value = self.func(instance)
+        setattr(instance, self.func.__name__, value)
+        return value
+
+
+def nop_write(prop):
+    """Make this a property with a nop setter"""
+    def nop(self, value):
+        pass
+    return prop.setter(nop)
diff --git a/grc/core/utils/descriptors/evaluated.py b/grc/core/utils/descriptors/evaluated.py
new file mode 100644
index 0000000000..313cee5b96
--- /dev/null
+++ b/grc/core/utils/descriptors/evaluated.py
@@ -0,0 +1,112 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+
+class Evaluated(object):
+    def __init__(self, expected_type, default, name=None):
+        self.expected_type = expected_type
+        self.default = default
+
+        self.name = name or 'evaled_property_{}'.format(id(self))
+        self.eval_function = self.default_eval_func
+
+    @property
+    def name_raw(self):
+        return '_' + self.name
+
+    def default_eval_func(self, instance):
+        raw = getattr(instance, self.name_raw)
+        try:
+            value = instance.parent_block.evaluate(raw)
+        except Exception as error:
+            if raw:
+                instance.add_error_message("Failed to eval '{}': {}".format(raw, error))
+            return self.default
+
+        if not isinstance(value, self.expected_type):
+            instance.add_error_message("Can not cast evaluated value '{}' to type {}"
+                                       "".format(value, self.expected_type))
+            return self.default
+        # print(instance, self.name, raw, value)
+        return value
+
+    def __call__(self, func):
+        self.name = func.__name__
+        self.eval_function = func
+        return self
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        attribs = instance.__dict__
+        try:
+            value = attribs[self.name]
+        except KeyError:
+            value = attribs[self.name] = self.eval_function(instance)
+        return value
+
+    def __set__(self, instance, value):
+        attribs = instance.__dict__
+        value = value or self.default
+        if isinstance(value, str) and value.startswith('${') and value.endswith('}'):
+            attribs[self.name_raw] = value[2:-1].strip()
+        else:
+            attribs[self.name] = type(self.default)(value)
+
+    def __delete__(self, instance):
+        attribs = instance.__dict__
+        if self.name_raw in attribs:
+            attribs.pop(self.name, None)
+
+
+class EvaluatedEnum(Evaluated):
+    def __init__(self, allowed_values, default=None, name=None):
+        self.allowed_values = allowed_values if isinstance(allowed_values, (list, tuple)) else \
+            allowed_values.split()
+        default = default if default is not None else self.allowed_values[0]
+        super(EvaluatedEnum, self).__init__(str, default, name)
+
+    def default_eval_func(self, instance):
+        value = super(EvaluatedEnum, self).default_eval_func(instance)
+        if value not in self.allowed_values:
+            instance.add_error_message("Value '{}' not in allowed values".format(value))
+            return self.default
+        return value
+
+
+class EvaluatedPInt(Evaluated):
+    def __init__(self, name=None):
+        super(EvaluatedPInt, self).__init__(int, 1, name)
+
+    def default_eval_func(self, instance):
+        value = super(EvaluatedPInt, self).default_eval_func(instance)
+        if value < 1:
+            # todo: log
+            return self.default
+        return value
+
+
+class EvaluatedFlag(Evaluated):
+    def __init__(self, name=None):
+        super(EvaluatedFlag, self).__init__((bool, int), False, name)
+
+
+def setup_names(cls):
+    for name, attrib in cls.__dict__.items():
+        if isinstance(attrib, Evaluated):
+            attrib.name = name
+    return cls
diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py
index cc03e9cb1c..427585e93c 100644
--- a/grc/core/utils/expr_utils.py
+++ b/grc/core/utils/expr_utils.py
@@ -23,17 +23,105 @@ import string
 
 import six
 
+
+def expr_replace(expr, replace_dict):
+    """
+    Search for vars in the expression and add the prepend.
+
+    Args:
+        expr: an expression string
+        replace_dict: a dict of find:replace
+
+    Returns:
+        a new expression with the prepend
+    """
+    expr_splits = _expr_split(expr, var_chars=VAR_CHARS + '.')
+    for i, es in enumerate(expr_splits):
+        if es in list(replace_dict.keys()):
+            expr_splits[i] = replace_dict[es]
+    return ''.join(expr_splits)
+
+
+def get_variable_dependencies(expr, vars):
+    """
+    Return a set of variables used in this expression.
+
+    Args:
+        expr: an expression string
+        vars: a list of variable names
+
+    Returns:
+        a subset of vars used in the expression
+    """
+    expr_toks = _expr_split(expr)
+    return set(v for v in vars if v in expr_toks)
+
+
+def sort_objects(objects, get_id, get_expr):
+    """
+    Sort a list of objects according to their expressions.
+
+    Args:
+        objects: the list of objects to sort
+        get_id: the function to extract an id from the object
+        get_expr: the function to extract an expression from the object
+
+    Returns:
+        a list of sorted objects
+    """
+    id2obj = {get_id(obj): obj for obj in objects}
+    # Map obj id to expression code
+    id2expr = {get_id(obj): get_expr(obj) for obj in objects}
+    # Sort according to dependency
+    sorted_ids = _sort_variables(id2expr)
+    # Return list of sorted objects
+    return [id2obj[id] for id in sorted_ids]
+
+
+import ast
+
+
+def dependencies(expr, names=None):
+    node = ast.parse(expr, mode='eval')
+    used_ids = frozenset([n.id for n in ast.walk(node) if isinstance(n, ast.Name)])
+    return used_ids & names if names else used_ids
+
+
+def sort_objects2(objects, id_getter, expr_getter, check_circular=True):
+    known_ids = {id_getter(obj) for obj in objects}
+
+    def dependent_ids(obj):
+        deps = dependencies(expr_getter(obj))
+        return [id_ if id_ in deps else None for id_ in known_ids]
+
+    objects = sorted(objects, key=dependent_ids)
+
+    if check_circular:  # walk var defines step by step
+        defined_ids = set()  # variables defined so far
+        for obj in objects:
+            deps = dependencies(expr_getter(obj), known_ids)
+            if not defined_ids.issuperset(deps):  # can't have an undefined dep
+                raise RuntimeError(obj, deps, defined_ids)
+            defined_ids.add(id_getter(obj))  # define this one
+
+    return objects
+
+
+
+
 VAR_CHARS = string.ascii_letters + string.digits + '_'
 
 
-class graph(object):
+class _graph(object):
     """
     Simple graph structure held in a dictionary.
     """
 
-    def __init__(self): self._graph = dict()
+    def __init__(self):
+        self._graph = dict()
 
-    def __str__(self): return str(self._graph)
+    def __str__(self):
+        return str(self._graph)
 
     def add_node(self, node_key):
         if node_key in self._graph:
@@ -61,7 +149,7 @@ class graph(object):
         return self._graph[node_key]
 
 
-def expr_split(expr, var_chars=VAR_CHARS):
+def _expr_split(expr, var_chars=VAR_CHARS):
     """
     Split up an expression by non alphanumeric characters, including underscore.
     Leave strings in-tact.
@@ -93,40 +181,7 @@ def expr_split(expr, var_chars=VAR_CHARS):
     return [t for t in toks if t]
 
 
-def expr_replace(expr, replace_dict):
-    """
-    Search for vars in the expression and add the prepend.
-
-    Args:
-        expr: an expression string
-        replace_dict: a dict of find:replace
-
-    Returns:
-        a new expression with the prepend
-    """
-    expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.')
-    for i, es in enumerate(expr_splits):
-        if es in list(replace_dict.keys()):
-            expr_splits[i] = replace_dict[es]
-    return ''.join(expr_splits)
-
-
-def get_variable_dependencies(expr, vars):
-    """
-    Return a set of variables used in this expression.
-
-    Args:
-        expr: an expression string
-        vars: a list of variable names
-
-    Returns:
-        a subset of vars used in the expression
-    """
-    expr_toks = expr_split(expr)
-    return set(v for v in vars if v in expr_toks)
-
-
-def get_graph(exprs):
+def _get_graph(exprs):
     """
     Get a graph representing the variable dependencies
 
@@ -138,7 +193,7 @@ def get_graph(exprs):
     """
     vars = list(exprs.keys())
     # Get dependencies for each expression, load into graph
-    var_graph = graph()
+    var_graph = _graph()
     for var in vars:
         var_graph.add_node(var)
     for var, expr in six.iteritems(exprs):
@@ -148,7 +203,7 @@ def get_graph(exprs):
     return var_graph
 
 
-def sort_variables(exprs):
+def _sort_variables(exprs):
     """
     Get a list of variables in order of dependencies.
 
@@ -159,7 +214,7 @@ def sort_variables(exprs):
         a list of variable names
     @throws Exception circular dependencies
     """
-    var_graph = get_graph(exprs)
+    var_graph = _get_graph(exprs)
     sorted_vars = list()
     # Determine dependency order
     while var_graph.get_nodes():
@@ -173,24 +228,3 @@ def sort_variables(exprs):
         for var in indep_vars:
             var_graph.remove_node(var)
     return reversed(sorted_vars)
-
-
-def sort_objects(objects, get_id, get_expr):
-    """
-    Sort a list of objects according to their expressions.
-
-    Args:
-        objects: the list of objects to sort
-        get_id: the function to extract an id from the object
-        get_expr: the function to extract an expression from the object
-
-    Returns:
-        a list of sorted objects
-    """
-    id2obj = dict([(get_id(obj), obj) for obj in objects])
-    # Map obj id to expression code
-    id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects])
-    # Sort according to dependency
-    sorted_ids = sort_variables(id2expr)
-    # Return list of sorted objects
-    return [id2obj[id] for id in sorted_ids]
diff --git a/grc/core/utils/extract_docs.py b/grc/core/utils/extract_docs.py
index cff8a81099..7688f98de5 100644
--- a/grc/core/utils/extract_docs.py
+++ b/grc/core/utils/extract_docs.py
@@ -98,8 +98,7 @@ def docstring_from_make(key, imports, make):
         if '$' in blk_cls:
             raise ValueError('Not an identifier')
         ns = dict()
-        for _import in imports:
-            exec(_import.strip(), ns)
+        exec(imports.strip(), ns)
         blk = eval(blk_cls, ns)
         doc_strings = {key: blk.__doc__}
 
@@ -166,7 +165,8 @@ class SubprocessLoader(object):
             else:
                 break  # normal termination, return
             finally:
-                self._worker.terminate()
+                if self._worker:
+                    self._worker.terminate()
         else:
             print("Warning: docstring loader crashed too often", file=sys.stderr)
         self._thread = None
@@ -277,7 +277,7 @@ elif __name__ == '__main__':
         print(key)
         for match, doc in six.iteritems(docs):
             print('-->', match)
-            print(doc.strip())
+            print(str(doc).strip())
             print()
         print()
 
diff --git a/grc/core/utils/flow_graph_complexity.py b/grc/core/utils/flow_graph_complexity.py
new file mode 100644
index 0000000000..d06f04ab5f
--- /dev/null
+++ b/grc/core/utils/flow_graph_complexity.py
@@ -0,0 +1,50 @@
+
+def calculate(flowgraph):
+    """ Determines the complexity of a flowgraph """
+    dbal = 0
+    for block in flowgraph.blocks:
+        # Skip options block
+        if block.key == 'options':
+            continue
+
+        # Don't worry about optional sinks?
+        sink_list = [c for c in block.sinks if not c.optional]
+        source_list = [c for c in block.sources if not c.optional]
+        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(len(c.connections()) for c in sink_list) / max(sinks, 1.0)), 1.0)
+        source_multi = max(float(sum(len(c.connections()) for c in source_list) / max(sources, 1.0)), 1.0)
+        dbal += base * multi * sink_multi * source_multi
+
+    blocks = float(len(flowgraph.blocks))
+    connections = float(len(flowgraph.connections))
+    elements = blocks + connections
+    disabled_connections = sum(not c.enabled for c in flowgraph.connections)
+
+    variables = elements - blocks - connections
+    enabled = float(len(flowgraph.get_enabled_blocks()))
+
+    # Disabled multiplier
+    if enabled > 0:
+        disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05))
+    else:
+        disabled_multi = 1
+
+    # Connection multiplier (How many connections )
+    if (connections - disabled_connections) > 0:
+        conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05))
+    else:
+        conn_multi = 1
+
+    final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6)
+    return final
diff --git a/grc/core/utils/shlex.py b/grc/core/utils/shlex.py
deleted file mode 100644
index 6b620fa396..0000000000
--- a/grc/core/utils/shlex.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright 2016 Free Software Foundation, Inc.
-#
-# This file is part of GNU Radio
-#
-# GNU Radio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3, or (at your option)
-# any later version.
-#
-# GNU Radio is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with GNU Radio; see the file COPYING.  If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-
-from __future__ import absolute_import
-
-import re
-import shlex
-
-# back port from python3
-
-_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
-
-
-def _shlex_quote(s):
-    """Return a shell-escaped version of the string *s*."""
-    if not s:
-        return "''"
-    if _find_unsafe(s) is None:
-        return s
-
-    # use single quotes, and put single quotes into double quotes
-    # the string $'b is then quoted as '$'"'"'b'
-    return "'" + s.replace("'", "'\"'\"'") + "'"
-
-
-if not hasattr(shlex, 'quote'):
-    quote = _shlex_quote
-else:
-    quote = shlex.quote
-
-split = shlex.split
diff --git a/grc/gui/Application.py b/grc/gui/Application.py
index c1456c3a8d..b4df4d172e 100644
--- a/grc/gui/Application.py
+++ b/grc/gui/Application.py
@@ -20,18 +20,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import, print_function
 
+import logging
 import os
 import subprocess
-import logging
 
 from gi.repository import Gtk, Gio, GLib, GObject
 
-from . import Dialogs, Actions, Executor, FileDialogs, Utils, Bars
+from . import Constants, Dialogs, Actions, Executor, FileDialogs, Utils, Bars
+
 from .MainWindow import MainWindow
-from .ParserErrorsDialog import ParserErrorsDialog
+# from .ParserErrorsDialog import ParserErrorsDialog
 from .PropsDialog import PropsDialog
 
-from ..core import ParseXML, Messages
+from ..core import Messages
 
 
 log = logging.getLogger(__name__)
@@ -212,9 +213,9 @@ class Application(Gtk.Application):
             main.update_panel_visibility(main.CONSOLE, Actions.TOGGLE_CONSOLE_WINDOW.get_active())
             main.update_panel_visibility(main.VARIABLES, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR.get_active())
 
-            if ParseXML.xml_failures:
-                Messages.send_xml_errors_if_any(ParseXML.xml_failures)
-                Actions.XML_PARSER_ERRORS_DISPLAY.set_enabled(True)
+            #if ParseXML.xml_failures:
+            #    Messages.send_xml_errors_if_any(ParseXML.xml_failures)
+            #    Actions.XML_PARSER_ERRORS_DISPLAY.set_enabled(True)
 
             # Force an update on the current page to match loaded preferences.
             # In the future, change the __init__ order to load preferences first
@@ -284,11 +285,11 @@ class Application(Gtk.Application):
                             for param in block.params.values():
                                 for variable in flow_graph.get_variables():
                                     # If a block parameter exists that is a variable, create a parameter for it
-                                    if param.get_value() == variable.get_id():
+                                    if param.get_value() == variable.name:
                                         params.append(param.get_value())
                                 for flow_param in flow_graph.get_parameters():
                                     # If a block parameter exists that is a parameter, create a parameter for it
-                                    if param.get_value() == flow_param.get_id():
+                                    if param.get_value() == flow_param.name:
                                         params.append(param.get_value())
 
 
@@ -302,15 +303,15 @@ class Application(Gtk.Application):
                             for connection in block.connections:
 
                                 # Get id of connected blocks
-                                source_id = connection.source_block.get_id()
-                                sink_id = connection.sink_block.get_id()
+                                source_id = connection.source_block.name
+                                sink_id = connection.sink_block.name
 
                                 # If connected block is not in the list of selected blocks create a pad for it
                                 if flow_graph.get_block(source_id) not in flow_graph.selected_blocks():
-                                    pads.append({'key': connection.sink_port.key, 'coord': connection.source_port.coordinate, 'block_id' : block.get_id(), 'direction': 'source'})
+                                    pads.append({'key': connection.sink_port.key, 'coord': connection.source_port.coordinate, 'block_id' : block.name, 'direction': 'source'})
 
                                 if flow_graph.get_block(sink_id) not in flow_graph.selected_blocks():
-                                    pads.append({'key': connection.source_port.key, 'coord': connection.sink_port.coordinate, 'block_id' : block.get_id(), 'direction': 'sink'})
+                                    pads.append({'key': connection.source_port.key, 'coord': connection.sink_port.coordinate, 'block_id' : block.name, 'direction': 'sink'})
 
 
                         # Copy the selected blocks and paste them into a new page
@@ -324,10 +325,10 @@ class Application(Gtk.Application):
 
                         # Set flow graph to heir block type
                         top_block  = flow_graph.get_block("top_block")
-                        top_block.get_param('generate_options').set_value('hb')
+                        top_block.params['generate_options'].set_value('hb')
 
                         # this needs to be a unique name
-                        top_block.get_param('id').set_value('new_heir')
+                        top_block.params['id'].set_value('new_heir')
 
                         # Remove the default samp_rate variable block that is created
                         remove_me  = flow_graph.get_block("samp_rate")
@@ -339,7 +340,7 @@ class Application(Gtk.Application):
                         for param in params:
                             param_id = flow_graph.add_new_block('parameter',(x_pos,10))
                             param_block = flow_graph.get_block(param_id)
-                            param_block.get_param('id').set_value(param)
+                            param_block.params['id'].set_value(param)
                             x_pos = x_pos + 100
 
                         for pad in pads:
@@ -357,10 +358,10 @@ class Application(Gtk.Application):
                                 source = source_block.get_source(pad['key'])
 
                                 # Ensure the port types match
-                                while pad_sink.get_type() != source.get_type():
+                                while pad_sink.dtype != source.dtype:
 
                                     # Special case for some blocks that have non-standard type names, e.g. uhd
-                                    if pad_sink.get_type() == 'complex' and source.get_type() == 'fc32':
+                                    if pad_sink.dtype == 'complex' and source.dtype == 'fc32':
                                         break;
                                     pad_block.type_controller_modify(1)
 
@@ -378,9 +379,9 @@ class Application(Gtk.Application):
                                 sink = sink_block.get_sink(pad['key'])
 
                                 # Ensure the port types match
-                                while sink.get_type() != pad_source.get_type():
+                                while sink.dtype != pad_source.dtype:
                                     # Special case for some blocks that have non-standard type names, e.g. uhd
-                                    if pad_source.get_type() == 'complex' and sink.get_type() == 'fc32':
+                                    if pad_source.dtype == 'complex' and sink.dtype == 'fc32':
                                         break;
                                     pad_block.type_controller_modify(1)
 
@@ -567,7 +568,8 @@ class Application(Gtk.Application):
         # View Parser Errors
         ##################################################
         elif action == Actions.XML_PARSER_ERRORS_DISPLAY:
-            ParserErrorsDialog(ParseXML.xml_failures).run()
+            # ParserErrorsDialog(ParseXML.xml_failures).run()
+            pass
         ##################################################
         # Undo/Redo
         ##################################################
@@ -616,7 +618,7 @@ class Application(Gtk.Application):
             #otherwise try to save
             else:
                 try:
-                    ParseXML.to_file(flow_graph.export_data(), page.file_path)
+                    self.platform.save_flow_graph(page.file_path, flow_graph)
                     flow_graph.grc_file_path = page.file_path
                     page.saved = True
                 except IOError:
@@ -639,14 +641,14 @@ class Application(Gtk.Application):
                 else:
                     dup_file_path = page.file_path
                     dup_file_name = '.'.join(dup_file_path.split('.')[:-1]) + "_copy" # Assuming .grc extension at the end of file_path
-                    dup_file_path_temp = dup_file_name+'.grc'
+                    dup_file_path_temp = dup_file_name + Constants.FILE_EXTENSION
                     count = 1
                     while os.path.exists(dup_file_path_temp):
-                        dup_file_path_temp = dup_file_name+'('+str(count)+').grc'
+                        dup_file_path_temp = '{}({}){}'.format(dup_file_name, count, Constants.FILE_EXTENSION)
                         count += 1
                     dup_file_path_user = FileDialogs.SaveFlowGraph(main, dup_file_path_temp).run()
                     if dup_file_path_user is not None:
-                        ParseXML.to_file(flow_graph.export_data(), dup_file_path_user)
+                        self.platform.save_flow_graph(dup_file_path_user, flow_graph)
                         Messages.send('Saved Copy to: "' + dup_file_path_user + '"\n')
             except IOError:
                 Messages.send_fail_save("Can not create a copy of the flowgraph\n")
@@ -707,11 +709,13 @@ class Application(Gtk.Application):
         elif action == Actions.PAGE_CHANGE:  # pass and run the global actions
             pass
         elif action == Actions.RELOAD_BLOCKS:
-            self.platform.build_block_library()
+            self.platform.build_library()
             main.btwin.repopulate()
-            Actions.XML_PARSER_ERRORS_DISPLAY.set_enabled(bool(
-                ParseXML.xml_failures))
-            Messages.send_xml_errors_if_any(ParseXML.xml_failures)
+
+            #todo: implement parser error dialog for YAML
+            #Actions.XML_PARSER_ERRORS_DISPLAY.set_enabled(bool(ParseXML.xml_failures))
+            #Messages.send_xml_errors_if_any(ParseXML.xml_failures)
+
             # Force a redraw of the graph, by getting the current state and re-importing it
             main.update_pages()
 
@@ -721,8 +725,9 @@ class Application(Gtk.Application):
             main.btwin.search_entry.grab_focus()
         elif action == Actions.OPEN_HIER:
             for b in flow_graph.selected_blocks():
-                if b._grc_source:
-                    main.new_page(b._grc_source, show=True)
+                grc_source = b.extra_data.get('grc_source', '')
+                if grc_source:
+                    main.new_page(b.grc_source, show=True)
         elif action == Actions.BUSSIFY_SOURCES:
             for b in flow_graph.selected_blocks():
                 b.bussify('source')
@@ -781,8 +786,8 @@ class Application(Gtk.Application):
 
         Actions.BLOCK_CREATE_HIER.set_enabled(bool(selected_blocks))
         Actions.OPEN_HIER.set_enabled(bool(selected_blocks))
-        Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks))
-        Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks))
+        #Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks))
+        #Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks))
         Actions.RELOAD_BLOCKS.enable()
         Actions.FIND_BLOCKS.enable()
 
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index 8504200459..3b9b8642f9 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -172,7 +172,7 @@ class BlockTreeWindow(Gtk.VBox):
         # add block
         iter_ = treestore.insert_before(categories[category], None)
         treestore.set_value(iter_, KEY_INDEX, block.key)
-        treestore.set_value(iter_, NAME_INDEX, block.name)
+        treestore.set_value(iter_, NAME_INDEX, block.label)
         treestore.set_value(iter_, DOC_INDEX, _format_doc(block.documentation))
 
     def update_docs(self):
@@ -225,7 +225,7 @@ class BlockTreeWindow(Gtk.VBox):
             self.expand_module_in_tree()
         else:
             matching_blocks = [b for b in list(self.platform.blocks.values())
-                               if key in b.key.lower() or key in b.name.lower()]
+                               if key in b.key.lower() or key in b.label.lower()]
 
             self.treestore_search.clear()
             self._categories_search = {tuple(): None}
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index 51213a6154..45c9095313 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -241,15 +241,15 @@ class ErrorsDialog(Gtk.Dialog):
         self.store.clear()
         for element, message in flowgraph.iter_error_messages():
             if element.is_block:
-                src, aspect = element.get_id(), ''
+                src, aspect = element.name, ''
             elif element.is_connection:
-                src = element.source_block.get_id()
-                aspect = "Connection to '{}'".format(element.sink_block.get_id())
+                src = element.source_block.name
+                aspect = "Connection to '{}'".format(element.sink_block.name)
             elif element.is_port:
-                src = element.parent_block.get_id()
+                src = element.parent_block.name
                 aspect = "{} '{}'".format('Sink' if element.is_sink else 'Source', element.name)
             elif element.is_param:
-                src = element.parent_block.get_id()
+                src = element.parent_block.name
                 aspect = "Param '{}'".format(element.name)
             else:
                 src = aspect = ''
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index f913d63966..01502b38f9 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -249,7 +249,7 @@ class MainWindow(Gtk.ApplicationWindow):
             return
         try: #try to load from file
             if file_path: Messages.send_start_load(file_path)
-            flow_graph = self._platform.get_new_flow_graph()
+            flow_graph = self._platform.make_flow_graph()
             flow_graph.grc_file_path = file_path
             #print flow_graph
             page = Page(
diff --git a/grc/gui/ParamWidgets.py b/grc/gui/ParamWidgets.py
index 71cb1b7a7d..18d1da736b 100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@ -88,11 +88,11 @@ class InputParam(Gtk.HBox):
         Set the markup, color, tooltip, show/hide.
         """
         self.label.set_markup(self.param.format_label_markup(self._have_pending_changes))
-        self.set_color('dtype_' + self.param.get_type())
+        self.set_color('dtype_' + self.param.dtype)
 
         self.set_tooltip_text(self.param.format_tooltip_text())
 
-        if self.param.get_hide() == 'all':
+        if self.param.hide == 'all':
             self.hide()
         else:
             self.show_all()
@@ -214,19 +214,17 @@ class EnumParam(InputParam):
     def __init__(self, *args, **kwargs):
         InputParam.__init__(self, *args, **kwargs)
         self._input = Gtk.ComboBoxText()
-        for option_name in self.param.options_names:
+        for option_name in self.param.options.values():
             self._input.append_text(option_name)
 
-        value = self.param.get_value()
-        active_index = self.param.options.index(value)
-        self._input.set_active(active_index)
-
+        self.param_values = list(self.param.options)
+        self._input.set_active(self.param_values.index(self.param.get_value()))
         self._input.connect('changed', self._editing_callback)
         self._input.connect('changed', self._apply_change)
         self.pack_start(self._input, False, False, 0)
 
     def get_text(self):
-        return self.param.options[self._input.get_active()]
+        return self.param_values[self._input.get_active()]
 
     def set_tooltip_text(self, text):
         self._input.set_tooltip_text(text)
@@ -238,13 +236,13 @@ class EnumEntryParam(InputParam):
     def __init__(self, *args, **kwargs):
         InputParam.__init__(self, *args, **kwargs)
         self._input = Gtk.ComboBoxText.new_with_entry()
-        for option_name in self.param.options_names:
+        for option_name in self.param.options.values():
             self._input.append_text(option_name)
 
+        self.param_values = list(self.param.options)
         value = self.param.get_value()
         try:
-            active_index = self.param.options.index(value)
-            self._input.set_active(active_index)
+            self._input.set_active(self.param_values.index(value))
         except ValueError:
             self._input.set_active(-1)
             self._input.get_child().set_text(value)
@@ -263,7 +261,7 @@ class EnumEntryParam(InputParam):
         if self.has_custom_value:
             return self._input.get_child().get_text()
         else:
-            return self.param.options[self._input.get_active()]
+            return self.param_values[self._input.get_active()]
 
     def set_tooltip_text(self, text):
         if self.has_custom_value:  # custom entry
@@ -304,16 +302,16 @@ class FileParam(EntryParam):
             dirname = os.getcwd()  # fix bad paths
 
         #build the dialog
-        if self.param.get_type() == 'file_open':
+        if self.param.dtype == 'file_open':
             file_dialog = Gtk.FileChooserDialog('Open a Data File...', None,
                 Gtk.FileChooserAction.OPEN, ('gtk-cancel',Gtk.ResponseType.CANCEL,'gtk-open',Gtk.ResponseType.OK))
-        elif self.param.get_type() == 'file_save':
+        elif self.param.dtype == 'file_save':
             file_dialog = Gtk.FileChooserDialog('Save a Data File...', None,
                 Gtk.FileChooserAction.SAVE, ('gtk-cancel',Gtk.ResponseType.CANCEL, 'gtk-save',Gtk.ResponseType.OK))
             file_dialog.set_do_overwrite_confirmation(True)
             file_dialog.set_current_name(basename) #show the current filename
         else:
-            raise ValueError("Can't open file chooser dialog for type " + repr(self.param.get_type()))
+            raise ValueError("Can't open file chooser dialog for type " + repr(self.param.dtype))
         file_dialog.set_current_folder(dirname) #current directory
         file_dialog.set_select_multiple(False)
         file_dialog.set_local_only(True)
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index aeade75d78..2a38bc619e 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -24,7 +24,8 @@ import os
 
 from .Config import Config
 from . import canvas
-from ..core.Platform import Platform as CorePlatform
+from ..core.platform import Platform as CorePlatform
+from ..core.utils.backports import ChainMap
 
 
 class Platform(CorePlatform):
@@ -61,8 +62,15 @@ class Platform(CorePlatform):
     Config = Config
     FlowGraph = canvas.FlowGraph
     Connection = canvas.Connection
-    block_classes = {key: canvas.Block.make_cls_with_base(cls)
-                     for key, cls in CorePlatform.block_classes.items()}
+
+    def new_block_class(self, block_id, **data):
+        cls = CorePlatform.new_block_class(self, block_id, **data)
+        return canvas.Block.make_cls_with_base(cls)
+
+    block_classes_build_in = {key: canvas.Block.make_cls_with_base(cls)
+                              for key, cls in CorePlatform.block_classes_build_in.items()}
+    block_classes = ChainMap({}, block_classes_build_in)
+
     port_classes = {key: canvas.Port.make_cls_with_base(cls)
                     for key, cls in CorePlatform.port_classes.items()}
     param_classes = {key: canvas.Param.make_cls_with_base(cls)
diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py
index 5f39770e78..9ce9bf2701 100644
--- a/grc/gui/PropsDialog.py
+++ b/grc/gui/PropsDialog.py
@@ -40,7 +40,7 @@ class PropsDialog(Gtk.Dialog):
 
         Gtk.Dialog.__init__(
             self,
-            title='Properties: ' + block.name,
+            title='Properties: ' + block.label,
             transient_for=parent,
             modal=True,
             destroy_with_parent=True,
@@ -70,7 +70,7 @@ class PropsDialog(Gtk.Dialog):
 
         # Params boxes for block parameters
         self._params_boxes = []
-        self._build_param_tab_boxes(block.params)
+        self._build_param_tab_boxes()
 
         # Docs for the block
         self._docs_text_display = doc_view = SimpleTextDisplay()
@@ -109,26 +109,26 @@ class PropsDialog(Gtk.Dialog):
         self.connect('response', self._handle_response)
         self.show_all()  # show all (performs initial gui update)
 
-    def _build_param_tab_boxes(self, params):
-        tab_labels = (p.tab_label for p in self._block.params.values())
+    def _build_param_tab_boxes(self):
+        categories = (p.category for p in self._block.params.values())
 
-        def unique_tab_labels():
+        def unique_categories():
             seen = {Constants.DEFAULT_PARAM_TAB}
             yield Constants.DEFAULT_PARAM_TAB
-            for tab_label in tab_labels:
-                if tab_label in seen:
+            for cat in categories:
+                if cat in seen:
                     continue
-                yield tab_label
-                seen.add(tab_label)
+                yield cat
+                seen.add(cat)
 
-        for tab in unique_tab_labels():
+        for category in unique_categories():
             label = Gtk.Label()
             vbox = Gtk.VBox()
             scroll_box = Gtk.ScrolledWindow()
             scroll_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
             scroll_box.add(vbox)
             self.notebook.append_page(scroll_box, label)
-            self._params_boxes.append((tab, label, vbox))
+            self._params_boxes.append((category, label, vbox))
 
     def _params_changed(self):
         """
@@ -143,7 +143,7 @@ class PropsDialog(Gtk.Dialog):
         """
         old_hash = self._hash
         new_hash = self._hash = hash(tuple(
-            (hash(param), param.name, param.get_type(), param.get_hide() == 'all',)
+            (hash(param), param.name, param.dtype, param.hide == 'all',)
             for param in self._block.params.values()
         ))
         return new_hash != old_hash
@@ -171,7 +171,7 @@ class PropsDialog(Gtk.Dialog):
         """
         if force or self._params_changed():
             # hide params box before changing
-            for tab, label, vbox in self._params_boxes:
+            for category, label, vbox in self._params_boxes:
                 vbox.hide()
                 # empty the params box
                 for child in vbox.get_children():
@@ -181,7 +181,7 @@ class PropsDialog(Gtk.Dialog):
                 box_all_valid = True
                 for param in self._block.params.values():
                     # todo: why do we even rebuild instead of really hiding params?
-                    if param.get_tab_label() != tab or param.get_hide() == 'all':
+                    if param.category != category or param.hide == 'all':
                         continue
                     box_all_valid = box_all_valid and param.is_valid()
 
@@ -190,7 +190,7 @@ class PropsDialog(Gtk.Dialog):
                     vbox.pack_start(input_widget, input_widget.expand, True, 1)
 
                 label.set_markup('<span foreground="{color}">{name}</span>'.format(
-                    color='black' if box_all_valid else 'red', name=Utils.encode(tab)
+                    color='black' if box_all_valid else 'red', name=Utils.encode(category)
                 ))
                 vbox.show()  # show params box with new params
 
@@ -225,7 +225,7 @@ class PropsDialog(Gtk.Dialog):
             buf.insert(pos, '\n')
 
         # if given the current parameters an exact match can be made
-        block_constructor = self._block.get_make().rsplit('.', 2)[-1]
+        block_constructor = self._block.templates.render('make').rsplit('.', 2)[-1]
         block_class = block_constructor.partition('(')[0].strip()
         if block_class in docstrings:
             docstrings = {block_class: docstrings[block_class]}
@@ -246,9 +246,9 @@ class PropsDialog(Gtk.Dialog):
         key = block.key
 
         if key == 'epy_block':
-            src = block.get_param('_source_code').get_value()
+            src = block.params['_source_code'].get_value()
         elif key == 'epy_module':
-            src = block.get_param('source_code').get_value()
+            src = block.params['source_code'].get_value()
         else:
             src = ''
 
@@ -259,12 +259,12 @@ class PropsDialog(Gtk.Dialog):
             buf.insert(buf.get_end_iter(), text)
 
         buf.delete(buf.get_start_iter(), buf.get_end_iter())
-        insert('# Imports\n', '\n'.join(block.get_imports()))
+        insert('# Imports\n', block.templates.render('imports').strip('\n'))
         if block.is_variable:
-            insert('\n\n# Variables\n', block.get_var_make())
-        insert('\n\n# Blocks\n', block.get_make())
+            insert('\n\n# Variables\n', block.templates.render('var_make'))
+        insert('\n\n# Blocks\n', block.templates.render('make'))
         if src:
-            insert('\n\n# External Code ({}.py)\n'.format(block.get_id()), src)
+            insert('\n\n# External Code ({}.py)\n'.format(block.name), src)
 
     def _handle_key_press(self, widget, event):
         close_dialog = (
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index e310676420..c179c8bc84 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -174,13 +174,13 @@ class VariableEditor(Gtk.VBox):
         # Block specific values
         if block:
             if block.key == 'import':
-                value = block.get_param('import').get_value()
+                value = block.params['import'].get_value()
             elif block.key != "variable":
                 value = "<Open Properties>"
                 sp('editable', False)
                 sp('foreground', '#0D47A1')
             else:
-                value = block.get_param('value').get_value()
+                value = block.params['value'].get_value()
 
             # Check if there are errors in the blocks.
             # Show the block error as a tooltip
@@ -192,7 +192,7 @@ class VariableEditor(Gtk.VBox):
             else:
                 # Evaluate and show the value (if it is a variable)
                 if block.key == "variable":
-                    evaluated = str(block.get_param('value').evaluate())
+                    evaluated = str(block.params['value'].evaluate())
                     self.set_tooltip_text(evaluated)
         # Always set the text value.
         sp('text', value)
@@ -227,21 +227,21 @@ class VariableEditor(Gtk.VBox):
         imports = self.treestore.append(None, [None, 'Imports'])
         variables = self.treestore.append(None, [None, 'Variables'])
         for block in self._imports:
-            self.treestore.append(imports, [block, block.get_param('id').get_value()])
-        for block in sorted(self._variables, key=lambda v: v.get_id()):
-            self.treestore.append(variables, [block, block.get_param('id').get_value()])
+            self.treestore.append(imports, [block, block.params['id'].get_value()])
+        for block in sorted(self._variables, key=lambda v: v.name):
+            self.treestore.append(variables, [block, block.params['id'].get_value()])
 
     def _handle_name_edited_cb(self, cell, path, new_text):
         block = self.treestore[path][BLOCK_INDEX]
-        block.get_param('id').set_value(new_text)
+        block.params['id'].set_value(new_text)
         Actions.VARIABLE_EDITOR_UPDATE()
 
     def _handle_value_edited_cb(self, cell, path, new_text):
         block = self.treestore[path][BLOCK_INDEX]
         if block.is_import:
-            block.get_param('import').set_value(new_text)
+            block.params['import'].set_value(new_text)
         else:
-            block.get_param('value').set_value(new_text)
+            block.params['value'].set_value(new_text)
         Actions.VARIABLE_EDITOR_UPDATE()
 
     def handle_action(self, item, key, event=None):
@@ -258,12 +258,12 @@ class VariableEditor(Gtk.VBox):
             #Actions.BLOCK_PARAM_MODIFY()
             pass
         elif key == self.DELETE_BLOCK:
-            self.emit('remove_block', self._block.get_id())
+            self.emit('remove_block', self._block.name)
         elif key == self.DELETE_CONFIRM:
             if self._confirm_delete:
                 # Create a context menu to confirm the delete operation
                 confirmation_menu = Gtk.Menu()
-                block_id = self._block.get_param('id').get_value().replace("_", "__")
+                block_id = self._block.params['id'].get_value().replace("_", "__")
                 confirm = Gtk.MenuItem(label="Delete {}".format(block_id))
                 confirm.connect('activate', self.handle_action, self.DELETE_BLOCK)
                 confirmation_menu.add(confirm)
diff --git a/grc/gui/canvas/block.py b/grc/gui/canvas/block.py
index d336bc139a..33edf988c2 100644
--- a/grc/gui/canvas/block.py
+++ b/grc/gui/canvas/block.py
@@ -32,7 +32,7 @@ from ..Constants import (
     PORT_BORDER_SEPARATION, BLOCK_FONT, PARAM_FONT
 )
 from ...core import utils
-from ...core.Block import Block as CoreBlock
+from ...core.blocks import Block as CoreBlock
 
 
 class Block(CoreBlock, Drawable):
@@ -45,7 +45,7 @@ class Block(CoreBlock, Drawable):
         """
         super(self.__class__, self).__init__(parent, **n)
 
-        self.states.update(_coordinate=(0, 0), _rotation=0)
+        self.states.update(coordinate=(0, 0), rotation=0)
         self.width = self.height = 0
         Drawable.__init__(self)  # needs the states and initial sizes
 
@@ -68,7 +68,7 @@ class Block(CoreBlock, Drawable):
         Returns:
             the coordinate tuple (x, y) or (0, 0) if failure
         """
-        return Utils.scale(self.states['_coordinate'])
+        return Utils.scale(self.states['coordinate'])
 
     @coordinate.setter
     def coordinate(self, coor):
@@ -85,7 +85,7 @@ class Block(CoreBlock, Drawable):
                 Utils.align_to_grid(coor[0] + offset_x) - offset_x,
                 Utils.align_to_grid(coor[1] + offset_y) - offset_y
             )
-        self.states['_coordinate'] = coor
+        self.states['coordinate'] = coor
 
     @property
     def rotation(self):
@@ -95,7 +95,7 @@ class Block(CoreBlock, Drawable):
         Returns:
             the rotation in degrees or 0 if failure
         """
-        return self.states['_rotation']
+        return self.states['rotation']
 
     @rotation.setter
     def rotation(self, rot):
@@ -105,7 +105,7 @@ class Block(CoreBlock, Drawable):
         Args:
             rot: the rotation in degrees
         """
-        self.states['_rotation'] = rot
+        self.states['rotation'] = rot
 
     def _update_colors(self):
         self._bg_color = (
@@ -128,7 +128,8 @@ class Block(CoreBlock, Drawable):
             self._area = (0, 0, self.height, self.width)
         self.bounds_from_area(self._area)
 
-        bussified = self.current_bus_structure['source'], self.current_bus_structure['sink']
+        # bussified = self.current_bus_structure['source'], self.current_bus_structure['sink']
+        bussified = False, False
         for ports, has_busses in zip((self.active_sources, self.active_sinks), bussified):
             if not ports:
                 continue
@@ -160,9 +161,9 @@ class Block(CoreBlock, Drawable):
             PangoCairo.update_layout(cr, params_layout)
 
         title_layout.set_markup(
-            '<span {foreground} font_desc="{font}"><b>{name}</b></span>'.format(
+            '<span {foreground} font_desc="{font}"><b>{label}</b></span>'.format(
                 foreground='foreground="red"' if not self.is_valid() else '', font=BLOCK_FONT,
-                name=Utils.encode(self.name)
+                label=Utils.encode(self.label)
             )
         )
         title_width, title_height = title_layout.get_size()
@@ -170,7 +171,7 @@ class Block(CoreBlock, Drawable):
         # update the params layout
         if not self.is_dummy_block:
             markups = [param.format_block_surface_markup()
-                       for param in self.params.values() if param.get_hide() not in ('all', 'part')]
+                       for param in self.params.values() if param.hide not in ('all', 'part')]
         else:
             markups = ['<span font_desc="{font}"><b>key: </b>{key}</span>'.format(font=PARAM_FONT, key=self.key)]
 
@@ -200,15 +201,15 @@ class Block(CoreBlock, Drawable):
                      get_min_height_for_ports(self.active_sinks),
                      get_min_height_for_ports(self.active_sources))
 
-        def get_min_height_for_bus_ports(ports):
-            return 2 * PORT_BORDER_SEPARATION + sum(
-                port.height + PORT_SPACING for port in ports if port.get_type() == 'bus'
-            ) - PORT_SPACING
-
-        if self.current_bus_structure['sink']:
-            height = max(height, get_min_height_for_bus_ports(self.active_sinks))
-        if self.current_bus_structure['source']:
-            height = max(height, get_min_height_for_bus_ports(self.active_sources))
+        # def get_min_height_for_bus_ports(ports):
+        #     return 2 * PORT_BORDER_SEPARATION + sum(
+        #         port.height + PORT_SPACING for port in ports if port.dtype == 'bus'
+        #     ) - PORT_SPACING
+        #
+        # if self.current_bus_structure['sink']:
+        #     height = max(height, get_min_height_for_bus_ports(self.active_sinks))
+        # if self.current_bus_structure['source']:
+        #     height = max(height, get_min_height_for_bus_ports(self.active_sources))
 
         self.width, self.height = width, height = Utils.align_to_grid((width, height))
 
@@ -237,7 +238,7 @@ class Block(CoreBlock, Drawable):
 
         # Show the flow graph complexity on the top block if enabled
         if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.key == "options":
-            complexity = utils.calculate_flowgraph_complexity(self.parent)
+            complexity = utils.flow_graph_complexity.calculate(self.parent)
             markups.append(
                 '<span foreground="#444" size="medium" font_desc="{font}">'
                 '<b>Complexity: {num}bal</b></span>'.format(num=Utils.num_to_str(complexity), font=BLOCK_FONT)
@@ -344,7 +345,8 @@ class Block(CoreBlock, Drawable):
         Returns:
             true for change
         """
-        type_templates = ' '.join(p._type for p in self.get_children())
+        type_templates = ' '.join(p._type for p in self.params.values())
+        type_templates += ' '.join(p.get_raw('dtype') for p in (self.sinks + self.sources))
         type_param = None
         for key, param in six.iteritems(self.params):
             if not param.is_enum():
@@ -361,10 +363,10 @@ class Block(CoreBlock, Drawable):
 
         # Try to increment the enum by direction
         try:
-            keys = list(type_param.options)
-            old_index = keys.index(type_param.get_value())
-            new_index = (old_index + direction + len(keys)) % len(keys)
-            type_param.set_value(keys[new_index])
+            values = list(type_param.options)
+            old_index = values.index(type_param.get_value())
+            new_index = (old_index + direction + len(values)) % len(values)
+            type_param.set_value(values[new_index])
             return True
         except:
             return False
@@ -381,7 +383,7 @@ class Block(CoreBlock, Drawable):
         """
         changed = False
         # Concat the nports string from the private nports settings of all ports
-        nports_str = ' '.join(port._nports for port in self.get_ports())
+        nports_str = ' '.join(str(port.get_raw('multiplicity')) for port in self.ports())
         # Modify all params whose keys appear in the nports string
         for key, param in six.iteritems(self.params):
             if param.is_enum() or param.key not in nports_str:
diff --git a/grc/gui/canvas/connection.py b/grc/gui/canvas/connection.py
index ff790503ef..56dab45570 100644
--- a/grc/gui/canvas/connection.py
+++ b/grc/gui/canvas/connection.py
@@ -32,7 +32,7 @@ from ..Constants import (
     LINE_SELECT_SENSITIVITY,
 )
 from ...core.Connection import Connection as CoreConnection
-from ...core.Element import nop_write
+from ...core.utils.descriptors import nop_write
 
 
 class Connection(CoreConnection, Drawable):
@@ -93,10 +93,9 @@ class Connection(CoreConnection, Drawable):
         ]
         self._current_coordinates = None  # triggers _make_path()
 
-        def get_domain_color(domain_name):
-            domain = self.parent_platform.domains.get(domain_name, {})
-            color_spec = domain.get('color')
-            return colors.get_color(color_spec) if color_spec else colors.DEFAULT_DOMAIN_COLOR
+        def get_domain_color(domain_id):
+            domain = self.parent_platform.domains.get(domain_id, None)
+            return colors.get_color(domain.color) if domain else colors.DEFAULT_DOMAIN_COLOR
 
         if source.domain == GR_MESSAGE_DOMAIN:
             self._line_width_factor = 1.0
diff --git a/grc/gui/canvas/flowgraph.py b/grc/gui/canvas/flowgraph.py
index 14326fd3f6..394b12cfba 100644
--- a/grc/gui/canvas/flowgraph.py
+++ b/grc/gui/canvas/flowgraph.py
@@ -81,7 +81,7 @@ class FlowGraph(CoreFlowgraph, Drawable):
         Returns:
             a unique id
         """
-        block_ids = set(b.get_id() for b in self.blocks)
+        block_ids = set(b.name for b in self.blocks)
         for index in count():
             block_id = '{}_{}'.format(base_id, index)
             if block_id not in block_ids:
@@ -89,7 +89,7 @@ class FlowGraph(CoreFlowgraph, Drawable):
         return block_id
 
     def install_external_editor(self, param):
-        target = (param.parent_block.get_id(), param.key)
+        target = (param.parent_block.name, param.key)
 
         if target in self._external_updaters:
             editor = self._external_updaters[target]
@@ -118,7 +118,7 @@ class FlowGraph(CoreFlowgraph, Drawable):
     def handle_external_editor_change(self, new_value, target):
         try:
             block_id, param_key = target
-            self.get_block(block_id).get_param(param_key).set_value(new_value)
+            self.get_block(block_id).params[param_key].set_value(new_value)
 
         except (IndexError, ValueError):  # block no longer exists
             self._external_updaters[target].stop()
@@ -146,7 +146,7 @@ class FlowGraph(CoreFlowgraph, Drawable):
         # get the new block
         block = self.new_block(key)
         block.coordinate = coor
-        block.get_param('id').set_value(id)
+        block.params['id'].set_value(id)
         Actions.ELEMENT_CREATE()
         return id
 
@@ -262,18 +262,18 @@ class FlowGraph(CoreFlowgraph, Drawable):
                 except (KeyError, SyntaxError, ValueError):
                     pass
             if block_key == 'epy_block':
-                block.get_param('_io_cache').set_value(param_data.pop('_io_cache'))
-                block.get_param('_source_code').set_value(param_data.pop('_source_code'))
+                block.params['_io_cache'].set_value(param_data.pop('_io_cache'))
+                block.params['_source_code'].set_value(param_data.pop('_source_code'))
                 block.rewrite()  # this creates the other params
             for param_key, param_value in six.iteritems(param_data):
                 #setup id parameter
                 if param_key == 'id':
                     old_id2block[param_value] = block
                     #if the block id is not unique, get a new block id
-                    if param_value in (blk.get_id() for blk in self.blocks):
+                    if param_value in (blk.name for blk in self.blocks):
                         param_value = self._get_unique_id(param_value)
                 #set value to key
-                block.get_param(param_key).set_value(param_value)
+                block.params[param_key].set_value(param_value)
             #move block to offset coordinate
             block.move((x_off, y_off))
         #update before creating connections
@@ -282,11 +282,9 @@ class FlowGraph(CoreFlowgraph, Drawable):
         for connection_n in connections_n:
             source = old_id2block[connection_n.get('source_block_id')].get_source(connection_n.get('source_key'))
             sink = old_id2block[connection_n.get('sink_block_id')].get_sink(connection_n.get('sink_key'))
-            self.connect(source, sink)
-        #set all pasted elements selected
-        for block in selected:
-            selected = selected.union(set(block.get_connections()))
-        self.selected_elements = set(selected)
+            connection = self.connect(source, sink)
+            selected.add(connection)
+        self.selected_elements = selected
 
     ###########################################################################
     # Modify Selected
diff --git a/grc/gui/canvas/param.py b/grc/gui/canvas/param.py
index b027b7653a..e2c335d9cf 100644
--- a/grc/gui/canvas/param.py
+++ b/grc/gui/canvas/param.py
@@ -39,20 +39,20 @@ class Param(CoreParam):
         Returns:
             gtk input class
         """
-        type_ = self.get_type()
-        if type_ in ('file_open', 'file_save'):
+        dtype = self.dtype
+        if dtype in ('file_open', 'file_save'):
             input_widget_cls = ParamWidgets.FileParam
 
-        elif self.is_enum():
+        elif dtype == 'enum':
             input_widget_cls = ParamWidgets.EnumParam
 
         elif self.options:
             input_widget_cls = ParamWidgets.EnumEntryParam
 
-        elif type_ == '_multiline':
+        elif dtype == '_multiline':
             input_widget_cls = ParamWidgets.MultiLineEntryParam
 
-        elif type_ == '_multiline_python_external':
+        elif dtype == '_multiline_python_external':
             input_widget_cls = ParamWidgets.PythonEditorParam
 
         else:
@@ -64,8 +64,8 @@ class Param(CoreParam):
         block = self.parent
         # fixme: using non-public attribute here
         has_callback = \
-            hasattr(block, 'get_callbacks') and \
-            any(self.key in callback for callback in block._callbacks)
+            hasattr(block, 'templates') and \
+            any(self.key in callback for callback in block.templates.get('callbacks', ''))
 
         return '<span {underline} {foreground} font_desc="Sans 9">{label}</span>'.format(
             underline='underline="low"' if has_callback else '',
@@ -76,7 +76,7 @@ class Param(CoreParam):
 
     def format_tooltip_text(self):
         errors = self.get_error_messages()
-        tooltip_lines = ['Key: ' + self.key, 'Type: ' + self.get_type()]
+        tooltip_lines = ['Key: ' + self.key, 'Type: ' + self.dtype]
         if self.is_valid():
             value = str(self.get_evaluated())
             if len(value) > 100:
@@ -117,7 +117,7 @@ class Param(CoreParam):
         if not self.is_valid():
             return _truncate(value)
         if value in self.options:
-            return self.options_names[self.options.index(value)]
+            return self.options[value]  # its name
 
         ##################################################
         # Split up formatting by type
@@ -125,7 +125,7 @@ class Param(CoreParam):
         # Default center truncate
         truncate = 0
         e = self.get_evaluated()
-        t = self.get_type()
+        t = self.dtype
         if isinstance(e, bool):
             return str(e)
         elif isinstance(e, Constants.COMPLEX_TYPES):
diff --git a/grc/gui/canvas/port.py b/grc/gui/canvas/port.py
index b74e4adfcc..2ea35f3dd3 100644
--- a/grc/gui/canvas/port.py
+++ b/grc/gui/canvas/port.py
@@ -26,8 +26,9 @@ from gi.repository import Gtk, PangoCairo, Pango
 from . import colors
 from .drawable import Drawable
 from .. import Actions, Utils, Constants
-from ...core.Element import nop_write
-from ...core.Port import Port as CorePort
+
+from ...core.utils.descriptors import nop_write
+from ...core.ports import Port as CorePort
 
 
 class Port(CorePort, Drawable):
@@ -75,12 +76,13 @@ class Port(CorePort, Drawable):
         if not self.parent_block.enabled:
             self._font_color[-1] = 0.4
             color = colors.BLOCK_DISABLED_COLOR
+        elif self.domain == Constants.GR_MESSAGE_DOMAIN:
+            color = colors.PORT_TYPE_TO_COLOR.get('message')
         else:
             self._font_color[-1] = 1.0
-            color = colors.PORT_TYPE_TO_COLOR.get(self.get_type()) or colors.PORT_TYPE_TO_COLOR.get('')
-            vlen = self.get_vlen()
-            if vlen > 1:
-                dark = (0, 0, 30 / 255.0, 50 / 255.0, 70 / 255.0)[min(4, vlen)]
+            color = colors.PORT_TYPE_TO_COLOR.get(self.dtype) or colors.PORT_TYPE_TO_COLOR.get('')
+            if self.vlen > 1:
+                dark = (0, 0, 30 / 255.0, 50 / 255.0, 70 / 255.0)[min(4, self.vlen)]
                 color = tuple(max(c - dark, 0) for c in color)
         self._bg_color = color
         self._border_color = tuple(max(c - 0.3, 0) for c in color)
@@ -108,7 +110,7 @@ class Port(CorePort, Drawable):
         if cr:
             PangoCairo.update_layout(cr, self.label_layout)
 
-        if self.domain in (Constants.GR_MESSAGE_DOMAIN, Constants.DEFAULT_DOMAIN):
+        if self.domain in (Constants.GR_MESSAGE_DOMAIN, Constants.GR_STREAM_DOMAIN):
             self._line_width_factor = 1.0
         else:
             self._line_width_factor = 2.0
@@ -124,9 +126,9 @@ class Port(CorePort, Drawable):
         self.width = 2 * Constants.PORT_LABEL_PADDING + label_width / Pango.SCALE
         self.height = 2 * Constants.PORT_LABEL_PADDING + label_height / Pango.SCALE
         self._label_layout_offsets = [0, Constants.PORT_LABEL_PADDING]
-        if self.get_type() == 'bus':
-            self.height += Constants.PORT_EXTRA_BUS_HEIGHT
-            self._label_layout_offsets[1] += Constants.PORT_EXTRA_BUS_HEIGHT / 2
+        # if self.dtype == 'bus':
+        #     self.height += Constants.PORT_EXTRA_BUS_HEIGHT
+        #     self._label_layout_offsets[1] += Constants.PORT_EXTRA_BUS_HEIGHT / 2
         self.height += self.height % 2  # uneven height
 
     def draw(self, cr):
diff --git a/grc/main.py b/grc/main.py
index 4d04608269..2d182c226f 100755
--- a/grc/main.py
+++ b/grc/main.py
@@ -84,6 +84,7 @@ def main():
         prefs=gr.prefs(),
         install_prefix=gr.prefix()
     )
+    platform.build_library()
 
     log.debug("Loading application")
     app = Application(args.flow_graphs, platform)
diff --git a/grc/tests/__init__.py b/grc/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/grc/tests/resources/file1.xml b/grc/tests/resources/file1.xml
new file mode 100644
index 0000000000..f03288b85d
--- /dev/null
+++ b/grc/tests/resources/file1.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2014 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
+-->
+<block>
+    <name>testname</name>
+    <key>block_key</key>
+    <make>blocks.complex_to_mag_squared($(vlen))</make>
+    <param>
+        <name>Vec Length</name>
+        <key>vlen</key>
+        <value>1</value>
+        <type>int</type>
+        <tab>test</tab>
+    </param>
+    <param>
+        <name>Vec Length</name>
+        <key>out_type</key>
+        <value>complex</value>
+        <type>string</type>
+    </param>
+    <param>
+        <name>Alpha</name>
+        <key>a</key>
+        <value>0</value>
+        <type>($out_type)</type>
+    </param>
+    <check>$vlen &gt; 0</check>
+    <sink>
+        <name>in</name>
+        <type>complex</type>
+        <vlen>2 * $vlen</vlen>
+    </sink>
+    <sink>
+        <name>in2</name>
+        <type>message</type>
+    </sink>
+    <source>
+        <name>out</name>
+        <type>$out_type</type>
+        <vlen>$vlen</vlen>
+    </source>
+</block>
diff --git a/grc/tests/resources/file2.xml b/grc/tests/resources/file2.xml
new file mode 100644
index 0000000000..1300c7f5a1
--- /dev/null
+++ b/grc/tests/resources/file2.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2014 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
+-->
+<!--
+###################################################
+## And Const Block:
+##	all types, 1 output, 1 input & const
+###################################################
+ -->
+<block>
+	<name>And Const</name>
+	<key>blocks_and_const_xx</key>
+	<category>test</category>
+	<throttle>1</throttle>
+	<import>from gnuradio import blocks</import>
+	<make>blocks.and_const_$(type.fcn)($const)</make>
+	<callback>set_k($const)</callback>
+	<param>
+		<name>IO Type</name>
+		<key>type</key>
+		<type>enum</type>
+		<option>
+			<name>Int</name>
+			<key>int</key>
+			<opt>fcn:ii</opt>
+		</option>
+		<option>
+			<name>Short</name>
+			<key>short</key>
+			<opt>fcn:ss</opt>
+		</option>
+		<option>
+			<name>Byte</name>
+			<key>byte</key>
+			<opt>fcn:bb</opt>
+		</option>
+	</param>
+	<param>
+		<name>Constant</name>
+		<key>const</key>
+		<value>0</value>
+		<type>${type}</type>
+        <hide>#if $log then 'none' else 'part'#</hide>
+	</param>
+	<sink>
+		<name>in</name>
+		<type>$type</type>
+	</sink>
+	<source>
+		<name>out</name>
+		<type>$(type.fcn)</type>
+	</source>
+	<doc>
+This block creates a variable check box. \
+Leave the label blank to use the variable id as the label.
+
+A check box selects between two values of similar type. \
+Te values do not necessarily need to be of boolean type.
+
+The GUI hint can be used to position the widget within the application. \
+The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \
+Both the tab specification and the grid position are optional.
+	</doc>
+</block>
diff --git a/grc/tests/resources/file3.xml b/grc/tests/resources/file3.xml
new file mode 100644
index 0000000000..71753badb1
--- /dev/null
+++ b/grc/tests/resources/file3.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2014 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
+-->
+<!--
+###################################################
+##Variable Check Box:
+##	a gui check box form
+###################################################
+ -->
+<block>
+	<name>QT GUI Check Box</name>
+	<key>variable_qtgui_check_box</key>
+	<import>from PyQt4 import Qt</import>
+	<var_make>self.$(id) = $(id) = $value</var_make>
+	<make>#set $win = '_%s_check_box'%$id
+#if not $label()
+	#set $label = '"%s"'%$id
+#end if
+$win = Qt.QCheckBox($label)
+self._$(id)_choices = {True: $true, False: $false}
+self._$(id)_choices_inv = dict((v,k) for k,v in self._$(id)_choices.iteritems())
+self._$(id)_callback = lambda i: Qt.QMetaObject.invokeMethod($(win), "setChecked", Qt.Q_ARG("bool", self._$(id)_choices_inv[i]))
+self._$(id)_callback(self.$id)
+$(win).stateChanged.connect(lambda i: self.set_$(id)(self._$(id)_choices[bool(i)]))
+$(gui_hint()($win))</make>
+	<callback>self.set_$(id)($value)</callback>
+	<callback>self._$(id)_callback($id)</callback>
+	<param>
+		<name>Label</name>
+		<key>label</key>
+		<value></value>
+		<type>string</type>
+		<hide>#if $label() then 'none' else 'part'#</hide>
+	</param>
+	<param>
+		<name>Type</name>
+		<key>type</key>
+		<value>int</value>
+		<type>enum</type>
+		<hide>part</hide>
+		<option><name>Float</name><key>real</key><opt>conv:float</opt></option>
+		<option><name>Integer</name><key>int</key><opt>conv:int</opt></option>
+		<option><name>String</name><key>string</key><opt>conv:str</opt></option>
+		<option><name>Boolean</name><key>bool</key><opt>conv:bool</opt></option>
+		<option><name>Any</name><key>raw</key><opt>conv:eval</opt></option>
+	</param>
+	<param>
+		<name>Default Value</name>
+		<key>value</key>
+		<value>True</value>
+		<type>$type</type>
+	</param>
+	<param>
+		<name>True</name>
+		<key>true</key>
+		<value>True</value>
+		<type>$type</type>
+	</param>
+	<param>
+		<name>False</name>
+		<key>false</key>
+		<value>False</value>
+		<type>$type</type>
+	</param>
+	<param>
+		<name>GUI Hint</name>
+		<key>gui_hint</key>
+		<value></value>
+		<type>gui_hint</type>
+		<hide>part</hide>
+	</param>
+	<check>$value in ($true, $false)</check>
+	<doc>
+This block creates a variable check box. \
+Leave the label blank to use the variable id as the label.
+
+A check box selects between two values of similar type. \
+Te values do not necessarily need to be of boolean type.
+
+The GUI hint can be used to position the widget within the application. \
+The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \
+Both the tab specification and the grid position are optional.
+	</doc>
+</block>
diff --git a/grc/tests/test_block_flags.py b/grc/tests/test_block_flags.py
new file mode 100644
index 0000000000..9eecaf20d7
--- /dev/null
+++ b/grc/tests/test_block_flags.py
@@ -0,0 +1,26 @@
+
+from grc.core.blocks._flags import Flags
+
+
+def test_simple():
+    assert 'test' in Flags('_test_')
+
+
+def test_deprecated():
+    assert Flags.DEPRECATED == 'deprecated'
+    assert Flags('this is deprecated').deprecated is True
+
+
+def test_extend():
+    f = Flags('a')
+    f += 'b'
+    assert isinstance(f, Flags)
+    f += u'b'
+    assert isinstance(f, Flags)
+    f = Flags(u'a')
+    f += 'b'
+    assert isinstance(f, Flags)
+    f += u'b'
+    assert isinstance(f, Flags)
+
+    assert str(f) == 'abb'
diff --git a/grc/tests/test_block_templates.py b/grc/tests/test_block_templates.py
new file mode 100644
index 0000000000..df9ab37550
--- /dev/null
+++ b/grc/tests/test_block_templates.py
@@ -0,0 +1,45 @@
+import pytest
+
+from grc.core.blocks._templates import MakoTemplates
+from grc.core.errors import TemplateError
+
+
+class Block(object):
+    namespace_templates = {}
+
+    templates = MakoTemplates(None)
+
+    def __init__(self, **kwargs):
+        self.namespace_templates.update(kwargs)
+
+
+def test_simple():
+    t = MakoTemplates(_bind_to=Block(num='123'), test='abc${num}')
+    assert t['test'] == 'abc${num}'
+    assert t.render('test') == 'abc123'
+    assert 'abc${num}' in t._template_cache
+
+
+def test_instance():
+    block = Block(num='123')
+    block.templates['test'] = 'abc${num}'
+    assert block.templates.render('test') == 'abc123'
+    assert block.templates is block.__dict__['templates']
+
+
+def test_list():
+    templates = ['abc${num}', '${2 * num}c']
+    t = MakoTemplates(_bind_to=Block(num='123'), test=templates)
+    assert t['test'] == templates
+    assert t.render('test') == ['abc123', '123123c']
+    assert set(templates) == set(t._template_cache.keys())
+
+
+def test_parse_error():
+    with pytest.raises(TemplateError):
+        MakoTemplates(_bind_to=Block(num='123'), test='abc${num NOT CLOSING').render('test')
+
+
+def test_parse_error2():
+    with pytest.raises(TemplateError):
+        MakoTemplates(_bind_to=Block(num='123'), test='abc${ WRONG_VAR }').render('test')
diff --git a/grc/tests/test_cheetah_converter.py b/grc/tests/test_cheetah_converter.py
new file mode 100644
index 0000000000..7999955436
--- /dev/null
+++ b/grc/tests/test_cheetah_converter.py
@@ -0,0 +1,132 @@
+""""""
+
+import functools
+import grc.converter.cheetah_converter as parser
+
+
+def test_basic():
+    c = parser.Converter(names={'abc'})
+    for convert in (c.convert_simple, c.convert_hard, c.to_python):
+        assert 'abc' == convert('$abc')
+        assert 'abc' == convert('$abc()')
+        assert 'abc' == convert('$(abc)')
+        assert 'abc' == convert('$(abc())')
+        assert 'abc' == convert('${abc}')
+        assert 'abc' == convert('${abc()}')
+
+    assert c.stats['simple'] == 2 * 6
+    assert c.stats['hard'] == 1 * 6
+
+
+def test_simple():
+    convert = parser.Converter(names={'abc': {'def'}})
+    assert 'abc' == convert.convert_simple('$abc')
+    assert 'abc.def' == convert.convert_simple('$abc.def')
+    assert 'abc.def' == convert.convert_simple('$(abc.def)')
+    assert 'abc.def' == convert.convert_simple('${abc.def}')
+    try:
+        convert.convert_simple('$abc.not_a_sub_key')
+    except NameError:
+        assert True
+    else:
+        assert False
+
+
+def test_conditional():
+    convert = parser.Converter(names={'abc'})
+    assert '(asb_asd_ if abc > 0 else __not__)' == convert.convert_inline_conditional(
+        '#if $abc > 0 then asb_$asd_ else __not__')
+
+
+def test_simple_format_string():
+    convert = functools.partial(parser.Converter(names={'abc'}).convert_simple, spec=parser.FormatString)
+    assert '{abc}' == convert('$abc')
+    assert '{abc:eval}' == convert('$abc()')
+    assert '{abc}' == convert('$(abc)')
+    assert '{abc:eval}' == convert('$(abc())')
+    assert '{abc}' == convert('${abc}')
+    assert '{abc:eval}' == convert('${abc()}')
+
+
+def test_hard_format_string():
+    names = {'abc': {'ff'}, 'param1': {}, 'param2': {}}
+    convert = functools.partial(parser.Converter(names).convert_hard, spec=parser.FormatString)
+    assert 'make_a_cool_block_{abc.ff}({param1}, {param2})' == \
+           convert('make_a_cool_block_${abc.ff}($param1, $param2)')
+
+
+converter = parser.Converter(names={'abc'})
+c2p = converter.to_python
+
+
+def test_opts():
+    assert 'abc abc abc' == c2p('$abc $(abc) ${abc}')
+    assert 'abc abc.abc abc' == c2p('$abc $abc.abc ${abc}')
+    assert 'abc abc[''].abc abc' == c2p('$abc $abc[''].abc() ${abc}')
+
+
+def test_nested():
+    assert 'abc(abc) abc + abc abc[abc]' == c2p('$abc($abc) $(abc + $abc) ${abc[$abc]}')
+    assert '(abc_abc_)' == c2p('(abc_$(abc)_)')
+
+
+def test_nested2():
+    class Other(parser.Python):
+        nested_start = '{'
+        nested_end = '}'
+    assert 'abc({abc})' == converter.convert('$abc($abc)', spec=Other)
+
+
+def test_nested3():
+    class Other(parser.Python):
+        start = '{'
+        end = '}'
+    assert '{abc(abc)}' == converter.convert('$abc($abc)', spec=Other)
+
+
+def test_with_string():
+    assert 'abc "$(abc)" abc' == c2p('$abc "$(abc)" ${abc}')
+    assert 'abc \'$(abc)\' abc' == c2p('$abc \'$(abc)\' ${abc}')
+    assert 'abc "\'\'$(abc)" abc' == c2p('$abc "\'\'$(abc)" ${abc}')
+
+
+def test_if():
+    result = converter.to_mako("""
+        #if $abc > 0
+            test
+        #else if $abc < 0
+            test
+        #else
+            bla
+        #end if
+    """)
+
+    expected = """
+        % if abc > 0:
+            test
+        % elif abc < 0:
+            test
+        % else:
+            bla
+        % endif
+    """
+    assert result == expected
+
+
+def test_hash_end():
+    result = converter.to_mako('$abc#slurp')
+    assert result == '${abc}\\'
+
+
+def test_slurp_if():
+    result = converter.to_mako("""
+        $abc#slurp
+        #if $abc
+    """)
+
+    expected = """
+        ${abc}
+        % if abc:
+    """
+    assert result == expected
+
diff --git a/grc/tests/test_evaled_property.py b/grc/tests/test_evaled_property.py
new file mode 100644
index 0000000000..27957cd291
--- /dev/null
+++ b/grc/tests/test_evaled_property.py
@@ -0,0 +1,104 @@
+import collections
+import numbers
+
+from grc.core.utils.descriptors import Evaluated, EvaluatedEnum, EvaluatedPInt
+
+
+class A(object):
+    def __init__(self, **kwargs):
+        self.called = collections.defaultdict(int)
+        self.errors = []
+        self.namespace = kwargs
+
+    def add_error_message(self, msg):
+        self.errors.append(msg)
+
+    @property
+    def parent_block(self):
+        return self
+
+    def evaluate(self, expr):
+        self.called['evaluate'] += 1
+        return eval(expr, self.namespace)
+
+    @Evaluated(int, 1)
+    def foo(self):
+        self.called['foo'] += 1
+        return eval(self._foo)
+
+    bar = Evaluated(numbers.Real, 1.0, name='bar')
+
+    test = EvaluatedEnum(['a', 'b'], 'a', name='test')
+
+    lala = EvaluatedPInt()
+
+
+def test_fixed_value():
+    a = A()
+    a.foo = 10
+
+    assert not hasattr(a, '_foo')
+    assert a.foo == 10
+    assert a.called['foo'] == 0
+    delattr(a, 'foo')
+    assert a.foo == 10
+    assert a.called['foo'] == 0
+
+
+def test_evaled():
+    a = A()
+    a.foo = '${ 10 + 1 }'
+    assert getattr(a, '_foo') == '10 + 1'
+    assert a.foo == 11 and a.foo == 11
+    assert a.called['foo'] == 1
+    assert a.called['evaluate'] == 0
+    delattr(a, 'foo')
+    assert a.foo == 11 and a.foo == 11
+    assert a.called['foo'] == 2
+    assert not a.errors
+
+
+def test_evaled_with_default():
+    a = A()
+    a.bar = '${ 10 + 1 }'
+    assert getattr(a, '_bar') == '10 + 1'
+    assert a.bar == 11.0 and type(a.bar) == int
+    assert a.called['evaluate'] == 1
+    assert not a.errors
+
+
+def test_evaled_int_with_default():
+    a = A(ll=10)
+    a.lala = '${ ll * 2 }'
+    assert a.lala == 20
+    a.namespace['ll'] = -10
+    assert a.lala == 20
+    del a.lala
+    assert a.lala == 1
+    assert not a.errors
+
+
+def test_evaled_enum_fixed_value():
+    a = A()
+    a.test = 'a'
+    assert not hasattr(a, '_test')
+    assert a.test == 'a' and type(a.test) == str
+    assert not a.errors
+
+
+def test_evaled_enum():
+    a = A(bla=False)
+    a.test = '${ "a" if bla else "b" }'
+    assert a.test == 'b'
+    a.namespace['bla'] = True
+    assert a.test == 'b'
+    del a.test
+    assert a.test == 'a'
+    assert not a.errors
+
+
+def test_class_access():
+    a = A()
+    a.foo = '${ meme }'
+    descriptor = getattr(a.__class__, 'foo')
+    assert descriptor.name_raw == '_foo'
diff --git a/grc/tests/test_expr_utils.py b/grc/tests/test_expr_utils.py
new file mode 100644
index 0000000000..4f25477bf1
--- /dev/null
+++ b/grc/tests/test_expr_utils.py
@@ -0,0 +1,41 @@
+import operator
+
+import pytest
+
+from grc.core.utils import expr_utils
+
+id_getter = operator.itemgetter(0)
+expr_getter = operator.itemgetter(1)
+
+
+def test_simple():
+    objects = [
+        ['c', '2 * a + b'],
+        ['a', '1'],
+        ['b', '2 * a + unknown * d'],
+        ['d', '5'],
+    ]
+
+    expected = [
+        ['a', '1'],
+        ['d', '5'],
+        ['b', '2 * a + unknown * d'],
+        ['c', '2 * a + b'],
+    ]
+
+    out = expr_utils.sort_objects2(objects, id_getter, expr_getter)
+
+    assert out == expected
+
+
+def test_other():
+    test = [
+        ['c', '2 * a + b'],
+        ['a', '1'],
+        ['b', '2 * c + unknown'],
+    ]
+
+    expr_utils.sort_objects2(test, id_getter, expr_getter, check_circular=False)
+
+    with pytest.raises(RuntimeError):
+        expr_utils.sort_objects2(test, id_getter, expr_getter)
diff --git a/grc/tests/test_generator.py b/grc/tests/test_generator.py
new file mode 100644
index 0000000000..4c79ce4bd3
--- /dev/null
+++ b/grc/tests/test_generator.py
@@ -0,0 +1,46 @@
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+from os import path
+import tempfile
+
+from grc.core.platform import Platform
+
+
+def test_generator():
+    # c&p form compiler code.
+    # todo: make this independent from installed GR
+    grc_file = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
+    out_dir = tempfile.gettempdir()
+
+    platform = Platform(
+        name='GNU Radio Companion Compiler',
+        prefs=None,
+        version='0.0.0',
+    )
+    platform.build_library()
+
+    flow_graph = platform.make_flow_graph(grc_file)
+    flow_graph.rewrite()
+    flow_graph.validate()
+
+    assert flow_graph.is_valid()
+
+    generator = platform.Generator(flow_graph, path.join(path.dirname(__file__), 'resources'))
+    generator.write()
diff --git a/grc/tests/test_xml_parser.py b/grc/tests/test_xml_parser.py
new file mode 100644
index 0000000000..c68b6cdc5a
--- /dev/null
+++ b/grc/tests/test_xml_parser.py
@@ -0,0 +1,39 @@
+# Copyright 2017 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 os import path
+import sys
+
+from grc.converter import flow_graph
+
+
+def test_flow_graph_converter():
+    filename = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
+
+    data = flow_graph.from_xml(filename)
+
+    flow_graph.dump(data, sys.stdout)
+
+
+def test_flow_graph_converter_with_fp():
+    filename = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
+
+    with open(filename) as fp:
+        data = flow_graph.from_xml(fp)
+
+    flow_graph.dump(data, sys.stdout)
+
diff --git a/grc/tests/test_yaml_checker.py b/grc/tests/test_yaml_checker.py
new file mode 100644
index 0000000000..e6b466e511
--- /dev/null
+++ b/grc/tests/test_yaml_checker.py
@@ -0,0 +1,84 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+import yaml
+
+from grc.core.schema_checker import Validator, BLOCK_SCHEME
+
+
+BLOCK1 = """
+id: block_key
+label: testname
+
+parameters:
+-   id: vlen
+    label: Vec Length
+    dtype: int
+    default: 1
+-   id: out_type
+    label: Vec Length
+    dtype: string
+    default: complex
+-   id: a
+    label: Alpha
+    dtype: ${ out_type }
+    default: '0'
+
+inputs:
+-   label: in
+    domain: stream
+    dtype: complex
+    vlen: ${ 2 * vlen }
+-   name: in2
+    domain: message
+    id: in2
+
+outputs:
+-   label: out
+    domain: stream
+    dtype: ${ out_type }
+    vlen: ${ vlen }
+
+templates:
+    make: blocks.complex_to_mag_squared(${ vlen })
+    
+file_format: 1
+"""
+
+
+def test_min():
+    checker = Validator(BLOCK_SCHEME)
+    assert checker.run({'id': 'test', 'file_format': 1}), checker.messages
+    assert not checker.run({'name': 'test', 'file_format': 1})
+
+
+def test_extra_keys():
+    checker = Validator(BLOCK_SCHEME)
+    assert checker.run({'id': 'test', 'abcdefg': 'nonsense', 'file_format': 1})
+    assert checker.messages == [('block', 'warn', "Ignoring extra key 'abcdefg'")]
+
+
+def test_checker():
+    checker = Validator(BLOCK_SCHEME)
+    data = yaml.load(BLOCK1)
+    passed = checker.run(data)
+    if not passed:
+        print()
+        for msg in checker.messages:
+            print(msg)
+
+    assert passed, checker.messages
-- 
cgit v1.2.3


From 1fa89b3704d7f476e4395eb9358d5a6d7642251b Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <sebastian.koslowski@gmail.com>
Date: Thu, 26 Oct 2017 20:15:22 +0200
Subject: grc: disable auto-conversion and implement json cache

---
 grc/core/Config.py                      |   6 --
 grc/core/Constants.py                   |   2 +
 grc/core/blocks/__init__.py             |   1 +
 grc/core/blocks/_build.py               |  10 +--
 grc/core/cache.py                       |  99 +++++++++++++++++++++++++++++
 grc/core/platform.py                    | 109 +++++++++++++++-----------------
 grc/core/schema_checker/block.py        |   2 +-
 grc/core/utils/__init__.py              |  12 ++++
 grc/core/utils/descriptors/evaluated.py |   6 +-
 grc/gui/Platform.py                     |   4 +-
 10 files changed, 178 insertions(+), 73 deletions(-)
 create mode 100644 grc/core/cache.py

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

diff --git a/grc/core/Config.py b/grc/core/Config.py
index eb53e1751d..4accb74c63 100644
--- a/grc/core/Config.py
+++ b/grc/core/Config.py
@@ -31,8 +31,6 @@ class Config(object):
 
     hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR)
 
-    yml_block_cache = os.path.expanduser('~/.cache/grc_gnuradio')  # FIXME: remove this as soon as converter is stable
-
     def __init__(self, version, version_parts=None, name=None, prefs=None):
         self._gr_prefs = prefs if prefs else DummyPrefs()
         self.version = version
@@ -40,9 +38,6 @@ class Config(object):
         if name:
             self.name = name
 
-        if not os.path.exists(self.yml_block_cache):
-            os.mkdir(self.yml_block_cache)
-
     @property
     def block_paths(self):
         path_list_sep = {'/': ':', '\\': ';'}[os.path.sep]
@@ -50,7 +45,6 @@ class Config(object):
         paths_sources = (
             self.hier_block_lib_dir,
             os.environ.get('GRC_BLOCKS_PATH', ''),
-            self.yml_block_cache,
             self._gr_prefs.get_string('grc', 'local_blocks_path', ''),
             self._gr_prefs.get_string('grc', 'global_blocks_path', ''),
         )
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index fc5383378c..59c5dff35e 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -31,6 +31,8 @@ BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd')
 DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc')
 DEFAULT_HIER_BLOCK_LIB_DIR = os.path.expanduser('~/.grc_gnuradio')
 
+CACHE_FILE = os.path.expanduser('~/.cache/grc_gnuradio/cache.json')
+
 BLOCK_DESCRIPTION_FILE_FORMAT_VERSION = 1
 # File format versions:
 #  0: undefined / legacy
diff --git a/grc/core/blocks/__init__.py b/grc/core/blocks/__init__.py
index e4a085d477..4ca0d5d2bc 100644
--- a/grc/core/blocks/__init__.py
+++ b/grc/core/blocks/__init__.py
@@ -29,6 +29,7 @@ build_ins = {}
 
 
 def register_build_in(cls):
+    cls.loaded_from = '(build-in)'
     build_ins[cls.key] = cls
     return cls
 
diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py
index 9221433387..ce3496d9c4 100644
--- a/grc/core/blocks/_build.py
+++ b/grc/core/blocks/_build.py
@@ -29,7 +29,7 @@ def build(id, label='', category='', flags='', documentation='',
           parameters=None, inputs=None, outputs=None, templates=None, **kwargs):
     block_id = id
 
-    cls = type(block_id, (Block,), {})
+    cls = type(str(block_id), (Block,), {})
     cls.key = block_id
 
     cls.label = label or block_id.title()
@@ -63,7 +63,9 @@ def build(id, label='', category='', flags='', documentation='',
 
 
 def _single_mako_expr(value, block_id):
-    match = re.match(r'\s*\$\{\s*(.*?)\s*\}\s*', str(value))
-    if value and not match:
+    if not value:
+        return None
+    value = value.strip()
+    if not (value.startswith('${') and value.endswith('}')):
         raise ValueError('{} is not a mako substitution in {}'.format(value, block_id))
-    return match.group(1) if match else None
+    return value[2:-1].strip()
diff --git a/grc/core/cache.py b/grc/core/cache.py
new file mode 100644
index 0000000000..b72255ce1f
--- /dev/null
+++ b/grc/core/cache.py
@@ -0,0 +1,99 @@
+# Copyright 2017 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 __future__ import absolute_import, print_function
+
+from io import open
+import json
+import logging
+import os
+
+import six
+
+from .io import yaml
+
+logger = logging.getLogger(__name__)
+
+
+class Cache(object):
+
+    def __init__(self, filename):
+        self.cache_file = filename
+        self.cache = {}
+        self.need_cache_write = True
+        self._accessed_items = set()
+        try:
+            os.makedirs(os.path.dirname(filename))
+        except OSError:
+            pass
+        try:
+            self._converter_mtime = os.path.getmtime(filename)
+        except OSError:
+            self._converter_mtime = -1
+
+    def load(self):
+        try:
+            logger.debug("Loading block cache from: {}".format(self.cache_file))
+            with open(self.cache_file, encoding='utf-8') as cache_file:
+                self.cache = json.load(cache_file)
+            self.need_cache_write = False
+        except (IOError, ValueError):
+            self.need_cache_write = True
+
+    def get_or_load(self, filename):
+        self._accessed_items.add(filename)
+        if os.path.getmtime(filename) <= self._converter_mtime:
+            try:
+                return self.cache[filename]
+            except KeyError:
+                pass
+
+        with open(filename, encoding='utf-8') as fp:
+            data = yaml.safe_load(fp)
+        self.cache[filename] = data
+        self.need_cache_write = True
+        return data
+
+    def save(self):
+        if not self.need_cache_write:
+            return
+
+        logger.info('Saving %d entries to json cache', len(self.cache))
+        with open(self.cache_file, 'wb') as cache_file:
+            json.dump(self.cache, cache_file, encoding='utf-8')
+
+    def prune(self):
+        for filename in (set(self.cache) - self._accessed_items):
+            del self.cache[filename]
+
+    def __enter__(self):
+        self.load()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.save()
+
+
+def byteify(data):
+    if isinstance(data, dict):
+        return {byteify(key): byteify(value) for key, value in six.iteritems(data)}
+    elif isinstance(data, list):
+        return [byteify(element) for element in data]
+    elif isinstance(data, six.text_type) and six.PY2:
+        return data.encode('utf-8')
+    else:
+        return data
diff --git a/grc/core/platform.py b/grc/core/platform.py
index 538bacade2..54deef3455 100644
--- a/grc/core/platform.py
+++ b/grc/core/platform.py
@@ -19,7 +19,6 @@ from __future__ import absolute_import, print_function
 
 from codecs import open
 from collections import namedtuple
-import glob
 import os
 import logging
 from itertools import chain
@@ -33,6 +32,7 @@ from . import (
 )
 
 from .Config import Config
+from .cache import Cache
 from .base import Element
 from .io import yaml
 from .generator import Generator
@@ -141,44 +141,41 @@ class Platform(Element):
         self.connection_templates.clear()
         self._block_categories.clear()
 
-        # FIXME: remove this as soon as converter is stable
-        from ..converter import Converter
-        converter = Converter(self.config.block_paths, self.config.yml_block_cache)
-        converter.run()
-        logging.info('XML converter done.')
-
-        for file_path in self._iter_files_in_block_path(path):
-            try:
-                data = converter.cache[file_path]
-            except KeyError:
-                with open(file_path, encoding='utf-8') as fp:
-                    data = yaml.safe_load(fp)
-
-            if file_path.endswith('.block.yml'):
-                loader = self.load_block_description
-                scheme = schema_checker.BLOCK_SCHEME
-            elif file_path.endswith('.domain.yml'):
-                loader = self.load_domain_description
-                scheme = schema_checker.DOMAIN_SCHEME
-            elif file_path.endswith('.tree.yml'):
-                loader = self.load_category_tree_description
-                scheme = None
-            else:
-                continue
-
-            try:
-                checker = schema_checker.Validator(scheme)
-                passed = checker.run(data)
-                for msg in checker.messages:
-                    logger.warning('{:<40s} {}'.format(os.path.basename(file_path), msg))
-                if not passed:
-                    logger.info('YAML schema check failed for: ' + file_path)
-
-                loader(data, file_path)
-            except Exception as error:
-                logger.exception('Error while loading %s', file_path)
-                logger.exception(error)
-                raise
+        # # FIXME: remove this as soon as converter is stable
+        # from ..converter import Converter
+        # converter = Converter(self.config.block_paths, self.config.yml_block_cache)
+        # converter.run()
+        # logging.info('XML converter done.')
+
+        with Cache(Constants.CACHE_FILE) as cache:
+            for file_path in self._iter_files_in_block_path(path):
+                data = cache.get_or_load(file_path)
+
+                if file_path.endswith('.block.yml'):
+                    loader = self.load_block_description
+                    scheme = schema_checker.BLOCK_SCHEME
+                elif file_path.endswith('.domain.yml'):
+                    loader = self.load_domain_description
+                    scheme = schema_checker.DOMAIN_SCHEME
+                elif file_path.endswith('.tree.yml'):
+                    loader = self.load_category_tree_description
+                    scheme = None
+                else:
+                    continue
+
+                try:
+                    checker = schema_checker.Validator(scheme)
+                    passed = checker.run(data)
+                    for msg in checker.messages:
+                        logger.warning('{:<40s} {}'.format(os.path.basename(file_path), msg))
+                    if not passed:
+                        logger.info('YAML schema check failed for: ' + file_path)
+
+                    loader(data, file_path)
+                except Exception as error:
+                    logger.exception('Error while loading %s', file_path)
+                    logger.exception(error)
+                    raise
 
         for key, block in six.iteritems(self.blocks):
             category = self._block_categories.get(key, block.category)
@@ -201,10 +198,9 @@ class Platform(Element):
             if os.path.isfile(entry):
                 yield entry
             elif os.path.isdir(entry):
-                pattern = os.path.join(entry, '**.' + ext)
-                yield_from = glob.iglob(pattern)
-                for file_path in yield_from:
-                    yield file_path
+                for dirpath, dirnames, filenames in os.walk(entry):
+                    for filename in sorted(filter(lambda f: f.endswith('.' + ext), filenames)):
+                        yield os.path.join(dirpath, filename)
             else:
                 logger.debug('Ignoring invalid path entry %r', entry)
 
@@ -232,16 +228,18 @@ class Platform(Element):
             log.error('Unknown format version %d in %s', file_format, file_path)
             return
 
-        block_id = data.pop('id').rstrip('_')
+        block_id = data['id'] = data['id'].rstrip('_')
 
         if block_id in self.block_classes_build_in:
             log.warning('Not overwriting build-in block %s with %s', block_id, file_path)
             return
         if block_id in self.blocks:
-            log.warning('Block with id "%s" overwritten by %s', block_id, file_path)
+            log.warning('Block with id "%s" loaded from\n  %s\noverwritten by\n  %s',
+                        block_id, self.blocks[block_id].loaded_from, file_path)
 
         try:
-            block_cls = self.blocks[block_id] = self.new_block_class(block_id, **data)
+            block_cls = self.blocks[block_id] = self.new_block_class(**data)
+            block_cls.loaded_from = file_path
         except errors.BlockLoadError as error:
             log.error('Unable to load block %s', block_id)
             log.exception(error)
@@ -288,19 +286,12 @@ class Platform(Element):
         path = []
 
         def load_category(name, elements):
-            if not isinstance(name, str):
-                log.debug('invalid name %r', name)
-                return
-            if isinstance(elements, list):
-                pass
-            elif isinstance(elements, str):
-                elements = [elements]
-            else:
-                log.debug('Ignoring elements of %s', name)
+            if not isinstance(name, six.string_types):
+                log.debug('Invalid name %r', name)
                 return
             path.append(name)
-            for element in elements:
-                if isinstance(element, str):
+            for element in utils.to_list(elements):
+                if isinstance(element, six.string_types):
                     block_id = element
                     self._block_categories[block_id] = list(path)
                 elif isinstance(element, dict):
@@ -415,8 +406,8 @@ class Platform(Element):
             fg.import_data(data)
         return fg
 
-    def new_block_class(self, block_id, **data):
-        return blocks.build(block_id, **data)
+    def new_block_class(self, **data):
+        return blocks.build(**data)
 
     def make_block(self, parent, block_id, **kwargs):
         cls = self.block_classes[block_id]
diff --git a/grc/core/schema_checker/block.py b/grc/core/schema_checker/block.py
index ea079b4276..d511e36887 100644
--- a/grc/core/schema_checker/block.py
+++ b/grc/core/schema_checker/block.py
@@ -37,7 +37,7 @@ TEMPLATES_SCHEME = expand(
 BLOCK_SCHEME = expand(
     id=Spec(types=str_, required=True, item_scheme=None),
     label=str_,
-    category=(list, str_),
+    category=str_,
     flags=(list, str_),
 
     parameters=Spec(types=list, required=False, item_scheme=PARAM_SCHEME),
diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py
index 660eb594a5..f2ac986fb4 100644
--- a/grc/core/utils/__init__.py
+++ b/grc/core/utils/__init__.py
@@ -17,5 +17,17 @@
 
 from __future__ import absolute_import
 
+import six
+
 from . import epy_block_io, expr_utils, extract_docs, flow_graph_complexity
 from .hide_bokeh_gui_options_if_not_installed import hide_bokeh_gui_options_if_not_installed
+
+
+def to_list(value):
+    if not value:
+        return []
+    elif isinstance(value, six.string_types):
+        return [value]
+    else:
+        return list(value)
+
diff --git a/grc/core/utils/descriptors/evaluated.py b/grc/core/utils/descriptors/evaluated.py
index 313cee5b96..e8bce6e6ed 100644
--- a/grc/core/utils/descriptors/evaluated.py
+++ b/grc/core/utils/descriptors/evaluated.py
@@ -15,6 +15,10 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
+
+import six
+
 
 class Evaluated(object):
     def __init__(self, expected_type, default, name=None):
@@ -62,7 +66,7 @@ class Evaluated(object):
     def __set__(self, instance, value):
         attribs = instance.__dict__
         value = value or self.default
-        if isinstance(value, str) and value.startswith('${') and value.endswith('}'):
+        if isinstance(value, six.text_type) and value.startswith('${') and value.endswith('}'):
             attribs[self.name_raw] = value[2:-1].strip()
         else:
             attribs[self.name] = type(self.default)(value)
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index 2a38bc619e..8eb79f3459 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -63,8 +63,8 @@ class Platform(CorePlatform):
     FlowGraph = canvas.FlowGraph
     Connection = canvas.Connection
 
-    def new_block_class(self, block_id, **data):
-        cls = CorePlatform.new_block_class(self, block_id, **data)
+    def new_block_class(self, **data):
+        cls = CorePlatform.new_block_class(self, **data)
         return canvas.Block.make_cls_with_base(cls)
 
     block_classes_build_in = {key: canvas.Block.make_cls_with_base(cls)
-- 
cgit v1.2.3


From 14d79b777e773e299a1ffa0dd12d2508a46370a0 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <sebastian.koslowski@gmail.com>
Date: Wed, 8 Nov 2017 21:28:35 +0100
Subject: grc: move some of the param checkers to separate file

---
 grc/core/Constants.py                   |  20 ++---
 grc/core/params/dtypes.py               | 103 +++++++++++++++++++++
 grc/core/params/param.py                | 154 ++++++++------------------------
 grc/core/utils/descriptors/evaluated.py |   7 +-
 grc/gui/Utils.py                        |   4 +-
 grc/gui/canvas/param.py                 |   4 +-
 6 files changed, 161 insertions(+), 131 deletions(-)
 create mode 100644 grc/core/params/dtypes.py

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

diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 59c5dff35e..8ed8899c70 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import absolute_import
 
 import os
+import numbers
 import stat
 
 import numpy
@@ -54,7 +55,7 @@ TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP
                       stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
 HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH
 
-PARAM_TYPE_NAMES = (
+PARAM_TYPE_NAMES = {
     'raw', 'enum',
     'complex', 'real', 'float', 'int',
     'complex_vector', 'real_vector', 'float_vector', 'int_vector',
@@ -63,18 +64,17 @@ PARAM_TYPE_NAMES = (
     'id', 'stream_id',
     'gui_hint',
     'import',
-)
+}
+
+PARAM_TYPE_MAP = {
+    'complex': numbers.Complex,
+    'float': numbers.Real,
+    'real': numbers.Real,
+    'int': numbers.Integral,
+}
 
 # Define types, native python + numpy
 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
-COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
-REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
-INT_TYPES = [int, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
-             numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
-# Cast to tuple for isinstance, concat subtypes
-COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
-REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
-INT_TYPES = tuple(INT_TYPES)
 
 # Updating colors. Using the standard color palette from:
 #   http://www.google.com/design/spec/style/color.html#color-color-palette
diff --git a/grc/core/params/dtypes.py b/grc/core/params/dtypes.py
new file mode 100644
index 0000000000..cddfdefe45
--- /dev/null
+++ b/grc/core/params/dtypes.py
@@ -0,0 +1,103 @@
+# Copyright 2008-2017 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 __future__ import absolute_import
+
+import re
+
+from six.moves import builtins
+
+from .. import blocks
+from .. import Constants
+
+
+# Blacklist certain ids, its not complete, but should help
+ID_BLACKLIST = ['self', 'options', 'gr', 'math', 'firdes'] + dir(builtins)
+try:
+    from gnuradio import gr
+    ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
+except (ImportError, AttributeError):
+    pass
+
+
+validators = {}
+
+
+def validates(*dtypes):
+    def decorator(func):
+        for dtype in dtypes:
+            assert dtype in Constants.PARAM_TYPE_NAMES
+            validators[dtype] = func
+        return func
+    return decorator
+
+
+class ValidateError(Exception):
+    """Raised by validate functions"""
+
+
+@validates('id')
+def validate_block_id(param):
+    value = param.value
+    # Can python use this as a variable?
+    if not re.match(r'^[a-z|A-Z]\w*$', value):
+        raise ValidateError('ID "{}" must begin with a letter and may contain letters, numbers, '
+                            'and underscores.'.format(value))
+    if value in ID_BLACKLIST:
+        raise ValidateError('ID "{}" is blacklisted.'.format(value))
+    block_names = [block.name for block in param.parent_flowgraph.iter_enabled_blocks()]
+    # Id should only appear once, or zero times if block is disabled
+    if param.key == 'id' and block_names.count(value) > 1:
+        raise ValidateError('ID "{}" is not unique.'.format(value))
+    elif value not in block_names:
+        raise ValidateError('ID "{}" does not exist.'.format(value))
+    return value
+
+
+@validates('stream_id')
+def validate_stream_id(param):
+    value = param.value
+    stream_ids = [
+        block.params['stream_id'].value
+        for block in param.parent_flowgraph.iter_enabled_blocks()
+        if isinstance(block, blocks.VirtualSink)
+    ]
+    # Check that the virtual sink's stream id is unique
+    if isinstance(param.parent_block, blocks.VirtualSink) and stream_ids.count(value) >= 2:
+        # Id should only appear once, or zero times if block is disabled
+        raise ValidateError('Stream ID "{}" is not unique.'.format(value))
+    # Check that the virtual source's steam id is found
+    elif isinstance(param.parent_block, blocks.VirtualSource) and value not in stream_ids:
+        raise ValidateError('Stream ID "{}" is not found.'.format(value))
+
+
+@validates('complex', 'real', 'float', 'int')
+def validate_scalar(param):
+    valid_types = Constants.PARAM_TYPE_MAP[param.dtype]
+    if not isinstance(param.value, valid_types):
+        raise ValidateError('Expression {!r} is invalid for type {!r}.'.format(
+            param.value, param.dtype))
+
+
+@validates('complex_vector', 'real_vector', 'float_vector', 'int_vector')
+def validate_vector(param):
+    # todo: check vector types
+
+    valid_types = Constants.PARAM_TYPE_MAP[param.dtype.split('_', 1)[0]]
+    if not all(isinstance(item, valid_types) for item in param.value):
+        raise ValidateError('Expression {!r} is invalid for type {!r}.'.format(
+            param.value, param.dtype))
diff --git a/grc/core/params/param.py b/grc/core/params/param.py
index 787be9a19f..30a48bb434 100644
--- a/grc/core/params/param.py
+++ b/grc/core/params/param.py
@@ -18,28 +18,19 @@
 from __future__ import absolute_import
 
 import ast
-import numbers
-import re
 import collections
 import textwrap
 
 import six
-from six.moves import builtins, range
+from six.moves import range
 
-from .. import Constants, blocks
+from .. import Constants
 from ..base import Element
 from ..utils.descriptors import Evaluated, EvaluatedEnum, setup_names
 
+from . import dtypes
 from .template_arg import TemplateArg
 
-# Blacklist certain ids, its not complete, but should help
-ID_BLACKLIST = ['self', 'options', 'gr', 'math', 'firdes'] + dir(builtins)
-try:
-    from gnuradio import gr
-    ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
-except (ImportError, AttributeError):
-    pass
-
 
 @setup_names
 class Param(Element):
@@ -147,6 +138,10 @@ class Param(Element):
         except Exception as e:
             self.add_error_message(str(e))
 
+        rewriter = getattr(dtypes, 'rewrite_' + self.dtype, None)
+        if rewriter:
+            rewriter(self)
+
     def validate(self):
         """
         Validate the param.
@@ -156,6 +151,13 @@ class Param(Element):
         if self.dtype not in Constants.PARAM_TYPE_NAMES:
             self.add_error_message('Type "{}" is not a possible type.'.format(self.dtype))
 
+        validator = dtypes.validators.get(self.dtype, None)
+        if self._init and validator:
+            try:
+                validator(self)
+            except dtypes.ValidateError as e:
+                self.add_error_message(e.message)
+
     def get_evaluated(self):
         return self._evaluated
 
@@ -173,68 +175,41 @@ class Param(Element):
         expr = self.get_value()
 
         #########################
-        # Enum Type
+        # ID and Enum types (not evaled)
         #########################
-        if self.is_enum():
+        if dtype in ('id', 'stream_id') or self.is_enum():
             return expr
 
         #########################
         # Numeric Types
         #########################
         elif dtype in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'):
-            # Raise exception if python cannot evaluate this value
-            try:
-                value = self.parent_flowgraph.evaluate(expr)
-            except Exception as value:
-                raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, value))
-            # Raise an exception if the data is invalid
-            if dtype == 'raw':
-                return value
-            elif dtype == 'complex':
-                if not isinstance(value, Constants.COMPLEX_TYPES):
-                    raise Exception('Expression "{}" is invalid for type complex.'.format(str(value)))
-                return value
-            elif dtype in ('real', 'float'):
-                if not isinstance(value, Constants.REAL_TYPES):
-                    raise Exception('Expression "{}" is invalid for type float.'.format(str(value)))
-                return value
-            elif dtype == 'int':
-                if not isinstance(value, Constants.INT_TYPES):
-                    raise Exception('Expression "{}" is invalid for type integer.'.format(str(value)))
-                return value
-            elif dtype == 'hex':
-                return hex(value)
-            elif dtype == 'bool':
-                if not isinstance(value, bool):
-                    raise Exception('Expression "{}" is invalid for type bool.'.format(str(value)))
-                return value
+            if expr:
+                try:
+                    value = self.parent_flowgraph.evaluate(expr)
+                except Exception as e:
+                    raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, e))
             else:
-                raise TypeError('Type "{}" not handled'.format(dtype))
+                value = 0
+            if dtype == 'hex':
+                value = hex(value)
+            elif dtype == 'bool':
+                value = bool(value)
+            return value
+
         #########################
         # Numeric Vector Types
         #########################
         elif dtype in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
-            default = []
-
             if not expr:
-                return default   # Turn a blank string into an empty list, so it will eval
-
+                return []   # Turn a blank string into an empty list, so it will eval
             try:
                 value = self.parent.parent.evaluate(expr)
             except Exception as value:
                 raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, value))
-
             if not isinstance(value, Constants.VECTOR_TYPES):
                 self._lisitify_flag = True
                 value = [value]
-
-            # Raise an exception if the data is invalid
-            if dtype == 'complex_vector' and not all(isinstance(item, numbers.Complex) for item in value):
-                raise Exception('Expression "{}" is invalid for type complex vector.'.format(value))
-            elif dtype in ('real_vector', 'float_vector') and not all(isinstance(item, numbers.Real) for item in value):
-                raise Exception('Expression "{}" is invalid for type float vector.'.format(value))
-            elif dtype == 'int_vector' and not all(isinstance(item, Constants.INT_TYPES) for item in value):
-                raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(value)))
             return value
         #########################
         # String Types
@@ -242,7 +217,7 @@ class Param(Element):
         elif dtype in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
             # Do not check if file/directory exists, that is a runtime issue
             try:
-                value = self.parent.parent.evaluate(expr)
+                value = self.parent_flowgraph.evaluate(expr)
                 if not isinstance(value, str):
                     raise Exception()
             except:
@@ -252,28 +227,10 @@ class Param(Element):
                 ast.parse(value)  # Raises SyntaxError
             return value
         #########################
-        # Unique ID Type
-        #########################
-        elif dtype == 'id':
-            self.validate_block_id()
-            return expr
-
-        #########################
-        # Stream ID Type
-        #########################
-        elif dtype == 'stream_id':
-            self.validate_stream_id()
-            return expr
-
-        #########################
         # GUI Position/Hint
         #########################
         elif dtype == 'gui_hint':
-            if self.parent_block.state == 'disabled':
-                return ''
-            else:
-                return self.parse_gui_hint(expr)
-
+            return self.parse_gui_hint(expr) if self.parent_block.state == 'enabled' else ''
         #########################
         # Import Type
         #########################
@@ -292,37 +249,6 @@ class Param(Element):
         else:
             raise TypeError('Type "{}" not handled'.format(dtype))
 
-    def validate_block_id(self):
-        value = self.value
-        # Can python use this as a variable?
-        if not re.match(r'^[a-z|A-Z]\w*$', value):
-            raise Exception('ID "{}" must begin with a letter and may contain letters, numbers, '
-                            'and underscores.'.format(value))
-        if value in ID_BLACKLIST:
-            raise Exception('ID "{}" is blacklisted.'.format(value))
-        block_names = [block.name for block in self.parent_flowgraph.iter_enabled_blocks()]
-        # Id should only appear once, or zero times if block is disabled
-        if self.key == 'id' and block_names.count(value) > 1:
-            raise Exception('ID "{}" is not unique.'.format(value))
-        elif value not in block_names:
-            raise Exception('ID "{}" does not exist.'.format(value))
-        return value
-
-    def validate_stream_id(self):
-        value = self.value
-        stream_ids = [
-            block.params['stream_id'].value
-            for block in self.parent_flowgraph.iter_enabled_blocks()
-            if isinstance(block, blocks.VirtualSink)
-            ]
-        # Check that the virtual sink's stream id is unique
-        if isinstance(self.parent_block, blocks.VirtualSink) and stream_ids.count(value) >= 2:
-            # Id should only appear once, or zero times if block is disabled
-            raise Exception('Stream ID "{}" is not unique.'.format(value))
-        # Check that the virtual source's steam id is found
-        elif isinstance(self.parent_block, blocks.VirtualSource) and value not in stream_ids:
-            raise Exception('Stream ID "{}" is not found.'.format(value))
-
     def to_code(self):
         """
         Convert the value to code.
@@ -333,24 +259,20 @@ class Param(Element):
             a string representing the code
         """
         self._init = True
-        v = self.get_value()
-        t = self.dtype
+        value = self.get_value()
         # String types
-        if t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
+        if self.dtype in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'):
             if not self._init:
                 self.evaluate()
-            return repr(v) if self._stringify_flag else v
+            return repr(value) if self._stringify_flag else value
 
         # Vector types
-        elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
+        elif self.dtype in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'):
             if not self._init:
                 self.evaluate()
-            if self._lisitify_flag:
-                return '(%s, )' % v
-            else:
-                return '(%s)' % v
+            return '[' + value + ']' if self._lisitify_flag else value
         else:
-            return v
+            return value
 
     def get_opt(self, item):
         return self.options.attributes[self.get_value()][item]
@@ -470,7 +392,7 @@ class Param(Element):
         optionally a given key
 
         Args:
-            type: the specified type
+            dtype: the specified type
             key: the key to match against
 
         Returns:
diff --git a/grc/core/utils/descriptors/evaluated.py b/grc/core/utils/descriptors/evaluated.py
index e8bce6e6ed..0e1b68761c 100644
--- a/grc/core/utils/descriptors/evaluated.py
+++ b/grc/core/utils/descriptors/evaluated.py
@@ -79,9 +79,10 @@ class Evaluated(object):
 
 class EvaluatedEnum(Evaluated):
     def __init__(self, allowed_values, default=None, name=None):
-        self.allowed_values = allowed_values if isinstance(allowed_values, (list, tuple)) else \
-            allowed_values.split()
-        default = default if default is not None else self.allowed_values[0]
+        if isinstance(allowed_values, six.string_types):
+            allowed_values = set(allowed_values.split())
+        self.allowed_values = allowed_values
+        default = default if default is not None else next(iter(self.allowed_values))
         super(EvaluatedEnum, self).__init__(str, default, name)
 
     def default_eval_func(self, instance):
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index f47c2e6b97..1b32e91439 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -19,6 +19,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import
 
+import numbers
+
 from gi.repository import GLib
 import cairo
 import six
@@ -91,7 +93,7 @@ def num_to_str(num):
                 return template.format(value / factor, symbol.strip())
         return template.format(value, '')
 
-    if isinstance(num, Constants.COMPLEX_TYPES):
+    if isinstance(num, numbers.Complex):
         num = complex(num)  # Cast to python complex
         if num == 0:
             return '0'
diff --git a/grc/gui/canvas/param.py b/grc/gui/canvas/param.py
index 845ff5a926..5777423c68 100644
--- a/grc/gui/canvas/param.py
+++ b/grc/gui/canvas/param.py
@@ -17,6 +17,8 @@
 
 from __future__ import absolute_import
 
+import numbers
+
 from .drawable import Drawable
 from .. import ParamWidgets, Utils, Constants
 from ...core.params import Param as CoreParam
@@ -126,7 +128,7 @@ class Param(CoreParam):
         t = self.dtype
         if isinstance(e, bool):
             return str(e)
-        elif isinstance(e, Constants.COMPLEX_TYPES):
+        elif isinstance(e, numbers.Complex):
             dt_str = Utils.num_to_str(e)
         elif isinstance(e, Constants.VECTOR_TYPES):
             # Vector types
-- 
cgit v1.2.3