Skip to main content

CTkKanban

CTkKanban is a small Kanban widget for CustomTkinter. It focuses on predictable card editing and movement instead of trying to be a database framework or a complete project-management application.

Install

python -m pip install CTkKanBan

Import the package with its canonical lowercase name:

import customtkinter as ctk

from ctk_kanban import CTkKanbanBoard

app = ctk.CTk()
app.geometry("1000x650")

board = CTkKanbanBoard(
    app,
    columns=[
        {"id": "todo", "title": "To do"},
        {"id": "doing", "title": "Doing"},
        {"id": "done", "title": "Done"},
    ],
    cards=[
        {
            "id": 1,
            "column": "todo",
            "title": "Try the simplified board",
            "description": "Click for details and use the handle to drag.",
            "priority": "High",
            "tags": ["demo"],
        }
    ],
    on_change=lambda event: print(event["type"], event["data"]),
)
board.pack(fill="both", expand=True)
app.mainloop()

Interaction model

  • Click a card to select it and open the editor drawer.
  • Save explicitly with Save changes or Enter; Escape cancels.
  • Drag cards only from their upper-right drag handle.
  • Use the visible menu for move and delete actions.
  • Columns use menu actions for left/right movement instead of column dragging.

There is no inline editing, click-away autosave, whole-card dragging, floating drag preview, or window-wide drag binding. A local Tk grab makes sure a handle drag always receives its release event.

Styling

Board surfaces, controls, text, hover states, and scrollbars follow the active CustomTkinter color theme. Call ctk.set_default_color_theme(...) before creating the board to use another built-in or custom theme. Priority and tag metadata remain visible as compact colored pills. The optional theme mapping can override individual board tokens when needed.

Data

Columns contain id and title. Cards contain id, column, title, and the optional description, priority, and tags fields. IDs must be unique and must be nonblank strings or integers. Priorities are empty, Low, Medium, High, or Critical. Tags are trimmed, nonblank strings without commas.

get_data() returns a detached snapshot for application-owned storage. Use string or integer IDs when the snapshot will be encoded as JSON. set_data(snapshot) replaces the displayed board without emitting an event. The optional on_change callback receives one event after each successful add, edit, move, or delete that changes board data; event["data"] contains the latest complete snapshot. Search also changes only the view and does not emit an event.

Persistence, retries, paging, polling, and conflict handling intentionally live in the host application rather than the widget.

Database rows

Database results can be converted without adding a database-driver dependency to CTkKanban. Mapping rows from psycopg dict_row, sqlite3.Row, and SQLAlchemy are accepted by snapshot_from_rows():

from ctk_kanban import snapshot_from_rows

snapshot = snapshot_from_rows(column_rows, card_rows)
board.set_data(snapshot)

Plain DB-API tuple results can be converted using cursor metadata. Fetch each result before reusing its cursor:

from ctk_kanban import rows_from_cursor, snapshot_from_rows

cursor.execute("SELECT id, title FROM kanban_columns ORDER BY position")
columns = rows_from_cursor(cursor)

cursor.execute(
    """
    SELECT id, column_id AS column, title, description, priority, tags
    FROM kanban_cards
    ORDER BY column_id, position
    """
)
cards = rows_from_cursor(cursor)

board.set_data(snapshot_from_rows(columns, cards))

Use SQL aliases such as column_id AS column to produce CTkKanban's exact record keys. Result column names must be unique. rows_from_cursor() consumes all remaining rows returned by the cursor.

snapshot_from_cursors(columns_cursor, cards_cursor) is a shorter equivalent when two separately executed cursors are available. Every snapshot helper normalizes and validates the complete result before returning it.

Asynchronous loading

load_async() performs fetching and validation on a daemon worker, then calls set_data() and user callbacks safely on Tk's thread:

import psycopg
from psycopg.rows import dict_row

from ctk_kanban import snapshot_from_rows


def fetch_board():
    with psycopg.connect(DATABASE_URL, row_factory=dict_row) as connection:
        columns = connection.execute(COLUMN_QUERY).fetchall()
        cards = connection.execute(CARD_QUERY).fetchall()
        return snapshot_from_rows(columns, cards)


board.load_async(
    fetch_board,
    on_success=lambda snapshot: print("Loaded", len(snapshot["cards"]), "cards"),
    on_error=lambda error: print("Load failed:", error),
)

board.is_loading reports pending work and board.load_error retains the most recent asynchronous error. Existing data is preserved on failure unless clear_on_error=True is requested. Starting a newer load makes an older result stale, so it cannot overwrite newer data.

Pass on_card_open when the host application owns card editing. Its callback receives the card snapshot and replaces the built-in drawer when a card opens.

Main API

get_data() / set_data(data)
get_card(id) / get_cards(column_id=None) / get_columns()
add_card() / update_card() / move_card() / delete_card()
add_column() / update_column() / move_column() / delete_column()
open_add_card_editor() / open_edit_card_editor(id)
search(query)
set_loading(bool) / load_async(fetch_snapshot, ...)
rows_from_cursor(cursor)
snapshot_from_rows(columns, cards) / snapshot_from_cursors(columns_cursor, cards_cursor)

The Tk-free BoardModel is also public for applications that want to validate or manipulate board data without creating a window. Column.from_definition() and Card.from_definition() expose the same normalization for typed application code, while BoardSnapshot, ColumnRecord, and CardRecord provide public typing shapes.

Migrating from 1.x

Version 2 is intentionally breaking. Remove dynamic field definitions, inline editing options, persistence adapters, advanced filter/sort options, and the large set of enable_*/show_* constructor flags. Replace mutation-specific callbacks with on_change, and import from ctk_kanban rather than CTkKanBan. Remove custom record keys before loading data; v2 rejects fields outside its small schema instead of silently discarding them.

Development

python -m pip install -e ".[dev]"
python -m pytest -q
python -m ruff check .
python -m mypy ctk_kanban

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ctkkanban-2.0.1.tar.gz (40.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ctkkanban-2.0.1-py3-none-any.whl (33.3 kB view details)

Uploaded Python 3

File details

Details for the file ctkkanban-2.0.1.tar.gz.

File metadata

  • Download URL: ctkkanban-2.0.1.tar.gz
  • Upload date:
  • Size: 40.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ctkkanban-2.0.1.tar.gz
Algorithm Hash digest
SHA256 b89bdce99b5b1e8e301c91992cd2d777c1766e0f7a19906e876d8bd27ceb563b
MD5 b1f09945a7ed27c24e4bc07483b5448a
BLAKE2b-256 92a2064f175fc10e0bfcf70d3358e5663498636f547a8f0319b719ea04f6ffc3

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctkkanban-2.0.1.tar.gz:

Publisher: publish.yml on Harry-g25/CTkKanBan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ctkkanban-2.0.1-py3-none-any.whl.

File metadata

  • Download URL: ctkkanban-2.0.1-py3-none-any.whl
  • Upload date:
  • Size: 33.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ctkkanban-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 67379643e84ff67e90ef2a244e74ccaeda3e4dee203388fc48be416a7f01cf89
MD5 cc43a1559300a5d02e66f580279ef4fe
BLAKE2b-256 7105bed86d307c96a6a34e7feca019116cb1beddbaff2e61893082ce454e3178

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctkkanban-2.0.1-py3-none-any.whl:

Publisher: publish.yml on Harry-g25/CTkKanBan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

2.3.0

2 files

2.2.1

2 files

2.1.0

2 files

This release

2.0.1 This release

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page