Zaznaczenie w QSqlTableModel na QTableView

0

Witam, mam problem, u mnie w aplikacji jest funkcja edit przez którą edytuję się pewien obiekt. Wszystko działało na podstawie zaznaczenia w TableView i przenoszenia to do modela jako w sumie ten sam rekord przez id,
dopóki nie dodałem do programu możliwość sortowania w w QTableViewie :( teraz niestety wybiera mi rekord inny niż jest zaznaczony, myślałem długo i nie mam pomysłu jak to rozwiązać.

void Supplies_menu::on_btnedit_clicked()
{

    ///QModelIndex index = ui->tableView->selectionModel()->selectedIndexes().first();
    //  ui->tableView->setSortingEnabled(true);
    if(ui->tableView->selectionModel()->hasSelection())
    {
        QModelIndex index = ui->tableView->selectionModel()->selectedIndexes().first();
        
        if (index.isValid())
        {
            int selectedRow;
            selectedRow = index.row();
            Adding *add = new Adding(selectedRow,this);
            add->setModal(true);
            add->database_open();
            add->exec();
            table_display();
        }


        else {QMessageBox::critical(this,"error","error");}


    }
    else {
        QMessageBox::information(this,"Selection","You haven't selected any index");
    }
}

void Supplies_menu::on_btndel_clicked()
{
    if(ui->tableView->selectionModel()->hasSelection())
    {
        QModelIndex index = ui->tableView->selectionModel()->selectedIndexes().first();

        if (index.isValid())
        {
            ////////////////////WPISZ TU DIALOG czxy jest siur
            int row = index.row();
            Adding *add = new Adding(row,this);
            add->database_open();
            add->database_delete();
            table_display();
        }
        else {
            QMessageBox::warning(this,"Selected Index","Your selection is wrong. Try again");
        }
    }
    else {
        QMessageBox::information(this,"Selection","You haven't selected any index");
    }
}
void Adding::database_edit()
{
    database_open();
    if(db.open())
    {
           query.prepare("SELECT * FROM produkty");
        MySqlTableModel *model = new  MySqlTableModel(this,db);
        query.exec();
        model->wywSetQuery(query);
        nm = model->record(IdInView).value("name").toString();
        nt = model->record(IdInView).value("notes").toString();
        QString Kc = model->record(IdInView).value("kcal").toString();
        QString Car = model->record(IdInView).value("carbonates").toString();
        QString Pro = model->record(IdInView).value("proteins").toString();
        QString Fa = model->record(IdInView).value("fats").toString();
        QString Wei = model->record(IdInView).value("weight").toString();
        dat = model->record(IdInView).value("date").toDate();
        iconT = model->record(IdInView).value("img").toByteArray();
        Type = model->record(IdInView).value("type").toString();
        IdInBase = model->record(IdInView).value("productid").toInt();

        ui->lnname->setText(nm);
        ui->lnnotes->setText(nt);
        ui->lnkcal->setText(Kc);
        ui->lncarbonates->setText(Car);
        ui->lnproteins->setText(Pro);
        ui->lnfats->setText(Fa);
        ui->lnweight->setText(Wei);

        if(Type=="1")
        {
            ui->btn1->setVisible(true);
            ui->btn2->setVisible(false);
            ui->btn3->setVisible(false);
            ui->btn4->setVisible(false);
        }
        else if(Type=="2")
        {
            ui->btn1->setVisible(false);
            ui->btn2->setVisible(true);
            ui->btn3->setVisible(false);
            ui->btn4->setVisible(false);
        }
        else if(Type=="3")
        {
            ui->btn1->setVisible(false);
            ui->btn2->setVisible(false);
            ui->btn3->setVisible(true);
            ui->btn4->setVisible(false);
        }
        else if(Type=="4")
        {
            ui->btn1->setVisible(false);
            ui->btn2->setVisible(false);
            ui->btn3->setVisible(false);
            ui->btn4->setVisible(true);
        }


        QImage image; image.loadFromData(iconT);
        ui->icon->setPixmap(QPixmap::fromImage(image));




        //img.loadFromData(Icon);
        // ui->icon->setPixmap(QPixmap::fromImage(img));

        // QVariant currentIcon = model->data(model->index(id,2),0);
        //  QByteArray bytes = currentIcon.toByteArray();



    }
    else QMessageBox::warning(this,"Connecting","Database conection failed!");
}

Tam potem jest dodawanie do database itd spokojnie ;)

