graphtools.py 23.3 KB
Newer Older
Yan's avatar
Yan committed
1
2
from PyQt5 import QtWidgets
from PyQt5 import QtCore
3
4
5
from matplotlib.backends.backend_qt5agg import\
    FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
Yan's avatar
Yan committed
6
from matplotlib.widgets import SpanSelector
Yan's avatar
Yan committed
7
import numpy as np
Yan's avatar
Yan committed
8
import prasopes.datatools as dt
Yan's avatar
Yan committed
9
import prasopes.config as cf
10
import matplotlib
11
matplotlib.use("Qt5Agg")
Yan's avatar
Yan committed
12

Yan's avatar
Yan committed
13

3Yan's avatar
3Yan committed
14
15
16
17
18
colors = np.array([[0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255],
                   [0, 200, 255], [255, 200, 0], [255, 100, 0],
                   [200, 50, 0], [255, 0, 200], [0, 100, 0],
                   [0, 100, 255], [100, 100, 100]])

19

20
ann_bbox = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.5)
3Yan's avatar
3Yan committed
21

22

23
24
25
26
27
28
class AugFigureCanvas(FigureCanvas):
    """Figure canvas fitted with mass spectrum, chromatogram and more"""
    def __init__(self):
        self.figure = Figure(figsize=(5, 4), dpi=100, facecolor="None",
                             constrained_layout=True)
        super().__init__(self.figure)
Yan's avatar
Yan committed
29
        self.filename = ""
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        self.ds = []
        self.ms = dict(annotation=[], name="Spectrum", xlabel="m/z",
                       ylabel="ion count", xtics=20, predict=None,
                       params=[], headers=[], texts=[])
        self.chrom = dict(
            x=[0], y=[0], t_start=None, t_end=None, machtype=None,
            name="Chromatogram", xlabel="time(min)", ylabel="total ion count",
            timesarg=[])
        self.drlcache = [None, None]
        grid = self.figure.add_gridspec(2, 1)
        self.chromplot = self.figure.add_subplot(grid[0, 0],
                                                 facecolor=(1, 1, 1, 0.8))
        self.spectplot = self.figure.add_subplot(grid[1, 0],
                                                 facecolor=(1, 1, 1, 0.8))
        self.setStyleSheet("background-color:transparent;")
        self.setAutoFillBackground(False)
        self.paramstable = dt.table(["", "name", "value"], 100)
        pan_factory(self.chromplot)
3Yan's avatar
3Yan committed
48
        zoom_factory(self.chromplot, 0.9)
49
        pan_factory(self.spectplot, self.ms)
3Yan's avatar
3Yan committed
50
        zoom_factory(self.spectplot, 0.9, self.ms)
51
52
53
54
55
56
        textedit_factory(self.spectplot, self.ms)
        self.mass_selector = AugSpanSelector(self.spectplot, self.ms)
        self.time_selector = SpanSelector(
                self.chromplot, lambda x_min, x_max: pick_times(
                    x_min, x_max, self), 'horizontal', useblit=True,
                rectprops=dict(alpha=0.15, facecolor='purple'), button=3)
Yan's avatar
Yan committed
57
58
59
60
61
        self.figure.set_constrained_layout(False)

    def constrained_draw(self):
        self.figure.execute_constrained_layout()
        self.draw()
62
63


Yan's avatar
Yan committed
64
65
class AugSpanSelector(SpanSelector):
    def __init__(self, ax, data):
66
67
68
69
70
        super().__init__(
            ax, onselect=lambda x, y: None, direction='horizontal',
            minspan=0.01, useblit=True, rectprops=dict(
                alpha=0.15, facecolor='purple'),
            onmove_callback=None, span_stays=False, button=3)
Yan's avatar
Yan committed
71
        self.data = data
72

Yan's avatar
Yan committed
73
74
75
76
77
78
79
80
81
82
    def _press(self, event):
        """on button press event override"""
        if QtWidgets.QApplication.keyboardModifiers() ==\
                QtCore.Qt.ShiftModifier:
            self.direction = 'vertical'
            self.onselect = self.pick_intensities
        else:
            self.direction = 'horizontal'
            self.onselect = self.pick_masses
        self.new_axes(self.ax)
83
        super()._press(event)
Yan's avatar
Yan committed
84

