source: sasview/src/sans/guiframe/local_perspectives/plotting/masking.py @ 8f52edd

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 8f52edd was 8f52edd, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Update guiframe imports for latest move

  • Property mode set to 100644
File size: 28.2 KB
RevLine 
[bf7ad65]1"""
2    Mask editor
3"""
[d955bf19]4################################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#
9#If you use DANSE applications to do scientific research that leads to
10#publication, we ask that you acknowledge the use of the software with the
11#following sentence:
12#
[21969a4a]13#This work benefited from DANSE software developed under NSF award DMR-0520547.
[d955bf19]14#
15#copyright 2008, University of Tennessee
16################################################################################
[c5874f2]17
18
[a5ca7ef]19##Todo: cleaning up, improving the maskplotpanel initialization, and testing.
[c5874f2]20import wx
21import sys
[4a4164c]22import time
[4752c31]23import matplotlib.cm as cm
[32c0841]24import math
25import copy
26import numpy
[f468791]27from sans.plottools.PlotPanel import PlotPanel
28from sans.plottools.plottables import Graph
[c5874f2]29from binder import BindArtist
[7434020]30from sans.guiframe.dataFitting import Data1D, Data2D
[c5874f2]31from boxMask import BoxMask
[55a0dc1]32from sans.guiframe.events import SlicerEvent
33from sans.guiframe.events import StatusEvent
[32c0841]34(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
[c5874f2]35
[3f68e93]36DEFAULT_CMAP = cm.get_cmap('jet')
[c5874f2]37_BOX_WIDTH = 76
38_SCALE = 1e-6
[ca3b45e7]39_STATICBOX_WIDTH = 380
[52e0f2d]40
[c5874f2]41#SLD panel size
[32c0841]42if sys.platform.count("win32") > 0:
[52e0f2d]43    PANEL_SIZE = 350
[c5874f2]44    FONT_VARIANT = 0
45else:
[52e0f2d]46    PANEL_SIZE = 300
[c5874f2]47    FONT_VARIANT = 1
48   
[8f52edd]49from sans.data_util.calcthread import CalcThread
[c5874f2]50
[4a4164c]51class CalcPlot(CalcThread):
52    """
53    Compute Resolution
54    """
55    def __init__(self,
56                 id = -1,
57                 panel = None,
58                 image = None,
59                 completefn = None,
60                 updatefn   = None,
61                 elapsed = 0,
62                 yieldtime  = 0.01,
63                 worktime   = 0.01
64                 ):
65        """
66        """
[bf7ad65]67        CalcThread.__init__(self, completefn,
[4a4164c]68                 updatefn,
69                 yieldtime,
70                 worktime)
71        self.starttime = 0
72        self.id = id 
73        self.panel = panel
74        self.image = image
75       
76       
77    def compute(self):
78        """
79        excuting computation
80        """
81        elapsed = time.time() - self.starttime
82       
83        self.complete(panel=self.panel, image=self.image, elapsed=elapsed)
84       
85       
[c5874f2]86class MaskPanel(wx.Dialog):
87    """
[d955bf19]88    Provides the Mask Editor GUI.
[c5874f2]89    """
90    ## Internal nickname for the window, used by the AUI manager
91    window_name = "Mask Editor"
92    ## Name to appear on the window title bar
93    window_caption = "Mask Editor"
94    ## Flag to tell the AUI manager to put this panel in the center pane
95    CENTER_PANE = True
[32c0841]96    def __init__(self, parent=None, base=None, 
97                 data=None, id=-1, *args, **kwds):
[c5874f2]98        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
[7434020]99        kwds["size"] = wx.Size(_STATICBOX_WIDTH*0.8, PANEL_SIZE) 
[32c0841]100        wx.Dialog.__init__(self, parent, id=id,  *args, **kwds)
101       
[c5874f2]102        if data != None:
103            #Font size
[32c0841]104            kwds = []
[c5874f2]105            self.SetWindowVariant(variant=FONT_VARIANT)
[32c0841]106            self.SetTitle("Mask Editor for " + data.name)
[c5874f2]107            self.parent = base
108            self.data = data
109            self.str = self.data.__str__()
110            ## mask for 2D
111            self.mask = data.mask
112            self.default_mask = copy.deepcopy(data.mask)
113            ## masked data from GUI
114            self.slicer_mask = None
115            self.slicer = None
116            self.slicer_z = 5
117            self.data.interactive = True
118            ## when 2 data have the same id override the 1 st plotted
119            self.name = self.data.name
120            # Panel for 2D plot
[32c0841]121            self.plotpanel = Maskplotpanel(self, -1,
122                                           style=wx.TRANSPARENT_WINDOW)
[c5874f2]123            self.cmap = DEFAULT_CMAP
124            ## Create Artist and bind it
125            self.subplot = self.plotpanel.subplot
126            self.connect = BindArtist(self.subplot.figure)
127            self._setup_layout()
[32c0841]128            self.newplot = Data2D(image=self.data.data)
[c5874f2]129            self.newplot.setValues(self.data)
130            self.plotpanel.add_image(self.newplot) 
131            self._update_mask(self.mask)
132            self.Centre()
133            self.Layout()
134            # bind evt_close to _draw in fitpage
[8c21d53]135            self.Bind(wx.EVT_CLOSE, self.OnClose)
[c5874f2]136           
137    def ShowMessage(self, msg=''):
138        """
[d955bf19]139        Show error message when mask covers whole data area
[c5874f2]140        """
141        mssg = 'Erase, redraw or clear the mask. \n\r'
142        mssg += 'The data range can not be completely masked... \n\r'
143        mssg += msg
[32c0841]144        wx.MessageBox(mssg, 'Error', wx.OK | wx.ICON_ERROR)
[c5874f2]145   
146    def _setup_layout(self):
147        """
[d955bf19]148        Set up the layout
[c5874f2]149        """
[02aff3d]150        note = "Note: This masking applies\n     only to %s." % self.data.name
[3912db5]151        note_txt = wx.StaticText(self, -1, note) 
152        note_txt.SetForegroundColour(wx.RED)
[c5874f2]153        shape = "Select a Shape for Masking:"
154        #  panel
[32c0841]155        sizer = wx.GridBagSizer(10, 10)
[c5874f2]156        #---------inputs----------------
157        shape_txt = wx.StaticText(self, -1, shape) 
[32c0841]158        sizer.Add(shape_txt, (1, 1), flag=wx.TOP|wx.LEFT|wx.BOTTOM, border=5)
159        self.innersector_rb = wx.RadioButton(self, -1, "Double Wings")
160        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerSectorMask,
161                  id=self.innersector_rb.GetId())
162        sizer.Add(self.innersector_rb, (2, 1), 
163                  flag=wx.RIGHT|wx.BOTTOM, border=5)
164        self.innercircle_rb = wx.RadioButton(self, -1, "Circular Disk")
165        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerRingMask,
166                  id=self.innercircle_rb.GetId())
167        sizer.Add(self.innercircle_rb, (3, 1),
168                   flag=wx.RIGHT|wx.BOTTOM, border=5)
169       
170        self.innerbox_rb = wx.RadioButton(self, -1, "Rectangular Disk")
171        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerBoxMask,
172                  id=self.innerbox_rb.GetId())
173        sizer.Add(self.innerbox_rb, (4, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
[21969a4a]174
[32c0841]175        self.outersector_rb = wx.RadioButton(self, -1, "Double Wing Window")
176        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterSectorMask, 
177                  id=self.outersector_rb.GetId())
178        sizer.Add(self.outersector_rb, (5, 1),
179                  flag=wx.RIGHT|wx.BOTTOM, border=5)
[c5874f2]180       
181        #outersector_y_txt = wx.StaticText(self, -1, 'Outer Sector')
[32c0841]182        self.outercircle_rb = wx.RadioButton(self, -1, "Circular Window")
183        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterRingMask,
184                  id=self.outercircle_rb.GetId())
185        sizer.Add(self.outercircle_rb, (6, 1), 
186                  flag=wx.RIGHT|wx.BOTTOM, border=5)
[c5874f2]187        #outerbox_txt = wx.StaticText(self, -1, 'Outer Box')
[32c0841]188        self.outerbox_rb = wx.RadioButton(self, -1, "Rectangular Window")
189        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterBoxMask, 
190                  id=self.outerbox_rb.GetId())
191        sizer.Add(self.outerbox_rb, (7, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
[3912db5]192        sizer.Add(note_txt, (8, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
[c5874f2]193        self.innercircle_rb.SetValue(False)
194        self.outercircle_rb.SetValue(False)       
195        self.innerbox_rb.SetValue(False)
196        self.outerbox_rb.SetValue(False)
197        self.innersector_rb.SetValue(False)
198        self.outersector_rb.SetValue(False)
[32c0841]199        sizer.Add(self.plotpanel, (0, 2), (13, 13), 
200                  wx.EXPAND|wx.LEFT|wx.RIGHT, 15)
[c5874f2]201
202        #-----Buttons------------1
[21969a4a]203        id_button = wx.NewId()
204        button_add = wx.Button(self, id_button, "Add")
[c5874f2]205        button_add.SetToolTipString("Add the mask drawn.")
[32c0841]206        button_add.Bind(wx.EVT_BUTTON, self.onAddMask, id=button_add.GetId()) 
[c5874f2]207        sizer.Add(button_add, (13, 7))
[21969a4a]208        id_button = wx.NewId()
209        button_erase = wx.Button(self, id_button, "Erase")
[c5874f2]210        button_erase.SetToolTipString("Erase the mask drawn.")
[32c0841]211        button_erase.Bind(wx.EVT_BUTTON, self.onEraseMask,
212                          id=button_erase.GetId()) 
[c5874f2]213        sizer.Add(button_erase, (13, 8))
[21969a4a]214        id_button = wx.NewId()
215        button_reset = wx.Button(self, id_button, "Reset")
[c5874f2]216        button_reset.SetToolTipString("Reset the mask.")
[32c0841]217        button_reset.Bind(wx.EVT_BUTTON, self.onResetMask,
218                          id=button_reset.GetId()) 
219        sizer.Add(button_reset, (13, 9), flag=wx.RIGHT|wx.BOTTOM, border=15)
[21969a4a]220        id_button = wx.NewId()
221        button_reset = wx.Button(self, id_button, "Clear")
[c5874f2]222        button_reset.SetToolTipString("Clear all mask.")
[32c0841]223        button_reset.Bind(wx.EVT_BUTTON, self.onClearMask,
224                          id=button_reset.GetId()) 
225        sizer.Add(button_reset, (13, 10), flag=wx.RIGHT|wx.BOTTOM, border=15)
[c5874f2]226        sizer.AddGrowableCol(3)
227        sizer.AddGrowableRow(2)
228        self.SetSizerAndFit(sizer)
229        self.Centre()
230        self.Show(True)
231
[32c0841]232    def onInnerBoxMask(self, event=None):
[c5874f2]233        """
[d955bf19]234        Call Draw Box Slicer and get mask inside of the box
[c5874f2]235        """
236        #get ready for next evt
237        event.Skip()       
[32c0841]238        #from boxMask import BoxMask
239        if event != None:
240            self.onClearSlicer(event)         
[c5874f2]241        self.slicer_z += 1
[32c0841]242        self.slicer =  BoxMask(self, self.subplot,
243                               zorder=self.slicer_z, side=True)
[c5874f2]244        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
245        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
246        self.update()
247        self.slicer_mask = self.slicer.update()
248       
[32c0841]249    def onOuterBoxMask(self, event=None):
[c5874f2]250        """
[d955bf19]251        Call Draw Box Slicer and get mask outside of the box
[c5874f2]252        """
253        event.Skip()       
[32c0841]254        #from boxMask import BoxMask
255        if event != None:
256            self.onClearSlicer(event)     
[c5874f2]257        self.slicer_z += 1
[32c0841]258        self.slicer =  BoxMask(self, self.subplot,
259                               zorder=self.slicer_z, side=False)
[c5874f2]260        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
261        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
262        self.update()
263        self.slicer_mask = self.slicer.update()
264
[32c0841]265    def onInnerSectorMask(self, event=None):
[c5874f2]266        """
[d955bf19]267        Call Draw Sector Slicer and get mask inside of the sector
[c5874f2]268        """
269        event.Skip()
270        from sectorMask import SectorMask
[32c0841]271        if event != None:
[c5874f2]272            self.onClearSlicer(event)
273        self.slicer_z += 1
[32c0841]274        self.slicer =  SectorMask(self, self.subplot,
275                                  zorder=self.slicer_z, side=True)
[c5874f2]276        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
277        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
278        self.update()
279        self.slicer_mask = self.slicer.update() 
[a5ca7ef]280
[bf7ad65]281    def onOuterSectorMask(self, event=None):
[c5874f2]282        """
[d955bf19]283        Call Draw Sector Slicer and get mask outside of the sector
[c5874f2]284        """
285        event.Skip()
286        from sectorMask import SectorMask
[32c0841]287        if event != None:
[c5874f2]288            self.onClearSlicer(event)
289        self.slicer_z += 1
[32c0841]290        self.slicer =  SectorMask(self, self.subplot,
291                                  zorder=self.slicer_z, side=False)
[c5874f2]292        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
293        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
294        self.update()     
295        self.slicer_mask = self.slicer.update()   
[a5ca7ef]296
[c5874f2]297    def onInnerRingMask(self, event=None):
298        """
[d955bf19]299        Perform inner circular cut on Phi and draw circular slicer
[c5874f2]300        """
301        event.Skip()
302        from AnnulusSlicer import CircularMask
[32c0841]303        if event != None:
[c5874f2]304            self.onClearSlicer(event)
305        self.slicer_z += 1
[21969a4a]306        self.slicer = CircularMask(self, self.subplot,
[32c0841]307                                   zorder=self.slicer_z, side=True)
[c5874f2]308        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
309        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
310        self.update()
311        self.slicer_mask = self.slicer.update() 
[a5ca7ef]312
[c5874f2]313    def onOuterRingMask(self, event=None):
314        """
[d955bf19]315        Perform outer circular cut on Phi and draw circular slicer
[c5874f2]316        """
317        event.Skip()
318        from AnnulusSlicer import CircularMask
[32c0841]319        if event != None:
[c5874f2]320            self.onClearSlicer(event)
321        self.slicer_z += 1
[bf7ad65]322        self.slicer = CircularMask(self, self.subplot,
[32c0841]323                                   zorder=self.slicer_z, side=False)   
[c5874f2]324        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
325        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
326        self.update()
327        self.slicer_mask = self.slicer.update()     
328       
329    def onAddMask(self, event):
330        """
[d955bf19]331        Add new mask to old mask
[c5874f2]332        """
[32c0841]333        if not self.slicer == None:
[c5874f2]334            data = Data2D()
335            data = self.data
336            self.slicer_mask = self.slicer.update()
337            data.mask = self.data.mask & self.slicer_mask
338            self._check_display_mask(data.mask, event)
339           
340    def _check_display_mask(self, mask, event):
341        """
[d955bf19]342        check if the mask valid and update the plot
343       
344        :param mask: mask data
[c5874f2]345        """
346        ## Redraw the current image
347        self._update_mask(mask)
348
349    def onEraseMask(self, event):
350        """
[d955bf19]351        Erase new mask from old mask
[c5874f2]352        """
[bf7ad65]353        if not self.slicer == None:
[c5874f2]354            self.slicer_mask = self.slicer.update()
355            mask = self.data.mask
[bf7ad65]356            mask[self.slicer_mask == False] = True
[c5874f2]357            self._check_display_mask(mask, event)
358           
359    def onResetMask(self, event):
360        """
[d955bf19]361        Reset mask to the original mask
362        """       
[c5874f2]363        self.slicer_z += 1
[32c0841]364        self.slicer =  BoxMask(self, self.subplot, 
365                               zorder=self.slicer_z, side=True)
[c5874f2]366        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
367        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
368        mask = copy.deepcopy(self.default_mask)
369        self.data.mask = mask
370        # update mask plot
371        self._check_display_mask(mask, event)
372       
373    def onClearMask(self, event):
374        """
[d955bf19]375        Clear mask
[c5874f2]376        """           
377        self.slicer_z += 1
[32c0841]378        self.slicer =  BoxMask(self, self.subplot,
379                               zorder=self.slicer_z, side=True)
[c5874f2]380        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
381        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
382        #mask = copy.deepcopy(self.default_mask)
383        mask = numpy.ones(len(self.data.mask), dtype=bool)
384        self.data.mask = mask
385        # update mask plot
386        self._check_display_mask(mask, event)
387       
388    def onClearSlicer(self, event):
389        """
[d955bf19]390        Clear the slicer on the plot
[c5874f2]391        """
[32c0841]392        if not self.slicer == None:
[c5874f2]393            self.slicer.clear()
394            self.subplot.figure.canvas.draw()
395            self.slicer = None
396
397    def update(self, draw=True):
398        """
[d955bf19]399        Respond to changes in the model by recalculating the
400        profiles and resetting the widgets.
[c5874f2]401        """
402        self.plotpanel.draw()
403       
404    def _set_mask(self, mask):
405        """
[d955bf19]406        Set mask
[c5874f2]407        """
408        self.data.mask = mask
409       
[3858e0f]410    def set_plot_unfocus(self):
411        """
412        Not implemented
413        """
414        pass
415   
[bf7ad65]416    def _update_mask(self, mask):
[c5874f2]417        """
[d955bf19]418        Respond to changes in masking
[c5874f2]419        """ 
420        # the case of liitle numbers of True points
[32c0841]421        if (len(mask[mask]) < 10 and self.data != None):
[c5874f2]422            self.ShowMessage()
423            mask = copy.deepcopy(self.mask)
424            self.data.mask = mask
425        else:
426            self.mask = mask
427        # make temperary data to plot
428        temp_mask = numpy.zeros(len(mask))
429        temp_data = copy.deepcopy(self.data)
430        # temp_data default is None
431        # This method is to distinguish between masked point and data point = 0.
432        temp_mask = temp_mask/temp_mask
433        temp_mask[mask] = temp_data.data[mask]
434        # set temp_data value for self.mask==True, else still None
435        #temp_mask[mask] = temp_data[mask]
[bf7ad65]436       
437        #TODO: refactor this horrible logic
438        temp_data.data[mask == False] = temp_mask[mask == False]
[c5874f2]439        self.plotpanel.clear()
440        if self.slicer != None:
441            self.slicer.clear()
442            self.slicer = None
443        # Post slicer None event
444        event = self._getEmptySlicerEvent()
445        wx.PostEvent(self, event)
[9266c66]446       
[ca6c17e8]447        ##use this method
[e306b96e]448        #set zmax and zmin to plot: Fix it w/ data.
[52e0f2d]449        if self.plotpanel.scale == 'log_{10}':
450            zmax = math.log10(max(self.data.data[self.data.data>0]))
451            zmin = math.log10(min(self.data.data[self.data.data>0]))
[e306b96e]452        else:
453            zmax = max(self.data.data[self.data.data>0])
454            zmin = min(self.data.data[self.data.data>0])
[ca6c17e8]455        #plot   
[32c0841]456        plot = self.plotpanel.image(data=temp_mask,
[c5874f2]457                       qx_data=self.data.qx_data,
458                       qy_data=self.data.qy_data,
[32c0841]459                       xmin=self.data.xmin,
460                       xmax=self.data.xmax,
461                       ymin=self.data.ymin,
462                       ymax=self.data.ymax,
463                       zmin=zmin,
464                       zmax=zmax,
465                       cmap=self.cmap,
466                       color=0, symbol=0, label=self.data.name)
[54180f9]467        # axis labels
[0aee80d]468        self.plotpanel.axes[0].set_xlabel('$\\rm{Q}_{x}(A^{-1})$')
469        self.plotpanel.axes[0].set_ylabel('$\\rm{Q}_{y}(A^{-1})$')
[c5874f2]470        self.plotpanel.render()
471        self.plotpanel.subplot.figure.canvas.draw_idle()
472       
473    def _getEmptySlicerEvent(self):
474        """
[d955bf19]475        create an empty slicervent
[c5874f2]476        """
477        self.innerbox_rb.SetValue(False)
478        self.outerbox_rb.SetValue(False)
479        self.innersector_rb.SetValue(False)
480        self.outersector_rb.SetValue(False)
481        self.innercircle_rb.SetValue(False)
482        self.outercircle_rb.SetValue(False)
483        return SlicerEvent(type=None,
484                           params=None,
485                           obj_class=None) 
486             
[32c0841]487    def _draw_model(self, event):
[c5874f2]488        """
[d955bf19]489         on_close, update the model2d plot
[c5874f2]490        """
491        pass
492       
493    def freeze_axes(self):
[d955bf19]494        """
[21969a4a]495        freeze axes
[d955bf19]496        """
[c5874f2]497        self.plotpanel.axes_frozen = True
[d955bf19]498       
[c5874f2]499    def thaw_axes(self):
[d955bf19]500        """
[21969a4a]501        thaw axes
[d955bf19]502        """
503        self.plotpanel.axes_frozen = False       
504         
[32c0841]505    def onMouseMotion(self, event):
[d955bf19]506        """
[21969a4a]507        onMotion event
[d955bf19]508        """
[c5874f2]509        pass
[d955bf19]510   
[c5874f2]511    def onWheel(self, event):
[d955bf19]512        """
[21969a4a]513        on wheel event
[d955bf19]514        """
[c5874f2]515        pass 
[309a1f0]516   
517    def OnClose(self, event):
518        """
[bf7ad65]519            Processing close event
[309a1f0]520        """
[8c7ba5b]521        try:
522            self.parent._draw_masked_model(event)
523        except:
524            # when called by data panel
525            event.Skip()
[7434020]526            pass   
527
528class FloatPanel(wx.Dialog):
529    """
530    Provides the Mask Editor GUI.
531    """
532    ## Internal nickname for the window, used by the AUI manager
533    window_name = "Plot"
534    ## Name to appear on the window title bar
535    window_caption = "Plot"
536    ## Flag to tell the AUI manager to put this panel in the center pane
[4a4164c]537    CENTER_PANE = False
538    ID = wx.NewId()
[7434020]539    def __init__(self, parent=None, base=None, 
[4a4164c]540                 data=None, dimension=1, id=ID, *args, **kwds):
[7434020]541        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
[52e0f2d]542        kwds["size"] = wx.Size(PANEL_SIZE*1.5, PANEL_SIZE*1.5) 
[7434020]543        wx.Dialog.__init__(self, parent, id=id,  *args, **kwds)
544       
545        if data != None:
546            #Font size
547            kwds = []
548            self.SetWindowVariant(variant=FONT_VARIANT)
549            self.SetTitle("Plot " + data.name)
550            self.parent = base
551            self.data = data
552            self.str = self.data.__str__()
553            ## when 2 data have the same id override the 1 st plotted
554            self.name = self.data.name
555            self.dimension = dimension
556            # Panel for 2D plot
557            self.plotpanel = Maskplotpanel(self, -1, dimension,
558                                           style=wx.TRANSPARENT_WINDOW)
[52e0f2d]559            self.plotpanel._SetInitialSize()
[657e52c]560            self.plotpanel.prevXtrans = "x"
561            self.plotpanel.prevYtrans = "y"
[7434020]562           
563            self.cmap = DEFAULT_CMAP
564            ## Create Artist and bind it
565            self.subplot = self.plotpanel.subplot
566            self._setup_layout()
567            if self.dimension == 1:
568                self.newplot = Data1D(x=data.x, y=data.y, 
569                                      dx=data.dx, dy=data.dy)
570                self.newplot.name = data.name
571            else:   
572                self.newplot = Data2D(image=self.data.data)
573                self.newplot.setValues(self.data)
[4a4164c]574                    # Compute and get the image plot
575            self.get_plot()
576            #self.plotpanel.add_image(self.newplot)
[7434020]577            self.Centre()
578            self.Layout()
[4a4164c]579   
580    def get_plot(self):
581        """
582        Get Plot panel
583        """
584        cal_plot = CalcPlot(panel=self.plotpanel, 
585                                   image=self.newplot, 
586                                   completefn=self.complete)
587        cal_plot.queue()
588   
589    def complete(self, panel, image, elapsed=None):
590        """
591        Plot image
592       
593        :param image: newplot [plotpanel]
594        """
595        wx.CallAfter(panel.add_image, image) 
596       
[7434020]597    def _setup_layout(self):
598        """
599        Set up the layout
600        """
601        #  panel
602        sizer = wx.GridBagSizer(10, 10)
603        if self.dimension == 3:
604            note = "Note: I am very SLOW.     Please be PATIENT...\n"
[4a4164c]605            if len(self.data.data) > 3600:
606                note += "Rotation disabled for pixels > 60x60."
[7434020]607            note_txt = wx.StaticText(self, -1, note) 
608            note_txt.SetForegroundColour(wx.RED)
[52e0f2d]609            sizer.Add(note_txt, (0, 2), flag=wx.RIGHT|wx.TOP, border=5)
[7434020]610       
[52e0f2d]611        sizer.Add(self.plotpanel, (1, 0), (9, 9), 
[7434020]612                  wx.EXPAND|wx.ALL, 15)
613
614        sizer.AddGrowableCol(3)
615        sizer.AddGrowableRow(2)
616       
617        self.SetSizerAndFit(sizer)
618        self.Centre()
619        self.Show(True)
620       
621    def set_plot_unfocus(self):
622        """
623        Not implemented
624        """
625        pass
626   
627
628    def _draw_model(self, event):
629        """
630         on_close, update the model2d plot
631        """
632        pass
633       
634    def freeze_axes(self):
635        """
636        freeze axes
637        """
638        self.plotpanel.axes_frozen = True
[25952cd]639       
[7434020]640    def thaw_axes(self):
641        """
642        thaw axes
643        """
644        self.plotpanel.axes_frozen = False       
645   
646    def OnClose(self, event):
647        """
648        """
649        try:
650            self.plotpanel.subplot.figure.clf()
[4752c31]651            self.plotpanel.Close()
[7434020]652        except:
653            # when called by data panel
654            event.Skip()
655            pass
656
657               
[a5ca7ef]658class Maskplotpanel(PlotPanel):
[d955bf19]659    """
[7434020]660    PlotPanel for Quick plot and masking plot
[d955bf19]661    """
[7434020]662    def __init__(self, parent, id=-1, dimension=2, color=None, 
663                                            dpi=None, **kwargs):
[d955bf19]664        """
665        """
[c5874f2]666        PlotPanel.__init__(self, parent, id=id, color=color, dpi=dpi, **kwargs)
667       
668        # Keep track of the parent Frame
669        self.parent = parent
670        # Internal list of plottable names (because graph
671        # doesn't have a dictionary of handles for the plottables)
[7434020]672        self.dimension = dimension
[c5874f2]673        self.plots = {}
[a5ca7ef]674        self.graph = Graph()
[7434020]675        #add axis labels
[046af80]676        self.graph.xaxis('\\rm{x} ', '')
677        self.graph.yaxis('\\rm{y} ', '')
[c5874f2]678       
679    def add_toolbar(self):
680        """
[d955bf19]681        Add toolbar
[c5874f2]682        """
683        # Not implemented
684        pass
[7434020]685   
[c5874f2]686    def on_set_focus(self, event):
687        """
[d955bf19]688        send to the parenet the current panel on focus
[c5874f2]689        """
[7434020]690        if self.dimension == 3:
691            pass
692        else:
693            self.draw()   
[c5874f2]694         
695    def add_image(self, plot):
[d955bf19]696        """
[21969a4a]697        Add Image
[d955bf19]698        """
[c5874f2]699        self.plots[plot.name] = plot
[a5ca7ef]700        #init graph
[bf7ad65]701        self.graph = Graph()
[a5ca7ef]702        #add plot
[c5874f2]703        self.graph.add(plot)
[a5ca7ef]704        #add axes
[7434020]705        if self.dimension == 1:
706            self.xaxis_label = '\\rm{x} '
707            self.xaxis_unit = ''
708            self.yaxis_label = '\\rm{y} '
709            self.yaxis_unit = ''
[a5ca7ef]710        #draw
[7434020]711        # message
712        status_type = 'progress' 
713        msg = 'Plotting...'
714        self._status_info(msg, status_type)
715        status_type = 'stop'           
[c5874f2]716        self.graph.render(self)
[52e0f2d]717        self.subplot.figure.canvas.resizing = False
[4a4164c]718        if self.dimension < 3:
719            self.graph.render(self)
[52e0f2d]720            self.subplot.figure.canvas.draw()
721        elif FONT_VARIANT:
722            self.subplot.figure.canvas.draw()
[7434020]723        msg = 'Plotting Completed.'
724        self._status_info(msg, status_type)
[a5ca7ef]725       
[c5874f2]726    def onMouseMotion(self, event):
727        """
[d955bf19]728        Disable dragging 2D image
[c5874f2]729        """
730        pass
[7434020]731   
732    def onWheel(self, event):
733        """
734        """
735        pass 
736     
737    def onLeftDown(self, event):
738        """
739        Disables LeftDown
740        """
741        pass
742   
743    def onPick(self, event):
744        """
745        Disables OnPick
746        """
747        pass
748   
749    def draw(self):
750        """
751        Draw
752        """
753        # message
754        status_type = 'progress' 
755        msg = 'Plotting...'
756        self._status_info(msg, status_type)
757        status_type = 'stop'           
758       
759        if self.dimension == 3:
760            pass
761        else:
[bf7ad65]762            self.subplot.figure.canvas.draw_idle() 
[7434020]763       
764        msg = 'Plotting Completed.'
765        self._status_info(msg, status_type)
766       
[c5874f2]767    def onContextMenu(self, event):
768        """
[d955bf19]769        Default context menu for a plot panel
[c5874f2]770        """
[7434020]771        # Selective Slicer plot popup menu
[b40439d9]772        slicerpop = wx.Menu()
[c5874f2]773       
[7434020]774        id = wx.NewId()
[bf7ad65]775        slicerpop.Append(id, '&Print Image', 'Print image')
[7434020]776        wx.EVT_MENU(self, id, self.onPrint)
777
778        id = wx.NewId()
779        slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
780        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
781       
782        if self.dimension == 1:
783            id = wx.NewId()
784            slicerpop.Append(id, '&Change Scale')
785            wx.EVT_MENU(self, id, self._onProperties)
786        else:
787            slicerpop.AppendSeparator()
788            id_cm = wx.NewId()
789            slicerpop.Append(id_cm, '&Toggle Linear/Log scale')
790            wx.EVT_MENU(self, id_cm, self._onToggleScale)
[c5874f2]791               
792        pos = event.GetPosition()
793        pos = self.ScreenToClient(pos)
794        self.PopupMenu(slicerpop, pos)
[7434020]795       
796    def _status_info(self, msg = '', type = "update"):
797        """
798        Status msg
799        """
800        if type == "stop":
801            label = "Plotting..."
802            able = True
803        else:   
804            label = "Wait..."
805            able = False
806        if self.parent.parent.parent != None:
807                wx.PostEvent(self.parent.parent.parent, 
808                             StatusEvent(status = msg, type = type ))
[c5874f2]809
[e306b96e]810class ViewerFrame(wx.Frame):
[b9d68ff]811    """
812    Add comment
813    """
[e306b96e]814    def __init__(self, parent, id, title):
[b9d68ff]815        """
816        comment
817        :param parent: parent panel/container
818        """
[e306b96e]819        # Initialize the Frame object
[32c0841]820        wx.Frame.__init__(self, parent, id, title,
821                          wx.DefaultPosition, wx.Size(950, 850))
[e306b96e]822        # Panel for 1D plot
[32c0841]823        self.plotpanel = Maskplotpanel(self, -1, style=wx.RAISED_BORDER)
[c5874f2]824
[e306b96e]825class ViewApp(wx.App):
826    def OnInit(self):
827        frame = ViewerFrame(None, -1, 'testView')   
828        frame.Show(True)
[fdc127c]829        #self.SetTopWindow(frame)
[e306b96e]830       
831        return True
832               
[c5874f2]833if __name__ == "__main__": 
834    app = ViewApp(0)
835    app.MainLoop()     
Note: See TracBrowser for help on using the repository browser.