>
> How are you doing with pythonOCC development?
> I'm really curious ;')
> Is there some OCAF code you'd be willing to share?
I was writing a little traits example illustrating
dependency-resolution. I wanted to add a Chamfer filter and hence needed
to extract edges from solids. That's when I ran into the Null Shapes
issue with your Topo class (and TopExp_Explorer).
Knowing I can't make lists of subshapes I could work round this but as I
think about the requirements of a chamfer-tool, tracking sub-shapes is a
difficult problem which OCAF/TNaming addresses. My traits example
doesn't (yet) hook up to OCAF (although this is perfectly possible) so
I'm not going to bother with the chamfer-tool after all. I was just
going to post what I've got on the wiki, but it doesn't appear to do
syntax highlighting (any chance of setting up a MoinMoin wiki instead?
it's more python-friendly).
... so ... I'll just post the code here ...
There are 4 files attached. Run feature_tree_test.py. You need Traits
installed (version >=3.0 should be OK), with a Traits-UI backend (I've
only tested with the WX backend).
The model is constructed from a box and a sphere and a boolean-op
filter. You can select any of these objects from the tree and edit them
and see the result in the wxViewer3d window.
This code also serves as an example of how to update a
AIS_InteractiveContext with a new Shape, after some edit operation.
I going to press on with OCAF stuff...
Bryan
>
> Cheers,
>
> -jelle
>
>
"""
A simple example of a editable feature tree. We only have two source objects
(box and sphere) and a boolean-op filter.
"""
from occ_model import BlockSource, SphereSource, BooleanOpFilter
from occ_display import OCCModel, DisplayShape
block = BlockSource()
sphere = SphereSource()
bop = BooleanOpFilter(input=block, tool=sphere)
shape = DisplayShape(input=bop)
model = OCCModel(shapes=[shape])
model.configure_traits()
from enthought.traits.api import HasTraits, Instance, Event, List, Str, Button
from enthought.traits.ui.api import CustomEditor, View, Item, TreeEditor, TreeNode,\
VSplit, HSplit
from OCC.Display.wxDisplay import wxViewer3d
from OCC import AIS
from occ_model import Input, ProcessObject
import wx
#Jelle please don't shout at me!
import os
os.environ['CSF_GraphicShr'] = r"/usr/local/lib/libTKOpenGl.so"
class MyCanvas(wxViewer3d):
def __init__(self, *args, **kwds):
super(MyCanvas, self).__init__(*args, **kwds)
self.SetSizeWH(800,-1)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self._pending = []
self.context = None
def OnPaint(self, event):
event.Skip()
if self.context is None:
self.FirstDisplay()
def Display(self, shape):
context = self.context
if context is None:
self._pending.append(shape)
else:
ais_shape = shape.ais_shape
context.Display(ais_shape.GetHandle())
def Render(self):
context = self.context
if context is not None:
context.Redisplay(AIS.AIS_KOI_Shape)
def FirstDisplay(self):
self.InitDriver()
viewer = self._display
self.context = viewer.Context
for shape in self._pending:
self.Display(shape)
self._pending = []
self._display.FitAll()
def MakeCanvas(parent, editor):
"""
A factory function to produce the wxViewer3d instance. This is
called by the CustomEditor object.
I still havn't found a nice way to do the InitDriver() stuff.
"""
canvas = MyCanvas(parent)
shapes = editor.object.shape_list.shapes
def on_render(obj, name, vnew):
canvas.Render()
for shape in shapes:
canvas.Display(shape)
shape.on_trait_change(on_render, "render")
return canvas
class DisplayShape(HasTraits):
"""
A display-able shape. This class wraps AIS.AIS_Shape.
Instances of this class are the root shape objects in the model
and are the things we actually visualise.
We could add visualisation traits to this object
"""
name = Str("an AIS shape")
input = Input
_input = List
ais_shape = Instance(AIS.AIS_Shape)
render = Event
def _input_changed(self, name, vold, vnew):
if vold is not None:
vold.on_trait_change(self.on_modified, "modified", remove=True)
vnew.on_trait_change(self.on_modified, "modified")
self._input = [vnew]
def on_modified(self, vnew):
if vnew is True:
shape = self.input.shape
self.ais_shape.Set(shape)
self.render = True
def _ais_shape_default(self):
return AIS.AIS_Shape(self.input.shape)
class ShapeList(HasTraits):
"""
Just a container for DisplayShapes. This exists mostly
because the TreeEditor expects child nodes on a single
sub-object. A simple list doesn't work.
"""
shapes = List(DisplayShape)
occ_tree = TreeEditor(nodes=[
TreeNode(node_for=[ShapeList],
auto_open=True,
children='shapes',
label="=Shapes",
view=View()),
TreeNode(node_for=[DisplayShape],
auto_open=True,
children='_input',
label='name',
view=View() ),
TreeNode(node_for=[ProcessObject],
auto_open=True,
children='_inputs',
label='name')],
orientation="vertical"
)
class OCCModel(HasTraits):
shapes = List
shape_list = Instance(ShapeList)
traits_view=View(HSplit(
Item('shape_list', editor=occ_tree, show_label=False,
width=-300),
Item('shape_list', editor=CustomEditor(MakeCanvas),
show_label=False, resizable=True),
id="occ.traits.test_feature_model_layout",
dock="fixed"
),
resizable=True,
width=1000,
height=800,
id="occ.traits.test_feature_model"
)
def _shapes_changed(self, vnew):
self.shape_list = ShapeList(shapes=vnew)
from enthought.traits.api import (HasTraits, Property, Bool,
on_trait_change, cached_property, Instance,
Float as _Float, List, Str, Enum
)
from enthought.traits.ui.api import View, Item
from utils import Tuple, EditorTraits
from OCC import TDF, TopoDS, BRepPrimAPI, BRepAlgoAPI, gp
###defining an dedicated trait for filter inputs means
###we can track input changes easily
Input = Instance(klass="ProcessObject", process_input=True)
class Float(EditorTraits, _Float):
"""I define my own Float trait because I want to change the
default behaviour of the default editors to have auto_set=False"""
class ProcessObject(HasTraits):
"""
Base class for all model component objects
"""
name = Str
#
#This flag indicates if the object parameters have changed
#
modified = Bool(True)
#
#We could link each process-object to a node in an OCAF document
#Not used yet.
#
label = Instance(TDF.TDF_Label)
#
#This is the output of the object. The property calls the execute method
#to evaluate the result (which in turn calls up the tree)
#
shape = Property(Instance(TopoDS.TopoDS_Shape))
#
#Shadow trait which stores the cached shape
#
_shape = Instance(TopoDS.TopoDS_Shape)
#
#A list of all inputs, for the benefit of the TreeEditor
#
_inputs = List
#
#We hook up listeners to each input to listen to changes in their
#modification trait. Hence, modifications propagate down the tree
#
@on_trait_change("+process_input")
def on_input_change(self, obj, name, vold, vnew):
print "ch", vold, vnew
if vold is not None:
vold.on_trait_change(self.on_modify, 'modified', remove=True)
if vold in self._input_set:
del self._inputs[self._inputs.index(vold)]
vnew.on_trait_change(self.on_modify, 'modified')
self._inputs.append(vnew)
def on_modify(self, vnew):
if vnew:
self.modified = True
def _get_shape(self):
if self.modified:
shape = self.execute()
self._shape = shape
self.modified = False
return shape
else:
return self._shape
def execute(self):
"""return a TopoDS_Shape object"""
raise NotImplementedError
class BlockSource(ProcessObject):
name = "Block"
dims = Tuple(10.0,20.0,30.0, editor_traits={'cols':3})
position = Tuple(0.,0.,0., editor_traits={'cols':3})
x_axis = Tuple(1.,0.,0., editor_traits={'cols':3})
z_axis = Tuple(0.,0.,1., editor_traits={'cols':3})
traits_view = View('name',
'dims',
'position',
'x_axis',
'z_axis')
@on_trait_change("dims, position, x_axis, z_axis")
def on_edit(self):
self.modified = True
def execute(self):
ax = gp.gp_Ax2(gp.gp_Pnt(*self.position),
gp.gp_Dir(*self.z_axis),
gp.gp_Dir(*self.x_axis))
m_box = BRepPrimAPI.BRepPrimAPI_MakeBox(ax, *self.dims)
return m_box.Shape()
class SphereSource(ProcessObject):
name="Sphere"
radius = Float(5.0)
position = Tuple(0.,0.,0., editor_traits={'cols':3})
traits_view = View('name',
'radius',
'position')
@on_trait_change("radius, position")
def on_edit(self):
self.modified = True
def execute(self):
pt = gp.gp_Pnt(*self.position)
R = self.radius
sph = BRepPrimAPI.BRepPrimAPI_MakeSphere(pt, R)
return sph.Shape()
class BooleanOpFilter(ProcessObject):
name = "Boolean Operation"
input = Input
tool = Input
operation = Enum("cut", "fuse", "common")
map = {'cut': BRepAlgoAPI.BRepAlgoAPI_Cut,
'fuse': BRepAlgoAPI.BRepAlgoAPI_Fuse,
'common': BRepAlgoAPI.BRepAlgoAPI_Common}
traits_view = View('operation')
def _operation_changed(self, vnew):
self.name = "Boolean Op: %s"%vnew
self.modified = True
def execute(self):
builder = self.map[self.operation]
s1 = self.input.shape
s2 = self.tool.shape
return builder(s1, s2).Shape()
from enthought.traits.api import Range as _Range, Tuple as _Tuple
from enthought.traits.ui.api import TupleEditor
class EditorTraits(object):
def get_editor(self, *args, **kwds):
e = super(EditorTraits, self).get_editor(*args, **kwds)
editor_t = {'auto_set':False,
'enter_set':True}
metadata = self._metadata
if 'editor_traits' in metadata:
editor_t.update(metadata['editor_traits'])
e.set(**editor_t)
return e
class Range(EditorTraits, _Range):
pass
class Tuple(EditorTraits, _Tuple):
pass
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org
https://mail.gna.org/listinfo/pythonocc-users