QDataWidgetMapper и изменение foregn key |
Здравствуйте, гость ( Вход | Регистрация )
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 - пользователь выбирает нужное или _вводит новое_, я получаю строку которую он выбрал\ввел. Как мне теперь правильно изменить значение в главной модели и все это сохранить в бд? |
|
|
Begemot |
30.5.2010, 12:41
Сообщение
#2
|
Студент Группа: Новичок Сообщений: 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. ставим курсор в средний эдит В этот момент строка которую мы поменяли и которая уже сохранена в базу судя по логам делегата, возвращяется к первоначальному значению. |
|
|
Текстовая версия | Сейчас: 25.11.2024, 13:16 |