Search code examples
pythontkintertoplevel

Multiple Toplevel windows using "after" for updates


I am developing a Python Tkinter app which can open multiple instances of a toplevel window.

The following extract tests if an instance of the toplevel class is running and if not, runs a new instance. If it does exist, it brings it to the foreground.

        if (ConsoleGui.winlog[pcs] is not None):
            try:
                ConsoleGui.winlog[pcs].lift()
            except:
                ConsoleGui.winlog[pcs] = ClassEpsL.CreateEspLog(Console, pcs)
        else:
            ConsoleGui.winlog[pcs] = ClassEpsL.CreateEspLog(Console, pcs)

This works nicley.

My problem is the each instance of the toplevel window, has a function which runs in an "after" loop to update a text widget eg

def CreateEspLog(self, pcs):
        espLWin = Toplevel(self)
        ...
        text = Text(espLWin, wrap="none", width=600, height=400)
        ...
        def refreshEspL(self):
            print("pcsNode: {}".format(ClassEpsL.pcsNode))
            if MQTT.BrokerConnected:
                ClassEpsL.espLogUpdate(self)
            espLWin.after(200, refreshEspL, self)

        espLWin.after(200, refreshEspL, self)
        return espLWin

The latest instance of the toplevel window takes over the running of this loop from the older ones which no longer update.

I haven't been able to find a solution to either:

create instances which are somehow uniquely identified for the line:

espLWin.after(200, refreshEspL, self)

Or can I somehow call my toplevel window function refreshEspL from my root window eg

ConsoleGui.winlog[pcs].refreshEspL(self)

Solution

  • I managed to solve this with help from: How do I create a class to make multiple toplevels of one main window in tkinter?

    I am new to Python and Tkinter, but this might help someone else new.

    By creating my instance as a class with variable names like this:

    class ClassEspL(Toplevel):
        def __init__(self, top_level, textWW, pcsNode):
            super().__init__()
            self.top_level = top_level
            self.title("ESP Logging WIN: {} - {}".format(pcsNode, Site.PCSList[pcsNode].eui64))
    
            self.textW = textWW
            textW = Text(self, wrap="none", width=70 , height=20, bg="black")
            textW.pack()
        self.updateF(pcsNode, textW)
        
        def updateF(self, pcsNode, textW):
            print("pcsNode: {}".format(pcsNode))
            textW.insert(END, line, tag)
            textW.see(END)
            self.after(1000, self.updateF, pcsNode, textW)
    

    and calling it from my root window like this:

    def callback_ESPLogger(self, pcs):
        def createShowLoggerWindow(self, pcs):
            if (ConsoleGui.winlog[pcs] is not None):
                try:
                    ConsoleGui.winlog[pcs].lift()
                except:
                    ConsoleGui.winlog[pcs] = ClassEspL("top_level{}".format(pcs), "textW{}".format(pcs), pcs)
            else:
                ConsoleGui.winlog[pcs] = ClassEspL("top_level{}".format(pcs), "textW{}".format(pcs), pcs)
                
        if pcs == 99: # create multiple instance
            for pcs in range(0, Site.Config.numberofpcs):
                createShowLoggerWindow(self, pcs)
        else:
            createShowLoggerWindow(self, pcs) # create 1 instance
    

    ... each instance of my ClassEspL has a unique name and handle and my updateF functions don't take over each other.