1

I wonder if someone can put me in the right direction, with his/her wisdom and skill of Qt programming as I have following logic issue with my project:

  1. I am using three different controls of delegate with QtableView

    i. ProdIdDelegate public QItemDelegate creates a QcomboBox delegate for m_prodid

    ii. QtySpinDelegate public QitemDelegate create a QspinBox delegate for m_qty

    iii. TaxCDelegate public QitemDelegate create a QcomboBox delegate for m_taxcode

  2. All these delegate integrated within QtableView control using a *m_modelo as a class of QstandardItem.

  3. there are altogether 7 column in QtableView control

  4. Delegate m_prodid get filled with database table field product code and ready for selection as drop-down items list.

  5. All three delegate has a SIGNAL/SLOT respectively their appropriate scan n search

QcomboBox(m_prodid) : CurrentIndexChanged(QString) SLOT (myscan descript_n_price)

The real question is:

Upon selection of any item from the drop-down combo box its search result such as description and price can be displayed in the QtableView’s column with real-time effect ie number of time same row selection changed, the description, a price must change along with immediate effect and display must be updated at same time.

The problem is the signal emitted by CurrentIndexChange by combo box remain within their local source file it does not cross into the main control of QtableView source file, where all formatted data displayed and calculated.

Following source code will give some understanding what trying to achieve? 

#ifndef PRODIDDELEGATE_H
#define PRODIDDELEGATE_H
#include "ak_connection.h"
#include <QItemDelegate>
#include <QStyledItemDelegate>
#include <QComboBox>
#include <vector>
#include <string>

class ProdIdDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    explicit ProdIdDelegate(QObject *parent = nullptr);

    QString getProdId () const { return m_proid;}
    QString getDescription () const { return m_description; }
    double getPrice () const { return m_price; }
    void setDescription (const QString &description) {m_description = description; }
    void setProdId(const QString &prodid) { m_proid = prodid; }
    void setPrice (const double &price) { m_price = price; }
protected:

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const;
    void setEditorData(QWidget *editor, const QModelIndex &index) const;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const;
    void paint (QPainter *painter,
                const QStyleOptionViewItem &option,
                const QModelIndex &index) const;
    QSize sizeHint (const QStyleOptionViewItem &option,
                    const QModelIndex &index) const;
private:
    void setValuesToVariable(QString str);
    std::vector<std::string> Items;
    QString m_proid;
    QString m_description;
    double m_price;
    AK_connection *akdb;

private slots:
    void onComboItemChanged(const QString &text);
    void commitAndCloseEditor();
};

#endif // PRODIDDELEGATE_H

Implementation file

#include "prodiddelegate.h"
#include <QComboBox>
#include <QDebug>
#include <QStyledItemDelegate>
#include <QSqlQuery>
#include <QSqlError>

// #define _TEST_

ProdIdDelegate::ProdIdDelegate(QObject *parent)
    : QStyledItemDelegate (parent)
{   
    (void) parent;
#ifdef _TEST_
    m_description = "A Quick Borwn Fox Jump Over Little Lazy Dog.";
    m_price = 87634.90;

    Items.push_back("Test0");
    Items.push_back("Test1");
    Items.push_back("Test2");
    Items.push_back("Test3");
    Items.push_back("Test4");
    Items.push_back("Test5");
    Items.push_back("Test6");
    Items.push_back("Test7");
    Items.push_back("Test8");
    Items.push_back("Test9");
#else
     akdb = new AK_connection(this);
     akdb->AK_open(true);
#endif
}

