进度条与Gtkmm

时间:2017-04-20 13:37:46

标签: c++ gtkmm gtkmm3

您好我正在寻找gtkmm的信号。基本上我正在做一些模拟,我想要的是这样的:

我假设我做了5次模拟:

shardCollection

但我不知道我必须使用哪个信号以及如何转换为此信号。

非常感谢你的帮助!!!

1 个答案:

答案 0 :(得分:0)

您在问题中提供的伪代码实际上应该有效 - 无需信号。但是,您可以在模拟中引入信号以更新进度条。恕我直言,这不会解决你的问题,我会尝试解释为什么以及如何解决它:

你提供的上下文有点太少了,所以,我将介绍一些更多的假设:你有一个主窗口,其中包含一个按钮或工具栏项或菜单项(甚至所有这些项),它们开始模拟。

让我们设想你在Gtk::ProgressBar::set_fraction()设置一个断点。

一旦调试器在此断点处停止,您将在堆栈跟踪上找到以下调用(可能还有许多其他调用):

  • 的Gtk ::主:: run()的
  • 小部件的信号处理程序或启动模拟的动作
  • 运行五个模拟的功能
  • 并持续拨打Gtk::ProgressBar::set_fraction()

如果您可以检查Gtk::ProgressBar的内部,您会注意到Gtk::ProgressBar::set_fraction()中的所有内容都已正确完成。那有什么不对?

当你调用Gtk::ProgressBar::set_fraction()时,它可能会生成一个公开事件(即向Gtk::Main内的事件队列添加一个事件,并请求自己刷新)。问题是,在完成模拟的所有五次运行之前,您可能不会处理请求。 (请记住,对此负责的Gtk::Main::run()是我的想象堆栈跟踪的最高/最外调用。)因此,在模拟结束之前不会进行刷新 - 这太晚了。 (顺便说一句,Gtk +的作者在手册中的某处说明了他们优化事件的聪明才智。也就是说,事件队列中Gtk::ProgressBar最终可能只有一个公开事件,但这并不能使你的情况变得更好。)< / p>

因此,在调用Gtk::ProgressBar::set_fraction()之后,必须以某种方式刷新事件队列,然后再进行模拟。 这听起来像离开模拟,离开调用小部件信号处理程序,返回Gtk::Main::run()进行进一步的事件处理,最后回到下一个模拟步骤 - 可怕的想法。但我们做得更简单了。为此,我们基本上使用以下代码(在gtkmm 2.4中):

while (Gtk::Main::events_pending()) Gtk::Main::iteration(false);

(这应该与您使用的gtkmm版本相同,但如有疑问,请参阅手册。)

应在更新进度条分数后立即进行,并在继续模拟之前完成。

这递归地进入主循环的(部分)并处理Gtk::Main的事件队列中的所有未决事件,因此,在模拟继续之前,进度条被暴露。您可能会担心&#34;递归地进入主循环&#34;但是我在GTK +手册的某个地方读到它是允许的(并且合理地解决了这样的问题)以及需要注意的事项(即限制递归次数并授予适当的&#34;回滚&#34;)

在您的情况下,我们称之为一般长时间运行的功能的模拟是什么。因为这些长时间运行的函数是算法(​​在任何东西的库中),不会受到任何GUI内容的污染,我们围绕这个基本概念构建了一些管理基础结构,包括

  • 进步&#34;代理&#34;具有update(double)方法和信号槽的对象
  • 一个自定义进度对话框,可以将信号处理程序连接到这样的进度对象(即其信号槽)。

长时间运行的函数获取进度对象(作为参数),并负责以适当的时间间隔使用适当的进度因子调用Progress::update()方法。 (我们只使用[0,1]范围内的值。)

一个问题是调用进度更新的间隔。如果经常调用它,GUI将显着减慢长时间运行的功能。相反的情况(称之为不够经常)会导致GUI的响应性降低。因此,我们更经常决定进度更新。为了减少GUI的耗时,我们记住了进度对话框中上次更新的时间,并跳过下一次刷新,直到测量自上次刷新后的特定持续时间。因此,长时间运行的功能对于进度更新仍有一些额外的努力,但它不再可识别。 (良好的刷新间隔是恕我直言0.1秒 - 人类的感知阈值,但如果有疑问,你可以选择0.05秒。)

刷新所有待处理事件也会导致处理鼠标事件(以及其他GTK +信号)。这允许另一个有用的功能:中止长时间运行的功能。

当&#34;取消&#34;按下我们进度对话框的按钮,它设置一个内部标志。如果下次检查标志时更新进度。如果该标志变为true,则会抛出一个特殊异常。 throw立即中止进度更新的调用者(长时间运行的函数)。必须在按钮的信号处理程序(或任何称为长时间运行的函数)中捕获此异常。否则,它将会#34;到Gtk::Main中的事件调度程序,它肯定会被捕获,这将中止您的应用程序。 (每当我忘记捕捉时,我经常看到它。)另一方面:捕捉特殊异常清楚地说明长时间运行的功能已经中止(相反通过常规返回结束)。这可能也可能不是可以在GUI上声明的内容。

最后,上述解决方案可能会导致另一个问题:它可以在模拟运行时启动模拟(通过GUI)。这是可能的,因为可以在进行更新时处理用于模拟开始的按钮按下。为了防止这种情况,实际上有一个简单的解决方案:在GUI中模拟开始时设置一个标志,直到它完成,并防止在设置标志时进一步启动。另一个选项可以是在模拟开始时使窗口小部件/操作不敏感。如果您的应用程序中有多个不同的长时间运行函数可能会或可能不会相互排斥,则此主题会变得更加复杂 - 导致类似排除矩阵的行为。好吧,我们务实地解决了它......(但没有矩阵)。

最后但并非最不重要的是,我想提一下,我们使用类似的概念来输出日志视图(例如,在任何长时间运行的过程中,可视化记录信息,警告和错误)。恕我直言,为最终用户提供一些视觉动作总是好的。否则,他们可能会感到无聊并使用电话抱怨(太慢)软件实际上会让你花时间加快速度(你必须打破一个恶性循环......)