Adding text to a Shape

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
AndreaG
Posts: 11
Joined: Tue Feb 11, 2014 7:50 am

Adding text to a Shape

Post by AndreaG »

Hi!

I am doing some experiments with a custom FeaturePython object that should draw a line with a label.

Code: Select all

class LabeledLine(object):
    def __init__(self, obj):
        obj.Proxy = self
        obj.addProperty("App::PropertyVector", "P1", "", "First point")
        obj.addProperty("App::PropertyVector", "P2", "", "Second point")

    def onChanged(self, fp, prop):
        if prop in ["P1", "P2"]:
            self.execute(fp)

    def execute(self, fp): 
        ''' Draw a line with a label '''
        fp.Shape = Part.makeLine(fp.P1, fp.P2) 

        text = Draft.makeText("Text", point=fp.Shape.BoundBox.Center)
        text.ViewObject.FontSize = 2
However, with this code I get two distinct objects in the tree view on the left sidebar of the application instead of one: the line and the text.

The latter is not related in anyway with the line:
- When I change the properties P1 and P2 I get every time a new text object
- When I delete the line the text remains in the scene

Is it possible to embed the text inside fp.Shape so that I can have a more consistent behavior?
ickby
Veteran
Posts: 3116
Joined: Wed Oct 05, 2011 7:36 am

Re: Adding text to a Shape

Post by ickby »

Draft.makeText creates a document object, it therefore get's added directly into the tree. you cant add it to a shape, as it does not create a shape, as far as I know it does not work with occ types at all. The document object holds the text string and the viewprovider draws the text directly into the sceane graph. So you have two possibilities:

1. Make your own viewprovider and copy the behaviour of the one for draft text object
2. Dependend on you FreeCAD version you can use draft ShapeString to create a text as shape and use it as you currently intend
AndreaG
Posts: 11
Joined: Tue Feb 11, 2014 7:50 am

Re: Adding text to a Shape

Post by AndreaG »

The StringShape would be the ideal solution for my purpose.
I saw it in the documentation but I'm not able to use the function with my installation.

I'm currently running this version:

Code: Select all

OS: Windows 7
Platform: 32-bit
Version: 0.13.1828
Python version: 2.6.2
Qt version: 4.5.2
Coin version: 3.1.0
SoQt version: 1.4.1
OCC version: 6.3.0
Is it available in 0.14 (unstable)?
AndreaG
Posts: 11
Joined: Tue Feb 11, 2014 7:50 am

Re: Adding text to a Shape

Post by AndreaG »

I installed the latest build from Sourceforge (Version: 0.14.2778 (Git)) and I am trying to use the ShapeString.

I changed my code into this one:

Code: Select all

class LabeledLine(object):
    def __init__(self, obj):
        obj.Proxy = self
        obj.addProperty("App::PropertyVector", "P1", "", "First point")
        obj.addProperty("App::PropertyVector", "P2", "", "Second point")

    def onChanged(self, fp, prop):
        if prop in ["P1", "P2"]:
            self.execute(fp)

    def execute(self, fp): 
        ''' Draw a line with a label '''
        line = Part.makeLine(fp.P1, fp.P2)
        text = Draft.makeShapeString("Text", "../Mod/myMod/fonts/Arial.ttf", 5.)
        text.Placement.Base = line.BoundBox.Center
        fp.Shape = Part.Compound([line, text])
However when I use the function Draft.makeShapeString() I still get a distinct object in the tree view.
This happens when I run the commands in the Python Console from the GUI of the App.