0

I jeszcze jedno pytanie mi się nasuwa dało by się aby domyślnie po kliknięciu na header kolumny w tableviewie był on nie rosnący a malejący za pierwszym kliknięciem?

1

Pierwszy z brzegu wynik w Google

Reimplementujesz w swoim modelu funkcję headerData i ustawiasz tryb sortowania dla odpowiedniej kolumny poprzez role Qt::InitialSortOrderRole

2

E tam reimplementuje headerData - starczy wywołać QTableView::setSortingEnabled()

2

A co do zaznaczenia tabeli, toć w innym wątku klarowałem - selectionModel()->selectedRows(), i tabeli pokazującej nastawić setSelectionBehavior(QAbstractItemView::SelectRows), i setSelectionMode(QAbstractItemView::SingleSelection), a jak wyciągniesz numer wiersza to użyć QSqlRecordu. Tym sposobem czy posortowane, czy nie posortowane otrzymasz rzeczywisty numer wiersza do zabawy.

1
MasterBLB napisał(a):

E tam reimplementuje headerData - starczy wywołać QTableView::setSortingEnabled()

Tak, ale domyślnie sortowanie jest ustawione na Qt::AscendingOrder, a OP chce żeby było Qt::DescendingOrder.
Czyli albo musi wywołać dodatkowo sortByColumn(), albo reimplementować w modelu headerData().

2

Jak sobie setSortingEnabled() włączy na tabeli to już będzie mógł wyklikać na nagłówku w jakiej kolejności ma być posortowane. Ale jest inny dzynks, na dzień dobry sortowanie jest robione na pierwszej kolumnie, jak autor chce aby początkowe sortowanie używało innej niż pierwsza to już bez QTableView::sortByColumn() faktycznie się nie obędzie.

EDYTKA:
setSortingEnabled() włącza możliwość sortowania poprzez kliknięcie nagłówka, domyślnie włącza sortowanie na 1 kolumnie,i kolejność DESC.
sortByColumn() pozwala posortować jakąś kolumnę według podanej kolejności, ale tylko z poziomu kodu programu - użytkownik nie może sobie klikać.

Czyli tak, potrzebujesz Bracie po kolei:

  • włączyć setSortingEnabled()
  • dać sortByColumn(0, ASC) -> dzięki temu jak kliknie nagłówek po raz pierwszy będzie kolejność DESC.
  • dać sortByColumn(1, DESC) -> jak wyżej, i to będzie stan początkowy tabeli.

O taki efekt ci chodzi?

0
MasterBLB napisał(a):

Jak sobie setSortingEnabled() włączy na tabeli to już będzie mógł wyklikać na nagłówku w jakiej kolejności ma być posortowane. Ale jest inny dzynks, na dzień dobry sortowanie jest robione na pierwszej kolumnie, jak autor chce aby początkowe sortowanie używało innej niż pierwsza to już bez QTableView::sortByColumn() faktycznie się nie obędzie.

EDYTKA:
setSortingEnabled() włącza możliwość sortowania poprzez kliknięcie nagłówka, domyślnie włącza sortowanie na 1 kolumnie,i kolejność DESC.
sortByColumn() pozwala posortować jakąś kolumnę według podanej kolejności, ale tylko z poziomu kodu programu - użytkownik nie może sobie klikać.

Czyli tak, potrzebujesz Bracie po kolei:

  • włączyć setSortingEnabled()
  • dać sortByColumn(0, ASC) -> dzięki temu jak kliknie nagłówek po raz pierwszy będzie kolejność DESC.
  • dać sortByColumn(1, DESC) -> jak wyżej, i to będzie stan początkowy tabeli.

