summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gr-digital/grc/blks2_packet_decoder.xml1
-rw-r--r--gr-digital/grc/blks2_packet_encoder.xml1
-rw-r--r--gr-digital/include/gnuradio/digital/ofdm_equalizer_base.h3
-rw-r--r--gr-digital/include/gnuradio/digital/ofdm_equalizer_simpledfe.h3
-rw-r--r--gr-digital/include/gnuradio/digital/ofdm_equalizer_static.h3
-rw-r--r--gr-uhd/grc/gen_uhd_usrp_blocks.py92
-rw-r--r--gr-uhd/include/gnuradio/uhd/usrp_source.h77
-rw-r--r--gr-uhd/lib/usrp_block_impl.h8
-rw-r--r--gr-uhd/lib/usrp_sink_impl.cc2
-rw-r--r--gr-uhd/lib/usrp_source_impl.cc98
-rw-r--r--gr-uhd/lib/usrp_source_impl.h9
-rw-r--r--gr-uhd/swig/uhd_swig.i12
-rw-r--r--gr-utils/python/modtool/gr-newmod/CMakeLists.txt9
-rw-r--r--gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt16
-rw-r--r--gr-utils/python/modtool/modtool_add.py5
-rw-r--r--grc/blocks/variable_function_probe.xml18
-rw-r--r--grc/core/Block.py23
-rw-r--r--grc/core/Constants.py1
-rw-r--r--grc/core/Element.py5
-rw-r--r--grc/core/FlowGraph.py18
-rw-r--r--grc/core/Messages.py4
-rw-r--r--grc/core/generator/Generator.py14
-rw-r--r--grc/core/utils/expr_utils.py13
-rw-r--r--grc/gui/ActionHandler.py436
-rw-r--r--grc/gui/Actions.py88
-rw-r--r--grc/gui/Bars.py12
-rw-r--r--grc/gui/Block.py59
-rw-r--r--grc/gui/Colors.py2
-rw-r--r--grc/gui/Constants.py2
-rw-r--r--grc/gui/Dialogs.py10
-rw-r--r--grc/gui/Executor.py34
-rw-r--r--grc/gui/FileDialogs.py50
-rw-r--r--grc/gui/FlowGraph.py68
-rw-r--r--grc/gui/MainWindow.py147
-rw-r--r--grc/gui/NotebookPage.py9
-rw-r--r--grc/gui/Param.py19
-rw-r--r--grc/gui/Preferences.py21
-rw-r--r--grc/gui/Utils.py9
-rw-r--r--grc/gui/VariableEditor.py354
39 files changed, 1360 insertions, 395 deletions
diff --git a/gr-digital/grc/blks2_packet_decoder.xml b/gr-digital/grc/blks2_packet_decoder.xml
index c3672450ea..3b66f0024f 100644
--- a/gr-digital/grc/blks2_packet_decoder.xml
+++ b/gr-digital/grc/blks2_packet_decoder.xml
@@ -8,6 +8,7 @@
<name>Packet Decoder</name>
<key>blks2_packet_decoder</key>
<category>Deprecated</category>
+ <flags>deprecated</flags>
<import>from grc_gnuradio import blks2 as grc_blks2</import>
<make>grc_blks2.packet_demod_$(type.fcn)(grc_blks2.packet_decoder(
access_code=$access_code,
diff --git a/gr-digital/grc/blks2_packet_encoder.xml b/gr-digital/grc/blks2_packet_encoder.xml
index 92de9c9570..75cb5eabf1 100644
--- a/gr-digital/grc/blks2_packet_encoder.xml
+++ b/gr-digital/grc/blks2_packet_encoder.xml
@@ -8,6 +8,7 @@
<name>Packet Encoder</name>
<key>blks2_packet_encoder</key>
<category>Deprecated</category>
+ <flags>deprecated</flags>
<import>from grc_gnuradio import blks2 as grc_blks2</import>
<make>grc_blks2.packet_mod_$(type.fcn)(grc_blks2.packet_encoder(
samples_per_symbol=$samples_per_symbol,
diff --git a/gr-digital/include/gnuradio/digital/ofdm_equalizer_base.h b/gr-digital/include/gnuradio/digital/ofdm_equalizer_base.h
index ca53382944..85443da8f2 100644
--- a/gr-digital/include/gnuradio/digital/ofdm_equalizer_base.h
+++ b/gr-digital/include/gnuradio/digital/ofdm_equalizer_base.h
@@ -30,7 +30,8 @@
namespace gr {
namespace digital {
- /* \brief Base class for implementation details of frequency-domain OFDM equalizers.
+ /*!
+ * \brief Base class for implementation details of frequency-domain OFDM equalizers.
* \ingroup ofdm_blk
* \ingroup equalizers_blk
*/
diff --git a/gr-digital/include/gnuradio/digital/ofdm_equalizer_simpledfe.h b/gr-digital/include/gnuradio/digital/ofdm_equalizer_simpledfe.h
index d526f9f157..03a4c82b91 100644
--- a/gr-digital/include/gnuradio/digital/ofdm_equalizer_simpledfe.h
+++ b/gr-digital/include/gnuradio/digital/ofdm_equalizer_simpledfe.h
@@ -29,7 +29,8 @@
namespace gr {
namespace digital {
- /* \brief Simple decision feedback equalizer for OFDM.
+ /*!
+ * \brief Simple decision feedback equalizer for OFDM.
* \ingroup ofdm_blk
* \ingroup equalizers_blk
*
diff --git a/gr-digital/include/gnuradio/digital/ofdm_equalizer_static.h b/gr-digital/include/gnuradio/digital/ofdm_equalizer_static.h
index 892025c2b0..ed3fe83f04 100644
--- a/gr-digital/include/gnuradio/digital/ofdm_equalizer_static.h
+++ b/gr-digital/include/gnuradio/digital/ofdm_equalizer_static.h
@@ -29,7 +29,8 @@
namespace gr {
namespace digital {
- /* \brief Very simple static equalizer for OFDM.
+ /*!
+ * \brief Very simple static equalizer for OFDM.
* \ingroup ofdm_blk
* \ingroup equalizers_blk
*
diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py
index fd2e77707e..e99de0d7d0 100644
--- a/gr-uhd/grc/gen_uhd_usrp_blocks.py
+++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py
@@ -82,6 +82,12 @@ self.\$(id).set_antenna(\$ant$(n), $n)
self.\$(id).set_bandwidth(\$bw$(n), $n)
\#end if
#if $sourk == 'source'
+ \#if \$lo_export$(n)() and not \$hide_lo_controls()
+self.\$(id).set_lo_export_enabled(\$lo_export$(n), uhd.ALL_LOS, $n)
+ \#end if
+ \#if \$lo_source$(n)() and not \$hide_lo_controls()
+self.\$(id).set_lo_source(\$lo_source$(n), uhd.ALL_LOS, $n)
+ \#end if
\#if \$dc_offs_enb$(n)()
self.\$(id).set_auto_dc_offset(\$dc_offs_enb$(n), $n)
\#end if
@@ -101,6 +107,14 @@ self.\$(id).set_normalized_gain(\$gain$(n), $n)
self.\$(id).set_gain(\$gain$(n), $n)
\#end if
</callback>
+ <callback>\#if not \$hide_lo_controls()
+set_lo_source(\$lo_source$(n), uhd.ALL_LOS, $n)
+\#end if
+ </callback>
+ <callback>\#if not \$hide_lo_controls()
+set_lo_export_enabled(\$lo_export$(n), uhd.ALL_LOS, $n)
+\#end if
+ </callback>
<callback>set_antenna(\$ant$(n), $n)</callback>
<callback>set_bandwidth(\$bw$(n), $n)</callback>
#end for
@@ -524,6 +538,62 @@ PARAMS_TMPL = """ <param>
</param>
#if $sourk == 'source'
<param>
+ <name>Ch$(n): LO Source</name>
+ <key>lo_source$(n)</key>
+ <value>internal</value>
+ <type>string</type>
+ <hide>
+ \#if not \$nchan() > $n
+ all
+ \#elif \$hide_lo_controls()
+ all
+ \#else
+ none
+ \#end if
+ </hide>
+ <option>
+ <name>Internal</name>
+ <key>internal</key>
+ </option>
+ <option>
+ <name>External</name>
+ <key>external</key>
+ </option>
+ <option>
+ <name>Companion</name>
+ <key>companion</key>
+ </option>
+ <tab>RF Options</tab>
+ </param>
+#end if
+#if $sourk == 'source'
+ <param>
+ <name>Ch$(n): LO Export</name>
+ <key>lo_export$(n)</key>
+ <value>False</value>
+ <type>bool</type>
+ <hide>
+ \#if not \$nchan() > $n
+ all
+ \#elif \$hide_lo_controls()
+ all
+ \#else
+ none
+ \#end if
+ </hide>
+ <option>
+ <name>True</name>
+ <key>True</key>
+ </option>
+ <option>
+ <name>False</name>
+ <key>False</key>
+ </option>
+ <tab>RF Options</tab>
+ </param>
+#end if
+#if $sourk == 'source'
+ <param>
<name>Ch$(n): Enable DC Offset Correction</name>
<key>dc_offs_enb$(n)</key>
<value>""</value>
@@ -573,6 +643,25 @@ SHOW_CMD_PORT_PARAM = """
</param>
"""
+SHOW_LO_CONTROLS_PARAM = """
+ <param>
+ <name>Show LO Controls</name>
+ <key>hide_lo_controls</key>
+ <value>True</value>
+ <type>bool</type>
+ <hide>part</hide>
+ <option>
+ <name>Yes</name>
+ <key>False</key>
+ </option>
+ <option>
+ <name>No</name>
+ <key>True</key>
+ </option>
+ <tab>Advanced</tab>
+ </param>
+"""
+
TSBTAG_PARAM = """ <param>
<name>TSB tag name</name>
<key>len_tag_name</key>
@@ -605,7 +694,8 @@ if __name__ == '__main__':
else: raise Exception, 'is %s a source or sink?'%file
params = ''.join([parse_tmpl(PARAMS_TMPL, n=n, sourk=sourk) for n in range(max_num_channels)])
- params += SHOW_CMD_PORT_PARAM
+ params += SHOW_CMD_PORT_PARAM
+ params += SHOW_LO_CONTROLS_PARAM
if sourk == 'sink':
params += TSBTAG_PARAM
lentag_arg = TSBTAG_ARG
diff --git a/gr-uhd/include/gnuradio/uhd/usrp_source.h b/gr-uhd/include/gnuradio/uhd/usrp_source.h
index 19201c031c..a0022b3876 100644
--- a/gr-uhd/include/gnuradio/uhd/usrp_source.h
+++ b/gr-uhd/include/gnuradio/uhd/usrp_source.h
@@ -140,6 +140,83 @@ namespace gr {
*/
virtual ::uhd::dict<std::string, std::string> get_usrp_info(size_t chan = 0) = 0;
+
+ /*!
+ * Get a list of possible LO stage names
+ * \param chan the channel index 0 to N-1
+ * \return a vector of strings for possible LO names
+ */
+ virtual std::vector<std::string> get_lo_names(size_t chan = 0) = 0;
+
+ /*!
+ * Set the LO source for the usrp device.
+ * For usrps that support selectable LOs, this function
+ * allows switching between them.
+ * Typical options for source: internal, external.
+ * \param src a string representing the LO source
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_lo_source(const std::string &src, const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Get the currently set LO source.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return the configured LO source
+ */
+ virtual const std::string get_lo_source(const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Get a list of possible LO sources.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return a vector of strings for possible settings
+ */
+ virtual std::vector<std::string> get_lo_sources(const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Set whether the LO used by the usrp device is exported
+ * For usrps that support exportable LOs, this function
+ * configures if the LO used by chan is exported or not.
+ * \param enabled if true then export the LO
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1 for the source channel
+ */
+ virtual void set_lo_export_enabled(bool enabled, const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Returns true if the currently selected LO is being exported.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ */
+ virtual bool get_lo_export_enabled(const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Set the RX LO frequency (Advanced).
+ * \param freq the frequency to set the LO to
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1
+ * \return a coerced LO frequency
+ */
+ virtual double set_lo_freq(double freq, const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Get the current RX LO frequency (Advanced).
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return the configured LO frequency
+ */
+ virtual double get_lo_freq(const std::string &name, size_t chan = 0) = 0;
+
+ /*!
+ * Get the LO frequency range of the RX LO.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return a frequency range object
+ */
+ virtual ::uhd::freq_range_t get_lo_freq_range(const std::string &name, size_t chan = 0) = 0;
+
/*!
* Enable/disable the automatic DC offset correction.
* The automatic correction subtracts out the long-run average.
diff --git a/gr-uhd/lib/usrp_block_impl.h b/gr-uhd/lib/usrp_block_impl.h
index cb07fb356d..a1bbf97826 100644
--- a/gr-uhd/lib/usrp_block_impl.h
+++ b/gr-uhd/lib/usrp_block_impl.h
@@ -40,6 +40,14 @@
namespace gr {
namespace uhd {
+ static const size_t ALL_MBOARDS = ::uhd::usrp::multi_usrp::ALL_MBOARDS;
+ static const size_t ALL_CHANS = ::uhd::usrp::multi_usrp::ALL_CHANS;
+ static const std::string ALL_GAINS = ::uhd::usrp::multi_usrp::ALL_GAINS;
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ static const std::string ALL_LOS = ::uhd::usrp::multi_usrp::ALL_LOS;
+#else
+ static const std::string ALL_LOS;
+#endif
class usrp_block_impl : virtual public usrp_block
{
public:
diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc
index e2e4d334b6..d8f98fdd80 100644
--- a/gr-uhd/lib/usrp_sink_impl.cc
+++ b/gr-uhd/lib/usrp_sink_impl.cc
@@ -29,8 +29,6 @@
namespace gr {
namespace uhd {
- static const size_t ALL_CHANS = ::uhd::usrp::multi_usrp::ALL_CHANS;
-
usrp_sink::sptr
usrp_sink::make(const ::uhd::device_addr_t &device_addr,
const ::uhd::io_type_t &io_type,
diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc
index b2bddbef17..ed0b891770 100644
--- a/gr-uhd/lib/usrp_source_impl.cc
+++ b/gr-uhd/lib/usrp_source_impl.cc
@@ -281,6 +281,104 @@ namespace gr {
return _dev->get_rx_bandwidth_range(chan);
}
+ std::vector<std::string>
+ usrp_source_impl::get_lo_names(size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->get_rx_lo_names(chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ const std::string
+ usrp_source_impl::get_lo_source(const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->get_rx_lo_source(name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ std::vector<std::string>
+ usrp_source_impl::get_lo_sources(const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->get_rx_lo_sources(name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ void
+ usrp_source_impl::set_lo_source(const std::string &src, const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->set_rx_lo_source(src, name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ bool
+ usrp_source_impl::get_lo_export_enabled(const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->get_rx_lo_export_enabled(name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ void
+ usrp_source_impl::set_lo_export_enabled(bool enabled, const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->set_rx_lo_export_enabled(enabled, name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ ::uhd::freq_range_t
+ usrp_source_impl::get_lo_freq_range(const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->get_rx_lo_freq_range(name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ double
+ usrp_source_impl::get_lo_freq(const std::string &name, size_t chan)
+ {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->get_rx_lo_freq(name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
+ double
+ usrp_source_impl::set_lo_freq(double freq, const std::string &name, size_t chan) {
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+ chan = _stream_args.channels[chan];
+ return _dev->set_rx_lo_freq(freq, name, chan);
+#else
+ throw std::runtime_error("not implemented in this version");
+#endif
+ }
+
void
usrp_source_impl::set_auto_dc_offset(const bool enable, size_t chan)
{
diff --git a/gr-uhd/lib/usrp_source_impl.h b/gr-uhd/lib/usrp_source_impl.h
index 78a8341a5a..f6225a7e35 100644
--- a/gr-uhd/lib/usrp_source_impl.h
+++ b/gr-uhd/lib/usrp_source_impl.h
@@ -77,6 +77,12 @@ namespace gr {
::uhd::sensor_value_t get_sensor(const std::string &name, size_t chan);
std::vector<std::string> get_sensor_names(size_t chan);
::uhd::usrp::dboard_iface::sptr get_dboard_iface(size_t chan);
+ std::vector<std::string> get_lo_names(size_t chan);
+ const std::string get_lo_source(const std::string &name, size_t chan);
+ std::vector<std::string> get_lo_sources(const std::string &name, size_t chan);
+ bool get_lo_export_enabled(const std::string &name, size_t chan);
+ double get_lo_freq(const std::string &name, size_t chan);
+ ::uhd::freq_range_t get_lo_freq_range(const std::string &name, size_t chan);
// Set Commands
void set_subdev_spec(const std::string &spec, size_t mboard);
@@ -96,6 +102,9 @@ namespace gr {
void set_iq_balance(const std::complex<double> &correction, size_t chan);
void set_stream_args(const ::uhd::stream_args_t &stream_args);
void set_start_time(const ::uhd::time_spec_t &time);
+ void set_lo_source(const std::string &src, const std::string &name = ALL_LOS, size_t chan = 0);
+ void set_lo_export_enabled(bool enabled, const std::string &name = ALL_LOS, size_t chan = 0);
+ double set_lo_freq(double freq, const std::string &name, size_t chan);
void issue_stream_cmd(const ::uhd::stream_cmd_t &cmd);
void flush(void);
diff --git a/gr-uhd/swig/uhd_swig.i b/gr-uhd/swig/uhd_swig.i
index 108f544da3..b82d0fdae9 100644
--- a/gr-uhd/swig/uhd_swig.i
+++ b/gr-uhd/swig/uhd_swig.i
@@ -155,8 +155,20 @@ static uhd::device_addrs_t find_devices_raw(const uhd::device_addr_t &dev_addr =
////////////////////////////////////////////////////////////////////////
%{
static const size_t ALL_MBOARDS = uhd::usrp::multi_usrp::ALL_MBOARDS;
+static const size_t ALL_CHANS = uhd::usrp::multi_usrp::ALL_CHANS;
+static const std::string ALL_GAINS = uhd::usrp::multi_usrp::ALL_GAINS;
+
+#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API
+static const std::string ALL_LOS = uhd::usrp::multi_usrp::ALL_LOS;
+#else
+static const std::string ALL_LOS;
+#endif
%}
+
static const size_t ALL_MBOARDS;
+static const size_t ALL_CHANS;
+static const std::string ALL_GAINS;
+static const std::string ALL_LOS;
%{
#include <uhd/version.hpp>
diff --git a/gr-utils/python/modtool/gr-newmod/CMakeLists.txt b/gr-utils/python/modtool/gr-newmod/CMakeLists.txt
index 6f32f21833..bde9dc0c0f 100644
--- a/gr-utils/python/modtool/gr-newmod/CMakeLists.txt
+++ b/gr-utils/python/modtool/gr-newmod/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2011,2012,2014 Free Software Foundation, Inc.
+# Copyright 2011,2012,2014,2016 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -40,6 +40,12 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
#make sure our local CMake Modules path comes first
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
+# Set the version information here
+set(VERSION_INFO_MAJOR_VERSION 1)
+set(VERSION_INFO_API_COMPAT 0)
+set(VERSION_INFO_MINOR_VERSION 0)
+set(VERSION_INFO_MAINT_VERSION git)
+
########################################################################
# Compiler specific setup
########################################################################
@@ -119,6 +125,7 @@ find_package(Doxygen)
set(GR_REQUIRED_COMPONENTS RUNTIME)
find_package(Gnuradio "3.7.2" REQUIRED)
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
+include(GrVersion)
if(NOT CPPUNIT_FOUND)
message(FATAL_ERROR "CppUnit required to compile howto")
diff --git a/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt b/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt
index 6192c51f04..10a15b7dd9 100644
--- a/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt
+++ b/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2011,2012 Free Software Foundation, Inc.
+# Copyright 2011,2012,2016 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -47,11 +47,8 @@ endif(APPLE)
########################################################################
# Install built library files
########################################################################
-install(TARGETS gnuradio-howto
- LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file
- ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file
- RUNTIME DESTINATION bin # .dll file
-)
+include(GrMiscUtils)
+GR_LIBRARY_FOO(gnuradio-howto RUNTIME_COMPONENT "howto_runtime" DEVEL_COMPONENT "howto_devel")
########################################################################
# Build and register unit test
@@ -76,3 +73,10 @@ target_link_libraries(
)
GR_ADD_TEST(test_howto test-howto)
+
+########################################################################
+# Print summary
+########################################################################
+message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}")
+message(STATUS "Building for version: ${VERSION} / ${LIBVER}")
+
diff --git a/gr-utils/python/modtool/modtool_add.py b/gr-utils/python/modtool/modtool_add.py
index eb269080b4..d560c0878c 100644
--- a/gr-utils/python/modtool/modtool_add.py
+++ b/gr-utils/python/modtool/modtool_add.py
@@ -82,9 +82,8 @@ class ModToolAdd(ModTool):
if self._info['lang'] is None:
while self._info['lang'] not in ['cpp', 'python']:
self._info['lang'] = raw_input("Language (python/cpp): ")
-
- if self._info['lang'] == 'c++':
- self._info['lang'] = 'cpp'
+ if self._info['lang'] == 'c++':
+ self._info['lang'] = 'cpp'
print "Language: %s" % {'cpp': 'C++', 'python': 'Python'}[self._info['lang']]
diff --git a/grc/blocks/variable_function_probe.xml b/grc/blocks/variable_function_probe.xml
index baa996c0ec..47c11b29fe 100644
--- a/grc/blocks/variable_function_probe.xml
+++ b/grc/blocks/variable_function_probe.xml
@@ -10,7 +10,7 @@
<import>import time</import>
<import>import threading</import>
<var_make>self.$(id) = $(id) = $value</var_make>
- <make>#slurp
+ <make>
def _$(id)_probe():
while True:
#set $obj = 'self' + ('.' + $block_id() if $block_id() else '')
@@ -22,15 +22,10 @@ def _$(id)_probe():
time.sleep(1.0 / ($poll_rate))
_$(id)_thread = threading.Thread(target=_$(id)_probe)
_$(id)_thread.daemon = True
-_$(id)_thread.start()</make>
+_$(id)_thread.start()
+ </make>
<callback>self.set_$(id)($value)</callback>
<param>
- <name>Value</name>
- <key>value</key>
- <value>0</value>
- <type>raw</type>
- </param>
- <param>
<name>Block ID</name>
<key>block_id</key>
<value>my_block_0</value>
@@ -55,6 +50,13 @@ _$(id)_thread.start()</make>
<value>10</value>
<type>real</type>
</param>
+ <param>
+ <name>Initial Value</name>
+ <key>value</key>
+ <value>0</value>
+ <type>raw</type>
+ <hide>part</hide>
+ </param>
<doc>
Periodically probe a function and set its value to this variable.
diff --git a/grc/core/Block.py b/grc/core/Block.py
index 106e4bc89a..f67d990857 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -27,10 +27,10 @@ from . Constants import (
BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI,
ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB,
BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS,
+ BLOCK_FLAG_DEPRECATED,
BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED
)
from . Element import Element
-from . FlowGraph import _variable_matcher
def _get_keys(lst):
@@ -149,15 +149,16 @@ class Block(Element):
# indistinguishable from normal GR blocks. Make explicit
# checks for them here since they have no work function or
# buffers to manage.
- is_virtual_or_pad = self._key in (
+ self.is_virtual_or_pad = self._key in (
"virtual_source", "virtual_sink", "pad_source", "pad_sink")
- is_variable = self._key.startswith('variable')
+ self.is_variable = self._key.startswith('variable')
+ self.is_import = (self._key == 'import')
# Disable blocks that are virtual/pads or variables
- if is_virtual_or_pad or is_variable:
+ if self.is_virtual_or_pad or self.is_variable:
self._flags += BLOCK_FLAG_DISABLE_BYPASS
- if not (is_virtual_or_pad or is_variable or self._key == 'options'):
+ if not (self.is_virtual_or_pad or self.is_variable or self._key == 'options'):
self.get_params().append(self.get_parent().get_parent().Param(
block=self,
n=odict({'name': 'Block Alias',
@@ -168,7 +169,7 @@ class Block(Element):
})
))
- if (len(sources) or len(sinks)) and not is_virtual_or_pad:
+ if (len(sources) or len(sinks)) and not self.is_virtual_or_pad:
self.get_params().append(self.get_parent().get_parent().Param(
block=self,
n=odict({'name': 'Core Affinity',
@@ -178,7 +179,7 @@ class Block(Element):
'tab': ADVANCED_PARAM_TAB
})
))
- if len(sources) and not is_virtual_or_pad:
+ if len(sources) and not self.is_virtual_or_pad:
self.get_params().append(self.get_parent().get_parent().Param(
block=self,
n=odict({'name': 'Min Output Buffer',
@@ -253,7 +254,7 @@ class Block(Element):
self.add_error_message('Check "{}" did not evaluate.'.format(check))
# For variables check the value (only if var_value is used
- if _variable_matcher.match(self.get_key()) and self._var_value != '$value':
+ if self.is_variable and self._var_value != '$value':
value = self._var_value
try:
value = self.get_var_value()
@@ -356,7 +357,7 @@ class Block(Element):
"""
Resolve all import statements.
Split each import statement at newlines.
- Combine all import statments into a list.
+ Combine all import statements into a list.
Filter empty imports.
Returns:
@@ -626,6 +627,10 @@ class Block(Element):
def bypass_disabled(self):
return BLOCK_FLAG_DISABLE_BYPASS in self._flags
+ @property
+ def is_deprecated(self):
+ return BLOCK_FLAG_DEPRECATED in self._flags
+
##############################################
# Access Params
##############################################
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 1d1a17ee93..462049cc73 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -47,6 +47,7 @@ BLOCK_FLAG_THROTTLE = 'throttle'
BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass'
BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui'
BLOCK_FLAG_NEED_WX_GUI = 'need_wx_gui'
+BLOCK_FLAG_DEPRECATED = 'deprecated'
# Block States
BLOCK_DISABLED = 0
diff --git a/grc/core/Element.py b/grc/core/Element.py
index b96edb0a72..67c36e12b4 100644
--- a/grc/core/Element.py
+++ b/grc/core/Element.py
@@ -100,6 +100,7 @@ class Element(object):
is_flow_graph = False
is_block = False
+
is_dummy_block = False
is_connection = False
@@ -107,3 +108,7 @@ class Element(object):
is_port = False
is_param = False
+
+ is_variable = False
+
+ is_import = False
diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py
index 2f33baf8d0..949eecaa71 100644
--- a/grc/core/FlowGraph.py
+++ b/grc/core/FlowGraph.py
@@ -18,7 +18,7 @@
import imp
import time
from itertools import ifilter, chain
-from operator import methodcaller
+from operator import methodcaller, attrgetter
import re
@@ -27,7 +27,6 @@ from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
from .Element import Element
from .utils import odict, expr_utils
-_variable_matcher = re.compile('^(variable\w*)$')
_parameter_matcher = re.compile('^(parameter)$')
_monitors_searcher = re.compile('(ctrlport_monitor)')
_bussink_searcher = re.compile('^(bus_sink)$')
@@ -72,32 +71,31 @@ class FlowGraph(Element):
##############################################
def get_imports(self):
"""
- Get a set of all import statments in this flow graph namespace.
+ Get a set of all import statements in this flow graph namespace.
Returns:
a set of import statements
"""
- imports = sum([block.get_imports() for block in self.get_enabled_blocks()], [])
- imports = sorted(set(imports))
- return imports
+ imports = sum([block.get_imports() for block in self.iter_enabled_blocks()], [])
+ return sorted(set(imports))
def get_variables(self):
"""
Get a list of all variables in this flow graph namespace.
- Exclude paramterized variables.
+ Exclude parameterized variables.
Returns:
a sorted list of variable blocks in order of dependency (indep -> dep)
"""
- variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.iter_enabled_blocks())
+ variables = filter(attrgetter('is_variable'), self.iter_enabled_blocks())
return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make'))
def get_parameters(self):
"""
- Get a list of all paramterized variables in this flow graph namespace.
+ Get a list of all parameterized variables in this flow graph namespace.
Returns:
- a list of paramterized variables
+ a list of parameterized variables
"""
parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks())
return parameters
diff --git a/grc/core/Messages.py b/grc/core/Messages.py
index da50487e5b..8daa12c33f 100644
--- a/grc/core/Messages.py
+++ b/grc/core/Messages.py
@@ -66,10 +66,6 @@ def send_init(platform):
)
-def send_page_switch(file_path):
- send('\nShowing: "%s"\n' % file_path)
-
-
def send_xml_errors_if_any(xml_failures):
if xml_failures:
send('\nXML parser: Found {0} erroneous XML file{1} while loading the '
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index 641bdea6e6..fb7a3afb99 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -145,6 +145,10 @@ class TopBlockGenerator(object):
filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks),
lambda b: b.get_id(), _get_block_sort_text
)
+ deprecated_block_keys = set(block.get_name() for block in blocks_all if block.is_deprecated)
+ for key in deprecated_block_keys:
+ Messages.send_warning("The block {!r} is deprecated.".format(key))
+
# List of regular blocks (all blocks minus the special ones)
blocks = filter(lambda b: b not in (imports + parameters), blocks_all)
@@ -210,17 +214,23 @@ class TopBlockGenerator(object):
connection_templates = fg.get_parent().connection_templates
msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections())
+
# List of variable names
var_ids = [var.get_id() for var in parameters + variables]
replace_dict = dict((var_id, 'self.' + var_id) for var_id in var_ids)
callbacks_all = []
for block in blocks_all:
callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_callbacks())
+
# Map var id to callbacks
+ def uses_var_id():
+ used = expr_utils.get_variable_dependencies(callback, [var_id])
+ return used and 'self.' + var_id in callback # callback might contain var_id itself
+
callbacks = {}
for var_id in var_ids:
- callbacks[var_id] = [callback for callback in callbacks_all
- if expr_utils.get_variable_dependencies(callback, ['self.' + var_id])]
+ callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id()]
+
# Load the namespace
namespace = {
'title': title,
diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py
index c5069583e0..2059ceff9f 100644
--- a/grc/core/utils/expr_utils.py
+++ b/grc/core/utils/expr_utils.py
@@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
import string
-VAR_CHARS = string.letters + string.digits + '_.'
+VAR_CHARS = string.letters + string.digits + '_'
class graph(object):
@@ -56,7 +56,7 @@ class graph(object):
return self._graph[node_key]
-def expr_split(expr):
+def expr_split(expr, var_chars=VAR_CHARS):
"""
Split up an expression by non alphanumeric characters, including underscore.
Leave strings in-tact.
@@ -72,7 +72,7 @@ def expr_split(expr):
tok = ''
quote = ''
for char in expr:
- if quote or char in VAR_CHARS:
+ if quote or char in var_chars:
if char == quote:
quote = ''
tok += char
@@ -99,7 +99,7 @@ def expr_replace(expr, replace_dict):
Returns:
a new expression with the prepend
"""
- expr_splits = expr_split(expr)
+ expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.')
for i, es in enumerate(expr_splits):
if es in replace_dict.keys():
expr_splits[i] = replace_dict[es]
@@ -189,8 +189,3 @@ def sort_objects(objects, get_id, get_expr):
sorted_ids = sort_variables(id2expr)
# Return list of sorted objects
return [id2obj[id] for id in sorted_ids]
-
-
-if __name__ == '__main__':
- for i in sort_variables({'x': '1', 'y': 'x+1', 'a': 'x+y', 'b': 'y+1', 'c': 'a+b+x+y'}):
- print i
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 077786d4b4..2b39079f76 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -25,7 +25,7 @@ import subprocess
from . import Dialogs, Preferences, Actions, Executor, Constants
from .FileDialogs import (OpenFlowGraphFileDialog, SaveFlowGraphFileDialog,
- SaveReportsFileDialog, SaveScreenShotDialog,
+ SaveConsoleFileDialog, SaveScreenShotDialog,
OpenQSSFileDialog)
from .MainWindow import MainWindow
from .ParserErrorsDialog import ParserErrorsDialog
@@ -61,13 +61,13 @@ class ActionHandler:
self.main_window.connect('delete-event', self._quit)
self.main_window.connect('key-press-event', self._handle_key_press)
self.get_page = self.main_window.get_page
- self.get_flow_graph = self.main_window.get_flow_graph
self.get_focus_flag = self.main_window.get_focus_flag
#setup the messages
- Messages.register_messenger(self.main_window.add_report_line)
+ Messages.register_messenger(self.main_window.add_console_line)
Messages.send_init(platform)
#initialize
self.init_file_paths = file_paths
+ self.init = False
Actions.APPLICATION_INITIALIZE()
def _handle_key_press(self, widget, event):
@@ -104,6 +104,14 @@ class ActionHandler:
def _handle_action(self, action, *args):
#print action
+ main = self.main_window
+ page = main.get_page()
+ flow_graph = page.get_flow_graph() if page else None
+
+ def flow_graph_update(fg=flow_graph):
+ main.vars.update_gui()
+ fg.update()
+
##################################################
# Initialize/Quit
##################################################
@@ -112,12 +120,13 @@ class ActionHandler:
self.init_file_paths = filter(os.path.exists, Preferences.get_open_files())
if not self.init_file_paths: self.init_file_paths = ['']
for file_path in self.init_file_paths:
- if file_path: self.main_window.new_page(file_path) #load pages from file paths
+ if file_path: main.new_page(file_path) #load pages from file paths
if Preferences.file_open() in self.init_file_paths:
- self.main_window.new_page(Preferences.file_open(), show=True)
- if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
+ main.new_page(Preferences.file_open(), show=True)
+ if not self.get_page():
+ main.new_page() # ensure that at least a blank page exists
- self.main_window.btwin.search_entry.hide()
+ main.btwin.search_entry.hide()
# Disable all actions, then re-enable a few
for action in Actions.get_all_actions():
@@ -128,14 +137,17 @@ class ActionHandler:
Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY,
Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY,
Actions.TYPES_WINDOW_DISPLAY, Actions.TOGGLE_BLOCKS_WINDOW,
- Actions.TOGGLE_REPORTS_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
+ Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
Actions.TOOLS_RUN_FDESIGN, Actions.TOGGLE_SCROLL_LOCK,
- Actions.CLEAR_REPORTS, Actions.SAVE_REPORTS,
+ Actions.CLEAR_CONSOLE, Actions.SAVE_CONSOLE,
Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID,
Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB,
Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY,
Actions.FLOW_GRAPH_OPEN_QSS_THEME,
+ Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
+ Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR,
+ Actions.TOGGLE_HIDE_VARIABLES,
Actions.SELECT_ALL,
):
action.set_sensitive(True)
@@ -144,9 +156,9 @@ class ActionHandler:
if ParseXML.xml_failures:
Messages.send_xml_errors_if_any(ParseXML.xml_failures)
Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(True)
-
+ self.init = True
elif action == Actions.APPLICATION_QUIT:
- if self.main_window.close_pages():
+ if main.close_pages():
gtk.main_quit()
exit(0)
##################################################
@@ -155,27 +167,27 @@ class ActionHandler:
elif action == Actions.ELEMENT_SELECT:
pass #do nothing, update routines below
elif action == Actions.NOTHING_SELECT:
- self.get_flow_graph().unselect()
+ flow_graph.unselect()
elif action == Actions.SELECT_ALL:
- self.get_flow_graph().select_all()
+ flow_graph.select_all()
##################################################
# Enable/Disable
##################################################
elif action == Actions.BLOCK_ENABLE:
- if self.get_flow_graph().enable_selected(True):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.enable_selected(True):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.BLOCK_DISABLE:
- if self.get_flow_graph().enable_selected(False):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.enable_selected(False):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.BLOCK_BYPASS:
- if self.get_flow_graph().bypass_selected():
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.bypass_selected():
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
##################################################
# Cut/Copy/Paste
##################################################
@@ -183,20 +195,20 @@ class ActionHandler:
Actions.BLOCK_COPY()
Actions.ELEMENT_DELETE()
elif action == Actions.BLOCK_COPY:
- self.clipboard = self.get_flow_graph().copy_to_clipboard()
+ self.clipboard = flow_graph.copy_to_clipboard()
elif action == Actions.BLOCK_PASTE:
if self.clipboard:
- self.get_flow_graph().paste_from_clipboard(self.clipboard)
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ flow_graph.paste_from_clipboard(self.clipboard)
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
##################################################
# Create heir block
##################################################
elif action == Actions.BLOCK_CREATE_HIER:
# keeping track of coordinates for pasting later
- coords = self.get_flow_graph().get_selected_blocks()[0].get_coordinate()
+ coords = flow_graph.get_selected_blocks()[0].get_coordinate()
x,y = coords
x_min = x
y_min = y
@@ -205,15 +217,15 @@ class ActionHandler:
params = [];
# Save the state of the leaf blocks
- for block in self.get_flow_graph().get_selected_blocks():
+ for block in flow_graph.get_selected_blocks():
# Check for string variables within the blocks
for param in block.get_params():
- for variable in self.get_flow_graph().get_variables():
+ for variable in flow_graph.get_variables():
# If a block parameter exists that is a variable, create a parameter for it
if param.get_value() == variable.get_id():
params.append(param.get_value())
- for flow_param in self.get_flow_graph().get_parameters():
+ for flow_param in flow_graph.get_parameters():
# If a block parameter exists that is a parameter, create a parameter for it
if param.get_value() == flow_param.get_id():
params.append(param.get_value())
@@ -233,39 +245,39 @@ class ActionHandler:
sink_id = connection.get_sink().get_parent().get_id()
# If connected block is not in the list of selected blocks create a pad for it
- if self.get_flow_graph().get_block(source_id) not in self.get_flow_graph().get_selected_blocks():
+ if flow_graph.get_block(source_id) not in flow_graph.get_selected_blocks():
pads.append({'key': connection.get_sink().get_key(), 'coord': connection.get_source().get_coordinate(), 'block_id' : block.get_id(), 'direction': 'source'})
- if self.get_flow_graph().get_block(sink_id) not in self.get_flow_graph().get_selected_blocks():
+ if flow_graph.get_block(sink_id) not in flow_graph.get_selected_blocks():
pads.append({'key': connection.get_source().get_key(), 'coord': connection.get_sink().get_coordinate(), 'block_id' : block.get_id(), 'direction': 'sink'})
# Copy the selected blocks and paste them into a new page
# then move the flowgraph to a reasonable position
Actions.BLOCK_COPY()
- self.main_window.new_page()
+ main.new_page()
Actions.BLOCK_PASTE()
coords = (x_min,y_min)
- self.get_flow_graph().move_selected(coords)
+ flow_graph.move_selected(coords)
# Set flow graph to heir block type
- top_block = self.get_flow_graph().get_block("top_block")
+ top_block = flow_graph.get_block("top_block")
top_block.get_param('generate_options').set_value('hb')
# this needs to be a unique name
top_block.get_param('id').set_value('new_heir')
# Remove the default samp_rate variable block that is created
- remove_me = self.get_flow_graph().get_block("samp_rate")
- self.get_flow_graph().remove_element(remove_me)
+ remove_me = flow_graph.get_block("samp_rate")
+ flow_graph.remove_element(remove_me)
# Add the param blocks along the top of the window
x_pos = 150
for param in params:
- param_id = self.get_flow_graph().add_new_block('parameter',(x_pos,10))
- param_block = self.get_flow_graph().get_block(param_id)
+ param_id = flow_graph.add_new_block('parameter',(x_pos,10))
+ param_block = flow_graph.get_block(param_id)
param_block.get_param('id').set_value(param)
x_pos = x_pos + 100
@@ -274,13 +286,13 @@ class ActionHandler:
if pad['direction'] == 'sink':
# Add new PAD_SINK block to the canvas
- pad_id = self.get_flow_graph().add_new_block('pad_sink', pad['coord'])
+ pad_id = flow_graph.add_new_block('pad_sink', pad['coord'])
# setup the references to the sink and source
- pad_block = self.get_flow_graph().get_block(pad_id)
+ pad_block = flow_graph.get_block(pad_id)
pad_sink = pad_block.get_sinks()[0]
- source_block = self.get_flow_graph().get_block(pad['block_id'])
+ source_block = flow_graph.get_block(pad['block_id'])
source = source_block.get_source(pad['key'])
# Ensure the port types match
@@ -292,16 +304,16 @@ class ActionHandler:
pad_block.type_controller_modify(1)
# Connect the pad to the proper sinks
- new_connection = self.get_flow_graph().connect(source,pad_sink)
+ new_connection = flow_graph.connect(source,pad_sink)
elif pad['direction'] == 'source':
- pad_id = self.get_flow_graph().add_new_block('pad_source', pad['coord'])
+ pad_id = flow_graph.add_new_block('pad_source', pad['coord'])
# setup the references to the sink and source
- pad_block = self.get_flow_graph().get_block(pad_id)
+ pad_block = flow_graph.get_block(pad_id)
pad_source = pad_block.get_sources()[0]
- sink_block = self.get_flow_graph().get_block(pad['block_id'])
+ sink_block = flow_graph.get_block(pad['block_id'])
sink = sink_block.get_sink(pad['key'])
# Ensure the port types match
@@ -312,140 +324,172 @@ class ActionHandler:
pad_block.type_controller_modify(1)
# Connect the pad to the proper sinks
- new_connection = self.get_flow_graph().connect(pad_source,sink)
+ new_connection = flow_graph.connect(pad_source,sink)
# update the new heir block flow graph
- self.get_flow_graph().update()
+ flow_graph_update()
##################################################
# Move/Rotate/Delete/Create
##################################################
elif action == Actions.BLOCK_MOVE:
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
+ elif action in Actions.BLOCK_ALIGNMENTS:
+ if flow_graph.align_selected(action):
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.BLOCK_ROTATE_CCW:
- if self.get_flow_graph().rotate_selected(90):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.rotate_selected(90):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.BLOCK_ROTATE_CW:
- if self.get_flow_graph().rotate_selected(-90):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.rotate_selected(-90):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.ELEMENT_DELETE:
- if self.get_flow_graph().remove_selected():
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
+ if flow_graph.remove_selected():
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
Actions.NOTHING_SELECT()
- self.get_page().set_saved(False)
+ page.set_saved(False)
elif action == Actions.ELEMENT_CREATE:
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
Actions.NOTHING_SELECT()
- self.get_page().set_saved(False)
+ page.set_saved(False)
elif action == Actions.BLOCK_INC_TYPE:
- if self.get_flow_graph().type_controller_modify_selected(1):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.type_controller_modify_selected(1):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.BLOCK_DEC_TYPE:
- if self.get_flow_graph().type_controller_modify_selected(-1):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.type_controller_modify_selected(-1):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.PORT_CONTROLLER_INC:
- if self.get_flow_graph().port_controller_modify_selected(1):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.port_controller_modify_selected(1):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
elif action == Actions.PORT_CONTROLLER_DEC:
- if self.get_flow_graph().port_controller_modify_selected(-1):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ if flow_graph.port_controller_modify_selected(-1):
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
##################################################
# Window stuff
##################################################
elif action == Actions.ABOUT_WINDOW_DISPLAY:
- platform = self.get_flow_graph().get_parent()
+ platform = flow_graph.get_parent()
Dialogs.AboutDialog(platform.config)
elif action == Actions.HELP_WINDOW_DISPLAY:
Dialogs.HelpDialog()
elif action == Actions.TYPES_WINDOW_DISPLAY:
- Dialogs.TypesDialog(self.get_flow_graph().get_parent())
+ Dialogs.TypesDialog(flow_graph.get_parent())
elif action == Actions.ERRORS_WINDOW_DISPLAY:
- Dialogs.ErrorsDialog(self.get_flow_graph())
- elif action == Actions.TOGGLE_REPORTS_WINDOW:
- if action.get_active():
- self.main_window.reports_scrolled_window.show()
- else:
- self.main_window.reports_scrolled_window.hide()
+ Dialogs.ErrorsDialog(flow_graph)
+ elif action == Actions.TOGGLE_CONSOLE_WINDOW:
+ main.update_panel_visibility(main.CONSOLE, action.get_active())
action.save_to_preferences()
elif action == Actions.TOGGLE_BLOCKS_WINDOW:
- if action.get_active():
- self.main_window.btwin.show()
- else:
- self.main_window.btwin.hide()
+ main.update_panel_visibility(main.BLOCKS, action.get_active())
action.save_to_preferences()
elif action == Actions.TOGGLE_SCROLL_LOCK:
active = action.get_active()
- self.main_window.text_display.scroll_lock = active
+ main.text_display.scroll_lock = active
if active:
- self.main_window.text_display.scroll_to_end()
+ main.text_display.scroll_to_end()
action.save_to_preferences()
- elif action == Actions.CLEAR_REPORTS:
- self.main_window.text_display.clear()
- elif action == Actions.SAVE_REPORTS:
- file_path = SaveReportsFileDialog(self.get_page().get_file_path()).run()
+ elif action == Actions.CLEAR_CONSOLE:
+ main.text_display.clear()
+ elif action == Actions.SAVE_CONSOLE:
+ file_path = SaveConsoleFileDialog(page.get_file_path()).run()
if file_path is not None:
- self.main_window.text_display.save(file_path)
+ main.text_display.save(file_path)
elif action == Actions.TOGGLE_HIDE_DISABLED_BLOCKS:
Actions.NOTHING_SELECT()
elif action == Actions.TOGGLE_AUTO_HIDE_PORT_LABELS:
action.save_to_preferences()
- for page in self.main_window.get_pages():
+ for page in main.get_pages():
page.get_flow_graph().create_shapes()
- elif action == Actions.TOGGLE_SNAP_TO_GRID:
+ elif action in (Actions.TOGGLE_SNAP_TO_GRID,
+ Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
+ Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB):
action.save_to_preferences()
- elif action == Actions.TOGGLE_SHOW_BLOCK_COMMENTS:
+ elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY:
action.save_to_preferences()
- elif action == Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB:
+ for page in main.get_pages():
+ flow_graph_update(page.get_flow_graph())
+ elif action == Actions.TOGGLE_HIDE_VARIABLES:
+ # Call the variable editor TOGGLE in case it needs to be showing
+ Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR()
+ Actions.NOTHING_SELECT()
action.save_to_preferences()
- elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY:
+ elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR:
+ # See if the variables are hidden
+ if Actions.TOGGLE_HIDE_VARIABLES.get_active():
+ # Force this to be shown
+ main.update_panel_visibility(main.VARIABLES, True)
+ action.set_active(True)
+ action.set_sensitive(False)
+ else:
+ if action.get_sensitive():
+ main.update_panel_visibility(main.VARIABLES, action.get_active())
+ else: # This is occurring after variables are un-hidden
+ # Leave it enabled
+ action.set_sensitive(True)
+ action.set_active(True)
+ #Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_sensitive(action.get_active())
action.save_to_preferences()
- for page in self.main_window.get_pages():
- page.get_flow_graph().update()
+ elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR:
+ if self.init:
+ md = gtk.MessageDialog(main,
+ gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
+ gtk.BUTTONS_CLOSE, "Moving the variable editor requires a restart of GRC.")
+ md.run()
+ md.destroy()
+ action.save_to_preferences()
##################################################
# Param Modifications
##################################################
elif action == Actions.BLOCK_PARAM_MODIFY:
- selected_block = self.get_flow_graph().get_selected_block()
+ if action.args:
+ selected_block = action.args[0]
+ else:
+ selected_block = flow_graph.get_selected_block()
if selected_block:
self.dialog = PropsDialog(selected_block)
response = gtk.RESPONSE_APPLY
while response == gtk.RESPONSE_APPLY: # rerun the dialog if Apply was hit
response = self.dialog.run()
if response in (gtk.RESPONSE_APPLY, gtk.RESPONSE_ACCEPT):
- self.get_flow_graph().update()
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_page().set_saved(False)
+ flow_graph_update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ page.set_saved(False)
else: # restore the current state
- n = self.get_page().get_state_cache().get_current_state()
- self.get_flow_graph().import_data(n)
- self.get_flow_graph().update()
+ n = page.get_state_cache().get_current_state()
+ flow_graph.import_data(n)
+ flow_graph_update()
if response == gtk.RESPONSE_APPLY:
# null action, that updates the main window
Actions.ELEMENT_SELECT()
self.dialog.destroy()
self.dialog = None
elif action == Actions.EXTERNAL_UPDATE:
- self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.get_flow_graph().update()
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ flow_graph_update()
if self.dialog is not None:
self.dialog.update_gui(force=True)
- self.get_page().set_saved(False)
+ page.set_saved(False)
+ elif action == Actions.VARIABLE_EDITOR_UPDATE:
+ page.get_state_cache().save_new_state(flow_graph.export_data())
+ flow_graph_update()
+ page.set_saved(False)
##################################################
# View Parser Errors
##################################################
@@ -455,35 +499,36 @@ class ActionHandler:
# Undo/Redo
##################################################
elif action == Actions.FLOW_GRAPH_UNDO:
- n = self.get_page().get_state_cache().get_prev_state()
+ n = page.get_state_cache().get_prev_state()
if n:
- self.get_flow_graph().unselect()
- self.get_flow_graph().import_data(n)
- self.get_flow_graph().update()
- self.get_page().set_saved(False)
+ flow_graph.unselect()
+ flow_graph.import_data(n)
+ flow_graph_update()
+ page.set_saved(False)
elif action == Actions.FLOW_GRAPH_REDO:
- n = self.get_page().get_state_cache().get_next_state()
+ n = page.get_state_cache().get_next_state()
if n:
- self.get_flow_graph().unselect()
- self.get_flow_graph().import_data(n)
- self.get_flow_graph().update()
- self.get_page().set_saved(False)
+ flow_graph.unselect()
+ flow_graph.import_data(n)
+ flow_graph_update()
+ page.set_saved(False)
##################################################
# New/Open/Save/Close
##################################################
elif action == Actions.FLOW_GRAPH_NEW:
- self.main_window.new_page()
+ main.new_page()
if args:
- self.get_flow_graph()._options_block.get_param('generate_options').set_value(args[0])
- self.get_flow_graph().update()
+ flow_graph._options_block.get_param('generate_options').set_value(args[0])
+ flow_graph_update()
elif action == Actions.FLOW_GRAPH_OPEN:
- file_paths = args if args else OpenFlowGraphFileDialog(self.get_page().get_file_path()).run()
+ 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
for i,file_path in enumerate(file_paths):
- self.main_window.new_page(file_path, show=(i==0))
+ main.new_page(file_path, show=(i==0))
Preferences.add_recent_file(file_path)
- self.main_window.tool_bar.refresh_submenus()
- self.main_window.menu_bar.refresh_submenus()
+ 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 +
@@ -496,97 +541,103 @@ class ActionHandler:
except Exception as e:
Messages.send("Failed to save QSS preference: " + str(e))
elif action == Actions.FLOW_GRAPH_CLOSE:
- self.main_window.close_page()
+ main.close_page()
elif action == Actions.FLOW_GRAPH_SAVE:
#read-only or undefined file path, do save-as
- if self.get_page().get_read_only() or not self.get_page().get_file_path():
+ if page.get_read_only() or not page.get_file_path():
Actions.FLOW_GRAPH_SAVE_AS()
#otherwise try to save
else:
try:
- ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path())
- self.get_flow_graph().grc_file_path = self.get_page().get_file_path()
- self.get_page().set_saved(True)
+ ParseXML.to_file(flow_graph.export_data(), page.get_file_path())
+ flow_graph.grc_file_path = page.get_file_path()
+ page.set_saved(True)
except IOError:
- Messages.send_fail_save(self.get_page().get_file_path())
- self.get_page().set_saved(False)
+ Messages.send_fail_save(page.get_file_path())
+ page.set_saved(False)
elif action == Actions.FLOW_GRAPH_SAVE_AS:
- file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run()
+ file_path = SaveFlowGraphFileDialog(page.get_file_path()).run()
if file_path is not None:
- self.get_page().set_file_path(file_path)
+ page.set_file_path(file_path)
Actions.FLOW_GRAPH_SAVE()
Preferences.add_recent_file(file_path)
- self.main_window.tool_bar.refresh_submenus()
- self.main_window.menu_bar.refresh_submenus()
+ main.tool_bar.refresh_submenus()
+ main.menu_bar.refresh_submenus()
elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
- file_path, background_transparent = SaveScreenShotDialog(self.get_page().get_file_path()).run()
+ file_path, background_transparent = SaveScreenShotDialog(page.get_file_path()).run()
if file_path is not None:
- pixbuf = self.get_flow_graph().get_drawing_area().get_screenshot(background_transparent)
+ pixbuf = flow_graph.get_drawing_area().get_screenshot(background_transparent)
pixbuf.save(file_path, Constants.IMAGE_FILE_EXTENSION[1:])
##################################################
# Gen/Exec/Stop
##################################################
elif action == Actions.FLOW_GRAPH_GEN:
- if not self.get_page().get_proc():
- if not self.get_page().get_saved() or not self.get_page().get_file_path():
+ if not page.get_proc():
+ if not page.get_saved() or not page.get_file_path():
Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved
- if self.get_page().get_saved() and self.get_page().get_file_path():
- generator = self.get_page().get_generator()
+ if page.get_saved() and page.get_file_path():
+ generator = page.get_generator()
try:
Messages.send_start_gen(generator.get_file_path())
generator.write()
- except Exception,e: Messages.send_fail_gen(e)
- else: self.generator = None
+ except Exception as e:
+ Messages.send_fail_gen(e)
+ else:
+ self.generator = None
elif action == Actions.FLOW_GRAPH_EXEC:
- if not self.get_page().get_proc():
+ if not page.get_proc():
Actions.FLOW_GRAPH_GEN()
xterm = self.platform.config.xterm_executable
if Preferences.xterm_missing() != xterm:
if not os.path.exists(xterm):
Dialogs.MissingXTermDialog(xterm)
Preferences.xterm_missing(xterm)
- if self.get_page().get_saved() and self.get_page().get_file_path():
- Executor.ExecFlowGraphThread(self)
+ if page.get_saved() and page.get_file_path():
+ Executor.ExecFlowGraphThread(
+ flow_graph_page=page,
+ xterm_executable=xterm,
+ callback=self.update_exec_stop
+ )
elif action == Actions.FLOW_GRAPH_KILL:
- if self.get_page().get_proc():
+ if page.get_proc():
try:
- self.get_page().get_proc().kill()
+ page.get_proc().kill()
except:
- print "could not kill process: %d" % self.get_page().get_proc().pid
+ print "could not kill process: %d" % page.get_proc().pid
elif action == Actions.PAGE_CHANGE: # pass and run the global actions
pass
elif action == Actions.RELOAD_BLOCKS:
self.platform.load_blocks()
- self.main_window.btwin.clear()
- self.platform.load_block_tree(self.main_window.btwin)
+ main.btwin.clear()
+ self.platform.load_block_tree(main.btwin)
Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool(
ParseXML.xml_failures))
Messages.send_xml_errors_if_any(ParseXML.xml_failures)
# Force a redraw of the graph, by getting the current state and re-importing it
- self.main_window.update_pages()
+ main.update_pages()
elif action == Actions.FIND_BLOCKS:
- self.main_window.btwin.show()
- self.main_window.btwin.search_entry.show()
- self.main_window.btwin.search_entry.grab_focus()
+ main.update_panel_visibility(main.BLOCKS, True)
+ main.btwin.search_entry.show()
+ main.btwin.search_entry.grab_focus()
elif action == Actions.OPEN_HIER:
- for b in self.get_flow_graph().get_selected_blocks():
+ for b in flow_graph.get_selected_blocks():
if b._grc_source:
- self.main_window.new_page(b._grc_source, show=True)
+ main.new_page(b._grc_source, show=True)
elif action == Actions.BUSSIFY_SOURCES:
n = {'name':'bus', 'type':'bus'}
- for b in self.get_flow_graph().get_selected_blocks():
+ for b in flow_graph.get_selected_blocks():
b.bussify(n, 'source')
- self.get_flow_graph()._old_selected_port = None
- self.get_flow_graph()._new_selected_port = None
+ flow_graph._old_selected_port = None
+ flow_graph._new_selected_port = None
Actions.ELEMENT_CREATE()
elif action == Actions.BUSSIFY_SINKS:
n = {'name':'bus', 'type':'bus'}
- for b in self.get_flow_graph().get_selected_blocks():
+ for b in flow_graph.get_selected_blocks():
b.bussify(n, 'sink')
- self.get_flow_graph()._old_selected_port = None
- self.get_flow_graph()._new_selected_port = None
+ flow_graph._old_selected_port = None
+ flow_graph._new_selected_port = None
Actions.ELEMENT_CREATE()
elif action == Actions.TOOLS_RUN_FDESIGN:
@@ -598,15 +649,22 @@ class ActionHandler:
##################################################
# Global Actions for all States
##################################################
- selected_block = self.get_flow_graph().get_selected_block()
- selected_blocks = self.get_flow_graph().get_selected_blocks()
+ page = main.get_page() # page and flowgraph might have changed
+ flow_graph = page.get_flow_graph() if page else None
+
+ selected_blocks = flow_graph.get_selected_blocks()
+ selected_block = selected_blocks[0] if selected_blocks else None
#update general buttons
- Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid())
- Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements()))
+ Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not flow_graph.is_valid())
+ Actions.ELEMENT_DELETE.set_sensitive(bool(flow_graph.get_selected_elements()))
Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(selected_block))
Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(selected_blocks))
Actions.BLOCK_ROTATE_CW.set_sensitive(bool(selected_blocks))
+ #update alignment options
+ for act in Actions.BLOCK_ALIGNMENTS:
+ if act:
+ act.set_sensitive(len(selected_blocks) > 1)
#update cut/copy/paste
Actions.BLOCK_CUT.set_sensitive(bool(selected_blocks))
Actions.BLOCK_COPY.set_sensitive(bool(selected_blocks))
@@ -631,17 +689,17 @@ class ActionHandler:
#set the exec and stop buttons
self.update_exec_stop()
#saved status
- Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved())
- self.main_window.update()
+ Actions.FLOW_GRAPH_SAVE.set_sensitive(not page.get_saved())
+ main.update()
try: #set the size of the flow graph area (if changed)
- new_size = (self.get_flow_graph().get_option('window_size') or
+ new_size = (flow_graph.get_option('window_size') or
self.platform.config.default_canvas_size)
- if self.get_flow_graph().get_size() != tuple(new_size):
- self.get_flow_graph().set_size(*new_size)
+ if flow_graph.get_size() != tuple(new_size):
+ flow_graph.set_size(*new_size)
except: pass
#draw the flow graph
- self.get_flow_graph().update_selected()
- self.get_flow_graph().queue_draw()
+ flow_graph.update_selected()
+ flow_graph.queue_draw()
return True #action was handled
def update_exec_stop(self):
@@ -649,7 +707,7 @@ class ActionHandler:
Update the exec and stop buttons.
Lock and unlock the mutex for race conditions with exec flow graph threads.
"""
- sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_proc()
+ sensitive = self.main_window.get_flow_graph().is_valid() and not self.get_page().get_proc()
Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive)
Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive)
Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_proc() is not None)
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index 354e536a82..9b2af36b93 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -92,6 +92,7 @@ class _ActionBase(object):
self.set_accel_group(get_accel_group())
self.set_accel_path(accel_path)
gtk.accel_map_add_entry(accel_path, keyval, mod_mask)
+ self.args = None
def __str__(self):
"""
@@ -105,10 +106,11 @@ class _ActionBase(object):
def __repr__(self): return str(self)
- def __call__(self):
+ def __call__(self, *args):
"""
Emit the activate signal when called with ().
"""
+ self.args = args
self.emit('activate')
@@ -171,6 +173,7 @@ class ToggleAction(gtk.ToggleAction, _ActionBase):
########################################################################
PAGE_CHANGE = Action()
EXTERNAL_UPDATE = Action()
+VARIABLE_EDITOR_UPDATE = Action()
FLOW_GRAPH_NEW = Action(
label='_New',
tooltip='Create a new flow graph',
@@ -253,6 +256,45 @@ BLOCK_ROTATE_CW = Action(
stock_id=gtk.STOCK_GO_FORWARD,
keypresses=(gtk.keysyms.Right, NO_MODS_MASK),
)
+BLOCK_VALIGN_TOP = Action(
+ label='Vertical Align Top',
+ tooltip='Align tops of selected blocks',
+ keypresses=(gtk.keysyms.t, gtk.gdk.SHIFT_MASK),
+)
+BLOCK_VALIGN_MIDDLE = Action(
+ label='Vertical Align Middle',
+ tooltip='Align centers of selected blocks vertically',
+ keypresses=(gtk.keysyms.m, gtk.gdk.SHIFT_MASK),
+)
+BLOCK_VALIGN_BOTTOM = Action(
+ label='Vertical Align Bottom',
+ tooltip='Align bottoms of selected blocks',
+ keypresses=(gtk.keysyms.b, gtk.gdk.SHIFT_MASK),
+)
+BLOCK_HALIGN_LEFT = Action(
+ label='Horizontal Align Left',
+ tooltip='Align left edges of blocks selected blocks',
+ keypresses=(gtk.keysyms.l, gtk.gdk.SHIFT_MASK),
+)
+BLOCK_HALIGN_CENTER = Action(
+ label='Horizontal Align Center',
+ tooltip='Align centers of selected blocks horizontally',
+ keypresses=(gtk.keysyms.c, gtk.gdk.SHIFT_MASK),
+)
+BLOCK_HALIGN_RIGHT = Action(
+ label='Horizontal Align Right',
+ tooltip='Align right edges of selected blocks',
+ keypresses=(gtk.keysyms.r, gtk.gdk.SHIFT_MASK),
+)
+BLOCK_ALIGNMENTS = [
+ BLOCK_VALIGN_TOP,
+ BLOCK_VALIGN_MIDDLE,
+ BLOCK_VALIGN_BOTTOM,
+ None,
+ BLOCK_HALIGN_LEFT,
+ BLOCK_HALIGN_CENTER,
+ BLOCK_HALIGN_RIGHT,
+]
BLOCK_PARAM_MODIFY = Action(
label='_Properties',
tooltip='Modify params for the selected block',
@@ -288,6 +330,26 @@ TOGGLE_HIDE_DISABLED_BLOCKS = ToggleAction(
stock_id=gtk.STOCK_MISSING_IMAGE,
keypresses=(gtk.keysyms.d, gtk.gdk.CONTROL_MASK),
)
+TOGGLE_HIDE_VARIABLES = ToggleAction(
+ label='Hide Variables',
+ tooltip='Hide all variable blocks',
+ preference_name='hide_variables',
+ default=False,
+)
+TOGGLE_FLOW_GRAPH_VAR_EDITOR = ToggleAction(
+ label='Show _Variable Editor',
+ tooltip='Show the variable editor. Modify variables and imports in this flow graph',
+ stock_id=gtk.STOCK_EDIT,
+ default=True,
+ keypresses=(gtk.keysyms.e, gtk.gdk.CONTROL_MASK),
+ preference_name='variable_editor_visable',
+)
+TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction(
+ label='Move the Variable Editor to the Sidebar',
+ tooltip='Move the variable editor to the sidebar',
+ default=False,
+ preference_name='variable_editor_sidebar',
+)
TOGGLE_AUTO_HIDE_PORT_LABELS = ToggleAction(
label='Auto-Hide _Port Labels',
tooltip='Automatically hide port labels',
@@ -340,11 +402,11 @@ ERRORS_WINDOW_DISPLAY = Action(
tooltip='View flow graph errors',
stock_id=gtk.STOCK_DIALOG_ERROR,
)
-TOGGLE_REPORTS_WINDOW = ToggleAction(
- label='Show _Reports Panel',
- tooltip='Toggle visibility of the Report widget',
+TOGGLE_CONSOLE_WINDOW = ToggleAction(
+ label='Show _Console Panel',
+ tooltip='Toggle visibility of the console',
keypresses=(gtk.keysyms.r, gtk.gdk.CONTROL_MASK),
- preference_name='reports_window_visible'
+ preference_name='console_window_visible'
)
TOGGLE_BLOCKS_WINDOW = ToggleAction(
label='Show _Block Tree Panel',
@@ -353,8 +415,8 @@ TOGGLE_BLOCKS_WINDOW = ToggleAction(
preference_name='blocks_window_visible'
)
TOGGLE_SCROLL_LOCK = ToggleAction(
- label='Reports Scroll _Lock',
- tooltip='Toggle scroll lock for the report window',
+ label='Console Scroll _Lock',
+ tooltip='Toggle scroll lock for the console window',
preference_name='scroll_lock'
)
ABOUT_WINDOW_DISPLAY = Action(
@@ -421,14 +483,14 @@ FIND_BLOCKS = Action(
keypresses=(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
gtk.keysyms.slash, NO_MODS_MASK),
)
-CLEAR_REPORTS = Action(
- label='_Clear Reports',
- tooltip='Clear Reports',
+CLEAR_CONSOLE = Action(
+ label='_Clear Console',
+ tooltip='Clear Console',
stock_id=gtk.STOCK_CLEAR,
)
-SAVE_REPORTS = Action(
- label='_Save Reports',
- tooltip='Save Reports',
+SAVE_CONSOLE = Action(
+ label='_Save Console',
+ tooltip='Save Console',
stock_id=gtk.STOCK_SAVE,
)
OPEN_HIER = Action(
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index cb101111cd..a4819b973c 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -31,6 +31,7 @@ TOOLBAR_LIST = (
Actions.FLOW_GRAPH_SAVE,
Actions.FLOW_GRAPH_CLOSE,
None,
+ Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
Actions.FLOW_GRAPH_SCREEN_CAPTURE,
None,
Actions.BLOCK_CUT,
@@ -86,6 +87,7 @@ MENU_BAR_LIST = (
None,
Actions.BLOCK_ROTATE_CCW,
Actions.BLOCK_ROTATE_CW,
+ (gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS),
None,
Actions.BLOCK_ENABLE,
Actions.BLOCK_DISABLE,
@@ -96,10 +98,14 @@ MENU_BAR_LIST = (
(gtk.Action('View', '_View', None, None), [
Actions.TOGGLE_BLOCKS_WINDOW,
None,
- Actions.TOGGLE_REPORTS_WINDOW,
+ Actions.TOGGLE_CONSOLE_WINDOW,
Actions.TOGGLE_SCROLL_LOCK,
- Actions.SAVE_REPORTS,
- Actions.CLEAR_REPORTS,
+ Actions.SAVE_CONSOLE,
+ Actions.CLEAR_CONSOLE,
+ None,
+ Actions.TOGGLE_HIDE_VARIABLES,
+ Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
+ Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR,
None,
Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 815982c318..55c8805fae 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -22,13 +22,8 @@ pygtk.require('2.0')
import gtk
import pango
-from . import Actions, Colors, Utils
+from . import Actions, Colors, Utils, Constants
-from .Constants import (
- BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION,
- PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT,
- BORDER_PROXIMITY_SENSITIVITY
-)
from . Element import Element
from ..core.Param import num_to_str
from ..core.utils import odict
@@ -98,18 +93,19 @@ class Block(Element, _Block):
Returns:
the coordinate tuple (x, y) or (0, 0) if failure
"""
+ proximity = Constants.BORDER_PROXIMITY_SENSITIVITY
try: #should evaluate to tuple
coor = eval(self.get_param('_coordinate').get_value())
x, y = map(int, coor)
fgW,fgH = self.get_parent().get_size()
if x <= 0:
x = 0
- elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY:
- x = fgW - BORDER_PROXIMITY_SENSITIVITY
+ elif x >= fgW - proximity:
+ x = fgW - proximity
if y <= 0:
y = 0
- elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY:
- y = fgH - BORDER_PROXIMITY_SENSITIVITY
+ elif y >= fgH - proximity:
+ y = fgH - proximity
return (x, y)
except:
self.set_coordinate((0, 0))
@@ -175,8 +171,8 @@ class Block(Element, _Block):
rotation = eval(self.get_param('_rotation').get_value())
return int(rotation)
except:
- self.set_rotation(POSSIBLE_ROTATIONS[0])
- return POSSIBLE_ROTATIONS[0]
+ self.set_rotation(Constants.POSSIBLE_ROTATIONS[0])
+ return Constants.POSSIBLE_ROTATIONS[0]
def set_rotation(self, rot):
"""
@@ -204,23 +200,24 @@ class Block(Element, _Block):
#create the main layout
layout = gtk.DrawingArea().create_pango_layout('')
layouts.append(layout)
- layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=BLOCK_FONT))
+ layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=Constants.BLOCK_FONT))
self.label_width, self.label_height = layout.get_pixel_size()
#display the params
if self.is_dummy_block:
markups = [
- '<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>'.format(font=PARAM_FONT, key=self._key)
+ '<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>'
+ ''.format(font=Constants.PARAM_FONT, key=self._key)
]
else:
markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')]
if markups:
layout = gtk.DrawingArea().create_pango_layout('')
- layout.set_spacing(LABEL_SEPARATION*pango.SCALE)
+ layout.set_spacing(Constants.LABEL_SEPARATION * pango.SCALE)
layout.set_markup('\n'.join(markups))
layouts.append(layout)
w, h = layout.get_pixel_size()
self.label_width = max(w, self.label_width)
- self.label_height += h + LABEL_SEPARATION
+ self.label_height += h + Constants.LABEL_SEPARATION
width = self.label_width
height = self.label_height
#setup the pixmap
@@ -235,31 +232,31 @@ class Block(Element, _Block):
if i == 0: w_off = (width-w)/2
else: w_off = 0
pixmap.draw_layout(gc, w_off, h_off, layout)
- h_off = h + h_off + LABEL_SEPARATION
+ h_off = h + h_off + Constants.LABEL_SEPARATION
#create vertical and horizontal pixmaps
self.horizontal_label = pixmap
if self.is_vertical():
self.vertical_label = self.get_parent().new_pixmap(height, width)
Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label)
#calculate width and height needed
- W = self.label_width + 2 * BLOCK_LABEL_PADDING
+ W = self.label_width + 2 * Constants.BLOCK_LABEL_PADDING
def get_min_height_for_ports():
visible_ports = filter(lambda p: not p.get_hide(), ports)
- min_height = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION
+ min_height = 2*Constants.PORT_BORDER_SEPARATION + len(visible_ports) * Constants.PORT_SEPARATION
if visible_ports:
min_height -= ports[0].H
return min_height
H = max(
[ # labels
- self.label_height + 2 * BLOCK_LABEL_PADDING
+ self.label_height + 2 * Constants.BLOCK_LABEL_PADDING
] +
[ # ports
get_min_height_for_ports() for ports in (self.get_sources_gui(), self.get_sinks_gui())
] +
[ # bus ports only
- 2 * PORT_BORDER_SEPARATION +
- sum([port.H + PORT_SPACING for port in ports if port.get_type() == 'bus']) - PORT_SPACING
+ 2 * Constants.PORT_BORDER_SEPARATION +
+ sum([port.H + Constants.PORT_SPACING for port in ports if port.get_type() == 'bus']) - Constants.PORT_SPACING
for ports in (self.get_sources_gui(), self.get_sinks_gui())
]
)
@@ -284,12 +281,12 @@ class Block(Element, _Block):
block=self,
comment=comment,
complexity=complexity,
- font=BLOCK_FONT))
+ font=Constants.BLOCK_FONT))
# Setup the pixel map. Make sure that layout not empty
width, height = layout.get_pixel_size()
if width and height:
- padding = BLOCK_LABEL_PADDING
+ padding = Constants.BLOCK_LABEL_PADDING
pixmap = self.get_parent().new_pixmap(width + 2 * padding,
height + 2 * padding)
gc = pixmap.new_gc()
@@ -321,9 +318,9 @@ class Block(Element, _Block):
)
#draw label image
if self.is_horizontal():
- window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1)
+ window.draw_drawable(gc, self.horizontal_label, 0, 0, x+Constants.BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1)
elif self.is_vertical():
- window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1)
+ window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+Constants.BLOCK_LABEL_PADDING, -1, -1)
def what_is_selected(self, coor, coor_m=None):
"""
@@ -345,6 +342,10 @@ class Block(Element, _Block):
if not self._comment_pixmap:
return
x, y = self.get_coordinate()
- y += self.H if self.is_horizontal() else self.W
- window.draw_drawable(gc, self._comment_pixmap, 0, 0, x,
- y + BLOCK_LABEL_PADDING, -1, -1)
+
+ if self.is_horizontal():
+ y += self.H + Constants.BLOCK_LABEL_PADDING
+ else:
+ x += self.H + Constants.BLOCK_LABEL_PADDING
+
+ window.draw_drawable(gc, self._comment_pixmap, 0, 0, x, y, -1, -1)
diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py
index 050b363cdd..d322afa410 100644
--- a/grc/gui/Colors.py
+++ b/grc/gui/Colors.py
@@ -39,7 +39,7 @@ try:
#block color constants
BLOCK_ENABLED_COLOR = get_color('#F1ECFF')
BLOCK_DISABLED_COLOR = get_color('#CCCCCC')
- BLOCK_BYPASSED_COLOR = get_color('#FFFFE6')
+ BLOCK_BYPASSED_COLOR = get_color('#F4FF81')
#connection color constants
CONNECTION_ENABLED_COLOR = get_color('black')
CONNECTION_DISABLED_COLOR = get_color('#BBBBBB')
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index e267c6ca02..022564cd77 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -40,7 +40,7 @@ MIN_DIALOG_WIDTH = 500
MIN_DIALOG_HEIGHT = 500
# default sizes
DEFAULT_BLOCKS_WINDOW_WIDTH = 100
-DEFAULT_REPORTS_WINDOW_WIDTH = 100
+DEFAULT_CONSOLE_WINDOW_WIDTH = 100
DEFAULT_CANVAS_SIZE_DEFAULT = 1280, 1024
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index 6cfdd50a34..1d114356c8 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -92,21 +92,21 @@ class TextDisplay(SimpleTextDisplay):
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
def save(self, file_path):
- report_file = open(file_path, 'w')
+ console_file = open(file_path, 'w')
buffer = self.get_buffer()
- report_file.write(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True))
- report_file.close()
+ console_file.write(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True))
+ console_file.close()
# Callback functions to handle the scrolling lock and clear context menus options
# Action functions are set by the ActionHandler's init function
def clear_cb(self, menu_item, web_view):
- Actions.CLEAR_REPORTS()
+ Actions.CLEAR_CONSOLE()
def scroll_back_cb(self, menu_item, web_view):
Actions.TOGGLE_SCROLL_LOCK()
def save_cb(self, menu_item, web_view):
- Actions.SAVE_REPORTS()
+ Actions.SAVE_CONSOLE()
def populate_popup(self, view, menu):
"""Create a popup menu for the scroll lock and clear functions"""
diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py
index f75f514cdb..bf9eecb9a8 100644
--- a/grc/gui/Executor.py
+++ b/grc/gui/Executor.py
@@ -30,7 +30,7 @@ from ..core import Messages
class ExecFlowGraphThread(threading.Thread):
"""Execute the flow graph as a new process and wait on it to finish."""
- def __init__(self, action_handler):
+ def __init__(self, flow_graph_page, xterm_executable, callback):
"""
ExecFlowGraphThread constructor.
@@ -38,19 +38,17 @@ class ExecFlowGraphThread(threading.Thread):
action_handler: an instance of an ActionHandler
"""
threading.Thread.__init__(self)
- self.update_exec_stop = action_handler.update_exec_stop
- self.flow_graph = action_handler.get_flow_graph()
- self.xterm_executable = action_handler.platform.config.xterm_executable
- #store page and dont use main window calls in run
- self.page = action_handler.get_page()
- #get the popen
+
+ self.page = flow_graph_page # store page and dont use main window calls in run
+ self.xterm_executable = xterm_executable
+ self.update_callback = callback
+
try:
- self.p = self._popen()
- self.page.set_proc(self.p)
- #update
- self.update_exec_stop()
+ self.process = self._popen()
+ self.page.set_proc(self.process)
+ self.update_callback()
self.start()
- except Exception, e:
+ except Exception as e:
Messages.send_verbose_exec(str(e))
Messages.send_end_exec()
@@ -58,7 +56,7 @@ class ExecFlowGraphThread(threading.Thread):
"""
Execute this python flow graph.
"""
- run_command = self.flow_graph.get_option('run_command')
+ run_command = self.page.get_flow_graph().get_option('run_command')
generator = self.page.get_generator()
try:
@@ -90,19 +88,19 @@ class ExecFlowGraphThread(threading.Thread):
Wait on the executing process by reading from its stdout.
Use gobject.idle_add when calling functions that modify gtk objects.
"""
- #handle completion
+ # handle completion
r = "\n"
while r:
gobject.idle_add(Messages.send_verbose_exec, r)
- r = os.read(self.p.stdout.fileno(), 1024)
- self.p.poll()
+ r = os.read(self.process.stdout.fileno(), 1024)
+ self.process.poll()
gobject.idle_add(self.done)
def done(self):
"""Perform end of execution tasks."""
- Messages.send_end_exec(self.p.returncode)
+ Messages.send_end_exec(self.process.returncode)
self.page.set_proc(None)
- self.update_exec_stop()
+ self.update_callback()
###########################################################
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index 4b5770ad21..e9430b1f88 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -33,7 +33,7 @@ import Utils
##################################################
OPEN_FLOW_GRAPH = 'open flow graph'
SAVE_FLOW_GRAPH = 'save flow graph'
-SAVE_REPORTS = 'save reports'
+SAVE_CONSOLE = 'save console'
SAVE_IMAGE = 'save image'
OPEN_QSS_THEME = 'open qss theme'
@@ -43,46 +43,45 @@ File <b>$encode($filename)</b> Exists!\nWould you like to overwrite the existing
FILE_DNE_MARKUP_TMPL="""\
File <b>$encode($filename)</b> Does not Exist!"""
-##################################################
+
+
# File Filters
-##################################################
-##the filter for flow graph files
def get_flow_graph_files_filter():
filter = gtk.FileFilter()
filter.set_name('Flow Graph Files')
filter.add_pattern('*'+Preferences.file_extension())
return filter
+
def get_text_files_filter():
filter = gtk.FileFilter()
filter.set_name('Text Files')
filter.add_pattern('*'+TEXT_FILE_EXTENSION)
return filter
-##the filter for image files
+
def get_image_files_filter():
filter = gtk.FileFilter()
filter.set_name('Image Files')
filter.add_pattern('*'+IMAGE_FILE_EXTENSION)
return filter
-##the filter for all files
+
def get_all_files_filter():
filter = gtk.FileFilter()
filter.set_name('All Files')
filter.add_pattern('*')
return filter
-##the filter for qss files
+
def get_qss_themes_filter():
filter = gtk.FileFilter()
filter.set_name('QSS Themes')
filter.add_pattern('*.qss')
return filter
-##################################################
+
# File Dialogs
-##################################################
class FileDialogHelper(gtk.FileChooserDialog):
"""
A wrapper class for the gtk file chooser dialog.
@@ -105,6 +104,7 @@ class FileDialogHelper(gtk.FileChooserDialog):
self.set_local_only(True)
self.add_filter(get_all_files_filter())
+
class FileDialog(FileDialogHelper):
"""A dialog box to save or open flow graph files. This is a base class, do not use."""
@@ -124,8 +124,8 @@ class FileDialog(FileDialogHelper):
FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph to a File...')
self.add_and_set_filter(get_flow_graph_files_filter())
self.set_current_name(path.basename(current_file_path))
- elif self.type == SAVE_REPORTS:
- FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save Reports to a File...')
+ elif self.type == SAVE_CONSOLE:
+ FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save Console to a File...')
self.add_and_set_filter(get_text_files_filter())
file_path = path.splitext(path.basename(current_file_path))[0]
self.set_current_name(file_path) #show the current filename
@@ -164,11 +164,11 @@ class FileDialog(FileDialogHelper):
#############################################
# Handle Save Dialogs
#############################################
- if self.type in (SAVE_FLOW_GRAPH, SAVE_REPORTS, SAVE_IMAGE):
+ if self.type in (SAVE_FLOW_GRAPH, SAVE_CONSOLE, SAVE_IMAGE):
filename = self.get_filename()
extension = {
SAVE_FLOW_GRAPH: Preferences.file_extension(),
- SAVE_REPORTS: TEXT_FILE_EXTENSION,
+ SAVE_CONSOLE: TEXT_FILE_EXTENSION,
SAVE_IMAGE: IMAGE_FILE_EXTENSION,
}[self.type]
#append the missing file extension if the filter matches
@@ -205,11 +205,25 @@ class FileDialog(FileDialogHelper):
self.destroy()
return filename
-class OpenFlowGraphFileDialog(FileDialog): type = OPEN_FLOW_GRAPH
-class SaveFlowGraphFileDialog(FileDialog): type = SAVE_FLOW_GRAPH
-class OpenQSSFileDialog(FileDialog): type = OPEN_QSS_THEME
-class SaveReportsFileDialog(FileDialog): type = SAVE_REPORTS
-class SaveImageFileDialog(FileDialog): type = SAVE_IMAGE
+
+class OpenFlowGraphFileDialog(FileDialog):
+ type = OPEN_FLOW_GRAPH
+
+
+class SaveFlowGraphFileDialog(FileDialog):
+ type = SAVE_FLOW_GRAPH
+
+
+class OpenQSSFileDialog(FileDialog):
+ type = OPEN_QSS_THEME
+
+
+class SaveConsoleFileDialog(FileDialog):
+ type = SAVE_CONSOLE
+
+
+class SaveImageFileDialog(FileDialog):
+ type = SAVE_IMAGE
class SaveScreenShotDialog(SaveImageFileDialog):
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index 357f87c894..02d5197fb0 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -312,6 +312,47 @@ class FlowGraph(Element, _Flowgraph):
selected_block.move(delta_coordinate)
self.element_moved = True
+ def align_selected(self, calling_action=None):
+ """
+ Align the selected blocks.
+
+ Args:
+ calling_action: the action initiating the alignment
+
+ Returns:
+ True if changed, otherwise False
+ """
+ blocks = self.get_selected_blocks()
+ if calling_action is None or not blocks:
+ return False
+
+ # compute common boundary of selected objects
+ min_x, min_y = max_x, max_y = blocks[0].get_coordinate()
+ for selected_block in blocks:
+ x, y = selected_block.get_coordinate()
+ min_x, min_y = min(min_x, x), min(min_y, y)
+ x += selected_block.W
+ y += selected_block.H
+ max_x, max_y = max(max_x, x), max(max_y, y)
+ ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2
+
+ # align the blocks as requested
+ transform = {
+ Actions.BLOCK_VALIGN_TOP: lambda x, y, w, h: (x, min_y),
+ Actions.BLOCK_VALIGN_MIDDLE: lambda x, y, w, h: (x, ctr_y - h/2),
+ Actions.BLOCK_VALIGN_BOTTOM: lambda x, y, w, h: (x, max_y - h),
+ Actions.BLOCK_HALIGN_LEFT: lambda x, y, w, h: (min_x, y),
+ Actions.BLOCK_HALIGN_CENTER: lambda x, y, w, h: (ctr_x-w/2, y),
+ Actions.BLOCK_HALIGN_RIGHT: lambda x, y, w, h: (max_x - w, y),
+ }.get(calling_action, lambda *args: args)
+
+ for selected_block in blocks:
+ x, y = selected_block.get_coordinate()
+ w, h = selected_block.W, selected_block.H
+ selected_block.set_coordinate(transform(x, y, w, h))
+
+ return True
+
def rotate_selected(self, rotation):
"""
Rotate the selected blocks by multiples of 90 degrees.
@@ -322,7 +363,8 @@ class FlowGraph(Element, _Flowgraph):
Returns:
true if changed, otherwise false.
"""
- if not self.get_selected_blocks(): return False
+ if not self.get_selected_blocks():
+ return False
#initialize min and max coordinates
min_x, min_y = self.get_selected_block().get_coordinate()
max_x, max_y = self.get_selected_block().get_coordinate()
@@ -387,10 +429,14 @@ class FlowGraph(Element, _Flowgraph):
window.draw_rectangle(gc, False, x, y, w, h)
#draw blocks on top of connections
hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
+ hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active()
blocks = sorted(self.blocks, key=methodcaller('get_enabled'))
+
for element in chain(self.connections, blocks):
if hide_disabled_blocks and not element.get_enabled():
continue # skip hidden disabled blocks and connections
+ if hide_variables and (element.is_variable or element.is_import):
+ continue # skip hidden disabled blocks and connections
element.draw(gc, window)
#draw selected blocks on top of selected connections
for selected_element in self.get_selected_connections() + self.get_selected_blocks():
@@ -474,15 +520,15 @@ class FlowGraph(Element, _Flowgraph):
selected_port = None
selected = set()
#check the elements
+ hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
+ hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active()
for element in reversed(self.get_elements()):
+ if hide_disabled_blocks and not element.get_enabled():
+ continue # skip hidden disabled blocks and connections
+ if hide_variables and (element.is_variable or element.is_import):
+ continue # skip hidden disabled blocks and connections
selected_element = element.what_is_selected(coor, coor_m)
- if not selected_element: continue
- # hidden disabled connections, blocks and their ports can not be selected
- if Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() and (
- selected_element.is_block and not selected_element.get_enabled() or
- selected_element.is_connection and not selected_element.get_enabled() or
- selected_element.is_port and not selected_element.get_parent().get_enabled()
- ):
+ if not selected_element:
continue
#update the selected port information
if selected_element.is_port:
@@ -533,7 +579,8 @@ class FlowGraph(Element, _Flowgraph):
Returns:
a block or None
"""
- return self.get_selected_blocks() and self.get_selected_blocks()[0] or None
+ selected_blocks = self.get_selected_blocks()
+ return selected_blocks[0] if selected_blocks else None
def get_selected_elements(self):
"""
@@ -551,7 +598,8 @@ class FlowGraph(Element, _Flowgraph):
Returns:
a block, port, or connection or None
"""
- return self.get_selected_elements() and self.get_selected_elements()[0] or None
+ selected_elements = self.get_selected_elements()
+ return selected_elements[0] if selected_elements else None
def update_selected_elements(self):
"""
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 6da240d85b..1437391236 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -23,8 +23,9 @@ import gtk
from . import Bars, Actions, Preferences, Utils
from .BlockTreeWindow import BlockTreeWindow
+from .VariableEditor import VariableEditor
from .Constants import \
- NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH
+ NEW_FLOGRAPH_TITLE, DEFAULT_CONSOLE_WINDOW_WIDTH
from .Dialogs import TextDisplay, MessageDialogHelper
from .NotebookPage import NotebookPage
@@ -56,6 +57,7 @@ PAGE_TITLE_MARKUP_TMPL = """\
#end if
"""
+
############################################################
# Main window
############################################################
@@ -63,10 +65,15 @@ PAGE_TITLE_MARKUP_TMPL = """\
class MainWindow(gtk.Window):
"""The topmost window with menus, the tool bar, and other major windows."""
+ # Constants the action handler can use to indicate which panel visibility to change.
+ BLOCKS = 0
+ CONSOLE = 1
+ VARIABLES = 2
+
def __init__(self, platform, action_handler_callback):
"""
- MainWindow contructor
- Setup the menu, toolbar, flowgraph editor notebook, block selection window...
+ MainWindow constructor
+ Setup the menu, toolbar, flow graph editor notebook, block selection window...
"""
self._platform = platform
@@ -76,48 +83,80 @@ class MainWindow(gtk.Window):
(o.get_key(), o.get_name(), o.get_key() == generate_mode_default)
for o in gen_opts.get_options()]
- # load preferences
+ # Load preferences
Preferences.load(platform)
- #setup window
+
+ # Setup window
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
vbox = gtk.VBox()
- self.hpaned = gtk.HPaned()
self.add(vbox)
- #create the menu bar and toolbar
+
+ # Create the menu bar and toolbar
self.add_accel_group(Actions.get_accel_group())
self.menu_bar = Bars.MenuBar(generate_modes, action_handler_callback)
vbox.pack_start(self.menu_bar, False)
- self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback )
+ self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback)
vbox.pack_start(self.tool_bar, False)
- vbox.pack_start(self.hpaned)
- #create the notebook
+
+ # Main parent container for the different panels
+ self.container = gtk.HPaned()
+ vbox.pack_start(self.container)
+
+ # Create the notebook
self.notebook = gtk.Notebook()
self.page_to_be_closed = None
self.current_page = None
self.notebook.set_show_border(False)
- self.notebook.set_scrollable(True) #scroll arrows for page tabs
+ self.notebook.set_scrollable(True) # scroll arrows for page tabs
self.notebook.connect('switch-page', self._handle_page_change)
- #setup containers
- self.flow_graph_vpaned = gtk.VPaned()
- #flow_graph_box.pack_start(self.scrolled_window)
- self.flow_graph_vpaned.pack1(self.notebook)
- self.hpaned.pack1(self.flow_graph_vpaned)
- self.btwin = BlockTreeWindow(platform, self.get_flow_graph);
- self.hpaned.pack2(self.btwin, False) #dont allow resize
- #create the reports window
+
+ # Create the console window
self.text_display = TextDisplay()
- #house the reports in a scrolled window
- self.reports_scrolled_window = gtk.ScrolledWindow()
- self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- self.reports_scrolled_window.add(self.text_display)
- self.reports_scrolled_window.set_size_request(-1, DEFAULT_REPORTS_WINDOW_WIDTH)
- self.flow_graph_vpaned.pack2(self.reports_scrolled_window, False) #dont allow resize
- #load preferences and show the main window
+ self.console_window = gtk.ScrolledWindow()
+ self.console_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self.console_window.add(self.text_display)
+ self.console_window.set_size_request(-1, DEFAULT_CONSOLE_WINDOW_WIDTH)
+
+ # Create the block tree and variable panels
+ self.btwin = BlockTreeWindow(platform, self.get_flow_graph)
+ self.vars = VariableEditor(platform, self.get_flow_graph)
+
+ # Figure out which place to put the variable editor
+ self.left = gtk.VPaned()
+ self.right = gtk.VPaned()
+ self.left_subpanel = gtk.HPaned()
+
+ self.variable_panel_sidebar = Preferences.variable_editor_sidebar()
+ if self.variable_panel_sidebar:
+ self.left.pack1(self.notebook)
+ self.left.pack2(self.console_window, False)
+ self.right.pack1(self.btwin)
+ self.right.pack2(self.vars, False)
+ else:
+ # Put the variable editor in a panel with the console
+ self.left.pack1(self.notebook)
+ self.left_subpanel.pack1(self.console_window, shrink=False)
+ self.left_subpanel.pack2(self.vars, resize=False, shrink=True)
+ self.left.pack2(self.left_subpanel, False)
+
+ # Create the right panel
+ self.right.pack1(self.btwin)
+
+ self.container.pack1(self.left)
+ self.container.pack2(self.right, False)
+
+ # load preferences and show the main window
self.resize(*Preferences.main_window_size())
- self.flow_graph_vpaned.set_position(Preferences.reports_window_position())
- self.hpaned.set_position(Preferences.blocks_window_position())
+ self.container.set_position(Preferences.blocks_window_position())
+ self.left.set_position(Preferences.console_window_position())
+ if self.variable_panel_sidebar:
+ self.right.set_position(Preferences.variable_editor_position(sidebar=True))
+ else:
+ self.left_subpanel.set_position(Preferences.variable_editor_position())
+
self.show_all()
- self.reports_scrolled_window.hide()
+ self.console_window.hide()
+ self.vars.hide()
self.btwin.hide()
############################################################
@@ -148,14 +187,45 @@ class MainWindow(gtk.Window):
page_num: new page number
"""
self.current_page = self.notebook.get_nth_page(page_num)
- Messages.send_page_switch(self.current_page.get_file_path())
Actions.PAGE_CHANGE()
+ def update_panel_visibility(self, panel, visibility=True):
+ """
+ Handles changing visibility of panels.
+ """
+ # Set the visibility for the requested panel, then update the containers if they need
+ # to be hidden as well.
+
+ if panel == self.BLOCKS:
+ self.btwin.set_visible(visibility)
+ elif panel == self.CONSOLE:
+ self.console_window.set_visible(visibility)
+ elif panel == self.VARIABLES:
+ self.vars.set_visible(visibility)
+ else:
+ return
+
+ if self.variable_panel_sidebar:
+ # If both the variable editor and block panels are hidden, hide the right container
+ if not self.btwin.get_visible() and not self.vars.get_visible():
+ self.right.hide()
+ else:
+ self.right.show()
+ else:
+ if not self.btwin.get_visible():
+ self.right.hide()
+ else:
+ self.right.show()
+ if not self.vars.get_visible() and not self.console_window.get_visible():
+ self.left_subpanel.hide()
+ else:
+ self.left_subpanel.show()
+
############################################################
- # Report Window
+ # Console Window
############################################################
- def add_report_line(self, line):
+ def add_console_line(self, line):
"""
Place line at the end of the text buffer, then scroll its window all the way down.
@@ -227,8 +297,12 @@ class MainWindow(gtk.Window):
Preferences.set_open_files(open_files)
Preferences.file_open(open_file)
Preferences.main_window_size(self.get_size())
- Preferences.reports_window_position(self.flow_graph_vpaned.get_position())
- Preferences.blocks_window_position(self.hpaned.get_position())
+ Preferences.console_window_position(self.left.get_position())
+ Preferences.blocks_window_position(self.container.get_position())
+ if self.variable_panel_sidebar:
+ Preferences.variable_editor_position(self.right.get_position(), sidebar=True)
+ else:
+ Preferences.variable_editor_position(self.left_subpanel.get_position())
Preferences.save()
return True
@@ -272,7 +346,7 @@ class MainWindow(gtk.Window):
"""
Set the title of the main window.
Set the titles on the page tabs.
- Show/hide the reports window.
+ Show/hide the console window.
Args:
title: the window title
@@ -298,6 +372,9 @@ class MainWindow(gtk.Window):
#show/hide notebook tabs
self.notebook.set_show_tabs(len(self.get_pages()) > 1)
+ # Need to update the variable window when changing
+ self.vars.update_gui()
+
def update_pages(self):
"""
Forces a reload of all the pages in this notebook.
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index 6614649c89..c9e8d0f186 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -39,13 +39,13 @@ class NotebookPage(gtk.HBox):
file_path: path to a flow graph file
"""
self._flow_graph = flow_graph
- self.set_proc(None)
+ self.process = None
#import the file
self.main_window = main_window
- self.set_file_path(file_path)
+ self.file_path = file_path
initial_state = flow_graph.get_parent().parse_flow_graph(file_path)
self.state_cache = StateCache(initial_state)
- self.set_saved(True)
+ self.saved = True
#import the data to the flow graph
self.get_flow_graph().import_data(initial_state)
#initialize page gui
@@ -189,8 +189,7 @@ class NotebookPage(gtk.HBox):
Args:
file_path: file path string
"""
- if file_path: self.file_path = os.path.abspath(file_path)
- else: self.file_path = ''
+ self.file_path = os.path.abspath(file_path) if file_path else ''
def get_saved(self):
"""
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index bf0a59b96b..4b5a3c294a 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -84,7 +84,7 @@ class InputParam(gtk.HBox):
self._have_pending_changes = True
self._update_gui()
if self._editing_callback:
- self._editing_callback()
+ self._editing_callback(self, None)
def _apply_change(self, *args):
"""
@@ -95,7 +95,7 @@ class InputParam(gtk.HBox):
self.param.set_value(self.get_text())
#call the callback
if self._changed_callback:
- self._changed_callback(*args)
+ self._changed_callback(self, None)
else:
self.param.validate()
#gui update
@@ -129,8 +129,19 @@ class EntryParam(InputParam):
return self._input.get_text()
def set_color(self, color):
- self._input.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
- self._input.modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR)
+ need_status_color = self.label not in self.get_children()
+ text_color = (
+ Colors.PARAM_ENTRY_TEXT_COLOR if not need_status_color else
+ gtk.gdk.color_parse('blue') if self._have_pending_changes else
+ gtk.gdk.color_parse('red') if not self.param.is_valid() else
+ Colors.PARAM_ENTRY_TEXT_COLOR)
+ base_color = (
+ Colors.BLOCK_DISABLED_COLOR
+ if need_status_color and not self.param.get_parent().get_enabled()
+ else gtk.gdk.color_parse(color)
+ )
+ self._input.modify_base(gtk.STATE_NORMAL, base_color)
+ self._input.modify_text(gtk.STATE_NORMAL, text_color)
def set_tooltip_text(self, text):
try:
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
index 1a194fd8c5..5fbdfe927a 100644
--- a/grc/gui/Preferences.py
+++ b/grc/gui/Preferences.py
@@ -140,14 +140,31 @@ def add_recent_file(file_name):
set_recent_files(recent_files[:10]) # Keep up to 10 files
-def reports_window_position(pos=None):
- return entry('reports_window_position', pos, default=-1) or 1
+def console_window_position(pos=None):
+ return entry('console_window_position', pos, default=-1) or 1
def blocks_window_position(pos=None):
return entry('blocks_window_position', pos, default=-1) or 1
+def variable_editor_position(pos=None, sidebar=False):
+ # Figure out default
+ if sidebar:
+ w, h = main_window_size()
+ return entry('variable_editor_sidebar_position', pos, default=int(h*0.7))
+ else:
+ return entry('variable_editor_position', pos, default=int(blocks_window_position()*0.5))
+
+
+def variable_editor_sidebar(pos=None):
+ return entry('variable_editor_sidebar', pos, default=False)
+
+
+def variable_editor_confirm_delete(pos=None):
+ return entry('variable_editor_confirm_delete', pos, default=True)
+
+
def xterm_missing(cmd=None):
return entry('xterm_missing', cmd, default='INVALID_XTERM_SETTING')
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index f20e3c0fa6..51b9b19e9f 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -123,10 +123,11 @@ class TemplateParser(object):
parse_template = TemplateParser()
-def align_to_grid(coor):
- _align = lambda: int(round(x / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
+def align_to_grid(coor, mode=round):
+ def align(value):
+ return int(mode(value / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
try:
- return [_align() for x in coor]
+ return map(align, coor)
except TypeError:
x = coor
- return _align()
+ return align(coor)
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
new file mode 100644
index 0000000000..7721f3bda6
--- /dev/null
+++ b/grc/gui/VariableEditor.py
@@ -0,0 +1,354 @@
+"""
+Copyright 2015 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 operator import attrgetter
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+
+from . import Actions
+from . import Preferences
+from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH
+
+BLOCK_INDEX = 0
+ID_INDEX = 1
+
+
+class VariableEditorContextMenu(gtk.Menu):
+ """ A simple context menu for our variable editor """
+ def __init__(self, var_edit):
+ gtk.Menu.__init__(self)
+
+ self.imports = gtk.MenuItem("Add _Import")
+ self.imports.connect('activate', var_edit.handle_action, var_edit.ADD_IMPORT)
+ self.add(self.imports)
+
+ self.variables = gtk.MenuItem("Add _Variable")
+ self.variables.connect('activate', var_edit.handle_action, var_edit.ADD_VARIABLE)
+ self.add(self.variables)
+ self.add(gtk.SeparatorMenuItem())
+
+ self.enable = gtk.MenuItem("_Enable")
+ self.enable.connect('activate', var_edit.handle_action, var_edit.ENABLE_BLOCK)
+ self.disable = gtk.MenuItem("_Disable")
+ self.disable.connect('activate', var_edit.handle_action, var_edit.DISABLE_BLOCK)
+ self.add(self.enable)
+ self.add(self.disable)
+ self.add(gtk.SeparatorMenuItem())
+
+ self.delete = gtk.MenuItem("_Delete")
+ self.delete.connect('activate', var_edit.handle_action, var_edit.DELETE_BLOCK)
+ self.add(self.delete)
+ self.add(gtk.SeparatorMenuItem())
+
+ self.properties = gtk.MenuItem("_Properties...")
+ self.properties.connect('activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES)
+ self.add(self.properties)
+ self.show_all()
+
+ def update_sensitive(self, selected, enabled=False):
+ self.delete.set_sensitive(selected)
+ self.properties.set_sensitive(selected)
+ self.enable.set_sensitive(selected and not enabled)
+ self.disable.set_sensitive(selected and enabled)
+
+
+class VariableEditor(gtk.VBox):
+
+ # Actions that are handled by the editor
+ ADD_IMPORT = 0
+ ADD_VARIABLE = 1
+ OPEN_PROPERTIES = 2
+ DELETE_BLOCK = 3
+ DELETE_CONFIRM = 4
+ ENABLE_BLOCK = 5
+ DISABLE_BLOCK = 6
+
+ def __init__(self, platform, get_flow_graph):
+ gtk.VBox.__init__(self)
+ self.platform = platform
+ self.get_flow_graph = get_flow_graph
+ self._block = None
+ self._mouse_button_pressed = False
+
+ # Only use the model to store the block reference and name.
+ # Generate everything else dynamically
+ self.treestore = gtk.TreeStore(gobject.TYPE_PYOBJECT, # Block reference
+ gobject.TYPE_STRING) # Category and block name
+ self.treeview = gtk.TreeView(self.treestore)
+ self.treeview.set_enable_search(False)
+ self.treeview.set_search_column(-1)
+ #self.treeview.set_enable_search(True)
+ #self.treeview.set_search_column(ID_INDEX)
+ self.treeview.get_selection().set_mode('single')
+ self.treeview.set_headers_visible(True)
+ self.treeview.connect('button-press-event', self._handle_mouse_button_press)
+ self.treeview.connect('button-release-event', self._handle_mouse_button_release)
+ self.treeview.connect('motion-notify-event', self._handle_motion_notify)
+ self.treeview.connect('key-press-event', self._handle_key_button_press)
+
+ # Block Name or Category
+ 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.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_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ id_column.set_cell_data_func(self.id_cell, self.set_properties)
+ self.id_column = id_column
+ self.treeview.append_column(id_column)
+ self.treestore.set_sort_column_id(ID_INDEX, gtk.SORT_ASCENDING)
+ # For forcing resize
+ self._col_width = 0
+
+ # Block Value
+ self.value_cell = gtk.CellRendererText()
+ self.value_cell.connect('edited', self._handle_value_edited_cb)
+ value_column = gtk.TreeViewColumn("Value", self.value_cell)
+ value_column.set_name("value")
+ value_column.set_resizable(False)
+ value_column.set_expand(True)
+ value_column.set_min_width(100)
+ value_column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
+ value_column.set_cell_data_func(self.value_cell, self.set_value)
+ self.value_column = value_column
+ self.treeview.append_column(value_column)
+
+ # Block Actions (Add, Remove)
+ self.action_cell = gtk.CellRendererPixbuf()
+ value_column.pack_start(self.action_cell, False)
+ value_column.set_cell_data_func(self.action_cell, self.set_icon)
+
+ # Make the scrolled window to hold the tree view
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled_window.add_with_viewport(self.treeview)
+ scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
+ self.pack_start(scrolled_window)
+
+ # Context menus
+ self._context_menu = VariableEditorContextMenu(self)
+ self._confirm_delete = Preferences.variable_editor_confirm_delete()
+
+ # Sets cell contents
+ def set_icon(self, col, cell, model, iter):
+ block = model.get_value(iter, BLOCK_INDEX)
+ if block:
+ pb = self.treeview.render_icon(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU, None)
+ else:
+ pb = self.treeview.render_icon(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU, None)
+ cell.set_property('pixbuf', pb)
+
+ def set_value(self, col, cell, model, iter):
+ sp = cell.set_property
+ block = model.get_value(iter, BLOCK_INDEX)
+
+ # Set the default properties for this column first.
+ # Some set in set_properties() may be overridden (editable for advanced variable blocks)
+ self.set_properties(col, cell, model, iter)
+
+ # Set defaults
+ value = None
+ self.set_tooltip_text(None)
+
+ # Block specific values
+ if block:
+ if block.get_key() == 'import':
+ value = block.get_param('import').get_value()
+ elif block.get_key() != "variable":
+ value = "<Open Properties>"
+ sp('editable', False)
+ sp('foreground', '#0D47A1')
+ else:
+ value = block.get_param('value').get_value()
+
+ # Check if there are errors in the blocks.
+ # Show the block error as a tooltip
+ error_message = block.get_error_messages()
+ if len(error_message) > 0:
+ # Set the error message to the last error in the list.
+ # This should be the first message generated
+ self.set_tooltip_text(error_message[-1])
+ else:
+ # Evaluate and show the value (if it is a variable)
+ if block.get_key() == "variable":
+ evaluated = str(block.get_param('value').evaluate())
+ self.set_tooltip_text(evaluated)
+ # Always set the text value.
+ sp('text', value)
+
+ def set_properties(self, col, cell, model, iter):
+ sp = cell.set_property
+ block = model.get_value(iter, BLOCK_INDEX)
+ # Set defaults
+ sp('sensitive', True)
+ sp('editable', False)
+ sp('foreground', None)
+
+ # Block specific changes
+ if block:
+ if not block.get_enabled():
+ # Disabled block. But, this should still be editable
+ sp('editable', True)
+ sp('foreground', 'gray')
+ else:
+ sp('editable', True)
+ if block.get_error_messages():
+ sp('foreground', 'red')
+
+ def update_gui(self):
+ if not self.get_flow_graph():
+ return
+ self._update_blocks()
+ self._rebuild()
+ self.treeview.expand_all()
+
+ def _update_blocks(self):
+ self._imports = filter(attrgetter('is_import'),
+ self.get_flow_graph().blocks)
+ self._variables = filter(attrgetter('is_variable'),
+ self.get_flow_graph().blocks)
+
+ def _rebuild(self, *args):
+ self.treestore.clear()
+ imports = self.treestore.append(None, [None, 'Imports'])
+ variables = self.treestore.append(None, [None, 'Variables'])
+ for block in self._imports:
+ self.treestore.append(imports, [block, block.get_param('id').get_value()])
+ for block in sorted(self._variables, key=lambda v: v.get_id()):
+ self.treestore.append(variables, [block, block.get_param('id').get_value()])
+
+ def _handle_name_edited_cb(self, cell, path, new_text):
+ block = self.treestore[path][BLOCK_INDEX]
+ block.get_param('id').set_value(new_text)
+ Actions.VARIABLE_EDITOR_UPDATE()
+
+ def _handle_value_edited_cb(self, cell, path, new_text):
+ block = self.treestore[path][BLOCK_INDEX]
+ if block.is_import:
+ block.get_param('import').set_value(new_text)
+ else:
+ block.get_param('value').set_value(new_text)
+ Actions.VARIABLE_EDITOR_UPDATE()
+
+ def handle_action(self, item, key, event=None):
+ """
+ Single handler for the different actions that can be triggered by the context menu,
+ key presses or mouse clicks. Also triggers an update of the flow graph and editor.
+ """
+ if key == self.ADD_IMPORT:
+ self.get_flow_graph().add_new_block('import')
+ elif key == self.ADD_VARIABLE:
+ self.get_flow_graph().add_new_block('variable')
+ elif key == self.OPEN_PROPERTIES:
+ Actions.BLOCK_PARAM_MODIFY(self._block)
+ elif key == self.DELETE_BLOCK:
+ self.get_flow_graph().remove_element(self._block)
+ elif key == self.DELETE_CONFIRM:
+ if self._confirm_delete:
+ # Create a context menu to confirm the delete operation
+ confirmation_menu = gtk.Menu()
+ block_id = self._block.get_param('id').get_value().replace("_", "__")
+ confirm = gtk.MenuItem("Delete {}".format(block_id))
+ confirm.connect('activate', self.handle_action, self.DELETE_BLOCK)
+ confirmation_menu.add(confirm)
+ confirmation_menu.show_all()
+ confirmation_menu.popup(None, None, None, event.button, event.time)
+ else:
+ self.handle_action(None, self.DELETE_BLOCK, None)
+ elif key == self.ENABLE_BLOCK:
+ self._block.set_enabled(True)
+ elif key == self.DISABLE_BLOCK:
+ self._block.set_enabled(False)
+ Actions.VARIABLE_EDITOR_UPDATE()
+
+ def _handle_mouse_button_press(self, widget, event):
+ """
+ Handles mouse button for several different events:
+ - Double Click to open properties for advanced blocks
+ - Click to add/remove blocks
+ """
+ # Save the column width to see if it changes on button_release
+ self._mouse_button_pressed = True
+ self._col_width = self.id_column.get_width()
+
+ path = widget.get_path_at_pos(int(event.x), int(event.y))
+ if path:
+ # If there is a valid path, then get the row, column and block selected.
+ row = self.treestore[path[0]]
+ col = path[1]
+ self._block = row[BLOCK_INDEX]
+
+ if event.button == 1 and col.get_name() == "value":
+ # Make sure this has a block (not the import/variable rows)
+ if self._block and event.type == gtk.gdk._2BUTTON_PRESS:
+ # Open the advanced dialog if it is a gui variable
+ if self._block.get_key() not in ("variable", "import"):
+ self.handle_action(None, self.OPEN_PROPERTIES, event=event)
+ return True
+ if event.type == gtk.gdk.BUTTON_PRESS:
+ # User is adding/removing blocks
+ # Make sure this is the action cell (Add/Remove Icons)
+ if path[2] > col.cell_get_position(self.action_cell)[0]:
+ if row[1] == "Imports":
+ # Add a new import block.
+ self.handle_action(None, self.ADD_IMPORT, event=event)
+ elif row[1] == "Variables":
+ # Add a new variable block
+ self.handle_action(None, self.ADD_VARIABLE, event=event)
+ else:
+ self.handle_action(None, self.DELETE_CONFIRM, event=event)
+ return True
+ elif event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS:
+ if self._block:
+ self._context_menu.update_sensitive(True, enabled=self._block.get_enabled())
+ else:
+ self._context_menu.update_sensitive(False)
+ self._context_menu.popup(None, None, None, event.button, event.time)
+
+ # Null handler. Stops the treeview from handling double click events.
+ if event.type == gtk.gdk._2BUTTON_PRESS:
+ return True
+ return False
+
+ def _handle_mouse_button_release(self, widget, event):
+ self._mouse_button_pressed = False
+ return False
+
+ def _handle_motion_notify(self, widget, event):
+ # Check to see if the column size has changed
+ if self._mouse_button_pressed and self.id_column.get_width() != self._col_width:
+ self.value_column.queue_resize()
+ return False
+
+ def _handle_key_button_press(self, widget, event):
+ model, path = self.treeview.get_selection().get_selected_rows()
+ if path and self._block:
+ if self._block.get_enabled() and event.string == "d":
+ self.handle_action(None, self.DISABLE_BLOCK, None)
+ return True
+ elif not self._block.get_enabled() and event.string == "e":
+ self.handle_action(None, self.ENABLE_BLOCK, None)
+ return True
+ return False