|
#$Id: vinaWizard.py 288 2017-01-14 16:52:23Z sarkiss $
"""This module implement Vina Wizard
"""
import wx
from wx.lib import flatnotebook
import utils
import pickle
import shutil, glob, time, os, sys
from icons import residuePNG, molPNG, cubePNG, adtPNG, eTablePNG
from preferences import global_preferences
from vsModel import autodockPreferencesPage, autodockRemotePreferencesPage
import runProcess
from time import strftime
import pybel, openbabel
from traitedBabel import openbabelAutoDockParameters
import urllib2
try:
from webServices import QueryRemoteJobs
except:
QueryRemoteJobs = None
scale_factor = 1.3 #used to scale the search box
runAutoDockVinaOptions = ["Local", "Cluster (Portable Batch System)", "Remote (Opal Web Services)"]
VinaServiceURI = autodockRemotePreferencesPage.URI+'/'+autodockRemotePreferencesPage.VinaService
class StartPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(self, -1, "This wizard will guide you through setting up and running AutoDock Vina.\n")
sizer.Add(label, 0, wx.WEST|wx.NORTH, 5)
self.infoLabel = wx.StaticText(self, -1, "")
sizer.Add(self.infoLabel, 1, wx.ALL, 5)
self.runAutoDockVinaOptions = runAutoDockVinaOptions
if not utils.which('qsub'): #Cluster (Portable Batch System) execution mode is not supported for Windows
txt = "Cluster (Portable Batch System)"
if txt in self.runAutoDockVinaOptions:
self.runAutoDockVinaOptions.remove(txt)
try:
txt = urllib2.urlopen(VinaServiceURI).read()
if not "vina" in txt:
self.runAutoDockVinaOptions.remove("Remote (Opal Web Services)")
except:
self.runAutoDockVinaOptions.remove("Remote (Opal Web Services)")
self.rb = wx.RadioBox(self, -1, "Vina Execution Mode", choices=self.runAutoDockVinaOptions)
self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, self.rb)
sizer.Add(self.rb, 0, wx.EXPAND)
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
lin = wx.StaticLine(self)
startButton = wx.Button(self, wx.ID_FORWARD, "Start")
buttonSizer.Add(wx.StaticText(self, -1, "Click on Start button to begin --->"), 0, wx.WEST|wx.NORTH, 5)
buttonSizer.Add((150, -1), 1, flag=wx.EXPAND | wx.ALIGN_RIGHT)
buttonSizer.Add(startButton, 0, wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT|wx.EAST|wx.SOUTH, 1)
sizer.Add(lin,0,wx.EXPAND)
sizer.Add(buttonSizer, 0, wx.EXPAND|wx.ALIGN_BOTTOM)
self.SetSizer(sizer)
sizer.SetSizeHints(self)
self.Bind(wx.EVT_BUTTON, self.Start, startButton)
self.sizer = sizer
# self.Bind(wx.EVT_SHOW, self.SetActive)
self.frame = self.TopLevelParent
def Start(self, event):
self.Parent.SetSelection(1)
self.frame.navigator.Selection = 1
def SetActive(self, event):
"This method is bound to wx.EVT_SHOW, i.e., invoked when this page is shown"
if self.Shown:
pref = global_preferences
try:
mode = int(pref.get('AutoDock.vinaExecutionMode'))
self.rb.SetSelection(mode)
except:
pref = global_preferences
pref.set('AutoDock.vinaExecutionMode', 0)
pref.flush()
mode = 0
self.rb.SetSelection(mode)
self.EvtRadioBox(None)
def EvtRadioBox(self, event):
"Called when one of the Vina Execution mode Radio button is selected."
selection = self.rb.GetSelection()
if self.runAutoDockVinaOptions[selection] != "Remote (Opal Web Services)":
vinaPath = utils.which(autodockPreferencesPage.vina)
if not vinaPath:
self.infoLabel.SetLabel("Please set Autodock Vina path using Edit -> Preferences....")
if event: #avoid infinite recursion
self.SetActive(event)
return
self.infoLabel.SetLabel(vinaPath+" will be used for docking. " )
else:
self.infoLabel.SetLabel("Vina Service running at "
+ VinaServiceURI + " will be used for docking.")
try:
pref = global_preferences
pref.set('AutoDock.vinaExecutionMode', selection)
pref.flush()
except: #avoid IOError when multiple copies are run
pass
from boxUI import VinaBoxUI
from enthought.traits.api import HasTraits, List, Trait, Int, Bool, Float, Enum, File, Str, TraitHandler
from enthought.traits.ui.api import Item, Group, View, CheckListEditor, VGroup, HGroup, spring, EnumEditor
from miscTraits import PositiveInt, TraitPositiveInteger, PositiveFloat
from wx.lib.buttons import ThemedGenBitmapTextButton
class VinaParameters(HasTraits):
exhaustiveness = PositiveInt(8)
num_modes = PositiveInt(9)
traits_view = View(Item(name = 'exhaustiveness', tooltip="Exhaustiveness of the global search (roughly proportional to time)"),
Item(name = 'num_modes', tooltip="Maximum number of binding modes to generate"),
title = 'Vina Parameters',
buttons = ['OK', 'Cancel']
)
from miscCtrl import CheckMixListCtrl
import wx.lib.intctrl
class RunVinaPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
topSizer = wx.BoxSizer(wx.HORIZONTAL)
listBoxSizer = wx.BoxSizer(wx.VERTICAL)
#self.ligText = wx.StaticText(self, -1, "No ligand selected for virtural screening.")
self.listCtrl = CheckMixListCtrl(self, style=wx.LC_REPORT | wx.LC_VRULES | wx.LC_HRULES )
self.listCtrl.InsertColumn(0, "Ligand")
self.listCtrl.InsertColumn(1, "Progress")
listBoxSizer.Add(self.listCtrl, 1, wx.EXPAND)
topSizer.Add(listBoxSizer, 1, wx.EXPAND)
listBoxSizer = wx.BoxSizer(wx.VERTICAL)
boxWidget = VinaBoxUI()
view = boxWidget.View()
boxUI = view.ui(boxWidget, self, kind='subpanel')
self.comboBox = wx.ComboBox(self, id=wx.ID_ANY, style=wx.CB_READONLY)
listBoxSizer.Add(self.comboBox, 0, wx.EXPAND)
listBoxSizer.Add(boxUI.control, 1, wx.EXPAND)
topSizer.Add(listBoxSizer, 1, wx.EXPAND)
sizer.Add(topSizer, 1, wx.EXPAND, wx.ALIGN_BOTTOM)
self.forwardButton = wx.Button(self, wx.ID_FORWARD, "")
self.backButton = wx.Button(self, wx.ID_BACKWARD, "")
bitmap = wx.ArtProvider_GetBitmap(wx.ART_EXECUTABLE_FILE, wx.ART_BUTTON)
self.runVinaButton = ThemedGenBitmapTextButton(self, -1, bitmap, "Run Vina")
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
lin = wx.StaticLine(self)
self.selectButton = wx.Button(self, -1, "Select")
buttonSizer.Add(self.selectButton, 0)
buttonSizer.Add(self.runVinaButton, 0, 0, wx.LEFT|wx.RIGHT, 10)
self.parametersButton = wx.Button(self, -1, "Parameters")
buttonSizer.Add(self.parametersButton, 0)
buttonSizer.Add((150, -1), 1, flag=wx.EXPAND | wx.ALIGN_RIGHT)
buttonSizer.Add(self.backButton, 0, 1, wx.ALIGN_RIGHT)
buttonSizer.Add(self.forwardButton, 0, 1, wx.ALIGN_RIGHT)
sizer.Add(lin,0,wx.EXPAND)
sizer.Add(buttonSizer, 0, wx.EXPAND|wx.ALIGN_BOTTOM)
self.SetSizer(sizer)
self.Bind(wx.EVT_BUTTON, self.Next, self.forwardButton)
self.Bind(wx.EVT_BUTTON, self.Back, self.backButton)
self.Bind(wx.EVT_BUTTON, self.Run, self.runVinaButton)
self.Bind(wx.EVT_BUTTON, self.Select, self.selectButton)
self.Bind(wx.EVT_BUTTON, self.OnParameters, self.parametersButton)
self.Bind(wx.EVT_COMBOBOX, self.OnMacromoleculeChanged, self.comboBox)
self.frame = self.TopLevelParent
self.vsModel = self.frame.vsModel
boxWidget.set(interactor=self.frame.mayaviEngine.scene.interactor)
boxWidget.set(place_factor=1)
boxWidget.rotation_enabled = False
boxWidget.key_press_activation = False
boxWidget.add_observer("InteractionEvent", boxWidget.ChangeBox)
self.boxWidget = boxWidget
self.runnig = False
self.buttons = [self.forwardButton, self.backButton, self.runVinaButton, self.selectButton]
self.vinaParameters = VinaParameters()
def SetActive(self, event):
"This method is bound to wx.EVT_SHOW, i.e., invoked when this page is shown"
if self.runnig:
self.EnableButtons(False)
return
else:
self.EnableButtons(True)
if not self.frame.vinaWiz.selectMoleculesPage.ligandPass or not hasattr(self.vsModel,'ligands'):
self.frame.vinaWiz.selectMoleculesPage.Next(None)
if not hasattr(self.vsModel,'ligands') or self.vsModel.ligands == []:
dlg = wx.MessageDialog(self, "Please select a ligand!",'A Message Box',
wx.OK| wx.ICON_EXCLAMATION)
dlg.ShowModal()
dlg.Destroy()
wx.CallAfter(self.Parent.SetSelection, 1)
return
#check if macromoleculePath is set
if not self.frame.vinaWiz.selectMoleculesPage.macromoleculePass:
dlg = wx.MessageDialog(self, "Please select macromolecule!",'Vina Message Box',
wx.OK| wx.ICON_EXCLAMATION)
dlg.ShowModal()
dlg.Destroy()
wx.CallAfter(self.Parent.SetSelection, 1)
return
ligands = self.vsModel.ligands
self.ligandCount = len(ligands)
self.listCtrl.ClearAll()
self.listCtrl.InsertColumn(0, "Ligand", width=250)
self.listCtrl.InsertColumn(1, "Progress")
self.ligands = []
for index, ligand in enumerate(ligands):
txt = os.path.splitext(os.path.basename(ligand))[0]
self.ligands.append(txt)
self.listCtrl.InsertStringItem(self.ligandCount, txt)
self.OnSelectAll(None)
#activate 3D Graphics tab
self.frame.view.SetSelection(self.frame.view.GetPageIndex(self.frame.canvas3D))
self.comboBox.Clear() #clears drop-down menu
#now read macromolecule if necessary
macromoleculePaths = self.frame.vinaWiz.selectMoleculesPage.macromoleculePaths
self.macromolecules = []
for macromoleculePath in macromoleculePaths:
tmp, ext = os.path.splitext(macromoleculePath)
basePath, receptorName = os.path.split(tmp)
if not receptorName in self.frame.molNav.moleculesNames:
self.macromolecules.append(self.frame.molNav.TryOpenMolecule(macromoleculePath)[0])
else:
index = self.frame.molNav.moleculesNames.index(receptorName)
self.macromolecules.append(self.frame.molNav.molecules[index])
#setup grid dimensions and populate comboBox
macromolecule = self.macromolecules[-1]
if not hasattr(macromolecule, 'max_XDimension'):
center = macromolecule.getCenter()
macromolecule.initCenter = tuple(center)
macromolecule.box_center = center #box_center can change later on depending on user interaction
macromolecule.X_center, macromolecule.Y_center, macromolecule.Z_center = center
bounds = macromolecule.assembly.GetBounds()
macromolecule.max_XDimension = abs(bounds[1]-bounds[0])*scale_factor
macromolecule.X_dimension = macromolecule.max_XDimension
macromolecule.max_YDimension = abs(bounds[3]-bounds[2]*scale_factor)
macromolecule.Y_dimension = macromolecule.max_YDimension
macromolecule.max_ZDimension = abs(bounds[5]-bounds[4]*scale_factor)
macromolecule.Z_dimension = macromolecule.max_ZDimension
macromolecule.initDimension = (macromolecule.X_dimension,macromolecule.Y_dimension,macromolecule.Z_dimension)
self.comboBox.Append(macromolecule.name, macromolecule)
macromolecule.basePath = basePath
macromolecule.receptorName = receptorName
if not self.macromolecules:
dlg = wx.MessageDialog(self, "No macromolecule found. Please make sure there is a proper macromolecule pdbqt file in:\n"+str(macromoleculePaths),'Vina Message Box',
wx.OK| wx.ICON_EXCLAMATION)
dlg.ShowModal()
dlg.Destroy()
wx.CallAfter(self.Parent.SetSelection, 2)
return
#select first macromolecule and setup boxWidget
self.select_macromolecule = self.macromolecules[0]
self.comboBox.SetValue(self.select_macromolecule.name)
self.boxWidget.X_center = self.select_macromolecule.X_center
self.boxWidget.Y_center = self.select_macromolecule.Y_center
self.boxWidget.Z_center = self.select_macromolecule.Z_center
self.boxWidget.X_dimension = self.select_macromolecule.X_dimension
self.boxWidget.Y_dimension = self.select_macromolecule.Y_dimension
self.boxWidget.Z_dimension = self.select_macromolecule.Z_dimension
self.boxWidget.max_XDimension = self.select_macromolecule.max_XDimension
self.boxWidget.max_YDimension = self.select_macromolecule.max_YDimension
self.boxWidget.max_ZDimension = self.select_macromolecule.max_ZDimension
self.boxWidget.initCenter = self.select_macromolecule.initCenter
self.boxWidget.initDimension = self.select_macromolecule.initDimension
self.boxWidget.enabled = True
#self.boxWidget._maximize_fired()
self.frame.canvas3D.Refresh()
def OnMacromoleculeChanged(self, event):
"Called when an item on the Macromolecule ComboBox list is selected"
cb = event.GetEventObject()
data = cb.GetClientData(event.GetSelection())
self.UpdateMacromoleculeBox()
#update boxWidget with settings from currenty selected macromolecule
self.boxWidget.X_center = data.X_center
self.boxWidget.Y_center = data.Y_center
self.boxWidget.Z_center = data.Z_center
self.boxWidget.X_dimension = data.X_dimension
self.boxWidget.Y_dimension = data.Y_dimension
self.boxWidget.Z_dimension = data.Z_dimension
self.boxWidget.initDimension = data.initDimension
self.select_macromolecule = data
self.boxWidget.max_XDimension = self.select_macromolecule.max_XDimension
self.boxWidget.max_YDimension = self.select_macromolecule.max_YDimension
self.boxWidget.max_ZDimension = self.select_macromolecule.max_ZDimension
self.boxWidget.initCenter = self.select_macromolecule.initCenter
def UpdateMacromoleculeBox(self):
#copy current boxWidget's settings for previously selected macromolecule
self.select_macromolecule.X_center = self.boxWidget.X_center
self.select_macromolecule.Y_center = self.boxWidget.Y_center
self.select_macromolecule.Z_center = self.boxWidget.Z_center
self.select_macromolecule.X_dimension = self.boxWidget.X_dimension
self.select_macromolecule.Y_dimension = self.boxWidget.Y_dimension
self.select_macromolecule.Z_dimension = self.boxWidget.Z_dimension
def Next(self, event):
"Goto next page"
self.UpdateMacromoleculeBox()
#this part is done since user can change selection before clicking Next
self.outFiles = []
selectedLigands = []
removeLigand = []
for index, ligand in enumerate(self.ligands):
if self.listCtrl.IsChecked(index):
selectedLigands.append(ligand)
else:
removeLigand.append(ligand)
for ligand in removeLigand:
self.ligands.remove(ligand)
self.flagRunVina = False
for macromolecule in self.macromolecules:
outFiles = glob.glob(macromolecule.basePath+os.sep+'*_out.pdbqt')
if outFiles:
for ligand in selectedLigands:
out_str = os.path.join(macromolecule.basePath, ligand+'_out.pdbqt')
if not out_str in outFiles:
self.flagRunVina = True
self.outFiles.append(out_str)
else:
self.flagRunVina = True
break
self.boxWidget.enabled = False
if self.flagRunVina:
self.Run(None)
else:
self.Forward()
self.boxWidget.enabled = False
def Forward(self):
self.frame.vinaWiz.book.Enable()
self.frame.view.SetSelection(0) #3D Graphics
analyzePage = self.frame.vinaWiz.analyzePage
analyzePage.Clear()
maximum = len(self.outFiles)
if maximum > 2:
dlg = wx.ProgressDialog("Parsing Vina Output Files. Please Wait...",
"Parsing Vina Output Files. Please Wait...",
maximum = maximum,
parent=self,
style = wx.PD_CAN_ABORT
| wx.PD_APP_MODAL
| wx.PD_ELAPSED_TIME
#| wx.PD_ESTIMATED_TIME
| wx.PD_REMAINING_TIME
)
self.frame.Refresh()
keepGoing = True
analyzePage.list = []
for index, outFile in enumerate(self.outFiles):
if maximum > 2:
(keepGoing, skip) = dlg.Update(index, "Parsing "+os.path.split(outFile)[-1]+" ("+str(index+1) +" of " +str(maximum)+")")
if not keepGoing:
break
if os.path.exists(outFile):
try:
analyzePage.AddDocking(outFile, updateTable=False)
except Exception, inst:
self.frame.log.error("Open "+outFile+" for details.\n"+ str(inst))
else:
self.frame.log.error("Error in vinaWizard.RunVinaPage.AddDocking. File does not exist: "+outFile)
if maximum > 2:
dlg.Destroy()
if keepGoing:
self.Parent.SetSelection(3)
analyzePage.conformations.items.extend(analyzePage.list)
def Back(self, event):
"Goto previous page"
self.Parent.SetSelection(1)
self.boxWidget.enabled = False
def OnParameters(self, event):
self.vinaParameters.configure_traits(kind='livemodal')
def Run(self, event):
self.UpdateMacromoleculeBox()
self.frame.TryCommand(self.TryRun, None)
def TryRun(self, event):
removeLigand = []
for index, ligand in enumerate(self.ligands):
if not self.listCtrl.IsChecked(index):
removeLigand.append(ligand)
for ligand in removeLigand:
self.ligands.remove(ligand)
if not self.ligands: return
for macromolecule in self.macromolecules:
file = open(os.path.join(macromolecule.basePath,"conf.txt"),'w')
file.write("receptor = "+macromolecule.receptorName+".pdbqt\n")
if macromolecule.receptorName.endswith('_rigid'):
file.write("flex = "+macromolecule.receptorName.replace('_rigid','_flex')+".pdbqt\n")
self.vinaParameters.exhaustiveness
file.write("exhaustiveness = "+str(self.vinaParameters.exhaustiveness)+"\n")
file.write("num_modes = "+str(self.vinaParameters.num_modes)+"\n")
file.write("center_x = "+str(macromolecule.X_center)+"\n")
file.write("center_y = "+str(macromolecule.Y_center)+"\n")
file.write("center_z = "+str(macromolecule.Z_center)+"\n")
file.write("size_x = "+str(macromolecule.X_dimension)+"\n")
file.write("size_y = "+str(macromolecule.Y_dimension)+"\n")
file.write("size_z = "+str(macromolecule.Z_dimension)+"\n")
file.write("cpu = "+str(autodockPreferencesPage.cpu_num))
file.close()
selection = self.Parent.GetPage(0).rb.GetSelection()
vinaExecutionMode = self.frame.vinaWiz.startPage.runAutoDockVinaOptions[selection]
if vinaExecutionMode == "Local":
if not utils.which(autodockPreferencesPage.vina):
dlg = wx.MessageDialog(self, "Cannot find "+autodockPreferencesPage.vina+
". Use Edit -> Preferences to set Vina path.", 'Command not found.', wx.OK| wx.ICON_EXCLAMATION)
dlg.ShowModal()
dlg.Destroy()
return
self.availableJobs = 1 #vina detects and uses all CPU cores. autodockPreferencesPage.cpu_num
self.currentLigand = self.ligands[0]
self.currentMacromolecule = self.macromolecules[0]
self.ligandCount = len(self.ligands)
self.macromoleculeCount = len(self.macromolecules)
self.remainingJobs = len(self.ligands)*len(self.macromolecules)
self.Bind(wx.EVT_TIMER, self.CheckAvailability)
self.timer = wx.Timer(self)
self.timer.Start(500)
elif vinaExecutionMode == "Cluster (Portable Batch System)":
import pbsJobs
pbsJob = pbsJobs.startVina(self)
return
else:
self.RunWS()
return
self.runnig = True
self.EnableButtons(False)
self.frame.statusBar.SetStatusText("Running Vina. Please Wait...", 0)
def RunWS(self):
"""Run all jobs at once using Web Services.
We keep the info about remote job running in /etc folder which contains
tab delimited list of job ID's and output files."""
self.outFiles = []
self.jobIDs = []
self.runnig = True
self.listCtrl.resizeColumn(1) #otherwise list is not shown fully
lenMacromolecules = len(self.macromolecules)
lenLigands = len(self.ligands)
maximum = lenLigands*lenMacromolecules
dlg = wx.ProgressDialog("Sending Vina Web Services Request. Please Wait...",
"Sending Vina Web Services Request. Please Wait...",
maximum = maximum,
parent=self,
style = wx.PD_CAN_ABORT
| wx.PD_APP_MODAL
| wx.PD_ELAPSED_TIME
#| wx.PD_ESTIMATED_TIME
| wx.PD_REMAINING_TIME
)
self.frame.Refresh()
keepGoing = True
try:
for macro_index, macromolecule in enumerate(self.macromolecules):
for index, ligand in enumerate(self.ligands):
self.basePath = macromolecule.basePath #basePath is where macromolecule is stored
self.receptorName = macromolecule.receptorName
jobID = self.frame.vinaWS.Start(parent=self, ligand=ligand)
if jobID:
self.listCtrl.SetStringItem(index, 1, "Running")
self.jobIDs.append(jobID)
else: #
self.EnableButtons()
keepGoing = False
break
self.outFiles.append(os.path.join(self.basePath, ligand+"_out.pdbqt"))
(keepGoing, skip) = dlg.Update(index+lenLigands*macro_index, "Sending data for "+ligand+" ("+str(index+1) +" of " +str(lenLigands)+")"+
"\nMacromolecule is "+macromolecule.name+" ("+str(macro_index+1) +" of " +str(lenMacromolecules)+")")
if not keepGoing:
break
except Exception,e:
self.frame.log.error("Error in running Vina via web services: \n"+str(e))
dlg.Destroy()
#self.frame.vinaWS.firstTime = False
if keepGoing:
urlFilePath = os.path.join(self.vsModel.etcFolder,'Vina_RemoteJobs')
urlFile = open(urlFilePath,'w')
for index, jobID in enumerate(self.jobIDs):
urlFile.write(jobID+"\t" + self.outFiles[index]+"\n")
urlFile.close()
self.frame.vinaWS.parent = self
remJobs = QueryRemoteJobs(urlFilePath, self.frame, vina=True)
remJobs.parent = self
self.frame.statusBar.SetStatusText("Running Vina at "+autodockRemotePreferencesPage.URI, 0)
self.runnig = True
else:
self.frame.statusBar.SetStatusText("", 0)
self.runnig = False
self.SetActive(None)
for job in self.jobIDs:
self.frame.vinaWS.destroy(job)
def CheckAvailability(self, event):
"Checks if there is an available CPU? If yes, runs Vina"
try:
index = self.ligands.index(self.currentLigand)
except ValueError: #This is needed to avoid ValueError: list.index(x): x not in list
self.currentLigand = self.ligands[index+1]
return
if self.availableJobs:
self.listCtrl.SetStringItem(index, 1, "Running...")
macromolecule_index = self.macromolecules.index(self.currentMacromolecule)
try:
ligandFolder = os.pardir+os.sep+os.pardir+os.path.sep+"Ligands"
outputFile = os.path.abspath(os.path.join(self.currentMacromolecule.basePath, self.currentLigand+"_out.pdbqt"))
cmdTxt = [autodockPreferencesPage.vina, '--config', 'conf.txt', '--ligand',
ligandFolder+os.path.sep+self.currentLigand+".pdbqt", '--out', outputFile]
self.processPanel = runProcess.ProcessPanel(self.GrandParent, cmdTxt,
self.currentMacromolecule.basePath, outputFile,
self.CheckResults, use_stdout=True)
self.frame.view.AddPage(self.processPanel, "Vina - "+self.currentMacromolecule.receptorName+"/"+self.currentLigand, select=True)
self.processPanel.Start()
except Exception, inst:
self.frame.log.error("Exception in RunVinaPage.CheckAvailability\n"+ str(inst))
self.availableJobs -= 1
macromolecule_index += 1
if macromolecule_index < self.macromoleculeCount:
self.currentMacromolecule = self.macromolecules[macromolecule_index]
else:
self.currentMacromolecule = self.macromolecules[0]
index += 1
if index == self.ligandCount: #last ligand
self.timer.Stop()
del self.timer
else:
self.currentLigand = self.ligands[index]
def CheckResults(self, page, outputFile, success=False):
"Called after Vina finished running"
ligand = os.path.split(outputFile)[1]
index = self.ligands.index(str(ligand.replace("_out.pdbqt", '')))
if success == "Terminated":
self.frame.statusBar.SetStatusText("Vina Terminated.", 0)
self.listCtrl.SetStringItem(index, 1, "Terminated")
self.runnig = False
self.EnableButtons(True)
self.SetActive(None)
elif not success:
self.frame.view.DeletePage(self.frame.view.GetPageIndex(page))
if success and os.path.exists(outputFile):
self.frame.view.DeletePage(self.frame.view.GetPageIndex(page))
self.listCtrl.SetStringItem(index, 1, "Finished")
if os.path.exists(outputFile):
try:
self.Parent.GetPage(3).AddDocking(outputFile)
except Exception, inst:
self.frame.log.error("Open "+outputFile+" for details.\n"+ str(inst))
self.availableJobs += 1
self.remainingJobs -= 1
if self.remainingJobs == 0:
self.frame.statusBar.SetStatusText("Finished Running Vina.", 0)
self.flagRunVina = False
self.frame.view.SetSelection(0) #3D Graphics
wx.CallAfter(self.Parent.SetSelection, 3)
self.runnig = False
self.EnableButtons()
self.frame.autodockNav.RefreshMacroolecules()
self.boxWidget.enabled = False
def Select(self, event):
menu = wx.Menu()
allMenu = menu.Append(wx.ID_ANY, "All")
self.Bind(wx.EVT_MENU, self.OnSelectAll, allMenu)
invertMenu = menu.Append(wx.ID_ANY, "Invert Selection")
self.Bind(wx.EVT_MENU, self.OnInvertSelection, invertMenu)
self.PopupMenu(menu)
event.Skip()
def OnSelectAll(self, event):
lenLigands = len(self.ligands)
for index in range(lenLigands):
self.listCtrl.CheckItem(index)
def OnInvertSelection(self, event):
lenLigands = len(self.ligands)
for index in range(lenLigands):
if self.listCtrl.IsChecked(index):
self.listCtrl.CheckItem(index, False)
else:
self.listCtrl.CheckItem(index)
def OnGroup1Select( self, event ):
radio_selected = event.GetEventObject()
for radio, button in self.algo_ctrls:
if radio is radio_selected:
button.Enable(True)
else:
button.Enable(False)
def EnableButtons(self, enable=True):
for item in self.buttons:
item.Enable(enable)
from icons import table_savePNG, database_savePNG
ID_SAVE_CSV = wx.NewId()
ID_SAVE_SDF = wx.NewId()
import enthought
from MolKit.pdbParser import PdbqtParser
class AnalyzeVinaPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
mainSizer = wx.BoxSizer()
conformations = Conformations()
self.conformations = conformations
self.dockings = {}
self.conformation = None
view = conformations.View(self)
grid = view.ui(conformations, self, kind='subpanel')
self.grid = grid
self.Bind(wx.EVT_SHOW, self.SetActive)
self.frame = self.TopLevelParent
#this part is needed to add tooltip
if sys.version_info[0] == 2 and sys.version_info[1] == 5:
self.wxgrid = grid.control.Children[0].Children[0].Children[4]
else:
self.wxgrid = grid.control.Children[0].Children[0].Children[2]
self.wxgrid.SetMinSize((0,0))
wx.EVT_MOTION(self.wxgrid.Children[1], self.OnMouseMotion)
self.wxgrid.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self._on_label_left_click)
self.prev_col = None
self.editor = grid._editors[0]
self.editor.toolbar.control.AddLabelTool(ID_SAVE_CSV, "Save as CSV", table_savePNG, shortHelp="Save as Comma-Separated Values (CSV)")
self.Bind(wx.EVT_TOOL, self.OnSaveCSV, id=ID_SAVE_CSV)
self.editor.toolbar.control.AddLabelTool(ID_SAVE_SDF, "Save as SDF", database_savePNG, shortHelp="Save as SDF (Structure Data Format).\nStores structures and Binding Affinity.")
self.Bind(wx.EVT_TOOL, self.OnSaveSDF, id=ID_SAVE_SDF)
self.wxgrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.OnRightUp)
mainSizer.Add(grid.control, 1, wx.EXPAND)
self.SetSizer(mainSizer)
mainSizer.SetSizeHints(self)
self.editor.toolbar.control.Realize()
self.list = []
def _on_label_left_click(self, evt):
row, col = evt.GetRow(), evt.GetCol()
# A row value of -1 means this click happened on a column.
# vice versa, a col value of -1 means a row click.
if row == -1:
self.grid._editors[0].grid._column_sort( col )
def AddDocking(self, filename, updateTable=True):
if not os.path.exists(filename):
self.frame.log.error("File does not exist: "+filename)
return
parser = PdbqtParser(filename, modelsAs='conformations')
molecules = parser.parse()
if not molecules:
self.frame.log.error("No docked conformation found in "+filename+"..")
return
mol = molecules[0]
head, tail = os.path.split(filename)
name = os.path.splitext(tail)[0]
outputName = name.replace("_out", '')
targetName = os.path.split(head)[1]
name = targetName+"_"+outputName
name = name.lower()
mol.name = name
self.dockings[name] = mol
if name in self.frame.molNav.moleculesNames:
index = self.frame.molNav.moleculesNames.index(name)
self.frame.molNav.Remove(index)
tmpList = []#without tmpList self.conformations update takes too long
vina_energy = sys.maxint
for index, vina_result in enumerate(mol.vina_results):
conf = Conformation(name = name,
vina_energy = float(vina_result[0]),
mode=index,
rmsd_lb = float(vina_result[1]),
rmsd_ub = float(vina_result[2]),
index = index
)
if conf.vina_energy < vina_energy:
vina_energy = conf.vina_energy
tmpList.append(conf)
if updateTable:
self.conformations.items.extend(tmpList)
else:
self.list.extend(tmpList)
args = (outputName, targetName, vina_energy, "",
strftime("%Y.%m.%d %H:%M:%S"), "Vina")
if not hasattr(self.frame.dbView, 'resultsTable'):
self.frame.dbView.Activate(None, showProgress=False)
self.frame.dbView.resultsTable.AddItem(args)
#self.frame.view.SetSelection(self.frame.view.GetPageIndex(self.frame.canvas3D))
def SetActive(self, event):
"This method is bound to wx.EVT_SHOW, i.e., invoked when this page is shown"
if self.frame.statusBar:
self.frame.statusBar.SetStatusText('', 0)
#self.grid.control.Children[0].Children[0].Children[0].SetPosition((0,0))
def OnSelect(self, conformation):
"Updates conformation of the ligand"
if not conformation:
return
else:
self.conformation = conformation
name = conformation.name
if name in self.frame.molNav.moleculesNames:
self.dockings[name].allAtoms.setConformation(conformation.index)
self.frame.molNav.UpdateConformation(self.dockings[name], self.dockings[name].allAtoms)
else:
molecule = self.dockings[name]
aName = molecule.name
molecule.name = name
molecule.buildBondsByDistance()
#self.frame.molNav.AddBonds(molecule, aName, force=True)
self.frame.molNav.ext = 'PDBQT'
self.frame.molNav.AddMolecule(molecule, resetCamera=False)
self.conformation = conformation
self.dockings[name].allAtoms.setConformation(conformation.index)
self.frame.molNav.UpdateConformation(self.dockings[name], self.dockings[name].allAtoms)
self.frame.view.SetSelection(self.frame.view.GetPageIndex(self.frame.canvas3D))
self.wxgrid.SetFocus() #otherwise can't scroll pass next molecule using down arrow on keyboard.
def Clear(self, event=None):
self.conformations.items = []
self.dockings = {}
def OnMouseMotion(self, event):
"Modified from http://wiki.wxpython.org/wxGrid_ToolTips"
x, y = self.wxgrid.CalcUnscrolledPosition(event.GetPosition())
col = self.wxgrid.XToCol(x)
if col != self.prev_col and col >= 0:
self.prev_col = col
hinttext = ''
if col == 1:
hinttext = 'Predicted Binding Affinity is in kcal/mol.'
elif col == 2:
hinttext = 'RMSD lower bound'
elif col == 3:
hinttext = 'RMSD lower bound'
self.wxgrid.Children[1].SetToolTipString(hinttext)
event.Skip()
def OnRightUp(self, event):
self.editor.set_selection(self.conformations.items[event.Row])
menu = wx.Menu()
saveComplexAsMenu = menu.Append(wx.ID_ANY, "Save Docked Complex as PDB ...")
self.Bind(wx.EVT_MENU, self.OnSaveComplex, saveComplexAsMenu)
menu.AppendSeparator()
DelMenu = menu.Append(wx.ID_ANY, "Delete All")
self.Bind(wx.EVT_MENU, self.Clear, DelMenu)
self.PopupMenu(menu)
def OnSaveCSV(self, event):
dlg = wx.FileDialog(self, "Save as CSV", os.getcwd(), "",
"Comma Separated Values (*.csv)|*.csv",
style=wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
fileName = dlg.GetPath()
if fileName[-3:].lower() != 'csv':
fileName = fileName +".csv"
if os.path.exists(fileName):
dlg1 = wx.MessageDialog(self, fileName +" already exists. Overwrite File?",
'Overwrite File?',
wx.YES_NO | wx.ICON_INFORMATION
)
if dlg1.ShowModal() != wx.ID_YES:
dlg1.Destroy()
dlg.Destroy()
return
outFile = open(fileName, 'w')
outFile.write('Ligand,Binding Affinity,rmsd/ub, rmsd/lb\n')
for item in self.conformations.items:
txt = item.name +","+str(item.vina_energy)+","+str(item.rmsd_ub)+","+str(item.rmsd_lb)
outFile.write(txt+"\n")
outFile.close()
dlg.Destroy()
def OnSaveComplex(self, event):
"Saves docked ligand and macromolecule complex as pdb"
conformation = self.editor.selected_row
last_saveComplexFolder = self.frame.wxcfg.Read("last_saveComplexFolder")
dlg = wx.FileDialog(self, "Save Docked Complex as PDB", last_saveComplexFolder, "",
"PDB Format (*.pdb)|*.pdb",
style=wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
fileName = dlg.GetPath()
if fileName[-3:].lower() != 'pdb':
fileName = fileName +".pdb"
if os.path.exists(fileName):
dlg1 = wx.MessageDialog(self, fileName +" already exists. Overwrite File?",
'Overwrite File?',
wx.YES_NO | wx.ICON_INFORMATION
)
if dlg1.ShowModal() != wx.ID_YES:
dlg1.Destroy()
dlg.Destroy()
return
self.frame.wxcfg.Write("last_saveComplexFolder", os.path.split(fileName)[0])
from MolKit.pdbWriter import PdbWriter
writer = PdbWriter()
writer.write(fileName, self.dockings[conformation.name], records=['ATOM', 'HETATM', 'CONECT'])
#get macromolecule's path
filename = self.dockings[conformation.name].parser.filename
receptorName = os.path.split(os.path.split(filename)[0])[-1]
receptorPath = os.path.join(self.frame.vsModel.macromoleculesFolder, receptorName)
receptorPath = os.path.join(receptorPath, receptorName+".pdbqt")
if receptorPath.endswith('_flex.pdbqt'): receptorPath = receptorPath.replace('_flex.pdbqt', '_rigid.pdbqt')
mol = self.frame.pmv.mv.readMolecule(receptorPath)
writer = PdbWriter()
pyrx_tmpfile = os.path.join(self.frame.vsModel.etcFolder, 'pyrx_tmpfile.pdb')
writer.write(pyrx_tmpfile, mol, records=['ATOM', 'HETATM'])
compexFile = open(fileName,'a')
txt = open(pyrx_tmpfile).read()
compexFile.write(txt)
compexFile.close()
os.remove(pyrx_tmpfile)
dlg.Destroy()
def OnSaveSDF(self, event):
if not self.conformations.items:
dlg = wx.MessageDialog(self, 'Results table is empty. Please insert new items.',
'A Message Box',
wx.OK | wx.ICON_INFORMATION
)
dlg.ShowModal()
dlg.Destroy()
return
last_saveSDFFolder = self.frame.wxcfg.Read("last_saveSDFFolder")
dlg = wx.FileDialog(self, "Save as SDF", last_saveSDFFolder, "",
"Structure Data Format (*.sdf)|*.sdf",
style=wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
fileName = dlg.GetPath()
if fileName[-3:].lower() != 'sdf':
fileName = fileName +".sdf"
if os.path.exists(fileName):
dlg1 = wx.MessageDialog(self, fileName +" already exists. Overwrite File?",
'Overwrite File?',
wx.YES_NO | wx.ICON_INFORMATION
)
if dlg1.ShowModal() != wx.ID_YES:
dlg1.Destroy()
dlg.Destroy()
return
self.frame.wxcfg.Write("last_saveSDFFolder", os.path.split(fileName)[0])
outputfile = pybel.Outputfile("sdf", str(fileName), overwrite=True)
if openbabelAutoDockParameters.numberOfPoses == 0:
for item in self.conformations.items:
mol = self.frame.openBabel.ConvertToOB(self.dockings[item.name], item.index)
pairdata = openbabel.OBPairData()
pairdata.SetAttribute("Vina Binding Affinity")
pairdata.SetValue(str(item.vina_energy))
mol.CloneData(pairdata)
mol.SetTitle(str(item.name+"_"+str(item.index)))
outputfile.write(pybel.Molecule(mol))
else:
self.conformations.items.sort(key=lambda x: x.vina_energy)
for item in self.conformations.items[0:openbabelAutoDockParameters.numberOfPoses]:
mol = self.frame.openBabel.ConvertToOB(self.dockings[item.name], item.index)
pairdata = openbabel.OBPairData()
pairdata.SetAttribute("Vina Binding Affinity")
pairdata.SetValue(str(item.vina_energy))
mol.CloneData(pairdata)
mol.SetTitle(str(item.name+"_"+str(item.index)))
outputfile.write(pybel.Molecule(mol))
outputfile.close()
dlg.Destroy()
def Open(self, event=None):
last_VinaOutFolder = self.frame.wxcfg.Read("last_VinaOutFolder")
dlg = wx.FileDialog(self, "Choose Vina Output", last_VinaOutFolder, '',
"Vina Output (*_out.pdbqt)|*_out.pdbqt", style=wx.OPEN | wx.MULTIPLE)
if dlg.ShowModal() == wx.ID_OK:
fileNames = dlg.GetPaths()
for fileName in fileNames:
self.AddDocking(fileName)
if fileNames:
self.frame.wxcfg.Write("last_VinaOutFolder", os.path.split(fileNames[0])[0])
dlg.Destroy()
self.frame.controls.SetSelection(self.frame.controls.GetPageIndex(self.frame.vinaWiz))
self.frame.vinaWiz.book.Selection = 3
from enthought.traits.ui.api import View, Group, Item, TableEditor
from enthought.traits.api import HasTraits, HasStrictTraits, Str, Int, Float, List
from enthought.traits.ui.table_column import ObjectColumn
from enthought.traits.ui.table_filter import EvalFilterTemplate, MenuFilterTemplate, \
RuleFilterTemplate, RuleTableFilter, MenuTableFilter
class Conformation(HasTraits):
name = Str
vina_energy = Float
mode = Int
rmsd_ub = Float
rmsd_lb = Float
index = Int
class Conformations(HasTraits):
items = List(Conformation)
def View(self, parent):
"on_add_new is called to add new element"
def my_row_factory(**kw):
parent.Open()
return None
FilterTemplate = MenuTableFilter(name='No filter', template=True,)
table_editor = TableEditor(
columns = [ ObjectColumn(name='name', editable = False, label='Ligand'),
ObjectColumn(name='vina_energy', label='Binding Affinity (kcal/mol)', editable = False),
ObjectColumn(name='mode', label='Mode', editable = False),
ObjectColumn(name='rmsd_lb', label='RMSD lower bound', editable = False),
ObjectColumn(name='rmsd_ub', label='RMSD upper bound', editable = False),
],
reorderable = False,
sort_model = True,
auto_size = False,
on_select = parent.OnSelect,
editable = True,
show_toolbar = True,
row_factory = my_row_factory,
filters = [FilterTemplate] ,
deletable = True,
)
self.table_editor = table_editor
return View(
Group( Item( 'items',
show_label = False,
editor = table_editor
),
)
)
class VinaWizard(wx.Panel):
def __init__(self, frame):
wx.Panel.__init__(self, frame, -1)
book = wx.Notebook(self, wx.ID_ANY)
self.book = book
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(book, 1, wx.EXPAND)
self.SetSizer(sizer)
self.sizer = sizer
self.CreateIcons()
startPage = StartPage(book)
book.AddPage(startPage, "Start Here", imageId=0)
self.startPage = startPage
from selectMolecules import SelectMoleculesPage
selectMoleculesPage = SelectMoleculesPage(book, checkAtomTypes=False)
book.AddPage(selectMoleculesPage, "Select Molecules", imageId=1)
self.selectMoleculesPage = selectMoleculesPage
runVina = RunVinaPage(book)
book.AddPage(runVina, "Run Vina")
self.runVinaPage = runVina
analyze = AnalyzeVinaPage(book)
book.AddPage(analyze, "Analyze Results", imageId=2)
self.analyzePage = analyze
frame.controls.AddPage(self, "Vina Wizard")
self.frame = frame
book.SetSelection(0)
wx.EVT_NOTEBOOK_PAGE_CHANGED(self.book, -1, self.PageChanged)
wx.CallAfter(startPage.SetActive, None)
from webServices import VinaWebService
self.frame.vinaWS = VinaWebService(self.frame)
#this part is needed to check the status of previously run remote jobs
remoteJobsFilePath = os.path.join(self.frame.vsModel.etcFolder,'Vina_RemoteJobs')
if os.path.exists(remoteJobsFilePath):
if os.path.exists(remoteJobsFilePath+"_old"):
jobsList = open(remoteJobsFilePath).readlines()
jobsList.extend(open(remoteJobsFilePath+"_old").readlines())
jobsList = list(set(jobsList))
open(remoteJobsFilePath+"_old", 'w').writelines(jobsList)
os.remove(remoteJobsFilePath)
else:
open(remoteJobsFilePath+"_old", 'w').write(open(remoteJobsFilePath).read())
wx.FutureCall(2000, QueryRemoteJobs, remoteJobsFilePath+"_old", self.frame, vina=True)
elif os.path.exists(remoteJobsFilePath+"_old"):
wx.FutureCall(2000, QueryRemoteJobs, remoteJobsFilePath+"_old", self.frame, vina=True)
def PageChanged(self, event):
if self.TopLevelParent: #make sure that SetActive is not called on Exit
self.book.GetPage(event.GetSelection()).SetActive(event)
event.Skip()
def CreateIcons(self):
imageSize = (16,16)
il = wx.ImageList(16,16)
self.il = il
tip = wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_TOOLBAR, imageSize)
il.Add(tip)
il.Add(residuePNG)
# il.Add(adtPNG)
il.Add(eTablePNG)
self.book.SetImageList(il)
|