#6719 closed enhancement (fixed)
Block trigger handlers
| Reported by: | Tristan Croll | Owned by: | Eric Pettersen |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | Core | Version: | |
| Keywords: | Cc: | chimera-programmers | |
| Blocked By: | Blocking: | ||
| Notify when closed: | Platform: | all | |
| Project: | ChimeraX |
Description
The following bug report has been submitted:
Platform: Linux-5.13.0-40-generic-x86_64-with-glibc2.31
ChimeraX Version: 1.3 (2021-12-08 23:08:33 UTC)
Description
I keep running into situations where it would be convenient to temporarily block a given trigger handler rather than the entire trigger (e.g. allowing the GUI to update settings and setting changes from the API to update the GUI without getting into an infinite loop). One way to achieve that is via a simple context manager to just remove and reinstate it:
{{{
from contextlib import contextmanager
@contextmanager
def block_trigger_handler(handler):
name = handler._name
func = handler._func
triggerset = handler._trigger_set
triggerset.remove_handler(handler)
try:
yield
finally:
triggerset.add_handler(name, func)
}}}
... but considering that this involves the use of three private variables it's not the best approach. Might be nice to give the `_TriggerHandler` class its own block method?
OpenGL version: 3.3.0 NVIDIA 510.47.03
OpenGL renderer: NVIDIA TITAN Xp/PCIe/SSE2
OpenGL vendor: NVIDIA Corporation
Manufacturer: Dell Inc.
Model: Precision T5600
OS: Ubuntu 20.04 focal
Architecture: 64bit ELF
Virutal Machine: none
CPU: 32 Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz
Cache Size: 20480 KB
Memory:
total used free shared buff/cache available
Mem: 62Gi 14Gi 37Gi 244Mi 10Gi 47Gi
Swap: 4.9Gi 0B 4.9Gi
Graphics:
03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [TITAN Xp] [10de:1b02] (rev a1)
Subsystem: NVIDIA Corporation GP102 [TITAN Xp] [10de:11df]
Kernel driver in use: nvidia
Locale: ('en_GB', 'UTF-8')
PyQt5 5.15.2, Qt 5.15.2
Installed Packages:
alabaster: 0.7.12
appdirs: 1.4.4
Babel: 2.9.1
backcall: 0.2.0
blockdiag: 2.0.1
certifi: 2021.10.8
cftime: 1.5.1.1
charset-normalizer: 2.0.9
ChimeraX-AddCharge: 1.2.2
ChimeraX-AddH: 2.1.11
ChimeraX-AlignmentAlgorithms: 2.0
ChimeraX-AlignmentHdrs: 3.2
ChimeraX-AlignmentMatrices: 2.0
ChimeraX-Alignments: 2.2.3
ChimeraX-AlphaFold: 1.0
ChimeraX-AltlocExplorer: 1.0.1
ChimeraX-AmberInfo: 1.0
ChimeraX-Arrays: 1.0
ChimeraX-Atomic: 1.31
ChimeraX-AtomicLibrary: 4.2
ChimeraX-AtomSearch: 2.0
ChimeraX-AtomSearchLibrary: 1.0
ChimeraX-AxesPlanes: 2.0
ChimeraX-BasicActions: 1.1
ChimeraX-BILD: 1.0
ChimeraX-BlastProtein: 2.0
ChimeraX-BondRot: 2.0
ChimeraX-BugReporter: 1.0
ChimeraX-BuildStructure: 2.6.1
ChimeraX-Bumps: 1.0
ChimeraX-BundleBuilder: 1.1
ChimeraX-ButtonPanel: 1.0
ChimeraX-CageBuilder: 1.0
ChimeraX-CellPack: 1.0
ChimeraX-Centroids: 1.2
ChimeraX-ChemGroup: 2.0
ChimeraX-Clashes: 2.2.2
ChimeraX-Clipper: 0.17.0
ChimeraX-ColorActions: 1.0
ChimeraX-ColorGlobe: 1.0
ChimeraX-ColorKey: 1.5
ChimeraX-CommandLine: 1.1.5
ChimeraX-ConnectStructure: 2.0
ChimeraX-Contacts: 1.0
ChimeraX-Core: 1.3
ChimeraX-CoreFormats: 1.1
ChimeraX-coulombic: 1.3.2
ChimeraX-Crosslinks: 1.0
ChimeraX-Crystal: 1.0
ChimeraX-CrystalContacts: 1.0
ChimeraX-DataFormats: 1.2.2
ChimeraX-Dicom: 1.0
ChimeraX-DistMonitor: 1.1.5
ChimeraX-DistUI: 1.0
ChimeraX-Dssp: 2.0
ChimeraX-EMDB-SFF: 1.0
ChimeraX-ExperimentalCommands: 1.0
ChimeraX-FileHistory: 1.0
ChimeraX-FunctionKey: 1.0
ChimeraX-Geometry: 1.1
ChimeraX-gltf: 1.0
ChimeraX-Graphics: 1.1
ChimeraX-Hbonds: 2.1.2
ChimeraX-Help: 1.2
ChimeraX-HKCage: 1.3
ChimeraX-IHM: 1.1
ChimeraX-ImageFormats: 1.2
ChimeraX-IMOD: 1.0
ChimeraX-IO: 1.0.1
ChimeraX-ISOLDE: 1.3
ChimeraX-ItemsInspection: 1.0
ChimeraX-Label: 1.1
ChimeraX-LinuxSupport: 1.0
ChimeraX-ListInfo: 1.1.1
ChimeraX-Log: 1.1.4
ChimeraX-LookingGlass: 1.1
ChimeraX-Maestro: 1.8.1
ChimeraX-Map: 1.1
ChimeraX-MapData: 2.0
ChimeraX-MapEraser: 1.0
ChimeraX-MapFilter: 2.0
ChimeraX-MapFit: 2.0
ChimeraX-MapSeries: 2.1
ChimeraX-Markers: 1.0
ChimeraX-Mask: 1.0
ChimeraX-MatchMaker: 2.0.4
ChimeraX-MDcrds: 2.6
ChimeraX-MedicalToolbar: 1.0.1
ChimeraX-Meeting: 1.0
ChimeraX-MLP: 1.1
ChimeraX-mmCIF: 2.4
ChimeraX-MMTF: 2.1
ChimeraX-Modeller: 1.2.6
ChimeraX-ModelPanel: 1.2.1
ChimeraX-ModelSeries: 1.0
ChimeraX-Mol2: 2.0
ChimeraX-Morph: 1.0
ChimeraX-MouseModes: 1.1
ChimeraX-Movie: 1.0
ChimeraX-Neuron: 1.0
ChimeraX-Nucleotides: 2.0.2
ChimeraX-OpenCommand: 1.7
ChimeraX-PDB: 2.6.5
ChimeraX-PDBBio: 1.0
ChimeraX-PDBLibrary: 1.0.2
ChimeraX-PDBMatrices: 1.0
ChimeraX-Phenix: 0.3
ChimeraX-PickBlobs: 1.0
ChimeraX-Positions: 1.0
ChimeraX-PresetMgr: 1.0.1
ChimeraX-PubChem: 2.1
ChimeraX-ReadPbonds: 1.0.1
ChimeraX-Registration: 1.1
ChimeraX-RemoteControl: 1.0
ChimeraX-ResidueFit: 1.0
ChimeraX-RestServer: 1.1
ChimeraX-RNALayout: 1.0
ChimeraX-RotamerLibMgr: 2.0.1
ChimeraX-RotamerLibsDunbrack: 2.0
ChimeraX-RotamerLibsDynameomics: 2.0
ChimeraX-RotamerLibsRichardson: 2.0
ChimeraX-Sample: 0.1
ChimeraX-SaveCommand: 1.5
ChimeraX-SchemeMgr: 1.0
ChimeraX-SDF: 2.0
ChimeraX-Segger: 1.0
ChimeraX-Segment: 1.0
ChimeraX-SelInspector: 1.0
ChimeraX-SeqView: 2.4.6
ChimeraX-Shape: 1.0.1
ChimeraX-Shell: 1.0
ChimeraX-Shortcuts: 1.1
ChimeraX-ShowAttr: 1.0
ChimeraX-ShowSequences: 1.0
ChimeraX-SideView: 1.0
ChimeraX-Smiles: 2.1
ChimeraX-SmoothLines: 1.0
ChimeraX-SpaceNavigator: 1.0
ChimeraX-StdCommands: 1.6.1
ChimeraX-STL: 1.0
ChimeraX-Storm: 1.0
ChimeraX-Struts: 1.0
ChimeraX-Surface: 1.0
ChimeraX-SwapAA: 2.0
ChimeraX-SwapRes: 2.1
ChimeraX-TapeMeasure: 1.0
ChimeraX-Test: 1.0
ChimeraX-Toolbar: 1.1
ChimeraX-ToolshedUtils: 1.2
ChimeraX-Tug: 1.0
ChimeraX-UI: 1.13.7
ChimeraX-uniprot: 2.2
ChimeraX-UnitCell: 1.0
ChimeraX-ViewDockX: 1.0.1
ChimeraX-VIPERdb: 1.0
ChimeraX-Vive: 1.1
ChimeraX-VolumeMenu: 1.0
ChimeraX-Voyager: 0.1
ChimeraX-VTK: 1.0
ChimeraX-WavefrontOBJ: 1.0
ChimeraX-WebCam: 1.0
ChimeraX-WebServices: 1.0
ChimeraX-Zone: 1.0
colorama: 0.4.4
cxservices: 1.1
cycler: 0.11.0
Cython: 0.29.24
decorator: 5.1.0
distro: 1.6.0
docutils: 0.17.1
filelock: 3.0.12
funcparserlib: 0.3.6
grako: 3.16.5
h5py: 3.6.0
html2text: 2020.1.16
idna: 3.3
ihm: 0.21
imagecodecs: 2021.4.28
imagesize: 1.3.0
ipykernel: 5.5.5
ipython: 7.23.1
ipython-genutils: 0.2.0
jedi: 0.18.0
Jinja2: 3.0.1
jsonpickle: 2.0.0
jupyter-client: 6.1.12
jupyter-core: 4.9.1
kiwisolver: 1.3.2
line-profiler: 3.3.0
lxml: 4.6.3
lz4: 3.1.3
MarkupSafe: 2.0.1
matplotlib: 3.4.3
matplotlib-inline: 0.1.3
msgpack: 1.0.2
netCDF4: 1.5.7
networkx: 2.6.2
numexpr: 2.8.0
numpy: 1.21.2
openvr: 1.16.801
packaging: 21.3
ParmEd: 3.2.0
parso: 0.8.3
pexpect: 4.8.0
pickleshare: 0.7.5
Pillow: 8.3.2
pip: 21.2.4
pkginfo: 1.7.1
prompt-toolkit: 3.0.23
psutil: 5.8.0
ptyprocess: 0.7.0
pycollada: 0.7.1
pydicom: 2.1.2
Pygments: 2.10.0
PyOpenGL: 3.1.5
PyOpenGL-accelerate: 3.1.5
pyparsing: 3.0.6
PyQt5-commercial: 5.15.2
PyQt5-sip: 12.8.1
PyQtWebEngine-commercial: 5.15.2
python-dateutil: 2.8.2
python-igraph: 0.9.6
pytz: 2021.3
pyvis: 0.1.9
pyzmq: 22.3.0
qtconsole: 5.1.1
QtPy: 1.11.3
RandomWords: 0.3.0
rdkit-pypi: 2021.3.5.1
requests: 2.26.0
scipy: 1.7.1
setuptools: 57.5.0
sfftk-rw: 0.7.1
six: 1.16.0
snowballstemmer: 2.2.0
sortedcontainers: 2.4.0
Sphinx: 4.2.0
sphinx-autodoc-typehints: 1.12.0
sphinxcontrib-applehelp: 1.0.2
sphinxcontrib-blockdiag: 2.0.0
sphinxcontrib-devhelp: 1.0.2
sphinxcontrib-htmlhelp: 2.0.0
sphinxcontrib-jsmath: 1.0.1
sphinxcontrib-qthelp: 1.0.3
sphinxcontrib-serializinghtml: 1.1.5
suds-jurko: 0.6
tables: 3.6.1
texttable: 1.6.4
tifffile: 2021.4.8
tinyarray: 1.2.3
tornado: 6.1
traitlets: 5.1.1
urllib3: 1.26.7
wcwidth: 0.2.5
webcolors: 1.11.1
wheel: 0.37.0
wheel-filename: 1.3.0
Change History (5)
comment:1 by , 4 years ago
| Cc: | added |
|---|---|
| Component: | Unassigned → Core |
| Owner: | set to |
| Platform: | → all |
| Project: | → ChimeraX |
| Status: | new → accepted |
| Summary: | ChimeraX bug report submission → Block trigger handlers |
| Type: | defect → enhancement |
comment:3 by , 3 years ago
While debugging #8253 I found myself revisiting this... my existing hacky band-aid solution was a context manager to remove the existing handler and replace it when done:
@contextmanager
def block_managed_trigger_handler(object, handler_name):
'''
`object.handler_name` should be a ChimeraX trigger handler. The original handler
will be deleted, and replaced with an identical new one on exit of the context.
'''
h = getattr(object, handler_name)
name = h._name
func = h._func
triggerset = h._trigger_set
triggerset.remove_handler(h)
try:
yield
finally:
setattr(object, handler_name, triggerset.add_handler(name, func))
... but this was doing weird things in the context of #8253 (I guess the long delays were breaking some of PyQt's assumptions with regard to threading... eventually it would get to a state where things seemed to happen out of order - the old handler wasn't properly removed and fired when it shouldn't). If I instead do:
from chimerax.core.triggerset import _TriggerHandler
class BlockableTriggerHandler(_TriggerHandler):
def invoke(self, data, remove_if_error):
if getattr(self, '_blocked', 0):
return
super().invoke(data, remove_if_error)
@contextmanager
def block(self):
self._blocked = getattr(self, '_blocked', 0) + 1
try:
yield
finally:
self._blocked -= 1
def manual_block(self):
self._blocked = getattr(self, '_blocked', 0) +1
def manual_release(self):
if getattr(self, '_blocked', 0) <= 0:
raise RuntimeError('More releases than blocks')
self._blocked -= 1
h = some_triggerset.add_handler('trigger name', some_func)
h.__class__ = BlockableTriggerHandler
with h.block():
do_something()
... and everything is well-behaved... but I'd be much more comfortable without this sort of monkey-patching.
comment:4 by , 3 years ago
| Resolution: | → fixed |
|---|---|
| Status: | accepted → closed |
Okay, incorporated this but named the context manager "blocked" and dropped the "manual_" prefixes from block and release. Added an analogous context manager to the _Trigger class.
follow-up: 5 comment:5 by , 3 years ago
Awesome, thanks! On Tue, Jan 3, 2023 at 9:47 PM ChimeraX <ChimeraX-bugs-admin@cgl.ucsf.edu> wrote:
Seems okay to me. Will leave this open for a few days to give the other programmers a chance to weigh in if desired.