Улучши этот код.
1. Сделай меню Файл - а в нём Выход (из программы).
2. Также меню Настройки - перенеси в него галочку Сворачивать программу в трей...
3. Меню Справка - О программе (версия программы в формате 2024-oct-30).

import wx
import wx.adv
import wx.lib.buttons as buttons
import requests
import re
import os
import json

PROXY_FILE = 'proxies.json'
SETTINGS_FILE = 'settings.json'

def get_ip_and_isp(proxy_data=None, use_proxy=False):
    try:
        proxies = None
        if use_proxy and proxy_data:
            protocol = proxy_data.get('protocol', 'socks5')
            server_port = proxy_data['server_port']
            username = proxy_data.get('username')
            password = proxy_data.get('password')
            proxy_url = f"{protocol}://{server_port}"
            if username and password:
                proxy_url = f"{protocol}://{username}:{password}@{server_port}"
            proxies = {
                'http': proxy_url,
                'https': proxy_url,
            }
        response = requests.get('https://ipinfo.io/json', proxies=proxies)
        data = response.json()
        ip = data.get('ip', 'N/A')
        org = data.get('org', 'N/A')

        # Извлечение имени провайдера без AS-номера
        if org.startswith('AS'):
            isp = ' '.join(org.split(' ')[1:])
        else:
            isp = org

        # Удаление юридических форм (с учётом регистра и возможных кавычек)
        legal_forms = ['PJSC', 'LLC', 'JSC', 'OJSC', 'АО', 'ОАО', 'ЗАО', 'ООО', 'ПАО']
        pattern = r'\b(' + '|'.join(legal_forms) + r')\b'
        isp = re.sub(pattern, '', isp, flags=re.IGNORECASE).strip()

        # Удаление лишних символов, таких как кавычки или специальные символы
        isp = isp.replace('"', '').replace('«', '').replace('»', '').strip()

        # Удаление лишних пробелов
        isp = ' '.join(isp.split())

        return ip, isp
    except Exception as e:
        return 'Ошибка', 'Ошибка'

