Я хочу, чтобы мой QChart динамически обновлялся всякий раз, когда точка добавлялась к объекту QLineSeries, присоединенному к нему, но, похоже, это обновление происходит только после завершения цикла while, который я выполняю. Я использую указанный цикл while в interface.cpp, который вызывает функцию updatePlot (), которая добавляет точку данных в ряд линий, но это обновляет диаграмму только после того, как цикл while полностью завершится. Псевдокод происходящего здесь:
qtwindow.cpp
// Constructor that initializes the series which will be passed into the interface
AlgoWindow::AlgoWindow( ..., TradingInterface* interface, ... ) {
...
QLineSeries* series = new QLineSeries();
QLineSeries* benchmark = new QLineSeries();
QChart* chart = new QChart();
chart->addSeries(series);
chart->addSeries(benchmark);
// Also creates custom axes which are attached to each series
...
}
// Slot connected to a button signal
void AlgoWindow::buttonClicked() {
// Runs the backtest
interface->runbacktest(..., series, benchmark, ...);
}
interface.cpp
void TradingInterface::runbacktest(..., QtCharts::QLineSeries* algoplot, QtCharts::QLineSeries* benchplot) {
// Runs a huge while loop that continuously checks for events
while (continue_backtest) {
if (!eventsqueue.isEmpty()) {
// Handle each event for the bar
} else {
// All events have been handled for the day, so plot
updatePlot(algoplot, benchplot);
}
}
}
void TradingInterface::updatePlot(QtCharts::QLineSeries *algoseries,
QtCharts::QLineSeries *benchseries) {
// Get the date and the information to put in each point
long date = portfolio.bars->latestDates.back();
double equitycurve = portfolio.all_holdings.rbegin().operator*().second["equitycurve"];
double benchcurve = benchmarkportfolio.all_holdings.rbegin().operator*.second["equitycurve"];
// Append the new points to their respective QLineSeries
algoseries->append(date * 1000, equitycurve*100);
benchseries->append(date * 1000, benchcurve*100);
}
Это не дает мне ошибок, и цикл while завершается, но линии отображаются только после выхода из runbacktest (). Затем он отображает все данные правильно, но все сразу.
Мне нужно, чтобы QChart обновлялся каждый раз, когда добавлялись строки, что я предполагал использовать какую-то форму пользовательского слушателя сигнального слота, но я понятия не имею, как это сделать. Если график не будет обновляться до завершения функции, возможно ли это даже в рамках QChart?
Кроме того, я уже попробовал QChart :: update () и QChartView :: repaint (). Оба дали те же результаты, что и без.
РЕДАКТИРОВАТЬ: Я попытался настроить новый поток, который посылает сигнал обратно в основной поток всякий раз, когда данные завершаются, но, похоже, ничего не изменилось. QChart по-прежнему не обновляется до тех пор, пока не будут введены все данные. Я добавил пару строк, чтобы помочь отладке, и похоже, что функция, которая излучает сигнал, работает нормально, но функция слота, которая получает сигнал, запускается только после завершения потока. Мало того, но замедление сигналов во время сна не заставляет его работать медленно (как я думал), поскольку QChart все еще отказывается обновляться до последнего обновления addData ().
Либо удалите цикл while и выполняйте работу по одному с таймером.
Или запустить свой runbacktest
функция в другом потоке и отправить сигнал на обновление QChart
в потоке пользовательского интерфейса, когда данные готовы.
В любом случае вам нужно вернуть управление циклу событий, чтобы можно было перерисовать диаграмму.
Идиома Qt для запуска операции «непрерывно» заключается в использовании «таймера» нулевой длительности. На самом деле это не таймер, но Qt называет его одним.
Вы можете выполнять операцию порциями, которые занимают приблизительно миллисекунду. Для этого инвертируйте поток управления. Qt не предоставляет слишком много синтаксического сахара для него, но это легко исправить.
Преобразуйте этот код, который поддерживает цикл:
for (int i = 0; i < 1000; ++i) {
doSomething(i);
}
в эту лямбду, которая вызывается циклом событий:
m_tasks.addTask([this](i = 0) mutable {
doSomething(i);
++i;
return i < 1000;
});
при условии:
class Controller : public QObject {
Tasks m_tasks;
...
};
где Tasks
Класс поддерживает список задач, которые должны быть выполнены в цикле событий:
class Tasks : public QObject {
Q_OBJECT
QBasicTimer timer;
std::list<std::function<bool()>> tasks;
protected:
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != timer.timerId())
return;
for (auto it = tasks.begin(); it != tasks.end(); ) {
bool keep = (*it)();
if (!keep)
it = tasks.erase(it);
else
++it;
}
if (tasks.empty())
timer.stop();
}
public:
using QObject :: QObject;
template <typename F> void addTask(F &&fun) {
tasks.emplace_back(std::forward(fun));
if (!timer.isActive())
timer.start(0, this);
}
};