Sub classing QAbstractTableModel newbie questions



  • Hello,

    I have just started my journey into GUI and Qt development so I'm still trying to learn the ropes. I have done about 30 google searches looking for my answer but have not been able to find a solution. Here is what I'm trying to do..
    I have a custom class called Ledger that contains a QMap that contains another custom object "Account". I sub classed QAbstractTableModel to handle displaying my QMap. In my main window I display the model in a tableview. I have a button that opens a dialog box and accepts the input for creating a new item in the QMap. I know that when the dialog box returns my item is added to the QMap but the table view was not updating. So I did a bunch of google searches and could not figure out why my table wouldn't update. So then I tried to connect a signal and slot from my main window to my model but kept getting an error that my slot did not exists. My only solution was to call my slot function directly from the main window and pass in the information to create a new Account item in the QMap. Anyway, any help, advice criticism on how this should be done is welcome.

    #include <QAbstractTableModel>
    #include "account.h"
    #include <QMap>
    class AccountModel : public QAbstractTableModel
    {
    public:
        AccountModel();
        void setChartOfAccounts(QMap<int, Account>& chart);
    
        // overloaded functions to read table
        int rowCount(const QModelIndex &parent) const;
        int columnCount(const QModelIndex &parent) const;
        QVariant data(const QModelIndex &index, int role) const;
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    
        // overloaded functions to write to table
        bool setData(const QModelIndex &index, const QVariant &value, int role);
        Qt::ItemFlags flags(const QModelIndex &index) const;
    
    public slots:
        void insertData(int, QString, QString);
    
    private:
        QMap<int,Account> chartOfAccounts;
        int accountAt(int offset) const;
    
    };
    
    #include "accountmodel.h"
    
    AccountModel::AccountModel()
    {
    }
    int AccountModel::rowCount(const QModelIndex &parent) const
    {
        return chartOfAccounts.count();
    }
    int AccountModel::columnCount(const QModelIndex &parent) const
    {
        return 3;
    }
    QVariant AccountModel::data(const QModelIndex &index, int role) const
    {
        if(!index.isValid())
            return QVariant();
    
        if (role == Qt::DisplayRole || role == Qt::EditRole)
        {
            const int key = accountAt(index.row());
            switch(index.column())
            {
            case 0:
                return chartOfAccounts.value(key).getAccountNumber();
                break;
            case 1:
                return chartOfAccounts.value(key).getAccountName();
                break;
            case 2:
                return QString("$%1").arg(chartOfAccounts.value(key).getBalance());
                break;
            default:
                return QString("Error reading");
            }
        }
    
        return QVariant();
    }
    
    void AccountModel::setChartOfAccounts(QMap<int, Account> &chart)
    {
        chartOfAccounts = chart;
    }
    
    int AccountModel::accountAt(int offset) const
    {
        return (chartOfAccounts.begin() + offset).key();
    }
    
    bool AccountModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if(index.isValid() && role == Qt::EditRole)
        {
            int key = accountAt(index.row());
    
            switch(index.column())
            {
            case 1:
                chartOfAccounts[key].setAccountName(value.toString());
                break;
            }
            emit dataChanged(index, index);
            return true;
        }
    
        return false;
    }
    
    Qt::ItemFlags AccountModel::flags(const QModelIndex &index) const
    {
        Qt::ItemFlags flags = QAbstractItemModel::flags(index);
        if(index.column() == 1)
            flags |= Qt::ItemIsEditable;
        return flags;
    }
    
    QVariant AccountModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
        if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
            switch(section)
            {
            case 0:
                return QString("Account Number");
                break;
            case 1:
                return QString("Account Name");
                break;
            case 2:
                return QString("Current Balance");
                break;
            default:
                return QString("Not yet defined");
            }
        }
        return QVariant();
    }
    
    void AccountModel::insertData(int number, QString name, QString type)
    {
        if(!chartOfAccounts.contains(number)){
            int row = rowCount(QModelIndex());
            beginInsertRows(QModelIndex(), row, row);
            chartOfAccounts[number] = Account(number, name, 0.0);
            endInsertRows();
        }
    }
    
    
    #include <QMainWindow>
    #include "accountmodel.h"
    #include <QMap>
    #include "ledger.h"
    #include "addaccountdialog.h"
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    public slots:
        void insertAccount(int, QString, QString);
    private slots:
        void on_pb_addAccount_clicked();
    
    
        void on_pb_RemoveAccount_clicked();
    
    private:
        Ui::MainWindow *ui;
        AccountModel *accountModel;
        Ledger *ledger;
    
        // Dialogs
        AddAccountDialog *addAccountDialog;
    };
    
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QDebug>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        addAccountDialog = nullptr;
        ledger = new Ledger();
    
        ledger->addAccount(202, "Supplies", 32.99);
        ledger->addAccount(303, "general liablity", 49.92);
        accountModel = new AccountModel();
        accountModel->setChartOfAccounts(ledger->getChart());
        ui->tableView->setModel(accountModel);
        ui->tableView->setAlternatingRowColors(true);
        ui->tableView->resizeColumnsToContents();
        ui->tableView->horizontalHeader()->setStretchLastSection(true);
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ledger;
        delete ui;
    }
    
    void MainWindow::on_pb_addAccount_clicked()
    {
        if (addAccountDialog != nullptr)
            delete addAccountDialog;
        addAccountDialog = new AddAccountDialog(this);
        connect(addAccountDialog, SIGNAL(insertAccount(int,QString,QString)), this, SLOT(insertAccount(int,QString,QString)));
    
        addAccountDialog->show();
    }
    
    void MainWindow::insertAccount(int number, QString name, QString type)
    {
        if(!ledger->accountExists(number))
        {
            accountModel->insertData(number, name, type);
        }
    }
    
    
    void MainWindow::on_pb_RemoveAccount_clicked()
    {
    
        
    }
    


    1. At least one problem:
      Every QObject subclass needs Q_OBJECT macro.
      Without this macro you do not have access to signal/slot functionality.

    class AccountModel : public QAbstractTableModel
    {
    Q_OBJECT:
    public:
    AccountModel(QObject* parent=NULL);

    1. May or may not be a problem depending on the usage, but very likely to become sooner or later.
      Constructor should properly construct parent.
      In case of QObject children it means you should be able to specify parent:

    AccountModel ::AccountModel (QObject *parent)
    : QAbstractTableModel(parent)
    {
    ....
    }



  • @alex_malyu Thank you!!! I really appreciate it. I can't believe I missed that.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.