O taki efekt ci chodzi?

Właśnie nie, liczyłem na to żeby domyślnie jak user kliknie na header jakiejś kolumny mu weszło na desc (czy tam na asc w drugiej) żeby mógł sobie klikać. Bo tak to mam na razie, może sobie klikać oraz zaczyna się tabela wyświetlać posortowana wg kolumny 8 i DESC.
Czyli zamierzany przeze mnie efekt jest nieosiągalny?

0
MasterBLB napisał(a):

A co do zaznaczenia tabeli, toć w innym wątku klarowałem - selectionModel()->selectedRows(), i tabeli pokazującej nastawić setSelectionBehavior(QAbstractItemView::SelectRows), i setSelectionMode(QAbstractItemView::SingleSelection), a jak wyciągniesz numer wiersza to użyć QSqlRecordu. Tym sposobem czy posortowane, czy nie posortowane otrzymasz rzeczywisty numer wiersza do zabawy.

Nie do końca rozumiem, mam numer rekordu w tableviewie który jest już posortowany czyli nie pokrywa się on z numerem w bazie danych więc dostaje przez to potem do edycji czy usunięcia inny obiekt niż zaznaczyłem w tableviewie. Jak za pomocą QSqlRecourdu?

1

Pewnie osiągalny, ale swoje zamiary opisujesz ciut niejasno. No nic, skoro i tak reimplementujesz model do tej tabeli możesz użyć czegoś takiego:

QVariant MyModel::headerData(int section, Qt::Orientation orientation, role) const 
{
    if (role == Qt::InitialSortOrderRole) 
    {
        // use section, orientation, ...
        return QVariant::fromValue(Qt::DescendingOrder); // or maybe Ascending
    }
    return ModelZJakiegoDziedziczysz::headerData(section, orientation, role);
} 

jeśli to zastosujesz, to wtedy bez sortByColumn()

1
Xezolpl napisał(a):

Nie do końca rozumiem, mam numer rekordu w tableviewie który jest już posortowany czyli nie pokrywa się on z numerem w bazie danych więc dostaje przez to potem do edycji czy usunięcia inny obiekt niż zaznaczyłem w tableviewie. Jak za pomocą QSqlRecourdu?

A tak - QSqlRecord QSqlTableModel::record(int row) const. Za row podstawiasz to, co otrzymałeś z selectionModelu, QSqlRecord z kolei zagwarantuje powiązanie z rzeczywistym wierszem jaki siedzi w bazie danych.

0
MasterBLB napisał(a):
Xezolpl napisał(a):

Nie do końca rozumiem, mam numer rekordu w tableviewie który jest już posortowany czyli nie pokrywa się on z numerem w bazie danych więc dostaje przez to potem do edycji czy usunięcia inny obiekt niż zaznaczyłem w tableviewie. Jak za pomocą QSqlRecourdu?

A tak - QSqlRecord QSqlTableModel::record(int row) const. Za row podstawiasz to, co otrzymałeś z selectionModelu, QSqlRecord z kolei zagwarantuje powiązanie z rzeczywistym wierszem jaki siedzi w bazie danych.

No dobra ale teraz mam coś takiego jako QSqlRecord.

QSqlRecord rec = model->record(IdInView);

A dane pobieram, edytuje i wstawiam w formie

/*String*/  nm = model->record(IdInView).value("name").toString();

I później nm jest ustawiane textem lnedita z którego zczytuje dane i umieszczam je w bazie

Czyli muszę mieć tego rec jako inta - numer wiersza w bazie danych i potem IdInView ustawić na wartość tego inta

0

A więc nazywa się MySqlTableModel:

#ifndef MYSQLTABLEMODEL_H
#define MYSQLTABLEMODEL_H
#include <QSqlTableModel>
#include <QObject>

class MySqlTableModel : public QSqlTableModel
{
public:

    explicit MySqlTableModel( QObject *parent, QSqlDatabase db);


   void wywSetQuery(const QSqlQuery &query);
   QVariant MyheaderData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
};

