drltools.py 25 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
import logging
Yan's avatar
Yan committed
15
16
matplotlib.use("Qt5Agg")

Yan's avatar
Yan committed
17

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


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


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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):
Yan's avatar
Yan committed
44
    """parent table profile spectrum updating procedure"""
Yan's avatar
Yan committed
45
    # Dont do anything to graph when the spectrum is not populated
Yan's avatar
Yan committed
46
    logger.info("updating parent table row {} profile".format(row))
Yan's avatar
Yan committed
47
    if isinstance(dataset['masses'], type(None)):
48
49
        return

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

Yan's avatar
Yan committed
65

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


3Yan's avatar
3Yan committed
76
def get_daughterset(ds, drls):
Yan's avatar
Yan committed
77
    # TODO: write a less resources demanding function - probably "per-line"
Yan's avatar
Yan committed
78
    names = []
3Yan's avatar
3Yan committed
79
    times = ds['chrom_dat'][0, :] - drls['tshift'].value()
Yan's avatar
Yan committed
80
    intensities = []
3Yan's avatar
3Yan committed
81
82
83
    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
84
85
86
87
88
89
90
91
92
93
94
            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
95
            intensities.append(intensity)
3Yan's avatar
3Yan committed
96
            names.append("{} - ({})".format(
3Yan's avatar
3Yan committed
97
                drls['dt'].item(row, 0).text(),
3Yan's avatar
3Yan committed
98
                cortext))
Yan's avatar
Yan committed
99
100
101
    return names, times, intensities


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


3Yan's avatar
3Yan committed
122
def update_drlspectrum(ds, drls, drlspectrum):
Yan's avatar
Yan committed
123
124
    """Generic DRL spectrum updating procedure"""
    logger.info("updating DRL spectrum")
125
    # Dont do anything when the dataset is not populated
Yan's avatar
Yan committed
126
    if isinstance(ds['masses'], type(None)):
Yan's avatar
Yan committed
127
        return
