Brand UI, Excel spreadsheet link, worked example and a test suite - #7
Merged
Conversation
The window had no application icon at all and wore a dated light-blue Windows-XP-era style sheet whose colours matched nothing in the brand. Icon Bundle the crystal-ball logo inside the package at eight resolutions plus a Windows .ico, and install it as both the QApplication and the window icon. Register an AppUserModelID so the Windows task bar shows it instead of the host interpreter's icon. Theme Add vatic/theme.py: a closed palette of white plus the four hues taken from the banner (#2323FF, #7E3DFF, #C04AFF, #24AEFF), with every other value derived as a tint or shade of one of them. White is the dominant surface and the lightest hue, cyan, carries the primary action. Ships a contrast auditor; every ink-on-surface pairing clears WCAG AA. JetBrains Mono is bundled under the SIL OFL so the interface renders identically on machines that do not have it installed. Layout Add a branded header carrying the logo and the active analysis name, and with it the Run Simulation button, which previously existed only as F5 and a menu entry. Title the group boxes that were rendering as empty frames, give the assumption and formula tables their own add/remove actions, and put the input sidebar in a scroll area. The window's minimum height drops from 1197px to 523px, so it now fits a 1366x768 laptop screen. Charts Serve plotly.js from a bundled local copy and re-plot through Plotly.react against a persistent page, so charts no longer wait on a CDN round-trip (measured at 6.3s here) and work with no network at all. Re-colour all 13 chart types and the PDF/PPTX exports onto the brand palette; Box and Violin now cycle the ramp instead of drawing every series in one colour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Palette Add four light washes, one per logo hue, and tint each sidebar panel with a different one so the column reads as distinct sections instead of a single undifferentiated stack. Titles pick up the matching hue. All four are pale enough that body and muted ink still clear WCAG AA on them; the contrast audit covers every new pairing. Popups A QMenu and a combo box drop-down are top level windows, so the style sheet's border radius left the square native corners painted behind the rounded edge. Popups are now given a translucent background, applied when each menu is created rather than mid-show so Qt never has to recreate the native window while it is appearing. Scroll wheel A spin box or combo box inside the new scrolling sidebar swallowed the wheel and edited itself, so scrolling past the iteration count silently changed it and invalidated the results. Unfocused controls now hand the wheel back to the scroll area; focused ones still adjust normally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Formulas The expression column was edited in place, so a formula was typed into a cell a few characters wide and scrolled out of sight as it grew. The formula table is now read-only and double-clicking a row, the toolbar button, the Edit menu or the context menu all open a ForecastDialog with full-width fields for name, expression and the spec limits, validated before the row is written. The keypad still appends to the selected row, which never needed a cell editor. Statistics compute_statistics now also reports variance, skewness and kurtosis, which the reference implementation reports per variable and this one did not. The conventions deliberately match it: population variance, the standardised third moment with no sample-size correction, and Pearson kurtosis where a normal distribution sits at 3.0. Excess kurtosis is offered alongside it since that is what most readers expect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The project had no examples and no tests at all. examples/ seal_tolerance.py runs the Double-D seal gland tolerance stack-up from the original vatic project through this app's own API, with no spreadsheet involved: eight toleranced dimensions modelled as Normal(nominal, tol/3), three intermediate areas, and two characteristics judged against their spec limits. build_seal_workbook.py regenerates the same model as a spreadsheet with named ranges, ready for a future spreadsheet-driven run. The upstream repository carries no licence, so its workbook is not redistributed; the generator rebuilds an equivalent one from the same published dimensions and formulas, which also keeps the spreadsheet reproducible from source control instead of being an opaque binary. tests/ 72 tests covering the statistics, the capability metrics, the formula evaluator and the theme. The maths is checked against closed-form values and an independent derivation rather than against itself, and three tests pin down defects in the reference implementation that are deliberately not reproduced: the normal density used where the cumulative distribution is required, the Cpm exponent written with bitwise XOR, and the ignored zshift that left Zst equal to Zlt. The seal example is verified end to end: every simulated mean is checked against the closed-form nominal, including the fact that Gland Fill % overfills at nominal and must report a negative Cpk. Adds a Tests workflow across Linux, Windows and macOS on 3.12 and 3.13. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mcerp draws its Latin hypercube through numpy's legacy global random state, and nothing seeded it, so two runs of the same model returned different Cpk, different PPM and a different tornado ordering. That is untenable when the output is used to sign off a design, and it made the maths untestable. Adds seed_sampler() and a Seed control beside the iteration count, defaulting to "random" so behaviour is unchanged unless a seed is chosen. The worked example now pins a seed, so its printed numbers are stable, and two tests assert that the same seed reproduces a run exactly while a different one does not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vatic began life as a spreadsheet application: Excel was the model and Python only drove the sampling. That capability was missing here entirely. This adds it back, several orders of magnitude faster and without the data loss. Engine vatic/sheetmodel.py holds the pure data model (cell references, tagged assumptions and forecasts) and imports on every platform. vatic/excel/ is the Windows half: typed errors with the worksheet-error decoder, a session owning the COM connection, and a runner. The reference implementation wrote one cell and recalculated once per trial -- a cross-process call per cell per iteration, about eighteen minutes for ten thousand trials. The runner instead writes the whole sample matrix to a hidden sheet, points each assumption cell at its column through INDEX, and builds a one-variable data table over a trial index, so Excel resolves every trial in a single recalculation. Measured against live Excel 16.0: 1.4s for ten thousand trials, 0.45s to re-run. Three details were established by probing Excel rather than by assumption. Workbook.Calculate does not exist, so the runner uses the Application and Worksheet forms. An unfilled cell reports Color 16777215 with Pattern xlNone, so restoring the colour alone leaves a white fill behind and the pattern must be restored too. A data table's input cell has to live on the table's own sheet or Excel rejects it outright. Restoration runs in a finally block, so a crash or a cancel still puts the workbook back. Crucially the original FORMULA is restored, not its computed value: the reference implementation snapshotted the value and wrote that back, silently destroying a formula in any tagged input cell. CoUninitialize is deliberately not called when a session closes. Callers routinely still hold Range proxies, and tearing the apartment down beneath them turns their eventual collection into RPC_E_DISCONNECTED, which crashes the interpreter instead of raising. UI A Spreadsheet menu connects a workbook, tags the cell selected in Excel as an assumption or a forecast, and clears the tags again. Tagged cells are tinted in the sheet and their original fill is put back. Run Simulation drives Excel when a workbook is connected and the in-app model otherwise, and results flow into the same charts, statistics and reports. Help > Using vatic with Excel explains the whole flow, what the run changes, what it restores, and what each failure means. Tests Ten of them drive a private hidden Excel instance and skip where Excel is absent, so they never touch a session the user has open. They check the results against numpy exactly, that a formula in a tagged cell survives, that the scratch sheets and application settings are restored, that cancellation still restores, and that worksheet errors are counted rather than silently read as zero. Thirty more cover the model and error decoder on every platform. Also fixes an in-table editor that was too short for its own text, and declares pywin32 as an optional dependency so it stops being pruned. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vatic/__init__.py imported main eagerly, so `import vatic.analytics` pulled in the whole Qt stack, QtWebEngine included. On a head-less Linux runner that import fails outright for want of the system libraries Qt needs, which took the Ubuntu test job down even though nothing under test uses Qt. The numerical, theme and spreadsheet-model modules have no Qt dependency of their own, so nothing should force one on them. `main` now resolves through a module __getattr__: `from vatic import main` still works, and importing any other module pulls in zero Qt modules. Also ignores Excel's ~$ owner files, which appear beside any workbook that gets opened and must never be committed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… button CI excel_available() only checked for Windows and pywin32, so on a GitHub Windows runner it reported True and the spreadsheet tests tried to start an Excel that is not installed, failing with CO_E_CLASSSTRING. It now confirms Excel is registered by looking the ProgID up in the registry, which proves the install without launching anything. That HRESULT is also mapped, so a user without Excel gets the explanation rather than a raw COM error. Contrast The disabled primary button painted white text on a near-white fill, 1.37:1. It now uses muted ink on the sunken surface. A new test walks the generated style sheet and fails on any rule that sets both a colour and a background under 3:1. The existing audit only covered pairs the palette declares, which is exactly how this one slipped through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A selected row showed white text on a near-white background. The style sheet painted the row with the pale selection wash, but a cell's text is drawn by the delegate using selection-color, which the global QWidget rule had set to white for the solid blue selection used by lists and menus. The result was white on #E5E5FF, a contrast ratio of 1.16:1. Both selection colours are now pinned on the table itself, so the pale row carries near-black ink at about 14:1. Setting them through the widget's palette does not work: a style sheet's selection-color always wins over the palette, which is why the first attempt at this changed nothing. A test now walks the generated sheet and fails any rule that sets a selection background without a legible selection-color to go with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six commits. The app had no icon, no tests, no examples, and no way to touch a
spreadsheet; it now has all four.
Application icon
setWindowIconappeared nowhere in the repo, so the app inherited the Pythoninterpreter's icon. The crystal-ball logo now ships inside the package at eight
resolutions plus a Windows
.ico, installed as both the application and windowicon, with an AppUserModelID so the task bar shows it. Verified from a clean
wheel install.
Interface
vatic/theme.pyreplaces the hand-written XP-era style sheet with a tokensystem and a contrast auditor. Body ink is a neutral near-black at 15.6:1;
the four logo hues are reserved for the primary action, selection, panel
washes and the charts. Every audited ink-on-surface pairing clears WCAG AA,
and a test fails the build if that stops being true.
Layout: a branded header carrying the logo and the active analysis, and with it
the Run Simulation button, which previously existed only as F5 and a menu
entry. Group boxes that rendered as empty frames got titles, the tables got
add/remove actions, and the input sidebar scrolls -- the window's minimum
height drops from 1197px to 523px, so it fits a 1366x768 laptop.
Formulas are edited in a dialog rather than in a cell editor a few characters
wide. JetBrains Mono is bundled under the SIL OFL so the interface renders the
same on machines that do not have it.
Charts serve plotly.js from a bundled copy through
Plotly.reactagainst apersistent page, so they no longer wait on a CDN round-trip (6.3s measured
here) and work with no network at all. All 13 chart types and the PDF/PPTX
exports moved onto the brand palette.
Maths
compute_statisticsnow also reports variance, skewness and kurtosis, whichthe reference implementation reports per variable and this one did not. The
conventions match it deliberately: population variance, the standardised third
moment with no sample-size correction, and Pearson kurtosis where a normal
distribution sits at 3.0.
Runs are reproducible. Nothing seeded numpy's global state, which mcerp samples
through, so two runs of the same model returned different Cpk, different PPM
and a different tornado ordering. There is now a Seed control, defaulting to
"random" so behaviour is unchanged unless a seed is chosen.
Excel link
vatic began as a spreadsheet application -- Excel was the model, Python only
drove the sampling -- and that capability was missing here entirely.
vatic/sheetmodel.pyis the pure data model and imports on every platform.vatic/excel/is the Windows half: typed errors with a worksheet-errordecoder, a session owning the COM connection, and a runner.
The original wrote one cell and recalculated once per trial: a cross-process
call per cell per iteration, about eighteen minutes for ten thousand trials.
The runner writes the whole sample matrix to a hidden sheet, points each
assumption cell at its column through
INDEX, and builds a one-variable datatable over a trial index, so Excel resolves every trial in a single
recalculation. Measured against live Excel 16.0: 1.4s for ten thousand
trials, 0.45s to re-run.
Three details were settled by probing Excel rather than by assumption:
Workbook.Calculatedoes not exist; the Application and Worksheet forms do.Color 16777215withPattern xlNone, so restoringthe colour alone leaves a white fill behind.
rejects it outright.
Restoration runs in a
finallyblock, so a crash or a cancel still puts theworkbook back, and it restores the original formula rather than its
computed value -- the original snapshotted the value and wrote that back,
silently destroying a formula in any tagged input cell.
CoUninitializeisdeliberately not called on close: callers still hold Range proxies, and tearing
the apartment down beneath them turns their collection into
RPC_E_DISCONNECTED, which crashes the interpreter instead of raising.A Spreadsheet menu connects a workbook and tags the cell selected in Excel
as an assumption or a forecast; tagged cells are tinted in the sheet and their
original fill is restored. Help > Using vatic with Excel documents the
whole flow, what a run changes, what it restores, and what each failure means.
Examples and tests
Neither existed before.
examples/seal_tolerance.pyruns the Double-D seal gland tolerance stack-upfrom the original project through this app's API.
build_seal_workbook.pyregenerates the same model as a spreadsheet with named ranges. The upstream
repository carries no licence, so its workbook is not redistributed; the
generator rebuilds an equivalent one from the same published dimensions and
formulas, which also keeps it reproducible from source control.
113 tests, checked against closed-form values and independent derivations
rather than against the implementation. Ten drive a private hidden Excel
instance and skip where Excel is absent, so they never touch an open session.
A new Tests workflow runs Linux, Windows and macOS on 3.12 and 3.13.
Notes for review
the ~1.4s a run takes. A COM apartment belongs to its creating thread and a
correct threaded version needs message pumping; at 1.4s that trade seemed
right, but it is worth doing properly before anyone points this at a heavy
workbook.
calls
ss.norm(...)without importingscipy.stats, relying on afrom mcerp import *leak that modern mcerp closed with__all__. Its otherdefects -- the normal density used where the CDF is required, a
Cpmexponent written with bitwise XOR, and an ignored
zshiftleaving Zst equalto Zlt -- are not reproduced here, and a test pins down each one.
🤖 Generated with Claude Code