Source code for xmipp3.viewers.viewer_validate_fscq

# -*- coding: utf-8 -*-
# **************************************************************************
# *
# * Authors:     Erney Ramirez Aportela (eramirez@cnb.csic.es)
# *
# * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************

import os
from tkinter import Tk, CENTER, Scrollbar, TOP, BOTH, Y

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors as mcolors
from pyworkflow.utils import getExt, removeExt
from os.path import abspath
from pwem.viewers import (LocalResolutionViewer, EmPlotter, ChimeraView,
                          DataView, ChimeraAttributeViewer)
from pyworkflow.gui import *
from pyworkflow.protocol.params import (LabelParam, EnumParam)
from pyworkflow.viewer import ProtocolViewer, DESKTOP_TKINTER
from xmipp3.protocols.protocol_validate_fscq import (XmippProtValFit, 
                                                     RESTA_FILE_MRC, 
                                                     OUTPUT_PDBMRC_FILE, 
                                                     PDB_VALUE_FILE,
                                                     RESTA_FILE_NORM,
                                                     PDB_NORM_FILE)


[docs]class XmippProtValFitViewer(LocalResolutionViewer, ChimeraAttributeViewer): """ Visualization tools for validation fsc-q. FSC-Q is a Xmipp package for evaluate the map-to-model fit """ _label = 'viewer validation_fsc-q' _targets = [XmippProtValFit] _environments = [DESKTOP_TKINTER] RESIDUE = 0 ATOM = 1 def __init__(self, *args, **kwargs): ProtocolViewer.__init__(self, *args, **kwargs) def _defineParams(self, form): self._env = os.environ.copy() form.addSection(label='FSC-Q results') group = form.addGroup('Chimera visualization') group.addParam('displayVolume', LabelParam, important=True, label='Display FSC-Q Volume Output') group.addParam('displayNormVolume', LabelParam, important=True, label='Display FSC-Qr Volume Output') group.addParam('displayPDB', EnumParam, choices=['by residue', 'by atom'], default=0, important=True, display=EnumParam.DISPLAY_COMBO, label='Display FSC-Q on PDB Output', help='FSC-Q projected on the atomic model') group.addParam('displayNormPDB', EnumParam, choices=['by residue', 'by atom'], default=0, important=True, display=EnumParam.DISPLAY_COMBO, label='Display FSC-Qr on PDB Output', help='FSC-Qr projected on the atomic model') group = form.addGroup('Statistics') group.addParam('calculateFscqNeg', LabelParam, important=True, label='Amino acids with possible overfitting', help='Amino acids that have atoms with FSC-Q < -1' ' are determined. It is suggested that these amino acids ' ' be re-checked in order to improve the fit.') group.addParam('calculateFscqPos', LabelParam, important=True, label='Amino acids with low resolvability', help='Amino acids that have atoms with FSC-Q > 1' ' are determined. It is suggested that these amino acids ' ' be re-checked in order to improve the fit.') super()._defineParams(form) from pwem.wizards.wizard import ColorScaleWizardBase group = form.addGroup('Color settings') ColorScaleWizardBase.defineColorScaleParams(group, defaultLowest=-3, defaultHighest=3, defaultIntervals=21, defaultColorMap='RdBu_r') def _getVisualizeDict(self): self.protocol._createFilenameTemplates() visualizeDict = {'displayVolume': self._visualize_vol, 'displayNormVolume': self._visualize_norm_vol, 'displayPDB': self._visualize_pdb, 'displayNormPDB': self._visualize_norm_pdb, 'calculateFscqNeg': self._statistics, 'calculateFscqPos': self._statistics} visualizeDict.update(ChimeraAttributeViewer._getVisualizeDict(self)) return visualizeDict def _create_legend(self, scale): fnCmd = self.protocol._getExtraPath("chimera_output.py") f = open(fnCmd, 'w') f.write("from chimerax.core.commands import run\n") f.write("from chimerax.graphics.windowsize import window_size\n") f.write("from PyQt5.QtGui import QFontMetrics\n") f.write("from PyQt5.QtGui import QFont\n") f.write("run(session, 'set bgColor white')\n") # get window size so we can place labels properly f.write("v = session.main_view\n") f.write("vx,vy=v.window_size\n") # Calculate heights and Y positions: font, scale height and firstY f.write('font = QFont("Arial", 12)\n') f.write('f = QFontMetrics(font)\n') f.write('_height = 1 * f.height()/vy\n') # Font height f.write('_half_scale_height = _height * 3.5\n') # Full height of the scale f.write("_firstY= 0.5 + _half_scale_height\n") # Y location for first label val = scale f.write('scale = %f \n' % val) f.write("run(session, '2dlabel text -%.2f bgColor red xpos 0.01 ypos %f size 12' % (scale, _firstY)) \n") f.write("run(session, '2dlabel text -%.2f bgColor orange xpos 0.01 ypos %f size 12' % (scale/1.5, _firstY-_height)) \n") f.write("run(session, '2dlabel text -%.2f bgColor gold xpos 0.01 ypos %f size 12' % (scale/3, _firstY-2*_height)) \n") f.write("run(session, '2dlabel text %05.2f bgColor yellow xpos 0.01 ypos %f size 12' % (00.00, _firstY-3*_height)) \n") f.write("run(session, '2dlabel text %05.2f bgColor lime xpos 0.01 ypos %f size 12' % (scale/3, _firstY-4*_height)) \n") f.write("run(session, '2dlabel text %05.2f bgColor cyan xpos 0.01 ypos %f size 12' % (scale/1.5, _firstY-5*_height)) \n") f.write("run(session, '2dlabel text %05.2f bgColor dodger blue xpos 0.01 ypos %f size 12' % (scale, _firstY-6*_height)) \n") def _visualize_vol(self, obj, **args): self._create_legend(3); fnCmd = self.protocol._getExtraPath("chimera_output.py") f = open(fnCmd, 'a') f.write("run(session, 'open %s')\n" % self.protocol._getFileName(OUTPUT_PDBMRC_FILE)) f.write("run(session, 'open %s')\n" % self.protocol._getFileName(RESTA_FILE_MRC)) f.write("run(session, 'volume #2 voxelSize %s step 1')\n" % self.protocol.inputVolume.get().getSamplingRate() ) f.write("run(session, 'volume #3 voxelSize %s')\n" % self.protocol.inputVolume.get().getSamplingRate() ) f.write("run(session, 'vol #3 hide')\n") f.write("run(session, 'color sample #2 map #3 palette" " -3.0,red:-2.0,orange:-1.0,gold:0,yellow:1.0,lime:2.0,cyan:3.0,#1e90ff')\n") f.close() view = ChimeraView(fnCmd) return [view] def _visualize_norm_vol(self, obj, **args): self._create_legend(1.5); fnCmd = self.protocol._getExtraPath("chimera_output.py") f = open(fnCmd, 'a') f.write("run(session, 'open %s')\n" % self.protocol._getFileName(OUTPUT_PDBMRC_FILE)) f.write("run(session, 'open %s')\n" % self.protocol._getFileName(RESTA_FILE_NORM)) f.write("run(session, 'volume #2 voxelSize %s step 1')\n" % self.protocol.inputVolume.get().getSamplingRate() ) f.write("run(session, 'volume #3 voxelSize %s')\n" % self.protocol.inputVolume.get().getSamplingRate() ) f.write("run(session, 'vol #3 hide')\n") f.write("run(session, 'color sample #2 map #3 palette" " -1.5,red:-1.0,orange:-0.5,gold:0,yellow:0.5,lime:1.0,cyan:1.5,#1e90ff')\n") f.close() view = ChimeraView(fnCmd) return [view] def _visualize_pdb(self, obj, **args): self._create_legend(3); fnCmd = self.protocol._getExtraPath("chimera_output.py") f = open(fnCmd, 'a') f.write("run(session, 'open %s')\n" % self.protocol._getFileName(PDB_VALUE_FILE)) if self.displayPDB == self.RESIDUE: f.write("run(session, 'cartoon')\n") f.write("run(session, 'hide target ab')\n") f.write("run(session, 'color byattribute occupancy palette" " -3.0,red:-2.0,orange:-1.0,gold:0,yellow:1.0,lime:2.0,cyan:3.0,#1e90ff" " ave residue')\n") else: f.write("run(session, 'cartoon hide')\n") f.write("run(session, 'show target ab')\n") f.write("run(session, 'style stick')\n") f.write("run(session, 'color byattribute occupancy palette" " -3.0,red:-2.0,orange:-1.0,gold:0,yellow:1.0,lime:2.0,cyan:3.0,#1e90ff')\n") f.close() view = ChimeraView(fnCmd) return [view] def _visualize_norm_pdb(self, obj, **args): self._create_legend(1.5); fnCmd = self.protocol._getExtraPath("chimera_output.py") f = open(fnCmd, 'a') f.write("run(session, 'open %s')\n" % self.protocol._getFileName(PDB_NORM_FILE)) if self.displayNormPDB == self.RESIDUE: f.write("run(session, 'cartoon')\n") f.write("run(session, 'hide target ab')\n") f.write("run(session, 'color byattribute occupancy palette" " -1.5,red:-1.0,orange:-0.5,gold:0,yellow:0.5,lime:1.0,cyan:1.5,#1e90ff" " ave residue')\n") else: f.write("run(session, 'cartoon hide')\n") f.write("run(session, 'show target ab')\n") f.write("run(session, 'style stick')\n") f.write("run(session, 'color byattribute occupancy palette" " -1.5,red:-1.0,orange:-0.5,gold:0,yellow:0.5,lime:1.0,cyan:1.5,#1e90ff')\n") f.close() view = ChimeraView(fnCmd) return [view] def _calculate_fscq(self, obj, **args): fnRoot = os.path.abspath(self.protocol._getExtraPath()) bool = 0 overfitting_list = [] poorfitting_list = [] with open(fnRoot + '/' + PDB_VALUE_FILE) as f: lines_data = f.readlines() for j, lin in enumerate(lines_data): if lin.startswith('ATOM') or lin.startswith('HETATM'): resnumber = int(lin[22:26]) if bool == 1 and resnumber == resnumber_ctl: resatomname = lin[12:16].strip() resname = lin[17:20].strip() chain = lin[21] fscq = float(lin[54:60]) current_frag.append(fscq) elif bool == 1 and resnumber != resnumber_ctl: meanFscq = np.mean(current_frag) current_frag.sort() if current_frag[0] <= -1: overfitting_list.append((resname, resnumber_ctl, chain, current_frag[0], meanFscq)) if current_frag[-1] >= 1: poorfitting_list.append((resname, resnumber_ctl, chain, current_frag[-1], meanFscq)) current_frag = [] resnumber_ctl = resnumber resatomname = lin[12:16].strip() resname = lin[17:20].strip() chain = lin[21] fscq = float(lin[54:60]) current_frag.append(fscq) else: bool = 1 current_frag = [] lines_aa = [] resnumber_ctl = resnumber resatomname = lin[12:16].strip() resname = lin[17:20].strip() chain = lin[21] fscq = float(lin[54:60]) current_frag.append(fscq) if obj == 'calculateFscqNeg': return overfitting_list else: return poorfitting_list def _statistics(self, obj, **args): mainFrame = Tk() statistics = Statistics('FSC-Q Statistics', mainFrame, statistics=self._calculate_fscq(obj, **args)) statistics.mainloop()
[docs]class Statistics(ttk.Frame): """ Windows to hold a plugin manager help """ def __init__(self, title, mainFrame, **kwargs): super().__init__(mainFrame) mainFrame.title(title) mainFrame.configure(width=1500, height=400) self.FrameTable = tk.PanedWindow(mainFrame, orient=tk.VERTICAL) self.FrameTable.pack(side=TOP, fill=BOTH, expand=Y) self.aminolist = kwargs['statistics'] self.fill_statistics() self.FrameTable.rowconfigure(0, weight=1) self.FrameTable.columnconfigure(0, weight=1) self.flag=False
[docs] def fill_statistics(self): self.addStatisticsTable(0, 0)
[docs] def addStatisticsTable(self, row, column): """ This methods shows some statistics values """ def fill_table(): try: for values in self.aminolist: if self.aminolist.index(values) % 2 == 0: self.Table.insert('', tk.END, text='', values=(values[0] + str(values[1]) + "-" + values[2], round(values[3], 2), round(values[4], 2)), tags=('even',)) else: self.Table.insert('', tk.END, text='', values=(values[0] + str(values[1]) + "-" + values[2], round(values[3], 2), round(values[4], 2)), tags=('odd',)) except Exception as e: pass self.columns = ("aminoacid", "fscq", "mean") self.columsText = ("Aminoacids", "atom FSC-Q", "Mean FSC-Q aminoacid") self.Table = ttk.Treeview(self.FrameTable, columns=self.columns) self.Table.grid(row=row, column=column, sticky='news') self.Table.tag_configure("heading", background='sky blue', foreground='black', font=('Calibri', 10, 'bold')) self.Table.tag_configure('even', background='white', foreground='black') self.Table.tag_configure('odd', background='gainsboro', foreground='black') self.Table.heading(self.columns[0], text=self.columsText[0]) self.Table.heading(self.columns[1], text=self.columsText[1]) self.Table.heading(self.columns[2], text=self.columsText[2]) self.Table.column("#0", width=0, minwidth=0, stretch=False) self.Table.column(self.columns[0], anchor=CENTER) self.Table.column(self.columns[1], anchor=CENTER) self.Table.column(self.columns[2], anchor=CENTER) yscroll = Scrollbar(self.FrameTable, orient='vertical', command=self.Table.yview) yscroll.grid(row=row, column=column + 1, sticky='news') self.Table.configure(yscrollcommand=yscroll.set) yscroll.configure(command=self.Table.yview) self.Table.bind("<Button-1>", self._orderTable, True) fill_table()
def _orderTable(self, event): x, y, widget = event.x, event.y, event.widget column = self.Table.identify_column(x) row = self.Table.identify_column(y) if row == '#1': # click over heading col = 0 if column == '#2': col = 1 elif column == '#3': col = 2 self.Table.heading(self.columns[col], text=self.columsText[col], command=lambda: \ self.treeview_sort_column(col, self.flag))
[docs] def treeview_sort_column(self, col, reverse): if col == 0: l = [(self.Table.set(k, col), k) for k in self.Table.get_children('')] else: l = [(float(self.Table.set(k, col)), k) for k in self.Table.get_children('')] l.sort(reverse=reverse) for index, (_, k) in enumerate(l): self.Table.move(k, '', index) self.Table.heading(col, command=lambda: self.treeview_sort_column(col, not reverse)) self.flag = not self.flag