diff --git a/docs/MigrationGuide.txt b/docs/MigrationGuide.txt index 30f73ce1..ff417bb9 100644 --- a/docs/MigrationGuide.txt +++ b/docs/MigrationGuide.txt @@ -236,3 +236,17 @@ wx.ListCtrl public. Instead use the associated getter/setter methods or the auto-generated properties that are using them. +wx.TreeCtrl +----------- + +* The GetItemData and SetItemData now behave just like GetItemPyData + and SetItemPyData did in Classic wxPython. In other words, instead + of needing to create and use instances of wx.TreeItemData to + associate Python data objects with tree items, you just use the + Python objects directly. It will also work when passing the data + objects directly to the AppendItem, InsertItem, etc. methods. (If + anybody was actually using the wx.TreeItemData objects directly + before and are unable to adapt then please let Robin know.) The + [G|S]etItemPyData members still exist, but are now deprecated + aliases for [G|S]etItemData. + diff --git a/etg/_core.py b/etg/_core.py index 64ed4be5..08bced7f 100644 --- a/etg/_core.py +++ b/etg/_core.py @@ -148,6 +148,7 @@ INCLUDES = [ # core 'toolbar', 'infobar', 'listctrl', + 'treeitemdata', 'treectrl', # toplevel and dialogs diff --git a/etg/treectrl.py b/etg/treectrl.py index 82facc1f..b111533c 100644 --- a/etg/treectrl.py +++ b/etg/treectrl.py @@ -18,13 +18,11 @@ DOCSTRING = "" # The classes and/or the basename of the Doxygen XML files to be processed by # this script. ITEMS = [ "wxTreeItemId", - ##"wxTreeItemData", + ##"wxTreeItemData", We're using a MappedType instead "wxTreeCtrl", "wxTreeEvent", ] -DEPENDS = [ 'src/treeitemdata.h' ] - #--------------------------------------------------------------------------- def run(): @@ -37,7 +35,6 @@ def run(): # customizing the generated code and docstrings. - #------------------------------------------------------- c = module.find('wxTreeItemId') assert isinstance(c, etgtools.ClassDef) @@ -49,85 +46,21 @@ def run(): module.insertItemBefore(c, td) - #------------------------------------------------------- - # Instead of using the wxTreeItemData defined in the dox file we'll - # create our own subclass that knows about dealing with PyObjects - # properly and then tweak the wxTreeCtrl methods below to define the use - # of this new class instead. - - # TODO: build this with ClassDef, etc. extractor objects instead of WigCode. - - # OR maybe just assume that nobody uses the Set/GetId and make it a %MappedType? - # Then Set/GetItemPyData can just be aliases. - tid = etgtools.WigCode("""\ - class TreeItemData { - %TypeHeaderCode - #include "treeitemdata.h" - %End - - public: - TreeItemData(PyObject* obj = NULL); - ~TreeItemData(); - - const wxTreeItemId& GetId() const; - void SetId(const wxTreeItemId& id); - - PyObject* GetData(); - void SetData(PyObject* obj); - - %Property(name=Id, get=GetId, set=SetId) - %Property(name=Data, get=GetData, set=SetData) - }; - """) - module.insertItemAfter(c, tid) - - - #------------------------------------------------------- c = module.find('wxTreeCtrl') tools.fixWindowClass(c) module.addGlobalStr('wxTreeCtrlNameStr', before=c) - # Switch all wxTreeItemData parameters to our TreeItemData class. + # Set all wxTreeItemData parameters to transfer ownership. Is this still needed with MappedTypes? for item in c.allItems(): - if hasattr(item, 'type') and item.type == 'wxTreeItemData *': - item.type = 'TreeItemData *' - if isinstance(item, etgtools.ParamDef): + if hasattr(item, 'type') and item.type == 'wxTreeItemData *' and \ + isinstance(item, etgtools.ParamDef): item.transfer = True - # Typecast the return value to our data item type - c.find('GetItemData').setCppCode( - 'return dynamic_cast(self->GetItemData(*item));') - - # The setter takes ownership of the data object - c.find('SetItemData.data').transfer = True - - # Add methods that allow direct setting/getting of the PyObject without - # requiring the programmer to use our TreeItemData class themselves. - c.addCppMethod('PyObject*', 'GetItemPyData', '(const wxTreeItemId& item)', - doc='Get the Python object associated with the tree item.', - body="""\ - TreeItemData* data = (TreeItemData*)self->GetItemData(*item); - if (data == NULL) { - RETURN_NONE(); - } - return data->GetData(); - """) - c.addCppMethod('void', 'SetItemPyData', '(const wxTreeItemId& item, PyObject* obj)', - doc='Associate a Python object with an item in the treectrl.', - body="""\ - TreeItemData* data = (TreeItemData*)self->GetItemData(*item); - if (data == NULL) { - data = new TreeItemData(obj); - self->SetItemData(*item, data); - } else { - data->SetData(obj); - } - """) c.addPyCode("""\ - TreeCtrl.GetPyData = wx.deprecated(TreeCtrl.GetItemPyData) - TreeCtrl.SetPyData = wx.deprecated(TreeCtrl.SetItemPyData) + TreeCtrl.GetItemPyData = wx.deprecated(TreeCtrl.GetItemData) + TreeCtrl.SetItemPyData = wx.deprecated(TreeCtrl.SetItemData) """) diff --git a/src/treeitemdata.h b/src/treeitemdata.h deleted file mode 100644 index 7461cb3c..00000000 --- a/src/treeitemdata.h +++ /dev/null @@ -1,48 +0,0 @@ -//-------------------------------------------------------------------------- -// Name: treeitemdata.h -// Purpose: This class is used to associate a PyObject with a tree item. -// -// Author: Robin Dunn -// -// Created: 26-Mar-2012 -// Copyright: (c) 2012 by Total Control Software -// Licence: wxWindows license -//-------------------------------------------------------------------------- - -#ifndef TREEITEMDATA_H -#define TREEITEMDATA_H - -#include - -// A wxTreeItemData that knows what to do with PyObjects for maintianing the refcount -class TreeItemData : public wxTreeItemData { -public: - TreeItemData(PyObject* obj = NULL) { - if (obj == NULL) - obj = Py_None; - wxPyBLOCK_THREADS( Py_INCREF(obj) ); - m_obj = obj; - } - - ~TreeItemData() { - wxPyBLOCK_THREADS( Py_DECREF(m_obj) ); - } - - PyObject* GetData() { - wxPyBLOCK_THREADS( Py_INCREF(m_obj) ); - return m_obj; - } - - void SetData(PyObject* obj) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - Py_DECREF(m_obj); - m_obj = obj; - Py_INCREF(obj); - wxPyEndBlockThreads(blocked); - } - -private: - PyObject* m_obj; -}; - -#endif diff --git a/src/treeitemdata.sip b/src/treeitemdata.sip new file mode 100644 index 00000000..b31fed8c --- /dev/null +++ b/src/treeitemdata.sip @@ -0,0 +1,64 @@ +//-------------------------------------------------------------------------- +// Name: treeitemdata.sip +// Purpose: A MappedType for wxTreeItemData that handles containing +// arbitrary PyObjects, dealing with their refcounts, etc. +// +// Author: Robin Dunn +// +// Created: 28-Mar-2012 +// Copyright: (c) 2012 by Total Control Software +// Licence: wxWindows license +//-------------------------------------------------------------------------- + + +%ModuleHeaderCode +#include + +// A wxTreeItemData that knows what to do with PyObjects for maintianing the refcount +class wxPyTreeItemData : public wxTreeItemData { +public: + wxPyTreeItemData(PyObject* obj = NULL) { + if (obj == NULL) + obj = Py_None; + wxPyBLOCK_THREADS( Py_INCREF(obj) ); + m_obj = obj; + } + + ~wxPyTreeItemData() { + wxPyBLOCK_THREADS( Py_DECREF(m_obj) ); + } + + PyObject* m_obj; +}; +%End + + +%MappedType wxTreeItemData +{ + %ConvertToTypeCode + // Code to test a PyObject for compatibility + if (!sipIsErr) { + return TRUE; // any python object is valid + } + + // Code to create a new wxClientData from the PyObject + wxPyTreeItemData* data = new wxPyTreeItemData(sipPy); + *sipCppPtr = data; + return sipGetState(sipTransferObj); + %End + + + %ConvertFromTypeCode + // Code to convert a wxPyTreeItemData back to the PyObject. + PyObject* obj; + if (sipCpp == NULL) { + obj = Py_None; + } else { + wxPyTreeItemData* data = static_cast(sipCpp); + obj = data->m_obj; + } + Py_INCREF(obj); + return obj; + %End + +}; diff --git a/unittests/test_treectrl.py b/unittests/test_treectrl.py index e3e18f34..c102246d 100644 --- a/unittests/test_treectrl.py +++ b/unittests/test_treectrl.py @@ -17,40 +17,61 @@ class treectrl_Tests(wtc.WidgetTestCase): def test_treectrlTreeItemId(self): tree = wx.TreeCtrl(self.frame) root = tree.AddRoot('root item') - self.assertTrue( isinstance(root, wx.TreeItemId) ) - self.assertTrue( root.IsOk() ) + self.assertTrue(isinstance(root, wx.TreeItemId)) + self.assertTrue(root.IsOk()) r = tree.GetRootItem() - self.assertTrue( r is not root ) - self.assertTrue( r == root ) + self.assertTrue(r is not root) + self.assertTrue(r == root) child = tree.AppendItem(root, 'child item') - self.assertTrue( child is not root ) - self.assertTrue( child != root ) + self.assertTrue(child is not root) + self.assertTrue(child != root) def test_treectrlTreeItemData(self): value = 'Some Python Object' tree = wx.TreeCtrl(self.frame) - data = wx.TreeItemData(value) - root = tree.AddRoot('root item', data=data) - d = tree.GetItemData(root) - self.assertTrue(d.GetData() == value) - self.assertTrue(d.Data == value) - self.assertTrue(d.Id == root) + root = tree.AddRoot('root item', data=value) + v = tree.GetItemData(root) + self.assertTrue(v == value) tree.SetItemData(root, None) self.assertTrue(tree.GetItemData(root) is None) def test_treectrlTreeItemPyData(self): + # ensure that the "Py" versions raise deprecation warnings value = 'Some Python Object' tree = wx.TreeCtrl(self.frame) root = tree.AddRoot('root item') - tree.SetItemPyData(root, value) - self.assertTrue(tree.GetItemPyData(root) == value) - self.assertTrue(tree.GetItemData(root).GetData() == value) - tree.SetItemPyData(root, None) - self.assertTrue(tree.GetItemPyData(root) is None) + tree.SetItemData(root, value) + + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("error") + with self.assertRaises(wx.wxPyDeprecationWarning): + tree.SetItemPyData(root, value) + + with warnings.catch_warnings(): + warnings.simplefilter("error") + with self.assertRaises(wx.wxPyDeprecationWarning): + tree.GetItemPyData(root) + + + def test_treectrlGetSelections(self): + tree = wx.TreeCtrl(self.frame, style=wx.TR_MULTIPLE) + root = tree.AddRoot('root item') + c1 = tree.AppendItem(root, 'c1') + c2 = tree.AppendItem(root, 'c2') + tree.SelectItem(c1) + tree.SelectItem(c2) + self.assertTrue(tree.IsSelected(c1)) + self.assertTrue(tree.IsSelected(c2)) + + sel = tree.GetSelections() + self.assertTrue(isinstance(sel, list)) + self.assertTrue(len(sel) == 2) + self.assertTrue(isinstance(sel[0], wx.TreeItemId)) @@ -90,6 +111,7 @@ class treectrl_Tests(wtc.WidgetTestCase): wx.TREE_HITTEST_ONITEMLOWERPART wx.TREE_HITTEST_ONITEM + def test_treeEventsExist(self): wx.wxEVT_COMMAND_TREE_BEGIN_DRAG wx.wxEVT_COMMAND_TREE_BEGIN_RDRAG