I am trying to make a table in which the cells of one of its columns are editable only through a dialog box that will open when pressing a button in it. In addition, I would like said button to be visible only when the mouse passes over that cell.
Reading about it, it seems that the idea of inserting a custom widget is possible but it penalizes performance (according to comments on SO) and is not the right way to go. This comes out in the documentation:
The items shown in a table view, like those in the other item views, are rendered and edited using standard delegates. However, for some tasks it is sometimes useful to be able to insert widgets in a table instead. Widgets are set for particular indexes with the setIndexWidget() function, and later retrieved with indexWidget().
So the most "natural" way is through delegates. I have this piece of code (copied entirely from here ) that proposes to use the method
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
To draw the button, and
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
So that it responds to the click of the mouse. Like this (my implementation):
void DelegadoFormulasMedicion::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.isValid())
{
QStyleOptionButton boton;
QRect r = option.rect;//getting the rect of the cell
int x,y,w,h;
w = m_ancho_boton;
h = r.height();//button height
x = r.left() + r.width() - w;//the X coordinate
y = r.top();//the Y coordinate
boton.rect = QRect(x,y,w,h);
boton.text = "...";
boton.state = QStyle::State_MouseOver;
QApplication::style()->drawControl( QStyle::CE_PushButton, &boton, painter);
}
else
{
DelegadoBase::paint(painter, option, index);
}
}
Y
bool DelegadoFormulasMedicion::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
if( event->type() == QEvent::MouseButtonRelease )
{
QMouseEvent * e = (QMouseEvent *)event;
int clickX = e->x();
int clickY = e->y();
QRect r = option.rect;//getting the rect of the cell
int x,y,w,h;
x = r.left() + r.width() - m_ancho_boton;//the X coordinate
y = r.top();//the Y coordinate
w = m_ancho_boton;//button width
h = r.height();//button height
if( clickX > x && clickX < x + w )
if( clickY > y && clickY < y + h )
{
qDebug()<<"Abro dialogo";//aqui va mi dialogo
}
return true;
}
return false;
}
So far there don't seem to be any problems. Now I have to draw this button if the mouse is over the cell, for which I understand that I have to capture the event.
For this I was following these steps .
1.- Create a signal in the table:
signals:
void hoverIndexChanged(bool dentro);
2.- Enable this in the constructor of the table:
setMouseTracking(true);
setAttribute(Qt::WA_Hover);
3.- Capture the mouse position
void TablaMed::mouseMoveEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
QModelIndex index = indexAt(pos);
if (index.isValid())
{
if (index.column() == tipoColumnaTMedCert::FORMULA)
{
//qDebug()<<"Estoy dentro";
emit hoverIndexChanged(true);
}
else
{
//qDebug()<<"Estoy fuera";
emit hoverIndexChanged(false);
}
}
TablaBase::mouseMoveEvent(event);
}
4.- Connect the signal and the slot:
connect(this,SIGNAL(hoverIndexChanged(bool)),dlgFM,SLOT(onHoverIndexChanged(bool)));
In this way I could change a bool member of the delegate so that with the pertinent modifications in the paint() method shown above, said button would be shown or not, but I am not able to collect that signal in the delegate.
Update
The above works, only that I had forgotten to put the Q_OBJECT macro in the delegate
I've just put a complete (very minimal) example of the table in pastebin, with the code supplied by @eferion and the one above. I couldn't say if one is more appropriate than the other.
You can easily get that effect by pulling the event filter:
The logic is extremely simple:
A button is added as a child of the table, this is done so that it is painted using the viewport of the table itself, which guarantees that the button is always painted on top of the table:
Next, our object installs itself as the event filter for the table and configures it to handle mouse movement events.
The object then receives the events that are generated on the table. In this case pay attention only to those referring to mouse movements.
Before this event, it asks the table to know which is the cell on which the mouse is located.
Note that it is necessary to convert the coordinates of the event. In this case ,
QTableView
use oneviewport
for row headers, one for column headers, and one for cells. We need to know the coordinates within the viewport of the cells.Well, if it turns out that the
index
return is valid and above it belongs to column 1, then we position the mouse to the right of the cell. To do this we get the area of the cell.Initializing this object is trivial: