__main__.py 17.1 KB
Newer Older
Yan's avatar
Yan committed
1
2
3
4
#!/usr/bin/env python3

from PyQt5 import QtCore
from PyQt5 import QtWidgets
Yan's avatar
Yan committed
5
from PyQt5 import QtGui
6
from PyQt5 import QtPrintSupport
Yan's avatar
Yan committed
7
from rawprasslib import load_raw
Yan's avatar
Yan committed
8
from prasopes.predictmz import predict as getmzpattern
Yan's avatar
Yan committed
9
10
try:
    from rawautoparams import load_params
Yan's avatar
Yan committed
11
    import rawautoparams
Yan's avatar
Yan committed
12
    autoparams = True
13
except ImportError:
Yan's avatar
Yan committed
14
    autoparams = False
Yan's avatar
Yan committed
15
import rawprasslib
Yan's avatar
Yan committed
16
import numpy as np
Yan's avatar
Yan committed
17
import prasopes.config as cf
18
import prasopes.datatools as dt
19
import prasopes.drltools_gui as drlgui
Yan's avatar
Yan committed
20
import prasopes.filetools as ft
Yan's avatar
Yan committed
21
22
import prasopes.graphtools as gt
import prasopes.imagetools as imgt
Yan's avatar
Yan committed
23
import prasopes.zcetools as zce
Yan's avatar
Yan committed
24
import prasopes.docks as docks
Yan's avatar
Yan committed
25
import prasopes.tangoicons
Yan's avatar
Yan committed
26
27
import sys
import logging
3Yan's avatar
3Yan committed
28
import os.path
Yan's avatar
Yan committed
29
30


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


35
36
37
38
class QStatusBarLogger(logging.Handler):
    def __init__(self, parent=None):
        super().__init__()
        self.statusBar = QtWidgets.QStatusBar(parent)
Yan's avatar
Yan committed
39
40
        self.trigger = update_signal()
        self.msg = str("")
41
42

    def emit(self, record):
Yan's avatar
Yan committed
43
44
        self.msg = self.format(record)
        self.trigger.signal.emit()
45
46


47
def load_file(parent, augCanvas, update, settings, loadthread, filename=None):
Yan's avatar
Yan committed
48
    """populates dataset and plots it"""
49
50
51
    directory = augCanvas.filename or settings.value("open_folder")
    filename = filename or QtWidgets.QFileDialog.getOpenFileName(
            caption="Open spectrum", directory=directory,
3Yan's avatar
3Yan committed
52
            filter="Finnigan RAW files (*.raw, *.RAW)")[0]
53
54
    if filename != '' and os.path.isfile(filename)\
            and not os.path.isdir(filename):
Yan's avatar
Yan committed
55
56
        error = update_signal()
        errormsg = []
57

58
        def runfnc():
Yan's avatar
Yan committed
59
            try:
Yan's avatar
Yan committed
60
                [i.clear() for i in (
61
62
                    augCanvas.ds, augCanvas.chrom['timesarg'],
                    augCanvas.ms['params'], augCanvas.ms['headers'])]
63
64
                [augCanvas.ds.append(dict(
                    chrom_dat=i[0], masses=i[1], matrix=i[2]))
65
                 for i in load_raw(filename, settings.value("tmp_location"))]
66
            except rawprasslib.ParsingException as pex:
Yan's avatar
Yan committed
67
68
                errormsg.append("Opening of the file has failed!")
                errormsg.append(
69
70
                    "File is incompatible with the rawprasslib, "
                    "canceling request!\n\n"
Yan's avatar
Yan committed
71
                    "Error message:\n{}".format(pex.args[0]))
Yan's avatar
Yan committed
72
                error.signal.emit()
73
                return
74
            if autoparams:
75
                try:
76
                    (augCanvas.ms['params'], rawheaders,
Yan's avatar
Yan committed
77
78
                        augCanvas.chrom['machtype']) = load_params(
                                filename, settings.value("tmp_location"))
79
                    segments = [len(subset['chrom_dat'][0])
80
                                for subset in augCanvas.ds]
81
                    indicies = [sum(segments[:i+1])
82
                                for i in range(len(segments))]
83
84
                    augCanvas.ms['headers'] = np.split(
                            rawheaders, indicies)[:-1]
85
                except Exception as pex:
86
87
                    errormsg.append(
                            "File is incompatible with the rawautoparams,")
Yan's avatar
Yan committed
88
89
90
91
                    errormsg.append(
                            "no parameters loaded!\n\n"
                            "Error message:\n{}".format(pex.args[0]))
                    error.signal.emit()
92
93
            gt.populate(augCanvas)
            augCanvas.filename = filename
94
95
96
            oldrecents = settings.value("recents")
            oldrecents.remove(filename) if filename in oldrecents else None
            settings.setValue("recents", [filename, *oldrecents])
97
            update.signal.emit()
98
99
        error.signal.connect(lambda: QtWidgets.QMessageBox.critical(
            parent, errormsg[0], errormsg[1]))
100
101
        loadthread.run = runfnc
        loadthread.start()
3Yan's avatar
3Yan committed
102

Yan's avatar
Yan committed
103

104
def print_graph(augCanvas):
Yan's avatar
Yan committed
105
    def printimage(printdevice, img):
Yan's avatar
Yan committed
106
        printer.setResolution(600)
Yan's avatar
Yan committed
107
        painter = QtGui.QPainter(printdevice)
Yan's avatar
Yan committed
108
109
110
111
        font = painter.font()
        linesize = printer.resolution()/15
        font.setPixelSize(linesize)
        painter.setFont(font)
112
        painter.drawImage(0, 0, img)
Yan's avatar
Yan committed
113
114
115
        offset = img.size().height()
        line = 1
        spacing = 1.5
116
117
118
119
120
        for row in range(augCanvas.paramstable.rowCount()):
            if augCanvas.paramstable.cellWidget(row, 0).checkState() == 2:
                text = augCanvas.paramstable.item(row, 1).text() +\
                       augCanvas.paramstable.item(row, 2).text()
                painter.drawText(300, int(offset+line*linesize*spacing), text)
Yan's avatar
Yan committed
121
                line += 1
Yan's avatar
Yan committed
122
        painter.end()
123
    # TODO: substitute the QPrintPreviewDialog with QPrintPreviewWidget
124
125
    printPreview = QtPrintSupport.QPrintPreviewDialog()
    printer = printPreview.printer()
Yan's avatar
Yan committed
126
127
    printer.setPageSize(printer.A5)
    printer.setDuplex(printer.DuplexNone)
128
129
    image = imgt.paint_image(augCanvas.ms, augCanvas.spectplot,
                             augCanvas.filename, printer)
Yan's avatar
Yan committed
130
    printPreview.paintRequested.connect(lambda:
Yan's avatar
Yan committed
131
                                        printimage(printer, image))
132
133
134
    printPreview.exec()


135
136
137
138
139
140
141
def update_spectrum(augCanvas, config):
    if augCanvas.filename:
        slims = [augCanvas.spectplot.get_xlim(),
                 augCanvas.spectplot.get_ylim()]
        augCanvas.ds.clear()
        [augCanvas.ds.append(dict(chrom_dat=i[0], masses=i[1], matrix=i[2]))
         for i in load_raw(augCanvas.filename, config.value("tmp_location"))]
3Yan's avatar
3Yan committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
        if autoparams:
            try:
                (augCanvas.ms['params'], rawheaders,
                    augCanvas.chrom['machtype']) = load_params(
                            augCanvas.filename, config.value("tmp_location"))
                segments = [len(subset['chrom_dat'][0])
                            for subset in augCanvas.ds]
                indicies = [sum(segments[:i+1])
                            for i in range(len(segments))]
                augCanvas.ms['headers'] = np.split(
                        rawheaders, indicies)[:-1]
            except Exception as pex:
                QtWidgets.QMessageBox.critical(None,
                        "File is incompatible with the rawautoparams",
                        "no parameters loaded!\n\n"
                        "Error message:\n{}".format(pex))
158
159
160
161
162
        gt.populate(augCanvas)
        augCanvas.spectplot.set_xlim(slims[0])
        augCanvas.spectplot.set_ylim(slims[1])
        gt.ann_spec(augCanvas.spectplot, augCanvas.ms)
        augCanvas.draw()
Yan's avatar
Yan committed
163
164


165
def update_recents(rcm, main_window, augCanvas, update, config, loadthread):
166
    """updates recents_menu (rcm)"""
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    rcm.clear()
    # Actions need to be stored somewhere. Otherwise they end up in garbage.
    rcm.actionCache = []
    for i, j in enumerate(config.value("recents"), start=1):
        rcm.actionCache.append(QtWidgets.QAction("&{}. {}".format(i, j), None))
        rcm.actionCache[-1].triggered.connect(lambda _, fn=j: load_file(
            main_window, augCanvas, update, config, loadthread, filename=fn))
    rcm.actionCache.append(rcm.addSeparator())
    rcm.actionCache.append(QtWidgets.QAction("Clear Recents", None))
    [rcm.actionCache[-1].triggered.connect(i) for i in (
         lambda: config.setValue("recents", ""), lambda: update_recents(
             rcm, main_window, augCanvas, update, config, loadthread))]
    rcm.addActions(rcm.actionCache)


182
def dropped(event, parent, augCanvas, update, config, loadthread):
183
    dropurl = event.mimeData().urls()[0].toLocalFile()
184
    load_file(parent, augCanvas, update, config, loadthread, filename=dropurl)
Yan's avatar
Yan committed
185
186
187
188


def drag_entered(event):
    if event.mimeData().hasUrls() and event.mimeData().urls()[0]\
189
            .toLocalFile().lower().endswith('.raw'):
Yan's avatar
Yan committed
190
191
192
        event.accept()


193
def predictmz(form, augCanvas):
Yan's avatar
Yan committed
194
195
    text = form.text()
    if text == "":
196
        augCanvas.ms["predict"] = None
Yan's avatar
Yan committed
197
        return
198
199
200
201
202
203
204
    slims = [augCanvas.spectplot.get_xlim(),
             augCanvas.spectplot.get_ylim()]
    augCanvas.ms["predict"] = getmzpattern(text)
    gt.populate(augCanvas)
    augCanvas.spectplot.set_xlim(slims[0])
    augCanvas.spectplot.set_ylim(slims[1])
    augCanvas.draw()
Yan's avatar
Yan committed
205
206


207
def oddeven_changed(augCanvas, config, oddevenact):
3Yan's avatar
3Yan committed
208
    config.setValue("view/oddeven", oddevenact.isChecked())
209
    update_spectrum(augCanvas, config)
3Yan's avatar
3Yan committed
210
211


3Yan's avatar
3Yan committed
212
def key_pressed(event, augCanvas, config, update):
Yan's avatar
Yan committed
213
    if event.key() == QtCore.Qt.Key_F5:
214
        update_spectrum(augCanvas, config)
3Yan's avatar
3Yan committed
215
216
        if augCanvas.filename:
            update.signal.emit()
217
218
    if event.key() == QtCore.Qt.Key_C:
        if event.modifiers().__int__() == QtCore.Qt.ControlModifier:
219
220
221
222
223
            if augCanvas.paramstable.underMouse():
                dt.clip_tablestr(augCanvas)
            else:
                imgt.clip_spect_img(augCanvas.ms, augCanvas.spectplot,
                                    augCanvas.filename)
224
225
        if event.modifiers().__int__() == QtCore.Qt.ControlModifier + \
                QtCore.Qt.ShiftModifier:
226
            dt.clip_spectstr(augCanvas)
Yan's avatar
Yan committed
227
    if event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right):
228
        gt.shift_times(event, augCanvas)
Yan's avatar
Yan committed
229
230


Yan's avatar
Yan committed
231
232
def about(parent):
    """constructs window with "about" info"""
Yan's avatar
Yan committed
233
234
235
    rawparver = rawautoparams.__version__ if autoparams else\
            "library not found"

Yan's avatar
Yan committed
236
237
238
    QtWidgets.QMessageBox.information(
            parent, "About Prasopes",
            "Prasopes Finnigan raw file viewer\n\n"
Yan's avatar
Yan committed
239
240
241
242
            "Version: {} (alpha)\n\n"
            "Rawprasslib version: {}\n"
            "Rawautoparams version: {}".format(
                prasopes.__version__, rawprasslib.__version__, rawparver))
Yan's avatar
Yan committed
243
244


Yan's avatar
Yan committed
245
def main():
246
    app = QtWidgets.QApplication(sys.argv)
247
    loadthread = QtCore.QThread()
248

249
    augCanvas = gt.AugFigureCanvas()
250
    update = update_signal()
Yan's avatar
Yan committed
251

Yan's avatar
Yan committed
252
253
    config = cf.settings()

254
    barHandler = QStatusBarLogger()
255
256
    barHandler.trigger.signal.connect(
        lambda: barHandler.statusBar.showMessage(barHandler.msg))
257

Yan's avatar
Yan committed
258
    p_logger = logging.getLogger('parseLogger')
259
    params_logger = logging.getLogger('acqLogLogger')
Yan's avatar
Yan committed
260
    drl_logger = logging.getLogger('drlLogger')
261
    zce_logger = logging.getLogger('zceLogger')
Yan's avatar
Yan committed
262
    logging.basicConfig()
263
    # p_logger.setLevel("WARN")
264
    p_logger.setLevel("DEBUG")
265
    # drl_logger.setLevel("INFO")
Yan's avatar
Yan committed
266
    drl_logger.setLevel("DEBUG")