85
86
87
88
89
    def _release(self, event):
        """on button release event"""
        if self.pressv is None:
            return
        elif self.direction == 'horizontal':
90
            super()._release(event)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
        else:
            self.rect.set_visible(False)
            self.canvas.draw_idle()
            vmax = self._get_data(event)[1] or self.prev[1]
            span = vmax - self.ax.get_ylim()[0]
            if self.minspan is not None and span < self.minspan:
                return
            self.onselect(self.ax.get_ylim()[0], vmax)
            self.pressv = None
            return False

    def _set_span_xy(self, event):
        """Setting the span coordinates override"""
        if self.direction == 'horizontal':
105
            super()._set_span_xy(event)
106
        else:
107
            x, y = self._get_data(event)
108
109
110
111
112
            if y is None:
                return
            self.prev = x, y
            self.rect.set_y(self.ax.get_ylim()[0])
            self.rect.set_height(y)
Yan's avatar
Yan committed
113
114
115
116
117
118
119
120
121
122
123

    def pick_masses(self, vmin, vmax):
        """zoom the spectrum in x axis by mass range"""
        self.ax.set_xlim(vmin, vmax)
        autozoomy(self.ax)
        ann_spec(self.ax, self.data)

    def pick_intensities(self, vmin, vmax):
        """zoom the spectrum in y axis by top intensity from range"""
        self.ax.set_ylim(-vmax*0.01, vmax)
        ann_spec(self.ax, self.data)
Yan's avatar
Yan committed
124
125


Yan's avatar
Yan committed
126
127
128
129
130
131
132
133
134
135
class FixedScalarFormatter(matplotlib.ticker.ScalarFormatter):
    def __init__(self):
        super().__init__()
        self._powerlimits = (0,0)

    def _set_format(self):
        """_set_format override"""
        self.format = "%.2f"


Yan's avatar
Yan committed
136
def zoom_factory(axis, base_scale, plot_data=None):
Yan's avatar
Yan committed
137
    """returns zooming functionality to axis"""
Yan's avatar
Yan committed
138
    def zoom_fun(event, pd, ax, scale):
Yan's avatar
Yan committed
139
        """zoom when scrolling"""
Yan's avatar
Yan committed
140
        if event.inaxes == axis:
3Yan's avatar
3Yan committed
141
            scale_factor = np.power(scale, event.step)
142
            if QtWidgets.QApplication.keyboardModifiers() !=\
Yan's avatar
Yan committed
143
                    QtCore.Qt.ShiftModifier:
144
                data = event.ydata
Yan's avatar
Yan committed
145
146
                new_top = data + (ax.get_ylim()[1] - data) \
                    * scale_factor
Yan's avatar
Yan committed
147
148
149
150
                ymin = -0.01
                if type(pd) is dict and "c_ymin" in pd:
                    ymin = pd['c_ymin']
                axis.set_ylim([new_top * ymin, new_top])
151
152
            else:
                data = event.xdata
Yan's avatar
Yan committed
153
154
                x_left = data - ax.get_xlim()[0]
                x_right = ax.get_xlim()[1] - data
Yan's avatar
Yan committed
155
                ax.set_xlim([data - x_left * scale_factor,
Yan's avatar
Yan committed
156
                            data + x_right * scale_factor])
Yan's avatar
Yan committed
157
            if type(pd) is dict and "annotation" in pd:
Yan's avatar
Yan committed
158
                ann_spec(event.inaxes, pd)
159
            ax.figure.canvas.draw()
Yan's avatar
Yan committed
160

Yan's avatar
Yan committed
161
    fig = axis.get_figure()
162
    fig.canvas.mpl_connect('scroll_event', lambda event: zoom_fun(
3Yan's avatar
3Yan committed
163
        event, plot_data, axis, base_scale))
Yan's avatar
Yan committed
164
165


Yan's avatar
Yan committed
166
def pan_factory(axis, plot=None):
Yan's avatar
Yan committed
167
    """pan spectrum when you press a button"""
Yan's avatar
Yan committed
168
    def pan_fun(event, ax, pd):
169
        # Release focus from other objects when clicking on graph.
Yan's avatar
Yan committed
170
        focusedWidget = QtWidgets.QApplication.focusWidget()
171
        if focusedWidget and event.inaxes == ax:
Yan's avatar
Yan committed
172
            focusedWidget.clearFocus()
Yan's avatar
Yan committed
173
        # re-scale to origin if doubleclicked
Yan's avatar
Yan committed
174
175
176
        if event.dblclick and event.inaxes == ax:
            ax.get_figure()
            ax.autoscale(True)
Yan's avatar
Yan committed
177
178
179
180
            ymin = -0.01
            if type(pd) is dict and "c_ymin" in pd:
                ymin = pd['c_ymin']
            ax.set_ylim(ax.get_ylim()[1]*ymin, ax.get_ylim()[1]*1.1)
Yan's avatar
Yan committed
181
182
            if type(pd) is dict and "annotation" in pd:
                ann_spec(ax, pd)
Yan's avatar
Yan committed
183
            ax.figure.canvas.draw()
Yan's avatar
Yan committed
184
        # otherwise pan
Yan's avatar
Yan committed
185
        elif event.button == 1 and event.inaxes == ax:
Yan's avatar
Yan committed
186
            ax.start_pan(event.x, event.y, event.button)
Yan's avatar
Yan committed
187
188
189
            id_drag = fig.canvas.mpl_connect(
                'motion_notify_event',
                lambda action: drag_fun(action, ax))
Yan's avatar
Yan committed
190
            id_release = fig.canvas.mpl_connect(
Yan's avatar
Yan committed
191
192
                'button_release_event',
                lambda action: drag_end(
Yan's avatar
Yan committed
193
                    action, id_drag, id_release, pd, ax))
Yan's avatar
Yan committed
194

195
    def drag_fun(event, ax):
Yan's avatar
Yan committed
196
197
198
        ax.drag_pan(1, 'x', event.x, event.y)
        ax.figure.canvas.draw()

Yan's avatar
Yan committed
199
    def drag_end(event, id_drag, id_release, pd, ax):
Yan's avatar
Yan committed
200
201
202
        if event.button == 1:
            fig.canvas.mpl_disconnect(id_drag)
            fig.canvas.mpl_disconnect(id_release)
Yan's avatar
Yan committed
203
204
            if type(pd) is dict and "annotation" in pd:
                ann_spec(ax, pd)
205
            ax.figure.canvas.draw()
Yan's avatar
Yan committed
206

Yan's avatar
Yan committed
207
    fig = axis.get_figure()
Yan's avatar
Yan committed
208
    fig.canvas.mpl_connect('button_press_event',
Yan's avatar
Yan committed
209
                           lambda action: pan_fun(action, axis, plot))
Yan's avatar
Yan committed
210

Yan's avatar
Yan committed
211

212
213
214
215
216
217
def textedit_factory(axis, plot_data):
    def annpicked(pickevent):
        if isinstance(pickevent.artist, matplotlib.text.Annotation) and\
                pickevent.mouseevent.button == 2:
            annotation = pickevent.artist
            textdial = QtWidgets.QInputDialog.getText(
218
                    None, "Enter new annotation", "",
219
                    text=annotation.get_text())
220
221
            if textdial[1]:
                annotation.set_text(textdial[0].replace('\\n', '\n'))
222
223
                if len(textdial[0]) == 0 and annotation in plot_data['texts']:
                    plot_data['texts'].remove(annotation)
3Yan's avatar
3Yan committed
224
                elif annotation in plot_data['annotation']:
225
226
227
                    annotation.set_bbox(ann_bbox)
                    plot_data['annotation'].remove(annotation)
                    plot_data['texts'].append(annotation)
3Yan's avatar
3Yan committed
228
                axis.figure.canvas.draw()
229
230
231
232

    axis.figure.canvas.mpl_connect('pick_event', annpicked)


233
def plot_subtime(augCanvas):
Yan's avatar
Yan committed
234
    """plot averaged spectrum of subselected part of the chromatogram"""
235
236
237
238
239
240
    slims = [augCanvas.spectplot.get_xlim(), augCanvas.spectplot.get_ylim()]
    chlims = [augCanvas.chromplot.get_xlim(), augCanvas.chromplot.get_ylim()]
    augCanvas.ms['annotation'].clear()
    augCanvas.spectplot.clear()
    augCanvas.chromplot.clear()

241
    for i, subset in enumerate(augCanvas.ds):
