Develop Custom Cards
This page shows how to add a card to Make Dataset using the current Operation/Params architecture, with the user interface kept separate from the card logic.
When adding a built-in card, use the repository’s make-dataset-card-dev skill for the full specification, implementation, documentation, and validation workflow. Use this page as a quick reference.
Architecture overview
Each card has three layers:
::
The UI layer contains no algorithms. It reads widgets, constructs the params object, and invokes the operation.
The Operation layer must not import
PySide6,qfluentwidgets, orMessageManager. Report errors by raising exceptions.
Step 1: Choose the operation type
Type |
Base class |
Method signature |
Use it for |
|---|---|---|---|
|
|
|
Per-structure transformations such as strain, perturbation, supercells, and doping |
|
|
|
Whole-dataset filtering or ordering, such as FPS |
|
|
|
Generation without an input structure, such as crystal prototypes |
Step 2: Define the Params dataclass
Define it in the appropriate module under src/NepTrainKit/core/cards/ (lattice.py, alloy.py, defect.py, magnetism.py, structure.py, or filter.py):
from dataclasses import dataclass
@dataclass(frozen=True)
class MyCardParams:
"""Parameters for my card."""
param_a: str = "default"
param_b: float = 1.0
use_seed: bool = False
seed: int = 0
Use a frozen dataclass.
Give every field a default value.
Use snake_case names that match the keys returned by the UI’s
get_params()method.
Step 3: Implement the operation
In the same module:
::
Rules:
Do not import UI libraries.
Raise an exception when parameter validation fails.
Use
append_config_tagto add a traceable label.Do not silently fall back to another behavior.
Step 4: Implement the UI card
Create a module under src/NepTrainKit/ui/views/_card/ and inherit from MakeDataCard:
::
Read legacy keys only when migrating an existing card. The template above is for a new card, so
from_dictreads onlyparams. When migrating a card already present in saved user workflows, theelsebranch may construct Params from legacy fields such asdata["old_key"]to preserve backward compatibility.
Key rules
Do not override
run(). The base class dispatches work to the correct thread from the type returned bycreate_operation().Do not put algorithms in the UI.
init_ui()constructs widgets,get_params()reads values, and the Operation owns all computation.Do not add a new
process_structure()implementation. If an existing compatibility method must remain, reduce it to one delegation line:return self.create_operation().run_structure(structure, self.get_params()).Use
SpinBoxUnitInputFramefor numeric values,ComboBoxfor enumerations,CheckBoxfor switches, andLineEditfor strings.
Step 5: Register, document, and test the card
Decorate the UI class with
@CardManager.register_card.Import the class in
src/NepTrainKit/ui/views/_card/__init__.pyand add it to__all__.Add
docs/source/module/make-dataset-cards/cards/my-card.md. Start its example from a diagnosed training-set problem, then explain how the card adds the missing structures. See the card documentation guide.Write operation tests that do not require Qt:
def test_my_card_operation(): op = MyCardOperation() result = op.run_structure(test_atoms, MyCardParams(param_b=2.0)) assert len(result) > 0
Validate
python skills/make-dataset-card-dev/scripts/run_card_checks.py --quick
python tools/docs/audit_card_docs.py
Install an external card
Built-in cards live in src/NepTrainKit/ui/views/_card/. Put an external custom card in the cards/ subdirectory of the user configuration directory; NepTrainKit discovers it at startup.