class ModellerLauncher(ToolInstance): """Generate the inputs needed by Modeller for comparative modeling""" help = "help:user/tools/modeller.html" SESSION_SAVE = False def __init__(self, session, tool_name): ToolInstance.__init__(self, session, tool_name) from chimerax.ui import MainToolWindow self.tool_window = MainToolWindow(self, close_destroys=False, statusbar=False) parent = self.tool_window.ui_area from PyQt5.QtWidgets import QListWidget, QFormLayout, QAbstractItemView, QGroupBox, QVBoxLayout from PyQt5.QtWidgets import QDialogButtonBox as qbbox interface_layout = QVBoxLayout() interface_layout.setContentsMargins(0, 0, 0, 0) interface_layout.setSpacing(0) parent.setLayout(interface_layout) alignments_area = QGroupBox("Sequence alignments") interface_layout.addWidget(alignments_area) interface_layout.setStretchFactor(alignments_area, 1) alignments_layout = QVBoxLayout() alignments_layout.setContentsMargins(0, 0, 0, 0) alignments_area.setLayout(alignments_layout) self.alignment_list = AlignmentListWidget(session) self.alignment_list.setSelectionMode( QAbstractItemView.ExtendedSelection) self.alignment_list.keyPressEvent = session.ui.forward_keystroke self.alignment_list.value_changed.connect(self._list_selection_cb) self.alignment_list.alignments_changed.connect( self._update_sequence_menus) alignments_layout.addWidget(self.alignment_list) alignments_layout.setStretchFactor(self.alignment_list, 1) targets_area = QGroupBox("Target sequences") self.targets_layout = QFormLayout() targets_area.setLayout(self.targets_layout) interface_layout.addWidget(targets_area) self.seq_menu = {} self._update_sequence_menus(session.alignments.alignments) options_area = QGroupBox("Options") options_layout = QVBoxLayout() options_layout.setContentsMargins(0, 0, 0, 0) options_area.setLayout(options_layout) interface_layout.addWidget(options_area) interface_layout.setStretchFactor(options_area, 2) from chimerax.ui.options import CategorizedSettingsPanel, BooleanOption, IntOption, PasswordOption, \ OutputFolderOption panel = CategorizedSettingsPanel(category_sorting=False, buttons=False) options_layout.addWidget(panel) from .settings import get_settings settings = get_settings(session) panel.add_option( "Basic", BooleanOption( "Make multichain model from multichain template", settings.multichain, None, balloon= "If false, all chains (templates) associated with an alignment will be used in combination\n" "to model the target sequence of that alignment, i.e. a monomer will be generated from the\n" "alignment. If true, the target sequence will be modeled from each template, i.e. a multimer\n" "will be generated from the alignment (assuming multiple chains are associated).", attr_name="multichain", settings=settings)) max_models = 1000 panel.add_option( "Basic", IntOption( "Number of models", settings.num_models, None, attr_name="num_models", settings=settings, min=1, max=max_models, balloon= "Number of model structures to generate. Must be no more than %d.\n" "Warning: please consider the calculation time" % max_models)) key = "" if settings.license_key is None else settings.license_key panel.add_option( "Basic", PasswordOption( "Modeller license key", key, None, attr_name="license_key", settings=settings, balloon= "Your Modeller license key. You can obtain a license key by registering at the Modeller web site" )) panel.add_option( "Advanced", BooleanOption( "Use fast/approximate mode (produces only one model)", settings.fast, None, attr_name="fast", settings=settings, balloon= "If enabled, use a fast approximate method to generate a single model.\n" "Typically use to get a rough idea what the model will look like or\n" "to check that the alignment is reasonable.")) panel.add_option( "Advanced", BooleanOption( "Include non-water HETATM residues from template", settings.het_preserve, None, attr_name="het_preserve", settings=settings, balloon= "If enabled, all non-water HETATM residues in the template\n" "structure(s) will be transferred into the generated models.")) panel.add_option( "Advanced", BooleanOption( "Build models with hydrogens", settings.hydrogens, None, attr_name="hydrogens", settings=settings, balloon= "If enabled, the generated models will include hydrogen atoms.\n" "Otherwise, only heavy atom coordinates will be built.\n" "Increases computation time by approximately a factor of 4.")) panel.add_option( "Advanced", OutputFolderOption( "Temporary folder location (optional)", settings.temp_path, None, attr_name="temp_path", settings=settings, balloon= "Specify a folder for temporary files. If not specified,\n" "a location will be generated automatically.")) panel.add_option( "Advanced", BooleanOption( "Include water molecules from template", settings.water_preserve, None, attr_name="water_preserve", settings=settings, balloon="If enabled, all water molecules in the template\n" "structure(s) will be included in the generated models.")) from PyQt5.QtCore import Qt from chimerax.ui.widgets import Citation interface_layout.addWidget(Citation( session, "A. Sali and T.L. Blundell.\n" "Comparative protein modelling by satisfaction of spatial restraints.\n" "J. Mol. Biol. 234, 779-815, 1993.", prefix="Publications using Modeller results should cite:", pubmed_id=18428767), alignment=Qt.AlignCenter) bbox = qbbox(qbbox.Ok | qbbox.Cancel | qbbox.Help) bbox.accepted.connect(self.launch_modeller) bbox.rejected.connect(self.delete) from chimerax.core.commands import run bbox.helpRequested.connect( lambda run=run, ses=session: run(ses, "help " + self.help)) interface_layout.addWidget(bbox) self.tool_window.manage(None) def delete(self): ToolInstance.delete(self) def launch_modeller(self): from chimerax.core.commands import run, FileNameArg, StringArg from chimerax.core.errors import UserError alignments = self.alignment_list.value if not alignments: raise UserError("No alignments chosen for modeling") aln_seq_args = [] for aln in alignments: seq_menu = self.seq_menu[aln] seq = seq_menu.value if not seq: raise UserError("No target sequence chosen for alignment %s" % aln.ident) aln_seq_args.append( StringArg.unparse("%s:%d" % (aln.ident, aln.seqs.index(seq) + 1))) from .settings import get_settings settings = get_settings(self.session) run( self.session, "modeller comparative %s multichain %s numModels %d fast %s hetPreserve %s" " hydrogens %s%s waterPreserve %s" % (" ".join(aln_seq_args), repr( settings.multichain).lower(), settings.num_models, repr(settings.fast).lower(), repr(settings.het_preserve).lower(), repr(settings.hydrogens).lower(), " tempPath %s" % FileNameArg.unparse(settings.temp_path) if settings.temp_path else "", repr(settings.water_preserve).lower())) self.delete() def _list_selection_cb(self): layout = self.targets_layout while layout.count() > 0: item = layout.takeAt(0) if not item: break widget = item.widget() if isinstance(widget, AlignSeqMenuButton): widget.setHidden(True) else: widget.deleteLater() for sel_aln in self.alignment_list.value: mb = self.seq_menu[sel_aln] mb.setHidden(False) layout.addRow(sel_aln.ident, mb) def _update_sequence_menus(self, alignments): alignment_set = set(alignments) for aln, mb in list(self.seq_menu.items()): if aln not in alignment_set: row, role = self.targets_layout.getWidgetPosition(mb) if row >= 0: self.targets_layout.removeRow(row) del self.seq_menu[aln] for aln in alignments: if aln not in self.seq_menu: self.seq_menu[aln] = AlignSeqMenuButton( aln, no_value_button_text="No target chosen")