pythonbeeware

App to store and view multiple instances of itself


My Toga-based app has the user enter information in a series of boxes. In the end, the information is used to start a number of processes governed by a scheduler. Once the processes are started, progress should be displayed to the user through continuous updates of a status box.

The user is to run several instances of the app at the same time. The user should be able to make a new instance of the app and view the status box of all the previous instances that have ongoing processes.

The current approach (with multiprocessing added for operational stability) essentially looks as follows:

import toga
import multiprocessing
from toga.style import Pack


class Test(toga.App):
    
    instances = []
    
    def startup(self):

        self.main_box = toga.Box(style=Pack(direction='column'))        

        self.main_window = toga.MainWindow(title=self.formal_name)
        
        self.main_window.content = self.main_box
        
        self.main_window.show()
        
        self.work()
        
    
    def work(self):
        
        add_but = toga.Button('Add instance to list', on_press=self.add_but_hdl)
        
        ins_num = len(Test.instances) + 1
        
        ins_lbl = toga.Label('Current instance number is {}'.format(ins_num))
        
        if Test.instances:
            prv_ins_idx = len(Test.instances) - 1
            self.main_box.add(Test.instances[prv_ins_idx].main_window.show(),
                              ins_lbl, 
                              add_but)
        else:
            self.main_box.add(ins_lbl, add_but)
            
            
    def add_but_hdl(self, widget):
        
        Test.instances.append(self)
        
        self.init_proc()
        
        
    def init_proc(self):
        
        proc = multiprocessing.Process(target=self.mk_app_ins)
        
        proc.start()
        
        
    def mk_app_ins(self):
        
        new_ins = Test()
        
        new_ins.main_loop()


def main():
        
    return Test()

When running the above through briefcase dev and pressing Add instance to list, the above leads to the error:

[tender] Starting in dev mode... =========================================================================== Failed to register: An object is already exported for the interface org.gtk.Application at ...

Can the setup be made to work, or should it be disregarded in favor of an entirely different approach? What could a viable solution look like?


Solution

  • As it says in the multiprocessing documentation, the default process start method on Linux is fork, which means "the child process, when it begins, is effectively identical to the parent process". So the child process will believe it already has a GUI running.

    Instead, you should probably use the spawn method, which will give the child process a fresh start. You can do this by calling multiprocessing.set_start_method.

    Or rather than dealing with the complexity of multiple processes, it might be easier to use just one process with multiple windows. Toga supports this on Linux, Windows and macOS.

    On Android or iOS, Toga currently only supports a single window, but you can modify the UI of that window as much as you like to allow the user to see different things. You can do this by adding or removing child widgets, or even reassigning the window's content attribute.