Catch Close-Event from child-window created with io.popen


#1

Hi Guys,

i create a process in a child-windows with io.popen.

If users closes this window, for example via click on “X” i need to
react in the parent window.

How can i catch the evt_close of this child-process?


#2

Hi Marvin,
thx for ur long answer, but this is not exactly what i wanted, because
my childprocess is not really a second ruby process. In my case i am
using Gnuplot in the childwindow and coontrol it via buttons in the
parent window.
So here is my example:

At first i create my parent window with some buttons and the following
code to invoke the child process:


def on_buttonclick
@gnuplot = Plot.new(filelist)
end

def on_anotherbuttonclick
@gnuplot.do_something
end

Here is what my Plot-Class does:

class Plot
def initialize(filelist)
@gp=IO.popen(“gnuplot”,“w+”)
@gp.puts(“plot #{filelist}”)
end

def do_something
@gp.puts(“do something”)
end

end

Situation after Click on first button:

  • Parent window is open with two button
  • Gnuplot-Window has been openend as a child of parentwindow with some
    plot
  • both are idle
  • After Click on second button the childprocess is forced to do
    something, this is what the parent process should do.

Now my problem:
If the user closes the Childwindow (For example Alt-F4, or click on “X”
the Window closes, but the object @gnuplot still exists in memory.

If this happens i want to react in ParentProcess for example with
@gnuplot.close or disable some buttons which have no function any longer
when Childwindows doesnt exist anymore.

evt_close in my Plot-Class does not seem to work, because this event is
not thrown in case of clicking on “X” or Alr-F4.

So the question is: How to catch those (User-)Events?

If i could catch these events i could use your solution with SIGTERM


#3

Am Thu, 13 Sep 2012 16:46:35 +0200
schrieb “Daniel S.” removed_email_address@domain.invalid:

Hi Marvin,
thx for ur long answer, but this is not exactly what i wanted,
because my childprocess is not really a second ruby process. In my
case i am using Gnuplot in the childwindow and coontrol it via
buttons in the parent window.

You cannot listen to events to other processes. So if GnuPlot doesn’t
have a possibility to tell you when its main window is closed, you
can’t check this without relying on dirty hacks such as making xdotool
check the visibility of the window regularily by means of a Wx::Timer.

Situation after Click on first button:

  • Parent window is open with two button
  • Gnuplot-Window has been openend as a child of parentwindow with
    some plot
  • both are idle
  • After Click on second button the childprocess is forced to do
    something, this is what the parent process should do.

First for clarity: This way the GnuPlot window is not a child window
of your application. It’s the main window of a completely autonome and
separate process. The GnuPlot process is a child process of your Ruby
process, but the window belongs to the child process, not to your
process.

I don’t know GnuPlot, but when its main window exits (as a result to
[Alt]+[F4] or clicking on the close button) I suspect the process
running the window dies, doesn’t it? If you’re developing for a *nix
platform there’s a special process signal, SIGCHLD, which is fired on
your process when a child process dies. Any *nix application
(including Ruby applications) can register a handler for this signal,
so you can for example do (IO.popen creates child processes, and #spawn
does so as well):

=====================================
spawn(“sleep 5”)

Signal.trap(“SIGCHLD”) do
puts “This was it.”
puts “But for $500.000 I will send you the body.”
exit
end

puts “If you don’t pay $1.000.000 I will kill this child.”

loop do
puts “I’m waiting…”
sleep 0.5
end

You should be able to combine this with your usecase[1]. I however am
not sure how wxRuby reacts if you try to modify wx stuff from this
asynchronously run code; I often saw segfaults or arcane error messages
when I even tried this with threads, so I cannot predict what happens
from within signal handlers. What you probably can do safely is
registering a callback to run as soon as possible (probably utilising
Timer.after with a very short time interval) which will then be run
from within the wx eventloop and therefore be safe.

So the question is: How to catch those (User-)Events?

As said: There’s no way to listen to events for another process (apart
from using GDB, I suppose).

If i could catch these events i could use your solution with SIGTERM

Vale,
Marvin

[1] I mean the concept employed by the code, not the way this
hypothetic kidnapper acts, of course. Btw. from the code you can see he
never had the intention to leave the child alive after receiving the
money.


Blog: http://pegasus-alpha.eu/blog

ASCII-Ribbon-Kampagne () | ASCII Ribbon Campaign ()


#4

Am Thu, 13 Sep 2012 08:59:47 +0200
schrieb “Daniel S.” removed_email_address@domain.invalid:

Hi Guys,

i create a process in a child-windows with io.popen.

If users closes this window, for example via click on “X” i need to
react in the parent window.

How can i catch the evt_close of this child-process?

If I get you right, you run a Ruby application via IO.popen and want
your parent process to do something? Something like this:

=================================
IO.popen(“ruby myapp.rb”){…stuff…}

? So you have three possibilities now:

  1. EVT_CLOSE usually means your child application is going to exit. So
    just use IO.popen with the block form, which will wait until your child
    process has finished.

  2. If you can’t use the block form for some reason, use Process.wait
    which will wait for all child processes to exit.

  3. Assuming you #veto the close event, but want your parent process to
    take action nevertheless, you can use process signals to achieve what
    you want. In the parent process, register a signal handler for your
    “reaction”:

=================================
Signal.trap(“SIGUSR1”) do
puts “Hi, I’ve been notified!”

or do whatever you want

end

In the child process, register an event handler for the EVT_CLOSE event
that notifies your parent process (Process.ppid returns the PID of
your process’ parent process):

=================================
evt_close do |event|
Process.kill(“SIGUSR1”, Process.ppid)
event.veto # Do not close the window
end

Or if you meant it the other way round, having your child process to
react when the parent process gets EVT_CLOSE, you could catch the
EVT_CLOSE in the parent process and send a signal to the child process.
Example (using the open4 gem):

=================================
require “open4”
require “wx”

class MainFrame < Wx::Frame
include Wx

def initialize(parent = nil)
super(parent, title: “Test”, size: [400, 400])

StaticText.new(self, label: "Test app", pos: [20, 20])
@button = Button.new(self, label: "Click me", pos: [20, 50])

evt_button(@button, :on_button_click)
evt_close(:on_close)

end

private

def on_button_click(event)
# Note you cannot use the block form
@childpid, @childstdin, @childstdout, @childstderr =
Open4.popen4(“sleep”)
end

def on_close(event)
Process.kill(“SIGTERM”, @childpid)
event.skip # Otherwise closing is prohibited
end

end

class MyApp < Wx::App

def on_init
@mainwindow = MainFrame.new
@mainwindow.show
end
end

This application will send SIGTERM to the “sleep” child process when
EVT_CLOSE is received.

Vale,
Marvin


Blog: http://pegasus-alpha.eu/blog

ASCII-Ribbon-Kampagne () | ASCII Ribbon Campaign ()


#5

First for clarity: This way the GnuPlot window is not a child window
of your application. It’s the main window of a completely autonome and
separate process. The GnuPlot process is a child process of your Ruby
process, but the window belongs to the child process, not to your
process.

okay, this was my problem…

I don’t know GnuPlot, but when its main window exits (as a result to
[Alt]+[F4] or clicking on the close button) I suspect the process
running the window dies, doesn’t it?

No, it doesn’t. I’ll check some gnuplot forums to realize this behavior.
After this is can use SIGCHLD, i think

As an alternative i’ll prevent the gnuplot window to be closed by user
(don’t know yet how to do so) and implement a “close window”-button in
my parent which kills the whole gnuplot-process…

Thx for your help!