#endif // MYSQLTABLEMODEL_H
#include "mysqltablemodel.h"

MySqlTableModel::MySqlTableModel(QObject *parent, QSqlDatabase db)
     : QSqlTableModel(parent, db)
 {

     db = db.isValid() ? db : QSqlDatabase::database();
 }

void  MySqlTableModel::wywSetQuery(const QSqlQuery &query)
{
   setQuery(query);//setQuery() jest protected, ale wywołujesz je wewnątrz metody klasy która odziedziczyła QSqlTableModel - dla tej klasy metoda setQuery() jest traktowana jak publiczna.
}
QVariant MySqlTableModel::MyheaderData(int section, Qt::Orientation orientation, int role) const
{
    if ((section==1)||(section==4)||(section==5)||(section==6)||(section==7)||(section==8))
    {

        if (role == Qt::InitialSortOrderRole) {

            return QVariant::fromValue(Qt::DescendingOrder);
        }
        else return QVariant::fromValue(Qt::AscendingOrder);
      }

}

A tu m.in jest używany bo nie ma sensu reszty pokazywać bo byłoby to koło 10funkcji. Tutaj stylizuję tą tabelę i modela oraz podpinam do tableViewa.

void Supplies_menu::table_display(int typ)
{
    ////STWORZENIE TABELI
    Adding *add = new Adding(rec,this);
    add->database_open();
    add->getDatabase(db2);


    QSqlQuery *query2= new QSqlQuery();
    if(typ==0)
    {
        query2->prepare("SELECT * FROM produkty ORDER BY weight");
                ui->tableView->sortByColumn(8);
    }
    else if(typ==1)
    {
        query2->prepare("SELECT * FROM produkty WHERE type=1 ORDER BY weight");
        ui->tableView->sortByColumn(8);
    }
    else if(typ==2)
    {
        query2->prepare("SELECT * FROM produkty WHERE type=2 ORDER BY weight");
        ui->tableView->sortByColumn(8);
    }
    else if(typ==3)
    {
        query2->prepare("SELECT * FROM produkty WHERE type=3 ORDER BY weight");
        ui->tableView->sortByColumn(8);
    }
    else if(typ==4)
    {
        query2->prepare("SELECT * FROM produkty WHERE type=4 ORDER BY weight");
        ui->tableView->sortByColumn(8);
    }
    query2->exec();
    MySqlTableModel *model = new MySqlTableModel(this,db2);




    /// STYLIZACJA TABELI I WKLADANIE BAZY DANYCH


    model->setEditStrategy(QSqlTableModel::OnFieldChange);
    model->wywSetQuery(*query2);
    ui->tableView->setModel(model);
    ui->tableView->hideColumn(0);
    ui->tableView->hideColumn(3);
    ui->tableView->hideColumn(10);
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setColumnWidth(1,35);
    ui->tableView->setColumnWidth(2,222);
    ui->tableView->setColumnWidth(8,110);
    ui->tableView->setColumnWidth(9,75);
    ui->tableView->resizeRowsToContents();



    add->database_close();

    QSortFilterProxyModel *m=new QSortFilterProxyModel(this);
    m->setDynamicSortFilter(true);
    m->setSourceModel(model);
    ui->tableView->setModel(m);
    ui->tableView->setSortingEnabled(true);
    ui->tableView->sortByColumn(8,Qt::DescendingOrder);


}
1
Xezolpl napisał(a):

Właśnie nie, liczyłem na to żeby domyślnie jak user kliknie na header jakiejś kolumny mu weszło na desc (czy tam na asc w drugiej) żeby mógł sobie klikać. Bo tak to mam na razie, może sobie klikać oraz zaczyna się tabela wyświetlać posortowana wg kolumny 8 i DESC.
Czyli zamierzany przeze mnie efekt jest nieosiągalny?

Spróbuj poustawiać tryb sortowana dla wybranych kolumn ZANIM podłączysz model do widoku.

