Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions src/rail_network_graph/GUI/gui_creator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import logging

import customtkinter as ctk
from pandas import DataFrame

from rail_network_graph.graphs.stations_graph import StationsGraph
from rail_network_graph.GUI.counter_panel.input_panel import InputPanel
from rail_network_graph.GUI.counter_panel.submit_handler import SubmitHandler
from rail_network_graph.GUI.map.map_canvas import MapCanvas
from rail_network_graph.GUI.map.map_plotter import MapPlotter
from rail_network_graph.GUI.station_info.station_info_logic import StationInfoLogic
from rail_network_graph.GUI.station_info.station_info_popup import StationInfoPopup

log = logging.getLogger(__name__)


class GUICreator:
"""
Create and initialize all main GUI components:

- interactive map,
- input panel (search + discount),
- station info popup,
- ticket popup (via submit handler).

:param root_window: Main application window (CTk/Tk frame).
:param map_plotter: Object used to draw the station map.
:param station_coords: Station coordinates {station_id: (lon, lat)}.
:param station_names: Station names {station_id: name}.
:param merged_data: GTFS merged DataFrame.
:param stops_data: Original stops DataFrame.
:param graph: Pre-built station graph.
"""

def __init__(
self,
root_window: ctk.CTk | ctk.CTkFrame,
map_plotter: MapPlotter,
station_coords: dict[str, tuple[float, float]],
station_names: dict[str, str],
merged_data: DataFrame,
stops_data: DataFrame,
graph: StationsGraph,
) -> None:
self.root = root_window
self.map_plotter = map_plotter
self.station_coords = station_coords
self.station_names = station_names
self.merged_data = merged_data
self.graph = graph
self.stops_data = stops_data

# Component instances (initially None)
self.station_info_popup: StationInfoPopup | None = None
self.station_info_logic: StationInfoLogic | None = None
self.map_canvas: MapCanvas | None = None
self.input_panel: InputPanel | None = None

log.info("Initializing GUICreator")
log.debug(
"GUICreator init: stations=%d, station_names=%d, merged_data_shape=%s, stops_data_shape=%s",
len(self.station_coords),
len(self.station_names),
getattr(self.merged_data, "shape", None),
getattr(self.stops_data, "shape", None),
)

# Call methods to sequentially initialize all GUI parts
self.create_station_popup()
self.create_station_logic()
self.create_map_canvas()
self.create_input_panel_with_handler()
log.info("GUICreator initialization complete")

def create_station_popup(self) -> None:
"""Create the popup window used to display station info."""
log.debug("Creating StationInfoPopup")
# Initialize the visual component for displaying station timetables/connections
self.station_info_popup = StationInfoPopup(self.root)
log.debug("StationInfoPopup created: %s", type(self.station_info_popup).__name__)

def create_station_logic(self) -> None:
"""Initialize the logic that fetches station connection data."""
log.debug("Creating StationInfoLogic")
# Initialize the data handler for generating station information (timetables)
self.station_info_logic = StationInfoLogic(self.station_names, self.merged_data)
log.debug("StationInfoLogic created with station_names=%d", len(self.station_names))

def create_map_canvas(self) -> None:
"""Create the interactive map canvas and bind events."""
log.debug("Creating MapCanvas")
# Initialize the interactive map component, passing the plotter and info components
self.map_canvas = MapCanvas(
self.root,
self.map_plotter,
self.station_coords,
self.station_info_popup,
self.station_info_logic,
)
log.info("MapCanvas created and initialized")

def create_input_panel_with_handler(self) -> None:
"""Create the input panel and attach the submit handler."""
log.debug("Creating InputPanel and SubmitHandler")
# Create the input panel (search fields, discount selector)
self.input_panel = InputPanel(self.root, submit_callback=None)
# Create the handler that processes inputs and performs routing/price calculation
submit_handler = SubmitHandler(
self.input_panel,
self.root,
self.map_canvas,
self.merged_data,
self.stops_data,
self.graph,
)
# Link the handler's method to the input panel's search button
self.input_panel.set_submit_callback(submit_handler.handle_submit)
log.info("InputPanel created and SubmitHandler connected to Search button")