Qt i SQL - Nie dodaje wierszy

0

Cześć,
Czy ktoś z was by mi podpowiedział dlaczego poniższy kod wstawia mi tylko jeden wiersz ? A co z resztą ?
Dane w pewnych przypadkach mi się też dublują. W bazie mam 7 kolumn i 0 wierszy. Próbuję wiersze dodawać za pomocą kodu, a nie bezpośrednio w bazie.

    QSqlTableModel *SqlTableModel;
    SqlTableModel = new QSqlTableModel(this, *connect);
    SqlTableModel->setTable(QString("tZamowienia2"));
    SqlTableModel->select();

    QModelIndex idx;
    //qDebug()<<"wierszy"<<SqlTableModel->rowCount(idx);
    //qDebug()<<"kolumn"<<SqlTableModel->columnCount(idx);
    SqlTableModel->insertRow(0,idx);
    idx = SqlTableModel->index(0,1);
    SqlTableModel->setData(idx,"0 wiersz, 1 kolumna",Qt::EditRole);

    idx = SqlTableModel->index(0,2);
    SqlTableModel->setData(idx,"0 wiersz, 2 kolumna",Qt::EditRole);

    idx = SqlTableModel->index(0,3);
    SqlTableModel->setData(idx,"0 wiersz, 3 kolumna",Qt::EditRole);

    SqlTableModel->insertRow(1,idx);
    idx = SqlTableModel->index(1,1);
    SqlTableModel->setData(idx,"1 wiersz, 1 kolumna",Qt::EditRole);

    SqlTableModel->insertRow(2,idx);
    idx = SqlTableModel->index(2,1);
    SqlTableModel->setData(idx,"2 wiersz, 1 kolumna",Qt::EditRole);

    SqlTableModel->insertRow(3,idx);
    idx = SqlTableModel->index(3,1);
    SqlTableModel->setData(idx,"3 wiersz, 1 kolumna",Qt::EditRole);

    //qDebug()<<SqlTableModel->data(idx);

    QTableView *widok = new QTableView();
    widok->setModel(SqlTableModel);

Będę wdzięczny za odpowiedź.

0

Jaka jest struktura tablicy?
Jaki komunikat zwraca SqlTableModel->setData()?

0

Nie rozumiem co miałoby zwracać "setData" ? Bo mnie się wydaje, że ta metoda służy do ustawienia danych w widoku tabeli ?

Jeżeli chodzi o strukturę tabeli to mam tylko jedną tabelę w której jest tylko 7 pól bez unikalnego indeksu dla każdego rekordu.

jeżeli chodzi o zwracanie tekstu to wypisuję sobie ostatni wiersz który chcę dodać jak niżej:

    idx = SqlTableModel->index(3,1);
    qDebug()<<SqlTableModel->data(idx);

i zwraca mi:

QVariant(Invalid)

Nawet nie wiem czemu ? Bo w kodzie wydaje mi się ustawione prawidłowo ?

0
zkubinski napisał(a):

... wydaje mi się ... ustawione prawidłowo ?

Masz rację! Wydaje ci się!

0
qDebug()<<SqlTableModel->setData(idx,"3 wiersz, 1 kolumna",Qt::EditRole);

zwraca

false

0
qDebug()<<"Error"<<SqlTableModel->lastError();

zwraca

QSqlError("", "", "")

0

Chcesz mi wmówić że: qDebug()<<"Error"<< ...,
zapisuje do loga coś co nie rozpoczyna się od słowa Error?
Ładna próba ale wg mnie żart nieudany,

0

nie rozumiem czemu czepiasz się drobnostek ?

Po prostu pominąłem to co tam sam wpisałem ręcznie i wypisałem do tematu to co jest istotne tj. to co chciałeś, ten "Error" był dla mnie abym wiedział, gdzie ten błąd będzie wypisany w komunikatach aplikacji, czy brak tego słowa wniesie coś nowego do dyskusji ?

0

Chcesz mi wmówić że w logu masz: ErrorQSqlError("", "", "")

0

ku Twojemu i mojemu zdziwieniu tak jest na dowód (o ile uwierzysz screen) nie wiem czy tu się załączy ale szkoda, że nie można wstawić obrazka w posta

