Skip to content

feat(gui): readable figures in Connections and Statistics, and two table fixes - #132

Merged
donislawdev merged 5 commits into
masterfrom
feat/search-bar-placement
Aug 18, 2026
Merged

feat(gui): readable figures in Connections and Statistics, and two table fixes#132
donislawdev merged 5 commits into
masterfrom
feat/search-bar-placement

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Five changes to the two tables and the Statistics page, each with its own commit.

Traffic in Connections is shown in the unit that fits the number

The five traffic columns and the footer under them were fixed at bytes / 1024 with one decimal.
A 5 GB flow therefore read 5242880.0 - nine digits with no grouping - and a ninety-byte DNS
answer read 0.0, which says nothing happened on a row that exists because something did. One
fixed unit cannot serve both the user whose traffic is megabytes and the one whose traffic is
kilobytes, and the connection log holds both in the same table.

Every cell now carries whichever of B, KB, MB or GB fits its own value (utils.human_bytes,
1024-based, three significant digits), so the [KB] has left the headers and the five column
tooltips no longer promise kilobytes.

Per cell rather than per column, and that is the decision worth recording. A column-wide unit
taken from the largest row keeps the column arithmetically comparable, but at 200 000 flows it
renders everything below the first screen as 0.0. Per-cell pays for that in cross-row reading
instead, and it pays where nobody looks: the pair actually read across a row is down against
down seen, the same traffic before and after impairment, within an order of magnitude even at
99% loss, while down against up differs thousandfold and is never read as a pair. Three
significant digits cap a cell at seven characters, so the columns stay right-aligned and
scannable. avg keeps raw bytes: a packet size is 40-65535 B, where 1460 beats 1.43 KB.

Unaffected, and checked rather than assumed: sorting works on the raw numbers in views.DERIVED,
both CSV exports stay raw bytes, the search matches process, protocol, address and ports only, and
the kb column id keeps its name because it is written into the saved layout of every user who has
sorted this table. Ctrl+C copies what the cell shows, so a traffic cell now arrives as 5.24 GB;
the machine-readable path remains the CSV export.

Also in this branch

  • A header tooltip describes its own column, hidden ones or not. identify_column answers with
    a display position, and the resolution indexed that into the full column list, so hiding one
    column made every header to its right explain its neighbour. Tk is asked to translate now. The
    event log gets the fix for free.
  • The figures on the Statistics page can be copied - a context menu per value, and buttons for
    the whole session panel or the whole counter grid. The text is built from the same registries the
    screen renders from, so it cannot drift from what is on screen.
  • The search box on the Control page sits against the page margin instead of floating in the
    middle of it.

Verification

  • Full suite: 1164 passed, twice (before and after the last edits). GUI smoke and the real-Tk
    render check at 1366x768 in both languages: OK.
  • Real Tk, both languages, a snapshot spanning 0 B to 5 GB: nothing clips, the widest cell being
    1023 MB and the tightest column keeping 23 px of slack. The fake tkinter cannot answer this -
    it has no font metrics and no column geometry.
  • A repaint costs 2.1x what it did (0.28-0.43 ms against 0.13-0.21 ms at a 23-slot viewport,
    measured paired because the absolutes drifted by half between runs on an idle machine). That is
    once per 700 ms tick.
  • Mutation registry: three new entries for this change, all caught; the full registry run is green
    with the canary reporting BROKEN as it must.

Not done

  • Digit grouping for packets and dropped, which are also long at 200 000 flows.
  • The Statistics session panel keeps its fixed MB, and the chart axis its fixed KB/s.
  • The decimal separator stays a dot everywhere, as it is in the rest of the program.

🤖 Generated with Claude Code

donislawdev and others added 5 commits August 18, 2026 15:53
…le of it

Reported after using it: the bar looked like something dropped on top of the
page rather than part of it - a bit high, and floating in empty space.

Measured on real Tk at 1200x900: the bar's right edge lined up with the section
cards, but the BOX ended 65 px short of them, because the count was pinned to
the margin on its right. The box is what the eye calls "the search", so the box
is what has to be flush. The count moved to the LEFT of the label, where it
still cannot push anything around - everything to its right has a fixed width -
and the box now ends level with the cards beside it.

It also sits a few pixels lower, tight to the content it belongs to instead of
hanging under the tab strip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every value on that page is a label, which cannot even be selected, so the
computer name, the addresses and the counters could only be retyped by hand into
a bug report.

Two ways in, matching what the connection table already does: a right-click menu
on any value, its caption or its cell - copy that one value, or the whole tab -
and a button per panel, "Copy session details" beside the repro buttons and
"Copy counters" under the grid. The button is also the keyboard path, because a
menu only a mouse can open is not an answer for everyone.

