mirror of
https://github.com/Ircama/epson_print_conf.git
synced 2025-05-09 13:42:03 -04:00
v2.2 - bug fixing and UI improvements
This commit is contained in:
parent
f2e4955725
commit
249ee446ab
3 changed files with 344 additions and 96 deletions
18
README.md
18
README.md
|
@ -99,10 +99,14 @@ This GUI runs on any Operating Systems supported by Python (not just Windows), b
|
|||
GUI usage:
|
||||
|
||||
```
|
||||
python ui.py [-h] [-P PICKLE_FILE] [-O]
|
||||
python ui.py [-h] [-m MODEL] [-a HOSTNAME] [-P PICKLE_FILE] [-O]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-m MODEL, --model MODEL
|
||||
Printer model. Example: -m XP-205
|
||||
-a HOSTNAME, --address HOSTNAME
|
||||
Printer host name or IP address. (Example: -a 192.168.1.87)
|
||||
-P PICKLE_FILE, --pickle PICKLE_FILE
|
||||
Load a pickle configuration archive saved by parse_devices.py
|
||||
-O, --override Replace the default configuration with the one in the pickle file instead of merging (default
|
||||
|
@ -484,16 +488,22 @@ printer.reset_waste_ink_levels()
|
|||
printer.brute_force_read_key()
|
||||
printer.write_first_ti_received_time(2000, 1, 2)
|
||||
|
||||
# Dump all printer parameters
|
||||
# Dump all printer configuration parameters
|
||||
from pprint import pprint
|
||||
pprint(printer.parm)
|
||||
```
|
||||
|
||||
# "black" way to dump all printer parameters
|
||||
"black" way to dump all printer parameters:
|
||||
|
||||
```python
|
||||
import textwrap, black
|
||||
from epson_print_conf import EpsonPrinter
|
||||
printer = EpsonPrinter(model="TX730WD", hostname="192.168.178.29")
|
||||
mode = black.Mode(line_length=200, magic_trailing_comma=False)
|
||||
print(textwrap.indent(black.format_str(f"{printer.model}: " + repr(printer.parm), mode=mode), 8*' '))
|
||||
print(textwrap.indent(black.format_str(f'"{printer.model}": ' + repr(printer.parm), mode=mode), 8*' '))
|
||||
|
||||
# Print status:
|
||||
print(black.format_str(f'"{printer.model}": ' + repr(printer.stats()), mode=mode))
|
||||
```
|
||||
|
||||
## Output example
|
||||
|
|
|
@ -955,6 +955,26 @@ class EpsonPrinter:
|
|||
"in '%s' configuration.",
|
||||
sameas, printer_name
|
||||
)
|
||||
# process itertools classes
|
||||
def expand_itertools_in_dict(d):
|
||||
for key, value in d.items():
|
||||
if isinstance(value, dict): # If the value is another dictionary, recurse into it
|
||||
expand_itertools_in_dict(value)
|
||||
elif isinstance(
|
||||
value, (
|
||||
itertools.chain, itertools.cycle, itertools.islice,
|
||||
itertools.permutations, itertools.combinations,
|
||||
itertools.product, itertools.zip_longest,
|
||||
itertools.starmap, itertools.groupby
|
||||
)
|
||||
):
|
||||
d[key] = list(value) # Convert itertools object to a list
|
||||
elif isinstance(value, list): # Check inside lists for dictionaries
|
||||
for i, item in enumerate(value):
|
||||
if isinstance(item, dict):
|
||||
expand_itertools_in_dict(item)
|
||||
for printer_name, printer_data in conf.copy().items():
|
||||
expand_itertools_in_dict(printer_data)
|
||||
|
||||
def stats(self):
|
||||
"""Return all available information about a printer."""
|
||||
|
@ -1954,7 +1974,7 @@ class EpsonPrinter:
|
|||
)
|
||||
return d
|
||||
|
||||
def reset_waste_ink_levels(self) -> bool:
|
||||
def reset_waste_ink_levels(self, dry_run=False) -> bool:
|
||||
"""
|
||||
Set waste ink levels to 0.
|
||||
"""
|
||||
|
@ -1962,12 +1982,16 @@ class EpsonPrinter:
|
|||
logging.error("EpsonPrinter - invalid API usage")
|
||||
return None
|
||||
if "raw_waste_reset" in self.parm:
|
||||
if dry_run:
|
||||
return True
|
||||
for oid, value in self.parm["raw_waste_reset"].items():
|
||||
if not self.write_eeprom(oid, value, label="raw_waste_reset"):
|
||||
return False
|
||||
return True
|
||||
if "main_waste" not in self.parm:
|
||||
return None
|
||||
if dry_run:
|
||||
return True
|
||||
for oid in self.parm["main_waste"]["oids"]:
|
||||
if not self.write_eeprom(oid, 0, label="main_waste"):
|
||||
return False
|
||||
|
|
396
ui.py
396
ui.py
|
@ -1,10 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Epson Printer Configuration via SNMP (TCP/IP) - GUI
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import threading
|
||||
import ipaddress
|
||||
import inspect
|
||||
from datetime import datetime
|
||||
import socket
|
||||
|
||||
import black
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, Menu
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
|
@ -17,10 +26,11 @@ from epson_print_conf import EpsonPrinter
|
|||
from find_printers import PrinterScanner
|
||||
|
||||
|
||||
VERSION = "2.1"
|
||||
VERSION = "2.2"
|
||||
|
||||
NO_CONF_ERROR = (
|
||||
"[ERROR] Please select a printer model and a valid IP address, or press 'Detect Printers'.\n"
|
||||
"[ERROR] Please select a printer model and a valid IP address,"
|
||||
" or press 'Detect Printers'.\n"
|
||||
)
|
||||
|
||||
|
||||
|
@ -60,15 +70,34 @@ def get_printer_models(input_string):
|
|||
|
||||
|
||||
class ToolTip:
|
||||
def __init__(self, widget, text="widget info", wrap_length=10):
|
||||
def __init__(
|
||||
self,
|
||||
widget,
|
||||
text="widget info",
|
||||
wrap_length=10,
|
||||
destroy=True
|
||||
):
|
||||
self.widget = widget
|
||||
self.text = text
|
||||
self.wrap_length = wrap_length
|
||||
self.tooltip_window = None
|
||||
|
||||
# Check and remove existing bindings if they exist
|
||||
if destroy:
|
||||
self.remove_existing_binding("<Enter>")
|
||||
self.remove_existing_binding("<Leave>")
|
||||
self.remove_existing_binding("<Button-1>")
|
||||
|
||||
# Set new bindings
|
||||
widget.bind("<Enter>", self.enter, "+") # Show the tooltip on hover
|
||||
widget.bind("<Leave>", self.leave, "+") # Hide the tooltip on leave
|
||||
widget.bind("<Button-1>", self.leave, "+") # Hide tooltip on mouse click
|
||||
|
||||
def remove_existing_binding(self, event):
|
||||
# Check if there's already a binding for the event
|
||||
if self.widget.bind(event):
|
||||
self.widget.unbind(event) # Remove the existing binding
|
||||
|
||||
def enter(self, event=None):
|
||||
if self.tooltip_window or not self.text:
|
||||
return
|
||||
|
@ -128,18 +157,26 @@ class ToolTip:
|
|||
class BugFixedDateEntry(DateEntry):
|
||||
"""
|
||||
Fixes a bug on the calendar that does not accept mouse selection with Linux
|
||||
Fixes a drop down bug when the DateEntry widget is not focused
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def drop_down(self):
|
||||
self.focus_set() # Set focus to the DateEntry widget
|
||||
super().drop_down()
|
||||
if self._top_cal is not None and not self._calendar.winfo_ismapped():
|
||||
self._top_cal.lift()
|
||||
|
||||
|
||||
class EpsonPrinterUI(tk.Tk):
|
||||
def __init__(self, conf_dict={}, replace_conf=False):
|
||||
def __init__(
|
||||
self,
|
||||
model: str = None,
|
||||
hostname: str = None,
|
||||
conf_dict={},
|
||||
replace_conf=False
|
||||
):
|
||||
super().__init__()
|
||||
self.title("Epson Printer Configuration - v" + VERSION)
|
||||
self.geometry("500x500")
|
||||
|
@ -149,6 +186,9 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.ip_list_cycle = None
|
||||
self.conf_dict = conf_dict
|
||||
self.replace_conf = replace_conf
|
||||
self.text_dump = ""
|
||||
self.mode = black.Mode(line_length=200, magic_trailing_comma=False)
|
||||
self.printer = None
|
||||
|
||||
# configure the main window to be resizable
|
||||
self.columnconfigure(0, weight=1)
|
||||
|
@ -172,7 +212,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
model_ip_frame.columnconfigure(0, weight=1) # Allow column to expand
|
||||
model_ip_frame.columnconfigure(1, weight=1) # Allow column to expand
|
||||
|
||||
# printer model selection
|
||||
# BOX printer model selection
|
||||
model_frame = ttk.LabelFrame(
|
||||
model_ip_frame, text="Printer Model", padding=PAD
|
||||
)
|
||||
|
@ -182,12 +222,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
model_frame.columnconfigure(0, weight=0)
|
||||
model_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# Model combobox
|
||||
self.model_var = tk.StringVar()
|
||||
if (
|
||||
"internal_data" in conf_dict
|
||||
and "default_model" in conf_dict["internal_data"]
|
||||
):
|
||||
self.model_var.set(conf_dict["internal_data"]["default_model"])
|
||||
if model:
|
||||
self.model_var.set(model)
|
||||
ttk.Label(model_frame, text="Model:").grid(
|
||||
row=0, column=0, sticky=tk.W, padx=PADX
|
||||
)
|
||||
|
@ -203,11 +246,12 @@ class EpsonPrinterUI(tk.Tk):
|
|||
)
|
||||
ToolTip(
|
||||
self.model_dropdown,
|
||||
"Select the model of the printer, or press 'Detect Printers'.\nPress F2 to dump the parameters associated to the printer model.",
|
||||
"Select the model of the printer, or press 'Detect Printers'.\n"
|
||||
"Press F2 to dump the parameters associated to the printer model.",
|
||||
)
|
||||
self.model_dropdown.bind("<F2>", self.printer_config)
|
||||
|
||||
# IP address entry
|
||||
# BOX IP address
|
||||
ip_frame = ttk.LabelFrame(
|
||||
model_ip_frame, text="Printer IP Address", padding=PAD
|
||||
)
|
||||
|
@ -217,12 +261,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
ip_frame.columnconfigure(0, weight=0)
|
||||
ip_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# IP address entry
|
||||
self.ip_var = tk.StringVar()
|
||||
if (
|
||||
"internal_data" in conf_dict
|
||||
and "hostname" in conf_dict["internal_data"]
|
||||
):
|
||||
self.ip_var.set(conf_dict["internal_data"]["hostname"])
|
||||
if hostname:
|
||||
self.ip_var.set(hostname)
|
||||
ttk.Label(ip_frame, text="IP Address:").grid(
|
||||
row=0, column=0, sticky=tk.W, padx=PADX
|
||||
)
|
||||
|
@ -233,7 +280,12 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.ip_entry.bind("<F2>", self.next_ip)
|
||||
ToolTip(
|
||||
self.ip_entry,
|
||||
"Enter the IP address, or press 'Detect Printers' (you can also enter part of the IP address to speed up the detection), or press F2 more times to get the next local IP address, which can then be edited (by removing the last part before pressing 'Detect Printers').",
|
||||
"Enter the IP address, or press 'Detect Printers'"
|
||||
" (you can also enter part of the IP address"
|
||||
" to speed up the detection),"
|
||||
" or press F2 more times to get the next local IP address,"
|
||||
" which can then be edited"
|
||||
" (by removing the last part before pressing 'Detect Printers').",
|
||||
)
|
||||
|
||||
# [row 1] Container frame for the two LabelFrames Power-off timer and TI Received Time
|
||||
|
@ -245,7 +297,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
container_frame.columnconfigure(0, weight=1) # Allow column to expand
|
||||
container_frame.columnconfigure(1, weight=1) # Allow column to expand
|
||||
|
||||
# Power-off timer
|
||||
# BOX Power-off timer (minutes)
|
||||
po_timer_frame = ttk.LabelFrame(
|
||||
container_frame, text="Power-off timer (minutes)", padding=PAD
|
||||
)
|
||||
|
@ -259,6 +311,19 @@ class EpsonPrinterUI(tk.Tk):
|
|||
# Configure validation command for numeric entry
|
||||
validate_cmd = self.register(self.validate_number_input)
|
||||
|
||||
# Power-off timer (minutes) - Get Button
|
||||
button_width = 7
|
||||
self.get_po_minutes = ttk.Button(
|
||||
po_timer_frame,
|
||||
text="Get",
|
||||
width=button_width,
|
||||
command=self.get_po_mins,
|
||||
)
|
||||
self.get_po_minutes.grid(
|
||||
row=0, column=0, padx=PADX, pady=PADY, sticky=tk.W
|
||||
)
|
||||
|
||||
# Power-off timer (minutes) - minutes Entry
|
||||
self.po_timer_var = tk.StringVar()
|
||||
self.po_timer_entry = ttk.Entry(
|
||||
po_timer_frame,
|
||||
|
@ -271,26 +336,24 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.po_timer_entry.grid(
|
||||
row=0, column=1, pady=PADY, padx=PADX, sticky=(tk.W, tk.E)
|
||||
)
|
||||
ToolTip(self.po_timer_entry, "Enter a number of minutes.")
|
||||
|
||||
button_width = 7
|
||||
get_po_minutes = ttk.Button(
|
||||
po_timer_frame,
|
||||
text="Get",
|
||||
width=button_width,
|
||||
command=self.get_po_mins,
|
||||
ToolTip(
|
||||
self.po_timer_entry,
|
||||
"Enter a number of minutes.",
|
||||
destroy=False
|
||||
)
|
||||
get_po_minutes.grid(row=0, column=0, padx=PADX, pady=PADY, sticky=tk.W)
|
||||
|
||||
set_po_minutes = ttk.Button(
|
||||
# Power-off timer (minutes) - Set Button
|
||||
self.set_po_minutes = ttk.Button(
|
||||
po_timer_frame,
|
||||
text="Set",
|
||||
width=button_width,
|
||||
command=self.set_po_mins,
|
||||
)
|
||||
set_po_minutes.grid(row=0, column=2, padx=PADX, pady=PADY, sticky=tk.E)
|
||||
self.set_po_minutes.grid(
|
||||
row=0, column=2, padx=PADX, pady=PADY, sticky=tk.E
|
||||
)
|
||||
|
||||
# TI Received Time
|
||||
# BOX TI Received Time (date)
|
||||
ti_received_frame = ttk.LabelFrame(
|
||||
container_frame, text="TI Received Time (date)", padding=PAD
|
||||
)
|
||||
|
@ -301,7 +364,18 @@ class EpsonPrinterUI(tk.Tk):
|
|||
ti_received_frame.columnconfigure(1, weight=1) # Calendar column
|
||||
ti_received_frame.columnconfigure(2, weight=0) # Button column on the right
|
||||
|
||||
# TI Received Time Calendar Widget
|
||||
# TI Received Time - Get Button
|
||||
self.get_ti_received = ttk.Button(
|
||||
ti_received_frame,
|
||||
text="Get",
|
||||
width=button_width,
|
||||
command=self.get_ti_date,
|
||||
)
|
||||
self.get_ti_received.grid(
|
||||
row=0, column=0, padx=PADX, pady=PADY, sticky=tk.W
|
||||
)
|
||||
|
||||
# TI Received Time - Calendar Widget
|
||||
self.date_entry = BugFixedDateEntry(
|
||||
ti_received_frame, date_pattern="yyyy-mm-dd"
|
||||
)
|
||||
|
@ -309,24 +383,22 @@ class EpsonPrinterUI(tk.Tk):
|
|||
row=0, column=1, padx=PADX, pady=PADY, sticky=(tk.W, tk.E)
|
||||
)
|
||||
self.date_entry.delete(0, "end") # blank the field removing the current date
|
||||
ToolTip(self.date_entry, "Enter a valid date with format YYYY-MM-DD.")
|
||||
|
||||
# TI Received Time Buttons
|
||||
get_ti_received = ttk.Button(
|
||||
ti_received_frame,
|
||||
text="Get",
|
||||
width=button_width,
|
||||
command=self.get_ti_date,
|
||||
ToolTip(
|
||||
self.date_entry,
|
||||
"Enter a valid date with format YYYY-MM-DD.",
|
||||
destroy=False
|
||||
)
|
||||
get_ti_received.grid(row=0, column=0, padx=PADX, pady=PADY, sticky=tk.W)
|
||||
|
||||
set_ti_received = ttk.Button(
|
||||
# TI Received Time - Set Button
|
||||
self.set_ti_received = ttk.Button(
|
||||
ti_received_frame,
|
||||
text="Set",
|
||||
width=button_width,
|
||||
command=self.set_ti_date,
|
||||
)
|
||||
set_ti_received.grid(row=0, column=2, padx=PADX, pady=PADY, sticky=tk.E)
|
||||
self.set_ti_received.grid(
|
||||
row=0, column=2, padx=PADX, pady=PADY, sticky=tk.E
|
||||
)
|
||||
|
||||
# [row 2] Buttons
|
||||
row_n += 1
|
||||
|
@ -334,6 +406,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
button_frame.grid(row=row_n, column=0, pady=PADY, sticky=(tk.W, tk.E))
|
||||
button_frame.columnconfigure((0, 1, 2), weight=1)
|
||||
|
||||
# Detect Printers
|
||||
self.detect_button = ttk.Button(
|
||||
button_frame,
|
||||
text="Detect Printers",
|
||||
|
@ -343,6 +416,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
row=0, column=0, padx=PADX, pady=PADX, sticky=(tk.W, tk.E)
|
||||
)
|
||||
|
||||
# Printer Status
|
||||
self.status_button = ttk.Button(
|
||||
button_frame, text="Printer Status", command=self.printer_status
|
||||
)
|
||||
|
@ -350,6 +424,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
row=0, column=1, padx=PADX, pady=PADY, sticky=(tk.W, tk.E)
|
||||
)
|
||||
|
||||
# Reset Waste Ink Levels
|
||||
self.reset_button = ttk.Button(
|
||||
button_frame,
|
||||
text="Reset Waste Ink Levels",
|
||||
|
@ -359,7 +434,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
row=0, column=2, padx=PADX, pady=PADX, sticky=(tk.W, tk.E)
|
||||
)
|
||||
|
||||
# [row 3] Status display
|
||||
# [row 3] Status display (including ScrolledText and Treeview)
|
||||
row_n += 1
|
||||
status_frame = ttk.LabelFrame(main_frame, text="Status", padding=PAD)
|
||||
status_frame.grid(
|
||||
|
@ -379,6 +454,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
padx=PADY,
|
||||
sticky=(tk.W, tk.E, tk.N, tk.S),
|
||||
)
|
||||
self.status_text.bind("<Tab>", self.focus_next)
|
||||
self.status_text.bind("<Key>", lambda e: "break") # disable editing text
|
||||
self.status_text.bind(
|
||||
"<Control-c>",
|
||||
|
@ -430,7 +506,13 @@ class EpsonPrinterUI(tk.Tk):
|
|||
# Create a context menu
|
||||
self.context_menu = Menu(self, tearoff=0)
|
||||
self.context_menu.add_command(
|
||||
label="Copy", command=self.copy_selected_item
|
||||
label="Copy this item", command=self.copy_selected_item
|
||||
)
|
||||
self.context_menu.add_command(
|
||||
label="Copy all items", command=self.copy_all_items
|
||||
)
|
||||
self.context_menu.add_command(
|
||||
label="Print all items", command=self.print_items
|
||||
)
|
||||
|
||||
# Bind the right-click event to the Treeview
|
||||
|
@ -439,6 +521,82 @@ class EpsonPrinterUI(tk.Tk):
|
|||
# Hide the Treeview initially
|
||||
self.tree_frame.grid_remove()
|
||||
|
||||
self.model_var.trace('w', self.change_widget_states)
|
||||
self.ip_var.trace('w', self.change_widget_states)
|
||||
self.change_widget_states()
|
||||
|
||||
def focus_next(self, event):
|
||||
event.widget.tk_focusNext().focus()
|
||||
return("break")
|
||||
|
||||
def change_widget_states(self, index=None, value=None, op=None):
|
||||
if self.ip_var.get():
|
||||
self.status_button.state(["!disabled"])
|
||||
self.printer = None
|
||||
else:
|
||||
self.status_button.state(["disabled"])
|
||||
if self.ip_var.get() and self.model_var.get():
|
||||
self.printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=self.model_var.get(),
|
||||
hostname=self.ip_var.get()
|
||||
)
|
||||
if not self.printer:
|
||||
return
|
||||
if not self.printer.parm:
|
||||
self.reset_printer_model()
|
||||
return
|
||||
if self.printer.parm.get("stats", {}).get("Power off timer"):
|
||||
self.po_timer_entry.state(["!disabled"])
|
||||
self.get_po_minutes.state(["!disabled"])
|
||||
self.set_po_minutes.state(["!disabled"])
|
||||
ToolTip(self.get_po_minutes, "")
|
||||
else:
|
||||
self.po_timer_entry.state(["disabled"])
|
||||
self.get_po_minutes.state(["disabled"])
|
||||
self.set_po_minutes.state(["disabled"])
|
||||
ToolTip(
|
||||
self.get_po_minutes,
|
||||
"Feature not defined in the printer configuration."
|
||||
)
|
||||
if self.printer.parm.get("stats", {}).get("First TI received time"):
|
||||
self.date_entry.state(["!disabled"])
|
||||
self.get_ti_received.state(["!disabled"])
|
||||
self.set_ti_received.state(["!disabled"])
|
||||
ToolTip(self.get_ti_received, "")
|
||||
else:
|
||||
self.date_entry.state(["disabled"])
|
||||
self.get_ti_received.state(["disabled"])
|
||||
self.set_ti_received.state(["disabled"])
|
||||
ToolTip(
|
||||
self.get_ti_received,
|
||||
"Feature not defined in the printer configuration."
|
||||
)
|
||||
if self.printer.reset_waste_ink_levels(dry_run=True):
|
||||
self.reset_button.state(["!disabled"])
|
||||
ToolTip(
|
||||
self.reset_button,
|
||||
"Ensure you really want this before pressing this key."
|
||||
)
|
||||
else:
|
||||
self.reset_button.state(["disabled"])
|
||||
ToolTip(
|
||||
self.reset_button,
|
||||
"Feature not defined in the printer configuration."
|
||||
)
|
||||
else:
|
||||
self.po_timer_entry.state(["disabled"])
|
||||
self.get_po_minutes.state(["disabled"])
|
||||
self.set_po_minutes.state(["disabled"])
|
||||
|
||||
self.date_entry.state(["disabled"])
|
||||
self.get_ti_received.state(["disabled"])
|
||||
self.set_ti_received.state(["disabled"])
|
||||
|
||||
self.reset_button.state(["disabled"])
|
||||
self.update_idletasks()
|
||||
|
||||
def next_ip(self, event):
|
||||
ip = self.ip_var.get()
|
||||
if self.ip_list_cycle == None:
|
||||
|
@ -468,20 +626,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.after(100, lambda: method_to_call(cursor=False))
|
||||
return
|
||||
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):
|
||||
if not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, NO_CONF_ERROR)
|
||||
self.config(cursor="")
|
||||
self.update()
|
||||
return
|
||||
printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=model,
|
||||
hostname=ip_address
|
||||
)
|
||||
if not printer.parm.get("stats", {}).get("Power off timer"):
|
||||
if not self.printer:
|
||||
return
|
||||
if not self.printer.parm.get("stats", {}).get("Power off timer"):
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[ERROR]: Missing 'Power off timer' in configuration\n",
|
||||
|
@ -490,7 +643,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.update_idletasks()
|
||||
return
|
||||
try:
|
||||
po_timer = printer.stats()["stats"]["Power off timer"]
|
||||
po_timer = self.printer.stats()["stats"]["Power off timer"]
|
||||
self.status_text.insert(
|
||||
tk.END, f"[INFO] Power off timer: {po_timer} minutes.\n"
|
||||
)
|
||||
|
@ -510,20 +663,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.after(100, lambda: method_to_call(cursor=False))
|
||||
return
|
||||
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):
|
||||
if not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, NO_CONF_ERROR)
|
||||
self.config(cursor="")
|
||||
self.update_idletasks()
|
||||
return
|
||||
printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=model,
|
||||
hostname=ip_address
|
||||
)
|
||||
if not printer.parm.get("stats", {}).get("Power off timer"):
|
||||
if not self.printer:
|
||||
return
|
||||
if not self.printer.parm.get("stats", {}).get("Power off timer"):
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[ERROR]: Missing 'Power off timer' in configuration\n",
|
||||
|
@ -547,7 +695,7 @@ class EpsonPrinterUI(tk.Tk):
|
|||
)
|
||||
if response:
|
||||
try:
|
||||
printer.write_poweroff_timer(int(po_timer))
|
||||
self.printer.write_poweroff_timer(int(po_timer))
|
||||
except Exception as e:
|
||||
self.status_text.insert(tk.END, f"[ERROR] {e}\n")
|
||||
else:
|
||||
|
@ -566,20 +714,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.after(100, lambda: method_to_call(cursor=False))
|
||||
return
|
||||
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):
|
||||
if not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, NO_CONF_ERROR)
|
||||
self.config(cursor="")
|
||||
self.update_idletasks()
|
||||
return
|
||||
printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=model,
|
||||
hostname=ip_address
|
||||
)
|
||||
if not printer.parm.get("stats", {}).get("First TI received time"):
|
||||
if not self.printer:
|
||||
return
|
||||
if not self.printer.parm.get("stats", {}).get("First TI received time"):
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[ERROR]: Missing 'First TI received time' in configuration\n",
|
||||
|
@ -589,7 +732,8 @@ class EpsonPrinterUI(tk.Tk):
|
|||
return
|
||||
try:
|
||||
date_string = datetime.strptime(
|
||||
printer.stats()["stats"]["First TI received time"], "%d %b %Y"
|
||||
self.printer.stats(
|
||||
)["stats"]["First TI received time"], "%d %b %Y"
|
||||
).strftime("%Y-%m-%d")
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
|
@ -611,20 +755,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.after(100, lambda: method_to_call(cursor=False))
|
||||
return
|
||||
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):
|
||||
if not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, NO_CONF_ERROR)
|
||||
self.config(cursor="")
|
||||
self.update_idletasks()
|
||||
return
|
||||
printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=model,
|
||||
hostname=ip_address
|
||||
)
|
||||
if not printer.parm.get("stats", {}).get("First TI received time"):
|
||||
if not self.printer:
|
||||
return
|
||||
if not self.printer.parm.get("stats", {}).get("First TI received time"):
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[ERROR]: Missing 'First TI received time' in configuration\n",
|
||||
|
@ -635,14 +774,15 @@ class EpsonPrinterUI(tk.Tk):
|
|||
date_string = self.date_entry.get_date()
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[INFO] Set 'First TI received time' (YYYY-MM-DD) to: {date_string.strftime('%Y-%m-%d')}.\n",
|
||||
f"[INFO] Set 'First TI received time' (YYYY-MM-DD) to: "
|
||||
f"{date_string.strftime('%Y-%m-%d')}.\n",
|
||||
)
|
||||
response = messagebox.askyesno(
|
||||
"Confirm Action", "Are you sure you want to proceed?"
|
||||
)
|
||||
if response:
|
||||
try:
|
||||
printer.write_first_ti_received_time(
|
||||
self.printer.write_first_ti_received_time(
|
||||
date_string.year, date_string.month, date_string.day
|
||||
)
|
||||
except Exception as e:
|
||||
|
@ -686,7 +826,8 @@ class EpsonPrinterUI(tk.Tk):
|
|||
if not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
"[ERROR] Please enter a valid IP address, or press 'Detect Printers'.\n"
|
||||
"[ERROR] Please enter a valid IP address, or "
|
||||
"press 'Detect Printers'.\n"
|
||||
)
|
||||
self.config(cursor="")
|
||||
self.update_idletasks()
|
||||
|
@ -697,8 +838,12 @@ class EpsonPrinterUI(tk.Tk):
|
|||
model=model,
|
||||
hostname=ip_address
|
||||
)
|
||||
|
||||
if not printer:
|
||||
return
|
||||
try:
|
||||
self.text_dump = black.format_str(
|
||||
f'"{printer.model}": ' + repr(printer.stats()), mode=self.mode
|
||||
)
|
||||
self.show_treeview()
|
||||
|
||||
# Configure tags
|
||||
|
@ -720,14 +865,43 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.config(cursor="")
|
||||
self.update_idletasks()
|
||||
|
||||
def reset_printer_model(self):
|
||||
self.show_status_text_view()
|
||||
if self.model_var.get():
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
'[ERROR]: Unknown printer model '
|
||||
f'"{self.model_var.get()}"\n',
|
||||
)
|
||||
else:
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
'[ERROR]: Select a valid printer model.\n'
|
||||
)
|
||||
self.config(cursor="")
|
||||
self.update()
|
||||
self.model_var.set("")
|
||||
|
||||
def printer_config(self, cursor=True):
|
||||
"""
|
||||
Pressing F2 dumps the printer configuration
|
||||
"""
|
||||
model = self.model_var.get()
|
||||
printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=model
|
||||
)
|
||||
if not printer:
|
||||
return
|
||||
if not printer.parm:
|
||||
self.reset_printer_model()
|
||||
return
|
||||
try:
|
||||
self.text_dump = black.format_str(
|
||||
f'"{printer.model}": ' + repr(printer.parm),
|
||||
mode=self.mode
|
||||
)
|
||||
self.show_treeview()
|
||||
|
||||
# Configure tags
|
||||
|
@ -757,25 +931,20 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.after(100, lambda: method_to_call(cursor=False))
|
||||
return
|
||||
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):
|
||||
if not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, NO_CONF_ERROR)
|
||||
self.config(cursor="")
|
||||
self.update_idletasks()
|
||||
return
|
||||
printer = EpsonPrinter(
|
||||
conf_dict=self.conf_dict,
|
||||
replace_conf=self.replace_conf,
|
||||
model=model,
|
||||
hostname=ip_address
|
||||
)
|
||||
response = messagebox.askyesno(
|
||||
"Confirm Action", "Are you sure you want to proceed?"
|
||||
)
|
||||
if not self.printer:
|
||||
return
|
||||
if response:
|
||||
try:
|
||||
printer.reset_waste_ink_levels()
|
||||
self.printer.reset_waste_ink_levels()
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
"[INFO] Waste ink levels have been reset."
|
||||
|
@ -817,7 +986,9 @@ class EpsonPrinterUI(tk.Tk):
|
|||
if len(printers) == 1:
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[INFO] Found printer '{printers[0]['name']}' at {printers[0]['ip']} (hostname: {printers[0]['hostname']})\n",
|
||||
f"[INFO] Found printer '{printers[0]['name']}' "
|
||||
f"at {printers[0]['ip']} "
|
||||
f"(hostname: {printers[0]['hostname']})\n",
|
||||
)
|
||||
self.ip_var.set(printers[0]["ip"])
|
||||
for model in get_printer_models(printers[0]["name"]):
|
||||
|
@ -834,7 +1005,8 @@ class EpsonPrinterUI(tk.Tk):
|
|||
for printer in printers:
|
||||
self.status_text.insert(
|
||||
tk.END,
|
||||
f"[INFO] {printer['name']} found at {printer['ip']} (hostname: {printer['hostname']})\n",
|
||||
f"[INFO] {printer['name']} found at {printer['ip']}"
|
||||
f" (hostname: {printer['hostname']})\n",
|
||||
)
|
||||
else:
|
||||
self.status_text.insert(tk.END, "[WARN] No printers found.\n")
|
||||
|
@ -943,6 +1115,27 @@ class EpsonPrinterUI(tk.Tk):
|
|||
self.clipboard_clear()
|
||||
self.clipboard_append(item_text)
|
||||
|
||||
def copy_all_items(self):
|
||||
"""Copy all items to the clipboard."""
|
||||
self.clipboard_clear()
|
||||
self.clipboard_append(self.text_dump)
|
||||
|
||||
def print_items(self):
|
||||
"""Print items."""
|
||||
self.clipboard_append(self.text_dump)
|
||||
message = (
|
||||
"\x1B\x40" # Initialize printer
|
||||
"Printer configuration\n"
|
||||
+ self.text_dump
|
||||
)
|
||||
ip_address = self.ip_var.get()
|
||||
if not self._is_valid_ip(ip_address):
|
||||
return
|
||||
# Send the message to the printer
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect((ip_address, 9100))
|
||||
sock.sendall(message.encode('utf-8') + b"\f")
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
@ -951,6 +1144,22 @@ def main():
|
|||
parser = argparse.ArgumentParser(
|
||||
epilog='epson_print_conf GUI'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-m',
|
||||
'--model',
|
||||
dest='model',
|
||||
action="store",
|
||||
help='Printer model. Example: -m XP-205',
|
||||
default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a',
|
||||
'--address',
|
||||
dest='hostname',
|
||||
action="store",
|
||||
help='Printer host name or IP address. (Example: -a 192.168.1.87)',
|
||||
default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
'-P',
|
||||
"--pickle",
|
||||
|
@ -974,12 +1183,17 @@ def main():
|
|||
if args.pickle:
|
||||
conf_dict = pickle.load(args.pickle[0])
|
||||
|
||||
return EpsonPrinterUI(conf_dict=conf_dict, replace_conf=args.override)
|
||||
return EpsonPrinterUI(
|
||||
model=args.model,
|
||||
hostname=args.hostname,
|
||||
conf_dict=conf_dict,
|
||||
replace_conf=args.override
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main().mainloop()
|
||||
except:
|
||||
except KeyboardInterrupt:
|
||||
print("\nInterrupted.")
|
||||
sys.exit(0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue