1507 lines
60 KiB
Python
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()
|
|
|