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

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since acefa2b was 245ae18, checked in by lewis, 8 years ago

Don't log coords on corfunc QRange plot, only on drag

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