| Trees | Indices | Help |
|
|---|
|
|
1 """Panel which generates high-quality figures of the selections in active List.
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 import cStringIO
23 import itertools
24 import os
25 import sys
26 import time
27 import traceback
28 import gobject
29 import cairo
30 import gtk
31 import qubx.settings
32 import qubx.GTK
33 import qubx.pyenv
34 import qubx.tree
35 import qubx.faces
36 import qubx.task
37 import qubx.dataGTK
38 import qubx.notebook
39 import qubx.notebookGTK
40 import qubx.toolspace
41
42 from gtk import gdk
43 from gtk import keysyms
44 from itertools import izip, count
45 from qubx.util_types import *
46 from qubx.accept import *
47 from qubx.dataGTK import UNSET_IDL
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.pyenvGTK import prompt_entry
50 from qubx.settings import Property, Propertied
51 from numpy import arange, array, zeros
52 import numpy
53 from qubx.task import Tasks
54 from qubx.toolspace import measure_string, SubLayer_Label, SubLayer_Check, SubLayer_Radio, COLOR_LABEL, COLOR_CLEAR, LAYER_BG
55
56 LINE_EMS = 1.7
57
58 LISTFIG_POSITION_HIDDEN = 0
59 LISTFIG_POSITION_ABOVE = 1
60 LISTFIG_POSITION_BELOW = 2
61 LISTFIG_POSITION_RIGHT = 3
62 LISTFIG_POSITION_FLOAT = 4
63
64
65 COLOR_LF_BG = ('list_figure.bg', (1.0, 1.0, 1.0, 1.0))
66 qubx.toolspace.ColorInfo[COLOR_LF_BG[0]].label = "ListFigure background"
67 COLOR_LF_LABEL = ('list_figure.label', (0.0, 0.0, 0.0, 1.0))
68 qubx.toolspace.ColorInfo[COLOR_LF_LABEL[0]].label = "ListFigure text"
69 COLOR_LF_TRACE = ('list_figure.trace', (0.0, 0.0, 0.0, 1.0))
70 qubx.toolspace.ColorInfo[COLOR_LF_TRACE[0]].label = "ListFigure traces"
71 COLOR_LF_STIMULUS = ('list_figure.stimulus', (0.0, 0.0, 0.0, 1.0))
72 qubx.toolspace.ColorInfo[COLOR_LF_STIMULUS[0]].label = "ListFigure stimulus traces"
73 COLOR_LF_IDL = ('list_figure.idl', (1.0, 0.0, 0.0, 0.9))
74 qubx.toolspace.ColorInfo[COLOR_LF_IDL[0]].label = "ListFigure idealization"
75 COLOR_LF_STIMULUS_IDL = ('list_figure.stimulus.idl', (1.0, 0.0, 0.0, 0.9))
76 qubx.toolspace.ColorInfo[COLOR_LF_STIMULUS_IDL[0]].label = "ListFigure stimulus idealization"
77 COLOR_LF_FIT = ('list_figure.fit', (1.0, 0.0, 0.0, 0.8))
78 qubx.toolspace.ColorInfo[COLOR_LF_FIT[0]].label = "ListFigure fit curves"
79 COLOR_LF_ZERO = ('list_figure.zero', (0.0, 0.0, 0.0, 1.0))
80 qubx.toolspace.ColorInfo[COLOR_LF_ZERO[0]].label = "ListFigure zero line (baseline)"
81 COLOR_LF_FG_TROUBLE = ('list_figure.trouble.fg', (1.0, 1.0, 0.25, 0.9))
82 qubx.toolspace.ColorInfo[COLOR_LF_FG_TROUBLE[0]].label = 'ListFigure options attention foreground'
83 COLOR_LF_BG_TROUBLE = ('list_figure.trouble.bg', (0.6, 0.0, 0.0, 0.7))
84 qubx.toolspace.ColorInfo[COLOR_LF_BG_TROUBLE[0]].label = 'ListFigure options attention background'
85 COLOR_LF_OPTION = ('list_figure.option', (1, .8, .8, 1))
86 qubx.toolspace.ColorInfo[COLOR_LF_OPTION[0]].label = 'ListFigure option text'
87
88 COLOR_LF_TAB_SEL = ('list_figure.selected_tab', (0.0, 0.0, 0.0, 1.0))
89 qubx.toolspace.ColorInfo[COLOR_LF_TAB_SEL[0]].label = "ListFigure selected tab"
90 COLOR_LF_TAB = LAYER_BG
91
92 @Propertied( # Layout:
93 Property('output_w', 1600, 'Output width (pixels)'),
94 Property('output_h', 900, 'Output height (pixels)'),
95 Property('margin_h', 0.05, 'Size of left and right margins, as a proportion of total width'),
96 Property('margin_v', 0.05, 'Size of top and bottom margins, as a proportion of total height'),
97 Property('tracepad_h', 0.02, 'Size of whitespace to left and right of each trace, as a proportion of trace width'),
98 Property('tracepad_v', 0.02, 'Size of whitespace above and below each trace, as a proportion of trace height'),
99 Property('font_face', 'sans-serif', 'Label font'),
100 Property('font_weight', 800, 'Label font weight (1 <= font_weight <= 1000)'),
101 Property('font_height', 0.75, 'Label font height as a proportion of line height (excluding descenders)'),
102 Property('font_max_width', 0.25, 'Maximum label width as a proportion of line width'),
103 # Draw:
104 Property('draw_exact', 1, 'True to draw each datapoint, False to draw vertical distributions at each pixel'),
105 Property('dot_size', 0.02, 'Size of datapoint dots (or 0.0 to disable) as a proportion of trace height'),
106 Property('line_width', 0.01, 'Thickness of lines between datapoints (or 0.0 to disable) as a proportion of trace height'),
107 Property('line_opacity', 0.8, 'Opacity of lines (0.0 <= line_opacity <= 1.0)'),
108 Property('zero_line_width', 0.01, 'Thickness of baseline at y=0'),
109 # colors are shown in Draw panel but controlled by global Appearance.colors
110 # Traces:
111 Property('multi_file', 0, 'True to draw traces from multiple open files'),
112 Property('multi_file_group', 1, 'Group in Data table of files to be drawn'),
113 Property('trace_label_format', '%(Label)s', 'Format string for trace labels, using named fields of the List table'),
114 Property('trace_label_baseline', 0.1, 'distance between trace bottom and label bottom, as a proportion of trace height'),
115 Property('trace_sampled', 1, 'True to draw sampled data'),
116 Property('trace_idealized', 1, 'True to draw idealized data'),
117 Property('trace_idealized_above', 1, 'True to draw idealized data on its own line'),
118 Property('trace_fit', 1, 'True to draw fit curves'),
119 Property('trace_zero_line', 0, 'True to draw a line at y=0'),
120 # Stimulus:
121 Property('stimulus_label_format', '%(Name)s [%(Units)s]', 'Format string for stimulus labels, using named fields of the Scope table'),
122 Property('stimulus_label_baseline', 0.1, 'distance between stimulus bottom and label bottom, as a proportion of trace height'),
123 Property('stimulus_position', LISTFIG_POSITION_BELOW, 'location of stimulus display: LISTFIG_POSITION_HIDDEN, LISTFIG_POSITION_ABOVE, or LISTFIG_POSITION_BELOW',
124 value_names={LISTFIG_POSITION_HIDDEN: "LISTFIG_POSITION_HIDDEN", LISTFIG_POSITION_ABOVE: "LISTFIG_POSITION_ABOVE", LISTFIG_POSITION_BELOW: "LISTFIG_POSITION_BELOW"}),
125 Property('stimulus_trace', 0, 'index (in List) of trace whose stimulus is to be displayed'),
126 Property('stimulus_sampled', 1, 'True to draw sampled data'),
127 Property('stimulus_idealized', 1, 'True to draw idealized data'),
128 Property('stimulus_zero_line', 0, 'True to draw a line at y=0'),
129 # Zoom: e.g. zoom_min_Current, zoom_max_Current
130 # Scale Bars:
131 Property('scalebar_font_height', 0.2, 'as a proportion of trace height'),
132 Property('scalebar_number_format', '%.1g', 'Format string for scalebar numbers, e.g. %.1g'),
133 Property('scalebar_position', LISTFIG_POSITION_RIGHT, 'location of scale bars: LISTFIG_POSITION_HIDDEN, LISTFIG_POSITION_ABOVE, LISTFIG_POSITION_BELOW, LISTFIG_POSITION_RIGHT, or LISTFIG_POSITION_FLOAT',
134 value_names={LISTFIG_POSITION_HIDDEN: "LISTFIG_POSITION_HIDDEN", LISTFIG_POSITION_ABOVE: "LISTFIG_POSITION_ABOVE", LISTFIG_POSITION_BELOW: "LISTFIG_POSITION_BELOW",
135 LISTFIG_POSITION_RIGHT: "LISTFIG_POSITION_RIGHT", LISTFIG_POSITION_FLOAT: "LISTFIG_POSITION_FLOAT"}),
136 Property('scalebar_vertical_labels', 1, 'False to write all text horizontally'),
137 Property('scalebar_timebar', 0, 'True to draw a separate scale bar for the Time axis'),
138 Property('scalebar_timebar_x', 0.5, 'Horizontal position (center) of timebar, as a proportion of figure width'),
139 Property('scalebar_timebar_y', 0.9, 'Vertical position (center) of timebar, as a proportion of figure height'),
140 Property('scalebar_timebar_autodim', 1, 'False to specify dimension (length) manually'),
141 Property('scalebar_timebar_dim', 1.0, 'Length (in seconds) of timebar, if not scalebar_timebar_autodim')
142 )
144 """
145
146 Panel which generates high-quality figures from the selection list.
147
148 """
149
150 __explore_featured = ['plot', 'layHeaders', 'headers', 'subPresets', 'panels', 'subNotebook', 'nbPicture', 'nbScalebars',
151 'scalebar_signal', 'update', 'nb_scalebars_draw', 'prepare_draw']
152
154 """@param name: caption for this panel's tab"""
155 qubx.faces.Face.__init__(self, name, global_name)
156 self.__ref = Reffer()
157 self.set_size_request(100, 70)
158
159 self.plot = pack_item(qubx.toolspace.ToolSpace(), self, expand=True)
160 self.plot.OnDraw += self.__ref(self.__onDraw)
161 self.plot.OnPress += self.__ref(bind(self.__onClickHeader, None)) # clicking outside prefs panel hides it
162 self.layHeaders = qubx.toolspace.Layer(x=2, y=0.5, w=6*13+12+2+2, h=LINE_EMS, cBG=COLOR_CLEAR)
163 self.plot.add_layer(self.layHeaders)
164 self.headers = [SubLayer_Label('Layout', 0, 1, x=1, y=0, w=11, h=LINE_EMS, cBG=COLOR_LF_TAB, action=self.__ref(bind(self.__onClickHeader, 0))),
165 SubLayer_Label('Draw', 0, 1, x=1+1*13, y=0, w=11, h=LINE_EMS, cBG=COLOR_LF_TAB, action=self.__ref(bind(self.__onClickHeader, 1))),
166 SubLayer_Label('Traces', 0, 1, x=1+2*13, y=0, w=11, h=LINE_EMS, cBG=COLOR_LF_TAB, action=self.__ref(bind(self.__onClickHeader, 2))),
167 SubLayer_Label('Stimulus', 0, 1, x=1+3*13, y=0, w=11, h=LINE_EMS, cBG=COLOR_LF_TAB, action=self.__ref(bind(self.__onClickHeader, 3))),
168 SubLayer_Label('Zoom', 0, 1, x=1+4*13, y=0, w=11, h=LINE_EMS, cBG=COLOR_LF_TAB, action=self.__ref(bind(self.__onClickHeader, 4))),
169 SubLayer_Label('Scale Bars', 0, 1, x=1+5*13, y=0, w=11, h=LINE_EMS, cBG=COLOR_LF_TAB, action=self.__ref(bind(self.__onClickHeader, 5)))]
170 for hdr in self.headers:
171 self.layHeaders.add_sublayer(hdr)
172 self.subPresets = qubx.toolspace.SubLayer_Popup(color=COLOR_LABEL, on_popup=self.__ref(self.__onPresetsPopup), caption=' Presets...',
173 x=-16, y=0, w=12, h=LINE_EMS, cBG=COLOR_LF_TAB)
174 self.headers.append(self.layHeaders.add_sublayer(SubLayer_Label('?', 0, 1, x=-3.5, y=0, w=1.5, h=LINE_EMS, border=1, cBG=COLOR_LF_TAB,
175 action=self.__ref(bind(self.__onClickHeader, 6)))))
176 self.subPresets.popup = gtk.Menu()
177 self.layHeaders.add_sublayer(self.subPresets)
178
179 self.__scalebar_signal = None
180 self.propertied_connect_settings('ListFigure')
181
182 self.panels = []
183 self.__last_panel = None
184 pw = self.layHeaders.rq_w - 2 # margin
185 # Layout:
186 panel = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(10+4)*LINE_EMS)
187 self.panels.append(qubx.toolspace.LayerSet([panel]))
188 panel.add_sublayer(SubLayer_Label('Notebook output Width:', -1, 1, x=1, y=LINE_EMS, w=pw/2, h=LINE_EMS))
189 self.subOutputW = panel.add_sublayer(SubLayer_Label('%i'%self.output_w, -1, 1, x=1+pw/2, y=LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
190 tooltip="Width in pixels of the output from this panel's Notebook menu",
191 action=self.__ref(bind(self.__onClickEditProperty, 'output_w', 'Notebook output Width:', acceptIntGreaterThan(0))),
192 scroll=self.__ref(bind_with_args(self.__onScrollIntProperty, 'output_w', 1, None))))
193 panel.add_sublayer(SubLayer_Label('Notebook output Height:', -1, 1, x=1, y=2*LINE_EMS, w=pw/2, h=LINE_EMS))
194 self.subOutputH = panel.add_sublayer(SubLayer_Label('%i'%self.output_h, -1, 1, x=1+pw/2, y=2*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
195 tooltip="Height in pixels of the output from this panel's Notebook menu",
196 action=self.__ref(bind(self.__onClickEditProperty, 'output_h', 'Notebook output Height:', acceptIntGreaterThan(0))),
197 scroll=self.__ref(bind_with_args(self.__onScrollIntProperty, 'output_h', 1, None))))
198 panel.add_sublayer(SubLayer_Label('Margin (horizontal):', -1, 1, x=1, y=3*LINE_EMS, w=pw/2, h=LINE_EMS))
199 self.subMarginH = panel.add_sublayer(SubLayer_Label('%.2f'%self.margin_h, -1, 1, x=1+pw/2, y=3*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
200 tooltip="Size of left and right margins, as a proportion of total width",
201 action=self.__ref(bind(self.__onClickEditProperty, 'margin_h', 'Margin (horizontal):', acceptFloatBetween(0.0, 0.45))),
202 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'margin_h', 0.0, 0.45))))
203 panel.add_sublayer(SubLayer_Label('Margin (vertical):', -1, 1, x=1, y=4*LINE_EMS, w=pw/2, h=LINE_EMS))
204 self.subMarginV = panel.add_sublayer(SubLayer_Label('%.2f'%self.margin_v, -1, 1, x=1+pw/2, y=4*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
205 tooltip="Size of top and bottom margins, as a proportion of total width",
206 action=self.__ref(bind(self.__onClickEditProperty, 'margin_v', 'Margin (vertical):', acceptFloatBetween(0.0, 0.45))),
207 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'margin_v', 0.0, 0.45))))
208 panel.add_sublayer(SubLayer_Label('Trace padding (horizontal):', -1, 1, x=1, y=5*LINE_EMS, w=pw/2, h=LINE_EMS))
209 self.subTracepadH = panel.add_sublayer(SubLayer_Label('%.2f'%self.tracepad_h, -1, 1, x=1+pw/2, y=5*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
210 tooltip="Size of whitespace to left and right of each trace, as a proportion of trace width",
211 action=self.__ref(bind(self.__onClickEditProperty, 'tracepad_h', 'Trace padding (horizontal):', acceptFloatBetween(0.0, 0.45))),
212 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'tracepad_h', 0.0, 0.45))))
213 panel.add_sublayer(SubLayer_Label('Trace padding (vertical):', -1, 1, x=1, y=6*LINE_EMS, w=pw/2, h=LINE_EMS))
214 self.subTracepadV = panel.add_sublayer(SubLayer_Label('%.2f'%self.tracepad_v, -1, 1, x=1+pw/2, y=6*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
215 tooltip="Size of whitespace above and below each trace, as a proportion of trace width",
216 action=self.__ref(bind(self.__onClickEditProperty, 'tracepad_v', 'Trace padding (vertical):', acceptFloatBetween(0.0, 0.45))),
217 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'tracepad_v', 0.0, 0.45))))
218 panel.add_sublayer(SubLayer_Label('Font:', -1, 1, x=1, y=7*LINE_EMS, w=pw/2, h=LINE_EMS))
219 self.subFontFace = panel.add_sublayer(SubLayer_Label(self.font_face, -1, 1, x=1+pw/2, y=7*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
220 tooltip="Font name for labels (default: sans-serif)",
221 action=self.__ref(self.__onClickFontFace)))
222 panel.add_sublayer(SubLayer_Label('Font weight:', -1, 1, x=1, y=8*LINE_EMS, w=pw/2, h=LINE_EMS))
223 self.subFontWeight = panel.add_sublayer(SubLayer_Label('%i'%self.font_weight, -1, 1, x=1+pw/2, y=8*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
224 tooltip="Boldness of label font, between 1 and 1000",
225 action=self.__ref(bind(self.__onClickEditProperty, 'font_weight', 'Font weight:', acceptIntBetween(1, 1000))),
226 scroll=self.__ref(bind_with_args(self.__onScrollIntProperty, 'font_weight', 1, 1000))))
227 panel.add_sublayer(SubLayer_Label('Font height:', -1, 1, x=1, y=9*LINE_EMS, w=pw/2, h=LINE_EMS))
228 self.subFontHeight = panel.add_sublayer(SubLayer_Label('%.2f'%self.font_height, -1, 1, x=1+pw/2, y=9*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
229 tooltip="as a proportion of line height",
230 action=self.__ref(bind(self.__onClickEditProperty, 'font_height', 'Font height:', acceptFloatBetween(0.0, 1.0))),
231 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'font_height', 0.0, 1.0))))
232 panel.add_sublayer(SubLayer_Label('Max label width:', -1, 1, x=1, y=10*LINE_EMS, w=pw/2, h=LINE_EMS))
233 self.subFontMaxWidth = panel.add_sublayer(SubLayer_Label('%.2f'%self.font_max_width, -1, 1, x=1+pw/2, y=10*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
234 tooltip="as a proportion of line width",
235 action=self.__ref(bind(self.__onClickEditProperty, 'font_max_width', 'Max label width:', acceptFloatBetween(0.0, 1.0))),
236 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'font_max_width', 0.0, 1.0))))
237 panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=12*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
238 action=self.__ref(bind(self.__onClickHeader, None))))
239
240 # Draw:
241 colors = [COLOR_LF_BG, COLOR_LF_LABEL, COLOR_LF_TRACE, COLOR_LF_IDL, COLOR_LF_FIT, COLOR_LF_STIMULUS, COLOR_LF_STIMULUS_IDL, COLOR_LF_ZERO]
242 panel = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(5+len(colors)+4)*LINE_EMS)
243 self.panels.append(qubx.toolspace.LayerSet([panel]))
244 self.subDrawExact = SubLayer_Check(caption="Draw every point", tooltip="disables approximate drawing at lower resolutions",
245 active=self.draw_exact, color=COLOR_LF_OPTION,
246 x=1, y=LINE_EMS, w=pw, h=LINE_EMS)
247 self.subDrawExact.OnToggle += self.__ref(bind(self.__onToggleProperty, 'draw_exact'))
248 panel.add_sublayer(self.subDrawExact)
249 panel.add_sublayer(SubLayer_Label('Dot size:', -1, 1, x=1, y=2*LINE_EMS, w=pw/2, h=LINE_EMS))
250 self.subDotSize = panel.add_sublayer(SubLayer_Label('%.2f'%self.dot_size, -1, 1, x=1+pw/2, y=2*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
251 tooltip="as a proportion of trace height (or 0.0 for no dots)",
252 action=self.__ref(bind(self.__onClickEditProperty, 'dot_size', 'Dot size:', acceptFloatBetween(0.0, 0.3))),
253 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'dot_size', 0.0, 0.3))))
254 panel.add_sublayer(SubLayer_Label('Line width:', -1, 1, x=1, y=3*LINE_EMS, w=pw/2, h=LINE_EMS))
255 self.subLineWidth = panel.add_sublayer(SubLayer_Label('%.2f'%self.line_width, -1, 1, x=1+pw/2, y=3*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
256 tooltip="as a proportion of trace height (or 0.0 for no lines)",
257 action=self.__ref(bind(self.__onClickEditProperty, 'line_width', 'Line width:', acceptFloatBetween(0.0, 0.3))),
258 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'line_width', 0.0, 0.3))))
259 panel.add_sublayer(SubLayer_Label('Line opacity:', -1, 1, x=1, y=4*LINE_EMS, w=pw/2, h=LINE_EMS))
260 self.subLineOpacity = panel.add_sublayer(SubLayer_Label('%.2f'%self.line_opacity, -1, 1, x=1+pw/2, y=4*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
261 tooltip="between 0.0 (transparent) and 1.0 (opaque)",
262 action=self.__ref(bind(self.__onClickEditProperty, 'line_opacity', 'Line opacity:', acceptFloatBetween(0.0, 1.0))),
263 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'line_opacity', 0.0, 1.0))))
264 panel.add_sublayer(SubLayer_Label('Zero line (baseline) width:', -1, 1, x=1, y=5*LINE_EMS, w=pw/2, h=LINE_EMS))
265 self.subZeroLineWidth = panel.add_sublayer(SubLayer_Label('%.2f'%self.zero_line_width, -1, 1, x=1+pw/2, y=5*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
266 tooltip="as a proportion of trace height (or 0.0 for no baseline)",
267 action=self.__ref(bind(self.__onClickEditProperty, 'zero_line_width', 'Zero line width:', acceptFloatBetween(0.0, 0.3))),
268 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'zero_line_width', 0.0, 0.3))))
269 iline = 6
270 for color in colors:
271 panel.add_sublayer(SubLayer_Label(qubx.toolspace.ColorInfo[color[0]].label, -1, 1, x=1, y=iline*LINE_EMS, w=pw/2, h=LINE_EMS))
272 panel.add_sublayer(SubLayer_Label("<click to edit>", 0, 1, x=1+pw/2, y=iline*LINE_EMS, w=pw/2, h=LINE_EMS, cBG=color, border=1,
273 tooltip="these colors aren't part of this panel's Presets; instead they are controlled by the Presets in Admin:Settings",
274 action=self.__ref(bind(qubx.toolspace.EditOneColor, color))))
275 iline += 1
276 panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=(iline+1)*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
277 action=self.__ref(bind(self.__onClickHeader, None))))
278
279 # Traces:
280 panel = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(9+4)*LINE_EMS)
281 self.panels.append(qubx.toolspace.LayerSet([panel]))
282 self.subMultiFile = SubLayer_Check(caption="Multi-file", tooltip="Draws traces from all files in Data table with this Group number",
283 active=self.multi_file, color=COLOR_LF_OPTION,
284 x=1, y=LINE_EMS, w=pw/2, h=LINE_EMS)
285 self.subMultiFile.OnToggle += self.__ref(bind(self.__onToggleProperty, 'multi_file'))
286 panel.add_sublayer(self.subMultiFile)
287 panel.add_sublayer(SubLayer_Label('Group:', 1, 1, x=1, y=LINE_EMS, w=pw/2-1, h=LINE_EMS, color=COLOR_LF_OPTION, hover_color=COLOR_LF_OPTION))
288 self.subMultiFileGroup = panel.add_sublayer(SubLayer_Label('%i'%self.multi_file_group, -1, 1, x=1+pw/2, y=LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
289 tooltip="To pick which open data files to draw, set their Group in the Data table",
290 action=self.__ref(bind(self.__onClickEditProperty, 'multi_file_group', 'Group:', acceptIntGreaterThan(-1))),
291 scroll=self.__ref(bind_with_args(self.__onScrollIntProperty, 'multi_file_group', 0, None))))
292 panel.add_sublayer(SubLayer_Label('Trace label format:', -1, 1, x=1, y=2*LINE_EMS, w=pw/2, h=LINE_EMS))
293 self.subTraceLabelFormat = panel.add_sublayer(SubLayer_Label(self.trace_label_format, -1, 1, x=1+pw/2, y=2*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
294 tooltip="template using fields of the List table by name, e.g. %(Label)s",
295 action=self.__ref(bind(self.__onClickEditProperty, 'trace_label_format', 'Trace label format:'))))
296 panel.add_sublayer(SubLayer_Label('Trace label baseline:', -1, 1, x=1, y=3*LINE_EMS, w=pw/2, h=LINE_EMS))
297 self.subTraceLabelBaseline = panel.add_sublayer(SubLayer_Label('%.2f'%self.trace_label_baseline, -1, 1, x=1+pw/2, y=3*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
298 tooltip="distance from trace bottom, as a proportion of trace height'",
299 action=self.__ref(bind(self.__onClickEditProperty, 'trace_label_baseline', 'Trace label baseline:', acceptFloatBetween(0.0, 1.0))),
300 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'trace_label_baseline', 0.0, 1.0))))
301 panel.add_sublayer(SubLayer_Label('Draw signals:', -1, 1, x=1, y=4*LINE_EMS, w=pw/2, h=LINE_EMS))
302 self.subTraceSignals = panel.add_sublayer(SubLayer_Label("", -1, 1, x=1+pw/2, y=4*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
303 tooltip="click to pick which signals are drawn",
304 action=self.__ref(self.__onClickTraceSignals)))
305 self.subTraceSampled = SubLayer_Check(caption="Draw sampled data", tooltip="enables drawing sampled data points",
306 active=self.trace_sampled, color=COLOR_LF_OPTION,
307 x=1, y=5*LINE_EMS, w=pw, h=LINE_EMS)
308 self.subTraceSampled.OnToggle += self.__ref(bind(self.__onToggleProperty, 'trace_sampled'))
309 panel.add_sublayer(self.subTraceSampled)
310 self.subTraceIdealized = SubLayer_Check(caption="Draw idealized data", tooltip="enables drawing idealized events",
311 active=self.trace_idealized, color=COLOR_LF_OPTION,
312 x=1, y=6*LINE_EMS, w=pw, h=LINE_EMS)
313 self.subTraceIdealized.OnToggle += self.__ref(bind(self.__onToggleProperty, 'trace_idealized'))
314 panel.add_sublayer(self.subTraceIdealized)
315 self.subTraceIdealizedAbove = SubLayer_Check(caption="idealized data above sampled", tooltip="else drawn on top of sampled data",
316 active=self.trace_idealized_above, color=COLOR_LF_OPTION,
317 x=1, y=7*LINE_EMS, w=pw, h=LINE_EMS)
318 self.subTraceIdealizedAbove.OnToggle += self.__ref(bind(self.__onToggleProperty, 'trace_idealized_above'))
319 panel.add_sublayer(self.subTraceIdealizedAbove)
320 self.subTraceFit = SubLayer_Check(caption="Draw fit curves", tooltip="enables drawing fit curves from MacRates and least-squares curve fitting",
321 active=self.trace_fit, color=COLOR_LF_OPTION,
322 x=1, y=8*LINE_EMS, w=pw, h=LINE_EMS)
323 self.subTraceFit.OnToggle += self.__ref(bind(self.__onToggleProperty, 'trace_fit'))
324 panel.add_sublayer(self.subTraceFit)
325 self.subTraceZeroLine = SubLayer_Check(caption="Draw zero line (baseline)", tooltip="draws a line at y=0",
326 active=self.trace_zero_line, color=COLOR_LF_OPTION,
327 x=1, y=9*LINE_EMS, w=pw, h=LINE_EMS)
328 self.subTraceZeroLine.OnToggle += self.__ref(bind(self.__onToggleProperty, 'trace_zero_line'))
329 panel.add_sublayer(self.subTraceZeroLine)
330 panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=11*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
331 action=self.__ref(bind(self.__onClickHeader, None))))
332
333 # Stimulus
334 panel = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(8+4)*LINE_EMS)
335 self.panels.append(qubx.toolspace.LayerSet([panel]))
336 panel.add_sublayer(SubLayer_Label('Signal label format:', -1, 1, x=1, y=LINE_EMS, w=pw/2, h=LINE_EMS))
337 self.subStimulusLabelFormat = panel.add_sublayer(SubLayer_Label(self.stimulus_label_format, -1, 1, x=1+pw/2, y=LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
338 tooltip="template using fields of the Scope table by name, e.g. %(Name)s [%(Units)s]",
339 action=self.__ref(bind(self.__onClickEditProperty, 'stimulus_label_format', 'Signal label format:'))))
340 panel.add_sublayer(SubLayer_Label('Stimulus label baseline:', -1, 1, x=1, y=2*LINE_EMS, w=pw/2, h=LINE_EMS))
341 self.subStimulusLabelBaseline = panel.add_sublayer(SubLayer_Label('%.2f'%self.stimulus_label_baseline, -1, 1, x=1+pw/2, y=2*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
342 tooltip="distance from trace bottom, as a proportion of trace height'",
343 action=self.__ref(bind(self.__onClickEditProperty, 'stimulus_label_baseline', 'Stimulus label baseline:', acceptFloatBetween(0.0, 1.0))),
344 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'stimulus_label_baseline', 0.0, 1.0))))
345 panel.add_sublayer(SubLayer_Label('Stimulus position:', -1, 1, x=1, y=3*LINE_EMS, w=pw/4, h=LINE_EMS))
346 rad = self.subStimulusPositionAbove = SubLayer_Radio(label="Top", x=pw/4, y=3*LINE_EMS, w=pw/4, h=LINE_EMS,
347 active=(self.stimulus_position == LISTFIG_POSITION_ABOVE),
348 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
349 on_radio=self.__ref(self.__onRadioStimulusPosition), pos=LISTFIG_POSITION_ABOVE)
350 panel.add_sublayer(rad)
351 self.subStimulusPositionBelow = panel.add_sublayer(SubLayer_Radio(label="Bottom", fellow=rad, x=pw/2, y=3*LINE_EMS, w=pw/4, h=LINE_EMS, pos=LISTFIG_POSITION_BELOW,
352 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
353 active=(self.stimulus_position == LISTFIG_POSITION_BELOW)))
354 self.subStimulusPositionHidden = panel.add_sublayer(SubLayer_Radio(label="None", fellow=rad, x=3*pw/4, y=3*LINE_EMS, w=pw/4, h=LINE_EMS, pos=LISTFIG_POSITION_HIDDEN,
355 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
356 active=(self.stimulus_position == LISTFIG_POSITION_HIDDEN)))
357 self.subsStimulusPosition = [self.subStimulusPositionHidden, self.subStimulusPositionAbove, self.subStimulusPositionBelow]
358 panel.add_sublayer(SubLayer_Label('Draw signals:', -1, 1, x=1, y=4*LINE_EMS, w=pw/2, h=LINE_EMS))
359 self.subStimulusSignals = panel.add_sublayer(SubLayer_Label("", -1, 1, x=1+pw/2, y=4*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
360 tooltip="click to pick which stimulus signals are drawn",
361 action=self.__ref(self.__onClickStimulusSignals)))
362 panel.add_sublayer(SubLayer_Label('Index of example:', -1, 1, x=1, y=5*LINE_EMS, w=pw/2, h=LINE_EMS))
363 self.subStimulusTrace = panel.add_sublayer(SubLayer_Label('%i'%self.stimulus_trace, -1, 1, x=1+pw/2, y=5*LINE_EMS, w=pw/2, h=LINE_EMS, color=COLOR_LF_OPTION,
364 tooltip="Index of the trace (in the List table) whose stimulus is drawn",
365 action=self.__ref(bind(self.__onClickEditProperty, 'stimulus_trace', 'Index of example trace:', acceptIntGreaterThan(-1))),
366 scroll=self.__ref(bind_with_args(self.__onScrollIntProperty, 'stimulus_trace', 0, None))))
367 self.subStimulusSampled = SubLayer_Check(caption="Draw sampled data", tooltip="enables drawing sampled data points",
368 active=self.stimulus_sampled, color=COLOR_LF_OPTION,
369 x=1, y=6*LINE_EMS, w=pw, h=LINE_EMS)
370 self.subStimulusSampled.OnToggle += self.__ref(bind(self.__onToggleProperty, 'stimulus_sampled'))
371 panel.add_sublayer(self.subStimulusSampled)
372 self.subStimulusIdealized = SubLayer_Check(caption="Draw idealized data", tooltip="enables drawing idealized events",
373 active=self.stimulus_idealized, color=COLOR_LF_OPTION,
374 x=1, y=7*LINE_EMS, w=pw, h=LINE_EMS)
375 self.subStimulusIdealized.OnToggle += self.__ref(bind(self.__onToggleProperty, 'stimulus_idealized'))
376 panel.add_sublayer(self.subStimulusIdealized)
377 self.subStimulusZeroLine = SubLayer_Check(caption="Draw zero line (baseline)", tooltip="draws a line at y=0",
378 active=self.stimulus_zero_line, color=COLOR_LF_OPTION,
379 x=1, y=8*LINE_EMS, w=pw, h=LINE_EMS)
380 self.subStimulusZeroLine.OnToggle += self.__ref(bind(self.__onToggleProperty, 'stimulus_zero_line'))
381 panel.add_sublayer(self.subStimulusZeroLine)
382 panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=10*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
383 action=self.__ref(bind(self.__onClickHeader, None))))
384
385
386 # Zoom:
387 panel = self.panZoom = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(0+4)*LINE_EMS)
388 self.panels.append(qubx.toolspace.LayerSet([panel]))
389 self.subZoomClose = panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=2*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
390 action=self.__ref(bind(self.__onClickHeader, None))))
391
392
393 # Scale Bars:
394 panel = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(9+4)*LINE_EMS)
395 self.panels.append(qubx.toolspace.LayerSet([panel]))
396 panel.add_sublayer(SubLayer_Label('Number format:', -1, 1, x=1, y=LINE_EMS, w=pw/4, h=LINE_EMS))
397 self.subScalebarNumberFormat = panel.add_sublayer(SubLayer_Label(self.scalebar_number_format, -1, 1, x=1+pw/5, y=LINE_EMS, w=pw/5, h=LINE_EMS, color=COLOR_LF_OPTION,
398 tooltip="e.g. %.3g, look up 'printf' for examples",
399 action=self.__ref(bind(self.__onClickEditProperty, 'scalebar_number_format', 'Number format:'))))
400 panel.add_sublayer(SubLayer_Label('Font height:', -1, 1, x=1+(2*pw)/5, y=LINE_EMS, w=pw/5, h=LINE_EMS))
401 self.subScalebarFontHeight = panel.add_sublayer(SubLayer_Label('%.2f'%self.scalebar_font_height, -1, 1, x=1+(3*pw)/5, y=LINE_EMS, w=pw/5, h=LINE_EMS, color=COLOR_LF_OPTION,
402 tooltip="as a proportion of trace height",
403 action=self.__ref(bind(self.__onClickEditProperty, 'scalebar_font_height', 'Scale bar font height:', acceptFloatBetween(0.0, 1.0))),
404 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'scalebar_font_height', 0.0, 1.0))))
405 self.subScalebarVerticalLabels = SubLayer_Check(caption="Vertical labels", tooltip="writes text along the scale bar's axis, instead of horizontally",
406 active=self.scalebar_vertical_labels, color=COLOR_LF_OPTION,
407 x=1+(4*pw)/5, y=LINE_EMS, w=pw/5, h=LINE_EMS)
408 self.subScalebarVerticalLabels.OnToggle += self.__ref(bind(self.__onToggleProperty, 'scalebar_vertical_labels'))
409 panel.add_sublayer(SubLayer_Label('Scale bar position:', -1, 1, x=1, y=2*LINE_EMS, w=pw/6, h=LINE_EMS))
410 rad = self.subScalebarPositionAbove = SubLayer_Radio(label="Top", x=(2*pw)/6, y=2*LINE_EMS, w=pw/6, h=LINE_EMS,
411 active=(self.scalebar_position == LISTFIG_POSITION_ABOVE),
412 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
413 on_radio=self.__ref(self.__onRadioScalebarPosition), pos=LISTFIG_POSITION_ABOVE)
414 panel.add_sublayer(rad)
415 self.subScalebarPositionBelow = panel.add_sublayer(SubLayer_Radio(label="Bottom", fellow=rad, x=(3*pw)/6, y=2*LINE_EMS, w=pw/6, h=LINE_EMS, pos=LISTFIG_POSITION_BELOW,
416 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
417 active=(self.scalebar_position == LISTFIG_POSITION_BELOW)))
418 self.subScalebarPositionRight = panel.add_sublayer(SubLayer_Radio(label="Right", fellow=rad, x=(4*pw)/6, y=2*LINE_EMS, w=pw/6, h=LINE_EMS, pos=LISTFIG_POSITION_RIGHT,
419 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
420 active=(self.scalebar_position == LISTFIG_POSITION_RIGHT)))
421 self.subScalebarPositionFloat = panel.add_sublayer(SubLayer_Radio(label="Float", fellow=rad, x=(5*pw)/6, y=2*LINE_EMS, w=pw/6, h=LINE_EMS, pos=LISTFIG_POSITION_FLOAT,
422 tooltip='you position each scale bar independently, below',
423 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
424 active=(self.scalebar_position == LISTFIG_POSITION_FLOAT)))
425 self.subScalebarPositionHidden = panel.add_sublayer(SubLayer_Radio(label="None", fellow=rad, x=pw/6, y=2*LINE_EMS, w=pw/6, h=LINE_EMS, pos=LISTFIG_POSITION_HIDDEN,
426 active=(self.scalebar_position == LISTFIG_POSITION_HIDDEN),
427 color=COLOR_LF_OPTION, fill_color=COLOR_LF_OPTION,
428 tooltip="Use the Notebook to generate scale bars separately, then place them where you choose on the figure."))
429 self.subsScalebarPosition = [self.subScalebarPositionHidden, self.subScalebarPositionAbove, self.subScalebarPositionBelow, self.subScalebarPositionRight, self.subScalebarPositionFloat]
430 panel.add_sublayer(self.subScalebarVerticalLabels)
431 self.subScalebarTimebar = SubLayer_Check(caption="Time bar", tooltip="draws a separate scale bar for the time axis",
432 active=self.scalebar_timebar, color=COLOR_LF_OPTION,
433 x=1, y=3*LINE_EMS, w=10, h=LINE_EMS)
434 self.subScalebarTimebar.OnToggle += self.__ref(bind(self.__onToggleProperty, 'scalebar_timebar'))
435 panel.add_sublayer(self.subScalebarTimebar)
436 panel.add_sublayer(SubLayer_Label('at x:', 1, 1, x=13, y=3*LINE_EMS, w=4, h=LINE_EMS))
437 self.subScalebarTimebarX = panel.add_sublayer(SubLayer_Label('%.3f'%self.scalebar_timebar_x, -1, 1, x=18, y=3*LINE_EMS, w=8, h=LINE_EMS, color=COLOR_LF_OPTION,
438 tooltip="as a proportion of figure width",
439 action=self.__ref(bind(self.__onClickEditProperty, 'scalebar_timebar_x', 'Time bar X position:', acceptFloatBetween(0.0, 1.0))),
440 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'scalebar_timebar_x', 0.0, 1.0))))
441 panel.add_sublayer(SubLayer_Label('y:', 1, 1, x=27, y=3*LINE_EMS, w=4, h=LINE_EMS))
442 self.subScalebarTimebarY = panel.add_sublayer(SubLayer_Label('%.3f'%self.scalebar_timebar_y, -1, 1, x=32, y=3*LINE_EMS, w=8, h=LINE_EMS, color=COLOR_LF_OPTION,
443 tooltip="as a proportion of figure height",
444 action=self.__ref(bind(self.__onClickEditProperty, 'scalebar_timebar_y', 'Time bar Y position:', acceptFloatBetween(0.0, 1.0))),
445 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'scalebar_timebar_y', 0.0, 1.0))))
446 panel.add_sublayer(SubLayer_Label('Time bar width:', -1, 1, x=1, y=4*LINE_EMS, w=12, h=LINE_EMS))
447 self.subScalebarTimebarAutodim = panel.add_sublayer(SubLayer_Check(caption="Auto", tooltip="picks a round number small enough to fit within the height of one trace",
448 active=self.scalebar_timebar_autodim, color=COLOR_LF_OPTION,
449 x=13, y=4*LINE_EMS, w=8, h=LINE_EMS))
450 self.subScalebarTimebarAutodim.OnToggle += self.__ref(bind(self.__onToggleProperty, 'scalebar_timebar_autodim'))
451 panel.add_sublayer(SubLayer_Label('or', 1, 1, x=22, y=4*LINE_EMS, w=4, h=LINE_EMS))
452 self.subScalebarTimebarDim = panel.add_sublayer(SubLayer_Label('%.3g'%self.scalebar_timebar_dim, -1, 1, x=27, y=4*LINE_EMS, w=8, h=LINE_EMS, color=COLOR_LF_OPTION,
453 tooltip="width of the time bar, in seconds",
454 action=self.__ref(bind(self.__onClickEditProperty, 'scalebar_timebar_dim', 'Time bar width (seconds):', acceptFloatGreaterThan(0.0))),
455 scroll=self.__ref(bind_with_args(self.__onScrollFloatProperty, 'scalebar_timebar_dim', 0.0, None))))
456 panel.add_sublayer(SubLayer_Label('sec', -1, 1, x=36, y=4*LINE_EMS, w=2, h=LINE_EMS))
457
458 panel.add_sublayer(SubLayer_Label('Signal:', -1, 1, x=1, y=6*LINE_EMS, w=8, h=LINE_EMS))
459 self.subScalebarSignal = panel.add_sublayer(SubLayer_Label('foo', 0, 1, x=7, y=6*LINE_EMS, w=16, h=LINE_EMS, border=1, color=COLOR_LF_OPTION,
460 tooltip='click to pick a signal and show its options',
461 action=self.__ref(self.__onClickScalebarSignal)))
462 self.subScalebarSignalPrev = panel.add_sublayer(SubLayer_Label('<', 0, 1, x=26, y=6*LINE_EMS, w=LINE_EMS, h=LINE_EMS, border=1,
463 tooltip="show options for previous signal",
464 action=self.__ref(bind(lambda: (self.__scalebar_signal is None) or self.set_scalebar_signal(max(0, self.__scalebar_signal-1))))))
465 self.subScalebarSignalNext = panel.add_sublayer(SubLayer_Label('>', 0, 1, x=28, y=6*LINE_EMS, w=LINE_EMS, h=LINE_EMS, border=1,
466 tooltip="show options for next signal",
467 action=self.__ref(bind(lambda: (self.__scalebar_signal is None) or self.set_scalebar_signal(min(len(self.__signals)-1, self.__scalebar_signal+1))))))
468 self.subSsDrawScalebar = panel.add_sublayer(SubLayer_Check(caption='Draw scale bar', tooltip="", color=COLOR_LF_OPTION,
469 x=7, y=7*LINE_EMS, w=pw/5, h=LINE_EMS))
470 self.subSsDrawTimebar = panel.add_sublayer(SubLayer_Check(caption='Draw time bar', tooltip="otherwise, draws only a vertical line", color=COLOR_LF_OPTION,
471 x=7+pw/5, y=7*LINE_EMS, w=pw/5, h=LINE_EMS))
472 self.subSsInvertX = panel.add_sublayer(SubLayer_Check(caption='Invert X', tooltip="draws the time bar leftward from the origin", color=COLOR_LF_OPTION,
473 x=7+(2*pw)/5, y=7*LINE_EMS, w=pw/5, h=LINE_EMS))
474 self.subSsInvertY = panel.add_sublayer(SubLayer_Check(caption='Invert Y', tooltip="draws the scale bar downward from the origin", color=COLOR_LF_OPTION,
475 x=7+(3*pw)/5, y=7*LINE_EMS, w=pw/5, h=LINE_EMS))
476 panel.add_sublayer(SubLayer_Label('Height:', -1, 1, x=7, y=8*LINE_EMS, w=8, h=LINE_EMS))
477 self.subSsAutodim = panel.add_sublayer(SubLayer_Check(caption='Auto', tooltip="picks a round number small enough to fit within the height of one trace", color=COLOR_LF_OPTION,
478 x=16, y=8*LINE_EMS, w=8, h=LINE_EMS))
479 panel.add_sublayer(SubLayer_Label('or', 0, 1, x=25, y=8*LINE_EMS, w=2, h=LINE_EMS))
480 self.subSsDim = panel.add_sublayer(SubLayer_Label('x', -1, 1, x=28, y=8*LINE_EMS, w=12, h=LINE_EMS, color=COLOR_LF_OPTION, tooltip='Height of the scale bar, in data units'))
481 panel.add_sublayer(SubLayer_Label('[pos:right]', -1, 1, x=7, y=9*LINE_EMS, w=8, h=LINE_EMS))
482 self.subSsEveryTrace = panel.add_sublayer(SubLayer_Check(caption='in every trace', tooltip="otherwise, draws this scale bar only in the first relevant trace", color=COLOR_LF_OPTION,
483 x=15, y=9*LINE_EMS, w=8, h=LINE_EMS))
484 panel.add_sublayer(SubLayer_Label('[pos:float] X:', 1, 1, x=30, y=9*LINE_EMS, w=10, h=LINE_EMS))
485 self.subSsX = panel.add_sublayer(SubLayer_Label('x', -1, 1, x=41, y=9*LINE_EMS, w=8, h=LINE_EMS, color=COLOR_LF_OPTION, tooltip='horizontal position, as a proportion of figure width'))
486 panel.add_sublayer(SubLayer_Label('Y:', 1, 1, x=50, y=9*LINE_EMS, w=2, h=LINE_EMS))
487 self.subSsY = panel.add_sublayer(SubLayer_Label('x', -1, 1, x=53, y=9*LINE_EMS, w=8, h=LINE_EMS, color=COLOR_LF_OPTION, tooltip='vertical position, as a proportion of figure height'))
488
489 panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=11*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
490 action=self.__ref(bind(self.__onClickHeader, None))))
491
492 # ?
493 panel = qubx.toolspace.Layer(x=self.layHeaders.rq_x, y=self.layHeaders.rq_y+self.layHeaders.rq_h, w=self.layHeaders.rq_w, h=(5+4)*LINE_EMS)
494 self.panels.append(qubx.toolspace.LayerSet([panel]))
495 panel.add_sublayer(SubLayer_Label("This panel generates publication figures from the data in a List.",
496 -1, 1, x=1, y=LINE_EMS, w=pw, h=LINE_EMS))
497 panel.add_sublayer(SubLayer_Label("To use: build a List of selections in the Data panel, edit their Labels in the List table, and edit signal Name and Units in the Scope table.",
498 -1, 1, x=1, y=2*LINE_EMS, w=pw, h=LINE_EMS))
499 panel.add_sublayer(SubLayer_Label("Click the headers (Layout, Draw, ...) for options.",
500 -1, 1, x=1, y=3*LINE_EMS, w=pw, h=LINE_EMS))
501 panel.add_sublayer(SubLayer_Label("Set the output dimensions under Layout, then click the Notebook icon (at lower left) to copy or save the image.",
502 -1, 1, x=1, y=5*LINE_EMS, w=pw, h=LINE_EMS))
503 panel.add_sublayer(SubLayer_Label('Close', 0, 1, x=pw/2, y=7*LINE_EMS, w=10, h=1.2*LINE_EMS, cBG=LAYER_BG, border=1,
504 action=self.__ref(bind(self.__onClickHeader, None))))
505
506
507 ww = self.layHeaders.rq_x + self.layHeaders.rq_w - 4 - LINE_EMS
508 self.layWarnings = self.plot.add_layer(qubx.toolspace.Layer(x=4, y=-2*LINE_EMS, w=ww+LINE_EMS, h=LINE_EMS, cBG=COLOR_CLEAR))
509 self.subWarnings = self.layWarnings.add_sublayer(SubLayer_Label("", 0, 1, w=-LINE_EMS, h=LINE_EMS, color=COLOR_LF_FG_TROUBLE, hover_color=COLOR_LF_BG))
510
511 QubX = qubx.global_namespace.QubX
512 self.__datafile = self.__dataview = None
513 self.__signals = []
514 QubX.Data.OnSwitch += self.__ref(self.__onSwitchData)
515 QubX.Data.table.OnSet += self.__ref(self.__onSetDataTable)
516
517 self.layNotebook = qubx.toolspace.Layer(x=1, y=-3, w=2, h=2, cBG=COLOR_CLEAR)
518 self.subNotebook = qubx.notebookGTK.SubLayer_Notebook(x=0, y=0, w=2, h=2)
519 self.layNotebook.add_sublayer(self.subNotebook)
520 self.plot.add_layer(self.layNotebook)
521 self.nbPicture = qubx.notebookGTK.NbPicture('Picture', self.global_name and ('%s.nbPicture'%self.global_name) or '',
522 self.nb_picture_get_shape,
523 self.nb_picture_draw)
524 self.nbScalebars = qubx.notebookGTK.NbPicture('Scale Bars', self.global_name and ('%s.nbScalebars'%self.global_name) or '',
525 self.nb_scalebars_get_shape,
526 self.nb_scalebars_draw)
527 self.subNotebook.items.append(self.nbPicture)
528 self.subNotebook.items.append(self.nbScalebars)
529
530 self.__onSwitchData(QubX.Data, QubX.Data.file)
531 self.__onClickHeader(6) # show info
532 scalebar_signal = property(lambda self: self.__scalebar_signal, lambda self, x: self.set_scalebar_signal(x), 'Index of signal whose scale bars are being edited')
534 ref = self.__scalebar_signal_ref = Reffer() # clears stale event handlers
535 self.__scalebar_signal = x
536 if not (0 <= x < len(self.__signals)):
537 x = None
538 if x is None:
539 self.subScalebarSignal.label = '<none>'
540 else:
541 rec = self.__signals[x]
542 self.subScalebarSignal.label = rec.Name
543 self.subSsDrawScalebar.active = bool(getattr(self, "%s_in_scalebar"%rec.Name))
544 self.subSsDrawTimebar.active = bool(getattr(self, "%s_sb_timebar"%rec.Name))
545 self.subSsInvertX.active = bool(getattr(self, "%s_sb_invert_x"%rec.Name))
546 self.subSsInvertY.active = bool(getattr(self, "%s_sb_invert_y"%rec.Name))
547 self.subSsAutodim.active = bool(getattr(self, "%s_sb_autodim"%rec.Name))
548 self.subSsDim.label = '%.3g' % getattr(self, "%s_sb_dim"%rec.Name)
549 self.subSsEveryTrace.active = bool(getattr(self, "%s_sb_every_trace"%rec.Name))
550 self.subSsX.label = '%.3f' % getattr(self, "%s_sb_float_x"%rec.Name)
551 self.subSsY.label = '%.3f' % getattr(self, "%s_sb_float_y"%rec.Name)
552 self.subSsDrawScalebar.OnToggle += ref(bind(self.__onToggleProperty, '%s_in_scalebar'%rec.Name))
553 self.subSsDrawTimebar.OnToggle += ref(bind(self.__onToggleProperty, '%s_sb_timebar'%rec.Name))
554 self.subSsInvertX.OnToggle += ref(bind(self.__onToggleProperty, '%s_sb_invert_x'%rec.Name))
555 self.subSsInvertY.OnToggle += ref(bind(self.__onToggleProperty, '%s_sb_invert_y'%rec.Name))
556 self.subSsAutodim.OnToggle += ref(bind(self.__onToggleProperty, '%s_sb_autodim'%rec.Name))
557 self.subSsDim.action = ref(bind(self.__onClickEditProperty, '%s_sb_dim'%rec.Name, 'Scale bar width:', acceptFloatGreaterThan(0.0)))
558 self.subSsDim.scroll = ref(bind_with_args(self.__onScrollFloatProperty, '%s_sb_dim'%rec.Name, 0.0, None))
559 self.subSsEveryTrace.OnToggle += ref(bind(self.__onToggleProperty, '%s_sb_every_trace'%rec.Name))
560 self.subSsX.action = ref(bind(self.__onClickEditProperty, '%s_sb_float_x'%rec.Name, 'Scale bar X position:', acceptFloatBetween(0.0, 1.0)))
561 self.subSsX.scroll = ref(bind_with_args(self.__onScrollFloatProperty, '%s_sb_float_x'%rec.Name, 0.0, 1.0))
562 self.subSsY.action = ref(bind(self.__onClickEditProperty, '%s_sb_float_y'%rec.Name, 'Scale bar Y position:', acceptFloatBetween(0.0, 1.0)))
563 self.subSsY.scroll = ref(bind_with_args(self.__onScrollFloatProperty, '%s_sb_float_y'%rec.Name, 0.0, 1.0))
565 super(ListFigureFace, self).propertied_set(value, name)
566 if name == 'output_w':
567 self.subOutputW.label = '%i' % value
568 elif name == 'output_h':
569 self.subOutputH.label = '%i' % value
570 elif name == 'margin_h':
571 self.subMarginH.label = '%.2f' % value
572 elif name == 'margin_v':
573 self.subMarginV.label = '%.2f' % value
574 elif name == 'tracepad_h':
575 self.subTracepadH.label = '%.2f' % value
576 elif name == 'tracepad_v':
577 self.subTracepadV.label = '%.2f' % value
578 elif name == 'font_face':
579 self.subFontFace.label = value
580 elif name == 'font_weight':
581 self.subFontWeight.label = '%i' % value
582 elif name == 'font_height':
583 self.subFontHeight.label = '%.2f' % value
584 elif name == 'font_max_width':
585 self.subFontMaxWidth.label = '%.2f' % value
586 elif name == 'draw_exact':
587 self.subDrawExact.active = bool(value)
588 elif name == 'dot_size':
589 self.subDotSize.label = '%.2f' % value
590 elif name == 'line_width':
591 self.subLineWidth.label = '%.2f' % value
592 elif name == 'line_opacity':
593 self.subLineOpacity.label = '%.2f' % value
594 elif name == 'zero_line_width':
595 self.subZeroLineWidth.label = '%.2f' % value
596 elif name == 'multi_file':
597 self.subMultiFile.active = bool(value)
598 elif name == 'multi_file_group':
599 self.subMultiFileGroup.label = '%i' % value
600 elif name == 'trace_label_format':
601 self.subTraceLabelFormat.label = value
602 elif name == 'trace_label_baseline':
603 self.subTraceLabelBaseline.label = '%.2f' % value
604 elif name == 'trace_sampled':
605 self.subTraceSampled.active = bool(value)
606 elif name == 'trace_idealized':
607 self.subTraceIdealized.active = bool(value)
608 elif name == 'trace_idealized_above':
609 self.subTraceIdealizedAbove.active = bool(value)
610 elif name == 'trace_fit':
611 self.subTraceFit.active = bool(value)
612 elif name == 'trace_zero_line':
613 self.subTraceZeroLine.active = bool(value)
614 elif name == 'stimulus_label_format':
615 self.subStimulusLabelFormat.label = value
616 elif name == 'stimulus_label_baseline':
617 self.subStimulusLabelBaseline.label = '%.2f' % value
618 elif name == 'stimulus_position':
619 self.subsStimulusPosition[value].active = True
620 elif name == 'stimulus_trace':
621 self.subStimulusTrace.label = '%i' % value
622 elif name == 'stimulus_sampled':
623 self.subStimulusSampled.active = bool(value)
624 elif name == 'stimulus_idealized':
625 self.subStimulusIdealized.active = bool(value)
626 elif name == 'stimulus_zero_line':
627 self.subStimulusZeroLine.active = bool(value)
628 elif name == 'scalebar_number_format':
629 self.subScalebarNumberFormat.label = value
630 elif name == 'scalebar_font_height':
631 self.subScalebarFontHeight.label = '%.2f' % value
632 elif name == 'scalebar_position':
633 self.subsScalebarPosition[value].active = True
634 elif name == 'scalebar_vertical_labels':
635 self.subScalebarVerticalLabels.active = bool(value)
636 elif name == 'scalebar_timebar':
637 self.subScalebarTimebar.active = bool(value)
638 elif name == 'scalebar_timebar_x':
639 self.subScalebarTimebarX.label = '%.3f' % value
640 elif name == 'scalebar_timebar_y':
641 self.subScalebarTimebarY.label = '%.3f' % value
642 elif name == 'scalebar_timebar_autodim':
643 self.subScalebarTimebarAutodim.active = bool(value)
644 elif name == 'scalebar_timebar_dim':
645 self.subScalebarTimebarDim.label = '%.3f' % value
646 else:
647 found = [None] # boxed for modification below
648 def is_signal_prop(pname):
649 match = re.match("(.*)_%s"%pname, name)
650 if match:
651 for rec in self.__signals:
652 if rec.Name == match.group(1):
653 found[0] = rec
654 return True
655 return False
656 if is_signal_prop('in_trace'):
657 self.__update_trace_signals()
658 elif is_signal_prop('in_stimulus'):
659 self.__update_stimulus_signals()
660 elif is_signal_prop('min'):
661 found[0].lbl_min.label = '%.3g' % value
662 elif is_signal_prop('max'):
663 found[0].lbl_max.label = '%.3g' % value
664 elif is_signal_prop('in_scalebar'):
665 if self.__signals.index(found[0]) == self.__scalebar_signal:
666 self.subSsDrawScalebar.active = bool(value)
667 elif is_signal_prop('sb_timebar'):
668 if self.__signals.index(found[0]) == self.__scalebar_signal:
669 self.subSsDrawTimebar.active = bool(value)
670 elif is_signal_prop('sb_invert_x'):
671 if self.__signals.index(found[0]) == self.__scalebar_signal:
672 self.subSsInvertX.active = bool(value)
673 elif is_signal_prop('sb_invert_y'):
674 if self.__signals.index(found[0]) == self.__scalebar_signal:
675 self.subSsInvertY.active = bool(value)
676 elif is_signal_prop('sb_autodim'):
677 if self.__signals.index(found[0]) == self.__scalebar_signal:
678 self.subSsAutodim.active = bool(value)
679 elif is_signal_prop('sb_dim'):
680 if self.__signals.index(found[0]) == self.__scalebar_signal:
681 self.subSsDim.label = '%.3g' % value
682 elif is_signal_prop('sb_every_trace'):
683 if self.__signals.index(found[0]) == self.__scalebar_signal:
684 self.subSsEveryTrace.active = bool(value)
685 elif is_signal_prop('sb_float_x'):
686 if self.__signals.index(found[0]) == self.__scalebar_signal:
687 self.subSsX.label = '%.3f' % value
688 elif is_signal_prop('sb_float_y'):
689 if self.__signals.index(found[0]) == self.__scalebar_signal:
690 self.subSsY.label = '%.3f' % value
691 if not (name in ('output_w', 'output_h')):
692 self.update()
693
695 return ";".join(rec.Name for rec in self.__signals if getattr(self, "%s%s" % (rec.Name, tag)))
697 self.subTraceSignals.label = self.__signals_to_string("_in_trace")
698 if self.subTraceSignals.label:
699 self.subTraceSignals.cBG = COLOR_CLEAR
700 elif self.__signals:
701 self.subTraceSignals.cBG = COLOR_LF_BG_TROUBLE
702 self.__onClickHeader(2)
706 menu = gtk.Menu()
707 for rec in self.__signals:
708 active = getattr(self, "%s%s" % (rec.Name, tag))
709 build_menuitem(rec.Name, rec.ref(bind(self.__onClickSignal, rec, tag, active)),
710 active=active, item_class=gtk.CheckMenuItem, menu=menu)
711 menu.popup(None, None, None, 0, 0)
715 ref = self.__ref_scalebar_signal = Reffer()
716 menu = gtk.Menu()
717 for i, rec in enumerate(self.__signals):
718 build_menuitem(rec.Name, ref(bind(self.set_scalebar_signal, i)), menu=menu, active=self.__scalebar_signal==i, item_class=gtk.CheckMenuItem)
719 menu.popup(None, None, None, 0, 0)
720
722 self.subPresets.popup.foreach(lambda item: self.subPresets.popup.remove(item))
723 self.subPresets.ref = Reffer()
724 for name in qubx.settings.SettingsMgr['ListFigure'].listNames():
725 build_menuitem(name, self.subPresets.ref(bind(self.__use_preset, name)), menu=self.subPresets.popup)
726 build_menuitem('Manage...', self.__ref(self.__onClickManagePresets), menu=self.subPresets.popup)
727 build_menuitem('Add to menu...', self.__ref(self.__onClickAddPreset), menu=self.subPresets.popup)
728 return True
730 qubx.pyenv.env.OnScriptable('SettingsMgr["ListFigure"].load(%s)' % repr(name))
731 qubx.settings.SettingsMgr['ListFigure'].load(name)
733 dlg = qubx.settingsGTK.PresetsDialog('ListFigure', self.parent_window)
734 dlg.run()
735 dlg.destroy()
737 name = prompt_entry('Preset name:', '', str, '%s', 'ListFigure presets: Add to menu...', self.parent_window)
738 if not name: return
739 qubx.pyenv.env.OnScriptable('SettingsMgr["ListFigure"].save(%s)' % repr(name))
740 qubx.settings.SettingsMgr['ListFigure'].save(name)
741
743 self.update()
745 if (field == 'Group') and self.multi_file and (self.multi_file_group in (val, prev)):
746 self.update()
748 if self.__datafile:
749 self.__dataview.OnListSorted -= self.__ref(self.__onChangeList)
750 self.__dataview.signals.OnInsert -= self.__ref(self.__onInsertSignal)
751 self.__dataview.signals.OnRemoving -= self.__ref(self.__onRemovingSignal)
752 self.__dataview.signals.OnSet -= self.__ref(self.__onSetSignal)
753 for i in reversed(xrange(1, self.__dataview.signals.size)):
754 self.__onRemovingSignal(i, False)
755 self.__datafile.lists.OnChangeList -= self.__ref(self.__onChangeList)
756 self.__datafile.lists.OnSwitchList -= self.__ref(self.__onChangeList)
757 self.__datafile.OnChangeSamples -= self.__ref(self.__onSamplesChanged)
758 self.__datafile.OnChangeIdealization -= self.__ref(self.__onSamplesChanged)
759 self.__datafile.OnChangeFits -= self.__ref(self.__onSamplesChanged)
760 self.__datafile = file if (not (file.signals is None)) else None
761 if self.__datafile:
762 self.__dataview = Data.view
763 self.__dataview.OnListSorted += self.__ref(self.__onChangeList)
764 self.__datafile.lists.OnChangeList += self.__ref(self.__onChangeList)
765 self.__datafile.lists.OnSwitchList += self.__ref(self.__onChangeList)
766 for i in xrange(1, self.__dataview.signals.size):
767 self.__onInsertSignal(i, False)
768 self.__dataview.signals.OnInsert += self.__ref(self.__onInsertSignal)
769 self.__dataview.signals.OnRemoving += self.__ref(self.__onRemovingSignal)
770 self.__dataview.signals.OnSet += self.__ref(self.__onSetSignal)
771 # assuming signal names don't change
772 self.__datafile.OnChangeSamples += self.__ref(self.__onSamplesChanged)
773 self.__datafile.OnChangeIdealization += self.__ref(self.__onSamplesChanged)
774 self.__datafile.OnChangeFits += self.__ref(self.__onSamplesChanged)
775 else:
776 self.__dataview = None
777 self.scalebar_signal = self.__scalebar_signal
778 self.update()
780 self.update()
781 if self.__datafile and self.__datafile.list and self.__datafile.list.size and ((self.stimulus_trace >= self.__datafile.list.size - 1)):
782 self.stimulus_trace = self.__datafile.list.size - 1
784 ix = rec.Index
785 if not hasattr(self, '%s_in_trace' % rec.Name): # original properties
786 if prev_name is None: # use defaults
787 lo, hi = self.__dataview.display_limits(ix)
788 self.propertied_add_property(Property('%s_in_trace' % rec.Name, (rec.Center != qubx.dataGTK.SIGNAL_CENTER_HIDDEN), ""))
789 self.propertied_add_property(Property('%s_in_stimulus' % rec.Name, 0, ""))
790 self.propertied_add_property(Property('%s_in_scalebar' % rec.Name, (rec.Center != qubx.dataGTK.SIGNAL_CENTER_HIDDEN), ""))
791 self.propertied_add_property(Property('%s_min' % rec.Name, lo, ""))
792 self.propertied_add_property(Property('%s_max' % rec.Name, hi, ""))
793 else: # use properties from previous signal name
794 self.propertied_add_property(Property('%s_in_trace' % rec.Name, getattr(self, '%s_in_trace'%prev_name), ""))
795 self.propertied_add_property(Property('%s_in_stimulus' % rec.Name, getattr(self, '%s_in_stimulus'%prev_name), ""))
796 self.propertied_add_property(Property('%s_in_scalebar' % rec.Name, getattr(self, '%s_in_scalebar'%prev_name), ""))
797 self.propertied_add_property(Property('%s_min' % rec.Name, getattr(self, '%s_min'%prev_name), ""))
798 self.propertied_add_property(Property('%s_max' % rec.Name, getattr(self, '%s_max'%prev_name), ""))
799 if not hasattr(self, '%s_scalebar_timebar' % rec.Name): # second-gen properties
800 if prev_name is None:
801 self.propertied_add_property(Property('%s_sb_timebar' % rec.Name, 1, ""))
802 self.propertied_add_property(Property('%s_sb_invert_x' % rec.Name, 1, ""))
803 self.propertied_add_property(Property('%s_sb_invert_y' % rec.Name, 0, ""))
804 self.propertied_add_property(Property('%s_sb_autodim' % rec.Name, 1, ""))
805 self.propertied_add_property(Property('%s_sb_dim' % rec.Name, 1.0, ""))
806 self.propertied_add_property(Property('%s_sb_every_trace' % rec.Name, 0, ""))
807 self.propertied_add_property(Property('%s_sb_float_x' % rec.Name, 0.9, ""))
808 self.propertied_add_property(Property('%s_sb_float_y' % rec.Name, 0.9, ""))
809 else:
810 self.propertied_add_property(Property('%s_sb_timebar' % rec.Name, getattr(self, '%s_sb_timebar'%prev_name), ""))
811 self.propertied_add_property(Property('%s_sb_invert_x' % rec.Name, getattr(self, '%s_sb_invert_x'%prev_name), ""))
812 self.propertied_add_property(Property('%s_sb_invert_y' % rec.Name, getattr(self, '%s_sb_invert_y'%prev_name), ""))
813 self.propertied_add_property(Property('%s_sb_autodim' % rec.Name, getattr(self, '%s_sb_autodim'%prev_name), ""))
814 self.propertied_add_property(Property('%s_sb_dim' % rec.Name, getattr(self, '%s_sb_dim'%prev_name), ""))
815 self.propertied_add_property(Property('%s_sb_every_trace' % rec.Name, getattr(self, '%s_sb_every_trace'%prev_name), ""))
816 self.propertied_add_property(Property('%s_sb_float_x' % rec.Name, getattr(self, '%s_sb_float_x'%prev_name), ""))
817 self.propertied_add_property(Property('%s_sb_float_y' % rec.Name, getattr(self, '%s_sb_float_y'%prev_name), ""))
818 pw = self.layHeaders.rq_w - 2 # margin
819 new_controls = False
820 if rec.caption_min:
821 rec.caption_min.label = '%s min:' % rec.Name
822 rec.lbl_min.label = '%.3g' % getattr(self, "%s_min"%rec.Name)
823 rec.caption_max.label = '%s max:' % rec.Name
824 rec.lbl_max.label = '%.3g' % getattr(self, "%s_max"%rec.Name)
825 else:
826 rec.caption_min = self.panZoom.add_sublayer(SubLayer_Label('%s min:' % rec.Name, -1, 1, x=1, y=ix*LINE_EMS, w=pw/4, h=LINE_EMS))
827 rec.lbl_min = self.panZoom.add_sublayer(SubLayer_Label('%.3g' % getattr(self, "%s_min"%rec.Name), -1, 1, x=1+pw/4, y=ix*LINE_EMS, w=pw/4, h=LINE_EMS, color=COLOR_LF_OPTION))
828 rec.caption_max = self.panZoom.add_sublayer(SubLayer_Label('%s max:' % rec.Name, -1, 1, x=1+pw/2, y=ix*LINE_EMS, w=pw/4, h=LINE_EMS))
829 rec.lbl_max = self.panZoom.add_sublayer(SubLayer_Label('%.3g' % getattr(self, "%s_max"%rec.Name), -1, 1, x=1+3*pw/4, y=ix*LINE_EMS, w=pw/4-6-LINE_EMS, h=LINE_EMS, color=COLOR_LF_OPTION))
830 rec.btn_invert = self.panZoom.add_sublayer(SubLayer_Label('Invert', 0, 1, x=-LINE_EMS-8, y=ix*LINE_EMS, w=7, h=LINE_EMS, border=1,
831 tooltip="Displays this signal upside-down",
832 action=rec.ref(bind(self.__onClickInvert, rec))))
833 rec.btn_grab = self.panZoom.add_sublayer(SubLayer_Label('?', 0, 1, x=-LINE_EMS-1, y=ix*LINE_EMS, w=LINE_EMS, h=LINE_EMS, border=1,
834 tooltip="Updates %s min and max from the Data panel" % rec.Name,
835 action=rec.ref(bind(self.__onClickGrabZoom, rec))))
836 new_controls = True
837 if prev_name or new_controls:
838 rec.lbl_min.action = rec.ref(bind(self.__onClickEditProperty, '%s_min'%rec.Name, '%s min:'%rec.Name, acceptFloat))
839 rec.lbl_min.scroll = rec.ref(bind_with_args(self.__onScrollFloatProperty, '%s_min'%rec.Name, None, None))
840 rec.lbl_max.action = rec.ref(bind(self.__onClickEditProperty, '%s_max'%rec.Name, '%s max:'%rec.Name, acceptFloat))
841 rec.lbl_max.scroll = rec.ref(bind_with_args(self.__onScrollFloatProperty, '%s_max'%rec.Name, None, None))
843 if ix == 0: return
844 rec = Anon(self.__dataview.signals[ix]) # unlike TableRow, Anon allows modification
845 rec.ref = Reffer()
846 self.__signals.insert(ix-1, rec)
847 self.__setup_signal(rec)
848 for rec in self.__signals[ix:]:
849 rec.Index += 1
850 for sub in (rec.caption_min, rec.lbl_min, rec.caption_max, rec.lbl_max, rec.btn_grab, rec.btn_invert):
851 sub.y = sub.rq_y + LINE_EMS
852 self.subZoomClose.y = self.subZoomClose.rq_y + LINE_EMS
853 self.panZoom.h = self.panZoom.rq_h + LINE_EMS
854 self.__update_trace_signals()
855 self.__update_stimulus_signals()
856 if self.__scalebar_signal is None:
857 self.scalebar_signal = 0
859 if ix == 0: return
860 rec = self.__signals[ix-1]
861 self.panZoom.remove_sublayer(rec.btn_invert)
862 self.panZoom.remove_sublayer(rec.btn_grab)
863 self.panZoom.remove_sublayer(rec.lbl_max)
864 self.panZoom.remove_sublayer(rec.caption_max)
865 self.panZoom.remove_sublayer(rec.lbl_min)
866 self.panZoom.remove_sublayer(rec.caption_min)
867 del self.__signals[ix-1]
868 if ix < (len(self.__signals) - 1):
869 for rec in self.__signals[ix-1:]:
870 rec.Index -= 1
871 for sub in (rec.caption_min, rec.lbl_min, rec.caption_max, rec.lbl_max, rec.btn_grab, rec.btn_invert):
872 sub.y = sub.rq_y - LINE_EMS
873 self.subZoomClose.y = self.subZoomClose.rq_y - LINE_EMS
874 self.panZoom.h = self.panZoom.rq_h - LINE_EMS
875 self.__update_trace_signals()
876 self.__update_stimulus_signals()
877 if self.__scalebar_signal == ix:
878 self.scalebar_signal = self.__scalebar_signal
880 setattr(self.__signals[ix-1], field, val)
881 if field == 'Name':
882 self.__setup_signal(self.__signals[ix-1], prev)
883 self.__update_trace_signals()
884 self.__update_stimulus_signals()
885 if self.__scalebar_signal == (ix-1):
886 self.scalebar_signal = self.__scalebar_signal
887 self.update()
889 self.update()
893
895 self.plot.layerset = None if (index is None) else self.panels[index]
896 if not (self.__last_panel is None):
897 self.headers[self.__last_panel].cBG = COLOR_LF_TAB
898 self.__last_panel = None
899 if not (index is None):
900 self.headers[index].cBG = COLOR_LF_TAB_SEL
901 self.__last_panel = index
902
904 val = prompt_entry(label, getattr(self, name), accept, parent=self.parent_window)
905 if not (val is None):
906 self.propertied_on_user_set(name, val)
908 self.propertied_on_user_set(name, scrolled_int(getattr(self, name), offset, event.state&gdk.CONTROL_MASK, event.state&gdk.SHIFT_MASK, low, high))
910 self.propertied_on_user_set(name, scrolled_float(getattr(self, name), offset, event.state&gdk.CONTROL_MASK, event.state&gdk.SHIFT_MASK, low, high))
913
915 dlg = gtk.FontSelectionDialog("Pick a font...")
916 dlg.get_font_selection().set_preview_text('Caption')
917 dlg.set_font_name('%s%s %i' % (self.font_face, self.plot.appearance.font_bold and " bold" or "", self.plot.appearance.font_size))
918 fn = None
919 if gtk.RESPONSE_OK == dlg.run():
920 fn = dlg.get_font_selection().get_family().get_name()
921 dlg.destroy()
922 if not (fn is None):
923 self.propertied_on_user_set('font_face', fn)
925 self.__popup_signals('_in_trace')
927 self.__popup_signals('_in_stimulus')
931 lo, hi = self.__dataview.display_limits(rec.Index)
932 self.propertied_on_user_set('%s_min' % rec.Name, lo)
933 self.propertied_on_user_set('%s_max' % rec.Name, hi)
935 hi = getattr(self, "%s_min"%rec.Name)
936 setattr(self, "%s_min"%rec.Name, getattr(self, "%s_max"%rec.Name))
937 setattr(self, "%s_max"%rec.Name, hi)
939 self.__popup_signals('_in_scalebar')
942
947
949 g, di = self.prepare_draw(self.output_w, self.output_h)
950 w = g.tracepad_h
951 for i, rec in enumerate(reversed(di.scalebar_signals)):
952 one_w, one_h = measure_scalebar(g, di, rec, do_xlabel=(i==0))
953 w += one_w
954 return int(round(w)), int(round(one_h+2*di.xlabel_m.height))
956 context.set_source_rgba(1, 1, 1, 1)
957 context.paint()
958 g, di = self.prepare_draw(self.output_w, self.output_h, context)
959 draw_scalebars(context, g, di, -g.tracepad_h, di.xlabel_m.height-g.tracepad_v, w)
960
962 # sets up font, line width; returns geometry, datainfo
963
964 if context is None:
965 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 128)
966 cr = cairo.Context(surface)
967 else:
968 cr = context
969
970 if (view is None) and self.multi_file:
971 Data = qubx.global_namespace.QubX.Data
972 views = [Data.views[i] for i in xrange(len(Data.views)) if (Data.table[i, 'Group'] == self.multi_file_group)]
973 n = len(views) or 1
974 r, c = qubx.GTK.aspect_dimensions(w or 1, h or 1, n)
975 w1, h1 = w/c, h/r
976 g = Anon(sub=[])
977 di = Anon(sub=[])
978 i = j = 0
979 for v in views:
980 g1, di1 = self.prepare_draw(w1, h1, cr, v)
981 if g1 is None:
982 continue
983 g1.xoff, g1.yoff = i*w1, j*h1
984 g.sub.append(g1)
985 di.sub.append(di1)
986 i += 1
987 if i == c:
988 i = 0
989 j += 1
990 # the front view is the main g,di pair that's returned, also in the .sub lists
991 if v == self.__dataview:
992 g1.sub = g.sub
993 di1.sub = di.sub
994 g, di = g1, di1
995 g.multi_file = True
996 return g, di
997
998 dataview = self.__dataview if view is None else view
999 cr.select_font_face(self.font_face, cairo.FONT_SLANT_NORMAL,
1000 (self.font_weight >= 750) and cairo.FONT_WEIGHT_BOLD or cairo.FONT_WEIGHT_NORMAL)
1001
1002 g = Anon( # geometry
1003 line_width_input=self.line_width, line_opacity=self.line_opacity, zero_line_width_input=self.zero_line_width,
1004 trace_sampled=self.trace_sampled, trace_fit=self.trace_fit, trace_zero_line=self.trace_zero_line,
1005 trace_idealized=self.trace_idealized, trace_idealized_above=self.trace_idealized_above,
1006 trace_label_baseline=self.trace_label_baseline,
1007 stimulus_sampled=self.stimulus_sampled, stimulus_idealized=self.stimulus_idealized, stimulus_zero_line=self.stimulus_zero_line,
1008 stimulus_label_baseline=self.stimulus_label_baseline, stimulus_trace=self.stimulus_trace)
1009
1010
1011 di = Anon( # data info
1012 signal_segs={}, trace_signals=[], stimulus_signals=[], scalebar_signals=[],
1013 warnings=[], scalebar_format_fail=False)
1014
1015 signals = self.__signals if (dataview == self.__dataview) else [Anon(dataview.signals[i]) for i in xrange(1, dataview.signals.size)]
1016 for rec in signals:
1017 used = False
1018 scaled = False
1019 if getattr(self, "%s_in_trace" % rec.Name):
1020 di.trace_signals.append(rec)
1021 used = True
1022 if getattr(self, "%s_in_stimulus" % rec.Name):
1023 di.stimulus_signals.append(rec)
1024 used = True
1025 if getattr(self, "%s_in_scalebar" % rec.Name):
1026 di.scalebar_signals.append(rec)
1027 scaled = True
1028 # don't need data series (used = False)
1029 if used:
1030 di.signal_segs[rec.Name] = dataview.get_segmentation_list(signal=dataview.signals.index(rec.Name)-1)
1031 if used or scaled:
1032 rec.lo, rec.hi = getattr(self, "%s_min"%rec.Name), getattr(self, "%s_max"%rec.Name)
1033 if not di.trace_signals:
1034 return None, None
1035
1036 di.Nseg = 0
1037 di.maxNsample = 0
1038 if di.signal_segs:
1039 di.Nseg = len(di.signal_segs[di.trace_signals[0].Name])
1040 for seg in di.signal_segs[di.trace_signals[0].Name]:
1041 di.maxNsample = max(di.maxNsample, seg.n)
1042 if not di.maxNsample:
1043 return None, None
1044 listview = qubx.global_namespace.QubX.Tables.find_view('List')
1045 if listview.table == dataview.file.list:
1046 # sorted order only defined for onscreen data file
1047 di.seg_order = [listview.raw_ix(i) for i in xrange(di.Nseg)]
1048 else:
1049 di.seg_order = range(di.Nseg)
1050
1051 g.margin_h = int(round(self.margin_h * w))
1052 g.margin_v = int(round(self.margin_v * h))
1053 g.cw = w - 2*g.margin_h
1054 g.ch = h - 2*g.margin_v
1055
1056 Ntrace = ((self.trace_idealized_above and 2 or 1) * len(di.trace_signals) * di.Nseg
1057 + (self.stimulus_position != LISTFIG_POSITION_HIDDEN) * len(di.stimulus_signals)
1058 + ((self.scalebar_position in (LISTFIG_POSITION_ABOVE, LISTFIG_POSITION_BELOW)) and 1 or 0)) or 1
1059 g.tracepad_h = int(round(self.tracepad_h * g.cw))
1060 g.tracepad_v = int(round(self.tracepad_v * g.ch / Ntrace))
1061 g.trace_h = int(round((g.ch*1.0/Ntrace)*(1.0 - 2*self.tracepad_v)))
1062 g.line_h = (g.trace_h+2*g.tracepad_v) * (self.trace_idealized_above and 2 or 1) * len(di.trace_signals)
1063 g.trace_font_h = int(round(self.font_height * g.line_h))
1064 try:
1065 di.trace_labels = [self.trace_label_format % row.__dict__ for row in dataview.file.list]
1066 except:
1067 di.trace_labels = [row.Label for row in dataview.file.list]
1068 di.warnings.append('Error in Trace label format')
1069 while ( (g.trace_font_h > 8) and (max(measure_string(cr, g.trace_font_h, lbl).width for lbl in di.trace_labels) > (self.font_max_width * g.cw)) ):
1070 g.trace_font_h -= max(2, int(round(0.1*g.trace_font_h)))
1071 try:
1072 di.stimulus_labels = [self.stimulus_label_format % rec.__dict__ for rec in di.stimulus_signals]
1073 except:
1074 di.stimulus_labels = [rec.Name for rec in di.stimulus_signals]
1075 di.warnings.append('Error in Stimulus label format')
1076 if di.stimulus_labels:
1077 while ( (g.trace_font_h > 8) and (max(measure_string(cr, g.trace_font_h, lbl).width for lbl in di.stimulus_labels) > (self.font_max_width * g.cw)) ):
1078 g.trace_font_h -= max(2, int(round(0.1*g.trace_font_h)))
1079 g.stim_font_h = min(g.trace_font_h, int(round(self.font_height * (g.trace_h+2*g.tracepad_v))))
1080 g.label_w = max(measure_string(cr, g.trace_font_h, lbl).width for lbl in di.trace_labels)
1081 if di.stimulus_labels:
1082 g.label_w = max(g.label_w, max(measure_string(cr, g.stim_font_h, lbl).width for lbl in di.stimulus_labels))
1083 g.trace_w = g.cw - g.label_w - 3*g.tracepad_h
1084 g.dot_size = self.dot_size * g.trace_h
1085 g.line_width = self.line_width * g.trace_h
1086 g.zero_line_width = self.zero_line_width * g.trace_h
1087
1088 g.color_label = self.plot.appearance.color(COLOR_LF_LABEL)
1089 g.color_trace = self.plot.appearance.color(COLOR_LF_TRACE)
1090 g.color_idl = self.plot.appearance.color(COLOR_LF_IDL)
1091 g.color_fit = self.plot.appearance.color(COLOR_LF_FIT)
1092 g.color_stim = self.plot.appearance.color(COLOR_LF_STIMULUS)
1093 g.color_stim_idl = self.plot.appearance.color(COLOR_LF_STIMULUS_IDL)
1094 g.color_zero_line = self.plot.appearance.color(COLOR_LF_ZERO)
1095
1096 # scale bars
1097 g.scalebar_position = self.scalebar_position
1098 g.scalebar_vertical_labels = self.scalebar_vertical_labels
1099 g.scalebar_timebar = self.scalebar_timebar
1100 g.scalebar_timebar_x = self.scalebar_timebar_x
1101 g.scalebar_timebar_y = self.scalebar_timebar_y
1102 g.scalebar_yoff = 0.1 * g.trace_h
1103 g.sec_per_pix = di.maxNsample * dataview.file.sampling / g.trace_w
1104 g.font_size_num = max(2, int(round(self.scalebar_font_height * g.trace_h)))
1105 if self.scalebar_timebar_autodim:
1106 dx_sec = scalebar_length(g.trace_h * g.sec_per_pix)
1107 else:
1108 dx_sec = self.scalebar_timebar_dim
1109 g.scalebar_dx_pix = dx_pix = round(dx_sec / g.sec_per_pix)
1110 dx_scaled, dx_unit_prefix = scaled_units(dx_sec)
1111 xlabel = '%.0f' % dx_scaled
1112 di.xlabel = "%s %ss" % (xlabel, dx_unit_prefix)
1113 di.xlabel_m = measure_string(cr, g.font_size_num, di.xlabel)
1114 for rec in di.scalebar_signals:
1115 rec.sb_timebar = getattr(self, "%s_sb_timebar"%rec.Name)
1116 rec.sb_invert_x = getattr(self, "%s_sb_invert_x"%rec.Name)
1117 rec.sb_invert_y = getattr(self, "%s_sb_invert_y"%rec.Name)
1118 rec.sb_every_trace = getattr(self, "%s_sb_every_trace"%rec.Name)
1119 rec.sb_float_x = getattr(self, "%s_sb_float_x"%rec.Name)
1120 rec.sb_float_y = getattr(self, "%s_sb_float_y"%rec.Name)
1121 rec.sb_drawn = False
1122 lo, hi = rec.lo, rec.hi
1123 if getattr(self, "%s_sb_autodim"%rec.Name):
1124 dy_u = scalebar_length(0.9*abs(hi - lo))
1125 else:
1126 dy_u = getattr(self, "%s_sb_dim"%rec.Name)
1127 rec.dy_pix = round(dy_u * g.trace_h / abs(hi - lo))
1128 ylabel = '%.2g' % dy_u
1129 if not di.scalebar_format_fail:
1130 try:
1131 ylabel = self.scalebar_number_format % dy_u
1132 except:
1133 ylabel = '%.2g' % dy_u
1134 warnings.append('Error in Scale bar number format')
1135 di.scalebar_format_fail = True
1136 y_units = (" %s" % rec.Units) if rec.Units else ""
1137 rec.ylabel = ylabel + y_units
1138 rec.ylabel_m = measure_string(cr, g.font_size_num, rec.ylabel)
1139
1140 cr.set_line_width(g.line_width)
1141 return g, di
1142
1144 context.set_source_rgba(* self.plot.appearance.color(COLOR_LF_BG))
1145 context.rectangle(0, 0, w, h)
1146 context.fill()
1147 if not self.__dataview:
1148 return
1149
1150 context.save()
1151 if g_in is None:
1152 g, di = self.prepare_draw(w, h, context)
1153 if g is None:
1154 context.restore()
1155 return
1156 if g.multi_file:
1157 n = len(g.sub)
1158 if n:
1159 r, c = qubx.GTK.aspect_dimensions(w or 1, h or 1, n)
1160 w1, h1 = w/c, h/r
1161 for g1, di1 in izip(g.sub, di.sub):
1162 context.save()
1163 context.translate(g1.xoff, g1.yoff)
1164 self.__onDraw(context, w1, h1, g1, di1)
1165 context.restore()
1166 context.restore()
1167 return
1168 else:
1169 g, di = g_in, di_in
1170
1171 if (di.maxNsample < (4*g.trace_w)) or self.draw_exact:
1172 xx = arange(di.maxNsample, dtype='float32')
1173 xx *= g.trace_w / di.maxNsample
1174 draw_sampled = lambda c, g, di, elx, ely, elw, elh, lo, hi, seg, color: draw_sampled_exact(c, g, di, elx, ely, elw, elh, lo, hi, seg, color, xx)
1175
1176 else:
1177 draw_sampled = draw_sampled_compressed
1178
1179 line_x = g.margin_h
1180 line_y = g.margin_v
1181 if self.scalebar_position == LISTFIG_POSITION_ABOVE:
1182 line_x, line_y = draw_scalebars(context, g, di, line_x, line_y, g.cw - 3*g.tracepad_h)
1183 if self.stimulus_position == LISTFIG_POSITION_ABOVE:
1184 line_x, line_y = draw_stimulus(context, g, di, draw_sampled, line_x, line_y)
1185 line_x, line_y = draw_traces(context, g, di, draw_sampled, line_x, line_y)
1186 if self.stimulus_position == LISTFIG_POSITION_BELOW:
1187 line_x, line_y = draw_stimulus(context, g, di, draw_sampled, line_x, line_y)
1188 if self.scalebar_position == LISTFIG_POSITION_BELOW:
1189 line_x, line_y = draw_scalebars(context, g, di, line_x, line_y, g.cw - 3*g.tracepad_h)
1190 if self.scalebar_timebar:
1191 draw_timebar(context, g, di, None, w*self.scalebar_timebar_x, h*self.scalebar_timebar_y)
1192 if self.scalebar_position == LISTFIG_POSITION_FLOAT:
1193 draw_scalebars_float(context, g, di, w, h)
1194
1195 context.restore()
1196 self.subWarnings.label = '; '.join(di.warnings)
1197 self.subWarnings.cBG = COLOR_LF_BG_TROUBLE if di.warnings else None
1198
1201 if raw < 0:
1202 raw *= -1
1203 if raw == 0:
1204 raw = 1
1205 base = floor(log10(raw))
1206 rmn = log10(raw) - base
1207 if rmn < log10(2):
1208 return 10**base
1209 if rmn < log10(5):
1210 return 2*10**base
1211 return 5*10**base
1212
1214 if x < 0:
1215 x *= -1
1216 for (scale, prefix) in [(1e9, 'G'), (1e6, 'M'), (1e3, 'K'), (1.0, ''), (1e-3, 'm'), (1e-6, 'u'), (1e-9, 'n'), (1e-12, 'p')]:
1217 if x >= scale:
1218 return x/scale, prefix
1219
1226
1229 m = measure_string(context, points, s) # also sets font_size
1230 if baseline is None:
1231 b = ely + (elh - m.height)/2 - m.ybearing
1232 else:
1233 b = ely + (1.0 - baseline) * elh
1234 context.move_to(elx + (elw - m.width)/2 - m.xbearing, b)
1235 context.show_text(s)
1236
1239 if g.zero_line_width:
1240 if ((lo < hi) and (lo <= 0 <= hi)) or ((hi < lo) and (hi <= 0 <= lo)):
1241 yzero = ely + hi * (elh * 1.0 / (hi - lo))
1242 context.set_line_width(g.zero_line_width)
1243 context.set_source_rgba(* g.color_zero_line)
1244 context.move_to(elx, yzero)
1245 context.line_to(elx+elw, yzero)
1246 context.stroke()
1247
1250 context.save()
1251 context.translate(elx, ely)
1252 context.rectangle(0, 0, elw, elh)
1253 context.clip()
1254 r_in, g_in, b_in, a_in = color
1255
1256 for chunk in seg.chunks:
1257 samples = transform_y(elh, lo, hi, chunk.get_samples().samples)
1258 xx_chunk = xx[chunk.f - seg.f:chunk.l+1-seg.f]
1259 if g.dot_size:
1260 if chunk.included:
1261 context.set_source_rgba(r_in, g_in, b_in, a_in)
1262 else:
1263 context.set_source_rgba(0.5, 0.5, 0.5, 0.5)
1264 for x, y in izip(xx_chunk, samples):
1265 context.move_to(x+g.dot_size, y)
1266 context.arc(x, y, g.dot_size, 0, 2*pi)
1267 context.fill()
1268 if g.line_width:
1269 if chunk.included:
1270 context.set_source_rgba(r_in, g_in, b_in, g.line_opacity)
1271 else:
1272 context.set_source_rgba(0.5, 0.5, 0.5, 0.5*g.line_opacity)
1273 at = 0
1274 while at < (chunk.n-1):
1275 batch_n = min(chunk.n - at, 1024)
1276 context.move_to(xx_chunk[at], samples[at])
1277 for i in xrange(at, at+batch_n):
1278 context.line_to(xx_chunk[i], samples[i])
1279 context.stroke()
1280 at += batch_n - 1
1281 context.restore()
1282
1284 context.save()
1285 context.translate(elx, ely)
1286 context.rectangle(0, 0, elw, elh)
1287 context.clip()
1288 cFull = color
1289 cPartial = qubx.toolspace.SETALPHA(cFull, .5*cFull[3])
1290 context.set_line_width(1)
1291 context.translate(0.5, 0)
1292
1293 seg_w = int(round(elw * seg.n / di.maxNsample))
1294 samp_per_pix = di.maxNsample / elw
1295 for chunk in seg.chunks:
1296 i0 = max(0, min(seg_w-1, int(round(seg_w*float(chunk.f -seg.f)/seg.n))))
1297 i1 = max(0, min(seg_w, int(round(seg_w*float(chunk.l+1-seg.f)/seg.n))))
1298 samples = chunk.get_samples().samples
1299 resolution = g.line_width_input * abs(hi - lo)
1300 lows, highs, gauss_los, gauss_his = qubx.fast.data.raster_samples(i1-i0, samples, samp_per_pix, resolution)
1301 transform_y(elh, lo, hi, lows)
1302 transform_y(elh, lo, hi, highs)
1303 transform_y(elh, lo, hi, gauss_los)
1304 transform_y(elh, lo, hi, gauss_his)
1305 if chunk.included:
1306 context.set_source_rgba(*cFull)
1307 else:
1308 context.set_source_rgba(0.5, 0.5, 0.5, 0.5)
1309 for i, lo, hi in izip(count(i0), lows, highs):
1310 context.move_to(i, lo)
1311 context.line_to(i, hi)
1312 context.stroke()
1313 if chunk.included:
1314 context.set_source_rgba(*cFull)
1315 for i, lo, hi in izip(count(i0), gauss_los, gauss_his):
1316 context.move_to(i, lo)
1317 context.line_to(i, hi)
1318 context.stroke()
1319 context.restore()
1320
1322 context.save()
1323 context.translate(elx, ely)
1324 context.rectangle(0, 0, elw, elh)
1325 context.clip()
1326 line_width = 0.5 * g.line_width
1327 context.set_line_width(1)
1328 context.translate(0.5, 0)
1329
1330 seg_w = int(round(elw * seg.n / di.maxNsample))
1331 lows = zeros(shape=(seg_w,), dtype='float32')
1332 highs_scaled = zeros(shape=(seg_w,), dtype='float32')
1333 lows += UNSET_IDL
1334 highs_scaled += UNSET_IDL
1335 amps = array(seg.file.ideal[seg.signal].seg[seg.index].amp, dtype='float64')
1336 seg.file.ideal[seg.signal].idl.sample_dwells(seg.f, seg.l, amps, seg_w, lows, highs_scaled)
1337 lows_scaled = array(lows, copy=True)
1338 transform_y(elh, lo, hi, lows_scaled)
1339 transform_y(elh, lo, hi, highs_scaled)
1340 for chunk in seg.chunks:
1341 if chunk.included:
1342 context.set_source_rgba(*color)
1343 else:
1344 context.set_source_rgba(0.5, 0.5, 0.5, 0.5)
1345 i0 = max(0, min(seg_w-1, int(round(seg_w*float(chunk.f -seg.f)/seg.n))))
1346 i1 = max(0, min(seg_w, int(round(seg_w*float(chunk.l+1-seg.f)/seg.n))))
1347 for i, lo_raw, lo, hi in izip(count(i0), lows[i0:i1], lows_scaled[i0:i1], highs_scaled[i0:i1]):
1348 if lo_raw != UNSET_IDL:
1349 context.move_to(i, lo - line_width)
1350 context.line_to(i, hi + line_width)
1351 context.stroke()
1352 context.restore()
1353
1355 context.save()
1356 context.translate(elx, ely)
1357 context.rectangle(0, 0, elw, elh)
1358 context.clip()
1359 line_width = 0.5 * g.line_width
1360 context.set_line_width(1)
1361 context.translate(0.5, 0)
1362
1363 seg_w = int(round(elw * seg.n / di.maxNsample))
1364 lows = zeros(shape=(seg_w,), dtype='float32')
1365 highs_scaled = zeros(shape=(seg_w,), dtype='float32')
1366 lows += UNSET_IDL
1367 highs_scaled += UNSET_IDL
1368 amps = seg.file.fits[seg.signal].idl.segmeans[seg.index]
1369 stds = seg.file.fits[seg.signal].idl.segstds[seg.index]
1370 seg.file.fits[seg.signal].idl.sample_dwells_std(seg.f, seg.l, amps, stds, seg_w, lows, highs_scaled)
1371 lows_scaled = array(lows, copy=True)
1372 transform_y(elh, lo, hi, lows_scaled)
1373 transform_y(elh, lo, hi, highs_scaled)
1374 for chunk in seg.chunks:
1375 if chunk.included:
1376 context.set_source_rgba(*color)
1377 else:
1378 context.set_source_rgba(0.5, 0.5, 0.5, 0.5)
1379 i0 = max(0, min(seg_w-1, int(round(seg_w*float(chunk.f -seg.f)/seg.n))))
1380 i1 = max(0, min(seg_w, int(round(seg_w*float(chunk.l+1-seg.f)/seg.n))))
1381 for i, lo_raw, lo, hi in izip(count(i0), lows[i0:i1], lows_scaled[i0:i1], highs_scaled[i0:i1]):
1382 if lo_raw != UNSET_IDL:
1383 context.move_to(i, lo - line_width)
1384 context.line_to(i, hi + line_width)
1385 context.stroke()
1386 context.restore()
1387
1390 for itrace in di.seg_order:
1391 context.set_source_rgba(* g.color_label)
1392 draw_string(context, lx+g.tracepad_h, ly+g.tracepad_v, g.label_w, g.line_h-2*g.tracepad_v, g.trace_font_h, di.trace_labels[itrace], baseline=g.trace_label_baseline)
1393 trace_x = lx + g.tracepad_h + g.label_w + g.tracepad_h
1394 trace_y = ly + g.tracepad_v
1395 for rec in di.trace_signals:
1396 lo, hi = rec.lo, rec.hi
1397 if g.trace_idealized_above:
1398 draw_idealized(context, g, di, trace_x, trace_y, g.trace_w, g.trace_h, lo, hi, di.signal_segs[rec.Name][itrace], g.color_idl)
1399 trace_y += g.trace_h + 2*g.tracepad_v
1400 if g.trace_zero_line:
1401 draw_zero_line(context, g, di, trace_x, trace_y, g.trace_w, g.trace_h, lo, hi)
1402 if g.trace_sampled:
1403 draw_sampled(context, g, di, trace_x, trace_y, g.trace_w, g.trace_h, lo, hi, di.signal_segs[rec.Name][itrace], g.color_trace)
1404 if g.trace_idealized and not g.trace_idealized_above:
1405 draw_idealized(context, g, di, trace_x, trace_y, g.trace_w, g.trace_h, lo, hi, di.signal_segs[rec.Name][itrace], g.color_idl)
1406 if g.trace_fit:
1407 draw_fit(context, g, di, trace_x, trace_y, g.trace_w, g.trace_h, lo, hi, di.signal_segs[rec.Name][itrace], g.color_fit)
1408 if (g.scalebar_position == LISTFIG_POSITION_RIGHT) and (rec.sb_every_trace or not rec.sb_drawn):
1409 rec.sb_drawn = True
1410 draw_scalebar_right(context, g, di, trace_x+g.tracepad_h, trace_y, g.trace_w, g.trace_h, rec)
1411 trace_y += g.trace_h + 2*g.tracepad_v
1412 ly += g.line_h
1413 return lx, ly
1414
1416 for istim, rec in enumerate(di.stimulus_signals):
1417 lo, hi = rec.lo, rec.hi
1418 context.set_source_rgba(* g.color_label)
1419 draw_string(context, lx+g.tracepad_h, ly+g.tracepad_v, g.label_w, g.trace_h, g.stim_font_h, di.stimulus_labels[istim], baseline=g.stimulus_label_baseline)
1420 if g.stimulus_zero_line:
1421 draw_zero_line(context, g, di, lx+2*g.tracepad_h+g.label_w, ly+g.tracepad_v, g.trace_w, g.trace_h, lo, hi)
1422 if g.stimulus_sampled:
1423 draw_sampled(context, g, di, lx+2*g.tracepad_h+g.label_w, ly+g.tracepad_v, g.trace_w, g.trace_h, lo, hi, di.signal_segs[rec.Name][g.stimulus_trace], g.color_stim)
1424 if g.stimulus_idealized:
1425 draw_idealized(context, g, di, lx+2*g.tracepad_h+g.label_w, ly+g.tracepad_v, g.trace_w, g.trace_h, lo, hi, di.signal_segs[rec.Name][g.stimulus_trace], g.color_stim_idl)
1426 if (g.scalebar_position == LISTFIG_POSITION_RIGHT) and (rec.sb_every_trace or not rec.sb_drawn):
1427 rec.sb_drawn = True
1428 draw_scalebar_right(context, g, di, lx+2*g.tracepad_h+g.label_w, ly+g.tracepad_v, g.trace_w+g.tracepad_h, g.trace_h, rec)
1429 ly += g.trace_h + 2*g.tracepad_v
1430 return lx, ly
1431
1433 yoff = g.scalebar_yoff
1434 context.set_source_rgba(* g.color_label)
1435 context.set_line_width(g.line_width)
1436 context.move_to(x, y)
1437 xmove = (- g.scalebar_dx_pix) if (rec and rec.sb_invert_x) else g.scalebar_dx_pix
1438 context.line_to(x+xmove, y)
1439 context.stroke()
1440 lbl_x = x + 0.5*(g.scalebar_dx_pix - di.xlabel_m.width)
1441 if xmove < 0:
1442 lbl_x += xmove
1443 if rec and rec.sb_invert_y:
1444 lbl_y = y - 2*g.line_width
1445 else:
1446 lbl_y = y + di.xlabel_m.height + 2*g.line_width
1447 context.set_font_size(g.font_size_num)
1448 context.move_to(lbl_x, lbl_y)
1449 context.show_text(di.xlabel)
1450 return x + xmove
1451
1454
1456 context.set_font_size(g.font_size_num)
1457 context.save()
1458 w, h = measure_ybar_label(g, di, rec)
1459 context.translate(x+g.line_width, y)
1460 if not rec.sb_invert_x:
1461 context.translate(-2*g.line_width - w, 0)
1462 if g.scalebar_vertical_labels:
1463 context.translate(- rec.ylabel_m.font.descent, 0)
1464 if g.scalebar_vertical_labels:
1465 if rec.sb_invert_y:
1466 context.translate(0, 0.5*(rec.dy_pix - h))
1467 else:
1468 context.translate(0, 0.5*(rec.dy_pix - h) - rec.dy_pix)
1469 context.rotate(-pi/2)
1470 context.translate(-h, w)
1471 else:
1472 if rec.sb_invert_y:
1473 context.translate(0, rec.dy_pix - yoff)
1474 else:
1475 context.translate(0, yoff - rec.dy_pix + h)
1476 context.move_to(0, 0)
1477 context.show_text(rec.ylabel)
1478 context.restore()
1480 if g.scalebar_vertical_labels:
1481 return rec.ylabel_m.height, rec.ylabel_m.width
1482 else:
1483 return rec.ylabel_m.width, rec.ylabel_m.height
1485 yoff = g.scalebar_yoff
1486 ymove = rec.dy_pix if rec.sb_invert_y else (- rec.dy_pix)
1487 context.set_source_rgba(* g.color_label)
1488 context.set_line_width(g.line_width)
1489 context.move_to(x, y)
1490 context.line_to(x, y + ymove)
1491 context.stroke()
1492 draw_ybar_label(context, g, di, rec, x, y, yoff)
1495
1500
1502 yoff = g.scalebar_yoff
1503 at_x = w - g.tracepad_h
1504 if rec.sb_invert_x:
1505 at_x -= measure_ybar(g, di, rec)
1506 else:
1507 at_x -= measure_timebar(g, di, rec)[0]
1508 if rec.sb_invert_y:
1509 at_y = yoff
1510 else:
1511 at_y = h - yoff
1512 return draw_scalebar(context, g, di, x+at_x, y+at_y, rec)
1513
1515 if rec.sb_timebar:
1516 draw_timebar(context, g, di, rec, x, y)
1517 draw_ybar(context, g, di, rec, x, y)
1518 if rec.sb_invert_x:
1519 x -= measure_timebar(g, di, rec)[0]
1520 else:
1521 x -= measure_ybar(g, di, rec)
1522 return x - g.scalebar_yoff
1523
1525 time_w, time_h = measure_timebar(g, di, rec)
1526 y_h = measure_ybar(g, di, rec)
1527 w = g.scalebar_yoff + time_w + y_h + g.tracepad_h
1528 # h = y_h + time_h
1529 h = g.trace_h
1530 if do_xlabel:
1531 h += time_h
1532 return (w, h)
1533
1535 w_remain = w
1536 context.save()
1537 context.translate(lx+g.tracepad_h, ly+g.tracepad_v)
1538 for i, rec in enumerate(reversed(di.scalebar_signals)):
1539 w_remain = draw_scalebar_boxed(context, g, di, 0, 0, w_remain, g.trace_h, rec)
1540 context.restore()
1541 ly += g.trace_h + 2*g.tracepad_v
1542 return lx, ly
1543
1545 for rec in di.scalebar_signals:
1546 draw_scalebar(context, g, di, rec.sb_float_x*w, rec.sb_float_y*h, rec)
1547
1548 # separate stimulus line width?
1549 # separate idl line width?
1550
1551 # Nb scalebars: per existing signal plus time
1552
1553 # krishna's defaults
1554
1555 # overlap or average traces
1556 # overlap all stimulus traces?
1557
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Dec 15 19:07:38 2017 | http://epydoc.sourceforge.net |