如何在Qt Widgets应用中使用受阻的UI线程运行动画?

时间:2018-07-27 15:31:55

标签: qt qtwidgets

在QML中,您可以使用Animator类型来“即使在UI线程被阻止的情况下,在场景图的渲染线程上进行动画处理”。

如何为Qt小部件实现相同的目的?

基本上,我想要类似的东西:

1) start loading screen / splash-screen
2) start GUI-blocking operation
3) stop loading screen / splash-screen

无法将ui阻止操作移至单独的线程(正在创建小部件)。我无法修改此部分。

我尝试了QQuickWidgetQQuickView,其中包含带有Animator的启动画面场景,但没有用-他们也被阻止了。

编辑:在单独的线程中,我读取了包含UI描述的文件。然后,我递归地创建数百个Widget(包括QQuickWidget,用于图像的QLabel,Web视图等)。

问题的关键在于查看是否有“解决方法”(例如,在具有自己的事件循环的某个单独窗口中显示上述QML场景)。不幸的是,在这一点上,上述内容的整体设计无法做更多的事情。

1 个答案:

答案 0 :(得分:0)

您创建的窗口小部件可能执行了太多工作。您必须确切指定要创建的小部件数量以及创建方式。显示一些示例代码。通常,GUI线程用于协作式多任务处理-如果您有“阻塞”的东西,请将其分解成很小的块。例如,假设您正在处理一些XML或json文件以构建UI。您可以让该任务一次完成一个小部件,然后在每次事件循环将要阻塞时都被调用(即使用零时长的“计时器”并反转控件)。

您还应该在gui线程之外进行尽可能多的工作。即应该读取UI描述并将其转换为有效的表示形式,以将要完成的工作封装在主线程中。这种转换必须异步完成。

最简单的实现方法是将每个小部件的创建封装在引用某个上下文对象的lambda中。这样的lambda将具有签名[...](BatchContext &ctx)。这些lambda的向量也将由CreationContext对象保留:

class BatchContext : public QObject {
  Q_OBJECT
public:
  using Op = std::function<void(CreationContext &)>;
  using QObject::QObject;
  // useful for Op to keep track of where things go
  void push(QWidget *w) { m_stack.push_back(w); }
  QWidget *pop() { return m_stack.isEmpty() ? nullptr : m_stack.takeLast(); }
  QWidget *top() const { return m_stack.isEmpty() ? nullptr : m_stack.last(); }
  int stackSize() const { return m_stack.size(); }
  bool stackEmpty() const { return m_stack.isEmpty(); }

  Q_SLOT void startExec() {
    if (m_execIndex < ops.size())
      m_execTimer.start(0, this);
  }
  template <typename F>
  void addOp(F &&op) { m_ops.push_back(std::forward<F>(op)); }
  ...
private:
  QVector<Op> m_ops;
  QVector<QWidget *> m_stack;
  QBasicTimer m_execTimer;
  int m_execIndex = 0;
  void timerEvent(QTimerEvent *ev) override {
    if (ev->timerId() == m_execTimer.timerId())
      if (!exec())
        m_execTimer.stop();
  }
  /// Does a unit of work, returns true if more work is available
  bool exec() {
    if (m_execIndex < m_ops.size())
      m_ops.at(m_execIndex++)(*this);
    return m_execIndex < ops.size();
  }
};

异步创建上下文之后,可以将其传递到主线程,然后在其中调用startExec(),并且将一次创建小部件。堆栈只是一个示例,说明了如何实现窗口小部件创建过程的一个方面-跟踪什么窗口小部件是“当前父对象”。