Skip to content

Building a real-time PC stats visualizer command line tool with Python

Posted on:July 29, 2023 at 09:57 AM
Reading time:12 minutes

Hello there, coders! Today, we’re going to build a dynamic command-line application that visualizes your PC’s performance metrics in real-time (like this one). If that sounds exciting, let’s dive right in!

Table of contents

Open Table of contents

What are we building?

We’re crafting a Python application that pulls key performance metrics such as CPU usage, memory usage, disk usage, and more. The application will then display these metrics in a visually appealing, easy-to-read format in the command line. This is a useful tool for anyone looking to get instant insights into their computer’s performance.

Tools we need

For this project, we’ll be using Python and a couple of libraries - psutil for retrieving system information, rich for creating an attractive console interface, and finally asciichartpy for a generating an ASCII based chart.

Breaking down the project

Our application has two main parts:

  1. Fetching system data using psutil.
  2. Displaying the data using rich (and asciichartpy to display the chart).

Fetching system data

To fetch system data, we’ll use the psutil library, which can gather information about CPU, memory, disks, network, sensors, and processes in our system. In layman’s terms, it’s like our personal system inspector.

Displaying the data

For the display, we’re using the rich library. rich can create text with styles, colors, and even tables, making our command line look like a work of art!

Implementation

Installing libraries

First, let’s install our necessary libraries with pip:

pip install psutil rich asciichartpy

Then we can start building our project. We’ll divide the code into four parts: utils.py for fetching data, charts.py for creating charts, tables.py for creating tables, and main.py for running our application.

Fetching system stats

In utils.py, we’ll define a function get_system_info() to gather system statistics:

# utils.py

import psutil
import time


def get_system_info():
    cpu_percent = psutil.cpu_percent()
    memory_info = psutil.virtual_memory()
    disk_info = psutil.disk_usage("/")
    uptime = time.time() - psutil.boot_time()
    network_info = psutil.net_io_counters()
    processes = len(psutil.pids())
    swap_info = psutil.swap_memory()

    return (
        cpu_percent,
        memory_info,
        disk_info,
        uptime,
        network_info,
        processes,
        swap_info,
    )

Creating the chart

In charts.py, we’ll create a function to generate our CPU chart:

# charts.py

from rich.text import Text
from asciichartpy import plot


def get_cpu_chart(cpu_data):
    return Text(plot(list(cpu_data), {"height": 18}), style="cyan")

Creating the tables

In tables.py, we’ll create functions to generate different tables that display different PC stats:

# tables.py

from rich.table import Table


def get_system_table(metric, percent, total, used, name, color):
    table = Table(expand=True)

    table.add_column("Metric", justify="center", style=f"{color}", no_wrap=True)
    table.add_column("Value", justify="center", style=f"{color}", no_wrap=True)

    table.add_row(f"{name} Usage", f"{percent}%")
    table.add_row("Total", f"{total / (1024.0 ** 3):.2f} GB")
    table.add_row("Used", f"{used / (1024.0 ** 3):.2f} GB")
    table.add_row("Usage Percentage", f"{percent}%")

    return table


def get_uptime_table(uptime):
    table = Table(expand=True)
    table.add_column(
        "Uptime (in seconds)", style="purple", justify="center", no_wrap=True
    )
    table.add_row(f"{uptime:.2f}")
    return table


def get_network_table(network_info):
    table = Table(expand=True)
    table.add_column("Sent (GB)", style="red", justify="center", no_wrap=True)
    table.add_column("Received (GB)", style="red", justify="center", no_wrap=True)
    table.add_row(
        f"{network_info.bytes_sent / (1024.0 ** 3):.2f}",
        f"{network_info.bytes_recv / (1024.0 ** 3):.2f}",
    )
    return table


def get_processes_table(processes):
    table = Table(expand=True)
    table.add_column("Running processes", style="green", justify="center", no_wrap=True)
    table.add_row(f"{processes}")
    return table


