I want to add a scroll bar in the second GUI where all the lists (card layout form) of servers are displayed how to do it . Im new in programming and tried using canvas and the scrollbar but it just changes the width of the card layout. I want it to be static Here is the code:
import tkinter as tk
from tkinter import messagebox, ttk
from backend import start_backend, servers # Import the backend logic
import threading
import requests
# Flag to track if the load balancer is running
is_running = False
backend_thread = None # Thread for the backend Flask app
def start_load_balancer():
"""Starts the load balancer."""
global is_running, backend_thread
if not is_running:
backend_thread = threading.Thread(target=start_backend, daemon=True)
backend_thread.start()
is_running = True
status_label.config(text="Load Balancer: Running", fg="green")
else:
messagebox.showinfo("Info", "Load Balancer is already running!")
def stop_load_balancer():
"""Stops the load balancer."""
global is_running
if is_running:
messagebox.showinfo("Info", "To stop the load balancer, manually terminate the process (CTRL+C in terminal).")
status_label.config(text="Load Balancer: Stopped", fg="red")
is_running = False
else:
messagebox.showinfo("Info", "Load Balancer is not running!")
def toggle_logs(server_index, log_table):
"""Toggles the visibility of the log table."""
if log_table.winfo_ismapped():
log_table.grid_remove() # Hide the table
else:
log_table.grid() # Show the table
def update_server_statuses(cards, log_tables):
"""Updates the status and logs of each server dynamically."""
for server, card, log_table in zip(servers, cards, log_tables):
try:
response = requests.get(server["url"], timeout=2)
if response.status_code == 200:
server["status"] = "Active"
else:
server["status"] = "Inactive"
except requests.exceptions.ConnectionError:
server["status"] = "Inactive"
# Update the card with the current status and request count
card.config(
text=f"Name: Server\nIP: {server['url']}\nStatus: {server['status']}\nRequests: {server['request_count']}",
fg="green" if server["status"] == "Active" else "red",
)
# Clear and update the log table, even if hidden
for i in log_table.get_children():
log_table.delete(i)
for log in server.get("logs", []):
log_table.insert("", "end", values=(log["timestamp"], log["method"], log["path"]))
# Repeat every 5 seconds
root.after(5000, lambda: update_server_statuses(cards, log_tables))
def initialize_main_gui():
"""Initializes the main GUI for the Load Balancer."""
global root, status_label
root = tk.Tk()
root.title("Load Balancer GUI")
root.geometry("1280x720")
# Title Label
title_label = tk.Label(root, text="Load Balancer - Server Statuses", font=("Arial", 20, "bold"))
title_label.pack(pady=20)
# Frame for control buttons
button_frame = tk.Frame(root, bg="#f7f7f7")
button_frame.pack(pady=10)
# Start and Stop Buttons
start_button = tk.Button(
button_frame,
text="Start Load Balancer",
command=start_load_balancer,
bg="#4CAF50",
fg="white",
font=("Arial", 14),
width=20
)
start_button.grid(row=0, column=0, padx=10)
stop_button = tk.Button(
button_frame,
text="Stop Load Balancer",
command=stop_load_balancer,
bg="#f44336",
fg="white",
font=("Arial", 14),
width=20
)
stop_button.grid(row=0, column=1, padx=10)
# Status Label
global status_label
status_label = tk.Label(root, text="Load Balancer: Stopped", fg="red", font=("Arial", 16))
status_label.pack(pady=10)
# Frame for card layout
card_frame = tk.Frame(root, bg="#f7f7f7")
card_frame.pack(pady=20, padx=20, fill="both", expand=True)
# Create cards and log tables for each server
cards = []
log_tables = []
for index, server in enumerate(servers):
# Frame for card and dropdown
server_frame = tk.Frame(card_frame, bg="white", bd=4, relief="ridge", padx=10, pady=10)
server_frame.pack(pady=10, fill="x", padx=10)
server_frame.grid_columnconfigure(0, weight=1) # Card (expandable column)
server_frame.grid_columnconfigure(1, weight=0) # Spacer
server_frame.grid_columnconfigure(2, weight=0) # Logs button column
# Card to display server information
card = tk.Label(
server_frame,
text=f"Name: Server\nIP: {server['url']}\nStatus: {server['status']}\nRequests: {server['request_count']}",
font=("Arial", 14),
bg="white",
fg="orange",
anchor="w",
justify="left",
)
card.grid(row=0, column=0, sticky="w")
cards.append(card)
# Dropdown button to toggle logs
dropdown_button = tk.Button(
server_frame,
text="Show Logs",
command=lambda i=index: toggle_logs(i, log_tables[i]),
bg="#2196F3",
fg="white",
font=("Arial", 12),
)
dropdown_button.grid(row=0, column=2, sticky="e", padx=20)
# Log table (initially hidden)
log_table = ttk.Treeview(server_frame, columns=("Timestamp", "Method", "Path"), show="headings", height=5)
log_table.heading("Timestamp", text="Timestamp")
log_table.heading("Method", text="Method")
log_table.heading("Path", text="Path")
log_table.column("Timestamp", width=200)
log_table.column("Method", width=100)
log_table.column("Path", width=200)
log_table.grid(row=1, column=0, columnspan=3, sticky="nsew", padx=10, pady=10)
log_table.grid_remove() # Start hidden
log_tables.append(log_table)
# Update server statuses and logs periodically
update_server_statuses(cards, log_tables)
root.mainloop()
def add_server():
"""Adds a new server with IP and Port to the backend configuration."""
server_ip = ip_entry.get()
server_port = port_entry.get()
if server_ip and server_port:
try:
# Ensure the port is a valid integer
server_port = int(server_port)
servers.append({"url": f"http://{server_ip}:{server_port}", "connections": 0, "request_count": 0, "status": "Unknown", "logs": []})
server_list.insert(tk.END, f"{server_ip}:{server_port}")
ip_entry.delete(0, tk.END)
port_entry.delete(0, tk.END)
except ValueError:
messagebox.showwarning("Warning", "Please enter a valid port number!")
else:
messagebox.showwarning("Warning", "Please enter both IP address and port!")
def delete_server():
"""Deletes the selected server from the list and backend configuration."""
selected_index = server_list.curselection()
if selected_index:
selected_server = server_list.get(selected_index)
server_list.delete(selected_index)
servers[:] = [server for server in servers if f"{server['url'].split('//')[1]}" != selected_server]
messagebox.showinfo("Info", f"Server {selected_server} removed successfully!")
else:
messagebox.showwarning("Warning", "Please select a server to delete!")
def proceed_to_main_gui():
"""Proceeds to the main GUI if servers are added."""
if not servers:
messagebox.showwarning("Warning", "Please add at least one server before proceeding!")
return
initial_gui.destroy()
initialize_main_gui()
# Initial GUI to add servers
initial_gui = tk.Tk()
initial_gui.title("Add Backend Servers")
initial_width = 900
initial_height = 800
# Center the window on the screen
screen_width = initial_gui.winfo_screenwidth()
screen_height = initial_gui.winfo_screenheight()
x_position = (screen_width // 2) - (initial_width // 2)
y_position = (screen_height // 2) - (initial_height // 2)
initial_gui.geometry(f"{initial_width}x{initial_height}+{x_position}+{y_position}")
initial_gui.configure(bg="#f7f7f7")
# Title Label
title_label = tk.Label(
initial_gui,
text="Load Balancer Configuration",
font=("Arial", 20, "bold"),
bg="#f7f7f7",
fg="#333"
)
title_label.pack(pady=20)
# Instructions Label
instruction_label = tk.Label(
initial_gui,
text="Enter the IP address and port of backend servers and click 'Add'.\nYou can also delete servers before proceeding.",
font=("Arial", 14),
bg="#f7f7f7",
fg="#555"
)
instruction_label.pack(pady=10)
# Input Fields for Server IP and Port
input_frame = tk.Frame(initial_gui, bg="#f7f7f7")
input_frame.pack(pady=10)
ip_label = tk.Label(input_frame, text="IP Address:", font=("Arial", 12), bg="#f7f7f7")
ip_label.grid(row=0, column=0, padx=5, pady=5, sticky="e")
ip_entry = tk.Entry(input_frame, font=("Arial", 12), width=30, bd=2, relief="groove")
ip_entry.grid(row=0, column=1, padx=5, pady=5)
port_label = tk.Label(input_frame, text="Port:", font=("Arial", 12), bg="#f7f7f7")
port_label.grid(row=1, column=0, padx=5, pady=5, sticky="e")
port_entry = tk.Entry(input_frame, font=("Arial", 12), width=10, bd=2, relief="groove")
port_entry.grid(row=1, column=1, padx=5, pady=5, sticky="w")
# Buttons for Adding and Deleting Servers
button_frame = tk.Frame(initial_gui, bg="#f7f7f7")
button_frame.pack(pady=10)
add_button = tk.Button(
button_frame,
text="Add Server",
command=add_server,
bg="#4CAF50",
fg="white",
font=("Arial", 14),
width=15
)
add_button.grid(row=0, column=0, padx=10)
delete_button = tk.Button(
button_frame,
text="Delete Server",
command=delete_server,
bg="#f44336",
fg="white",
font=("Arial", 14),
width=15
)
delete_button.grid(row=0, column=1, padx=10)
# Listbox to Display Added Servers
server_list_frame = tk.Frame(initial_gui, bg="#f7f7f7")
server_list_frame.pack(pady=10)
server_list_label = tk.Label(
server_list_frame,
text="Added Servers:",
font=("Arial", 16),
bg="#f7f7f7",
fg="#333"
)
server_list_label.pack(anchor="w")
server_list = tk.Listbox(server_list_frame, font=("Arial", 14), width=60, height=10, bd=2, relief="groove")
server_list.pack(pady=5)
# Proceed Button
proceed_button = tk.Button(
initial_gui,
text="Proceed",
command=proceed_to_main_gui,
bg="#2196F3",
fg="white",
font=("Arial", 16),
width=20
)
proceed_button.pack(pady=30)
# Run the Initial GUI
initial_gui.mainloop()
I want to add a scroll bar in the second GUI where all the lists (card layout form) of servers are displayed how to do it . Im new in programming and tried using canvas and the scrollbar but it just changes the width of the card layout. I want it to be static Here is the code:
import tkinter as tk
from tkinter import messagebox, ttk
from backend import start_backend, servers # Import the backend logic
import threading
import requests
# Flag to track if the load balancer is running
is_running = False
backend_thread = None # Thread for the backend Flask app
def start_load_balancer():
"""Starts the load balancer."""
global is_running, backend_thread
if not is_running:
backend_thread = threading.Thread(target=start_backend, daemon=True)
backend_thread.start()
is_running = True
status_label.config(text="Load Balancer: Running", fg="green")
else:
messagebox.showinfo("Info", "Load Balancer is already running!")
def stop_load_balancer():
"""Stops the load balancer."""
global is_running
if is_running:
messagebox.showinfo("Info", "To stop the load balancer, manually terminate the process (CTRL+C in terminal).")
status_label.config(text="Load Balancer: Stopped", fg="red")
is_running = False
else:
messagebox.showinfo("Info", "Load Balancer is not running!")
def toggle_logs(server_index, log_table):
"""Toggles the visibility of the log table."""
if log_table.winfo_ismapped():
log_table.grid_remove() # Hide the table
else:
log_table.grid() # Show the table
def update_server_statuses(cards, log_tables):
"""Updates the status and logs of each server dynamically."""
for server, card, log_table in zip(servers, cards, log_tables):
try:
response = requests.get(server["url"], timeout=2)
if response.status_code == 200:
server["status"] = "Active"
else:
server["status"] = "Inactive"
except requests.exceptions.ConnectionError:
server["status"] = "Inactive"
# Update the card with the current status and request count
card.config(
text=f"Name: Server\nIP: {server['url']}\nStatus: {server['status']}\nRequests: {server['request_count']}",
fg="green" if server["status"] == "Active" else "red",
)
# Clear and update the log table, even if hidden
for i in log_table.get_children():
log_table.delete(i)
for log in server.get("logs", []):
log_table.insert("", "end", values=(log["timestamp"], log["method"], log["path"]))
# Repeat every 5 seconds
root.after(5000, lambda: update_server_statuses(cards, log_tables))
def initialize_main_gui():
"""Initializes the main GUI for the Load Balancer."""
global root, status_label
root = tk.Tk()
root.title("Load Balancer GUI")
root.geometry("1280x720")
# Title Label
title_label = tk.Label(root, text="Load Balancer - Server Statuses", font=("Arial", 20, "bold"))
title_label.pack(pady=20)
# Frame for control buttons
button_frame = tk.Frame(root, bg="#f7f7f7")
button_frame.pack(pady=10)
# Start and Stop Buttons
start_button = tk.Button(
button_frame,
text="Start Load Balancer",
command=start_load_balancer,
bg="#4CAF50",
fg="white",
font=("Arial", 14),
width=20
)
start_button.grid(row=0, column=0, padx=10)
stop_button = tk.Button(
button_frame,
text="Stop Load Balancer",
command=stop_load_balancer,
bg="#f44336",
fg="white",
font=("Arial", 14),
width=20
)
stop_button.grid(row=0, column=1, padx=10)
# Status Label
global status_label
status_label = tk.Label(root, text="Load Balancer: Stopped", fg="red", font=("Arial", 16))
status_label.pack(pady=10)
# Frame for card layout
card_frame = tk.Frame(root, bg="#f7f7f7")
card_frame.pack(pady=20, padx=20, fill="both", expand=True)
# Create cards and log tables for each server
cards = []
log_tables = []
for index, server in enumerate(servers):
# Frame for card and dropdown
server_frame = tk.Frame(card_frame, bg="white", bd=4, relief="ridge", padx=10, pady=10)
server_frame.pack(pady=10, fill="x", padx=10)
server_frame.grid_columnconfigure(0, weight=1) # Card (expandable column)
server_frame.grid_columnconfigure(1, weight=0) # Spacer
server_frame.grid_columnconfigure(2, weight=0) # Logs button column
# Card to display server information
card = tk.Label(
server_frame,
text=f"Name: Server\nIP: {server['url']}\nStatus: {server['status']}\nRequests: {server['request_count']}",
font=("Arial", 14),
bg="white",
fg="orange",
anchor="w",
justify="left",
)
card.grid(row=0, column=0, sticky="w")
cards.append(card)
# Dropdown button to toggle logs
dropdown_button = tk.Button(
server_frame,
text="Show Logs",
command=lambda i=index: toggle_logs(i, log_tables[i]),
bg="#2196F3",
fg="white",
font=("Arial", 12),
)
dropdown_button.grid(row=0, column=2, sticky="e", padx=20)
# Log table (initially hidden)
log_table = ttk.Treeview(server_frame, columns=("Timestamp", "Method", "Path"), show="headings", height=5)
log_table.heading("Timestamp", text="Timestamp")
log_table.heading("Method", text="Method")
log_table.heading("Path", text="Path")
log_table.column("Timestamp", width=200)
log_table.column("Method", width=100)
log_table.column("Path", width=200)
log_table.grid(row=1, column=0, columnspan=3, sticky="nsew", padx=10, pady=10)
log_table.grid_remove() # Start hidden
log_tables.append(log_table)
# Update server statuses and logs periodically
update_server_statuses(cards, log_tables)
root.mainloop()
def add_server():
"""Adds a new server with IP and Port to the backend configuration."""
server_ip = ip_entry.get()
server_port = port_entry.get()
if server_ip and server_port:
try:
# Ensure the port is a valid integer
server_port = int(server_port)
servers.append({"url": f"http://{server_ip}:{server_port}", "connections": 0, "request_count": 0, "status": "Unknown", "logs": []})
server_list.insert(tk.END, f"{server_ip}:{server_port}")
ip_entry.delete(0, tk.END)
port_entry.delete(0, tk.END)
except ValueError:
messagebox.showwarning("Warning", "Please enter a valid port number!")
else:
messagebox.showwarning("Warning", "Please enter both IP address and port!")
def delete_server():
"""Deletes the selected server from the list and backend configuration."""
selected_index = server_list.curselection()
if selected_index:
selected_server = server_list.get(selected_index)
server_list.delete(selected_index)
servers[:] = [server for server in servers if f"{server['url'].split('//')[1]}" != selected_server]
messagebox.showinfo("Info", f"Server {selected_server} removed successfully!")
else:
messagebox.showwarning("Warning", "Please select a server to delete!")
def proceed_to_main_gui():
"""Proceeds to the main GUI if servers are added."""
if not servers:
messagebox.showwarning("Warning", "Please add at least one server before proceeding!")
return
initial_gui.destroy()
initialize_main_gui()
# Initial GUI to add servers
initial_gui = tk.Tk()
initial_gui.title("Add Backend Servers")
initial_width = 900
initial_height = 800
# Center the window on the screen
screen_width = initial_gui.winfo_screenwidth()
screen_height = initial_gui.winfo_screenheight()
x_position = (screen_width // 2) - (initial_width // 2)
y_position = (screen_height // 2) - (initial_height // 2)
initial_gui.geometry(f"{initial_width}x{initial_height}+{x_position}+{y_position}")
initial_gui.configure(bg="#f7f7f7")
# Title Label
title_label = tk.Label(
initial_gui,
text="Load Balancer Configuration",
font=("Arial", 20, "bold"),
bg="#f7f7f7",
fg="#333"
)
title_label.pack(pady=20)
# Instructions Label
instruction_label = tk.Label(
initial_gui,
text="Enter the IP address and port of backend servers and click 'Add'.\nYou can also delete servers before proceeding.",
font=("Arial", 14),
bg="#f7f7f7",
fg="#555"
)
instruction_label.pack(pady=10)
# Input Fields for Server IP and Port
input_frame = tk.Frame(initial_gui, bg="#f7f7f7")
input_frame.pack(pady=10)
ip_label = tk.Label(input_frame, text="IP Address:", font=("Arial", 12), bg="#f7f7f7")
ip_label.grid(row=0, column=0, padx=5, pady=5, sticky="e")
ip_entry = tk.Entry(input_frame, font=("Arial", 12), width=30, bd=2, relief="groove")
ip_entry.grid(row=0, column=1, padx=5, pady=5)
port_label = tk.Label(input_frame, text="Port:", font=("Arial", 12), bg="#f7f7f7")
port_label.grid(row=1, column=0, padx=5, pady=5, sticky="e")
port_entry = tk.Entry(input_frame, font=("Arial", 12), width=10, bd=2, relief="groove")
port_entry.grid(row=1, column=1, padx=5, pady=5, sticky="w")
# Buttons for Adding and Deleting Servers
button_frame = tk.Frame(initial_gui, bg="#f7f7f7")
button_frame.pack(pady=10)
add_button = tk.Button(
button_frame,
text="Add Server",
command=add_server,
bg="#4CAF50",
fg="white",
font=("Arial", 14),
width=15
)
add_button.grid(row=0, column=0, padx=10)
delete_button = tk.Button(
button_frame,
text="Delete Server",
command=delete_server,
bg="#f44336",
fg="white",
font=("Arial", 14),
width=15
)
delete_button.grid(row=0, column=1, padx=10)
# Listbox to Display Added Servers
server_list_frame = tk.Frame(initial_gui, bg="#f7f7f7")
server_list_frame.pack(pady=10)
server_list_label = tk.Label(
server_list_frame,
text="Added Servers:",
font=("Arial", 16),
bg="#f7f7f7",
fg="#333"
)
server_list_label.pack(anchor="w")
server_list = tk.Listbox(server_list_frame, font=("Arial", 14), width=60, height=10, bd=2, relief="groove")
server_list.pack(pady=5)
# Proceed Button
proceed_button = tk.Button(
initial_gui,
text="Proceed",
command=proceed_to_main_gui,
bg="#2196F3",
fg="white",
font=("Arial", 16),
width=20
)
proceed_button.pack(pady=30)
# Run the Initial GUI
initial_gui.mainloop()
Share
Improve this question
edited Feb 2 at 10:26
Friedrich
4,84613 gold badges53 silver badges49 bronze badges
asked Feb 1 at 19:06
Evan BoseEvan Bose
111 bronze badge
2
|
1 Answer
Reset to default 0You can create a simple scrollable frame using Canvas
, Scrollbar
and Frame
widgets:
# simple vertical scrollable frame widget
class VerticalScrolledFrame(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master)
self._canvas = tk.Canvas(self, bd=0, highlightthickness=0)
self._vscrollbar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
self._canvas.config(yscrollcommand=self._vscrollbar.set)
self._vscrollbar.pack(side='right', fill='y')
self._canvas.pack(side='left', fill='both', expand=1)
self._internal = tk.Frame(self._canvas, **kwargs)
self._internal_id = self._canvas.create_window(0, 0, window=self._internal, anchor='nw')
self._internal.bind('<Configure>', self._on_internal_resize)
self._canvas.bind('<Configure>', self._on_canvas_resize)
def _on_internal_resize(self, event):
self._canvas.config(scrollregion=self._canvas.bbox('all'))
def _on_canvas_resize(self, event):
self._canvas.itemconfig(self._internal_id, width=event.width)
@property
def internal(self):
return self._internal
Then use this scrollable frame to hold those card layout forms:
def initialize_main_gui():
...
# Frame for card layout
scrolled_frame = VerticalScrolledFrame(root, bg='#f7f7f7')
scrolled_frame.pack(pady=20, padx=20, fill="both", expand=True)
card_frame = scrolled_frame.internal
...
Result:
servers
in the modulebackend
. 2. why you iteratedfor index, server in enumerate(servers):
? Did you want to declare servers variable as list? – Suramuthu R Commented Feb 2 at 4:08