DEV Community

Cover image for Stop Building Ugly Apps: Create a Modern Python Dashboard in 15 Minutes 📊
Larry
Larry

Posted on • Originally published at logicpy.com

Stop Building Ugly Apps: Create a Modern Python Dashboard in 15 Minutes 📊

We’ve all been there. You write a Python script that processes data beautifully, calculates complex metrics, and works without a hitch. Then, you decide to give it a user interface using standard Tkinter.

The result? An application that looks like it belongs in Windows 95.

Today, we are done with ugly apps. We are going to build a standalone Data Analysis Dashboard that embeds live Matplotlib charts inside a modern, dark-mode interface using CustomTkinter.

Originally published on LogicPy.com

🚀 Quick Overview

  • Goal: Build a GUI App that visualizes live data.
  • Difficulty: Intermediate.
  • Time: ~20 Minutes.
  • Stack: Python, CustomTkinter, Matplotlib.

The Goal: A Professional Analytics Tool

We aren't just making a window with random buttons. We are building a layout that follows the "Golden Rules of GUI Design":

  1. Simplicity: A clean 2-column grid layout.
  2. Consistency: A unified dark color theme that matches the charts.
  3. Feedback: Immediate visual updates when selecting different metrics.

The Tech Stack

To achieve this, we need three ingredients:

  1. CustomTkinter: For the modern UI styling (Dark mode ready).
  2. Matplotlib: To generate the graphs.
  3. FigureCanvasTkAgg: The critical connector that lets Matplotlib "draw" inside a Tkinter window.

The Code Implementation

Below is the complete code. Notice how we use a Sidebar for controls (inputs) and a Main Frame for the visual output. This separates the doing from the seeing.

import customtkinter as ctk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# --- Rule 2: Consistency (Theme Setup) ---
ctk.set_appearance_mode("Dark")  
ctk.set_default_color_theme("blue")

class AnalyticsDashboard(ctk.CTk):
    def __init__(self):
        super().__init__()

        # Window Configuration
        self.title("LogicPy Data Analyzer")
        self.geometry("1000x600")

        # --- Rule 4: Responsiveness (Grid Layout) ---
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=1)

        # --- Component 1: The Sidebar (Controls) ---
        self.sidebar = ctk.CTkFrame(self, width=200, corner_radius=0)
        self.sidebar.grid(row=0, column=0, sticky="nsew")

        self.logo_label = ctk.CTkLabel(self.sidebar, text="LOGICPY\nANALYTICS", font=ctk.CTkFont(size=20, weight="bold"))
        self.logo_label.pack(padx=20, pady=(20, 10))

        # Control Buttons
        self.btn_sales = ctk.CTkButton(self.sidebar, text="Show Sales Trend", command=self.show_sales_data)
        self.btn_sales.pack(padx=20, pady=10)

        self.btn_users = ctk.CTkButton(self.sidebar, text="Show User Growth", command=self.show_user_data)
        self.btn_users.pack(padx=20, pady=10)

        # --- Component 2: The Main Display (Visualization) ---
        self.main_view = ctk.CTkFrame(self, corner_radius=0, fg_color="transparent")
        self.main_view.grid(row=0, column=1, sticky="nsew", padx=20, pady=20)

        # Placeholder for the chart canvas
        self.chart_canvas = None

        # Initialize with first dataset
        self.show_sales_data()

    def render_chart(self, x_data, y_data, title, line_color):
        """
        Refreshes the main view with a new Matplotlib chart.
        """
        # Rule 5: Feedback (Clear old data before showing new)
        if self.chart_canvas:
            self.chart_canvas.get_tk_widget().destroy()

        # Create the Matplotlib Figure
        # Note: We set the facecolor to match the CustomTkinter Dark Theme
        fig, ax = plt.subplots(figsize=(6, 5), dpi=100)
        fig.patch.set_facecolor('#242424') 
        ax.set_facecolor('#242424')

        # Plotting the Data
        ax.plot(x_data, y_data, marker='o', color=line_color, linewidth=3)

        # Styling the Chart to look "Modern"
        ax.set_title(title, color='white', fontsize=14, pad=15)
        ax.tick_params(axis='x', colors='gray')
        ax.tick_params(axis='y', colors='gray')
        ax.spines['bottom'].set_color('gray')
        ax.spines['left'].set_color('gray')
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)

        # Embedding into Tkinter
        self.chart_canvas = FigureCanvasTkAgg(fig, master=self.main_view)
        self.chart_canvas.draw()
        self.chart_canvas.get_tk_widget().pack(fill="both", expand=True)

    def show_sales_data(self):
        # Simulated Data Source
        months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
        sales = [12, 19, 24, 35, 42, 58]
        self.render_chart(months, sales, "Monthly Revenue ($k)", "#3B8ED0")

    def show_user_data(self):
        # Simulated Data Source
        months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
        users = [50, 120, 150, 210, 300, 450]
        self.render_chart(months, users, "Active User Count", "#2CC985")

if __name__ == "__main__":
    app = AnalyticsDashboard()
    app.mainloop()
Enter fullscreen mode Exit fullscreen mode

Why This Matters

Take a close look at the render_chart function. By matching the Matplotlib background color (#242424) to the CustomTkinter background, the chart feels like a native part of the application, not a pasted image.

What's Next?

You now have the skeleton of a professional data tool. In my next post on LogicPy.com, I’ll be showing how to package this script into an .EXE file so you can send it to friends or clients.

👉 Check out the full Data Visualization Series here.

Before you go, I’d love to hear from you. If something here sparked your curiosity or you’re stuck on a design problem of your own, drop your questions in the comments.

Top comments (0)