PDA

View Full Version : Howto swap list of data in a tableview



andsun
20th March 2015, 14:30
Hi guys,

I have come across a problem the is to hard, sofar for me. Have an application with several dialogs, and one is a dialog built up with a combobox and a tableview + buttons

I have a
struct limits { float lower[10], float upper [10}] that is written, based on 10 elements and 20 streams to a data file every 3 -5 minutes together with other data in my system.
The Filehandler is pointing to next position to write and when reaching the max values, return to the beginning of the file to continue. It holds 10 000 rows of data.

Now I want to present major data and limits based on a selected value in a combobox. That is, I create a QStandardItemModel models [ NoOfStreams ] and fills each Model with QStandardItem to be presented in a specific column and row.

Sofar so good, but when I select a stream and want to fetch the QStandardItemModel for the selected stream, either it doesn't show anything or it chrashes, depending och solution. The swaping is done in the View and the Models were created in the Models area and sent to the View's Q_Property.

So my question is How shall I perform the swapping, or am I doing (thinking) this wrong?

My ideas are from example FrozenColumn and modified


in Model
read(filehandler, container, sizeOfLimits)

QStandardItemModel models[streams]
//loop and fill with QStandardItem
QStandardItem * newItem
//for each stream,
// for each row,
// for each col
newItem = new QStandardItem
newItem->setText(container[col]->textValue)
models[stream].setItem(row, col, newItem)
//
// asign models[] to VIEW ui's Q_PROPERTY
gui->ui->setModel(models)

// VIEW
// sofar OK
// when comboBox index changed
// verify in scope
// this crasches
tableview->setModel(Models()[cboBox_Index])

// but not this, and it displays nothing in the tableview
QStandardItemModel *model = new QStandardItemModel(&m_Model[cboBox_index])
tableview->setModel(model)

ChrisW67
20th March 2015, 20:13
It is very unclear what message your post and pseudo code is trying to convey. Certainly nothing in there looks like a "swap".

If you want to change the model displayed by a view simply call the view's setModel() function with the new model.

andsun
21st March 2015, 08:41
Hi ChrisW67

Sorry, for the blurry question.
I've created an array of QStandardItemModel's and added data divided into one Model per stream
Figured that the easiest way is to put stream number in a combobox and when changed simply use stream number as index to find correct model in the array.
Then take the array and use setModel(modelStreamnr).

However I don't get this to work. Nothing displays. Thinking I'm doing something wrong in creating the model, adding data or obstructing some rule

Above pseudo, line 22 make a setModel, by geting the Q_PROPERTY model array and index out the correct one to be returned and set in setModel

Thanks for quick answer

wysota
21st March 2015, 09:52
IMO the easiest solution is to have one model for all your streams and either have a proxy filter model to filter out the things you don't want or to make each stream a top-level item of the view and stream data children of a respective item and then to only set a different index as the root index of the view to show a given stream.

Of course your original approach would have worked too. The problem doesn't seem related to Qt but rather to C++ and indexing a C-array.

andsun
21st March 2015, 12:24
Hi, interresting, never heard/thought about either of your suggested solutions, but must try them out. Feels like stream as top level might work best... mainly because I don't understand the proxy filter solution but hope the Qt manual will help. :confused:

Agree on point about my solution, must be in my c++ code. :o

andsun
23rd March 2015, 07:51
To quit the thread with an answer ;)
What I wanted to do is not supported, as I can find out about from "Advanced-Qt-Programming-Creating-Great-Software-with-Cpp-and-Qt-4".

Tree models work in terms of parents and children, where an item’s row is
its position in its parent’s list of children. (In theory, a tree model can be a
recursive tree of tables, but none of Qt’s views supports this.)

However, testing with wysotas suggestion to use QProxyFilter works just fine, only have to add one column with streams to filter on, and hide it. Found example here http://doc.qt.io/qt-5/qsortfilterproxymodel.html
and here as an example http://doc.qt.io/qt-5/qtwidgets-itemviews-customsortfiltermodel-example.html

Thanks guys -this can be closed for now

wysota
23rd March 2015, 09:03
What exactly is "not supported"?

andsun
23rd March 2015, 10:01
Hi wysota,

Haven't tried it out fully yet, but my problem displays where it is not supported, as I understand it.

I don't know for sure but as I got it, the tree model with subitems of tables can't be displayed in, at the time existing Views like QTreeView.
This is where my problem lays, and why I implemented the proxy filter to get around it.
I filter out the "table" within one single model, instead of fetching a model that holds each table from the array of models.

Make sense? What do you think?

