crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

 
Ответить в данную темуНачать новую тему
> QDataWidgetMapper и изменение foregn key
Begemot
  опции профиля:
сообщение 28.5.2010, 17:44
Сообщение #1


Студент
*

Группа: Новичок
Сообщений: 13
Регистрация: 15.5.2010
Пользователь №: 1718

Спасибо сказали: 0 раз(а)




Репутация:   0  


Случилось тут проблема, и уже больше суток не могу решить, пробовал разные подходы все или вообще не работает или работает но через раз (не шучу).

Есть MySqlRelationalTableModel, у нее одно из полей внешний ключ (fkey) ну и соответвенно связанная таблица ID, Name. Все поля таблицы привязаны к виджетам на форме через QDataWidgetMapper, fkey привязан к QLineEdit. Отображается все отлично, вместо значения ключа я вижу соответвующий Name из связанной таблицы.

Вопрос как правильно програмно изменить это значение. Для юзера это выглядит так - QLineEdit read only, по щелчку на нему - открывается диалог, в котором список Name - пользователь выбирает нужное или _вводит новое_, я получаю строку которую он выбрал\ввел. Как мне теперь правильно изменить значение в главной модели и все это сохранить в бд?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 28.5.2010, 18:59
Сообщение #2


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9656
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


setModelData и submitAll
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Begemot
  опции профиля:
сообщение 29.5.2010, 7:39
Сообщение #3


Студент
*

Группа: Новичок
Сообщений: 13
Регистрация: 15.5.2010
Пользователь №: 1718

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(Litkevich Yuriy @ 28.5.2010, 18:59) *
setModelData и submitAll


Я так полагаю что речь идет о делегате, потому как setModelData есть только у наследников QAbstractItemDelegate. Но из контрола (наследник QLineEdit) к делегату доступ получить вроде нельзя (не передавая его руками)? да это и не важно, в принципе, setModelData вызовется автоматически при потере контролом фокуса, использую setEditStrategy(QSqlTableModel::OnFieldChange);

Сейчас работа идет так, в классе наследнике QEditLine перехватывается щелчек мыши, показывается диалог - пользователь выбирает строку, эта строка записывается в наш QLineEdit, дальше ждем потери фокуса, для автоматического сохранения.

У меня есть свой делегат (изначально писался для обслуживания комбобоксов), для этого поля добавляю туда такой код
CODE

void DogDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
switch(index.column())
{
......

case Animals_Breed:
QString text = editor->property("text").toString();
qDebug()<<"save Animals_Breed " << text;

// --- get relation table and try to find our string
QSqlTableModel * rm = ((QSqlRelationalTableModel *)model)->relationModel(Animals_Breed);
QModelIndexList indexList = rm->match(rm->index(0, Breeds_Breed), Qt::DisplayRole, text, 1, Qt::MatchExactly);
int id = 0;
if (!indexList.isEmpty())
id = rm->data(rm->index(indexList.first().row(), Breeds_Id)).toInt();
else
{ // there is no such string - we should add it
QSqlRecord record(rm->record());
record.setValue(Breeds_Breed, text);
rm->insertRecord(0, record);
rm->submit();

// --- Get id for new added string
QModelIndexList indexList = rm->match(rm->index(0, Breeds_Breed), Qt::DisplayRole, text, 1, Qt::MatchExactly);
if (!indexList.isEmpty())
id = rm->data(rm->index(indexList.first().row(), Breeds_Id)).toInt();
else
Q_ASSERT(0);
}

model->setData(index, id);
((QSqlRelationalTableModel*) model)->submitAll();
return;

}
QItemDelegate::setModelData(editor, model, index);
}


Если маппер пытается сабмитить это поле, я получаю в rm таблицу связанную с внешним ключем, и пытаюсь в соответвуюшей колонке найти значение которое у нас в лайнэдите. Если значение есть - все отлично, беру соответсвующий id из внешней таблицы и пишу его в главную таблицу.
Если там такого значения нет, значит пользователь ввел новую породу - я добавляю ее в связанную таблицу, сабмичу ее, дальше получаю id и пишу в главную.

