drltools.py 26 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
    for row in rows:
3Yan's avatar
3Yan committed
241
242
243
        drls['dt'].cellWidget(row,0).setCheckState(0)
        drls['dt'].removeRow(row)
        drls['pt'].removeRow(row)
3Yan's avatar
3Yan committed
244
        cors = []
3Yan's avatar
3Yan committed
245
        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

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


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

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

333
    return table
334
335


3Yan's avatar
3Yan committed
336
def load_drltables(parent, dataset, drls, drlspectrum):
Yan's avatar
Yan committed
337
338
339
340
341
342
343
344
    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
345
        corrections = []
Yan's avatar
Yan committed
346

3Yan's avatar
3Yan committed
347

Yan's avatar
Yan committed
348
349
        with open(filename, 'r') as cfile:
            rawdata = cfile.read().splitlines()
3Yan's avatar
3Yan committed
350
351
        for i in range(len(rawdata[0].split(","))-4):
            corrections.append([])
Yan's avatar
Yan committed
352
353
        for i in range(1, len(rawdata)):
            rawline = rawdata[i].split(",")
354
            # TODO: rawline[4] can be -1 - decide if accept this behaviour
Yan's avatar
Yan committed
355
356
357
358
            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
359
360
361
362
363
364
                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
365
                                   states, *corrections)):
Yan's avatar
Yan committed
366
                k.append(rawline[j])
3Yan's avatar
3Yan committed
367
368
369
        for row in reversed(range(drls['pt'].rowCount())):
            drls['dt'].removeRow(row)
            drls['pt'].removeRow(row)
370
        # first populate only the parent table
Yan's avatar
Yan committed
371
        for i in range(len(names)):
3Yan's avatar
3Yan committed
372
373
374
375
            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])
376
        # and after that the daughter table
Yan's avatar
Yan committed
377
        for i in range(len(names)):
3Yan's avatar
3Yan committed
378
            for j in range(int((len(rawline)-4)/2)):
3Yan's avatar
3Yan committed
379
                drls['dt'].cellWidget(i, 1+j*2).setCurrentIndex(
3Yan's avatar
3Yan committed
380
381
                    int(corrections[0+j*2][i]))
                drls['dt'].item(i, 2+j*2).setText(corrections[1+j*2][i])
3Yan's avatar
3Yan committed
382
            drls['dt'].cellWidget(i, 0).setCheckState(int(states[i]))
Yan's avatar
Yan committed
383
384


3Yan's avatar
3Yan committed
385
def save_drlconfig(drls, parent, exp_f_name=None):
Yan's avatar
Yan committed
386
    """safe DRL table layout so it can be summoned when needed"""
387
388
389
390
    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
391
    if exp_f_name is not '':
3Yan's avatar
3Yan committed
392
393
394
395
396
        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
397
398
        expf = open(exp_f_name, 'w')
        expf.write("#ion_name, start m/z, end m/z, visible,"
3Yan's avatar
3Yan committed
399
                   "{}\n".format(cortext))
3Yan's avatar
3Yan committed
400
        for row in range(drls['pt'].rowCount()):
Yan's avatar
Yan committed
401
402
            vals = []
            for i in range(3):
3Yan's avatar
3Yan committed
403
404
                vals.append(drls['pt'].item(row, i).text())
            vals.append(drls['dt'].cellWidget(row, 0).checkState())
3Yan's avatar
3Yan committed
405
406
407
            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
408
409
410
            expf.write("{}\n".format((",".join(map(str, vals)))))
        expf.close()

Yan's avatar
Yan committed
411

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

    pnames, ptimes, pintensities = get_parentset(ds, drls)
424
425
426
427
428
429
430
431
432
433
434
435
436
437
    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
438
439
440
441
442
443
444
445
446
447
    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
448
449
450
451
452
453
454
    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]))))
        for j in range(len(times)):
Yan's avatar
Yan committed
455
            dataset = list()
456
457
458
            dataset.append(times[j])
            for intensity in table[1]:
                dataset.append(intensity[j])
Yan's avatar
Yan committed
459
460
            expf.write("{}\n".format((",".join(map(str, dataset)))))
        expf.close()
3Yan's avatar
3Yan committed
461
    save_drlconfig(drls, parent, exp_f_name[2])
Yan's avatar
Yan committed
462

Yan's avatar
Yan committed
463

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


3Yan's avatar
3Yan committed
481
def clip_range(drls):
482
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
483
    if activeObject in (drls['pt'], drls['dt']):
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
        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
509
def paste_clip(ds, drls, drlspectrum):
510
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
511
    if activeObject in (drls['pt'], drls['dt'])\
512
513
514
515
516
517
518
519
520
521
522
523
524
525
        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
526
                if table == drls['pt'] and j < 3:
527
                    if i >= (table.rowCount()):
3Yan's avatar
3Yan committed
528
                        add_line(ds, drls, drlspectrum)
529
                    table.item(i, j).setText(col)
3Yan's avatar
3Yan committed
530
                if table == drls['dt'] and j == 2\
531
532
533
534
                     and i < table.rowCount():
                     table.item(i,j).setText(col)


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


Yan's avatar
Yan committed
552
def main_window(parent, ds, filename, cache):
553
    """constructs a dialog window"""
3Yan's avatar
3Yan committed
554
555
    def saveonclose(widget, event, buffer, drls, canvas):
        buffer[0], buffer[1] = drls, canvas
Yan's avatar
Yan committed
556
        QtWidgets.QMainWindow.closeEvent(widget, event)
Yan's avatar
Yan committed
557

3Yan's avatar
3Yan committed
558
559
    # pt = parenttable
    # dt = daughtertable
3Yan's avatar
3Yan committed
560
    drls = dict(pt=None, dt=None, tshift=None, cors=3, rel=None)
3Yan's avatar
3Yan committed
561

Yan's avatar
Yan committed
562
    window = QtWidgets.QMainWindow(
Yan's avatar
Yan committed
563
        parent, windowTitle='Delayed reactant labelling')
Yan's avatar
Yan committed
564
565
    main_widget = QtWidgets.QWidget(window)
    window.setCentralWidget(main_widget)
Yan's avatar
Yan committed
566

Yan's avatar
Yan committed
567
    window.closeEvent = lambda event: saveonclose(
3Yan's avatar
3Yan committed
568
        window, event, cache, drls, graph_canvas)
Yan's avatar
Yan committed
569

Yan's avatar
Yan committed
570
571
    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
572
573
574
    graph_canvas = FigureCanvas(dial_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
Yan's avatar
Yan committed
575
    graphlabels = dict(x=[0], y=[0], line=None, name="",
Yan's avatar
Yan committed
576
577
                       xlabel="time(min)",
                       ylabel="relative intensity")
Yan's avatar
Yan committed
578
579
580
581
    gt.pan_factory(chromplot)
    gt.zoom_factory(chromplot, 1.15)
    gt.pop_plot(chromplot, graphlabels)

3Yan's avatar
3Yan committed
582
583
584
585
    time_title = QtWidgets.QLabel("Time shift (min):")
    drls['tshift'] = QtWidgets.QDoubleSpinBox(
            minimum=-100, maximum=1440, decimals=3)

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

588
589
590
    drl_load = QtWidgets.QPushButton("&Load")
    drl_save = QtWidgets.QPushButton("&Save")
    drl_export = QtWidgets.QPushButton("&Export")
Yan's avatar
Yan committed
591
    drl_print = QtWidgets.QPushButton("&Print")
592
    close = QtWidgets.QPushButton("&Close")
Yan's avatar
Yan committed
593
    close.clicked.connect(window.close)
594

Yan's avatar
Yan committed
595
596
597
    btn_add = QtWidgets.QPushButton("&Add")
    btn_rem = QtWidgets.QPushButton("Remove")

3Yan's avatar
3Yan committed
598
    if cache == [None, None]:
3Yan's avatar
3Yan committed
599
600
601
602
603
        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
604
605
        drls['pt'] = iontable(["Name", "start (m/z)", "end (m/z)",
                               "profile"])
3Yan's avatar
3Yan committed
606
607
608
609
        #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
610
        add_line(ds, drls, chromplot)
Yan's avatar
Yan committed
611
    else:
3Yan's avatar
3Yan committed
612
613
        drls = cache[0]
        graph_canvas = cache[1]
614

615
    window.keyPressEvent = lambda event: key_pressed(
3Yan's avatar
3Yan committed
616
        event, ds, drls, chromplot)
617

618
    btn_add.clicked.connect(lambda: add_line(
3Yan's avatar
3Yan committed
619
        ds, drls, chromplot))
620
    btn_rem.clicked.connect(lambda: remove_rows(
3Yan's avatar
3Yan committed
621
        ds, drls, chromplot))
Yan's avatar
Yan committed
622
    drl_load.clicked.connect(lambda: load_drltables(
3Yan's avatar
3Yan committed
623
        main_widget, ds, drls, chromplot))
Yan's avatar
Yan committed
624
    drl_save.clicked.connect(lambda: save_drlconfig(
3Yan's avatar
3Yan committed
625
        drls, main_widget))
Yan's avatar
Yan committed
626
    drl_print.clicked.connect(lambda: print_graph(
3Yan's avatar
3Yan committed
627
        graphlabels, ds, drls))
Yan's avatar
Yan committed
628
    drl_export.clicked.connect(lambda: export_drlspectrum(
3Yan's avatar
3Yan committed
629
630
631
632
        main_widget, filename, ds, drls))

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

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

3Yan's avatar
3Yan committed
637
638
    drls['tshift'].valueChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))
639

3Yan's avatar
3Yan committed
640
641
642
    drls['rel'].stateChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))

Yan's avatar
Yan committed
643
    main_layout = QtWidgets.QVBoxLayout(main_widget)
Yan's avatar
Yan committed
644
    sub_layout = QtWidgets.QHBoxLayout()
3Yan's avatar
3Yan committed
645
646
    graph_layout = QtWidgets.QVBoxLayout()
    graphparams_layout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
647
    tablelayout = QtWidgets.QVBoxLayout()
Yan's avatar
Yan committed
648
    pt_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
649
    main_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
650

Yan's avatar
Yan committed
651
652
653
654
    pt_butlayout.addWidget(btn_add)
    pt_butlayout.addWidget(btn_rem)
    pt_butlayout.addStretch(0)

655
656
    main_layout.addLayout(sub_layout)
    main_layout.addWidget(HBar())
Yan's avatar
Yan committed
657
    main_layout.addLayout(main_butlayout)
658

Yan's avatar
Yan committed
659
660
    main_butlayout.addWidget(drl_load)
    main_butlayout.addWidget(drl_save)
Yan's avatar
Yan committed
661
    main_butlayout.addWidget(drl_print)
Yan's avatar
Yan committed
662
663
    main_butlayout.addStretch(1)
    main_butlayout.addWidget(drl_export)
664
    main_butlayout.addWidget(close)
Yan's avatar
Yan committed
665

3Yan's avatar
3Yan committed
666
    sub_layout.addLayout(graph_layout, stretch=1)
Yan's avatar
Yan committed
667
    sub_layout.addLayout(tablelayout)
3Yan's avatar
3Yan committed
668
669
670
671
672
    graph_layout.addWidget(graph_canvas, stretch=1)
    graph_layout.addLayout(graphparams_layout)
    graphparams_layout.addWidget(time_title)
    graphparams_layout.addWidget(drls['tshift'])
    graphparams_layout.addStretch(1)
3Yan's avatar
3Yan committed
673
674
    graphparams_layout.addWidget(drls['rel'])
    graphparams_layout.addStretch(1)
675

Yan's avatar
Yan committed
676
    tablelayout.addWidget(QtWidgets.QLabel("Raw ions table:"))
3Yan's avatar
3Yan committed
677
    tablelayout.addWidget(drls['pt'])
Yan's avatar
Yan committed
678
679
    tablelayout.addLayout(pt_butlayout)
    tablelayout.addWidget(QtWidgets.QLabel("Corrected ions table:"))
3Yan's avatar
3Yan committed
680
    tablelayout.addWidget(drls['dt'])
681

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