#!/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 from PyQt5 import QtGui from PyQt5 import QtPrintSupport from rawprasslib import load_raw from rawprasslib import rawprasslib from prasopes.predictmz import predict as getmzpattern try: from rawautoparams import load_params autoparams = True except ImportError: autoparams = False import numpy as np import prasopes.config as cf import prasopes.datatools as dt import prasopes.drltools as drl import prasopes.filetools as ft import prasopes.graphtools as gt import prasopes.imagetools as imgt import prasopes.zcetools as zce import prasopes.docks as docks import prasopes.tangoicons import sys import matplotlib import logging import os.path matplotlib.use("Qt5Agg") class update_signal(QtCore.QObject): signal = QtCore.pyqtSignal() class QStatusBarLogger(logging.Handler): def __init__(self, parent=None): super().__init__() self.statusBar = QtWidgets.QStatusBar(parent) self.trigger = update_signal() self.msg = str("") def emit(self, record): self.msg = self.format(record) self.trigger.signal.emit() class AugFigureCanvas(FigureCanvas): # TODO: move the widget into graphtools when I'm done def __init__(self): self.figure = Figure(figsize=(5, 4), dpi=100, facecolor="None", constrained_layout=True) super().__init__(self.figure) self.ds = [] self.ms = dict(annotation=[], name="Spectrum", xlabel="m/z", ylabel="ion count", xtics=20, predict=None, params=[], headers=[], texts=[]) self.chrom = dict( x=[0], y=[0], t_start=None, t_end=None, machtype=None, name="Chromatogram", xlabel="time(min)", ylabel="total ion count", timesarg=[]) self.filename = None self.drlcache = [None, None] grid = self.figure.add_gridspec(2, 1) self.chromplot = self.figure.add_subplot(grid[0, 0], facecolor=(1, 1, 1, 0.8)) self.spectplot = self.figure.add_subplot(grid[1, 0], facecolor=(1, 1, 1, 0.8)) self.setStyleSheet("background-color:transparent;") self.setAutoFillBackground(False) self.paramstable = dt.table(["", "name", "value"], 100) gt.pan_factory(self.chromplot) gt.zoom_factory(self.chromplot, 1.15) gt.pan_factory(self.spectplot, self.ms) gt.zoom_factory(self.spectplot, 1.15, self.ms) gt.textedit_factory(self.spectplot, self.ms) self.mass_selector = gt.AugSpanSelector(self.spectplot, self.ms) # TODO: rewrite timeSelector to better shape when I'm done self.time_selector = SpanSelector( self.chromplot, lambda x_min, x_max: gt.pick_times( x_min, x_max, self), 'horizontal', useblit=True, rectprops=dict(alpha=0.15, facecolor='purple'), button=3) def load_file(parent, augCanvas, update, settings, loadthread, filename=None): """populates dataset and plots it""" directory = augCanvas.filename or settings.value("open_folder") filename = filename or QtWidgets.QFileDialog.getOpenFileName( caption="Open spectrum", directory=directory, filter="Finnigan RAW files (*.raw, *.RAW)")[0] if filename != '' and os.path.isfile(filename)\ and not os.path.isdir(filename): error = update_signal() errormsg = [] def runfnc(): try: [i.clear() for i in ( augCanvas.ds, augCanvas.chrom['timesarg'], augCanvas.ms['params'], augCanvas.ms['headers'])] [augCanvas.ds.append(dict( chrom_dat=i[0], masses=i[1], matrix=i[2])) for i in load_raw(filename, settings.value("tmp_location"))] except rawprasslib.ParsingException as pex: errormsg.append("Opening of the file has failed!") errormsg.append( "File is incompatible with the rawprasslib, " "canceling request!\n\n" "Error message:\n{}".format(pex.args[0])) error.signal.emit() return if autoparams: try: (augCanvas.ms['params'], rawheaders, augCanvas.chrom['machtype']) = load_params(filename) 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: errormsg.append( "File is incompatible with the rawautoparams,") errormsg.append( "no parameters loaded!\n\n" "Error message:\n{}".format(pex.args[0])) error.signal.emit() gt.populate(augCanvas) augCanvas.filename = filename parent.setWindowTitle("Prasopes - {}".format( os.path.basename(filename))) update.signal.emit() error.signal.connect(lambda: QtWidgets.QMessageBox.critical( parent, errormsg[0], errormsg[1])) loadthread.run = runfnc loadthread.start() def print_graph(augCanvas): def printimage(printdevice, img): printer.setResolution(600) painter = QtGui.QPainter(printdevice) font = painter.font() linesize = printer.resolution()/15 font.setPixelSize(linesize) painter.setFont(font) painter.drawImage(0, 0, img) offset = img.size().height() line = 1 spacing = 1.5 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) line += 1 painter.end() # TODO: substitute the QPrintPreviewDialog with QPrintPreviewWidget printPreview = QtPrintSupport.QPrintPreviewDialog() printer = printPreview.printer() printer.setPageSize(printer.A5) printer.setDuplex(printer.DuplexNone) image = imgt.paint_image(augCanvas.ms, augCanvas.spectplot, augCanvas.filename, printer) printPreview.paintRequested.connect(lambda: printimage(printer, image)) printPreview.exec() 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() def dropped(event, parent, augCanvas, update, config, loadthread): dropurl = event.mimeData().urls()[0].toLocalFile() load_file(parent, augCanvas, update, config, loadthread, filename=dropurl) def drag_entered(event): if event.mimeData().hasUrls() and event.mimeData().urls()[0]\ .toLocalFile().lower().endswith('.raw'): event.accept() def predictmz(form, augCanvas): text = form.text() if text == "": augCanvas.ms["predict"] = None return 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() def oddeven_changed(augCanvas, config, oddevenact): config.setValue("view/oddeven", oddevenact.isChecked()) update_spectrum(augCanvas, config) def key_pressed(event, augCanvas, config): if event.key() == QtCore.Qt.Key_F5: update_spectrum(augCanvas, config) if event.key() == QtCore.Qt.Key_C: if event.modifiers().__int__() == QtCore.Qt.ControlModifier: if augCanvas.paramstable.underMouse(): dt.clip_tablestr(augCanvas) else: imgt.clip_spect_img(augCanvas.ms, augCanvas.spectplot, augCanvas.filename) if event.modifiers().__int__() == QtCore.Qt.ControlModifier + \ QtCore.Qt.ShiftModifier: dt.clip_spectstr(augCanvas) if event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right): gt.shift_times(event, augCanvas) def about(parent): """constructs window with "about" info""" QtWidgets.QMessageBox.information( parent, "About Prasopes", "Prasopes Finnigan raw file viewer\n\n" "Version: 0.0.12 (alpha)") def main(): app = QtWidgets.QApplication(sys.argv) loadthread = QtCore.QThread() augCanvas = AugFigureCanvas() update = update_signal() config = cf.settings() barHandler = QStatusBarLogger() barHandler.trigger.signal.connect( lambda: barHandler.statusBar.showMessage(barHandler.msg)) p_logger = logging.getLogger('parseLogger') params_logger = logging.getLogger('acqLogLogger') drl_logger = logging.getLogger('drlLogger') zce_logger = logging.getLogger('zceLogger') # mpl_logger = logging.getLogger('matplotlib') # mpl_logger.setLevel("DEBUG") logging.basicConfig() # p_logger.setLevel("WARN") # p_logger.setLevel("DEBUG") # drl_logger.setLevel("INFO") drl_logger.setLevel("DEBUG") zce_logger.setLevel("DEBUG") # params_logger.setLevel("DEBUG") p_logger.addHandler(barHandler) zce_logger.addHandler(barHandler) params_logger.addHandler(barHandler) barHandler.setLevel("DEBUG") main_window = QtWidgets.QMainWindow(windowTitle="Prasopes") if QtGui.QIcon.themeName() == "": QtGui.QIcon.setThemeName("TangoMFK") consoledock = docks.consoleDockWidget( locals(), "&Console", "view/consolevisible") treedock = docks.treeDockWidget( "&File browser", "view/filebrowservisible", update, load_file, main_window, augCanvas, config, loadthread) paramsdock = docks.augDock("Acquisition parameters", "&Acq parameters", "view/acqparvisible") update.signal.connect(lambda: gt.update_paramstable(augCanvas)) paramsdock.setWidget(augCanvas.paramstable) openact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "document-open"), "&Open...", None) openact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_O) openact.triggered.connect(lambda: load_file( main_window, augCanvas, update, config, loadthread)) exportact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "document-save-as"), "&Export...", None) exportact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_E) exportact.triggered.connect(lambda: ft.export_dial( augCanvas, main_window)) printact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "document-print"), "&Print", None) printact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_P) printact.triggered.connect(lambda: print_graph(augCanvas)) settingsact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "preferences-system"), "&Settings...", None) settingsact.triggered.connect(lambda: cf.dial(main_window)) quitact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "application-exit"), "&Quit", None) quitact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_Q) quitact.triggered.connect(main_window.close) zceact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "applications-utilities"), "&TSQ zce...", None) zceact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_T) zceact.triggered.connect(lambda: zce.dialog( main_window, augCanvas, update)) drlact = QtWidgets.QAction(QtGui.QIcon.fromTheme( "applications-utilities"), "&DRL...", None) drlact.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_D) drlact.triggered.connect(lambda: drl.main_window( main_window, augCanvas, update)) aboutact = QtWidgets.QAction("&About Prasopes", None) aboutact.triggered.connect(lambda: about(main_window)) autozoomy = QtWidgets.QAction(QtGui.QIcon.fromTheme( "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())) autozoomy.triggered.connect(lambda: gt.autozoomy(augCanvas.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( augCanvas.spectplot, augCanvas.ms)) intensitiesact.triggered.connect(lambda: augCanvas.draw()) oddevenact = QtWidgets.QAction( "&Odd / even", None, checkable=True, checked=config.value( "view/oddeven", type=bool)) oddevenact.triggered.connect( lambda: oddeven_changed(augCanvas, config, oddevenact)) predictform = QtWidgets.QLineEdit(maximumWidth=150) predictform.editingFinished.connect(lambda: predictmz( predictform, augCanvas)) file_menu = QtWidgets.QMenu('&File', main_window) main_window.menuBar().addMenu(file_menu) file_menu.addAction(openact) file_menu.addAction(exportact) file_menu.addSeparator() file_menu.addAction(printact) file_menu.addSeparator() file_menu.addAction(settingsact) file_menu.addSeparator() file_menu.addAction(quitact) tools_menu = QtWidgets.QMenu('&Tools', main_window) main_window.menuBar().addMenu(tools_menu) tools_menu.addAction(zceact) tools_menu.addAction(drlact) tools_menu.addSeparator() view_menu = QtWidgets.QMenu('&View', main_window) [view_menu.addAction(i.action) for i in (treedock, paramsdock, consoledock)] [view_menu.addAction(i) for i in (autozoomy, intensitiesact)] view_menu.addSeparator() view_menu.addAction(oddevenact) main_window.menuBar().addMenu(view_menu) help_menu = QtWidgets.QMenu('&Help', main_window) main_window.menuBar().addMenu(help_menu) help_menu.addAction(aboutact) main_window.setCentralWidget(augCanvas) toolBar = QtWidgets.QToolBar(main_window) toolBar.setAllowedAreas(QtCore.Qt.TopToolBarArea) toolBar.setFloatable(False) toolBar.setMovable(False) toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) toolBar.addAction(openact) toolBar.addAction(exportact) toolBar.addSeparator() toolBar.addWidget(QtWidgets.QLabel("Predict Formula:")) toolBar.addWidget(predictform) toolBar.addSeparator() toolBar.addAction(zceact) toolBar.addAction(drlact) toolBar.addSeparator() toolBar.addAction(autozoomy) main_window.dragEnterEvent = lambda event: drag_entered(event) main_window.dropEvent = lambda event: dropped( event, main_window, augCanvas, update, config, loadthread) main_window.setAcceptDrops(True) main_window.keyPressEvent = lambda event: key_pressed( event, augCanvas, config) main_window.addToolBar(QtCore.Qt.TopToolBarArea, toolBar) main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, treedock) main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, paramsdock) main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, consoledock) main_window.setStatusBar(barHandler.statusBar) main_window.setFocus() if len(sys.argv) == 2: load_file(main_window, augCanvas, update, config, loadthread, filename=sys.argv[1]) else: gt.pop_plot(0, 0, augCanvas.spectplot, augCanvas.ms) gt.pop_plot(0, 0, augCanvas.chromplot, augCanvas.chrom) main_window.show() sys.exit(app.exec_()) if __name__ == "__main__": main()