✦ Complete guide and API reference

CTkKanBan

Everything you need to build a database-backed Kanban workflow with CustomTkinter: installation, data modeling, forms, querying, CRUD integration, styling, callbacks, and the complete public API.

$ python -m pip install CTkKanBan
Drag and Drop Built-in Forms CustomTkinter Python 3.10+ Database Ready

Introduction

The package is built around one primary widget: CTkKanbanBoard. You give it an ordered set of columns, an optional initial set of cards, and an optional field schema. From there, the board handles rendering, selection, drag-and-drop movement, search, filters, sorting, inline field editing, built-in forms, and context menus.

Two design choices define the project:

  • The board owns defensive copies of your column and card dictionaries. It never asks you to mutate UI state in place.
  • Persistence is adapter-driven for database applications, with a legacy callback path for files and small local boards. The included SQLite adapter is transactional and operation-first.

What CTkKanBan gives you

A reusable board widget, default card rendering, live inline editing, built-in add/edit forms, per-card and per-column operations, and a large style surface.

What CTkKanBan leaves to you

Deployment, authentication, application-level rules beyond field validators, and any backend-specific authorization policy.

Good fit: desktop apps that already use CustomTkinter and need a flexible board widget without tying the UI to a database, ORM, or opinionated schema.

Installation

The package metadata in pyproject.toml declares:

  • requires-python = ">=3.10"
  • dependencies = ["customtkinter>=5.2.2,<7", "tzdata ..."]

1. Create a virtual environment

bash
python -m venv .venv

2. Activate it

bash
# Windows PowerShell
.venv\Scripts\Activate.ps1

# macOS / Linux
source .venv/bin/activate

3. Install the package

bash
python -m pip install CTkKanBan

Verify the interpreter and installed package before debugging GUI code:

bash
python -c "import CTkKanBan; print(CTkKanBan.__version__, CTkKanBan.__file__)"

4. Explore the source examples (optional)

The examples are included in the source repository and source distribution, not the installed wheel. From a source checkout, install the checkout and run:

bash
python -m pip install -e .
python example_all_features.py
python example_sqlite.py

The demo exercises custom fields, built-in forms, callbacks, styling, drag-and-drop, search, filtering, sorting, and a custom card renderer. example_sqlite.py is the runnable persistence example. Together they are the best source-level tour of intended application usage.

Release validation: CI installs the built wheel with its declared dependencies, runs pip check, and exercises the SQLite adapter before a release artifact can be attested or published.

Quick Start

A minimal board needs three things:

  1. An ordered list of columns.
  2. An optional list of cards.
  3. A CTk window that gives the board room to expand.
python
import customtkinter as ctk

from ctk_kanban import CTkKanbanBoard


columns = [
    {"id": "todo", "title": "To Do", "color": "#3B82F6"},
    {"id": "doing", "title": "Doing", "color": "#F59E0B"},
    {"id": "done", "title": "Done", "color": "#10B981"},
]

cards = [
    {"id": 1, "column": "todo", "title": "Write docs", "sort_order": 1},
    {"id": 2, "column": "doing", "title": "Review callbacks", "sort_order": 1},
]

app = ctk.CTk()
app.title("CTkKanBan Quick Start")
app.geometry("1100x700")
app.grid_rowconfigure(0, weight=1)
app.grid_columnconfigure(0, weight=1)

board = CTkKanbanBoard(app, columns=columns, cards=cards)
board.grid(row=0, column=0, sticky="nsew")

app.mainloop()
Key rule: every card must include id, column, and title. The value of card["column"] must exactly match an existing column["id"].
Mutation rule: after the widget is created, use methods such as add_card(), update_card(), move_card(), and set_state() instead of mutating the original input lists. The board stores deep copies.

Choose a Storage Path

Start with the smallest persistence option that matches the application. You can change paths later because the board UI talks to storage through stable operation and result contracts.

Prototype

In-memory board

Pass columns and cards directly. Best for UI work and local prototypes.

Build the first board →
Included adapter

SQLite

Use the transactional adapter for a local database with schema creation, revisions, and paging.

Use SQLite →
Most flexible

Your existing CRUD

Wrap four repository functions for SQL, NoSQL, an ORM, a cloud SDK, or an HTTP API.

Connect CRUD →
Advanced

Custom data source

Implement the full protocol for native queries, event feeds, conflict snapshots, or bulk writes.

Implement the protocol →
One writer only: configure either data_source or the legacy on_data_changed snapshot callback. The constructor rejects both together because two persistence writers can diverge.

Board Model

The board works with plain mappings rather than custom ORM or dataclass types. The typed definitions in ctk_kanban.models document the expected shapes, but at runtime the widget accepts dictionary-like mappings and normalizes them.

Columns

A column is an ordered board lane. The required keys are id and title. Column IDs must be hashable and unique. Optional keys let you control appearance and behavior per column:

color max_cards locked show_count show_add_button show_menu

Cards

A card must include id, column, and title. Any additional keys are preserved. The board uses sort_order only for manual ordering; all other values are application-defined card data.

Fields

Fields describe how card keys should behave in the default renderer, built-in form, search, filtering, and sorting. If you do not provide a field list, the package uses a default schema containing title, description, priority, assignee, due_date, and tags.

If you provide a custom field list and omit title, the validator inserts a default title field at the front because the board always requires it.

Data state vs. view state

get_data() returns only mutable board data: columns and cards. get_state() goes further and includes the current search query, active filters, sort settings, per-column sorts, and the selected card ID. Use set_data() when reloading from storage, and set_state() when you want to restore both data and the current board view.

Build Your First Board

The simplest practical workflow is to build a board from local data, then mutate it through the public API. This lets you prototype UI behavior before introducing persistence.

python
import customtkinter as ctk

from ctk_kanban import CTkKanbanBoard


app = ctk.CTk()
app.geometry("1200x760")
app.grid_rowconfigure(0, weight=1)
app.grid_columnconfigure(0, weight=1)

board = CTkKanbanBoard(
    app,
    columns=[
        {"id": "backlog", "title": "Backlog"},
        {"id": "todo", "title": "To Do"},
        {"id": "done", "title": "Done"},
    ],
    cards=[
        {"id": 1, "column": "backlog", "title": "Audit dark theme", "sort_order": 1},
        {"id": 2, "column": "todo", "title": "Write tutorial", "sort_order": 1},
    ],
)
board.grid(row=0, column=0, sticky="nsew")

# Programmatic updates
board.add_card({"id": 3, "column": "todo", "title": "Review API docs"})
board.move_card(1, "todo", 0)
board.update_card(2, {"title": "Write getting started guide"})

app.mainloop()

A few behavior details are worth knowing immediately:

  • add_card() appends a new card to the end of its column when sort_order is omitted.
  • move_card() and reorder_card() rewrite manual sort_order values so the column remains sequential.
  • get_cards_by_column() returns the current display order, not the raw insertion order of the internal card mapping.

Inline Editing and Built-in Card Forms

The built-in form system is driven entirely by field definitions. If enable_builtin_card_form=True, the board can open the add form automatically from the toolbar and per-column add buttons. Existing cards use inline field editing by default.

Click any value in the default card renderer to edit it in place. Empty editable show_on_card fields receive an add placeholder. Enter, focus loss, or the check button saves; Escape cancels. Text areas use Ctrl+Enter to save. Set enable_inline_card_editing=False to retain the earlier full-card edit flow.

python
fields = [
    {
        "key": "title",
        "label": "Title",
        "type": "text",
        "required": True,
        "show_on_card": True,
        "show_in_form": True,
        "searchable": True,
        "sortable": True,
    },
    {
        "key": "priority",
        "label": "Priority",
        "type": "select",
        "options": ["Low", "Medium", "High"],
        "default": "Medium",
        "show_on_card": True,
        "show_in_form": True,
        "filterable": True,
        "sortable": True,
    },
    {
        "key": "description",
        "label": "Description",
        "type": "textarea",
        "show_on_card": True,
        "show_in_form": True,
        "searchable": True,
    },
]

board = CTkKanbanBoard(
    app,
    columns=[{"id": "todo", "title": "To Do"}],
    cards=[],
    fields=fields,
    card_form_mode="sidepanel",  # or "popup"
    enable_inline_card_editing=True,
    enable_builtin_card_form=True,
)

board.open_add_card_form("todo")

card_form_mode="popup"

Uses CardFormDialog, a modal CTkToplevel wrapper around the form.

card_form_mode="sidepanel"

Embeds the same form content in a panel beside the board instead of opening a top-level window.

External form flow: if enable_builtin_card_form=False, the board does not create its own form. Instead it emits on_add_card_requested and on_edit_card_requested so your application can open its own editor UI.
Field controls: the built-in form and inline editor give dedicated widgets to textarea, checkbox, select, and date. Other field types, including datetime, tag, badge without options, and multiselect, use compact text entry controls. For tags and multiselect, comma-separated input is normalized to list[str].

Search, Filter, and Sort

Search, filter, and sort behavior is field-aware. The board only exposes fields in the built-in UI if the field definitions opt into that behavior.

python
fields = [
    {"key": "title", "label": "Title", "type": "text", "required": True, "searchable": True, "sortable": True},
    {"key": "assignee", "label": "Assignee", "type": "select", "options": ["Maya", "Owen"], "filterable": True, "searchable": True},
    {"key": "priority", "label": "Priority", "type": "select", "options": ["Low", "Medium", "High"], "filterable": True, "sortable": True},
    {"key": "due_date", "label": "Due date", "type": "date", "filterable": True, "sortable": True},
]

board = CTkKanbanBoard(
    app,
    columns=columns,
    cards=cards,
    fields=fields,
    filter_mode="dim",  # or "hide"
)

board.search("Maya")
board.apply_filters({"priority": "High", "overdue_only": True})
board.sort_cards("due_date")
board.sort_cards("manual", reverse=True, column_id="review")

Search rules

  • The query is case-insensitive.
  • The board searches only fields marked searchable=True.
  • If no fields are marked searchable, the board falls back to searching title and description.

Filter rules

  • The built-in filter dialog generates exact-match filters from fields marked filterable=True.
  • apply_filters() also accepts list/set filters and callable predicates.
  • filter_mode="hide" removes non-matching cards from view.
  • filter_mode="dim" keeps non-matching cards visible but visually de-emphasized.

Sort rules

  • Allowed keys are manual, priority, due_date, created_date, updated_date, title, and any field marked sortable=True.
  • manual uses sort_order.
  • priority has a fixed ranking of Critical, High, Medium, then Low.
  • Per-column sorting is available through sort_cards(..., column_id=...) when allow_column_sorting=True.

Database and Persistence

Database integration is operation-first. The board updates optimistically, sends a focused mutation to one background persistence worker, then merges canonical IDs, timestamps, versions, or defaults returned by storage. Choose one of the following integration levels.

Option Use it when You implement
SQLiteKanbanDataSource You need a complete local database with no external driver. Only application setup and initial seed data.
CRUDKanbanDataSource You already have repository, ORM, collection, SDK, or HTTP CRUD functions. Read, create, update, and delete callbacks.
KanbanDataSource You need native database queries, bulk writes, event history, or change streams. The complete five-method protocol.
on_data_changed You have a small file or legacy full-snapshot save function. One synchronous snapshot callback.

SQLite: complete local persistence

The included adapter owns its schema and uses a fresh thread-safe connection for each operation. It provides transactions, atomic batches, optimistic revisions, idempotent event replay, generated card IDs, timestamps, server-side adapter querying, paging, and change polling.

python
import customtkinter as ctk

from ctk_kanban import CTkKanbanBoard, SQLiteKanbanDataSource


app = ctk.CTk()
source = SQLiteKanbanDataSource("kanban.db")
source.seed_board(
    "work",
    [
        {"id": "todo", "title": "To Do"},
        {"id": "done", "title": "Done"},
    ],
    cards=[],
    replace=False,
)

board = CTkKanbanBoard(
    app,
    data_source=source,
    board_id="work",
    actor_id="desktop-user-17",
    auto_load=True,
    server_side_query=True,
    page_size=100,
    poll_interval_ms=2000,
    completed_columns=["done"],
)
board.pack(fill="both", expand=True)
app.mainloop()
Seeding behavior: replace=False creates missing records without clearing an existing board. Use replace=True for an intentional reset; it clears cards, columns, event history, and the board revision.

Bring your own CRUD

CRUDKanbanDataSource is database-neutral: it does not guess a connection string, driver, schema, query language, or authentication model. Your application keeps those decisions and supplies four normal repository callbacks. This works for PostgreSQL, MySQL, SQL Server, Oracle, MongoDB, DynamoDB, Redis, graph databases, cloud SDKs, ORMs, and remote APIs.

python
from contextlib import contextmanager

from ctk_kanban import CRUDKanbanDataSource


def read_board(board_id):
    return {
        "columns": repository.list_columns(board_id),
        "cards": repository.list_cards(board_id),
        "revision": repository.get_revision(board_id),  # Optional
    }


def create(resource, board_id, record, context):
    # resource is exactly "card" or "column".
    return repository.insert(
        resource,
        board_id,
        record,
        position=context.position,
        transaction=context.transaction,
        event_id=context.metadata.event_id,
    )


def update(resource, board_id, record_id, record, context):
    return repository.update(
        resource,
        board_id,
        record_id,
        record,
        position=context.position,
        transaction=context.transaction,
        expected_revision=context.metadata.expected_revision,
    )


def delete(resource, board_id, record_id, context):
    repository.delete(
        resource,
        board_id,
        record_id,
        transaction=context.transaction,
    )


@contextmanager
def transaction(events):
    with repository.begin() as session:
        yield session


source = CRUDKanbanDataSource(
    read=read_board,
    create=create,
    update=update,
    delete=delete,
    transaction=transaction,  # Optional but recommended for atomic batches
)

