// A06.02: interfícies gràfiques

Primers passos amb Qt (II)

Números aleatoris, imatges, una casella i el teclat; tres ginys dins d'un; un diàleg, una finestra principal amb menú i la manera de cridar-los. El que cal per fer els deu exercicis de l'A06.02.

El que escrius (previsio.cpp)
void Previsio::mostra()
{
    if (m_icona)
        ui->lbText->setPixmap(m_icones.at(m_index));
    else
        ui->lbText->setText(m_noms.at(m_index));
}
El que veu l'usuari
Previsió del temps
pluja
Icona
Nova previsió (amagat)

Amb la casella marcada, es veu la imatge; desmarcada, el nom.

Un mateix exemple durant tota la pàgina: la previsió del temps

Per no donar-te les solucions de l'A06.02, els exemples no parlen de llenguatges de programació sinó d'una previsió del temps: una etiqueta amb «sol», «núvol», «pluja» o «neu» (o la seva icona), una casella i un botó. Cada bloc és el que necessites per a un o dos exercicis. Els noms canvien, l'estructura és la mateixa:

A l'enunciatAquíQuè és
Ex00 (ex01 a ex06)Previsioel giny d'una ciutat: etiqueta, casella i botó
btAleat, lbText, cbImatgebtNova, lbText, cbIconael botó, l'etiqueta i la casella
Ex07 (QWidget)Ciutatstres Previsio i un botó que les actualitza
Ex08 (QDialog)Ajustosun diàleg que conté un Ciutats
Ex09 (QMainWindow)Finestrauna finestra amb menú que conté un Ciutats

A l'examen has d'usar els noms que et demanen (Ex00…). A la pàgina hi ha noms parlants perquè es llegeixin sols. Tot el codi d'aquesta pàgina és Qt 6 i CMake, i s'ha compilat i provat.

Un giny propi (ex01)

Ja saps fer una classe amb el Designer (introQt, bloc 3). Aquí és el mateix: un projecte nou de tipus Qt Widgets Application amb CMake, una classe amb el nom amb majúscula inicial i QWidget de base, i el disseny.

El disseny: un layout vertical amb tres coses

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Previsio</class>
 <widget class="QWidget" name="Previsio">
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="lbText">
     <property name="font"><font><pointsize>24</pointsize></font></property>
     <property name="text"><string>Prem el botó</string></property>
     <property name="alignment"><set>Qt::AlignCenter</set></property>
    </widget>
   </item>
   <item>
    <widget class="QCheckBox" name="cbIcona">
     <property name="text"><string>Icona</string></property>
     <property name="checked"><bool>true</bool></property>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item><spacer name="horizontalSpacer"><property name="orientation"><enum>Qt::Horizontal</enum></property></spacer></item>
     <item><widget class="QPushButton" name="btNova"><property name="text"><string>Nova previsió</string></property></widget></item>
     <item><spacer name="horizontalSpacer_2"><property name="orientation"><enum>Qt::Horizontal</enum></property></spacer></item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>
  • Un QVBoxLayout conté, de dalt a baix: l'etiqueta lbText, la casella cbIcona i una fila horitzontal.
  • La fila és un QHBoxLayout amb spacer — botó — spacer: els dos espais elàstics centren el botó. És l'estructura que es veu a l'arbre d'objectes de l'enunciat (ex01).
  • Els objectName importen: són els noms de ui->lbText i dels slots on_btNova_clicked. Al constructor, setWindowTitle(...) posa el títol de la finestra.

Números i llistes (ex02, ex03)

El botó ha de sortejar alguna cosa i escriure-la a l'etiqueta. Són tres peces: un generador, una manera de convertir-ho en text i, si vols noms en lloc de números, una llista.

Un número aleatori

void Previsio::on_btNova_clicked()
{
    int graus;

    graus = QRandomGenerator::global()->bounded(-5, 36);
    ui->lbText->setText(QString("%1 °C").arg(graus));
}
  • QRandomGenerator::global()->bounded(a, b) dona un enter de a fins a b - 1: el límit superior no s'inclou. Per a un dau, bounded(1, 7). No cal sembrar-lo.
  • QString("%1 °C").arg(graus): %1 és un forat que arg omple amb el número. Amb %1 i %2 i .arg(a).arg(b) se'n poden posar diversos.
  • setText escriu el text a l'etiqueta.
  • Les captures de codi de l'enunciat fan servir qsrand(), qrand() i trUtf8(), que no existeixen a Qt 6: no compilen. Al pas a pas «Número aleatori» i a «Troba l'error» ho pots veure.

