Alpha4 polish griglie e login
This commit is contained in:
@@ -70,6 +70,14 @@ from busy_overlay import InlineBusyOverlay
|
||||
from gestione_aree import AsyncRunner
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import (
|
||||
TABLE_HEADER_BG,
|
||||
TABLE_HEADER_FG,
|
||||
TABLE_ROW_EVEN,
|
||||
TABLE_ROW_ODD,
|
||||
apply_tksheet_visual_style,
|
||||
apply_tksheet_zebra,
|
||||
)
|
||||
from user_session import UserSession
|
||||
from version_info import module_version, versioned_title
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
@@ -391,13 +399,14 @@ class ScrollTable(ctk.CTkFrame):
|
||||
self._sort_key: Optional[str] = None
|
||||
self._sort_reverse = False
|
||||
self.total_w = sum(c.width for c in self.columns)
|
||||
self._vbar_visible = False
|
||||
|
||||
self.grid_rowconfigure(1, weight=1)
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# header
|
||||
self.h_canvas = tk.Canvas(self, height=ROW_H, highlightthickness=0, bd=0)
|
||||
self.h_inner = ctk.CTkFrame(self.h_canvas, fg_color="#f3f3f3",
|
||||
self.h_canvas = tk.Canvas(self, height=ROW_H, highlightthickness=0, bd=0, bg=TABLE_HEADER_BG)
|
||||
self.h_inner = ctk.CTkFrame(self.h_canvas, fg_color=TABLE_HEADER_BG,
|
||||
height=ROW_H, width=self.total_w)
|
||||
self.h_canvas.create_window((0,0), window=self.h_inner, anchor="nw",
|
||||
width=self.total_w, height=ROW_H)
|
||||
@@ -414,7 +423,6 @@ class ScrollTable(ctk.CTkFrame):
|
||||
# scrollbars
|
||||
self.vbar = tk.Scrollbar(self, orient="vertical", command=self.b_canvas.yview)
|
||||
self.xbar = tk.Scrollbar(self, orient="horizontal", command=self._xscroll_both)
|
||||
self.vbar.grid(row=1, column=1, sticky="ns")
|
||||
self.xbar.grid(row=2, column=0, sticky="ew")
|
||||
|
||||
# link scroll
|
||||
@@ -436,14 +444,14 @@ class ScrollTable(ctk.CTkFrame):
|
||||
for w in self.h_inner.winfo_children():
|
||||
w.destroy()
|
||||
|
||||
row = ctk.CTkFrame(self.h_inner, fg_color="#f3f3f3",
|
||||
row = ctk.CTkFrame(self.h_inner, fg_color=TABLE_HEADER_BG,
|
||||
height=ROW_H, width=self.total_w)
|
||||
row.pack(fill="x", expand=False)
|
||||
row.pack_propagate(False)
|
||||
|
||||
for col in self.columns:
|
||||
holder = ctk.CTkFrame(
|
||||
row, fg_color="#f3f3f3",
|
||||
row, fg_color=TABLE_HEADER_BG,
|
||||
width=col.width, height=ROW_H,
|
||||
border_width=1, border_color=self.GRID_COLOR
|
||||
)
|
||||
@@ -454,7 +462,7 @@ class ScrollTable(ctk.CTkFrame):
|
||||
if col.key == self._sort_key:
|
||||
header_text = f"{col.title} {'↓' if self._sort_reverse else '↑'}"
|
||||
|
||||
lbl = ctk.CTkLabel(holder, text=header_text, anchor="w")
|
||||
lbl = ctk.CTkLabel(holder, text=header_text, anchor="w", text_color=TABLE_HEADER_FG)
|
||||
lbl.pack(fill="both", padx=(self.PADX_L, self.PADX_R), pady=self.PADY)
|
||||
|
||||
if self.on_header_click and col.key != "__check__":
|
||||
@@ -475,10 +483,27 @@ class ScrollTable(ctk.CTkFrame):
|
||||
"""Keep the scroll region aligned with the current body content width."""
|
||||
self.b_canvas.itemconfigure(self.body_window, width=self.total_w)
|
||||
sr = self.b_canvas.bbox("all")
|
||||
content_height = int(sr[3]) if sr else 0
|
||||
if sr:
|
||||
self.b_canvas.configure(scrollregion=(0,0,max(self.total_w, sr[2]), sr[3]))
|
||||
else:
|
||||
self.b_canvas.configure(scrollregion=(0,0,self.total_w,0))
|
||||
self._update_vertical_scrollbar(content_height)
|
||||
|
||||
def _update_vertical_scrollbar(self, content_height: int):
|
||||
"""Show the vertical scrollbar only when body rows exceed the visible area."""
|
||||
try:
|
||||
visible_height = max(1, int(self.b_canvas.winfo_height()))
|
||||
except Exception:
|
||||
visible_height = 1
|
||||
needs_scroll = content_height > visible_height + 2
|
||||
if needs_scroll and not self._vbar_visible:
|
||||
self.vbar.grid(row=1, column=1, sticky="ns")
|
||||
self._vbar_visible = True
|
||||
elif not needs_scroll and self._vbar_visible:
|
||||
self.vbar.grid_remove()
|
||||
self._vbar_visible = False
|
||||
self.b_canvas.yview_moveto(0)
|
||||
|
||||
def _on_body_configure(self):
|
||||
"""React to body resize events by syncing dimensions and header scroll."""
|
||||
@@ -513,8 +538,11 @@ class ScrollTable(ctk.CTkFrame):
|
||||
delta = getattr(event, "delta", 0)
|
||||
if delta == 0:
|
||||
return
|
||||
if not self._vbar_visible:
|
||||
return "break"
|
||||
step = -1 if delta > 0 else 1
|
||||
self.b_canvas.yview_scroll(step, "units")
|
||||
return "break"
|
||||
|
||||
def clear_rows(self):
|
||||
"""Remove all rendered body rows."""
|
||||
@@ -530,14 +558,15 @@ class ScrollTable(ctk.CTkFrame):
|
||||
checkbox_builder: Optional[Callable[[tk.Widget], ctk.CTkCheckBox]] = None,
|
||||
):
|
||||
"""Append one row to the table body."""
|
||||
row = ctk.CTkFrame(self.b_inner, fg_color="transparent",
|
||||
row_bg = TABLE_ROW_EVEN if row_index % 2 == 0 else TABLE_ROW_ODD
|
||||
row = ctk.CTkFrame(self.b_inner, fg_color=row_bg,
|
||||
height=ROW_H, width=self.total_w)
|
||||
row.pack(fill="x", expand=False)
|
||||
row.pack_propagate(False)
|
||||
|
||||
for i, col in enumerate(self.columns):
|
||||
holder = ctk.CTkFrame(
|
||||
row, fg_color="transparent",
|
||||
row, fg_color=row_bg,
|
||||
width=col.width, height=ROW_H,
|
||||
border_width=1, border_color=self.GRID_COLOR
|
||||
)
|
||||
@@ -549,10 +578,10 @@ class ScrollTable(ctk.CTkFrame):
|
||||
cb = checkbox_builder(holder)
|
||||
cb.pack(padx=(self.PADX_L, self.PADX_R), pady=self.PADY, anchor="w")
|
||||
else:
|
||||
ctk.CTkLabel(holder, text="").pack(fill="both")
|
||||
ctk.CTkLabel(holder, text="", fg_color=row_bg).pack(fill="both")
|
||||
else:
|
||||
anchor = (anchors[i] if anchors else col.anchor)
|
||||
ctk.CTkLabel(holder, text=values[i], anchor=anchor).pack(
|
||||
ctk.CTkLabel(holder, text=values[i], anchor=anchor, fg_color=row_bg, text_color="#111827").pack(
|
||||
fill="both", padx=(self.PADX_L, self.PADX_R), pady=self.PADY
|
||||
)
|
||||
|
||||
@@ -696,6 +725,7 @@ class GestionePickingListFrame(ctk.CTkFrame):
|
||||
self.detail_sheet.change_theme("light green")
|
||||
self.detail_sheet.enable_bindings("all")
|
||||
self.detail_sheet.headers(self._detail_headers(), redraw=False)
|
||||
apply_tksheet_visual_style(self.detail_sheet)
|
||||
self.detail_sheet.bind("<ButtonRelease-1>", self._on_detail_sheet_left_click, add="+")
|
||||
self.detail_sheet.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
@@ -736,8 +766,10 @@ class GestionePickingListFrame(ctk.CTkFrame):
|
||||
data,
|
||||
reset_col_positions=True,
|
||||
reset_row_positions=True,
|
||||
redraw=True,
|
||||
redraw=False,
|
||||
)
|
||||
apply_tksheet_visual_style(self.detail_sheet)
|
||||
apply_tksheet_zebra(self.detail_sheet, len(data))
|
||||
self.detail_sheet.set_all_column_widths()
|
||||
|
||||
def _detail_sort_value(self, row: Dict[str, Any], key: str):
|
||||
|
||||
@@ -20,6 +20,7 @@ from audit_log import log_user_action
|
||||
from busy_overlay import InlineBusyOverlay
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import style_treeview, zebra_tag
|
||||
from user_session import UserSession
|
||||
from version_info import module_version, versioned_title
|
||||
|
||||
@@ -511,10 +512,6 @@ class ScaricoDialog(ctk.CTkToplevel):
|
||||
tree_host.grid_rowconfigure(0, weight=1)
|
||||
tree_host.grid_columnconfigure(0, weight=1)
|
||||
|
||||
style = ttk.Style(self)
|
||||
style.configure("Scarico.Treeview", rowheight=28, font=theme_font(self._theme, "tree_body_font", ("Segoe UI", 10)))
|
||||
style.configure("Scarico.Treeview.Heading", font=theme_font(self._theme, "tree_heading_font", ("Segoe UI", 10, "bold")))
|
||||
|
||||
self.rows_tree = ttk.Treeview(
|
||||
tree_host,
|
||||
columns=("sel", "udc", "last", "diag"),
|
||||
@@ -522,6 +519,13 @@ class ScaricoDialog(ctk.CTkToplevel):
|
||||
style="Scarico.Treeview",
|
||||
selectmode="none",
|
||||
)
|
||||
style_treeview(
|
||||
self.rows_tree,
|
||||
style_name="Scarico.Treeview",
|
||||
rowheight=28,
|
||||
font=theme_font(self._theme, "tree_body_font", ("Segoe UI", 10)),
|
||||
heading_font=theme_font(self._theme, "tree_heading_font", ("Segoe UI", 10, "bold")),
|
||||
)
|
||||
self.rows_tree.heading("sel", text="Sel")
|
||||
self.rows_tree.heading("udc", text=loc_text("scarico.col.udc", catalog=self._locale_catalog, default="UDC"))
|
||||
self.rows_tree.heading("last", text=loc_text("scarico.col.last_insert", catalog=self._locale_catalog, default="Ultimo inserimento"))
|
||||
@@ -575,6 +579,7 @@ class ScaricoDialog(ctk.CTkToplevel):
|
||||
row.last_event_at,
|
||||
row.diagnostic_note or "",
|
||||
),
|
||||
tags=(zebra_tag(idx),),
|
||||
)
|
||||
|
||||
self.update_idletasks()
|
||||
|
||||
@@ -68,7 +68,7 @@ class LoginWindow(tk.Toplevel):
|
||||
self._clear_topmost_after_id: str | None = None
|
||||
|
||||
self.title(versioned_title(loc_text("login.msg.title", catalog=self._locale_catalog, default="Login"), __name__))
|
||||
self.geometry("170x145+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "165x155+0+0")))
|
||||
self.geometry("170x148+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "165x170+0+0")))
|
||||
self.resizable(False, False)
|
||||
try:
|
||||
if parent is not None and parent.winfo_viewable():
|
||||
@@ -91,53 +91,59 @@ class LoginWindow(tk.Toplevel):
|
||||
def _build_ui(self) -> None:
|
||||
"""Build the compact operator login form."""
|
||||
|
||||
body = ttk.Frame(self, padding=8 if self.compact else 8)
|
||||
body = ttk.Frame(self, padding=6 if self.compact else 8)
|
||||
body.pack(fill="both", expand=True)
|
||||
body.columnconfigure(1, weight=0)
|
||||
|
||||
row_offset = 0
|
||||
|
||||
ttk.Label(body, text="User:").grid(row=row_offset, column=0, sticky="w", padx=(0, 4), pady=4)
|
||||
ttk.Label(body, text="User:").grid(row=row_offset, column=0, sticky="w", padx=(0, 4), pady=(2, 3))
|
||||
self.login_entry = ttk.Entry(body, textvariable=self.login_var, width=10, font=("Segoe UI", 10 if self.compact else 9))
|
||||
self.login_entry.grid(row=row_offset, column=1, sticky="w", pady=4)
|
||||
self.login_entry.grid(row=row_offset, column=1, sticky="w", pady=(2, 3))
|
||||
|
||||
ttk.Label(body, text="Pwd:").grid(row=row_offset + 1, column=0, sticky="w", padx=(0, 4), pady=4)
|
||||
ttk.Label(body, text="Pwd:").grid(row=row_offset + 1, column=0, sticky="w", padx=(0, 4), pady=(2, 2))
|
||||
self.password_entry = ttk.Entry(body, textvariable=self.password_var, width=10, show="*", font=("Segoe UI", 10 if self.compact else 9))
|
||||
self.password_entry.grid(row=row_offset + 1, column=1, sticky="w", pady=4)
|
||||
self.password_entry.grid(row=row_offset + 1, column=1, sticky="w", pady=(2, 2))
|
||||
|
||||
if self.compact:
|
||||
actions = ttk.Frame(body)
|
||||
actions.grid(row=row_offset + 2, column=0, columnspan=2, sticky="ew", pady=(6, 0))
|
||||
self._cancel_button = ttk.Button(
|
||||
actions,
|
||||
text="Annulla",
|
||||
command=self._on_cancel,
|
||||
)
|
||||
self._cancel_button.grid(row=1, column=0, sticky="ew", pady=(4, 0))
|
||||
actions.grid(row=row_offset + 2, column=0, columnspan=2, sticky="ew", pady=(3, 0))
|
||||
self._login_button = ttk.Button(
|
||||
actions,
|
||||
text="OK",
|
||||
command=self._on_login,
|
||||
)
|
||||
self._login_button.grid(row=0, column=0, sticky="ew")
|
||||
else:
|
||||
self.status_label = ttk.Label(body, textvariable=self._status_var, foreground="#555555")
|
||||
self.status_label.grid(row=2, column=0, columnspan=2, sticky="w", pady=(2, 2))
|
||||
|
||||
actions = ttk.Frame(body)
|
||||
actions.grid(row=3, column=0, columnspan=2, sticky="w", pady=(6, 0))
|
||||
self._cancel_button = ttk.Button(
|
||||
actions,
|
||||
text=loc_text("login.button.cancel", catalog=self._locale_catalog, default="Annulla"),
|
||||
text="Annulla",
|
||||
command=self._on_cancel,
|
||||
)
|
||||
self._cancel_button.grid(row=1, column=0, sticky="ew", pady=(4, 0))
|
||||
self._cancel_button.grid(row=1, column=0, sticky="ew", pady=(3, 0))
|
||||
else:
|
||||
self.status_label = ttk.Label(body, textvariable=self._status_var, foreground="#555555")
|
||||
self.status_label.grid(row=2, column=0, columnspan=2, sticky="w", pady=(1, 1))
|
||||
|
||||
actions = ttk.Frame(body)
|
||||
actions.grid(row=3, column=0, columnspan=2, sticky="w", pady=(3, 0))
|
||||
self._login_button = ttk.Button(
|
||||
actions,
|
||||
text=loc_text("login.button.submit", catalog=self._locale_catalog, default="OK"),
|
||||
command=self._on_login,
|
||||
)
|
||||
self._login_button.grid(row=0, column=0, sticky="ew")
|
||||
self._cancel_button = ttk.Button(
|
||||
actions,
|
||||
text=loc_text("login.button.cancel", catalog=self._locale_catalog, default="Annulla"),
|
||||
command=self._on_cancel,
|
||||
)
|
||||
self._cancel_button.grid(row=1, column=0, sticky="ew", pady=(3, 0))
|
||||
|
||||
for widget in (self.login_entry, self.password_entry, self._login_button, self._cancel_button):
|
||||
try:
|
||||
widget.configure(takefocus=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.bind("<Return>", lambda _e: self._on_login())
|
||||
self.bind("<Escape>", lambda _e: self._on_cancel())
|
||||
|
||||
@@ -23,7 +23,8 @@ from gestione_aree import AsyncRunner
|
||||
from gestione_scarico import move_pallet_async
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||
from ui_theme import theme_color, theme_font, theme_padding, theme_section, theme_value
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import style_treeview, zebra_tag
|
||||
from version_info import module_version, versioned_title
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
|
||||
@@ -301,35 +302,6 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
def _setup_tree_style(self):
|
||||
"""Apply a denser, spreadsheet-like style to the main result grid."""
|
||||
|
||||
style = ttk.Style(self)
|
||||
style.configure(
|
||||
"ResetCorsie.Treeview.Heading",
|
||||
font=theme_font(self._theme, "tree_heading_font", ("Segoe UI", 10, "bold")),
|
||||
background=theme_value(self._theme, "tree_heading_bg", "#b8c7db"),
|
||||
foreground=theme_value(self._theme, "tree_heading_fg", "#10243e"),
|
||||
relief="flat",
|
||||
padding=theme_padding(self._theme, "tree_heading_padding", (8, 6)),
|
||||
)
|
||||
style.map(
|
||||
"ResetCorsie.Treeview.Heading",
|
||||
background=[("active", theme_value(self._theme, "tree_heading_bg_active", "#aebfd6"))],
|
||||
relief=[("pressed", "groove"), ("!pressed", "flat")],
|
||||
)
|
||||
style.configure(
|
||||
"ResetCorsie.Treeview",
|
||||
font=theme_font(self._theme, "tree_body_font", ("Segoe UI", 10)),
|
||||
rowheight=int(theme_value(self._theme, "tree_row_height", 30)),
|
||||
background=theme_value(self._theme, "tree_body_bg", "#ffffff"),
|
||||
fieldbackground=theme_value(self._theme, "tree_body_bg", "#ffffff"),
|
||||
foreground=theme_value(self._theme, "tree_body_fg", "#1f1f1f"),
|
||||
borderwidth=0,
|
||||
)
|
||||
style.map(
|
||||
"ResetCorsie.Treeview",
|
||||
background=[("selected", theme_value(self._theme, "tree_selected_bg", "#cfe4ff"))],
|
||||
foreground=[("selected", theme_value(self._theme, "tree_selected_fg", "#10243e"))],
|
||||
)
|
||||
|
||||
@_log_call()
|
||||
def _build_ui(self):
|
||||
"""Create selectors, summary widgets and the occupied-cell grid."""
|
||||
@@ -414,8 +386,13 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
width=int(theme_value(self._theme, "tree_col_num_udc_width", 160)),
|
||||
anchor=str(theme_value(self._theme, "tree_col_num_udc_anchor", "center")),
|
||||
)
|
||||
self.tree.tag_configure("odd", background=theme_value(self._theme, "tree_row_odd_bg", "#ffffff"))
|
||||
self.tree.tag_configure("even", background=theme_value(self._theme, "tree_row_even_bg", "#f3f6fb"))
|
||||
style_treeview(
|
||||
self.tree,
|
||||
style_name="ResetCorsie.Treeview",
|
||||
rowheight=int(theme_value(self._theme, "tree_row_height", 30)),
|
||||
font=theme_font(self._theme, "tree_body_font", ("Segoe UI", 10)),
|
||||
heading_font=theme_font(self._theme, "tree_heading_font", ("Segoe UI", 10, "bold")),
|
||||
)
|
||||
|
||||
sy = ttk.Scrollbar(mid, orient="vertical", command=self.tree.yview)
|
||||
sx = ttk.Scrollbar(mid, orient="horizontal", command=self.tree.xview)
|
||||
@@ -532,7 +509,7 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
for idx, (_idc, ubi, n) in enumerate(det_rows):
|
||||
tag = "even" if idx % 2 else "odd"
|
||||
tag = zebra_tag(idx)
|
||||
self.tree.insert("", "end", values=(ubi, n), tags=(tag,))
|
||||
except Exception as ex:
|
||||
_MODULE_LOGGER.exception(f"Errore UI refresh reset corsie corsia={corsia}: {ex}")
|
||||
|
||||
@@ -11,6 +11,13 @@ from busy_overlay import InlineBusyOverlay
|
||||
from gestione_aree import AsyncRunner
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import (
|
||||
apply_tksheet_visual_style,
|
||||
apply_tksheet_zebra,
|
||||
merge_tags,
|
||||
style_treeview,
|
||||
zebra_tag,
|
||||
)
|
||||
from version_info import module_version, versioned_title
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||
@@ -177,17 +184,25 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
self.use_sheet = False
|
||||
cols = ("IDCella", "Ubicazione", "UDC", "Lotto", "Codice", "Descrizione")
|
||||
self.tree = ttk.Treeview(wrap, columns=cols, show="headings")
|
||||
self._style = ttk.Style(self)
|
||||
try:
|
||||
self._style.theme_use(self._style.theme_use())
|
||||
except Exception:
|
||||
pass
|
||||
self._style.configure("Search.Treeview", rowheight=22, font=("", 9))
|
||||
self._style.configure("Search.Treeview.Heading", font=("", 9, "bold"), background="#F3F4F6")
|
||||
self._style.map("Search.Treeview", background=[("selected", "#DCEBFF")])
|
||||
self.tree.configure(style="Search.Treeview")
|
||||
self.tree.tag_configure("even", background="#FFFFFF")
|
||||
self.tree.tag_configure("odd", background="#F7F9FC")
|
||||
headings = {
|
||||
"IDCella": ("IDCella", 90, "e"),
|
||||
"Ubicazione": ("Ubicazione", 150, "w"),
|
||||
"UDC": ("UDC / Barcode", 130, "w"),
|
||||
"Lotto": ("Lotto", 130, "w"),
|
||||
"Codice": ("Codice prodotto", 150, "w"),
|
||||
"Descrizione": ("Descrizione prodotto", 340, "w"),
|
||||
}
|
||||
for col in cols:
|
||||
text, width, anchor = headings[col]
|
||||
self.tree.heading(col, text=text)
|
||||
self.tree.column(col, width=width, anchor=anchor, stretch=True)
|
||||
self._style = style_treeview(
|
||||
self.tree,
|
||||
style_name="Search.Treeview",
|
||||
rowheight=22,
|
||||
font=("", 9),
|
||||
heading_font=("", 9, "bold"),
|
||||
)
|
||||
self.tree.tag_configure("id9999", background="#FFECEC", foreground="#B00020")
|
||||
|
||||
sy = ttk.Scrollbar(wrap, orient="vertical", command=self.tree.yview)
|
||||
@@ -212,7 +227,7 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
is9999 = int(vals[0]) == 9999
|
||||
except Exception:
|
||||
is9999 = False
|
||||
tags = ("id9999", zebra) if is9999 else (zebra,)
|
||||
tags = merge_tags(zebra, "id9999" if is9999 else "")
|
||||
self.tree.item(iid, tags=tags)
|
||||
|
||||
def _export_xlsx(self):
|
||||
@@ -417,6 +432,8 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
hdrs = list(headers)
|
||||
hdrs[c] = hdrs[c] + arrow
|
||||
self.sheet.headers(hdrs)
|
||||
apply_tksheet_visual_style(self.sheet)
|
||||
apply_tksheet_zebra(self.sheet, len(data))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -452,7 +469,10 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
for row in rows:
|
||||
idc, ubi, udc_v, lot_v, cod_v, desc_v = row
|
||||
data.append([idc, ubi, udc_v, lot_v, cod_v, desc_v])
|
||||
self.sheet.headers(["IDCella", "Ubicazione", "UDC", "Lotto", "Codice", "Descrizione"])
|
||||
apply_tksheet_visual_style(self.sheet)
|
||||
self.sheet.set_sheet_data(data)
|
||||
apply_tksheet_zebra(self.sheet, len(data))
|
||||
self.sheet.set_all_cell_sizes_to_text()
|
||||
except Exception:
|
||||
self.use_sheet = False
|
||||
@@ -461,12 +481,11 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
self.tree.delete(iid)
|
||||
for idx, row in enumerate(rows):
|
||||
idc, ubi, udc_v, lot_v, cod_v, desc_v = row
|
||||
zebra = "even" if idx % 2 == 0 else "odd"
|
||||
try:
|
||||
is9999 = int(idc) == 9999
|
||||
except Exception:
|
||||
is9999 = False
|
||||
tags = ("id9999", zebra) if is9999 else (zebra,)
|
||||
tags = merge_tags(zebra_tag(idx), "id9999" if is9999 else "")
|
||||
self.tree.insert("", "end", values=(idc, ubi, udc_v, lot_v, cod_v, desc_v), tags=tags)
|
||||
|
||||
if not rows:
|
||||
|
||||
@@ -13,6 +13,7 @@ from busy_overlay import InlineBusyOverlay
|
||||
from gestione_aree import AsyncRunner
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import merge_tags, style_treeview, zebra_tag
|
||||
from version_info import module_version, versioned_title
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
|
||||
@@ -251,8 +252,10 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
for col in columns:
|
||||
tree.heading(col, text=col)
|
||||
tree.column(col, width=widths.get(col, 100), anchor="w")
|
||||
style_treeview(tree, style_name="HistoryPicking.Treeview", rowheight=24)
|
||||
tree.tag_configure("done", background="#ECECEC")
|
||||
tree.tag_configure("active", background="#EAF7EA")
|
||||
tree.tag_configure("warning", background="#FFE3B3")
|
||||
sy = ttk.Scrollbar(wrap, orient="vertical", command=tree.yview)
|
||||
sx = ttk.Scrollbar(wrap, orient="horizontal", command=tree.xview)
|
||||
tree.configure(yscrollcommand=sy.set, xscrollcommand=sx.set)
|
||||
@@ -298,7 +301,19 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
self.detail_tree.delete(*self.detail_tree.get_children(""))
|
||||
for index, row in enumerate(rows):
|
||||
stato = str(row.get("StatoOperativo") or "")
|
||||
tag = "done" if stato in {"Chiusa", "Esaurita"} else "active" if int(row.get("IDStato") or 0) == 1 else ""
|
||||
is_open_with_shipped = (
|
||||
str(row.get("StatoDocumento") or "") == "P"
|
||||
and int(row.get("RigheSpedite") or 0) > 0
|
||||
)
|
||||
tag = (
|
||||
"warning"
|
||||
if is_open_with_shipped
|
||||
else "done"
|
||||
if stato in {"Chiusa", "Esaurita"}
|
||||
else "active"
|
||||
if int(row.get("IDStato") or 0) == 1
|
||||
else ""
|
||||
)
|
||||
self.master_tree.insert(
|
||||
"",
|
||||
"end",
|
||||
@@ -314,7 +329,7 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
stato,
|
||||
row.get("IDStato", ""),
|
||||
),
|
||||
tags=(tag,) if tag else (),
|
||||
tags=merge_tags(zebra_tag(index), tag),
|
||||
)
|
||||
|
||||
def _on_master_select(self, _event=None) -> None:
|
||||
@@ -357,8 +372,10 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
|
||||
def _fill_detail(self, rows: list[dict[str, Any]]) -> None:
|
||||
self.detail_tree.delete(*self.detail_tree.get_children(""))
|
||||
for row in rows:
|
||||
for index, row in enumerate(rows):
|
||||
is_open_shipped = str(row.get("StatoDocumento") or "") == "P" and int(row.get("Cella") or 0) == 9999
|
||||
done = str(row.get("StatoDocumento") or "") == "D" or int(row.get("Cella") or 0) == 9999
|
||||
tag = "warning" if is_open_shipped else "done" if done else ""
|
||||
self.detail_tree.insert(
|
||||
"",
|
||||
"end",
|
||||
@@ -374,7 +391,7 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
row.get("Ubicazione", ""),
|
||||
row.get("Ordinamento", ""),
|
||||
),
|
||||
tags=("done",) if done else (),
|
||||
tags=merge_tags(zebra_tag(index), tag),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from busy_overlay import InlineBusyOverlay
|
||||
from gestione_aree import AsyncRunner
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import merge_tags, style_treeview, zebra_tag
|
||||
from version_info import module_version, versioned_title
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
|
||||
@@ -221,6 +222,7 @@ class StoricoUDCWindow(ctk.CTkToplevel):
|
||||
for col in cols:
|
||||
self.tree.heading(col, text=col)
|
||||
self.tree.column(col, width=90, anchor="w")
|
||||
style_treeview(self.tree, style_name="HistoryUDC.Treeview", rowheight=24)
|
||||
self.tree.column("ID", width=70, anchor="e")
|
||||
self.tree.column("Tipo", width=55, anchor="center")
|
||||
self.tree.column("Rif", width=70, anchor="e")
|
||||
@@ -283,7 +285,7 @@ class StoricoUDCWindow(ctk.CTkToplevel):
|
||||
value = row.get(name, "")
|
||||
return "" if value is None else value
|
||||
|
||||
for row in rows:
|
||||
for index, row in enumerate(rows):
|
||||
tipo = str(row.get("Tipo") or "")
|
||||
tag = "versamento" if tipo == "V" else "prelievo" if tipo == "P" else "spedita" if tipo == "SPED" else ""
|
||||
self.tree.insert(
|
||||
@@ -302,7 +304,7 @@ class StoricoUDCWindow(ctk.CTkToplevel):
|
||||
_value(row, "ModUtente"),
|
||||
_value(row, "ModDataOra"),
|
||||
),
|
||||
tags=(tag,) if tag else (),
|
||||
tags=merge_tags(zebra_tag(index), tag),
|
||||
)
|
||||
|
||||
|
||||
|
||||
108
ui_tables.py
Normal file
108
ui_tables.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""Shared visual helpers for data grids."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from tkinter import ttk
|
||||
from typing import Any
|
||||
|
||||
TABLE_ROW_EVEN = "#FFFFFF"
|
||||
TABLE_ROW_ODD = "#F4F6F8"
|
||||
TABLE_HEADER_BG = "#D1D5DB"
|
||||
TABLE_HEADER_FG = "#111827"
|
||||
TABLE_SELECTED_BG = "#DCEBFF"
|
||||
TABLE_SELECTED_FG = "#111827"
|
||||
|
||||
|
||||
def style_treeview(
|
||||
tree: ttk.Treeview,
|
||||
*,
|
||||
style_name: str,
|
||||
rowheight: int = 24,
|
||||
font: Any = ("Segoe UI", 9),
|
||||
heading_font: Any = ("Segoe UI", 9, "bold"),
|
||||
) -> ttk.Style:
|
||||
"""Apply a consistent high-contrast header and zebra-ready style."""
|
||||
|
||||
style = ttk.Style(tree)
|
||||
style.configure(style_name, rowheight=rowheight, font=font)
|
||||
style.configure(
|
||||
f"{style_name}.Heading",
|
||||
font=heading_font,
|
||||
background=TABLE_HEADER_BG,
|
||||
foreground=TABLE_HEADER_FG,
|
||||
relief="flat",
|
||||
)
|
||||
style.map(
|
||||
f"{style_name}.Heading",
|
||||
background=[("active", TABLE_HEADER_BG), ("pressed", TABLE_HEADER_BG)],
|
||||
foreground=[("active", TABLE_HEADER_FG), ("pressed", TABLE_HEADER_FG)],
|
||||
)
|
||||
style.map(
|
||||
style_name,
|
||||
background=[("selected", TABLE_SELECTED_BG)],
|
||||
foreground=[("selected", TABLE_SELECTED_FG)],
|
||||
)
|
||||
tree.configure(style=style_name)
|
||||
configure_treeview_zebra_tags(tree)
|
||||
return style
|
||||
|
||||
|
||||
def configure_treeview_zebra_tags(tree: ttk.Treeview) -> None:
|
||||
"""Register alternating row color tags on a Treeview."""
|
||||
|
||||
tree.tag_configure("even", background=TABLE_ROW_EVEN)
|
||||
tree.tag_configure("odd", background=TABLE_ROW_ODD)
|
||||
|
||||
|
||||
def zebra_tag(index: int) -> str:
|
||||
"""Return the alternating row tag for the given zero-based index."""
|
||||
|
||||
return "even" if index % 2 == 0 else "odd"
|
||||
|
||||
|
||||
def merge_tags(*tags: str) -> tuple[str, ...]:
|
||||
"""Return non-empty tags preserving order."""
|
||||
|
||||
return tuple(tag for tag in tags if tag)
|
||||
|
||||
|
||||
def apply_tksheet_visual_style(sheet: Any) -> None:
|
||||
"""Apply best-effort header contrast and zebra rows to a tksheet widget."""
|
||||
|
||||
try:
|
||||
sheet.set_options(
|
||||
header_bg=TABLE_HEADER_BG,
|
||||
header_fg=TABLE_HEADER_FG,
|
||||
header_selected_cells_bg=TABLE_HEADER_BG,
|
||||
header_selected_cells_fg=TABLE_HEADER_FG,
|
||||
table_bg=TABLE_ROW_EVEN,
|
||||
table_fg="#111827",
|
||||
selected_rows_bg=TABLE_SELECTED_BG,
|
||||
selected_rows_fg=TABLE_SELECTED_FG,
|
||||
selected_cells_bg=TABLE_SELECTED_BG,
|
||||
selected_cells_fg=TABLE_SELECTED_FG,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def apply_tksheet_zebra(sheet: Any, row_count: int) -> None:
|
||||
"""Apply alternating row colors to a tksheet widget when supported."""
|
||||
|
||||
try:
|
||||
sheet.dehighlight_rows(redraw=False)
|
||||
except Exception:
|
||||
pass
|
||||
for row_index in range(row_count):
|
||||
try:
|
||||
sheet.highlight_rows(
|
||||
rows=[row_index],
|
||||
bg=TABLE_ROW_EVEN if row_index % 2 == 0 else TABLE_ROW_ODD,
|
||||
redraw=False,
|
||||
)
|
||||
except Exception:
|
||||
break
|
||||
try:
|
||||
sheet.redraw()
|
||||
except Exception:
|
||||
pass
|
||||
@@ -164,7 +164,7 @@
|
||||
}
|
||||
},
|
||||
"login_window": {
|
||||
"window_geometry": "165x155+0+0",
|
||||
"window_geometry": "165x170+0+0",
|
||||
"overlay_cover_fg_color": ["#d9d9d9", "#4a4a4a"],
|
||||
"overlay_panel_fg_color": ["#f2f2f2", "#353535"],
|
||||
"overlay_panel_corner_radius": 10,
|
||||
|
||||
@@ -22,6 +22,7 @@ from gestione_scarico import move_pallet_async
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import merge_tags, style_treeview, zebra_tag
|
||||
from version_info import module_version, versioned_title
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
|
||||
@@ -510,6 +511,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
self.tree.column("col2", width=250, anchor="w")
|
||||
self.tree.column("col3", width=120, anchor="w")
|
||||
self.tree.column("col4", width=260, anchor="w")
|
||||
style_treeview(self.tree, style_name="MultiUDC.Treeview", rowheight=24)
|
||||
y = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
|
||||
x = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview)
|
||||
self.tree.configure(yscrollcommand=y.set, xscrollcommand=x.set)
|
||||
@@ -545,6 +547,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
):
|
||||
self.sum_tbl.heading(key, text=title)
|
||||
self.sum_tbl.column(key, width=width, anchor=anchor)
|
||||
style_treeview(self.sum_tbl, style_name="MultiUDCSummary.Treeview", rowheight=24)
|
||||
y2 = ttk.Scrollbar(inner, orient="vertical", command=self.sum_tbl.yview)
|
||||
x2 = ttk.Scrollbar(inner, orient="horizontal", command=self.sum_tbl.xview)
|
||||
self.sum_tbl.configure(yscrollcommand=y2.set, xscrollcommand=x2.set)
|
||||
@@ -663,7 +666,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
"""Populate root tree nodes after the aisle query completes."""
|
||||
rows = _json_obj(res).get("rows", [])
|
||||
_log_dataset("multi_udc_corsie", rows)
|
||||
for row in rows:
|
||||
for index, row in enumerate(rows):
|
||||
corsia = row.get("Corsia")
|
||||
if not corsia:
|
||||
continue
|
||||
@@ -675,7 +678,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
text=self._format_corsia_text(corsia),
|
||||
values=("", "", ""),
|
||||
open=False,
|
||||
tags=("corsia",),
|
||||
tags=merge_tags(zebra_tag(index), "corsia"),
|
||||
)
|
||||
self.tree.insert(node_id, "end", iid=f"{node_id}::lazy", text="...", values=("", "", ""))
|
||||
|
||||
@@ -727,7 +730,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
if not rows:
|
||||
self.tree.insert(parent_iid, "end", text="(nessuna cella con >1 UDC)", values=("", "", ""))
|
||||
return
|
||||
for row in rows:
|
||||
for index, row in enumerate(rows):
|
||||
idc = row["IDCella"]
|
||||
ubi = row["Ubicazione"]
|
||||
corsia = row.get("Corsia")
|
||||
@@ -744,7 +747,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
text=label,
|
||||
values=(f"IDCella {idc}", "", ""),
|
||||
open=False,
|
||||
tags=("cella", f"corsia:{corsia}"),
|
||||
tags=merge_tags(zebra_tag(index), "cella", f"corsia:{corsia}"),
|
||||
)
|
||||
if not any(child.endswith("::lazy") for child in self.tree.get_children(node_id)):
|
||||
self.tree.insert(node_id, "end", iid=f"{node_id}::lazy", text="...", values=("", "", ""))
|
||||
@@ -784,7 +787,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
idcella_txt = self.tree.item(parent_iid, "values")[0]
|
||||
idcella_num = int(idcella_txt.split()[-1]) if idcella_txt else None
|
||||
|
||||
for row in rows:
|
||||
for index, row in enumerate(rows):
|
||||
pallet = row.get("Pallet", "")
|
||||
desc = row.get("Descrizione", "")
|
||||
lotto = row.get("Lotto", "")
|
||||
@@ -816,7 +819,13 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
iid=leaf_id,
|
||||
text=self._format_pallet_text(str(pallet), leaf_id in self.selected_udc_keys),
|
||||
values=(desc, lotto, causale),
|
||||
tags=("pallet", f"corsia:{corsia_val}", f"ubicazione:{cella_ubi}", f"idcella:{idcella_num}"),
|
||||
tags=merge_tags(
|
||||
zebra_tag(index),
|
||||
"pallet",
|
||||
f"corsia:{corsia_val}",
|
||||
f"ubicazione:{cella_ubi}",
|
||||
f"idcella:{idcella_num}",
|
||||
),
|
||||
)
|
||||
|
||||
@_log_call()
|
||||
@@ -990,11 +999,12 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
_log_dataset("multi_udc_riepilogo", rows)
|
||||
for item in self.sum_tbl.get_children():
|
||||
self.sum_tbl.delete(item)
|
||||
for row in rows:
|
||||
for index, row in enumerate(rows):
|
||||
self.sum_tbl.insert(
|
||||
"",
|
||||
"end",
|
||||
values=(row.get("Corsia"), row.get("TotCelle", 0), row.get("CelleMultiple", 0), f"{row.get('Percentuale', 0):.2f}"),
|
||||
tags=(zebra_tag(index),),
|
||||
)
|
||||
|
||||
def expand_all(self):
|
||||
|
||||
Reference in New Issue
Block a user