267
    zce_logger.setLevel("DEBUG")
268
    # params_logger.setLevel("DEBUG")
269
270
    p_logger.addHandler(barHandler)
    zce_logger.addHandler(barHandler)
271
272
    params_logger.addHandler(barHandler)
    barHandler.setLevel("DEBUG")
Yan's avatar
Yan committed
273

Yan's avatar
Yan committed
274
    main_window = QtWidgets.QMainWindow(windowTitle="Prasopes")
275
276
    update.signal.connect(lambda: main_window.setWindowTitle(
        "Prasopes - {}".format(os.path.basename(augCanvas.filename))))
Yan's avatar
Yan committed
277

278
    if QtGui.QIcon.themeName() == "":
Yan's avatar
Yan committed
279
280
        QtGui.QIcon.setThemeName("TangoMFK")

Yan's avatar
Yan committed
281
282
283
    consoledock = docks.consoleDockWidget(
            locals(), "&Console", "view/consolevisible")
    treedock = docks.treeDockWidget(
284
285
            "&File browser", "view/filebrowservisible", update, load_file,
            main_window, augCanvas, config, loadthread)
Yan's avatar
Yan committed
286
    paramsdock = docks.AugDock("Acquisition parameters", "&Acq parameters",
287
                               "view/acqparvisible")
288
289
    update.signal.connect(lambda: gt.update_paramstable(augCanvas))
    paramsdock.setWidget(augCanvas.paramstable)
Yan's avatar
Yan committed
290

Yan's avatar
Yan committed
291
    openact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
292
        "document-open"), "&Open...", None)
Yan's avatar
Yan committed
293
294
    openact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_O)
    openact.triggered.connect(lambda: load_file(
295
        main_window, augCanvas, update, config, loadthread))
Yan's avatar
Yan committed
296
    exportact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
297
        "document-save-as"), "&Export...", None)
Yan's avatar
Yan committed
298
299
    exportact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_E)
    exportact.triggered.connect(lambda: ft.export_dial(
300
        augCanvas, main_window))
301
302
303
    printact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
        "document-print"), "&Print", None)
    printact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_P)
304
    printact.triggered.connect(lambda: print_graph(augCanvas))
Yan's avatar
Yan committed
305
    settingsact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
306
        "preferences-system"), "&Settings...", None)
Yan's avatar
Yan committed
307
308
    settingsact.triggered.connect(lambda: cf.dial(main_window))
    quitact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
309
        "application-exit"), "&Quit", None)
Yan's avatar
Yan committed
310
311
312
    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
313
        "applications-utilities"), "&TSQ zce...", None)
Yan's avatar
Yan committed
314
315
    zceact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_T)
    zceact.triggered.connect(lambda: zce.dialog(
316
        main_window, augCanvas, update))
Yan's avatar
Yan committed
317
    drlact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
318
        "applications-utilities"), "&DRL...", None)
Yan's avatar
Yan committed
319
    drlact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_D)
320
    drlact.triggered.connect(lambda: drlgui.main_window(
321
        main_window, augCanvas, update))
Yan's avatar
Yan committed
322
323
    aboutact = QtWidgets.QAction("&About Prasopes", None)
    aboutact.triggered.connect(lambda: about(main_window))
Yan's avatar
Yan committed
324
    autozoomy = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
325
326
327
328
        "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()))
Yan's avatar
Yan committed
329
    autozoomy.triggered.connect(lambda: gt.autozoomy(augCanvas.spectplot))
330
    intensitiesact = QtWidgets.QAction(
Yan's avatar
Yan committed
331
332
        "&Show intensities", None, checkable=True,
        checked=config.value("view/intensities", type=bool))
333
334
    intensitiesact.triggered.connect(lambda: config.setValue(
        "view/intensities", intensitiesact.isChecked()))
Yan's avatar
Yan committed
335
336
    intensitiesact.triggered.connect(lambda: gt.ann_spec(
        augCanvas.spectplot, augCanvas.ms))
337
    intensitiesact.triggered.connect(lambda: augCanvas.draw())
338
    oddevenact = QtWidgets.QAction(
Yan's avatar
Yan committed
339
340
            "&Odd / even", None, checkable=True,
            checked=config.value("view/oddeven", type=bool))
341
342
    oddevenact.triggered.connect(
        lambda: oddeven_changed(augCanvas, config, oddevenact))
Yan's avatar
Yan committed
343

Yan's avatar
Yan committed
344
345
    predictform = QtWidgets.QLineEdit(maximumWidth=150)
    predictform.editingFinished.connect(lambda: predictmz(
346
        predictform, augCanvas))