0

jeszcze zrobiłem tak

    QSqlError err;
    err = SqlTableModel->lastError();
    qDebug()<<"Error"<<err.text();

zwraca mi

Error " "

0

zadziałała funkcja

SqlTableModel->insertRows(0,4,idx);

dodało wiersze, a do wierszy wpisało dane.
Zastanawiam się:

  1. dlaczego nie działa dla każdego z wierszy oddzielnie ?
SqlTableModel->insertRow(0,idx);
  1. co w dokumentacji mają na myśli
bool QSqlTableModel::insertRows(int row, int count, const QModelIndex &parent = QModelIndex())

rozumiem, że int row wstawiam od jakiego numeru wiersza mają być wstawiane kolejne wiersze ?

drugi argument int count rozumiem, że to ma być licznik ile chcę mieć tych wierszy ?

  1. w pkt 2. chyba nie mam racji, bo próbowałem zrobić i nie działało.
SqlTableModel->insertRows(4,8,idx);
  1. Dlaczego po wykorzystaniu funkcji "insertRows" mam * w tabeli, zamiast numerowania wierszy ?

  2. Co trzeba zrobić aby po 2x kliknięciu w tabelę i wprowadzeniu danych do niej zapisało dane w bazie ?

Chciałem dopisać jeszcze, że aby zadziałała funkcja insertRows to musiałem dopisać

SqlTableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);

natomiast jeżeli chciałbym te dane teraz zrzucić do bazy to musiałbym wykorzystać metodę

SqlTableModel->setEditStrategy(QSqlTableModel::OnFieldChange);

tylko problem jest taki, że po wybraniu tej metody nie mam w ogóle tabeli i danych nie zrzuca do bazy danych.

Czy mógłby ktoś objaśnić ? A raczej czy w ogóle ktoś chętnie to objaśni ?
W załączeniu screen z przykładu

0

Na pierwszy punkt znalazłem odpowiedź.

  1. dlaczego nie działa dla każdego z wierszy oddzielnie ?
SqlTableModel->insertRow(0,idx);

Problem polega na tym, że QModelIndex nie mapuje właściwie indeksów. I poniżej lecę z przykładami i od razu wrzucam wyjście z qDebug

Rozważmy poniższy kod

    idx = SqlTableModel->index(0,0);
    SqlTableModel->insertRow(0,idx);
    SqlTableModel->setData(idx,"1",Qt::EditRole);

linijka 1 - mapuję index na index(0,0)
linijka 2 - wstawiam wiersz zaczynający się od numeru ZERO i mapuję jego index z modelu.
linijka 3 - wstawiam do utworzonego wiersza dane ale mimo, że kod jest poprawny wiersz z zawartością się nie pokazuje

Wypisuję zawartość indexu za pomocą kodu

qDebug()<<"1"<<idx;

Wyjście z qDebug 1 QModelIndex(-1,-1,0x0,QSqlTableModel(0x9de6120))

No to teraz analizujemy dalej:
zrobiłem tak

    idx = SqlTableModel->index(0,0);
    SqlTableModel->insertRow(0);
    SqlTableModel->setData(idx,"1",Qt::EditRole);

Kod zmienia się w linijce 2 gdzie usuwam po prostu zmienną idx i po skompilowaniu programu moim oczom pokazuje się poprawnie wstawiony wiersz z właściwymi danymi w swoich miejscach.

wyjście z qDebug pokazuje poprawnie zmapowany index, co mnie zdziwiło
1 QModelIndex(0,0,0x0,QSqlTableModel(0x9de6120))

potem analizuję sobie różne postacie zapisu indexu

    idx = SqlTableModel->index(0,0);
    SqlTableModel->insertRow(0, SqlTableModel->index(0,0));
    SqlTableModel->setData(idx,"1",Qt::EditRole);
    qDebug()<<"1"<<idx;
    qDebug()<<"2"<<SqlTableModel->index(0,0);

i znowu niespodzianka:
SqlTableModel->insertRow(0, SqlTableModel->index(0,0)); - poprawnie wpisuje dane
SqlTableModel->insertRow(0, idx); - w ogóle nie wpisuje danych ani wiersza

