mirror of
https://github.com/Ircama/epson_print_conf.git
synced 2025-05-29 06:15:28 -04:00
UI improvements
This commit is contained in:
parent
b0709705d4
commit
eac2b41375
2 changed files with 183 additions and 45 deletions
|
@ -1,4 +1,5 @@
|
||||||
PyYAML
|
PyYAML
|
||||||
git+https://github.com/etingof/pysnmp.git@master#egg=pysnmp
|
git+https://github.com/etingof/pysnmp.git@master#egg=pysnmp
|
||||||
pyasyncore;python_version>="3.12"
|
pyasyncore;python_version>="3.12"
|
||||||
|
tkcalendar
|
||||||
|
|
||||||
|
|
227
ui.py
227
ui.py
|
@ -1,99 +1,164 @@
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
import ipaddress
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, Menu
|
from tkinter import ttk, Menu
|
||||||
from tkinter.scrolledtext import ScrolledText
|
from tkinter.scrolledtext import ScrolledText
|
||||||
import threading
|
|
||||||
import ipaddress
|
|
||||||
import re
|
|
||||||
from find_printers import PrinterScanner
|
|
||||||
from epson_print_conf import EpsonPrinter
|
|
||||||
import tkinter.font as tkfont
|
import tkinter.font as tkfont
|
||||||
|
from tkcalendar import DateEntry # Ensure you have: pip install tkcalendar
|
||||||
|
|
||||||
|
from epson_print_conf import EpsonPrinter
|
||||||
|
from find_printers import PrinterScanner
|
||||||
|
|
||||||
|
|
||||||
|
VERSION = "2.0"
|
||||||
|
|
||||||
class EpsonPrinterUI(tk.Tk):
|
class EpsonPrinterUI(tk.Tk):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.title("Epson Printer Configuration")
|
self.title("Epson Printer Configuration - v" + VERSION)
|
||||||
self.geometry("450x400")
|
self.geometry("450x500")
|
||||||
|
self.minsize(450, 500)
|
||||||
self.printer_scanner=PrinterScanner()
|
self.printer_scanner=PrinterScanner()
|
||||||
|
|
||||||
# configure the main window to be resizable
|
# configure the main window to be resizable
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
FRAME_PAD = 10
|
||||||
|
PAD = 5
|
||||||
|
PADX = 5
|
||||||
|
PADY = 5
|
||||||
|
|
||||||
# main Frame
|
# main Frame
|
||||||
main_frame = ttk.Frame(self, padding="10")
|
main_frame = ttk.Frame(self, padding=FRAME_PAD)
|
||||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
main_frame.columnconfigure(0, weight=1)
|
main_frame.columnconfigure(0, weight=1)
|
||||||
main_frame.rowconfigure(3, weight=1)
|
main_frame.rowconfigure(4, weight=1) # Number of elements
|
||||||
|
|
||||||
# printer model selection
|
# [0] printer model selection
|
||||||
model_frame = ttk.LabelFrame(main_frame, text="Printer Model", padding="10")
|
model_frame = ttk.LabelFrame(main_frame, text="Printer Model", padding=PAD)
|
||||||
model_frame.grid(row=0, column=0, pady=10, sticky=(tk.W, tk.E))
|
model_frame.grid(row=0, column=0, pady=PADY, sticky=(tk.W, tk.E))
|
||||||
model_frame.columnconfigure(1, weight=1)
|
model_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
self.model_var = tk.StringVar()
|
self.model_var = tk.StringVar()
|
||||||
ttk.Label(model_frame, text="Select Printer Model:").grid(row=0, column=0, sticky=tk.W, padx=5)
|
ttk.Label(model_frame, text="Select Printer Model:").grid(row=0, column=0, sticky=tk.W, padx=PADX)
|
||||||
self.model_dropdown = ttk.Combobox(model_frame, textvariable=self.model_var)
|
self.model_dropdown = ttk.Combobox(model_frame, textvariable=self.model_var)
|
||||||
self.model_dropdown['values'] = sorted(EpsonPrinter().valid_printers)
|
self.model_dropdown['values'] = sorted(EpsonPrinter().valid_printers)
|
||||||
self.model_dropdown.grid(row=0, column=1, pady=5, padx=5, sticky=(tk.W, tk.E))
|
self.model_dropdown.grid(row=0, column=1, pady=PADY, padx=PADX, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
# IP address entry
|
# [1] IP address entry
|
||||||
ip_frame = ttk.LabelFrame(main_frame, text="Printer IP Address", padding="10")
|
ip_frame = ttk.LabelFrame(main_frame, text="Printer IP Address", padding=PAD)
|
||||||
ip_frame.grid(row=1, column=0, pady=10, sticky=(tk.W, tk.E))
|
ip_frame.grid(row=1, column=0, pady=PADY, sticky=(tk.W, tk.E))
|
||||||
ip_frame.columnconfigure(1, weight=1)
|
ip_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
self.ip_var = tk.StringVar()
|
self.ip_var = tk.StringVar()
|
||||||
ttk.Label(ip_frame, text="Enter Printer IP Address:").grid(row=0, column=0, sticky=tk.W, padx=5)
|
ttk.Label(ip_frame, text="Enter Printer IP Address:").grid(row=0, column=0, sticky=tk.W, padx=PADX)
|
||||||
self.ip_entry = ttk.Entry(ip_frame, textvariable=self.ip_var)
|
self.ip_entry = ttk.Entry(ip_frame, textvariable=self.ip_var)
|
||||||
self.ip_entry.grid(row=0, column=1, pady=5, padx=5, sticky=(tk.W, tk.E))
|
self.ip_entry.grid(row=0, column=1, pady=PADY, padx=PADX, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
|
# [2] Container frame for the two LabelFrames Power-off timer and TI Received Time
|
||||||
|
container_frame = ttk.Frame(main_frame, padding=PAD)
|
||||||
|
container_frame.grid(row=2, column=0, pady=PADY, sticky=(tk.W, tk.E))
|
||||||
|
container_frame.columnconfigure(0, weight=1) # Allow column to expand
|
||||||
|
container_frame.columnconfigure(1, weight=1) # Allow column to expand
|
||||||
|
|
||||||
|
# Power-off timer
|
||||||
|
po_timer_frame = ttk.LabelFrame(container_frame, text="Power-off timer (minutes)", padding=PAD)
|
||||||
|
po_timer_frame.grid(row=0, column=0, pady=PADY, padx=(0, PADX), sticky=(tk.W, tk.E))
|
||||||
|
po_timer_frame.columnconfigure(0, weight=0) # Button column on the left
|
||||||
|
po_timer_frame.columnconfigure(1, weight=1) # Entry column
|
||||||
|
po_timer_frame.columnconfigure(2, weight=0) # Button column on the right
|
||||||
|
|
||||||
# buttons
|
# Configure validation command for numeric entry
|
||||||
button_frame = ttk.Frame(main_frame, padding="10")
|
validate_cmd = self.register(self.validate_number_input)
|
||||||
button_frame.grid(row=2, column=0, pady=10, sticky=(tk.W, tk.E))
|
|
||||||
|
self.po_timer_var = tk.StringVar()
|
||||||
|
self.po_timer_entry = ttk.Entry(po_timer_frame, textvariable=self.po_timer_var, validate='all', validatecommand=(validate_cmd, "%P"), width=6)
|
||||||
|
self.po_timer_entry.grid(row=0, column=1, pady=PADY, padx=PADX, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
|
get_po_minutes = ttk.Button(po_timer_frame, text="Get", width=6, command=self.get_po_mins)
|
||||||
|
get_po_minutes.grid(row=0, column=0, padx=PADX, pady=PADY, sticky=tk.W)
|
||||||
|
|
||||||
|
set_po_minutes = ttk.Button(po_timer_frame, text="Set", width=6, command=self.set_po_mins)
|
||||||
|
set_po_minutes.grid(row=0, column=2, padx=PADX, pady=PADY, sticky=tk.E)
|
||||||
|
|
||||||
|
# TI Received Time
|
||||||
|
ti_received_frame = ttk.LabelFrame(container_frame, text="TI Received Time (date)", padding=PAD)
|
||||||
|
ti_received_frame.grid(row=0, column=1, pady=PADY, padx=(PADX, 0), sticky=(tk.W, tk.E))
|
||||||
|
ti_received_frame.columnconfigure(0, weight=0) # Button column on the left
|
||||||
|
ti_received_frame.columnconfigure(1, weight=0) # Calendar column
|
||||||
|
ti_received_frame.columnconfigure(2, weight=0) # Button column on the right
|
||||||
|
|
||||||
|
# TI Received Time Calendar Widget
|
||||||
|
self.date_entry = DateEntry(ti_received_frame, date_pattern="yy-mm-dd", width=10, borderwidth=2)
|
||||||
|
self.date_entry.grid(row=0, column=1, padx=PADX, pady=PADY, sticky=(tk.W, tk.E))
|
||||||
|
self.date_entry.delete(0,"end")
|
||||||
|
|
||||||
|
# TI Received Time Buttons
|
||||||
|
get_ti_received = ttk.Button(ti_received_frame, text="Get", width=6, command=self.get_ti_date)
|
||||||
|
get_ti_received.grid(row=0, column=0, padx=PADX, pady=PADY, sticky=tk.W)
|
||||||
|
|
||||||
|
set_ti_received = ttk.Button(ti_received_frame, text="Set", width=6, command=self.set_ti_date)
|
||||||
|
set_ti_received.grid(row=0, column=2, padx=PADX, pady=PADY, sticky=tk.E)
|
||||||
|
|
||||||
|
# [3] Buttons
|
||||||
|
button_frame = ttk.Frame(main_frame, padding=PAD)
|
||||||
|
button_frame.grid(row=3, column=0, pady=PADY, sticky=(tk.W, tk.E))
|
||||||
button_frame.columnconfigure((0, 1, 2), weight=1)
|
button_frame.columnconfigure((0, 1, 2), weight=1)
|
||||||
|
|
||||||
self.detect_button = ttk.Button(button_frame, text="Detect Printers", command=self.start_detect_printers)
|
self.detect_button = ttk.Button(button_frame, text="Detect Printers", command=self.start_detect_printers)
|
||||||
self.detect_button.grid(row=0, column=0, padx=5, pady=5, sticky=(tk.W, tk.E))
|
self.detect_button.grid(row=0, column=0, padx=PADX, pady=PADX, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
self.status_button = ttk.Button(button_frame, text="Print Status", command=self.print_status)
|
self.status_button = ttk.Button(button_frame, text="Print Status", command=self.print_status)
|
||||||
self.status_button.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))
|
self.status_button.grid(row=0, column=1, padx=PADX, pady=PADY, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
self.reset_button = ttk.Button(button_frame, text="Reset Waste Ink Levels", command=self.reset_waste_ink)
|
self.reset_button = ttk.Button(button_frame, text="Reset Waste Ink Levels", command=self.reset_waste_ink)
|
||||||
self.reset_button.grid(row=0, column=2, padx=5, pady=5, sticky=(tk.W, tk.E))
|
self.reset_button.grid(row=0, column=2, padx=PADX, pady=PADX, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
# status display
|
# [4] Status display
|
||||||
status_frame = ttk.LabelFrame(main_frame, text="Status", padding="10")
|
status_frame = ttk.LabelFrame(main_frame, text="Status", padding=PAD)
|
||||||
status_frame.grid(row=3, column=0, pady=10, sticky=(tk.W, tk.E, tk.N, tk.S))
|
status_frame.grid(row=4, column=0, pady=PADY, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
status_frame.columnconfigure(0, weight=1)
|
status_frame.columnconfigure(0, weight=1)
|
||||||
status_frame.rowconfigure(0, weight=1)
|
status_frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
# ScrolledText widget
|
# ScrolledText widget
|
||||||
self.status_text = ScrolledText(status_frame, height=10, width=50, wrap=tk.WORD)
|
self.status_text = ScrolledText(status_frame, wrap=tk.WORD, font=("TkDefaultFont"))
|
||||||
self.status_text.grid(row=0, column=0, pady=5, padx=5, sticky=(tk.W, tk.E, tk.N, tk.S))
|
self.status_text.grid(row=0, column=0, pady=PADY, padx=PADY, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
self.status_text.bind("<Key>", lambda e: "break") # disable editing text
|
||||||
|
# self.status_text.bind("<Button-1>", lambda e: "break") # also disable the mouse
|
||||||
|
|
||||||
# Style configuration
|
# Create a frame to contain the Treeview and its scrollbar
|
||||||
|
self.tree_frame = tk.Frame(status_frame)
|
||||||
|
self.tree_frame.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
self.tree_frame.columnconfigure(0, weight=1)
|
||||||
|
self.tree_frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# Style configuration for the treeview
|
||||||
style = ttk.Style(self)
|
style = ttk.Style(self)
|
||||||
default_font = style.lookup("Treeview.Heading", "font")
|
treeview_font = style.lookup("Treeview.Heading", "font")
|
||||||
|
|
||||||
# If the default_font is a tuple, split into components
|
# For the treeview, if the treeview_font is a tuple, split into components
|
||||||
if isinstance(default_font, tuple):
|
if isinstance(treeview_font, tuple):
|
||||||
default_font_name, default_font_size = default_font[0], default_font[1]
|
treeview_font_name, treeview_font_size = treeview_font[0], treeview_font[1]
|
||||||
else:
|
else:
|
||||||
# If font is not a tuple, it might be a font string or other format.
|
# If font is not a tuple, it might be a font string or other format.
|
||||||
default_font_name, default_font_size = tkfont.Font().actual('family'), tkfont.Font().actual('size')
|
treeview_font_name, treeview_font_size = tkfont.Font().actual('family'), tkfont.Font().actual('size')
|
||||||
|
|
||||||
style.configure("Treeview.Heading",
|
style.configure("Treeview.Heading",
|
||||||
font=(default_font_name, default_font_size - 2, "bold"),
|
font=(treeview_font_name, treeview_font_size - 2, "bold"),
|
||||||
background="lightblue",
|
background="lightblue",
|
||||||
foreground="darkblue")
|
foreground="darkblue")
|
||||||
|
|
||||||
# Create and configure the Treeview widget
|
# Create and configure the Treeview widget
|
||||||
self.tree = ttk.Treeview(status_frame, style="Treeview")
|
self.tree = ttk.Treeview(self.tree_frame, style="Treeview")
|
||||||
self.tree.heading("#0", text="Status Information", anchor='w')
|
self.tree.heading("#0", text="Status Information", anchor='w')
|
||||||
self.tree.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
self.tree.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
# Create a vertical scrollbar for the Treeview
|
# Create a vertical scrollbar for the Treeview
|
||||||
tree_scrollbar = ttk.Scrollbar(status_frame, orient="vertical", command=self.tree.yview)
|
tree_scrollbar = ttk.Scrollbar(self.tree_frame, orient="vertical", command=self.tree.yview)
|
||||||
tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S))
|
tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S))
|
||||||
|
|
||||||
# Configure the Treeview to use the scrollbar
|
# Configure the Treeview to use the scrollbar
|
||||||
|
@ -106,18 +171,90 @@ class EpsonPrinterUI(tk.Tk):
|
||||||
# Bind the right-click event to the Treeview
|
# Bind the right-click event to the Treeview
|
||||||
self.tree.bind("<Button-3>", self.show_context_menu)
|
self.tree.bind("<Button-3>", self.show_context_menu)
|
||||||
|
|
||||||
# Hide the grid view initially
|
# Hide the Treeview initially
|
||||||
self.tree.grid_remove()
|
self.tree_frame.grid_remove()
|
||||||
|
|
||||||
|
def get_po_mins(self):
|
||||||
|
self.show_status_text_view()
|
||||||
|
model = self.model_var.get()
|
||||||
|
ip_address = self.ip_var.get()
|
||||||
|
if not model or not self._is_valid_ip(ip_address):
|
||||||
|
self.status_text.insert(tk.END, "[ERROR] Please select a printer model and enter a valid IP address.\n")
|
||||||
|
return
|
||||||
|
printer = EpsonPrinter(model=model, hostname=ip_address)
|
||||||
|
try:
|
||||||
|
po_timer = printer.stats()['stats']['Power off timer']
|
||||||
|
self.status_text.insert(tk.END, f"[INFO] Power off timer: {po_timer} minutes.\n")
|
||||||
|
self.po_timer_var.set(po_timer)
|
||||||
|
except Exception as e:
|
||||||
|
self.status_text.insert(tk.END, f"[ERROR] {e}: Missing 'Power off timer' in configuration\n")
|
||||||
|
|
||||||
|
def set_po_mins(self):
|
||||||
|
self.show_status_text_view()
|
||||||
|
model = self.model_var.get()
|
||||||
|
ip_address = self.ip_var.get()
|
||||||
|
if not model or not self._is_valid_ip(ip_address):
|
||||||
|
self.status_text.insert(tk.END, "[ERROR] Please select a printer model and enter a valid IP address.\n")
|
||||||
|
return
|
||||||
|
printer = EpsonPrinter(model=model, hostname=ip_address)
|
||||||
|
try:
|
||||||
|
po_timer = printer.stats()['stats']['Power off timer']
|
||||||
|
po_timer = self.po_timer_var.get()
|
||||||
|
if not po_timer.isnumeric():
|
||||||
|
self.status_text.insert(tk.END, "[ERROR] Please Use a valid value for minutes.\n")
|
||||||
|
return
|
||||||
|
self.status_text.insert(tk.END, f"[INFO] Set Power off timer: {po_timer} minutes.\n")
|
||||||
|
printer.write_poweroff_timer(int(po_timer))
|
||||||
|
except Exception as e:
|
||||||
|
self.status_text.insert(tk.END, f"[ERROR] {e}: Cannot set 'Power off timer'; missing configuration\n")
|
||||||
|
|
||||||
|
def get_ti_date(self):
|
||||||
|
self.show_status_text_view()
|
||||||
|
model = self.model_var.get()
|
||||||
|
ip_address = self.ip_var.get()
|
||||||
|
if not model or not self._is_valid_ip(ip_address):
|
||||||
|
self.status_text.insert(tk.END, "[ERROR] Please select a printer model and enter a valid IP address.\n")
|
||||||
|
return
|
||||||
|
printer = EpsonPrinter(model=model, hostname=ip_address)
|
||||||
|
try:
|
||||||
|
date_string = datetime.strptime(printer.stats()['stats']['First TI received time'], '%d %b %Y').strftime('%y-%m-%d')
|
||||||
|
self.status_text.insert(tk.END, f"[INFO] First TI received time (YY-MM-DD): {date_string}.\n")
|
||||||
|
self.date_entry.set_date(date_string)
|
||||||
|
except Exception as e:
|
||||||
|
self.status_text.insert(tk.END, f"[ERROR] {e}: Missing 'First TI received time' in configuration\n")
|
||||||
|
|
||||||
|
def set_ti_date(self):
|
||||||
|
self.show_status_text_view()
|
||||||
|
model = self.model_var.get()
|
||||||
|
ip_address = self.ip_var.get()
|
||||||
|
if not model or not self._is_valid_ip(ip_address):
|
||||||
|
self.status_text.insert(tk.END, "[ERROR] Please select a printer model and enter a valid IP address.\n")
|
||||||
|
return
|
||||||
|
printer = EpsonPrinter(model=model, hostname=ip_address)
|
||||||
|
try:
|
||||||
|
date_string = datetime.strptime(printer.stats()['stats']['First TI received time'], '%d %b %Y').strftime('%y-%m-%d')
|
||||||
|
date_string = self.date_entry.get_date()
|
||||||
|
self.status_text.insert(tk.END, f"[INFO] Set 'First TI received time' (YY-MM-DD) to: {date_string.strftime('%Y-%m-%d')}.\n")
|
||||||
|
#printer.write_first_ti_received_time(date_string.year, date_string.month, date_string.day)
|
||||||
|
except Exception as e:
|
||||||
|
self.status_text.insert(tk.END, f"[ERROR] {e}: Cannot set 'First TI received time'; missing configuration\n")
|
||||||
|
|
||||||
|
def validate_number_input(self, new_value):
|
||||||
|
# This function will be called with the new input value
|
||||||
|
if new_value == "" or new_value.isdigit():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def show_status_text_view(self):
|
def show_status_text_view(self):
|
||||||
"""Show the status frame and hide the Treeview."""
|
"""Show the status frame and hide the Treeview."""
|
||||||
self.tree.grid_remove()
|
self.tree_frame.grid_remove()
|
||||||
self.status_text.grid()
|
self.status_text.grid()
|
||||||
|
|
||||||
def show_treeview(self):
|
def show_treeview(self):
|
||||||
"""Show the Treeview and hide the status frame."""
|
"""Show the Treeview and hide the status frame."""
|
||||||
self.status_text.grid_remove()
|
self.status_text.grid_remove()
|
||||||
self.tree.grid()
|
self.tree_frame.grid()
|
||||||
|
|
||||||
def print_status(self):
|
def print_status(self):
|
||||||
self.show_status_text_view()
|
self.show_status_text_view()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue