source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 71fa9bb9

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

inspecting resizing

  • Property mode set to 100644
File size: 18.1 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        self.x_size = None
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        self._add_more_tool()
89       
90    def get_symbol_label(self):
91        """
92        Associates label to symbol
93        """
94        _labels = {}
95        i = 0
96        _labels['Circle'] = i
97        i += 1
98        _labels['Cross X '] = i
99        i += 1
100        _labels['Triangle Down'] = i
101        i += 1
102        _labels['Triangle Up'] = i
103        i += 1
104        _labels['Triangle Left'] = i
105        i += 1
106        _labels['Triangle Right'] = i
107        i += 1
108        _labels['Cross +'] = i
109        i += 1
110        _labels['Square'] = i
111        i += 1
112        _labels['Diamond'] = i
113        i += 1
114        _labels['Diamond'] = i
115        i += 1
116        _labels['Hexagon1'] = i
117        i += 1
118        _labels['Hexagon2'] = i
119        i += 1
120        _labels['Pentagon'] = i
121        i += 1
122        _labels['Line'] = i
123        return _labels
124
125   
126    def set_data(self, list=None):
127        """
128        """
129        pass
130   
131    def _reset(self):
132        """
133        Resets internal data and graph
134        """   
135        self.graph.reset()
136        self.plots      = {}
137       
138    def _OnReSize(self, event):   
139        """
140        On response of the resize of a panel, set axes_visiable False
141        """
142        # Ready for another event
143        # Do not remove this Skip. Otherwise it will get runtime error.
144        event.Skip() 
145        print "self.x_size,self.GetSize()",self.x_size, self.GetSize()
146        # It was found that wx >= 2.9.3 sends an event even if no size changed.
147        # So manually recode the size (=x_size) and compare here.
148        if self.x_size != None:
149            if self.x_size == self.GetSize():
150                self.resizing = False
151                self.canvas.set_resizing(self.resizing)
152                print "not resizing"
153                return
154        self.x_size = self.GetSize()
155       
156        # set the resizing flag
157        self.resizing = True
158        self.canvas.set_resizing(self.resizing)
159        self.parent.set_schedule(True)
160        pos_x, pos_y = self.GetPositionTuple()
161        if pos_x != 0 and pos_y != 0:
162            self.size, _ = self.GetClientSizeTuple()
163       
164    def set_resizing(self, resizing=False):
165        """
166        Set the resizing (True/False)
167        """
168        self.resizing = resizing
169        #self.canvas.set_resizing(resizing)
170   
171    def schedule_full_draw(self, func='append'):   
172        """
173        Put self in schedule to full redraw list
174        """
175        # append/del this panel in the schedule list
176        self.parent.set_schedule_full_draw(self, func)
177       
178
179    def remove_data_by_id(self, id):
180        """'
181        remove data from plot
182        """
183        if id in self.plots.keys():
184            data =  self.plots[id]
185            self.graph.delete(data)
186            data_manager = self._manager.parent.get_data_manager()
187            data_list, theory_list = data_manager.get_by_id(id_list=[id])
188           
189            if id in data_list.keys():
190                data = data_list[id]
191            if id in theory_list.keys():
192                data = theory_list[id]
193           
194            del self.plots[id]
195            self.graph.render(self)
196            self.subplot.figure.canvas.draw_idle()   
197            if len(self.graph.plottables) == 0:
198                #onRemove: graph is empty must be the panel must be destroyed
199                self.parent.delete_panel(self.uid)
200       
201    def plot_data(self, data):
202        """
203        Data is ready to be displayed
204       
205        :param event: data event
206        """
207        if data.id in self.plots.keys():
208            #replace
209            self.graph.replace(data)
210            self.plots[data.id] = data
211        else:
212            self.plots[data.id] = data
213            self.graph.add(self.plots[data.id]) 
214       
215        x_label, x_unit = data.get_xaxis()
216        y_label, y_unit = data.get_yaxis()
217        self.graph.xaxis(x_unit, x_label)
218        self.graph.yaxis(y_unit, y_label)
219        ## Set the view scale for all plots
220        self._onEVT_FUNC_PROPERTY()
221        ## render the graph<=No need this done in canvas
222        #self.graph.render(self)
223        #self.subplot.figure.canvas.draw_idle()
224   
225    def draw_plot(self):
226        """
227        Draw plot
228        """
229        self.draw() 
230
231
232       
233    def onLeftDown(self,event): 
234        """
235        left button down and ready to drag
236        Display the position of the mouse on the statusbar
237        """
238        PlotPanel.onLeftDown(self, event)
239        ax = event.inaxes
240        if ax != None:
241            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
242            wx.PostEvent(self.parent, StatusEvent(status=position))
243        # unfocus all
244        self.parent.set_plot_unfocus() 
245        #post nd event to notify guiframe that this panel is on focus
246        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
247
248       
249    def _ontoggle_hide_error(self, event):
250        """
251        Toggle error display to hide or show
252        """
253       
254        selected_plot = self.plots[self.graph.selected_plottable]
255        if self.hide_menu.GetText() == "Hide Error":
256            selected_plot.hide_error = True
257        else:
258            selected_plot.hide_error = False
259        ## increment graph color
260        self.graph.render(self)
261        self.subplot.figure.canvas.draw_idle() 
262         
263    def _onRemove(self, event):
264        """
265        Remove a plottable from the graph and render the graph
266       
267        :param event: Menu event
268       
269        """
270        ## Check if there is a selected graph to remove
271        if self.graph.selected_plottable in self.plots.keys():
272            selected_plot = self.plots[self.graph.selected_plottable]
273            id = self.graph.selected_plottable
274            self.remove_data_by_id(id)
275           
276    def onContextMenu(self, event):
277        """
278        1D plot context menu
279       
280        :param event: wx context event
281       
282        """
283        self._slicerpop = PanelMenu()
284        self._slicerpop.set_plots(self.plots)
285        self._slicerpop.set_graph(self.graph)     
286        # Various plot options
287        id = wx.NewId()
288        self._slicerpop.Append(id, '&Save Image', 'Save image as PNG')
289        wx.EVT_MENU(self, id, self.onSaveImage)
290        id = wx.NewId()
291        self._slicerpop.Append(id, '&Print Image', 'Print image ')
292        wx.EVT_MENU(self, id, self.onPrint)
293        id = wx.NewId()
294        self._slicerpop.Append(id, '&Print Preview', 'Print preview')
295        wx.EVT_MENU(self, id, self.onPrinterPreview)
296       
297        id = wx.NewId()
298        self._slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
299        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
300       
301        self._slicerpop.AppendSeparator()
302
303        #add menu of other plugins
304        item_list = self.parent.get_context_menu(self)
305
306        if (not item_list == None) and (not len(item_list) == 0):
307            for item in item_list:
308                try:
309                    id = wx.NewId()
310                    self._slicerpop.Append(id, item[0], item[1])
311                    wx.EVT_MENU(self, id, item[2])
312                except:
313                    msg = "ModelPanel1D.onContextMenu: "
314                    msg += "bad menu item  %s" % sys.exc_value
315                    wx.PostEvent(self.parent, StatusEvent(status=msg))
316                    pass
317            self._slicerpop.AppendSeparator()
318        #id = wx.NewId()
319        #self._slicerpop.Append(id, '&Print image', 'Print image')
320        if self.graph.selected_plottable in self.plots:
321            plot = self.plots[self.graph.selected_plottable]
322           
323            id = wx.NewId()
324            name = plot.name
325            self._slicerpop.Append(id, "&Save Points as a File")
326            self._slicerpop.AppendSeparator()
327            if plot.name != 'SLD':
328                wx.EVT_MENU(self, id, self._onSave)
329                id = wx.NewId()
330                self._slicerpop.Append(id, '&Linear Fit')
331                wx.EVT_MENU(self, id, self.onFitting)
332                self._slicerpop.AppendSeparator()
333   
334                id = wx.NewId()
335                self._slicerpop.Append(id, "Remove %s Curve" % name)
336                wx.EVT_MENU(self, id, self._onRemove)
337                if not plot.is_data:
338                    id = wx.NewId()
339                    self._slicerpop.Append(id, '&Freeze', 'Freeze')
340                    wx.EVT_MENU(self, id, self.onFreeze)
341                symbol_menu = wx.Menu()
342                for label in self._symbol_labels:
343                    id = wx.NewId()
344                    symbol_menu.Append(id, str(label), str(label))
345                    wx.EVT_MENU(self, id, self.onChangeSymbol)
346                id = wx.NewId()
347                self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
348                self._slicerpop.AppendSeparator()
349   
350                id = wx.NewId()
351                self.hide_menu = self._slicerpop.Append(id, "Hide Error")
352   
353                if plot.dy is not None and plot.dy != []:
354                    if plot.hide_error :
355                        self.hide_menu.SetText('Show Error')
356                    else:
357                        self.hide_menu.SetText('Hide Error')
358                else:
359                    self.hide_menu.Enable(False)
360                wx.EVT_MENU(self, id, self._ontoggle_hide_error)
361               
362                self._slicerpop.AppendSeparator()
363                # Option to hide
364                #TODO: implement functionality to hide a plottable (legend click)
365       
366       
367        id = wx.NewId()
368        self._slicerpop.Append(id, '&Change scale')
369        wx.EVT_MENU(self, id, self._onProperties)
370        id = wx.NewId()
371        self._slicerpop.Append(id, '&Reset Graph')
372        wx.EVT_MENU(self, id, self.onResetGraph) 
373        pos = event.GetPosition()
374        pos = self.ScreenToClient(pos)
375        self.PopupMenu(self._slicerpop, pos)
376     
377    def onFreeze(self, event):
378        """
379        """
380        plot = self.plots[self.graph.selected_plottable]
381        self.parent.onfreeze([plot.id])
382       
383    def onChangeSymbol(self, event):
384        """
385        """
386        menu = event.GetEventObject()
387        id = event.GetId()
388        label =  menu.GetLabel(id)
389        selected_plot = self.plots[self.graph.selected_plottable]
390        selected_plot.symbol = self._symbol_labels[label]
391        ## Set the view scale for all plots
392        self._onEVT_FUNC_PROPERTY()
393        ## render the graph
394        #self.graph.render(self)
395        #self.subplot.figure.canvas.draw_idle()
396       
397    def _onsaveTXT(self, path):
398        """
399        Save file as txt
400           
401        :TODO: Refactor and remove this method. See TODO in _onSave.
402       
403        """
404        data = self.plots[self.graph.selected_plottable]
405       
406        if not path == None:
407            out = open(path, 'w')
408            has_errors = True
409            if data.dy == None or data.dy == []:
410                has_errors = False
411            # Sanity check
412            if has_errors:
413                try:
414                    if len(data.y) != len(data.dy):
415                        has_errors = False
416                except:
417                    has_errors = False
418            if has_errors:
419                if data.dx != None:
420                    out.write("<X>   <Y>   <dY>   <dX>\n")
421                else:
422                    out.write("<X>   <Y>   <dY>\n")
423            else:
424                out.write("<X>   <Y>\n")
425               
426            for i in range(len(data.x)):
427                if has_errors:
428                    if data.dx != None:
429                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
430                                                    data.y[i],
431                                                    data.dy[i],
432                                                    data.dx[i]))
433                    else:
434                        out.write("%g  %g  %g\n" % (data.x[i], 
435                                                    data.y[i],
436                                                    data.dy[i]))
437                else:
438                    out.write("%g  %g\n" % (data.x[i], 
439                                            data.y[i]))
440            out.close()                 
441            try:
442                self._default_save_location = os.path.dirname(path)
443            except:
444                pass   
445               
446    def _onSave(self, evt):
447        """
448        Save a data set to a text file
449       
450        :param evt: Menu event
451       
452        """
453       
454        path = None
455        wildcard = "Text files (*.txt)|*.txt|"\
456        "CanSAS 1D files(*.xml)|*.xml" 
457        dlg = wx.FileDialog(self, "Choose a file",
458                            self._default_save_location,
459                             "", wildcard , wx.SAVE)
460       
461        if dlg.ShowModal() == wx.ID_OK:
462            path = dlg.GetPath()
463            # ext_num = 0 for .txt, ext_num = 1 for .xml
464            # This is MAC Fix
465            ext_num = dlg.GetFilterIndex()
466            if ext_num == 0:
467                format = '.txt'
468            else:
469                format = '.xml'
470            path = os.path.splitext(path)[0] + format
471            mypath = os.path.basename(path)
472           
473            #TODO: This is bad design. The DataLoader is designed
474            #to recognize extensions.
475            # It should be a simple matter of calling the .
476            #save(file, data, '.xml') method
477            # of the DataLoader.loader.Loader class.
478            from DataLoader.loader import  Loader
479            #Instantiate a loader
480            loader = Loader() 
481            data = self.plots[self.graph.selected_plottable]
482            format = ".txt"
483            if os.path.splitext(mypath)[1].lower() == format:
484                # Make sure the ext included in the file name
485                # especially on MAC
486                fName = os.path.splitext(path)[0] + format
487                self._onsaveTXT(fName)
488            format = ".xml"
489            if os.path.splitext(mypath)[1].lower() == format:
490                # Make sure the ext included in the file name
491                # especially on MAC
492                fName = os.path.splitext(path)[0] + format
493                loader.save(fName, data, format)
494            try:
495                self._default_save_location = os.path.dirname(path)
496            except:
497                pass   
498        dlg.Destroy()
499
500    def _add_more_tool(self):
501        """
502        Add refresh button in the tool bar
503        """
504        if self.parent.__class__.__name__ != 'ViewerFrame':
505            return
506        self.toolbar.AddSeparator()
507        id_delete = wx.NewId()
508        delete =  wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_TOOLBAR)
509        self.toolbar.AddSimpleTool(id_delete, delete,
510                           'Delete', 'permanently Delete')
511
512        self.toolbar.Realize()
513        wx.EVT_TOOL(self, id_delete,  self._on_delete)
514
515    def _on_delete(self, event): 
516        """
517        Refreshes the plotpanel on refresh tollbar button
518        """
519       
520        if self.parent is not None:
521            wx.PostEvent(self.parent, 
522                         NewPlotEvent(group_id=self.group_id,
523                                      action="delete"))
524           
Note: See TracBrowser for help on using the repository browser.