dowód (poniżej wyjście z qDebug odpowiednio dla 1 i 2)
1 QModelIndex(-1,-1,0x0,QSqlTableModel(0x9de6120))
2 QModelIndex(0,0,0x0,QSqlTableModel(0x9de6120))

Pytanie teraz mam takie. Jak to w końcu z tym QModelIndex jest ? Jak tego używać poprawnie i co w ogóle jest tu poprawne ?

Wydaje mi się, że zapis

    QModelIndex idx;
    idx = SqlTableModel->index(0,0);
    SqlTableModel->insertRow(0,idx);
    SqlTableModel->setData(idx,"1",Qt::EditRole); 

jest zapisem poprawnym, a mimo to nie działa. WHY ?

0

Dlaczego się tak gimnastykujesz? Co dokładnie chcesz osiągnąć?
Skoro używasz QSqlTableModel to nie prościej użyć po prostu QSqlTableModel::insertRecord?

0

owszem, można i tak ale ja poznaję bibliotekę Qt i chcę wiedzieć jak się jej używa, chcę mieć porównanie co jest wygodniejsze, lepsze etc...

Poza tym używając insertRecord - musiałbym utworzyć zapytanie, wpisać wartość, załadować do modelu i wyświetlić w tablicy - czyli trochę więcej roboty. Ale ta metoda bardziej by się nadała do wybierania i wstawiania danych za pomocą zapytań.

U mnie przypadek jest trochę inny, mam tabelę SQL i chcę tylko do niej dodawać rekordy. Bez tworzenia zapytań.

0

QSqlTableModel/QSqlRelationalTableModel jest właśnie po to żeby nie klepać ręcznie zapytań, tylko skonstruować rekord i podać go do modelu, który sam zadba o całą resztę, tzn. wrzuci dane do bazy i poinformuje podłączone widoki o zmianach.
Potwierdza to dokumentacja

QSqlTableModel is a high-level alternative to QSqlQuery for navigating and modifying individual SQL tables. It typically results in less code and requires no knowledge of SQL syntax.

Polecam Qt Model/View Tutorial.
Warto też spojrzeć na Qt Simple Tree Model Example - tutaj zobaczysz jak działa QModelIndex.

Dłubanie przy setData/insertRows/etc. ma sens jeśli tworzysz własny model na podstawie QAbstractItemModel, QAbstractListModel czy QAbstractTableModel.

0

QSqlTableModel - jest do baz danych opartych o wyświetlanie danych z jednej tabeli - gdy chcę skorzystać z więcej niż jednej tabeli, to muszę zrobić niestandardowy widok.
QSqlRelationalTableModel - jest do tabel które zawierają w sobie klucz obcy z innej tabeli i wyświetlają dane za pomocą listy rozwijanej

czyli w obu przypadkach nie muszę klepać poleceń SQL-owych ? Dobrze rozumiem ?

Natomiast gdybym chciał zrobić swój niestandardowy widok, to muszę skorzystać z klas

  1. Muszę pobrać dane z bazy za pomocą modelu -> QAbstractTableModel tworząc jakieś zapytanie wybierające korzystając z klasy QSqlQuery
  2. dane z pkt.1. muszę wyświetlić w widoku -> QTableView - z kolei ta klasa domyślnie ma tryb tylko do odczytu ale ten tryb można zmienić za pomocą flagi Qt::ItemIsEditable (tylko jeszcze nie wiem jak)
  3. Gdybym chciał mieć w polu danego widoku listę rozwijaną, która pobiera dane z innej tabeli np klucz obcy, to skorzystałbym z klasy QSqlRelationalTableModel i wynik zapytania przekazałbym do klasy QItemDelegate i tam utworzyłbym widżeta QComboBox

Czy dobrze myślę ?

0
zkubinski napisał(a):

QSqlTableModel - jest do baz danych opartych o wyświetlanie danych z jednej tabeli

Tak.

gdy chcę skorzystać z więcej niż jednej tabeli, to muszę zrobić niestandardowy widok.

Nie widok, tylko model. Najlepiej dziedzicząc po QSqlQueryModel

QSqlRelationalTableModel - jest do tabel które zawierają w sobie klucz obcy z innej tabeli

