Как изменять размеры root-элемента в зависимости от размеров окна?
Есть GUI на tkinter с картой. Выставлен начальный размер 800 на 600. Но когда я разворачиваю окно на весь экран - карта остается в прежнем размере. Как заставить сам root разворачиваться вместе с окном?
Код:
import json
import tkinter
import tkintermapview
# Определение дефолтных значений
DEFAULT_LATITUDE = 0.0
DEFAULT_LONGITUDE = 0.0
DEFAULT_USER_ID = "2000"
root = tkinter.Tk()
root.geometry(f"{800}x{600}")
root.title("Finder Alert Button Map")
root.iconbitmap('ic_launcher_playstore.ico')
# Флаг для отслеживания открытого информационного окна
info_window_open = 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 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
map_widget = tkintermapview.TkinterMapView(root, width=800, height=600, corner_radius=0)
map_widget.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
map_widget.set_zoom(15)
map_widget.add_right_click_menu_command(label="Get address",
command=get_address,
pass_coords=True)
markers = {} # Словарь для отслеживания маркеров по user_id
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, 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"]
# Проверяем, изменились ли координаты
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}
else:
# Если маркер уже существует, обновляем его координаты
marker, path = markers[user_id]['marker'], markers[user_id]['path']
map_widget.set_position(latitude, longitude)
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()
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)
# Создаем две кнопки
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)
# Привязываем функцию on_closing к событию закрытия окна
root.protocol("WM_DELETE_WINDOW", on_closing)
root.after(1000, update_map)
root.mainloop()
Ответы (2 шт):
Чтобы реализовать изменение размеров root-элемента в зависимости от размеров окна, вы можете использовать обработчик событий изменения размеров окна. Вам нужно будет обновить размеры root при изменении размеров окна, чтобы они соответствовали новым размерам.
Вот как вы можете модифицировать ваш код:
Добавьте функцию для обновления размеров
root:def update_root_size(event): root.geometry(f"{event.width}x{event.height}")
Привяжите эту функцию к событию изменения размеров окна:
root.bind("", update_root_size)
Полный код с изменениями:
def update_root_size(event):
root.geometry(f"{event.width}x{event.height}")
root.bind("<Configure>", update_root_size)
root.mainloop()
Теперь, когда вы изменяете размеры окна, update_root_size будет вызываться, и root будет обновляться в соответствии с новыми размерами окна.
вот полный код с изменением:
import json
import tkinter
import tkintermapview
DEFAULT_LATITUDE = 0.0
DEFAULT_LONGITUDE = 0.0
DEFAULT_USER_ID = "2000"
root = tkinter.Tk()
root.geometry(f"{800}x{600}")
root.title("Finder Alert Button Map")
root.iconbitmap('ic_launcher_playstore.ico')
info_window_open = False
def get_address(coords):
global info_window_open
if not info_window_open:
adr = tkintermapview.convert_coordinates_to_address(coords[0], coords[1])
show_info(adr)
info_window_open = True
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 update_root_size(event):
root.geometry(f"{event.width}x{event.height}")
root.bind("<Configure>", update_root_size)
map_widget = tkintermapview.TkinterMapView(root, width=800, height=600, corner_radius=0)
map_widget.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
map_widget.set_zoom(15)
map_widget.add_right_click_menu_command(label="Get address",
command=get_address,
pass_coords=True)
markers = {}
def write_default_values_to_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, 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"]
if (latitude, longitude) != update_map.last_coordinates:
update_map.last_coordinates = (latitude, longitude)
if user_id not in markers:
marker, path = create_marker(latitude, longitude, user_id)
markers[user_id] = {'marker': marker, 'path': path}
else:
marker, path = markers[user_id]['marker'], markers[user_id]['path']
map_widget.set_position(latitude, longitude)
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)
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()
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)
root.protocol("WM_DELETE_WINDOW", on_closing)
root.after(1000, update_map)
root.mainloop()
Решение было найдено, благодаря Сергей Кох. Достаточно установить для map_widget параметры relwidth=1.0 и relheight=1.0:
map_widget.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER, relwidth=1.0, relheight=1.0)