source: sasview/src/sas/sasgui/perspectives/calculator/data_operator.py @ 1309205b

Last change on this file since 1309205b was fa81e94, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Initial commit of the P(r) inversion perspective.
Code merged from Jeff Krzywon's ESS_GUI_Pr branch.
Also, minor 2to3 mods to sascalc/sasgui to enble error free setup.

  • Property mode set to 100755
File size: 34.9 KB
Line 
1"""
2GUI for the data operations panel (sum and multiply)
3"""
4import wx
5import sys
6import time
7import numpy as np
8from sas.sascalc.dataloader.data_info import Data1D
9from sas.sasgui.plottools.PlotPanel import PlotPanel
10from sas.sasgui.plottools.plottables import Graph
11from sas.sasgui.plottools import transform
12from matplotlib.font_manager import FontProperties
13from sas.sasgui.guiframe.events import StatusEvent
14from sas.sasgui.perspectives.calculator import calculator_widgets as widget
15from sas.sasgui.guiframe.documentation_window import DocumentationWindow
16
17#Control panel width
18if sys.platform.count("win32") > 0:
19    PANEL_TOP = 0
20    PANEL_WIDTH = 790
21    PANEL_HEIGTH = 370
22    FONT_VARIANT = 0
23    _BOX_WIDTH = 200
24    ON_MAC = False
25else:
26    PANEL_TOP = 60
27    _BOX_WIDTH = 230
28    PANEL_WIDTH = 900
29    PANEL_HEIGTH = 430
30    FONT_VARIANT = 1
31    ON_MAC = True
32
33class DataOperPanel(wx.ScrolledWindow):
34    """
35    """
36    def __init__(self, parent, *args, **kwds):
37        kwds['name'] = "Data Operation"
38        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGTH)
39        wx.ScrolledWindow.__init__(self, parent, *args, **kwds)
40        self.parent = parent
41        #sizers etc.
42        self.main_sizer = None
43        self.name_sizer = None
44        self.button_sizer = None
45        self.data_namectr = None
46        self.numberctr = None
47        self.data1_cbox = None
48        self.operator_cbox = None
49        self.data2_cbox = None
50        self.data_title_tcl = None
51        self.out_pic = None
52        self.equal_pic = None
53        self.data1_pic = None
54        self.operator_pic = None
55        self.data2_pic = None
56        self.output = None
57        self._notes = None
58        #text grayed color
59        self.color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
60        #data
61        self._data = self.get_datalist()
62        self._do_layout()
63        self.fill_data_combox()
64        self.fill_oprator_combox()
65        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_on_focus)
66
67    def _define_structure(self):
68        """
69        define initial sizer
70        """
71        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
72        title = "Data Operation "
73        title += "[ + (add); - (subtract); "
74        title += "* (multiply); / (divide); "
75        title += "| (append) ]"
76        name_box = wx.StaticBox(self, -1, title)
77        self.name_sizer = wx.StaticBoxSizer(name_box, wx.HORIZONTAL)
78        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
79
80    def _layout_name(self):
81        """
82        Do the layout for data name related widgets
83        """
84        new_data_sizer = wx.BoxSizer(wx.VERTICAL)
85        equal_sizer = wx.BoxSizer(wx.VERTICAL)
86        old_data1_sizer = wx.BoxSizer(wx.VERTICAL)
87        operator_sizer = wx.BoxSizer(wx.VERTICAL)
88        old_data2_sizer = wx.BoxSizer(wx.VERTICAL)
89        data2_hori_sizer = wx.BoxSizer(wx.HORIZONTAL)
90        data_name = wx.StaticText(self, -1, 'Output Data Name')
91        equal_name = wx.StaticText(self, -1, ' =', size=(50, 25))
92        data1_name = wx.StaticText(self, -1, 'Data1')
93        operator_name = wx.StaticText(self, -1, 'Operator')
94        data2_name = wx.StaticText(self, -1, 'Data2 (or Number)')
95        self.data_namectr = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 25), style=wx.TE_PROCESS_ENTER)
96        self.data_namectr.SetToolTipString("Hit 'Enter' key after typing.")
97        self.data_namectr.SetValue(str('MyNewDataName'))
98        self.numberctr = wx.TextCtrl(self, -1, size=(_BOX_WIDTH / 3, 25), style=wx.TE_PROCESS_ENTER)
99        self.numberctr.SetToolTipString("Hit 'Enter' key after typing.")
100        self.numberctr.SetValue(str(1.0))
101        self.data1_cbox = wx.ComboBox(self, -1, size=(_BOX_WIDTH, 25),
102                                      style=wx.CB_READONLY)
103        self.operator_cbox = wx.ComboBox(self, -1, size=(70, 25),
104                                         style=wx.CB_READONLY)
105        operation_tip = "Add: +, Subtract: -, "
106        operation_tip += "Multiply: *, Divide: /, "
107        operation_tip += "Append(Combine): | "
108        self.operator_cbox.SetToolTipString(operation_tip)
109        self.data2_cbox = wx.ComboBox(self, -1, size=(_BOX_WIDTH * 2 / 3, 25),
110                                       style=wx.CB_READONLY)
111
112        self.out_pic = SmallPanel(self, -1, True,
113                                    size=(_BOX_WIDTH, _BOX_WIDTH),
114                                    style=wx.NO_BORDER)
115        self.equal_pic = SmallPanel(self, -1, True, '=',
116                                    size=(50, _BOX_WIDTH),
117                                    style=wx.NO_BORDER)
118        self.data1_pic = SmallPanel(self, -1, True,
119                                    size=(_BOX_WIDTH, _BOX_WIDTH),
120                                    style=wx.NO_BORDER)
121        self.operator_pic = SmallPanel(self, -1, True, '+',
122                                    size=(70, _BOX_WIDTH),
123                                    style=wx.NO_BORDER)
124        self.data2_pic = SmallPanel(self, -1, True,
125                                    size=(_BOX_WIDTH, _BOX_WIDTH),
126                                    style=wx.NO_BORDER)
127        for ax in self.equal_pic.axes:
128            ax.set_frame_on(False)
129        for ax in self.operator_pic.axes:
130            ax.set_frame_on(False)
131
132        new_data_sizer.AddMany([(data_name, 0, wx.LEFT, 3),
133                                       (self.data_namectr, 0, wx.LEFT, 3),
134                                       (self.out_pic, 0, wx.LEFT, 3)])
135        equal_sizer.AddMany([(13, 13), (equal_name, 0, wx.LEFT, 3),
136                                       (self.equal_pic, 0, wx.LEFT, 3)])
137        old_data1_sizer.AddMany([(data1_name, 0, wx.LEFT, 3),
138                                       (self.data1_cbox, 0, wx.LEFT, 3),
139                                       (self.data1_pic, 0, wx.LEFT, 3)])
140        operator_sizer.AddMany([(operator_name, 0, wx.LEFT, 3),
141                                 (self.operator_cbox, 0, wx.LEFT, 3),
142                                 (self.operator_pic, 0, wx.LEFT, 3)])
143        data2_hori_sizer.AddMany([(self.data2_cbox, 0, wx.LEFT, 0),
144                                       (self.numberctr, 0, wx.RIGHT, 0)])
145        old_data2_sizer.AddMany([(data2_name, 0, wx.LEFT, 3),
146                                       (data2_hori_sizer, 0, wx.LEFT, 3),
147                                       (self.data2_pic, 0, wx.LEFT, 3)])
148        self.name_sizer.AddMany([(new_data_sizer, 0, wx.LEFT | wx.TOP, 5),
149                                       (equal_sizer, 0, wx.TOP, 5),
150                                       (old_data1_sizer, 0, wx.TOP, 5),
151                                       (operator_sizer, 0, wx.TOP, 5),
152                                       (old_data2_sizer, 0, wx.TOP, 5)])
153        self.data2_cbox.Show(True)
154
155        self._show_numctrl(self.numberctr, False)
156
157        wx.EVT_TEXT_ENTER(self.data_namectr, -1, self.on_name)
158        wx.EVT_TEXT(self.numberctr, -1, self.on_number)
159        wx.EVT_COMBOBOX(self.data1_cbox, -1, self.on_select_data1)
160        wx.EVT_COMBOBOX(self.operator_cbox, -1, self.on_select_operator)
161        wx.EVT_COMBOBOX(self.data2_cbox, -1, self.on_select_data2)
162
163    def _show_numctrl(self, ctrl, enable=True):
164        """
165        Show/Hide on Win
166        Enable/Disable on MAC
167        """
168        if ON_MAC:
169            ctrl.Enable(enable)
170            children = ctrl.GetChildren()
171            if len(children) > 0:
172                ctrl.GetChildren()[0].SetBackGroundColour(self.color)
173            if enable:
174                wx.EVT_TEXT_ENTER(self.numberctr, -1, self.on_number)
175        else:
176            if not ctrl.IsEnabled():
177                ctrl.Enable(True)
178            ctrl.Show(enable)
179
180    def on_name(self, event=None):
181        """
182        On data name typing
183        """
184        if event is not None:
185            event.Skip()
186        item = event.GetEventObject()
187        if item.IsEnabled():
188            self._set_textctrl_color(item, 'white')
189        else:
190            self._set_textctrl_color(item, self.color)
191        text = item.GetValue().strip()
192        self._check_newname(text)
193
194    def _check_newname(self, name=None):
195        """
196        Check name ctr strings
197        """
198        self.send_warnings('')
199        msg = ''
200        if name is None:
201            text = self.data_namectr.GetValue().strip()
202        else:
203            text = name
204        state_list = list(self.get_datalist().values())
205        name_list = []
206        for state in state_list:
207            if state.data is None:
208                theory_list = state.get_theory()
209                theory, _ = list(theory_list.values())[0]
210                d_name = str(theory.name)
211            else:
212                d_name = str(state.data.name)
213            name_list.append(d_name)
214        if text in name_list:
215            self._set_textctrl_color(self.data_namectr, 'pink')
216            msg = "DataOperation: The name already exists."
217        if len(text) == 0:
218            self._set_textctrl_color(self.data_namectr, 'pink')
219            msg = "DataOperation: Type the data name first."
220        if self._notes:
221            self.send_warnings(msg, 'error')
222        self.name_sizer.Layout()
223        self.Refresh()
224
225    def _set_textctrl_color(self, ctrl, color):
226        """
227        Set TextCtrl color
228        """
229        if ON_MAC:
230            children = ctrl.GetChildren()
231            if len(children) > 0:
232                children[0].SetBackgroundColour(color)
233        else:
234            ctrl.SetBackgroundColour(color)
235        self.name_sizer.Layout()
236
237    def on_number(self, event=None, control=None):
238        """
239        On selecting Number for Data2
240        """
241        self.send_warnings('')
242        item = control
243        if item is None and event is not None:
244            item = event.GetEventObject()
245        elif item is None:
246            raise ValueError("Event or control must be supplied")
247        text = item.GetValue().strip()
248        if self.numberctr.IsShown():
249            if self.numberctr.IsEnabled():
250                self._set_textctrl_color(self.numberctr, 'white')
251                try:
252                    val = float(text)
253                    pos = self.data2_cbox.GetCurrentSelection()
254                    self.data2_cbox.SetClientData(pos, val)
255                except:
256                    self._set_textctrl_color(self.numberctr, 'pink')
257                    if event is None:
258                        msg = "DataOperation: Number requires a float number."
259                        self.send_warnings(msg, 'error')
260                    return False
261            else:
262                self._set_textctrl_color(self.numberctr, self.color)
263
264        self.put_text_pic(self.data2_pic, content=str(val))
265        self.check_data_inputs()
266        if self.output is not None:
267            self.output.name = str(self.data_namectr.GetValue())
268        self.draw_output(self.output)
269        self.Refresh()
270        return True
271
272    def on_select_data1(self, event=None):
273        """
274        On select data1
275        """
276        self.send_warnings('')
277        item = event.GetEventObject()
278        pos = item.GetCurrentSelection()
279        data = item.GetClientData(pos)
280        if data is None:
281            content = "?"
282            self.put_text_pic(self.data1_pic, content)
283        else:
284            self.data1_pic.add_image(data)
285        self.check_data_inputs()
286        if self.output is not None:
287            self.output.name = str(self.data_namectr.GetValue())
288        self.draw_output(self.output)
289
290    def on_select_operator(self, event=None):
291        """
292        On Select an Operator
293        """
294        self.send_warnings('')
295        item = event.GetEventObject()
296        text = item.GetValue().strip()
297        self.put_text_pic(self.operator_pic, content=text)
298        self.check_data_inputs()
299        if self.output is not None:
300            self.output.name = str(self.data_namectr.GetValue())
301        self.draw_output(self.output)
302
303    def on_select_data2(self, event=None):
304        """
305        On Selecting Data2
306        """
307        self.send_warnings('')
308        item = event.GetEventObject()
309        text = item.GetValue().strip().lower()
310        self._show_numctrl(self.numberctr, text == 'number')
311        pos = item.GetCurrentSelection()
312        data = item.GetClientData(pos)
313        content = "?"
314        if not (self.numberctr.IsShown() and self.numberctr.IsEnabled()):
315            if data is None:
316                content = "?"
317                self.put_text_pic(self.data2_pic, content)
318            else:
319                self.data2_pic.add_image(data)
320            self.check_data_inputs()
321        else:
322            content = str(self.numberctr.GetValue().strip())
323            try:
324                content = float(content)
325                data = content
326            except:
327                self._set_textctrl_color(self.numberctr, 'pink')
328                content = "?"
329                data = None
330            item.SetClientData(pos, data)
331            if data is not None:
332                self.check_data_inputs()
333
334            self.put_text_pic(self.data2_pic, content)
335
336        if self.output is not None:
337            self.output.name = str(self.data_namectr.GetValue())
338        self.draw_output(self.output)
339
340    def put_text_pic(self, pic=None, content=''):
341        """
342        Put text to the pic
343        """
344        pic.set_content(content)
345        pic.add_text()
346        pic.draw()
347
348    def check_data_inputs(self):
349        """
350        Check data1 and data2 whether or not they are ready for operation
351        """
352        self._set_textctrl_color(self.data1_cbox, 'white')
353        self._set_textctrl_color(self.data2_cbox, 'white')
354        flag = False
355        pos1 = self.data1_cbox.GetCurrentSelection()
356        data1 = self.data1_cbox.GetClientData(pos1)
357        if data1 is None:
358            self.output = None
359            return flag
360        pos2 = self.data2_cbox.GetCurrentSelection()
361        data2 = self.data2_cbox.GetClientData(pos2)
362
363        if data2 is None:
364            self.output = None
365            return flag
366        if self.numberctr.IsShown():
367            if self.numberctr.IsEnabled():
368                self._set_textctrl_color(self.numberctr, 'white')
369                try:
370                    float(data2)
371                    if self.operator_cbox.GetValue().strip() == '|':
372                        msg = "DataOperation: This operation can not accept "
373                        msg += "a float number."
374                        self.send_warnings(msg, 'error')
375                        self._set_textctrl_color(self.numberctr, 'pink')
376                        self.output = None
377                        return flag
378                except:
379                    msg = "DataOperation: Number requires a float number."
380                    self.send_warnings(msg, 'error')
381                    self._set_textctrl_color(self.numberctr, 'pink')
382                    self.output = None
383                    return flag
384            else:
385                self._set_textctrl_color(self.numberctr, self.color)
386        elif data1.__class__.__name__ != data2.__class__.__name__:
387            self._set_textctrl_color(self.data1_cbox, 'pink')
388            self._set_textctrl_color(self.data2_cbox, 'pink')
389            msg = "DataOperation: Data types must be same."
390            self.send_warnings(msg, 'error')
391            self.output = None
392            return flag
393        try:
394            self.output = self.make_data_out(data1, data2)
395        except:
396            self._check_newname()
397            self._set_textctrl_color(self.data1_cbox, 'pink')
398            self._set_textctrl_color(self.data2_cbox, 'pink')
399            msg = "DataOperation: %s" % sys.exc_info()[1]
400            self.send_warnings(msg, 'error')
401            self.output = None
402            return flag
403        return True
404
405    def make_data_out(self, data1, data2):
406        """
407        Make a temp. data output set
408        """
409        output = None
410        pos = self.operator_cbox.GetCurrentSelection()
411        operator = self.operator_cbox.GetClientData(pos)
412        try:
413            exec("output = data1 %s data2" % operator)
414        except:
415            raise
416        return output
417
418
419    def draw_output(self, output):
420        """
421        Draw output data(temp)
422        """
423        out = self.out_pic
424        if output is None:
425            content = "?"
426            self.put_text_pic(out, content)
427        else:
428            out.add_image(output)
429        wx.CallAfter(self.name_sizer.Layout)
430        self.Layout()
431        self.Refresh()
432
433    def _layout_button(self):
434        """
435            Do the layout for the button widgets
436        """
437        self.bt_apply = wx.Button(self, -1, "Apply", size=(_BOX_WIDTH / 2, -1))
438        app_tip = "Generate the Data and send to Data Explorer."
439        self.bt_apply.SetToolTipString(app_tip)
440        self.bt_apply.Bind(wx.EVT_BUTTON, self.on_click_apply)
441
442        self.bt_help = wx.Button(self, -1, "HELP")
443        app_tip = "Get help on Data Operations."
444        self.bt_help.SetToolTipString(app_tip)
445        self.bt_help.Bind(wx.EVT_BUTTON, self.on_help)
446
447        self.bt_close = wx.Button(self, -1, 'Close', size=(_BOX_WIDTH / 2, -1))
448        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
449        self.bt_close.SetToolTipString("Close this panel.")
450
451        self.button_sizer.AddMany([(PANEL_WIDTH / 2, 25),
452                                   (self.bt_apply, 0, wx.RIGHT, 10),
453                                   (self.bt_help, 0, wx.RIGHT, 10),
454                                   (self.bt_close, 0, wx.RIGHT, 10)])
455
456    def _do_layout(self):
457        """
458        Draw the current panel
459        """
460        self._define_structure()
461        self._layout_name()
462        self._layout_button()
463        self.main_sizer.AddMany([(self.name_sizer, 0, wx.EXPAND | wx.ALL, 10),
464                                (self.button_sizer, 0,
465                                          wx.EXPAND | wx.TOP | wx.BOTTOM, 5)])
466        self.SetSizer(self.main_sizer)
467        self.SetScrollbars(20, 20, 25, 65)
468        self.SetAutoLayout(True)
469
470    def set_panel_on_focus(self, event):
471        """
472        On Focus at this window
473        """
474        if event is not None:
475            event.Skip()
476        self._data = self.get_datalist()
477        if ON_MAC:
478            self.fill_data_combox()
479        else:
480            children = self.GetChildren()
481            # update the list only when it is on the top
482            if self.FindFocus() in children:
483                self.fill_data_combox()
484
485    def fill_oprator_combox(self):
486        """
487        fill the current combobox with the operator
488        """
489        operator_list = [' +', ' -', ' *', " /", " |"]
490        for oper in operator_list:
491            pos = self.operator_cbox.Append(str(oper))
492            self.operator_cbox.SetClientData(pos, str(oper.strip()))
493        self.operator_cbox.SetSelection(0)
494
495
496    def fill_data_combox(self):
497        """
498        fill the current combobox with the available data
499        """
500        pos_pre1 = self.data1_cbox.GetCurrentSelection()
501        pos_pre2 = self.data2_cbox.GetCurrentSelection()
502        current1 = self.data1_cbox.GetLabel()
503        current2 = self.data2_cbox.GetLabel()
504        if pos_pre1 < 0:
505            pos_pre1 = 0
506        if pos_pre2 < 0:
507            pos_pre2 = 0
508        self.data1_cbox.Clear()
509        self.data2_cbox.Clear()
510
511        if not self._data:
512            pos = self.data1_cbox.Append('No Data Available')
513            self.data1_cbox.SetSelection(pos)
514            self.data1_cbox.SetClientData(pos, None)
515            pos2 = self.data2_cbox.Append('No Data Available')
516            self.data2_cbox.SetSelection(pos2)
517            self.data2_cbox.SetClientData(pos2, None)
518            return
519        pos1 = self.data1_cbox.Append('Select Data')
520        self.data1_cbox.SetSelection(pos1)
521        self.data1_cbox.SetClientData(pos1, None)
522        pos2 = self.data2_cbox.Append('Select Data')
523        self.data2_cbox.SetSelection(pos2)
524        self.data2_cbox.SetClientData(pos2, None)
525        pos3 = self.data2_cbox.Append('Number')
526        val = None
527        if (self.numberctr.IsShown() and self.numberctr.IsEnabled()):
528            try:
529                val = float(self.numberctr.GetValue())
530            except:
531                val = None
532        self.data2_cbox.SetClientData(pos3, val)
533        dnames = []
534        ids = list(self._data.keys())
535        for id in ids:
536            if id is not None:
537                if self._data[id].data is not None:
538                    dnames.append(self._data[id].data.name)
539                else:
540                    theory_list = self._data[id].get_theory()
541                    theory, _ = list(theory_list.values())[0]
542                    dnames.append(theory.name)
543        ind = np.argsort(dnames)
544        if len(ind) > 0:
545            val_list = np.array(list(self._data.values()))[ind]
546            for datastate in val_list:
547                data = datastate.data
548                if data is not None:
549                    name = data.name
550                    pos1 = self.data1_cbox.Append(str(name))
551                    self.data1_cbox.SetClientData(pos1, data)
552                    pos2 = self.data2_cbox.Append(str(name))
553                    self.data2_cbox.SetClientData(pos2, data)
554                    if str(current1) == str(name):
555                      pos_pre1 = pos1
556                    if str(current2) == str(name):
557                      pos_pre2 = pos2
558                try:
559                    theory_list = datastate.get_theory()
560                    for theory, _ in list(theory_list.values()):
561                        th_name = theory.name
562                        posth1 = self.data1_cbox.Append(str(th_name))
563                        self.data1_cbox.SetClientData(posth1, theory)
564                        posth2 = self.data2_cbox.Append(str(th_name))
565                        self.data2_cbox.SetClientData(posth2, theory)
566                        if str(current1) == str(th_name):
567                            pos_pre1 = posth1
568                        if str(current2) == str(th_name):
569                            pos_pre2 = posth2
570                except:
571                    continue
572        self.data1_cbox.SetSelection(pos_pre1)
573        self.data2_cbox.SetSelection(pos_pre2)
574
575    def get_datalist(self):
576        """
577        """
578        data_manager = self.parent.parent.get_data_manager()
579        if data_manager is not None:
580            return  data_manager.get_all_data()
581        else:
582            return {}
583
584    def on_click_apply(self, event):
585        """
586        changes are saved in data object imported to edit
587        """
588        self.send_warnings('')
589        self.data_namectr.SetBackgroundColour('white')
590        state_list = list(self.get_datalist().values())
591        name = self.data_namectr.GetValue().strip()
592        name_list = []
593        for state in state_list:
594            if state.data is None:
595                theory_list = state.get_theory()
596                theory, _ = list(theory_list.values())[0]
597                d_name = str(theory.name)
598            else:
599                d_name = str(state.data.name)
600            name_list.append(d_name)
601        if name in name_list:
602            self._set_textctrl_color(self.data_namectr, 'pink')
603            msg = "The Output Data Name already exists...   "
604            wx.MessageBox(msg, 'Error')
605            return
606        if name == '':
607            self._set_textctrl_color(self.data_namectr, 'pink')
608            msg = "Please type the output data name first...   "
609            wx.MessageBox(msg, 'Error')
610            return
611        if self.output is None:
612            msg = "No Output Data has been generated...   "
613            wx.MessageBox(msg, 'Error')
614            return
615        if self.numberctr.IsEnabled() and self.numberctr.IsShown():
616            valid_num = self.on_number(control=self.numberctr)
617            if not valid_num:
618                return
619        # send data to data manager
620        self.output.name = name
621        self.output.run = "Data Operation"
622        self.output.instrument = "SasView"
623        self.output.id = str(name) + str(time.time())
624        data = {self.output.id :self.output}
625        self.parent.parent.add_data(data)
626        self.name_sizer.Layout()
627        self.Refresh()
628        #must post event here
629        event.Skip()
630
631    def on_help(self, event):
632        """
633        Bring up the Data Operations Panel Documentation whenever
634        the HELP button is clicked.
635
636        Calls DocumentationWindow with the path of the location within the
637        documentation tree (after /doc/ ....".  Note that when using old
638        versions of Wx (before 2.9) and thus not the release version of
639        installers, the help comes up at the top level of the file as
640        webbrowser does not pass anything past the # to the browser when it is
641        running "file:///...."
642
643    :param evt: Triggers on clicking the help button
644    """
645
646        _TreeLocation = "user/sasgui/perspectives/calculator/"
647        _TreeLocation += "data_operator_help.html"
648        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
649                                          "Data Operation Help")
650
651    def disconnect_panels(self):
652        """
653        """
654        self.out_pic.connect.disconnect()
655        self.equal_pic.connect.disconnect()
656        self.data1_pic.connect.disconnect()
657        self.operator_pic.connect.disconnect()
658        self.data2_pic.connect.disconnect()
659
660    def on_close(self, event):
661        """
662        leave data as it is and close
663        """
664        self.parent.OnClose()
665
666    def set_plot_unfocus(self):
667        """
668        Unfocus on right click
669        """
670
671    def send_warnings(self, msg='', info='info'):
672        """
673        Send warning to status bar
674        """
675        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info=info))
676
677class SmallPanel(PlotPanel):
678    """
679    PlotPanel for Quick plot and masking plot
680    """
681    def __init__(self, parent, id= -1, is_number=False, content='?', **kwargs):
682        """
683        """
684        PlotPanel.__init__(self, parent, id=id, **kwargs)
685        self.is_number = is_number
686        self.content = content
687        self.point = None
688        self.position = (0.4, 0.5)
689        self.scale = 'linear'
690        self.prevXtrans = "x"
691        self.prevYtrans = "y"
692        self.viewModel = "--"
693        self.subplot.set_xticks([])
694        self.subplot.set_yticks([])
695        self.add_text()
696        self.figure.subplots_adjust(left=0.1, bottom=0.1)
697
698    def set_content(self, content=''):
699        """
700        Set text content
701        """
702        self.content = str(content)
703
704    def add_toolbar(self):
705        """
706        Add toolbar
707        """
708        # Not implemented
709        pass
710
711    def on_set_focus(self, event):
712        """
713        send to the parenet the current panel on focus
714        """
715        pass
716
717    def add_image(self, plot):
718        """
719        Add Image
720        """
721        self.content = ''
722        self.textList = []
723        self.plots = {}
724        self.clear()
725        self.point = plot
726        try:
727            self.figure.delaxes(self.figure.axes[0])
728            self.subplot = self.figure.add_subplot(111)
729            #self.figure.delaxes(self.figure.axes[1])
730        except:
731            pass
732        try:
733            name = plot.name
734        except:
735            name = plot.filename
736        self.plots[name] = plot
737
738        #init graph
739        self.graph = Graph()
740
741        #add plot
742        self.graph.add(plot)
743        #draw
744        self.graph.render(self)
745
746        try:
747            self.figure.delaxes(self.figure.axes[1])
748        except:
749            pass
750        self.subplot.figure.canvas.resizing = False
751        self.subplot.tick_params(axis='both', labelsize=9)
752        # Draw zero axis lines
753        self.subplot.axhline(linewidth=1, color='r')
754        self.subplot.axvline(linewidth=1, color='r')
755
756        self.erase_legend()
757        try:
758            # mpl >= 1.1.0
759            self.figure.tight_layout()
760        except:
761            self.figure.subplots_adjust(left=0.1, bottom=0.1)
762        self.subplot.figure.canvas.draw()
763
764    def add_text(self):
765        """
766        Text in the plot
767        """
768        if not self.is_number:
769            return
770
771        self.clear()
772        try:
773            self.figure.delaxes(self.figure.axes[0])
774            self.subplot = self.figure.add_subplot(111)
775            self.figure.delaxes(self.figure.axes[1])
776        except:
777            pass
778        self.subplot.set_xticks([])
779        self.subplot.set_yticks([])
780        label = self.content
781        FONT = FontProperties()
782        xpos, ypos = (0.4, 0.5)
783        font = FONT.copy()
784        font.set_size(14)
785
786        self.textList = []
787        self.subplot.set_xlim((0, 1))
788        self.subplot.set_ylim((0, 1))
789
790        try:
791            if self.content != '?':
792                float(label)
793        except:
794            self.subplot.set_frame_on(False)
795        try:
796            # mpl >= 1.1.0
797            self.figure.tight_layout()
798        except:
799            self.figure.subplots_adjust(left=0.1, bottom=0.1)
800        if len(label) > 0 and xpos > 0 and ypos > 0:
801            new_text = self.subplot.text(str(xpos), str(ypos), str(label),
802                                           fontproperties=font)
803            self.textList.append(new_text)
804
805    def erase_legend(self):
806        """
807        Remove Legend
808        """
809        #for ax in self.axes:
810        self.remove_legend(self.subplot)
811
812    def onMouseMotion(self, event):
813        """
814        Disable dragging 2D image
815        """
816
817    def onWheel(self, event):
818        """
819        """
820
821    def onLeftDown(self, event):
822        """
823        Disables LeftDown
824        """
825
826    def onPick(self, event):
827        """
828        Remove Legend
829        """
830        for ax in self.axes:
831            self.remove_legend(ax)
832
833
834    def draw(self):
835        """
836        Draw
837        """
838        if self.dimension == 3:
839            pass
840        else:
841            self.subplot.figure.canvas.resizing = False
842            self.subplot.tick_params(axis='both', labelsize=9)
843            self.erase_legend()
844            self.subplot.figure.canvas.draw_idle()
845            try:
846                self.figure.delaxes(self.figure.axes[1])
847            except:
848                pass
849
850
851    def onContextMenu(self, event):
852        """
853        Default context menu for a plot panel
854        """
855        id = wx.NewId()
856        slicerpop = wx.Menu()
857        data = self.point
858        if issubclass(data.__class__, Data1D):
859            slicerpop.Append(id, '&Change Scale')
860            wx.EVT_MENU(self, id, self._onProperties)
861        else:
862            slicerpop.Append(id, '&Toggle Linear/Log Scale')
863            wx.EVT_MENU(self, id, self.ontogglescale)
864        try:
865            # mouse event
866            pos_evt = event.GetPosition()
867            pos = self.ScreenToClient(pos_evt)
868        except:
869            # toolbar event
870            pos_x, pos_y = self.toolbar.GetPositionTuple()
871            pos = (pos_x, pos_y + 5)
872        self.PopupMenu(slicerpop, pos)
873
874    def ontogglescale(self, event):
875        """
876        On toggle 2d scale
877        """
878        self._onToggleScale(event)
879        try:
880            # mpl >= 1.1.0
881            self.figure.tight_layout()
882        except:
883            self.figure.subplots_adjust(left=0.1, bottom=0.1)
884        try:
885            self.figure.delaxes(self.figure.axes[1])
886        except:
887            pass
888
889    def _onProperties(self, event):
890        """
891        when clicking on Properties on context menu ,
892        The Property dialog is displayed
893        The user selects a transformation for x or y value and
894        a new plot is displayed
895        """
896        list = []
897        list = self.graph.returnPlottable()
898        if len(list(list.keys())) > 0:
899            first_item = list(list.keys())[0]
900            if first_item.x != []:
901                from sas.sasgui.plottools.PropertyDialog import Properties
902                dial = Properties(self, -1, 'Change Scale')
903                # type of view or model used
904                dial.xvalue.Clear()
905                dial.yvalue.Clear()
906                dial.view.Clear()
907                dial.xvalue.Insert("x", 0)
908                dial.xvalue.Insert("log10(x)", 1)
909                dial.yvalue.Insert("y", 0)
910                dial.yvalue.Insert("log10(y)", 1)
911                dial.view.Insert("--", 0)
912                dial.view.Insert("Linear y vs x", 1)
913                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
914                dial.Update()
915                if dial.ShowModal() == wx.ID_OK:
916                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
917                    if self.viewModel == "Linear y vs x":
918                        self.xLabel = "x"
919                        self.yLabel = "y"
920                        self.viewModel = "--"
921                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
922                    self._onEVT_FUNC_PROPERTY()
923                dial.Destroy()
924
925    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
926        """
927        Receive the x and y transformation from myDialog,
928        Transforms x and y in View
929        and set the scale
930        """
931        list = []
932        list = self.graph.returnPlottable()
933        # Changing the scale might be incompatible with
934        # currently displayed data (for instance, going
935        # from ln to log when all plotted values have
936        # negative natural logs).
937        # Go linear and only change the scale at the end.
938        self.set_xscale("linear")
939        self.set_yscale("linear")
940        _xscale = 'linear'
941        _yscale = 'linear'
942        for item in list:
943            item.setLabel(self.xLabel, self.yLabel)
944            # control axis labels from the panel itself
945            yname, yunits = item.get_yaxis()
946            xname, xunits = item.get_xaxis()
947            # Goes through all possible scales
948            # Goes through all possible scales
949            if(self.xLabel == "x"):
950                item.transformX(transform.toX, transform.errToX)
951                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
952            if(self.xLabel == "log10(x)"):
953                item.transformX(transform.toX_pos, transform.errToX_pos)
954                _xscale = 'log'
955                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
956            if(self.yLabel == "y"):
957                item.transformY(transform.toX, transform.errToX)
958                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
959            if(self.yLabel == "log10(y)"):
960                item.transformY(transform.toX_pos, transform.errToX_pos)
961                _yscale = 'log'
962                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
963            item.transformView()
964        self.prevXtrans = self.xLabel
965        self.prevYtrans = self.yLabel
966        self.set_xscale(_xscale)
967        self.set_yscale(_yscale)
968        self.draw()
969
970class DataOperatorWindow(widget.CHILD_FRAME):
971    def __init__(self, parent, manager, *args, **kwds):
972        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGTH)
973        widget.CHILD_FRAME.__init__(self, parent, *args, **kwds)
974        self.parent = parent
975        self.manager = manager
976        self.panel = DataOperPanel(parent=self)
977        wx.EVT_CLOSE(self, self.OnClose)
978        self.SetPosition((wx.LEFT, PANEL_TOP))
979        self.Show()
980
981    def OnClose(self, event=None):
982        """
983        On close event
984        """
985        if self.manager is not None:
986            self.manager.data_operator_frame = None
987        self.panel.disconnect_panels()
988        self.Destroy()
989
990
991if __name__ == "__main__":
992
993    app = wx.App()
994    widget.CHILD_FRAME = wx.Frame
995    window = DataOperatorWindow(parent=None, data=[], title="Data Editor")
996    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.