如何使用 Qt QPainter 平滑地增加一个角度/长度变化的弧?这是我刚刚从 Qt 的 Analog Clock Window Example 创建的最少代码.
代码在 50 毫秒内随机更改 m_value
+- 5。这是为了模拟我想要实现的实际行为。圆弧从 12 点钟位置开始并逆时针增长。 m_value
缩放以适应 360 度(12 点到 12 点)。
我的目标是根据给定的(模拟)值实时平滑地改变弧长,而不管输入值抖动如何。
我想完成两件事:
- 圆弧的平滑重绘。当前代码直接重绘了 当时的值(value)。我什至不使用子角度值。结果 在弧线末端视觉上有噪音。
- 使用 V-Sync 更新绘图。为了不浪费
非显示重绘的计算能力。我不知道怎么办
通过 V-Sync 触发
render
事件,所以我设置了 33 毫秒 定时器。当m_value
的变化时间少于 30 毫秒时,这是必需的。
我不想要的
- 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
最佳答案
我不熟悉仅使用 Qt Gui 的方法来解决这个问题,所以我将展示如何使用 Qt Widgets 来解决这个问题。
- Smooth redraw of the arc. The current code directly redraw the value at the time. I does not even use sub angle value. The result is visually noisy at the end of the arc.
您可以使用 Qt 的 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"
- Update the drawing in along with V-Sync. So that I don't waste computation power for non-displayed redraw. I don't know how to trigger render event by V-Sync, so I've setup 33-millisecond timer. This is needed when m_value changes in less than 30 msec.
如果您使用 update()
,我认为 Qt 应该会为您处理这个问题:
Updates the widget unless updates are disabled or the widget is hidden.
This function does not cause an immediate repaint; instead it schedules a paint event for processing when Qt returns to the main event loop. This permits Qt to optimize for more speed and less flicker than a call to repaint() does.
Calling update() several times normally results in just one paintEvent() call.
关于c++ - 使用 QPainter 流畅绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35532459/