242
243
244
245
        selection = augCanvas.chrom['timesarg'][i]
        if len(augCanvas.ms['headers']) == len(augCanvas.ds):
            legend = legendize(augCanvas.ms['headers'][i][selection],
                               augCanvas.chrom)
Yan's avatar
Yan committed
246
247
        else:
            legend = None
3Yan's avatar
3Yan committed
248
        if cf.settings().value("view/oddeven", type=bool):
249
            for j in range(2):
Yan's avatar
Yan committed
250
251
                chromx = subset['chrom_dat'][0, :][j::2]
                chromy = subset['chrom_dat'][1, :][j::2]
252
253
                pop_plot(chromx, chromy, augCanvas.chromplot,
                         augCanvas.chrom, i*2+j, legend)
3Yan's avatar
3Yan committed
254
255
256
                if not np.array_equal(selection[j::2], []):
                    clr = i*2+j if selection[0] % 2 == 0\
                            else i*2+(1-j)
Yan's avatar
Yan committed
257
                    ms_x = subset['masses']
3Yan's avatar
3Yan committed
258
                    ms_y = np.mean(subset['matrix'][selection[j::2]], axis=0)
259
260
                    pop_plot(ms_x, ms_y, augCanvas.spectplot,
                             augCanvas.ms, clr, legend)
3Yan's avatar
3Yan committed
261
262
                    dots_x = subset['chrom_dat'][0, selection[j::2]]
                    dots_y = subset['chrom_dat'][1, selection[j::2]]
263
                    augCanvas.chromplot.plot(dots_x, dots_y, '.', color=(
3Yan's avatar
3Yan committed
264
                        colors[(clr) % len(colors)]/255))
3Yan's avatar
3Yan committed
265
266
267
        else:
            chromx = subset['chrom_dat'][0, :]
            chromy = subset['chrom_dat'][1, :]
268
269
            pop_plot(chromx, chromy, augCanvas.chromplot,
                     augCanvas.chrom, i, legend)
3Yan's avatar
3Yan committed
270
            if not np.array_equal(selection, []):
3Yan's avatar
3Yan committed
271
                ms_x = subset['masses']
3Yan's avatar
3Yan committed
272
                ms_y = np.mean(subset['matrix'][selection], axis=0)
273
274
                pop_plot(ms_x, ms_y, augCanvas.spectplot,
                         augCanvas.ms, i, legend)
3Yan's avatar
3Yan committed
275
276
                dots_x = subset['chrom_dat'][0, selection]
                dots_y = subset['chrom_dat'][1, selection]
277
                augCanvas.chromplot.plot(dots_x, dots_y, '.', color=(
3Yan's avatar
3Yan committed
278
                    colors[i % len(colors)]/255))
279

280
    augCanvas.spectplot.set_xlim(slims[0])
Yan's avatar
Yan committed
281
    if not cf.settings().value("view/autozoomy", type=bool):
282
283
        augCanvas.spectplot.set_ylim(slims[1])
    if not augCanvas.ms['headers'] == []:
Yan's avatar
Yan committed
284
285
286
287
288
        for ax in (augCanvas.spectplot, augCanvas.chromplot):
            ax.legend(loc=2)
            ax.get_legend().set_in_layout(False)
            ax.get_legend().set_visible(
                    cf.settings().value("view/legend", type=bool))
Yan's avatar
Yan committed
289
    else:
290
291
292
293
        autozoomy(augCanvas.spectplot)
    ann_spec(augCanvas.spectplot, augCanvas.ms)
    augCanvas.chromplot.set_xlim(chlims[0])
    augCanvas.chromplot.set_ylim(chlims[1])
294
    augCanvas.draw()
295
296


297
def pick_times(x_min, x_max, augCanvas):
298
    """subselect part of the chromatogram and plot it"""
Yan's avatar
Yan committed
299
300
301
302
    augCanvas.ds.timemin = x_min
    augCanvas.ds.timemax = x_max
    populate(augCanvas)
    """augCanvas.chrom['t_start'] = x_min
303
    augCanvas.chrom['t_end'] = x_max
3Yan's avatar
3Yan committed
304
    times = dt.argsubselect(np.concatenate(
305
306
307
        [subset['chrom_dat'][0] for subset in augCanvas.ds]), x_min, x_max)
    augCanvas.chrom['timesarg'].clear()
    for subset in augCanvas.ds:
308
309
        goodtimes = np.where((times < len(subset['chrom_dat'][0]))
                             & ~(times < 0))[0]
310
        augCanvas.chrom['timesarg'].append(times[goodtimes])
3Yan's avatar
3Yan committed
311
        times = times - len(subset['chrom_dat'][0])
312
    update_paramstable(augCanvas)
Yan's avatar
Yan committed
313
    plot_subtime(augCanvas)"""
314
315


316
def shift_times(event, augCanvas):
Yan's avatar
Yan committed
317
318
319
320
321
322
323
    """shifts times when arrow is pressed"""
    if event.key() == QtCore.Qt.Key_Left:
        move = -1
    elif event.key() == QtCore.Qt.Key_Right:
        move = +1
    else:
        return
324
325
326
327
    if not np.array_equal(augCanvas.chrom['timesarg'], []):
        x_min, x_max = augCanvas.chrom['t_start'], augCanvas.chrom['t_end']
        alltimes = np.concatenate([subset['chrom_dat'][0] for subset
                                   in augCanvas.ds])
Yan's avatar
Yan committed
328
        times = dt.argsubselect(alltimes, x_min, x_max) + move
329
        goodtimes = np.where((times < len(alltimes)) & ~(times < 0))[0]
Yan's avatar
Yan committed
330
        if not np.array_equal(goodtimes, []):
331
            x_min, x_max = alltimes[times[goodtimes[[0, -1]]]]
332
            pick_times(x_min, x_max, augCanvas)
Yan's avatar
Yan committed
333

Yan's avatar
Yan committed
334

335
def autozoomy(ms_spec):
3Yan's avatar
3Yan committed
336
    if cf.settings().value("view/autozoomy", type=bool) and not (
337
338
            np.array_equal(ms_spec.lines[0].get_xdata(), [0]) and
            len(ms_spec.lines) == 1):
Yan's avatar
Yan committed
339
        ms_spec.autoscale(True, 'y')
3Yan's avatar
3Yan committed
340
341
        gap = 0.01
        ymax = np.max([np.max(line.get_data()[1][dt.argsubselect(
Yan's avatar
Yan committed
342
            line.get_data()[0], *ms_spec.get_xlim())])*1.1
3Yan's avatar
3Yan committed
343
            for line in ms_spec.lines])
Yan's avatar
Yan committed
344
        ms_spec.set_ylim(-ymax*gap, ymax)
3Yan's avatar
3Yan committed
345
        ms_spec.figure.canvas.draw()
Yan's avatar
Yan committed
346

347

Yan's avatar
Yan committed
348
def ann_spec(ms_spec, msdata, ann_limit=0.01):
Yan's avatar
Yan committed
349
    """annotate spectrum
350
351

    First define the array, in which the annotation should occur.
Yan's avatar
Yan committed
352
    Then remove values which are invalid as local maximas. Local maximas are then
353
354
    reduced to a representation of the important ones by the sub_peaks
    function"""
355

3Yan's avatar
3Yan committed
356
    def sub_peaks(peakz, hardpeaks, xrange, yrange, coef_x=15, coef_y=20):
Yan's avatar
Yan committed
357
        """Returns reasonable subselection of local maximas"""
Yan's avatar
Yan committed
358
359
        hardxy = np.array([i.xy for i in hardpeaks], dtype=[
            ('x', np.float32), ('y', np.float32)])
3Yan's avatar
3Yan committed
360
        sort_peaks = np.flipud(np.sort(np.array(peakz), order='y')).copy()
Yan's avatar
Yan committed
361
362
        red_x = xrange / coef_x
        red_y = yrange / coef_y
Yan's avatar
Yan committed
363
        big_peaks = np.array([], dtype=[('x', np.float32), ('y', np.float32)])
3Yan's avatar
3Yan committed
364
        for peak in np.nditer(sort_peaks, flags=["zerosize_ok"]):
365
366
367
368
369
            if not (np.any((abs(peak['y'] - big_peaks['y']) < red_y)
                           & (abs(peak['x'] - big_peaks['x']) < red_x)) or
                    np.any((abs(peak['y'] - hardxy['y']) < red_y)
                           & (abs(peak['x'] - hardxy['x']) < red_x))):
                big_peaks = np.append(big_peaks, peak)
370
371
        return big_peaks

Yan's avatar
Yan committed
372
    peaks = []
3Yan's avatar
3Yan committed
373
374
    for line in ms_spec.lines:
        xdata, ydata = line.get_data()
Yan's avatar
Yan committed
375
376
377
378
379
380
381
382
383
384
385
        # Thanks to:
        # https://gist.github.com/ben741/d8c70b608d96d9f7ed231086b237ba6b
        minlim = ms_spec.get_ylim()[1] * ann_limit
        lims = [*ms_spec.get_xlim(),*ms_spec.get_ylim()]
        maxargs = np.where((xdata[1:-1] > lims[0]) & (xdata[1:-1] < lims[1]) &
                           (ydata[1:-1] > minlim) & (ydata[1:-1] < lims[3]) &
                           (ydata[1:-1] > ydata[0:-2]) &
                           (ydata[1:-1] > ydata[2:]))[0] + 1
        peakline = np.empty([len(maxargs)], dtype=[('x', np.float32), ('y', np.float32)])
        peakline['x'], peakline['y'] = xdata[maxargs], ydata[maxargs]
        peaks.append(peakline)
Yan's avatar
Yan committed
386

Yan's avatar
Yan committed
387
    # delete objects from the spectra
Yan's avatar
Yan committed
388
    for intensity in msdata['annotation']:
Yan's avatar
Yan committed
389
        intensity.remove()
Yan's avatar
Yan committed
390
    # remove them from tracking
Yan's avatar
Yan committed
391
    msdata['annotation'].clear()
Yan's avatar
Yan committed
392

393
394
395
396
397
398
399
400
    if len(peaks) == 0:
        return
    peaks = np.concatenate(peaks)

    s_peaks = sub_peaks(peaks, msdata['texts'],
                        np.diff(ms_spec.get_xlim()),
                        np.diff(ms_spec.get_ylim()))

Yan's avatar
Yan committed
401
    dispints = cf.settings().value("view/intensities", type=bool)
402
    for peak in s_peaks:
Yan's avatar
Yan committed
403
404
        annotation = '{0:.2f}\n{1: .2e}'.format(peak[0], peak[1])\
                if dispints else '{0:.2f}'.format(peak[0])
405
406
407
        peaktext = ms_spec.annotate(
            annotation, xy=(peak['x'], peak['y']), textcoords='data',
            picker=True, in_layout=False)
408
        msdata['annotation'].append(peaktext)
409

Yan's avatar
Yan committed
410

411
def pop_plot(xdata, ydata, plot, plot_data, colornum=0, legend=None, annotate=True):
Yan's avatar
Yan committed
412
    """Define and populate plot"""
3Yan's avatar
3Yan committed
413
    if len(xdata):
Yan's avatar
Yan committed
414
        print(colornum)
3Yan's avatar
3Yan committed
415
416
        plot.plot(xdata, ydata, linewidth=1, color=(
            colors[colornum % len(colors)]/255), label=legend)
Yan's avatar
Yan committed
417
    plot.set_title(plot_data['name'], loc="right")
Yan's avatar
Yan committed
418
419
    plot.set_xlabel(plot_data['xlabel'])
    plot.set_ylabel(plot_data['ylabel'])
420
    plot.set_ylim(plot.get_ylim()[1] * -0.01,
Yan's avatar
Yan committed
421
                  plot.get_ylim()[1] * 1.1)
Yan's avatar
Yan committed
422
    plot.yaxis.set_major_formatter(FixedScalarFormatter())
423
    # put hardcoded annotation if there is some
3Yan's avatar
3Yan committed
424
425
    if "texts" in plot_data and not any(
            data in plot.get_children() for data in plot_data['texts']):
426
        plot_data['texts'] = [plot.annotate(
427
            a.get_text(), a.xy, picker=True, bbox=ann_bbox, in_layout=False)
3Yan's avatar
3Yan committed
428
            for a in plot_data['texts']]
429
430
    if "annotation" in plot_data and annotate == True:
            ann_spec(plot, plot_data)