tableView->sortByColumn(0, Qt::DescendingOrder);
tableView->sortByColumn(1, Qt::AscendingOrder);
/*...*/
tableView->setModel(model);

Co do opcji z reimplementacją model::headerData() to nie wiem czy o czymś zapomniałem, ale ni huhu nie mogę złapać roli Qt::InitialSortOrderRole. Tak jakby QTableView nigdy takiego żądania nie wysyłało. Może @MasterBLB wie coś wiecej :P

0
tajny_agent napisał(a):
Xezolpl napisał(a):

Właśnie nie, liczyłem na to żeby domyślnie jak user kliknie na header jakiejś kolumny mu weszło na desc (czy tam na asc w drugiej) żeby mógł sobie klikać. Bo tak to mam na razie, może sobie klikać oraz zaczyna się tabela wyświetlać posortowana wg kolumny 8 i DESC.
Czyli zamierzany przeze mnie efekt jest nieosiągalny?

Spróbuj poustawiać tryb sortowana dla wybranych kolumn ZANIM podłączysz model do widoku.

tableView->sortByColumn(0, Qt::DescendingOrder);
tableView->sortByColumn(1, Qt::AscendingOrder);
/*...*/
tableView->setModel(model);

Co do opcji z reimplementacją model::headerData() to nie wiem czy o czymś zapomniałem, ale ni huhu nie mogę złapać roli Qt::InitialSortOrderRole. Tak jakby QTableView nigdy takiego żądania nie wysyłało. Może @MasterBLB wie coś wiecej :P

Niestety nadal to samo, zaczynają się wszystkie z ascending :/

0
MasterBLB napisał(a):
Xezolpl napisał(a):

Nie do końca rozumiem, mam numer rekordu w tableviewie który jest już posortowany czyli nie pokrywa się on z numerem w bazie danych więc dostaje przez to potem do edycji czy usunięcia inny obiekt niż zaznaczyłem w tableviewie. Jak za pomocą QSqlRecourdu?

A tak - QSqlRecord QSqlTableModel::record(int row) const. Za row podstawiasz to, co otrzymałeś z selectionModelu, QSqlRecord z kolei zagwarantuje powiązanie z rzeczywistym wierszem jaki siedzi w bazie danych.

A co mogę zrobić jeśli mój model nie dziedziczy z QSqlTableModel tylko z QIdentityProxyModel i nie ma funkcji record?

0

Od biedy możesz użyć funkcji sourceModel() (klik), żeby dostać się do modelu na którym działa proxy.
Ale wygodniej po prostu trzymać wskaźnik na model jako składową klasy.

0
tajny_agent napisał(a):

Od biedy możesz użyć funkcji sourceModel() (klik), żeby dostać się do modelu na którym działa proxy.
Ale wygodniej po prostu trzymać wskaźnik na model jako składową klasy.

Ok, ustawiłem na wskaźnik, zrobiłem gettera do QSqlTableModelu

int x = ui->tableView->selectionModel()->selectedIndexes().first().row();
   QSqlRecord rec = dbManager->getSqlTableModel()->record(x);
   int id = rec.value("productid").toInt();
   qDebug() << id;

lecz mimo wszystko id jest z tableviewa nie z modelu czyli zaleznie od posortowania daje rozne wyniki dla pojedynczego produktu.

0

Hmm, nie rozumiem. O to chodzi w sortowaniu :>

0
tajny_agent napisał(a):

Hmm, nie rozumiem. O to chodzi w sortowaniu :>

Yyyy no to tak, mam baze danych w niej tabele z tymi produktami, a w programie tableviewa który ją wyświetla z modelu. Tableview ma sorting endabled i jak user klika sobie w headery to zmienia sobie sposob sortowania. Ja chce się dostac niezaleznie od posortowania do wlasciwego id zeby moc go zmienic w bazie gdy user go zaznaczy i kliknie przycisk (edytuj/usun)

1

Jeśli używasz proxy to spróbuj najpierw zmapować indeks

