Search code examples
pythontkintermessagebox

Python Message box not updating when class method is requested from a global instance request. What am I missing here?


import tkinter as TK
from tkinter import *
from tkinter import ttk

############################################
root = TK.Tk()
root.geometry("1000x700+1+5")
WindowTitle = 'Test MessageBox'
Revision = 'Support 1.0';

gServerData = 'Let there be Rock'


# COMMAND/Communication Message Window Test Button #############
def ButtonTest():
    global gServerData
    #gServerData = '128,TestingMojo'
    print('* ButtonTest Function: Ready to Print in MessageBox',gServerData)
    Api.PrintMessage()

# TABS Notebook page Setup 
class API_Tabs(TK.Frame):

    def __init__(self,*args,**kwargs):
        Frame.__init__(self,*args,**kwargs)
        self.notebook = ttk.Notebook()
        self.Tabs()
        self.notebook.place(x=5, y = 1, height=690, width =990)
        self.master.title(WindowTitle)
        #self.MessageHistoryText = MessageHistoryText

    #Setup ttk Notebook style 
    def Tabs(self):
        page1 = Api_Select(self.notebook)
        page2 = Api_Operations(self.notebook) 

        self.notebook.add(page1,text="Main")
        self.notebook.add(page2,text="Operation")   

############################################    
class Api_Select(Frame):
    def __init__(self,name,*args,**kwargs):
        Frame.__init__(self,*args,**kwargs)
        self.name = name


class Api_Operations(Frame):
    def __init__(self,name,*args,**kwargs):
        Frame.__init__(self,*args,**kwargs)
        self.name = name
        self.createWidgets()
        self.PrintMessage()

    def PrintMessage(self):
        global gServerData
        print('* Print Message Function: Message Ready',gServerData)
        self.MessageHistoryText.delete('1.0',END)
        self.MessageHistoryText.insert(TK.END, 'GlobalServer:' + gServerData + '\n', 'notice')
        self.MessageHistoryText.see(TK.END)
        self.MessageHistoryText.update()
        root.update()
        print('* Print Message Function: MessageBox Update',gServerData)

    def createWidgets(self):
        self.mainFrame = Frame(self) #TK.Frame(self)
        self.mainFrame.place(x=0, y = 0, height=650, width = 1000)

        self.ButtonTestPB = TK.Button(self.mainFrame, text='TEST', command= ButtonTest)
        self.ButtonTestPB.place(x=50, y = 100, height=50, width = 50)

        # COMMAND/Communication Entry Window and Send Button #############
        self.MessageFrame = TK.Frame(self.mainFrame)
        self.MessageFrame.place(x=240, y = 50, height=370, width = 360)
        self.MessageCmdLabel = TK.Label(self.MessageFrame, text= 'Command/Communications',anchor="w" )
        self.MessageCmdLabel.place(x=0, y = 0, height=25, width = 200)
        self.MessageHistoryText = TK.Text(self.MessageFrame, height=20, width=40, relief=TK.SUNKEN)
        self.MessageHistoryScroll = TK.Scrollbar(self.MessageFrame)     
        self.MessageHistoryText.config(yscrollcommand=self.MessageHistoryScroll.set)
        self.MessageHistoryScroll.config(command=self.MessageHistoryText.yview)
        self.MessageHistoryText.place(x=0, y = 25, height=300, width = 350)
        self.MessageHistoryScroll.place(x=350, y = 25, height=300, width = 15)      
        self.MessageHistoryText.tag_configure('warning', foreground='red', background='gainsboro')
        self.MessageHistoryText.tag_configure('notice', foreground='blue', background='gainsboro')  

App = API_Tabs(root)
Api = Api_Operations(App)
#_thread.start_new_thread(Receive, ("Receive_Thread",))
App.mainloop()
root.destroy()  

''' Using the Button on the ops page should give me the same result as the statup text in the message box. This is a small example of a very large GUI. I have a thread running that calls a global function when data is received. This all works. What I need to do is have the received data print to my operations page message box without requesting it. The test button is to mimic the request.

But it is not working. Any Thoughts? '''


Solution

  • I run your code, click the button and see the print()s, so I don't know what is wrong. Your question says "But it is not working" but doesn't describe exactly how it is failing. The example provided seems to work fine and doesn't reproduce the problem?

    Avoid using threads though - you don't need a thread to receive something from network, you can use tkinter's low-level createfilehandler to create an event that will be called when something is available to read in a socket, without freezing the gui and without threads. Or if you prefer higher level (recommended) You can use an asynchronous framework communication protocols!

    EDIT: From the comments I can finally see what your problem is: You have two instances of the Api_Operations class.

    One of them is being instanced and stored as the global variable named Api:

    Api = Api_Operations(App)
    

    The other is created inside the API_Tabs class, in the Tabs() method, and stored as a local variable named page2:

    page2 = Api_Operations(self.notebook) 
    

    Since you are displaying page2 but updating Api, the updates never show because the instance you're updating is not the one shown in the screen.

    To fix it, store the instance you create in API_Tabs.Tabs() as an attribute:

    def Tabs(self):
        page1 = Api_Select(self.notebook)
        self.Api = Api_Operations(self.notebook) 
    
        self.notebook.add(page1,text="Main")
        self.notebook.add(self.Api, text="Operation")
    

    Then in your ButtonTest() function you can access it using the API_Tabs instance stored in the global name App:

    App.Api.PrintMessage()
    

    You can remove your Api = Api_Operations(App) line.