Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:RBVI/ChimeraX into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
elainecmeng committed Nov 15, 2024
2 parents 0f045b0 + b8dfc2e commit 3473b4c
Show file tree
Hide file tree
Showing 11 changed files with 383 additions and 103 deletions.
4 changes: 4 additions & 0 deletions docs/presentations.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ <h2>ChimeraX Demonstrations, Talks and Posters</h2>
<a href="https://www.rbvi.ucsf.edu/chimerax/data/dms-june2024/deep_mutational_scan.html">Visualizating deep mutational scan scores in ChimeraX</a>. June 10, 2024.
</p>

<p>
<a href="https://www.rbvi.ucsf.edu/chimerax/data/membrane-jun2024/projection.html">Adding slices to see membrane complexes in tomograms</a>. May 2, 2024.
</p>

<p>
<a href="https://www.rbvi.ucsf.edu/chimerax/data/diffplot-apr2024/diffplot.html">Clustering protein structures with UMAP</a>. April 24, 2024.
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/build_structure/bundle_info.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<BundleInfo name="ChimeraX-BuildStructure" version="2.13" package="chimerax.build_structure"
<BundleInfo name="ChimeraX-BuildStructure" version="2.13.1" package="chimerax.build_structure"
customInit="true" minSessionVersion="1" maxSessionVersion="1">

<!-- Additional information about bundle source -->
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/build_structure/src/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def confirm_close(self):
msg_box.setWindowTitle("Confirm Close")
msg_box.setText(f"There are active {text}")
msg_box.setInformativeText(f"Closing Build Structure will deactivate these {text}"
" md forget their orignal values.\nReally close?" )
" and forget their original values.\nReally close?" )
msg_box.setStandardButtons(QMessageBox.No | QMessageBox.Yes)
msg_box.setDefaultButton(QMessageBox.Yes)
cb = QCheckBox("Don't ask in the future")
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/dicom/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# including partial copies, of the software or any revisions
# or derivations thereof.
# === UCSF ChimeraX Copyright ===
__version__ = "1.2.5"
__version__ = "1.2.6"
from chimerax.core.toolshed import BundleAPI
from chimerax.map import add_map_format
from chimerax.core.tools import get_singleton
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/dicom/src/ui/metadata_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .widgets import DICOMTable

dicom_template_url: str = (
"http://dicomlookup.com/lookup.asp?sw=Tnumber&q=%s" # noqa they don't have https
"https://dicomlookup.com/dicomtags/%s"
)

try:
Expand Down
7 changes: 6 additions & 1 deletion src/bundles/mutation_scores/src/ms_csv_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# copies, of the software or any revisions or derivations thereof.
# === UCSF ChimeraX Copyright ===

def open_mutation_scores_csv(session, path, chain = None, name = None):
def open_mutation_scores_csv(session, path, chain = None, name = None, show_plot = True):
mset = _read_mutation_scores_csv(path, name = name)

from .ms_data import mutation_scores_manager
Expand All @@ -45,6 +45,11 @@ def open_mutation_scores_csv(session, path, chain = None, name = None):
if mres > 0:
message += f' Found scores for {mres} residues not present in atomic model.'

if show_plot and session.ui.is_gui and len(mset.score_names()) >= 2:
x_score_name, y_score_name = mset.score_names()[:2]
from .ms_scatter_plot import mutation_scores_scatter_plot
mutation_scores_scatter_plot(session, x_score_name, y_score_name, mset.name, replace = False)

return mset, message

def _read_mutation_scores_csv(path, name = None):
Expand Down
303 changes: 229 additions & 74 deletions src/bundles/mutation_scores/src/ms_scatter_plot.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/bundles/phenix_ui/bundle_info.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<BundleInfo name="ChimeraX-PhenixUI" version="1.4"
<BundleInfo name="ChimeraX-PhenixUI" version="1.4.1"
package="chimerax.phenix_ui"
minSessionVersion="1" maxSessionVersion="1">

Expand Down
53 changes: 31 additions & 22 deletions src/bundles/phenix_ui/src/emplace_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def running(self):
'verbose': False
}
def phenix_local_fit(session, model, center=None, map_data=None, *, resolution=0.0, show_sharpened_map=False,
apply_symmetry=False, prefitted=None, block=None, phenix_location=None,
apply_symmetry=False, prefitted=None, show_tool=True, block=None, phenix_location=None,
verbose=command_defaults['verbose'], option_arg=[], position_arg=[]):

