Add treectrl

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71046 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2012-03-29 16:59:21 +00:00
parent 63796d6f95
commit 95c5c5f50e
6 changed files with 431 additions and 2 deletions

View File

@@ -145,10 +145,9 @@ other dev stuff
* all missing common dialogs
* print (as in print framework classes)
* mdi (die mdi! die!)
* treectrl
* dirctrl
* cshelp
* dragimag
* dragimag
* datectrl
* hyperlink
* pickerbase, clrpicker, filepicker, fontpicker

View File

@@ -148,6 +148,7 @@ INCLUDES = [ # core
'toolbar',
'infobar',
'listctrl',
'treectrl',
# toplevel and dialogs
'nonownedwnd',

237
etg/treectrl.py Normal file
View File

@@ -0,0 +1,237 @@
#---------------------------------------------------------------------------
# Name: etg/treectrl.py
# Author: Robin Dunn
#
# Created: 26-Mar-2012
# Copyright: (c) 2012 by Total Control Software
# License: wxWindows License
#---------------------------------------------------------------------------
import etgtools
import etgtools.tweaker_tools as tools
PACKAGE = "wx"
MODULE = "_core"
NAME = "treectrl" # Base name of the file to generate to for this script
DOCSTRING = ""
# The classes and/or the basename of the Doxygen XML files to be processed by
# this script.
ITEMS = [ "wxTreeItemId",
##"wxTreeItemData",
"wxTreeCtrl",
"wxTreeEvent",
]
DEPENDS = [ 'src/treeitemdata.h' ]
#---------------------------------------------------------------------------
def run():
# Parse the XML file(s) building a collection of Extractor objects
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
etgtools.parseDoxyXML(module, ITEMS)
#-----------------------------------------------------------------
# Tweak the parsed meta objects in the module object as needed for
# customizing the generated code and docstrings.
#-------------------------------------------------------
c = module.find('wxTreeItemId')
assert isinstance(c, etgtools.ClassDef)
c.addCppMethod('int', '__nonzero__', '()', """\
return self->IsOk();
""")
td = etgtools.TypedefDef(name='wxTreeItemIdValue', type='void*')
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.
for item in c.allItems():
if hasattr(item, 'type') and item.type == 'wxTreeItemData *':
item.type = 'TreeItemData *'
if isinstance(item, etgtools.ParamDef):
item.transfer = True
# Typecast the return value to our data item type
c.find('GetItemData').setCppCode(
'return dynamic_cast<TreeItemData*>(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)
""")
# We can't use wxClassInfo
c.find('EditLabel.textCtrlClass').ignore()
# Replace GetSelections with a method that returns a Python list
# size_t GetSelections(wxArrayTreeItemIds& selection) const;
c.find('GetSelections').ignore()
c.addCppMethod('PyObject*', 'GetSelections', '()',
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();
PyObject* rval = PyList_New(0);
wxArrayTreeItemIds array;
size_t num, x;
num = self->GetSelections(array);
for (x=0; x < num; x++) {
wxTreeItemId *tii = new wxTreeItemId(array.Item(x));
PyObject* item = wxPyConstructObject((void*)tii, wxT("wxTreeItemId"), true);
PyList_Append(rval, item);
Py_DECREF(item);
}
wxPyEndBlockThreads(blocked);
return rval;
""")
# Change GetBoundingRect to return the rectangle instead of modifying the parameter.
#bool GetBoundingRect(const wxTreeItemId& item, wxRect& rect, bool textOnly = false) const;
c.find('GetBoundingRect').ignore()
c.addCppMethod('PyObject*', 'GetBoundingRect', '(const wxTreeItemId& item, bool textOnly=false)',
doc="""\
Returns the rectangle bounding the item. If textOnly is true,
only the rectangle around the item's label will be returned, otherwise
the item's image is also taken into account. The return value may be None
if the rectangle was not successfully retrieved, such as if the item is
currently not visible.
""",
isFactory=True,
body="""\
wxRect rect;
if (self->GetBoundingRect(*item, rect, textOnly)) {
wxPyBlock_t blocked = wxPyBeginBlockThreads();
wxRect* r = new wxRect(rect);
PyObject* val = wxPyConstructObject((void*)r, wxT("wxRect"), true);
wxPyEndBlockThreads(blocked);
return val;
}
else
RETURN_NONE();
""")
# switch the virtualness back on for those methods that need to have it.
c.find('OnCompareItems').isVirtual = True
# transfer imagelist ownership
c.find('AssignImageList.imageList').transfer = True
c.find('AssignStateImageList.imageList').transfer = True
c.find('AssignButtonsImageList.imageList').transfer = True
# Make the cookie values be returned, instead of setting it through the parameter
c.find('GetFirstChild.cookie').out = True
c.find('GetFirstChild.cookie').inOut = True
#-------------------------------------------------------
c = module.find('wxTreeEvent')
tools.fixEventClass(c)
c.addPyCode("""\
EVT_TREE_BEGIN_DRAG = PyEventBinder(wxEVT_COMMAND_TREE_BEGIN_DRAG , 1)
EVT_TREE_BEGIN_RDRAG = PyEventBinder(wxEVT_COMMAND_TREE_BEGIN_RDRAG , 1)
EVT_TREE_BEGIN_LABEL_EDIT = PyEventBinder(wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT , 1)
EVT_TREE_END_LABEL_EDIT = PyEventBinder(wxEVT_COMMAND_TREE_END_LABEL_EDIT , 1)
EVT_TREE_DELETE_ITEM = PyEventBinder(wxEVT_COMMAND_TREE_DELETE_ITEM , 1)
EVT_TREE_GET_INFO = PyEventBinder(wxEVT_COMMAND_TREE_GET_INFO , 1)
EVT_TREE_SET_INFO = PyEventBinder(wxEVT_COMMAND_TREE_SET_INFO , 1)
EVT_TREE_ITEM_EXPANDED = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_EXPANDED , 1)
EVT_TREE_ITEM_EXPANDING = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_EXPANDING , 1)
EVT_TREE_ITEM_COLLAPSED = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_COLLAPSED , 1)
EVT_TREE_ITEM_COLLAPSING = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_COLLAPSING , 1)
EVT_TREE_SEL_CHANGED = PyEventBinder(wxEVT_COMMAND_TREE_SEL_CHANGED , 1)
EVT_TREE_SEL_CHANGING = PyEventBinder(wxEVT_COMMAND_TREE_SEL_CHANGING , 1)
EVT_TREE_KEY_DOWN = PyEventBinder(wxEVT_COMMAND_TREE_KEY_DOWN , 1)
EVT_TREE_ITEM_ACTIVATED = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_ACTIVATED , 1)
EVT_TREE_ITEM_RIGHT_CLICK = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK , 1)
EVT_TREE_ITEM_MIDDLE_CLICK = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 1)
EVT_TREE_END_DRAG = PyEventBinder(wxEVT_COMMAND_TREE_END_DRAG , 1)
EVT_TREE_STATE_IMAGE_CLICK = PyEventBinder(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 1)
EVT_TREE_ITEM_GETTOOLTIP = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 1)
EVT_TREE_ITEM_MENU = PyEventBinder(wxEVT_COMMAND_TREE_ITEM_MENU, 1)
""")
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)
#---------------------------------------------------------------------------
if __name__ == '__main__':
run()