The bridge translates board operations into CRUD calls:

  • Card creates, updates, and deletes map directly to the corresponding callback.
  • Card moves and reorders become focused card updates.
  • Column order is supplied as context.position.
  • Column ID changes also update affected card records.
  • Generated IDs are remapped when create returns the canonical stored record.
  • Search, filters, sorting, paging, and totals run over the result of read_board.
  • A batch shares the object yielded by transaction(events).

CRUD callback return values

Return Meaning
Record mapping Accepted. The returned database values become the canonical card or column.
None or True Accepted using the record submitted to the callback.
False Rejected. The board restores the optimistic UI change.
MutationResult Advanced response with conflicts, revisions, ID maps, changed records, or retryability.
Connection rule: CRUD callbacks run on the persistence worker, not Tk's UI thread. Do not share a thread-bound cursor or session created on the main thread. Open a connection per call or yield a worker-owned session from transaction(events).

Implement the full data-source protocol

Use a direct adapter when reading every record into the CRUD bridge would be inefficient or when the backend should own concurrency, queries, event history, and batching.

python
from ctk_kanban import (
    BoardLoadResult,
    CardPage,
    CardQuery,
    ChangePage,
    MutationEvent,
    MutationResult,
)


class MyDataSource:
    def load_board(
        self, board_id: str, query: CardQuery | None = None
    ) -> BoardLoadResult:
        ...

    def apply_mutation(self, event: MutationEvent) -> MutationResult:
        ...

    def apply_batch(self, events: list[MutationEvent]) -> MutationResult:
        ...

    def query_cards(self, board_id: str, query: CardQuery) -> CardPage:
        ...

    def get_changes(
        self, board_id: str, since_revision: int | str | None
    ) -> ChangePage:
        ...

Adapter methods are intentionally blocking and thread-safe; PersistenceCoordinator owns the worker. Treat event_id as an idempotency key, check expected_revision in the same transaction as the write, and make apply_batch() all-or-nothing.

Mutation lifecycle and canonical records

  1. Optimistic UI: the board validates and applies the local operation immediately.
  2. Focused event: only affected records and trace metadata are submitted.
  3. Background write: one worker preserves mutation order away from Tk.
  4. Canonical merge: returned IDs, timestamps, versions, defaults, and affected records replace local values.
  5. Failure recovery: rejection restores the prior board state; transient failures retry and may queue offline.

Retries, offline queue, and status

RetryPolicy defaults to three attempts with bounded exponential backoff. Connection, timeout, and operating-system failures move the coordinator offline after retries. Pending operations remain visible and queue in memory in FIFO order.

A custom adapter should raise ConnectionError, TimeoutError, or OSError for retryable connectivity failures. Domain rejections should return MutationResult(accepted=False, reason=...) instead.

idle loading saving saved offline retrying conflict error

Observe transitions with on_persistence_status or get_persistence_status(). Call set_online(True) after connectivity returns, or call retry_last_save() after resolving a rejected write.

Queue durability: the built-in offline queue is process-local. It is lost if the application exits. Use a durable outbox in your repository or adapter when crash recovery is required.

Optimistic conflicts

Storage can return ConflictDetails when expected_revision is stale. Configure conflict_strategy as:

  • "server_wins": replace local data with the server snapshot.
  • "local_wins": retry the local operation against the current server revision.
  • "callback": invoke on_conflict so the application decides.

Loading, querying, paging, and polling

  • auto_load=True loads board_id after the widget is constructed.
  • server_side_query=True delegates toolbar search, filters, sorting, and paging.
  • page_size controls the requested page size; load_next_page() appends more.
  • poll_interval_ms > 0 calls get_changes() and reloads after remote events.
  • For push systems, call refresh_from_source() from the notification handler.

Legacy snapshot callback

on_data_changed remains useful for JSON files and small local snapshots. It runs synchronously and receives all columns and cards plus the focused action event.

python
def save_snapshot(event):
    write_json({"columns": event["columns"], "cards": event["cards"]})
    return True


board = CTkKanbanBoard(
    app,
    columns=columns,
    cards=cards,
    on_data_changed=save_snapshot,
)

Return False or {"cancel": True, "reason": "..."} to reject and restore an action. Use on_action_cancelled to display or log the reason.

Reload rule: set_data(), set_cards(), set_columns(), and set_state() replace local state without persistence callbacks. They are intended for application-driven reloads. With a data source, prefer refresh_from_source().

Production checklist

  • Keep credentials and authorization in the application database layer.
  • Use stable string or integer IDs across JSON and database boundaries.
  • Return generated IDs, defaults, UTC timestamps, and record versions from writes.
  • Make event IDs idempotent and enforce expected revisions transactionally.
  • Provide a transaction callback for multi-record CRUD operations.
  • Test disconnect, retry, conflict, duplicate submission, and remote-change paths.
  • Call destroy() when disposing the board so pending Tk callbacks and the worker close.

The standalone database integration guide contains the same persistence model in a compact format for teams that want a database-only handout.

Custom Rendering and Theme

The default renderer uses a modern adaptive light/dark system: layered board surfaces, colored column rails, priority-accented cards, compact metadata tiles, filter chips, persistence status pills, and matching form controls. These defaults work without a custom style and remain fully configurable.

The default card renderer is field-driven, but you can fully replace card content with a custom renderer while still using the built-in search, form, filter, and callback infrastructure.

python
import customtkinter as ctk


def render_card(card_frame, card_data, fields, theme):
    card_frame.grid_columnconfigure(0, weight=1)

    ctk.CTkLabel(
        card_frame,
        text=card_data["title"],
        anchor="w",
        font=ctk.CTkFont(size=14, weight="bold"),
        text_color=theme["text_color"],
    ).grid(row=0, column=0, sticky="ew", padx=10, pady=(10, 4))

    if card_data.get("description"):
        ctk.CTkLabel(
            card_frame,
            text=str(card_data["description"]),
            anchor="w",
            justify="left",
            wraplength=230,
            text_color=theme["muted_text_color"],
        ).grid(row=1, column=0, sticky="ew", padx=10, pady=(0, 10))


style = {
    "board_fg_color": ("#EEF2F7", "#09111F"),
    "column_fg_color": ("#E2E8F0", "#152033"),
    "card_fg_color": ("#FFFFFF", "#101827"),
    "card_hover_color": ("#F8FAFC", "#17233A"),
    "card_selected_color": ("#DBEAFE", "#1E3A5F"),
    "drop_indicator_color": "#38BDF8",
}

board = CTkKanbanBoard(
    app,
    columns=columns,
    cards=cards,
    fields=fields,
    style=style,
    priority_colors={"Critical": "#B91C1C"},
    tag_colors={"API": "#1D4ED8"},
    card_renderer=render_card,
)

The renderer signature is:

CardRenderer = Callable[[Any, dict[str, Any], list[dict[str, Any]], dict[str, Any]], None]

The first argument is the card frame that you populate. The board does not add default content when a custom renderer is supplied.

Important: a custom renderer changes only the card visuals. Built-in forms, search, filtering, sorting, and validation still use the board's field definitions and card dictionaries.

Drag and Reorder

