在我的无模式 Gtk::Dialog 中,动画 gif 图像没有被动画化

问题描述

我的目标是在 Gtk::Dialog 中显示一个简短的“请稍候...”对话框和动画 gif(微调器)。

我的问题是,当我不使用 Gtk:Dialog::run() 时,gif 不会被动画化,而当我 do 使用 Gtk:Dialog::run() 方法时,它完全阻止了我的运行之后的代码。而且由于我的对话框中没有任何按钮,它会无限期地挂在那里。有没有办法解决这个问题?我没有成功地让动画 gif 在非模态对话框中工作,即没有使用 run() 方法。

我使用的是 gtkmm 3.0

编译:{{1​​}}

main.cc

g++ examplewindow.cc main.cc -o main `pkg-config gtkmm-3.0 --cflags --libs` 

examplewindow.h

#include "examplewindow.h"
#include <gtkmm/application.h>
#include <iostream>

int main(int argc,char *argv[])
{
  auto app = Gtk::Application::create("org.gtkmm.example");

  ExampleWindow window;    

  //Shows the window and returns when it is closed.
  //return app->make_window_and_run<ExampleWindow>(argc,argv);
  return app->run(window);
}

examplewindow.cc

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_clicked();

  //Child widgets:
  Gtk::Box m_VBox;
  Gtk::Box m_ButtonBox;
  Gtk::Button m_Button;
};

#endif //GTKMM_EXAMPLEWINDOW_H

解决方法

让我们看看原来的问题。您在其上创建了一个名为 show() 的对话框,执行了一些长时间运行的过程,然后关闭了该对话框。该过程有效,但您的程序在处理过程中冻结了。这是为什么?

图形界面通过处理消息(事件)来工作。一些事件运行计时器,例如告诉动画进入下一帧的事件。有些是根据需要生成的,例如告诉图像绘制当前帧的那些。这些事件需要被触发和处理才能生效。您通过调用 show_all() 触发了适当的事件,但您没有给程序处理这些事件的机会。

您使用了一个按钮点击来启动您的长期运行过程。该单击是由主事件处理循环处理的事件。该循环然后等待点击被完全处理,然后再进入下一个事件。但是,您在处理程序中有长时间运行的进程。主事件循环必须等待该过程完成才能处理新事件,例如显示和动画图像的事件。在您销毁对话之前,您从未给它机会完成它的工作。

调用对话框的 run() 方法通过为对话框启动一个新的事件循环来部分修复这种情况。因此,即使主事件循环仍然被您的点击处理程序阻塞,新的事件也可以被处理。对话框的事件循环接收到显示动画所需的事件,因此您的程序再次响应。很遗憾,run() 阻止了您长时间运行的进程,因此我们的情况也没有好到哪里去。


最简单的解决方法是不再完全阻塞主事件循环。您可以让长时间运行的进程定期允许通过 Gtk::Main::iteration() 处理事件。此函数调用主事件循环的迭代,使您的程序保持响应。向它传递一个 false 参数,以便它仅在有需要处理的事件时才处理事件(而不是等待事件发生)。

    for (unsigned long i = 0; i <= 2010101010; i++)
    {
        if (i == 2010101010)
          std::cout << "Done" << std::endl;

        // Periodically process events
        if ( i % 10000 == 0 )                    // <---- after some suitable amount of work
            if ( !Gtk::Main::iteration(false) )  // <---- allow events to be processed
                // Abort the work.
                break;
    }

返回值应该告诉你是否应该退出,但我在我的测试中没有得到它(与文档相比,返回值似乎具有相反的含义)。也许对话框本身使应用程序保持活动状态?呃,这可能是下一个问题,一旦这部分工作。


其他方法会将您长时间运行的进程移出点击处理程序。如果您让点击处理程序快速结束,主事件循环可以在没有您额外提示的情况下完成其工作。但是,这需要进行一些调整,以便 Gtk::Dialog 比对 on_button_clicked() 的调用寿命更长。这有点重构,但可能值得花时间。我将提供两个选项(没有代码)。

  1. 您可以让您的工作在多个 timeout signals 上运行。将您长时间运行的流程分成较小的块,每个块的大小都适合回调。 (那有多大?不确定。现在,假设最多几毫秒。)让按钮单击事件以允许 GUI 更新的优先级启动第一个超时信号。 (我记得,PRIORITY_DEFAULT_IDLE 应该有效。)对于间隔,如果 0 不会过度混淆 Gtk+,我会尝试。 (我还没有尝试过,但它似乎是合理的。)如果 0 间隔有效,则使用 connect_once() 而不是 connect() 可能是明智的,并让每个块安排下一个超时时间。最后一块将负责关闭对话框。

  2. 您可以将长时间运行的进程移至另一个线程。多线程编程有它自己的一系列问题,有时还有很多设置,但这是它非常适合的事情。如果长时间运行的进程与主事件循环位于不同的线程中,操作系统将负责确保每个线程获得一些 CPU 时间。您的长时间运行的流程可能会突然消失,而主事件循环将能够同时处理事件,而无需您的特殊干预。


最后说明:
如果您的对话是为了与用户进行单向交流,那么它看起来更像是独白而不是对话。不好意思,更像是一个普通的窗口而不是一个对话框。此外,我会确保您了解 Gtk::ProgressBar,它“通常用于显示长时间运行的操作的进度”。只是一个选择;喜欢你的形象是可以理解的。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...