Sorry to say haven't found any newer, good books on the subject (Advanced Qt programming with Qt 5.nn), suggestions on that?

wysota
23rd March 2015, 11:41
the tree model with subitems of tables can't be displayed in, at the time existing Views like QTreeView.
Why not? QTreeView, as the name suggests, can display tree models. Not that you need that in my opinion, I suggested using root indexes to display a list/table from a tree model.


#include <QtWidgets>

int main(int argc, char **argv) {
QApplication app(argc, argv);
QWidget w;
QVBoxLayout *l = new QVBoxLayout(&w);
QComboBox *cb = new QComboBox;
l->addWidget(cb);
QListView *view = new QListView;
l->addWidget(view);
QStandardItemModel model;
for(int s=0;s<10;++s) {
QStandardItem *stream = new QStandardItem(QString("Stream %1").arg(s+1));
int cnt = qrand() % 20;
for(int e = 0; e < cnt; ++e) {
QStandardItem *item = new QStandardItem(QString("Entry %1 of stream %2").arg(e+1).arg(s+1));
stream->appendRow(item);
}
model.appendRow(stream);
}

view->setModel(&model);
cb->setModel(&model);
view->setRootIndex(model.index(cb->currentIndex(), 0));
QObject::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [&](int idx) { view->setRootIndex(model.index(idx, 0)); });
l->addWidget(new QLabel("Full model:"));
QTreeView *tview = new QTreeView;
l->addWidget(tview);
tview->setModel(&model);
w.show();
return app.exec();
}

andsun
24th March 2015, 07:01
Hi, thks for code sample
Think I'm too inexperianced here though. don't get

QObject::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [&](int idx) { view->setRootIndex(model.index(idx, 0)); });


Guess somehow explained(?) in the old style that I grasp/still use ... and in pseudo stylish it means like:
connect ( cboBox, SIGNAL( currentIndexChanged(int idx)), treeView, SLOT( treeView->SetRootIndex(int idx)))

:confused:

ChrisW67
24th March 2015, 09:02
This line uses the Qt5 new-style connect() and a C++11 lambda function in place of a named slot function.


QObject::connect(
cb,
//^^^ sender object
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
//^^^ a PointerToMemberFunction for the currentIndexChanged(int) signal function and not the currentIndexChanged(QString) one
[&](int idx) { view->setRootIndex(model.index(idx, 0)); }
//^^^ a C++11 lambda function accepting an int argument (Functor)
);

// equivalent to the traditional Qt

connect(
cb,
SIGNAL(QComboBox::currentIndexChanged(int)),
SLOT(someSlot(int))
);

// with a slot function
void someSlot(int idx) {
view->setRootIndex(model.index(idx, 0));
}

andsun
24th March 2015, 13:09
Hi ChrisW67,

perfect, now I get it.
Also, I was missing the CONFIG += c++11 in the .pro file. And now it compiles and works.
Shall play around with the code and in case I have trouble I'll be back.

Added after 1 23 minutes:

Hi again...

I don't seem to get out what I want. Like an excel sheet with grouped rows and columns
11027

Have tried to modify the above code, in its simplest form I've done this, added items to a QList and appended the list as columns
Am I way of here, should I use a table instead or is it doable??


or(int s=0;s<10;++s) {
QStandardItem *stream = new QStandardItem(QString("Stream %1").arg(s+1));
int cnt = qrand() % 20;
QList<QStandardItem *> items;
for(int e = 0; e < cnt; ++e) {

QStandardItem *item = new QStandardItem(QString("Column 1 tablerow %1 of stream %2").arg(e+1).arg(s+1));
items.append(item);

QStandardItem *subitem = new QStandardItem(QString("Column 2 of stream %2").arg(e+1).arg(s+1));
items.append(subitem);

}
stream->appendColumn(items);
model.appendRow(stream);
}


/anders

ChrisW67
24th March 2015, 20:37
Please post code inside
... tags.

You asked to show a table for a stream selected separately. Wysota's example does that but using a single tree model for the entire data set rather than your original array-of-models approach. The single list view in the example displays a subset of the tree based on the combo box selection. Using a table view rather than a list view is just a matter of changing the view object class and populating more than one column of the model.

If you want to display the entire tree in a tree view then create a QTreeView and set the tree model as its model. Nothing more to it. No combo box required.

No view in Qt out-of-the-box does anything visually like the Excel grouping screen capture.

andsun
25th March 2015, 06:49
Hi

True, & sorry for missing code tags

I've experimented and as you say ChrisW67, just change the view... as well as one get the answer on what one asks for ;)

Thanks a lot guys