NAP-10 — Migration of the event system#

Author:

Wouter-Michiel Vierdag <michiel.vierdag@cellonautica.ai>

Created:

2026-26-07

Status:

Draft

Type:

<Standards Track | Process>

Version:

1

Purpose#

Napari currently relies on several mechanisms for propagating information throughout the application, including Qt signals and slots, vispy events, napari.utils.events, and psygnal. These mechanisms are used interchangeably in different parts of the codebase, despite representing different concepts: some notify that state has changed, while others dispatch user interactions before any state change has occurred. As a result, application logic becomes coupled to backend-specific APIs and contributors must understand multiple communication models.

A key observation motivating this proposal is that not all communication in napari has the same semantics. State-change notifications (signals) and interaction dispatch (events being dispatched, though not in the traditional sense of having one targeted receiver) serve different purposes and should not necessarily share the same abstraction.
The goal is therefore not to replace every event with psygnal, but to establish a coherent application dispatch model with explicit semantics for signals, events, and the relationship between them. This document proposes an initial migration strategy rather than a fixed implementation plan. We expect to refine the design as experience is gained during implementation, documenting significant architectural decisions and any deviations from the original proposal.

image

Terminology and concepts#

Throughout this proposal, the terms event and signal are used in their architectural sense rather than according to napari’s current implementation. The distinction is important because one of the goals of this migration is to clarify the semantics of communication throughout napari. Much of the confusion in previous discussions arose because the word event has been used to describe both user interactions (such as mouse presses) and state-change notifications (such as layer properties changing), despite these representing fundamentally different concepts.

Events in Qt terminology#

Events in the sense of Qt are objects derived from QEvent (or one of its subclasses) that represent something that happened either inside an application or as a result of external activity that the application needs to respond to.1 An event is delivered to a specific QObject, which acts as the intended receiver. Every QObject has an event() method that acts as a dispatcher: it does not normally handle the event itself, but routes it to the appropriate event handler such as QObject.mousePressEvent(), QObject.keyPressEvent(), or QObject.paintEvent(). An event handler is a method of a QObject subclass that contains the code responsible for responding to a particular type of event when it is delivered to that object. If the handler accepts the event, Qt considers it handled. This means that the event is not propagated further. If it is ignored, Qt may attempt to propagate the event to another object, such as a parent widget.

        flowchart TD
    A[External source<br/>Mouse / Keyboard / Timer / Application] --> B[Create QEvent object]

    B --> C[Event Queue]

    C --> D[Qt Event Loop]

    D --> E[Send event to target QObject]

    E --> F["QObject::event(QEvent*)"]

    F --> G{What type of event?}

    G -->|Mouse| H["mousePressEvent()<br/>mouseMoveEvent()"]
    G -->|Keyboard| I["keyPressEvent()<br/>keyReleaseEvent()"]
    G -->|Painting| J["paintEvent()"]
    G -->|Timer| K["timerEvent()"]

    H --> L{Accepted?}
    I --> L
    J --> L
    K --> L

    L -->|Yes| M[Event handled]
    L -->|No| N["Event ignored<br/>May propagate to parent"]

    M --> O[Qt continues event processing]
    N --> O
    

A Qt event is therefore a directed message with three important properties:
  1. An event has a source

    • The operating system

    • The Qt framework

    • The application itself

  2. An event has a target

    • A specific QObject

    • Usually the widget under the mouse cursor, the widget with keyboard focus, etc.

  3. Dispatch chain

    • Qt creates the event

    • The event enters the event queue

    • The event loop retrieves it

    • Qt sends it to the target object

    • QObject.event() dispatches it to the appropriate handler

    • The handler decides whether the event was handled

The key idea is: A Qt event answers the question: “Something happened. Who should handle it?”

Signals (Qt signals and slots)#

Signals and slots are a different mechanism from events.

  • A signal notification emitted by an object to announce that some observable state has changed. The emitter does not know, or need to know, which components are listening.

  • A slot is a function (or method) that is connected to a signal and is executed when that signal is emitted. A slot defines the action that should happen in response to a notification. It says “Perform this action when signal happened”.2

The model looks like this:

        flowchart TD
    A[Object changes state<br/>or something interesting happens] --> B[Object emits signal]

    B --> C[Signal]

    C --> D[Connected slot 1<br/>Callback function]
    C --> E[Connected slot 2<br/>Callback function]
    C --> F[Connected slot 3<br/>Callback function]
    

Unlike events:

  • Signals do not have a single intended receiver.

  • Signals can have zero, one, or many receivers.

  • Signals are connected explicitly by the developer.

  • Signals are not dispatched through QObject.event().

The object emitting the signal does not need to know who is listening or what the listeners will do.

Now connecting a signal to a slot would look something like this:

button.clicked.connect(save_file) # clicked is signal and save_file is the slot

Now clicked could also have other slots connected:

        flowchart LR
    A[QPushButton] -->|emits| B[clicked signal]

    B --> C[save_file slot]

    B --> D[update_status slot]

    B --> E[other connected callbacks]
    

The button does not know:

  • what save_file() does

  • how many listeners exist

  • whether anyone is listening at all

It just emits.

Signal / slots vs Events#

Although both mechanisms communicate information between components, they serve different purposes. Events dispatch interactions to potential handlers, whereas signals notify interested observers that state has already changed.

Qt Event

Qt Signal

Meaning

Something happened

Notify observers about something

Direction

One sender → one target

One sender → many receivers

Receiver

Determined by Qt

Explicitly connected by programmer

Routing

Event queue and event dispatcher

Signal-slot connection

Handler

Event handler (mousePressEvent)

Slot / callback

Propagation

Can be accepted, ignored, or propagated

No propagation

Example

Mouse click, key press, paint event

Button clicked, value changed

A dispatch mechanism determines how information flows between components. Both events and signals are dispatch mechanisms, but they have different semantics. Event dispatch determines which component should respond to an interaction, potentially allowing handlers to consume or propagate it. Signal dispatch broadcasts that state has changed to any interested observers. The remainder of this proposal uses the term dispatch model to refer to the combination of these mechanisms that napari exposes at the application level.

Current terminology in napari#

By now it is perhaps already clear that napari is using terminology different from Qt at the moment. Most of the objects currently exposed through napari.utils.events are semantically signals, despite being called events.3 They are emitted after state has changed, support multiple connected callbacks, and do not participate in event propagation or acceptance. In other words, they behave much more like Qt signals than Qt events. The example below showcasing a napari event that behaves like signal -> slot highlights this:

layer.events.data.connect(callback)

This means that conceptually it looks like this:

        flowchart TD
    A[Layer data changes] --> B[data changes]

    B --> C[layer.events.data]

    C -->|.connect| D[Viewer updates]

    C -->|.connect| E[Plugin reacts]

    C -->|.connect| F[Other listeners]
    

So in short, In napari terminology, an “event” is usually a state-change notification, which is much closer to a QSignal than a QEvent.
Throughout the remainder of this proposal, the term signal refers to state-change notifications, while event refers to dispatching interactions that may be handled or propagated. This distinction reflects the intended semantics of the proposed architecture rather than the terminology used by the current implementation.

Situation in envisioned rendering backend (pygfx)#

For migration, it makes sense to look at how other rendering backends deal with events. From code in pygfx it is clear that the way pygfx talks about events is very similar to the way Qt talks about it. This is clear from their docstring of the Event class4:

class Event:
    """Event base class.

    If a target is set, an event can bubble up through a hierarchy
    of targets, connected through a ``parent`` property.
    To prevent an event from bubbling up, use ``stop_propagation``.

    It is also possible to cancel events, which will stop any further
    handling of the event (also by the same target).

    Parameters
    ----------
    type : Union[str, EventType]
        The name of the event.
    bubbles : bool
        If True, the event bubbles up through the scene tree.
    target : EventTarget
        The object onto which the event was dispatched.
    root : RootEventHandler
        A reference to the root event handler.
    time_stamp : float
        The time at which the event was created (in seconds). Might not be an actual
        time stamp so please only use this for relative time measurements.
    cancelled : bool
        A boolean value indicating whether the event is cancelled.
    event_type : str
        Unused.
    """

Although the details differ slightly (for example, cancelled versus Qt’s accepted state), the overall dispatch model is conceptually very similar to Qt’s: events are dispatched to targets, may propagate, and may be consumed by handlers.

Previous discussions#

The ideas presented in this proposal have been discussed in various forms over several years, motivated by different but closely related goals including improved developer ergonomics, stronger static typing, backend independence, and simplification of napari’s communication infrastructure. Although these discussions often focused on adopting psygnal, they collectively highlight a broader architectural need for a more coherent application dispatch model.

