From 787cfb858e4d228db5c171b30ae784aa4b8362fb Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 30 May 2012 03:24:34 +0000 Subject: [PATCH] Add and use the wxPyThreadBlocker class. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71603 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- etg/animate.py | 5 ++++ etg/bitmap.py | 6 ++-- etg/cmndata.py | 11 +++----- etg/dataobj.py | 3 +- etg/event.py | 8 +----- etg/image.py | 14 +++------- etg/joystick.py | 6 ++-- etg/listctrl.py | 3 +- etg/stream.py | 3 +- etg/treectrl.py | 6 ++-- etgtools/generators.py | 1 - etgtools/tweaker_tools.py | 5 +--- src/app_ex.cpp | 59 ++++++++++++++++++--------------------- src/event_ex.cpp | 3 +- src/pyevent.sip | 12 +++----- src/stream_input.cpp | 21 ++++---------- src/stream_output.cpp | 21 ++++---------- src/userdata.sip | 2 +- src/wxpy_api.h | 34 ++++++++++++++++++++-- 19 files changed, 101 insertions(+), 122 deletions(-) diff --git a/etg/animate.py b/etg/animate.py index 879af7f7..6db92387 100644 --- a/etg/animate.py +++ b/etg/animate.py @@ -39,6 +39,11 @@ def run(): tools.fixWindowClass(c) module.addGlobalStr('wxAnimationCtrlNameStr', c) + # move this before wxAnimationCtrl so it can be used for default arg values + item = module.find('wxNullAnimation') + module.items.remove(item) + module.insertItemBefore(c, item) + # TODO: It would be nice to be able to use the generic verison on all # platforms since the native GTK version has some limitations... diff --git a/etg/bitmap.py b/etg/bitmap.py index 0d3b4563..2dae4b51 100644 --- a/etg/bitmap.py +++ b/etg/bitmap.py @@ -224,12 +224,11 @@ def run(): body="""\ wxBitmap* bmp = new wxBitmap(width, height, 24); wxPyCopyBitmapFromBuffer(bmp, (byte*)data->m_ptr, data->m_len, wxBitmapBufferFormat_RGB); - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; if (PyErr_Occurred()) { delete bmp; bmp = NULL; } - wxPyEndBlockThreads(blocked); return bmp; """) @@ -267,12 +266,11 @@ def run(): body="""\ wxBitmap* bmp = new wxBitmap(width, height, 32); wxPyCopyBitmapFromBuffer(bmp, (byte*)data->m_ptr, data->m_len, wxBitmapBufferFormat_RGBA); - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; if (PyErr_Occurred()) { delete bmp; bmp = NULL; } - wxPyEndBlockThreads(blocked); return bmp; """) diff --git a/etg/cmndata.py b/etg/cmndata.py index a3484483..52963443 100644 --- a/etg/cmndata.py +++ b/etg/cmndata.py @@ -67,23 +67,20 @@ def run(): # holding the data... c.addCppMethod('PyObject*', 'GetPrivData', '()', """\ PyObject* data; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; data = PyBytes_FromStringAndSize(self->GetPrivData(), self->GetPrivDataLen()); - wxPyEndBlockThreads(blocked); return data; """) c.addCppMethod('void', 'SetPrivData', '(PyObject* data)', """\ + wxPyThreadBlocker blocker; if (! PyBytes_Check(data)) { - wxPyBLOCK_THREADS(PyErr_SetString(PyExc_TypeError, - "Expected string object")); - return /* NULL */ ; + wxPyErr_SetString(PyExc_TypeError, "Expected string object"); + return; } - wxPyBlock_t blocked = wxPyBeginBlockThreads(); self->SetPrivData(PyBytes_AS_STRING(data), PyBytes_GET_SIZE(data)); - wxPyEndBlockThreads(blocked); """) c.addAutoProperties() diff --git a/etg/dataobj.py b/etg/dataobj.py index ac112b5c..915e84fa 100644 --- a/etg/dataobj.py +++ b/etg/dataobj.py @@ -50,14 +50,13 @@ def addGetAllFormats(klass, pureVirtual=False): size_t count = self->GetFormatCount(dir); wxDataFormat* formats = new wxDataFormat[count]; self->GetAllFormats(formats, dir); - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* list = PyList_New(count); for (size_t i=0; im_callbackUserData != NULL)) { wxPyCallback *cb = (wxPyCallback*)entry->m_callbackUserData; - //wxPyBlock_t blocked = wxPyBeginBlockThreads(); - //int result = PyObject_Compare(cb->m_func, func); - //wxPyEndBlockThreads(blocked); - //if (result == 0) { if (cb->m_func == func) { delete cb; self->GetDynamicEventTable()->Erase(node); @@ -458,18 +454,16 @@ def run(): c.find('GetFiles').setCppCode("""\ int count = self->GetNumberOfFiles(); wxString* files = self->GetFiles(); - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* list = PyList_New(count); if (!list) { PyErr_SetString(PyExc_MemoryError, "Can't allocate list of files!"); - wxPyEndBlockThreads(blocked); return NULL; } for (int i=0; iGetData(); Py_ssize_t len = self->GetWidth() * self->GetHeight() * 3; PyObject* rv; - Py_buffer view; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyBuffer_FillInfo(&view, NULL, data, len, 0, PyBUF_WRITABLE|PyBUF_FORMAT|PyBUF_ND); - rv = PyMemoryView_FromBuffer(&view); - wxPyEndBlockThreads(blocked); + wxPyThreadBlocker blocker; + rv = wxPyMakeBuffer(data, len); return rv; """) @@ -245,11 +242,8 @@ def run(): byte* data = self->GetAlpha(); Py_ssize_t len = self->GetWidth() * self->GetHeight(); PyObject* rv; - Py_buffer view; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyBuffer_FillInfo(&view, NULL, data, len, 0, PyBUF_WRITABLE|PyBUF_FORMAT|PyBUF_ND); - rv = PyMemoryView_FromBuffer(&view); - wxPyEndBlockThreads(blocked); + wxPyThreadBlocker blocker; + rv = wxPyMakeBuffer(data, len); return rv; """) diff --git a/etg/joystick.py b/etg/joystick.py index 0ed87ac3..496433ce 100644 --- a/etg/joystick.py +++ b/etg/joystick.py @@ -37,10 +37,8 @@ def run(): class wxJoystick : public wxObject { public: wxJoystick(int joystick = wxJOYSTICK1) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyErr_SetString(PyExc_NotImplementedError, - "wxJoystick is not available on this platform."); - wxPyEndBlockThreads(blocked); + wxPyErr_SetString(PyExc_NotImplementedError, + "wxJoystick is not available on this platform."); } wxPoint GetPosition() const { return wxPoint(-1,-1); } int GetPosition(unsigned axis) const { return -1; } diff --git a/etg/listctrl.py b/etg/listctrl.py index 41b6b356..0008d02d 100644 --- a/etg/listctrl.py +++ b/etg/listctrl.py @@ -67,7 +67,7 @@ def run(): { int retval = 0; PyObject* func = (PyObject*)funcPtr; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* args = Py_BuildValue("(ii)", item1, item2); PyObject* result = PyEval_CallObject(func, args); @@ -77,7 +77,6 @@ def run(): Py_DECREF(result); } - wxPyEndBlockThreads(blocked); return retval; } """) diff --git a/etg/stream.py b/etg/stream.py index 26ae4c94..84aa10cf 100644 --- a/etg/stream.py +++ b/etg/stream.py @@ -111,7 +111,7 @@ def run(): static PyObject* _makeReadBufObj(wxInputStream* self, wxMemoryBuffer& buf) { PyObject* obj = NULL; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; wxStreamError err = self->GetLastError(); // error check if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF) { PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); @@ -120,7 +120,6 @@ def run(): // Return the data as a string object. TODO: Py3 obj = PyBytes_FromStringAndSize(buf, buf.GetDataLen()); } - wxPyEndBlockThreads(blocked); return obj; } """) diff --git a/etg/treectrl.py b/etg/treectrl.py index 8a3a5731..1f839762 100644 --- a/etg/treectrl.py +++ b/etg/treectrl.py @@ -74,7 +74,7 @@ def run(): doc='Returns a list of currently selected items in the tree. This function ' 'can be called only if the control has the wx.TR_MULTIPLE style.', body="""\ - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* rval = PyList_New(0); wxArrayTreeItemIds array; size_t num, x; @@ -85,7 +85,6 @@ def run(): PyList_Append(rval, item); Py_DECREF(item); } - wxPyEndBlockThreads(blocked); return rval; """) @@ -104,10 +103,9 @@ def run(): body="""\ wxRect rect; if (self->GetBoundingRect(*item, rect, textOnly)) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; wxRect* r = new wxRect(rect); PyObject* val = wxPyConstructObject((void*)r, wxT("wxRect"), true); - wxPyEndBlockThreads(blocked); return val; } else diff --git a/etgtools/generators.py b/etgtools/generators.py index 5ce3f34e..0a36d60e 100644 --- a/etgtools/generators.py +++ b/etgtools/generators.py @@ -83,7 +83,6 @@ def nci(text, numSpaces=0, stripLeading=True): # io.StringIO reads/writes unicode objects for both Python 2.7 and 3.x. For # 2.7 we'll convert any string values to unicode objects before storing them # in the StringIO - import io class Utf8EncodingStream(io.StringIO): if sys.version_info < (3,): diff --git a/etgtools/tweaker_tools.py b/etgtools/tweaker_tools.py index 5bd3272b..9ab200f6 100644 --- a/etgtools/tweaker_tools.py +++ b/etgtools/tweaker_tools.py @@ -867,7 +867,7 @@ static {{ {objType}* array; Py_ssize_t idx, len; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; // ensure that it is a sequence if (! PySequence_Check(source)) @@ -893,7 +893,6 @@ static array = new {objType}[*count]; if (!array) {{ PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); - wxPyEndBlockThreads(blocked); return NULL; }} for (idx=0; idx= 3 - int len = PyObject_Length(pyArg); - argv[x] = new argType[len+1]; - wxPyUnicode_AsWideChar(pyArg, argv[x], len+1); - #else - argv[x] = strdup(PyBytes_AsString(pyArg)); - #endif + { + wxPyThreadBlocker blocker; + PyObject* sysargv = PySys_GetObject("argv"); + if (sysargv != NULL) { + argc = PyList_Size(sysargv); + argv = new argType*[argc+1]; + int x; + for(x=0; x= 3 + int len = PyObject_Length(pyArg); + argv[x] = new argType[len+1]; + wxPyUnicode_AsWideChar(pyArg, argv[x], len+1); + #else + argv[x] = strdup(PyBytes_AsString(pyArg)); + #endif + } + argv[argc] = NULL; } - argv[argc] = NULL; } - wxPyEndBlockThreads(blocked); - // Initialize wxWidgets #ifdef __WXOSX__ @@ -232,17 +230,15 @@ void wxPyApp::_BootstrapApp() result = wxEntryStart(argc, argv); // wxApp takes ownership of the argv array, don't delete it here - blocked = wxPyBeginBlockThreads(); if (! result) { - PyErr_SetString(PyExc_SystemError, - "wxEntryStart failed, unable to initialize wxWidgets!" + wxPyErr_SetString(PyExc_SystemError, + "wxEntryStart failed, unable to initialize wxWidgets!" #ifdef __WXGTK__ - " (Is DISPLAY set properly?)" + " (Is DISPLAY set properly?)" #endif ); goto error; } - wxPyEndBlockThreads(blocked); haveInitialized = true; } else { @@ -256,12 +252,11 @@ void wxPyApp::_BootstrapApp() OnPreInit(); result = OnInit(); - blocked = wxPyBeginBlockThreads(); if (! result) { - PyErr_SetString(PyExc_SystemExit, "OnInit returned false, exiting..."); + wxPyErr_SetString(PyExc_SystemExit, "OnInit returned false, exiting..."); } error: - wxPyEndBlockThreads(blocked); + return; } diff --git a/src/event_ex.cpp b/src/event_ex.cpp index bcac6236..5103e734 100644 --- a/src/event_ex.cpp +++ b/src/event_ex.cpp @@ -40,7 +40,7 @@ void wxPyCallback::EventThunker(wxEvent& event) { PyObject* tuple; bool checkSkip = false; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; wxString className = event.GetClassInfo()->GetClassName(); arg = wxPyConstructObject((void*)&event, className); @@ -59,5 +59,4 @@ void wxPyCallback::EventThunker(wxEvent& event) { } Py_DECREF(tuple); } - wxPyEndBlockThreads(blocked); } diff --git a/src/pyevent.sip b/src/pyevent.sip index 7110f930..af606ec1 100644 --- a/src/pyevent.sip +++ b/src/pyevent.sip @@ -40,10 +40,9 @@ ~wxPyEvtDict() { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; Py_DECREF(m_dict); m_dict = NULL; - wxPyEndBlockThreads(blocked); } PyObject* _getAttrDict() @@ -55,7 +54,7 @@ PyObject* __getattr__(PyObject* name) { PyObject* value = NULL; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; if (PyDict_Contains(m_dict, name)) { value = PyDict_GetItem(m_dict, name); Py_INCREF(value); @@ -63,25 +62,22 @@ else { PyErr_SetObject(PyExc_AttributeError, name); } - wxPyEndBlockThreads(blocked); return value; } void __setattr__(PyObject* name, PyObject* value) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyDict_SetItem(m_dict, name, value); - wxPyEndBlockThreads(blocked); } void __delattr__(PyObject* name) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; if (PyDict_Contains(m_dict, name)) PyDict_DelItem(m_dict, name); else PyErr_SetObject(PyExc_AttributeError, name); - wxPyEndBlockThreads(blocked); } protected: diff --git a/src/stream_input.cpp b/src/stream_input.cpp index 824328cf..344d10aa 100644 --- a/src/stream_input.cpp +++ b/src/stream_input.cpp @@ -33,29 +33,24 @@ public: wxPyInputStream(PyObject* fileObj, bool block=true) { m_block = block; - wxPyBlock_t blocked = wxPyBlock_t_default; - if (block) blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker(m_block); m_read = wxPyGetMethod(fileObj, "read"); m_seek = wxPyGetMethod(fileObj, "seek"); m_tell = wxPyGetMethod(fileObj, "tell"); - - if (block) wxPyEndBlockThreads(blocked); } virtual ~wxPyInputStream() { - wxPyBlock_t blocked = wxPyBlock_t_default; - if (m_block) blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker(m_block); Py_XDECREF(m_read); Py_XDECREF(m_seek); Py_XDECREF(m_tell); - if (m_block) wxPyEndBlockThreads(blocked); } wxPyInputStream(const wxPyInputStream& other) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; m_read = other.m_read; m_seek = other.m_seek; m_tell = other.m_tell; @@ -63,7 +58,6 @@ public: Py_INCREF(m_read); Py_INCREF(m_seek); Py_INCREF(m_tell); - wxPyEndBlockThreads(blocked); } protected: @@ -88,7 +82,7 @@ protected: if (bufsize == 0) return 0; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* arglist = Py_BuildValue("(i)", bufsize); PyObject* result = PyEval_CallObject(m_read, arglist); Py_DECREF(arglist); @@ -106,7 +100,6 @@ protected: } else m_lasterror = wxSTREAM_READ_ERROR; - wxPyEndBlockThreads(blocked); return o; } @@ -118,7 +111,7 @@ protected: wxFileOffset OnSysSeek(wxFileOffset off, wxSeekMode mode) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* arglist = PyTuple_New(2); if (sizeof(wxFileOffset) > sizeof(long)) @@ -133,13 +126,12 @@ protected: PyObject* result = PyEval_CallObject(m_seek, arglist); Py_DECREF(arglist); Py_XDECREF(result); - wxPyEndBlockThreads(blocked); return OnSysTell(); } wxFileOffset OnSysTell() const { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* arglist = Py_BuildValue("()"); PyObject* result = PyEval_CallObject(m_tell, arglist); Py_DECREF(arglist); @@ -151,7 +143,6 @@ protected: o = wxPyInt_AsLong(result); Py_DECREF(result); }; - wxPyEndBlockThreads(blocked); return o; } diff --git a/src/stream_output.cpp b/src/stream_output.cpp index 2ba6e025..691bda59 100644 --- a/src/stream_output.cpp +++ b/src/stream_output.cpp @@ -33,29 +33,24 @@ public: wxPyOutputStream(PyObject* fileObj, bool block=true) { m_block = block; - wxPyBlock_t blocked = wxPyBlock_t_default; - if (block) blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker(m_block); m_write = wxPyGetMethod(fileObj, "write"); m_seek = wxPyGetMethod(fileObj, "seek"); m_tell = wxPyGetMethod(fileObj, "tell"); - - if (block) wxPyEndBlockThreads(blocked); } virtual ~wxPyOutputStream() { - wxPyBlock_t blocked = wxPyBlock_t_default; - if (m_block) blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker(m_block); Py_XDECREF(m_write); Py_XDECREF(m_seek); Py_XDECREF(m_tell); - if (m_block) wxPyEndBlockThreads(blocked); } wxPyOutputStream(const wxPyOutputStream& other) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; m_write = other.m_write; m_seek = other.m_seek; m_tell = other.m_tell; @@ -63,7 +58,6 @@ public: Py_INCREF(m_write); Py_INCREF(m_seek); Py_INCREF(m_tell); - wxPyEndBlockThreads(blocked); } protected: @@ -94,7 +88,7 @@ protected: if (bufsize == 0) return 0; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* arglist = PyTuple_New(1); PyTuple_SET_ITEM(arglist, 0, PyBytes_FromStringAndSize((char*)buffer, bufsize)); @@ -105,13 +99,12 @@ protected: Py_DECREF(result); else m_lasterror = wxSTREAM_WRITE_ERROR; - wxPyEndBlockThreads(blocked); return bufsize; } wxFileOffset OnSysSeek(wxFileOffset off, wxSeekMode mode) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* arglist = PyTuple_New(2); if (sizeof(wxFileOffset) > sizeof(long)) @@ -126,13 +119,12 @@ protected: PyObject* result = PyEval_CallObject(m_seek, arglist); Py_DECREF(arglist); Py_XDECREF(result); - wxPyEndBlockThreads(blocked); return OnSysTell(); } wxFileOffset OnSysTell() const { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyObject* arglist = Py_BuildValue("()"); PyObject* result = PyEval_CallObject(m_tell, arglist); Py_DECREF(arglist); @@ -144,7 +136,6 @@ protected: o = wxPyInt_AsLong(result); Py_DECREF(result); }; - wxPyEndBlockThreads(blocked); return o; } diff --git a/src/userdata.sip b/src/userdata.sip index 24079f16..7c95498b 100644 --- a/src/userdata.sip +++ b/src/userdata.sip @@ -64,7 +64,7 @@ %ConvertFromTypeCode - // Code to convert a wxClientData back to the PyObject. + // Code to convert a wxPyUserData back to the PyObject. PyObject* obj; if (sipCpp == NULL) { obj = Py_None; diff --git a/src/wxpy_api.h b/src/wxpy_api.h index b170f108..6744a30c 100644 --- a/src/wxpy_api.h +++ b/src/wxpy_api.h @@ -46,6 +46,8 @@ //-------------------------------------------------------------------------- typedef PyGILState_STATE wxPyBlock_t; +#define wxPyBlock_t_default PyGILState_UNLOCKED + typedef unsigned char byte; typedef unsigned char* buffer; @@ -74,7 +76,7 @@ inline void wxPyEndAllowThreads(PyThreadState* saved) { // A macro that will help to execute simple statments wrapped in // StartBlock/EndBlockThreads calls #define wxPyBLOCK_THREADS(stmt) \ - { wxPyBlock_t blocked = wxPyBeginBlockThreads(); stmt; wxPyEndBlockThreads(blocked); } + { wxPyThreadBlocker _blocker; stmt; } // Raise any exception with a string value (blocking threads) #define wxPyErr_SetString(err, str) \ @@ -96,6 +98,7 @@ inline void wxPyEndAllowThreads(PyThreadState* saved) { inline PyObject* wxPyMakeBuffer(void* ptr, Py_ssize_t len) { + // GIL should already be held Py_buffer view; PyBuffer_FillInfo(&view, NULL, ptr, len, 0, PyBUF_WRITABLE|PyBUF_FORMAT|PyBUF_ND); return PyMemoryView_FromBuffer(&view); @@ -123,6 +126,7 @@ inline PyObject* wxPyMakeBuffer(void* ptr, Py_ssize_t len) { inline Py_ssize_t wxPyUnicode_AsWideChar(PyObject* unicode, wchar_t* w, Py_ssize_t size) { + // GIL should already be held #if PY_MAJOR_VERSION >= 3 return PyUnicode_AsWideChar(unicode, w, size); #else @@ -162,7 +166,7 @@ inline wxPyAPI* wxPyGetAPIPtr() } //-------------------------------------------------------------------------- -// Inline wrappers to call the API functions +// Inline wrappers which call the functions in the API structure // Convert a PyObject to a wxString // Assumes that the GIL has already been acquired. @@ -184,4 +188,30 @@ inline wxPyBlock_t wxPyBeginBlockThreads() inline void wxPyEndBlockThreads(wxPyBlock_t blocked) { wxPyGetAPIPtr()->p_wxPyEndBlockThreads(blocked); } + + + + +//-------------------------------------------------------------------------- +// Convenience helper for RAII thread blocking +class wxPyThreadBlocker { +public: + explicit wxPyThreadBlocker(bool block=true) + : m_oldstate(block ? wxPyBeginBlockThreads() : wxPyBlock_t_default), + m_block(block) + { } + + ~wxPyThreadBlocker() { + if (m_block) { + wxPyEndBlockThreads(m_oldstate); + } + } + +private: + void operator=(const wxPyThreadBlocker&); + explicit wxPyThreadBlocker(const wxPyThreadBlocker&); + wxPyBlock_t m_oldstate; + bool m_block; +}; + #endif