## this is the custodisco kiosk import tkinter as tk from tkinter import font as tkfont from tkinter import Canvas, ttk, Text, filedialog, messagebox from PIL import Image, ImageTk, ImageDraw from qreader import QReader from pyzbar.pyzbar import decode import numpy as np import tozpl import subprocess import threading import json import os import cv2 import time import qrcode import addtoDB import re # Import re for regular expression matching import os import shutil import hashlib # Configuration MIGRATION_ITEMS_DIR = "/home/trav/Documents/migration_items" # Global variables class GlobalVars: qr_code_value = None print_type = "neither" migration_ticket = "" selected_user = None BUTTON_FONT = None TEXT_FONT = None class Kiosk(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.frame = None self.frames_history = [] self.geometry('1366x768') self.attributes('-fullscreen', True) self.config(cursor="crosshair") self.QRX = None self.QRY = None self.QRscale = 1 # Initialize fonts GlobalVars.BUTTON_FONT = tkfont.Font(size=24, family='Helvetica') GlobalVars.TEXT_FONT = tkfont.Font(size=30, family='Helvetica') self.switch_frame(Screen0) def switch_frame(self, frame_class, keep_history=True): if keep_history and self.frame: self.frames_history.append(type(self.frame)) new_frame = frame_class(self) if self.frame is not None: self.frame.destroy() self.frame = new_frame self.frame.pack(fill="both", expand=True) def add_home_button(self, frame): # Create the "Start Over" button home_button = tk.Button(frame, text="Start Over from the beginning", command=self.show_warning_dialog, bg='peach puff', width=24, font=GlobalVars.BUTTON_FONT) home_button.place(x=0, y=0) # top-left corner def show_warning_dialog(self): result = messagebox.askokcancel("Warning", "If you start over, you will lose all information entered so far. Are you sure you want to start over?", icon='warning') if result: self.start_over() def start_over(self): global migration_ticket global MIGRATION_ITEMS_DIR # Check if migration_ticket is defined if 'migration_ticket' in globals(): # Delete any existing migration files if migration_ticket: text_file = os.path.join(MIGRATION_ITEMS_DIR, f"{migration_ticket}.txt") image_file = os.path.join(MIGRATION_ITEMS_DIR, f"{migration_ticket}.jpg") if os.path.exists(text_file): os.remove(text_file) if os.path.exists(image_file): os.remove(image_file) # Reset migration_ticket migration_ticket = "" # Reset other global variables GlobalVars.qr_code_value = None GlobalVars.print_type = "neither" GlobalVars.selected_user = None # Switch to the home screen self.switch_frame(Screen0) # home screen class Screen0(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') title_font = tkfont.Font(size=42, family='Helvetica') # 30% bigger # Split the screen into two frames left_frame = tk.Frame(self, bg='#bcfef9') right_frame = tk.Frame(self, bg='#bcfef9', padx=40) # 40px padding on the right side left_frame.grid(row=0, column=0, sticky='nsew') right_frame.grid(row=0, column=1, sticky='nsew') self.grid_columnconfigure(0, weight=1, minsize=800) # For left frame self.grid_columnconfigure(1, weight=1) # For right frame # Title and buttons on the left side title_label = tk.Label(left_frame, text="Custodisco", bg='#bcfef9', fg='#800080', font=('Helvetica', 64)) # dark purple color title_label.pack(side='top', pady=200) # adjust to your needs # Welcome message on the left side # welcome_text = """""" # welcome_label = tk.Label(left_frame, text=welcome_text, bg='#bcfef9', font=GlobalVars.TEXT_FONT, justify='left', wraplength=650) # welcome_label.pack(side='top', padx=20, pady=20) # tk.Button(right_frame, text="Create Item", command=lambda: master.switch_frame(Screen1), height=4, width=75, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(side='top', pady=30) # Button for Migration mode tk.Button(right_frame, text="Create Item", command=lambda: master.switch_frame(Screen15), height=4, width=75, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(side='top', pady=30) tk.Button(right_frame, text="Lookup Item", command=lambda: master.switch_frame(Screen14), height=4, width=75, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(side='top', pady=30) # Create the quit button tk.Button(right_frame, text="Quit", command=self.quit_program, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) def quit_program(self): self.master.destroy() # do you have ssb? class Screen1(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Create the label widget with the text label = tk.Label(self, text="Would you like to associate your item with a Scuttlebutt account?", font=GlobalVars.TEXT_FONT) label.pack(pady=90) tk.Button(self, text="Yes, and I already have an account", command=lambda: master.switch_frame(Screen2), height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=20) # tk.Button(self, text="Yes, but I need to create an account now", command=lambda: master.switch_frame(Screen7), height=3, width=50, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=20) tk.Button(self, text="No thanks", command=lambda: master.switch_frame(Screen3), height=3, width=50, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=20) class VirtualScrolledFrame(ttk.Frame): def __init__(self, parent, *args, **kw): ttk.Frame.__init__(self, parent, *args, **kw) # Create a canvas object and a vertical scrollbar for scrolling it self.vscrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL) self.canvas = tk.Canvas(self, yscrollcommand=self.vscrollbar.set, bg='#bcfef9') self.vscrollbar.config(command=self.canvas.yview) # Reset the view self.canvas.xview_moveto(0) self.canvas.yview_moveto(0) # Create a frame inside the canvas which will be scrolled with it self.interior = tk.Frame(self.canvas, bg='#bcfef9') self.interior_id = self.canvas.create_window(0, 0, window=self.interior, anchor=tk.NW) # Pack the widgets self.vscrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Bind events to the Canvas self.canvas.bind('', self._configure_canvas) self.interior.bind('', self._configure_interior) def _configure_interior(self, event): # Update the scrollbars to match the size of the inner frame size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight()) self.canvas.config(scrollregion="0 0 %s %s" % size) if self.interior.winfo_reqwidth() != self.canvas.winfo_width(): # Update the canvas's width to fit the inner frame self.canvas.config(width=self.interior.winfo_reqwidth()) def _configure_canvas(self, event): if self.interior.winfo_reqwidth() != self.canvas.winfo_width(): # Update the inner frame's width to fill the canvas self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width()) # find yourself in list of ssb users class Screen2(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') self.selected_label = None self.filtered_users = [] # Create a new frame at the top for the label and text box self.top_frame = tk.Frame(self, bg='#bcfef9') self.top_frame.pack(side="top", fill="x", pady=(60, 10)) # Add a label with text wrapping self.label = tk.Label(self.top_frame, text="Start typing your public key or alias to find yourself in the list then click on your key to select it.", font=GlobalVars.TEXT_FONT, wraplength=800, bg='#bcfef9') self.label.pack(side="top", pady=(0, 10)) # Add text box to the top frame self.entry = tk.Entry(self.top_frame, font=GlobalVars.TEXT_FONT) self.entry.bind('', lambda e: self.update_users_list()) self.entry.pack(side="top", fill="x", padx=20) # Focus on the entry box self.entry.focus_set() # Create container for user list self.container = tk.Frame(self, bg='#bcfef9') self.container.pack(fill='both', expand=True, padx=20, pady=10) # Create a canvas for the user list self.canvas = tk.Canvas(self.container, bg='#bcfef9') self.scrollbar = ttk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas, bg='#bcfef9') self.scrollable_frame.bind( "", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")) ) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) self.scrollbar.pack(side="right", fill="y") # Configure the scrollbar style to make it larger style = ttk.Style() style.configure("Vertical.TScrollbar", arrowsize=48, width=48) # Create a frame for action buttons at the bottom self.button_frame = tk.Frame(self, bg='#bcfef9') self.button_frame.pack(side="bottom", fill="x", pady=10) # The 'Refresh List' button self.refresh_button = tk.Button(self.button_frame, text="Refresh List", command=self.refresh_users, height=2, width=20, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.refresh_button.pack(side="left", padx=(20, 10)) # The 'Done' button to navigate to next screen self.done_button = tk.Button(self.button_frame, text="Done", command=lambda: master.switch_frame(Screen3), height=2, width=20, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.done_button.pack(side="right", padx=(10, 20)) # Initialize users list from users.json self.users = self.get_users_from_file() self.update_users_list() master.add_home_button(self) def update_users_list(self): search_text = self.entry.get().lower() self.filtered_users = [user for user in self.users if search_text in user['id'].lower() or search_text in self.unescape_unicode(user.get('alias', '')).lower()] self.display_users() def display_users(self): for widget in self.scrollable_frame.winfo_children(): widget.destroy() for index, user in enumerate(self.filtered_users): try: frame = tk.Frame(self.scrollable_frame, bg='#bcfef9') frame.pack(fill='x', expand=True, pady=2) alias = self.unescape_unicode(user.get('alias', '')) id = user.get('id', '') alias_label = tk.Label(frame, text=alias, font=('Helvetica', 14, 'bold'), width=20, anchor='w', bg='#bcfef9') alias_label.pack(side='left', padx=(0, 10)) id_label = tk.Label(frame, text=id, font=('Helvetica', 14), anchor='w', bg='#bcfef9') id_label.pack(side='left', expand=True, fill='x') frame.bind('', lambda e, u=user, f=frame: self.on_user_clicked(f, u)) alias_label.bind('', lambda e, u=user, f=frame: self.on_user_clicked(f, u)) id_label.bind('', lambda e, u=user, f=frame: self.on_user_clicked(f, u)) if GlobalVars.selected_user == id: self.highlight_frame(frame) self.selected_label = frame except Exception as e: print(f"Error displaying user {index + 1}: {str(e)}") print(f"User data: {user}") print(traceback.format_exc()) self.scrollable_frame.update_idletasks() self.canvas.configure(scrollregion=self.canvas.bbox("all")) def on_user_clicked(self, frame, user): # Unhighlight the previously selected user if self.selected_label is not None and self.widget_exists(self.selected_label): self.unhighlight_frame(self.selected_label) # Update the selected user GlobalVars.selected_user = user['id'] self.selected_label = frame # Highlight the newly selected user self.highlight_frame(frame) self.update_done_button_state() # Print debug information print(f"User clicked: {user['id']}") print(f"Selected label: {self.selected_label}") print(f"Global selected user: {GlobalVars.selected_user}") def highlight_frame(self, frame): frame.configure(bg="light blue") for child in frame.winfo_children(): child.configure(bg="light blue") print(f"Highlighted frame: {frame}") def unhighlight_frame(self, frame): frame.configure(bg="#bcfef9") for child in frame.winfo_children(): child.configure(bg="#bcfef9") print(f"Unhighlighted frame: {frame}") def update_users_list(self): search_text = self.entry.get().lower() self.filtered_users = [user for user in self.users if search_text in user['id'].lower() or search_text in self.unescape_unicode(user.get('alias', '')).lower()] self.display_users() # Preserve selection after filtering if GlobalVars.selected_user: for widget in self.scrollable_frame.winfo_children(): if isinstance(widget, tk.Frame): id_label = widget.winfo_children()[-1] if id_label['text'] == GlobalVars.selected_user: self.highlight_frame(widget) self.selected_label = widget break def scroll_to_widget(self, widget): self.canvas.update_idletasks() self.canvas.yview_moveto(widget.winfo_y() / self.scrollable_frame.winfo_height()) def update_done_button_state(self): if GlobalVars.selected_user: self.done_button.configure(state="normal") else: self.done_button.configure(state="disabled") def widget_exists(self, widget): try: widget.winfo_exists() return True except tk.TclError: return False def refresh_users(self): try: self.users = self.get_scuttlebutt_users() self.update_users_list() self.save_users_to_file(self.users) messagebox.showinfo("Success", "User list has been refreshed and updated with aliases.") except Exception as e: print(f"An error occurred while refreshing the user list: {e}") messagebox.showerror("Error", "An error occurred while refreshing the user list. Please try again later.") def get_scuttlebutt_users(self): try: result = subprocess.run(['node', 'scuttlebot.js'], capture_output=True, text=True, timeout=60) if result.returncode == 0: users = json.loads(result.stdout) return users else: print(f"Command failed with error: {result.stderr}") return [] except subprocess.TimeoutExpired: print("Command timed out after 60 seconds") return [] except json.JSONDecodeError: print("Failed to parse JSON output") return [] except Exception as e: print(f"An unexpected error occurred: {str(e)}") return [] def get_users_from_file(self): try: with open('users.json') as f: users = json.load(f) return users except FileNotFoundError: return [] def save_users_to_file(self, users): with open('users.json', 'w') as f: json.dump(users, f) @staticmethod def unescape_unicode(s): return s.encode('utf-8').decode('unicode_escape') #take photo of item class Screen3(tk.Frame): def __init__(self, master=None): tk.Frame.__init__(self, master, bg='#bcfef9') # Create the "Start Over" button home_button = tk.Button(text="Start Over", command=self.show_warning_dialog, bg='peach puff', font=GlobalVars.BUTTON_FONT) home_button.place(x=0, y=0) # top-left corner self.vid = cv2.VideoCapture(2) self.is_capturing = True self.freeze_frame = None # Video feed self.canvas = tk.Canvas(self, width=self.vid.get(cv2.CAP_PROP_FRAME_WIDTH), height=self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.canvas.pack(side="left") # Info and button on the right self.text_frame = tk.Frame(self, bg='#bcfef9') self.text_frame.pack(side="right", fill="both", expand=True) tk.Label(self.text_frame, text="Now we will take a picture of your item to show up on Scuttlebutt.\n\nIf you tap Take Photo a second time it will re-take the photo\nbut wont show you a preview during the countdown (this is a bug)", font=("Helvetica", 16), bg='#bcfef9').pack(pady=10) self.button = tk.Button(self.text_frame, text="Take Photo", command=self.take_photo, height=3, width=37, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.button.pack(pady=10) self.done_button = tk.Button(self, text="Done", command=self.done, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.done_button.place(relx=0.9, rely=0.9, anchor='se') self.update_image() def show_warning_dialog(self): result = messagebox.askokcancel("Warning", "If you start over, you will lose all information entered so far. Are you sure you want to start over?", icon='warning') if result: self.homer() def homer(self): self.__del__() self.master.switch_frame(Screen0) self.vid = cv2.VideoCapture(2) self.is_capturing = True self.freeze_frame = None # Video feed self.canvas = tk.Canvas(self, width=self.vid.get(cv2.CAP_PROP_FRAME_WIDTH), height=self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.canvas.pack(side="left") # Info and button on the right self.text_frame = tk.Frame(self, bg='#bcfef9') self.text_frame.pack(side="right", fill="both", expand=True) tk.Label(self.text_frame, text="Now we will take a picture of your item to show up on Scuttlebutt.\n\nIf you tap Take Photo a second time it will re-take the photo\nbut wont show you a preview during the countdown (this is a bug)", font=("Helvetica", 16), bg='#bcfef9').pack(pady=10) self.button = tk.Button(self.text_frame, text="Take Photo", command=self.take_photo, height=3, width=37, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.button.pack(pady=10) self.done_button = tk.Button(self, text="Done", command=self.done, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.done_button.place(relx=0.9, rely=0.9, anchor='se') self.update_image() def homer(self): self.__del__() self.master.switch_frame(Screen0) #take photo of item class Screen3(tk.Frame): def __init__(self, master=None): tk.Frame.__init__(self, master, bg='#bcfef9') # Create the "Start Over" button home_button = tk.Button(text="Start Over", command=self.show_warning_dialog, bg='peach puff', font=GlobalVars.BUTTON_FONT) home_button.place(x=0, y=0) # top-left corner self.vid = cv2.VideoCapture(2) self.is_capturing = True self.freeze_frame = None self.countdown_text = None self.last_photo = None # Video feed self.canvas = tk.Canvas(self, width=self.vid.get(cv2.CAP_PROP_FRAME_WIDTH), height=self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.canvas.pack(side="left") # Info and button on the right self.text_frame = tk.Frame(self, bg='#bcfef9') self.text_frame.pack(side="right", fill="both", expand=True) # tk.Label(self.text_frame, text="Take a photo of your item", font=("Helvetica", 16), bg='#bcfef9').pack(pady=10) self.button = tk.Button(self.text_frame, text="Take Photo", command=self.take_photo, height=3, width=37, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.button.pack(pady=10) self.done_button = tk.Button(self, text="Done", command=self.done, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.done_button.place(relx=0.9, rely=0.9, anchor='se') self.update_image() # Bind the destroy event to release resources self.bind("", self.on_destroy) def show_warning_dialog(self): result = messagebox.askokcancel("Warning", "If you start over, you will lose all information entered so far. Are you sure you want to start over?", icon='warning') if result: self.start_over() def start_over(self): self.release_resources() self.master.switch_frame(Screen0) def update_image(self): if self.is_capturing and self.vid.isOpened(): ret, frame = self.vid.read() if ret: self.cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) self.img = Image.fromarray(self.cv2image) self.imgtk = ImageTk.PhotoImage(image=self.img) self.canvas.delete("all") # Clear previous image self.canvas.create_image(0, 0, image=self.imgtk, anchor='nw') elif self.last_photo: self.canvas.delete("all") self.canvas.create_image(0, 0, image=self.last_photo, anchor='nw') if self.countdown_text: self.canvas.create_text(self.canvas.winfo_width() // 2, self.canvas.winfo_height() // 2, text=self.countdown_text, fill="white", font=("Helvetica", 120)) self.after(10, self.update_image) def take_photo(self): self.is_capturing = True self.countdown_text = None countdown_thread = threading.Thread(target=self.countdown) countdown_thread.start() def countdown(self): for i in range(3, 0, -1): self.countdown_text = str(i) time.sleep(1) self.countdown_text = None # Capture the current frame ret, frame = self.vid.read() if ret: rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) pil_image = Image.fromarray(rgb_image) pil_image.save('freeze_frame.jpg') self.display_taken_photo() self.is_capturing = False def display_taken_photo(self): image = Image.open('freeze_frame.jpg') self.last_photo = ImageTk.PhotoImage(image) self.canvas.delete("all") # Clear the canvas self.canvas.create_image(0, 0, image=self.last_photo, anchor='nw') def done(self): global migration_ticket global MIGRATION_ITEMS_DIR # If migration_ticket is set, copy the photo to MIGRATION_ITEMS_DIR if migration_ticket: source_path = 'freeze_frame.jpg' destination_path = os.path.join(MIGRATION_ITEMS_DIR, f"{migration_ticket}.jpg") try: shutil.copy2(source_path, destination_path) print(f"Photo saved as {destination_path}") except Exception as e: print(f"Error saving photo: {e}") self.release_resources() self.master.switch_frame(Screen5) def release_resources(self): self.is_capturing = False if self.vid.isOpened(): self.vid.release() def on_destroy(self, event): self.release_resources() def __del__(self): self.release_resources() # draw a sticker class Screen4(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') # Configure column minsizes self.grid_columnconfigure(0, minsize=675) self.grid_columnconfigure(1, minsize=100) self.grid_columnconfigure(2, minsize=100) # Creating a frame for the left side of the screen for drawing and instructions self.left_frame = tk.Frame(self, bg='#bcfef9') self.left_frame.grid(row=0, column=0, padx=2) # Frame for the tools self.right_frame = tk.Frame(self, bg='#bcfef9') self.right_frame.grid(row=0, column=2, padx=70) # Add Import Image Button tk.Button(self.left_frame, text="Import Image", command=self.import_image, height=2, width=15, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # Add instructions self.label = tk.Label(self.left_frame, text="You may now draw your sticker :)", wraplength=650, font=GlobalVars.TEXT_FONT) self.label.pack(pady=2) # Drawing area self.drawing = Image.new('1', (650, 360), 1) self.draw = ImageDraw.Draw(self.drawing) self.last_draw = None # Set initial drawing color to black and size to 1 self.draw_color = 'black' self.draw_size = 1 # Creating the Canvas for drawing self.canvas = Canvas(self.left_frame, width=650, height=360, bg='white') self.canvas.bind("", self.draw_line) self.image_on_canvas = None self.canvas.pack(pady=20) self.canvas.bind("", self.reset_last_draw) self.add_qr_box() # Create frames for pen size and color tools self.pen_size_frame = tk.Frame(self.right_frame, bg='#bcfef9') self.pen_size_frame.pack(pady=(0, 20)) self.pen_color_frame = tk.Frame(self.right_frame, bg='#bcfef9') self.pen_color_frame.pack(pady=(0, 20)) # Pen size label tk.Label(self.pen_size_frame, text="Pen Size", font=GlobalVars.TEXT_FONT, bg='#bcfef9').pack() # Add Draw Size buttons pen_sizes = [(".", 1), ("*", 2), ("⚬", 3), ("⬤", 4), ("⬛", 5)] for i, (text, size) in enumerate(pen_sizes): tk.Button(self.pen_size_frame, text=text, command=lambda s=size: self.set_draw_size(s), height=2, width=5, bg='peach puff').pack(pady=2) # Pen color label tk.Label(self.pen_color_frame, text="Pen Color", font=GlobalVars.TEXT_FONT, bg='#bcfef9').pack() # Creating color buttons colors = ['black', 'gray', 'white'] for color in colors: tk.Button(self.pen_color_frame, height=2, width=5, bg=color, command=lambda c=color: self.set_draw_color(c)).pack(pady=2) # Add Clear Drawing Button tk.Button(self.right_frame, text="Clear Drawing", command=self.clear_drawing, height=2, width=15, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # Done button tk.Button(self.right_frame, text="Done", command=self.next, height=3, width=10, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # Adding a home button master.add_home_button(self) # Define the info_label to display QRX, QRY, and QRscale values self.info_label = tk.Label(self.right_frame, text="", bg='#bcfef9', font=GlobalVars.TEXT_FONT) self.info_label.pack(pady=5) def draw_line(self, event): x, y = event.x, event.y if self.last_draw: points = self.get_points_on_line(*self.last_draw, x, y) for px, py in points: if self.draw_color == 'gray': self.draw_dithered_point(px, py) else: self.draw_point(px, py) self.last_draw = (x, y) def get_points_on_line(self, x0, y0, x1, y1): points = [] dx = abs(x1 - x0) dy = abs(y1 - y0) sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy while True: points.append((x0, y0)) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy: err -= dy x0 += sx if e2 < dx: err += dx y0 += sy return points def draw_point(self, x, y): color = 0 if self.draw_color == 'black' else 1 for dx in range(self.draw_size): for dy in range(self.draw_size): self.canvas.create_rectangle(x+dx, y+dy, x+dx+1, y+dy+1, fill=self.draw_color, outline='') self.draw.point((x+dx, y+dy), fill=color) def draw_dithered_point(self, x, y): for dx in range(self.draw_size): for dy in range(self.draw_size): if (x+dx+y+dy) % 2 == 0: self.canvas.create_rectangle(x+dx, y+dy, x+dx+1, y+dy+1, fill='black', outline='') self.draw.point((x+dx, y+dy), fill=0) else: self.canvas.create_rectangle(x+dx, y+dy, x+dx+1, y+dy+1, fill='white', outline='') self.draw.point((x+dx, y+dy), fill=1) def next(self): self.drawing.save("drawing.png") self.master.switch_frame(Screen13) def reset_last_draw(self, event): self.last_draw = None def set_draw_color(self, color): self.draw_color = color def set_draw_size(self, size): self.draw_size = size def clear_drawing(self): self.canvas.delete("all") self.drawing = Image.new('1', (650, 360), 1) self.draw = ImageDraw.Draw(self.drawing) self.add_qr_box() def add_qr_box(self, x=506, y=217, size=1): box_size = 37 * size self.canvas.create_rectangle(x, y, x + box_size, y + box_size, outline='black', fill='white') self.canvas.create_text(x + box_size/2, y + box_size/2, text="QR", fill="black") def import_image(self): file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")]) if not file_path: return file_parts = file_path.split('-')[-4:] if len(file_parts) < 4: messagebox.showerror("Filename Error", "The filename does not follow the expected pattern.") return try: QRX = int(file_parts[1]) QRY = int(file_parts[2]) QRscale = int(file_parts[3].split('.')[0]) self.master.QRX = QRX self.master.QRY = QRY self.master.QRscale = QRscale self.info_label.config(text=f"QRX: {QRX}, QRY: {QRY}, QRscale: {QRscale}") except ValueError as e: messagebox.showerror("Filename Error", "The filename does not follow the expected pattern.") return self.canvas.delete("all") img = Image.open(file_path) img = img.convert('1') if img.size[0] > 675 or img.size[1] > 375: img = img.crop((0, 0, 650, 360)) self.add_qr_box(QRX, QRY, QRscale) self.drawing.paste(img) self.imported_img = ImageTk.PhotoImage(img) self.image_on_canvas = self.canvas.create_image(0, 0, image=self.imported_img, anchor='nw') self.add_qr_box(QRX, QRY, QRscale) # typed description class Screen5(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Adding the information label self.info_label = tk.Label(self, text="Please enter a description of your item.", font=GlobalVars.TEXT_FONT, wraplength=500) self.info_label.pack(pady=70) # Adding the text entry field self.info_entry = tk.Text(self, height=10, width=50, font=("Helvetica", 16)) self.info_entry.pack(pady=10) # Adding the done button self.done_button = tk.Button(self, text="Done", command=self.save_info_and_switch, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT) self.done_button.pack(pady=10) # Setting the focus to the text entry field self.info_entry.focus_set() def save_info_and_switch(self): global info_text global migration_ticket global MIGRATION_ITEMS_DIR info_text = self.info_entry.get("1.0", "end-1c") info_text = info_text.replace('\n', '\\n') # If migration_ticket is set (not empty), save the text and image to MIGRATION_ITEMS_DIR if migration_ticket: # Save text text_file_path = os.path.join(MIGRATION_ITEMS_DIR, f"{migration_ticket}.txt") try: with open(text_file_path, 'w') as f: f.write(info_text) print(f"Text saved as {text_file_path}") except Exception as e: print(f"Error saving text: {e}") # Save image source_image_path = 'freeze_frame.jpg' destination_image_path = os.path.join(MIGRATION_ITEMS_DIR, f"{migration_ticket}.jpg") try: shutil.copy2(source_image_path, destination_image_path) print(f"Photo saved as {destination_image_path}") except Exception as e: print(f"Error saving photo: {e}") self.master.switch_frame(Screen11) # I understand class Screen6(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) tk.Button(self, text="I Understand", command=lambda: master.switch_frame(Screen3), height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # create user not implemented lol class Screen7(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Assume there's a method to manage the text entry self.info_label = tk.Label(self, text="Hiii sorry this hasn't been implemented yet!", font=("Helvetica", 16), wraplength=500) self.info_label.pack() #tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen6), height=3, width=30, bg='peach puff').pack(pady=10) # draw a ribbon tag class Screen8(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') # Original dimensions self.original_width = 325 self.original_height = 179 # Doubled dimensions for display self.display_width = self.original_width * 2 self.display_height = self.original_height * 2 # Grid size (2x2 pixels) self.grid_size = 2 # Add the home button master.add_home_button(self) # Main container to hold all elements main_container = tk.Frame(self, bg='#bcfef9') main_container.place(relx=0.5, rely=0.5, anchor='center') # Left frame for drawing area and import button left_frame = tk.Frame(main_container, bg='#bcfef9') left_frame.pack(side='left', padx=(0, 20)) # Right frame for tools and buttons right_frame = tk.Frame(main_container, bg='#bcfef9') right_frame.pack(side='right') # Import Image Button tk.Button(left_frame, text="Import Image", command=self.import_image, height=2, width=15, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # Simplified instructions self.label = tk.Label(left_frame, text="You may now draw your ribbon :)", wraplength=300, font=GlobalVars.TEXT_FONT, bg='#bcfef9') self.label.pack(pady=10) # Drawing area (doubled size for display) self.drawing = Image.new('1', (self.original_width, self.original_height), 1) self.draw = ImageDraw.Draw(self.drawing) self.last_draw = None # Set initial drawing color to black and size to 1 self.draw_color = 'black' self.draw_size = 1 # Canvas for drawing (doubled size for display) self.canvas = Canvas(left_frame, width=self.display_width, height=self.display_height, bg='white') self.canvas.bind("", self.draw_line) self.canvas.bind("", self.reset_last_draw) self.canvas.pack(pady=10) # Pen size frame pen_size_frame = tk.Frame(right_frame, bg='#bcfef9') pen_size_frame.pack(pady=10) tk.Label(pen_size_frame, text="Pen Size", font=GlobalVars.TEXT_FONT, bg='#bcfef9').pack() # Pen size buttons pen_sizes = [(".", 1), ("*", 2), ("⚬", 3), ("⬤", 4), ("⬛", 5)] for text, size in pen_sizes: tk.Button(pen_size_frame, text=text, command=lambda s=size: self.set_draw_size(s), height=2, width=5, bg='peach puff').pack(pady=2) # Pen color frame pen_color_frame = tk.Frame(right_frame, bg='#bcfef9') pen_color_frame.pack(pady=10) tk.Label(pen_color_frame, text="Pen Color", font=GlobalVars.TEXT_FONT, bg='#bcfef9').pack() # Color buttons colors = ['black', 'gray', 'white'] for color in colors: tk.Button(pen_color_frame, height=2, width=5, bg=color, command=lambda c=color: self.set_draw_color(c)).pack(pady=2) # Clear Drawing Button tk.Button(right_frame, text="Clear Drawing", command=self.clear_drawing, height=2, width=15, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # Done button tk.Button(right_frame, text="Done", command=self.next, height=3, width=10, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) def draw_line(self, event): x, y = event.x // self.grid_size, event.y // self.grid_size if self.last_draw: points = self.get_points_on_line(*self.last_draw, x, y) for px, py in points: if self.draw_color == 'gray': self.draw_dithered_point(px, py) else: self.draw_point(px, py) self.last_draw = (x, y) def get_points_on_line(self, x0, y0, x1, y1): points = [] dx = abs(x1 - x0) dy = abs(y1 - y0) sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy while True: points.append((x0, y0)) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy: err -= dy x0 += sx if e2 < dx: err += dx y0 += sy return points def draw_point(self, x, y): color = 0 if self.draw_color == 'black' else 1 for dx in range(self.draw_size): for dy in range(self.draw_size): # Draw on the display canvas (2x2 pixels) self.canvas.create_rectangle( (x+dx)*self.grid_size, (y+dy)*self.grid_size, (x+dx+1)*self.grid_size, (y+dy+1)*self.grid_size, fill=self.draw_color, outline='' ) # Draw on the actual image (1x1 pixel) self.draw.point((x+dx, y+dy), fill=color) def draw_dithered_point(self, x, y): for dx in range(self.draw_size): for dy in range(self.draw_size): if (x+dx+y+dy) % 2 == 0: # Draw on the display canvas (2x2 pixels) self.canvas.create_rectangle( (x+dx)*self.grid_size, (y+dy)*self.grid_size, (x+dx+1)*self.grid_size, (y+dy+1)*self.grid_size, fill='black', outline='' ) # Draw on the actual image (1x1 pixel) self.draw.point((x+dx, y+dy), fill=0) else: # Draw on the display canvas (2x2 pixels) self.canvas.create_rectangle( (x+dx)*self.grid_size, (y+dy)*self.grid_size, (x+dx+1)*self.grid_size, (y+dy+1)*self.grid_size, fill='white', outline='' ) # Draw on the actual image (1x1 pixel) self.draw.point((x+dx, y+dy), fill=1) def reset_last_draw(self, event): self.last_draw = None def set_draw_color(self, color): self.draw_color = color def set_draw_size(self, size): self.draw_size = size def clear_drawing(self): self.canvas.delete("all") self.drawing = Image.new('1', (self.original_width, self.original_height), 1) self.draw = ImageDraw.Draw(self.drawing) def import_image(self): file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")]) if not file_path: return img = Image.open(file_path) img = img.convert('1') if img.size != (self.original_width, self.original_height): img = img.resize((self.original_width, self.original_height), Image.LANCZOS) self.drawing = img self.draw = ImageDraw.Draw(self.drawing) # Display the image at 2x size display_img = img.resize((self.display_width, self.display_height), Image.NEAREST) self.imported_img = ImageTk.PhotoImage(display_img) self.canvas.delete("all") self.canvas.create_image(0, 0, image=self.imported_img, anchor='nw') def next(self): # The drawing is already at the correct size, so we can save it directly self.drawing.save("drawing.png") self.master.switch_frame(Screen13) # txt update class Screen9(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Assume there's a method to manage the text entry tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen10), height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) #THX BYE class Screen10(tk.Frame): def __init__(self, master): GlobalVars.selected_user = None # Reset the selected user tk.Frame.__init__(self, master, bg='#bcfef9') tk.Label(self, text="Thank you!", bg='#bcfef9', font=('Helvetica', 48)).pack() tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen0), height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=10) # Sticker or tag? class Screen11(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Instructions self.label = tk.Label(self, text="Which type of tag would you like to design?", wraplength=400, # adjust to suit needs font=GlobalVars.TEXT_FONT) self.label.pack(pady=50) # Button functions def select_ribbon(): global print_type print_type = 'ribbon' master.switch_frame(Screen8) def select_sticker(): global print_type print_type = 'sticker' master.switch_frame(Screen4) # Buttons tk.Button(self, text="Sticker", command=select_sticker, height=4, width=39, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(side='top', pady=30) tk.Button(self, text="Ribbon tag", command=select_ribbon, height=4, width=39, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(side='top', pady=30) # after QR scanned for lookup class Screen12(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') print(f"Initializing Screen12 with QR code value: {GlobalVars.qr_code_value}") if GlobalVars.qr_code_value is None: self.display_error("Error: QR code value is None") return try: qr_code_str = GlobalVars.qr_code_value if isinstance(GlobalVars.qr_code_value, str) else GlobalVars.qr_code_value.decode('utf-8') print(f"Attempting to get message content for: {qr_code_str}") message_content, image_path, replies = addtoDB.get_message_content(qr_code_str) print(f"Received message content: {message_content}") print(f"Received image path: {image_path}") print(f"Received replies: {replies}") if message_content is None: self.display_error(f"Error: Failed to retrieve message content for {qr_code_str}") else: self.display_content(message_content, image_path, replies) except Exception as e: self.display_error(f"An error occurred while processing message: {str(e)}") def display_error(self, message): print(f"Displaying error: {message}") error_frame = tk.Frame(self, bg='#bcfef9') error_frame.pack(expand=True, fill='both', padx=20, pady=20) tk.Label(error_frame, text=message, bg='#bcfef9', font=GlobalVars.TEXT_FONT, wraplength=500).pack(pady=50) # Add "Go Back" button for error cases tk.Button(error_frame, text="Go Back", command=lambda: self.master.switch_frame(Screen0), height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=20) def get_alias(self, user_id): try: with open('users.json') as f: users = json.load(f) for user in users: if user['id'] == user_id: return user.get('alias', '') except FileNotFoundError: return '' return '' def display_content(self, message_content, image_path, replies): # Main content frame content_frame = tk.Frame(self, bg='#bcfef9') content_frame.pack(expand=True, fill='both', padx=20, pady=10) # Left column: Image left_column = tk.Frame(content_frame, bg='#bcfef9') left_column.pack(side='left', fill='both', expand=False) if image_path and os.path.exists(image_path): try: img = Image.open(image_path) img.thumbnail((550, 550)) # Slightly smaller than before photo = ImageTk.PhotoImage(img) img_label = tk.Label(left_column, image=photo, bg='#bcfef9') img_label.image = photo # Keep a reference img_label.pack(expand=True, fill='both') print(f"Displayed image: {image_path}") except Exception as e: print(f"Error displaying image {image_path}: {e}") elif image_path: print(f"Image file not found: {image_path}") else: print("No image path provided") # Right column: Scrollable text right_column = tk.Frame(content_frame, bg='#bcfef9') right_column.pack(side='right', fill='both', padx=10, expand=True) canvas = tk.Canvas(right_column, bg='#bcfef9') scrollbar = ttk.Scrollbar(right_column, orient="vertical", command=canvas.yview) scrollable_frame = tk.Frame(canvas, bg='#bcfef9') scrollable_frame.bind( "", lambda e: canvas.configure(scrollregion=canvas.bbox("all")) ) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) # Display original message text_content = message_content.get('content', {}).get('text', '') # Remove markdown image syntax text_content = re.sub(r'!\[.*?\]\(.*?\)', '', text_content).strip() tk.Label(scrollable_frame, text=text_content, wraplength=650, justify='left', bg='#bcfef9', font=("Helvetica", 28)).pack(pady=5, padx=(5, 0)) # Display replies if replies: tk.Label(scrollable_frame, text="Replies:", wraplength=650, justify='left', bg='#bcfef9', font=("Helvetica", 24, "bold")).pack(pady=(20, 5), padx=(5, 0)) for reply in replies: author_id = reply.get('value', {}).get('author', 'Unknown') author_alias = self.get_alias(author_id) author_display = f"{author_alias} ({author_id})" if author_alias else author_id reply_text = reply.get('value', {}).get('content', {}).get('text', '') tk.Label(scrollable_frame, text=f"{author_display}:", wraplength=650, justify='left', bg='#bcfef9', font=("Helvetica", 20, "bold")).pack(pady=(10, 0), padx=(5, 0)) tk.Label(scrollable_frame, text=reply_text, wraplength=650, justify='left', bg='#bcfef9', font=("Helvetica", 18)).pack(pady=(0, 10), padx=(5, 0)) canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") # Configure the scrollbar style to make it larger style = ttk.Style() style.configure("Vertical.TScrollbar", arrowsize=48, width=48) # Add "Done" button for successful content display tk.Button(self, text="Done", command=lambda: self.master.switch_frame(Screen0), height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(side="bottom", pady=20) #time to print class Screen13(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Create a container to hold the widgets container = tk.Frame(self) container.place(relx=0.5, rely=0.5, anchor='center') # instructions tk.Label(container, text="Wonderful! It is now time to post your item to Scuttlebutt and to print your tag. You can still cancel by hitting Start Over if you like.", wraplength=600, font=GlobalVars.TEXT_FONT).grid(row=0, column=0, columnspan=2) # buttons master.add_home_button(self) tk.Button(container, text="Print", command=self.printy, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).grid(row=2, column=0, pady=20) # go ahead and print the thing def printy(self): global print_type, migration_ticket, MIGRATION_ITEMS_DIR # Specify the path to your image file path_to_image = "/home/trav/Documents/custodiosk/freeze_frame.jpg" # Get QR data from the main application QRX = self.master.QRX QRY = self.master.QRY QRscale = self.master.QRscale # make ssb post key = addtoDB.addToSSB(path_to_image, info_text, 1) # If we have a migration ticket, append the message ID to the description file if migration_ticket: description_file_path = os.path.join(MIGRATION_ITEMS_DIR, f"{migration_ticket}.txt") try: with open(description_file_path, 'a') as f: f.write(f"\n\n\n{key}") print(f"Appended message ID to description file: {description_file_path}") except Exception as e: print(f"Error appending message ID to description file: {e}") # ssb give! (make sure we have a UID to give to first) if GlobalVars.selected_user and GlobalVars.selected_user.strip() != "": nothing = addtoDB.addToSSB(GlobalVars.selected_user, key, 2) # gonna need to revise this later but for now the textile tags are always full-size QR: if print_type == "ribbon": QRscale = 7 # Create qr code #from https://ourcodeworld.com/articles/read/554/how-to-create-a-qr-code-image-or-svg-in-python qr = qrcode.QRCode( version = 1, error_correction = qrcode.constants.ERROR_CORRECT_H, box_size = QRscale, border = 0, ) # Add data qr.add_data(key) qr.make(fit=True) # Create an image from the QR Code instance img = qr.make_image() whereToSaveQR = 'qr.jpg' img.save(whereToSaveQR) # compose image for tag drawing = Image.open("drawing.png") # drawing qr = Image.open("qr.jpg") # qr #### merge em ## if sticker if print_type == "sticker": ## if we didn't custom set X/Y, set to defaults if QRX is None and QRY is None: QRX=506 QRY=217 merged_image = Image.new('L', (675, 375), "white") merged_image.paste(drawing, (0, 8)) merged_image.paste(qr, (QRX, QRY+8)) # we add 8 because this is slightly off from when we drew it merged_image.save("merged_image.png") # if ribbon if print_type == "ribbon": merged_image = Image.new('L', (375, 675), "white") merged_image.paste(drawing, (25, 100)) # set it 25 in because that's the border merged_image.paste(qr, (42, 279)) # paste without mask merged_image.save("merged_image.png") image = Image.open("merged_image.png") # rotated_image = image.transpose(Image.ROTATE_270) # Transpose and rotate 90 degrees, old version when we weren't doing ribbon vertical # rotated_image.save("merged_image.png") # Get the ZPL code for the image zpl_code = tozpl.print_to_zpl("merged_image.png") #save the zpl # Open the file in write mode with open("to_print.zpl", "w") as file: # Write the string to the file file.write(zpl_code) #print(zpl_code) #only needed for testing #print (print_type) # print to sticker printer if print_type == "sticker": try: result = subprocess.Popen('lpr -P sticker_printer -o raw to_print.zpl', shell=True, stdout=subprocess.PIPE, ) # print('no print') except: print('traceback.format_exc():\n%s' % traceback.format_exc()) exit() # or print to tag printer: if print_type == "ribbon": try: result = subprocess.Popen('lpr -P tag-printer -o raw to_print.zpl', shell=True, stdout=subprocess.PIPE, ) except: print('traceback.format_exc():\n%s' % traceback.format_exc()) exit() self.master.switch_frame(Screen10) # Switching to Screen10 after Done # lookup item class Screen14(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='white') # Main container self.main_container = tk.Frame(self, bg='white') self.main_container.pack(fill='both', expand=True) # Video feed frame (left side) self.video_frame = tk.Frame(self.main_container, bg='white') self.video_frame.pack(side='left', fill='both', expand=True, padx=20, pady=20) # Instruction frame (right side) self.instruction_frame = tk.Frame(self.main_container, bg='white') self.instruction_frame.pack(side='right', fill='both', expand=True, padx=20, pady=20) # Setup the video feed self.video = tk.Label(self.video_frame, bg='white') self.video.pack(fill='both', expand=True) # Setup the instruction self.instruction = tk.Label(self.instruction_frame, text="Hold the QR code up to the camera", font=("Helvetica", 36), bg='white', fg='black', wraplength=500, justify='center') self.instruction.pack(fill='both', expand=True) # Cancel button self.cancel_button = tk.Button(self.instruction_frame, text="Cancel", command=lambda: self.master.switch_frame(Screen0), height=3, width=30, bg='white', fg='black', font=GlobalVars.BUTTON_FONT) self.cancel_button.pack(pady=20) self.qreader = QReader() self.cap = cv2.VideoCapture(2) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) self.frame_count = 0 self.update_frame() def update_frame(self): ret, frame = self.cap.read() self.frame_count += 1 if ret: # Process every 3rd frame for QR detection if self.frame_count % 3 == 0: # Convert to grayscale for QR detection gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) decoded_text = self.qreader.detect_and_decode(image=gray) if decoded_text: if isinstance(decoded_text, tuple) and len(decoded_text) > 0: qr_value = next((item for item in decoded_text if item is not None), None) if qr_value: GlobalVars.qr_code_value = qr_value print(f"QR code scanned: {GlobalVars.qr_code_value}") self.cap.release() self.master.switch_frame(Screen12) return # Display the original frame rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) image = Image.fromarray(rgb_frame) imgtk = ImageTk.PhotoImage(image=image) self.video.imgtk = imgtk self.video.configure(image=imgtk) self.after(10, self.update_frame) def destroy(self): if self.cap.isOpened(): self.cap.release() super().destroy() # New screen for Create Migration Item class Screen15(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') # Create a main container frame main_frame = tk.Frame(self, bg='#bcfef9') main_frame.place(relx=0.5, rely=0.5, anchor='center') # Add the home button to the main frame (this will be at the top) master.add_home_button(self) # Instructions (now in the centered main frame) tk.Label(main_frame, text="If you're archiving an Item of Migration, please enter the last 3 digits of your ticket number. Otherwise, you can leave this blank.", font=GlobalVars.TEXT_FONT, bg='#bcfef9', wraplength=500).pack(pady=20) # Text box for entering ticket number (now in the centered main frame) self.ticket_entry = tk.Entry(main_frame, font=GlobalVars.TEXT_FONT) self.ticket_entry.pack(pady=20) # Done button (now in the centered main frame) tk.Button(main_frame, text="Done", command=self.process_ticket, height=3, width=30, bg='peach puff', font=GlobalVars.BUTTON_FONT).pack(pady=20) # Set focus to the ticket entry field self.ticket_entry.focus_set() def process_ticket(self): global migration_ticket ticket = self.ticket_entry.get().strip() if ticket == "": # If the field is blank, proceed without setting migration_ticket migration_ticket = "" self.master.switch_frame(Screen1) # Go to the Scuttlebutt username selection screen elif re.match(r'^\d{1,4}$', ticket): if self.is_ticket_available(ticket): migration_ticket = ticket self.master.switch_frame(Screen1) # Go to the Scuttlebutt username selection screen else: messagebox.showerror("Invalid Input", "This migration number is already in use. Please try a different number.") else: messagebox.showerror("Invalid Input", "Please check your number and try again. It should be a 1-4 digit number or left blank.") def is_ticket_available(self, ticket): # Get all files in the migration items directory files = os.listdir(MIGRATION_ITEMS_DIR) # Strip file extensions and check if the ticket number exists existing_numbers = [os.path.splitext(f)[0] for f in files] return ticket not in existing_numbers if __name__ == "__main__": app = Kiosk() app.mainloop()