# Find the phenix.voyager.emplace_local executable
Expand Down Expand Up @@ -147,9 +147,9 @@ def phenix_local_fit(session, model, center=None, map_data=None, *, resolution=0
# Run phenix.voyager.emplace_local
# keep a reference to 'd' in the callback so that the temporary directory isn't removed before
# the program runs
callback = lambda transform, sharpened_map, *args, session=session, maps=map_data, \
ssm=show_sharpened_map, app_sym=apply_symmetry, d_ref=d: _process_results(session,
transform, sharpened_map, model, maps, ssm, app_sym)
callback = lambda transforms, sharpened_map, llgs, ccs, *args, session=session, maps=map_data, \
ssm=show_sharpened_map, app_sym=apply_symmetry, show_tool=show_tool, d_ref=d: \
_process_results(session, transforms, sharpened_map, llgs, ccs, model, maps, ssm, app_sym, show_tool)
FitJob(session, exe_path, option_arg, map_arg1, map_arg2, search_center,
"model.pdb", prefitted_arg, position_arg, temp_dir, resolution, verbose, callback, block)

Expand Down Expand Up @@ -215,36 +215,44 @@ def view_box(session, model):
return (face_intercepts[0] + face_intercepts[1]) / 2
raise ViewBoxError("Center of view does not intersect %s bounding box" % model)

def _process_results(session, transform, sharpened_map, orig_model, maps, show_sharpened_map,
apply_symmetry):
def _process_results(session, transforms, llgs, ccs, sharpened_map, orig_model, maps, show_sharpened_map,
apply_symmetry, show_tool):
session.logger.status("Fitting job finished")
if orig_model.deleted:
raise UserError("Structure being fitting was deleted during fitting")
from chimerax.geometry import Place
orig_model.scene_position = Place(transform) * orig_model.scene_position
sharpened_map.name = "sharpened local map"
sharpened_map.display = show_sharpened_map
session.models.add([sharpened_map])
session.logger.status("Fitting job finished")
from chimerax.core.commands import run, concise_model_spec, StringArg
if apply_symmetry:
sym_map = maps[0]
if sym_map.deleted:
raise UserError("Map being fitted has been deleted; not applying symmetry")
from chimerax.core.commands import run, concise_model_spec, StringArg
run(session, "measure symmetry " + sym_map.atomspec)
if maps[0].data.symmetries:
session.logger.warning("Map being fitted has been deleted; not applying symmetry")
apply_symmetry = False
else:
run(session, "measure symmetry " + sym_map.atomspec)
if not sym_map.data.symmetries:
session.logger.warning(
'Could not determine symmetry for %s<br><br>'
'If you know the symmetry of the map, you can create symmetry copies of the structure'
' with the <a href="help:user/commands/sym.html">sym</a> command and then combine the'
' symmetry copies with the original structure with the <a'
' href="help:user/commands/combine.html">combine</a> command'
% sym_map, is_html=True)
apply_symmetry = False
if show_tool and len(transforms) > 1 and session.ui.is_gui:
from .tool import EmplaceLocalResultsViewer
EmplaceLocalResultsViewer(session, orig_model, transforms, llgs, ccs,
sym_map if apply_symmetry else None)
else:
from chimerax.geometry import Place
orig_model.scene_position = Place(transforms[0]) * orig_model.scene_position
if apply_symmetry:
prev_models = set(session.models[:])
run(session, "sym " + orig_model.atomspec + " symmetry " + sym_map.atomspec + " copies true")
added = [m for m in session.models if m not in prev_models]
run(session, "combine " + concise_model_spec(session, [orig_model] + added) + " close true"
" modelId %d name %s" % (orig_model.id[0], StringArg.unparse(orig_model.name)))
else:
session.logger.warning(
'Could not determine symmetry for %s<br><br>'
'If you know the symmetry of the map, you can create symmetry copies of the structure'
' with the <a href="help:user/commands/sym.html">sym</a> command and then combine the'
' symmetry copies with the original structure with the <a'
' href="help:user/commands/combine.html">combine</a> command'
% sym_map, is_html=True)

#NOTE: We don't use a REST server; reference code retained in douse.py

Expand Down Expand Up @@ -313,7 +321,7 @@ def _run_fit_subprocess(session, exe_path, optional_args, map1_file_name, map2_f
', '.join(["%g" % v for v in info["mapCC"]])))

from numpy import array
return array(info['RT'][0]), sharpened_maps[0]
return [array(rt) for rt in info['RT']], info["mapLLG"], info["mapCC"], sharpened_maps[0]

