source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 8960479

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 8960479 was a436b2e, checked in by Jae Cho <jhjcho@…>, 13 years ago

Still working on SLDpanel

  • Property mode set to 100644
File size: 16.6 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 PanelOnFocusEvent
29from sans.guiframe.events import EVT_NEW_LOADED_DATA
30from sans.guiframe.utils import PanelMenu
31from sans.guiframe.dataFitting import Data1D
32from sans.guiframe.panel_base import PanelBase
33from binder import BindArtist
34
35DEFAULT_QMAX = 0.05
36DEFAULT_QSTEP = 0.001
37DEFAULT_BEAM = 0.005
38BIN_WIDTH = 1
39
40
41class ModelPanel1D(PlotPanel, PanelBase):
42    """
43    Plot panel for use with the GUI manager
44    """
45   
46    ## Internal name for the AUI manager
47    window_name = "plotpanel"
48    ## Title to appear on top of the window
49    window_caption = "Plot Panel"
50    ## Flag to tell the GUI manager that this panel is not
51    #  tied to any perspective
52    ALWAYS_ON = True
53    ## Group ID
54    group_id = None
55   
56    def __init__(self, parent, id=-1, color = None,
57                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
58        PlotPanel.__init__(self, parent, id=id, style=style, **kwargs)
59        PanelBase.__init__(self, parent)
60        ## Reference to the parent window
61        self.parent = parent
62        ## Plottables
63        self.plots = {}
64        #context menu
65        self._slicerpop = None
66       
67        self._available_data = []
68        self._menu_add_ids = []
69        self._symbol_labels = self.get_symbol_label()
70     
71        self.hide_menu = None
72        ## Unique ID (from gui_manager)
73        self.uid = None
74       
75        ## Default locations
76        self._default_save_location = os.getcwd() 
77        self.size = None       
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        pos_x, pos_y = self.GetPositionTuple()
148        if pos_x != 0 and pos_y != 0:
149            self.size, _ = self.GetClientSizeTuple()
150       
151    def set_resizing(self, resizing=False):
152        """
153        Set the resizing (True/False)
154        """
155        self.resizing = resizing
156        #self.canvas.set_resizing(resizing)
157   
158    def schedule_full_draw(self, func='append'):   
159        """
160        Put self in schedule to full redraw list
161        """
162        # append/del this panel in the schedule list
163        self.parent.set_schedule_full_draw(self, func)
164       
165
166    def remove_data_by_id(self, id):
167        """'
168        remove data from plot
169        """
170        if id in self.plots.keys():
171            data =  self.plots[id]
172            self.graph.delete(data)
173            data_manager = self._manager.parent.get_data_manager()
174            data_list, theory_list = data_manager.get_by_id(id_list=[id])
175           
176            if id in data_list.keys():
177                data = data_list[id]
178            if id in theory_list.keys():
179                data = theory_list[id]
180           
181            del self.plots[id]
182            self.graph.render(self)
183            self.subplot.figure.canvas.draw_idle()   
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            name = plot.name
312            self._slicerpop.Append(id, "&Save Points as a File")
313            self._slicerpop.AppendSeparator()
314            if plot.name != 'SLD':
315                wx.EVT_MENU(self, id, self._onSave)
316                id = wx.NewId()
317                self._slicerpop.Append(id, '&Linear Fit')
318                wx.EVT_MENU(self, id, self.onFitting)
319                self._slicerpop.AppendSeparator()
320   
321                id = wx.NewId()
322                self._slicerpop.Append(id, "Remove %s Curve" % name)
323                wx.EVT_MENU(self, id, self._onRemove)
324                if not plot.is_data:
325                    id = wx.NewId()
326                    self._slicerpop.Append(id, '&Freeze', 'Freeze')
327                    wx.EVT_MENU(self, id, self.onFreeze)
328                symbol_menu = wx.Menu()
329                for label in self._symbol_labels:
330                    id = wx.NewId()
331                    symbol_menu.Append(id, str(label), str(label))
332                    wx.EVT_MENU(self, id, self.onChangeSymbol)
333                id = wx.NewId()
334                self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
335                self._slicerpop.AppendSeparator()
336   
337                id = wx.NewId()
338                self.hide_menu = self._slicerpop.Append(id, "Hide Error")
339   
340                if plot.dy is not None and plot.dy != []:
341                    if plot.hide_error :
342                        self.hide_menu.SetText('Show Error')
343                    else:
344                        self.hide_menu.SetText('Hide Error')
345                else:
346                    self.hide_menu.Enable(False)
347                wx.EVT_MENU(self, id, self._ontoggle_hide_error)
348               
349                self._slicerpop.AppendSeparator()
350                # Option to hide
351                #TODO: implement functionality to hide a plottable (legend click)
352       
353       
354        id = wx.NewId()
355        self._slicerpop.Append(id, '&Change scale')
356        wx.EVT_MENU(self, id, self._onProperties)
357        id = wx.NewId()
358        self._slicerpop.Append(id, '&Reset Graph')
359        wx.EVT_MENU(self, id, self.onResetGraph) 
360        pos = event.GetPosition()
361        pos = self.ScreenToClient(pos)
362        self.PopupMenu(self._slicerpop, pos)
363     
364    def onFreeze(self, event):
365        """
366        """
367        plot = self.plots[self.graph.selected_plottable]
368        self.parent.onfreeze([plot.id])
369       
370    def onChangeSymbol(self, event):
371        """
372        """
373        menu = event.GetEventObject()
374        id = event.GetId()
375        label =  menu.GetLabel(id)
376        selected_plot = self.plots[self.graph.selected_plottable]
377        selected_plot.symbol = self._symbol_labels[label]
378        ## Set the view scale for all plots
379        self._onEVT_FUNC_PROPERTY()
380        ## render the graph
381        #self.graph.render(self)
382        #self.subplot.figure.canvas.draw_idle()
383       
384    def _onsaveTXT(self, path):
385        """
386        Save file as txt
387           
388        :TODO: Refactor and remove this method. See TODO in _onSave.
389       
390        """
391        data = self.plots[self.graph.selected_plottable]
392       
393        if not path == None:
394            out = open(path, 'w')
395            has_errors = True
396            if data.dy == None or data.dy == []:
397                has_errors = False
398            # Sanity check
399            if has_errors:
400                try:
401                    if len(data.y) != len(data.dy):
402                        has_errors = False
403                except:
404                    has_errors = False
405            if has_errors:
406                if data.dx != None:
407                    out.write("<X>   <Y>   <dY>   <dX>\n")
408                else:
409                    out.write("<X>   <Y>   <dY>\n")
410            else:
411                out.write("<X>   <Y>\n")
412               
413            for i in range(len(data.x)):
414                if has_errors:
415                    if data.dx != None:
416                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
417                                                    data.y[i],
418                                                    data.dy[i],
419                                                    data.dx[i]))
420                    else:
421                        out.write("%g  %g  %g\n" % (data.x[i], 
422                                                    data.y[i],
423                                                    data.dy[i]))
424                else:
425                    out.write("%g  %g\n" % (data.x[i], 
426                                            data.y[i]))
427            out.close()                 
428            try:
429                self._default_save_location = os.path.dirname(path)
430            except:
431                pass   
432               
433    def _onSave(self, evt):
434        """
435        Save a data set to a text file
436       
437        :param evt: Menu event
438       
439        """
440       
441        path = None
442        wildcard = "Text files (*.txt)|*.txt|"\
443        "CanSAS 1D files(*.xml)|*.xml" 
444        dlg = wx.FileDialog(self, "Choose a file",
445                            self._default_save_location,
446                             "", wildcard , wx.SAVE)
447       
448        if dlg.ShowModal() == wx.ID_OK:
449            path = dlg.GetPath()
450            # ext_num = 0 for .txt, ext_num = 1 for .xml
451            # This is MAC Fix
452            ext_num = dlg.GetFilterIndex()
453            if ext_num == 0:
454                format = '.txt'
455            else:
456                format = '.xml'
457            path = os.path.splitext(path)[0] + format
458            mypath = os.path.basename(path)
459           
460            #TODO: This is bad design. The DataLoader is designed
461            #to recognize extensions.
462            # It should be a simple matter of calling the .
463            #save(file, data, '.xml') method
464            # of the DataLoader.loader.Loader class.
465            from DataLoader.loader import  Loader
466            #Instantiate a loader
467            loader = Loader() 
468            data = self.plots[self.graph.selected_plottable]
469            format = ".txt"
470            if os.path.splitext(mypath)[1].lower() == format:
471                # Make sure the ext included in the file name
472                # especially on MAC
473                fName = os.path.splitext(path)[0] + format
474                self._onsaveTXT(fName)
475            format = ".xml"
476            if os.path.splitext(mypath)[1].lower() == format:
477                # Make sure the ext included in the file name
478                # especially on MAC
479                fName = os.path.splitext(path)[0] + format
480                loader.save(fName, data, format)
481            try:
482                self._default_save_location = os.path.dirname(path)
483            except:
484                pass   
485        dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.