2024-08-07 14:28:48 -04:00

1507 lines
60 KiB
Python

## 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('<Configure>', self._configure_canvas)
self.interior.bind('<Configure>', 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('<KeyRelease>', 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(
"<Configure>",
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('<Button-1>', lambda e, u=user, f=frame: self.on_user_clicked(f, u))
alias_label.bind('<Button-1>', lambda e, u=user, f=frame: self.on_user_clicked(f, u))
id_label.bind('<Button-1>', 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("<Destroy>", 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("<B1-Motion>", self.draw_line)
self.image_on_canvas = None
self.canvas.pack(pady=20)
self.canvas.bind("<ButtonRelease-1>", 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("<B1-Motion>", self.draw_line)
self.canvas.bind("<ButtonRelease-1>", 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(
"<Configure>",
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()