DEV Community

Marcin Firmuga
Marcin Firmuga

Posted on

Wednesday Code Autopsy #4: The Canvas Arc Nobody Uses - PC Workman

Needed circular progress gauge. Googled "Python circular progress bar." Every answer: pip install [library].

Drew mine with pure tkinter Canvas:

  • Zero dependencies
  • Two parameters: style="arc", extent=-270
  • Professional look
  • 20 lines of code

secret: tkinter has THREE arc styles. 99% of devs only use one.


The Problem

I needed a circular progress gauge for PC Workman's "First Setup & Drivers" page.

Something clean. Professional. The kind you see in system health dashboards.

First instinct: Google.

Search: "Python circular progress bar"

Results:

  • matplotlib circular progress tutorial
  • PIL + pillow gauge example
  • PyQt QProgressBar subclass
  • Custom tkinter widget library

Every answer started the same: pip install [library].

PC Workman is already PyQt6 + tkinter + psutil + SQLite. Adding matplotlib for one gauge? Overkill.

There had to be a better way.


The Discovery

Opened the tkinter Canvas docs.

Not Stack Overflow. Not a tutorial. The actual documentation.

Found create_arc():

style: One of 'pieslice' (default), 'chord', or 'arc'.

style="arc" — draws just the arc itself. No fill. No center lines.

This was it.


The Solution

def _draw_arc(canvas, score):
    # Background arc (full 270° track)
    canvas.create_arc(10, 10, 110, 110,
                      start=225, extent=-270,
                      style="arc", outline="#1f2937", width=7)

    # Progress arc (proportional to score)
    if score is not None:
        color = _score_color(score)
        extent = -int(270 * score / 100)
        canvas.create_arc(10, 10, 110, 110,
                          start=225, extent=extent,
                          style="arc", outline=color, width=7)
Enter fullscreen mode Exit fullscreen mode

Result:

[10-second PC Health gauge animation]

Clean. Professional. Zero dependencies.


How It Works

The Geometry

create_arc() takes a bounding box, not center + radius:

canvas.create_arc(x1, y1, x2, y2, ...)
#                 └─────┬─────┘
#                 rectangle containing circle
Enter fullscreen mode Exit fullscreen mode

For a perfect circle at (60, 60) with radius 50:

canvas.create_arc(10, 10, 110, 110, ...)
#                 60-50, 60-50, 60+50, 60+50
Enter fullscreen mode Exit fullscreen mode

The Angles

tkinter measures angles from East (3 o'clock) = 0°, counterclockwise:

Direction Degrees
East
North 90°
West 180°
South 270°

Trick: Negative extent goes clockwise.

For a speedometer (7:30 to 4:30 position):

start=225,   # 225° = bottom-left (7:30)
extent=-270  # -270° clockwise = wraps to bottom-right (4:30)
Enter fullscreen mode Exit fullscreen mode

Perfect 270° gauge.


The Secret Third Style

Most devs know two Canvas arc styles:

Style What It Draws
"pieslice" Filled wedge from center (default)
"chord" Arc + straight line connecting ends
"arc" Just the arc curve

Almost nobody uses style="arc".

That's your circular progress bar.

style="arc", outline="#10b981", width=7
Enter fullscreen mode Exit fullscreen mode

7-pixel green arc. No fill. No custom drawing.
style="arc" has been in tkinter since Python 1.x. 99% of developers have never touched it.


The Production Implementation

The two-line example is simplified. Here's what ships in PC Workman:

def _draw_arc(canvas, score):
    canvas.delete("all")
    W, H = 108, 92
    cx, cy, r = W // 2, H // 2 + 4, 35

    # Layer 1: Outer glow (wider, darker)
    canvas.create_arc(cx-r-4, cy-r-4, cx+r+4, cy+r+4,
                      start=225, extent=-270, style="arc",
                      outline="#1a2035", width=10)

    # Layer 2: Track (gray path)
    canvas.create_arc(cx-r, cy-r, cx+r, cy+r,
                      start=225, extent=-270,
                      style="arc", outline="#1f2937", width=7)

    # Layer 3: Progress (colored, proportional)
    if score is not None:
        col = _score_color(score)
        extent = -int(270 * score / 100)
        canvas.create_arc(cx-r, cy-r, cx+r, cy+r,
                          start=225, extent=extent,
                          style="arc", outline=col, width=7)

        # Score text
        canvas.create_text(cx, cy - 3, text=str(score),
                          fill=col, font=("Consolas", 22, "bold"))
        canvas.create_text(cx, cy + 17, text="/100",
                          fill="#6b7280", font=("Consolas", 9))
Enter fullscreen mode Exit fullscreen mode

Three layers on same Canvas:

  1. Outer glow — 10px dark, 4px larger radius (depth)
  2. Track — 7px gray, full 270° (background)
  3. Progress — 7px colored, proportional (gauge)

No masking. No blending. Just stacking arcs.


The Math

extent = -int(270 * score / 100)
Enter fullscreen mode Exit fullscreen mode
Score Calculation Result
100 -int(270 * 1.0) -270 (full)
75 -int(270 * 0.75) -202 (¾)
50 -int(270 * 0.5) -135 (½)
0 -int(270 * 0.0) 0 (empty)

The int() is critical. tkinter won't accept float extents — it'll silently fail and draw nothing.


Color Grading

def _score_color(score):
    if score >= 80:
        return "#10b981"  # Green
    if score >= 55:
        return "#fbbf24"  # Amber
    return "#ef4444"      # Red

def _score_grade(score):
    if score >= 85: return "EXCELLENT"
    if score >= 70: return "GOOD"
    if score >= 50: return "FAIR"
    return "POOR"
Enter fullscreen mode Exit fullscreen mode

Three thresholds. Same color in arc, number, and label.

No color library. Just hex values.


The Scanning State

Gauge drawn twice:

  1. On load — gray track only (score=None)
  2. After scan — colored progress (score=0-100)
# Initial
_draw_arc(health_canvas, None)

# After scan thread
_draw_arc(health_canvas, calculated_score)
Enter fullscreen mode Exit fullscreen mode

if score is not None prevents empty arc or crash.

While scanning (registry check, driver status, updates), gauge waits as clean gray circle.


Copyable Minimal Example

Want this in your project? Here's the stripped version:

import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, width=120, height=120, bg="#0a0e14")
canvas.pack()

def draw_gauge(score):
    canvas.delete("all")

    # Gray track
    canvas.create_arc(10, 10, 110, 110,
                      start=225, extent=-270,
                      style="arc", outline="#1f2937", width=8)

    # Colored progress
    if score:
        extent = -int(270 * score / 100)
        canvas.create_arc(10, 10, 110, 110,
                          start=225, extent=extent,
                          style="arc", outline="#10b981", width=8)

        # Text
        canvas.create_text(60, 60, text=str(score),
                          fill="white", font=("Consolas", 18, "bold"))

draw_gauge(73)
root.mainloop()
Enter fullscreen mode Exit fullscreen mode

20 lines. Zero dependencies. Production-ready.


Why This Matters

This gauge powers health check in PC Workman (110+ downloads, 23 GitHub stars).
First thing users see in "First Setup & Drivers."

Makes the section feel premium — smooth, clean, not default tkinter.
All because I read Canvas docs instead of installing matplotlib.


The Bigger Lesson

Most developers solve problems by adding dependencies.
Need progress bar? Install library.

Need chart? Install library.

Need widget? Install library.

Nothing wrong with libraries. Good ones save time.
But sometimes the best solution is in stdlib docs.
In a parameter you've never used.
On a page you've never read.
style="arc" has existed since Python 1.x.
99% of developers never touched it.
I found it in 5 minutes of reading docs instead of Googling "how to."


What's Next

PC Workman v1.7.x builds toward TURBO Mode. Windows optimization tools:

  • CPU unparking
  • RAM flushing
  • Service management
  • Power plan switching

"First Setup & Drivers" scans system, gives health score before TURBO touches anything.

This gauge is part of that foundation.

v1.7.2 ships next week.


Your Turn

What's your "I can't believe this is possible with just X" moment?
Time you almost installed library, then realized solution was in docs?
Drop it below


Links

PC Workman:

Follow the build:


Wednesday Code Autopsy continues next week.

Building PC Workman in public. One arc at a time.

Top comments (0)