PDA

View Full Version : QTableView and Color Editing



apatwork
6th December 2017, 12:04
Hi there,

I'm implementing editing functionality for a column in a QTableView that contains a color setting. The table in question contains a column that displays a color value. It looks as follows:

12712

This value should be made editable. After searching the internet for possible ways to accomplish that I decided for a solution that uses a QColorDialog. My current implementation looks as follows:

In the constructor of the class that contains the QTableView (ui->graphsTbl) I added a connect statement that activates a slot method editGraphColor(QModelIndex) upon double click on an instance of the color column:



connect(ui->graphsTbl, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editGraphColor(QModelIndex)));


The slot method looks as follows:



void MainWindow::editGraphColor(QModelIndex modelIx) {
if (modelIx.column() != GraphsTableModel::COLUMNS::PEN_COLOR)
return;
QColor currColor = modelIx.model()->data(modelIx, Qt::BackgroundRole).value<QColor>();
QColor color = QColorDialog::getColor(currColor, this, "Pick a color",
QColorDialog::DontUseNativeDialog);
if (color.isValid() && color != currColor)
ui->graphsTbl->model()->setData(modelIx, color, Qt::BackgroundRole);
}


First I filter out a double click on another column than the PEN_COLOR column. Then I retrieve the current color from the target color cell and pass it to the QColorDialog. After execution of the QColorDialog I pass the new color (in case it differs from the previous color) back to the model.

In the table's model class (named GraphsTableModel and inheriting from QAbstractTableModel) I implemented the Qt::BackgroundRole for the "PEN_COLOR" column in the data(...) as well as setData(...) methods:



QVariant GraphsTableModel::data(const QModelIndex &index, int role) const {
if (!isIndexValid(index))
return QVariant();

const Graph &graph(*mGraphs.at(index.row()));
if (role == Qt::DisplayRole) {
switch (index.column()) {
case LEGEND:
return graph.legend();
case PEN_COLOR:
return QString("Double Click to Change");
[...]
default:
return QVariant();
}
} else if (role == Qt::EditRole) {
switch (index.column()) {
case LEGEND:
return graph.legend();
[...]
default:
return QVariant();
}
} else if (role == Qt::BackgroundRole) {
if (index.column() == PEN_COLOR)
return graph.penColor();
else
return QVariant();
} else
return QVariant();
}

bool GraphsTableModel::setData(const QModelIndex& index, const QVariant& value, int role) {
if (!isIndexValid(index))
return false;

Graph &graph(*mGraphs.at(index.row()));
if (role == Qt::EditRole) {
switch(index.column()) {
case LEGEND:
graph.setLegend(value.toString());
break;
[...]
default:
return false;
}
} else if (role == Qt::BackgroundRole) {
if (index.column() == PEN_COLOR)
graph.setPenColor(value.value<QColor>());
} else
return false;

emit dataChanged(index, index);
return true;
}


There is some strange behavior with this code:

1. After a double click on a cell in the "Pen Color" column the QColorDialog is shown. After having selected another color and clicked on the "OK" button the QColorDialog vanishes but immediately shows up again. Only after a click on the "OK" button in the reappeared dialog it is closed and does not show up again.

2. The color cell for which I changed the color shows a white background and a text cursor (looks as if it were to store textual data and be in edit mode). After a mouse click on a position outside that cell it shows the new color and no text cursor anymore.

I have no idea what I did wrong. I'd expect that after changing the color using the QColorDialog and having passed the new color value into the model the QTableView would update its display (triggered by the dataChanged(...) signal emitted at the end of the model's setData(...) method.

And I'm a bit confused of the roles concept in Qt. I tried out what happens when I implement the Qt::EditRole for the PEN_COLOR column in the setData(...) method:


[...]
Graph &graph(*mGraphs.at(index.row()));
if (role == Qt::EditRole) {
switch(index.column()) {
case LEGEND:
graph.setLegend(value.toString());
break;
case PEN_COLOR:
graph.setPenColor(value.value<QColor>());
break;
[...] default:
return false;
}
} else if (role == Qt::BackgroundRole) {
if (index.column() == PEN_COLOR)
graph.setPenColor(value.value<QColor>());
} else
return false;

emit dataChanged(index, index);
return true;
}


but this did not change the behavior.
Any helful hint on what I'm doing wrong would be highly appreciated.

d_stranz
6th December 2017, 19:23
2. The color cell for which I changed the color shows a white background and a text cursor (looks as if it were to store textual data and be in edit mode). After a mouse click on a position outside that cell it shows the new color and no text cursor anymore.

All table cells store text data. Single-clicking puts it into edit mode, in which case the table view creates a line edit over the cell into which the user types the new text. When the editor is closed (by clicking somewhere else), setData() will be called and the table updated with the new text, icon, background, etc. If you want to defeat this behavior, then you need to 1) install a delegate or 2) set the cell as non-editable.

I don't know what's happening with your double-click behavior.

apatwork
7th December 2017, 13:29
Hi,

thanks a lot for your information. This sentence enlightened me:



If you want to defeat this behavior, then you need to 1) install a delegate or 2) set the cell as non-editable.


In fact, with my table's color column I'm not using the "standard" editing mechanism. My editor is the QColorDialog and it is started through the signal slot mechanism upon double click on a color cell in my table. Because of this I have to disable editing for the pen color column. This is done in the flags(...) method of the QTableView's model class (GraphsTableModel in my case). With this little change now the color cell is no longer in edit mode after I have changed its color using the QColorDialog.
From my understanding I cannot use the QColorDialog in a delegate because the editor used in a delegate is meant for being placed in the cell's area.

There's just one little issue left over: When double clicking on a cell it first gets selected (changes its background color to blue). Maybe this happens in response to the first click. After having changed the color (using the QColorDialog) the cell still is selected and therefore the new color is not yet visible. It becomes visible only after clicking somewhere else (this way unselecting the cell). At the moment I don't know how to get rid of that behavior.
BTW, for me not a single click but a double click puts a cell into edit mode. Maybe this is platform specific behavior? I'm developing my application on Linux.

d_stranz
11th December 2017, 16:38
You could try setting the flags to also exclude "selectable". This might prevent the cell from being highlighted. Or you could override the single-click event to handle the pen color cells and passing the event to the parent class for other cells.

apatwork
12th December 2017, 15:35
Hi!

Thanks a lot for your helpful hints. I tried both of them and in principle they do what they are meant for. But there are still some strange behaviors left over. Let's have a look at my table:

12722

As can be seen on the screenshot there is a combination of different kinds of data in the table.


The first and 9th columns show text.
The second and third columns show boolean values.
The fourth column shows a color.
The 5th, 6th and 8th columns display icons and use QComboBoxes as editors.
The 7th shows text with a QComboBox as editor.


All columns should be editable. I had already hard times to figure out how to make icons in columns using QComboBoxes as editors work. In principle now (almost) everything works but there are still some issues which I don't know how to handle:

1. It must be possible to select a single row in my table and move it to a different row inside the table. With the columns changed to be no longer selectable it seems to be no longer possible to select a row. I have configured QAbstractItemView::SelectRows as selection behavior of the QTableView. It would be completely sufficient if I could perform a selection of a single row in the table by clicking on its vertical header cell. But I couldn't figure out yet how to accomplish this.

2. The behavior in regard to editing is inconsistent between the different kinds of columns:


For the columns containing text editing can be started with either a double click or a click of the F2 key.
The check boxes can be toggled with single clicks
For the color cell editing now is started upon a single click
The columns showing icons require a double click to run their respective editors and the changed values are stored in the model just after a single click somewhere outside the table cells.


I'd prefer a uniform behavior of all columns when they are to be edited, i.e. a single click or the F2 key clicked to activate the respective editor. So it would be great if I could configure this behavior somehow ...

Thanks a lot for your patience!