Both texts are built from the registries the panels render, never a second
hand-written list, so a row added to the registry is copied the day it appears.
The format is what the tab shows, translated captions and units included -
that is what makes it paste-able.

Ctrl+C is deliberately not bound here. The connection table and the event log own
it on their own widgets, and a root binding without add= replaces the one before
it, which is the defect the Control page's Ctrl+F had to be rescued from. A test
asserts this page adds no root binding for it.

The confirmation is logged only after reading the clipboard back: copy_to_clipboard
logs its own failure, so a success line printed beside it would contradict the
error the user had just read. The fake tkinter gained clipboard_get for the same
reason - without it the round trip raised an AttributeError that the crash logger
swallowed, which is a registered fault on every test that copies.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reported from the running program: hide a column in Connections and every header
to its right explained its neighbour - with only a couple of columns left, the
tooltip could describe a column that was not on screen at all.

`identify_column` answers with a DISPLAY position: "#2" is the second column
currently shown, not the second in the registry. The resolution indexed that into
the full column list, so the two agreed only while everything was visible. Tk is
asked to translate now - `column(spec, "id")` is correct whatever is hidden - and
the old arithmetic stays as a fallback for a Tk that refuses the query.

Measured on real Tk before writing anything: with displaycolumns ("a", "c"),
hovering "c" reports "#2" and the old code answered "b". Verified after the fix by
walking the real header strip at five visibility settings, and the same walk was
re-run with the old resolution patched back in, where it fails exactly as
reported. A check that cannot fail proves nothing.

Sorting was never affected - heading commands bind per column identifier. The
event log gets the fix for free, being the same widget.

Why the suite was quiet: the existing tooltip test stubs identify_column to "#3"
and asserts columns[2], which encodes the buggy rule, and it never hides a column.
The fake tkinter modelled neither displaycolumns nor column("#N", "id"), putting
the whole class out of reach. It resolves a display spec against the shown columns
now, and the new test hides columns before it looks.

Also fixes a latent race in test_failsafe.py, found by this run and unrelated to
the product: the unloading-driver fake raised 10 ms after start, which the capture
loop turns into a fail-stop, so a slow main thread saw a session that had already
stopped. It waits to be closed before raising now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The connection table renders every traffic column as bytes/1024 with one
decimal. A 5 GB flow therefore reads "5242880.0" - nine digits with no
grouping - and a 40-byte ICMP row reads "0.0", which says nothing happened
on a row that exists because something did.

This is the helper those columns will use. The unit follows the VALUE
rather than the column, so the same table serves a user whose traffic is
megabytes and one whose traffic is kilobytes. Three significant digits keep
the width at seven characters, which is what lets a column of them stay
right-aligned and readable down the page. 1024-based and spelled "KB", the
way the presets, the chart axis and both READMEs have spelled it since the
first version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The five traffic columns and the footer under them were fixed at bytes/1024
with one decimal. A 5 GB flow therefore read "5242880.0" and a ninety-byte
DNS answer read "0.0" - and one fixed unit cannot serve both the user whose
traffic is megabytes and the one whose traffic is kilobytes, which the
connection log holds in the same table. Each cell now carries whichever of
B, KB, MB or GB fits its own value, so the "[KB]" has left the headers and
the five tooltips no longer promise kilobytes.

Per CELL rather than per column, and that is the decision worth recording. A
column-wide unit taken from the largest row keeps the column arithmetically
comparable, but at 200 000 flows it renders everything below the first
screen as "0.0". Per-cell pays for that in cross-row reading instead, and it
pays where nobody looks: the pair actually read ACROSS a row is "down"
against "down seen", the same traffic before and after impairment, within an
order of magnitude even at 99% loss, while "down" against "up" differs
thousandfold and is never read as a pair. Three significant digits cap a
cell at seven characters, so the columns stay right-aligned and scannable.
"avg" keeps raw bytes: a packet size is 40-65535 B, where "1460" beats
"1.43 KB".

Untouched, and checked rather than assumed: sorting works on the raw numbers
in views.DERIVED, both CSV exports stay raw bytes, the search matches
process, protocol, address and ports only, and the "kb" column id keeps its
name because it is written into the layout of every user who has sorted this
table. Ctrl+C copies what the cell shows, so a traffic cell now arrives as
"5.24 GB" - the machine-readable path remains the CSV export.

Measured on real Tk in both languages, against a snapshot spanning 0 B to
5 GB: nothing clips. The widest cell is "1023 MB" and the tightest traffic
column keeps 27 px of slack at 100% scaling, 45 px at 150%. A repaint costs
1.9-2.1x what it did, paired against the old formatter because the absolutes
drifted by half between runs - once per 700 ms tick.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit 47bf1a2 into master Aug 18, 2026
8 checks passed
@donislawdev
donislawdev deleted the feat/search-bar-placement branch August 18, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant