updated app
This commit is contained in:
2
main.py
2
main.py
@@ -5,7 +5,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.properties import StringProperty, ListProperty
|
from kivy.properties import StringProperty, ListProperty
|
||||||
from utils import (
|
from py_scripts.utils import (
|
||||||
generate_key, load_key, encrypt_data, decrypt_data,
|
generate_key, load_key, encrypt_data, decrypt_data,
|
||||||
check_server_settings, save_server_settings,
|
check_server_settings, save_server_settings,
|
||||||
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day
|
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day
|
||||||
|
|||||||
2
py_scripts/__init__.py
Normal file
2
py_scripts/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# py_scripts package
|
||||||
|
# Contains utility scripts for the Traccar Animation application
|
||||||
BIN
py_scripts/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
py_scripts/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
py_scripts/__pycache__/utils.cpython-311.pyc
Normal file
BIN
py_scripts/__pycache__/utils.cpython-311.pyc
Normal file
Binary file not shown.
BIN
py_scripts/__pycache__/video_3d_generator.cpython-311.pyc
Normal file
BIN
py_scripts/__pycache__/video_3d_generator.cpython-311.pyc
Normal file
Binary file not shown.
@@ -219,7 +219,8 @@ def process_preview_util(
|
|||||||
import folium
|
import folium
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from utils import html_to_image
|
# Import html_to_image function from within the same module
|
||||||
|
# (it's defined later in this file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
project_folder = os.path.join(RESOURCES_FOLDER, "projects", project_name)
|
project_folder = os.path.join(RESOURCES_FOLDER, "projects", project_name)
|
||||||
@@ -1 +1 @@
|
|||||||
gAAAAABoZS4Ed-JJHBU90HAwhBvMJ7HfCSU-AeSaFDEt-Hg1wJW57NBoZJw_9lXEw-uooZCbQnMEUAd9mmF_I_FR1oRC-u_6cqU01jHGQq8OzJubmvBiZ1zYVlCO-kpmmFB8jv0LaBLU
|
gAAAAABoa47dY_7ed4KPuQv7x1BWyfC8-MEwtoIo0u5lhW2Qp1BwdtL9Biry5xG0BhOGE7MgaO7-kSKJuZDiOxVzSXenDEeT0Bq7dW5GvIK8o_7Z5CN0gyXog_bBCV3FZvQ-b_s9fCkn
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 537 KiB |
@@ -1,232 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T06:00:23.000+00:00",
|
|
||||||
"end_time": "2025-06-15T06:15:19.000+00:00",
|
|
||||||
"duration_seconds": 897,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.78012944444444,
|
|
||||||
"longitude": 24.16865166666667
|
|
||||||
},
|
|
||||||
"location_suggestion": "\u0218oseaua Sibiului"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T07:27:39.000+00:00",
|
|
||||||
"end_time": "2025-06-15T07:56:41.000+00:00",
|
|
||||||
"duration_seconds": 1744,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.35275055555555,
|
|
||||||
"longitude": 24.042727222222222
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Podul \u0218ipotului 1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T08:36:25.000+00:00",
|
|
||||||
"end_time": "2025-06-15T08:48:10.000+00:00",
|
|
||||||
"duration_seconds": 702,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.390987777777774,
|
|
||||||
"longitude": 23.980355
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T09:02:08.000+00:00",
|
|
||||||
"end_time": "2025-06-15T09:09:53.765+00:00",
|
|
||||||
"duration_seconds": 466,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.409075,
|
|
||||||
"longitude": 23.938845
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T10:29:06.000+00:00",
|
|
||||||
"end_time": "2025-06-15T11:04:51.000+00:00",
|
|
||||||
"duration_seconds": 2180,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.40981166666667,
|
|
||||||
"longitude": 23.793883888888892
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T11:21:38.000+00:00",
|
|
||||||
"end_time": "2025-06-15T11:24:05.928+00:00",
|
|
||||||
"duration_seconds": 147,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.38780444444444,
|
|
||||||
"longitude": 23.751438888888888
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T11:25:30.000+00:00",
|
|
||||||
"end_time": "2025-06-15T11:28:36.000+00:00",
|
|
||||||
"duration_seconds": 186,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.390328333333336,
|
|
||||||
"longitude": 23.74829666666667
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T11:42:42.000+00:00",
|
|
||||||
"end_time": "2025-06-15T11:51:12.000+00:00",
|
|
||||||
"duration_seconds": 501,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.39019166666667,
|
|
||||||
"longitude": 23.70160277777778
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T11:52:48.000+00:00",
|
|
||||||
"end_time": "2025-06-15T12:01:47.000+00:00",
|
|
||||||
"duration_seconds": 540,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.39456,
|
|
||||||
"longitude": 23.69228777777778
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T12:12:38.000+00:00",
|
|
||||||
"end_time": "2025-06-15T12:19:57.845+00:00",
|
|
||||||
"duration_seconds": 440,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.38043944444445,
|
|
||||||
"longitude": 23.651888333333336
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strategica"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T12:58:38.000+00:00",
|
|
||||||
"end_time": "2025-06-15T13:53:36.000+00:00",
|
|
||||||
"duration_seconds": 3297,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.57911,
|
|
||||||
"longitude": 23.62189888888889
|
|
||||||
},
|
|
||||||
"location_suggestion": "Transalpina"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-15T14:28:22.000+00:00",
|
|
||||||
"end_time": "2025-06-15T14:38:39.000+00:00",
|
|
||||||
"duration_seconds": 618,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.78371777777778,
|
|
||||||
"longitude": 23.613761666666665
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Valea Frumoasei"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-19T05:25:20.000+00:00",
|
|
||||||
"end_time": "2025-06-19T14:09:05.000+00:00",
|
|
||||||
"duration_seconds": 31426,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.79909222222222,
|
|
||||||
"longitude": 24.085676666666668
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-20T05:18:03.000+00:00",
|
|
||||||
"end_time": "2025-06-20T14:05:26.784+00:00",
|
|
||||||
"duration_seconds": 31644,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.798976111111116,
|
|
||||||
"longitude": 24.085469444444445
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-21T07:45:07.000+00:00",
|
|
||||||
"end_time": "2025-06-21T08:04:04.000+00:00",
|
|
||||||
"duration_seconds": 1049,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.82062444444445,
|
|
||||||
"longitude": 24.151072222222222
|
|
||||||
},
|
|
||||||
"location_suggestion": "Calea \u0218urii Mari 36"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-23T05:16:09.000+00:00",
|
|
||||||
"end_time": "2025-06-23T05:18:52.000+00:00",
|
|
||||||
"duration_seconds": 163,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.79242277777778,
|
|
||||||
"longitude": 24.139758888888892
|
|
||||||
},
|
|
||||||
"location_suggestion": "\u0218oseaua Alba Iulia"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-23T05:26:59.000+00:00",
|
|
||||||
"end_time": "2025-06-23T13:42:32.000+00:00",
|
|
||||||
"duration_seconds": 29558,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.79897166666667,
|
|
||||||
"longitude": 24.085509444444444
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-24T05:02:36.000+00:00",
|
|
||||||
"end_time": "2025-06-24T05:06:24.161+00:00",
|
|
||||||
"duration_seconds": 228,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.79917111111111,
|
|
||||||
"longitude": 24.086246666666668
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Europa Unit\u0103"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-24T05:06:26.000+00:00",
|
|
||||||
"end_time": "2025-06-24T13:44:34.000+00:00",
|
|
||||||
"duration_seconds": 31088,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.79897666666667,
|
|
||||||
"longitude": 24.08552166666667
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-25T05:23:38.000+00:00",
|
|
||||||
"end_time": "2025-06-25T13:52:00.000+00:00",
|
|
||||||
"duration_seconds": 30500,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.799183888888884,
|
|
||||||
"longitude": 24.085637777777777
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-26T05:10:24.000+00:00",
|
|
||||||
"end_time": "2025-06-26T14:09:48.774+00:00",
|
|
||||||
"duration_seconds": 32365,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.799045,
|
|
||||||
"longitude": 24.085640555555557
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-27T05:13:04.000+00:00",
|
|
||||||
"end_time": "2025-06-27T14:08:01.630+00:00",
|
|
||||||
"duration_seconds": 32098,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.79897444444445,
|
|
||||||
"longitude": 24.085511666666665
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": "2025-06-30T04:39:06.000+00:00",
|
|
||||||
"end_time": "2025-06-30T13:13:43.000+00:00",
|
|
||||||
"duration_seconds": 30878,
|
|
||||||
"location": {
|
|
||||||
"latitude": 45.798997222222226,
|
|
||||||
"longitude": 24.085517777777778
|
|
||||||
},
|
|
||||||
"location_suggestion": "Strada Monaco"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 792 KiB |
@@ -1,9 +0,0 @@
|
|||||||
To update the colors to the specified values, we will convert the hex color codes to RGBA format (values between 0 and 1) and update the `server_box_color` property in the `HomeScreen` class.
|
|
||||||
|
|
||||||
Here are the RGBA equivalents of the provided hex colors:
|
|
||||||
|
|
||||||
- **Yellow (#FB8D14)**: `(0.984, 0.553, 0.078, 1)`
|
|
||||||
- **Red (#E8083E)**: `(0.909, 0.031, 0.243, 1)`
|
|
||||||
- **Green (#02864A)**: `(0.008, 0.525, 0.290, 1)`
|
|
||||||
The RGBA equivalent of `#573CFA` is `(0.341, 0.235, 0.980, 1)`.
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
python -m venv track
|
|
||||||
source track/bin/activate
|
|
||||||
pip install -r reqirements.txt
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
|||||||
import requests
|
|
||||||
|
|
||||||
def get_device_route(server_url, token, device_id, from_time, to_time):
|
|
||||||
"""
|
|
||||||
Fetch all positions for a device in a time frame from Traccar server.
|
|
||||||
"""
|
|
||||||
url = f"{server_url}/reports/route"
|
|
||||||
headers = {
|
|
||||||
"Authorization": f"Bearer {token}",
|
|
||||||
"Accept": "application/json"
|
|
||||||
}
|
|
||||||
params = {
|
|
||||||
"deviceId": device_id,
|
|
||||||
"from": from_time,
|
|
||||||
"to": to_time
|
|
||||||
}
|
|
||||||
response = requests.get(url, headers=headers, params=params)
|
|
||||||
if response.status_code == 200:
|
|
||||||
try:
|
|
||||||
positions = response.json()
|
|
||||||
print(f"Retrieved {len(positions)} positions.")
|
|
||||||
return positions
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error parsing JSON: {e}")
|
|
||||||
print(response.text)
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
print(f"Failed to fetch positions: {response.status_code} - {response.text}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
# Example usage:
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Use your actual Traccar API endpoint (not /reports/route)
|
|
||||||
server_url = "https://gps.moto-adv.com/api"
|
|
||||||
token = "SDBGAiEA4sNXvVhL8w_Jd-5oCiXAuS5La5yelCQemfNysZYItaMCIQDOkzaoWKHbNnbZJw9ruGlsvbp35d90x3EGOZLXW_Gls3sidSI6MSwiZSI6IjIwMjUtMDYtMDlUMjE6MDA6MDAuMDAwKzAwOjAwIn0"
|
|
||||||
device_id = 1 # Replace with your device ID
|
|
||||||
from_time = "2024-06-02T21:00:00Z"
|
|
||||||
to_time = "2025-06-03T20:59:00Z"
|
|
||||||
positions = get_device_route(server_url, token, device_id, from_time, to_time)
|
|
||||||
for pos in positions:
|
|
||||||
print(f"{pos['deviceTime']}: {pos['latitude']}, {pos['longitude']}")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
SDBGAiEA4sNXvVhL8w_Jd-5oCiXAuS5La5yelCQemfNysZYItaMCIQDOkzaoWKHbNnbZJw9ruGlsvbp35d90x3EGOZLXW_Gls3sidSI6MSwiZSI6IjIwMjUtMDYtMDlUMjE6MDA6MDAuMDAwKzAwOjAwIn0
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,10 +5,10 @@ import json
|
|||||||
import math
|
import math
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.properties import StringProperty, NumericProperty, AliasProperty
|
from kivy.properties import StringProperty, NumericProperty, AliasProperty
|
||||||
from utils import (
|
from py_scripts.utils import (
|
||||||
process_preview_util, optimize_route_entries_util
|
process_preview_util, optimize_route_entries_util
|
||||||
)
|
)
|
||||||
from video_3d_generator import generate_3d_video_animation
|
from py_scripts.video_3d_generator import generate_3d_video_animation
|
||||||
from kivy.uix.popup import Popup
|
from kivy.uix.popup import Popup
|
||||||
from kivy.uix.button import Button
|
from kivy.uix.button import Button
|
||||||
from kivy.uix.label import Label
|
from kivy.uix.label import Label
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.properties import StringProperty, ListProperty
|
from kivy.properties import StringProperty, ListProperty
|
||||||
from utils import (
|
from py_scripts.utils import (
|
||||||
generate_key, load_key, encrypt_data, decrypt_data,
|
generate_key, load_key, encrypt_data, decrypt_data,
|
||||||
check_server_settings, save_server_settings,
|
check_server_settings, save_server_settings,
|
||||||
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day
|
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from kivy.clock import Clock
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from config import RESOURCES_FOLDER, CREDENTIALS_FILE
|
from config import RESOURCES_FOLDER, CREDENTIALS_FILE
|
||||||
from utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
|
from py_scripts.utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
|
||||||
|
|
||||||
|
|
||||||
class IconButton(ButtonBehavior, Image):
|
class IconButton(ButtonBehavior, Image):
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from kivy.clock import Clock
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from config import RESOURCES_FOLDER, CREDENTIALS_FILE
|
from config import RESOURCES_FOLDER, CREDENTIALS_FILE
|
||||||
from utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
|
from py_scripts.utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
|
||||||
|
|
||||||
|
|
||||||
class LoginScreen(Screen):
|
class LoginScreen(Screen):
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.properties import StringProperty, ListProperty
|
from kivy.properties import StringProperty, ListProperty
|
||||||
from utils import (
|
from py_scripts.utils import (
|
||||||
generate_key, load_key, encrypt_data, decrypt_data,
|
generate_key, load_key, encrypt_data, decrypt_data,
|
||||||
check_server_settings, save_server_settings,
|
check_server_settings, save_server_settings,
|
||||||
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day
|
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day
|
||||||
|
|||||||
Binary file not shown.
@@ -1,284 +0,0 @@
|
|||||||
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()
|
|
||||||
Reference in New Issue
Block a user