| Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date | |
| All threads index page 3 | |
Hi,
Does anyone have a good example of how to properly implement a
QDataWidgetMapper?
I've tried the following, which works, exactly once. If I try to
Update() a second time, it will fail at
QPersistentModelIndex::isValid()
inside the QAbstractItemModel.
Am I doing it wrong or is this a bug?
Thanks.
John
DataInterface::DataInterface(void)
{
db = QSqlDatabase::addDatabase("QMYSQL");
dmod = new QSqlTableModel(this, db);
dmod->setEditStrategy(QSqlTableModel::OnManualSubmit);
dmap = new QDataWidgetMapper(this);
dmap->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
dmap->setModel(dmod);
}
void DataInterface::Update(void)
{
bool ok;
ok = dmap->submit();
ok = dmod->submitAll();
}
--
[ signature omitted ]
For anyone who is struggling to implement a QDataWidgetMapper, here is a working example tested with Qt 4.2.3. I'm submitting this due to a complete lack of any kind of examples in the docs or on the internet. Hopefully Trolltech will fix this in the future. There are a few bugs (or is it features?) in this version of Qt that had me stumped for a long time. First, you cannot map the text of a QComboBox or a QLabel to a database field, and mapping a QTextEdit will result in DATA LOSS without the attached patch. My next problem was simply using the mapper. Submitting data to the same row twice would result in an error because the mapper loses synchronization with the table model after doing a submitAll() from the table model. It is a simple fix to just save the currentIndex() of the mapper BEFORE submitting your changes then do a setCurrentIndex() back to the stored value. Problem solved. John
#include "database.h"
#include "database.h"
int main(int argc, char *argv[])
{
QApplication *app = new QApplication(argc, argv);
DataEntryForm *EntryForm = new DataEntryForm();
return app->exec();
}
DataEntryForm::DataEntryForm(void) : Ui::DataEntryForm()
{
setupUi(this);
this->show();
db = QSqlDatabase::addDatabase("QMYSQL");
Connect();
model = new QSqlTableModel(this, db);
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
mapper= new QDataWidgetMapper(this);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
mapper->setModel(model);
MapWidget(partText, "PartsTable", "MfgPartNumber");
MapWidget(descriptionText, "PartsTable", "Description");
connect(nextButton, SIGNAL(clicked()), this, SLOT(NextClicked()));
connect(prevButton, SIGNAL(clicked()), this, SLOT(PrevClicked()));
connect(searchButton, SIGNAL(clicked()), this, SLOT(SearchClicked()));
connect(submitButton, SIGNAL(clicked()), this, SLOT(SubmitClicked()));
}
void DataEntryForm::NextClicked(void)
{
mapper->toNext();
resultLabel->setText(QString("Result: %1 of %2").arg(mapper->currentIndex() + 1).arg(model->rowCount()));
}
void DataEntryForm::PrevClicked(void)
{
mapper->toPrevious();
resultLabel->setText(QString("Result: %1 of %2").arg(mapper->currentIndex() + 1).arg(model->rowCount()));
}
void DataEntryForm::SearchClicked(void)
{
Search(partSearchText->text());
resultLabel->setText(QString("Result: %1 of %2").arg(mapper->currentIndex() + 1).arg(model->rowCount()));
}
void DataEntryForm::SubmitClicked(void)
{
SubmitData();
}
void DataEntryForm::MapWidget(QWidget *widget, const QString &table, const QString &field)
{
mapper->addMapping(widget, FindField(table, field));
}
int DataEntryForm::FindField(const QString &table, const QString &field)
{
QSqlRecord fields = db.record(table);
int column = -1;
for (int i = 0; i < fields.count(); ++i)
{
if (fields.fieldName(i) == field)
{
column = i;
break;
}
}
if (column == -1)
{
QMessageBox::warning(this, "Not found...", QString("Field '%1' not found in table %2").arg(field).arg(table));
}
return column;
}
void DataEntryForm::Connect(void)
{
bool ok;
db.setPort(3306);
db.setHostName("YourServerName or IP");
db.setDatabaseName("Parts_dbo");
db.setUserName("root");
db.setPassword("xxxxxx");
ok = db.open();
if (ok == false)
{
QMessageBox::warning(this, "Connection Failed", db.lastError().text(), 0, 0, 0);
}
}
void DataEntryForm::Search(const QString &value)
{
model->clear();
model->setTable("PartsTable");
model->setFilter(QString("MfgPartNumber LIKE '%%1%'").arg(value));
model->setSort(0, Qt::AscendingOrder);
model->select();
if (model->lastError().type() != QSqlError::NoError) {
QMessageBox::critical(this, "MySQL Database Error", QString("%1").arg(model->lastError().text()));
return;
}
if (model->rowCount() == 0) {
QMessageBox::warning(this, "Not found", QString("No items found matching %1").arg(value));
return;
} else {
mapper->toFirst();
}
}
void DataEntryForm::SubmitData(void)
{
bool ok;
int index = mapper->currentIndex();
ok = mapper->submit();
if (ok == true)
{
ok = model->submitAll();
if (ok == true) {
mapper->setCurrentIndex(index);
QMessageBox::information(this, "Success!", "Your Update Was Submitted to the Database.");
} else {
QMessageBox::warning(this, "Failed!", "Error: Table model could not submit data to the database!");
QMessageBox::warning(this, "Error Message...", model->lastError().text());
}
} else {
QMessageBox::warning(this, "Failed!", "Error: Widget mapper could not submit data to the model!");
QMessageBox::warning(this, "Error Message...", model->lastError().text());
}
}
#include <QApplication>
#include <QApplication>
#include <QtCore>
#include <QtSql>
#include <QtGui>
#include "ui_dataentryform.h"
class DataEntryForm : public QMainWindow, public Ui::DataEntryForm
{
Q_OBJECT
public:
DataEntryForm(void);
protected slots:
void NextClicked(void);
void PrevClicked(void);
void SearchClicked(void);
void SubmitClicked(void);
private:
void SubmitData(void);
void Connect(void);
void Search(const QString &value);
void MapWidget(QWidget *widget, const QString &table, const QString &field);
int FindField(const QString &table, const QString &field);
QSqlDatabase db;
QSqlTableModel *model;
QDataWidgetMapper *mapper;
};
Attachment:
Attachment:
dataentryform.ui
Description: application/designer######################################################################
# Automatically generated by qmake (2.01a) Thu May 10 09:15:47 2007
######################################################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
HEADERS += database.h
FORMS += dataentryform.ui
SOURCES += database.cpp
QT += sql
diff -up dir1/qcombobox.h dir2/qcombobox.h
diff -up dir1/qcombobox.h dir2/qcombobox.h
--- dir1/qcombobox.h 2006-12-28 08:34:13.000000000 -0500
+++ dir2/qcombobox.h 2007-02-21 04:58:46.000000000 -0500
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
+** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
**
** This file is part of the QtGui module of the Qt Toolkit.
**
@@ -48,7 +48,7 @@ class Q_GUI_EXPORT QComboBox : public QW
Q_ENUMS(SizeAdjustPolicy)
Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
Q_PROPERTY(int count READ count)
- Q_PROPERTY(QString text READ currentText WRITE setEditText NOTIFY editTextChanged USER true)
+ Q_PROPERTY(QString currentText READ currentText)
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(int maxVisibleItems READ maxVisibleItems WRITE setMaxVisibleItems)
Q_PROPERTY(int maxCount READ maxCount WRITE setMaxCount)
diff -up dir1/qlabel.h dir2/qlabel.h
--- dir1/qlabel.h 2006-12-27 15:12:19.000000000 -0500
+++ dir2/qlabel.h 2007-02-21 04:58:46.000000000 -0500
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
+** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
**
** This file is part of the QtGui module of the Qt Toolkit.
**
@@ -35,7 +35,7 @@ class QLabelPrivate;
class Q_GUI_EXPORT QLabel : public QFrame
{
Q_OBJECT
- Q_PROPERTY(QString text READ text WRITE setText USER true)
+ Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(Qt::TextFormat textFormat READ textFormat WRITE setTextFormat)
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
Q_PROPERTY(bool scaledContents READ hasScaledContents WRITE setScaledContents)
diff -up dir1/qtextedit.h dir2/qtextedit.h
--- dir1/qtextedit.h 2006-12-27 15:17:26.000000000 -0500
+++ dir2/qtextedit.h 2007-02-21 04:58:45.000000000 -0500
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
+** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
**
** This file is part of the QtGui module of the Qt Toolkit.
**
@@ -61,8 +61,7 @@ class Q_GUI_EXPORT QTextEdit : public QA
QDOC_PROPERTY(QTextOption::WrapMode wordWrapMode READ wordWrapMode WRITE setWordWrapMode)
Q_PROPERTY(int lineWrapColumnOrWidth READ lineWrapColumnOrWidth WRITE setLineWrapColumnOrWidth)
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
- Q_PROPERTY(QString html READ toHtml WRITE setHtml NOTIFY textChanged)
- Q_PROPERTY(QString text READ toPlainText WRITE setPlainText NOTIFY textChanged USER true)
+ Q_PROPERTY(QString html READ toHtml WRITE setHtml NOTIFY textChanged USER true)
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
Q_PROPERTY(int tabStopWidth READ tabStopWidth WRITE setTabStopWidth)
Q_PROPERTY(bool acceptRichText READ acceptRichText WRITE setAcceptRichText)
Message 3 in thread
John Voltz wrote:
> For anyone who is struggling to implement a QDataWidgetMapper, here is a
> working example tested with Qt 4.2.3. I'm submitting this due to a
> complete lack of any kind of examples in the docs or on the internet.
> Hopefully Trolltech will fix this in the future.
There's a simple example in the 4.3.0 release candidate:
http://doc.trolltech.com/4.3/itemviews-simplewidgetmapper.html
Other examples will follow, either for the final release or shortly
afterwards in the snapshots.
> There are a few bugs (or is it features?) in this version of Qt that had
> me stumped for a long time. First, you cannot map the text of a
> QComboBox or a QLabel to a database field, and mapping a QTextEdit will
> result in DATA LOSS without the attached patch.
Have you submitted a bug report for this?
David
--
[ signature omitted ]
Message 4 in thread
Thanks for the response David. If I can make a suggestion, it would be
great if the documentation mentioned what widgets are mappable, and what
property of the widget is mapped. Also, an example of how to submit
changes back to the database would be nice. As my earlier post
explained, I found what seems to be a bug when doing a submitAll() from
the QSqlTableModel. The index of the mapper gets lost after submitAll().
As far as bug reports go, I hate to waste the Qt developers' time with
invalid bug reports. How do I know this wasn't done on purpose? The
documentation doesn't say, so for all I know this was intentional.
I did submit a bug report for the QComboBox, QLabel, QTextEdit problem I
discovered and someone at Trolltech (I forget who) mentioned that it is
an area that will be getting some attention soon, basically because the
USER mapped property is fixed and really should be changeable.
I'm porting a database application from Visual Basic .NET over to Qt,
and so far I would say that the only thing that is vastly different from
.NET to Qt is the inability in Qt to change the mapped property of a
control without doing some difficult and messy subclassing.
Thanks,
John
--
[ signature omitted ]
Message 5 in thread
John Voltz wrote:
> Thanks for the response David. If I can make a suggestion, it would be
> great if the documentation mentioned what widgets are mappable, and what
> property of the widget is mapped.
Essentially, the property that is used is the "user property" of the widget.
For example, the user properties of QLineEdit and QDateTime are "text"
and "dateTime" respectively.
> Also, an example of how to submit
> changes back to the database would be nice. As my earlier post
> explained, I found what seems to be a bug when doing a submitAll() from
> the QSqlTableModel. The index of the mapper gets lost after submitAll().
I'm not sure it's supposed to be used that way. In your implementation,
you called submit() on the QDataWidgetMapper instance, but why did you
also call submitAll()?
> As far as bug reports go, I hate to waste the Qt developers' time with
> invalid bug reports. How do I know this wasn't done on purpose? The
> documentation doesn't say, so for all I know this was intentional.
> I did submit a bug report for the QComboBox, QLabel, QTextEdit problem I
> discovered and someone at Trolltech (I forget who) mentioned that it is
> an area that will be getting some attention soon, basically because the
> USER mapped property is fixed and really should be changeable.
That's more or less correct. QDataWidgetMapper now has an overload of
addMapping() that accepts the name of the property to map, so you can use
an alternative property with widgets that already have a user property,
but it also enables you to use widgets that don't have a user property at
all.
> I'm porting a database application from Visual Basic .NET over to Qt,
> and so far I would say that the only thing that is vastly different from
> .NET to Qt is the inability in Qt to change the mapped property of a
> control without doing some difficult and messy subclassing.
Hopefully Qt 4.3 will be better in this respect.
--
[ signature omitted ]