Этот подход работает если пользователь просто выбирает из списка строку (которая уже есть в таблице). Если же он вводит новую строку - я получаю проблемы :(
1. Строка добавляется в бд.
2. В едит бокс попадает соответвующее значение (уже прочитанное из бд, а не то что я руками поставил)
3. дальше пользователь ставит фокус в другой контрол, и потом когда фокус переходит в третий контрол - уходит из второго, значение в едит боксе и в базе данных сбрасывается в предыдущее, и так в 66% случаях.

submitAll не помогает :( видимо проблема в том что при изменении\сабмите связаной таблицы, то ли главная модель не обновляется то ли еще что - но оно "забывает" изменения в 2 их 3 случаях. Что делать ума не приложу, как-то ресетить модель? заново перезагружать? что-то еще? Или это просто еще 1 баг :(
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 29.5.2010, 11:25
Сообщение #4


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9656
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


Цитата(Begemot @ 29.5.2010, 11:39) *
потому как setModelData есть только у наследников QAbstractItemDelegate
извиняюсь, имел в виду QAbstractItemModel::setData
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Begemot
  опции профиля:
сообщение 30.5.2010, 12:41
Сообщение #5


Студент
*

Группа: Новичок
Сообщений: 13
Регистрация: 15.5.2010
Пользователь №: 1718

Спасибо сказали: 0 раз(а)




Репутация:   0  


Ни прямой вызов setData, ни вызов его в делегате, ни вариант заставить маппер засабмитить у меня не работает так как надо.

Написал тестовое приложение, в котором явно видна, проблема, может кто-может посмотреть? Ниже тексты трех файлов, свой делегат нужен просто для того что бы выводить лог сохранения \ чтения данных, в h файле вверху можно изменить define отвечающий за родителя делегата, я проверял оба варианта - они работают одинаково похоже. Код сохранения сейчас в void RelationLineEdit::mousePressEvent(QMouseEvent* e)

main.cpp

CODE

#include <QApplication>

#include "window.h"

int main(int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}



window.h
CODE

#ifndef WINDOW_H
#define WINDOW_H


//#define MyDelegateParent QSqlRelationalDelegate
#define MyDelegateParent QItemDelegate


#include <QtGui>
#include <QtSql>

class QComboBox;
class QDataWidgetMapper;
class QItemSelectionModel;
class QLabel;
class QLineEdit;
class QPushButton;
class QSqlRelationalTableModel;
class QStandardItemModel;
class QStringListModel;
class QTextEdit;
class RelationLineEdit;
class MySqlRelationalTableModel;

class Window : public QWidget
{
Q_OBJECT

public:
Window(QWidget *parent = 0);

private slots:
void updateButtons(int row);

private:
void setupModel();

QLabel *nameLabel;
QLabel *addressLabel;
QLabel *typeLabel;
QLineEdit *nameEdit;
QTextEdit *addressEdit;
RelationLineEdit * typeEdit;
QPushButton *nextButton;
QPushButton *previousButton;

MySqlRelationalTableModel *model;
QItemSelectionModel *selectionModel;
QDataWidgetMapper *mapper;
int typeIndex;
/*QModelIndex */ int indexFixQTBUG1086;

private slots:
void FixQTBUG1086Before();
void FixQTBUG1086After();


};

//------------------------------------------------------------------------------

class RelationLineEdit : public QLineEdit
{
Q_OBJECT
public:
RelationLineEdit(QWidget *parent = 0);
~RelationLineEdit();

void SetData(QSqlRelationalTableModel * _model, QDataWidgetMapper * _mapper, const QString _title) { model = _model; mapper = _mapper; title = _title; }


protected:
virtual void mousePressEvent(QMouseEvent* e);

private:
QSqlRelationalTableModel * model;
QDataWidgetMapper * mapper;
QString title;
};

//------------------------------------------------------------------------------
class SelectRelationValueDlg : public QDialog
{
Q_OBJECT
public:
SelectRelationValueDlg(QWidget * parent, QSqlTableModel * model, const QString & current, const QString title);
~SelectRelationValueDlg();

QString GetText() const { return pEdit->text(); }

private slots:
void CurrentRowChanged(const QModelIndex & current);
void TextChanged(const QString & text);

private:
QVBoxLayout *verticalLayout;
QLineEdit *pEdit;
QListView *pList;
QDialogButtonBox *buttonBox;
};



/* Subclassed model as workaround for bug
http://bugreports.qt.nokia.com/browse/QTBUG-1086
http://begemotov.net/wxwidgets/qt/qt-te-zh...tolko-v-profil/
Похоже каждый раз когда маппер сохраняет данные, делается select, мы испускаем сигналы, который вызывающий код должен привязать к процедурам сохранения\востановления индекса
connect(modelname, SIGNAL(BeforeSelected()), this, SLOT(FixQTBUG1086Before()));\
connect(modelname, SIGNAL(AfterSelected()), this, SLOT(FixQTBUG1086After()));\
*/

//------------------------------------------------------------------------------

class MySqlRelationalTableModel : public QSqlRelationalTableModel
{
Q_OBJECT
public:
MySqlRelationalTableModel(QWidget *parent = 0) : QSqlRelationalTableModel(parent) {}

virtual bool select()
{
emit BeforeSelected(); // without this it doesn't work
const bool ret = QSqlRelationalTableModel::select();
emit AfterSelected(); // without this it doesn't work
return ret;
}

signals:
void BeforeSelected();
void AfterSelected();
};

#endif

//------------------------------------------------------------------------------

class MyDelegate : public MyDelegateParent
{
Q_OBJECT

public:
MyDelegate(QObject *parent = 0) {};
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
};



window.cpp
CODE

#include "window.h"


#define Person_TypeId 3
#define Addresstype_Id 0
#define Addresstype_Place 1

Window::Window(QWidget *parent) : QWidget(parent), mapper(0)
{
setupModel();
indexFixQTBUG1086 = -1;

nameLabel = new QLabel(tr("Na&me:"));
nameEdit = new QLineEdit();
addressLabel = new QLabel(tr("&Address:"));
addressEdit = new QTextEdit();
typeLabel = new QLabel(tr("&Type:"));
typeEdit = new RelationLineEdit();
nextButton = new QPushButton(tr("&Next"));
previousButton = new QPushButton(tr("&Previous"));

nameLabel->setBuddy(nameEdit);
addressLabel->setBuddy(addressEdit);
typeLabel->setBuddy(typeEdit);

mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->setItemDelegate(new MyDelegate(this));
mapper->addMapping(nameEdit, model->fieldIndex("name"));
mapper->addMapping(addressEdit, model->fieldIndex("address"));
mapper->addMapping(typeEdit, typeIndex);


typeEdit->SetData(model, mapper, tr("Select place"));

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(typeLabel, 3, 0, 1, 1);
layout->addWidget(typeEdit, 3, 1, 1, 1);
setLayout(layout);

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

void Window::setupModel()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(0, tr("Cannot open database"),
tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it."), QMessageBox::Cancel);
return;
}

QSqlQuery query;
query.exec("create table person (id int primary key, name varchar(20), address varchar(200), typeid int)");
query.exec("insert into person values(1, 'Alice', '<qt>123 Main Street<br/>Market Town</qt>', 101)");
query.exec("insert into person values(2, 'Bob', '<qt>PO Box 32<br/>Mail Handling Service<br/>Service City</qt>', 102)");
query.exec("insert into person values(3, 'Carol', '<qt>The Lighthouse<br/>Remote Island</qt>', 103)");
query.exec("insert into person values(4, 'Donald', '<qt>47338 Park Avenue<br/>Big City</qt>', 101)");
query.exec("insert into person values(5, 'Emma', '<qt>Research Station<br/>Base Camp<br/> Big Mountain</qt>', 103)");

query.exec("create table addresstype (id int, description varchar(20))");
query.exec("insert into addresstype values(101, 'Home')");
query.exec("insert into addresstype values(102, 'Work')");
query.exec("insert into addresstype values(103, 'Other')");

model = new MySqlRelationalTableModel(this);
connect(model, SIGNAL(BeforeSelected()), this, SLOT(FixQTBUG1086Before()));
connect(model, SIGNAL(AfterSelected()), this, SLOT(FixQTBUG1086After()));

model->setTable("person");
model->setEditStrategy(QSqlTableModel::OnFieldChange);

typeIndex = model->fieldIndex("typeid");

model->setRelation(typeIndex, QSqlRelation("addresstype", "id", "description"));
model->select();
}

void Window::updateButtons(int row)
{
previousButton->setEnabled(row > 0);
nextButton->setEnabled(row < model->rowCount() - 1);
}

void Window::FixQTBUG1086Before()
{
if (!mapper) return;
indexFixQTBUG1086 = mapper->currentIndex();
}

void Window::FixQTBUG1086After()
{
if (!mapper) return;
if (indexFixQTBUG1086 > -1)
mapper->setCurrentIndex(indexFixQTBUG1086);
}

//------------------------------------------------------------------------------

SelectRelationValueDlg::SelectRelationValueDlg(QWidget * parent, QSqlTableModel * model, const QString & current, const QString title) : QDialog(parent)
{

if (objectName().isEmpty()) setObjectName(QString::fromUtf8("SelectRelationValueDlg"));
resize(400, 300);
verticalLayout = new QVBoxLayout(this);
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
pEdit = new QLineEdit(this);
pEdit->setObjectName(QString::fromUtf8("pEdit"));

verticalLayout->addWidget(pEdit);

pList = new QListView(this);
pList->setObjectName(QString::fromUtf8("pList"));

verticalLayout->addWidget(pList);

buttonBox = new QDialogButtonBox(this);
buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
buttonBox->setOrientation(Qt::Horizontal);
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

verticalLayout->addWidget(buttonBox);

connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

pList->setModel(model);
pList->setModelColumn(1);
setWindowTitle(title);

connect(pList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(CurrentRowChanged(QModelIndex)));
connect(pList, SIGNAL(clicked(QModelIndex)), this, SLOT(CurrentRowChanged(QModelIndex))); // Надо - для того что бы менять текст при клике на текущую выделенную строку
connect(pEdit, SIGNAL(textChanged(const QString &)), this, SLOT(TextChanged(const QString &)));

pEdit->setText(current);
}

SelectRelationValueDlg::~SelectRelationValueDlg()
{

}

void SelectRelationValueDlg::CurrentRowChanged(const QModelIndex & current)
{
pEdit->setText(current.data().toString());
}

void SelectRelationValueDlg::TextChanged(const QString & text)
{
QSqlTableModel * model = (QSqlTableModel *)pList->model();
QModelIndexList indexList = model->match(model->index(0, 0), Qt::DisplayRole, text, 1, Qt::MatchExactly);

if (!indexList.isEmpty())
pList->selectionModel()->setCurrentIndex(indexList.first(), QItemSelectionModel::ClearAndSelect);
else
pList->clearSelection(); //selectionModel()->setCurrentIndex(ui->pList->selectionModel()->currentIndex(), QItemSelectionModel::Clear);
}

//------------------------------------------------------------------------------

void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
switch(index.column())
{
case Person_TypeId:
qDebug() << "read Person_TypeId";
break;
}
MyDelegateParent::setEditorData(editor, index);
}

void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
switch(index.column())
{
case Person_TypeId:
{
QString text = editor->property("text").toString();
qDebug()<<"save Person_TypeId : " << text;
}
break;
}
MyDelegateParent::setModelData(editor, model, index);
}

//------------------------------------------------------------------------------

RelationLineEdit::RelationLineEdit(QWidget *parent) : QLineEdit(parent)//, model(0)
{
setReadOnly(true);
setCursor(Qt::PointingHandCursor);
}

RelationLineEdit::~RelationLineEdit()
{

}

void RelationLineEdit::mousePressEvent(QMouseEvent* e)
{
QSqlTableModel * relModel = model->relationModel(Person_TypeId);

SelectRelationValueDlg dlg(this, relModel, text(), title);
if (dlg.exec() != QDialog::Accepted) return;

QString text = dlg.GetText();
qDebug()<<"change place :" << text;

// --- get relation table and try to find our string
QModelIndexList indexList = relModel->match(relModel->index(0, Addresstype_Place), Qt::DisplayRole, text, 1, Qt::MatchExactly);
int id = 0;
if (!indexList.isEmpty())
id = relModel->data(relModel->index(indexList.first().row(), Addresstype_Id)).toInt();
else
{ // there is no such string - we should add it
QSqlRecord record(relModel->record());
record.setValue(Addresstype_Place, text);
relModel->insertRecord(0, record);
relModel->submitAll();
// --- Get id for new added string
QModelIndexList indexList = relModel->match(relModel->index(0, Addresstype_Place), Qt::DisplayRole, text, 1, Qt::MatchExactly);
if (!indexList.isEmpty())
id = relModel->data(relModel->index(indexList.first().row(), Addresstype_Id)).toInt();
else
Q_ASSERT(0);
}

setText(text);

QModelIndex index = model->index(mapper->currentIndex(), Person_TypeId);
model->setData(index, id);
model->submitAll();
}


pro файла у меня нету, крейтор у меня не хочет работать, пишу в студии, но думаю что этот код должно быть легко попдправить под мой пример.
CODE

HEADERS = window.h
SOURCES = main.cpp \
window.cpp
QT += sql

# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/sqlwidgetmapper
sources.files = $$SOURCES $$HEADERS *.pro
sources.path = $$[QT_INSTALL_EXAMPLES]/sql/sqlwidgetmapper
INSTALLS += target sources

wince*: DEPLOYMENT_PLUGIN += qsqlite


Помогите разобратся, я что-то не так делаю или все-так я опять нарвался на баг:(

Да, забыл самое главное.
Алгоритм такой
1. Кликаем на нижнем поле
2. Выбираем из списка любою строку, например other
3. ок

- так все хорошо, а вот если попробовать добавить новую строку - облом

1. Кликаем на нижнем поле
2. к текущей строке добавляем 2ку (other2) ну или пишем что угодно другое.
3. ок
4. ставим курсор в верхний эдит
5. ставим курсор в средний эдит

В этот момент строка которую мы поменяли и которая уже сохранена в базу судя по логам делегата, возвращяется к первоначальному значению.




Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Begemot
  опции профиля:
сообщение 30.5.2010, 13:13
Сообщение #6


Студент
*

Группа: Новичок
Сообщений: 13
Регистрация: 15.5.2010
Пользователь №: 1718

Спасибо сказали: 0 раз(а)




Репутация:   0  


Допустил ошибку, надо поправить код создания базы данных сделав id автоинкрементным столбцами, надо заменить две соответвующие строчки на эти

query.exec("create table person (id integer primary key, name varchar(20), address varchar(200), typeid int)");
query.exec("create table addresstype (id integer primary key, description varchar(20))");
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Begemot
  опции профиля:
сообщение 30.5.2010, 19:26
Сообщение #7


Студент
*

Группа: Новичок
Сообщений: 13
Регистрация: 15.5.2010
Пользователь №: 1718

Спасибо сказали: 0 раз(а)




Репутация:   0  


Вообщем похоже все плохо, Now I do not use QSqlRelationalTable model at all. Useless class. To many errors... and not so good design also. (с) Qt Bugtracker, полностью согласен. Вот еще по теме - статус поражает, и еще. :(

В принципе сейчас перейдя на стратегию ручного сабмита и дико извратившись, я сумел добится нужного поведения, но такой изврат оставлять нельзя. Так что буду наверно отказыватся от QSqlRelationalTable , брать QSqlTable и делать все ручками. блин почти 3 дня коту под хвости из за багов QSqlRelationalTable.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 30.5.2010, 23:05
Сообщение #8


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9656
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


Факт: Qt 4.3.5 — наименее глючная версия в плане Модель/представление.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Гость_kkmspb_*
сообщение 24.8.2012, 10:41
Сообщение #9





Гости








    


У похожая ситуация :
setData( .. меняет значение в поле у QSqlRelationalTableModel
делаем submitAll(
mapper связанный с этим полем неправильно отображает ...
делаем _mapper->setCurrentIndex(0); и теперь все правильно
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 12.12.2019, 4:30