Qt e ereditarietà

di il
9 risposte

Qt e ereditarietà

Ciao a tutti,
innanzitutto spero che in questo forum ci sia qualcuno che conosce Qt, purtroppo con l'inglese non me la cavo benissimo e le risorse in italiano per Qt sono veramente poche.
Sto sviluppando un piccolo programma con le librerie Qt, il tutto per cercare di approfondire sia il C++ che le librerie Qt.
Ho un dubbio però che vorrei esporvi e sentire il vostro parere.
Allora... da quello che ho studiato ho capito che nel C++ conviene fare una classe per ogni compito che deve essere svolto.
Quindi, ora mi trovo con la classe principale (MainWindow) che crea la finestra principale e i menù ai quali sono ovviamente collegati degli slot.
La prima voce di menù è (guarda caso) "Inserisci nuovo" e seguendo il ragionamento sopra, per gestire l'inserimento dei dati dovrei creare una nuova classe.
Il mio dilemma è se sia meglio creare una nuova classe e passare come parametro al costruttore il puntatore alla classe principale (cosa che ho già fatto e funziona) o se sia meglio creare la nuova classe ereditando quella principale (MainWindow).
La seconda soluzione ho provato a metterla in pratica ma non ne sono ancora venuto fuori, e nel frattempo mi è sorto il dubbio che forse non è la soluzione migliore.
Che ne pensate?

Grazie.

Stefano

9 Risposte

  • Re: Qt e ereditarietà

    La classe MainWindow è già ereditata da QMainWindow, basta che aggiungi i tuoi metodi (se quello che devi fare non è troppo complesso). Se vuoi, puoi creare le tue classi, l'importante è che siano figlie di classi Q, se vuoi gestire al meglio l'integrazione con il resto dell'applicazione.
    Suppongo che "inserisci nuovo" voglia aprire un dialogo: con Qt Designer crei la tua finestra di dialogo (che eredita da QDialog), aggiungi i metodi per la gestione dei segnali, crei un metodo salvaDati nella MainWindow che leggi i dati dal dialogo, colleghi gli slot dalla MainWindow... Chiaro che se l'applicazione è troppo complessa conviene spezzettarla in moduli o classi separate.
  • Re: Qt e ereditarietà

    Ciao e per iniziare grazie della risposta.
    Suppongo che "inserisci nuovo" voglia aprire un dialogo: con Qt Designer crei la tua finestra di dialogo (che eredita da QDialog), aggiungi i metodi per la gestione dei segnali, crei un metodo salvaDati nella MainWindow che leggi i dati dal dialogo, colleghi gli slot dalla MainWindow... Chiaro che se l'applicazione è troppo complessa conviene spezzettarla in moduli o classi separate.
    Prima di tutto premetto che per la prima volta ho voluto iniziare a scrivere un programma senza usare il designer ma creando l'interfaccia direttamente da codice, e già questa per me è una grossa novità, ma vedo che con un po' di pazienza riesco a venirne fuori.
    In realtà "Inserisci nuovo" non andrà ad aprire un dialogo ma andrà a creare e visualizzare dei widget nella finestra principale (MainWindow).
    I widget creati e visualizzati saranno un QTab, un QTableWidget per ogni TAB e alcuni QPushButton al di fuori del QTab.
    Poi una volta inseriti i vari dati bisognerà salvarli in un file dopo averli formattati come serve a me.
    Quindi, pensavo per la gestione dell'inserimento ed edit dei dati di creare una classe apposita (correggimi se sbaglio) e per la gestione del salvataggio su file e lettura da file di creare un'ulteriore classe.
    Poi a tutto questo sarà abbinata una comunicazione seriale e quindi altra classe anche per gestire la comunicazione seriale che tra l'altro pensavo di far girare all'interno di un QThread (mai usato finora un QThread).
    Insomma, per me una bella sfida
    La classe MainWindow è già ereditata da QMainWindow, basta che aggiungi i tuoi metodi (se quello che devi fare non è troppo complesso). Se vuoi, puoi creare le tue classi, l'importante è che siano figlie di classi Q, se vuoi gestire al meglio l'integrazione con il resto dell'applicazione.
    E qui mi hai già rimesso in crisi
    Per esempio, la classe che gestirà l'inserimento dei dati di quale classe Q dovrebbe essere figlia visto che non sarà un QDialog?
    La mia tentazione per semplificarmi la vita sarebbe quella di aggiungere tanti metodi nella classe principale spezzettandola semplicemente in tanti file, ma non credo che sia la cosa più corretta da fare.
    Insomma, mi piacerebbe imparare a farle bene queste cose.

    Grazie.

    Stefano
  • Re: Qt e ereditarietà

    Sulla scelta di disegnare i widget via codice posso anche essere d'accordo (alcuni widget non sono facili da disegnare con Qt Designer, poi l'aspetto funzionale a volte lascia a desiderare).
    Le tue classi le puoi fare stand-alone, oppure derivate da QObject, la classe base di Qt. Se nelle tue classi non usi niente di Qt, allora vanno bene stand-alone. Altrimenti devi trovare la classe più opportuna da cui derivare.
    Per l'inserimento dei dati puoi fare un dialog, oppure mettere i widget direttamente in un tab, devi solo creare un metodo tipo MainWindow::LeggiParametri che collegato a qualcosa (pulsante, menu) legga il contenuto dei widget. Per gestire la seriale non credo serva un thread, guarda gli esempi della classe QSerialPort.
  • Re: Qt e ereditarietà

    Grazie mille dell'aiuto.
    Cerco di approfondire
  • Re: Qt e ereditarietà

    E rieccomi, scusatemi se rompo un po', ma voglio capire alcune cose.
    Allora, ho approfondito un po' il discorso e mi sono studiato per bene i layout.
    Visto che ora mi è entrato in testa il concetto che con tanti piccoli widget se ne può fare uno più complesso raggruppandoli, ho pensato di fare in modo che la finestra che voglio creare diventi un unico widget che poi vado ad inserire in MainWindow::CentralWidget e su consiglio di Andrea ho ereditato la mia classe da QWidget (visto che dovrà diventare un widget).
    Quindi leggendo un po' di qua, un po' di la e dormendoci sopra una notte sono riuscito a fare tutto.
    Unico problema è che non riesco a connettere un segnale ad uno slot del mio widget, perchè a runtime ricevo questo errore: QObject::connect: No such slot QWidget::b1Pressed().
    Il problema ce l'ho solo con il connect all'interno della classe inputWindow, perchè quello in MainWindow funziona bene.

    Volevo connettere un pulsante allo slot b1Pressed per capire poi come tornare indietro alla videata precedente, ma mi sono bloccato qui e non sono riuscito a capire anche dalle varie letture e ricerche che ho fatto dov'è l'errore.
    Di seguito il codice che ho scritto (è scritto da cani perchè è come il widget che ho costruito, una grande prova con dentro un guazzabuglio di piccole prove )

    mainwindow.cpp (mainwindows.h non lo metto perchè non ha praticamente nulla se non la definizione dello slot e le solite cose)
    #include "mainwindow.h"
    #include "inputwindow.h"
    #include <QHBoxLayout>
    #include <QPushButton>
    #include <QDebug>
    
    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
    {
        this->setGeometry(200,0,1024,768);
        pb = new QPushButton("Apri", this);
        QObject::connect(pb, SIGNAL(released()), this, SLOT (input()));
    }
    
    MainWindow::~MainWindow() {}
    
    void MainWindow::input()
    {
        qDebug() << "input";
        pb->setVisible(false);
        InputWindow *p = new InputWindow(this);
        this->setCentralWidget(p);
    }
    
    inputwindow.h
    
    #ifndef INPUTWINDOW_H
    #define INPUTWINDOW_H
    
    #include <QVBoxLayout>
    #include <QWidget>
    #include <QTableWidgetItem>
    #include <QCheckBox>
    #include <QComboBox>
    
    class InputWindow : public QWidget
    {
    public:
        InputWindow(QWidget *parent);
    
    private slots:
        void    b1Pressed();
    };
    
    class STable : public QWidget
    {
    public:
         STable(QWidget *parent = nullptr);
    
    private:
         QTableWidgetItem *RightAlignedItem(QString value);
         QTableWidgetItem *CenterAlignedItem(QString value);
         QCheckBox        *SetLineWritable(void);
         QComboBox        *SetComboBox(void);
    };
    
    #endif // INPUTWINDOW_H
    
    inputwindow.cpp
    #include "mainwindow.h"
    #include "inputwindow.h"
    #include <QVBoxLayout>
    #include <QHBoxLayout>
    #include <QTabWidget>
    #include <QPushButton>
    #include <QDebug>
    
    InputWindow::InputWindow(QWidget *parent) : QWidget(parent)
    {
        QVBoxLayout *vb = new QVBoxLayout(this);
        QHBoxLayout *hb1 = new  QHBoxLayout;
    
        QPushButton *b1 = new QPushButton("Chiudi");
        QObject::connect(b1, SIGNAL(released()), this, SLOT(b1Pressed()));
    
        QPushButton *b2 = new QPushButton("B2");
        QPushButton *b3 = new QPushButton("B3");
        hb1->addWidget(b1);
        hb1->addWidget(b2);
        hb1->addWidget(b3);
        vb->addLayout(hb1);
    
        QHBoxLayout *hb2 = new  QHBoxLayout;
        QTabWidget *tab = new QTabWidget(this);
        tab->addTab(new STable, "Prova1");
        tab->addTab(new STable, "Prova2");
        hb2->addWidget(tab);
        vb->addLayout(hb2);
    
        QHBoxLayout *hb3 = new QHBoxLayout;
        QPushButton *bbb1 = new QPushButton("BBB1");
        QPushButton *bbb2 = new QPushButton("BBB2");
        QPushButton *bbb3 = new QPushButton("BBB3");
        QPushButton *bbb4 = new QPushButton("BBB4");
        QPushButton *bbb5 = new QPushButton("BBB5");
        QPushButton *bbb6 = new QPushButton("BBB6");
        QPushButton *bbb7 = new QPushButton("BBB7");
        QPushButton *bbb8 = new QPushButton("BBB8");
        hb3->addWidget(bbb1);
        hb3->addWidget(bbb2);
        hb3->addWidget(bbb3);
        hb3->addWidget(bbb4);
        hb3->addWidget(bbb5);
        hb3->addWidget(bbb6);
        hb3->addWidget(bbb7);
        hb3->addWidget(bbb8);
        vb->addLayout(hb3);
     
       qDebug() << "Passo di qua";
    }
    
    void InputWindow::b1Pressed()
    {
        qDebug() << "b1Pressed";
    }
    
    STable::STable(QWidget *parent) : QWidget(parent)
    {
        QTableWidget *tbl = new QTableWidget(100,7);
        tbl->setHorizontalHeaderLabels(QStringList()<<"Descrizione"<<"aaa"<<"bbb"<<"ccc"<<"ddd"<<"eee"<<"fff");
        tbl->setColumnWidth(0, 460);
        tbl->setColumnWidth(1, 60);
        tbl->setColumnWidth(2, 140);
        tbl->setColumnWidth(3, 90);
        tbl->setColumnWidth(4, 90);
        tbl->setColumnWidth(5, 90);
        tbl->setColumnWidth(6, 20);
    //    tbl->horizontalHeader()->setSectionResizeMode(6, QHeaderView::Fixed); //Fisso la larghezza della colonna 6
    
        for(int i = 0; i < tbl->rowCount(); i++)
        {
            tbl->setRowHeight(i, 10);
            tbl->setItem(i, 1, RightAlignedItem("0"));
            tbl->setCellWidget(i, 2, SetComboBox());
            tbl->setItem(i, 3, RightAlignedItem("0"));
            tbl->setItem(i, 4, RightAlignedItem("0"));
            tbl->setItem(i, 5, RightAlignedItem("0"));
            tbl->setCellWidget(i, 6, SetLineWritable());
        }
    
        QVBoxLayout *Layout = new QVBoxLayout;
        Layout->addWidget(tbl);
        setLayout(Layout);
    }
    
    // ******************
    QTableWidgetItem *STable::RightAlignedItem(QString value)
    {
        auto item = new QTableWidgetItem(value);
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        return item;
    }
    
    QTableWidgetItem *STable::CenterAlignedItem(QString value)
    {
        auto item = new QTableWidgetItem(value);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        return item;
    }
    
    QCheckBox *STable::SetLineWritable(void)
    {
        QCheckBox *checkBoxItem = new QCheckBox;
        checkBoxItem->setCheckState(Qt::Unchecked);
        checkBoxItem->setStyleSheet("margin-left:5px; margin-right:5px;");
        return checkBoxItem;
    }
    
    QComboBox *STable::SetComboBox(void)
    {
        QComboBox *comboBox = new QComboBox;
        comboBox->addItem("1");
        comboBox->addItem("2");
        comboBox->addItem("3");
        comboBox->addItem("4");
        comboBox->addItem("5");
        comboBox->addItem("6");
        comboBox->addItem("7");
        return comboBox;
    }
    
    A questo punto faccio già una serie di domane per evitare di dovervi stressare ancora:
    1. Secondo voi dove sto sbagliando con il comando "connect"?
    2. Il codice che ho scritto è corretto così o ci sarebbero modi migliori per scriverlo?
    3. Per tornare alla videata principale (dove c'è il pulsante "apri"), devo prima fare un setVisible(false) del widget e poi distruggerlo? E' la cosa più corretta da fare? Non mi è ben chiaro come rimuovere in widget più che altro dalla view.

    Grazie anche per la pazienza

    Stefano
  • Re: Qt e ereditarietà

    Dovresti racchiudere il codice tra tag CODE non QUOTE.
  • Re: Qt e ereditarietà

    Scusa, non sono ancora pratico di questo forum, comunque ho corretto.
    Ho risolto il problema dell'errore sul comando connect.
    Bastava inserire Q_OBJECT sulla classe, cosa che avevo già fatto ma mi dava errore in compilazione.
    Poi mi è venuto in mente di provare a fare un Clean ed ha funzionato. Non è la prima volta che il fatto di non fare un Clean mi crea problemi, ma quasi ogni volta ci casco
    Restano gli altri due quesiti.
    Grazie
  • Re: Qt e ereditarietà

    2. Il codice che ho scritto è corretto così o ci sarebbero modi migliori per scriverlo?
    Se funziona quanto basta, non toccare che si guasta (proverbio popolare).
    3. Per tornare alla videata principale (dove c'è il pulsante "apri"), devo prima fare un setVisible(false) del widget e poi distruggerlo? E' la cosa più corretta da fare? Non mi è ben chiaro come rimuovere in widget più che altro dalla view.
    Devi fare una chiamata alla funzione close() in b1Pressed e basta. Il problema è far tornare visibile il pulsante "apri".
  • Re: Qt e ereditarietà

    Andrea Quaglia ha scritto:


    Se funziona quanto basta, non toccare che si guasta (proverbio popolare).
    Ah ah, bellissimo, non l'avevo mai sentito.
    Devi fare una chiamata alla funzione close() in b1Pressed e basta. Il problema è far tornare visibile il pulsante "apri".
    Nel frattempo ho anche risolto.
    Nello slot b1Pressed() avevo messo un delete this; e ho creato un segnale destroyed().
    Quindi il mio b1Pressed() era così:
    
    void InputWindow::b1Pressed()
    {
        emit(destroyed());
        delete this;
        qDebug() << "b1Pressed";
    }
    
    e funzionava, ovviamente avendo modificato anche MainWindows con la connessione al segnale e l'aggiunta di uno slot che rende visibile "Apri".
    Ora ho sostituito il delete this; con InputWindow::close(); e funziona lo stesso, anche se credo che il close sia più corretto del delete.

    Sono soddisfatto, perchè la soluzione del segnale destroyed() e tutta farina del mio sacco senza fare nemmeno una ricerca su google.
    Mi da l'impressione che pian pianino sto iniziando a capirci qualcosa

    Grazie.
Devi accedere o registrarti per scrivere nel forum
9 risposte