| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
print(boxShape.Vertexes) print(boxShape.Edges) print(boxShape.Wires) print(boxShape.Faces) print(boxShape.Shells) print(boxShape.Solids)
For example, let's find the area of each face of our box shape above:
for f in boxShape.Faces: print(f.Area)
Or, for each edge, its start point and end point:
for e in boxShape.Edges: print("New edge") print("Start point:") print(e.Vertexes[0].Point) print("End point:") print(e.Vertexes[1].Point)
As you see, if our boxShape has a "Vertexes" attribute, each Edge of the boxShape also has a "Vertexes" attribute. As we can expect, the boxShape will have 8 vertices, while the edge will only have 2, which are both part of the list of 8.
We can always check what is the type of a shape:
print(boxShape.ShapeType) print(boxShape.Faces[0].ShapeType) print (boxShape.Vertexes[2].ShapeType)
So to resume the whole diagram of Part Shapes: Everything starts with Vertices. With one or two vertices, you form an Edge (full circles have only one vertex). With one or more Edges, you form a Wire. With one or more closed Wires, you form a Face (the additional Wires become "holes" in the Face). With one or more Faces, you form a Shell. When a Shell is fully closed (watertight), you can form a Solid from it. And finally, you can join any number of Shapes of any types together, which is then called a Compound.
We can now try creating complex shapes from scratch, by constructing all their components one by one. For example, let's try to create a volume like this:
We will start by creating a planar shape like this:
First, let's create the four base points:
V1 = FreeCAD.Vector(0,10,0) V2 = FreeCAD.Vector(30,10,0) V3 = FreeCAD.Vector(30,-10,0) V4 = FreeCAD.Vector(0,-10,0)
Then we can create the two linear segments:
L1 = Part.Line(V1,V2) L2 = Part.Line(V4,V3)
Note that we didn't need to create Vertices? We could immediately create Part.Lines from FreeCAD Vectors. This is because here we haven't created Edges yet. A Part.Line (as well as Part.Circle, Part.Arc, Part.Ellipse or PArt.BSpline) does not create an Edge, but rather a base geometry on which an Edge will be created. Edges are always made from such a base geometry, which is stored its Curve attribute. So if you have an Edge, doing:
print(Edge.Curve)
will show you what kind of Edge this is, that is, if it is based on a line, an arc, etc... But let's come back to our exercise, and build the arc segments. For this, we will need a third point, so we can use the convenient Part.Arc, which takes 3 points:
VC1 = FreeCAD.Vector(-10,0,0) C1 = Part.Arc(V1,VC1,V4) VC2 = FreeCAD.Vector(40,0,0) C2 = Part.Arc(V2,VC2,V3)
Now we have 2 lines (L1 and L2) and 2 arcs (C1 and C2). We need to turn them into edges:
E1 = Part.Edge(L1) E2 = Part.Edge(L2) E3 = Part.Edge(C1) E4 = Part.Edge(C2)
Alternatively, base geometries also have a toShape() function that do exactly the same thing:
E1 = L1.toShape() E2 = L2.toShape() ...
Once we have a series of Edges, we can now form a Wire, by giving it a list of Edges. We don't need to take care of the order. OpenCasCade, the geometry "engine" of FreeCAD, is extraordinarily tolerant to unordered geometry. It will sort out what to do:
W = Part.Wire([E1,E2,E3,E4])
And we can check if our Wire was correctly understood, and that it is correclty closed:
print( W.isClosed() )
Which will print "True" or "False". In order to make a Face, we need closed Wires, so it is always a good idea to check that before creating the Face. Now we can create a Face, by giving it a single Wire (or a list of Wires if we had holes):
F = Part.Face(W)
Then we extrude it:
P = F.extrude(FreeCAD.Vector(0,0,10))
Note that P is already a Solid:
print(P.ShapeType)
Because when extruding a single Face, we always get a Solid. This wouldn't be the case, for example, if we had extruded the Wire instead:
S = W.extrude(FreeCAD.Vector(0,0,10)) print(s.ShapeType)
Which will of course give us a hollow shell, with the top and bottom faces missing.
Now that we have our final Shape, we are anxious to see it on screen! So let's create a generic object, and attribute it our new Solid:
myObj2 = FreeCAD.ActiveDocument.addObject("Part::Feature","My_Strange_Solid") myObj2.Shape = P FreeCAD.ActiveDocument.recompute()
Altenatively, the Part module also provides a shortcut that does the above operation quicker (but you cannot choose the name of the object):
Part.show(P)
All of the above, and much more, is explained in detail on the Part Scripting page, together with examples.
Read more:
The Part Workbench: http://www.freecadweb.org/wiki/index.php?title=Part_Workbench
Part scripting: http://www.freecadweb.org/wiki/index.php?title=Topological_data_scripting
Creating parametric objects
In the previous chapter, we saw how to create Part geometry, and how to display it on screen, by attaching it to a "dumb" (non-parametric) document object. This is tedious when we want to change the shape of that object. We would need to create a new shape, then attribute it again to our object.
However, we also saw in all the preceding chapters of this manual how parametric objects are powerful. We only need to change one property, and the shape is recalculated on-the-fly.
Internally, parametric objects don't do anything different than we just did: They recalculate the contents of their Shape property, over and over, each time another property has changed.
FreeCAD provides a very convenient system to build such parametric objects fully in Python. They consist of a simple Python class, which defines all the properties that the object needs, and what will happen when one of these properties changes. The structure of such parametric object is as simple as this:
class myParametricObject: def __init__(self,obj): obj.Proxy = self obj.addProperty("App::PropertyFloat","MyLength") ... def execute(self,obj): print ("Recalculating the shape...") print ("The value of MyLength is:") print (obj.MyLength) ...
All Python classes usually have an init method. What is inside that method is executed when that class is instantiated (which means, in programming slang, that a Python Object is created from that class. Think of a class as a "template" to create live copies of it). In our init function here, we do two important things: 1) store our class itself into the "Proxy" attribute of our FreeCAD documetn object, that is, the FreeCAD document object will carry this code, inside itself, and 2) create all the properties our object needs. There are many types of properties available, you can get the full list by typing this code:
FreeCAD.ActiveDocument.addObject("Part::FeaturePython","dummy").supportedProperties()
Then, the second important part is the execute method. Any code in this method will be executed when the object is marked to be recomputed, which will happen when a property has been changed. That is all there is to it. Inside execute, you need to do all that needs tobe done, that is, calculating a new shape, and attributing to the object itself with something like obj.Shape = myNewShape. That is why the execute method takes an "obj" argument, which will be the FreeCAD document object itself, so we can manipulate it inside our python code.
One last thing is important to remember: When you create such parametric objects in a FreeCAD document, when you save the file, the python code above is not stored inside the file. This is for security reasons, if a FreeCAD file contained code, it would be possible for someone to distribute FreeCAD files containing malicious code that could harm other people's computers. So, if you distribute a file that contains objects made with the above code, such code must also be present on the computer that will open the file. The easiest way to achieve that is usually to save the code above in a macro, and distribute the macro together with your FreeCAD file, or share your macro on the FreeCAD macros repository where anybody can download it.
Below, we will do a small exercise, building a parametric object that is a simple parametric rectangular face. More complex examples are available on the parametric object example and in the FreeCAD source code itself.
We will give our object two properties: Length and Width, which we will use to construct a rectangle. Then, since our object will already have a pre-built Placement property (all geometric object have one by default, no need to add it ourselves), we will displace our rectangle to the location/rotation set in the Placement, so the user will be able to move the rectangle anywhere by editing the Placement property.
class ParametricRectangle: def __init__(self,obj): obj.Proxy = self obj.addProperty("App::PropertyFloat","Length") obj.addProperty("App::PropertyFloat","Width") def execute(self,obj): # we need to import the FreeCAD module here too, because we might be running out of the Console # (in a macro, for example) where the FreeCAD module has not been imported automatically import Part,FreeCAD # first we need to make sure the values of Length and Width are not 0 # otherwise the Part.Line will complain that both points are equal if (obj.Length == 0) or (obj.Width == 0): # if yes, exit this method without doing anything return # we create 4 points for the 4 corners v1 = FreeCAD.Vector(0,0,0) v2 = FreeCAD.Vector(obj.Length,0,0) v3 = FreeCAD.Vector(obj.Length,obj.Width,0) v4 = FreeCAD.Vector(0,obj.Width,0) # we create 4 edges e1 = Part.Line(v1,v2).toShape() e2 = Part.Line(v2,v3).toShape() e3 = Part.Line(v3,v4).toShape() e4 = Part.Line(v4,v1).toShape() # we create a wire w = Part.Wire([e1,e2,e3,e4]) # we create a face f = Part.Face(w) # All shapes have a Placement too. We give our shape the value of the placement # set by the user. This will move/rotate the face automatically. f.Placement = obj.Placement # all done, we can attribute our shape to the object! obj.Shape = f
Instead of pasting the above code in the Python console, we'd better save it somewhere, so we can reuse and modify it later. For example in a new macro (menu Tools -> Macros -> Create). Name it, for example, "ParamRectangle". However, FreeCAD macros are saved with a .FCMacro extension, which Python doesn't recognize when using import. So, before using the above code, we will need to rename the ParamRectangle.FCMacro file to ParamRectangle.py. This can be done simply from your file explorer, by navigating to the Macros folder indicated in menu Tools -> Macros.
Once that is done, we can now do this in the Python Console:
import ParamRectangle
By exploring the contents of ParamRectangle, we can verify that it contains our ParametricRectangle class.
To create a new parametric object using our ParametricRectangle class, we will use the following code. Observe that we use Part::FeaturePython instead of Part::Feature that we have been using in the previous chapters (The Python version allows to define our own parametric behaviour):
myObj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Rectangle") ParamRectangle.ParametricRectangle(myObj) myObj.ViewObject.Proxy = 0 # this is mandatory unless we code the ViewProvider too FreeCAD.ActiveDocument.recompute()
Nothing will appear on screen just yet, because the Length and Width properties are 0, which will trigger our "do-nothing" condition inside execute. We just need to change the values of Length and Width, and our object will magically appear and be recalculated on-the-fly.
Of course it would be tedious to have to type these 4 lines of Python code each time we want to create a new parametric rectangle. A very simple way to solve this is placing the 4 lines above inside our ParamRectangle.py file, at the end, after the end of the ParametricRectange class (We can do this from the Macro editor).
Now, when we type import ParamRectangle, a new parametric rectangle will automatically be created. Even better, we can add a toolbar button that will do just that:
Open menu Tools -> Customize
Under the "Macros" tab, select our ParamRectangle.py macro, fill in the details as you wish, and press "Add":
Under the Toolbars tab, create a new custom toolbar in the workbench of your choice (or globally), select your macro and add it to the toolbar:
That's it, we now have a new toolbar button which, when clicked, will create a parametric rectangle.
Remeber, if you want to distribute files created with this new tool to other people, they must have the ParamRectangle.py macro installed on their computer too.
Read more
The FreeCAD macros repository: http://www.freecadweb.org/wiki/index.php?title=Macros_recipes
Parametric object example: http://www.freecadweb.org/wiki/index.php?title=Scripted_objects
More examples in the FreeCAD code: https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/TemplatePyMod/FeaturePython.py
Creating interface tools
In the last two chapters, we saw how to create Part geometry and create parametric objects. One last piece is missing to gain full control over FreeCAD: Create tools that will interact with the user.
In many situations, it is not very user-friendly to construct an object with zero-values, like we did with the rectangle in the previous chapter, and then ask the user to fill in the Height and Width values in the Properties panel. This works for a very small number of objects, but will become very tedious if you have a lot of rectangles to make. A better way would be to be able to already give the Height and Width when creating the rectangle.
Python offers a basic tool to have the user enter text on screen:
text = raw_input("Height of the rectangle?") print("The entered height is ",text)
However, this requires a running Python console, and when running our code from a macro, we are not always sure that the Python console will be turned on on the user's machine.
The Graphical User Interface, or GUI, that is, all the part of FreeCAD that is displayed on your screen (the menu, toolbars, 3D view, etc), is all there for that purpose: interact with the user. FreeCAD's interface is built with Qt), a very common open-source GUI toolkit that offers a big range of tools such as dialog boxes, buttons, labels, text input boxes or pull-down menus (all these are generically called "widgets").
The Qt tools are very easy to use from Python, thanks to a Python module called Pyside (there are several other Python modules to communicate with Qt from Python too). This module allows you to create and interact with widgets, read what the user did with them (what was filled in text boxes) or do things when, for example, a button was pressed.
Qt also provides another interesting tool called Qt Designer, which is today embedded inside a bigger application called Qt Creator. It allows to design dialog boxes and interface panels graphically, instead of having to code them manually. In this chapter, we will use Qt Creator to desig a panel widget that we will use in the Task panel of FreeCAD. So you will need to download and install Qt Creator from its official page if you are on Windows or Mac (on Linux it will usually be available from your software manager application).
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |