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

Posted

技术标签:

【中文标题】动画 gif 图像在我的无模式 Gtk::Dialog 中没有动画【英文标题】:Animated gif image isn't being animated in my modeless Gtk::Dialog 【发布时间】:2021-03-12 19:06:48 【问题描述】:

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

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

我正在使用 gtkmm 3.0

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

main.cc

#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.h

#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

examplewindow.cc

#include "examplewindow.h"
#include <iostream>

ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::ORIENTATION_VERTICAL),
      m_ButtonBox(Gtk::Orientation::ORIENTATION_VERTICAL),
      m_Button("Show Dialog")

  set_title("Test animated gif");
  set_default_size(800, 600);

  add(m_VBox);

  m_VBox.pack_start(m_ButtonBox);
  m_ButtonBox.pack_start(m_Button);
  m_Button.set_hexpand(true);
  m_Button.set_halign(Gtk::Align::ALIGN_CENTER);
  m_Button.set_valign(Gtk::Align::ALIGN_CENTER);
  m_Button.grab_focus();
  m_Button.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_clicked));

  show_all_children();


ExampleWindow::~ExampleWindow()



void ExampleWindow::on_button_clicked()

  Gtk::Dialog m_Dialog;
  m_Dialog.set_transient_for(*this);
  m_Dialog.set_size_request(200, 200);
  m_Dialog.set_decorated(false);
  Gtk::Image imageLoading = Gtk::Image();
  imageLoading.property_pixbuf_animation() = Gdk::PixbufAnimation::create_from_file("gtkmm_logo.gif");
  m_Dialog.get_vbox()->pack_start(imageLoading);
  m_Dialog.show_all();

  m_Dialog.run();

  /******** This, below, never gets executed as run() is blocking the program...********/
  
  // Dummy "long" operation
  for (int i = 0; i <= 2010101010; i++)
  
    if (i == 2010101010)
      std::cout << "Done" << std::endl;
  

  m_Dialog.response(Gtk::RESPONSE_ACCEPT);
  m_Dialog.hide();

【问题讨论】:

刚刚添加了一个简单的例子;提前谢谢你! 我已经从您的原始问题中恢复了一些部分。您的编辑让您进入了XY problem 的领域。你真正的问题是让事情变得生动起来。将您的注意力转移到run() 会忽略run() 不是适合该工作的工具的可能性,这可能会完全偏离您的问题。 【参考方案1】:

让我们看看原来的问题。您在上面创建了一个名为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() 的调用更有效。这有点重构,但它可能值得花时间。我将介绍两个选项(没有代码)。

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

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


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

【讨论】:

以上是关于动画 gif 图像在我的无模式 Gtk::Dialog 中没有动画的主要内容,如果未能解决你的问题,请参考以下文章

IOS支持显示GIF动画图片吗? [复制]

如何在我的 Gatsby 博客网站中有效地显示 GIF 图像?

使用动画背景

使用 PHP/CodeIgniter 动态检测动画 GIF

在颤振中使用动画 gif

在 Cocoa 中创建动画 GIF - 定义帧类型