Yan's avatar
Yan committed
347

348
349
350
351
352
353
    recents_menu = QtWidgets.QMenu('Open &Recent', main_window)
    update_recents(recents_menu, main_window, augCanvas,
                   update, config, loadthread)
    update.signal.connect(lambda: update_recents(
        recents_menu, main_window, augCanvas, update, config, loadthread))

Yan's avatar
Yan committed
354
355
    file_menu = QtWidgets.QMenu('&File', main_window)
    main_window.menuBar().addMenu(file_menu)
Yan's avatar
Yan committed
356
    file_menu.addAction(openact)
357
    file_menu.addMenu(recents_menu)
Yan's avatar
Yan committed
358
359
    file_menu.addAction(exportact)
    file_menu.addSeparator()
360
361
    file_menu.addAction(printact)
    file_menu.addSeparator()
Yan's avatar
Yan committed
362
363
364
    file_menu.addAction(settingsact)
    file_menu.addSeparator()
    file_menu.addAction(quitact)
Yan's avatar
Yan committed
365
366
    tools_menu = QtWidgets.QMenu('&Tools', main_window)
    main_window.menuBar().addMenu(tools_menu)
Yan's avatar
Yan committed
367
368
    tools_menu.addAction(zceact)
    tools_menu.addAction(drlact)
369
    tools_menu.addSeparator()
Yan's avatar
Yan committed
370
    view_menu = QtWidgets.QMenu('&View', main_window)
371
372
    [view_menu.addAction(i.action) for i in
     (treedock, paramsdock, consoledock)]
Yan's avatar
Yan committed
373
    [view_menu.addAction(i) for i in (autozoomy, intensitiesact)]
374
375
    view_menu.addSeparator()
    view_menu.addAction(oddevenact)
Yan's avatar
Yan committed
376
    main_window.menuBar().addMenu(view_menu)
Yan's avatar
Yan committed
377
378
379
    help_menu = QtWidgets.QMenu('&Help', main_window)
    main_window.menuBar().addMenu(help_menu)
    help_menu.addAction(aboutact)
Yan's avatar
Yan committed
380

381
    main_window.setCentralWidget(augCanvas)
Yan's avatar
Yan committed
382

Yan's avatar
Yan committed
383
384
385
386
387
    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
388

Yan's avatar
Yan committed
389
390
391
    toolBar.addAction(openact)
    toolBar.addAction(exportact)
    toolBar.addSeparator()
Yan's avatar
Yan committed
392
393
394
    toolBar.addWidget(QtWidgets.QLabel("Predict Formula:"))
    toolBar.addWidget(predictform)
    toolBar.addSeparator()
Yan's avatar
Yan committed
395
396
    toolBar.addAction(zceact)
    toolBar.addAction(drlact)
Yan's avatar
Yan committed
397
398
    toolBar.addSeparator()
    toolBar.addAction(autozoomy)
Yan's avatar
Yan committed
399

Yan's avatar
Yan committed
400
401
    main_window.dragEnterEvent = lambda event: drag_entered(event)
    main_window.dropEvent = lambda event: dropped(
402
        event, main_window, augCanvas, update, config, loadthread)
Yan's avatar
Yan committed
403
    main_window.setAcceptDrops(True)
404
    main_window.keyPressEvent = lambda event: key_pressed(
3Yan's avatar
3Yan committed
405
            event, augCanvas, config, update)
Yan's avatar
Yan committed
406
407
    main_window.resizeEvent = lambda event: augCanvas.constrained_draw()
    update.signal.connect(lambda: augCanvas.constrained_draw())
Yan's avatar
Yan committed
408

409
410
    main_window.addToolBar(QtCore.Qt.TopToolBarArea, toolBar)
    main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, treedock)
Yan's avatar
Yan committed
411
    main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, paramsdock)
Yan's avatar
Yan committed
412
    main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, consoledock)
413
    main_window.setStatusBar(barHandler.statusBar)
414
415
416

    main_window.setFocus()

Yan's avatar
Yan committed
417
    if len(sys.argv) == 2:
418
        load_file(main_window, augCanvas, update, config, loadthread,
419
                  filename=sys.argv[1])
Yan's avatar
Yan committed
420
    else:
421
422
        gt.pop_plot(0, 0, augCanvas.spectplot, augCanvas.ms)
        gt.pop_plot(0, 0, augCanvas.chromplot, augCanvas.chrom)
Yan's avatar
Yan committed
423
424
425

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

427

Yan's avatar
Yan committed
428
429
if __name__ == "__main__":
    main()