Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Plot function
#1
Hi Everyone,
apologies my code is all over the place, however I am trying to bar chart plot the cycle time for each model selected. I want the chart to be in my main gui somwhere over to the right of my existing columns. I cannot get the code to work can someone please help me fix it?
import tkinter as tk
from tkinter import ttk
import math
from datetime import timedelta
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg



# Helper functions
def rgb_to_hex(r, g, b):
    return f'#{r:02x}{g:02x}{b:02x}'

def set_widget_bg_color(widget, bg_color, fg_color="black"):
    widget_class = widget.winfo_class()
    
    if widget_class == "Label":
        widget.config(bg=bg_color, fg=fg_color)
    elif widget_class == "Frame" or widget_class == "Toplevel":
        widget.config(bg=bg_color)
    elif widget_class == "Text":
        widget.config(bg=bg_color, fg=fg_color, insertbackground=fg_color)
    
    for child in widget.winfo_children():
        set_widget_bg_color(child, bg_color, fg_color)

def flash_red(widget, flash_count=5, interval=500):
    original_color = widget.cget("foreground")

    if flash_count % 2 == 0:
        widget.config(foreground=original_color)
    else:
        widget.config(foreground="red")

    if flash_count > 1:
        widget.after(interval, flash_red, widget, flash_count - 1, interval)

def format_duration(seconds):
    duration = timedelta(seconds=seconds)
    hours, remainder = divmod(duration.seconds, 3600)
    minutes, seconds = divmod(remainder, 60)
    return "{:02}:{:02}:{:02}".format(int(duration.days * 24 + hours), minutes, seconds)

def calculate_travel_time(travel_distance):
    return (
        (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
        (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
    )



def update_plot():
    plt.clf()  # Clear the previous plot
    
    models = [model_vars[col].get() for col in range(num_columns)]
    cycle_times = [cycle_time_labels[col]['text'] for col in range(num_columns)]
    
    plt.barh(models, [float(time.split(":")[0]) + float(time.split(":")[1])/60 + float(time.split(":")[2])/3600 for time in cycle_times])
    plt.xlabel("Cycle Time (hours)")
    plt.ylabel("Model")
    plt.title("Cycle Time Comparison")
    
    # Draw the plot onto the canvas
    canvas = FigureCanvasTkAgg(plt.gcf(), master=canvas_frame)
    canvas.draw()
    canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

def update_all_timings(col):
    # Call the functions to update timings for this column
    update_model_travel_times(col)
    calculate_cycle_time(col)
    update_plot()  # Call the function to update the plot






#Initialization of core data===============================================================
bg_color = rgb_to_hex(255, 205, 17)

models_data = {
    "Caterpillar LHD models": {
        "R1300G": ["DB 3.4", "DB 2.8", "DB 2.5", "DB 3.1"],
        "R1600H": ["DB 5.9", "DB 4.8", "DB 4.2", "DB 5.6"],
        "R1700": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5", "DB 8"],
        "R1700XE": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5"],
        "R1700G": ["DB 4.6", "DB 5", "DB 6.6", "DB 5.7", "DB 7.3", "DB 8.8"],
        "R2900G": ["DB 8.9", "DB 7.2", "DB 6.3", "DB 8.3"],
        "R2900": ["DB 6.3", "DB 7.2", "DB 8.3", "DB 8.9"],
        "R2900XE": ["DB 7.4", "DB 8.6", "DB 9.2", "DB 9.8"],
        "R3000H": ["DB 10.5", "DB 8.9", "DB 9.5"]
    },
    "Sandvik LHD models": {
        "SANDVIK LH517i": ["DB 7", "DB 7.6", "DB 8.6", "DB 9.1", "DB 8.4"],
        "SANDVIK LH621i": ["DB 8.0", "DB 9.0", "DB 10.7", "DB 11.2"],
        "SANDVIK LH515i": ["DB 6.3", "DB 6.8", "DB 7.5"],
        "SANDVIK LH514": ["DB 6.2", "DB 7", "DB 5.4"],
        "Toro™ LH625iE": ["DB 10"],
        "Sandvik LH514E": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7"],
        "Toro™ LH514BE": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7", "DB 7.5"],
        "Sandvik LH409E": ["DB 3.8", "DB 4.3", "DB 4.6"]
    },
    "Epiroc LHD models": {
        "Epiroc ST14 SG": ["DB 4.7", "DB 5", "DB 5.4", "DB 5.8", "DB 6.4", "DB 7.0", "DB 7.8"],
        "Epiroc ST18 SG": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"],
        "Epiroc ST14": ["DB 4.7", "DB 5", "DB 5.4", "DB 5.8", "DB 6.4", "DB 7.0", "DB 7.8"],
        "Epiroc ST18 S": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"]
    },
    "Komatsu LHD models": {
        "WX18H": ["DB 8.2", "DB 9.2", "DB 10", "DB 11.2"],
        "WX22H": ["DB 10", "DB 11", "DB 12.2", "DB 13.8"]
    }
}

payloads = {
    "R1300G": 6800,
    "R1600H": 10200,
    "R1700": 15000,
    "R1700XE": 15000,
    "R1700G": 12500,
    "R2900G": 17200,
    "R2900": 17200,
    "R2900XE": 18500,
    "R3000H": 20000,
    "SANDVIK LH517i": 17200,
    "SANDVIK LH621i": 21000,
    "SANDVIK LH515i": 15000,
    "SANDVIK LH514": 14000,
    "Toro™ LH625iE": 25000,
    "Sandvik LH514E": 14000,
    "Toro™ LH514BE": 14000,
    "Sandvik LH409E": 9600,
    "Epiroc ST14 SG": 14000,
    "Epiroc ST18 SG": 17500,
    "Epiroc ST14": 14000,
    "Epiroc ST18 S": 17500,
    "WX18H": 18000,
    "WX22H": 22000
}


#Widget Configuration Functions:===========================================================
def set_widget_bg_color(widget, bg_color, fg_color="black"):
    widget_class = widget.winfo_class()
    
    if widget_class == "Label":
        widget.config(bg=bg_color, fg=fg_color)  # Set the foreground color to fg_color
    elif widget_class == "Frame" or widget_class == "Toplevel":
        widget.config(bg=bg_color)
    elif widget_class == "Text":
        widget.config(bg=bg_color, fg=fg_color, insertbackground=fg_color)  # Set the foreground color to fg_color
    # Add more widgets here as needed

    # Recursively apply to children
    for child in widget.winfo_children():
        set_widget_bg_color(child, bg_color, fg_color)


def on_group_selected(column):
    selected_group = group_vars[column].get()
    model_dropdowns[column]['values'] = list(models_data[selected_group].keys())
    model_vars[column].set("Select Model")
    buckets_dropdowns[column]['values'] = []
    update_all_timings(column)  # Update timings first


def on_model_selected(column):
    selected_group = group_vars[column].get()
    selected_model = model_vars[column].get()
    if selected_model != "Select Model":
        buckets_dropdowns[column]['values'] = models_data[selected_group][selected_model]
        payload_labels[column].config(text=f"{payloads.get(selected_model, 'N/A')} kg")
        update_model_travel_times(column)
        update_all_timings(column)



def update_all_timings(col):
    # Call the functions to update timings for this column
    update_model_travel_times(col)
    calculate_cycle_time(col)

def update_model_travel_times(col):
    raw_distance = travel_distance_entries[col].get()
    load_time_empty = not load_time_entries[col].get().strip()
    dump_time_empty = not dump_time_entries[col].get().strip()
    delay_time_empty = not delay_time_entries[col].get().strip()

    if not raw_distance.strip():
        if load_time_empty and dump_time_empty and delay_time_empty:
            model_travel_time_labels[col].config(text="Input Required")
            flash_red(model_travel_time_labels[col])
        else:
            model_travel_time_labels[col].config(text="")
            # Reset the color to the original color if it was flashed
            model_travel_time_labels[col].config(foreground="black")
    else:
        try:
            travel_distance = float(raw_distance)
            travel_time = calculate_travel_time(travel_distance)
            formatted_travel_time = format_duration(travel_time * 60)
            model_travel_time_labels[col].config(text=formatted_travel_time)
        except ValueError:
            model_travel_time_labels[col].config(text="Invalid Distance")
            flash_red(model_travel_time_labels[col])






def calculate_cycle_time(col):
    try:
        travel_time = calculate_travel_time(float(travel_distance_entries[col].get()))
        load_time = float(load_time_entries[col].get())
        dump_time = float(dump_time_entries[col].get())
        delay_time = float(delay_time_entries[col].get())
        
        # Calculate total cycle time in hours
        cycle_time = travel_time + load_time/60  + dump_time/60 + delay_time/60 
        
        # Convert cycle time from hours to seconds
        seconds_total = cycle_time * 60
        
        # Format the total time into a readable string format
        formatted_cycle_time = format_duration(seconds_total)
        
        cycle_time_labels[col].config(text=formatted_cycle_time)
    except ValueError:
        cycle_time_labels[col].config(text="Input Required")



    





#Main Application Logic:==============================================================================

root = tk.Tk()
root.title("HRV LHD CPT Tool")

# Create a style for the labels
style = ttk.Style()
style.configure('My.TLabel', background=bg_color, foreground='black', font=("Arial", 10))
style.configure('MyBold.TLabel', background=bg_color, foreground='black', font=("Arial", 10, "bold"))



canvas_frame = tk.Frame(root)
canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

canvas = tk.Canvas(canvas_frame,  bg=bg_color)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

scrollbar = ttk.Scrollbar(canvas_frame, orient="vertical", command=canvas.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

canvas.configure(yscrollcommand=scrollbar.set)

content_frame = tk.Frame(canvas)
canvas.create_window((0, 0), window=content_frame, anchor="nw")


# Dynamic UI Creation
num_columns = len(models_data) # This line needs to be here, before any access to num_columns
group_vars = []
model_vars = []
model_dropdowns = []
buckets_dropdowns = []
travel_distance_entries = []
calculate_buttons = []
payload_labels = []
model_travel_time_labels = []
load_time_entries = []
dump_time_entries = []
delay_time_entries = []
cycle_time_labels = []
payload_titles = []
raw_distance =[]
travel_distance =[]




for col in range(num_columns):
    
    # Group dropdown
    group_var = tk.StringVar()
    group_var.set(list(models_data.keys())[col])
    group_vars.append(group_var)
    
    group_dropdown = ttk.Combobox(content_frame, textvariable=group_var, values=list(models_data.keys()), width=21)
    group_dropdown.bind("<<ComboboxSelected>>", lambda event, col=col: on_group_selected(col))
    group_dropdown.grid(row=0, column=col, padx=10, pady=10)
    
    # Model dropdown
    model_var = tk.StringVar()
    model_var.set("Select Model")
    model_vars.append(model_var)
    
    model_dropdown = ttk.Combobox(content_frame, textvariable=model_var, values=[], width=18)
    model_dropdown.bind("<<ComboboxSelected>>", lambda event, col=col: on_model_selected(col))
    model_dropdown.grid(row=1, column=col, padx=10, pady=10)
    model_dropdowns.append(model_dropdown)
    
    # Buckets dropdown
    buckets_var = tk.StringVar()
    buckets_var.set("Select Bucket")
    buckets_dropdown = ttk.Combobox(content_frame, textvariable=buckets_var, values=[], width=8)
    buckets_dropdown.grid(row=2, column=col, padx=10, pady=10)
    buckets_dropdowns.append(buckets_dropdown)

    # Rated Payload title
    payload_title = ttk.Label(content_frame, text="Rated Payload (kg)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    payload_title.grid(row=3, column=col, padx=10, pady=5)
    payload_titles.append(payload_title)

    # Rated Payload for the model
    payload_label = ttk.Label(content_frame, text="N/A", font=("Arial", 10), style='MyBold.TLabel')
    payload_label.grid(row=4, column=col, padx=10, pady=5)
    payload_labels.append(payload_label)

    # Travel Distance Entry
    travel_distance_var_col = tk.StringVar()
    travel_distance_entry_col = tk.Entry(content_frame, textvariable=travel_distance_var_col, justify=tk.CENTER, width=8)
    travel_distance_entry_col.grid(row=5, column=col, padx=10, pady=5)
    travel_distance_entries.append(travel_distance_entry_col)

    # Calculate button for the column
    calculate_button_col = tk.Button(content_frame, text="Calculate", command=lambda col=col: update_all_timings(col))
    calculate_button_col.grid(row=20, column=col, padx=10, pady=10)
    calculate_buttons.append(calculate_button_col)


    # Adding title for Travel Time
    travel_time_title = ttk.Label(content_frame, text="Travel Time (hh:mm:ss)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    travel_time_title.grid(row=7, column=col, padx=10, pady=5)

    # Travel Time label for the model
    travel_time_label = ttk.Label(content_frame, text="N/A", font=("Arial", 10,"bold"), style='MyBold.TLabel')
    travel_time_label.grid(row=8, column=col, padx=10, pady=5)
    model_travel_time_labels.append(travel_time_label)

    # Load Time Entry
    load_time_var_col = tk.StringVar()
    load_time_label = ttk.Label(content_frame, text="Load Time (seconds)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    load_time_label.grid(row=9, column=col, padx=10, pady=5)
    load_time_entry = tk.Entry(content_frame, textvariable=load_time_var_col, justify=tk.CENTER, width=8)
    load_time_entry.grid(row=10, column=col, padx=10, pady=5)
    load_time_entries.append(load_time_entry)

    # Dump Time Entry
    dump_time_var_col = tk.StringVar()
    dump_time_label = ttk.Label(content_frame, text="Dump Time (seconds)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    dump_time_label.grid(row=11, column=col, padx=10, pady=5)
    dump_time_entry = tk.Entry(content_frame, textvariable=dump_time_var_col, justify=tk.CENTER, width=8)
    dump_time_entry.grid(row=12, column=col, padx=10, pady=5)
    dump_time_entries.append(dump_time_entry)

    # Delay Time Entry
    delay_time_var_col = tk.StringVar()
    delay_time_label = ttk.Label(content_frame, text="Delay Time (seconds)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    delay_time_label.grid(row=13, column=col, padx=10, pady=5)
    delay_time_entry = tk.Entry(content_frame, textvariable=delay_time_var_col, justify=tk.CENTER, width=8)
    delay_time_entry.grid(row=14, column=col, padx=10, pady=5)
    delay_time_entries.append(delay_time_entry)

    # Cycle Time display
    cycle_time_title = ttk.Label(content_frame, text="Cycle Time (hh:mm:ss)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    cycle_time_title.grid(row=15, column=col, padx=10, pady=5)

    cycle_time_label = ttk.Label(content_frame, text="", font=("Arial", 10, "bold"), style='MyBold.TLabel')
    cycle_time_label.grid(row=16, column=col, padx=10, pady=5)
    cycle_time_labels.append(cycle_time_label)




# Update initial options
for col in range(num_columns):
    on_group_selected(col)
    on_model_selected(col)
    group_vars[col].set(list(models_data.keys())[col])
    model_vars[col].set(list(models_data[list(models_data.keys())[col]].keys())[0])
    buckets_dropdowns[col].set(models_data[list(models_data.keys())[col]][list(models_data[list(models_data.keys())[col]].keys())[0]][0])



# Configure scroll
def on_frame_configure(event):
    canvas.configure(scrollregion=canvas.bbox("all"))

content_frame.bind("<Configure>", on_frame_configure)

# Set widget background colors
set_widget_bg_color(root, bg_color)

for dropdown in model_dropdowns + buckets_dropdowns:
    dropdown['style'] = 'TCombobox'
    dropdown['justify'] = 'center'

# Event Callbacks============================================================================================

def on_group_selected(column):
    selected_group = group_vars[column].get()
    model_dropdowns[column]['values'] = list(models_data[selected_group].keys())
    model_vars[column].set("Select Model")
    buckets_dropdowns[column]['values'] = []


def on_model_selected(column):
    selected_group = group_vars[column].get()
    selected_model = model_vars[column].get()
    if selected_model != "Select Model":
        buckets_dropdowns[column]['values'] = models_data[selected_group][selected_model]
        payload_labels[column].config(text=f"{payloads.get(selected_model, 'N/A')} kg")
        update_model_travel_times(column)



    
    # If you've successfully retrieved the travel distance, continue processing
    travel_time = calculate_travel_time(travel_distance)
    formatted_travel_time = format_duration(travel_time * 60)  # Convert hours to minutes
    model_travel_time_labels[col].config(text=formatted_travel_time)



def update_all_timings(col):
    # Call the functions to update timings for this column
    update_model_travel_times(col)
    calculate_cycle_time(col)
    update_plot()

canvas_frame = tk.Frame(root)
canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Update the plot initially
update_plot()

# RUN MAIN LOOP
root.mainloop()
deanhystad write Aug-23-2023, 12:33 PM:
Please post code, output and errors between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply
#2
Nope, nope, nope, nope. Your code does not run. You have the same functions defined multiple times. The code is messy and far longer than I want to read. You don't even ask a clear question. If you are asking for assistance, you really need to do better. Post a small, runnable example that demonstrates the problem you are trying to solve, a problem that is clearly described in your post.

I modified the update_plot() function so you can at least get an idea of what your program looks like with the plot appearing right of the form.
def update_plot():
    plt.clf()  # Clear the previous plot
     
    models = [model_vars[col].get() for col in range(num_columns)]
    cycle_times = [cycle_time_labels[col]['text'] for col in range(num_columns)]
     
    plt.barh(models, [float(random.randint(1, 100)) for time in cycle_times])
    plt.xlabel("Cycle Time (hours)")
    plt.ylabel("Model")
    plt.title("Cycle Time Comparison")
The existing program crashed because you called update_plot() when the form was empty. It crashes whenever you change something in the form, because editing the form calls update_plot(), and the form is only partially complete. One way to get around this is catch the exceptions in update_plot. If there is an exception, don't change the plot. Maybe display a message to let the user know why the plot isn't updating.
def update_plot():
    plt.clf()  # Clear the previous plot
     
    models = [model_vars[col].get() for col in range(num_columns)]
    cycle_times = [cycle_time_labels[col]['text'] for col in range(num_columns)]
    try:
        cycle_times = [float(time.split(":")[0]) + float(time.split(":")[1])/60 + float(time.split(":")[2])/3600 for time in cycle_times]
    except (ValueError, IndexError):
        # Maybe display message?
        return

    plt.barh(models, cycle_times)
    plt.xlabel("Cycle Time (hours)")
    plt.ylabel("Model")
    plt.title("Cycle Time Comparison")
     
    # Draw the plot onto the canvas
    canvas = FigureCanvasTkAgg(plt.gcf(), master=canvas_frame)
    canvas.draw()
    canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
calculate_travel_distance() has an error. You pass a list as the travel_distance argument, but you treat travel_distance like it is a float. Not sure what the fix is because I don't know what you are trying to accomplish there. Either you need to modify the function to take a list, or you need to pass a float. I modified the function call.
def update_model_travel_times(col):
    raw_distance = travel_distance_entries[col].get()
    load_time_empty = not load_time_entries[col].get().strip()
    dump_time_empty = not dump_time_entries[col].get().strip()
    delay_time_empty = not delay_time_entries[col].get().strip()
 
    if not raw_distance.strip():
        if load_time_empty and dump_time_empty and delay_time_empty:
            model_travel_time_labels[col].config(text="Input Required")
            flash_red(model_travel_time_labels[col])
        else:
            model_travel_time_labels[col].config(text="")
            # Reset the color to the original color if it was flashed
            model_travel_time_labels[col].config(foreground="black")
    else:
        try:
            travel_distance = float(raw_distance)
            travel_time = calculate_travel_time(travel_distance[col])  # <-- Pass float, not list
            formatted_travel_time = format_duration(travel_time * 60)
            model_travel_time_labels[col].config(text=formatted_travel_time)
        except ValueError:
            model_travel_time_labels[col].config(text="Invalid Distance")
            flash_red(model_travel_time_labels[col])
Reply
#3
Hi

really appreciate the help and I realise my code is messy and apoligise in advance



i am getting the following error
PS C:\Users\SedatA> & C:/Users/SedatA/AppData/Local/Programs/Python/Python311/python.exe c:/Users/SedatA/Desktop/Python/GUI/today
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\SedatA\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "c:\Users\SedatA\Desktop\Python\GUI\today", line 417, in on_frame_configure
canvas.configure(scrollregion=canvas.bbox("all"))
^^^^^^^^^^^^^^^^
AttributeError: 'FigureCanvasTkAgg' object has no attribute 'configure'
PS C:\Users\SedatA>


for this code:

import tkinter as tk
from tkinter import ttk
import math
from datetime import timedelta
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# Main Application Logic

root = tk.Tk()
root.title("HRV LHD CPT Tool")

def rgb_to_hex(r, g, b):
return f'#{r:02x}{g:02x}{b:02x}'
bg_color = rgb_to_hex(255, 205, 17)

style = ttk.Style()
style.configure('My.TLabel', background=bg_color, foreground='black', font=("Arial", 10))
style.configure('MyBold.TLabel', background=bg_color, foreground='black', font=("Arial", 10, "bold"))


# Canvas and scrollbar for the entire content
canvas_frame = tk.Frame(root)
canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

canvas = tk.Canvas(canvas_frame, bg=bg_color)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

scrollbar = ttk.Scrollbar(canvas_frame, orient="vertical", command=canvas.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

canvas.configure(yscrollcommand=scrollbar.set)

content_frame = tk.Frame(canvas)
canvas.create_window((0, 0), window=content_frame, anchor="nw")


#Initialization of core data===============================================================


models_data = {
"Caterpillar LHD models": {
"R1300G": ["DB 3.4", "DB 2.8", "DB 2.5", "DB 3.1"],
"R1600H": ["DB 5.9", "DB 4.8", "DB 4.2", "DB 5.6"],
"R1700": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5", "DB 8"],
"R1700XE": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5"],
"R1700G": ["DB 4.6", "DB 5", "DB 6.6", "DB 5.7", "DB 7.3", "DB 8.8"],
"R2900G": ["DB 8.9", "DB 7.2", "DB 6.3", "DB 8.3"],
"R2900": ["DB 6.3", "DB 7.2", "DB 8.3", "DB 8.9"],
"R2900XE": ["DB 7.4", "DB 8.6", "DB 9.2", "DB 9.8"],
"R3000H": ["DB 10.5", "DB 8.9", "DB 9.5"]
},
"Sandvik LHD models": {
"SANDVIK LH517i": ["DB 7", "DB 7.6", "DB 8.6", "DB 9.1", "DB 8.4"],
"SANDVIK LH621i": ["DB 8.0", "DB 9.0", "DB 10.7", "DB 11.2"],
"SANDVIK LH515i": ["DB 6.3", "DB 6.8", "DB 7.5"],
"SANDVIK LH514": ["DB 6.2", "DB 7", "DB 5.4"],
"Toro™ LH625iE": ["DB 10"],
"Sandvik LH514E": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7"],
"Toro™ LH514BE": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7", "DB 7.5"],
"Sandvik LH409E": ["DB 3.8", "DB 4.3", "DB 4.6"]
},
"Epiroc LHD models": {
"Epiroc ST14 SG": ["DB 4.7", "DB 5", "DB 5.4", "DB 5.8", "DB 6.4", "DB 7.0", "DB 7.8"],
"Epiroc ST18 SG": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"],
"Epiroc ST14": ["DB 4.7", "DB 5", "DB 5.4", "DB 5.8", "DB 6.4", "DB 7.0", "DB 7.8"],
"Epiroc ST18 S": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"]
},
"Komatsu LHD models": {
"WX18H": ["DB 8.2", "DB 9.2", "DB 10", "DB 11.2"],
"WX22H": ["DB 10", "DB 11", "DB 12.2", "DB 13.8"]
}
}

payloads = {
"R1300G": 6800,
"R1600H": 10200,
"R1700": 15000,
"R1700XE": 15000,
"R1700G": 12500,
"R2900G": 17200,
"R2900": 17200,
"R2900XE": 18500,
"R3000H": 20000,
"SANDVIK LH517i": 17200,
"SANDVIK LH621i": 21000,
"SANDVIK LH515i": 15000,
"SANDVIK LH514": 14000,
"Toro™ LH625iE": 25000,
"Sandvik LH514E": 14000,
"Toro™ LH514BE": 14000,
"Sandvik LH409E": 9600,
"Epiroc ST14 SG": 14000,
"Epiroc ST18 SG": 17500,
"Epiroc ST14": 14000,
"Epiroc ST18 S": 17500,
"WX18H": 18000,
"WX22H": 22000
}


# Helper functions


def set_widget_bg_color(widget, bg_color, fg_color="black"):
widget_class = widget.winfo_class()

if widget_class == "Label":
widget.config(bg=bg_color, fg=fg_color)
elif widget_class == "Frame" or widget_class == "Toplevel":
widget.config(bg=bg_color)
elif widget_class == "Text":
widget.config(bg=bg_color, fg=fg_color, insertbackground=fg_color)

for child in widget.winfo_children():
set_widget_bg_color(child, bg_color, fg_color)

def flash_red(widget, flash_count=5, interval=500):
original_color = widget.cget("foreground")

if flash_count % 2 == 0:
widget.config(foreground=original_color)
else:
widget.config(foreground="red")

if flash_count > 1:
widget.after(interval, flash_red, widget, flash_count - 1, interval)

def format_duration(seconds):
duration = timedelta(seconds=seconds)
hours, remainder = divmod(duration.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return "{:02}:{:02}:{:02}".format(int(duration.days * 24 + hours), minutes, seconds)

def calculate_travel_time(travel_distance):
travel_time = (
(0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
(0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
)
return travel_time

def update_model_travel_times(col):
raw_distance = travel_distance_entries[col].get()
load_time_empty = not load_time_entries[col].get().strip()
dump_time_empty = not dump_time_entries[col].get().strip()
delay_time_empty = not delay_time_entries[col].get().strip()

if not raw_distance.strip():
if load_time_empty and dump_time_empty and delay_time_empty:
model_travel_time_labels[col].config(text="Input Required")
flash_red(model_travel_time_labels[col])
else:
model_travel_time_labels[col].config(text="")
# Reset the color to the original color if it was flashed
model_travel_time_labels[col].config(foreground="black")
else:
try:
travel_distance = float(raw_distance)
travel_time = calculate_travel_time(travel_distance)
formatted_travel_time = format_duration(travel_time * 60)
model_travel_time_labels[col].config(text=formatted_travel_time)
except ValueError:
model_travel_time_labels[col].config(text="Invalid Distance")
flash_red(model_travel_time_labels[col])


def calculate_cycle_time(col):
try:
travel_time = calculate_travel_time(float(travel_distance_entries[col].get()))
load_time = float(load_time_entries[col].get())
dump_time = float(dump_time_entries[col].get())
delay_time = float(delay_time_entries[col].get())

# Calculate total cycle time in hours
cycle_time = travel_time + load_time/60 + dump_time/60 + delay_time/60

# Convert cycle time from hours to seconds
seconds_total = cycle_time * 60

# Format the total time into a readable string format
formatted_cycle_time = format_duration(seconds_total)

cycle_time_labels[col].config(text=formatted_cycle_time)
except ValueError:
cycle_time_labels[col].config(text="Input Required")

canvas = FigureCanvasTkAgg(plt.figure(), master=canvas_frame)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)






def update_all_timings(col):
update_model_travel_times(col)
calculate_cycle_time(col)
# Comment out the following line to prevent unnecessary calls to update_plot()
# update_plot()

def on_group_selected(column):
selected_group = group_vars[column].get()
model_dropdowns[column]['values'] = list(models_data[selected_group].keys())
model_vars[column].set("Select Model")
buckets_dropdowns[column]['values'] = []

def on_model_selected(column):
selected_group = group_vars[column].get()
selected_model = model_vars[column].get()
if selected_model != "Select Model":
buckets_dropdowns[column]['values'] = models_data[selected_group][selected_model]
payload_labels[column].config(text=f"{payloads.get(selected_model, 'N/A')} kg")
update_model_travel_times(column)

def on_frame_configure(event):
canvas.configure(scrollregion=canvas.bbox("all"))




# Dynamic UI Creation
num_columns = len(models_data) # This line needs to be here, before any access to num_columns
group_vars = []
model_vars = []
model_dropdowns = []
buckets_dropdowns = []
travel_distance_entries = []
calculate_buttons = []
payload_labels = []
model_travel_time_labels = []
load_time_entries = []
dump_time_entries = []
delay_time_entries = []
cycle_time_labels = []
payload_titles = []
raw_distance =[]
travel_distance =[]

for col in range(num_columns):
# Group dropdown
group_var = tk.StringVar()
group_var.set(list(models_data.keys())[col])
group_vars.append(group_var)

group_dropdown = ttk.Combobox(content_frame, textvariable=group_var, values=list(models_data.keys()), width=21)
group_dropdown.bind("<<ComboboxSelected>>", lambda event, col=col: on_group_selected(col))
group_dropdown.grid(row=0, column=col, padx=10, pady=10)

# Model dropdown
model_var = tk.StringVar()
model_var.set("Select Model")
model_vars.append(model_var)

model_dropdown = ttk.Combobox(content_frame, textvariable=model_var, values=[], width=18)
model_dropdown.bind("<<ComboboxSelected>>", lambda event, col=col: on_model_selected(col))
model_dropdown.grid(row=1, column=col, padx=10, pady=10)
model_dropdowns.append(model_dropdown)

# Buckets dropdown
buckets_var = tk.StringVar()
buckets_var.set("Select Bucket")
buckets_dropdown = ttk.Combobox(content_frame, textvariable=buckets_var, values=[], width=8)
buckets_dropdown.grid(row=2, column=col, padx=10, pady=10)
buckets_dropdowns.append(buckets_dropdown)

# Rated Payload title
payload_title = ttk.Label(content_frame, text="Rated Payload (kg)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
payload_title.grid(row=3, column=col, padx=10, pady=5)
payload_titles.append(payload_title)

# Rated Payload for the model
payload_label = ttk.Label(content_frame, text="N/A", font=("Arial", 10), style='MyBold.TLabel')
payload_label.grid(row=4, column=col, padx=10, pady=5)
payload_labels.append(payload_label)

# Travel Distance Entry
travel_distance_var_col = tk.StringVar()
travel_distance_entry_col = tk.Entry(content_frame, textvariable=travel_distance_var_col, justify=tk.CENTER, width=8)
travel_distance_entry_col.grid(row=5, column=col, padx=10, pady=5)
travel_distance_entries.append(travel_distance_entry_col)

# Calculate button for the column
calculate_button_col = tk.Button(content_frame, text="Calculate", command=lambda col=col: update_all_timings(col))
calculate_button_col.grid(row=20, column=col, padx=10, pady=10)
calculate_buttons.append(calculate_button_col)

# Adding title for Travel Time
travel_time_title = ttk.Label(content_frame, text="Travel Time (hh:mm:ss)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
travel_time_title.grid(row=7, column=col, padx=10, pady=5)

# Travel Time label for the model
travel_time_label = ttk.Label(content_frame, text="N/A", font=("Arial", 10,"bold"), style='MyBold.TLabel')
travel_time_label.grid(row=8, column=col, padx=10, pady=5)
model_travel_time_labels.append(travel_time_label)

# Load Time Entry
load_time_var_col = tk.StringVar()
load_time_label = ttk.Label(content_frame, text="Load Time (seconds)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
load_time_label.grid(row=9, column=col, padx=10, pady=5)
load_time_entry = tk.Entry(content_frame, textvariable=load_time_var_col, justify=tk.CENTER, width=8)
load_time_entry.grid(row=10, column=col, padx=10, pady=5)
load_time_entries.append(load_time_entry)

# Dump Time Entry
dump_time_var_col = tk.StringVar()
dump_time_label = ttk.Label(content_frame, text="Dump Time (seconds)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
dump_time_label.grid(row=11, column=col, padx=10, pady=5)
dump_time_entry = tk.Entry(content_frame, textvariable=dump_time_var_col, justify=tk.CENTER, width=8)
dump_time_entry.grid(row=12, column=col, padx=10, pady=5)
dump_time_entries.append(dump_time_entry)

# Delay Time Entry
delay_time_var_col = tk.StringVar()
delay_time_label = ttk.Label(content_frame, text="Delay Time (seconds)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
delay_time_label.grid(row=13, column=col, padx=10, pady=5)
delay_time_entry = tk.Entry(content_frame, textvariable=delay_time_var_col, justify=tk.CENTER, width=8)
delay_time_entry.grid(row=14, column=col, padx=10, pady=5)
delay_time_entries.append(delay_time_entry)

# Cycle Time display
cycle_time_title = ttk.Label(content_frame, text="Cycle Time (hh:mm:ss)", font=("Arial", 10, "bold"), style='MyBold.TLabel')
cycle_time_title.grid(row=15, column=col, padx=10, pady=5)

cycle_time_label = ttk.Label(content_frame, text="", font=("Arial", 10, "bold"), style='MyBold.TLabel')
cycle_time_label.grid(row=16, column=col, padx=10, pady=5)
cycle_time_labels.append(cycle_time_label)

def update_plot():
plt.clf() # Clear the previous plot

models = [model_vars[col].get() for col in range(num_columns)]
cycle_times = [cycle_time_labels[col]['text'] for col in range(num_columns)]

# Convert cycle_times to hours, minutes, seconds format
cycle_time_in_hours = []
for time in cycle_times:
if time.strip():
hours, minutes, seconds = map(int, time.split(":"))
total_hours = hours + minutes / 60 + seconds / 3600
cycle_time_in_hours.append(total_hours)
else:
cycle_time_in_hours.append(0)

plt.barh(models, cycle_time_in_hours)
plt.xlabel("Cycle Time (hours)")
plt.ylabel("Model")
plt.title("Cycle Time Comparison")


# Update the plot on the existing canvas
canvas.draw()

# Call update_plot() to initially display the plot
update_plot()


# Update initial options

# Update initial options for the first column
on_group_selected(0)

# Set default options for the remaining columns
for col in range(1, num_columns):
on_group_selected(col)


# Set default options for the remaining columns
for col in range(1, num_columns):
group_vars[col].set("Select Group")
model_vars[col].set("Select Model")
buckets_dropdowns[col]['values'] = []
payload_labels[col].config(text="N/A")
model_travel_time_labels[col].config(text="N/A")
load_time_entries[col].delete(0, tk.END)
dump_time_entries[col].delete(0, tk.END)
delay_time_entries[col].delete(0, tk.END)
cycle_time_labels[col].config(text="")

# Set widget background colors
set_widget_bg_color(root, bg_color)

for dropdown in model_dropdowns + buckets_dropdowns:
dropdown['style'] = 'TCombobox'
dropdown['justify'] = 'center'


# Event Callbacks...
def on_group_selected(column):
selected_group = group_vars[column].get()
model_dropdowns[column]['values'] = list(models_data[selected_group].keys())
model_vars[column].set("Select Model")
buckets_dropdowns[column]['values'] = []

def on_model_selected(column):
selected_group = group_vars[column].get()
selected_model = model_vars[column].get()
if selected_model != "Select Model":
buckets_dropdowns[column]['values'] = models_data[selected_group][selected_model]
payload_labels[column].config(text=f"{payloads.get(selected_model, 'N/A')} kg")
update_model_travel_times(column)

def update_all_timings(col):
# Call the functions to update timings for this column
update_model_travel_times(col)
calculate_cycle_time(col)
# Comment out the following line to prevent unnecessary calls to update_plot()
# update_plot()


# Define canvas_frame as a global variable before any function
canvas_frame = None

# Event Callbacks...
def on_frame_configure(event):
global canvas_frame # Declare the use of the global variable
canvas.configure(scrollregion=canvas.bbox("all"))

for dropdown in model_dropdowns + buckets_dropdowns:
dropdown['style'] = 'TCombobox'
dropdown['justify'] = 'center'

# Configure the scrollbar and canvas
content_frame.bind("<Configure>", on_frame_configure)

# Update the plot initially
update_plot()

# Run main loop
root.mainloop()
Reply
#4
Do not use the "Reply" button unless you want to reference something from a post. Even if you do want to reply to another post, use quotes or edit the reply down to a smaller size. I'll delete your post that is nothing other than my post with no additional ccontent.

Where are the Python tags around your code? People cannot easily run you code if you don't surround it with Python tags. I'm not doing it for you again.

Your problem is you reuse canvas and then pretend it is a global variable. Which canvas are you configuring? the canvas that scrolls your form, or the canvas that contains your plot? The plot canvas does not support a configure method.

Do better on you posts. You must use the python/error/output tags.

And stop posting your entire program. Write a runnable program that demonstrates the problem and post that. I find writing an example program often exposes the source of the problem. Even if it doesn't I have a small program that others might be willing to look at. You are asing for help. You should be doing most of the work.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How to draw the Probability Density Function (PDF) plot regardless of sampe size? amylopectin 3 3,505 Mar-02-2022, 09:34 PM
Last Post: Larz60+
  How to plot intraday data of several days in one plot mistermister 3 2,933 Dec-15-2020, 07:43 PM
Last Post: deanhystad
  How to plot vertically stacked plot with same x-axis and SriMekala 0 1,944 Jun-12-2019, 03:31 PM
Last Post: SriMekala

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020