【问题标题】:What event can I hook in to that fires as soons as my Gtk3 window *has already* closed?一旦我的 Gtk3 窗口 * 已经 * 关闭,我可以挂钩什么事件?
【发布时间】:2020-02-12 21:58:41
【问题描述】:

我希望在我的 Gtk3 窗口关闭后执行一些代码。我试过delete-eventdestroydestroy-event 似乎根本没有触发。

我知道 Gdk 和 Gtk 之间存在一些差异,但我不确定那是什么。我想我问的是正确的事情。

我正在使用gtk_window_new() 的node.js 绑定创建窗口,并使用gtk_window_close() 关闭它。这样做并使用上述事件时,事件处理程序会在窗口仍在屏幕上时触发。

编辑:

根据要求,一些示例代码:

const gi = require('node-gtk')
const Gtk = gi.require('Gtk', '3.0');

gi.startLoop();
Gtk.init();

const win = new Gtk.Window();
win.on('destroy', () => Gtk.mainQuit());
win.on('delete-event', () => false);

const button = new Gtk.Button({ label: 'Do it' });
win.add(button);
button.on('clicked', function () {win.close();});

win.showAll();
Gtk.main();

shell_command("wmctrl -lG");

function shell_command(cmd_str) {

    const execSync = require('child_process').execSync;
    code = execSync(cmd_str);
    return code.toString();

}

在我的 Linux Mint 19.3 系统上,上面的代码创建了一个带有 1 个按钮的小窗口。单击该按钮会导致执行崩溃,并出现以下终端错误:

Error: Command failed: wmctrl -lG
X Error of failed request:  BadWindow (invalid Window parameter)
  Major opcode of failed request:  20 (X_GetProperty)
  Resource id in failed request:  0x6c00003
  Serial number of failed request:  26
  Current serial number in output stream:  26

window id0x6c00003是上面代码创建的窗口的window id,在运行脚本之后,按下按钮之前运行wmctrl可以证明。

如果我只是将wmctrl 的调用放在一个 50 毫秒的计时器中,它就可以正常工作。

setTimeout(function () {shell_command("wmctrl -lG");}, 50);

这就是让我相信这是一种竞争条件的原因。我认为wmctrl 在我的窗口仍然存在时正在获取窗口 id,但是在我的窗口关闭之后 进一步查询这些 id,这会导致上述错误。这是我的理论,如果它是正确的,Gtk.main() 在窗口真正消失之前返回(并且提到的其他事件也是如此)。

【问题讨论】:

  • 一般来说,这些信号delete-event、destroy和destroy-event起作用。您能否分享一个示例代码,也许我们可以理解它为什么不起作用。
  • 并不是它们根本不工作,而是它们工作时窗口还在屏幕上。 destroy-event 似乎实际上不起作用,但这可能与在创建时启用它的需要有关,我没有这样做,也不知道该怎么做。共享代码很困难,因为这是更广泛的应用程序的一部分,而且因为我使用的是大多数人不熟悉的节点模块。不过,我会尝试编辑一些内容。
  • @SivaGuru 代码已添加。
  • 收到销毁信号后,尝试调用一个释放所有对主窗口的引用的函数(gtk_widget_destroy 或 g_object_unref 或),然后尝试调用 Gtk.mainQuit()。我尝试修改 c 代码,但它在那里工作。您将其称为响应条件的理由似乎是有效的(此问题也可能与 node-gtk 有关)。

标签: gtk gdk


【解决方案1】:
#include <gtk/gtk.h>

gboolean destroy(gpointer window, gpointer testing);

void destroy1(GtkWidget *window, gpointer testing);

int main(int argc, char *argv[]) {

  GtkWidget *window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_widget_add_events(GTK_WIDGET(window), GDK_ALL_EVENTS_MASK);

  gtk_widget_show(window);

  g_signal_connect(window, "destroy", G_CALLBACK(destroy1), NULL);
  g_signal_connect(window, "destroy-event", G_CALLBACK(destroy), NULL);

  gtk_main();

  printf("closing application \n");
  // you could try to add the code here 
  //which will be only be exited after the window is destroyed
  sleep(10);
  return 0;
}

void destroy1(GtkWidget *window, gpointer testing) {
  printf("sleeping for 5 seconds \n");
  gtk_widget_destroy(GTK_WIDGET(window));
  sleep(5);
  gtk_main_quit();
  printf("i have slept \n");
}

gboolean destroy(gpointer window, gpointer testing) {
  printf("sleeping for 5 seconds \n");
  sleep(5);
  printf("i have slept \n");

  return TRUE;
  }

窗口完全销毁后,主循环将结束。所以,你可以尝试在 gtk_main(); 之后添加你的代码;

当 gdk 窗口被销毁时,会发出 ::destroy-event 信号。您很少会收到此信号,因为大多数小部件在销毁窗口之前会断开与窗口的连接,因此在销毁时没有小部件拥有窗口。 (https://www.gnu.org/software/guile-gnome/docs/gtk/html/GtkWidget.html)。

【讨论】:

  • 我不敢相信我是个多么白痴的人。显然,只需将其放在 main() 之后即可。
  • 事实证明这并没有成功。相反,它导致了wmctrl 中的竞争条件。基本上发生的事情是Gtk.main()win.close() 之后返回,然后我的代码调用wmctrl 运行 Gtk 实际上 关闭窗口,这导致wmctrl 抛出一个错误。 Gtk.main() 不会同步结束。
  • 我不理解这个问题。你能像任何示例工作代码一样用 wmctrl 更新你的代码吗..
  • 我进一步更新了代码并扩展了原帖中的解释。从那以后,我改变了我的应用程序,使这不再重要,但未来的搜索可能会关心。
猜你喜欢
  • 2011-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多