【问题标题】:How to run animation with blocked UI thread in Qt Widgets app?如何在 Qt Widgets 应用程序中使用阻塞的 UI 线程运行动画?
【发布时间】:2018-07-27 15:31:55
【问题描述】:

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

如何为 Qt Widgets 实现相同的功能?

基本上,我想要这样的东西:

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

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

我尝试了 QQuickWidgetQQuickView 包含带有 Animator 的初始屏幕场景,但它不起作用 - 它们也被阻止了。

编辑:在单独的线程中,我阅读了包含 UI 描述的文件。然后我递归地创建数百个小部件(包括 QQuickWidgets、图像的 QLabels、Web 视图等)。

问题的重点是查看是否有“解决方法”(例如,在具有自己的事件循环的某个单独窗口中显示上述 QML 场景)。不幸的是,在这一点上,对于上述的整体设计,我们无能为力。

【问题讨论】:

  • 您不应阻塞 GUI 线程。如果你说阻塞任务是创建小部件,那闻起来就像一个糟糕的设计。

标签: qt qtwidgets


【解决方案1】:

可能您正在创建的小部件工作量太大。您必须准确指定要创建的小部件数量以及创建方式。显示一些示例代码。一般来说,GUI 线程用于协作多任务处理——如果你有一些“阻塞”的东西,把它分解成小块。例如,假设您正在处理一些 XML 或 json 文件来构建 UI。您可以让该任务一次执行一个小部件,并在每次事件循环即将阻塞时被调用(即使用零持续时间“计时器”并反转控制)。

您还应该在 gui 线程之外做尽可能多的工作。 IE。应该读取 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(),并且将一次创建一个小部件。堆栈只是您如何实现小部件创建过程的一个方面的一个示例 - 跟踪哪个小部件是“当前父级”。

【讨论】:

  • 谢谢!我编辑了问题以回答您的评论。长话短说:它确实是从基于文件的表示中构建 UI,尽管现在对此无能为力。因此需要更“丑陋”的解决方案。
猜你喜欢
  • 2012-06-26
  • 2015-05-07
  • 1970-01-01
  • 1970-01-01
  • 2020-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多