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)
-