> 
> 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

Reply via email to