commit-gnuradio
[Top][All Lists]
Advanced

[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
 +



reply via email to

[Prev in Thread] Current Thread [Next in Thread]