|
|
@@ -0,0 +1,473 @@
|
|
|
+import tkinter as tk
|
|
|
+from tkinter import ttk, messagebox, filedialog
|
|
|
+from datetime import datetime
|
|
|
+import csv
|
|
|
+from db import Database
|
|
|
+from tkcalendar import DateEntry
|
|
|
+
|
|
|
+class TheatreApp:
|
|
|
+ def __init__(self, db):
|
|
|
+ self.db = db
|
|
|
+ self.root = tk.Tk()
|
|
|
+ self.root.title("Театральная система")
|
|
|
+ self.root.geometry("1100x650")
|
|
|
+ self.create_widgets()
|
|
|
+ self.refresh_all()
|
|
|
+
|
|
|
+ def create_widgets(self):
|
|
|
+ notebook = ttk.Notebook(self.root)
|
|
|
+ notebook.pack(fill=tk.BOTH, expand=True)
|
|
|
+
|
|
|
+ # Отделы
|
|
|
+ self.depts_frame = ttk.Frame(notebook)
|
|
|
+ notebook.add(self.depts_frame, text="Отделы")
|
|
|
+ self.depts_tree = self._make_table(self.depts_frame,
|
|
|
+ ["ID","Фамилия","Имя","Отчество","Email","Реквизиты"])
|
|
|
+ tk.Button(self.depts_frame, text="Обновить", command=self.refresh_departments).pack(pady=5)
|
|
|
+
|
|
|
+ # Пользователи
|
|
|
+ self.users_frame = ttk.Frame(notebook)
|
|
|
+ notebook.add(self.users_frame, text="Пользователи")
|
|
|
+ self.users_tree = self._make_table(self.users_frame,
|
|
|
+ ["ID","Фамилия","Имя","Отчество","Дата рождения","Телефон"])
|
|
|
+ btn_frame = tk.Frame(self.users_frame)
|
|
|
+ btn_frame.pack(pady=5)
|
|
|
+ tk.Button(btn_frame, text="Добавить", command=self.add_user).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame, text="Редактировать", command=self.edit_user).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame, text="Удалить", command=self.delete_user).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame, text="Обновить", command=self.refresh_users).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ # Билеты
|
|
|
+ self.tickets_frame = ttk.Frame(notebook)
|
|
|
+ notebook.add(self.tickets_frame, text="Билеты")
|
|
|
+ self.tickets_tree = self._make_table(self.tickets_frame,
|
|
|
+ ["ID","Отдел","Пользователь","Спектакль","Цена","Место","Дата/время","Полная стоимость"])
|
|
|
+ btn_frame2 = tk.Frame(self.tickets_frame)
|
|
|
+ btn_frame2.pack(pady=5)
|
|
|
+ tk.Button(btn_frame2, text="Добавить", command=self.add_ticket).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame2, text="Редактировать", command=self.edit_ticket).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame2, text="Удалить", command=self.delete_ticket).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame2, text="Поиск", command=self.search_tickets).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame2, text="Отчёт (выручка)", command=self.revenue_report).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame2, text="Обновить", command=self.refresh_tickets).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ def _make_table(self, parent, columns):
|
|
|
+ tree = ttk.Treeview(parent, columns=columns, show="headings", height=20)
|
|
|
+ for col in columns:
|
|
|
+ tree.heading(col, text=col)
|
|
|
+ tree.column(col, width=120, anchor="center")
|
|
|
+ tree.pack(fill=tk.BOTH, expand=True)
|
|
|
+ return tree
|
|
|
+
|
|
|
+ def _fill_tree(self, tree, rows):
|
|
|
+ for item in tree.get_children():
|
|
|
+ tree.delete(item)
|
|
|
+ for row in rows:
|
|
|
+ tree.insert("", "end", values=row)
|
|
|
+
|
|
|
+ def refresh_departments(self):
|
|
|
+ rows = self.db.get_departments()
|
|
|
+ self._fill_tree(self.depts_tree, rows)
|
|
|
+
|
|
|
+ def refresh_users(self):
|
|
|
+ rows = self.db.get_users()
|
|
|
+ self._fill_tree(self.users_tree, rows)
|
|
|
+
|
|
|
+ def refresh_tickets(self):
|
|
|
+ rows = self.db.get_tickets()
|
|
|
+ self._fill_tree(self.tickets_tree, rows)
|
|
|
+
|
|
|
+ def add_user(self):
|
|
|
+ dialog = UserDialog(self.root, self.db, title="Добавить пользователя")
|
|
|
+ self.wait_for_dialog(dialog, self.refresh_users)
|
|
|
+
|
|
|
+ def edit_user(self):
|
|
|
+ selected = self.users_tree.selection()
|
|
|
+ if not selected:
|
|
|
+ messagebox.showerror("Ошибка", "Выберите пользователя")
|
|
|
+ return
|
|
|
+ values = self.users_tree.item(selected[0])["values"]
|
|
|
+ user_id = values[0]
|
|
|
+ defaults = {
|
|
|
+ "last_name": values[1],
|
|
|
+ "first_name": values[2],
|
|
|
+ "middle_name": values[3] if values[3] else "",
|
|
|
+ "birth_date": values[4],
|
|
|
+ "phone": values[5]
|
|
|
+ }
|
|
|
+ dialog = UserDialog(self.root, self.db, user_id=user_id, defaults=defaults, title="Редактировать пользователя")
|
|
|
+ self.wait_for_dialog(dialog, self.refresh_users)
|
|
|
+
|
|
|
+ def delete_user(self):
|
|
|
+ selected = self.users_tree.selection()
|
|
|
+ if not selected:
|
|
|
+ messagebox.showerror("Ошибка", "Выберите пользователя")
|
|
|
+ return
|
|
|
+ user_id = self.users_tree.item(selected[0])["values"][0]
|
|
|
+ if messagebox.askyesno("Удаление", f"Удалить пользователя ID={user_id}?"):
|
|
|
+ self.db.delete_user(user_id)
|
|
|
+ self.refresh_users()
|
|
|
+
|
|
|
+ def add_ticket(self):
|
|
|
+ dialog = TicketDialog(self.root, self.db, title="Добавить билет")
|
|
|
+ self.wait_for_dialog(dialog, self.refresh_tickets)
|
|
|
+
|
|
|
+ def edit_ticket(self):
|
|
|
+ selected = self.tickets_tree.selection()
|
|
|
+ if not selected:
|
|
|
+ messagebox.showerror("Ошибка", "Выберите билет")
|
|
|
+ return
|
|
|
+ values = self.tickets_tree.item(selected[0])["values"]
|
|
|
+ ticket_id = values[0]
|
|
|
+ defaults = {
|
|
|
+ "department_id": values[1],
|
|
|
+ "user_id": values[2],
|
|
|
+ "performance_name": values[3],
|
|
|
+ "ticket_price": values[4],
|
|
|
+ "hall_seat": values[5],
|
|
|
+ "event_datetime": values[6]
|
|
|
+ }
|
|
|
+ dialog = TicketDialog(self.root, self.db, ticket_id=ticket_id, defaults=defaults, title="Редактировать билет")
|
|
|
+ self.wait_for_dialog(dialog, self.refresh_tickets)
|
|
|
+
|
|
|
+ def delete_ticket(self):
|
|
|
+ selected = self.tickets_tree.selection()
|
|
|
+ if not selected:
|
|
|
+ messagebox.showerror("Ошибка", "Выберите билет")
|
|
|
+ return
|
|
|
+ ticket_id = self.tickets_tree.item(selected[0])["values"][0]
|
|
|
+ if messagebox.askyesno("Удаление", f"Удалить билет ID={ticket_id}?"):
|
|
|
+ self.db.delete_ticket(ticket_id)
|
|
|
+ self.refresh_tickets()
|
|
|
+
|
|
|
+ def search_tickets(self):
|
|
|
+ SearchDialog(self.root, self.db)
|
|
|
+
|
|
|
+ def revenue_report(self):
|
|
|
+ perf = tk.simpledialog.askstring("Отчёт", "Введите название спектакля:")
|
|
|
+ if perf:
|
|
|
+ total = self.db.revenue_by_performance(perf)
|
|
|
+ messagebox.showinfo("Выручка", f"Выручка по спектаклю '{perf}': {total} руб.")
|
|
|
+
|
|
|
+ def refresh_all(self):
|
|
|
+ self.refresh_departments()
|
|
|
+ self.refresh_users()
|
|
|
+ self.refresh_tickets()
|
|
|
+
|
|
|
+ def wait_for_dialog(self, dialog, refresh_callback):
|
|
|
+ self.root.wait_window(dialog.top)
|
|
|
+ refresh_callback()
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self.root.mainloop()
|
|
|
+
|
|
|
+
|
|
|
+# ДИАЛОГ ПОЛЬЗОВАТЕЛЯ (календарь для даты рождения)
|
|
|
+class UserDialog:
|
|
|
+ def __init__(self, parent, db, user_id=None, defaults=None, title="Пользователь"):
|
|
|
+ self.db = db
|
|
|
+ self.user_id = user_id
|
|
|
+ self.top = tk.Toplevel(parent)
|
|
|
+ self.top.title(title)
|
|
|
+ self.top.grab_set()
|
|
|
+ self.top.resizable(False, False)
|
|
|
+
|
|
|
+ row = 0
|
|
|
+ tk.Label(self.top, text="Фамилия:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.last_name_entry = tk.Entry(self.top, width=30)
|
|
|
+ self.last_name_entry.grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Имя:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.first_name_entry = tk.Entry(self.top, width=30)
|
|
|
+ self.first_name_entry.grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Отчество:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.middle_name_entry = tk.Entry(self.top, width=30)
|
|
|
+ self.middle_name_entry.grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Дата рождения:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ frame_birth = tk.Frame(self.top)
|
|
|
+ frame_birth.grid(row=row, column=1, padx=5, pady=5, sticky="w")
|
|
|
+ self.birth_date = DateEntry(frame_birth, width=12, background='darkblue',
|
|
|
+ foreground='white', borderwidth=2,
|
|
|
+ date_pattern='yyyy-mm-dd')
|
|
|
+ self.birth_date.pack(side=tk.LEFT)
|
|
|
+ tk.Button(frame_birth, text="Сегодня", command=self.set_today_birth).pack(side=tk.LEFT, padx=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Телефон:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.phone_entry = tk.Entry(self.top, width=30)
|
|
|
+ self.phone_entry.grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ btn_frame = tk.Frame(self.top)
|
|
|
+ btn_frame.grid(row=row, column=0, columnspan=2, pady=10)
|
|
|
+ tk.Button(btn_frame, text="Сохранить", command=self.save).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame, text="Отмена", command=self.top.destroy).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ if defaults:
|
|
|
+ self.last_name_entry.insert(0, defaults.get("last_name", ""))
|
|
|
+ self.first_name_entry.insert(0, defaults.get("first_name", ""))
|
|
|
+ self.middle_name_entry.insert(0, defaults.get("middle_name", ""))
|
|
|
+ self.phone_entry.insert(0, defaults.get("phone", ""))
|
|
|
+ birth = defaults.get("birth_date")
|
|
|
+ if birth:
|
|
|
+ try:
|
|
|
+ if isinstance(birth, datetime):
|
|
|
+ self.birth_date.set_date(birth)
|
|
|
+ else:
|
|
|
+ self.birth_date.set_date(datetime.strptime(str(birth), "%Y-%m-%d"))
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ self.birth_date.set_date(datetime.now())
|
|
|
+
|
|
|
+ def set_today_birth(self):
|
|
|
+ self.birth_date.set_date(datetime.now())
|
|
|
+
|
|
|
+ def save(self):
|
|
|
+ last = self.last_name_entry.get().strip()
|
|
|
+ first = self.first_name_entry.get().strip()
|
|
|
+ middle = self.middle_name_entry.get().strip() or None
|
|
|
+ phone = self.phone_entry.get().strip()
|
|
|
+ birth_date = self.birth_date.get_date().strftime("%Y-%m-%d")
|
|
|
+
|
|
|
+ if not last or not first or not phone:
|
|
|
+ messagebox.showerror("Ошибка", "Заполните фамилию, имя и телефон")
|
|
|
+ return
|
|
|
+
|
|
|
+ if self.user_id:
|
|
|
+ self.db.update_user(self.user_id, last, first, middle, birth_date, phone)
|
|
|
+ else:
|
|
|
+ self.db.add_user(last, first, middle, birth_date, phone)
|
|
|
+ self.top.destroy()
|
|
|
+
|
|
|
+
|
|
|
+# ДИАЛОГ БИЛЕТА
|
|
|
+class TicketDialog:
|
|
|
+ def __init__(self, parent, db, ticket_id=None, defaults=None, title="Билет"):
|
|
|
+ self.db = db
|
|
|
+ self.ticket_id = ticket_id
|
|
|
+ self.top = tk.Toplevel(parent)
|
|
|
+ self.top.title(title)
|
|
|
+ self.top.grab_set()
|
|
|
+ self.top.resizable(False, False)
|
|
|
+
|
|
|
+ self.dept_list = db.get_departments_list()
|
|
|
+ self.user_list = db.get_users_list()
|
|
|
+
|
|
|
+ self.dept_var = tk.StringVar()
|
|
|
+ self.user_var = tk.StringVar()
|
|
|
+ self.perf_var = tk.StringVar()
|
|
|
+ self.price_var = tk.StringVar()
|
|
|
+ self.seat_var = tk.StringVar()
|
|
|
+ self.date_var = tk.StringVar()
|
|
|
+ self.hour_var = tk.StringVar()
|
|
|
+ self.minute_var = tk.StringVar()
|
|
|
+
|
|
|
+ row = 0
|
|
|
+ tk.Label(self.top, text="Отдел:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ dept_combo = ttk.Combobox(self.top, textvariable=self.dept_var,
|
|
|
+ values=[t for _, t in self.dept_list], width=40)
|
|
|
+ dept_combo.grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Пользователь:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ user_combo = ttk.Combobox(self.top, textvariable=self.user_var,
|
|
|
+ values=[t for _, t in self.user_list], width=40)
|
|
|
+ user_combo.grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Спектакль:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ tk.Entry(self.top, textvariable=self.perf_var, width=42).grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Цена:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ tk.Entry(self.top, textvariable=self.price_var, width=42).grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Место:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ tk.Entry(self.top, textvariable=self.seat_var, width=42).grid(row=row, column=1, padx=5, pady=5)
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ # Дата и время
|
|
|
+ tk.Label(self.top, text="Дата:").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.date_picker = DateEntry(self.top, width=12, background='darkblue',
|
|
|
+ foreground='white', borderwidth=2,
|
|
|
+ date_pattern='yyyy-mm-dd', textvariable=self.date_var)
|
|
|
+ self.date_picker.grid(row=row, column=1, padx=5, pady=5, sticky="w")
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Час (0-23):").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.hour_spin = tk.Spinbox(self.top, from_=0, to=23, width=5, format="%02.0f", textvariable=self.hour_var)
|
|
|
+ self.hour_spin.grid(row=row, column=1, padx=5, pady=5, sticky="w")
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ tk.Label(self.top, text="Минуты (0-59):").grid(row=row, column=0, padx=5, pady=5, sticky="e")
|
|
|
+ self.minute_spin = tk.Spinbox(self.top, from_=0, to=59, width=5, format="%02.0f", textvariable=self.minute_var)
|
|
|
+ self.minute_spin.grid(row=row, column=1, padx=5, pady=5, sticky="w")
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ def set_now():
|
|
|
+ now = datetime.now()
|
|
|
+ self.date_picker.set_date(now)
|
|
|
+ self.hour_var.set(f"{now.hour:02d}")
|
|
|
+ self.minute_var.set(f"{now.minute:02d}")
|
|
|
+ tk.Button(self.top, text="Сейчас", command=set_now).grid(row=row, column=1, padx=5, pady=5, sticky="w")
|
|
|
+ row += 1
|
|
|
+
|
|
|
+ if defaults:
|
|
|
+ self.set_defaults(defaults)
|
|
|
+ else:
|
|
|
+ self.date_picker.set_date(datetime.now())
|
|
|
+ self.hour_var.set("00")
|
|
|
+ self.minute_var.set("00")
|
|
|
+
|
|
|
+ btn_frame = tk.Frame(self.top)
|
|
|
+ btn_frame.grid(row=row, column=0, columnspan=2, pady=10)
|
|
|
+ tk.Button(btn_frame, text="Сохранить", command=self.save).pack(side=tk.LEFT, padx=5)
|
|
|
+ tk.Button(btn_frame, text="Отмена", command=self.top.destroy).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ def set_defaults(self, defaults):
|
|
|
+ dept_id = defaults.get("department_id")
|
|
|
+ if dept_id:
|
|
|
+ for id_, text in self.dept_list:
|
|
|
+ if id_ == dept_id:
|
|
|
+ self.dept_var.set(text)
|
|
|
+ break
|
|
|
+ user_id = defaults.get("user_id")
|
|
|
+ if user_id:
|
|
|
+ for id_, text in self.user_list:
|
|
|
+ if id_ == user_id:
|
|
|
+ self.user_var.set(text)
|
|
|
+ break
|
|
|
+ self.perf_var.set(defaults.get("performance_name", ""))
|
|
|
+ self.price_var.set(str(defaults.get("ticket_price", "")))
|
|
|
+ self.seat_var.set(defaults.get("hall_seat", ""))
|
|
|
+ dt = defaults.get("event_datetime")
|
|
|
+ if dt:
|
|
|
+ if isinstance(dt, datetime):
|
|
|
+ self.date_picker.set_date(dt)
|
|
|
+ self.hour_var.set(f"{dt.hour:02d}")
|
|
|
+ self.minute_var.set(f"{dt.minute:02d}")
|
|
|
+ else:
|
|
|
+ try:
|
|
|
+ dt_obj = datetime.strptime(str(dt), "%Y-%m-%d %H:%M:%S")
|
|
|
+ self.date_picker.set_date(dt_obj)
|
|
|
+ self.hour_var.set(f"{dt_obj.hour:02d}")
|
|
|
+ self.minute_var.set(f"{dt_obj.minute:02d}")
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ def save(self):
|
|
|
+ dept_text = self.dept_var.get()
|
|
|
+ dept_id = None
|
|
|
+ for id_, text in self.dept_list:
|
|
|
+ if text == dept_text:
|
|
|
+ dept_id = id_
|
|
|
+ break
|
|
|
+ if dept_id is None:
|
|
|
+ messagebox.showerror("Ошибка", "Выберите отдел из списка")
|
|
|
+ return
|
|
|
+
|
|
|
+ user_text = self.user_var.get()
|
|
|
+ user_id = None
|
|
|
+ for id_, text in self.user_list:
|
|
|
+ if text == user_text:
|
|
|
+ user_id = id_
|
|
|
+ break
|
|
|
+ if user_id is None:
|
|
|
+ messagebox.showerror("Ошибка", "Выберите пользователя из списка")
|
|
|
+ return
|
|
|
+
|
|
|
+ perf = self.perf_var.get().strip()
|
|
|
+ if not perf:
|
|
|
+ messagebox.showerror("Ошибка", "Введите название спектакля")
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ price = float(self.price_var.get().strip())
|
|
|
+ except ValueError:
|
|
|
+ messagebox.showerror("Ошибка", "Цена должна быть числом")
|
|
|
+ return
|
|
|
+
|
|
|
+ seat = self.seat_var.get().strip()
|
|
|
+ if not seat:
|
|
|
+ messagebox.showerror("Ошибка", "Введите место")
|
|
|
+ return
|
|
|
+
|
|
|
+ date_obj = self.date_picker.get_date()
|
|
|
+ try:
|
|
|
+ hour = int(self.hour_var.get().strip())
|
|
|
+ minute = int(self.minute_var.get().strip())
|
|
|
+ except ValueError:
|
|
|
+ messagebox.showerror("Ошибка", "Время должно быть числом")
|
|
|
+ return
|
|
|
+ if hour < 0 or hour > 23 or minute < 0 or minute > 59:
|
|
|
+ messagebox.showerror("Ошибка", "Некорректное время")
|
|
|
+ return
|
|
|
+ event_dt = datetime(date_obj.year, date_obj.month, date_obj.day, hour, minute)
|
|
|
+
|
|
|
+ if self.ticket_id:
|
|
|
+ self.db.update_ticket(self.ticket_id, dept_id, user_id, perf, price, seat, event_dt)
|
|
|
+ else:
|
|
|
+ self.db.add_ticket(dept_id, user_id, perf, price, seat, event_dt)
|
|
|
+ self.top.destroy()
|
|
|
+
|
|
|
+
|
|
|
+# ДИАЛОГ ПОИСКА
|
|
|
+class SearchDialog:
|
|
|
+ def __init__(self, parent, db):
|
|
|
+ self.db = db
|
|
|
+ self.top = tk.Toplevel(parent)
|
|
|
+ self.top.title("Поиск билетов")
|
|
|
+ self.top.grab_set()
|
|
|
+ self.top.geometry("900x500")
|
|
|
+
|
|
|
+ frame = tk.Frame(self.top)
|
|
|
+ frame.pack(pady=10)
|
|
|
+ tk.Label(frame, text="Фамилия (часть):").grid(row=0, column=0, padx=5, pady=5)
|
|
|
+ self.last_entry = tk.Entry(frame, width=20)
|
|
|
+ self.last_entry.grid(row=0, column=1, padx=5, pady=5)
|
|
|
+ tk.Label(frame, text="Спектакль (часть):").grid(row=0, column=2, padx=5, pady=5)
|
|
|
+ self.perf_entry = tk.Entry(frame, width=20)
|
|
|
+ self.perf_entry.grid(row=0, column=3, padx=5, pady=5)
|
|
|
+ tk.Button(frame, text="Искать", command=self.search).grid(row=0, column=4, padx=10)
|
|
|
+
|
|
|
+ self.tree = ttk.Treeview(self.top, columns=("ID","Фамилия","Имя","Спектакль","Цена","Место","Дата","Полная"), show="headings")
|
|
|
+ for col in self.tree["columns"]:
|
|
|
+ self.tree.heading(col, text=col)
|
|
|
+ self.tree.column(col, width=100, anchor="center")
|
|
|
+ self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
|
|
|
+
|
|
|
+ tk.Button(self.top, text="Экспорт в CSV", command=self.export_csv).pack(pady=5)
|
|
|
+
|
|
|
+ def search(self):
|
|
|
+ last = self.last_entry.get().strip()
|
|
|
+ perf = self.perf_entry.get().strip()
|
|
|
+ rows = self.db.search_tickets(last, perf)
|
|
|
+ for item in self.tree.get_children():
|
|
|
+ self.tree.delete(item)
|
|
|
+ if rows:
|
|
|
+ for row in rows:
|
|
|
+ self.tree.insert("", "end", values=row)
|
|
|
+ else:
|
|
|
+ messagebox.showinfo("Результат", "Ничего не найдено")
|
|
|
+
|
|
|
+ def export_csv(self):
|
|
|
+ if not self.tree.get_children():
|
|
|
+ messagebox.showwarning("Нет данных", "Сначала выполните поиск")
|
|
|
+ return
|
|
|
+ filename = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV", "*.csv")])
|
|
|
+ if filename:
|
|
|
+ data = []
|
|
|
+ for child in self.tree.get_children():
|
|
|
+ data.append(self.tree.item(child)["values"])
|
|
|
+ with open(filename, 'w', encoding='utf-8', newline='') as f:
|
|
|
+ writer = csv.writer(f)
|
|
|
+ writer.writerow(["ID","Фамилия","Имя","Спектакль","Цена","Место","Дата","Полная"])
|
|
|
+ writer.writerows(data)
|
|
|
+ messagebox.showinfo("Успех", f"Экспортировано в {filename}")
|