The board supports both card dragging and column dragging. These features are enabled by default and are controlled by constructor flags rather than a separate drag manager.

  • enable_card_drag toggles card movement.
  • enable_card_reorder allows reordering inside a column.
  • enable_column_drag allows dragging column headers.
  • enable_drag_preview shows a drag preview window during movement.
  • show_drop_indicator displays insertion markers.
  • enable_horizontal_autoscroll and enable_vertical_autoscroll keep drag targets reachable near the edges.

Two column-level rules affect dragging:

  • A locked column cannot receive moved cards.
  • If enforce_column_limits=True, a column with max_cards set refuses additional cards once the limit is reached.

Selection, Menus, and Forms

CTkKanBan keeps a single selected card ID. Programmatic selection is exposed through select_card(), clear_selection(), and get_selected_card().

The built-in card context menu includes:

  • Open: starts inline title editing by default.
  • Edit: starts inline title editing directly.
  • Delete: optionally confirms, then deletes.
  • Duplicate: copies the card and appends (copy) to the title.
  • Move to: lists eligible target columns.

You can append your own actions with card_context_menu_items. Each custom menu item can be always enabled, always disabled, or conditionally enabled with a callable.

State Snapshots and Bulk Loading

The board exposes a small but useful state API for serialization, persistence, and undo-like workflows.

Method What it captures or restores
get_data() Columns and cards only.
set_data(data) Replaces columns and cards without touching persistence callbacks.
get_state() Columns, cards, search query, filters, sort, per-column sorts, and selected card.
set_state(state) Restores a snapshot previously returned by get_state().
set_cards(cards) Replaces only the card collection.
set_columns(columns) Replaces only the column collection after validating that existing cards still fit.

This split is deliberate. Use get_data() for persistence formats that should not include UI search/filter state. Use get_state() for restoring the board as the user left it.

Undo and redo

End-user mutations record defensive before/after snapshots up to undo_limit. undo() and redo() restore those snapshots; with a data source attached, the board computes focused delta events so partially loaded server data is not overwritten.

Atomic application batches

Use apply_batch() for imports and multi-step edits. Supported operation names are add_card, update_card, delete_card, move_card, add_column, update_column, delete_column, and move_column.

python
saved = board.apply_batch(
    [
        {
            "operation": "update_card",
            "card_id": 10,
            "new_data": {"priority": "High"},
        },
        {
            "operation": "move_card",
            "card_id": 10,
            "target_column": "review",
        },
        {
            "operation": "add_card",
            "card_data": {
                "column": "todo",
                "title": "Follow up",
            },
        },
    ],
    source="bulk_edit",
)
Atomicity: the UI rolls back the whole batch on rejection. Durable atomicity requires apply_batch() in a full adapter or a transaction callback in the CRUD bridge.

Styling Surface

The board has a broad, flat style dictionary. Colors can be passed directly through theme or style, and the constructor merges nested priority_colors, tag_colors, and font_config overrides from the style mapping.

Visual details such as card_metadata_fg_color, card_accent_width, column_control_fg_color, column_count_full_fg_color, filter_chip_fg_color, and the success, warning, and danger surface colors can be changed independently. Existing broad keys such as card_fg_color and button_fg_color continue to work.

In practice:

  • style overrides theme when both set the same style key.
  • A font_config entry such as {"toolbar": font} becomes toolbar_font internally.
  • get_style() returns the full merged style snapshot.
Performance note: the default incremental rendering path reuses existing widgets for most add, update, move, delete, filter, and sort operations. The board also batches drag motion work and deferred widget cleanup through drag_update_interval_ms and cleanup_time_budget_ms.

Constructor

Primary entry point:

CTkKanbanBoard(master, columns=None, cards=None, fields=None, *, ...)

The tables below cover the complete constructor surface for this release. Run help(CTkKanbanBoard) against your installed version when you need its exact live signature.

Common core data and layout parameters

Parameter Type / default Meaning
master Any parent widget Parent passed to the underlying CTkFrame.
columns None Ordered column definitions. Each column needs id and title.
cards None Initial card definitions. Cards must reference existing column IDs.
fields None Field schema for rendering, forms, search, filter, and sort. Defaults are used when omitted.
enable_horizontal_scroll True Wraps the board area in a horizontal CTkScrollableFrame.
enable_column_scroll True Wraps each column body in a vertical CTkScrollableFrame.
column_width 300 Column width in pixels. Validation requires at least 160.
column_height 600 Column height in pixels. Validation requires at least 180.
column_gap 12 Horizontal spacing between columns. Must be non-negative.
board_padding None Outer padding. When omitted, the theme's board_padding value is used.

Toolbar parameters

Parameter Default Meaning
show_toolbar True Creates the built-in toolbar row above the board.
show_search True Shows the search box when enable_search=True.
show_filter_button True Shows the filter button when enable_filters=True.
show_sort_button True Shows the sort button when enable_sorting=True.
show_add_card_button True Shows the toolbar-level add-card button.
show_clear_filters_button True Shows the toolbar clear button.

Column, card, and drag parameters

Parameter Default Meaning
show_card_count True Shows per-column card counts unless a column overrides with show_count.
show_column_add_button True Shows per-column add buttons unless a column overrides with show_add_button.
show_column_menu True Shows each column's menu button unless a column overrides with show_menu.
enforce_column_limits True Honors max_cards limits during add and move operations.
enable_column_drag True Allows dragging columns by their headers.
card_mode "detailed" "compact" truncates visible content; "detailed" shows more fields.
enable_card_hover True Applies hover colors to cards.
enable_card_selection True Selects cards on click and updates selection visuals.
enable_card_context_menu True Shows the built-in right-click menu.
enable_card_double_click True Double-click starts inline title editing unless you override it with a callback.
enable_inline_card_editing True Lets users click default-rendered card fields and edit them inside the card.
enable_builtin_card_form True Controls whether the board creates its own add/edit forms.
card_form_mode "popup" Switches between a modal dialog and a side panel.
confirm_delete True Asks for confirmation before request_delete_card() deletes a card.
enable_card_drag True Allows dragging cards.
enable_card_reorder True Allows reordering within a column.
enable_drag_preview True Shows a preview window while dragging cards.
drag_preview_opacity 1.0 Clamped to the supported range 0.1 to 1.0.
show_drop_indicator True Displays an insertion marker during drag operations.
enable_horizontal_autoscroll True Autoscrolls the board horizontally during card drags.
enable_vertical_autoscroll True Autoscrolls individual columns vertically during card drags.
incremental_card_rendering True Reuses widgets for most card-level mutations instead of forcing a full refresh.
incremental_column_rendering True Reuses widgets for many column-level mutations.
drag_update_interval_ms 16 Coalesces high-frequency drag motion updates.
autoscroll_interval_ms 45 Minimum interval between autoscroll updates.
cleanup_time_budget_ms 8 Time budget for deferred widget cleanup batches. Must be at least 1.

Search, filter, sort, and extension parameters

Parameter Default Meaning
enable_search True Enables search APIs and toolbar search UI.
enable_filters True Enables filter APIs and built-in filter dialog.
enable_sorting True Enables sorting APIs and built-in sort menus.
allow_column_sorting True Allows sort_cards(..., column_id=...) and the "Sort column..." menu entry.
default_sort "manual" Initial global sort key. Must be an allowed sort key.
filter_mode "hide" Either remove or dim cards that do not match the current view.
show_no_results True Displays an empty-result message in columns with no visible cards.
theme None Style override mapping merged into the default theme.
style None Alias for theme-style overrides. Wins over theme on overlapping keys.
priority_colors None Per-priority chip color overrides merged over DEFAULT_PRIORITY_COLORS.
tag_colors None Per-tag chip color overrides.
font_config None Mapping of font slots such as toolbar or card_title to font objects.
card_renderer None Custom card rendering callback. Replaces the default card body builder.
card_context_menu_items None Extra context-menu actions appended after the built-in items.

Responsive layout and card presentation

Parameter Default Meaning
responsive_columns True Lets columns adapt between the configured minimum and maximum widths.
min_column_width 240 Smallest responsive column width; must be at least 160.
max_column_width 420 Largest responsive column width; must not be below the minimum.
column_control_size 30 Pixel size used by compact column controls; minimum 28.
card_density "comfortable" Spacing preset: "compact", "comfortable", or "spacious".
show_drag_handles True Shows the visual drag affordance on cards.
max_visible_tags 6 Maximum tags rendered on a card before remaining tags are summarized.
tags_per_row 3 Maximum tag chips placed on each rendered row.
confirm_discard_changes True Prompts before replacing or closing a dirty built-in form.
highlight_search_matches True Highlights matching text in visible card content.

Data source, reliability, IDs, history, and localization

Parameter Default Meaning
data_source None SQLite, CRUD, or custom KanbanDataSource used for durable state.
board_id "default" Durable board namespace passed to every data-source operation.
actor_id None User, process, or device identity included with mutation metadata.
auto_load False Loads the board from data_source after construction.
server_side_query False Delegates search, filters, sorting, and paging to the data source.
page_size 100 Requested card page size; must be at least 1.
poll_interval_ms 0 Change polling interval. Zero disables polling.
retry_policy None Optional RetryPolicy; defaults are used when omitted.
disable_while_saving True Rejects overlapping user mutations while a write is pending.
id_factory None Callable that creates local card IDs; UUID strings are used by default.
use_temporary_ids True Marks new IDs as temporary so storage may replace them canonically.
immutable_card_ids True Prevents ordinary updates from changing a card ID.
immutable_column_ids True Prevents ordinary updates from changing a column ID.
conflict_strategy "server_wins" "server_wins", "local_wins", or "callback".
undo_limit 50 Maximum in-memory undo entries. Zero disables history.
completion_field "completed" Boolean card key used by overdue and completion behavior.
completed_columns None Column IDs whose cards count as completed for overdue behavior.
timezone_name "UTC" IANA timezone used for date comparison and display.
locale_name None Optional locale name used when formatting stored timestamps.
normalize_empty_values True Normalizes empty optional form values using each field's empty-value rule.
logger None Optional logger for persistence errors and operational diagnostics.

Callback parameters

Callbacks receive an event mapping. Mutation callbacks may cancel before persistence; status and conflict callbacks observe the asynchronous data-source lifecycle.

Parameter Default When it runs
on_card_clickedNoneAfter a card click and selection update.
on_card_double_clickedNoneAfter a card double-click.
on_card_right_clickedNoneWhen opening the card context flow.
on_card_movedNoneBefore committing a cross-column move.
on_card_reorderedNoneBefore committing same-column order changes.
on_card_createdNoneBefore a created card is persisted.
on_card_updatedNoneBefore a card update is persisted.
on_card_deletedNoneBefore a card deletion is persisted.
on_column_createdNoneBefore a column creation is persisted.
on_column_updatedNoneBefore a column update is persisted.
on_column_deletedNoneBefore a column deletion is persisted.
on_column_reorderedNoneBefore column order is committed.
on_data_changedNoneLegacy full-snapshot persistence path.
on_filter_changedNoneBefore active filters change.
on_search_changedNoneBefore the search query changes.
on_sort_changedNoneBefore global or column sort changes.
on_action_cancelledNoneAfter an optimistic action is restored.
on_errorNoneWhen callback, load, query, or persistence work fails.
on_add_card_requestedNoneOverrides the built-in add-card form request.
on_edit_card_requestedNoneOverrides the built-in edit-card form request.
on_persistence_statusNoneOn loading, saving, saved, retrying, offline, conflict, or error states.
on_conflictNoneWhen storage reports a conflict; may return "server_wins" or "local_wins".

Board Methods

Mutation methods

Method Returns Notes
add_card(card_data, *, source="api") Card copy or None Validates, assigns a new trailing sort_order when needed, and can be cancelled.
update_card(card_id, new_data, *, source="api") Card copy or None Merges new data into the current card, revalidates it, and can rename the card ID.
delete_card(card_id, *, source="api") bool Deletes and reindexes manual order in the source column. Returns False on cancellation.
duplicate_card(card_id, *, source="api") Card copy or None Copies the card, generates a new ID, appends (copy) to the title, and adds it.
move_card(card_id, target_column, target_index=None, *, source="api") bool Moves or reorders a card and rewrites manual sort_order values.
reorder_card(card_id, target_index, *, source="api") bool Convenience wrapper for same-column reordering.
add_column(column_data, *, source="api") Column copy or None Appends a column to the end of the board order and can be cancelled.
update_column(column_id, new_data, *, source="api") Column copy or None Updates metadata and can atomically rename the column ID across existing cards.
delete_column(column_id, *, source="api") bool Deletes only empty columns. Raises if any card still belongs to the column.
move_column(column_id, target_index, *, source="api") bool Moves a column in the ordered column list.
apply_batch(operations, *, source="batch") bool Applies supported card and column operations as one UI change and one adapter batch.

Query and bulk-state methods

Method Returns Notes
get_card(card_id) Card copy or None Raises if the ID is unhashable, but returns None for a missing hashable ID.
get_all_cards() list[dict[str, Any]] Returns the owned insertion order of the internal card mapping.
get_cards_by_column(column_id) list[dict[str, Any]] Returns the current display order for that column, including active sorting.
get_column(column_id) Column copy or None Returns one column by ID.
get_columns() list[dict[str, Any]] Returns the current board column order.
get_style() dict[str, Any] Returns the merged theme plus priority_colors, tag_colors, and font_config.
get_data() BoardData Returns only columns and cards.
load_data(data) None Alias for set_data().
set_data(data) None Replaces columns and cards while preserving current view state unless you overwrite it.
load_cards(cards) None Alias for set_cards().
set_cards(cards) None Replaces the complete card collection without firing persistence callbacks.
set_columns(columns) None Replaces columns after checking that existing cards do not reference removed columns.
clear_board() None Removes all cards while preserving columns and clearing selection.
get_state() dict[str, Any] Returns data plus search, filters, global sort, per-column sorts, and selected card ID.
set_state(state) None Restores a previously captured board state snapshot.