One of the earliest discussions arose from the migration of magicgui to psygnal #3373. That discussion highlighted the usability and maintainability benefits of replacing the dynamic Event objects with explicit, typed signals. In particular, it noted that the current napari.utils.events API relies heavily on dynamically generated event objects and EmitterGroup, making it difficult to discover available events, reason about callback signatures, provide IDE autocompletion, or perform static type checking. By contrast, psygnal provides a Qt-like signals and slots API with explicit signal definitions, improved typing, and callback signatures that are immediately visible to users and developers.
A second discussion, motivated by experiments replacing the vispy canvas with a pygfx backend #7373, identified the event system as a key architectural obstacle to supporting multiple rendering backends. At present, many user interactions originate within vispy before being propagated into napari, tightly coupling application behavior to a specific rendering backend. The discussion proposed that napari should instead own its application-level dispatch model, with rendering backends acting as integration layers that translate between backend-native communication mechanisms and napari’s internal abstractions.
Building on these discussions, @jacopoabramo explored an initial prototype migration to psygnal in #8387, following an earlier discussion on Zulip. The prototype was primarily motivated by improving static typing across the codebase. It demonstrated that replacing EmitterGroup with explicit psygnal.SignalGroup definitions could provide significantly stronger typing and improve IDE support, although accommodating napari’s inheritance hierarchy required additional typing protocols and highlighted several implementation challenges. The prototype also identified opportunities to simplify layer initialization, property setters, and event definitions. It was intentionally presented as an exploratory implementation rather than a mergeable solution, with the goal of informing future architectural discussions.

Finally, #8509 demonstrated that psygnal-based models can coexist successfully within napari by migrating overlays from napari’s custom EventedModel implementation to psygnal.EventedModel. Although limited in scope, this provided an initial proof of concept that parts of napari’s event infrastructure can be migrated incrementally without requiring an immediate, project-wide transition.

Rationale#

The current communication infrastructure has served napari well, but it has become a significant source of architectural complexity. Today, napari uses several communication mechanisms simultaneously, including Qt signals and slots, vispy events, napari.utils.events, and, in some places, psygnal. These mechanisms are present throughout the codebase and represent different communication semantics, but there is currently no clear architectural distinction between them.
As a result, contributors must understand multiple APIs with different connection patterns, callback signatures, and behaviors, while the napari application logic is often coupled directly to backend-specific communication mechanisms. A key motivation for this migration is to establish a coherent application dispatch model for napari with explicit semantics for different types of communication. In particular, napari currently uses the term event to describe both state-change notifications and user interactions, despite these representing fundamentally different concepts.
State changes, such as a layer’s opacity changing or a layer being inserted, are naturally represented as signals: notifications that something has changed and that interested components may react. User interactions, such as mouse and keyboard input, are instead events: dispatched interactions that may require ordering, handling, propagation, or acceptance before application state is modified.
The goal of this migration is therefore not to replace every event with psygnal, but to establish a clear separation between these concepts. Psygnal provides the foundation for napari’s internal signaling infrastructure, while backend-specific event systems such as those provided by Qt and vispy remain encapsulated where they are required. Rather than allowing GUI or rendering frameworks to define napari’s application-level communication model, napari should own the interfaces between application components and treat backend-specific systems as implementation details at the boundaries of the architecture.
Beyond the architectural benefits, the current napari.utils.events implementation has several limitations when used for state-change notifications. These notifications are represented by dynamically constructed Event objects whose available attributes are determined at runtime, making them difficult to inspect, document, and type. Similarly, EmitterGroups are created dynamically, limiting IDE autocompletion and static analysis.
In practice, many of these notifications have simple, well-defined signatures (for example, “opacity changed” or “layer inserted”), yet the current implementation obscures this information behind a generic event object. Psygnal provides a more explicit and familiar signaling model. Signals declare the values they emit, callback signatures are directly visible, and static type checkers and IDEs can infer the expected interfaces. Many contributors are already familiar with the Qt signals and slots model, and psygnal provides similar semantics in pure Python while remaining independent of any particular GUI framework.
Finally, establishing a consistent application-level communication model will simplify the internal architecture and reduce the maintenance burden of supporting multiple overlapping abstractions. It will make communication pathways easier to reason about, improve static typing and documentation, and provide a clearer foundation for future work such as additional rendering backends. While the migration represents a substantial effort, it provides an opportunity to modernize one of napari’s core infrastructure components while preserving the semantics required by both state notifications and user interaction dispatch. Please see below for a more concise display of the problems with the current communication model.

The problem with the current communication model#

Napari currently uses several different mechanisms to communicate between components throughout the codebase:

  • the custom napari.utils.events framework (derived from VisPy)

  • Vispy’s event system

  • Qt signals and slots

  • psygnal (currently used by overlays)

These mechanisms are used in different parts of the codebase time and represent different communication semantics. As a result, different parts of napari communicate using different abstractions, making the flow of information through the application difficult to reason about.
This has several consequences.

Tight coupling to backend libraries#

Much of napari’s internal communication is coupled directly to Qt or vispy because napari application code frequently interacts with their native communication mechanisms. This makes it difficult to separate application logic from GUI and rendering concerns and complicates support for alternative backends.
Ideally, backend-specific communication should remain an implementation detail, with napari exposing a consistent application-level dispatch model regardless of which GUI or rendering backend is being used.

Multiple programming models#

Contributors currently need to understand several communication models:

  • Qt signals and slots

  • Vispy events

  • napari.utils.events

  • psygnal

Each has different semantics, callback signatures, connection mechanisms, and lifecycle rules. This increases the cognitive load for contributors, makes APIs less consistent, and complicates the development of reusable infrastructure.

Dynamic event definitions#

Many state-change notifications are represented using dynamically constructed Event objects and EmitterGroups.

The information carried by these events is determined at runtime, making it difficult to:

  • discover available notifications

  • understand callback signatures

  • provide IDE autocompletion

  • perform static type checking

  • generate accurate documentation

Although many notifications have simple, well-defined interfaces (for example, “opacity changed” or “layer inserted”), these interfaces are hidden behind generic event objects.

Threading#

Napari already contains components that execute across multiple threads, including computational workloads and GUI-related components. Threading behaviour is currently influenced by the communication mechanisms provided by backend libraries. For example, Qt imposes thread-affinity requirements for GUI objects, while rendering backends have their own communication models.
Although these constraints cannot be removed, they should remain encapsulated within backend integration layers wherever possible. Napari components should not need to understand backend-specific threading behavior simply to communicate with one another.
Furthermore, users / developers would benefit from improved communication of propagating events to the main thread.

Proposed design principles#

The following principles are intended to guide the migration. They describe the desired architecture rather than prescribing every implementation detail. Where practical considerations require deviations from these principles, those decisions should be documented.

1. One application dispatch model#

Napari should expose a single, coherent application dispatch model for communication between internal components. Backend libraries such as Qt and vispy will continue to use their native communication mechanisms internally, but these should remain implementation details encapsulated within napari-owned integration layers. Application code should communicate using napari’s dispatch model rather than directly depending on backend-specific APIs.

2. Distinguish signals from events#

The migration should make a clear architectural distinction between signals and events. Signals represent state-change notifications. For example:

  • layer opacity changed

  • layer inserted

  • camera zoom changed

Events represent interactions that are being dispatched before application state changes. For example:

  • mouse press

  • mouse drag

  • key press

Signals and events have different semantics and should not be forced into the same abstraction.

3. Use explicit, typed signals#

State-change notifications should be represented using explicit psygnal signals. For example:

opacity = Signal(float)
layer_inserted = Signal(Layer)
camera_zoom = Signal(float)

rather than dynamically constructed event objects:

event.value
event.source
event.type

Explicit signal definitions improve:

  • static type checking

  • IDE autocompletion

  • documentation generation

  • developer understanding of APIs

4. Separate dispatch semantics from payloads#

The mechanism used to dispatch information should be independent of the objects representing that information. Signals should emit typed values or domain objects. Events should carry well-defined payload types describing the interaction being dispatched. For example, an interaction event might carry a typed MouseEvent object containing position, button, modifiers, and other interaction-specific information. Separating dispatch semantics from payload representation makes communication easier to understand while avoiding the limitations of dynamically populated event objects.

5. Encapsulate backend communication#

Qt, Vispy, and future rendering or GUI backends should continue to use the communication mechanisms that are most appropriate for those frameworks. Rather than replacing these mechanisms, napari should encapsulate them within backend integration layers. This keeps backend-specific communication localized while exposing a consistent application-level API to the rest of napari and to plugins.

6. Preserve behaviour before improving APIs#

The migration should prioritize behavioral compatibility over immediate API redesign. Existing semantics—including callback ordering, event propagation, lifecycle behaviour, and plugin expectations—should be preserved wherever practical. API improvements should be introduced incrementally through well-defined deprecation cycles.

7. Make threading behaviour explicit#

Backend-specific threading constraints should remain encapsulated wherever possible. Where application-level communication crosses thread boundaries, the semantics should be clearly documented. This proposal does not attempt to redefine how backend frameworks manage threads, but it should establish clear guidance for how signals, events, and backend integrations interact with existing threading models.

8. Minimise custom infrastructure#

Where appropriate, the migration should favour standard psygnal abstractions over custom napari infrastructure. State-change notifications should primarily use Signal and SignalGroup, avoiding additional napari-specific abstractions unless they provide clear architectural benefits. Interaction events may require richer dispatch semantics than psygnal alone provides, but these should be implemented using the smallest amount of custom infrastructure necessary.

9. Design for long-term maintainability#

The communication infrastructure is fundamental to napari’s architecture and plugin ecosystem. The migration should favour simple, explicit, and well-documented abstractions that can evolve over time without requiring repeated ecosystem-wide API changes. A successful design should make it easier to support future rendering backends, improve tooling and documentation, and reduce the long-term maintenance burden of the communication infrastructure.

Proposed architecture#

The proposed architecture establishes a single application communication model for napari while recognizing that different kinds of communication have different semantics. State-change notifications should be represented using psygnal signals, while interaction events (such as mouse and keyboard input) should continue to be treated as events with richer dispatch semantics. Rather than attempting to replace every communication mechanism with psygnal, the goal is to ensure that napari owns the application-level interfaces through which signals and events flow.
Backend libraries, including Qt, Vispy, and future rendering or GUI frameworks, will continue to use their native communication mechanisms internally. Backend-specific communication should remain encapsulated within backend integration layers maintained by napari. These integration layers expose stable napari interfaces without leaking backend implementation details into the rest of the application. Application components—including models, viewer state, controllers, plugins, and backend integrations—should therefore communicate through explicit napari-owned signals and events rather than directly depending on backend-specific APIs. Conceptually, the architecture becomes:

        flowchart TB

    User["User interaction<br/>(mouse, keyboard, touch)"]

    subgraph Backends
        Qt["Qt"]
        Vispy["VisPy"]
        Pygfx["Future backend<br/>(e.g. pygfx)"]
    end

    subgraph Napari["napari"]
        Adapters["Backend integration layers"]

        Events["Interaction events<br/>(dispatch)"]

        Models["Models"]
        Viewer["Viewer state"]
        Controllers["Controllers"]

        Signals["psygnal Signals<br/>(state-change notifications)"]

        Plugins["Plugins"]
    end

    Renderer["Rendering backend"]

    User --> Qt
    User --> Vispy
    User -. future .-> Pygfx

    Qt --> Adapters
    Vispy --> Adapters
    Pygfx --> Adapters

    Adapters --> Events

    Events --> Models
    Events --> Viewer
    Events --> Controllers

    Models --> Signals
    Viewer --> Signals
    Controllers --> Signals

    Signals --> Plugins
    Signals --> Controllers
    Signals --> Viewer

    Viewer --> Renderer
    Controllers --> Renderer
    

Under this architecture:

  • psygnal.Signal and SignalGroup become the primary mechanism for state-change notifications.

  • EmitterGroup is gradually replaced by explicit SignalGroup definitions.

  • State changes emit typed values directly (for example, Signal(float) for opacity changes).

  • Rich interaction events (for example mouse and keyboard input) remain event objects with well-defined semantics, including propagation and acceptance where appropriate.

  • Backend integrations remain responsible for translating between backend-specific communication mechanisms and napari’s application interfaces.

  • Qt communication remains Qt communication where appropriate, and similarly for vispy and future rendering backends, avoiding unnecessary translation for high-frequency internal operations.

This architecture separates application behavior from backend implementation details while recognizing that backend frameworks will continue to manage their own communication internally. Supporting a new rendering or GUI backend therefore becomes primarily a matter of implementing a new backend integration layer rather than introducing another communication model throughout the application.

Migration strategy#

The migration should be incremental, allowing the communication model to evolve while maintaining behavioral compatibility wherever practical. Replacing the existing infrastructure in a single step would introduce a large, difficult-to-review change with a high risk of regressions across models, rendering, GUI components, and plugins. Instead, the migration should proceed in small, independently reviewable stages, with compatibility maintained throughout the transition.
Where possible, new APIs should be introduced before legacy APIs are deprecated, allowing contributors and plugin authors to migrate gradually.

Backward compatibility#

A compatibility layer should be introduced before significant internal migration begins. The compatibility layer should allow the existing napari.utils.events API to coexist with the new signaling infrastructure. Possible mechanisms include:

  • re-emitting psygnal signals through existing EmitterGroup interfaces

  • temporarily supporting both callback styles

  • wrapping legacy Event objects where necessary

  • emitting deprecation warnings for legacy APIs

The exact implementation remains an open design question and should be informed by experience from earlier migrations, including the overlays migration. The compatibility layer is intended only as migration infrastructure and should eventually be removed.

Proposed migration phases#

1 Compatibility infrastructure#

Establish the foundations required for incremental migration.

This includes:

  • compatibility wrappers

  • bridging between EmitterGroup and SignalGroup

  • naming conventions

  • migration utilities

  • any additional functionality required from psygnal

The success criterion for this phase is that new components can adopt the new communication model without breaking existing code.

2 Internal state models#

Migrate internal models that primarily communicate state changes. Likely candidates include:

  • EventedModel

  • Camera

  • Selections

  • Dimensions

  • ViewerModel

  • other internal state models

  • Layer after planned refactor

These components are relatively self-contained and primarily emit state-change notifications, making them good early candidates for migration. Where appropriate, psygnal.EventedModel should replace napari’s custom implementation.

3 State change consumers#

Update application components that consume state-change notifications. This includes:

  • controllers*

  • viewer logic

  • actions

  • menus

  • Qt widgets

  • plugin-facing APIs

At this stage, the majority of application state changes should be communicated through explicit typed signals.

*With regards to what is meant with controllers here, controller should:

  • listen to events of rendering backend and GUI frontend and tell the napari models what changed.

  • based on changes to the napari models tell the rendering backend / GUI frontend what to draw / update. In this respect something like the LayerControls can be seen as a controller and also everything that inherits the VispyBaseLayer as it translates between napari models and vispy.

4 Backend integration#

Review communication at the boundaries between napari and backend frameworks. Rather than replacing Qt or vispy communication internally, this phase focuses on ensuring that backend-specific communication remains encapsulated within well-defined integration layers. Particular attention should be paid to high-frequency interaction events, such as mouse movement and dragging, where unnecessary abstraction could introduce measurable overhead. Any proposed changes should therefore be benchmarked before adoption. Exactly how interaction events should be represented remains an open design question and should be informed by experimentation.

5 Deprecation and cleanup#

Once the new communication model has matured:

  • publish migration guidance

  • update developer documentation

  • deprecate legacy APIs

  • remove obsolete compatibility infrastructure

The length of the deprecation period should depend on the complexity of the compatibility layer and the impact on plugins.

Open design questions#

Several important questions remain intentionally unresolved.

These include:

  • the final compatibility strategy between EmitterGroup and SignalGroup

  • the appropriate representation of interaction events

  • the semantics of event propagation and event acceptance

  • whether rich interaction events should use existing backend event objects or napari-defined event types

  • how backend integration layers should be structured

  • performance characteristics of high-frequency event dispatch

  • whether additional functionality is required from psygnal

These questions should be resolved incrementally as experience is gained during the migration.

Success criteria#

The migration will be considered successful when:

  • napari exposes a single, coherent application communication model with clearly defined semantics for both signals and events

  • state-change notifications use explicit, typed psygnal signals

  • interaction events have well-defined dispatch semantics independent of individual backend implementations

  • backend-specific communication remains encapsulated within napari-owned integration layers

  • Napari application logic no longer depends directly on backend communication APIs

  • event and signal interfaces are explicit, discoverable, and statically analysable

  • plugin authors have a documented migration path

  • the legacy napari.utils.events infrastructure is fully replaced through incremental changes.

  • threading expectations and backend responsibilities are clearly documented

  • adding a new rendering or GUI backend does not require introducing another application communication model.

References and Footnotes#

All NAPs should be declared as dedicated to the public domain with the CC0 license [1], as in Copyright, below, with attribution encouraged with CC0+BY [2].

Changelog#

  1. #1086 Introduces initial version of the document