## this is the custodisco kiosk code for dweb camp 2023 import tkinter as tk from tkinter import font as tkfont from tkinter import Canvas, ttk, Text, filedialog, messagebox from PIL import Image, ImageTk, ImageDraw import tozpl import subprocess import threading import json import os import cv2 import time import qrcode import addtoDB from pyzbar.pyzbar import decode qr_code_value = None # global variable to store QR code value print_type = "neither" # print type when printing class GlobalVars: selected_user = 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 globals()['BUTTON_FONT'] = tkfont.Font(size=24, family='Helvetica') globals()['TEXT_FONT'] = tkfont.Font(size=20, 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 back_frame(self): if self.frames_history: self.switch_frame(self.frames_history.pop(), keep_history=False) def add_home_button(self, frame): # Create the "Start Over" button home_button = tk.Button(frame, text="Start Over from the beginning", command=lambda: self.switch_frame(Screen0), bg='peach puff', width=24, font=BUTTON_FONT) home_button.place(x=0, y=0) # top-left corner # home screen class Screen0(tk.Frame): def __init__(self, master): global BUTTON_FONT global TEXT_FONT 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', 48)) # dark purple color title_label.pack(side='top', pady=20) # adjust to your needs # Welcome message on the left side welcome_text = """If you have an item that is special to you, this kiosk allows you to create a tag for your item. Build your relationship with your item by committing to mend and care for it. If you put your contact info on the tag the item may be returned to you if lost. You can use your fingers to interact with the kiosk but also there is a stylus that can help with drawing. More info at https://teafry.me/custodisco""" welcome_label = tk.Label(left_frame, text=welcome_text, bg='#bcfef9', font=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=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=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=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="Do you have a Scuttlebutt account? If no just tap Yes and then Done on the next screen, it's fine.", font=TEXT_FONT) label.pack(pady=60) tk.Button(self, text="Yes", command=lambda: master.switch_frame(Screen2), height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=50) tk.Button(self, text="No, I Want to Make One Now (not implemented yet)", command=lambda: master.switch_frame(Screen7), height=3, width=50, bg='peach puff', font=BUTTON_FONT).pack(pady=50) # find yourself in list of ssb users class Screen2(tk.Frame): selected_user = None # This is the global variable to store selected user global BUTTON_FONT global TEXT_FONT def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') self.selected_label = None # This is the global variable to store selected label # Create a new frame at the top for the label and text box self.top_frame = tk.Frame(self) self.top_frame.pack(side="top", fill="x", pady=60) # Add a label and text box to the top frame self.label = tk.Label(self.top_frame, text="Start typing your public key to find yourself in the list then click on your key to select it.\n\nIf you can't find your name it might help to follow the Custodisco Scuttlebutt account.", font=TEXT_FONT) self.label.pack(side="left") self.entry = tk.Entry(self.top_frame, font=TEXT_FONT) self.entry.bind('', lambda e: self.update_users_list()) self.entry.pack(side="left") # Focus on the entry box self.entry.focus_set() # Create container for user list self.container = ttk.Frame(self, height=500) # Define a height here self.container.pack(fill='both', expand=True, padx=20, pady=20) # Initialize users list from users.json self.users = self.get_users_from_file() self.update_users_list() # Highlight selected user if one exists if GlobalVars.selected_user is not None: for widget in self.container.winfo_children(): if isinstance(widget, tk.Button) and widget['text'] == GlobalVars.selected_user: widget.configure(relief=tk.SUNKEN, bg="light blue") self.selected_label = widget # The 'Done' button to navigate to next screen self.done_button = tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen3), height=3, width=30, bg='peach puff', font=BUTTON_FONT) self.done_button.pack(side="bottom", padx=20, pady=10) # The 'Refresh List' button self.refresh_button = tk.Button(self, text="Refresh List (takes 30 seconds or so)", command=self.refresh_users, height=3, width=30, bg='peach puff', font=BUTTON_FONT) self.refresh_button.pack(side="bottom", padx=20, pady=10) master.add_home_button(self) def refresh_users(self): try: # Update users list from Scuttlebutt and display it self.users = self.get_scuttlebutt_users() self.update_users_list() 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): result = subprocess.run(['node', 'scuttlebot.js'], capture_output=True, text=True) if result.returncode == 0: users = json.loads(result.stdout) return users else: raise Exception("Command failed: " + result.stderr) def get_users_from_file(self): with open('users.json') as f: users = json.load(f) return users def update_users_list(self): # Remove all widgets from container for widget in self.container.winfo_children(): widget.destroy() # Filter users based on search text search_text = self.entry.get().lower() filtered_users = [user for user in self.users if search_text in user['id'].lower()] # Display filtered users self.display_users(filtered_users) def display_users(self, users): # Scrollable list of users canvas = tk.Canvas(self.container, width=460) # Decrease the width by scrollbar's width style = ttk.Style() style.configure("Vertical.TScrollbar", gripcount=0, arrowsize=15, width=40) # Adjust width here scrollbar = ttk.Scrollbar(self.container, orient='vertical', command=canvas.yview, style="Vertical.TScrollbar") scrollable_frame = ttk.Frame(canvas) 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) # Add user buttons to scrollable frame for user in users: button = tk.Button(scrollable_frame, text=user['id'], font=('Helvetica', 24), bd=0) # Adjust font size here button.pack(anchor="w") button.config(command=lambda button=button, user=user: self.on_user_clicked(button, user)) canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") def widget_exists(self, widget): try: widget.winfo_exists() return True except tk.TclError: return False def on_user_clicked(self, button, user): # Remove highlight from previously selected user if self.selected_label is not None and self.widget_exists(self.selected_label): self.selected_label.configure(relief=tk.FLAT, bg='SystemButtonFace') # default color # Store selected user and button GlobalVars.selected_user = user['id'] self.selected_label = button # Highlight clicked label self.selected_label.configure(relief=tk.SUNKEN, bg="light blue") #take photo of item class Screen3(tk.Frame): def __init__(self, master=None): tk.Frame.__init__(self, master, bg='#bcfef9') global BUTTON_FONT global TEXT_FONT # Create the "Start Over" button home_button = tk.Button(text="Start Over", command=self.homer, bg='peach puff', font=BUTTON_FONT) home_button.place(x=0, y=0) # top-left corner self.vid = cv2.VideoCapture(0) 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=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=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) def update_image(self): if self.is_capturing: 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.create_image(0, 0, image=self.imgtk, anchor='nw') self.after(10, self.update_image) def take_photo(self): if self.freeze_frame is not None: self.canvas.delete(self.freeze_frame) self.freeze_frame = None if not self.is_capturing: self.is_capturing = True self.update_image() countdown_thread = threading.Thread(target=self.countdown) countdown_thread.start() def countdown(self): for i in range(3, 0, -1): self.button.config(text=str(i)) time.sleep(1) self.button.config(text="Take Photo") self.is_capturing = False rgb_image = cv2.cvtColor(self.cv2image, cv2.COLOR_RGBA2RGB) pil_image = Image.fromarray(rgb_image) pil_image.save('freeze_frame.jpg') self.display_taken_photo() def display_taken_photo(self): image = Image.open('freeze_frame.jpg') photo = ImageTk.PhotoImage(image) self.freeze_frame = self.canvas.create_image(0, 0, image=photo, anchor='nw') def done(self): self.__del__() self.master.switch_frame(Screen5) def __del__(self): if self.vid.isOpened(): self.vid.release() # draw a sticker class Screen4(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') global BUTTON_FONT global TEXT_FONT # Configure column minsizes self.grid_columnconfigure(0, minsize=675) # considering 675 width + 50 padding on each side 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=BUTTON_FONT).pack(pady=10) # Add instructions self.label = tk.Label(self.left_frame, text="You may now draw your sticker :) This will be printed ~ 1.25 by 2.25 inches. You might have better results using thicker lines (see drawing tools on the right). The image you are drawing now will not be posted to Scuttlebutt.", wraplength=650, # adjust to suit needs font=TEXT_FONT) self.label.pack(pady=2) # is this the 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 # To store imported image's reference self.canvas.pack(pady=20) self.canvas.bind("", self.reset_last_draw) self.add_qr_box() # Add QR box to the canvas #Create a frame for the buttons grid self.buttons_frame = tk.Frame(self.right_frame, bg='#bcfef9') self.buttons_frame.pack(pady=50) # Define the info_label to display QRX, QRY, and QRscale values self.info_label = tk.Label(self.right_frame, text="", bg='#bcfef9', font=TEXT_FONT) self.info_label.pack(pady=5) # Use pack instead of grid # Add Draw Size buttons tk.Button(self.buttons_frame, text=".", command=lambda: self.set_draw_size(1), height=3, width=10, bg='peach puff').grid(row=0, column=0, padx=5, pady=5) tk.Button(self.buttons_frame, text="*", command=lambda: self.set_draw_size(2), height=3, width=10, bg='peach puff').grid(row=1, column=0, padx=5, pady=5) tk.Button(self.buttons_frame, text="⚬", command=lambda: self.set_draw_size(3), height=3, width=10, bg='peach puff').grid(row=2, column=0, padx=5, pady=5) tk.Button(self.buttons_frame, text="⬤", command=lambda: self.set_draw_size(4), height=3, width=10, bg='peach puff').grid(row=3, column=0, padx=5, pady=5) # Creating color buttons tk.Button(self.buttons_frame, height=5, width=10, bg='black', command=lambda: self.set_draw_color('black')).grid(row=0, column=1, padx=5, pady=5) tk.Button(self.buttons_frame, height=5, width=10, bg='white', command=lambda: self.set_draw_color('white')).grid(row=1, column=1, padx=5, pady=5) # Add label for pen color buttons self.color_label = tk.Label(self.right_frame, text="^ drawing tools ^", font=TEXT_FONT) self.color_label.pack(pady=5) # Add Clear Drawing Button tk.Button(self.right_frame, text="clear drawing", command=self.clear_drawing, height=2, width=10, bg='peach puff', font=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=BUTTON_FONT).pack(pady=10) # Adding a home button master.add_home_button(self) def draw_line(self, event): x, y = event.x, event.y if self.last_draw: self.canvas.create_line(*self.last_draw, x, y, fill=self.draw_color, width=self.draw_size) self.draw.line([*self.last_draw, x, y], fill=0 if self.draw_color == 'black' else 1, width=self.draw_size) self.last_draw = (x, y) def next(self): # Save the drawing as a .png file self.drawing.save("drawing.png") # next screen 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") # Clear canvas self.drawing = Image.new('1', (650, 360), 1) # Create new blank drawing image self.draw = ImageDraw.Draw(self.drawing) # Prepare to draw on the new blank image self.add_qr_box() # Add QR box to the canvas # add_qr_box to accept coordinates and size def add_qr_box(self, x=506, y=217, size=1): # Adjust the size based on QRscale 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): # Open file dialog to select an image file file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")]) if not file_path: # Exit the method if no file is selected return # Split the file path at hyphens and take the last three parts 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: # Extract and convert the numeric parts QRX = int(file_parts[1]) QRY = int(file_parts[2]) QRscale = int(file_parts[3].split('.')[0]) # Split at the dot and take the first part # Update the main application's QR data self.master.QRX = QRX self.master.QRY = QRY self.master.QRscale = QRscale # Display QRX, QRY, and QRscale values 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 # Clear the canvas self.canvas.delete("all") # Load the image img = Image.open(file_path) # Convert to 1-bit black and white img = img.convert('1') # Resize or crop the image to fit the canvas if img.size[0] > 675 or img.size[1] > 375: img = img.crop((0, 0, 650, 360)) self.add_qr_box(QRX, QRY, QRscale) # Paste the imported image onto the drawing image self.drawing.paste(img) # Save image reference to avoid garbage collection self.imported_img = ImageTk.PhotoImage(img) # Add image to the canvas self.image_on_canvas = self.canvas.create_image(0, 0, image=self.imported_img, anchor='nw') # add the QR box at the new position 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) global BUTTON_FONT global TEXT_FONT # Adding the information label self.info_label = tk.Label(self, text="Please enter any information you'd like about your item. This can be make/model of the item, a name for the item, diagnostic information, provenance, historical context, contact info, stories, whatever. Along with the photo, this will be posted on Scuttlebutt forever and cannot be deleted. You can always add more information later via any Scuttlebutt client.", font=TEXT_FONT, wraplength=500) self.info_label.pack(pady=10) # 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=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): # Saving the text from the entry field to a global variable global info_text info_text = self.info_entry.get("1.0", "end-1c") # escape the newlines!: info_text = info_text.replace('\n', '\\n') self.master.switch_frame(Screen11) # I understand class Screen6(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#bcfef9') global BUTTON_FONT global TEXT_FONT 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=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') global BUTTON_FONT global TEXT_FONT 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): global BUTTON_FONT global TEXT_FONT # default set QRscale to 8 QRscale = 8 tk.Frame.__init__(self, master, bg='#bcfef9') # Creating a frame for the left side of the screen for drawing self.left_frame = tk.Frame(self, bg='#bcfef9') self.left_frame.pack(side='left', padx=50) # Frame for the instructions self.center_frame = tk.Frame(self, bg='#bcfef9') self.center_frame.pack(side='left', padx=50) # Frame for the tools self.right_frame = tk.Frame(self, bg='#bcfef9') self.right_frame.pack(side='left', padx=50) # Add instructions self.label = tk.Label(self.center_frame, text="You may now draw your tag! You might like to include contact info for yourself, the name of the item, a drawing, or whatever you want, it's your artistic expression. Thin lines might not show up well (try different drawing tools on the right).", wraplength=400, font=TEXT_FONT) self.label.pack(pady=30) # the drawing area self.drawing = Image.new('1', (325, 179), 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=325, height=179, bg='white') self.canvas.bind("", self.draw_line) self.canvas.pack(pady=20) self.canvas.bind("", self.reset_last_draw) # self.add_qr_box() # Add QR box to the canvas #Create a frame for the buttons grid self.buttons_frame = tk.Frame(self.right_frame, bg='#bcfef9') self.buttons_frame.pack(pady=10) # Add Import Image Button tk.Button(self.buttons_frame, text="Import Image", command=self.import_image, height=2, width=15, bg='peach puff', font=BUTTON_FONT).grid(row=4, column=0, columnspan=2, pady=10, sticky='ew') # Add Draw Size buttons tk.Button(self.buttons_frame, text=".", command=lambda: self.set_draw_size(1), height=3, width=10, bg='peach puff').grid(row=0, column=0, padx=5, pady=5) tk.Button(self.buttons_frame, text="*", command=lambda: self.set_draw_size(2), height=3, width=10, bg='peach puff').grid(row=1, column=0, padx=5, pady=5) tk.Button(self.buttons_frame, text="⚬", command=lambda: self.set_draw_size(3), height=3, width=10, bg='peach puff').grid(row=2, column=0, padx=5, pady=5) tk.Button(self.buttons_frame, text="⬤", command=lambda: self.set_draw_size(4), height=3, width=10, bg='peach puff').grid(row=3, column=0, padx=5, pady=5) # Creating color buttons tk.Button(self.buttons_frame, height=5, width=10, bg='black', command=lambda: self.set_draw_color('black')).grid(row=0, column=1, padx=5, pady=5) tk.Button(self.buttons_frame, height=5, width=10, bg='white', command=lambda: self.set_draw_color('white')).grid(row=1, column=1, padx=5, pady=5) # Add label for pen color buttons self.color_label = tk.Label(self.right_frame, text="^ drawing tools ^", font=("Helvetica", 16)) self.color_label.pack(pady=5) # Add Clear Drawing Button tk.Button(self.right_frame, text="clear drawing", command=self.clear_drawing, height=3, width=20, bg='peach puff', font=BUTTON_FONT).pack(pady=10) # Done button tk.Button(self.right_frame, text="Done", command=self.next, height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=10) # Adding a home button master.add_home_button(self.center_frame) def draw_line(self, event): x, y = event.x, event.y if self.last_draw: self.canvas.create_line(*self.last_draw, x, y, fill=self.draw_color, width=self.draw_size) self.draw.line([*self.last_draw, x, y], fill=0 if self.draw_color == 'black' else 1, width=self.draw_size) self.last_draw = (x, y) def next(self): # Save the drawing as a .png file self.drawing.save("drawing.png") # next screen 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") # Clear canvas self.drawing = Image.new('1', (475, 375), 1) # Create new blank drawing image self.draw = ImageDraw.Draw(self.drawing) # Prepare to draw on the new blank image self.add_qr_box() # Add QR box to the canvas def add_qr_box(self): self.canvas.create_rectangle(346, 229, 475, 358, outline='black', fill='white') self.canvas.create_text(355, 260, text="QR", fill="black") def import_image(self): # Open file dialog to select an image file file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.png")]) if file_path: # Check if the selected file has a correct extension if not (file_path.lower().endswith('.jpg') or file_path.lower().endswith('.png')): tk.messagebox.showerror("Invalid file", "Please select a .jpg or .png file.") return # Load the image img = Image.open(file_path) # Convert to 1-bit black and white img = img.convert('1') # Resize or crop the image to fit the canvas if img.size[0] > 325 or img.size[1] > 179: img = img.crop((0, 0, 325, 179)) # Paste the imported image onto the drawing image self.drawing.paste(img) # Save image reference to avoid garbage collection self.imported_img = ImageTk.PhotoImage(img) # Clear the canvas self.canvas.delete("all") # Add image to the canvas self.image_on_canvas = self.canvas.create_image(0, 0, image=self.imported_img, anchor='nw') # Re-add QR box overlay # self.add_qr_box() # txt update class Screen9(tk.Frame): def __init__(self, master): global BUTTON_FONT global TEXT_FONT 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=BUTTON_FONT).pack(pady=10) #THX BYE class Screen10(tk.Frame): def __init__(self, master): global BUTTON_FONT global TEXT_FONT 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=BUTTON_FONT).pack(pady=10) # Sticker or tag? class Screen11(tk.Frame): def __init__(self, master): global BUTTON_FONT global TEXT_FONT tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) # Instructions self.label = tk.Label(self, text="Do you want to design a sticker or a ribbon? The sticker is suitable for hard surfaces and the ribbon is a tag suitable for sewing into textiles.", wraplength=400, # adjust to suit needs font=("Helvetica", 16)) 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=BUTTON_FONT).pack(side='top', pady=30) tk.Button(self, text="Ribbon tag", command=select_ribbon, height=4, width=39, bg='peach puff', font=BUTTON_FONT).pack(side='top', pady=30) # after QR scanned for lookup class Screen12(tk.Frame): def __init__(self, master): global BUTTON_FONT global TEXT_FONT tk.Frame.__init__(self, master, bg='#bcfef9') master.add_home_button(self) tk.Button(self, text="not implemented yet", command=lambda: master.switch_frame(Screen8), height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=10) tk.Button(self, text="Post Update (also not implemented)", command=lambda: master.switch_frame(Screen9), height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=10) #time to print class Screen13(tk.Frame): def __init__(self, master): global BUTTON_FONT global TEXT_FONT 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=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=BUTTON_FONT).grid(row=2, column=0, pady=20) # go ahead and print the thing def printy(self): global print_type # 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) # 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) # 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, (25, 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, ) 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): global BUTTON_FONT global TEXT_FONT tk.Frame.__init__(self, master, bg='#bcfef9') # divide the screen into two halves left_frame = tk.Frame(self, bg='#bcfef9') # updated background color right_frame = tk.Frame(self, bg='#bcfef9') # updated background color left_frame.pack(side='left', fill='both', expand=True) right_frame.pack(side='right', fill='both', expand=True) # add the home button to the right frame master.add_home_button(right_frame) # setup the instruction on the right side, with wraplength to avoid running off the screen instruction = tk.Label(right_frame, text="Please hold the QR code of a Custodisco tag up to the camera to be scanned. This isn't currently working! Sorry! If you download a Scuttlebutt client and follow the Custodisco Kiosk account you can look up Custodisco items on your own device. Ask someone who knows about Scuttlebutt for help ;)", font=("Helvetica", 16), bg='#bcfef9', wraplength=300) # updated wraplength instruction.pack(pady=100) # increased padding to avoid overlap with the button # setup the video feed on the left side self.video = tk.Label(left_frame, bg='#bcfef9') # updated background color self.video.pack(pady=10) # Open the camera for video capture self.cap = cv2.VideoCapture(0) self.master.after(10, self.update_frame) def update_frame(self): global qr_code_value # Capture frame-by-frame ret, frame = self.cap.read() # Check if the frame is not None if not ret or frame is None: print("Failed to capture frame") return # Our operations on the frame come here gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Look for QR codes in the frame codes = decode(gray) for code in codes: # If a QR code is detected, store its value and switch to Screen12 qr_code_value = code.data.decode('utf-8') self.cap.release() # Close the video capture self.master.switch_frame(Screen12) return # Display the resulting frame cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) img = Image.fromarray(cv2image) imgtk = ImageTk.PhotoImage(image=img) self.video.imgtk = imgtk self.video.configure(image=imgtk) self.master.after(10, self.update_frame) # Call the update_frame function after 10 milliseconds def destroy(self): self.cap.release() # Close the video capture when the frame is destroyed super().destroy() # Call the parent class's destroy method if __name__ == "__main__": app = Kiosk() BUTTON_FONT = tkfont.Font(size=24, family='Helvetica') TEXT_FONT = tkfont.Font(size=20, family='Helvetica') app.mainloop()