setup special wxgui connect on sinks, needs testing
[debian/gnuradio] / gr-wxgui / src / python / common.py
index c6b9509b293a5cbf66a2a310569a3511ee310526..dca41c9a3ec1d0f11fa8e48f4b784c29f498e1be 100644 (file)
 # Boston, MA 02110-1301, USA.
 #
 
+##################################################
+# conditional disconnections of wx flow graph
+##################################################
+import wx
+
+def bind_to_visible_event(win, callback):
+       """
+       Bind a callback to a window when its visibility changes.
+       Specifically, callback when the window changes visibility
+       when a notebook tab event in one of the parents occurs.
+       @param win the wx window
+       @param callback a 1 param function
+       """
+       #is the window visible in the hierarchy
+       def is_wx_window_visible(my_win):
+               while True:
+                       parent = my_win.GetParent()
+                       if not parent: return True #reached the top of the hierarchy
+                       #if we are hidden, then finish, otherwise keep traversing up
+                       if isinstance(parent, wx.Notebook) and parent.GetCurrentPage() != my_win: return False
+                       my_win = parent
+       #call the callback, the arg is shown or not
+       def callback_factory(my_win, my_callback):
+               return lambda *args: my_callback(is_wx_window_visible(my_win))
+       handler = callback_factory(win, callback)
+       #bind the handler to all the parent notebooks
+       while win:
+               if isinstance(win, wx.Notebook):
+                       win.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, handler)
+               if not win.GetParent():
+                       win.Bind(wx.EVT_ACTIVATE, handler)
+               win = win.GetParent()
+
+from gnuradio import gr
+
+def conditional_connect(source, sink, hb, win, size):
+       nulls = list()
+       cache = [None]
+       def callback(visible, init=False):
+               if visible == cache[0]: return
+               cache[0] = visible
+               if not init: hb.lock()
+               print 'visible', visible, source, sink
+               if visible:
+                       if not init:
+                               hb.disconnect(source, nulls[0])
+                               hb.disconnect(nulls[1], nulls[2])
+                               hb.disconnect(nulls[2], sink)
+                               while nulls: nulls.pop()
+                       hb.connect(source, sink)
+               else:
+                       if not init: hb.disconnect(source, sink)
+                       nulls.extend([gr.null_sink(size), gr.null_source(size), gr.head(size, 0)])
+                       hb.connect(source, nulls[0])
+                       hb.connect(nulls[1], nulls[2], sink)
+               if not init: hb.unlock()
+       callback(False, init=True) #initially connect
+       bind_to_visible_event(win, callback)
+
+class wxgui_hb(object):
+       def wxgui_connect(self, *points):
+               """
+               Use wxgui connect when the first point is the self source of the hb.
+               The win property of this object should be set to the wx window.
+               When this method tries to connect self to the next point,
+               it will conditionally make this connection based on the visibility state.
+               """
+               try:
+                       assert points[0] == self or points[0][0] == self
+                       conditional_connect(
+                               points[0], points[1],
+                               win=self.win, hb=self,
+                               size=self._hb.input_signature().sizeof_stream_item(0),
+                       )
+                       if len(points[1:]) > 1: self.connect(*points[1:])
+               except (AssertionError, IndexError): self.connect(*points)
+
 #A macro to apply an index to a key
 index_key = lambda key, i: "%s_%d"%(key, i+1)
 
@@ -44,31 +121,26 @@ def register_access_methods(destination, controller):
 ##################################################
 # Input Watcher Thread
 ##################################################
-import threading
+from gnuradio import gru
 
-class input_watcher(threading.Thread):
+class input_watcher(gru.msgq_runner):
        """
        Input watcher thread runs forever.
        Read messages from the message queue.
        Forward messages to the message handler.
        """
        def __init__ (self, msgq, controller, msg_key, arg1_key='', arg2_key=''):
-               threading.Thread.__init__(self)
-               self.setDaemon(1)
-               self.msgq = msgq
                self._controller = controller
                self._msg_key = msg_key
                self._arg1_key = arg1_key
                self._arg2_key = arg2_key
-               self.keep_running = True
-               self.start()
+               gru.msgq_runner.__init__(self, msgq, self.handle_msg)
+
+       def handle_msg(self, msg):
+               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()
 
-       def run(self):
-               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()
 
 ##################################################
 # Shared Functions
@@ -142,6 +214,25 @@ def get_min_max(samples):
        scale_factor = 3
        mean = numpy.average(samples)
        rms = numpy.max([scale_factor*((numpy.sum((samples-mean)**2)/len(samples))**.5), .1])
-       min = mean - rms
-       max = mean + rms
-       return min, max
+       min_val = mean - rms
+       max_val = mean + rms
+       return min_val, max_val
+
+def get_min_max_fft(fft_samps):
+       """
+       Get the minimum and maximum bounds for an array of fft samples.
+       @param samples the array of real values
+       @return a tuple of min, max
+       """
+       #get the peak level (max of the samples)
+       peak_level = numpy.max(fft_samps)
+       #separate noise samples
+       noise_samps = numpy.sort(fft_samps)[:len(fft_samps)/2]
+       #get the noise floor
+       noise_floor = numpy.average(noise_samps)
+       #get the noise deviation
+       noise_dev = numpy.std(noise_samps)
+       #determine the maximum and minimum levels
+       max_level = peak_level
+       min_level = noise_floor - abs(2*noise_dev)
+       return min_level, max_level