source: sasview/src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py @ c155a16

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 c155a16 was c155a16, checked in by Ricardo Ferraz Leal <ricleal@…>, 7 years ago

Logging is now logger

  • Property mode set to 100644
File size: 31.6 KB
RevLine 
[1bf33c1]1
[d955bf19]2################################################################################
[090e07e]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.
[d955bf19]6#
[090e07e]7# See the license text in license.txt
[d955bf19]8#
[090e07e]9# copyright 2008, University of Tennessee
[d955bf19]10################################################################################
[1bf33c1]11
12
13import wx
[4ac8556]14import sys
15import math
16import numpy
[090e07e]17import logging
[d7bb526]18from sas.sasgui.plottools.PlotPanel import PlotPanel
[d85c194]19from sas.sasgui.guiframe.events import StatusEvent
20from sas.sasgui.guiframe.events import PanelOnFocusEvent
21from sas.sasgui.guiframe.utils import PanelMenu, IdList
22from sas.sasgui.guiframe.panel_base import PanelBase
23from sas.sasgui.guiframe.gui_style import GUIFRAME_ICON
[f8be87d]24from appearanceDialog import appearanceDialog
[8f59e95]25from graphAppearance import graphAppearance
[1bf33c1]26
[c155a16]27logger = logging.getLogger()
28
[0d9dae8]29DEFAULT_QMAX = 0.05
[1bf33c1]30DEFAULT_QSTEP = 0.001
31DEFAULT_BEAM = 0.005
[32c0841]32BIN_WIDTH = 1
[940aca7]33IS_MAC = (sys.platform == 'darwin')
[0d9dae8]34
[8f59e95]35
36def find_key(dic, val):
37    """return the key of dictionary dic given the value"""
38    return [k for k, v in dic.iteritems() if v == val][0]
39
[691643c]40class ModelPanel1D(PlotPanel, PanelBase):
[1bf33c1]41    """
[d955bf19]42    Plot panel for use with the GUI manager
[1bf33c1]43    """
[090e07e]44
[1bf33c1]45    ## Internal name for the AUI manager
46    window_name = "plotpanel"
47    ## Title to appear on top of the window
[37c36d9]48    window_caption = "Graph"
[1bf33c1]49    ## Flag to tell the GUI manager that this panel is not
50    #  tied to any perspective
51    ALWAYS_ON = True
52    ## Group ID
53    group_id = None
[6f16e25]54    _menu_ids = IdList()
[090e07e]55
56    def __init__(self, parent, id=-1, color=None,
[32c0841]57                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
58        PlotPanel.__init__(self, parent, id=id, style=style, **kwargs)
[a45037aa]59        PanelBase.__init__(self, parent)
[1bf33c1]60        ## Reference to the parent window
61        self.parent = parent
[ae84427]62        if hasattr(parent, "parent"):
63            self.parent = self.parent.parent
[1bf33c1]64        ## Plottables
65        self.plots = {}
[ae84427]66        self.frame = None
[090e07e]67        # context menu
[52b8b74]68        self._slicerpop = None
69        self._available_data = []
[a07e72f]70        self._symbol_labels = self.get_symbol_label()
[2636188]71        self._color_labels = self.get_color_label()
72        self.currColorIndex = ""
[36288ca]73        self._is_changed_legend_label = False
[657e52c]74        self.is_xtick = False
75        self.is_ytick = False
[090e07e]76
[a07e72f]77        self.hide_menu = None
[1bf33c1]78        ## Unique ID (from gui_manager)
79        self.uid = None
[66718a1]80        self.x_size = None
[6063b16]81        ## Default locations
[090e07e]82        # self._default_save_location = os.getcwd()
83        self.size = None
84        self.vl_ind = 0
85        ## Graph
86        # self.graph = Graph()
[1bf33c1]87        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
[32c0841]88        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
[1bf33c1]89        self.graph.render(self)
[3e001f9]90        self.cursor_id = None
[090e07e]91
[6d727ae]92        # In resizing event
93        self.resizing = False
94        self.canvas.set_resizing(self.resizing)
95        self.Bind(wx.EVT_SIZE, self._OnReSize)
[6e75ed0]96        self.parent.SetFocus()
[090e07e]97
[5a54aa4]98        # If true, there are 3 qrange bars
99        self.is_corfunc = False
100
[090e07e]101
[a07e72f]102    def get_symbol_label(self):
[52b8b74]103        """
[a07e72f]104        Associates label to symbol
[52b8b74]105        """
[a07e72f]106        _labels = {}
107        i = 0
[6d727ae]108        _labels['Circle'] = i
[a07e72f]109        i += 1
[6d727ae]110        _labels['Cross X '] = i
[a07e72f]111        i += 1
112        _labels['Triangle Down'] = i
113        i += 1
114        _labels['Triangle Up'] = i
115        i += 1
116        _labels['Triangle Left'] = i
117        i += 1
118        _labels['Triangle Right'] = i
119        i += 1
[6d727ae]120        _labels['Cross +'] = i
[a07e72f]121        i += 1
122        _labels['Square'] = i
123        i += 1
[318b5bbb]124        _labels['diamond'] = i
[a07e72f]125        i += 1
126        _labels['Diamond'] = i
127        i += 1
128        _labels['Hexagon1'] = i
129        i += 1
130        _labels['Hexagon2'] = i
131        i += 1
132        _labels['Pentagon'] = i
133        i += 1
[6d727ae]134        _labels['Line'] = i
[6c4130a]135        i += 1
136        _labels['Dash'] = i
137        i += 1
138        _labels['Vline'] = i
139        i += 1
140        _labels['Step'] = i
[a07e72f]141        return _labels
[090e07e]142
[2636188]143    def get_color_label(self):
144        """
145        Associates label to a specific color
146        """
147        _labels = {}
148        i = 0
149        _labels['Blue'] = i
150        i += 1
151        _labels['Green'] = i
152        i += 1
153        _labels['Red'] = i
154        i += 1
155        _labels['Cyan'] = i
156        i += 1
157        _labels['Magenta'] = i
158        i += 1
159        _labels['Yellow'] = i
[ad019b83]160        i += 1
161        _labels['Black'] = i
[2636188]162        return _labels
[a07e72f]163
[090e07e]164
[32c0841]165    def set_data(self, list=None):
[3c44c66]166        """
167        """
168        pass
[090e07e]169
[1bf33c1]170    def _reset(self):
171        """
[d955bf19]172        Resets internal data and graph
[090e07e]173        """
[1bf33c1]174        self.graph.reset()
[090e07e]175        self.plots = {}
[cafa75f]176        self.is_zoomed = False
[090e07e]177
178    def _OnReSize(self, event):
[6d727ae]179        """
180        On response of the resize of a panel, set axes_visiable False
181        """
[66718a1]182        # It was found that wx >= 2.9.3 sends an event even if no size changed.
[294efd5]183        # So manually recode the size (=x_size) and compare here.
[940aca7]184        # Massy code to work around:<
185        if self.parent._mgr != None:
186            max_panel = self.parent._mgr.GetPane(self)
187            if max_panel.IsMaximized():
188                self.parent._mgr.RestorePane(max_panel)
189                max_panel.Maximize()
[66718a1]190        if self.x_size != None:
191            if self.x_size == self.GetSize():
[71fa9bb9]192                self.resizing = False
193                self.canvas.set_resizing(self.resizing)
[66718a1]194                return
[294efd5]195        self.x_size = self.GetSize()
196
[ad9e5e1]197        # Ready for another event
[4520830]198        # Do not remove this Skip. Otherwise it will get runtime error on wx>=2.9.
[090e07e]199        event.Skip()
[6d727ae]200        # set the resizing flag
201        self.resizing = True
202        self.canvas.set_resizing(self.resizing)
203        self.parent.set_schedule(True)
[adf44c2]204        pos_x, pos_y = self.GetPositionTuple()
205        if pos_x != 0 and pos_y != 0:
[294efd5]206            self.size, _ = self.GetClientSizeTuple()
[2b29bb4]207        self.SetSizer(self.sizer)
[090e07e]208        wx.CallAfter(self.parent.disable_app_menu, self)
209
[3e001f9]210    def on_plot_qrange(self, event=None):
211        """
212        On Qmin Qmax vertical line event
213        """
214        if event == None:
215            return
[090e07e]216        event.Skip()
[3e001f9]217        active_ctrl = event.active
218        if active_ctrl == None:
219            return
[5a54aa4]220        if hasattr(event, 'is_corfunc'):
221            self.is_corfunc = event.is_corfunc
[3e001f9]222        if event.id in self.plots.keys():
[b99a4552]223            ctrl = event.ctrl
224            self.cursor_id = event.id
[3e001f9]225            # Set line position and color
226            colors = ['red', 'purple']
[b99a4552]227            x_data = self.plots[self.cursor_id].x
228            values = [max(x_data.min(), float(ctrl[0].GetValue())),
229                      min(x_data.max(), float(ctrl[1].GetValue()))]
[3901e7c]230            if len(ctrl) == 3:
231                colors.append('purple')
232                values.append(min(x_data.max(), float(ctrl[2].GetValue())))
[3e001f9]233            if self.ly == None:
234                self.ly = []
[b99a4552]235                for c, v in zip(colors, values):
236                    h = self.subplot.axvline(x=v, color=c, lw=2.5, alpha=0.7)
237                    h.set_rasterized(True)
238                    self.ly.append(h)
[3e001f9]239            try:
240                # Display x,y in the status bar if possible
241                xval = float(active_ctrl.GetValue())
242                position = self.get_data_xy_vals(xval)
[245ae18]243                if position != None and not self.is_corfunc:
[3e001f9]244                    wx.PostEvent(self.parent, StatusEvent(status=position))
245            except:
[c155a16]246                logger.error(sys.exc_value)
[3e001f9]247            if not event.leftdown:
[090e07e]248                # text event
[3e001f9]249                try:
250                    is_moved = False
[b99a4552]251                    for h, v in zip(self.ly, values):
[3e001f9]252                        # check if vline moved
[b99a4552]253                        if h.get_xdata() != v:
254                            h.set_xdata(v)
[3e001f9]255                            is_moved = True
256                    if is_moved:
[090e07e]257                        self.canvas.draw()
[3e001f9]258                except:
[c155a16]259                    logger.error(sys.exc_value)
[090e07e]260                event.Skip()
[3e001f9]261                return
262            self.q_ctrl = ctrl
[b99a4552]263            for h, c, v in zip(self.ly, colors, values):
264                h.set_color(c)
265                h.set_xdata(v)
[3e001f9]266            self.canvas.draw()
267        else:
268            self.q_ctrl = None
[090e07e]269
[3e001f9]270    def get_data_xy_vals(self, xval):
271        """
272        Get x, y data values near x = x_val
273        """
274        try:
275            x_data = self.plots[self.cursor_id].x
276            y_data = self.plots[self.cursor_id].y
277            indx = self._find_nearest(x_data, xval)
278            pos_x = x_data[indx]
279            pos_y = y_data[indx]
280            position = str(pos_x), str(pos_y)
281            return position
282        except:
283            return None
[090e07e]284
[3e001f9]285    def _find_nearest(self, array, value):
286        """
287        Find and return the nearest value in array to the value.
288        Used in cusor_line()
289        :Param array: numpy array
290        :Param value: float
291        """
292        idx = (numpy.abs(array - value)).argmin()
[090e07e]293        return int(idx)  # array.flat[idx]
294
[3e001f9]295    def _check_line_positions(self, pos_x=None, nop=None):
296        """
297        Check vertical line positions
298        :Param pos_x: position of the current line [float]
299        :Param nop: number of plots [int]
300        """
301        ly = self.ly
302        ly0x = ly[0].get_xdata()
303        ly1x = ly[1].get_xdata()
[5a54aa4]304        ly2x = None
305        if self.is_corfunc: ly2x = ly[2].get_xdata()
[3e001f9]306        self.q_ctrl[0].SetBackgroundColour('white')
307        self.q_ctrl[1].SetBackgroundColour('white')
308        if ly0x >= ly1x:
309            if self.vl_ind == 0:
310                ly[1].set_xdata(pos_x)
311                ly[1].set_zorder(nop)
312                self.q_ctrl[1].SetValue(str(pos_x))
313                self.q_ctrl[0].SetBackgroundColour('pink')
314            elif self.vl_ind == 1:
315                ly[0].set_xdata(pos_x)
316                ly[0].set_zorder(nop)
317                self.q_ctrl[0].SetValue(str(pos_x))
318                self.q_ctrl[1].SetBackgroundColour('pink')
[5a54aa4]319        elif ly2x is not None and ly1x >= ly2x:
320            if self.vl_ind == 1:
321                ly[2].set_xdata(posx)
322                ly[2].set_zorder(nop)
323                self.q_ctrl[2].SetValue(str(pos_x))
324            elif self.vl_ind == 2:
325                ly[1].set_xdata(posx)
326                ly[1].set_zorder(nop)
327                self.q_ctrl[1].SetValue(str(pos_x))
328
[090e07e]329
[3e001f9]330    def _get_cusor_lines(self, event):
331        """
332        Revmove or switch cursor line if drawn
333        :Param event: LeftClick mouse event
[090e07e]334        """
[3e001f9]335        ax = event.inaxes
[ae84427]336        if hasattr(event, "action"):
337            dclick = event.action == 'dclick'
338            if ax == None or dclick:
339                # remove the vline
340                self._check_zoom_plot()
341                self.canvas.draw()
342                self.q_ctrl = None
[090e07e]343                return
[3e001f9]344        if self.ly != None and event.xdata != None:
345            # Selecting a new line if cursor lines are displayed already
346            dqmin = math.fabs(event.xdata - self.ly[0].get_xdata())
347            dqmax = math.fabs(event.xdata - self.ly[1].get_xdata())
[5a54aa4]348            if not self.is_corfunc:
349                is_qmax = dqmin > dqmax
350                if is_qmax:
351                    self.vl_ind = 1
352                else:
353                    self.vl_ind = 0
[3e001f9]354            else:
[5a54aa4]355                dqmax2 = math.fabs(event.xdata - self.ly[2].get_xdata())
356                closest = min(dqmin, dqmax, dqmax2)
357                self.vl_ind = { dqmin: 0, dqmax: 1, dqmax2: 2 }.get(closest)
[090e07e]358
[3e001f9]359    def cusor_line(self, event):
360        """
361        Move the cursor line to write Q range
362        """
363        if self.q_ctrl == None:
364            return
[090e07e]365        # release a q range vline
[3e001f9]366        if self.ly != None and not self.leftdown:
367            for ly in self.ly:
368                ly.set_alpha(0.7)
369                self.canvas.draw()
370            return
371        ax = event.inaxes
372        if ax == None or not hasattr(event, 'action'):
373            return
374        end_drag = event.action != 'drag' and event.xdata != None
375        nop = len(self.plots)
[090e07e]376        pos_x, _ = float(event.xdata), float(event.ydata)
[3e001f9]377        try:
378            ly = self.ly
379            ly0x = ly[0].get_xdata()
380            ly1x = ly[1].get_xdata()
381            if ly0x == ly1x:
382                if ly[0].get_zorder() > ly[1].get_zorder():
383                    self.vl_ind = 0
384                else:
385                    self.vl_ind = 1
386            vl_ind = self.vl_ind
387            x_data = self.plots[self.cursor_id].x
388            xmin = x_data.min()
389            xmax = x_data.max()
390            indx = self._find_nearest(x_data, pos_x)
391            # Need to hold LeftButton to drag
392            if end_drag:
393                if event.button:
394                    self._check_line_positions(pos_x, nop)
[090e07e]395                return
[3e001f9]396            if indx >= len(x_data):
397                indx = len(x_data) - 1
398            pos_x = x_data[indx]
399            if xmin == ly1x:
400                vl_ind = 1
401            elif xmax == ly0x:
402                vl_ind = 0
403            else:
404                ly[vl_ind].set_xdata(pos_x)
405                ly[vl_ind].set_zorder(nop + 1)
406                self._check_line_positions(pos_x, nop)
407            ly[vl_ind].set_xdata(pos_x)
408            ly[vl_ind].set_alpha(1.0)
409            ly[vl_ind].set_zorder(nop + 1)
410            self.canvas.draw()
411            self.q_ctrl[vl_ind].SetValue(str(pos_x))
412        except:
[c155a16]413            logger.error(sys.exc_value)
[090e07e]414
[6d727ae]415    def set_resizing(self, resizing=False):
416        """
417        Set the resizing (True/False)
418        """
419        self.resizing = resizing
[090e07e]420        # self.canvas.set_resizing(resizing)
421
422    def schedule_full_draw(self, func='append'):
[6d727ae]423        """
424        Put self in schedule to full redraw list
425        """
426        # append/del this panel in the schedule list
427        self.parent.set_schedule_full_draw(self, func)
428
[a07e72f]429    def remove_data_by_id(self, id):
[98816c43]430        """
431            Remove data from plot
[a07e72f]432        """
433        if id in self.plots.keys():
[090e07e]434            data = self.plots[id]
[a07e72f]435            self.graph.delete(data)
436            data_manager = self._manager.parent.get_data_manager()
[df22224]437            data_list, theory_list = data_manager.get_by_id(id_list=[id])
[090e07e]438
[df22224]439            if id in data_list.keys():
440                data = data_list[id]
[248b918]441            if id in theory_list.keys():
[df22224]442                data = theory_list[id]
[090e07e]443
[a07e72f]444            del self.plots[id]
445            self.graph.render(self)
[090e07e]446            self.subplot.figure.canvas.draw_idle()
[a07e72f]447            if len(self.graph.plottables) == 0:
[090e07e]448                # onRemove: graph is empty must be the panel must be destroyed
[df22224]449                self.parent.delete_panel(self.uid)
[090e07e]450
[a07e72f]451    def plot_data(self, data):
[1bf33c1]452        """
[d955bf19]453        Data is ready to be displayed
[090e07e]454
[d955bf19]455        :param event: data event
[1bf33c1]456        """
[b837e1f]457        if data.__class__.__name__ == 'Data2D':
458            return
[6c4130a]459        plot_keys = self.plots.keys()
460        if data.id in plot_keys:
[090e07e]461            # Recover panel prop.s
[e4a703a]462            xlo, xhi = self.subplot.get_xlim()
463            ylo, yhi = self.subplot.get_ylim()
[5eede4e]464            old_data = self.plots[data.id]
[36288ca]465            if self._is_changed_legend_label:
466                data.label = old_data.label
[c9579e0]467            if old_data.__class__.__name__ == 'Data1D':
468                data.custom_color = old_data.custom_color
469                data.symbol = old_data.symbol
470                data.markersize = old_data.markersize
[6c4130a]471                data.zorder = len(plot_keys)
[e4a703a]472            # Replace data
[a07e72f]473            self.graph.replace(data)
474            self.plots[data.id] = data
[e4a703a]475            ## Set the view scale for all plots
[a4964b8e]476            try:
477                self._onEVT_FUNC_PROPERTY()
[cafa75f]478            except Exception, exc:
[090e07e]479                wx.PostEvent(self.parent,
480                             StatusEvent(status="Plotting Error: %s" % str(exc), info="error"))
[cafa75f]481            if self.is_zoomed:
[7022fdc]482                # Recover the x,y limits
[090e07e]483                self.subplot.set_xlim((xlo, xhi))
484                self.subplot.set_ylim((ylo, yhi))
[a07e72f]485        else:
486            self.plots[data.id] = data
[090e07e]487            self.graph.add(self.plots[data.id])
[6c4130a]488            data.zorder = len(plot_keys)
[e4a703a]489            ## Set the view scale for all plots
[a4964b8e]490            try:
491                self._onEVT_FUNC_PROPERTY()
[940aca7]492                if IS_MAC:
493                    # MAC: forcing to plot 2D avg
494                    self.canvas._onDrawIdle()
[090e07e]495            except Exception, exc:
[4904062]496                wx.PostEvent(self.parent, StatusEvent(status=\
[090e07e]497                    "Plotting Error: %s" % str(exc), info="error"))
[fe48fcc]498            self.toolbar.update()
[cafa75f]499            self.is_zoomed = False
[090e07e]500
[6d727ae]501    def draw_plot(self):
502        """
503        Draw plot
504        """
[090e07e]505        self.draw()
[6d727ae]506
[090e07e]507    def onLeftDown(self, event):
508        """
[d955bf19]509        left button down and ready to drag
510        Display the position of the mouse on the statusbar
[6c0568b]511        """
[090e07e]512        # self.parent.set_plot_unfocus()
[3e001f9]513        self._get_cusor_lines(event)
[e85e2bc]514        ax = event.inaxes
[3e001f9]515        PlotPanel.onLeftDown(self, event)
[e85e2bc]516        if ax != None:
[902f373]517            try:
[090e07e]518                pos_x = float(event.xdata)  # / size_x
519                pos_y = float(event.ydata)  # / size_y
520                pos_x = "%8.3g" % pos_x
521                pos_y = "%8.3g" % pos_y
[902f373]522                self.position = str(pos_x), str(pos_y)
523                wx.PostEvent(self.parent, StatusEvent(status=self.position))
524            except:
[090e07e]525                self.position = None
[0275276]526        # unfocus all
[090e07e]527        self.parent.set_plot_unfocus()
528        # post nd event to notify guiframe that this panel is on focus
[a45037aa]529        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
[df68da1]530
[090e07e]531
[a07e72f]532    def _ontoggle_hide_error(self, event):
533        """
534        Toggle error display to hide or show
535        """
[70ec588d]536        menu = event.GetEventObject()
[090e07e]537        event_id = event.GetId()
538        self.set_selected_from_menu(menu, event_id)
[ebf422a]539        # Check zoom
540        xlo, xhi = self.subplot.get_xlim()
541        ylo, yhi = self.subplot.get_ylim()
542
[a07e72f]543        selected_plot = self.plots[self.graph.selected_plottable]
[5637362]544        if self.hide_menu.GetText() == "Hide Error Bar":
[a07e72f]545            selected_plot.hide_error = True
546        else:
547            selected_plot.hide_error = False
548        ## increment graph color
549        self.graph.render(self)
[090e07e]550        self.subplot.figure.canvas.draw_idle()
[cafa75f]551        if self.is_zoomed:
[ebf422a]552            # Recover the x,y limits
[090e07e]553            self.subplot.set_xlim((xlo, xhi))
554            self.subplot.set_ylim((ylo, yhi))
[2eb3d18c]555        self.graph.selected_plottable = None
[090e07e]556
[ebf422a]557
[1bf33c1]558    def _onRemove(self, event):
559        """
[090e07e]560        Remove a plottable from the graph and render the graph
561
[d955bf19]562        :param event: Menu event
[090e07e]563
[1bf33c1]564        """
[70ec588d]565        menu = event.GetEventObject()
[090e07e]566        event_id = event.GetId()
567        self.set_selected_from_menu(menu, event_id)
[6c0568b]568        ## Check if there is a selected graph to remove
[a07e72f]569        if self.graph.selected_plottable in self.plots.keys():
[090e07e]570            graph_id = self.graph.selected_plottable
571            self.remove_data_by_id(graph_id)
572
[1bf33c1]573    def onContextMenu(self, event):
574        """
[d955bf19]575        1D plot context menu
[090e07e]576
[d955bf19]577        :param event: wx context event
[090e07e]578
[1bf33c1]579        """
[52b8b74]580        self._slicerpop = PanelMenu()
581        self._slicerpop.set_plots(self.plots)
[090e07e]582        self._slicerpop.set_graph(self.graph)
[6f16e25]583        ids = iter(self._menu_ids)
[090e07e]584
[efb482a]585        # Various plot options
586        wx_id = ids.next()
587        self._slicerpop.Append(wx_id, '&Save Image', 'Save image as PNG')
588        wx.EVT_MENU(self, wx_id, self.onSaveImage)
589        wx_id = ids.next()
590        self._slicerpop.Append(wx_id, '&Print Image', 'Print image ')
591        wx.EVT_MENU(self, wx_id, self.onPrint)
[090e07e]592
[efb482a]593        wx_id = ids.next()
594        self._slicerpop.Append(wx_id, '&Copy to Clipboard',
595                               'Copy to the clipboard')
596        wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu)
597
598        self._slicerpop.AppendSeparator()
[70ec588d]599
600        for plot in self.plots.values():
[090e07e]601            # title = plot.title
[6d727ae]602            name = plot.name
[70ec588d]603            plot_menu = wx.Menu()
[242aff5]604            if self.graph.selected_plottable:
[4a9bce1]605                if not self.graph.selected_plottable in self.plots.keys():
606                    continue
[242aff5]607                if plot != self.plots[self.graph.selected_plottable]:
608                    continue
[090e07e]609
[6f16e25]610            wx_id = ids.next()
[090e07e]611            plot_menu.Append(wx_id, "&DataInfo", name)
612            wx.EVT_MENU(self, wx_id, self. _onDataShow)
[6f16e25]613            wx_id = ids.next()
[090e07e]614            plot_menu.Append(wx_id, "&Save Points as a File", name)
615            wx.EVT_MENU(self, wx_id, self._onSave)
[940aca7]616            plot_menu.AppendSeparator()
[090e07e]617
618            # add menu of other plugins
[940aca7]619            item_list = self.parent.get_current_context_menu(self)
[70ec588d]620            if (not item_list == None) and (not len(item_list) == 0):
[2204dc5]621                for item, wx_id in zip(item_list, [ids.next() for i in range(len(item_list))]):
[242aff5]622
[70ec588d]623                    try:
[090e07e]624                        plot_menu.Append(wx_id, item[0], name)
625                        wx.EVT_MENU(self, wx_id, item[2])
[70ec588d]626                    except:
627                        msg = "ModelPanel1D.onContextMenu: "
628                        msg += "bad menu item  %s" % sys.exc_value
629                        wx.PostEvent(self.parent, StatusEvent(status=msg))
630                plot_menu.AppendSeparator()
[090e07e]631
632            if self.parent.ClassName.count('wxDialog') == 0:
[2f12acc]633                if plot.id != 'fit':
634                    wx_id = ids.next()
635                    plot_menu.Append(wx_id, '&Linear Fit', name)
636                    wx.EVT_MENU(self, wx_id, self.onFitting)
637                    plot_menu.AppendSeparator()
[090e07e]638
[6f16e25]639                wx_id = ids.next()
[090e07e]640                plot_menu.Append(wx_id, "Remove", name)
641                wx.EVT_MENU(self, wx_id, self._onRemove)
[a436b2e]642                if not plot.is_data:
[6f16e25]643                    wx_id = ids.next()
[090e07e]644                    plot_menu.Append(wx_id, '&Freeze', name)
645                    wx.EVT_MENU(self, wx_id, self.onFreeze)
646                plot_menu.AppendSeparator()
647
[70ec588d]648                if plot.is_data:
[6f16e25]649                    wx_id = ids.next()
[090e07e]650                    self.hide_menu = plot_menu.Append(wx_id, "Hide Error Bar", name)
651
[70ec588d]652                    if plot.dy is not None and plot.dy != []:
[090e07e]653                        if plot.hide_error:
[70ec588d]654                            self.hide_menu.SetText('Show Error Bar')
655                        else:
656                            self.hide_menu.SetText('Hide Error Bar')
[a436b2e]657                    else:
[70ec588d]658                        self.hide_menu.Enable(False)
[090e07e]659                    wx.EVT_MENU(self, wx_id, self._ontoggle_hide_error)
[f8be87d]660
[090e07e]661                    plot_menu.AppendSeparator()
[f8be87d]662
[6f16e25]663                wx_id = ids.next()
[090e07e]664                plot_menu.Append(wx_id, '&Modify Plot Property', name)
665                wx.EVT_MENU(self, wx_id, self.createAppDialog)
[6f16e25]666            wx_id = ids.next()
[090e07e]667            # plot_menu.SetTitle(name)
668            self._slicerpop.AppendMenu(wx_id, '&%s' % name, plot_menu)
[f866fb5]669            # Option to hide
[090e07e]670            # TODO: implement functionality to hide a plottable (legend click)
671
[efb482a]672        self._slicerpop.AppendSeparator()
673        loc_menu = wx.Menu()
674        for label in self._loc_labels:
[6f16e25]675            wx_id = ids.next()
[efb482a]676            loc_menu.Append(wx_id, str(label), str(label))
677            wx.EVT_MENU(self, wx_id, self.onChangeLegendLoc)
[8f59e95]678
[efb482a]679        wx_id = ids.next()
680        self._slicerpop.Append(wx_id, '&Modify Graph Appearance',
681                               'Modify graph appearance')
682        wx.EVT_MENU(self, wx_id, self.modifyGraphAppearance)
683        self._slicerpop.AppendSeparator()
[090e07e]684
[efb482a]685
686        if self.position != None:
687            wx_id = ids.next()
688            self._slicerpop.Append(wx_id, '&Add Text')
689            wx.EVT_MENU(self, wx_id, self._on_addtext)
[6f16e25]690            wx_id = ids.next()
[efb482a]691            self._slicerpop.Append(wx_id, '&Remove Text')
692            wx.EVT_MENU(self, wx_id, self._on_removetext)
693            self._slicerpop.AppendSeparator()
694        wx_id = ids.next()
695        self._slicerpop.Append(wx_id, '&Change Scale')
696        wx.EVT_MENU(self, wx_id, self._onProperties)
697        self._slicerpop.AppendSeparator()
698        wx_id = ids.next()
[895c9cb]699        self._slicerpop.Append(wx_id, '&Set Graph Range')
700        wx.EVT_MENU(self, wx_id, self.onSetRange)
701        wx_id = ids.next()
[efb482a]702        self._slicerpop.Append(wx_id, '&Reset Graph Range')
703        wx.EVT_MENU(self, wx_id, self.onResetGraph)
704
705        if self.parent.ClassName.count('wxDialog') == 0:
[857d00f]706            self._slicerpop.AppendSeparator()
[6f16e25]707            wx_id = ids.next()
[efb482a]708            self._slicerpop.Append(wx_id, '&Window Title')
709            wx.EVT_MENU(self, wx_id, self.onChangeCaption)
[dc51a7f]710        try:
711            pos_evt = event.GetPosition()
712            pos = self.ScreenToClient(pos_evt)
713        except:
714            pos_x, pos_y = self.toolbar.GetPositionTuple()
715            pos = (pos_x, pos_y + 5)
[52b8b74]716        self.PopupMenu(self._slicerpop, pos)
[090e07e]717
[895c9cb]718    def onSetRange(self, event):
719        # Display dialog
720        # self.subplot.set_xlim((low, high))
721        # self.subplot.set_ylim((low, high))
722        from sas.sasgui.plottools.RangeDialog import RangeDialog
723        d = RangeDialog(self, -1)
724        xlim = self.subplot.get_xlim()
725        ylim = self.subplot.get_ylim()
726        d.SetXRange(xlim)
727        d.SetYRange(ylim)
728        if d.ShowModal() == wx.ID_OK:
729            x_range = d.GetXRange()
730            y_range = d.GetYRange()
731            if x_range is not None and y_range is not None:
732                self.subplot.set_xlim(x_range)
733                self.subplot.set_ylim(y_range)
734                self.subplot.figure.canvas.draw_idle()
[29e872e]735                self.is_zoomed = True
[895c9cb]736        d.Destroy()
737
[e6a93df]738    def onFreeze(self, event):
739        """
[f866fb5]740        on Freeze data
[e6a93df]741        """
[70ec588d]742        menu = event.GetEventObject()
[090e07e]743        wx_id = event.GetId()
744        self.set_selected_from_menu(menu, wx_id)
[e6a93df]745        plot = self.plots[self.graph.selected_plottable]
746        self.parent.onfreeze([plot.id])
[090e07e]747
[1bf33c1]748    def _onSave(self, evt):
749        """
[d955bf19]750        Save a data set to a text file
[090e07e]751
[d955bf19]752        :param evt: Menu event
[090e07e]753
[1bf33c1]754        """
[70ec588d]755        menu = evt.GetEventObject()
[090e07e]756        event_id = evt.GetId()
757        self.set_selected_from_menu(menu, event_id)
[176fbf1]758        data = self.plots[self.graph.selected_plottable]
759        default_name = data.label
[c553b18]760        if default_name.count('.') > 0:
761            default_name = default_name.split('.')[0]
762        default_name += "_out"
763        if self.parent != None:
[176fbf1]764            self.parent.save_data1d(data, default_name)
[c5a769e]765
[0aca693]766    def _onDataShow(self, evt):
767        """
768        Show the data set in text
[090e07e]769
[0aca693]770        :param evt: Menu event
[090e07e]771
[0aca693]772        """
773        menu = evt.GetEventObject()
[090e07e]774        event_id = evt.GetId()
775        self.set_selected_from_menu(menu, event_id)
[0aca693]776        data = self.plots[self.graph.selected_plottable]
777        default_name = data.label
778        if default_name.count('.') > 0:
779            default_name = default_name.split('.')[0]
[090e07e]780        # default_name += "_out"
[0aca693]781        if self.parent != None:
782            self.parent.show_data1d(data, default_name)
[090e07e]783
[6e75ed0]784    def _on_hide(self, event):
785        """
786        Hides the plot when button is pressed
[090e07e]787        """
[c5a769e]788        if self.parent is not None:
[6e75ed0]789            self.parent.hide_panel(self.uid)
[f8be87d]790
[ae84427]791    def on_close(self, event):
792        """
793        On Close Event
794        """
795        ID = self.uid
796        self.parent.delete_panel(ID)
[090e07e]797
[f8be87d]798    def createAppDialog(self, event):
799        """
800        Create the custom dialog for fit appearance modification
801        """
802        menu = event.GetEventObject()
[090e07e]803        event_id = event.GetId()
804        self.set_selected_from_menu(menu, event_id)
[f866fb5]805        self.appearance_selected_plot = \
806                        self.plots[self.graph.selected_plottable]
[f8be87d]807        # find current properties
808        curr_color = self.appearance_selected_plot.custom_color
809        curr_symbol = self.appearance_selected_plot.symbol
810        curr_size = self.appearance_selected_plot.markersize
811        curr_label = self.appearance_selected_plot.label
812
813        if curr_color == None:
814            curr_color = self._color_labels['Blue']
815            curr_symbol = 13
816
[9f51c2c]817        self.appD = appearanceDialog(self, 'Modify Plot Property')
818        icon = self.parent.GetIcon()
819        self.appD.SetIcon(icon)
[090e07e]820        self.appD.set_defaults(float(curr_size), int(curr_color),
821                               str(appearanceDialog.find_key(self.get_symbol_label(),
822                                                             int(curr_symbol))), curr_label)
823        self.appD.Bind(wx.EVT_CLOSE, self.on_AppDialog_close)
[e63cf11]824        self.graph.selected_plottable = None
[9f51c2c]825
[f866fb5]826    def on_AppDialog_close(self, event):
827        """
828        on_Modify Plot Property_close
829        """
[090e07e]830        if self.appD.okay_clicked == True:
831            info = self.appD.get_current_values()
[f866fb5]832            self.appearance_selected_plot.custom_color = \
833                        self._color_labels[info[1].encode('ascii', 'ignore')]
[9f51c2c]834
835            self.appearance_selected_plot.markersize = float(info[0])
[f866fb5]836            self.appearance_selected_plot.symbol = \
[090e07e]837                        self.get_symbol_label()[info[2]]
[9f51c2c]838            self.appearance_selected_plot.label = str(info[3])
[f8be87d]839        self.appD.Destroy()
840        self._check_zoom_plot()
[8f59e95]841
[f866fb5]842    def modifyGraphAppearance(self, event):
843        """
[090e07e]844        On Modify Graph Appearance
[f866fb5]845        """
846        self.graphApp = graphAppearance(self, 'Modify Graph Appearance')
[9f51c2c]847        icon = self.parent.GetIcon()
848        self.graphApp.SetIcon(icon)
[090e07e]849        self.graphApp.setDefaults(self.grid_on, self.legend_on,
850                                  self.xaxis_label, self.yaxis_label,
851                                  self.xaxis_unit, self.yaxis_unit,
852                                  self.xaxis_font, self.yaxis_font,
853                                  find_key(self.get_loc_label(), self.legendLoc),
854                                  self.xcolor, self.ycolor,
[657e52c]855                                  self.is_xtick, self.is_ytick)
[9f51c2c]856        self.graphApp.Bind(wx.EVT_CLOSE, self.on_graphApp_close)
[8f59e95]857
[9f51c2c]858    def on_graphApp_close(self, event):
[83d9120]859        """
860        Gets values from graph appearance dialog and sends them off
861        to modify the plot
862        """
863        graph_app = self.graphApp
864        toggle_grid = graph_app.get_togglegrid()
865        legend_loc = graph_app.get_legend_loc()
866        toggle_legend = graph_app.get_togglelegend()
[090e07e]867
868        self.onGridOnOff(toggle_grid)
[83d9120]869        self.ChangeLegendLoc(legend_loc)
870        self.onLegend(toggle_legend)
[8a687cfd]871
[83d9120]872        self.xaxis_label = graph_app.get_xlab()
873        self.yaxis_label = graph_app.get_ylab()
874        self.xaxis_unit = graph_app.get_xunit()
875        self.yaxis_unit = graph_app.get_yunit()
[657e52c]876        self.xaxis_font = graph_app.get_xfont()
877        self.yaxis_font = graph_app.get_yfont()
[090e07e]878        self.is_xtick = graph_app.get_xtick_check()
879        self.is_ytick = graph_app.get_ytick_check()
[657e52c]880        if self.is_xtick:
881            self.xaxis_tick = self.xaxis_font
882        if self.is_ytick:
883            self.yaxis_tick = self.yaxis_font
[8a687cfd]884
[090e07e]885        self.xaxis(self.xaxis_label, self.xaxis_unit,
886                   graph_app.get_xfont(), graph_app.get_xcolor(),
[657e52c]887                   self.xaxis_tick)
[090e07e]888        self.yaxis(self.yaxis_label, self.yaxis_unit,
[657e52c]889                   graph_app.get_yfont(), graph_app.get_ycolor(),
890                   self.yaxis_tick)
[8f59e95]891
[83d9120]892        graph_app.Destroy()
Note: See TracBrowser for help on using the repository browser.