514 lines
20 KiB
Python
514 lines
20 KiB
Python
import tkinter as tk
|
|
from tkinter import font as tkfont
|
|
from tkinter import Canvas
|
|
from tkinter import ttk
|
|
from tkinter import Text
|
|
from PIL import Image, ImageTk, ImageDraw
|
|
import tozpl
|
|
import subprocess
|
|
import threading
|
|
import json
|
|
import os
|
|
import cv2
|
|
import time
|
|
import qrcode
|
|
import addtoDB
|
|
|
|
|
|
|
|
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.switch_frame(Screen0)
|
|
|
|
def switch_frame(self, frame_class, keep_history=True):
|
|
if keep_history and self.frame:
|
|
self.frames_history.append(type(self.frame))
|
|
new_frame = frame_class(self)
|
|
if self.frame is not None:
|
|
self.frame.destroy()
|
|
self.frame = new_frame
|
|
self.frame.pack(fill="both", expand=True)
|
|
|
|
def back_frame(self):
|
|
if self.frames_history:
|
|
self.switch_frame(self.frames_history.pop(), keep_history=False)
|
|
|
|
def add_home_button(self, frame):
|
|
# Create the "Start Over" button
|
|
button_font = tkfont.Font(size=16, family='Helvetica') # 16 point font
|
|
home_button = tk.Button(frame, text="Start Over", command=lambda: self.switch_frame(Screen0), bg='peach puff', font=button_font)
|
|
home_button.place(x=0, y=0) # top-left corner
|
|
|
|
|
|
|
|
|
|
class Screen0(tk.Frame):
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
title_font = tkfont.Font(size=39, family='Helvetica') # 30% bigger
|
|
button_font = tkfont.Font(size=13, family='Helvetica') # 30% bigger
|
|
title_label = tk.Label(self, text="Custodisco", bg='#bcfef9', font=('Helvetica', 48))
|
|
title_label.pack(side='top', pady=20) # adjust to your needs
|
|
emoji_label = tk.Label(self, text="✨", fg='yellow', bg='#bcfef9', font=title_font)
|
|
emoji_label.pack(side='top')
|
|
tk.Button(self, text="Create Item", command=lambda: master.switch_frame(Screen1), height=4, width=39, bg='peach puff', font=button_font).pack(side='top', pady=30)
|
|
tk.Button(self, text="Lookup Item", command=lambda: master.switch_frame(Screen14), height=4, width=39, bg='peach puff', font=button_font).pack(side='top', pady=30)
|
|
|
|
# Create the quit button
|
|
tk.Button(self, text="Quit", command=self.quit_program, height=3, width=30, bg='peach puff').pack(pady=10)
|
|
|
|
def quit_program(self):
|
|
self.master.destroy()
|
|
|
|
|
|
|
|
class Screen1(tk.Frame):
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
master.add_home_button(self)
|
|
|
|
# Create the label widget with the text
|
|
label = tk.Label(self, text="Do you have a Scuttlebutt account?", font=('Helvetica', 16))
|
|
label.pack(pady=10)
|
|
|
|
tk.Button(self, text="Yes", command=lambda: master.switch_frame(Screen2), height=3, width=30, bg='peach puff').pack(pady=10)
|
|
tk.Button(self, text="No, I Want to Make One Now", command=lambda: master.switch_frame(Screen7), height=3, width=30, bg='peach puff').pack(pady=10)
|
|
|
|
# find yourself in list of ssb users
|
|
class Screen2(tk.Frame):
|
|
selected_user = None # This is the global variable to store selected user
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
|
|
# Create a new frame at the top for the label and text box
|
|
self.top_frame = tk.Frame(self)
|
|
self.top_frame.pack(side="top", fill="x", pady=40)
|
|
|
|
# The 'Done' button to navigate to next screen
|
|
self.done_button = tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen3), height=3, width=30, bg='peach puff', font=('Helvetica', 16))
|
|
self.done_button.pack(side="right", padx=20, pady=10)
|
|
|
|
# The 'Refresh List' button
|
|
self.refresh_button = tk.Button(self, text="Refresh List (takes a minute)", command=self.refresh_users, height=3, width=30, bg='peach puff', font=('Helvetica', 16))
|
|
self.refresh_button.pack(side="right", padx=20, pady=10)
|
|
|
|
# Add a label and text box to the top frame
|
|
self.label = tk.Label(self.top_frame, text="Start typing your public key to find yourself in the list then click on your key to select it.", font=('Helvetica', 16))
|
|
self.label.pack(side="left")
|
|
self.entry = tk.Entry(self.top_frame, font=('Helvetica', 16))
|
|
self.entry.bind('<KeyRelease>', lambda e: self.update_users_list())
|
|
self.entry.pack(side="left")
|
|
|
|
# Focus on the entry box
|
|
self.entry.focus_set()
|
|
|
|
# Create container for user list
|
|
self.container = ttk.Frame(self, height=500) # Define a height here
|
|
self.container.pack(fill='both', expand=True)
|
|
|
|
# Initialize users list from users.json
|
|
self.users = self.get_users_from_file()
|
|
self.update_users_list()
|
|
|
|
master.add_home_button(self)
|
|
|
|
|
|
def refresh_users(self):
|
|
# Update users list from Scuttlebutt and display it
|
|
self.users = self.get_scuttlebutt_users()
|
|
self.update_users_list()
|
|
|
|
def get_scuttlebutt_users(self):
|
|
result = subprocess.run(['node', 'scuttlebot.js'], capture_output=True, text=True)
|
|
if result.returncode == 0:
|
|
users = json.loads(result.stdout)
|
|
return users
|
|
else:
|
|
raise Exception("Command failed: " + result.stderr)
|
|
|
|
def get_users_from_file(self):
|
|
with open('users.json') as f:
|
|
users = json.load(f)
|
|
return users
|
|
|
|
def update_users_list(self):
|
|
# Remove all widgets from container
|
|
for widget in self.container.winfo_children():
|
|
widget.destroy()
|
|
|
|
# Filter users based on search text
|
|
search_text = self.entry.get().lower()
|
|
filtered_users = [user for user in self.users if search_text in user['id'].lower()]
|
|
|
|
# Display filtered users
|
|
self.display_users(filtered_users)
|
|
|
|
def display_users(self, users):
|
|
# Scrollable list of users
|
|
canvas = tk.Canvas(self.container, width=500) # Increase the width if necessary
|
|
scrollbar = ttk.Scrollbar(self.container, orient='vertical', command=canvas.yview)
|
|
scrollable_frame = ttk.Frame(canvas)
|
|
|
|
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)
|
|
|
|
# Add user labels to scrollable frame
|
|
for user in users:
|
|
label = tk.Label(scrollable_frame, text=user['id'], font=('Helvetica', 16))
|
|
label.bind("<Button-1>", self.on_user_clicked)
|
|
label.pack(anchor="w")
|
|
|
|
canvas.pack(side="left", fill="both", expand=True)
|
|
scrollbar.pack(side="right", fill="y")
|
|
|
|
def on_user_clicked(self, event):
|
|
# Remove highlight from previously selected user
|
|
if Screen2.selected_user is not None:
|
|
self.selected_user.configure(bg=self.container.cget("bg"))
|
|
|
|
# Highlight clicked label
|
|
event.widget.configure(bg="light blue")
|
|
|
|
# Store selected user
|
|
Screen2.selected_user = event.widget.cget("text")
|
|
|
|
|
|
#take photo of item
|
|
class Screen3(tk.Frame):
|
|
def __init__(self, master=None):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
master.add_home_button(self)
|
|
|
|
self.vid = cv2.VideoCapture(0)
|
|
self.is_capturing = True
|
|
self.freeze_frame = None
|
|
|
|
# Video feed
|
|
self.canvas = tk.Canvas(self, width=self.vid.get(cv2.CAP_PROP_FRAME_WIDTH), height=self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
self.canvas.pack(side="left")
|
|
|
|
# Info and button on the right
|
|
self.text_frame = tk.Frame(self, bg='#bcfef9')
|
|
self.text_frame.pack(side="right", fill="both", expand=True)
|
|
tk.Label(self.text_frame, text="Now we will take a picture of your item to show up on Scuttlebutt", font=("Helvetica", 16), bg='#bcfef9').pack(pady=10)
|
|
self.button = tk.Button(self.text_frame, text="Take Photo", command=self.take_photo, font=("Helvetica", 32), height=3, width=37, bg='peach puff')
|
|
self.button.pack(pady=10)
|
|
|
|
self.done_button = tk.Button(self, text="Done", command=self.done, height=3, width=30, bg='peach puff')
|
|
self.done_button.place(relx=0.9, rely=0.9, anchor='se')
|
|
|
|
self.update_image()
|
|
|
|
def update_image(self):
|
|
if self.is_capturing:
|
|
ret, frame = self.vid.read()
|
|
if ret:
|
|
self.cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
|
|
self.img = Image.fromarray(self.cv2image)
|
|
self.imgtk = ImageTk.PhotoImage(image=self.img)
|
|
self.canvas.create_image(0, 0, image=self.imgtk, anchor='nw')
|
|
|
|
self.after(10, self.update_image)
|
|
|
|
def take_photo(self):
|
|
if self.freeze_frame is not None:
|
|
self.canvas.delete(self.freeze_frame)
|
|
self.freeze_frame = None
|
|
if not self.is_capturing:
|
|
self.is_capturing = True
|
|
self.update_image()
|
|
countdown_thread = threading.Thread(target=self.countdown)
|
|
countdown_thread.start()
|
|
|
|
def countdown(self):
|
|
for i in range(3, 0, -1):
|
|
self.button.config(text=str(i))
|
|
time.sleep(1)
|
|
|
|
self.button.config(text="Take Photo")
|
|
self.is_capturing = False
|
|
rgb_image = cv2.cvtColor(self.cv2image, cv2.COLOR_RGBA2RGB)
|
|
pil_image = Image.fromarray(rgb_image)
|
|
pil_image.save('freeze_frame.jpg')
|
|
self.display_taken_photo()
|
|
|
|
def display_taken_photo(self):
|
|
image = Image.open('freeze_frame.jpg')
|
|
photo = ImageTk.PhotoImage(image)
|
|
self.freeze_frame = self.canvas.create_image(0, 0, image=photo, anchor='nw')
|
|
|
|
def done(self):
|
|
self.__del__()
|
|
self.master.switch_frame(Screen5)
|
|
|
|
def __del__(self):
|
|
if self.vid.isOpened():
|
|
self.vid.release()
|
|
|
|
|
|
# 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 any information you'd like about your item. This can be make/model of the itme, a name for the item, diagnostic information, provenance, historical context, contact info, stories, whatever. Along with the photo, this will be posted on Scuttlebutt forever and cannot be deleted. You can always add more information later via any Scuttlebutt client.", font=("Helvetica", 16), wraplength=500)
|
|
self.info_label.pack(pady=10)
|
|
|
|
# Adding the text entry field
|
|
self.info_entry = tk.Text(self, height=10, width=50, font=("Helvetica", 16))
|
|
self.info_entry.pack(pady=10)
|
|
|
|
# Adding the done button
|
|
self.done_button = tk.Button(self, text="Done", command=self.save_info_and_switch, height=3, width=30, bg='peach puff')
|
|
self.done_button.pack(pady=10)
|
|
|
|
# Setting the focus to the text entry field
|
|
self.info_entry.focus_set()
|
|
|
|
def save_info_and_switch(self):
|
|
# Saving the text from the entry field to a global variable
|
|
global info_text
|
|
info_text = self.info_entry.get("1.0", "end-1c")
|
|
# escape the newlines!:
|
|
info_text = info_text.replace('\n', '\\n')
|
|
self.master.switch_frame(Screen8)
|
|
|
|
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').pack(pady=10)
|
|
|
|
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
|
|
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen6), height=3, width=30, bg='peach puff').pack(pady=10)
|
|
|
|
|
|
# draw the tag! :)
|
|
class Screen8(tk.Frame):
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
|
|
# Creating a frame for the left side of the screen for drawing and right side for the buttons
|
|
self.left_frame = tk.Frame(self, bg='#bcfef9')
|
|
self.right_frame = tk.Frame(self, bg='#bcfef9')
|
|
self.left_frame.pack(side='left', padx=10)
|
|
self.right_frame.pack(side='right', padx=10)
|
|
|
|
# Add instructions
|
|
self.label = tk.Label(self.left_frame, text="You may now draw your tag! This will be printed as either a sticker or a ribbon, both of which are 1.25 by 2.25 inches. You can include contact info for yourself, the name of the item, a drawing, or whatever you want, it's your artistic expression. In the bottom right will be a small QR code which links to the Scuttlebutt post for your item.",
|
|
wraplength=500, # adjust to suit needs
|
|
font=("Helvetica", 16))
|
|
self.label.pack(pady=20)
|
|
|
|
self.drawing = Image.new('1', (300, 540), 1)
|
|
self.draw = ImageDraw.Draw(self.drawing)
|
|
self.last_draw = None
|
|
|
|
# Set initial drawing color to black
|
|
self.draw_color = 'black'
|
|
|
|
# Creating the Canvas for drawing
|
|
self.canvas = Canvas(self.left_frame, width=300, height=540, bg='white')
|
|
self.canvas.bind("<B1-Motion>", self.draw_line)
|
|
self.canvas.pack(pady=20)
|
|
self.canvas.bind("<ButtonRelease-1>", self.reset_last_draw)
|
|
self.add_qr_box() # Add QR box to the canvas
|
|
|
|
# Creating color buttons
|
|
tk.Button(self.right_frame, height=10, width=10, bg='black', command=lambda: self.set_draw_color('black')).pack(pady=5)
|
|
tk.Button(self.right_frame, height=10, width=10, bg='white', command=lambda: self.set_draw_color('white')).pack(pady=5)
|
|
|
|
# Add label for pen color buttons
|
|
self.color_label = tk.Label(self.right_frame, text="switch pen color", font=("Helvetica", 16))
|
|
self.color_label.pack(pady=5)
|
|
|
|
# Add Clear Drawing Button
|
|
tk.Button(self.right_frame, text="clear drawing", command=self.clear_drawing, height=3, width=20, bg='peach puff').pack(pady=10)
|
|
|
|
# done button
|
|
tk.Button(self.right_frame, text="Done", command=self.next, height=3, width=30, bg='peach puff').pack(pady=10)
|
|
|
|
# Adding a home button
|
|
master.add_home_button(self.right_frame)
|
|
|
|
def draw_line(self, event):
|
|
x, y = event.x, event.y
|
|
if self.last_draw:
|
|
self.canvas.create_line(*self.last_draw, x, y, fill=self.draw_color)
|
|
self.draw.line([*self.last_draw, x, y], fill=0 if self.draw_color == 'black' else 1)
|
|
self.last_draw = (x, y)
|
|
|
|
def next(self):
|
|
|
|
# Save the drawing as a .png file
|
|
self.drawing.save("drawing.png")
|
|
|
|
# next screen
|
|
self.master.switch_frame(Screen11)
|
|
|
|
|
|
def reset_last_draw(self, event):
|
|
self.last_draw = None
|
|
|
|
def set_draw_color(self, color):
|
|
self.draw_color = color
|
|
|
|
def clear_drawing(self):
|
|
self.canvas.delete("all") # Clear canvas
|
|
self.drawing = Image.new('1', (300, 540), 1) # Create new blank drawing image
|
|
self.draw = ImageDraw.Draw(self.drawing) # Prepare to draw on the new blank image
|
|
self.add_qr_box() # Add QR box to the canvas
|
|
|
|
def add_qr_box(self):
|
|
self.canvas.create_rectangle(300-43, 540-43, 300, 540, outline='black', fill='white')
|
|
self.canvas.create_text(300-22, 540-22, text="QR", fill="black")
|
|
|
|
|
|
# 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').pack(pady=10)
|
|
|
|
#THX BYE
|
|
class Screen10(tk.Frame):
|
|
def __init__(self, master):
|
|
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').pack(pady=10)
|
|
|
|
|
|
#sticker or tag? then print
|
|
class Screen11(tk.Frame):
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
|
|
# 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. Otherwise choose whether you'd like a sticker or a fabric tag printed!", wraplength=600, font=('Helvetica', 16)).grid(row=0, column=0, columnspan=2)
|
|
|
|
# buttons
|
|
master.add_home_button(self)
|
|
tk.Button(container, text="Sticker", command=lambda: self.printy(2), height=3, width=30, bg='peach puff').grid(row=2, column=0, pady=20)
|
|
tk.Button(container, text="Tag", command=lambda: self.printy(1), height=3, width=30, bg='peach puff').grid(row=2, column=1, pady=20)
|
|
|
|
|
|
# go ahead and print the thing
|
|
def printy(self, orientation):
|
|
|
|
|
|
# Specify the path to your image file
|
|
path_to_image = "/home/trav/Documents/custodiosk/freeze_frame.jpg"
|
|
|
|
# make ssb post
|
|
key = addtoDB.addToSSB(path_to_image,info_text)
|
|
|
|
# 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 = 1,
|
|
border = 1,
|
|
)
|
|
# 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
|
|
merged_image = Image.new('L', drawing.size)
|
|
merged_image.paste(drawing, (0, 0))
|
|
merged_image.paste(qr, (257, 497)) # paste without mask
|
|
merged_image.save("merged_image.png")
|
|
|
|
|
|
# if sticker we gotta rotate 90 degrees.... I think
|
|
if orientation == 2:
|
|
image = Image.open("merged_image.png")
|
|
rotated_image = image.transpose(Image.ROTATE_270) # Transpose and rotate 90 degrees
|
|
rotated_image.save("merged_image.png")
|
|
|
|
|
|
# Get the ZPL code for the image
|
|
zpl_code = tozpl.print_to_zpl("merged_image.png")
|
|
|
|
# send the ZPL to the printer babeee
|
|
#print(zpl_code) #only needed for testing
|
|
|
|
|
|
#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 to sticker printer
|
|
if orientation == 2:
|
|
try:
|
|
result = subprocess.Popen('lpr -P sticker_printer -o raw to_print.zpl', shell=True, stdout=subprocess.PIPE, )
|
|
except:
|
|
print('traceback.format_exc():\n%s' % traceback.format_exc())
|
|
exit()
|
|
# or print to tag printer:
|
|
elif orientation == 1:
|
|
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()
|
|
|
|
|
|
# post update to SSB transferring ownership to the user
|
|
# fill this in ^^^
|
|
|
|
self.master.switch_frame(Screen10) # Switching to Screen10 after Done
|
|
|
|
class Screen12(tk.Frame):
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
master.add_home_button(self)
|
|
tk.Button(self, text="Re-print Tag", command=lambda: master.switch_frame(Screen8), height=3, width=30, bg='peach puff').pack(pady=10)
|
|
tk.Button(self, text="Post Update", command=lambda: master.switch_frame(Screen9), height=3, width=30, bg='peach puff').pack(pady=10)
|
|
|
|
class Screen14(tk.Frame):
|
|
def __init__(self, master):
|
|
tk.Frame.__init__(self, master, bg='#bcfef9')
|
|
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen12), height=3, width=30, bg='peach puff').pack(pady=10)
|
|
|
|
if __name__ == "__main__":
|
|
app = Kiosk()
|
|
app.mainloop()
|
|
|