Reputation: 799
I am trying to make a QStyledItemDelegate behave like a custom QWidget I wrote, so I can switch my code to a model/view approach.
The custom QWidget is a complex button which shows four "sub buttons" in it's corners on mouse over (so all in all there are five signals). It's also drag&droppable with a custom drag pixmap. To achieve this I am using mousePressEvent, mouseReleaseEvent, mouseMoveEvent, enterEvent and leaveEvent. This is what it looks like with and without the "sub buttons" shown on mouse over:
I have since switched my main code to use a model/view approach and am trying to use this widget as a QStyledItemDelegate for my customised ListView. I have tried assigning the custom Widget as an editor like this:
class ToolButtonDelegate( QStyledItemDelegate ):
def __init__( self, parent=None ):
super( ToolButtonDelegate, self).__init__( parent )
self.parent = parent
def createEditor( self, parent, option, index ):
if not index.isValid():
return False
btn = FancyButton( index.data( Qt.UserRole ), parent=parent )
return btn
This seems promising as it draws the "FancyButton" class for the item I click on. However, I need this to be a mouse over event. After a bit more research I tried connecting the QAbstractItemView.entered slot to QAbstractItemView.edit signal:
self.entered.connect( self.edit )
This works only for the first item I move my mouse pointer over, then I get these errors:
edit: editing failed
So now I'm stuck again with these problems:
I have a feeling I'm heading into the wrong direction here.
Upvotes: 4
Views: 6448
Reputation: 25690
AFAIK, you can't use custom widgets for viewing items in a List/Table/Tree-View-delegates. You can use them for editing, but if the view-mode requires anything other than just a redraw, you're pretty stumped.
We've worked around this by having our own container widgets which listen to model events and add/remove item-widgets dynamically. A shame really, since it feels rather basic.
The reason might be that the views are supposed to be fast and able to show a lot of items, and having a lot of widgets isn't fast, so they've chosen to just allow a custom repaint function (which allows them to use justone container widget, with custom painting in different areas.)
Maybe there's a way around it, and if so, I'd be happy to hear about it.
Update
If you need to catch events, maybe you can install an event filter to the container view, then use QListView::indexAt to find the right item/widget/delegate to send the event to?
You can then use openPersistentEditor and closePersistentEditor on mouseenter/leave to enable editing, i.e. show/close your custom widget on an item.
Upvotes: 0
Reputation: 101
I'd urge you to go back to delegates instead of widgets, purely for performance reasons =)
You can work with delegates mouse over events in paint method using states, particularly QStyle::State_MouseOver. As for clicks on buttons you can override editorEvent which receives all mouse events and then work with regions of where mouse clicks occurred. For example:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (option.state & QStyle::State_MouseOver)
{
// draw stuff which appears on mouse over
} else {
// draw stuff that appears when mouse is not over control
}
}
bool editorEvent(QEvent *event, QAbstractItemModel*, const QStyleOptionViewItem &option, const QModelIndex &index)
{
// Emit a signal when the icon is clicked
if(event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
QRect editButtonRect = editIcon.rect().translated(editIconPos(option));
QRect deleteButtonRect = deleteIcon.rect().translated(deleteIconPos(option));
if(editButtonRect.contains(mouseEvent->pos()))
{
emit editIndexClicked(index);
} else if (deleteButtonRect.contains(mouseEvent->pos())) {
emit deleteIndexClicked(index);
}
}
return false;
}
Also you can check out this topic HowTo create delegate for QTreeWidget?, this is for QTreeWidget, but I think same methods would apply to your case too.
Upvotes: 4