drltools.py 11.7 KB
Newer Older
Yan's avatar
Yan committed
1
2
3
4
5
6
7
#!/usr/bin/env python3

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.widgets import SpanSelector
from PyQt5 import QtCore
from PyQt5 import QtWidgets
8
from PyQt5 import QtGui
Yan's avatar
Yan committed
9
10
11
import sys
import matplotlib
import numpy as np
Yan's avatar
Yan committed
12
13
import prasopes.datatools as dt
import prasopes.graphtools as gt
Yan's avatar
Yan committed
14
15
16
matplotlib.use("Qt5Agg")

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


Yan's avatar
Yan committed
24
def update_profile(start, end, spectrum, dataset):
25
26
    """spectrum updating procedure"""
    #Dont do anything to graph when the spectrum is not populated
Yan's avatar
Yan committed
27
    if (type(dataset['masses'])) == type(None):
28
29
        return

Yan's avatar
Yan committed
30
31
    masses = dataset['masses']
    massargs = dt.argsubselect(masses, start, end)
32
    yshape = np.mean(dataset['matrix'], axis=0)
Yan's avatar
Yan committed
33
34
    spectrum.clear()
    dots_x = masses
35
    dots_y = yshape
Yan's avatar
Yan committed
36
37
    full_x = masses[massargs]
    full_y = yshape[massargs]
38
39
    spectrum.plot(dots_x, dots_y, ':', color='gray')
    spectrum.plot(full_x, full_y, 'r')
Yan's avatar
Yan committed
40
41
42
43
    xex = (masses[massargs[-1]]-masses[massargs[0]])*0.25
    spectrum.set_xlim(masses[massargs[0]]-xex,
                      masses[massargs[-1]]+xex)
    ymax = max(yshape[massargs])
44
45
46
    spectrum.set_ylim(ymax*-0.1, ymax*1.2)
    spectrum.figure.canvas.draw()

Yan's avatar
Yan committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def get_daughterset(ptable, dtable, ds):
    names = []
    times = ds['chrom_dat'][0,:]
    intensities = []
    #TODO: resolve intensities trouble
    for row in range(dtable.rowCount()):
        if dtable.cellWidget(row, 1).checkState() == 2:
            startm = dt.floatize(ptable.cellWidget(row,1).text())
            endm = dt.floatize(ptable.cellWidget(row,2).text())
            massargs = dt.argsubselect(ds['masses'], startm, endm)
            intensity = (np.sum(
                ds['matrix'].T[massargs].T, axis=1)) / ds['chrom_dat'][1]
            cor = dtable.cellWidget(row, 2).currentIndex()-1
            if cor != -1:
                factor = dt.floatize(dtable.cellWidget(row,3).text())
                startm = dt.floatize(ptable.cellWidget(cor,1).text())
                endm = dt.floatize(ptable.cellWidget(cor,2).text())
                massargs = dt.argsubselect(ds['masses'], startm, endm)
                corrections = ((np.sum(
                    ds['matrix'].T[massargs].T, axis=1))\
                    / ds['chrom_dat'][1]) * factor
                itensity = intensities - corrections
            intensities.append(intensity)
            names.append(dtable.item(row,1).text())
    return names, times, intensities


def update_drlspectrum(ptable, dtable, ds, drlspectrum):
    #Dont do when the dataset is not populated
    if (type(ds['masses'])) == type(None):
        return

    names, times, intensities = get_daughterset(ptable, dtable, ds)
    for i in range(len(drlspectrum.lines)):
        drlspectrum.lines[0].remove()
    for i in intensities:
        drlspectrum.plot(times, i)
    if len(names) != 0:
        drlspectrum.set_ylim(top=np.amax(intensities)*1.1,
                bottom=np.amax(intensities)*-0.01)
    drlspectrum.figure.canvas.draw()
88
89

def getTableItemList(ptable):
90
    ion_list = []
91
    for i in range(ptable.rowCount()):
Yan's avatar
Yan committed
92
93
        if type(ptable.cellWidget(i, 0)) is not type(None):
            name = ptable.cellWidget(i, 0).text()
94
95
96
        else:
            name = ""
        text = "{} ({}-{})".format(
Yan's avatar
Yan committed
97
98
            name, (ptable.cellWidget(i, 1).text()),
            (ptable.cellWidget(i, 2).text()))
99
        ion_list.append(text)
100
101
102
103
    return ion_list


def update_drl(start_mz, end_mz, spectrum, dataset, pname, dname,
Yan's avatar
Yan committed
104
        ptable, dtable, drlspectrum):
Yan's avatar
Yan committed
105
106
    """cares that masses are in order and valid, then refresh tables"""
    for line in start_mz, end_mz:
Yan's avatar
Yan committed
107
108
109
        status = QtGui.QDoubleValidator().validate(line.text(), 0)[0]
        if status != QtGui.QValidator.Acceptable:
            line.setText("0")
Yan's avatar
Yan committed
110

Yan's avatar
Yan committed
111
112
    start = dt.floatize(start_mz.text())
    end = dt.floatize(end_mz.text())
113
114
115
116
117
118

    if start > end:
        start, end = end, start
        start_mz.setText(str(start))
        end_mz.setText(str(end))

Yan's avatar
Yan committed
119
    dname.setText("{} ({:.2f}-{:.2f})".format(
Yan's avatar
Yan committed
120
        pname.text(), start, end))
Yan's avatar
Yan committed
121
122
123
124
125
126
127
128

    for i in (range(dtable.rowCount())):
        index = dtable.cellWidget(i,2).currentIndex()
        dtable.cellWidget(i,2).clear()
        dtable.cellWidget(i,2).addItem("")
        dtable.cellWidget(i,2).addItems(getTableItemList(ptable))
        dtable.cellWidget(i,2).setCurrentIndex(index)

Yan's avatar
Yan committed
129
130
    update_profile(start, end, spectrum, dataset)
    update_drlspectrum(ptable, dtable, dataset, drlspectrum)
131
132
133


def remove_rows(ptable, dtable):
Yan's avatar
Yan committed
134
    # TODO: maybe nicer selection in future, but this works for now
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    rows = reversed(list(set(
        map(lambda index: index.row(), ptable.selectedIndexes()))))
    for row in rows:
        dtable.removeRow(row)
        ptable.removeRow(row)
        for i in range(dtable.rowCount()):
            index = dtable.cellWidget(i,2).currentIndex()
            dtable.cellWidget(i,2).clear()
            dtable.cellWidget(i,2).addItem("")
            dtable.cellWidget(i,2).addItems(getTableItemList(ptable))
            if index == row+1:
                dtable.cellWidget(i,2).setCurrentIndex(0)
            elif index > row+1:
                dtable.cellWidget(i,2).setCurrentIndex(index-1)


def setDoubleCell(table,row,column):
    """populate table cell with float-validated posititve text"""
    table.setCellWidget(row,column,QtWidgets.QLineEdit())
    validator = QtGui.QDoubleValidator()
    validator.setBottom(0)
    table.cellWidget(row,column).setValidator(validator)
    table.cellWidget(row,column).setFrame(False)
    table.cellWidget(row,column).setText("0")
    table.horizontalHeader().setSectionResizeMode(
            column, QtWidgets.QHeaderView.ResizeToContents)
    return table.cellWidget(row,column)


Yan's avatar
Yan committed
164
165
def add_line(parent_widget, mass_selector, spectrum, ds, parenttable,
        daughtertable, drlspectrum):
166
167
168
169
170
171
172
173
174
    """add parent ion to the table"""
    ion_graph = Figure(figsize=(3, 1.5), dpi=100, facecolor="None")
    ionspect = ion_graph.add_subplot(111, facecolor=(1, 1, 1, 0.8),
            position=(-0.01,-0.01,1.02,1.02))
    graph_canvas = FigureCanvas(ion_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)

    newrow = parenttable.rowCount()
175

176
177
178
179
180
181
182
    parenttable.setRowCount(newrow + 1)
    pname = QtWidgets.QLineEdit()
    pname.setFrame(False)
    parenttable.setCellWidget(newrow, 0, pname)
    start_mz = setDoubleCell(parenttable, newrow, 1)
    end_mz = setDoubleCell(parenttable, newrow, 2)
    parenttable.setCellWidget(newrow, 3, graph_canvas)
183

184
    daughtertable.setRowCount(newrow + 1)
Yan's avatar
Yan committed
185
186

    #TODO: decide which box will be used in the final version
Yan's avatar
Yan committed
187
    checkbox = QtWidgets.QCheckBox()
188
189
190
191
    daughtertable.setItem(newrow,0, QtWidgets.QTableWidgetItem())
    daughtertable.item(newrow,0).setFlags(daughtertable.item(newrow,0).flags()
            & ~QtCore.Qt.ItemIsSelectable)
    daughtertable.setCellWidget(newrow, 0, QtWidgets.QCheckBox())
192

193
194
    dname = QtWidgets.QTableWidgetItem()
    dname.setFlags(dname.flags() & ~QtCore.Qt.ItemIsEditable)
Yan's avatar
Yan committed
195
    dname.setTextAlignment(QtCore.Qt.AlignRight)
196
    daughtertable.setItem(newrow, 1, dname)
Yan's avatar
Yan committed
197
    daughtertable.setCellWidget(newrow, 1, checkbox)
198

199
200
201
202
203
    corto = QtWidgets.QComboBox()
    corto.setFrame(False)
    corto.addItem("")
    corto.addItems(getTableItemList(parenttable))
    daughtertable.setCellWidget(newrow, 2, corto)
204

205
    corfact = setDoubleCell(daughtertable, newrow, 3)
206

207
    update_drl(start_mz, end_mz, ionspect, ds, pname, dname, 
Yan's avatar
Yan committed
208
            parenttable, daughtertable, drlspectrum)
209

Yan's avatar
Yan committed
210
211
    pname.editingFinished.connect(lambda: update_drl(
        start_mz, end_mz, ionspect, ds, pname, dname, parenttable,
Yan's avatar
Yan committed
212
        daughtertable, drlspectrum))
213
214
    start_mz.editingFinished.connect(lambda: update_drl(
        start_mz, end_mz, ionspect, ds, pname, dname, parenttable,
Yan's avatar
Yan committed
215
        daughtertable, drlspectrum))
216
217
    end_mz.editingFinished.connect(lambda: update_drl(
        start_mz, end_mz, ionspect, ds, pname, dname, parenttable,
Yan's avatar
Yan committed
218
219
220
        daughtertable, drlspectrum))
    checkbox.stateChanged.connect(lambda: update_drlspectrum(
        parenttable, daughtertable, ds, drlspectrum))
221
222


223
224
225
def iontable(labels, exp_col):
    """creates a table for ions"""
    table = QtWidgets.QTableWidget(columnCount=len(labels))
226
    table.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
227
228
                        QtWidgets.QSizePolicy.Expanding)
    table.setHorizontalHeaderLabels(labels)
229
230
231
232
233
234
235
    table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

    for n in range(table.columnCount()):
        table.horizontalHeader().setSectionResizeMode(
            n, QtWidgets.QHeaderView.Stretch)
    table.setMinimumSize(600,0)

236
    return table
237
238


Yan's avatar
Yan committed
239
def dialog(parent, ds, filename, mass_selector, spectrum, cache):
240
    """constructs a dialog window"""
241

Yan's avatar
Yan committed
242
243
    def savecache(cache,pt,dt,canvas):
        cache[0], cache[1], cache[2] = pt, dt, canvas
Yan's avatar
Yan committed
244

Yan's avatar
Yan committed
245
246
    dial_widget = QtWidgets.QDialog(
        parent, windowTitle='Delayed reactant labelling')
Yan's avatar
Yan committed
247

Yan's avatar
Yan committed
248
249
    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
250
251
252
    graph_canvas = FigureCanvas(dial_graph)
    graph_canvas.setStyleSheet("background-color:transparent;")
    graph_canvas.setAutoFillBackground(False)
Yan's avatar
Yan committed
253
254
255
256
257
258
    graphlabels = dict(x=[0], y=[0], line=None, name="",
                xlabel="time(min)", ylabel="relative intensity")
    gt.pan_factory(chromplot)
    gt.zoom_factory(chromplot, 1.15)
    gt.pop_plot(chromplot, graphlabels)

Yan's avatar
Yan committed
259

260
261
262
263
    drl_load = QtWidgets.QPushButton("&Load")
    drl_save = QtWidgets.QPushButton("&Save")
    drl_export = QtWidgets.QPushButton("&Export")
    close = QtWidgets.QPushButton("&Close")
264
265
    close.clicked.connect(dial_widget.close)

Yan's avatar
Yan committed
266
267
268
    btn_add = QtWidgets.QPushButton("&Add")
    btn_rem = QtWidgets.QPushButton("Remove")

269
270
    # pt = parenttable
    # dt = daughtertable
Yan's avatar
Yan committed
271
    if cache == [None, None, None]:
Yan's avatar
Yan committed
272
273
        dt = iontable(["","Name", "corrected to", "factor"], 2)
        dt.horizontalHeader().setSectionResizeMode(
274
            0, QtWidgets.QHeaderView.ResizeToContents)
Yan's avatar
Yan committed
275
        dt.horizontalHeader().setSectionResizeMode(
276
277
            3, QtWidgets.QHeaderView.Fixed)

Yan's avatar
Yan committed
278
279
        pt = iontable(["Name", "start (m/z)", "end (m/z)", "profile"], 2)
        for i in range(5):
Yan's avatar
Yan committed
280
281
            add_line(dial_widget, mass_selector, spectrum, ds, pt, dt,
                    chromplot)
Yan's avatar
Yan committed
282
283
284
    else:
        pt = cache[0]
        dt = cache[1]
Yan's avatar
Yan committed
285
        graph_canvas = cache[2]
286
287

    btn_add.clicked.connect(lambda: add_line(
Yan's avatar
Yan committed
288
        dial_widget, mass_selector, spectrum, ds, pt, dt, chromplot))
289
    btn_rem.clicked.connect(lambda: remove_rows(pt,dt))
Yan's avatar
Yan committed
290
    dial_widget.finished.connect(lambda: savecache(cache, pt, dt, graph_canvas))
291

Yan's avatar
Yan committed
292
293
294
    main_layout = QtWidgets.QVBoxLayout(dial_widget)
    sub_layout = QtWidgets.QHBoxLayout()
    tablelayout = QtWidgets.QVBoxLayout()
Yan's avatar
Yan committed
295
    pt_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
296
    main_butlayout = QtWidgets.QHBoxLayout()
Yan's avatar
Yan committed
297

Yan's avatar
Yan committed
298
299
300
301
    pt_butlayout.addWidget(btn_add)
    pt_butlayout.addWidget(btn_rem)
    pt_butlayout.addStretch(0)

302
303
    main_layout.addLayout(sub_layout)
    main_layout.addWidget(HBar())
Yan's avatar
Yan committed
304
    main_layout.addLayout(main_butlayout)
305

Yan's avatar
Yan committed
306
307
308
309
    main_butlayout.addWidget(drl_load)
    main_butlayout.addWidget(drl_save)
    main_butlayout.addStretch(1)
    main_butlayout.addWidget(drl_export)
310
    main_butlayout.addWidget(close)
Yan's avatar
Yan committed
311

312
    sub_layout.addWidget(graph_canvas, stretch=1)
Yan's avatar
Yan committed
313
    sub_layout.addLayout(tablelayout)
314

Yan's avatar
Yan committed
315
316
317
318
319
    tablelayout.addWidget(QtWidgets.QLabel("Raw ions table:"))
    tablelayout.addWidget(pt)
    tablelayout.addLayout(pt_butlayout)
    tablelayout.addWidget(QtWidgets.QLabel("Corrected ions table:"))
    tablelayout.addWidget(dt)
320

Yan's avatar
Yan committed
321
    dial_widget.show()