wxPython StaticText in class not updating

Question:

I have a simple local network chat program with a chat log that is a wx.StaticText object and I’m having issues with it not updating when I set the label. I had it working previously with just self.chat_box.SetLabel(chatHistory_Display), but that was before I had to separate the elements into their own panels so I could have a scrolling panel for just the static text.

When running the program, you type a message in the bottom textctrl box, hit Enter or click the send button, then your message is supposed to appear in the black chat history box. The code runs fine with no errors, and when running the code step by step, the chatHistory and chatHistory_Display variables contains the correct value, but nothing happens when setting the label. The text box remains blank.

My code below. Apologies, I’m sure my code is a mess as I’m very new to wxPython and python in general and I’m trying to learn. The LogPanel class below contains my static text object, and the function i’m calling that sets the label is in append_chat.

import wx
import wx.lib.scrolledpanel as scrolled

chatHistory = []


class LogPanel(scrolled.ScrolledPanel):
    def __init__(self, parent):
        wx.ScrolledWindow.__init__(self, parent)
        self.SetupScrolling()

        # Chat log
        self.chat_box = wx.StaticText(self, style=wx.TE_MULTILINE | wx.BORDER_THEME | wx.VSCROLL | wx.ST_NO_AUTORESIZE)
        self.chat_box.SetBackgroundColour(wx.Colour(0, 0, 0, wx.ALPHA_OPAQUE))
        self.chat_box.SetForegroundColour(wx.Colour(255, 255, 255, wx.ALPHA_OPAQUE))

        # Sizer
        sizer_log = wx.StaticBoxSizer(wx.VERTICAL, self)
        sizer_log.Add(self.chat_box, 1, wx.EXPAND | wx.ALL, 5)
        self.SetSizer(sizer_log)

    def append_chat(self, message):
        chatHistory.append(message)
        print('Message to be added: ' + message)  # Prints message to be appended to chat history
        chatHistory_Display = ''
        for message in chatHistory:
            chatHistory_Display += message + 'n'
        self.chat_box.SetLabel(chatHistory_Display)
        print('Entire chat history: ' + str(chatHistory))  # Prints entire chat history


class SendPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # Send button
        self.my_btn = wx.Button(self, label='Send')
        self.my_btn.Bind(wx.EVT_BUTTON, self.send_message)  # Even bind

        # Message box
        self.text_ctrl = wx.TextCtrl(style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, parent=self)
        self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.key_code)

        # sizer
        box_send = wx.BoxSizer(wx.HORIZONTAL)
        box_send.Add(self.my_btn, 0, wx.ALL, 5)
        box_send.Add(self.text_ctrl, 1, wx.EXPAND | wx.ALL, 5)
        self.SetSizer(box_send)

        self.function = LogPanel(self)

    def send_message(self, event):
        msg = self.text_ctrl.GetValue()
        if msg:
            self.function.append_chat(msg)
            self.text_ctrl.Clear()

    def key_code(self, event):
        unicodeKey = event.GetUnicodeKey()
        if event.GetModifiers() == wx.MOD_SHIFT and unicodeKey == wx.WXK_RETURN:
            self.text_ctrl.WriteText('n')
            # print("Shift + Enter")
        elif unicodeKey == wx.WXK_RETURN:
            self.send_message(event)
            # print("Just Enter")
        else:
            event.Skip()
            # print("Any other character")


class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='Chatting with Bob', name='Local Instant Messenger')

        # Panels
        main_panel = wx.Panel(self)
        sub_panel_Log = LogPanel(main_panel)
        sub_panel_Send = SendPanel(main_panel)

        # Sizer
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(sub_panel_Log, 1, wx.EXPAND | wx.ALL, 5)
        main_sizer.Add(sub_panel_Send, 0, wx.EXPAND | wx.ALL, 5)
        main_panel.SetSizer(main_sizer)


if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