48
src/treeitemdata.h Normal file
View File

@@ -0,0 +1,48 @@
//--------------------------------------------------------------------------
// 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 <wx/treebase.h>
// 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

View File

@@ -88,6 +88,9 @@ inline void wxPyEndAllowThreads(PyThreadState* saved) {
wxPyBLOCK_THREADS( PyErr_SetString(PyExc_NotImplementedError, msg) );
// A convenience macro for properly returning Py_None
//#define RETURN_NONE() { Py_INCREF(Py_None); return Py_None; }
#define RETURN_NONE() { wxPyBLOCK_THREADS(Py_INCREF(Py_None)); return Py_None; }
//--------------------------------------------------------------------------
// The API items whose implementation can not or should not be inline

141
unittests/test_treectrl.py Normal file
View File

@@ -0,0 +1,141 @@
import imp_unittest, unittest
import wtc
import wx
#---------------------------------------------------------------------------
class treectrl_Tests(wtc.WidgetTestCase):
def test_treectrlCtor(self):
t = wx.TreeCtrl(self.frame)
def test_treectrlDefaultCtor(self):
t = wx.TreeCtrl()
t.Create(self.frame)
def test_treectrlTreeItemId(self):
tree = wx.TreeCtrl(self.frame)
root = tree.AddRoot('root item')
self.assertTrue( isinstance(root, wx.TreeItemId) )
self.assertTrue( root.IsOk() )
r = tree.GetRootItem()
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 )
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)
tree.SetItemData(root, None)
self.assertTrue(tree.GetItemData(root) is None)
def test_treectrlTreeItemPyData(self):
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)
def test_treectrlConstantsExist(self):
wx.TR_NO_BUTTONS
wx.TR_HAS_BUTTONS
wx.TR_NO_LINES
wx.TR_LINES_AT_ROOT
wx.TR_SINGLE
wx.TR_MULTIPLE
wx.TR_HAS_VARIABLE_ROW_HEIGHT
wx.TR_EDIT_LABELS
wx.TR_HIDE_ROOT
wx.TR_ROW_LINES
wx.TR_FULL_ROW_HIGHLIGHT
wx.TR_DEFAULT_STYLE
wx.TR_TWIST_BUTTONS
wx.TreeItemIcon_Normal
wx.TreeItemIcon_Selected
wx.TreeItemIcon_Expanded
wx.TreeItemIcon_SelectedExpanded
wx.TREE_ITEMSTATE_NONE
wx.TREE_ITEMSTATE_NEXT
wx.TREE_ITEMSTATE_PREV
wx.TREE_HITTEST_ABOVE
wx.TREE_HITTEST_BELOW
wx.TREE_HITTEST_NOWHERE
wx.TREE_HITTEST_ONITEMBUTTON
wx.TREE_HITTEST_ONITEMICON
wx.TREE_HITTEST_ONITEMINDENT
wx.TREE_HITTEST_ONITEMLABEL
wx.TREE_HITTEST_ONITEMRIGHT
wx.TREE_HITTEST_ONITEMSTATEICON
wx.TREE_HITTEST_TOLEFT
wx.TREE_HITTEST_TORIGHT
wx.TREE_HITTEST_ONITEMUPPERPART
wx.TREE_HITTEST_ONITEMLOWERPART
wx.TREE_HITTEST_ONITEM
def test_treeEventsExist(self):
wx.wxEVT_COMMAND_TREE_BEGIN_DRAG
wx.wxEVT_COMMAND_TREE_BEGIN_RDRAG
wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT
wx.wxEVT_COMMAND_TREE_DELETE_ITEM
wx.wxEVT_COMMAND_TREE_GET_INFO
wx.wxEVT_COMMAND_TREE_SET_INFO
wx.wxEVT_COMMAND_TREE_ITEM_EXPANDED
wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING
wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSED
wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSING
wx.wxEVT_COMMAND_TREE_SEL_CHANGED
wx.wxEVT_COMMAND_TREE_SEL_CHANGING
wx.wxEVT_COMMAND_TREE_KEY_DOWN
wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED
wx.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
wx.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK
wx.wxEVT_COMMAND_TREE_END_DRAG
wx.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
wx.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
wx.wxEVT_COMMAND_TREE_ITEM_MENU
wx.EVT_TREE_BEGIN_DRAG
wx.EVT_TREE_BEGIN_RDRAG
wx.EVT_TREE_BEGIN_LABEL_EDIT
wx.EVT_TREE_END_LABEL_EDIT
wx.EVT_TREE_DELETE_ITEM
wx.EVT_TREE_GET_INFO
wx.EVT_TREE_SET_INFO
wx.EVT_TREE_ITEM_EXPANDED
wx.EVT_TREE_ITEM_EXPANDING
wx.EVT_TREE_ITEM_COLLAPSED
wx.EVT_TREE_ITEM_COLLAPSING
wx.EVT_TREE_SEL_CHANGED
wx.EVT_TREE_SEL_CHANGING
wx.EVT_TREE_KEY_DOWN
wx.EVT_TREE_ITEM_ACTIVATED
wx.EVT_TREE_ITEM_RIGHT_CLICK
wx.EVT_TREE_ITEM_MIDDLE_CLICK
wx.EVT_TREE_END_DRAG
wx.EVT_TREE_STATE_IMAGE_CLICK
wx.EVT_TREE_ITEM_GETTOOLTIP
wx.EVT_TREE_ITEM_MENU
#---------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()