source: sasview/src/sas/sasgui/perspectives/calculator/data_operator.py @ 34f23c8

ticket-1249
Last change on this file since 34f23c8 was 34f23c8, checked in by Paul Kienzle <pkienzle@…>, 3 months ago

py3/wx4 compatibility changes for gui. Refs #1249

  • Property mode set to 100644
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        name_list = []
205        for state in self.get_datalist().values():
206            if state.data is None:
207                theory_list = state.get_theory()
208                theory, _ = list(theory_list.values())[0]
209                d_name = str(theory.name)
210            else:
211                d_name = str(state.data.name)
212            name_list.append(d_name)
213        if text in name_list:
214            self._set_textctrl_color(self.data_namectr, 'pink')
215            msg = "DataOperation: The name already exists."
216        if len(text) == 0:
217            self._set_textctrl_color(self.data_namectr, 'pink')
218            msg = "DataOperation: Type the data name first."
219        if self._notes:
220            self.send_warnings(msg, 'error')
221        self.name_sizer.Layout()
222        self.Refresh()
223
224    def _set_textctrl_color(self, ctrl, color):
225        """
226        Set TextCtrl color
227        """
228        if ON_MAC:
229            children = ctrl.GetChildren()
230            if len(children) > 0:
231                children[0].SetBackgroundColour(color)
232        else:
233            ctrl.SetBackgroundColour(color)
234        self.name_sizer.Layout()
235
236    def on_number(self, event=None, control=None):
237        """
238        On selecting Number for Data2
239        """
240        self.send_warnings('')
241        item = control
242        if item is None and event is not None:
243            item = event.GetEventObject()
244        elif item is None:
245            raise ValueError("Event or control must be supplied")
246        text = item.GetValue().strip()
247        if self.numberctr.IsShown():
248            if self.numberctr.IsEnabled():
249                self._set_textctrl_color(self.numberctr, 'white')
250                try:
251                    val = float(text)
252                    pos = self.data2_cbox.GetCurrentSelection()
253                    self.data2_cbox.SetClientData(pos, val)
254                except:
255                    self._set_textctrl_color(self.numberctr, 'pink')
256                    if event is None:
257                        msg = "DataOperation: Number requires a float number."
258                        self.send_warnings(msg, 'error')
259                    return False
260            else:
261                self._set_textctrl_color(self.numberctr, self.color)
262
263        self.put_text_pic(self.data2_pic, content=str(val))
264        self.check_data_inputs()
265        if self.output is not None:
266            self.output.name = str(self.data_namectr.GetValue())
267        self.draw_output(self.output)
268        self.Refresh()
269        return True
270
271    def on_select_data1(self, event=None):
272        """
273        On select data1
274        """
275        self.send_warnings('')
276        item = event.GetEventObject()
277        pos = item.GetCurrentSelection()
278        data = item.GetClientData(pos)
279        if data is None:
280            content = "?"
281            self.put_text_pic(self.data1_pic, content)
282        else:
283            self.data1_pic.add_image(data)
284        self.check_data_inputs()
285        if self.output is not None:
286            self.output.name = str(self.data_namectr.GetValue())
287        self.draw_output(self.output)
288
289    def on_select_operator(self, event=None):
290        """
291        On Select an Operator
292        """
293        self.send_warnings('')
294        item = event.GetEventObject()
295        text = item.GetValue().strip()
296        self.put_text_pic(self.operator_pic, content=text)
297        self.check_data_inputs()
298        if self.output is not None:
299            self.output.name = str(self.data_namectr.GetValue())
300        self.draw_output(self.output)
301
302    def on_select_data2(self, event=None):
303        """
304        On Selecting Data2
305        """
306        self.send_warnings('')
307        item = event.GetEventObject()
308        text = item.GetValue().strip().lower()
309        self._show_numctrl(self.numberctr, text == 'number')
310        pos = item.GetCurrentSelection()
311        data = item.GetClientData(pos)
312        content = "?"
313        if not (self.numberctr.IsShown() and self.numberctr.IsEnabled()):
314            if data is None:
315                content = "?"
316                self.put_text_pic(self.data2_pic, content)
317            else:
318                self.data2_pic.add_image(data)
319            self.check_data_inputs()
320        else:
321            content = str(self.numberctr.GetValue().strip())
322            try:
323                content = float(content)
324                data = content
325            except:
326                self._set_textctrl_color(self.numberctr, 'pink')
327                content = "?"
328                data = None
329            item.SetClientData(pos, data)
330            if data is not None:
331                self.check_data_inputs()
332
333            self.put_text_pic(self.data2_pic, content)
334
335        if self.output is not None:
336            self.output.name = str(self.data_namectr.GetValue())
337        self.draw_output(self.output)
338
339    def put_text_pic(self, pic=None, content=''):
340        """
341        Put text to the pic
342        """
343        pic.set_content(content)
344        pic.add_text()
345        pic.draw()
346
347    def check_data_inputs(self):
348        """
349        Check data1 and data2 whether or not they are ready for operation
350        """
351        self._set_textctrl_color(self.data1_cbox, 'white')
352        self._set_textctrl_color(self.data2_cbox, 'white')
353        flag = False
354        pos1 = self.data1_cbox.GetCurrentSelection()
355        data1 = self.data1_cbox.GetClientData(pos1)
356        if data1 is None:
357            self.output = None
358            return flag
359        pos2 = self.data2_cbox.GetCurrentSelection()
360        data2 = self.data2_cbox.GetClientData(pos2)
361
362        if data2 is None:
363            self.output = None
364            return flag
365        if self.numberctr.IsShown():
366            if self.numberctr.IsEnabled():
367                self._set_textctrl_color(self.numberctr, 'white')
368                try:
369                    float(data2)
370                    if self.operator_cbox.GetValue().strip() == '|':
371                        msg = "DataOperation: This operation can not accept "
372                        msg += "a float number."
373                        self.send_warnings(msg, 'error')
374                        self._set_textctrl_color(self.numberctr, 'pink')
375                        self.output = None
376                        return flag
377                except:
378                    msg = "DataOperation: Number requires a float number."
379                    self.send_warnings(msg, 'error')
380                    self._set_textctrl_color(self.numberctr, 'pink')
381                    self.output = None
382                    return flag
383            else:
384                self._set_textctrl_color(self.numberctr, self.color)
385        elif data1.__class__.__name__ != data2.__class__.__name__:
386            self._set_textctrl_color(self.data1_cbox, 'pink')
387            self._set_textctrl_color(self.data2_cbox, 'pink')
388            msg = "DataOperation: Data types must be same."
389            self.send_warnings(msg, 'error')
390            self.output = None
391            return flag
392        try:
393            self.output = self.make_data_out(data1, data2)
394        except Exception as exc:
395            self._check_newname()
396            self._set_textctrl_color(self.data1_cbox, 'pink')
397            self._set_textctrl_color(self.data2_cbox, 'pink')
398            msg = "DataOperation: %s" % exc
399            self.send_warnings(msg, 'error')
400            self.output = None
401            return flag
402        return True
403
404    def make_data_out(self, data1, data2):
405        """
406        Make a temp. data output set
407        """
408        output = None
409        pos = self.operator_cbox.GetCurrentSelection()
410        operator = self.operator_cbox.GetClientData(pos)
411        try:
412            output = eval("data1 %s data2" % operator,
413                          {"data1": data1, "data2": data2})
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        for id in self._data.keys():
535            if id is not None:
536                if self._data[id].data is not None:
537                    dnames.append(self._data[id].data.name)
538                else:
539                    theory_list = self._data[id].get_theory()
540                    theory, _ = list(theory_list.values())[0]
541                    dnames.append(theory.name)
542        ind = np.argsort(dnames)
543        if len(ind) > 0:
544            val_list = np.array(list(self._data.values()))[ind]
545            for datastate in val_list:
546                data = datastate.data
547                if data is not None:
548                    name = data.name
549                    pos1 = self.data1_cbox.Append(str(name))
550                    self.data1_cbox.SetClientData(pos1, data)
551                    pos2 = self.data2_cbox.Append(str(name))
552                    self.data2_cbox.SetClientData(pos2, data)
553                    if str(current1) == str(name):
554                      pos_pre1 = pos1
555                    if str(current2) == str(name):
556                      pos_pre2 = pos2
557                try:
558                    theory_list = datastate.get_theory()
559                    for theory, _ in theory_list.values():
560                        th_name = theory.name
561                        posth1 = self.data1_cbox.Append(str(th_name))
562                        self.data1_cbox.SetClientData(posth1, theory)
563                        posth2 = self.data2_cbox.Append(str(th_name))
564                        self.data2_cbox.SetClientData(posth2, theory)
565                        if str(current1) == str(th_name):
566                            pos_pre1 = posth1
567                        if str(current2) == str(th_name):
568                            pos_pre2 = posth2
569                except:
570                    continue
571        self.data1_cbox.SetSelection(pos_pre1)
572        self.data2_cbox.SetSelection(pos_pre2)
573
574    def get_datalist(self):
575        """
576        """
577        data_manager = self.parent.parent.get_data_manager()
578        if data_manager is not None:
579            return  data_manager.get_all_data()
580        else:
581            return {}
582
583    def on_click_apply(self, event):
584        """
585        changes are saved in data object imported to edit
586        """
587        self.send_warnings('')
588        self.data_namectr.SetBackgroundColour('white')
589        name = self.data_namectr.GetValue().strip()
590        name_list = []
591        for state in self.get_datalist().values():
592            if state.data is None:
593                theory_list = state.get_theory()
594                theory, _ = list(theory_list.values())[0]
595                d_name = str(theory.name)
596            else:
597                d_name = str(state.data.name)
598            name_list.append(d_name)
599        if name in name_list:
600            self._set_textctrl_color(self.data_namectr, 'pink')
601            msg = "The Output Data Name already exists...   "
602            wx.MessageBox(msg, 'Error')
603            return
604        if name == '':
605            self._set_textctrl_color(self.data_namectr, 'pink')
606            msg = "Please type the output data name first...   "
607            wx.MessageBox(msg, 'Error')
608            return
609        if self.output is None:
610            msg = "No Output Data has been generated...   "
611            wx.MessageBox(msg, 'Error')
612            return
613        if self.numberctr.IsEnabled() and self.numberctr.IsShown():
614            valid_num = self.on_number(control=self.numberctr)
615            if not valid_num:
616                return
617        # send data to data manager
618        self.output.name = name
619        self.output.run = "Data Operation"
620        self.output.instrument = "SasView"
621        self.output.id = str(name) + str(time.time())
622        data = {self.output.id :self.output}
623        self.parent.parent.add_data(data)
624        self.name_sizer.Layout()
625        self.Refresh()
626        #must post event here
627        event.Skip()
628
629    def on_help(self, event):
630        """
631        Bring up the Data Operations Panel Documentation whenever
632        the HELP button is clicked.
633
634        Calls DocumentationWindow with the path of the location within the
635        documentation tree (after /doc/ ....".  Note that when using old
636        versions of Wx (before 2.9) and thus not the release version of
637        installers, the help comes up at the top level of the file as
638        webbrowser does not pass anything past the # to the browser when it is
639        running "file:///...."
640
641    :param evt: Triggers on clicking the help button
642    """
643
644        _TreeLocation = "user/sasgui/perspectives/calculator/"
645        _TreeLocation += "data_operator_help.html"
646        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
647                                          "Data Operation Help")
648
649    def disconnect_panels(self):
650        """
651        """
652        self.out_pic.connect.disconnect()
653        self.equal_pic.connect.disconnect()
654        self.data1_pic.connect.disconnect()
655        self.operator_pic.connect.disconnect()
656        self.data2_pic.connect.disconnect()
657
658    def on_close(self, event):
659        """
660        leave data as it is and close
661        """
662        self.parent.OnClose()
663
664    def set_plot_unfocus(self):
665        """
666        Unfocus on right click
667        """
668
669    def send_warnings(self, msg='', info='info'):
670        """
671        Send warning to status bar
672        """
673        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info=info))
674
675class SmallPanel(PlotPanel):
676    """
677    PlotPanel for Quick plot and masking plot
678    """
679    def __init__(self, parent, id= -1, is_number=False, content='?', **kwargs):
680        """
681        """
682        PlotPanel.__init__(self, parent, id=id, **kwargs)
683        self.is_number = is_number
684        self.content = content
685        self.point = None
686        self.position = (0.4, 0.5)
687        self.scale = 'linear'
688        self.prevXtrans = "x"
689        self.prevYtrans = "y"
690        self.viewModel = "--"
691        self.subplot.set_xticks([])
692        self.subplot.set_yticks([])
693        self.add_text()
694        self.figure.subplots_adjust(left=0.1, bottom=0.1)
695
696    def set_content(self, content=''):
697        """
698        Set text content
699        """
700        self.content = str(content)
701
702    def add_toolbar(self):
703        """
704        Add toolbar
705        """
706        # Not implemented
707        pass
708
709    def on_set_focus(self, event):
710        """
711        send to the parenet the current panel on focus
712        """
713        pass
714
715    def add_image(self, plot):
716        """
717        Add Image
718        """
719        self.content = ''
720        self.textList = []
721        self.plots = {}
722        self.clear()
723        self.point = plot
724        try:
725            self.figure.delaxes(self.figure.axes[0])
726            self.subplot = self.figure.add_subplot(111)
727            #self.figure.delaxes(self.figure.axes[1])
728        except:
729            pass
730        try:
731            name = plot.name
732        except:
733            name = plot.filename
734        self.plots[name] = plot
735
736        #init graph
737        self.graph = Graph()
738
739        #add plot
740        self.graph.add(plot)
741        #draw
742        self.graph.render(self)
743
744        try:
745            self.figure.delaxes(self.figure.axes[1])
746        except:
747            pass
748        self.subplot.figure.canvas.resizing = False
749        self.subplot.tick_params(axis='both', labelsize=9)
750        # Draw zero axis lines
751        self.subplot.axhline(linewidth=1, color='r')
752        self.subplot.axvline(linewidth=1, color='r')
753
754        self.erase_legend()
755        try:
756            # mpl >= 1.1.0
757            self.figure.tight_layout()
758        except:
759            self.figure.subplots_adjust(left=0.1, bottom=0.1)
760        self.subplot.figure.canvas.draw()
761
762    def add_text(self):
763        """
764        Text in the plot
765        """
766        if not self.is_number:
767            return
768
769        self.clear()
770        try:
771            self.figure.delaxes(self.figure.axes[0])
772            self.subplot = self.figure.add_subplot(111)
773            self.figure.delaxes(self.figure.axes[1])
774        except:
775            pass
776        self.subplot.set_xticks([])
777        self.subplot.set_yticks([])
778        label = self.content
779        FONT = FontProperties()
780        xpos, ypos = (0.4, 0.5)
781        font = FONT.copy()
782        font.set_size(14)
783
784        self.textList = []
785        self.subplot.set_xlim((0, 1))
786        self.subplot.set_ylim((0, 1))
787
788        try:
789            if self.content != '?':
790                float(label)
791        except:
792            self.subplot.set_frame_on(False)
793        try:
794            # mpl >= 1.1.0
795            self.figure.tight_layout()
796        except:
797            self.figure.subplots_adjust(left=0.1, bottom=0.1)
798        if len(label) > 0 and xpos > 0 and ypos > 0:
799            new_text = self.subplot.text(str(xpos), str(ypos), str(label),
800                                           fontproperties=font)
801            self.textList.append(new_text)
802
803    def erase_legend(self):
804        """
805        Remove Legend
806        """
807        #for ax in self.axes:
808        self.remove_legend(self.subplot)
809
810    def onMouseMotion(self, event):
811        """
812        Disable dragging 2D image
813        """
814
815    def onWheel(self, event):
816        """
817        """
818
819    def onLeftDown(self, event):
820        """
821        Disables LeftDown
822        """
823
824    def onPick(self, event):
825        """
826        Remove Legend
827        """
828        for ax in self.axes:
829            self.remove_legend(ax)
830
831
832    def draw(self):
833        """
834        Draw
835        """
836        if self.dimension == 3:
837            pass
838        else:
839            self.subplot.figure.canvas.resizing = False
840            self.subplot.tick_params(axis='both', labelsize=9)
841            self.erase_legend()
842            self.subplot.figure.canvas.draw_idle()
843            try:
844                self.figure.delaxes(self.figure.axes[1])
845            except:
846                pass
847
848
849    def onContextMenu(self, event):
850        """
851        Default context menu for a plot panel
852        """
853        id = wx.NewId()
854        slicerpop = wx.Menu()
855        data = self.point
856        if issubclass(data.__class__, Data1D):
857            slicerpop.Append(id, '&Change Scale')
858            wx.EVT_MENU(self, id, self._onProperties)
859        else:
860            slicerpop.Append(id, '&Toggle Linear/Log Scale')
861            wx.EVT_MENU(self, id, self.ontogglescale)
862        try:
863            # mouse event
864            pos_evt = event.GetPosition()
865            pos = self.ScreenToClient(pos_evt)
866        except:
867            # toolbar event
868            pos_x, pos_y = self.toolbar.GetPosition()
869            pos = (pos_x, pos_y + 5)
870        self.PopupMenu(slicerpop, pos)
871
872    def ontogglescale(self, event):
873        """
874        On toggle 2d scale
875        """
876        self._onToggleScale(event)
877        try:
878            # mpl >= 1.1.0
879            self.figure.tight_layout()
880        except:
881            self.figure.subplots_adjust(left=0.1, bottom=0.1)
882        try:
883            self.figure.delaxes(self.figure.axes[1])
884        except:
885            pass
886
887    def _onProperties(self, event):
888        """
889        When clicking on Properties on context menu, the
890        Property dialog is displayed the user selects a
891        transformation for x or y value and a new plot is displayed
892        """
893        plottables = self.graph.returnPlottable()
894        if plottables:
895            # TODO: key order is random prior to py 3.7
896            first_item = list(plottables.keys())[0]
897            if first_item.x != []:
898                from sas.sasgui.plottools.PropertyDialog import Properties
899                dial = Properties(self, -1, 'Change Scale')
900                # type of view or model used
901                dial.xvalue.Clear()
902                dial.yvalue.Clear()
903                dial.view.Clear()
904                dial.xvalue.Insert("x", 0)
905                dial.xvalue.Insert("log10(x)", 1)
906                dial.yvalue.Insert("y", 0)
907                dial.yvalue.Insert("log10(y)", 1)
908                dial.view.Insert("--", 0)
909                dial.view.Insert("Linear y vs x", 1)
910                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
911                dial.Update()
912                if dial.ShowModal() == wx.ID_OK:
913                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
914                    if self.viewModel == "Linear y vs x":
915                        self.xLabel = "x"
916                        self.yLabel = "y"
917                        self.viewModel = "--"
918                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
919                    self._onEVT_FUNC_PROPERTY()
920                dial.Destroy()
921
922    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
923        """
924        Receive the x and y transformation from myDialog,
925        Transforms x and y in View
926        and set the scale
927        """
928        # Changing the scale might be incompatible with
929        # currently displayed data (for instance, going
930        # from ln to log when all plotted values have
931        # negative natural logs).
932        # Go linear and only change the scale at the end.
933        self.set_xscale("linear")
934        self.set_yscale("linear")
935        _xscale = 'linear'
936        _yscale = 'linear'
937        for item in self.graph.returnPlottable():
938            item.setLabel(self.xLabel, self.yLabel)
939            # control axis labels from the panel itself
940            yname, yunits = item.get_yaxis()
941            xname, xunits = item.get_xaxis()
942            # Goes through all possible scales
943            # Goes through all possible scales
944            if(self.xLabel == "x"):
945                item.transformX(transform.toX, transform.errToX)
946                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
947            if(self.xLabel == "log10(x)"):
948                item.transformX(transform.toX_pos, transform.errToX_pos)
949                _xscale = 'log'
950                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
951            if(self.yLabel == "y"):
952                item.transformY(transform.toX, transform.errToX)
953                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
954            if(self.yLabel == "log10(y)"):
955                item.transformY(transform.toX_pos, transform.errToX_pos)
956                _yscale = 'log'
957                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
958            item.transformView()
959        self.prevXtrans = self.xLabel
960        self.prevYtrans = self.yLabel
961        self.set_xscale(_xscale)
962        self.set_yscale(_yscale)
963        self.draw()
964
965class DataOperatorWindow(widget.CHILD_FRAME):
966    def __init__(self, parent, manager, *args, **kwds):
967        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGTH)
968        widget.CHILD_FRAME.__init__(self, parent, *args, **kwds)
969        self.parent = parent
970        self.manager = manager
971        self.panel = DataOperPanel(parent=self)
972        wx.EVT_CLOSE(self, self.OnClose)
973        self.SetPosition((wx.LEFT, PANEL_TOP))
974        self.Show()
975
976    def OnClose(self, event=None):
977        """
978        On close event
979        """
980        if self.manager is not None:
981            self.manager.data_operator_frame = None
982        self.panel.disconnect_panels()
983        self.Destroy()
984
985
986if __name__ == "__main__":
987
988    app = wx.App()
989    widget.CHILD_FRAME = wx.Frame
990    window = DataOperatorWindow(parent=None, data=[], title="Data Editor")
991    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.