How can I create a chart using python

133 views Asked by At

I'm trying to create a chart object in python using customtkinter, I didn't do it with canvas because it didn't fit what I was looking for (plus I wanted the challenge of trying), but I'm stuck on calculating the y-axis data to be equivalent to the height of the bars.

The x-axis data loads fine, but the y-axis data I can't get it to display equivalent to the bars as it would in a normal chart.

Can anyone give me a hand with this? This is the code I have:

import customtkinter as ctk
import csv

class CTkChart(ctk.CTkFrame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.global_width = self.cget('width')
        self.global_height = self.cget('height')
        self.col = 0

    def y_axis(self, text):
        frame = ctk.CTkFrame(self, fg_color="transparent", width=(self.global_width/10))
        frame.grid(row=0, column=0, padx=2, pady=2, sticky="nsew")

        sep = ctk.CTkLabel(frame, text="", fg_color="transparent")
        sep.grid(row=0, column=0, padx=2, pady=((self.global_height/10)))

        inner_frame = ctk.CTkFrame(frame, fg_color="transparent", width=(self.global_width/10))
        inner_frame.grid(row=0, column=1, padx=2, pady=0, sticky="n")  

        label = ctk.CTkLabel(frame, text=text, fg_color="transparent", width=20, height=5)
        label.grid(row=0, column=0, padx=0, pady=0)
        label.grid_rowconfigure(0, weight=0)
        label.grid_columnconfigure(0, weight=0)
        
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.bar_height = []
        return inner_frame

    def x_axis(self, text):
        frame = ctk.CTkFrame(self, fg_color="transparent", height=(self.global_width/10))
        frame.grid(row=1, column=1, padx=2, pady=2, sticky="nsew")
        
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)

        sep = ctk.CTkLabel(frame, text="", fg_color="transparent")
        sep.grid(row=0, column=0, padx=(self.global_width/5), pady=2)

        label = ctk.CTkLabel(frame, text=text, fg_color="transparent", width=40, height=5)
        label.grid(row=0, column=1, padx=0, pady=0)
        return frame

    def chart(self):
        frame = ctk.CTkFrame(self, fg_color="transparent")
        frame.grid(row=0, column=1, padx=2, pady=2, sticky="nsew")
        
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        return frame

    def create_bar(self, parent, h, color, value):
        label = ctk.CTkLabel(parent, text="", fg_color=color, width=20, height=h)
        label.grid(row=0, column=self.col, padx=2, pady=2, sticky="s")

        self.bar_height.append(h)

        data = ctk.CTkLabel(parent, text=value, fg_color="transparent", width=20, height=20)
        data.grid(row=1, column=self.col, padx=2, pady=2, sticky="s")
    
    def data_y(self, parent, text, row, l_data, max_value):
        height = max(self.bar_height) / (l_data) * 2
        label = ctk.CTkLabel(parent, text=text, fg_color="transparent", width=20, height=height)
        label.grid(row=l_data - row - 1, column=0, padx=2, pady=0, sticky="n")

    def clear_chart(self):

        for widget in self.winfo_children():
            widget.destroy()

    def create_chart(self, x_values, y_values, color):

        self.clear_chart()

        chart = self.chart()
        x = self.x_axis('test')
        y = self.y_axis("t\ne\ns\nt")

        max_value = max(y_values)

        max_bar_height = self.global_height * 0.8

        for n, value in enumerate(y_values):
            bar_height = (value / max_value) * max_bar_height
            print(f"Bar {n}: value={value}, bar_height={bar_height}")
            self.col = n
            self.create_bar(chart, bar_height, color, value)

        for n, value in reversed(list(enumerate(y_values))):
            if n % 2 == 0:
                label = str(round((value), 2))
                self.data_y(y, label, n, len(y_values), max_value)
                print(f"Label {n}: value={value}, label={label}")

root = ctk.CTk()
root.geometry("700x700")

frame = CTkChart(root, height=600)

chart = frame.chart()

x_values = ['Label 1', 'Label 2', 'Label 3', 'Label 4']
y_values = [100, 90, 150, 200]

frame.create_chart(x_values, y_values, "blue")

frame.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")

root.mainloop()

The idea would be to make it dynamic, that is to say, that regardless of the data it receives, it can be adapted. With static data I manage to show it well, but with dynamic data it always breaks.

Thanks in advance to all!

EDIT

I realized that I had copied the wrong code, I copied the last code I was testing instead of the one that was closest to the result I wanted.

I edited it with the correct code, I apologize for that.

This is how it looks like with the example values:

enter image description here

This is what I want:

enter image description here

0

There are 0 answers