进度条设计模式?

时间:2010-03-24 09:35:22

标签: design-patterns oop progress-bar

我正在编写的应用程序执行长度算法,通常需要几分钟才能完成。在此期间,我想向用户显示一个进度条,指示尽可能精确地完成了多少算法。

算法分为几个步骤,每个步骤都有自己的典型时序。例如 -

  • 初始化(500毫秒)
  • 阅读输入(5秒)
  • 步骤1(30秒)
  • 第2步(3分钟)
  • 写输出(7秒)
  • 关闭(10毫秒)

每个步骤都可以通过设置其工作范围,比如[0到150],然后在主循环中报告它完成的值,很容易地报告其进度。

我目前设置的是嵌套进度监视器的方案,它构成了一种隐含的进度报告树。

所有进度监视器都从接口IProgressMonitor继承:

class IProgressMonitor
{
public:
  void setRange(int from, int to) = 0;
  void setValue(int v) = 0;
};

树的根是ProgressMonitor,它连接到实际的GUI界面:

class GUIBarProgressMonitor : public IProgressMonitor
{
   GUIBarProgressMonitor(ProgressBarWidget *);
};

树中的任何其他节点都是监视器,它控制父进程的一部分:

class SubProgressMonitor : public IProgressMonitor
{
  SubProgressMonitor(IProgressMonitor *parent, int parentFrom, int parentLength)
  ...
};

SubProgressMonitor可以控制其父级的[parentFrom, parentFrom+parentLength]范围。

通过该方案,我能够根据全局时间中每个步骤的预期相对部分静态地划分顶级进度。然后可以将每个步骤进一步细分为等等“

这样做的主要缺点是除法是静态的,根据运行时发现的变量进行更改会很痛苦。

所以问题是:是否有任何已知的进度监控设计模式可以解决这个问题?

5 个答案:

答案 0 :(得分:5)

一种非常有趣的方法是用户感知。

Chris Harrison发表了一篇关于用户如何根据进度条报告的进度感知时间的文章(尽管实际持续时间在所有实验中明显相同)

请注意,首选显示公式为(x +(1-x)/ 2) 8 其中x是0到1比例的实际进度:)

因此,我建议:

  • 收集有关给定任务的时间百分比的一些统计数据
  • 测量初始化并使用它来缩放进度条上的进度,悲观(例如准备10-15%的缓冲区)
  • 就在最后一个任务之前(或者是最后几个任务,只要它们具有确定的持续时间),全力以赴以便及时完成进度条(具有渐进式加速)

我知道,这不准确,但如果用户认为它更快,我会满足于此!

答案 1 :(得分:4)

Péter是我在一个大型项目上采用的方法;在我们的试点和首次推出期间,我们的数千个移动设备中的每一个都发回了时间和使用数据,我们使用了微调任务配置所花费的时间的平均值,中位数和标准差(当时允许任务运行,允许运行多长时间,进度条显示中使用的值等等。由于我们的解决方案有点像你的解决方案,但是由XML配置文件中提供的值驱动,我们考虑将其构建为自动化系统(例如,服务器会在某个时间间隔内检查这些值,注意某些任务在最近需要更长时间比他们过去的日子更新配置文件以重新安排或延长它们了,但是认为仅仅为了防止每隔几周进行一次快速的人工审查就不值得麻烦。

由于我不知道您的问题的技术解决方案,我认为您向用户展示的内容(以及您花费多少时间开发解决方案)应基于功能性问题:谁在使用此功能?信息需要多准确?这是一个互动的过程,在此过程中他们不能做任何其他工作,或者他们能让它在后台运行并回到它吗?工作过程中您的长时间运行功能是时间敏感型还是关键任务型?

对不起,我实际上无法给你你正在寻找的答案,但也许考虑一下你想要实现的目标,会让你失去一个好主意。 =)

答案 2 :(得分:2)

构建一个AggregateProgressMonitor,它根据子进度监视器报告的信息自动计算子进度分区。儿童进步监测器至少应告知父母“预期”的运行时间。然后可以根据运行时参数通过各自的操作更新子监视器估计的运行时间,并相应地自动调整整体进度报告。

像这样......

class IAggregateProgressMonitor : public IProgressMonitor
{
   void setChildValue(IProgressMonitor *, int v);
   void setChildEstimatedTime(IProgressMonitor *, int v);
}

class AggregateProgressMonitor : public IAggregateProgressMonitor 
{
   void setChildValue(IProgressMonitor * child, int v)
   {
      int aggregateValue = mapChildValueToAggregateValue(child, v);
      setValue(aggregateValue);
   }

   void setChildEstimatedTime(IProgressMonitor * child, ulong ms)
   {
      children[child]->estimatedTime = ms;
      updateChildProgressRatios();
   }
}

class SubProgressMonitor : public IProgressMonitor
{
  SubProgressMonitor(IAggregateProgressMonitor *parent, int parentFrom, 
                     int parentLength) ... ;
  void setValue(int v)
  {
     parent->setChildValue(this, v);
  }

  void setEstimatedRunningTime(ulong ms)
  {
    parent->setChildEstimatedTime(this, ms);
  } 
};

您甚至可以使用第一步的观察时间重新映射后续进度报告器,以便更准确。

您需要在AggregateProgressMonitor中保留某种有序的地图,以便能够跟踪和计算儿童的所有信息。

完成后,您可以扩展AggregateProgressMonitor(覆盖IProgressMonitor方法)以向用户显示进度。

答案 3 :(得分:1)

这是一个棘手的问题,我们在以前的项目中也一直在努力。

我能想到的最好的方法是收集每个阶段在现实生活中实际需要多长时间的统计数据,并相应地调整相对间隔长度。

我们还没有在那个项目中实现它(至少只要我在那里),所以这只是一个理论上的想法: - )

答案 4 :(得分:0)

您可以考虑使用进度循环替换进度条。如果任务有N个步骤,则在饼图中进行N个楔形,并在该步骤运行时像进度条一样填充每个楔形。

作为一个额外的步骤,可能会为每个步骤显示一些文本,因此在步骤进行时他们可以阅读。