Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import tkinter as tk
- from tkinter import ttk
- import re
- # Хранение контактов
- contacts = []
- def validate_fio_input(char):
- # Проверяем, что введённый символ соответствует правилам для ФИО (буквы, пробелы и дефисы)
- return re.match(r"[А-Яа-яA-Za-z\s-]", char) is not None
- def validate_phone_input(char):
- # Проверяем, что введённый символ является цифрой для номера телефона
- return re.match(r"\d", char) is not None
- def format_phone_number(phone):
- # Форматируем номер телефона
- digits = re.sub(r'\D', '', phone) # Удаляем все нецифровые символы из строки
- # Проверяем, что номер состоит из 11 цифр и начинается с '7'
- if len(digits) == 11 and digits.startswith("7"):
- # Возвращаем номер в формате: +7(XXX) XXX-XX-XX
- return f"+{digits[0]}({digits[1:4]}) {digits[4:7]}-{digits[7:9]}-{digits[9:11]}"
- return phone # Если номер не соответствует формату, возвращаем оригинальный
- def on_phone_input_change(*args):
- # Обработчик изменения ввода номера телефона
- phone = phone_number_var.get() # Получаем текущее значение номера телефона
- formatted_phone = format_phone_number(phone) # Форматируем номер телефона
- if formatted_phone != phone: # Проверяем, изменился ли формат номера
- phone_number_var.set(formatted_phone) # Устанавливаем отформатированный номер
- phone_number_entry.icursor(len(formatted_phone)) # Устанавливаем курсор в конец поля ввода
- def save_contacts_to_file(filename='contacts.txt'):
- """Сохраняем контакты в текстовый файл."""
- with open(filename, 'w', encoding='utf-8') as file: # Открываем файл для записи
- for contact in contacts: # Проходим по всем контактам
- # Записываем ФИО, телефон и категорию в файл, разделяя их запятой
- file.write(f"{contact['ФИО']}, {contact['Телефон']}, {contact['Категория']}\n")
- # Отображаем сообщение об успешном сохранении
- display_message("Успех", f"Контакты сохранены в '{filename}'.")
- def load_contacts_from_file(filename='contacts.txt'):
- """Загружаем контакты из текстового файла."""
- global contacts # Объявляем переменную contacts глобальной
- try:
- with open(filename, 'r', encoding='utf-8') as file: # Открываем файл для чтения
- contacts = [] # Очищаем текущий список контактов
- for line in file: # Считываем файл построчно
- if line.strip(): # Проверяем, что строка не пустая
- # Разделяем строку на ФИО, телефон и категорию
- fio, phone, category = line.strip().split(', ')
- contact = {"ФИО": fio, "Телефон": phone, "Категория": category} # Создаем словарь контакта
- contacts.append(contact) # Добавляем контакт в список
- update_table() # Обновляем отображение таблицы после загрузки контактов
- display_message("Успех", "Контакты загружены из файла.") # Сообщаем об успешной загрузке
- except FileNotFoundError:
- # Обрабатываем случай, когда файл не найден
- display_message("Информация", "Файл не найден. Начинаем с пустого списка контактов.")
- except Exception as e:
- # Обрабатываем любые другие ошибки при загрузке
- display_message("Ошибка", f"Ошибка при загрузке: {str(e)}", error=True)
- def save_contact():
- first_name = FIO.get().strip() # Получаем и очищаем введенное ФИО
- phone = phone_number_var.get().strip() # Получаем и очищаем введённый номер телефона
- category = categories.get().strip() # Получаем и очищаем введённую категорию
- # Проверка на наличие пустых полей
- if not first_name or not phone or not category:
- display_message("Ошибка", "Все поля должны быть заполнены.", error=True) # Показываем ошибку
- return
- # Проверка на уникальность ФИО в списке контактов
- if any(contact['ФИО'] == first_name for contact in contacts):
- display_message("Ошибка", "Контакт с таким ФИО уже существует.", error=True)
- return
- # Создаем новый контакт и добавляем его в список
- contact = {"ФИО": first_name.title(), "Телефон": phone, "Категория": category}
- contacts.append(contact) # Добавляем контакт в список
- update_table() # Обновляем отображение таблицы
- # Сохраняем контакты в текстовый файл
- save_contacts_to_file()
- # Очищаем поля ввода для новых значений
- FIO.delete(0, tk.END)
- phone_number_var.set("") # Сбрасываем номер телефона
- categories.delete(0, tk.END) # Очищаем поле категории
- display_message("Успех", "Контакт сохранен.") # Показываем сообщение об успешном сохранении
- def search_contact():
- # Получаем текст из поля ввода и удаляем лишние пробелы
- query = FIO.get().strip()
- # Находим контакты, в которых ФИО соответствует введённому запросу (без учета регистра)
- results = [contact for contact in contacts if query.lower() in contact["ФИО"].lower()]
- # Обновляем таблицу с результатами поиска
- update_table(results)
- # Если результаты поиска пустые, выводим сообщение пользователю
- if not results:
- display_message("Результаты поиска", "Контакты не найдены.")
- def update_table(filtered_contacts=None):
- # Удаляем все текущие строки из таблицы
- for row in tree.get_children():
- tree.delete(row)
- # Выбираем, какие контакты отображать: отфильтрованные или все
- to_display = filtered_contacts if filtered_contacts is not None else contacts
- # Вставляем каждую контактную запись в таблицу
- for contact in to_display:
- tree.insert("", "end", values=(contact["ФИО"], contact["Телефон"], contact["Категория"]))
- def delete_contact():
- # Получаем выбранные элементы (контакты) из таблицы
- selected_item = tree.selection()
- # Если есть выбраные элементы
- if selected_item:
- for item in selected_item: # Проходим по всем выбранным элементам
- index = tree.index(item) # Получаем индекс выбранного элемента
- contacts.pop(index) # Удаляем контакт из списка по индексу
- update_table() # Обновляем таблицу после удаления
- display_message("Успех", "Контакт удален.") # Показываем сообщение об успешном удалении
- save_contacts_to_file() # Сохраняем обновлённый список контактов в файл
- else:
- # Если нет выбранных контактов, выводим ошибку
- display_message("Ошибка", "Не выбран контакт для удаления.")
- def edit_contact():
- # Получаем выбранный элемент из таблицы
- selected_item = tree.selection()
- # Проверяем, был ли выбран контакт
- if selected_item:
- index = tree.index(selected_item[0]) # Получаем индекс выбранного элемента
- contact = contacts[index] # Извлекаем данные выбранного контакта
- # Очищаем поля ввода перед загрузкой новых данных
- FIO.delete(0, tk.END) # Очищаем поле ФИО
- FIO.insert(0, contact["ФИО"]) # Заполняем поле ФИО данными из контакта
- phone_number_var.set(contact["Телефон"]) # Устанавливаем номер телефона
- categories.delete(0, tk.END) # Очищаем поле категорий
- categories.insert(0, contact["Категория"]) # Заполняем поле категорий данными из контакта
- # Выводим сообщение о том, что контакт для редактирования загружен
- display_message("Контакт для редактирования загружен.")
- else:
- # Если контакт не был выбран, выводим ошибку
- display_message("Ошибка", "Не выбран контакт для редактирования.")
- def display_message(title, message, error=False):
- # Активируем текстовое поле для отображения сообщения
- message_area.config(state=tk.NORMAL)
- message_area.delete(1.0, tk.END) # Очищаем предыдущее сообщение
- message_area.insert(tk.END, f"{title}: {message}\n") # Вставляем новое сообщение
- message_area.config(state=tk.DISABLED) # Деактивируем текстовое поле, чтобы избежать изменений
- # Создаем главное окно приложения
- root = tk.Tk()
- root.title("Контактная книга") # Устанавливаем заголовок окна
- root.geometry("400x600") # Устанавливаем размеры окна
- frame = ttk.Frame(root, padding="10") # Создаем фрейм для размещения виджетов
- frame.grid(sticky=(tk.N, tk.E, tk.S, tk.W)) # Позволяем фрейму занимать доступное пространство
- # Регистрация валидации для ввода
- vcmd_fio = (root.register(validate_fio_input), '%S') # Валидация для поля ФИО
- vcmd_phone = (root.register(validate_phone_input), '%S') # Валидация для поля телефона
- # Создаем переменную для отслеживания ввода в поле телефона
- phone_number_var = tk.StringVar() # Создаем переменную типа StringVar
- phone_number_var.trace_add("write", on_phone_input_change) # Добавляем обработчик изменения текста
- # Метки для полей ввода
- ttk.Label(frame, text="ФИО:").grid(row=0, column=0, sticky=tk.E, padx=5, pady=5)
- FIO = ttk.Entry(frame, validate='key', validatecommand=vcmd_fio) # Поле ввода для ФИО с валидацией
- FIO.grid(row=0, column=1, sticky=tk.NE + tk.W, padx=5, pady=5)
- ttk.Label(frame, text="Телефон:").grid(row=1, column=0, sticky=tk.E, padx=5, pady=5)
- phone_number_entry = ttk.Entry(frame, textvariable=phone_number_var, validate='key', validatecommand=vcmd_phone)
- phone_number_entry.grid(row=1, column=1, sticky=tk.NE + tk.W, padx=5, pady=5)
- phone_number_var.set("+7") # Подсказка пользователю
- ttk.Label(frame, text="Категория:").grid(row=2, column=0, sticky=tk.E, padx=5, pady=5)
- categories = ttk.Entry(frame) # Поле ввода для категории контакта
- categories.grid(row=2, column=1, sticky=tk.NE + tk.W, padx=5, pady=5)
- # Кнопки управления
- add_contact = ttk.Button(frame, text="Сохранить контакт", command=save_contact) # Кнопка для сохранения контакта
- add_contact.grid(row=0, column=2, sticky=tk.NE, padx=5, pady=5)
- delete_contact_button = ttk.Button(frame, text="Удалить контакт",
- command=delete_contact) # Кнопка для удаления контакта
- delete_contact_button.grid(row=1, column=2, sticky=tk.NE, padx=5, pady=5)
- search_contact_button = ttk.Button(frame, text="Поиск контакта", command=search_contact) # Кнопка для поиска контакта
- search_contact_button.grid(row=2, column=2, sticky=tk.NE, padx=5, pady=5)
- edit_contact_button = ttk.Button(frame, text="Редактировать контакт",
- command=edit_contact) # Кнопка для редактирования контакта
- edit_contact_button.grid(row=3, column=2, sticky=tk.NE, padx=5, pady=5)
- # Создаем таблицу для отображения контактов
- columns = ("ФИО", "Телефон", "Категория") # Определяем колонки таблицы
- tree = ttk.Treeview(root, columns=columns, show='headings') # Создаем виджет таблицы
- tree.heading("ФИО", text="ФИО") # Устанавливаем заголовок для первой колонки
- tree.heading("Телефон", text="Телефон") # Устанавливаем заголовок для второй колонки
- tree.heading("Категория", text="Категория") # Устанавливаем заголовок для третьей колонки
- # Установка ширины колонок
- tree.column("ФИО", anchor=tk.W, width=150) # Задаем ширину и выравнивание для колонки ФИО
- tree.column("Телефон", anchor=tk.W, width=120) # Задаем ширину и выравнивание для колонки Телефон
- tree.column("Категория", anchor=tk.W, width=100) # Задаем ширину и выравнивание для колонки Категория
- # Добавляем таблицу в окно
- tree.grid(row=1, column=0, padx=10, pady=10, sticky=(tk.W, tk.E)) # Устанавливаем таблицу в сетку
- # Поле для отображения сообщений
- message_area = tk.Text(root, height=5, width=50, state=tk.DISABLED) # Создаем текстовое поле для сообщений
- message_area.grid(row=2, column=0, padx=10, pady=10, sticky=(tk.W, tk.NE))
- # Настройка выравнивания для всех элементов
- root.columnconfigure(0, weight=1) # Задаем вес для первой колонки, чтобы она занимала доступное пространство
- root.rowconfigure(1, weight=1) # Задаем вес для первой строки с таблицей
- frame.columnconfigure(0, weight=1) # Задаем вес для первой колонки фрейма
- frame.columnconfigure(1, weight=1) # Задаем вес для второй колонки фрейма
- frame.columnconfigure(2, weight=1) # Задаем вес для третьей колонки фрейма
- # Загружаем контакты при старте приложения
- load_contacts_from_file()
- # Запускаем основной цикл приложения
- root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement