使用QPainter进行平滑绘图

时间:2016-02-21 04:50:23

标签: c++ qt animation

如何使用Qt QPainter平滑地生长角度/长度变化的弧?这是我刚从Qt的Analog Clock Window Example创建的最小代码。

代码在50毫秒内随机更改m_value + - 5。这是为了模拟我想要实现的实际行为。弧开始于12点钟位置并逆时针方向增长。 m_value缩放到360度(12点到12点)。

我的目标是实时平滑地改变弧的长度,以响应给定的(模拟)值,而不管输入值是否抖动。

我想完成两件事:

  1. 平滑重绘圆弧。当前代码直接重绘了 当时的价值。我甚至不使用子角度值。结果 在弧的尽头看起来很吵。
  2. 将图形与V-Sync一起更新。所以我不浪费 非显示重绘的计算能力。我不知道该怎么做 通过V-Sync触发render事件,所以我设置了33毫秒 计时器。当m_value在不到30毫秒内发生变化时,需要这样做。
  3. 我不想要的东西

    • QtQuick。我正在寻找QPainter方法来做到这一点。

    我正在使用的平台:

    • Qt 5.x
    • 在Debian Linux上(如果重要)
    #include <QtGui>
    
    #include "rasterwindow.h"
    
    class SmoothArc : public RasterWindow
    {
    public:
        SmoothArc();
    
    protected:
        void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
        void render(QPainter *p) Q_DECL_OVERRIDE;
    
    private:
        int m_timerId;
        int m_valueTimerId;
        int m_value = 50;
    };
    
    SmoothArc::SmoothArc()
    {
        setTitle("Smooth Arc");
        resize(200, 200);
    
        m_timerId = startTimer(33);
        m_valueTimerId = startTimer(100);
    }
    
    void SmoothArc::timerEvent(QTimerEvent *event)
    {
        if (event->timerId() == m_timerId)
            renderLater();
        if (event->timerId() == m_valueTimerId) {
            m_value = m_value + (qrand() % 11 - 5);
            if (m_value > 100) m_value = 100;
            if (m_value < 0) m_value = 0;
        }
    }
    
    void SmoothArc::render(QPainter *p)
    {
        p->setRenderHint(QPainter::Antialiasing);
        int side = qMin(width(), height());
        p->scale(side / 200.0, side / 200.0);
    
        QRectF rect(10, 10, 180, 180);
        QPen pen = p->pen();
        pen.setWidth(10);
        p->setPen(pen);
        p->drawArc(rect, 90*16, (360*(m_value/100.0))*16);
    }
    
    int main(int argc, char **argv)
    {
        QGuiApplication app(argc, argv);
        SmoothArc arc;
        arc.show();
        return app.exec();
    }
    

    完整代码位于https://github.com/yashi/smooth-arc。通常的构建过程如下所示。

    git clone https://github.com/yashi/smooth-arc.git
    cd smooth-arc
    qmake
    make
    ./smooth-arc
    

1 个答案:

答案 0 :(得分:2)

我不熟悉仅针对此问题的Qt Gui方法,因此我将展示如何使用Qt Widgets来实现。

  
      
  1. 平滑重绘圆弧。当前代码直接重绘当时的值。我甚至不使用子角度值。结果在弧的末端有视觉上的噪音。
  2.   

您可以使用Qt&#39; animation framework来插值属性更改:

#include <QtWidgets>

class SmoothArc : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)

public:
    SmoothArc();

    qreal value() const;
    void setValue(qreal value);

signals:
    void valueChanged();

protected:
    void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
    void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;

private:
    int m_valueTimerId;
    qreal m_value;
    QPropertyAnimation m_animation;
};

SmoothArc::SmoothArc()
{
    resize(200, 200);

    m_valueTimerId = startTimer(100);
    m_value = 50;
    m_animation.setTargetObject(this);
    m_animation.setPropertyName("value");
}

qreal SmoothArc::value() const
{
    return m_value;
}

void SmoothArc::setValue(qreal value)
{
    if (qFuzzyCompare(value, m_value))
        return;

    m_value = value;
    update();
    emit valueChanged();
}

void SmoothArc::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == m_valueTimerId) {
        qreal newValue = m_value + (qrand() % 11 - 5);
        if (newValue > 100) newValue = 100;
        if (newValue < 0) newValue = 0;

        if (m_animation.state() == QPropertyAnimation::Running)
            m_animation.stop();
        m_animation.setStartValue(m_value);
        m_animation.setEndValue(newValue);
        m_animation.start();
    }
}

void SmoothArc::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    int side = qMin(width(), height());
    p.scale(side / 200.0, side / 200.0);

    QRectF rect(10, 10, 180, 180);
    QPen pen = p.pen();
    pen.setWidth(10);
    p.setPen(pen);
    p.drawArc(rect, 90*16, (360*(m_value/100.0))*16);
}

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    SmoothArc arc;
    arc.show();
    return app.exec();
}

#include "main.moc"

arc animation gif

  
      
  1. 将图形与V-Sync一起更新。这样我就不会浪费计算能力来进行不显示的重绘。我不知道如何通过V-Sync触发渲染事件,所以我设置了33毫秒的计时器。当m_value变化小于30毫秒时,这是必需的。
  2.   

如果您使用update()

,我认为Qt应该为您处理此问题
  

更新小部件,除非禁用更新或隐藏小部件。

     

此功能不会立即重绘;相反,当Qt返回主事件循环时,它会调度一个paint事件进行处理。这允许Qt优化以获得比调用repaint()更快的速度和更少的闪烁。

     

多次调用update()通常只会导致一次paintEvent()调用。