Una llista de noms

    m_noms << "sol" << "núvol" << "pluja" << "neu";

void Previsio::on_btNova_clicked()
{
    int i;

    i = QRandomGenerator::global()->bounded(0, m_noms.size());
    ui->lbText->setText(m_noms.at(i));
}
  • QStringList és una llista de cadenes; << hi afegeix elements i at(i) en llegeix un. Els índexs comencen a 0.
  • El generador dona l'índex, no el valor: bounded(0, m_noms.size()) és 0, 1, 2 o 3 (size() és 4 i no s'inclou).
  • Guarda la llista com a atribut de la classe (m_noms) i omple-la al constructor, una sola vegada.

Imatges i recursos (ex04)

Perquè les imatges viatgin dins de l'executable (i no s'hagin de copiar al costat), Qt les converteix en codi C++. Aquesta part és molt fàcil d'oblidar, i si t'oblides no dona cap error.

Tres passos

  1. Un fitxer .qrc (al Creator, Add New ▸ Qt ▸ Qt Resource File) que llista les imatges. El prefix i el nom formen la ruta: :/img + icones/sol.png.
    <RCC>
        <qresource prefix="/img">
            <file>icones/sol.png</file>
            <file>icones/nuvol.png</file>
            <file>icones/pluja.png</file>
            <file>icones/neu.png</file>
        </qresource>
    </RCC>
  2. Activar rcc al CMakeLists.txt. qt_standard_project_setup() només activa AUTOMOC i AUTOUIC; falta AUTORCC i el .qrc ha d'estar a qt_add_executable:
    qt_standard_project_setup()
    
    set(CMAKE_AUTORCC ON)      # sense això, el .qrc no es converteix en codi
    
    qt_add_executable(previsio
        main.cpp
        previsio.cpp
        previsio.h
        previsio.ui
        icones.qrc             # el fitxer de recursos, llistat com un altre fitxer
    )
  3. Carregar-les amb QPixmap i una ruta que comença per :/, i posar-les a l'etiqueta amb setPixmap:
        m_icones << QPixmap(":/img/icones/sol.png")
                 << QPixmap(":/img/icones/nuvol.png")
                 << QPixmap(":/img/icones/pluja.png")
                 << QPixmap(":/img/icones/neu.png");
    
    void Previsio::on_btNova_clicked()
    {
        int i;
    
        i = QRandomGenerator::global()->bounded(0, m_icones.size());
        ui->lbText->setPixmap(m_icones.at(i));
    }
  • Un QPixmap és un valor lleuger (comparteix internament les dades): es guarda en un QList<QPixmap> sense new ni delete.
  • setPixmap substitueix el text de l'etiqueta, i setText substitueix la imatge: no cal esborrar res abans.
  • Sense AUTORCC el programa compila, s'executa i l'etiqueta queda en blanc, sense cap missatge. Prova-ho al pas a pas «Imatge i recursos».

Estat i casella (ex05)

La casella decideix si l'etiqueta mostra la imatge o el nom, i el botó sorteja una previsió nova. Si cada slot escriu directament a l'etiqueta, les combinacions es multipliquen. La solució és guardar l'estat i tenir una única funció que dibuixa a partir d'ell.

Dos valors i una funció

void Previsio::on_btNova_clicked()
{
    novaPrevisio();
}

void Previsio::on_cbIcona_toggled(bool activa)
{
    m_icona = activa;
    mostra();
}

void Previsio::novaPrevisio()
{
    m_index = QRandomGenerator::global()->bounded(0, m_noms.size());
    mostra();
}

void Previsio::mostra()
{
    if (m_icona)
        ui->lbText->setPixmap(m_icones.at(m_index));
    else
        ui->lbText->setText(m_noms.at(m_index));
}
  • L'estat de la finestra són dos valors: m_index (l'últim sorteig) i m_icona (la casella). No hi ha res més.
  • mostra() és l'única funció que escriu a l'etiqueta. El botó canvia m_index i crida mostra(); la casella canvia m_icona i crida mostra(). Així, en tornar a marcar la casella, hi surt la imatge del darrer sorteig, sense cap codi extra.
  • Els slots són funcions privades (private slots:) amb el nom on_<objectName>_<senyal>, com sempre.

Quin senyal de la casella?

SenyalRepDes de
toggled(bool)un bool: marcada o nototes les versions de Qt
checkStateChanged(Qt::CheckState)Qt::Checked, Qt::Unchecked o Qt::PartiallyCheckedQt 6.7 (és el que et ofereix el Creator 6.9 al Go to slot…)
stateChanged(int)un enterantic; deprecat des de la 6.9

Aquests exemples fan servir toggled(bool) perquè és el més simple (bool activa) i va a totes les versions. Si el Creator et genera on_cbIcona_checkStateChanged(Qt::CheckState estat), és equivalent: m_icona = (estat == Qt::Checked);. Però en un kit anterior a la 6.7 aquest nom no es connectaria (mira «Si surt un error»).

El teclat (ex06)

Fins ara tot venia d'un senyal d'un botó. Una tecla és un esdeveniment: Qt el crida a una funció virtual de QWidget, i tu la substitueixes. Ho has vist al repàs de classes: virtual i override.

keyPressEvent

