Почему не удаляется элемент из словаря по ключу (библиотека tkintermapview)
Написано небольшое приложение, которое отображает на карте несколько маркеров. Данные для создания маркеров приходят по двум каналам: читаются из JSON-файла и трансформируются из адреса в координаты, посредством встроенной библиотеки. Между определенными маркерами необходимо прокладывать маршруты, а через время удалять маркер и маршрут(путь) к нему, кликнув по маркеру. Вопрос: не получается удалять путь к маркеру, к которому он (путь) прокладывается. В функции test() в блоке else сам 'marker' удаляется, но при попытке удаления по ключу 'path' происходит ошибка:
Traceback (most recent call last): File "C:\Python37-32\lib\tkinter_init_.py", line 1702, in call return self.func(*args) File "G:\Python\Delivery Map\venv\lib\site-packages\tkintermapview\canvas_position_marker.py", line 119, in click self.command(self) File "G:\Python\Delivery Map\test.py", line 341, in test marker_info['path'].delete() AttributeError: 'dict' object has no attribute 'delete'
вроде бы все правильно делаю, добавляю в словарь значение пути, но не работает. Хотя те маркеры, которые создаются из JSON-файла, посредством
markers[user_id] = {'marker': marker, 'path': path}
одновременно с началом пути, прекрасно удаляются вместе с маршрутом. Но по логике программы часть маркеров должна сначала создаваться, а уже потом к ним должны прокладываться маршруты. Если кто знает в чем я затупил - пните в нужную сторону. Прошу прощенья за сумбурное описание проблемы.
import json
import tkinter
from tkinter.constants import END, NE
import requests
import tkintermapview
import pygame
import ttk
import functions
# Определение дефолтных значений
DEFAULT_LATITUDE = 0.0
DEFAULT_LONGITUDE = 0.0
DEFAULT_USER_ID = "2000"
root = tkinter.Tk()
root.geometry(f"{800}x{600}")
root.title("Delivery Map")
root.iconbitmap('ic_launcher_playstore.ico')
# Флаг для отслеживания открытого информационного окна
info_window_open = False
pygame.mixer.init()
msg = tkinter.StringVar(root)
variable_list = tkinter.StringVar(root)
variable_list.set("Your choise")
if_click_to_marker = False
selected_markers_lat = ""
selected_markers_lon = ""
create_markers_lat = ""
create_markers_lon = ""
marker_id = ""
marker_text = ""
flag_stop_update_position = False
def get_address(coords):
global info_window_open
if not info_window_open: # Проверяем, открыто ли уже информационное окно
print("Coords: ", coords)
adr = tkintermapview.convert_coordinates_to_address(coords[0], coords[1])
print(adr.street, adr.housenumber, adr.postal, adr.city, adr.state, adr.country, adr.latlng)
show_info(adr)
info_window_open = True
def on_paste(event):
# Получаем содержимое буфера обмена
text = root.clipboard_get()
# Вставляем содержимое буфера обмена в поле
textField.insert(tkinter.INSERT, text)
def show_info(address):
global info_window_open
message = (
f"{address.street}, {address.housenumber}\n"
f"{address.postal} {address.city}\n"
f"{address.state}, {address.country}\n"
f"{address.latlng}"
)
info_window = tkinter.Toplevel(root)
info_window.iconbitmap('ic_launcher_playstore.ico')
info_window.configure(bg="lightblue")
info_window.title("Address info")
info_text = tkinter.Text(info_window, height=5, width=40)
info_text.insert(tkinter.END, message, "center")
info_text.tag_configure("center", justify="center") # Конфигурируем тег для центрирования
info_text.config(state=tkinter.DISABLED) # Запретить редактирование текста
info_text.pack(padx=10, pady=10)
info_button = tkinter.Button(info_window, text="OK", command=lambda: close_info_window(info_window))
info_button.pack(pady=(0, 10))
def close_info_window(window):
global info_window_open
window.destroy()
info_window_open = False
def left_click_event(coordinates_tuple):
global if_click_to_marker
if if_click_to_marker:
pass
# if_click_to_marker = False
else:
print("left click", coordinates_tuple)
def delete_all_path():
map_widget.delete_all_path()
map_widget = tkintermapview.TkinterMapView(root, width=800, height=600, corner_radius=0)
map_widget.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER, relwidth=1.0, relheight=1.0)
map_widget.set_zoom(15)
map_widget.add_right_click_menu_command(label="Get address",
command=get_address,
pass_coords=True)
map_widget.add_right_click_menu_command(label="Delete all path",
command=delete_all_path,
pass_coords=False)
map_widget.add_left_click_map_command(left_click_event)
markers = {} # Словарь для отслеживания маркеров по user_id
object_markers = {} # Словарь для отслеживания маркеров объектов по user_id
marker_list = []
def write_default_values_to_json():
"""Записывает дефолтные значения в JSON-файл."""
data_to_write = {
"lat": DEFAULT_LATITUDE,
"lon": DEFAULT_LONGITUDE,
"user_id": DEFAULT_USER_ID
}
with open('data.json', 'w') as json_file:
json.dump(data_to_write, json_file, indent=2)
def on_closing():
"""Функция вызывается при закрытии главного окна."""
write_default_values_to_json()
root.destroy()
def create_marker(latitude, longitude, user_id):
# Создаем маркер
marker = map_widget.set_marker(latitude, longitude, marker_color_outside="blue", marker_color_circle="gray",
command=remove_marker_and_path)
marker.set_text(user_id)
# Создаем путь и добавляем первую позицию
path = map_widget.set_path([marker.position, (latitude, longitude)])
path.add_position(latitude, longitude)
# Возвращаем маркер и путь
return marker, path
def update_map():
try:
with open('data.json', 'r') as json_file:
data_read = json.load(json_file)
latitude = data_read["lat"]
longitude = data_read["lon"]
user_id = data_read["user_id"]
global flag_stop_update_position
# Проверяем, изменились ли координаты
if (latitude, longitude) != update_map.last_coordinates:
# Сохраняем последние координаты
update_map.last_coordinates = (latitude, longitude)
if user_id not in markers:
# Если для данного user_id еще нет маркера, создаем его и добавляем в словарь
marker, path = create_marker(latitude, longitude, user_id)
markers[user_id] = {'marker': marker, 'path': path}
# play_sound('1.mp3')
marker_list.append(user_id)
update_combobox()
else:
# Если маркер уже существует, обновляем его координаты
marker, path = markers[user_id]['marker'], markers[user_id]['path']
if not flag_stop_update_position:
map_widget.set_position(latitude, longitude)
# flag_stop_update_position = True
marker.set_position(latitude, longitude)
# path.add_position(latitude, longitude)
except FileNotFoundError:
print("File 'data.json' not found.")
pass
except json.JSONDecodeError:
print("JSONDecodeError: File 'data.json' is empty or contains invalid JSON data.")
pass
except KeyError as e:
print(f"KeyError: Missing key {e} in 'data.json'.")
pass
except Exception as e:
print(f"An unexpected error occurred: {e}")
pass
root.after(2000, update_map)
# Инициализируем последние координаты как (0, 0)
update_map.last_coordinates = (0, 0)
def remove_marker_and_path(marker):
if marker.text in markers:
# Удаляем маркер и связанный с ним путь
marker_info = markers.pop(marker.text)
marker_info['marker'].delete()
marker_info['path'].delete()
marker_list.remove(marker.text)
update_combobox()
def play_sound(file):
pygame.mixer.init()
audio_file_path = file
# Воспроизводим звук
pygame.mixer.music.load(audio_file_path)
pygame.mixer.music.play()
def button1_clicked(event):
map_widget.set_tile_server("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png")
def button2_clicked(event):
map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=m&hl=ru&x={x}&y={y}&z={z}&s=Ga", max_zoom=22)
def button3_clicked(event):
map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga", max_zoom=22)
def clear_entry():
textField.delete(0, END) # удаление введенного текста
def get_address_and_parsing_it(event):
global msg
global create_markers_lat
global create_markers_lon
global marker_id
global marker_text
tf_get = msg.get()
if tf_get != "":
try:
adr = tkintermapview.convert_address_to_coordinates(tf_get)
create_markers_lat = adr[0]
create_markers_lon = adr[1]
marker_text = tf_get
marker_id = tf_get
marker_id = marker_id.replace(" ", "")
marker_id = marker_id.replace(",", "_")
print(f"marker_text: {marker_text}")
print(f"marker_id: {marker_id}")
print(adr[0])
print(adr[1])
update_object_map()
# create_object_marker(create_markers_lat, create_markers_lon, marker_id, marker_text)
# new_marker = map_widget.set_marker(adr[0], adr[1], text=f"{tf_get}", command=test)
# map_widget.set_position(adr[0], adr[1])
# map_widget.set_zoom(22)
# new_marker.set_position(adr[0], adr[1])
except:
functions.message("Адрес не найден!")
clear_entry()
textField.insert(END, "Харьков, ")
def create_object_marker(latitude, longitude, user_id, text):
# Создаем маркер
marker = map_widget.set_marker(latitude, longitude, marker_color_outside="red", marker_color_circle="purple",
command=test)
marker.set_text(text)
# Создаем путь и добавляем первую позицию
# path = map_widget.set_path([marker.position, (latitude, longitude)])
# path.add_position(latitude, longitude)
# Возвращаем маркер и путь
return marker # , path
def update_object_map():
global create_markers_lat
global create_markers_lon
global marker_id
global marker_text
try:
latitude = create_markers_lat
longitude = create_markers_lon
object_id = marker_text
marker_text = marker_text
if object_id not in object_markers:
# Если для данного user_id еще нет маркера, создаем его и добавляем в словарь
marker = create_object_marker(latitude, longitude, object_id, marker_text)
object_markers[object_id] = {'marker': marker}
map_widget.set_position(latitude, longitude)
marker_list.append(object_id)
else:
# Если маркер уже существует, обновляем его координаты
marker = object_markers[object_id]['marker']
map_widget.set_position(latitude, longitude)
marker.set_position(latitude, longitude)
# path.add_position(latitude, longitude)
except Exception as e:
print(f"An unexpected error occurred in update_object_map: {e}")
pass
def test(marker):
global if_click_to_marker
global selected_markers_lat
global selected_markers_lon
if if_click_to_marker == False:
def get_route(start_coords, end_coords):
# Запрос маршрута через сервис OpenRouteService
url = f"https://api.openrouteservice.org/v2/directions/driving-car?api_key=MY_API_KEY&start={start_coords[1]},{start_coords[0]}&end={end_coords[1]},{end_coords[0]}"
headers = {"Accept": "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"}
response = requests.get(url, headers=headers)
data = response.json()
return data
def display_route(route):
# Отображаем маршрут на карте
if 'features' in route and len(route['features']) > 0:
coordinates = route['features'][0]['geometry']['coordinates']
points = [(coord[1], coord[0]) for coord in coordinates]
map_widget.set_path(points)
print(f"marker clicked!")
latitude, longitude = marker.position # Получаем координаты маркера
latitude_str = str(latitude)
longitude_str = str(longitude)
coords_str = f"Latitude: {latitude_str}, Longitude: {longitude_str}"
print(f"Coordinates: {coords_str}")
start_coords = (selected_markers_lat, selected_markers_lon)
end_coords = (latitude, longitude)
# Получаем маршрут от начальной до конечной точки
route = get_route(start_coords, end_coords)
object_markers[marker_text]['path'] = route
# Отображаем маршрут на карте
display_route(route)
map_widget.set_position(start_coords[0], start_coords[1])
if_click_to_marker = True
else:
if marker.text in object_markers:
# Удаляем маркер и связанный с ним путь
marker_info = object_markers.pop(marker.text)
marker_info['marker'].delete()
marker_info['path'].delete()
if_click_to_marker = False
marker_list.remove(marker.text)
def update_combobox():
# Мы можем обновлять список в соответствии с определенными условиями или данными.
new_values = marker_list # Новые значения списка
combobox['values'] = new_values # Обновляем список в Combobox
def handle_selection_combobox(event):
global selected_markers_lat
global selected_markers_lon
# Получаем выбранный ключ (идентификатор пользователя) из Combobox
selected_user_id = combobox.get()
# Проверяем, что выбран какой-то элемент из Combobox
if selected_user_id:
# Проверяем, есть ли выбранный пользователь в словаре markers
if selected_user_id in markers:
# Получаем маркер для выбранного пользователя
marker_info = markers[selected_user_id]
# Получаем координаты из маркера
latitude, longitude = marker_info['marker'].position
selected_markers_lat = latitude
selected_markers_lon = longitude
# Теперь у вас есть координаты (широта и долгота) для выбранного пользователя
print(f"Координаты для пользователя {selected_user_id}: Широта - {latitude}, Долгота - {longitude}")
else:
print(f"Маркер для пользователя {selected_user_id} не найден.")
else:
print("Ничего не выбрано в Combobox.")
# Создаем три кнопки
button1 = tkinter.Button(root, text="OSM maps")
button1.pack(side=tkinter.LEFT, padx=5, pady=5, anchor='s')
button1.bind("<Button-1>", button1_clicked)
button2 = tkinter.Button(root, text="Google maps")
button2.pack(side=tkinter.LEFT, padx=5, pady=5, anchor='s')
button2.bind("<Button-1>", button2_clicked)
button3 = tkinter.Button(root, text="Google satellite")
button3.pack(side=tkinter.LEFT, padx=5, pady=5, anchor='s')
button3.bind("<Button-1>", button3_clicked)
textField = tkinter.Entry(root, background="GRAY", textvariable=msg)
textField.pack(side=tkinter.LEFT, padx=5, pady=7, fill="x", expand=True, anchor='s')
textField['font'] = 20
textField.insert(0, "Харьков, ")
# Привязываем обработчик к событию вставки (<<Control-v>>)
textField.bind('<Control-v>', on_paste)
button4 = tkinter.Button(root, text="Найти адрес")
button4.pack(side=tkinter.RIGHT, padx=5, pady=5, anchor='s')
button4.bind("<Button-1>", get_address_and_parsing_it)
combobox = ttk.Combobox(root, values=marker_list, textvariable=marker_list)
combobox.pack(side=tkinter.RIGHT, padx=5, pady=5, anchor='n')
# Привязываем функцию к событию выбора элемента Combobox
combobox.bind("<<ComboboxSelected>>", handle_selection_combobox)
# Привязываем функцию on_closing к событию закрытия окна
root.protocol("WM_DELETE_WINDOW", on_closing)
root.after(1000, update_map)
root.mainloop()