@@ -618,7 +626,7 @@ $("avvia").onclick = avviaAnteprima;
$("ferma").onclick = fermaAnteprima;
$("scatta").onclick = scatta;
$("azzeraMax").onclick = function () { A.nitMax = 0; $("nitidezzaMax").textContent = "-"; $("barraNitidezza").style.width = "0"; };
-// #92: il mirino vale per entrambi i riquadri; la casella «Mostra immagine calibrata» apre il riquadro e raddrizza il frame gia' tenuto
+// #92: il mirino vale per entrambi i riquadri; #93: la casella «Attiva calibrazione» apre il riquadro e raddrizza il frame gia' tenuto
$("mirinoCasella").onchange = function () { $("mirino").hidden = $("mirinoCal").hidden = !$("mirinoCasella").checked; };
function mostraRiquadroCal() {
var si = $("mostraCal").checked;
@@ -995,6 +1003,7 @@ function disegnaElenco() {
annullaConferma();
t.textContent = "";
C.celle = [];
+ disegnaSceltaCal(); // #93: la combo sotto la calibrata segue l'elenco, anche se vuoto
if (!C.elenco.length) {
var vuota = document.createElement("div");
vuota.className = "nota";
@@ -1035,7 +1044,7 @@ function disegnaElenco() {
var bc = bottone("Carica", "", function () { caricaNome(c.nome); });
bc.disabled = !$("sorgente").value;
az.appendChild(bc);
- if (attiva) az.appendChild(bottone("Scollega", "secondario", scollega));
+ if (attiva) az.appendChild(bottone("Scollega", "secondario", function () { scollega("msgElenco"); })); // #93: senza l'evento come parametro
az.appendChild(bottone("Elimina", "secondario", function (e) { eliminaNome(c.nome, e.currentTarget); }));
sc.appendChild(riga1);
sc.appendChild(riga2);
@@ -1050,41 +1059,80 @@ function segnaRisoluzioni() {
(C.celle || []).forEach(function (c) {
c.span.hidden = !(A.ris && c.w > 0 && c.h > 0 && (A.ris.w !== c.w || A.ris.h !== c.h));
});
+ // #93: stesso avviso nelle voci della combo; si cambia solo il testo, cosi' scelta e menu aperto restano
+ var sel = $("sceltaCal");
+ for (var i = 0; i < sel.options.length; i++) if (sel.options[i].value) sel.options[i].textContent = testoVoceCal(sel.options[i].dataset);
+}
+
+// #93: testo di una voce della combo: «nome · W×H · px/mm», con «risoluzione diversa» se il frame noto non combacia
+function testoVoceCal(d) {
+ var w = Number(d.w), h = Number(d.h);
+ var t = d.nome + " · " + (w && h ? fmt(w, 0) + "×" + fmt(h, 0) : "-") + " · " + fmt(Number(d.px), 3) + " px/mm";
+ if (A.ris && w > 0 && h > 0 && (A.ris.w !== w || A.ris.h !== h)) t += " — risoluzione diversa";
+ return t;
+}
+// #93: ridisegna la combo (ogni ricarica dell'elenco); preselezionata la calibrazione attiva, senza sorgente tutto spento
+function disegnaSceltaCal() {
+ var sel = $("sceltaCal");
+ sel.textContent = "";
+ var nessuna = document.createElement("option");
+ nessuna.value = "";
+ nessuna.textContent = "— nessuna (non calibrata) —";
+ sel.appendChild(nessuna);
+ (C.elenco || []).forEach(function (c) {
+ var o = document.createElement("option");
+ o.value = c.nome;
+ o.dataset.nome = c.nome; o.dataset.w = c.image_width || 0; o.dataset.h = c.image_height || 0; o.dataset.px = c.px_per_mm || 0;
+ o.textContent = testoVoceCal(o.dataset);
+ sel.appendChild(o);
+ });
+ sel.value = C.attiva !== null && C.attiva !== undefined ? C.attiva : "";
+ if (sel.selectedIndex < 0) sel.selectedIndex = 0;
+ sel.disabled = $("confermaCal").disabled = !$("sorgente").value;
}
// «Carica»: la calibrazione diventa quella attiva della sorgente. Un errore (es. risoluzione diversa) non ferma l'anteprima.
-async function caricaNome(nome) {
+async function caricaNome(nome, idMsg) {
+ idMsg = idMsg || "msgElenco"; // #93: il messaggio puo' stare sotto la calibrata (msgSceltaCal)
var sorgente = $("sorgente").value;
- if (!sorgente) { dire("msgElenco", "Scegliere una sorgente nella tab Acquisizione.", "errore"); return; }
- dire("msgElenco", "Caricamento di «" + nome + "»...", "");
+ if (!sorgente) { dire(idMsg, "Scegliere una sorgente nella tab Acquisizione.", "errore"); return; }
+ dire(idMsg, "Caricamento di «" + nome + "»...", "");
try {
var r = await fetch("/camera/calibrazione-attiva", { method: "PUT", headers: JSON_H, body: JSON.stringify({ source: sorgente, nome: nome }) });
if (sorgente !== $("sorgente").value) return;
- if (!r.ok) { dire("msgElenco", await dettaglio(r), "errore"); return; }
- dire("msgElenco", "Calibrazione «" + nome + "» caricata sulla sorgente.", "ok");
+ if (!r.ok) { dire(idMsg, await dettaglio(r), "errore"); disegnaSceltaCal(); return; } // #93: la combo torna sull'attiva
+ dire(idMsg, "Calibrazione «" + nome + "» caricata sulla sorgente.", "ok");
A.bloccoCal = false; // #92
- await aggiornaStato(); // prima lo stato nuovo, poi il riquadro calibrata con la nuova calibrazione
+ await aggiornaStato(); // prima lo stato nuovo (rilegge anche l'elenco, #93: schede e combo allineate), poi il riquadro calibrata
aggiornaCalibrata();
} catch (e) {
- dire("msgElenco", "Servizio non raggiungibile.", "errore");
+ dire(idMsg, "Servizio non raggiungibile.", "errore");
+ disegnaSceltaCal(); // #93
}
}
// «Scollega»: la sorgente torna senza calibrazione, la libreria resta
-async function scollega() {
+async function scollega(idMsg) {
+ idMsg = idMsg || "msgElenco"; // #93
var sorgente = $("sorgente").value;
if (!sorgente) return;
try {
var r = await fetch("/camera/calibration?source=" + encodeURIComponent(sorgente), { method: "DELETE" });
- dire("msgElenco", r.ok ? "Calibrazione scollegata dalla sorgente (resta fra le salvate)." : await dettaglio(r), r.ok ? "ok" : "errore");
+ dire(idMsg, r.ok ? "Calibrazione scollegata dalla sorgente (resta fra le salvate)." : await dettaglio(r), r.ok ? "ok" : "errore");
} catch (e) {
- dire("msgElenco", "Servizio non raggiungibile.", "errore");
+ dire(idMsg, "Servizio non raggiungibile.", "errore");
}
A.bloccoCal = false; // #92
- await aggiornaStato();
+ await aggiornaStato(); // #93: rilegge anche l'elenco, quindi la combo torna sull'attiva anche dopo un errore
aggiornaCalibrata(); // #92: senza calibrazione il riquadro dice che non c'e'
}
+// #93: CONFERMA sotto la calibrata: voce scelta -> PUT calibrazione-attiva, «nessuna» -> DELETE calibration
+$("confermaCal").onclick = function () {
+ var nome = $("sceltaCal").value;
+ if (nome) caricaNome(nome, "msgSceltaCal"); else scollega("msgSceltaCal");
+};
+
// «Elimina» di una riga: conferma in pagina con un secondo clic entro 4 s (niente finestre del browser)
function annullaConferma() {
clearTimeout(C.confermaTimer);
diff --git a/scripts/tests/test_pagina_acquisizione.py b/scripts/tests/test_pagina_acquisizione.py
index e7b9d78..1c68ae3 100644
--- a/scripts/tests/test_pagina_acquisizione.py
+++ b/scripts/tests/test_pagina_acquisizione.py
@@ -42,7 +42,7 @@ def test_id_principali():
def test_sezioni_e_testi_italiani():
for testo in ("Sorgente", "Anteprima", "Parametri camera", "Calibrazione",
"Salva come predefiniti", "Carica immagine da file...", "Questa sorgente non ha parametri regolabili",
- "Mostra immagine calibrata"): # #92
+ "Attiva calibrazione"): # #92 #93
assert testo in PAGINA, testo
assert "it-IT" in PAGINA # numeri con la virgola
assert "Choose File" not in PAGINA
diff --git a/scripts/tests/test_pagina_tab.py b/scripts/tests/test_pagina_tab.py
index b74d70c..5a2c91d 100644
--- a/scripts/tests/test_pagina_tab.py
+++ b/scripts/tests/test_pagina_tab.py
@@ -30,7 +30,7 @@ def test_sorgente_in_cima_poi_riquadri():
assert i_acq < i_sorg < i_ant < i_riq < PAGINA.index('id="sezioneParametri"') < i_cal
assert PAGINA.index('id="sezioneCalibrazione"') > i_cal
# barra: casella calibrata e mirino; la vecchia casella «Immagine grezza» non c'e' piu'
- assert 'id="mostraCal"' in PAGINA and "Mostra immagine calibrata" in PAGINA
+ assert 'id="mostraCal"' in PAGINA and "Attiva calibrazione" in PAGINA
assert 'id="grezzo"' not in PAGINA and "Immagine grezza (senza correzione)" not in PAGINA
# riquadri: «Immagine» sempre visibile, «Calibrata» nascosta finche' la casella e' spenta
assert re.search(r'
', PAGINA)
@@ -124,3 +124,21 @@ def test_elenco_sorgenti_senza_attese_e_calibrata_solo_se_serve():
assert "nessuna calibrazione attiva su questa sorgente" in PAGINA
assert "if (r.status === 409) A.bloccoCal = true;" in PAGINA and "if (A.bloccoCal) return;" in PAGINA
assert PAGINA.count("A.bloccoCal = false;") >= 3 # cambio sorgente, Carica, Scollega
+
+
+def test_attiva_calibrazione_con_scelta_e_conferma():
+ # #93: sotto la calibrata una combo e CONFERMA; la casella cambia nome
+ for i in ('id="sceltaCal"', 'id="confermaCal"', 'id="msgSceltaCal"'):
+ assert i in PAGINA, i
+ assert "Attiva calibrazione" in PAGINA and ">CONFERMA" in PAGINA
+ assert "Mostra immagine calibrata" not in PAGINA
+ assert PAGINA.index('id="riquadroCal"') < PAGINA.index('id="sceltaCal"') < PAGINA.index('id="confermaCal"') < PAGINA.index('id="msgSceltaCal"')
+ # il gestore di CONFERMA usa PUT calibrazione-attiva (via caricaNome) o DELETE calibration (via scollega), messaggio sotto la calibrata
+ gestore = PAGINA[PAGINA.index('$("confermaCal").onclick'):]
+ gestore = gestore[:gestore.index("};")]
+ assert 'caricaNome(nome, "msgSceltaCal")' in gestore and 'scollega("msgSceltaCal")' in gestore
+ assert 'method: "PUT"' in PAGINA and "/camera/calibrazione-attiva" in PAGINA
+ assert 'method: "DELETE"' in PAGINA and '"/camera/calibration?source="' in PAGINA
+ # voce vuota «nessuna», suffisso di risoluzione, combo ridisegnata con l'elenco e spenta senza sorgente
+ assert "— nessuna (non calibrata) —" in PAGINA and "— risoluzione diversa" in PAGINA
+ assert "disegnaSceltaCal();" in PAGINA and "= !$(\"sorgente\").value" in PAGINA