简体   繁体   English

为什么我的自定义图形项在基于Qt的C ++ GUI应用程序中不断重绘?

[英]Why are my custom graphical items constantly repainting in a Qt-based C++ GUI application?

My application has a QMdiArea, inside which subwindows are shown which contain instances of QGraphicsView-derived views (GfxInteractiveView), which in turn visualize scenes containing custom QGraphicsItem-derived items. 我的应用程序有一个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);
};

So far so good, but I also have an another item GfxPointItem which is fully custom with a paint() that actually does something, and when I add it to the scene, CPU usage jumps to full on one of the cores as all the items in the hierarchy enter a repaint loop which continues for as long the window is visible or the custom item is present in the scene. 到目前为止,还不错,但是我还有另一个项目GfxPointItem,它是使用paint()完全自定义的,实际上可以执行某些操作,当我将其添加到场景中时,CPU使用率在所有项目中的一个核心上已满在层次结构中,输入一个重绘循环,该循环将持续到窗口可见或场景中存在自定义项为止。 A glimpse at the stack when this happens shows that none of my function is responsible for calling paint(), the event is generated on the event loop. 瞥见发生这种情况时的堆栈,表明我的函数都不负责调用paint(),该事件在事件循环中生成。 Here's the code for GfxPointItem: 这是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());
}

I've no idea why the event loop keeps repainting the items. 我不知道为什么事件循环不断重新绘制项目。 I wouldn't even notice this but I hit a problem with showing a standard QFileDialog::getExistingDirectory() which wouldn't even manage to get painted while a window with the graphical items was visible, because the repaints stole all execution time on the main thread away from it, resulting in a freeze. 我什至没有注意到这一点,但是在显示标准QFileDialog :: getExistingDirectory()时遇到了一个问题,当带有图形项的窗口可见时,它甚至都无法进行绘制,因为重新绘制偷了所有的执行时间。主线程远离它,导致冻结。 After that, I added tracing statements to the paint functions and came up with tens of thousands of entries in the logfile after a couple seconds while the application was seemingly doing nothing. 之后,我在绘画函数中添加了跟踪语句,并在几秒钟后应用程序似乎无所事事的情况下在日志文件中提出了成千上万个条目。 In task manager, the CPU usage is around 25% when the window is visible (on a 4-core processor) and it drops to 0 when I close it. 在任务管理器中,当窗口可见(在4核处理器上)时,CPU使用率约为25%,当我关闭窗口时,CPU使用率降至0。

None of my code forces these repaints, so what is? 我的代码都没有强制这些重绘,那是什么? How can I debug the event loop to find the source of this behaviour, which slows down my application and causes freezes? 如何调试事件循环以查找此行为的来源,从而减慢我的应用程序并导致冻结?

Thanks! 谢谢!

Qt version is the newest 5.0.2 binary bundle and the application is compiled with Visual C++ 2012 for x64. Qt版本是最新的5.0.2二进制包,并且该应用程序是使用Visual C ++ 2012 x64编译的。

It would seem to me that when calling setScale in paint it connects to setPaintScale , which then calls the QWidget's update function, which then invalidates the area and when it gets back to the main event loop causes a repaint . 在我看来,当在paint调用setScale ,它连接到setPaintScale ,后者随后调用QWidget的update函数,该函数会使区域无效,并在返​​回到主事件循环时导致重新绘制

So you end up getting 1 paint required event every time you call the paint method. 因此,每次调用paint方法时,您最终都会收到1个需要油漆的事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM