graphtools.py 23 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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)
        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.filename = None
        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"""
299
300
    augCanvas.chrom['t_start'] = x_min
    augCanvas.chrom['t_end'] = x_max
3Yan's avatar
3Yan committed
301
    times = dt.argsubselect(np.concatenate(
302
303
304
        [subset['chrom_dat'][0] for subset in augCanvas.ds]), x_min, x_max)
    augCanvas.chrom['timesarg'].clear()
    for subset in augCanvas.ds:
305
306
        goodtimes = np.where((times < len(subset['chrom_dat'][0]))
                             & ~(times < 0))[0]
307
        augCanvas.chrom['timesarg'].append(times[goodtimes])
3Yan's avatar
3Yan committed
308
        times = times - len(subset['chrom_dat'][0])
309
310
    update_paramstable(augCanvas)
    plot_subtime(augCanvas)
311
312


313
def shift_times(event, augCanvas):
Yan's avatar
Yan committed
314
315
316
317
318
319
320
    """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
321
322
323
324
    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
325
        times = dt.argsubselect(alltimes, x_min, x_max) + move
326
        goodtimes = np.where((times < len(alltimes)) & ~(times < 0))[0]
Yan's avatar
Yan committed
327
        if not np.array_equal(goodtimes, []):
328
            x_min, x_max = alltimes[times[goodtimes[[0, -1]]]]
329
            pick_times(x_min, x_max, augCanvas)
Yan's avatar
Yan committed
330

Yan's avatar
Yan committed
331

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

344

Yan's avatar
Yan committed
345
def ann_spec(ms_spec, msdata, ann_limit=0.01):
Yan's avatar
Yan committed
346
    """annotate spectrum
347
348

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

3Yan's avatar
3Yan committed
353
    def sub_peaks(peakz, hardpeaks, xrange, yrange, coef_x=15, coef_y=20):
Yan's avatar
Yan committed
354
        """Returns reasonable subselection of local maximas"""
Yan's avatar
Yan committed
355
356
        hardxy = np.array([i.xy for i in hardpeaks], dtype=[
            ('x', np.float32), ('y', np.float32)])
3Yan's avatar
3Yan committed
357
        sort_peaks = np.flipud(np.sort(np.array(peakz), order='y')).copy()
Yan's avatar
Yan committed
358
359
        red_x = xrange / coef_x
        red_y = yrange / coef_y
Yan's avatar
Yan committed
360
        big_peaks = np.array([], dtype=[('x', np.float32), ('y', np.float32)])
3Yan's avatar
3Yan committed
361
        for peak in np.nditer(sort_peaks, flags=["zerosize_ok"]):
362
363
364
365
366
            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)
367
368
        return big_peaks

Yan's avatar
Yan committed
369
    peaks = []
3Yan's avatar
3Yan committed
370
371
    for line in ms_spec.lines:
        xdata, ydata = line.get_data()
Yan's avatar
Yan committed
372
373
374
375
376
377
378
379
380
381
382
        # 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
383

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

390
391
392
393
394
395
396
397
    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
398
    dispints = cf.settings().value("view/intensities", type=bool)
399
    for peak in s_peaks:
Yan's avatar
Yan committed
400
401
        annotation = '{0:.2f}\n{1: .2e}'.format(peak[0], peak[1])\
                if dispints else '{0:.2f}'.format(peak[0])
402
403
404
        peaktext = ms_spec.annotate(
            annotation, xy=(peak['x'], peak['y']), textcoords='data',
            picker=True, in_layout=False)
405
        msdata['annotation'].append(peaktext)
406

Yan's avatar
Yan committed
407

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

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

Yan's avatar
Yan committed
440
    def translate(wut):
441
        if chrom_data['machtype'] == 47:
442
            if wut[1] in (0, 1):
443
444
445
446
447
                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
448
449
450
451
        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
452
            else:
Yan's avatar
Yan committed
453
454
455
456
                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
457
        else:
458
            text = "unknown header type"
Yan's avatar
Yan committed
459
        return text
Yan's avatar
Yan committed
460
461
462
463
    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
464
    strtext = " and\n".join(strdata) + "; t = {:.2f}-{:.2f} min".format(
Yan's avatar
Yan committed
465
                    chrom_data['t_start'], chrom_data['t_end'])
Yan's avatar
Yan committed
466
467
468
    return strtext


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

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

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


526
527
528
def update_paramstable(augCanvas):
    if len(augCanvas.ms['params']) == 0:
        augCanvas.paramstable.setRowCount(0)
Yan's avatar
Yan committed
529
        return
530
531
532
533
534
535
    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]))
536
    for row, paramname in enumerate(augCanvas.ms['params'][0]):
537
538
539
        [augCanvas.paramstable.setItem(row, col, QtWidgets.QTableWidgetItem())
         for col in range(1, 3)]
        augCanvas.paramstable.setCellWidget(row, 0, QtWidgets.QCheckBox())
540
        if states:
541
542
            augCanvas.paramstable.cellWidget(row, 0).setCheckState(states[row])
        augCanvas.paramstable.item(row, 1).setText(paramname)
543
        vals = [param[row] for param in augCanvas.ms['params'][1]
544
                if (param[0] >= augCanvas.chrom['t_start'] and
545
                param[0] <= augCanvas.chrom['t_end'])]
Yan's avatar
Yan committed
546
        if len(vals) == 0:
Yan's avatar
Yan committed
547
            text = ""
Yan's avatar
Yan committed
548
549
550
551
552
553
554
555
        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)
556
        augCanvas.paramstable.item(row, 2).setText(text)