В моем приложении есть QMdiArea, внутри которого показаны подокна, которые содержат экземпляры производных от QGraphicsView представлений (GfxInteractiveView), которые, в свою очередь, визуализируют сцены, содержащие пользовательские производные от QGraphicsItem элементы.
/// An image item which is displayed as the background of a scene.
class GfxImageItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
protected:
GfxInteractiveImgView *view_;
QPixmap pixmap_;
QList<GfxPointItem *> pointItems_;
public:
GfxImageItem(GfxInteractiveImgView *view);
// set the pixmap to the image loaded from this path
bool load(const QString &path);
// normally not overriden, here just for tracing
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QLOG("Painting image"); // homebrew macro for tracing
QGraphicsPixmapItem::paint(painter, option, widget);
}
};
/// A generated image drawn at the foreground of a scene.
class GfxMapItem : public QGraphicsPixmapItem
{
public:
GfxMapItem(QGraphicsItem *item);
void regenerateMap();
// same as GfxMapItem, here just for tracing
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};
Пока все хорошо, но у меня также есть еще один элемент GfxPointItem, который полностью настраивается с помощью paint (), который на самом деле что-то делает, и когда я добавляю его в сцену, загрузка ЦП переходит на полную на одном из ядер, так как все элементы в иерархии входят в цикл перерисовки, который продолжается до тех пор, пока окно является видимым или пользовательский элемент присутствует в сцене. Взгляд на стек, когда это происходит, показывает, что ни одна из моих функций не отвечает за вызов paint (), событие генерируется в цикле событий. Вот код для GfxPointItem:
/// A draggable item representing an analysis point on the map, drawn on top of the map.
class GfxPointItem : public QGraphicsObject
{
Q_OBJECT
protected:
GfxImageItem *imgParent_;
GfxInteractiveImgView *view_;
int size_, fontSize_;
QColor color_, oldColor_;
qreal paintScale_;
QRectF boundingRect_;
bool active_, static_;
QStaticText pointText_, valueText_;
public:
GfxPointItem(GfxImageItem *parent, GfxInteractiveImgView *view, const QPointF &pos);
void setActive(bool arg);
void setStatic(bool arg);
void setColor(const QColor &color) { color_ = color; update(); }
virtual QRectF boundingRect() const { return boundingRect_; }
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
signals:
void changedPos(int index, QPointF newpos);
public slots:
void setPaintScale(qreal value);
void setPointText(const QString &text);
void setValueText(const QString &text);
protected:
void updateBoundRect();
void updatePointText();
QPoint valueTextPos() const;
QPoint pointTextPos() const;
};GfxPointItem::GfxPointItem(GfxImageItem *parent, GfxInteractiveImgView *view, const QPointF &pos, int index) : QGraphicsObject(parent),
imgParent_(parent),
view_(view),
index_(index), size_(8), fontSize_(8),
color_(Qt::black),
paintScale_(view->invscale()),
drawLabel_(true), active_(false), static_(false), floatPrec_(false)
{
QLOGX("Creating new at " << pos.x() << "," << pos.y() << ", index: " << index);
setPos(pos);
updatePointText();
connect(view, SIGNAL(scaleChanged(qreal)), this, SLOT(setPaintScale(qreal)));
}
/// An inactive point wil not respond to hover events and will not be movable.
void GfxPointItem::setActive(bool arg)
{
QLOGX("Setting active state: " << arg);
active_ = arg;
setAcceptHoverEvents(arg);
setFlag(QGraphicsItem::ItemIsMovable, arg);
}
/// Set or disable static mode on point. In static mode, the point text is not updated when changing position, so it can retain a constant label.
void GfxPointItem::setStatic(bool arg)
{
QLOGX("Setting static mode: " << arg);
static_ = arg;
}
void GfxPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QLOGX("Painting point");
static const int margin = 2;
setScale(paintScale_);
QPen pen;
pen.setWidth(1);
pen.setColor(color_);
painter->setPen(pen);
// paint the centerpoint marker (two crossed lines)
painter->drawLine(QPointF(-size_, 0), QPointF( size_, 0));
painter->drawLine(QPointF(0, -size_), QPointF(0, size_));
// the label box and the two static text lines inside
pen.setWidth(0);
painter->setPen(pen);
QFont font;
font.setPointSize(fontSize_);
painter->setFont(font);
QBrush brush(Qt::SolidPattern);
brush.setColor(QColor(255, 255, 127)); // sand yellow
painter->setBrush(brush);
// point text size, value text size
QSizeF pts = pointText_.size(),
vts = valueText_.size();
// point text position, value text position
QPoint vtp = valueTextPos(),
ptp = pointTextPos();
// point id and position label and value indicator label in a rectangular box
int shift = (valueText_.text().isEmpty()) ? 0 : vts.height();
QRectF rect(ptp.x()-margin, ptp.y(), std::max(pts.width(), vts.width())+margin, pts.height() + shift);
painter->drawRect(rect);
painter->drawStaticText(ptp, pointText_);
painter->drawStaticText(vtp, valueText_);
}
void GfxPointItem::setPaintScale(qreal value)
{
QLOGX("Updating scale: " << value);
paintScale_ = value;
update();
}
void GfxPointItem::setPointText(const QString &text)
{
QLOGX("Setting text: " << text);
pointText_.setText(text);
updateBoundRect();
update();
}
void GfxPointItem::setValueText(const QString &text)
{
QLOGX("Setting value text: " << text);
valueText_.setText(text);
updateBoundRect();
update();
}
void GfxPointItem::updateBoundRect()
{
QLOGX("Updating bounding rect");
boundingRect_.setRect(- size_, pointTextPos().y(),
(2 * size_) + std::max(pointText_.size().width(), valueText_.size().width()),
(2 * size_) + pointText_.size().height() + valueText_.size().height());
}
void GfxPointItem::updatePointText()
{
QLOGX("Updating point text");
pointText_.setText("P" + QString::number(index_ + 1) + " ("+ (floatPrec_ ? QString::number(pos().x()) : QString::number(std::floor(pos().x()))) + ","+ (floatPrec_ ? QString::number(pos().y()) : QString::number(std::floor(pos().y()))) + ")");
updateBoundRect();
update();
}
void GfxPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QLOGX("Mouse move");
QPointF p = pos();
QPointF ep = event->pos();
QGraphicsItem::mouseMoveEvent(event);
if (!static_) updatePointText();
emit changedPos(index_, pos());
}
Я понятия не имею, почему цикл обработки событий перерисовывает элементы. Я даже не заметил бы этого, но столкнулся с проблемой показа стандартного QFileDialog :: getExistingDirectory (), который даже не удалось бы нарисовать, пока окно с графическими элементами было видно, потому что перерисовки украли все время выполнения на Основная нить от него, что приводит к зависанию. После этого я добавил операторы трассировки в функции рисования и через пару секунд обнаружил десятки тысяч записей в файле журнала, в то время как приложение, казалось бы, ничего не делало. В диспетчере задач загрузка ЦП составляет около 25%, когда окно видно (на 4-ядерном процессоре), и при закрытии оно падает до 0.
Ни один из моих кодов не вызывает эти перерисовки, так что же? Как я могу отладить цикл событий, чтобы найти источник этого поведения, которое замедляет мое приложение и вызывает зависания?
Спасибо!
Версия Qt является новейшим двоичным пакетом 5.0.2, и приложение скомпилировано с Visual C ++ 2012 для x64.
Мне кажется что при звонке setScale
в paint
это соединяется с setPaintScale
, который затем вызывает QWidget update
функция, которая затем делает недействительной область и когда она возвращается в основной цикл событий, вызывает перекрашивание.
Таким образом, вы получаете 1 событие, необходимое для рисования, каждый раз, когда вызываете метод рисования.
Других решений пока нет …