commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] r10660 - in gnuradio/trunk: . gnuradio-core/src/lib/io


From: jcorgan
Subject: [Commit-gnuradio] r10660 - in gnuradio/trunk: . gnuradio-core/src/lib/io gr-wxgui/src/python gr-wxgui/src/python/plotter grc/data/platforms/python grc/data/platforms/python/blocks grc/examples/simple grc/examples/trellis grc/src/grc_gnuradio/wxgui
Date: Thu, 19 Mar 2009 20:16:20 -0600 (MDT)

Author: jcorgan
Date: 2009-03-19 20:16:20 -0600 (Thu, 19 Mar 2009)
New Revision: 10660

Added:
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink.i
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.cc
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.h
   gnuradio/trunk/gr-wxgui/src/python/histo_window.py
   gnuradio/trunk/gr-wxgui/src/python/histosink_gl.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/bar_plotter.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/common.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/grid_plotter_base.py
   gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_histosink2.xml
Modified:
   gnuradio/trunk/
   gnuradio/trunk/gnuradio-core/src/lib/io/Makefile.am
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.cc
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.h
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink.i
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.cc
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.h
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.cc
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.h
   gnuradio/trunk/gnuradio-core/src/lib/io/gr_trigger_mode.h
   gnuradio/trunk/gnuradio-core/src/lib/io/io.i
   gnuradio/trunk/gr-wxgui/src/python/Makefile.am
   gnuradio/trunk/gr-wxgui/src/python/common.py
   gnuradio/trunk/gr-wxgui/src/python/const_window.py
   gnuradio/trunk/gr-wxgui/src/python/constants.py
   gnuradio/trunk/gr-wxgui/src/python/constsink_gl.py
   gnuradio/trunk/gr-wxgui/src/python/fft_window.py
   gnuradio/trunk/gr-wxgui/src/python/fftsink_gl.py
   gnuradio/trunk/gr-wxgui/src/python/fftsink_nongl.py
   gnuradio/trunk/gr-wxgui/src/python/number_window.py
   gnuradio/trunk/gr-wxgui/src/python/numbersink2.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/Makefile.am
   gnuradio/trunk/gr-wxgui/src/python/plotter/__init__.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/channel_plotter.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/plotter_base.py
   gnuradio/trunk/gr-wxgui/src/python/plotter/waterfall_plotter.py
   gnuradio/trunk/gr-wxgui/src/python/pubsub.py
   gnuradio/trunk/gr-wxgui/src/python/scope_window.py
   gnuradio/trunk/gr-wxgui/src/python/scopesink2.py
   gnuradio/trunk/gr-wxgui/src/python/scopesink_gl.py
   gnuradio/trunk/gr-wxgui/src/python/scopesink_nongl.py
   gnuradio/trunk/gr-wxgui/src/python/waterfall_window.py
   gnuradio/trunk/gr-wxgui/src/python/waterfallsink_gl.py
   gnuradio/trunk/grc/data/platforms/python/block_tree.xml
   gnuradio/trunk/grc/data/platforms/python/blocks/Makefile.am
   gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_constellationsink2.xml
   gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_fftsink2.xml
   gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_numbersink2.xml
   gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_scopesink2.xml
   gnuradio/trunk/grc/examples/simple/ber_simulation.grc
   gnuradio/trunk/grc/examples/trellis/interference_cancellation.grc
   gnuradio/trunk/grc/src/grc_gnuradio/wxgui/callback_controls.py
Log:
Merged r10463:10658 from jblum/gui_guts into trunk.  Trunk passes distcheck.


Property changes on: gnuradio/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /gnuradio/branches/developers/eb/t348:10638-10648
/gnuradio/branches/developers/michaelld/am_swig_4:10555-10595
/gnuradio/branches/developers/michaelld/two_mods:10540-10546
   + /gnuradio/branches/developers/eb/t348:10638-10648
/gnuradio/branches/developers/jblum/gui_guts:10464-10658
/gnuradio/branches/developers/michaelld/am_swig_4:10555-10595
/gnuradio/branches/developers/michaelld/two_mods:10540-10546

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/Makefile.am
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/Makefile.am 2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/Makefile.am 2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -33,6 +33,7 @@
        gr_file_source.cc               \
        gr_file_descriptor_sink.cc      \
        gr_file_descriptor_source.cc    \
+       gr_histo_sink_f.cc              \
        gr_message_sink.cc              \
        gr_message_source.cc            \
        gr_oscope_guts.cc               \
@@ -64,6 +65,7 @@
        gr_file_source.h                \
        gr_file_descriptor_sink.h       \
        gr_file_descriptor_source.h     \
+       gr_histo_sink_f.h               \
        gr_message_sink.h               \
        gr_message_source.h             \
        gr_oscope_guts.h                \
@@ -101,6 +103,7 @@
        gr_file_source.i                \
        gr_file_descriptor_sink.i       \
        gr_file_descriptor_source.i     \
+       gr_histo_sink.i                 \
        gr_message_sink.i               \
        gr_message_source.i             \
        gr_oscope_sink.i                \

Copied: gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink.i (from rev 
10658, 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink.i)
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink.i                     
        (rev 0)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink.i     2009-03-20 
02:16:20 UTC (rev 10660)
@@ -0,0 +1,39 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU Radio
+ * 
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ * 
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+GR_SWIG_BLOCK_MAGIC(gr,histo_sink_f)
+
+gr_histo_sink_f_sptr gr_make_histo_sink_f (gr_msg_queue_sptr msgq);
+
+class gr_histo_sink_f : public gr_sync_block
+{
+public:
+  ~gr_histo_sink_f (void);
+
+  unsigned int get_frame_size(void);
+  unsigned int get_num_bins(void);
+
+  void set_frame_size(unsigned int frame_size);
+  void set_num_bins(unsigned int num_bins);
+
+};

Copied: gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.cc (from rev 
10658, 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.cc)
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.cc                  
        (rev 0)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.cc  2009-03-20 
02:16:20 UTC (rev 10660)
@@ -0,0 +1,169 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gr_histo_sink_f.h>
+#include <gr_io_signature.h>
+
+static float get_clean_num(float num){
+  if (num == 0) return 0;
+  /* extract sign and exponent from num */
+  int sign = (num < 0) ? -1 : 1; num = fabs(num);
+  float exponent = floor(log10(num));
+  /* search for closest number with base 1, 2, 5, 10 */
+  float closest_num = 10*pow(10, exponent);
+  if (fabs(num - 1*pow(10, exponent)) < fabs(num - closest_num))
+    closest_num = 1*pow(10, exponent);
+  if (fabs(num - 2*pow(10, exponent)) < fabs(num - closest_num))
+    closest_num = 2*pow(10, exponent);
+  if (fabs(num - 5*pow(10, exponent)) < fabs(num - closest_num))
+    closest_num = 5*pow(10, exponent);
+  return sign*closest_num;
+}
+
+gr_histo_sink_f_sptr
+gr_make_histo_sink_f (gr_msg_queue_sptr msgq)
+{
+  return gr_histo_sink_f_sptr (new gr_histo_sink_f (msgq));
+}
+
+gr_histo_sink_f::gr_histo_sink_f (gr_msg_queue_sptr msgq)
+  : gr_sync_block ("histo_sink_f", gr_make_io_signature (1, 1, sizeof 
(float)), gr_make_io_signature (0, 0, 0)),
+  d_msgq (msgq), d_num_bins(11), d_frame_size(1000), d_sample_count(0), 
d_bins(NULL), d_samps(NULL)
+{
+  pthread_mutex_init(&d_mutex, 0);
+  //allocate arrays and clear
+  set_num_bins(d_num_bins);
+  set_frame_size(d_frame_size);
+}
+
+gr_histo_sink_f::~gr_histo_sink_f (void)
+{
+  pthread_mutex_destroy(&d_mutex);
+  delete [] d_samps;
+  delete [] d_bins;
+}
+
+int
+gr_histo_sink_f::work (int noutput_items,
+  gr_vector_const_void_star &input_items,
+  gr_vector_void_star &output_items)
+{
+  const float *in = (const float *) input_items[0];
+  pthread_mutex_lock(&d_mutex);
+  for (unsigned int i = 0; i < (unsigned int)noutput_items; i++){
+    d_samps[d_sample_count] = in[i];
+    d_sample_count++;
+    /* processed a frame? */
+    if (d_sample_count == d_frame_size){
+      send_frame();
+      clear();
+    }
+  }
+  pthread_mutex_unlock(&d_mutex);
+  return noutput_items;
+}
+
+void
+gr_histo_sink_f::send_frame(void){
+  /* output queue full, drop the data */
+  if (d_msgq->full_p()) return;
+  /* find the minimum and maximum */
+  float minimum = d_samps[0];
+  float maximum = d_samps[0];
+  for (unsigned int i = 0; i < d_frame_size; i++){
+    if (d_samps[i] < minimum) minimum = d_samps[i];
+    if (d_samps[i] > maximum) maximum = d_samps[i];
+  }
+  minimum = get_clean_num(minimum);
+  maximum = get_clean_num(maximum);
+  if (minimum == maximum || minimum > maximum) return; //useless data or screw 
up?
+  /* load the bins */
+  int index;
+  float bin_width = (maximum - minimum)/(d_num_bins-1);
+  for (unsigned int i = 0; i < d_sample_count; i++){
+    index = round((d_samps[i] - minimum)/bin_width);
+    /* ensure the index range in case a small floating point error is involed 
*/
+    if (index < 0) index = 0;
+    if (index >= (int)d_num_bins) index = d_num_bins-1;
+    d_bins[index]++;
+  }
+  /* Build a message to hold the output records */
+  gr_message_sptr msg = gr_make_message(0, minimum, maximum, 
d_num_bins*sizeof(float));
+  float *out = (float *)msg->msg(); // get pointer to raw message buffer
+  /* normalize the bins and put into message */
+  for (unsigned int i = 0; i < d_num_bins; i++){
+    out[i] = ((float)d_bins[i])/d_frame_size;
+  }
+  /* send the message */
+  d_msgq->handle(msg);
+}
+
+void
+gr_histo_sink_f::clear(void){
+  d_sample_count = 0;
+  /* zero the bins */
+  for (unsigned int i = 0; i < d_num_bins; i++){
+    d_bins[i] = 0;
+  }
+}
+
+/**************************************************
+ * Getters
+ **************************************************/
+unsigned int
+gr_histo_sink_f::get_frame_size(void){
+  return d_frame_size;
+}
+
+unsigned int
+gr_histo_sink_f::get_num_bins(void){
+  return d_num_bins;
+}
+
+/**************************************************
+ * Setters
+ **************************************************/
+void
+gr_histo_sink_f::set_frame_size(unsigned int frame_size){
+  pthread_mutex_lock(&d_mutex);
+  d_frame_size = frame_size;
+  /* allocate a new sample array */
+  delete [] d_samps;
+  d_samps = new float[d_frame_size];
+  clear();
+  pthread_mutex_unlock(&d_mutex);
+}
+
+void
+gr_histo_sink_f::set_num_bins(unsigned int num_bins){
+  pthread_mutex_lock(&d_mutex);
+  d_num_bins = num_bins;
+  /* allocate a new bin array */
+  delete [] d_bins;
+  d_bins = new unsigned int[d_num_bins];
+  clear();
+  pthread_mutex_unlock(&d_mutex);
+}

Copied: gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.h (from rev 
10658, 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.h)
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.h                   
        (rev 0)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_histo_sink_f.h   2009-03-20 
02:16:20 UTC (rev 10660)
@@ -0,0 +1,70 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_GR_HISTO_SINK_F_H
+#define INCLUDED_GR_HISTO_SINK_F_H
+
+#include <gr_sync_block.h>
+#include <gr_msg_queue.h>
+#include <pthread.h>
+
+class gr_histo_sink_f;
+typedef boost::shared_ptr<gr_histo_sink_f> gr_histo_sink_f_sptr;
+
+gr_histo_sink_f_sptr gr_make_histo_sink_f (gr_msg_queue_sptr msgq);
+
+/*!
+ * \brief Histogram module.
+ * \ingroup sink
+ */
+class gr_histo_sink_f : public gr_sync_block
+{
+private:
+  gr_msg_queue_sptr    d_msgq;
+  unsigned int d_num_bins;
+  unsigned int d_frame_size;
+  unsigned int d_sample_count;
+  unsigned int *d_bins;
+  float *d_samps;
+  pthread_mutex_t d_mutex;
+
+  friend gr_histo_sink_f_sptr gr_make_histo_sink_f (gr_msg_queue_sptr msgq);
+  gr_histo_sink_f (gr_msg_queue_sptr msgq);
+  void send_frame(void);
+  void clear(void);
+
+public:
+  ~gr_histo_sink_f (void);
+
+  int work (int noutput_items,
+    gr_vector_const_void_star &input_items,
+    gr_vector_void_star &output_items);
+
+  unsigned int get_frame_size(void);
+  unsigned int get_num_bins(void);
+
+  void set_frame_size(unsigned int frame_size);
+  void set_num_bins(unsigned int num_bins);
+
+};
+
+#endif /* INCLUDED_GR_HISTO_SINK_F_H */

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.cc
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.cc   2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.cc   2009-03-20 
02:16:20 UTC (rev 10660)
@@ -31,30 +31,31 @@
 #include <math.h>
 #include <assert.h>
 
-static const int OUTPUT_RECORD_SIZE = 2048;    // must be power of 2
+static const int OUTPUT_RECORD_SIZE = 2048;        // must be power of 2
 
 static inline int
-wrap_bi (int buffer_index)             // wrap buffer index
+wrap_bi (int buffer_index)                // wrap buffer index
 {
   return buffer_index & (OUTPUT_RECORD_SIZE - 1);
 }
 
 static inline int
-incr_bi (int buffer_index)             // increment buffer index
+incr_bi (int buffer_index)                // increment buffer index
 {
   return wrap_bi (buffer_index + 1);
 }
 
 static inline int
-decr_bi (int buffer_index)             // decrement buffer index
+decr_bi (int buffer_index)                // decrement buffer index
 {
   return wrap_bi (buffer_index - 1);
 }
 
-gr_oscope_guts::gr_oscope_guts (int nchannels, double sample_rate, 
gr_msg_queue_sptr msgq)
-  : d_nchannels (nchannels),
+gr_oscope_guts::gr_oscope_guts (double sample_rate, gr_msg_queue_sptr msgq)
+  : d_nchannels (1),
     d_msgq (msgq), 
-    d_trigger_mode (gr_TRIG_AUTO),
+    d_trigger_mode (gr_TRIG_MODE_AUTO),
+    d_trigger_slope (gr_TRIG_SLOPE_POS),
     d_trigger_channel (0),
     d_sample_rate (sample_rate),
     d_update_rate (20),
@@ -65,19 +66,14 @@
     d_decimator_count_init (1),
     d_hold_off_count (0),
     d_hold_off_count_init (OUTPUT_RECORD_SIZE/2-1),
+    d_pre_trigger_count (0),
     d_post_trigger_count (0),
