Esempio n. 1
0
    def loadFileMzML(self, file_path):
        self.isAnnoOn = False
        self.msexperimentWidget = QSplitter(Qt.Vertical)

        # data processing
        scans = self.readMS(file_path)

        # set Widgets
        self.spectrum_widget = SpectrumWidget()
        self.scan_widget = ScanTableWidget(scans)
        self.seqIons_widget = SequenceIonsWidget()
        self.error_widget = ErrorWidget()
        self.tic_widget = TICWidget()
        self.drawTic(scans)

        # connected signals
        self.scan_widget.sigScanClicked.connect(self.updateWidgetDataFromRow)
        self.tic_widget.sigRTClicked.connect(self.ticToTable)

        self.msexperimentWidget.addWidget(self.tic_widget)
        self.msexperimentWidget.addWidget(self.seqIons_widget)
        self.msexperimentWidget.addWidget(self.spectrum_widget)
        self.msexperimentWidget.addWidget(self.error_widget)
        self.msexperimentWidget.addWidget(self.scan_widget)
        self.mainlayout.addWidget(self.msexperimentWidget)

        # set widget sizes, where error plot is set smaller
        widget_height = self.msexperimentWidget.sizeHint().height()
        size_list = [
            widget_height, widget_height, widget_height, widget_height * 0.5,
            widget_height
        ]
        self.msexperimentWidget.setSizes(size_list)

        # default : first row selected.
        self.scan_widget.table_view.selectRow(0)
