From afc04c3f3871336569bf43129e5f90bc32405246 Mon Sep 17 00:00:00 2001 From: matteopilz Date: Thu, 31 Jul 2025 10:57:45 +0200 Subject: [PATCH] removed view files --- src/view/ControllerWidget.py | 344 -------------- src/view/ErrorWidget.py | 83 ---- src/view/FilesNumberHandler.py | 359 --------------- src/view/GUI_FastaViewer.py | 817 --------------------------------- src/view/MS1MapWidget.py | 65 --- src/view/ScanBrowserWidget.py | 52 --- src/view/ScanTableWidget.py | 270 ----------- src/view/SequenceIonsWidget.py | 362 --------------- src/view/SpectrumWidget.py | 241 ---------- src/view/TICWidget.py | 333 -------------- src/view/mzTabLoadWidget.py | 48 -- src/view/mzTabTableWidget.py | 316 ------------- 12 files changed, 3290 deletions(-) delete mode 100644 src/view/ControllerWidget.py delete mode 100644 src/view/ErrorWidget.py delete mode 100644 src/view/FilesNumberHandler.py delete mode 100644 src/view/GUI_FastaViewer.py delete mode 100644 src/view/MS1MapWidget.py delete mode 100644 src/view/ScanBrowserWidget.py delete mode 100644 src/view/ScanTableWidget.py delete mode 100644 src/view/SequenceIonsWidget.py delete mode 100644 src/view/SpectrumWidget.py delete mode 100644 src/view/TICWidget.py delete mode 100644 src/view/mzTabLoadWidget.py delete mode 100644 src/view/mzTabTableWidget.py diff --git a/src/view/ControllerWidget.py b/src/view/ControllerWidget.py deleted file mode 100644 index 13231bdc8..000000000 --- a/src/view/ControllerWidget.py +++ /dev/null @@ -1,344 +0,0 @@ -import json -import re -from collections import namedtuple - -import numpy as np -import pyopenms -from ErrorWidget import ErrorWidget -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QHBoxLayout, QWidget, QSplitter -from ScanTableWidget import ScanTableWidget -from SequenceIonsWidget import SequenceIonsWidget -from SpectrumWidget import SpectrumWidget -from TICWidget import TICWidget - -PeakAnnoStruct = namedtuple( - "PeakAnnoStruct", - "mz intensity text_label \ - symbol symbol_color", -) -LadderAnnoStruct = namedtuple( - "LadderAnnoStruct", - "mz_list \ - text_label_list color", -) - - -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.calculateTIC()) - - 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) - # transform string data back to a dict - ions_dict = json.loads(ions) - 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()) - # annotation(s) of raw ion data (used as key(s)) - self.filteredIonFragments = [] - - 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 diff --git a/src/view/ErrorWidget.py b/src/view/ErrorWidget.py deleted file mode 100644 index 2144cd0ad..000000000 --- a/src/view/ErrorWidget.py +++ /dev/null @@ -1,83 +0,0 @@ -import numpy as np -import pyqtgraph as pg -from PyQt5.QtCore import QPointF -from pyqtgraph import PlotWidget - -pg.setConfigOption("background", "w") # white background -pg.setConfigOption("foreground", "k") # black peaks - - -class ErrorWidget(PlotWidget): - """ - Used to plot a error plot to display the derivation - between exact mass and theoretical mass. - """ - - def __init__(self, *args): - PlotWidget.__init__(self) - - self.setLimits(xMin=0) - self.setLabel("bottom", "m/z") - self.setLabel("left", "ppm") - self._mzs = np.array([]) - self._ppm = np.array([]) - self._color_lib = np.array([]) - self.getViewBox().sigXRangeChanged.connect(self._autoscaleYAxis) - self.setMouseEnabled(x=True, y=False) - - def setMassErrors(self, mz, ppm, colors): - """ - Used for creating new error plot - with the m/z of the peptides fragments. - :param mz: An numpy array of m/z - (mass divided by charge number) of the ions - (starting with xyz or abc) - :param ppm: An numpy array of random numbers, - ppm needs to be calculated - :param colors: An numpy array of colors consisting of red and blue - (representing prefix -> blue and suffix -> red ions) - - """ - self._mzs = mz - self._ppm = ppm - self._color_lib = colors - self.redraw() - - def redraw(self): - self.plot(clear=True) - self.setXRange(np.amin(self._mzs), np.amax(self._mzs)) - self._autoscaleYAxis() - self._plotHorizontalLine() - self._plotMassErrors() - - def _plotMassErrors(self): - scattergraph = pg.ScatterPlotItem() - points = [] - for i in range(0, self._ppm.size): - points.append( - { - "pos": (self._mzs[i], self._ppm[i]), - "brush": pg.mkBrush(self._color_lib[i]), - } - ) - scattergraph.addPoints(points) - self.addItem(scattergraph) - - def _plotHorizontalLine(self): - horizontalLine = pg.InfiniteLine( - pos=QPointF(0.0, 0.0), angle=0, pen=pg.mkColor("k") - ) - self.addItem(horizontalLine) - - def _autoscaleYAxis(self): - x_range = self.getAxis("bottom").range - if x_range == [0, 1]: # workaround for axis sometimes not being set - x_range = [np.amin(self._mzs), np.amax(self._mzs)] - self.currMaxY = self._getMaxMassErrorInRange(x_range) - if self.currMaxY: - self.setYRange(self.currMaxY * (-1), self.currMaxY, update=False) - - def _getMaxMassErrorInRange(self, xrange): - left = np.searchsorted(self._mzs, xrange[0], side="left") - right = np.searchsorted(self._mzs, xrange[1], side="right") - return np.amax(abs(self._ppm[left:right]), initial=1) diff --git a/src/view/FilesNumberHandler.py b/src/view/FilesNumberHandler.py deleted file mode 100644 index 0cf5d3f35..000000000 --- a/src/view/FilesNumberHandler.py +++ /dev/null @@ -1,359 +0,0 @@ -import sys -import os -from PyQt5 import QtWidgets -from PyQt5.QtWidgets import (QWidget, QToolTip, - QPushButton, QApplication, QMainWindow, - QAction, qApp, - QHBoxLayout, QVBoxLayout, QMessageBox, - QLineEdit, QTableWidget, QTableWidgetItem, - QGridLayout, QPlainTextEdit, - QDesktopWidget, QLabel, QRadioButton, - QGroupBox, QSizePolicy, QCheckBox, QFileDialog, - QTextEdit, QTextBrowser) -from PyQt5.QtGui import QFont, QColor - -#making a files dictionary that is going to be a global variable - -files_dictionary = { - - "fasta": "", - "tsv": "", - "data": "", - "mzML": "", - "idXML": "", - "ini_path" : "" - } - -booleans_dictionary = { - - "fasta": False, - "tsv": False, - "data": False, - "mzML": False, - "idXML": False, - "ini_path" : False - } - -class Files_Number_Handler(): - """This class contains methods that are used to determine the number of - files in a given parameter. It can be a folder path or an array - - Attributes - ---------- - - - fasta_files : Array - an array to save the paths of fasta files - - tsv_files : Array - an array to save the paths of tsv files - - mzML_files : Array - an array to save the paths of mzMl files - - idXML_files : Array - an array to save the paths of idXML files - - ini_files : Array - an array to save the paths of ini files - fileslist : Array - an array containg all the files of a fiven folder - - User_Warning : QMessageBox - a QMessageBox to inform the user of erros - - Methods - ------- - Identify_Files_Numbers(folder_path) - gets a folder path and determines if there are less than one file - of each type that is needed if not than saves the files in arrays - - Identify_Files_Numbers_Manualy(folder_path) - works just like Identify_Files_Numbers but only cheks files that - can not be loaded manually - - Check_If_More_Than_One(arraytotest) - checks if there is more than one element in a given array - - Check_If_Less_Than_One(arraytotest) - checks if there is less than one element in a given array - - Check_If_One(arraytotest) - checks if there is exactly one element in a given array - - Dictionary_Change_File(file_type,file_path) - changes the value of a given file type in the global dictionary - - Dictionary_Return_Value(file_type) - gives the value of the key that mathces the file type in the dictionary - - Dictionary_Change_Boolean(file_type) - changes the bollean value of the key that mathces the given file type - - Dictionary_Return_Boolean(file_type) - returns the value of the key that matches the given file type - - """ - - #gets a folder path as an argument and searches for files that end with - #.fasta or .tsv and saves them in the corresponding Array - - def Identify_Files_Numbers(folder_path): - - """ - gets a folder path and determines if there are less than one file - of each type that is needed if not than saves the files in arrays - - Parameters - --------- - folder_path - a string that descibes the path to a folder - - Returns - ------- - fasta_files,tsv_files,mzML_files,idXML_files,ini_files - array that contain the paths to the files as strings - """ - fasta_files=[] - tsv_files = [] - mzML_files = [] - idXML_files= [] - ini_files = [] - - fileslist = sorted(os.listdir(folder_path)) - - for file in fileslist: - if file.endswith(".fasta"): - fasta_files.append(file) - - if file.endswith(".tsv"): - tsv_files.append(file) - - if file.endswith(".mzML"): - mzML_files.append(file) - - if file.endswith(".idXML"): - idXML_files.append(file) - - if file.endswith(".ini"): - ini_files.append(file) - - if file.endswith(".csv"): - tsv_files.append(file) - - if len(mzML_files) == 0 and len(idXML_files) == 0: - User_Warning = QMessageBox() - User_Warning.setIcon(QMessageBox.Information) - User_Warning.setText("No mzML and idXML files found. Please select a different folder.") - User_Warning.setWindowTitle("Information") - Information = User_Warning.exec_() - - - if len(mzML_files) == 0 and len(idXML_files) != 0: - User_Warning = QMessageBox() - User_Warning.setIcon(QMessageBox.Information) - User_Warning.setText("No mzML files found. Please select a different folder.") - User_Warning.setWindowTitle("Information") - Information = User_Warning.exec_() - Files_Number_Handler.Dictionary_Change_Boolean('idXML') - - if len(mzML_files) != 0 and len(idXML_files) == 0: - User_Warning = QMessageBox() - User_Warning.setIcon(QMessageBox.Information) - User_Warning.setText("No idXML files found. Please select a different folder.") - User_Warning.setWindowTitle("Information") - Information = User_Warning.exec_() - Files_Number_Handler.Dictionary_Change_Boolean('mzML') - - if len(mzML_files) != 0 and len(idXML_files) != 0: - Files_Number_Handler.Dictionary_Change_Boolean('mzML') - Files_Number_Handler.Dictionary_Change_Boolean('idXML') - - - - - return fasta_files,tsv_files,mzML_files,idXML_files,ini_files - - #works just like Identify_Files_Numbers but for the manualy option - - def Identify_Files_Numbers_Manualy(folder_path): - """ - gets a folder path and determines if there are less than one file - of each type that is needed if not than saves the files in arrays - - Parameters - --------- - folder_path - a string that descibes the path to a folder - - Returns - ------- - idXML_files,mzML_files - array that contain the paths to the files as strings - """ - - mzML_files = [] - idXML_files= [] - fileslist = sorted(os.listdir(folder_path)) - for file in fileslist: - if file.endswith(".mzML"): - mzML_files.append(file) - - if file.endswith(".idXML"): - idXML_files.append(file) - - - if len(mzML_files) == 0 and len(idXML_files) == 0: - User_Warning = QMessageBox() - User_Warning.setIcon(QMessageBox.Information) - User_Warning.setText("No mzML and idXML files found. Pleas select a different folder.") - User_Warning.setWindowTitle("Information") - Information = User_Warning.exec_() - - - - if len(mzML_files) == 0 and len(idXML_files) != 0: - User_Warning = QMessageBox() - User_Warning.setIcon(QMessageBox.Information) - User_Warning.setText("No mzML files found. Pleas select a different folder.") - User_Warning.setWindowTitle("Information") - Information = User_Warning.exec_() - Files_Number_Handler.Dictionary_Change_Boolean('idXML') - - - if len(mzML_files) != 0 and len(idXML_files) == 0: - User_Warning = QMessageBox() - User_Warning.setIcon(QMessageBox.Information) - User_Warning.setText("No idXML files found. Pleas select a different folder.") - User_Warning.setWindowTitle("Information") - Information = User_Warning.exec_() - Files_Number_Handler.Dictionary_Change_Boolean('mzML') - - if len(mzML_files) != 0 and len(idXML_files) != 0: - Files_Number_Handler.Dictionary_Change_Boolean('mzML') - Files_Number_Handler.Dictionary_Change_Boolean('idXML') - - - - return idXML_files,mzML_files - - #checks if array contains only on element, it is important because if - #more than 1 file exists user needs to select the file he wants to use - - def Check_If_More_Than_One(arraytotest): - """ - checks if there is more than one element in a given array - - Parameters - --------- - arraytotest - an array containing file paths as elements - - Returns - ------- - True or False - """ - return len(arraytotest) > 1 - - def Check_If_Less_Than_One(arraytotest): - """ - checks if there is less than one element in a given array - - Parameters - --------- - arraytotest - an array containing file paths as elements - - Returns - ------- - True or False - """ - return len(arraytotest) == 0 - - def Check_If_One(arraytotest) : - """ - cheks if there is exactly one element in a given array - - Parameters - --------- - arraytotest - an array containing file paths as elements - - Returns - ------- - True or False - """ - return len(arraytotest) == 1 - - - #used to save paths of files when loaded manually from a tab widget - - def Dictionary_Change_File(file_type,file_path): - """ - changes the value of a given file type in the global dictionary - - Parameters - --------- - file_type - a string that descibes a spesific file type - - Returns - ------- - none - """ - files_dictionary[file_type] = file_path - - #used to return the values of the dictionary - - def Dictionary_Return_Value(file_type): - """ - gives the value of the key that mathces the file type in the dictionary - - Parameters - --------- - file_type - a string that descibes a spesific file type - - Returns - ------- - files_dictionary[file_type] - a string that descibes a path to a spesific file type - """ - return files_dictionary[file_type] - - #changes the value to true if file loaded, - #should be used whenever loading data - #in to the dictionary manually - - def Dictionary_Change_Boolean(file_type): - """ - changes the bollean value of the key that mathces the given file type - - Parameters - --------- - file_type - a string that descibes a spesific file type - - Returns - ------- - none - """ - booleans_dictionary[file_type] = True - - #returns the boolean for the file - - def Dictionary_Return_Boolean(file_type): - """ - returns the value of the key that matches the given file type - - Parameters - --------- - file_type - a string that descibes a spesific file type - - Returns - ------- - booleans_dictionary[file_type] - a boolean that shows if a spesific file type has been loaded - """ - return booleans_dictionary[file_type] diff --git a/src/view/GUI_FastaViewer.py b/src/view/GUI_FastaViewer.py deleted file mode 100644 index a2b4a7771..000000000 --- a/src/view/GUI_FastaViewer.py +++ /dev/null @@ -1,817 +0,0 @@ -import sys -from PyQt5 import QtWidgets -from PyQt5.QtWidgets import (QWidget, QToolTip, - QPushButton, QApplication, QMainWindow, - QAction, qApp, - QHBoxLayout, QVBoxLayout, QMessageBox, - QLineEdit, QTableWidget, QTableWidgetItem, - QGridLayout, QScrollArea, QPlainTextEdit, - QDesktopWidget, QLabel, QRadioButton, - QGroupBox, QSizePolicy, QCheckBox, QFileDialog, - QTextEdit, QTextBrowser, QInputDialog) -from PyQt5.QtGui import QFont, QColor, QTextCharFormat, QTextCursor -from PyQt5.QtCore import Qt, QUrl -# from dictionaries import Dict -sys.path.insert(0, '../examples') -from LoadFasta_FastaViewer import LoadFasta_FastaViewer # NOQA: E402 -from FilesNumberHandler import Files_Number_Handler - - - -class GUI_FastaViewer(QMainWindow): - """ - A class used to make and change the appearance of the FastaViewer. - It enables to load a fasta file of a colletion of protein sequences - and search for proteins by its accesion number, name or subsequence. - - - ... - - Attributes - ---------- - searchButtonP : QtWidgets.QPushButton - a button to be shown on window and exc action on click - - searchButtonP : QtWidgets.QPushButton - Button to be shown on window and exc action on click - - boxPro : QLineEdit(self) - Textfield in wich the user can type inputs - - tw : QtWidgets.QTreeWidget - Treewidget used to hold and show data on the sceen - - mainwidget : QWidget - QWidget that contains all the Widgets - - main_layout : QVBoxLayout - The main Layout of the Window, contains all other Layouts - - set1,set2,set3 : QHBoxLayout() - Horizontal Layouts that hold different Widgets - - radioname,radioid,radioseq=QRadioButton - A QRadioButton that appears on sceen an can be cheked - - - color : QColor - Red Color for the searched seq - - colorblack : QColor - Black color for the found Protein sequence - fileloaded : int - Integer to check if file has been loaded - Methods - ------- - _init_(self) - Sets Window size na exc. initUI() - - initUi(self) - Creates the User Interface - - center(self) - Centers the Window on screen - - cutstring(self,oldstring,proteinseq) - Cuts the oldstring when proteinseq appears and returns a list of - the cuts - - loadFile(self) - A function for the loadbutton that open QFileDialog and saves the - Path to the file fo the logic class - - searchClicked(self) - A function for the searchButtonP, it checks if input is correct - and exc a search function - - radioIdSearch() - Searches for the Protein Information by ID - - - sequenceSearch() - Searches for the Protein Information by sequence and also changes - the color of the are of the sequence that is the same as the input - - nameSearch() - Searches for the Protein Information by name - - main() - runs the QApplication - """ - - def __init__(self): - """Gets self and sets Window size na exc. initUI() - - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - nothing - """ - super().__init__() - self.resize(1280, 720) - self.initUI() - - def initUI(self): - """Gets self and creates the User Interface - - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - nothing - """ - - # creating Buttons - self.searchButtonP = QtWidgets.QPushButton(self) - self.searchButtonP.setText("Search") - self.searchButtonP.clicked.connect(self.searchClicked) - - self.loadbutton = QtWidgets.QPushButton(self) - self.loadbutton.setText("Load") - self.loadbutton.clicked.connect(self.loadPath) - - self.changeReversePattern = QtWidgets.QPushButton(self) - self.changeReversePattern.setText("Add Decoy Pattern") - self.changeReversePattern.clicked.connect(self.user_Dialog_ChangeReversePattern) - self.changeReversePattern.setFixedWidth(166) - - # creating testboxes for the buttons - self.boxPro = QLineEdit(self) - - # Creating treewidget for displaying the proteins - self.tw = QtWidgets.QTreeWidget() - self.tw.setHeaderLabels(["Accession", "Organism", "Protein Name"]) - self.tw.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - - # Layout - self.mainwidget = QWidget(self) - self.main_layout = QVBoxLayout(self.mainwidget) - - # every set contains all widgets on the level they should be in the UI - # in set1 there is a textbox and a button - self.set1 = QHBoxLayout() - self.set1.addWidget(self.boxPro) - self.set1.addWidget(self.searchButtonP) - self.set1.addWidget(self.loadbutton) - - # set 2 contains the radiobuttons and a label - self.set2 = QHBoxLayout() - self.radioname = QRadioButton("Name") - self.radioid = QRadioButton("ID") - self.radioseq = QRadioButton("Sequence") - self.radioname.setChecked(True) - self.decoycheck = QCheckBox("Decoy search", self) - self.datalabel = QLabel() - self.datalabel.setText("Fasta not loaded") - self.set2.addWidget(self.radioname) - self.set2.addWidget(self.radioid) - self.set2.addWidget(self.radioseq) - self.set2.addWidget(self.decoycheck) - self.set2.addStretch(1) - self.set2.addWidget(self.changeReversePattern) - - # set 3 contains the table and the result box - self.set3 = QHBoxLayout() - self.set3.addWidget(self.tw) - - # adding all QHBoxLayout to the main QVBoxLayout - self.main_layout.addLayout(self.set1) - self.main_layout.addLayout(self.set2) - self.main_layout.addLayout(self.set3) - self.main_layout.addWidget(self.datalabel) - - self.mainwidget.setLayout(self.main_layout) - self.setCentralWidget(self.mainwidget) - self.setWindowTitle('Protein Viewer') - - self.extra_pattern = '' - - # defining some colors to marked searched sequences - self.color = QColor(255, 0, 0) - self.colorblack = QColor(0, 0, 0) - self.center() - self.show() - # defining a counter var that checks if a file has been loaded - # if not than there should be an error Window when a search is done - - self.fileloaded = 0 - - # centering the widget - def center(self): - """Gets self and centers the Window - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - changes so that the Window appears on the center of the screen - """ - qr = self.frameGeometry() - cp = QDesktopWidget().availableGeometry().center() - qr.moveCenter(cp) - self.move(qr.topLeft()) - - # defining a help function to cut the sequence that is being search from the - # Protein sequence that has been found - - def cutstring(self, oldstring: str, proteinseq: str) -> str: - """Gets two strings and splits the first string in a list - on the playces where the second string is found. This helps - to change the color of the sequences later on - - Parameters - ---------- - oldstring : str - the enteire proteinseq as a string - proteinseq : str - is the searched seq and is used to split the entire seq in parts - - Returns - ------- - list - a list of strings to be later put together after recoloring - """ - cut = oldstring.split(proteinseq) - return cut - # defining the function for load button to get path of database - def clearFastaViewer(self): - self.tw.clear() - - def loadPath(self): - self.clearFastaViewer() - self.filename = QFileDialog.getOpenFileName() - # if 'cancel' was pressed - if self.filename[0] == '': - pass - else: - # saving the file path in the dictionary - Files_Number_Handler.Dictionary_Change_File("fasta", self.filename[0]) - Files_Number_Handler.Dictionary_Change_Boolean("fasta") - self.loadFile(self.filename[0]) - - - # creates a user dialog to change default reverse pattern - def user_Dialog_ChangeReversePattern(self): - text, okPressed = QInputDialog.getText(self, "Add reverse pattern", "Add:", QLineEdit.Normal, "") - if okPressed and text != '': - self.extra_pattern = text - - - def loadFile(self, fasta_path): - """Gets QMainWindow and opens a QFileDialog and loads path - - Parameters - ---------- - self : QMainWindow - the MainWindow of the class - - - Returns - ------- - nothing , it changes the QMainWindow so that the user can see that a file - has been loaded - """ - - self.fileloaded = 1 - - # loading the lists before searching in order to make the search faster - - self.dictKeyAccession, self.proteinList, self.proteinNameList, self.proteinOSList, self.dictKeyAccessionDECOY, self.proteinListDECOY, self.proteinNameListDECOY, self.proteinOSListDECOY = LoadFasta_FastaViewer.protein_dictionary( - fasta_path, self.extra_pattern) - self.datalabel.setText("Fasta loaded") - for i in range(len(self.dictKeyAccession)): - ID = list(self.dictKeyAccession.keys())[i] - Proteinname = self.proteinNameList[i] - OS = self.proteinOSList[i] - self.cg = QtWidgets.QTreeWidgetItem(self.tw) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - self.tw.itemClicked.connect(self.clickTreeItem) - - # defining a function to creat TreeItems - - def createTreeItem(self, item, ID, Protein): - """Gets a TreeItem and creats two child Items, a Qlabel and a QTextEdit. - Firs Child Item holds a QTextEdit with the given Protein sequence. - Second Cild Item holds a QLabel with a hyperlink to the database UniProt. - - Parameters - ---------- - self : QMainWindow - the MainWindow of the class - item : QTreeWidgetItem - for which the child items will be created - ID : The specific protein accesion - which is needed for the link to the database - Protein : The specific protein sequence - that will be displayed in the QTextEdit - - - Returns - ------- - nothing , it changes the Treewidget - and creates two child items for the handed in tree item - """ - self.link = QLabel() - self.link.setTextInteractionFlags( - Qt.LinksAccessibleByMouse) - self.link.setOpenExternalLinks(True) - self.link.setTextFormat(Qt.RichText) - self.link.setText("" + "More Information"+" ") - self.textp = QTextEdit() - self.textp.resize( - self.textp.width(), self.textp.height()) - self.textp.insertPlainText( - "Proteinsequence: " + Protein + "\n") - self.textp.setReadOnly(True) - self.cgChild = QtWidgets.QTreeWidgetItem( - item) - self.cgChild2 = QtWidgets.QTreeWidgetItem( - item) - self.cgChild.setFirstColumnSpanned(True) - self.tw.setItemWidget( - self.cgChild, 0, self.textp) - self.tw.setItemWidget( - self.cgChild2, 0, self.link) - - # method when TreeItem was cklicked - - def clickTreeItem(self, item): - '''Gets a QTreeWidgetItem and its ID data of the first - collumn. The ID and the corresponding protein sequence are - handed to the createTreeItem method. - - Parameters - ---------- - self : QMainWindow - the MainWindow of the class - item : clicked QTreeWidgetItem - from which the ID is obtained - - Returns - ------- - nothing - ''' - num = item.childCount() - # prevents multiple creation of the same child tree items - if num == 0: - ID = item.data(0, 0) - index = list(self.dictKeyAccession.keys()).index(ID) - Protein = self.proteinList[index] - self.createTreeItem(item, ID, Protein) - - def clickTreeItemDecoy(self, item): - '''Does the same as clickTreeItem but - hands the corresponding DECOY protein sequence - to the create TreeItem method. - ''' - num = item.childCount() - if num == 0: - ID = item.data(0, 0) - index = list(self.dictKeyAccessionDECOY).index(ID) - Protein = self.proteinListDECOY[index] - self.createTreeItem(item, ID, Protein) - - def createTreeItemSeqSearch(self, item, ID, Protein): - """Gets a TreeItem and creats two child Items and a Qlabel. - Firs Child Item holds a QTextEdit with the given Protein sequence. - Second Cild Item holds a QLabel with a hyperlink to the database UniProt. - - Parameters - ---------- - self : QMainWindow - the MainWindow of the class - item : QTreeWidgetItem - for which the child items will be created - ID : The specific protein accesion - which is needed for the link to the database - Protein : A QTextEdit widget with the specific portein sequence - - - Returns - ------- - nothing , it changes the Treewidget - and creates two child items for the handed in tree item - """ - self.link = QLabel() - self.link.setTextInteractionFlags( - Qt.LinksAccessibleByMouse) - self.link.setOpenExternalLinks(True) - self.link.setTextFormat(Qt.RichText) - self.link.setText("" + "More Information"+" ") - - self.cgChild = QtWidgets.QTreeWidgetItem( - item) - self.cgChild2 = QtWidgets.QTreeWidgetItem( - item) - self.cgChild.setFirstColumnSpanned(True) - self.tw.setItemWidget( - self.cgChild, 0, Protein) - self.tw.setItemWidget( - self.cgChild2, 0, self.link) - - def clickTreeItemSeqSearch(self, item): - '''Gets a QTreeWidgetItem and its ID data of the first - collumn. The ID and the corresponding QTextEdit widget with the - protein sequence are handed to the createTreeItem method. - - Parameters - ---------- - self : QMainWindow - the MainWindow of the class - item : clicked QTreeWidgetItem - from which the ID is obtained - - Returns - ------- - nothing - ''' - num = item.childCount() - if num == 0: - ID = item.data(0, 0) - Protein = self.SequencSearchDict.get(ID) - self.createTreeItemSeqSearch(item, ID, Protein) - - def clickTreeItemSeqSearchDecoy(self, item): - '''Does the same as clickTreeItemSeqSearch but - hands the corresponding DECOY protein sequence - to the create TreeItem method. - ''' - num = item.childCount() - if num == 0: - ID = item.data(0, 0) - Protein = self.SequencSearchDictDECOY.get(ID) - self.createTreeItemSeqSearch(item, ID, Protein) - - # defining the searchClicked method for the searchButtonP - - def searchClicked(self): - """Gets self and searches for Protein and shows the result - on QMainWindow - - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - nothing but changes the QMainWindow to show the Protein or an Error message - """ - if self.fileloaded == 0: - self.error = QMessageBox() - self.error.setIcon(QMessageBox.Information) - self.error.setText("Please load Data before searching") - self.error.setWindowTitle("Error") - c = self.error.exec_() - else: - self.tw.clear() - # check if inputbox is empty. if empty return error if not proceed - if self.boxPro.text() == "": - self.error = QMessageBox() - self.error.setIcon(QMessageBox.Information) - self.error.setText("Please enter input before searching") - self.error.setWindowTitle("Error") - a = self.error.exec_() - else: - if self.radioid.isChecked(): - self.radioIdSearch() - - if self.radioseq.isChecked(): - self.sequenceSearch() - - if self.radioname.isChecked(): - self.nameSearch() - # doc recommends enabling sorting after loading the tree with elements - self.tw.setSortingEnabled(True) - - def radioIdSearch(self): - """Gets self and searches for Protein based on ID - and shows the result on QMainWindow, also adds a hyperlink for - more Information - - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - nothing but changes the QMainWindow to show the Protein in treewidget - """ - atLeastOneProteinFound = False - protein_accession_maybe_sub_sequence = self.boxPro.text() - - if self.decoycheck.isChecked(): - for protein_accession in self.dictKeyAccessionDECOY: - if protein_accession_maybe_sub_sequence in protein_accession: - atLeastOneProteinFound = True - index = list(self.dictKeyAccessionDECOY).index( - protein_accession) - ID = list(self.dictKeyAccessionDECOY.keys())[ - index] - Proteinname = self.proteinNameListDECOY[index] - OS = self.proteinOSListDECOY[index] - self.cg = QtWidgets.QTreeWidgetItem(self.tw) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - - header = self.tw.header() - header.setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) - header.setStretchLastSection(True) - self.tw.itemClicked.disconnect() - self.tw.itemClicked.connect(self.clickTreeItemDecoy) - - else: - for protein_accession in self.dictKeyAccession: - if protein_accession_maybe_sub_sequence in protein_accession: - atLeastOneProteinFound = True - index = list(self.dictKeyAccession).index( - protein_accession) - ID = list(self.dictKeyAccession.keys())[index] - Proteinname = self.proteinNameList[index] - OS = self.proteinOSList[index] - self.dummy = ID - self.cg = QtWidgets.QTreeWidgetItem(self.tw) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - - header = self.tw.header() - header.setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) - header.setStretchLastSection(True) - self.tw.itemClicked.disconnect() - self.tw.itemClicked.connect(self.clickTreeItem) - - if not atLeastOneProteinFound: - self.msg = QMessageBox() - self.msg.setIcon(QMessageBox.Information) - self.msg.setText( - "No matching protein accession found in database.") - self.msg.setWindowTitle("Error") - x = self.msg.exec_() - - def sequenceSearch(self): - """Gets self and searches for Protein based on sequence - and shows the result on QMainWindow, also adds a hyperlink for - more Information - - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - nothing but changes the QMainWindow to show the Protein in treewidget - also changes the color of the parts of the sequence - that are being searched - """ - atLeastOneProteinFound = False - protein_sub_sequence = self.boxPro.text() - - # dictionaries with ID as key and corresponding QTextEdit with protein sequence as value - - self.SequencSearchDict = {} - self.SequencSearchDictDECOY = {} - - if self.decoycheck.isChecked(): - for protein_sequence in self.proteinListDECOY: - if protein_sub_sequence in protein_sequence: - atLeastOneProteinFound = True - index = self.proteinListDECOY.index( - protein_sequence) - ID = list(self.dictKeyAccessionDECOY.keys())[ - index] - Protein = self.proteinListDECOY[index] - Proteinname = self.proteinNameListDECOY[index] - OS = self.proteinOSListDECOY[index] - - self.cg = QtWidgets.QTreeWidgetItem(self.tw) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - - self.textp = QTextEdit() - self.textp.resize( - self.textp.width(), self.textp.height()) - cuts = self.cutstring( - Protein, protein_sub_sequence) - self.textp.insertPlainText( - "Proteinsequence: " - ) - - for i in range(len(cuts)): - # while we are at the beginning of the list - if (cuts[i] == '' and i == 0): - self.textp.setTextColor(self.color) - self.textp.insertPlainText( - protein_sub_sequence) - self.textp.setTextColor( - self.colorblack) - # if we are in the middle of the list and the sub_string appears more than once - elif (cuts[i] == ''): - self.textp.setTextColor(self.color) - self.textp.insertPlainText( - protein_sub_sequence) - self.textp.setTextColor( - self.colorblack) - else: - if (i == len(cuts) - 1): - self.textp.insertPlainText(cuts[i]) - else: - self.textp.insertPlainText(cuts[i]) - self.textp.setTextColor(self.color) - self.textp.insertPlainText( - protein_sub_sequence) - self.textp.setTextColor( - self.colorblack) - self.textp.insertPlainText("\n") - - self.textp.setReadOnly(True) - self.SequencSearchDictDECOY[ID] = self.textp - - header = self.tw.header() - header.setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) - header.setStretchLastSection(True) - self.tw.itemClicked.disconnect() - self.tw.itemClicked.connect(self.clickTreeItemSeqSearchDecoy) - - else: - for protein_sequence in self.proteinList: - if protein_sub_sequence in protein_sequence: - atLeastOneProteinFound = True - index = self.proteinList.index( - protein_sequence) - ID = list(self.dictKeyAccession.keys())[index] - Protein = self.proteinList[index] - Proteinname = self.proteinNameList[index] - OS = self.proteinOSList[index] - - self.cg = QtWidgets.QTreeWidgetItem(self.tw) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - - self.textp = QTextEdit() - self.textp.resize( - self.textp.width(), self.textp.height()) - cuts = self.cutstring( - Protein, protein_sub_sequence) - self.textp.insertPlainText( - "Proteinsequence: " - ) - - for i in range(len(cuts)): - # while we are at the beginning of the list - - if (cuts[i] == '' and i == 0): - self.textp.setTextColor(self.color) - self.textp.insertPlainText( - protein_sub_sequence) - self.textp.setTextColor( - self.colorblack) - - # while we are in the middle of the list - - elif (cuts[i] == ''): - self.textp.setTextColor(self.color) - self.textp.insertPlainText( - protein_sub_sequence) - self.textp.setTextColor( - self.colorblack) - else: - if (i == len(cuts) - 1): - self.textp.insertPlainText(cuts[i]) - else: - self.textp.insertPlainText(cuts[i]) - self.textp.setTextColor(self.color) - self.textp.insertPlainText( - protein_sub_sequence) - self.textp.setTextColor( - self.colorblack) - - self.textp.setReadOnly(True) - self.SequencSearchDict[ID] = self.textp - - header = self.tw.header() - header.setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) - header.setStretchLastSection(True) - self.tw.itemClicked.disconnect() - self.tw.itemClicked.connect(self.clickTreeItemSeqSearch) - - if not atLeastOneProteinFound: - self.msg = QMessageBox() - self.msg.setIcon(QMessageBox.Information) - self.msg.setText( - "No matching protein sequence found in database.") - self.msg.setWindowTitle("Error") - x = self.msg.exec_() - - def nameSearch(self): - """Gets self and searches for Protein based on Proteinname - and shows the result on QMainWindow, also adds a hyperlink for - more Information - - Parameters - ---------- - self : QMainWindow - - - Returns - ------- - nothing but changes the QMainWindow to show the Protein in treewidget - """ - atLeastOneProteinFound = False - protein_sub_name = self.boxPro.text() - - if self.decoycheck.isChecked(): - for protein_name in self.proteinNameListDECOY: - if protein_sub_name in protein_name: - atLeastOneProteinFound = True - index = self.proteinNameListDECOY.index( - protein_name) - ID = list(self.dictKeyAccessionDECOY.keys())[ - index] - Proteinname = protein_name - OS = self.proteinOSListDECOY[index] - - self.cg = QtWidgets.QTreeWidgetItem(self.tw) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - - header = self.tw.header() - header.setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) - header.setStretchLastSection(True) - self.tw.itemClicked.disconnect() - self.tw.itemClicked.connect(self.clickTreeItemDecoy) - - else: - for protein_name in self.proteinNameList: - if protein_sub_name in protein_name: - atLeastOneProteinFound = True - index = self.proteinNameList.index( - protein_name) - ID = list(self.dictKeyAccession.keys())[index] - Proteinname = self.proteinNameList[index] - OS = self.proteinOSList[index] - - self.cg = QtWidgets.QTreeWidgetItem(self.tw,) - self.cg.setData(0, 0, ID) - self.cg.setData(1, 0, OS) - self.cg.setData(2, 0, Proteinname) - - header = self.tw.header() - header.setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) - header.setStretchLastSection(True) - self.tw.itemClicked.disconnect() - self.tw.itemClicked.connect(self.clickTreeItem) - - if not atLeastOneProteinFound: - self.msg = QMessageBox() - self.msg.setIcon(QMessageBox.Information) - self.msg.setText( - "No matching protein name found in database.") - self.msg.setWindowTitle("Error") - x = self.msg.exec_() - - -def main(): - """Gets nothing, runs the QApplication - - Parameters - ---------- - nothing - - - Returns - ------- - nothing - """ - app = QApplication(sys.argv) - ex = GUI_FastaViewer() - sys.exit(app.exec_()) - - -if __name__ == '__main__': - main() diff --git a/src/view/MS1MapWidget.py b/src/view/MS1MapWidget.py deleted file mode 100644 index f97894dbf..000000000 --- a/src/view/MS1MapWidget.py +++ /dev/null @@ -1,65 +0,0 @@ -import numpy as np -import pyopenms -import pyqtgraph as pg -from pyqtgraph import PlotWidget - - -class MS1MapWidget(PlotWidget): - def __init__(self, parent=None, dpi=100): - PlotWidget.__init__(self) - self.setLabel("bottom", "m/z") - self.setLabel("left", "RT") - - def setSpectra(self, msexperiment): - msexperiment.updateRanges() - - # resolution: mz_res Da in m/z, rt_res seconds in RT dimension - mz_res = 1.0 - rt_res = 1.0 - - # size of image - cols = 1.0 / mz_res * msexperiment.getMaxMZ() - rows = 1.0 / rt_res * msexperiment.getMaxRT() - - # create regular spaced data to turn spectra into an image - """se_comment: max_intensity was never used""" - """max_intensity = msexperiment.getMaxInt()""" - bilip = pyopenms.BilinearInterpolation() - tmp = bilip.getData() - tmp.resize(int(rows), int(cols), float()) - bilip.setData(tmp) - - bilip.setMapping_0(0.0, 0.0, rows - 1, msexperiment.getMaxRT()) - bilip.setMapping_1(0.0, 0.0, cols - 1, msexperiment.getMaxMZ()) - - img = pg.ImageItem(autoDownsample=True) - self.addItem(img) - - for spec in msexperiment: - if spec.getMSLevel() == 1: - mzs, ints = spec.get_peaks() - rt = spec.getRT() - for i in range(0, len(mzs)): - bilip.addValue(rt, mzs[i], ints[i]) # slow - - data = np.ndarray(shape=(int(cols), int(rows)), dtype=np.float64) - grid_data = bilip.getData() - for i in range(int(rows)): - for j in range(int(cols)): - data[j][i] = grid_data.getValue(i, j) # slow - - # Set a custom color map - pos = np.array([0.0, 0.01, 0.05, 0.1, 1.0]) - color = np.array( - [ - (255, 255, 255, 0), - (255, 255, 0, 255), - (255, 0, 0, 255), - (0, 0, 255, 255), - (0, 0, 0, 255), - ], - dtype=np.ubyte, - ) - cmap = pg.ColorMap(pos, color) - img.setLookupTable(cmap.getLookupTable(0.0, 1.0, 256)) - img.setImage(data) diff --git a/src/view/ScanBrowserWidget.py b/src/view/ScanBrowserWidget.py deleted file mode 100644 index 7ea3f2b64..000000000 --- a/src/view/ScanBrowserWidget.py +++ /dev/null @@ -1,52 +0,0 @@ -from PyQt5.QtCore import ( - Qt, -) -from PyQt5.QtWidgets import ( - QHBoxLayout, - QWidget, - QSplitter, -) -from ScanTableWidget import ScanTableWidget -from SpectrumWidget import SpectrumWidget - - -class ScanBrowserWidget(QWidget): - def __init__(self, *args, **kwargs): - QWidget.__init__(self, *args, **kwargs) - self.mainlayout = QHBoxLayout(self) - self.isAnnoOn = False - - def clearLayout(self, layout): - for i in reversed(range(layout.count())): - layout.itemAt(i).widget().setParent(None) - - # def loadFile(self, file_path): - def loadMSExperiment(self, exp): - self.isAnnoOn = False - self.msexperimentWidget = QSplitter(Qt.Vertical) - - # data processing - # scans = self.readMS(file_path) - scans = exp - - # set Widgets - self.spectrum_widget = SpectrumWidget() - self.scan_widget = ScanTableWidget(scans) - self.scan_widget.scanClicked.connect(self.redrawPlot) - self.msexperimentWidget.addWidget(self.spectrum_widget) - self.msexperimentWidget.addWidget(self.scan_widget) - self.mainlayout.addWidget(self.msexperimentWidget) - - # default : first row selected. - self.scan_widget.table_view.selectRow(0) - - def redrawPlot(self): - # set new spectrum and redraw - self.spectrum_widget.setSpectrum(self.scan_widget.curr_spec) - if self.isAnnoOn: # update annotation list - self.updateController() - self.spectrum_widget.redrawPlot() - - def updateController(self): - # for overrriding - return diff --git a/src/view/ScanTableWidget.py b/src/view/ScanTableWidget.py deleted file mode 100644 index 33a5bee18..000000000 --- a/src/view/ScanTableWidget.py +++ /dev/null @@ -1,270 +0,0 @@ -from PyQt5.QtCore import ( - Qt, - QAbstractTableModel, - pyqtSignal, - QItemSelectionModel, - QSortFilterProxyModel, - QSignalMapper, - QPoint, - QRegExp, - QModelIndex, -) -from PyQt5.QtGui import QPen, QPainter -from PyQt5.QtWidgets import ( - QVBoxLayout, - QWidget, - QAction, - QTableView, - QMenu, - QAbstractItemView, - QItemDelegate, -) - - -class RTUnitDelegate(QItemDelegate): - """ - Displays the minute values of the RT besides the RT values - given in seconds. Through the delegate the RT values in - the table are not changed, only difference is the display - of the data in table_view. s - """ - - def __init__(self, parent, *args): - super(RTUnitDelegate, self).__init__(parent, *args) - - def paint(self, painter, option, index): - painter.save() - painter.setPen(QPen(Qt.black)) - if index.isValid(): - rt_min = str(round(index.siblingAtColumn(2).data() * 1.0 / 60, 3)) - rt_sec = str(round(index.siblingAtColumn(2).data(), 3)) - text = " " + rt_sec + "\t [" + rt_min + " Min" + "]" - - painter.setRenderHint(QPainter.Antialiasing) - # adjust text into cell - cell = option.rect - cell.adjust(0, 5, 0, 5) - painter.drawText(cell, Qt.AlignLeft, text) - - painter.restore() - - -class ScanTableWidget(QWidget): - """ - Used for displaying information in a table. - - =============================== ========================================= - **Signals:** - sigScanClicked Emitted when the user has clicked - on a row of the table and returns the - current index. This index contains - information about the current rows column - data. - - =============================== ========================================= - """ - - sigScanClicked = pyqtSignal(QModelIndex, name="scanClicked") - - header = [ - "MS level", - "Index", - "RT (min)", - "precursor m/z", - "charge", - "ID", - "PeptideSeq", - "PeptideIons", - ] - - def __init__(self, ms_experiment, *args): - QWidget.__init__(self, *args) - self.ms_experiment = ms_experiment - - self.table_model = ScanTableModel( - self, self.ms_experiment, self.header) - self.table_view = QTableView() - - # register a proxy class for filering and sorting the scan table - self.proxy = QSortFilterProxyModel(self) - self.proxy.setSourceModel(self.table_model) - - self.table_view.sortByColumn(1, Qt.AscendingOrder) - - # setup selection model - self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) - self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) - self.table_view.setModel(self.proxy) - self.table_view.setSelectionModel(QItemSelectionModel(self.proxy)) - - # header - self.horizontalHeader = self.table_view.horizontalHeader() - self.horizontalHeader.sectionClicked.connect(self.onHeaderClicked) - - # enable sorting - self.table_view.setSortingEnabled(True) - - # connect signals to slots - self.table_view.selectionModel().currentChanged.connect( - self.onCurrentChanged - ) # keyboard moves to new row - self.horizontalHeader.sectionClicked.connect(self.onHeaderClicked) - - layout = QVBoxLayout(self) - layout.addWidget(self.table_view) - self.setLayout(layout) - - # hide column 7 with the PepIon data, intern information usage - self.table_view.setColumnHidden(7, True) - - # Add rt in minutes for better TIC interaction - self.table_view.setItemDelegateForColumn(2, RTUnitDelegate(self)) - self.table_view.setColumnWidth(2, 160) - - # default : first row selected. in OpenMSWidgets - - def onRowSelected(self, index): - """se_comment: hard-refactoring to comply to pep8""" - if index.siblingAtColumn(1).data() is None: - return # prevents crash if row gets filtered out - self.curr_spec = self.ms_experiment.getSpectrum( - index.siblingAtColumn(1).data()) - self.scanClicked.emit(index) - - def onCurrentChanged(self, new_index, old_index): - self.onRowSelected(new_index) - - def onHeaderClicked(self, logicalIndex): - if logicalIndex != 0: - return # allow filter on first column only for now - - self.logicalIndex = logicalIndex - self.menuValues = QMenu(self) - self.signalMapper = QSignalMapper(self) - - # get unique values from (unfiltered) model - valuesUnique = set( - [ - self.table_model.index(row, self.logicalIndex).data() - for row in range( - self.table_model.rowCount( - self.table_model.index(-1, self.logicalIndex) - ) - ) - ] - ) - - if len(valuesUnique) == 1: - return # no need to select anything - - actionAll = QAction("Show All", self) - actionAll.triggered.connect(self.onShowAllRows) - self.menuValues.addAction(actionAll) - self.menuValues.addSeparator() - - """se_comment: hard-refactoring to comply to pep8""" - l: enumerate = enumerate(sorted(list(set(valuesUnique)))) - for actionNumber, actionName in l: - action = QAction(actionName, self) - self.signalMapper.setMapping(action, actionNumber) - action.triggered.connect(self.signalMapper.map) - self.menuValues.addAction(action) - - self.signalMapper.mapped.connect(self.onSignalMapper) - - # get screen position of table header and open menu - headerPos = self.table_view.mapToGlobal(self.horizontalHeader.pos()) - posY = headerPos.y() + self.horizontalHeader.height() - posX = headerPos.x() + \ - self.horizontalHeader.sectionPosition(self.logicalIndex) - self.menuValues.exec_(QPoint(posX, posY)) - - def onShowAllRows(self): - filterColumn = self.logicalIndex - filterString = QRegExp("", Qt.CaseInsensitive, QRegExp.RegExp) - - self.proxy.setFilterRegExp(filterString) - self.proxy.setFilterKeyColumn(filterColumn) - - def onSignalMapper(self, i): - stringAction = self.signalMapper.mapping(i).text() - filterColumn = self.logicalIndex - filterString = QRegExp( - stringAction, Qt.CaseSensitive, QRegExp.FixedString) - - self.proxy.setFilterRegExp(filterString) - self.proxy.setFilterKeyColumn(filterColumn) - - -class ScanTableModel(QAbstractTableModel): - """ - TODO: directly read model data from MSExperiment to remove copies - """ - - def __init__(self, parent, ms_experiment, header, *args): - QAbstractTableModel.__init__(self, parent, *args) - self.header = header - - # create array with MSSpectrum - self.scanRows = self.getScanListAsArray( - ms_experiment) # data type: list - - def getScanListAsArray(self, ms_experiment): - scanArr = [] - for index, spec in enumerate(ms_experiment.getMetaData()): - MSlevel = "MS" + str(spec.getMSLevel()) - RT = spec.getRT() - prec_mz = "-" - charge = "-" - native_id = spec.getNativeID() - if len(spec.getPrecursors()) == 1: - prec_mz = spec.getPrecursors()[0].getMZ() - charge = spec.getPrecursors()[0].getCharge() - PeptideSeq = "-" - PeptideIons = "-" - - scanArr.append( - [ - MSlevel, - index, - RT, - prec_mz, - charge, - native_id, - PeptideSeq, - PeptideIons, - ] - ) - return scanArr - - def headerData(self, col, orientation, role): - if orientation == Qt.Horizontal and role == Qt.DisplayRole: - return self.header[col] - return None - - def rowCount(self, parent): - return len(self.scanRows) - - def columnCount(self, parent): - return len(self.header) - - def setData(self, index, value, role): - if index.isValid() and role == Qt.DisplayRole: - self.scanRows[index.row()][index.column()] = value - self.dataChanged.emit(index, index, {Qt.DisplayRole, Qt.EditRole}) - return value - return None - - def flags(self, index): - if not index.isValid(): - return None - return Qt.ItemIsEnabled | Qt.ItemIsSelectable - - def data(self, index, role): - if not index.isValid(): - return None - value = self.scanRows[index.row()][index.column()] - if role == Qt.EditRole: - return value - elif role == Qt.DisplayRole: - return value diff --git a/src/view/SequenceIonsWidget.py b/src/view/SequenceIonsWidget.py deleted file mode 100644 index bcd887fdc..000000000 --- a/src/view/SequenceIonsWidget.py +++ /dev/null @@ -1,362 +0,0 @@ -from PyQt5.QtCore import Qt, QPointF -from PyQt5.QtGui import ( - QFont, - QFontMetricsF, - QPainter, - QColor, - QPen, - QBrush, - QSpacerItem, - QSizePolicy, -) -from PyQt5.QtWidgets import QWidget, QHBoxLayout - - -class SequenceIonsWidget(QWidget): - """ - Used for creates a window for a peptide sequence with its given ions, - which is adjusted to the sequence size. - To avoid contortions of the window, spaceritems are added. - - """ - - HEIGHT = 0 - WIDTH = 0 - SUFFIX_HEIGHT = 0 - - def __init__(self, *args): - QWidget.__init__(self, *args) - - self.initUI() - - def initUI(self): - self.mainlayout = QHBoxLayout(self) - self.mainlayout.setContentsMargins(0, 0, 0, 0) - self.container = QWidget(self) - self.container.setStyleSheet("background-color:white;") - - self.setWindowTitle("SequenceIons Viewer") - self.seqIons_layout = QHBoxLayout(self.container) - - # change default setting of 11 px - self.seqIons_layout.setContentsMargins(0, 0, 0, 0) - self._pep = observed_peptide() - # resize window to fit peptide size - self.resize() - - self._pep.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) - self._pep.setMinimumSize( - SequenceIonsWidget.WIDTH, SequenceIonsWidget.HEIGHT) - self.seqIons_layout.addItem( - QSpacerItem( - 40, - SequenceIonsWidget.HEIGHT, - QSizePolicy.MinimumExpanding, - QSizePolicy.Minimum, - ) - ) - self.seqIons_layout.addWidget(self._pep) - self.seqIons_layout.addItem( - QSpacerItem( - 40, - SequenceIonsWidget.HEIGHT, - QSizePolicy.MinimumExpanding, - QSizePolicy.Minimum, - ) - ) - - self.setFixedHeight(SequenceIonsWidget.HEIGHT) - self.mainlayout.addWidget(self.container) - self.show() - - def resize(self): - """ - The integer 8 represents the additional space needed - for the in addition drawn lines. The 18 represents the - monospace width. - - """ - if len(self._pep.sequence) == 0: - SequenceIonsWidget.WIDTH = 0 - else: - SequenceIonsWidget.WIDTH = \ - (len(self._pep.sequence) * 18) + \ - (len(self._pep.sequence) - 1) * 8 - self.calculateHeight() - - def calculateHeight(self): - """ - Calculate window height adjusting to sequence height - with possible ions and set default setting in case of no - ions. - - """ - prefix = self._pep.prefix - suffix = self._pep.suffix - - if suffix == {}: - max_ion_suff = 1 - else: - max_ion_suff = len( - suffix[max(suffix, key=lambda key: len(suffix[key]))]) - - if prefix == {}: - max_ion_pre = 1 - else: - max_ion_pre = len( - prefix[max(prefix, key=lambda key: len(prefix[key]))]) - - metrics_pep = QFontMetricsF(self._pep.getFont_Pep()) - height_pep = metrics_pep.height() - - metrics_ion = QFontMetricsF(self._pep.getFont_Ion()) - height_ion = metrics_ion.height() - - # window height calculated with the sum of max prefix and suffix height - height_ion_pre = height_ion * max_ion_pre + 15 - SequenceIonsWidget.SUFFIX_HEIGHT = height_ion * max_ion_suff + 5 - SequenceIonsWidget.HEIGHT = \ - ( - height_pep + height_ion_pre + SequenceIonsWidget.SUFFIX_HEIGHT - ) - - def setPeptide(self, seq): - self._pep.setSequence(seq) - self.updateWindow() - - def setSuffix(self, suff): - self._pep.setSuffix(suff) - self.updateWindow() - - def setPrefix(self, pre): - self._pep.setPrefix(pre) - self.updateWindow() - - def updateWindow(self): - self.resize() - self._pep.setMinimumSize( - SequenceIonsWidget.WIDTH, SequenceIonsWidget.HEIGHT) - self.setFixedHeight(SequenceIonsWidget.HEIGHT) - self.update() - - def clear(self): - self._pep.sequence = "" - self._pep.suffix = {} - self._pep.prefix = {} - self.update() - - -class observed_peptide(QWidget): - """ - Used for creates a peptide sequence with its given ions. - The ions can be stacked above each other, e.g. in case for - a1, b1. Each amino letter is also separated by a line - and prefixes are colored blue, otherwise suffixes are colored - red. - - """ - - def __init__(self): - QWidget.__init__(self) - self.initUI() - - def initUI(self): - self.sequence = "" - self.suffix = {} - self.prefix = {} - self.colors = { - "black": QColor(0, 0, 0), - "red": QColor(255, 0, 0), - "blue": QColor(0, 0, 255), - } - - def setSequence(self, seq): - self.sequence = seq - - def setSuffix(self, lst): - self.suffix = lst - - def setPrefix(self, lst): - self.prefix = lst - - def paintEvent(self, event): - qp = QPainter() - qp.begin(self) - qp.setRenderHint(QPainter.Antialiasing) - qp.fillRect(event.rect(), QBrush(Qt.white)) # or changed to Qt.white - self._drawPeptide(qp) - qp.end() - - def _drawPeptide(self, qp): - qp.setWindow(0, 0, SequenceIonsWidget.WIDTH, SequenceIonsWidget.HEIGHT) - qp.setPen(self.colors["black"]) - qp.setFont(self.getFont_Pep()) - self._fragmentPeptide(qp) - - def _fragmentPeptide(self, qp): - """ - In this function the sequence will be created stepwise. - Each char of the sequence is drawn separately to add the - lines between and the ions. - - 1. Check if sequence is given, if so - then transform seq into a dictionary - (with the indices representing the positions of the chars). - 2. For each char in the sequence: - First, calculate start position of char - (be aware that the char rect is created - at the left bottom corner of - the char, meaning we have to add the height of the Font - & possible suffixes to the starting height position - to move it into the center of the window). - - Secound, calculate the center point for the vertical Line. - The Line consists of a point start and point end. - The starting line xPos yield in the - start_point + blank - (SPACE/2), - where blank represents the additional - space from the starting point after each new char. - - Third, if prefix or suffix ions are given, - then distinguish between suffix and prefix to draw the vertical - line with either left or right line or both. - - Because of changing the fonts for the ions, - the fonts needs to be reset. - - """ - SPACE = 8 - - if self.sequence != "": - - seq = list(self.sequence) - dict_seq = {i: seq[i] for i in range(0, len(seq))} - - metrics = QFontMetricsF(self.getFont_Pep()) - - blank = 0 - for i, s in dict_seq.items(): - i_rev = self._getReverseIndex(i, dict_seq) - - width = metrics.boundingRect(s).width() - height = metrics.boundingRect(s).height() - - start_point = 0 - - # position of char with center indent - position = QPointF( - start_point + blank, - SequenceIonsWidget.SUFFIX_HEIGHT + height - ) - qp.drawText(position, s) - - # position lines for possible ions - centerOfLine = \ - ( - SequenceIonsWidget.SUFFIX_HEIGHT + height - height / 4 - ) - 1 - - start_linePos = QPointF( - start_point + blank - (SPACE / 2), - centerOfLine - height / 2 - 2.5 - ) - end_linePos = QPointF( - start_linePos.x(), centerOfLine + height / 2 + 2.5 - ) - - qp.setFont(self.getFont_Ion()) - metrics_ion = QFontMetricsF(self.getFont_Ion()) - - if i in self.prefix: - left_linePos = self._drawIonsLines( - qp, start_linePos, end_linePos, SPACE, "prefix" - ) - self._drawPrefixIon(qp, i, metrics_ion, left_linePos) - - # for given line of existing prefix, expand with given suffix - if i in self.prefix and i_rev in self.suffix: - right_linePos = self._drawIonsLines( - qp, start_linePos, end_linePos, SPACE, "suffix" - ) - self._drawSuffixIon( - qp, i_rev, metrics_ion, end_linePos, right_linePos - ) - - elif i_rev in self.suffix and i not in self.prefix: - right_linePos = self._drawIonsLines( - qp, start_linePos, end_linePos, SPACE, "suffix" - ) - self._drawSuffixIon( - qp, i_rev, metrics_ion, start_linePos, right_linePos - ) - - blank += width + SPACE - qp.setPen(self._getPen(self.colors["black"])) - qp.setFont(self.getFont_Pep()) - - def _drawPrefixIon(self, qp, index, metrics_ion, pos_left): - qp.setPen(self._getPen(self.colors["blue"])) - prefix_ions = sorted(self.prefix[index]) - blank_ion = 10 - - for ion in prefix_ions: - height_ion = metrics_ion.boundingRect(ion).height() - pos_ion = QPointF(pos_left.x(), pos_left.y() + blank_ion) - qp.drawText(pos_ion, ion) - blank_ion += height_ion - - def _drawSuffixIon( - self, - qp, - index_reverse, - metrics_ion, - pos_end, - pos_right): - qp.setPen(self._getPen(self.colors["red"])) - suffix_ions = sorted(self.suffix[index_reverse], reverse=True) - blank_ion = 5 - - for ion in suffix_ions: - height_ion = metrics_ion.boundingRect(ion).height() - pos_ion = QPointF(pos_end.x() + 2.5, pos_right.y() - blank_ion) - qp.drawText(pos_ion, ion) - blank_ion += height_ion - - def _drawIonsLines(self, qp, pos_start, pos_end, SPACE, order): - qp.setPen(self._getPen(self.colors["black"])) - - if order == "prefix": - qp.drawLine(pos_start, pos_end) - pos_left = QPointF(pos_end.x() - 2 * SPACE, pos_end.y()) - qp.drawLine(pos_end, pos_left) - return pos_left - - if order == "suffix": - qp.drawLine(pos_start, pos_end) - pos_right = QPointF(pos_start.x() + 2 * SPACE, pos_start.y()) - qp.drawLine(pos_start, pos_right) - return pos_right - - def getFont_Pep(self): - font = QFont("Courier") - font.setStyleHint(QFont.TypeWriter) - font.setPixelSize(30) - return font - - def getFont_Ion(self): - font = QFont("Courier") - font.setStyleHint(QFont.TypeWriter) - font.setPixelSize(10) - return font - - def _getPen(self, color): - # style settings for the lines - pen = QPen(color, 0.75, Qt.SolidLine) - pen.setStyle(Qt.DashDotLine) - return pen - - def _getReverseIndex(self, i, dict_seq): - i_rev = 0 - if i != 0: - i_rev = list(dict_seq.keys())[-i] - return i_rev diff --git a/src/view/SpectrumWidget.py b/src/view/SpectrumWidget.py deleted file mode 100644 index 7dfac8bba..000000000 --- a/src/view/SpectrumWidget.py +++ /dev/null @@ -1,241 +0,0 @@ -from collections import namedtuple - -import numpy as np -import pyqtgraph as pg -from PyQt5.QtCore import Qt -from pyqtgraph import PlotWidget - -# structure for annotation (here for reference) -PeakAnnoStruct = namedtuple( - "PeakAnnoStruct", - "mz intensity text_label \ - symbol symbol_color", -) -LadderAnnoStruct = namedtuple( - "LadderAnnoStruct", - "mz_list \ - text_label_list color", -) - -pg.setConfigOption("background", "w") # white background -pg.setConfigOption("foreground", "k") # black peaks - - -class SpectrumWidget(PlotWidget): - def __init__(self, parent=None, dpi=100): - PlotWidget.__init__(self) - self.setLimits(yMin=0, xMin=0) - self.setMouseEnabled(y=False) - self.setLabel("bottom", "m/z") - self.setLabel("left", "intensity") - self.highlighted_peak_label = None - self.peak_annotations = None - self.ladder_annotations = None - # numpy arrays for fast look-up - self._mzs = np.array([]) - self._ints = np.array([]) - self.getViewBox().sigXRangeChanged.connect(self._autoscaleYAxis) - self.getViewBox().sigRangeChangedManually.connect( - self.redrawLadderAnnotations - ) # redraw anno - self.proxy = pg.SignalProxy( - self.scene().sigMouseMoved, rateLimit=60, slot=self._onMouseMoved - ) - - def setSpectrum( - self, spectrum, zoomToFullRange=False - ): # add a default value for displaying all peaks - self.plot(clear=True) - self.zoomToFullRange = zoomToFullRange # relevant in redrawPlot() - # delete old highlighte "hover" peak - """se_comment: changed != to is not""" - if self.highlighted_peak_label is not None: - self.removeItem(self.highlighted_peak_label) - self.highlighted_peak_label = None - self.spec = spectrum - self._mzs, self._ints = self.spec.get_peaks() - self._autoscaleYAxis() - # for annotation in ControllerWidget - self.minMZ = np.amin(self._mzs) - self.maxMZ = np.amax(self._mzs) - self.redrawPlot() - - def setPeakAnnotations(self, p_annotations): - self.peak_annotation_list = p_annotations - - def setLadderAnnotations(self, ladder_visible=[]): - self._ladder_visible = ladder_visible # LadderAnnoStruct - - def clearLadderAnnotation(self, ladder_key_to_clear): - try: - if ladder_key_to_clear in self._ladder_anno_lines.keys(): - self._clear_ladder_item(ladder_key_to_clear) - except (AttributeError, NameError): - return - - def redrawPlot(self): - self.plot(clear=True) - if self.zoomToFullRange: - self.setXRange(self.minMZ, self.maxMZ) - self._plot_spectrum() - self._clear_annotations() - self._plot_peak_annotations() - self._plot_ladder_annotations() - - def redrawLadderAnnotations(self): - self._plot_ladder_annotations() - - def _autoscaleYAxis(self): - x_range = self.getAxis("bottom").range - if x_range == [ - 0, - 1, - ]: # workaround for axis sometimes not being set - # TODO: check if this is resovled - x_range = [np.amin(self._mzs), np.amax(self._mzs)] - self.currMaxY = self._getMaxIntensityInRange(x_range) - if self.currMaxY: - self.setYRange(0, self.currMaxY, update=False) - - def _plot_peak_annotations(self): - try: - self.peak_annotation_list - except (AttributeError, NameError): - return - - if self.peak_annotation_list is not None: - for item in self.peak_annotation_list: # item : PeakAnnoStruct - self.plot( - [item.mz], - [item.intensity], - symbol=item.symbol, - symbolBrush=pg.mkBrush(item.symbol_color), - symbolSize=14, - ) - if item.text_label: - label = pg.TextItem( - text=item.text_label, color=item.symbol_color, anchor=( - 0.5, 1) - ) - self.addItem(label) - label.setPos(item.mz, item.intensity) - - def _getMaxIntensityInRange(self, xrange): - left = np.searchsorted(self._mzs, xrange[0], side="left") - right = np.searchsorted(self._mzs, xrange[1], side="right") - return np.amax(self._ints[left:right], initial=1) - - def _plot_spectrum(self): - bargraph = pg.BarGraphItem(x=self._mzs, height=self._ints, width=0) - self.addItem(bargraph) - - def _plot_ladder_annotations(self): - try: - self._ladder_visible - except (AttributeError, NameError): - return - try: - self.currMaxY - except (AttributeError, NameError): - self.currMaxY = self._getMaxIntensityInRange( - self.getAxis("bottom").range) - - xlimit = [self._mzs[0], self._mzs[-1]] - for ladder_key, lastruct in self._ladder_visible.items(): - if ladder_key in self._ladder_anno_lines.keys(): # update - self._ladder_anno_lines[ladder_key][0].setData( - [xlimit[0], xlimit[1]], [self.currMaxY, self.currMaxY] - ) # horizontal line - cntr = 0 - for x in lastruct.mz_list: - self._ladder_anno_lines[ladder_key][cntr + 1].setData( - [x, x], [0, self.currMaxY] - ) - self._ladder_anno_labels[ladder_key][cntr].setPos( - x, self.currMaxY - ) # horizon line doesn't have label - cntr += 1 - else: # plot - pen = pg.mkPen(lastruct.color, width=2, style=Qt.DotLine) - self._ladder_anno_lines[ladder_key] = [] - self._ladder_anno_labels[ladder_key] = [] - - self._ladder_anno_lines[ladder_key].append( - # horizon line. index 0 - self.plot( - [xlimit[0], xlimit[1]], [ - self.currMaxY, self.currMaxY], pen=pen - ) - ) - """se_comment: hard-refactor to comply to pep8""" - z = zip(lastruct.mz_list, lastruct.text_label_list) - for x, txt_label in z: - self._ladder_anno_lines[ladder_key].append( - self.plot([x, x], [0, self.currMaxY], pen=pen) - ) - label = pg.TextItem( - text=txt_label, color=lastruct.color, anchor=(1, -1) - ) - label.setPos(x, self.currMaxY) - label.setParentItem( - self._ladder_anno_lines[ladder_key][-1]) - self._ladder_anno_labels[ladder_key].append(label) - - def _clear_annotations(self): - self._ladder_visible = dict() - self._ladder_anno_lines = dict() - self._ladder_anno_labels = dict() - - def _clear_peak_annotations(self): - self.peak_annotation_list = None - - def _clear_ladder_item(self, key): - for anno in self._ladder_anno_lines[key]: - anno.clear() - for pos in self._ladder_anno_labels[key]: - pos.setPos(0, 0) - del self._ladder_anno_lines[key] - del self._ladder_anno_labels[key] - - def _onMouseMoved(self, evt): - pos = evt[0] # using signal proxy - # turns original arguments into a tuple - if self.sceneBoundingRect().contains(pos): - mouse_point = self.getViewBox().mapSceneToView(pos) - pixel_width = self.getViewBox().viewPixelSize()[0] - left = np.searchsorted( - self._mzs, mouse_point.x() - 4.0 * pixel_width, side="left" - ) - right = np.searchsorted( - self._mzs, mouse_point.x() + 4.0 * pixel_width, side="right" - ) - if left == right: # none found -> remove text - """se_comment: changed != to is not""" - if self.highlighted_peak_label is not None: - self.highlighted_peak_label.setText("") - return - # get point in range with minimum squared distance - dx = np.square(np.subtract(self._mzs[left:right], mouse_point.x())) - dy = np.square(np.subtract( - self._ints[left:right], mouse_point.y())) - idx_max_int_in_range = np.argmin(np.add(dx, dy)) - x = self._mzs[left + idx_max_int_in_range] - y = self._ints[left + idx_max_int_in_range] - """se_comment: changed == to is""" - if self.highlighted_peak_label is None: - self.highlighted_peak_label = pg.TextItem( - text="{0:.3f}".format(x), - color=(100, 100, 100), - anchor=(0.5, 1.5) - ) - self.addItem( - self.highlighted_peak_label, ignoreBounds=True - ) # ignore bounds to prevent rescaling of axis - # if the text item touches the border - self.highlighted_peak_label.setText("{0:.3f}".format(x)) - self.highlighted_peak_label.setPos(x, y) - else: - # mouse moved out of visible area: remove highlighting item - """se_comment: changed != to is not""" - if self.highlighted_peak_label is not None: - self.highlighted_peak_label.setText("") diff --git a/src/view/TICWidget.py b/src/view/TICWidget.py deleted file mode 100644 index 0e8e46f2d..000000000 --- a/src/view/TICWidget.py +++ /dev/null @@ -1,333 +0,0 @@ -import numpy as np -import pyqtgraph as pg -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtGui import QKeySequence -from PyQt5.QtWidgets import QShortcut -from pyqtgraph import PlotWidget - -pg.setConfigOption("background", "w") # white background -pg.setConfigOption("foreground", "k") # black peaks - - -class TICWidget(PlotWidget): - """ - Used for creating a TIC plot - with dynamic zooming to avoid label collisions. - - =============================== ========================================= - **Signals:** - sigRTClicked Emitted when the user has clicked on TIC - plot and returns the clicked RT value. - - sigSeleRTRegionChangeFinished Emitted while the user is double clicking - on a region in TIC plot and creates a - region by dragging a horizontal line. - The signal returns the start and end - RT values within the region. - =============================== ========================================= - """ - - sigRTClicked = pyqtSignal(float, name="sigRTClicked") - sigSeleRTRegionChangeFinished = pyqtSignal( - float, float, name="sigRTRegionChangeFinished" - ) - - def __init__(self, parent=None, dpi=100): - PlotWidget.__init__(self) - self.setLimits(yMin=0, xMin=0) - self.setMouseEnabled(y=False) - self.setLabel("bottom", "RT (min)") - self.setLabel("left", "relative intensity (%)") - self._peak_labels = {} - self._existTIC = True - # numpy arrays for fast look-up - self._rts = np.array([]) - self._ints = np.array([]) - self._peak_indices = np.array([]) - self._currentIntensitiesInRange = np.array([]) - self._region = None - self.getViewBox().sigXRangeChanged.connect(self._autoscaleYAxis) - - self.scene().sigMouseClicked.connect(self._clicked) # emits rt_clicked - - # shortcut to init region - self.shortcut1 = QShortcut(QKeySequence("Ctrl+r"), self) - self.shortcut1.activated.connect(self._rgn_shortcut) - - # in cases only MS2 spectra are given - def checkExistTIC(self): - if self._rts.size == 0: - self._existTIC = False - - def setTIC(self, chromatogram): - """ - Used to set new TIC and with given Information (rts, ints) - - :param chromatogram: data from the MSExperiment - """ - if self._peak_labels != {}: - self._clear_labels() - self._peak_labels = {} - self._chrom = chromatogram - self._rts, self._ints = self._chrom.get_peaks() - - self.checkExistTIC() - if self._existTIC: - self._rts_in_min() - self._relative_ints() - self._peak_indices = self._find_Peak() - self._autoscaleYAxis() - self.redrawPlot() - - def _rts_in_min(self): - self._rts = np.array([x / 60 for x in self._rts]) - - def _relative_ints(self): - maxInt = np.amax(self._ints) - self._ints = np.array([((x / maxInt) * 100) for x in self._ints]) - - def redrawPlot(self): - self.plot(clear=True) - self._plot_tic() - self._draw_peak_label() - - def _autoscaleYAxis(self): - """ - Used to adjust y axis with the maximal y value - from the current RT values. Also, redraws peak labels - depending on the current displayed RT values. - - """ - x_range = self.getAxis("bottom").range - if x_range == [0, 1]: # workaround for axis sometimes not being set - x_range = [np.amin(self._rts), np.amax(self._rts)] - self.currMaxY = self._getMaxIntensityInRange(x_range) - if self.currMaxY: - self.setYRange(0, self.currMaxY, update=False) - self._redrawLabels() - - def _getMaxIntensityInRange(self, xrange): - """ - :param xrange: A list of [min, max] bounding RT values. - :return: An float value representing the maximal - intensity current x range. - """ - left = np.searchsorted(self._rts, xrange[0], side="left") - right = np.searchsorted(self._rts, xrange[1], side="right") - self._currentIntensitiesInRange = self._ints[left:right] - - return np.amax(self._ints[left:right], initial=1) - - def _plot_tic(self): - plotgraph = pg.PlotDataItem(self._rts, self._ints) - self.addItem(plotgraph) - - def _find_Peak(self): - """ - Calculates all indices from the intensity values to locate peaks. - This function operates on the principle that it compares peak values - against each other until it founds a maximal turning point. - - :return: A numpy array containing all peak indices, - sorted descending (max first -> min last). - """ - data = self._ints - maxIndices = np.zeros_like(data) - peakValue = -np.inf - for indx in range(0, len(data), 1): - if peakValue < data[indx]: - peakValue = data[indx] - for j in range(indx, len(data)): - if peakValue < data[j]: - break - elif peakValue == data[j]: - continue - elif peakValue > data[j]: - peakIndex = indx + np.floor(abs(indx - j) / 2) - # marking found index - maxIndices[peakIndex.astype(int)] = 1 - indx = j - break - peakValue = data[indx] - maxIndices = np.where(maxIndices)[0] - - # sort indices of high points from largest intensity to smallest - maxIndices = sorted(maxIndices, key=lambda x: data[x], reverse=True) - - return maxIndices - - def _add_label(self, label_id, label_text, pos_x, pos_y): - label = pg.TextItem(anchor=(0.5, 1)) - label.setText(text="{0:.2f}".format(label_text), color=(0, 0, 0)) - label.setPos(pos_x, pos_y) - self._peak_labels[label_id] = {"label": label} - self.addItem(label, ignoreBounds=True) - - if self._label_clashes(label_id): - self._remove_label(label_id) - - def _remove_label(self, label_id): - self.removeItem(self._peak_labels[label_id]["label"]) - del self._peak_labels[label_id] - - def _clear_labels(self): - for label_id in self._peak_labels.keys(): - self.removeItem(self._peak_labels[label_id]["label"]) - self._peak_labels = {} - - def _label_clashes(self, label_id): - """ - Calculates possible clash of new added label to other existing labels. - The clash is measured by the - collision of the label boundingRects, - which are representing displayed scene positions. - - :param label_id: Represents index of peak position in peak_indices. - :return: A boolean indicating if there is a clash or not. - """ - new_label = label_id - clash = False - - # scaling the distance with the correct pixel size - pixel_width = self.getViewBox().viewPixelSize()[0] - limit_distance = 20.0 * pixel_width - - if self._peak_labels == {}: - return False - - for exist_label in list(self._peak_labels): - if exist_label != new_label: - new_label_rect =\ - self._peak_labels[new_label]["label"].mapRectToDevice( - self._peak_labels[new_label]["label"].boundingRect() - ) - exist_label_rect = self._peak_labels[exist_label][ - "label" - ].mapRectToDevice( - self._peak_labels[exist_label]["label"].boundingRect() - ) - - if not new_label_rect.intersects(exist_label_rect): - exist_label_X = self._peak_labels[exist_label]["label"].x() - new_label_X = self._peak_labels[new_label]["label"].x() - - distance = abs(new_label_X - exist_label_X) - - if distance < limit_distance: - clash = True - break - else: - clash = False - - elif new_label_rect.intersects(exist_label_rect): - clash = True - break - else: - if len(self._peak_labels) == 1 and exist_label == new_label: - clash = False - return clash - - def _draw_peak_label(self): - """ - Function draws peak labels, - starting with the maximal peak to the minimal peak. - In each addition possible label clashes will be calculated, - if so then delete label. - """ - if self._peak_labels == {}: - for index in self._peak_indices: - if self._ints[index] in self._currentIntensitiesInRange: - self._add_label( - index, self._rts[index], - self._rts[index], - self._ints[index] - ) - - def _redrawLabels(self): - self._clear_labels() - self._draw_peak_label() - - def _clicked(self, event): - if self._existTIC: - pos = event.scenePos() - if self.sceneBoundingRect().contains(pos): - mouse_point = self.getViewBox().mapSceneToView(pos) - closest_datapoint_idx = self._calculate_closest_datapoint( - mouse_point.x() - ) - self.sigRTClicked.emit( - self._rts[closest_datapoint_idx] - ) # notify observers - - # check the selected rt region and return the bounds - if self._region is not None: - self._region.sigRegionChangeFinished.connect( - self._rtRegionBounds) - - def mouseDoubleClickEvent(self, event): - super(TICWidget, self).mouseDoubleClickEvent(event) - try: - mouse_point = self.getViewBox().mapSceneToView(event.pos()) - closest_datapoint_idx = self._calculate_closest_datapoint( - mouse_point.x()) - rgn_start = self._rts[closest_datapoint_idx] - - if self._region is None: - region = pg.LinearRegionItem() - region.setRegion((rgn_start, rgn_start)) - self._region = region - self.addItem(region, ignoreBounds=True) - - # delete the region when hovering over the region per doubleClk - self._delete_region() - except ValueError: - print("No TIC values to click on") - - def _calculate_closest_datapoint(self, point_x): - """ - :param point_x: mouse clicked position - :return: closest data point near a peak - - """ - larger_idx = np.searchsorted(self._rts, point_x, side="left") - smaller_idx = 0 - if larger_idx >= self._rts.size: # to avoid array out of bounds - larger_idx -= 1 - if larger_idx > 0: - smaller_idx = larger_idx - 1 - if abs(self._rts[larger_idx] - point_x) < \ - abs(self._rts[smaller_idx] - point_x): - closest_datapoint_idx = larger_idx - - else: - closest_datapoint_idx = smaller_idx - - return closest_datapoint_idx - - def _rtRegionBounds(self): - region_bounds = self._region.getRegion() - start_rg = region_bounds[0] - - stop_rg_idx = self._calculate_closest_datapoint(region_bounds[1]) - stop_rg = self._rts[stop_rg_idx] - - # set the new region of interest - self._region.setRegion((start_rg, stop_rg)) - - self.sigSeleRTRegionChangeFinished.emit( - start_rg, stop_rg) # notify observers - - def _delete_region(self): - if self._region.mouseHovering: - self.removeItem(self._region) - self._region = None - - def _rgn_shortcut(self): - # click region, with following shortcut -> create region - rgn_start = self.getViewBox().mapSceneToView(self.lastMousePos) - - if self._region is None: - region = pg.LinearRegionItem() - region.setRegion((rgn_start, rgn_start)) - self._region = region - self.addItem(region, ignoreBounds=True) diff --git a/src/view/mzTabLoadWidget.py b/src/view/mzTabLoadWidget.py deleted file mode 100644 index 695673426..000000000 --- a/src/view/mzTabLoadWidget.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys -import webbrowser -from PyQt5 import QtGui, QtWidgets -from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QVBoxLayout, QTableWidgetItem, QPushButton, QFileDialog -from mzTabTableWidget import Window as mz - -class Window(QWidget): - def __init__(self): - super().__init__() - - self.title = "mzTabTableWidget" - self.top = 100 - self.left = 100 - self.width = 500 - self.height = 500 - - self.vBox = QVBoxLayout() - - self.InitWindow() - - def InitWindow(self): - self.setWindowIcon(QtGui.QIcon("icon.png")) - self.setWindowTitle(self.title) - self.setGeometry(self.top, self.left, self.width, self.height) - - self.mzTabTableWidget = mz() - - self.loadButton = QtWidgets.QPushButton(self) - self.loadButton.setText("load") - self.loadButton.clicked.connect(self.loadFile) - - self.vBox.addWidget(self.loadButton) - self.vBox.addWidget(self.mzTabTableWidget) - - self.setLayout(self.vBox) - self.show() - - def loadFile(self): - self.filename = QFileDialog.getOpenFileName() - self.mzTabTableWidget.readFile(self.filename[0]) - -# the following is here to test the widget on its own, leave it commented if you want to use it elsewhere -""" -if __name__== '__main__': - app = QApplication(sys.argv) - ex = Window() - sys.exit(app.exec_()) -""" \ No newline at end of file diff --git a/src/view/mzTabTableWidget.py b/src/view/mzTabTableWidget.py deleted file mode 100644 index f6df06859..000000000 --- a/src/view/mzTabTableWidget.py +++ /dev/null @@ -1,316 +0,0 @@ -""" -mzTabTableWidget ----------------- -This script allows the user to transfer information about proteins and psms from a mzTab file into two tables, -one containing the proteins, the other one containing the psms. - -By clicking on a row, the tables get updated regarding their listed proteins or psms. -Once you choose a protein/psm, the table displays only those psms/proteins that are linked to one another. - -This tool is designed to accept mzTab files. It is required to save those files under '.../examples/data/' or -change the path within the InitWindow. -""" -import sys -import webbrowser -from PyQt5 import QtGui, QtWidgets -from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QVBoxLayout, QTableWidgetItem, QPushButton, QFileDialog - - -class Window(QWidget): - def __init__(self): - super().__init__() - - self.title = "mzTabTableWidget" - self.top = 100 - self.left = 100 - self.width = 500 - self.height = 500 - self.tableRows = 5 - - self.fileLoaded = False - - self.PRTFull = [] - self.PSMFull = [] - - self.PRTFiltered = [] - self.PSMFiltered = [] - - self.PRTColumn = [True] - self.PSMColumn = [True] - - self.selectedPRT = "" - self.selectedPSM = "" - - self.tablePRTFull = QTableWidget() - self.tablePSMFull = QTableWidget() - - self.tablePRTFiltered = QTableWidget() - self.tablePSMFiltered = QTableWidget() - - self.vBoxPRT = QVBoxLayout() - self.vBoxPSM = QVBoxLayout() - - self.outerVBox = QVBoxLayout() - - self.InitWindow() - - def InitWindow(self): - self.setWindowIcon(QtGui.QIcon("icon.png")) - self.setWindowTitle(self.title) - self.setGeometry(self.top, self.left, self.width, self.height) - - self.tablePRTFull.setHidden(True) - self.tablePSMFull.setHidden(True) - self.tablePRTFiltered.setHidden(True) - self.tablePSMFiltered.setHidden(True) - - self.tablePRTFull.itemClicked.connect(self.PRTClicked) - self.tablePRTFiltered.itemClicked.connect(self.PRTClicked) - self.tablePSMFull.itemClicked.connect(self.PSMClicked) - self.tablePSMFiltered.itemClicked.connect(self.PSMClicked) - - self.tablePRTFull.itemDoubleClicked.connect(self.browsePRT) - self.tablePRTFiltered.itemDoubleClicked.connect(self.browsePRT) - self.tablePSMFull.itemDoubleClicked.connect(self.browsePSM) - self.tablePSMFiltered.itemDoubleClicked.connect(self.browsePSM) - - self.vBoxPRT.addWidget(self.tablePRTFull) - self.vBoxPRT.addWidget(self.tablePRTFiltered) - - self.vBoxPSM.addWidget(self.tablePSMFull) - self.vBoxPSM.addWidget(self.tablePSMFiltered) - - self.outerVBox.addLayout(self.vBoxPRT) - self.outerVBox.addLayout(self.vBoxPSM) - - self.setLayout(self.outerVBox) - self.show() - - def readFile(self, file): - if self.fileLoaded: - self.tablePRTFull.clear() - self.tablePSMFull.clear() - self.tablePSMFiltered.clear() - self.tablePRTFiltered.clear() - - self.tablePRTFull.setRowCount(0) - self.tablePSMFull.setRowCount(0) - self.tablePSMFiltered.setRowCount(0) - self.tablePRTFiltered.setRowCount(0) - - self.PRTFull.clear() - self.PSMFull.clear() - - self.PRTFiltered.clear() - self.PSMFiltered.clear() - - self.PRTColumn.clear() - self.PSMColumn.clear() - - self.PRTColumn = [True] - self.PSMColumn = [True] - - self.parser(file) - - self.PRTColumn *= len(self.PRTFull[1]) - self.PSMColumn *= len(self.PSMFull[1]) - - self.initTables() - self.createTable(self.tablePRTFull, self.PRTFull) - self.createTable(self.tablePSMFull, self.PSMFull) - - self.hidePRTColumns() - self.hidePSMColumns() - - self.tablePRTFull.setHidden(False) - self.tablePSMFull.setHidden(False) - - self.fileLoaded = True - - - def parser(self, file): - """parses the given mzTab file and saves PRT and PSM information - Parameters - ---------- - file : str - The file path of the mzTab file - """ - - with open(file) as inp: - for line in inp: - if line.startswith("PRH"): - self.PRTFull.append(line.strip().split('\t')) - elif line.startswith("PRT") and not line.endswith("protein_details\n"): - self.PRTFull.append(line.strip().split('\t')) - elif line.startswith("PSH") or line.startswith("PSM"): - self.PSMFull.append(line.strip().split('\t')) - - for item in self.PRTFull: - item.pop(0) - - for item in self.PSMFull: - item.pop(0) - - - - def initTables(self): - """draws protein and psm tables with headers""" - - self.tablePRTFull.setRowCount(len(self.PRTFull)) - self.tablePRTFull.setColumnCount(len(self.PRTFull[0])) - self.tablePRTFull.setHorizontalHeaderLabels(self.PRTFull[0]) - - self.tablePSMFull.setRowCount(len(self.PSMFull)) - self.tablePSMFull.setColumnCount(len(self.PSMFull[0])) - self.tablePSMFull.setHorizontalHeaderLabels(self.PSMFull[0]) - - self.tablePRTFiltered.setRowCount(0) - self.tablePRTFiltered.setColumnCount(len(self.PRTFull[0])) - self.tablePRTFiltered.setHorizontalHeaderLabels(self.PRTFull[0]) - - self.tablePSMFiltered.setRowCount(0) - self.tablePSMFiltered.setColumnCount(len(self.PSMFull[0])) - self.tablePSMFiltered.setHorizontalHeaderLabels(self.PSMFull[0]) - - """removes now unnecessary header information from content lists """ - self.PRTFull.pop(0) - self.PSMFull.pop(0) - - def createTable(self, table, content): - """parameters: tableWidget to draw content in. Content to be drawn in list form""" - """Setting count to zero empties the table. Then table is (re-)filled with specified content""" - table.setRowCount(0) - table.setRowCount(len(content)) - - j = 0 - k = 0 - - for item in content[0:]: - while k < (len(content)): - while j < (len(item)): - table.setItem(k, j, QTableWidgetItem(item[j])) - j += 1 - else: - k += 1 - j = 0 - break - - self.tablePRTFull.resizeColumnsToContents() # resize columns - self.tablePSMFull.resizeColumnsToContents() # resize columns - self.tablePRTFiltered.resizeColumnsToContents() # resize columns - self.tablePSMFiltered.resizeColumnsToContents() # resize columns - - def hidePRTColumns(self): - """hides constant columns in PRT table by default by checking if every value equals""" - i = 0 - j = 0 - k = 0 - - while i < len(self.PRTFull) - 1: - while j < len(self.PRTFull[i]): - if self.PRTColumn[j]: - if self.PRTFull[i][j] != self.PRTFull[i + 1][j]: - self.PRTColumn[j] = False - j += 1 - i += 1 - - while k < len(self.PRTColumn): - if self.PRTColumn[k]: - self.tablePRTFull.setColumnHidden(k, True) - self.tablePRTFiltered.setColumnHidden(k, True) - k += 1 - - def hidePSMColumns(self): - """hides constant columns in PSM table by default by checking if every value equals""" - i = 0 - j = 0 - k = 0 - - while i < len(self.PSMFull) - 1: - while j < len(self.PSMFull[i]): - if self.PSMColumn[j]: - if self.PSMFull[i][j] != self.PSMFull[i + 1][j]: - self.PSMColumn[j] = False - j += 1 - i += 1 - - while k < len(self.PSMColumn): - if self.PSMColumn[k]: - self.tablePSMFull.setColumnHidden(k, True) - self.tablePSMFiltered.setColumnHidden(k, True) - k += 1 - - def PRTClicked(self, item): - - if self.tablePRTFull.isHidden(): - relevantContent = self.PRTFiltered - else: - relevantContent = self.PRTFull - - accession = relevantContent[item.row()][0] - - if self.selectedPSM == accession: - self.unfilterPSM() - else: - self.filterPSM(accession) - - def PSMClicked(self, item): - - if self.tablePSMFull.isHidden(): - relevantContent = self.PSMFiltered - else: - relevantContent = self.PSMFull - - accession = relevantContent[item.row()][2] - - if self.selectedPRT == accession: - self.unfilterPRT() - else: - self.filterPRT(accession) - - def filterPRT(self, accession): - self.tablePRTFiltered.setHidden(False) - self.tablePRTFull.setHidden(True) - - self.selectedPRT = accession - - self.PRTFiltered = [p for p in self.PRTFull if p[0] == self.selectedPRT] - self.createTable(self.tablePRTFiltered, self.PRTFiltered) - - def filterPSM(self, accession): - self.tablePSMFiltered.setHidden(False) - self.tablePSMFull.setHidden(True) - - self.selectedPSM = accession - - self.PSMFiltered = [p for p in self.PSMFull if p[2] == self.selectedPSM] - self.createTable(self.tablePSMFiltered, self.PSMFiltered) - - def unfilterPRT(self): - self.tablePRTFiltered.setHidden(True) - self.tablePRTFull.setHidden(False) - self.selectedPRT = "" - self.PRTFiltered = [] - - def unfilterPSM(self): - self.tablePSMFiltered.setHidden(True) - self.tablePSMFull.setHidden(False) - self.selectedPSM = "" - self.PSMFiltered = [] - - def browsePRT(self, item): - if self.tablePRTFull.isHidden(): - accession = self.PRTFiltered[item.row()][0].split("|", 2)[1] - else: - accession = self.PRTFull[item.row()][0].split("|", 2)[1] - - webbrowser.open("https://www.uniprot.org/uniprot/" + accession) - - def browsePSM(self, item): - if self.tablePSMFull.isHidden(): - accession = self.PSMFiltered[item.row()][2].split("|", 2)[1] - else: - accession = self.PSMFull[item.row()][2].split("|", 2)[1] - - webbrowser.open("https://www.uniprot.org/uniprot/" + accession) -