Source code for BFEE2.gui

# the GUI of new BFEE

import os
import shutil
import sys
import webbrowser

# use appdirs to manage persistent configuration
from appdirs import user_config_dir
from PySide2 import QtCore
from PySide2.QtGui import QFont, QIcon
from PySide2.QtWidgets import (QAction, QApplication, QCheckBox, QComboBox,
                               QFileDialog, QGridLayout, QGroupBox,
                               QHBoxLayout, QLabel, QLineEdit, QListWidget,
                               QMainWindow, QMessageBox, QPushButton,
                               QSplitter, QTabWidget, QToolBar, QVBoxLayout,
                               QWidget)

import BFEE2.inputGenerator as inputGenerator
import BFEE2.postTreatment as postTreatment
import BFEE2.templates_gromacs.BFEEGromacs as BFEEGromacs
from BFEE2.commonTools import commonSlots, fileParser, ploter

try:
    import importlib.resources as pkg_resources
except ImportError:
    # Try backported to PY<37 `importlib_resources`.
    import importlib_resources as pkg_resources

import BFEE2.version
from BFEE2 import doc

__PROGRAM_NAME__ = f'BFEEstimator v{BFEE2.version.__VERSION__}'

[docs]class mainSettings(QWidget): """settings in the menubar set pathes of third-party softwares (VMD, gmx and tleap) """ def __init__(self): super().__init__() self.config_dir = user_config_dir('BFEE2', 'chinfo') # test if config directory exists if not os.path.exists(self.config_dir): # create it if not exists os.makedirs(self.config_dir) self._initUI() self._initSingalsSlots() self.setWindowTitle('Settings') self._readConfig() #self.setGeometry(0,0,0,0) #self.show() def _initUI(self): """settings GUI """ self.mainLayout = QVBoxLayout() self.thirdPartySoftware = QGroupBox('Third party software:') self.thirdPartySoftwareLayout = QVBoxLayout() # settings grid self.settingsGridLayout = QGridLayout() # vmd self.vmdLabel = QLabel('VMD:') self.vmdLineEdit = QLineEdit() self.vmdButton = QPushButton('Browse') self.settingsGridLayout.addWidget(self.vmdLabel, 0, 0) self.settingsGridLayout.addWidget(self.vmdLineEdit, 0, 1) self.settingsGridLayout.addWidget(self.vmdButton, 0, 2) # gmx #self.gromacsLayout = QHBoxLayout() #self.gromacsLabel = QLabel('Gromacs:') #self.gromacsLineEdit = QLineEdit() #self.gromacsButton = QPushButton('Browse') #self.settingsGridLayout.addWidget(self.gromacsLabel, 1, 0) #self.settingsGridLayout.addWidget(self.gromacsLineEdit,1 ,1) #self.settingsGridLayout.addWidget(self.gromacsButton, 1, 2) # tleap #self.tleapLayout = QHBoxLayout() #self.tleapLabel = QLabel('tleap:') #self.tleapLineEdit = QLineEdit() #self.tleapButton = QPushButton('Browse') #self.settingsGridLayout.addWidget(self.tleapLabel, 2, 0) #self.settingsGridLayout.addWidget(self.tleapLineEdit, 2, 1) #self.settingsGridLayout.addWidget(self.tleapButton, 2, 2) # OK and Cancel self.settingsButtonLayout = QHBoxLayout() self.settingsOKButton = QPushButton('OK') #self.settingsCancelButton = QPushButton('Cancel') self.settingsButtonLayout.addWidget(QSplitter()) self.settingsButtonLayout.addWidget(self.settingsOKButton) #self.settingsButtonLayout.addWidget(self.settingsCancelButton) self.thirdPartySoftwareLayout.addLayout(self.settingsGridLayout) self.thirdPartySoftwareLayout.addLayout(self.settingsButtonLayout) self.thirdPartySoftware.setLayout(self.thirdPartySoftwareLayout) self.mainLayout.addWidget(self.thirdPartySoftware) self.setLayout(self.mainLayout) def _initSingalsSlots(self): """initialize singals and slots """ self.vmdButton.clicked.connect(commonSlots.openFileDialog('exe', self.vmdLineEdit)) #self.gromacsButton.clicked.connect(commonSlots.openFileDialog('exe', self.gromacsLineEdit)) #self.tleapButton.clicked.connect(commonSlots.openFileDialog('exe', self.tleapLineEdit)) self.settingsOKButton.clicked.connect(self._OKSlot()) def _readConfig(self): """read the config saving paths for third-party softwares """ if not os.path.exists(f'{self.config_dir}/3rdSoft.ini'): return with open(f'{self.config_dir}/3rdSoft.ini', 'r') as cFile: line = cFile.readline() self.vmdLineEdit.setText(line.strip()) #line = cFile.readline() #self.gromacsLineEdit.setText(line.strip()) #line = cFile.readline() #self.tleapLineEdit.setText(line.strip()) def _writeConfig(self): """write the config saving paths for third-party softwares """ with open(f'{self.config_dir}/3rdSoft.ini', 'w') as cFile: cFile.write(self.vmdLineEdit.text() + '\n') #cFile.write(self.gromacsLineEdit.text() + '\n') #cFile.write(self.tleapLineEdit.text() + '\n') def _OKSlot(self): """the slot corresponding the OK button """ def f(): self._writeConfig() self.close() return f
[docs]class geometricAdvancedSettings(QWidget): """advanced settings for the geometric route set pulling direction, non-standard solvent and number of stratification windows """ def __init__(self): super().__init__() self._initUI() self._initSingalsSlots() self.setWindowTitle('Geometric advanced settings') self.setGeometry(0,0,0,0) #self.show() def _initUI(self): """initialize UI for the geometric advanced settings """ self.mainLayout = QVBoxLayout() # user-defined pulling direction self.userDefinedDirection = QGroupBox('User-defined pulling direction') self.userDefinedDirectionLayout = QHBoxLayout() self.userDefinedDirectionLabel = QLabel('Reference:') self.userDefinedDirectionLineEdit = QLineEdit() self.userDefinedDirectionLayout.addWidget(self.userDefinedDirectionLabel) self.userDefinedDirectionLayout.addWidget(self.userDefinedDirectionLineEdit) self.userDefinedDirection.setLayout(self.userDefinedDirectionLayout) # non-standard solvent self.nonStandardSolvent = QGroupBox('User-provided large box') self.nonStandardSolventLayout = QGridLayout() self.nonStandardSolventPsfLabel = QLabel('psf/parm file:') self.nonStandardSolventPsfLineEdit = QLineEdit() self.nonStandardSolventPsfButton = QPushButton('Browse') self.nonStandardSolventPdbLabel = QLabel('pdb/rst7 file:') self.nonStandardSolventPdbLineEdit = QLineEdit() self.nonStandardSolventPdbButton = QPushButton('Browse') self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfLabel, 0, 0) self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfLineEdit, 0, 1) self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfButton, 0, 2) self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbLabel, 1, 0) self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbLineEdit, 1, 1) self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbButton, 1, 2) self.nonStandardSolvent.setLayout(self.nonStandardSolventLayout) # stratification self.stratification = QGroupBox('Stratification windows') self.stratificationLayout = QGridLayout() self.stratificationRMSDBoundLabel = QLabel('RMSD(Bound):') self.stratificationRMSDBoundLineEdit = QLineEdit('1') self.stratificationTheta = QLabel('Theta:') self.stratificationThetaLineEdit = QLineEdit('1') self.stratificationPhiLabel = QLabel('Phi:') self.stratificationPhiLineEdit = QLineEdit('1') self.stratificationPsiLabel = QLabel('Psi:') self.stratificationPsiLineEdit = QLineEdit('1') self.stratificationthetaLabel = QLabel('theta:') self.stratificationthetaLineEdit = QLineEdit('1') self.stratificationphiLabel = QLabel('phi:') self.stratificationphiLineEdit = QLineEdit('1') self.stratificationRLabel = QLabel('r:') self.stratificationRLineEdit = QLineEdit('1') self.stratificationRMSDUnboundLabel = QLabel('RMSD(Unbound):') self.stratificationRMSDUnboundLineEdit = QLineEdit('1') self.stratificationLayout.addWidget(self.stratificationRMSDBoundLabel, 0, 0) self.stratificationLayout.addWidget(self.stratificationRMSDBoundLineEdit, 0, 1) self.stratificationLayout.addWidget(self.stratificationTheta, 0, 2) self.stratificationLayout.addWidget(self.stratificationThetaLineEdit, 0, 3) self.stratificationLayout.addWidget(self.stratificationPhiLabel, 0, 4) self.stratificationLayout.addWidget(self.stratificationPhiLineEdit, 0, 5) self.stratificationLayout.addWidget(self.stratificationPsiLabel, 0, 6) self.stratificationLayout.addWidget(self.stratificationPsiLineEdit, 0, 7) self.stratificationLayout.addWidget(self.stratificationthetaLabel, 1, 0) self.stratificationLayout.addWidget(self.stratificationthetaLineEdit, 1, 1) self.stratificationLayout.addWidget(self.stratificationphiLabel, 1, 2) self.stratificationLayout.addWidget(self.stratificationphiLineEdit, 1, 3) self.stratificationLayout.addWidget(self.stratificationRLabel, 1, 4) self.stratificationLayout.addWidget(self.stratificationRLineEdit, 1, 5) self.stratificationLayout.addWidget(self.stratificationRMSDUnboundLabel, 1, 6) self.stratificationLayout.addWidget(self.stratificationRMSDUnboundLineEdit, 1, 7) self.stratification.setLayout(self.stratificationLayout) # pinning down the protein self.pinDownPro = QGroupBox('Pinning down the protein') self.pinDownProLayout = QHBoxLayout() self.pinDownProCheckbox = QCheckBox('Pinning down the protein') self.pinDownProCheckbox.setChecked(True) self.pinDownProLayout.addWidget(self.pinDownProCheckbox) self.pinDownPro.setLayout(self.pinDownProLayout) # membrane protein self.modeling = QGroupBox('Modeling (avaiable for CHARMM FF)') self.modelingLayout = QVBoxLayout() self.memProCheckbox = QCheckBox('Membrane protein') self.memProCheckbox.setChecked(False) self.neutralizeLigOnlyLayout = QHBoxLayout() self.neutralizeLigOnlyLabel = QLabel('Auto-neutralize ligand-only system by:') self.neutralizeLigOnlyCombobox = QComboBox() self.neutralizeLigOnlyCombobox.addItem('NaCl') self.neutralizeLigOnlyCombobox.addItem('KCl') self.neutralizeLigOnlyCombobox.addItem('CaCl2') self.neutralizeLigOnlyCombobox.addItem('None') self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyLabel) self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyCombobox) self.modelingLayout.addWidget(self.memProCheckbox) self.modelingLayout.addLayout(self.neutralizeLigOnlyLayout) self.modeling.setLayout(self.modelingLayout) # parallel runs for error estimation self.parallelRuns = QGroupBox('Parallel runs') self.parallelRunsLayout = QHBoxLayout() self.parallelRunsLabel = QLabel('Number of parallel runs: ') self.parallelRunsLineEdit = QLineEdit('1') self.parallelRunsLayout.addWidget(self.parallelRunsLabel) self.parallelRunsLayout.addWidget(self.parallelRunsLineEdit) self.parallelRuns.setLayout(self.parallelRunsLayout) self.geometricAdvancedSettingsButtonLayout = QHBoxLayout() self.geometricAdvancedSettingsOKButton = QPushButton('OK') #self.geometricAdvancedSettingsCancelButton = QPushButton('Cancel') self.geometricAdvancedSettingsButtonLayout.addWidget(QSplitter()) self.geometricAdvancedSettingsButtonLayout.addWidget(self.geometricAdvancedSettingsOKButton) #self.geometricAdvancedSettingsButtonLayout.addWidget(self.geometricAdvancedSettingsCancelButton) self.mainLayout.addWidget(self.userDefinedDirection) self.mainLayout.addWidget(self.nonStandardSolvent) self.mainLayout.addWidget(self.stratification) self.mainLayout.addWidget(self.pinDownPro) self.mainLayout.addWidget(self.modeling) self.mainLayout.addWidget(self.parallelRuns) self.mainLayout.addLayout(self.geometricAdvancedSettingsButtonLayout) self.setLayout(self.mainLayout) def _initSingalsSlots(self): """initialize (connect) signial and slots for geometric advanced settings """ self.nonStandardSolventPsfButton.clicked.connect( commonSlots.openFileDialog( 'psf/parm/top', self.nonStandardSolventPsfLineEdit ) ) self.nonStandardSolventPdbButton.clicked.connect( commonSlots.openFileDialog( 'pdb/gro', self.nonStandardSolventPdbLineEdit ) ) self.geometricAdvancedSettingsOKButton.clicked.connect(self.close)
[docs]class alchemicalAdvancedSettings(QWidget): """advanced settings for the alchemical route set the number of stratification windows """ def __init__(self): super().__init__() self._initUI() self._initSingalsSlots() self.setWindowTitle('Alchemical advanced settings') self.setGeometry(0,0,0,0) #self.show() def _initUI(self): """initialize UI for the alchemical advanced settings """ self.mainLayout = QVBoxLayout() # stratification windows self.stratification = QGroupBox('Stratification windows') self.stratificationLayout = QGridLayout() self.boundLigandLabel = QLabel('Ligand/Bound state:') self.boundLigandLineEdit = QLineEdit('50') self.unboundLigandLabel = QLabel('Ligand/Unbound state:') self.unboundLigandLineEdit = QLineEdit('20') self.boundRestraintsLabel = QLabel('Restraints/Bound state:') self.boundRestraintsLineEdit = QLineEdit('50') self.unboundRestraintsLabel = QLabel('Restraints/Unbound state:') self.unboundRestraintsLineEdit = QLineEdit('20') self.stratificationLayout.addWidget(self.boundLigandLabel, 0, 0) self.stratificationLayout.addWidget(self.boundLigandLineEdit, 0, 1) self.stratificationLayout.addWidget(self.unboundLigandLabel, 0, 2) self.stratificationLayout.addWidget(self.unboundLigandLineEdit, 0, 3) self.stratificationLayout.addWidget(self.boundRestraintsLabel, 1, 0) self.stratificationLayout.addWidget(self.boundRestraintsLineEdit, 1, 1) self.stratificationLayout.addWidget(self.unboundRestraintsLabel, 1, 2) self.stratificationLayout.addWidget(self.unboundRestraintsLineEdit, 1, 3) self.stratification.setLayout(self.stratificationLayout) # double-wide simulation self.doubleWide = QGroupBox('Double-wide simulation') self.doubleWideLayout = QGridLayout() self.doubleWideCheckbox = QCheckBox('Generate input files for double-wide simulations') self.doubleWideCheckbox.setChecked(False) self.doubleWideLayout.addWidget(self.doubleWideCheckbox) self.doubleWide.setLayout(self.doubleWideLayout) # minimize before sampling in each window self.minBeforeSample = QGroupBox('Minimization before sampling') self.minBeforeSampleLayout = QVBoxLayout() self.minBeforeSampleCheckbox = QCheckBox('Minimize before sampling in each window') self.minBeforeSampleCheckbox.setChecked(False) self.minBeforeSampleLayout.addWidget(self.minBeforeSampleCheckbox) self.minBeforeSample.setLayout(self.minBeforeSampleLayout) # pinning down the protein self.pinDownPro = QGroupBox('Pinning down the protein') self.pinDownProLayout = QHBoxLayout() self.pinDownProCheckbox = QCheckBox('Pinning down the protein') self.pinDownProCheckbox.setChecked(True) self.pinDownProLayout.addWidget(self.pinDownProCheckbox) self.pinDownPro.setLayout(self.pinDownProLayout) # membrane protein self.modeling = QGroupBox('Modeling (avaiable for CHARMM FF)') self.modelingLayout = QHBoxLayout() self.memProCheckbox = QCheckBox('Membrane protein') self.memProCheckbox.setChecked(False) self.neutralizeLigOnlyLayout = QHBoxLayout() self.neutralizeLigOnlyLabel = QLabel('Auto-neutralize ligand-only system by:') self.neutralizeLigOnlyCombobox = QComboBox() self.neutralizeLigOnlyCombobox.addItem('NaCl') self.neutralizeLigOnlyCombobox.addItem('KCl') self.neutralizeLigOnlyCombobox.addItem('CaCl2') self.neutralizeLigOnlyCombobox.addItem('None') self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyLabel) self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyCombobox) self.modelingLayout.addWidget(self.memProCheckbox) self.modelingLayout.addLayout(self.neutralizeLigOnlyLayout) self.modeling.setLayout(self.modelingLayout) self.alchemicalAdvancedSettingsButtonLayout = QHBoxLayout() self.alchemicalAdvancedSettingsOKButton = QPushButton('OK') #self.alchemicalAdvancedSettingsCancelButton = QPushButton('Cancel') self.alchemicalAdvancedSettingsButtonLayout.addWidget(QSplitter()) self.alchemicalAdvancedSettingsButtonLayout.addWidget(self.alchemicalAdvancedSettingsOKButton) #self.alchemicalAdvancedSettingsButtonLayout.addWidget(self.alchemicalAdvancedSettingsCancelButton) self.mainLayout.addWidget(self.stratification) self.mainLayout.addWidget(self.doubleWide) self.mainLayout.addWidget(self.pinDownPro) self.mainLayout.addWidget(self.minBeforeSample) self.mainLayout.addWidget(self.modeling) self.mainLayout.addLayout(self.alchemicalAdvancedSettingsButtonLayout) self.setLayout(self.mainLayout) def _initSingalsSlots(self): """initialize (connect) signals and slots for the alchemical advanced settings """ self.alchemicalAdvancedSettingsOKButton.clicked.connect(self.close)
[docs]class mainUI(QMainWindow): """the main window UI include the preTreatment, postTreatment and QuickPlot tab the preTreatment tab include NAMD and Gromacs tab the postTreatment tab include geometric and alchemical tab """ def __init__(self): super().__init__() self._initActions() self._initNAMDTab() self._initGromacsTab() self._initPreTreatmentTab() self._initQuickPlotTab() self._initGeometricTab() self._initAlchemicalTab() self._initPostTreatmentTab() self._initSingalsSlots() self._initMainUI() # other dialogs self.mainSettings = mainSettings() self.alchemicalAdvancedSettings = alchemicalAdvancedSettings() self.geometricAdvancedSettings = geometricAdvancedSettings() self.setGeometry(0,0,0,0) self.setWindowTitle(__PROGRAM_NAME__) self.show() def _initActions(self): ''' initialize actions for menubar ''' # settings self.settingsAction = QAction('&Settings', self) self.settingsAction.setStatusTip('Set pathes for third-party softwares') self.settingsAction.triggered.connect(self._mainSettings()) # exit self.exitAction = QAction('&Exit', self) self.exitAction.setStatusTip('Exit application') self.exitAction.triggered.connect(QApplication.quit) # help self.helpAction = QAction('&Help', self) self.helpAction.setStatusTip('Open user manual') self.helpAction.triggered.connect(self._openDocFile) # python API self.pythonAPIAction = QAction('&Python API', self) self.pythonAPIAction.setStatusTip('Open Python API Documentation') self.pythonAPIAction.triggered.connect(self._openPythonAPIFile) # about self.aboutAction = QAction('&About', self) self.aboutAction.setStatusTip('About BFEEstimator') self.aboutAction.triggered.connect(self._showAboutBox()) def _initMainUI(self): """initialize main window """ # status bar #self.statusBar() # menu bar menubar = self.menuBar() menubar.setNativeMenuBar(False) self.fileMenu = menubar.addMenu('&File') self.fileMenu.addAction(self.settingsAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.exitAction) self.helpMenu = menubar.addMenu('&Help') self.helpMenu.addAction(self.helpAction) self.helpMenu.addAction(self.pythonAPIAction) self.helpMenu.addSeparator() self.helpMenu.addAction(self.aboutAction) # main layout self.mainLayout = QVBoxLayout() # title self.title = QLabel('Binding Free Energy Estimator') titleFont = QFont() titleFont.setBold(True) self.title.setFont(titleFont) self.titleBox = QGroupBox() self.titleBoxLayout = QVBoxLayout() self.titleBoxLayout.addWidget(self.title, alignment=QtCore.Qt.AlignCenter) self.titleBox.setLayout(self.titleBoxLayout) # tabs self.mainTabs = QTabWidget() #self.preTreatmentTab = QWidget() #self.postTreatmentTab = QWidget() #self.quickPlot = QWidget() self.mainTabs.addTab(self.preTreatmentTab, 'Pre-treatment') self.mainTabs.addTab(self.postTreatmentTab, 'Post-treatment') self.mainTabs.addTab(self.quickPlot, 'Quick-Plot') # main layout #self.mainLayout.addWidget(self.titleBox) self.mainLayout.addWidget(self.mainTabs) self.mainWidgit = QWidget() self.mainWidgit.setLayout(self.mainLayout) self.setCentralWidget(self.mainWidgit) def _initPreTreatmentTab(self): """initialize pre-treatment tab """ self.preTreatmentTab = QWidget() # pre-treatment tabs # NAMD and gromacs self.preTreatmentMainTabs = QTabWidget() self.preTreatmentMainTabs.addTab(self.NAMDTab, 'NAMD') self.preTreatmentMainTabs.addTab(self.GromacsTab, 'Gromacs') self.preTreatmentMainLayout = QVBoxLayout() self.preTreatmentMainLayout.addWidget(self.preTreatmentMainTabs) # other parameters self.otherParameters = QGroupBox('Other parameters') self.otherParametersLayout = QVBoxLayout() # temperature, selection protein and ligand layout self.otherParametersChildLayout = QGridLayout() # temperature self.temperatureLabel = QLabel('Temperature: ') self.temperatureLineEdit = QLineEdit('300') self.otherParametersChildLayout.addWidget(self.temperatureLabel, 0, 0) self.otherParametersChildLayout.addWidget(self.temperatureLineEdit, 0, 1) # select protein self.selectProteinLabel = QLabel('Select protein: ') self.selectProteineLineEdit = QLineEdit('segid SH3D') self.otherParametersChildLayout.addWidget(self.selectProteinLabel, 1, 0) self.otherParametersChildLayout.addWidget(self.selectProteineLineEdit, 1, 1) # select ligand self.selectLigandLabel = QLabel('Select ligand: ') self.selectLigandLineEdit = QLineEdit('segid PPRO') self.otherParametersChildLayout.addWidget(self.selectLigandLabel, 2, 0) self.otherParametersChildLayout.addWidget(self.selectLigandLineEdit, 2, 1) # select strategy self.selectStrategyLayout = QHBoxLayout() self.selectStrategyLabel = QLabel('Select strategy: ') self.selectStrategyCombobox = QComboBox() self.selectStrategyCombobox.addItem('Geometric') self.selectStrategyCombobox.addItem('Alchemical') self.selectStrategyAdvancedButton = QPushButton('Advanced settings') self.selectStrategyChildLayout = QHBoxLayout() self.selectStrategyChildLayout.addWidget(self.selectStrategyCombobox) self.selectStrategyChildLayout.addWidget(self.selectStrategyAdvancedButton) self.selectStrategyLayout.addWidget(self.selectStrategyLabel) self.selectStrategyLayout.addLayout(self.selectStrategyChildLayout) # generate input button self.generateInputButton = QPushButton('Generate Inputs') self.otherParametersLayout.addLayout(self.otherParametersChildLayout) self.otherParametersLayout.addLayout(self.selectStrategyLayout) self.otherParameters.setLayout(self.otherParametersLayout) self.preTreatmentMainLayout.addWidget(self.otherParameters) self.preTreatmentMainLayout.addWidget(self.generateInputButton) self.preTreatmentTab.setLayout(self.preTreatmentMainLayout) def _initNAMDTab(self): """initialize NAMD Tab in pre-treatment Tab """ self.NAMDTab = QWidget() self.NAMDTabMainLayout = QVBoxLayout() # inputs for the complex self.inputsForComplex = QGroupBox('Inputs for complex') self.inputsForComplexLayout = QGridLayout() # psf/parm self.psfLabel = QLabel('psf/parm file:') self.psfLineEdit = QLineEdit() self.psfButton = QPushButton('Browse') self.inputsForComplexLayout.addWidget(self.psfLabel, 0, 0) self.inputsForComplexLayout.addWidget(self.psfLineEdit, 0, 1) self.inputsForComplexLayout.addWidget(self.psfButton, 0, 2) # coor self.coorLabel = QLabel('pdb/rst file:') self.coorLineEdit = QLineEdit() self.coorButton = QPushButton('Browse') self.inputsForComplexLayout.addWidget(self.coorLabel, 1, 0) self.inputsForComplexLayout.addWidget(self.coorLineEdit, 1, 1) self.inputsForComplexLayout.addWidget(self.coorButton, 1, 2) # force fields self.forceFields = QGroupBox('Force fields') self.forceFieldsLayout = QVBoxLayout() # force field type self.forceFieldTypeLayout = QHBoxLayout() self.forceFieldTypeLabel = QLabel('Force field type:') self.forceFieldCombobox = QComboBox() self.forceFieldCombobox.addItem('CHARMM') self.forceFieldCombobox.addItem('Amber') self.forceFieldTypeLayout.addWidget(self.forceFieldTypeLabel) self.forceFieldTypeLayout.addWidget(self.forceFieldCombobox) # CHARMM force field files self.forceFieldFilesLayout = QVBoxLayout() self.forceFieldFilesLabel = QLabel('Force field files:') self.forceFieldFilesBox = QListWidget() self.forceFieldFilesChildLayout = QHBoxLayout() self.forceFieldAddButton = QPushButton('Add') self.forceFieldClearButton = QPushButton('Clear') self.forceFieldFilesChildLayout.addWidget(self.forceFieldAddButton) self.forceFieldFilesChildLayout.addWidget(self.forceFieldClearButton) self.forceFieldFilesLayout.addWidget(self.forceFieldFilesLabel) self.forceFieldFilesLayout.addWidget(self.forceFieldFilesBox) self.forceFieldFilesLayout.addLayout(self.forceFieldFilesChildLayout) self.forceFieldsLayout.addLayout(self.forceFieldTypeLayout) self.forceFieldsLayout.addLayout(self.forceFieldFilesLayout) self.inputsForComplex.setLayout(self.inputsForComplexLayout) self.forceFields.setLayout(self.forceFieldsLayout) self.NAMDTabMainLayout.addWidget(self.inputsForComplex) self.NAMDTabMainLayout.addWidget(self.forceFields) self.NAMDTab.setLayout(self.NAMDTabMainLayout) def _initGromacsTab(self): """initialize GMX Tab in pre-treatment Tab """ self.GromacsTab = QWidget() self.GromacsTabMainLayout = QVBoxLayout() # inputs for the complex self.GromacsInputsForComplex = QGroupBox('Inputs for complex') self.GromacsInputsForComplexLayout = QGridLayout() # top self.topLabel = QLabel('top file: ') self.topLineEdit = QLineEdit() self.topButton = QPushButton('Browse') self.GromacsInputsForComplexLayout.addWidget(self.topLabel, 0, 0) self.GromacsInputsForComplexLayout.addWidget(self.topLineEdit, 0, 1) self.GromacsInputsForComplexLayout.addWidget(self.topButton, 0, 2) # gro self.gromacsPdbLabel = QLabel('pdb file: ') self.gromacsPdbLineEdit = QLineEdit() self.gromacsPdbButton = QPushButton('Browse') self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbLabel, 1, 0) self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbLineEdit, 1, 1) self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbButton, 1, 2) self.GromacsInputsForComplexLayout.addWidget(QSplitter(), 2, 1) self.GromacsInputsForComplex.setLayout(self.GromacsInputsForComplexLayout) # inputs for the ligand-only system self.GromacsInputsLigandOnly = QGroupBox('Inputs for ligand-only system') self.GromacsInputsLigandOnlyLayout = QGridLayout() # top self.gromacsLigandOnlyTopLabel = QLabel('top file: ') self.gromacsLigandOnlyTopLineEdit = QLineEdit() self.gromacsLigandOnlyTopButton = QPushButton('Browse') self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopLabel, 0, 0) self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopLineEdit, 0, 1) self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopButton, 0, 2) # gro self.gromacsLigandOnlyPdbLabel = QLabel('pdb file: ') self.gromacsLigandOnlyPdbLineEdit = QLineEdit() self.gromacsLigandOnlyPdbButton = QPushButton('Browse') self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbLabel, 1, 0) self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbLineEdit, 1, 1) self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbButton, 1, 2) self.GromacsInputsLigandOnlyLayout.addWidget(QSplitter(), 2, 1) self.GromacsInputsLigandOnly.setLayout(self.GromacsInputsLigandOnlyLayout) self.GromacsTabMainLayout.addWidget(self.GromacsInputsForComplex) self.GromacsTabMainLayout.addWidget(self.GromacsInputsLigandOnly) self.GromacsTab.setLayout(self.GromacsTabMainLayout) def _initPostTreatmentTab(self): """initialize pre-treatment tab """ self.postTreatmentTab = QWidget() # post-treatment tabs # Geometric and alchemical self.postTreatmentMainTabs = QTabWidget() self.postTreatmentMainTabs.addTab(self.geometricTab, 'Geometric') self.postTreatmentMainTabs.addTab(self.alchemicalTab, 'Alchemical') self.postTreatmentMainLayout = QVBoxLayout() self.postTreatmentMainLayout.addWidget(self.postTreatmentMainTabs) self.calculateButton = QPushButton('Calculate binding free energy') self.postTreatmentMainLayout.addWidget(self.calculateButton) self.postTreatmentTab.setLayout(self.postTreatmentMainLayout) def _initGeometricTab(self): """initialize geometric tab of post-treatment """ self.geometricTab = QWidget() self.geometricTabLayout = QVBoxLayout() # pmf inputs self.pmfInputs = QGroupBox('PMF inputs (.czar.pmf/.UI.pmf):') self.pmfInputsLayout = QVBoxLayout() # bound stats self.boundStateLabel = QLabel('Bound state:') self.boundStateLayout = QGridLayout() # RMSD self.rmsdBoundLabel = QLabel('RMSD: ') self.rmsdBoundLineEdit = QLineEdit() self.rmsdBoundButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.rmsdBoundLabel, 0, 0) self.boundStateLayout.addWidget(self.rmsdBoundLineEdit, 0, 1) self.boundStateLayout.addWidget(self.rmsdBoundButton, 0, 2) # Theta self.ThetaLabel = QLabel('Theta:') self.ThetaLineEdit = QLineEdit() self.ThetaButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.ThetaLabel, 1, 0) self.boundStateLayout.addWidget(self.ThetaLineEdit, 1, 1) self.boundStateLayout.addWidget(self.ThetaButton, 1, 2) # Phi self.PhiLabel = QLabel('Phi: ') self.PhiLineEdit = QLineEdit() self.PhiButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.PhiLabel, 2, 0) self.boundStateLayout.addWidget(self.PhiLineEdit, 2, 1) self.boundStateLayout.addWidget(self.PhiButton, 2, 2) # Psi self.PsiLabel = QLabel('Psi: ') self.PsiLineEdit = QLineEdit() self.PsiButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.PsiLabel, 3, 0) self.boundStateLayout.addWidget(self.PsiLineEdit, 3, 1) self.boundStateLayout.addWidget(self.PsiButton, 3, 2) # theta self.thetaLayout = QHBoxLayout() self.thetaLabel = QLabel('theta:') self.thetaLineEdit = QLineEdit() self.thetaButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.thetaLabel, 4, 0) self.boundStateLayout.addWidget(self.thetaLineEdit, 4, 1) self.boundStateLayout.addWidget(self.thetaButton, 4, 2) # phi self.phiLayout = QHBoxLayout() self.phiLabel = QLabel('phi: ') self.phiLineEdit = QLineEdit() self.phiButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.phiLabel, 5, 0) self.boundStateLayout.addWidget(self.phiLineEdit, 5, 1) self.boundStateLayout.addWidget(self.phiButton, 5, 2) # r self.rLabel = QLabel('r: ') self.rLineEdit = QLineEdit() self.rButton = QPushButton('Browse') self.boundStateLayout.addWidget(self.rLabel, 6, 0) self.boundStateLayout.addWidget(self.rLineEdit, 6, 1) self.boundStateLayout.addWidget(self.rButton, 6, 2) self.unboundStateLabel = QLabel('Unbound state:') # RMSD unbound self.rmsdUnboundLayout = QHBoxLayout() self.rmsdUnboundLabel = QLabel('RMSD: ') self.rmsdUnboundLineEdit = QLineEdit() self.rmsdUnboundButton = QPushButton('Browse') self.rmsdUnboundLayout.addWidget(self.rmsdUnboundLabel) self.rmsdUnboundLayout.addWidget(self.rmsdUnboundLineEdit) self.rmsdUnboundLayout.addWidget(self.rmsdUnboundButton) self.pmfInputsLayout.addWidget(self.boundStateLabel) self.pmfInputsLayout.addLayout(self.boundStateLayout) self.pmfInputsLayout.addWidget(self.unboundStateLabel) self.pmfInputsLayout.addLayout(self.rmsdUnboundLayout) self.pmfInputs.setLayout(self.pmfInputsLayout) # force constants self.forceConstants = QGroupBox('Force constants (in Colvars unit):') self.forceConstantsLayout = QGridLayout() self.fcBoundStateLabel = QLabel('Bound state:') # all widgets self.fcRMSDLabel = QLabel('RMSD:') self.fcRMSDLineEdit = QLineEdit('10') self.fcThetaLabel = QLabel('Theta:') self.fcThetaLineEdit = QLineEdit('0.1') self.fcPhiLabel = QLabel('Phi:') self.fcPhiLineEdit = QLineEdit('0.1') self.fcPsiLabel = QLabel('Psi:') self.fcPsiLineEdit = QLineEdit('0.1') self.fcthetaLabel = QLabel('theta:') self.fcthetaLineEdit = QLineEdit('0.1') self.fcphiLabel = QLabel('phi:') self.fcphiLineEdit = QLineEdit('0.1') self.forceConstantsLayout.addWidget(self.fcRMSDLabel, 0, 0) self.forceConstantsLayout.addWidget(self.fcRMSDLineEdit, 0, 1) self.forceConstantsLayout.addWidget(self.fcThetaLabel, 0, 2) self.forceConstantsLayout.addWidget(self.fcThetaLineEdit, 0, 3) self.forceConstantsLayout.addWidget(self.fcPhiLabel, 0, 4) self.forceConstantsLayout.addWidget(self.fcPhiLineEdit, 0, 5) self.forceConstantsLayout.addWidget(self.fcPsiLabel, 1, 0) self.forceConstantsLayout.addWidget(self.fcPsiLineEdit, 1, 1) self.forceConstantsLayout.addWidget(self.fcthetaLabel, 1, 2) self.forceConstantsLayout.addWidget(self.fcthetaLineEdit, 1, 3) self.forceConstantsLayout.addWidget(self.fcphiLabel, 1, 4) self.forceConstantsLayout.addWidget(self.fcphiLineEdit, 1, 5) self.forceConstants.setLayout(self.forceConstantsLayout) # other parameters self.postOtherParams = QGroupBox('Other parameters:') self.postOtherParamsLayout = QHBoxLayout() self.postTemperatureLabel = QLabel('temperature:') self.postTemperatureLineEdit = QLineEdit('300') self.postRstarLabel = QLabel(' r*:') self.postRstarLineEdit = QLineEdit('30') self.postPMFTypeLabel = QLabel(' PMF type:') self.postPMFTypeBox = QComboBox() self.postPMFTypeBox.addItem('NAMD') self.postPMFTypeBox.addItem('Gromacs') self.postOtherParamsLayout.addWidget(self.postTemperatureLabel) self.postOtherParamsLayout.addWidget(self.postTemperatureLineEdit) self.postOtherParamsLayout.addWidget(self.postRstarLabel) self.postOtherParamsLayout.addWidget(self.postRstarLineEdit) self.postOtherParamsLayout.addWidget(self.postPMFTypeLabel) self.postOtherParamsLayout.addWidget(self.postPMFTypeBox) self.postOtherParams.setLayout(self.postOtherParamsLayout) self.fcBoundStateLabel = QLabel('Bound state:') self.geometricTabLayout.addWidget(self.pmfInputs) self.geometricTabLayout.addWidget(self.forceConstants) self.geometricTabLayout.addWidget(self.postOtherParams) self.geometricTab.setLayout(self.geometricTabLayout) def _initAlchemicalTab(self): """initialize alchemical tab of post-treatment """ self.alchemicalTab = QWidget() self.alchemicalTabLayout = QVBoxLayout() self.restraintInputs = QGroupBox('Inputs for alchemical simulations (.fepout/.log):') self.restraintInputsLayout = QVBoxLayout() # bound state self.alchemicalBoundStateLabel = QLabel('Atoms/Bound state (.fepout):') self.alchemicalBoundStateLayout = QGridLayout() self.alchemicalForwardLabel1 = QLabel('Forward:') self.alchemicalForwardLineEdit1 = QLineEdit() self.alchemicalForwardButton1 = QPushButton('Browse') self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardLabel1, 0, 0) self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardLineEdit1, 0, 1) self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardButton1, 0, 2) self.alchemicalBackwardLabel1 = QLabel('Backward:') self.alchemicalBackwardLineEdit1 = QLineEdit() self.alchemicalBackwardButton1 = QPushButton('Browse') self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardLabel1, 1, 0) self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardLineEdit1, 1, 1) self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardButton1, 1, 2) self.alchemicalBoundStateLabel2 = QLabel('Restraints/Bound state (.log):') self.alchemicalBoundStateLayout2 = QGridLayout() self.alchemicalForwardLabel2 = QLabel('Forward:') self.alchemicalForwardLineEdit2 = QLineEdit() self.alchemicalForwardButton2 = QPushButton('Browse') self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardLabel2, 0, 0) self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardLineEdit2, 0, 1) self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardButton2, 0, 2) self.alchemicalBackwardLabel2 = QLabel('Backward:') self.alchemicalBackwardLineEdit2 = QLineEdit() self.alchemicalBackwardButton2 = QPushButton('Browse') self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardLabel2, 1, 0) self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardLineEdit2, 1, 1) self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardButton2, 1, 2) # unbound state self.alchemicalUnboundStateLabel = QLabel('Atoms/Unbound state (.fepout):') self.alchemicalUnboundStateLayout = QGridLayout() self.alchemicalForwardLabel3 = QLabel('Forward:') self.alchemicalForwardLineEdit3 = QLineEdit() self.alchemicalForwardButton3 = QPushButton('Browse') self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardLabel3, 0, 0) self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardLineEdit3, 0, 1) self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardButton3, 0, 2) self.alchemicalBackwardLabel3 = QLabel('Backward:') self.alchemicalBackwardLineEdit3 = QLineEdit() self.alchemicalBackwardButton3 = QPushButton('Browse') self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardLabel3, 1, 0) self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardLineEdit3, 1, 1) self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardButton3, 1, 2) self.alchemicalUnboundStateLabel2 = QLabel('Restraints/Unbound state (.log):') self.alchemicalUnboundStateLayout2 = QGridLayout() self.alchemicalForwardLabel4 = QLabel('Forward:') self.alchemicalForwardLineEdit4 = QLineEdit() self.alchemicalForwardButton4 = QPushButton('Browse') self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardLabel4, 0, 0) self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardLineEdit4, 0, 1) self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardButton4, 0, 2) self.alchemicalBackwardLabel4 = QLabel('Backward:') self.alchemicalBackwardLineEdit4 = QLineEdit() self.alchemicalBackwardButton4 = QPushButton('Browse') self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardLabel4, 1, 0) self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardLineEdit4, 1, 1) self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardButton4, 1, 2) self.restraintInputsLayout.addWidget(self.alchemicalBoundStateLabel) self.restraintInputsLayout.addLayout(self.alchemicalBoundStateLayout) self.restraintInputsLayout.addWidget(self.alchemicalBoundStateLabel2) self.restraintInputsLayout.addLayout(self.alchemicalBoundStateLayout2) self.restraintInputsLayout.addWidget(self.alchemicalUnboundStateLabel) self.restraintInputsLayout.addLayout(self.alchemicalUnboundStateLayout) self.restraintInputsLayout.addWidget(self.alchemicalUnboundStateLabel2) self.restraintInputsLayout.addLayout(self.alchemicalUnboundStateLayout2) self.restraintInputs.setLayout(self.restraintInputsLayout) # alchemical force constants self.alchemicalForceConstants = QGroupBox('Force constants (in Colvars unit):') # all widgets self.alchemicalFCLayout = QHBoxLayout() self.alchemicalfcThetaLabel = QLabel('Theta:') self.alchemicalfcThetaLineEdit = QLineEdit('0.1') self.alchemicalfcPhiLabel = QLabel(' Phi: ') self.alchemicalfcPhiLineEdit = QLineEdit('0.1') self.alchemicalfcPsiLabel = QLabel('Psi: ') self.alchemicalfcPsiLineEdit = QLineEdit('0.1') self.alchemicalfcthetaLabel = QLabel('theta:') self.alchemicalfcthetaLineEdit = QLineEdit('0.1') self.alchemicalfcphiLabel = QLabel(' phi: ') self.alchemicalfcphiLineEdit = QLineEdit('0.1') self.alchemicalfcRLabel = QLabel('r: ') self.alchemicalfcRLineEdit = QLineEdit('10') self.alchemicalFCLayout.addWidget(self.alchemicalfcThetaLabel) self.alchemicalFCLayout.addWidget(self.alchemicalfcThetaLineEdit) self.alchemicalFCLayout.addWidget(self.alchemicalfcPhiLabel) self.alchemicalFCLayout.addWidget(self.alchemicalfcPhiLineEdit) self.alchemicalFCLayout.addWidget(self.alchemicalfcPsiLabel) self.alchemicalFCLayout.addWidget(self.alchemicalfcPsiLineEdit) self.alchemicalFCLayout.addWidget(self.alchemicalfcthetaLabel) self.alchemicalFCLayout.addWidget(self.alchemicalfcthetaLineEdit) self.alchemicalFCLayout.addWidget(self.alchemicalfcphiLabel) self.alchemicalFCLayout.addWidget(self.alchemicalfcphiLineEdit) self.alchemicalFCLayout.addWidget(self.alchemicalfcRLabel) self.alchemicalFCLayout.addWidget(self.alchemicalfcRLineEdit) self.alchemicalForceConstants.setLayout(self.alchemicalFCLayout) # alchemical restraint centers self.alchemicalRestraintCenters = QGroupBox('Restraint centers (in Colvars unit) and temperature:') # all widgets self.alchemicalRCLayout = QHBoxLayout() self.alchemicalRCThetaLabel = QLabel('Theta:') self.alchemicalRCThetaLineEdit = QLineEdit('0') self.alchemicalRCthetaLabel = QLabel('theta:') self.alchemicalRCthetaLineEdit = QLineEdit('90') self.alchemicalRCRLabel = QLabel('r: ') self.alchemicalRCRLineEdit = QLineEdit('8') self.alchemicalPostTemperatureLabel = QLabel('temperature:') self.alchemicalPostTemperatureLineEdit = QLineEdit('300') self.alchemicalRCLayout.addWidget(self.alchemicalRCThetaLabel) self.alchemicalRCLayout.addWidget(self.alchemicalRCThetaLineEdit) self.alchemicalRCLayout.addWidget(self.alchemicalRCthetaLabel) self.alchemicalRCLayout.addWidget(self.alchemicalRCthetaLineEdit) self.alchemicalRCLayout.addWidget(self.alchemicalRCRLabel) self.alchemicalRCLayout.addWidget(self.alchemicalRCRLineEdit) self.alchemicalRCLayout.addWidget(self.alchemicalPostTemperatureLabel) self.alchemicalRCLayout.addWidget(self.alchemicalPostTemperatureLineEdit) self.alchemicalRestraintCenters.setLayout(self.alchemicalRCLayout) self.alchemicalTabLayout.addWidget(self.restraintInputs) self.alchemicalTabLayout.addWidget(self.alchemicalForceConstants) self.alchemicalTabLayout.addWidget(self.alchemicalRestraintCenters) self.alchemicalTab.setLayout(self.alchemicalTabLayout) def _initQuickPlotTab(self): """initialize quick-plot tab """ self.quickPlot = QWidget() self.quickPlotLayout = QVBoxLayout() # plot a (stratified) pmf self.plotPmf = QGroupBox('Plot (stratified) PMFs:') self.plotPmfLayout = QVBoxLayout() self.plotPmfLabel = QLabel('PMF files:') self.plotPmfBox = QListWidget() self.plotPmfChildLayout = QHBoxLayout() self.plotPmfAddButton = QPushButton('Add') self.plotPmfClearButton = QPushButton('Clear') self.plotPmfPlotButton = QPushButton('Plot') self.plotPmfChildLayout.addWidget(self.plotPmfAddButton) self.plotPmfChildLayout.addWidget(self.plotPmfClearButton) self.plotPmfChildLayout.addWidget(self.plotPmfPlotButton) self.plotPmfLayout.addWidget(self.plotPmfLabel) self.plotPmfLayout.addWidget(self.plotPmfBox) self.plotPmfLayout.addLayout(self.plotPmfChildLayout) self.plotPmf.setLayout(self.plotPmfLayout) # calculate pmf RMSD convergence self.plotPmfConvergence = QGroupBox('Calculate PMF RMSD convergence:') self.plotPmfConvergenceLayout = QVBoxLayout() self.plotPmfConvergenceLabel = QLabel('history file:') self.plotPmfConvergenceBox = QLineEdit() self.plotPmfConvergenceChildLayout = QHBoxLayout() self.plotPmfConvergenceBrowseButton = QPushButton('Browse') self.plotPmfConvergencePlotButton = QPushButton('Plot') self.plotPmfConvergenceChildLayout.addWidget(self.plotPmfConvergenceBrowseButton) self.plotPmfConvergenceChildLayout.addWidget(self.plotPmfConvergencePlotButton) self.plotPmfConvergenceLayout.addWidget(self.plotPmfConvergenceLabel) self.plotPmfConvergenceLayout.addWidget(self.plotPmfConvergenceBox) self.plotPmfConvergenceLayout.addLayout(self.plotPmfConvergenceChildLayout) self.plotPmfConvergence.setLayout(self.plotPmfConvergenceLayout) # merge a (stratified) pmf self.mergePmf = QGroupBox('Merge (stratified) PMFs:') self.mergePmfLayout = QVBoxLayout() self.mergePmfLabel = QLabel('PMF files:') self.mergePmfBox = QListWidget() self.mergePmfChildLayout = QHBoxLayout() self.mergePmfAddButton = QPushButton('Add') self.mergePmfClearButton = QPushButton('Clear') self.mergePmfmergeButton = QPushButton('Merge') self.mergePmfChildLayout.addWidget(self.mergePmfAddButton) self.mergePmfChildLayout.addWidget(self.mergePmfClearButton) self.mergePmfChildLayout.addWidget(self.mergePmfmergeButton) self.mergePmfLayout.addWidget(self.mergePmfLabel) self.mergePmfLayout.addWidget(self.mergePmfBox) self.mergePmfLayout.addLayout(self.mergePmfChildLayout) self.mergePmf.setLayout(self.mergePmfLayout) self.quickPlotLayout.addWidget(self.plotPmf) self.quickPlotLayout.addWidget(self.mergePmf) self.quickPlotLayout.addWidget(self.plotPmfConvergence) self.quickPlot.setLayout(self.quickPlotLayout) # slots are defined below # otherwise they are defined in slots.py def _mainSettings(self): """call main settings """ def f(): self.mainSettings.show() return f def _advancedSettings(self, comboBox): """call advanced settings in pre-treatment the returned function is depended on the comboBox(strategy type) """ def f(): if comboBox.currentText() == 'Geometric': self.geometricAdvancedSettings.show() elif comboBox.currentText() == 'Alchemical': self.alchemicalAdvancedSettings.show() return f def _changeFFButtonState(self): """enable/disable the add and clear button of force field section """ if self.forceFieldCombobox.currentText() == 'CHARMM': self.forceFieldAddButton.setEnabled(True) self.forceFieldClearButton.setEnabled(True) self.forceFieldFilesBox.setEnabled(True) elif self.forceFieldCombobox.currentText() == 'Amber': self.forceFieldAddButton.setEnabled(False) self.forceFieldClearButton.setEnabled(False) self.forceFieldFilesBox.setEnabled(False) def _openDocFile(self): """open Documentation file """ with pkg_resources.path(doc, 'Doc.pdf') as docFile: webbrowser.open('file:///' + str(docFile)) def _openPythonAPIFile(self): """open Python API Documentation file """ with pkg_resources.path(doc, 'PythonAPI.pdf') as pythonAPIFile: webbrowser.open('file:///' + str(pythonAPIFile)) def _showAboutBox(self): """the about message box Returns: function obj: a slot function that shows that about message box """ def f(): QMessageBox.about( self, 'About', f'<center><b>{__PROGRAM_NAME__}</b></center><br>'+r''' Binding free energy estimator (BFEE) is a python-based software that automates absolute binding free energy calculations through either the alchemical or geometric route by molecular dynamics simulations.<br> <b>Authors:</b><br> Haohao Fu (<a href="mailto:fhh2626@gmail.com">fhh2626@gmail.com</a>)<br> Haochuan Chen (<a href="mailto:summersnow9403@gmail.com">summersnow9403@gmail.com</a>)<br> <b>License:</b><br> BFEE2 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 3 of the License, or (at your option) any later version.<br> <b>Contact</b> Wensheng Cai (<a href="mailto:wscai@nankai.edu.cn">wscai@nankai.edu.cn</a>) and Chris Chipot (<a href="mailto:chipot@ks.uiuc.edu">chipot@ks.uiuc.edu</a>) for further copyright information. ''') return f def _showGeometricResults(self, unit): ''' calculate binding from the geometric route, parameters in the Geometric tab will be read. Show a QMessageBox for the result Inputs: unit (string): 'namd' or 'gromacs' ''' pTreat = postTreatment.postTreatment( float(self.postTemperatureLineEdit.text()), unit, 'geometric') pmfs = [ self.rmsdBoundLineEdit.text(), self.ThetaLineEdit.text(), self.PhiLineEdit.text(), self.PsiLineEdit.text(), self.thetaLineEdit.text(), self.phiLineEdit.text(), self.rLineEdit.text(), self.rmsdUnboundLineEdit.text() ] try: parameters = [ float(self.fcRMSDLineEdit.text()), float(self.fcThetaLineEdit.text()), float(self.fcPhiLineEdit.text()), float(self.fcPsiLineEdit.text()), float(self.fcthetaLineEdit.text()), float(self.fcphiLineEdit.text()), float(self.postRstarLineEdit.text()), float(self.fcRMSDLineEdit.text()) ] except: QMessageBox.warning(self, 'Error', f'Force constant or r* input error!') return # check inputs for item in pmfs: if not os.path.exists(item): QMessageBox.warning(self, 'Error', f'file {item} does not exist!') return # calculate free energies result = pTreat.geometricBindingFreeEnergy(pmfs, parameters) QMessageBox.about( self, 'Result', f'\ Results:\n\ ΔG(site,c) = {result[0]:.2f} kcal/mol\n\ ΔG(site,eulerTheta) = {result[1]:.2f} kcal/mol\n\ ΔG(site,eulerPhi) = {result[2]:.2f} kcal/mol\n\ ΔG(site,eulerPsi) = {result[3]:.2f} kcal/mol\n\ ΔG(site,polarTheta) = {result[4]:.2f} kcal/mol\n\ ΔG(site,polarPhi) = {result[5]:.2f} kcal/mol\n\ (1/beta)*ln(S*I*C0) = {result[6]:.2f} kcal/mol\n\ ΔG(bulk,c) = {result[7]:.2f} kcal/mol\n\ ΔG(bulk,o) = {result[8]:.2f} kcal/mol\n\ \n\ Standard Binding Free Energy:\n\ ΔG(total) = {result[9]:.2f} kcal/mol\n' ) def _showAlchemicalResults(self, unit): """calculate binding from the alchemical route, parameters in the alchemical tab will be read. Show a QMessageBox for the result Args: unit (str): 'namd' or 'gromacs' """ pTreat = postTreatment.postTreatment( float(self.alchemicalPostTemperatureLineEdit.text()), unit, 'geometric') # alchemical outputs files = [ self.alchemicalForwardLineEdit1.text(), self.alchemicalBackwardLineEdit1.text(), self.alchemicalForwardLineEdit2.text(), self.alchemicalBackwardLineEdit2.text(), self.alchemicalForwardLineEdit3.text(), self.alchemicalBackwardLineEdit3.text(), self.alchemicalForwardLineEdit4.text(), self.alchemicalBackwardLineEdit4.text() ] try: parameters = [ float(self.alchemicalRCThetaLineEdit.text()), float(self.alchemicalRCthetaLineEdit.text()), float(self.alchemicalRCRLineEdit.text()), float(self.alchemicalfcThetaLineEdit.text()), float(self.alchemicalfcPhiLineEdit.text()), float(self.alchemicalfcPsiLineEdit.text()), float(self.alchemicalfcthetaLineEdit.text()), float(self.alchemicalfcphiLineEdit.text()), float(self.alchemicalfcRLineEdit.text()) ] except: QMessageBox.warning(self, 'Error', f'Force constant or restraint center input error!') return # check inputs for item in [ self.alchemicalBackwardLineEdit1.text(), self.alchemicalBackwardLineEdit2.text(), self.alchemicalBackwardLineEdit3.text(), self.alchemicalBackwardLineEdit4.text() ]: if (not os.path.exists(item)) and item != '': QMessageBox.warning(self, 'Error', f'backward file {item} does not exist and is not empty!') return for item in [ self.alchemicalForwardLineEdit1.text(), self.alchemicalForwardLineEdit2.text(), self.alchemicalForwardLineEdit3.text(), self.alchemicalForwardLineEdit4.text(), ]: if not os.path.exists(item): QMessageBox.warning(self, 'Error', f'file {item} does not exist!') return # calculate free energies result, errors = pTreat.alchemicalBindingFreeEnergy(files, parameters) QMessageBox.about( self, 'Result', f'\ Results:\n\ ΔG(site,couple) = {result[0]:.2f} ± {errors[0]:.2f} kcal/mol\n\ ΔG(site,c+o+a+r) = {result[1]:.2f} ± {errors[1]:.2f} kcal/mol\n\ ΔG(bulk,decouple) = {result[2]:.2f} ± {errors[2]:.2f} kcal/mol\n\ ΔG(bulk,c) = {result[3]:.2f} ± {errors[3]:.2f} kcal/mol\n\ ΔG(bulk,o+a+r) = {result[4]:.2f} kcal/mol\n\ \n\ Standard Binding Free Energy:\n\ ΔG(total) = {result[5]:.2f} ± {errors[5]:.2f} kcal/mol\n' ) def _showFinalResults(self): """calculate binding free energy and show the final results Returns: function obj: a slot function that calculates binding free energy and show the final results """ def f(): if self.postPMFTypeBox.currentText() == 'NAMD': unit = 'namd' elif self.postPMFTypeBox.currentText() == 'Gromacs': unit = 'gromacs' if self.postTreatmentMainTabs.currentIndex() == 0: jobType = 'geometric' elif self.postTreatmentMainTabs.currentIndex() == 1: jobType = 'alchemical' if jobType == 'geometric': self._showGeometricResults(unit) elif jobType == 'alchemical': self._showAlchemicalResults(unit) return f def _generateInputFiles(self): """generate input files for binding free energy simulation Returens: function obj: a slot function that generates input files for binding free energy simulation """ def f(): path = QFileDialog.getExistingDirectory(None, 'Select a directory') # cancel if path == '': return iGenerator = inputGenerator.inputGenerator() # third-party softwares and user-provided solvation boxes for item in [ self.mainSettings.vmdLineEdit.text(), #self.mainSettings.gromacsLineEdit.text(), #self.mainSettings.tleapLineEdit.text(), self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text(), self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text() ]: if ((not os.path.exists(item)) and item != ''): QMessageBox.warning(self, 'Error', f'file {item} does not exist!') return if self.preTreatmentMainTabs.currentIndex() == 0: # force fields forceFieldFiles = [self.forceFieldFilesBox.item(i).text() for i in range(self.forceFieldFilesBox.count())] # NAMD files for item in [self.psfLineEdit.text(), self.coorLineEdit.text()] + forceFieldFiles: if not os.path.exists(item): QMessageBox.warning(self, 'Error', f'file {item} does not exist!') return # check inputs try: float(self.temperatureLineEdit.text()) stratification = [ int(self.geometricAdvancedSettings.stratificationRMSDBoundLineEdit.text()), int(self.geometricAdvancedSettings.stratificationThetaLineEdit.text()), int(self.geometricAdvancedSettings.stratificationPhiLineEdit.text()), int(self.geometricAdvancedSettings.stratificationPsiLineEdit.text()), int(self.geometricAdvancedSettings.stratificationthetaLineEdit.text()), int(self.geometricAdvancedSettings.stratificationphiLineEdit.text()), int(self.geometricAdvancedSettings.stratificationRLineEdit.text()), int(self.geometricAdvancedSettings.stratificationRMSDUnboundLineEdit.text()) ] alchemicalStratification = [ int(self.alchemicalAdvancedSettings.boundLigandLineEdit.text()), int(self.alchemicalAdvancedSettings.boundRestraintsLineEdit.text()), int(self.alchemicalAdvancedSettings.unboundLigandLineEdit.text()), int(self.alchemicalAdvancedSettings.unboundRestraintsLineEdit.text()) ] except: QMessageBox.warning(self, 'Error', f'Force constant or r* input error!') return # job type if self.forceFieldCombobox.currentText() == 'CHARMM': forceFieldType = 'charmm' elif self.forceFieldCombobox.currentText() == 'Amber': forceFieldType = 'amber' # make sure there are CHARMM FF files if forceFieldFiles == [] and forceFieldType == 'charmm': QMessageBox.warning( self, 'Error', f'\ CHARMM force field files must be specified!' ) return if self.selectStrategyCombobox.currentText() == 'Geometric': # for the amber force field, files of large box must be provided if forceFieldType == 'amber': if self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text() == '' or \ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text() == '': QMessageBox.warning( self, 'Error', f'\ Coordinate and topology files of large box must be \ provided in "Advanced Settings"when using the Amber \ force fields!' ) return try: iGenerator.generateNAMDGeometricFiles( path, self.psfLineEdit.text(), self.coorLineEdit.text(), forceFieldType, forceFieldFiles, float(self.temperatureLineEdit.text()), self.selectProteineLineEdit.text(), self.selectLigandLineEdit.text(), self.geometricAdvancedSettings.userDefinedDirectionLineEdit.text(), self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text(), self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text(), stratification, self.geometricAdvancedSettings.memProCheckbox.isChecked(), self.geometricAdvancedSettings.neutralizeLigOnlyCombobox.currentText(), self.geometricAdvancedSettings.pinDownProCheckbox.isChecked(), int(self.geometricAdvancedSettings.parallelRunsLineEdit.text()), self.mainSettings.vmdLineEdit.text() ) except fileParser.SelectionError: QMessageBox.warning( self, 'Error', f'\ Selection corresponding to nothing!\n\ Check your selection again!' ) if os.path.exists(f'{path}/BFEE'): shutil.rmtree(f'{path}/BFEE') return except inputGenerator.DirectoryExistError: QMessageBox.warning( self, 'Error', f'\ ./BFEE* directory already exists!' ) return except inputGenerator.FileTypeUnknownError: QMessageBox.warning( self, 'Error', f'\ Unkwn input file types! The following file types are supported:\n\ psf, parm, prm7, parm7, prmtop, pdb, coor, rst, rst7, inpcrd ' ) return except PermissionError: QMessageBox.warning( self, 'Error', f'\ Cannot read input files due to the permission reason!\n\ Restart the program or check the authority of the files!' ) return except Exception as e: print(e) QMessageBox.warning( self, 'Error', f'\ Unknown error! The error message is: \n\ {e}\n' ) return elif self.selectStrategyCombobox.currentText() == 'Alchemical': try: iGenerator.generateNAMDAlchemicalFiles( path, self.psfLineEdit.text(), self.coorLineEdit.text(), forceFieldType, forceFieldFiles, float(self.temperatureLineEdit.text()), self.selectProteineLineEdit.text(), self.selectLigandLineEdit.text(), alchemicalStratification, self.alchemicalAdvancedSettings.doubleWideCheckbox.isChecked(), self.alchemicalAdvancedSettings.minBeforeSampleCheckbox.isChecked(), self.alchemicalAdvancedSettings.memProCheckbox.isChecked(), self.geometricAdvancedSettings.neutralizeLigOnlyCombobox.currentText(), self.alchemicalAdvancedSettings.pinDownProCheckbox.isChecked(), self.mainSettings.vmdLineEdit.text() ) except PermissionError: QMessageBox.warning( self, 'Error', f'\ Cannot read input files due to the permission reason!\n\ Restart the program or check the authority of the files!' ) return except fileParser.SelectionError: QMessageBox.warning( self, 'Error', f'\ Selection corresponding to nothing!\n\ Check your selection again!' ) if os.path.exists(f'{path}/BFEE'): shutil.rmtree(f'{path}/BFEE') return except inputGenerator.DirectoryExistError: QMessageBox.warning( self, 'Error', f'\ ./BFEE directory already exists!' ) return except inputGenerator.FileTypeUnknownError: QMessageBox.warning( self, 'Error', f'\ Unkwn input file types! The following file types are supported:\n\ psf, parm, prm7, parm7, prmtop, pdb, coor, rst, rst7, inpcrd ' ) return except Exception as e: print(e) QMessageBox.warning( self, 'Error', f'\ Unknown error! The error message is: \n\ {e}\n' ) return # gromacs if self.preTreatmentMainTabs.currentIndex() == 1: QMessageBox.warning(self, 'Warning', f'Any setting in "Advanced settings" is not supported by Gromacs!') for item in [ self.topLineEdit.text(), self.gromacsPdbLineEdit.text(), self.gromacsLigandOnlyPdbLineEdit.text(), self.gromacsLigandOnlyTopLineEdit.text() ]: if not os.path.exists(item): QMessageBox.warning(self, 'Error', f'file {item} does not exist!') return if self.selectStrategyCombobox.currentText() == 'Geometric': try: iGenerator.generateGromacsGeometricFiles( path=path, topFile=self.topLineEdit.text(), pdbFile=self.gromacsPdbLineEdit.text(), ligandOnlyTopFile=self.gromacsLigandOnlyTopLineEdit.text(), ligandOnlyPdbFile=self.gromacsLigandOnlyPdbLineEdit.text(), selectionPro=self.selectProteineLineEdit.text(), selectionLig=self.selectLigandLineEdit.text(), temperature=float(self.temperatureLineEdit.text()) ) except inputGenerator.DirectoryExistError: QMessageBox.warning( self, 'Error', f'\ ./BFEE directory already exists!' ) return except BFEEGromacs.SelectionError: QMessageBox.warning( self, 'Error', f'\ Selection corresponding to nothing!\n\ Check your selection again!' ) if os.path.exists(f'{path}/BFEE'): shutil.rmtree(f'{path}/BFEE') return except Exception as e: print(e) QMessageBox.warning( self, 'Error', f'\ Unknown error!' ) return elif self.selectStrategyCombobox.currentText() == 'Alchemical': QMessageBox.warning(self, 'Error', f'Alchemical route is not supported using Gromacs!') return QMessageBox.information(self, 'Input generation', f'Input files have been generated successfully!') del iGenerator return f def _mergePMFs(self): """merge a series of overlapped pmfs Returns: function obj: a slot function that merges a series of overlapped pmfs """ def f(): if self.mergePmfBox.count() == 0: QMessageBox.warning(self, 'Warning', f'Warning, no PMF selected!') return path, _ = QFileDialog.getSaveFileName(None, 'Set the name of merged PMF') pmfs = [ploter.readPMF(self.mergePmfBox.item(i).text()) for i in range(self.mergePmfBox.count())] mergedPMF = ploter.mergePMF(pmfs) ploter.writePMF(path, mergedPMF) QMessageBox.information(self, 'Merge PMFs', f'PMF merged successfully!') return f def _plotPMFs(self): """plot a series of overlapped pmfs Returns: function obj: a slot function that plots a series of overlapped pmfs """ def f(): if self.plotPmfBox.count() == 0: QMessageBox.warning(self, 'Warning', f'Warning, no PMF selected!') return pmfs = [ploter.readPMF(self.plotPmfBox.item(i).text()) for i in range(self.plotPmfBox.count())] mergedPMF = ploter.mergePMF(pmfs) ploter.plotPMF(mergedPMF) return f def _plotRMSDConvergence(self): """plot time evolution of PMF rmsd with respect to zero array Returns: function obj: a slot function that plots time evolution of PMF rmsd with respect to zero array """ def f(): path = self.plotPmfConvergenceBox.text() if not os.path.exists(path): QMessageBox.warning(self, 'Error', f'file {path} does not exist!') return rmsds = ploter.parseHistFile(path) ploter.plotConvergence(rmsds) return f def _initSingalsSlots(self): """initialize (connect) singals and slots """ # pre-treatment tab self.selectStrategyAdvancedButton.clicked.connect(self._advancedSettings(self.selectStrategyCombobox)) # NAMD tab self.psfButton.clicked.connect(commonSlots.openFileDialog('psf/parm', self.psfLineEdit)) self.coorButton.clicked.connect(commonSlots.openFileDialog('pdb/rst', self.coorLineEdit)) # force field selection self.forceFieldCombobox.currentTextChanged.connect(self._changeFFButtonState) self.forceFieldAddButton.clicked.connect(commonSlots.openFilesDialog('prm', self.forceFieldFilesBox)) self.forceFieldClearButton.clicked.connect(self.forceFieldFilesBox.clear) # gromacs tab self.gromacsPdbButton.clicked.connect(commonSlots.openFileDialog('pdb', self.gromacsPdbLineEdit)) self.topButton.clicked.connect(commonSlots.openFileDialog('top', self.topLineEdit)) self.gromacsLigandOnlyPdbButton.clicked.connect(commonSlots.openFileDialog('pdb', self.gromacsLigandOnlyPdbLineEdit)) self.gromacsLigandOnlyTopButton.clicked.connect(commonSlots.openFileDialog('top', self.gromacsLigandOnlyTopLineEdit)) # geometric tab self.rmsdBoundButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rmsdBoundLineEdit)) self.rmsdUnboundButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rmsdUnboundLineEdit)) self.ThetaButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.ThetaLineEdit)) self.PhiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.PhiLineEdit)) self.PsiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.PsiLineEdit)) self.thetaButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.thetaLineEdit)) self.phiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.phiLineEdit)) self.rButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rLineEdit)) # alchemical tab self.alchemicalForwardButton1.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalForwardLineEdit1)) self.alchemicalBackwardButton1.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalBackwardLineEdit1)) self.alchemicalForwardButton2.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalForwardLineEdit2)) self.alchemicalBackwardButton2.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalBackwardLineEdit2)) self.alchemicalForwardButton3.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalForwardLineEdit3)) self.alchemicalBackwardButton3.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalBackwardLineEdit3)) self.alchemicalForwardButton4.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalForwardLineEdit4)) self.alchemicalBackwardButton4.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalBackwardLineEdit4)) # generate input files self.generateInputButton.clicked.connect(self._generateInputFiles()) # calculate binding free energy self.calculateButton.clicked.connect(self._showFinalResults()) # quick-plot tab self.plotPmfAddButton.clicked.connect(commonSlots.openFilesDialog('pmf', self.plotPmfBox)) self.plotPmfClearButton.clicked.connect(self.plotPmfBox.clear) self.plotPmfPlotButton.clicked.connect(self._plotPMFs()) self.mergePmfAddButton.clicked.connect(commonSlots.openFilesDialog('pmf', self.mergePmfBox)) self.mergePmfClearButton.clicked.connect(self.mergePmfBox.clear) self.mergePmfmergeButton.clicked.connect(self._mergePMFs()) self.plotPmfConvergenceBrowseButton.clicked.connect(commonSlots.openFileDialog('pmf', self.plotPmfConvergenceBox)) self.plotPmfConvergencePlotButton.clicked.connect(self._plotRMSDConvergence())