Tak

i wyświetlają dane za pomocą listy rozwijanej

To zależy jaki item delegate zostanie ustawiony.

czyli w obu przypadkach nie muszę klepać poleceń SQL-owych ? Dobrze rozumiem ?

Tak.

Natomiast gdybym chciał zrobić swój niestandardowy widok, to muszę skorzystać z klas

Chodzi Ci o widok czy model?

  1. Muszę pobrać dane z bazy za pomocą modelu -> QAbstractTableModel tworząc jakieś zapytanie wybierające korzystając z klasy QSqlQuery

Albo QSqlQueryModel. (przykłady)

  1. dane z pkt.1. muszę wyświetlić w widoku -> QTableView - z kolei ta klasa domyślnie ma tryb tylko do odczytu ale ten tryb można zmienić za pomocą flagi Qt::ItemIsEditable (tylko jeszcze nie wiem jak)

O tym czy coś można edytować czy nie decydujesz w modelu (nadpisując funkcje setData i flags). Widok się do tego dostosuje.

  1. Gdybym chciał mieć w polu danego widoku listę rozwijaną, która pobiera dane z innej tabeli np klucz obcy, to skorzystałbym z klasy QSqlRelationalTableModel i wynik zapytania przekazałbym do klasy QItemDelegate i tam utworzyłbym widżeta QComboBox

Mniej więcej. Tutaj masz przykład jak działa i jak skonfigurować QSqlRelationalTableModel.
Rozwijana lista dla klucza obcego jest ready-to-use w postaci QSqlRelationalDelegate

0

Chodzi Ci o widok czy model?

Chodzi mi o model i widok. W sumie jest to opisane tu -> Programowanie - Model/Widok i zaraz na początku tej dokumentacji jest rysunek który ilustruje jak to wszystko działa. Czyli:
Dane ładowane są do Modelu, z Modelu do Widoku - jest też taki pośrednik jak Delegat, którego zadaniem jest wyświetlanie np listy rozwijanej na podstawie danych otrzymanych np z innego modelu z np z innej tabeli i w sumie mój poprzedni post rozpisałem krokowo czy dobrze całą ideę działania rozumiem i czy klasy które bym zastosował współpracowałyby ze sobą. Poruszam ten temat, ponieważ muszę sobie to poukładać żeby wiedzieć jak się do tego zabrać

0

Powiedz, co masz na myśli pisząc

zkubinski napisał(a):

swój niestandardowy widok

Ja "widok" rozumiem jako dowolną klasę dziedziczącą po QAbstractItemView i w tym kontekście nie ma żadnego znaczenia jak zaimplementowany jest model(czyli cokolwiek dziedziczące po QAbstractItemModel).

Klasy typu QSqlTableModel czy QSqlRelationalTableModelgeneral purpose, więc mają też swoje ograniczenia. Samemu musisz zdecydować czy w Twoim przypadku będą wystarczające czy jednak będziesz musiał napisać własny model. Oprócz tego masz jeszcze proxy models: fitrowanie,sortowanie,etc.
Bez konkretnego przypadku jedyne co można napisać to: tak, użyj model/view framework.

0

już doszedłem czemu nie chciało mi dodawać wierszy.
Brakowało mi trzeciego parametru QModelIndex()

Zamiast:

    QModelIndex idx;
    idx = SqlTableModel->index(0,0); // <- Domyślny parametr `QModelIndex()`
    SqlTableModel->insertRow(0,idx);
    SqlTableModel->setData(idx,"1",Qt::EditRole); 

Powinno być

    QModelIndex idx;
    idx = SqlTableModel->index(0,0,QModelIndex()); // <- Ręcznie dodany parametr `QModelIndex()`
    SqlTableModel->insertRow(0,idx);
    SqlTableModel->setData(idx,"1",Qt::EditRole); 

Jeżeli index ma rodzica to zamiast podać QModelIndex() należy podać rodzica np

    QModelIndex idxRodzic;
    model->index(0,0,QModelIndex());

    QModelIndex idxDziecko;
    model->index(1,0,idxRodzic);

Opisane jest to tu -> Wykład

1 użytkowników online, w tym zalogowanych: 0, gości: 1