drltools.py 24.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
Yan's avatar
Yan committed
8
9
import matplotlib
import numpy as np
Yan's avatar
Yan committed
10
11
import prasopes.datatools as dt
import prasopes.graphtools as gt
Yan's avatar
Yan committed
12
import prasopes.filetools as ft
Yan's avatar
Yan committed
13
import os.path
Yan's avatar
Yan committed
14
15
matplotlib.use("Qt5Agg")

Yan's avatar
Yan committed
16

Yan's avatar
Yan committed
17
class HBar(QtWidgets.QFrame):
18
    """horizontal bar class"""
Yan's avatar
Yan committed
19
20
21
22
23
24
    def __init__(self):
        super(HBar, self).__init__()
        self._main = QtWidgets.QWidget()
        self.setFrameShape(QtWidgets.QFrame.HLine)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def floatize(table, row, column):
    """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()
    validator.setBottom(0)
    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):
40
    """spectrum updating procedure"""
Yan's avatar
Yan committed
41
42
    # Dont do anything to graph when the spectrum is not populated
    if isinstance(dataset['masses'], type(None)):
43
44
        return

45
    spectrum = pt.cellWidget(row, 3).figure.get_axes()[0]
Yan's avatar
Yan committed
46
    masses = dataset['masses']
47
48
    massargs = dt.argsubselect(
        masses, floatize(pt, row, 1), floatize(pt, row, 2))
49
    yshape = np.mean(dataset['matrix'], axis=0)
Yan's avatar
Yan committed
50
    spectrum.clear()
51
52
53
    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
54
55
56
    spectrum.set_xlim(masses[massargs[0]]-xex,
                      masses[massargs[-1]]+xex)
    ymax = max(yshape[massargs])
57
58
59
    spectrum.set_ylim(ymax*-0.1, ymax*1.2)
    spectrum.figure.canvas.draw()

Yan's avatar
Yan committed
60

3Yan's avatar
3Yan committed
61
62
63
def get_intensity(row, ds, drls):
    startm = floatize(drls['pt'], row, 1)
    endm = floatize(drls['pt'], row, 2)
Yan's avatar
Yan committed
64
    massargs = dt.argsubselect(ds['masses'], startm, endm)
65
    nonzero_tic = list(map(lambda x: max(x, 1), ds['chrom_dat'][1]))
Yan's avatar
Yan committed
66
    intensity = (np.sum(
67
        ds['matrix'].T[massargs].T, axis=1)) / nonzero_tic
Yan's avatar
Yan committed
68
69
70
    return intensity


3Yan's avatar
3Yan committed
71
def get_daughterset(ds, drls):
Yan's avatar
Yan committed
72
    # TODO: write a less resources demanding function - probably "per-line"
Yan's avatar
Yan committed
73
    names = []
3Yan's avatar
3Yan committed
74
    times = ds['chrom_dat'][0, :] - drls['tshift'].value()
Yan's avatar
Yan committed
75
    intensities = []
3Yan's avatar
3Yan committed
76
77
78
    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
79
80
81
82
83
84
85
86
87
88
89
            corlist = []
            for i in range(drls['cors']):
                cor = drls['dt'].cellWidget(row, 1+i*2).currentIndex() - 1
                if cor not in (-2, -1):
                    factor = floatize(drls['dt'], row, 2+i*2)
                    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
90
            intensities.append(intensity)
3Yan's avatar
3Yan committed
91
            names.append("{} - ({})".format(
3Yan's avatar
3Yan committed
92
                drls['dt'].item(row, 0).text(),
3Yan's avatar
3Yan committed
93
                cortext))
Yan's avatar
Yan committed
94
95
96
    return names, times, intensities


3Yan's avatar
3Yan committed
97
def get_parentset(ds, drls):
98
99
100
101
102
    names = []
    times = ds['chrom_dat'][0, :]
    intensities = []
    # TODO: resolve intensities trouble
    rowlist = []
3Yan's avatar
3Yan committed
103
104
    for row in range(drls['dt'].rowCount()):
        if drls['dt'].cellWidget(row, 0).checkState() == 2:
105
            rowlist.append(row)
3Yan's avatar
3Yan committed
106
107
            if drls['dt'].cellWidget(row, 1).currentIndex() > 0\
                    and floatize(drls['dt'], row, 2) != 0:
108
                rowlist.append(
3Yan's avatar
3Yan committed
109
                    drls['dt'].cellWidget(row, 1).currentIndex()-1)
110
    for row in set(rowlist):
3Yan's avatar
3Yan committed
111
            intensity = get_intensity(row, ds, drls)
112
            intensities.append(intensity)
3Yan's avatar
3Yan committed
113
            names.append(drls['dt'].item(row, 0).text())
114
115
116
    return names, times, intensities


3Yan's avatar
3Yan committed
117
def update_drlspectrum(ds, drls, drlspectrum):
118
    # Dont do anything when the dataset is not populated
Yan's avatar
Yan committed
119
    if isinstance(ds['masses'], type(None)):
Yan's avatar
Yan committed
120
        return
121
    colors = np.array([[0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255],
122
123
124
                       [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
125

126
    # TODO: write a less resources demanding function
3Yan's avatar
3Yan committed
127
    names, times, intensities = get_daughterset(ds, drls)
Yan's avatar
Yan committed
128
129
    for i in range(len(drlspectrum.lines)):
        drlspectrum.lines[0].remove()
130

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

133
    i = 0
3Yan's avatar
3Yan committed
134
135
136
137
    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(
138
                QtGui.QColor(*colors[row % len(colors)], alpha=50)))
3Yan's avatar
3Yan committed
139
140
            drls['dt'].blockSignals(False)
            label = " {}".format(drls['pt'].item(row, 0).text())
3Yan's avatar
3Yan committed
141
142
143
144
            intensity = intensities[i]
            if drls['rel'].checkState() == 2:
                intensity = intensity / np.sum(intensities, 0)
            drlspectrum.plot(times, intensity, label=label,
145
146
                             color=(colors[row % len(colors)] / 255))
            i += 1
147
        else:
3Yan's avatar
3Yan committed
148
            drls['dt'].item(row, 0).setBackground(QtGui.QBrush())
149

150
    if len(names) != 0:
3Yan's avatar
3Yan committed
151
152
153
154
        gmax = np.amax(intensities)
        if drls['rel'].checkState() == 2:
            gmax = 1
        drlspectrum.set_ylim(top=gmax*1.1, bottom=gmax*-0.01)
155
        drlspectrum.legend(loc=2)
156
157
    drlspectrum.figure.canvas.draw()

Yan's avatar
Yan committed
158

Yan's avatar
Yan committed
159
def gettableitemlist(ptable):
160
    ion_list = []
Yan's avatar
Yan committed
161
162
163
    for row in range(ptable.rowCount()):
        text = []
        for i in range(3):
164
165
            if not isinstance(ptable.item(row, i), type(None)):
                frg = ptable.item(row, i).text()
Yan's avatar
Yan committed
166
167
168
169
170
            else:
                frg = ""
            text.append(frg)
        line = "{} ({}-{})".format(*text)
        ion_list.append(line)
171
172
173
    return ion_list


3Yan's avatar
3Yan committed
174
def update_corrfors(drls):
175
    """update corrections selection layout of the daughter table"""
3Yan's avatar
3Yan committed
176
177
    ionlist = gettableitemlist(drls['pt'])
    for row in range(drls['dt'].rowCount()):
3Yan's avatar
3Yan committed
178
179
180
181
182
183
184
185
186
        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
187

188

3Yan's avatar
3Yan committed
189
def ptable_changed(row, column, ds, drls, drlspectrum):
190
    """routine called by change of the ptable spectra"""
3Yan's avatar
3Yan committed
191
192
    update_corrfors(drls)
    drls['dt'].item(row, 0).setText(gettableitemlist(drls['pt'])[row])
193
    if column in (1, 2):
3Yan's avatar
3Yan committed
194
        update_profile(drls['pt'], row, ds)
195

196

3Yan's avatar
3Yan committed
197
def dtable_changed(row, column, ds, drls, drlspectrum):
198
    """routine called by change of the dtable spectra"""
3Yan's avatar
3Yan committed
199
    #TODO: multiple corrections not incorporated!!!
3Yan's avatar
3Yan committed
200
    if drls['dt'].cellWidget(row, 0).checkState() == 2:
201
202
        if (column == 0)\
           or (column == 2
3Yan's avatar
3Yan committed
203
204
               and drls['dt'].cellWidget(row, 1).currentIndex() != 0):
                update_drlspectrum(ds, drls, drlspectrum)
205

Yan's avatar
Yan committed
206

3Yan's avatar
3Yan committed
207
def corr_changed(row, ds, drls, drlspectrum):
208
    """routine called by change of correction for ion"""
3Yan's avatar
3Yan committed
209
    #TODO: multiple corrections not incorporated!!!
3Yan's avatar
3Yan committed
210
211
212
    if (drls['dt'].cellWidget(row, 0).checkState() == 2
       and floatize(drls['dt'], row, 2) != 0):
        update_drlspectrum(ds, drls, drlspectrum)
213
214


3Yan's avatar
3Yan committed
215
def remove_rows(ds, drls, drlspectrum, rows=None):
Yan's avatar
Yan committed
216
    # TODO: maybe nicer selection in future, but this works for now
217
218
    if rows == None:
        rows = reversed(list(set(
3Yan's avatar
3Yan committed
219
            map(lambda x: x.row(), drls['pt'].selectedIndexes()))))
220
    for row in rows:
3Yan's avatar
3Yan committed
221
222
223
224
        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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
            for cornum in range(drls['cors']):
                corfor = drls['dt'].cellWidget(i, 1+cornum*2)
                corfor.disconnect()
                index = corfor.currentIndex()
                corfor.clear()
                corfor.addItem("")
                corfor.addItems(gettableitemlist(drls['pt']))
                if index == row+1:
                    corfor.setCurrentIndex(0)
                    corr_changed(i, ds, drls, drlspectrum)
                elif index > row+1:
                    corfor.setCurrentIndex(index-1)
                else:
                    corfor.setCurrentIndex(index)
                    corfor.currentIndexChanged.connect(lambda:
                        corr_changed(i, ds, drls, drlspectrum))
241
242


3Yan's avatar
3Yan committed
243
def add_line(ds, drls, drlspectrum):
244
    """add parent ion to the table"""
3Yan's avatar
3Yan committed
245
    newrow = drls['pt'].rowCount()
246

3Yan's avatar
3Yan committed
247
248
    drls['pt'].blockSignals(True)
    drls['dt'].blockSignals(True)
249

3Yan's avatar
3Yan committed
250
    drls['pt'].setRowCount(newrow + 1)
251
    for i in range(3):
3Yan's avatar
3Yan committed
252
        drls['pt'].setItem(newrow, i, QtWidgets.QTableWidgetItem())
253
        if newrow is not 0:
3Yan's avatar
3Yan committed
254
255
            drls['pt'].item(newrow, i).setText(str(floatize(
                    drls['pt'], newrow-1, i)+1))
256

257
    ion_graph = Figure(figsize=(3, 1.5), dpi=100, facecolor="None")
258
259
    ion_graph.add_subplot(111, facecolor=(1, 1, 1, 0.8),
                          position=(-0.01, -0.01, 1.02, 1.02))
260
261
262
    graph_canvas = FigureCanvas(ion_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
3Yan's avatar
3Yan committed
263
    drls['pt'].setCellWidget(newrow, 3, graph_canvas)
264

3Yan's avatar
3Yan committed
265
    drls['dt'].setRowCount(newrow + 1)
Yan's avatar
Yan committed
266
    checkbox = QtWidgets.QCheckBox()
3Yan's avatar
3Yan committed
267
    checkbox.setFocusProxy(drls['dt'])
268
269
    dname = QtWidgets.QTableWidgetItem()
    dname.setFlags(dname.flags() & ~QtCore.Qt.ItemIsEditable)
Yan's avatar
Yan committed
270
    dname.setTextAlignment(QtCore.Qt.AlignRight)
3Yan's avatar
3Yan committed
271
272
    drls['dt'].setItem(newrow, 0, dname)
    drls['dt'].setCellWidget(newrow, 0, checkbox)
3Yan's avatar
3Yan committed
273
274
275

    for i in range(drls['cors']):
        corfor = QtWidgets.QComboBox()
Yan's avatar
Yan committed
276
277
        #TODO: verify on windows that this fixed the weird selection issues
        #corfor.setFocusProxy(drls['dt'])
3Yan's avatar
3Yan committed
278
279
280
281
282
        corfor.setFrame(False)
        drls['dt'].setCellWidget(newrow, 1+i*2, corfor)
        drls['dt'].setItem(newrow, 2+i*2, QtWidgets.QTableWidgetItem())
        corfor.currentIndexChanged.connect(lambda: corr_changed(
            newrow, ds, drls, drlspectrum))
283

3Yan's avatar
3Yan committed
284
285
    drls['pt'].blockSignals(False)
    drls['dt'].blockSignals(False)
286

3Yan's avatar
3Yan committed
287
    ptable_changed(newrow, 1, ds, drls, drlspectrum)
288

Yan's avatar
Yan committed
289
    checkbox.stateChanged.connect(lambda: update_drlspectrum(
3Yan's avatar
3Yan committed
290
        ds, drls, drlspectrum))
291
292


Yan's avatar
Yan committed
293
def iontable(labels):
294
295
    """creates a table for ions"""
    table = QtWidgets.QTableWidget(columnCount=len(labels))
296
    table.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
297
298
                        QtWidgets.QSizePolicy.Expanding)
    table.setHorizontalHeaderLabels(labels)
299
300
301
302
303
    table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

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

306
    return table
307
308


3Yan's avatar
3Yan committed
309
def load_drltables(parent, dataset, drls, drlspectrum):
Yan's avatar
Yan committed
310
311
312
313
314
315
316
317
    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
318
        corrections = []
Yan's avatar
Yan committed
319

3Yan's avatar
3Yan committed
320

Yan's avatar
Yan committed
321
322
        with open(filename, 'r') as cfile:
            rawdata = cfile.read().splitlines()
3Yan's avatar
3Yan committed
323
324
        for i in range(len(rawdata[0].split(","))-4):
            corrections.append([])
Yan's avatar
Yan committed
325
326
        for i in range(1, len(rawdata)):
            rawline = rawdata[i].split(",")
327
            # TODO: rawline[4] can be -1 - decide if accept this behaviour
3Yan's avatar
3Yan committed
328
329
            # TODO: not checking if multiple corrections are sane
            if len(rawline) < 6 or int(rawline[3]) not in range(3) \
330
                    or int(rawline[4]) not in range(-1,len(rawdata)):
Yan's avatar
Yan committed
331
332
333
334
335
336
                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
337
                                   states, *corrections)):
Yan's avatar
Yan committed
338
                k.append(rawline[j])
3Yan's avatar
3Yan committed
339
340
341
        for row in reversed(range(drls['pt'].rowCount())):
            drls['dt'].removeRow(row)
            drls['pt'].removeRow(row)
342
        # first populate only the parent table
Yan's avatar
Yan committed
343
        for i in range(len(names)):
3Yan's avatar
3Yan committed
344
345
346
347
            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])
348
        # and after that the daughter table
Yan's avatar
Yan committed
349
        for i in range(len(names)):
3Yan's avatar
3Yan committed
350
            for j in range(int((len(rawline)-4)/2)):
3Yan's avatar
3Yan committed
351
                drls['dt'].cellWidget(i, 1+j*2).setCurrentIndex(
3Yan's avatar
3Yan committed
352
353
                    int(corrections[0+j*2][i]))
                drls['dt'].item(i, 2+j*2).setText(corrections[1+j*2][i])
3Yan's avatar
3Yan committed
354
            drls['dt'].cellWidget(i, 0).setCheckState(int(states[i]))
Yan's avatar
Yan committed
355
356


3Yan's avatar
3Yan committed
357
def save_drlconfig(drls, parent, exp_f_name=None):
Yan's avatar
Yan committed
358
    """safe DRL table layout so it can be summoned when needed"""
359
360
361
362
    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
363
    if exp_f_name is not '':
3Yan's avatar
3Yan committed
364
365
366
367
368
        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
369
370
        expf = open(exp_f_name, 'w')
        expf.write("#ion_name, start m/z, end m/z, visible,"
3Yan's avatar
3Yan committed
371
                   "{}\n".format(cortext))
3Yan's avatar
3Yan committed
372
        for row in range(drls['pt'].rowCount()):
Yan's avatar
Yan committed
373
374
            vals = []
            for i in range(3):
3Yan's avatar
3Yan committed
375
376
                vals.append(drls['pt'].item(row, i).text())
            vals.append(drls['dt'].cellWidget(row, 0).checkState())
3Yan's avatar
3Yan committed
377
378
379
            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
380
381
382
            expf.write("{}\n".format((",".join(map(str, vals)))))
        expf.close()

Yan's avatar
Yan committed
383

3Yan's avatar
3Yan committed
384
def export_drlspectrum(parent, fn, ds, drls):
Yan's avatar
Yan committed
385
386
    if fn[0] is None:
        QtWidgets.QMessageBox.warning(
387
            None, "Export DRL dataset",
Yan's avatar
Yan committed
388
389
            "Nothing to export, cancelling request")
        return
3Yan's avatar
3Yan committed
390
391
392
393
394
395
    names, times, intensities = get_daughterset(ds, drls)
    subset = np.where(times > 0)[0]
    times = times[subset]
    intensities = intensities[subset]

    pnames, ptimes, pintensities = get_parentset(ds, drls)
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    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
410
411
412
413
414
415
416
417
418
419
    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
420
421
422
423
424
425
426
    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
427
            dataset = list()
428
429
430
            dataset.append(times[j])
            for intensity in table[1]:
                dataset.append(intensity[j])
Yan's avatar
Yan committed
431
432
            expf.write("{}\n".format((",".join(map(str, dataset)))))
        expf.close()
3Yan's avatar
3Yan committed
433
    save_drlconfig(drls, parent, exp_f_name[2])
Yan's avatar
Yan committed
434

Yan's avatar
Yan committed
435

3Yan's avatar
3Yan committed
436
def print_graph(labels, ds, drls):
Yan's avatar
Yan committed
437
    printfig = Figure(figsize=(5, 2), dpi=100)
Yan's avatar
Yan committed
438
439
440
    printplot = printfig.add_subplot(111)
    printcanvas = FigureCanvas(printfig)
    gt.pop_plot(printplot, labels)
3Yan's avatar
3Yan committed
441
    update_drlspectrum(ds, drls, printplot)
Yan's avatar
Yan committed
442
    widget = QtWidgets.QDialog(None, windowTitle='Print preview')
Yan's avatar
Yan committed
443
444
    layout = QtWidgets.QVBoxLayout(widget)
    layout.addWidget(printcanvas)
Yan's avatar
Yan committed
445
    widget.resize(600, 400)
Yan's avatar
Yan committed
446
447
448
449
450
451
452
    widget.show()
    dialog = QtPrintSupport.QPrintDialog()
    if dialog.exec_() == QtWidgets.QDialog.Accepted:
        printcanvas.render(dialog.printer())
    widget.close()


3Yan's avatar
3Yan committed
453
def clip_range(drls):
454
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
455
    if activeObject in (drls['pt'], drls['dt']):
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
        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
481
def paste_clip(ds, drls, drlspectrum):
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
        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
498
                if table == drls['pt'] and j < 3:
499
                    if i >= (table.rowCount()):
3Yan's avatar
3Yan committed
500
                        add_line(ds, drls, drlspectrum)
501
                    table.item(i, j).setText(col)
3Yan's avatar
3Yan committed
502
                if table == drls['dt'] and j == 2\
503
504
505
506
                     and i < table.rowCount():
                     table.item(i,j).setText(col)


3Yan's avatar
3Yan committed
507
def key_pressed(event, ds, drls, drlspectrum):
508
    if event.key() == QtCore.Qt.Key_Delete:
509
        rows = reversed(list(map(lambda x: x.row(),
3Yan's avatar
3Yan committed
510
511
            drls['pt'].selectionModel().selectedRows())))
        remove_rows(ds, drls, drlspectrum, rows)
Yan's avatar
Yan committed
512
    if event.key() == QtCore.Qt.Key_F5:
3Yan's avatar
3Yan committed
513
514
515
        update_drlspectrum(ds, drls, drlspectrum)
        for row in range(drls['pt'].rowCount()):
            update_profile(drls['pt'], row, ds)
516
517
    if event.key() == QtCore.Qt.Key_C\
        and event.modifiers().__int__() == QtCore.Qt.ControlModifier:
3Yan's avatar
3Yan committed
518
        clip_range(drls)
519
520
    if event.key() == QtCore.Qt.Key_V\
        and event.modifiers().__int__() == QtCore.Qt.ControlModifier:
3Yan's avatar
3Yan committed
521
        paste_clip(ds, drls, drlspectrum)
522
523


Yan's avatar
Yan committed
524
def main_window(parent, ds, filename, cache):
525
    """constructs a dialog window"""
3Yan's avatar
3Yan committed
526
527
    def saveonclose(widget, event, buffer, drls, canvas):
        buffer[0], buffer[1] = drls, canvas
Yan's avatar
Yan committed
528
        QtWidgets.QMainWindow.closeEvent(widget, event)
Yan's avatar
Yan committed
529

3Yan's avatar
3Yan committed
530
531
    # pt = parenttable
    # dt = daughtertable
3Yan's avatar
3Yan committed
532
    drls = dict(pt=None, dt=None, tshift=None, cors=3, rel=None)
3Yan's avatar
3Yan committed
533

Yan's avatar
Yan committed
534
    window = QtWidgets.QMainWindow(
Yan's avatar
Yan committed
535
        parent, windowTitle='Delayed reactant labelling')
Yan's avatar
Yan committed
536
537
    main_widget = QtWidgets.QWidget(window)
    window.setCentralWidget(main_widget)
Yan's avatar
Yan committed
538

Yan's avatar
Yan committed
539
    window.closeEvent = lambda event: saveonclose(
3Yan's avatar
3Yan committed
540
        window, event, cache, drls, graph_canvas)
Yan's avatar
Yan committed
541

Yan's avatar
Yan committed
542
543
    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
544
545
546
    graph_canvas = FigureCanvas(dial_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
Yan's avatar
Yan committed
547
    graphlabels = dict(x=[0], y=[0], line=None, name="",
Yan's avatar
Yan committed
548
549
                       xlabel="time(min)",
                       ylabel="relative intensity")
Yan's avatar
Yan committed
550
551
552
553
    gt.pan_factory(chromplot)
    gt.zoom_factory(chromplot, 1.15)
    gt.pop_plot(chromplot, graphlabels)

3Yan's avatar
3Yan committed
554
555
556
557
    time_title = QtWidgets.QLabel("Time shift (min):")
    drls['tshift'] = QtWidgets.QDoubleSpinBox(
            minimum=-100, maximum=1440, decimals=3)

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

560
561
562
    drl_load = QtWidgets.QPushButton("&Load")
    drl_save = QtWidgets.QPushButton("&Save")
    drl_export = QtWidgets.QPushButton("&Export")
Yan's avatar
Yan committed
563
    drl_print = QtWidgets.QPushButton("&Print")
564
    close = QtWidgets.QPushButton("&Close")
Yan's avatar
Yan committed
565
    close.clicked.connect(window.close)
566

Yan's avatar
Yan committed
567
568
569
    btn_add = QtWidgets.QPushButton("&Add")
    btn_rem = QtWidgets.QPushButton("Remove")

3Yan's avatar
3Yan committed
570
    if cache == [None, None]:
3Yan's avatar
3Yan committed
571
572
573
574
575
        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
576
577
        drls['pt'] = iontable(["Name", "start (m/z)", "end (m/z)",
                               "profile"])
3Yan's avatar
3Yan committed
578
579
580
581
        #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
582
        add_line(ds, drls, chromplot)
Yan's avatar
Yan committed
583
    else:
3Yan's avatar
3Yan committed
584
585
        drls = cache[0]
        graph_canvas = cache[1]
586

587
    window.keyPressEvent = lambda event: key_pressed(
3Yan's avatar
3Yan committed
588
        event, ds, drls, chromplot)
589

590
    btn_add.clicked.connect(lambda: add_line(
3Yan's avatar
3Yan committed
591
        ds, drls, chromplot))
592
    btn_rem.clicked.connect(lambda: remove_rows(
3Yan's avatar
3Yan committed
593
        ds, drls, chromplot))
Yan's avatar
Yan committed
594
    drl_load.clicked.connect(lambda: load_drltables(
3Yan's avatar
3Yan committed
595
        main_widget, ds, drls, chromplot))
Yan's avatar
Yan committed
596
    drl_save.clicked.connect(lambda: save_drlconfig(
3Yan's avatar
3Yan committed
597
        drls, main_widget))
Yan's avatar
Yan committed
598
    drl_print.clicked.connect(lambda: print_graph(
3Yan's avatar
3Yan committed
599
        graphlabels, ds, drls))
Yan's avatar
Yan committed
600
    drl_export.clicked.connect(lambda: export_drlspectrum(
3Yan's avatar
3Yan committed
601
602
603
604
        main_widget, filename, ds, drls))

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

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

3Yan's avatar
3Yan committed
609
610
    drls['tshift'].valueChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))
611

3Yan's avatar
3Yan committed
612
613
614
    drls['rel'].stateChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))

Yan's avatar
Yan committed
615
    main_layout = QtWidgets.QVBoxLayout(main_widget)
Yan's avatar
Yan committed
616
    sub_layout = QtWidgets.QHBoxLayout()
3Yan's avatar
3Yan committed
617
618
    graph_layout = QtWidgets.QVBoxLayout()
    graphparams_layout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
619
    tablelayout = QtWidgets.QVBoxLayout()
Yan's avatar
Yan committed
620
    pt_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
621
    main_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
622

Yan's avatar
Yan committed
623
624
625
626
    pt_butlayout.addWidget(btn_add)
    pt_butlayout.addWidget(btn_rem)
    pt_butlayout.addStretch(0)

627
628
    main_layout.addLayout(sub_layout)
    main_layout.addWidget(HBar())
Yan's avatar
Yan committed
629
    main_layout.addLayout(main_butlayout)
630

Yan's avatar
Yan committed
631
632
    main_butlayout.addWidget(drl_load)
    main_butlayout.addWidget(drl_save)
Yan's avatar
Yan committed
633
    main_butlayout.addWidget(drl_print)
Yan's avatar
Yan committed
634
635
    main_butlayout.addStretch(1)
    main_butlayout.addWidget(drl_export)
636
    main_butlayout.addWidget(close)
Yan's avatar
Yan committed
637

3Yan's avatar
3Yan committed
638
    sub_layout.addLayout(graph_layout, stretch=1)
Yan's avatar
Yan committed
639
    sub_layout.addLayout(tablelayout)
3Yan's avatar
3Yan committed
640
641
642
643
644
    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
645
646
    graphparams_layout.addWidget(drls['rel'])
    graphparams_layout.addStretch(1)
647

Yan's avatar
Yan committed
648
    tablelayout.addWidget(QtWidgets.QLabel("Raw ions table:"))
3Yan's avatar
3Yan committed
649
    tablelayout.addWidget(drls['pt'])
Yan's avatar
Yan committed
650
651
    tablelayout.addLayout(pt_butlayout)
    tablelayout.addWidget(QtWidgets.QLabel("Corrected ions table:"))
3Yan's avatar
3Yan committed
652
    tablelayout.addWidget(drls['dt'])
653

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