diff --git a/.gitignore b/.gitignore index ff7e515..b89ecb8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ -freeze_frame.jpg +\freeze_frame.jpg qr.jpg users.json merged_image.jpg drawing.png to_print.zpl +tmp/* +merged_image.png +__pycache__/* +freeze_frame.png +image-temp.jpg diff --git a/__pycache__/addtoDB.cpython-38.pyc b/__pycache__/addtoDB.cpython-38.pyc deleted file mode 100644 index 302e9d6..0000000 Binary files a/__pycache__/addtoDB.cpython-38.pyc and /dev/null differ diff --git a/__pycache__/tozpl.cpython-38.pyc b/__pycache__/tozpl.cpython-38.pyc deleted file mode 100644 index 8994712..0000000 Binary files a/__pycache__/tozpl.cpython-38.pyc and /dev/null differ diff --git a/addtoDB.py b/addtoDB.py index 3be2c59..a9af16c 100644 --- a/addtoDB.py +++ b/addtoDB.py @@ -9,7 +9,7 @@ import traceback import os, sys import json import subprocess - +import logging def main(): @@ -26,7 +26,6 @@ def main(): exit(1) - def addToSSB(pathToImage,description,mintOrGive): # mint @@ -68,5 +67,48 @@ def addToSSB(pathToImage,description,mintOrGive): return key +def get_message_content(message_id): + try: + print(f"Executing command: ./ssb-post.sh get_message_content {message_id}") + result = subprocess.run(['./ssb-post.sh', 'get_message_content', message_id], + capture_output=True, text=True, check=True) + + print(f"Command output:\n{result.stdout}") + + # Split the output into lines + lines = result.stdout.split('\n') + + # Extract the image path + image_path = None + for line in lines: + if line.startswith("IMAGE_PATH:"): + image_path = line.split(":", 1)[1].strip() + break + + # Find the start of the JSON content + json_start = next(i for i, line in enumerate(lines) if line.strip().startswith("{")) + + # Join the JSON lines and parse + json_content = "\n".join(lines[json_start:]) + message_content = json.loads(json_content) + + print(f"Parsed message content: {message_content}") + print(f"Image path: {image_path}") + + return message_content, image_path + except subprocess.CalledProcessError as e: + print(f"Error: subprocess.CalledProcessError - {e}") + print(f"Debug: stdout = {e.stdout}") + print(f"Debug: stderr = {e.stderr}") + return None, None + except json.JSONDecodeError as e: + print(f"Error: json.JSONDecodeError - {e}") + print(f"Debug: stdout = {result.stdout}") + return None, None + except Exception as e: + print(f"Error: Unexpected exception - {e}") + return None, None + + if __name__ == '__main__': main() diff --git a/freeze_frame.jpg b/freeze_frame.jpg deleted file mode 100644 index eb23dff..0000000 Binary files a/freeze_frame.jpg and /dev/null differ diff --git a/kiosk.py b/kiosk.py new file mode 100644 index 0000000..e0bde6e --- /dev/null +++ b/kiosk.py @@ -0,0 +1,1361 @@ +## 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) + + + +# find yourself in list of ssb users +class Screen2(tk.Frame): + selected_user = None # This is the global variable to store selected user + + 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, bg='#bcfef9') + self.top_frame.pack(side="top", fill="x", pady=60) + + # Add a label with text wrapping + 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.", + font=GlobalVars.TEXT_FONT, + wraplength=800, # Adjust this value as needed + bg='#bcfef9') + self.label.pack(side="top", pady=(0, 20)) # Add some padding below the label + + # 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 = 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=GlobalVars.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=GlobalVars.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=50, width=50) # 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=('Liberation Mono', 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') + + # 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(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=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(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=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(0) + 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 = addtoDB.get_message_content(qr_code_str) + print(f"Received message content: {message_content}") + print(f"Received image path: {image_path}") + + 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) + 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 display_content(self, message_content, image_path): + # 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) + + 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)) + + 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() + diff --git a/kiosk6.py b/kiosk6-backup-pre-claude.py similarity index 98% rename from kiosk6.py rename to kiosk6-backup-pre-claude.py index d616451..d8f46b6 100644 --- a/kiosk6.py +++ b/kiosk6-backup-pre-claude.py @@ -533,8 +533,14 @@ class Screen5(tk.Frame): # Saving the text from the entry field to a global variable global info_text info_text = self.info_entry.get("1.0", "end-1c") + # test + # print(info_text) # escape the newlines!: info_text = info_text.replace('\n', '\\n') + # print(info_text) + # escape quotes as well: + # info_text = info_text.replace('"', '\\"') + # print(info_text) self.master.switch_frame(Screen11) # I understand @@ -801,6 +807,10 @@ class Screen13(tk.Frame): # 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 @@ -840,7 +850,7 @@ class Screen13(tk.Frame): 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.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 @@ -863,6 +873,7 @@ class Screen13(tk.Frame): 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() diff --git a/ssb-custodisco-plugin.js b/ssb-custodisco-plugin.js new file mode 100644 index 0000000..1bdd83f --- /dev/null +++ b/ssb-custodisco-plugin.js @@ -0,0 +1,26 @@ +const ssbClient = require('ssb-client') + +module.exports = { + name: 'custodisco', + version: '1.0.0', + manifest: { + getItem: 'async' + }, + init: (server, config) => { + return { + getItem: async (id) => { + return new Promise((resolve, reject) => { + ssbClient((err, sbot) => { + if (err) return reject(err) + + sbot.get(id, (err, value) => { + sbot.close() + if (err) return reject(err) + resolve(value) + }) + }) + }) + } + } + } +} diff --git a/ssb-post.sh b/ssb-post.sh index 76c131f..5a3175a 100755 --- a/ssb-post.sh +++ b/ssb-post.sh @@ -65,3 +65,82 @@ BLAAB fi +## get_message_content +if [ "$1" == "get_message_content" ] +then + messageID=$2 + + echo "Attempting to retrieve message: $messageID" >&2 + + # Get message content + message_content=$(ssb-server get "$messageID" 2>&1) + + # Check if ssb-server get was successful + if [ $? -ne 0 ]; then + echo "Error: ssb-server get failed" >&2 + echo "$message_content" >&2 + exit 1 + fi + + echo "Successfully retrieved message content" >&2 + + # Extract blob information + blobs=$(echo "$message_content" | jq -r '.content.mentions[] | select(.link | startswith("&")) | .link') + +# Process blobs +if [ -n "$blobs" ]; then + echo "Processing blobs..." >&2 + echo "$message_content" | jq -c '.content.mentions[]' | while read -r mention; do + blob=$(echo "$mention" | jq -r '.link') + if [[ "$blob" == \&* ]]; then + echo "Found a blob: $blob" >&2 + # First, try to get the mime type from the message content + mime_type=$(echo "$mention" | jq -r '.type') + + # If mime type is not in the message content, try to get it from blobs.meta + if [[ "$mime_type" == "null" || -z "$mime_type" ]]; then + if ssb-server blobs.has "$blob"; then + mime_type=$(ssb-server blobs.meta "$blob" | jq -r '.type') + else + echo "Blob not available locally: $blob" >&2 + continue + fi + fi + + echo "Blob mime type: $mime_type" >&2 + + if [[ "$mime_type" == "image/jpeg" || "$mime_type" == "image/png" ]]; then + # Create tmp directory if it doesn't exist + mkdir -p tmp + + # Get blob and save it to a file + file_extension="${mime_type#image/}" + short_name=$(echo "$blob" | md5sum | cut -d' ' -f1) + file_name="${short_name}.$file_extension" + full_path="tmp/$file_name" + + echo "Attempting to save blob to: $full_path" >&2 + echo "Executing command: ssb-server blobs.get $blob > $full_path" >&2 + + if ssb-server blobs.get "$blob" > "$full_path" 2>/dev/null; then + echo "Blob saved: $full_path" >&2 + echo "IMAGE_PATH:$full_path" + else + echo "Failed to retrieve blob: $blob" >&2 + echo "ssb-server blobs.get exit code: $?" >&2 + # If the file wasn't created, let's try to output the blob data directly + echo "Attempting to output blob data directly:" >&2 + ssb-server blobs.get "$blob" | head -c 100 | xxd >&2 + fi + else + echo "Skipping non-image blob: $blob (type: $mime_type)" >&2 + fi + fi + done +else + echo "No blobs found in the message." >&2 +fi + + # Output message content + echo "$message_content" +fi