History, data-source, sizing, and lifecycle methods

Method Returns Notes
can_undo() bool Reports whether an undo snapshot is available.
undo() bool Restores the previous state and persists the focused delta when a data source is attached.
can_redo() bool Reports whether a redo snapshot is available.
redo() bool Reapplies the latest undone state and persists its focused delta.
refresh_from_source() bool Starts an asynchronous board load; returns False without a data source.
query_source(query, *, append=False) bool Runs a CardQuery asynchronously and replaces or appends the visible page.
load_next_page(column_id=None) bool Requests the next page globally or for one column using current query state.
get_persistence_status() dict[str, Any] Returns state, message, board revision, and queued mutation count.
retry_last_save() bool Retries the last rejected or failed pending write.
set_online(online) None Marks persistence online/offline; going online starts FIFO queue replay.
remap_card_id(old_id, new_id, *, persist=False) Card copy Replaces a temporary card ID and updates selection/widgets without duplication.
set_column_width(width, column_id=None) None Sets the shared width or one adjustable column width within configured bounds.
destroy() None Cancels polling/cleanup callbacks, closes forms, and shuts down the persistence worker.

View and UI methods

Method Returns Notes
refresh() None Fully rebuilds the visible board from the owned data and current view state.
search(query) bool Updates the current query and can be cancelled by on_search_changed.
clear_search() None Convenience wrapper that resets the query to an empty string.
apply_filters(filters) bool Replaces the active filter mapping and can be cancelled by on_filter_changed.
clear_filters() None Equivalent to apply_filters({}).
sort_cards(sort_key, reverse=False, column_id=None) bool Applies a global or per-column display sort without mutating card content except manual move order APIs.
select_card(card_id) Card copy Updates the single-card selection state and card visuals.
clear_selection() None Clears the current selection.
get_selected_card() Card copy or None Returns the selected card if one is currently selected.
start_inline_card_edit(card_id, field_key="title") bool Starts editing one visible writable field inside a default-rendered card.
open_add_card_form(column_id=None) None Opens the add flow for a column or the first unlocked column when omitted.
open_edit_card_form(card_id) None Explicitly opens the configured full-card popup or side-panel form.
request_delete_card(card_id) bool Runs the configured confirmation flow, then deletes through the normal delete API.

Persistence API

These public exports form the database-neutral boundary. Application code usually constructs one data source and passes it to CTkKanbanBoard; adapter authors use the typed contracts directly.

Data-source classes

Export Purpose
KanbanDataSource Runtime-checkable five-method protocol accepted by the board.
SQLiteKanbanDataSource Included transactional SQLite implementation.
CRUDKanbanDataSource Four-callback bridge for an application-owned repository.
PersistenceCoordinator Single-worker ordering, retries, offline replay, loads, queries, and callback scheduling.
RetryPolicy Retry configuration: attempts, initial delay, multiplier, and maximum delay.
CRUDResource Type alias for the literal resource names "card" and "column".
CRUDWriteResult Type alias for record mappings, MutationResult, booleans, or None.

CRUDKanbanDataSource

CRUDKanbanDataSource(*, read, create, update, delete, transaction=None, changes=None)
Callback Signature Responsibility
read (board_id) Return BoardLoadResult or a mapping with columns, cards, and optional revision.
create (resource, board_id, record, context) Insert a card or column and preferably return its canonical stored record.
update (resource, board_id, record_id, record, context) Replace/update one stored record. The ID is the previous ID during a rename.
delete (resource, board_id, record_id, context) Delete one stored card or column.
transaction (events) -> context manager Optional atomic scope. The yielded value becomes context.transaction.
changes (board_id, since_revision) Optional optimized change feed returning ChangePage or an equivalent mapping.

CRUDContext

Attribute Meaning
eventThe complete focused MutationEvent.
metadataConvenience property exposing event.metadata.
transactionObject yielded by the transaction context manager, or None.
positionOrdered column position for column create/reorder operations.
previous_idStored identifier targeted by update or delete.

SQLiteKanbanDataSource

SQLiteKanbanDataSource(path)
Method Purpose
seed_board(board_id, columns, cards, *, replace=False)Creates/imports an initial board.
load_board(board_id, query=None)Loads columns and a card page.
query_cards(board_id, query)Searches, filters, sorts, and pages cards.
apply_mutation(event)Applies one event transactionally and idempotently.
apply_batch(events)Applies one-board events atomically with temporary-ID rebasing.
get_changes(board_id, since_revision)Returns persisted events after a revision.

Mutation contracts

Contract Important fields
EventMetadata board_id, actor_id, event_id, transaction_id, expected_revision, source, timestamp.
MutationEvent type, focused payload, and metadata. to_dict() and from_mapping() cross serialization boundaries.
MutationResult accepted, reason, canonical card/column, changed records, id_map, board_revision, conflict, and retryable.
ConflictDetails expected_revision, actual_revision, optional server_data, and a user-facing message.
PersistenceState Literal status values used by persistence status callbacks.

Query and page contracts

Contract Fields
CardQuery column_id, search, filters, sort_key, reverse, offset, limit, completion settings, and timezone.
CardPage cards, total, offset, limit, column_totals, board_revision, and derived has_more.
BoardLoadResult columns, initial cards, column_totals, board_revision, and has_more.
ChangePage Mutation events after a revision and the latest board_revision.

RetryPolicy defaults

FieldDefaultMeaning
attempts3Total attempts including the first call.
initial_delay0.25Seconds before the first retry.
multiplier2.0Delay multiplier after each failure.
max_delay3.0Maximum delay between attempts.

Data Types

ColumnDefinition

Key Required Meaning
id Yes Hashable, non-empty identifier used by cards and public APIs.
title Yes Display name shown in the column header.
color No Header accent color for the column's vertical color bar.
max_cards No Non-negative card limit. Enforced only when enforce_column_limits=True.
locked No Blocks card additions and moves into the column.
show_count No Per-column override for the count badge.
show_add_button No Per-column override for the header add button.
show_menu No Per-column override for the header menu button.

CardDefinition

Key Required Meaning
id Yes Hashable, non-empty card identifier.
column Yes The column ID that currently owns the card.
title Yes Card title. Cannot be empty.
sort_order No Numeric manual order value used by the manual sort mode.
Any other key No Preserved as card data for rendering, searching, filtering, sorting, and callbacks.

FieldDefinition

Key Meaning
key Unique field name stored in card dictionaries.
label User-facing label used on cards, forms, and dialogs.
type One of text, textarea, number, select, multiselect, date, datetime, checkbox, tag, tags, badge, or hidden.
required Marks the field as required during validation. title is always required.
default Default value used by the built-in add form.
placeholder Entry placeholder text for the built-in form.
options Allowed options for select and multiselect.
show_on_card Includes the field in the default card renderer and makes it an inline-edit target.
show_in_form Includes the field in the built-in form.
searchable Includes the field in board search.
filterable Includes the field in the built-in filter dialog.
sortable Allows the field key to be used as a sort key.
read_only Disables editing in both the inline editor and built-in form while preserving the value.
validator Custom callable (value, card_copy) -> bool | str | None.

Built-in field behavior by type

Field type Built-in form control Default card rendering behavior
text Single-line entry Label: value text.
textarea Textbox Wrapped body text.
number Single-line entry, converted to int or float Label: value text.
select Option menu Label: value text unless the field key is priority.
multiselect Comma-separated entry parsed to list[str] Label: value text.
date / datetime Single-line entry Label: value text.
checkbox Checkbox Yes / No text.
tag, tags, badge Single-line entry, except tags parses comma-separated values Rendered as chips.
hidden Not shown by default Not shown by default.

ContextMenuItem and related aliases

Type Definition
ContextMenuItem Requires label and callback. Optional enabled accepts a boolean or callable, and separator_before inserts a menu separator.
BoardData {"columns": [...], "cards": [...]}
KanbanEvent dict[str, Any]
KanbanCallback Callable[[KanbanEvent], Any]
CardRenderer Callable[[Any, dict[str, Any], list[dict[str, Any]], dict[str, Any]], None]

Callback Reference

Every event dictionary created by the board includes type, timestamp, and source. The rest of the payload depends on the callback.

Cancellation contract

Cancellable callbacks may reject an operation by returning False or a mapping with {"cancel": True}. Non-cancellable callbacks are purely observational.

Callback Event type Cancellable Key payload fields
on_card_clicked card_clicked No card_id, card_data, x_root, y_root
on_card_double_clicked card_double_clicked No card_id, card_data, pointer coordinates when triggered by mouse
on_card_right_clicked card_right_clicked No card_id, card_data, pointer coordinates
on_card_created card_created Yes card_id, card_data, column_data
on_card_updated card_updated Yes card_id, old_card_id, card_data, old_card_data, changed_fields, changed_cards, column_data
on_card_deleted card_deleted Yes card_id, card_data, old_card_data, changed_cards
on_card_moved card_moved Yes card_id, old_column, new_column, old_index, new_index, old_sort_order, new_sort_order, changed_cards
on_card_reordered card_reordered Yes Same payload shape as on_card_moved for same-column order changes.
on_column_created column_created Yes column_id, column_data, index
on_column_updated column_updated Yes column_id, old_column_id, column_data, old_column_data, changed_fields, affected_cards
on_column_deleted column_deleted Yes column_id, column_data, old_index
on_column_reordered column_reordered Yes column_id, old_index, new_index, columns
on_data_changed data_changed Yes action_type, action_event, columns, cards
on_filter_changed filter_changed Yes filters, old_filters
on_search_changed search_changed Yes query, old_query
on_sort_changed sort_changed Yes sort_key, reverse, column_id, old_sort
on_action_cancelled action_cancelled No action_type, reason, action_event
on_error error No error, message, optional action_event
on_add_card_requested add_card_requested No column_id, column_data
on_edit_card_requested edit_card_requested No card_id, card_data
on_persistence_status persistence_status No board_id, state, message, queued_count
on_conflict conflict Decision mutation and conflict. Return "server_wins" or "local_wins"; another return keeps the configured strategy.

Custom context menu callbacks

A ContextMenuItem.callback receives a single event with type="card_context_action", source="context_menu", card_id, and card_data.

Theme and Style Reference

The package exports DEFAULT_THEME, DEFAULT_STYLE, DEFAULT_PRIORITY_COLORS, merge_theme(), and merge_style(). The following keys exist in DEFAULT_THEME.

Board, toolbar, and column keys

Keys Used by
board_fg_color, board_corner_radius, board_padding Board container background, shape, and outer padding.
toolbar_fg_color, toolbar_border_color, toolbar_border_width, toolbar_corner_radius, toolbar_text_color, toolbar_button_text_color, toolbar_primary_button_text_color Toolbar surface and button text styling.
column_fg_color, column_header_fg_color, column_border_color, column_border_width, column_corner_radius, column_header_corner_radius, column_title_text_color, column_count_fg_color, column_count_text_color, column_control_hover_color, column_no_results_text_color Column frame, header, count badge, controls, and no-results text.

Card and badge keys

Keys Used by
card_fg_color, card_hover_color, card_selected_color, card_border_color, card_selected_border_color, card_border_width, card_corner_radius, card_title_text_color, card_body_text_color, card_metadata_text_color, card_min_height, card_gap Default card background, border, typography, and spacing.
tag_fg_color, tag_text_color, badge_text_color, priority_colors, tag_colors Chip rendering for tag, tags, badge, and priority values.
drop_indicator_color, drag_preview_fg_color, drag_preview_text_color Drag indicator and drag preview window styling.

Buttons, inputs, dialogs, menus, and utility keys

Keys Used by
button_fg_color, button_hover_color, button_text_color, button_text_color_disabled, button_corner_radius, button_border_width Primary action buttons, including the toolbar add button and form save button.
secondary_button_fg_color, secondary_button_hover_color, secondary_button_text_color, secondary_button_text_color_disabled, secondary_button_corner_radius, secondary_button_border_width Secondary action buttons such as toolbar filter/sort/clear and form cancel.
search_fg_color, search_border_color, search_text_color, search_placeholder_text_color Toolbar search entry.
input_fg_color, input_border_color, input_text_color, input_placeholder_text_color, input_corner_radius, input_border_width Single-line form controls.
textbox_fg_color, textbox_border_color, textbox_text_color, textbox_corner_radius, textbox_border_width Textarea controls in forms.
checkbox_fg_color, checkbox_hover_color, checkbox_border_color, checkbox_checkmark_color, checkbox_text_color, checkbox_text_color_disabled, checkbox_corner_radius, checkbox_border_width Checkbox form controls and filter dialog checkbox.
optionmenu_fg_color, optionmenu_button_color, optionmenu_button_hover_color, optionmenu_text_color, optionmenu_text_color_disabled, optionmenu_dropdown_fg_color, optionmenu_dropdown_hover_color, optionmenu_dropdown_text_color, optionmenu_corner_radius Select controls and filter dialog option menus.
dialog_fg_color, dialog_border_color, dialog_border_width, dialog_corner_radius, dialog_title_text_color, dialog_text_color Popup dialogs.
panel_fg_color, panel_border_color, panel_border_width, panel_corner_radius, panel_title_text_color Side-panel form mode.
menu_fg_color, menu_hover_color, menu_text_color, menu_hover_text_color, menu_disabled_text_color, menu_border_width Tk menus created for sort, move, and card actions.
scrollbar_button_color, scrollbar_button_hover_color Scrollable column bodies and dialogs.
danger_color, muted_text_color, text_color, overlay_text_color, corner_radius, border_width Shared text, alert, and fallback geometry values across the widget tree.

Font slots

The constructor accepts a font_config mapping. Each entry is stored internally as {name}_font. The current widgets read these font slots:

toolbar_font input_font optionmenu_font menu_font button_font secondary_button_font checkbox_font column_title_font column_count_font column_button_font card_title_font card_body_font card_metadata_font badge_font form_title_font form_label_font filter_title_font

Lower-Level Widgets

Most applications only instantiate CTkKanbanBoard. The package also exports lower-level widgets that the board itself uses. These are most useful when you want to extend or embed parts of the board behavior in a custom composition.

Class Primary purpose Public methods
CTkKanbanCard Renders one card and forwards pointer gestures to the board. set_selected(), set_dimmed(), set_dragging()
CTkKanbanColumn Owns a column header, scrollable card layout, no-results label, and drop indicator. update_column_data(), add_card_widget(), place_card_widget(), set_card_widget_order(), remove_card_widget(), update_card_count(), show_drop_indicator(), clear_drop_indicator(), prepare_drag_geometry(), clear_drag_geometry(), card_index_at(), contains_point(), autoscroll(), show_no_results(), clear_no_results()
CTkKanbanToolbar Optional toolbar used by the board. set_search_query(), set_filter_active(), destroy()

Advanced dialog classes

Class Signature Use case
ctk_kanban.dialogs.CardFormFrame CardFormFrame(master, fields, theme, *, title, initial_data=None, on_submit, on_close=None, **kwargs) Reusable embedded form widget. Exposes focus_first_control().
ctk_kanban.dialogs.CardFormDialog CardFormDialog(master, fields, theme, *, title, initial_data=None, on_submit, on_close=None) Modal popup wrapper around CardFormFrame.
ctk_kanban.dialogs.FilterDialog FilterDialog(master, fields, cards, theme, *, current_filters=None, on_apply) Exact-match filter editor generated from filterable fields.

Helper Modules

These functions are available from submodules and are useful when you want to integrate more tightly with the package's data model or UI helpers.

ctk_kanban.themes

Function or constant Purpose
DEFAULT_THEME / DEFAULT_STYLE Default style dictionary.
DEFAULT_PRIORITY_COLORS Default priority chip colors for Critical, High, Medium, and Low.
merge_theme(theme=None) Returns a defensive copy of the default theme updated by the supplied mapping.
merge_style(style=None) Alias for merge_theme().

ctk_kanban.events

Function Purpose
create_event(event_type, source="api", **payload) Creates a standard event dictionary with a UTC ISO timestamp and source tag.
cancellation_reason(result) Interprets the return value of a callback and extracts a cancellation reason when present.

ctk_kanban.validators

Function Purpose
validate_column(column) Validates and normalizes one column definition.
validate_columns(columns) Validates an ordered column collection and rejects duplicate IDs.
validate_card(card, column_ids) Validates one card while preserving custom fields.
validate_cards(cards, column_ids) Validates a card collection and rejects duplicate IDs.
validate_field(field) Validates one field definition and fills default values.
validate_fields(fields) Validates the field list or returns the package defaults when None.
validate_card_values(card, fields) Applies required, option, type, and custom validation rules to card values.
validate_context_menu_items(items) Validates custom context-menu action definitions.

ctk_kanban.utils

Function Purpose
clone(value) Deep-copies public data before storing it or returning it.
display_value(value) Converts values into concise text for default rendering and form population.
searchable_text(value) Creates case-folded search text from a value.
parse_temporal(value) Best-effort conversion of dates and ISO strings into timezone-aware datetime values.
comparable_value(value) Builds a stable sort value across temporal, numeric, string, boolean, and other types.
parse_list_value(value) Normalizes comma-separated or iterable list-like form input to list[str].
generate_card_id(existing_ids) Generates the next integer ID when all current IDs are integers, otherwise a UUID string.
iter_widget_tree(widget) Yields a widget and all descendants.

Exceptions

Exception Meaning
KanbanError Base class for all package-specific exceptions.
KanbanValidationError Raised when board options or public data shapes are invalid.
KanbanDuplicateIDError Raised when a card ID, column ID, or field key is duplicated.
KanbanUnknownColumnError Raised when a card references a missing column or a column API targets an unknown ID.
KanbanMoveCancelledError Exported for callers that want to treat a cancelled move as an error condition.
KanbanPersistenceError Raised for board-level persistence constraints, such as mutating while saves are locked.
KanbanConflictError Exported persistence subtype for applications that represent concurrency conflicts as exceptions.

FAQ and Troubleshooting

Why is a card not visible even though it exists?

Check the current search query, active filters, and filter_mode. In "hide" mode, non-matching cards are removed from the visible column contents. In "dim" mode they remain visible but muted.

Why did add_card() or update_card() return None?

A cancellable callback rejected the change. Check on_card_created, on_card_updated, or on_data_changed, and listen to on_action_cancelled for the reason.

Why did delete_card(), move_card(), or move_column() return False?

The operation was cancelled by a callback or, for delete, the user rejected a confirmation prompt.

Why can't I delete a column?

The public API only allows deleting empty columns. Move or delete the column's cards first.

Why does the overdue filter not match anything?

The built-in overdue check reads the configured due-date and completion fields. Completed columns and the display timezone are configurable, and database timestamps can be rendered for the chosen locale.

How do I add my own database layer?

Wrap your repository's four CRUD functions with CRUDKanbanDataSource. Implement KanbanDataSource directly only when you need native server-side queries, bulk writes, conflict snapshots, or change streams. Start at Database and Persistence.

Why is the board stuck on offline or error?

Inspect get_persistence_status() and your application logs. Transient connection failures retry and then queue offline; call set_online(True) when connectivity returns. Rejected writes and conflicts require resolution before retry_last_save().

Why does polling reload the board?

get_changes() reports that durable state advanced, then the board calls refresh_from_source() to obtain a consistent page. Set poll_interval_ms=0 to disable polling or call refresh from your own push notification handler.

Why did a generated database ID not appear?

Return the complete canonical stored card from a CRUD create callback, or set MutationResult.card/id_map in a custom adapter. The board will replace its temporary ID and update selection and widgets.

Can callbacks touch Tk widgets?

UI callbacks run on Tk's thread, but data-source and CRUD methods run on the persistence worker. Keep Tk access out of storage code; return typed results and let the coordinator schedule board updates.

Notes and Limitations

  • The package includes a transactional SQLite adapter and a database-agnostic CRUD callback bridge. Advanced integrations can implement the KanbanDataSource protocol directly.
  • The CRUD bridge filters and pages the complete read result in the worker. For very large datasets, implement native query_cards() in a full data source.
  • The retry/offline queue is in memory, not a durable application outbox.
  • The built-in UI does not provide a column creation, edit, or delete dialog. Column structure changes are available through the API, not a dedicated editor.
  • Multiselect values use comma-separated entry. Date fields use the built-in calendar picker.
  • Database adapters may populate canonical created_at, updated_at, and version fields. Legacy created_date and updated_date sort keys remain available for existing schemas.
  • The internal filter matcher accepts both column and status as aliases for the card's current column ID, but the built-in filter UI generates only the fields marked filterable=True plus overdue_only.
  • Card and column IDs must be hashable. Floating-point IDs must also be finite.
  • Prefer stable string or integer IDs when board data crosses a JSON or database boundary.
  • The built-in SQLite adapter requires non-empty string or integer card and column IDs so their types survive JSON storage. In-memory boards accept any valid hashable ID.
Back to top