Files
traccar_animation/widgets/pause_edit_popup.py
2025-06-12 16:16:09 +03:00

284 lines
12 KiB
Python

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()