3

This is my first post here, but i received a lot of help from all of you guys, since i started programming.

I am new to Qt and try to make my first project at the moment. My question is about the communication between the model, the underlying data and the view.

I first made the background things (data storing etc.) separate from the GUI. Now I have a template class Matrix and I wrote a template class MatrixModel which inherits from QAbstractTableModel. This is working fine, means that I can edit the table and the values refresh in the view and in the data from model.

Now my question: How do I change the data in the model without writing a new method?

mainwindow.cpp:

matrix<int> m = {{1,2,3},{4,5,6},{7,8,9}};
MatrixModel<int>* model = new MatrixModel<int>(m);
QTableView* tableView = new QTableView;
tableView->setModel(model);

Is there any way to call m.transpose() to the model's underlying data? What happens if I change m? Does it affect the model's data? I tried a lot of things but nothing worked. One "problem" is that I can't use standard signal/slot syntax because I'm working with template classes.

My last try was to make an update class in the model:

template<typename T>
void MatrixModel<T>::updateAll() {
    QModelIndex topLeft = index(0,0);
    QModelIndex bottomRight = index(rowCount()-1, columnCount()-1);
    emit dataChanged(topLeft, bottomRight);
}

and I tried to connect it with a button and a lambda function:

connect(transposeButton, &QPushButton::clicked, [=,&m,&model]() {
m.transpose(); model->updateAll();
});

but that seemed to crash my program if i press the button. I am really desperated xD hope you can help me. If you need more information please just ask^^

Best Regards

Dennis

EDIT 1: Okay so far i figured out (with your help :)), that I have to write the functions of the matrix again in the model (e.g. transpose()) and emit data changes from there (with emit dataChanged or beginResetModel()...) but I can't modify the models underlying data. If I write

This in the mainwindow:

connect(transposeButton, &QPushButton::clicked, [&model]() {
    model->transpose();
});

This in the Model:

template<typename T>
void MatrixModel<T>::transpose() {
    m(0,0) = 5;
}

my program just crashes if i press the button transpose. If i comment the line

m(0,0) = 5;

out, everything works.

EDIT 2: Maybe there is a problem with the data storage, so here is my copy constructor and the private variables of my matrix class:

private:
std::vector <T> data;
size_t rows, columns;

//Copy constructor
template<typename T>
matrix<T>::matrix(const matrix<T>& other)
    : rows(other.rows), columns(other.columns) {
    data = other.data;
}
syc
  • 73
  • 1
  • 8
  • if your model's dimensions are changing, `dataChanged()` signal isn't enough. You should call `beginResetModel()` before changing data, then call `endResetModel()` when you are done. . . – Mike May 14 '16 at 08:26
  • so if your matrix isn't a square one, calling `transpose()` then just emitting the `dataChanged()` signal might lead to crashes and accessing invalid indexes in your matrix – Mike May 14 '16 at 08:29
  • Okay I get that, but I can just call that from my model. But I am calling the function m.transpose() from my mainwindow.cpp where I can't call the beginResetModel (because it's protected). And I don't want to write all functions again in my matrix model. – syc May 14 '16 at 08:30
  • And besides that I am trying with a square one to reduce the problems I have to watch. But one thing to your guess. What happens if I emit layoutChanged ? Isn't it to rearrange dimensions? – syc May 14 '16 at 08:32

2 Answers2

1

You should have your matrix variable as a private member in your model, so that all edits to the matrix should be done through functions in your model. that means your model will have a public transpose function that calls transpose on your matrix data then emit dataChanged() signal. And you shouldn't have any direct access to your matrix because changing it without the model's knowledge will not give what you want. that way, it isn't your responsibility to call updateAll after each change to the matrix variable since your model did the change and updated itself accordingly. . .

Mike
  • 8,055
  • 1
  • 30
  • 44
  • Okay, just thought so:P so I have to rewrite the functions for my model. But I read that we shouldn't use resetmodel because it paints all again. Wouldn't it be better to emit from top left to bottom right? Besides that my program crashed if I try to call model functions (even simple ones like the new transpose function where I beginResetModel transpose endResetModel . Thx for your help :) – syc May 14 '16 at 09:01
  • I figured out, that removing the beginResetModel() and endResetModel() works. So editing m inside the model is working. – syc May 14 '16 at 09:06
  • if your model's dimensions aren't changed it is enough to emit `dataChanged()` , but if your dimensions are changing you should call functions like `beginRemoveRow` or `beginResetModel`, or .. and then `endRemoveRow` or `endRemoveRow`... after changing the data structure – Mike May 14 '16 at 09:07
  • Sorry.... it isn't i commented it out xD maybe there is something wrong with the connect statement? – syc May 14 '16 at 09:12
  • It just crashes. Seems to be the case if I call a model function from the connect lambda function – syc May 14 '16 at 09:29
  • does the crash occur in the lambda function? – Mike May 14 '16 at 09:34
  • If I debug it stops at the line with the connect statement. But more specific I don't know :S – syc May 14 '16 at 09:36
  • The problem is 100% in the lambda function (and the calling statements there.) If it is empty it works, even with model->transpose(); IFF the transpose function is empty. But if i write something in there like m(0,0)= 10; to change the underlying data, the program crashes if i press the button. – syc May 14 '16 at 09:42
  • is your `matrix m` variable a local variable in the model's constructor? it should be a member in the model. if it is a local variable then the crash is because the matrix is maybe using invalid memory when the lambda function is called. post more code so that I can help you. – Mike May 14 '16 at 10:26
  • I have a private member variable m in the model which gets data by the constructor (which takes a matrix too). Anything else is working (changes in the view, then setData called, then data called to get table entries) and all edits in the view are shown directly. – syc May 14 '16 at 10:29
  • and how does your matrix's copy constructor work? do use just a shallow copy?? – Mike May 14 '16 at 10:32
  • hmmm okay... maybe there is the problem... but without gui i think it's working. My matrix class just uses std::vector. And my copy constructor just uses copy constructor or vector, which is deep copy or? – syc May 14 '16 at 10:40
  • See my edit. Just tell me what you need ^^ And thanks for your help :) – syc May 14 '16 at 11:03
1

I got it...

connect(transposeButton, &QPushButton::clicked, [model]() {
    beginResetModel();
    m(0,0)=50;
    endResetModel();
});

works... model already is an pointer, so i give the same pointer to the lambda function. Now i just have to figure out what is not right with my transpose function because this is still not working, but the crashes are gone :P thank you for your suggestions :)

syc
  • 73
  • 1
  • 8