void Previsio::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Plus)
        ui->btNova->setVisible(true);
    else if (event->key() == Qt::Key_Minus)
        ui->btNova->setVisible(false);
    else
        QWidget::keyPressEvent(event);
}
  • Es declara a protected: amb override, i cal #include <QKeyEvent>. Escrit exactament així: qualsevol errada al nom i override ho diu.
  • event->key() és un codi (Qt::Key_Plus, Qt::Key_Minus…) que es compara amb ==. setVisible(true) mostra un giny i setVisible(false) l'amaga.
  • «Per defecte ha de sortir sense el botó»: al constructor, ui->btNova->setVisible(false);.
  • El darrer else és important. QWidget::keyPressEvent(event) marca l'esdeveniment com a ignorat, i així Qt l'ofereix a qui toqui (per exemple, a un diàleg que vol tancar-se amb l'Esc). Si un esdeveniment no s'ignora, es considera atès.

A qui arriba la tecla?

La tecla va primer al giny que té el focus (per exemple, la casella). La casella no en fa res i l'ignora; Qt l'ofereix aleshores al seu pare, el Previsio, i el teu keyPressEvent la rep. Per això funciona encara que el focus estigui en un fill. Però només arriba als pares del giny amb focus: al bloc següent hi ha tres Previsio i només un tindrà el focus.

Si el «+» no et respon, comprova que la finestra té el focus (fes-hi clic) i, mentre proves, pots comparar amb una altra tecla, per exemple Qt::Key_A.

Tres ginys en un (ex07)

A introQt ja has vist un giny dins d'un altre. Aquí n'hi ha tres, tots de la mateixa classe Previsio, i un botó que els actualitza alhora. La novetat és que un senyal es connecta a tres slots.

ciutats.cpp

#include "ciutats.h"
#include "ui_ciutats.h"
#include "previsio.h"

Ciutats::Ciutats(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Ciutats)
{
    ui->setupUi(this);
    setWindowTitle("Tres ciutats");

    m_bcn = new Previsio;
    m_gir = new Previsio;
    m_lle = new Previsio;

    ui->vlEsq->addWidget(m_bcn);
    ui->vlCen->addWidget(m_gir);
    ui->vlDre->addWidget(m_lle);

    connect(this, &Ciutats::actualitzaTot, m_bcn, &Previsio::novaPrevisio);
    connect(this, &Ciutats::actualitzaTot, m_gir, &Previsio::novaPrevisio);
    connect(this, &Ciutats::actualitzaTot, m_lle, &Previsio::novaPrevisio);
    connect(ui->btTot, &QPushButton::clicked, this, &Ciutats::actualitza);
}

Ciutats::~Ciutats()
{
    delete ui;
}

void Ciutats::actualitza()
{
    emit actualitzaTot();
}
  • Es creen tres Previsio amb new, sense pare, i addWidget les col·loca a cada layout buit i els dona el pare (el Ciutats): ja no cal cap delete.
  • actualitzaTot() és un senyal propi, sense implementació (ho fa moc). Es connecta tres cops, un per ciutat, amb novaPrevisio(). Quan s'emet, els tres slots s'executen en l'ordre en què s'han connectat (bcn, gir, lle).
  • El botó no toca les ciutats directament: crida actualitza(), que només emet el senyal. Així, altres parts del programa (per exemple, un menú) poden fer el mateix cridant actualitza(), sense saber res de les ciutats.
  • novaPrevisio() és un slot públic de Previsio (public slots:), perquè es pot connectar des de fora.

La trampa de la llavor

Una solució freqüent és posar srand(time(NULL)) al constructor de Previsio. Les tres ciutats es construeixen dins del mateix segon, reben la mateixa llavor i el primer rand() és el mateix: en arrencar, les tres previsions són iguals. Amb QRandomGenerator::global() no cal sembrar res. Passa'l pas a pas amb l'opció «srand() a cada constructor».

Diàlegs (ex08)

Un diàleg és una finestra petita que et demana una cosa i espera la resposta. Al Designer, en crear la classe tria la plantilla Dialog with Buttons Bottom: et dona un QDialog amb un QDialogButtonBox (els botons «D'acord» i «Cancel·la») ja connectat.

Un diàleg que conté un giny

#include "ajustos.h"
#include "ui_ajustos.h"
#include "ciutats.h"

Ajustos::Ajustos(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Ajustos)
{
    ui->setupUi(this);
    setWindowTitle("Ajustos");

    ui->vlContingut->addWidget(new Ciutats);
}

Ajustos::~Ajustos()
{
    delete ui;
}
  • Igual que a la finestra anterior: un layout buit (vlContingut) i un addWidget que hi posa un Ciutats.
  • Els botons ja fan «el que és típic»: al .ui hi ha dues connexions, accepted()accept() i rejected()reject(). No cal escriure cap slot.
  • Modal: exec() mostra el diàleg i no torna fins que es tanca; retorna QDialog::Accepted (1) o QDialog::Rejected (0). L'Esc i la X de la finestra també són «cancel·la».
Ajustos dlg;

if (dlg.exec() == QDialog::Accepted)
    qDebug() &lt;&lt; "L'usuari ha acceptat";

Finestra principal, menú i diàleg (ex09, ex10)

Una QMainWindow és una finestra amb una barra de menú, un giny central i, si vols, una barra d'estat. El giny central és un QWidget amb un layout: el codi hi posa el Ciutats, com a tot arreu.

finestra.cpp

#include "finestra.h"
#include "ui_finestra.h"
#include "ciutats.h"
#include "ajustos.h"

Finestra::Finestra(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::Finestra)
{
    ui->setupUi(this);
    setWindowTitle("Previsió del temps");

    m_ciutats = new Ciutats;
    ui->vlCentral->addWidget(m_ciutats);
}

Finestra::~Finestra()
{
    delete ui;
}

void Finestra::on_actionActualitza_triggered()
{
    m_ciutats->actualitza();
}

void Finestra::on_actionSurt_triggered()
{
    close();
}

void Finestra::on_actionObreDialeg_triggered()
{
    Ajustos dlg(this);

    dlg.exec();
}
  • El menú es dibuixa al Designer: escrius «Opcions» a la barra de dalt i, a sota, «Actualitza» i «Surt». Cada element és una QAction amb el seu objectName (actionActualitza…). Un & davant d'una lletra en fa l'accés ràpid, i «Ctrl+U» és una drecera.
  • El senyal d'una acció és triggered(): el slot per nom és on_actionActualitza_triggered. Aquí només crida m_ciutats->actualitza(): el menú fa el mateix que el botó sense duplicar codi, perquè els dos criden la mateixa funció.
  • close() tanca la finestra. Com que era l'última, l'aplicació acaba (a.exec() retorna). QApplication::quit() ho fa directament, encara que hi hagi altres finestres obertes.
  • Cridar un diàleg (ex10): Ajustos dlg(this); crea el diàleg a la pila amb la finestra com a pare (es mostra centrat sobre ella) i dlg.exec() el manté obert. En acabar la funció, dlg es destrueix sol.
  • Si en lloc d'exec() fas dlg.show() amb el diàleg a la pila, no bloqueja: la funció acaba a l'instant i el diàleg desapareix (mira «Troba l'error»).

Si surt un error

Molts errors d'aquesta unitat són silenciosos. Aquests són els que surten de veritat en provar el codi de la pàgina.

Què veusQuanQuè vol dir

Tria un exemple i avança'l pas a pas. A l'esquerra veus el codi i l'estat; a la dreta, la finestra i la traça: qui crida qui, i quins senyals s'emeten. Els resultats (el que es veu a cada finestra, el valor que retorna exec()…) s'han comprovat executant els programes de veritat amb Qt 6.4. Als camps de sobre el codi hi pots posar els valors que «surten» del generador aleatori.

Codi (línia acabada d'executar)
Estat
OnValor
La finestra
Traça de crides

⚡ senyal · ▸ funció teva · ▹ funció de Qt · el sagnat indica que la crida es fa dins de l'anterior.

A06.02: els deu exercicis

Cada targeta té l'enunciat, el resultat esperat (les captures són de l'enunciat), què necessites de la teoria i dues pistes que pots obrir d'una en una. Recorda que el codi que surt en algunes captures de l'enunciat és de Qt 5 i a Qt 6 no compila (qsrand, qrand, trUtf8): fes servir QRandomGenerator.

1. El giny inicial (ex01)

Desenvolupa el projecte ex01 (QWidget) amb els noms de classes i arxius proposats: classe Ex00, un QLabel lbText amb el text «Prem botó» i un QPushButton btAleat («Número aleatori») centrat a sota entre dos spacers.

Captura de l'enunciat, exercici 1
Disseny esperat i arbre d'objectes de l'enunciat.
Necessites: un giny propi
Pista 1

Al Creator, el nom de la classe (amb majúscula: Ex00) i la classe base QWidget es trien a l'assistent. Un layout vertical, i dins una etiqueta i un layout horitzontal.

Pista 2

La fila del botó és spacer, botó, spacer dins d'un QHBoxLayout. Comprova els objectName: han de coincidir amb l'enunciat.

2. Un número aleatori (ex02)

Fes que surti un número aleatori entre l'1 i el 6 quan es premi el botó «Número aleatori».

Captura de l'enunciat, exercici 2
Resultat esperat.
Necessites: números i llistes
Pista 1

El límit superior de bounded no s'inclou: quin número has de posar perquè el 6 pugui sortir?

Pista 2

L'enunciat mostra trUtf8("%1").arg(1+qrand()%6), que no compila a Qt 6. Substitueix-ho per QString::number(...) o QString("%1").arg(...).

3. Un llenguatge de programació (ex03)

Fes que surti el nom d'un llenguatge de programació de la llista C++, C, Java, Python, PHP, Lua.

Captura de l'enunciat, exercici 3
Resultat esperat.
Necessites: números i llistes
Pista 1

Un QStringList com a atribut de la classe, omplert al constructor.

Pista 2

El generador ha de donar un índex: bounded(0, llista.size()).

4. La imatge del llenguatge (ex04)

Com a evolució d'ex03, presenta la imatge icònica del llenguatge de programació (les imatges van adjuntes a l'examen).

Captura de l'enunciat, exercici 4
Resultat esperat.
Necessites: imatges i recursos
Pista 1

Un fitxer .qrc llistat a qt_add_executable i set(CMAKE_AUTORCC ON). Comprova la ruta: prefix + nom del fitxer.

Pista 2

Si no surt res, el programa no dona cap error: repassa AUTORCC i la ruta. Amb un QList<QPixmap> no cal cap new.

5. Una casella (ex05)

Afegeix una casella de selecció perquè aparegui la imatge o el nom del llenguatge de programació.

Captura de l'enunciat, exercici 5
Desmarcada: el nom.
Captura de l'enunciat, exercici 5
Marcada: la imatge.
Necessites: estat i casella
Pista 1

Guarda dos valors com a atributs (l'índex del sorteig i si la casella és marcada) i fes una única funció que dibuixi l'etiqueta.

Pista 2

El botó i la casella només canvien l'estat i criden aquesta funció. Amb el Creator 6.9, el Go to slot… et proposarà checkStateChanged; toggled(bool) és equivalent i més simple.

6. Les tecles + i - (ex06)

Fes que en prémer la tecla «+» hi aparegui el botó «Número aleatori» i en prémer «-» s'amagui. Per defecte ha de sortir sense el botó.

Captura de l'enunciat, exercici 6
Sense el botó (per defecte).
Captura de l'enunciat, exercici 6
Amb el botó, després de prémer «+».
Necessites: el teclat
Pista 1

Substitueix keyPressEvent(QKeyEvent *event) amb override i compara event->key() amb Qt::Key_Plus i Qt::Key_Minus.

Pista 2

L'estat inicial s'aconsegueix amb setVisible(false) al constructor. Per a les altres tecles, crida QWidget::keyPressEvent(event).

7. Tres ginys en un (ex07)

Desenvolupa el projecte ex07 (QWidget) encastant tres ginys ex06. En prémer el botó «Aleatori» els tres ginys s'han d'actualitzar.

Captura de l'enunciat, exercici 7
Resultat esperat.
Necessites: tres ginys en un
Pista 1

Un layout buit per a cada giny, new Ex00 i addWidget per a cada un. El slot que sorteja ha de ser public.

Pista 2

Un senyal propi connectat als tres slots, o el clicked() del botó connectat tres vegades. Si les tres imatges surten iguals en arrencar, revisa on has posat srand.

8. Un diàleg (ex08)

Desenvolupa el projecte ex08 (QDialog) encastant el giny ex07. Els dos botons inferiors del diàleg han de retornar el que és típic en un diàleg.

Captura de l'enunciat, exercici 8
Resultat esperat.
Pista 1

Crea la classe amb la plantilla Dialog with Buttons Bottom: els botons ja venen connectats a accept() i reject().

Pista 2

«Retornar el típic» és el resultat de exec(): QDialog::Accepted o QDialog::Rejected. Prova-ho amb un qDebug().

9. Finestra principal amb menú (ex09)

Desenvolupa el projecte ex09 (QMainWindow) encastant el giny ex07. El menú «Opcions» conté dos ítems. Quan se selecciona «Aleatori» al menú fa el mateix que prémer el botó «Aleatori», i si se selecciona «Surt» el programa s'acaba.

Captura de l'enunciat, exercici 9
Resultat esperat.
Necessites: finestra principal
Pista 1

Una QMainWindow necessita un giny central amb un layout: hi poses el giny ex07 igual que als altres exercicis.

Pista 2

Fes que el giny ex07 tingui un slot públic que faci el mateix que el botó, i crida'l des del triggered() de l'acció. Per sortir, close() o QApplication::quit().

10. Cridar el diàleg (ex10)

Com a evolució d'ex09, fes que una opció del menú («Diàleg ▸ Crida diàleg») faci una crida al diàleg ex08.

Captura de l'enunciat, exercici 10
Resultat esperat.
Pista 1

Un slot per a l'acció que crea el diàleg a la pila amb this com a pare i crida exec().

Pista 2

Si el diàleg apareix i desapareix a l'instant, potser has fet servir show() amb el diàleg a la pila.

Com explicar cada solució

L'enunciat demana fer «les captures de pantalla que cregueu convenient per a explicar la solució proposada». Una manera de fer-ho bé, per a cada exercici:

  • una captura de la finestra funcionant;
  • l'arbre d'objectes del Designer (ginys, objectName, layouts);
  • els senyals i slots que has fet servir, i qui els connecta;
  • un error que t'hagi sortit i com l'has resolt.

Entrena't: què mostra la finestra?

Llegeix les accions de l'usuari i pensa l'estat final de la finestra: què hi ha a l'etiqueta, si la casella és marcada i si el botó es veu. Cada cop surt un escenari diferent.


    

Troba l'error

Vuit casos, tots reals. Abans d'obrir cada targeta decideix: compila? què fa en executar-lo? Després compara amb el que passa de veritat.