def register_command(logger):
from chimerax.core.commands import CmdDesc, register
Expand All @@ -336,6 +344,7 @@ def register_command(logger):
('resolution', NonNegativeFloatArg),
('show_sharpened_map', BoolArg),
('apply_symmetry', BoolArg),
('show_tool', BoolArg),
],
synopsis = 'Place structure in map'
)
Expand Down
107 changes: 107 additions & 0 deletions src/bundles/phenix_ui/src/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,113 @@ def launch_douse(self):
run(self.session, cmd)
self.delete()

class EmplaceLocalResultsViewer(ToolInstance):
def __init__(self, session, *args):
# if 'args' is empty, we are being restored from a session and _finalize_init() will be called later
super().__init__(session, "Local EM Fitting Results")
if not args:
return
self._finalize_init(*args)

def _finalize_init(self, orig_model, transforms, llgs, ccs, sym_map, *, table_state=None):
self.orig_model = orig_model
self.orig_position = orig_model.position
self.transforms = transforms
self.llgs = llgs
self.ccs = ccs
self.sym_map = sym_map

from chimerax.core.models import REMOVE_MODELS
from chimerax.atomic import get_triggers
self.handlers = [
self.session.triggers.add_handler(REMOVE_MODELS, self._models_removed_cb),
]
self._interpolate_handler = None

from chimerax.ui import MainToolWindow
self.tool_window = tw = MainToolWindow(self, close_destroys=False)
parent = tw.ui_area

from Qt.QtWidgets import QHBoxLayout, QButtonGroup, QVBoxLayout, QRadioButton, QCheckBox
from Qt.QtWidgets import QPushButton, QLabel, QToolButton, QGridLayout
layout = QVBoxLayout()
layout.setContentsMargins(2,2,2,2)
layout.setSpacing(0)
parent.setLayout(layout)

#TODO
self.table = self._build_table(table_state)
layout.addWidget(self.table, stretch=1)

self.tool_window.manage('side')

def delete(self):
for handler in self.handlers:
handler.remove()
if self._interpolate_handler:
self._interpolate_handler.remove()
self.orig_model = self.sym_map = None
super().delete()

def _build_table(self, table_state):
class TableDatum:
def __init__(self, num, transform, llg, cc):
self.num = num
self.transform = transform
self.llg = llg
self.cc = cc
from chimerax.ui.widgets import ItemTable
table = ItemTable()
result_col = table.add_column("Result", "num")
table.add_column("Correlation Coefficient", "cc", format="%g")
table.add_column("Log-Likelihood Gain", "llg", format="%g")
table.data = [TableDatum(*args)
for args in zip(range(1, len(self.transforms)+1), self.transforms, self.llgs, self.ccs)]
table.launch(select_mode=table.SelectionMode.SingleSelection, session_info=table_state)
table.sort_by(result_col, table.SORT_ASCENDING)
table.selection_changed.connect(self._new_selection)
table.selected = table.data[0:1]
return table

def _interpolate(self, destination):
if self._interpolate_handler:
self._interpolate_handler.remove()
# interpolation code largely cribbed from map_fit.search.move_models/move_step
def make_step(trig_name, trig_data, *, tool=self, destination=destination, frame_info=[10]):
m = tool.orig_model
b = m.bounds()
finish = False
if b:
num_frames = frame_info[0]
cscene = .5 * (b.xyz_min + b.xyz_max)
c = m.scene_position.inverse() * cscene # Center in model coordinates
m.position = m.position.interpolate(destination, c, 1.0/num_frames)
if num_frames > 1:
frame_info[0] = num_frames - 1
else:
finish = True
else:
m.position = destination
finish = True
if finish:
tool._interpolate_handler.remove()
tool._interpolate_handler = None
self._interpolate_handler = self.session.triggers.add_handler("new frame", make_step)

def _models_removed_cb(self, trig_name, trig_data):
if self.orig_model in trig_data:
self.delete()

def _new_selection(self, selected, unselected):
if not selected:
return
from chimerax.geometry import Place
destination = Place(selected[0].transform) * self.orig_position
if unselected:
self._interpolate(destination)
else:
self.orig_model.position = destination

class LaunchEmplaceLocalTool(ToolInstance):
help = "help:user/tools/localemfitting.html"

Expand Down
2 changes: 1 addition & 1 deletion src/bundles/surface/src/updaters.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def reset_state(self, session):
#
def _updater_active(u):
if hasattr(u, 'surface'):
if u.surface is None or u.surface.deleted:
if u.surface is None or u.surface.deleted or not u.surface.SESSION_SAVE:
return False
if hasattr(u, 'closed') and u.closed():
return False
Expand Down

0 comments on commit 3473b4c

Please sign in to comment.