Advertisement
Maks_1

контактная книга

Jan 10th, 2025
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.41 KB | Software | 0 0
  1. import tkinter as tk
  2. from tkinter import ttk
  3. import re
  4.  
  5. # Хранение контактов
  6. contacts = []
  7.  
  8.  
  9. def validate_fio_input(char):
  10.     # Проверяем, что введённый символ соответствует правилам для ФИО (буквы, пробелы и дефисы)
  11.     return re.match(r"[А-Яа-яA-Za-z\s-]", char) is not None
  12.  
  13.  
  14. def validate_phone_input(char):
  15.     # Проверяем, что введённый символ является цифрой для номера телефона
  16.     return re.match(r"\d", char) is not None
  17.  
  18.  
  19. def format_phone_number(phone):
  20.     # Форматируем номер телефона
  21.     digits = re.sub(r'\D', '', phone)  # Удаляем все нецифровые символы из строки
  22.     # Проверяем, что номер состоит из 11 цифр и начинается с '7'
  23.     if len(digits) == 11 and digits.startswith("7"):
  24.         # Возвращаем номер в формате: +7(XXX) XXX-XX-XX
  25.         return f"+{digits[0]}({digits[1:4]}) {digits[4:7]}-{digits[7:9]}-{digits[9:11]}"
  26.     return phone  # Если номер не соответствует формату, возвращаем оригинальный
  27.  
  28.  
  29. def on_phone_input_change(*args):
  30.     # Обработчик изменения ввода номера телефона
  31.     phone = phone_number_var.get()  # Получаем текущее значение номера телефона
  32.     formatted_phone = format_phone_number(phone)  # Форматируем номер телефона
  33.     if formatted_phone != phone:  # Проверяем, изменился ли формат номера
  34.         phone_number_var.set(formatted_phone)  # Устанавливаем отформатированный номер
  35.         phone_number_entry.icursor(len(formatted_phone))  # Устанавливаем курсор в конец поля ввода
  36.  
  37.  
  38. def save_contacts_to_file(filename='contacts.txt'):
  39.     """Сохраняем контакты в текстовый файл."""
  40.     with open(filename, 'w', encoding='utf-8') as file:  # Открываем файл для записи
  41.         for contact in contacts:  # Проходим по всем контактам
  42.             # Записываем ФИО, телефон и категорию в файл, разделяя их запятой
  43.             file.write(f"{contact['ФИО']}, {contact['Телефон']}, {contact['Категория']}\n")
  44.     # Отображаем сообщение об успешном сохранении
  45.     display_message("Успех", f"Контакты сохранены в '{filename}'.")
  46.  
  47.  
  48. def load_contacts_from_file(filename='contacts.txt'):
  49.     """Загружаем контакты из текстового файла."""
  50.     global contacts  # Объявляем переменную contacts глобальной
  51.     try:
  52.         with open(filename, 'r', encoding='utf-8') as file:  # Открываем файл для чтения
  53.             contacts = []  # Очищаем текущий список контактов
  54.             for line in file:  # Считываем файл построчно
  55.                 if line.strip():  # Проверяем, что строка не пустая
  56.                     # Разделяем строку на ФИО, телефон и категорию
  57.                     fio, phone, category = line.strip().split(', ')
  58.                     contact = {"ФИО": fio, "Телефон": phone, "Категория": category}  # Создаем словарь контакта
  59.                     contacts.append(contact)  # Добавляем контакт в список
  60.             update_table()  # Обновляем отображение таблицы после загрузки контактов
  61.             display_message("Успех", "Контакты загружены из файла.")  # Сообщаем об успешной загрузке
  62.     except FileNotFoundError:
  63.         # Обрабатываем случай, когда файл не найден
  64.         display_message("Информация", "Файл не найден. Начинаем с пустого списка контактов.")
  65.     except Exception as e:
  66.         # Обрабатываем любые другие ошибки при загрузке
  67.         display_message("Ошибка", f"Ошибка при загрузке: {str(e)}", error=True)
  68.  
  69.  
  70. def save_contact():
  71.     first_name = FIO.get().strip()  # Получаем и очищаем введенное ФИО
  72.     phone = phone_number_var.get().strip()  # Получаем и очищаем введённый номер телефона
  73.     category = categories.get().strip()  # Получаем и очищаем введённую категорию
  74.  
  75.     # Проверка на наличие пустых полей
  76.     if not first_name or not phone or not category:
  77.         display_message("Ошибка", "Все поля должны быть заполнены.", error=True)  # Показываем ошибку
  78.         return
  79.  
  80.     # Проверка на уникальность ФИО в списке контактов
  81.     if any(contact['ФИО'] == first_name for contact in contacts):
  82.         display_message("Ошибка", "Контакт с таким ФИО уже существует.", error=True)
  83.         return
  84.  
  85.     # Создаем новый контакт и добавляем его в список
  86.     contact = {"ФИО": first_name.title(), "Телефон": phone, "Категория": category}
  87.     contacts.append(contact)  # Добавляем контакт в список
  88.     update_table()  # Обновляем отображение таблицы
  89.  
  90.     # Сохраняем контакты в текстовый файл
  91.     save_contacts_to_file()
  92.  
  93.     # Очищаем поля ввода для новых значений
  94.     FIO.delete(0, tk.END)
  95.     phone_number_var.set("")  # Сбрасываем номер телефона
  96.     categories.delete(0, tk.END)  # Очищаем поле категории
  97.     display_message("Успех", "Контакт сохранен.")  # Показываем сообщение об успешном сохранении
  98.  
  99.  
  100. def search_contact():
  101.     # Получаем текст из поля ввода и удаляем лишние пробелы
  102.     query = FIO.get().strip()
  103.     # Находим контакты, в которых ФИО соответствует введённому запросу (без учета регистра)
  104.     results = [contact for contact in contacts if query.lower() in contact["ФИО"].lower()]
  105.     # Обновляем таблицу с результатами поиска
  106.     update_table(results)
  107.  
  108.     # Если результаты поиска пустые, выводим сообщение пользователю
  109.     if not results:
  110.         display_message("Результаты поиска", "Контакты не найдены.")
  111.  
  112.  
  113. def update_table(filtered_contacts=None):
  114.     # Удаляем все текущие строки из таблицы
  115.     for row in tree.get_children():
  116.         tree.delete(row)
  117.  
  118.         # Выбираем, какие контакты отображать: отфильтрованные или все
  119.     to_display = filtered_contacts if filtered_contacts is not None else contacts
  120.  
  121.     # Вставляем каждую контактную запись в таблицу
  122.     for contact in to_display:
  123.         tree.insert("", "end", values=(contact["ФИО"], contact["Телефон"], contact["Категория"]))
  124.  
  125.  
  126. def delete_contact():
  127.     # Получаем выбранные элементы (контакты) из таблицы
  128.     selected_item = tree.selection()
  129.  
  130.     # Если есть выбраные элементы
  131.     if selected_item:
  132.         for item in selected_item:  # Проходим по всем выбранным элементам
  133.             index = tree.index(item)  # Получаем индекс выбранного элемента
  134.             contacts.pop(index)  # Удаляем контакт из списка по индексу
  135.         update_table()  # Обновляем таблицу после удаления
  136.         display_message("Успех", "Контакт удален.")  # Показываем сообщение об успешном удалении
  137.         save_contacts_to_file()  # Сохраняем обновлённый список контактов в файл
  138.     else:
  139.         # Если нет выбранных контактов, выводим ошибку
  140.         display_message("Ошибка", "Не выбран контакт для удаления.")
  141.  
  142. def edit_contact():
  143.     # Получаем выбранный элемент из таблицы
  144.     selected_item = tree.selection()
  145.  
  146.     # Проверяем, был ли выбран контакт
  147.     if selected_item:
  148.         index = tree.index(selected_item[0])  # Получаем индекс выбранного элемента
  149.         contact = contacts[index]  # Извлекаем данные выбранного контакта
  150.  
  151.         # Очищаем поля ввода перед загрузкой новых данных
  152.         FIO.delete(0, tk.END)  # Очищаем поле ФИО
  153.         FIO.insert(0, contact["ФИО"])  # Заполняем поле ФИО данными из контакта
  154.         phone_number_var.set(contact["Телефон"])  # Устанавливаем номер телефона
  155.         categories.delete(0, tk.END)  # Очищаем поле категорий
  156.         categories.insert(0, contact["Категория"])  # Заполняем поле категорий данными из контакта
  157.  
  158.         # Выводим сообщение о том, что контакт для редактирования загружен
  159.         display_message("Контакт для редактирования загружен.")
  160.     else:
  161.         # Если контакт не был выбран, выводим ошибку
  162.         display_message("Ошибка", "Не выбран контакт для редактирования.")
  163.  
  164.  
  165. def display_message(title, message, error=False):
  166.     # Активируем текстовое поле для отображения сообщения
  167.     message_area.config(state=tk.NORMAL)
  168.     message_area.delete(1.0, tk.END)  # Очищаем предыдущее сообщение
  169.     message_area.insert(tk.END, f"{title}: {message}\n")  # Вставляем новое сообщение
  170.     message_area.config(state=tk.DISABLED)  # Деактивируем текстовое поле, чтобы избежать изменений
  171.  
  172.  
  173. # Создаем главное окно приложения
  174. root = tk.Tk()
  175. root.title("Контактная книга")  # Устанавливаем заголовок окна
  176. root.geometry("400x600")  # Устанавливаем размеры окна
  177.  
  178. frame = ttk.Frame(root, padding="10")  # Создаем фрейм для размещения виджетов
  179. frame.grid(sticky=(tk.N, tk.E, tk.S, tk.W))  # Позволяем фрейму занимать доступное пространство
  180.  
  181. # Регистрация валидации для ввода
  182. vcmd_fio = (root.register(validate_fio_input), '%S')  # Валидация для поля ФИО
  183. vcmd_phone = (root.register(validate_phone_input), '%S')  # Валидация для поля телефона
  184.  
  185. # Создаем переменную для отслеживания ввода в поле телефона
  186. phone_number_var = tk.StringVar()  # Создаем переменную типа StringVar
  187. phone_number_var.trace_add("write", on_phone_input_change)  # Добавляем обработчик изменения текста
  188.  
  189. # Метки для полей ввода
  190. ttk.Label(frame, text="ФИО:").grid(row=0, column=0, sticky=tk.E, padx=5, pady=5)
  191. FIO = ttk.Entry(frame, validate='key', validatecommand=vcmd_fio)  # Поле ввода для ФИО с валидацией
  192. FIO.grid(row=0, column=1, sticky=tk.NE + tk.W, padx=5, pady=5)
  193.  
  194. ttk.Label(frame, text="Телефон:").grid(row=1, column=0, sticky=tk.E, padx=5, pady=5)
  195. phone_number_entry = ttk.Entry(frame, textvariable=phone_number_var, validate='key', validatecommand=vcmd_phone)
  196. phone_number_entry.grid(row=1, column=1, sticky=tk.NE + tk.W, padx=5, pady=5)
  197. phone_number_var.set("+7")  # Подсказка пользователю
  198.  
  199. ttk.Label(frame, text="Категория:").grid(row=2, column=0, sticky=tk.E, padx=5, pady=5)
  200. categories = ttk.Entry(frame)  # Поле ввода для категории контакта
  201. categories.grid(row=2, column=1, sticky=tk.NE + tk.W, padx=5, pady=5)
  202.  
  203. # Кнопки управления
  204. add_contact = ttk.Button(frame, text="Сохранить контакт", command=save_contact)  # Кнопка для сохранения контакта
  205. add_contact.grid(row=0, column=2, sticky=tk.NE, padx=5, pady=5)
  206.  
  207. delete_contact_button = ttk.Button(frame, text="Удалить контакт",
  208.                                    command=delete_contact)  # Кнопка для удаления контакта
  209. delete_contact_button.grid(row=1, column=2, sticky=tk.NE, padx=5, pady=5)
  210.  
  211. search_contact_button = ttk.Button(frame, text="Поиск контакта", command=search_contact)  # Кнопка для поиска контакта
  212. search_contact_button.grid(row=2, column=2, sticky=tk.NE, padx=5, pady=5)
  213.  
  214. edit_contact_button = ttk.Button(frame, text="Редактировать контакт",
  215.                                  command=edit_contact)  # Кнопка для редактирования контакта
  216. edit_contact_button.grid(row=3, column=2, sticky=tk.NE, padx=5, pady=5)
  217.  
  218. # Создаем таблицу для отображения контактов
  219. columns = ("ФИО", "Телефон", "Категория")  # Определяем колонки таблицы
  220. tree = ttk.Treeview(root, columns=columns, show='headings')  # Создаем виджет таблицы
  221. tree.heading("ФИО", text="ФИО")  # Устанавливаем заголовок для первой колонки
  222. tree.heading("Телефон", text="Телефон")  # Устанавливаем заголовок для второй колонки
  223. tree.heading("Категория", text="Категория")  # Устанавливаем заголовок для третьей колонки
  224.  
  225. # Установка ширины колонок
  226. tree.column("ФИО", anchor=tk.W, width=150)  # Задаем ширину и выравнивание для колонки ФИО
  227. tree.column("Телефон", anchor=tk.W, width=120)  # Задаем ширину и выравнивание для колонки Телефон
  228. tree.column("Категория", anchor=tk.W, width=100)  # Задаем ширину и выравнивание для колонки Категория
  229.  
  230. # Добавляем таблицу в окно
  231. tree.grid(row=1, column=0, padx=10, pady=10, sticky=(tk.W, tk.E))  # Устанавливаем таблицу в сетку
  232.  
  233. # Поле для отображения сообщений
  234. message_area = tk.Text(root, height=5, width=50, state=tk.DISABLED)  # Создаем текстовое поле для сообщений
  235. message_area.grid(row=2, column=0, padx=10, pady=10, sticky=(tk.W, tk.NE))
  236.  
  237. # Настройка выравнивания для всех элементов
  238. root.columnconfigure(0, weight=1)  # Задаем вес для первой колонки, чтобы она занимала доступное пространство
  239. root.rowconfigure(1, weight=1)  # Задаем вес для первой строки с таблицей
  240.  
  241. frame.columnconfigure(0, weight=1)  # Задаем вес для первой колонки фрейма
  242. frame.columnconfigure(1, weight=1)  # Задаем вес для второй колонки фрейма
  243. frame.columnconfigure(2, weight=1)  # Задаем вес для третьей колонки фрейма
  244.  
  245. # Загружаем контакты при старте приложения
  246. load_contacts_from_file()
  247.  
  248. # Запускаем основной цикл приложения
  249. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement