максимально оптимизимруй приложение даже для маленьких экранов
оптимизируй всё что только можно
также жобавь всяких прикольных фич

СДЕЛАЙ ПОДРОБНЫЙ АНАЛИЗ КОДА ИСПРАВЬ ВСЕ ОШИБКИю ТЩАТЕЛЬНО ПРОВЕРЬ ЛОГИКУ КОДА И ИСПРАВЬ ЛОГИЧЕСКИЕ ОШИБКИ

import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import matplotlib
import numpy as np
import pandas as pd
import mplcursors  # Для интерактивных подсказок
from scipy.ndimage import gaussian_filter  # Для сглаживания данных
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D  # Для 3D графиков
from matplotlib import cm, colors  # Для управления цветовыми картами

# Используем встроенный Tkinter бэкэнд
matplotlib.use('TkAgg')

class ContourMapApp:
    def __init__(self, master):
        self.master = master
        self.master.title("Контурная Карта")
        self.master.geometry("1600x1000")

        # Инициализация основных переменных
        self.z_original = None  # Оригинальные данные
        self.z = None  # Обработанные данные
        self.x = None
        self.y = None
        self.colorbar = None
        self.current_style = 'ggplot'

        # Инициализация Tkinter переменных
        self.plot_type = tk.StringVar(value='Filled Contours')  # Тип отображения
        self.log_scale = tk.BooleanVar(value=False)  # Логарифмическая шкала
        self.interp_method = tk.StringVar(value='None')  # Метод интерполяции
        self.smooth_var = tk.BooleanVar(value=False)  # Сглаживание данных
        self.smooth_sigma = tk.DoubleVar(value=1.0)  # Параметр сглаживания
        self.color_norm = tk.StringVar(value='linear')  # Нормализация цвета
        self.style = tk.StringVar(value='ggplot')  # Стиль Matplotlib
        self.grid_var = tk.BooleanVar(value=True)  # Отображение сетки

        # Инициализация переменной для статусной панели
        self.status = tk.StringVar()
        self.status.set("Готово")

        # Создание верхней панели с кнопками и настройками
        self.create_top_frame()

        # Создание области для графика
        self.create_plot_area()

        # Добавление меню для дополнительных функций
        self.create_menu()

        # Создание статусной панели
        self.create_status_bar()

    def create_top_frame(self):
        self.top_frame = tk.Frame(self.master)
        self.top_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)

        # Кнопка загрузки файла
        self.load_button = tk.Button(self.top_frame, text="Загрузить Файл", command=self.load_file)
        self.load_button.pack(side=tk.LEFT, padx=5)

        # Выбор стиля Matplotlib
        self.style_label = tk.Label(self.top_frame, text="Стиль Графика:")
        self.style_label.pack(side=tk.LEFT, padx=5)

        self.style_menu = ttk.Combobox(
            self.top_frame,
            textvariable=self.style,
            values=sorted(m for m in plt.style.available),
            state='readonly',
            width=15
        )
        self.style_menu.pack(side=tk.LEFT, padx=5)
        self.style_menu.bind("<<ComboboxSelected>>", lambda e: self.update_style())

        # Выбор типа отображения
        self.plot_type_label = tk.Label(self.top_frame, text="Тип Отображения:")
        self.plot_type_label.pack(side=tk.LEFT, padx=5)

        self.plot_type_menu = ttk.Combobox(
            self.top_frame,
            textvariable=self.plot_type,
            values=['Filled Contours', 'Contour Lines', '3D Surface'],
            state='readonly',
            width=15
        )
        self.plot_type_menu.pack(side=tk.LEFT, padx=5)
        self.plot_type_menu.bind("<<ComboboxSelected>>", lambda e: self.update_plot())

        # Выбор цветовой карты
        self.colormap_label = tk.Label(self.top_frame, text="Цветовая карта:")
        self.colormap_label.pack(side=tk.LEFT, padx=5)

        self.colormap_var = tk.StringVar(value='viridis')
        self.colormap_menu = ttk.Combobox(
            self.top_frame,
            textvariable=self.colormap_var,
            values=sorted(m for m in plt.colormaps()),
            state='readonly',
            width=15
        )
        self.colormap_menu.pack(side=tk.LEFT, padx=5)
        self.colormap_menu.bind("<<ComboboxSelected>>", lambda e: self.update_plot())

        # Настройка уровней контуров
        self.levels_label = tk.Label(self.top_frame, text="Уровни:")
        self.levels_label.pack(side=tk.LEFT, padx=5)

        self.levels_entry = tk.Entry(self.top_frame, width=10)
        self.levels_entry.insert(0, "10")
        self.levels_entry.pack(side=tk.LEFT, padx=5)
        self.levels_entry.bind("<Return>", lambda e: self.update_plot())

        # Поля для заголовка и меток осей
        self.title_label = tk.Label(self.top_frame, text="Заголовок:")
        self.title_label.pack(side=tk.LEFT, padx=5)

        self.title_entry = tk.Entry(self.top_frame, width=15)
        self.title_entry.insert(0, "Контурная Карта")
        self.title_entry.pack(side=tk.LEFT, padx=5)
        self.title_entry.bind("<Return>", lambda e: self.update_plot())

        self.xlabel_label = tk.Label(self.top_frame, text="Ось X:")
        self.xlabel_label.pack(side=tk.LEFT, padx=5)

        self.xlabel_entry = tk.Entry(self.top_frame, width=10)
        self.xlabel_entry.insert(0, "X")
        self.xlabel_entry.pack(side=tk.LEFT, padx=5)
        self.xlabel_entry.bind("<Return>", lambda e: self.update_plot())

        self.ylabel_label = tk.Label(self.top_frame, text="Ось Y:")
        self.ylabel_label.pack(side=tk.LEFT, padx=5)

        self.ylabel_entry = tk.Entry(self.top_frame, width=10)
        self.ylabel_entry.insert(0, "Y")
        self.ylabel_entry.pack(side=tk.LEFT, padx=5)
        self.ylabel_entry.bind("<Return>", lambda e: self.update_plot())

        # Настройка отображения сетки
        self.grid_check = tk.Checkbutton(
            self.top_frame, text="Сетка", variable=self.grid_var, command=self.update_plot
        )
        self.grid_check.pack(side=tk.LEFT, padx=5)

        # Опция логарифмической шкалы
        self.log_check = tk.Checkbutton(
            self.top_frame, text="Лог. шкала Z", variable=self.log_scale, command=self.update_plot
        )
        self.log_check.pack(side=tk.LEFT, padx=5)

        # Настройка метода интерполяции
        self.interp_label = tk.Label(self.top_frame, text="Интерполяция:")
        self.interp_label.pack(side=tk.LEFT, padx=5)

        self.interp_menu = ttk.Combobox(
            self.top_frame,
            textvariable=self.interp_method,
            values=['None', 'nearest', 'linear', 'cubic'],
            state='readonly',
            width=10
        )
        self.interp_menu.pack(side=tk.LEFT, padx=5)
        self.interp_menu.bind("<<ComboboxSelected>>", lambda e: self.update_plot())

        # Опции сглаживания данных
        self.smooth_check = tk.Checkbutton(
            self.top_frame, text="Сгладить данные", variable=self.smooth_var, command=self.update_plot
        )
        self.smooth_check.pack(side=tk.LEFT, padx=5)

        self.smooth_label = tk.Label(self.top_frame, text="Sigma:")
        self.smooth_label.pack(side=tk.LEFT, padx=2)

        self.smooth_spinbox = tk.Spinbox(
            self.top_frame, from_=0.1, to=10.0, increment=0.1, textvariable=self.smooth_sigma, width=5,
            command=self.update_plot
        )
        self.smooth_spinbox.pack(side=tk.LEFT, padx=2)
        self.smooth_spinbox.bind("<Return>", lambda e: self.update_plot())

        # Настройка ограничений осей
        self.xlim_label = tk.Label(self.top_frame, text="X пределы:")
        self.xlim_label.pack(side=tk.LEFT, padx=5)

        self.xmin_entry = tk.Entry(self.top_frame, width=7)
        self.xmin_entry.pack(side=tk.LEFT, padx=2)
        self.xmin_entry.insert(0, "")

        self.xmax_entry = tk.Entry(self.top_frame, width=7)
        self.xmax_entry.pack(side=tk.LEFT, padx=2)
        self.xmax_entry.insert(0, "")

        self.ylim_label = tk.Label(self.top_frame, text="Y пределы:")
        self.ylim_label.pack(side=tk.LEFT, padx=5)

        self.ymin_entry = tk.Entry(self.top_frame, width=7)
        self.ymin_entry.pack(side=tk.LEFT, padx=2)
        self.ymin_entry.insert(0, "")

        self.ymax_entry = tk.Entry(self.top_frame, width=7)
        self.ymax_entry.pack(side=tk.LEFT, padx=2)
        self.ymax_entry.insert(0, "")

        self.apply_limits_button = tk.Button(self.top_frame, text="Применить Ограничения", command=self.apply_limits)
        self.apply_limits_button.pack(side=tk.LEFT, padx=5)

        # Кнопка экспорта выбранных данных
        self.export_button = tk.Button(self.top_frame, text="Экспорт Выбранных Данных", command=self.export_selected_region)
        self.export_button.pack(side=tk.LEFT, padx=5)

        # Кнопка сохранения изображения с расширенными опциями
        self.save_button = tk.Button(self.top_frame, text="Сохранить Изображение", command=self.save_plot)
        self.save_button.pack(side=tk.RIGHT, padx=5)

    def create_plot_area(self):
        self.figure = plt.Figure(figsize=(14, 8))
        self.ax = self.figure.add_subplot(111)

        self.canvas = FigureCanvasTkAgg(self.figure, master=self.master)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # Добавление панели инструментов Matplotlib
        toolbar = matplotlib.backends.backend_tkagg.NavigationToolbar2Tk(self.canvas, self.master)
        toolbar.update()
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def create_menu(self):
        menubar = tk.Menu(self.master)
        self.master.config(menu=menubar)

        # Меню просмотра
        view_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Вид", menu=view_menu)
        view_menu.add_command(label="Сбросить Масштаб", command=self.reset_view)

        # Меню помощи
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Справка", menu=help_menu)
        help_menu.add_command(label="О программе", command=self.show_about)

    def create_status_bar(self):
        self.status_bar = tk.Label(self.master, textvariable=self.status, bd=1, relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

    def load_file(self):
        file_path = filedialog.askopenfilename(
            title="Выберите файл с данными",
            filetypes=(("CSV файлы", "*.csv"), ("Текстовые файлы", "*.txt"), ("Excel файлы", "*.xlsx"), ("Все файлы", "*.*"))
        )
        if not file_path:
            self.update_status("Загрузка файла отменена.")
            return

        try:
            # Автоматическое определение разделителя и формата файла
            if file_path.endswith('.xlsx'):
                df = pd.read_excel(file_path, header=None)
            else:
                df = pd.read_csv(file_path, header=None, sep=None, engine='python')

            self.z_original = df.values
            self.y, self.x = self.z_original.shape
            self.z = self.z_original.copy()
            self.update_status("Данные загружены успешно.")
            self.update_plot()
            self.display_statistics()
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось загрузить файл:\n{e}")
            self.update_status("Ошибка при загрузке файла.")

    def display_statistics(self):
        if self.z is None:
            return

        min_val = np.min(self.z)
        max_val = np.max(self.z)
        mean_val = np.mean(self.z)

        stats_message = f"Статистика данных:\nМинимум: {min_val}\nМаксимум: {max_val}\nСреднее: {mean_val:.2f}"
        messagebox.showinfo("Статистика Данныx", stats_message)
        self.update_status("Статистика отображена.")

    def plot_contour(self):
        if self.z is None:
            return

        plot_type = self.plot_type.get()
        levels = self.get_levels()
        cmap_name = self.colormap_var.get()
        show_grid = self.grid_var.get()
        log_scale = self.log_scale.get()
        interp_method = self.interp_method.get()
        smooth = self.smooth_var.get()
        sigma = self.smooth_sigma.get()
        norm_type = self.color_norm.get()

        # Установка заголовка и меток осей
        title = self.title_entry.get()
        xlabel = self.xlabel_entry.get()
        ylabel = self.ylabel_entry.get()

        x = np.arange(self.x)
        y = np.arange(self.y)

        # Настройка интерполяции
        if interp_method != 'None':
            from scipy.interpolate import griddata
            grid_x, grid_y = np.linspace(x.min(), x.max(), self.x * 2), np.linspace(y.min(), y.max(), self.y * 2)
            X, Y = np.meshgrid(grid_x, grid_y)
            Z = self.z_original.copy()
            try:
                self.z = griddata((x.flatten(), y.flatten()), self.z_original.flatten(),
                                  (X, Y), method=interp_method)
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось выполнить интерполяцию:\n{e}")
                self.update_status("Ошибка при интерполяции данных.")
                return
        else:
            X, Y = np.meshgrid(x, y)
            self.z = self.z_original.copy()

        # Применение логарифмической шкалы, если выбрано
        if log_scale:
            # Заменяем нули и отрицательные значения на минимально положительное для логарифма
            if np.any(self.z <= 0):
                min_positive = np.min(self.z[self.z > 0])
                self.z[self.z <= 0] = min_positive
            self.z = np.log(self.z)

        # Применение сглаживания, если выбрано
        if smooth:
            self.z = gaussian_filter(self.z, sigma=sigma)

        # Настройка нормализации цветовой карты
        if norm_type == 'log':
            norm = colors.LogNorm()
        else:
            norm = None

        if plot_type == '3D Surface':
            # Создание 3D осей
            ax = self.figure.add_subplot(111, projection='3d')
            if levels > 0:
                cmap = self.get_discrete_colormap(cmap_name, levels)
            else:
                cmap = cmap_name

            if norm is not None:
                surf = ax.plot_surface(X, Y, self.z, cmap=cmap, norm=norm, edgecolor='none')
            else:
                surf = ax.plot_surface(X, Y, self.z, cmap=cmap, edgecolor='none')
            self.colorbar = self.figure.colorbar(surf, ax=ax, shrink=0.5, aspect=10)
            ax.set_title(title)
            ax.set_xlabel(xlabel)
            ax.set_ylabel(ylabel)
            ax.set_zlabel('Z')
            ax.grid(show_grid)

            # Добавление интерактивных подсказок
            cursor = mplcursors.cursor(surf, hover=True)

            @cursor.connect("add")
            def on_add(sel):
                # Для 3D графика индексация немного сложнее
                # Используем nearest indexing
                x_val = X[int(sel.target.index / self.z.shape[1]), sel.target.index % self.z.shape[1]]
                y_val = Y[int(sel.target.index / self.z.shape[1]), sel.target.index % self.z.shape[1]]
                z_val = self.z[int(sel.target.index / self.z.shape[1]), sel.target.index % self.z.shape[1]]
                sel.annotation.set(text=f"x={x_val}, y={y_val}, z={z_val:.2f}")
        else:
            # Создание 2D осей
            ax = self.figure.add_subplot(111)
            if plot_type == 'Filled Contours':
                contour = ax.contourf(X, Y, self.z, levels=levels, cmap=cmap_name, norm=norm)
            elif plot_type == 'Contour Lines':
                contour = ax.contour(X, Y, self.z, levels=levels, cmap=cmap_name, norm=norm)
                ax.clabel(contour, inline=True, fontsize=8)
            else:
                contour = None

            if contour:
                self.colorbar = self.figure.colorbar(contour, ax=ax)
            ax.set_title(title)
            ax.set_xlabel(xlabel)
            ax.set_ylabel(ylabel)
            ax.grid(show_grid)

            # Добавление интерактивных подсказок
            if contour:
                cursor = mplcursors.cursor(contour, hover=True)

                @cursor.connect("add")
                def on_add(sel):
                    # Получение индекса для 2D графика
                    x_idx = int(sel.target.index / self.z.shape[1])
                    y_idx = sel.target.index % self.z.shape[1]
                    x_val = X[x_idx, y_idx]
                    y_val = Y[x_idx, y_idx]
                    z_val = self.z[x_idx, y_idx]
                    sel.annotation.set(text=f"x={x_val}, y={y_val}, z={z_val:.2f}")

        self.ax = ax

    def get_discrete_colormap(self, cmap_name, levels):
        """
        Создает дискретную цветовую карту с заданным количеством уровней.
        """
        cmap = cm.get_cmap(cmap_name, levels)
        return cmap

    def get_levels(self):
        try:
            levels = int(self.levels_entry.get())
            if levels <= 0:
                raise ValueError
        except ValueError:
            messagebox.showwarning(
                "Предупреждение",
                "Количество уровней должно быть положительным целым числом. Используется значение по умолчанию (10)."
            )
            levels = 10
            self.levels_entry.delete(0, tk.END)
            self.levels_entry.insert(0, "10")
        return levels

    def apply_limits(self):
        if self.z is None:
            messagebox.showwarning("Предупреждение", "Сначала загрузите данные.")
            return

        x_min = self.xmin_entry.get()
        x_max = self.xmax_entry.get()
        y_min = self.ymin_entry.get()
        y_max = self.ymax_entry.get()

        try:
            if x_min and x_max:
                x_min = float(x_min)
                x_max = float(x_max)
                self.ax.set_xlim(x_min, x_max)
            if y_min and y_max:
                y_min = float(y_min)
                y_max = float(y_max)
                self.ax.set_ylim(y_min, y_max)
            if isinstance(self.ax, Axes3D):
                # Для 3D графика можно добавить ограничение Z, если нужно
                pass  # Здесь можно добавить поля для Z если требуется

            self.canvas.draw()
            self.update_status("Ограничения осей применены.")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось применить ограничения осей:\n{e}")
            self.update_status("Ошибка при применении ограничений осей.")

    def update_plot(self):
        if self.z is None:
            return

        # Удаляем существующую colorbar, если она есть
        if self.colorbar:
            try:
                self.colorbar.remove()
            except:
                pass
            self.colorbar = None

        # Очищаем всю фигуру перед построением нового графика
        self.figure.clf()

        # Применяем выбранный стиль
        plt.style.use(self.style.get())

        # Строим контурную карту
        self.plot_contour()

        # Обновляем холст
        self.canvas.draw()
        self.update_status("График обновлён.")

    def save_plot(self):
        if self.z is None:
            messagebox.showwarning("Предупреждение", "Нет графика для сохранения.")
            return

        file_path = filedialog.asksaveasfilename(
            defaultextension=".png",
            filetypes=(
                ("PNG Image", "*.png"),
                ("JPEG Image", "*.jpg"),
                ("PDF Document", "*.pdf"),
                ("SVG Image", "*.svg"),
                ("All Files", "*.*")
            )
        )
        if not file_path:
            return

        try:
            self.figure.savefig(file_path)
            messagebox.showinfo("Успех", f"Изображение сохранено по пути:\n{file_path}")
            self.update_status(f"Изображение сохранено: {file_path}")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось сохранить изображение:\n{e}")
            self.update_status("Ошибка при сохранении изображения.")

    def reset_view(self):
        if self.z is None:
            return
        # Очистка всей фигуры и повторное построение графика
        self.figure.clf()
        self.colorbar = None
        self.update_plot()
        self.update_status("Масштаб сброшен.")

    def show_about(self):
        messagebox.showinfo(
            "О программе",
            (
                "Приложение для отображения контурных карт.\n"
                "Разработано на Python с использованием Tkinter и Matplotlib.\n\n"
                "Функции:\n"
                "- Загрузка данных из CSV, TXT и Excel файлов.\n"
                "- Выбор типа отображения: заполненные контуры, контурные линии, 3D поверхность.\n"
                "- Настройка цветовой карты, уровней контуров и нормализации.\n"
                "- Настройка заголовка и меток осей.\n"
                "- Отображение статистики данных.\n"
                "- Включение/отключение сетки.\n"
                "- Применение логарифмической шкалы к значениям Z.\n"
                "- Настройка ограничений осей X и Y.\n"
                "- Интерполяция данных.\n"
                "- Сглаживание данных.\n"
                "- Интерактивные подсказки при наведении курсора.\n"
                "- Экспорт выбранных данных.\n"
                "- Сохранение графика в различных форматах.\n"
                "- Выбор стиля графика."
            )
        )

    def update_style(self):
        selected_style = self.style.get()
        plt.style.use(selected_style)
        self.update_plot()
        self.update_status(f"Стиль графика изменён на: {selected_style}")

    def apply_transformations(self):
        # Применяем логарифмическую шкалу и сглаживание при загрузке данных
        if self.log_scale.get():
            Z = self.z_original.copy()
            if np.any(Z <= 0):
                min_positive = np.min(Z[Z > 0])
                Z[Z <= 0] = min_positive
            Z = np.log(Z)
            self.z = Z
        else:
            self.z = self.z_original.copy()

        if self.smooth_var.get():
            sigma = self.smooth_sigma.get()
            self.z = gaussian_filter(self.z, sigma=sigma)

    def update_status(self, message):
        self.status.set(message)
        self.master.update_idletasks()

    def export_selected_region(self):
        # Функция для экспорта выбранной области данных
        # Реализуется с использованием функционала Matplotlib для выбора области
        if self.z is None:
            messagebox.showwarning("Предупреждение", "Сначала загрузите данные.")
            return

        messagebox.showinfo("Информация", "Пожалуйста, выделите область на графике для экспорта.")

        try:
            from matplotlib.widgets import RectangleSelector

            def onselect(eclick, erelease):
                try:
                    x1, y1 = int(eclick.xdata), int(eclick.ydata)
                    x2, y2 = int(erelease.xdata), int(erelease.ydata)
                    xmin, xmax = sorted([x1, x2])
                    ymin, ymax = sorted([y1, y2])
                    # Проверка границ
                    xmin = max(xmin, 0)
                    xmax = min(xmax, self.x - 1)
                    ymin = max(ymin, 0)
                    ymax = min(ymax, self.y - 1)
                    region = self.z[ymin:ymax+1, xmin:xmax+1]
                    # Сохранение выбранной области
                    export_path = filedialog.asksaveasfilename(
                        defaultextension=".csv",
                        filetypes=(("CSV файлы", "*.csv"), ("Все файлы", "*.*")),
                        title="Сохранить выбранную область данных"
                    )
                    if export_path:
                        pd.DataFrame(region).to_csv(export_path, header=False, index=False)
                        messagebox.showinfo("Успех", f"Данные сохранены по пути:\n{export_path}")
                        self.update_status(f"Данные экспортированы: {export_path}")
                    else:
                        self.update_status("Экспорт данных отменён.")
                except Exception as e:
                    messagebox.showerror("Ошибка", f"Не удалось экспортировать выбранную область:\n{e}")
                    self.update_status("Ошибка при экспорте данных.")

            rs = RectangleSelector(self.ax, onselect, drawtype='box', useblit=True,
                                   button=[1],  # Только левая кнопка мыши
                                   minspanx=5, minspany=5,
                                   spancoords='pixels',
                                   interactive=True)
            self.update_status("Выделите область для экспорта.")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось экспортировать выбранную область:\n{e}")
            self.update_status("Ошибка при экспорте данных.")

def main():
    root = tk.Tk()
    app = ContourMapApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()