0.7.1: i tasti delle azioni rapide dichiarano la loro altezza

In Silica il tasto piglia l'altezza dal tema (itemSizeExtraSmall, ~56 px) e non
dal contenuto: con icona e etichetta (~110) il testo usciva dalla scatola e la
riga di contorno della colonna finiva sopra le etichette. Ora height:
Theme.itemSizeMedium. Aggiunto il controllo in tests/label_fit_check.py e il
registro di posizione e altezza di ogni riga della Home.
This commit is contained in:
Carlo
2026-10-11 12:26:20 +02:00
parent e8937dc1ea
commit 2b397b6c3a
7 changed files with 100 additions and 13 deletions
+7 -1
View File
@@ -58,7 +58,7 @@ CREDS = BASE / "aira.json"
TOKENS = BASE / "aira_tokens.json" TOKENS = BASE / "aira_tokens.json"
POOL_CFG = BASE / "aira_config.json" POOL_CFG = BASE / "aira_config.json"
VERSION = "0.7.0" VERSION = "0.7.1"
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
@@ -693,6 +693,12 @@ def boost_restanti(st: dict) -> int:
quando = datetime.fromisoformat(fine.replace("Z", "+00:00")) quando = datetime.fromisoformat(fine.replace("Z", "+00:00"))
except ValueError: except ValueError:
return 0 return 0
# L'impianto manda la fine del boost ora con il fuso, ora senza (ora
# locale): senza, va letta nel fuso dell'IMPIANTO, altrimenti la
# sottrazione con "adesso" (che il fuso ce l'ha) solleva TypeError e
# /summary risponde 502 -- l'app resta senza dati ("Nessun dato").
if quando.tzinfo is None:
quando = quando.replace(tzinfo=PLANT_TZ)
return max(0, int((quando - datetime.now(timezone.utc)).total_seconds())) return max(0, int((quando - datetime.now(timezone.utc)).total_seconds()))
+12
View File
@@ -231,6 +231,18 @@ check("boost in corso: restano ~1200 secondi",
check("boost finito: zero secondi", check("boost finito: zero secondi",
b.boost_restanti({"hot_water_boost": {"end_time": "2026-01-01T00:00:00Z"}}) == 0) b.boost_restanti({"hot_water_boost": {"end_time": "2026-01-01T00:00:00Z"}}) == 0)
check("boost mai avviato: zero secondi", b.boost_restanti({}) == 0) check("boost mai avviato: zero secondi", b.boost_restanti({}) == 0)
# L'impianto puo' mandare la fine del boost SENZA fuso (ora locale). E' la forma
# che ha fatto rispondere 502 a /summary: la sottrazione con "adesso" (che il
# fuso ce l'ha) solleva TypeError, e l'app resta senza dati ("Nessun dato").
# Il conto alla rovescia deve reggere entrambe le forme.
_fine_naive = (datetime.now(b.PLANT_TZ) + timedelta(seconds=1200)).strftime("%Y-%m-%dT%H:%M:%S")
check("boost con fine senza fuso (ora dell'impianto)",
abs(b.boost_restanti({"hot_water_boost": {"end_time": _fine_naive}}) - 1200) <= 3)
check("riassunto con fine senza fuso (la forma che dava 502)",
(b.summarize({"hot_water_boost": {"end_time": _fine_naive}}) or {}).get("boost_active") is True)
check("boost con fine fuori orario (fuso diverso)",
b.boost_restanti({"hot_water_boost": {"end_time": "2020-01-01T00:00:00"}}) == 0)
riassunto = b.summarize({"hot_water_boost": {"end_time": fra}}) riassunto = b.summarize({"hot_water_boost": {"end_time": fra}})
check("riassunto: dice che il boost e' in corso", riassunto.get("boost_active") is True) check("riassunto: dice che il boost e' in corso", riassunto.get("boost_active") is True)
check("riassunto: porta i secondi del boost", check("riassunto: porta i secondi del boost",
+26 -1
View File
@@ -410,7 +410,32 @@ respinto con 400 **prima** di partire.
4. mentre il boost è in corso si deve vedere un **conto alla rovescia da 30:00 a 4. mentre il boost è in corso si deve vedere un **conto alla rovescia da 30:00 a
00:00**. 00:00**.
## 12. Conseguenze dell'uso reale (0.7.0) ## 12. I tasti delle azioni rapide e la riga che li copriva — v0.7.1
Carlo: *"Quando premo un bottone la riga sotto si disallinea coprendo i
pulsanti"*.
Non era la riga a spostarsi: era il **tasto** a essere piu' basso del suo
contenuto. In Silica `Button` dichiara `height: implicitHeight` e
`implicitHeight: Theme.itemSizeExtraSmall` (~56 px), un'altezza pensata per
un'icona *accanto* a un'etichetta corta. I tre tasti delle azioni rapide hanno
icona **e** etichetta (ingombro visibile ~110-160 px, quello che io avevo
misurato come "tasto 161 px"): il contenuto esce dalla scatola, e la riga
successiva della colonna -- le letture di contorno -- viene messa subito sotto
la scatola, cioe' **sopra** l'etichetta che sporge. Da li' l'impressione che si
"disallinei".
**Come è stato fatto (0.7.1).**
- i tre tasti dichiarano `height: Theme.itemSizeMedium` (`Button.qml` del target
Sailfish letto dentro il motore di build, non indovinato);
- `tests/label_fit_check.py` adesso pretende l'altezza dichiarata su ogni tasto
che porta icona e testo: senza, il tema ne da' ~56 px contro ~110 di
contenuto;
- il registro dell'avvio (dopo un secondo) elenca **posizione e altezza di ogni
riga della Home**, cosi' se la disposizione non torna si legge dal registro
quale riga finisce dove invece di dedurlo da uno screenshot.
## 13. Conseguenze dell'uso reale (0.7.0)
Deciso con l'uso, non in anticipo: Deciso con l'uso, non in anticipo:
+24 -2
View File
@@ -261,6 +261,13 @@ Page {
Button { Button {
id: boostBtn id: boostBtn
width: (parent.width - 2 * parent.spacing) / 3 width: (parent.width - 2 * parent.spacing) / 3
// In Silica il tasto piglia l'altezza dal tema
// (itemSizeExtraSmall ~56 px), ma icona E etichetta ne
// occupano ~110: senza questa riga l'etichetta esce dalla
// scatola e la riga successiva della colonna le finisce
// sopra (segnalato da Carlo: "la riga sotto copre i
// pulsanti"). Vedi tests/label_fit_check.py.
height: Theme.itemSizeMedium
text: qsTr("Boost") text: qsTr("Boost")
// La casella dell'icona la decide il tema: senza, l'Image // La casella dell'icona la decide il tema: senza, l'Image
// prende la misura del file e si mangia il testo. // prende la misura del file e si mangia il testo.
@@ -274,6 +281,7 @@ Page {
Button { Button {
id: assenzaBtn id: assenzaBtn
width: (parent.width - 2 * parent.spacing) / 3 width: (parent.width - 2 * parent.spacing) / 3
height: Theme.itemSizeMedium
// Il pulsante dice cosa succede premendolo, non lo stato. // Il pulsante dice cosa succede premendolo, non lo stato.
// Etichette corte: nel tasto, largo un terzo di schermo, // Etichette corte: nel tasto, largo un terzo di schermo,
// icona e testo devono starci insieme. // icona e testo devono starci insieme.
@@ -302,6 +310,7 @@ Page {
Button { Button {
width: (parent.width - 2 * parent.spacing) / 3 width: (parent.width - 2 * parent.spacing) / 3
height: Theme.itemSizeMedium
// "Comandi" (8 lettere) non ci sta accanto a un'icona in un // "Comandi" (8 lettere) non ci sta accanto a un'icona in un
// terzo di schermo: la pagina resta "Comandi impianto". // terzo di schermo: la pagina resta "Comandi impianto".
text: qsTr("More") text: qsTr("More")
@@ -350,7 +359,20 @@ Page {
Timer { Timer {
id: misura id: misura
interval: 1000 interval: 1000
onTriggered: console.log("harbour-aira home: contenuto", flick.contentHeight, onTriggered: {
"schermo", height) console.log("harbour-aira home: contenuto", flick.contentHeight,
"schermo", height)
// Ogni riga della Home con la sua posizione: se una riga finisce
// addosso a un'altra, qui si legge quale e di quanto. E' la riga da
// mandare al primo avvio se la disposizione non torna.
for (var i = 0; i < col.children.length; ++i) {
var r = col.children[i]
if (r.height === undefined)
continue
console.log("harbour-aira riga", i, "y", Math.round(r.y),
"h", Math.round(r.height),
r.text !== undefined ? JSON.stringify(r.text) : "")
}
}
} }
} }
+8 -1
View File
@@ -1,6 +1,6 @@
Name: harbour-aira Name: harbour-aira
Summary: Control your Aira heat pump through a local bridge Summary: Control your Aira heat pump through a local bridge
Version: 0.7.0 Version: 0.7.1
Release: 1 Release: 1
Group: Qt/Qt Group: Qt/Qt
License: MIT License: MIT
@@ -37,6 +37,13 @@ Address and token of the bridge live in the app settings (AppConfigLocation);
no personal data is stored in the binary. no personal data is stored in the binary.
%changelog %changelog
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.7.1-1
- i tre tasti delle azioni rapide dichiarano la loro altezza: in Silica il tasto
la piglia dal tema (Theme.itemSizeExtraSmall, ~56 px) e non dal contenuto,
quindi con icona E etichetta il testo (~110 px) usciva dalla scatola e la riga
di contorno finiva sopra le etichette (segnalato: "la riga sotto si disallinea
coprendo i pulsanti").
- il registro dell'avvio elenca posizione e altezza di ogni riga della Home.
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.7.0-1 * Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.7.0-1
- assenza con le date: popup con inizio e fine (DatePickerDialog, componenti - assenza con le date: popup con inizio e fine (DatePickerDialog, componenti
verificati nella guida Silica dell'SDK). Il bridge manda inizio e fine come verificati nella guida Silica dell'SDK). Il bridge manda inizio e fine come
+2 -2
View File
@@ -21,7 +21,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
app->setOrganizationName(QStringLiteral("harbour")); app->setOrganizationName(QStringLiteral("harbour"));
app->setApplicationName(QStringLiteral("aira")); app->setApplicationName(QStringLiteral("aira"));
app->setApplicationVersion(QStringLiteral("0.7.0")); app->setApplicationVersion(QStringLiteral("0.7.1"));
Settings settings; Settings settings;
ApiClient api(&settings); ApiClient api(&settings);
@@ -36,7 +36,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
// Prima riga del log: dice subito se il device sta girando la build nuova // Prima riga del log: dice subito se il device sta girando la build nuova
// o una stale (trappola nota del build engine). // o una stale (trappola nota del build engine).
qDebug() << "harbour-aira v0.7.0 build" << __DATE__ << __TIME__; qDebug() << "harbour-aira v0.7.1 build" << __DATE__ << __TIME__;
view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings); view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings);
view->rootContext()->setContextProperty(QStringLiteral("api"), &api); view->rootContext()->setContextProperty(QStringLiteral("api"), &api);
+21 -6
View File
@@ -31,7 +31,7 @@ FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
def testi_dei_tasti(qml: str): def testi_dei_tasti(qml: str):
"""I testi dei tasti che hanno un'icona, nell'ordine in cui appaiono.""" """I tasti con icona: (testo, altezza dichiarata o None), in ordine."""
fuori = [] fuori = []
for blocco in re.finditer(r"Button\s*\{(.*?)\n\s*\}", qml, re.S): for blocco in re.finditer(r"Button\s*\{(.*?)\n\s*\}", qml, re.S):
corpo = blocco.group(1) corpo = blocco.group(1)
@@ -40,7 +40,11 @@ def testi_dei_tasti(qml: str):
riga = re.search(r"text:\s*(.+)", corpo) riga = re.search(r"text:\s*(.+)", corpo)
if not riga: if not riga:
continue continue
fuori.extend(re.findall(r'qsTr\("([^"]+)"\)', riga.group(1))) testo = re.findall(r'qsTr\("([^"]+)"\)', riga.group(1))
if not testo:
continue
alt = re.search(r"^\s*height:\s*(.+)$", corpo, re.M)
fuori.append((testo[0], alt.group(1).strip() if alt else None))
return fuori return fuori
@@ -62,13 +66,24 @@ def main():
qml = (RADICE / "qml" / "pages" / "MainPage.qml").read_text(encoding="utf-8") qml = (RADICE / "qml" / "pages" / "MainPage.qml").read_text(encoding="utf-8")
ts = (RADICE / "translations" / "harbour-aira-it.ts").read_text(encoding="utf-8") ts = (RADICE / "translations" / "harbour-aira-it.ts").read_text(encoding="utf-8")
it = traduzioni(ts) it = traduzioni(ts)
sorgenti = testi_dei_tasti(qml) origine = testi_dei_tasti(qml)
if len(sorgenti) < 3: if len(origine) < 3:
print("ESITO: trovati %d testi con icona, ne servono 3" % len(sorgenti)) print("ESITO: trovati %d testi con icona, ne servono 3" % len(origine))
return 1 return 1
sorgenti = [t for t, _ in origine]
disponibile = TASTO - 2 * RIENTRO - ICONA - DISTACCO
problema = 0 problema = 0
# 1. L'altezza. In Silica il tasto la piglia dal tema
# (Theme.itemSizeExtraSmall, ~56 px) e non dal contenuto: con icona E
# etichetta il contenuto ne occupa ~110, esce dalla scatola, e la riga
# successiva della colonna viene messa sopra la scatola -- cioe' sopra
# l'etichetta che sporge. Chi ha entrambi deve dichiarare l'altezza.
for testo, alt in origine:
if not alt:
print(" %-9s nessuna altezza dichiarata: il tema ne da' ~56 px, il contenuto ~110" % testo)
problema += 1
disponibile = TASTO - 2 * RIENTRO - ICONA - DISTACCO
f = ImageFont.truetype(FONT, CARATTERE) f = ImageFont.truetype(FONT, CARATTERE)
def largo(testo): def largo(testo):