动画 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 中没有动画的主要内容,如果未能解决你的问题,请参考以下文章