mirror of
https://github.com/gnuradio/gnuradio-companion.git
synced 2025-12-10 00:42:30 -06:00
grc: pep8 formatting
Signed-off-by: Josh Morman <jmorman@gnuradio.org>
This commit is contained in:
parent
edb6bc8628
commit
b7828c950c
@ -38,7 +38,8 @@ def main(args=None):
|
||||
name='GNU Radio Companion Compiler',
|
||||
prefs=gr.prefs(),
|
||||
version=gr.version(),
|
||||
version_parts=(gr.major_version(), gr.api_version(), gr.minor_version())
|
||||
version_parts=(gr.major_version(),
|
||||
gr.api_version(), gr.minor_version())
|
||||
)
|
||||
platform.build_library()
|
||||
|
||||
|
||||
@ -94,7 +94,8 @@ def convert_block_xml(node):
|
||||
data['asserts'] = [converter.to_python_dec(check_node.text)
|
||||
for check_node in node.iterfind('check')] or no_value
|
||||
|
||||
data['templates'] = convert_templates(node, converter.to_mako, block_id) or no_value
|
||||
data['templates'] = convert_templates(
|
||||
node, converter.to_mako, block_id) or no_value
|
||||
|
||||
docs = node.findtext('doc')
|
||||
if docs:
|
||||
@ -103,7 +104,8 @@ def convert_block_xml(node):
|
||||
|
||||
data['file_format'] = current_file_format
|
||||
|
||||
data = OrderedDict((key, value) for key, value in data.items() if value is not no_value)
|
||||
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
|
||||
@ -116,8 +118,10 @@ def auto_hide_params_for_item_sizes(data):
|
||||
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)
|
||||
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:
|
||||
@ -135,7 +139,8 @@ def convert_templates(node, convert, block_id=''):
|
||||
imports = yaml.MultiLineString(imports)
|
||||
templates['imports'] = imports or no_value
|
||||
|
||||
templates['var_make'] = convert(node.findtext('var_make') or '') or no_value
|
||||
templates['var_make'] = convert(
|
||||
node.findtext('var_make') or '') or no_value
|
||||
|
||||
make = convert(node.findtext('make') or '')
|
||||
if make:
|
||||
@ -160,8 +165,10 @@ def convert_param_xml(node, convert):
|
||||
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'))
|
||||
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
|
||||
@ -192,7 +199,8 @@ def convert_port_xml(node, convert):
|
||||
else:
|
||||
port['dtype'] = dtype
|
||||
vlen = node.findtext('vlen')
|
||||
port['vlen'] = int(vlen) if vlen and vlen.isdigit() else convert(vlen) or no_value
|
||||
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
|
||||
@ -207,4 +215,5 @@ def check_mako_template(block_id, expr):
|
||||
try:
|
||||
Template(expr)
|
||||
except Exception as error:
|
||||
print(block_id, expr, type(error), error, '', sep='\n', file=sys.stderr)
|
||||
print(block_id, expr, type(error), error,
|
||||
'', sep='\n', file=sys.stderr)
|
||||
|
||||
@ -18,7 +18,8 @@ cheetah_substitution = re.compile(
|
||||
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>.*?) ?(#|$)')
|
||||
cheetah_inline_if = re.compile(
|
||||
r'#if (?P<cond>.*) then (?P<then>.*?) ?else (?P<else>.*?) ?(#|$)')
|
||||
|
||||
|
||||
class Python(object):
|
||||
@ -116,7 +117,8 @@ class Converter(object):
|
||||
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'))
|
||||
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)
|
||||
@ -220,7 +222,8 @@ class Converter(object):
|
||||
out.append(char)
|
||||
delim_to_find = False
|
||||
|
||||
elif delim_to_find and char in ')]}' and extra_close(): # end of substitution
|
||||
# end of substitution
|
||||
elif delim_to_find and char in ')]}' and extra_close():
|
||||
out.append(spec.end)
|
||||
out.append(char)
|
||||
delim_to_find = False
|
||||
@ -235,7 +238,8 @@ class Converter(object):
|
||||
|
||||
out = ''.join(out)
|
||||
# fix: eval stuff
|
||||
out = re.sub(r'(?P<arg>' + r'|'.join(self.extended) + r')\(\)', r'\g<arg>', out)
|
||||
out = re.sub(r'(?P<arg>' + r'|'.join(self.extended) +
|
||||
r')\(\)', r'\g<arg>', out)
|
||||
|
||||
self.stats['hard'] += 1
|
||||
return spec.type(out)
|
||||
|
||||
@ -50,7 +50,8 @@ class Converter(object):
|
||||
self._force = force
|
||||
|
||||
try:
|
||||
logger.debug("Loading block cache from: {}".format(self.cache_file))
|
||||
logger.debug(
|
||||
"Loading block cache from: {}".format(self.cache_file))
|
||||
with open(self.cache_file, encoding='utf-8') as cache_file:
|
||||
self.cache = byteify(json.load(cache_file))
|
||||
except (IOError, ValueError):
|
||||
@ -108,7 +109,8 @@ class Converter(object):
|
||||
|
||||
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('._-')
|
||||
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):
|
||||
@ -142,7 +144,8 @@ class Converter(object):
|
||||
if name.endswith(suffix):
|
||||
yield path.join(root, name)
|
||||
else:
|
||||
logger.warning('Invalid entry in search path: {}'.format(block_path))
|
||||
logger.warning(
|
||||
'Invalid entry in search path: {}'.format(block_path))
|
||||
|
||||
|
||||
def byteify(data):
|
||||
|
||||
@ -53,7 +53,7 @@ def load_stdlib(filename, document_type_def=None):
|
||||
if isinstance(filename, str):
|
||||
with open(filename, 'rb') as xml_file:
|
||||
data = xml_file.read().decode('utf-8')
|
||||
else: # Already opened
|
||||
else: # Already opened
|
||||
data = filename.read().decode('utf-8')
|
||||
|
||||
try:
|
||||
|
||||
@ -17,13 +17,16 @@ class Config(object):
|
||||
license = __doc__.strip()
|
||||
website = 'https://www.gnuradio.org/'
|
||||
|
||||
hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR)
|
||||
hier_block_lib_dir = os.environ.get(
|
||||
'GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR)
|
||||
|
||||
def __init__(self, version, version_parts=None, name=None, prefs=None):
|
||||
self._gr_prefs = prefs if prefs else DummyPrefs()
|
||||
self.version = version
|
||||
self.version_parts = version_parts or version[1:].split('-', 1)[0].split('.')[:3]
|
||||
self.enabled_components = self._gr_prefs.get_string('grc', 'enabled_components', '')
|
||||
self.version_parts = version_parts or version[1:].split(
|
||||
'-', 1)[0].split('.')[:3]
|
||||
self.enabled_components = self._gr_prefs.get_string(
|
||||
'grc', 'enabled_components', '')
|
||||
if name:
|
||||
self.name = name
|
||||
|
||||
|
||||
@ -96,12 +96,14 @@ class Connection(Element):
|
||||
if source_dtype != sink_dtype and source_dtype not in ALIASES_OF.get(
|
||||
sink_dtype, set()
|
||||
):
|
||||
self.add_error_message('Source IO type "{}" does not match sink IO type "{}".'.format(source_dtype, sink_dtype))
|
||||
self.add_error_message('Source IO type "{}" does not match sink IO type "{}".'.format(
|
||||
source_dtype, sink_dtype))
|
||||
|
||||
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))
|
||||
self.add_error_message(
|
||||
'Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size))
|
||||
|
||||
##############################################
|
||||
# Import/Export Methods
|
||||
|
||||
@ -41,7 +41,7 @@ DEFAULT_DOMAIN = GR_STREAM_DOMAIN
|
||||
|
||||
# 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
|
||||
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 = {
|
||||
@ -50,7 +50,7 @@ PARAM_TYPE_NAMES = {
|
||||
'complex_vector', 'real_vector', 'float_vector', 'int_vector',
|
||||
'hex', 'string', 'bool',
|
||||
'file_open', 'file_save', 'dir_select', '_multiline', '_multiline_python_external',
|
||||
'id', 'stream_id','name',
|
||||
'id', 'stream_id', 'name',
|
||||
'gui_hint',
|
||||
'import',
|
||||
}
|
||||
@ -89,31 +89,31 @@ 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),
|
||||
('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),
|
||||
('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),
|
||||
}
|
||||
|
||||
ALIASES_OF = {
|
||||
@ -135,4 +135,5 @@ ALIASES_OF = {
|
||||
}
|
||||
|
||||
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())
|
||||
TYPE_TO_SIZEOF.update((key, sizeof)
|
||||
for key, (sizeof, _) in ALIAS_TYPES.items())
|
||||
|
||||
@ -68,7 +68,8 @@ class FlowGraph(Element):
|
||||
Returns:
|
||||
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]
|
||||
variables = [block for block in self.iter_enabled_blocks()
|
||||
if block.is_variable]
|
||||
return expr_utils.sort_objects(variables, attrgetter('name'), methodcaller('get_var_make'))
|
||||
|
||||
def get_parameters(self):
|
||||
@ -78,7 +79,8 @@ class FlowGraph(Element):
|
||||
Returns:
|
||||
a list of parameterized variables
|
||||
"""
|
||||
parameters = [b for b in self.iter_enabled_blocks() if b.key == 'parameter']
|
||||
parameters = [b for b in self.iter_enabled_blocks()
|
||||
if b.key == 'parameter']
|
||||
return parameters
|
||||
|
||||
def get_snippets(self):
|
||||
@ -126,7 +128,8 @@ class FlowGraph(Element):
|
||||
"""
|
||||
Get a list of all ControlPort monitors
|
||||
"""
|
||||
monitors = [b for b in self.iter_enabled_blocks() if 'ctrlport_monitor' in b.key]
|
||||
monitors = [b for b in self.iter_enabled_blocks()
|
||||
if 'ctrlport_monitor' in b.key]
|
||||
return monitors
|
||||
|
||||
def get_python_modules(self):
|
||||
@ -189,7 +192,8 @@ class FlowGraph(Element):
|
||||
filename=shlex.quote(file_path))
|
||||
return shlex.split(run_command) if split else run_command
|
||||
except Exception as e:
|
||||
raise ValueError("Can't parse run command {!r}: {}".format(run_command, e))
|
||||
raise ValueError(
|
||||
"Can't parse run command {!r}: {}".format(run_command, e))
|
||||
|
||||
def get_imported_names(self):
|
||||
"""
|
||||
@ -241,7 +245,8 @@ class FlowGraph(Element):
|
||||
# this is ok behavior, unfortunately we could be hiding other import bugs
|
||||
pass
|
||||
except Exception:
|
||||
log.exception('Failed to evaluate import expression "{0}"'.format(expr), exc_info=True)
|
||||
log.exception('Failed to evaluate import expression "{0}"'.format(
|
||||
expr), exc_info=True)
|
||||
pass
|
||||
|
||||
self.imported_names = list(namespace.keys())
|
||||
@ -252,17 +257,20 @@ class FlowGraph(Element):
|
||||
exec(expr, module.__dict__)
|
||||
namespace[id] = module
|
||||
except Exception:
|
||||
log.exception('Failed to evaluate expression in module {0}'.format(id), exc_info=True)
|
||||
log.exception(
|
||||
'Failed to evaluate expression in module {0}'.format(id), exc_info=True)
|
||||
pass
|
||||
|
||||
# Load parameters
|
||||
np = {} # params don't know each other
|
||||
for parameter_block in self.get_parameters():
|
||||
try:
|
||||
value = eval(parameter_block.params['value'].to_code(), namespace)
|
||||
value = eval(
|
||||
parameter_block.params['value'].to_code(), namespace)
|
||||
np[parameter_block.name] = value
|
||||
except Exception:
|
||||
log.exception('Failed to evaluate parameter block {0}'.format(parameter_block.name), exc_info=True)
|
||||
log.exception('Failed to evaluate parameter block {0}'.format(
|
||||
parameter_block.name), exc_info=True)
|
||||
pass
|
||||
namespace.update(np) # Merge param namespace
|
||||
|
||||
@ -273,13 +281,16 @@ class FlowGraph(Element):
|
||||
for variable_block in self.get_variables():
|
||||
try:
|
||||
variable_block.rewrite()
|
||||
value = eval(variable_block.value, namespace, variable_block.namespace)
|
||||
value = eval(variable_block.value, namespace,
|
||||
variable_block.namespace)
|
||||
namespace[variable_block.name] = value
|
||||
self.namespace.update(namespace) # rewrite on subsequent blocks depends on an updated self.namespace
|
||||
except TypeError: #Type Errors may happen, but that doesn't matter as they are displayed in the gui
|
||||
# rewrite on subsequent blocks depends on an updated self.namespace
|
||||
self.namespace.update(namespace)
|
||||
except TypeError: # Type Errors may happen, but that doesn't matter as they are displayed in the gui
|
||||
pass
|
||||
except Exception:
|
||||
log.exception('Failed to evaluate variable block {0}'.format(variable_block.name), exc_info=True)
|
||||
log.exception('Failed to evaluate variable block {0}'.format(
|
||||
variable_block.name), exc_info=True)
|
||||
pass
|
||||
|
||||
self._eval_cache.clear()
|
||||
@ -397,7 +408,8 @@ class FlowGraph(Element):
|
||||
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)
|
||||
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):
|
||||
@ -424,7 +436,8 @@ class FlowGraph(Element):
|
||||
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)
|
||||
self.new_block(block_id='_dummy',
|
||||
missing_block_id=block_id, **block_data)
|
||||
)
|
||||
|
||||
block.import_data(**block_data)
|
||||
@ -443,7 +456,8 @@ class FlowGraph(Element):
|
||||
if block.is_dummy_block:
|
||||
port = block.add_missing_port(key, dir)
|
||||
else:
|
||||
raise LookupError('%s key %r not in %s block keys' % (dir, key, dir))
|
||||
raise LookupError(
|
||||
'%s key %r not in %s block keys' % (dir, key, dir))
|
||||
return port
|
||||
|
||||
had_connect_errors = False
|
||||
@ -461,8 +475,10 @@ class FlowGraph(Element):
|
||||
src_port_id, snk_port_id, source_block, sink_block)
|
||||
|
||||
# build the connection
|
||||
source_port = verify_and_get_port(src_port_id, source_block, 'source')
|
||||
sink_port = verify_and_get_port(snk_port_id, 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)
|
||||
|
||||
@ -478,7 +494,8 @@ class FlowGraph(Element):
|
||||
# Flowgraph errors depending on disabled blocks are not displayed
|
||||
# in the error dialog box
|
||||
# So put a message into the Property window of the dummy block
|
||||
block.add_error_message('Block id "{}" not found.'.format(block.key))
|
||||
block.add_error_message(
|
||||
'Block id "{}" not found.'.format(block.key))
|
||||
|
||||
self.rewrite() # global rewrite
|
||||
return had_connect_errors
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#
|
||||
|
||||
|
||||
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
@ -43,6 +42,7 @@ def send(message):
|
||||
for messenger in MESSENGERS_LIST:
|
||||
messenger(_indent + message)
|
||||
|
||||
|
||||
# register stdout by default
|
||||
register_messenger(sys.stdout.write)
|
||||
|
||||
@ -117,7 +117,8 @@ def send_fail_save(file_path):
|
||||
|
||||
|
||||
def send_fail_connection(msg=''):
|
||||
send('>>> Error: Cannot create connection.\n' + ('\t{}\n'.format(msg) if msg else ''))
|
||||
send('>>> Error: Cannot create connection.\n' +
|
||||
('\t{}\n'.format(msg) if msg else ''))
|
||||
|
||||
|
||||
def send_fail_load_preferences(prefs_file_path):
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -21,6 +21,7 @@ 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
|
||||
|
||||
@ -79,7 +79,8 @@ def build_ports(ports_raw, direction):
|
||||
|
||||
port_id = port.setdefault('id', str(next(stream_port_ids)))
|
||||
if port_id in port_ids:
|
||||
raise Exception('Port id "{}" already exists in {}s'.format(port_id, direction))
|
||||
raise Exception(
|
||||
'Port id "{}" already exists in {}s'.format(port_id, direction))
|
||||
port_ids.add(port_id)
|
||||
|
||||
ports.append(port)
|
||||
@ -137,16 +138,19 @@ def _single_mako_expr(value, block_id):
|
||||
return None
|
||||
value = value.strip()
|
||||
if not (value.startswith('${') and value.endswith('}')):
|
||||
raise ValueError('{} is not a mako substitution in {}'.format(value, block_id))
|
||||
raise ValueError(
|
||||
'{} is not a mako substitution in {}'.format(value, block_id))
|
||||
return value[2:-1].strip()
|
||||
|
||||
|
||||
def _validate_option_attributes(param_data, block_id):
|
||||
if param_data['dtype'] != 'enum':
|
||||
send_warning('{} - option_attributes are for enums only, ignoring'.format(block_id))
|
||||
send_warning(
|
||||
'{} - option_attributes are for enums only, ignoring'.format(block_id))
|
||||
del param_data['option_attributes']
|
||||
else:
|
||||
for key in list(param_data['option_attributes'].keys()):
|
||||
if key in dir(str):
|
||||
del param_data['option_attributes'][key]
|
||||
send_warning('{} - option_attribute "{}" overrides str, ignoring'.format(block_id, key))
|
||||
send_warning(
|
||||
'{} - option_attribute "{}" overrides str, ignoring'.format(block_id, key))
|
||||
|
||||
@ -16,12 +16,15 @@ from ..errors import TemplateError
|
||||
|
||||
# The utils dict contains convenience functions
|
||||
# that can be called from any template
|
||||
|
||||
|
||||
def no_quotes(string, fallback=None):
|
||||
if len(string) > 2:
|
||||
if str(string)[0] + str(string)[-1] in ("''", '""'):
|
||||
return str(string)[1:-1]
|
||||
return str(fallback if fallback else string)
|
||||
|
||||
|
||||
utils = {'no_quotes': no_quotes}
|
||||
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ from ._flags import Flags
|
||||
from ..base import Element
|
||||
from ..utils.descriptors import lazy_property
|
||||
|
||||
|
||||
def _get_elem(iterable, key):
|
||||
items = list(iterable)
|
||||
for item in items:
|
||||
@ -39,7 +40,7 @@ class Block(Element):
|
||||
key = ''
|
||||
label = ''
|
||||
category = []
|
||||
vtype = '' # This is only used for variables when we want C++ output
|
||||
vtype = '' # This is only used for variables when we want C++ output
|
||||
flags = Flags('')
|
||||
documentation = {'': ''}
|
||||
|
||||
@ -67,13 +68,16 @@ class Block(Element):
|
||||
if self.key == 'options':
|
||||
self.params['id'].hide = 'part'
|
||||
|
||||
self.sinks = [port_factory(parent=self, **params) for params in self.inputs_data]
|
||||
self.sources = [port_factory(parent=self, **params) for params in self.outputs_data]
|
||||
self.sinks = [port_factory(parent=self, **params)
|
||||
for params in self.inputs_data]
|
||||
self.sources = [port_factory(parent=self, **params)
|
||||
for params in self.outputs_data]
|
||||
|
||||
self.active_sources = [] # on rewrite
|
||||
self.active_sinks = [] # on rewrite
|
||||
|
||||
self.states = {'state': True, 'bus_source': False, 'bus_sink': False, 'bus_structure': None}
|
||||
self.states = {'state': True, 'bus_source': False,
|
||||
'bus_sink': False, 'bus_structure': None}
|
||||
self.block_namespace = {}
|
||||
self.deprecated = self.is_deprecated()
|
||||
|
||||
@ -81,7 +85,8 @@ class Block(Element):
|
||||
# This is a workaround to allow embedded python blocks/modules to load as there is
|
||||
# currently 'cpp' in the flags by default caused by the other built-in blocks
|
||||
if hasattr(self, 'cpp_templates'):
|
||||
self.orig_cpp_templates = self.cpp_templates # The original template, in case we have to edit it when transpiling to C++
|
||||
# The original template, in case we have to edit it when transpiling to C++
|
||||
self.orig_cpp_templates = self.cpp_templates
|
||||
|
||||
self.current_bus_structure = {'source': None, 'sink': None}
|
||||
|
||||
@ -100,8 +105,8 @@ class Block(Element):
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
# region Rewrite_and_Validation
|
||||
|
||||
def rewrite(self):
|
||||
"""
|
||||
Add and remove ports to adjust for the nports.
|
||||
@ -125,7 +130,8 @@ class Block(Element):
|
||||
|
||||
self.update_bus_logic()
|
||||
# disconnect hidden ports
|
||||
self.parent_flowgraph.disconnect(*[p for p in self.ports() if p.hidden])
|
||||
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]
|
||||
@ -142,11 +148,12 @@ class Block(Element):
|
||||
# this is ok behavior, unfortunately we could be hiding other import bugs
|
||||
pass
|
||||
except Exception:
|
||||
self.add_error_message(f'Failed to evaluate import expression {imports!r}')
|
||||
self.add_error_message(
|
||||
f'Failed to evaluate import expression {imports!r}')
|
||||
|
||||
def update_bus_logic(self):
|
||||
###############################
|
||||
## Bus Logic
|
||||
# Bus Logic
|
||||
###############################
|
||||
|
||||
for direc in {'source', 'sink'}:
|
||||
@ -170,13 +177,12 @@ class Block(Element):
|
||||
removed_bus_connections.append(c)
|
||||
ports.remove(port)
|
||||
|
||||
|
||||
if (bus_state):
|
||||
struct = self.form_bus_structure(direc)
|
||||
self.current_bus_structure[direc] = struct
|
||||
|
||||
# Hide ports that are not part of the bus structure
|
||||
#TODO: Blocks where it is desired to only have a subset
|
||||
# TODO: Blocks where it is desired to only have a subset
|
||||
# of ports included in the bus still has some issues
|
||||
for idx, port in enumerate(ports):
|
||||
if any([idx in bus for bus in self.current_bus_structure[direc]]):
|
||||
@ -187,19 +193,20 @@ class Block(Element):
|
||||
# Add the Bus Ports to the list of ports
|
||||
for i in range(len(struct)):
|
||||
# self.sinks = [port_factory(parent=self, **params) for params in self.inputs_data]
|
||||
port = self.parent.parent.make_port(self, direction=direc, id=str(len(ports)), label='bus', dtype='bus', bus_struct=struct[i])
|
||||
port = self.parent.parent.make_port(self, direction=direc, id=str(
|
||||
len(ports)), label='bus', dtype='bus', bus_struct=struct[i])
|
||||
ports.append(port)
|
||||
|
||||
for (saved_port, connection) in zip(removed_bus_ports, removed_bus_connections):
|
||||
if port.key == saved_port.key:
|
||||
self.parent_flowgraph.connections.remove(connection)
|
||||
self.parent_flowgraph.connections.remove(
|
||||
connection)
|
||||
if saved_port.is_source:
|
||||
connection.source_port = port
|
||||
if saved_port.is_sink:
|
||||
connection.sink_port = port
|
||||
self.parent_flowgraph.connections.add(connection)
|
||||
|
||||
|
||||
else:
|
||||
self.current_bus_structure[direc] = None
|
||||
|
||||
@ -209,8 +216,6 @@ class Block(Element):
|
||||
port.hidden = port.stored_hidden_state
|
||||
port.stored_hidden_state = None
|
||||
|
||||
|
||||
|
||||
def _rewrite_nports(self, ports):
|
||||
for port in ports:
|
||||
if hasattr(port, 'master_port'): # Not a master port and no left-over clones
|
||||
@ -218,7 +223,7 @@ class Block(Element):
|
||||
port.vlen = port.master_port.vlen
|
||||
continue
|
||||
nports = port.multiplicity
|
||||
for clone in port.clones[nports-1:]:
|
||||
for clone in port.clones[nports - 1:]:
|
||||
# Remove excess connections
|
||||
self.parent_flowgraph.disconnect(clone)
|
||||
port.remove_clone(clone)
|
||||
@ -245,9 +250,11 @@ class Block(Element):
|
||||
for expr in self.asserts:
|
||||
try:
|
||||
if not self.evaluate(expr):
|
||||
self.add_error_message('Assertion "{}" failed.'.format(expr))
|
||||
self.add_error_message(
|
||||
'Assertion "{}" failed.'.format(expr))
|
||||
except Exception:
|
||||
self.add_error_message('Assertion "{}" did not evaluate.'.format(expr))
|
||||
self.add_error_message(
|
||||
'Assertion "{}" did not evaluate.'.format(expr))
|
||||
|
||||
def _validate_generate_mode_compat(self):
|
||||
"""check if this is a GUI block and matches the selected generate option"""
|
||||
@ -261,7 +268,8 @@ class Block(Element):
|
||||
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'))
|
||||
check_generate_mode('QT GUI', Flags.NEED_QT_GUI,
|
||||
('qt_gui', 'hb_qt_gui'))
|
||||
|
||||
def _validate_output_language_compat(self):
|
||||
"""check if this block supports the selected output language"""
|
||||
@ -269,19 +277,23 @@ class Block(Element):
|
||||
|
||||
if current_output_language == 'cpp':
|
||||
if 'cpp' not in self.flags:
|
||||
self.add_error_message("This block does not support C++ output.")
|
||||
self.add_error_message(
|
||||
"This block does not support C++ output.")
|
||||
|
||||
if self.key == 'parameter':
|
||||
if not self.params['type'].value:
|
||||
self.add_error_message("C++ output requires you to choose a parameter type.")
|
||||
self.add_error_message(
|
||||
"C++ output requires you to choose a parameter type.")
|
||||
|
||||
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)
|
||||
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))
|
||||
self.add_error_message(
|
||||
'Value "{}" cannot be evaluated:\n{}'.format(self.value, err))
|
||||
# endregion
|
||||
|
||||
# region Properties
|
||||
@ -458,7 +470,7 @@ class Block(Element):
|
||||
# For container types we must also determine the type of the template parameter(s)
|
||||
return 'std::vector<' + get_type(str(evaluated[0]), type(evaluated[0])) + '>'
|
||||
|
||||
except IndexError: # empty list
|
||||
except IndexError: # empty list
|
||||
return 'std::vector<std::string>'
|
||||
|
||||
if _vtype == dict:
|
||||
@ -466,9 +478,9 @@ class Block(Element):
|
||||
# For container types we must also determine the type of the template parameter(s)
|
||||
key = list(evaluated)[0]
|
||||
val = list(evaluated.values())[0]
|
||||
return 'std::map<' + get_type(str(key), type(key)) + ', ' + get_type(str(val), type(val)) +'>'
|
||||
return 'std::map<' + get_type(str(key), type(key)) + ', ' + get_type(str(val), type(val)) + '>'
|
||||
|
||||
except IndexError: # empty dict
|
||||
except IndexError: # empty dict
|
||||
return 'std::map<std::string, std::string>'
|
||||
|
||||
else:
|
||||
@ -480,12 +492,12 @@ class Block(Element):
|
||||
# The r-value for these types must be transformed to create legal C++ syntax.
|
||||
if self.vtype in ['bool', 'gr_complex'] or 'std::map' in self.vtype or 'std::vector' in self.vtype:
|
||||
evaluated = ast.literal_eval(value)
|
||||
self.cpp_templates['var_make'] = self.cpp_templates['var_make'].replace('${value}', self.get_cpp_value(evaluated))
|
||||
self.cpp_templates['var_make'] = self.cpp_templates['var_make'].replace(
|
||||
'${value}', self.get_cpp_value(evaluated))
|
||||
|
||||
if 'string' in self.vtype:
|
||||
self.cpp_templates['includes'].append('#include <string>')
|
||||
|
||||
|
||||
def get_cpp_value(self, pyval):
|
||||
|
||||
if type(pyval) == int or type(pyval) == float:
|
||||
@ -494,8 +506,10 @@ class Block(Element):
|
||||
# Check for PI and replace with C++ constant
|
||||
pi_re = r'^(math|numpy|np|scipy|sp)\.pi$'
|
||||
if re.match(pi_re, str(pyval)):
|
||||
val_str = re.sub(pi_re, 'boost::math::constants::pi<double>()', val_str)
|
||||
self.cpp_templates['includes'].append('#include <boost/math/constants/constants.hpp>')
|
||||
val_str = re.sub(
|
||||
pi_re, 'boost::math::constants::pi<double>()', val_str)
|
||||
self.cpp_templates['includes'].append(
|
||||
'#include <boost/math/constants/constants.hpp>')
|
||||
|
||||
return str(pyval)
|
||||
|
||||
@ -503,7 +517,8 @@ class Block(Element):
|
||||
return str(pyval)[0].lower() + str(pyval)[1:]
|
||||
|
||||
elif type(pyval) == complex:
|
||||
self.cpp_templates['includes'].append('#include <gnuradio/gr_complex.h>')
|
||||
self.cpp_templates['includes'].append(
|
||||
'#include <gnuradio/gr_complex.h>')
|
||||
evaluated = ast.literal_eval(str(pyval).strip())
|
||||
return '{' + str(evaluated.real) + ', ' + str(evaluated.imag) + '}'
|
||||
|
||||
@ -523,7 +538,8 @@ class Block(Element):
|
||||
self.cpp_templates['includes'].append('#include <map>')
|
||||
val_str = '{'
|
||||
for key in pyval:
|
||||
val_str += '{' + self.get_cpp_value(key) + ', ' + self.get_cpp_value(pyval[key]) + '}, '
|
||||
val_str += '{' + self.get_cpp_value(key) + \
|
||||
', ' + self.get_cpp_value(pyval[key]) + '}, '
|
||||
|
||||
if len(val_str) > 1:
|
||||
# truncate to trim superfluous ', ' from the end
|
||||
@ -535,7 +551,6 @@ class Block(Element):
|
||||
self.cpp_templates['includes'].append('#include <string>')
|
||||
return '"' + pyval + '"'
|
||||
|
||||
|
||||
def is_virtual_sink(self):
|
||||
return self.key == 'virtual_sink'
|
||||
|
||||
@ -561,8 +576,8 @@ class Block(Element):
|
||||
print(exception.message)
|
||||
return False
|
||||
|
||||
|
||||
# Block bypassing
|
||||
|
||||
def get_bypassed(self):
|
||||
"""
|
||||
Check if the block is bypassed
|
||||
@ -618,7 +633,8 @@ class Block(Element):
|
||||
@property
|
||||
def namespace(self):
|
||||
# update block namespace
|
||||
self.block_namespace.update({key:param.get_evaluated() for key, param in self.params.items()})
|
||||
self.block_namespace.update(
|
||||
{key: param.get_evaluated() for key, param in self.params.items()})
|
||||
return self.block_namespace
|
||||
|
||||
@property
|
||||
@ -721,7 +737,7 @@ class Block(Element):
|
||||
|
||||
struct = [range(len(ports))]
|
||||
# struct = list(range(len(ports)))
|
||||
#TODO for more complicated port structures, this code is needed but not working yet
|
||||
# TODO for more complicated port structures, this code is needed but not working yet
|
||||
if any([p.multiplicity for p in ports]):
|
||||
structlet = []
|
||||
last = 0
|
||||
@ -736,8 +752,8 @@ class Block(Element):
|
||||
continue
|
||||
|
||||
if p.multiplicity > 1:
|
||||
cnt = p.multiplicity-1
|
||||
structlet.append([idx+j for j in range(p.multiplicity)])
|
||||
cnt = p.multiplicity - 1
|
||||
structlet.append([idx + j for j in range(p.multiplicity)])
|
||||
else:
|
||||
structlet.append([idx])
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ from . import Block, register_build_in
|
||||
|
||||
from ._build import build_params
|
||||
|
||||
|
||||
@register_build_in
|
||||
class DummyBlock(Block):
|
||||
|
||||
@ -19,12 +20,14 @@ class DummyBlock(Block):
|
||||
|
||||
def __init__(self, parent, missing_block_id, parameters, **_):
|
||||
self.key = missing_block_id
|
||||
self.parameters_data = build_params([], False, False, self.flags, self.key)
|
||||
self.parameters_data = build_params(
|
||||
[], False, False, self.flags, self.key)
|
||||
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'))
|
||||
self.params.setdefault(param_id, param_factory(
|
||||
parent=self, id=param_id, dtype='string'))
|
||||
|
||||
def is_valid(self):
|
||||
return False
|
||||
|
||||
@ -122,7 +122,8 @@ class EPyBlock(Block):
|
||||
self.label = blk_io.name or blk_io.cls
|
||||
self.documentation = {'': blk_io.doc}
|
||||
|
||||
self.module_name = "{}_{}".format(self.parent_flowgraph.get_option("id"), self.name)
|
||||
self.module_name = "{}_{}".format(
|
||||
self.parent_flowgraph.get_option("id"), self.name)
|
||||
self.templates['imports'] = 'import {} as {} # embedded python block'.format(
|
||||
self.module_name, self.name)
|
||||
self.templates['make'] = '{mod}.{cls}({args})'.format(
|
||||
@ -131,7 +132,7 @@ class EPyBlock(Block):
|
||||
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')
|
||||
@ -195,7 +196,8 @@ class EPyBlock(Block):
|
||||
def validate(self):
|
||||
super(EPyBlock, self).validate()
|
||||
if self._epy_reload_error:
|
||||
self.params['_source_code'].add_error_message(str(self._epy_reload_error))
|
||||
self.params['_source_code'].add_error_message(
|
||||
str(self._epy_reload_error))
|
||||
|
||||
|
||||
@register_build_in
|
||||
@ -240,6 +242,7 @@ class EPyModule(Block):
|
||||
|
||||
def rewrite(self):
|
||||
super(EPyModule, self).rewrite()
|
||||
self.module_name = "{}_{}".format(self.parent_flowgraph.get_option("id"), self.name)
|
||||
self.module_name = "{}_{}".format(
|
||||
self.parent_flowgraph.get_option("id"), self.name)
|
||||
self.templates['imports'] = 'import {} as {} # embedded python module'.format(
|
||||
self.module_name, self.name)
|
||||
|
||||
@ -21,7 +21,8 @@ class VirtualSink(Block):
|
||||
flags.set('cpp')
|
||||
|
||||
parameters_data = build_params(
|
||||
params_raw=[dict(label='Stream ID', id='stream_id', dtype='stream_id')],
|
||||
params_raw=[
|
||||
dict(label='Stream ID', id='stream_id', dtype='stream_id')],
|
||||
have_inputs=False, have_outputs=False, flags=flags, block_id=key
|
||||
)
|
||||
inputs_data = [dict(domain='stream', dtype='', direction='sink', id="0")]
|
||||
@ -45,10 +46,12 @@ class VirtualSource(Block):
|
||||
flags.set('cpp')
|
||||
|
||||
parameters_data = build_params(
|
||||
params_raw=[dict(label='Stream ID', id='stream_id', dtype='stream_id')],
|
||||
params_raw=[
|
||||
dict(label='Stream ID', id='stream_id', dtype='stream_id')],
|
||||
have_inputs=False, have_outputs=False, flags=flags, block_id=key
|
||||
)
|
||||
outputs_data = [dict(domain='stream', dtype='', direction='source', id="0")]
|
||||
outputs_data = [dict(domain='stream', dtype='',
|
||||
direction='source', id="0")]
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
super(VirtualSource, self).__init__(parent, **kwargs)
|
||||
|
||||
@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class Cache(object):
|
||||
|
||||
def __init__(self, filename, version = None):
|
||||
def __init__(self, filename, version=None):
|
||||
self.cache_file = filename
|
||||
self.version = version
|
||||
self.cache = {}
|
||||
@ -57,7 +57,7 @@ class Cache(object):
|
||||
if modtime <= self._converter_mtime:
|
||||
try:
|
||||
cached = self.cache[filename]
|
||||
if int(cached["cached-at"]+0.5) >= modtime:
|
||||
if int(cached["cached-at"] + 0.5) >= modtime:
|
||||
return cached["data"]
|
||||
logger.info(f"Cache for {filename} outdated, loading yaml")
|
||||
except KeyError:
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#
|
||||
|
||||
|
||||
|
||||
class GRCError(Exception):
|
||||
"""Generic error class"""
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
from ..utils import expr_utils
|
||||
from operator import methodcaller, attrgetter
|
||||
|
||||
|
||||
class FlowGraphProxy(object): # TODO: move this in a refactored Generator
|
||||
|
||||
def __init__(self, fg):
|
||||
@ -59,8 +60,8 @@ class FlowGraphProxy(object): # TODO: move this in a refactored Generator
|
||||
'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'],
|
||||
'cpp_size': type_param.options.attributes[type_param.get_value()]['cpp_size'],
|
||||
'size': type_param.options.attributes[type_param.get_value()]['size'],
|
||||
'cpp_size': type_param.options.attributes[type_param.get_value()]['cpp_size'],
|
||||
'optional': bool(pad.params['optional'].get_evaluated()),
|
||||
}
|
||||
num_ports = pad.params['num_streams'].get_evaluated()
|
||||
@ -127,7 +128,8 @@ class FlowGraphProxy(object): # TODO: move this in a refactored Generator
|
||||
Returns:
|
||||
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]
|
||||
variables = [block for block in self.iter_enabled_blocks()
|
||||
if block.is_variable]
|
||||
return expr_utils.sort_objects(variables, attrgetter('name'), methodcaller('get_cpp_var_make'))
|
||||
|
||||
def includes(self):
|
||||
@ -157,6 +159,7 @@ class FlowGraphProxy(object): # TODO: move this in a refactored Generator
|
||||
"""
|
||||
return [block.cpp_templates.render('packages') for block in self.iter_enabled_blocks() if not (block.is_virtual_sink() or block.is_virtual_source())]
|
||||
|
||||
|
||||
def get_hier_block_io(flow_graph, direction, domain=None):
|
||||
"""
|
||||
Get a list of io ports for this flow graph.
|
||||
@ -172,7 +175,7 @@ def get_hier_block_io(flow_graph, direction, domain=None):
|
||||
'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'],
|
||||
'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()
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#
|
||||
|
||||
|
||||
|
||||
from .hier_block import HierBlockGenerator, QtHierBlockGenerator
|
||||
from .top_block import TopBlockGenerator
|
||||
from .cpp_top_block import CppTopBlockGenerator
|
||||
|
||||
@ -8,6 +8,7 @@ from .cpp_top_block import CppTopBlockGenerator
|
||||
from .. import Constants
|
||||
from ..io import yaml
|
||||
|
||||
|
||||
class CppHierBlockGenerator(CppTopBlockGenerator):
|
||||
"""Extends the top block generator to also generate a block YML file"""
|
||||
|
||||
@ -27,7 +28,8 @@ class CppHierBlockGenerator(CppTopBlockGenerator):
|
||||
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'))
|
||||
self.file_path = os.path.join(
|
||||
hier_block_lib_dir, self._flow_graph.get_option('id'))
|
||||
self.file_path_yml = self.file_path + '.block.yml'
|
||||
|
||||
def write(self):
|
||||
@ -55,7 +57,6 @@ class CppHierBlockGenerator(CppTopBlockGenerator):
|
||||
# Windows only supports S_IREAD and S_IWRITE, other flags are ignored
|
||||
os.chmod(self.file_path_yml, self._mode)
|
||||
|
||||
|
||||
def _build_block_n_from_flow_graph_io(self):
|
||||
"""
|
||||
Generate a block YML nested data from the flow graph IO
|
||||
@ -136,7 +137,8 @@ class CppHierBlockGenerator(CppTopBlockGenerator):
|
||||
t_cpp = data['cpp_templates'] = collections.OrderedDict()
|
||||
|
||||
t_cpp['includes'] = []
|
||||
t_cpp['includes'].append('#include "{id}/{id}.hpp"'.format(id=self._flow_graph.get_option('id')))
|
||||
t_cpp['includes'].append(
|
||||
'#include "{id}/{id}.hpp"'.format(id=self._flow_graph.get_option('id')))
|
||||
|
||||
# Make data
|
||||
if parameters:
|
||||
@ -147,7 +149,8 @@ class CppHierBlockGenerator(CppTopBlockGenerator):
|
||||
),
|
||||
)
|
||||
else:
|
||||
t_cpp['make'] = 'this->${{id}} = {cls}_sptr(make_{cls}());'.format(cls=block_id)
|
||||
t_cpp['make'] = 'this->${{id}} = {cls}_sptr(make_{cls}());'.format(
|
||||
cls=block_id)
|
||||
t_cpp['declarations'] = '{cls}_sptr ${{id}};'.format(cls=block_id)
|
||||
|
||||
# Callback data
|
||||
@ -206,7 +209,8 @@ def get_hier_block_io(flow_graph, direction, domain=None):
|
||||
|
||||
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()
|
||||
pads = flow_graph.get_pad_sources(
|
||||
) if direction == 'inputs' else flow_graph.get_pad_sinks()
|
||||
|
||||
for pad in pads:
|
||||
for port in (pad.sources if direction == 'inputs' else pad.sinks):
|
||||
|
||||
@ -38,7 +38,8 @@ class CppTopBlockGenerator(object):
|
||||
"""
|
||||
|
||||
self._flow_graph = FlowGraphProxy(flow_graph)
|
||||
self._generate_options = self._flow_graph.get_option('generate_options')
|
||||
self._generate_options = self._flow_graph.get_option(
|
||||
'generate_options')
|
||||
|
||||
self._mode = TOP_BLOCK_FILE_MODE
|
||||
# Handle the case where the directory is read-only
|
||||
@ -48,25 +49,26 @@ class CppTopBlockGenerator(object):
|
||||
filename = self._flow_graph.get_option('id')
|
||||
self.file_path = os.path.join(output_dir, filename)
|
||||
self.output_dir = output_dir
|
||||
|
||||
|
||||
def _warnings(self):
|
||||
throttling_blocks = [b for b in self._flow_graph.get_enabled_blocks()
|
||||
if b.flags.throttle]
|
||||
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.")
|
||||
"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.")
|
||||
"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}
|
||||
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))
|
||||
|
||||
@ -76,7 +78,8 @@ class CppTopBlockGenerator(object):
|
||||
|
||||
fg = self._flow_graph
|
||||
platform = fg.parent
|
||||
self.title = fg.get_option('title') or fg.get_option('id').replace('_', ' ').title()
|
||||
self.title = fg.get_option('title') or fg.get_option(
|
||||
'id').replace('_', ' ').title()
|
||||
variables = fg.get_cpp_variables()
|
||||
parameters = fg.get_parameters()
|
||||
monitors = fg.get_monitors()
|
||||
@ -119,7 +122,8 @@ class CppTopBlockGenerator(object):
|
||||
Returns:
|
||||
a string of C++ code
|
||||
"""
|
||||
file_path = self.file_path + '/' + self._flow_graph.get_option('id') + '.cpp'
|
||||
file_path = self.file_path + '/' + \
|
||||
self._flow_graph.get_option('id') + '.cpp'
|
||||
|
||||
output = []
|
||||
|
||||
@ -132,12 +136,12 @@ class CppTopBlockGenerator(object):
|
||||
**self.namespace
|
||||
)
|
||||
# strip trailing white-space
|
||||
flow_graph_code = "\n".join(line.rstrip() for line in flow_graph_code.split("\n"))
|
||||
flow_graph_code = "\n".join(line.rstrip()
|
||||
for line in flow_graph_code.split("\n"))
|
||||
output.append((file_path, flow_graph_code))
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def _build_cpp_header_code_from_template(self):
|
||||
"""
|
||||
Convert the flow graph to a C++ header file.
|
||||
@ -145,7 +149,8 @@ class CppTopBlockGenerator(object):
|
||||
Returns:
|
||||
a string of C++ code
|
||||
"""
|
||||
file_path = self.file_path + '/' + self._flow_graph.get_option('id') + '.hpp'
|
||||
file_path = self.file_path + '/' + \
|
||||
self._flow_graph.get_option('id') + '.hpp'
|
||||
|
||||
output = []
|
||||
|
||||
@ -158,7 +163,8 @@ class CppTopBlockGenerator(object):
|
||||
**self.namespace
|
||||
)
|
||||
# strip trailing white-space
|
||||
flow_graph_code = "\n".join(line.rstrip() for line in flow_graph_code.split("\n"))
|
||||
flow_graph_code = "\n".join(line.rstrip()
|
||||
for line in flow_graph_code.split("\n"))
|
||||
output.append((file_path, flow_graph_code))
|
||||
|
||||
return output
|
||||
@ -175,7 +181,7 @@ class CppTopBlockGenerator(object):
|
||||
|
||||
cmake_tuples = []
|
||||
cmake_opt = self._flow_graph.get_option("cmake_opt")
|
||||
cmake_opt = " " + cmake_opt # To make sure we get rid of the "-D"s when splitting
|
||||
cmake_opt = " " + cmake_opt # To make sure we get rid of the "-D"s when splitting
|
||||
|
||||
for opt_string in cmake_opt.split(" -D"):
|
||||
opt_string = opt_string.strip()
|
||||
@ -196,7 +202,8 @@ class CppTopBlockGenerator(object):
|
||||
**self.namespace
|
||||
)
|
||||
# strip trailing white-space
|
||||
flow_graph_code = "\n".join(line.rstrip() for line in flow_graph_code.split("\n"))
|
||||
flow_graph_code = "\n".join(line.rstrip()
|
||||
for line in flow_graph_code.split("\n"))
|
||||
output.append((file_path, flow_graph_code))
|
||||
|
||||
return output
|
||||
@ -255,7 +262,8 @@ class CppTopBlockGenerator(object):
|
||||
def _get_block_sort_text(block):
|
||||
code = block.cpp_templates.render('declarations')
|
||||
try:
|
||||
code += block.params['gui_hint'].get_value() # Newer gui markup w/ qtgui
|
||||
# Newer gui markup w/ qtgui
|
||||
code += block.params['gui_hint'].get_value()
|
||||
except:
|
||||
pass
|
||||
return code
|
||||
@ -265,7 +273,8 @@ class CppTopBlockGenerator(object):
|
||||
if b.enabled and not (b.get_bypassed() or b.is_import or b in parameters or b.key == 'options' or b.is_virtual_source() or b.is_virtual_sink())
|
||||
]
|
||||
|
||||
blocks = expr_utils.sort_objects(blocks, operator.attrgetter('name'), _get_block_sort_text)
|
||||
blocks = expr_utils.sort_objects(
|
||||
blocks, operator.attrgetter('name'), _get_block_sort_text)
|
||||
blocks_make = []
|
||||
for block in blocks:
|
||||
translations = block.cpp_templates.render('translations')
|
||||
@ -279,7 +288,8 @@ class CppTopBlockGenerator(object):
|
||||
{r"gr\.sizeof_([\w_]+)": r"sizeof(\1)"}
|
||||
)
|
||||
for key in translations:
|
||||
make = re.sub(key.replace("\\\\", "\\"), translations[key], make)
|
||||
make = re.sub(key.replace("\\\\", "\\"),
|
||||
translations[key], make)
|
||||
declarations = declarations.replace(key, translations[key])
|
||||
if make:
|
||||
blocks_make.append((block, make, declarations))
|
||||
@ -293,7 +303,8 @@ class CppTopBlockGenerator(object):
|
||||
fg = self._flow_graph
|
||||
variables = fg.get_cpp_variables()
|
||||
|
||||
type_translation = {'complex': 'gr_complex', 'real': 'double', 'float': 'float', 'int': 'int', 'complex_vector': 'std::vector<gr_complex>', 'real_vector': 'std::vector<double>', 'float_vector': 'std::vector<float>', 'int_vector': 'std::vector<int>', 'string': 'std::string', 'bool': 'bool'}
|
||||
type_translation = {'complex': 'gr_complex', 'real': 'double', 'float': 'float', 'int': 'int', 'complex_vector': 'std::vector<gr_complex>',
|
||||
'real_vector': 'std::vector<double>', 'float_vector': 'std::vector<float>', 'int_vector': 'std::vector<int>', 'string': 'std::string', 'bool': 'bool'}
|
||||
# If the type is explcitly specified, translate to the corresponding C++ type
|
||||
for var in list(variables):
|
||||
if var.params['value'].dtype != 'raw':
|
||||
@ -304,26 +315,28 @@ class CppTopBlockGenerator(object):
|
||||
# Create an executable fragment of code containing all 'raw' variables in
|
||||
# order to infer the lvalue types.
|
||||
#
|
||||
# Note that this differs from using ast.literal_eval() as literal_eval evaluates one
|
||||
# variable at a time. The code fragment below evaluates all variables together which
|
||||
# Note that this differs from using ast.literal_eval() as literal_eval evaluates one
|
||||
# variable at a time. The code fragment below evaluates all variables together which
|
||||
# allows the variables to reference each other (i.e. a = b * c).
|
||||
prog = 'def get_decl_types():\n'
|
||||
prog += '\tvar_types = {}\n'
|
||||
for var in variables:
|
||||
prog += '\t' + str(var.params['id'].value) + '=' + str(var.params['value'].value) + '\n'
|
||||
prog += '\tvar_types = {}\n';
|
||||
prog += '\t' + str(var.params['id'].value) + \
|
||||
'=' + str(var.params['value'].value) + '\n'
|
||||
prog += '\tvar_types = {}\n'
|
||||
for var in variables:
|
||||
prog += '\tvar_types[\'' + str(var.params['id'].value) + '\'] = type(' + str(var.params['id'].value) + ')\n'
|
||||
prog += '\tvar_types[\'' + str(var.params['id'].value) + \
|
||||
'\'] = type(' + str(var.params['id'].value) + ')\n'
|
||||
prog += '\treturn var_types'
|
||||
|
||||
# Execute the code fragment in a separate namespace and retrieve the lvalue types
|
||||
var_types = {}
|
||||
namespace = {}
|
||||
try:
|
||||
exec(prog, namespace)
|
||||
var_types = namespace['get_decl_types']()
|
||||
exec(prog, namespace)
|
||||
var_types = namespace['get_decl_types']()
|
||||
except Exception as excp:
|
||||
print('Failed to get parameter lvalue types: %s' %(excp))
|
||||
print('Failed to get parameter lvalue types: %s' % (excp))
|
||||
|
||||
# Format the rvalue of each variable expression
|
||||
for var in variables:
|
||||
@ -334,20 +347,22 @@ class CppTopBlockGenerator(object):
|
||||
parameters = fg.get_parameters()
|
||||
|
||||
for param in parameters:
|
||||
type_translation = {'eng_float' : 'double', 'intx' : 'int', 'str' : 'std::string', 'complex': 'gr_complex'};
|
||||
type_translation = {'eng_float': 'double', 'intx': 'int',
|
||||
'str': 'std::string', 'complex': 'gr_complex'}
|
||||
param.vtype = type_translation[param.params['type'].value]
|
||||
|
||||
if param.vtype == 'gr_complex':
|
||||
evaluated = ast.literal_eval(param.params['value'].value.strip())
|
||||
cpp_cmplx = '{' + str(evaluated.real) + ', ' + str(evaluated.imag) + '}'
|
||||
evaluated = ast.literal_eval(
|
||||
param.params['value'].value.strip())
|
||||
cpp_cmplx = '{' + str(evaluated.real) + \
|
||||
', ' + str(evaluated.imag) + '}'
|
||||
|
||||
# Update the 'var_make' entry in the cpp_templates dictionary
|
||||
d = param.cpp_templates
|
||||
cpp_expr = d['var_make'].replace('${value}', cpp_cmplx)
|
||||
d.update({'var_make':cpp_expr})
|
||||
d.update({'var_make': cpp_expr})
|
||||
param.cpp_templates = d
|
||||
|
||||
|
||||
def _callbacks(self):
|
||||
fg = self._flow_graph
|
||||
variables = fg.get_cpp_variables()
|
||||
@ -361,16 +376,19 @@ class CppTopBlockGenerator(object):
|
||||
callbacks_all = []
|
||||
for block in fg.iter_enabled_blocks():
|
||||
if not (block.is_virtual_sink() or block.is_virtual_source()):
|
||||
callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_cpp_callbacks())
|
||||
callbacks_all.extend(expr_utils.expr_replace(
|
||||
cb, replace_dict) for cb in block.get_cpp_callbacks())
|
||||
|
||||
# Map var id to callbacks
|
||||
def uses_var_id(callback):
|
||||
used = expr_utils.get_variable_dependencies(callback, [var_id])
|
||||
return used and ('this->' + var_id in callback) # callback might contain var_id itself
|
||||
# callback might contain var_id itself
|
||||
return used and ('this->' + var_id in callback)
|
||||
|
||||
callbacks = {}
|
||||
for var_id in var_ids:
|
||||
callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id(callback)]
|
||||
callbacks[var_id] = [
|
||||
callback for callback in callbacks_all if uses_var_id(callback)]
|
||||
|
||||
return callbacks
|
||||
|
||||
@ -402,14 +420,17 @@ class CppTopBlockGenerator(object):
|
||||
|
||||
# Get the virtual blocks and resolve their connections
|
||||
connection_factory = fg.parent_platform.Connection
|
||||
virtual_source_connections = [c for c in connections if isinstance(c.source_block, blocks.VirtualSource)]
|
||||
virtual_source_connections = [c for c in connections if isinstance(
|
||||
c.source_block, blocks.VirtualSource)]
|
||||
for connection in virtual_source_connections:
|
||||
sink = connection.sink_port
|
||||
for source in connection.source_port.resolve_virtual_source():
|
||||
resolved = connection_factory(fg.orignal_flowgraph, source, sink)
|
||||
resolved = connection_factory(
|
||||
fg.orignal_flowgraph, source, sink)
|
||||
connections.append(resolved)
|
||||
|
||||
virtual_connections = [c for c in connections if (isinstance(c.source_block, blocks.VirtualSource) or isinstance(c.sink_block, blocks.VirtualSink))]
|
||||
virtual_connections = [c for c in connections if (isinstance(
|
||||
c.source_block, blocks.VirtualSource) or isinstance(c.sink_block, blocks.VirtualSink))]
|
||||
for connection in virtual_connections:
|
||||
# Remove the virtual connection
|
||||
connections.remove(connection)
|
||||
@ -423,7 +444,8 @@ class CppTopBlockGenerator(object):
|
||||
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]]
|
||||
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)
|
||||
|
||||
@ -435,7 +457,8 @@ class CppTopBlockGenerator(object):
|
||||
if not sink.enabled:
|
||||
# Ignore disabled connections
|
||||
continue
|
||||
connection = connection_factory(fg.orignal_flowgraph, source_port, sink.sink_port)
|
||||
connection = connection_factory(
|
||||
fg.orignal_flowgraph, source_port, sink.sink_port)
|
||||
connections.append(connection)
|
||||
# Remove this sink connection
|
||||
connections.remove(sink)
|
||||
@ -451,7 +474,8 @@ class CppTopBlockGenerator(object):
|
||||
template = templates[con.type]
|
||||
|
||||
if con.source_port.dtype != 'bus':
|
||||
code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port)
|
||||
code = template.render(
|
||||
make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port)
|
||||
if not self._generate_options.startswith('hb'):
|
||||
code = 'this->tb->' + code
|
||||
rendered.append(code)
|
||||
@ -470,7 +494,8 @@ class CppTopBlockGenerator(object):
|
||||
hidden_portb = portb.parent.sinks[port_num]
|
||||
connection = fg.parent_platform.Connection(
|
||||
parent=self, source=hidden_porta, sink=hidden_portb)
|
||||
code = template.render(make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb)
|
||||
code = template.render(
|
||||
make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb)
|
||||
if not self._generate_options.startswith('hb'):
|
||||
code = 'this->tb->' + code
|
||||
rendered.append(code)
|
||||
|
||||
@ -128,7 +128,6 @@ class HierBlockGenerator(TopBlockGenerator):
|
||||
'set_{key}(${{ {key} }})'.format(key=param_block.name) for param_block in parameters
|
||||
]
|
||||
|
||||
|
||||
# Documentation
|
||||
data['documentation'] = "\n".join(field for field in (
|
||||
self._flow_graph.get_option('author'),
|
||||
@ -178,7 +177,8 @@ def get_hier_block_io(flow_graph, direction, domain=None):
|
||||
|
||||
Returns a list of blocks
|
||||
"""
|
||||
pads = flow_graph.get_pad_sources() if direction == 'inputs' else flow_graph.get_pad_sinks()
|
||||
pads = flow_graph.get_pad_sources(
|
||||
) if direction == 'inputs' else flow_graph.get_pad_sinks()
|
||||
|
||||
for pad in pads:
|
||||
for port in (pad.sources if direction == 'inputs' else pad.sinks):
|
||||
|
||||
@ -30,7 +30,8 @@ class TopBlockGenerator(object):
|
||||
"""
|
||||
|
||||
self._flow_graph = FlowGraphProxy(flow_graph)
|
||||
self._generate_options = self._flow_graph.get_option('generate_options')
|
||||
self._generate_options = self._flow_graph.get_option(
|
||||
'generate_options')
|
||||
|
||||
self._mode = TOP_BLOCK_FILE_MODE
|
||||
# Handle the case where the directory is read-only
|
||||
@ -58,7 +59,8 @@ class TopBlockGenerator(object):
|
||||
"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}
|
||||
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))
|
||||
|
||||
@ -67,7 +69,8 @@ class TopBlockGenerator(object):
|
||||
self._warnings()
|
||||
|
||||
fg = self._flow_graph
|
||||
self.title = fg.get_option('title') or fg.get_option('id').replace('_', ' ').title()
|
||||
self.title = fg.get_option('title') or fg.get_option(
|
||||
'id').replace('_', ' ').title()
|
||||
variables = fg.get_variables()
|
||||
parameters = fg.get_parameters()
|
||||
monitors = fg.get_monitors()
|
||||
@ -97,7 +100,8 @@ class TopBlockGenerator(object):
|
||||
|
||||
fg = self._flow_graph
|
||||
platform = fg.parent
|
||||
title = fg.get_option('title') or fg.get_option('id').replace('_', ' ').title()
|
||||
title = fg.get_option('title') or fg.get_option(
|
||||
'id').replace('_', ' ').title()
|
||||
variables = fg.get_variables()
|
||||
parameters = fg.get_parameters()
|
||||
monitors = fg.get_monitors()
|
||||
@ -110,7 +114,8 @@ class TopBlockGenerator(object):
|
||||
else:
|
||||
continue
|
||||
|
||||
file_path = os.path.join(self.output_dir, block.module_name + ".py")
|
||||
file_path = os.path.join(
|
||||
self.output_dir, block.module_name + ".py")
|
||||
output.append((file_path, src))
|
||||
|
||||
self.namespace = {
|
||||
@ -131,7 +136,8 @@ class TopBlockGenerator(object):
|
||||
**self.namespace
|
||||
)
|
||||
# strip trailing white-space
|
||||
flow_graph_code = "\n".join(line.rstrip() for line in flow_graph_code.split("\n"))
|
||||
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
|
||||
@ -142,7 +148,8 @@ class TopBlockGenerator(object):
|
||||
seen = set()
|
||||
output = []
|
||||
|
||||
need_path_hack = any(imp.endswith("# grc-generated hier_block") for imp in imports)
|
||||
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
|
||||
@ -184,7 +191,8 @@ class TopBlockGenerator(object):
|
||||
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
|
||||
# Newer gui markup w/ qtgui
|
||||
code += block.params['gui_hint'].get_value()
|
||||
except KeyError:
|
||||
# No gui hint
|
||||
pass
|
||||
@ -195,7 +203,8 @@ class TopBlockGenerator(object):
|
||||
if b.enabled and not (b.get_bypassed() or b.is_import or b.is_snippet or b in parameters or b.key == 'options')
|
||||
]
|
||||
|
||||
blocks = expr_utils.sort_objects(blocks, operator.attrgetter('name'), _get_block_sort_text)
|
||||
blocks = expr_utils.sort_objects(
|
||||
blocks, operator.attrgetter('name'), _get_block_sort_text)
|
||||
blocks_make = []
|
||||
for block in blocks:
|
||||
make = block.templates.render('make')
|
||||
@ -217,16 +226,19 @@ class TopBlockGenerator(object):
|
||||
|
||||
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())
|
||||
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(callback):
|
||||
used = expr_utils.get_variable_dependencies(callback, [var_id])
|
||||
return used and (('self.' + var_id in callback) or ('this->' + var_id in callback)) # callback might contain var_id itself
|
||||
# callback might contain var_id itself
|
||||
return used and (('self.' + var_id in callback) or ('this->' + var_id in callback))
|
||||
|
||||
callbacks = {}
|
||||
for var_id in var_ids:
|
||||
callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id(callback)]
|
||||
callbacks[var_id] = [
|
||||
callback for callback in callbacks_all if uses_var_id(callback)]
|
||||
|
||||
return callbacks
|
||||
|
||||
@ -253,14 +265,17 @@ class TopBlockGenerator(object):
|
||||
|
||||
# Get the virtual blocks and resolve their connections
|
||||
connection_factory = fg.parent_platform.Connection
|
||||
virtual_source_connections = [c for c in connections if isinstance(c.source_block, blocks.VirtualSource)]
|
||||
virtual_source_connections = [c for c in connections if isinstance(
|
||||
c.source_block, blocks.VirtualSource)]
|
||||
for connection in virtual_source_connections:
|
||||
sink = connection.sink_port
|
||||
for source in connection.source_port.resolve_virtual_source():
|
||||
resolved = connection_factory(fg.orignal_flowgraph, source, sink)
|
||||
resolved = connection_factory(
|
||||
fg.orignal_flowgraph, source, sink)
|
||||
connections.append(resolved)
|
||||
|
||||
virtual_connections = [c for c in connections if (isinstance(c.source_block, blocks.VirtualSource) or isinstance(c.sink_block, blocks.VirtualSink))]
|
||||
virtual_connections = [c for c in connections if (isinstance(
|
||||
c.source_block, blocks.VirtualSource) or isinstance(c.sink_block, blocks.VirtualSink))]
|
||||
for connection in virtual_connections:
|
||||
# Remove the virtual connection
|
||||
connections.remove(connection)
|
||||
@ -274,7 +289,8 @@ class TopBlockGenerator(object):
|
||||
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]]
|
||||
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)
|
||||
|
||||
@ -286,7 +302,8 @@ class TopBlockGenerator(object):
|
||||
if not sink.enabled:
|
||||
# Ignore disabled connections
|
||||
continue
|
||||
connection = connection_factory(fg.orignal_flowgraph, source_port, sink.sink_port)
|
||||
connection = connection_factory(
|
||||
fg.orignal_flowgraph, source_port, sink.sink_port)
|
||||
connections.append(connection)
|
||||
# Remove this sink connection
|
||||
connections.remove(sink)
|
||||
@ -301,7 +318,8 @@ class TopBlockGenerator(object):
|
||||
for con in sorted(connections, key=by_domain_and_blocks):
|
||||
template = templates[con.type]
|
||||
if con.source_port.dtype != 'bus':
|
||||
code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port)
|
||||
code = template.render(
|
||||
make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port)
|
||||
rendered.append(code)
|
||||
else:
|
||||
# Bus ports need to iterate over the underlying connections and then render
|
||||
@ -318,10 +336,8 @@ class TopBlockGenerator(object):
|
||||
hidden_portb = portb.parent.sinks[port_num_b]
|
||||
connection = fg.parent_platform.Connection(
|
||||
parent=self, source=hidden_porta, sink=hidden_portb)
|
||||
code = template.render(make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb)
|
||||
code = template.render(
|
||||
make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb)
|
||||
rendered.append(code)
|
||||
|
||||
|
||||
|
||||
|
||||
return rendered
|
||||
|
||||
@ -11,6 +11,7 @@ import yaml
|
||||
|
||||
from ..params.param import attributed_str
|
||||
|
||||
|
||||
class GRCDumper(yaml.SafeDumper):
|
||||
@classmethod
|
||||
def add(cls, data_type):
|
||||
@ -21,7 +22,8 @@ class GRCDumper(yaml.SafeDumper):
|
||||
|
||||
def represent_ordered_mapping(self, data):
|
||||
value = []
|
||||
node = yaml.MappingNode(u'tag:yaml.org,2002:map', value, flow_style=False)
|
||||
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
|
||||
@ -62,7 +64,8 @@ class MultiLineString(str):
|
||||
|
||||
|
||||
GRCDumper.add_representer(OrderedDict, GRCDumper.represent_ordered_mapping)
|
||||
GRCDumper.add_representer(OrderedDictFlowing, GRCDumper.represent_ordered_mapping_flowing)
|
||||
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)
|
||||
@ -71,7 +74,8 @@ GRCDumper.add_representer(attributed_str, GRCDumper.represent_str)
|
||||
|
||||
|
||||
def dump(data, stream=None, **kwargs):
|
||||
config = dict(stream=stream, default_flow_style=False, indent=4, Dumper=GRCDumper)
|
||||
config = dict(stream=stream, default_flow_style=False,
|
||||
indent=4, Dumper=GRCDumper)
|
||||
config.update(kwargs)
|
||||
return yaml.dump_all([data], **config)
|
||||
|
||||
|
||||
@ -16,7 +16,8 @@ from .. import Constants
|
||||
ID_BLACKLIST = ['self', 'default'] + dir(builtins)
|
||||
try:
|
||||
from gnuradio import gr
|
||||
ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
|
||||
ID_BLACKLIST.extend(attr for attr in dir(
|
||||
gr.top_block()) if not attr.startswith('_'))
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
@ -32,12 +33,13 @@ def validates(*dtypes):
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
||||
class ValidateError(Exception):
|
||||
"""Raised by validate functions"""
|
||||
|
||||
|
||||
@validates('id')
|
||||
def validate_block_id(param,black_listed_ids):
|
||||
def validate_block_id(param, black_listed_ids):
|
||||
value = param.value
|
||||
# Can python use this as a variable?
|
||||
|
||||
@ -45,10 +47,11 @@ def validate_block_id(param,black_listed_ids):
|
||||
raise ValidateError('ID "{}" must begin with a letter and may contain letters, numbers, '
|
||||
'and underscores.'.format(value))
|
||||
if value in (black_listed_ids + ID_BLACKLIST) and \
|
||||
not getattr(param.parent_block, 'exempt_from_id_validation', False):
|
||||
not getattr(param.parent_block, 'exempt_from_id_validation', False):
|
||||
# Grant blacklist exemption to epy blocks and modules
|
||||
raise ValidateError('ID "{}" is blacklisted.'.format(value))
|
||||
block_names = [block.name for block in param.parent_flowgraph.iter_enabled_blocks()]
|
||||
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))
|
||||
@ -58,7 +61,7 @@ def validate_block_id(param,black_listed_ids):
|
||||
|
||||
|
||||
@validates('name')
|
||||
def validate_name(param,black_listed_ids):
|
||||
def validate_name(param, black_listed_ids):
|
||||
# Name of a function or other block that will be generated literally not as a string
|
||||
value = param.value
|
||||
|
||||
@ -71,7 +74,7 @@ def validate_name(param,black_listed_ids):
|
||||
|
||||
|
||||
@validates('stream_id')
|
||||
def validate_stream_id(param,black_listed_ids):
|
||||
def validate_stream_id(param, black_listed_ids):
|
||||
value = param.value
|
||||
stream_ids = [
|
||||
block.params['stream_id'].value
|
||||
@ -88,7 +91,7 @@ def validate_stream_id(param,black_listed_ids):
|
||||
|
||||
|
||||
@validates('complex', 'real', 'float', 'int')
|
||||
def validate_scalar(param,black_listed_ids):
|
||||
def validate_scalar(param, black_listed_ids):
|
||||
valid_types = Constants.PARAM_TYPE_MAP[param.dtype]
|
||||
if not isinstance(param.get_evaluated(), valid_types):
|
||||
raise ValidateError('Expression {!r} is invalid for type {!r}.'.format(
|
||||
@ -96,19 +99,21 @@ def validate_scalar(param,black_listed_ids):
|
||||
|
||||
|
||||
@validates('complex_vector', 'real_vector', 'float_vector', 'int_vector')
|
||||
def validate_vector(param,black_listed_ids):
|
||||
def validate_vector(param, black_listed_ids):
|
||||
# todo: check vector types
|
||||
|
||||
if param.get_evaluated() is None:
|
||||
raise ValidateError('Expression {!r} is invalid for type{!r}.'.format(param.get_evaluated(), param.dtype))
|
||||
raise ValidateError('Expression {!r} is invalid for type{!r}.'.format(
|
||||
param.get_evaluated(), param.dtype))
|
||||
|
||||
valid_types = Constants.PARAM_TYPE_MAP[param.dtype.split('_', 1)[0]]
|
||||
if not all(isinstance(item, valid_types) for item in param.get_evaluated()):
|
||||
raise ValidateError('Expression {!r} is invalid for type {!r}.'.format(
|
||||
param.get_evaluated(), param.dtype))
|
||||
|
||||
|
||||
@validates('gui_hint')
|
||||
def validate_gui_hint(param,black_listed_ids):
|
||||
def validate_gui_hint(param, black_listed_ids):
|
||||
try:
|
||||
param.parse_gui_hint(param.value)
|
||||
except Exception as e:
|
||||
|
||||
@ -18,6 +18,7 @@ from .template_arg import TemplateArg
|
||||
|
||||
attributed_str = type('attributed_str', (str,), {})
|
||||
|
||||
|
||||
@setup_names
|
||||
class Param(Element):
|
||||
|
||||
@ -73,12 +74,14 @@ class Param(Element):
|
||||
options.attributes = collections.defaultdict(dict)
|
||||
|
||||
padding = [''] * max(len(values), len(labels))
|
||||
attributes = {key: value + padding for key, value in attributes.items()}
|
||||
attributes = {key: value + padding for key,
|
||||
value in attributes.items()}
|
||||
|
||||
for i, option in enumerate(values):
|
||||
# Test against repeated keys
|
||||
if option in options:
|
||||
raise KeyError('Value "{}" already exists in options'.format(option))
|
||||
raise KeyError(
|
||||
'Value "{}" already exists in options'.format(option))
|
||||
# get label
|
||||
try:
|
||||
label = str(labels[i])
|
||||
@ -86,7 +89,8 @@ class Param(Element):
|
||||
label = str(option)
|
||||
# Store the option
|
||||
options[option] = label
|
||||
options.attributes[option] = {attrib: values[i] for attrib, values in attributes.items()}
|
||||
options.attributes[option] = {attrib: values[i]
|
||||
for attrib, values in attributes.items()}
|
||||
|
||||
default = next(iter(options)) if options else ''
|
||||
if not self.value:
|
||||
@ -151,12 +155,13 @@ class Param(Element):
|
||||
"""
|
||||
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))
|
||||
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,self.parent_flowgraph.get_imported_names())
|
||||
validator(self, self.parent_flowgraph.get_imported_names())
|
||||
except dtypes.ValidateError as e:
|
||||
self.add_error_message(str(e))
|
||||
|
||||
@ -207,12 +212,14 @@ class Param(Element):
|
||||
if expr:
|
||||
try:
|
||||
if isinstance(expr, str) and self.is_float(expr[:-1]):
|
||||
scale_factor = expr[-1:]
|
||||
if scale_factor in self.scale:
|
||||
expr = str(float(expr[:-1])*self.scale[scale_factor])
|
||||
scale_factor = expr[-1:]
|
||||
if scale_factor in self.scale:
|
||||
expr = str(float(expr[:-1]) *
|
||||
self.scale[scale_factor])
|
||||
value = self.parent_flowgraph.evaluate(expr)
|
||||
except Exception as e:
|
||||
raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, e))
|
||||
raise Exception(
|
||||
'Value "{}" cannot be evaluated:\n{}'.format(expr, e))
|
||||
else:
|
||||
value = None # No parameter value provided
|
||||
if dtype == 'hex':
|
||||
@ -230,7 +237,8 @@ class Param(Element):
|
||||
try:
|
||||
value = self.parent.parent.evaluate(expr)
|
||||
except Exception as value:
|
||||
raise Exception('Value "{}" cannot be evaluated:\n{}'.format(expr, value))
|
||||
raise Exception(
|
||||
'Value "{}" cannot be evaluated:\n{}'.format(expr, value))
|
||||
if not isinstance(value, Constants.VECTOR_TYPES):
|
||||
self._lisitify_flag = True
|
||||
value = [value]
|
||||
@ -339,7 +347,8 @@ class Param(Element):
|
||||
e = self.parent_flowgraph.evaluate(pos)
|
||||
|
||||
if not isinstance(e, (list, tuple)) or len(e) not in (2, 4) or not all(isinstance(ei, int) for ei in e):
|
||||
raise Exception('Invalid GUI Hint entered: {e!r} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
|
||||
raise Exception(
|
||||
'Invalid GUI Hint entered: {e!r} (Must be a list of {{2,4}} non-negative integers).'.format(e=e))
|
||||
|
||||
if len(e) == 2:
|
||||
row, col = e
|
||||
@ -348,10 +357,12 @@ class Param(Element):
|
||||
row, col, row_span, col_span = e
|
||||
|
||||
if (row < 0) or (col < 0):
|
||||
raise Exception('Invalid GUI Hint entered: {e!r} (non-negative integers only).'.format(e=e))
|
||||
raise Exception(
|
||||
'Invalid GUI Hint entered: {e!r} (non-negative integers only).'.format(e=e))
|
||||
|
||||
if (row_span < 1) or (col_span < 1):
|
||||
raise Exception('Invalid GUI Hint entered: {e!r} (positive row/column span required).'.format(e=e))
|
||||
raise Exception(
|
||||
'Invalid GUI Hint entered: {e!r} (positive row/column span required).'.format(e=e))
|
||||
|
||||
return row, col, row_span, col_span
|
||||
|
||||
@ -360,7 +371,8 @@ class Param(Element):
|
||||
if block.key == 'qtgui_tab_widget' and block.name == tab)
|
||||
tab_block = next(iter(tabs), None)
|
||||
if not tab_block:
|
||||
raise Exception('Invalid tab name entered: {tab} (Tab name not found).'.format(tab=tab))
|
||||
raise Exception(
|
||||
'Invalid tab name entered: {tab} (Tab name not found).'.format(tab=tab))
|
||||
|
||||
tab_index_size = int(tab_block.params['num_tabs'].value)
|
||||
if index >= tab_index_size:
|
||||
@ -369,7 +381,8 @@ class Param(Element):
|
||||
|
||||
# Collision Detection
|
||||
def collision_detection(row, col, row_span, col_span):
|
||||
my_parent = '{tab}@{index}'.format(tab=tab, index=index) if tab else 'main'
|
||||
my_parent = '{tab}@{index}'.format(tab=tab,
|
||||
index=index) if tab else 'main'
|
||||
# Calculate hostage cells
|
||||
for r in range(row, row + row_span):
|
||||
for c in range(col, col + col_span):
|
||||
@ -378,7 +391,8 @@ class Param(Element):
|
||||
for other in self.get_all_params('gui_hint'):
|
||||
if other is self:
|
||||
continue
|
||||
collision = next(iter(self.hostage_cells & other.hostage_cells), None)
|
||||
collision = next(
|
||||
iter(self.hostage_cells & other.hostage_cells), None)
|
||||
if collision:
|
||||
raise Exception('Block {block!r} is also using parent {parent!r}, cell {cell!r}.'.format(
|
||||
block=other.parent_block.name, parent=collision[0], cell=collision[1]
|
||||
@ -390,7 +404,8 @@ class Param(Element):
|
||||
if not pos:
|
||||
layout = '{tab}_layout_{index}'.format(tab=tab, index=index)
|
||||
else:
|
||||
layout = '{tab}_grid_layout_{index}'.format(tab=tab, index=index)
|
||||
layout = '{tab}_grid_layout_{index}'.format(
|
||||
tab=tab, index=index)
|
||||
else:
|
||||
if not pos:
|
||||
layout = 'top_layout'
|
||||
@ -412,8 +427,8 @@ class Param(Element):
|
||||
self.{layout}.setColumnStretch(c, 1)
|
||||
""".strip('\n')).format(
|
||||
layout=layout, widget=widget,
|
||||
row=row, row_span=row_span, row_end=row+row_span,
|
||||
col=col, col_span=col_span, col_end=col+col_span,
|
||||
row=row, row_span=row_span, row_end=row + row_span,
|
||||
col=col, col_span=col_span, col_end=col + col_span,
|
||||
)
|
||||
elif self.parent_flowgraph.get_option('output_language') == 'cpp':
|
||||
widget_str = textwrap.dedent("""
|
||||
@ -424,17 +439,19 @@ class Param(Element):
|
||||
{layout}->setColumnStretch(c, 1);
|
||||
""".strip('\n')).format(
|
||||
layout=layout, widget=widget,
|
||||
row=row, row_span=row_span, row_end=row+row_span,
|
||||
col=col, col_span=col_span, col_end=col+col_span,
|
||||
row=row, row_span=row_span, row_end=row + row_span,
|
||||
col=col, col_span=col_span, col_end=col + col_span,
|
||||
)
|
||||
else:
|
||||
widget_str = ''
|
||||
|
||||
else:
|
||||
if self.parent_flowgraph.get_option('output_language') == 'python':
|
||||
widget_str = 'self.{layout}.addWidget({widget})'.format(layout=layout, widget=widget)
|
||||
widget_str = 'self.{layout}.addWidget({widget})'.format(
|
||||
layout=layout, widget=widget)
|
||||
elif self.parent_flowgraph.get_option('output_language') == 'cpp':
|
||||
widget_str = '{layout}->addWidget({widget});'.format(layout=layout, widget=widget)
|
||||
widget_str = '{layout}->addWidget({widget});'.format(
|
||||
layout=layout, widget=widget)
|
||||
else:
|
||||
widget_str = ''
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#
|
||||
|
||||
|
||||
|
||||
class TemplateArg(str):
|
||||
"""
|
||||
A cheetah template argument created from a param.
|
||||
|
||||
@ -36,7 +36,8 @@ class Platform(Element):
|
||||
|
||||
self.config = self.Config(*args, **kwargs)
|
||||
self.block_docstrings = {}
|
||||
self.block_docstrings_loaded_callback = lambda: None # dummy to be replaced by BlockTreeWindow
|
||||
# dummy to be replaced by BlockTreeWindow
|
||||
self.block_docstrings_loaded_callback = lambda: None
|
||||
|
||||
self._docstring_extractor = utils.extract_docs.SubprocessLoader(
|
||||
callback_query_result=self._save_docstring_extraction_result,
|
||||
@ -108,7 +109,8 @@ class Platform(Element):
|
||||
Messages.send('>>> Generating: {}\n'.format(generator.file_path))
|
||||
generator.write()
|
||||
except Exception as e:
|
||||
Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e)))
|
||||
Messages.send(
|
||||
'>>> Generate Error: {}: {}\n'.format(file_path, str(e)))
|
||||
return None, None
|
||||
|
||||
return flow_graph, generator.file_path
|
||||
@ -126,8 +128,8 @@ class Platform(Element):
|
||||
self.connection_templates.clear()
|
||||
self.cpp_connection_templates.clear()
|
||||
self._block_categories.clear()
|
||||
|
||||
with Cache(Constants.CACHE_FILE, version = self.config.version) as cache:
|
||||
|
||||
with Cache(Constants.CACHE_FILE, version=self.config.version) as cache:
|
||||
for file_path in self._iter_files_in_block_path(path):
|
||||
|
||||
if file_path.endswith('.block.yml'):
|
||||
@ -147,9 +149,11 @@ class Platform(Element):
|
||||
data = cache.get_or_load(file_path)
|
||||
passed = checker.run(data)
|
||||
for msg in checker.messages:
|
||||
logger.warning('{:<40s} {}'.format(os.path.basename(file_path), msg))
|
||||
logger.warning('{:<40s} {}'.format(
|
||||
os.path.basename(file_path), msg))
|
||||
if not passed:
|
||||
logger.info('YAML schema check failed for: ' + file_path)
|
||||
logger.info(
|
||||
'YAML schema check failed for: ' + file_path)
|
||||
|
||||
loader(data, file_path)
|
||||
except Exception as error:
|
||||
@ -184,8 +188,8 @@ class Platform(Element):
|
||||
raise RuntimeError(errstr)
|
||||
else:
|
||||
# might have some cleanup to do on the options block in particular
|
||||
utils.hide_bokeh_gui_options_if_not_installed(self.blocks['options'])
|
||||
|
||||
utils.hide_bokeh_gui_options_if_not_installed(
|
||||
self.blocks['options'])
|
||||
|
||||
def _iter_files_in_block_path(self, path=None, ext='yml'):
|
||||
"""Iterator for block descriptions and category trees"""
|
||||
@ -220,13 +224,15 @@ class Platform(Element):
|
||||
# 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)
|
||||
log.error('Unknown format version %d in %s',
|
||||
file_format, file_path)
|
||||
return
|
||||
|
||||
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)
|
||||
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" loaded from\n %s\noverwritten by\n %s',
|
||||
@ -256,7 +262,8 @@ class Platform(Element):
|
||||
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)
|
||||
log.warning('Cannot parse color code "%s" in %s',
|
||||
color, file_path)
|
||||
return
|
||||
|
||||
self.domains[domain_id] = self.Domain(
|
||||
@ -272,8 +279,10 @@ class Platform(Element):
|
||||
log.warn('Invalid connection template.')
|
||||
continue
|
||||
connection_id = str(source_id), str(sink_id)
|
||||
self.connection_templates[connection_id] = connection.get('connect', '')
|
||||
self.cpp_connection_templates[connection_id] = connection.get('cpp_connect', '')
|
||||
self.connection_templates[connection_id] = connection.get(
|
||||
'connect', '')
|
||||
self.cpp_connection_templates[connection_id] = connection.get(
|
||||
'cpp_connect', '')
|
||||
|
||||
def load_category_tree_description(self, data, file_path):
|
||||
"""Parse category tree file and add it to list"""
|
||||
@ -326,7 +335,8 @@ class Platform(Element):
|
||||
# todo: try
|
||||
if not is_xml:
|
||||
data = yaml.safe_load(fp)
|
||||
validator = schema_checker.Validator(schema_checker.FLOW_GRAPH_SCHEME)
|
||||
validator = schema_checker.Validator(
|
||||
schema_checker.FLOW_GRAPH_SCHEME)
|
||||
validator.run(data)
|
||||
|
||||
if is_xml:
|
||||
@ -340,13 +350,15 @@ class Platform(Element):
|
||||
data = flow_graph.export_data()
|
||||
|
||||
try:
|
||||
data['connections'] = [yaml.ListFlowing(i) for i in data['connections']]
|
||||
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'])
|
||||
d['states']['coordinate'] = yaml.ListFlowing(
|
||||
d['states']['coordinate'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@ -393,7 +405,8 @@ class Platform(Element):
|
||||
Connection = Connection
|
||||
|
||||
block_classes_build_in = blocks.build_ins
|
||||
block_classes = utils.backports.ChainMap({}, block_classes_build_in) # separates build-in from loaded blocks)
|
||||
# separates build-in from loaded blocks)
|
||||
block_classes = utils.backports.ChainMap({}, block_classes_build_in)
|
||||
|
||||
port_classes = {
|
||||
None: ports.Port, # default
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
from itertools import chain
|
||||
@ -28,10 +28,12 @@ def _sources_from_virtual_sink_port(sink_port, _traversed=None):
|
||||
"""
|
||||
source_ports_per_virtual_connection = (
|
||||
# there can be multiple ports per virtual connection
|
||||
_sources_from_virtual_source_port(c.source_port, _traversed) # type: list
|
||||
_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
|
||||
# concatenate generated lists of ports
|
||||
return list(chain(*source_ports_per_virtual_connection))
|
||||
|
||||
|
||||
def _sources_from_virtual_source_port(source_port, _traversed=None):
|
||||
@ -62,7 +64,8 @@ def _sources_from_virtual_source_port(source_port, _traversed=None):
|
||||
_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
|
||||
# concatenate generated lists of ports
|
||||
return list(chain(*source_ports_per_virtual_connection))
|
||||
|
||||
|
||||
def downstream_ports(port):
|
||||
@ -82,7 +85,8 @@ def _sinks_from_virtual_source_port(source_port, _traversed=None):
|
||||
_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
|
||||
# concatenate generated lists of ports
|
||||
return list(chain(*sink_ports_per_virtual_connection))
|
||||
|
||||
|
||||
def _sinks_from_virtual_sink_port(sink_port, _traversed=None):
|
||||
@ -111,4 +115,5 @@ def _sinks_from_virtual_sink_port(sink_port, _traversed=None):
|
||||
_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
|
||||
# concatenate generated lists of ports
|
||||
return list(chain(*sink_ports_per_virtual_connection))
|
||||
|
||||
@ -34,17 +34,21 @@ class Port(Element):
|
||||
self._dir = direction
|
||||
self.key = id
|
||||
if not label:
|
||||
label = id if not id.isdigit() else {'sink': 'in', 'source': 'out'}[direction]
|
||||
label = id if not id.isdigit() else {'sink': 'in', 'source': 'out'}[
|
||||
direction]
|
||||
if dtype == 'bus':
|
||||
# Look for existing busses to give proper index
|
||||
busses = [p for p in self.parent.ports() if p._dir == self._dir and p.dtype == 'bus']
|
||||
busses = [p for p in self.parent.ports() if p._dir ==
|
||||
self._dir and p.dtype == 'bus']
|
||||
bus_structure = self.parent.current_bus_structure[self._dir]
|
||||
bus_index = len(busses)
|
||||
if len(bus_structure) > bus_index:
|
||||
number = str(len(busses)) + '#' + str(len(bus_structure[bus_index]))
|
||||
number = str(len(busses)) + '#' + \
|
||||
str(len(bus_structure[bus_index]))
|
||||
label = dtype + number
|
||||
else:
|
||||
raise ValueError('Could not initialize bus port due to incompatible bus structure')
|
||||
raise ValueError(
|
||||
'Could not initialize bus port due to incompatible bus structure')
|
||||
|
||||
self.name = self._base_name = label
|
||||
|
||||
@ -102,7 +106,8 @@ class Port(Element):
|
||||
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))
|
||||
self.add_error_message(
|
||||
'Type "{}" is not a possible type.'.format(self.dtype))
|
||||
|
||||
try:
|
||||
domain = platform.domains[self.domain]
|
||||
@ -113,7 +118,8 @@ class Port(Element):
|
||||
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))
|
||||
self.add_error_message(
|
||||
'Domain key "{}" is not registered.'.format(self.domain))
|
||||
|
||||
def rewrite(self):
|
||||
del self.vlen
|
||||
@ -150,9 +156,11 @@ class Port(Element):
|
||||
|
||||
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
|
||||
find_port(_virtual_connections.downstream_ports)
|
||||
# we don't want to override the template
|
||||
self.set_evaluated('dtype', port.dtype)
|
||||
# we don't want to override the template
|
||||
self.set_evaluated('vlen', port.vlen)
|
||||
self.domain = port.domain
|
||||
except AttributeError:
|
||||
self.domain = Constants.DEFAULT_DOMAIN
|
||||
@ -206,7 +214,7 @@ class Port(Element):
|
||||
enabled: None for all, True for enabled only, False for disabled only
|
||||
"""
|
||||
for con in self.parent_flowgraph.connections:
|
||||
#TODO clean this up - but how to get past this validation
|
||||
# TODO clean this up - but how to get past this validation
|
||||
# things don't compare simply with an x in y because
|
||||
# bus ports are created differently.
|
||||
port_in_con = False
|
||||
@ -242,5 +250,6 @@ class Port(Element):
|
||||
if bus_structure:
|
||||
busses = [i for i in get_ports if i.dtype == 'bus']
|
||||
bus_index = busses.index(self)
|
||||
ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports)
|
||||
ports = filter(lambda a: ports.index(
|
||||
a) in bus_structure[bus_index], ports)
|
||||
return ports
|
||||
|
||||
@ -12,7 +12,8 @@ PARAM_SCHEME = expand(
|
||||
|
||||
options=list,
|
||||
option_labels=list,
|
||||
option_attributes=Spec(types=dict, required=False, item_scheme=(str, list)),
|
||||
option_attributes=Spec(types=dict, required=False,
|
||||
item_scheme=(str, list)),
|
||||
|
||||
hide=str,
|
||||
)
|
||||
@ -59,7 +60,8 @@ BLOCK_SCHEME = expand(
|
||||
value=str,
|
||||
|
||||
templates=Spec(types=dict, required=False, item_scheme=TEMPLATES_SCHEME),
|
||||
cpp_templates=Spec(types=dict, required=False, item_scheme=CPP_TEMPLATES_SCHEME),
|
||||
cpp_templates=Spec(types=dict, required=False,
|
||||
item_scheme=CPP_TEMPLATES_SCHEME),
|
||||
|
||||
documentation=str,
|
||||
grc_source=str,
|
||||
|
||||
@ -30,16 +30,19 @@ class ChainMap(MutableMapping):
|
||||
def __getitem__(self, key):
|
||||
for mapping in self.maps:
|
||||
try:
|
||||
return mapping[key] # can't use 'key in mapping' with defaultdict
|
||||
# can't use 'key in mapping' with defaultdict
|
||||
return mapping[key]
|
||||
except KeyError:
|
||||
pass
|
||||
return self.__missing__(key) # support subclasses that define __missing__
|
||||
# support subclasses that define __missing__
|
||||
return self.__missing__(key)
|
||||
|
||||
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
|
||||
# reuses stored hash values if possible
|
||||
return len(set().union(*self.maps))
|
||||
|
||||
def __iter__(self):
|
||||
return iter(set().union(*self.maps))
|
||||
@ -85,7 +88,8 @@ class ChainMap(MutableMapping):
|
||||
try:
|
||||
del self.maps[0][key]
|
||||
except KeyError:
|
||||
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
|
||||
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."""
|
||||
@ -99,7 +103,8 @@ class ChainMap(MutableMapping):
|
||||
try:
|
||||
return self.maps[0].pop(key, *args)
|
||||
except KeyError:
|
||||
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
|
||||
raise KeyError(
|
||||
'Key not found in the first mapping: {!r}'.format(key))
|
||||
|
||||
def clear(self):
|
||||
"""Clear maps[0], leaving maps[1:] intact."""
|
||||
|
||||
@ -23,6 +23,7 @@ class lazy_property(object):
|
||||
|
||||
def nop_write(prop):
|
||||
"""Make this a property with a nop setter"""
|
||||
|
||||
def nop(self, value):
|
||||
pass
|
||||
return prop.setter(nop)
|
||||
|
||||
@ -23,7 +23,8 @@ class Evaluated(object):
|
||||
value = instance.parent_block.evaluate(raw)
|
||||
except Exception as error:
|
||||
if raw:
|
||||
instance.add_error_message("Failed to eval '{}': {}".format(raw, error))
|
||||
instance.add_error_message(
|
||||
"Failed to eval '{}': {}".format(raw, error))
|
||||
return self.default
|
||||
|
||||
if not isinstance(value, self.expected_type):
|
||||
@ -68,13 +69,15 @@ class EvaluatedEnum(Evaluated):
|
||||
if isinstance(allowed_values, str):
|
||||
allowed_values = set(allowed_values.split())
|
||||
self.allowed_values = allowed_values
|
||||
default = default if default is not None else next(iter(self.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):
|
||||
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))
|
||||
instance.add_error_message(
|
||||
"Value '{}' not in allowed values".format(value))
|
||||
return self.default
|
||||
return value
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@ TYPE_MAP = {
|
||||
'int8': 'byte', 'uint8': 'byte',
|
||||
}
|
||||
|
||||
BlockIO = collections.namedtuple('BlockIO', 'name cls params sinks sources doc callbacks')
|
||||
BlockIO = collections.namedtuple(
|
||||
'BlockIO', 'name cls params sinks sources doc callbacks')
|
||||
|
||||
|
||||
def _ports(sigs, msgs):
|
||||
@ -72,11 +73,13 @@ def extract(cls):
|
||||
|
||||
def settable(attr):
|
||||
try:
|
||||
return callable(getattr(cls, attr).fset) # check for a property with setter
|
||||
# check for a property with setter
|
||||
return callable(getattr(cls, attr).fset)
|
||||
except AttributeError:
|
||||
return attr in instance.__dict__ # not dir() - only the instance attribs
|
||||
|
||||
callbacks = [attr for attr in dir(instance) if attr in init_args and settable(attr)]
|
||||
callbacks = [attr for attr in dir(
|
||||
instance) if attr in init_args and settable(attr)]
|
||||
|
||||
sinks = _ports(instance.in_sig(),
|
||||
pmt.to_python(instance.message_ports_in()))
|
||||
|
||||
@ -7,6 +7,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
"""
|
||||
|
||||
|
||||
import ast
|
||||
import string
|
||||
|
||||
|
||||
@ -64,12 +65,10 @@ def sort_objects(objects, get_id, get_expr):
|
||||
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)])
|
||||
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
|
||||
|
||||
|
||||
@ -93,8 +92,6 @@ def sort_objects2(objects, id_getter, expr_getter, check_circular=True):
|
||||
return objects
|
||||
|
||||
|
||||
|
||||
|
||||
VAR_CHARS = string.ascii_letters + string.digits + '_'
|
||||
|
||||
|
||||
@ -205,7 +202,8 @@ def _sort_variables(exprs):
|
||||
# Determine dependency order
|
||||
while var_graph.get_nodes():
|
||||
# Get a list of nodes with no edges
|
||||
indep_vars = [var for var in var_graph.get_nodes() if not var_graph.get_edges(var)]
|
||||
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
|
||||
|
||||
@ -55,7 +55,8 @@ def docstring_guess_from_key(key):
|
||||
else:
|
||||
return doc_strings
|
||||
|
||||
pattern = re.compile('^' + init_name.replace('_', '_*').replace('x', r'\w') + r'\w*$')
|
||||
pattern = re.compile(
|
||||
'^' + init_name.replace('_', '_*').replace('x', r'\w') + r'\w*$')
|
||||
for match in filter(pattern.match, dir(module)):
|
||||
try:
|
||||
doc_strings[match] = getattr(module, match).__doc__
|
||||
@ -135,7 +136,8 @@ class SubprocessLoader(object):
|
||||
break
|
||||
try:
|
||||
self._worker = subprocess.Popen(
|
||||
args=(sys.executable, '-uc', self.BOOTSTRAP.format(__file__)),
|
||||
args=(sys.executable, '-uc',
|
||||
self.BOOTSTRAP.format(__file__)),
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
@ -286,7 +288,8 @@ elif __name__ == '__main__':
|
||||
# r.query('uhd_source')
|
||||
r.query('expr_utils_graph')
|
||||
r.query('blocks_add_cc')
|
||||
r.query('blocks_add_cc', ['import gnuradio.blocks'], 'gnuradio.blocks.add_cc(')
|
||||
r.query('blocks_add_cc', ['import gnuradio.blocks'],
|
||||
'gnuradio.blocks.add_cc(')
|
||||
# r.query('analog_feedforward_agc_cc')
|
||||
# r.query('uhd_source')
|
||||
# r.query('uhd_source')
|
||||
|
||||
@ -37,17 +37,20 @@ def calculate(flowgraph):
|
||||
|
||||
# Disabled multiplier
|
||||
if enabled > 0:
|
||||
disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05))
|
||||
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))
|
||||
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)
|
||||
final = round(max((dbal - 1) * disabled_multi *
|
||||
conn_multi * connections, 0.0) / 1000000, 6)
|
||||
return final
|
||||
|
||||
except Exception:
|
||||
|
||||
@ -85,10 +85,10 @@ class Namespace(object):
|
||||
key = "{}.{}".format(prefix, name)
|
||||
if prefix == "app":
|
||||
pass
|
||||
#self.app.add_action(action)
|
||||
# self.app.add_action(action)
|
||||
elif prefix == "win":
|
||||
pass
|
||||
#self.win.add_action(action)
|
||||
# self.win.add_action(action)
|
||||
|
||||
#log.debug("Registering action as '{}'".format(key))
|
||||
self._actions[key] = action
|
||||
@ -132,7 +132,7 @@ class Namespace(object):
|
||||
class Action(Gio.SimpleAction):
|
||||
|
||||
# Change these to normal python properties.
|
||||
#prefs_name
|
||||
# prefs_name
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
@ -295,7 +295,7 @@ FLOW_GRAPH_DUPLICATE = actions.register(
|
||||
"app.flowgraph.duplicate",
|
||||
label='_Duplicate',
|
||||
tooltip='Create a duplicate of current flow graph',
|
||||
#stock_id=Gtk.STOCK_COPY,
|
||||
# stock_id=Gtk.STOCK_COPY,
|
||||
keypresses=["<Ctrl><Shift>d"],
|
||||
)
|
||||
FLOW_GRAPH_CLOSE = actions.register(
|
||||
@ -433,26 +433,27 @@ BLOCK_BYPASS = actions.register(
|
||||
keypresses=["b"],
|
||||
)
|
||||
ZOOM_IN = actions.register("win.zoom_in",
|
||||
label='Zoom In',
|
||||
tooltip='Increase the canvas zoom level',
|
||||
keypresses=["<Ctrl>plus","<Ctrl>equal","<Ctrl>KP_Add"],
|
||||
)
|
||||
label='Zoom In',
|
||||
tooltip='Increase the canvas zoom level',
|
||||
keypresses=["<Ctrl>plus",
|
||||
"<Ctrl>equal", "<Ctrl>KP_Add"],
|
||||
)
|
||||
ZOOM_OUT = actions.register("win.zoom_out",
|
||||
label='Zoom Out',
|
||||
tooltip='Decrease the canvas zoom level',
|
||||
keypresses=["<Ctrl>minus","<Ctrl>KP_Subtract"],
|
||||
)
|
||||
label='Zoom Out',
|
||||
tooltip='Decrease the canvas zoom level',
|
||||
keypresses=["<Ctrl>minus", "<Ctrl>KP_Subtract"],
|
||||
)
|
||||
ZOOM_RESET = actions.register("win.zoom_reset",
|
||||
label='Reset Zoom',
|
||||
tooltip='Reset the canvas zoom level',
|
||||
keypresses=["<Ctrl>0","<Ctrl>KP_0"],
|
||||
)
|
||||
label='Reset Zoom',
|
||||
tooltip='Reset the canvas zoom level',
|
||||
keypresses=["<Ctrl>0", "<Ctrl>KP_0"],
|
||||
)
|
||||
TOGGLE_SNAP_TO_GRID = actions.register("win.snap_to_grid",
|
||||
label='_Snap to grid',
|
||||
tooltip='Snap blocks to a grid for an easier connection alignment',
|
||||
preference_name='snap_to_grid',
|
||||
default=True,
|
||||
)
|
||||
label='_Snap to grid',
|
||||
tooltip='Snap blocks to a grid for an easier connection alignment',
|
||||
preference_name='snap_to_grid',
|
||||
default=True,
|
||||
)
|
||||
TOGGLE_HIDE_DISABLED_BLOCKS = actions.register(
|
||||
"win.hide_disabled",
|
||||
label='Hide _Disabled Blocks',
|
||||
@ -493,8 +494,7 @@ TOGGLE_SHOW_BLOCK_IDS = actions.register(
|
||||
TOGGLE_FLOW_GRAPH_VAR_EDITOR = actions.register(
|
||||
"win.toggle_variable_editor",
|
||||
label='Show _Variable Editor',
|
||||
tooltip=
|
||||
'Show the variable editor. Modify variables and imports in this flow graph',
|
||||
tooltip='Show the variable editor. Modify variables and imports in this flow graph',
|
||||
icon_name='accessories-text-editor',
|
||||
default=True,
|
||||
keypresses=["<Ctrl>e"],
|
||||
|
||||
@ -7,7 +7,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
@ -65,7 +64,8 @@ class Application(Gtk.Application):
|
||||
self.set_accels_for_action(x, keypress)
|
||||
|
||||
# Initialize
|
||||
self.init_file_paths = [os.path.abspath(file_path) for file_path in file_paths]
|
||||
self.init_file_paths = [os.path.abspath(
|
||||
file_path) for file_path in file_paths]
|
||||
self.init = False
|
||||
|
||||
def do_startup(self):
|
||||
@ -80,7 +80,7 @@ class Application(Gtk.Application):
|
||||
self.main_window.connect('delete-event', self._quit)
|
||||
self.get_focus_flag = self.main_window.get_focus_flag
|
||||
|
||||
#setup the messages
|
||||
# setup the messages
|
||||
Messages.register_messenger(self.main_window.add_console_line)
|
||||
Messages.send_init(self.platform)
|
||||
|
||||
@ -117,7 +117,8 @@ class Application(Gtk.Application):
|
||||
file_path_to_show = self.config.file_open()
|
||||
for file_path in (self.init_file_paths or self.config.get_open_files()):
|
||||
if os.path.exists(file_path):
|
||||
main.new_page(file_path, show=file_path_to_show == file_path)
|
||||
main.new_page(
|
||||
file_path, show=file_path_to_show == file_path)
|
||||
if not main.current_page:
|
||||
main.new_page() # ensure that at least a blank page exists
|
||||
|
||||
@ -190,9 +191,12 @@ class Application(Gtk.Application):
|
||||
action.load_from_preferences()
|
||||
|
||||
# Hide the panels *IF* it's saved in preferences
|
||||
main.update_panel_visibility(main.BLOCKS, Actions.TOGGLE_BLOCKS_WINDOW.get_active())
|
||||
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())
|
||||
main.update_panel_visibility(
|
||||
main.BLOCKS, Actions.TOGGLE_BLOCKS_WINDOW.get_active())
|
||||
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())
|
||||
|
||||
# Force an update on the current page to match loaded preferences.
|
||||
# In the future, change the __init__ order to load preferences first
|
||||
@ -210,7 +214,7 @@ class Application(Gtk.Application):
|
||||
# Selections
|
||||
##################################################
|
||||
elif action == Actions.ELEMENT_SELECT:
|
||||
pass #do nothing, update routines below
|
||||
pass # do nothing, update routines below
|
||||
elif action == Actions.NOTHING_SELECT:
|
||||
flow_graph.unselect()
|
||||
elif action == Actions.SELECT_ALL:
|
||||
@ -271,7 +275,6 @@ class Application(Gtk.Application):
|
||||
x_min = min(block.coordinate[0] for block in selected_blocks)
|
||||
y_min = min(block.coordinate[1] for block in selected_blocks)
|
||||
|
||||
|
||||
for connection in flow_graph.connections:
|
||||
|
||||
# Get id of connected blocks
|
||||
@ -283,7 +286,8 @@ class Application(Gtk.Application):
|
||||
pads.append({
|
||||
'key': connection.sink_port.key,
|
||||
'coord': source.coordinate,
|
||||
'block_index': selected_blocks.index(sink) + 1, # Ignore the options block
|
||||
# Ignore the options block
|
||||
'block_index': selected_blocks.index(sink) + 1,
|
||||
'direction': 'source'
|
||||
})
|
||||
|
||||
@ -292,7 +296,8 @@ class Application(Gtk.Application):
|
||||
pads.append({
|
||||
'key': connection.source_port.key,
|
||||
'coord': sink.coordinate,
|
||||
'block_index': selected_blocks.index(source) + 1, # Ignore the options block
|
||||
# Ignore the options block
|
||||
'block_index': selected_blocks.index(source) + 1,
|
||||
'direction': 'sink'
|
||||
})
|
||||
|
||||
@ -335,7 +340,8 @@ class Application(Gtk.Application):
|
||||
pad_block = flow_graph.get_block(pad_id)
|
||||
pad_sink = pad_block.sinks[0]
|
||||
|
||||
source_block = flow_graph.get_block(flow_graph.blocks[pad['block_index']].name)
|
||||
source_block = flow_graph.get_block(
|
||||
flow_graph.blocks[pad['block_index']].name)
|
||||
source = source_block.get_source(pad['key'])
|
||||
|
||||
# ensure the port types match
|
||||
@ -350,13 +356,15 @@ class Application(Gtk.Application):
|
||||
new_connection = flow_graph.connect(source, pad_sink)
|
||||
|
||||
elif pad['direction'] == 'source':
|
||||
pad_id = flow_graph.add_new_block('pad_source', pad['coord'])
|
||||
pad_id = flow_graph.add_new_block(
|
||||
'pad_source', pad['coord'])
|
||||
|
||||
# setup the references to the sink and source
|
||||
pad_block = flow_graph.get_block(pad_id)
|
||||
pad_source = pad_block.sources[0]
|
||||
|
||||
sink_block = flow_graph.get_block(flow_graph.blocks[pad['block_index']].name)
|
||||
sink_block = flow_graph.get_block(
|
||||
flow_graph.blocks[pad['block_index']].name)
|
||||
sink = sink_block.get_sink(pad['key'])
|
||||
|
||||
# ensure the port types match
|
||||
@ -496,7 +504,8 @@ class Application(Gtk.Application):
|
||||
# to be visible.
|
||||
varedit = Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR
|
||||
if active:
|
||||
log.debug("Variables are hidden. Forcing the variable panel to be visible.")
|
||||
log.debug(
|
||||
"Variables are hidden. Forcing the variable panel to be visible.")
|
||||
varedit.disable()
|
||||
else:
|
||||
varedit.enable()
|
||||
@ -548,8 +557,9 @@ class Application(Gtk.Application):
|
||||
while response == Gtk.ResponseType.APPLY: # rerun the dialog if Apply was hit
|
||||
response = self.dialog.run()
|
||||
if response in (Gtk.ResponseType.APPLY, Gtk.ResponseType.ACCEPT):
|
||||
page.state_cache.save_new_state(flow_graph.export_data())
|
||||
### Following line forces a complete update of io ports
|
||||
page.state_cache.save_new_state(
|
||||
flow_graph.export_data())
|
||||
# Following line forces a complete update of io ports
|
||||
flow_graph_update()
|
||||
page.saved = False
|
||||
if response in (Gtk.ResponseType.REJECT, Gtk.ResponseType.ACCEPT):
|
||||
@ -600,18 +610,21 @@ class Application(Gtk.Application):
|
||||
main.new_page()
|
||||
args = (GLib.Variant('s', 'qt_gui'),)
|
||||
flow_graph = main.current_page.flow_graph
|
||||
flow_graph.options_block.params['generate_options'].set_value(str(args[0])[1:-1])
|
||||
flow_graph.options_block.params['generate_options'].set_value(str(args[0])[
|
||||
1:-1])
|
||||
flow_graph.options_block.params['author'].set_value(getuser())
|
||||
flow_graph_update(flow_graph)
|
||||
elif action == Actions.FLOW_GRAPH_NEW_TYPE:
|
||||
main.new_page()
|
||||
if args:
|
||||
flow_graph = main.current_page.flow_graph
|
||||
flow_graph.options_block.params['generate_options'].set_value(str(args[0])[1:-1])
|
||||
flow_graph.options_block.params['generate_options'].set_value(str(args[0])[
|
||||
1:-1])
|
||||
flow_graph_update(flow_graph)
|
||||
elif action == Actions.FLOW_GRAPH_OPEN:
|
||||
file_paths = args[0] if args[0] else FileDialogs.OpenFlowGraph(main, page.file_path).run()
|
||||
if file_paths: # Open a new page for each file, show only the first
|
||||
file_paths = args[0] if args[0] else FileDialogs.OpenFlowGraph(
|
||||
main, page.file_path).run()
|
||||
if file_paths: # Open a new page for each file, show only the first
|
||||
for i, file_path in enumerate(file_paths):
|
||||
main.new_page(file_path, show=(i == 0))
|
||||
self.config.add_recent_file(file_path)
|
||||
@ -631,10 +644,10 @@ class Application(Gtk.Application):
|
||||
main.tool_bar.refresh_submenus()
|
||||
main.menu.refresh_submenus()
|
||||
elif action == Actions.FLOW_GRAPH_SAVE:
|
||||
#read-only or undefined file path, do save-as
|
||||
# read-only or undefined file path, do save-as
|
||||
if page.get_read_only() or not page.file_path:
|
||||
Actions.FLOW_GRAPH_SAVE_AS()
|
||||
#otherwise try to save
|
||||
# otherwise try to save
|
||||
else:
|
||||
try:
|
||||
self.platform.save_flow_graph(page.file_path, flow_graph)
|
||||
@ -670,18 +683,25 @@ class Application(Gtk.Application):
|
||||
Actions.FLOW_GRAPH_SAVE_AS()
|
||||
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
|
||||
# Assuming .grc extension at the end of file_path
|
||||
dup_file_name = '.'.join(
|
||||
dup_file_path.split('.')[:-1]) + "_copy"
|
||||
dup_file_path_temp = dup_file_name + Constants.FILE_EXTENSION
|
||||
count = 1
|
||||
while os.path.exists(dup_file_path_temp):
|
||||
dup_file_path_temp = '{}({}){}'.format(dup_file_name, count, Constants.FILE_EXTENSION)
|
||||
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()
|
||||
dup_file_path_user = FileDialogs.SaveFlowGraph(
|
||||
main, dup_file_path_temp).run()
|
||||
if dup_file_path_user is not None:
|
||||
self.platform.save_flow_graph(dup_file_path_user, flow_graph)
|
||||
Messages.send('Saved Copy to: "' + dup_file_path_user + '"\n')
|
||||
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")
|
||||
Messages.send_fail_save(
|
||||
"Can not create a copy of the flowgraph\n")
|
||||
elif action == Actions.FLOW_GRAPH_DUPLICATE:
|
||||
previous = flow_graph
|
||||
# Create a new page
|
||||
@ -694,10 +714,12 @@ class Application(Gtk.Application):
|
||||
page.state_cache.save_new_state(new_flow_graph.export_data())
|
||||
page.saved = False
|
||||
elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
|
||||
file_path, background_transparent = FileDialogs.SaveScreenShot(main, page.file_path).run()
|
||||
file_path, background_transparent = FileDialogs.SaveScreenShot(
|
||||
main, page.file_path).run()
|
||||
if file_path is not None:
|
||||
try:
|
||||
Utils.make_screenshot(flow_graph, file_path, background_transparent)
|
||||
Utils.make_screenshot(
|
||||
flow_graph, file_path, background_transparent)
|
||||
except ValueError:
|
||||
Messages.send('Failed to generate screen shot\n')
|
||||
##################################################
|
||||
@ -717,7 +739,6 @@ class Application(Gtk.Application):
|
||||
except Exception as e:
|
||||
Messages.send_fail_gen(e)
|
||||
|
||||
|
||||
elif action == Actions.FLOW_GRAPH_EXEC:
|
||||
if not page.process:
|
||||
Actions.FLOW_GRAPH_GEN()
|
||||
@ -734,7 +755,7 @@ class Application(Gtk.Application):
|
||||
flow_graph_page=page,
|
||||
xterm_executable=xterm,
|
||||
callback=self.update_exec_stop
|
||||
)
|
||||
)
|
||||
elif action == Actions.FLOW_GRAPH_KILL:
|
||||
if page.process:
|
||||
try:
|
||||
@ -748,7 +769,7 @@ class Application(Gtk.Application):
|
||||
self.platform.build_library()
|
||||
main.btwin.repopulate()
|
||||
|
||||
#todo: implement parser error dialog for YAML
|
||||
# todo: implement parser error dialog for YAML
|
||||
|
||||
# Force a redraw of the graph, by getting the current state and re-importing it
|
||||
main.update_pages()
|
||||
@ -791,21 +812,21 @@ class Application(Gtk.Application):
|
||||
selected_blocks = list(flow_graph.selected_blocks())
|
||||
selected_block = selected_blocks[0] if selected_blocks else None
|
||||
|
||||
#update general buttons
|
||||
# update general buttons
|
||||
Actions.ERRORS_WINDOW_DISPLAY.set_enabled(not flow_graph.is_valid())
|
||||
Actions.ELEMENT_DELETE.set_enabled(bool(flow_graph.selected_elements))
|
||||
Actions.BLOCK_PARAM_MODIFY.set_enabled(bool(selected_block))
|
||||
Actions.BLOCK_ROTATE_CCW.set_enabled(bool(selected_blocks))
|
||||
Actions.BLOCK_ROTATE_CW.set_enabled(bool(selected_blocks))
|
||||
#update alignment options
|
||||
# update alignment options
|
||||
for act in Actions.BLOCK_ALIGNMENTS:
|
||||
if act:
|
||||
act.set_enabled(len(selected_blocks) > 1)
|
||||
#update cut/copy/paste
|
||||
# update cut/copy/paste
|
||||
Actions.BLOCK_CUT.set_enabled(bool(selected_blocks))
|
||||
Actions.BLOCK_COPY.set_enabled(bool(selected_blocks))
|
||||
Actions.BLOCK_PASTE.set_enabled(bool(self.clipboard))
|
||||
#update enable/disable/bypass
|
||||
# update enable/disable/bypass
|
||||
can_enable = any(block.state != 'enabled'
|
||||
for block in selected_blocks)
|
||||
can_disable = any(block.state != 'disabled'
|
||||
|
||||
42
gui/Bars.py
42
gui/Bars.py
@ -42,11 +42,14 @@ TOOLBAR_LIST = [
|
||||
(Actions.FLOW_GRAPH_OPEN, 'flow_graph_recent'),
|
||||
Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_CLOSE],
|
||||
[Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.FLOW_GRAPH_SCREEN_CAPTURE],
|
||||
[Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE],
|
||||
[Actions.BLOCK_CUT, Actions.BLOCK_COPY,
|
||||
Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE],
|
||||
[Actions.FLOW_GRAPH_UNDO, Actions.FLOW_GRAPH_REDO],
|
||||
[Actions.ERRORS_WINDOW_DISPLAY, Actions.FLOW_GRAPH_GEN, Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL],
|
||||
[Actions.ERRORS_WINDOW_DISPLAY, Actions.FLOW_GRAPH_GEN,
|
||||
Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL],
|
||||
[Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW],
|
||||
[Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS, Actions.TOGGLE_HIDE_DISABLED_BLOCKS],
|
||||
[Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE,
|
||||
Actions.BLOCK_BYPASS, Actions.TOGGLE_HIDE_DISABLED_BLOCKS],
|
||||
[Actions.FIND_BLOCKS, Actions.RELOAD_BLOCKS, Actions.OPEN_HIER]
|
||||
]
|
||||
|
||||
@ -56,23 +59,28 @@ MENU_BAR_LIST = [
|
||||
('_File', [
|
||||
[(Actions.FLOW_GRAPH_NEW, 'flow_graph_new_type'), Actions.FLOW_GRAPH_DUPLICATE,
|
||||
Actions.FLOW_GRAPH_OPEN, (Actions.FLOW_GRAPH_OPEN_RECENT, 'flow_graph_recent')],
|
||||
[Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_SAVE_AS, Actions.FLOW_GRAPH_SAVE_COPY],
|
||||
[Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_SAVE_AS,
|
||||
Actions.FLOW_GRAPH_SAVE_COPY],
|
||||
[Actions.FLOW_GRAPH_SCREEN_CAPTURE],
|
||||
[Actions.FLOW_GRAPH_CLOSE, Actions.APPLICATION_QUIT]
|
||||
]),
|
||||
('_Edit', [
|
||||
[Actions.FLOW_GRAPH_UNDO, Actions.FLOW_GRAPH_REDO],
|
||||
[Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE, Actions.SELECT_ALL],
|
||||
[Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, ('_Align', Actions.BLOCK_ALIGNMENTS)],
|
||||
[Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE,
|
||||
Actions.ELEMENT_DELETE, Actions.SELECT_ALL],
|
||||
[Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW,
|
||||
('_Align', Actions.BLOCK_ALIGNMENTS)],
|
||||
[Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS],
|
||||
[Actions.BLOCK_PARAM_MODIFY]
|
||||
]),
|
||||
('_View', [
|
||||
[Actions.TOGGLE_BLOCKS_WINDOW],
|
||||
[Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_SCROLL_LOCK, Actions.SAVE_CONSOLE, Actions.CLEAR_CONSOLE],
|
||||
[Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_SCROLL_LOCK,
|
||||
Actions.SAVE_CONSOLE, Actions.CLEAR_CONSOLE],
|
||||
[Actions.TOGGLE_HIDE_VARIABLES, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR,
|
||||
Actions.TOGGLE_SHOW_PARAMETER_EXPRESSION, Actions.TOGGLE_SHOW_PARAMETER_EVALUATION],
|
||||
[Actions.TOGGLE_HIDE_DISABLED_BLOCKS, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS, Actions.TOGGLE_SHOW_BLOCK_IDS,],
|
||||
[Actions.TOGGLE_HIDE_DISABLED_BLOCKS, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
|
||||
Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS, Actions.TOGGLE_SHOW_BLOCK_IDS, ],
|
||||
[Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB],
|
||||
[Actions.ZOOM_IN],
|
||||
[Actions.ZOOM_OUT],
|
||||
@ -87,15 +95,18 @@ MENU_BAR_LIST = [
|
||||
[Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY]
|
||||
]),
|
||||
('_Help', [
|
||||
[Actions.HELP_WINDOW_DISPLAY, Actions.TYPES_WINDOW_DISPLAY, Actions.KEYBOARD_SHORTCUTS_WINDOW_DISPLAY, Actions.XML_PARSER_ERRORS_DISPLAY],
|
||||
[Actions.HELP_WINDOW_DISPLAY, Actions.TYPES_WINDOW_DISPLAY,
|
||||
Actions.KEYBOARD_SHORTCUTS_WINDOW_DISPLAY, Actions.XML_PARSER_ERRORS_DISPLAY],
|
||||
[Actions.GET_INVOLVED_WINDOW_DISPLAY, Actions.ABOUT_WINDOW_DISPLAY]
|
||||
])]
|
||||
|
||||
|
||||
# The list of actions for the context menu.
|
||||
CONTEXT_MENU_LIST = [
|
||||
[Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE],
|
||||
[Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS],
|
||||
[Actions.BLOCK_CUT, Actions.BLOCK_COPY,
|
||||
Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE],
|
||||
[Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW,
|
||||
Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS],
|
||||
[("_More", [
|
||||
[Actions.BLOCK_CREATE_HIER, Actions.OPEN_HIER],
|
||||
[Actions.BUSSIFY_SOURCES, Actions.BUSSIFY_SINKS]
|
||||
@ -181,7 +192,8 @@ class MenuHelper(SubMenuHelper):
|
||||
target = "{}.{}".format(parent.prefix, parent.name)
|
||||
menuitem = Gio.MenuItem.new(label, None)
|
||||
if hasattr(parent, "icon_name"):
|
||||
menuitem.set_icon(Gio.Icon.new_for_string(parent.icon_name))
|
||||
menuitem.set_icon(
|
||||
Gio.Icon.new_for_string(parent.icon_name))
|
||||
|
||||
# Create the new submenu
|
||||
if isinstance(child, list):
|
||||
@ -216,6 +228,7 @@ class MenuHelper(SubMenuHelper):
|
||||
parent_obj.remove(obj_idx)
|
||||
parent_obj.insert_item(obj_idx, obj)
|
||||
|
||||
|
||||
class ToolbarHelper(SubMenuHelper):
|
||||
"""
|
||||
Builds a toolbar from a given list of actions.
|
||||
@ -275,6 +288,7 @@ class ToolbarHelper(SubMenuHelper):
|
||||
create_func, parent_obj, _, obj, set_func = self.submenus[name]
|
||||
set_func(obj, create_func())
|
||||
|
||||
|
||||
class Menu(Gio.Menu, MenuHelper):
|
||||
""" Main Menu """
|
||||
|
||||
@ -309,7 +323,7 @@ class Toolbar(Gtk.Toolbar, ToolbarHelper):
|
||||
ToolbarHelper.__init__(self)
|
||||
|
||||
self.set_style(Gtk.ToolbarStyle.ICONS)
|
||||
#self.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
|
||||
# self.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
|
||||
|
||||
#SubMenuCreator.__init__(self)
|
||||
# SubMenuCreator.__init__(self)
|
||||
self.build_toolbar(TOOLBAR_LIST, self)
|
||||
|
||||
@ -34,7 +34,8 @@ def _format_doc(doc):
|
||||
|
||||
|
||||
def _format_cat_tooltip(category):
|
||||
tooltip = '{}: {}'.format('Category' if len(category) > 1 else 'Module', category[-1])
|
||||
tooltip = '{}: {}'.format('Category' if len(
|
||||
category) > 1 else 'Module', category[-1])
|
||||
|
||||
if category == ('Core',):
|
||||
tooltip += '\n\nThis subtree is meant for blocks included with GNU Radio (in-tree).'
|
||||
@ -68,26 +69,33 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
# search entry
|
||||
self.search_entry = Gtk.Entry()
|
||||
try:
|
||||
self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, 'edit-find')
|
||||
self.search_entry.set_icon_activatable(Gtk.EntryIconPosition.PRIMARY, False)
|
||||
self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, 'window-close')
|
||||
self.search_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.PRIMARY, 'edit-find')
|
||||
self.search_entry.set_icon_activatable(
|
||||
Gtk.EntryIconPosition.PRIMARY, False)
|
||||
self.search_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, 'window-close')
|
||||
self.search_entry.connect('icon-release', self._handle_icon_event)
|
||||
except AttributeError:
|
||||
pass # no icon for old pygtk
|
||||
self.search_entry.connect('changed', self._update_search_tree)
|
||||
self.search_entry.connect('key-press-event', self._handle_search_key_press)
|
||||
self.search_entry.connect(
|
||||
'key-press-event', self._handle_search_key_press)
|
||||
self.pack_start(self.search_entry, False, False, 0)
|
||||
|
||||
# make the tree model for holding blocks and a temporary one for search results
|
||||
self.treestore = Gtk.TreeStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
|
||||
self.treestore_search = Gtk.TreeStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
|
||||
self.treestore = Gtk.TreeStore(
|
||||
GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
|
||||
self.treestore_search = Gtk.TreeStore(
|
||||
GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
|
||||
|
||||
self.treeview = Gtk.TreeView(model=self.treestore)
|
||||
self.treeview.set_enable_search(False) # disable pop up search box
|
||||
self.treeview.set_search_column(-1) # really disable search
|
||||
self.treeview.set_headers_visible(False)
|
||||
self.treeview.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
self.treeview.connect('button-press-event', self._handle_mouse_button_press)
|
||||
self.treeview.connect('button-press-event',
|
||||
self._handle_mouse_button_press)
|
||||
self.treeview.connect('key-press-event', self._handle_search_key_press)
|
||||
|
||||
self.treeview.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
|
||||
@ -99,13 +107,16 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
column.set_sort_column_id(0)
|
||||
self.treestore.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
||||
# setup drag and drop
|
||||
self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, Constants.DND_TARGETS, Gdk.DragAction.COPY)
|
||||
self.treeview.enable_model_drag_source(
|
||||
Gdk.ModifierType.BUTTON1_MASK, Constants.DND_TARGETS, Gdk.DragAction.COPY)
|
||||
self.treeview.connect('drag-data-get', self._handle_drag_get_data)
|
||||
# make the scrolled window to hold the tree view
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.add(self.treeview)
|
||||
scrolled_window.set_size_request(Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
|
||||
scrolled_window.set_size_request(
|
||||
Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
|
||||
self.pack_start(scrolled_window, True, True, 0)
|
||||
# map categories to iters, automatic mapping for root
|
||||
self._categories = {tuple(): None}
|
||||
@ -128,10 +139,11 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
self.treeview.collapse_all()
|
||||
core_module_iter = self._categories.get((module_name,))
|
||||
if core_module_iter:
|
||||
self.treeview.expand_row(self.treestore.get_path(core_module_iter), False)
|
||||
self.treeview.expand_row(
|
||||
self.treestore.get_path(core_module_iter), False)
|
||||
|
||||
############################################################
|
||||
## Block Tree Methods
|
||||
# Block Tree Methods
|
||||
############################################################
|
||||
def add_block(self, block, treestore=None, categories=None):
|
||||
"""
|
||||
@ -144,16 +156,19 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
treestore = treestore or self.treestore
|
||||
categories = categories or self._categories
|
||||
|
||||
category = tuple(filter(str, block.category)) # tuple is hashable, remove empty cats
|
||||
# tuple is hashable, remove empty cats
|
||||
category = tuple(filter(str, block.category))
|
||||
|
||||
# add category and all sub categories
|
||||
for level, parent_cat_name in enumerate(category, 1):
|
||||
parent_category = category[:level]
|
||||
if parent_category not in categories:
|
||||
iter_ = treestore.insert_before(categories[parent_category[:-1]], None)
|
||||
iter_ = treestore.insert_before(
|
||||
categories[parent_category[:-1]], None)
|
||||
treestore.set_value(iter_, NAME_INDEX, parent_cat_name)
|
||||
treestore.set_value(iter_, KEY_INDEX, '')
|
||||
treestore.set_value(iter_, DOC_INDEX, _format_cat_tooltip(parent_category))
|
||||
treestore.set_value(
|
||||
iter_, DOC_INDEX, _format_cat_tooltip(parent_category))
|
||||
categories[parent_category] = iter_
|
||||
# add block
|
||||
iter_ = treestore.insert_before(categories[category], None)
|
||||
@ -175,7 +190,7 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
self.treestore_search.foreach(update_doc)
|
||||
|
||||
############################################################
|
||||
## Helper Methods
|
||||
# Helper Methods
|
||||
############################################################
|
||||
def _get_selected_block_key(self):
|
||||
"""
|
||||
@ -195,7 +210,7 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
self.treeview.expand_to_path(path)
|
||||
|
||||
############################################################
|
||||
## Event Handlers
|
||||
# Event Handlers
|
||||
############################################################
|
||||
def _handle_icon_event(self, widget, icon, event):
|
||||
if icon == Gtk.EntryIconPosition.PRIMARY:
|
||||
@ -216,7 +231,8 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
self.treestore_search.clear()
|
||||
self._categories_search = {tuple(): None}
|
||||
for block in matching_blocks:
|
||||
self.add_block(block, self.treestore_search, self._categories_search)
|
||||
self.add_block(block, self.treestore_search,
|
||||
self._categories_search)
|
||||
self.treeview.set_model(self.treestore_search)
|
||||
self.treeview.expand_all()
|
||||
|
||||
@ -232,7 +248,8 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
selected = self.treestore_search.iter_children(selected)
|
||||
if selected is not None:
|
||||
key = self.treestore_search.get_value(selected, KEY_INDEX)
|
||||
if key: self.emit('create_new_block', key)
|
||||
if key:
|
||||
self.emit('create_new_block', key)
|
||||
elif widget == self.treeview:
|
||||
key = self._get_selected_block_key()
|
||||
if key:
|
||||
@ -248,7 +265,7 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
self.search_entry.hide()
|
||||
|
||||
elif (event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_f) \
|
||||
or event.keyval == Gdk.KEY_slash:
|
||||
or event.keyval == Gdk.KEY_slash:
|
||||
# propagation doesn't work although treeview search is disabled =(
|
||||
# manually trigger action...
|
||||
Actions.FIND_BLOCKS.activate()
|
||||
@ -258,7 +275,7 @@ class BlockTreeWindow(Gtk.VBox):
|
||||
Actions.TOGGLE_BLOCKS_WINDOW.activate()
|
||||
|
||||
else:
|
||||
return False # propagate event
|
||||
return False # propagate event
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@ -142,7 +142,8 @@ class Config(CoreConfig):
|
||||
|
||||
def get_recent_files(self):
|
||||
""" Gets recent files, removes any that do not exist and re-saves it """
|
||||
files = list(filter(os.path.exists, self.get_file_list('files_recent')))
|
||||
files = list(
|
||||
filter(os.path.exists, self.get_file_list('files_recent')))
|
||||
self.set_recent_files(files)
|
||||
return files
|
||||
|
||||
@ -168,9 +169,9 @@ class Config(CoreConfig):
|
||||
# Figure out default
|
||||
if sidebar:
|
||||
_, h = self.main_window_size()
|
||||
return self.entry('variable_editor_sidebar_position', pos, default=int(h*0.7))
|
||||
return self.entry('variable_editor_sidebar_position', pos, default=int(h * 0.7))
|
||||
else:
|
||||
return self.entry('variable_editor_position', pos, default=int(self.blocks_window_position()*0.5))
|
||||
return self.entry('variable_editor_position', pos, default=int(self.blocks_window_position() * 0.5))
|
||||
|
||||
def variable_editor_sidebar(self, pos=None):
|
||||
return self.entry('variable_editor_sidebar', pos, default=False)
|
||||
|
||||
@ -7,17 +7,15 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
"""
|
||||
|
||||
|
||||
from ..core import Messages
|
||||
from .Dialogs import TextDisplay, MessageDialogWrapper
|
||||
from .Constants import DEFAULT_CONSOLE_WINDOW_WIDTH
|
||||
from gi.repository import Gtk, Gdk, GObject
|
||||
import os
|
||||
import logging
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GObject
|
||||
|
||||
from .Constants import DEFAULT_CONSOLE_WINDOW_WIDTH
|
||||
from .Dialogs import TextDisplay, MessageDialogWrapper
|
||||
|
||||
from ..core import Messages
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -91,7 +91,8 @@ and kindly ask to update their GRC Block Descriptions or Block Tree to include a
|
||||
# _SCREEN = Gdk.Screen.get_default()
|
||||
# _SCREEN_RESOLUTION = _SCREEN.get_resolution() if _SCREEN else -1
|
||||
# DPI_SCALING = _SCREEN_RESOLUTION / 96.0 if _SCREEN_RESOLUTION > 0 else 1.0
|
||||
DPI_SCALING = 1.0 # todo: figure out the GTK3 way (maybe cairo does this for us
|
||||
# todo: figure out the GTK3 way (maybe cairo does this for us
|
||||
DPI_SCALING = 1.0
|
||||
|
||||
# Gtk-themes classified as dark
|
||||
GTK_DARK_THEMES = [
|
||||
@ -113,7 +114,10 @@ def update_font_size(font_size):
|
||||
PORT_FONT = BLOCK_FONT
|
||||
PARAM_FONT = "%s %f" % (FONT_FAMILY, font_size - 0.5)
|
||||
|
||||
PORT_SEPARATION = PORT_SPACING + 2 * PORT_LABEL_PADDING + int(1.5 * font_size)
|
||||
PORT_SEPARATION += -PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple
|
||||
PORT_SEPARATION = PORT_SPACING + 2 * \
|
||||
PORT_LABEL_PADDING + int(1.5 * font_size)
|
||||
PORT_SEPARATION += - \
|
||||
PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple
|
||||
|
||||
|
||||
update_font_size(DEFAULT_FONT_SIZE)
|
||||
|
||||
@ -237,7 +237,8 @@ class ErrorsDialog(Gtk.Dialog):
|
||||
aspect = "Connection to '{}'".format(element.sink_block.name)
|
||||
elif element.is_port:
|
||||
src = element.parent_block.name
|
||||
aspect = "{} '{}'".format('Sink' if element.is_sink else 'Source', element.name)
|
||||
aspect = "{} '{}'".format(
|
||||
'Sink' if element.is_sink else 'Source', element.name)
|
||||
elif element.is_param:
|
||||
src = element.parent_block.name
|
||||
aspect = "Param '{}'".format(element.name)
|
||||
@ -279,7 +280,7 @@ def show_about(parent, config):
|
||||
except GLib.Error:
|
||||
Messages.send("Failed to set window logo\n")
|
||||
|
||||
#ad.set_comments("")
|
||||
# ad.set_comments("")
|
||||
ad.set_copyright(config.license.splitlines()[0])
|
||||
ad.set_website(config.website)
|
||||
|
||||
@ -311,6 +312,7 @@ def show_help(parent):
|
||||
parent, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, title='Help', markup=markup
|
||||
).run_and_destroy()
|
||||
|
||||
|
||||
def show_keyboard_shortcuts(parent):
|
||||
""" Display keyboard shortcut-keys. """
|
||||
markup = textwrap.dedent("""\
|
||||
@ -371,7 +373,8 @@ def show_get_involved(parent):
|
||||
|
||||
def show_types(parent):
|
||||
""" Display information about standard data types. """
|
||||
colors = [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES]
|
||||
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(
|
||||
@ -424,7 +427,8 @@ def choose_editor(parent, config):
|
||||
file_dialog = Gtk.FileChooserDialog(
|
||||
'Select an Editor...', None,
|
||||
Gtk.FileChooserAction.OPEN,
|
||||
('gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK),
|
||||
('gtk-cancel', Gtk.ResponseType.CANCEL,
|
||||
'gtk-open', Gtk.ResponseType.OK),
|
||||
transient_for=parent
|
||||
)
|
||||
file_dialog.set_select_multiple(False)
|
||||
@ -449,7 +453,8 @@ def choose_editor(parent, config):
|
||||
# Save
|
||||
editor = config.editor = process
|
||||
except Exception:
|
||||
Messages.send('>>> Unable to load the default editor. Please choose an editor.\n')
|
||||
Messages.send(
|
||||
'>>> Unable to load the default editor. Please choose an editor.\n')
|
||||
|
||||
if editor == '':
|
||||
Messages.send('>>> No editor selected.\n')
|
||||
|
||||
@ -79,10 +79,10 @@ class DrawingArea(Gtk.DrawingArea):
|
||||
self.set_can_focus(True)
|
||||
self.connect('focus-out-event', self._handle_focus_lost_event)
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Handlers
|
||||
##########################################################################
|
||||
|
||||
def _handle_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time):
|
||||
"""
|
||||
Handle a drag and drop by adding a block at the given coordinate.
|
||||
@ -96,7 +96,7 @@ class DrawingArea(Gtk.DrawingArea):
|
||||
self._set_zoom_factor(zoom_factor)
|
||||
|
||||
def zoom_out(self):
|
||||
change = 1/1.2
|
||||
change = 1 / 1.2
|
||||
zoom_factor = max(self.zoom_factor * change, 0.1)
|
||||
self._set_zoom_factor(zoom_factor)
|
||||
|
||||
|
||||
@ -80,7 +80,8 @@ class ExecFlowGraphThread(threading.Thread):
|
||||
Execute this C++ flow graph after generating and compiling it.
|
||||
"""
|
||||
generator = self.page.get_generator()
|
||||
run_command = generator.file_path + '/build/' + self.flow_graph.get_option('id')
|
||||
run_command = generator.file_path + \
|
||||
'/build/' + self.flow_graph.get_option('id')
|
||||
|
||||
dirname = generator.file_path
|
||||
builddir = os.path.join(dirname, 'build')
|
||||
|
||||
@ -41,7 +41,8 @@ class FileDialogHelper(Gtk.FileChooserDialog, object):
|
||||
|
||||
Gtk.FileChooserDialog.__init__(self, title=self.title, action=self.action,
|
||||
transient_for=parent)
|
||||
self.add_buttons('gtk-cancel', Gtk.ResponseType.CANCEL, ok_stock, Gtk.ResponseType.OK)
|
||||
self.add_buttons('gtk-cancel', Gtk.ResponseType.CANCEL,
|
||||
ok_stock, Gtk.ResponseType.OK)
|
||||
self.set_select_multiple(False)
|
||||
self.set_local_only(True)
|
||||
|
||||
@ -49,12 +50,14 @@ class FileDialogHelper(Gtk.FileChooserDialog, object):
|
||||
self.current_file_path = current_file_path or path.join(
|
||||
Constants.DEFAULT_FILE_PATH, Constants.NEW_FLOGRAPH_TITLE + Constants.FILE_EXTENSION)
|
||||
|
||||
self.set_current_folder(path.dirname(current_file_path)) # current directory
|
||||
self.set_current_folder(path.dirname(
|
||||
current_file_path)) # current directory
|
||||
self.setup_filters()
|
||||
|
||||
def setup_filters(self, filters=None):
|
||||
set_default = True
|
||||
filters = filters or ([(self.filter_label, self.filter_ext)] if self.filter_label else [])
|
||||
filters = filters or (
|
||||
[(self.filter_label, self.filter_ext)] if self.filter_label else [])
|
||||
filters.append(('All Files', ''))
|
||||
for label, ext in filters:
|
||||
if not label:
|
||||
@ -81,7 +84,8 @@ class SaveFileDialog(FileDialogHelper):
|
||||
|
||||
def __init__(self, parent, current_file_path):
|
||||
super(SaveFileDialog, self).__init__(parent, current_file_path)
|
||||
self.set_current_name(path.splitext(path.basename(self.current_file_path))[0] + self.filter_ext)
|
||||
self.set_current_name(path.splitext(path.basename(
|
||||
self.current_file_path))[0] + self.filter_ext)
|
||||
self.set_create_folders(True)
|
||||
self.set_do_overwrite_confirmation(True)
|
||||
|
||||
@ -94,7 +98,8 @@ class OpenFileDialog(FileDialogHelper):
|
||||
Dialogs.MessageDialogWrapper(
|
||||
self.parent,
|
||||
Gtk.MessageType.WARNING, Gtk.ButtonsType.CLOSE, 'Cannot Open!',
|
||||
'File <b>{filename}</b> Does not Exist!'.format(filename=Utils.encode(filename)),
|
||||
'File <b>{filename}</b> Does not Exist!'.format(
|
||||
filename=Utils.encode(filename)),
|
||||
).run_and_destroy()
|
||||
|
||||
def get_filename(self):
|
||||
@ -145,7 +150,8 @@ class SaveConsole(SaveFileDialog):
|
||||
|
||||
class SaveScreenShot(SaveFileDialog):
|
||||
title = 'Save a Flow Graph Screen Shot...'
|
||||
filters = [('PDF Files', '.pdf'), ('PNG Files', '.png'), ('SVG Files', '.svg')]
|
||||
filters = [('PDF Files', '.pdf'), ('PNG Files', '.png'),
|
||||
('SVG Files', '.svg')]
|
||||
filter_ext = '.pdf' # the default
|
||||
|
||||
def __init__(self, parent, current_file_path=''):
|
||||
@ -154,7 +160,8 @@ class SaveScreenShot(SaveFileDialog):
|
||||
self.config = Gtk.Application.get_default().config
|
||||
|
||||
self._button = button = Gtk.CheckButton(label='Background transparent')
|
||||
self._button.set_active(self.config.screen_shot_background_transparent())
|
||||
self._button.set_active(
|
||||
self.config.screen_shot_background_transparent())
|
||||
self.set_extra_widget(button)
|
||||
|
||||
def setup_filters(self, filters=None):
|
||||
@ -164,7 +171,8 @@ class SaveScreenShot(SaveFileDialog):
|
||||
Dialogs.MessageDialogWrapper(
|
||||
self.parent,
|
||||
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, 'Can not Save!',
|
||||
'File Extension of <b>{filename}</b> not supported!'.format(filename=Utils.encode(filename)),
|
||||
'File Extension of <b>{filename}</b> not supported!'.format(
|
||||
filename=Utils.encode(filename)),
|
||||
).run_and_destroy()
|
||||
|
||||
def run(self):
|
||||
|
||||
@ -43,7 +43,8 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
MainWindow constructor
|
||||
Setup the menu, toolbar, flow graph editor notebook, block selection window...
|
||||
"""
|
||||
Gtk.ApplicationWindow.__init__(self, title="GNU Radio Companion", application=app)
|
||||
Gtk.ApplicationWindow.__init__(
|
||||
self, title="GNU Radio Companion", application=app)
|
||||
log.debug("__init__()")
|
||||
|
||||
self._platform = platform
|
||||
@ -63,7 +64,8 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
icon = icon_theme.lookup_icon("gnuradio-grc", 48, 0)
|
||||
if not icon:
|
||||
# Set default window icon
|
||||
self.set_icon_from_file(os.path.dirname(os.path.abspath(__file__)) + "/icon.png")
|
||||
self.set_icon_from_file(os.path.dirname(
|
||||
os.path.abspath(__file__)) + "/icon.png")
|
||||
else:
|
||||
# Use gnuradio icon
|
||||
self.set_icon(icon.load_icon())
|
||||
@ -85,7 +87,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
vbox.pack_start(self.tool_bar, False, False, 0)
|
||||
|
||||
# Main parent container for the different panels
|
||||
self.main = Gtk.HPaned() #(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.main = Gtk.HPaned() # (orientation=Gtk.Orientation.HORIZONTAL)
|
||||
vbox.pack_start(self.main, True, True, 0)
|
||||
|
||||
# Create the notebook
|
||||
@ -99,15 +101,19 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
# Create the block tree and variable panels
|
||||
self.btwin = BlockTreeWindow(platform)
|
||||
self.btwin.connect('create_new_block', self._add_block_to_current_flow_graph)
|
||||
self.btwin.connect('create_new_block',
|
||||
self._add_block_to_current_flow_graph)
|
||||
self.vars = VariableEditor()
|
||||
self.vars.connect('create_new_block', self._add_block_to_current_flow_graph)
|
||||
self.vars.connect('remove_block', self._remove_block_from_current_flow_graph)
|
||||
self.vars.connect('create_new_block',
|
||||
self._add_block_to_current_flow_graph)
|
||||
self.vars.connect(
|
||||
'remove_block', self._remove_block_from_current_flow_graph)
|
||||
|
||||
# Figure out which place to put the variable editor
|
||||
self.left = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
|
||||
self.right = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
|
||||
self.left_subpanel = Gtk.HPaned() #orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.left = Gtk.VPaned() # orientation=Gtk.Orientation.VERTICAL)
|
||||
self.right = Gtk.VPaned() # orientation=Gtk.Orientation.VERTICAL)
|
||||
# orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.left_subpanel = Gtk.HPaned()
|
||||
|
||||
self.variable_panel_sidebar = self.config.variable_editor_sidebar()
|
||||
if self.variable_panel_sidebar:
|
||||
@ -133,9 +139,11 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.main.set_position(self.config.blocks_window_position())
|
||||
self.left.set_position(self.config.console_window_position())
|
||||
if self.variable_panel_sidebar:
|
||||
self.right.set_position(self.config.variable_editor_position(sidebar=True))
|
||||
self.right.set_position(
|
||||
self.config.variable_editor_position(sidebar=True))
|
||||
else:
|
||||
self.left_subpanel.set_position(self.config.variable_editor_position())
|
||||
self.left_subpanel.set_position(
|
||||
self.config.variable_editor_position())
|
||||
|
||||
self.show_all()
|
||||
log.debug("Main window ready")
|
||||
@ -238,16 +246,18 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
file_path: optional file to load into the flow graph
|
||||
show: true if the page should be shown after loading
|
||||
"""
|
||||
#if the file is already open, show the open page and return
|
||||
if file_path and file_path in self._get_files(): #already open
|
||||
page = self.notebook.get_nth_page(self._get_files().index(file_path))
|
||||
# if the file is already open, show the open page and return
|
||||
if file_path and file_path in self._get_files(): # already open
|
||||
page = self.notebook.get_nth_page(
|
||||
self._get_files().index(file_path))
|
||||
self._set_page(page)
|
||||
return
|
||||
try: #try to load from file
|
||||
if file_path: Messages.send_start_load(file_path)
|
||||
try: # try to load from file
|
||||
if file_path:
|
||||
Messages.send_start_load(file_path)
|
||||
flow_graph = self._platform.make_flow_graph()
|
||||
flow_graph.grc_file_path = file_path
|
||||
#print flow_graph
|
||||
# print flow_graph
|
||||
page = Page(
|
||||
self,
|
||||
flow_graph=flow_graph,
|
||||
@ -260,18 +270,20 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
str(Messages.flowgraph_error)
|
||||
)
|
||||
)
|
||||
if file_path: Messages.send_end_load()
|
||||
except Exception as e: #return on failure
|
||||
if file_path:
|
||||
Messages.send_end_load()
|
||||
except Exception as e: # return on failure
|
||||
Messages.send_fail_load(e)
|
||||
if isinstance(e, KeyError) and str(e) == "'options'":
|
||||
# This error is unrecoverable, so crash gracefully
|
||||
exit(-1)
|
||||
return
|
||||
#add this page to the notebook
|
||||
# add this page to the notebook
|
||||
self.notebook.append_page(page, page.tab)
|
||||
self.notebook.set_tab_reorderable(page, True)
|
||||
#only show if blank or manual
|
||||
if not file_path or show: self._set_page(page)
|
||||
# only show if blank or manual
|
||||
if not file_path or show:
|
||||
self._set_page(page)
|
||||
|
||||
def close_pages(self):
|
||||
"""
|
||||
@ -280,25 +292,29 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
Returns:
|
||||
true if all closed
|
||||
"""
|
||||
open_files = [file for file in self._get_files() if file] #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
|
||||
# close each page
|
||||
for page in sorted(self.get_pages(), key=lambda p: p.saved):
|
||||
self.page_to_be_closed = page
|
||||
closed = self.close_page(False)
|
||||
if not closed:
|
||||
break
|
||||
if self.notebook.get_n_pages(): return False
|
||||
#save state before closing
|
||||
if self.notebook.get_n_pages():
|
||||
return False
|
||||
# save state before closing
|
||||
self.config.set_open_files(open_files)
|
||||
self.config.file_open(open_file)
|
||||
self.config.main_window_size(self.get_size())
|
||||
self.config.console_window_position(self.left.get_position())
|
||||
self.config.blocks_window_position(self.main.get_position())
|
||||
if self.variable_panel_sidebar:
|
||||
self.config.variable_editor_position(self.right.get_position(), sidebar=True)
|
||||
self.config.variable_editor_position(
|
||||
self.right.get_position(), sidebar=True)
|
||||
else:
|
||||
self.config.variable_editor_position(self.left_subpanel.get_position())
|
||||
self.config.variable_editor_position(
|
||||
self.left_subpanel.get_position())
|
||||
self.config.save()
|
||||
return True
|
||||
|
||||
@ -311,29 +327,31 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
Args:
|
||||
ensure: boolean
|
||||
"""
|
||||
if not self.page_to_be_closed: self.page_to_be_closed = self.current_page
|
||||
#show the page if it has an executing flow graph or is unsaved
|
||||
if not self.page_to_be_closed:
|
||||
self.page_to_be_closed = self.current_page
|
||||
# show the page if it has an executing flow graph or is unsaved
|
||||
if self.page_to_be_closed.process or not self.page_to_be_closed.saved:
|
||||
self._set_page(self.page_to_be_closed)
|
||||
#unsaved? ask the user
|
||||
# unsaved? ask the user
|
||||
if not self.page_to_be_closed.saved:
|
||||
response = self._save_changes() # return value is either OK, CLOSE, or CANCEL
|
||||
response = self._save_changes() # return value is either OK, CLOSE, or CANCEL
|
||||
if response == Gtk.ResponseType.OK:
|
||||
Actions.FLOW_GRAPH_SAVE() #try to save
|
||||
if not self.page_to_be_closed.saved: #still unsaved?
|
||||
self.page_to_be_closed = None #set the page to be closed back to None
|
||||
Actions.FLOW_GRAPH_SAVE() # try to save
|
||||
if not self.page_to_be_closed.saved: # still unsaved?
|
||||
self.page_to_be_closed = None # set the page to be closed back to None
|
||||
return False
|
||||
elif response == Gtk.ResponseType.CANCEL:
|
||||
self.page_to_be_closed = None
|
||||
return False
|
||||
#stop the flow graph if executing
|
||||
# stop the flow graph if executing
|
||||
if self.page_to_be_closed.process:
|
||||
Actions.FLOW_GRAPH_KILL()
|
||||
#remove the page
|
||||
self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed))
|
||||
# remove the page
|
||||
self.notebook.remove_page(
|
||||
self.notebook.page_num(self.page_to_be_closed))
|
||||
if ensure and self.notebook.get_n_pages() == 0:
|
||||
self.new_page() #no pages, make a new one
|
||||
self.page_to_be_closed = None #set the page to be closed back to None
|
||||
self.new_page() # no pages, make a new one
|
||||
self.page_to_be_closed = None # set the page to be closed back to None
|
||||
return True
|
||||
|
||||
############################################################
|
||||
@ -405,7 +423,8 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
page: the page widget
|
||||
"""
|
||||
self.current_page = page
|
||||
self.notebook.set_current_page(self.notebook.page_num(self.current_page))
|
||||
self.notebook.set_current_page(
|
||||
self.notebook.page_num(self.current_page))
|
||||
|
||||
def _save_changes(self):
|
||||
"""
|
||||
|
||||
@ -118,9 +118,12 @@ class Page(Gtk.HBox):
|
||||
self.viewport.add(self.drawing_area)
|
||||
|
||||
self.scrolled_window = Gtk.ScrolledWindow()
|
||||
self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
|
||||
self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
|
||||
self.scrolled_window.connect('key-press-event', self._handle_scroll_window_key_press)
|
||||
self.scrolled_window.set_size_request(
|
||||
MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
|
||||
self.scrolled_window.set_policy(
|
||||
Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
|
||||
self.scrolled_window.connect(
|
||||
'key-press-event', self._handle_scroll_window_key_press)
|
||||
|
||||
self.scrolled_window.add(self.viewport)
|
||||
self.pack_start(self.scrolled_window, True, True, 0)
|
||||
|
||||
@ -42,6 +42,7 @@ def have_dark_theme():
|
||||
return False
|
||||
return is_dark_theme(theme)
|
||||
|
||||
|
||||
def add_style_provider():
|
||||
"""
|
||||
Load GTK styles
|
||||
@ -55,6 +56,8 @@ def add_style_provider():
|
||||
style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
)
|
||||
|
||||
|
||||
add_style_provider()
|
||||
|
||||
|
||||
@ -93,7 +96,8 @@ 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.label.set_markup(
|
||||
self.param.format_label_markup(self._have_pending_changes))
|
||||
self.set_color('dtype_' + self.param.dtype)
|
||||
|
||||
self.set_tooltip_text(self.param.format_tooltip_text())
|
||||
@ -117,14 +121,14 @@ class InputParam(Gtk.HBox):
|
||||
Handle a gui change by setting the new param value,
|
||||
calling the callback (if applicable), and updating.
|
||||
"""
|
||||
#set the new value
|
||||
# set the new value
|
||||
self.param.set_value(self.get_text())
|
||||
#call the callback
|
||||
# call the callback
|
||||
if self._changed_callback:
|
||||
self._changed_callback(self, None)
|
||||
else:
|
||||
self.param.validate()
|
||||
#gui update
|
||||
# gui update
|
||||
self._have_pending_changes = False
|
||||
self._update_gui()
|
||||
|
||||
@ -205,7 +209,8 @@ class PythonEditorParam(InputParam):
|
||||
self.pack_start(button, True, True, True)
|
||||
|
||||
def open_editor(self, widget=None):
|
||||
self.param.parent_flowgraph.install_external_editor(self.param, parent=self._transient_for)
|
||||
self.param.parent_flowgraph.install_external_editor(
|
||||
self.param, parent=self._transient_for)
|
||||
|
||||
def get_text(self):
|
||||
pass # we never update the value from here
|
||||
@ -297,13 +302,15 @@ class FileParam(EntryParam):
|
||||
"""
|
||||
# get the paths
|
||||
file_path = self.param.is_valid() and self.param.get_evaluated() or ''
|
||||
(dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '')
|
||||
(dirname, basename) = os.path.isfile(
|
||||
file_path) and os.path.split(file_path) or (file_path, '')
|
||||
# check for qss theme default directory
|
||||
if self.param.key == 'qt_qss_theme':
|
||||
dirname = os.path.dirname(dirname) # trim filename
|
||||
if not os.path.exists(dirname):
|
||||
config = self.param.parent_platform.config
|
||||
dirname = os.path.join(config.install_prefix, '/share/gnuradio/themes')
|
||||
config = self.param.parent_platform.config
|
||||
dirname = os.path.join(
|
||||
config.install_prefix, '/share/gnuradio/themes')
|
||||
if not os.path.exists(dirname):
|
||||
dirname = os.getcwd() # fix bad paths
|
||||
|
||||
@ -313,17 +320,20 @@ class FileParam(EntryParam):
|
||||
title='Open a Data File...', action=Gtk.FileChooserAction.OPEN,
|
||||
transient_for=self._transient_for,
|
||||
)
|
||||
file_dialog.add_buttons('gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK)
|
||||
file_dialog.add_buttons(
|
||||
'gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK)
|
||||
elif self.param.dtype == 'file_save':
|
||||
file_dialog = Gtk.FileChooserDialog(
|
||||
title='Save a Data File...', action=Gtk.FileChooserAction.SAVE,
|
||||
transient_for=self._transient_for,
|
||||
)
|
||||
file_dialog.add_buttons('gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-save', Gtk.ResponseType.OK)
|
||||
file_dialog.add_buttons(
|
||||
'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.dtype))
|
||||
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)
|
||||
@ -334,6 +344,7 @@ class FileParam(EntryParam):
|
||||
self._apply_change()
|
||||
file_dialog.destroy() # destroy the dialog
|
||||
|
||||
|
||||
class DirectoryParam(FileParam):
|
||||
"""Provide an entry box for a directory and a button to browse for it."""
|
||||
|
||||
@ -344,23 +355,26 @@ class DirectoryParam(FileParam):
|
||||
"""
|
||||
dirname = self.param.get_evaluated() if self.param.is_valid() else ''
|
||||
|
||||
if not os.path.isdir(dirname): # Check if directory exists, if not fall back to workdir
|
||||
# Check if directory exists, if not fall back to workdir
|
||||
if not os.path.isdir(dirname):
|
||||
dirname = os.getcwd()
|
||||
|
||||
if self.param.dtype == "dir_select": # Setup directory selection dialog, and fail for unexpected dtype
|
||||
if self.param.dtype == "dir_select": # Setup directory selection dialog, and fail for unexpected dtype
|
||||
dir_dialog = Gtk.FileChooserDialog(
|
||||
title='Select a Directory...', action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
transient_for=self._transient_for
|
||||
)
|
||||
else:
|
||||
raise ValueError("Can't open directory chooser dialog for type " + repr(self.param.dtype))
|
||||
raise ValueError(
|
||||
"Can't open directory chooser dialog for type " + repr(self.param.dtype))
|
||||
|
||||
# Set dialog properties
|
||||
dir_dialog.add_buttons('gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK)
|
||||
dir_dialog.add_buttons(
|
||||
'gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK)
|
||||
dir_dialog.set_current_folder(dirname)
|
||||
dir_dialog.set_local_only(True)
|
||||
dir_dialog.set_select_multiple(False)
|
||||
|
||||
|
||||
# Show dialog and update parameter on success
|
||||
if Gtk.ResponseType.OK == dir_dialog.run():
|
||||
path = dir_dialog.get_filename()
|
||||
|
||||
@ -24,7 +24,8 @@ class ParserErrorsDialog(Gtk.Dialog):
|
||||
Args:
|
||||
block: a block instance
|
||||
"""
|
||||
GObject.GObject.__init__(self, title='Parser Errors', buttons=(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT))
|
||||
GObject.GObject.__init__(self, title='Parser Errors', buttons=(
|
||||
Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT))
|
||||
|
||||
self._error_logs = None
|
||||
self.tree_store = Gtk.TreeStore(str)
|
||||
@ -48,11 +49,12 @@ class ParserErrorsDialog(Gtk.Dialog):
|
||||
tree_view.expand_row(row.path, False)
|
||||
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.add(tree_view)
|
||||
|
||||
self.vbox.pack_start(scrolled_window, True)
|
||||
self.set_size_request(2*MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT)
|
||||
self.set_size_request(2 * MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT)
|
||||
self.show_all()
|
||||
|
||||
def update_tree_store(self, error_logs):
|
||||
@ -68,7 +70,8 @@ class ParserErrorsDialog(Gtk.Dialog):
|
||||
code = None
|
||||
for error in errors:
|
||||
# http://lxml.de/api/lxml.etree._LogEntry-class.html
|
||||
em = self.tree_store.append(parent, ["Line {e.line}: {e.message}".format(e=error)])
|
||||
em = self.tree_store.append(
|
||||
parent, ["Line {e.line}: {e.message}".format(e=error)])
|
||||
if code:
|
||||
self.tree_store.append(em, ["\n".join(
|
||||
"{} {}{}".format(line, code[line - 1].replace("\t", " ").strip("\n"),
|
||||
|
||||
@ -64,7 +64,8 @@ class PropsDialog(Gtk.Dialog):
|
||||
self._docs_text_display = doc_view = SimpleTextDisplay()
|
||||
doc_view.get_buffer().create_tag('b', weight=Pango.Weight.BOLD)
|
||||
self._docs_box = Gtk.ScrolledWindow()
|
||||
self._docs_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
self._docs_box.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
self._docs_vbox = Gtk.VBox(homogeneous=False, spacing=0)
|
||||
self._docs_box.add(self._docs_vbox)
|
||||
self._docs_link = Gtk.Label(use_markup=True)
|
||||
@ -81,7 +82,8 @@ class PropsDialog(Gtk.Dialog):
|
||||
# todo: set font size in non-deprecated way
|
||||
# code_view.override_font(Pango.FontDescription('monospace %d' % Constants.FONT_SIZE))
|
||||
code_box = Gtk.ScrolledWindow()
|
||||
code_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
code_box.set_policy(Gtk.PolicyType.AUTOMATIC,
|
||||
Gtk.PolicyType.AUTOMATIC)
|
||||
code_box.add(self._code_text_display)
|
||||
notebook.append_page(code_box, Gtk.Label(label="Generated Code"))
|
||||
else:
|
||||
@ -90,7 +92,8 @@ class PropsDialog(Gtk.Dialog):
|
||||
# Error Messages for the block
|
||||
self._error_messages_text_display = SimpleTextDisplay()
|
||||
self._error_box = Gtk.ScrolledWindow()
|
||||
self._error_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
self._error_box.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
self._error_box.add(self._error_messages_text_display)
|
||||
vpaned.pack2(self._error_box)
|
||||
vpaned.set_position(int(0.65 * Constants.MIN_DIALOG_HEIGHT))
|
||||
@ -117,7 +120,8 @@ class PropsDialog(Gtk.Dialog):
|
||||
label = Gtk.Label()
|
||||
vbox = Gtk.VBox()
|
||||
scroll_box = Gtk.ScrolledWindow()
|
||||
scroll_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
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((category, label, vbox))
|
||||
@ -232,7 +236,8 @@ class PropsDialog(Gtk.Dialog):
|
||||
buf.insert(pos, '\n')
|
||||
|
||||
# if given the current parameters an exact match can be made
|
||||
block_constructor = self._block.templates.render('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]}
|
||||
|
||||
@ -9,6 +9,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
from . import Actions
|
||||
from .Constants import STATE_CACHE_SIZE
|
||||
|
||||
|
||||
class StateCache(object):
|
||||
"""
|
||||
The state cache is an interface to a list to record data/states and to revert to previous states.
|
||||
@ -23,7 +24,7 @@ class StateCache(object):
|
||||
Args:
|
||||
initial_state: the initial state (nested data)
|
||||
"""
|
||||
self.states = [None] * STATE_CACHE_SIZE #fill states
|
||||
self.states = [None] * STATE_CACHE_SIZE # fill states
|
||||
self.current_state_index = 0
|
||||
self.num_prev_states = 0
|
||||
self.num_next_states = 0
|
||||
@ -38,10 +39,12 @@ class StateCache(object):
|
||||
Args:
|
||||
state: the new state
|
||||
"""
|
||||
self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE
|
||||
self.current_state_index = (
|
||||
self.current_state_index + 1) % STATE_CACHE_SIZE
|
||||
self.states[self.current_state_index] = state
|
||||
self.num_prev_states = self.num_prev_states + 1
|
||||
if self.num_prev_states == STATE_CACHE_SIZE: self.num_prev_states = STATE_CACHE_SIZE - 1
|
||||
if self.num_prev_states == STATE_CACHE_SIZE:
|
||||
self.num_prev_states = STATE_CACHE_SIZE - 1
|
||||
self.num_next_states = 0
|
||||
self.update_actions()
|
||||
|
||||
@ -63,7 +66,8 @@ class StateCache(object):
|
||||
the previous state or None
|
||||
"""
|
||||
if self.num_prev_states > 0:
|
||||
self.current_state_index = (self.current_state_index + STATE_CACHE_SIZE -1)%STATE_CACHE_SIZE
|
||||
self.current_state_index = (
|
||||
self.current_state_index + STATE_CACHE_SIZE - 1) % STATE_CACHE_SIZE
|
||||
self.num_next_states = self.num_next_states + 1
|
||||
self.num_prev_states = self.num_prev_states - 1
|
||||
return self.get_current_state()
|
||||
@ -77,7 +81,8 @@ class StateCache(object):
|
||||
the next state or None
|
||||
"""
|
||||
if self.num_next_states > 0:
|
||||
self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE
|
||||
self.current_state_index = (
|
||||
self.current_state_index + 1) % STATE_CACHE_SIZE
|
||||
self.num_next_states = self.num_next_states - 1
|
||||
self.num_prev_states = self.num_prev_states + 1
|
||||
return self.get_current_state()
|
||||
|
||||
14
gui/Utils.py
14
gui/Utils.py
@ -32,7 +32,7 @@ def get_rotated_coordinate(coor, rotation):
|
||||
# handles negative angles
|
||||
rotation = (rotation + 360) % 360
|
||||
if rotation not in Constants.POSSIBLE_ROTATIONS:
|
||||
raise ValueError('unusable rotation angle "%s"'%str(rotation))
|
||||
raise ValueError('unusable rotation angle "%s"' % str(rotation))
|
||||
# determine the number of degrees to rotate
|
||||
cos_r, sin_r = {
|
||||
0: (1, 0), 90: (0, 1), 180: (-1, 0), 270: (0, -1),
|
||||
@ -76,7 +76,7 @@ def num_to_str(num):
|
||||
"""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'):
|
||||
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())
|
||||
@ -87,7 +87,8 @@ def num_to_str(num):
|
||||
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 ''
|
||||
output += eng_notation(num.imag, '+g' if output else 'g') + \
|
||||
'j' if num.imag else ''
|
||||
return output
|
||||
else:
|
||||
return str(num)
|
||||
@ -144,6 +145,7 @@ def scale_scalar(coor, reverse=False):
|
||||
factor = Constants.DPI_SCALING if not reverse else 1 / Constants.DPI_SCALING
|
||||
return int(coor * factor)
|
||||
|
||||
|
||||
def get_modifier_key(angle_brackets=False):
|
||||
"""
|
||||
Get the modifier key based on platform.
|
||||
@ -167,9 +169,11 @@ def get_modifier_key(angle_brackets=False):
|
||||
|
||||
|
||||
_nproc = None
|
||||
|
||||
|
||||
def get_cmake_nproc():
|
||||
""" Get number of cmake processes for C++ flowgraphs """
|
||||
global _nproc # Cached result
|
||||
global _nproc # Cached result
|
||||
if _nproc:
|
||||
return _nproc
|
||||
try:
|
||||
@ -180,5 +184,5 @@ def get_cmake_nproc():
|
||||
if not _nproc:
|
||||
_nproc = 1
|
||||
|
||||
_nproc = max(_nproc//2 - 1, 1)
|
||||
_nproc = max(_nproc // 2 - 1, 1)
|
||||
return _nproc
|
||||
|
||||
@ -22,29 +22,35 @@ class VariableEditorContextMenu(Gtk.Menu):
|
||||
Gtk.Menu.__init__(self)
|
||||
|
||||
self.imports = Gtk.MenuItem(label="Add _Import")
|
||||
self.imports.connect('activate', var_edit.handle_action, var_edit.ADD_IMPORT)
|
||||
self.imports.connect(
|
||||
'activate', var_edit.handle_action, var_edit.ADD_IMPORT)
|
||||
self.add(self.imports)
|
||||
|
||||
self.variables = Gtk.MenuItem(label="Add _Variable")
|
||||
self.variables.connect('activate', var_edit.handle_action, var_edit.ADD_VARIABLE)
|
||||
self.variables.connect(
|
||||
'activate', var_edit.handle_action, var_edit.ADD_VARIABLE)
|
||||
self.add(self.variables)
|
||||
self.add(Gtk.SeparatorMenuItem())
|
||||
|
||||
self.enable = Gtk.MenuItem(label="_Enable")
|
||||
self.enable.connect('activate', var_edit.handle_action, var_edit.ENABLE_BLOCK)
|
||||
self.enable.connect(
|
||||
'activate', var_edit.handle_action, var_edit.ENABLE_BLOCK)
|
||||
self.disable = Gtk.MenuItem(label="_Disable")
|
||||
self.disable.connect('activate', var_edit.handle_action, var_edit.DISABLE_BLOCK)
|
||||
self.disable.connect(
|
||||
'activate', var_edit.handle_action, var_edit.DISABLE_BLOCK)
|
||||
self.add(self.enable)
|
||||
self.add(self.disable)
|
||||
self.add(Gtk.SeparatorMenuItem())
|
||||
|
||||
self.delete = Gtk.MenuItem(label="_Delete")
|
||||
self.delete.connect('activate', var_edit.handle_action, var_edit.DELETE_BLOCK)
|
||||
self.delete.connect(
|
||||
'activate', var_edit.handle_action, var_edit.DELETE_BLOCK)
|
||||
self.add(self.delete)
|
||||
self.add(Gtk.SeparatorMenuItem())
|
||||
|
||||
self.properties = Gtk.MenuItem(label="_Properties...")
|
||||
self.properties.connect('activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES)
|
||||
self.properties.connect(
|
||||
'activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES)
|
||||
self.add(self.properties)
|
||||
self.show_all()
|
||||
|
||||
@ -87,13 +93,16 @@ class VariableEditor(Gtk.VBox):
|
||||
self.treeview = Gtk.TreeView(model=self.treestore)
|
||||
self.treeview.set_enable_search(False)
|
||||
self.treeview.set_search_column(-1)
|
||||
#self.treeview.set_enable_search(True)
|
||||
#self.treeview.set_search_column(ID_INDEX)
|
||||
# self.treeview.set_enable_search(True)
|
||||
# self.treeview.set_search_column(ID_INDEX)
|
||||
self.treeview.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
|
||||
self.treeview.set_headers_visible(True)
|
||||
self.treeview.connect('button-press-event', self._handle_mouse_button_press)
|
||||
self.treeview.connect('button-release-event', self._handle_mouse_button_release)
|
||||
self.treeview.connect('motion-notify-event', self._handle_motion_notify)
|
||||
self.treeview.connect('button-press-event',
|
||||
self._handle_mouse_button_press)
|
||||
self.treeview.connect('button-release-event',
|
||||
self._handle_mouse_button_release)
|
||||
self.treeview.connect('motion-notify-event',
|
||||
self._handle_motion_notify)
|
||||
self.treeview.connect('key-press-event', self._handle_key_button_press)
|
||||
|
||||
# Block Name or Category
|
||||
@ -133,9 +142,11 @@ class VariableEditor(Gtk.VBox):
|
||||
|
||||
# Make the scrolled window to hold the tree view
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.add(self.treeview)
|
||||
scrolled_window.set_size_request(Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
|
||||
scrolled_window.set_size_request(
|
||||
Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
|
||||
self.pack_start(scrolled_window, True, True, 0)
|
||||
|
||||
# Context menus
|
||||
@ -215,9 +226,11 @@ 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.params['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()])
|
||||
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]
|
||||
@ -243,7 +256,7 @@ class VariableEditor(Gtk.VBox):
|
||||
self.emit('create_new_block', 'variable')
|
||||
elif key == self.OPEN_PROPERTIES:
|
||||
# TODO: This probably isn't working because the action doesn't expect a parameter
|
||||
#Actions.BLOCK_PARAM_MODIFY()
|
||||
# Actions.BLOCK_PARAM_MODIFY()
|
||||
pass
|
||||
elif key == self.DELETE_BLOCK:
|
||||
self.emit('remove_block', self._block.name)
|
||||
@ -251,12 +264,15 @@ class VariableEditor(Gtk.VBox):
|
||||
if self._confirm_delete:
|
||||
# Create a context menu to confirm the delete operation
|
||||
confirmation_menu = Gtk.Menu()
|
||||
block_id = self._block.params['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)
|
||||
confirm.connect('activate', self.handle_action,
|
||||
self.DELETE_BLOCK)
|
||||
confirmation_menu.add(confirm)
|
||||
confirmation_menu.show_all()
|
||||
confirmation_menu.popup(None, None, None, None, event.button, event.time)
|
||||
confirmation_menu.popup(
|
||||
None, None, None, None, event.button, event.time)
|
||||
else:
|
||||
self.handle_action(None, self.DELETE_BLOCK, None)
|
||||
elif key == self.ENABLE_BLOCK:
|
||||
@ -287,7 +303,8 @@ class VariableEditor(Gtk.VBox):
|
||||
if self._block and event.type == Gdk.EventType._2BUTTON_PRESS:
|
||||
# Open the advanced dialog if it is a gui variable
|
||||
if self._block.key not in ("variable", "import"):
|
||||
self.handle_action(None, self.OPEN_PROPERTIES, event=event)
|
||||
self.handle_action(
|
||||
None, self.OPEN_PROPERTIES, event=event)
|
||||
return True
|
||||
if event.type == Gdk.EventType.BUTTON_PRESS:
|
||||
# User is adding/removing blocks
|
||||
@ -295,19 +312,24 @@ class VariableEditor(Gtk.VBox):
|
||||
if path[2] > col.cell_get_position(self.action_cell)[0]:
|
||||
if row[1] == "Imports":
|
||||
# Add a new import block.
|
||||
self.handle_action(None, self.ADD_IMPORT, event=event)
|
||||
self.handle_action(
|
||||
None, self.ADD_IMPORT, event=event)
|
||||
elif row[1] == "Variables":
|
||||
# Add a new variable block
|
||||
self.handle_action(None, self.ADD_VARIABLE, event=event)
|
||||
self.handle_action(
|
||||
None, self.ADD_VARIABLE, event=event)
|
||||
else:
|
||||
self.handle_action(None, self.DELETE_CONFIRM, event=event)
|
||||
self.handle_action(
|
||||
None, self.DELETE_CONFIRM, event=event)
|
||||
return True
|
||||
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
|
||||
if self._block:
|
||||
self._context_menu.update_sensitive(True, enabled=self._block.enabled)
|
||||
self._context_menu.update_sensitive(
|
||||
True, enabled=self._block.enabled)
|
||||
else:
|
||||
self._context_menu.update_sensitive(False)
|
||||
self._context_menu.popup(None, None, None, None, event.button, event.time)
|
||||
self._context_menu.popup(
|
||||
None, None, None, None, event.button, event.time)
|
||||
|
||||
# Null handler. Stops the treeview from handling double click events.
|
||||
if event.type == Gdk.EventType._2BUTTON_PRESS:
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -64,7 +64,8 @@ class Block(CoreBlock, Drawable):
|
||||
"""
|
||||
coor = Utils.scale(coor, reverse=True)
|
||||
if Actions.TOGGLE_SNAP_TO_GRID.get_active():
|
||||
offset_x, offset_y = (0, self.height / 2) if self.is_horizontal() else (self.height / 2, 0)
|
||||
offset_x, offset_y = (
|
||||
0, self.height / 2) if self.is_horizontal() else (self.height / 2, 0)
|
||||
coor = (
|
||||
Utils.align_to_grid(coor[0] + offset_x) - offset_x,
|
||||
Utils.align_to_grid(coor[1] + offset_y) - offset_y
|
||||
@ -137,8 +138,10 @@ class Block(CoreBlock, Drawable):
|
||||
for ports, has_busses in zip((self.active_sources, self.active_sinks), bussified):
|
||||
if not ports:
|
||||
continue
|
||||
port_separation = Constants.PORT_SEPARATION if not has_busses else ports[0].height + Constants.PORT_SPACING
|
||||
offset = (self.height - (len(ports) - 1) * port_separation - ports[0].height) / 2
|
||||
port_separation = Constants.PORT_SEPARATION if not has_busses else ports[
|
||||
0].height + Constants.PORT_SPACING
|
||||
offset = (self.height - (len(ports) - 1) *
|
||||
port_separation - ports[0].height) / 2
|
||||
for port in ports:
|
||||
port.create_shapes()
|
||||
port.coordinate = {
|
||||
@ -148,7 +151,8 @@ class Block(CoreBlock, Drawable):
|
||||
270: (offset, +self.width),
|
||||
}[port.connector_direction]
|
||||
|
||||
offset += Constants.PORT_SEPARATION if not has_busses else port.height + Constants.PORT_SPACING
|
||||
offset += Constants.PORT_SEPARATION if not has_busses else port.height + \
|
||||
Constants.PORT_SPACING
|
||||
|
||||
def create_labels(self, cr=None):
|
||||
"""Create the labels for the signal block."""
|
||||
@ -178,7 +182,8 @@ class Block(CoreBlock, Drawable):
|
||||
markups = [param.format_block_surface_markup()
|
||||
for param in self.params.values() if (param.hide not in ('all', 'part') or (param.dtype == 'id' and force_show_id))]
|
||||
else:
|
||||
markups = ['<span font_desc="{font}"><b>key: </b>{key}</span>'.format(font=Constants.PARAM_FONT, key=self.key)]
|
||||
markups = ['<span font_desc="{font}"><b>key: </b>{key}</span>'.format(
|
||||
font=Constants.PARAM_FONT, key=self.key)]
|
||||
|
||||
params_layout.set_spacing(Constants.LABEL_SEPARATION * Pango.SCALE)
|
||||
params_layout.set_markup('\n'.join(markups))
|
||||
@ -197,12 +202,13 @@ class Block(CoreBlock, Drawable):
|
||||
self.create_port_labels()
|
||||
|
||||
def get_min_height_for_ports(ports):
|
||||
min_height = 2 * Constants.PORT_BORDER_SEPARATION + len(ports) * Constants.PORT_SEPARATION
|
||||
min_height = 2 * Constants.PORT_BORDER_SEPARATION + \
|
||||
len(ports) * Constants.PORT_SEPARATION
|
||||
# If any of the ports are bus ports - make the min height larger
|
||||
if any([p.dtype == 'bus' for p in ports]):
|
||||
min_height = 2 * Constants.PORT_BORDER_SEPARATION + sum(
|
||||
port.height + Constants.PORT_SPACING for port in ports if port.dtype == 'bus'
|
||||
) - Constants.PORT_SPACING
|
||||
) - Constants.PORT_SPACING
|
||||
|
||||
else:
|
||||
if ports:
|
||||
@ -213,11 +219,13 @@ class Block(CoreBlock, Drawable):
|
||||
get_min_height_for_ports(self.active_sinks),
|
||||
get_min_height_for_ports(self.active_sources))
|
||||
|
||||
self.width, self.height = width, height = Utils.align_to_grid((width, height))
|
||||
self.width, self.height = width, height = Utils.align_to_grid(
|
||||
(width, height))
|
||||
|
||||
self._surface_layouts_offsets = [
|
||||
(0, (height - label_height) / 2.0),
|
||||
(0, (height - label_height) / 2.0 + Constants.LABEL_SEPARATION + title_height / Pango.SCALE)
|
||||
(0, (height - label_height) / 2.0 +
|
||||
Constants.LABEL_SEPARATION + title_height / Pango.SCALE)
|
||||
]
|
||||
|
||||
title_layout.set_width(width * Pango.SCALE)
|
||||
@ -243,7 +251,8 @@ class Block(CoreBlock, Drawable):
|
||||
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=Constants.BLOCK_FONT)
|
||||
'<b>Complexity: {num}bal</b></span>'.format(
|
||||
num=Utils.num_to_str(complexity), font=Constants.BLOCK_FONT)
|
||||
)
|
||||
comment = self.comment # Returns None if there are no comments
|
||||
if comment:
|
||||
@ -303,7 +312,8 @@ class Block(CoreBlock, Drawable):
|
||||
for port in self.active_ports():
|
||||
port_selected = port.what_is_selected(
|
||||
coor=[a - b for a, b in zip(coor, self.coordinate)],
|
||||
coor_m=[a - b for a, b in zip(coor, self.coordinate)] if coor_m is not None else None
|
||||
coor_m=[
|
||||
a - b for a, b in zip(coor, self.coordinate)] if coor_m is not None else None
|
||||
)
|
||||
if port_selected:
|
||||
return port_selected
|
||||
@ -359,7 +369,8 @@ class Block(CoreBlock, Drawable):
|
||||
true for change
|
||||
"""
|
||||
type_templates = ' '.join(p.dtype for p in self.params.values()) + ' '
|
||||
type_templates += ' '.join(p.get_raw('dtype') for p in (self.sinks + self.sources))
|
||||
type_templates += ' '.join(p.get_raw('dtype')
|
||||
for p in (self.sinks + self.sources))
|
||||
type_param = None
|
||||
for key, param in self.params.items():
|
||||
if not param.is_enum():
|
||||
@ -396,7 +407,8 @@ class Block(CoreBlock, Drawable):
|
||||
"""
|
||||
changed = False
|
||||
# Concat the nports string from the private nports settings of all ports
|
||||
nports_str = ' '.join(str(port.get_raw('multiplicity')) for port in self.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 self.params.items():
|
||||
if param.is_enum() or param.key not in nports_str:
|
||||
|
||||
@ -25,6 +25,7 @@ def get_color(color_code):
|
||||
# fg colors
|
||||
#################################################################################
|
||||
|
||||
|
||||
HIGHLIGHT_COLOR = get_color('#00FFFF')
|
||||
BORDER_COLOR = get_color('#616161')
|
||||
BORDER_COLOR_DISABLED = get_color('#888888')
|
||||
@ -62,8 +63,10 @@ DEFAULT_DOMAIN_COLOR = get_color('#777777')
|
||||
# port colors
|
||||
#################################################################################
|
||||
|
||||
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())
|
||||
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())
|
||||
|
||||
|
||||
#################################################################################
|
||||
@ -109,4 +112,3 @@ LIGHT_THEME_STYLES = b"""
|
||||
|
||||
#enum_custom { background-color: #EEEEEE; }
|
||||
"""
|
||||
|
||||
|
||||
@ -73,11 +73,13 @@ class Connection(CoreConnection, Drawable):
|
||||
|
||||
# first two components relative to source connector, rest relative to sink connector
|
||||
self._rel_points = [
|
||||
rotate((15, 0), source.rotation), # line from 0,0 to here, bezier curve start
|
||||
# line from 0,0 to here, bezier curve start
|
||||
rotate((15, 0), source.rotation),
|
||||
rotate((50, 0), source.rotation), # bezier curve control point 1
|
||||
rotate((-50, 0), sink.rotation), # bezier curve control point 2
|
||||
rotate((-15, 0), sink.rotation), # bezier curve end
|
||||
rotate((-CONNECTOR_ARROW_HEIGHT, 0), sink.rotation), # line to arrow head
|
||||
rotate((-CONNECTOR_ARROW_HEIGHT, 0),
|
||||
sink.rotation), # line to arrow head
|
||||
]
|
||||
self._current_coordinates = None # triggers _make_path()
|
||||
|
||||
@ -110,7 +112,8 @@ class Connection(CoreConnection, Drawable):
|
||||
|
||||
# make rel_point all relative to source connector
|
||||
p0 = 0, 0 # x_start - x_pos, y_start - y_pos
|
||||
p1, p2, (dx_e1, dy_e1), (dx_e2, dy_e2), (dx_e3, dy_e3) = self._rel_points
|
||||
p1, p2, (dx_e1, dy_e1), (dx_e2, dy_e2), (dx_e3,
|
||||
dy_e3) = self._rel_points
|
||||
p3 = x_e + dx_e1, y_e + dy_e1
|
||||
p4 = x_e + dx_e2, y_e + dy_e2
|
||||
p5 = x_e + dx_e3, y_e + dy_e3
|
||||
@ -137,7 +140,8 @@ class Connection(CoreConnection, Drawable):
|
||||
self.create_shapes() # triggers _make_path() call below
|
||||
self._current_port_rotations = port_rotations
|
||||
|
||||
new_coordinates = (source.parent_block.coordinate, sink.parent_block.coordinate)
|
||||
new_coordinates = (source.parent_block.coordinate,
|
||||
sink.parent_block.coordinate)
|
||||
if self._current_coordinates != new_coordinates:
|
||||
self._make_path(cr)
|
||||
self._current_coordinates = new_coordinates
|
||||
@ -175,7 +179,7 @@ class Connection(CoreConnection, Drawable):
|
||||
cr.set_source_rgba(*color2)
|
||||
cr.rotate(self._arrow_rotation)
|
||||
cr.rel_move_to(CONNECTOR_ARROW_HEIGHT, 0)
|
||||
cr.rel_line_to(-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE/2)
|
||||
cr.rel_line_to(-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE / 2)
|
||||
cr.rel_line_to(0, CONNECTOR_ARROW_BASE)
|
||||
cr.close_path()
|
||||
cr.fill()
|
||||
@ -238,4 +242,5 @@ class DummyCoreConnection(object):
|
||||
def has_real_sink(self):
|
||||
return self.sink_port is not self._dummy_port
|
||||
|
||||
|
||||
DummyConnection = Connection.make_cls_with_base(DummyCoreConnection)
|
||||
|
||||
@ -125,7 +125,8 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
editor.open_editor()
|
||||
except Exception as e:
|
||||
# Problem launching the editor. Need to select a new editor.
|
||||
Messages.send('>>> Error opening an external editor. Please select a different editor.\n')
|
||||
Messages.send(
|
||||
'>>> Error opening an external editor. Please select a different editor.\n')
|
||||
# Reset the editor to force the user to select a new one.
|
||||
self.parent_platform.config.editor = ''
|
||||
|
||||
@ -153,10 +154,13 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
# calculate the position coordinate
|
||||
h_adj = scroll_pane.get_hadjustment()
|
||||
v_adj = scroll_pane.get_vadjustment()
|
||||
if coor is None: coor = (
|
||||
int(random.uniform(.25, .75)*h_adj.get_page_size() + h_adj.get_value()),
|
||||
int(random.uniform(.25, .75)*v_adj.get_page_size() + v_adj.get_value()),
|
||||
)
|
||||
if coor is None:
|
||||
coor = (
|
||||
int(random.uniform(.25, .75) *
|
||||
h_adj.get_page_size() + h_adj.get_value()),
|
||||
int(random.uniform(.25, .75) *
|
||||
v_adj.get_page_size() + v_adj.get_value()),
|
||||
)
|
||||
# get the new block
|
||||
block = self.new_block(key)
|
||||
block.coordinate = coor
|
||||
@ -218,17 +222,17 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
Returns:
|
||||
the clipboard
|
||||
"""
|
||||
#get selected blocks
|
||||
# get selected blocks
|
||||
blocks = list(self.selected_blocks())
|
||||
if not blocks:
|
||||
return None
|
||||
#calc x and y min
|
||||
# calc x and y min
|
||||
x_min, y_min = blocks[0].coordinate
|
||||
for block in blocks:
|
||||
x, y = block.coordinate
|
||||
x_min = min(x, x_min)
|
||||
y_min = min(y, y_min)
|
||||
#get connections between selected blocks
|
||||
# get connections between selected blocks
|
||||
connections = list(filter(
|
||||
lambda c: c.source_block in blocks and c.sink_block in blocks,
|
||||
self.connections,
|
||||
@ -281,7 +285,8 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
block.move((x_off, y_off))
|
||||
while any(Utils.align_to_grid(block.coordinate) == Utils.align_to_grid(other.coordinate)
|
||||
for other in self.blocks if other is not block):
|
||||
block.move((Constants.CANVAS_GRID_SIZE, Constants.CANVAS_GRID_SIZE))
|
||||
block.move((Constants.CANVAS_GRID_SIZE,
|
||||
Constants.CANVAS_GRID_SIZE))
|
||||
# shift all following blocks
|
||||
x_off += Constants.CANVAS_GRID_SIZE
|
||||
y_off += Constants.CANVAS_GRID_SIZE
|
||||
@ -353,15 +358,16 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
if not blocks:
|
||||
return
|
||||
|
||||
min_x, min_y = self.selected_block.coordinate
|
||||
min_x, min_y = self.selected_block.coordinate
|
||||
for selected_block in blocks:
|
||||
x, y = selected_block.coordinate
|
||||
min_x, min_y = min(min_x, x), min(min_y, y)
|
||||
|
||||
# Sanitize delta_coordinate so that blocks don't move to negative coordinate
|
||||
delta_coordinate = max(delta_coordinate[0],-min_x), max(delta_coordinate[1], -min_y)
|
||||
delta_coordinate = max(
|
||||
delta_coordinate[0], -min_x), max(delta_coordinate[1], -min_y)
|
||||
|
||||
# Move selected blocks
|
||||
# Move selected blocks
|
||||
for selected_block in blocks:
|
||||
selected_block.move(delta_coordinate)
|
||||
self.element_moved = True
|
||||
@ -388,15 +394,15 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
x += selected_block.width
|
||||
y += selected_block.height
|
||||
max_x, max_y = max(max_x, x), max(max_y, y)
|
||||
ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2
|
||||
ctr_x, ctr_y = (max_x + min_x) / 2, (max_y + min_y) / 2
|
||||
|
||||
# align the blocks as requested
|
||||
transform = {
|
||||
Actions.BLOCK_VALIGN_TOP: lambda x, y, w, h: (x, min_y),
|
||||
Actions.BLOCK_VALIGN_MIDDLE: lambda x, y, w, h: (x, ctr_y - h/2),
|
||||
Actions.BLOCK_VALIGN_MIDDLE: lambda x, y, w, h: (x, ctr_y - h / 2),
|
||||
Actions.BLOCK_VALIGN_BOTTOM: lambda x, y, w, h: (x, max_y - h),
|
||||
Actions.BLOCK_HALIGN_LEFT: lambda x, y, w, h: (min_x, y),
|
||||
Actions.BLOCK_HALIGN_CENTER: lambda x, y, w, h: (ctr_x-w/2, y),
|
||||
Actions.BLOCK_HALIGN_CENTER: lambda x, y, w, h: (ctr_x - w / 2, y),
|
||||
Actions.BLOCK_HALIGN_RIGHT: lambda x, y, w, h: (max_x - w, y),
|
||||
}.get(calling_action, lambda *args: args)
|
||||
|
||||
@ -419,21 +425,22 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
"""
|
||||
if not any(self.selected_blocks()):
|
||||
return False
|
||||
#initialize min and max coordinates
|
||||
# initialize min and max coordinates
|
||||
min_x, min_y = max_x, max_y = self.selected_block.coordinate
|
||||
# rotate each selected block, and find min/max coordinate
|
||||
for selected_block in self.selected_blocks():
|
||||
selected_block.rotate(rotation)
|
||||
#update the min/max coordinate
|
||||
# update the min/max coordinate
|
||||
x, y = selected_block.coordinate
|
||||
min_x, min_y = min(min_x, x), min(min_y, y)
|
||||
max_x, max_y = max(max_x, x), max(max_y, y)
|
||||
#calculate center point of selected blocks
|
||||
ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2
|
||||
#rotate the blocks around the center point
|
||||
# calculate center point of selected blocks
|
||||
ctr_x, ctr_y = (max_x + min_x) / 2, (max_y + min_y) / 2
|
||||
# rotate the blocks around the center point
|
||||
for selected_block in self.selected_blocks():
|
||||
x, y = selected_block.coordinate
|
||||
x, y = Utils.get_rotated_coordinate((x - ctr_x, y - ctr_y), rotation)
|
||||
x, y = Utils.get_rotated_coordinate(
|
||||
(x - ctr_x, y - ctr_y), rotation)
|
||||
selected_block.coordinate = (x + ctr_x, y + ctr_y)
|
||||
return True
|
||||
|
||||
@ -496,7 +503,7 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
element.create_labels(cr)
|
||||
|
||||
def create_shapes(self):
|
||||
#TODO - this is a workaround for bus ports not having a proper coordinate
|
||||
# TODO - this is a workaround for bus ports not having a proper coordinate
|
||||
# until the shape is drawn. The workaround is to draw blocks before connections
|
||||
|
||||
for element in filter(lambda x: x.is_block, self._elements_to_draw):
|
||||
@ -582,7 +589,8 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
|
||||
else: # called from a mouse release
|
||||
if not self.element_moved and (not self.selected_elements or self.drawing_area.ctrl_mask) and not self._new_connection:
|
||||
selected_elements = self.what_is_selected(self.coordinate, self.press_coor)
|
||||
selected_elements = self.what_is_selected(
|
||||
self.coordinate, self.press_coor)
|
||||
|
||||
# this selection and the last were ports, try to connect them
|
||||
if self.make_connection():
|
||||
@ -635,7 +643,8 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
|
||||
if selected_port and selected_port.is_source:
|
||||
selected.remove(selected_port.parent_block)
|
||||
self._new_connection = DummyConnection(selected_port, coordinate=coor)
|
||||
self._new_connection = DummyConnection(
|
||||
selected_port, coordinate=coor)
|
||||
self.drawing_area.queue_draw()
|
||||
# update selected ports
|
||||
if selected_port is not self._new_selected_port:
|
||||
@ -806,19 +815,21 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
dX, dY = x - X, y - Y
|
||||
|
||||
if Actions.TOGGLE_SNAP_TO_GRID.get_active() or self.drawing_area.mod1_mask:
|
||||
dX, dY = int(round(dX / Constants.CANVAS_GRID_SIZE)), int(round(dY / Constants.CANVAS_GRID_SIZE))
|
||||
dX, dY = int(round(dX / Constants.CANVAS_GRID_SIZE)
|
||||
), int(round(dY / Constants.CANVAS_GRID_SIZE))
|
||||
dX, dY = dX * Constants.CANVAS_GRID_SIZE, dY * Constants.CANVAS_GRID_SIZE
|
||||
else:
|
||||
dX, dY = int(round(dX)), int(round(dY))
|
||||
|
||||
if dX != 0 or dY != 0:
|
||||
self.move_selected((dX, dY))
|
||||
self.coordinate = (X+dX, Y+dY)
|
||||
self.coordinate = (X + dX, Y + dY)
|
||||
redraw = True
|
||||
return redraw
|
||||
|
||||
def get_extents(self):
|
||||
show_comments = Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active()
|
||||
|
||||
def sub_extents():
|
||||
for element in self._elements_to_draw:
|
||||
yield element.get_extents()
|
||||
@ -828,5 +839,6 @@ class FlowGraph(CoreFlowgraph, Drawable):
|
||||
extent = 10000000, 10000000, 0, 0
|
||||
cmps = (min, min, max, max)
|
||||
for sub_extent in sub_extents():
|
||||
extent = [cmp(xy, e_xy) for cmp, xy, e_xy in zip(cmps, extent, sub_extent)]
|
||||
extent = [cmp(xy, e_xy)
|
||||
for cmp, xy, e_xy in zip(cmps, extent, sub_extent)]
|
||||
return tuple(extent)
|
||||
|
||||
@ -86,20 +86,20 @@ class Param(CoreParam):
|
||||
tooltip_lines.extend(' * ' + msg for msg in errors)
|
||||
return '\n'.join(tooltip_lines)
|
||||
|
||||
|
||||
|
||||
##################################################
|
||||
# Truncate helper method
|
||||
##################################################
|
||||
|
||||
def truncate(self, string, style=0):
|
||||
max_len = max(27 - len(self.name), 3)
|
||||
if len(string) > max_len:
|
||||
if style < 0: # Front truncate
|
||||
string = '...' + string[3-max_len:]
|
||||
string = '...' + string[3 - max_len:]
|
||||
elif style == 0: # Center truncate
|
||||
string = string[:max_len//2 - 3] + '...' + string[-max_len//2:]
|
||||
string = string[:max_len // 2 - 3] + \
|
||||
'...' + string[-max_len // 2:]
|
||||
elif style > 0: # Rear truncate
|
||||
string = string[:max_len-3] + '...'
|
||||
string = string[:max_len - 3] + '...'
|
||||
return string
|
||||
|
||||
def pretty_print(self):
|
||||
@ -183,5 +183,5 @@ class Param(CoreParam):
|
||||
display_value = expr_string
|
||||
|
||||
return '<span {foreground} font_desc="{font}"><b>{label}:</b> {value}</span>'.format(
|
||||
foreground='foreground="red"' if not self.is_valid() else '', font=Constants.PARAM_FONT,
|
||||
foreground='foreground="red"' if not self.is_valid() else '', font=Constants.PARAM_FONT,
|
||||
label=Utils.encode(self.name), value=display_value)
|
||||
|
||||
@ -68,9 +68,11 @@ class Port(CorePort, Drawable):
|
||||
color = colors.PORT_TYPE_TO_COLOR.get('message')
|
||||
else:
|
||||
self._font_color[-1] = 1.0
|
||||
color = colors.PORT_TYPE_TO_COLOR.get(self.dtype) or colors.PORT_TYPE_TO_COLOR.get('')
|
||||
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)]
|
||||
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)
|
||||
@ -84,8 +86,8 @@ class Port(CorePort, Drawable):
|
||||
self.bounds_from_area(self._area)
|
||||
|
||||
self._connector_coordinate = {
|
||||
0: (self.width, self.height / 2),
|
||||
90: (self.height / 2, 0),
|
||||
0: (self.width, self.height / 2),
|
||||
90: (self.height / 2, 0),
|
||||
180: (0, self.height / 2),
|
||||
270: (self.height / 2, self.width)
|
||||
}[self.connector_direction]
|
||||
@ -112,7 +114,8 @@ class Port(CorePort, Drawable):
|
||||
label_width, label_height = self.label_layout.get_size()
|
||||
|
||||
self.width = 2 * Constants.PORT_LABEL_PADDING + label_width / Pango.SCALE
|
||||
self.height = (2 * Constants.PORT_LABEL_PADDING + label_height*(3 if self.dtype == 'bus' else 1)) / Pango.SCALE
|
||||
self.height = (2 * Constants.PORT_LABEL_PADDING + label_height *
|
||||
(3 if self.dtype == 'bus' else 1)) / Pango.SCALE
|
||||
self._label_layout_offsets = [0, Constants.PORT_LABEL_PADDING]
|
||||
|
||||
self.height += self.height % 2 # uneven height
|
||||
|
||||
12
main.py
12
main.py
@ -4,12 +4,14 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
|
||||
import argparse, logging, sys
|
||||
from gi.repository import Gtk
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('PangoCairo', '1.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
VERSION_AND_DISCLAIMER_TEMPLATE = """\
|
||||
@ -34,7 +36,8 @@ def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version())
|
||||
parser.add_argument('flow_graphs', nargs='*')
|
||||
parser.add_argument('--log', choices=['debug', 'info', 'warning', 'error', 'critical'], default='warning')
|
||||
parser.add_argument(
|
||||
'--log', choices=['debug', 'info', 'warning', 'error', 'critical'], default='warning')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Enable logging
|
||||
@ -73,7 +76,8 @@ def main():
|
||||
log.debug("Loading platform")
|
||||
platform = Platform(
|
||||
version=gr.version(),
|
||||
version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()),
|
||||
version_parts=(gr.major_version(), gr.api_version(),
|
||||
gr.minor_version()),
|
||||
prefs=gr.prefs(),
|
||||
install_prefix=gr.prefix()
|
||||
)
|
||||
|
||||
@ -47,6 +47,7 @@ def die(error, message):
|
||||
print("The original error message follows.", file=sys.stderr)
|
||||
sys.exit(type(error).__name__ + '\n\n' + msg)
|
||||
|
||||
|
||||
def check_gtk():
|
||||
try:
|
||||
import gi
|
||||
|
||||
@ -37,9 +37,11 @@ def test_list():
|
||||
|
||||
def test_parse_error():
|
||||
with pytest.raises(TemplateError):
|
||||
MakoTemplates(_bind_to=Block(num='123'), test='abc${num NOT CLOSING').render('test')
|
||||
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')
|
||||
MakoTemplates(_bind_to=Block(num='123'),
|
||||
test='abc${ WRONG_VAR }').render('test')
|
||||
|
||||
@ -39,7 +39,8 @@ def test_conditional():
|
||||
|
||||
|
||||
def test_simple_format_string():
|
||||
convert = functools.partial(parser.Converter(names={'abc'}).convert_simple, spec=parser.FormatString)
|
||||
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)')
|
||||
@ -50,7 +51,8 @@ def test_simple_format_string():
|
||||
|
||||
def test_hard_format_string():
|
||||
names = {'abc': {'ff'}, 'param1': {}, 'param2': {}}
|
||||
convert = functools.partial(parser.Converter(names).convert_hard, spec=parser.FormatString)
|
||||
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)')
|
||||
|
||||
@ -66,7 +68,8 @@ def test_opts():
|
||||
|
||||
|
||||
def test_nested():
|
||||
assert 'abc(abc) abc + abc abc[abc]' == c2p('$abc($abc) $(abc + $abc) ${abc[$abc]}')
|
||||
assert 'abc(abc) abc + abc abc[abc]' == c2p(
|
||||
'$abc($abc) $(abc + $abc) ${abc[$abc]}')
|
||||
assert '(abc_abc_)' == c2p('(abc_$(abc)_)')
|
||||
|
||||
|
||||
|
||||
@ -13,11 +13,13 @@ import tempfile
|
||||
|
||||
from grc.compiler import main
|
||||
|
||||
|
||||
def test_compiler(capsys):
|
||||
args = Namespace(
|
||||
output=tempfile.gettempdir(),
|
||||
user_lib_dir=False,
|
||||
grc_files=[path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')],
|
||||
grc_files=[path.join(path.dirname(__file__),
|
||||
'resources', 'test_compiler.grc')],
|
||||
run=True
|
||||
)
|
||||
|
||||
|
||||
@ -13,11 +13,13 @@ import tempfile
|
||||
|
||||
from grc.compiler import main
|
||||
|
||||
|
||||
def test_cpp(capsys):
|
||||
args = Namespace(
|
||||
output=tempfile.gettempdir(),
|
||||
user_lib_dir=False,
|
||||
grc_files=[path.join(path.dirname(__file__), 'resources', 'test_cpp.grc')],
|
||||
grc_files=[path.join(path.dirname(__file__),
|
||||
'resources', 'test_cpp.grc')],
|
||||
run=True
|
||||
)
|
||||
|
||||
|
||||
@ -14,12 +14,14 @@ except FileExistsError:
|
||||
# Gather blocks
|
||||
BLOCK_PATHS = []
|
||||
ROOT = path.join(path.dirname(__file__), '../..')
|
||||
BLOCK_PATHS = [path.join(path.dirname(__file__), '../../grc/blocks'), '../../build/gr-uhd/grc']
|
||||
BLOCK_PATHS = [path.join(path.dirname(
|
||||
__file__), '../../grc/blocks'), '../../build/gr-uhd/grc']
|
||||
for file_dir in os.scandir(ROOT):
|
||||
# If it is a module
|
||||
if path.isdir(file_dir) and file_dir.name.startswith("gr-"):
|
||||
BLOCK_PATHS.append(path.join(file_dir, "grc"))
|
||||
|
||||
|
||||
def gather_examples():
|
||||
global ROOT
|
||||
example_paths = []
|
||||
@ -39,11 +41,13 @@ def gather_examples():
|
||||
continue
|
||||
return example_paths
|
||||
|
||||
|
||||
def print_proper(element):
|
||||
if element.is_block:
|
||||
return element.name
|
||||
return f"{element.parent.name} - {element}"
|
||||
|
||||
|
||||
@pytest.mark.examples
|
||||
@pytest.mark.parametrize("example", gather_examples())
|
||||
def test_all_examples(example):
|
||||
@ -62,7 +66,9 @@ def test_all_examples(example):
|
||||
flow_graph.rewrite()
|
||||
flow_graph.validate()
|
||||
|
||||
assert flow_graph.is_valid(), (example.name, [f"{print_proper(elem)}: {msg}" for elem, msg in flow_graph.iter_error_messages()])
|
||||
assert flow_graph.is_valid(), (example.name, [
|
||||
f"{print_proper(elem)}: {msg}" for elem, msg in flow_graph.iter_error_messages()])
|
||||
|
||||
generator = platform.Generator(flow_graph, path.join(path.dirname(__file__), 'resources/tests'))
|
||||
generator = platform.Generator(flow_graph, path.join(
|
||||
path.dirname(__file__), 'resources/tests'))
|
||||
generator.write()
|
||||
|
||||
@ -15,7 +15,8 @@ from grc.core.platform import Platform
|
||||
|
||||
def test_generator():
|
||||
# c&p form compiler code.
|
||||
grc_file = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
|
||||
grc_file = path.join(path.dirname(__file__),
|
||||
'resources', 'test_compiler.grc')
|
||||
out_dir = tempfile.gettempdir()
|
||||
block_paths = [
|
||||
path.join(path.dirname(__file__), '../../grc/blocks'),
|
||||
@ -35,5 +36,6 @@ def test_generator():
|
||||
|
||||
assert flow_graph.is_valid()
|
||||
|
||||
generator = platform.Generator(flow_graph, path.join(path.dirname(__file__), 'resources'))
|
||||
generator = platform.Generator(
|
||||
flow_graph, path.join(path.dirname(__file__), 'resources'))
|
||||
generator.write()
|
||||
|
||||
@ -11,7 +11,8 @@ from grc.converter import flow_graph
|
||||
|
||||
|
||||
def test_flow_graph_converter():
|
||||
filename = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
|
||||
filename = path.join(path.dirname(__file__),
|
||||
'resources', 'test_compiler.grc')
|
||||
|
||||
data = flow_graph.from_xml(filename)
|
||||
|
||||
@ -19,7 +20,8 @@ def test_flow_graph_converter():
|
||||
|
||||
|
||||
def test_flow_graph_converter_with_fp():
|
||||
filename = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
|
||||
filename = path.join(path.dirname(__file__),
|
||||
'resources', 'test_compiler.grc')
|
||||
|
||||
with open(filename, 'rb') as fp:
|
||||
data = flow_graph.from_xml(fp)
|
||||
|
||||
@ -58,7 +58,8 @@ def test_min():
|
||||
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'")]
|
||||
assert checker.messages == [
|
||||
('block', 'warn', "Ignoring extra key 'abcdefg'")]
|
||||
|
||||
|
||||
def test_checker():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user