128
    colors = np.array([[0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255],
129
130
131
                       [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
132

133
    # TODO: write a less resources demanding function
3Yan's avatar
3Yan committed
134
    names, times, intensities = get_daughterset(ds, drls)
Yan's avatar
Yan committed
135
136
    for i in range(len(drlspectrum.lines)):
        drlspectrum.lines[0].remove()
137

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

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

157
    if len(names) != 0:
3Yan's avatar
3Yan committed
158
159
160
161
        gmax = np.amax(intensities)
        if drls['rel'].checkState() == 2:
            gmax = 1
        drlspectrum.set_ylim(top=gmax*1.1, bottom=gmax*-0.01)
162
        drlspectrum.legend(loc=2)
163
164
    drlspectrum.figure.canvas.draw()

Yan's avatar
Yan committed
165

Yan's avatar
Yan committed
166
def gettableitemlist(ptable):
167
    ion_list = []
Yan's avatar
Yan committed
168
169
170
    for row in range(ptable.rowCount()):
        text = []
        for i in range(3):
171
172
            if not isinstance(ptable.item(row, i), type(None)):
                frg = ptable.item(row, i).text()
Yan's avatar
Yan committed
173
174
175
176
177
            else:
                frg = ""
            text.append(frg)
        line = "{} ({}-{})".format(*text)
        ion_list.append(line)
178
179
180
    return ion_list


3Yan's avatar
3Yan committed
181
def update_corrfors(drls):
182
    """update corrections selection layout of the daughter table"""
3Yan's avatar
3Yan committed
183
184
    ionlist = gettableitemlist(drls['pt'])
    for row in range(drls['dt'].rowCount()):
3Yan's avatar
3Yan committed
185
186
187
188
189
190
191
192
193
        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
194

195

3Yan's avatar
3Yan committed
196
def ptable_changed(row, column, ds, drls, drlspectrum):
197
    """routine called by change of the ptable spectra"""
3Yan's avatar
3Yan committed
198
199
    update_corrfors(drls)
    drls['dt'].item(row, 0).setText(gettableitemlist(drls['pt'])[row])
200
    if column in (1, 2):
3Yan's avatar
3Yan committed
201
        update_profile(drls['pt'], row, ds)
202

203

3Yan's avatar
3Yan committed
204
def dtable_changed(row, column, ds, drls, drlspectrum):
205
    """routine called by change of the dtable spectra"""
3Yan's avatar
3Yan committed
206
    #TODO: multiple corrections not incorporated!!!
3Yan's avatar
3Yan committed
207
    if drls['dt'].cellWidget(row, 0).checkState() == 2:
208
209
        if (column == 0)\
           or (column == 2
3Yan's avatar
3Yan committed
210
211
               and drls['dt'].cellWidget(row, 1).currentIndex() != 0):
                update_drlspectrum(ds, drls, drlspectrum)
212

Yan's avatar
Yan committed
213

3Yan's avatar
3Yan committed
214
def corr_changed(row, ds, drls, drlspectrum):
215
    """routine called by change of correction for ion"""
3Yan's avatar
3Yan committed
216
    #TODO: multiple corrections not incorporated!!!
3Yan's avatar
3Yan committed
217
218
219
    if (drls['dt'].cellWidget(row, 0).checkState() == 2
       and floatize(drls['dt'], row, 2) != 0):
        update_drlspectrum(ds, drls, drlspectrum)
220
221


3Yan's avatar
3Yan committed
222
def remove_rows(ds, drls, drlspectrum, rows=None):
Yan's avatar
Yan committed
223
    # TODO: maybe nicer selection in future, but this works for now
224
225
    if rows == None:
        rows = reversed(list(set(
3Yan's avatar
3Yan committed
226
            map(lambda x: x.row(), drls['pt'].selectedIndexes()))))
227
    for row in rows:
3Yan's avatar
3Yan committed
228
229
230
231
        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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
            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))
248
249


3Yan's avatar
3Yan committed
250
def add_line(ds, drls, drlspectrum):
251
    """add parent ion to the table"""
3Yan's avatar
3Yan committed
252
    newrow = drls['pt'].rowCount()
253

3Yan's avatar
3Yan committed
254
255
    drls['pt'].blockSignals(True)
    drls['dt'].blockSignals(True)
256

3Yan's avatar
3Yan committed
257
    drls['pt'].setRowCount(newrow + 1)
258
    for i in range(3):
3Yan's avatar
3Yan committed
259
        drls['pt'].setItem(newrow, i, QtWidgets.QTableWidgetItem())
260
        if newrow is not 0:
3Yan's avatar
3Yan committed
261
262
            drls['pt'].item(newrow, i).setText(str(floatize(
                    drls['pt'], newrow-1, i)+1))
263

264
    ion_graph = Figure(figsize=(3, 1.5), dpi=100, facecolor="None")
265
266
    ion_graph.add_subplot(111, facecolor=(1, 1, 1, 0.8),
                          position=(-0.01, -0.01, 1.02, 1.02))
267
268
269
    graph_canvas = FigureCanvas(ion_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
3Yan's avatar
3Yan committed
270
    drls['pt'].setCellWidget(newrow, 3, graph_canvas)
271

3Yan's avatar
3Yan committed
272
    drls['dt'].setRowCount(newrow + 1)
Yan's avatar
Yan committed
273
    checkbox = QtWidgets.QCheckBox()
3Yan's avatar
3Yan committed
274
    checkbox.setFocusProxy(drls['dt'])
275
276
    dname = QtWidgets.QTableWidgetItem()
    dname.setFlags(dname.flags() & ~QtCore.Qt.ItemIsEditable)
Yan's avatar
Yan committed
277
    dname.setTextAlignment(QtCore.Qt.AlignRight)
3Yan's avatar
3Yan committed
278
279
    drls['dt'].setItem(newrow, 0, dname)
    drls['dt'].setCellWidget(newrow, 0, checkbox)
3Yan's avatar
3Yan committed
280
281
282

    for i in range(drls['cors']):
        corfor = QtWidgets.QComboBox()
Yan's avatar
Yan committed
283
284
        #TODO: verify on windows that this fixed the weird selection issues
        #corfor.setFocusProxy(drls['dt'])
3Yan's avatar
3Yan committed
285
286
287
288
289
        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))
290

3Yan's avatar
3Yan committed
291
292
    drls['pt'].blockSignals(False)
    drls['dt'].blockSignals(False)
293

3Yan's avatar
3Yan committed
294
    ptable_changed(newrow, 1, ds, drls, drlspectrum)
295

Yan's avatar
Yan committed
296
    checkbox.stateChanged.connect(lambda: update_drlspectrum(
3Yan's avatar
3Yan committed
297
        ds, drls, drlspectrum))
298
299


Yan's avatar
Yan committed
300
def iontable(labels):
301
302
    """creates a table for ions"""
    table = QtWidgets.QTableWidget(columnCount=len(labels))
