Skip to content
  • 0 Votes
    5 Posts
    606 Views
    B

    Well, it took a lot of time for me to solve this problem. I solved it not in the way I wanted, but it works fine.
    Firstly, work with MS Access multi-valued fields is quite strange but quite simple: you should clear a field like DELETE Field.Value FROM MyTable WHERE Table.ID = MyID;. Then you can leave it clear or populate with new values performing several INSERT queries: INSERT INTO MyTable (Field.Value) VALUES (MyValue) WHERE MyTable.ID = MyID;.
    And it turned out that it's not that easy to make such code work in OnManualSubmit strategy table model, so I've made a widget which allows you to modify such fields. It is called MultiValueEditor.
    MultiValueEditor.h

    #ifndef MULTIVALUEEDITOR_H #define MULTIVALUEEDITOR_H #include <QDialog> #include <QListWidget> #include <QHash> #include <QSqlRelation> class MultiValueEditor : public QDialog { Q_OBJECT QModelIndex m_projectIndex; QListWidget* m_list; QHash<int, QListWidgetItem*> m_idToItem; public: explicit MultiValueEditor(QModelIndex t_index, const QSqlRelation& t_relation, QWidget* parent = nullptr); ~MultiValueEditor(); public slots: void accept() override; signals: void signalFinished(); }; #endif // MULTIVALUEEDITOR_H

    MultiValueEditor.cpp

    #include "multivalueeditor.h" #include <QSqlQuery> #include <QVBoxLayout> #include <QDialogButtonBox> #include <QPushButton> #include <QMessageBox> #include <QSqlError> #include <QDebug> #include <QSortFilterProxyModel> #include <QSqlTableModel> MultiValueEditor::MultiValueEditor(QModelIndex t_index, const QSqlRelation& t_relation, QWidget *parent) : QDialog(parent) , m_projectIndex(t_index) { const QString queryStr = QString("SELECT [%1], [%2] FROM [%3] ORDER BY [%2];") .arg(t_relation.indexColumn(), t_relation.displayColumn(), t_relation.tableName()); QSqlQuery query(queryStr, QSqlDatabase::database()); m_list = new QListWidget; while (query.next()) { const int id = query.value(0).toInt(); const QString value = query.value(1).toString(); QListWidgetItem* item = new QListWidgetItem(value); m_list->addItem(item); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); m_idToItem[id] = item; } QString value = m_projectIndex.data(Qt::EditRole).toString(); if (!value.isEmpty()) { for (const QString& id : value.split(';')) { m_idToItem[id.toInt()]->setCheckState(Qt::Checked); } } QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, &QDialogButtonBox::accepted, this, &MultiValueEditor::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &MultiValueEditor::reject); QVBoxLayout* mainLayout = new QVBoxLayout; mainLayout->addWidget(m_list); mainLayout->addWidget(buttonBox); setLayout(mainLayout); setWindowTitle(QString("Edit field '%1'").arg(t_relation.tableName())); setAttribute(Qt::WA_DeleteOnClose); } MultiValueEditor::~MultiValueEditor() {} void MultiValueEditor::accept() { // I use QSFPM, but there can be either QSqlTableModel or QSqlRelationalTableModel const QSortFilterProxyModel* proxyModel = qobject_cast<const QSortFilterProxyModel*>(m_projectIndex.model()); QSqlTableModel* sqlModel = qobject_cast<QSqlTableModel*>(proxyModel->sourceModel()); const QString primaryKeyName = sqlModel->headerData(0, Qt::Horizontal).toString(); const QString tableName = sqlModel->tableName(); const QString fieldName = sqlModel->headerData(m_projectIndex.column(), Qt::Horizontal).toString(); QString queryStr = QString("DELETE [%1].Value FROM [%2] WHERE [%3]=:tableId;") .arg(fieldName, tableName, primaryKeyName); QSqlQuery query; query.prepare(queryStr); const int tableId = m_projectIndex.siblingAtColumn(0).data().toInt(); query.bindValue(":tableId", tableId); if (!query.exec()) { QMessageBox msgBox(QMessageBox::Critical, ERROR_TITLE, QString("Error executing SQL-query\n%1") .arg(query.lastError().text()), QMessageBox::Ok, this); msgBox.exec(); } for (auto it = m_idToItem.constBegin(); it != m_idToItem.constEnd(); ++it) { if (it.value()->checkState() == Qt::Checked) { queryStr = QString("INSERT INTO [%1] ([%2].Value) " "VALUES (:fieldId) WHERE [%3]=:tableId;") .arg(tableName, fieldName, primaryKeyName); query.prepare(queryStr); query.bindValue(":fieldId", it.key()); query.bindValue(":tableId", tableId); query.exec(); } } emit signalFinished(); close(); }

    And in a place where you manage your table view (in my case in the MainWindow) you should connect TableView's double click with the execution of the MultiValueEditor. In my case it looks like this:
    MainWindow.cpp

    void MainWindow::setupDatabaseModels() { //... connect(m_projectTableView, &CustomTableView::doubleClicked, this, &MainWindow::slotEditProject); //... } void MainWindow::slotEditProject(const QModelIndex &index) { const int col = index.column(); if (col == 5) { QSqlRelation relation = QSqlRelation("Employees", "ID", "Name"); MultiValueEditor* multivalueEditor = new MultiValueEditor(index, relation, this); connect(multivalueEditor, &MultiValueEditor::signalFinished, [=](){ m_projectTableModel->select(); }); multivalueEditor->open(); } //... }

    Hope this will help somebody.

  • 0 Votes
    3 Posts
    1k Views
    D

    I am not finding the way to do this: to have ListView virtualization in all ListView usages.
    Someone is able to undertand why?

  • 0 Votes
    3 Posts
    1k Views
    M

    Ok thx, i've also found a very detailed solution on stackoverflow on how to use a parent above evrything to display a component on top.
    I didn't know about the ToolTip component. i might endup using it since it has the same purpose as what I did but probably more efficient and more weird-case-proof. So thank you for your answser.

  • 0 Votes
    11 Posts
    7k Views
    HaithamH

    @VRonin
    Can you please provide an example or something to follow? Because I am still a newbie at both Qt and QML.
    Sorry for bothering you with my many questions.

    Update:
    as you can see in the code, I change the state of the delegate through the mouse area in the delegate (it's commented out in the code). I was using it to test the states, now I've noticed another thing; Whenever I collapse the parent Item and then expand it, the previous states are not saved....does this have to do anything with what you mentioned?

  • 0 Votes
    2 Posts
    1k Views
    mrjjM

    @tokafr said:

    Mydelegate *delegate = new Mydelegate;

    hi, i might miss something but why dont you just create another instance?

    Mydelegate *delegate2= new Mydelegate;
    view2 -> setItemDelegate(delegate2);

    Normally a delegate (instance) is not shared between views as its not intended.

  • 0 Votes
    2 Posts
    2k Views
    P

    I eventually found a solution/workaround to the problem. I am posting it here if anybody ever needs it.

    The main issue it seems is wanting to filter the C++ model in QML/JavaScript. Getting elements out of the C++ model into QML seems to be possible as a returning a QVariantMap maps to a JS object in the QML engine. Using this returned object as a model proved not to work directly. This might work with further effort but due to time constraints this approach was abandoned.

    The new approach was to create a C++ model derived from QAbstractItemModel and reimplementing the necessary functions. This model is essentially a filtering proxy for Model_A and Model_B.

    The filtering is implemented in QAbstractItemModel::getData() and the elements are exposed through a reimplementation of QAbstractItemModel::roleNames().

    For this filtering proxy to change state when the underlying data changes the dataChanged() signals of the underlying models was connected to a method in the proxy model computing the correct index invoking emit dataChanged(index).

    The QML delegate reuse was then implemented as follows:

    Rectangle { // some rendering of Model_A Repeater { delegate: Delegate_B{} model: Item{ property var propertyDelegateB: model.propertyDelegateB // mulitple properties possible } } }

    This worked for my use case as I needed only one element in the model. For multiple elements this approach might not be ideal.