Skip to content

Object-panel transparency badge scans every atom of every object, including hidden reps and disabled objects #271

Description

@javierbq

Two latent inefficiencies in object_has_atom_transp() (modules/pymol/appkit_inspector.py:234), both found while diagnosing the "opens but won't rotate" report fixed in #270.

#270 stops this running twice a second, so it is no longer a responsiveness bug — but the scan is still needlessly expensive every time the cache is invalidated (i.e. after every command), so it's worth fixing on its own.

1. The rep gate runs after the scan it is supposed to avoid

def object_has_atom_transp(obj):
    """... The count_atoms probe runs only when an override exists (cheap short-circuit)."""
    summ = transp_summary(obj)          # <-- full per-atom cmd.iterate, unconditional
    for rep, setting in REP_TRANSP.items():
        entry = summ.get(setting)
        if entry and entry[2]:
            if cmd.count_atoms('(%s) & rep %s' % (obj, rep)) > 0:
                return True
    return False

The docstring's "cheap short-circuit" only guards the count_atoms probe. transp_summary() — the per-atom cmd.iterate over the whole object (appkit_inspector.py:191) — runs unconditionally, even for an object showing only lines, where no rep in REP_TRANSP can be transparent and the answer is always False.

Inverting it (probe which of the ~3 transparency-capable reps are actually shown, then scan only those settings — or skip the scan entirely when none are) is cheap: a per-rep count_atoms is a C++ selector pass over one object, measured at ~0.3 ms against ~38 ms for the Python per-atom scan.

It would also shrink the scan itself. The iterate expression is built from all of TRANSP_SETTINGS:

expr = '_visit((%s))' % ', '.join('s.%s' % s for s in TRANSP_SETTINGS)

Narrowing it to the settings whose rep is shown drops the per-atom namespace lookups from 4 to 2 for a cartoon-only object. That matters because each lookup that misses goes through a raised and string-formatted AttributeError in PyMOL's iterate namespace — PyErr_Format/PyUnicode_FromFormatV were the hot leaves in the profile in #270.

2. Disabled objects are scanned

poll_panel() computes has_transp for every object in public_objects, enabled or not. Hiding objects to make a heavy session manageable therefore doesn't help. Measured on the 12-object / 79,568-atom session from #270, with 10 of 12 objects disabled:

poll_panel, all enabled     : 381 ms
poll_panel, 10 of 12 hidden : 393 ms   <- no improvement

A disabled object's row is still drawn in the panel, so its badge is arguably still wanted — but it can't change while hidden, so at minimum it shouldn't be rescanned on every invalidation.

Suggested fix

Compute the shown-rep set first; skip transp_summary() entirely when no transparency-capable rep is active, and pass it the narrowed setting list otherwise. Keep the #219 non-molecular guard and the #256 group behaviour intact — testing/tests/test_appkit_objpanel_poll.py covers both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions