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

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 ec1584e was a3c96f7a, checked in by Gervaise Alina <gervyh@…>, 14 years ago

updating plot display

  • Property mode set to 100644
File size: 14.0 KB
RevLine 
[1bf33c1]1
[d955bf19]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################################################################################
[1bf33c1]11
12
13import wx
[4ac8556]14import sys
15import os
16import pylab
17import math
18import numpy
19import time
[a07e72f]20
[1bf33c1]21from danse.common.plottools.PlotPanel import PlotPanel
[e5664f2]22from danse.common.plottools.plottables import Graph
[3b69ca6]23from sans.guiframe import dataFitting
[df7046f]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
[a45037aa]29from sans.guiframe.events import PanelOnFocusEvent
[52b8b74]30from sans.guiframe.events import EVT_NEW_LOADED_DATA
[0d9dae8]31from sans.guiframe.utils import PanelMenu
[4ac8556]32from sans.guiframe.dataFitting import Data1D
[691643c]33from sans.guiframe.panel_base import PanelBase
[1bf33c1]34from binder import BindArtist
35
[0d9dae8]36DEFAULT_QMAX = 0.05
[1bf33c1]37DEFAULT_QSTEP = 0.001
38DEFAULT_BEAM = 0.005
[32c0841]39BIN_WIDTH = 1
[1bf33c1]40
[0d9dae8]41
[691643c]42class ModelPanel1D(PlotPanel, PanelBase):
[1bf33c1]43    """
[d955bf19]44    Plot panel for use with the GUI manager
[1bf33c1]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   
[32c0841]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)
[a45037aa]60        PanelBase.__init__(self, parent)
[1bf33c1]61        ## Reference to the parent window
62        self.parent = parent
63        ## Plottables
64        self.plots = {}
[52b8b74]65        #context menu
66        self._slicerpop = None
[a07e72f]67       
[52b8b74]68        self._available_data = []
69        self._menu_add_ids = []
[a07e72f]70        self._symbol_labels = self.get_symbol_label()
71     
72        self.hide_menu = None
[1bf33c1]73        ## Unique ID (from gui_manager)
74        self.uid = None
[a07e72f]75       
[6063b16]76        ## Default locations
77        self._default_save_location = os.getcwd()       
[1bf33c1]78        ## Graph       
79        self.graph = Graph()
80        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
[32c0841]81        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
[1bf33c1]82        self.graph.render(self)
[a07e72f]83       
84    def get_symbol_label(self):
[52b8b74]85        """
[a07e72f]86        Associates label to symbol
[52b8b74]87        """
[a07e72f]88        _labels = {}
89        i = 0
90        _labels['Points'] = i
91        i += 1
92        _labels['X '] = i
93        i += 1
94        _labels['Triangle Down'] = i
95        i += 1
96        _labels['Triangle Up'] = i
97        i += 1
98        _labels['Triangle Left'] = i
99        i += 1
100        _labels['Triangle Right'] = i
101        i += 1
102        _labels['Plus'] = i
103        i += 1
104        _labels['Square'] = i
105        i += 1
106        _labels['Thin Diamond'] = i
107        i += 1
108        _labels['Diamond'] = i
109        i += 1
110        _labels['Hexagon1'] = i
111        i += 1
112        _labels['Hexagon2'] = i
113        i += 1
114        _labels['Pentagon'] = i
115        i += 1
116        _labels['Curve'] = i
117        return _labels
118
[52b8b74]119   
[32c0841]120    def set_data(self, list=None):
[3c44c66]121        """
122        """
123        pass
124   
[1bf33c1]125    def _reset(self):
126        """
[d955bf19]127        Resets internal data and graph
[1bf33c1]128        """   
129        self.graph.reset()
130        self.plots      = {}
131   
[a07e72f]132    def remove_data_by_id(self, id):
133        """'
134        remove data from plot
135        """
136        if id in self.plots.keys():
137            data =  self.plots[id]
138            self.graph.delete(data)
139            data_manager = self._manager.parent.get_data_manager()
[df22224]140            data_list, theory_list = data_manager.get_by_id(id_list=[id])
141           
142            if id in data_list.keys():
143                data = data_list[id]
144            else:
145                data = theory_list[id]
146           
[a07e72f]147            del self.plots[id]
148            self.graph.render(self)
149            self.subplot.figure.canvas.draw_idle()   
[df22224]150            event = RemoveDataEvent(data=data)
151            wx.PostEvent(self.parent, event)
[a07e72f]152            if len(self.graph.plottables) == 0:
[df22224]153                #onRemove: graph is empty must be the panel must be destroyed
154                self.parent.delete_panel(self.uid)
[a07e72f]155       
156    def plot_data(self, data):
[1bf33c1]157        """
[d955bf19]158        Data is ready to be displayed
159       
160        :param event: data event
[1bf33c1]161        """
[a07e72f]162        if data.id in self.plots.keys():
163            #replace
164            self.graph.replace(data)
165            self.plots[data.id] = data
166        else:
167            self.plots[data.id] = data
168            self.graph.add(self.plots[data.id]) 
[ffd23b5]169       
[a07e72f]170        x_label, x_unit = data.get_xaxis()
171        y_label, y_unit = data.get_yaxis()
172        self.graph.xaxis(x_unit, x_label)
173        self.graph.yaxis(y_unit, y_label)
[6c0568b]174        ## Set the view scale for all plots
[1bf33c1]175        self._onEVT_FUNC_PROPERTY()
[6c0568b]176        ## render the graph
[1bf33c1]177        self.graph.render(self)
178        self.subplot.figure.canvas.draw_idle()
[a07e72f]179
180       
[1bf33c1]181    def onLeftDown(self,event): 
[6c0568b]182        """
[d955bf19]183        left button down and ready to drag
184        Display the position of the mouse on the statusbar
[6c0568b]185        """
[1bf33c1]186        PlotPanel.onLeftDown(self, event)
187        ax = event.inaxes
188        if ax != None:
189            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
190            wx.PostEvent(self.parent, StatusEvent(status=position))
[4ed210f4]191           
192        #post nd event to notify guiframe that this panel is on focus
[a45037aa]193        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
[a07e72f]194        self._manager.set_panel_on_focus(self)
[4ed210f4]195       
[a07e72f]196    def _ontoggle_hide_error(self, event):
197        """
198        Toggle error display to hide or show
199        """
200       
201        selected_plot = self.plots[self.graph.selected_plottable]
202        if self.hide_menu.GetText() == "Hide Error":
203            selected_plot.hide_error = True
204        else:
205            selected_plot.hide_error = False
206        ## increment graph color
207        self.graph.render(self)
208        self.subplot.figure.canvas.draw_idle() 
209         
[1bf33c1]210    def _onRemove(self, event):
211        """
[d955bf19]212        Remove a plottable from the graph and render the graph
213       
214        :param event: Menu event
215       
[1bf33c1]216        """
[6c0568b]217        ## Check if there is a selected graph to remove
[a07e72f]218        if self.graph.selected_plottable in self.plots.keys():
219            selected_plot = self.plots[self.graph.selected_plottable]
220            id = self.graph.selected_plottable
[df22224]221            self.remove_data_by_id(id)
222           
[1bf33c1]223    def onContextMenu(self, event):
224        """
[d955bf19]225        1D plot context menu
226       
227        :param event: wx context event
228       
[1bf33c1]229        """
[52b8b74]230        self._slicerpop = PanelMenu()
231        self._slicerpop.set_plots(self.plots)
232        self._slicerpop.set_graph(self.graph)     
[9a585d0]233        # Various plot options
234        id = wx.NewId()
[52b8b74]235        self._slicerpop.Append(id, '&Save image', 'Save image as PNG')
[9a585d0]236        wx.EVT_MENU(self, id, self.onSaveImage)
237        id = wx.NewId()
[52b8b74]238        self._slicerpop.Append(id, '&Print image', 'Print image ')
[18eba35]239        wx.EVT_MENU(self, id, self.onPrint)
240        id = wx.NewId()
[52b8b74]241        self._slicerpop.Append(id, '&Print Preview', 'image preview for print')
[18eba35]242        wx.EVT_MENU(self, id, self.onPrinterPreview)
[52b8b74]243       
[a3c96f7a]244       
[52b8b74]245        #add menu of other plugins
[a07e72f]246        item_list = self.parent.get_context_menu(self)
[32c0841]247        if (not item_list == None) and (not len(item_list) == 0):
[9a585d0]248            for item in item_list:
249                try:
250                    id = wx.NewId()
[52b8b74]251                    self._slicerpop.Append(id, item[0], item[1])
[9a585d0]252                    wx.EVT_MENU(self, id, item[2])
253                except:
[32c0841]254                    msg = "ModelPanel1D.onContextMenu: "
[a07e72f]255                    msg += "bad menu item  %s" % sys.exc_value
[32c0841]256                    wx.PostEvent(self.parent, StatusEvent(status=msg))
[9a585d0]257                    pass
[52b8b74]258            self._slicerpop.AppendSeparator()
259        id = wx.NewId()
[a07e72f]260       
[52b8b74]261        self._slicerpop.Append(id, '&Print image', 'Print image')
[1bf33c1]262        if self.graph.selected_plottable in self.plots:
263            plot = self.plots[self.graph.selected_plottable]
[e6a93df]264            if not plot.is_data:
265                id = wx.NewId()
266                self._slicerpop.Append(id, '&Freeze', 'Freeze')
267                wx.EVT_MENU(self, id, self.onFreeze)
[a3c96f7a]268            symbol_menu = wx.Menu()
269            for label in self._symbol_labels:
270                id = wx.NewId()
271                symbol_menu.Append(id, str(label), str(label))
272                wx.EVT_MENU(self, id, self.onChangeSymbol)
273            id = wx.NewId()
274            self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
275            self._slicerpop.AppendSeparator()
[df22224]276            #else:
277            id = wx.NewId()
278            self._slicerpop.Append(id, '&Linear Fit')
279            wx.EVT_MENU(self, id, self.onFitting)
[1bf33c1]280            id = wx.NewId()
281            name = plot.name
[52b8b74]282            self._slicerpop.Append(id, "&Save points")
[1bf33c1]283            wx.EVT_MENU(self, id, self._onSave)
284            id = wx.NewId()
[52b8b74]285            self._slicerpop.Append(id, "Remove %s curve" % name)
[1bf33c1]286            wx.EVT_MENU(self, id, self._onRemove)
[a07e72f]287            id = wx.NewId()
288            self.hide_menu = self._slicerpop.Append(id, "Hide Error")
289            if plot.dy is not None or plot.dy != []:
290                if plot.hide_error :
291                    self.hide_menu.SetText('Show Error')
292                else:
293                    self.hide_menu.SetText('Hide Error')
294            else:
295                self.hide_menu.Disable()
296            wx.EVT_MENU(self, id, self._ontoggle_hide_error)
297           
[52b8b74]298            self._slicerpop.AppendSeparator()
[1bf33c1]299            # Option to hide
300            #TODO: implement functionality to hide a plottable (legend click)
[df22224]301       
[a07e72f]302       
[1bf33c1]303        id = wx.NewId()
[52b8b74]304        self._slicerpop.Append(id, '&Change scale')
[1bf33c1]305        wx.EVT_MENU(self, id, self._onProperties)
306        id = wx.NewId()
[52b8b74]307        self._slicerpop.Append(id, '&Reset Graph')
[d468daa]308        wx.EVT_MENU(self, id, self.onResetGraph) 
[1bf33c1]309        pos = event.GetPosition()
310        pos = self.ScreenToClient(pos)
[52b8b74]311        self.PopupMenu(self._slicerpop, pos)
[a07e72f]312     
[e6a93df]313    def onFreeze(self, event):
314        """
315        """
316        plot = self.plots[self.graph.selected_plottable]
317        self.parent.onfreeze([plot.id])
318       
[a07e72f]319    def onChangeSymbol(self, event):
[6c0568b]320        """
321        """
[a07e72f]322        menu = event.GetEventObject()
323        id = event.GetId()
324        label =  menu.GetLabel(id)
325        selected_plot = self.plots[self.graph.selected_plottable]
326        selected_plot.symbol = self._symbol_labels[label]
327        ## Set the view scale for all plots
328        self._onEVT_FUNC_PROPERTY()
329        ## render the graph
330        self.graph.render(self)
331        self.subplot.figure.canvas.draw_idle()
[c81140c]332       
[42d27f2]333    def _onsaveTXT(self, path):
334        """
[d955bf19]335        Save file as txt
[1abcb04]336           
[d955bf19]337        :TODO: Refactor and remove this method. See TODO in _onSave.
338       
[42d27f2]339        """
340        data = self.plots[self.graph.selected_plottable]
341       
342        if not path == None:
343            out = open(path, 'w')
344            has_errors = True
[32c0841]345            if data.dy == None or data.dy == []:
[42d27f2]346                has_errors = False
347            # Sanity check
348            if has_errors:
349                try:
350                    if len(data.y) != len(data.dy):
351                        has_errors = False
352                except:
353                    has_errors = False
354            if has_errors:
355                out.write("<X>   <Y>   <dY>\n")
356            else:
357                out.write("<X>   <Y>\n")
358               
359            for i in range(len(data.x)):
360                if has_errors:
361                    out.write("%g  %g  %g\n" % (data.x[i], 
362                                                data.y[i],
363                                               data.dy[i]))
364                else:
365                    out.write("%g  %g\n" % (data.x[i], 
366                                            data.y[i]))
367            out.close()                 
[6063b16]368            try:
369                self._default_save_location = os.path.dirname(path)
370            except:
371                pass   
[8bd764d]372               
[1bf33c1]373    def _onSave(self, evt):
374        """
[d955bf19]375        Save a data set to a text file
376       
377        :param evt: Menu event
378       
[1bf33c1]379        """
[a07e72f]380       
381        path = None
382        wildcard = "Text files (*.txt)|*.txt|"\
383        "CanSAS 1D files(*.xml)|*.xml" 
384        dlg = wx.FileDialog(self, "Choose a file",
385                            self._default_save_location,
386                             "", wildcard , wx.SAVE)
387       
388        if dlg.ShowModal() == wx.ID_OK:
389            path = dlg.GetPath()
390            mypath = os.path.basename(path)
[1bf33c1]391           
[a07e72f]392            #TODO: This is bad design. The DataLoader is designed
393            #to recognize extensions.
394            # It should be a simple matter of calling the .
395            #save(file, data, '.xml') method
396            # of the DataLoader.loader.Loader class.
397            from DataLoader.loader import  Loader
398            #Instantiate a loader
399            loader = Loader() 
400            data = self.plots[self.graph.selected_plottable]
401            format = ".txt"
402            if os.path.splitext(mypath)[1].lower() == format:
403                 self._onsaveTXT( path)
404            format = ".xml"
405            if os.path.splitext(mypath)[1].lower() == format:
406                loader.save(path, data, format)
407            try:
408                self._default_save_location = os.path.dirname(path)
409            except:
410                pass   
411        dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.