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)