diff --git a/src/rail_network_graph/GUI/gui_creator.py b/src/rail_network_graph/GUI/gui_creator.py new file mode 100644 index 0000000..6537479 --- /dev/null +++ b/src/rail_network_graph/GUI/gui_creator.py @@ -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")