generated from brennen/python_project
implement send_to_printer
add dithering to image preview
This commit is contained in:
@@ -12,6 +12,7 @@ from pathlib import Path
|
|||||||
from platform import system
|
from platform import system
|
||||||
|
|
||||||
from PIL import ImageOps
|
from PIL import ImageOps
|
||||||
|
from PIL.ImagePalette import ImagePalette
|
||||||
from PIL.ImageTk import Image, PhotoImage
|
from PIL.ImageTk import Image, PhotoImage
|
||||||
from serial import Serial
|
from serial import Serial
|
||||||
|
|
||||||
@@ -20,7 +21,18 @@ IWII_RESOLUTION = Resolution(576, 720)
|
|||||||
|
|
||||||
SUPPORTED_IMAGE_TYPES = sorted("*"+ext for ext, type_ in Image.registered_extensions().items() if type_ in ("PNG", "GIF", "JPEG", "WEBP", "BMP"))
|
SUPPORTED_IMAGE_TYPES = sorted("*"+ext for ext, type_ in Image.registered_extensions().items() if type_ in ("PNG", "GIF", "JPEG", "WEBP", "BMP"))
|
||||||
|
|
||||||
def widget_action(func: Callable):
|
IWII_PALETTE = ImagePalette(
|
||||||
|
"P",
|
||||||
|
[
|
||||||
|
255, 255, 255, #white
|
||||||
|
0, 255, 255, #cyan
|
||||||
|
255, 0, 255, #magenta
|
||||||
|
255, 255, 0, #yellow
|
||||||
|
0, 0, 0, #black
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def widget_action(func: Callable[[Event], None]) -> Callable[[Event], None]:
|
||||||
# decorator to wrap widget actions in an error handler that displays exceptions in error message windows
|
# decorator to wrap widget actions in an error handler that displays exceptions in error message windows
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(event: Event):
|
def wrapper(event: Event):
|
||||||
@@ -43,13 +55,14 @@ def widget_action(func: Callable):
|
|||||||
parent=event.widget.master
|
parent=event.widget.master
|
||||||
)
|
)
|
||||||
|
|
||||||
event.widget.master.quit()
|
quit_photobooth(event)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@widget_action
|
@widget_action
|
||||||
def open_preview(event: Event):
|
def open_preview(event: Event) -> None:
|
||||||
file = filedialog.askopenfilename(
|
file = filedialog.askopenfilename(
|
||||||
parent=event.widget.master,
|
parent=event.widget.master,
|
||||||
title="Open Image",
|
title="Open Image",
|
||||||
@@ -64,17 +77,53 @@ def open_preview(event: Event):
|
|||||||
elif Path(file).exists() and Path(file).is_file():
|
elif Path(file).exists() and Path(file).is_file():
|
||||||
# load image data and resize to fit the IWII resolution preserving aspect ratio
|
# load image data and resize to fit the IWII resolution preserving aspect ratio
|
||||||
image_data = ImageOps.contain(Image.open(file), IWII_RESOLUTION)
|
image_data = ImageOps.contain(Image.open(file), IWII_RESOLUTION)
|
||||||
photo = PhotoImage(image_data, size=IWII_RESOLUTION)
|
|
||||||
event.widget.create_image(0,0, anchor=NW, image=photo)
|
p_img = Image.new('P', image_data.size)
|
||||||
|
p_img.putpalette(IWII_PALETTE)
|
||||||
|
|
||||||
|
conv = image_data.quantize(palette=p_img).convert("RGB")
|
||||||
|
|
||||||
|
photo = PhotoImage(conv, size=IWII_RESOLUTION)
|
||||||
|
event.widget.create_image(0,0, anchor=CENTER, image=photo)
|
||||||
# tkinter does not maintain a reference to the local variables of this function upon return
|
# tkinter does not maintain a reference to the local variables of this function upon return
|
||||||
# without references, the photo and its data get garbage collected and nothing is display
|
# without references, the photo and its data get garbage collected and nothing is display
|
||||||
# therefore we must create references to them to keep them around
|
# therefore we must create references to them to keep them around
|
||||||
event.widget._displayed_photo=photo
|
event.widget._displayed_photo=photo
|
||||||
event.widget.master.photobooth_image = image_data
|
event.widget.master.photobooth_image = conv
|
||||||
|
|
||||||
@widget_action
|
@widget_action
|
||||||
def send_to_iwii(event: Event):
|
def send_to_printer(event: Event) -> None:
|
||||||
raise NotImplementedError
|
if not event.widget.master.photobooth_image:
|
||||||
|
messagebox.showwarning(
|
||||||
|
title="Warning",
|
||||||
|
message="Nothing to print. Please take a picture first.",
|
||||||
|
parent=event.widget.master
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
with Serial(
|
||||||
|
"/dev/ttyUSB0",
|
||||||
|
baudrate=9600,
|
||||||
|
xonxoff=True,
|
||||||
|
rtscts=False
|
||||||
|
) as printer:
|
||||||
|
printer.write(event.widget.master.photobooth_image.tobytes())
|
||||||
|
|
||||||
|
|
||||||
|
@widget_action
|
||||||
|
def clear_preview(event: Event) -> None:
|
||||||
|
if event.widget.master.photobooth_image:
|
||||||
|
# remove the image data
|
||||||
|
event.widget.master.photobooth_image = None
|
||||||
|
# clear the canvas
|
||||||
|
event.widget.delete("all")
|
||||||
|
# remove the reference to the data formerly displayed on the canvas so it gets GC'd
|
||||||
|
del event.widget._displayed_photo
|
||||||
|
|
||||||
|
|
||||||
|
@widget_action
|
||||||
|
def quit_photobooth(event: Event) -> None:
|
||||||
|
event.widget.master.destroy()
|
||||||
|
event.widget.master.quit()
|
||||||
|
|
||||||
|
|
||||||
def photobooth():
|
def photobooth():
|
||||||
@@ -107,7 +156,9 @@ def photobooth():
|
|||||||
|
|
||||||
preview = Canvas(master=app, height=IWII_RESOLUTION.height, width=IWII_RESOLUTION.width)
|
preview = Canvas(master=app, height=IWII_RESOLUTION.height, width=IWII_RESOLUTION.width)
|
||||||
preview.bind("<space>", open_preview)
|
preview.bind("<space>", open_preview)
|
||||||
preview.bind("<Return>", send_to_iwii)
|
preview.bind("<Return>", send_to_printer)
|
||||||
|
preview.bind("c", clear_preview)
|
||||||
|
preview.bind("q", quit_photobooth)
|
||||||
preview.focus_set()
|
preview.focus_set()
|
||||||
preview.pack(side="top")
|
preview.pack(side="top")
|
||||||
app.mainloop()
|
app.mainloop()
|
||||||
|
|||||||
Reference in New Issue
Block a user