On the other hand, the application crashes (actually it doesn't respond anymore) when I use the code above with my FeaturePython object.
I haven't understood exactly the point that causes the problem yet, since I have some problem in debugging my Python code...

Am I doing anything wrong in the code?
User avatar
shoogen
Veteran
Posts: 2823
Joined: Thu Dec 01, 2011 5:24 pm

Re: Adding text to a Shape

Post by shoogen »

Draft.makeShapeString adds a new Object to the Document. It does not return a shape.
Please have a look at https://github.com/FreeCAD/FreeCAD_sf_m ... t.py#L4565
ickby
Veteran
Posts: 3116
Joined: Wed Oct 05, 2011 7:36 am

Re: Adding text to a Shape

Post by ickby »

you can have a look at the implementation and use Part.makeWireString the same way draft ShapeString uses it. This method was added together with shapestring, so it si also only avaliable in the new version
User avatar
wandererfan
Veteran
Posts: 6318
Joined: Tue Nov 06, 2012 5:42 pm
Contact:

Re: Adding text to a Shape

Post by wandererfan »

Something like this in your Execute method should build you a Compound shape that is a line + text:

Code: Select all

line = Part.makeLine(P1, P2)   # a shape
TempText = Draft.makeShapeString(String,FontPath+FontName,Height,Track)  # a document object
ts = TempText.Shape.copy() # a shape
App.ActiveDocument.removeObject(TempText.Name)
tsplm = ts.Placement
tsplm.move(line.BoundBox.Center)
compshape = Part.Compound([line, ts])    # a shape
Part.show(compshape)  
wf
AndreaG
Posts: 11
Joined: Tue Feb 11, 2014 7:50 am

Re: Adding text to a Shape

Post by AndreaG »

Thanks to all of three for the answers!

I am trying the code above but I still have a strange behavior: the application is apparently blocked (the window does not respond) and after a while (a few minutes) the following error appears:

Code: Select all

<unknown exception traceback><type 'exceptions.RuntimeError'>: maximum recursion depth exceeded
Traceback (most recent call last):
  File "C:\Program Files\FreeCAD_0.14.2778_x64\Mod\myMod\MyTools.py", line 46, in execute
    text_tmp = Draft.makeShapeString(self.label,"../Mod/myMod/fonts/Arial.ttf", 3., 0)  # a document object
  File "C:\Program Files\FreeCAD_0.14.2778_x64\Mod\Draft\Draft.py", line 1985, in makeShapeString
    _ShapeString(obj)
  File "C:\Program Files\FreeCAD_0.14.2778_x64\Mod\Draft\Draft.py", line 4220, in __init__
    _DraftObject.__init__(self,obj,"ShapeString")
<type 'exceptions.RuntimeError'>: maximum recursion depth exceeded while calling a Python object
T1: maximum recursion depth exceeded while calling a Python object
If I start FreeCAD and run the commands in the Python console they seem to work fine.
But, if I start FreeCAD, execute my Tool, wait the function to return with the error and finally run the commands in the Python console the application blocks again.

This is the code in the execute function:

Code: Select all

 def execute(self, fp):
        ''' Draw a line to highlight the edge with a label'''
        line = Part.makeLine(fp.P1, fp.P2)   # a shape
        text_tmp = Draft.makeShapeString(self.label,"../Mod/MPH/fonts/Arial.ttf", 3., 0)  # a document object
        ts = text_tmp.Shape.copy() # a shape
        App.ActiveDocument.removeObject(text_tmp.Name)
        tsplm = ts.Placement
        tsplm.move(line.BoundBox.Center)
        compshape = Part.Compound([line, ts])    # a shape
        # Part.show(compshape)
        fp.Shape = Part.Compound([line, ts])
I guess something is wrong with Draft.makeShapeString but I can't understand what it is.

I have updated to the following version:

Code: Select all

OS: Windows 7
Platform: 64-bit
Version: 0.14.2778 (Git)
Branch: master
Hash: 0506a918b24e015ac6277a7cf613ce151e1dee44
Python version: 2.6.4
Qt version: 4.5.3
Coin version: 2.4.5a
SoQt version: 1.4.1
User avatar
wandererfan
Veteran
Posts: 6318
Joined: Tue Nov 06, 2012 5:42 pm
Contact:

Re: Adding text to a Shape

Post by wandererfan »

AndreaG wrote:I guess something is wrong with Draft.makeShapeString but I can't understand what it is.
There's a race condition between the "FreeCAD.ActiveDocument.recompute()" in makeShapeString and the "execute" method in your class. execute calls makeShapeString which calls recompute which calls execute which calls makeShapeString ... until Python throws a recursion depth exception.

This isn't exactly what you were looking for, but it seems to work (based on Draft.makeBlock):

Code: Select all

import FreeCAD
import Part
import Draft

class LabeledLine(Draft._DraftObject):
    def __init__(self, obj):
        Draft._DraftObject.__init__(self,obj,"LabeledLine")
        obj.addProperty("App::PropertyLinkList","Components","Draft",
                        "The line and text components of this labeled line")

    def onChanged(self, fp, prop):
        if prop in ["Components"]:
            self.createGeometry(fp)

    def execute(self, fp):
        self.createGeometry(fp)
        
    def createGeometry(self,fp):
        import Part
        plm = fp.Placement
        shps = []
        for c in fp.Components:
            shps.append(c.Shape)
        if shps:
            shape = Part.makeCompound(shps)
            fp.Shape = shape
        fp.Placement = plm

# set the parameters
P1 = FreeCAD.Vector(0,0,0)
P2 = FreeCAD.Vector(500,500,0)
LabelText = "Some Text for My Line"
FontPath = '/usr/share/fonts/truetype/msttcorefonts/'
FontName = 'Arial.ttf'
FontFile = FontPath+FontName
Size = 100.0
myLine = Draft.makeLine(P1,P2)
myString = Draft.makeShapeString(LabelText,FontFile,Size)
myString.Placement.move(myLine.Shape.BoundBox.Center)

# make the feature
feat = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","LabeledLine")        
LabeledLine(feat)
feat.Components = [myLine,myString]
Draft._ViewProviderDraft(feat.ViewObject)
AndreaG
Posts: 11
Joined: Tue Feb 11, 2014 7:50 am

Re: Adding text to a Shape

Post by AndreaG »

wandererfan wrote: There's a race condition between the "FreeCAD.ActiveDocument.recompute()" in makeShapeString and the "execute" method in your class. execute calls makeShapeString which calls recompute which calls execute which calls makeShapeString ... until Python throws a recursion depth exception.
This makes perfect sense!
Since the text in my label is static, I moved the creation of ShapeString in the __init__() method of my class and I saved the copy of the Shape in self.labelShape.
Now the execute function only creates the line and changes the placement of the text. Everything seems to work fine!

Thanks! :mrgreen:
Post Reply