Tkinter has no built-in way to stream live data into a chart. So I built one.

Tkinter has no built-in way to stream live data into a chart. So I built one.

Tkinter has a solid set of widgets for building desktop GUIs, but if you need a chart that updates in real time — think monitoring dashboards, sensor feeds, live logs — you're mostly stuck reaching for matplotlib embedded in a canvas, which introduces a dependency and a non-trivial amount of boilerplate just to get a simple scrolling line.

I built tkchart to fill that gap. It's a pure-Python library (zero external dependencies) that adds a LineChart widget directly into Tkinter. You create a chart, attach Line objects to it, and call show_data() from a background thread. That's the full loop.

import tkinter as tk
import tkchart
import threading, time, random

root = tk.Tk()

chart = tkchart.LineChart(
    master=root,
    x_axis_values=("t-9", "t-8", "t-7", "t-6", "t-5", "t-4", "t-3", "t-2", "t-1", "t"),
    y_axis_values=(0, 1000),
    y_axis_section_count=5,
    x_axis_section_count=10,
)
chart.pack(pady=10)

line = tkchart.Line(master=chart, color="#5dffb6", size=2, fill="enabled")

def stream():
    while True:
        chart.show_data(line=line, data=[random.randint(0, 1000)])
        time.sleep(0.5)

threading.Thread(target=stream, daemon=True).start()
root.mainloop()

A few things I spent time on:

  • Threading: show_data() is meant to be called from a non-main thread. Tkinter isn't thread-safe by default, so all canvas operations are marshalled back to the main loop carefully.
  • Multiple lines: You can layer several Line objects on the same LineChart, each with independent style (solid/dashed/dotted, fill, point highlights, custom colors).
  • Post-construction config: v2.2.0 added granular configure_*() methods so you can change axis colors, font styles, pointer behavior, etc. at runtime without rebuilding the widget.
  • Data retrieval: get_line_data(), get_lines_visible_data(), and friends let you query what's currently on screen — useful if you want to trigger alerts when a value crosses a threshold.

Install: pip install tkchart

GitHub: https://github.com/Thisal-D/tkchart

PyPI: https://pypi.org/project/tkchart/

What's the hardest part of building real-time data widgets in Tkinter for you? I'm curious whether the threading model is a dealbreaker for some use cases or if people just live with after() polling.

u/Fabulous-Tip-8007 — 11 days ago
▲ 8 r/FunMachineLearning+1 crossposts

I built EliminationSearchCV — a GridSearchCV alternative that cut search time by 152x with almost no accuracy loss

GridSearchCV has a fundamental problem: it never learns from early results.

A bad learning_rate=0.5 gets re-evaluated in every downstream combination. Adding one new 4-value parameter can quadruple your total training time. It treats round 1 and round 1000 as equally uninformed.

I built EliminationSearchCV to fix this.

GitHub: https://github.com/thisal-d/elimination-search-cv

PyPI: https://pypi.org/project/elimination-search-cv

How it works:

Instead of running the full Cartesian product upfront, it works in rounds:

  • Round 1: test each parameter value in isolation
  • Eliminate the worst performers per parameter
  • Round 2: test the surviving pairs
  • Eliminate again globally
  • Repeat until one winner remains

Concrete example — tuning LogisticRegression with 4 parameters:

param_grid = {
    'C':        [0.001, 0.01, 0.1, 1, 10, 100],  # 6 values
    'penalty':  ['l1', 'l2'],                      # 2 values
    'solver':   ['liblinear', 'saga'],             # 2 values
    'max_iter': [1000, 2000],                      # 2 values
}
# GridSearchCV: 6×2×2×2 = 48 combos × 5 folds = 240 fits
# EliminationSearchCV: 23 fits total
Round Combos tested Result
1 — single params 12 C:[1], penalty:['l1'], solver:['liblinear'], max_iter:[1000]
2 — pairs 6 unchanged (already 1 value each)
3 — triples 4 unchanged
4 — full 1 final result
Total 23 fits vs 240 for GridSearchCV

One extra thing: invalid combos (e.g. penalty='l1' + solver='lbfgs' which sklearn rejects) are caught, scored 0.0, and eliminated naturally — no crashes, no special handling needed.

Benchmark results (cv=2, elimination_rate=0.8, 10k samples, 3 datasets avg):

Model Speedup Accuracy diff
DecisionTree 152x -0.0008
RandomForest 36x -0.0002
KNeighbors 11x -0.0004
GradientBoosting 35x -0.0194
LogisticRegression 4x -0.0004

> Note: light grids (small search spaces) are actually slower with > this approach — the elimination overhead isn't worth it there. This > shines on large grids.

Drop-in replacement for GridSearchCV:

# Before
search = GridSearchCV(model, param_grid, cv=5)

# After — same interface, just swap the class
search = EliminationSearchCV(model, param_grid, cv=5, elimination_rate=0.8)

search.fit(X_train, y_train)
print(search.best_params_)   # same as GridSearchCV
print(search.best_score_)    # same as GridSearchCV
search.best_estimator_.predict(X_test)  # already refitted, ready to go
pip install elimination-search-cv

GitHub: https://github.com/thisal-d/elimination-search-cv

This is v0.0.1 — early stage and experimental. The algorithm's behaviour varies by dataset. I'd genuinely love to hear if it breaks on your use case, that feedback is more useful to me than praise right now.

Happy to answer questions!

u/Fabulous-Tip-8007 — 12 days ago