Yan's avatar
Yan committed
431
432
433
434
435
    if "xtics" in plot_data:
        plot.locator_params(nbins=plot_data["xtics"], axis='x')
        plot.minorticks_on()
        plot.tick_params(axis='y', which='minor', left=False)

Yan's avatar
Yan committed
436

Yan's avatar
Yan committed
437
def legendize(rawlegend, chrom_data):
438
    # sanity check
Yan's avatar
Yan committed
439
440
    if len(rawlegend) == 0:
        return None
441
442
443
    marks = ["-", "+"]
    quads = ["q3", "q1"]

Yan's avatar
Yan committed
444
    def translate(wut):
445
        if chrom_data['machtype'] == 47:
446
            if wut[1] in (0, 1):
447
448
449
450
451
                text = "{}{}ms; m/z = {:.1f}-{:.1f}".format(
                        marks[int(wut[0])], quads[int(wut[1])], *wut[4:])
            else:
                text = "{}ms^{} {:.2f}@{:.1f}V; m/z = {:.1f}-{:.1f}".format(
                        marks[int(wut[0])], *wut[1:])
Yan's avatar
Yan committed
452
453
454
455
        elif chrom_data['machtype'] in (57, 63):
            if int(wut[1]) == 1:
                text = "{}ms; m/z = {:.1f}-{:.1f}".format(
                        marks[int(wut[0])], *wut[2:])
Yan's avatar
Yan committed
456
            else:
Yan's avatar
Yan committed
457
458
459
460
                text = ("{}ms^{:.0f};" + "".join([" {:.2f}/{:.1f}@{:.1f}V" for
                                                  _ in range(int(wut[1])-1)]) +
                        "; m/z = {:.1f}-{:.1f}").format(
                                marks[int(wut[0])], *wut[1:])
Yan's avatar
Yan committed
461
        else:
462
            text = "unknown header type"
Yan's avatar
Yan committed
463
        return text
Yan's avatar
Yan committed
464
465
466
467
    uniqindexs = np.unique(np.array(rawlegend), return_index=True)\
        if np.array(rawlegend).dtype == np.dtype('O') else\
        np.unique(np.array(rawlegend), axis=0, return_index=True)
    strdata = [translate(i) for i in rawlegend[np.sort(uniqindexs[1])]]
Yan's avatar
Yan committed
468
    strtext = " and\n".join(strdata) + "; t = {:.2f}-{:.2f} min".format(
Yan's avatar
Yan committed
469
                    chrom_data['t_start'], chrom_data['t_end'])
Yan's avatar
Yan committed
470
471
472
    return strtext


473
def populate(augCanvas):
Yan's avatar
Yan committed
474
    """populate the GUI plots with desired dataset"""
475
    if np.array_equal(augCanvas.ds, []):
Yan's avatar
Yan committed
476
        return
477
    [i.clear() for i in (augCanvas.ms['annotation'],
478
     augCanvas.chromplot, augCanvas.spectplot)]
Yan's avatar
Yan committed
479

480
481
    if augCanvas.ms['predict']:
        # TODO: Fix the broken code
482
483
        predict = augCanvas.ms['predict']
        maxm = np.argmax(predict[1]) + predict[0]
3Yan's avatar
3Yan committed
484
485
        maxseek = dt.argsubselect(linex, maxm-.5, maxm+.5)
        maxpos = maxseek[np.argmax(liney[maxseek])]
486
        crudeints = predict[1] * augCanvas.ms['y'][maxpos]
487
        crudemasses = (np.arange(len(predict[1])) + linex[maxpos])
Yan's avatar
Yan committed
488
        pmasses, pints = [], []
489
490
        [pmasses.extend([np.nan, i, i]) for i in crudemasses]
        [pints.extend([np.nan, 0, i]) for i in crudeints]
491
        augCanvas.spectplot.plot(pmasses, pints, linewidth=1)
Yan's avatar
Yan committed
492

Yan's avatar
Yan committed
493
494
495
496
497
498
    chromxy = augCanvas.ds.get_chromatograms()
    msxy = augCanvas.ds.get_spectra()
    for i in range(len(msxy)):
        #if len(augCanvas.ds.headers) == len(augCanvas.ds.chromatograms):
        if False:
            legend = legendize(augCanvas.ds.headers[i], augCanvas.chrom)
Yan's avatar
Yan committed
499
500
        else:
            legend = None
