[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] [gnuradio] 89/101: Merge remote-tracking branch 'upstr
From: |
git |
Subject: |
[Commit-gnuradio] [gnuradio] 89/101: Merge remote-tracking branch 'upstream/next' into gtk3 |
Date: |
Thu, 16 Mar 2017 14:58:13 +0000 (UTC) |
This is an automated email from the git hooks/post-receive script.
jcorgan pushed a commit to branch python3
in repository gnuradio.
commit 7f25c0120fc7bc6a6eeee87878cf387647d51614
Merge: e1acf2d 1d50d70
Author: Sebastian Koslowski <address@hidden>
Date: Thu Nov 17 21:20:55 2016 +0100
Merge remote-tracking branch 'upstream/next' into gtk3
CMakeLists.txt | 4 +
README.hacking | 60 +-
cmake/Modules/FindQwt.cmake | 3 +-
cmake/Modules/GrBoost.cmake | 7 +-
cmake/Modules/GrPlatform.cmake | 8 +
cmake/msvc/config.h | 4 -
docs/doxygen/other/doxypy.py | 56 +-
docs/sphinx/source/digital_blocks.rst | 1 -
docs/sphinx/source/index.rst | 1 -
dtools/bin/update_fsf_address | 26 +-
gnuradio-runtime/examples/mp-sched/plot_flops.py | 4 +-
gnuradio-runtime/examples/mp-sched/synthetic.py | 39 +-
.../examples/mp-sched/wfm_rcv_pll_to_wav.py | 36 +-
gnuradio-runtime/examples/network/audio_sink.py | 29 +-
gnuradio-runtime/examples/network/audio_source.py | 31 +-
.../examples/network/dial_tone_sink.py | 29 +-
.../examples/network/dial_tone_source.py | 39 +-
gnuradio-runtime/examples/network/vector_sink.py | 26 +-
gnuradio-runtime/examples/network/vector_source.py | 23 +-
gnuradio-runtime/lib/constants.cc.in | 19 +
gnuradio-runtime/lib/controlport/rpcmanager.cc | 5 -
.../lib/controlport/rpcserver_selector.cc | 4 +
gnuradio-runtime/lib/flat_flowgraph.cc | 9 +
gnuradio-runtime/lib/pmt/pmt.cc | 12 +-
gnuradio-runtime/lib/top_block.cc | 19 +-
.../python/gnuradio/ctrlport/gr-perf-monitorx | 2 +-
.../python/gnuradio/gr/qa_flowgraph.py | 37 +-
gr-analog/examples/tags/uhd_burst_detector.py | 40 +-
gr-analog/grc/analog_block_tree.xml | 1 -
gr-analog/grc/analog_cpfsk_bc.xml | 1 +
gr-analog/include/gnuradio/analog/cpfsk_bc.h | 1 +
gr-analog/lib/fastnoise_source_X_impl.cc.t | 4 +-
gr-audio/doc/audio.dox | 8 +-
gr-audio/examples/python/audio_copy.py | 25 +-
gr-audio/examples/python/audio_play.py | 30 +-
gr-audio/examples/python/audio_to_file.py | 33 +-
gr-audio/examples/python/dial_tone.py | 23 +-
gr-audio/examples/python/dial_tone_daemon.py | 22 +-
gr-audio/examples/python/dial_tone_wav.py | 30 +-
gr-audio/examples/python/mono_tone.py | 28 +-
gr-audio/examples/python/multi_tone.py | 30 +-
gr-audio/examples/python/noise.py | 20 +-
gr-audio/examples/python/spectrum_inversion.py | 26 +-
gr-audio/examples/python/test_resampler.py | 28 +-
gr-audio/lib/windows/windows_sink.cc | 22 +-
gr-audio/lib/windows/windows_sink.h | 14 +-
gr-audio/lib/windows/windows_source.cc | 455 ++++---
gr-audio/lib/windows/windows_source.h | 37 +-
gr-blocks/CMakeLists.txt | 1 -
.../examples/ctrlport/usrp_sink_controller.py | 36 +-
.../examples/ctrlport/usrp_source_controller.py | 36 +-
gr-blocks/grc/blks2_error_rate.xml | 70 --
gr-blocks/grc/blks2_selector.xml | 98 --
gr-blocks/grc/blks2_tcp_sink.xml | 90 --
gr-blocks/grc/blks2_tcp_source.xml | 90 --
gr-blocks/grc/blks2_valve.xml | 73 --
gr-blocks/lib/ConfigChecks.cmake | 7 +
gr-blocks/lib/nlog10_ff_impl.cc | 30 +-
gr-blocks/lib/nlog10_ff_impl.h | 4 +-
gr-blocks/lib/test_tag_variable_rate_ff_impl.cc | 12 +
gr-blocks/python/blocks/qa_nlog10.py | 6 +-
gr-blocks/python/grc_gnuradio/README | 8 -
gr-blocks/python/grc_gnuradio/__init__.py | 1 -
gr-blocks/python/grc_gnuradio/blks2/error_rate.py | 140 ---
gr-blocks/python/grc_gnuradio/blks2/selector.py | 142 ---
gr-blocks/python/grc_gnuradio/blks2/tcp.py | 70 --
gr-digital/CMakeLists.txt | 9 +-
gr-digital/examples/CMakeLists.txt | 7 -
gr-digital/examples/demod/ber_simulation.grc | 1238 --------------------
gr-digital/examples/demod/dpsk_loopback.grc | 878 --------------
gr-digital/examples/demod/gfsk_loopback.grc | 598 ----------
gr-digital/examples/demod/gmsk_loopback.grc | 590 ----------
gr-digital/examples/example_costas.py | 52 +-
gr-digital/examples/example_fll.py | 54 +-
gr-digital/examples/example_timing.py | 60 +-
gr-digital/examples/gen_whitener.py | 17 +-
gr-digital/examples/ofdm/benchmark_rx.py | 124 --
gr-digital/examples/ofdm/benchmark_tx.py | 124 --
gr-digital/examples/ofdm/gr_plot_ofdm.py | 278 -----
gr-digital/examples/ofdm/tunnel.py | 271 -----
gr-digital/grc/blks2_packet_decoder.xml | 77 --
gr-digital/grc/blks2_packet_encoder.xml | 121 --
gr-digital/grc/digital_block_tree.xml | 7 -
gr-digital/grc/digital_dxpsk_demod.xml | 1 +
gr-digital/grc/digital_dxpsk_mod.xml | 1 +
gr-digital/grc/digital_mpsk_receiver_cc.xml | 86 --
gr-digital/grc/digital_ofdm_demod.xml | 143 ---
gr-digital/grc/digital_ofdm_frame_acquisition.xml | 77 --
gr-digital/grc/digital_ofdm_frame_sink.xml | 79 --
gr-digital/grc/digital_ofdm_insert_preamble.xml | 63 -
gr-digital/grc/digital_ofdm_mod.xml | 156 ---
gr-digital/grc/digital_ofdm_sampler.xml | 67 --
gr-digital/grc/digital_ofdm_sync_pn.xml | 61 -
gr-digital/grc/digital_psk_demod.xml | 1 +
gr-digital/grc/digital_psk_mod.xml | 1 +
gr-digital/grc/digital_qam_demod.xml | 1 +
gr-digital/grc/digital_qam_mod.xml | 1 +
gr-digital/include/gnuradio/digital/CMakeLists.txt | 6 -
.../include/gnuradio/digital/mpsk_receiver_cc.h | 147 ---
.../gnuradio/digital/ofdm_frame_acquisition.h | 82 --
.../include/gnuradio/digital/ofdm_frame_sink.h | 71 --
.../gnuradio/digital/ofdm_insert_preamble.h | 79 --
.../include/gnuradio/digital/ofdm_mapper_bcv.h | 67 --
gr-digital/include/gnuradio/digital/ofdm_sampler.h | 57 -
gr-digital/lib/CMakeLists.txt | 6 -
gr-digital/lib/mpsk_receiver_cc_impl.cc | 330 ------
gr-digital/lib/mpsk_receiver_cc_impl.h | 242 ----
gr-digital/lib/msk_timing_recovery_cc_impl.cc | 8 +
gr-digital/lib/ofdm_frame_acquisition_impl.cc | 32 +-
gr-digital/lib/ofdm_frame_sink_impl.cc | 84 +-
gr-digital/lib/ofdm_insert_preamble_impl.cc | 18 +-
gr-digital/lib/ofdm_mapper_bcv_impl.cc | 52 +-
gr-digital/lib/ofdm_sampler_impl.cc | 24 +-
gr-digital/python/digital/qa_mpsk_receiver.py | 146 ---
.../python/digital/qa_ofdm_insert_preamble.py | 178 ---
gr-digital/python/grc_gnuradio/blks2/packet.py | 257 ----
gr-digital/swig/CMakeLists.txt | 83 +-
gr-digital/swig/digital_swig.i | 287 -----
.../CMakeLists.txt => swig/digital_swig.py.in} | 17 +-
gr-digital/swig/digital_swig0.i | 124 ++
gr-digital/swig/digital_swig1.i | 120 ++
gr-digital/swig/digital_swig2.i | 109 ++
gr-dtv/CMakeLists.txt | 1 +
gr-dtv/examples/vv003-cr23.grc | 6 +-
gr-dtv/examples/vv018-miso.grc | 6 +-
gr-dtv/grc/dtv_dvbt2_framemapper_cc.xml | 4 +-
gr-dtv/lib/atsc/atsc_sync_impl.cc | 42 +-
gr-dtv/lib/atsc/atsc_sync_impl.h | 10 +-
gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.cc | 12 -
gr-dtv/lib/dvbt2/dvbt2_interleaver_bb_impl.cc | 50 +-
gr-dtv/swig/CMakeLists.txt | 2 +-
gr-fec/lib/cc_decoder_impl.cc | 3 +
gr-filter/lib/pfb_arb_resampler_ccc_impl.cc | 1 -
gr-filter/lib/pfb_arb_resampler_ccf_impl.cc | 1 -
gr-filter/lib/pfb_arb_resampler_fff_impl.cc | 1 -
gr-qtgui/apps/qt_digital.py | 307 -----
gr-qtgui/apps/qt_digital_window.py | 161 ---
gr-qtgui/apps/qt_digital_window.ui | 342 ------
gr-qtgui/examples/c++/CMakeLists.txt | 17 +-
gr-qtgui/include/gnuradio/qtgui/freqcontrolpanel.h | 2 +
gr-qtgui/include/gnuradio/qtgui/utils.h | 3 +
gr-qtgui/lib/FrequencyDisplayPlot.cc | 1 -
gr-qtgui/lib/TimeDomainDisplayPlot.cc | 8 +-
gr-qtgui/lib/VectorDisplayPlot.cc | 22 +-
gr-qtgui/lib/const_sink_c_impl.cc | 6 +-
gr-qtgui/lib/edit_box_msg_impl.cc | 6 +-
gr-qtgui/lib/freq_sink_c_impl.cc | 6 +-
gr-qtgui/lib/freq_sink_f_impl.cc | 6 +-
gr-qtgui/lib/freqcontrolpanel.cc | 19 +-
gr-qtgui/lib/freqdisplayform.cc | 6 +
gr-qtgui/lib/histogram_sink_f_impl.cc | 6 +-
gr-qtgui/lib/qtgui_util.cc | 12 +
gr-qtgui/lib/sink_c_impl.cc | 6 +-
gr-qtgui/lib/sink_f_impl.cc | 6 +-
gr-qtgui/lib/time_raster_sink_b_impl.cc | 6 +-
gr-qtgui/lib/time_raster_sink_f_impl.cc | 6 +-
gr-qtgui/lib/time_sink_c_impl.cc | 6 +-
gr-qtgui/lib/time_sink_f_impl.cc | 6 +-
gr-qtgui/lib/vector_sink_f_impl.cc | 6 +-
gr-qtgui/lib/waterfall_sink_c_impl.cc | 6 +-
gr-qtgui/lib/waterfall_sink_f_impl.cc | 6 +-
gr-qtgui/python/qtgui/CMakeLists.txt | 2 +
gr-qtgui/python/qtgui/__init__.py | 1 +
gr-qtgui/python/qtgui/range.py.cmakein | 2 +
.../python/qtgui/util.py.cmakein | 22 +-
gr-trellis/examples/python/CMakeLists.txt | 1 -
gr-trellis/examples/python/test_cpm.py | 161 ---
gr-trellis/swig/CMakeLists.txt | 63 +-
.../swig/trellis_swig.py.in | 22 +-
gr-trellis/swig/trellis_swig0.i | 117 ++
.../swig/{trellis_swig.i => trellis_swig1.i} | 83 +-
gr-uhd/CMakeLists.txt | 2 +-
gr-uhd/apps/uhd_app.py | 24 +-
gr-uhd/include/gnuradio/uhd/usrp_sink.h | 35 +-
gr-uhd/include/gnuradio/uhd/usrp_source.h | 35 +-
gr-uhd/lib/CMakeLists.txt | 2 +-
gr-uhd/lib/usrp_block_impl.cc | 38 +-
gr-uhd/lib/usrp_block_impl.h | 2 +-
gr-uhd/lib/usrp_sink_impl.cc | 73 +-
gr-uhd/lib/usrp_sink_impl.h | 12 +-
gr-uhd/lib/usrp_source_impl.cc | 96 +-
gr-uhd/lib/usrp_source_impl.h | 13 +-
gr-uhd/swig/CMakeLists.txt | 2 +-
.../gr-newmod/cmake/Modules/GrPlatform.cmake | 8 +
gr-utils/python/utils/CMakeLists.txt | 1 -
gr-utils/python/utils/gr_plot_const | 4 +-
gr-utils/python/utils/gr_read_file_metadata | 4 +-
gr-utils/python/utils/grcc | 89 --
gr-zeromq/examples/python/client.py | 20 +-
gr-zeromq/examples/python/gui.py | 22 +-
gr-zeromq/examples/python/server.py | 18 +-
grc/compiler.py | 76 ++
grc/core/Config.py | 4 +-
grc/core/FlowGraph.py | 15 +-
grc/core/Param.py | 2 +-
grc/core/Platform.py | 23 +-
grc/core/generator/flow_graph.tmpl | 3 +-
grc/core/utils/epy_block_io.py | 2 +-
.../blks2/__init__.py => grc/core/utils/shlex.py | 39 +-
grc/gui/Application.py | 3 +-
grc/gui/Constants.py | 10 +-
grc/gui/Dialogs.py | 2 +-
grc/gui/Executor.py | 35 +-
grc/gui/ParamWidgets.py | 4 +-
grc/gui/PropsDialog.py | 4 +-
grc/gui/Utils.py | 10 +
grc/gui/VariableEditor.py | 10 +-
grc/gui/canvas/block.py | 3 +-
grc/scripts/CMakeLists.txt | 2 +-
grc/scripts/gnuradio-companion | 34 +-
grc/scripts/grcc | 64 +
grc/tests/resources/test_compiler.grc | 253 ++++
.../__init__.py => grc/tests/test_compiler.py | 31 +-
213 files changed, 2434 insertions(+), 10486 deletions(-)
diff --cc grc/compiler.py
index 0000000,0cda0d9..b2361b8
mode 000000,100755..100755
--- a/grc/compiler.py
+++ b/grc/compiler.py
@@@ -1,0 -1,76 +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(),
++ prefs=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 --cc grc/core/Config.py
index 3455a38,744ad06..cc199a3
--- a/grc/core/Config.py
+++ b/grc/core/Config.py
@@@ -32,10 -32,12 +32,12 @@@ class Config(object)
hier_block_lib_dir = os.environ.get('GRC_HIER_PATH',
Constants.DEFAULT_HIER_BLOCK_LIB_DIR)
- def __init__(self, version, version_parts=None, prefs=None):
- def __init__(self, prefs_file, version, version_parts=None, name=None):
- self.prefs = prefs_file
++ def __init__(self, version, version_parts=None, name=None, prefs=None):
+ self._gr_prefs = prefs if prefs else DummyPrefs()
self.version = version
self.version_parts = version_parts or version[1:].split('-',
1)[0].split('.')[:3]
+ if name:
+ self.name = name
@property
def block_paths(self):
diff --cc grc/core/FlowGraph.py
index 18a5778,ecae11c..bf5bf6d
--- a/grc/core/FlowGraph.py
+++ b/grc/core/FlowGraph.py
@@@ -15,18 -15,17 +15,19 @@@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA
+from __future__ import absolute_import, print_function
+
import imp
-from itertools import ifilter, chain
-from operator import methodcaller, attrgetter
+import time
import re
+from operator import methodcaller
+import collections
+ import sys
-import time
from . import Messages
from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
from .Element import Element
- from .utils import expr_utils
-from .utils import odict, expr_utils, shlex
++from .utils import expr_utils, shlex
_parameter_matcher = re.compile('^(parameter)$')
_monitors_searcher = re.compile('(ctrlport_monitor)')
@@@ -392,7 -420,7 +403,7 @@@ class FlowGraph(Element)
cwd=self.grc_file_path
)
if file_path: # grc file found. load and get block
-
self.parent_platform.load_and_generate_flow_graph(file_path)
- self.platform.load_and_generate_flow_graph(file_path,
hier_only=True)
++
self.parent_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
diff --cc grc/core/Param.py
index 31393b1,fd098b7..9544d79
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@@ -17,19 -17,20 +17,19 @@@ along with this program; if not, write
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
+from __future__ import absolute_import
+
import ast
-import weakref
import re
+import collections
+
+from six.moves import builtins, filter, map, range, zip
from . import Constants
-from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
-from .Element import Element
-from .utils import odict
+from .Element import Element, nop_write
# Blacklist certain ids, its not complete, but should help
- ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'math', 'firdes'] +
dir(builtins)
-import __builtin__
-
-
-ID_BLACKLIST = ['self', 'options', 'gr', 'math', 'firdes'] + dir(__builtin__)
++ID_BLACKLIST = ['self', 'options', 'gr', 'math', 'firdes'] + dir(builtins)
try:
from gnuradio import gr
ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not
attr.startswith('_'))
diff --cc grc/core/Platform.py
index 1e43271,b73dade..73937f1
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@@ -122,10 -125,11 +122,11 @@@ class Platform(Element)
generator.write()
except Exception as e:
Messages.send('>>> Generate Error: {}: {}\n'.format(file_path,
str(e)))
- return False
+ return None, None
- self.load_block_xml(generator.file_path_xml)
- return True
+ if flow_graph.get_option('generate_options').startswith('hb'):
- self.load_block_xml(generator.get_file_path_xml())
++ self.load_block_xml(generator.file_path_xml)
+ return flow_graph, generator.file_path
def build_block_library(self):
"""load the blocks and block tree from the search paths"""
diff --cc grc/gui/Application.py
index 5c643ea,b9f534f..e2290b3
--- a/grc/gui/Application.py
+++ b/grc/gui/Application.py
@@@ -519,21 -515,29 +519,22 @@@ class Application(Gtk.Application)
elif action == Actions.FLOW_GRAPH_NEW:
main.new_page()
if args:
+ flow_graph = main.get_flow_graph()
flow_graph._options_block.get_param('generate_options').set_value(args[0])
- flow_graph_update()
+ flow_graph_update(flow_graph)
elif action == Actions.FLOW_GRAPH_OPEN:
- file_paths = args if args else
OpenFlowGraphFileDialog(page.get_file_path()).run()
- if file_paths: #open a new page for each file, show only the first
+ file_paths = args if args else FileDialogs.OpenFlowGraph(main,
page.file_path).run()
+ if file_paths: # Open a new page for each file, show only the
first
for i,file_path in enumerate(file_paths):
main.new_page(file_path, show=(i==0))
- Preferences.add_recent_file(file_path)
+ self.config.add_recent_file(file_path)
main.tool_bar.refresh_submenus()
main.menu_bar.refresh_submenus()
- main.vars.update_gui()
-
elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
- file_paths =
OpenQSSFileDialog(self.platform.config.install_prefix +
- '/share/gnuradio/themes/').run()
+ file_paths = FileDialogs.OpenQSS(main,
self.platform.config.install_prefix +
+ '/share/gnuradio/themes/').run()
if file_paths:
- try:
- prefs = self.platform.config.prefs
- prefs.set_string("qtgui", "qss", file_paths[0])
- prefs.save()
- except Exception as e:
- Messages.send("Failed to save QSS preference: " + str(e))
+ self.platform.config.default_qss_theme = file_paths[0]
elif action == Actions.FLOW_GRAPH_CLOSE:
main.close_page()
elif action == Actions.FLOW_GRAPH_SAVE:
diff --cc grc/gui/Constants.py
index 0a555b3,f77221e..01ea23e
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@@ -17,16 -17,17 +17,16 @@@ along with this program; if not, write
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import gtk
+from __future__ import absolute_import
+
- from gi.repository import Gtk
++from gi.repository import Gtk, Gdk
from ..core.Constants import *
# default path for the open/save dialogs
- DEFAULT_FILE_PATH = os.getcwd()
+ DEFAULT_FILE_PATH = os.getcwd() if os.name != 'nt' else
os.path.expanduser("~/Documents")
-
-# file extensions
-IMAGE_FILE_EXTENSION = '.png'
-TEXT_FILE_EXTENSION = '.txt'
+FILE_EXTENSION = '.grc'
# name for new/unsaved flow graphs
NEW_FLOGRAPH_TITLE = 'untitled'
@@@ -93,14 -96,10 +93,20 @@@ SCROLL_DISTANCE = 1
# How close the mouse click can be to a line and register a connection select.
LINE_SELECT_SENSITIVITY = 5
-_SCREEN_RESOLUTION = gtk.gdk.screen_get_default().get_resolution()
-DPI_SCALING = _SCREEN_RESOLUTION / 96.0 if _SCREEN_RESOLUTION > 0 else 1.0
+DEFAULT_BLOCK_MODULE_TOOLTIP = """\
+This subtree holds all blocks (from OOT modules) that specify no module name.
\
+The module name is the root category enclosed in square brackets.
+
+Please consider contacting OOT module maintainer for any block in here \
+and kindly ask to update their GRC Block Descriptions or Block Tree to
include a module name."""
+
+
++# _SCREEN = Gdk.Screen.get_default()
++# _SCREEN_RESOLUTION = _SCREEN.get_resolution() if _SCREEN else -1
++# DPI_SCALING = _SCREEN_RESOLUTION / 96.0 if _SCREEN_RESOLUTION > 0 else 1.0
++DPI_SCALING = 1.0 # todo: figure out the GTK3 way (maybe cairo does this for
us
+
+
def update_font_size(font_size):
global PORT_SEPARATION, BLOCK_FONT, PORT_FONT, PARAM_FONT, FONT_SIZE
diff --cc grc/gui/Dialogs.py
index 0eb8895,83ad965..09ecd48
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@@ -90,13 -72,13 +90,13 @@@ class TextDisplay(SimpleTextDisplay)
# for each \b delete one char from the buffer
back_count = 0
start_iter = self.get_buffer().get_end_iter()
- while line[back_count] == '\b':
+ while len(line) > back_count and line[back_count] == '\b':
# stop at the beginning of a line
- if not start_iter.starts_line(): start_iter.backward_char()
+ if not start_iter.starts_line():
+ start_iter.backward_char()
back_count += 1
- # remove chars
+ # remove chars from buffer
self.get_buffer().delete(start_iter, self.get_buffer().get_end_iter())
- # return remaining text
return line[back_count:]
def scroll_to_end(self):
diff --cc grc/gui/Executor.py
index 7168c9e,f5a75ab..7c01d92
--- a/grc/gui/Executor.py
+++ b/grc/gui/Executor.py
@@@ -15,17 -15,14 +15,16 @@@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA
+from __future__ import absolute_import
++
+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 gi.repository import GLib
-from ..core.utils import shlex
from ..core import Messages
@@@ -97,23 -92,5 +90,5 @@@ class ExecFlowGraphThread(threading.Thr
def done(self):
"""Perform end of execution tasks."""
Messages.send_end_exec(self.process.returncode)
- self.page.set_proc(None)
+ self.page.process = None
self.update_callback()
-
-
- ###########################################################
- # back-port from python3
- ###########################################################
- _find_unsafe = re.compile(r'address@hidden:,./-]').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 --cc grc/gui/ParamWidgets.py
index e5657c2,0000000..b79a856
mode 100644,000000..100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@@ -1,300 -1,0 +1,300 @@@
+# Copyright 2007-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 __future__ import absolute_import
+import os
+
+from gi.repository import Gtk, Gdk
+
- from . import Colors
++from . import Colors, Utils
+
+
+class InputParam(Gtk.HBox):
+ """The base class for an input parameter inside the input parameters
dialog."""
+ expand = False
+
+ def __init__(self, param, changed_callback=None, editing_callback=None):
+ Gtk.HBox.__init__(self)
+
+ self.param = param
+ self._changed_callback = changed_callback
+ self._editing_callback = editing_callback
+
+ self.label = Gtk.Label()
- self.label.set_size_request(150, -1)
++ self.label.set_size_request(Utils.scale_scalar(150), -1)
+ self.label.show()
+ self.pack_start(self.label, False, False, 0)
+
+ self.tp = None
+ self._have_pending_changes = False
+
+ self.connect('show', self._update_gui)
+
+ def set_color(self, color):
+ pass
+
+ def set_tooltip_text(self, text):
+ pass
+
+ def get_text(self):
+ raise NotImplementedError()
+
+ def _update_gui(self, *args):
+ """
+ Set the markup, color, tooltip, show/hide.
+ """
+
self.label.set_markup(self.param.format_label_markup(self._have_pending_changes))
+
+ # fixme: find a non-deprecated way to change colors
+ # self.set_color(Colors.PARAM_ENTRY_COLORS.get(
+ # self.param.get_type(), Colors.PARAM_ENTRY_DEFAULT_COLOR)
+ # )
+
+ self.set_tooltip_text(self.param.format_tooltip_text())
+
+ if self.param.get_hide() == 'all':
+ self.hide()
+ else:
+ self.show_all()
+
+ def _mark_changed(self, *args):
+ """
+ Mark this param as modified on change, but validate only on focus-lost
+ """
+ self._have_pending_changes = True
+ self._update_gui()
+ if self._editing_callback:
+ self._editing_callback(self, None)
+
+ def _apply_change(self, *args):
+ """
+ Handle a gui change by setting the new param value,
+ calling the callback (if applicable), and updating.
+ """
+ #set the new value
+ self.param.set_value(self.get_text())
+ #call the callback
+ if self._changed_callback:
+ self._changed_callback(self, None)
+ else:
+ self.param.validate()
+ #gui update
+ self._have_pending_changes = False
+ self._update_gui()
+
+ def _handle_key_press(self, widget, event):
+ if event.keyval == Gdk.KEY_Return and event.get_state() &
Gdk.ModifierType.CONTROL_MASK:
+ self._apply_change(widget, event)
+ return True
+ return False
+
+ def apply_pending_changes(self):
+ if self._have_pending_changes:
+ self._apply_change()
+
+
+class EntryParam(InputParam):
+ """Provide an entry box for strings and numbers."""
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self._input = Gtk.Entry()
+ self._input.set_text(self.param.get_value())
+ self._input.connect('changed', self._mark_changed)
+ self._input.connect('focus-out-event', self._apply_change)
+ self._input.connect('key-press-event', self._handle_key_press)
+ self.pack_start(self._input, True, True, 0)
+
+ def get_text(self):
+ return self._input.get_text()
+
+ def set_color(self, color):
+ self._input.override_background_color(Gtk.StateType.NORMAL, color)
+
+ def set_tooltip_text(self, text):
+ self._input.set_tooltip_text(text)
+
+
+class MultiLineEntryParam(InputParam):
+ """Provide an multi-line box for strings."""
+ expand = True
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self._buffer = Gtk.TextBuffer()
+ self._buffer.set_text(self.param.get_value())
+ self._buffer.connect('changed', self._mark_changed)
+
+ self._view = Gtk.TextView()
+ self._view.set_buffer(self._buffer)
+ self._view.connect('focus-out-event', self._apply_change)
+ self._view.connect('key-press-event', self._handle_key_press)
+ # fixme: add border to TextView
+
+ self._sw = Gtk.ScrolledWindow()
+ self._sw.set_policy(Gtk.PolicyType.AUTOMATIC,
Gtk.PolicyType.AUTOMATIC)
+ self._sw.add(self._view)
+
+ self.pack_start(self._sw, True, True, True)
+
+ def get_text(self):
+ buf = self._buffer
+ text = buf.get_text(buf.get_start_iter(), buf.get_end_iter(),
+ include_hidden_chars=False)
+ return text.strip()
+
+ def set_color(self, color):
+ self._view.override_background_color(Gtk.StateType.NORMAL, color)
+
+ def set_tooltip_text(self, text):
+ self._view.set_tooltip_text(text)
+
+
+class PythonEditorParam(InputParam):
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ button = self._button = Gtk.Button(label='Open in Editor')
+ button.connect('clicked', self.open_editor)
+ self.pack_start(button, True, True, True)
+
+ def open_editor(self, widget=None):
+ self.param.parent_flowgraph.install_external_editor(self.param)
+
+ def get_text(self):
+ pass # we never update the value from here
+
+ def _apply_change(self, *args):
+ pass
+
+
+class EnumParam(InputParam):
+ """Provide an entry box for Enum types with a drop down menu."""
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self._input = Gtk.ComboBoxText()
+ for option_name in self.param.options_names:
+ self._input.append_text(option_name)
+
+ value = self.param.get_value()
+ active_index = self.param.options.index(value)
+ self._input.set_active(active_index)
+
+ self._input.connect('changed', self._editing_callback)
+ self._input.connect('changed', self._apply_change)
+ self.pack_start(self._input, False, False, 0)
+
+ def get_text(self):
+ return self.param.options[self._input.get_active()]
+
+ def set_tooltip_text(self, text):
+ self._input.set_tooltip_text(text)
+
+
+class EnumEntryParam(InputParam):
+ """Provide an entry box and drop down menu for Raw Enum types."""
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self._input = Gtk.ComboBoxText.new_with_entry()
+ for option_name in self.param.options_names:
+ self._input.append_text(option_name)
+
+ value = self.param.get_value()
+ try:
+ active_index = self.param.options.index(value)
+ self._input.set_active(active_index)
+ except ValueError:
+ self._input.set_active(-1)
+ self._input.get_child().set_text(value)
+
+ self._input.connect('changed', self._apply_change)
+ self._input.get_child().connect('changed', self._mark_changed)
+ self._input.get_child().connect('focus-out-event', self._apply_change)
+ self._input.get_child().connect('key-press-event',
self._handle_key_press)
+ self.pack_start(self._input, False, False, 0)
+
+ @property
+ def has_custom_value(self):
+ return self._input.get_active() == -1
+
+ def get_text(self):
+ if self.has_custom_value:
+ return self._input.get_child().get_text()
+ else:
+ return self.param.options[self._input.get_active()]
+
+ def set_tooltip_text(self, text):
+ if self.has_custom_value: # custom entry
+ self._input.get_child().set_tooltip_text(text)
+ else:
+ self._input.set_tooltip_text(text)
+
+ def set_color(self, color):
+ self._input.get_child().modify_base(
+ Gtk.StateType.NORMAL,
+ color if not self.has_custom_value else
Colors.PARAM_ENTRY_ENUM_CUSTOM_COLOR
+ )
+
+
+class FileParam(EntryParam):
+ """Provide an entry box for filename and a button to browse for a file."""
+
+ def __init__(self, *args, **kwargs):
+ EntryParam.__init__(self, *args, **kwargs)
+ self._open_button = Gtk.Button(label='...')
+ self._open_button.connect('clicked', self._handle_clicked)
+ self.pack_start(self._open_button, False, False, 0)
+
+ def _handle_clicked(self, widget=None):
+ """
+ If the button was clicked, open a file dialog in open/save format.
+ Replace the text in the entry with the new filename from the file
dialog.
+ """
+ #get the paths
+ file_path = self.param.is_valid() and self.param.get_evaluated() or ''
+ (dirname, basename) = os.path.isfile(file_path) and
os.path.split(file_path) or (file_path, '')
+ # check for qss theme default directory
+ if self.param.key == 'qt_qss_theme':
+ dirname = os.path.dirname(dirname) # trim filename
+ if not os.path.exists(dirname):
+ config = self.param.parent_platform.config
+ dirname = os.path.join(config.install_prefix,
'/share/gnuradio/themes')
+ if not os.path.exists(dirname):
+ dirname = os.getcwd() # fix bad paths
+
+ #build the dialog
+ if self.param.get_type() == 'file_open':
+ file_dialog = Gtk.FileChooserDialog('Open a Data File...', None,
+ Gtk.FileChooserAction.OPEN,
('gtk-cancel',Gtk.ResponseType.CANCEL,'gtk-open',Gtk.ResponseType.OK))
+ elif self.param.get_type() == 'file_save':
+ file_dialog = Gtk.FileChooserDialog('Save a Data File...', None,
+ Gtk.FileChooserAction.SAVE,
('gtk-cancel',Gtk.ResponseType.CANCEL, 'gtk-save',Gtk.ResponseType.OK))
+ file_dialog.set_do_overwrite_confirmation(True)
+ file_dialog.set_current_name(basename) #show the current filename
+ else:
+ raise ValueError("Can't open file chooser dialog for type " +
repr(self.param.get_type()))
+ file_dialog.set_current_folder(dirname) #current directory
+ file_dialog.set_select_multiple(False)
+ file_dialog.set_local_only(True)
+ if Gtk.ResponseType.OK == file_dialog.run(): #run the dialog
+ file_path = file_dialog.get_filename() #get the file path
+ self._input.set_text(file_path)
+ self._editing_callback()
+ self._apply_change()
+ file_dialog.destroy() # destroy the dialog
diff --cc grc/gui/PropsDialog.py
index 3a0f6ae,a5b46cb..ca1e3c5
--- a/grc/gui/PropsDialog.py
+++ b/grc/gui/PropsDialog.py
@@@ -34,36 -59,32 +34,38 @@@ class PropsDialog(Gtk.Dialog)
"""
Properties dialog constructor.
- Args:
+ Args:%
block: a block instance
"""
- self._hash = 0
- gtk.Dialog.__init__(
+ Gtk.Dialog.__init__(
self,
- title='Properties: %s' % block.get_name(),
- buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT,
- gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
- gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
+ title='Properties: ' + block.name,
+ transient_for=parent,
+ modal=True,
+ destroy_with_parent=True,
+ )
+ self.add_buttons(
+ Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT,
+ Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
+ Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY,
)
- self.set_response_sensitive(gtk.RESPONSE_APPLY, False)
+ self.set_response_sensitive(Gtk.ResponseType.APPLY, False)
- self.set_size_request(Constants.MIN_DIALOG_WIDTH,
Constants.MIN_DIALOG_HEIGHT)
+ self.set_size_request(*Utils.scale(
- (MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT)
++ (Constants.MIN_DIALOG_WIDTH, Constants.MIN_DIALOG_HEIGHT)
+ ))
+
self._block = block
+ self._hash = 0
- vpaned = gtk.VPaned()
- self.vbox.pack_start(vpaned)
+ vpaned = Gtk.VPaned()
+ self.vbox.pack_start(vpaned, True, True, 0)
# Notebook to hold param boxes
- notebook = gtk.Notebook()
+ notebook = self.notebook = Gtk.Notebook()
notebook.set_show_border(False)
notebook.set_scrollable(True) # scroll arrows for page tabs
- notebook.set_tab_pos(gtk.POS_TOP)
+ notebook.set_tab_pos(Gtk.PositionType.TOP)
vpaned.pack1(notebook, True)
# Params boxes for block parameters
diff --cc grc/gui/Utils.py
index 782a7e3,3ab8d20..38fde80
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@@ -106,40 -96,47 +106,50 @@@ def encode(value)
Older versions of glib seg fault if the last byte starts a multi-byte
character.
"""
-
valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8')
- return gobject.markup_escape_text(valid_utf8)
+ return GLib.markup_escape_text(valid_utf8)
-class TemplateParser(object):
- def __init__(self):
- self.cache = {}
+def make_screenshot(flow_graph, file_path, transparent_bg=False):
+ if not file_path:
+ return
- def __call__(self, tmpl_str, **kwargs):
- """
- Parse the template string with the given args.
- Pass in the xml encode method for pango escape chars.
+ x_min, y_min, x_max, y_max = flow_graph.extend
+ padding = Constants.CANVAS_GRID_SIZE
+ width = x_max - x_min + 2 * padding
+ height = y_max - y_min + 2 * padding
- Args:
- tmpl_str: the template as a string
+ if file_path.endswith('.png'):
+ psurf = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ elif file_path.endswith('.pdf'):
+ psurf = cairo.PDFSurface(file_path, width, height)
+ elif file_path.endswith('.svg'):
+ psurf = cairo.SVGSurface(file_path, width, height)
+ else:
+ raise ValueError('Unknown file format')
- Returns:
- a string of the parsed template
- """
- kwargs['encode'] = encode
- template = self.cache.setdefault(tmpl_str, Template.compile(tmpl_str))
- return str(template(namespaces=kwargs))
+ cr = cairo.Context(psurf)
-parse_template = TemplateParser()
+ if not transparent_bg:
+ cr.set_source_rgba(*Colors.FLOWGRAPH_BACKGROUND_COLOR)
+ cr.rectangle(0, 0, width, height)
+ cr.fill()
+ cr.translate(padding - x_min, padding - y_min)
+ flow_graph.draw(cr)
-def align_to_grid(coor, mode=round):
- def align(value):
- return int(mode(value / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
- try:
- return map(align, coor)
- except TypeError:
- x = coor
- return align(coor)
+ if file_path.endswith('.png'):
+ psurf.write_to_png(file_path)
+ if file_path.endswith('.pdf') or file_path.endswith('.svg'):
+ cr.show_page()
+ psurf.finish()
+
+
+ def scale(coor, reverse=False):
- factor = DPI_SCALING if not reverse else 1 / DPI_SCALING
++ factor = Constants.DPI_SCALING if not reverse else 1 /
Constants.DPI_SCALING
+ return tuple(int(x * factor) for x in coor)
+
++
+ def scale_scalar(coor, reverse=False):
- factor = DPI_SCALING if not reverse else 1 / DPI_SCALING
++ factor = Constants.DPI_SCALING if not reverse else 1 /
Constants.DPI_SCALING
+ return int(coor * factor)
diff --cc grc/gui/VariableEditor.py
index 44dd292,45f0bb7..484395b
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@@ -17,11 -17,17 +17,11 @@@ along with this program; if not, write
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from operator import attrgetter
+from __future__ import absolute_import
-import pygtk
-pygtk.require('2.0')
-import gtk
-import gobject
+from gi.repository import Gtk, Gdk, GObject
- from . import Actions, Constants
-from . import Actions
-from . import Preferences
-from . import Utils
-from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH
++from . import Actions, Constants, Utils
BLOCK_INDEX = 0
ID_INDEX = 1
@@@ -109,15 -107,15 +109,15 @@@ class VariableEditor(Gtk.VBox)
self.treeview.connect('key-press-event',
self._handle_key_button_press)
# Block Name or Category
- self.id_cell = gtk.CellRendererText()
+ self.id_cell = Gtk.CellRendererText()
self.id_cell.connect('edited', self._handle_name_edited_cb)
- id_column = gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX)
+ id_column = Gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX)
id_column.set_name("id")
id_column.set_resizable(True)
- id_column.set_max_width(300)
- id_column.set_min_width(80)
- id_column.set_fixed_width(100)
+ id_column.set_max_width(Utils.scale_scalar(300))
+ id_column.set_min_width(Utils.scale_scalar(80))
+ id_column.set_fixed_width(Utils.scale_scalar(100))
- id_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ id_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
id_column.set_cell_data_func(self.id_cell, self.set_properties)
self.id_column = id_column
self.treeview.append_column(id_column)
@@@ -132,8 -130,8 +132,8 @@@
value_column.set_name("value")
value_column.set_resizable(False)
value_column.set_expand(True)
- value_column.set_min_width(100)
+ value_column.set_min_width(Utils.scale_scalar(100))
- value_column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
+ value_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
value_column.set_cell_data_func(self.value_cell, self.set_value)
self.value_column = value_column
self.treeview.append_column(value_column)
diff --cc grc/gui/canvas/block.py
index 7e28a21,0000000..fc8a533
mode 100644,000000..100644
--- a/grc/gui/canvas/block.py
+++ b/grc/gui/canvas/block.py
@@@ -1,390 -1,0 +1,391 @@@
+"""
+Copyright 2007, 2008, 2009 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 __future__ import absolute_import
+
+import math
+
+import six
+from gi.repository import Gtk, Pango, PangoCairo
+
+from .drawable import Drawable
+
+from .. import Actions, Colors, Utils, Constants
+from ..Constants import (
+ BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION,
+ PORT_BORDER_SEPARATION, BLOCK_FONT, PARAM_FONT
+)
+
+from ...core import utils
+from ...core.Block import Block as CoreBlock
+
+
+class Block(CoreBlock, Drawable):
+ """The graphical signal block."""
+
+ def __init__(self, parent, **n):
+ """
+ Block constructor.
+ Add graphics related params to the block.
+ """
+ super(self.__class__, self).__init__(parent, **n)
+
+ self.states.update(_coordinate=(0, 0), _rotation=0)
+ self.width = self.height = 0
+ Drawable.__init__(self) # needs the states and initial sizes
+
+ self._surface_layouts = [
+ Gtk.DrawingArea().create_pango_layout(''), # title
+ Gtk.DrawingArea().create_pango_layout(''), # params
+ ]
+ self._surface_layout_offsets = 0, 0
+ self._comment_layout = None
+
+ self._area = []
+ self._border_color = self._bg_color = Colors.BLOCK_ENABLED_COLOR
+ self._font_color = list(Colors.FONT_COLOR)
+
+ @property
+ def coordinate(self):
+ """
+ Get the coordinate from the position param.
+
+ Returns:
+ the coordinate tuple (x, y) or (0, 0) if failure
+ """
- return self.states['_coordinate']
++ return Utils.scale(self.states['_coordinate'])
+
+ @coordinate.setter
+ def coordinate(self, coor):
+ """
+ Set the coordinate into the position param.
+
+ Args:
+ coor: the coordinate tuple (x, y)
+ """
++ coor = Utils.scale(coor, reverse=True)
+ if Actions.TOGGLE_SNAP_TO_GRID.get_active():
+ offset_x, offset_y = (0, self.height / 2) if self.is_horizontal()
else (self.height / 2, 0)
+ coor = (
+ Utils.align_to_grid(coor[0] + offset_x) - offset_x,
+ Utils.align_to_grid(coor[1] + offset_y) - offset_y
+ )
+ self.states['_coordinate'] = coor
+
+ @property
+ def rotation(self):
+ """
+ Get the rotation from the position param.
+
+ Returns:
+ the rotation in degrees or 0 if failure
+ """
+ return self.states['_rotation']
+
+ @rotation.setter
+ def rotation(self, rot):
+ """
+ Set the rotation into the position param.
+
+ Args:
+ rot: the rotation in degrees
+ """
+ self.states['_rotation'] = rot
+
+ def _update_colors(self):
+ self._bg_color = (
+ Colors.MISSING_BLOCK_BACKGROUND_COLOR if self.is_dummy_block else
+ Colors.BLOCK_BYPASSED_COLOR if self.state == 'bypassed' else
+ Colors.BLOCK_ENABLED_COLOR if self.state == 'enabled' else
+ Colors.BLOCK_DISABLED_COLOR
+ )
+ self._font_color[-1] = 1.0 if self.state == 'enabled' else 0.4
+ self._border_color = (
+ Colors.MISSING_BLOCK_BORDER_COLOR if self.is_dummy_block else
+ Colors.BORDER_COLOR_DISABLED if not self.state == 'enabled' else
Colors.BORDER_COLOR
+ )
+
+ def create_shapes(self):
+ """Update the block, parameters, and ports when a change occurs."""
+ if self.is_horizontal():
+ self._area = (0, 0, self.width, self.height)
+ elif self.is_vertical():
+ self._area = (0, 0, self.height, self.width)
+ self.bounds_from_area(self._area)
+
+ bussified = self.current_bus_structure['source'],
self.current_bus_structure['sink']
+ for ports, has_busses in zip((self.active_sources,
self.active_sinks), bussified):
+ if not ports:
+ continue
+ port_separation = PORT_SEPARATION if not has_busses else
ports[0].height + PORT_SPACING
+ offset = (self.height - (len(ports) - 1) * port_separation -
ports[0].height) / 2
+ for index, port in enumerate(ports):
+ port.create_shapes()
+
+ port.coordinate = {
+ 0: (+self.width, offset),
+ 90: (offset, -port.width),
+ 180: (-port.width, offset),
+ 270: (offset, +self.width),
+ }[port.get_connector_direction()]
+ offset += PORT_SEPARATION if not has_busses else port.height
+ PORT_SPACING
+
+ port.connector_length = Constants.CONNECTOR_EXTENSION_MINIMAL
+ \
+
Constants.CONNECTOR_EXTENSION_INCREMENT * index
+
+ def create_labels(self):
+ """Create the labels for the signal block."""
+ title_layout, params_layout = self._surface_layouts
+
+ title_layout.set_markup(
+ '<span {foreground}
font_desc="{font}"><b>{name}</b></span>'.format(
+ foreground='foreground="red"' if not self.is_valid() else '',
font=BLOCK_FONT,
+ name=Utils.encode(self.name)
+ )
+ )
+ title_width, title_height = title_layout.get_pixel_size()
+
+ # update the params layout
+ if not self.is_dummy_block:
+ markups = [param.format_block_surface_markup()
+ for param in self.params.values() if param.get_hide()
not in ('all', 'part')]
+ else:
+ markups = ['<span font_desc="{font}"><b>key:
</b>{key}</span>'.format(font=PARAM_FONT, key=self.key)]
+
+ params_layout.set_spacing(LABEL_SEPARATION * Pango.SCALE)
+ params_layout.set_markup('\n'.join(markups))
+ params_width, params_height = params_layout.get_pixel_size() if
markups else (0, 0)
+
+ label_width = max(title_width, params_width)
+ label_height = title_height + LABEL_SEPARATION + params_height
+
+ title_layout.set_width(label_width * Pango.SCALE)
+ title_layout.set_alignment(Pango.Alignment.CENTER)
+
+ # calculate width and height needed
+ width = label_width + 2 * BLOCK_LABEL_PADDING
+ height = label_height + 2 * BLOCK_LABEL_PADDING
+
+ self._update_colors()
+ self.create_port_labels()
+
+ def get_min_height_for_ports(ports):
+ min_height = 2 * PORT_BORDER_SEPARATION + len(ports) *
PORT_SEPARATION
+ if ports:
+ min_height -= ports[-1].height
+ return min_height
+
+ height = max(height,
+ get_min_height_for_ports(self.active_sinks),
+ get_min_height_for_ports(self.active_sources))
+
+ def get_min_height_for_bus_ports(ports):
+ return 2 * PORT_BORDER_SEPARATION + sum(
+ port.height + PORT_SPACING for port in ports if
port.get_type() == 'bus'
+ ) - PORT_SPACING
+
+ if self.current_bus_structure['sink']:
+ height = max(height,
get_min_height_for_bus_ports(self.active_sinks))
+ if self.current_bus_structure['source']:
+ height = max(height,
get_min_height_for_bus_ports(self.active_sources))
+
+ self.width, self.height = width, height = Utils.align_to_grid((width,
height))
+
+ self._surface_layout_offsets = [
+ (width - label_width) / 2.0,
+ (height - label_height) / 2.0
+ ]
+
+ self.create_comment_layout()
+
+ def create_port_labels(self):
+ for ports in (self.active_sinks, self.active_sources):
+ max_width = 0
+ for port in ports:
+ port.create_labels()
+ max_width = max(max_width, port.width_with_label)
+ for port in ports:
+ port.width = max_width
+
+ def create_comment_layout(self):
+ markups = []
+
+ # Show the flow graph complexity on the top block if enabled
+ if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.key
== "options":
+ complexity = utils.calculate_flowgraph_complexity(self.parent)
+ markups.append(
+ '<span foreground="#444" size="medium" font_desc="{font}">'
+ '<b>Complexity:
{num}bal</b></span>'.format(num=Utils.num_to_str(complexity), font=BLOCK_FONT)
+ )
+ comment = self.comment # Returns None if there are no comments
+ if comment:
+ if markups:
+ markups.append('<span></span>')
+
+ markups.append('<span foreground="{foreground}"
font_desc="{font}">{comment}</span>'.format(
+ foreground='#444' if self.enabled else '#888',
font=BLOCK_FONT, comment=Utils.encode(comment)
+ ))
+ if markups:
+ layout = self._comment_layout =
Gtk.DrawingArea().create_pango_layout('')
+ layout.set_markup(''.join(markups))
+ else:
+ self._comment_layout = None
+
+ def draw(self, cr):
+ """
+ Draw the signal block with label and inputs/outputs.
+ """
+ border_color = Colors.HIGHLIGHT_COLOR if self.highlighted else
self._border_color
+ cr.translate(*self.coordinate)
+
+ for port in self.active_ports(): # ports first
+ cr.save()
+ port.draw(cr)
+ cr.restore()
+
+ cr.rectangle(*self._area)
+ cr.set_source_rgba(*self._bg_color)
+ cr.fill_preserve()
+ cr.set_source_rgba(*border_color)
+ cr.stroke()
+
+ # title and params label
+ if self.is_vertical():
+ cr.rotate(-math.pi / 2)
+ cr.translate(-self.width, 0)
+ cr.translate(*self._surface_layout_offsets)
+
+ cr.set_source_rgba(*self._font_color)
+ for layout in self._surface_layouts:
+ PangoCairo.update_layout(cr, layout)
+ PangoCairo.show_layout(cr, layout)
+ _, h = layout.get_pixel_size()
+ cr.translate(0, h + LABEL_SEPARATION)
+
+ def what_is_selected(self, coor, coor_m=None):
+ """
+ Get the element that is selected.
+
+ Args:
+ coor: the (x,y) tuple
+ coor_m: the (x_m, y_m) tuple
+
+ Returns:
+ this block, a port, or None
+ """
+ for port in self.active_ports():
+ port_selected = port.what_is_selected(
+ coor=[a - b for a, b in zip(coor, self.coordinate)],
+ coor_m=[a - b for a, b in zip(coor, self.coordinate)] if
coor_m is not None else None
+ )
+ if port_selected:
+ return port_selected
+ return Drawable.what_is_selected(self, coor, coor_m)
+
+ def draw_comment(self, cr):
+ if not self._comment_layout:
+ return
+ x, y = self.coordinate
+
+ if self.is_horizontal():
+ y += self.height + BLOCK_LABEL_PADDING
+ else:
+ x += self.height + BLOCK_LABEL_PADDING
+
+ cr.save()
+ cr.translate(x, y)
+ PangoCairo.update_layout(cr, self._comment_layout)
+ PangoCairo.show_layout(cr, self._comment_layout)
+ cr.restore()
+
+ @property
+ def extend(self):
+ extend = Drawable.extend.fget(self)
+ x, y = self.coordinate
+ for port in self.active_ports():
+ extend = (min_or_max(xy, offset + p_xy) for offset, min_or_max,
xy, p_xy in zip(
+ (x, y, x, y), (min, min, max, max), extend, port.extend
+ ))
+ return tuple(extend)
+
+ ##############################################
+ # Controller Modify
+ ##############################################
+ def type_controller_modify(self, direction):
+ """
+ Change the type controller.
+
+ Args:
+ direction: +1 or -1
+
+ Returns:
+ true for change
+ """
+ type_templates = ' '.join(p._type for p in self.get_children())
+ type_param = None
+ for key, param in six.iteritems(self.params):
+ if not param.is_enum():
+ continue
+ # Priority to the type controller
+ if param.key in type_templates:
+ type_param = param
+ break
+ # Use param if type param is unset
+ if not type_param:
+ type_param = param
+ if not type_param:
+ return False
+
+ # Try to increment the enum by direction
+ try:
+ keys = list(type_param.options)
+ old_index = keys.index(type_param.get_value())
+ new_index = (old_index + direction + len(keys)) % len(keys)
+ type_param.set_value(keys[new_index])
+ return True
+ except:
+ return False
+
+ def port_controller_modify(self, direction):
+ """
+ Change the port controller.
+
+ Args:
+ direction: +1 or -1
+
+ Returns:
+ true for change
+ """
+ changed = False
+ # Concat the nports string from the private nports settings of all
ports
+ nports_str = ' '.join(port._nports for port in self.get_ports())
+ # Modify all params whose keys appear in the nports string
+ for key, param in six.iteritems(self.params):
+ if param.is_enum() or param.key not in nports_str:
+ continue
+ # Try to increment the port controller by direction
+ try:
+ value = param.get_evaluated() + direction
+ if value > 0:
+ param.set_value(value)
+ changed = True
+ except:
+ pass
+ return changed
+
- [Commit-gnuradio] [gnuradio] 78/101: Merge remote-tracking branch 'upstream/next' into gtk3, (continued)
- [Commit-gnuradio] [gnuradio] 78/101: Merge remote-tracking branch 'upstream/next' into gtk3, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 79/101: grc: refactor: move drawables in subpackage, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 95/101: grc: gtk3: curved connections, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 82/101: grc: gtk3: Converted to Gtk.Application (ActionHandler) and Gtk.ApplicationWindow (MainWindow), git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 96/101: grc: gtk3: invalid connection all red, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 90/101: Merge remote-tracking branch 'upstream/next' into gtk3, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 97/101: grc: gtk3: drag to connect, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 84/101: grc: refactor: Moved preferences to Config.py, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 101/101: Merge branch 'gtk3' into python3, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 47/101: grc: refactor: Port, Param, Options init clean-up, git, 2017/03/16
- [Commit-gnuradio] [gnuradio] 89/101: Merge remote-tracking branch 'upstream/next' into gtk3,
git <=