NotNamedDwayne
NotNamedDwayne

Reputation: 571

pyGTK running 2 "main loops" at once

I am writing a pyGTK program which (among other things) will allow the user to select and run a second program from a list. I would like to start the program that the user selects as a completely separate process with it's own main loop, but I'm not sure that this is a safe thing to do. I ran a quick test (see below), and it seemed to be safe, but I don't do a lot of GUI programming so I know there might be things I'm not thinking of. I couldn't find any tutorials addressing this, so I thought I'd ask before I get too deep in.

The two programs do not need to talk once the second one has started.

Here's the test I tried.

import gtk
import numpy as np

class TopGUI(gtk.Window):
    def __init__(self):
        super(TopGUI,self).__init__()
        parentLabel = gtk.Label('I am the father')
        newWindowBtn = gtk.Button('meet my brother')
        vBox1 = gtk.VBox(False,4)
        vBox1.pack_start(parentLabel, True, True, 4)
        vBox1.pack_start(newWindowBtn, True, True, 4)

        newWindowBtn.connect("clicked", self.on_clicked) # 0p3n Br07h3r loop on butt0n pre55

        self.add(vBox1)
        self.show_all()
        self.startItUp

    def on_clicked(self, leButton):
        broWin = SecondGUI()
        broWin.startItUp

    def startItUp(self):
        gtk.main()

class SecondGUI(gtk.Window):
    bigthing = None
    def __init__(self):
        super(SecondGUI,self).__init__()
        brotherLabel = gtk.Label('I am the 3\/1L U|\|kl3. I D3v0ur M3M0ry!')
        spinner = gtk.Spinner()
        vBox1 = gtk.VBox(True,4)
        vBox1.pack_start(brotherLabel, True, True, 4)
        vBox1.pack_start(spinner, True, True, 4)
        spinner.start()

        self.add(vBox1)
        self.show_all()
        self.bigthing = np.zeros([10000,10000])

    def startItUp(self):
        gtk.main()


fatherLoop = TopGUI()

for posterity, here is the improved version of the test code implementing abarnert's solution.

import gtk
import numpy as np
import multiprocessing

class TopGUI(gtk.Window):
    def __init__(self):
        super(TopGUI,self).__init__()
        parentLabel = gtk.Label('I am the father')
        newWindowBtn = gtk.Button('meet my brother')
        vBox1 = gtk.VBox(False,4)
        vBox1.pack_start(parentLabel, True, True, 4)
        vBox1.pack_start(newWindowBtn, True, True, 4)

        newWindowBtn.connect("clicked", self.on_clicked) # 0p3n Br07h3r loop on butt0n pre55
        self.connect("destroy", gtk.main_quit)

        self.add(vBox1)
        self.show_all()
        self.startItUp()

    def on_clicked(self, leButton):
        self.childProcess = multiprocessing.Process(target=child_main)
        self.childProcess.start()

    def startItUp(self):
        gtk.main()

class SecondGUI(gtk.Window):
    bigthing = None
    def __init__(self):
        super(SecondGUI,self).__init__()
        brotherLabel = gtk.Label('I am the 3\/1L U|\|kl3. I D3v0ur M3M0ry!')
        spinner = gtk.Spinner()
        vBox1 = gtk.VBox(True,4)
        vBox1.pack_start(brotherLabel, True, True, 4)
        vBox1.pack_start(spinner, True, True, 4)
        spinner.start()

        self.add(vBox1)
        self.show_all()
        self.bigthing = np.zeros([10000,10000])

        self.connect("destroy", gtk.main_quit)
        self.startItUp()

    def startItUp(self):
        gtk.main()

def parent_main():
    fatherLoop = TopGUI()

def child_main():
    broWin = SecondGUI()

if __name__ == '__main__':
    parent_main()

Upvotes: 0

Views: 286

Answers (1)

abarnert
abarnert

Reputation: 365657

I would like to start the program that the user selects as a completely separate process with it's own main loop, but I'm not sure that this is a safe thing to do.

Yes, that would be safe, and in fact it's probably exactly what you want. Separate processes don't share anything unless you go out of your way to (by fd inheritance, shared memory, pipes, etc.).

But your code isn't doing that, or even trying to do that. There's nothing in there to start a child process; you just create a new object in the same interpreter. So, as soon as you start your second GUI, it will end up running its own gtk.main(), making the first GUI hang until the second one quits.

But you're just referencing self.startItUp (and broWin.startItUp) everywhere instead of calling self.startItUp(). So the second GUI never starts up, and everything is "safe" in the trivial sense that doing nothing at all is always safe.

You probably want to use multiprocessing. First, you can't just do fatherLoop = TopGUI() at the top level, because then every process may end up running it. (Whether it does is implementation/platform-dependent.) Then, you need to explicitly create a Process and start it for the child. So, something like this:

class TopGUI(gtk.Window):
    # ...
    def on_clicked(self, leButton):
        self.child = multiprocessing.Process(target=child_main)
        self.child.start()
    # ...

def parent_main():
    fatherLoop = TopGUI()

def child_main():
    broWin = SecondGUI()

if __name__ == '__main__':
    parent_main()

I'm assuming you're going to fix your code so that constructing a TopGUI() or a SecondGUI() calls self.startItUp().

Upvotes: 2

Related Questions