【问题标题】:Updating QChart from QLineSeries in a running while loop在运行的 while 循环中从 QLineSeries 更新 QChart
【发布时间】:2018-06-22 22:04:33
【问题描述】:

我想让我的 QChart 动态更新,只要将一个点添加到附加到它的 QLineSeries 对象,但似乎此更新仅在我正在运行的 while 循环完成后发生。我在 interface.cpp 中使用所说的 while 循环,它调用一个函数 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() 的最终更新之后。

【问题讨论】:

    标签: c++ qt qchart


    【解决方案1】:

    要么删除您的 while 循环,然后使用计时器一步一步地执行工作。

    或者在另一个线程中运行您的runbacktest 函数,并在数据准备好时发送信号以更新 UI 线程中的QChart

    无论哪种方式,您都需要将控制权交还给事件循环,以便重新绘制图表。

    【讨论】:

    • 我试图创建一个新线程,它在数据准备好时发出一个信号,但它发出的信号似乎只在线程完成后才被插槽接收。我在主窗口类中初始化了一个自定义 QObject 类,并将其传递给 runbacktest()。该 QObject 有一个信号,该信号连接到主窗口类中的插槽函数,并且它在每天之后发出该信号。但是,我仍然遇到与以前相同的问题。我摆弄着让线程休眠,但问题似乎仍然是 QChart 直到所有数据都进入后才会更新。
    • @s_kirkiles 问题似乎出在您的实现上,我使用了答案中指示的方法并且它工作正常,您可以提供您的线程的minimal reproducible example
    • 通常添加线程来尝试解决问题会导致出现更多问题。那一个建议不是好建议。否则没关系。
    【解决方案2】:

    “连续”运行操作的 Qt 习惯用法是使用零持续时间的“计时器”。它实际上不是一个计时器,但 Qt 称之为计时器。

    您可以在大约一毫秒的时间内分块执行操作。为此,反转控制流。 Qt 并没有为它提供太多的语法糖,但它很容易补救。

    转换这段代码,保持循环:

    for (int i = 0; i < 1000; ++i) {
      doSomething(i);
    }
    

    进入这个由事件循环调用的 lambda:

    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);
      }
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-28
      • 1970-01-01
      • 1970-01-01
      • 2018-08-17
      • 2020-03-01
      • 1970-01-01
      • 2021-12-07
      相关资源
      最近更新 更多