PDA

View Full Version : Qt 4.7, QDataWidgetMapper I can't figure out how to get this working at all.



jkyle
13th May 2010, 03:20
I have a working model derived from QAbstractTableModel. It's currently happily populating lists and tables with the appropriate values.

I want to display a selected item in a widget view with some labels, text fields, etc.

Currently, I'm just trying to statically display the first item in the list to see it work. Code references are below.

First, I did not use mapper->setCurrentModelIndex(), but the mapper->toFirst() method always returned a QModelIndex of (-1,-1).

I'm having a lot of issues debugging because this seems to do virtually nothing.

It appears like it cycles through the items and calls the data() method with the Qt::EditRole, but that's it.

Currently, editing of values is not implemented. I just need to display them in the widget fields. Am I not using QDataWidgetMapper correctly? Is it only for editable widgets? Why is it only calling the EditRole and never the DisplayRole?

Any tips are greatly appreciated.

My Model:


#ifndef COMPONENTMODEL_H
#define COMPONENTMODEL_H
#include <QAbstractTableModel>
#include <QResource>
#include <QStringList>
#include <QIcon>

#include "Common/Global.h"
#include "Components/ComponentList.h"
#include "Components/ComponentInterface.h"

namespace kex
{
class ComponentModel : public QAbstractTableModel
{
Q_OBJECT

public:
ComponentModel(int types = ComponentInterface::AllComponents,
QObject *parent = 0);

/** \brief Default destructor.
*
* Copyright 2010 KSpace MRI. All Rights Reserved.
*
* \author James Kyle
* \author $LastChangedBy$
* \date 2010-4-12
* \date $LastChangedDate$
* \version $Rev$
**/
~ComponentModel() {}

int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;

QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role) const;

private:
const ComponentList::ComponentQList *_componentList;
public slots:
void updateComponentList();
};


}
#endif // COMPONENTMODEL_H


My Model's data() method:


QVariant ComponentModel::data(const QModelIndex &index, int role) const
{
QVariant result;
if (role == Qt::DecorationRole)
{
QIcon icon(":/images/other/Science-64.png");
result = icon;
} else if (role == Qt::DisplayRole)
{
qDebug() << "called model data() display role";

if (index.row() < _componentList->count() &&
index.row() >= 0 &&
index.column() >= 0 &&
index.column() < columnCount())
{
qDebug() << "valid index";

AbstractComponent::Pointer comp = (*_componentList)[index.row()];

if (index.column() == 0)
{
qDebug() << "column 0";

result.setValue(comp->name());
}
else {
result.setValue(QString("a column value"));
}
}
}
return result;
}


The method that configures the QDataWidgetMapper


void MainWindow::setUpWidgetMapper()
{
// set up the data mapper for displaying
ComponentModel *model = new ComponentModel(ComponentInterface::AllComponents,
this);
mapper->setModel(model);
mapper->addMapping(componentNameLabel, 0);
mapper->addMapping(typeNameLabel, 1);
mapper->addMapping(componentDurationLabel, 4);
mapper->addMapping(componentDescriptionTextEdit, 3);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
mapper->setCurrentModelIndex(model->index(0,0));
mapper->toFirst();
}

jkyle
13th May 2010, 17:22
Further investigation and testing has revealed that QDataWidgetMapper only seems to work for editable widgets like QLineEdit. I've filed a bug on this issue: http://bugreports.qt.nokia.com/browse/QTBUG-10672

It's unclear to me whether it's an intentional, but (and I hope I'm not missing something) undocumented limitation or if it's an actual bug.

If anyone would like a full working example of this behavior, just download Qt's example projects here => http://doc.trolltech.com/qq/qq21-datawidgetmapper.html

Then change any of the QLineEdit's to their associated QLabel's and nothing happens.

wysota
13th May 2010, 17:31
Further investigation and testing has revealed that QDataWidgetMapper only seems to work for editable widgets like QLineEdit.

Naah... that's not true. I used it many times with labels and such. Maybe you just didn't tell it which property it should use? If you can craft a minimal compilable example reproducing the problem, I'm sure we can work this out.

jkyle
13th May 2010, 17:44
The simple example provided in the docs is a great representation of what I'm doing. Downloaded here: http://doc.trolltech.com/qq/qq21-datawidgetmapper.html, the subproject "simplewidgetmapper". I just changed the QLineEdit pointers to QLabel pointers. It's short enough where I'll just put the constructor implementation here with a couple of comments, the rest of the app is exactly the same:

*edit* And thanks! I've been banging my head on the desk over this one :P


Window::Window(QWidget *parent)
: QWidget(parent)
{
setupModel();

nameLabel = new QLabel(tr("Na&me:"));
nameEdit = new QLineEdit();
addressLabel = new QLabel(tr("&Address:"));
addressEdit = new QTextEdit();
ageLabel = new QLabel(tr("A&ge (in years):"));
ageSpinBox = new QSpinBox();
nextButton = new QPushButton(tr("&Next"));
previousButton = new QPushButton(tr("&Previous"));

nameLabel->setBuddy(nameEdit);
addressLabel->setBuddy(addressEdit);
ageLabel->setBuddy(ageSpinBox);

mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
/*
When we change the below from Edit's to Labels, it doesn't work.
*/
// This used to be mapper->addMapping(nameEdit, 0);
mapper->addMapping(nameLabel, 0);
// This used to be mapper->addMapping(addressEdit, 1);
mapper->addMapping(addressLabel, 1);
mapper->addMapping(ageSpinBox, 2);

connect(previousButton, SIGNAL(clicked()),
mapper, SLOT(toPrevious()));
connect(nextButton, SIGNAL(clicked()),
mapper, SLOT(toNext()));
connect(mapper, SIGNAL(currentIndexChanged(int)),
this, SLOT(updateButtons(int)));

QGridLayout *layout = new QGridLayout();
layout->addWidget(nameLabel, 0, 0, 1, 1);
layout->addWidget(nameEdit, 0, 1, 1, 1);
layout->addWidget(previousButton, 0, 2, 1, 1);
layout->addWidget(addressLabel, 1, 0, 1, 1);
layout->addWidget(addressEdit, 1, 1, 2, 1);
layout->addWidget(nextButton, 1, 2, 1, 1);
layout->addWidget(ageLabel, 3, 0, 1, 1);
layout->addWidget(ageSpinBox, 3, 1, 1, 1);
setLayout(layout);

setWindowTitle(tr("Simple Widget Mapper"));
mapper->toFirst();
}

jkyle
13th May 2010, 17:58
With the hint of looking at properties, I made some changes. I'm getting data into the labels now but it feels odd.

For example:


mapper->addMapping(myQLabel, 0, "text");

But as I noticed before, this calls the Model's Qt::EditRole. So in my model I did:



if (role == Qt::EditRole)
{
result = index.data(Qt::DisplayRole);
}


I'm not sure if this is the correct way of going about it?

wysota
13th May 2010, 18:16
Yes, that's ok, the data widget mapper uses the edit role to get the contents of the model. If you want to override that, you can provide it with your own item delegate with reimplemented setEditorData() method. There you can make the delegate query for the display role directly if that's what you want.