动画 gif 图片在我的无模式 Gtk::Dialog 中没有动画
Animated gif image isn't being animated in my modeless Gtk::Dialog
我的目标是在 Gtk::Dialog.
中显示带有动画 gif(微调器)的简短“请稍候...”对话框
我的问题是,当我不使用Gtk:Dialog::run()
时,gif不会被动画化,而当我使用时,使用Gtk:Dialog::run()
方法之后它完全阻止了我的 运行ning 代码。而且由于我的对话框中没有任何按钮,它会无限期地挂在那里。有办法解决吗?我没有成功让动画 gif 在非模态对话框中工作,即不使用 运行() 方法。
我正在使用 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();
}
让我们看看原来的问题。您在上面创建了一个名为 show()
的对话框,进行了一些长时间的 运行 处理,然后关闭了该对话框。该过程有效,但您的程序在处理过程中冻结了。这是为什么?
图形界面通过处理消息(事件)来工作。一些事件 运行 关闭计时器,例如告诉动画转到下一帧的事件。有些是根据需要生成的,例如告诉图像绘制当前帧的那些。这些事件需要被触发和处理才能有效。您通过调用 show_all()
触发了适当的事件,但您没有给程序处理这些事件的机会。
您通过单击按钮开始了漫长的 运行ning 过程。该点击是由您的主事件处理循环处理的事件。然后该循环等待点击被完全处理,然后再继续下一个事件。但是,您在处理程序中有 long-运行ning 进程。主事件循环必须等待该过程完成,然后才能处理新事件,例如显示和动画化图像的事件。在你销毁它之前,你从来没有给过你的对话框完成它的工作的机会。
调用对话框的 run()
方法通过为对话框启动一个新的事件循环部分地解决了这个问题。因此,即使主事件循环仍被您的点击处理程序阻塞,也可以处理新事件。对话框的事件循环接收到显示动画所需的事件,因此您的程序再次响应。不幸的是,run()
阻止了您漫长的 运行ning 过程,所以我们的情况并没有好转。
最简单的解决方法是不再完全阻塞主事件循环。您可以让您的 long-运行ning 进程定期允许通过 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;
}
return 值应该告诉你是否应该退出,但我在测试中没有得到它的工作(return 值似乎具有相反的含义与文档相比)。也许对话框本身让应用程序保持活力?嗯,这可以是下一个问题,一旦这部分工作。
其他方法会将您的长 运行ning 过程从点击处理程序中移出。如果你让点击处理程序快速结束,主事件循环可以在没有你额外提示的情况下完成它的工作。但是,这需要进行一些调整,以便 Gtk::Dialog
比对 on_button_clicked()
的调用更有效。这有点重构,但可能值得花时间。我将提供两个选项(没有代码)。
您可以让您的工作在多个 timeout signals 上运行。将长 运行ning 进程分成更小的块,每个块的大小适合回调。 (那有多大?不确定。现在,我们假设最多几毫秒。)让按钮单击事件启动第一个超时信号,优先级允许 GUI 更新。 (我记得,PRIORITY_DEFAULT_IDLE
应该有效。)对于间隔,如果这不会过度混淆 Gtk+,我会尝试 0
。 (我没有尝试过,但它似乎是合理的。)如果 0 间隔有效,使用 connect_once()
而不是 connect()
可能是明智的,并且让每个块安排下一个超时。最后一个块将负责关闭对话框。
您可以将长时间 运行ning 进程移动到另一个线程。多线程编程有其自身的一系列问题,有时还有很多设置,但这是它非常适合的事情。如果您的 long-运行ning 进程与主事件循环处于不同的线程中,则操作系统将负责确保每个线程获得一些 CPU 时间。您的长 运行ning 进程可以突然消失,并且主事件循环将能够同时处理事件而无需您的特殊干预。
最后的笔记:
如果您的对话是为了与用户进行单向交流,那么它看起来更像是独白而不是对话。对不起,更像是一个普通的 window 而不是对话。此外,我将确保您了解 Gtk::ProgressBar
,它“通常用于显示长时间 运行ning 操作的进度。”只是一个选择;喜欢你的形象是可以理解的。
我的目标是在 Gtk::Dialog.
中显示带有动画 gif(微调器)的简短“请稍候...”对话框我的问题是,当我不使用Gtk:Dialog::run()
时,gif不会被动画化,而当我使用时,使用Gtk:Dialog::run()
方法之后它完全阻止了我的 运行ning 代码。而且由于我的对话框中没有任何按钮,它会无限期地挂在那里。有办法解决吗?我没有成功让动画 gif 在非模态对话框中工作,即不使用 运行() 方法。
我正在使用 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();
}
让我们看看原来的问题。您在上面创建了一个名为 show()
的对话框,进行了一些长时间的 运行 处理,然后关闭了该对话框。该过程有效,但您的程序在处理过程中冻结了。这是为什么?
图形界面通过处理消息(事件)来工作。一些事件 运行 关闭计时器,例如告诉动画转到下一帧的事件。有些是根据需要生成的,例如告诉图像绘制当前帧的那些。这些事件需要被触发和处理才能有效。您通过调用 show_all()
触发了适当的事件,但您没有给程序处理这些事件的机会。
您通过单击按钮开始了漫长的 运行ning 过程。该点击是由您的主事件处理循环处理的事件。然后该循环等待点击被完全处理,然后再继续下一个事件。但是,您在处理程序中有 long-运行ning 进程。主事件循环必须等待该过程完成,然后才能处理新事件,例如显示和动画化图像的事件。在你销毁它之前,你从来没有给过你的对话框完成它的工作的机会。
调用对话框的 run()
方法通过为对话框启动一个新的事件循环部分地解决了这个问题。因此,即使主事件循环仍被您的点击处理程序阻塞,也可以处理新事件。对话框的事件循环接收到显示动画所需的事件,因此您的程序再次响应。不幸的是,run()
阻止了您漫长的 运行ning 过程,所以我们的情况并没有好转。
最简单的解决方法是不再完全阻塞主事件循环。您可以让您的 long-运行ning 进程定期允许通过 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;
}
return 值应该告诉你是否应该退出,但我在测试中没有得到它的工作(return 值似乎具有相反的含义与文档相比)。也许对话框本身让应用程序保持活力?嗯,这可以是下一个问题,一旦这部分工作。
其他方法会将您的长 运行ning 过程从点击处理程序中移出。如果你让点击处理程序快速结束,主事件循环可以在没有你额外提示的情况下完成它的工作。但是,这需要进行一些调整,以便 Gtk::Dialog
比对 on_button_clicked()
的调用更有效。这有点重构,但可能值得花时间。我将提供两个选项(没有代码)。
您可以让您的工作在多个 timeout signals 上运行。将长 运行ning 进程分成更小的块,每个块的大小适合回调。 (那有多大?不确定。现在,我们假设最多几毫秒。)让按钮单击事件启动第一个超时信号,优先级允许 GUI 更新。 (我记得,
PRIORITY_DEFAULT_IDLE
应该有效。)对于间隔,如果这不会过度混淆 Gtk+,我会尝试0
。 (我没有尝试过,但它似乎是合理的。)如果 0 间隔有效,使用connect_once()
而不是connect()
可能是明智的,并且让每个块安排下一个超时。最后一个块将负责关闭对话框。您可以将长时间 运行ning 进程移动到另一个线程。多线程编程有其自身的一系列问题,有时还有很多设置,但这是它非常适合的事情。如果您的 long-运行ning 进程与主事件循环处于不同的线程中,则操作系统将负责确保每个线程获得一些 CPU 时间。您的长 运行ning 进程可以突然消失,并且主事件循环将能够同时处理事件而无需您的特殊干预。
最后的笔记:
如果您的对话是为了与用户进行单向交流,那么它看起来更像是独白而不是对话。对不起,更像是一个普通的 window 而不是对话。此外,我将确保您了解 Gtk::ProgressBar
,它“通常用于显示长时间 运行ning 操作的进度。”只是一个选择;喜欢你的形象是可以理解的。