303
    table.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
304
305
                        QtWidgets.QSizePolicy.Expanding)
    table.setHorizontalHeaderLabels(labels)
306
307
308
309
310
    table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

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

313
    return table
314
315


3Yan's avatar
3Yan committed
316
def load_drltables(parent, dataset, drls, drlspectrum):
Yan's avatar
Yan committed
317
318
319
320
321
322
323
324
    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
325
        corrections = []
Yan's avatar
Yan committed
326

3Yan's avatar
3Yan committed
327

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


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

Yan's avatar
Yan committed
390

3Yan's avatar
3Yan committed
391
def export_drlspectrum(parent, fn, ds, drls):
Yan's avatar
Yan committed
392
393
    if fn[0] is None:
        QtWidgets.QMessageBox.warning(
394
            None, "Export DRL dataset",
Yan's avatar
Yan committed
395
396
            "Nothing to export, cancelling request")
        return
3Yan's avatar
3Yan committed
397
398
399
400
401
402
    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)
403
404
405
406
407
408
409
410
411
412
413
414
415
416
    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
417
418
419
420
421
422
423
424
425
426
    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
427
428
429
430
431
432
433
    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
434
            dataset = list()
435
436
437
            dataset.append(times[j])
            for intensity in table[1]:
                dataset.append(intensity[j])
Yan's avatar
Yan committed
438
439
            expf.write("{}\n".format((",".join(map(str, dataset)))))
        expf.close()
3Yan's avatar
3Yan committed
440
    save_drlconfig(drls, parent, exp_f_name[2])
Yan's avatar
Yan committed
441

Yan's avatar
Yan committed
442

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


3Yan's avatar
3Yan committed
460
def clip_range(drls):
461
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
462
    if activeObject in (drls['pt'], drls['dt']):
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
        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
488
def paste_clip(ds, drls, drlspectrum):
489
    activeObject = QtWidgets.QApplication.focusWidget()
3Yan's avatar
3Yan committed
490
    if activeObject in (drls['pt'], drls['dt'])\
491
492
493
494
495
496
497
498
499
500
501
502
503
504
        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
505
                if table == drls['pt'] and j < 3:
506
                    if i >= (table.rowCount()):
3Yan's avatar
3Yan committed
507
                        add_line(ds, drls, drlspectrum)
508
                    table.item(i, j).setText(col)
3Yan's avatar
3Yan committed
509
                if table == drls['dt'] and j == 2\
510
511
512
513
                     and i < table.rowCount():
                     table.item(i,j).setText(col)


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


Yan's avatar
Yan committed
531
def main_window(parent, ds, filename, cache):
532
    """constructs a dialog window"""
3Yan's avatar
3Yan committed
533
534
    def saveonclose(widget, event, buffer, drls, canvas):
        buffer[0], buffer[1] = drls, canvas
Yan's avatar
Yan committed
535
        QtWidgets.QMainWindow.closeEvent(widget, event)
Yan's avatar
Yan committed
536

3Yan's avatar
3Yan committed
537
538
    # pt = parenttable
    # dt = daughtertable
3Yan's avatar
3Yan committed
539
    drls = dict(pt=None, dt=None, tshift=None, cors=3, rel=None)
3Yan's avatar
3Yan committed
540

Yan's avatar
Yan committed
541
    window = QtWidgets.QMainWindow(
Yan's avatar
Yan committed
542
        parent, windowTitle='Delayed reactant labelling')
Yan's avatar
Yan committed
543
544
    main_widget = QtWidgets.QWidget(window)
    window.setCentralWidget(main_widget)
Yan's avatar
Yan committed
545

Yan's avatar
Yan committed
546
    window.closeEvent = lambda event: saveonclose(
3Yan's avatar
3Yan committed
547
        window, event, cache, drls, graph_canvas)
Yan's avatar
Yan committed
548

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

3Yan's avatar
3Yan committed
561
562
563
564
    time_title = QtWidgets.QLabel("Time shift (min):")
    drls['tshift'] = QtWidgets.QDoubleSpinBox(
            minimum=-100, maximum=1440, decimals=3)

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

567
568
569
    drl_load = QtWidgets.QPushButton("&Load")
    drl_save = QtWidgets.QPushButton("&Save")
    drl_export = QtWidgets.QPushButton("&Export")
Yan's avatar
Yan committed
570
    drl_print = QtWidgets.QPushButton("&Print")
571
    close = QtWidgets.QPushButton("&Close")
