__main__.py 16.2 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
142
143
144
145
146
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"))]
        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
147
148


149
def update_recents(rcm, main_window, augCanvas, update, config, loadthread):
150
    """updates recents_menu (rcm)"""
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
    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)


166
def dropped(event, parent, augCanvas, update, config, loadthread):
167
    dropurl = event.mimeData().urls()[0].toLocalFile()
168
    load_file(parent, augCanvas, update, config, loadthread, filename=dropurl)
Yan's avatar
Yan committed
169
170
171
172


def drag_entered(event):
    if event.mimeData().hasUrls() and event.mimeData().urls()[0]\
173
            .toLocalFile().lower().endswith('.raw'):
Yan's avatar
Yan committed
174
175
176
        event.accept()


177
def predictmz(form, augCanvas):
Yan's avatar
Yan committed
178
179
    text = form.text()
    if text == "":
180
        augCanvas.ms["predict"] = None
Yan's avatar
Yan committed
181
        return
182
183
184
185
186
187
188
    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
189
190


191
def oddeven_changed(augCanvas, config, oddevenact):
3Yan's avatar
3Yan committed
192
    config.setValue("view/oddeven", oddevenact.isChecked())
193
    update_spectrum(augCanvas, config)
3Yan's avatar
3Yan committed
194
195


196
def key_pressed(event, augCanvas, config):
Yan's avatar
Yan committed
197
    if event.key() == QtCore.Qt.Key_F5:
198
        update_spectrum(augCanvas, config)
199
200
    if event.key() == QtCore.Qt.Key_C:
        if event.modifiers().__int__() == QtCore.Qt.ControlModifier:
201
202
203
204
205
            if augCanvas.paramstable.underMouse():
                dt.clip_tablestr(augCanvas)
            else:
                imgt.clip_spect_img(augCanvas.ms, augCanvas.spectplot,
                                    augCanvas.filename)
206
207
        if event.modifiers().__int__() == QtCore.Qt.ControlModifier + \
                QtCore.Qt.ShiftModifier:
208
            dt.clip_spectstr(augCanvas)
Yan's avatar
Yan committed
209
    if event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right):
210
        gt.shift_times(event, augCanvas)
Yan's avatar
Yan committed
211
212


Yan's avatar
Yan committed
213
214
def about(parent):
    """constructs window with "about" info"""
Yan's avatar
Yan committed
215
216
217
    rawparver = rawautoparams.__version__ if autoparams else\
            "library not found"

Yan's avatar
Yan committed
218
219
220
    QtWidgets.QMessageBox.information(
            parent, "About Prasopes",
            "Prasopes Finnigan raw file viewer\n\n"
Yan's avatar
Yan committed
221
222
223
224
            "Version: {} (alpha)\n\n"
            "Rawprasslib version: {}\n"
            "Rawautoparams version: {}".format(
                prasopes.__version__, rawprasslib.__version__, rawparver))
Yan's avatar
Yan committed
225
226


Yan's avatar
Yan committed
227
def main():
228
    app = QtWidgets.QApplication(sys.argv)
229
    loadthread = QtCore.QThread()
230

231
    augCanvas = gt.AugFigureCanvas()
232
    update = update_signal()
Yan's avatar
Yan committed
233

Yan's avatar
Yan committed
234
235
    config = cf.settings()

236
    barHandler = QStatusBarLogger()
237
238
    barHandler.trigger.signal.connect(
        lambda: barHandler.statusBar.showMessage(barHandler.msg))
239

Yan's avatar
Yan committed
240
    p_logger = logging.getLogger('parseLogger')
241
    params_logger = logging.getLogger('acqLogLogger')
Yan's avatar
Yan committed
242
    drl_logger = logging.getLogger('drlLogger')
243
    zce_logger = logging.getLogger('zceLogger')
Yan's avatar
Yan committed
244
    logging.basicConfig()
245
    # p_logger.setLevel("WARN")
246
    p_logger.setLevel("DEBUG")
247
    # drl_logger.setLevel("INFO")
Yan's avatar
Yan committed
248
    drl_logger.setLevel("DEBUG")
249
    zce_logger.setLevel("DEBUG")
250
    # params_logger.setLevel("DEBUG")
251
252
    p_logger.addHandler(barHandler)
    zce_logger.addHandler(barHandler)
