diff options
Diffstat (limited to 'grc')
37 files changed, 731 insertions, 382 deletions
diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 4c782a7f7d..eed5202657 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -54,16 +54,6 @@ GR_REGISTER_COMPONENT("gnuradio-companion" ENABLE_GRC if(ENABLE_GRC) ######################################################################## -# Setup CPack components -######################################################################## -include(GrPackage) -CPACK_COMPONENT("grc" - DISPLAY_NAME "GNU Radio Companion" - DESCRIPTION "Graphical flow graph designer" - DEPENDS "runtime_python" -) - -######################################################################## # Create and install the grc conf file ######################################################################## file(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${GRC_BLOCKS_DIR} blocksdir) @@ -93,7 +83,6 @@ configure_file( install( FILES ${CMAKE_CURRENT_BINARY_DIR}/grc.conf DESTINATION ${GR_PREFSDIR} - COMPONENT "grc" ) file(GLOB py_files "*.py") @@ -101,7 +90,6 @@ file(GLOB py_files "*.py") GR_PYTHON_INSTALL( FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc - COMPONENT "grc" ) ######################################################################## @@ -118,18 +106,6 @@ string(REPLACE "\\" "\\\\" GRC_BLOCKS_PATH ${GRC_BLOCKS_PATH}) file(TO_NATIVE_PATH ${GR_PYTHON_DIR} GR_PYTHON_POSTFIX) string(REPLACE "\\" "\\\\" GR_PYTHON_POSTFIX ${GR_PYTHON_POSTFIX}) -CPACK_SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} - #!include \\\"winmessages.nsh\\\" - WriteRegStr HKLM ${HLKM_ENV} \\\"GRC_BLOCKS_PATH\\\" \\\"$INSTDIR\\\\${GRC_BLOCKS_PATH}\\\" - SendMessage \\\${HWND_BROADCAST} \\\${WM_WININICHANGE} 0 \\\"STR:Environment\\\" /TIMEOUT=5000 -") - -CPACK_SET(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} - #!include \\\"winmessages.nsh\\\" - DeleteRegValue HKLM ${HLKM_ENV} \\\"GRC_BLOCKS_PATH\\\" - SendMessage \\\${HWND_BROADCAST} \\\${WM_WININICHANGE} 0 \\\"STR:Environment\\\" /TIMEOUT=5000 -") - endif(WIN32) ######################################################################## diff --git a/grc/blocks/CMakeLists.txt b/grc/blocks/CMakeLists.txt index 0c2a1f7901..d46b1febbe 100644 --- a/grc/blocks/CMakeLists.txt +++ b/grc/blocks/CMakeLists.txt @@ -22,6 +22,19 @@ include(GrPython) file(GLOB xml_files "*.xml") +macro(REPLACE_IN_FILE _xml_block match replace) + set(xml_block_src "${CMAKE_CURRENT_SOURCE_DIR}/${_xml_block}") + set(xml_block "${CMAKE_CURRENT_BINARY_DIR}/${_xml_block}") + + list(REMOVE_ITEM xml_files "${xml_block_src}") + file(READ "${xml_block_src}" xml_block_src_text) + string(REPLACE "${match}" "${replace}" + xml_block_text "${xml_block_src_text}") + file(WRITE "${xml_block}" "${xml_block_text}") + + list(APPEND generated_xml_files "${xml_block}") +endmacro() + macro(GEN_BLOCK_XML _generator _xml_block) set(generator ${CMAKE_CURRENT_SOURCE_DIR}/${_generator}) set(xml_block ${CMAKE_CURRENT_BINARY_DIR}/${_xml_block}) @@ -30,14 +43,17 @@ macro(GEN_BLOCK_XML _generator _xml_block) DEPENDS ${generator} OUTPUT ${xml_block} COMMAND ${PYTHON_EXECUTABLE} ${generator} ${xml_block} ) -endmacro(GEN_BLOCK_XML) +endmacro() + +GEN_BLOCK_XML(variable_struct.xml.py variable_struct.xml) -GEN_BLOCK_XML(variable_struct.xml.py variable_struct.xml) +if(DESIRED_QT_VERSION EQUAL 4) + REPLACE_IN_FILE(options.xml PyQt5 PyQt4) +endif() add_custom_target(grc_generated_xml ALL DEPENDS ${generated_xml_files}) install( FILES ${xml_files} ${generated_xml_files} DESTINATION ${GRC_BLOCKS_DIR} - COMPONENT "grc" ) diff --git a/grc/blocks/options.xml b/grc/blocks/options.xml index 1dee986c5c..252a0b2e2d 100644 --- a/grc/blocks/options.xml +++ b/grc/blocks/options.xml @@ -11,17 +11,13 @@ <key>options</key> <import>from gnuradio import gr</import> <import>from gnuradio.filter import firdes</import> - <import>#if $generate_options() == 'wx_gui' -from grc_gnuradio import wxgui as grc_wxgui -import wx -#end if -#if $generate_options() == 'qt_gui' -from PyQt4 import Qt + <import>#if $generate_options() == 'qt_gui' +from PyQt5 import Qt import sys #end if #if not $generate_options().startswith('hb') -from optparse import OptionParser -from gnuradio.eng_option import eng_option +from argparse import ArgumentParser +from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation #end if</import> <make></make> @@ -65,10 +61,6 @@ else: self.stop(); self.wait()</callback> <key>qt_gui</key> </option> <option> - <name>WX GUI</name> - <key>wx_gui</key> - </option> - <option> <name>No GUI</name> <key>no_gui</key> </option> @@ -108,17 +100,7 @@ else: self.stop(); self.wait()</callback> <key>run</key> <value>True</value> <type>bool</type> - <hide> -#if $generate_options() in ('qt_gui', 'wx_gui') - #if $run() - part - #else - none - #end if -#else - all -#end if - </hide> + <hide>#if $generate_options() == 'qt_gui' then ('part' if $run() else 'none') else 'all'#</hide> <option> <name>Autostart</name> <key>True</key> @@ -133,26 +115,14 @@ else: self.stop(); self.wait()</callback> <key>max_nouts</key> <value>0</value> <type>int</type> - <hide>#if $generate_options().startswith('hb') -all#slurp -#elif $max_nouts() -none#slurp -#else -part#slurp -#end if</hide> + <hide>#if $generate_options().startswith('hb') then 'all' else ('none' if $max_nouts() else 'part')#</hide> </param> <param> <name>Realtime Scheduling</name> <key>realtime_scheduling</key> <value></value> <type>enum</type> - <hide>#if $generate_options().startswith('hb') -all#slurp -#elif $realtime_scheduling() -none#slurp -#else -part#slurp -#end if</hide> + <hide>#if $generate_options().startswith('hb') then 'all' else ('none' if $realtime_scheduling() else 'part')#</hide> <option> <name>Off</name> <key></key> @@ -167,17 +137,7 @@ part#slurp <key>qt_qss_theme</key> <value></value> <type>file_open</type> - <hide> -#if $generate_options() in ('qt_gui',) - #if $qt_qss_theme() - none - #else - part - #end if -#else - all -#end if -</hide> + <hide>#if $generate_options() == 'qt_gui' then ('none' if $qt_qss_theme() else 'part') else 'all'#</hide> </param> <param> <name>Thread-safe setters</name> @@ -200,11 +160,7 @@ part#slurp <key>run_command</key> <value>{python} -u {filename}</value> <type>string</type> - <hide>#if $generate_options().startswith('hb') -all#slurp -#else -part#slurp -#end if</hide> + <hide>#if $generate_options().startswith('hb') then 'all' else 'part'</hide> <tab>Advanced</tab> </param> <param> diff --git a/grc/blocks/parameter.xml b/grc/blocks/parameter.xml index e35b8f4d1d..b0713218fd 100644 --- a/grc/blocks/parameter.xml +++ b/grc/blocks/parameter.xml @@ -55,9 +55,11 @@ </option> <option> <name>String</name> - <key>string</key> + <key>str</key> <opt>type:string</opt> - </option> + </option> + <!-- Do not forget to add option value type handler import into + grc/python/flow_graph.tmpl for each new type. --> <!-- not supported yet in tmpl <option> <name>Boolean</name> diff --git a/grc/compiler.py b/grc/compiler.py new file mode 100755 index 0000000000..0cda0d946d --- /dev/null +++ b/grc/compiler.py @@ -0,0 +1,76 @@ +# Copyright 2016 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +from __future__ import print_function, absolute_import + +import argparse +import os +import subprocess + +from gnuradio import gr + +from .core import Messages +from .core.Platform import Platform + + +def argument_parser(): + parser = argparse.ArgumentParser(description=( + "Compile a GRC file (.grc) into a GNU Radio Python program and run it." + )) + parser.add_argument("-o", "--output", metavar='DIR', default='.', + help="Output directory for compiled program [default=%(default)s]") + parser.add_argument("-u", "--user-lib-dir", action='store_true', default=False, + help="Output to default hier_block library (overwrites -o)") + parser.add_argument("-r", "--run", action="store_true", default=False, + help="Run the program after compiling [default=%(default)s]") + parser.add_argument(metavar="GRC_FILE", dest='grc_files', nargs='+', + help=".grc file to compile") + return parser + + +def main(args=None): + args = args or argument_parser().parse_args() + + platform = Platform( + name='GNU Radio Companion Compiler', + prefs_file=gr.prefs(), + version=gr.version(), + version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()) + ) + out_dir = args.output if not args.user_lib_dir else platform.config.hier_block_lib_dir + if os.path.exists(out_dir): + pass # all is well + elif args.save_to_lib: + os.mkdir(out_dir) # create missing hier_block lib directory + else: + exit('Error: Invalid output directory') + + Messages.send_init(platform) + flow_graph = file_path = None + for grc_file in args.grc_files: + os.path.exists(grc_file) or exit('Error: missing ' + grc_file) + Messages.send('\n') + + flow_graph, file_path = platform.load_and_generate_flow_graph( + os.path.abspath(grc_file), os.path.abspath(out_dir)) + if not file_path: + exit('Compilation error') + if file_path and args.run: + run_command_args = flow_graph.get_run_command(file_path, split=True) + subprocess.call(run_command_args) diff --git a/grc/core/Block.py b/grc/core/Block.py index 737142442b..de1d83ee9a 100644 --- a/grc/core/Block.py +++ b/grc/core/Block.py @@ -24,7 +24,7 @@ from Cheetah.Template import Template from .utils import epy_block_io, odict from . Constants import ( - BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI, + BLOCK_FLAG_NEED_QT_GUI, ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB, BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS, BLOCK_FLAG_DEPRECATED, @@ -41,7 +41,7 @@ def _get_elem(lst, key): try: return lst[_get_keys(lst).index(key)] except ValueError: - raise ValueError('Key "{0}" not found in {1}.'.format(key, _get_keys(lst))) + raise ValueError('Key "{}" not found in {}.'.format(key, _get_keys(lst))) class Block(Element): @@ -121,7 +121,7 @@ class Block(Element): key = param.get_key() # Test against repeated keys if key in self.get_param_keys(): - raise Exception('Key "{0}" already exists in params'.format(key)) + raise Exception('Key "{}" already exists in params'.format(key)) # Store the param self.get_params().append(param) # Create the source objects @@ -130,7 +130,7 @@ class Block(Element): key = source.get_key() # Test against repeated keys if key in self.get_source_keys(): - raise Exception('Key "{0}" already exists in sources'.format(key)) + raise Exception('Key "{}" already exists in sources'.format(key)) # Store the port self.get_sources().append(source) self.back_ofthe_bus(self.get_sources()) @@ -140,7 +140,7 @@ class Block(Element): key = sink.get_key() # Test against repeated keys if key in self.get_sink_keys(): - raise Exception('Key "{0}" already exists in sinks'.format(key)) + raise Exception('Key "{}" already exists in sinks'.format(key)) # Store the port self.get_sinks().append(sink) self.back_ofthe_bus(self.get_sinks()) @@ -250,9 +250,9 @@ class Block(Element): check_res = self.resolve_dependencies(check) try: if not self.get_parent().evaluate(check_res): - self.add_error_message('Check "{0}" failed.'.format(check)) + self.add_error_message('Check "{}" failed.'.format(check)) except: - self.add_error_message('Check "{0}" did not evaluate.'.format(check)) + self.add_error_message('Check "{}" did not evaluate.'.format(check)) # For variables check the value (only if var_value is used if self.is_variable and self._var_value != '$value': @@ -261,7 +261,7 @@ class Block(Element): value = self.get_var_value() self.get_parent().evaluate(value) except Exception as err: - self.add_error_message('Value "{0}" cannot be evaluated:\n{1}'.format(value, err)) + self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err)) # check if this is a GUI block and matches the selected generate option current_generate_option = self.get_parent().get_option('generate_options') @@ -272,10 +272,9 @@ class Block(Element): self.get_name().upper().startswith(label) ) if block_requires_mode and current_generate_option not in valid_options: - self.add_error_message("Can't generate this block in mode: {0} ".format( + self.add_error_message("Can't generate this block in mode: {} ".format( repr(current_generate_option))) - check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',)) check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui')) if self._epy_reload_error: self.get_param('_source_code').add_error_message(str(self._epy_reload_error)) @@ -390,7 +389,7 @@ class Block(Element): callback = self.resolve_dependencies(callback) if 'self.' in callback: return callback - return 'self.{0}.{1}'.format(self.get_id(), callback) + return 'self.{}.{}'.format(self.get_id(), callback) return map(make_callback, self._callbacks) def is_virtual_sink(self): @@ -584,7 +583,7 @@ class Block(Element): return True def __str__(self): - return 'Block - {0} - {1}({2})'.format(self.get_id(), self.get_name(), self.get_key()) + return 'Block - {} - {}({})'.format(self.get_id(), self.get_name(), self.get_key()) def get_id(self): return self.get_param('id').get_value() @@ -699,7 +698,7 @@ class Block(Element): try: return str(Template(tmpl, n)) except Exception as err: - return "Template error: {0}\n {1}".format(tmpl, err) + return "Template error: {}\n {}".format(tmpl, err) ############################################## # Controller Modify diff --git a/grc/core/CMakeLists.txt b/grc/core/CMakeLists.txt index 51b0dacba6..f340127873 100644 --- a/grc/core/CMakeLists.txt +++ b/grc/core/CMakeLists.txt @@ -22,7 +22,6 @@ file(GLOB py_files "*.py") GR_PYTHON_INSTALL( FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core - COMPONENT "grc" ) file(GLOB dtd_files "*.dtd") @@ -30,7 +29,6 @@ file(GLOB dtd_files "*.dtd") install( FILES ${dtd_files} default_flow_graph.grc DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core - COMPONENT "grc" ) add_subdirectory(generator) diff --git a/grc/core/Config.py b/grc/core/Config.py index 78ff344998..744ad06ba9 100644 --- a/grc/core/Config.py +++ b/grc/core/Config.py @@ -32,10 +32,12 @@ class Config(object): hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR) - def __init__(self, prefs_file, version, version_parts=None): + def __init__(self, prefs_file, version, version_parts=None, name=None): self.prefs = prefs_file self.version = version self.version_parts = version_parts or version[1:].split('-', 1)[0].split('.')[:3] + if name: + self.name = name @property def block_paths(self): diff --git a/grc/core/Connection.py b/grc/core/Connection.py index 49eae69c82..c028d89ddc 100644 --- a/grc/core/Connection.py +++ b/grc/core/Connection.py @@ -75,16 +75,13 @@ class Connection(Element): pass def __str__(self): - return 'Connection (\n\t{0}\n\t\t{1}\n\t{2}\n\t\t{3}\n)'.format( + return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format( self.get_source().get_parent(), self.get_source(), self.get_sink().get_parent(), self.get_sink(), ) - def is_msg(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'msg' - def is_bus(self): return self.get_source().get_type() == self.get_sink().get_type() == 'bus' @@ -102,7 +99,7 @@ class Connection(Element): source_domain = self.get_source().get_domain() sink_domain = self.get_sink().get_domain() if (source_domain, sink_domain) not in platform.connection_templates: - self.add_error_message('No connection known for domains "{0}", "{1}"'.format( + self.add_error_message('No connection known for domains "{}", "{}"'.format( source_domain, sink_domain)) too_many_other_sinks = ( not platform.domains.get(source_domain, []).get('multiple_sinks', False) and @@ -114,15 +111,15 @@ class Connection(Element): ) if too_many_other_sinks: self.add_error_message( - 'Domain "{0}" can have only one downstream block'.format(source_domain)) + 'Domain "{}" can have only one downstream block'.format(source_domain)) if too_many_other_sources: self.add_error_message( - 'Domain "{0}" can have only one upstream block'.format(sink_domain)) + 'Domain "{}" can have only one upstream block'.format(sink_domain)) source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() if source_size != sink_size: - self.add_error_message('Source IO size "{0}" does not match sink IO size "{1}".'.format(source_size, sink_size)) + self.add_error_message('Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size)) def get_enabled(self): """ diff --git a/grc/core/Constants.py b/grc/core/Constants.py index 61a44d0c78..edd3442a94 100644 --- a/grc/core/Constants.py +++ b/grc/core/Constants.py @@ -48,7 +48,6 @@ DEFAULT_DOMAIN = GR_STREAM_DOMAIN BLOCK_FLAG_THROTTLE = 'throttle' BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass' BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui' -BLOCK_FLAG_NEED_WX_GUI = 'need_wx_gui' BLOCK_FLAG_DEPRECATED = 'deprecated' # Block States @@ -109,7 +108,6 @@ CORE_TYPES = ( # name, key, sizeof, color ('Integer 16', 's16', 2, GRC_COLOR_YELLOW), ('Integer 8', 's8', 1, GRC_COLOR_PURPLE_A400), ('Bits (unpacked byte)', 'bit', 1, GRC_COLOR_PURPLE_A100), - ('Message Queue', 'msg', 0, GRC_COLOR_DARK_GREY), ('Async Message', 'message', 0, GRC_COLOR_GREY), ('Bus Connection', 'bus', 0, GRC_COLOR_WHITE), ('Wildcard', '', 0, GRC_COLOR_WHITE), @@ -148,4 +146,3 @@ SHORT_VECTOR_COLOR_SPEC = '#CCCC33' BYTE_VECTOR_COLOR_SPEC = '#CC66CC' ID_COLOR_SPEC = '#DDDDDD' WILDCARD_COLOR_SPEC = '#FFFFFF' -MSG_COLOR_SPEC = '#777777' diff --git a/grc/core/Element.py b/grc/core/Element.py index d80753d8fa..67c36e12b4 100644 --- a/grc/core/Element.py +++ b/grc/core/Element.py @@ -66,7 +66,7 @@ class Element(object): error_messages = list(self._error_messages) # Make a copy for child in filter(lambda c: c.get_enabled() and not c.get_bypassed(), self.get_children()): for msg in child.get_error_messages(): - error_messages.append("{0}:\n\t{1}".format(child, msg.replace("\n", "\n\t"))) + error_messages.append("{}:\n\t{}".format(child, msg.replace("\n", "\n\t"))) return error_messages def rewrite(self): diff --git a/grc/core/Element.pyi b/grc/core/Element.pyi new file mode 100644 index 0000000000..c81180a33e --- /dev/null +++ b/grc/core/Element.pyi @@ -0,0 +1,54 @@ +# Copyright 2008, 2009, 2015, 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from . import Platform, FlowGraph, Block + +def lazy_property(func): + return func + + +class Element(object): + + def __init__(self, parent=None): + ... + + @property + def parent(self): + ... + + def get_parent_by_type(self, cls): + parent = self.parent + if parent is None: + return None + elif isinstance(parent, cls): + return parent + else: + return parent.get_parent_by_type(cls) + + @lazy_property + def parent_platform(self): -> Platform.Platform + ... + + @lazy_property + def parent_flowgraph(self): -> FlowGraph.FlowGraph + ... + + @lazy_property + def parent_block(self): -> Block.Block + ... + + diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py index 48563eefb1..ecae11cf1a 100644 --- a/grc/core/FlowGraph.py +++ b/grc/core/FlowGraph.py @@ -16,16 +16,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import imp -import time from itertools import ifilter, chain from operator import methodcaller, attrgetter - import re +import sys +import time from . import Messages from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION from .Element import Element -from .utils import odict, expr_utils +from .utils import odict, expr_utils, shlex _parameter_matcher = re.compile('^(parameter)$') _monitors_searcher = re.compile('(ctrlport_monitor)') @@ -64,7 +64,7 @@ class FlowGraph(Element): self._options_block = self.new_block('options') def __str__(self): - return 'FlowGraph - {0}({1})'.format(self.get_option('title'), self.get_option('id')) + return 'FlowGraph - {}({})'.format(self.get_option('title'), self.get_option('id')) ############################################## # TODO: Move these to new generator package @@ -186,6 +186,16 @@ class FlowGraph(Element): """ return self._options_block.get_param(key).get_evaluated() + def get_run_command(self, file_path, split=False): + run_command = self.get_option('run_command') + try: + run_command = run_command.format( + python=shlex.quote(sys.executable), + 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)) + ############################################## # Access Elements ############################################## @@ -410,7 +420,7 @@ class FlowGraph(Element): cwd=self.grc_file_path ) if file_path: # grc file found. load and get block - self.platform.load_and_generate_flow_graph(file_path) + self.platform.load_and_generate_flow_graph(file_path, hier_only=True) block = self.new_block(key) # can be None if not block: # looks like this block key cannot be found @@ -461,7 +471,7 @@ class FlowGraph(Element): self.connect(source_port, sink_port) except LookupError as e: Messages.send_error_load( - 'Connection between {0}({1}) and {2}({3}) could not be made.\n\t{4}'.format( + 'Connection between {}({}) and {}({}) could not be made.\n\t{}'.format( source_block_id, source_key, sink_block_id, sink_key, e)) errors = True diff --git a/grc/core/Param.py b/grc/core/Param.py index 26f9d2f451..fd098b76c5 100644 --- a/grc/core/Param.py +++ b/grc/core/Param.py @@ -30,7 +30,7 @@ from .utils import odict import __builtin__ -ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__) +ID_BLACKLIST = ['self', 'options', 'gr', 'math', 'firdes'] + dir(__builtin__) try: from gnuradio import gr ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_')) @@ -49,14 +49,14 @@ def _get_elem(lst, key): try: return lst[_get_keys(lst).index(key)] except ValueError: - raise ValueError('Key "{0}" not found in {1}.'.format(key, _get_keys(lst))) + raise ValueError('Key "{}" not found in {}.'.format(key, _get_keys(lst))) def num_to_str(num): """ Display logic for numbers """ def eng_notation(value, fmt='g'): """Convert a number to a string in engineering notation. E.g., 5e-9 -> 5n""" - template = '{0:' + fmt + '}{1}' + template = '{:' + fmt + '}{}' magnitude = abs(value) for exp, symbol in zip(range(9, -15-1, -3), 'GMk munpf'): factor = 10 ** exp @@ -92,15 +92,15 @@ class Option(Element): try: key, value = opt.split(':') except: - raise Exception('Error separating "{0}" into key:value'.format(opt)) + raise Exception('Error separating "{}" into key:value'.format(opt)) # Test against repeated keys if key in self._opts: - raise Exception('Key "{0}" already exists in option'.format(key)) + raise Exception('Key "{}" already exists in option'.format(key)) # Store the option self._opts[key] = value def __str__(self): - return 'Option {0}({1})'.format(self.get_name(), self.get_key()) + return 'Option {}({})'.format(self.get_name(), self.get_key()) def get_name(self): return self._name @@ -180,26 +180,26 @@ class Param(Element): key = option.get_key() # Test against repeated keys if key in self.get_option_keys(): - raise Exception('Key "{0}" already exists in options'.format(key)) + raise Exception('Key "{}" already exists in options'.format(key)) # Store the option self.get_options().append(option) # Test the enum options if self.is_enum(): # Test against options with identical keys if len(set(self.get_option_keys())) != len(self.get_options()): - raise Exception('Options keys "{0}" are not unique.'.format(self.get_option_keys())) + raise Exception('Options keys "{}" are not unique.'.format(self.get_option_keys())) # Test against inconsistent keys in options opt_keys = self.get_options()[0].get_opt_keys() for option in self.get_options(): if set(opt_keys) != set(option.get_opt_keys()): - raise Exception('Opt keys "{0}" are not identical across all options.'.format(opt_keys)) + raise Exception('Opt keys "{}" are not identical across all options.'.format(opt_keys)) # If a value is specified, it must be in the options keys if value or value in self.get_option_keys(): self._value = value else: self._value = self.get_option_keys()[0] if self.get_value() not in self.get_option_keys(): - raise Exception('The value "{0}" is not in the possible values of "{1}".'.format(self.get_value(), self.get_option_keys())) + raise Exception('The value "{}" is not in the possible values of "{}".'.format(self.get_value(), self.get_option_keys())) else: self._value = value or '' self._default = value @@ -215,7 +215,7 @@ class Param(Element): 'hex', 'string', 'bool', 'file_open', 'file_save', '_multiline', '_multiline_python_external', 'id', 'stream_id', - 'grid_pos', 'notebook', 'gui_hint', + 'gui_hint', 'import', ) @@ -290,7 +290,7 @@ class Param(Element): return self.get_value() def __str__(self): - return 'Param - {0}({1})'.format(self.get_name(), self.get_key()) + return 'Param - {}({})'.format(self.get_name(), self.get_key()) def get_color(self): """ @@ -317,8 +317,6 @@ class Param(Element): 'string': Constants.BYTE_VECTOR_COLOR_SPEC, 'id': Constants.ID_COLOR_SPEC, 'stream_id': Constants.ID_COLOR_SPEC, - 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, - 'notebook': Constants.INT_VECTOR_COLOR_SPEC, 'raw': Constants.WILDCARD_COLOR_SPEC, }[self.get_type()] except: @@ -354,9 +352,6 @@ class Param(Element): return 'part' except: pass - # Hide empty grid positions - if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): - return 'part' return hide def validate(self): @@ -366,7 +361,7 @@ class Param(Element): """ Element.validate(self) if self.get_type() not in self.get_types(): - self.add_error_message('Type "{0}" is not a possible type.'.format(self.get_type())) + self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type())) self._evaluated = None try: @@ -405,30 +400,30 @@ class Param(Element): try: e = self.get_parent().get_parent().evaluate(v) except Exception, e: - raise Exception('Value "{0}" cannot be evaluated:\n{1}'.format(v, e)) + raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e)) # Raise an exception if the data is invalid if t == 'raw': return e elif t == 'complex': if not isinstance(e, COMPLEX_TYPES): - raise Exception('Expression "{0}" is invalid for type complex.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type complex.'.format(str(e))) return e elif t == 'real' or t == 'float': if not isinstance(e, REAL_TYPES): - raise Exception('Expression "{0}" is invalid for type float.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type float.'.format(str(e))) return e elif t == 'int': if not isinstance(e, INT_TYPES): - raise Exception('Expression "{0}" is invalid for type integer.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type integer.'.format(str(e))) return e elif t == 'hex': return hex(e) elif t == 'bool': if not isinstance(e, bool): - raise Exception('Expression "{0}" is invalid for type bool.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type bool.'.format(str(e))) return e else: - raise TypeError('Type "{0}" not handled'.format(t)) + raise TypeError('Type "{}" not handled'.format(t)) ######################### # Numeric Vector Types ######################### @@ -440,28 +435,28 @@ class Param(Element): try: e = self.get_parent().get_parent().evaluate(v) except Exception, e: - raise Exception('Value "{0}" cannot be evaluated:\n{1}'.format(v, e)) + raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e)) # Raise an exception if the data is invalid if t == 'complex_vector': if not isinstance(e, VECTOR_TYPES): self._lisitify_flag = True e = [e] if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): - raise Exception('Expression "{0}" is invalid for type complex vector.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e))) return e elif t == 'real_vector' or t == 'float_vector': if not isinstance(e, VECTOR_TYPES): self._lisitify_flag = True e = [e] if not all([isinstance(ei, REAL_TYPES) for ei in e]): - raise Exception('Expression "{0}" is invalid for type float vector.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e))) return e elif t == 'int_vector': if not isinstance(e, VECTOR_TYPES): self._lisitify_flag = True e = [e] if not all([isinstance(ei, INT_TYPES) for ei in e]): - raise Exception('Expression "{0}" is invalid for type integer vector.'.format(str(e))) + raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e))) return e ######################### # String Types @@ -484,14 +479,14 @@ class Param(Element): elif t == 'id': # Can python use this as a variable? if not _check_id_matcher.match(v): - raise Exception('ID "{0}" must begin with a letter and may contain letters, numbers, and underscores.'.format(v)) + raise Exception('ID "{}" must begin with a letter and may contain letters, numbers, and underscores.'.format(v)) ids = [param.get_value() for param in self.get_all_params(t)] # Id should only appear once, or zero times if block is disabled if ids.count(v) > 1: - raise Exception('ID "{0}" is not unique.'.format(v)) + raise Exception('ID "{}" is not unique.'.format(v)) if v in ID_BLACKLIST: - raise Exception('ID "{0}" is blacklisted.'.format(v)) + raise Exception('ID "{}" is blacklisted.'.format(v)) return v ######################### @@ -507,11 +502,11 @@ class Param(Element): if self.get_parent().is_virtual_sink(): # Id should only appear once, or zero times if block is disabled if ids.count(v) > 1: - raise Exception('Stream ID "{0}" is not unique.'.format(v)) + raise Exception('Stream ID "{}" is not unique.'.format(v)) # Check that the virtual source's steam id is found if self.get_parent().is_virtual_source(): if v not in ids: - raise Exception('Stream ID "{0}" is not found.'.format(v)) + raise Exception('Stream ID "{}" is not found.'.format(v)) return v ######################### @@ -551,65 +546,6 @@ class Param(Element): return self._ws return GuiHint(widget_str) ######################### - # Grid Position Type - ######################### - elif t == 'grid_pos': - if not v: - # Allow for empty grid pos - return '' - e = self.get_parent().get_parent().evaluate(v) - if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): - raise Exception('A grid position must be a list of 4 integers.') - row, col, row_span, col_span = e - # Check row, col - if row < 0 or col < 0: - raise Exception('Row and column must be non-negative.') - # Check row span, col span - if row_span <= 0 or col_span <= 0: - raise Exception('Row and column span must be greater than zero.') - # Get hostage cell parent - try: - my_parent = self.get_parent().get_param('notebook').evaluate() - except: - my_parent = '' - # Calculate hostage cells - for r in range(row_span): - for c in range(col_span): - self._hostage_cells.append((my_parent, (row+r, col+c))) - # Avoid collisions - params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) - for param in params: - for parent, cell in param._hostage_cells: - if (parent, cell) in self._hostage_cells: - raise Exception('Another graphical element is using parent "{0}", cell "{1}".'.format(str(parent), str(cell))) - return e - ######################### - # Notebook Page Type - ######################### - elif t == 'notebook': - if not v: - # Allow for empty notebook - return '' - - # Get a list of all notebooks - notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) - # Check for notebook param syntax - try: - notebook_id, page_index = map(str.strip, v.split(',')) - except: - raise Exception('Bad notebook page format.') - # Check that the notebook id is valid - try: - notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] - except: - raise Exception('Notebook id "{0}" is not an existing notebook id.'.format(notebook_id)) - - # Check that page index exists - if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): - raise Exception('Page index "{0}" is not a valid index number.'.format(page_index)) - return notebook_id, page_index - - ######################### # Import Type ######################### elif t == 'import': @@ -618,14 +554,14 @@ class Param(Element): try: exec v in n except ImportError: - raise Exception('Import "{0}" failed.'.format(v)) + raise Exception('Import "{}" failed.'.format(v)) except Exception: - raise Exception('Bad import syntax: "{0}".'.format(v)) + raise Exception('Bad import syntax: "{}".'.format(v)) return filter(lambda k: str(k) != '__builtins__', n.keys()) ######################### else: - raise TypeError('Type "{0}" not handled'.format(t)) + raise TypeError('Type "{}" not handled'.format(t)) def to_code(self): """ diff --git a/grc/core/Platform.py b/grc/core/Platform.py index 297e8b0ae5..b73dade2e8 100644 --- a/grc/core/Platform.py +++ b/grc/core/Platform.py @@ -75,7 +75,7 @@ class Platform(Element): self.build_block_library() def __str__(self): - return 'Platform - {0}({1})'.format(self.config.key, self.config.name) + return 'Platform - {}({})'.format(self.config.key, self.config.name) @staticmethod def find_file_in_paths(filename, paths, cwd): @@ -93,42 +93,43 @@ class Platform(Element): if os.path.exists(os.path.normpath(file_path)): return file_path - def load_and_generate_flow_graph(self, file_path): + def load_and_generate_flow_graph(self, file_path, out_path=None, hier_only=False): """Loads a flow graph from file and generates it""" Messages.set_indent(len(self._auto_hier_block_generate_chain)) - Messages.send('>>> Loading: %r\n' % file_path) + Messages.send('>>> Loading: {}\n'.format(file_path)) if file_path in self._auto_hier_block_generate_chain: Messages.send(' >>> Warning: cyclic hier_block dependency\n') - return False + return None, None self._auto_hier_block_generate_chain.add(file_path) try: flow_graph = self.get_new_flow_graph() flow_graph.grc_file_path = file_path - # Other, nested higiter_blocks might be auto-loaded here + # Other, nested hier_blocks might be auto-loaded here flow_graph.import_data(self.parse_flow_graph(file_path)) flow_graph.rewrite() flow_graph.validate() if not flow_graph.is_valid(): raise Exception('Flowgraph invalid') - if not flow_graph.get_option('generate_options').startswith('hb'): + if hier_only and not flow_graph.get_option('generate_options').startswith('hb'): raise Exception('Not a hier block') except Exception as e: - Messages.send('>>> Load Error: {0}: {1}\n'.format(file_path, str(e))) - return False + Messages.send('>>> Load Error: {}: {}\n'.format(file_path, str(e))) + return None, None finally: self._auto_hier_block_generate_chain.discard(file_path) Messages.set_indent(len(self._auto_hier_block_generate_chain)) try: - Messages.send('>>> Generating: {0}\n'.format(file_path)) - generator = self.Generator(flow_graph, file_path) + generator = self.Generator(flow_graph, out_path or file_path) + Messages.send('>>> Generating: {}\n'.format(generator.file_path)) generator.write() except Exception as e: - Messages.send('>>> Generate Error: {0}: {1}\n'.format(file_path, str(e))) - return False + Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e))) + return None, None - self.load_block_xml(generator.get_file_path_xml()) - return True + if flow_graph.get_option('generate_options').startswith('hb'): + self.load_block_xml(generator.get_file_path_xml()) + return flow_graph, generator.file_path def build_block_library(self): """load the blocks and block tree from the search paths""" @@ -192,7 +193,7 @@ class Platform(Element): block = self.Block(self._flow_graph, n) key = block.get_key() if key in self.blocks: - print >> sys.stderr, 'Warning: Block with key "{0}" already exists.\n\tIgnoring: {1}'.format(key, xml_file) + print >> sys.stderr, 'Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file) else: # Store the block self.blocks[key] = block self._blocks_n[key] = n @@ -227,10 +228,10 @@ class Platform(Element): key = n.find('key') if not key: - print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {0}'.format(xml_file) + print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file) return if key in self.domains: # test against repeated keys - print >> sys.stderr, 'Warning: Domain with key "{0}" already exists.\n\tIgnoring: {1}'.format(key, xml_file) + print >> sys.stderr, 'Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file) return #to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '') @@ -245,7 +246,7 @@ class Platform(Element): gtk.gdk.color_parse(color) except (ValueError, ImportError): if color: # no color is okay, default set in GUI - print >> sys.stderr, 'Warning: Can\'t parse color code "{0}" for domain "{1}" '.format(color, key) + print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key) color = None self.domains[key] = dict( @@ -257,9 +258,9 @@ class Platform(Element): for connection_n in n.findall('connection'): key = (connection_n.find('source_domain'), connection_n.find('sink_domain')) if not all(key): - print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{0}'.format(xml_file) + print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file) elif key in self.connection_templates: - print >> sys.stderr, 'Warning: Connection template "{0}" already exists.\n\t{1}'.format(key, xml_file) + print >> sys.stderr, 'Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file) else: self.connection_templates[key] = connection_n.find('make') or '' diff --git a/grc/core/Port.py b/grc/core/Port.py index 8808bc4dbd..88601dc87a 100644 --- a/grc/core/Port.py +++ b/grc/core/Port.py @@ -32,7 +32,7 @@ def _get_source_from_virtual_sink_port(vsp): return _get_source_from_virtual_source_port( vsp.get_enabled_connections()[0].get_source()) except: - raise Exception('Could not resolve source for virtual sink port {0}'.format(vsp)) + raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) def _get_source_from_virtual_source_port(vsp, traversed=[]): @@ -43,7 +43,7 @@ def _get_source_from_virtual_source_port(vsp, traversed=[]): if not vsp.get_parent().is_virtual_source(): return vsp if vsp in traversed: - raise Exception('Loop found when resolving virtual source {0}'.format(vsp)) + raise Exception('Loop found when resolving virtual source {}'.format(vsp)) try: return _get_source_from_virtual_source_port( _get_source_from_virtual_sink_port( @@ -57,7 +57,7 @@ def _get_source_from_virtual_source_port(vsp, traversed=[]): ), traversed + [vsp], ) except: - raise Exception('Could not resolve source for virtual source port {0}'.format(vsp)) + raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) def _get_sink_from_virtual_source_port(vsp): @@ -70,7 +70,7 @@ def _get_sink_from_virtual_source_port(vsp): return _get_sink_from_virtual_sink_port( vsp.get_enabled_connections()[0].get_sink()) except: - raise Exception('Could not resolve source for virtual source port {0}'.format(vsp)) + raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) def _get_sink_from_virtual_sink_port(vsp, traversed=[]): @@ -81,7 +81,7 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]): if not vsp.get_parent().is_virtual_sink(): return vsp if vsp in traversed: - raise Exception('Loop found when resolving virtual sink {0}'.format(vsp)) + raise Exception('Loop found when resolving virtual sink {}'.format(vsp)) try: return _get_sink_from_virtual_sink_port( _get_sink_from_virtual_source_port( @@ -95,7 +95,7 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]): ), traversed + [vsp], ) except: - raise Exception('Could not resolve source for virtual sink port {0}'.format(vsp)) + raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) class Port(Element): @@ -119,8 +119,6 @@ class Port(Element): elif n['domain'] == GR_MESSAGE_DOMAIN: n['key'] = n['name'] n['type'] = 'message' # For port color - if n['type'] == 'msg': - n['key'] = 'msg' if not n.find('key'): n['key'] = str(next(block.port_counters[dir == 'source'])) @@ -142,9 +140,9 @@ class Port(Element): def __str__(self): if self.is_source: - return 'Source - {0}({1})'.format(self.get_name(), self.get_key()) + return 'Source - {}({})'.format(self.get_name(), self.get_key()) if self.is_sink: - return 'Sink - {0}({1})'.format(self.get_name(), self.get_key()) + return 'Sink - {}({})'.format(self.get_name(), self.get_key()) def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() @@ -155,18 +153,12 @@ class Port(Element): def validate(self): Element.validate(self) if self.get_type() not in self.get_types(): - self.add_error_message('Type "{0}" is not a possible type.'.format(self.get_type())) + self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type())) platform = self.get_parent().get_parent().get_parent() if self.get_domain() not in platform.domains: - self.add_error_message('Domain key "{0}" is not registered.'.format(self.get_domain())) + self.add_error_message('Domain key "{}" is not registered.'.format(self.get_domain())) if not self.get_enabled_connections() and not self.get_optional(): self.add_error_message('Port is not connected.') - # Message port logic - if self.get_type() == 'msg': - if self.get_nports(): - self.add_error_message('A port of type "msg" cannot have "nports" set.') - if self.get_vlen() != 1: - self.add_error_message('A port of type "msg" must have a "vlen" of 1.') def rewrite(self): """ diff --git a/grc/core/generator/CMakeLists.txt b/grc/core/generator/CMakeLists.txt index 4bdd59a7a2..492ad7c4ad 100644 --- a/grc/core/generator/CMakeLists.txt +++ b/grc/core/generator/CMakeLists.txt @@ -22,11 +22,9 @@ file(GLOB py_files "*.py") GR_PYTHON_INSTALL( FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/generator - COMPONENT "grc" ) install(FILES flow_graph.tmpl DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/generator - COMPONENT "grc" ) diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py index 3062440814..33d50d6b19 100644 --- a/grc/core/generator/Generator.py +++ b/grc/core/generator/Generator.py @@ -133,10 +133,6 @@ class TopBlockGenerator(object): def _get_block_sort_text(block): code = block.get_make().replace(block.get_id(), ' ') try: - code += block.get_param('notebook').get_value() # Older gui markup w/ wxgui - except: - pass - try: code += block.get_param('gui_hint').get_value() # Newer gui markup w/ qtgui except: pass @@ -165,7 +161,7 @@ class TopBlockGenerator(object): # Filter out virtual sink connections def cf(c): - return not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink()) + return not (c.is_bus() or c.get_sink().get_parent().is_virtual_sink()) connections = filter(cf, fg.get_enabled_connections()) # Get the virtual blocks and resolve their connections @@ -214,7 +210,6 @@ class TopBlockGenerator(object): )) connection_templates = fg.get_parent().connection_templates - msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections()) # List of variable names var_ids = [var.get_id() for var in parameters + variables] @@ -243,7 +238,6 @@ class TopBlockGenerator(object): 'blocks': blocks, 'connections': connections, 'connection_templates': connection_templates, - 'msgs': msgs, 'generate_options': self._generate_options, 'callbacks': callbacks, } diff --git a/grc/core/generator/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl index 1ef251c46b..2adb555486 100644 --- a/grc/core/generator/flow_graph.tmpl +++ b/grc/core/generator/flow_graph.tmpl @@ -11,7 +11,6 @@ ##@param parameters the parameter blocks ##@param blocks the signal blocks ##@param connections the connections -##@param msgs the msg type connections ##@param generate_options the type of flow graph ##@param callbacks variable id map to callback strings ######################################################## @@ -36,9 +35,13 @@ $DIVIDER import threading #end if +#if $generate_options == 'qt_gui' +from distutils.version import StrictVersion +#end if + ## Call XInitThreads as the _very_ first thing. ## After some Qt import, it's too late -#if $generate_options in ('wx_gui', 'qt_gui') +#if $generate_options == 'qt_gui' if __name__ == '__main__': import ctypes import sys @@ -72,28 +75,13 @@ $imp ##Create Class ## Write the class declaration for a top or hier block. ## The parameter names are the arguments to __init__. -## Determine the absolute icon path (wx gui only). ## Setup the IO signature (hier block only). ######################################################## #set $class_name = $flow_graph.get_option('id') #set $param_str = ', '.join(['self'] + ['%s=%s'%(param.get_id(), param.get_make()) for param in $parameters]) -#if $generate_options == 'wx_gui' - #import gtk - #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) - - -class $(class_name)(grc_wxgui.top_block_gui): - - def __init__($param_str): - grc_wxgui.top_block_gui.__init__(self, title="$title") - #if $icon - _icon_path = "$icon.get_filename()" - self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) - #end if -#elif $generate_options == 'qt_gui' +#if $generate_options == 'qt_gui' from gnuradio import qtgui - class $(class_name)(gr.top_block, Qt.QWidget): def __init__($param_str): @@ -118,7 +106,14 @@ class $(class_name)(gr.top_block, Qt.QWidget): self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "$class_name") - self.restoreGeometry(self.settings.value("geometry").toByteArray()) + + try: + if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"): + self.restoreGeometry(self.settings.value("geometry").toByteArray()) + else: + self.restoreGeometry(self.settings.value("geometry")) + except: + pass #elif $generate_options == 'no_gui' @@ -198,18 +193,6 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) $indent($var.get_var_make()) #end for ######################################################## -##Create Message Queues -######################################################## -#if $msgs - - $DIVIDER - # Message Queues - $DIVIDER -#end if -#for $msg in $msgs - $(msg.get_source().get_parent().get_id())_msgq_out = $(msg.get_sink().get_parent().get_id())_msgq_in = gr.msg_queue(2) -#end for -######################################################## ##Create Blocks ######################################################## #if $blocks @@ -338,19 +321,22 @@ $short_id#slurp def argument_parser(): - #set $desc_args = 'usage="%prog: [options]", option_class=eng_option' + #set $arg_parser_args = '' #if $flow_graph.get_option('description') - #set $desc_args += ', description=description' + #set $arg_parser_args = 'description=description' description = $repr($flow_graph.get_option('description')) #end if - parser = OptionParser($desc_args) + parser = ArgumentParser($arg_parser_args) #for $param in $parameters #set $type = $param.get_param('type').get_value() #if $type #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option( - "$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param), - help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") + parser.add_argument( + #if $make_short_id($param) + "$make_short_id($param)", #slurp + #end if + "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type=$type, default=$make_default($type, $param), + help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%(default)r]") #end if #end for return parser @@ -360,27 +346,15 @@ def argument_parser(): def main(top_block_cls=$(class_name), options=None): #if $parameters if options is None: - options, _ = argument_parser().parse_args() + options = argument_parser().parse_args() #end if #if $flow_graph.get_option('realtime_scheduling') if gr.enable_realtime_scheduling() != gr.RT_OK: print "Error: failed to enable real-time scheduling." #end if - #if $generate_options == 'wx_gui' - tb = top_block_cls($(', '.join($params_eq_list))) - #if $flow_graph.get_option('max_nouts') - tb.Run($flow_graph.get_option('run'), $flow_graph.get_option('max_nouts')) - #else - tb.Start($flow_graph.get_option('run')) - #for $m in $monitors - (tb.$m.get_id()).start() - #end for - tb.Wait() - #end if - #elif $generate_options == 'qt_gui' - from distutils.version import StrictVersion - if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"): + #if $generate_options == 'qt_gui' + if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"): style = gr.prefs().get_string('qtgui', 'style', 'raster') Qt.QApplication.setGraphicsSystem(style) qapp = Qt.QApplication(sys.argv) @@ -401,7 +375,7 @@ def main(top_block_cls=$(class_name), options=None): def quitting(): tb.stop() tb.wait() - qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting) + qapp.aboutToQuit.connect(quitting) #for $m in $monitors if $m.has_param('en'): if $m.get_param('en').get_value(): diff --git a/grc/core/utils/CMakeLists.txt b/grc/core/utils/CMakeLists.txt index 2528fbc43c..3ba65258a5 100644 --- a/grc/core/utils/CMakeLists.txt +++ b/grc/core/utils/CMakeLists.txt @@ -22,5 +22,4 @@ file(GLOB py_files "*.py") GR_PYTHON_INSTALL( FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/utils - COMPONENT "grc" ) diff --git a/grc/core/utils/odict.py b/grc/core/utils/odict.py index 9d69082600..85927e869f 100644 --- a/grc/core/utils/odict.py +++ b/grc/core/utils/odict.py @@ -59,7 +59,7 @@ class odict(DictMixin): """ index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) if key in self._keys: - raise KeyError('Cannot insert, key "{0}" already exists'.format(str(key))) + raise KeyError('Cannot insert, key "{}" already exists'.format(str(key))) self._keys.insert(index+1, key) self._data[key] = val @@ -75,7 +75,7 @@ class odict(DictMixin): """ index = (pos_key is not None) and self._keys.index(pos_key) or 0 if key in self._keys: - raise KeyError('Cannot insert, key "{0}" already exists'.format(str(key))) + raise KeyError('Cannot insert, key "{}" already exists'.format(str(key))) self._keys.insert(index, key) self._data[key] = val diff --git a/grc/core/utils/shlex.py b/grc/core/utils/shlex.py new file mode 100644 index 0000000000..6b620fa396 --- /dev/null +++ b/grc/core/utils/shlex.py @@ -0,0 +1,47 @@ +# Copyright 2016 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +from __future__ import absolute_import + +import re +import shlex + +# back port from python3 + +_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search + + +def _shlex_quote(s): + """Return a shell-escaped version of the string *s*.""" + if not s: + return "''" + if _find_unsafe(s) is None: + return s + + # use single quotes, and put single quotes into double quotes + # the string $'b is then quoted as '$'"'"'b' + return "'" + s.replace("'", "'\"'\"'") + "'" + + +if not hasattr(shlex, 'quote'): + quote = _shlex_quote +else: + quote = shlex.quote + +split = shlex.split diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 39c6993a37..b90ea485ee 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -273,7 +273,7 @@ class Block(Element, _Block): # Show the flowgraph complexity on the top block if enabled if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.get_key() == "options": complexity = calculate_flowgraph_complexity(self.get_parent()) - complexity = "Complexity: {0}bal".format(num_to_str(complexity)) + complexity = "Complexity: {}bal".format(num_to_str(complexity)) layout = gtk.DrawingArea().create_pango_layout('') layout.set_markup(Utils.parse_template(COMMENT_COMPLEXITY_MARKUP_TMPL, diff --git a/grc/gui/CMakeLists.txt b/grc/gui/CMakeLists.txt index aa9592b351..12be4a8151 100644 --- a/grc/gui/CMakeLists.txt +++ b/grc/gui/CMakeLists.txt @@ -22,5 +22,4 @@ file(GLOB py_files "*.py") GR_PYTHON_INSTALL( FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/gui - COMPONENT "grc" ) diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py index f91a341541..f5a75ab55b 100644 --- a/grc/gui/Executor.py +++ b/grc/gui/Executor.py @@ -15,15 +15,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -import gobject -import os -import threading -import shlex import subprocess -import sys -import re +import threading from distutils.spawn import find_executable +import gobject +import os + +from ..core.utils import shlex from ..core import Messages @@ -40,6 +39,7 @@ class ExecFlowGraphThread(threading.Thread): threading.Thread.__init__(self) self.page = flow_graph_page # store page and dont use main window calls in run + self.flow_graph = self.page.get_flow_graph() self.xterm_executable = xterm_executable self.update_callback = callback @@ -56,16 +56,9 @@ class ExecFlowGraphThread(threading.Thread): """ Execute this python flow graph. """ - run_command = self.page.get_flow_graph().get_option('run_command') generator = self.page.get_generator() - - try: - run_command = run_command.format( - python=shlex_quote(sys.executable), - filename=shlex_quote(generator.file_path)) - run_command_args = shlex.split(run_command) - except Exception as e: - raise ValueError("Can't parse run command {!r}: {0}".format(run_command, e)) + run_command = self.flow_graph.get_run_command(generator.file_path) + run_command_args = shlex.split(run_command) # When in no gui mode on linux, use a graphical terminal (looks nice) xterm_executable = find_executable(self.xterm_executable) @@ -101,21 +94,3 @@ class ExecFlowGraphThread(threading.Thread): Messages.send_end_exec(self.process.returncode) self.page.set_proc(None) self.update_callback() - - -########################################################### -# back-port from python3 -########################################################### -_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - - -def shlex_quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return "''" - if _find_unsafe(s) is None: - return s - - # use single quotes, and put single quotes into double quotes - # the string $'b is then quoted as '$'"'"'b' - return "'" + s.replace("'", "'\"'\"'") + "'" diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index e0cd7d1441..5bcf018120 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -77,7 +77,7 @@ class FlowGraph(Element, _Flowgraph): a unique id """ for index in count(): - block_id = '{0}_{1}'.format(base_id, index) + block_id = '{}_{}'.format(base_id, index) if block_id not in (b.get_id() for b in self.blocks): break return block_id diff --git a/grc/gui/ParserErrorsDialog.py b/grc/gui/ParserErrorsDialog.py index 57485eda93..68ee459414 100644 --- a/grc/gui/ParserErrorsDialog.py +++ b/grc/gui/ParserErrorsDialog.py @@ -83,7 +83,7 @@ class ParserErrorsDialog(gtk.Dialog): em = self.tree_store.append(parent, ["Line {e.line}: {e.message}".format(e=error)]) if code: self.tree_store.append(em, ["\n".join( - "{0} {1}{2}".format(line, code[line - 1].replace("\t", " ").strip("\n"), + "{} {}{}".format(line, code[line - 1].replace("\t", " ").strip("\n"), " " * 20 + "<!-- ERROR -->" if line == error.line else "") for line in range(error.line - 2, error.line + 3) if 0 < line <= len(code) )]) diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index cfea13e1a8..a5b46cbbac 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -267,7 +267,7 @@ class PropsDialog(gtk.Dialog): insert('\n\n# Variables\n', block.get_var_make()) insert('\n\n# Blocks\n', block.get_make()) if src: - insert('\n\n# External Code ({0}.py)\n'.format(block.get_id()), src) + insert('\n\n# External Code ({}.py)\n'.format(block.get_id()), src) def _handle_key_press(self, widget, event): """ diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py index d85b846b3a..7d15d47142 100644 --- a/grc/gui/Utils.py +++ b/grc/gui/Utils.py @@ -97,7 +97,7 @@ def encode(value): character. """ - valid_utf8 = value.decode('utf-8', 'replace').encode('utf-8') + valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8') return gobject.markup_escape_text(valid_utf8) diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py index 362a7f687d..7721f3bda6 100644 --- a/grc/gui/VariableEditor.py +++ b/grc/gui/VariableEditor.py @@ -270,7 +270,7 @@ class VariableEditor(gtk.VBox): # Create a context menu to confirm the delete operation confirmation_menu = gtk.Menu() block_id = self._block.get_param('id').get_value().replace("_", "__") - confirm = gtk.MenuItem("Delete {0}".format(block_id)) + confirm = gtk.MenuItem("Delete {}".format(block_id)) confirm.connect('activate', self.handle_action, self.DELETE_BLOCK) confirmation_menu.add(confirm) confirmation_menu.show_all() diff --git a/grc/main.py b/grc/main.py index ae7a0ce115..0edab40769 100644..100755 --- a/grc/main.py +++ b/grc/main.py @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -import optparse +import argparse import gtk from gnuradio import gr @@ -34,10 +34,10 @@ This is free software, and you are welcome to redistribute it. def main(): - parser = optparse.OptionParser( - usage='usage: %prog [options] [saved flow graphs]', - version=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version()) - options, args = parser.parse_args() + parser = argparse.ArgumentParser( + description=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version()) + parser.add_argument('flow_graphs', nargs='*') + args = parser.parse_args() try: gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) @@ -50,6 +50,6 @@ def main(): version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()), install_prefix=gr.prefix() ) - ActionHandler(args, platform) + ActionHandler(args.flow_graphs, platform) gtk.main() diff --git a/grc/scripts/CMakeLists.txt b/grc/scripts/CMakeLists.txt index 6cc78c3cf3..20366e0212 100644 --- a/grc/scripts/CMakeLists.txt +++ b/grc/scripts/CMakeLists.txt @@ -19,9 +19,8 @@ ######################################################################## GR_PYTHON_INSTALL( - PROGRAMS gnuradio-companion + PROGRAMS gnuradio-companion grcc DESTINATION ${GR_RUNTIME_DIR} - COMPONENT "grc" ) add_subdirectory(freedesktop) diff --git a/grc/scripts/freedesktop/CMakeLists.txt b/grc/scripts/freedesktop/CMakeLists.txt index 47e836f697..f936b366fc 100644 --- a/grc/scripts/freedesktop/CMakeLists.txt +++ b/grc/scripts/freedesktop/CMakeLists.txt @@ -31,7 +31,6 @@ install(FILES gnuradio-grc.xml gnuradio-grc.desktop DESTINATION ${grc_freedesktop_path} - COMPONENT "grc" ) find_program(HAVE_XDG_UTILS xdg-desktop-menu) @@ -44,6 +43,6 @@ if(UNIX AND HAVE_XDG_UTILS) @ONLY) install( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/grc_setup_freedesktop - DESTINATION ${GR_PKG_LIBEXEC_DIR} COMPONENT "grc" + DESTINATION ${GR_PKG_LIBEXEC_DIR} ) endif(UNIX AND HAVE_XDG_UTILS) diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index 34bb0bf110..bacbbe2334 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -1,22 +1,20 @@ #!/usr/bin/env python -""" -Copyright 2016 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -GNU Radio Companion is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -""" +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import sys diff --git a/grc/scripts/grcc b/grc/scripts/grcc new file mode 100755 index 0000000000..c3a53a91a6 --- /dev/null +++ b/grc/scripts/grcc @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import os +import sys + + +GR_IMPORT_ERROR_MESSAGE = """\ +Cannot import gnuradio. + +Is the model path environment variable set correctly? + All OS: PYTHONPATH + +Is the library path environment variable set correctly? + Linux: LD_LIBRARY_PATH + Windows: PATH + MacOSX: DYLD_LIBRARY_PATH +""" + + +def die(error, message): + msg = "{0}\n\n({1})".format(message, error) + exit(type(error).__name__ + '\n\n' + msg) + + +def check_gnuradio_import(): + try: + from gnuradio import gr + except ImportError as err: + die(err, GR_IMPORT_ERROR_MESSAGE) + + +def run_main(): + script_path = os.path.dirname(os.path.abspath(__file__)) + source_tree_subpath = "/grc/scripts" + + if not script_path.endswith(source_tree_subpath): + # run the installed version + from gnuradio.grc.compiler import main + else: + print("Running from source tree") + sys.path.insert(1, script_path[:-len(source_tree_subpath)]) + from grc.compiler import main + exit(main()) + + +if __name__ == '__main__': + check_gnuradio_import() + run_main() diff --git a/grc/tests/resources/test_compiler.grc b/grc/tests/resources/test_compiler.grc new file mode 100644 index 0000000000..cc56acedca --- /dev/null +++ b/grc/tests/resources/test_compiler.grc @@ -0,0 +1,253 @@ +<?xml version='1.0' encoding='utf-8'?> +<?grc format='1' created='3.7.11'?> +<flow_graph> + <timestamp>Thu Sep 15 12:56:40 2016</timestamp> + <block> + <key>options</key> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value></value> + </param> + <param> + <key>category</key> + <value>[GRC Hier Blocks]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>hier_block_src_path</key> + <value>.:</value> + </param> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>qt_qss_theme</key> + <value></value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>run_command</key> + <value>{python} -u {filename}</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>title</key> + <value></value> + </param> + </block> + <block> + <key>blocks_add_const_vxx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>const</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(360, 28)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_add_const_vxx_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_null_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>bus_conns</key> + <value>[[0,],]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(504, 32)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_null_sink_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_vector_source_x</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(208, 12)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_vector_source_x_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>repeat</key> + <value>False</value> + </param> + <param> + <key>tags</key> + <value>[]</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>vector</key> + <value>(0, 0, 0)</value> + </param> + </block> + <connection> + <source_block_id>blocks_add_const_vxx_0</source_block_id> + <sink_block_id>blocks_null_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_source_x_0</source_block_id> + <sink_block_id>blocks_add_const_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/grc/tests/test_compiler.py b/grc/tests/test_compiler.py new file mode 100644 index 0000000000..27b5670871 --- /dev/null +++ b/grc/tests/test_compiler.py @@ -0,0 +1,38 @@ +# Copyright 2016 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +from argparse import Namespace +from os import path +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')], + run=True + ) + + main(args) + + out, err = capsys.readouterr() + assert not err |