source: sasview/calculatorview/perspectives/calculator/resolution_calculator_panel.py @ eb9d94a

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since eb9d94a was e91c179, checked in by Jae Cho <jhjcho@…>, 13 years ago

adjust window size mac

  • Property mode set to 100644
File size: 48.0 KB
Line 
1"""
2   This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2008, 2009, 2010 University of Tennessee
9"""
10import wx
11import sys
12import matplotlib
13#Use the WxAgg back end. The Wx one takes too long to render
14matplotlib.use('WXAgg')
15from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
16from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar
17from matplotlib.backend_bases import FigureManagerBase
18# Wx-Pylab magic for displaying plots within an application's window.
19from matplotlib import _pylab_helpers
20# The Figure object is used to create backend-independent plot representations.
21from matplotlib.figure import Figure
22
23#from sans.guicomm.events import StatusEvent 
24from sans.calculator.resolution_calculator import ResolutionCalculator
25from sans.guiframe.events import StatusEvent 
26from calculator_widgets import OutputTextCtrl
27from calculator_widgets import InputTextCtrl
28from wx.lib.scrolledpanel import ScrolledPanel
29from math import fabs
30_BOX_WIDTH = 100
31_Q_DEFAULT = 0.0
32#Slit length panel size
33if sys.platform.count("win32") > 0:
34    PANEL_WIDTH = 525
35    PANEL_HEIGHT = 653
36    FONT_VARIANT = 0
37    IS_WIN = True
38else:
39    PANEL_WIDTH = 540
40    PANEL_HEIGHT = 662
41    FONT_VARIANT = 1
42    IS_WIN = False
43
44_SOURCE_MASS = {'Alpha':6.64465620E-24,
45                'Deuteron':3.34358320E-24,
46                'Neutron':1.67492729E-24, 
47                'Photon': 0.0,
48                'Proton':1.67262137E-24,
49                'Triton':5.00826667E-24}
50
51class ResolutionCalculatorPanel(ScrolledPanel):
52    """
53    Provides the Resolution calculator GUI.
54    """
55    ## Internal nickname for the window, used by the AUI manager
56    window_name = "SANS Resolution Estimator"
57    ## Name to appear on the window title bar
58    window_caption = ""
59    ## Flag to tell the AUI manager to put this panel in the center pane
60    CENTER_PANE = True
61   
62    def __init__(self, parent,  *args, **kwds):
63        kwds["size"]= (PANEL_WIDTH * 2, PANEL_HEIGHT)
64        kwds["style"]= wx.FULL_REPAINT_ON_RESIZE
65        ScrolledPanel.__init__(self, parent, *args, **kwds)
66        self.SetupScrolling()
67        self.parent = parent
68       
69        # input defaults
70        self.qx = []
71        self.qy = []
72        # dQ defaults
73        self.sigma_r = None
74        self.sigma_phi = None
75        self.sigma_1d = None
76        # dQ 2d image
77        self.image = None
78        #Font size
79        self.SetWindowVariant(variant=FONT_VARIANT)
80        # Object that receive status event
81        self.resolution = ResolutionCalculator()
82        # Source selection dic
83        self.source_mass = _SOURCE_MASS
84        #layout attribute
85        self.hint_sizer = None
86        # detector coordinate of estimation of sigmas
87        self.det_coordinate = 'cartesian'
88        self.source_cb = None
89        self._do_layout()
90
91    def _define_structure(self):
92        """
93        Define the main sizers building to build this application.
94        """
95        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
96        self.vertical_l_sizer = wx.BoxSizer(wx.VERTICAL)
97        self.vertical_r_spacer = wx.BoxSizer(wx.VERTICAL)
98        self.vertical_r_frame = wx.StaticBox(self, -1, '')
99        self.vertical_r_sizer = wx.StaticBoxSizer(self.vertical_r_frame,
100                                                  wx.VERTICAL)
101        self.box_source = wx.StaticBox(self, -1,
102                                str(self.window_caption))
103        self.boxsizer_source = wx.StaticBoxSizer(self.box_source,
104                                                    wx.VERTICAL)
105        self.mass_sizer = wx.BoxSizer(wx.HORIZONTAL)
106        self.intensity_sizer = wx.BoxSizer(wx.HORIZONTAL)
107        self.wavelength_sizer = wx.BoxSizer(wx.HORIZONTAL)
108        self.wavelength_spread_sizer = wx.BoxSizer(wx.HORIZONTAL)
109        self.source_aperture_sizer = wx.BoxSizer(wx.HORIZONTAL)
110        self.sample_aperture_sizer = wx.BoxSizer(wx.HORIZONTAL)
111        self.source2sample_distance_sizer = wx.BoxSizer(wx.HORIZONTAL)
112        self.sample2sample_distance_sizer = wx.BoxSizer(wx.HORIZONTAL)
113        self.sample2detector_distance_sizer = wx.BoxSizer(wx.HORIZONTAL)
114        self.detector_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
115        self.detector_pix_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
116        #self.detector_offset_sizer = wx.BoxSizer(wx.HORIZONTAL)
117        self.input_sizer = wx.BoxSizer(wx.VERTICAL)
118        self.output_sizer = wx.BoxSizer(wx.VERTICAL)
119        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
120        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
121       
122    def _layout_mass(self):
123        """
124        Fill the sizer containing mass
125        """
126        # get the mass
127        mass_value = str(self.resolution.mass)
128        self.mass_txt = wx.StaticText(self, -1, 
129                                'Source: ')
130        self.mass_hint = "Mass of Neutrons m = %s [g]"\
131                                 % str(self.resolution.mass)
132        self.source_cb = wx.ComboBox(self, -1,
133                                style=wx.CB_READONLY,
134                                name = '%s'%mass_value)
135        # Sort source name because wx2.9 on Mac does not support CB_SORT
136        # Custom sorting
137        source_list = []
138        for key, value in self.source_mass.iteritems():
139            name_source = str(key)
140            source_list.append(name_source)
141        source_list.sort()
142        for idx in range(len(source_list)):
143            self.source_cb.Append(source_list[idx],idx)
144        self.source_cb.SetStringSelection("Neutron") 
145       
146        wx.EVT_COMBOBOX(self.source_cb,-1, self._on_source_selection)     
147        source_hint = "Source Selection: Affect on"
148        source_hint += " the gravitational contribution.\n"
149        source_hint += "Mass of %s: m = %s [g]" % \
150                            ('Neutron', str(self.resolution.mass))
151        self.mass_txt.SetToolTipString(source_hint)
152        self.mass_sizer.AddMany([(self.mass_txt, 0, wx.LEFT, 15),
153                                    (self.source_cb, 0, wx.LEFT, 15)])   
154       
155    def _layout_intensity(self):
156        """
157        Fill the sizer containing intensity
158        """
159        # get the intensity
160        intensity_value = str(self.resolution.intensity)
161        intensity_unit_txt = wx.StaticText(self, -1, '[counts/s]')
162        intensity_txt = wx.StaticText(self, -1, 
163                                'Intensity: ')
164        self.intensity_tcl = InputTextCtrl(self, -1, 
165                                        size=(_BOX_WIDTH,-1))
166        intensity_hint = "Intensity of Neutrons"
167        self.intensity_tcl.SetValue(intensity_value)
168        self.intensity_tcl.SetToolTipString(intensity_hint)
169        self.intensity_sizer.AddMany([(intensity_txt, 0, wx.LEFT, 15),
170                                    (self.intensity_tcl, 0, wx.LEFT, 15),
171                                    (intensity_unit_txt,0, wx.LEFT, 10)])   
172
173       
174    def _layout_wavelength(self):
175        """
176        Fill the sizer containing wavelength
177        """
178        # get the wavelength
179        wavelength_value = str(self.resolution.wavelength)
180        wavelength_unit_txt = wx.StaticText(self, -1, '[A]')
181        wavelength_txt = wx.StaticText(self, -1, 
182                                'Wavelength: ')
183        self.wavelength_tcl = InputTextCtrl(self, -1, 
184                                         size=(_BOX_WIDTH,-1))
185        wavelength_hint = "Wavelength of Neutrons"
186        self.wavelength_tcl.SetValue(wavelength_value)
187        self.wavelength_tcl.SetToolTipString(wavelength_hint)
188        self.wavelength_sizer.AddMany([(wavelength_txt, 0, wx.LEFT, 15),
189                                    (self.wavelength_tcl, 0, wx.LEFT, 15),
190                                    (wavelength_unit_txt,0, wx.LEFT, 10)])   
191         
192       
193    def _layout_wavelength_spread(self):
194        """
195        Fill the sizer containing wavelength
196        """
197        # get the wavelength
198        wavelength_spread_value = str(self.resolution.wavelength_spread)
199        wavelength_spread_unit_txt = wx.StaticText(self, -1, '')
200        wavelength_spread_txt = wx.StaticText(self, -1, 
201                                'Wavelength Spread: ')
202        self.wavelength_spread_tcl = InputTextCtrl(self, -1, 
203                                         size=(_BOX_WIDTH,-1))
204        wavelength_spread_hint = "Wavelength  Spread of Neutrons"
205        self.wavelength_spread_tcl.SetValue(wavelength_spread_value)
206        self.wavelength_spread_tcl.SetToolTipString(wavelength_spread_hint)
207        self.wavelength_spread_sizer.AddMany([(wavelength_spread_txt, 0, 
208                                               wx.LEFT, 15),
209                                (self.wavelength_spread_tcl, 0, wx.LEFT, 15),
210                                (wavelength_spread_unit_txt,0, wx.LEFT, 10)])     
211         
212       
213    def _layout_source_aperture(self):
214        """
215        Fill the sizer containing source aperture size
216        """
217        # get the wavelength
218        source_aperture_value = str(self.resolution.source_aperture_size[0])
219        if len(self.resolution.source_aperture_size)>1:
220            source_aperture_value += ", "
221            source_aperture_value += str(\
222                                    self.resolution.source_aperture_size[1])
223        source_aperture_unit_txt = wx.StaticText(self, -1, '[cm]')
224        source_aperture_txt = wx.StaticText(self, -1, 
225                                'Source Aperture Size: ')
226        self.source_aperture_tcl = InputTextCtrl(self, -1, 
227                                         size=(_BOX_WIDTH,-1))
228        source_aperture_hint = "Source Aperture Size"
229        self.source_aperture_tcl.SetValue(source_aperture_value)
230        self.source_aperture_tcl.SetToolTipString(source_aperture_hint)
231        self.source_aperture_sizer.AddMany([(source_aperture_txt, 0, 
232                                               wx.LEFT, 15),
233                                (self.source_aperture_tcl, 0, wx.LEFT, 15),
234                                    (source_aperture_unit_txt,0, wx.LEFT, 10)]) 
235
236       
237    def _layout_sample_aperture(self):
238        """
239        Fill the sizer containing sample aperture size
240        """
241        # get the wavelength
242        sample_aperture_value = str(self.resolution.sample_aperture_size[0])
243        if len(self.resolution.sample_aperture_size)>1:
244            sample_aperture_value += ", "
245            sample_aperture_value += str(\
246                                    self.resolution.sample_aperture_size[1])
247        sample_aperture_unit_txt = wx.StaticText(self, -1, '[cm]')
248        sample_aperture_txt = wx.StaticText(self, -1, 
249                                'Sample Aperture Size: ')
250        self.sample_aperture_tcl = InputTextCtrl(self, -1, 
251                                         size=(_BOX_WIDTH,-1))
252        sample_aperture_hint = "Sample Aperture Size"
253        self.sample_aperture_tcl.SetValue(sample_aperture_value)
254        self.sample_aperture_tcl.SetToolTipString(sample_aperture_hint)
255        self.sample_aperture_sizer.AddMany([(sample_aperture_txt, 0, 
256                                               wx.LEFT, 15),
257                                (self.sample_aperture_tcl, 0, wx.LEFT, 15),
258                                    (sample_aperture_unit_txt,0, wx.LEFT, 10)]) 
259
260
261    def _layout_source2sample_distance(self):
262        """
263        Fill the sizer containing souce2sample_distance
264        """
265        # get the wavelength
266        source2sample_distance_value = str(\
267                                    self.resolution.source2sample_distance[0])
268
269        source2sample_distance_unit_txt = wx.StaticText(self, -1, '[cm]')
270        source2sample_distance_txt = wx.StaticText(self, -1, 
271                                'Source to Sample Aperture Distance: ')
272        self.source2sample_distance_tcl = InputTextCtrl(self, -1, 
273                                         size=(_BOX_WIDTH,-1))
274        source2sample_distance_hint = "Source to Sample Aperture Distance"
275        self.source2sample_distance_tcl.SetValue(source2sample_distance_value)
276        self.source2sample_distance_tcl.SetToolTipString(\
277                                                source2sample_distance_hint)
278        self.source2sample_distance_sizer.AddMany([(source2sample_distance_txt, 
279                                               0, wx.LEFT, 15),
280                            (self.source2sample_distance_tcl, 0, wx.LEFT, 15),
281                            (source2sample_distance_unit_txt,0, wx.LEFT, 10)]) 
282
283    def _layout_sample2sample_distance(self):
284        """
285        Fill the sizer containing sampleslit2sample_distance
286        """
287        # get the distance
288        sample2sample_distance_value = str(\
289                                    self.resolution.sample2sample_distance[0])
290
291        sample2sample_distance_unit_txt = wx.StaticText(self, -1, '[cm]')
292        sample2sample_distance_txt = wx.StaticText(self, -1, 
293                                'Sample Offset: ')
294        self.sample2sample_distance_tcl = InputTextCtrl(self, -1, 
295                                         size=(_BOX_WIDTH,-1))
296        sample2sample_distance_hint = "Sample Aperture to Sample Distance"
297        self.sample2sample_distance_tcl.SetValue(sample2sample_distance_value)
298        self.sample2sample_distance_tcl.SetToolTipString(\
299                                                sample2sample_distance_hint)
300        self.sample2sample_distance_sizer.AddMany([(sample2sample_distance_txt, 
301                                               0, wx.LEFT, 15),
302                            (self.sample2sample_distance_tcl, 0, wx.LEFT, 15),
303                            (sample2sample_distance_unit_txt,0, wx.LEFT, 10)]) 
304
305
306
307    def _layout_sample2detector_distance(self):
308        """
309        Fill the sizer containing sample2detector_distance
310        """
311        # get the wavelength
312        sample2detector_distance_value = str(\
313                                    self.resolution.sample2detector_distance[0])
314
315        sample2detector_distance_unit_txt = wx.StaticText(self, -1, '[cm]')
316        sample2detector_distance_txt = wx.StaticText(self, -1, 
317                                'Sample Aperture to Detector Distance: ')
318        self.sample2detector_distance_tcl = InputTextCtrl(self, -1, 
319                                         size=(_BOX_WIDTH,-1))
320        sample2detector_distance_hint = \
321                                "Sample Aperture to Detector Distance"
322        self.sample2detector_distance_tcl.SetValue(\
323                                                sample2detector_distance_value)
324        self.sample2detector_distance_tcl.SetToolTipString(\
325                                                sample2detector_distance_hint)
326        self.sample2detector_distance_sizer.AddMany([\
327                            (sample2detector_distance_txt, 0, wx.LEFT, 15),       
328                            (self.sample2detector_distance_tcl, 0, wx.LEFT, 15),
329                            (sample2detector_distance_unit_txt,0, wx.LEFT, 10)]) 
330       
331    def _layout_detector_size(self):
332        """
333        Fill the sizer containing detector size
334        """
335        # get the wavelength
336        detector_size_value = str(self.resolution.detector_size[0])
337        if len(self.resolution.detector_size)>1:
338            detector_size_value += ", "
339            detector_size_value += str(self.resolution.detector_size[1])
340        detector_size_unit_txt = wx.StaticText(self, -1, '')
341        detector_size_txt = wx.StaticText(self, -1, 
342                                'Number of Pixels on Detector: ')
343        self.detector_size_tcl = InputTextCtrl(self, -1, 
344                                         size=(_BOX_WIDTH,-1))
345        detector_size_hint = "Number of Pixels on Detector"
346        self.detector_size_tcl.SetValue(detector_size_value)
347        self.detector_size_tcl.SetToolTipString(detector_size_hint)
348        self.detector_size_sizer.AddMany([(detector_size_txt, 0, 
349                                               wx.LEFT, 15),
350                                (self.detector_size_tcl, 0, wx.LEFT, 15),
351                                    (detector_size_unit_txt,0, wx.LEFT, 10)]) 
352
353       
354    def _layout_detector_pix_size(self):
355        """
356        Fill the sizer containing detector pixel size
357        """
358        # get the detector_pix_size
359        detector_pix_size_value = str(self.resolution.detector_pix_size[0])
360        if len(self.resolution.detector_pix_size)>1:
361            detector_pix_size_value += ", "
362            detector_pix_size_value += str(self.resolution.detector_pix_size[1])
363        detector_pix_size_unit_txt = wx.StaticText(self, -1, '[cm]')
364        detector_pix_size_txt = wx.StaticText(self, -1, 
365                                'Detector Pixel Size: ')
366        self.detector_pix_size_tcl = InputTextCtrl(self, -1, 
367                                         size=(_BOX_WIDTH,-1))
368        detector_pix_size_hint = "Detector Pixel Size"
369        self.detector_pix_size_tcl.SetValue(detector_pix_size_value)
370        self.detector_pix_size_tcl.SetToolTipString(detector_pix_size_hint)
371        self.detector_pix_size_sizer.AddMany([(detector_pix_size_txt, 0, 
372                                               wx.LEFT, 15),
373                                (self.detector_pix_size_tcl, 0, wx.LEFT, 15),
374                                (detector_pix_size_unit_txt,0, wx.LEFT, 10)])
375       
376    def _layout_input(self):
377        """
378        Fill the sizer containing inputs; qx, qy
379        """
380       
381        q_title = wx.StaticText(self, -1, 
382                            "[Q Location of the Estimation]:")
383        # sizers for inputs
384        inputQx_sizer = wx.BoxSizer(wx.HORIZONTAL)
385        inputQy_sizer = wx.BoxSizer(wx.HORIZONTAL)
386        # get the default dq
387        qx_value = str(_Q_DEFAULT)
388        qy_value = str(_Q_DEFAULT)
389        qx_unit_txt = wx.StaticText(self, -1, '[1/A]  ')
390        qy_unit_txt = wx.StaticText(self, -1, '[1/A]  ')
391        qx_name_txt = wx.StaticText(self, -1, 
392                                'Qx: ')
393        qy_name_txt = wx.StaticText(self, -1, 
394                                'Qy: ')
395        self.qx_tcl = InputTextCtrl(self, -1, 
396                                         size=(_BOX_WIDTH*3,-1))
397        self.qy_tcl = InputTextCtrl(self, -1, 
398                                         size=(_BOX_WIDTH*3,-1))
399        qx_hint = "Type the Qx value."
400        qy_hint = "Type the Qy value."
401        self.qx_tcl.SetValue(qx_value)
402        self.qy_tcl.SetValue(qy_value)
403        self.qx_tcl.SetToolTipString(qx_hint)
404        self.qy_tcl.SetToolTipString(qy_hint)
405        inputQx_sizer.AddMany([(qx_name_txt, 0, wx.LEFT, 15),
406                                    (self.qx_tcl, 0, wx.LEFT, 15),
407                                    (qx_unit_txt, 0, wx.LEFT, 15)])
408        inputQy_sizer.AddMany([(qy_name_txt, 0, wx.LEFT, 15),
409                                    (self.qy_tcl, 0, wx.LEFT, 15),
410                                    (qy_unit_txt, 0, wx.LEFT, 15)])
411        self.input_sizer.AddMany([(q_title, 0, wx.LEFT, 15), 
412                                    (inputQx_sizer, 0,
413                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
414                                    (inputQy_sizer, 0, 
415                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
416                                #(self.compute_button, 0, wx.LEFT, 30)])
417       
418    def _layout_output(self):
419        """
420        Fill the sizer containing dQ|| and dQ+
421        """
422        sigma_title = wx.StaticText(self, -1, 
423                        "[Standard Deviation of the Resolution Distribution]:")
424         # sizers for inputs
425        outputQxy_sizer = wx.BoxSizer(wx.HORIZONTAL)
426        outputQ_sizer = wx.BoxSizer(wx.HORIZONTAL)
427        sigma_unit = '['+'1/A' +']'
428        self.sigma_r_txt = wx.StaticText(self, -1, 
429                                           'Sigma_x:   ')
430        self.sigma_r_tcl = OutputTextCtrl(self, -1, 
431                                                 size=(_BOX_WIDTH*0.8,-1))
432        self.sigma_phi_txt = wx.StaticText(self, -1, 
433                                           'Sigma_y:')
434        self.sigma_phi_tcl = OutputTextCtrl(self, -1, 
435                                                 size=(_BOX_WIDTH*0.8,-1))
436        self.sigma_lamd_txt = wx.StaticText(self, -1, 
437                                           'Sigma_lamd:')
438        self.sigma_lamd_tcl = OutputTextCtrl(self, -1, 
439                                                 size=(_BOX_WIDTH*0.7,-1))
440        sigma_1d_txt = wx.StaticText(self, -1, '( 1D:   Sigma:')
441        self.sigma_1d_tcl = OutputTextCtrl(self, -1, 
442                                                 size=(_BOX_WIDTH*0.7,-1))
443        sigmax_hint = " The x component of the geometric resolution,"
444        sigmax_hint +=  " excluding sigma_lamda."
445        sigmay_hint = " The y component of the geometric resolution,"
446        sigmay_hint +=  " excluding sigma_lamda."
447        sigma_hint_lamd = " The wavelength contribution in the radial direction"
448        sigma_hint_lamd += ".\n Note: The phi component is always zero."
449        sigma_hint_1d = " Resolution in 1-dimension (for 1D data)."
450        self.sigma_r_tcl.SetToolTipString(sigmax_hint)
451        self.sigma_phi_tcl.SetToolTipString(sigmay_hint)
452        self.sigma_lamd_tcl.SetToolTipString(sigma_hint_lamd)
453        self.sigma_1d_tcl.SetToolTipString(sigma_hint_1d)
454        sigma_r_unit_txt = wx.StaticText(self, -1, sigma_unit)
455        sigma_phi_unit_txt = wx.StaticText(self, -1, sigma_unit)
456        sigma_lamd_unit_txt = wx.StaticText(self, -1, sigma_unit)
457        sigma_1d_unit_txt = wx.StaticText(self, -1, sigma_unit+' )')
458        outputQxy_sizer.AddMany([(self.sigma_r_txt, 0, wx.LEFT, 15),
459                                    (self.sigma_r_tcl, 0, wx.LEFT, 15),
460                                    (sigma_r_unit_txt, 0, wx.LEFT, 15),
461                                    (self.sigma_phi_txt, 0, wx.LEFT, 15),
462                                    (self.sigma_phi_tcl, 0, wx.LEFT, 15),
463                                    (sigma_phi_unit_txt, 0, wx.LEFT, 15)])
464        outputQ_sizer.AddMany([(self.sigma_lamd_txt, 0, wx.LEFT, 15),
465                                    (self.sigma_lamd_tcl, 0, wx.LEFT, 15),
466                                    (sigma_lamd_unit_txt, 0, wx.LEFT, 15),
467                                    (sigma_1d_txt, 0, wx.LEFT, 15),
468                                    (self.sigma_1d_tcl, 0, wx.LEFT, 15),
469                                    (sigma_1d_unit_txt, 0, wx.LEFT, 15)])
470        self.output_sizer.AddMany([(sigma_title, 0, wx.LEFT, 15), 
471                                    (outputQxy_sizer, 0,
472                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
473                                    (outputQ_sizer, 0, 
474                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
475       
476   
477    def _layout_hint(self):
478        """
479        Fill the sizer containing hint
480        """
481        hint_msg = ""
482        #hint_msg += "This tool is to approximately compute "
483        #hint_msg += "the resolution (dQ)."
484
485        self.hint_txt = wx.StaticText(self, -1, hint_msg)
486        self.hint_sizer.AddMany([(self.hint_txt, 0, wx.LEFT, 15)])
487   
488    def _layout_button(self): 
489        """
490        Do the layout for the button widgets
491        """ 
492        #outerbox_txt = wx.StaticText(self, -1, 'Outer Box')
493        #self.x_y_rb = wx.RadioButton(self, -1,"Cartesian")
494        #self.Bind(wx.EVT_RADIOBUTTON,
495        #          self._on_xy_coordinate, id=self.x_y_rb.GetId())
496        #self.r_phi_rb = wx.RadioButton(self, -1,"Polar")
497        #self.Bind(wx.EVT_RADIOBUTTON,
498        #          self._on_rp_coordinate, id=self.r_phi_rb.GetId())
499        #self.r_phi_rb.SetValue(True)
500        #reset button
501        id = wx.NewId()
502        self.reset_button = wx.Button(self, id, "Reset")
503        hint_on_reset = "..."
504        self.reset_button.SetToolTipString(hint_on_reset)
505        self.Bind(wx.EVT_BUTTON, self.on_reset, id=id)
506        #compute button
507        id = wx.NewId()
508        self.compute_button = wx.Button(self, id, "Compute")
509        hint_on_compute = "..."
510        self.compute_button.SetToolTipString(hint_on_compute)
511        self.Bind(wx.EVT_BUTTON, self.on_compute, id=id)
512        # close button
513        self.bt_close = wx.Button(self, wx.ID_CANCEL,'Close')
514        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
515        self.bt_close.SetToolTipString("Close this window.")
516        """
517        self.button_sizer.AddMany([(self.r_phi_rb,  0, wx.LEFT, 15),
518                                   (self.x_y_rb,  0, wx.LEFT, 15),
519                                   (self.reset_button, 0, wx.LEFT, 50),
520                                   (self.compute_button, 0, wx.LEFT, 15),
521                                   (self.bt_close, 0, wx.LEFT, 15)])#370)])
522        """
523        self.button_sizer.Add((110, -1))
524        self.button_sizer.AddMany([(self.reset_button, 0, wx.LEFT, 50),
525                                   (self.compute_button, 0, wx.LEFT, 15),
526                                   (self.bt_close, 0, wx.LEFT, 15)])
527        self.compute_button.SetFocus()
528       
529    def _layout_image(self):
530        """
531        Layout for image plot
532        """
533        color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
534
535        # Contribution by James C.
536        # Instantiate a figure object that will contain our plots.
537        # Make the fig a little smaller than the default
538        self.figure = Figure(figsize=(6.5, 6), facecolor = 'white')
539       
540        # Initialize the figure canvas, mapping the figure object to the plot
541        # engine backend.
542        self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure)
543
544        # Wx-Pylab magic ...
545        # Make our canvas the active figure manager for pylab so that when
546        # pylab plotting statements are executed they will operate on our
547        # canvas and not create a new frame and canvas for display purposes.
548        # This technique allows this application to execute code that uses
549        # pylab stataments to generate plots and embed these plots in our
550        # application window(s).
551        self.fm = FigureManagerBase(self.canvas, 1)
552        _pylab_helpers.Gcf.set_active(self.fm)
553       
554        # Instantiate the matplotlib navigation toolbar and explicitly show it.
555        mpl_toolbar = Toolbar(self.canvas)
556        # Diable pan
557        mpl_toolbar.DeleteToolByPos(3)
558
559        # Add a toolbar into the frame
560        mpl_toolbar.Realize()
561
562        # Compute before adding the canvas to the sizer
563        self.on_compute()
564       
565        # Fill up the sizer
566        if IS_WIN:
567            gap = 27
568        else:
569            gap = 13
570        self.vertical_r_sizer.Add(self.canvas, 0, 
571                                       wx.ALL|wx.EXPAND, 2) 
572        self.vertical_r_spacer.Add((0, gap)) 
573        self.vertical_r_spacer.Add(self.vertical_r_sizer, 0, 
574                                       wx.ALL|wx.EXPAND, 2)
575        self.vertical_r_spacer.Add((0, gap)) 
576        self.vertical_r_spacer.Add(wx.StaticLine(self), 0, 
577                                       wx.ALL|wx.EXPAND, 2)
578        self.vertical_r_spacer.Add(mpl_toolbar, 0,  wx.ALL|wx.EXPAND, 2)
579
580       
581    def _do_layout(self):
582        """
583            Draw window layout
584        """
585        #  Title of parameters
586        instrument_txt = wx.StaticText(self, -1, 
587                                '[Instrumental Parameters]:')
588        # Build individual layouts
589        self._define_structure()
590        self._layout_mass()
591        #self._layout_intensity()
592        self._layout_wavelength()
593        self._layout_wavelength_spread()
594        self._layout_source_aperture()
595        self._layout_sample_aperture()
596        self._layout_source2sample_distance()
597        self._layout_sample2detector_distance()
598        self._layout_sample2sample_distance()
599        self._layout_detector_size()
600        self._layout_detector_pix_size()
601        self._layout_input()
602        self._layout_output()
603        self._layout_hint()
604        self._layout_button()
605        # Fill the sizers
606        self.boxsizer_source.AddMany([(instrument_txt, 0,
607                                       wx.EXPAND|wx.LEFT, 15),
608                                      (self.mass_sizer, 0,
609                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
610                                      #(self.intensity_sizer, 0,
611                                      #wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
612                                      (self.wavelength_sizer, 0,
613                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
614                                      (self.wavelength_spread_sizer, 0,
615                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
616                                      (self.source_aperture_sizer, 0,
617                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
618                                      (self.sample_aperture_sizer, 0,
619                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
620                                      (self.source2sample_distance_sizer, 0,
621                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
622                                      (self.sample2detector_distance_sizer, 0,
623                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
624                                      (self.sample2sample_distance_sizer, 0,
625                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
626                                      (self.detector_size_sizer, 0,
627                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
628                                      (self.detector_pix_size_sizer, 0,
629                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
630                                      (wx.StaticLine(self), 0, 
631                                       wx.ALL|wx.EXPAND, 5),
632                                      (self.input_sizer, 0,
633                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
634                                      (wx.StaticLine(self), 0, 
635                                       wx.ALL|wx.EXPAND, 5),
636                                      (self.output_sizer, 0,
637                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
638        self.vertical_l_sizer.AddMany([(self.boxsizer_source, 0, wx.ALL, 10),
639                                       (wx.StaticLine(self), 0, 
640                                       wx.ALL|wx.EXPAND, 5),
641                                  (self.button_sizer, 0,
642                                    wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
643        self.main_sizer.Add(self.vertical_l_sizer, 0, wx.ALL, 10)
644        # Build image plot layout                     
645        self._layout_image()
646        # Add a vertical static line
647        self.main_sizer.Add( wx.StaticLine(self, -1, (2,2), 
648                            (2,PANEL_HEIGHT * 0.94), style = wx.LI_VERTICAL))
649        # Add the plot to main sizer
650        self.main_sizer.Add(self.vertical_r_spacer, 0, wx.ALL, 10)
651        self.SetSizer(self.main_sizer)
652        self.SetAutoLayout(True)
653
654    def on_close(self, event):
655        """
656        close the window containing this panel
657        """
658        # get ready for other events
659        if event is not None:
660            event.Skip()
661        # Clear the plot
662        if self.image != None:
663            self.image.clf()
664            #self.image.draw()
665        # Close panel
666        self.parent.Close()
667        # reset image
668        self.image = None
669   
670    def on_compute(self, event = None): 
671        """
672        Execute the computation of resolution
673        """
674        wx.CallAfter(self.on_compute_call, event)
675       
676    def on_compute_call(self, event = None):
677        """
678        Execute the computation of resolution
679        """
680        # Skip event for next event
681        if event != None:
682            event.Skip()
683            msg = "Please Check your input values "
684            msg += "before starting the computation..."
685
686        # message
687        status_type = 'progress' 
688        msg = 'Calculating...'
689        self._status_info(msg, status_type)
690
691        status_type = 'stop'           
692        # Q min max list default
693        qx_min = []   
694        qx_max = [] 
695        qy_min = [] 
696        qy_max = [] 
697        # Get all the values at set to compute
698        #intensity = self.intensity_tcl.GetValue()
699        #self.resolution.set_intensity(float(intensity))
700        wavelength = self.wavelength_tcl.GetValue()
701        self.resolution.set_wavelength(float(wavelength))
702        source = self.source_cb.GetValue()
703        mass = self.source_mass[str(source)]
704        self.resolution.set_neutron_mass(float(mass))
705        wavelength_spread = self.wavelength_spread_tcl.GetValue()
706        self.resolution.set_wavelength_spread(float(wavelength_spread))
707        source_aperture_size = self.source_aperture_tcl.GetValue()
708        source_aperture_size = self._string2list(source_aperture_size)
709        self.resolution.set_source_aperture_size(source_aperture_size)
710        sample_aperture_size = self.sample_aperture_tcl.GetValue()
711        sample_aperture_size = self._string2list(sample_aperture_size)
712        self.resolution.set_sample_aperture_size(sample_aperture_size)
713        source2sample_distance = self.source2sample_distance_tcl.GetValue()
714        source2sample_distance = self._string2list(source2sample_distance)
715        self.resolution.set_source2sample_distance(source2sample_distance)
716        sample2sample_distance = self.sample2sample_distance_tcl.GetValue()
717        sample2sample_distance = self._string2list(sample2sample_distance)
718        self.resolution.set_sample2sample_distance(sample2sample_distance)
719        sample2detector_distance = self.sample2detector_distance_tcl.GetValue()
720        sample2detector_distance = self._string2list(sample2detector_distance)
721        self.resolution.set_sample2detector_distance(sample2detector_distance)
722        detector_size = self.detector_size_tcl.GetValue()
723        detector_size = self._string2list(detector_size)
724        self.resolution.set_detector_size(detector_size)
725        detector_pix_size = self.detector_pix_size_tcl.GetValue()
726        detector_pix_size = self._string2list(detector_pix_size)
727        self.resolution.set_detector_pix_size(detector_pix_size)
728        self.qx = self._string2inputlist(self.qx_tcl.GetValue())
729        self.qy = self._string2inputlist(self.qy_tcl.GetValue())
730       
731        try:
732            # Find min max of qs
733            xmin = min(self.qx)
734            xmax = max(self.qx)
735            ymin = min(self.qy)
736            ymax = max(self.qy)
737        except:
738            msg = "An error occured during the resolution computation."
739            msg += "Please check your inputs..."
740            self._status_info(msg, status_type)
741            raise ValueError, "Invalid Q Input..."
742
743        # Validate the q inputs
744        self._validate_q_input(self.qx, self.qy)
745       
746        # Make list of q min max for mapping
747        for length in range(len(self.qx)):
748            qx_min.append(xmin)
749            qx_max.append(xmax)
750        for length in range(len(self.qy)):
751            qy_min.append(ymin)
752            qy_max.append(ymax)
753       
754        # Compute the resolution
755        if self.image != None:
756            #_pylab_helpers.Gcf.set_active(self.fm)
757            # Clear the image before redraw
758            self.image.clf()
759            #self.image.draw()
760            # reset the image
761            self.resolution.reset_image()
762
763        # Compute and get the image plot
764        try:
765            self.image = map(self._map_func, self.qx, self.qy, 
766                             qx_min, qx_max, qy_min, qy_max)[0]
767            msg = "Finished the resolution computation..."
768            self._status_info(msg, status_type)
769        except:
770            msg = "An error occured during the resolution computation."
771            msg += "Please check your inputs..."
772            self._status_info(msg, status_type)
773            raise ValueError, "Invalid Q Input: Out of detector range..."
774       
775        # Draw lines in image before drawing
776        self._draw_lines(self.image)
777        # Draw image
778        self.image.draw()
779        #self.vertical_r_sizer.Layout()
780       
781        # Get and format the sigmas
782        sigma_r = self.format_number(self.resolution.sigma_1)
783        sigma_phi = self.format_number(self.resolution.sigma_2)
784        sigma_lamd = self.format_number(self.resolution.sigma_lamd)
785        sigma_1d =  self.format_number(self.resolution.sigma_1d)
786
787        # Set output values
788        self.sigma_r_tcl.SetValue(str(sigma_r))
789        self.sigma_phi_tcl.SetValue(str(sigma_phi))
790        self.sigma_lamd_tcl.SetValue(str(sigma_lamd))
791        self.sigma_1d_tcl.SetValue(str(sigma_1d))
792       
793    def _draw_lines(self, image = None):
794        """
795        Draw lines in image if applicable
796        : Param image: pylab object
797        """
798        if image == None:
799            return
800       
801        # Get the params from resolution
802        # ploting range
803        qx_min = self.resolution.qx_min
804        qx_max = self.resolution.qx_max
805        qy_min = self.resolution.qy_min
806        qy_max = self.resolution.qy_max
807       
808        # detector range
809        detector_qx_min = self.resolution.detector_qx_min
810        detector_qx_max = self.resolution.detector_qx_max
811        detector_qy_min = self.resolution.detector_qy_min
812        detector_qy_max = self.resolution.detector_qy_max
813       
814        # Draw zero axis lines
815        if qy_min < 0 and qy_max >= 0:
816            image.axhline(linewidth = 1)
817        if qx_min < 0 and qx_max >= 0:
818            image.axvline(linewidth = 1)
819           
820        # Find x and y ratio values to draw the detector outline
821        x_min = fabs(detector_qx_min - qx_min) / (qx_max - qx_min)
822        x_max = fabs(detector_qx_max - qx_min) / (qx_max - qx_min)
823        y_min = fabs(detector_qy_min - qy_min) / (qy_max - qy_min)
824        y_max = fabs(detector_qy_max - qy_min) / (qy_max - qy_min)
825       
826        # Draw Detector outline
827        if detector_qy_min >= qy_min:
828            image.axhline(y = detector_qy_min + 0.0002,
829                               xmin = x_min,
830                               xmax = x_max, 
831                               linewidth = 2, color='r')
832        if detector_qy_max <= qy_max:
833            image.axhline(y = detector_qy_max - 0.0002, 
834                               xmin = x_min, 
835                               xmax = x_max, 
836                               linewidth = 2, color='r')
837        if detector_qx_min >= qx_min:
838            image.axvline(x = detector_qx_min + 0.0002, 
839                               ymin = y_min, 
840                               ymax = y_max, 
841                               linewidth = 2, color='r')
842        if detector_qx_max <= qx_max:
843            image.axvline(x = detector_qx_max - 0.0002, 
844                               ymin = y_min, 
845                               ymax = y_max, 
846                               linewidth = 2, color='r')
847
848    def _map_func(self, qx, qy, qx_min, qx_max, qy_min, qy_max):   
849        """
850        Prepare the Mapping for the computation
851        : params qx, qy, qx_min, qx_max, qy_min, qy_max:
852       
853        : return: image (pylab)
854        """
855        # calculate 2D resolution distribution image
856        image = self.resolution.compute_and_plot(float(qx), float(qy), 
857                                 qx_min, qx_max, qy_min, qy_max, 
858                                 self.det_coordinate)
859        return image
860   
861    def _validate_q_input(self, qx, qy):   
862        """
863        Check if q inputs are valid
864        : params qx:  qx as a list
865        : params qy:  qy as a list
866       
867        : return: True/False
868        """
869        # check qualifications
870        if qx.__class__.__name__ != 'list':
871            return False
872        if qy.__class__.__name__ != 'list' :
873            return False
874        if len(qx) < 1:
875            return False
876        if len(qy) < 1:
877            return False
878        # allow one input
879        if len(qx) == 1 and len(qy) > 1:
880            qx = [qx[0] for ind in range(len(qy))]
881            self.qx = qx
882        if len(qy) == 1 and len(qx) > 1:
883            qy = [qy[0] for ind in range(len(qx))]
884            self.qy = qy
885        # check length
886        if len(qx) != len(qy):
887            return False
888
889        return True 
890     
891    def on_reset(self, event):
892        """
893        Execute the reset
894        """
895        # skip for another event
896        if event != None:
897            event.Skip() 
898        # init resolution_calculator
899        self.resolution = ResolutionCalculator()
900        self.resolution.get_all_instrument_params()
901        # reset all param values
902        self.source_cb.SetValue('Neutron')
903        self._on_source_selection(None)
904        #self.intensity_tcl.SetValue(str(self.resolution.intensity))
905        self.wavelength_tcl.SetValue(str(self.resolution.wavelength))
906        self.wavelength_spread_tcl.SetValue(\
907                                        str(self.resolution.wavelength_spread))
908        source_aperture_value = str(self.resolution.source_aperture_size[0])
909        if len(self.resolution.source_aperture_size)>1:
910            source_aperture_value += ", "
911            source_aperture_value += \
912                str(self.resolution.source_aperture_size[1])
913        self.source_aperture_tcl.SetValue(str(source_aperture_value))
914        sample_aperture_value = str(self.resolution.sample_aperture_size[0])
915        if len(self.resolution.sample_aperture_size)>1:
916            sample_aperture_value += ", "
917            sample_aperture_value += \
918                str(self.resolution.sample_aperture_size[1])
919        self.sample_aperture_tcl.SetValue(sample_aperture_value)
920        source2sample_distance_value = \
921            str(self.resolution.source2sample_distance[0])
922        self.source2sample_distance_tcl.SetValue(source2sample_distance_value)
923        sample2sample_distance_value = \
924            str(self.resolution.sample2sample_distance[0])
925        self.sample2sample_distance_tcl.SetValue(sample2sample_distance_value)
926        sample2detector_distance_value = \
927            str(self.resolution.sample2detector_distance[0])
928        self.sample2detector_distance_tcl.SetValue(\
929                                            sample2detector_distance_value)
930        detector_size_value = str(self.resolution.detector_size[0])
931        if len(self.resolution.detector_size)>1:
932            detector_size_value += ", "
933            detector_size_value += str(self.resolution.detector_size[1])
934        self.detector_size_tcl.SetValue(detector_size_value)
935        detector_pix_size_value = str(self.resolution.detector_pix_size[0])
936        if len(self.resolution.detector_pix_size)>1:
937            detector_pix_size_value += ", "
938            detector_pix_size_value += str(self.resolution.detector_pix_size[1])
939        self.detector_pix_size_tcl.SetValue(detector_pix_size_value)
940        #layout attribute
941        self.hint_sizer = None
942        # reset q inputs
943        self.qx_tcl.SetValue(str(_Q_DEFAULT))
944        self.qy_tcl.SetValue(str(_Q_DEFAULT))
945        # reset sigma outputs
946        self.sigma_r_tcl.SetValue('')
947        self.sigma_phi_tcl.SetValue('')
948        self.sigma_1d_tcl.SetValue('')
949        # reset radio button
950        #self.r_phi_rb.SetValue(True)
951        # Finally re-compute
952        self.on_compute()
953        # msg on info
954        msg = " Finished the resetting..."
955        self._status_info(msg, 'stop')
956       
957    def format_number(self, value=None):
958        """
959        Return a float in a standardized, human-readable formatted string
960        """
961        try: 
962            value = float(value)
963        except:
964            output = None
965            return output
966
967        output = "%-7.4g" % value
968        return output.lstrip().rstrip() 
969     
970    def _string2list(self, string):
971        """
972        Change NNN, NNN to list,ie. [NNN, NNN] where NNN is a number
973        """
974        new_string = []
975        # check the number of floats
976        try:
977            strg = float(string)
978            new_string.append(strg)
979            #new_string.append(0)
980        except:
981            string_split = string.split(',')
982            if len(string_split) == 2:
983                str_1 = string_split[0]
984                str_2 = string_split[1]
985                new_string.append(float(str_1))
986                new_string.append(float(str_2))
987            elif len(string_split) == 1:
988                str_1 = string_split[0]
989                new_string.append(float(str_1))
990            else:
991                msg = "The numbers must be one or two (separated by ',')..."
992                self._status_info(msg, 'stop')
993                raise RuntimeError, msg
994
995        return new_string
996   
997    def _string2inputlist(self, string):
998        """
999        Change NNN, NNN,... to list,ie. [NNN, NNN,...] where NNN is a number
1000       
1001        : return new_string: string like list
1002        """
1003        new_string = []
1004        string_split = string.split(',')
1005        length = len(string_split)
1006        for ind in range(length):
1007            try:
1008                value = float(string_split[ind])
1009                new_string.append(value)
1010            except:
1011                pass
1012       
1013        return new_string
1014
1015    def _on_xy_coordinate(self,event=None):
1016        """
1017        Set the detector coordinate for sigmas to x-y coordinate
1018        """
1019        if event != None:
1020            event.Skip()       
1021        # Set the coordinate in Cartesian       
1022        self.det_coordinate = 'cartesian'
1023        self.sigma_r_txt.SetLabel('Sigma_x:')
1024        self.sigma_phi_txt.SetLabel('Sigma_y:')
1025        self._onparamEnter()
1026       
1027    def _on_rp_coordinate(self,event=None):
1028        """
1029        Set the detector coordinate for sigmas to polar coordinate
1030        """
1031        if event != None:
1032            event.Skip()       
1033        # Set the coordinate in polar           
1034        self.det_coordinate = 'polar'
1035        self.sigma_r_txt.SetLabel('Sigma_r:   ')
1036        self.sigma_phi_txt.SetLabel('Sigma_phi:')
1037        self._onparamEnter()
1038       
1039    def _status_info(self, msg = '', type = "update"):
1040        """
1041        Status msg
1042        """
1043        if self.parent.parent != None:
1044                wx.PostEvent(self.parent.parent, 
1045                             StatusEvent(status = msg, type = type ))
1046
1047
1048    def _onparamEnter(self, event = None):
1049        """
1050        On Text_enter_callback, perform compute
1051        """
1052        self.on_compute()
1053       
1054    def _on_source_selection(self, event = None):
1055        """
1056        On source combobox selection
1057        """
1058        if event != None:
1059            combo = event.GetEventObject()
1060            event.Skip()
1061        else:
1062            combo = self.source_cb
1063        selection = combo.GetValue()
1064        mass = self.source_mass[selection]
1065        self.resolution.set_neutron_mass(mass)   
1066        source_hint = "Source Selection: Affect on"
1067        source_hint += " the gravitational contribution.\n"
1068        source_hint += "Mass of %s: m = %s [g]" % \
1069                            (selection, str(self.resolution.get_neutron_mass()))
1070        #source_tip.SetTip(source_hint)
1071        self.mass_txt.ToolTip.SetTip(source_hint)
1072       
1073class ResolutionWindow(wx.Frame):
1074    def __init__(self, parent = None, title = "SANS Resolution Estimator",
1075                  size=(PANEL_WIDTH * 2, PANEL_HEIGHT), *args, **kwds):
1076        kwds['title'] = title
1077        kwds['size'] = size
1078        wx.Frame.__init__(self, parent=None, *args, **kwds)
1079        self.parent = parent
1080        self.panel = ResolutionCalculatorPanel(parent=self)
1081        self.Centre()
1082        self.Show(True)
1083       
1084if __name__ == "__main__": 
1085    app = wx.PySimpleApp()
1086    frame = ResolutionWindow()   
1087    frame.Show(True)
1088    app.MainLoop()     
Note: See TracBrowser for help on using the repository browser.