Using different Views with one model and different data
-
ok I set the wrong prefix, but my idea will not work
Main.cpp:SignalNamesProxyModel* proxy_signal_names = new SignalNamesProxyModel(this); proxy_signal_names->setSourceModel(m_data.getSignalNames()); m_listview_signal->setModel(proxy_signal_names);es());
My plan was to set the row to a negative number, so I know in the model, that this will get different data:
Proxymodel.cpp:
QVariant SignalNamesProxyModel::data(const QModelIndex &index, int role) const { const QAbstractItemModel * model = index.model(); QString value = sourceModel()->data(model->index(-1,index.row()),role).toString(); return value; }
Model.cpp:
QVariant SignalData::data(const QModelIndex &index, int role) const { int row = index.row(); int column = index.column(); if (role == Qt::DisplayRole){ if(row >= 0){ return m_data[row][column]; }else if(row == -1){ return m_column_names[column]; }else if(row == -2){ return m_column_units[column]; } } return QVariant(); }
But this does not work, because when I set the row to a negative number, the column becomes -1 too.
Is there another way to get different data?
-
Hi,
Wouldn't the KConcatenateRowsProxyModel do what you want ?
-
Hi,
Wouldn't the KConcatenateRowsProxyModel do what you want ?
@SGaist said in Using different Views with one model and different data:
Wouldn't the KConcatenateRowsProxyModel do what you want ?
Thank you SGaist, but in the documentation there is written, that it is assumed that all rows have the same number ("All models are assumed to have the same number of columns")
This is my dataclass:
class SignalData : public QAbstractTableModel { Q_OBJECT public: SignalData(Data *parent); int rowCount(const QModelIndex &parent = QModelIndex()) const override ; int columnCount(const QModelIndex &parent = QModelIndex()) const override ; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); // wieso darf hier kein override stehen? bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; int readMDFFile(QString path); private: Data* m_parent; std::vector<std::vector<double> > m_data; std::vector<QString> m_column_names; std::vector<QString> m_column_units; };
m_data are the datas, column_names are the names of the data columns and olumn_units are the units of the data columns.
For example: size(m_column_names) = size(m_column_units) = 8
and size(m_data) = 8x1000 or so -
Right, I was thinking about KExtraColumnsProxyModel.
But I realise I might have misunderstood your final goal.
Can you show an image of what you would like to show in your view ?
-
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.