Yan's avatar
Yan committed
501
502
503
504
        pop_plot(msxy[i][0], msxy[i][1], augCanvas.spectplot, augCanvas.ms, i, legend)
        pop_plot(chromxy[i][0], chromxy[i][1], augCanvas.chromplot,
                augCanvas.chrom, i, legend)
        """if cf.settings().value("view/oddeven", type=bool):
3Yan's avatar
3Yan committed
505
            msx = subset['masses']
506
            for j in range(2):
3Yan's avatar
3Yan committed
507
508
509
                msy = np.mean(subset['matrix'][j::2], axis=0)
                chromx = subset['chrom_dat'][0, :][j::2]
                chromy = subset['chrom_dat'][1, :][j::2]
510
511
512
513
                pop_plot(msx, msy, augCanvas.spectplot, augCanvas.ms,
                         i*2+j, legend)
                pop_plot(chromx, chromy, augCanvas.chromplot,
                         augCanvas.chrom, i*2+j, legend)
514
        else:
3Yan's avatar
3Yan committed
515
516
517
518
            msx = subset['masses']
            msy = np.mean(subset['matrix'], axis=0)
            chromx = subset['chrom_dat'][0, :]
            chromy = subset['chrom_dat'][1, :]
519
520
            pop_plot(msx, msy, augCanvas.spectplot, augCanvas.ms, i, legend)
            pop_plot(chromx, chromy, augCanvas.chromplot, augCanvas.chrom,
Yan's avatar
Yan committed
521
                     i, legend)"""
522
523
    for ax in (augCanvas.spectplot, augCanvas.chromplot):
        if not augCanvas.ms['headers'] == []:
Yan's avatar
Yan committed
524
            ax.legend(loc=2)
Yan's avatar
Yan committed
525
            ax.get_legend().set_in_layout(False)
Yan's avatar
Yan committed
526
527
            ax.get_legend().set_visible(
                    cf.settings().value("view/legend", type=bool))
3Yan's avatar
3Yan committed
528
529
        ax.autoscale(True)
        ax.set_ylim(ax.get_ylim()[1]*-0.01, ax.get_ylim()[1]*1.1)
Yan's avatar
Yan committed
530
    augCanvas.constrained_draw()
3Yan's avatar
3Yan committed
531
    return
Yan's avatar
Yan committed
532
533


534
535
536
def update_paramstable(augCanvas):
    if len(augCanvas.ms['params']) == 0:
        augCanvas.paramstable.setRowCount(0)
Yan's avatar
Yan committed
537
        return
538
539
540
541
542
543
    elif len(augCanvas.ms['params'][0]) == augCanvas.paramstable.rowCount():
        states = [augCanvas.paramstable.cellWidget(row, 0).checkState()
                  for row in range(augCanvas.paramstable.rowCount())]
    else:
        states = False
        augCanvas.paramstable.setRowCount(len(augCanvas.ms['params'][0]))
544
    for row, paramname in enumerate(augCanvas.ms['params'][0]):
545
546
547
        [augCanvas.paramstable.setItem(row, col, QtWidgets.QTableWidgetItem())
         for col in range(1, 3)]
        augCanvas.paramstable.setCellWidget(row, 0, QtWidgets.QCheckBox())
548
        if states:
549
550
            augCanvas.paramstable.cellWidget(row, 0).setCheckState(states[row])
        augCanvas.paramstable.item(row, 1).setText(paramname)
551
        vals = [param[row] for param in augCanvas.ms['params'][1]
552
                if (param[0] >= augCanvas.chrom['t_start'] and
553
                param[0] <= augCanvas.chrom['t_end'])]
Yan's avatar
Yan committed
554
        if len(vals) == 0:
Yan's avatar
Yan committed
555
            text = ""
Yan's avatar
Yan committed
556
557
558
559
560
561
562
563
        elif all([type(val) in [np.float32, np.float64] for val in vals]):
            aver = np.average(vals)
            minim = min(vals)
            maxim = max(vals)
            text = "{:.2f} (from {:.2f} to {:.2f})".format(aver, minim, maxim)
        else:
            values = [str(i) for i in np.unique(np.array(vals), axis=0)]
            text = " or ".join(values)
564
        augCanvas.paramstable.item(row, 2).setText(text)