diff options
Diffstat (limited to 'grc')
42 files changed, 885 insertions, 453 deletions
diff --git a/grc/base/Block.py b/grc/base/Block.py index afe326bbf4..2a20c22ef7 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -19,12 +19,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from . import odict from . Constants import ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB +from . Constants import BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS +from . Constants import BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED from Element import Element from Cheetah.Template import Template from UserDict import UserDict from itertools import imap + class TemplateArg(UserDict): """ A cheetah template argument created from a param. @@ -46,11 +49,16 @@ class TemplateArg(UserDict): def __call__(self): return self._param.get_evaluated() -def _get_keys(lst): return [elem.get_key() for elem in lst] + +def _get_keys(lst): + return [elem.get_key() for elem in lst] + + def _get_elem(lst, key): try: return lst[_get_keys(lst).index(key)] except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) + class Block(Element): def __init__(self, flow_graph, n): @@ -73,6 +81,10 @@ class Block(Element): self._name = n.find('name') self._key = n.find('key') self._category = n.find('category') or '' + self._flags = n.find('flags') or '' + # Backwards compatibility + if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: + self._flags += BLOCK_FLAG_THROTTLE self._grc_source = n.find('grc_source') or '' self._block_wrapper_path = n.find('block_wrapper_path') self._bussify_sink = n.find('bus_sink') @@ -143,6 +155,10 @@ class Block(Element): and (self._key != "pad_sink")) is_variable = self._key.startswith('variable') + # Disable blocks that are virtual/pads or variables + if not is_not_virtual_or_pad or is_variable: + self._flags += BLOCK_FLAG_DISABLE_BYPASS + if is_not_virtual_or_pad and not is_variable: self.get_params().append(self.get_parent().get_parent().Param( block=self, @@ -197,7 +213,6 @@ class Block(Element): }) )) - def back_ofthe_bus(self, portlist): portlist.sort(key=lambda p: p._type == 'bus') @@ -205,6 +220,35 @@ class Block(Element): buslist = [p for p in ports if p._type == 'bus'] return buslist or ports + # Main functions to get and set the block state + # Also kept get_enabled and set_enabled to keep compatibility + def get_state(self): + """ + Gets the block's current state. + + Returns: + ENABLED - 0 + BYPASSED - 1 + DISABLED - 2 + """ + try: return int(eval(self.get_param('_enabled').get_value())) + except: return BLOCK_ENABLED + + def set_state(self, state): + """ + Sets the state for the block. + + Args: + ENABLED - 0 + BYPASSED - 1 + DISABLED - 2 + """ + if state in [BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED]: + self.get_param('_enabled').set_value(str(state)) + else: + self.get_param('_enabled').set_value(str(BLOCK_ENABLED)) + + # Enable/Disable Aliases def get_enabled(self): """ Get the enabled state of the block. @@ -212,8 +256,7 @@ class Block(Element): Returns: true for enabled """ - try: return eval(self.get_param('_enabled').get_value()) - except: return True + return not (self.get_state() == BLOCK_DISABLED) def set_enabled(self, enabled): """ @@ -221,8 +264,45 @@ class Block(Element): Args: enabled: true for enabled + + Returns: + True if block changed state """ - self.get_param('_enabled').set_value(str(enabled)) + old_state = self.get_state() + new_state = BLOCK_ENABLED if enabled else BLOCK_DISABLED + self.set_state(new_state) + return old_state != new_state + + # Block bypassing + def get_bypassed(self): + """ + Check if the block is bypassed + """ + return self.get_state() == BLOCK_BYPASSED + + def set_bypassed(self): + """ + Bypass the block + + Returns: + True if block chagnes state + """ + if self.get_state() != BLOCK_BYPASSED and self.can_bypass(): + self.set_state(BLOCK_BYPASSED) + return True + return False + + def can_bypass(self): + """ Check the number of sinks and sources and see if this block can be bypassed """ + # Check to make sure this is a single path block + # Could possibly support 1 to many blocks + if len(self.get_sources()) != 1 or len(self.get_sinks()) != 1: + return False + if not (self.get_sources()[0].get_type() == self.get_sinks()[0].get_type()): + return False + if self.bypass_disabled(): + return False + return True def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) @@ -240,6 +320,10 @@ class Block(Element): def get_block_wrapper_path(self): return self._block_wrapper_path def get_comment(self): return self.get_param('comment').get_value() + def get_flags(self): return self._flags + def throtteling(self): return BLOCK_FLAG_THROTTLE in self._flags + def bypass_disabled(self): return BLOCK_FLAG_DISABLE_BYPASS in self._flags + ############################################## # Access Params ############################################## @@ -414,7 +498,7 @@ class Block(Element): """ n = odict() n['key'] = self.get_key() - n['param'] = map(lambda p: p.export_data(), self.get_params()) + n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str)) if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): n['bus_sink'] = str(1); if 'bus' in map(lambda a: a.get_type(), self.get_sources()): diff --git a/grc/base/Constants.py b/grc/base/Constants.py index efae0ecbb5..1e83de63b5 100644 --- a/grc/base/Constants.py +++ b/grc/base/Constants.py @@ -38,3 +38,13 @@ DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd') GR_STREAM_DOMAIN = "gr_stream" GR_MESSAGE_DOMAIN = "gr_message" DEFAULT_DOMAIN = GR_STREAM_DOMAIN + +BLOCK_FLAG_THROTTLE = 'throttle' +BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass' +BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui' +BLOCK_FLAG_NEED_WX_GUI = 'need_ex_gui' + +# Block States +BLOCK_DISABLED = 0 +BLOCK_ENABLED = 1 +BLOCK_BYPASSED = 2 diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index fb25b46821..52ec741db8 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -78,9 +78,6 @@ class FlowGraph(Element): bus_structure = block.form_bus_structure('sink'); if 'bus' in map(lambda a: a.get_type(), get_p_gui()): - - - if len(get_p_gui()) > len(bus_structure): times = range(len(bus_structure), len(get_p_gui())); for i in times: @@ -100,8 +97,6 @@ class FlowGraph(Element): port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc); get_p().append(port); - - for child in self.get_children(): child.rewrite() refactor_bus_structure(); @@ -126,13 +121,13 @@ class FlowGraph(Element): def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0] def get_blocks_unordered(self): return filter(lambda e: e.is_block(), self.get_elements()) def get_blocks(self): - blocks = self.get_blocks_unordered(); - for i in range(len(blocks)): - if blocks[i].get_key() == 'variable': - blk = blocks[i]; - blocks.remove(blk); - blocks.insert(1, blk); - return blocks; + # refactored the slow, ugly version + # don't know why we need this here, using it for sorted export_data() + return sorted(self.get_blocks_unordered(), key=lambda b: ( + b.get_key() != 'options', # options to the front + not b.get_key().startswith('variable'), # then vars + str(b) + )) def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements()) def get_children(self): return self.get_elements() def get_elements(self): @@ -152,13 +147,22 @@ class FlowGraph(Element): def get_enabled_blocks(self): """ - Get a list of all blocks that are enabled. + Get a list of all blocks that are enabled and not bypassed. Returns: a list of blocks """ return filter(lambda b: b.get_enabled(), self.get_blocks()) + def get_bypassed_blocks(self): + """ + Get a list of all blocks that are bypassed. + + Returns: + a list of blocks + """ + return filter(lambda b: b.get_bypassed(), self.get_blocks()) + def get_enabled_connections(self): """ Get a list of all connections that are enabled. @@ -250,8 +254,8 @@ class FlowGraph(Element): """ n = odict() n['timestamp'] = self._timestamp - n['block'] = [block.export_data() for block in self.get_blocks()] - n['connection'] = [connection.export_data() for connection in self.get_connections()] + n['block'] = [b.export_data() for b in self.get_blocks()] # already sorted + n['connection'] = [c.export_data() for c in sorted(self.get_connections(), key=str)] instructions = odict({ 'created': self.get_parent().get_version_short(), 'format': FLOW_GRAPH_FILE_FORMAT_VERSION, diff --git a/grc/base/Param.py b/grc/base/Param.py index 94be8b0fe0..c2f413ccbe 100644 --- a/grc/base/Param.py +++ b/grc/base/Param.py @@ -140,7 +140,7 @@ class Param(Element): def get_color(self): return '#FFFFFF' def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) def is_param(self): return True - def get_name(self): return self._name + def get_name(self): return self.get_parent().resolve_dependencies(self._name).strip() def get_key(self): return self._key def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() diff --git a/grc/blocks/.gitignore b/grc/blocks/.gitignore new file mode 100644 index 0000000000..2ac4a79e69 --- /dev/null +++ b/grc/blocks/.gitignore @@ -0,0 +1 @@ +variable_struct.xml diff --git a/grc/blocks/CMakeLists.txt b/grc/blocks/CMakeLists.txt index 98b6512a09..0c2a1f7901 100644 --- a/grc/blocks/CMakeLists.txt +++ b/grc/blocks/CMakeLists.txt @@ -18,6 +18,26 @@ # Boston, MA 02110-1301, USA. ######################################################################## +include(GrPython) + file(GLOB xml_files "*.xml") -install(FILES ${xml_files} DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "grc") +macro(GEN_BLOCK_XML _generator _xml_block) + set(generator ${CMAKE_CURRENT_SOURCE_DIR}/${_generator}) + set(xml_block ${CMAKE_CURRENT_BINARY_DIR}/${_xml_block}) + list(APPEND generated_xml_files ${xml_block}) + add_custom_command( + DEPENDS ${generator} OUTPUT ${xml_block} + COMMAND ${PYTHON_EXECUTABLE} ${generator} ${xml_block} + ) +endmacro(GEN_BLOCK_XML) + +GEN_BLOCK_XML(variable_struct.xml.py variable_struct.xml) + +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 6588dc72d0..dc02b83c2a 100644 --- a/grc/blocks/options.xml +++ b/grc/blocks/options.xml @@ -19,7 +19,7 @@ import wx from PyQt4 import Qt import sys #end if -#if $generate_options() != 'hb' +#if not $generate_options().startswith('hb') from optparse import OptionParser from gnuradio.eng_option import eng_option from gnuradio import eng_notation @@ -49,9 +49,9 @@ else: self.stop(); self.wait()</callback> <hide>#if $description() then 'none' else 'part'#</hide> </param> <param> - <name>Window Size</name> + <name>Canvas Size</name> <key>window_size</key> - <value>1280, 1024</value> + <value></value> <type>int_vector</type> <hide>part</hide> </param> @@ -76,13 +76,17 @@ else: self.stop(); self.wait()</callback> <name>Hier Block</name> <key>hb</key> </option> + <option> + <name>QT GUI Hier Block</name> + <key>hb_qt_gui</key> + </option> </param> <param> <name>Category</name> <key>category</key> <value>Custom</value> <type>string</type> - <hide>#if $generate_options() == 'hb' then 'none' else 'all'#</hide> + <hide>#if $generate_options().startswith('hb') then 'none' else 'all'#</hide> </param> <param> <name>Run Options</name> @@ -129,7 +133,7 @@ else: self.stop(); self.wait()</callback> <key>max_nouts</key> <value>0</value> <type>int</type> - <hide>#if $generate_options() == 'hb' + <hide>#if $generate_options().startswith('hb') all#slurp #elif $max_nouts() none#slurp @@ -142,7 +146,7 @@ part#slurp <key>realtime_scheduling</key> <value></value> <type>enum</type> - <hide>#if $generate_options() == 'hb' + <hide>#if $generate_options().startswith('hb') all#slurp #elif $realtime_scheduling() none#slurp @@ -174,9 +178,9 @@ part#slurp </option> <tab>Advanced</tab> </param> - <check>len($window_size) == 2</check> - <check>300 <= $(window_size)[0] <= 4096</check> - <check>300 <= $(window_size)[1] <= 4096</check> + <check>not $window_size or len($window_size) == 2</check> + <check>not $window_size or 300 <= $(window_size)[0] <= 4096</check> + <check>not $window_size or 300 <= $(window_size)[1] <= 4096</check> <doc> The options block sets special parameters for the flow graph. \ Only one option block is allowed per flow graph. diff --git a/grc/blocks/variable_struct.xml.py b/grc/blocks/variable_struct.xml.py new file mode 100644 index 0000000000..e43200828b --- /dev/null +++ b/grc/blocks/variable_struct.xml.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +MAX_NUM_FIELDS = 20 + +HEADER = """\ +<block> + <name>Struct Variable</name> + <key>variable_struct</key> + <category>Variables</category> + <import>def struct(data): return type('Struct', (object,), data)()</import> + <var_make>self.$id = $id = struct({{#slurp +#for $i in range({0}): +#set $field = $getVar('field' + str(i)) +#if len(str($field)) > 2 +$field: $getVar('value' + str(i)), #slurp +#end if +#end for +}})</var_make> + <var_value>struct({{#slurp +#for $i in range({0}): +#set $field = $getVar('field' + str(i)) +#if len(str($field)) > 2 +$field: $getVar('value' + str(i)), #slurp +#end if +#end for +}})</var_value> + <make></make> +""" + +FIELD0 = """\ + <param> + <name>Field 0</name> + <key>field0</key> + <value>field0</value> + <type>string</type> + <hide>part</hide> + <tab>Fields</tab> + </param> +""" + +FIELDS = """\ + <param> + <name>Field {0}</name> + <key>field{0}</key> + <value></value> + <type>string</type> + <hide>part</hide> + <tab>Fields</tab> + </param> +""" + +VALUES = """\ + <param> + <name>$field{0}()</name> + <key>value{0}</key> + <value>0</value> + <type>raw</type> + <hide>#if $field{0}() then 'none' else 'all'#</hide> + </param> +""" + +CHECKS = """\ + <check>($str($field{0}) or "a")[0].isalpha()</check> + <check>($str($field{0}) or "a").isalnum()</check> +""" + +FOOTER = """\ + <doc>This is a simple struct/record like variable. + +Attribute/field names can be specified in the tab 'Fields'. +For each non-empty field a parameter with type raw is shown. +Value access via the dot operator, e.g. "variable_struct_0.field0" + </doc> +</block> +""" + + +def make_xml(num_fields): + return ''.join(( + HEADER.format(num_fields), + FIELD0, ''.join(FIELDS.format(i) for i in range(1, num_fields)), + ''.join(VALUES.format(i) for i in range(num_fields)), + ''.join(CHECKS.format(i) for i in range(num_fields)), + FOOTER + )) + + +if __name__ == '__main__': + import sys + try: + filename = sys.argv[1] + except IndexError: + filename = __file__[:-3] + + data = make_xml(MAX_NUM_FIELDS) + + with open(filename, 'w') as fp: + fp.write(data.encode()) diff --git a/grc/freedesktop/CMakeLists.txt b/grc/freedesktop/CMakeLists.txt index d95fe04ac5..47e836f697 100644 --- a/grc/freedesktop/CMakeLists.txt +++ b/grc/freedesktop/CMakeLists.txt @@ -26,6 +26,8 @@ install(FILES grc-icon-64.png grc-icon-48.png grc-icon-32.png + grc-icon-24.png + grc-icon-16.png gnuradio-grc.xml gnuradio-grc.desktop DESTINATION ${grc_freedesktop_path} diff --git a/grc/freedesktop/convert.sh b/grc/freedesktop/convert.sh new file mode 100755 index 0000000000..e2cba264a6 --- /dev/null +++ b/grc/freedesktop/convert.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +SRC="gnuradio_logo_icon-square.svg" +SIZES="16 24 32 48 64 128 256" + +for size in $SIZES +do + inkscape --without-gui \ + --export-png="grc-icon-$size.png" \ + --export-area=8.0:8.0:141.316:141.316 \ + --export-width=$size \ + --export-height=$size \ + $SRC; +done diff --git a/grc/freedesktop/gnuradio-grc.desktop b/grc/freedesktop/gnuradio-grc.desktop index 5fd0497808..39beeca1b8 100644 --- a/grc/freedesktop/gnuradio-grc.desktop +++ b/grc/freedesktop/gnuradio-grc.desktop @@ -6,3 +6,4 @@ Exec=gnuradio-companion %F Categories=Development; MimeType=application/gnuradio-grc; Icon=gnuradio-grc +StartupWMClass=gnuradio-companion diff --git a/grc/freedesktop/gnuradio_logo_icon-square.svg b/grc/freedesktop/gnuradio_logo_icon-square.svg new file mode 100644 index 0000000000..3b54bf4001 --- /dev/null +++ b/grc/freedesktop/gnuradio_logo_icon-square.svg @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="150" + height="150" + id="svg3965" + version="1.1" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="gnuradio_logo_icon.svg"> + <defs + id="defs3967" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="3.959798" + inkscape:cx="139.75154" + inkscape:cy="47.200773" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1176" + inkscape:window-x="0" + inkscape:window-y="24" + inkscape:window-maximized="1" /> + <metadata + id="metadata3970"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-57.226591,-6.6600272)"> + <g + id="g4684" + transform="translate(30,0)"> + <path + class="fil3" + d="m 136.1636,73.874976 c 3.50539,3.465 5.25827,7.69571 5.25827,12.691774 0,4.87524 -1.7426,9.05563 -5.2278,12.54083 -3.4852,3.4852 -7.64539,5.22779 -12.48024,5.22779 -3.39165,0 -6.44775,-0.82488 -9.16795,-2.47464 l -6.34075,6.34075 c 3.94548,4.48866 5.91804,9.83941 5.91804,16.05189 0,6.69649 -2.39351,12.43842 -7.18087,17.22578 -4.78701,4.78736 -10.5013,7.18087 -17.142875,7.18087 -6.751771,0 -12.535866,-2.38004 -17.350511,-7.13977 -4.815,-4.75937 -7.222323,-10.51511 -7.222323,-17.26688 0,-6.75248 2.324055,-12.53587 6.973582,-17.35087 4.648819,-4.815 10.349292,-7.22232 17.101417,-7.22232 5.988189,0 11.18799,1.78334 15.60083,5.34968 l 6.03602,-6.03567 c -3.40972,-3.437 -5.11441,-7.57948 -5.11441,-12.42744 0,-4.915634 1.69228,-9.126144 5.07685,-12.631534 0.31429,-0.32528 0.63532,-0.63567 0.96272,-0.93083 l -6.1728,-6.17279 c -2.68193,2.54799 -5.857438,3.82216 -9.526533,3.82216 -3.871062,0 -7.187598,-1.36453 -9.948188,-4.09358 -2.760945,-2.72906 -4.141418,-6.02929 -4.141418,-9.90071 0,-3.87142 1.332993,-7.18795 3.998622,-9.94854 2.66563,-2.76095 5.933977,-4.14142 9.805394,-4.14142 3.967086,0 7.330393,1.36488 10.091693,4.09394 2.76024,2.7287 4.14071,6.06082 4.14071,9.99602 0,2.69256 -0.67465,5.11583 -2.02429,7.27016 l 6.85275,6.85311 c 2.49768,-1.40351 5.30114,-2.10544 8.41146,-2.10544 5.03646,0 9.3072,1.73233 12.8126,5.19768 z M 96.164267,48.757896 c -2.189409,0 -4.030157,0.77704 -5.521535,2.3322 -1.491378,1.5548 -2.237244,3.4115 -2.237244,5.56937 0,2.22094 0.729921,4.07764 2.189764,5.56937 1.459842,1.49138 3.316181,2.23689 5.569015,2.23689 2.126339,0 3.927043,-0.76925 5.402833,-2.30846 1.47543,-1.53922 2.21315,-3.37182 2.21315,-5.4978 0,-1.0474 -0.19027,-2.04697 -0.57118,-2.99905 -0.38055,-0.95209 -0.9202,-1.77697 -1.61823,-2.47536 -0.69838,-0.72957 -1.53106,-1.31669 -2.499092,-1.76102 -0.967677,-0.44433 -1.943504,-0.66614 -2.927481,-0.66614 z m -6.364842,61.714484 c -3.818976,0 -7.028858,1.35567 -9.629645,4.06736 -2.601496,2.71205 -3.902244,5.94992 -3.902244,9.71363 0,3.87354 1.272755,7.11141 3.818976,9.71291 2.545512,2.60079 5.78374,3.90153 9.712913,3.90153 3.708071,0 6.848858,-1.34185 9.422362,-4.02661 2.573153,-2.6837 3.860433,-5.87976 3.860433,-9.58783 0,-1.82693 -0.33201,-3.56989 -0.99638,-5.23028 -0.66401,-1.66039 -1.60476,-3.09933 -2.822596,-4.31681 -1.217481,-1.27311 -2.670236,-2.29677 -4.358622,-3.07169 -1.687323,-0.77492 -3.389528,-1.16221 -5.105197,-1.16221 z M 123.71383,76.534226 c -2.78043,0 -5.11724,0.98681 -7.01078,2.96115 -1.8939,1.97433 -2.84067,4.33133 -2.84067,7.071374 0,2.82048 0.92657,5.17748 2.78008,7.07138 1.8535,1.89354 4.21051,2.84032 7.07137,2.84032 2.6993,0 4.98579,-0.97725 6.85949,-2.93138 1.87335,-1.95414 2.8102,-4.28067 2.8102,-6.98032 0,-1.3298 -0.24165,-2.599014 -0.72532,-3.807634 -0.4833,-1.20863 -1.16822,-2.25638 -2.05476,-3.14256 -0.88654,-0.92693 -1.94421,-1.67209 -3.17303,-2.23618 -1.22882,-0.56445 -2.46756,-0.84615 -3.71658,-0.84615 z" + id="path3484" + inkscape:connector-curvature="0" + style="fill:#ff6905;fill-rule:evenodd" /> + <path + class="fil3" + d="m 71.293796,36.662816 c 4.548897,-8.03374 13.97622,-13.55421 24.865865,-13.55421 10.854209,0 20.255669,5.48503 24.821569,13.4759 l 5.54103,-5.68453 c -6.21354,-9.32279 -17.48799,-15.55547 -30.362599,-15.55547 -12.967086,0 -24.311338,6.32303 -30.49618,15.7578 l 5.630315,5.56051 z" + id="path3486" + inkscape:connector-curvature="0" + style="fill:#ff6905;fill-rule:evenodd" /> + <path + class="fil3" + d="m 96.092693,28.893756 c -9.484725,0 -17.688189,4.48441 -21.598228,10.99595 l 5.61685,5.5474 c 2.163189,-5.05347 8.501457,-8.71335 15.981378,-8.71335 7.554687,0 13.944327,3.73358 16.044447,8.86536 l 5.55449,-5.69835 c -3.91004,-6.51224 -12.1135,-10.99701 -21.598937,-10.99701 z" + id="path3488" + inkscape:connector-curvature="0" + style="fill:#ff6905;fill-rule:evenodd" /> + <text + x="195.22275" + y="148.5569" + class="fil4 fnt0" + id="text3496" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + <text + x="256.77316" + y="148.5569" + class="fil4 fnt0" + id="text3506" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + <text + x="279.70544" + y="148.5569" + class="fil4 fnt0" + id="text3510" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + <text + x="344.62241" + y="148.5569" + class="fil4 fnt0" + id="text3520" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + <text + x="473.09561" + y="148.5569" + class="fil4 fnt0" + id="text3538" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + <text + x="548.49011" + y="148.5569" + class="fil4 fnt0" + id="text3550" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + <text + x="687.89447" + y="148.5569" + class="fil4 fnt0" + id="text3570" + style="font-size:17.76259804px;font-weight:bold;fill:#969594;fill-rule:evenodd;font-family:Harabara" /> + </g> + </g> +</svg> diff --git a/grc/freedesktop/grc-icon-128.png b/grc/freedesktop/grc-icon-128.png Binary files differindex d94ef35dc3..13efe806ba 100644 --- a/grc/freedesktop/grc-icon-128.png +++ b/grc/freedesktop/grc-icon-128.png diff --git a/grc/freedesktop/grc-icon-16.png b/grc/freedesktop/grc-icon-16.png Binary files differnew file mode 100644 index 0000000000..bdd1823b3d --- /dev/null +++ b/grc/freedesktop/grc-icon-16.png diff --git a/grc/freedesktop/grc-icon-24.png b/grc/freedesktop/grc-icon-24.png Binary files differnew file mode 100644 index 0000000000..a124768125 --- /dev/null +++ b/grc/freedesktop/grc-icon-24.png diff --git a/grc/freedesktop/grc-icon-256.png b/grc/freedesktop/grc-icon-256.png Binary files differindex e4e8e54ae1..077688eac5 100644 --- a/grc/freedesktop/grc-icon-256.png +++ b/grc/freedesktop/grc-icon-256.png diff --git a/grc/freedesktop/grc-icon-256.svg b/grc/freedesktop/grc-icon-256.svg deleted file mode 100644 index 87526d46c6..0000000000 --- a/grc/freedesktop/grc-icon-256.svg +++ /dev/null @@ -1,216 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="256" - height="256" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.44.1" - version="1.0" - sodipodi:docbase="/home/past/src" - sodipodi:docname="grc-icon-v3.svg" - inkscape:export-filename="/home/past/src/grc-icon-v3.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4"> - <linearGradient - id="linearGradient3661"> - <stop - style="stop-color:#0012dc;stop-opacity:1;" - offset="0" - id="stop3663" /> - <stop - style="stop-color:#8b92ff;stop-opacity:0.55371898;" - offset="1" - id="stop3665" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3661" - id="linearGradient2801" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(-162.6648,798.0997)" - x1="17.664845" - y1="132.0565" - x2="157.82524" - y2="132.0565" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="2.7382812" - inkscape:cx="126.48791" - inkscape:cy="128.00013" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - gridoriginx="1px" - gridoriginy="1px" - gridspacingx="2px" - gridspacingy="2px" - guidecolor="#00ff0a" - guideopacity="0.49803922" - inkscape:grid-points="true" - inkscape:window-width="1098" - inkscape:window-height="904" - inkscape:window-x="149" - inkscape:window-y="42" - showguides="true" - inkscape:guide-bbox="true" - inkscape:object-points="true" - inkscape:object-nodes="true" - inkscape:object-bbox="true"> - <sodipodi:guide - orientation="vertical" - position="224" - id="guide10639" /> - <sodipodi:guide - orientation="vertical" - position="227.64729" - id="guide10641" /> - <sodipodi:guide - orientation="vertical" - position="220" - id="guide10643" /> - <sodipodi:guide - orientation="horizontal" - position="268.4015" - id="guide10645" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <cc:license - rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" /> - <dc:creator> - <cc:Agent> - <dc:title>Patrick Strasser <patrick.strasser@tugraz.at></dc:title> - </cc:Agent> - </dc:creator> - <dc:description>Icon/Symbol for the GNURadio Companion</dc:description> - <dc:title>grc-icon.svg</dc:title> - <dc:date>2007-02-23</dc:date> - </cc:Work> - <cc:License - rdf:about="http://creativecommons.org/licenses/by-sa/2.5/"> - <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> - <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/Attribution" /> - <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> - </cc:License> - </rdf:RDF> - </metadata> - <g - inkscape:label="Ebene 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-796.3622)"> - <g - id="g7451" - transform="matrix(1.025628,0,0,1.030546,-0.101723,-32.00742)"> - <path - id="rect2760" - d="M 4.1981031,916.37787 L 160.00074,916.37787 L 160.00074,1048.3467 L 4.1981031,1048.3467 L 4.1981031,916.37787 z " - style="fill:white;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.78145933;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <g - transform="matrix(0.995753,0,0,1.003897,164.8198,-8.972397)" - id="g2789" - style="stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none"> - <rect - style="fill:url(#linearGradient2801);fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.78288651;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect9020" - width="140.1604" - height="16.796082" - x="-145" - y="921.75818" /> - <path - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:7.78288651;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M -161.50649,938.55428 L -4.8395996,938.55428" - id="path9005" /> - </g> - </g> - <g - id="g5503" - transform="matrix(1.028571,0,0,1.172413,-5.14284,-137.9928)"> - <rect - y="800.36212" - x="40" - height="116.00005" - width="140" - id="rect4562" - style="fill:#f3c690;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.28504848;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <g - transform="matrix(0.921053,0,0,1,26.93956,1.859948)" - id="g3694"> - <path - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:7.59084845;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 44,820.3622 L 136.35974,820.3622" - id="path4564" /> - <path - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:7.59084749;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 80,811.74976 L 80,828.3622" - id="path5451" /> - </g> - <g - id="g5499"> - <rect - style="fill:white;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.28504944;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7223" - width="140" - height="68.000015" - x="40" - y="848.36218" /> - <path - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:7.28505039;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 57.001362,905.90132 C 88.923615,905.8552 86.182775,867.89142 95.399136,867.52563 C 104.60967,867.16008 113.73233,867.60291 124.38688,868.00066 C 137.23411,868.48027 130.39915,906.48027 162.99863,906.48027" - id="path7225" - sodipodi:nodetypes="czss" /> - </g> - </g> - <rect - style="fill:#b890f3;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.99999952;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect14319" - width="79.999992" - height="64.000023" - x="27.999992" - y="960.36249" /> - <rect - style="fill:#f3c690;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.99999905;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect15206" - width="24.000004" - height="24.000004" - x="108" - y="980.36218" /> - <path - id="path13320" - style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:7.99999666;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" - d="M 220.97574,800.36203 L 220.97574,865.80513 C 236.42474,865.93437 248.49884,861.19996 248.96304,854.45866 C 249.42721,847.71737 237.26568,843.20829 221.81667,846.27676 C 206.36767,849.34522 193.45938,858.02071 192.98843,867.61976 C 192.53101,876.94268 204.68583,884.63729 220.13479,885.43882 C 235.58379,885.69443 248.49884,880.72833 248.96304,873.98703 C 249.42721,867.24575 237.26567,862.73666 221.81666,865.80513 C 206.36766,868.8736 193.45939,877.54909 192.98843,887.14813 C 192.53101,896.47106 204.68582,904.16566 220.13479,904.86701 C 235.5838,905.02246 248.49885,900.05636 248.96305,893.31506 C 249.42722,886.57378 237.26568,882.06469 221.81667,885.13316 C 206.36767,888.20162 193.45939,896.87711 192.98844,906.47616 C 192.53102,915.79909 204.68583,923.49369 220.13479,923.98015 C 235.58379,923.92069 248.49884,918.95459 248.96304,912.21329 C 249.42721,905.47201 237.26567,900.96293 221.81666,904.0314 C 206.36766,907.09986 193.45939,915.77535 192.98843,925.37439 C 192.53101,934.69732 207.20989,943.06708 221.81667,943.00644 L 221.81667,967.97713 C 221.63716,982.45754 209.62079,992.36197 195.88792,992.36199 L 132.42659,992.36199" - sodipodi:nodetypes="cccssscssscssscssccc" /> - </g> -</svg> diff --git a/grc/freedesktop/grc-icon-32.png b/grc/freedesktop/grc-icon-32.png Binary files differindex 1e4f4f6c5a..a345aace3c 100644 --- a/grc/freedesktop/grc-icon-32.png +++ b/grc/freedesktop/grc-icon-32.png diff --git a/grc/freedesktop/grc-icon-48.png b/grc/freedesktop/grc-icon-48.png Binary files differindex caddc92ad4..c522a5d0ec 100644 --- a/grc/freedesktop/grc-icon-48.png +++ b/grc/freedesktop/grc-icon-48.png diff --git a/grc/freedesktop/grc-icon-64.png b/grc/freedesktop/grc-icon-64.png Binary files differindex fa8e06e280..df4f6dc07b 100644 --- a/grc/freedesktop/grc-icon-64.png +++ b/grc/freedesktop/grc-icon-64.png diff --git a/grc/freedesktop/grc_setup_freedesktop.in b/grc/freedesktop/grc_setup_freedesktop.in index 1e3546197d..87a388e2ec 100644 --- a/grc/freedesktop/grc_setup_freedesktop.in +++ b/grc/freedesktop/grc_setup_freedesktop.in @@ -27,7 +27,7 @@ # @SRCDIR@ ################################################## -ICON_SIZES="32 48 64 128 256" +ICON_SIZES="16 24 32 48 64 128 256" if [ -n "$2" ]; then SRCDIR="$2" else diff --git a/grc/grc.conf.in b/grc/grc.conf.in index 99ae9caff5..71c4f63bca 100644 --- a/grc/grc.conf.in +++ b/grc/grc.conf.in @@ -6,3 +6,5 @@ global_blocks_path = @blocksdir@ local_blocks_path = xterm_executable = @GRC_XTERM_EXE@ +canvas_font_size = 8 +canvas_default_size = 1280, 1024 diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index fee96624bb..15565127e3 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -18,7 +18,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import os -import signal from Constants import IMAGE_FILE_EXTENSION import Actions import pygtk @@ -29,15 +28,17 @@ import subprocess import Preferences from threading import Thread import Messages -from .. base import ParseXML +from .. base import ParseXML, Constants from MainWindow import MainWindow from PropsDialog import PropsDialog from ParserErrorsDialog import ParserErrorsDialog import Dialogs from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveReportsFileDialog, SaveImageFileDialog +from . Constants import DEFAULT_CANVAS_SIZE gobject.threads_init() + class ActionHandler: """ The action handler will setup all the major window components, @@ -124,6 +125,7 @@ class ActionHandler: Actions.CLEAR_REPORTS, Actions.SAVE_REPORTS, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS, + Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB, ): action.set_sensitive(True) if ParseXML.xml_failures: Messages.send_xml_errors_if_any(ParseXML.xml_failures) @@ -146,6 +148,7 @@ class ActionHandler: Actions.TOGGLE_SCROLL_LOCK, Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS, + Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB, ): action.load_from_preferences() elif action == Actions.APPLICATION_QUIT: if self.main_window.close_pages(): @@ -171,6 +174,11 @@ class ActionHandler: self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) + elif action == Actions.BLOCK_BYPASS: + if self.get_flow_graph().bypass_selected(): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) ################################################## # Cut/Copy/Paste ################################################## @@ -405,6 +413,8 @@ class ActionHandler: action.save_to_preferences() elif action == Actions.TOGGLE_SHOW_BLOCK_COMMENTS: action.save_to_preferences() + elif action == Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB: + action.save_to_preferences() ################################################## # Param Modifications ################################################## @@ -553,23 +563,34 @@ class ActionHandler: ################################################## # Global Actions for all States ################################################## + selected_block = self.get_flow_graph().get_selected_block() + selected_blocks = self.get_flow_graph().get_selected_blocks() + #update general buttons Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) - Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) - Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(selected_block)) + Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(selected_blocks)) + Actions.BLOCK_ROTATE_CW.set_sensitive(bool(selected_blocks)) #update cut/copy/paste - Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_CUT.set_sensitive(bool(selected_blocks)) + Actions.BLOCK_COPY.set_sensitive(bool(selected_blocks)) Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) - #update enable/disable - Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_CREATE_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.OPEN_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BUSSIFY_SOURCES.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BUSSIFY_SINKS.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + #update enable/disable/bypass + can_enable = any(block.get_state() != Constants.BLOCK_ENABLED + for block in selected_blocks) + can_disable = any(block.get_state() != Constants.BLOCK_DISABLED + for block in selected_blocks) + can_bypass_all = all(block.can_bypass() for block in selected_blocks) \ + and any (not block.get_bypassed() for block in selected_blocks) + Actions.BLOCK_ENABLE.set_sensitive(can_enable) + Actions.BLOCK_DISABLE.set_sensitive(can_disable) + Actions.BLOCK_BYPASS.set_sensitive(can_bypass_all) + + Actions.BLOCK_CREATE_HIER.set_sensitive(bool(selected_blocks)) + Actions.OPEN_HIER.set_sensitive(bool(selected_blocks)) + Actions.BUSSIFY_SOURCES.set_sensitive(bool(selected_blocks)) + Actions.BUSSIFY_SINKS.set_sensitive(bool(selected_blocks)) Actions.RELOAD_BLOCKS.set_sensitive(True) Actions.FIND_BLOCKS.set_sensitive(True) #set the exec and stop buttons @@ -578,7 +599,8 @@ class ActionHandler: Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) self.main_window.update() try: #set the size of the flow graph area (if changed) - new_size = self.get_flow_graph().get_option('window_size') + new_size = (self.get_flow_graph().get_option('window_size') or + DEFAULT_CANVAS_SIZE) if self.get_flow_graph().get_size() != tuple(new_size): self.get_flow_graph().set_size(*new_size) except: pass diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index b2b3a76386..d864db7e16 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -106,7 +106,8 @@ class Action(gtk.Action, _ActionBase): Pass additional arguments such as keypresses. """ - def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None): + def __init__(self, keypresses=(), name=None, label=None, tooltip=None, + stock_id=None): """ Create a new Action instance. @@ -129,7 +130,8 @@ class ToggleAction(gtk.ToggleAction, _ActionBase): Pass additional arguments such as keypresses. """ - def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None, preference_name=None): + def __init__(self, keypresses=(), name=None, label=None, tooltip=None, + stock_id=None, preference_name=None, default=True): """ Create a new ToggleAction instance. @@ -137,22 +139,24 @@ class ToggleAction(gtk.ToggleAction, _ActionBase): key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...) the: regular gtk.Action parameters (defaults to None) """ - if name is None: name = label + if name is None: + name = label gtk.ToggleAction.__init__(self, - name=name, label=label, - tooltip=tooltip, stock_id=stock_id, + name=name, label=label, tooltip=tooltip, stock_id=stock_id, ) - #register this action _ActionBase.__init__(self, label, keypresses) self.preference_name = preference_name + self.default = default def load_from_preferences(self): if self.preference_name is not None: - self.set_active(Preferences.bool_entry(self.preference_name)) + self.set_active(Preferences.bool_entry(self.preference_name, + default=self.default)) def save_to_preferences(self): if self.preference_name is not None: - Preferences.bool_entry(self.preference_name, self.get_active()) + Preferences.bool_entry(self.preference_name, + value=self.get_active()) ######################################################################## # Actions @@ -247,6 +251,12 @@ BLOCK_DISABLE = Action( stock_id=gtk.STOCK_DISCONNECT, keypresses=(gtk.keysyms.d, NO_MODS_MASK), ) +BLOCK_BYPASS = Action( + label='_Bypass', + tooltip='Bypass the selected block', + stock_id=gtk.STOCK_MEDIA_FORWARD, + keypresses=(gtk.keysyms.b, NO_MODS_MASK), +) TOGGLE_SNAP_TO_GRID = ToggleAction( label='_Snap to grid', tooltip='Snap blocks to a grid for an easier connection alignment', @@ -268,6 +278,13 @@ TOGGLE_SHOW_BLOCK_COMMENTS = ToggleAction( tooltip="Show comment beneath each block", preference_name='show_block_comments' ) +TOGGLE_SHOW_CODE_PREVIEW_TAB = ToggleAction( + label='Generated Code Preview', + tooltip="Show a preview of the code generated for each Block in its " + "Properties Dialog", + preference_name='show_generated_code_tab', + default=False, +) BLOCK_CREATE_HIER = Action( label='C_reate Hier', tooltip='Create hier block from selected blocks', @@ -298,13 +315,13 @@ ERRORS_WINDOW_DISPLAY = Action( stock_id=gtk.STOCK_DIALOG_ERROR, ) TOGGLE_REPORTS_WINDOW = ToggleAction( - label='Show _Reports', + label='Show _Reports Panel', tooltip='Toggle visibility of the Report widget', keypresses=(gtk.keysyms.r, gtk.gdk.CONTROL_MASK), preference_name='reports_window_visible' ) TOGGLE_BLOCKS_WINDOW = ToggleAction( - label='Show _Block Tree', + label='Show _Block Tree Panel', tooltip='Toggle visibility of the block tree widget', keypresses=(gtk.keysyms.b, gtk.gdk.CONTROL_MASK), preference_name='blocks_window_visible' diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index 40ce20536c..f0f8dac7fb 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -49,6 +49,7 @@ TOOLBAR_LIST = ( None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, + Actions.BLOCK_BYPASS, Actions.TOGGLE_HIDE_DISABLED_BLOCKS, None, Actions.FIND_BLOCKS, @@ -85,6 +86,7 @@ MENU_BAR_LIST = ( None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, + Actions.BLOCK_BYPASS, None, Actions.BLOCK_PARAM_MODIFY, ]), @@ -101,6 +103,8 @@ MENU_BAR_LIST = ( Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS, None, + Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB, + None, Actions.ERRORS_WINDOW_DISPLAY, Actions.FIND_BLOCKS, ]), @@ -134,6 +138,7 @@ CONTEXT_MENU_LIST = [ Actions.BLOCK_ROTATE_CW, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, + Actions.BLOCK_BYPASS, None, (gtk.Action('More', '_More', None, None), [ Actions.BLOCK_CREATE_HIER, diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 60f19fc1a4..11273a537b 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -22,10 +22,10 @@ import Utils import Colors from .. base import odict from Constants import BORDER_PROXIMITY_SENSITIVITY -from Constants import \ - BLOCK_LABEL_PADDING, \ - PORT_SEPARATION, LABEL_SEPARATION, \ +from Constants import ( + BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION, PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT +) import Actions import pygtk pygtk.require('2.0') @@ -72,6 +72,7 @@ class Block(Element): )) Element.__init__(self) self._comment_pixmap = None + self.has_busses = [False, False] # source, sink def get_coordinate(self): """ @@ -145,7 +146,9 @@ class Block(Element): """Create the labels for the signal block.""" Element.create_labels(self) self._bg_color = self.is_dummy_block() and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ + self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \ self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR + layouts = list() #create the main layout layout = gtk.DrawingArea().create_pango_layout('') @@ -188,13 +191,14 @@ class Block(Element): self.vertical_label = self.get_parent().new_pixmap(height, width) Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) #calculate width and height needed - self.W = self.label_width + 2*BLOCK_LABEL_PADDING + self.W = self.label_width + 2 * BLOCK_LABEL_PADDING + def get_min_height_for_ports(): visible_ports = filter(lambda p: not p.get_hide(), ports) H = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION if visible_ports: H -= ports[0].H return H - self.H = max(*( + self.H = max( [ # labels self.label_height + 2 * BLOCK_LABEL_PADDING ] + @@ -202,11 +206,15 @@ class Block(Element): get_min_height_for_ports() for ports in (self.get_sources_gui(), self.get_sinks_gui()) ] + [ # bus ports only - 4 * PORT_BORDER_SEPARATION + - sum([port.H + PORT_SEPARATION for port in ports if port.get_type() == 'bus']) - PORT_SEPARATION + 2 * PORT_BORDER_SEPARATION + + sum([port.H + PORT_SPACING for port in ports if port.get_type() == 'bus']) - PORT_SPACING for ports in (self.get_sources_gui(), self.get_sinks_gui()) ] - )) + ) + self.has_busses = [ + any(port.get_type() == 'bus' for port in ports) + for ports in (self.get_sources_gui(), self.get_sinks_gui()) + ] self.create_comment_label() def create_comment_label(self): diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py index f64106b03f..52c95e8edf 100644 --- a/grc/gui/Colors.py +++ b/grc/gui/Colors.py @@ -38,6 +38,7 @@ try: #block color constants BLOCK_ENABLED_COLOR = get_color('#F1ECFF') BLOCK_DISABLED_COLOR = get_color('#CCCCCC') + BLOCK_BYPASSED_COLOR = get_color('#FFFFE6') #connection color constants CONNECTION_ENABLED_COLOR = get_color('black') CONNECTION_DISABLED_COLOR = get_color('#BBBBBB') diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py index 0dc6279fd2..a8395f631e 100644 --- a/grc/gui/Constants.py +++ b/grc/gui/Constants.py @@ -18,78 +18,104 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import pygtk + pygtk.require('2.0') import gtk import os +import sys +from gnuradio import gr + +_gr_prefs = gr.prefs() + -##default path for the open/save dialogs +# default path for the open/save dialogs DEFAULT_FILE_PATH = os.getcwd() -##file extensions +# file extensions IMAGE_FILE_EXTENSION = '.png' TEXT_FILE_EXTENSION = '.txt' -##name for new/unsaved flow graphs +# name for new/unsaved flow graphs NEW_FLOGRAPH_TITLE = 'untitled' -##main window constraints +# main window constraints MIN_WINDOW_WIDTH = 600 MIN_WINDOW_HEIGHT = 400 -##dialog constraints +# dialog constraints MIN_DIALOG_WIDTH = 500 MIN_DIALOG_HEIGHT = 500 -##default sizes +# default sizes DEFAULT_BLOCKS_WINDOW_WIDTH = 100 DEFAULT_REPORTS_WINDOW_WIDTH = 100 -## flow-graph canvas fonts + +try: # ugly, but matches current code style + raw = _gr_prefs.get_string('grc', 'canvas_default_size', '1280, 1024') + DEFAULT_CANVAS_SIZE = tuple(int(x.strip('() ')) for x in raw.split(',')) + if len(DEFAULT_CANVAS_SIZE) != 2 or not all(300 < x < 4096 for x in DEFAULT_CANVAS_SIZE): + raise Exception() +except: + DEFAULT_CANVAS_SIZE = 1280, 1024 + print >> sys.stderr, "Error: invalid 'canvas_default_size' setting." + +# flow-graph canvas fonts +try: # ugly, but matches current code style + FONT_SIZE = _gr_prefs.get_long('grc', 'canvas_font_size', 8) + if FONT_SIZE <= 0: + raise Exception() +except: + FONT_SIZE = 8 + print >> sys.stderr, "Error: invalid 'canvas_font_size' setting." FONT_FAMILY = "Sans" -FONT_SIZE = 8 BLOCK_FONT = "%s %f" % (FONT_FAMILY, FONT_SIZE) PORT_FONT = BLOCK_FONT PARAM_FONT = "%s %f" % (FONT_FAMILY, FONT_SIZE - 0.5) -##The size of the state saving cache in the flow graph (for undo/redo functionality) +# size of the state saving cache in the flow graph (undo/redo functionality) STATE_CACHE_SIZE = 42 -##Shared targets for drag and drop of blocks +# Shared targets for drag and drop of blocks DND_TARGETS = [('STRING', gtk.TARGET_SAME_APP, 0)] -#label constraint dimensions +# label constraint dimensions LABEL_SEPARATION = 3 BLOCK_LABEL_PADDING = 7 PORT_LABEL_PADDING = 2 -#port constraint dimensions -PORT_SEPARATION = 32 -PORT_BORDER_SEPARATION = 9 +# canvas grid size +CANVAS_GRID_SIZE = 8 + +# port constraint dimensions +PORT_BORDER_SEPARATION = 8 +PORT_SPACING = 2 * PORT_BORDER_SEPARATION +PORT_SEPARATION = PORT_SPACING + 2 * PORT_LABEL_PADDING + int(1.5 * FONT_SIZE) +PORT_SEPARATION += -PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple + PORT_MIN_WIDTH = 20 PORT_LABEL_HIDDEN_WIDTH = 10 -#minimal length of connector +# minimal length of connector CONNECTOR_EXTENSION_MINIMAL = 11 -#increment length for connector +# increment length for connector CONNECTOR_EXTENSION_INCREMENT = 11 -#connection arrow dimensions +# connection arrow dimensions CONNECTOR_ARROW_BASE = 13 CONNECTOR_ARROW_HEIGHT = 17 -#possible rotations in degrees +# possible rotations in degrees POSSIBLE_ROTATIONS = (0, 90, 180, 270) -#How close can the mouse get to the window border before mouse events are ignored. +# How close can the mouse get to the window border before mouse events are ignored. BORDER_PROXIMITY_SENSITIVITY = 50 -#How close the mouse can get to the edge of the visible window before scrolling is invoked. +# How close the mouse can get to the edge of the visible window before scrolling is invoked. SCROLL_PROXIMITY_SENSITIVITY = 30 -#When the window has to be scrolled, move it this distance in the required direction. +# When the window has to be scrolled, move it this distance in the required direction. SCROLL_DISTANCE = 15 -#How close the mouse click can be to a line and register a connection select. +# How close the mouse click can be to a line and register a connection select. LINE_SELECT_SENSITIVITY = 5 -# canvas grid size -CANVAS_GRID_SIZE = 8 diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 97f814f1bf..bf6e1eed78 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -211,9 +211,21 @@ class FlowGraph(Element): """ changed = False for selected_block in self.get_selected_blocks(): - if selected_block.get_enabled() != enable: - selected_block.set_enabled(enable) - changed = True + if selected_block.set_enabled(enable): changed = True + return changed + + def bypass_selected(self): + """ + Bypass the selected blocks. + + Args: + None + Returns: + true if changed + """ + changed = False + for selected_block in self.get_selected_blocks(): + if selected_block.set_bypassed(): changed = True return changed def move_selected(self, delta_coordinate): diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index db77ff2112..eda28a0e94 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.py @@ -25,4 +25,4 @@ class Platform(Element): self._prefs_file = prefs_file - def get_prefs_file(self): return self._prefs_file
\ No newline at end of file + def get_prefs_file(self): return self._prefs_file diff --git a/grc/gui/Port.py b/grc/gui/Port.py index 9abda878bf..93372ead93 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -18,10 +18,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from Element import Element -from Constants import \ - PORT_SEPARATION, CONNECTOR_EXTENSION_MINIMAL, \ - CONNECTOR_EXTENSION_INCREMENT, \ +from Constants import ( + PORT_SEPARATION, PORT_SPACING, CONNECTOR_EXTENSION_MINIMAL, + CONNECTOR_EXTENSION_INCREMENT, CANVAS_GRID_SIZE, PORT_LABEL_PADDING, PORT_MIN_WIDTH, PORT_LABEL_HIDDEN_WIDTH, PORT_FONT +) from .. base.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN import Utils import Actions @@ -79,56 +80,55 @@ class Port(Element): #reverse the order of ports for these rotations if rotation in (180, 270): index = length-index-1 - offset = (self.get_parent().H - (length-1)*PORT_SEPARATION - self.H)/2 + + port_separation = PORT_SEPARATION \ + if self.get_parent().has_busses[self.is_source()] \ + else max([port.H for port in ports]) + PORT_SPACING + + offset = (self.get_parent().H - (length-1)*port_separation - self.H)/2 #create areas and connector coordinates if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): - x = -1*W - y = PORT_SEPARATION*index+offset + x = -W + y = port_separation*index+offset self.add_area((x, y), (W, self.H)) self._connector_coordinate = (x-1, y+self.H/2) elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): x = self.get_parent().W - y = PORT_SEPARATION*index+offset + y = port_separation*index+offset self.add_area((x, y), (W, self.H)) self._connector_coordinate = (x+1+W, y+self.H/2) elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): - y = -1*W - x = PORT_SEPARATION*index+offset + y = -W + x = port_separation*index+offset self.add_area((x, y), (self.H, W)) self._connector_coordinate = (x+self.H/2, y-1) elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): y = self.get_parent().W - x = PORT_SEPARATION*index+offset + x = port_separation*index+offset self.add_area((x, y), (self.H, W)) self._connector_coordinate = (x+self.H/2, y+1+W) #the connector length self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index - def modify_height(self, start_height): - type_dict = {'bus':(lambda a: a * 3)}; - - if self.get_type() in type_dict: - return type_dict[self.get_type()](start_height); - else: - return start_height; - def create_labels(self): """Create the labels for the socket.""" Element.create_labels(self) self._bg_color = Colors.get_color(self.get_color()) - #create the layout + # create the layout layout = gtk.DrawingArea().create_pango_layout('') layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self, font=PORT_FONT)) self.w, self.h = layout.get_pixel_size() - self.W, self.H = 2*PORT_LABEL_PADDING + self.w, 2*PORT_LABEL_PADDING+self.h - self.H = self.modify_height(self.H) - #create the pixmap + self.W = 2 * PORT_LABEL_PADDING + self.w + self.H = 2 * PORT_LABEL_PADDING + self.h * ( + 3 if self.get_type() == 'bus' else 1) + self.H += self.H % 2 + # create the pixmap pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h) gc = pixmap.new_gc() gc.set_foreground(self._bg_color) pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h) pixmap.draw_layout(gc, 0, 0, layout) - #create vertical and horizontal pixmaps + # create vertical and horizontal pixmaps self.horizontal_label = pixmap if self.is_vertical(): self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w) diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py index e7b05519ea..109fe5e85e 100644 --- a/grc/gui/Preferences.py +++ b/grc/gui/Preferences.py @@ -18,24 +18,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import ConfigParser -import os + _platform = None _config_parser = ConfigParser.ConfigParser() -def file_extension(): return '.'+_platform.get_key() + +def file_extension(): + return '.'+_platform.get_key() + def load(platform): global _platform _platform = platform - #create sections + # create sections _config_parser.add_section('main') _config_parser.add_section('files_open') - try: _config_parser.read(_platform.get_prefs_file()) - except: pass + try: + _config_parser.read(_platform.get_prefs_file()) + except: + pass + + def save(): - try: _config_parser.write(open(_platform.get_prefs_file(), 'w')) - except: pass + try: + _config_parser.write(open(_platform.get_prefs_file(), 'w')) + except: + pass + ########################################################################### # Special methods for specific program functionalities @@ -46,49 +56,63 @@ def main_window_size(size=None): _config_parser.set('main', 'main_window_width', size[0]) _config_parser.set('main', 'main_window_height', size[1]) else: - try: return ( - _config_parser.getint('main', 'main_window_width'), - _config_parser.getint('main', 'main_window_height'), - ) - except: return (1, 1) - -def file_open(file=None): - if file is not None: _config_parser.set('main', 'file_open', file) + try: + w = _config_parser.getint('main', 'main_window_width') + h = _config_parser.getint('main', 'main_window_height') + except: + w, h = 1, 1 + return w, h + + +def file_open(filename=None): + if filename is not None: + _config_parser.set('main', 'file_open', filename) else: - try: return _config_parser.get('main', 'file_open') - except: return '' + try: + return _config_parser.get('main', 'file_open') + except: + return '' + def files_open(files=None): if files is not None: - _config_parser.remove_section('files_open') #clear section + _config_parser.remove_section('files_open') # clear section _config_parser.add_section('files_open') - for i, file in enumerate(files): - _config_parser.set('files_open', 'file_open_%d'%i, file) + for i, filename in enumerate(files): + _config_parser.set('files_open', 'file_open_%d' % i, filename) + else: - files = list() - i = 0 - while True: - try: - files.append(_config_parser.get('files_open', 'file_open_%d'%i)) - except: - return files - i += 1 + try: + files = [value for name, value in _config_parser.items('files_open') + if name.startswith('file_open_')] + except: + files = [] + return files + def reports_window_position(pos=None): - if pos is not None: _config_parser.set('main', 'reports_window_position', pos) + if pos is not None: + _config_parser.set('main', 'reports_window_position', pos) else: - try: return _config_parser.getint('main', 'reports_window_position') or 1 #greater than 0 - except: return -1 + try: + return _config_parser.getint('main', 'reports_window_position') or 1 #greater than 0 + except: + return -1 + def blocks_window_position(pos=None): - if pos is not None: _config_parser.set('main', 'blocks_window_position', pos) + if pos is not None: + _config_parser.set('main', 'blocks_window_position', pos) else: - try: return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0 - except: return -1 + try: + return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0 + except: + return -1 + -def bool_entry(key, active=None, default=True): - if active is not None: - _config_parser.set('main', key, active) +def bool_entry(key, value=None, default=True): + if value is not None: + _config_parser.set('main', key, value) else: try: return _config_parser.getboolean('main', key) diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index 470e2d59d8..abf242691f 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -21,9 +21,11 @@ import pygtk pygtk.require('2.0') import gtk +import Actions from Dialogs import SimpleTextDisplay -from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT +from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT, FONT_SIZE import Utils +import pango TAB_LABEL_MARKUP_TMPL="""\ #set $foreground = $valid and 'black' or 'red' @@ -101,6 +103,20 @@ class PropsDialog(gtk.Dialog): self._docs_box.add_with_viewport(self._docs_text_display) notebook.append_page(self._docs_box, gtk.Label("Documentation")) + # Generated code for the block + if Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB.get_active(): + self._code_text_display = code_view = SimpleTextDisplay() + code_view.set_wrap_mode(gtk.WRAP_NONE) + code_view.get_buffer().create_tag('b', weight=pango.WEIGHT_BOLD) + code_view.modify_font(pango.FontDescription( + 'monospace %d' % FONT_SIZE)) + code_box = gtk.ScrolledWindow() + code_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + code_box.add_with_viewport(self._code_text_display) + notebook.append_page(code_box, gtk.Label("Generated Code")) + else: + self._code_text_display = None + # Error Messages for the block self._error_messages_text_display = SimpleTextDisplay() self._error_box = gtk.ScrolledWindow() @@ -127,9 +143,10 @@ class PropsDialog(gtk.Dialog): true if changed """ old_hash = self._hash - #create a tuple of things from each param that affects the params box + # create a tuple of things from each param that affects the params box self._hash = hash(tuple([( - hash(param), param.get_type(), param.get_hide() == 'all', + hash(param), param.get_name(), param.get_type(), + param.get_hide() == 'all', ) for param in self._block.get_params()])) return self._hash != old_hash @@ -138,7 +155,7 @@ class PropsDialog(gtk.Dialog): A change occurred within a param: Rewrite/validate the block and update the gui. """ - #update for the block + # update for the block self._block.rewrite() self._block.validate() self._update_gui() @@ -155,9 +172,9 @@ class PropsDialog(gtk.Dialog): Update the documentation block. Hide the box if there are no docs. """ - #update the params box + # update the params box if self._params_changed(): - #hide params box before changing + # hide params box before changing for tab, label, vbox in self._params_boxes: vbox.hide_all() # empty the params box @@ -173,17 +190,38 @@ class PropsDialog(gtk.Dialog): input_widget = param.get_input(self._handle_changed, self._activate_apply) vbox.pack_start(input_widget, input_widget.expand) label.set_markup(Utils.parse_template(TAB_LABEL_MARKUP_TMPL, valid=box_all_valid, tab=tab)) - #show params box with new params + # show params box with new params vbox.show_all() - #update the errors box + # update the errors box if self._block.is_valid(): self._error_box.hide() else: self._error_box.show() messages = '\n\n'.join(self._block.get_error_messages()) self._error_messages_text_display.set_text(messages) - #update the docs box + # update the docs box self._docs_text_display.set_text(self._block.get_doc()) + # update the generated code + self._update_generated_code_page() + + def _update_generated_code_page(self): + if not self._code_text_display: + return # user disabled code preview + + buffer = self._code_text_display.get_buffer() + block = self._block + + def insert(header, text): + if not text: + return + buffer.insert_with_tags_by_name(buffer.get_end_iter(), header, 'b') + buffer.insert(buffer.get_end_iter(), text) + + buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) + insert('# Imports\n', '\n'.join(block.get_imports())) + if block.get_key().startswith('variable'): + insert('\n\n# Variables\n', block.get_var_make()) + insert('\n\n# Blocks\n', block.get_make()) def _handle_key_press(self, widget, event): """ @@ -209,3 +247,5 @@ class PropsDialog(gtk.Dialog): self.set_response_sensitive(gtk.RESPONSE_APPLY, False) return True return False + + diff --git a/grc/python/Block.py b/grc/python/Block.py index 48b827792e..303aa85ed5 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -17,22 +17,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from collections import defaultdict +import itertools +import collections +from .. base.Constants import BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI from .. base.Block import Block as _Block from .. gui.Block import Block as _GUIBlock + from . FlowGraph import _variable_matcher import extract_docs -class Block(_Block, _GUIBlock): - - def is_virtual_sink(self): return self.get_key() == 'virtual_sink' - def is_virtual_source(self): return self.get_key() == 'virtual_source' - ##for make source to keep track of indexes - _source_count = 0 - ##for make sink to keep track of indexes - _sink_count = 0 +class Block(_Block, _GUIBlock): def __init__(self, flow_graph, n): """ @@ -52,9 +48,9 @@ class Block(_Block, _GUIBlock): self._var_make = n.find('var_make') self._checks = n.findall('check') self._callbacks = n.findall('callback') - self._throttle = n.find('throttle') or '' self._bus_structure_source = n.find('bus_structure_source') or '' self._bus_structure_sink = n.find('bus_structure_sink') or '' + self.port_counters = [itertools.count(), itertools.count()] #build the block _Block.__init__( self, @@ -78,8 +74,6 @@ class Block(_Block, _GUIBlock): except: return '' - def throttle(self): return bool(self._throttle) - def validate(self): """ Validate this block. @@ -102,13 +96,22 @@ class Block(_Block, _GUIBlock): self.get_parent().evaluate(value) except Exception as err: self.add_error_message('Value "%s" cannot be evaluated:\n%s' % (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') - for label, option in (('WX GUI', 'wx_gui'), ('QT GUI', 'qt_gui')): - if self.get_name().startswith(label) and current_generate_option != option: + + def check_generate_mode(label, flag, valid_options): + block_requires_mode = ( + flag in self.get_flags() or + 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 " + repr(current_generate_option)) + check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',)) + check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui')) + def rewrite(self): """ Add and remove ports to adjust for the nports. @@ -130,13 +133,13 @@ class Block(_Block, _GUIBlock): master_port.remove_clone(port) ports.remove(port) # add more cloned ports - for i in range(num_ports, nports): + for j in range(num_ports, nports): port = master_port.add_clone() - ports.insert(ports.index(master_port) + i, port) + ports.insert(ports.index(master_port) + j, port) self.back_ofthe_bus(ports) # renumber non-message/-msg ports - domain_specific_port_index = defaultdict(int) + domain_specific_port_index = collections.defaultdict(int) for port in filter(lambda p: p.get_key().isdigit(), ports): domain = port.get_domain() port._key = str(domain_specific_port_index[domain]) @@ -204,3 +207,9 @@ class Block(_Block, _GUIBlock): if 'self.' in callback: return callback return 'self.%s.%s'%(self.get_id(), callback) return map(make_callback, self._callbacks) + + def is_virtual_sink(self): + return self.get_key() == 'virtual_sink' + + def is_virtual_source(self): + return self.get_key() == 'virtual_source' diff --git a/grc/python/Constants.py b/grc/python/Constants.py index 79ff8bab35..1df1fc492b 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -50,31 +50,55 @@ FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') + +# Updating colors. Using the standard color pallette from: +# http://www.google.com/design/spec/style/color.html#color-color-palette +# Most are based on the main, primary color standard. Some are within +# that color's spectrum when it was deemed necessary. +GRC_COLOR_BROWN = '#795548' +GRC_COLOR_BLUE = '#2196F3' +GRC_COLOR_LIGHT_GREEN = '#8BC34A' +GRC_COLOR_GREEN = '#4CAF50' +GRC_COLOR_AMBER = '#FFC107' +GRC_COLOR_PURPLE = '#9C27B0' +GRC_COLOR_CYAN = '#00BCD4' +GRC_COLOR_GR_ORANGE = '#FF6905' +GRC_COLOR_ORANGE = '#F57C00' +GRC_COLOR_LIME = '#CDDC39' +GRC_COLOR_TEAL = '#009688' +GRC_COLOR_YELLOW = '#FFEB3B' +GRC_COLOR_PINK = '#F50057' +GRC_COLOR_LIGHT_PURPLE = '#E040FB' +GRC_COLOR_DARK_GREY = '#72706F' +GRC_COLOR_GREY = '#BDBDBD' +GRC_COLOR_WHITE = '#FFFFFF' + + CORE_TYPES = ( #name, key, sizeof, color - ('Complex Float 64', 'fc64', 16, '#CC8C69'), - ('Complex Float 32', 'fc32', 8, '#3399FF'), - ('Complex Integer 64', 'sc64', 16, '#66CC00'), - ('Complex Integer 32', 'sc32', 8, '#33cc66'), - ('Complex Integer 16', 'sc16', 4, '#cccc00'), - ('Complex Integer 8', 'sc8', 2, '#cc00cc'), - ('Float 64', 'f64', 8, '#66CCCC'), - ('Float 32', 'f32', 4, '#FF8C69'), - ('Integer 64', 's64', 8, '#99FF33'), - ('Integer 32', 's32', 4, '#00FF99'), - ('Integer 16', 's16', 2, '#FFFF66'), - ('Integer 8', 's8', 1, '#FF66FF'), - ('Message Queue', 'msg', 0, '#777777'), - ('Async Message', 'message', 0, '#C0C0C0'), - ('Bus Connection', 'bus', 0, '#FFFFFF'), - ('Wildcard', '', 0, '#FFFFFF'), + ('Complex Float 64', 'fc64', 16, GRC_COLOR_BROWN), + ('Complex Float 32', 'fc32', 8, GRC_COLOR_BLUE), + ('Complex Integer 64', 'sc64', 16, GRC_COLOR_LIGHT_GREEN), + ('Complex Integer 32', 'sc32', 8, GRC_COLOR_GREEN), + ('Complex Integer 16', 'sc16', 4, GRC_COLOR_AMBER), + ('Complex Integer 8', 'sc8', 2, GRC_COLOR_PURPLE), + ('Float 64', 'f64', 8, GRC_COLOR_CYAN), + ('Float 32', 'f32', 4, GRC_COLOR_ORANGE), + ('Integer 64', 's64', 8, GRC_COLOR_LIME), + ('Integer 32', 's32', 4, GRC_COLOR_TEAL), + ('Integer 16', 's16', 2, GRC_COLOR_YELLOW), + ('Integer 8', 's8', 1, GRC_COLOR_LIGHT_PURPLE), + ('Message Queue', 'msg', 0, GRC_COLOR_DARK_GREY), + ('Async Message', 'message', 0, GRC_COLOR_GREY), + ('Bus Connection', 'bus', 0, GRC_COLOR_WHITE), + ('Wildcard', '', 0, GRC_COLOR_WHITE), ) ALIAS_TYPES = { - 'complex' : (8, '#3399FF'), - 'float' : (4, '#FF8C69'), - 'int' : (4, '#00FF99'), - 'short' : (2, '#FFFF66'), - 'byte' : (1, '#FF66FF'), + 'complex' : (8, GRC_COLOR_BLUE), + 'float' : (4, GRC_COLOR_ORANGE), + 'int' : (4, GRC_COLOR_TEAL), + 'short' : (2, GRC_COLOR_YELLOW), + 'byte' : (1, GRC_COLOR_LIGHT_PURPLE), } TYPE_TO_COLOR = dict() diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index d7337b8a96..49530af8a3 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import expr_utils from .. base.FlowGraph import FlowGraph as _FlowGraph from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph -from .. base.odict import odict import re _variable_matcher = re.compile('^(variable\w*)$') diff --git a/grc/python/Generator.py b/grc/python/Generator.py index 14a5ee7e2a..f807b59ad1 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -27,6 +27,7 @@ from Cheetah.Template import Template from .. gui import Messages from .. base import ParseXML from .. base import odict +from .. base.Constants import BLOCK_FLAG_NEED_QT_GUI from . Constants import TOP_BLOCK_FILE_MODE, FLOW_GRAPH_TEMPLATE, \ XTERM_EXECUTABLE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, BLOCK_DTD @@ -47,9 +48,13 @@ class Generator(object): """ self._generate_options = flow_graph.get_option('generate_options') if self._generate_options == 'hb': - self._generator = HierBlockGenerator(flow_graph, file_path) + generator_cls = HierBlockGenerator + elif self._generate_options == 'hb_qt_gui': + generator_cls = QtHierBlockGenerator else: - self._generator = TopBlockGenerator(flow_graph, file_path) + generator_cls = TopBlockGenerator + + self._generator = generator_cls(flow_graph, file_path) def get_generate_options(self): return self._generate_options @@ -86,8 +91,8 @@ class TopBlockGenerator(object): def write(self): """generate output and write it to files""" # do throttle warning - throttling_blocks = filter(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks()) - if not throttling_blocks and self._generate_options != 'hb': + throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks()) + if not throttling_blocks and not self._generate_options.startswith('hb'): Messages.send_warning("This flow graph may not have flow control: " "no audio or RF hardware blocks found. " "Add a Misc->Throttle block to your flow " @@ -165,18 +170,65 @@ class TopBlockGenerator(object): except: pass return code + blocks = expr_utils.sort_objects( - self._flow_graph.get_enabled_blocks(), + filter(lambda b: b.get_enabled() and not b.get_bypassed(), self._flow_graph.get_blocks()), lambda b: b.get_id(), _get_block_sort_text ) - # list of regular blocks (all blocks minus the special ones) + # List of regular blocks (all blocks minus the special ones) blocks = filter(lambda b: b not in (imports + parameters), blocks) - # list of connections where each endpoint is enabled (sorted by domains, block names) - connections = filter(lambda c: not (c.is_bus() or c.is_msg()), self._flow_graph.get_enabled_connections()) + + # Filter out virtual sink connections + cf = lambda c: not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink()) + connections = filter(cf, self._flow_graph.get_enabled_connections()) + + # Get the virtual blocks and resolve their conenctions + virtual = filter(lambda c: c.get_source().get_parent().is_virtual_source(), connections) + for connection in virtual: + source = connection.get_source().resolve_virtual_source() + sink = connection.get_sink() + resolved = self._flow_graph.get_parent().Connection(flow_graph=self._flow_graph, porta=source, portb=sink) + connections.append(resolved) + # Remove the virtual connection + connections.remove(connection) + + # Bypassing blocks: Need to find all the enabled connections for the block using + # the *connections* object rather than get_connections(). Create new connections + # that bypass the selected block and remove the existing ones. This allows adjacent + # bypassed blocks to see the newly created connections to downstream blocks, + # allowing them to correctly construct bypass connections. + bypassed_blocks = self._flow_graph.get_bypassed_blocks() + for block in bypassed_blocks: + # Get the upstream connection (off of the sink ports) + # Use *connections* not get_connections() + get_source_connection = lambda c: c.get_sink() == block.get_sinks()[0] + source_connection = filter(get_source_connection, connections) + # The source connection should never have more than one element. + assert (len(source_connection) == 1) + + # Get the source of the connection. + source_port = source_connection[0].get_source() + + # Loop through all the downstream connections + get_sink_connections = lambda c: c.get_source() == block.get_sources()[0] + for sink in filter(get_sink_connections, connections): + if not sink.get_enabled(): + # Ignore disabled connections + continue + sink_port = sink.get_sink() + connection = self._flow_graph.get_parent().Connection(flow_graph=self._flow_graph, porta=source_port, portb=sink_port) + connections.append(connection) + # Remove this sink connection + connections.remove(sink) + # Remove the source connection + connections.remove(source_connection[0]) + + # List of connections where each endpoint is enabled (sorted by domains, block names) connections.sort(key=lambda c: ( c.get_source().get_domain(), c.get_sink().get_domain(), c.get_source().get_parent().get_id(), c.get_sink().get_parent().get_id() )) + connection_templates = self._flow_graph.get_parent().get_connection_templates() msgs = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) # list of variable names @@ -329,3 +381,29 @@ class HierBlockGenerator(TopBlockGenerator): n = {'block': block_n} return n + + +class QtHierBlockGenerator(HierBlockGenerator): + + def _build_block_n_from_flow_graph_io(self): + n = HierBlockGenerator._build_block_n_from_flow_graph_io(self) + block_n = n['block'] + + if not block_n['name'].upper().startswith('QT GUI'): + block_n['name'] = 'QT GUI ' + block_n['name'] + + block_n.insert_after('category', 'flags', BLOCK_FLAG_NEED_QT_GUI) + + gui_hint_param = odict() + gui_hint_param['name'] = 'GUI Hint' + gui_hint_param['key'] = 'gui_hint' + gui_hint_param['value'] = '' + gui_hint_param['type'] = 'gui_hint' + gui_hint_param['hide'] = 'part' + block_n['param'].append(gui_hint_param) + + block_n['make'] += ( + "\n#set $win = 'self.%s' % $id" + "\n${gui_hint()($win)}" + ) + return n diff --git a/grc/python/Port.py b/grc/python/Port.py index 765e1d7423..249d7aed71 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -100,14 +100,11 @@ class Port(_Port, _GUIPort): elif n['domain'] == GR_MESSAGE_DOMAIN: n['key'] = n['name'] n['type'] = 'message' # for port color - if n['type'] == 'msg': n['key'] = 'msg' - if dir == 'source' and not n.find('key'): - n['key'] = str(block._source_count) - block._source_count += 1 - if dir == 'sink' and not n.find('key'): - n['key'] = str(block._sink_count) - block._sink_count += 1 - #build the port + if n['type'] == 'msg': + n['key'] = 'msg' + if not n.find('key'): + n['key'] = str(next(block.port_counters[dir == 'source'])) + # build the port _Port.__init__( self, block=block, diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 8cfd3dd392..145f4d8610 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -25,7 +25,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Top level element. A block contains a name, ...parameters list, and list of IO ports. --> -<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, var_value?, make, callback*, param_tab_order?, param*, bus_sink?, bus_source?, check*, sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> +<!ELEMENT block (name, key, category?, throttle?, flags?, import*, var_make?, var_value?, + make, callback*, param_tab_order?, param*, bus_sink?, bus_source?, check*, + sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> <!-- Sub level elements. --> @@ -64,3 +66,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <!ELEMENT callback (#PCDATA)> <!ELEMENT optional (#PCDATA)> <!ELEMENT throttle (#PCDATA)> +<!ELEMENT flags (#PCDATA)> diff --git a/grc/python/default_flow_graph.grc b/grc/python/default_flow_graph.grc index 53d39e885a..059509d34b 100644 --- a/grc/python/default_flow_graph.grc +++ b/grc/python/default_flow_graph.grc @@ -14,7 +14,7 @@ </param> <param> <key>_coordinate</key> - <value>(10, 10)</value> + <value>(8, 8)</value> </param> <param> <key>_rotation</key> @@ -33,7 +33,7 @@ </param> <param> <key>_coordinate</key> - <value>(10, 170)</value> + <value>(8, 160)</value> </param> <param> <key>_rotation</key> diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 814b513213..3cc16e7e14 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -1,4 +1,4 @@ -#if $generate_options != 'hb' +#if not $generate_options.startswith('hb') #!/usr/bin/env python2 #end if ######################################################## @@ -61,7 +61,8 @@ sys.path.append(os.environ.get('GRC_HIER_PATH', os.path.expanduser('~/.grc_gnura #end if #for $imp in $imports -$(imp.replace(" # grc-generated hier_block", "")) +##$(imp.replace(" # grc-generated hier_block", "")) +$imp #end for ######################################################## @@ -76,6 +77,7 @@ $(imp.replace(" # grc-generated hier_block", "")) #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): @@ -85,7 +87,7 @@ class $(class_name)(grc_wxgui.top_block_gui): self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) #end if #elif $generate_options == 'qt_gui' -from distutils.version import StrictVersion + class $(class_name)(gr.top_block, Qt.QWidget): def __init__($param_str): @@ -110,16 +112,21 @@ class $(class_name)(gr.top_block, Qt.QWidget): self.settings = Qt.QSettings("GNU Radio", "$class_name") self.restoreGeometry(self.settings.value("geometry").toByteArray()) - #elif $generate_options == 'no_gui' + class $(class_name)(gr.top_block): def __init__($param_str): gr.top_block.__init__(self, "$title") -#elif $generate_options == 'hb' +#elif $generate_options.startswith('hb') #set $in_sigs = $flow_graph.get_hier_block_stream_io('in') #set $out_sigs = $flow_graph.get_hier_block_stream_io('out') + +#if $generate_options == 'hb_qt_gui' +class $(class_name)(gr.hier_block2, Qt.QWidget): +#else class $(class_name)(gr.hier_block2): +#end if #def make_io_sig($io_sigs) #set $size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in $io_sigs] #if len($io_sigs) == 0 @@ -143,6 +150,14 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) #for $pad in $flow_graph.get_hier_block_message_io('out') self.message_port_register_hier_in("$pad['label']") #end for + #if $generate_options == 'hb_qt_gui' + + Qt.QWidget.__init__(self) + self.top_layout = Qt.QVBoxLayout() + self.top_grid_layout = Qt.QGridLayout() + self.top_layout.addLayout(self.top_grid_layout) + self.setLayout(self.top_layout) + #end if #end if #if $flow_graph.get_option('thread_safe_setters') @@ -240,15 +255,8 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) #for $con in $connections #set global $source = $con.get_source() #set global $sink = $con.get_sink() - ##resolve virtual sources to the actual sources - #if $source.get_parent().is_virtual_source() - #set global $source = $source.resolve_virtual_source() - #end if - ##do not generate connections with virtual sinks - #if not $sink.get_parent().is_virtual_sink() - #include source=$connection_templates[($source.get_domain(), $sink.get_domain())] + #include source=$connection_templates[($source.get_domain(), $sink.get_domain())] - #end if #end for ######################################################## @@ -304,7 +312,7 @@ $param.get_make()#slurp #end if $short_id#slurp #end def -#if $generate_options != 'hb' +#if not $generate_options.startswith('hb') if __name__ == '__main__': parser = OptionParser(option_class=eng_option, usage="%prog: [options]") @@ -334,7 +342,8 @@ if __name__ == '__main__': tb.Wait() #end if #elif $generate_options == 'qt_gui' - if(StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0")): + from distutils.version import StrictVersion + if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"): Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster')) qapp = Qt.QApplication(sys.argv) tb = $(class_name)($(', '.join($params_eq_list))) @@ -346,6 +355,7 @@ if __name__ == '__main__': #end if #end if tb.show() + def quitting(): tb.stop() tb.wait() @@ -358,7 +368,7 @@ if __name__ == '__main__': sys.stderr.write("Monitor '{0}' does not have an enable ('en') parameter.".format("tb.$m.get_id()")) #end for qapp.exec_() - tb = None #to clean up Qt widgets + tb = None # to clean up Qt widgets #elif $generate_options == 'no_gui' tb = $(class_name)($(', '.join($params_eq_list))) #set $run_options = $flow_graph.get_option('run_options') diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index 6b7a8295ba..77345bed1a 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -61,6 +61,14 @@ def show_gtk_error_dialog(title, message): d.run() +def check_gtk_init(): + try: + gtk.init_check() + except RuntimeError: + print 'GTK initialization failed - bailing' + exit(-1) + + def check_gnuradio_import(): try: from gnuradio import gr @@ -114,6 +122,7 @@ def main(): if __name__ == '__main__': + check_gtk_init() check_gnuradio_import() ensure_blocks_path() main() |