diff --git a/src/bundles/alphafold/src/alphafold21_predict_colab.ipynb b/src/bundles/alphafold/src/alphafold21_predict_colab.ipynb
index 5529d99ec5..830bb830a9 100644
--- a/src/bundles/alphafold/src/alphafold21_predict_colab.ipynb
+++ b/src/bundles/alphafold/src/alphafold21_predict_colab.ipynb
@@ -266,8 +266,8 @@
"# We have to use \"--no-warn-conflicts\" because colab already has a lot preinstalled with requirements different to ours\n",
"pip install --no-warn-conflicts \"colabfold[alphafold-minus-jax] @ git+https://github.com/sokrypton/ColabFold@dc9fc3d03379d23784e796f4c7fd31d173bafaa2\"\n",
"# high risk high gain\n",
- "pip uninstall jaxlib -y\n",
- "pip install \"jax[cuda11_cudnn805]==0.3.24\" jaxlib==0.3.24+cuda11.cudnn805 -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html\n",
+ "pip uninstall jax jaxlib -y\n",
+ "pip install \"jax[cuda]==0.3.25\" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html\n",
"touch COLABFOLD_READY\n",
"'''\n",
" run_shell_commands(cmds, 'install_colabfold.sh', install_log)\n",
diff --git a/src/bundles/alphafold/src/alphafold_test_colab.ipynb b/src/bundles/alphafold/src/alphafold_test_colab.ipynb
index 5529d99ec5..830bb830a9 100644
--- a/src/bundles/alphafold/src/alphafold_test_colab.ipynb
+++ b/src/bundles/alphafold/src/alphafold_test_colab.ipynb
@@ -266,8 +266,8 @@
"# We have to use \"--no-warn-conflicts\" because colab already has a lot preinstalled with requirements different to ours\n",
"pip install --no-warn-conflicts \"colabfold[alphafold-minus-jax] @ git+https://github.com/sokrypton/ColabFold@dc9fc3d03379d23784e796f4c7fd31d173bafaa2\"\n",
"# high risk high gain\n",
- "pip uninstall jaxlib -y\n",
- "pip install \"jax[cuda11_cudnn805]==0.3.24\" jaxlib==0.3.24+cuda11.cudnn805 -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html\n",
+ "pip uninstall jax jaxlib -y\n",
+ "pip install \"jax[cuda]==0.3.25\" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html\n",
"touch COLABFOLD_READY\n",
"'''\n",
" run_shell_commands(cmds, 'install_colabfold.sh', install_log)\n",
diff --git a/src/bundles/alphafold/src/colabfold_predict.ipynb b/src/bundles/alphafold/src/colabfold_predict.ipynb
index 5529d99ec5..830bb830a9 100644
--- a/src/bundles/alphafold/src/colabfold_predict.ipynb
+++ b/src/bundles/alphafold/src/colabfold_predict.ipynb
@@ -266,8 +266,8 @@
"# We have to use \"--no-warn-conflicts\" because colab already has a lot preinstalled with requirements different to ours\n",
"pip install --no-warn-conflicts \"colabfold[alphafold-minus-jax] @ git+https://github.com/sokrypton/ColabFold@dc9fc3d03379d23784e796f4c7fd31d173bafaa2\"\n",
"# high risk high gain\n",
- "pip uninstall jaxlib -y\n",
- "pip install \"jax[cuda11_cudnn805]==0.3.24\" jaxlib==0.3.24+cuda11.cudnn805 -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html\n",
+ "pip uninstall jax jaxlib -y\n",
+ "pip install \"jax[cuda]==0.3.25\" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html\n",
"touch COLABFOLD_READY\n",
"'''\n",
" run_shell_commands(cmds, 'install_colabfold.sh', install_log)\n",
diff --git a/src/bundles/alphafold/src/colabfold_predict.py b/src/bundles/alphafold/src/colabfold_predict.py
index bd794ea1c2..55c15f5686 100644
--- a/src/bundles/alphafold/src/colabfold_predict.py
+++ b/src/bundles/alphafold/src/colabfold_predict.py
@@ -239,8 +239,8 @@ def install(use_amber = False, use_templates = False, install_log = 'install_log
# We have to use "--no-warn-conflicts" because colab already has a lot preinstalled with requirements different to ours
pip install --no-warn-conflicts "colabfold[alphafold-minus-jax] @ git+https://github.com/sokrypton/ColabFold@dc9fc3d03379d23784e796f4c7fd31d173bafaa2"
# high risk high gain
-pip uninstall jaxlib -y
-pip install "jax[cuda11_cudnn805]==0.3.24" jaxlib==0.3.24+cuda11.cudnn805 -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html
+pip uninstall jax jaxlib -y
+pip install "jax[cuda]==0.3.25" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html
touch COLABFOLD_READY
'''
run_shell_commands(cmds, 'install_colabfold.sh', install_log)
diff --git a/src/bundles/atom_search/bundle_info.xml b/src/bundles/atom_search/bundle_info.xml
index 778abba4a6..d198b9bb4d 100644
--- a/src/bundles/atom_search/bundle_info.xml
+++ b/src/bundles/atom_search/bundle_info.xml
@@ -26,7 +26,7 @@
-
+
diff --git a/src/bundles/atomic/bundle_info.xml b/src/bundles/atomic/bundle_info.xml
index 8475d9ed23..3a28d8cc18 100644
--- a/src/bundles/atomic/bundle_info.xml
+++ b/src/bundles/atomic/bundle_info.xml
@@ -62,7 +62,7 @@
-
+
diff --git a/src/bundles/atomic/bundle_info.xml.in b/src/bundles/atomic/bundle_info.xml.in
index eee9639d5e..397b132e69 100644
--- a/src/bundles/atomic/bundle_info.xml.in
+++ b/src/bundles/atomic/bundle_info.xml.in
@@ -62,7 +62,7 @@
-
+
diff --git a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.cpp b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.cpp
index 067f75d36d..3034636531 100644
--- a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.cpp
+++ b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.cpp
@@ -294,15 +294,17 @@ AtomicStructure::normalize_ss_ids()
}
void
-AtomicStructure::make_chains() const
+AtomicStructure::_make_chains() const
{
- if (_chains != nullptr) {
- for (auto c: *_chains)
- delete c;
- delete _chains;
+ std::set pre_existing;
+ if (_chains == nullptr) {
+ _chains = new Chains();
+ } else {
+ for (auto chain: *_chains)
+ pre_existing.insert(chain->chain_id());
}
+ _chains_made = true; // prevent resursion
- _chains = new Chains();
auto polys = polymers();
// In an ideal world there would be a one-to-one correspondence between
@@ -321,6 +323,8 @@ AtomicStructure::make_chains() const
for (auto key_polys: id_to_polys) {
auto id_type = key_polys.first;
auto chain_id = id_type.first;
+ if (pre_existing.find(chain_id) != pre_existing.end())
+ continue;
auto pt_type = id_type.second;
auto& chain_polys = key_polys.second;
std::vector res_list;
diff --git a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.h b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.h
index 5b50dd281c..dbbbf581aa 100644
--- a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.h
+++ b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/AtomicStructure.h
@@ -33,19 +33,19 @@ namespace atomstruct {
class ATOMSTRUCT_IMEX AtomicStructure: public Structure {
friend class Atom; // for IDATM stuff and structure categories
- friend class Bond; // for checking if make_chains() has been run yet, struct categories
+ friend class Bond; // struct categories
friend class Residue; // for _polymers_computed
friend class StructureSeq; // for remove_chain()
private:
void _compute_atom_types();
void _compute_structure_cats() const;
+ void _make_chains() const;
public:
AtomicStructure(PyObject* logger = nullptr) : Structure(logger) {}
void compute_secondary_structure(float energy_cutoff = -0.5, int min_helix_length = 3,
int min_strand_length = 3, bool = false, CompSSInfo* = nullptr);
AtomicStructure* copy() const;
- void make_chains() const;
void normalize_ss_ids();
std::vector> polymers(
PolymerMissingStructure missing_structure_treatment = PMS_ALWAYS_CONNECTS,
diff --git a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.cpp b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.cpp
index 202cd53bde..64535ef23e 100644
--- a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.cpp
+++ b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.cpp
@@ -883,10 +883,10 @@ Structure::_delete_atoms(const std::set& atoms, bool verify)
void
Structure::_form_chain_check(Atom* a1, Atom* a2, Bond* b)
{
- // If initial construction is over (i.e. Python instance exists) and make_chains()
- // has been called (i.e. _chains is not null), then need to check if new bond
+ // If initial construction is over (i.e. Python instance exists) and _make_chains()
+ // has been called (i.e. _chain_made is true), then need to check if new bond
// or missing-structure pseudobond creates a chain or coalesces chain fragments
- if (_chains == nullptr)
+ if (!_chains_made)
return;
auto inst = py_instance(false);
Py_DECREF(inst);
@@ -1134,16 +1134,11 @@ Structure::find_residue(const ChainID& chain_id, int num, char insert, ResName&
}
void
-Structure::make_chains() const
+Structure::_make_chains() const
{
- // since Graphs don't have sequences, they don't have chains
- if (_chains != nullptr) {
- for (auto c: *_chains)
- delete c;
- delete _chains;
- }
-
+ // since non-atomic Structures don't have sequences, they don't have chains
_chains = new Chains();
+ _chains_made = true;
}
Atom *
diff --git a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.h b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.h
index 6b634ccd9b..943c2d7e5c 100644
--- a/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.h
+++ b/src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.h
@@ -146,6 +146,7 @@ class ATOMSTRUCT_IMEX Structure: public GraphicsChanges,
float _ball_scale = 0.25;
Bonds _bonds;
mutable Chains* _chains;
+ mutable bool _chains_made = false;
ChangeTracker* _change_tracker;
CoordSets _coord_sets;
bool _display = true;
@@ -202,6 +203,7 @@ class ATOMSTRUCT_IMEX Structure: public GraphicsChanges,
std::set& left_missing_structure_atoms,
std::set& right_missing_structure_atoms,
const std::set* deleted_atoms = nullptr) const;
+ virtual void _make_chains() const;
Bond* _new_bond(Atom* a1, Atom* a2, bool bond_only);
Chain* _new_chain(const ChainID& chain_id, PolymerType pt = PT_NONE) const {
auto chain = new Chain(chain_id, const_cast(this), pt);
@@ -243,7 +245,7 @@ class ATOMSTRUCT_IMEX Structure: public GraphicsChanges,
void bonded_groups(std::vector>* groups,
bool consider_missing_structure) const;
const Bonds& bonds() const { return _bonds; }
- const Chains& chains() const { if (_chains == nullptr) make_chains(); return *_chains; }
+ const Chains& chains() const { if (!_chains_made) _make_chains(); return *_chains; }
void change_chain_ids(const std::vector, const std::vector,
bool /*non-polymeric*/=true);
ChangeTracker* change_tracker() { return _change_tracker; }
@@ -276,7 +278,6 @@ class ATOMSTRUCT_IMEX Structure: public GraphicsChanges,
bool is_traj;
PyObject* logger() const { return _logger; }
bool lower_case_chains;
- virtual void make_chains() const;
std::map> metadata;
Atom* new_atom(const char* name, const Element& e);
Bond* new_bond(Atom* a1, Atom* a2) { return _new_bond(a1, a2, false); }
diff --git a/src/bundles/atomic_lib/bundle_info.xml b/src/bundles/atomic_lib/bundle_info.xml
index ed6613aa52..edc6020d2d 100644
--- a/src/bundles/atomic_lib/bundle_info.xml
+++ b/src/bundles/atomic_lib/bundle_info.xml
@@ -1,4 +1,4 @@
-
-
+
diff --git a/src/bundles/connect_structure/bundle_info.xml b/src/bundles/connect_structure/bundle_info.xml
index 71c932eb8e..b9ee96aa00 100644
--- a/src/bundles/connect_structure/bundle_info.xml
+++ b/src/bundles/connect_structure/bundle_info.xml
@@ -22,7 +22,7 @@
-
+
diff --git a/src/bundles/core/src/__main__.py b/src/bundles/core/src/__main__.py
index 2fd520add3..5ee14c1573 100644
--- a/src/bundles/core/src/__main__.py
+++ b/src/bundles/core/src/__main__.py
@@ -806,8 +806,8 @@ def init(argv, event_loop=True):
import runpy
import warnings
exit = SystemExit(os.EX_OK)
- from chimerax.core.python_utils import chimerax_user_base
- with warnings.catch_warnings(), chimerax_user_base():
+ from chimerax.core.python_utils import chimerax_environment
+ with warnings.catch_warnings(), chimerax_environment():
warnings.filterwarnings("ignore", category=BytesWarning)
global_dict = {
'session': sess
diff --git a/src/bundles/core/src/python_utils.py b/src/bundles/core/src/python_utils.py
index 4edcb149d0..ac5dd012d2 100644
--- a/src/bundles/core/src/python_utils.py
+++ b/src/bundles/core/src/python_utils.py
@@ -32,21 +32,57 @@ def chimerax_python_executable():
@contextmanager
-def chimerax_user_base():
- """Make pip install packages to ChimeraX's customary PYTHONUSERBASE.
+def chimerax_environment():
+ """Setup environment for Python to match ChimeraX setup
- Without this context manager, Python will install packages in the traditional
- user directory at, on macOS, ~/Library/Python/(version)/lib/python/site-packages
- instead of our location at ~/Library/Application Support/ChimeraX/(cx_version)
+ In particular, set PYTHONUSERBASE so pip will install/uninstall packages
+ in the ChimeraX "user" location. Also remove from environment any
+ variables that would alter ChimeraX's behaviour.
"""
from chimerax import app_dirs
- old_pythonuserbase = os.environ.get('PYTHONUSERBASE', None)
- os.environ['PYTHONUSERBASE'] = app_dirs.user_data_dir
+ PROTECT = {
+ 'PYTHONDONTWRITEBYTECODE': None,
+ # 'PYTHONDEBUG': None,
+ # 'PYTHONINSPECT': None,
+ # 'PYTHONOPTIMIZE': None,
+ 'PYTHONNOUSERSITE': None,
+ # 'PYTHONUNBUFFERED': None,
+ # 'PYTHONVERBOSE': None,
+ # 'PYTHONWARNINGS': None,
+ 'PYTHONSTARTUP': None,
+ 'PYTHONPATH': None,
+ 'PYTHONHOME': None,
+ 'PYTHONPLATLIBDIR': None,
+ 'PYTHONCASEOK': None,
+ 'PYTHONUTF8': None,
+ 'PYTHONIOENCODING': None,
+ 'PYTHONFAULTHANDLER': None,
+ # 'PYTHONHASHSEED': None,
+ 'PYTHONINTMAXSTRDIGITS': None,
+ # 'PYTHONMALLOC': None,
+ 'PYTHONCOERCECLOCALE': None,
+ # 'PYTHONBREAKPOINT': None,
+ # 'PYTHONDEVMODE': None,
+ 'PYTHONPYCACHEPREFIX': None,
+ 'PYTHONWARNDEFAULTENCODING': None,
+ 'PYTHONUSERBASE': app_dirs.user_data_dir,
+ }
+ old_environ = {}
+ for var, new_value in PROTECT.items():
+ old_value = os.environ.get(var, None)
+ if old_value is None and new_value is None:
+ continue
+ old_environ[var] = old_value
+ if new_value is None:
+ del os.environ[var]
+ else:
+ os.environ[var] = new_value
yield
- if old_pythonuserbase is None:
- del os.environ['PYTHONUSERBASE']
- else:
- os.environ['PYTHONUSERBASE'] = old_pythonuserbase
+ for var, value in old_environ.items():
+ if value is None:
+ del os.environ[var]
+ else:
+ os.environ[var] = value
def is_link(path):
@@ -83,10 +119,9 @@ def run_pip(command):
# the user site directory is the ChimeraX application location.
import subprocess
prog = chimerax_python_executable()
- pip_cmd = [prog] + subprocess._args_from_interpreter_flags() + ["-m", "pip"]
+ pip_cmd = [prog] + ["-m", "pip"]
# pip_cmd = [sys.executable, "-m", "pip"]
- from chimerax.core.python_utils import chimerax_user_base
- with chimerax_user_base():
+ with chimerax_environment():
cp = subprocess.run(pip_cmd + command, capture_output=True)
return cp
diff --git a/src/bundles/core/src/scripting.py b/src/bundles/core/src/scripting.py
index 80fb1f65c5..b36896080c 100644
--- a/src/bundles/core/src/scripting.py
+++ b/src/bundles/core/src/scripting.py
@@ -223,8 +223,6 @@ def probably_chimera1_session(evalue):
return False
chimera1_session_message = """\
-ChimeraX cannot open a regular Chimera session. An exporter from Chimera
-to ChimeraX is being worked on but only handles molecules and molecular surfaces
-(not volumes) at this time. If that is sufficient, use the latest Chimera
-daily build and its File->Export Scene menu item, and change the resulting
-dialog's "File Type" to ChimeraX."""
+ChimeraX cannot open a regular Chimera session. An exporter from Chimera to
+ChimeraX is available in the latest Chimera release. Use its File->Export Scene
+menu item, and change the resulting dialog's "File Type" to ChimeraX."""
diff --git a/src/bundles/dssp/bundle_info.xml b/src/bundles/dssp/bundle_info.xml
index 01c11af70e..f507121eae 100644
--- a/src/bundles/dssp/bundle_info.xml
+++ b/src/bundles/dssp/bundle_info.xml
@@ -25,7 +25,7 @@
-
+
diff --git a/src/bundles/mmcif/bundle_info.xml b/src/bundles/mmcif/bundle_info.xml
index 6c4786be39..ae23a1f37a 100644
--- a/src/bundles/mmcif/bundle_info.xml
+++ b/src/bundles/mmcif/bundle_info.xml
@@ -53,7 +53,7 @@
-
+
diff --git a/src/bundles/mmtf/bundle_info.xml b/src/bundles/mmtf/bundle_info.xml
index 01b3d664a9..9d9967d4c0 100644
--- a/src/bundles/mmtf/bundle_info.xml
+++ b/src/bundles/mmtf/bundle_info.xml
@@ -32,7 +32,7 @@
-
+
diff --git a/src/bundles/mouse_modes/src/std_modes.py b/src/bundles/mouse_modes/src/std_modes.py
index a06b9881da..56342e151f 100644
--- a/src/bundles/mouse_modes/src/std_modes.py
+++ b/src/bundles/mouse_modes/src/std_modes.py
@@ -865,22 +865,42 @@ class MoveToCenterMode(MouseMode):
def mouse_down(self, event):
MouseMode.mouse_down(self, event)
xyz = _picked_xyz(event, self.session)
- if xyz is None:
- return
-
- # Move camera so it is centered on picked point.
- c = self.session.main_view.camera
- cx,cy,cz = c.position.inverse() * xyz
- steps = self.frames
- from chimerax.core.commands import Axis
- mxy = (-cx/steps, -cy/steps, 0)
- axis = Axis(coords = mxy)
- from chimerax.std_commands.move import move
- move(self.session, axis, frames = steps)
+ if xyz is not None:
+ _move_to_center(self.session, xyz, steps=self.frames)
+
+def _move_to_center(session, xyz, steps = 10):
+ '''Move camera so it is centered on picked point.'''
+ c = session.main_view.camera
+ cx,cy,cz = c.position.inverse() * xyz
+ from chimerax.core.commands import Axis
+ mxy = (-cx/steps, -cy/steps, 0)
+ axis = Axis(coords = mxy)
+ from chimerax.std_commands.move import move
+ move(session, axis, frames = steps)
+
+ # Set center of rotation
+ from chimerax.std_commands import cofr
+ cofr.cofr(session, pivot=xyz)
+
+class MoveToCenterOrTranslateMode(MoveMouseMode):
+ '''
+ Clicking on an atom, bond, ribbon, pseudobond or volume surface
+ centers the view on that point and sets the center of rotation at that position
+ on mouse release if no drag has been done. Drag translates models.
+ '''
+ name = 'center or translate'
+ mouse_action = 'translate'
+ frames = 10 # Animate motion over this number of frames
+ min_drag = 5 # Pixels. Smaller drags do a centering.
- # Set center of rotation
- from chimerax.std_commands import cofr
- cofr.cofr(self.session, pivot=xyz)
+ def mouse_up(self, event):
+ dx,dy = self.mouse_down_position
+ MoveMouseMode.mouse_up(self, event)
+ ux,uy = event.position()
+ if abs(dx-ux) < self.min_drag or abs(dy-uy) < self.min_drag:
+ xyz = _picked_xyz(event, self.session)
+ if xyz is not None:
+ _move_to_center(self.session, xyz, steps=self.frames)
class NullMouseMode(MouseMode):
'''Used to assign no mode to a mouse button.'''
@@ -1161,6 +1181,7 @@ def standard_mouse_mode_classes():
ObjectIdMouseMode,
CenterOfRotationMode,
MoveToCenterMode,
+ MoveToCenterOrTranslateMode,
SwipeAsScrollMouseMode,
NullMouseMode,
]
diff --git a/src/bundles/nucleotides/src/_data.py b/src/bundles/nucleotides/src/_data.py
index 6d99e719eb..956b69589a 100644
--- a/src/bundles/nucleotides/src/_data.py
+++ b/src/bundles/nucleotides/src/_data.py
@@ -480,9 +480,11 @@ def take_snapshot(self, session, flags):
@classmethod
def restore_snapshot(cls, session, data):
+ nuc = _nucleotides(session)
+ if data is None:
+ return nuc
if data['version'] != 1:
raise RestoreError("unknown nucleotide session state version")
- nuc = _nucleotides(session)
infos = data['infos']
for mol, (info, params) in infos.items():
if info is None:
diff --git a/src/bundles/pdb/bundle_info.xml b/src/bundles/pdb/bundle_info.xml
index e976415ef6..4b812630bd 100644
--- a/src/bundles/pdb/bundle_info.xml
+++ b/src/bundles/pdb/bundle_info.xml
@@ -30,7 +30,7 @@
-
+
diff --git a/src/bundles/pdb_lib/bundle_info.xml b/src/bundles/pdb_lib/bundle_info.xml
index 29e6d9d241..5577576524 100644
--- a/src/bundles/pdb_lib/bundle_info.xml
+++ b/src/bundles/pdb_lib/bundle_info.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/src/bundles/toolshed_utils/src/__init__.py b/src/bundles/toolshed_utils/src/__init__.py
index d130de1f5f..39406211e7 100644
--- a/src/bundles/toolshed_utils/src/__init__.py
+++ b/src/bundles/toolshed_utils/src/__init__.py
@@ -261,11 +261,12 @@ def _install_bundle(toolshed, bundles, logger, *, per_user=True, reinstall=False
logger.error("You do not have permission to install %s for %s" %
(bundle_name, who))
return
- installed = re.findall(r"^\s*Successfully installed.*$", results, re.M)
- if installed:
- logger.info('\n'.join(installed))
- else:
- logger.info('No bundles were installed')
+ # `result` is empty giving -qq, depend on toolshed.reload to report installation
+ # installed = re.findall(r"^\s*Successfully installed.*$", results, re.M)
+ # if installed:
+ # logger.info('\n'.join(installed))
+ # else:
+ # logger.info('No bundles were installed')
toolshed.set_install_timestamp(per_user)
changes = toolshed.reload(logger, rebuild_cache=True, report=True)
diff --git a/src/bundles/ui/bundle_info.xml b/src/bundles/ui/bundle_info.xml
index ddfe7c6e25..857b101a0d 100644
--- a/src/bundles/ui/bundle_info.xml
+++ b/src/bundles/ui/bundle_info.xml
@@ -1,4 +1,4 @@
-
diff --git a/src/bundles/ui/src/gui.py b/src/bundles/ui/src/gui.py
index 5f11ef37ac..2b80060dfe 100644
--- a/src/bundles/ui/src/gui.py
+++ b/src/bundles/ui/src/gui.py
@@ -1101,6 +1101,7 @@ def _populate_menus(self, session):
self.tools_menu = mb.addMenu("&Tools")
self.tools_menu.setToolTipsVisible(True)
self.update_tools_menu(session)
+ self.tools_menu.aboutToShow.connect(self._update_running_tools)
self._settings_ui_widget = None
self._accumulated_settings_options = []
@@ -1682,8 +1683,8 @@ def update_tools_menu(self, session):
self._checkbutton_tools = {}
from Qt.QtWidgets import QMenu
from Qt.QtGui import QAction
- tools_menu = QMenu("&Tools", self.menuBar())
- tools_menu.setToolTipsVisible(True)
+ tools_menu = self.tools_menu
+ tools_menu.clear()
categories = {}
self._tools_cache = set()
for bi in session.toolshed.bundle_info(session.logger):
@@ -1726,12 +1727,6 @@ def update_tools_menu(self, session):
tools_menu.addAction(more_tools)
# running tools will go below this...
self._tools_menu_separator = tools_menu.addSection("Running Tools")
- tools_menu.aboutToShow.connect(self._update_running_tools)
- mb = self.menuBar()
- old_action = self.tools_menu.menuAction()
- mb.insertMenu(old_action, tools_menu)
- mb.removeAction(old_action)
- self.tools_menu = tools_menu
def _update_running_tools(self, *args):
# clear out old running tools