auto proxyIndex = ui->tableView->selectionModel()->selectedIndexes().first();
auto x = proxyModel->mapToSource(proxyIndex).row();
   QSqlRecord rec = dbManager->getSqlTableModel()->record(x);
   int id = rec.value("productid").toInt();
   qDebug() << id;

QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const

0
tajny_agent napisał(a):

Jeśli używasz proxy to spróbuj najpierw zmapować indeks

auto proxyIndex = ui->tableView->selectionModel()->selectedIndexes().first();
auto x = proxyModel->mapToSource(proxyIndex).row();
   QSqlRecord rec = dbManager->getSqlTableModel()->record(x);
   int id = rec.value("productid").toInt();
   qDebug() << id;

QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const

Tyle że mój model nie dziedziczy chyba z proxyModel, i ogólnie to nie mam takiej opcji (map to source) moj model tj tak

QAbstractItemModel* DbManager::imageTableModel()
{
    if (!m_budleDecodedModel && m_db.isOpen())
    {
        m_tableModel = new QSqlTableModel(this, m_db);
        m_tableModel->setTable("products");
        m_tableModel->select();

        m_sortingModel =new QSortFilterProxyModel(this);
        m_sortingModel->setDynamicSortFilter(true);
        m_sortingModel->setSourceModel(m_tableModel);

        m_budleDecodedModel = new SqlBlobDecoderModel(this);
        m_budleDecodedModel->setSourceModel(m_sortingModel);
        m_budleDecodedModel->setImageColumn(TableImageColumn);
    }
    return m_budleDecodedModel;
}

gdzie

class SqlBlobDecoderModel : public QIdentityProxyModel

/EDIT
Nawet próbując tak

   QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel;
   proxyModel->setSourceModel(dbManager->imageTableModel());
   auto proxyIndex = ui->tableView->selectionModel()->selectedIndexes().first();
   auto x = proxyModel->mapToSource(proxyIndex).row();
      QSqlRecord rec = dbManager->getSqlTableModel()->record(x);
      int xD = rec.value("productid").toInt();
      qDebug() << xD;

to wbija error że QSortFilterProxyModel: index from wrong model passed to mapToSource 0

0

Nie wiem jak, ale udało mi się rozwiązać ten problem samemu xD
żadne w/w sposoby nie zadziałały więc:
Productid czyli unikalny klucz dla produktu jest jako pierwszy w bazie danych
Normalnie mimo sortowania teraz działa bo gdy user klika przycisk to sortowanie robi się na ułamek sekundy ASC na product id, wtedy łapiemy numer indexu, bierzemy jego data() i zmieniamy na inta. Proste i aż serduszko cieszy ^^ (spędzało mi to sen z powiek przez 2tygodnie)

void Supplies_menu::on_btnedit_clicked()
{
   ui->tableView->sortByColumn(0,Qt::AscendingOrder);
   auto idx = ui->tableView->selectionModel()->selectedIndexes().first();
   QVariant var = dbManager->imageTableModel()->data(idx);
   int id = var.toInt();
   qDebug() << id;
   ui->tableView->sortByColumn(8,Qt::DescendingOrder);

   Product product(this);

   dbManager->getProductValuesFromDatabase(id);

   product.loadData(dbManager->getByteArray(),dbManager->getName(),dbManager->getNotes(),dbManager->getKcal(),
                    dbManager->getCarbonates(),dbManager->getProteins(),dbManager->getFats(),
                    dbManager->getWeight(),dbManager->getDate(),dbManager->getType());

   product.setModal(true);
   product.exec();

   if(dbManager->editProduct(product.getImageToByteArray(),product.getName(),product.getNotes(),product.getKcal(),
                         product.getCarbonates(),product.getProteins(),product.getFats(),
                         product.getWeight(),product.getDate(),product.getType(),id))
                           QMessageBox::information(this,"Success","Successfully applied changes to the product.");
   else QMessageBox::critical(this,"Editing error","Could not edit product in the database");
}

Teraz mimo sortowania czy usuniętych rekordów z bazy (bo productid idzie cały czas +1) wszystko działa poprawnie :)

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