Compare commits
13 Commits
f686250063
...
3c6876420f
Author | SHA1 | Date | |
---|---|---|---|
3c6876420f | |||
383cad8808 | |||
8abb5bc2ee | |||
106a18b091 | |||
d9219fd595 | |||
87347903ad | |||
1799d4c55f | |||
a6d204e937 | |||
f045d4a65c | |||
0620d546b2 | |||
cdaca6534e | |||
79087694c7 | |||
c9b1b99cc0 |
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
\freeze_frame.jpg
|
||||
qr.jpg
|
||||
users.json
|
||||
merged_image.jpg
|
||||
drawing.png
|
||||
to_print.zpl
|
||||
tmp/*
|
||||
merged_image.png
|
||||
__pycache__/*
|
||||
freeze_frame.png
|
||||
image-temp.jpg
|
12
README.md
12
README.md
@ -0,0 +1,12 @@
|
||||
This is the code for the [Custodisco](teafry.me/custodisco/) kiosk. There will be other Custodisco repos, this one is just for the kiosk. As far as I know it has only been run by me, on a Thinkpad x230t with 2 Zebra GX430T printers, one loaded with ribbons, one loaded with stickers. 2.25" x 1.25" stickers and ribbons segments.
|
||||
|
||||
I will eventually write up a guide for getting this code running I suppose. You gotta keep `ssb-server` running in the background for it to work.
|
||||
|
||||
I collaborated with chatgpt on like 85% of the code.
|
||||
|
||||
## planned features/bug fixes:
|
||||
- import image
|
||||
- cleaner lines in the draw area
|
||||
- make SSB account
|
||||
- lookup item by QR code
|
||||
- update looked up item
|
114
addtoDB-backup.py
Normal file
114
addtoDB-backup.py
Normal file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
#this script takes a file as an option and adds that file scuttlebutt
|
||||
# originally from ebb, modified for custodisco
|
||||
|
||||
import optparse
|
||||
import traceback
|
||||
import os, sys
|
||||
import json
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
|
||||
def main():
|
||||
#get options and arguments
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--file', '-f', action='store', dest='file', help='this needs to be a file path')
|
||||
|
||||
options, arguments = p.parse_args()
|
||||
|
||||
if options.file:
|
||||
pathToImage=options.file
|
||||
else:
|
||||
print("you need to provide a file path")
|
||||
exit(1)
|
||||
|
||||
|
||||
def addToSSB(pathToImage,description,mintOrGive):
|
||||
|
||||
# mint
|
||||
if mintOrGive == 1:
|
||||
try:
|
||||
result = subprocess.Popen('./ssb-post.sh mint "' + description + '" ' + pathToImage, shell=True, stdout=subprocess.PIPE, )
|
||||
except:
|
||||
print('traceback.format_exc():\n%s' % traceback.format_exc())
|
||||
exit()
|
||||
|
||||
# else give
|
||||
elif mintOrGive == 2:
|
||||
try:
|
||||
result = subprocess.Popen('./ssb-post.sh give "' + pathToImage + '" ' + description, shell=True, stdout=subprocess.PIPE, )
|
||||
except:
|
||||
print('traceback.format_exc():\n%s' % traceback.format_exc())
|
||||
exit()
|
||||
|
||||
# get the ssb json from the bash command we just ran
|
||||
newssb = result.stdout.read().decode() # decode bytes to string
|
||||
print(newssb)
|
||||
|
||||
# CHECK that newssb is _anything_ if ssb-server isn't running that may show as garbage that will crash the program
|
||||
if len(newssb) == 0:
|
||||
print("String is empty")
|
||||
|
||||
#convert string to object
|
||||
# Make sure it's valid JSON before loading
|
||||
try:
|
||||
json_object = json.loads(newssb)
|
||||
except json.JSONDecodeError:
|
||||
print("Invalid JSON")
|
||||
return "offline" # Return the blouch
|
||||
|
||||
# get the key for the post we just made
|
||||
key = json_object["key"]
|
||||
print (key)
|
||||
|
||||
return key
|
||||
|
||||
|
||||
def get_message_content(message_id):
|
||||
try:
|
||||
print(f"Executing command: ./ssb-post.sh get_message_content {message_id}")
|
||||
result = subprocess.run(['./ssb-post.sh', 'get_message_content', message_id],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
print(f"Command output:\n{result.stdout}")
|
||||
|
||||
# Split the output into lines
|
||||
lines = result.stdout.split('\n')
|
||||
|
||||
# Extract the image path
|
||||
image_path = None
|
||||
for line in lines:
|
||||
if line.startswith("IMAGE_PATH:"):
|
||||
image_path = line.split(":", 1)[1].strip()
|
||||
break
|
||||
|
||||
# Find the start of the JSON content
|
||||
json_start = next(i for i, line in enumerate(lines) if line.strip().startswith("{"))
|
||||
|
||||
# Join the JSON lines and parse
|
||||
json_content = "\n".join(lines[json_start:])
|
||||
message_content = json.loads(json_content)
|
||||
|
||||
print(f"Parsed message content: {message_content}")
|
||||
print(f"Image path: {image_path}")
|
||||
|
||||
return message_content, image_path
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: subprocess.CalledProcessError - {e}")
|
||||
print(f"Debug: stdout = {e.stdout}")
|
||||
print(f"Debug: stderr = {e.stderr}")
|
||||
return None, None
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: json.JSONDecodeError - {e}")
|
||||
print(f"Debug: stdout = {result.stdout}")
|
||||
return None, None
|
||||
except Exception as e:
|
||||
print(f"Error: Unexpected exception - {e}")
|
||||
return None, None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
131
addtoDB.py
Normal file
131
addtoDB.py
Normal file
@ -0,0 +1,131 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
#this script takes a file as an option and adds that file scuttlebutt
|
||||
# originally from ebb, modified for custodisco
|
||||
|
||||
import optparse
|
||||
import traceback
|
||||
import os, sys
|
||||
import json
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
|
||||
def main():
|
||||
#get options and arguments
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--file', '-f', action='store', dest='file', help='this needs to be a file path')
|
||||
|
||||
options, arguments = p.parse_args()
|
||||
|
||||
if options.file:
|
||||
pathToImage=options.file
|
||||
else:
|
||||
print("you need to provide a file path")
|
||||
exit(1)
|
||||
|
||||
|
||||
def addToSSB(pathToImage,description,mintOrGive):
|
||||
|
||||
# mint
|
||||
if mintOrGive == 1:
|
||||
try:
|
||||
result = subprocess.Popen('./ssb-post.sh mint "' + description + '" ' + pathToImage, shell=True, stdout=subprocess.PIPE, )
|
||||
except:
|
||||
print('traceback.format_exc():\n%s' % traceback.format_exc())
|
||||
exit()
|
||||
|
||||
# else give
|
||||
elif mintOrGive == 2:
|
||||
try:
|
||||
result = subprocess.Popen('./ssb-post.sh give "' + pathToImage + '" ' + description, shell=True, stdout=subprocess.PIPE, )
|
||||
except:
|
||||
print('traceback.format_exc():\n%s' % traceback.format_exc())
|
||||
exit()
|
||||
|
||||
# get the ssb json from the bash command we just ran
|
||||
newssb = result.stdout.read().decode() # decode bytes to string
|
||||
print(newssb)
|
||||
|
||||
# CHECK that newssb is _anything_ if ssb-server isn't running that may show as garbage that will crash the program
|
||||
if len(newssb) == 0:
|
||||
print("String is empty")
|
||||
|
||||
#convert string to object
|
||||
# Make sure it's valid JSON before loading
|
||||
try:
|
||||
json_object = json.loads(newssb)
|
||||
except json.JSONDecodeError:
|
||||
print("Invalid JSON")
|
||||
return "offline" # Return the blouch
|
||||
|
||||
# get the key for the post we just made
|
||||
key = json_object["key"]
|
||||
print (key)
|
||||
|
||||
return key
|
||||
|
||||
|
||||
def get_message_content(message_id):
|
||||
try:
|
||||
print(f"Executing command: ./ssb-post.sh get_message_content {message_id}")
|
||||
result = subprocess.run(['./ssb-post.sh', 'get_message_content', message_id],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
print(f"Command output:\n{result.stdout}")
|
||||
|
||||
# Split the output into lines
|
||||
lines = result.stdout.split('\n')
|
||||
|
||||
# Extract the image path
|
||||
image_path = None
|
||||
for line in lines:
|
||||
if line.startswith("IMAGE_PATH:"):
|
||||
image_path = line.split(":", 1)[1].strip()
|
||||
break
|
||||
|
||||
# Find the start and end of the main message content
|
||||
json_start = next(i for i, line in enumerate(lines) if line.strip().startswith("{"))
|
||||
replies_start = next(i for i, line in enumerate(lines) if line.strip() == "REPLIES_START")
|
||||
|
||||
# Join the JSON lines for the main message and parse
|
||||
json_content = "\n".join(lines[json_start:replies_start])
|
||||
message_content = json.loads(json_content)
|
||||
|
||||
# Parse replies
|
||||
replies = []
|
||||
reply_json = ""
|
||||
for line in lines[replies_start+1:]:
|
||||
if line.strip() == "REPLIES_END":
|
||||
break
|
||||
if line.strip() == "{":
|
||||
reply_json = "{"
|
||||
elif line.strip() == "}":
|
||||
reply_json += "}"
|
||||
replies.append(json.loads(reply_json))
|
||||
reply_json = ""
|
||||
else:
|
||||
reply_json += line
|
||||
|
||||
print(f"Parsed message content: {message_content}")
|
||||
print(f"Image path: {image_path}")
|
||||
print(f"Number of replies: {len(replies)}")
|
||||
|
||||
return message_content, image_path, replies
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: subprocess.CalledProcessError - {e}")
|
||||
print(f"Debug: stdout = {e.stdout}")
|
||||
print(f"Debug: stderr = {e.stderr}")
|
||||
return None, None, None
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: json.JSONDecodeError - {e}")
|
||||
print(f"Debug: stdout = {result.stdout}")
|
||||
return None, None, None
|
||||
except Exception as e:
|
||||
print(f"Error: Unexpected exception - {e}")
|
||||
return None, None, None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
961
kiosk6-backup-pre-claude.py
Normal file
961
kiosk6-backup-pre-claude.py
Normal file
@ -0,0 +1,961 @@
|
||||
## this is the custodisco kiosk code for dweb camp 2023
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import font as tkfont
|
||||
from tkinter import Canvas, ttk, Text, filedialog, messagebox
|
||||
from PIL import Image, ImageTk, ImageDraw
|
||||
import tozpl
|
||||
import subprocess
|
||||
import threading
|
||||
import json
|
||||
import os
|
||||
import cv2
|
||||
import time
|
||||
import qrcode
|
||||
import addtoDB
|
||||
from pyzbar.pyzbar import decode
|
||||
|
||||
qr_code_value = None # global variable to store QR code value
|
||||
print_type = "neither" # print type when printing
|
||||
|
||||
|
||||
class GlobalVars:
|
||||
selected_user = 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
|
||||
globals()['BUTTON_FONT'] = tkfont.Font(size=24, family='Helvetica')
|
||||
globals()['TEXT_FONT'] = tkfont.Font(size=20, 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 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
|
||||
home_button = tk.Button(frame, text="Start Over from the beginning", command=lambda: self.switch_frame(Screen0), bg='peach puff', width=24, font=BUTTON_FONT)
|
||||
home_button.place(x=0, y=0) # top-left corner
|
||||
|
||||
# home screen
|
||||
class Screen0(tk.Frame):
|
||||
def __init__(self, master):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
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', 48)) # dark purple color
|
||||
title_label.pack(side='top', pady=20) # adjust to your needs
|
||||
|
||||
# Welcome message on the left side
|
||||
welcome_text = """If you have an item that is special to you, this kiosk allows you to create a tag for your item.
|
||||
|
||||
Build your relationship with your item by committing to mend and care for it.
|
||||
|
||||
If you put your contact info on the tag the item may be returned to you if lost.
|
||||
|
||||
You can use your fingers to interact with the kiosk but also there is a stylus that can help with drawing.
|
||||
|
||||
More info at https://teafry.me/custodisco"""
|
||||
welcome_label = tk.Label(left_frame, text=welcome_text, bg='#bcfef9', font=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=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=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=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="Do you have a Scuttlebutt account? If no just tap Yes and then Done on the next screen, it's fine.", font=TEXT_FONT)
|
||||
label.pack(pady=60)
|
||||
|
||||
tk.Button(self, text="Yes", command=lambda: master.switch_frame(Screen2), height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=50)
|
||||
tk.Button(self, text="No, I Want to Make One Now (not implemented yet)", command=lambda: master.switch_frame(Screen7), height=3, width=50, bg='peach puff', font=BUTTON_FONT).pack(pady=50)
|
||||
|
||||
|
||||
|
||||
# find yourself in list of ssb users
|
||||
class Screen2(tk.Frame):
|
||||
selected_user = None # This is the global variable to store selected user
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
|
||||
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
self.selected_label = None # This is the global variable to store selected label
|
||||
|
||||
# Create a new frame at the top for the label and text box
|
||||
self.top_frame = tk.Frame(self)
|
||||
self.top_frame.pack(side="top", fill="x", pady=60)
|
||||
|
||||
# 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.\n\nIf you can't find your name it might help to follow the Custodisco Scuttlebutt account.", font=TEXT_FONT)
|
||||
self.label.pack(side="left")
|
||||
self.entry = tk.Entry(self.top_frame, font=TEXT_FONT)
|
||||
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, padx=20, pady=20)
|
||||
|
||||
# Initialize users list from users.json
|
||||
self.users = self.get_users_from_file()
|
||||
self.update_users_list()
|
||||
|
||||
# Highlight selected user if one exists
|
||||
if GlobalVars.selected_user is not None:
|
||||
for widget in self.container.winfo_children():
|
||||
if isinstance(widget, tk.Button) and widget['text'] == GlobalVars.selected_user:
|
||||
widget.configure(relief=tk.SUNKEN, bg="light blue")
|
||||
self.selected_label = widget
|
||||
|
||||
|
||||
# The 'Done' button to navigate to next screen
|
||||
self.done_button = tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen3), height=3, width=30, bg='peach puff', font=BUTTON_FONT)
|
||||
self.done_button.pack(side="bottom", padx=20, pady=10)
|
||||
|
||||
# The 'Refresh List' button
|
||||
self.refresh_button = tk.Button(self, text="Refresh List (takes 30 seconds or so)", command=self.refresh_users, height=3, width=30, bg='peach puff', font=BUTTON_FONT)
|
||||
self.refresh_button.pack(side="bottom", padx=20, pady=10)
|
||||
|
||||
master.add_home_button(self)
|
||||
|
||||
def refresh_users(self):
|
||||
try:
|
||||
# Update users list from Scuttlebutt and display it
|
||||
self.users = self.get_scuttlebutt_users()
|
||||
self.update_users_list()
|
||||
except Exception as e:
|
||||
print(f"An error occurred while refreshing the user list: {e}")
|
||||
messagebox.showerror("Error", "An error occurred while refreshing the user list. Please try again later.")
|
||||
|
||||
def get_scuttlebutt_users(self):
|
||||
result = subprocess.run(['node', 'scuttlebot.js'], capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
users = json.loads(result.stdout)
|
||||
return users
|
||||
else:
|
||||
raise Exception("Command failed: " + result.stderr)
|
||||
|
||||
def get_users_from_file(self):
|
||||
with open('users.json') as f:
|
||||
users = json.load(f)
|
||||
return users
|
||||
|
||||
def update_users_list(self):
|
||||
# Remove all widgets from container
|
||||
for widget in self.container.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
# Filter users based on search text
|
||||
search_text = self.entry.get().lower()
|
||||
filtered_users = [user for user in self.users if search_text in user['id'].lower()]
|
||||
|
||||
# Display filtered users
|
||||
self.display_users(filtered_users)
|
||||
|
||||
def display_users(self, users):
|
||||
# Scrollable list of users
|
||||
canvas = tk.Canvas(self.container, width=460) # Decrease the width by scrollbar's width
|
||||
style = ttk.Style()
|
||||
style.configure("Vertical.TScrollbar", gripcount=0,
|
||||
arrowsize=15, width=40) # Adjust width here
|
||||
scrollbar = ttk.Scrollbar(self.container, orient='vertical', command=canvas.yview, style="Vertical.TScrollbar")
|
||||
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 buttons to scrollable frame
|
||||
for user in users:
|
||||
button = tk.Button(scrollable_frame, text=user['id'], font=('Helvetica', 24), bd=0) # Adjust font size here
|
||||
button.pack(anchor="w")
|
||||
button.config(command=lambda button=button, user=user: self.on_user_clicked(button, user))
|
||||
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
||||
def widget_exists(self, widget):
|
||||
try:
|
||||
widget.winfo_exists()
|
||||
return True
|
||||
except tk.TclError:
|
||||
return False
|
||||
|
||||
def on_user_clicked(self, button, user):
|
||||
# Remove highlight from previously selected user
|
||||
if self.selected_label is not None and self.widget_exists(self.selected_label):
|
||||
self.selected_label.configure(relief=tk.FLAT, bg='SystemButtonFace') # default color
|
||||
|
||||
# Store selected user and button
|
||||
GlobalVars.selected_user = user['id']
|
||||
self.selected_label = button
|
||||
|
||||
# Highlight clicked label
|
||||
self.selected_label.configure(relief=tk.SUNKEN, bg="light blue")
|
||||
|
||||
|
||||
|
||||
|
||||
#take photo of item
|
||||
class Screen3(tk.Frame):
|
||||
def __init__(self, master=None):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
|
||||
# Create the "Start Over" button
|
||||
home_button = tk.Button(text="Start Over", command=self.homer, bg='peach puff', font=BUTTON_FONT)
|
||||
home_button.place(x=0, y=0) # top-left corner
|
||||
|
||||
self.vid = cv2.VideoCapture(0)
|
||||
self.is_capturing = True
|
||||
self.freeze_frame = None
|
||||
|
||||
# Video feed
|
||||
self.canvas = tk.Canvas(self, width=self.vid.get(cv2.CAP_PROP_FRAME_WIDTH), height=self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
||||
self.canvas.pack(side="left")
|
||||
|
||||
# Info and button on the right
|
||||
self.text_frame = tk.Frame(self, bg='#bcfef9')
|
||||
self.text_frame.pack(side="right", fill="both", expand=True)
|
||||
tk.Label(self.text_frame, text="Now we will take a picture of your item to show up on Scuttlebutt.\n\nIf you tap Take Photo a second time it will re-take the photo\nbut wont show you a preview during the countdown (this is a bug)", font=("Helvetica", 16), bg='#bcfef9').pack(pady=10)
|
||||
self.button = tk.Button(self.text_frame, text="Take Photo", command=self.take_photo, height=3, width=37, bg='peach puff', font=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=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)
|
||||
|
||||
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()
|
||||
|
||||
# draw a sticker
|
||||
class Screen4(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
|
||||
# Configure column minsizes
|
||||
self.grid_columnconfigure(0, minsize=675) # considering 675 width + 50 padding on each side
|
||||
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=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
# Add instructions
|
||||
self.label = tk.Label(self.left_frame, text="You may now draw your sticker :) This will be printed ~ 1.25 by 2.25 inches. You might have better results using thicker lines (see drawing tools on the right). The image you are drawing now will not be posted to Scuttlebutt.",
|
||||
wraplength=650, # adjust to suit needs
|
||||
font=TEXT_FONT)
|
||||
self.label.pack(pady=2)
|
||||
|
||||
# is this the 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 # To store imported image's reference
|
||||
self.canvas.pack(pady=20)
|
||||
self.canvas.bind("<ButtonRelease-1>", self.reset_last_draw)
|
||||
self.add_qr_box() # Add QR box to the canvas
|
||||
|
||||
#Create a frame for the buttons grid
|
||||
self.buttons_frame = tk.Frame(self.right_frame, bg='#bcfef9')
|
||||
self.buttons_frame.pack(pady=50)
|
||||
|
||||
# Define the info_label to display QRX, QRY, and QRscale values
|
||||
self.info_label = tk.Label(self.right_frame, text="", bg='#bcfef9', font=TEXT_FONT)
|
||||
self.info_label.pack(pady=5) # Use pack instead of grid
|
||||
|
||||
|
||||
# Add Draw Size buttons
|
||||
tk.Button(self.buttons_frame, text=".", command=lambda: self.set_draw_size(1), height=3, width=10, bg='peach puff').grid(row=0, column=0, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, text="*", command=lambda: self.set_draw_size(2), height=3, width=10, bg='peach puff').grid(row=1, column=0, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, text="⚬", command=lambda: self.set_draw_size(3), height=3, width=10, bg='peach puff').grid(row=2, column=0, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, text="⬤", command=lambda: self.set_draw_size(4), height=3, width=10, bg='peach puff').grid(row=3, column=0, padx=5, pady=5)
|
||||
|
||||
# Creating color buttons
|
||||
tk.Button(self.buttons_frame, height=5, width=10, bg='black', command=lambda: self.set_draw_color('black')).grid(row=0, column=1, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, height=5, width=10, bg='white', command=lambda: self.set_draw_color('white')).grid(row=1, column=1, padx=5, pady=5)
|
||||
|
||||
# Add label for pen color buttons
|
||||
self.color_label = tk.Label(self.right_frame, text="^ drawing tools ^", font=TEXT_FONT)
|
||||
self.color_label.pack(pady=5)
|
||||
|
||||
# Add Clear Drawing Button
|
||||
tk.Button(self.right_frame, text="clear drawing", command=self.clear_drawing, height=2, width=10, bg='peach puff', font=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=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
# Adding a home button
|
||||
master.add_home_button(self)
|
||||
|
||||
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, width=self.draw_size)
|
||||
self.draw.line([*self.last_draw, x, y], fill=0 if self.draw_color == 'black' else 1, width=self.draw_size)
|
||||
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(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") # Clear canvas
|
||||
self.drawing = Image.new('1', (650, 360), 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
|
||||
|
||||
# add_qr_box to accept coordinates and size
|
||||
def add_qr_box(self, x=506, y=217, size=1):
|
||||
# Adjust the size based on QRscale
|
||||
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):
|
||||
|
||||
# Open file dialog to select an image file
|
||||
file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")])
|
||||
|
||||
if not file_path: # Exit the method if no file is selected
|
||||
return
|
||||
|
||||
# Split the file path at hyphens and take the last three parts
|
||||
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:
|
||||
# Extract and convert the numeric parts
|
||||
QRX = int(file_parts[1])
|
||||
QRY = int(file_parts[2])
|
||||
QRscale = int(file_parts[3].split('.')[0]) # Split at the dot and take the first part
|
||||
|
||||
# Update the main application's QR data
|
||||
self.master.QRX = QRX
|
||||
self.master.QRY = QRY
|
||||
self.master.QRscale = QRscale
|
||||
|
||||
# Display QRX, QRY, and QRscale values
|
||||
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
|
||||
|
||||
# Clear the canvas
|
||||
self.canvas.delete("all")
|
||||
# Load the image
|
||||
img = Image.open(file_path)
|
||||
# Convert to 1-bit black and white
|
||||
img = img.convert('1')
|
||||
# Resize or crop the image to fit the canvas
|
||||
if img.size[0] > 675 or img.size[1] > 375:
|
||||
img = img.crop((0, 0, 650, 360))
|
||||
self.add_qr_box(QRX, QRY, QRscale)
|
||||
|
||||
# Paste the imported image onto the drawing image
|
||||
self.drawing.paste(img)
|
||||
|
||||
# Save image reference to avoid garbage collection
|
||||
self.imported_img = ImageTk.PhotoImage(img)
|
||||
|
||||
# Add image to the canvas
|
||||
self.image_on_canvas = self.canvas.create_image(0, 0, image=self.imported_img, anchor='nw')
|
||||
|
||||
# add the QR box at the new position
|
||||
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)
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
|
||||
# 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 item, 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=TEXT_FONT, 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', font=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):
|
||||
# Saving the text from the entry field to a global variable
|
||||
global info_text
|
||||
info_text = self.info_entry.get("1.0", "end-1c")
|
||||
# test
|
||||
# print(info_text)
|
||||
# escape the newlines!:
|
||||
info_text = info_text.replace('\n', '\\n')
|
||||
# print(info_text)
|
||||
# escape quotes as well:
|
||||
# info_text = info_text.replace('"', '\\"')
|
||||
# print(info_text)
|
||||
self.master.switch_frame(Screen11)
|
||||
|
||||
# I understand
|
||||
class Screen6(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
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=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')
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
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):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
|
||||
# default set QRscale to 8
|
||||
QRscale = 8
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
|
||||
# Creating a frame for the left side of the screen for drawing
|
||||
self.left_frame = tk.Frame(self, bg='#bcfef9')
|
||||
self.left_frame.pack(side='left', padx=50)
|
||||
|
||||
# Frame for the instructions
|
||||
self.center_frame = tk.Frame(self, bg='#bcfef9')
|
||||
self.center_frame.pack(side='left', padx=50)
|
||||
|
||||
# Frame for the tools
|
||||
self.right_frame = tk.Frame(self, bg='#bcfef9')
|
||||
self.right_frame.pack(side='left', padx=50)
|
||||
|
||||
# Add instructions
|
||||
self.label = tk.Label(self.center_frame, text="You may now draw your tag! You might like to include contact info for yourself, the name of the item, a drawing, or whatever you want, it's your artistic expression. Thin lines might not show up well (try different drawing tools on the right).", wraplength=400, font=TEXT_FONT)
|
||||
self.label.pack(pady=30)
|
||||
|
||||
# the drawing area
|
||||
self.drawing = Image.new('1', (325, 179), 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=325, height=179, 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
|
||||
|
||||
#Create a frame for the buttons grid
|
||||
self.buttons_frame = tk.Frame(self.right_frame, bg='#bcfef9')
|
||||
self.buttons_frame.pack(pady=10)
|
||||
|
||||
# Add Import Image Button
|
||||
tk.Button(self.buttons_frame, text="Import Image", command=self.import_image, height=2, width=15, bg='peach puff', font=BUTTON_FONT).grid(row=4, column=0, columnspan=2, pady=10, sticky='ew')
|
||||
|
||||
|
||||
# Add Draw Size buttons
|
||||
tk.Button(self.buttons_frame, text=".", command=lambda: self.set_draw_size(1), height=3, width=10, bg='peach puff').grid(row=0, column=0, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, text="*", command=lambda: self.set_draw_size(2), height=3, width=10, bg='peach puff').grid(row=1, column=0, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, text="⚬", command=lambda: self.set_draw_size(3), height=3, width=10, bg='peach puff').grid(row=2, column=0, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, text="⬤", command=lambda: self.set_draw_size(4), height=3, width=10, bg='peach puff').grid(row=3, column=0, padx=5, pady=5)
|
||||
|
||||
# Creating color buttons
|
||||
tk.Button(self.buttons_frame, height=5, width=10, bg='black', command=lambda: self.set_draw_color('black')).grid(row=0, column=1, padx=5, pady=5)
|
||||
tk.Button(self.buttons_frame, height=5, width=10, bg='white', command=lambda: self.set_draw_color('white')).grid(row=1, column=1, padx=5, pady=5)
|
||||
|
||||
# Add label for pen color buttons
|
||||
self.color_label = tk.Label(self.right_frame, text="^ drawing tools ^", 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', font=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
# Done button
|
||||
tk.Button(self.right_frame, text="Done", command=self.next, height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
# Adding a home button
|
||||
master.add_home_button(self.center_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, width=self.draw_size)
|
||||
self.draw.line([*self.last_draw, x, y], fill=0 if self.draw_color == 'black' else 1, width=self.draw_size)
|
||||
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(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") # Clear canvas
|
||||
self.drawing = Image.new('1', (475, 375), 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(346, 229, 475, 358, outline='black', fill='white')
|
||||
self.canvas.create_text(355, 260, text="QR", fill="black")
|
||||
|
||||
|
||||
|
||||
def import_image(self):
|
||||
# Open file dialog to select an image file
|
||||
file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.png")])
|
||||
|
||||
if file_path:
|
||||
# Check if the selected file has a correct extension
|
||||
if not (file_path.lower().endswith('.jpg') or file_path.lower().endswith('.png')):
|
||||
tk.messagebox.showerror("Invalid file", "Please select a .jpg or .png file.")
|
||||
return
|
||||
|
||||
# Load the image
|
||||
img = Image.open(file_path)
|
||||
|
||||
# Convert to 1-bit black and white
|
||||
img = img.convert('1')
|
||||
|
||||
# Resize or crop the image to fit the canvas
|
||||
if img.size[0] > 325 or img.size[1] > 179:
|
||||
img = img.crop((0, 0, 325, 179))
|
||||
|
||||
# Paste the imported image onto the drawing image
|
||||
self.drawing.paste(img)
|
||||
|
||||
# Save image reference to avoid garbage collection
|
||||
self.imported_img = ImageTk.PhotoImage(img)
|
||||
|
||||
# Clear the canvas
|
||||
self.canvas.delete("all")
|
||||
|
||||
# Add image to the canvas
|
||||
self.image_on_canvas = self.canvas.create_image(0, 0, image=self.imported_img, anchor='nw')
|
||||
|
||||
# Re-add QR box overlay
|
||||
# self.add_qr_box()
|
||||
|
||||
|
||||
# txt update
|
||||
class Screen9(tk.Frame):
|
||||
def __init__(self, master):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
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=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
#THX BYE
|
||||
class Screen10(tk.Frame):
|
||||
def __init__(self, master):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
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=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
# Sticker or tag?
|
||||
class Screen11(tk.Frame):
|
||||
def __init__(self, master):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
master.add_home_button(self)
|
||||
# Instructions
|
||||
self.label = tk.Label(self, text="Do you want to design a sticker or a ribbon? The sticker is suitable for hard surfaces and the ribbon is a tag suitable for sewing into textiles.",
|
||||
wraplength=400, # adjust to suit needs
|
||||
font=("Helvetica", 16))
|
||||
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=BUTTON_FONT).pack(side='top', pady=30)
|
||||
tk.Button(self, text="Ribbon tag", command=select_ribbon, height=4, width=39, bg='peach puff', font=BUTTON_FONT).pack(side='top', pady=30)
|
||||
|
||||
|
||||
# after QR scanned for lookup
|
||||
class Screen12(tk.Frame):
|
||||
def __init__(self, master):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
master.add_home_button(self)
|
||||
tk.Button(self, text="not implemented yet", command=lambda: master.switch_frame(Screen8), height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=10)
|
||||
tk.Button(self, text="Post Update (also not implemented)", command=lambda: master.switch_frame(Screen9), height=3, width=30, bg='peach puff', font=BUTTON_FONT).pack(pady=10)
|
||||
|
||||
#time to print
|
||||
class Screen13(tk.Frame):
|
||||
def __init__(self, master):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
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=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=BUTTON_FONT).grid(row=2, column=0, pady=20)
|
||||
|
||||
|
||||
# go ahead and print the thing
|
||||
def printy(self):
|
||||
global print_type
|
||||
|
||||
# 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)
|
||||
|
||||
# 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):
|
||||
global BUTTON_FONT
|
||||
global TEXT_FONT
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
|
||||
# divide the screen into two halves
|
||||
left_frame = tk.Frame(self, bg='#bcfef9') # updated background color
|
||||
right_frame = tk.Frame(self, bg='#bcfef9') # updated background color
|
||||
left_frame.pack(side='left', fill='both', expand=True)
|
||||
right_frame.pack(side='right', fill='both', expand=True)
|
||||
|
||||
# add the home button to the right frame
|
||||
master.add_home_button(right_frame)
|
||||
|
||||
# setup the instruction on the right side, with wraplength to avoid running off the screen
|
||||
instruction = tk.Label(right_frame, text="Please hold the QR code of a Custodisco tag up to the camera to be scanned. This isn't currently working! Sorry! If you download a Scuttlebutt client and follow the Custodisco Kiosk account you can look up Custodisco items on your own device. Ask someone who knows about Scuttlebutt for help ;)", font=("Helvetica", 16), bg='#bcfef9', wraplength=300) # updated wraplength
|
||||
instruction.pack(pady=100) # increased padding to avoid overlap with the button
|
||||
|
||||
# setup the video feed on the left side
|
||||
self.video = tk.Label(left_frame, bg='#bcfef9') # updated background color
|
||||
self.video.pack(pady=10)
|
||||
|
||||
# Open the camera for video capture
|
||||
self.cap = cv2.VideoCapture(0)
|
||||
self.master.after(10, self.update_frame)
|
||||
|
||||
def update_frame(self):
|
||||
global qr_code_value
|
||||
|
||||
# Capture frame-by-frame
|
||||
ret, frame = self.cap.read()
|
||||
|
||||
# Check if the frame is not None
|
||||
if not ret or frame is None:
|
||||
print("Failed to capture frame")
|
||||
return
|
||||
|
||||
# Our operations on the frame come here
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# Look for QR codes in the frame
|
||||
codes = decode(gray)
|
||||
|
||||
for code in codes:
|
||||
# If a QR code is detected, store its value and switch to Screen12
|
||||
qr_code_value = code.data.decode('utf-8')
|
||||
self.cap.release() # Close the video capture
|
||||
self.master.switch_frame(Screen12)
|
||||
return
|
||||
|
||||
# Display the resulting frame
|
||||
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
|
||||
img = Image.fromarray(cv2image)
|
||||
imgtk = ImageTk.PhotoImage(image=img)
|
||||
self.video.imgtk = imgtk
|
||||
self.video.configure(image=imgtk)
|
||||
self.master.after(10, self.update_frame) # Call the update_frame function after 10 milliseconds
|
||||
|
||||
def destroy(self):
|
||||
self.cap.release() # Close the video capture when the frame is destroyed
|
||||
super().destroy() # Call the parent class's destroy method
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = Kiosk()
|
||||
BUTTON_FONT = tkfont.Font(size=24, family='Helvetica')
|
||||
TEXT_FONT = tkfont.Font(size=20, family='Helvetica')
|
||||
app.mainloop()
|
||||
|
@ -50,8 +50,7 @@ void displayMainMenu(int buttonY) {
|
||||
}
|
||||
|
||||
void displayCreateItemScreen(int buttonY) {
|
||||
textSize(64);
|
||||
textFont(createFont("SansSerif", 64));
|
||||
textFont(createFont("Serif", 40));
|
||||
fill(0); // Black
|
||||
text("do you have an existing Scuttlebutt account?", width/2, height/4);
|
||||
|
||||
@ -121,11 +120,11 @@ buttonNoPressed = false;
|
||||
void checkButtonHover() {
|
||||
int buttonY = height/2 + (height - height/2) / 2;
|
||||
if(state == 0) {
|
||||
button1Hovered = mouseX > width/3 - 100 && mouseX < width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
button2Hovered = mouseX > 2 * width/3 - 100 && mouseX < 2 * width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
button1Hovered = mouseX > width/3 - 100 && mouseX < width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
button2Hovered = mouseX > 2 * width/3 - 100 && mouseX < 2 * width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
} else if(state == 1) {
|
||||
buttonYesHovered = mouseX > width/3 - 100 && mouseX < width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
buttonNoHovered = mouseX > 2 * width/3 - 100 && mouseX < 2 * width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
buttonYesHovered = mouseX > width/3 - 100 && mouseX < width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
buttonNoHovered = mouseX > 2 * width/3 - 100 && mouseX < 2 * width/3 + 100 && mouseY > buttonY - 50 && mouseY < buttonY + 50;
|
||||
}
|
||||
}
|
||||
|
17
old kiosks/description for gpt.txt
Normal file
17
old kiosks/description for gpt.txt
Normal file
@ -0,0 +1,17 @@
|
||||
hi. I wonder if you could write a python program for me. The program is a kiosk with a number of different screens. In addition to the buttons described below, each screen also needs a home button in the top left that returns to Screen 0 as well as a back button in the bottom left that goes to the previous screen. Below I've detailed each screen, please generate the corresponding full screen (1366 by 768 resolution) python program:
|
||||
|
||||
Screen 0: this is the title screen it has 2 buttons: create Item and Lookup Item. Create Item goes to Screen 1 and Lookup Item goes to Screen 14.
|
||||
Screen 1: this screen has 2 buttons, Yes and No I Want to Make One Now. Yes goes to Screen 2, No I Want to Make One Now goes to Screen 7.
|
||||
Screen 2: this screen contains a list that can be scrolled through and once an item from the list a Done button can be selected. The Done button goes to Screen 3.
|
||||
Screen 3: this screen contains a live video feed from the webcam. There is a button, Take Photo, which causes a 3-2-1 countdown to appear and then the camera video feed is frozen. Once a photo has been taken in this way the Continue button may be pressed which takes us to Screen 5.
|
||||
Screen 5: this screen contains a text box where the user can enter text. Once any text has been entered a Done button may be pressed which goes to Screen 8.
|
||||
Screen 6: this screen contains some text and a button that says "I Understand" which goes to Screen 3.
|
||||
Screen 7: this screen contains a texbox where a user can enter text. Once any text has been entered a Done button may be pressed which goes to Screen 6.
|
||||
Screen 8: this screen has a rectangle (whose shape is of the ratio 2.25:1.25) where the user can draw. There is 1 button, Done, which when pressed goes to Screen 11.
|
||||
Screen 9: this screen contains a text box which the user can enter text into. Once any text is entered they may click the Done button which takes us to Screen 10.
|
||||
Screen 10: this screen says "thank you!" and has a big done button which goes to Screen 0.
|
||||
Screen 11: this screen has 2 buttons, Sticker and Tag. A printType variable needs to be stored depending on which button is pressed. Both buttons take us to Screen 10.
|
||||
Screen 12: this screen has 2 buttons, Re-print Tag, which takes us to Screen 8, and Post Update, which takes us to Screen 9.
|
||||
Screen 14: this screen shows a live video feed. Once a QR code has been detected on the video, the QR code is decoded and stored to a variable. Then the screen changes to Screen 12.
|
||||
|
||||
thanks so much!
|
112
old kiosks/kiosk.py
Normal file
112
old kiosks/kiosk.py
Normal file
@ -0,0 +1,112 @@
|
||||
import pygame
|
||||
import sys
|
||||
|
||||
# Colors
|
||||
LIGHT_SKY_BLUE = (135, 206, 235)
|
||||
BLACK = (0, 0, 0)
|
||||
PEACH = (255, 218, 185)
|
||||
|
||||
# Screen dimensions
|
||||
SCREEN_WIDTH = 1366
|
||||
SCREEN_HEIGHT = 768
|
||||
|
||||
# Button dimensions
|
||||
BUTTON_WIDTH = 200
|
||||
BUTTON_HEIGHT = 100
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
|
||||
# Create the screen
|
||||
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.FULLSCREEN)
|
||||
|
||||
# Fonts
|
||||
title_font = pygame.font.SysFont("sans-serif", 64)
|
||||
subtitle_font = pygame.font.SysFont("serif", 32, italic=True)
|
||||
button_font = pygame.font.SysFont("sans-serif", 24)
|
||||
|
||||
class Button:
|
||||
def __init__(self, x, y, width, height, text, font, color, action=None):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.text = text
|
||||
self.font = font
|
||||
self.color = color
|
||||
self.action = action
|
||||
|
||||
def display(self):
|
||||
pygame.draw.rect(screen, self.color, (self.x - self.width/2, self.y - self.height/2, self.width, self.height))
|
||||
text_surface = self.font.render(self.text, True, BLACK)
|
||||
text_rect = text_surface.get_rect(center=(self.x, self.y))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
def is_hovered(self):
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
return pygame.Rect(self.x - self.width / 2, self.y - self.height / 2, self.width, self.height).collidepoint(mouse_pos)
|
||||
|
||||
def is_pressed(self):
|
||||
return self.is_hovered() and pygame.mouse.get_pressed()[0]
|
||||
|
||||
def handle_event(self, event):
|
||||
if event.type == pygame.MOUSEBUTTONUP and self.is_hovered():
|
||||
if self.action:
|
||||
self.action()
|
||||
|
||||
class Screen:
|
||||
def __init__(self, buttons, background_color):
|
||||
self.buttons = buttons
|
||||
self.background_color = background_color
|
||||
|
||||
def handle_event(self, event):
|
||||
for button in self.buttons:
|
||||
button.handle_event(event)
|
||||
|
||||
def display(self):
|
||||
screen.fill(self.background_color)
|
||||
for button in self.buttons:
|
||||
button.display()
|
||||
|
||||
# Define the buttons and screens
|
||||
buttons_main_menu = [
|
||||
Button(SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT, "create item", button_font, PEACH),
|
||||
Button(2 * SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT, "lookup item", button_font, PEACH),
|
||||
Button(50, 50, 50, 50, "H", button_font, PEACH)
|
||||
]
|
||||
buttons_create_item_screen = [
|
||||
Button(SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT, "yes", button_font, PEACH),
|
||||
Button(2 * SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT, "no", button_font, PEACH),
|
||||
Button(50, SCREEN_HEIGHT - 50, 50, 50, "<", button_font, PEACH),
|
||||
Button(50, 50, 50, 50, "H", button_font, PEACH)
|
||||
]
|
||||
main_menu_screen = Screen(buttons_main_menu, LIGHT_SKY_BLUE)
|
||||
create_item_screen = Screen(buttons_create_item_screen, LIGHT_SKY_BLUE)
|
||||
|
||||
# Set the actions of the buttons
|
||||
def go_to_create_item_screen():
|
||||
global current_screen
|
||||
current_screen = create_item_screen
|
||||
|
||||
buttons_main_menu[0].action = go_to_create_item_screen
|
||||
|
||||
# Game loop
|
||||
clock = pygame.time.Clock()
|
||||
running = True
|
||||
current_screen = main_menu_screen
|
||||
while running:
|
||||
clock.tick(60) # Limit the frame rate to 60 FPS
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
else:
|
||||
current_screen.handle_event(event)
|
||||
|
||||
# Render screen
|
||||
current_screen.display()
|
||||
|
||||
pygame.display.flip()
|
||||
|
181
old kiosks/kiosk2.py
Normal file
181
old kiosks/kiosk2.py
Normal file
@ -0,0 +1,181 @@
|
||||
import pygame
|
||||
import sys
|
||||
|
||||
# Colors
|
||||
LIGHT_SKY_BLUE = (135, 206, 235)
|
||||
BLACK = (0, 0, 0)
|
||||
PEACH = (255, 218, 185)
|
||||
DARK_PEACH = (205, 175, 149)
|
||||
DARKER_PEACH = (235, 200, 175)
|
||||
YELLOW = (255, 223, 0)
|
||||
|
||||
# Screen dimensions
|
||||
SCREEN_WIDTH = 800
|
||||
SCREEN_HEIGHT = 600
|
||||
|
||||
# Button dimensions
|
||||
BUTTON_WIDTH = 200
|
||||
BUTTON_HEIGHT = 100
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
|
||||
# Create the screen
|
||||
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.FULLSCREEN)
|
||||
|
||||
# Fonts
|
||||
title_font = pygame.font.SysFont("sans-serif", 64)
|
||||
subtitle_font = pygame.font.SysFont("serif", 32, italic=True)
|
||||
button_font = pygame.font.SysFont("sans-serif", 24)
|
||||
|
||||
# Text
|
||||
title_text = "custodisco"
|
||||
subtitle_text = "hi, what do you want to do?"
|
||||
button1_text = "create item"
|
||||
button2_text = "lookup item"
|
||||
button_yes_text = "yes"
|
||||
button_no_text = "no, I'd like to create one now"
|
||||
|
||||
# Button states
|
||||
button1_hovered = False
|
||||
button2_hovered = False
|
||||
button1_pressed = False
|
||||
button2_pressed = False
|
||||
button_yes_hovered = False
|
||||
button_no_hovered = False
|
||||
button_yes_pressed = False
|
||||
button_no_pressed = False
|
||||
back_button_hovered = False
|
||||
back_button_pressed = False
|
||||
home_button_hovered = False
|
||||
home_button_pressed = False
|
||||
|
||||
# Function to calculate button text dimensions
|
||||
def calculate_text_dimensions(text, font):
|
||||
text_width, text_height = font.size(text)
|
||||
return text_width, text_height
|
||||
|
||||
# Function to display text on the screen
|
||||
def display_text(text, font, color, x, y):
|
||||
text_surface = font.render(text, True, color)
|
||||
text_rect = text_surface.get_rect(center=(x, y))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# Function to display a button
|
||||
def display_button(text, font, color, x, y, width, height):
|
||||
pygame.draw.rect(screen, color, (x - width/2, y - height/2, width, height))
|
||||
text_width, text_height = calculate_text_dimensions(text, font)
|
||||
display_text(text, font, BLACK, x, y)
|
||||
|
||||
# Function to draw a sparkle
|
||||
def draw_sparkle(x, y):
|
||||
pygame.draw.line(screen, YELLOW, (x - 10, y - 10), (x + 10, y + 10), 2)
|
||||
pygame.draw.line(screen, YELLOW, (x + 10, y - 10), (x - 10, y + 10), 2)
|
||||
pygame.draw.line(screen, YELLOW, (x, y - 15), (x, y + 15), 2)
|
||||
pygame.draw.line(screen, YELLOW, (x - 15, y), (x + 15, y), 2)
|
||||
|
||||
# Main menu screen
|
||||
def display_main_menu():
|
||||
screen.fill(LIGHT_SKY_BLUE)
|
||||
|
||||
# Title
|
||||
display_text(title_text, title_font, BLACK, SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)
|
||||
draw_sparkle(SCREEN_WIDTH // 2 + 150, SCREEN_HEIGHT // 4 - 20)
|
||||
|
||||
# Subtitle
|
||||
display_text(subtitle_text, subtitle_font, BLACK, SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3)
|
||||
|
||||
# Button 1
|
||||
display_button(button1_text, button_font, PEACH, SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
|
||||
# Button 2
|
||||
display_button(button2_text, button_font, PEACH, 2 * SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
|
||||
# Home button
|
||||
display_button("H", button_font, PEACH, 50, 50, 50, 50)
|
||||
|
||||
# Create item screen
|
||||
def display_create_item_screen():
|
||||
screen.fill(LIGHT_SKY_BLUE)
|
||||
|
||||
# Title
|
||||
title_width, _ = calculate_text_dimensions("do you have an existing Scuttlebutt account?", subtitle_font)
|
||||
title_x = SCREEN_WIDTH // 2
|
||||
title_y = SCREEN_HEIGHT // 4
|
||||
display_text("do you have an existing Scuttlebutt account?", subtitle_font, BLACK, title_x, title_y)
|
||||
|
||||
# Button "Yes"
|
||||
display_button(button_yes_text, button_font, PEACH, SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
|
||||
# Button "No"
|
||||
display_button(button_no_text, button_font, PEACH, 2 * SCREEN_WIDTH // 3, SCREEN_HEIGHT // 2, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
|
||||
# Back button
|
||||
display_button("<", button_font, PEACH, 50, SCREEN_HEIGHT - 50, 50, 50)
|
||||
|
||||
# Home button
|
||||
display_button("H", button_font, PEACH, 50, 50, 50, 50)
|
||||
|
||||
# Game loop
|
||||
clock = pygame.time.Clock()
|
||||
running = True
|
||||
state = "main_menu" # Initial state
|
||||
while running:
|
||||
clock.tick(60) # Limit the frame rate to 60 FPS
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if state == "main_menu":
|
||||
if button1_hovered:
|
||||
button1_pressed = True
|
||||
elif button2_hovered:
|
||||
button2_pressed = True
|
||||
elif home_button_hovered:
|
||||
state = "main_menu"
|
||||
elif state == "create_item_screen":
|
||||
if button_yes_hovered:
|
||||
button_yes_pressed = True
|
||||
elif button_no_hovered:
|
||||
button_no_pressed = True
|
||||
elif back_button_hovered:
|
||||
state = "main_menu"
|
||||
elif home_button_hovered:
|
||||
state = "main_menu"
|
||||
elif event.type == pygame.MOUSEBUTTONUP:
|
||||
if state == "main_menu":
|
||||
if button1_pressed and button1_hovered:
|
||||
state = "create_item_screen"
|
||||
print("Going to create item screen")
|
||||
elif button2_pressed and button2_hovered:
|
||||
print("Going to lookup item screen")
|
||||
button1_pressed = False
|
||||
button2_pressed = False
|
||||
elif state == "create_item_screen":
|
||||
if button_yes_pressed and button_yes_hovered:
|
||||
print("You selected Yes")
|
||||
elif button_no_pressed and button_no_hovered:
|
||||
print("You selected No")
|
||||
button_yes_pressed = False
|
||||
button_no_pressed = False
|
||||
|
||||
# Update button states
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
button1_hovered = pygame.Rect(SCREEN_WIDTH // 3 - BUTTON_WIDTH / 2, SCREEN_HEIGHT // 2 - BUTTON_HEIGHT / 2, BUTTON_WIDTH, BUTTON_HEIGHT).collidepoint(mouse_pos)
|
||||
button2_hovered = pygame.Rect(2 * SCREEN_WIDTH // 3 - BUTTON_WIDTH / 2, SCREEN_HEIGHT // 2 - BUTTON_HEIGHT / 2, BUTTON_WIDTH, BUTTON_HEIGHT).collidepoint(mouse_pos)
|
||||
button_yes_hovered = pygame.Rect(SCREEN_WIDTH // 3 - BUTTON_WIDTH / 2, SCREEN_HEIGHT // 2 - BUTTON_HEIGHT / 2, BUTTON_WIDTH, BUTTON_HEIGHT).collidepoint(mouse_pos)
|
||||
button_no_hovered = pygame.Rect(2 * SCREEN_WIDTH // 3 - BUTTON_WIDTH / 2, SCREEN_HEIGHT // 2 - BUTTON_HEIGHT / 2, BUTTON_WIDTH, BUTTON_HEIGHT).collidepoint(mouse_pos)
|
||||
back_button_hovered = pygame.Rect(50 - 25, SCREEN_HEIGHT - 50 - 25, 50, 50).collidepoint(mouse_pos)
|
||||
home_button_hovered = pygame.Rect(50 - 25, 50 - 25, 50, 50).collidepoint(mouse_pos)
|
||||
|
||||
# Render screen based on state
|
||||
if state == "main_menu":
|
||||
display_main_menu()
|
||||
elif state == "create_item_screen":
|
||||
display_create_item_screen()
|
||||
|
||||
pygame.display.flip()
|
||||
|
91
old kiosks/kiosk3.py
Normal file
91
old kiosks/kiosk3.py
Normal file
@ -0,0 +1,91 @@
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtCore import *
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
self.stacked_widget = QStackedWidget()
|
||||
self.setCentralWidget(self.stacked_widget)
|
||||
|
||||
self.screens = {
|
||||
"Screen0": Screen0(self),
|
||||
"Screen1": Screen1(self),
|
||||
"Screen2": Screen2(self),
|
||||
"Screen3": Screen3(self),
|
||||
"Screen5": Screen5(self),
|
||||
"Screen6": Screen6(self),
|
||||
"Screen7": Screen7(self),
|
||||
"Screen8": Screen8(self),
|
||||
"Screen9": Screen9(self),
|
||||
"Screen10": Screen10(self),
|
||||
"Screen11": Screen11(self),
|
||||
"Screen12": Screen12(self),
|
||||
"Screen14": Screen14(self),
|
||||
}
|
||||
|
||||
for screen in self.screens.values():
|
||||
self.stacked_widget.addWidget(screen)
|
||||
|
||||
self.stacked_widget.setCurrentWidget(self.screens["Screen0"])
|
||||
|
||||
def go_to_screen(self, screen_name):
|
||||
self.stacked_widget.setCurrentWidget(self.screens[screen_name])
|
||||
|
||||
|
||||
class BaseScreen(QWidget):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(BaseScreen, self).__init__(parent)
|
||||
self.main_window = main_window
|
||||
self.layout = QVBoxLayout()
|
||||
|
||||
self.home_button = QPushButton('Home')
|
||||
self.home_button.clicked.connect(lambda: self.main_window.go_to_screen("Screen0"))
|
||||
self.layout.addWidget(self.home_button)
|
||||
|
||||
self.back_button = QPushButton('Back')
|
||||
# TODO: implement back functionality
|
||||
self.layout.addWidget(self.back_button)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
|
||||
class Screen0(BaseScreen):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(Screen0, self).__init__(main_window, parent)
|
||||
self.create_item_button = QPushButton('Create Item')
|
||||
self.create_item_button.clicked.connect(lambda: self.main_window.go_to_screen("Screen1"))
|
||||
self.layout.addWidget(self.create_item_button)
|
||||
|
||||
self.lookup_item_button = QPushButton('Lookup Item')
|
||||
self.lookup_item_button.clicked.connect(lambda: self.main_window.go_to_screen("Screen14"))
|
||||
self.layout.addWidget(self.lookup_item_button)
|
||||
|
||||
|
||||
class Screen1(BaseScreen):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(Screen1, self).__init__(main_window, parent)
|
||||
self.yes_button = QPushButton('Yes')
|
||||
self.yes_button.clicked.connect(lambda: self.main_window.go_to_screen("Screen2"))
|
||||
self.layout.addWidget(self.yes_button)
|
||||
|
||||
self.no_button = QPushButton('No I Want to Make One Now')
|
||||
self.no_button.clicked.connect(lambda: self.main_window.go_to_screen("Screen7"))
|
||||
self.layout.addWidget(self.no_button)
|
||||
|
||||
# ... other screens ...
|
||||
|
||||
|
||||
class Screen14(BaseScreen):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(Screen14, self).__init__(main_window, parent)
|
||||
self.qr_button = QPushButton('QR')
|
||||
# TODO: Implement QR functionality
|
||||
self.layout.addWidget(self.qr_button)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
window = MainWindow()
|
||||
window.showFullScreen()
|
||||
app.exec_()
|
||||
|
76
old kiosks/kiosk4.py
Normal file
76
old kiosks/kiosk4.py
Normal file
@ -0,0 +1,76 @@
|
||||
import pygame
|
||||
import sys
|
||||
|
||||
class Button:
|
||||
def __init__(self, x, y, w, h, text, callback):
|
||||
self.rect = pygame.Rect(x, y, w, h)
|
||||
self.text = text
|
||||
self.callback = callback
|
||||
|
||||
def handle_event(self, event):
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if self.rect.collidepoint(event.pos):
|
||||
self.callback()
|
||||
|
||||
def draw(self, screen):
|
||||
pygame.draw.rect(screen, (255, 255, 255), self.rect)
|
||||
font = pygame.font.Font(None, 36)
|
||||
text_surf = font.render(self.text, True, (0, 0, 0))
|
||||
screen.blit(text_surf, (self.rect.x + 10, self.rect.y + 10))
|
||||
|
||||
class Screen:
|
||||
def __init__(self):
|
||||
self.buttons = []
|
||||
|
||||
def handle_event(self, event):
|
||||
for button in self.buttons:
|
||||
button.handle_event(event)
|
||||
|
||||
def draw(self, screen):
|
||||
for button in self.buttons:
|
||||
button.draw(screen)
|
||||
|
||||
class Screen0(Screen):
|
||||
def __init__(self, screens):
|
||||
super().__init__()
|
||||
self.buttons.append(Button(50, 50, 200, 100, "Create Item", lambda: screens.set_current("Screen1")))
|
||||
self.buttons.append(Button(50, 200, 200, 100, "Lookup Item", lambda: screens.set_current("Screen14")))
|
||||
|
||||
# ... other screens ...
|
||||
|
||||
class Screens:
|
||||
def __init__(self):
|
||||
self.screens = {
|
||||
"Screen0": Screen0(self),
|
||||
# ... other screens ...
|
||||
}
|
||||
self.current = self.screens["Screen0"]
|
||||
|
||||
def set_current(self, name):
|
||||
self.current = self.screens[name]
|
||||
|
||||
def handle_event(self, event):
|
||||
self.current.handle_event(event)
|
||||
|
||||
def draw(self, screen):
|
||||
self.current.draw(screen)
|
||||
|
||||
def main():
|
||||
pygame.init()
|
||||
screen = pygame.display.set_mode((1366, 768))
|
||||
screens = Screens()
|
||||
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
screens.handle_event(event)
|
||||
|
||||
screen.fill((0, 0, 0))
|
||||
screens.draw(screen)
|
||||
pygame.display.flip()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
107
old kiosks/kiosk5.py
Normal file
107
old kiosks/kiosk5.py
Normal file
@ -0,0 +1,107 @@
|
||||
import tkinter as tk
|
||||
from tkinter import font as tkfont
|
||||
|
||||
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)
|
||||
|
||||
class Screen0(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
title_font = tkfont.Font(size=30, family='Helvetica')
|
||||
tk.Label(self, text="Custodisco ✨", bg='#bcfef9', font=title_font).pack()
|
||||
tk.Button(self, text="Create Item", command=lambda: master.switch_frame(Screen1)).pack()
|
||||
tk.Button(self, text="Lookup Item", command=lambda: master.switch_frame(Screen14)).pack()
|
||||
|
||||
|
||||
class Screen1(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Button(self, text="Yes", command=lambda: master.switch_frame(Screen2)).pack()
|
||||
tk.Button(self, text="No I Want to Make One Now", command=lambda: master.switch_frame(Screen7)).pack()
|
||||
|
||||
class Screen2(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Label(self, text="List Placeholder").pack()
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen3)).pack()
|
||||
|
||||
class Screen3(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Button(self, text="Take Photo", command=lambda: master.switch_frame(Screen5)).pack()
|
||||
|
||||
class Screen5(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Label(self, text="Text Entry Placeholder").pack()
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen8)).pack()
|
||||
|
||||
class Screen6(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Label(self, text="Some Text").pack()
|
||||
tk.Button(self, text="I Understand", command=lambda: master.switch_frame(Screen3)).pack()
|
||||
|
||||
class Screen7(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Label(self, text="Text Entry Placeholder").pack()
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen6)).pack()
|
||||
|
||||
class Screen8(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen11)).pack()
|
||||
|
||||
class Screen9(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Label(self, text="Text Entry Placeholder").pack()
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen10)).pack()
|
||||
|
||||
class Screen10(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Label(self, text="Thank you!").pack()
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen0)).pack()
|
||||
|
||||
class Screen11(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Button(self, text="Sticker", command=lambda: master.switch_frame(Screen10)).pack()
|
||||
tk.Button(self, text="Tag", command=lambda: master.switch_frame(Screen10)).pack()
|
||||
|
||||
class Screen12(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Button(self, text="Re-print Tag", command=lambda: master.switch_frame(Screen8)).pack()
|
||||
tk.Button(self, text="Post Update", command=lambda: master.switch_frame(Screen9)).pack()
|
||||
|
||||
class Screen14(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master)
|
||||
tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen12)).pack()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = Kiosk()
|
||||
app.mainloop()
|
||||
|
94
old kiosks/my version.py
Normal file
94
old kiosks/my version.py
Normal file
@ -0,0 +1,94 @@
|
||||
# find yourself in list of ssb users
|
||||
class Screen2(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
|
||||
# Add label for instructions
|
||||
tk.Label(self, text="Start typing your public key to find yourself in the list", bg='#bcfef9').pack(anchor='nw')
|
||||
|
||||
# Add entry box for user search
|
||||
self.search_var = tk.StringVar()
|
||||
self.search_var.trace('w', self.update_users_list)
|
||||
self.search_entry = tk.Entry(self, textvariable=self.search_var, width=50)
|
||||
self.search_entry.pack(anchor='nw')
|
||||
|
||||
# Refresh button
|
||||
refresh_button = tk.Button(self, text="Refresh List", command=self.refresh_users, height=3, width=30, bg='peach puff')
|
||||
refresh_button.place(relx=0.25, rely=0, anchor='nw')
|
||||
|
||||
# The 'Done' button to navigate to next screen
|
||||
done_button = tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen3), height=3, width=30, bg='peach puff')
|
||||
done_button.pack(pady=10)
|
||||
|
||||
|
||||
# Container for user list
|
||||
self.container = ttk.Frame(self)
|
||||
self.container.pack(fill='both', expand=True)
|
||||
|
||||
self.update_users_list()
|
||||
|
||||
def update_users_list(self, *args):
|
||||
search_text = self.search_var.get().lower()
|
||||
|
||||
# Remove current users list
|
||||
for widget in self.container.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
# Filtered list of users
|
||||
users = [user for user in self.users if search_text in user['id'].lower()]
|
||||
|
||||
self.display_users(users)
|
||||
|
||||
|
||||
def refresh_users(self):
|
||||
# Update users list from Scuttlebutt, save it to users.json and display it
|
||||
users = self.get_scuttlebutt_users()
|
||||
self.save_users_to_file(users)
|
||||
self.display_users(users)
|
||||
|
||||
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):
|
||||
if os.path.exists('users.json') and os.path.getsize('users.json') > 0:
|
||||
try:
|
||||
with open('users.json', 'r') as f:
|
||||
users = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
users = []
|
||||
else:
|
||||
users = []
|
||||
return users
|
||||
|
||||
def save_users_to_file(self, users):
|
||||
with open('users.json', 'w') as f:
|
||||
|
||||
json.dump(users, f)
|
||||
def display_users(self, users):
|
||||
# Scrollable list of users
|
||||
canvas = tk.Canvas(self.container)
|
||||
scrollbar = ttk.Scrollbar(self.container, orient='vertical', command=canvas.yview, width=60)
|
||||
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)
|
||||
|
||||
for user in users:
|
||||
tk.Label(scrollable_frame, text=user['id']).pack()
|
||||
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
90
old kiosks/theirversion.py
Normal file
90
old kiosks/theirversion.py
Normal file
@ -0,0 +1,90 @@
|
||||
class Screen2(tk.Frame):
|
||||
def __init__(self, master):
|
||||
tk.Frame.__init__(self, master, bg='#bcfef9')
|
||||
|
||||
# Load users from users.json if it exists and is not empty
|
||||
self.users = self.get_users_from_file()
|
||||
|
||||
# Label for instructions
|
||||
tk.Label(self, text="Start typing your public key to find yourself in the list", bg='#bcfef9').pack(anchor='nw')
|
||||
|
||||
# Entry box for user search
|
||||
self.search_var = tk.StringVar()
|
||||
self.search_var.trace('w', self.update_users_list)
|
||||
self.search_entry = tk.Entry(self, textvariable=self.search_var, width=50)
|
||||
self.search_entry.pack(anchor='nw')
|
||||
|
||||
# Refresh button
|
||||
refresh_button = tk.Button(self, text="Refresh List", command=self.refresh_users, height=3, width=30, bg='peach puff')
|
||||
refresh_button.place(relx=0.25, rely=0, anchor='nw')
|
||||
|
||||
# The 'Done' button to navigate to next screen
|
||||
done_button = tk.Button(self, text="Done", command=lambda: master.switch_frame(Screen3), height=3, width=30, bg='peach puff')
|
||||
done_button.pack(pady=10)
|
||||
|
||||
# Container for user list
|
||||
self.container = ttk.Frame(self)
|
||||
self.container.pack(fill='both', expand=True)
|
||||
|
||||
# Display initial list of users
|
||||
self.update_users_list()
|
||||
|
||||
def get_users_from_file(self):
|
||||
# Load users from users.json if it exists and is not empty
|
||||
if os.path.exists("users.json") and os.path.getsize("users.json") > 0:
|
||||
with open("users.json", 'r') as f:
|
||||
try:
|
||||
users = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
print("users.json is not valid JSON. Refreshing users...")
|
||||
users = self.refresh_users()
|
||||
else:
|
||||
users = self.refresh_users()
|
||||
return users
|
||||
|
||||
def refresh_users(self):
|
||||
# Update users list from Scuttlebutt and display it
|
||||
users = self.get_scuttlebutt_users()
|
||||
self.users = 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)
|
||||
# Save the users list to a JSON file
|
||||
with open('users.json', 'w') as f:
|
||||
json.dump(users, f)
|
||||
else:
|
||||
print("Command failed: " + result.stderr)
|
||||
users = []
|
||||
return users
|
||||
|
||||
def update_users_list(self, *args):
|
||||
search_text = self.search_var.get().lower()
|
||||
|
||||
# Remove current users list
|
||||
for widget in self.container.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
# Filtered list of users
|
||||
users = [user for user in self.users if search_text in user['id'].lower()]
|
||||
|
||||
self.display_users(users)
|
||||
|
||||
def display_users(self, users):
|
||||
# Scrollable list of users
|
||||
canvas = tk.Canvas(self.container)
|
||||
scrollbar = ttk.Scrollbar(self.container, orient='vertical', command=canvas.yview, width=60)
|
||||
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
|
||||
|
BIN
ribbon-template.png
Normal file
BIN
ribbon-template.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
39
scuttlebot (copy 1).js
Normal file
39
scuttlebot (copy 1).js
Normal file
@ -0,0 +1,39 @@
|
||||
// scuttlebot.js
|
||||
|
||||
const ssbClient = require('ssb-client');
|
||||
const ssbKeys = require('ssb-keys');
|
||||
const pull = require('pull-stream');
|
||||
|
||||
const keys = ssbKeys.loadOrCreateSync('~/.ssb/secret');
|
||||
|
||||
ssbClient(keys, (err, sbot) => {
|
||||
if (err) {
|
||||
console.error(JSON.stringify({ "error": "Failed to connect to the Scuttlebot server. Is it running?" }));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const authors = new Set();
|
||||
|
||||
pull(
|
||||
sbot.messagesByType("contact"),
|
||||
pull.drain((msg) => {
|
||||
authors.add(msg.value.author);
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.error(JSON.stringify({ "error": "Failed to retrieve messages." }));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const feeds = Array.from(authors).map(author => { return { id: author } });
|
||||
console.log(JSON.stringify(feeds));
|
||||
try {
|
||||
sbot.close(() => {});
|
||||
} catch(err) {
|
||||
console.error("Error closing SSB server connection: ", err);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
68
scuttlebot.js
Normal file
68
scuttlebot.js
Normal file
@ -0,0 +1,68 @@
|
||||
// scuttlebot.js
|
||||
|
||||
const ssbClient = require('ssb-client');
|
||||
const ssbKeys = require('ssb-keys');
|
||||
const pull = require('pull-stream');
|
||||
|
||||
const keys = ssbKeys.loadOrCreateSync('~/.ssb/secret');
|
||||
|
||||
ssbClient(keys, (err, sbot) => {
|
||||
if (err) {
|
||||
console.error(JSON.stringify({ "error": "Failed to connect to the Scuttlebot server. Is it running?" }));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const authors = new Set();
|
||||
const aliases = new Map();
|
||||
|
||||
pull(
|
||||
sbot.messagesByType("contact"),
|
||||
pull.drain((msg) => {
|
||||
authors.add(msg.value.author);
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.error(JSON.stringify({ "error": "Failed to retrieve messages." }));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Fetch aliases for all authors
|
||||
pull(
|
||||
sbot.query.read({
|
||||
query: [{
|
||||
$filter: {
|
||||
value: {
|
||||
content: { type: 'about' }
|
||||
}
|
||||
}
|
||||
}]
|
||||
}),
|
||||
pull.drain((msg) => {
|
||||
if (msg.value.content.about && msg.value.content.name) {
|
||||
aliases.set(msg.value.content.about, msg.value.content.name);
|
||||
}
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.error(JSON.stringify({ "error": "Failed to retrieve aliases." }));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const feeds = Array.from(authors).map(author => {
|
||||
return {
|
||||
id: author,
|
||||
alias: aliases.get(author) || ''
|
||||
}
|
||||
});
|
||||
console.log(JSON.stringify(feeds));
|
||||
try {
|
||||
sbot.close(() => {});
|
||||
} catch(err) {
|
||||
console.error("Error closing SSB server connection: ", err);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
26
ssb-custodisco-plugin.js
Normal file
26
ssb-custodisco-plugin.js
Normal file
@ -0,0 +1,26 @@
|
||||
const ssbClient = require('ssb-client')
|
||||
|
||||
module.exports = {
|
||||
name: 'custodisco',
|
||||
version: '1.0.0',
|
||||
manifest: {
|
||||
getItem: 'async'
|
||||
},
|
||||
init: (server, config) => {
|
||||
return {
|
||||
getItem: async (id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ssbClient((err, sbot) => {
|
||||
if (err) return reject(err)
|
||||
|
||||
sbot.get(id, (err, value) => {
|
||||
sbot.close()
|
||||
if (err) return reject(err)
|
||||
resolve(value)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
152
ssb-post.sh
Executable file
152
ssb-post.sh
Executable file
@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
# custodisco posting
|
||||
#GIVE and MINT options
|
||||
|
||||
|
||||
## ok we need to be passed a number of arguments
|
||||
## item description/GIVE to account ID = $2
|
||||
## image path/messageID of existing item = $3
|
||||
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "need arguments pls"
|
||||
fi
|
||||
|
||||
|
||||
## mint
|
||||
if [ "$1" == "mint" ]
|
||||
then
|
||||
itemDescription=$2
|
||||
itemImagePath=$3
|
||||
# remove meta-data from the image and compress a bit
|
||||
jpegoptim --strip-all --overwrite "$itemImagePath" --size=200k > /dev/null 2>&1
|
||||
|
||||
|
||||
#add blob
|
||||
blobID=$(cat $itemImagePath | ssb-server blobs.add) || exit 1
|
||||
|
||||
# publish the item
|
||||
ssb-server publish . <<BLAAB
|
||||
{
|
||||
"type":"post",
|
||||
"text":"\\n\\n$itemDescription\\n\\n a #custodisco item ",
|
||||
"custodisco":"true",
|
||||
"nft": "mint",
|
||||
"mentions": [
|
||||
{
|
||||
"name": "photo.jpg",
|
||||
"type": "image/jpeg",
|
||||
"link": "$blobID"
|
||||
}
|
||||
]
|
||||
}
|
||||
BLAAB
|
||||
fi
|
||||
|
||||
|
||||
## give
|
||||
if [ "$1" == "give" ]
|
||||
then
|
||||
account=$2
|
||||
messageID=$3
|
||||
|
||||
ssb-server publish . <<BLAAB
|
||||
{
|
||||
"type":"post",
|
||||
"text":"This item is stewarded by $account",
|
||||
"custodisco":"true",
|
||||
"nft": "give",
|
||||
"target": "$account",
|
||||
"root": "$messageID",
|
||||
"branch": "$messageID"
|
||||
}
|
||||
BLAAB
|
||||
fi
|
||||
|
||||
|
||||
## get_message_content
|
||||
if [ "$1" == "get_message_content" ]
|
||||
then
|
||||
messageID=$2
|
||||
|
||||
echo "Attempting to retrieve message: $messageID" >&2
|
||||
|
||||
# Get message content
|
||||
message_content=$(ssb-server get "$messageID" 2>&1)
|
||||
|
||||
# Check if ssb-server get was successful
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: ssb-server get failed" >&2
|
||||
echo "$message_content" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Successfully retrieved message content" >&2
|
||||
|
||||
# Extract blob information
|
||||
blobs=$(echo "$message_content" | jq -r '.content.mentions[] | select(.link | startswith("&")) | .link')
|
||||
|
||||
# Process blobs
|
||||
if [ -n "$blobs" ]; then
|
||||
echo "Processing blobs..." >&2
|
||||
echo "$message_content" | jq -c '.content.mentions[]' | while read -r mention; do
|
||||
blob=$(echo "$mention" | jq -r '.link')
|
||||
if [[ "$blob" == \&* ]]; then
|
||||
echo "Found a blob: $blob" >&2
|
||||
# First, try to get the mime type from the message content
|
||||
mime_type=$(echo "$mention" | jq -r '.type')
|
||||
|
||||
# If mime type is not in the message content, try to get it from blobs.meta
|
||||
if [[ "$mime_type" == "null" || -z "$mime_type" ]]; then
|
||||
if ssb-server blobs.has "$blob"; then
|
||||
mime_type=$(ssb-server blobs.meta "$blob" | jq -r '.type')
|
||||
else
|
||||
echo "Blob not available locally: $blob" >&2
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Blob mime type: $mime_type" >&2
|
||||
|
||||
if [[ "$mime_type" == "image/jpeg" || "$mime_type" == "image/png" ]]; then
|
||||
# Create tmp directory if it doesn't exist
|
||||
mkdir -p tmp
|
||||
|
||||
# Get blob and save it to a file
|
||||
file_extension="${mime_type#image/}"
|
||||
short_name=$(echo "$blob" | md5sum | cut -d' ' -f1)
|
||||
file_name="${short_name}.$file_extension"
|
||||
full_path="tmp/$file_name"
|
||||
|
||||
echo "Attempting to save blob to: $full_path" >&2
|
||||
echo "Executing command: ssb-server blobs.get $blob > $full_path" >&2
|
||||
|
||||
if ssb-server blobs.get "$blob" > "$full_path" 2>/dev/null; then
|
||||
echo "Blob saved: $full_path" >&2
|
||||
echo "IMAGE_PATH:$full_path"
|
||||
else
|
||||
echo "Failed to retrieve blob: $blob" >&2
|
||||
echo "ssb-server blobs.get exit code: $?" >&2
|
||||
# If the file wasn't created, let's try to output the blob data directly
|
||||
echo "Attempting to output blob data directly:" >&2
|
||||
ssb-server blobs.get "$blob" | head -c 100 | xxd >&2
|
||||
fi
|
||||
else
|
||||
echo "Skipping non-image blob: $blob (type: $mime_type)" >&2
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "No blobs found in the message." >&2
|
||||
fi
|
||||
|
||||
# Get replies
|
||||
replies=$(ssb-server query.read --private --reverse --limit 200 --query '{"value":{"content":{"root":"'$messageID'"}}}')
|
||||
|
||||
# Output message content and replies
|
||||
echo "$message_content"
|
||||
echo "REPLIES_START"
|
||||
echo "$replies"
|
||||
echo "REPLIES_END"
|
||||
fi
|
BIN
sticker-template.png
Normal file
BIN
sticker-template.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
135
tozpl.py
Normal file
135
tozpl.py
Normal file
@ -0,0 +1,135 @@
|
||||
#this code was converted to python from http://www.jcgonzalez.com/java-image-to-zpl-example by chatgpt and trav. We modified it together a bit to only accept black and white images :)
|
||||
|
||||
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
class ZPLConveter:
|
||||
def __init__(self):
|
||||
self.total = 0
|
||||
self.width_bytes = 0
|
||||
self.compress_hex = False
|
||||
self.map_code = {1: 'G', 2: 'H', 3: 'I', 4: 'J', 5: 'K', 6: 'L', 7: 'M', 8: 'N',
|
||||
9: 'O', 10: 'P', 11: 'Q', 12: 'R', 13: 'S', 14: 'T', 15: 'U', 16: 'V',
|
||||
17: 'W', 18: 'X', 19: 'Y', 20: 'g', 40: 'h', 60: 'i', 80: 'j', 100: 'k',
|
||||
120: 'l', 140: 'm', 160: 'n', 180: 'o', 200: 'p', 220: 'q', 240: 'r',
|
||||
260: 's', 280: 't', 300: 'u', 320: 'v', 340: 'w', 360: 'x', 380: 'y',
|
||||
400: 'z'}
|
||||
|
||||
def convert_from_img(self, img_path):
|
||||
image = Image.open(img_path)
|
||||
cuerpo = self.create_body(image)
|
||||
if self.compress_hex:
|
||||
cuerpo = self.encode_hex_ascii(cuerpo)
|
||||
return self.head_doc() + cuerpo + self.foot_doc()
|
||||
|
||||
def create_body(self, image):
|
||||
width, height = image.size
|
||||
orginal_image = np.array(image)
|
||||
index = 0
|
||||
aux_binary_char = ['0', '0', '0', '0', '0', '0', '0', '0']
|
||||
self.width_bytes = width // 8
|
||||
if width % 8 > 0:
|
||||
self.width_bytes = ((width // 8) + 1)
|
||||
else:
|
||||
self.width_bytes = width // 8
|
||||
self.total = self.width_bytes * height
|
||||
sb = []
|
||||
for h in range(height):
|
||||
for w in range(width):
|
||||
pixel = orginal_image[h, w]
|
||||
aux_char = '1' if pixel == 0 else '0' # 0 for black, 1 for white
|
||||
aux_binary_char[index] = aux_char
|
||||
index += 1
|
||||
if index == 8 or w == (width - 1):
|
||||
sb.append(self.four_byte_binary(''.join(aux_binary_char)))
|
||||
aux_binary_char = ['0', '0', '0', '0', '0', '0', '0', '0']
|
||||
index = 0
|
||||
sb.append("\n")
|
||||
return ''.join(sb)
|
||||
|
||||
@staticmethod
|
||||
def four_byte_binary(binary_str):
|
||||
decimal = int(binary_str, 2)
|
||||
if decimal > 15:
|
||||
return hex(decimal)[2:].upper()
|
||||
else:
|
||||
return "0" + hex(decimal)[2:].upper()
|
||||
|
||||
|
||||
def encode_hex_ascii(self, code):
|
||||
maxlinea = self.width_bytes * 2
|
||||
sb_code = []
|
||||
sb_linea = []
|
||||
previous_line = None
|
||||
counter = 1
|
||||
aux = code[0]
|
||||
first_char = False
|
||||
for i in range(1, len(code)):
|
||||
if first_char:
|
||||
aux = code[i]
|
||||
first_char = False
|
||||
continue
|
||||
if code[i] == '\n':
|
||||
if counter >= maxlinea and aux == '0':
|
||||
sb_linea.append(",")
|
||||
elif counter >= maxlinea and aux == 'F':
|
||||
sb_linea.append("!")
|
||||
elif counter > 20:
|
||||
multi20 = (counter // 20) * 20
|
||||
resto20 = (counter % 20)
|
||||
sb_linea.append(self.map_code[multi20])
|
||||
if resto20 != 0:
|
||||
sb_linea.append(self.map_code[resto20] + aux)
|
||||
else:
|
||||
sb_linea.append(aux)
|
||||
else:
|
||||
sb_linea.append(self.map_code[counter] + aux)
|
||||
counter = 1
|
||||
first_char = True
|
||||
if ''.join(sb_linea) == previous_line:
|
||||
sb_code.append(":")
|
||||
else:
|
||||
sb_code.append(''.join(sb_linea))
|
||||
previous_line = ''.join(sb_linea)
|
||||
sb_linea = []
|
||||
continue
|
||||
if aux == code[i]:
|
||||
counter += 1
|
||||
else:
|
||||
if counter > 20:
|
||||
multi20 = (counter // 20) * 20
|
||||
resto20 = (counter % 20)
|
||||
sb_linea.append(self.map_code[multi20])
|
||||
if resto20 != 0:
|
||||
sb_linea.append(self.map_code[resto20] + aux)
|
||||
else:
|
||||
sb_linea.append(aux)
|
||||
else:
|
||||
sb_linea.append(self.map_code[counter] + aux)
|
||||
counter = 1
|
||||
aux = code[i]
|
||||
return ''.join(sb_code)
|
||||
|
||||
def head_doc(self):
|
||||
return "^XA " + "^FO0,0^GFA," + str(self.total) + "," + str(self.total) + "," + str(self.width_bytes) + ", "
|
||||
|
||||
@staticmethod
|
||||
def foot_doc():
|
||||
return "^FS" + "^XZ"
|
||||
|
||||
def set_compress_hex(self, compress_hex):
|
||||
self.compress_hex = compress_hex
|
||||
|
||||
def set_blackness_limit_percentage(self, percentage):
|
||||
self.black_limit = (percentage * 768 // 100)
|
||||
|
||||
def print_to_zpl(img_path):
|
||||
converter = ZPLConveter()
|
||||
converter.set_compress_hex(True)
|
||||
return converter.convert_from_img(img_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
zp = ZPLConveter()
|
||||
zp.set_compress_hex(True)
|
||||
#print(zp.convert_from_img("drawing.png"))
|
1
users.json
Normal file
1
users.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user