__main__.py 16.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
Yan's avatar
Yan committed
19
import prasopes.drltools as drl
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
77
                    (augCanvas.ms['params'], rawheaders,
                        augCanvas.chrom['machtype']) = load_params(filename)
78
                    segments = [len(subset['chrom_dat'][0])
79
                                for subset in augCanvas.ds]
80
                    indicies = [sum(segments[:i+1])
81
                                for i in range(len(segments))]
82
83
                    augCanvas.ms['headers'] = np.split(
                            rawheaders, indicies)[:-1]
84
                except Exception as pex:
85
86
                    errormsg.append(
                            "File is incompatible with the rawautoparams,")
Yan's avatar
Yan committed
87
88
89
90
                    errormsg.append(
                            "no parameters loaded!\n\n"
                            "Error message:\n{}".format(pex.args[0]))
                    error.signal.emit()
91
92
            gt.populate(augCanvas)
            augCanvas.filename = filename
93
94
95
            oldrecents = settings.value("recents")
            oldrecents.remove(filename) if filename in oldrecents else None
            settings.setValue("recents", [filename, *oldrecents])
96
            update.signal.emit()
97
98
        error.signal.connect(lambda: QtWidgets.QMessageBox.critical(
            parent, errormsg[0], errormsg[1]))
99
100
        loadthread.run = runfnc
        loadthread.start()
3Yan's avatar
3Yan committed
101

Yan's avatar
Yan committed
102

103
def print_graph(augCanvas):
Yan's avatar
Yan committed
104
    def printimage(printdevice, img):
Yan's avatar
Yan committed
105
        printer.setResolution(600)
Yan's avatar
Yan committed
106
        painter = QtGui.QPainter(printdevice)
Yan's avatar
Yan committed
107
108
109
110
        font = painter.font()
        linesize = printer.resolution()/15
        font.setPixelSize(linesize)
        painter.setFont(font)
111
        painter.drawImage(0, 0, img)
Yan's avatar
Yan committed
112
113
114
        offset = img.size().height()
        line = 1
        spacing = 1.5
115
116
117
118
119
        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
120
                line += 1
Yan's avatar
Yan committed
121
        painter.end()
122
    # TODO: substitute the QPrintPreviewDialog with QPrintPreviewWidget
123
124
    printPreview = QtPrintSupport.QPrintPreviewDialog()
    printer = printPreview.printer()
Yan's avatar
Yan committed
125
126
    printer.setPageSize(printer.A5)
    printer.setDuplex(printer.DuplexNone)
127
128
    image = imgt.paint_image(augCanvas.ms, augCanvas.spectplot,
                             augCanvas.filename, printer)
Yan's avatar
Yan committed
129
    printPreview.paintRequested.connect(lambda:
Yan's avatar
Yan committed
130
                                        printimage(printer, image))
131
132
133
    printPreview.exec()


134
135
136
137
138
139
140
141
142
143
144
145
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
146
147


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


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


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


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


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


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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    main_window.setFocus()

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

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

408

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