QWidget *ProdIdDelegate::createEditor(QWidget *parent,
                                      const QStyleOptionViewItem &option,
                                      const QModelIndex &index) const
{
    QComboBox *prodId = new QComboBox(parent);
#ifndef _TEST_
    QSqlQuery q;
    q.prepare(QString("SELECT COALESCE(itemid,'') || " \
                      "COALESCE(description,'') AS ProdId " \
                      "FROM itemstbl WHERE status='1'"));

    if (!q.exec())
        qDebug () << q.lastError().text();
    while (q.next()) {
        prodId->addItem(q.value(0).toString());
    }
    q.clear();
#else
    for (unsigned int i=0; i < Items.size(); i++)
        prodId->addItem(Items[i].c_str());
#endif
    (void) option;
    (void) index;

    connect(prodId, SIGNAL(currentIndexChanged(QString)), this, SLOT(onComboItemChanged(QString)));
    return prodId;
}
void ProdIdDelegate::setEditorData(QWidget *editor,
                                   const QModelIndex &index) const
{
    QStyledItemDelegate m_siDelegate;

    if (QComboBox *cb = qobject_cast <QComboBox *> (editor)) {
        QString curItem = index.data(Qt::EditRole).toString();
        int cbIndex = cb->findText(curItem);
        // Is it valid? then adjust.
        if (cbIndex >= 0)
            cb->setCurrentIndex(cbIndex);
    } else {
        m_siDelegate.setEditorData(editor, index);
    }    

}
void ProdIdDelegate::setModelData(QWidget *editor,
                                  QAbstractItemModel *model,
                                  const QModelIndex &index) const
{
    QStyledItemDelegate m_siDelegate;

    if (QComboBox *cb = qobject_cast <QComboBox *> (editor)) {
        // Save current text of combo box to an item.
        model->setData(index, cb->currentText(), Qt::EditRole);
    } else {
        m_siDelegate.setModelData(editor, model, index);
    }
}
void ProdIdDelegate::updateEditorGeometry(QWidget *editor,
                                          const QStyleOptionViewItem &option,
                                          const QModelIndex &index) const
{
    editor->setGeometry(option.rect);
    (void) index;
}
void ProdIdDelegate::paint(QPainter *painter,
                           const QStyleOptionViewItem &option,
                           const QModelIndex &index) const
{
    (void) painter;
    (void) option;
    (void) index;
}
QSize ProdIdDelegate::sizeHint(const QStyleOptionViewItem &option,
                               const QModelIndex &index) const
{
   return QStyledItemDelegate::sizeHint(option, index);
}

void ProdIdDelegate::onComboItemChanged(const QString &text)
{    
    emit commitData(qobject_cast <QWidget *> (sender()));
    setProdId(text.left(5));
    //qDebug () << "SIGNAL OnComboChanged gives string:" << text
              //<< " File: " << __FILE__;
    setValuesToVariable(text.left(5));
}

void ProdIdDelegate::setValuesToVariable(QString str)
{
#ifdef _TEST_
    (void) str;
    setDescription(m_description);
    setPrice(m_price);
#else
    QSqlQuery q; q.prepare(QString("SELECT description,price FROM itemstbl "
                                   "WHERE itemid ='%1'AND status='1'").arg(str));
    if (!q.exec())
        qDebug () << q.lastError().text();

    while (q.next()) {
        setDescription(q.value(0).toString());
        setPrice(q.value(1).toDouble());
    }
#endif
    qDebug () << "Description:" << getDescription() << "Price:" << getPrice();
}

void ProdIdDelegate::commitAndCloseEditor()  // This signal yet to fired up.
{
    QWidget *editor = qobject_cast <QWidget *> (sender());
    emit commitData(editor);
    emit closeEditor(editor);
}

Main dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
#include "prodiddelegate.h"
#include "vatdelegate.h"
#include "qtyspin.h"
#include <QStandardItemModel>
#include <QTableWidget>
#include <QDebug>
#include <QLabel>
#include <cassert>

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    QDialog::setWindowTitle(">> Invoice Delegate <<");
    m_price = 20.87;
    m_vatrate = 0.0;
    m_qty     = 0;
    m_modelo         = new QStandardItemModel(this);
    m_prodIdDelegate = new ProdIdDelegate(this);
    m_qtyspinDelegate= new QtySpin(this);
    m_vatDelegate    = new VATDelegate(this);

    m_modelo->setRowCount(10);
    m_modelo->setColumnCount(7);

    m_modelo->setHorizontalHeaderItem(0, new QStandardItem("Prod ID"));
    m_modelo->setHorizontalHeaderItem(1, new QStandardItem("Description"));
    m_modelo->setHorizontalHeaderItem(2, new QStandardItem("Qty"));
    m_modelo->setHorizontalHeaderItem(3, new QStandardItem("Price"));
    m_modelo->setHorizontalHeaderItem(4, new QStandardItem("Amount"));
    m_modelo->setHorizontalHeaderItem(5, new QStandardItem("VATAmt"));
    m_modelo->setHorizontalHeaderItem(6, new QStandardItem("VAT"));

    ui->mytableView->setModel(m_modelo);
    assert (ui->mytableView->model());

    // set the column width
    ui->mytableView->setColumnWidth(0, 70);
    ui->mytableView->setItemDelegateForColumn(0, m_prodIdDelegate);
    ui->mytableView->showColumn(0);
    ui->mytableView->setColumnWidth(1, 400);
    ui->mytableView->setColumnWidth(2, 60);
    ui->mytableView->setItemDelegateForColumn(2, m_qtyspinDelegate);
    ui->mytableView->setColumnWidth(3, 80);
    ui->mytableView->setColumnWidth(4, 100);
    ui->mytableView->setColumnWidth(5, 80);
    ui->mytableView->setColumnWidth(6, 50);
    ui->mytableView->setItemDelegateForColumn(6, m_vatDelegate);

    QMetaObject::invokeMethod(ui->mytableView, "updateGeometries");

    for (int i=0; i<m_modelo->rowCount(); i++) {
        ui->mytableView->openPersistentEditor(m_modelo->index(i, 0));
        ui->mytableView->openPersistentEditor(m_modelo->index(i, 2));
        ui->mytableView->openPersistentEditor(m_modelo->index(i, 6));
    }

    ui->mytableView->show();
}

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

void Dialog::on_QuitpushButton_clicked()
{
    close();
}

void Dialog::on_mytableView_clicked(const QModelIndex &index)
{    (void) index;
    //qDebug() << "SIGNAL: QTableView_clicked at Row " <<  index.row() << " Column " << index.column();
    //qDebug () << "SIGNAL FROM Dialog_clicked " << ui->mytableView->model()->data(index).toString();
    //int curCol = index.column();
/*
    switch (curCol) {
        case 0:
            qDebug () << "ProdID " << ui->mytableView->model()->data(index).toString();
            GetCurrentItem(index.row());
          break;
        case 2:
            m_qty = ui->mytableView->model()->data(index).toInt();
            qDebug () << "Qty" << m_qty;
            putAmount(index.row());
          break;
        case 6:
            QString code = ui->mytableView->model()->data(index).toString();
            qDebug () << "VAT code " << code;
            switch (code.data()->toLatin1())
            {
                case 'S': m_vatrate = 20.0; break;
                case 'H': m_vatrate = 10.0; break;
                case 'Q': m_vatrate =  5.0; break;
                case 'E':
                case 'Z': m_vatrate = 0.0; break;
                default:  m_vatrate = 0.0;
            }
            putVatAmt (index.row());
          break;
        //default:
       }  */
    qDebug () << "Description" << m_prodIdDelegate->getProdId();
    qDebug () << "Price" << m_prodIdDelegate->getPrice();
    qDebug () << "QTY came:" << m_qtyspinDelegate->getQty();
    qDebug () << "VAT Rate came" << m_vatDelegate->getVATRate();
}

void Dialog::on_mytableView_doubleClicked(const QModelIndex &index)
{
    qDebug() << "SIGNAL: QTableView_doubleClicked at Row " << index.row() << " Column " << index.column();
}

