commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] [gnuradio] 47/101: grc: refactor: Port, Param, Options


From: git
Subject: [Commit-gnuradio] [gnuradio] 47/101: grc: refactor: Port, Param, Options init clean-up
Date: Thu, 16 Mar 2017 14:58:05 +0000 (UTC)

This is an automated email from the git hooks/post-receive script.

jcorgan pushed a commit to branch python3
in repository gnuradio.

commit 93ce3961a572da6ec3dbef1f24a22f4153acaa61
Author: Sebastian Koslowski <address@hidden>
Date:   Fri Jul 15 23:25:54 2016 +0200

    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(-)

diff --git a/grc/core/Block.py b/grc/core/Block.py
index 8ffb99b..d37909b 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 40fe69d..bc387a4 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 cd0514b..415b086 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 c81180a..46c6d34 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 841f85e..a2effad 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 e32bd91..6962813 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 ef6a92c..9030bef 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 d5d31c0..678be44 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 4b1717b..d095179 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 92b2167..0000000
--- 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__ = "address@hidden"
-__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 b55e471..c861193 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 6122ec1..87dd97a 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 bd07a66..97f9033 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 9d5b55f..f688d2a 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 0c5728f..4e6ba27 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 b8dd6aa..6a2a13f 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 4b3f6ed..db3ab9d 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 304c8b8..d474c66 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)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]