Skip to content

Design Guidelines

Chris Meyer edited this page May 1, 2024 · 11 revisions

This gives design guidelines for the software.

User Interface Assumptions

  • Target users are offline (laptop, desktop) and on microscope (powerful desktop with trackball).
  • Offline configurations are a laptop with a touchpad and 1920x1080 screen, desktop with larger screen and two button mouse.
  • Microscope configurations are a powerful desktop (dozens+ CPU cores), a large monitor, two button mouse, and trackball.
  • Avoid making assumptions specific to a microscope, i.e. supporting trackball is ok, but should not be required.
  • Attempt to use industry standard mouse tracking concepts and keyboard overloads, i.e. use Adobe PhotoShop, Inkscape, Figma, Word, etc. as examples when possible.
  • Prefer minimal changes to avoid changing customer workflow too much and to limit scope at the same time.

User Interface Internals

  • Prefer to use declarative over the older widgets. May require expanding widget library in some cases.
  • An exception (for now) is the inspector.
  • Canvas items are used for UI within the workspace area; there is no canvas item for editing text.
  • Widgets (declarative) are used for panels and dialogs; they include a text editing widget.
  • DrawingContext is low level drawing for canvas items.
  • As much as possible, create standalone drawing functions that do not depend on other objects.

Responsiveness

  • Anything running in periodic (including async tasks) should complete quickly (< 10ms, preferably faster).
  • Move long running code to threads.
  • Do not use spin loops. Use other threading primitives instead. Loops are ok with a sleep.

Actions, Menu Items, Key Handling

  • Encapsulate user actions into Action objects.
  • Actions get decorated with titles, icons, key overloads, etc.
  • Support undo. Prefer "undo log" over "undoable command" when possible.
  • For key handling in a context (e.g. raster image), use Window to translate key to action.
  • Prefer to make changes to model using actions rather than directly.
  • Allows for configurable key mapping, recording, encapsulation, etc.
  • Handle keys in panels and dialogs traditionally; but use actions where possible.
  • Action icons should have an SVG source (in artwork directory) and be rendered to 64x48 PNG for high resolution displays.

Data Model

  • Prefer abstract Model (e.g. DisplayLayer) over explicit classes (e.g. DisplayItem).
  • The Model technique supports easier maintenance going forward.

Use of Python, NumPy, and SciPy

  • Nearly all computation should be relocated to niondata.
  • Ok to access properties of NumPy arrays, but keep computations in niondata if possible.
  • Avoid using Python tricks - keep code simple and translatable to other programming languages.
  • Use specific type annotations in all possible cases. Use mypy for type checking.

General vs Specific

  • Keep microscopy-specific code in packages.
  • The nionswift project is meant to be general data processing; not specific to a particular field.
  • Some exceptions still exist.

Utility Panels

  • Use dockable panels for UI where user needs the information to be constantly available when in use and when there is only a single instance needed.
  • Use floating panels for UI where the user needs the information intermittently (such as during editing) or when there may be more than one instance needed.

The cursor position is an example of a dockable panel.

The computation editor is an example of a floating panel.

API

  • In libraries and other public API, try to keep existing functions working for backwards compatibility.
  • Adding arguments with default values to end of parameter list or keyword only parameters is usually ok.
  • For callbacks, prefer to always define **kwargs to allow for additional future arguments.
  • Use dict with a version key to introduce strong separation between modules instead of importing modules.
  • If using classes and it is not possible to maintain backwards compatibility...
  • Migrate to new API using code like from nion.data import ABC2 as ABC or from nion.data2 import ABC

Testing

  • Write tests for any non-obvious code. Tests not only provide regression tests, but also code explanation.
  • New code or bugs can be tested by writing failing test, adding feature/fixing bug, and ensuring test passes with new code.
  • Before commit, it is useful to confirm test fails with old code and succeeds with new code.
  • Try to write initial tests to confirm main functionality; write additional tests only when a problem is discovered.
  • Using this test strategy minimizes the initial effort and avoids the almost-impossible "test every possible case" scenario.
  • Do not use random numbers in tests. If required for some reason, seed the random number generator for repeatability.

Versioning File Formats and API

  • Minor releases should not change file formats or deprecate API
  • Minor releases can extend API
  • Major releases can change file formats, but must seamlessly load old versions
  • Use file format transformations (example here) if possible to ease migration.
  • Used file formats: profile, project, data item
  • Data item format defines metadata but not storage mechanism, which can be ndata, hdf5, etc.

TODO

  • Plug-in components.
  • Color themes.
  • Notifications.
  • Architectural decision records.