def get_swap_table(swap_info):
    table = Table(expand=True)
    table.add_column("Total (GB)", style="yellow", justify="center", no_wrap=True)
    table.add_column("Used (GB)", style="yellow", justify="center", no_wrap=True)
    table.add_row(
        f"{swap_info.total / (1024.0 ** 3):.2f}",
        f"{swap_info.used / (1024.0 ** 3):.2f}",
    )
    return table

Creating the live “canvas” to display everything

Finally, in main.py, we’ll bring everything together:

# main.py

import time
from collections import deque

from utils import get_system_info
from tables import (
    get_system_table,
    get_swap_table,
    get_network_table,
    get_processes_table,
    get_uptime_table,
)
from charts import get_cpu_chart
from rich.console import Console
from rich.layout import Layout
from rich.live import Live
from rich.panel import Panel

console = Console()
layout = Layout()
cpu_data = deque(maxlen=60)

# Define layout structure
layout.split_column(Layout(name="upper"), Layout(name="middle"), Layout(name="lower"))

layout["upper"].split_row(
    Layout(name="cpu"), Layout(name="memory"), Layout(name="disk")
)

layout["middle"].split_row(
    Layout(name="uptime"),
    Layout(name="network"),
    Layout(name="processes"),
    Layout(name="swap"),
)


def main():
    with Live(
        console=console, refresh_per_second=2, screen=True
    ) as live:  # use screen=True for full screen
        while True:
            (
                cpu_percent,
                memory_info,
                disk_info,
                uptime,
                network_info,
                processes,
                swap_info,
            ) = get_system_info()

            cpu_data.append(cpu_percent)

            cpu_table = get_system_table(
                cpu_percent,
                cpu_percent,
                memory_info.total,
                memory_info.used,
                "CPU",
                "cyan",
            )
            memory_table = get_system_table(
                memory_info.percent,
                memory_info.percent,
                memory_info.total,
                memory_info.used,
                "Memory",
                "green",
            )
            disk_table = get_system_table(
                disk_info.percent,
                disk_info.percent,
                disk_info.total,
                disk_info.used,
                "Disk",
                "yellow",
            )
            uptime_table = get_uptime_table(uptime)
            network_table = get_network_table(network_info)
            processes_table = get_processes_table(processes)
            swap_table = get_swap_table(swap_info)

            # Define layout structure
            layout.split_column(
                Layout(name="upper"),
                Layout(name="middle"),
                Layout(
                    Panel(
                        get_cpu_chart(cpu_data),
                        title="CPU Usage Graph",
                        style="bold cyan",
                    ),
                    ratio=2,
                ),
            )

            layout["upper"].split_row(
                Layout(Panel(cpu_table, title="CPU Usage", style="bold cyan")),
                Layout(Panel(memory_table, title="Memory Usage", style="bold green")),
                Layout(Panel(disk_table, title="Disk Usage", style="bold yellow")),
            )

            layout["middle"].split_row(
                Layout(Panel(uptime_table, title="Uptime", style="bold purple")),
                Layout(Panel(network_table, title="Network Usage", style="bold red")),
                Layout(Panel(processes_table, title="Processes", style="bold green")),
                Layout(Panel(swap_table, title="Saw Memory", style="bold yellow")),
            )

            live.update(layout)

            time.sleep(1)


if __name__ == "__main__":
    main()

Now you can run python main.py on your terminal and checkout your magnificent creation!

And there you have it, a Python command-line application that provides real-time visualization of your computer’s performance metrics.

Notes

In main.py, we use a deque to store the last 60 seconds of CPU usage data. We then use asciichartpy’s plot function to create a line graph of this data using the get_cpu_chart function we created inside charts.py. This graph is then updated every second in the Live context from rich.

Wrapping Up

This simple yet powerful tool provides valuable insights into your computer’s health. It’s a great project to sharpen your Python skills and to learn about system statistics and console-based applications.

Happy coding!

References