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

Last change on this file since 6d7b252b was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

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