void Dialog::GetCurrentItem(int rowCount)
{   QString myprc = QString("%1").arg(m_price);
    QStandardItem *m_descrpt = new QStandardItem("This is a test procedure");
    QStandardItem *m_pric   = new QStandardItem(myprc);
    m_modelo->setItem(rowCount, 1, m_descrpt);
    m_modelo->setItem(rowCount, 3, m_pric);
}

void Dialog::putAmount(int rowCount)
{
    double amt = m_price * m_qty;
    QString stramt = QString("%1").arg(amt);
    QStandardItem *m_amt     = new QStandardItem(stramt);
    m_modelo->setItem(rowCount, 4, m_amt);
}
void Dialog::putVatAmt(int rowCount)
{
    qDebug () << "VATrate" << m_vatrate;
    double vatam = (((m_price * m_qty) * m_vatrate) /100);
    QString strvat = QString("%1").arg(vatam);
    QStandardItem  *m_vatamt = new QStandardItem(strvat);
    m_modelo->setItem(rowCount, 5, m_vatamt);
}

Header file dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

namespace Ui {
class Dialog;
}

class QStandardItemModel;
class ProdIdDelegate;
class VATDelegate;
class QtySpin;
class QTableWidget;

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

private slots:
    void on_QuitpushButton_clicked();
    void on_mytableView_clicked(const QModelIndex &index);
    void on_mytableView_doubleClicked(const QModelIndex &index);
    void GetCurrentItem(int rowCount);
    void putAmount(int rowCount);
    void putVatAmt(int rowCount);
private:
    Ui::Dialog *ui;

    QStandardItemModel *m_modelo;
    ProdIdDelegate     *m_prodIdDelegate;
    QtySpin            *m_qtyspinDelegate;
    VATDelegate        *m_vatDelegate;
    QTableWidget       *m_curRow;
    QString            m_pid;
    double             m_vatrate;
    double             m_price;
    int                m_qty;
};

#endif // DIALOG_H

The following png image gives what suppose to source code should be doing?

An expected logic from above source codes

Result:

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
shylock
  • 41
  • 5
  • Could you explain the logic of the column VATAmt and VAT please. – eyllanesc Oct 20 '17 at 04:07
  • Upon search on prodId will get price value and using spinbox will get the quantity, then applying the following to work out tax: double vatam = (((m_price * m_qty) * m_vatrate) /100); VAT is char code it has assigned a value upon whichever letter selected tax rate applies. – shylock Oct 20 '17 at 09:28
  • is that table to be saved to a database? – eyllanesc Oct 20 '17 at 21:04
  • The table exists in SQLITE3 database and only qty field updated at a very end. But all the time the database table: 'itemstbl' searched for inventory items. You can create a table with fields prodid, description,qty,price for your own test. – shylock Oct 21 '17 at 20:03
  • I wonder did anyone managed to figure out, above stated problem logic? Although I have a crude solution for the same problem which works. But I am interested to have more efficient and less CPU intensive solution, I am sure there were many Qt C++ experts exists on the Forum with huge points in their credit, surely may have to jog their Qt knowledge to an extra mile or on a treadmill? – shylock Oct 21 '17 at 20:05
  • My idea is to work with the model instead of the delegate, I plan to use QSqlRelationalTableModel instead of QStandardItemModel, obviously I have to customize it to add the Description and Price columns. – eyllanesc Oct 21 '17 at 21:13
  • Sorry... I do not have a problem with QStandardItemModel or QSqlRelationalTableModel, my requirement IS that ProdIdDelegate class driven from QStyledItemDelegate must have the capability to update the fields in the QTableView Columns. It means data search in the class ProdIdDelegate and displayed in the Dialog main class and updated simultaneously if selection in QComboBox changes.and QtySpin box changes accordingly, as QComboBox and QSpinBox being an editor at an initial stage of Dialog main program. – shylock Oct 22 '17 at 01:01
  • Is that is not the right way to do things, delegates only serve to show the data, you should not process data in them. If you want to modify data that is related, the correct thing is to use the model. – eyllanesc Oct 22 '17 at 01:03

0 Answers0