custodisco-kiosk/kiosk6.py
2023-06-17 01:33:16 -04:00

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()