source: sasview/src/sas/sasgui/guiframe/local_perspectives/plotting/masking.py @ d3bce8c

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since d3bce8c was 9a5097c, checked in by andyfaff, 8 years ago

MAINT: import numpy as np

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