from kivy.uix.popup import Popup from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.button import Button from kivy.uix.textinput import TextInput from kivy.uix.filechooser import FileChooserIconView from kivy.uix.widget import Widget from kivy.graphics import Color, Rectangle, Line from kivy.uix.scrollview import ScrollView import os import json import shutil from geopy.geocoders import Nominatim def open_pauses_popup(screen_instance, project_name, RESOURCES_FOLDER, on_save_callback=None): project_folder = os.path.join(RESOURCES_FOLDER, "projects", project_name) pauses_path = os.path.join(project_folder, "pauses.json") # Main layout with dark background layout = BoxLayout(orientation='vertical', spacing=14, padding=14) with layout.canvas.before: Color(0.13, 0.13, 0.16, 1) layout.bg_rect = Rectangle(pos=layout.pos, size=layout.size) def update_bg_rect(instance, value): layout.bg_rect.pos = layout.pos layout.bg_rect.size = layout.size layout.bind(pos=update_bg_rect, size=update_bg_rect) pauses = [] if os.path.exists(pauses_path): with open(pauses_path, "r") as f: pauses = json.load(f) def suggest_location_name(lat, lon): try: geolocator = Nominatim(user_agent="traccar_animation") location = geolocator.reverse((lat, lon), exactly_one=True, radius=50, timeout=10) if not location or not location.raw or not location.address: return f"{lat:.5f}, {lon:.5f}" address = location.raw.get('address', {}) # 1. Place or location nearby (amenity, attraction, building, etc.) for key in ['attraction', 'building', 'amenity', 'shop', 'leisure', 'tourism', 'place', 'public_building']: if key in address: return address[key] # 2. Street name and number if 'road' in address and 'house_number' in address: return f"{address['road']} {address['house_number']}" # 3. Road name if 'road' in address: return address['road'] # 4. Fallback: lat/lon return f"{lat:.5f}, {lon:.5f}" except Exception: return f"{lat:.5f}, {lon:.5f}" # Main layout for popup (vertical) layout = BoxLayout(orientation='vertical', spacing=14, padding=14) # Scrollable area for pauses pauses_layout = BoxLayout(orientation='vertical', spacing=14, size_hint_y=None, padding=0) pauses_layout.bind(minimum_height=pauses_layout.setter('height')) for idx, pause in enumerate(pauses): # Main vertical box for this pause pause_box = BoxLayout( orientation='vertical', spacing=6, padding=[6, 0, 6, 4], size_hint_y=None, height=170 ) with pause_box.canvas.before: Color(0.20, 0.20, 0.25, 1) pause_box.bg_rect = Rectangle(pos=pause_box.pos, size=pause_box.size) Color(0.3, 0.5, 0.9, 1) # Border color pause_box.border_line = Line(rectangle=(pause_box.x, pause_box.y, pause_box.width, pause_box.height), width=1.2) def update_pause_box(instance, value, box=pause_box): box.bg_rect.pos = box.pos box.bg_rect.size = box.size box.border_line.rectangle = (box.x, box.y, box.width, box.height) pause_box.bind(pos=update_pause_box, size=update_pause_box) # --- Row 1: Name label --- title_label = Label( text=f"[b]Pause {idx+1}[/b]", markup=True, font_size=15, color=(1, 1, 1, 1), size_hint_y=0.2, # 20% of the pause_box height halign="center", valign="middle" ) def update_title_size(instance, value): instance.text_size = (instance.width, None) title_label.bind(width=update_title_size) pause_box.add_widget(title_label) # --- Row 2: Location group --- place_group = BoxLayout(orientation='vertical', spacing=4, padding=4, size_hint_y=0.4) with place_group.canvas.before: Color(0.3, 0.5, 0.9, 0.3) place_group.border_rect = Rectangle(pos=place_group.pos, size=place_group.size) Color(0.3, 0.5, 0.9, 1) place_group.border_line = Line(rectangle=(place_group.x, place_group.y, place_group.width, place_group.height), width=1) def update_place_group(instance, value, box=place_group): box.border_rect.pos = box.pos box.border_rect.size = box.size box.border_line.rectangle = (box.x, box.y, box.width, box.height) place_group.bind(pos=update_place_group, size=update_place_group) suggested_place = suggest_location_name(pause["location"]["latitude"], pause["location"]["longitude"]) suggested_label = Label( text=f"Suggested place: {suggested_place}", font_size=9, color=(0.7, 0.9, 1, 1), size_hint_y=None, height=12, halign="left", valign="middle" ) suggested_label.bind(width=update_title_size) place_group.add_widget(suggested_label) personalized_label = Label( text=f"Personalized name place: {pause.get('name', '')}", font_size=9, color=(1, 1, 1, 1), size_hint_y=None, height=12, halign="left", valign="middle" ) personalized_label.bind(width=update_title_size) edit_loc_btn = Button( text="Edit Place", size_hint_x=None, width=70, font_size=10, background_color=(0.341, 0.235, 0.980, 1), color=(1, 1, 1, 1), height=16 ) def open_edit_popup(instance, pause=pause, personalized_label=personalized_label): edit_layout = BoxLayout(orientation='vertical', spacing=4, padding=4) with edit_layout.canvas.before: Color(0.13, 0.13, 0.16, 1) edit_layout.bg_rect = Rectangle(pos=edit_layout.pos, size=edit_layout.size) edit_layout.bind(pos=lambda inst, val: setattr(edit_layout.bg_rect, 'pos', inst.pos), size=lambda inst, val: setattr(edit_layout.bg_rect, 'size', inst.size)) input_field = TextInput(text=pause.get('name', ''), multiline=False, background_color=(0.18,0.18,0.22,1), foreground_color=(1,1,1,1), font_size=10, height=18) save_btn = Button(text="Save", background_color=(0.341, 0.235, 0.980, 1), color=(1,1,1,1), height=18, font_size=10) edit_layout.add_widget(Label(text="Edit Place Name:", color=(1,1,1,1), font_size=10, height=12, size_hint_y=None)) edit_layout.add_widget(input_field) edit_layout.add_widget(save_btn) edit_popup = Popup(title="Edit Place", content=edit_layout, size_hint=(0.7, None), size=(0, 80)) def save_name(instance): pause['name'] = input_field.text personalized_label.text = f"Personalized name place: {input_field.text}" edit_popup.dismiss() save_btn.bind(on_press=save_name) edit_popup.open() edit_loc_btn.bind(on_press=open_edit_popup) personalized_row = BoxLayout(orientation='horizontal', spacing=2, size_hint_y=None, height=14) personalized_row.add_widget(personalized_label) personalized_row.add_widget(edit_loc_btn) place_group.add_widget(personalized_row) pause_box.add_widget(place_group) # --- Row 3: Pictures group --- photo_group = BoxLayout(orientation='vertical', spacing=1, padding=2, size_hint_y=0.4) with photo_group.canvas.before: Color(0.2, 0.8, 0.5, 0.2) photo_group.border_rect = Rectangle(pos=photo_group.pos, size=photo_group.size) Color(0.2, 0.8, 0.5, 1) photo_group.border_line = Line(rectangle=(photo_group.x, photo_group.y, photo_group.width, photo_group.height), width=1) def update_photo_group(instance, value, box=photo_group): box.border_rect.pos = box.pos box.border_rect.size = box.size box.border_line.rectangle = (box.x, box.y, box.width, box.height) photo_group.bind(pos=update_photo_group, size=update_photo_group) pause_img_folder = os.path.join(project_folder, f"pause_{idx+1}") os.makedirs(pause_img_folder, exist_ok=True) img_list = [f for f in os.listdir(pause_img_folder) if os.path.isfile(os.path.join(pause_img_folder, f))] if img_list: photo_count_label = Label( text=f"You have {len(img_list)} photo(s) for this pause.", font_size=9, color=(0.8, 0.8, 0.8, 1), size_hint_y=None, height=12 ) photo_list_label = Label( text=", ".join(img_list), font_size=8, color=(0.7, 0.7, 0.7, 1), size_hint_y=None, height=10 ) else: photo_count_label = Label( text="No photos are set for this pause.", font_size=9, color=(0.8, 0.8, 0.8, 1), size_hint_y=None, height=12 ) photo_list_label = Label( text="", font_size=8, color=(0.7, 0.7, 0.7, 1), size_hint_y=None, height=10 ) photo_group.add_widget(photo_count_label) photo_group.add_widget(photo_list_label) browse_btn = Button( text="Browse", size_hint_x=0.33, size_hint_y=None, height=16, font_size=10, background_color=(0.341, 0.235, 0.980, 1), color=(1,1,1,1) ) delete_btn = Button( text="Delete", size_hint_x=0.34, size_hint_y=None, height=16, font_size=10, background_color=(0.8, 0.1, 0.1, 1), color=(1,1,1,1) ) save_pause_btn = Button( text="Save Pause", size_hint_x=0.33, size_hint_y=None, height=16, font_size=10, background_color=(0.341, 0.235, 0.980, 1), color=(1,1,1,1) ) bottom_box = BoxLayout(orientation='horizontal', spacing=4, size_hint_y=None, height=18) bottom_box.add_widget(browse_btn) bottom_box.add_widget(delete_btn) bottom_box.add_widget(save_pause_btn) photo_group.add_widget(bottom_box) pause_box.add_widget(photo_group) # Separator line sep = Widget(size_hint_y=None, height=1) with sep.canvas: Color(0.25, 0.25, 0.30, 1) Line(points=[0, 1, 1000, 1], width=1) pause_box.add_widget(sep) pauses_layout.add_widget(pause_box) scroll = ScrollView(size_hint=(1, 1)) scroll.add_widget(pauses_layout) layout.add_widget(scroll) # Save all and close save_all_btn = Button(text="Save All & Close", size_hint_y=None, height=44, background_color=(0.341, 0.235, 0.980, 1), color=(1,1,1,1)) def save_all(instance): with open(pauses_path, "w") as f: json.dump(pauses, f, indent=2) if on_save_callback: on_save_callback() popup.dismiss() save_all_btn.bind(on_press=save_all) layout.add_widget(save_all_btn) popup = Popup(title="Edit Pauses", content=layout, size_hint=(0.95, 0.95), background_color=(0.13, 0.13, 0.16, 1)) popup.open()