Skip to content

fix(webui): keep a row while any of its hide_zero fields is visible - #3727

Merged
nicolargo merged 1 commit into
nicolargo:developfrom
ntdatt812:fix/webui-hide-zero-row-polarity
Sep 10, 2026
Merged

nicolargo merged 1 commit into
nicolargo:developfrom
ntdatt812:fix/webui-hide-zero-row-polarity

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

The bug

hide_zero is decided per field but applied per row, and the two front ends read that per-field verdict in opposite directions.

msg_curse drops a row only when all of its hide_zero_fields are hidden:

# glances/plugins/diskio/__init__.py, same in network
if all(self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields):
    continue

plugin-diskio.vue and plugin-network.vue keep a row only when all of them are visible:

return (
    (!readBytesRate || readBytesRate.hidden === false) &&
    (!writeBytesRate || writeBytesRate.hidden === false)
);

The two agree while both fields say the same thing, and disagree the moment one moves and the other does not — a disk that has only ever been read, an interface that has only ever received. Curses shows the row; the browser drops it entirely, with no indication that anything was filtered.

Measured

DiskioPlugin with hide_zero=True and hide_threshold_bytes=1024, fed an idle sample then a read-only one (2048 B/s read, 0 B/s write). The views dict that both front ends then read:

sda.read_bytes_rate_per_sec.hidden  = false
sda.write_bytes_rate_per_sec.hidden = true

Both predicates run against exactly that payload:

verdict
msg_curse (all(...hidden) → drop) shows the row
.vue before hides the row
.vue after shows the row

The change

Use the same rule as msg_curse in both components: && → ||, so a row survives while any of its fields is still visible. The existing !rate || guard for a row that has stats but no view entry yet is untouched.

Tests

The repo has no runner for the WebUI, so tests/test_hide_zero_row_visibility.py (5 tests) pins the rule on the side that can be tested here — including a test that first asserts the two fields really do disagree, so the rest cannot pass vacuously.

Mutation — turn the curses all(...) into any(...), i.e. give it the reading the .vue had: 2 fail, exactly the read-only and write-only cases.

tests/test_plugin_diskio.py, tests/test_plugin_network.py, tests/test_lazy_views.py — 86 passed. ruff check and ruff format --check clean.

One note, not fixed here

get_views() answers 'DEFAULT' for a key it cannot find, and 'DEFAULT' is truthy, so all(...) treats a missing view as hidden while the .vue guard treats it as visible. That only bites in the window where a disk appears in stats before it appears in views, and fixing it is a different change from this one — happy to follow up if you want it.

Independent of #3725 and #3726.

hide_zero is decided per field but applied per row, and the two front ends
read that per-field verdict in opposite directions.

msg_curse drops a disk only when *all* of its hide_zero_fields are hidden:

    if all(self.get_views(item=..., key=f, option='hidden') for f in self.hide_zero_fields):
        continue

plugin-diskio.vue and plugin-network.vue keep a row only when *all* of them
are visible:

    return (
        (!readBytesRate || readBytesRate.hidden === false) &&
        (!writeBytesRate || writeBytesRate.hidden === false)
    );

Those agree while both fields say the same thing, and disagree the moment
one moves and the other does not -- a disk that has only ever been read, an
interface that has only ever received. Curses shows it; the browser drops
it entirely.

Use the same rule as msg_curse in both components.
@nicolargo nicolargo added this to the Glances 4.5.7 milestone Sep 10, 2026
@nicolargo
nicolargo merged commit 32b684c into nicolargo:develop Sep 10, 2026
17 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants