Стабилизировать частоту вызовов QWidget :: paintEvent ()

Насколько я понимаю, paintEvent() выполняется в «основном цикле» QApplication объект, и может тратить время на выполнение своих внутренних системных задач, задерживая выполнение помещенных в очередь слотов или другие события.

Но что, если мне нужно воспроизвести очень плавную анимацию, и я замечаю периодические задержки основного цикла в этой анимации? Могу ли я создать отдельный специальный очень стабильный «основной цикл» и переназначить paintEvent() зовет к этому?

Постскриптум Да, GPU, OpenGL и другие приятные технологии были изобретены для плавной анимации, похожей на игру, я знаю, я знаю.

Моя программа: http://www.youtube.com/watch?v=KRk_LNd7EBg

Решение

Стабилизация частоты вызовов paintEvent (), которую я ищу, GPU, OpenGL или аппаратный vsync мне не помогут! Проблема нормальная, пока я не вычислю позицию пикселя в целых числах. Там всегда будут импульсы скорости движения пикселей. Чтобы решить мою «проблему», мне нужно измерить координаты в действительных числах (double, float) и реализовать алгоритм сглаживания.

0

Решение

Что вам нужно сделать, это то, что вы хотите, но в противоположном направлении. Вы предлагаете специальный «стабильный» основной цикл. Вместо этого вы хотите делать все, кроме GUI, «вещи» в потоке GUI. Это сделает основной цикл событий «стабильным».

update() добавляет порядок как «пожалуйста перекрасить!» в основной цикл, но основной цикл может быть занят, поэтому анимация будет отставать

Основной цикл будет не будьте заняты чем-либо, если он не выполняет код, который вы написали, и у вас нет явного контроля над ним. В этом нет никакой магии. Если вы не запускаете код в основном цикле, он не будет занят. Ваш комментарий выше не соответствует действительности. Если вы не запустите материал в основном цикле, он не будет занят, и все произойдет сразу же, как только update() называется. Возможно, вы захотите отследить выполнение кода в отладчике, чтобы убедиться в этом.

Сам по себе Qt не перегружает основной цикл обработки событий ненужными задачами, если вы не попросите его сделать это. То, что вы хотите, это обработать все, кроме взаимодействия с GUI, в другом потоке. Такие вещи, как доступ к сети, доступ к файлам, даже QSettings доступ — все это должно быть сделано в QObjectкоторые живут в рабочем потоке. Только основной поток GUI должен обрабатывать взаимодействие с пользователем, и только в минимальной манере — он должен делать только то, что непосредственно необходимо, чтобы реагировать на события и перерисовывать вещи. Любая другая обработка должна выполняться вне потока GUI. Вот как вы получаете плавную анимацию.

Еще одна важная вещь заключается в том, что ваши анимации должны управляться в реальном времени, а не предполагаемым временем. Таким образом, когда вы шагаете анимацию, вы должны использовать QElapsedTime чтобы измерить, сколько времени прошло с последнего шага, и использовать это время для расчета анимированных переменных. QAbstractAnimation и друзья уже справляются с этим для вас. Если вы не используете их, вам нужно будет сделать это самостоятельно.

Я догадываюсь, что ваш код просто плохой и делает что-то не-Qt-идиоматическим образом, и поэтому страдает. Вероятно, есть простые архитектурные причины того, почему это не гладко.

Ниже приведен простой пример того, как вы можете сделать это в QWidget, Обратите внимание на заметное отсутствие всего, что связано со временем, кроме расчета FPS. Это красота Qt. paintEvent() запрашивает анимацию currentValue() непосредственно. Это также может хранить значение в newValue() используйте его вместо слота, хотя это оставляет возможность задержки между временем вычисления значения и временем использования значения — скажем, из-за вытеснения.

Я предоставил пример, который использует Graphics View Framework в другом ответе.

В случае вашего приложения вы должны выбрать, где в осциллограмме визуализировать спектр на основе QElapsedTime так как вы начали воспроизведение. Это все, что нужно.

Пример поддерживает Qt 4/5 и использует QOpenGLWidget на Qt 5.4 и позже вместо тогдашнего устаревшего QGLWidget,

Скриншот

// https://github.com/KubaO/stackoverflown/tree/master/questions/widget-animation-18531776
#include <QtGlobal>
#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
#include <QtWidgets>
typedef QOpenGLWidget GLWidget;
#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
typedef QGLWidget GLWidget;
#else // Qt 4
#include <QtGui>
#include <QtOpenGL>
typedef QGLWidget GLWidget;
#endif

class Widget: public GLWidget
{
QElapsedTimer m_timer;
struct Animation : public QVariantAnimation {
void updateCurrentValue(const QVariant &) {}
} m_anim;
QPolygonF m_polygon;
qreal m_fps;
void paintEvent(QPaintEvent *) {
const qreal t = 0.05;
qreal iFps = 1E9/m_timer.nsecsElapsed();
m_fps = (1.0-t)*m_fps + t*iFps;
int len = qMin(height(), width());
QPainter p(this);
p.drawText(rect(), QString("%1,%2 FPS").arg(m_fps, 0, 'f', 0).arg(iFps, 0, 'f', 0));
p.translate(width()/2.0, height()/2.0);
p.scale(len*.8, len*.8);
p.rotate(m_anim.currentValue().toReal());
p.setPen(QPen(Qt::darkBlue, 0.1));
p.drawPolygon(m_polygon);
p.end();
m_timer.restart();
}
public:
Widget(QWidget *parent = 0) : GLWidget(parent), m_fps(0.0) {
m_anim.setDuration(2000);
m_anim.setStartValue(0);
m_anim.setEndValue(360);
m_anim.setEasingCurve(QEasingCurve::InBounce);
m_anim.setLoopCount(-1);
m_anim.start();
m_polygon.resize(4);
m_polygon[0] = QPointF(-0.3,  0);
m_polygon[1] = QPointF(-0.5,  0.3);
m_polygon[2] = QPointF( 0.5,  0);
m_polygon[3] = QPointF(-0.5, -0.3);
setAutoFillBackground(true);
connect(&m_anim, SIGNAL(valueChanged(QVariant)), SLOT(update()));
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]