1 """Components for the top level of the QUB Express or Fitness.
2
3 Copyright 2008-2014 Research Foundation State University of New York
4 This file is part of QUB Express.
5
6 QUB Express is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 QUB Express is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License,
17 named LICENSE.txt, in the QUB Express program directory. If not, see
18 <http://www.gnu.org/licenses/>.
19
20
21 """
22
23 import os
24 import traceback
25
26 import gtk
27 import gobject
28 import numpy
29
30 import qubx.data_types
31 import qubx.data_fit
32 import qubx.dataGTK
33 import qubx.faces
34 import qubx.GTK
35 import qubx.pyenv
36 import qubx.settings
37 import qubx.tree
38 import qubx.table
39 import qubx.tableGTK
40 import qubx.toolspace
41 import qubx.notebook
42 import qubx.notebookGTK
43
44 from itertools import izip, count
45 from math import *
46 from gtk import gdk
47 from qubx.accept import *
48 from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem
49 from qubx.settings import Property, Propertied
50 from qubx.table import COPY_ROWS_ALL, COPY_ROWS_GROUP, COPY_ROWS_CRITERIA, COPY_ROWS_CHECKED
51 from qubx.toolspace import ColorInfo
52 from qubx.util_types import *
53
54
55 COLOR_DATA_FILE_MENU = ('QubX.data.file.menu', (.6, .6, 1, 1))
56 ColorInfo[COLOR_DATA_FILE_MENU[0]].label = 'Data file menu triangle'
57
58 LORES_FRAC = (1.0 - 1.0 / (1.618)) / 2.0
59 LORES_MIN_EM = 8.0
60
61 @Propertied(Property('prompt_store_fit_curves', True, 'False to trust the user on "save fit curves in session"'),
62 Property('splitter_frac', 0.0, 'position between 0.0 and 1.0 of last hi/lo splitter moved'))
64 """
65 Panel displaying one of possibly several open data files. If has_modeling, file index 0 is L{qubx.data_types.QubData_Scratch}.
66
67 @ivar views: list of L{qubx.dataGTK.QubDataView}
68 """
69
70 __explore_featured = ['has_modeling', 'about', 'controls_fit', 'sequence', 'lstSequence', 'display_prefs',
71 'menubar', 'init_fitting', 'init_scratch', 'open_sequence', 'open_seq_next', 'go_seq',
72 'close_seq_curr', 'done_sequence', 'set_auto_save_session',
73 'copy_segments_to_list', 'list_add_screen', 'new_list', 'import_list', 'export_list',
74 'copy_list', 'measure_list', 'split_list', 'get_samples', 'get_samples_indexed',
75 'add_computed_signal']
76
77 - def __init__(self, tables, has_modeling, global_name=""):
78 qubx.faces.FilesFace.__init__(self, ' Data', qubx.data_types.QubDatas(),
79 lambda table:qubx.dataGTK.QubDataView_Lo(table,qubx.faces.FILEMENU_WIDTH), qubx.faces.FILEMENU_WIDTH,
80 popup_color=COLOR_DATA_FILE_MENU, global_name=global_name)
81 self.__ref = Reffer()
82 self.propertied_connect_settings('QubX.Data')
83 self.has_modeling = has_modeling
84 if not has_modeling:
85 for layer in (self.layFiles, self.layMenu):
86 self.page0.controls.remove_layer(layer)
87 self.page0.add_layer(layer)
88 self.page0.file = qubx.data_types.QubData_Blank('<no file>')
89 self.tables = tables
90 self.tables.add_table(self.table)
91 hdr = qubx.toolspace.Button_Popup_PNG(self.mnuFile, os.path.join(qubx.global_namespace.app_path, 'icons', 'folder.png'), "File menu")
92 hdr.show()
93 tables.find_view(' Data').set_index_widget(hdr, hdr.do_popup)
94 self.about = qubx.faces.TextFace("Data", global_name='QubX.About.Data')
95 self.about_notes = qubx.faces.TextFace("DataNotes", global_name='QubX.About.DataNotes')
96 self.about.show()
97 self.QubX = None
98 self.controls_fit = None
99 self.__showing_fit = False
100 self.__timeout_session = None
101 self.__timeout_session_minutes = 5
102
103 self.itemNew.hide()
104 self.itemSave.hide()
105 self.itemRevert.hide()
106 item = build_menuitem('Open Sequence...', self.__ref(lambda item: self.open_sequence()))
107 self.mnuFile.insert(item, 3)
108 item = self.itemCancelSequence = build_menuitem('Cancel Sequence', self.__ref(lambda item: self.open_sequence([])), show=False)
109 self.mnuFile.insert(item, 4)
110 self.mnuFile.append(build_menuitem('Save session', self.__ref(self.__onClickSaveSession)))
111 self.itemAutoSaveSession = build_menuitem('Auto-save session...', self.__ref(self.__onClickAutoSaveSession), menu=self.mnuFile, item_class=gtk.CheckMenuItem)
112 self.itemStoreFits = build_menuitem('Save fit curves in session', self.__ref(self.__onClickStoreFits), menu=self.mnuFile, item_class=gtk.CheckMenuItem)
113 self.__in_auto_session = False
114 self.mnuFile.append(build_menuitem('New Table...', self.__ref(lambda item: qubx.pyenv.env.globals['QubX'].new_table())))
115 self.__list_table = None
116 self.__ignore_store_fits = False
117 self.__open_seq = []
118 self.__ix_in_seq = -1
119 self.__curr_in_seq = None
120 self.__next_in_seq = None
121 self.sequence = qubx.faces.Face('Sequence')
122 self.lstSequence = qubx.GTK.CheckList()
123 self.lstSequence.OnClick += self.__ref(self.__onClickSequence)
124 pack_scrolled(self.lstSequence, self.sequence, expand=True)
125
126 self.display_prefs = qubx.settings.SettingsMgr['Data.Display']
127 self.display_prefs.OnSet = self.__ref(self.__onUpdateDisplay)
128
129 self.layFiles.cBG = qubx.scope.COLOR_SIGNAL_BG
130 self.mnuFiles.color = qubx.dataGTK.COLOR_DATA_LAYER_FG
131
132 self.menubar = gtk.MenuBar()
133 self.itemFile = gtk.MenuItem('File')
134 self.itemFile.connect('activate', bind(self.on_popup_file))
135 self.itemFile.show()
136 self.menubar.append(self.itemFile)
137 self.itemLists = gtk.MenuItem('Lists')
138 self.itemLists.connect('activate', self.__onPopupLists)
139 self.itemLists.show()
140 self.menubar.append(self.itemLists)
141 self.itemDisplay = gtk.MenuItem('Display')
142 self.itemDisplay.connect('activate', self.__onPopupDisplay)
143 self.itemDisplay.show()
144 self.menubar.append(self.itemDisplay)
145 self.itemTools = gtk.MenuItem('Tools')
146 self.itemTools.connect('activate', self.__onPopupTools)
147 self.itemTools.show()
148 self.menubar.append(self.itemTools)
149
150 self.init_scratch(has_modeling)
151 self.OnSwitch += self.__ref(self.__onSwitch)
152 self.OnSave += self.__ref(self.__onSave)
153 self.OnClosing += self.__ref(self.__onClosing)
154 qubx.notebook.Notebook.register_auto('Data_Open.Picture', 'Data picture, on open/choose')
155 qubx.notebook.Notebook.register_auto('Data_Open.PictureNO', 'Data picture, no overlays, on open/choose')
156 qubx.notebook.Notebook.register_auto('Data_Open.Table', 'Data table, on open/choose')
157 qubx.notebook.Notebook.register_auto('Data_Save.Picture', 'Data picture, on save')
158 qubx.notebook.Notebook.register_auto('Data_Save.PictureNO', 'Data picture, no overlays, on save')
159 qubx.notebook.Notebook.register_auto('Data_Save.Table', 'Data table, on save')
163 - def make_page(self, view):
164 page = gtk.VPaned()
165 page.pack1(view, True, True)
166 view.show()
167 page.pack2(view.hires, True, True)
168 view.hires.show()
169 page.show()
170 view.hires.mnuFile = view.mnuFile
171 view.hires.mnuFiles = view.mnuFiles
172 page.evt_on_size_allocate = page.connect('size_allocate', self.__onAllocPage)
173 page.evt_on_position = page.connect('notify::position', self.__onSplitterPosition)
174 return page
175 - def __onAllocPage(self, page, allocation):
176 if (self.splitter_frac == 0.0) or (page.get_position() < qubx.toolspace.Appearance.emsize):
177 gobject.idle_add(page.set_position, int(round(min(.6*allocation.height, max(LORES_MIN_EM*qubx.toolspace.Appearance.emsize, LORES_FRAC * allocation.height)))))
178 elif (page.get_position() > (allocation.height - qubx.toolspace.Appearance.emsize)):
179 gobject.idle_add(page.set_position, int(round(min(.6*allocation.height, max(LORES_MIN_EM*qubx.toolspace.Appearance.emsize, self.splitter_frac*allocation.height)))))
204 if show_fit and not self.__showing_fit:
205 if self.view:
206 self.view.hires.layerset = self.controls_fit
207 self.__showing_fit = True
208 elif (not show_fit) and self.__showing_fit:
209 if self.view:
210 self.view.hires.layerset = self.view.hires.controls
211 self.__showing_fit = False
232 - def open(self, path=None):
253 def finish():
254 self.index = i
255
256 qubx.pyenv.env.OnScriptable('QubX.Data.open(%s)' % repr(path))
257 dlg = qubx.GTK.BusyDialog('Opening %s...'%os.path.split(path)[1], task, finish)
258 dlg.set_size_request(300, 75)
259 try:
260 dlg.run()
261 finally:
262 dlg.destroy()
277 if not already_closed:
278 return self.close_seq_curr()
279 if not self.__open_seq: return
280 if self.__next_in_seq is None:
281 ix = self.__ix_in_seq + 1
282 while (ix < len(self.__open_seq)) and not self.lstSequence.get_active(ix):
283 ix += 1
284 if not (0 <= ix < len(self.__open_seq)):
285 self.done_sequence()
286 return
287 else:
288 ix = self.__next_in_seq
289 self.__next_in_seq = None
290 self.lstSequence.set_cursor((ix,), None, False)
291 self.open(self.__open_seq[ix])
292 self.__ix_in_seq = ix
293 self.__curr_in_seq = self.file
294 self.itemCancelSequence.show()
296 if not self.__open_seq: return
297 self.__next_in_seq = ix
298 self.close_seq_curr()
300 if self.__open_seq and self.__curr_in_seq:
301 self.show_file(self.__curr_in_seq.path)
302 self.close()
313 if self.file.path == self.__open_seq[ix]:
314
315 return
316 qubx.pyenv.env.OnScriptable('QubX.Data.go_seq(%i)' % ix)
317 self.go_seq(ix)
320 - def save(self, path=None):
334 dlg = qubx.GTK.BusyDialog('Saving %s...'%os.path.split(path)[1], task)
335 dlg.set_size_request(300, 75)
336 try:
337 dlg.run()
338 finally:
339 dlg.destroy()
341 self.file.notes = self.about_notes.get_text()
342 ix = self.index
343 if self.__open_seq and (self.views[ix].file == self.__curr_in_seq):
344 self.__curr_in_seq = None
345 gobject.idle_add(self.open_seq_next, True)
346 if self.prompt_store_fit_curves and not self.file.store_fit_curves:
347 Ndata = self.file.segmentation.segments[-1][1]
348 for fitsignal in self.file.fits:
349 if fitsignal.idl.count_dwells(0, Ndata-1, True):
350 nm = os.path.split(self.file.path)[1]
351 dlg = qubx.GTK.AskOnceDialog('%s - Save fit curves with data?' % qubx.global_namespace.QubX.appname, None,
352 """The data file %s
353 has unsaved fit curves. Save them in the session file?
354 (%s.qsf)
355
356 By default, they are not saved unless you select
357 "Save fit curves in session" from the File menu.""" % (nm, os.path.splitext(nm)[0]),
358 (("Don't Save", 0), ('Save', 1)), dont_show_again=False)
359 response, dont_show = dlg.run()
360 dlg.destroy()
361 self.file.store_fit_curves = response
362 self.prompt_store_fit_curves = not dont_show
363 break
373 if file:
374 if not (file.signals is None): self.tables.add_table(file.signals, None,
375 qubx.tableGTK.TableViewCtrls(file.signals, None,
376 ('Add computed signal...', self.add_computed_signal),
377 qubx.tableGTK.TableViewCtrls.REMOVE))
378 if not (view.signals is None): self.tables.add_table(view.signals)
379 if not (file.constants is None): self.tables.add_table(file.constants, None,
380 qubx.tableGTK.TableViewCtrls(file.constants, None,
381 qubx.tableGTK.TableViewCtrls.ADD,
382 qubx.tableGTK.TableViewCtrls.REMOVE))
383 if not (file.stimuli is None): self.tables.add_table(file.stimuli)
384 if not (file.segments is None): self.tables.add_table(file.segments, None,
385 qubx.tableGTK.TableViewCtrls(file.segments, None,
386 ('Copy row(s) to list...', self.copy_segments_to_list),
387 qubx.tableGTK.TableViewCtrls.CALCULATE,
388 qubx.tableGTK.TableViewCtrls.PLOT))
389 if not (file.lists is None):
390 file.lists.OnSwitchList += self.__ref(self.__onSwitchList)
391 self.__show_list_table(file.list)
392 self._file.OnChangePath += self.__ref(self.__onChangePath)
393 view.OnHint += self.__ref(self.__onHint)
394 view.hires.OnHint += self.__ref(self.__onHint)
395 self.about_notes.set_text(file.notes)
396 try:
397 self.itemFile.set_submenu(view.mnuFile)
398 self.itemLists.set_submenu(view.layLists.mnu)
399 self.itemDisplay.set_submenu(view.subDisplay.popup)
400 self.itemTools.set_submenu(view.hires.mnuOtherTools)
401 except:
402 self.itemFile.set_submenu(None)
403 self.itemLists.set_submenu(None)
404 self.itemDisplay.set_submenu(None)
405 self.itemTools.set_submenu(None)
406 view.hires.request_fit_controls = self.__onRqFit
407 if self.__showing_fit:
408 view.hires.layerset = self.controls_fit
409 if self.index >= (self.has_modeling and 1 or 0):
410 self.itemClose.show()
411 else:
412 self.itemClose.hide()
435 filters = []
436 for ext in sorted(qubx.data_types.Readers):
437 reader = qubx.data_types.GetReader(ext)
438 if ext == '.qdf':
439 filters.insert(0, (reader.description, reader.ext))
440 else:
441 filters.append((reader.description, reader.ext))
442 return filters
453 """Override: don't close the simulation data."""
454 self._no_to_all = False
455 self.done_sequence()
456 for i in reversed(xrange(1, self.table.size)):
457 self.index = i
458 if not self.close_one():
459 return False
460 return True
465 if self.__in_auto_session: return
466
467 QubX = qubx.pyenv.env.globals['QubX']
468 dlg = gtk.Dialog('%s - Auto-save session...' % QubX.appname, None, gtk.DIALOG_MODAL)
469 line = pack_item(gtk.HBox(), dlg.vbox)
470 chk = pack_check('Auto-save session (.qsf) every ', line, active=bool(self.__timeout_session))
471 txt = pack_item(qubx.GTK.NumEntry(self.__timeout_session_minutes, acceptIntGreaterThan(0), width_chars=4), line)
472 pack_label(' minutes', line)
473 dlg.add_button('Cancel', gtk.RESPONSE_REJECT)
474 dlg.add_button('OK', gtk.RESPONSE_ACCEPT)
475 response = dlg.run()
476 mins = chk.get_active() and txt.value or None
477 dlg.destroy()
478 if response == gtk.RESPONSE_ACCEPT:
479 qubx.pyenv.env.OnScriptable('QubX.Data.set_auto_save_session(minutes=%s)' % repr(mins))
480 self.set_auto_save_session(mins)
481 else:
482 self.__in_auto_session = True
483 self.itemAutoSaveSession.set_active(bool(self.__timeout_session))
484 self.__in_auto_session = False
485
487 if minutes:
488 self.__timeout_session_minutes = minutes
489 if self.__timeout_session:
490 gobject.source_remove(self.__timeout_session)
491 self.__in_auto_session = True
492 self.itemAutoSaveSession.set_active(bool(minutes))
493 self.__in_auto_session = False
494 if minutes:
495 self.__timeout_session = gobject.timeout_add(int(round(1000*60*minutes)), self.__onTimeoutSession)
496 else:
497 self.__timeout_session = None
504 if self.__ignore_store_fits: return
505 store = not self.file.store_fit_curves
506 qubx.pyenv.env.OnScriptable('QubX.Data.file.store_fit_curves = %s' % repr(store))
507 self.file.store_fit_curves = store
514 - def copy_segments_to_list(self, list_name=None, copy_rows_type=None, group=None, inverse=False, criteria=None):
515 tbn, crt, grp, inv, cta = qubx.tableGTK.prompt_copy_rows(self.file.segments, self.file.segments.label,
516 [l.list_name for l in self.file.lists.lists],
517 list_name, copy_rows_type, group, inverse, criteria)
518 if crt is None:
519 return
520 if crt == COPY_ROWS_ALL:
521 qubx.pyenv.env.OnScriptable('QubX.Data.copy_segments_to_list(list_name=%s, copy_rows_type=COPY_ROWS_ALL)' % repr(tbn))
522 elif crt == COPY_ROWS_GROUP:
523 qubx.pyenv.env.OnScriptable('QubX.Data.copy_segments_to_list(list_name=%s, copy_rows_type=COPY_ROWS_GROUP, group=%s, inverse=%s)'
524 % (repr(tbn), repr(grp), repr(inv)))
525 elif crt == COPY_ROWS_CRITERIA:
526 qubx.pyenv.env.OnScriptable('QubX.Data.copy_segments_to_list(list_name=%s, copy_rows_type=COPY_ROWS_CRITERIA, criteria=%s)'
527 % (repr(tbn),
528 repr(cta)))
529 elif crt == COPY_ROWS_CHECKED:
530 qubx.pyenv.env.OnScriptable('QubX.Data.copy_segments_to_list(list_name=%s, copy_rows_type=COPY_ROWS_CHECKED)' % repr(tbn))
531 self.file.lists.show_list(tbn)
532 to_list = self.file.list
533 self.tables.show_table(self.file.list)
534 if not self.file.segments:
535 rows = []
536 elif crt == COPY_ROWS_ALL:
537 rows = list(xrange(self.file.segments.size))
538 elif crt == COPY_ROWS_GROUP:
539 inv_op = (lambda x: not x) if inv else (lambda x: x)
540 tbl = self.file.segments
541 rows = [i for i in xrange(tbl.size) if inv_op(grp == tbl.get(i, 'Group'))]
542 elif crt == COPY_ROWS_CRITERIA:
543 rows = self.file.segments.rows_meeting_criteria(cta)
544 elif crt == COPY_ROWS_CHECKED:
545 rows = [i for i in xrange(tbl.size) if tbl.checked[i]]
546 for i in rows:
547 f, l, n = self.file.segmentation.segments[i]
548 row = self.file.segments.get_row(i).__dict__
549 entry = dict((f, row[f]) for f in self.file.segments.fields)
550 entry['From'] = f
551 entry['To'] = l
552 to_list.insert_selection(**entry)
553 return to_list
573 if not (self.__list_table is None):
574 self.tables.remove_table(self.__list_table)
575 self.__list_table = None
576 if not (item is None):
577 ref = Reffer()
578 mnuList = qubx.GTK.DynamicComboBox()
579 mnuList.OnPopulate += ref(self.__onPopulateLists)
580 mnuList.OnChanged += ref(self.__onChooseList)
581 mnuList.active_text = item.list_name
582 mnuListTools = gtk.Menu()
583 btnListTools = qubx.toolspace.Button_MenuLines(mnuListTools, 'List tools...', ref(bind(self.__onPopupListTools, mnuListTools)))
584 list_controls = qubx.tableGTK.TableViewCtrls(item, None,
585 'List:', mnuList,
586 ('New list...', self.new_list),
587 qubx.tableGTK.TableViewCtrls.REMOVE,
588 qubx.tableGTK.TableViewCtrls.ADD_FIELD,
589 qubx.tableGTK.TableViewCtrls.CALCULATE,
590 qubx.tableGTK.TableViewCtrls.PLOT,
591 btnListTools,
592 qubx.tableGTK.TableViewCtrls.CLOSE)
593 list_controls.ref = ref
594 list_controls.OnClickClose += ref(self.__onClickCloseList)
595 self.tables.add_table(item, None, list_controls)
596 self.tables.show_table(self.file.list)
597 self.tables.find_view('List').OnSortColumnChanged += ref(self.view.OnListSorted)
598 self.__list_table = item
599 else:
600 item = self.__list_table = qubx.data_types.SelectionList()
601 list_controls = qubx.tableGTK.TableViewCtrls(item, None, 'List: <none>', ('New list...', self.new_list))
602 self.tables.add_table(item, None, list_controls)
603 self.__list_table = item
635 - def copy_list(self, list_name=None, copy_rows_type=None, group=None, inverse=False, criteria=None):
636 tbn, crt, grp, inv, cta = qubx.tableGTK.prompt_copy_rows(self.file.list, self.file.list.list_name,
637 [l.list_name for l in self.file.lists.lists],
638 list_name, copy_rows_type, group, inverse, criteria)
639 if crt is None:
640 return
641 if crt == COPY_ROWS_ALL:
642 qubx.pyenv.env.OnScriptable('QubX.Data.copy_list(list_name=%s, copy_rows_type=COPY_ROWS_ALL)'
643 % repr(tbn))
644 elif crt == COPY_ROWS_GROUP:
645 qubx.pyenv.env.OnScriptable('QubX.Data.copy_list(list_name=%s, copy_rows_type=COPY_ROWS_GROUP, group=%s, inverse=%s)'
646 % (repr(tbn),
647 repr(grp), repr(inv)))
648 elif crt == COPY_ROWS_CRITERIA:
649 qubx.pyenv.env.OnScriptable('QubX.Data.copy_list(list_name=%s, copy_rows_type=COPY_ROWS_CRITERIA, criteria=%s)'
650 % (repr(tbn),
651 repr(cta)))
652 elif crt == COPY_ROWS_CHECKED:
653 qubx.pyenv.env.OnScriptable('QubX.Data.copy_list(list_name=%s, copy_rows_type=COPY_ROWS_CHECKED)'
654 % repr(tbn))
655 from_list = self.file.list
656 self.file.lists.show_list(tbn)
657 to_list = self.file.list
658 gobject.idle_add(self.tables.show_table, self.file.list)
659 to_list.copy_rows_from(from_list, copy_rows_type=crt, group=grp, inverse=inv, criteria=cta)
660 return to_list
665 lst = self.file.list
666 names = {} if (group_names is None) else group_names
667 groups = lst.groups_occupied(True)
668 for g in groups:
669 if not (g in names):
670 names[g] = g and ('%s-%i' % (lst.list_name, g)) or ""
671 values = qubx.GTK.PromptEntries([('This will build a new list from each non-zero Group in the old list.', None, None, None)] +
672 [('Group %i' % g, names[g], str, str) for g in groups],
673 "Split list by Group")
674 if values is None:
675 return
676 for i, g in enumerate(groups):
677 names[g] = values[i+1]
678 qubx.pyenv.env.OnScriptable('QubX.Data.split_list()')
679 for g in groups:
680 to_name = names[g]
681 if to_name:
682 to_list = self.file.lists.show_list(to_name)
683 to_list.copy_rows_from(lst, copy_rows_type=COPY_ROWS_GROUP, group=g, inverse=False)
729 new_signal_name = qubx.pyenvGTK.prompt_entry('Name of the new signal:', 'Untitled', str, str)
730 if not new_signal_name: return
731 name_hint = "Signal names: %s\n" % ', '.join(SafeName(name) for name in self.file.analog_sources.keys())
732 dlg = qubx.pyenvGTK.HelpTestDialog(self.file.accept_computed, "Enter an expression in terms of t (seconds) and the other signal names", "%s =" % new_signal_name,
733 COMPUTED_SIGNAL_HELP+name_hint+qubx.pyenv.PYTHON_HELP, self.help_bind, self.help_write_test)
734 response = dlg.run("")
735 expr = dlg.expr
736 dlg.destroy()
737 if response == gtk.RESPONSE_ACCEPT:
738 qubx.pyenv.env.OnScriptable('QubX.Data.file.signals.append({"Name" : %s, "Computed as" : %s})' % (repr(new_signal_name), repr(expr)))
739 self.file.signals.append({'Name' : new_signal_name, 'Computed as' : expr})
743 qubx.global_namespace.test_computed_signal = test_computed_signal
745 args = [(name, i+1) for i, name in enumerate(func.args)]
746 return "test_computed_signal(%s)" % (", ".join("%s=%s" % (arg, repr(val)) for arg, val in args))
747
748 COMPUTED_SIGNAL_HELP = """For example:
749
750 Current / Gain
751 Current / (Voltage - (-60.0))
752
753 """
754
755
756 qubx.dataGTK.Tools.register('list', 'Import...', lambda item: qubx.global_namespace.QubX.Data.import_list())
757 qubx.dataGTK.Tools.register('list', 'Export...', lambda item: qubx.global_namespace.QubX.Data.export_list())
758 qubx.dataGTK.Tools.register('list', 'Add onscreen selection...', lambda item: qubx.global_namespace.QubX.Data.list_add_screen())
759 qubx.dataGTK.Tools.register('list', 'Copy sel(s) to list...', lambda item: qubx.global_namespace.QubX.Data.copy_list())
760 qubx.dataGTK.Tools.register('list', 'Measure selections', lambda item: qubx.global_namespace.QubX.Data.measure_list())
761 qubx.dataGTK.Tools.register('list', 'Split list by Group...', lambda item: qubx.global_namespace.QubX.Data.split_list())
765 """Panel with source signal and file/screen; monitors QubX.DataSource and handles presets."""
766
767 __explore_featured = ['DataSource', 'chkFile', 'chkScreen', 'chkList', 'chkFilter', 'slideFreq', 'txtFreq', 'mnuSignal']
768
769 - def __init__(self, DataSource, global_name=""):
770 qubx.faces.Face.__init__(self, 'Data Source', global_name=global_name)
771 self.DataSource = DataSource
772 self.__ref = Reffer()
773 DataSource.OnChangeSource += self.__ref(self.__onChangeSource)
774 DataSource.OnChangeSignal += self.__ref(self.__onChangeSignal)
775 DataSource.OnChangeSignals += self.__ref(self.__onChangeSignals)
776 DataSource.OnChangeSamples += self.__ref(self.__onChangeSamples)
777 line = pack_item(gtk.HBox(), self)
778 pack_label('Data source:', line)
779 self.mnuSignal = pack_item(gtk.combo_box_new_text(), line, expand=True)
780 line = pack_item(gtk.HBox(), self)
781 self.chkFile = pack_radio('Whole File', line, expand=True,
782 active=(DataSource.source == qubx.dataGTK.DATASOURCE_FILE),
783 on_toggle=bind_with_args_before(self.__onToggleSource, qubx.dataGTK.DATASOURCE_FILE))
784 self.chkScreen = pack_radio('Screen', line, expand=True, group=self.chkFile,
785 active=(DataSource.source == qubx.dataGTK.DATASOURCE_SCREEN),
786 on_toggle=bind_with_args_before(self.__onToggleSource, qubx.dataGTK.DATASOURCE_SCREEN))
787 self.chkList = pack_radio('List', line, expand=True, group=self.chkFile,
788 active=(DataSource.source == qubx.dataGTK.DATASOURCE_LIST),
789 on_toggle=bind_with_args_before(self.__onToggleSource, qubx.dataGTK.DATASOURCE_LIST))
790 line = pack_item(gtk.HBox(), self)
791 self.chkFilter = pack_check('Filter', line, on_toggle=self.__onToggleFilter)
792 self.slideFreq = pack_item(qubx.GTK.Pow10Slider(powers=3), line, expand=True)
793 self.slideFreq.OnChange += self.__ref(self.__onSlideFreq)
794 self.txtFreq = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=4), line)
795 self.txtFreq.OnChange += self.__ref(self.__onChangeFreq)
796 pack_label('kHz', line)
797 self.__onChangeSignals(self.DataSource.signals)
798 self.__profile = qubx.settings.SettingsMgr['DataSource'].active
799 qubx.settings.SettingsMgr['DataSource'].OnSet = self.__ref(self.__onPreSet)
800 if self.__profile.find('Source').data:
801 DataSource.source = self.__profile['Source'].data[0]
802 if self.__profile.find('Signal').data:
803 DataSource.signal = self.__profile['Signal'].data[0]
804 self.mnuSignal.connect('changed', self.__onSignalChanged)
853