Milestone ultima alpha
This commit is contained in:
@@ -7,8 +7,12 @@ from tkinter import filedialog, messagebox, ttk
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from gestione_aree import AsyncRunner, BusyOverlay
|
||||
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 window_placement import place_window_fullsize_below_parent_later
|
||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||
|
||||
try:
|
||||
from openpyxl import Workbook
|
||||
@@ -78,14 +82,22 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
def __init__(self, parent: tk.Widget, db_app, session=None):
|
||||
"""Initialize widgets and keep a reference to the shared DB client."""
|
||||
super().__init__(parent)
|
||||
self.title("Warehouse - Ricerca UDC/Lotto/Codice")
|
||||
self.geometry("1100x720")
|
||||
self.minsize(900, 560)
|
||||
self._theme = theme_section("search_window", {})
|
||||
self._locale_catalog = load_locale_catalog()
|
||||
self._tooltip_catalog = load_tooltip_catalog()
|
||||
self.title(loc_text("search.title", catalog=self._locale_catalog, default="Ricerca UDC/Lotto/Codice"))
|
||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x720")))
|
||||
minsize = theme_value(self._theme, "window_minsize", [900, 560])
|
||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||
self.resizable(True, True)
|
||||
|
||||
self.db = db_app
|
||||
self.session = session
|
||||
self._busy = BusyOverlay(self)
|
||||
try:
|
||||
self.configure(fg_color=theme_color(self._theme, "window_fg_color", ("#efefef", "#2f2f2f")))
|
||||
except Exception:
|
||||
pass
|
||||
self._busy = InlineBusyOverlay(self, self._theme)
|
||||
self._async = AsyncRunner(self)
|
||||
self._sort_state: dict[str, bool] = {}
|
||||
|
||||
@@ -97,26 +109,62 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
self.grid_rowconfigure(1, weight=1)
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
|
||||
top = ctk.CTkFrame(self)
|
||||
top = ctk.CTkFrame(
|
||||
self,
|
||||
fg_color=theme_color(self._theme, "toolbar_frame_fg_color", ("#d7d7d7", "#3b3b3b")),
|
||||
)
|
||||
top.grid(row=0, column=0, sticky="nsew", padx=8, pady=8)
|
||||
for i in range(8):
|
||||
top.grid_columnconfigure(i, weight=0)
|
||||
top.grid_columnconfigure(7, weight=1)
|
||||
|
||||
ctk.CTkLabel(top, text="UDC:").grid(row=0, column=0, sticky="w")
|
||||
label_font = theme_font(self._theme, "toolbar_label_font", ("Segoe UI", 10))
|
||||
entry_font = theme_font(self._theme, "entry_font", ("Segoe UI", 10))
|
||||
button_font = theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold"))
|
||||
|
||||
ctk.CTkLabel(
|
||||
top, text=loc_text("search.label.udc", catalog=self._locale_catalog, default="UDC:"), font=label_font
|
||||
).grid(row=0, column=0, sticky="w")
|
||||
self.var_udc = tk.StringVar()
|
||||
ctk.CTkEntry(top, textvariable=self.var_udc, width=160).grid(row=0, column=1, sticky="w", padx=(4, 12))
|
||||
ctk.CTkEntry(top, textvariable=self.var_udc, width=160, font=entry_font).grid(
|
||||
row=0, column=1, sticky="w", padx=(4, 12)
|
||||
)
|
||||
|
||||
ctk.CTkLabel(top, text="Lotto:").grid(row=0, column=2, sticky="w")
|
||||
ctk.CTkLabel(
|
||||
top, text=loc_text("search.label.lot", catalog=self._locale_catalog, default="Lotto:"), font=label_font
|
||||
).grid(row=0, column=2, sticky="w")
|
||||
self.var_lotto = tk.StringVar()
|
||||
ctk.CTkEntry(top, textvariable=self.var_lotto, width=140).grid(row=0, column=3, sticky="w", padx=(4, 12))
|
||||
ctk.CTkEntry(top, textvariable=self.var_lotto, width=140, font=entry_font).grid(
|
||||
row=0, column=3, sticky="w", padx=(4, 12)
|
||||
)
|
||||
|
||||
ctk.CTkLabel(top, text="Codice prodotto:").grid(row=0, column=4, sticky="w")
|
||||
ctk.CTkLabel(
|
||||
top,
|
||||
text=loc_text("search.label.code", catalog=self._locale_catalog, default="Codice prodotto:"),
|
||||
font=label_font,
|
||||
).grid(row=0, column=4, sticky="w")
|
||||
self.var_codice = tk.StringVar()
|
||||
ctk.CTkEntry(top, textvariable=self.var_codice, width=160).grid(row=0, column=5, sticky="w", padx=(4, 12))
|
||||
ctk.CTkEntry(top, textvariable=self.var_codice, width=160, font=entry_font).grid(
|
||||
row=0, column=5, sticky="w", padx=(4, 12)
|
||||
)
|
||||
|
||||
ctk.CTkButton(top, text="Cerca", command=self._do_search).grid(row=0, column=6, sticky="w")
|
||||
ctk.CTkButton(top, text="Esporta XLSX", command=self._export_xlsx).grid(row=0, column=7, sticky="e")
|
||||
btn_search = ctk.CTkButton(
|
||||
top,
|
||||
text=loc_text("search.button.search", catalog=self._locale_catalog, default="Cerca"),
|
||||
command=self._do_search,
|
||||
font=button_font,
|
||||
)
|
||||
btn_search.grid(row=0, column=6, sticky="w")
|
||||
btn_export = ctk.CTkButton(
|
||||
top,
|
||||
text=loc_text("search.button.export", catalog=self._locale_catalog, default="Esporta XLSX"),
|
||||
command=self._export_xlsx,
|
||||
font=button_font,
|
||||
)
|
||||
btn_export.grid(row=0, column=7, sticky="e")
|
||||
tip = tooltip_text("launcher.open_search", catalog=self._tooltip_catalog)
|
||||
if tip:
|
||||
WidgetToolTip(btn_search, tip)
|
||||
|
||||
wrap = ctk.CTkFrame(self)
|
||||
wrap.grid(row=1, column=0, sticky="nsew", padx=8, pady=(0, 8))
|
||||
@@ -168,10 +216,22 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
"""Export the currently visible search results to an Excel file."""
|
||||
rows = [self.tree.item(iid, "values") for iid in self.tree.get_children("")]
|
||||
if not rows:
|
||||
messagebox.showinfo("Esporta", "Non ci sono righe da esportare.", parent=self)
|
||||
messagebox.showinfo(
|
||||
loc_text("search.msg.export_title", catalog=self._locale_catalog, default="Esporta"),
|
||||
loc_text("search.msg.export_empty", catalog=self._locale_catalog, default="Non ci sono righe da esportare."),
|
||||
parent=self,
|
||||
)
|
||||
return
|
||||
if not _HAS_XLSX:
|
||||
messagebox.showerror("Esporta", "Per l'esportazione serve 'openpyxl' (pip install openpyxl).", parent=self)
|
||||
messagebox.showerror(
|
||||
loc_text("search.msg.export_title", catalog=self._locale_catalog, default="Esporta"),
|
||||
loc_text(
|
||||
"search.msg.export_dep",
|
||||
catalog=self._locale_catalog,
|
||||
default="Per l'esportazione serve 'openpyxl' (pip install openpyxl).",
|
||||
),
|
||||
parent=self,
|
||||
)
|
||||
return
|
||||
|
||||
from datetime import datetime
|
||||
@@ -211,9 +271,17 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
for j, width in widths.items():
|
||||
ws.column_dimensions[get_column_letter(j)].width = min(max(width + 2, 10), 60)
|
||||
wb.save(fname)
|
||||
messagebox.showinfo("Esporta", f"File creato:\n{fname}", parent=self)
|
||||
messagebox.showinfo(
|
||||
loc_text("search.msg.export_title", catalog=self._locale_catalog, default="Esporta"),
|
||||
f"File creato:\n{fname}",
|
||||
parent=self,
|
||||
)
|
||||
except Exception as ex:
|
||||
messagebox.showerror("Esporta", f"Errore durante l'esportazione:{ex}", parent=self)
|
||||
messagebox.showerror(
|
||||
loc_text("search.msg.export_title", catalog=self._locale_catalog, default="Esporta"),
|
||||
loc_text("search.msg.export_error", catalog=self._locale_catalog, default="Errore durante l'esportazione:{error}").format(error=ex),
|
||||
parent=self,
|
||||
)
|
||||
|
||||
def _on_dclick(self, evt):
|
||||
"""Copy the selected pallet barcode when a result cell is double-clicked."""
|
||||
@@ -357,8 +425,12 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
|
||||
if not (udc or lotto or codice):
|
||||
if not messagebox.askyesno(
|
||||
"Conferma",
|
||||
"Nessun filtro impostato. Vuoi cercare su TUTTO il magazzino?",
|
||||
loc_text("search.msg.confirm_title", catalog=self._locale_catalog, default="Conferma"),
|
||||
loc_text(
|
||||
"search.msg.confirm_all",
|
||||
catalog=self._locale_catalog,
|
||||
default="Nessun filtro impostato. Vuoi cercare su TUTTO il magazzino?",
|
||||
),
|
||||
parent=self,
|
||||
):
|
||||
return
|
||||
@@ -396,8 +468,12 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
|
||||
if not rows:
|
||||
messagebox.showinfo(
|
||||
"Nessun risultato",
|
||||
"Nessuna corrispondenza trovata con le chiavi di ricerca inserite.",
|
||||
loc_text("search.msg.no_results_title", catalog=self._locale_catalog, default="Nessun risultato"),
|
||||
loc_text(
|
||||
"search.msg.no_results",
|
||||
catalog=self._locale_catalog,
|
||||
default="Nessuna corrispondenza trovata con le chiavi di ricerca inserite.",
|
||||
),
|
||||
parent=self,
|
||||
)
|
||||
else:
|
||||
@@ -408,9 +484,19 @@ class SearchWindow(ctk.CTkToplevel):
|
||||
|
||||
def _err(ex):
|
||||
self._busy.hide()
|
||||
messagebox.showerror("Errore ricerca", str(ex), parent=self)
|
||||
messagebox.showerror(
|
||||
loc_text("search.msg.error_title", catalog=self._locale_catalog, default="Errore ricerca"),
|
||||
str(ex),
|
||||
parent=self,
|
||||
)
|
||||
|
||||
self._async.run(self.db.query_json(SQL_SEARCH, params), _ok, _err, busy=self._busy, message="Cerco...")
|
||||
self._async.run(
|
||||
self.db.query_json(SQL_SEARCH, params),
|
||||
_ok,
|
||||
_err,
|
||||
busy=self._busy,
|
||||
message=loc_text("search.busy", catalog=self._locale_catalog, default="Cerco..."),
|
||||
)
|
||||
|
||||
|
||||
def open_search_window(parent, db_app, session=None):
|
||||
@@ -427,4 +513,9 @@ def open_search_window(parent, db_app, session=None):
|
||||
w = SearchWindow(parent, db_app, session=session)
|
||||
setattr(parent, key, w)
|
||||
place_window_fullsize_below_parent_later(parent, w)
|
||||
try:
|
||||
w.lift()
|
||||
w.focus_force()
|
||||
except Exception:
|
||||
pass
|
||||
return w
|
||||
|
||||
Reference in New Issue
Block a user