253
254
    params_logger.addHandler(barHandler)
    barHandler.setLevel("DEBUG")
Yan's avatar
Yan committed
255

Yan's avatar
Yan committed
256
    main_window = QtWidgets.QMainWindow(windowTitle="Prasopes")
257
258
    update.signal.connect(lambda: main_window.setWindowTitle(
        "Prasopes - {}".format(os.path.basename(augCanvas.filename))))
Yan's avatar
Yan committed
259

260
    if QtGui.QIcon.themeName() == "":
Yan's avatar
Yan committed
261
262
        QtGui.QIcon.setThemeName("TangoMFK")

Yan's avatar
Yan committed
263
264
265
    consoledock = docks.consoleDockWidget(
            locals(), "&Console", "view/consolevisible")
    treedock = docks.treeDockWidget(
266
267
            "&File browser", "view/filebrowservisible", update, load_file,
            main_window, augCanvas, config, loadthread)
Yan's avatar
Yan committed
268
    paramsdock = docks.AugDock("Acquisition parameters", "&Acq parameters",
269
                               "view/acqparvisible")
270
271
    update.signal.connect(lambda: gt.update_paramstable(augCanvas))
    paramsdock.setWidget(augCanvas.paramstable)
Yan's avatar
Yan committed
272

Yan's avatar
Yan committed
273
    openact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
274
        "document-open"), "&Open...", None)
Yan's avatar
Yan committed
275
276
    openact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_O)
    openact.triggered.connect(lambda: load_file(
277
        main_window, augCanvas, update, config, loadthread))
Yan's avatar
Yan committed
278
    exportact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
279
        "document-save-as"), "&Export...", None)
Yan's avatar
Yan committed
280
281
    exportact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_E)
    exportact.triggered.connect(lambda: ft.export_dial(
282
        augCanvas, main_window))
283
284
285
    printact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
        "document-print"), "&Print", None)
    printact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_P)
286
    printact.triggered.connect(lambda: print_graph(augCanvas))
Yan's avatar
Yan committed
287
    settingsact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
288
        "preferences-system"), "&Settings...", None)
Yan's avatar
Yan committed
289
290
    settingsact.triggered.connect(lambda: cf.dial(main_window))
    quitact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
291
        "application-exit"), "&Quit", None)
Yan's avatar
Yan committed
292
293
294
    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
295
        "applications-utilities"), "&TSQ zce...", None)
Yan's avatar
Yan committed
296
297
    zceact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_T)
    zceact.triggered.connect(lambda: zce.dialog(
298
        main_window, augCanvas, update))
Yan's avatar
Yan committed
299
    drlact = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
300
        "applications-utilities"), "&DRL...", None)
Yan's avatar
Yan committed
301
    drlact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_D)
302
    drlact.triggered.connect(lambda: drlgui.main_window(
303
        main_window, augCanvas, update))
Yan's avatar
Yan committed
304
305
    aboutact = QtWidgets.QAction("&About Prasopes", None)
    aboutact.triggered.connect(lambda: about(main_window))
Yan's avatar
Yan committed
306
    autozoomy = QtWidgets.QAction(QtGui.QIcon.fromTheme(
Yan's avatar
Yan committed
307
308
309
310
        "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
311
    autozoomy.triggered.connect(lambda: gt.autozoomy(augCanvas.spectplot))
312
    intensitiesact = QtWidgets.QAction(
Yan's avatar
Yan committed
313
314
        "&Show intensities", None, checkable=True,
        checked=config.value("view/intensities", type=bool))
315
316
    intensitiesact.triggered.connect(lambda: config.setValue(
        "view/intensities", intensitiesact.isChecked()))
Yan's avatar
Yan committed
317
318
    intensitiesact.triggered.connect(lambda: gt.ann_spec(
        augCanvas.spectplot, augCanvas.ms))
319
    intensitiesact.triggered.connect(lambda: augCanvas.draw())
320
    oddevenact = QtWidgets.QAction(
Yan's avatar
Yan committed
321
322
            "&Odd / even", None, checkable=True,
            checked=config.value("view/oddeven", type=bool))
323
324
    oddevenact.triggered.connect(
        lambda: oddeven_changed(augCanvas, config, oddevenact))
Yan's avatar
Yan committed
325

Yan's avatar
Yan committed
326
327
    predictform = QtWidgets.QLineEdit(maximumWidth=150)
    predictform.editingFinished.connect(lambda: predictmz(
328
        predictform, augCanvas))
Yan's avatar
Yan committed
329

330
331
332
333
334
335
    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
336
337
    file_menu = QtWidgets.QMenu('&File', main_window)
    main_window.menuBar().addMenu(file_menu)
Yan's avatar
Yan committed
338
    file_menu.addAction(openact)
339
    file_menu.addMenu(recents_menu)
Yan's avatar
Yan committed
340
341
    file_menu.addAction(exportact)
    file_menu.addSeparator()
342
343
    file_menu.addAction(printact)
    file_menu.addSeparator()
Yan's avatar
Yan committed
344
345
346
    file_menu.addAction(settingsact)
    file_menu.addSeparator()
    file_menu.addAction(quitact)
Yan's avatar
Yan committed
347
348
    tools_menu = QtWidgets.QMenu('&Tools', main_window)
    main_window.menuBar().addMenu(tools_menu)
Yan's avatar
Yan committed
349
350
    tools_menu.addAction(zceact)
    tools_menu.addAction(drlact)
351
    tools_menu.addSeparator()
Yan's avatar
Yan committed
352
    view_menu = QtWidgets.QMenu('&View', main_window)
353
354
    [view_menu.addAction(i.action) for i in
     (treedock, paramsdock, consoledock)]
Yan's avatar
Yan committed
355
    [view_menu.addAction(i) for i in (autozoomy, intensitiesact)]
356
357
    view_menu.addSeparator()
    view_menu.addAction(oddevenact)
Yan's avatar
Yan committed
358
    main_window.menuBar().addMenu(view_menu)
Yan's avatar
Yan committed
359
360
361
    help_menu = QtWidgets.QMenu('&Help', main_window)
    main_window.menuBar().addMenu(help_menu)
    help_menu.addAction(aboutact)
Yan's avatar
Yan committed
362

363
    main_window.setCentralWidget(augCanvas)
Yan's avatar
Yan committed
364

Yan's avatar
Yan committed
365
366
367
368
369
    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
370

Yan's avatar
Yan committed
371
372
373
    toolBar.addAction(openact)
    toolBar.addAction(exportact)
    toolBar.addSeparator()
Yan's avatar
Yan committed
374
375
376
    toolBar.addWidget(QtWidgets.QLabel("Predict Formula:"))
    toolBar.addWidget(predictform)
    toolBar.addSeparator()
Yan's avatar
Yan committed
377
378
    toolBar.addAction(zceact)
    toolBar.addAction(drlact)
Yan's avatar
Yan committed
379
380
    toolBar.addSeparator()
    toolBar.addAction(autozoomy)
Yan's avatar
Yan committed
381

Yan's avatar
Yan committed
382
383
    main_window.dragEnterEvent = lambda event: drag_entered(event)
    main_window.dropEvent = lambda event: dropped(
384
        event, main_window, augCanvas, update, config, loadthread)
Yan's avatar
Yan committed
385
    main_window.setAcceptDrops(True)
386
387
    main_window.keyPressEvent = lambda event: key_pressed(
            event, augCanvas, config)
Yan's avatar
Yan committed
388
389
    main_window.resizeEvent = lambda event: augCanvas.constrained_draw()
    update.signal.connect(lambda: augCanvas.constrained_draw())
Yan's avatar
Yan committed
390

391
392
    main_window.addToolBar(QtCore.Qt.TopToolBarArea, toolBar)
    main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, treedock)
Yan's avatar
Yan committed
393
    main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, paramsdock)
Yan's avatar
Yan committed
394
    main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, consoledock)
395
    main_window.setStatusBar(barHandler.statusBar)
396
397
398

    main_window.setFocus()

Yan's avatar
Yan committed
399
    if len(sys.argv) == 2:
400
        load_file(main_window, augCanvas, update, config, loadthread,
401
                  filename=sys.argv[1])
Yan's avatar
Yan committed
402
    else:
403
404
        gt.pop_plot(0, 0, augCanvas.spectplot, augCanvas.ms)
        gt.pop_plot(0, 0, augCanvas.chromplot, augCanvas.chrom)
Yan's avatar
Yan committed
405
406
407

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

409

Yan's avatar
Yan committed
410
411
if __name__ == "__main__":
    main()