Add tree view to printer status UI

This commit is contained in:
Ircama 2024-07-25 22:08:05 +02:00
parent a4d08e0721
commit 1ed4e93553

135
ui.py
View file

@ -1,11 +1,12 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk, Menu
from tkinter.scrolledtext import ScrolledText from tkinter.scrolledtext import ScrolledText
import threading import threading
import ipaddress import ipaddress
from epson_print_conf import EpsonPrinter
from find_printers import PrinterScanner from find_printers import PrinterScanner
from pprint import pformat from epson_print_conf import EpsonPrinter
import tkinter.font as tkfont
class EpsonPrinterUI(tk.Tk): class EpsonPrinterUI(tk.Tk):
def __init__(self): def __init__(self):
@ -64,10 +65,60 @@ class EpsonPrinterUI(tk.Tk):
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
self.status_text = ScrolledText(status_frame, height=10, width=50, wrap=tk.WORD) self.status_text = ScrolledText(status_frame, height=10, width=50, wrap=tk.WORD)
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=5, padx=5, sticky=(tk.W, tk.E, tk.N, tk.S))
# Style configuration
style = ttk.Style(self)
default_font = style.lookup("Treeview.Heading", "font")
# If the default_font is a tuple, split into components
if isinstance(default_font, tuple):
default_font_name, default_font_size = default_font[0], default_font[1]
else:
# 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')
style.configure("Treeview.Heading",
font=(default_font_name, default_font_size - 2, "bold"),
background="lightblue",
foreground="darkblue")
# Create and configure the Treeview widget
self.tree = ttk.Treeview(status_frame, style="Treeview")
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))
# Create a vertical scrollbar for the Treeview
tree_scrollbar = ttk.Scrollbar(status_frame, orient="vertical", command=self.tree.yview)
tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S))
# Configure the Treeview to use the scrollbar
self.tree.configure(yscrollcommand=tree_scrollbar.set)
# Create a context menu
self.context_menu = Menu(self, tearoff=0)
self.context_menu.add_command(label="Copy", command=self.copy_selected_item)
# Bind the right-click event to the Treeview
self.tree.bind("<Button-3>", self.show_context_menu)
# Hide the grid view initially
self.tree.grid_remove()
def show_status_text_view(self):
"""Show the status frame and hide the Treeview."""
self.tree.grid_remove()
self.status_text.grid()
def show_treeview(self):
"""Show the Treeview and hide the status frame."""
self.status_text.grid_remove()
self.tree.grid()
def print_status(self): def print_status(self):
self.show_status_text_view()
model = self.model_var.get() model = self.model_var.get()
ip_address = self.ip_var.get() ip_address = self.ip_var.get()
if not model or not self._is_valid_ip(ip_address): if not model or not self._is_valid_ip(ip_address):
@ -76,11 +127,17 @@ class EpsonPrinterUI(tk.Tk):
printer = EpsonPrinter(model=model, hostname=ip_address) printer = EpsonPrinter(model=model, hostname=ip_address)
try: try:
self.status_text.insert(tk.END, f"[INFO] {pformat(printer.stats())}\n") self.show_treeview()
# Populate the Treeview
self.populate_treeview('', self.tree, printer.stats())
# Expand all nodes
self.expand_all(self.tree)
except Exception as e: except Exception as e:
self.show_status_text_view()
self.status_text.insert(tk.END, f"[ERROR] {e}\n") self.status_text.insert(tk.END, f"[ERROR] {e}\n")
def reset_waste_ink(self): def reset_waste_ink(self):
self.show_status_text_view()
model = self.model_var.get() model = self.model_var.get()
ip_address = self.ip_var.get() ip_address = self.ip_var.get()
if not model or not self._is_valid_ip(ip_address): if not model or not self._is_valid_ip(ip_address):
@ -94,6 +151,7 @@ class EpsonPrinterUI(tk.Tk):
self.status_text.insert(tk.END, f"[ERROR] {e}\n") self.status_text.insert(tk.END, f"[ERROR] {e}\n")
def start_detect_printers(self): def start_detect_printers(self):
self.show_status_text_view()
self.status_text.insert(tk.END, "[INFO] Detecting printers... (this might take a while)\n") self.status_text.insert(tk.END, "[INFO] Detecting printers... (this might take a while)\n")
self.detect_button.config(state=tk.DISABLED) # disable button while processing self.detect_button.config(state=tk.DISABLED) # disable button while processing
@ -101,6 +159,7 @@ class EpsonPrinterUI(tk.Tk):
threading.Thread(target=self.detect_printers).start() threading.Thread(target=self.detect_printers).start()
def detect_printers(self): def detect_printers(self):
self.show_status_text_view()
printer_scanner=PrinterScanner() printer_scanner=PrinterScanner()
try: try:
printers = printer_scanner.get_all_printers() printers = printer_scanner.get_all_printers()
@ -121,6 +180,74 @@ class EpsonPrinterUI(tk.Tk):
except ValueError: except ValueError:
return False return False
def is_simple_type(self, data):
return isinstance(data, (str, int, float, bool))
def contains_parentheses(self, data):
"""Check if a string representation contains parentheses."""
if isinstance(data, (list, tuple, set)):
for item in data:
if isinstance(item, (tuple, list, set)):
return True
if isinstance(item, str) and ('(' in item or ')' in item):
return True
return False
def populate_treeview(self, parent, treeview, data):
if isinstance(data, dict):
for key, value in data.items():
if isinstance(value, (dict, list, set, tuple)):
node = treeview.insert(parent, 'end', text=key)
self.populate_treeview(node, treeview, value)
else:
treeview.insert(parent, 'end', text=f"{key}: {value}")
elif isinstance(data, list):
if all(self.is_simple_type(item) for item in data) and not self.contains_parentheses(data):
treeview.insert(parent, 'end', text=', '.join(map(str, data)))
else:
for item in data:
if isinstance(item, (dict, list, set, tuple)):
self.populate_treeview(parent, treeview, item)
else:
treeview.insert(parent, 'end', text=str(item))
elif isinstance(data, set):
if not self.contains_parentheses(data):
treeview.insert(parent, 'end', text=', '.join(map(str, data)))
else:
for item in data:
treeview.insert(parent, 'end', text=str(item))
elif isinstance(data, tuple):
treeview.insert(parent, 'end', text=str(data))
else:
treeview.insert(parent, 'end', text=str(data))
def expand_all(self, treeview):
def recursive_expand(item):
treeview.item(item, open=True)
children = treeview.get_children(item)
for child in children:
recursive_expand(child)
root_children = treeview.get_children()
for child in root_children:
recursive_expand(child)
def show_context_menu(self, event):
"""Show the context menu."""
# Select the item under the cursor
item = self.tree.identify_row(event.y)
if item:
self.tree.selection_set(item)
self.context_menu.post(event.x_root, event.y_root)
def copy_selected_item(self):
"""Copy the selected Treeview item text to the clipboard."""
selected_item = self.tree.selection()
if selected_item:
item_text = self.tree.item(selected_item[0], "text")
self.clipboard_clear()
self.clipboard_append(item_text)
if __name__ == "__main__": if __name__ == "__main__":
app = EpsonPrinterUI() app = EpsonPrinterUI()
app.mainloop() app.mainloop()