-    d_post_trigger_count_init (OUTPUT_RECORD_SIZE/2),
-    d_prev_sample (0)
+    d_post_trigger_count_init (OUTPUT_RECORD_SIZE/2)
 {
-  if (d_nchannels > MAX_CHANNELS){
-    fprintf (stderr, "gr_oscope_guts: too many channels.  MAX_CHANNELS = 
%d\n", MAX_CHANNELS);
-    throw std::runtime_error ("too many channels");
-  }
-
   for (int i = 0; i < MAX_CHANNELS; i++)
     d_buffer[i] = 0;
 
-  for (int i = 0; i < d_nchannels; i++){
+  for (int i = 0; i < MAX_CHANNELS; i++){
     d_buffer[i] = new float [OUTPUT_RECORD_SIZE];
     for (int j = 0; j < OUTPUT_RECORD_SIZE; j++)
       d_buffer[i][j] = 0.0;
@@ -109,10 +105,8 @@
   d_decimator_count = d_decimator_count_init;
   
   for (int i = 0; i < d_nchannels; i++)
-    d_buffer[i][d_obi] = channel_data[i];              // copy data into buffer
+    d_buffer[i][d_obi] = channel_data[i];                // copy data into 
buffer
 
-  int trigger = 0;
-  
   switch (d_state){
   case HOLD_OFF:
     d_hold_off_count--;
@@ -121,12 +115,8 @@
     break;
 
   case LOOK_FOR_TRIGGER:
-    trigger = found_trigger (d_buffer[d_trigger_channel][d_obi]);
-    if (trigger != 0){
+    if (found_trigger ())
       enter_post_trigger ();
-      if (trigger < 0)                 // previous sample was closer
-       d_post_trigger_count--;
-    }
     break;
 
   case POST_TRIGGER:
@@ -158,8 +148,8 @@
 void
 gr_oscope_guts::enter_look_for_trigger ()
 {
+  d_pre_trigger_count = 0;
   d_state = LOOK_FOR_TRIGGER;
-  d_prev_sample = d_buffer[d_trigger_channel][d_obi];
 }
 
 void
@@ -167,48 +157,49 @@
 {
   d_state = POST_TRIGGER;
   d_post_trigger_count = d_post_trigger_count_init;
+  //ensure that the trigger offset is no more than than half a sample
+  if (d_trigger_off > .5) d_trigger_off -= 1;
+  else d_post_trigger_count--;
 }
 
 // ----------------------------------------------------------------
-// returns 0 if no trigger found. 
-// returns +1 if this sample is the trigger point
-// returns -1 if the previous sample is the trigger point
+// returns true if trigger found
 
-int
-gr_oscope_guts::found_trigger (float new_sample)
+bool
+gr_oscope_guts::found_trigger ()
 {
-  float prev_sample = d_prev_sample;
-  d_prev_sample = new_sample;
-  bool trig;
+  float prev_sample = d_buffer[d_trigger_channel][decr_bi(d_obi)];
+  float new_sample = d_buffer[d_trigger_channel][d_obi];
 
   switch (d_trigger_mode){
 
-  case gr_TRIG_AUTO:           // always trigger
-    return +1;
-    
-  case gr_TRIG_POS_SLOPE:
-    trig = prev_sample < d_trigger_level && new_sample >= d_trigger_level;
-    if (trig){
-      if (fabs (prev_sample - d_trigger_level) < fabs (new_sample - 
d_trigger_level))
-       return -1;
-      else
-       return +1;
-    }
-    return 0;
+  case gr_TRIG_MODE_AUTO: //too many samples without a trigger
+    d_pre_trigger_count++;
+    if (d_pre_trigger_count > OUTPUT_RECORD_SIZE/2) return true;
 
-  case gr_TRIG_NEG_SLOPE:
-    trig = prev_sample > d_trigger_level && new_sample <= d_trigger_level;
-    if (trig){
-      if (fabs (prev_sample - d_trigger_level) < fabs (new_sample - 
d_trigger_level))
-       return -1;
-      else
-       return +1;
+  case gr_TRIG_MODE_NORM: //look for trigger
+    switch (d_trigger_slope){
+
+    case gr_TRIG_SLOPE_POS: //trigger point in pos slope?
+      if (new_sample < d_trigger_level || prev_sample >= d_trigger_level) 
return false;
+      break;
+
+    case gr_TRIG_SLOPE_NEG: //trigger point in neg slope?
+      if (new_sample > d_trigger_level || prev_sample <= d_trigger_level) 
return false;
+      break;
     }
-    return 0;
 
+    //calculate the trigger offset in % sample
+    d_trigger_off = (d_trigger_level - prev_sample)/(new_sample - prev_sample);
+    return true;
+
+  case gr_TRIG_MODE_FREE: //free run mode, always trigger
+    d_trigger_off = 0;
+    return true;
+
   default:
     assert (0);
-    return 0;
+    return false;
   }
 }
 
@@ -218,28 +209,30 @@
 void
 gr_oscope_guts::write_output_records ()
 {
-  // if the output queue if full, drop the data on the ground.
+  // if the output queue if full, drop the data like its hot.
   if (d_msgq->full_p())
     return;
-
-  // Build a message to hold the output records
+    // Build a message to hold the output records
   gr_message_sptr msg = 
-    gr_make_message(0,                                                // msg 
type
-                   d_nchannels,                                       // arg1 
for other side
-                   OUTPUT_RECORD_SIZE,                                // arg2 
for other side
-                   d_nchannels * OUTPUT_RECORD_SIZE * sizeof(float)); // 
sizeof payload
+    gr_make_message(0,                                         // msg type
+            d_nchannels,                                       // arg1 for 
other side
+            OUTPUT_RECORD_SIZE,                                // arg2 for 
other side
+            ((d_nchannels * OUTPUT_RECORD_SIZE) + 1) * sizeof(float)); // 
sizeof payload
 
-  float *out = (float *)msg->msg();    // get pointer to raw message buffer
+  float *out = (float *)msg->msg();        // get pointer to raw message buffer
 
   for (int ch = 0; ch < d_nchannels; ch++){
     // note that d_obi + 1 points at the oldest sample in the buffer
-    for (int i = 0; i < OUTPUT_RECORD_SIZE; i++)
+    for (int i = 0; i < OUTPUT_RECORD_SIZE; i++){
       out[i] = d_buffer[ch][wrap_bi(d_obi + 1 + i)];
-
+    }
     out += OUTPUT_RECORD_SIZE;
   }
-
-  d_msgq->handle(msg);         // send the msg
+  //Set the last sample as the trigger offset:
+  //  The non gl scope sink will not look at this last sample.
+  //  The gl scope sink will use this last sample as an offset.
+  out[0] = d_trigger_off;
+  d_msgq->handle(msg);                // send the msg
 }
 
 // ----------------------------------------------------------------
@@ -291,18 +284,20 @@
 bool
 gr_oscope_guts::set_trigger_mode (gr_trigger_mode mode)
 {
-  switch (mode){
-  case gr_TRIG_POS_SLOPE:
-  case gr_TRIG_NEG_SLOPE:
-  case gr_TRIG_AUTO:
-    d_trigger_mode = mode;
-    trigger_changed ();
-    return true;
-  }
-  return false;
+  d_trigger_mode = mode;
+  trigger_changed ();
+  return true;
 }
 
 bool
+gr_oscope_guts::set_trigger_slope (gr_trigger_slope slope)
+{
+  d_trigger_slope = slope;
+  trigger_changed ();
+  return true;
+}
+
+bool
 gr_oscope_guts::set_trigger_level (double trigger_level)
 {
   d_trigger_level = trigger_level;
@@ -315,23 +310,30 @@
 {
   // find the level 1/2 way between the min and the max
 
-  float        min_v = d_buffer[d_trigger_channel][0];
-  float        max_v = d_buffer[d_trigger_channel][0];
+  float min_v = d_buffer[d_trigger_channel][0];
+  float max_v = d_buffer[d_trigger_channel][0];
 
   for (int i = 1; i < OUTPUT_RECORD_SIZE; i++){
     min_v = std::min (min_v, d_buffer[d_trigger_channel][i]);
     max_v = std::max (max_v, d_buffer[d_trigger_channel][i]);
   }
+  return set_trigger_level((min_v + max_v) * 0.5);
+}
 
-  d_trigger_level = (min_v + max_v) * 0.5;
-  trigger_changed ();
-  return true;
+bool
+gr_oscope_guts::set_num_channels(int nchannels)
+{
+  if (nchannels > 0 && nchannels <= MAX_CHANNELS){
+    d_nchannels = nchannels;
+    return true;
+  }
+  return false;
 }
 
+
 void
 gr_oscope_guts::trigger_changed ()
 {
-  // d_prev_sample = d_buffer[d_trigger_channel][decr_bi(d_obi)];
   enter_look_for_trigger ();
 }
 
@@ -373,6 +375,12 @@
   return d_trigger_mode;
 }
 
+gr_trigger_slope
+gr_oscope_guts::get_trigger_slope () const
+{
+  return d_trigger_slope;
+}
+
 double
 gr_oscope_guts::get_trigger_level () const
 {

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.h
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.h    2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_guts.h    2009-03-20 
02:16:20 UTC (rev 10660)
@@ -41,13 +41,15 @@
  */
 
 class gr_oscope_guts {
+public:
+  static const int     MAX_CHANNELS = 8;
 private:
-  static const int     MAX_CHANNELS = 16;
   enum scope_state     { HOLD_OFF, LOOK_FOR_TRIGGER, POST_TRIGGER };
 
   int                  d_nchannels;            // how many channels
   gr_msg_queue_sptr    d_msgq;                 // message queue we stuff 
output records into
-  gr_trigger_mode      d_trigger_mode;         
+  gr_trigger_mode      d_trigger_mode;
+  gr_trigger_slope     d_trigger_slope;
   int                  d_trigger_channel;      // which channel to watch for 
trigger condition
   double               d_sample_rate;          // input sample rate in Hz
   double               d_update_rate;          // approx freq to produce an 
output record (Hz)
@@ -61,9 +63,10 @@
   int                  d_decimator_count_init;
   int                  d_hold_off_count;
   int                  d_hold_off_count_init;
+  int                  d_pre_trigger_count;
   int                  d_post_trigger_count;
   int                  d_post_trigger_count_init;
-  float                        d_prev_sample;                  // used for 
trigger checking
+  float                        d_trigger_off;                  //%sample 
trigger is off
 
   // NOT IMPLEMENTED
   gr_oscope_guts (const gr_oscope_guts &rhs);                  // no copy 
constructor
@@ -71,7 +74,7 @@
 
   void trigger_changed ();
   void update_rate_or_decimation_changed ();
-  int  found_trigger (float sample);   // returns -1, 0, +1
+  bool found_trigger ();       // returns true if found
   void write_output_records ();
 
   void enter_hold_off ();                      // called on state entry
@@ -80,7 +83,7 @@
 
 public:
   // CREATORS
-  gr_oscope_guts (int nchannels, double sample_rate, gr_msg_queue_sptr msgq);
+  gr_oscope_guts (double sample_rate, gr_msg_queue_sptr msgq);
   ~gr_oscope_guts ();
 
   // MANIPULATORS
@@ -95,9 +98,11 @@
   bool set_decimation_count (int decimation_count);
   bool set_trigger_channel (int channel);
   bool set_trigger_mode (gr_trigger_mode mode);
+  bool set_trigger_slope (gr_trigger_slope slope);
   bool set_trigger_level (double trigger_level);
   bool set_trigger_level_auto ();                              // set to 50% 
level
   bool set_sample_rate(double sample_rate);
+  bool set_num_channels(int nchannels);
 
 
   // ACCESSORS
@@ -107,6 +112,7 @@
   int get_decimation_count () const;
   int get_trigger_channel () const;
   gr_trigger_mode get_trigger_mode () const;
+  gr_trigger_slope get_trigger_slope () const;
   double get_trigger_level () const;
 
   // # of samples written to each output record.

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink.i
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink.i    2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink.i    2009-03-20 
02:16:20 UTC (rev 10660)
@@ -20,11 +20,7 @@
  * Boston, MA 02110-1301, USA.
  */
 
-enum gr_trigger_mode {
-  gr_TRIG_AUTO,                        // auto trigger (on anything)
-  gr_TRIG_POS_SLOPE,           // trigger on positive slope across trigger 
level
-  gr_TRIG_NEG_SLOPE            // trigger on negative slope across trigger 
level
-};
+%include gr_trigger_mode.h
 
 // GR_SWIG_BLOCK_MAGIC(gr,oscope_sink_x)
 
@@ -43,6 +39,7 @@
   bool set_decimation_count (int decimation_count);
   bool set_trigger_channel (int channel);
   bool set_trigger_mode (gr_trigger_mode mode);
+  bool set_trigger_slope (gr_trigger_slope slope);
   bool set_trigger_level (double trigger_level);
   bool set_trigger_level_auto ();                              // set to 50% 
level
   bool set_sample_rate(double sample_rate);
@@ -54,6 +51,7 @@
   int get_decimation_count () const;
   int get_trigger_channel () const;
   gr_trigger_mode get_trigger_mode () const;
+  gr_trigger_slope get_trigger_slope () const;
   double get_trigger_level () const;
 
   // # of samples written to each output record.

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.cc
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.cc 2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.cc 2009-03-20 
02:16:20 UTC (rev 10660)
@@ -38,20 +38,18 @@
 
 gr_oscope_sink_f::gr_oscope_sink_f (double sampling_rate, gr_msg_queue_sptr 
msgq)
   : gr_oscope_sink_x ("oscope_sink_f",
-                     gr_make_io_signature (1, MAX_CHANNELS, sizeof (float)),
+                     gr_make_io_signature (1, gr_oscope_guts::MAX_CHANNELS, 
sizeof (float)),
                      sampling_rate),
     d_msgq(msgq)
 {
+  d_guts = new gr_oscope_guts (d_sampling_rate, d_msgq);
 }
 
 
 bool
 gr_oscope_sink_f::check_topology (int ninputs, int noutputs)
 {
-  delete d_guts;
-  d_guts = 0;
-  d_guts = new gr_oscope_guts (ninputs, d_sampling_rate, d_msgq);
-  return true;
+  return d_guts->set_num_channels(ninputs);
 }
 
 
@@ -65,7 +63,7 @@
                        gr_vector_void_star &output_items)
 {
   int    ni = input_items.size ();
-  float          tmp[MAX_CHANNELS];
+  float          tmp[gr_oscope_guts::MAX_CHANNELS];
 
   for (int i = 0; i < noutput_items; i++){
 

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.h
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.h  2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_f.h  2009-03-20 
02:16:20 UTC (rev 10660)
@@ -36,13 +36,10 @@
  * \brief Building block for python oscilloscope module.
  * \ingroup sink
  *
- * Accepts 1 to 16 float streams.
+ * Accepts multiple float streams.
  */
 class gr_oscope_sink_f : public gr_oscope_sink_x
 {
-public:
-  static const int MAX_CHANNELS = 16;
-
 private:
   friend gr_oscope_sink_f_sptr
   gr_make_oscope_sink_f (double sampling_rate, gr_msg_queue_sptr msgq);

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.cc
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.cc 2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.cc 2009-03-20 
02:16:20 UTC (rev 10660)
@@ -69,6 +69,12 @@
 }
 
 bool
+gr_oscope_sink_x::set_trigger_slope (gr_trigger_slope slope)
+{
+  return d_guts->set_trigger_slope (slope);
+}
+
+bool
 gr_oscope_sink_x::set_trigger_level (double trigger_level)
 {
   return d_guts->set_trigger_level (trigger_level);
@@ -87,6 +93,12 @@
   return d_guts->set_sample_rate (sample_rate);
 }
 
+bool
+gr_oscope_sink_x::set_num_channels (int nchannels)
+{
+  return d_guts->set_num_channels (nchannels);
+}
+
 // ACCESSORS
 
 int
@@ -125,6 +137,12 @@
   return d_guts->get_trigger_mode ();
 }
 
+gr_trigger_slope
+gr_oscope_sink_x::get_trigger_slope () const
+{
+  return d_guts->get_trigger_slope ();
+}
+
 double
 gr_oscope_sink_x::get_trigger_level () const
 {

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.h
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.h  2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_oscope_sink_x.h  2009-03-20 
02:16:20 UTC (rev 10660)
@@ -51,9 +51,11 @@
   bool set_decimation_count (int decimation_count);
   bool set_trigger_channel (int channel);
   bool set_trigger_mode (gr_trigger_mode mode);
+  bool set_trigger_slope (gr_trigger_slope slope);
   bool set_trigger_level (double trigger_level);
   bool set_trigger_level_auto ();                              // set to 50% 
level
   bool set_sample_rate(double sample_rate);
+  bool set_num_channels (int nchannels);
 
 
   // ACCESSORS
@@ -63,6 +65,7 @@
   int get_decimation_count () const;
   int get_trigger_channel () const;
   gr_trigger_mode get_trigger_mode () const;
+  gr_trigger_slope get_trigger_slope () const;
   double get_trigger_level () const;
 
   // # of samples written to each output record.

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/gr_trigger_mode.h
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/gr_trigger_mode.h   2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/gr_trigger_mode.h   2009-03-20 
02:16:20 UTC (rev 10660)
@@ -24,9 +24,14 @@
 #define INCLUDED_GR_TRIGGER_MODE_H
 
 enum gr_trigger_mode {
-  gr_TRIG_AUTO,                        // auto trigger (on anything)
-  gr_TRIG_POS_SLOPE,           // trigger on positive slope across trigger 
level
-  gr_TRIG_NEG_SLOPE            // trigger on negative slope across trigger 
level
+  gr_TRIG_MODE_FREE,
+  gr_TRIG_MODE_AUTO,
+  gr_TRIG_MODE_NORM,
 };
 
+enum gr_trigger_slope {
+  gr_TRIG_SLOPE_POS,
+  gr_TRIG_SLOPE_NEG,
+};
+
 #endif /* INCLUDED_GR_TRIGGER_MODE_H */

Modified: gnuradio/trunk/gnuradio-core/src/lib/io/io.i
===================================================================
--- gnuradio/trunk/gnuradio-core/src/lib/io/io.i        2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gnuradio-core/src/lib/io/io.i        2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -30,6 +30,7 @@
 #include <gr_file_source.h>
 #include <gr_file_descriptor_sink.h>
 #include <gr_file_descriptor_source.h>
+#include <gr_histo_sink_f.h>
 #include <microtune_4702_eval_board.h>
 #include <microtune_4937_eval_board.h>
 #include <sdr_1000.h>
@@ -50,6 +51,7 @@
 %include "gr_file_source.i"
 %include "gr_file_descriptor_sink.i"
 %include "gr_file_descriptor_source.i"
+%include "gr_histo_sink.i"
 %include "microtune_xxxx_eval_board.i"
 %include "microtune_4702_eval_board.i"
 %include "microtune_4937_eval_board.i"

Modified: gnuradio/trunk/gr-wxgui/src/python/Makefile.am
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/Makefile.am      2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/Makefile.am      2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -42,6 +42,8 @@
        fftsink_gl.py                   \
        fft_window.py                   \
        gui.py                          \
+       histosink_gl.py                 \
+       histo_window.py                 \
        numbersink2.py                  \
        number_window.py                \
        plot.py                         \

Modified: gnuradio/trunk/gr-wxgui/src/python/common.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/common.py        2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/common.py        2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -19,60 +19,86 @@
 # Boston, MA 02110-1301, USA.
 #
 
-import threading
-import numpy
-import math
-import wx
+#A macro to apply an index to a key
+index_key = lambda key, i: "%s_%d"%(key, i+1)
 
-class prop_setter(object):
-       def _register_set_prop(self, controller, control_key, *args):
-               def set_method(value): controller[control_key] = value
-               if args: set_method(args[0])
-               setattr(self, 'set_%s'%control_key, set_method)
+def _register_access_method(destination, controller, key):
+       """
+       Helper function for register access methods.
+       This helper creates distinct set and get methods for each key
+       and adds them to the destination object.
+       """
+       def set(value): controller[key] = value
+       setattr(destination, 'set_'+key, set)
+       def get(): return controller[key]
+       setattr(destination, 'get_'+key, get) 
 
-##################################################
-# Custom Data Event
-##################################################
-EVT_DATA = wx.PyEventBinder(wx.NewEventType())
-class DataEvent(wx.PyEvent):
-       def __init__(self, data):
-               wx.PyEvent.__init__(self, wx.NewId(), EVT_DATA.typeId)
-               self.data = data
+def register_access_methods(destination, controller):
+       """
+       Register setter and getter functions in the destination object for all 
keys in the controller.
+       @param destination the object to get new setter and getter methods
+       @param controller the pubsub controller
+       """
+       for key in controller.keys(): _register_access_method(destination, 
controller, key)
 
 ##################################################
 # Input Watcher Thread
 ##################################################
+import threading
+
 class input_watcher(threading.Thread):
        """
        Input watcher thread runs forever.
        Read messages from the message queue.
        Forward messages to the message handler.
        """
-       def __init__ (self, msgq, handle_msg):
+       def __init__ (self, msgq, controller, msg_key, arg1_key='', 
arg2_key=''):
                threading.Thread.__init__(self)
                self.setDaemon(1)
                self.msgq = msgq
-               self._handle_msg = handle_msg
+               self._controller = controller
+               self._msg_key = msg_key
+               self._arg1_key = arg1_key
+               self._arg2_key = arg2_key
                self.keep_running = True
                self.start()
 
        def run(self):
-               while self.keep_running: 
self._handle_msg(self.msgq.delete_head().to_string())
+               while self.keep_running:
+                       msg = self.msgq.delete_head()
+                       if self._arg1_key: self._controller[self._arg1_key] = 
msg.arg1()
+                       if self._arg2_key: self._controller[self._arg2_key] = 
msg.arg2()
+                       self._controller[self._msg_key] = msg.to_string()
 
 ##################################################
 # WX Shared Classes
 ##################################################
+import math
+import wx
+
+EVT_DATA = wx.PyEventBinder(wx.NewEventType())
+class DataEvent(wx.PyEvent):
+       def __init__(self, data):
+               wx.PyEvent.__init__(self, wx.NewId(), EVT_DATA.typeId)
+               self.data = data
+
 class LabelText(wx.StaticText):
        """
        Label text to give the wx plots a uniform look.
        Get the default label text and set the font bold.
        """
        def __init__(self, parent, label):
-               wx.StaticText.__init__(self, parent, -1, label)
+               wx.StaticText.__init__(self, parent, label=label)
                font = self.GetFont()
                font.SetWeight(wx.FONTWEIGHT_BOLD)
                self.SetFont(font)
 
+class LabelBox(wx.BoxSizer):
+       def __init__(self, parent, label, widget):
+               wx.BoxSizer.__init__(self, wx.HORIZONTAL)
+               self.Add(wx.StaticText(parent, label=' %s '%label), 1, 
wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+               self.Add(widget, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+
 class IncrDecrButtons(wx.BoxSizer):
        """
        A horizontal box sizer with a increment and a decrement button.
@@ -84,14 +110,14 @@
                @param on_decr the event handler for decrement
                """
                wx.BoxSizer.__init__(self, wx.HORIZONTAL)
-               self._incr_button = wx.Button(parent, -1, '+', 
style=wx.BU_EXACTFIT)
+               self._incr_button = wx.Button(parent, label='+', 
style=wx.BU_EXACTFIT)
                self._incr_button.Bind(wx.EVT_BUTTON, on_incr)
                self.Add(self._incr_button, 0, wx.ALIGN_CENTER_VERTICAL)
-               self._decr_button = wx.Button(parent, -1, ' - ', 
style=wx.BU_EXACTFIT)
+               self._decr_button = wx.Button(parent, label=' - ', 
style=wx.BU_EXACTFIT)
                self._decr_button.Bind(wx.EVT_BUTTON, on_decr)
                self.Add(self._decr_button, 0, wx.ALIGN_CENTER_VERTICAL)
 
-       def Disable(self, disable=True): self.Enable(not disable)
+       def Disable(self): self.Enable(False)
        def Enable(self, enable=True):
                if enable:
                        self._incr_button.Enable()
@@ -104,7 +130,7 @@
        def __init__(self, parent, controller, control_key, true_label, 
false_label):
                self._controller = controller
                self._control_key = control_key
-               wx.Button.__init__(self, parent, -1, '', style=wx.BU_EXACTFIT)
+               wx.Button.__init__(self, parent, style=wx.BU_EXACTFIT)
                self.Bind(wx.EVT_BUTTON, self._evt_button)
                controller.subscribe(control_key, lambda x: self.SetLabel(x and 
true_label or false_label))
 
@@ -122,30 +148,55 @@
        def _evt_checkbox(self, e):
                self._controller[self._control_key] = bool(e.IsChecked())
 
+from gnuradio import eng_notation
+
+class TextBoxController(wx.TextCtrl):
+       def __init__(self, parent, controller, control_key, cast=float):
+               self._controller = controller
+               self._control_key = control_key
+               self._cast = cast
+               wx.TextCtrl.__init__(self, parent, style=wx.TE_PROCESS_ENTER)
+               self.Bind(wx.EVT_TEXT_ENTER, self._evt_enter)
+               controller.subscribe(control_key, lambda x: 
self.SetValue(eng_notation.num_to_str(x)))
+
+       def _evt_enter(self, e):
+               try: self._controller[self._control_key] = 
self._cast(eng_notation.str_to_num(self.GetValue()))
+               except: self._controller[self._control_key] = 
self._controller[self._control_key]
+
 class LogSliderController(wx.BoxSizer):
        """
        Log slider controller with display label and slider.
        Gives logarithmic scaling to slider operation.
        """
-       def __init__(self, parent, label, min_exp, max_exp, slider_steps, 
controller, control_key, formatter=lambda x: ': %.6f'%x):
+       def __init__(self, parent, prefix, min_exp, max_exp, slider_steps, 
controller, control_key, formatter=lambda x: ': %.6f'%x):
+               self._prefix = prefix
+               self._min_exp = min_exp
+               self._max_exp = max_exp
+               self._controller = controller
+               self._control_key = control_key
+               self._formatter = formatter
                wx.BoxSizer.__init__(self, wx.VERTICAL)
-               self._label = wx.StaticText(parent, -1, label + 
formatter(1/3.0))
+               self._label = wx.StaticText(parent, label=prefix + 
formatter(1/3.0))
                self.Add(self._label, 0, wx.EXPAND)
-               self._slider = wx.Slider(parent, -1, 0, 0, slider_steps, 
style=wx.SL_HORIZONTAL)
+               self._slider = wx.Slider(parent, minValue=0, 
maxValue=slider_steps, style=wx.SL_HORIZONTAL)
                self.Add(self._slider, 0, wx.EXPAND)
-               def _on_slider_event(event):
-                       controller[control_key] = \
-                       
10**(float(max_exp-min_exp)*self._slider.GetValue()/slider_steps + min_exp)
-               self._slider.Bind(wx.EVT_SLIDER, _on_slider_event)
-               def _on_controller_set(value):
-                       self._label.SetLabel(label + formatter(value))
-                       slider_value = 
slider_steps*(math.log10(value)-min_exp)/(max_exp-min_exp)
-                       slider_value = min(max(0, slider_value), slider_steps)
-                       if abs(slider_value - self._slider.GetValue()) > 1:
-                               self._slider.SetValue(slider_value)
-               controller.subscribe(control_key, _on_controller_set)
+               self._slider.Bind(wx.EVT_SLIDER, self._on_slider_event)
+               controller.subscribe(control_key, self._on_controller_set)
 
-       def Disable(self, disable=True): self.Enable(not disable)
+       def _get_slope(self):
+               return float(self._max_exp-self._min_exp)/self._slider.GetMax()
+
+       def _on_slider_event(self, e):
+               self._controller[self._control_key] = 
10**(self._get_slope()*self._slider.GetValue() + self._min_exp)
+
+       def _on_controller_set(self, value):
+               self._label.SetLabel(self._prefix + self._formatter(value))
+               slider_value = 
(math.log10(value)-self._min_exp)/self._get_slope()
+               slider_value = min(max(self._slider.GetMin(), slider_value), 
self._slider.GetMax())
+               if abs(slider_value - self._slider.GetValue()) > 1:
+                       self._slider.SetValue(slider_value)
+
+       def Disable(self): self.Enable(False)
        def Enable(self, enable=True):
                if enable:
                        self._slider.Enable()
@@ -154,45 +205,39 @@
                        self._slider.Disable()
                        self._label.Disable()
 
-class DropDownController(wx.BoxSizer):
+class DropDownController(wx.Choice):
        """
        Drop down controller with label and chooser.
        Srop down selection from a set of choices.
        """
-       def __init__(self, parent, label, choices, controller, control_key, 
size=(-1, -1)):
+       def __init__(self, parent, choices, controller, control_key, size=(-1, 
-1)):
                """
                @param parent the parent window
-               @param label the label for the drop down
                @param choices a list of tuples -> (label, value)
                @param controller the prop val controller
                @param control_key the prop key for this control
                """
-               wx.BoxSizer.__init__(self, wx.HORIZONTAL)
-               self._label = wx.StaticText(parent, -1, ' %s '%label)
-               self.Add(self._label, 1, wx.ALIGN_CENTER_VERTICAL)
-               self._chooser = wx.Choice(parent, -1, choices=[c[0] for c in 
choices], size=size)
-               def _on_chooser_event(event):
-                       controller[control_key] = 
choices[self._chooser.GetSelection()][1]
-               self._chooser.Bind(wx.EVT_CHOICE, _on_chooser_event)
-               self.Add(self._chooser, 0, wx.ALIGN_CENTER_VERTICAL)
-               def _on_controller_set(value):
-                       #only set the chooser if the value is a possible choice
-                       for i, choice in enumerate(choices):
-                               if value == choice[1]: 
self._chooser.SetSelection(i)
-               controller.subscribe(control_key, _on_controller_set)
+               self._controller = controller
+               self._control_key = control_key
+               self._choices = choices
+               wx.Choice.__init__(self, parent, choices=[c[0] for c in 
choices], size=size)
+               self.Bind(wx.EVT_CHOICE, self._on_chooser_event)
+               controller.subscribe(control_key, self._on_controller_set)
 
-       def Disable(self, disable=True): self.Enable(not disable)
-       def Enable(self, enable=True):
-               if enable:
-                       self._chooser.Enable()
-                       self._label.Enable()
-               else:
-                       self._chooser.Disable()
-                       self._label.Disable()
+       def _on_chooser_event(self, e):
+               self._controller[self._control_key] = 
self._choices[self.GetSelection()][1]
 
+       def _on_controller_set(self, value):
+               #only set the chooser if the value is a possible choice
+               for i, choice in enumerate(self._choices):
+                       if value == choice[1]: self.SetSelection(i)
+
 ##################################################
 # Shared Functions
 ##################################################
+import numpy
+import math
+
 def get_exp(num):
        """
        Get the exponent of the number in base 10.
@@ -209,8 +254,7 @@
        @return the closest number
        """
        if num == 0: return 0
-       if num > 0: sign = 1
-       else: sign = -1
+       sign = num > 0 and 1 or -1
        exp = get_exp(num)
        nums = numpy.array((1, 2, 5, 10))*(10**exp)
        return sign*nums[numpy.argmin(numpy.abs(nums - abs(num)))]
@@ -263,49 +307,3 @@
        min = mean - rms
        max = mean + rms
        return min, max
-
-def get_si_components(num):
-       """
-       Get the SI units for the number.
-       Extract the coeff and exponent of the number.
-       The exponent will be a multiple of 3.
-       @param num the floating point number
-       @return the tuple coeff, exp, prefix
-       """
-       exp = get_exp(num)
-       exp -= exp%3
-       exp = min(max(exp, -24), 24) #bounds on SI table below
-       prefix = {
-               24: 'Y', 21: 'Z',
-               18: 'E', 15: 'P',
-               12: 'T', 9: 'G',
-               6: 'M', 3: 'K',
-               0: '',
-               -3: 'm', -6: 'u',
-               -9: 'n', -12: 'p',
-               -15: 'f', -18: 'a',
-               -21: 'z', -24: 'y',
-       }[exp]
-       coeff = num/10**exp
-       return coeff, exp, prefix
-
-def label_format(num):
-       """
-       Format a floating point number into a presentable string.
-       If the number has an small enough exponent, use regular decimal.
-       Otherwise, format the number with floating point notation.
-       Exponents are normalized to multiples of 3.
-       In the case where the exponent was found to be -3,
-       it is best to display this as a regular decimal, with a 0 to the left.
-       @param num the number to format
-       @return a label string
-       """
-       coeff, exp, prefix = get_si_components(num)
-       if -3 <= exp < 3: return '%g'%num
-       return '%se%d'%('%.3g'%coeff, exp)
-
-if __name__ == '__main__':
-       import random
-       for i in range(-25, 25):
-               num = random.random()*10**i
-               print num, ':', get_si_components(num)

Modified: gnuradio/trunk/gr-wxgui/src/python/const_window.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/const_window.py  2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/const_window.py  2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -62,32 +62,31 @@
                @param parent the wx parent window
                """
                self.parent = parent
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
                control_box = wx.BoxSizer(wx.VERTICAL)
                self.marker_index = 2
                #begin control box
-               control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
-               #marker
-               control_box.AddStretchSpacer()
-               self.marker_chooser = common.DropDownController(self, 'Marker', 
MARKER_TYPES, parent, MARKER_KEY)
-               control_box.Add(self.marker_chooser, 0, wx.EXPAND)
                #alpha
                control_box.AddStretchSpacer()
-               self.alpha_slider = common.LogSliderController(
+               alpha_slider = common.LogSliderController(
                        self, 'Alpha',
                        ALPHA_MIN_EXP, ALPHA_MAX_EXP, SLIDER_STEPS,
-                       parent.ext_controller, parent.alpha_key,
+                       parent, ALPHA_KEY,
                )
-               control_box.Add(self.alpha_slider, 0, wx.EXPAND)
+               control_box.Add(alpha_slider, 0, wx.EXPAND)
                #gain_mu
                control_box.AddStretchSpacer()
-               self.gain_mu_slider = common.LogSliderController(
+               gain_mu_slider = common.LogSliderController(
                        self, 'Gain Mu',
                        GAIN_MU_MIN_EXP, GAIN_MU_MAX_EXP, SLIDER_STEPS,
-                       parent.ext_controller, parent.gain_mu_key,
+                       parent, GAIN_MU_KEY,
                )
-               control_box.Add(self.gain_mu_slider, 0, wx.EXPAND)
+               control_box.Add(gain_mu_slider, 0, wx.EXPAND)
+               #marker
+               control_box.AddStretchSpacer()
+               marker_chooser = common.DropDownController(self, MARKER_TYPES, 
parent, MARKER_KEY)
+               control_box.Add(common.LabelBox(self, 'Marker', 
marker_chooser), 0, wx.EXPAND)
                #run/stop
                control_box.AddStretchSpacer()
                self.run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
@@ -98,7 +97,7 @@
 ##################################################
 # Constellation window with plotter and control panel
 ##################################################
-class const_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+class const_window(wx.Panel, pubsub.pubsub):
        def __init__(
                self,
                parent,
@@ -110,22 +109,27 @@
                beta_key,
                gain_mu_key,
                gain_omega_key,
+               omega_key,
+               sample_rate_key,
        ):
                pubsub.pubsub.__init__(self)
-               #setup
-               self.ext_controller = controller
-               self.alpha_key = alpha_key
-               self.beta_key = beta_key
-               self.gain_mu_key = gain_mu_key
-               self.gain_omega_key = gain_omega_key
+               #proxy the keys
+               self.proxy(MSG_KEY, controller, msg_key)
+               self.proxy(ALPHA_KEY, controller, alpha_key)
+               self.proxy(BETA_KEY, controller, beta_key)
+               self.proxy(GAIN_MU_KEY, controller, gain_mu_key)
+               self.proxy(GAIN_OMEGA_KEY, controller, gain_omega_key)
+               self.proxy(OMEGA_KEY, controller, omega_key)
+               self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
                #init panel and plot
-               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
                self.plotter = plotter.channel_plotter(self)
                self.plotter.SetSize(wx.Size(*size))
                self.plotter.set_title(title)
                self.plotter.set_x_label('Inphase')
                self.plotter.set_y_label('Quadrature')
                self.plotter.enable_point_label(True)
+               self.plotter.enable_grid_lines(True)
                #setup the box with plot and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
@@ -133,22 +137,21 @@
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
                #alpha and gain mu 2nd orders
-               def set_beta(alpha): self.ext_controller[self.beta_key] = 
.25*alpha**2
-               self.ext_controller.subscribe(self.alpha_key, set_beta)
-               def set_gain_omega(gain_mu): 
self.ext_controller[self.gain_omega_key] = .25*gain_mu**2
-               self.ext_controller.subscribe(self.gain_mu_key, set_gain_omega)
-               #initial setup
-               self.ext_controller[self.alpha_key] = 
self.ext_controller[self.alpha_key]
-               self.ext_controller[self.gain_mu_key] = 
self.ext_controller[self.gain_mu_key]
-               self._register_set_prop(self, RUNNING_KEY, True)
-               self._register_set_prop(self, X_DIVS_KEY, 8)
-               self._register_set_prop(self, Y_DIVS_KEY, 8)
-               self._register_set_prop(self, MARKER_KEY, DEFAULT_MARKER_TYPE)
+               def set_beta(alpha): self[BETA_KEY] = .25*alpha**2
+               self.subscribe(ALPHA_KEY, set_beta)
+               def set_gain_omega(gain_mu): self[GAIN_OMEGA_KEY] = 
.25*gain_mu**2
+               self.subscribe(GAIN_MU_KEY, set_gain_omega)
+               #initialize values
+               self[ALPHA_KEY] = self[ALPHA_KEY]
+               self[GAIN_MU_KEY] = self[GAIN_MU_KEY]
+               self[RUNNING_KEY] = True
+               self[X_DIVS_KEY] = 8
+               self[Y_DIVS_KEY] = 8
+               self[MARKER_KEY] = DEFAULT_MARKER_TYPE
                #register events
-               self.ext_controller.subscribe(msg_key, self.handle_msg)
-               for key in (
-                       X_DIVS_KEY, Y_DIVS_KEY,
-               ): self.subscribe(key, self.update_grid)
+               self.subscribe(MSG_KEY, self.handle_msg)
+               self.subscribe(X_DIVS_KEY, self.update_grid)
+               self.subscribe(Y_DIVS_KEY, self.update_grid)
                #initial update
                self.update_grid()
 
@@ -173,15 +176,12 @@
                self.plotter.update()
 
        def update_grid(self):
-               #grid parameters
-               x_divs = self[X_DIVS_KEY]
-               y_divs = self[Y_DIVS_KEY]
                #update the x axis
                x_max = 2.0
-               self.plotter.set_x_grid(-x_max, x_max, 
common.get_clean_num(2.0*x_max/x_divs))
+               self.plotter.set_x_grid(-x_max, x_max, 
common.get_clean_num(2.0*x_max/self[X_DIVS_KEY]))
                #update the y axis
                y_max = 2.0
-               self.plotter.set_y_grid(-y_max, y_max, 
common.get_clean_num(2.0*y_max/y_divs))
+               self.plotter.set_y_grid(-y_max, y_max, 
common.get_clean_num(2.0*y_max/self[Y_DIVS_KEY]))
                #update plotter
                self.plotter.update()
 

Modified: gnuradio/trunk/gr-wxgui/src/python/constants.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/constants.py     2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/constants.py     2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -36,6 +36,7 @@
 GAIN_MU_KEY = 'gain_mu'
 GAIN_OMEGA_KEY = 'gain_omega'
 MARKER_KEY = 'marker'
+XY_MARKER_KEY = 'xy_marker'
 MSG_KEY = 'msg'
 NUM_LINES_KEY = 'num_lines'
 OMEGA_KEY = 'omega'
@@ -43,15 +44,15 @@
 REF_LEVEL_KEY = 'ref_level'
 RUNNING_KEY = 'running'
 SAMPLE_RATE_KEY = 'sample_rate'
-SCOPE_TRIGGER_CHANNEL_KEY = 'scope_trigger_channel'
-SCOPE_TRIGGER_LEVEL_KEY = 'scope_trigger_level'
-SCOPE_TRIGGER_MODE_KEY = 'scope_trigger_mode'
-SCOPE_X_CHANNEL_KEY = 'scope_x_channel'
-SCOPE_Y_CHANNEL_KEY = 'scope_y_channel'
-SCOPE_XY_MODE_KEY = 'scope_xy_mode'
 TRIGGER_CHANNEL_KEY = 'trigger_channel'
 TRIGGER_LEVEL_KEY = 'trigger_level'
 TRIGGER_MODE_KEY = 'trigger_mode'
+TRIGGER_SLOPE_KEY = 'trigger_slope'
+TRIGGER_SHOW_KEY = 'trigger_show'
+XY_MODE_KEY = 'xy_mode'
+X_CHANNEL_KEY = 'x_channel'
+Y_CHANNEL_KEY = 'y_channel'
+T_FRAC_OFF_KEY = 't_frac_off'
 T_DIVS_KEY = 't_divs'
 T_OFF_KEY = 't_off'
 T_PER_DIV_KEY = 't_per_div'
@@ -61,4 +62,7 @@
 Y_DIVS_KEY = 'y_divs'
 Y_OFF_KEY = 'y_off'
 Y_PER_DIV_KEY = 'y_per_div'
-
+MAXIMUM_KEY = 'maximum'
+MINIMUM_KEY = 'minimum'
+NUM_BINS_KEY = 'num_bins'
+FRAME_SIZE_KEY = 'frame_size'

Modified: gnuradio/trunk/gr-wxgui/src/python/constsink_gl.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/constsink_gl.py  2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/constsink_gl.py  2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -31,7 +31,7 @@
 ##################################################
 # Constellation sink block (wrapper for old wxgui)
 ##################################################
-class const_sink_c(gr.hier_block2, common.prop_setter):
+class const_sink_c(gr.hier_block2):
        """
        A constellation block with a gui window.
        """
@@ -97,8 +97,7 @@
                #connect
                self.connect(self, self._costas, self._retime, agc, sd, sink)
                #controller
-               def setter(p, k, x): # lambdas can't have assignments :(
-                   p[k] = x
+               def setter(p, k, x): p[k] = x
                self.controller = pubsub()
                self.controller.subscribe(ALPHA_KEY, self._costas.set_alpha)
                self.controller.publish(ALPHA_KEY, self._costas.alpha)
@@ -116,7 +115,7 @@
                #initial update
                self.controller[SAMPLE_RATE_KEY] = sample_rate
                #start input watcher
-               common.input_watcher(msgq, lambda x: setter(self.controller, 
MSG_KEY, x))
+               common.input_watcher(msgq, self.controller, MSG_KEY)
                #create window
                self.win = const_window.const_window(
                        parent=parent,
@@ -128,15 +127,9 @@
                        beta_key=BETA_KEY,
                        gain_mu_key=GAIN_MU_KEY,
                        gain_omega_key=GAIN_OMEGA_KEY,
+                       omega_key=OMEGA_KEY,
+                       sample_rate_key=SAMPLE_RATE_KEY,
                )
-               #register callbacks from window for external use
-               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
-                       setattr(self, attr, getattr(self.win, attr))
-               self._register_set_prop(self.controller, ALPHA_KEY)
-               self._register_set_prop(self.controller, BETA_KEY)
-               self._register_set_prop(self.controller, GAIN_MU_KEY)
-               self._register_set_prop(self.controller, OMEGA_KEY)
-               self._register_set_prop(self.controller, GAIN_OMEGA_KEY)
-               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
+               common.register_access_methods(self, self.win)
 
 

Modified: gnuradio/trunk/gr-wxgui/src/python/fft_window.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/fft_window.py    2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/fft_window.py    2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -39,8 +39,8 @@
 DEFAULT_WIN_SIZE = (600, 300)
 DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'fft_rate', 30)
 DIV_LEVELS = (1, 2, 5, 10, 20)
-FFT_PLOT_COLOR_SPEC = (0, 0, 1)
-PEAK_VALS_COLOR_SPEC = (0, 1, 0)
+FFT_PLOT_COLOR_SPEC = (0.3, 0.3, 1.0)
+PEAK_VALS_COLOR_SPEC = (0.0, 0.8, 0.0)
 NO_PEAK_VALS = list()
 
 ##################################################
@@ -57,31 +57,31 @@
                @param parent the wx parent window
                """
                self.parent = parent
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
                control_box = wx.BoxSizer(wx.VERTICAL)
                #checkboxes for average and peak hold
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
-               self.average_check_box = common.CheckBoxController(self, 
'Average', parent.ext_controller, parent.average_key)
-               control_box.Add(self.average_check_box, 0, wx.EXPAND)
-               self.peak_hold_check_box = common.CheckBoxController(self, 
'Peak Hold', parent, PEAK_HOLD_KEY)
-               control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
+               peak_hold_check_box = common.CheckBoxController(self, 'Peak 
Hold', parent, PEAK_HOLD_KEY)
+               control_box.Add(peak_hold_check_box, 0, wx.EXPAND)
+               average_check_box = common.CheckBoxController(self, 'Average', 
parent, AVERAGE_KEY)
+               control_box.Add(average_check_box, 0, wx.EXPAND)
                control_box.AddSpacer(2)
-               self.avg_alpha_slider = common.LogSliderController(
+               avg_alpha_slider = common.LogSliderController(
                        self, 'Avg Alpha',
                        AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
-                       parent.ext_controller, parent.avg_alpha_key,
+                       parent, AVG_ALPHA_KEY,
                        formatter=lambda x: ': %.4f'%x,
                )
-               parent.ext_controller.subscribe(parent.average_key, 
self.avg_alpha_slider.Enable)
-               control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+               parent.subscribe(AVERAGE_KEY, avg_alpha_slider.Enable)
+               control_box.Add(avg_alpha_slider, 0, wx.EXPAND)
                #radio buttons for div size
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Set dB/div'), 0, 
wx.ALIGN_CENTER)
                radio_box = wx.BoxSizer(wx.VERTICAL)
                self.radio_buttons = list()
                for y_per_div in DIV_LEVELS:
-                       radio_button = wx.RadioButton(self, -1, "%d 
dB/div"%y_per_div)
+                       radio_button = wx.RadioButton(self, label="%d 
dB/div"%y_per_div)
                        radio_button.Bind(wx.EVT_RADIOBUTTON, 
self._on_y_per_div)
                        self.radio_buttons.append(radio_button)
                        radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
@@ -91,18 +91,23 @@
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Set Ref Level'), 0, 
wx.ALIGN_CENTER)
                control_box.AddSpacer(2)
-               self._ref_lvl_buttons = common.IncrDecrButtons(self, 
self._on_incr_ref_level, self._on_decr_ref_level)
-               control_box.Add(self._ref_lvl_buttons, 0, wx.ALIGN_CENTER)
+               _ref_lvl_buttons = common.IncrDecrButtons(self, 
self._on_incr_ref_level, self._on_decr_ref_level)
+               control_box.Add(_ref_lvl_buttons, 0, wx.ALIGN_CENTER)
                #autoscale
                control_box.AddStretchSpacer()
-               self.autoscale_button = wx.Button(self, label='Autoscale', 
style=wx.BU_EXACTFIT)
-               self.autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
-               control_box.Add(self.autoscale_button, 0, wx.EXPAND)
+               autoscale_button = wx.Button(self, label='Autoscale', 
style=wx.BU_EXACTFIT)
+               autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
+               control_box.Add(autoscale_button, 0, wx.EXPAND)
                #run/stop
-               self.run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
-               control_box.Add(self.run_button, 0, wx.EXPAND)
+               run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
+               control_box.Add(run_button, 0, wx.EXPAND)
                #set sizer
                self.SetSizerAndFit(control_box)
+               #mouse wheel event
+               def on_mouse_wheel(event):
+                       if event.GetWheelRotation() < 0: 
self._on_incr_ref_level(event)
+                       else: self._on_decr_ref_level(event)
+               parent.plotter.Bind(wx.EVT_MOUSEWHEEL, on_mouse_wheel)
 
        ##################################################
        # Event handlers
@@ -117,16 +122,14 @@
                index = self.radio_buttons.index(selected_radio_button)
                self.parent[Y_PER_DIV_KEY] = DIV_LEVELS[index]
        def _on_incr_ref_level(self, event):
-               self.parent.set_ref_level(
-                       self.parent[REF_LEVEL_KEY] + self.parent[Y_PER_DIV_KEY])
+               self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] + 
self.parent[Y_PER_DIV_KEY]
        def _on_decr_ref_level(self, event):
-               self.parent.set_ref_level(
-                       self.parent[REF_LEVEL_KEY] - self.parent[Y_PER_DIV_KEY])
+               self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] - 
self.parent[Y_PER_DIV_KEY]
 
 ##################################################
 # FFT window with plotter and control panel
 ##################################################
-class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+class fft_window(wx.Panel, pubsub.pubsub):
        def __init__(
                self,
                parent,
@@ -150,47 +153,48 @@
                if y_per_div not in DIV_LEVELS: y_per_div = DIV_LEVELS[0]
                #setup
                self.samples = list()
-               self.ext_controller = controller
                self.real = real
                self.fft_size = fft_size
-               self.sample_rate_key = sample_rate_key
-               self.average_key = average_key
-               self.avg_alpha_key = avg_alpha_key
                self._reset_peak_vals()
+               #proxy the keys
+               self.proxy(MSG_KEY, controller, msg_key)
+               self.proxy(AVERAGE_KEY, controller, average_key)
+               self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
+               self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
                #init panel and plot
-               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
                self.plotter = plotter.channel_plotter(self)
                self.plotter.SetSize(wx.Size(*size))
                self.plotter.set_title(title)
+               self.plotter.enable_legend(True)
                self.plotter.enable_point_label(True)
+               self.plotter.enable_grid_lines(True)
                #setup the box with plot and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
                main_box.Add(self.plotter, 1, wx.EXPAND)
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
-               #initial setup
-               self.ext_controller[self.average_key] = 
self.ext_controller[self.average_key]
-               self.ext_controller[self.avg_alpha_key] = 
self.ext_controller[self.avg_alpha_key]
-               self._register_set_prop(self, PEAK_HOLD_KEY, peak_hold)
-               self._register_set_prop(self, Y_PER_DIV_KEY, y_per_div)
-               self._register_set_prop(self, Y_DIVS_KEY, y_divs)
-               self._register_set_prop(self, X_DIVS_KEY, 8) #approximate
-               self._register_set_prop(self, REF_LEVEL_KEY, ref_level)
-               self._register_set_prop(self, BASEBAND_FREQ_KEY, baseband_freq)
-               self._register_set_prop(self, RUNNING_KEY, True)
+               #initialize values
+               self[AVERAGE_KEY] = self[AVERAGE_KEY]
+               self[AVG_ALPHA_KEY] = self[AVG_ALPHA_KEY]
+               self[PEAK_HOLD_KEY] = peak_hold
+               self[Y_PER_DIV_KEY] = y_per_div
+               self[Y_DIVS_KEY] = y_divs
+               self[X_DIVS_KEY] = 8 #approximate
+               self[REF_LEVEL_KEY] = ref_level
+               self[BASEBAND_FREQ_KEY] = baseband_freq
+               self[RUNNING_KEY] = True
                #register events
-               self.subscribe(PEAK_HOLD_KEY, self.plotter.enable_legend)
-               self.ext_controller.subscribe(AVERAGE_KEY, lambda x: 
self._reset_peak_vals())
-               self.ext_controller.subscribe(msg_key, self.handle_msg)
-               self.ext_controller.subscribe(self.sample_rate_key, 
self.update_grid)
+               self.subscribe(AVERAGE_KEY, lambda x: self._reset_peak_vals())
+               self.subscribe(MSG_KEY, self.handle_msg)
+               self.subscribe(SAMPLE_RATE_KEY, self.update_grid)
                for key in (
                        BASEBAND_FREQ_KEY,
                        Y_PER_DIV_KEY, X_DIVS_KEY,
                        Y_DIVS_KEY, REF_LEVEL_KEY,
                ): self.subscribe(key, self.update_grid)
                #initial update
-               self.plotter.enable_legend(self[PEAK_HOLD_KEY])
                self.update_grid()
 
        def autoscale(self, *args):
@@ -207,9 +211,9 @@
                noise_floor -= abs(noise_floor)*.5
                peak_level += abs(peak_level)*.1
                #set the reference level to a multiple of y divs
-               
self.set_ref_level(self[Y_DIVS_KEY]*math.ceil(peak_level/self[Y_DIVS_KEY]))
+               self[REF_LEVEL_KEY] = 
self[Y_DIVS_KEY]*math.ceil(peak_level/self[Y_DIVS_KEY])
                #set the range to a clean number of the dynamic range
-               self.set_y_per_div(common.get_clean_num((peak_level - 
noise_floor)/self[Y_DIVS_KEY]))
+               self[Y_PER_DIV_KEY] = common.get_clean_num((peak_level - 
noise_floor)/self[Y_DIVS_KEY])
 
        def _reset_peak_vals(self): self.peak_vals = NO_PEAK_VALS
 
@@ -234,19 +238,21 @@
                if self[PEAK_HOLD_KEY]:
                        if len(self.peak_vals) != len(samples): self.peak_vals 
= samples
                        self.peak_vals = numpy.maximum(samples, self.peak_vals)
-               else: self._reset_peak_vals()
+                       #plot the peak hold
+                       self.plotter.set_waveform(
+                               channel='Peak',
+                               samples=self.peak_vals,
+                               color_spec=PEAK_VALS_COLOR_SPEC,
+                       )
+               else:
+                       self._reset_peak_vals()
+                       self.plotter.clear_waveform(channel='Peak')
                #plot the fft
                self.plotter.set_waveform(
                        channel='FFT',
                        samples=samples,
                        color_spec=FFT_PLOT_COLOR_SPEC,
                )
-               #plot the peak hold
-               self.plotter.set_waveform(
-                       channel='Peak',
-                       samples=self.peak_vals,
-                       color_spec=PEAK_VALS_COLOR_SPEC,
-               )
                #update the plotter
                self.plotter.update()
 
@@ -259,7 +265,7 @@
                The y axis depends on y per div, y divs, and ref level.
                """
                #grid parameters
-               sample_rate = self.ext_controller[self.sample_rate_key]
+               sample_rate = self[SAMPLE_RATE_KEY]
                baseband_freq = self[BASEBAND_FREQ_KEY]
                y_per_div = self[Y_PER_DIV_KEY]
                y_divs = self[Y_DIVS_KEY]
@@ -269,24 +275,21 @@
                if self.real: x_width = sample_rate/2.0
                else: x_width = sample_rate/1.0
                x_per_div = common.get_clean_num(x_width/x_divs)
-               coeff, exp, prefix = 
common.get_si_components(abs(baseband_freq) + abs(sample_rate/2.0))
                #update the x grid
                if self.real:
                        self.plotter.set_x_grid(
                                baseband_freq,
                                baseband_freq + sample_rate/2.0,
-                               x_per_div,
-                               10**(-exp),
+                               x_per_div, True,
                        )
                else:
                        self.plotter.set_x_grid(
                                baseband_freq - sample_rate/2.0,
                                baseband_freq + sample_rate/2.0,
-                               x_per_div,
-                               10**(-exp),
+                               x_per_div, True,
                        )
                #update x units
-               self.plotter.set_x_label('Frequency', prefix+'Hz')
+               self.plotter.set_x_label('Frequency', 'Hz')
                #update y grid
                self.plotter.set_y_grid(ref_level-y_per_div*y_divs, ref_level, 
y_per_div)
                #update y units

Modified: gnuradio/trunk/gr-wxgui/src/python/fftsink_gl.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/fftsink_gl.py    2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/fftsink_gl.py    2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -31,7 +31,7 @@
 ##################################################
 # FFT sink block (wrapper for old wxgui)
 ##################################################
-class _fft_sink_base(gr.hier_block2, common.prop_setter):
+class _fft_sink_base(gr.hier_block2):
        """
        An fft block with real/complex inputs and a gui window.
        """
@@ -85,9 +85,7 @@
                self.controller.subscribe(SAMPLE_RATE_KEY, fft.set_sample_rate)
                self.controller.publish(SAMPLE_RATE_KEY, fft.sample_rate)
                #start input watcher
-               def setter(p, k, x): # lambdas can't have assignments :(
-                   p[k] = x
-               common.input_watcher(msgq, lambda x: setter(self.controller, 
MSG_KEY, x))
+               common.input_watcher(msgq, self.controller, MSG_KEY)
                #create window
                self.win = fft_window.fft_window(
                        parent=parent,
@@ -106,12 +104,9 @@
                        peak_hold=peak_hold,
                        msg_key=MSG_KEY,
                )
-               #register callbacks from window for external use
-               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
-                       setattr(self, attr, getattr(self.win, attr))
-               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
-               self._register_set_prop(self.controller, AVERAGE_KEY)
-               self._register_set_prop(self.controller, AVG_ALPHA_KEY)
+               common.register_access_methods(self, self.win)
+               setattr(self.win, 'set_baseband_freq', getattr(self, 
'set_baseband_freq')) #BACKWARDS
+               setattr(self.win, 'set_peak_hold', getattr(self, 
'set_peak_hold')) #BACKWARDS
 
 class fft_sink_f(_fft_sink_base):
        _fft_chain = blks2.logpwrfft_f

Modified: gnuradio/trunk/gr-wxgui/src/python/fftsink_nongl.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/fftsink_nongl.py 2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/fftsink_nongl.py 2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -319,7 +319,7 @@
         # self.SetBackgroundColour ('black')
         
         self.build_popup_menu()
-        self.set_baseband_freq(0.0)
+        self.set_baseband_freq(self.fftsink.baseband_freq)
                 
         EVT_DATA_EVENT (self, self.set_data)
         wx.EVT_CLOSE (self, self.on_close_window)

Copied: gnuradio/trunk/gr-wxgui/src/python/histo_window.py (from rev 10658, 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histo_window.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/histo_window.py                          
(rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/histo_window.py  2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -0,0 +1,154 @@
+#
+# Copyright 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+##################################################
+# Imports
+##################################################
+import plotter
+import common
+import wx
+import numpy
+import math
+import pubsub
+from constants import *
+from gnuradio import gr #for gr.prefs
+
+##################################################
+# Constants
+##################################################
+DEFAULT_WIN_SIZE = (600, 300)
+
+##################################################
+# histo window control panel
+##################################################
+class control_panel(wx.Panel):
+       """
+       A control panel with wx widgits to control the plotter and histo sink.
+       """
+
+       def __init__(self, parent):
+               """
+               Create a new control panel.
+               @param parent the wx parent window
+               """
+               self.parent = parent
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
+               control_box = wx.BoxSizer(wx.VERTICAL)
+               SIZE = (100, -1)
+               control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
+               control_box.AddStretchSpacer()
+               #num bins
+               def num_bins_cast(num):
+                       num = int(num)
+                       assert num > 1
+                       return num
+               num_bins_ctrl = common.TextBoxController(self, parent, 
NUM_BINS_KEY, cast=num_bins_cast)
+               control_box.Add(common.LabelBox(self, ' Num Bins ', 
num_bins_ctrl), 0, wx.EXPAND)
+               control_box.AddStretchSpacer()
+               #frame size
+               frame_size_ctrl = common.TextBoxController(self, parent, 
FRAME_SIZE_KEY, cast=num_bins_cast)
+               control_box.Add(common.LabelBox(self, ' Frame Size ', 
frame_size_ctrl), 0, wx.EXPAND)
+               control_box.AddStretchSpacer()
+               #run/stop
+               self.run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
+               control_box.Add(self.run_button, 0, wx.EXPAND)
+               #set sizer
+               self.SetSizerAndFit(control_box)
+
+##################################################
+# histo window with plotter and control panel
+##################################################
+class histo_window(wx.Panel, pubsub.pubsub):
+       def __init__(
+               self,
+               parent,
+               controller,
+               size,
+               title,
+               maximum_key,
+               minimum_key,
+               num_bins_key,
+               frame_size_key,
+               msg_key,
+       ):
+               pubsub.pubsub.__init__(self)
+               #setup
+               self.samples = list()
+               #proxy the keys
+               self.proxy(MAXIMUM_KEY, controller, maximum_key)
+               self.proxy(MINIMUM_KEY, controller, minimum_key)
+               self.proxy(NUM_BINS_KEY, controller, num_bins_key)
+               self.proxy(FRAME_SIZE_KEY, controller, frame_size_key)
+               self.proxy(MSG_KEY, controller, msg_key)
+               #init panel and plot
+               wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
+               self.plotter = plotter.bar_plotter(self)
+               self.plotter.SetSize(wx.Size(*size))
+               self.plotter.set_title(title)
+               self.plotter.enable_point_label(True)
+               self.plotter.enable_grid_lines(False)
+               #setup the box with plot and controls
+               self.control_panel = control_panel(self)
+               main_box = wx.BoxSizer(wx.HORIZONTAL)
+               main_box.Add(self.plotter, 1, wx.EXPAND)
+               main_box.Add(self.control_panel, 0, wx.EXPAND)
+               self.SetSizerAndFit(main_box)
+               #initialize values
+               self[NUM_BINS_KEY] = self[NUM_BINS_KEY]
+               self[FRAME_SIZE_KEY] = self[FRAME_SIZE_KEY]
+               self[RUNNING_KEY] = True
+               self[X_DIVS_KEY] = 8
+               self[Y_DIVS_KEY] = 4
+               #register events
+               self.subscribe(MSG_KEY, self.handle_msg)
+               self.subscribe(X_DIVS_KEY, self.update_grid)
+               self.subscribe(Y_DIVS_KEY, self.update_grid)
+
+       def handle_msg(self, msg):
+               """
+               Handle the message from the fft sink message queue.
+               @param msg the frame as a character array
+               """
+               if not self[RUNNING_KEY]: return
+               #convert to floating point numbers
+               self.samples = 100*numpy.fromstring(msg, 
numpy.float32)[:self[NUM_BINS_KEY]] #only take first frame
+               self.plotter.set_bars(
+                       bars=self.samples,
+                       bar_width=0.6,
+                       color_spec=(0, 0, 1),
+               )
+               self.update_grid()
+
+       def update_grid(self):
+               if not len(self.samples): return
+               #calculate the maximum y value
+               y_off = math.ceil(numpy.max(self.samples))
+               y_off = min(max(y_off, 1.0), 100.0) #between 1% and 100%
+               #update the x grid
+               self.plotter.set_x_grid(
+                       self[MINIMUM_KEY], self[MAXIMUM_KEY],
+                       common.get_clean_num((self[MAXIMUM_KEY] - 
self[MINIMUM_KEY])/self[X_DIVS_KEY]),
+               )
+               self.plotter.set_x_label('Counts')
+               #update the y grid
+               self.plotter.set_y_grid(0, y_off, y_off/self[Y_DIVS_KEY])
+               self.plotter.set_y_label('Frequency', '%')
+               self.plotter.update()

Copied: gnuradio/trunk/gr-wxgui/src/python/histosink_gl.py (from rev 10658, 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histosink_gl.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/histosink_gl.py                          
(rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/histosink_gl.py  2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -0,0 +1,110 @@
+#
+# Copyright 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+##################################################
+# Imports
+##################################################
+import histo_window
+import common
+from gnuradio import gr, blks2
+from pubsub import pubsub
+from constants import *
+
+##################################################
+# histo sink block (wrapper for old wxgui)
+##################################################
+class histo_sink_f(gr.hier_block2):
+       """
+       A histogram block and a gui window.
+       """
+
+       def __init__(
+               self,
+               parent,
+               size=histo_window.DEFAULT_WIN_SIZE,
+               title='',
+               num_bins=11,
+               frame_size=1000,
+       ):
+               #init
+               gr.hier_block2.__init__(
+                       self,
+                       "histo_sink",
+                       gr.io_signature(1, 1, gr.sizeof_float),
+                       gr.io_signature(0, 0, 0),
+               )
+               #blocks
+               msgq = gr.msg_queue(2)
+               histo = gr.histo_sink_f(msgq)
+               histo.set_num_bins(num_bins)
+               histo.set_frame_size(frame_size)
+               #connect
+               self.connect(self, histo)
+               #controller
+               self.controller = pubsub()
+               self.controller.subscribe(NUM_BINS_KEY, histo.set_num_bins)
+               self.controller.publish(NUM_BINS_KEY, histo.get_num_bins)
+               self.controller.subscribe(FRAME_SIZE_KEY, histo.set_frame_size)
+               self.controller.publish(FRAME_SIZE_KEY, histo.get_frame_size)
+               #start input watcher
+               common.input_watcher(msgq, self.controller, MSG_KEY, 
arg1_key=MINIMUM_KEY, arg2_key=MAXIMUM_KEY)
+               #create window
+               self.win = histo_window.histo_window(
+                       parent=parent,
+                       controller=self.controller,
+                       size=size,
+                       title=title,
+                       maximum_key=MAXIMUM_KEY,
+                       minimum_key=MINIMUM_KEY,
+                       num_bins_key=NUM_BINS_KEY,
+                       frame_size_key=FRAME_SIZE_KEY,
+                       msg_key=MSG_KEY,
+               )
+               common.register_access_methods(self, self.win)
+
+# ----------------------------------------------------------------
+# Standalone test app
+# ----------------------------------------------------------------
+
+import wx
+from gnuradio.wxgui import stdgui2
+
+class test_app_block (stdgui2.std_top_block):
+    def __init__(self, frame, panel, vbox, argv):
+        stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
+
+        # build our flow graph
+        input_rate = 20.48e3
+
+        src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
+        #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+        thr2 = gr.throttle(gr.sizeof_float, input_rate)
+        sink2 = histo_sink_f (panel, title="Data", num_bins=31, 
frame_size=1000)
+        vbox.Add (sink2.win, 1, wx.EXPAND)
+
+        self.connect(src2, thr2, sink2)
+
+def main ():
+    app = stdgui2.stdapp (test_app_block, "Histo Sink Test App")
+    app.MainLoop ()
+
+if __name__ == '__main__':
+    main ()

Modified: gnuradio/trunk/gr-wxgui/src/python/number_window.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/number_window.py 2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/number_window.py 2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -53,23 +53,23 @@
                @param parent the wx parent window
                """
                self.parent = parent
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
                control_box = wx.BoxSizer(wx.VERTICAL)
                #checkboxes for average and peak hold
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
-               self.average_check_box = common.CheckBoxController(self, 
'Average', parent.ext_controller, parent.average_key)
-               control_box.Add(self.average_check_box, 0, wx.EXPAND)
                self.peak_hold_check_box = common.CheckBoxController(self, 
'Peak Hold', parent, PEAK_HOLD_KEY)
                control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
+               self.average_check_box = common.CheckBoxController(self, 
'Average', parent, AVERAGE_KEY)
+               control_box.Add(self.average_check_box, 0, wx.EXPAND)
                control_box.AddSpacer(2)
                self.avg_alpha_slider = common.LogSliderController(
                        self, 'Avg Alpha',
                        AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
-                       parent.ext_controller, parent.avg_alpha_key,
+                       parent, AVG_ALPHA_KEY,
                        formatter=lambda x: ': %.4f'%x,
                )
-               parent.ext_controller.subscribe(parent.average_key, 
self.avg_alpha_slider.Enable)
+               parent.subscribe(AVERAGE_KEY, self.avg_alpha_slider.Enable)
                control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
                #run/stop
                control_box.AddStretchSpacer()
@@ -81,7 +81,7 @@
 ##################################################
 # Numbersink window with label and gauges
 ##################################################
-class number_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+class number_window(wx.Panel, pubsub.pubsub):
        def __init__(
                self,
                parent,
@@ -98,20 +98,23 @@
                avg_alpha_key,
                peak_hold,
                msg_key,
+               sample_rate_key,
        ):
                pubsub.pubsub.__init__(self)
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
                #setup
                self.peak_val_real = NEG_INF
                self.peak_val_imag = NEG_INF
-               self.ext_controller = controller
                self.real = real
                self.units = units
                self.minval = minval
                self.maxval = maxval
                self.decimal_places = decimal_places
-               self.average_key = average_key
-               self.avg_alpha_key = avg_alpha_key
+               #proxy the keys
+               self.proxy(MSG_KEY, controller, msg_key)
+               self.proxy(AVERAGE_KEY, controller, average_key)
+               self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
+               self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
                #setup the box with display and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
@@ -128,13 +131,13 @@
                sizer.Add(self.gauge_real, 1, wx.EXPAND)
                sizer.Add(self.gauge_imag, 1, wx.EXPAND)
                self.SetSizerAndFit(main_box)
-               #initial setup
-               self.ext_controller[self.average_key] = 
self.ext_controller[self.average_key]
-               self.ext_controller[self.avg_alpha_key] = 
self.ext_controller[self.avg_alpha_key]
-               self._register_set_prop(self, PEAK_HOLD_KEY, peak_hold)
-               self._register_set_prop(self, RUNNING_KEY, True)
+               #initialize values
+               self[PEAK_HOLD_KEY] = peak_hold
+               self[RUNNING_KEY] = True
+               self[AVERAGE_KEY] = self[AVERAGE_KEY]
+               self[AVG_ALPHA_KEY] = self[AVG_ALPHA_KEY]
                #register events
-               self.ext_controller.subscribe(msg_key, self.handle_msg)
+               self.subscribe(MSG_KEY, self.handle_msg)
                self.Bind(common.EVT_DATA, self.update)
 
        def show_gauges(self, show_gauge):

Modified: gnuradio/trunk/gr-wxgui/src/python/numbersink2.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/numbersink2.py   2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/numbersink2.py   2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -31,7 +31,7 @@
 ##################################################
 # Number sink block (wrapper for old wxgui)
 ##################################################
-class _number_sink_base(gr.hier_block2, common.prop_setter):
+class _number_sink_base(gr.hier_block2):
        """
        An decimator block with a number window display
        """
@@ -74,29 +74,28 @@
                if self._real:
                        mult = gr.multiply_const_ff(factor)
                        add = gr.add_const_ff(ref_level)
-                       self._avg = gr.single_pole_iir_filter_ff(1.0)
+                       avg = gr.single_pole_iir_filter_ff(1.0)
                else:
                        mult = gr.multiply_const_cc(factor)
                        add = gr.add_const_cc(ref_level)
-                       self._avg = gr.single_pole_iir_filter_cc(1.0)
+                       avg = gr.single_pole_iir_filter_cc(1.0)
                msgq = gr.msg_queue(2)
                sink = gr.message_sink(self._item_size, msgq, True)
                #connect
-               self.connect(self, sd, mult, add, self._avg, sink)
-               #setup averaging
-               self._avg_alpha = avg_alpha
-               self.set_average(average)
-               self.set_avg_alpha(avg_alpha)
+               self.connect(self, sd, mult, add, avg, sink)
                #controller
                self.controller = pubsub()
                self.controller.subscribe(SAMPLE_RATE_KEY, sd.set_sample_rate)
-               self.controller.subscribe(AVERAGE_KEY, self.set_average)
-               self.controller.publish(AVERAGE_KEY, self.get_average)
-               self.controller.subscribe(AVG_ALPHA_KEY, self.set_avg_alpha)
-               self.controller.publish(AVG_ALPHA_KEY, self.get_avg_alpha)
+               self.controller.publish(SAMPLE_RATE_KEY, sd.sample_rate)
+               def update_avg(*args):
+                       if self.controller[AVERAGE_KEY]: 
avg.set_taps(self.controller[AVG_ALPHA_KEY])
+                       else: avg.set_taps(1.0)
+               self.controller.subscribe(AVERAGE_KEY, update_avg)
+               self.controller.subscribe(AVG_ALPHA_KEY, update_avg)
+               self.controller[AVERAGE_KEY] = average
+               self.controller[AVG_ALPHA_KEY] = avg_alpha
                #start input watcher
-               def set_msg(msg): self.controller[MSG_KEY] = msg
-               common.input_watcher(msgq, set_msg)
+               common.input_watcher(msgq, self.controller, MSG_KEY)
                #create window
                self.win = number_window.number_window(
                        parent=parent,
@@ -113,25 +112,12 @@
                        avg_alpha_key=AVG_ALPHA_KEY,
                        peak_hold=peak_hold,
                        msg_key=MSG_KEY,
+                       sample_rate_key=SAMPLE_RATE_KEY,
                )
-               #register callbacks from window for external use
-               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
-                       setattr(self, attr, getattr(self.win, attr))
-               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
+               common.register_access_methods(self, self.controller)
                #backwards compadibility
                self.set_show_gauge = self.win.show_gauges
 
-       def get_average(self): return self._average
-       def set_average(self, average):
-               self._average = average
-               if self.get_average(): self._avg.set_taps(self.get_avg_alpha())
-               else: self._avg.set_taps(1.0)
-
-       def get_avg_alpha(self): return self._avg_alpha
-       def set_avg_alpha(self, avg_alpha):
-               self._avg_alpha = avg_alpha
-               self.set_average(self.get_average())
-
 class number_sink_f(_number_sink_base):
        _item_size = gr.sizeof_float
        _real = True

Modified: gnuradio/trunk/gr-wxgui/src/python/plotter/Makefile.am
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/Makefile.am      2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/Makefile.am      2009-03-20 
02:16:20 UTC (rev 10660)
@@ -1,5 +1,5 @@
 #
-# Copyright 2004,2005,2008 Free Software Foundation, Inc.
+# Copyright 2004,2005,2008,2009 Free Software Foundation, Inc.
 # 
 # This file is part of GNU Radio
 # 
@@ -30,8 +30,11 @@
 
 ourpython_PYTHON =                     \
        __init__.py                     \
+       bar_plotter.py          \
        channel_plotter.py              \
+       common.py                       \
        gltext.py                       \
+       grid_plotter_base.py    \
        plotter_base.py                 \
        waterfall_plotter.py
 

Modified: gnuradio/trunk/gr-wxgui/src/python/plotter/__init__.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/__init__.py      2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/__init__.py      2009-03-20 
02:16:20 UTC (rev 10660)
@@ -21,3 +21,4 @@
 
 from channel_plotter import channel_plotter
 from waterfall_plotter import waterfall_plotter
+from bar_plotter import bar_plotter

Copied: gnuradio/trunk/gr-wxgui/src/python/plotter/bar_plotter.py (from rev 
10658, 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/bar_plotter.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/bar_plotter.py                   
        (rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/bar_plotter.py   2009-03-20 
02:16:20 UTC (rev 10660)
@@ -0,0 +1,144 @@
+#
+# Copyright 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import wx
+from grid_plotter_base import grid_plotter_base
+from OpenGL import GL
+import common
+import numpy
+
+LEGEND_TEXT_FONT_SIZE = 8
+LEGEND_BOX_PADDING = 3
+MIN_PADDING = 0, 0, 0, 70 #top, right, bottom, left
+#constants for the waveform storage
+SAMPLES_KEY = 'samples'
+COLOR_SPEC_KEY = 'color_spec'
+MARKERY_KEY = 'marker'
+TRIG_OFF_KEY = 'trig_off'
+
+##################################################
+# Bar Plotter for histogram waveforms
+##################################################
+class bar_plotter(grid_plotter_base):
+
+       def __init__(self, parent):
+               """
+               Create a new bar plotter.
+               """
+               #init
+               grid_plotter_base.__init__(self, parent, MIN_PADDING)
+               self._bars = list()
+               self._bar_width = .5
+               self._color_spec = (0, 0, 0)
+               #setup bar cache
+               self._bar_cache = self.new_gl_cache(self._draw_bars)
+               #setup bar plotter
+               self.register_init(self._init_bar_plotter)
+
+       def _init_bar_plotter(self):
+               """
+               Run gl initialization tasks.
+               """
+               GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
+
+       def _draw_bars(self):
+               """
+               Draw the vertical bars.
+               """
+               bars = self._bars
+               num_bars = len(bars)
+               if num_bars == 0: return
+               #use scissor to prevent drawing outside grid
+               GL.glEnable(GL.GL_SCISSOR_TEST)
+               GL.glScissor(
+                       self.padding_left,
+                       self.padding_bottom+1,
+                       self.width-self.padding_left-self.padding_right-1,
+                       self.height-self.padding_top-self.padding_bottom-1,
+               )
+               #load the points
+               points = list()
+               width = self._bar_width/2
+               for i, bar in enumerate(bars):
+                       points.extend([
+                                       (i-width, 0),
+                                       (i+width, 0),
+                                       (i+width, bar),
+                                       (i-width, bar),
+                               ]
+                       )
+               GL.glColor3f(*self._color_spec)
+               #matrix transforms
+               GL.glPushMatrix()
+               GL.glTranslatef(self.padding_left, self.padding_top, 0)
+               GL.glScalef(
+                       (self.width-self.padding_left-self.padding_right),
+                       (self.height-self.padding_top-self.padding_bottom),
+                       1,
+               )
+               GL.glTranslatef(0, 1, 0)
+               GL.glScalef(1.0/(num_bars-1), -1.0/(self.y_max-self.y_min), 1)
+               GL.glTranslatef(0, -self.y_min, 0)
+               #draw the bars
+               GL.glVertexPointerf(points)
+               GL.glDrawArrays(GL.GL_QUADS, 0, len(points))
+               GL.glPopMatrix()
+               GL.glDisable(GL.GL_SCISSOR_TEST)
+
+       def _populate_point_label(self, x_val, y_val):
+               """
+               Get the text the will populate the point label.
+               Give X and Y values for the current point.
+               Give values for the channel at the X coordinate.
+               @param x_val the current x value
+               @param y_val the current y value
+               @return a string with newlines
+               """
+               if len(self._bars) == 0: return ''
+               scalar = float(len(self._bars)-1)/(self.x_max - self.x_min)
+               #convert x val to bar #
+               bar_index = scalar*(x_val - self.x_min)
+               #if abs(bar_index - round(bar_index)) > self._bar_width/2: 
return ''
+               bar_index = int(round(bar_index))
+               bar_start = (bar_index - self._bar_width/2)/scalar + self.x_min
+               bar_end = (bar_index + self._bar_width/2)/scalar + self.x_min
+               bar_value = self._bars[bar_index]
+               return '%s to %s\n%s: %s'%(
+                       common.eng_format(bar_start, self.x_units),
+                       common.eng_format(bar_end, self.x_units),
+                       self.y_label, common.eng_format(bar_value, 
self.y_units),
+               )
+
+       def set_bars(self, bars, bar_width, color_spec):
+               """
+               Set the bars.
+               @param bars a list of bars
+               @param bar_width the fractional width of the bar, between 0 and 
1
+               @param color_spec the color tuple
+               """
+               self.lock()
+               self._bars = bars
+               self._bar_width = float(bar_width)
+               self._color_spec = color_spec
+               self._bar_cache.changed(True)
+               self.unlock()
+
+

Modified: gnuradio/trunk/gr-wxgui/src/python/plotter/channel_plotter.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/channel_plotter.py       
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/channel_plotter.py       
2009-03-20 02:16:20 UTC (rev 10660)
@@ -1,5 +1,5 @@
 #
-# Copyright 2008 Free Software Foundation, Inc.
+# Copyright 2008, 2009 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
@@ -20,20 +20,21 @@
 #
 
 import wx
-from plotter_base import grid_plotter_base
-from OpenGL.GL import *
-from gnuradio.wxgui import common
+from grid_plotter_base import grid_plotter_base
+from OpenGL import GL
+import common
 import numpy
 import gltext
 import math
 
 LEGEND_TEXT_FONT_SIZE = 8
 LEGEND_BOX_PADDING = 3
-PADDING = 35, 15, 40, 60 #top, right, bottom, left
+MIN_PADDING = 35, 10, 0, 0 #top, right, bottom, left
 #constants for the waveform storage
 SAMPLES_KEY = 'samples'
 COLOR_SPEC_KEY = 'color_spec'
 MARKERY_KEY = 'marker'
+TRIG_OFF_KEY = 'trig_off'
 
 ##################################################
 # Channel Plotter for X Y Waveforms
@@ -45,16 +46,21 @@
                Create a new channel plotter.
                """
                #init
-               grid_plotter_base.__init__(self, parent, PADDING)
+               grid_plotter_base.__init__(self, parent, MIN_PADDING)
+               #setup legend cache
+               self._legend_cache = self.new_gl_cache(self._draw_legend, 50)
+               self.enable_legend(False)
+               #setup waveform cache
+               self._waveform_cache = self.new_gl_cache(self._draw_waveforms, 
50)
                self._channels = dict()
-               self.enable_legend(False)
+               #init channel plotter
+               self.register_init(self._init_channel_plotter)
 
-       def _gl_init(self):
+       def _init_channel_plotter(self):
                """
                Run gl initialization tasks.
                """
-               glEnableClientState(GL_VERTEX_ARRAY)
-               self._grid_compiled_list_id = glGenLists(1)
+               GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
 
        def enable_legend(self, enable=None):
                """
@@ -65,73 +71,55 @@
                if enable is None: return self._enable_legend
                self.lock()
                self._enable_legend = enable
-               self.changed(True)
+               self._legend_cache.changed(True)
                self.unlock()
 
-       def draw(self):
+       def _draw_waveforms(self):
                """
-               Draw the grid and waveforms.
+               Draw the waveforms for each channel.
+               Scale the waveform data to the grid using gl matrix operations.
                """
-               self.lock()
-               self.clear()
-               #store the grid drawing operations
-               if self.changed():
-                       glNewList(self._grid_compiled_list_id, GL_COMPILE)
-                       self._draw_grid()
-                       self._draw_legend()
-                       glEndList()
-                       self.changed(False)
-               #draw the grid
-               glCallList(self._grid_compiled_list_id)
                #use scissor to prevent drawing outside grid
-               glEnable(GL_SCISSOR_TEST)
-               glScissor(
+               GL.glEnable(GL.GL_SCISSOR_TEST)
+               GL.glScissor(
                        self.padding_left+1,
                        self.padding_bottom+1,
                        self.width-self.padding_left-self.padding_right-1,
                        self.height-self.padding_top-self.padding_bottom-1,
                )
-               #draw the waveforms
-               self._draw_waveforms()
-               glDisable(GL_SCISSOR_TEST)
-               self._draw_point_label()
-               #swap buffer into display
-               self.SwapBuffers()
-               self.unlock()
-
-       def _draw_waveforms(self):
-               """
-               Draw the waveforms for each channel.
-               Scale the waveform data to the grid using gl matrix operations.
-               """
                for channel in reversed(sorted(self._channels.keys())):
                        samples = self._channels[channel][SAMPLES_KEY]
                        num_samps = len(samples)
                        if not num_samps: continue
                        #use opengl to scale the waveform
-                       glPushMatrix()
-                       glTranslatef(self.padding_left, self.padding_top, 0)
-                       glScalef(
+                       GL.glPushMatrix()
+                       GL.glTranslatef(self.padding_left, self.padding_top, 0)
+                       GL.glScalef(
                                
(self.width-self.padding_left-self.padding_right),
                                
(self.height-self.padding_top-self.padding_bottom),
                                1,
                        )
-                       glTranslatef(0, 1, 0)
+                       GL.glTranslatef(0, 1, 0)
                        if isinstance(samples, tuple):
                                x_scale, x_trans = 1.0/(self.x_max-self.x_min), 
-self.x_min
                                points = zip(*samples)
                        else:
-                               x_scale, x_trans = 1.0/(num_samps-1), 0
+                               x_scale, x_trans = 1.0/(num_samps-1), 
-self._channels[channel][TRIG_OFF_KEY]
                                points = zip(numpy.arange(0, num_samps), 
samples)
-                       glScalef(x_scale, -1.0/(self.y_max-self.y_min), 1)
-                       glTranslatef(x_trans, -self.y_min, 0)
+                       GL.glScalef(x_scale, -1.0/(self.y_max-self.y_min), 1)
+                       GL.glTranslatef(x_trans, -self.y_min, 0)
                        #draw the points/lines
-                       glColor3f(*self._channels[channel][COLOR_SPEC_KEY])
+                       GL.glColor3f(*self._channels[channel][COLOR_SPEC_KEY])
                        marker = self._channels[channel][MARKERY_KEY]
-                       if marker: glPointSize(marker)
-                       glVertexPointerf(points)
-                       glDrawArrays(marker is None and GL_LINE_STRIP or 
GL_POINTS, 0, len(points))
-                       glPopMatrix()
+                       if marker is None:
+                               GL.glVertexPointerf(points)
+                               GL.glDrawArrays(GL.GL_LINE_STRIP, 0, 
len(points))
+                       elif isinstance(marker, (int, float)) and marker > 0:
+                               GL.glPointSize(marker)
+                               GL.glVertexPointerf(points)
+                               GL.glDrawArrays(GL.GL_POINTS, 0, len(points))
+                       GL.glPopMatrix()
+               GL.glDisable(GL.GL_SCISSOR_TEST)
 
        def _populate_point_label(self, x_val, y_val):
                """
@@ -143,12 +131,9 @@
                @return a string with newlines
                """
                #create text
-               label_str = '%s: %s %s\n%s: %s %s'%(
-                       self.x_label,
-                       common.label_format(x_val),
-                       self.x_units, self.y_label,
-                       common.label_format(y_val),
-                       self.y_units,
+               label_str = '%s: %s\n%s: %s'%(
+                       self.x_label, common.eng_format(x_val, self.x_units),
+                       self.y_label, common.eng_format(y_val, self.y_units),
                )
                for channel in sorted(self._channels.keys()):
                        samples = self._channels[channel][SAMPLES_KEY]
@@ -156,11 +141,12 @@
                        if not num_samps: continue
                        if isinstance(samples, tuple): continue
                        #linear interpolation
-                       x_index = 
(num_samps-1)*(x_val/self.x_scalar-self.x_min)/(self.x_max-self.x_min)
+                       x_index = 
(num_samps-1)*(x_val-self.x_min)/(self.x_max-self.x_min)
                        x_index_low = int(math.floor(x_index))
                        x_index_high = int(math.ceil(x_index))
-                       y_value = (samples[x_index_high] - 
samples[x_index_low])*(x_index - x_index_low) + samples[x_index_low]
-                       label_str += '\n%s: %s %s'%(channel, 
common.label_format(y_value), self.y_units)
+                       scale = x_index - x_index_low + 
self._channels[channel][TRIG_OFF_KEY]
+                       y_value = (samples[x_index_high] - 
samples[x_index_low])*scale + samples[x_index_low]
+                       label_str += '\n%s: %s'%(channel, 
common.eng_format(y_value, self.y_units))
                return label_str
 
        def _draw_legend(self):
@@ -178,7 +164,7 @@
                        txt = gltext.Text(channel, 
font_size=LEGEND_TEXT_FONT_SIZE)
                        w, h = txt.get_size()
                        #draw rect + text
-                       glColor3f(*color_spec)
+                       GL.glColor3f(*color_spec)
                        self._draw_rect(
                                x_off - w - LEGEND_BOX_PADDING,
                                self.padding_top/2 - h/2 - LEGEND_BOX_PADDING,
@@ -188,21 +174,36 @@
                        txt.draw_text(wx.Point(x_off - w, self.padding_top/2 - 
h/2))
                        x_off -= w + 4*LEGEND_BOX_PADDING
 
-       def set_waveform(self, channel, samples, color_spec, marker=None):
+       def clear_waveform(self, channel):
                """
+               Remove a waveform from the list of waveforms.
+               @param channel the channel key
+               """
+               self.lock()
+               if channel in self._channels.keys():
+                       self._channels.pop(channel)
+                       self._legend_cache.changed(True)
+                       self._waveform_cache.changed(True)
+               self.unlock()
+
+       def set_waveform(self, channel, samples=[], color_spec=(0, 0, 0), 
marker=None, trig_off=0):
+               """
                Set the waveform for a given channel.
                @param channel the channel key
                @param samples the waveform samples
                @param color_spec the 3-tuple for line color
                @param marker None for line
+               @param trig_off fraction of sample for trigger offset
                """
                self.lock()
-               if channel not in self._channels.keys(): self.changed(True)
+               if channel not in self._channels.keys(): 
self._legend_cache.changed(True)
                self._channels[channel] = {
                        SAMPLES_KEY: samples,
                        COLOR_SPEC_KEY: color_spec,
                        MARKERY_KEY: marker,
+                       TRIG_OFF_KEY: trig_off,
                }
+               self._waveform_cache.changed(True)
                self.unlock()
 
 if __name__ == '__main__':

Copied: gnuradio/trunk/gr-wxgui/src/python/plotter/common.py (from rev 10658, 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/common.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/common.py                        
        (rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/common.py        2009-03-20 
02:16:20 UTC (rev 10660)
@@ -0,0 +1,131 @@
+#
+# Copyright 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import threading
+import time
+import math
+import wx
+
+##################################################
+# Number formatting
+##################################################
+def get_exp(num):
+       """
+       Get the exponent of the number in base 10.
+       @param num the floating point number
+       @return the exponent as an integer
+       """
+       if num == 0: return 0
+       return int(math.floor(math.log10(abs(num))))
+
+def get_si_components(num):
+       """
+       Get the SI units for the number.
+       Extract the coeff and exponent of the number.
+       The exponent will be a multiple of 3.
+       @param num the floating point number
+       @return the tuple coeff, exp, prefix
+       """
+       num = float(num)
+       exp = get_exp(num)
+       exp -= exp%3
+       exp = min(max(exp, -24), 24) #bounds on SI table below
+       prefix = {
+               24: 'Y', 21: 'Z',
+               18: 'E', 15: 'P',
+               12: 'T', 9: 'G',
+               6: 'M', 3: 'k',
+               0: '',
+               -3: 'm', -6: 'u',
+               -9: 'n', -12: 'p',
+               -15: 'f', -18: 'a',
+               -21: 'z', -24: 'y',
+       }[exp]
+       coeff = num/10**exp
+       return coeff, exp, prefix
+
+def sci_format(num):
+       """
+       Format a floating point number into scientific notation.
+       @param num the number to format
+       @return a label string
+       """
+       coeff, exp, prefix = get_si_components(num)
+       if -3 <= exp < 3: return '%g'%num
+       return '%.3ge%d'%(coeff, exp)
+
+def eng_format(num, units=''):
+       """
+       Format a floating point number into engineering notation.
+       @param num the number to format
+       @param units the units to append
+       @return a label string
+       """
+       coeff, exp, prefix = get_si_components(num)
+       if -3 <= exp < 3: return '%g'%num
+       return '%g%s%s%s'%(coeff, units and ' ' or '', prefix, units)
+
+##################################################
+# Interface with thread safe lock/unlock
+##################################################
+class mutex(object):
+       _lock = threading.Lock()
+       def lock(self): self._lock.acquire()
+       def unlock(self): self._lock.release()
+
+##################################################
+# Periodic update thread for point label
+##################################################
+class point_label_thread(threading.Thread, mutex):
+
+       def __init__(self, plotter):
+               self._plotter = plotter
+               self._coor_queue = list()
+               #bind plotter mouse events
+               self._plotter.Bind(wx.EVT_MOTION, lambda evt: 
self.enqueue(evt.GetPosition()))
+               self._plotter.Bind(wx.EVT_LEAVE_WINDOW, lambda evt: 
self.enqueue(None))
+               #start the thread
+               threading.Thread.__init__(self)
+               self.start()
+
+       def enqueue(self, coor):
+               self.lock()
+               self._coor_queue.append(coor)
+               self.unlock()
+
+       def run(self):
+               last_ts = time.time()
+               last_coor = coor = None
+               try: 
+                       while True:
+                               time.sleep(1.0/30.0)
+                               self.lock()
+                               #get most recent coor change
+                               if self._coor_queue:
+                                       coor = self._coor_queue[-1]
+                                       self._coor_queue = list()
+                               self.unlock()
+                               #update if coor change, or enough time expired
+                               if last_coor != coor or (time.time() - last_ts) 
> (1.0/2.0):
+                                       
self._plotter.set_point_label_coordinate(coor)
+                                       last_coor = coor
+                                       last_ts = time.time()
+               except wx.PyDeadObjectError: pass

Copied: gnuradio/trunk/gr-wxgui/src/python/plotter/grid_plotter_base.py (from 
rev 10658, 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/grid_plotter_base.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/grid_plotter_base.py             
                (rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/grid_plotter_base.py     
2009-03-20 02:16:20 UTC (rev 10660)
@@ -0,0 +1,370 @@
+#
+# Copyright 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import wx
+import wx.glcanvas
+from OpenGL import GL
+import common
+from plotter_base import plotter_base
+import gltext
+import math
+
+GRID_LINE_COLOR_SPEC = (.7, .7, .7) #gray
+GRID_BORDER_COLOR_SPEC = (0, 0, 0) #black
+TICK_TEXT_FONT_SIZE = 9
+TITLE_TEXT_FONT_SIZE = 13
+UNITS_TEXT_FONT_SIZE = 9
+AXIS_LABEL_PADDING = 5
+TICK_LABEL_PADDING = 5
+TITLE_LABEL_PADDING = 7
+POINT_LABEL_FONT_SIZE = 8
+POINT_LABEL_COLOR_SPEC = (1, 1, .5)
+POINT_LABEL_PADDING = 3
+GRID_LINE_DASH_LEN = 4
+
+##################################################
+# Grid Plotter Base Class
+##################################################
+class grid_plotter_base(plotter_base):
+
+       def __init__(self, parent, min_padding=(0, 0, 0, 0)):
+               plotter_base.__init__(self, parent)
+               #setup grid cache
+               self._grid_cache = self.new_gl_cache(self._draw_grid, 25)
+               self.enable_grid_lines(True)
+               #setup padding
+               self.padding_top_min, self.padding_right_min, 
self.padding_bottom_min, self.padding_left_min = min_padding
+               #store title and unit strings
+               self.set_title('Title')
+               self.set_x_label('X Label')
+               self.set_y_label('Y Label')
+               #init the grid to some value
+               self.set_x_grid(-1, 1, 1)
+               self.set_y_grid(-1, 1, 1)
+               #setup point label cache
+               self._point_label_cache = 
self.new_gl_cache(self._draw_point_label, 75)
+               self.enable_point_label(False)
+               self.set_point_label_coordinate(None)
+               common.point_label_thread(self)
+               #init grid plotter
+               self.register_init(self._init_grid_plotter)
+
+       def _init_grid_plotter(self):
+               """
+               Run gl initialization tasks.
+               """
+               GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
+
+       def set_point_label_coordinate(self, coor):
+               """
+               Set the point label coordinate.
+               @param coor the coordinate x, y tuple or None 
+               """
+               self.lock()
+               self._point_label_coordinate = coor
+               self._point_label_cache.changed(True)
+               self.update()
+               self.unlock()
+
+       def enable_point_label(self, enable=None):
+               """
+               Enable/disable the point label.
+               @param enable true to enable
+               @return the enable state when None
+               """
+               if enable is None: return self._enable_point_label
+               self.lock()
+               self._enable_point_label = enable
+               self._point_label_cache.changed(True)
+               self.unlock()
+
+       def set_title(self, title):
+               """
+               Set the title.
+               @param title the title string
+               """
+               self.lock()
+               self.title = title
+               self._grid_cache.changed(True)
+               self.unlock()
+
+       def set_x_label(self, x_label, x_units=''):
+               """
+               Set the x label and units.
+               @param x_label the x label string
+               @param x_units the x units string
+               """
+               self.lock()
+               self.x_label = x_label
+               self.x_units = x_units
+               self._grid_cache.changed(True)
+               self.unlock()
+
+       def set_y_label(self, y_label, y_units=''):
+               """
+               Set the y label and units.
+               @param y_label the y label string
+               @param y_units the y units string
+               """
+               self.lock()
+               self.y_label = y_label
+               self.y_units = y_units
+               self._grid_cache.changed(True)
+               self.unlock()
+
+       def set_x_grid(self, minimum, maximum, step, scale=False):
+               """
+               Set the x grid parameters.
+               @param minimum the left-most value
+               @param maximum the right-most value
+               @param step the grid spacing
+               @param scale true to scale the x grid
+               """
+               self.lock()
+               self.x_min = float(minimum)
+               self.x_max = float(maximum)
+               self.x_step = float(step)
+               if scale:
+                       coeff, exp, prefix = 
common.get_si_components(max(abs(self.x_min), abs(self.x_max)))
+                       self.x_scalar = 10**(-exp)
+                       self.x_prefix = prefix
+               else:
+                       self.x_scalar = 1.0
+                       self.x_prefix = ''
+               for cache in self._gl_caches: cache.changed(True)
+               self.unlock()
+
+       def set_y_grid(self, minimum, maximum, step, scale=False):
+               """
+               Set the y grid parameters.
+               @param minimum the bottom-most value
+               @param maximum the top-most value
+               @param step the grid spacing
+               @param scale true to scale the y grid
+               """
+               self.lock()
+               self.y_min = float(minimum)
+               self.y_max = float(maximum)
+               self.y_step = float(step)
+               if scale:
+                       coeff, exp, prefix = 
common.get_si_components(max(abs(self.y_min), abs(self.y_max)))
+                       self.y_scalar = 10**(-exp)
+                       self.y_prefix = prefix
+               else:
+                       self.y_scalar = 1.0
+                       self.y_prefix = ''
+               for cache in self._gl_caches: cache.changed(True)
+               self.unlock()
+
+       def _draw_grid(self):
+               """
+               Create the x, y, tick, and title labels.
+               Resize the padding for the labels.
+               Draw the border, grid, title, and labels.
+               """
+               ##################################################
+               # Create GL text labels
+               ##################################################
+               #create x tick labels
+               x_tick_labels = [(tick, self._get_tick_label(tick, 
self.x_units))
+                       for tick in self._get_ticks(self.x_min, self.x_max, 
self.x_step, self.x_scalar)]
+               #create x tick labels
+               y_tick_labels = [(tick, self._get_tick_label(tick, 
self.y_units))
+                       for tick in self._get_ticks(self.y_min, self.y_max, 
self.y_step, self.y_scalar)]
+               #create x label
+               x_label_str = self.x_units and "%s (%s%s)"%(self.x_label, 
self.x_prefix, self.x_units) or self.x_label
+               x_label = gltext.Text(x_label_str, bold=True, 
font_size=UNITS_TEXT_FONT_SIZE, centered=True)
+               #create y label
+               y_label_str = self.y_units and "%s (%s%s)"%(self.y_label, 
self.y_prefix, self.y_units) or self.y_label
+               y_label = gltext.Text(y_label_str, bold=True, 
font_size=UNITS_TEXT_FONT_SIZE, centered=True)
+               #create title
+               title_label = gltext.Text(self.title, bold=True, 
font_size=TITLE_TEXT_FONT_SIZE, centered=True)
+               ##################################################
+               # Resize the padding
+               ##################################################
+               self.padding_top = max(2*TITLE_LABEL_PADDING + 
title_label.get_size()[1], self.padding_top_min)
+               self.padding_right = max(2*TICK_LABEL_PADDING, 
self.padding_right_min)
+               self.padding_bottom = max(2*AXIS_LABEL_PADDING + 
TICK_LABEL_PADDING + x_label.get_size()[1] + max([label.get_size()[1] for tick, 
label in x_tick_labels]), self.padding_bottom_min)
+               self.padding_left = max(2*AXIS_LABEL_PADDING + 
TICK_LABEL_PADDING + y_label.get_size()[1] + max([label.get_size()[0] for tick, 
label in y_tick_labels]), self.padding_left_min)
+               ##################################################
+               # Draw Grid X
+               ##################################################
+               for tick, label in x_tick_labels:
+                       scaled_tick = 
(self.width-self.padding_left-self.padding_right)*\
+                               
(tick/self.x_scalar-self.x_min)/(self.x_max-self.x_min) + self.padding_left
+                       self._draw_grid_line(
+                               (scaled_tick, self.padding_top),
+                               (scaled_tick, self.height-self.padding_bottom),
+                       )
+                       w, h = label.get_size()
+                       label.draw_text(wx.Point(scaled_tick-w/2, 
self.height-self.padding_bottom+TICK_LABEL_PADDING))
+               ##################################################
+               # Draw Grid Y
+               ##################################################
+               for tick, label in y_tick_labels:
+                       scaled_tick = 
(self.height-self.padding_top-self.padding_bottom)*\
+                               (1 - 
(tick/self.y_scalar-self.y_min)/(self.y_max-self.y_min)) + self.padding_top
+                       self._draw_grid_line(
+                               (self.padding_left, scaled_tick),
+                               (self.width-self.padding_right, scaled_tick),
+                       )
+                       w, h = label.get_size()
+                       
label.draw_text(wx.Point(self.padding_left-w-TICK_LABEL_PADDING, 
scaled_tick-h/2))
+               ##################################################
+               # Draw Border
+               ##################################################
+               GL.glColor3f(*GRID_BORDER_COLOR_SPEC)
+               self._draw_rect(
+                       self.padding_left,
+                       self.padding_top,
+                       self.width - self.padding_right - self.padding_left,
+                       self.height - self.padding_top - self.padding_bottom,
+                       fill=False,
+               )
+               ##################################################
+               # Draw Labels
+               ##################################################
+               #draw title label
+               title_label.draw_text(wx.Point(self.width/2.0, 
TITLE_LABEL_PADDING + title_label.get_size()[1]/2))
+               #draw x labels
+               x_label.draw_text(wx.Point(
+                               
(self.width-self.padding_left-self.padding_right)/2.0 + self.padding_left,
+                               self.height-(AXIS_LABEL_PADDING + 
x_label.get_size()[1]/2),
+                               )
+               )
+               #draw y labels
+               y_label.draw_text(wx.Point(
+                               AXIS_LABEL_PADDING + y_label.get_size()[1]/2,
+                               
(self.height-self.padding_top-self.padding_bottom)/2.0 + self.padding_top,
+                       ), rotation=90,
+               )
+
+       def _get_tick_label(self, tick, unit):
+               """
+               Format the tick value and create a gl text.
+               @param tick the floating point tick value
+               @param unit the axis unit
+               @return the tick label text
+               """
+               if unit: tick_str = common.sci_format(tick)
+               else: tick_str = common.eng_format(tick)
+               return gltext.Text(tick_str, font_size=TICK_TEXT_FONT_SIZE)
+
+       def _get_ticks(self, min, max, step, scalar):
+               """
+               Determine the positions for the ticks.
+               @param min the lower bound
+               @param max the upper bound
+               @param step the grid spacing
+               @param scalar the grid scaling
+               @return a list of tick positions between min and max
+               """
+               #cast to float
+               min = float(min)
+               max = float(max)
+               step = float(step)
+               #check for valid numbers
+               try:
+                       assert step > 0
+                       assert max > min
+                       assert max - min > step
+               except AssertionError: return [-1, 1]
+               #determine the start and stop value
+               start = int(math.ceil(min/step))
+               stop = int(math.floor(max/step))
+               return [i*step*scalar for i in range(start, stop+1)]
+
+       def enable_grid_lines(self, enable=None):
+               """
+               Enable/disable the grid lines.
+               @param enable true to enable
+               @return the enable state when None
+               """
+               if enable is None: return self._enable_grid_lines
+               self.lock()
+               self._enable_grid_lines = enable
+               self._grid_cache.changed(True)
+               self.unlock()
+
+       def _draw_grid_line(self, coor1, coor2):
+               """
+               Draw a dashed line from coor1 to coor2.
+               @param corr1 a tuple of x, y
+               @param corr2 a tuple of x, y
+               """
+               if not self.enable_grid_lines(): return
+               length = math.sqrt((coor1[0] - coor2[0])**2 + (coor1[1] - 
coor2[1])**2)
+               num_points = int(length/GRID_LINE_DASH_LEN)
+               #calculate points array
+               points = [(
+                       coor1[0] + i*(coor2[0]-coor1[0])/(num_points - 1),
+                       coor1[1] + i*(coor2[1]-coor1[1])/(num_points - 1)
+               ) for i in range(num_points)]
+               #set color and draw
+               GL.glColor3f(*GRID_LINE_COLOR_SPEC)
+               GL.glVertexPointerf(points)
+               GL.glDrawArrays(GL.GL_LINES, 0, len(points))
+
+       def _draw_rect(self, x, y, width, height, fill=True):
+               """
+               Draw a rectangle on the x, y plane.
+               X and Y are the top-left corner.
+               @param x the left position of the rectangle
+               @param y the top position of the rectangle
+               @param width the width of the rectangle
+               @param height the height of the rectangle
+               @param fill true to color inside of rectangle
+               """
+               GL.glBegin(fill and GL.GL_QUADS or GL.GL_LINE_LOOP)
+               GL.glVertex2f(x, y)
+               GL.glVertex2f(x+width, y)
+               GL.glVertex2f(x+width, y+height)
+               GL.glVertex2f(x, y+height)
+               GL.glEnd()
+
+       def _draw_point_label(self):
+               """
+               Draw the point label for the last mouse motion coordinate.
+               The mouse coordinate must be an X, Y tuple.
+               The label will be drawn at the X, Y coordinate.
+               The values of the X, Y coordinate will be scaled to the current 
X, Y bounds.
+               """
+               if not self.enable_point_label(): return
+               if not self._point_label_coordinate: return
+               x, y = self._point_label_coordinate
+               if x < self.padding_left or x > self.width-self.padding_right: 
return
+               if y < self.padding_top or y > self.height-self.padding_bottom: 
return
+               #scale to window bounds
+               x_win_scalar = float(x - 
self.padding_left)/(self.width-self.padding_left-self.padding_right)
+               y_win_scalar = float((self.height - y) - 
self.padding_bottom)/(self.height-self.padding_top-self.padding_bottom)
+               #scale to grid bounds
+               x_val = x_win_scalar*(self.x_max-self.x_min) + self.x_min
+               y_val = y_win_scalar*(self.y_max-self.y_min) + self.y_min
+               #create text
+               label_str = self._populate_point_label(x_val, y_val)
+               if not label_str: return
+               txt = gltext.Text(label_str, font_size=POINT_LABEL_FONT_SIZE)
+               w, h = txt.get_size()
+               #draw rect + text
+               GL.glColor3f(*POINT_LABEL_COLOR_SPEC)
+               if x > self.width/2: x -= w+2*POINT_LABEL_PADDING
+               self._draw_rect(x, y-h-2*POINT_LABEL_PADDING, 
w+2*POINT_LABEL_PADDING, h+2*POINT_LABEL_PADDING)
+               txt.draw_text(wx.Point(x+POINT_LABEL_PADDING, 
y-h-POINT_LABEL_PADDING))

Modified: gnuradio/trunk/gr-wxgui/src/python/plotter/plotter_base.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/plotter_base.py  2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/plotter_base.py  2009-03-20 
02:16:20 UTC (rev 10660)
@@ -1,5 +1,5 @@
 #
-# Copyright 2008 Free Software Foundation, Inc.
+# Copyright 2008, 2009 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
@@ -21,27 +21,59 @@
 
 import wx
 import wx.glcanvas
-from OpenGL.GL import *
-from gnuradio.wxgui import common
-import threading
-import gltext
-import math
-import time
+from OpenGL import GL
+import common
 
 BACKGROUND_COLOR_SPEC = (1, 0.976, 1, 1) #creamy white
-GRID_LINE_COLOR_SPEC = (0, 0, 0) #black
-TICK_TEXT_FONT_SIZE = 9
-TITLE_TEXT_FONT_SIZE = 13
-UNITS_TEXT_FONT_SIZE = 9
-TICK_LABEL_PADDING = 5
-POINT_LABEL_FONT_SIZE = 8
-POINT_LABEL_COLOR_SPEC = (1, 1, .5)
-POINT_LABEL_PADDING = 3
 
 ##################################################
+# GL caching interface
+##################################################
+class gl_cache(object):
+       """
+       Cache a set of gl drawing routines in a compiled list.
+       """
+
+       def __init__(self, draw):
+               """
+               Create a new cache.
+               @param draw a function to draw gl stuff
+               """
+               self.changed(True)
+               self._draw = draw
+
+       def init(self):
+               """
+               To be called when gl initializes.
+               Create a new compiled list.
+               """
+               self._grid_compiled_list_id = GL.glGenLists(1)
+
+       def draw(self):
+               """
+               Draw the gl stuff using a compiled list.
+               If changed, reload the compiled list.
+               """
+               if self.changed():
+                       GL.glNewList(self._grid_compiled_list_id, GL.GL_COMPILE)
+                       self._draw()
+                       GL.glEndList()
+                       self.changed(False)
+               #draw the grid
+               GL.glCallList(self._grid_compiled_list_id)
+
+       def changed(self, state=None):
+               """
+               Set the changed flag if state is not None.
+               Otherwise return the changed flag.
+               """
+               if state is None: return self._changed
+               self._changed = state
+
+##################################################
 # OpenGL WX Plotter Canvas
 ##################################################
-class _plotter_base(wx.glcanvas.GLCanvas):
+class plotter_base(wx.glcanvas.GLCanvas, common.mutex):
        """
        Plotter base class for all plot types.
        """
@@ -54,340 +86,86 @@
                Bind the paint and size events.
                @param parent the parent widgit
                """
-               self._global_lock = threading.Lock()
                attribList = (wx.glcanvas.WX_GL_DOUBLEBUFFER, 
wx.glcanvas.WX_GL_RGBA)
                wx.glcanvas.GLCanvas.__init__(self, parent, 
attribList=attribList)
-               self.changed(False)
                self._gl_init_flag = False
                self._resized_flag = True
-               self._update_ts = 0
+               self._init_fcns = list()
+               self._draw_fcns = list()
+               self._gl_caches = list()
                self.Bind(wx.EVT_PAINT, self._on_paint)
                self.Bind(wx.EVT_SIZE, self._on_size)
+               self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
 
-       def lock(self): self._global_lock.acquire()
-       def unlock(self): self._global_lock.release()
+       def new_gl_cache(self, draw_fcn, draw_pri=50):
+               """
+               Create a new gl cache.
+               Register its draw and init function.
+               @return the new cache object
+               """
+               cache = gl_cache(draw_fcn)
+               self.register_init(cache.init)
+               self.register_draw(cache.draw, draw_pri)
+               self._gl_caches.append(cache)
+               return cache
 
+       def register_init(self, init_fcn):
+               self._init_fcns.append(init_fcn)
+
+       def register_draw(self, draw_fcn, draw_pri=50):
+               """
+               Register a draw function with a layer priority.
+               Large pri values are drawn last.
+               Small pri values are drawn first.
+               """
+               for i in range(len(self._draw_fcns)):
+                       if draw_pri < self._draw_fcns[i][0]:
+                               self._draw_fcns.insert(i, (draw_pri, draw_fcn))
+                               return
+               self._draw_fcns.append((draw_pri, draw_fcn))
+
        def _on_size(self, event):
                """
                Flag the resize event.
                The paint event will handle the actual resizing.
                """
+               self.lock()
                self._resized_flag = True
+               self.unlock()
 
        def _on_paint(self, event):
                """
-               Respond to paint events, call update.
+               Respond to paint events.
                Initialize GL if this is the first paint event.
+               Resize the view port if the width or height changed.
+               Redraw the screen, calling the draw functions.
                """
+               self.lock()
                self.SetCurrent()
                #check if gl was initialized
                if not self._gl_init_flag:
-                       glClearColor(*BACKGROUND_COLOR_SPEC)
-                       self._gl_init()
+                       GL.glClearColor(*BACKGROUND_COLOR_SPEC)
+                       for fcn in self._init_fcns: fcn()
                        self._gl_init_flag = True
                #check for a change in window size
                if self._resized_flag:
-                       self.lock()
                        self.width, self.height = self.GetSize()
-                       glMatrixMode(GL_PROJECTION)
-                       glLoadIdentity()
-                       glOrtho(0, self.width, self.height, 0, 1, 0)
-                       glMatrixMode(GL_MODELVIEW)
-                       glLoadIdentity()
-                       glViewport(0, 0, self.width, self.height)
+                       GL.glMatrixMode(GL.GL_PROJECTION)
+                       GL.glLoadIdentity()
+                       GL.glOrtho(0, self.width, self.height, 0, 1, 0)
+                       GL.glMatrixMode(GL.GL_MODELVIEW)
+                       GL.glLoadIdentity()
+                       GL.glViewport(0, 0, self.width, self.height)
+                       for cache in self._gl_caches: cache.changed(True)
                        self._resized_flag = False
-                       self.changed(True)
-                       self.unlock()
-               self.draw()
+               #clear, draw functions, swap
+               GL.glClear(GL.GL_COLOR_BUFFER_BIT)
+               for fcn in self._draw_fcns: fcn[1]()
+               self.SwapBuffers()
+               self.unlock()
 
        def update(self):
                """
                Force a paint event.
-               Record the timestamp.
                """
                wx.PostEvent(self, wx.PaintEvent())
-               self._update_ts = time.time()
-
-       def clear(self): glClear(GL_COLOR_BUFFER_BIT)
-
-       def changed(self, state=None):
-               """
-               Set the changed flag if state is not None.
-               Otherwise return the changed flag.
-               """
-               if state is not None: self._changed = state
-               else: return self._changed
-
-##################################################
-# Grid Plotter Base Class
-##################################################
-class grid_plotter_base(_plotter_base):
-
-       def __init__(self, parent, padding):
-               _plotter_base.__init__(self, parent)
-               self.padding_top, self.padding_right, self.padding_bottom, 
self.padding_left = padding
-               #store title and unit strings
-               self.set_title('Title')
-               self.set_x_label('X Label')
-               self.set_y_label('Y Label')
-               #init the grid to some value
-               self.set_x_grid(-1, 1, 1)
-               self.set_y_grid(-1, 1, 1)
-               #setup point label
-               self.enable_point_label(False)
-               self._mouse_coordinate = None
-               self.Bind(wx.EVT_MOTION, self._on_motion)
-               self.Bind(wx.EVT_LEAVE_WINDOW, self._on_leave_window)
-
-       def _on_motion(self, event):
-               """
-               Mouse motion, record the position X, Y.
-               """
-               self.lock()
-               self._mouse_coordinate = event.GetPosition()
-               #update based on last known update time
-               if time.time() - self._update_ts > 0.03: self.update()
-               self.unlock()
-
-       def _on_leave_window(self, event):
-               """
-               Mouse leave window, set the position to None.
-               """
-               self.lock()
-               self._mouse_coordinate = None
-               self.update()
-               self.unlock()
-
-       def enable_point_label(self, enable=None):
-               """
-               Enable/disable the point label.
-               @param enable true to enable
-               @return the enable state when None
-               """
-               if enable is None: return self._enable_point_label
-               self.lock()
-               self._enable_point_label = enable
-               self.changed(True)
-               self.unlock()
-
-       def set_title(self, title):
-               """
-               Set the title.
-               @param title the title string
-               """
-               self.lock()
-               self.title = title
-               self.changed(True)
-               self.unlock()
-
-       def set_x_label(self, x_label, x_units=''):
-               """
-               Set the x label and units.
-               @param x_label the x label string
-               @param x_units the x units string
-               """
-               self.lock()
-               self.x_label = x_label
-               self.x_units = x_units
-               self.changed(True)
-               self.unlock()
-
-       def set_y_label(self, y_label, y_units=''):
-               """
-               Set the y label and units.
-               @param y_label the y label string
-               @param y_units the y units string
-               """
-               self.lock()
-               self.y_label = y_label
-               self.y_units = y_units
-               self.changed(True)
-               self.unlock()
-
-       def set_x_grid(self, x_min, x_max, x_step, x_scalar=1.0):
-               """
-               Set the x grid parameters.
-               @param x_min the left-most value
-               @param x_max the right-most value
-               @param x_step the grid spacing
-               @param x_scalar the scalar factor
-               """
-               self.lock()
-               self.x_min = float(x_min)
-               self.x_max = float(x_max)
-               self.x_step = float(x_step)
-               self.x_scalar = float(x_scalar)
-               self.changed(True)
-               self.unlock()
-
-       def set_y_grid(self, y_min, y_max, y_step, y_scalar=1.0):
-               """
-               Set the y grid parameters.
-               @param y_min the bottom-most value
-               @param y_max the top-most value
-               @param y_step the grid spacing
-               @param y_scalar the scalar factor
-               """
-               self.lock()
-               self.y_min = float(y_min)
-               self.y_max = float(y_max)
-               self.y_step = float(y_step)
-               self.y_scalar = float(y_scalar)
-               self.changed(True)
-               self.unlock()
-
-       def _draw_grid(self):
-               """
-               Draw the border, grid, title, and units.
-               """
-               ##################################################
-               # Draw Border
-               ##################################################
-               glColor3f(*GRID_LINE_COLOR_SPEC)
-               self._draw_rect(
-                       self.padding_left,
-                       self.padding_top,
-                       self.width - self.padding_right - self.padding_left,
-                       self.height - self.padding_top - self.padding_bottom,
-                       fill=False,
-               )
-               ##################################################
-               # Draw Grid X
-               ##################################################
-               for tick in self._get_ticks(self.x_min, self.x_max, 
self.x_step, self.x_scalar):
-                       scaled_tick = 
(self.width-self.padding_left-self.padding_right)*\
-                               
(tick/self.x_scalar-self.x_min)/(self.x_max-self.x_min) + self.padding_left
-                       glColor3f(*GRID_LINE_COLOR_SPEC)
-                       self._draw_line(
-                               (scaled_tick, self.padding_top, 0),
-                               (scaled_tick, self.height-self.padding_bottom, 
0),
-                       )
-                       txt = self._get_tick_label(tick)
-                       w, h = txt.get_size()
-                       txt.draw_text(wx.Point(scaled_tick-w/2, 
self.height-self.padding_bottom+TICK_LABEL_PADDING))
-               ##################################################
-               # Draw Grid Y
-               ##################################################
-               for tick in self._get_ticks(self.y_min, self.y_max, 
self.y_step, self.y_scalar):
-                       scaled_tick = 
(self.height-self.padding_top-self.padding_bottom)*\
-                               (1 - 
(tick/self.y_scalar-self.y_min)/(self.y_max-self.y_min)) + self.padding_top
-                       glColor3f(*GRID_LINE_COLOR_SPEC)
-                       self._draw_line(
-                               (self.padding_left, scaled_tick, 0),
-                               (self.width-self.padding_right, scaled_tick, 0),
-                       )
-                       txt = self._get_tick_label(tick)
-                       w, h = txt.get_size()
-                       
txt.draw_text(wx.Point(self.padding_left-w-TICK_LABEL_PADDING, scaled_tick-h/2))
-               ##################################################
-               # Draw Title
-               ##################################################
-               #draw x units
-               txt = gltext.Text(self.title, bold=True, 
font_size=TITLE_TEXT_FONT_SIZE, centered=True)
-               txt.draw_text(wx.Point(self.width/2.0, .5*self.padding_top))
-               ##################################################
-               # Draw Labels
-               ##################################################
-               #draw x labels
-               x_label_str = self.x_units and "%s (%s)"%(self.x_label, 
self.x_units) or self.x_label
-               txt = gltext.Text(x_label_str, bold=True, 
font_size=UNITS_TEXT_FONT_SIZE, centered=True)
-               txt.draw_text(wx.Point(
-                               
(self.width-self.padding_left-self.padding_right)/2.0 + self.padding_left,
-                               self.height-.25*self.padding_bottom,
-                               )
-               )
-               #draw y labels
-               y_label_str = self.y_units and "%s (%s)"%(self.y_label, 
self.y_units) or self.y_label
-               txt = gltext.Text(y_label_str, bold=True, 
font_size=UNITS_TEXT_FONT_SIZE, centered=True)
-               txt.draw_text(wx.Point(
-                               .25*self.padding_left,
-                               
(self.height-self.padding_top-self.padding_bottom)/2.0 + self.padding_top,
-                       ), rotation=90,
-               )
-
-       def _get_tick_label(self, tick):
-               """
-               Format the tick value and create a gl text.
-               @param tick the floating point tick value
-               @return the tick label text
-               """
-               tick_str = common.label_format(tick)
-               return gltext.Text(tick_str, font_size=TICK_TEXT_FONT_SIZE)
-
-       def _get_ticks(self, min, max, step, scalar):
-               """
-               Determine the positions for the ticks.
-               @param min the lower bound
-               @param max the upper bound
-               @param step the grid spacing
-               @param scalar the grid scaling
-               @return a list of tick positions between min and max
-               """
-               #cast to float
-               min = float(min)
-               max = float(max)
-               step = float(step)
-               #check for valid numbers
-               assert step > 0
-               assert max > min
-               assert max - min > step
-               #determine the start and stop value
-               start = int(math.ceil(min/step))
-               stop = int(math.floor(max/step))
-               return [i*step*scalar for i in range(start, stop+1)]
-
-       def _draw_line(self, coor1, coor2):
-               """
-               Draw a line from coor1 to coor2.
-               @param corr1 a tuple of x, y, z
-               @param corr2 a tuple of x, y, z
-               """
-               glBegin(GL_LINES)
-               glVertex3f(*coor1)
-               glVertex3f(*coor2)
-               glEnd()
-
-       def _draw_rect(self, x, y, width, height, fill=True):
-               """
-               Draw a rectangle on the x, y plane.
-               X and Y are the top-left corner.
-               @param x the left position of the rectangle
-               @param y the top position of the rectangle
-               @param width the width of the rectangle
-               @param height the height of the rectangle
-               @param fill true to color inside of rectangle
-               """
-               glBegin(fill and GL_QUADS or GL_LINE_LOOP)
-               glVertex2f(x, y)
-               glVertex2f(x+width, y)
-               glVertex2f(x+width, y+height)
-               glVertex2f(x, y+height)
-               glEnd()
-
-       def _draw_point_label(self):
-               """
-               Draw the point label for the last mouse motion coordinate.
-               The mouse coordinate must be an X, Y tuple.
-               The label will be drawn at the X, Y coordinate.
-               The values of the X, Y coordinate will be scaled to the current 
X, Y bounds.
-               """
-               if not self.enable_point_label(): return
-               if not self._mouse_coordinate: return
-               x, y = self._mouse_coordinate
-               if x < self.padding_left or x > self.width-self.padding_right: 
return
-               if y < self.padding_top or y > self.height-self.padding_bottom: 
return
-               #scale to window bounds
-               x_win_scalar = float(x - 
self.padding_left)/(self.width-self.padding_left-self.padding_right)
-               y_win_scalar = float((self.height - y) - 
self.padding_bottom)/(self.height-self.padding_top-self.padding_bottom)
-               #scale to grid bounds
-               x_val = self.x_scalar*(x_win_scalar*(self.x_max-self.x_min) + 
self.x_min)
-               y_val = self.y_scalar*(y_win_scalar*(self.y_max-self.y_min) + 
self.y_min)
-               #create text
-               label_str = self._populate_point_label(x_val, y_val)
-               txt = gltext.Text(label_str, font_size=POINT_LABEL_FONT_SIZE)
-               w, h = txt.get_size()
-               #draw rect + text
-               glColor3f(*POINT_LABEL_COLOR_SPEC)
-               if x > self.width/2: x -= w+2*POINT_LABEL_PADDING
-               self._draw_rect(x, y-h-2*POINT_LABEL_PADDING, 
w+2*POINT_LABEL_PADDING, h+2*POINT_LABEL_PADDING)
-               txt.draw_text(wx.Point(x+POINT_LABEL_PADDING, 
y-h-POINT_LABEL_PADDING))

Modified: gnuradio/trunk/gr-wxgui/src/python/plotter/waterfall_plotter.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/plotter/waterfall_plotter.py     
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/plotter/waterfall_plotter.py     
2009-03-20 02:16:20 UTC (rev 10660)
@@ -1,5 +1,5 @@
 #
-# Copyright 2008 Free Software Foundation, Inc.
+# Copyright 2008, 2009 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
@@ -20,9 +20,9 @@
 #
 
 import wx
-from plotter_base import grid_plotter_base
-from OpenGL.GL import *
-from gnuradio.wxgui import common
+from grid_plotter_base import grid_plotter_base
+from OpenGL import GL
+import common
 import numpy
 import gltext
 import math
@@ -33,7 +33,7 @@
 LEGEND_WIDTH = 8
 LEGEND_FONT_SIZE = 8
 LEGEND_BORDER_COLOR_SPEC = (0, 0, 0) #black
-PADDING = 35, 60, 40, 60 #top, right, bottom, left
+MIN_PADDING = 0, 60, 0, 0 #top, right, bottom, left
 
 ceil_log2 = lambda x: 2**int(math.ceil(math.log(x)/math.log(2)))
 
@@ -91,7 +91,13 @@
                Create a new channel plotter.
                """
                #init
-               grid_plotter_base.__init__(self, parent, PADDING)
+               grid_plotter_base.__init__(self, parent, MIN_PADDING)
+               #setup legend cache
+               self._legend_cache = self.new_gl_cache(self._draw_legend)
+               #setup waterfall cache
+               self._waterfall_cache = self.new_gl_cache(self._draw_waterfall, 
50)
+               #setup waterfall plotter
+               self.register_init(self._init_waterfall)
                self._resize_texture(False)
                self._minimum = 0
                self._maximum = 0
@@ -102,78 +108,56 @@
                self.set_num_lines(0)
                self.set_color_mode(COLORS.keys()[0])
 
-       def _gl_init(self):
+       def _init_waterfall(self):
                """
                Run gl initialization tasks.
                """
-               self._grid_compiled_list_id = glGenLists(1)
-               self._waterfall_texture = glGenTextures(1)
+               self._waterfall_texture = GL.glGenTextures(1)
 
-       def draw(self):
-               """
-               Draw the grid and waveforms.
-               """
-               self.lock()
-               #resize texture
-               self._resize_texture()
-               #store the grid drawing operations
-               if self.changed():
-                       glNewList(self._grid_compiled_list_id, GL_COMPILE)
-                       self._draw_grid()
-                       self._draw_legend()
-                       glEndList()
-                       self.changed(False)
-               self.clear()
-               #draw the grid
-               glCallList(self._grid_compiled_list_id)
-               self._draw_waterfall()
-               self._draw_point_label()
-               #swap buffer into display
-               self.SwapBuffers()
-               self.unlock()
-
        def _draw_waterfall(self):
                """
                Draw the waterfall from the texture.
                The texture is circularly filled and will wrap around.
                Use matrix modeling to shift and scale the texture onto the 
coordinate plane.
                """
+               #resize texture
+               self._resize_texture()
                #setup texture
-               glBindTexture(GL_TEXTURE_2D, self._waterfall_texture)
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
-               glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
+               GL.glBindTexture(GL.GL_TEXTURE_2D, self._waterfall_texture)
+               GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, 
GL.GL_LINEAR)
+               GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, 
GL.GL_LINEAR)
+               GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, 
GL.GL_REPEAT)
+               GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, 
GL.GL_REPLACE)
                #write the buffer to the texture
                while self._buffer:
-                       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, self._pointer, 
self._fft_size, 1, GL_RGBA, GL_UNSIGNED_BYTE, self._buffer.pop(0))
+                       GL.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 
self._pointer, self._fft_size, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, 
self._buffer.pop(0))
                        self._pointer = (self._pointer + 1)%self._num_lines
                #begin drawing
-               glEnable(GL_TEXTURE_2D)
-               glPushMatrix()
+               GL.glEnable(GL.GL_TEXTURE_2D)
+               GL.glPushMatrix()
                #matrix scaling
-               glTranslatef(self.padding_left, self.padding_top, 0)
-               glScalef(
+               GL.glTranslatef(self.padding_left, self.padding_top, 0)
+               GL.glScalef(
                        float(self.width-self.padding_left-self.padding_right),
                        float(self.height-self.padding_top-self.padding_bottom),
                        1.0,
                )
                #draw texture with wrapping
-               glBegin(GL_QUADS)
+               GL.glBegin(GL.GL_QUADS)
                prop_y = float(self._pointer)/(self._num_lines-1)
                prop_x = float(self._fft_size)/ceil_log2(self._fft_size)
                off = 1.0/(self._num_lines-1)
-               glTexCoord2f(0, prop_y+1-off)
-               glVertex2f(0, 1)
-               glTexCoord2f(prop_x, prop_y+1-off)
-               glVertex2f(1, 1)
-               glTexCoord2f(prop_x, prop_y)
-               glVertex2f(1, 0)
-               glTexCoord2f(0, prop_y)
-               glVertex2f(0, 0)
-               glEnd()
-               glPopMatrix()
-               glDisable(GL_TEXTURE_2D)
+               GL.glTexCoord2f(0, prop_y+1-off)
+               GL.glVertex2f(0, 1)
+               GL.glTexCoord2f(prop_x, prop_y+1-off)
+               GL.glVertex2f(1, 1)
+               GL.glTexCoord2f(prop_x, prop_y)
+               GL.glVertex2f(1, 0)
+               GL.glTexCoord2f(0, prop_y)
+               GL.glVertex2f(0, 0)
+               GL.glEnd()
+               GL.glPopMatrix()
+               GL.glDisable(GL.GL_TEXTURE_2D)
 
        def _populate_point_label(self, x_val, y_val):
                """
@@ -183,7 +167,7 @@
                @param y_val the current y value
                @return a value string with units
                """
-               return '%s: %s %s'%(self.x_label, common.label_format(x_val), 
self.x_units)
+               return '%s: %s'%(self.x_label, common.eng_format(x_val, 
self.x_units))
 
        def _draw_legend(self):
                """
@@ -196,11 +180,11 @@
                x = self.width - self.padding_right + LEGEND_LEFT_PAD
                for i in range(LEGEND_NUM_BLOCKS):
                        color = 
COLORS[self._color_mode][int(255*i/float(LEGEND_NUM_BLOCKS-1))]
-                       glColor4f(*map(lambda c: ord(c)/255.0, color))
+                       GL.glColor4f(*map(lambda c: ord(c)/255.0, color))
                        y = self.height - (i+1)*block_height - 
self.padding_bottom
                        self._draw_rect(x, y, LEGEND_WIDTH, block_height)
                #draw rectangle around color scale border
-               glColor3f(*LEGEND_BORDER_COLOR_SPEC)
+               GL.glColor3f(*LEGEND_BORDER_COLOR_SPEC)
                self._draw_rect(x, self.padding_top, LEGEND_WIDTH, 
legend_height, fill=False)
                #draw each legend label
                label_spacing = float(legend_height)/(LEGEND_NUM_LABELS-1)
@@ -224,9 +208,9 @@
                self._buffer = list()
                self._pointer = 0
                if self._num_lines and self._fft_size:
-                       glBindTexture(GL_TEXTURE_2D, self._waterfall_texture)
+                       GL.glBindTexture(GL.GL_TEXTURE_2D, 
self._waterfall_texture)
                        data = numpy.zeros(self._num_lines*self._fft_size*4, 
numpy.uint8).tostring()
-                       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 
ceil_log2(self._fft_size), self._num_lines, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
+                       GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, 
ceil_log2(self._fft_size), self._num_lines, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, 
data)
                self._resize_texture_flag = False
 
        def set_color_mode(self, color_mode):
@@ -239,7 +223,7 @@
                self.lock()
                if color_mode in COLORS.keys():
                        self._color_mode = color_mode
-                       self.changed(True)
+                       self._legend_cache.changed(True)
                self.update()
                self.unlock()
 
@@ -268,7 +252,7 @@
                if self._minimum != minimum or self._maximum != maximum:
                        self._minimum = minimum
                        self._maximum = maximum
-                       self.changed(True)
+                       self._legend_cache.changed(True)
                if self._fft_size != len(samples):
                        self._fft_size = len(samples)
                        self._resize_texture(True)
@@ -279,4 +263,5 @@
                #convert the samples to RGBA data
                data = numpy.choose(samples, 
COLORS[self._color_mode]).tostring()
                self._buffer.append(data)
+               self._waterfall_cache.changed(True)
                self.unlock()

Modified: gnuradio/trunk/gr-wxgui/src/python/pubsub.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/pubsub.py        2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/pubsub.py        2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -28,73 +28,73 @@
 
 class pubsub(dict):
     def __init__(self):
-       self._publishers = { }
-       self._subscribers = { }
-       self._proxies = { }
-       
+        self._publishers = { }
+        self._subscribers = { }
+        self._proxies = { }
+
     def __missing__(self, key, value=None):
-       dict.__setitem__(self, key, value)
-       self._publishers[key] = None
-       self._subscribers[key] = []
-       self._proxies[key] = None
-       
+        dict.__setitem__(self, key, value)
+        self._publishers[key] = None
+        self._subscribers[key] = []
+        self._proxies[key] = None
+
     def __setitem__(self, key, val):
-       if not self.has_key(key): 
-           self.__missing__(key, val)
-       elif self._proxies[key] is not None:
-           (p, newkey) = self._proxies[key]
-           p[newkey] = val
-       else:
-           dict.__setitem__(self, key, val)
-       for sub in self._subscribers[key]:
-           # Note this means subscribers will get called in the thread
-           # context of the 'set' caller.
-           sub(val)
+        if not self.has_key(key):
+            self.__missing__(key, val)
+        elif self._proxies[key] is not None:
+            (p, pkey) = self._proxies[key]
+            p[pkey] = val
+        else:
+            dict.__setitem__(self, key, val)
+        for sub in self._subscribers[key]:
+            # Note this means subscribers will get called in the thread
+            # context of the 'set' caller.
+            sub(val)
 
     def __getitem__(self, key):
-       if not self.has_key(key): self.__missing__(key)
-       if self._proxies[key] is not None:
-           (p, newkey) = self._proxies[key]
-           return p[newkey]
-       elif self._publishers[key] is not None:
-           return self._publishers[key]()
-       else:
-           return dict.__getitem__(self, key)
+        if not self.has_key(key): self.__missing__(key)
+        if self._proxies[key] is not None:
+            (p, pkey) = self._proxies[key]
+            return p[pkey]
+        elif self._publishers[key] is not None:
+            return self._publishers[key]()
+        else:
+            return dict.__getitem__(self, key)
 
     def publish(self, key, publisher):
-       if not self.has_key(key): self.__missing__(key)
+        if not self.has_key(key): self.__missing__(key)
         if self._proxies[key] is not None:
-            (p, newkey) = self._proxies[key]
-            p.publish(newkey, publisher)
+            (p, pkey) = self._proxies[key]
+            p.publish(pkey, publisher)
         else:
             self._publishers[key] = publisher
-       
+
     def subscribe(self, key, subscriber):
-       if not self.has_key(key): self.__missing__(key)
+        if not self.has_key(key): self.__missing__(key)
         if self._proxies[key] is not None:
-            (p, newkey) = self._proxies[key]
-            p.subscribe(newkey, subscriber)
+            (p, pkey) = self._proxies[key]
+            p.subscribe(pkey, subscriber)
         else:
             self._subscribers[key].append(subscriber)
-       
+
     def unpublish(self, key):
         if self._proxies[key] is not None:
-            (p, newkey) = self._proxies[key]
-            p.unpublish(newkey)
+            (p, pkey) = self._proxies[key]
+            p.unpublish(pkey)
         else:
             self._publishers[key] = None
-       
+
     def unsubscribe(self, key, subscriber):
         if self._proxies[key] is not None:
-            (p, newkey) = self._proxies[key]
-            p.unsubscribe(newkey, subscriber)
+            (p, pkey) = self._proxies[key]
+            p.unsubscribe(pkey, subscriber)
         else:
             self._subscribers[key].remove(subscriber)
 
-    def proxy(self, key, p, newkey=None):
-       if not self.has_key(key): self.__missing__(key)
-       if newkey is None: newkey = key
-       self._proxies[key] = (p, newkey)        
+    def proxy(self, key, p, pkey=None):
+        if not self.has_key(key): self.__missing__(key)
+        if pkey is None: pkey = key
+        self._proxies[key] = (p, pkey)
 
     def unproxy(self, key):
         self._proxies[key] = None
@@ -110,22 +110,22 @@
     # Add some subscribers
     # First is a bare function
     def print_len(x):
-       print "len=%i" % (len(x), )
+        print "len=%i" % (len(x), )
     o.subscribe('foo', print_len)
 
     # The second is a class member function
     class subber(object):
-       def __init__(self, param):
-           self._param = param
-       def printer(self, x):
-           print self._param, `x`
+        def __init__(self, param):
+            self._param = param
+        def printer(self, x):
+            print self._param, `x`
     s = subber('param')
     o.subscribe('foo', s.printer)
 
     # The third is a lambda function
     o.subscribe('foo', lambda x: sys.stdout.write('val='+`x`+'\n'))
 
-    # Update key 'foo', will notify subscribers    
+    # Update key 'foo', will notify subscribers
     print "Updating 'foo' with three subscribers:"
     o['foo'] = 'bar';
 
@@ -135,7 +135,7 @@
     # Update now will only trigger second and third subscriber
     print "Updating 'foo' after removing a subscriber:"
     o['foo'] = 'bar2';
-    
+
     # Publish a key as a function, in this case, a lambda function
     o.publish('baz', lambda : 42)
     print "Published value of 'baz':", o['baz']
@@ -145,7 +145,7 @@
 
     # This will return None, as there is no publisher
     print "Value of 'baz' with no publisher:", o['baz']
-    
+
     # Set 'baz' key, it gets cached
     o['baz'] = 'bazzz'
 

Modified: gnuradio/trunk/gr-wxgui/src/python/scope_window.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/scope_window.py  2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/scope_window.py  2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -29,41 +29,40 @@
 import time
 import pubsub
 from constants import *
-from gnuradio import gr #for gr.prefs
+from gnuradio import gr #for gr.prefs, trigger modes
 
 ##################################################
 # Constants
 ##################################################
 DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'scope_rate', 30)
 DEFAULT_WIN_SIZE = (600, 300)
-DEFAULT_V_SCALE = 1000
+COUPLING_MODES = (
+       ('DC', False),
+       ('AC', True),
+)
 TRIGGER_MODES = (
-       ('Off', 0),
-       ('Neg', -1),
-       ('Pos', +1),
+       ('Freerun', gr.gr_TRIG_MODE_FREE),
+       ('Automatic', gr.gr_TRIG_MODE_AUTO),
+       ('Normal', gr.gr_TRIG_MODE_NORM),
 )
-TRIGGER_LEVELS = (
-       ('Auto', None),
-       ('+High', 0.75),
-       ('+Med', 0.5),
-       ('+Low', 0.25),
-       ('Zero', 0.0),
-       ('-Low', -0.25),
-       ('-Med', -0.5),
-       ('-High', -0.75),
+TRIGGER_SLOPES = (
+       ('Positive +', gr.gr_TRIG_SLOPE_POS),
+       ('Negative -', gr.gr_TRIG_SLOPE_NEG),
 )
 CHANNEL_COLOR_SPECS = (
-       (0, 0, 1),
-       (0, 1, 0),
-       (1, 0, 0),
-       (1, 0, 1),
+       (0.3, 0.3, 1.0),
+       (0.0, 0.8, 0.0),
+       (1.0, 0.0, 0.0),
+       (0.8, 0.0, 0.8),
 )
+TRIGGER_COLOR_SPEC = (1.0, 0.4, 0.0)
 AUTORANGE_UPDATE_RATE = 0.5 #sec
 MARKER_TYPES = (
+       ('Line Link', None),
+       ('Dot Large', 3.0),
+       ('Dot Med', 2.0),
        ('Dot Small', 1.0),
-       ('Dot Medium', 2.0),
-       ('Dot Large', 3.0),
-       ('Line Link', None),
+       ('None', 0.0),
 )
 DEFAULT_MARKER_TYPE = None
 
@@ -79,175 +78,213 @@
                Create a new control panel.
                @param parent the wx parent window
                """
+               SIZE = (100, -1)
                self.parent = parent
                wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
-               self.control_box = control_box = wx.BoxSizer(wx.VERTICAL)
-               #trigger options
+               control_box = wx.BoxSizer(wx.VERTICAL)
+               ##################################################
+               # Axes Options
+               ##################################################
                control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Trigger Options'), 0, 
wx.ALIGN_CENTER)
-               control_box.AddSpacer(2)
-               #trigger mode
-               self.trigger_mode_chooser = common.DropDownController(self, 
'Mode', TRIGGER_MODES, parent, TRIGGER_MODE_KEY)
-               control_box.Add(self.trigger_mode_chooser, 0, wx.EXPAND)
-               #trigger level
-               self.trigger_level_chooser = common.DropDownController(self, 
'Level', TRIGGER_LEVELS, parent, TRIGGER_LEVEL_KEY)
-               parent.subscribe(TRIGGER_MODE_KEY, lambda x: 
self.trigger_level_chooser.Disable(x==0))
-               control_box.Add(self.trigger_level_chooser, 0, wx.EXPAND)
-               #trigger channel
-               choices = [('Ch%d'%(i+1), i) for i in range(parent.num_inputs)]
-               self.trigger_channel_chooser = common.DropDownController(self, 
'Channel', choices, parent, TRIGGER_CHANNEL_KEY)
-               parent.subscribe(TRIGGER_MODE_KEY, lambda x: 
self.trigger_channel_chooser.Disable(x==0))
-               control_box.Add(self.trigger_channel_chooser, 0, wx.EXPAND)
-               #axes options
-               SPACING = 15
-               control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Axes Options'), 0, 
wx.ALIGN_CENTER)
                control_box.AddSpacer(2)
                ##################################################
                # Scope Mode Box
                ##################################################
-               self.scope_mode_box = wx.BoxSizer(wx.VERTICAL)
-               control_box.Add(self.scope_mode_box, 0, wx.EXPAND)
+               scope_mode_box = wx.BoxSizer(wx.VERTICAL)
+               control_box.Add(scope_mode_box, 0, wx.EXPAND)
                #x axis divs
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               self.scope_mode_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Secs/Div '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               x_buttons = common.IncrDecrButtons(self, self._on_incr_t_divs, 
self._on_decr_t_divs)
-               hbox.Add(x_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(SPACING)
+               x_buttons_scope = common.IncrDecrButtons(self, 
self._on_incr_t_divs, self._on_decr_t_divs)
+               scope_mode_box.Add(common.LabelBox(self, 'Secs/Div', 
x_buttons_scope), 0, wx.EXPAND)
                #y axis divs
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               self.scope_mode_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Units/Div '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               y_buttons = common.IncrDecrButtons(self, self._on_incr_y_divs, 
self._on_decr_y_divs)
-               parent.subscribe(AUTORANGE_KEY, y_buttons.Disable)
-               hbox.Add(y_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(SPACING)
+               y_buttons_scope = common.IncrDecrButtons(self, 
self._on_incr_y_divs, self._on_decr_y_divs)
+               parent.subscribe(AUTORANGE_KEY, lambda x: 
y_buttons_scope.Enable(not x))
+               scope_mode_box.Add(common.LabelBox(self, 'Counts/Div', 
y_buttons_scope), 0, wx.EXPAND)
                #y axis ref lvl
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               self.scope_mode_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Y Offset '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               y_off_buttons = common.IncrDecrButtons(self, 
self._on_incr_y_off, self._on_decr_y_off)
-               parent.subscribe(AUTORANGE_KEY, y_off_buttons.Disable)
-               hbox.Add(y_off_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(SPACING)
+               y_off_buttons_scope = common.IncrDecrButtons(self, 
self._on_incr_y_off, self._on_decr_y_off)
+               parent.subscribe(AUTORANGE_KEY, lambda x: 
y_off_buttons_scope.Enable(not x))
+               scope_mode_box.Add(common.LabelBox(self, 'Y Offset', 
y_off_buttons_scope), 0, wx.EXPAND)
+               #t axis ref lvl
+               scope_mode_box.AddSpacer(5)
+               t_off_slider = wx.Slider(self, size=SIZE, 
style=wx.SL_HORIZONTAL)
+               t_off_slider.SetRange(0, 1000)
+               def t_off_slider_changed(evt): parent[T_FRAC_OFF_KEY] = 
float(t_off_slider.GetValue())/t_off_slider.GetMax()
+               t_off_slider.Bind(wx.EVT_SLIDER, t_off_slider_changed)
+               parent.subscribe(T_FRAC_OFF_KEY, lambda x: 
t_off_slider.SetValue(int(round(x*t_off_slider.GetMax()))))
+               scope_mode_box.Add(common.LabelBox(self, 'T Offset', 
t_off_slider), 0, wx.EXPAND)
+               scope_mode_box.AddSpacer(5)
                ##################################################
                # XY Mode Box
                ##################################################
-               self.xy_mode_box = wx.BoxSizer(wx.VERTICAL)
-               control_box.Add(self.xy_mode_box, 0, wx.EXPAND)
-               #x and y channel
-               CHOOSER_WIDTH = 60
-               CENTER_SPACING = 10
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               self.xy_mode_box.Add(hbox, 0, wx.EXPAND)
-               choices = [('Ch%d'%(i+1), i) for i in range(parent.num_inputs)]
-               self.channel_x_chooser = common.DropDownController(self, 'X 
Ch', choices, parent, SCOPE_X_CHANNEL_KEY, (CHOOSER_WIDTH, -1))
-               hbox.Add(self.channel_x_chooser, 0, wx.EXPAND)
-               hbox.AddSpacer(CENTER_SPACING)
-               self.channel_y_chooser = common.DropDownController(self, 'Y 
Ch', choices, parent, SCOPE_Y_CHANNEL_KEY, (CHOOSER_WIDTH, -1))
-               hbox.Add(self.channel_y_chooser, 0, wx.EXPAND)
-               #div controls
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               self.xy_mode_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' X/Div '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               xy_mode_box = wx.BoxSizer(wx.VERTICAL)
+               control_box.Add(xy_mode_box, 0, wx.EXPAND)
+               #x div controls
                x_buttons = common.IncrDecrButtons(self, self._on_incr_x_divs, 
self._on_decr_x_divs)
-               parent.subscribe(AUTORANGE_KEY, x_buttons.Disable)
-               hbox.Add(x_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(CENTER_SPACING)
-               hbox.Add(wx.StaticText(self, -1, ' Y/Div '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               parent.subscribe(AUTORANGE_KEY, lambda x: x_buttons.Enable(not 
x))
+               xy_mode_box.Add(common.LabelBox(self, 'X/Div', x_buttons), 0, 
wx.EXPAND)
+               #y div controls
                y_buttons = common.IncrDecrButtons(self, self._on_incr_y_divs, 
self._on_decr_y_divs)
-               parent.subscribe(AUTORANGE_KEY, y_buttons.Disable)
-               hbox.Add(y_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               #offset controls
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               self.xy_mode_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' X Off '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               parent.subscribe(AUTORANGE_KEY, lambda x: y_buttons.Enable(not 
x))
+               xy_mode_box.Add(common.LabelBox(self, 'Y/Div', y_buttons), 0, 
wx.EXPAND)
+               #x offset controls
                x_off_buttons = common.IncrDecrButtons(self, 
self._on_incr_x_off, self._on_decr_x_off)
-               parent.subscribe(AUTORANGE_KEY, x_off_buttons.Disable)
-               hbox.Add(x_off_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(CENTER_SPACING)
-               hbox.Add(wx.StaticText(self, -1, ' Y Off '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               parent.subscribe(AUTORANGE_KEY, lambda x: 
x_off_buttons.Enable(not x))
+               xy_mode_box.Add(common.LabelBox(self, 'X Off', x_off_buttons), 
0, wx.EXPAND)
+               #y offset controls
                y_off_buttons = common.IncrDecrButtons(self, 
self._on_incr_y_off, self._on_decr_y_off)
-               parent.subscribe(AUTORANGE_KEY, y_off_buttons.Disable)
-               hbox.Add(y_off_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               ##################################################
-               # End Special Boxes
-               ##################################################
-               #misc options
-               control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Range Options'), 0, 
wx.ALIGN_CENTER)
-               #ac couple check box
-               self.ac_couple_check_box = common.CheckBoxController(self, 'AC 
Couple', parent, AC_COUPLE_KEY)
-               control_box.Add(self.ac_couple_check_box, 0, wx.ALIGN_LEFT)
+               parent.subscribe(AUTORANGE_KEY, lambda x: 
y_off_buttons.Enable(not x))
+               xy_mode_box.Add(common.LabelBox(self, 'Y Off', y_off_buttons), 
0, wx.EXPAND)
+               xy_mode_box.ShowItems(False)
                #autorange check box
                self.autorange_check_box = common.CheckBoxController(self, 
'Autorange', parent, AUTORANGE_KEY)
                control_box.Add(self.autorange_check_box, 0, wx.ALIGN_LEFT)
-               #marker
                control_box.AddStretchSpacer()
-               self.marker_chooser = common.DropDownController(self, 'Marker', 
MARKER_TYPES, parent, MARKER_KEY)
-               control_box.Add(self.marker_chooser, 0, wx.EXPAND)
-               #xy mode
-               control_box.AddStretchSpacer()
-               self.scope_xy_mode_button = common.ToggleButtonController(self, 
parent, SCOPE_XY_MODE_KEY, 'Scope Mode', 'X:Y Mode')
-               parent.subscribe(SCOPE_XY_MODE_KEY, self._on_scope_xy_mode)
-               control_box.Add(self.scope_xy_mode_button, 0, wx.EXPAND)
+               ##################################################
+               # Channel Options
+               ##################################################
+               TRIGGER_PAGE_INDEX = parent.num_inputs
+               XY_PAGE_INDEX = parent.num_inputs+1
+               control_box.Add(common.LabelText(self, 'Channel Options'), 0, 
wx.ALIGN_CENTER)
+               control_box.AddSpacer(2)
+               options_notebook = wx.Notebook(self)
+               control_box.Add(options_notebook, 0, wx.EXPAND)
+               def options_notebook_changed(evt):
+                       try:
+                               parent[TRIGGER_SHOW_KEY] = 
options_notebook.GetSelection() == TRIGGER_PAGE_INDEX
+                               parent[XY_MODE_KEY] = 
options_notebook.GetSelection() == XY_PAGE_INDEX
+                       except wx.PyDeadObjectError: pass
+               options_notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, 
options_notebook_changed)
+               def xy_mode_changed(mode):
+                       #ensure xy tab is selected
+                       if mode and options_notebook.GetSelection() != 
XY_PAGE_INDEX:
+                               options_notebook.SetSelection(XY_PAGE_INDEX)
+                       #ensure xy tab is not selected
+                       elif not mode and options_notebook.GetSelection() == 
XY_PAGE_INDEX:
+                               options_notebook.SetSelection(0)
+                       #show/hide control buttons
+                       scope_mode_box.ShowItems(not mode)
+                       xy_mode_box.ShowItems(mode)
+                       control_box.Layout()
+               parent.subscribe(XY_MODE_KEY, xy_mode_changed)
+               ##################################################
+               # Channel Menu Boxes
+               ##################################################
+               for i in range(parent.num_inputs):
+                       channel_menu_panel = wx.Panel(options_notebook)
+                       options_notebook.AddPage(channel_menu_panel, 
'Ch%d'%(i+1))
+                       channel_menu_box = wx.BoxSizer(wx.VERTICAL)
+                       channel_menu_panel.SetSizer(channel_menu_box)
+                       #ac couple check box
+                       channel_menu_box.AddStretchSpacer()
+                       coupling_chooser = 
common.DropDownController(channel_menu_panel, COUPLING_MODES, parent, 
common.index_key(AC_COUPLE_KEY, i), SIZE)
+                       
channel_menu_box.Add(common.LabelBox(channel_menu_panel, 'Coupling', 
coupling_chooser), 0, wx.EXPAND)
+                       #marker
+                       channel_menu_box.AddStretchSpacer()
+                       marker_chooser = 
common.DropDownController(channel_menu_panel, MARKER_TYPES, parent, 
common.index_key(MARKER_KEY, i), SIZE)
+                       
channel_menu_box.Add(common.LabelBox(channel_menu_panel, 'Marker', 
marker_chooser), 0, wx.EXPAND)
+                       channel_menu_box.AddStretchSpacer()
+               ##################################################
+               # Trigger Menu Box
+               ##################################################
+               trigger_menu_panel = wx.Panel(options_notebook)
+               options_notebook.AddPage(trigger_menu_panel, 'Trig')
+               trigger_menu_box = wx.BoxSizer(wx.VERTICAL)
+               trigger_menu_panel.SetSizer(trigger_menu_box)
+               #trigger mode
+               trigger_mode_chooser = 
common.DropDownController(trigger_menu_panel, TRIGGER_MODES, parent, 
TRIGGER_MODE_KEY, SIZE)
+               trigger_menu_box.Add(common.LabelBox(trigger_menu_panel, 
'Mode', trigger_mode_chooser), 0, wx.EXPAND)
+               #trigger slope
+               trigger_slope_chooser = 
common.DropDownController(trigger_menu_panel, TRIGGER_SLOPES, parent, 
TRIGGER_SLOPE_KEY, SIZE)
+               parent.subscribe(TRIGGER_MODE_KEY, lambda x: 
trigger_slope_chooser.Enable(x!=gr.gr_TRIG_MODE_FREE))
+               trigger_menu_box.Add(common.LabelBox(trigger_menu_panel, 
'Slope', trigger_slope_chooser), 0, wx.EXPAND)
+               #trigger channel
+               choices = [('Channel %d'%(i+1), i) for i in 
range(parent.num_inputs)]
+               trigger_channel_chooser = 
common.DropDownController(trigger_menu_panel, choices, parent, 
TRIGGER_CHANNEL_KEY, SIZE)
+               parent.subscribe(TRIGGER_MODE_KEY, lambda x: 
trigger_channel_chooser.Enable(x!=gr.gr_TRIG_MODE_FREE))
+               trigger_menu_box.Add(common.LabelBox(trigger_menu_panel, 
'Channel', trigger_channel_chooser), 0, wx.EXPAND)
+               #trigger level
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               trigger_menu_box.Add(hbox, 0, wx.EXPAND)
+               hbox.Add(wx.StaticText(trigger_menu_panel, label=' Level '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               trigger_level_button = wx.Button(trigger_menu_panel, 
label='50%', style=wx.BU_EXACTFIT)
+               parent.subscribe(TRIGGER_MODE_KEY, lambda x: 
trigger_level_button.Enable(x!=gr.gr_TRIG_MODE_FREE))
+               trigger_level_button.Bind(wx.EVT_BUTTON, 
self.parent.set_auto_trigger_level)
+               hbox.Add(trigger_level_button, 0, wx.ALIGN_CENTER_VERTICAL)
+               hbox.AddSpacer(10)
+               trigger_level_buttons = 
common.IncrDecrButtons(trigger_menu_panel, self._on_incr_trigger_level, 
self._on_decr_trigger_level)
+               parent.subscribe(TRIGGER_MODE_KEY, lambda x: 
trigger_level_buttons.Enable(x!=gr.gr_TRIG_MODE_FREE))
+               hbox.Add(trigger_level_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
+               ##################################################
+               # XY Menu Box
+               ##################################################
+               if parent.num_inputs > 1:
+                       xy_menu_panel = wx.Panel(options_notebook)
+                       options_notebook.AddPage(xy_menu_panel, 'XY')
+                       xy_menu_box = wx.BoxSizer(wx.VERTICAL)
+                       xy_menu_panel.SetSizer(xy_menu_box)
+                       #x and y channel choosers
+                       xy_menu_box.AddStretchSpacer()
+                       choices = [('Ch%d'%(i+1), i) for i in 
range(parent.num_inputs)]
+                       x_channel_chooser = 
common.DropDownController(xy_menu_panel, choices, parent, X_CHANNEL_KEY, SIZE)
+                       xy_menu_box.Add(common.LabelBox(xy_menu_panel, 'Ch X', 
x_channel_chooser), 0, wx.EXPAND)
+                       xy_menu_box.AddStretchSpacer()
+                       y_channel_chooser = 
common.DropDownController(xy_menu_panel, choices, parent, Y_CHANNEL_KEY, SIZE)
+                       xy_menu_box.Add(common.LabelBox(xy_menu_panel, 'Ch Y', 
y_channel_chooser), 0, wx.EXPAND)
+                       #marker
+                       xy_menu_box.AddStretchSpacer()
+                       marker_chooser = 
common.DropDownController(xy_menu_panel, MARKER_TYPES, parent, XY_MARKER_KEY, 
SIZE)
+                       xy_menu_box.Add(common.LabelBox(xy_menu_panel, 
'Marker', marker_chooser), 0, wx.EXPAND)
+                       xy_menu_box.AddStretchSpacer()
+               ##################################################
+               # Run/Stop Button
+               ##################################################
                #run/stop
                self.run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
                control_box.Add(self.run_button, 0, wx.EXPAND)
                #set sizer
                self.SetSizerAndFit(control_box)
+               #mouse wheel event
+               def on_mouse_wheel(event):
+                       if not parent[XY_MODE_KEY]:
+                               if event.GetWheelRotation() < 0: 
self._on_incr_t_divs(event)
+                               else: self._on_decr_t_divs(event)
+               parent.plotter.Bind(wx.EVT_MOUSEWHEEL, on_mouse_wheel)
 
        ##################################################
        # Event handlers
        ##################################################
-       def _on_scope_xy_mode(self, mode):
-               self.scope_mode_box.ShowItems(not mode)
-               self.xy_mode_box.ShowItems(mode)
-               self.control_box.Layout()
+       #trigger level
+       def _on_incr_trigger_level(self, event):
+               self.parent[TRIGGER_LEVEL_KEY] += self.parent[Y_PER_DIV_KEY]/3.
+       def _on_decr_trigger_level(self, event):
+               self.parent[TRIGGER_LEVEL_KEY] -= self.parent[Y_PER_DIV_KEY]/3.
        #incr/decr divs
        def _on_incr_t_divs(self, event):
-               self.parent.set_t_per_div(
-                       common.get_clean_incr(self.parent[T_PER_DIV_KEY]))
+               self.parent[T_PER_DIV_KEY] = 
common.get_clean_incr(self.parent[T_PER_DIV_KEY])
        def _on_decr_t_divs(self, event):
-               self.parent.set_t_per_div(
-                       common.get_clean_decr(self.parent[T_PER_DIV_KEY]))
+               self.parent[T_PER_DIV_KEY] = 
common.get_clean_decr(self.parent[T_PER_DIV_KEY])
        def _on_incr_x_divs(self, event):
-               self.parent.set_x_per_div(
-                       common.get_clean_incr(self.parent[X_PER_DIV_KEY]))
+               self.parent[X_PER_DIV_KEY] = 
common.get_clean_incr(self.parent[X_PER_DIV_KEY])
        def _on_decr_x_divs(self, event):
-               self.parent.set_x_per_div(
-                       common.get_clean_decr(self.parent[X_PER_DIV_KEY]))
+               self.parent[X_PER_DIV_KEY] = 
common.get_clean_decr(self.parent[X_PER_DIV_KEY])
        def _on_incr_y_divs(self, event):
-               self.parent.set_y_per_div(
-                       common.get_clean_incr(self.parent[Y_PER_DIV_KEY]))
+               self.parent[Y_PER_DIV_KEY] = 
common.get_clean_incr(self.parent[Y_PER_DIV_KEY])
        def _on_decr_y_divs(self, event):
-               self.parent.set_y_per_div(
-                       common.get_clean_decr(self.parent[Y_PER_DIV_KEY]))
+               self.parent[Y_PER_DIV_KEY] = 
common.get_clean_decr(self.parent[Y_PER_DIV_KEY])
        #incr/decr offset
-       def _on_incr_t_off(self, event):
-               self.parent.set_t_off(
-                       self.parent[T_OFF_KEY] + self.parent[T_PER_DIV_KEY])
-       def _on_decr_t_off(self, event):
-               self.parent.set_t_off(
-                       self.parent[T_OFF_KEY] - self.parent[T_PER_DIV_KEY])
        def _on_incr_x_off(self, event):
-               self.parent.set_x_off(
-                       self.parent[X_OFF_KEY] + self.parent[X_PER_DIV_KEY])
+               self.parent[X_OFF_KEY] = self.parent[X_OFF_KEY] + 
self.parent[X_PER_DIV_KEY]
        def _on_decr_x_off(self, event):
-               self.parent.set_x_off(
-                       self.parent[X_OFF_KEY] - self.parent[X_PER_DIV_KEY])
+               self.parent[X_OFF_KEY] = self.parent[X_OFF_KEY] - 
self.parent[X_PER_DIV_KEY]
        def _on_incr_y_off(self, event):
-               self.parent.set_y_off(
-                       self.parent[Y_OFF_KEY] + self.parent[Y_PER_DIV_KEY])
+               self.parent[Y_OFF_KEY] = self.parent[Y_OFF_KEY] + 
self.parent[Y_PER_DIV_KEY]
        def _on_decr_y_off(self, event):
-               self.parent.set_y_off(
-                       self.parent[Y_OFF_KEY] - self.parent[Y_PER_DIV_KEY])
+               self.parent[Y_OFF_KEY] = self.parent[Y_OFF_KEY] - 
self.parent[Y_PER_DIV_KEY]
 
 ##################################################
 # Scope window with plotter and control panel
 ##################################################
-class scope_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+class scope_window(wx.Panel, pubsub.pubsub):
        def __init__(
                self,
                parent,
@@ -259,11 +296,13 @@
                sample_rate_key,
                t_scale,
                v_scale,
-               ac_couple,
                xy_mode,
-               scope_trigger_level_key,
-               scope_trigger_mode_key,
-               scope_trigger_channel_key,
+               ac_couple_key,
+               trigger_level_key,
+               trigger_mode_key,
+               trigger_slope_key,
+               trigger_channel_key,
+               decimation_key,
                msg_key,
        ):
                pubsub.pubsub.__init__(self)
@@ -271,67 +310,73 @@
                assert num_inputs <= len(CHANNEL_COLOR_SPECS)
                #setup
                self.sampleses = None
-               self.ext_controller = controller
                self.num_inputs = num_inputs
-               self.sample_rate_key = sample_rate_key
-               autorange = v_scale is None
+               autorange = not v_scale
                self.autorange_ts = 0
-               if v_scale is None: v_scale = 1
+               v_scale = v_scale or 1
                self.frame_rate_ts = 0
-               self._init = False #HACK
-               #scope keys
-               self.scope_trigger_level_key = scope_trigger_level_key
-               self.scope_trigger_mode_key = scope_trigger_mode_key
-               self.scope_trigger_channel_key = scope_trigger_channel_key
+               #proxy the keys
+               self.proxy(MSG_KEY, controller, msg_key)
+               self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
+               self.proxy(TRIGGER_LEVEL_KEY, controller, trigger_level_key)
+               self.proxy(TRIGGER_MODE_KEY, controller, trigger_mode_key)
+               self.proxy(TRIGGER_SLOPE_KEY, controller, trigger_slope_key)
+               self.proxy(TRIGGER_CHANNEL_KEY, controller, trigger_channel_key)
+               self.proxy(DECIMATION_KEY, controller, decimation_key)
+               for i in range(num_inputs):
+                       self.proxy(common.index_key(AC_COUPLE_KEY, i), 
controller, common.index_key(ac_couple_key, i))
                #init panel and plot
-               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
                self.plotter = plotter.channel_plotter(self)
                self.plotter.SetSize(wx.Size(*size))
                self.plotter.set_title(title)
                self.plotter.enable_legend(True)
                self.plotter.enable_point_label(True)
+               self.plotter.enable_grid_lines(True)
                #setup the box with plot and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
                main_box.Add(self.plotter, 1, wx.EXPAND)
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
-               #initial setup
-               self._register_set_prop(self, RUNNING_KEY, True)
-               self._register_set_prop(self, AC_COUPLE_KEY, ac_couple)
-               self._register_set_prop(self, SCOPE_XY_MODE_KEY, xy_mode)
-               self._register_set_prop(self, AUTORANGE_KEY, autorange)
-               self._register_set_prop(self, T_PER_DIV_KEY, t_scale)
-               self._register_set_prop(self, X_PER_DIV_KEY, v_scale)
-               self._register_set_prop(self, Y_PER_DIV_KEY, v_scale)
-               self._register_set_prop(self, T_OFF_KEY, 0)
-               self._register_set_prop(self, X_OFF_KEY, 0)
-               self._register_set_prop(self, Y_OFF_KEY, 0)
-               self._register_set_prop(self, T_DIVS_KEY, 8)
-               self._register_set_prop(self, X_DIVS_KEY, 8)
-               self._register_set_prop(self, Y_DIVS_KEY, 8)
-               self._register_set_prop(self, SCOPE_X_CHANNEL_KEY, 0)
-               self._register_set_prop(self, SCOPE_Y_CHANNEL_KEY, num_inputs-1)
-               self._register_set_prop(self, FRAME_RATE_KEY, frame_rate)
-               self._register_set_prop(self, TRIGGER_CHANNEL_KEY, 0)
-               self._register_set_prop(self, TRIGGER_MODE_KEY, 1)
-               self._register_set_prop(self, TRIGGER_LEVEL_KEY, None)
-               self._register_set_prop(self, MARKER_KEY, DEFAULT_MARKER_TYPE)
-               #register events
-               self.ext_controller.subscribe(msg_key, self.handle_msg)
-               for key in (
+               #initialize values
+               self[RUNNING_KEY] = True
+               for i in range(self.num_inputs):
+                       self[common.index_key(AC_COUPLE_KEY, i)] = 
self[common.index_key(AC_COUPLE_KEY, i)]
+                       self[common.index_key(MARKER_KEY, i)] = 
DEFAULT_MARKER_TYPE
+               self[XY_MARKER_KEY] = 2.0
+               self[XY_MODE_KEY] = xy_mode
+               self[X_CHANNEL_KEY] = 0
+               self[Y_CHANNEL_KEY] = self.num_inputs-1
+               self[AUTORANGE_KEY] = autorange
+               self[T_PER_DIV_KEY] = t_scale
+               self[X_PER_DIV_KEY] = v_scale
+               self[Y_PER_DIV_KEY] = v_scale
+               self[T_OFF_KEY] = 0
+               self[X_OFF_KEY] = 0
+               self[Y_OFF_KEY] = 0
+               self[T_DIVS_KEY] = 8
+               self[X_DIVS_KEY] = 8
+               self[Y_DIVS_KEY] = 8
+               self[FRAME_RATE_KEY] = frame_rate
+               self[TRIGGER_LEVEL_KEY] = 0
+               self[TRIGGER_CHANNEL_KEY] = 0
+               self[TRIGGER_MODE_KEY] = gr.gr_TRIG_MODE_AUTO
+               self[TRIGGER_SLOPE_KEY] = gr.gr_TRIG_SLOPE_POS
+               self[T_FRAC_OFF_KEY] = 0.5
+               #register events for message
+               self.subscribe(MSG_KEY, self.handle_msg)
+               #register events for grid
+               for key in [common.index_key(MARKER_KEY, i) for i in 
range(self.num_inputs)] + [
+                       TRIGGER_LEVEL_KEY, TRIGGER_MODE_KEY,
                        T_PER_DIV_KEY, X_PER_DIV_KEY, Y_PER_DIV_KEY,
                        T_OFF_KEY, X_OFF_KEY, Y_OFF_KEY,
                        T_DIVS_KEY, X_DIVS_KEY, Y_DIVS_KEY,
-                       SCOPE_XY_MODE_KEY,
-                       SCOPE_X_CHANNEL_KEY,
-                       SCOPE_Y_CHANNEL_KEY,
-                       AUTORANGE_KEY,
-                       AC_COUPLE_KEY,
-                       MARKER_KEY,
-               ): self.subscribe(key, self.update_grid)
-               #initial update, dont do this here, wait for handle_msg #HACK
-               #self.update_grid()
+                       XY_MODE_KEY, AUTORANGE_KEY, T_FRAC_OFF_KEY,
+                       TRIGGER_SHOW_KEY, XY_MARKER_KEY, X_CHANNEL_KEY, 
Y_CHANNEL_KEY,
+               ]: self.subscribe(key, self.update_grid)
+               #initial update
+               self.update_grid()
 
        def handle_msg(self, msg):
                """
@@ -345,15 +390,23 @@
                if time.time() - self.frame_rate_ts < 1.0/self[FRAME_RATE_KEY]: 
return
                #convert to floating point numbers
                samples = numpy.fromstring(msg, numpy.float32)
+               #extract the trigger offset
+               self.trigger_offset = samples[-1]
+               samples = samples[:-1]
                samps_per_ch = len(samples)/self.num_inputs
                self.sampleses = [samples[samps_per_ch*i:samps_per_ch*(i+1)] 
for i in range(self.num_inputs)]
-               if not self._init: #HACK
-                       self._init = True
-                       self.update_grid()
                #handle samples
                self.handle_samples()
                self.frame_rate_ts = time.time()
 
+       def set_auto_trigger_level(self, *args):
+               """
+               Use the current trigger channel and samples to calculate the 
50% level.
+               """
+               if not self.sampleses: return
+               samples = self.sampleses[self[TRIGGER_CHANNEL_KEY]]
+               self[TRIGGER_LEVEL_KEY] = 
(numpy.max(samples)+numpy.min(samples))/2
+
        def handle_samples(self):
                """
                Handle the cached samples from the scope input.
@@ -361,52 +414,37 @@
                """
                if not self.sampleses: return
                sampleses = self.sampleses
-               #trigger level (must do before ac coupling)
-               self.ext_controller[self.scope_trigger_channel_key] = 
self[TRIGGER_CHANNEL_KEY]
-               self.ext_controller[self.scope_trigger_mode_key] = 
self[TRIGGER_MODE_KEY]
-               trigger_level = self[TRIGGER_LEVEL_KEY]
-               if trigger_level is None: 
self.ext_controller[self.scope_trigger_level_key] = ''
-               else:
-                       samples = sampleses[self[TRIGGER_CHANNEL_KEY]]
-                       self.ext_controller[self.scope_trigger_level_key] = \
-                       trigger_level*(numpy.max(samples)-numpy.min(samples))/2 
+ numpy.average(samples)
-               #ac coupling
-               if self[AC_COUPLE_KEY]:
-                       sampleses = [samples - numpy.average(samples) for 
samples in sampleses]
-               if self[SCOPE_XY_MODE_KEY]:
-                       x_samples = sampleses[self[SCOPE_X_CHANNEL_KEY]]
-                       y_samples = sampleses[self[SCOPE_Y_CHANNEL_KEY]]
+               if self[XY_MODE_KEY]:
+                       self[DECIMATION_KEY] = 1
+                       x_samples = sampleses[self[X_CHANNEL_KEY]]
+                       y_samples = sampleses[self[Y_CHANNEL_KEY]]
                        #autorange
                        if self[AUTORANGE_KEY] and time.time() - 
self.autorange_ts > AUTORANGE_UPDATE_RATE:
                                x_min, x_max = common.get_min_max(x_samples)
                                y_min, y_max = common.get_min_max(y_samples)
                                #adjust the x per div
                                x_per_div = 
common.get_clean_num((x_max-x_min)/self[X_DIVS_KEY])
-                               if x_per_div != self[X_PER_DIV_KEY]: 
self.set_x_per_div(x_per_div)
+                               if x_per_div != self[X_PER_DIV_KEY]: 
self[X_PER_DIV_KEY] = x_per_div; return
                                #adjust the x offset
                                x_off = 
x_per_div*round((x_max+x_min)/2/x_per_div)
-                               if x_off != self[X_OFF_KEY]: 
self.set_x_off(x_off)
+                               if x_off != self[X_OFF_KEY]: self[X_OFF_KEY] = 
x_off; return
                                #adjust the y per div
                                y_per_div = 
common.get_clean_num((y_max-y_min)/self[Y_DIVS_KEY])
-                               if y_per_div != self[Y_PER_DIV_KEY]: 
self.set_y_per_div(y_per_div)
+                               if y_per_div != self[Y_PER_DIV_KEY]: 
self[Y_PER_DIV_KEY] = y_per_div; return
                                #adjust the y offset
                                y_off = 
y_per_div*round((y_max+y_min)/2/y_per_div)
-                               if y_off != self[Y_OFF_KEY]: 
self.set_y_off(y_off)
+                               if y_off != self[Y_OFF_KEY]: self[Y_OFF_KEY] = 
y_off; return
                                self.autorange_ts = time.time()
                        #plot xy channel
                        self.plotter.set_waveform(
                                channel='XY',
                                samples=(x_samples, y_samples),
                                color_spec=CHANNEL_COLOR_SPECS[0],
-                               marker=self[MARKER_KEY],
+                               marker=self[XY_MARKER_KEY],
                        )
                        #turn off each waveform
                        for i, samples in enumerate(sampleses):
-                               self.plotter.set_waveform(
-                                       channel='Ch%d'%(i+1),
-                                       samples=[],
-                                       color_spec=CHANNEL_COLOR_SPECS[i],
-                               )
+                               
self.plotter.clear_waveform(channel='Ch%d'%(i+1))
                else:
                        #autorange
                        if self[AUTORANGE_KEY] and time.time() - 
self.autorange_ts > AUTORANGE_UPDATE_RATE:
@@ -415,86 +453,89 @@
                                y_max = numpy.max([bound[1] for bound in 
bounds])
                                #adjust the y per div
                                y_per_div = 
common.get_clean_num((y_max-y_min)/self[Y_DIVS_KEY])
-                               if y_per_div != self[Y_PER_DIV_KEY]: 
self.set_y_per_div(y_per_div)
+                               if y_per_div != self[Y_PER_DIV_KEY]: 
self[Y_PER_DIV_KEY] = y_per_div; return
                                #adjust the y offset
                                y_off = 
y_per_div*round((y_max+y_min)/2/y_per_div)
-                               if y_off != self[Y_OFF_KEY]: 
self.set_y_off(y_off)
+                               if y_off != self[Y_OFF_KEY]: self[Y_OFF_KEY] = 
y_off; return
                                self.autorange_ts = time.time()
-                       #plot each waveform
-                       for i, samples in enumerate(sampleses):
-                               #number of samples to scale to the screen
-                               num_samps = 
int(self[T_PER_DIV_KEY]*self[T_DIVS_KEY]*self.ext_controller[self.sample_rate_key])
-                               #handle num samps out of bounds
-                               if num_samps > len(samples):
-                                       self.set_t_per_div(
-                                               
common.get_clean_decr(self[T_PER_DIV_KEY]))
-                               elif num_samps < 2:
-                                       self.set_t_per_div(
-                                               
common.get_clean_incr(self[T_PER_DIV_KEY]))
-                                       num_samps = 0
-                               else:
+                       #number of samples to scale to the screen
+                       actual_rate = self.get_actual_rate()
+                       time_span = self[T_PER_DIV_KEY]*self[T_DIVS_KEY]
+                       num_samps = int(round(time_span*actual_rate))
+                       #handle the time offset
+                       t_off = 
self[T_FRAC_OFF_KEY]*(len(sampleses[0])/actual_rate - time_span)
+                       if t_off != self[T_OFF_KEY]: self[T_OFF_KEY] = t_off; 
return
+                       samps_off = int(round(actual_rate*self[T_OFF_KEY]))
+                       #adjust the decim so that we use about half the samps
+                       self[DECIMATION_KEY] = int(round(
+                                       
time_span*self[SAMPLE_RATE_KEY]/(0.5*len(sampleses[0]))
+                               )
+                       )
+                       #num samps too small, auto increment the time
+                       if num_samps < 2: self[T_PER_DIV_KEY] = 
common.get_clean_incr(self[T_PER_DIV_KEY])
+                       #num samps in bounds, plot each waveform
+                       elif num_samps <= len(sampleses[0]):
+                               for i, samples in enumerate(sampleses):
                                        #plot samples
                                        self.plotter.set_waveform(
                                                channel='Ch%d'%(i+1),
-                                               samples=samples[:num_samps],
+                                               
samples=samples[samps_off:num_samps+samps_off],
                                                
color_spec=CHANNEL_COLOR_SPECS[i],
-                                               marker=self[MARKER_KEY],
+                                               
marker=self[common.index_key(MARKER_KEY, i)],
+                                               trig_off=self.trigger_offset,
                                        )
                        #turn XY channel off
+                       self.plotter.clear_waveform(channel='XY')
+               #keep trigger level within range
+               if self[TRIGGER_LEVEL_KEY] > self.get_y_max():
+                       self[TRIGGER_LEVEL_KEY] = self.get_y_max(); return
+               if self[TRIGGER_LEVEL_KEY] < self.get_y_min():
+                       self[TRIGGER_LEVEL_KEY] = self.get_y_min(); return
+               #disable the trigger channel
+               if not self[TRIGGER_SHOW_KEY] or self[XY_MODE_KEY] or 
self[TRIGGER_MODE_KEY] == gr.gr_TRIG_MODE_FREE:
+                       self.plotter.clear_waveform(channel='Trig')
+               else: #show trigger channel
+                       trigger_level = self[TRIGGER_LEVEL_KEY]
+                       trigger_point = 
(len(self.sampleses[0])-1)/self.get_actual_rate()/2.0
                        self.plotter.set_waveform(
-                               channel='XY',
-                               samples=[],
-                               color_spec=CHANNEL_COLOR_SPECS[0],
+                               channel='Trig',
+                               samples=(
+                                       [self.get_t_min(), trigger_point, 
trigger_point, trigger_point, trigger_point, self.get_t_max()],
+                                       [trigger_level, trigger_level, 
self.get_y_max(), self.get_y_min(), trigger_level, trigger_level]
+                               ),
+                               color_spec=TRIGGER_COLOR_SPEC,
                        )
                #update the plotter
                self.plotter.update()
 
+       def get_actual_rate(self): return 
1.0*self[SAMPLE_RATE_KEY]/self[DECIMATION_KEY]
+       def get_t_min(self): return self[T_OFF_KEY]
+       def get_t_max(self): return self[T_PER_DIV_KEY]*self[T_DIVS_KEY] + 
self[T_OFF_KEY]
+       def get_x_min(self): return -1*self[X_PER_DIV_KEY]*self[X_DIVS_KEY]/2.0 
+ self[X_OFF_KEY]
+       def get_x_max(self): return self[X_PER_DIV_KEY]*self[X_DIVS_KEY]/2.0 + 
self[X_OFF_KEY]
+       def get_y_min(self): return -1*self[Y_PER_DIV_KEY]*self[Y_DIVS_KEY]/2.0 
+ self[Y_OFF_KEY]
+       def get_y_max(self): return self[Y_PER_DIV_KEY]*self[Y_DIVS_KEY]/2.0 + 
self[Y_OFF_KEY]
+
        def update_grid(self, *args):
                """
                Update the grid to reflect the current settings:
                xy divisions, xy offset, xy mode setting
                """
-               #grid parameters
-               t_per_div = self[T_PER_DIV_KEY]
-               x_per_div = self[X_PER_DIV_KEY]
-               y_per_div = self[Y_PER_DIV_KEY]
-               t_off = self[T_OFF_KEY]
-               x_off = self[X_OFF_KEY]
-               y_off = self[Y_OFF_KEY]
-               t_divs = self[T_DIVS_KEY]
-               x_divs = self[X_DIVS_KEY]
-               y_divs = self[Y_DIVS_KEY]
-               if self[SCOPE_XY_MODE_KEY]:
+               if self[T_FRAC_OFF_KEY] < 0: self[T_FRAC_OFF_KEY] = 0; return
+               if self[T_FRAC_OFF_KEY] > 1: self[T_FRAC_OFF_KEY] = 1; return
+               if self[XY_MODE_KEY]:
                        #update the x axis
-                       
self.plotter.set_x_label('Ch%d'%(self[SCOPE_X_CHANNEL_KEY]+1))
-                       self.plotter.set_x_grid(
-                               -1*x_per_div*x_divs/2.0 + x_off,
-                               x_per_div*x_divs/2.0 + x_off,
-                               x_per_div,
-                       )
+                       self.plotter.set_x_label('Ch%d'%(self[X_CHANNEL_KEY]+1))
+                       self.plotter.set_x_grid(self.get_x_min(), 
self.get_x_max(), self[X_PER_DIV_KEY])
                        #update the y axis
-                       
self.plotter.set_y_label('Ch%d'%(self[SCOPE_Y_CHANNEL_KEY]+1))
-                       self.plotter.set_y_grid(
-                               -1*y_per_div*y_divs/2.0 + y_off,
-                               y_per_div*y_divs/2.0 + y_off,
-                               y_per_div,
-                       )
+                       self.plotter.set_y_label('Ch%d'%(self[Y_CHANNEL_KEY]+1))
+                       self.plotter.set_y_grid(self.get_y_min(), 
self.get_y_max(), self[Y_PER_DIV_KEY])
                else:
                        #update the t axis
-                       coeff, exp, prefix = 
common.get_si_components(t_per_div*t_divs + t_off)
-                       self.plotter.set_x_label('Time', prefix+'s')
-                       self.plotter.set_x_grid(
-                               t_off,
-                               t_per_div*t_divs + t_off,
-                               t_per_div,
-                               10**(-exp),
-                       )
+                       self.plotter.set_x_label('Time', 's')
+                       self.plotter.set_x_grid(self.get_t_min(), 
self.get_t_max(), self[T_PER_DIV_KEY], True)
                        #update the y axis
                        self.plotter.set_y_label('Counts')
-                       self.plotter.set_y_grid(
-                               -1*y_per_div*y_divs/2.0 + y_off,
-                               y_per_div*y_divs/2.0 + y_off,
-                               y_per_div,
-                       )
+                       self.plotter.set_y_grid(self.get_y_min(), 
self.get_y_max(), self[Y_PER_DIV_KEY])
                #redraw current sample
                self.handle_samples()

Modified: gnuradio/trunk/gr-wxgui/src/python/scopesink2.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/scopesink2.py    2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/scopesink2.py    2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -38,7 +38,7 @@
     except ImportError:
         raise RuntimeError("Unable to import OpenGL. Are Python wrappers for 
OpenGL installed?")
 
-    from scopesink_gl import scope_sink_f, scope_sink_c, constellation_sink
+    from scopesink_gl import scope_sink_f, scope_sink_c
 
 else:
-    from scopesink_nongl import scope_sink_f, scope_sink_c, constellation_sink
+    from scopesink_nongl import scope_sink_f, scope_sink_c

Modified: gnuradio/trunk/gr-wxgui/src/python/scopesink_gl.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/scopesink_gl.py  2009-03-20 01:48:45 UTC 
(rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/scopesink_gl.py  2009-03-20 02:16:20 UTC 
(rev 10660)
@@ -28,10 +28,38 @@
 from pubsub import pubsub
 from constants import *
 
+class ac_couple_block(gr.hier_block2):
+       """
+       AC couple the incoming stream by subtracting out the low pass signal.
+       Mute the low pass filter to disable ac coupling.
+       """
+
+       def __init__(self, controller, ac_couple_key, ac_couple, 
sample_rate_key):
+               gr.hier_block2.__init__(
+                       self,
+                       "ac_couple",
+                       gr.io_signature(1, 1, gr.sizeof_float),
+                       gr.io_signature(1, 1, gr.sizeof_float),
+               )
+               #blocks
+               copy = gr.kludge_copy(gr.sizeof_float)
+               lpf = gr.single_pole_iir_filter_ff(0.0)
+               sub = gr.sub_ff()
+               mute = gr.mute_ff()
+               #connect
+               self.connect(self, copy, sub, self)
+               self.connect(copy, lpf, mute, (sub, 1))
+               #subscribe
+               controller.subscribe(ac_couple_key, lambda x: mute.set_mute(not 
x))
+               controller.subscribe(sample_rate_key, lambda x: 
lpf.set_taps(2.0/x))
+               #initialize
+               controller[ac_couple_key] = ac_couple
+               controller[sample_rate_key] = controller[sample_rate_key]
+
 ##################################################
 # Scope sink block (wrapper for old wxgui)
 ##################################################
-class _scope_sink_base(gr.hier_block2, common.prop_setter):
+class _scope_sink_base(gr.hier_block2):
        """
        A scope block with a gui window.
        """
@@ -42,15 +70,14 @@
                title='',
                sample_rate=1,
                size=scope_window.DEFAULT_WIN_SIZE,
-               frame_decim=None, #ignore (old wrapper)
-               v_scale=scope_window.DEFAULT_V_SCALE,
-               t_scale=None,
+               v_scale=0,
+               t_scale=0,
+               xy_mode=False,
+               ac_couple=False,
                num_inputs=1,
-               ac_couple=False,
-               xy_mode=False,
                frame_rate=scope_window.DEFAULT_FRAME_RATE,
        ):
-               if t_scale is None: t_scale = 0.001
+               if not t_scale: t_scale = 10.0/sample_rate
                #init
                gr.hier_block2.__init__(
                        self,
@@ -61,37 +88,41 @@
                #scope
                msgq = gr.msg_queue(2)
                scope = gr.oscope_sink_f(sample_rate, msgq)
+               #controller
+               self.controller = pubsub()
+               self.controller.subscribe(SAMPLE_RATE_KEY, 
scope.set_sample_rate)
+               self.controller.publish(SAMPLE_RATE_KEY, scope.sample_rate)
+               self.controller.subscribe(DECIMATION_KEY, 
scope.set_decimation_count)
+               self.controller.publish(DECIMATION_KEY, 
scope.get_decimation_count)
+               self.controller.subscribe(TRIGGER_LEVEL_KEY, 
scope.set_trigger_level)
+               self.controller.publish(TRIGGER_LEVEL_KEY, 
scope.get_trigger_level)
+               self.controller.subscribe(TRIGGER_MODE_KEY, 
scope.set_trigger_mode)
+               self.controller.publish(TRIGGER_MODE_KEY, 
scope.get_trigger_mode)
+               self.controller.subscribe(TRIGGER_SLOPE_KEY, 
scope.set_trigger_slope)
+               self.controller.publish(TRIGGER_SLOPE_KEY, 
scope.get_trigger_slope)
+               self.controller.subscribe(TRIGGER_CHANNEL_KEY, 
scope.set_trigger_channel)
+               self.controller.publish(TRIGGER_CHANNEL_KEY, 
scope.get_trigger_channel)
                #connect
                if self._real:
                        for i in range(num_inputs):
-                               self.connect((self, i), (scope, i))
+                               self.connect(
+                                       (self, i),
+                                       ac_couple_block(self.controller, 
common.index_key(AC_COUPLE_KEY, i), ac_couple, SAMPLE_RATE_KEY),
+                                       (scope, i),
+                               )
                else:
                        for i in range(num_inputs):
                                c2f = gr.complex_to_float() 
                                self.connect((self, i), c2f)
-                               self.connect((c2f, 0), (scope, 2*i+0))
-                               self.connect((c2f, 1), (scope, 2*i+1))
+                               for j in range(2):
+                                       self.connect(
+                                               (c2f, j), 
+                                               
ac_couple_block(self.controller, common.index_key(AC_COUPLE_KEY, 2*i+j), 
ac_couple, SAMPLE_RATE_KEY),
+                                               (scope, 2*i+j),
+                                       )
                        num_inputs *= 2
-               #controller
-               self.controller = pubsub()
-               self.controller.subscribe(SAMPLE_RATE_KEY, 
scope.set_sample_rate)
-               self.controller.publish(SAMPLE_RATE_KEY, scope.sample_rate)
-               def set_trigger_level(level):
-                       if level == '': scope.set_trigger_level_auto()
-                       else: scope.set_trigger_level(level)
-               self.controller.subscribe(SCOPE_TRIGGER_LEVEL_KEY, 
set_trigger_level)
-               def set_trigger_mode(mode):
-                       if mode == 0: mode = gr.gr_TRIG_AUTO
-                       elif mode < 0: mode = gr.gr_TRIG_NEG_SLOPE
-                       elif mode > 0: mode = gr.gr_TRIG_POS_SLOPE
-                       else: return
-                       scope.set_trigger_mode(mode)
-               self.controller.subscribe(SCOPE_TRIGGER_MODE_KEY, 
set_trigger_mode)
-               self.controller.subscribe(SCOPE_TRIGGER_CHANNEL_KEY, 
scope.set_trigger_channel)
                #start input watcher
-               def setter(p, k, x): # lambdas can't have assignments :(
-                   p[k] = x
-               common.input_watcher(msgq, lambda x: setter(self.controller, 
MSG_KEY, x))
+               common.input_watcher(msgq, self.controller, MSG_KEY)
                #create window
                self.win = scope_window.scope_window(
                        parent=parent,
@@ -103,21 +134,16 @@
                        sample_rate_key=SAMPLE_RATE_KEY,
                        t_scale=t_scale,
                        v_scale=v_scale,
-                       ac_couple=ac_couple,
                        xy_mode=xy_mode,
-                       scope_trigger_level_key=SCOPE_TRIGGER_LEVEL_KEY,
-                       scope_trigger_mode_key=SCOPE_TRIGGER_MODE_KEY,
-                       scope_trigger_channel_key=SCOPE_TRIGGER_CHANNEL_KEY,
+                       ac_couple_key=AC_COUPLE_KEY,
+                       trigger_level_key=TRIGGER_LEVEL_KEY,
+                       trigger_mode_key=TRIGGER_MODE_KEY,
+                       trigger_slope_key=TRIGGER_SLOPE_KEY,
+                       trigger_channel_key=TRIGGER_CHANNEL_KEY,
+                       decimation_key=DECIMATION_KEY,
                        msg_key=MSG_KEY,
                )
-               #register callbacks from window for external use
-               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
-                       setattr(self, attr, getattr(self.win, attr))
-               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
-               #backwards compadibility
-               self.win.set_format_line = lambda: setter(self.win, MARKER_KEY, 
None)
-               self.win.set_format_dot = lambda: setter(self.win, MARKER_KEY, 
2.0)
-               self.win.set_format_plus =  lambda: setter(self.win, 
MARKER_KEY, 3.0)
+               common.register_access_methods(self, self.win)
 
 class scope_sink_f(_scope_sink_base):
        _item_size = gr.sizeof_float
@@ -127,12 +153,6 @@
        _item_size = gr.sizeof_gr_complex
        _real = False
 
-#backwards compadible wrapper (maybe only grc uses this)
-class constellation_sink(scope_sink_c):
-       def __init__(self, *args, **kwargs):
-               scope_sink_c.__init__(self, *args, **kwargs)
-               self.set_scope_xy_mode(True)
-
 # ----------------------------------------------------------------
 # Stand-alone test application
 # ----------------------------------------------------------------
@@ -171,7 +191,6 @@
         self.thr = gr.throttle(gr.sizeof_gr_complex, input_rate)
 
         scope = scope_sink_c (panel,"Secret Data",sample_rate=input_rate,
-                              frame_decim=frame_decim,
                               v_scale=v_scale, t_scale=t_scale)
         vbox.Add (scope.win, 1, wx.EXPAND)
 

Modified: gnuradio/trunk/gr-wxgui/src/python/scopesink_nongl.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/scopesink_nongl.py       2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/scopesink_nongl.py       2009-03-20 
02:16:20 UTC (rev 10660)
@@ -35,7 +35,7 @@
 class scope_sink_f(gr.hier_block2):
     def __init__(self, parent, title='', sample_rate=1,
                  size=default_scopesink_size, frame_decim=default_frame_decim,
-                 v_scale=default_v_scale, t_scale=None, num_inputs=1):
+                 v_scale=default_v_scale, t_scale=None, num_inputs=1, 
**kwargs):
 
         gr.hier_block2.__init__(self, "scope_sink_f",
                                 gr.io_signature(num_inputs, num_inputs, 
gr.sizeof_float),
@@ -56,7 +56,7 @@
 class scope_sink_c(gr.hier_block2):
     def __init__(self, parent, title='', sample_rate=1,
                  size=default_scopesink_size, frame_decim=default_frame_decim,
-                 v_scale=default_v_scale, t_scale=None, num_inputs=1):
+                 v_scale=default_v_scale, t_scale=None, num_inputs=1, 
**kwargs):
 
         gr.hier_block2.__init__(self, "scope_sink_c",
                                 gr.io_signature(num_inputs, num_inputs, 
gr.sizeof_gr_complex),
@@ -167,10 +167,7 @@
 
         self.marker = 'line'
         self.xy = xy
-        if v_scale == None:        # 0 and None are both False, but 0 != None
-            self.autorange = True
-        else:
-            self.autorange = False # 0 is a valid v_scale            
+        self.autorange = not v_scale
         self.running = True
 
     def get_time_per_div (self):
@@ -320,7 +317,8 @@
         ctrlbox.Add (self.trig_chan_choice, 0, wx.ALIGN_CENTER)
 
         self.trig_mode_choice = wx.Choice (self, 1005,
-                                           choices = ['Auto', 'Pos', 'Neg'])
+                                           choices = ['Free', 'Auto', 'Norm'])
+        self.trig_mode_choice.SetSelection(1)
         self.trig_mode_choice.SetToolTipString ("Select trigger slope or Auto 
(untriggered roll)")
         wx.EVT_CHOICE (self, 1005, self.trig_mode_choice_event)
         ctrlbox.Add (self.trig_mode_choice, 0, wx.ALIGN_CENTER)
@@ -432,12 +430,12 @@
     def trig_mode_choice_event (self, evt):
         sink = self.info.scopesink
         s = evt.GetString ()
-        if s == 'Pos':
-            sink.set_trigger_mode (gr.gr_TRIG_POS_SLOPE)
-        elif s == 'Neg':
-            sink.set_trigger_mode (gr.gr_TRIG_NEG_SLOPE)
+        if s == 'Norm':
+            sink.set_trigger_mode (gr.gr_TRIG_MODE_NORM)
         elif s == 'Auto':
-            sink.set_trigger_mode (gr.gr_TRIG_AUTO)
+            sink.set_trigger_mode (gr.gr_TRIG_MODE_AUTO)
+        elif s == 'Free':
+            sink.set_trigger_mode (gr.gr_TRIG_MODE_FREE)
         else:
             assert 0, "Bad trig_mode_choice string"
     

Modified: gnuradio/trunk/gr-wxgui/src/python/waterfall_window.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/waterfall_window.py      2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/waterfall_window.py      2009-03-20 
02:16:20 UTC (rev 10660)
@@ -16,7 +16,7 @@
 # You should have received a copy of the GNU General Public License
 # along with GNU Radio; see the file COPYING.  If not, write to
 # the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
+# Boston, MA 02110-1`301, USA.
 #
 
 ##################################################
@@ -61,57 +61,57 @@
                @param parent the wx parent window
                """
                self.parent = parent
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
                control_box = wx.BoxSizer(wx.VERTICAL)
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
                #color mode
                control_box.AddStretchSpacer()
-               self.color_mode_chooser = common.DropDownController(self, 
'Color', COLOR_MODES, parent, COLOR_MODE_KEY)
-               control_box.Add(self.color_mode_chooser, 0, wx.EXPAND)
+               color_mode_chooser = common.DropDownController(self, 
COLOR_MODES, parent, COLOR_MODE_KEY)
+               control_box.Add(common.LabelBox(self, 'Color', 
color_mode_chooser), 0, wx.EXPAND)
                #average
                control_box.AddStretchSpacer()
-               self.average_check_box = common.CheckBoxController(self, 
'Average', parent.ext_controller, parent.average_key)
-               control_box.Add(self.average_check_box, 0, wx.EXPAND)
+               average_check_box = common.CheckBoxController(self, 'Average', 
parent, AVERAGE_KEY)
+               control_box.Add(average_check_box, 0, wx.EXPAND)
                control_box.AddSpacer(2)
-               self.avg_alpha_slider = common.LogSliderController(
+               avg_alpha_slider = common.LogSliderController(
                        self, 'Avg Alpha',
                        AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
-                       parent.ext_controller, parent.avg_alpha_key,
+                       parent, AVG_ALPHA_KEY,
                        formatter=lambda x: ': %.4f'%x,
                )
-               parent.ext_controller.subscribe(parent.average_key, 
self.avg_alpha_slider.Enable)
-               control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+               parent.subscribe(AVERAGE_KEY, avg_alpha_slider.Enable)
+               control_box.Add(avg_alpha_slider, 0, wx.EXPAND)
                #dyanmic range buttons
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Dynamic Range'), 0, 
wx.ALIGN_CENTER)
                control_box.AddSpacer(2)
-               self._dynamic_range_buttons = common.IncrDecrButtons(self, 
self._on_incr_dynamic_range, self._on_decr_dynamic_range)
-               control_box.Add(self._dynamic_range_buttons, 0, wx.ALIGN_CENTER)
+               dynamic_range_buttons = common.IncrDecrButtons(self, 
self._on_incr_dynamic_range, self._on_decr_dynamic_range)
+               control_box.Add(dynamic_range_buttons, 0, wx.ALIGN_CENTER)
                #ref lvl buttons
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Set Ref Level'), 0, 
wx.ALIGN_CENTER)
                control_box.AddSpacer(2)
-               self._ref_lvl_buttons = common.IncrDecrButtons(self, 
self._on_incr_ref_level, self._on_decr_ref_level)
-               control_box.Add(self._ref_lvl_buttons, 0, wx.ALIGN_CENTER)
+               ref_lvl_buttons = common.IncrDecrButtons(self, 
self._on_incr_ref_level, self._on_decr_ref_level)
+               control_box.Add(ref_lvl_buttons, 0, wx.ALIGN_CENTER)
                #num lines buttons
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Set Time Scale'), 0, 
wx.ALIGN_CENTER)
                control_box.AddSpacer(2)
-               self._time_scale_buttons = common.IncrDecrButtons(self, 
self._on_incr_time_scale, self._on_decr_time_scale)
-               control_box.Add(self._time_scale_buttons, 0, wx.ALIGN_CENTER)
+               time_scale_buttons = common.IncrDecrButtons(self, 
self._on_incr_time_scale, self._on_decr_time_scale)
+               control_box.Add(time_scale_buttons, 0, wx.ALIGN_CENTER)
                #autoscale
                control_box.AddStretchSpacer()
-               self.autoscale_button = wx.Button(self, label='Autoscale', 
style=wx.BU_EXACTFIT)
-               self.autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
-               control_box.Add(self.autoscale_button, 0, wx.EXPAND)
+               autoscale_button = wx.Button(self, label='Autoscale', 
style=wx.BU_EXACTFIT)
+               autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
+               control_box.Add(autoscale_button, 0, wx.EXPAND)
                #clear
-               self.clear_button = wx.Button(self, label='Clear', 
style=wx.BU_EXACTFIT)
-               self.clear_button.Bind(wx.EVT_BUTTON, self._on_clear_button)
-               control_box.Add(self.clear_button, 0, wx.EXPAND)
+               clear_button = wx.Button(self, label='Clear', 
style=wx.BU_EXACTFIT)
+               clear_button.Bind(wx.EVT_BUTTON, self._on_clear_button)
+               control_box.Add(clear_button, 0, wx.EXPAND)
                #run/stop
-               self.run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
-               control_box.Add(self.run_button, 0, wx.EXPAND)
+               run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
+               control_box.Add(run_button, 0, wx.EXPAND)
                #set sizer
                self.SetSizerAndFit(control_box)
 
@@ -119,34 +119,30 @@
        # Event handlers
        ##################################################
        def _on_clear_button(self, event):
-               self.parent.set_num_lines(self.parent[NUM_LINES_KEY])
+               self.parent[NUM_LINES_KEY] = self.parent[NUM_LINES_KEY]
        def _on_incr_dynamic_range(self, event):
-               self.parent.set_dynamic_range(
-                       min(self.parent[DYNAMIC_RANGE_KEY] + 10, 
MAX_DYNAMIC_RANGE))
+               self.parent[DYNAMIC_RANGE_KEY] = 
min(self.parent[DYNAMIC_RANGE_KEY] + 10, MAX_DYNAMIC_RANGE)
        def _on_decr_dynamic_range(self, event):
-               self.parent.set_dynamic_range(
-                       max(self.parent[DYNAMIC_RANGE_KEY] - 10, 
MIN_DYNAMIC_RANGE))
+               self.parent[DYNAMIC_RANGE_KEY] = 
max(self.parent[DYNAMIC_RANGE_KEY] - 10, MIN_DYNAMIC_RANGE)
        def _on_incr_ref_level(self, event):
-               self.parent.set_ref_level(
-                       self.parent[REF_LEVEL_KEY] + 
self.parent[DYNAMIC_RANGE_KEY]*.1)
+               self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] + 
self.parent[DYNAMIC_RANGE_KEY]*.1
        def _on_decr_ref_level(self, event):
-               self.parent.set_ref_level(
-                       self.parent[REF_LEVEL_KEY] - 
self.parent[DYNAMIC_RANGE_KEY]*.1)
+               self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] - 
self.parent[DYNAMIC_RANGE_KEY]*.1
        def _on_incr_time_scale(self, event):
-               old_rate = 
self.parent.ext_controller[self.parent.frame_rate_key]
-               self.parent.ext_controller[self.parent.frame_rate_key] *= 0.75
-               if self.parent.ext_controller[self.parent.frame_rate_key] == 
old_rate:
-                       self.parent.ext_controller[self.parent.decimation_key] 
+= 1
+               old_rate = self.parent[FRAME_RATE_KEY]
+               self.parent[FRAME_RATE_KEY] *= 0.75
+               if self.parent[FRAME_RATE_KEY] == old_rate:
+                       self.parent[DECIMATION_KEY] += 1
        def _on_decr_time_scale(self, event):
-               old_rate = 
self.parent.ext_controller[self.parent.frame_rate_key]
-               self.parent.ext_controller[self.parent.frame_rate_key] *= 1.25
-               if self.parent.ext_controller[self.parent.frame_rate_key] == 
old_rate:
-                       self.parent.ext_controller[self.parent.decimation_key] 
-= 1
+               old_rate = self.parent[FRAME_RATE_KEY]
+               self.parent[FRAME_RATE_KEY] *= 1.25
+               if self.parent[FRAME_RATE_KEY] == old_rate:
+                       self.parent[DECIMATION_KEY] -= 1
 
 ##################################################
 # Waterfall window with plotter and control panel
 ##################################################
-class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+class waterfall_window(wx.Panel, pubsub.pubsub):
        def __init__(
                self,
                parent,
@@ -169,20 +165,22 @@
                pubsub.pubsub.__init__(self)
                #setup
                self.samples = list()
-               self.ext_controller = controller
                self.real = real
                self.fft_size = fft_size
-               self.decimation_key = decimation_key
-               self.sample_rate_key = sample_rate_key
-               self.frame_rate_key = frame_rate_key
-               self.average_key = average_key
-               self.avg_alpha_key = avg_alpha_key
+               #proxy the keys
+               self.proxy(MSG_KEY, controller, msg_key)
+               self.proxy(DECIMATION_KEY, controller, decimation_key)
+               self.proxy(FRAME_RATE_KEY, controller, frame_rate_key)
+               self.proxy(AVERAGE_KEY, controller, average_key)
+               self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
+               self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
                #init panel and plot
-               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
                self.plotter = plotter.waterfall_plotter(self)
                self.plotter.SetSize(wx.Size(*size))
                self.plotter.set_title(title)
                self.plotter.enable_point_label(True)
+               self.plotter.enable_grid_lines(False)
                #setup the box with plot and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
@@ -192,26 +190,23 @@
                #plotter listeners
                self.subscribe(COLOR_MODE_KEY, self.plotter.set_color_mode)
                self.subscribe(NUM_LINES_KEY, self.plotter.set_num_lines)
-               #initial setup
-               self.ext_controller[self.average_key] = 
self.ext_controller[self.average_key]
-               self.ext_controller[self.avg_alpha_key] = 
self.ext_controller[self.avg_alpha_key]
-               self._register_set_prop(self, DYNAMIC_RANGE_KEY, dynamic_range)
-               self._register_set_prop(self, NUM_LINES_KEY, num_lines)
-               self._register_set_prop(self, Y_DIVS_KEY, 8)
-               self._register_set_prop(self, X_DIVS_KEY, 8) #approximate
-               self._register_set_prop(self, REF_LEVEL_KEY, ref_level)
-               self._register_set_prop(self, BASEBAND_FREQ_KEY, baseband_freq)
-               self._register_set_prop(self, COLOR_MODE_KEY, COLOR_MODES[0][1])
-               self._register_set_prop(self, RUNNING_KEY, True)
+               #initialize values
+               self[AVERAGE_KEY] = self[AVERAGE_KEY]
+               self[AVG_ALPHA_KEY] = self[AVG_ALPHA_KEY]
+               self[DYNAMIC_RANGE_KEY] = dynamic_range
+               self[NUM_LINES_KEY] = num_lines
+               self[Y_DIVS_KEY] = 8
+               self[X_DIVS_KEY] = 8 #approximate
+               self[REF_LEVEL_KEY] = ref_level
+               self[BASEBAND_FREQ_KEY] = baseband_freq
+               self[COLOR_MODE_KEY] = COLOR_MODES[0][1]
+               self[RUNNING_KEY] = True
                #register events
-               self.ext_controller.subscribe(msg_key, self.handle_msg)
-               self.ext_controller.subscribe(self.decimation_key, 
self.update_grid)
-               self.ext_controller.subscribe(self.sample_rate_key, 
self.update_grid)
-               self.ext_controller.subscribe(self.frame_rate_key, 
self.update_grid)
-               self.subscribe(BASEBAND_FREQ_KEY, self.update_grid)
-               self.subscribe(NUM_LINES_KEY, self.update_grid)
-               self.subscribe(Y_DIVS_KEY, self.update_grid)
-               self.subscribe(X_DIVS_KEY, self.update_grid)
+               self.subscribe(MSG_KEY, self.handle_msg)
+               for key in (
+                       DECIMATION_KEY, SAMPLE_RATE_KEY, FRAME_RATE_KEY,
+                       BASEBAND_FREQ_KEY, X_DIVS_KEY, Y_DIVS_KEY, 
NUM_LINES_KEY,
+               ): self.subscribe(key, self.update_grid)
                #initial update
                self.update_grid()
 
@@ -230,8 +225,8 @@
                noise_floor -= abs(noise_floor)*.5
                peak_level += abs(peak_level)*.1
                #set the range and level
-               self.set_ref_level(peak_level)
-               self.set_dynamic_range(peak_level - noise_floor)
+               self[REF_LEVEL_KEY] = peak_level
+               self[DYNAMIC_RANGE_KEY] = peak_level - noise_floor
 
        def handle_msg(self, msg):
                """
@@ -266,8 +261,8 @@
                The y axis depends on y per div, y divs, and ref level.
                """
                #grid parameters
-               sample_rate = self.ext_controller[self.sample_rate_key]
-               frame_rate = self.ext_controller[self.frame_rate_key]
+               sample_rate = self[SAMPLE_RATE_KEY]
+               frame_rate = self[FRAME_RATE_KEY]
                baseband_freq = self[BASEBAND_FREQ_KEY]
                num_lines = self[NUM_LINES_KEY]
                y_divs = self[Y_DIVS_KEY]
@@ -276,28 +271,25 @@
                if self.real: x_width = sample_rate/2.0
                else: x_width = sample_rate/1.0
                x_per_div = common.get_clean_num(x_width/x_divs)
-               coeff, exp, prefix = 
common.get_si_components(abs(baseband_freq) + abs(sample_rate/2.0))
                #update the x grid
                if self.real:
                        self.plotter.set_x_grid(
                                baseband_freq,
                                baseband_freq + sample_rate/2.0,
-                               x_per_div,
-                               10**(-exp),
+                               x_per_div, True,
                        )
                else:
                        self.plotter.set_x_grid(
                                baseband_freq - sample_rate/2.0,
                                baseband_freq + sample_rate/2.0,
-                               x_per_div,
-                               10**(-exp),
+                               x_per_div, True,
                        )
                #update x units
-               self.plotter.set_x_label('Frequency', prefix+'Hz')
+               self.plotter.set_x_label('Frequency', 'Hz')
                #update y grid
                duration = float(num_lines)/frame_rate
                y_per_div = common.get_clean_num(duration/y_divs)
-               self.plotter.set_y_grid(0, duration, y_per_div)
+               self.plotter.set_y_grid(0, duration, y_per_div, True)
                #update y units
                self.plotter.set_y_label('Time', 's')
                #update plotter

Modified: gnuradio/trunk/gr-wxgui/src/python/waterfallsink_gl.py
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/waterfallsink_gl.py      2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/gr-wxgui/src/python/waterfallsink_gl.py      2009-03-20 
02:16:20 UTC (rev 10660)
@@ -31,7 +31,7 @@
 ##################################################
 # Waterfall sink block (wrapper for old wxgui)
 ##################################################
-class _waterfall_sink_base(gr.hier_block2, common.prop_setter):
+class _waterfall_sink_base(gr.hier_block2):
        """
        An fft block with real/complex inputs and a gui window.
        """
@@ -89,9 +89,7 @@
                self.controller.subscribe(FRAME_RATE_KEY, fft.set_vec_rate)
                self.controller.publish(FRAME_RATE_KEY, fft.frame_rate)
                #start input watcher
-               def setter(p, k, x): # lambdas can't have assignments :(
-                       p[k] = x
-               common.input_watcher(msgq, lambda x: setter(self.controller, 
MSG_KEY, x))
+               common.input_watcher(msgq, self.controller, MSG_KEY)
                #create window
                self.win = waterfall_window.waterfall_window(
                        parent=parent,
@@ -111,12 +109,8 @@
                        avg_alpha_key=AVG_ALPHA_KEY,
                        msg_key=MSG_KEY,
                )
-               #register callbacks from window for external use
-               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
-                       setattr(self, attr, getattr(self.win, attr))
-               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
-               self._register_set_prop(self.controller, AVERAGE_KEY)
-               self._register_set_prop(self.controller, AVG_ALPHA_KEY)
+               common.register_access_methods(self, self.win)
+               setattr(self.win, 'set_baseband_freq', getattr(self, 
'set_baseband_freq')) #BACKWARDS
 
 class waterfall_sink_f(_waterfall_sink_base):
        _fft_chain = blks2.logpwrfft_f

Modified: gnuradio/trunk/grc/data/platforms/python/block_tree.xml
===================================================================
--- gnuradio/trunk/grc/data/platforms/python/block_tree.xml     2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/data/platforms/python/block_tree.xml     2009-03-20 
02:16:20 UTC (rev 10660)
@@ -38,6 +38,7 @@
                <block>wxgui_fftsink2</block>
                <block>wxgui_constellationsink2</block>
                <block>wxgui_waterfallsink2</block>
+               <block>wxgui_histosink2</block>
        </cat>
        <cat>
                <name>Operators</name>

Modified: gnuradio/trunk/grc/data/platforms/python/blocks/Makefile.am
===================================================================
--- gnuradio/trunk/grc/data/platforms/python/blocks/Makefile.am 2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/data/platforms/python/blocks/Makefile.am 2009-03-20 
02:16:20 UTC (rev 10660)
@@ -210,6 +210,7 @@
        variable_text_box.xml \
        wxgui_constellationsink2.xml \
        wxgui_fftsink2.xml \
+       wxgui_histosink2.xml \
        wxgui_numbersink2.xml \
        wxgui_scopesink2.xml \
        wxgui_waterfallsink2.xml \

Modified: 
gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_constellationsink2.xml
===================================================================
--- 
gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_constellationsink2.xml    
    2009-03-20 01:48:45 UTC (rev 10659)
+++ 
gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_constellationsink2.xml    
    2009-03-20 02:16:20 UTC (rev 10660)
@@ -7,14 +7,22 @@
 <block>
        <name>Constellation Sink</name>
        <key>wxgui_constellationsink2</key>
-       <import>from gnuradio.wxgui import scopesink2</import>
-       <make>scopesink2.constellation_sink(
+       <import>from gnuradio.wxgui import constsink_gl</import>
+       <make>constsink_gl.const_sink_c(
        self.GetWin(),
        title=$title,
        sample_rate=$samp_rate,
-       frame_decim=$frame_decim,
+       frame_rate=$frame_rate,
+       const_size=$const_size,
+       M=$M,
+       theta=$theta,
+       alpha=$alpha,
+       fmax=$fmax,
+       mu=$mu,
+       gain_mu=$gain_mu,
+       symbol_rate=$symbol_rate,
+       omega_limit=$omega_limit,
 )
-self.$(id).win.$(marker)()
 #set $grid_pos = $grid_pos.eval
 #if not grid_pos
 self.Add(self.$(id).win)
@@ -35,30 +43,66 @@
                <type>real</type>
        </param>
        <param>
-               <name>Frame Decimation</name>
-               <key>frame_decim</key>
-               <value>15</value>
+               <name>Frame Rate</name>
+               <key>frame_rate</key>
+               <value>5</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>Constellation Size</name>
+               <key>const_size</key>
+               <value>2048</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>M</name>
+               <key>M</key>
+               <value>4</value>
                <type>int</type>
        </param>
        <param>
-               <name>Marker</name>
-               <key>marker</key>
-               <value>set_format_plus</value>
-               <type>enum</type>
-               <option>
-                       <name>Line</name>
-                       <key>set_format_line</key>
-               </option>
-               <option>
-                       <name>Dot</name>
-                       <key>set_format_dot</key>
-               </option>
-               <option>
-                       <name>Plus</name>
-                       <key>set_format_plus</key>
-               </option>
+               <name>Theta</name>
+               <key>theta</key>
+               <value>0</value>
+               <type>real</type>
        </param>
        <param>
+               <name>Alpha</name>
+               <key>alpha</key>
+               <value>0.005</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>Max Freq</name>
+               <key>fmax</key>
+               <value>0.06</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>Mu</name>
+               <key>mu</key>
+               <value>0.5</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>Gain Mu</name>
+               <key>gain_mu</key>
+               <value>0.005</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>Symbol Rate</name>
+               <key>symbol_rate</key>
+               <value>samp_rate/4.</value>
+               <type>real</type>
+       </param>
+       <param>
+               <name>Omega Limit</name>
+               <key>omega_limit</key>
+               <value>0.005</value>
+               <type>real</type>
+       </param>
+       <param>
                <name>Grid Position</name>
                <key>grid_pos</key>
                <value></value>

Modified: gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_fftsink2.xml
===================================================================
--- gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_fftsink2.xml  
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_fftsink2.xml  
2009-03-20 02:16:20 UTC (rev 10660)
@@ -12,7 +12,6 @@
        self.GetWin(),
        baseband_freq=$baseband_freq,
        y_per_div=$y_per_div,
-       y_divs=$y_divs,
        ref_level=$ref_level,
        sample_rate=$samp_rate,
        fft_size=$fft_size,
@@ -68,16 +67,30 @@
                <name>Y per Div</name>
                <key>y_per_div</key>
                <value>10</value>
-               <type>real</type>
+               <type>enum</type>
+               <option>
+                       <name>1 dB</name>
+                       <key>1</key>
+               </option>
+               <option>
+                       <name>2 dB</name>
+                       <key>2</key>
+               </option>
+               <option>
+                       <name>5 dB</name>
+                       <key>5</key>
+               </option>
+               <option>
+                       <name>10 dB</name>
+                       <key>10</key>
+               </option>
+               <option>
+                       <name>20 dB</name>
+                       <key>20</key>
+               </option>
        </param>
        <param>
-               <name>Y Divs</name>
-               <key>y_divs</key>
-               <value>8</value>
-               <type>real</type>
-       </param>
-       <param>
-               <name>Reference Level</name>
+               <name>Ref Level (dB)</name>
                <key>ref_level</key>
                <value>50</value>
                <type>real</type>
@@ -95,40 +108,43 @@
                <type>int</type>
        </param>
        <param>
-               <name>Average Alpha</name>
-               <key>avg_alpha</key>
-               <value>0</value>
-               <type>real</type>
-       </param>
-       <param>
-               <name>Average</name>
-               <key>average</key>
+               <name>Peak Hold</name>
+               <key>peak_hold</key>
                <value>False</value>
                <type>enum</type>
+               <hide>#if $peak_hold.eval == 'True' then 'none' else 
'part'#</hide>
                <option>
-                       <name>Yes</name>
+                       <name>On</name>
                        <key>True</key>
                </option>
                <option>
-                       <name>No</name>
+                       <name>Off</name>
                        <key>False</key>
                </option>
        </param>
        <param>
-               <name>Peak Hold</name>
-               <key>peak_hold</key>
+               <name>Average</name>
+               <key>average</key>
                <value>False</value>
                <type>enum</type>
+               <hide>#if $average.eval == 'True' then 'none' else 
'part'#</hide>
                <option>
-                       <name>Yes</name>
+                       <name>On</name>
                        <key>True</key>
                </option>
                <option>
-                       <name>No</name>
+                       <name>Off</name>
                        <key>False</key>
                </option>
        </param>
        <param>
+               <name>Average Alpha</name>
+               <key>avg_alpha</key>
+               <value>0</value>
+               <type>real</type>
+               <hide>#if $average.eval == 'True' then 'none' else 'all'#</hide>
+       </param>
+       <param>
                <name>Grid Position</name>
                <key>grid_pos</key>
                <value></value>

Copied: gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_histosink2.xml 
(from rev 10658, 
gnuradio/branches/developers/jblum/gui_guts/grc/data/platforms/python/blocks/wxgui_histosink2.xml)
===================================================================
--- gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_histosink2.xml        
                        (rev 0)
+++ gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_histosink2.xml        
2009-03-20 02:16:20 UTC (rev 10660)
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##Histogram Sink
+###################################################
+ -->
+<block>
+       <name>Histo Sink</name>
+       <key>wxgui_histosink2</key>
+       <import>from gnuradio.wxgui import histosink_gl</import>
+       <make>histosink_gl.histo_sink_f(
+       self.GetWin(),
+       title=$title,
+       num_bins=$num_bins,
+       frame_size=$frame_size,
+)
+#set $grid_pos = $grid_pos.eval
+#if not grid_pos
+self.Add(self.$(id).win)
+#else
+self.GridAdd(self.$(id).win, $grid_pos[0], $grid_pos[1], $grid_pos[2], 
$grid_pos[3])
+#end if</make>
+       <callback>set_num_bins($num_bins)</callback>
+       <callback>set_frame_size($frame_size)</callback>
+       <param>
+               <name>Title</name>
+               <key>title</key>
+               <value>Histogram Plot</value>
+               <type>string</type>
+       </param>
+       <param>
+               <name>Num Bins</name>
+               <key>num_bins</key>
+               <value>27</value>
+               <type>int</type>
+       </param>
+       <param>
+               <name>Frame Size</name>
+               <key>frame_size</key>
+               <value>1000</value>
+               <type>int</type>
+       </param>
+       <param>
+               <name>Grid Position</name>
+               <key>grid_pos</key>
+               <value></value>
+               <type>grid_pos</type>
+       </param>
+       <sink>
+               <name>in</name>
+               <type>float</type>
+       </sink>
+       <doc>
+Use the Grid Position (row, column, row span, column span) to position the 
graphical element in the window.
+       </doc>
+</block>

Modified: gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_numbersink2.xml
===================================================================
--- gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_numbersink2.xml       
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_numbersink2.xml       
2009-03-20 02:16:20 UTC (rev 10660)
@@ -19,10 +19,10 @@
        ref_level=$ref_level,
        sample_rate=$samp_rate,
        number_rate=$number_rate,
-       average=$options.average,
+       average=$average,
        avg_alpha=#if $avg_alpha.eval then $avg_alpha else 'None'#,
        label=$title,
-       peak_hold=$options.peak_hold,
+       peak_hold=$peak_hold,
        show_gauge=$show_gauge,
 )
 #set $grid_pos = $grid_pos.eval
@@ -108,36 +108,43 @@
                <type>int</type>
        </param>
        <param>
-               <name>Average Alpha</name>
-               <key>avg_alpha</key>
-               <value>0</value>
-               <type>real</type>
+               <name>Peak Hold</name>
+               <key>peak_hold</key>
+               <value>False</value>
+               <type>enum</type>
+               <hide>#if $peak_hold.eval == 'True' then 'none' else 
'part'#</hide>
+               <option>
+                       <name>On</name>
+                       <key>True</key>
+               </option>
+               <option>
+                       <name>Off</name>
+                       <key>False</key>
+               </option>
        </param>
        <param>
-               <name>Options</name>
-               <key>options</key>
-               <value>none</value>
+               <name>Average</name>
+               <key>average</key>
+               <value>False</value>
                <type>enum</type>
+               <hide>#if $average.eval == 'True' then 'none' else 
'part'#</hide>
                <option>
-                       <name>None</name>
-                       <key>none</key>
-                       <opt>peak_hold:False</opt>
-                       <opt>average:False</opt>
+                       <name>On</name>
+                       <key>True</key>
                </option>
                <option>
-                       <name>Average</name>
-                       <key>average</key>
-                       <opt>peak_hold:False</opt>
-                       <opt>average:True</opt>
+                       <name>Off</name>
+                       <key>False</key>
                </option>
-               <option>
-                       <name>Peak Hold</name>
-                       <key>peak_hold</key>
-                       <opt>peak_hold:True</opt>
-                       <opt>average:False</opt>
-               </option>
        </param>
        <param>
+               <name>Average Alpha</name>
+               <key>avg_alpha</key>
+               <value>0</value>
+               <type>real</type>
+               <hide>#if $average.eval == 'True' then 'none' else 'all'#</hide>
+       </param>
+       <param>
                <name>Show Gauge</name>
                <key>show_gauge</key>
                <value>True</value>

Modified: gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_scopesink2.xml
===================================================================
--- gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_scopesink2.xml        
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/data/platforms/python/blocks/wxgui_scopesink2.xml        
2009-03-20 02:16:20 UTC (rev 10660)
@@ -13,21 +13,18 @@
        self.GetWin(),
        title=$title,
        sample_rate=$samp_rate,
-       frame_decim=$frame_decim,
-       v_scale=#if $v_scale.eval then $v_scale else 'None'#,
+       v_scale=$v_scale,
        t_scale=$t_scale,
+       ac_couple=$ac_couple,
+       xy_mode=$xy_mode,
        num_inputs=$num_inputs,
 )
-self.$(id).win.$(marker)()
 #set $grid_pos = $grid_pos.eval
 #if not grid_pos
 self.Add(self.$(id).win)
 #else
 self.GridAdd(self.$(id).win, $grid_pos[0], $grid_pos[1], $grid_pos[2], 
$grid_pos[3])
 #end if</make>
-<!--
-$(id).win.info.scopesink.set_trigger_channel($(trigger_channel))
-$(id).win.info.scopesink.set_trigger_mode(gr.$(trigger_mode)) -->
        <callback>set_sample_rate($samp_rate)</callback>
        <param>
                <name>Type</name>
@@ -58,12 +55,6 @@
                <type>real</type>
        </param>
        <param>
-               <name>Frame Decimation</name>
-               <key>frame_decim</key>
-               <value>15</value>
-               <type>int</type>
-       </param>
-       <param>
                <name>V Scale</name>
                <key>v_scale</key>
                <value>0</value>
@@ -72,51 +63,39 @@
        <param>
                <name>T Scale</name>
                <key>t_scale</key>
-               <value>.001</value>
+               <value>0</value>
                <type>real</type>
        </param>
        <param>
-               <name>Marker</name>
-               <key>marker</key>
-               <value>set_format_line</value>
+               <name>AC Couple</name>
+               <key>ac_couple</key>
+               <value>False</value>
                <type>enum</type>
+               <hide>#if $ac_couple.eval == 'True' then 'none' else 
'part'#</hide>
                <option>
-                       <name>Line</name>
-                       <key>set_format_line</key>
+                       <name>Off</name>
+                       <key>False</key>
                </option>
                <option>
-                       <name>Dot</name>
-                       <key>set_format_dot</key>
+                       <name>On</name>
+                       <key>True</key>
                </option>
-               <option>
-                       <name>Plus</name>
-                       <key>set_format_plus</key>
-               </option>
        </param>
-       <!-- <param>
-               <name>Trigger Channel</name>
-               <key>trigger_channel</key>
-               <value>0</value>
-               <type>int</type>
-       </param>
        <param>
-               <name>Trigger Mode</name>
-               <key>trigger_mode</key>
-               <value>gr_TRIG_AUTO</value>
+               <name>XY Mode</name>
+               <key>xy_mode</key>
+               <value>False</value>
                <type>enum</type>
+               <hide>#if $xy_mode.eval == 'True' then 'none' else 
'part'#</hide>
                <option>
-                       <name>Auto</name>
-                       <key>gr_TRIG_AUTO</key>
+                       <name>Off</name>
+                       <key>False</key>
                </option>
                <option>
-                       <name>Positive Slope</name>
-                       <key>gr_TRIG_POS_SLOPE</key>
+                       <name>On</name>
+                       <key>True</key>
                </option>
-               <option>
-                       <name>Negative Slope</name>
-                       <key>gr_TRIG_NEG_SLOPE</key>
-               </option>
-       </param> -->
+       </param>
        <param>
                <name>Num Inputs</name>
                <key>num_inputs</key>
@@ -129,6 +108,7 @@
                <value></value>
                <type>grid_pos</type>
        </param>
+       <check>not $xy_mode or '$type' == 'complex' or $num_inputs != 1</check>
        <sink>
                <name>in</name>
                <type>$type</type>
@@ -137,6 +117,10 @@
        <doc>
 Set the V Scale to 0 for the scope to auto-scale.
 
+Set the T Scale to 0 for automatic setting.
+
+XY Mode allows the scope to initialize as an XY plotter.
+
 Use the Grid Position (row, column, row span, column span) to position the 
graphical element in the window.
        </doc>
 </block>

Modified: gnuradio/trunk/grc/examples/simple/ber_simulation.grc
===================================================================
--- gnuradio/trunk/grc/examples/simple/ber_simulation.grc       2009-03-20 
01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/examples/simple/ber_simulation.grc       2009-03-20 
02:16:20 UTC (rev 10660)
@@ -1,6 +1,6 @@
 <?xml version='1.0' encoding='ASCII'?>
 <flow_graph>
-  <timestamp>Thu Jul 24 14:28:06 2008</timestamp>
+  <timestamp>Thu Mar 19 11:08:59 2009</timestamp>
   <block>
     <key>options</key>
     <param>
@@ -36,39 +36,12 @@
       <value>Custom</value>
     </param>
     <param>
-      <key>_coordinate</key>
-      <value>(16, 10)</value>
+      <key>realtime_scheduling</key>
+      <value></value>
     </param>
     <param>
-      <key>_rotation</key>
-      <value>0</value>
-    </param>
-  </block>
-  <block>
-    <key>gr_add_vxx</key>
-    <param>
-      <key>id</key>
-      <value>gr_add_vxx</value>
-    </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>type</key>
-      <value>complex</value>
-    </param>
-    <param>
-      <key>num_inputs</key>
-      <value>2</value>
-    </param>
-    <param>
-      <key>vlen</key>
-      <value>1</value>
-    </param>
-    <param>
       <key>_coordinate</key>
-      <value>(652, 395)</value>
+      <value>(16, 10)</value>
     </param>
     <param>
       <key>_rotation</key>
@@ -76,45 +49,6 @@
     </param>
   </block>
   <block>
-    <key>wxgui_constellationsink2</key>
-    <param>
-      <key>id</key>
-      <value>wxgui_constellationsink2</value>
-    </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>title</key>
-      <value>"Constellation: "+str(const)</value>
-    </param>
-    <param>
-      <key>samp_rate</key>
-      <value>samp_rate</value>
-    </param>
-    <param>
-      <key>frame_decim</key>
-      <value>15</value>
-    </param>
-    <param>
-      <key>marker</key>
-      <value>set_format_plus</value>
-    </param>
-    <param>
-      <key>grid_pos</key>
-      <value>2, 0, 1, 1</value>
-    </param>
-    <param>
-      <key>_coordinate</key>
-      <value>(907, 334)</value>
-    </param>
-    <param>
-      <key>_rotation</key>
-      <value>0</value>
-    </param>
-  </block>
-  <block>
     <key>gr_noise_source_x</key>
     <param>
       <key>id</key>
@@ -239,14 +173,18 @@
       <value>15</value>
     </param>
     <param>
+      <key>peak_hold</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>average</key>
+      <value>False</value>
+    </param>
+    <param>
       <key>avg_alpha</key>
       <value>0</value>
     </param>
     <param>
-      <key>options</key>
-      <value>none</value>
-    </param>
-    <param>
       <key>show_gauge</key>
       <value>False</value>
     </param>
@@ -306,7 +244,7 @@
     </param>
     <param>
       <key>type</key>
-      <value>"BER"</value>
+      <value>'BER'</value>
     </param>
     <param>
       <key>win_size</key>
@@ -483,6 +421,92 @@
       <value>0</value>
     </param>
   </block>
+  <block>
+    <key>gr_add_vxx</key>
+    <param>
+      <key>id</key>
+      <value>gr_add_vxx</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>type</key>
+      <value>complex</value>
+    </param>
+    <param>
+      <key>num_inputs</key>
+      <value>2</value>
+    </param>
+    <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>_coordinate</key>
+      <value>(652, 395)</value>
+    </param>
+    <param>
+      <key>_rotation</key>
+      <value>0</value>
+    </param>
+  </block>
+  <block>
+    <key>wxgui_scopesink2</key>
+    <param>
+      <key>id</key>
+      <value>wxgui_scopesink2_0</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>type</key>
+      <value>complex</value>
+    </param>
+    <param>
+      <key>title</key>
+      <value>"Constellation: "+str(const)</value>
+    </param>
+    <param>
+      <key>samp_rate</key>
+      <value>samp_rate</value>
+    </param>
+    <param>
+      <key>v_scale</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>t_scale</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>ac_couple</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>xy_mode</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>num_inputs</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>grid_pos</key>
+      <value>2, 0, 1, 1</value>
+    </param>
+    <param>
+      <key>_coordinate</key>
+      <value>(828, 368)</value>
+    </param>
+    <param>
+      <key>_rotation</key>
+      <value>0</value>
+    </param>
+  </block>
   <connection>
     <source_block_id>blks2_error_rate</source_block_id>
     <sink_block_id>wxgui_numbersink2</sink_block_id>
@@ -520,21 +544,21 @@
     <sink_key>1</sink_key>
   </connection>
   <connection>
-    <source_block_id>gr_add_vxx</source_block_id>
-    <sink_block_id>wxgui_constellationsink2</sink_block_id>
+    <source_block_id>random_source_x</source_block_id>
+    <sink_block_id>gr_throttle</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
     <source_block_id>random_source_x</source_block_id>
-    <sink_block_id>gr_throttle</sink_block_id>
+    <sink_block_id>gr_chunks_to_symbols_xx</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
   <connection>
-    <source_block_id>random_source_x</source_block_id>
-    <sink_block_id>gr_chunks_to_symbols_xx</sink_block_id>
+    <source_block_id>gr_add_vxx</source_block_id>
+    <sink_block_id>wxgui_scopesink2_0</sink_block_id>
     <source_key>0</source_key>
     <sink_key>0</sink_key>
   </connection>
-</flow_graph>
\ No newline at end of file
+</flow_graph>

Modified: gnuradio/trunk/grc/examples/trellis/interference_cancellation.grc
===================================================================
--- gnuradio/trunk/grc/examples/trellis/interference_cancellation.grc   
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/examples/trellis/interference_cancellation.grc   
2009-03-20 02:16:20 UTC (rev 10660)
@@ -1,6 +1,6 @@
 <?xml version='1.0' encoding='ASCII'?>
 <flow_graph>
-  <timestamp>Tue Nov 18 00:48:20 2008</timestamp>
+  <timestamp>Thu Mar 19 11:22:40 2009</timestamp>
   <block>
     <key>options</key>
     <param>
@@ -36,6 +36,10 @@
       <value>Custom</value>
     </param>
     <param>
+      <key>realtime_scheduling</key>
+      <value></value>
+    </param>
+    <param>
       <key>_coordinate</key>
       <value>(10, 10)</value>
     </param>
@@ -411,37 +415,6 @@
     </param>
   </block>
   <block>
-    <key>gr_add_vxx</key>
-    <param>
-      <key>id</key>
-      <value>gr_add_vxx_1</value>
-    </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>type</key>
-      <value>complex</value>
-    </param>
-    <param>
-      <key>num_inputs</key>
-      <value>2</value>
-    </param>
-    <param>
-      <key>vlen</key>
-      <value>1</value>
-    </param>
-    <param>
-      <key>_coordinate</key>
-      <value>(1400, 262)</value>
-    </param>
-    <param>
-      <key>_rotation</key>
-      <value>0</value>
-    </param>
-  </block>
-  <block>
     <key>gr_noise_source_x</key>
     <param>
       <key>id</key>
@@ -477,45 +450,6 @@
     </param>
   </block>
   <block>
-    <key>wxgui_constellationsink2</key>
-    <param>
-      <key>id</key>
-      <value>wxgui_constellationsink2_0</value>
-    </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>title</key>
-      <value>Constellation Plot</value>
-    </param>
-    <param>
-      <key>samp_rate</key>
-      <value>R</value>
-    </param>
-    <param>
-      <key>frame_decim</key>
-      <value>15</value>
-    </param>
-    <param>
-      <key>marker</key>
-      <value>set_format_plus</value>
-    </param>
-    <param>
-      <key>grid_pos</key>
-      <value></value>
-    </param>
-    <param>
-      <key>_coordinate</key>
-      <value>(1301, 74)</value>
-    </param>
-    <param>
-      <key>_rotation</key>
-      <value>0</value>
-    </param>
-  </block>
-  <block>
     <key>gr_sub_xx</key>
     <param>
       <key>id</key>
@@ -530,6 +464,10 @@
       <value>short</value>
     </param>
     <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
       <key>num_inputs</key>
       <value>2</value>
     </param>
@@ -607,6 +545,10 @@
       <value>short</value>
     </param>
     <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
       <key>num_inputs</key>
       <value>2</value>
     </param>
@@ -750,6 +692,10 @@
       <value>complex</value>
     </param>
     <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
       <key>num_inputs</key>
       <value>2</value>
     </param>
@@ -777,6 +723,10 @@
       <value>short</value>
     </param>
     <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
       <key>num_inputs</key>
       <value>2</value>
     </param>
@@ -920,6 +870,10 @@
       <value>complex</value>
     </param>
     <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
       <key>num_inputs</key>
       <value>2</value>
     </param>
@@ -947,6 +901,10 @@
       <value>short</value>
     </param>
     <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
       <key>num_inputs</key>
       <value>2</value>
     </param>
@@ -1064,14 +1022,18 @@
       <value>15</value>
     </param>
     <param>
+      <key>peak_hold</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>average</key>
+      <value>False</value>
+    </param>
+    <param>
       <key>avg_alpha</key>
       <value>0.001</value>
     </param>
     <param>
-      <key>options</key>
-      <value>average</value>
-    </param>
-    <param>
       <key>show_gauge</key>
       <value>True</value>
     </param>
@@ -1143,14 +1105,18 @@
       <value>15</value>
     </param>
     <param>
+      <key>peak_hold</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>average</key>
+      <value>False</value>
+    </param>
+    <param>
       <key>avg_alpha</key>
       <value>0.001</value>
     </param>
     <param>
-      <key>options</key>
-      <value>average</value>
-    </param>
-    <param>
       <key>show_gauge</key>
       <value>True</value>
     </param>
@@ -1222,103 +1188,28 @@
       <value>15</value>
     </param>
     <param>
-      <key>avg_alpha</key>
-      <value>0.001</value>
+      <key>peak_hold</key>
+      <value>False</value>
     </param>
     <param>
-      <key>options</key>
-      <value>average</value>
+      <key>average</key>
+      <value>False</value>
     </param>
     <param>
-      <key>show_gauge</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>grid_pos</key>
-      <value>1,0,1,1</value>
-    </param>
-    <param>
-      <key>_coordinate</key>
-      <value>(1269, 1417)</value>
-    </param>
-    <param>
-      <key>_rotation</key>
-      <value>0</value>
-    </param>
-  </block>
-  <block>
-    <key>wxgui_numbersink2</key>
-    <param>
-      <key>id</key>
-      <value>wxgui_numbersink2_0</value>
-    </param>
-    <param>
-      <key>_enabled</key>
-      <value>True</value>
-    </param>
-    <param>
-      <key>type</key>
-      <value>float</value>
-    </param>
-    <param>
-      <key>title</key>
-      <value>BER 1 (raw)</value>
-    </param>
-    <param>
-      <key>units</key>
-      <value>BER</value>
-    </param>
-    <param>
-      <key>samp_rate</key>
-      <value>R</value>
-    </param>
-    <param>
-      <key>base_value</key>
-      <value>0.0</value>
-    </param>
-    <param>
-      <key>min_value</key>
-      <value>0</value>
-    </param>
-    <param>
-      <key>max_value</key>
-      <value>1</value>
-    </param>
-    <param>
-      <key>factor</key>
-      <value>1.0</value>
-    </param>
-    <param>
-      <key>decimal_places</key>
-      <value>6</value>
-    </param>
-    <param>
-      <key>ref_level</key>
-      <value>0</value>
-    </param>
-    <param>
-      <key>number_rate</key>
-      <value>15</value>
-    </param>
-    <param>
       <key>avg_alpha</key>
       <value>0.001</value>
     </param>
     <param>
-      <key>options</key>
-      <value>average</value>
-    </param>
-    <param>
       <key>show_gauge</key>
       <value>True</value>
     </param>
     <param>
       <key>grid_pos</key>
-      <value>0,0,1,1</value>
+      <value>1,0,1,1</value>
     </param>
     <param>
       <key>_coordinate</key>
-      <value>(1267, 410)</value>
+      <value>(1269, 1417)</value>
     </param>
     <param>
       <key>_rotation</key>
@@ -1715,6 +1606,175 @@
       <value>0</value>
     </param>
   </block>
+  <block>
+    <key>gr_add_vxx</key>
+    <param>
+      <key>id</key>
+      <value>gr_add_vxx_1</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>type</key>
+      <value>complex</value>
+    </param>
+    <param>
+      <key>num_inputs</key>
+      <value>2</value>
+    </param>
+    <param>
+      <key>vlen</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>_coordinate</key>
+      <value>(1400, 262)</value>
+    </param>
+    <param>
+      <key>_rotation</key>
+      <value>0</value>
+    </param>
+  </block>
+  <block>
+    <key>wxgui_numbersink2</key>
+    <param>
+      <key>id</key>
+      <value>wxgui_numbersink2_0</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>type</key>
+      <value>float</value>
+    </param>
+    <param>
+      <key>title</key>
+      <value>BER 1 (raw)</value>
+    </param>
+    <param>
+      <key>units</key>
+      <value>BER</value>
+    </param>
+    <param>
+      <key>samp_rate</key>
+      <value>R</value>
+    </param>
+    <param>
+      <key>base_value</key>
+      <value>0.0</value>
+    </param>
+    <param>
+      <key>min_value</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>max_value</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>factor</key>
+      <value>1.0</value>
+    </param>
+    <param>
+      <key>decimal_places</key>
+      <value>6</value>
+    </param>
+    <param>
+      <key>ref_level</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>number_rate</key>
+      <value>15</value>
+    </param>
+    <param>
+      <key>peak_hold</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>average</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>avg_alpha</key>
+      <value>0.001</value>
+    </param>
+    <param>
+      <key>show_gauge</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>grid_pos</key>
+      <value>0,0,1,1</value>
+    </param>
+    <param>
+      <key>_coordinate</key>
+      <value>(1267, 410)</value>
+    </param>
+    <param>
+      <key>_rotation</key>
+      <value>0</value>
+    </param>
+  </block>
+  <block>
+    <key>wxgui_scopesink2</key>
+    <param>
+      <key>id</key>
+      <value>wxgui_scopesink2_0</value>
+    </param>
+    <param>
+      <key>_enabled</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>type</key>
+      <value>complex</value>
+    </param>
+    <param>
+      <key>title</key>
+      <value>Scope Plot</value>
+    </param>
+    <param>
+      <key>samp_rate</key>
+      <value>R</value>
+    </param>
+    <param>
+      <key>v_scale</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>t_scale</key>
+      <value>0</value>
+    </param>
+    <param>
+      <key>ac_couple</key>
+      <value>False</value>
+    </param>
+    <param>
+      <key>xy_mode</key>
+      <value>True</value>
+    </param>
+    <param>
+      <key>num_inputs</key>
+      <value>1</value>
+    </param>
+    <param>
+      <key>grid_pos</key>
+      <value></value>
+    </param>
+    <param>
+      <key>_coordinate</key>
+      <value>(1533, 149)</value>
+    </param>
+    <param>
+      <key>_rotation</key>
+      <value>0</value>
+    </param>
+  </block>
   <connection>
     <source_block_id>random_source_x_1</source_block_id>
     <sink_block_id>trellis_encoder_xx_1</sink_block_id>
@@ -1758,12 +1818,6 @@
     <sink_key>1</sink_key>
   </connection>
   <connection>
-    <source_block_id>gr_add_vxx_1</source_block_id>
-    <sink_block_id>wxgui_constellationsink2_0</sink_block_id>
-    <source_key>0</source_key>
-    <sink_key>0</sink_key>
-  </connection>
-  <connection>
     <source_block_id>gr_chunks_to_symbols_xx_1</source_block_id>
     <sink_block_id>gr_multiply_const_vxx_1</sink_block_id>
     <source_key>0</source_key>
@@ -2009,4 +2063,10 @@
     <source_key>0</source_key>
     <sink_key>1</sink_key>
   </connection>
+  <connection>
+    <source_block_id>gr_add_vxx_1</source_block_id>
+    <sink_block_id>wxgui_scopesink2_0</sink_block_id>
+    <source_key>0</source_key>
+    <sink_key>0</sink_key>
+  </connection>
 </flow_graph>

Modified: gnuradio/trunk/grc/src/grc_gnuradio/wxgui/callback_controls.py
===================================================================
--- gnuradio/trunk/grc/src/grc_gnuradio/wxgui/callback_controls.py      
2009-03-20 01:48:45 UTC (rev 10659)
+++ gnuradio/trunk/grc/src/grc_gnuradio/wxgui/callback_controls.py      
2009-03-20 02:16:20 UTC (rev 10660)
@@ -26,7 +26,7 @@
        """Label text class for uniform labels among all controls."""
 
        def __init__(self, window, label):
-               wx.StaticText.__init__(self, window, -1, str(label))
+               wx.StaticText.__init__(self, window, label=str(label))
                font = self.GetFont()
                font.SetWeight(wx.FONTWEIGHT_BOLD)
                self.SetFont(font)
@@ -92,7 +92,7 @@
        """House a button for variable control."""
 
        def _init(self):
-               self.button = wx.Button(self.get_window(), -1, 
self.labels[self.index])
+               self.button = wx.Button(self.get_window(), 
label=self.labels[self.index])
                self.button.Bind(wx.EVT_BUTTON, self._handle_changed)
                self.Add(self.button, 0, wx.ALIGN_CENTER)
 
@@ -107,7 +107,7 @@
        """House a drop down for variable control."""
 
        def _init(self):
-               self.drop_down = wx.Choice(self.get_window(), -1, 
choices=self.labels)
+               self.drop_down = wx.Choice(self.get_window(), 
choices=self.labels)
                self.Add(self.drop_down, 0, wx.ALIGN_CENTER)
                self.drop_down.Bind(wx.EVT_CHOICE, self._handle_changed)
                self.drop_down.SetSelection(self.index)
@@ -124,13 +124,13 @@
        def _init(self):
                #create box for radio buttons
                radio_box = wx.BoxSizer(self.radio_box_orientation)
-               panel = wx.Panel(self.get_window(), -1)
+               panel = wx.Panel(self.get_window())
                panel.SetSizer(radio_box)
                self.Add(panel, 0, wx.ALIGN_CENTER)
                #create radio buttons
                self.radio_buttons = list()
                for label in self.labels:
-                       radio_button = wx.RadioButton(panel, -1, label)
+                       radio_button = wx.RadioButton(panel, label=label)
                        radio_button.SetValue(False)
                        self.radio_buttons.append(radio_button)
                        radio_box.Add(radio_button, 0, self.radio_button_align)
@@ -177,13 +177,13 @@
                #create gui elements
                label_text_sizer = wx.BoxSizer(self.label_text_orientation) 
#label and text box container
                label_text = LabelText(self.get_window(), '%s: '%str(label))
-               self.text_box = text_box = wx.TextCtrl(self.get_window(), -1, 
str(value), style=wx.TE_PROCESS_ENTER)
+               self.text_box = text_box = wx.TextCtrl(self.get_window(), 
style=wx.TE_PROCESS_ENTER)
                text_box.Bind(wx.EVT_TEXT_ENTER, self._handle_enter) #bind this 
special enter hotkey event
                for obj in (label_text, text_box): #fill the container with 
label and text entry box
-                       label_text_sizer.Add(obj, 0, 
wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
+                       label_text_sizer.Add(obj, 0, wx.ALIGN_CENTER)
                self.Add(label_text_sizer, 0, wx.ALIGN_CENTER)
                #make the slider
-               self.slider = slider = wx.Slider(self.get_window(), -1, 
size=wx.Size(*self.get_slider_size()), style=self.slider_style)
+               self.slider = slider = wx.Slider(self.get_window(), 
size=wx.Size(*self.get_slider_size()), style=self.slider_style)
                try: slider.SetRange(0, num_steps)
                except Exception, e:
                        print >> sys.stderr, 'Error in set slider range: 
"%s".'%e
@@ -245,11 +245,11 @@
 class slider_horizontal_control(_slider_control_base):
        label_text_orientation = wx.HORIZONTAL
        slider_style = wx.SL_HORIZONTAL
-       def get_slider_size(self): return self.slider_length, 20
+       def get_slider_size(self): return self.slider_length, -1
 class slider_vertical_control(_slider_control_base):
        label_text_orientation = wx.VERTICAL
        slider_style = wx.SL_VERTICAL
-       def get_slider_size(self): return 20, self.slider_length
+       def get_slider_size(self): return -1, self.slider_length
 
 
##############################################################################################
 # Text Box Control
@@ -271,10 +271,10 @@
                #create gui elements
                label_text_sizer = wx.BoxSizer(wx.HORIZONTAL) #label and text 
box container
                label_text = LabelText(self.get_window(), '%s: '%str(label))
-               self.text_box = text_box = wx.TextCtrl(self.get_window(), -1, 
str(value), style=wx.TE_PROCESS_ENTER)
+               self.text_box = text_box = wx.TextCtrl(self.get_window(), 
value=str(value), style=wx.TE_PROCESS_ENTER)
                text_box.Bind(wx.EVT_TEXT_ENTER, self._handle_enter) #bind this 
special enter hotkey event
                for obj in (label_text, text_box): #fill the container with 
label and text entry box
-                       label_text_sizer.Add(obj, 0, 
wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
+                       label_text_sizer.Add(obj, 0, wx.ALIGN_CENTER)
                self.Add(label_text_sizer, 0, wx.ALIGN_CENTER)
                #detect string mode
                self._string_mode = isinstance(value, str)





reply via email to

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