Merged r10463:10658 from jblum/gui_guts into trunk. Trunk passes distcheck.
[debian/gnuradio] / gr-wxgui / src / python / fft_window.py
index 6e54aec872dd072a6a202cdf6541a058abe8b1ec..fdd5562dc2e89fec0895f970bad43a4510988d8d 100644 (file)
@@ -39,8 +39,8 @@ AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
 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 @@ class control_panel(wx.Panel):
                @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 @@ class control_panel(wx.Panel):
                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 @@ class control_panel(wx.Panel):
                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 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                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 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                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 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                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 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                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 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                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