drltools.py 27.8 KB
Newer Older
Yan's avatar
Yan committed
1
2
3
4
5
#!/usr/bin/env python3
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5 import QtCore
from PyQt5 import QtWidgets
6
from PyQt5 import QtGui
Yan's avatar
Yan committed
7
from PyQt5 import QtPrintSupport
3Yan's avatar
3Yan committed
8
from copy import copy
Yan's avatar
Yan committed
9
10
import matplotlib
import numpy as np
Yan's avatar
Yan committed
11
12
import prasopes.datatools as dt
import prasopes.graphtools as gt
Yan's avatar
Yan committed
13
import prasopes.filetools as ft
Yan's avatar
Yan committed
14
import os.path
Yan's avatar
Yan committed
15
import logging
Yan's avatar
Yan committed
16
17
matplotlib.use("Qt5Agg")

Yan's avatar
Yan committed
18

Yan's avatar
Yan committed
19
20
21
logger = logging.getLogger('drlLogger')


Yan's avatar
Yan committed
22
class HBar(QtWidgets.QFrame):
23
    """horizontal bar class"""
Yan's avatar
Yan committed
24
25
26
27
28
29
    def __init__(self):
        super(HBar, self).__init__()
        self._main = QtWidgets.QWidget()
        self.setFrameShape(QtWidgets.QFrame.HLine)


Yan's avatar
Yan committed
30
def floatize(table, row, column, nonneg=True):
31
32
33
34
    """grabs the tableWidgetItem and transforms its text safely to
    float, if the text is not acceptable as float, returns zero"""
    imptext = table.item(row, column).text()
    validator = QtGui.QDoubleValidator()
Yan's avatar
Yan committed
35
36
    if nonneg == True:
        validator.setBottom(0)
37
38
39
40
41
42
43
44
45
    status = validator.validate(imptext, 0)[0]
    if status == QtGui.QValidator.Acceptable:
        outfloat = float(imptext)
    else:
        outfloat = 0
    return outfloat


def update_profile(pt, row, dataset):
Yan's avatar
Yan committed
46
    """parent table profile spectrum updating procedure"""
Yan's avatar
Yan committed
47
    # Dont do anything to graph when the spectrum is not populated
Yan's avatar
Yan committed
48
    logger.debug("updating parent table row {} profile".format(row))
Yan's avatar
Yan committed
49
    if isinstance(dataset['masses'], type(None)):
50
51
        return

52
    spectrum = pt.cellWidget(row, 3).figure.get_axes()[0]
Yan's avatar
Yan committed
53
    masses = dataset['masses']
54
55
    massargs = dt.argsubselect(
        masses, floatize(pt, row, 1), floatize(pt, row, 2))
56
    yshape = np.mean(dataset['matrix'], axis=0)
Yan's avatar
Yan committed
57
    spectrum.clear()
58
59
60
    spectrum.plot(masses, yshape, ':', color='gray')
    spectrum.plot(masses[massargs], yshape[massargs], 'r')
    xex = max((masses[massargs[-1]]-masses[massargs[0]])*0.25, 0.20)
Yan's avatar
Yan committed
61
62
63
    spectrum.set_xlim(masses[massargs[0]]-xex,
                      masses[massargs[-1]]+xex)
    ymax = max(yshape[massargs])
64
65
66
    spectrum.set_ylim(ymax*-0.1, ymax*1.2)
    spectrum.figure.canvas.draw()

Yan's avatar
Yan committed
67

3Yan's avatar
3Yan committed
68
69
70
def get_intensity(row, ds, drls):
    startm = floatize(drls['pt'], row, 1)
    endm = floatize(drls['pt'], row, 2)
Yan's avatar
Yan committed
71
    massargs = dt.argsubselect(ds['masses'], startm, endm)
Yan's avatar
Yan committed
72
73
74
75
    #prevent division by 0
    intensity = np.divide(np.sum( ds['matrix'].T[massargs].T, axis=1),
            np.clip(ds['chrom_dat'][1], np.nextafter(
                0, 1, dtype=np.float32), None))
Yan's avatar
Yan committed
76
77
78
    return intensity


3Yan's avatar
3Yan committed
79
def get_daughterset(ds, drls):
Yan's avatar
Yan committed
80
81
    """Fuction to acquire the curves of the daugher ions"""
    logger.info("getting set of the daughter ions")
Yan's avatar
Yan committed
82
    # TODO: write a less resources demanding function - probably "per-line"
Yan's avatar
Yan committed
83
    names = []
3Yan's avatar
3Yan committed
84
    times = ds['chrom_dat'][0, :] - drls['tshift'].value()
Yan's avatar
Yan committed
85
    intensities = []
3Yan's avatar
3Yan committed
86
87
88
    for row in range(drls['dt'].rowCount()):
        if drls['dt'].cellWidget(row, 0).checkState() == 2:
            intensity = get_intensity(row, ds, drls)
3Yan's avatar
3Yan committed
89
90
91
92
            corlist = []
            for i in range(drls['cors']):
                cor = drls['dt'].cellWidget(row, 1+i*2).currentIndex() - 1
                if cor not in (-2, -1):
Yan's avatar
Yan committed
93
                    factor = floatize(drls['dt'], row, 2+i*2, False)
3Yan's avatar
3Yan committed
94
95
96
97
98
99
                    correction = get_intensity(cor, ds, drls) * factor
                    intensity = intensity - correction
                    corlist.append("{} * {}".format(
                        drls['dt'].item(row,2+i*2).text(),
                        drls['dt'].cellWidget(row,1+i*2).currentText()))
            cortext = " + ".join(corlist)
Yan's avatar
Yan committed
100
            intensities.append(intensity)
3Yan's avatar
3Yan committed
101
            names.append("{} - ({})".format(
3Yan's avatar
3Yan committed
102
                drls['dt'].item(row, 0).text(),
3Yan's avatar
3Yan committed
103
                cortext))
Yan's avatar
Yan committed
104
105
106
    return names, times, intensities


3Yan's avatar
3Yan committed
107
def get_parentset(ds, drls):
108
109
110
111
112
    names = []
    times = ds['chrom_dat'][0, :]
    intensities = []
    # TODO: resolve intensities trouble
    rowlist = []
3Yan's avatar
3Yan committed
113
114
    for row in range(drls['dt'].rowCount()):
        if drls['dt'].cellWidget(row, 0).checkState() == 2:
115
            rowlist.append(row)
3Yan's avatar
3Yan committed
116
117
            if drls['dt'].cellWidget(row, 1).currentIndex() > 0\
                    and floatize(drls['dt'], row, 2) != 0:
118
                rowlist.append(
3Yan's avatar
3Yan committed
119
                    drls['dt'].cellWidget(row, 1).currentIndex()-1)
120
    for row in set(rowlist):
3Yan's avatar
3Yan committed
121
            intensity = get_intensity(row, ds, drls)
122
            intensities.append(intensity)
3Yan's avatar
3Yan committed
123
            names.append(drls['dt'].item(row, 0).text())
124
125
126
    return names, times, intensities


3Yan's avatar
3Yan committed
127
def update_drlspectrum(ds, drls, drlspectrum):
Yan's avatar
Yan committed
128
129
    """Generic DRL spectrum updating procedure"""
    logger.info("updating DRL spectrum")
130
    # Dont do anything when the dataset is not populated
Yan's avatar
Yan committed
131
    if isinstance(ds['masses'], type(None)):
Yan's avatar
Yan committed
132
        return
133
    colors = np.array([[0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255],
134
135
136
                       [0, 200, 255], [255, 200, 0], [255, 100, 0],
                       [200, 50, 0], [255, 0, 200], [0, 100, 0],
                       [0, 100, 255], [100, 100, 100]])
Yan's avatar
Yan committed
137

138
    # TODO: write a less resources demanding function
3Yan's avatar
3Yan committed
139
    names, times, intensities = get_daughterset(ds, drls)
Yan's avatar
Yan committed
140
141
    for i in range(len(drlspectrum.lines)):
        drlspectrum.lines[0].remove()
142

3Yan's avatar
3Yan committed
143
    drlspectrum.axvline(0, color="#FF000088", linestyle=":")
3Yan's avatar
3Yan committed
144

145
    i = 0
3Yan's avatar
3Yan committed
146
147
148
149
    for row in range(drls['dt'].rowCount()):
        if drls['dt'].cellWidget(row, 0).checkState() == 2:
            drls['dt'].blockSignals(True)
            drls['dt'].item(row, 0).setBackground(QtGui.QBrush(
150
                QtGui.QColor(*colors[row % len(colors)], alpha=50)))
3Yan's avatar
3Yan committed
151
152
            drls['dt'].blockSignals(False)
            label = " {}".format(drls['pt'].item(row, 0).text())
3Yan's avatar
3Yan committed
153
154
            intensity = intensities[i]
            if drls['rel'].checkState() == 2:
Yan's avatar
Yan committed
155
156
157
                #Do not divide by 0
                intensity = np.divide(intensity, np.clip(np.sum(
                    intensities, 0), np.nextafter(
3Yan's avatar
3Yan committed
158
                        0, 1, dtype=np.float32), None), dtype=np.float64)
3Yan's avatar
3Yan committed
159
            drlspectrum.plot(times, intensity, label=label,
160
161
                             color=(colors[row % len(colors)] / 255))
            i += 1
162
        else:
3Yan's avatar
3Yan committed
163
            drls['dt'].item(row, 0).setBackground(QtGui.QBrush())
164

165
    if len(names) != 0:
3Yan's avatar
3Yan committed
166
167
168
169
        gmax = np.amax(intensities)
        if drls['rel'].checkState() == 2:
            gmax = 1
        drlspectrum.set_ylim(top=gmax*1.1, bottom=gmax*-0.01)
170
        drlspectrum.legend(loc=2)
171
172
    drlspectrum.figure.canvas.draw()

Yan's avatar
Yan committed
173

Yan's avatar
Yan committed
174
def gettableitemlist(ptable):
175
    ion_list = []
Yan's avatar
Yan committed
176
177
178
    for row in range(ptable.rowCount()):
        text = []
        for i in range(3):
179
180
            if not isinstance(ptable.item(row, i), type(None)):
                frg = ptable.item(row, i).text()
Yan's avatar
Yan committed
181
182
183
184
185
            else:
                frg = ""
            text.append(frg)
        line = "{} ({}-{})".format(*text)
        ion_list.append(line)
186
187
188
    return ion_list


3Yan's avatar
3Yan committed
189
def update_corrfors(drls):
190
    """update corrections selection layout of the daughter table"""
3Yan's avatar
3Yan committed
191
192
    ionlist = gettableitemlist(drls['pt'])
    for row in range(drls['dt'].rowCount()):
3Yan's avatar
3Yan committed
193
194
195
196
197
198
199
200
201
        for i in range(drls['cors']):
            corfor = drls['dt'].cellWidget(row, 1+i*2)
            index = corfor.currentIndex()
            corfor.blockSignals(True)
            corfor.clear()
            corfor.addItem("")
            corfor.addItems(ionlist)
            corfor.setCurrentIndex(index)
            corfor.blockSignals(False)
Yan's avatar
Yan committed
202

203

3Yan's avatar
3Yan committed
204
def ptable_changed(row, column, ds, drls, drlspectrum):
205
    """routine called by change of the ptable spectra"""
Yan's avatar
Yan committed
206
    logger.debug("ptable changed routine called")
3Yan's avatar
3Yan committed
207
208
    update_corrfors(drls)
    drls['dt'].item(row, 0).setText(gettableitemlist(drls['pt'])[row])
209
    if column in (1, 2):
3Yan's avatar
3Yan committed
210
        update_profile(drls['pt'], row, ds)
211

212

3Yan's avatar
3Yan committed
213
def dtable_changed(row, column, ds, drls, drlspectrum):
214
    """routine called by change of the dtable spectra"""
Yan's avatar
Yan committed
215
    logger.info("Change in the daughter ion table detected")
3Yan's avatar
3Yan committed
216
    if drls['dt'].cellWidget(row, 0).checkState() == 2:
Yan's avatar
Yan committed
217
218
219
        if (column == 0) or (column % 2 == 0 and drls['dt'].cellWidget(
            row, column-1).currentIndex() != 0):
            update_drlspectrum(ds, drls, drlspectrum)
220

Yan's avatar
Yan committed
221

3Yan's avatar
3Yan committed
222
def corr_changed(correction, ds, drls, drlspectrum):
223
    """routine called by change of correction for ion"""
Yan's avatar
Yan committed
224
    logger.info('''Change of the "correct to" detected''')
3Yan's avatar
3Yan committed
225
226
227
228
    for i in range(drls['dt'].rowCount()):
        for j in range(drls['dt'].columnCount()):
            if correction == drls['dt'].cellWidget(i, 1+j*2):
                row, column = i, 1 + j*2
3Yan's avatar
3Yan committed
229
    if (drls['dt'].cellWidget(row, 0).checkState() == 2
3Yan's avatar
3Yan committed
230
       and floatize(drls['dt'], row, column+1, False) != 0):
3Yan's avatar
3Yan committed
231
        update_drlspectrum(ds, drls, drlspectrum)
232
233


3Yan's avatar
3Yan committed
234
def remove_rows(ds, drls, drlspectrum, rows=None):
Yan's avatar
Yan committed
235
    # TODO: maybe nicer selection in future, but this works for now
3Yan's avatar
3Yan committed
236
    logger.info("remowing rows")
237
238
    if rows == None:
        rows = reversed(list(set(
3Yan's avatar
3Yan committed
239
            map(lambda x: x.row(), drls['pt'].selectedIndexes()))))
240
    cors = []
241
    for row in rows:
3Yan's avatar
3Yan committed
242
243
244
245
        drls['dt'].cellWidget(row,0).setCheckState(0)
        drls['dt'].removeRow(row)
        drls['pt'].removeRow(row)
        for i in range(drls['dt'].rowCount()):
3Yan's avatar
3Yan committed
246
247
            for cornum in range(drls['cors']):
                corfor = drls['dt'].cellWidget(i, 1+cornum*2)
3Yan's avatar
3Yan committed
248
                cors.append(corfor)
3Yan's avatar
3Yan committed
249
250
251
252
253
254
255
                corfor.disconnect()
                index = corfor.currentIndex()
                corfor.clear()
                corfor.addItem("")
                corfor.addItems(gettableitemlist(drls['pt']))
                if index == row+1:
                    corfor.setCurrentIndex(0)
3Yan's avatar
3Yan committed
256
                    corr_changed(corfor, ds, drls, drlspectrum)
3Yan's avatar
3Yan committed
257
258
259
260
                elif index > row+1:
                    corfor.setCurrentIndex(index-1)
                else:
                    corfor.setCurrentIndex(index)
3Yan's avatar
3Yan committed
261
262
    list(map(lambda x: x.currentIndexChanged.connect(lambda:
        corr_changed(x, ds, drls, drlspectrum)), cors))
263
264


3Yan's avatar
3Yan committed
265
def add_line(ds, drls, drlspectrum):
266
    """add parent ion to the table"""
Yan's avatar
Yan committed
267
    logger.debug("adding line")
3Yan's avatar
3Yan committed
268
    newrow = drls['pt'].rowCount()
269

3Yan's avatar
3Yan committed
270
271
    drls['pt'].blockSignals(True)
    drls['dt'].blockSignals(True)
272

3Yan's avatar
3Yan committed
273
    drls['pt'].setRowCount(newrow + 1)
274
    for i in range(3):
3Yan's avatar
3Yan committed
275
        drls['pt'].setItem(newrow, i, QtWidgets.QTableWidgetItem())
276
        if newrow is not 0:
3Yan's avatar
3Yan committed
277
278
            drls['pt'].item(newrow, i).setText(str(floatize(
                    drls['pt'], newrow-1, i)+1))
279

280
    ion_graph = Figure(figsize=(3, 1.5), dpi=100, facecolor="None")
281
282
    ion_graph.add_subplot(111, facecolor=(1, 1, 1, 0.8),
                          position=(-0.01, -0.01, 1.02, 1.02))
283
284
285
    graph_canvas = FigureCanvas(ion_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
3Yan's avatar
3Yan committed
286
    drls['pt'].setCellWidget(newrow, 3, graph_canvas)
287

3Yan's avatar
3Yan committed
288
    drls['dt'].setRowCount(newrow + 1)
Yan's avatar
Yan committed
289
    checkbox = QtWidgets.QCheckBox()
3Yan's avatar
3Yan committed
290
    checkbox.setFocusProxy(drls['dt'])
291
292
    dname = QtWidgets.QTableWidgetItem()
    dname.setFlags(dname.flags() & ~QtCore.Qt.ItemIsEditable)
Yan's avatar
Yan committed
293
    dname.setTextAlignment(QtCore.Qt.AlignRight)
3Yan's avatar
3Yan committed
294
295
    drls['dt'].setItem(newrow, 0, dname)
    drls['dt'].setCellWidget(newrow, 0, checkbox)
3Yan's avatar
3Yan committed
296
297

    for i in range(drls['cors']):
3Yan's avatar
3Yan committed
298
299
300
301
        col = copy(1+i*2)
        drls['dt'].setCellWidget(newrow, col, QtWidgets.QComboBox())
        drls['dt'].cellWidget(newrow, col).setFrame(False)
        drls['dt'].setItem(newrow, col+1, QtWidgets.QTableWidgetItem())
302

3Yan's avatar
3Yan committed
303
304
    drls['pt'].blockSignals(False)
    drls['dt'].blockSignals(False)
305

3Yan's avatar
3Yan committed
306
307
308
309
310
311
312
313
    #dirty hack, but if if cycle is applied it assigns all cellWidgets
    #to a maximal value of i (as this is the value of i after the end
    #of the cycle)
    cors = list(map(lambda x: drls['dt'].cellWidget(newrow, 1+x*2),
                    range(drls['cors'])))
    list(map(lambda x: x.currentIndexChanged.connect(lambda:
        corr_changed(x, ds, drls, drlspectrum)), cors))

3Yan's avatar
3Yan committed
314
    ptable_changed(newrow, 1, ds, drls, drlspectrum)
315
    select_all_btn_up(ds, drls, drlspectrum)
316

Yan's avatar
Yan committed
317
    checkbox.stateChanged.connect(lambda: update_drlspectrum(
3Yan's avatar
3Yan committed
318
        ds, drls, drlspectrum))
319
320
    checkbox.stateChanged.connect(lambda: select_all_btn_up(
        ds, drls, drlspectrum))
321
322


Yan's avatar
Yan committed
323
def iontable(labels):
324
325
    """creates a table for ions"""
    table = QtWidgets.QTableWidget(columnCount=len(labels))
326
    table.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
327
328
                        QtWidgets.QSizePolicy.Expanding)
    table.setHorizontalHeaderLabels(labels)
329
330
331
332
333
    table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

    for n in range(table.columnCount()):
        table.horizontalHeader().setSectionResizeMode(
            n, QtWidgets.QHeaderView.Stretch)
Yan's avatar
Yan committed
334
    table.setMinimumSize(600, 0)
335

336
    return table
337
338


3Yan's avatar
3Yan committed
339
def load_drltables(parent, dataset, drls, drlspectrum):
Yan's avatar
Yan committed
340
341
342
343
344
345
346
347
    filename = QtWidgets.QFileDialog.getOpenFileName(
            caption="Load DRL config tables",
            filter="comma-separated values (*.csv)")[0]
    if filename is not '':
        names = []
        start_masses = []
        end_masses = []
        states = []
3Yan's avatar
3Yan committed
348
        corrections = []
Yan's avatar
Yan committed
349

3Yan's avatar
3Yan committed
350

Yan's avatar
Yan committed
351
352
        with open(filename, 'r') as cfile:
            rawdata = cfile.read().splitlines()
3Yan's avatar
3Yan committed
353
354
        for i in range(len(rawdata[0].split(","))-4):
            corrections.append([])
Yan's avatar
Yan committed
355
356
        for i in range(1, len(rawdata)):
            rawline = rawdata[i].split(",")
357
            # TODO: rawline[4] can be -1 - decide if accept this behaviour
Yan's avatar
Yan committed
358
359
360
361
            n = len(rawline)
            if n < 6 or not n % 2 == 0 or (int(rawline[3]) not in
                range(3)) or not (set(map(int, rawline[4:n:2])) &
                set(range(-1, n))):
Yan's avatar
Yan committed
362
363
364
365
366
367
                QtWidgets.QMessageBox.warning(
                    parent, "Load DRL config tables",
                    "Config file corrupted on line {},"
                    " cancelling request".format(i+1))
                return
            for j, k in enumerate((names, start_masses, end_masses,
3Yan's avatar
3Yan committed
368
                                   states, *corrections)):
Yan's avatar
Yan committed
369
                k.append(rawline[j])
3Yan's avatar
3Yan committed
370
371
372
        for row in reversed(range(drls['pt'].rowCount())):
            drls['dt'].removeRow(row)
            drls['pt'].removeRow(row)
373
        # first populate only the parent table
Yan's avatar
Yan committed
374
        for i in range(len(names)):
3Yan's avatar
3Yan committed
375
376
377
378
            add_line(dataset, drls, drlspectrum)
            drls['pt'].item(i, 0).setText(names[i])
            drls['pt'].item(i, 1).setText(start_masses[i])
            drls['pt'].item(i, 2).setText(end_masses[i])
379
        # and after that the daughter table
Yan's avatar
Yan committed
380
        for i in range(len(names)):
3Yan's avatar
3Yan committed
381
            for j in range(int((len(rawline)-4)/2)):
3Yan's avatar
3Yan committed
382
                drls['dt'].cellWidget(i, 1+j*2).setCurrentIndex(
3Yan's avatar
3Yan committed
383
384
                    int(corrections[0+j*2][i]))
                drls['dt'].item(i, 2+j*2).setText(corrections[1+j*2][i])
3Yan's avatar
3Yan committed
385
            drls['dt'].cellWidget(i, 0).setCheckState(int(states[i]))
Yan's avatar
Yan committed
386
387


3Yan's avatar
3Yan committed
388
def save_drlconfig(drls, parent, exp_f_name=None):
Yan's avatar
Yan committed
389
    """safe DRL table layout so it can be summoned when needed"""
390
391
392
393
    if exp_f_name == None:
        exp_f_name = ft.get_save_filename(
            "Save DRL table layout", "comma-separated values (*.csv)",
            "csv", parent)
Yan's avatar
Yan committed
394
    if exp_f_name is not '':
3Yan's avatar
3Yan committed
395
396
397
398
399
        corlist = []
        for i in range(drls['cors']):
            corlist.append("corrected_to_{}, factor_{} ".format(
                i+1, i+1))
        cortext = ", ".join(corlist)
Yan's avatar
Yan committed
400
401
        expf = open(exp_f_name, 'w')
        expf.write("#ion_name, start m/z, end m/z, visible,"
3Yan's avatar
3Yan committed
402
                   "{}\n".format(cortext))
3Yan's avatar
3Yan committed
403
        for row in range(drls['pt'].rowCount()):
Yan's avatar
Yan committed
404
405
            vals = []
            for i in range(3):
3Yan's avatar
3Yan committed
406
407
                vals.append(drls['pt'].item(row, i).text())
            vals.append(drls['dt'].cellWidget(row, 0).checkState())
3Yan's avatar
3Yan committed
408
409
410
            for i in range(drls['cors']):
                vals.append(drls['dt'].cellWidget(row, 1+i*2).currentIndex())
                vals.append(drls['dt'].item(row, 2+i*2).text())
Yan's avatar
Yan committed
411
412
413
            expf.write("{}\n".format((",".join(map(str, vals)))))
        expf.close()

Yan's avatar
Yan committed
414

3Yan's avatar
3Yan committed
415
def export_drlspectrum(parent, fn, ds, drls):
Yan's avatar
Yan committed
416
417
    if fn[0] is None:
        QtWidgets.QMessageBox.warning(
418
            None, "Export DRL dataset",
Yan's avatar
Yan committed
419
420
            "Nothing to export, cancelling request")
        return
3Yan's avatar
3Yan committed
421
422
423
    names, times, intensities = get_daughterset(ds, drls)
    subset = np.where(times > 0)[0]
    times = times[subset]
Yan's avatar
Yan committed
424
    intensities = list(map(lambda x: x[subset], intensities))
3Yan's avatar
3Yan committed
425
426

    pnames, ptimes, pintensities = get_parentset(ds, drls)
427
428
429
430
431
432
433
434
435
436
437
438
439
440
    if names == []:
        QtWidgets.QMessageBox.warning(
            None, "Export DRL dataset",
            "Nothing to export, cancelling request")
        return
    fname = QtWidgets.QFileDialog.getSaveFileName(
            None,"Export DRL data",
        options=(QtWidgets.QFileDialog.DontConfirmOverwrite |
        QtWidgets.QFileDialog.HideNameFilterDetails))[0]
    if fname is '':
        return
    exp_f_name = list(map(lambda x: "{}/{}-{}.csv".format(
        fname, os.path.basename(fname), x),
        ["raw", "corrected", "input"]))
Yan's avatar
Yan committed
441
442
443
444
445
446
447
448
449
450
    for name in exp_f_name:
        if os.path.isfile(name):
            quest = QtWidgets.QMessageBox.warning(
                parent, "Export DRL data",
                "File {} already exists in the filesystem.\n"
                "Do you want to overwrite it?"
                .format(os.path.basename(name)),
                QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
            if quest == QtWidgets.QMessageBox.No:
                return
451
452
453
454
455
456
    if not os.path.exists(fname):
        os.makedirs(fname)
    for i, table in enumerate([[pnames, pintensities],
                               [names, intensities]]):
        expf = open(exp_f_name[i], 'w')
        expf.write("times, {}\n".format((",".join(table[0]))))
457
        expf.write("timeshift = {}".format(drls['tshift'].value()))
458
        for j in range(len(times)):
Yan's avatar
Yan committed
459
            dataset = list()
460
461
462
            dataset.append(times[j])
            for intensity in table[1]:
                dataset.append(intensity[j])
Yan's avatar
Yan committed
463
464
            expf.write("{}\n".format((",".join(map(str, dataset)))))
        expf.close()
3Yan's avatar
3Yan committed
465
    save_drlconfig(drls, parent, exp_f_name[2])
Yan's avatar
Yan committed
466

Yan's avatar
Yan committed
467

3Yan's avatar
3Yan committed
468
def print_graph(labels, ds, drls):
Yan's avatar
Yan committed
469
    printfig = Figure(figsize=(5, 2), dpi=100)
Yan's avatar
Yan committed
470
471
472
    printplot = printfig.add_subplot(111)
    printcanvas = FigureCanvas(printfig)
    gt.pop_plot(printplot, labels)
3Yan's avatar
3Yan committed
473
    update_drlspectrum(ds, drls, printplot)
Yan's avatar
Yan committed
474
    widget = QtWidgets.QDialog(None, windowTitle='Print preview')
Yan's avatar
Yan committed
475
476
    layout = QtWidgets.QVBoxLayout(widget)
    layout.addWidget(printcanvas)
Yan's avatar
Yan committed
477
    widget.resize(600, 400)
Yan's avatar
Yan committed
478
479
480
481
482
483
484
    widget.show()
    dialog = QtPrintSupport.QPrintDialog()
    if dialog.exec_() == QtWidgets.QDialog.Accepted:
        printcanvas.render(dialog.printer())
    widget.close()


3Yan's avatar
3Yan committed
485
def clip_range(drls):
486
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
487
    if activeObject in (drls['pt'], drls['dt']):
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
        table = activeObject
        if len(table.selectedRanges()) == 0:
            return
        if len(table.selectedRanges()) > 1:
            QtWidgets.QMessageBox.warning(
                None, "Operation not supported",
                "Operation not supported for multiple ranges,\n"
                "cancelling request")
            return
        sr = table.selectedRanges()[0]
        rows = []
        for row in range(sr.topRow(), sr.bottomRow() + 1):
            line = []
            for col in range(sr.leftColumn(), sr.rightColumn() + 1):
                if isinstance(table.cellWidget(row, col),
                            QtWidgets.QComboBox):
                    line.append(
                        table.cellWidget(row, col).currentText())
                elif isinstance(table.item(row, col),
                              QtWidgets.QTableWidgetItem):
                    line.append(table.item(row, col).text())
            rows.append(("\t").join(line))
        QtWidgets.QApplication.clipboard().setText(("\n").join(rows))


3Yan's avatar
3Yan committed
513
def paste_clip(ds, drls, drlspectrum):
514
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
515
    if activeObject in (drls['pt'], drls['dt'])\
516
517
518
519
520
521
522
523
524
525
526
527
528
529
        and activeObject.selectedRanges() != []:
        table = activeObject
        if len(table.selectedRanges()) > 1:
            QtWidgets.QMessageBox.warning(
                None, "Operation not supported",
                "Operation not supported for multiple ranges,\n"
                "cancelling request")
        cliptext = QtWidgets.QApplication.clipboard().text()
        rows = cliptext.split("\n")
        startrow = table.selectedRanges()[0].topRow()
        startcol = table.selectedRanges()[0].leftColumn()
        for i, row in enumerate(rows, start=startrow):
            cols = row.split("\t")
            for j, col in enumerate(cols, start=startcol):
3Yan's avatar
3Yan committed
530
                if table == drls['pt'] and j < 3:
531
                    if i >= (table.rowCount()):
3Yan's avatar
3Yan committed
532
                        add_line(ds, drls, drlspectrum)
533
                    table.item(i, j).setText(col)
534
                if table == drls['dt'] and j != 0 and j % 2 == 0\
535
536
537
538
                     and i < table.rowCount():
                     table.item(i,j).setText(col)


3Yan's avatar
3Yan committed
539
def key_pressed(event, ds, drls, drlspectrum):
540
    if event.key() == QtCore.Qt.Key_Delete:
541
        rows = reversed(list(map(lambda x: x.row(),
3Yan's avatar
3Yan committed
542
543
            drls['pt'].selectionModel().selectedRows())))
        remove_rows(ds, drls, drlspectrum, rows)
Yan's avatar
Yan committed
544
    if event.key() == QtCore.Qt.Key_F5:
3Yan's avatar
3Yan committed
545
546
547
        update_drlspectrum(ds, drls, drlspectrum)
        for row in range(drls['pt'].rowCount()):
            update_profile(drls['pt'], row, ds)
548
549
    if event.key() == QtCore.Qt.Key_C\
        and event.modifiers().__int__() == QtCore.Qt.ControlModifier:
3Yan's avatar
3Yan committed
550
        clip_range(drls)
551
552
    if event.key() == QtCore.Qt.Key_V\
        and event.modifiers().__int__() == QtCore.Qt.ControlModifier:
3Yan's avatar
3Yan committed
553
        paste_clip(ds, drls, drlspectrum)
554

555
556
def select_all_btn_up(ds, drls, drlspectrum, state=None):
    logger.info('''Change of the select-all button detected''')
557
558
559
560
561
562
563
564
565
566
    if state in (0,2):
        for row in range(drls['dt'].rowCount()):
            drls['dt'].cellWidget(row, 0).blockSignals(True)
            drls['dt'].cellWidget(row, 0).setCheckState(state)
            drls['dt'].cellWidget(row, 0).blockSignals(False)
        update_drlspectrum(ds, drls, drlspectrum)
    elif state == 1:
        drls['checkAll'].setCheckState(2)
    else:
        drls['checkAll'].blockSignals(True)
567
568
569
        btns = []
        for row in range(drls['dt'].rowCount()):
            btns.append(drls['dt'].cellWidget(row, 0).checkState())
570
        if all(btns):
571
            drls['checkAll'].setCheckState(2)
572
573
        elif any(btns):
            drls['checkAll'].setCheckState(1)
574
575
        else:
            drls['checkAll'].setCheckState(0)
576
        drls['checkAll'].blockSignals(False)
577

578

Yan's avatar
Yan committed
579
def main_window(parent, ds, filename, cache):
580
    """constructs a dialog window"""
3Yan's avatar
3Yan committed
581
582
    def saveonclose(widget, event, buffer, drls, canvas):
        buffer[0], buffer[1] = drls, canvas
Yan's avatar
Yan committed
583
        QtWidgets.QMainWindow.closeEvent(widget, event)
Yan's avatar
Yan committed
584

3Yan's avatar
3Yan committed
585
586
    # pt = parenttable
    # dt = daughtertable
587
588
    drls = dict(pt=None, dt=None, tshift=None, cors=3, rel=None,
                checkAll=None)
3Yan's avatar
3Yan committed
589

Yan's avatar
Yan committed
590
    window = QtWidgets.QMainWindow(
Yan's avatar
Yan committed
591
        parent, windowTitle='Delayed reactant labelling')
Yan's avatar
Yan committed
592
593
    main_widget = QtWidgets.QWidget(window)
    window.setCentralWidget(main_widget)
Yan's avatar
Yan committed
594

Yan's avatar
Yan committed
595
    window.closeEvent = lambda event: saveonclose(
3Yan's avatar
3Yan committed
596
        window, event, cache, drls, graph_canvas)
Yan's avatar
Yan committed
597

Yan's avatar
Yan committed
598
599
    dial_graph = Figure(figsize=(5, 2), dpi=100, facecolor="None")
    chromplot = dial_graph.add_subplot(111, facecolor=(1, 1, 1, 0.8))
Yan's avatar
Yan committed
600
601
602
    graph_canvas = FigureCanvas(dial_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
Yan's avatar
Yan committed
603
    graphlabels = dict(x=[0], y=[0], line=None, name="",
Yan's avatar
Yan committed
604
605
                       xlabel="time(min)",
                       ylabel="relative intensity")
Yan's avatar
Yan committed
606
607
608
609
    gt.pan_factory(chromplot)
    gt.zoom_factory(chromplot, 1.15)
    gt.pop_plot(chromplot, graphlabels)

3Yan's avatar
3Yan committed
610
611
612
613
    time_title = QtWidgets.QLabel("Time shift (min):")
    drls['tshift'] = QtWidgets.QDoubleSpinBox(
            minimum=-100, maximum=1440, decimals=3)

3Yan's avatar
3Yan committed
614
615
    drls['rel'] = QtWidgets.QCheckBox("Steady state approximation")

616
617
    drls['checkAll'] = QtWidgets.QCheckBox("Select all")

618
619
620
    drl_load = QtWidgets.QPushButton("&Load")
    drl_save = QtWidgets.QPushButton("&Save")
    drl_export = QtWidgets.QPushButton("&Export")
Yan's avatar
Yan committed
621
    drl_print = QtWidgets.QPushButton("&Print")
622
    close = QtWidgets.QPushButton("&Close")
Yan's avatar
Yan committed
623
    close.clicked.connect(window.close)
624

Yan's avatar
Yan committed
625
626
627
    btn_add = QtWidgets.QPushButton("&Add")
    btn_rem = QtWidgets.QPushButton("Remove")

3Yan's avatar
3Yan committed
628
    if cache == [None, None]:
3Yan's avatar
3Yan committed
629
630
631
632
633
        dcolums = ["Name"]
        for i in range(drls['cors']):
            dcolums.append("corrected for ({})".format(i+1))
            dcolums.append("factor ({})".format(i+1))
        drls['dt'] = iontable(dcolums)
3Yan's avatar
3Yan committed
634
635
        drls['pt'] = iontable(["Name", "start (m/z)", "end (m/z)",
                               "profile"])
3Yan's avatar
3Yan committed
636
637
638
639
        #TODO: DIRTY, DIRTY, DIRTY !!! do it nicer when I'll know how i want it
        for n in range(drls['dt'].columnCount()):
            drls['dt'].horizontalHeader().setSectionResizeMode(
                n, QtWidgets.QHeaderView.Interactive)
3Yan's avatar
3Yan committed
640
        add_line(ds, drls, chromplot)
Yan's avatar
Yan committed
641
    else:
3Yan's avatar
3Yan committed
642
643
        drls = cache[0]
        graph_canvas = cache[1]
644

645
    window.keyPressEvent = lambda event: key_pressed(
3Yan's avatar
3Yan committed
646
        event, ds, drls, chromplot)
647

648
    btn_add.clicked.connect(lambda: add_line(
3Yan's avatar
3Yan committed
649
        ds, drls, chromplot))
650
    btn_rem.clicked.connect(lambda: remove_rows(
3Yan's avatar
3Yan committed
651
        ds, drls, chromplot))
Yan's avatar
Yan committed
652
    drl_load.clicked.connect(lambda: load_drltables(
3Yan's avatar
3Yan committed
653
        main_widget, ds, drls, chromplot))
Yan's avatar
Yan committed
654
    drl_save.clicked.connect(lambda: save_drlconfig(
3Yan's avatar
3Yan committed
655
        drls, main_widget))
Yan's avatar
Yan committed
656
    drl_print.clicked.connect(lambda: print_graph(
3Yan's avatar
3Yan committed
657
        graphlabels, ds, drls))
Yan's avatar
Yan committed
658
    drl_export.clicked.connect(lambda: export_drlspectrum(
3Yan's avatar
3Yan committed
659
660
661
662
        main_widget, filename, ds, drls))

    drls['pt'].itemChanged.connect(lambda item: ptable_changed(
        item.row(), item.column(), ds, drls, chromplot))
663

3Yan's avatar
3Yan committed
664
665
    drls['dt'].itemChanged.connect(lambda item: dtable_changed(
        item.row(), item.column(), ds, drls, chromplot))
666

3Yan's avatar
3Yan committed
667
668
    drls['tshift'].valueChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))
669

3Yan's avatar
3Yan committed
670
671
672
    drls['rel'].stateChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))

673
674
675
    drls['checkAll'].stateChanged.connect(lambda state:
        select_all_btn_up(ds, drls, chromplot, state))

676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
    actionBar = QtWidgets.QToolBar(window)
    window.addToolBar(QtCore.Qt.BottomToolBarArea, actionBar)
    actionBar.setAllowedAreas(QtCore.Qt.BottomToolBarArea)
    actionBar.setFloatable(False)
    actionBar.setMovable(False)
    actionBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
    actionBar.addWidget(drl_load)
    actionBar.addWidget(drl_save)
    actionBar.addWidget(drl_print)
    stretchWidget = QtWidgets.QWidget()
    stretchWidget.setSizePolicy(QtWidgets.QSizePolicy.Expanding
                               ,QtWidgets.QSizePolicy.Preferred)
    actionBar.addWidget(stretchWidget)
    actionBar.addWidget(drl_export)
    actionBar.addWidget(close)

    dtdock = QtWidgets.QDockWidget()
    dtdock.setWidget(QtWidgets.QWidget())
    dtdock_layout = QtWidgets.QVBoxLayout(dtdock.widget())
    dtdock.setWindowTitle("Corrected ions table")
    dtdock_layout.addWidget(drls['checkAll'])
    dtdock_layout.addWidget(drls['dt'])

    ptdock = QtWidgets.QDockWidget()
    ptdock.setWidget(QtWidgets.QWidget())
    ptdock_layout = QtWidgets.QVBoxLayout(ptdock.widget())
    ptdock.setWindowTitle("Raw ions table")
    ptdock_layout.addWidget(drls['pt'])
Yan's avatar
Yan committed
704
705
706
707
    pt_butlayout = QtWidgets.QHBoxLayout()
    pt_butlayout.addWidget(btn_add)
    pt_butlayout.addWidget(btn_rem)
    pt_butlayout.addStretch(0)
708
    ptdock_layout.addLayout(pt_butlayout)
Yan's avatar
Yan committed
709

710
711
712
713
714
715
716
    window.addDockWidget(QtCore.Qt.BottomDockWidgetArea, dtdock)
    window.addDockWidget(QtCore.Qt.RightDockWidgetArea, ptdock)

    main_layout = QtWidgets.QVBoxLayout(main_widget)
    graphparams_layout = QtWidgets.QHBoxLayout()
    main_layout.addWidget(graph_canvas, stretch=1)
    main_layout.addLayout(graphparams_layout)
3Yan's avatar
3Yan committed
717
718
719
    graphparams_layout.addWidget(time_title)
    graphparams_layout.addWidget(drls['tshift'])
    graphparams_layout.addStretch(1)
3Yan's avatar
3Yan committed
720
721
    graphparams_layout.addWidget(drls['rel'])
    graphparams_layout.addStretch(1)
722
723


Yan's avatar
Yan committed
724
    window.show()