Как изменять размеры 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 шт):

Автор решения: FoxyHopper Dev

Чтобы реализовать изменение размеров root-элемента в зависимости от размеров окна, вы можете использовать обработчик событий изменения размеров окна. Вам нужно будет обновить размеры root при изменении размеров окна, чтобы они соответствовали новым размерам.

Вот как вы можете модифицировать ваш код:

  1. Добавьте функцию для обновления размеров root:

    def update_root_size(event): root.geometry(f"{event.width}x{event.height}")

  2. Привяжите эту функцию к событию изменения размеров окна:

    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()
→ Ссылка
Автор решения: Alexander Stepanenko

Решение было найдено, благодаря Сергей Кох. Достаточно установить для 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)
→ Ссылка