class MyTaskBarIcon(wx.adv.TaskBarIcon):
    def __init__(self, frame):
        super(MyTaskBarIcon, self).__init__()
        self.frame = frame
        # Установка иконки
        self.SetIcon(frame.icon, 'Моё IP и ISP')
        # Привязка событий
        self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.OnTaskBarLeftClick)
        self.Bind(wx.adv.EVT_TASKBAR_RIGHT_DOWN, self.OnTaskBarRightClick)

    def CreatePopupMenu(self):
        menu = wx.Menu()
        show_item = menu.Append(wx.ID_ANY, 'Показать')
        exit_item = menu.Append(wx.ID_EXIT, 'Выход')
        self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, show_item)
        self.Bind(wx.EVT_MENU, self.OnTaskBarClose, exit_item)
        return menu

    def OnTaskBarLeftClick(self, event):
        if self.frame.IsIconized():
            self.frame.Iconize(False)
        if not self.frame.IsShown():
            self.frame.Show()
        self.frame.Raise()

    def OnTaskBarRightClick(self, event):
        # Создание всплывающего меню
        self.PopupMenu(self.CreatePopupMenu())

    def OnTaskBarActivate(self, event):
        self.OnTaskBarLeftClick(None)

    def OnTaskBarClose(self, event):
        self.RemoveIcon()
        self.frame.taskbar_icon.Destroy()
        self.frame.Close()

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Моё IP и ISP", size=(450, 500))
        self.SetBackgroundColour(wx.Colour(30, 30, 30))

        # Установка иконки
        icon_path = 'D:\\app_ip.ico'
        if os.path.exists(icon_path):
            self.icon = wx.Icon(icon_path, wx.BITMAP_TYPE_ICO)
            self.SetIcon(self.icon)
        else:
            self.icon = wx.Icon()

        panel = wx.Panel(self)
        panel.SetBackgroundColour(wx.Colour(30, 30, 30))

        vbox = wx.BoxSizer(wx.VERTICAL)

        # Поле для IP
        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        ip_label = wx.StaticText(panel, label="Мой IP:")
        ip_label.SetForegroundColour(wx.Colour(255, 255, 255))
        self.ip_text = wx.TextCtrl(panel, style=wx.TE_READONLY)
        self.ip_text.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.ip_text.SetForegroundColour(wx.Colour(255, 255, 255))
        hbox1.Add(ip_label, flag=wx.RIGHT, border=8)
        hbox1.Add(self.ip_text, proportion=1)

        # Поле для ISP
        hbox2 = wx.BoxSizer(wx.HORIZONTAL)
        isp_label = wx.StaticText(panel, label="Мой ISP:")
        isp_label.SetForegroundColour(wx.Colour(255, 255, 255))
        self.isp_text = wx.TextCtrl(panel, style=wx.TE_READONLY)
        self.isp_text.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.isp_text.SetForegroundColour(wx.Colour(255, 255, 255))
        hbox2.Add(isp_label, flag=wx.RIGHT, border=8)
        hbox2.Add(self.isp_text, proportion=1)

        # Флажок "Использовать прокси"
        self.use_proxy_checkbox = wx.CheckBox(panel, label="Использовать прокси")
        self.use_proxy_checkbox.SetForegroundColour(wx.Colour(255, 255, 255))
        self.use_proxy_checkbox.SetValue(False)  # По умолчанию прокси не используется

        # Флажок "Сворачивать приложение в трей при нажатии на кнопку закрытия"
        self.minimize_to_tray_checkbox = wx.CheckBox(panel, label="Сворачивать приложение в трей при нажатии на кнопку закрытия")
        self.minimize_to_tray_checkbox.SetForegroundColour(wx.Colour(255, 255, 255))
        self.minimize_to_tray_checkbox.SetValue(False)

        # Выбор сохранённого прокси
        hbox3 = wx.BoxSizer(wx.HORIZONTAL)
        saved_proxy_label = wx.StaticText(panel, label="Сохранённые прокси:")
        saved_proxy_label.SetForegroundColour(wx.Colour(255, 255, 255))
        self.saved_proxy_choice = wx.ListBox(panel)
        self.saved_proxy_choice.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.saved_proxy_choice.SetForegroundColour(wx.Colour(255, 255, 255))
        hbox3.Add(saved_proxy_label, flag=wx.RIGHT, border=8)
        hbox3.Add(self.saved_proxy_choice, proportion=1)

        # Кнопки управления сохранёнными прокси
        hbox3_buttons = wx.BoxSizer(wx.HORIZONTAL)
        self.delete_proxy_button = buttons.GenButton(panel, label="Удалить выбранный прокси")
        self.delete_proxy_button.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.delete_proxy_button.SetForegroundColour(wx.Colour(255, 255, 255))
        self.delete_proxy_button.Bind(wx.EVT_BUTTON, self.on_delete_proxy)
        hbox3_buttons.Add(self.delete_proxy_button, flag=wx.RIGHT, border=10)

        # Поле для ввода нового прокси
        hbox4 = wx.BoxSizer(wx.HORIZONTAL)
        new_proxy_label = wx.StaticText(panel, label="Добавить прокси:")
        new_proxy_label.SetForegroundColour(wx.Colour(255, 255, 255))
        self.new_proxy_text = wx.TextCtrl(panel)
        self.new_proxy_text.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.new_proxy_text.SetForegroundColour(wx.Colour(255, 255, 255))
        hbox4.Add(new_proxy_label, flag=wx.RIGHT, border=8)
        hbox4.Add(self.new_proxy_text, proportion=1)

        # Кнопка для добавления нового прокси
        hbox4_buttons = wx.BoxSizer(wx.HORIZONTAL)
        self.add_proxy_button = buttons.GenButton(panel, label="Сохранить прокси")
        self.add_proxy_button.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.add_proxy_button.SetForegroundColour(wx.Colour(255, 255, 255))
        self.add_proxy_button.Bind(wx.EVT_BUTTON, self.on_add_proxy)
        hbox4_buttons.Add(self.add_proxy_button, flag=wx.RIGHT, border=10)

        # Кнопка "Получить IP и ISP"
        self.get_button = buttons.GenButton(panel, label="Получить IP и ISP")
        self.get_button.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.get_button.SetForegroundColour(wx.Colour(255, 255, 255))
        self.get_button.Bind(wx.EVT_BUTTON, self.on_get_button)
        self.get_button.Bind(wx.EVT_ENTER_WINDOW, self.on_button_hover)
        self.get_button.Bind(wx.EVT_LEAVE_WINDOW, self.on_button_leave)

        vbox.Add(hbox1, flag=wx.EXPAND|wx.ALL, border=10)
        vbox.Add(hbox2, flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=10)
        vbox.Add(self.use_proxy_checkbox, flag=wx.LEFT|wx.TOP, border=15)
        vbox.Add(self.minimize_to_tray_checkbox, flag=wx.LEFT|wx.TOP, border=15)
        vbox.Add(wx.StaticLine(panel), flag=wx.EXPAND|wx.ALL, border=5)
        vbox.Add(hbox3, flag=wx.EXPAND|wx.ALL, border=10)
        vbox.Add(hbox3_buttons, flag=wx.ALIGN_RIGHT|wx.RIGHT, border=20)
        vbox.Add(wx.StaticLine(panel), flag=wx.EXPAND|wx.ALL, border=5)
        vbox.Add(hbox4, flag=wx.EXPAND|wx.ALL, border=10)
        vbox.Add(hbox4_buttons, flag=wx.ALIGN_RIGHT|wx.RIGHT, border=20)
        vbox.Add(self.get_button, flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=20)

        panel.SetSizer(vbox)

        # Загрузка сохранённых прокси и настроек
        self.load_saved_proxies()
        self.load_settings()

        # Создание TaskBarIcon
        self.taskbar_icon = MyTaskBarIcon(self)

        # Обработка события закрытия окна
        self.Bind(wx.EVT_CLOSE, self.on_close)

    def on_close(self, event):
        # Сохранение настроек
        self.save_settings()
        if self.minimize_to_tray_checkbox.GetValue():
            self.Hide()
        else:
            # Удаляем иконку из трея и закрываем приложение
            self.taskbar_icon.RemoveIcon()
            self.taskbar_icon.Destroy()
            self.Destroy()

    def load_saved_proxies(self):
        self.saved_proxies = []
        if os.path.exists(PROXY_FILE):
            with open(PROXY_FILE, 'r', encoding='utf-8') as f:
                self.saved_proxies = json.load(f)
                self.update_saved_proxy_list()

    def save_proxies(self):
        with open(PROXY_FILE, 'w', encoding='utf-8') as f:
            json.dump(self.saved_proxies, f, ensure_ascii=False, indent=4)

    def update_saved_proxy_list(self):
        self.saved_proxy_choice.Clear()
        for proxy in self.saved_proxies:
            display_text = f"{proxy['protocol']}://{proxy['server_port']}"
            if proxy.get('username') and proxy.get('password'):
                display_text += f" ({proxy['username']}:{proxy['password']})"
            self.saved_proxy_choice.Append(display_text)

    def load_settings(self):
        if os.path.exists(SETTINGS_FILE):
            with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
                settings = json.load(f)
                use_proxy = settings.get('use_proxy', False)
                minimize_to_tray = settings.get('minimize_to_tray', False)
                selected_proxy_index = settings.get('selected_proxy_index', -1)

                self.use_proxy_checkbox.SetValue(use_proxy)
                self.minimize_to_tray_checkbox.SetValue(minimize_to_tray)
                if 0 <= selected_proxy_index < len(self.saved_proxies):
                    self.saved_proxy_choice.SetSelection(selected_proxy_index)
        else:
            # Если файл настроек не существует, устанавливаем значения по умолчанию
            self.use_proxy_checkbox.SetValue(False)
            self.minimize_to_tray_checkbox.SetValue(False)

    def save_settings(self):
        settings = {
            'use_proxy': self.use_proxy_checkbox.GetValue(),
            'minimize_to_tray': self.minimize_to_tray_checkbox.GetValue(),
            'selected_proxy_index': self.saved_proxy_choice.GetSelection(),
        }
        with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
            json.dump(settings, f, ensure_ascii=False, indent=4)

    def on_get_button(self, event):
        use_proxy = self.use_proxy_checkbox.GetValue()
        selected_index = self.saved_proxy_choice.GetSelection()
        if use_proxy and selected_index != wx.NOT_FOUND:
            proxy_data = self.saved_proxies[selected_index]
        else:
            proxy_data = None
        ip, isp = get_ip_and_isp(proxy_data, use_proxy)
        self.ip_text.SetValue(ip)
        self.isp_text.SetValue(isp)

    def on_add_proxy(self, event):
        proxy_input = self.new_proxy_text.GetValue().strip()
        if proxy_input:
            # Ожидаемый формат: protocol://server:port (username:password)
            parts = proxy_input.split()
            if len(parts) == 1 or len(parts) == 2:
                proxy_url = parts[0]
                auth = parts[1] if len(parts) == 2 else ''
                if '://' in proxy_url:
                    protocol, server_port = proxy_url.split('://', 1)
                else:
                    protocol = 'socks5'  # По умолчанию
                    server_port = proxy_url

                proxy_data = {'protocol': protocol, 'server_port': server_port}
                if auth:
                    if ':' in auth:
                        username, password = auth.split(':', 1)
                        proxy_data['username'] = username
                        proxy_data['password'] = password
                    else:
                        proxy_data['username'] = auth
                        proxy_data['password'] = ''

                # Добавляем прокси в список и сохраняем
                self.saved_proxies.append(proxy_data)
                self.save_proxies()
                self.update_saved_proxy_list()
                self.new_proxy_text.SetValue('')
            else:
                wx.MessageBox("Некорректный формат ввода прокси.", "Ошибка", wx.OK | wx.ICON_ERROR)
        else:
            wx.MessageBox("Поле для ввода прокси пустое.", "Ошибка", wx.OK | wx.ICON_ERROR)

    def on_delete_proxy(self, event):
        selected_index = self.saved_proxy_choice.GetSelection()
        if selected_index != wx.NOT_FOUND:
            del self.saved_proxies[selected_index]
            self.save_proxies()
            self.update_saved_proxy_list()
        else:
            wx.MessageBox("Не выбран прокси для удаления.", "Ошибка", wx.OK | wx.ICON_ERROR)

    def on_button_hover(self, event):
        self.get_button.SetBackgroundColour(wx.Colour(70, 70, 70))
        self.get_button.SetForegroundColour(wx.Colour(255, 255, 255))
        self.get_button.Refresh()
        event.Skip()

    def on_button_leave(self, event):
        self.get_button.SetBackgroundColour(wx.Colour(50, 50, 50))
        self.get_button.SetForegroundColour(wx.Colour(255, 255, 255))
        self.get_button.Refresh()
        event.Skip()

class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame()
        frame.Show()
        return True

if __name__ == "__main__":
    app = MyApp()
    app.MainLoop()