PDA

View Full Version : QTableWidget row swapping and insertion



fritz
16th November 2006, 03:43
Hi all,

I have placed a QTableWidget with 3 columns in Designer and promoted it to DataTableWidget. In implementation I have provided item delegates to have the first and the third column acting like QLineEdit and the second column acting like QComboBox. When cellChanged(int row, int) signal is emitted and if (row == rowCount() - 1) then a new row is added else if there is no data in the currently edited row (deleted just now) then the row is removed. I have also reimplemented insertRow() like this:



void DataTableWidget::insertRow(int row)
{
QTableWidget::insertRow(row); // call the parent function
// now make sure that all the cells exist
setItem(row, 0, new QTableWidgetItem());
setItem(row, 1, new QTableWidgetItem());
setItem(row, 2, new QTableWidgetItem());
}

Everything works like a charm, but now I have to implement an ability of moving rows up or down. I have provided a context menu with these actions, so when the user right-clicks on the cell:



void DataTableWidget::contextMenuEvent(QContextMenuEven t* event)
{
// disable these actions by default
rowDownAction->setEnabled(false);
rowUpAction->setEnabled(false);

int row = rowAt(event->y());

if (row != -1) // if there is a row under cursor
{
selectRow(row);
// if it is not the last row which is designed for entering new data only
// and not the first row which cannot be moved up
if (row != rowCount() - 1 && row > 0)
rowUpAction->setEnabled(true);

// if it is not the last row with data
// then we can move it down
if (row < rowCount() - 2)
rowDownAction->setEnabled(true);
}

contextMenu->popup(QPoint(event->globalX(), event->globalY()));
event->accept();
}

The problem is that I don't know how to swap the rows. I mean



void DataTableWidget::onRowDown()
{
// what is here?
}

I have tried simple


for (int c = 0; c < columnCount(); c++)
{
QTableWidgetItem* src = item(currentCell()->row(), c);
QTableWidgetItem* dest = item(currentCell()->row() + 1, c);
QTableWidgetItem* buf = src;
src = dest;
dest = buf;
}

as well as



for (int c = 0; c < columnCount(); c++)
{
QTableWidgetItem* src = item(currentCell()->row(), c);
QTableWidgetItem* dest = item(currentCell()->row() + 1, c);
setItem(currentCell()->row(), c, dest);
setItem(currentCell()->row() + 1, c, src);
}

but neither worked for me. Then I have decided to insertRow(currentCell()->row() + 1) and copy all the elements of the current row to the new row, then remove the current row. But I have discovered that it doesn't insertRow(int row) with row set to anything other than rowCount(). I mean the function is called and executed but nothing happens.

How can I swap rows and insert a new row in random position of the table?

thanks

wysota
16th November 2006, 06:20
This would be easier if you were using a model-view approach... There you would simply call insertRow, move the data and delete the old row. Here you could also try this - call insertRow(), then move data from items using setItem (the item will be removed from its origin) and finally call removeRow() on the old row. I doubt that insertRow() doesn't work for you, but if you're sure about it, maybe you should report it to Trolltech.

fritz
16th November 2006, 08:39
thanks for the response.

as I understand, I actually use model-view, the cells of the table are the descendants of QItemDelegate, they are viewed as text and edited with QLineEdit and QComboBox. You might want to have a look at this, I'm attaching the widget class.

wysota
16th November 2006, 09:50
Yes, you are using model-view, but the actual model and view is hidden from you, you use convenience classes which keep their own internal model so for example you can't just reorder the internal structures of the model, because you don't have access to them - you have to do that manually by moving each item. If you had a real model, you could do something like this:


void myModel::swapRows(QModelIndex i1, QModelIndex i2){
Q_ASSERT(i1.isValid() && i2.isValid());
int r1 = i1.row();
int r2 = i2.row();
if(r1==r2) return;
MyStruct temp = _internaldata.at(r1);
_internaldata[r1] = _internaldata[r2];
_internaldata[r2] = temp;
int cc = columnCount();
emit dataChanged(i1.sibling(r1, 0), i1.sibling(r1, cc-1);
emit dataChanged(i2.sibling(r2, 0), i2.sibling(r2, cc-1);
}

fritz
17th November 2006, 04:07
Thanks, but looking like I feel myself not that experienced in Qt to use such a deep way.

Okay, I have modified the code and now it inserts rows perfectly (you were right, that was my fault). It copies the values from the old row to the new one with setItem() as you told. So I have



void DataTableWidget::onRowDown()
{
int crow = currentItem()->row();

insertRow(crow + 2);

setItem(crow + 2, 0, item(crow, 0));
setItem(crow + 2, 1, item(crow, 1));
setItem(crow + 2, 2, item(crow, 2));

removeRow(crow);
}


I got SIGSEGV when I tried to removeRow(). I have commented it out and this code worked but the new row was somehow linked to the old one. I mean when I was changing data in the new row, the old one was changed in the same way and vice versa. Moreover when I had some changes in the widget and closed the dialogs, I had SIGSEGV too.

Then I tried to find a way how to remove the old cells and decided to get them by takeItem(), not by item(). So the final code looks like



void DataTableWidget::onRowDown()
{
int crow = currentItem()->row();

insertRow(crow + 2);

setItem(crow + 2, 0, takeItem(crow, 0));
setItem(crow + 2, 1, takeItem(crow, 1));
setItem(crow + 2, 2, takeItem(crow, 2));

removeRow(crow);
}

And this thing works, and removeRow() works too. At least when moving rows down :)

Thank you so much, wysota!