__main__.py 15 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 matplotlib.widgets import SpanSelector
Yan's avatar
Yan committed
6
from matplotlib.gridspec import GridSpec
Yan's avatar
Yan committed
7
8
from PyQt5 import QtCore
from PyQt5 import QtWidgets
Yan's avatar
Yan committed
9
from PyQt5 import QtGui
10
from PyQt5 import QtPrintSupport
Yan's avatar
Yan committed
11
from rawprasslib import load_raw
Yan's avatar
Yan committed
12
from rawprasslib import rawprasslib
Yan's avatar
Yan committed
13
from prasopes.predictmz import predict as getmzpattern
Yan's avatar
Yan committed
14
import numpy as np
Yan's avatar
Yan committed
15
import prasopes.config as cf
16
import prasopes.datatools as dt
Yan's avatar
Yan committed
17
import prasopes.drltools as drl
Yan's avatar
Yan committed
18
import prasopes.filetools as ft
Yan's avatar
Yan committed
19
20
import prasopes.graphtools as gt
import prasopes.imagetools as imgt
Yan's avatar
Yan committed
21
import prasopes.zcetools as zce
Yan's avatar
Yan committed
22
import prasopes.tangoicons
Yan's avatar
Yan committed
23
24
25
import sys
import matplotlib
import logging
3Yan's avatar
3Yan committed
26
import os.path
Yan's avatar
Yan committed
27
28
29
matplotlib.use("Qt5Agg")


30
31
32
33
class update_signal(QtCore.QObject):
    signal = QtCore.pyqtSignal()


34
def load_file(parent, chrom_plot, spc, d_set, ms_s, fn, chrom_s,
35
              update, settings, filename=None):
Yan's avatar
Yan committed
36
    """populates dataset and plots it"""
3Yan's avatar
3Yan committed
37
38
39
    directory=settings.value("open_folder")
    if fn[0] is not None:
        directory=fn[0]
Yan's avatar
Yan committed
40
41
    if filename == None:
        filename = QtWidgets.QFileDialog.getOpenFileName(
3Yan's avatar
3Yan committed
42
43
44
            caption="Open spectrum",
            directory=directory,
            filter="Finnigan RAW files (*.raw, *.RAW)")[0]
3Yan's avatar
3Yan committed
45
    if filename is not '' and os.path.isfile(filename):
Yan's avatar
Yan committed
46
        try:
3Yan's avatar
3Yan committed
47
            d_set.clear()
Yan's avatar
Yan committed
48
            chrom_s['timesarg'].clear()
3Yan's avatar
3Yan committed
49
50
            [d_set.append(dict(chrom_dat=i[0], masses=i[1], matrix=i[2])) for i
                    in load_raw(filename, settings.value("tmp_location"))]
Yan's avatar
Yan committed
51
52
53
54
55
56
57
58
        except rawprasslib.ParsingException as pex:
            QtWidgets.QMessageBox.critical(
                parent, "Opening of the file has failed!",
                "File is incompatible with the rawprasslib, "
                "canceling request!\n\n"
                "Error message:\n{}".format(pex.args[0]))
            return

Yan's avatar
Yan committed
59
        gt.populate(chrom_plot, spc, d_set, ms_s, chrom_s)
3Yan's avatar
3Yan committed
60
        fn[0] = filename
61
62
        parent.setWindowTitle("Prasopes - {}".format(
            os.path.basename(filename)))
63
        update.signal.emit()
3Yan's avatar
3Yan committed
64

Yan's avatar
Yan committed
65

Yan's avatar
Yan committed
66
67
def print_graph(data_set, mass_spec, chrom_spec, spect, fn):
    def printimage(printdevice, img):
Yan's avatar
Yan committed
68
        printer.setResolution(600)
Yan's avatar
Yan committed
69
        painter = QtGui.QPainter(printdevice)
Yan's avatar
Yan committed
70
        painter.drawImage(0,0,img)
Yan's avatar
Yan committed
71
72
        painter.end()
    #TODO: substitute the QPrintPreviewDialog with QPrintPreviewWidget
73
74
    printPreview = QtPrintSupport.QPrintPreviewDialog()
    printer = printPreview.printer()
Yan's avatar
Yan committed
75
76
    printer.setPageSize(printer.A5)
    printer.setDuplex(printer.DuplexNone)
Yan's avatar
Yan committed
77
    image = imgt.paint_image(mass_spec, spect, printer)
Yan's avatar
Yan committed
78
    printPreview.paintRequested.connect(lambda:
Yan's avatar
Yan committed
79
                                        printimage(printer, image))
80
81
82
    printPreview.exec()


Yan's avatar
Yan committed
83
84
def update_spectrum(chromatogram, spect, ds, ms, fn, chrom, config):
    if fn[0] is not None:
Yan's avatar
Yan committed
85
        slims = [spect.get_xlim(), spect.get_ylim()]
3Yan's avatar
3Yan committed
86
87
88
        ds.clear()
        [ds.append(dict(chrom_dat=i[0], masses=i[1], matrix=i[2])) for i
                in load_raw(fn[0], config.value("tmp_location"))]
Yan's avatar
Yan committed
89
90
91
        gt.populate(chromatogram, spect, ds, ms, chrom)
        spect.set_xlim(slims[0])
        spect.set_ylim(slims[1])
Yan's avatar
Yan committed
92
        gt.ann_spec(spect, ms)
Yan's avatar
Yan committed
93
94
95
        spect.get_figure().canvas.draw()


96
def dropped(event, parent, chromatogram, spectrum, ds, ms, filename,
3Yan's avatar
3Yan committed
97
            chrom, update, config):
98
    dropurl = event.mimeData().urls()[0].toLocalFile()
99
    load_file(parent, chromatogram, spectrum, ds, ms, filename,
3Yan's avatar
3Yan committed
100
              chrom, update, config, filename=dropurl)
Yan's avatar
Yan committed
101
102
103
104


def drag_entered(event):
    if event.mimeData().hasUrls() and event.mimeData().urls()[0]\
105
        .toLocalFile().lower().endswith('.raw'):
Yan's avatar
Yan committed
106
107
108
        event.accept()


Yan's avatar
Yan committed
109
110
111
112
113
114
115
116
117
118
119
120
121
def predictmz(form, chromatogram, spect, ds, ms, chrom):
    text = form.text()
    if text == "":
        ms["predict"] = None
        return
    slims = [spect.get_xlim(), spect.get_ylim()]
    ms["predict"] = getmzpattern(text)
    gt.populate(chromatogram, spect, ds, ms, chrom)
    spect.set_xlim(slims[0])
    spect.set_ylim(slims[1])
    spect.get_figure().canvas.draw()


3Yan's avatar
3Yan committed
122
123
124
125
126
127
def oddeven_changed(chromatogram, spectrum, ds, ms, filename, chrom, config,
        oddevenact):
    config.setValue("view/oddeven", oddevenact.isChecked())
    update_spectrum(chromatogram, spectrum, ds, ms, filename, chrom, config)


Yan's avatar
Yan committed
128
129
130
def key_pressed(event, chrom, spect, ds, ms_ds, fn, chrom_ds, config):
    if event.key() == QtCore.Qt.Key_F5:
        update_spectrum(chrom, spect, ds, ms_ds, fn, chrom_ds, config)
131
132
133
134
135
136
    if event.key() == QtCore.Qt.Key_C:
        if event.modifiers().__int__() == QtCore.Qt.ControlModifier:
            imgt.clip_spect_img(ms_ds, spect)
        if event.modifiers().__int__() == QtCore.Qt.ControlModifier+\
                                          QtCore.Qt.ShiftModifier:
            dt.clip_spectstr(spect, chrom_ds, fn)
Yan's avatar
Yan committed
137
138
    if event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right):
        gt.shift_times(event, spect, chrom, ds, ms_ds, chrom_ds)
Yan's avatar
Yan committed
139
140


Yan's avatar
Yan committed
141
142
143
144
145
def about(parent):
    """constructs window with "about" info"""
    QtWidgets.QMessageBox.information(
            parent, "About Prasopes",
            "Prasopes Finnigan raw file viewer\n\n"
Yan's avatar
Yan committed
146
            "Version: 0.0.11 (alpha)")
Yan's avatar
Yan committed
147
148


Yan's avatar
Yan committed
149
def main():
3Yan's avatar
3Yan committed
150
151
152
    ds = []
    ms = dict(annotation=[], name="Spectrum", xlabel="m/z",
              ylabel="ion count", xtics=20, predict=None)
153
    chrom = dict(x=[0], y=[0], t_start=None, t_end=None,
Yan's avatar
Yan committed
154
                 name="Chromatogram", xlabel="time(min)",
3Yan's avatar
3Yan committed
155
                 ylabel="total ion count", timesarg=[])
Yan's avatar
Yan committed
156
    filename = [None]
3Yan's avatar
3Yan committed
157
    drlcache = [None, None]
158
    update = update_signal()
Yan's avatar
Yan committed
159

Yan's avatar
Yan committed
160
161
    config = cf.settings()

Yan's avatar
Yan committed
162
    p_logger = logging.getLogger('parseLogger')
Yan's avatar
Yan committed
163
    drl_logger = logging.getLogger('drlLogger')
164
    zce_logger = logging.getLogger('zceLogger')
Yan's avatar
Yan committed
165
166
    logging.basicConfig()
    p_logger.setLevel("WARN")
Yan's avatar
Yan committed
167
168
    #drl_logger.setLevel("INFO")
    drl_logger.setLevel("DEBUG")
169
    zce_logger.setLevel("DEBUG")
Yan's avatar
Yan committed
170

Yan's avatar
Yan committed
171
    graph = Figure(figsize=(5, 4), dpi=100, facecolor="None")
Yan's avatar
Yan committed
172
173
174
    grid = graph.add_gridspec(2, 1)
    chromatogram = graph.add_subplot(grid[0,0], facecolor=(1, 1, 1, 0.8))
    spectrum = graph.add_subplot(grid[1,0], facecolor=(1, 1, 1, 0.8))
Yan's avatar
Yan committed
175
176
177
178
179
180
181
182
183
    graph.tight_layout()
    mpl_canvas = FigureCanvas(graph)
    mpl_canvas.setStyleSheet("background-color:transparent;")
    mpl_canvas.setAutoFillBackground(False)
    
    gt.pan_factory(chromatogram)
    gt.zoom_factory(chromatogram, 1.15)
    gt.pan_factory(spectrum, ms)
    gt.zoom_factory(spectrum, 1.15, ms)
184
    mass_selector = SpanSelector(
Yan's avatar
Yan committed
185
        spectrum, lambda x_min, x_max: gt.pick_masses(
186
187
188
            x_min, x_max, spectrum, ms), 'horizontal', minspan=0.01,
        useblit=True, rectprops=dict(alpha=0.15, facecolor='purple'),
        button=3)
189
    time_selector = SpanSelector(
Yan's avatar
Yan committed
190
191
192
193
194
195
         chromatogram, lambda x_min, x_max: gt.pick_times(
             x_min, x_max, spectrum, ds, chromatogram, ms, chrom), 
         'horizontal', useblit=True, rectprops=dict(
             alpha=0.15, facecolor='purple'), button=3)

    app = QtWidgets.QApplication(sys.argv)
Yan's avatar
Yan committed
196
    main_window = QtWidgets.QMainWindow(windowTitle="Prasopes")
Yan's avatar
Yan committed
197

Yan's avatar
Yan committed
198
199
200
    if QtGui.QIcon.themeName() is "":
        QtGui.QIcon.setThemeName("TangoMFK")

Yan's avatar
Yan committed
201
    openact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
202
        "document-open"), "&Open...", None)
Yan's avatar
Yan committed
203
204
    openact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_O)
    openact.triggered.connect(lambda: load_file(
205
        main_window, chromatogram, spectrum, ds, ms, filename, chrom,
206
        update, config))
Yan's avatar
Yan committed
207
    exportact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
208
        "document-save-as"), "&Export...", None)
Yan's avatar
Yan committed
209
210
    exportact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_E)
    exportact.triggered.connect(lambda: ft.export_dial(
211
        spectrum, chrom, filename, main_window))
212
213
214
    printact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
        "document-print"), "&Print", None)
    printact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_P)
Yan's avatar
Yan committed
215
216
    printact.triggered.connect(lambda: print_graph(
        ds, ms, chrom, spectrum, filename))
Yan's avatar
Yan committed
217
    settingsact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
218
        "preferences-system"), "&Settings...", None)
Yan's avatar
Yan committed
219
220
    settingsact.triggered.connect(lambda: cf.dial(main_window))
    quitact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
221
        "application-exit"), "&Quit", None)
Yan's avatar
Yan committed
222
223
224
    quitact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
    quitact.triggered.connect(main_window.close)
    zceact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
225
        "applications-utilities"), "&TSQ zce...", None)
Yan's avatar
Yan committed
226
227
    zceact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_T)
    zceact.triggered.connect(lambda: zce.dialog(
228
        main_window, ds, filename, update))
Yan's avatar
Yan committed
229
    drlact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
230
        "applications-utilities"), "&DRL...", None)
Yan's avatar
Yan committed
231
232
    drlact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_D)
    drlact.triggered.connect(lambda: drl.main_window(
233
        main_window, ds, filename, drlcache, update))
Yan's avatar
Yan committed
234
235
    aboutact = QtWidgets.QAction("&About Prasopes", None)
    aboutact.triggered.connect(lambda: about(main_window))
Yan's avatar
Yan committed
236
    autozoomy = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
237
238
239
240
        "zoom-original"), "Auto Zoom Y", None, checkable=True,
        checked=config.value("view/autozoomy", type=bool))
    autozoomy.triggered.connect(lambda: config.setValue(
                                "view/autozoomy", autozoomy.isChecked()))
241
242
243
244
245
246
247
248
249
250
251
    autozoomy.triggered.connect(lambda: gt.autozoomy(spectrum))
    intensitiesact = QtWidgets.QAction("&Show intensities", None,
            checkable=True, checked=config.value("view/intensities",
                type=bool))
    intensitiesact.triggered.connect(lambda: config.setValue(
        "view/intensities", intensitiesact.isChecked()))
    intensitiesact.triggered.connect(lambda: gt.ann_spec(spectrum, ms))
    intensitiesact.triggered.connect(lambda: spectrum.figure.canvas.draw())
    oddevenact = QtWidgets.QAction("&Odd / even", None, checkable=True,
            checked=config.value("view/oddeven", type=bool))
    oddevenact.triggered.connect(lambda:
3Yan's avatar
3Yan committed
252
253
        oddeven_changed(chromatogram, spectrum, ds, ms, filename, chrom,
            config, oddevenact))
Yan's avatar
Yan committed
254
255
256
257
258
    filebrowseract = QtWidgets.QAction(
            "&File browser", None, checkable=True,
            checked=config.value("view/filebrowservisible", type=bool))
    filebrowseract.triggered.connect(lambda: config.setValue(
        "view/filebrowservisible", filebrowseract.isChecked()))
259
260
261
    filebrowseract.triggered.connect(
        lambda: treedock.show() if filebrowseract.isChecked() == True
                else treedock.hide())
Yan's avatar
Yan committed
262

Yan's avatar
Yan committed
263
264
265
266
    predictform = QtWidgets.QLineEdit(maximumWidth=150)
    predictform.editingFinished.connect(lambda: predictmz(
        predictform, chromatogram, spectrum, ds, ms, chrom))

Yan's avatar
Yan committed
267
268
    file_menu = QtWidgets.QMenu('&File', main_window)
    main_window.menuBar().addMenu(file_menu)
Yan's avatar
Yan committed
269
270
271
    file_menu.addAction(openact)
    file_menu.addAction(exportact)
    file_menu.addSeparator()
272
273
    file_menu.addAction(printact)
    file_menu.addSeparator()
Yan's avatar
Yan committed
274
275
276
    file_menu.addAction(settingsact)
    file_menu.addSeparator()
    file_menu.addAction(quitact)
Yan's avatar
Yan committed
277
278
    tools_menu = QtWidgets.QMenu('&Tools', main_window)
    main_window.menuBar().addMenu(tools_menu)
Yan's avatar
Yan committed
279
280
    tools_menu.addAction(zceact)
    tools_menu.addAction(drlact)
281
    tools_menu.addSeparator()
Yan's avatar
Yan committed
282
283
284
    view_menu = QtWidgets.QMenu('&View', main_window)
    view_menu.addAction(filebrowseract)
    view_menu.addAction(autozoomy)
285
286
287
    view_menu.addAction(intensitiesact)
    view_menu.addSeparator()
    view_menu.addAction(oddevenact)
Yan's avatar
Yan committed
288
    main_window.menuBar().addMenu(view_menu)
Yan's avatar
Yan committed
289
290
291
    help_menu = QtWidgets.QMenu('&Help', main_window)
    main_window.menuBar().addMenu(help_menu)
    help_menu.addAction(aboutact)
Yan's avatar
Yan committed
292
293
294
295

    main_widget = QtWidgets.QWidget(main_window)
    main_window.setCentralWidget(main_widget)

Yan's avatar
Yan committed
296
297
298
299
300
    toolBar = QtWidgets.QToolBar(main_window)
    toolBar.setAllowedAreas(QtCore.Qt.TopToolBarArea)
    toolBar.setFloatable(False)
    toolBar.setMovable(False)
    toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
Yan's avatar
Yan committed
301

Yan's avatar
Yan committed
302
303
304
    toolBar.addAction(openact)
    toolBar.addAction(exportact)
    toolBar.addSeparator()
Yan's avatar
Yan committed
305
306
307
    toolBar.addWidget(QtWidgets.QLabel("Predict Formula:"))
    toolBar.addWidget(predictform)
    toolBar.addSeparator()
Yan's avatar
Yan committed
308
309
    toolBar.addAction(zceact)
    toolBar.addAction(drlact)
Yan's avatar
Yan committed
310
311
    toolBar.addSeparator()
    toolBar.addAction(autozoomy)
Yan's avatar
Yan committed
312

313
314
    #fileModel gots stuck sometimes, better give it a separate thread
    #it complains in separate thread but work as should, maybe fix later..
3Yan's avatar
3Yan committed
315
    fileModel = QtWidgets.QFileSystemModel()
316
317
    treedockThread = QtCore.QThread()
    treedockThread.start()
318
    fileModel.setRootPath('')
319
    fileModel.moveToThread(treedockThread)
320
    activeDir = fileModel.index(config.value("open_folder"))
3Yan's avatar
3Yan committed
321
322
323
324
    treeview = QtWidgets.QTreeView()
    treeview.setModel(fileModel)
    treeview.setCurrentIndex(activeDir)
    treeview.expand(activeDir)
325
326
    dirview = QtWidgets.QListView()
    dirview.setModel(fileModel)
3Yan's avatar
3Yan committed
327
    treedock = QtWidgets.QDockWidget()
Yan's avatar
Yan committed
328
329
    if not config.value("view/filebrowservisible",type=bool):
        treedock.hide()
330
331
332
333
    treedock.setWidget(QtWidgets.QWidget())
    treedock_layout = QtWidgets.QVBoxLayout(treedock.widget())
    treedock_layout.addWidget(treeview)
    treedock_layout.addWidget(dirview)
3Yan's avatar
3Yan committed
334
335
    update.signal.connect(
        lambda: treeview.setCurrentIndex(fileModel.index(filename[0])))
336
    update.signal.connect(
3Yan's avatar
3Yan committed
337
338
        lambda: dirview.setRootIndex(fileModel.index(
            os.path.dirname(os.path.realpath(filename[0])))))
3Yan's avatar
3Yan committed
339
340
341
    treeview.doubleClicked.connect(lambda index: load_file(
        main_window, chromatogram, spectrum, ds, ms, filename, chrom,
        update, config, filename=fileModel.filePath(index)))
342
343
344
345
346
347
    dirview.doubleClicked.connect(lambda index: load_file(
        main_window, chromatogram, spectrum, ds, ms, filename, chrom,
        update, config, filename=fileModel.filePath(index)))
    dirview.clicked.connect(lambda index: load_file(
        main_window, chromatogram, spectrum, ds, ms, filename, chrom,
        update, config, filename=fileModel.filePath(index)))
348
349
350
351
352
353
    def closeOverride(event, dock, action):
        action.setChecked(False)
        dock.hide()
        event.ignore()
    treedock.closeEvent = lambda event: closeOverride(
            event, treedock, filebrowseract)
3Yan's avatar
3Yan committed
354
355
    main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, treedock)

Yan's avatar
Yan committed
356
    layout = QtWidgets.QVBoxLayout(main_widget)
Yan's avatar
Yan committed
357
    layout.addWidget(toolBar)
Yan's avatar
Yan committed
358
359
    layout.addWidget(mpl_canvas)

3Yan's avatar
3Yan committed
360

Yan's avatar
Yan committed
361
    main_window.setFocus()
Yan's avatar
Yan committed
362
363
    main_window.dragEnterEvent = lambda event: drag_entered(event)
    main_window.dropEvent = lambda event: dropped(
364
        event, main_window, chromatogram, spectrum, ds, ms, filename,
3Yan's avatar
3Yan committed
365
        chrom, update, config)
Yan's avatar
Yan committed
366
    main_window.setAcceptDrops(True)
Yan's avatar
Yan committed
367
368
    main_window.keyPressEvent = lambda event: key_pressed(event, chromatogram,
            spectrum, ds, ms, filename, chrom, config)
Yan's avatar
Yan committed
369

Yan's avatar
Yan committed
370
    if len(sys.argv) == 2:
371
        load_file(main_window, chromatogram, spectrum, ds, ms,
3Yan's avatar
3Yan committed
372
                  filename, chrom, update, config, filename=sys.argv[1])
Yan's avatar
Yan committed
373
    else:
3Yan's avatar
3Yan committed
374
375
        gt.pop_plot(0, 0, spectrum, ms)
        gt.pop_plot(0, 0, chromatogram, chrom)
Yan's avatar
Yan committed
376
377
378

    main_window.show()
    sys.exit(app.exec_())
Yan's avatar
Yan committed
379

380

Yan's avatar
Yan committed
381
382
if __name__ == "__main__":
    main()