Using different Views with one model and different data
-
Sorry for my bad explanation.
Here is the main view.
This application should load Data files *.mf4 and store this data. The signals which are in this file are listen in the Listview "Signals". If you click on one of this signals, you will see a preview of this signal in the "Preview" Listview. The central Widget contains a tabwidget, where you can add charts. At the end you should be able to drag a Signal name in one of this charts, and the signal will be showed in this chart.
So I need the Signal name in the "Signals" Listview and the Signal data in the "Preview" and in the central widget charts.
-
Ok, then isn't it rather a tree style model that you should have ?
Or if still a table style, you should maybe consider creating an object that encapsulates your signals data and build your model on top of that.
-
@SGaist said in Using different Views with one model and different data:
Ok, then isn't it rather a tree style model that you should have ?
You mean something like this:
Sounds good for me, thanks@SGaist said in Using different Views with one model and different data:
Or if still a table style, you should maybe consider creating an object that encapsulates your signals data and build your model on top of that
You mean. creating for every signal a own model? Putting The signal names in a seperate model than the data?
-
No I created a tree model like in this example.
This is the code which add the data as SignalDataTreeItems to the m_root_item
beginInsertRows(QModelIndex(), 0, channel_list.size()-1); for (int channel_index : channel_list) { const mdf::channel& ch = channels[channel_index]; QList<QVariant> signalname = {QVariant::fromValue<QString>(QString::fromStdString(ch.get_name()))}; SignalDataTreeItem *signal = new SignalDataTreeItem(signalname); QList<QVariant> unit_string = {QVariant::fromValue<QString>(QString::fromStdString(ch.get_metadata_unit()))}; SignalDataTreeItem *unit = new SignalDataTreeItem(unit_string,signal); signal->appendChild(unit); // TODO: optimize this step! std::vector<double> column; ch.get_data_real(column); QList<QVariant> datas; foreach(double d, column){ datas.append(QVariant(d)); } SignalDataTreeItem *data = new SignalDataTreeItem(datas,signal); signal->appendChild(data); m_rootItem->appendChild(signal); } endInsertRows();
The problem is, that in the proxy the function data will never called.
SignalNamesProxyModel::SignalNamesProxyModel(QObject *parent): QSortFilterProxyModel (parent){ } int SignalNamesProxyModel::rowCount(const QModelIndex &parent) const { int rowcount = sourceModel()->rowCount(QModelIndex()); // of root item (number of signals) return rowcount; } int SignalNamesProxyModel::columnCount(const QModelIndex &parent) const{ return 1; } QVariant SignalNamesProxyModel::data(const QModelIndex &index, int role) const { QString value = sourceModel()->data(index).toString(); return value; } QModelIndex SignalNamesProxyModel::index(int row, int column, const QModelIndex &parent)const { return sourceModel()->index(row,0,QModelIndex()); // child "row" of m_root_item of sourceModel (signal) }
The problem is, that I don't understand how do I have to use beginInsertRows and endInsertRows, when I have to different datas, because the row count matches only for one view not for both at the same time.
-
I just re-read your first post. In the end, can't it be simplified to a two column table ?
First column: name
Second column: data -
@SGaist said in Using different Views with one model and different data:
I just re-read your first post. In the end, can't it be simplified to a two column table ?
First column: name
Second column: dataThe problem is, that in the data variable are multiple signals:
row0, column0: signal0, first value
row0, column1: signal0, second value
row1, column0: signal1, first value
row1, colimn1: signal1, second value
....So this is the second concept I tested while the weekend, it has nothing to do with the tree model.
I moved the signal name in a seperate listmodel (So it is easy to list all signal names in a list view).
The data I store in a table model.The problem is, how can I set, which row of this table should be shown?
For example:
I have a chart where I would like to plot some of this data (but only few signals, not every row). I would like to select which signal should be used for the x axis and which should be used on the y axis. To do that I created a QSortFilterProxyModel and reimplemented the rowCount, columnCount and the mapToSource function as follows:int SignalPreviewProxyModel::rowCount(const QModelIndex &parent) const { return 2*m_mapToSourceIndex.lenght(); } int SignalPreviewProxyModel::columnCount(const QModelIndex &parent) const{ int column = sourceModel()->columnCount(parent); return column; } QModelIndex SignalPreviewProxyModel::mapToSource(const QModelIndex & proxyIndex) const { if(!proxyIndex.isValid()){ return QModelIndex(); } int row = proxyIndex.row(); int column = proxyIndex.column(); int axis = row % 2; // modulo 2, because x and y axis int value = row /2; // graph if(axis == 0){ row = m_mapToSourceIndex.at(value).first; }else{ row = m_mapToSourceIndex.at(value).second; } QModelIndex index2 = sourceModel()->index(row,column,QModelIndex()); // get from already existing item the index return index2; }
In this concept, the problem is, that mapToSource transformation is not invertable ( when there should be the possibility to display mutiple signals in the graph, this is why m_mapToSourceIndex is a vector of mappings), so I'm not able create the mapFromSource function and I get a segmentation fault.
I really don't know how I can solve this problem (which is the most common way?).
Thanks for helping me! :) -
I'm still wondering if there's not a bit of model over-engineering.
Wouldn't it be simpler to have a class that encapsulates a "signal". Store of all your signals in a QVector of that class.
Then build a simple table module that would return the data from the correct signal object.
struct Signal { QString name; QString unit; QVector<QVariant> data; }; class SignalTableModel { QVariant data(const QModelIndex &index, int role) const { // Add index validity checks Signal signal(_signalVector.at(index.row())); if (index.column() == 1) { return signal.name(); } // etc. } };
-
So you mean, when column ==1 then return name, when column == 2 then return unit and when the column is greater than 2 return the respective value?
So then I have to use a proxy between this model and the signal name list view and set the columcount to 1?
And for the chartview(derived from tableview) I have to use a proxy, which maps the index to +2 because the first two rows are name and unit?
Do I understand you correct?
-
@SGaist said in Using different Views with one model and different data:
What do you mean by respective value ?
One signal consists of multiple data (due to a time range on which the signal was logged). The different signals are stored in a 2d array:std::vector<std::vector<double> > m_data;
@SGaist said in Using different Views with one model and different data:
You can use a QListView so no need for fiddling with a proxy model.
Yes for the signal names I have a QListView. The signal names are stored in a list model. This works fine.
Or did I missunderstand your question?
-
You wrote that you wanted to use a proxy and setting the columns count to 1 so I thought you were trying to "re-invent" QListView.
-
@SGaist said in Using different Views with one model and different data:
You wrote that you wanted to use a proxy and setting the columns count to 1 so I thought you were trying to "re-invent" QListView.
Oh sorry. this was in the concept with the tree model, but in the actual concept I have the data stored in a tablemodel.
-
Ok, good.
So at what point are you now ?
-
@SGaist said in Using different Views with one model and different data:
Ok, good.
So at what point are you now ?After reading you question I had a new idea (before I thought it is not possible to use KDChart to plot) :D :
- a data class which stores all the data
- a table model which stores a pointer to the data which I would like to display.
In this model I reimplement the data, rowcount and columncount function as following:
class Data { public: QVector<double>* getDataPointer(int index){return &m_data.at(index);} private: QVector<QVector<double>> m_data; }; // Every Diagram uses an object of this class class SignalModel: public QAbstractTableModel { public: - override rowcount - override columncount - override data void addData(QVector<double>* xaxis, QVector<double>* yaxis); private: QVector<QVector<double>*> m_data; };
void SignalModel::addData(QVector<double>* xaxis, QVector<double>* yaxis){ // KDChart expects, that the axis keys and values are stored in different rows m_data.append(xaxis); m_data.append(yaxis); } int SignalModel::rowCount(const QModelIndex &parent) const { return m_data.lenght(); } int SignalModel::columnCount(const QModelIndex &parent) const{ int column = m_data.at(0)->length() return column; } QVariant SignalModel::data(const QModelIndex &index) const{ return m_data.at(index.row)->at(index.column); }
removeRow and insertRow must be reimplemented too, to add dynamically new data.
For showing the signal names I would reimplement a listmodel.
I think it is the simplest method to do my task. (If there are errors in my code, it is because I didn't tested it, it's just a sketch).
I found kst-plot which does exactly what I want. So I think it is easier to extend this program and help to contribute to this one, than rewriting a new one.Thank you SGaist for your help and the inspiration. If this last concept is unclear, just write and I will extend it.