My answer is for people who, like I did for a while, implement render mode logic in their GraphicsItem::paint()
method.
For example :
GraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem*, QWidget*)
{
QPen _pen ;
const qreal normalPenWidthF = 1.5 ;
if(isSelected()) {
_pen.setColor(Settings::s_selectionColor) ;
_pen.setWidthF(Settings::s_selectionWidth) ;
}
else
if(isHovered()) {
_pen.setColor(Settings::s_hoveredColor) ;
_pen.setWidthF(Settings::s_selectionWidth) ;
}
else
if(someOtherLogic()) {
_pen.setColor(Settings::s_otherColor) ;
_pen.setWidthF(normalPenWidthF) ;
}
else {
_pen.setColor(TSPSettings::s_defaultColor) ;
_pen.setWidthF(normalPenWidthF) ;
}
//
painter->setPen(_pen) ;
painter->setBrush(Qt::NoBrush) ;
painter->drawEllipse(m_rect) ;
}
Here is how I achieved good QGraphicsView performance, even with large scenes involving multiple layers. It could even support dynamic clipping of shapes between layers.
- Custom GraphicsItems should inherit of QAbstractGraphicsShapeItem,
so you have setPen() and setBrush() support.
- Expose an interface to update pen and brush, and use some logic to
trigger an update only when needed.
.h
class AbstractGraphicsItem : public QAbstractGraphicsShapeItem
{
private :
bool m_hovered ;
public :
AbstractGraphicsItem() ;
virtual ~AbstractGraphicsItem() ;
bool isHovered() const { return m_hovered ; }
void setIsHovered(bool state) ;
// control render mode update
virtual void updatePenAndBrush()=0 ;
protected :
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) ;
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *e);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *e);
};
.cpp
AbstractGraphicsItem::AbstractGraphicsItem()
: QAbstractGraphicsShapeItem()
, m_hovered(false)
{
}
AbstractGraphicsItem::~AbstractGraphicsItem()
{
}
void AbstractGraphicsItem::setHovered(bool state)
{
if (h!=isHovered()) {
m_hovered = h ;
updatePenAndBrush() ;
update() ;
}
}
void AbstractGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent*)
{
setHovered(true) ;
}
void AbstractGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
{
setHovered(false) ;
}
QVariant AbstractGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
switch(change) {
case ItemSelectedHasChanged :
updatePenAndBrush() ;
break ;
}
return QAbstractGraphicsShapeItem::itemChange(change, value);
}
And then your GraphicsItem
(which inherits of AbstractGraphicsItem
) becomes :
void GraphicsItem::updatePenAndBrush()
{
QPen _pen ;
if(isSelected()) {
_pen.setColor(Settings::s_selectionColor) ;
_pen.setWidthF(Settings::s_selectionWidth) ;
} else
if(isHovered()) {
_pen.setColor(Settings::s_hoveredColor) ;
_pen.setWidthF(Settings::s_selectionWidth) ;
} else
if(someOtherLogic()) {
_pen.setColor(Settings::s_otherColor) ;
_pen.setWidthF(normalPenWidthF) ;
} else {
_pen.setColor(Settings::s_defaultColor) ;
_pen.setWidthF(normalPenWidthF) ;
}
_pen.setCosmetic(true) ;
setPen(_pen) ;
}
void GraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget *)
{
painter->setPen(pen()) ;
painter->setBrush(brush()) ;
painter->drawEllipse(s_rect) ;
}
The contents of the old GraphicsItem::paint()
method is now in GraphicsItem::updatePenAndBrush()
and is called every now and then, but not at every paint call. On the other side, the paint method gets to the basics.
Obviously you'll have to call updatePenAndBrush()
yourself, but it was not hard in my case.
It's not the only thing I did to improve performance. I searched a lot, and there is a lot of tweaking possible for a Graphics View system, but with this one my app went from barely usable to real-time (finally!)