I’ve look everywhere online and couldn’t find any answer that worked for me. I’ve tried all kinds of combinations of Refresh() and Update() like self.Refresh(), LogPanel.Refresh(), MyFrame.Refresh(), just plain Refresh(), and others I can’t remember (Pretty much any combination/placement PyCharm would be satisfied with. I tried wx.Yield as well.

I also tried using wx.TextCtrl with wx.TE_READONLY before splitting things up into separate classes, but I didn’t like that it shows the cursor and even with wx.TE_MULTILINE the text wouldn’t create new lines.

I have no idea what I’m doing, so any guidance would be helpful. Other tips not related to this specific issue would be appreciated as well.

Thanks.

Asked By: LoganAC34

||

Answers:

The clue is that weird glyph attached to your frame.

It’s a second occurrence of the LogPanel created in SendPanel by this line self.function = LogPanel(self).

Again, it’s my bete noire, everything sitting in it’s own class.

You need to point to it not recreate it and pointing to it, is either by directly creating a variable for the purpose or tracking it down through the hierarchy.

Here’s a simple way, by getting it via the grandparent:

import wx
import wx.lib.scrolledpanel as scrolled

chatHistory = []


class LogPanel(scrolled.ScrolledPanel):
    def __init__(self, parent):
        wx.ScrolledWindow.__init__(self, parent)
        self.SetupScrolling()

        # Chat log
        self.chat_box = wx.StaticText(self, style=wx.TE_MULTILINE | wx.BORDER_THEME | wx.VSCROLL | wx.ST_NO_AUTORESIZE)
        self.chat_box.SetBackgroundColour(wx.Colour(0, 0, 0, wx.ALPHA_OPAQUE))
        self.chat_box.SetForegroundColour(wx.Colour(255, 255, 255, wx.ALPHA_OPAQUE))

        # Sizer
        sizer_log = wx.StaticBoxSizer(wx.VERTICAL, self)
        sizer_log.Add(self.chat_box, 1, wx.EXPAND | wx.ALL, 5)
        self.SetSizer(sizer_log)

    def append_chat(self, message):
        chatHistory.append(message)
        print('Message to be added: ' + message)  # Prints message to be appended to chat history
        chatHistory_Display = ''
        for message in chatHistory:
            chatHistory_Display += message + 'n'
        self.chat_box.SetLabel(chatHistory_Display)
        print('Entire chat history: ' + str(chatHistory))  # Prints entire chat history


class SendPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        gparent = parent.GetParent()

        # Send button
        self.my_btn = wx.Button(self, label='Send')
        self.my_btn.Bind(wx.EVT_BUTTON, self.send_message)  # Even bind

        # Message box
        self.text_ctrl = wx.TextCtrl(style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, parent=self)
        self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.key_code)

        # sizer
        box_send = wx.BoxSizer(wx.HORIZONTAL)
        box_send.Add(self.my_btn, 0, wx.ALL, 5)
        box_send.Add(self.text_ctrl, 1, wx.EXPAND | wx.ALL, 5)
        self.SetSizer(box_send)

        self.function = gparent.sub_panel_Log

    def send_message(self, event):
        msg = self.text_ctrl.GetValue()
        if msg:
            self.function.append_chat(msg)
            self.text_ctrl.Clear()

    def key_code(self, event):
        unicodeKey = event.GetUnicodeKey()
        if event.GetModifiers() == wx.MOD_SHIFT and unicodeKey == wx.WXK_RETURN:
            self.text_ctrl.WriteText('n')
            # print("Shift + Enter")
        elif unicodeKey == wx.WXK_RETURN:
            self.send_message(event)
            # print("Just Enter")
        else:
            event.Skip()
            # print("Any other character")


class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='Chatting with Bob', name='Local Instant Messenger')

        # Panels
        main_panel = wx.Panel(self)
        self.sub_panel_Log = LogPanel(main_panel)
        self.sub_panel_Send = SendPanel(main_panel)

        # Sizer
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(self.sub_panel_Log, 1, wx.EXPAND | wx.ALL, 5)
        main_sizer.Add(self.sub_panel_Send, 0, wx.EXPAND | wx.ALL, 5)
        main_panel.SetSizer(main_sizer)


if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

enter image description here

It’s perhaps worth noting that using a multiline, readonly wx.TextCtrl might be better than a wx.StaticText for the log.

Then, you can simply write the message, rather than reconstruct the entire statictext.

Answered By: Rolf of Saxony
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.