Yan's avatar
Yan committed
572
    close.clicked.connect(window.close)
573

Yan's avatar
Yan committed
574
575
576
    btn_add = QtWidgets.QPushButton("&Add")
    btn_rem = QtWidgets.QPushButton("Remove")

3Yan's avatar
3Yan committed
577
    if cache == [None, None]:
3Yan's avatar
3Yan committed
578
579
580
581
582
        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
583
584
        drls['pt'] = iontable(["Name", "start (m/z)", "end (m/z)",
                               "profile"])
3Yan's avatar
3Yan committed
585
586
587
588
        #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
589
        add_line(ds, drls, chromplot)
Yan's avatar
Yan committed
590
    else:
3Yan's avatar
3Yan committed
591
592
        drls = cache[0]
        graph_canvas = cache[1]
593

594
    window.keyPressEvent = lambda event: key_pressed(
3Yan's avatar
3Yan committed
595
        event, ds, drls, chromplot)
596

597
    btn_add.clicked.connect(lambda: add_line(
3Yan's avatar
3Yan committed
598
        ds, drls, chromplot))
599
    btn_rem.clicked.connect(lambda: remove_rows(
3Yan's avatar
3Yan committed
600
        ds, drls, chromplot))
Yan's avatar
Yan committed
601
    drl_load.clicked.connect(lambda: load_drltables(
3Yan's avatar
3Yan committed
602
        main_widget, ds, drls, chromplot))
Yan's avatar
Yan committed
603
    drl_save.clicked.connect(lambda: save_drlconfig(
3Yan's avatar
3Yan committed
604
        drls, main_widget))
Yan's avatar
Yan committed
605
    drl_print.clicked.connect(lambda: print_graph(
3Yan's avatar
3Yan committed
606
        graphlabels, ds, drls))
Yan's avatar
Yan committed
607
    drl_export.clicked.connect(lambda: export_drlspectrum(
3Yan's avatar
3Yan committed
608
609
610
611
        main_widget, filename, ds, drls))

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

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

3Yan's avatar
3Yan committed
616
617
    drls['tshift'].valueChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))
618

3Yan's avatar
3Yan committed
619
620
621
    drls['rel'].stateChanged.connect(lambda: update_drlspectrum(
        ds, drls, chromplot))

Yan's avatar
Yan committed
622
    main_layout = QtWidgets.QVBoxLayout(main_widget)
Yan's avatar
Yan committed
623
    sub_layout = QtWidgets.QHBoxLayout()
3Yan's avatar
3Yan committed
624
625
    graph_layout = QtWidgets.QVBoxLayout()
    graphparams_layout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
626
    tablelayout = QtWidgets.QVBoxLayout()
Yan's avatar
Yan committed
627
    pt_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
628
    main_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
629

Yan's avatar
Yan committed
630
631
632
633
    pt_butlayout.addWidget(btn_add)
    pt_butlayout.addWidget(btn_rem)
    pt_butlayout.addStretch(0)

634
635
    main_layout.addLayout(sub_layout)
    main_layout.addWidget(HBar())
Yan's avatar
Yan committed
636
    main_layout.addLayout(main_butlayout)
637

Yan's avatar
Yan committed
638
639
    main_butlayout.addWidget(drl_load)
    main_butlayout.addWidget(drl_save)
Yan's avatar
Yan committed
640
    main_butlayout.addWidget(drl_print)
Yan's avatar
Yan committed
641
642
    main_butlayout.addStretch(1)
    main_butlayout.addWidget(drl_export)
643
    main_butlayout.addWidget(close)
Yan's avatar
Yan committed
644

3Yan's avatar
3Yan committed
645
    sub_layout.addLayout(graph_layout, stretch=1)
Yan's avatar
Yan committed
646
    sub_layout.addLayout(tablelayout)
3Yan's avatar
3Yan committed
647
648
649
650
651
    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
652
653
    graphparams_layout.addWidget(drls['rel'])
    graphparams_layout.addStretch(1)
654

Yan's avatar
Yan committed
655
    tablelayout.addWidget(QtWidgets.QLabel("Raw ions table:"))
3Yan's avatar
3Yan committed
656
    tablelayout.addWidget(drls['pt'])
Yan's avatar
Yan committed
657
658
    tablelayout.addLayout(pt_butlayout)
    tablelayout.addWidget(QtWidgets.QLabel("Corrected ions table:"))
3Yan's avatar
3Yan committed
659
    tablelayout.addWidget(drls['dt'])
660

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