#!/usr/bin/env python3 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from matplotlib.widgets import SpanSelector from matplotlib.gridspec import GridSpec 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: 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.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) def emit(self, record): msg = self.format(record) self.statusBar.showMessage(msg) def load_file(parent, chrom_plot, spc, d_set, ms_s, fn, chrom_s, update, settings, loadthread, filename=None): """populates dataset and plots it""" directory=settings.value("open_folder") if fn[0] is not None: directory=fn[0] if filename == None: filename = QtWidgets.QFileDialog.getOpenFileName( caption="Open spectrum", directory=directory, filter="Finnigan RAW files (*.raw, *.RAW)")[0] if filename is not '' and os.path.isfile(filename)\ and not os.path.isdir(filename): def runfnc(): try: d_set.clear() chrom_s['timesarg'].clear() [d_set.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: 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 [i.clear() for i in (ms_s['params'],ms_s['headers'])] if autoparams == True: try: ms_s['params'], rawheaders = load_params(filename) segments = [len(subset['chrom_dat'][0]) for subset in d_set] indicies = [sum(segments[:i+1]) for i in range(len(segments))] ms_s['headers'] = np.split(rawheaders, indicies)[:-1] except Exception as pex: QtWidgets.QMessageBox.critical( parent, "Parameters readout has failed!", "File is incompatible with the rawautoparams, " "no parameters loaded!\n\n" "Error message:\n{}".format(pex.args[0])) gt.populate(chrom_plot, spc, d_set, ms_s, chrom_s) fn[0] = filename parent.setWindowTitle("Prasopes - {}".format( os.path.basename(filename))) update.signal.emit() loadthread.run = runfnc loadthread.start() def print_graph(data_set, mass_spec, chrom_spec, spect, fn): def printimage(printdevice, img): printer.setResolution(600) painter = QtGui.QPainter(printdevice) painter.drawImage(0,0,img) 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(mass_spec, spect, printer) printPreview.paintRequested.connect(lambda: printimage(printer, image)) printPreview.exec() def update_spectrum(chromatogram, spect, ds, ms, fn, chrom, config): if fn[0] is not None: slims = [spect.get_xlim(), spect.get_ylim()] 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"))] gt.populate(chromatogram, spect, ds, ms, chrom) spect.set_xlim(slims[0]) spect.set_ylim(slims[1]) gt.ann_spec(spect, ms) spect.get_figure().canvas.draw() def dropped(event, parent, chromatogram, spectrum, ds, ms, filename, chrom, update, config, loadthread): dropurl = event.mimeData().urls()[0].toLocalFile() load_file(parent, chromatogram, spectrum, ds, ms, filename, chrom, 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, 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() 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) 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) 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) if event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right): gt.shift_times(event, spect, chrom, ds, ms_ds, chrom_ds) 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() ds = [] ms = dict(annotation=[], name="Spectrum", xlabel="m/z", ylabel="ion count", xtics=20, predict=None, params=[], headers=[]) chrom = dict(x=[0], y=[0], t_start=None, t_end=None, name="Chromatogram", xlabel="time(min)", ylabel="total ion count", timesarg=[]) filename = [None] drlcache = [None, None] update = update_signal() config = cf.settings() barHandler = QStatusBarLogger() p_logger = logging.getLogger('parseLogger') params_logger = logging.getLogger('acqLogLogger') drl_logger = logging.getLogger('drlLogger') zce_logger = logging.getLogger('zceLogger') 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") graph = Figure(figsize=(5, 4), dpi=100, facecolor="None") 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)) 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) mass_selector = SpanSelector( spectrum, lambda x_min, x_max: gt.pick_masses( x_min, x_max, spectrum, ms), 'horizontal', minspan=0.01, useblit=True, rectprops=dict(alpha=0.15, facecolor='purple'), button=3) time_selector = SpanSelector( 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) main_window = QtWidgets.QMainWindow(windowTitle="Prasopes") if QtGui.QIcon.themeName() is "": QtGui.QIcon.setThemeName("TangoMFK") 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, chromatogram, spectrum, ds, ms, filename, chrom, 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( spectrum, chrom, filename, 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( ds, ms, chrom, spectrum, filename)) 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, ds, filename, 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, ds, filename, drlcache, 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(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: oddeven_changed(chromatogram, spectrum, ds, ms, filename, chrom, config, oddevenact)) 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())) filebrowseract.triggered.connect( lambda: treedock.show() if filebrowseract.isChecked() == True else treedock.hide()) predictform = QtWidgets.QLineEdit(maximumWidth=150) predictform.editingFinished.connect(lambda: predictmz( predictform, chromatogram, spectrum, ds, ms, chrom)) 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(filebrowseract) view_menu.addAction(autozoomy) view_menu.addAction(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(mpl_canvas) 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) fileModel = QtWidgets.QFileSystemModel() fileModel.setRootPath('') activeDir = fileModel.index(config.value("open_folder")) treeview = QtWidgets.QTreeView() treeview.setModel(fileModel) treeview.setCurrentIndex(activeDir) treeview.expand(activeDir) dirview = QtWidgets.QListView() dirview.setModel(fileModel) treedock = QtWidgets.QDockWidget() def scrolltoonce(): fileModel.directoryLoaded.disconnect(scrolltoonce) treeview.scrollTo(fileModel.index(config.value("open_folder")), 1) fileModel.directoryLoaded.connect(scrolltoonce) fileModel.directoryLoaded.connect( lambda: treeview.resizeColumnToContents(0)) if not config.value("view/filebrowservisible",type=bool): treedock.hide() treedock.setWidget(QtWidgets.QWidget()) treedock_layout = QtWidgets.QVBoxLayout(treedock.widget()) treedock_layout.addWidget(treeview) treedock_layout.addWidget(dirview) update.signal.connect(lambda: treeview.setCurrentIndex(fileModel.index( filename[0]))) update.signal.connect(lambda: dirview.setRootIndex(fileModel.index( os.path.dirname(os.path.realpath(filename[0]))))) clickload = lambda index: load_file( main_window, chromatogram, spectrum, ds, ms, filename, chrom, update, config, loadthread, filename=fileModel.filePath(index)) for i in [treeview.doubleClicked, dirview.doubleClicked, dirview.clicked]: i.connect(clickload) #fileModel gots stuck sometimes, better give it a separate thread #it complains at the start, but still works later #and I did not found better solution treedockThread = QtCore.QThread() treedockThread.start() fileModel.moveToThread(treedockThread) def closeOverride(event, dock, action): action.setChecked(False) dock.hide() event.ignore() treedock.closeEvent = lambda event: closeOverride( event, treedock, filebrowseract) main_window.closeEvent = lambda event: treedockThread.quit() main_window.dragEnterEvent = lambda event: drag_entered(event) main_window.dropEvent = lambda event: dropped( event, main_window, chromatogram, spectrum, ds, ms, filename, chrom, update, config) main_window.setAcceptDrops(True) main_window.keyPressEvent = lambda event: key_pressed(event, chromatogram, spectrum, ds, ms, filename, chrom, config) main_window.addToolBar(QtCore.Qt.TopToolBarArea, toolBar) main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, treedock) main_window.setStatusBar(barHandler.statusBar) main_window.setFocus() if len(sys.argv) == 2: load_file(main_window, chromatogram, spectrum, ds, ms, filename, chrom, update, config, loadthread, filename=sys.argv[1]) else: gt.pop_plot(0, 0, spectrum, ms) gt.pop_plot(0, 0, chromatogram, chrom) main_window.show() sys.exit(app.exec_()) if __name__ == "__main__": main()