]> git.gag.com Git - debian/gnuradio/blob - grc/src/grc/gui/elements/Param.py
6e48e7b84dc31c7087d6db5fe63195e0d6c74135
[debian/gnuradio] / grc / src / grc / gui / elements / Param.py
1 """
2 Copyright 2007 Free Software Foundation, Inc.
3 This file is part of GNU Radio
4
5 GNU Radio Companion is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 GNU Radio Companion is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18 """
19 ##@package grc.gui.elements.Param
20 #GTK objects for handling input and the signal block parameter class.
21 #@author Josh Blum
22
23 import Utils
24 from Element import Element
25 import pygtk
26 pygtk.require('2.0')
27 import gtk
28 import pango
29 import gobject
30 from grc.Constants import *
31 from os import path
32
33 ######################################################################################################
34 # gtk objects for handling input
35 ######################################################################################################
36
37 class InputParam(gtk.HBox):
38         """The base class for an input parameter inside the input parameters dialog."""
39
40         def __init__(self, param, _handle_changed):
41                 gtk.HBox.__init__(self)
42                 self.param = param
43                 self._handle_changed = _handle_changed
44                 self.label = gtk.Label('') #no label, markup is added by set_markup
45                 self.label.set_size_request(150, -1)
46                 self.pack_start(self.label, False)
47                 self.set_markup = lambda m: self.label.set_markup(m)
48                 self.tp = None
49
50 class EntryParam(InputParam):
51         """Provide an entry box for strings and numbers."""
52
53         def __init__(self, *args, **kwargs):
54                 InputParam.__init__(self, *args, **kwargs)
55                 self.entry = input = gtk.Entry()
56                 input.set_text(self.param.get_value())
57                 input.connect('changed', self._handle_changed)
58                 self.pack_start(input, True)
59                 self.get_text = input.get_text
60                 #tool tip
61                 self.tp = gtk.Tooltips()
62                 self.tp.set_tip(self.entry, '')
63                 self.tp.enable()
64
65 class FileParam(EntryParam):
66         """Provide an entry box for filename and a button to browse for a file."""
67
68         def __init__(self, *args, **kwargs):
69                 EntryParam.__init__(self, *args, **kwargs)
70                 input = gtk.Button('...')
71                 input.connect('clicked', self._handle_clicked)
72                 self.pack_start(input, False)
73
74         def _handle_clicked(self, widget=None):
75                 """
76                 If the button was clicked, open a file dialog in open/save format.
77                 Replace the text in the entry with the new filename from the file dialog.
78                 """
79                 file_path = self.param.is_valid() and self.param.evaluate() or ''
80                 #bad file paths will be redirected to default
81                 if not path.exists(path.dirname(file_path)): file_path = DEFAULT_FILE_PATH
82                 if self.param.get_type() == 'file_open':
83                         file_dialog = gtk.FileChooserDialog('Open a Data File...', None,
84                                 gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK))
85                 elif self.param.get_type() == 'file_save':
86                         file_dialog = gtk.FileChooserDialog('Save a Data File...', None,
87                                 gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK))
88                         file_dialog.set_do_overwrite_confirmation(True)
89                         file_dialog.set_current_name(path.basename(file_path)) #show the current filename
90                 file_dialog.set_current_folder(path.dirname(file_path)) #current directory
91                 file_dialog.set_select_multiple(False)
92                 file_dialog.set_local_only(True)
93                 if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog
94                         file_path = file_dialog.get_filename() #get the file path
95                         self.entry.set_text(file_path)
96                         self._handle_changed()
97                 file_dialog.destroy() #destroy the dialog
98
99 class EnumParam(InputParam):
100         """Provide an entry box for Enum types with a drop down menu."""
101
102         def __init__(self, *args, **kwargs):
103                 InputParam.__init__(self, *args, **kwargs)
104                 input = gtk.ComboBox(gtk.ListStore(gobject.TYPE_STRING))
105                 cell = gtk.CellRendererText()
106                 input.pack_start(cell, True)
107                 input.add_attribute(cell, 'text', 0)
108                 for option in self.param.get_options(): input.append_text(option.get_name())
109                 input.set_active(int(self.param.get_option_keys().index(self.param.get_value())))
110                 input.connect("changed", self._handle_changed)
111                 self.pack_start(input, False)
112                 self.get_text = lambda: str(input.get_active()) #the get text parses the selected index to a string
113
114 ######################################################################################################
115 # A Flow Graph Parameter
116 ######################################################################################################
117
118 class Param(Element):
119         """The graphical parameter."""
120
121         def update(self):
122                 """!
123                 Called when an external change occurs.
124                 Update the graphical input by calling the change handler.
125                 """
126                 if hasattr(self, 'input'): self._handle_changed()
127
128         def get_input_object(self, callback=None):
129                 """!
130                 Get the graphical gtk class to represent this parameter.
131                 Create the input object with this data type and the handle changed method.
132                 @param callback a function of one argument(this param) to be called from the change handler
133                 @return gtk input object
134                 """
135                 self.callback = callback
136                 if self.is_enum(): input = EnumParam
137                 elif self.get_type() in ('file_open', 'file_save'): input = FileParam
138                 else: input = EntryParam
139                 self.input = input(self, self._handle_changed)
140                 return self.input
141
142         def _handle_changed(self, widget=None):
143                 """!
144                 When the input changes, write the inputs to the data type.
145                 Finish by calling the exteral callback.
146                 """
147                 value = self.input.get_text()
148                 if self.is_enum(): value = self.get_option_keys()[int(value)]
149                 self.set_value(value)
150                 #set the markup on the label, red for errors in corresponding data type.
151                 name = '<span font_desc="%s">%s</span>'%(PARAM_LABEL_FONT, Utils.xml_encode(self.get_name()))
152                 #special markups if param is involved in a callback
153                 if hasattr(self.get_parent(), 'get_callbacks') and \
154                         filter(lambda c: self.get_key() in c, self.get_parent()._callbacks):
155                         name = '<span underline="low">%s</span>'%name
156                 if not self.is_valid():
157                         self.input.set_markup('<span foreground="red">%s</span>'%name)
158                         tip = '- ' + '\n- '.join(self.get_error_messages())
159                 else:
160                         self.input.set_markup(name)
161                         tip = self.evaluate()
162                 #hide/show
163                 if self.get_hide() == 'all': self.input.hide_all()
164                 else: self.input.show_all()
165                 #set the tooltip
166                 if self.input.tp: self.input.tp.set_tip(self.input.entry, str(tip))
167                 #execute the external callback
168                 if self.callback: self.callback(self)
169
170         def get_markup(self):
171                 """!
172                 Create a markup to display the Param as a label on the SignalBlock.
173                 If the data type is an Enum type, use the cname of the Enum's current choice.
174                 Otherwise, use parsed the data type and use its string representation.
175                 If the data type is not valid, use a red foreground color.
176                 @return pango markup string
177                 """
178                 ###########################################################################
179                 # display logic for numbers
180                 ###########################################################################
181                 def float_to_str(var):
182                         if var-int(var) == 0: return '%d'%int(var)
183                         if var*10-int(var*10) == 0: return '%.1f'%var
184                         if var*100-int(var*100) == 0: return '%.2f'%var
185                         if var*1000-int(var*1000) == 0: return '%.3f'%var
186                         else: return '%.3g'%var
187                 def to_str(var):
188                         if isinstance(var, str): return var
189                         elif isinstance(var, complex):
190                                 if var.imag == var.real == 0: return '0' #value is zero
191                                 elif var.imag == 0: return '%s'%float_to_str(var.real) #value is real
192                                 elif var.real == 0: return '%sj'%float_to_str(var.imag) #value is imaginary
193                                 elif var.imag < 0: return '%s-%sj'%(float_to_str(var.real), float_to_str(var.imag*-1))
194                                 else: return '%s+%sj'%(float_to_str(var.real), float_to_str(var.imag))
195                         elif isinstance(var, float): return float_to_str(var)
196                         elif isinstance(var, int): return '%d'%var
197                         else: return str(var)
198                 ###########################################################################
199                 if self.is_valid():
200                         data = self.evaluate()
201                         t = self.get_type()
202                         if self.is_enum():
203                                 dt_str = self.get_option(self.get_value()).get_name()
204                         elif isinstance(data, (list, tuple, set)): #vector types
205                                 dt_str = ', '.join(map(to_str, data))
206                         else: dt_str = to_str(data)     #other types
207                         #truncate
208                         max_len = max(42 - len(self.get_name()), 3)
209                         if len(dt_str) > max_len:
210                                 dt_str = dt_str[:max_len-3] + '...'
211                         return '<b>%s:</b> %s'%(Utils.xml_encode(self.get_name()), Utils.xml_encode(dt_str))
212                 else: return '<span foreground="red"><b>%s:</b> error</span>'%Utils.xml_encode(self.get_name())
213
214         def get_layout(self):
215                 """!
216                 Create a layout based on the current markup.
217                 @return the pango layout
218                 """
219                 layout = gtk.DrawingArea().create_pango_layout('')
220                 layout.set_markup(self.get_markup())
221                 desc = pango.FontDescription(PARAM_FONT)
222                 layout.set_font_description(desc)
223                 return layout
224