source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 95b2bad

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 95b2bad was 0275276, checked in by Jae Cho <jhjcho@…>, 14 years ago

fixed plot_panel highlight problem

  • Property mode set to 100644
File size: 15.7 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2008, University of Tennessee
10################################################################################
11
12
13import wx
14import sys
15import os
16import pylab
17import math
18import numpy
19import time
20
21from danse.common.plottools.PlotPanel import PlotPanel
22from danse.common.plottools.plottables import Graph
23from sans.guiframe import dataFitting
24from sans.guiframe.events import EVT_NEW_PLOT
25from sans.guiframe.events import StatusEvent
26from sans.guiframe.events import NewPlotEvent
27from sans.guiframe.events import SlicerEvent
28from sans.guiframe.events import RemoveDataEvent
29from sans.guiframe.events import PanelOnFocusEvent
30from sans.guiframe.events import EVT_NEW_LOADED_DATA
31from sans.guiframe.utils import PanelMenu
32from sans.guiframe.dataFitting import Data1D
33from sans.guiframe.panel_base import PanelBase
34from binder import BindArtist
35
36DEFAULT_QMAX = 0.05
37DEFAULT_QSTEP = 0.001
38DEFAULT_BEAM = 0.005
39BIN_WIDTH = 1
40
41
42class ModelPanel1D(PlotPanel, PanelBase):
43    """
44    Plot panel for use with the GUI manager
45    """
46   
47    ## Internal name for the AUI manager
48    window_name = "plotpanel"
49    ## Title to appear on top of the window
50    window_caption = "Plot Panel"
51    ## Flag to tell the GUI manager that this panel is not
52    #  tied to any perspective
53    ALWAYS_ON = True
54    ## Group ID
55    group_id = None
56   
57    def __init__(self, parent, id=-1, color = None,
58                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
59        PlotPanel.__init__(self, parent, id=id, style=style, **kwargs)
60        PanelBase.__init__(self, parent)
61        ## Reference to the parent window
62        self.parent = parent
63        ## Plottables
64        self.plots = {}
65        #context menu
66        self._slicerpop = None
67       
68        self._available_data = []
69        self._menu_add_ids = []
70        self._symbol_labels = self.get_symbol_label()
71     
72        self.hide_menu = None
73        ## Unique ID (from gui_manager)
74        self.uid = None
75       
76        ## Default locations
77        self._default_save_location = os.getcwd()       
78        ## Graph       
79        self.graph = Graph()
80        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
81        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
82        self.graph.render(self)
83       
84        # In resizing event
85        self.resizing = False
86        self.canvas.set_resizing(self.resizing)
87        self.Bind(wx.EVT_SIZE, self._OnReSize)
88       
89    def get_symbol_label(self):
90        """
91        Associates label to symbol
92        """
93        _labels = {}
94        i = 0
95        _labels['Circle'] = i
96        i += 1
97        _labels['Cross X '] = i
98        i += 1
99        _labels['Triangle Down'] = i
100        i += 1
101        _labels['Triangle Up'] = i
102        i += 1
103        _labels['Triangle Left'] = i
104        i += 1
105        _labels['Triangle Right'] = i
106        i += 1
107        _labels['Cross +'] = i
108        i += 1
109        _labels['Square'] = i
110        i += 1
111        _labels['Diamond'] = i
112        i += 1
113        _labels['Diamond'] = i
114        i += 1
115        _labels['Hexagon1'] = i
116        i += 1
117        _labels['Hexagon2'] = i
118        i += 1
119        _labels['Pentagon'] = i
120        i += 1
121        _labels['Line'] = i
122        return _labels
123
124   
125    def set_data(self, list=None):
126        """
127        """
128        pass
129   
130    def _reset(self):
131        """
132        Resets internal data and graph
133        """   
134        self.graph.reset()
135        self.plots      = {}
136       
137    def _OnReSize(self, event):   
138        """
139        On response of the resize of a panel, set axes_visiable False
140        """
141        # ready for another event
142        event.Skip() 
143        # set the resizing flag
144        self.resizing = True
145        self.canvas.set_resizing(self.resizing)
146        self.parent.set_schedule(True)
147       
148       
149    def set_resizing(self, resizing=False):
150        """
151        Set the resizing (True/False)
152        """
153        self.resizing = resizing
154        #self.canvas.set_resizing(resizing)
155   
156    def schedule_full_draw(self, func='append'):   
157        """
158        Put self in schedule to full redraw list
159        """
160        # append/del this panel in the schedule list
161        self.parent.set_schedule_full_draw(self, func)
162       
163
164    def remove_data_by_id(self, id):
165        """'
166        remove data from plot
167        """
168        if id in self.plots.keys():
169            data =  self.plots[id]
170            self.graph.delete(data)
171            data_manager = self._manager.parent.get_data_manager()
172            data_list, theory_list = data_manager.get_by_id(id_list=[id])
173           
174            if id in data_list.keys():
175                data = data_list[id]
176            else:
177                data = theory_list[id]
178           
179            del self.plots[id]
180            self.graph.render(self)
181            self.subplot.figure.canvas.draw_idle()   
182            event = RemoveDataEvent(data=data)
183            wx.PostEvent(self.parent, event)
184            if len(self.graph.plottables) == 0:
185                #onRemove: graph is empty must be the panel must be destroyed
186                self.parent.delete_panel(self.uid)
187       
188    def plot_data(self, data):
189        """
190        Data is ready to be displayed
191       
192        :param event: data event
193        """
194        if data.id in self.plots.keys():
195            #replace
196            self.graph.replace(data)
197            self.plots[data.id] = data
198        else:
199            self.plots[data.id] = data
200            self.graph.add(self.plots[data.id]) 
201       
202        x_label, x_unit = data.get_xaxis()
203        y_label, y_unit = data.get_yaxis()
204        self.graph.xaxis(x_unit, x_label)
205        self.graph.yaxis(y_unit, y_label)
206        ## Set the view scale for all plots
207        self._onEVT_FUNC_PROPERTY()
208        ## render the graph<=No need this done in canvas
209        #self.graph.render(self)
210        #self.subplot.figure.canvas.draw_idle()
211   
212    def draw_plot(self):
213        """
214        Draw plot
215        """
216        self.draw() 
217
218
219       
220    def onLeftDown(self,event): 
221        """
222        left button down and ready to drag
223        Display the position of the mouse on the statusbar
224        """
225        PlotPanel.onLeftDown(self, event)
226        ax = event.inaxes
227        if ax != None:
228            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
229            wx.PostEvent(self.parent, StatusEvent(status=position))
230        # unfocus all
231        self.parent.set_plot_unfocus() 
232        #post nd event to notify guiframe that this panel is on focus
233        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
234
235       
236    def _ontoggle_hide_error(self, event):
237        """
238        Toggle error display to hide or show
239        """
240       
241        selected_plot = self.plots[self.graph.selected_plottable]
242        if self.hide_menu.GetText() == "Hide Error":
243            selected_plot.hide_error = True
244        else:
245            selected_plot.hide_error = False
246        ## increment graph color
247        self.graph.render(self)
248        self.subplot.figure.canvas.draw_idle() 
249         
250    def _onRemove(self, event):
251        """
252        Remove a plottable from the graph and render the graph
253       
254        :param event: Menu event
255       
256        """
257        ## Check if there is a selected graph to remove
258        if self.graph.selected_plottable in self.plots.keys():
259            selected_plot = self.plots[self.graph.selected_plottable]
260            id = self.graph.selected_plottable
261            self.remove_data_by_id(id)
262           
263    def onContextMenu(self, event):
264        """
265        1D plot context menu
266       
267        :param event: wx context event
268       
269        """
270        self._slicerpop = PanelMenu()
271        self._slicerpop.set_plots(self.plots)
272        self._slicerpop.set_graph(self.graph)     
273        # Various plot options
274        id = wx.NewId()
275        self._slicerpop.Append(id, '&Save Image', 'Save image as PNG')
276        wx.EVT_MENU(self, id, self.onSaveImage)
277        id = wx.NewId()
278        self._slicerpop.Append(id, '&Print Image', 'Print image ')
279        wx.EVT_MENU(self, id, self.onPrint)
280        id = wx.NewId()
281        self._slicerpop.Append(id, '&Print Preview', 'Print preview')
282        wx.EVT_MENU(self, id, self.onPrinterPreview)
283       
284        id = wx.NewId()
285        self._slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
286        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
287       
288        self._slicerpop.AppendSeparator()
289
290        #add menu of other plugins
291        item_list = self.parent.get_context_menu(self)
292
293        if (not item_list == None) and (not len(item_list) == 0):
294            for item in item_list:
295                try:
296                    id = wx.NewId()
297                    self._slicerpop.Append(id, item[0], item[1])
298                    wx.EVT_MENU(self, id, item[2])
299                except:
300                    msg = "ModelPanel1D.onContextMenu: "
301                    msg += "bad menu item  %s" % sys.exc_value
302                    wx.PostEvent(self.parent, StatusEvent(status=msg))
303                    pass
304            self._slicerpop.AppendSeparator()
305        #id = wx.NewId()
306        #self._slicerpop.Append(id, '&Print image', 'Print image')
307        if self.graph.selected_plottable in self.plots:
308            plot = self.plots[self.graph.selected_plottable]
309
310            id = wx.NewId()
311            self._slicerpop.Append(id, '&Linear Fit')
312            wx.EVT_MENU(self, id, self.onFitting)
313            self._slicerpop.AppendSeparator()
314            id = wx.NewId()
315            name = plot.name
316            self._slicerpop.Append(id, "&Save Points as a File")
317            wx.EVT_MENU(self, id, self._onSave)
318            id = wx.NewId()
319            self._slicerpop.Append(id, "Remove %s Curve" % name)
320            wx.EVT_MENU(self, id, self._onRemove)
321            if not plot.is_data:
322                id = wx.NewId()
323                self._slicerpop.Append(id, '&Freeze', 'Freeze')
324                wx.EVT_MENU(self, id, self.onFreeze)
325            symbol_menu = wx.Menu()
326            for label in self._symbol_labels:
327                id = wx.NewId()
328                symbol_menu.Append(id, str(label), str(label))
329                wx.EVT_MENU(self, id, self.onChangeSymbol)
330            id = wx.NewId()
331            self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
332            self._slicerpop.AppendSeparator()
333
334            id = wx.NewId()
335            self.hide_menu = self._slicerpop.Append(id, "Hide Error")
336            if plot.dy is not None or plot.dy != []:
337                if plot.hide_error :
338                    self.hide_menu.SetText('Show Error')
339                else:
340                    self.hide_menu.SetText('Hide Error')
341            else:
342                self.hide_menu.Disable()
343            wx.EVT_MENU(self, id, self._ontoggle_hide_error)
344           
345            self._slicerpop.AppendSeparator()
346            # Option to hide
347            #TODO: implement functionality to hide a plottable (legend click)
348       
349       
350        id = wx.NewId()
351        self._slicerpop.Append(id, '&Change scale')
352        wx.EVT_MENU(self, id, self._onProperties)
353        id = wx.NewId()
354        self._slicerpop.Append(id, '&Reset Graph')
355        wx.EVT_MENU(self, id, self.onResetGraph) 
356        pos = event.GetPosition()
357        pos = self.ScreenToClient(pos)
358        self.PopupMenu(self._slicerpop, pos)
359     
360    def onFreeze(self, event):
361        """
362        """
363        plot = self.plots[self.graph.selected_plottable]
364        self.parent.onfreeze([plot.id])
365       
366    def onChangeSymbol(self, event):
367        """
368        """
369        menu = event.GetEventObject()
370        id = event.GetId()
371        label =  menu.GetLabel(id)
372        selected_plot = self.plots[self.graph.selected_plottable]
373        selected_plot.symbol = self._symbol_labels[label]
374        ## Set the view scale for all plots
375        self._onEVT_FUNC_PROPERTY()
376        ## render the graph
377        #self.graph.render(self)
378        #self.subplot.figure.canvas.draw_idle()
379       
380    def _onsaveTXT(self, path):
381        """
382        Save file as txt
383           
384        :TODO: Refactor and remove this method. See TODO in _onSave.
385       
386        """
387        data = self.plots[self.graph.selected_plottable]
388       
389        if not path == None:
390            out = open(path, 'w')
391            has_errors = True
392            if data.dy == None or data.dy == []:
393                has_errors = False
394            # Sanity check
395            if has_errors:
396                try:
397                    if len(data.y) != len(data.dy):
398                        has_errors = False
399                except:
400                    has_errors = False
401            if has_errors:
402                if data.dx != None:
403                    out.write("<X>   <Y>   <dY>   <dX>\n")
404                else:
405                    out.write("<X>   <Y>   <dY>\n")
406            else:
407                out.write("<X>   <Y>\n")
408               
409            for i in range(len(data.x)):
410                if has_errors:
411                    if data.dx != None:
412                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
413                                                    data.y[i],
414                                                    data.dy[i],
415                                                    data.dx[i]))
416                    else:
417                        out.write("%g  %g  %g\n" % (data.x[i], 
418                                                    data.y[i],
419                                                    data.dy[i]))
420                else:
421                    out.write("%g  %g\n" % (data.x[i], 
422                                            data.y[i]))
423            out.close()                 
424            try:
425                self._default_save_location = os.path.dirname(path)
426            except:
427                pass   
428               
429    def _onSave(self, evt):
430        """
431        Save a data set to a text file
432       
433        :param evt: Menu event
434       
435        """
436       
437        path = None
438        wildcard = "Text files (*.txt)|*.txt|"\
439        "CanSAS 1D files(*.xml)|*.xml" 
440        dlg = wx.FileDialog(self, "Choose a file",
441                            self._default_save_location,
442                             "", wildcard , wx.SAVE)
443       
444        if dlg.ShowModal() == wx.ID_OK:
445            path = dlg.GetPath()
446            mypath = os.path.basename(path)
447           
448            #TODO: This is bad design. The DataLoader is designed
449            #to recognize extensions.
450            # It should be a simple matter of calling the .
451            #save(file, data, '.xml') method
452            # of the DataLoader.loader.Loader class.
453            from DataLoader.loader import  Loader
454            #Instantiate a loader
455            loader = Loader() 
456            data = self.plots[self.graph.selected_plottable]
457            format = ".txt"
458            if os.path.splitext(mypath)[1].lower() == format:
459                 self._onsaveTXT( path)
460            format = ".xml"
461            if os.path.splitext(mypath)[1].lower() == format:
462                loader.save(path, data, format)
463            try:
464                self._default_save_location = os.path.dirname(path)
465            except:
466                pass   
467        dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.