summaryrefslogtreecommitdiff
path: root/grc
diff options
context:
space:
mode:
Diffstat (limited to 'grc')
-rw-r--r--grc/CMakeLists.txt24
-rw-r--r--grc/blocks/CMakeLists.txt22
-rw-r--r--grc/blocks/options.xml62
-rw-r--r--grc/blocks/parameter.xml6
-rwxr-xr-xgrc/compiler.py76
-rw-r--r--grc/core/Block.py25
-rw-r--r--grc/core/CMakeLists.txt2
-rw-r--r--grc/core/Config.py4
-rw-r--r--grc/core/Connection.py13
-rw-r--r--grc/core/Constants.py3
-rw-r--r--grc/core/Element.py2
-rw-r--r--grc/core/Element.pyi54
-rw-r--r--grc/core/FlowGraph.py22
-rw-r--r--grc/core/Param.py126
-rw-r--r--grc/core/Platform.py41
-rw-r--r--grc/core/Port.py28
-rw-r--r--grc/core/generator/CMakeLists.txt2
-rw-r--r--grc/core/generator/Generator.py8
-rw-r--r--grc/core/generator/flow_graph.tmpl80
-rw-r--r--grc/core/utils/CMakeLists.txt1
-rw-r--r--grc/core/utils/odict.py4
-rw-r--r--grc/core/utils/shlex.py47
-rw-r--r--grc/gui/Block.py2
-rw-r--r--grc/gui/CMakeLists.txt1
-rw-r--r--grc/gui/Executor.py41
-rw-r--r--grc/gui/FlowGraph.py2
-rw-r--r--grc/gui/ParserErrorsDialog.py2
-rw-r--r--grc/gui/PropsDialog.py2
-rw-r--r--grc/gui/Utils.py2
-rw-r--r--grc/gui/VariableEditor.py2
-rwxr-xr-x[-rw-r--r--]grc/main.py12
-rw-r--r--grc/scripts/CMakeLists.txt3
-rw-r--r--grc/scripts/freedesktop/CMakeLists.txt3
-rwxr-xr-xgrc/scripts/gnuradio-companion34
-rwxr-xr-xgrc/scripts/grcc64
-rw-r--r--grc/tests/resources/test_compiler.grc253
-rw-r--r--grc/tests/test_compiler.py38
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