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.
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()
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.
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.
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()
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.