tests: controllo statico che le etichette dei tasti ci stiano
Le etichette si leggono da MainPage.qml e dal catalogo, la geometria dai numeri misurati sul telefono: se un'etichetta si allunga (o l'icona si ingrandisce) il controllo lo segnala prima della build. Su "Commands" fallisce, come deve.
This commit is contained in:
@@ -165,6 +165,7 @@ nessuno. Impiegano millisecondi e dicono il file di cui non sono contenti:
|
|||||||
```sh
|
```sh
|
||||||
python3 tests/qml_check_bindings.py qml # nessuna proprietà assegnata due volte
|
python3 tests/qml_check_bindings.py qml # nessuna proprietà assegnata due volte
|
||||||
python3 tests/qml_focus_check.py # un tocco apre solo quello che ha toccato
|
python3 tests/qml_focus_check.py # un tocco apre solo quello che ha toccato
|
||||||
|
python3 tests/label_fit_check.py # le etichette dei tasti ci stanno
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stato
|
## Stato
|
||||||
@@ -234,6 +235,7 @@ tests/core_test.cpp verifiche del nucleo (headless)
|
|||||||
tests/ui_contract_check.py chiavi dei QML contro quelle del bridge (bridge attivo)
|
tests/ui_contract_check.py chiavi dei QML contro quelle del bridge (bridge attivo)
|
||||||
tests/qml_check_bindings.py assegnazioni duplicate di proprieta' nei QML
|
tests/qml_check_bindings.py assegnazioni duplicate di proprieta' nei QML
|
||||||
tests/qml_focus_check.py un tocco apre solo le regolazioni di quell'oggetto
|
tests/qml_focus_check.py un tocco apre solo le regolazioni di quell'oggetto
|
||||||
|
tests/label_fit_check.py le etichette dei tasti ci stanno accanto all'icona
|
||||||
bridge/aira_bridge.py servizio HTTP/1.1 + JSON verso il cloud (gRPC)
|
bridge/aira_bridge.py servizio HTTP/1.1 + JSON verso il cloud (gRPC)
|
||||||
bridge/setup_creds.py salva le credenziali Aira (file 600)
|
bridge/setup_creds.py salva le credenziali Aira (file 600)
|
||||||
bridge/aira_probe.py sonda di sola lettura: login + stato impianto
|
bridge/aira_probe.py sonda di sola lettura: login + stato impianto
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ milliseconds and name the file they are unhappy about:
|
|||||||
```sh
|
```sh
|
||||||
python3 tests/qml_check_bindings.py qml # no property assigned twice in one object
|
python3 tests/qml_check_bindings.py qml # no property assigned twice in one object
|
||||||
python3 tests/qml_focus_check.py # a tap opens only the object it touched
|
python3 tests/qml_focus_check.py # a tap opens only the object it touched
|
||||||
|
python3 tests/label_fit_check.py # quick-action labels fit the button
|
||||||
```
|
```
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
@@ -248,6 +249,7 @@ tests/core_test.cpp core checks (headless)
|
|||||||
tests/ui_contract_check.py QML keys vs bridge keys (bridge must be running)
|
tests/ui_contract_check.py QML keys vs bridge keys (bridge must be running)
|
||||||
tests/qml_check_bindings.py duplicate property assignments in the QML
|
tests/qml_check_bindings.py duplicate property assignments in the QML
|
||||||
tests/qml_focus_check.py a tap opens only that object's settings
|
tests/qml_focus_check.py a tap opens only that object's settings
|
||||||
|
tests/label_fit_check.py quick-action labels fit next to the icon (measured on a phone)
|
||||||
bridge/ local HTTP bridge -> see bridge/README.md
|
bridge/ local HTTP bridge -> see bridge/README.md
|
||||||
translations/ harbour-aira-it.ts (English is the source language)
|
translations/ harbour-aira-it.ts (English is the source language)
|
||||||
docs/PIANO.md project plan and decisions (Italian)
|
docs/PIANO.md project plan and decisions (Italian)
|
||||||
|
|||||||
+3
-2
@@ -362,8 +362,9 @@ la geometria misurata sul telefono (161 px di tasto, rientri 16, distacco 24)
|
|||||||
le etichette **Assenza**, **Torna**, **Altro** e le inglesi **Away**, **Home**,
|
le etichette **Assenza**, **Torna**, **Altro** e le inglesi **Away**, **Home**,
|
||||||
**More** ci stanno con margine; "Comandi" (8 lettere) no, quindi il tasto dice
|
**More** ci stanno con margine; "Comandi" (8 lettere) no, quindi il tasto dice
|
||||||
"Altro" e la pagina resta "Comandi impianto". Prova ripetibile:
|
"Altro" e la pagina resta "Comandi impianto". Prova ripetibile:
|
||||||
`docs/icona/tasti-alla-misura-vera.png` (script in scratch, geometria nei
|
`tests/label_fit_check.py` (le etichette si leggono dai QML e dal catalogo: se
|
||||||
commenti); il pulsante Silica non espone il carattere, quindi non si può
|
una si allunga il controllo lo segnala) e `docs/icona/tasti-alla-misura-vera.png`
|
||||||
|
per l'occhio. Il pulsante Silica non espone il carattere, quindi non si può
|
||||||
rimpicciolire il testo: l'unica leva è l'icona.
|
rimpicciolire il testo: l'unica leva è l'icona.
|
||||||
|
|
||||||
**Stato attuale (0.6.8).** 85/85 verifiche del nucleo, 48/48 del bridge,
|
**Stato attuale (0.6.8).** 85/85 verifiche del nucleo, 48/48 del bridge,
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Controllo statico: le etichette dei tre tasti delle azioni rapide ci stanno?
|
||||||
|
|
||||||
|
Sul pulsante Silica il carattere non si puo' rimpicciolire (il tipo non espone
|
||||||
|
proprieta' `font`), quindi lo spazio per il testo dipende solo dall'icona. I
|
||||||
|
numeri qui sotto sono MISURATI sullo screenshot di un telefono vero (0.6.7):
|
||||||
|
|
||||||
|
tasto 161 px logici, rientri 16, distacco icona-testo 24
|
||||||
|
icona di Silica senza dichiarazione = 64 px (Theme.iconSizeMedium)
|
||||||
|
-> restavano 41 px di testo: le etichette uscivano tagliate
|
||||||
|
|
||||||
|
Con Theme.iconSizeExtraSmall (32 px) restano 73 px. Il carattere di Sailfish e'
|
||||||
|
piu' stretto di quello usato qui per misurare (DejaVu): dal confronto sul
|
||||||
|
telefono risulta circa 0,78 volte, quindi si misura col fattore di sicurezza.
|
||||||
|
|
||||||
|
Il test legge i testi veri da qml/pages/MainPage.qml e le traduzioni da
|
||||||
|
translations/harbour-aira-it.ts: non ci sono etichette scritte a mano qui.
|
||||||
|
Se cambia la geometria della riga (margini, spazi, altezza) questi numeri vanno
|
||||||
|
rimisurati su uno screenshot nuovo.
|
||||||
|
"""
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
RADICE = pathlib.Path(__file__).resolve().parent.parent
|
||||||
|
TASTO, RIENTRO, DISTACCO = 161.0, 16.0, 24.0
|
||||||
|
ICONA = 32.0 # Theme.iconSizeExtraSmall
|
||||||
|
CARATTERE = 22 # carattere dei pulsanti Silica
|
||||||
|
FATTORE = 0.78 # Sailfish (Nokia Pure) rispetto a DejaVu
|
||||||
|
FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
|
||||||
|
|
||||||
|
|
||||||
|
def testi_dei_tasti(qml: str):
|
||||||
|
"""I testi dei tasti che hanno un'icona, nell'ordine in cui appaiono."""
|
||||||
|
fuori = []
|
||||||
|
for blocco in re.finditer(r"Button\s*\{(.*?)\n\s*\}", qml, re.S):
|
||||||
|
corpo = blocco.group(1)
|
||||||
|
if "../icons/quick-" not in corpo:
|
||||||
|
continue
|
||||||
|
riga = re.search(r"text:\s*(.+)", corpo)
|
||||||
|
if not riga:
|
||||||
|
continue
|
||||||
|
fuori.extend(re.findall(r'qsTr\("([^"]+)"\)', riga.group(1)))
|
||||||
|
return fuori
|
||||||
|
|
||||||
|
|
||||||
|
def traduzioni(ts: str):
|
||||||
|
coppie = {}
|
||||||
|
for blocco in re.finditer(r"<source>(.*?)</source>\s*<translation[^>]*>(.*?)</translation>",
|
||||||
|
ts, re.S):
|
||||||
|
coppie[blocco.group(1)] = blocco.group(2)
|
||||||
|
return coppie
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
from PIL import ImageFont
|
||||||
|
except ImportError:
|
||||||
|
print("salto: manca Pillow (serve solo per misurare il testo)")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
qml = (RADICE / "qml" / "pages" / "MainPage.qml").read_text(encoding="utf-8")
|
||||||
|
ts = (RADICE / "translations" / "harbour-aira-it.ts").read_text(encoding="utf-8")
|
||||||
|
it = traduzioni(ts)
|
||||||
|
sorgenti = testi_dei_tasti(qml)
|
||||||
|
if len(sorgenti) < 3:
|
||||||
|
print("ESITO: trovati %d testi con icona, ne servono 3" % len(sorgenti))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
disponibile = TASTO - 2 * RIENTRO - ICONA - DISTACCO
|
||||||
|
problema = 0
|
||||||
|
f = ImageFont.truetype(FONT, CARATTERE)
|
||||||
|
|
||||||
|
def largo(testo):
|
||||||
|
# Pillow >= 9.2 ha getlength; prima si misurava con getsize
|
||||||
|
if hasattr(f, "getlength"):
|
||||||
|
return f.getlength(testo)
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
return ImageDraw.Draw(Image.new("L", (1, 1))).textlength(testo, font=f)
|
||||||
|
|
||||||
|
prova = [("it", [it.get(s, s) for s in sorgenti]), ("en", sorgenti)]
|
||||||
|
print("spazio per il testo: %.0f px (tasto %.0f, icona %.0f)" % (disponibile, TASTO, ICONA))
|
||||||
|
for lingua, etichette in prova:
|
||||||
|
for t in etichette:
|
||||||
|
serve = largo(t) * FATTORE
|
||||||
|
esito = "ok" if serve <= disponibile else "NON CI STA"
|
||||||
|
if serve > disponibile:
|
||||||
|
problema += 1
|
||||||
|
print(" %s %-9s %5.0f px %s" % (lingua, t, serve, esito))
|
||||||
|
if problema:
|
||||||
|
print("ESITO: %d etichette non ci stanno: accorciarle o ridurre l'icona" % problema)
|
||||||
|
return 1
|
||||||
|
print("ESITO: tutte le etichette ci stanno")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user