0

I am testing on windows 7, 64-bit, MSYS2 Mingw64 shell (the shell start command is C:\msys64\msys2_shell.cmd -use-full-path -mingw64); here I have installed via pacman: mingw-w64-x86_64-python2-2.7.13-1, mingw-w64-x86_64-wxWidgets-3.0.2-17 and mingw-w64-x86_64-wxPython-3.0.2.0-6.

Consider this code, which only has a title label, button and target label; when button is clicked, the label should change from "X" to "1":

import wx #, wx.html
import sys, os
from threading import Thread

# disable buffering (Windows)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)

class Frame(wx.Frame):
  def __init__(self, *args, **kwds):
    kwds["style"] = wx.DEFAULT_FRAME_STYLE
    wx.Frame.__init__(self, *args, **kwds)

    self.label = wx.StaticText(self, wx.ID_ANY, "Click the button to change label below: ")
    self.bt_main = wx.Button(self, label="Click ME")
    self.bt_main.Bind(wx.EVT_BUTTON, self.BtnClickHandler)
    self.label2 = wx.StaticText(self, wx.ID_ANY, "XX")

    sizer_vmain_app = wx.BoxSizer(wx.VERTICAL)
    sizer_vmain_app.Add(self.label, proportion=0, flag=wx.EXPAND, border=0)
    sizer_vmain_app.Add(self.bt_main, proportion=0, flag=0, border=0)
    sizer_vmain_app.Add(self.label2, proportion=0, flag=0, border=0)

    self.SetSizer(sizer_vmain_app)
    self.Layout()

  def BtnClickHandler(self, event):
    testThread = Thread(target=self.DoBtnClick)
    testThread.start()
    testThread.join()

  def DoBtnClick(self):
    print("BtnClickHandler ")
    myval = int("1")
    self.label2.SetLabel(str(myval))

if __name__ == "__main__":
  app = wx.PySimpleApp(0)
  wx.InitAllImageHandlers()
  app_frame = Frame(None, wx.ID_ANY, "")
  app.SetTopWindow(app_frame)
  app_frame.Show()
  app.MainLoop()

When I run this code as is, then the application freezes when it comes to the self.label2.SetLabel(str(myval)).

However, if I avoid the threading, and use this function instead:

  def BtnClickHandler(self, event):
    # testThread = Thread(target=self.DoBtnClick)
    # testThread.start()
    # testThread.join()
    self.DoBtnClick()

... then everything works fine. Note that I call this script by running python test.py in the MSYS2 Mingw64 shell.

So, is it possible to run this code with threading on Windows, and if so, how? (Otherwise, running it with threading under Linux works without problems)

sdaau
  • 36,975
  • 46
  • 198
  • 278

2 Answers2

0

The problem is that wxWidgets library is not thread-safe.

What it means is that you can't access GUI elements from the secondary thread. GUI access is supported from the main thread only - the one on which you created the application object.

Igor
  • 5,620
  • 11
  • 51
  • 103
0

You can't call methods affecting the GUI from any thread other than the main one. Instead, post an event to the main thread asking it to perform the required operation on the worker thread behalf.

In C++ the simplest way to do it, by far, is to use CallAfter() with a lambda, e.g. you could just do CallAfter([=](){ label2->SetLabel(myval); }) in the thread code. Unfortunately I don't know if this is available from Python.

VZ.
  • 21,740
  • 3
  • 39
  • 42