updated to generate trip
This commit is contained in:
419
screens/create_animation_screen_clean.py
Normal file
419
screens/create_animation_screen_clean.py
Normal file
@@ -0,0 +1,419 @@
|
||||
import kivy
|
||||
from kivy.uix.screenmanager import Screen
|
||||
import os
|
||||
import json
|
||||
import math
|
||||
from datetime import datetime
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import StringProperty, NumericProperty, AliasProperty
|
||||
from py_scripts.utils import (
|
||||
process_preview_util, optimize_route_entries_util
|
||||
)
|
||||
from py_scripts.advanced_3d_generator import NavigationAnimationGenerator
|
||||
from py_scripts.blender_animator import BlenderGPSAnimator
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.progressbar import ProgressBar
|
||||
from kivy.uix.textinput import TextInput
|
||||
from config import RESOURCES_FOLDER
|
||||
|
||||
class CreateAnimationScreen(Screen):
|
||||
project_name = StringProperty("")
|
||||
preview_html_path = StringProperty("") # Path to the HTML file for preview
|
||||
preview_image_path = StringProperty("") # Add this line
|
||||
preview_image_version = NumericProperty(0) # Add this line
|
||||
|
||||
def get_preview_image_source(self):
|
||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", self.project_name)
|
||||
img_path = os.path.join(project_folder, "preview.png")
|
||||
if os.path.exists(img_path):
|
||||
return img_path
|
||||
return "resources/images/track.png"
|
||||
|
||||
preview_image_source = AliasProperty(
|
||||
get_preview_image_source, None, bind=['project_name', 'preview_image_version']
|
||||
)
|
||||
|
||||
def on_pre_enter(self):
|
||||
# Update the route entries label with the actual number of entries
|
||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", self.project_name)
|
||||
positions_path = os.path.join(project_folder, "positions.json")
|
||||
count = 0
|
||||
if os.path.exists(positions_path):
|
||||
with open(positions_path, "r") as f:
|
||||
try:
|
||||
positions = json.load(f)
|
||||
count = len(positions)
|
||||
except Exception:
|
||||
count = 0
|
||||
self.ids.route_entries_label.text = f"Your route has {count} entries,"
|
||||
|
||||
def open_rename_popup(self):
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.textinput import TextInput
|
||||
from kivy.uix.label import Label
|
||||
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Enter new project name:")
|
||||
input_field = TextInput(text=self.project_name, multiline=False)
|
||||
btn_save = Button(text="Save", background_color=(0.008, 0.525, 0.290, 1))
|
||||
btn_cancel = Button(text="Cancel")
|
||||
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(input_field)
|
||||
layout.add_widget(btn_save)
|
||||
layout.add_widget(btn_cancel)
|
||||
|
||||
popup = Popup(
|
||||
title="Rename Project",
|
||||
content=layout,
|
||||
size_hint=(0.92, None),
|
||||
size=(0, 260),
|
||||
auto_dismiss=False
|
||||
)
|
||||
|
||||
def do_rename(instance):
|
||||
new_name = input_field.text.strip()
|
||||
if new_name and new_name != self.project_name:
|
||||
if self.rename_project_folder(self.project_name, new_name):
|
||||
self.project_name = new_name
|
||||
popup.dismiss()
|
||||
self.on_pre_enter() # Refresh label
|
||||
else:
|
||||
label.text = "Rename failed (name exists?)"
|
||||
else:
|
||||
label.text = "Please enter a new name."
|
||||
|
||||
btn_save.bind(on_press=do_rename)
|
||||
btn_cancel.bind(on_press=lambda x: popup.dismiss())
|
||||
popup.open()
|
||||
|
||||
def rename_project_folder(self, old_name, new_name):
|
||||
import os
|
||||
old_path = os.path.join(RESOURCES_FOLDER, "projects", old_name)
|
||||
new_path = os.path.join(RESOURCES_FOLDER, "projects", new_name)
|
||||
if os.path.exists(old_path) and not os.path.exists(new_path):
|
||||
os.rename(old_path, new_path)
|
||||
return True
|
||||
return False
|
||||
|
||||
def optimize_route_entries(self):
|
||||
# Create the popup and UI elements
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Processing route entries...")
|
||||
progress = ProgressBar(max=100, value=0)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Optimizing Route",
|
||||
content=layout,
|
||||
size_hint=(0.92, None),
|
||||
size=(0, 260),
|
||||
auto_dismiss=False
|
||||
)
|
||||
popup.open()
|
||||
|
||||
# Now call the utility function with these objects
|
||||
optimize_route_entries_util(
|
||||
self.project_name,
|
||||
RESOURCES_FOLDER,
|
||||
label,
|
||||
progress,
|
||||
popup,
|
||||
Clock,
|
||||
on_save=lambda: self.on_pre_enter()
|
||||
)
|
||||
|
||||
def preview_route(self):
|
||||
# Show processing popup
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Processing route preview...")
|
||||
progress = ProgressBar(max=100, value=0)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Previewing Route",
|
||||
content=layout,
|
||||
size_hint=(0.8, None),
|
||||
size=(0, 180),
|
||||
auto_dismiss=False
|
||||
)
|
||||
popup.open()
|
||||
|
||||
def set_preview_image_path(path):
|
||||
self.preview_image_path = path
|
||||
self.preview_image_version += 1 # Force AliasProperty to update
|
||||
self.property('preview_image_source').dispatch(self)
|
||||
self.ids.preview_image.reload()
|
||||
# Schedule the processing function
|
||||
Clock.schedule_once(
|
||||
lambda dt: process_preview_util(
|
||||
self.project_name,
|
||||
RESOURCES_FOLDER,
|
||||
label,
|
||||
progress,
|
||||
popup,
|
||||
self.ids.preview_image,
|
||||
set_preview_image_path,
|
||||
Clock
|
||||
),
|
||||
0.5
|
||||
)
|
||||
|
||||
def open_pauses_popup(self):
|
||||
"""Navigate to the pause edit screen"""
|
||||
pause_edit_screen = self.manager.get_screen("pause_edit")
|
||||
pause_edit_screen.set_project_and_callback(self.project_name, self.on_pre_enter)
|
||||
self.manager.current = "pause_edit"
|
||||
|
||||
def generate_google_earth_animation(self):
|
||||
"""Generate Google Earth-style flythrough animation using NavigationAnimationGenerator"""
|
||||
# Show processing popup
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Initializing Google Earth flythrough...")
|
||||
progress = ProgressBar(max=100, value=0)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Generating Google Earth Flythrough",
|
||||
content=layout,
|
||||
size_hint=(0.9, None),
|
||||
size=(0, 200),
|
||||
auto_dismiss=False
|
||||
)
|
||||
popup.open()
|
||||
|
||||
def run_google_earth_animation():
|
||||
try:
|
||||
# Update status
|
||||
def update_status(progress_val, status_text):
|
||||
def _update(dt):
|
||||
progress.value = progress_val
|
||||
label.text = status_text
|
||||
Clock.schedule_once(_update, 0)
|
||||
|
||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", self.project_name)
|
||||
positions_path = os.path.join(project_folder, "positions.json")
|
||||
|
||||
if not os.path.exists(positions_path):
|
||||
update_status(0, "Error: No GPS data found")
|
||||
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||
return
|
||||
|
||||
update_status(10, "Loading GPS data...")
|
||||
|
||||
# Check dependencies first
|
||||
generator = NavigationAnimationGenerator(project_folder)
|
||||
generator.check_dependencies()
|
||||
|
||||
update_status(20, "Processing GPS coordinates...")
|
||||
df = generator.load_gps_data(positions_path)
|
||||
|
||||
update_status(40, "Creating Google Earth flythrough...")
|
||||
output_video_path = os.path.join(project_folder, f"{self.project_name}_google_earth_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||
|
||||
# Progress callback for the generator
|
||||
def generator_progress(progress, message):
|
||||
update_status(40 + (progress * 0.5), message) # Map 0-100% to 40-90%
|
||||
|
||||
update_status(90, "Creating flythrough video...")
|
||||
success = generator.generate_frames(positions_path, style='google_earth', progress_callback=generator_progress)
|
||||
|
||||
if success and len(success) > 0:
|
||||
update_status(95, "Rendering final video...")
|
||||
video_success = generator.create_video(success, output_video_path, generator_progress)
|
||||
if video_success:
|
||||
update_status(100, "Google Earth flythrough complete!")
|
||||
output_path = output_video_path
|
||||
else:
|
||||
raise Exception("Failed to create video from frames")
|
||||
else:
|
||||
raise Exception("Failed to generate frames")
|
||||
|
||||
def show_success(dt):
|
||||
popup.dismiss()
|
||||
self.show_success_popup(
|
||||
"Google Earth Flythrough Complete!",
|
||||
f"Your Google Earth-style flythrough has been saved to:\n{output_path}",
|
||||
output_path
|
||||
)
|
||||
|
||||
Clock.schedule_once(show_success, 1)
|
||||
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
def show_error(dt):
|
||||
popup.dismiss()
|
||||
self.show_error_popup("Google Earth Animation Error", error_message)
|
||||
|
||||
Clock.schedule_once(show_error, 0)
|
||||
|
||||
# Schedule the animation generation
|
||||
Clock.schedule_once(lambda dt: run_google_earth_animation(), 0.5)
|
||||
|
||||
def generate_blender_animation(self):
|
||||
"""Generate cinema-quality animation using Blender"""
|
||||
# Show processing popup
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
label = Label(text="Initializing Blender rendering pipeline...")
|
||||
progress = ProgressBar(max=100, value=0)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Generating Blender Cinema Animation",
|
||||
content=layout,
|
||||
size_hint=(0.9, None),
|
||||
size=(0, 200),
|
||||
auto_dismiss=False
|
||||
)
|
||||
popup.open()
|
||||
|
||||
def run_blender_animation():
|
||||
try:
|
||||
# Update status
|
||||
def update_status(progress_val, status_text):
|
||||
def _update(dt):
|
||||
progress.value = progress_val
|
||||
label.text = status_text
|
||||
Clock.schedule_once(_update, 0)
|
||||
|
||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", self.project_name)
|
||||
positions_path = os.path.join(project_folder, "positions.json")
|
||||
|
||||
if not os.path.exists(positions_path):
|
||||
update_status(0, "Error: No GPS data found")
|
||||
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||
return
|
||||
|
||||
update_status(10, "Loading GPS data into Blender...")
|
||||
|
||||
# Check dependencies first
|
||||
animator = BlenderGPSAnimator(project_folder)
|
||||
animator.check_dependencies()
|
||||
|
||||
update_status(25, "Processing GPS coordinates...")
|
||||
gps_data = animator.load_gps_data(positions_path)
|
||||
|
||||
output_video_path = os.path.join(project_folder, f"{self.project_name}_blender_cinema_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||
|
||||
# Progress callback for the animator
|
||||
def animator_progress(progress, message):
|
||||
update_status(25 + (progress * 0.6), message) # Map 0-100% to 25-85%
|
||||
|
||||
update_status(85, "Rendering cinema-quality video...")
|
||||
success = animator.create_gps_animation(
|
||||
positions_path,
|
||||
output_video_path,
|
||||
progress_callback=animator_progress
|
||||
)
|
||||
|
||||
if success:
|
||||
update_status(100, "Blender cinema animation complete!")
|
||||
output_path = output_video_path
|
||||
else:
|
||||
raise Exception("Failed to generate Blender animation")
|
||||
|
||||
def show_success(dt):
|
||||
popup.dismiss()
|
||||
self.show_success_popup(
|
||||
"Blender Cinema Animation Complete!",
|
||||
f"Your cinema-quality animation has been saved to:\n{output_path}",
|
||||
output_path
|
||||
)
|
||||
|
||||
Clock.schedule_once(show_success, 1)
|
||||
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
def show_error(dt):
|
||||
popup.dismiss()
|
||||
self.show_error_popup("Blender Animation Error", error_message)
|
||||
|
||||
Clock.schedule_once(show_error, 0)
|
||||
|
||||
# Schedule the animation generation
|
||||
Clock.schedule_once(lambda dt: run_blender_animation(), 0.5)
|
||||
|
||||
def show_success_popup(self, title, message, file_path):
|
||||
"""Show success popup with option to open file location"""
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=15)
|
||||
|
||||
# Success message
|
||||
success_label = Label(
|
||||
text=message,
|
||||
text_size=(None, None),
|
||||
halign="center",
|
||||
valign="middle"
|
||||
)
|
||||
layout.add_widget(success_label)
|
||||
|
||||
# Buttons
|
||||
btn_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint_y=None, height=50)
|
||||
|
||||
open_folder_btn = Button(
|
||||
text="Open Folder",
|
||||
background_color=(0.2, 0.6, 0.9, 1)
|
||||
)
|
||||
|
||||
ok_btn = Button(
|
||||
text="OK",
|
||||
background_color=(0.3, 0.7, 0.3, 1)
|
||||
)
|
||||
|
||||
btn_layout.add_widget(open_folder_btn)
|
||||
btn_layout.add_widget(ok_btn)
|
||||
layout.add_widget(btn_layout)
|
||||
|
||||
popup = Popup(
|
||||
title=title,
|
||||
content=layout,
|
||||
size_hint=(0.9, 0.6),
|
||||
auto_dismiss=False
|
||||
)
|
||||
|
||||
def open_folder(instance):
|
||||
folder_path = os.path.dirname(file_path)
|
||||
os.system(f'xdg-open "{folder_path}"') # Linux
|
||||
popup.dismiss()
|
||||
|
||||
def close_popup(instance):
|
||||
popup.dismiss()
|
||||
|
||||
open_folder_btn.bind(on_press=open_folder)
|
||||
ok_btn.bind(on_press=close_popup)
|
||||
|
||||
popup.open()
|
||||
|
||||
def show_error_popup(self, title, message):
|
||||
"""Show error popup"""
|
||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=15)
|
||||
|
||||
error_label = Label(
|
||||
text=f"Error: {message}",
|
||||
text_size=(None, None),
|
||||
halign="center",
|
||||
valign="middle"
|
||||
)
|
||||
layout.add_widget(error_label)
|
||||
|
||||
ok_btn = Button(
|
||||
text="OK",
|
||||
background_color=(0.8, 0.3, 0.3, 1),
|
||||
size_hint_y=None,
|
||||
height=50
|
||||
)
|
||||
layout.add_widget(ok_btn)
|
||||
|
||||
popup = Popup(
|
||||
title=title,
|
||||
content=layout,
|
||||
size_hint=(0.8, 0.4),
|
||||
auto_dismiss=False
|
||||
)
|
||||
|
||||
ok_btn.bind(on_press=lambda x: popup.dismiss())
|
||||
popup.open()
|
||||
Reference in New Issue
Block a user