added bind to visible event function to callback when visibility changes within tabs
[debian/gnuradio] / gr-wxgui / src / python / common.py
1 #
2 # Copyright 2008 Free Software Foundation, Inc.
3 #
4 # This file is part of GNU Radio
5 #
6 # GNU Radio is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3, or (at your option)
9 # any later version.
10 #
11 # GNU Radio is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with GNU Radio; see the file COPYING.  If not, write to
18 # the Free Software Foundation, Inc., 51 Franklin Street,
19 # Boston, MA 02110-1301, USA.
20 #
21
22 import wx
23
24 def bind_to_visible_event(win, callback):
25         """
26         Bind a callback to a window when its visibility changes.
27         Specifically, callback when the window changes visibility
28         when a notebook tab event in one of the parents occurs.
29         @param win the wx window
30         @param callback a 1 param function
31         """
32         #is the window visible in the hierarchy
33         def is_wx_window_visible(my_win):
34                 while True:
35                         parent = my_win.GetParent()
36                         if not parent: return True #reached the top of the hierarchy
37                         #if we are hidden, then finish, otherwise keep traversing up
38                         if isinstance(parent, wx.Notebook) and parent.GetCurrentPage() != my_win: return False
39                         my_win = parent
40         #call the callback, the arg is shown or not
41         def callback_factory(my_win, my_callback):
42                 cache = [None]
43                 def the_callback(*args):
44                         visible = is_wx_window_visible(my_win)
45                         if cache[0] != visible: my_callback(visible)
46                         cache[0] = visible
47                 return the_callback
48         handler = callback_factory(win, callback)
49         #bind the handler to all the parent notebooks
50         while win:
51                 if isinstance(win, wx.Notebook):
52                         win.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, handler)
53                 if not win.GetParent():
54                         win.Bind(wx.EVT_ACTIVATE, handler)
55                 win = win.GetParent()
56
57 #A macro to apply an index to a key
58 index_key = lambda key, i: "%s_%d"%(key, i+1)
59
60 def _register_access_method(destination, controller, key):
61         """
62         Helper function for register access methods.
63         This helper creates distinct set and get methods for each key
64         and adds them to the destination object.
65         """
66         def set(value): controller[key] = value
67         setattr(destination, 'set_'+key, set)
68         def get(): return controller[key]
69         setattr(destination, 'get_'+key, get) 
70
71 def register_access_methods(destination, controller):
72         """
73         Register setter and getter functions in the destination object for all keys in the controller.
74         @param destination the object to get new setter and getter methods
75         @param controller the pubsub controller
76         """
77         for key in controller.keys(): _register_access_method(destination, controller, key)
78
79 ##################################################
80 # Input Watcher Thread
81 ##################################################
82 from gnuradio import gru
83
84 class input_watcher(gru.msgq_runner):
85         """
86         Input watcher thread runs forever.
87         Read messages from the message queue.
88         Forward messages to the message handler.
89         """
90         def __init__ (self, msgq, controller, msg_key, arg1_key='', arg2_key=''):
91                 self._controller = controller
92                 self._msg_key = msg_key
93                 self._arg1_key = arg1_key
94                 self._arg2_key = arg2_key
95                 gru.msgq_runner.__init__(self, msgq, self.handle_msg)
96
97         def handle_msg(self, msg):
98                 if self._arg1_key: self._controller[self._arg1_key] = msg.arg1()
99                 if self._arg2_key: self._controller[self._arg2_key] = msg.arg2()
100                 self._controller[self._msg_key] = msg.to_string()
101
102
103 ##################################################
104 # Shared Functions
105 ##################################################
106 import numpy
107 import math
108
109 def get_exp(num):
110         """
111         Get the exponent of the number in base 10.
112         @param num the floating point number
113         @return the exponent as an integer
114         """
115         if num == 0: return 0
116         return int(math.floor(math.log10(abs(num))))
117
118 def get_clean_num(num):
119         """
120         Get the closest clean number match to num with bases 1, 2, 5.
121         @param num the number
122         @return the closest number
123         """
124         if num == 0: return 0
125         sign = num > 0 and 1 or -1
126         exp = get_exp(num)
127         nums = numpy.array((1, 2, 5, 10))*(10**exp)
128         return sign*nums[numpy.argmin(numpy.abs(nums - abs(num)))]
129
130 def get_clean_incr(num):
131         """
132         Get the next higher clean number with bases 1, 2, 5.
133         @param num the number
134         @return the next higher number
135         """
136         num = get_clean_num(num)
137         exp = get_exp(num)
138         coeff = int(round(num/10**exp))
139         return {
140                 -5: -2,
141                 -2: -1,
142                 -1: -.5,
143                 1: 2,
144                 2: 5,
145                 5: 10,
146         }[coeff]*(10**exp)
147
148 def get_clean_decr(num):
149         """
150         Get the next lower clean number with bases 1, 2, 5.
151         @param num the number
152         @return the next lower number
153         """
154         num = get_clean_num(num)
155         exp = get_exp(num)
156         coeff = int(round(num/10**exp))
157         return {
158                 -5: -10,
159                 -2: -5,
160                 -1: -2,
161                 1: .5,
162                 2: 1,
163                 5: 2,
164         }[coeff]*(10**exp)
165
166 def get_min_max(samples):
167         """
168         Get the minimum and maximum bounds for an array of samples.
169         @param samples the array of real values
170         @return a tuple of min, max
171         """
172         scale_factor = 3
173         mean = numpy.average(samples)
174         rms = numpy.max([scale_factor*((numpy.sum((samples-mean)**2)/len(samples))**.5), .1])
175         min_val = mean - rms
176         max_val = mean + rms
177         return min_val, max_val
178
179 def get_min_max_fft(fft_samps):
180         """
181         Get the minimum and maximum bounds for an array of fft samples.
182         @param samples the array of real values
183         @return a tuple of min, max
184         """
185         #get the peak level (max of the samples)
186         peak_level = numpy.max(fft_samps)
187         #separate noise samples
188         noise_samps = numpy.sort(fft_samps)[:len(fft_samps)/2]
189         #get the noise floor
190         noise_floor = numpy.average(noise_samps)
191         #get the noise deviation
192         noise_dev = numpy.std(noise_samps)
193         #determine the maximum and minimum levels
194         max_level = peak_level
195         min_level = noise_floor - abs(2*noise_dev)
196         return min_level, max_level