Esempio n. 2
0
class ControllerWidget(QWidget):
    """
    Used to merge spectrum, table, TIC, error plot and sequenceIons widgets together.

    """
    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        self.mainlayout = QHBoxLayout(self)
        self.isAnnoOn = True
        self.clickedRT = None
        self.seleTableRT = None
        self.mzs = np.array([])
        self.ppm = np.array([])
        self.colors = np.array([])
        self.scanIDDict = {}
        self.curr_table_index = None
        self.filteredIonFragments = []
        self.peakAnnoData = None

    def clearLayout(self, layout):
        for i in reversed(range(layout.count())):
            layout.itemAt(i).widget().setParent(None)

    def loadFileMzML(self, file_path):
        self.isAnnoOn = False
        self.msexperimentWidget = QSplitter(Qt.Vertical)

        # data processing
        scans = self.readMS(file_path)

        # set Widgets
        self.spectrum_widget = SpectrumWidget()
        self.scan_widget = ScanTableWidget(scans)
        self.seqIons_widget = SequenceIonsWidget()
        self.error_widget = ErrorWidget()
        self.tic_widget = TICWidget()
        self.drawTic(scans)

        # connected signals
        self.scan_widget.sigScanClicked.connect(self.updateWidgetDataFromRow)
        self.tic_widget.sigRTClicked.connect(self.ticToTable)

        self.msexperimentWidget.addWidget(self.tic_widget)
        self.msexperimentWidget.addWidget(self.seqIons_widget)
        self.msexperimentWidget.addWidget(self.spectrum_widget)
        self.msexperimentWidget.addWidget(self.error_widget)
        self.msexperimentWidget.addWidget(self.scan_widget)
        self.mainlayout.addWidget(self.msexperimentWidget)

        # set widget sizes, where error plot is set smaller
        widget_height = self.msexperimentWidget.sizeHint().height()
        size_list = [
            widget_height, widget_height, widget_height, widget_height * 0.5,
            widget_height
        ]
        self.msexperimentWidget.setSizes(size_list)

        # default : first row selected.
        self.scan_widget.table_view.selectRow(0)

    def loadFileIdXML(self, file_path):
        prot_ids = []
        pep_ids = []
        pyopenms.IdXMLFile().load(file_path, prot_ids, pep_ids)
        Ions = {}

        # extract ID data from file
        for peptide_id in pep_ids:
            pep_mz = peptide_id.getMZ()
            pep_rt = peptide_id.getRT()

            for hit in peptide_id.getHits():
                pep_seq = str(hit.getSequence().toString())
                if "." in pep_seq:
                    pep_seq = pep_seq[3:-1]
                else:
                    pep_seq = pep_seq[2:-1]

                for anno in hit.getPeakAnnotations():
                    ion_charge = anno.charge
                    ion_mz = anno.mz
                    ion_label = anno.annotation

                    Ions[ion_label] = [ion_mz, ion_charge]

                self.scanIDDict[round(pep_rt, 3)] = {
                    'm/z': pep_mz,
                    'PepSeq': pep_seq,
                    'PepIons': Ions
                }
                Ions = {}

        self.saveIdData()

    def saveIdData(
            self):  # save ID data in table (correct rows) for later usage
        rows = self.scan_widget.table_model.rowCount(self.scan_widget)

        for row in range(0, rows - 1):
            tableRT = round(
                self.scan_widget.table_model.index(row, 2).data(), 3)
            if tableRT in self.scanIDDict:
                index_seq = self.scan_widget.table_model.index(row, 6)
                self.scan_widget.table_model.setData(
                    index_seq, self.scanIDDict[tableRT]['PepSeq'],
                    Qt.DisplayRole)

                index_ions = self.scan_widget.table_model.index(row, 7)
                # data needs to be a string, but reversible -> using json.dumps()
                self.scan_widget.table_model.setData(
                    index_ions,
                    json.dumps(self.scanIDDict[tableRT]['PepIons']),
                    Qt.DisplayRole)

    def readMS(self, file_path):
        # read MzML files
        exp = pyopenms.MSExperiment()
        pyopenms.MzMLFile().load(file_path, exp)
        return exp

    def drawTic(self, scans):
        self.tic_widget.setTIC(scans.getTIC())

    def ticToTable(self,
                   rt):  # connect Tic info to table, and select specific row
        self.clickedRT = round(rt * 60, 3)
        if self.clickedRT != self.seleTableRT:
            self.scan_widget.table_view.selectRow(self.findClickedRT())

    def findClickedRT(self):  # find clicked RT in the scan table
        rows = self.scan_widget.table_model.rowCount(self.scan_widget)

        for row in range(0, rows - 1):
            if self.clickedRT == round(
                    self.scan_widget.table_model.index(row, 2).data(), 3):
                index = self.scan_widget.table_model.index(row, 2)
                try:
                    self.curr_table_index = self.scan_widget.proxy.mapFromSource(
                        index)  # use proxy to get from filtered model index
                    return self.curr_table_index.row()
                except ValueError:
                    print('could not found ModelIndex of row')

    # for the future calculate ppm and add it to the table
    def errorData(self, ions_data):
        if ions_data not in "-":
            ions_data_dict = json.loads(ions_data)
            if ions_data_dict != {}:
                self.colors, self.mzs = self.filterColorsMZIons(ions_data_dict)
                mzs_size = len(self.mzs)
                self.ppm = np.random.randint(0, 3, size=mzs_size)
                self.error_widget.setMassErrors(
                    self.mzs, self.ppm,
                    self.colors)  # works for a static np.array
            else:
                self.error_widget.clear()
        else:
            self.error_widget.clear()

    def filterColorsMZIons(
        self, ions_data_dict
    ):  # create color/mz array by distinguishing between prefix & suffix ions
        self.peakAnnoData = {
        }  # key is ion annotation (e.g. b2): [mz, color distinguishing prefix, suffix]
        colors = []
        mzs = []
        col_red = (255, 0, 0)  # suffix
        col_blue = (0, 0, 255)  # prefix

        for fragData in self.filteredIonFragments:
            anno = fragData[0]
            if anno[0] in "abc":
                colors.append(col_blue)
                mzs.append(ions_data_dict[anno][0])
                self.peakAnnoData[fragData[1]] = [
                    ions_data_dict[anno][0], col_blue
                ]
            elif anno[0] in "xyz":
                colors.append(col_red)
                mzs.append(ions_data_dict[anno][0])
                self.peakAnnoData[fragData[1]] = [
                    ions_data_dict[anno][0], col_red
                ]
        return np.array(colors), np.array(mzs)

    def updateWidgetDataFromRow(
        self, index
    ):  # after clicking on a new row, update spectrum, error plot, peptideSeq
        # current row RT value
        self.seleTableRT = round(index.siblingAtColumn(2).data(), 3)

        # set new spectrum with setting that all peaks should be displayed
        self.spectrum_widget.setSpectrum(self.scan_widget.curr_spec,
                                         zoomToFullRange=True)

        # only draw sequence with given ions for MS2 and error plot
        if index.siblingAtColumn(0).data() == 'MS2':
            self.drawSeqIons(
                index.siblingAtColumn(6).data(),
                index.siblingAtColumn(7).data())
            self.errorData(index.siblingAtColumn(7).data())
            if self.peakAnnoData is not None:  # peakAnnoData created with existing ions in errorData (bc of coloring)
                self.spectrum_widget.setPeakAnnotations(
                    self.createPeakAnnotation())
                self.spectrum_widget.redrawPlot()
            else:
                self.spectrum_widget._clear_peak_annotations()
                self.spectrum_widget.redrawPlot()

        # otherwise delete old data
        elif index.siblingAtColumn(0).data() == 'MS1':
            self.seqIons_widget.clear()
            self.error_widget.clear()
            self.peakAnnoData = None
            self.spectrum_widget._clear_peak_annotations()
            self.spectrum_widget.redrawPlot()

    def createPeakAnnotation(self):
        pStructList = []
        # for the future -> check clashes like in the TIC widget and then add labels (should be done in SpectrumWidget)
        for anno, data in self.peakAnnoData.items():
            mz, anno_color = data[0], data[1]
            index = self.find_nearest_Index(self.spectrum_widget._mzs, mz)
            pStructList.append(
                PeakAnnoStruct(mz=self.spectrum_widget._mzs[index],
                               intensity=self.spectrum_widget._ints[index],
                               text_label=anno,
                               symbol=None,
                               symbol_color=anno_color))
        return pStructList

    def find_nearest_Index(self, array, value):
        array = np.asarray(array)
        idx = (np.abs(array - value)).argmin()
        return idx

    def drawSeqIons(self, seq, ions):  # generate provided peptide sequence
        seq = re.sub(r'\([^)]*\)', '',
                     seq)  # remove content in brackets -> easier usage

        # only draw sequence for M2 with peptide and ion data
        if seq not in "-" and ions not in "-":
            self.seqIons_widget.setPeptide(seq)
            ions_dict = json.loads(
                ions)  # transform string data back to a dict
            if ions_dict != {}:
                self.suffix, self.prefix = self.filterIonsPrefixSuffixData(
                    ions_dict)
                self.seqIons_widget.setPrefix(self.prefix)
                self.seqIons_widget.setSuffix(self.suffix)
            else:  # no ions data
                self.prefix, self.suffix = {}, {}
                self.seqIons_widget.setPrefix(self.prefix)
                self.seqIons_widget.setSuffix(self.suffix)
                self.peakAnnoData = None
        else:
            self.seqIons_widget.clear()
            self.peakAnnoData = None

    def filterIonsPrefixSuffixData(
            self,
            ions):  # filter raw ion data and return suffix and prefix dicts
        suffix = {}
        prefix = {}

        ions_anno = list(ions.keys())
        self.filteredIonFragments = [
        ]  # annotation(s) of raw ion data (used as key(s))

        for anno in ions_anno:
            if anno[1].isdigit() and anno[0] in "abcyxz":
                index, anno_short = self.filterAnnotationIon(anno)
                if (index in suffix) and (anno[0] in "yxz") and (
                        anno_short
                        not in suffix[index]):  # avoid double annos e.g. y14
                    suffix[index].append(anno_short)
                elif (index in prefix) and (anno[0] in "abc") and (
                        anno_short not in prefix[index]):
                    prefix[index].append(anno_short)
                elif anno[0] in "yxz":  # non existing keys
                    suffix[index] = [anno_short]
                elif anno[0] in "abc":  # non existing keys
                    prefix[index] = [anno_short]
        return suffix, prefix

    def filterAnnotationIon(self, fragment_anno):
        # filter from raw ion data annotation index and filtered annotation name (e.g. y2)
        index = [s for s in re.findall(r'-?\d+\.?\d*', fragment_anno)][0]
        ion_anno = fragment_anno.split(index)[0] + index
        self.filteredIonFragments.append((fragment_anno, ion_anno))
        return int(index), ion_anno
import sys
from PyQt5.QtWidgets import QApplication

import pyopenms

from GUI_EXAMPLE_BASE import GUI_EXAMPLE_BASE
sys.path.insert(0, '../view')
from SequenceIonsWidget import SequenceIonsWidget

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = GUI_EXAMPLE_BASE()  # plain QMainWindow with basic layout and menu bar
    example_widget = SequenceIonsWidget(ex)
    example_widget.setPeptide("HLDKIDMLKSLD")
    example_widget.setPrefix({
        1: ["a1", "b1", "c1"],
        3: ["a3", "b3", "c3"],
        5: ["a3", "b3", "c3"]
    })
    example_widget.setSuffix({
        1: ["x1", "x3"],
        2: ["x2", "y2"],
        4: ["x4", "y4", "z4"]
    })
    ex.setExampleWidget(example_widget)
    ex.show()
    sys.exit(app.exec_())