FreeCAD-macros

Форк
0
/
ObjectsToPython.FCMacro 
509 строк · 16.1 Кб
1
# -*- coding: utf-8 -*-
2

3
__Version__ = '0.7.3'
4
__Date__ = '2024-03-16'
5
__License__ = 'LGPL-3.0-or-later'
6
__Web__ = ''
7
__Wiki__ = 'README.md'
8
__Name__ = 'Objects To Python'
9
__Comment__ = 'Exports objects from a FreeCAD project to a python script'
10
__Author__ = 'Christi'
11
__Icon__ = 'ObjectsToPython.svg'
12
__Help__ = 'ObjectsToPython/README.md'
13
__Status__ = 'Alpha'
14
__Requires__ = 'FreeCAD >= 0.19'
15
__Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=34612'
16
__Files__ = 'ObjectsToPython/README.md,ObjectsToPython/ObjectsToPython.ui,ObjectsToPython.svg'
17

18
import os
19
import re
20

21
import FreeCAD as app
22
import FreeCADGui as gui
23
import Part
24
from FreeCAD import Vector, Placement
25

26

27
def exportObjectsToPython(doc=None):
28
    if doc is None:
29
        doc = app.activeDocument()
30

31
    add_script_line('from FreeCAD import Vector, Placement, Rotation')
32
    add_script_line('import Sketcher')
33
    add_script_line('import Part')
34
    add_script_line('import FreeCAD as app')
35
    objectlist = []
36
    skip_objects = [
37
        ('App::Line', 'X_Axis'),
38
        ('App::Line', 'Y_Axis'),
39
        ('App::Line', 'Z_Axis'),
40
        ('App::Plane', 'XY_Plane'),
41
        ('App::Plane', 'XZ_Plane'),
42
        ('App::Plane', 'YZ_Plane'),
43
        ('App::Origin', 'Origin'),
44
    ]
45

46
    selection = gui.Selection.getSelection()
47
    if not selection:
48
        selection = doc.Objects
49
    else:
50
        expandSelection(selection)
51

52
    for obj in doc.Objects:
53
        objectlist.append(doc.getObject(obj.Name))
54

55
    for obj in selection:
56
        if obj.TypeId == 'Sketcher::SketchObject':
57
            add_script_line('')
58
            add_script_line(f'def createSketch_{varname(obj)}(doc):')
59
            addObject(doc, obj, objectlist)
60
            add_body_line('return ' + varname(obj))
61

62
    add_script_line('')
63
    add_script_line('')
64
    add_script_line(f'def make_{doc.Name}():')
65
    if not selection:
66
        add_body_line(f"doc = app.newDocument('{doc.Name}')")
67
    else:
68
        add_body_line('doc = app.activeDocument()')
69

70
    for obj in selection:
71
        if (obj.TypeId, obj.Label) not in skip_objects:
72
            add_script_line('')
73
            if obj.TypeId == 'Sketcher::SketchObject':
74
                add_body_line(f'{varname(obj)} = createSketch_{
75
                              varname(obj)}(doc)')
76
            else:
77
                addObject(doc, obj, objectlist)
78

79
    for obj in selection:
80
        if obj.TypeId == 'PartDesign::Body':
81
            add_script_line('')
82
            add_body_line(f'{varname(obj)}.Group = {varname(obj)}_Group')
83
            if obj.Tip:
84
                add_body_line(f'{varname(obj)}.Tip = {varname(obj.Tip)}')
85

86
    add_body_line('')
87
    add_body_line('doc.recompute()')
88
    add_script_line('')
89
    add_script_line('')
90
    add_script_line(f'make_{doc.Name}()')
91

92

93
def varname(obj):
94
    forbidden = [' ', '.', ',', '%', '+', '-', '*',
95
                 '/', '(', ')', '[', ']', '=', '"', "'"]
96
    l = obj.Label
97
    for f in forbidden:
98
        l = l.replace(f, '_')
99

100
    if l[0].isdigit():
101
        l = '_' + l
102
    return l
103

104

105
def add_script_line(line, indent=0):
106
    spaces = ' ' * 4 * indent
107
    dialog.form.textEdit.append(spaces + line)
108

109

110
def add_body_line(line, indent=1):
111
    add_script_line(line, indent)
112

113

114
def expandSelection(selection):
115
    addthis = []
116
    for obj in selection:
117
        if hasattr(obj, 'Group'):
118
            addthis += obj.Group
119
        if hasattr(obj, 'Source'):
120
            addthis.append(obj.Source)
121
        if hasattr(obj, 'Links'):
122
            addthis += obj.Links
123

124
    addcount = 0
125
    for a in addthis:
126
        if ((a not in selection)
127
                and hasattr(obj, 'Name')
128
                and hasattr(obj, 'TypeId')):
129
            selection.insert(0, a)
130
            addcount = addcount + 1
131

132
    if addcount > 0:
133
        expandSelection(selection)
134

135

136
def addObject(doc, obj, objectlist):
137
    objname = varname(obj)
138
    add_body_line(f"{objname} = doc.addObject('{obj.TypeId}', '{objname}')")
139
    defaultobj = doc.addObject(obj.TypeId, obj.Name + 'Default')
140
    addProperties(obj, objname, defaultobj, objectlist)
141
    addProperties(obj.ViewObject, objname + '.ViewObject',
142
                  defaultobj.ViewObject, [])
143
    doc.removeObject(obj.Name + 'Default')
144

145

146
def addProperties(obj, objname, refobj, objectlist):
147
    skipProperties = ['Label', 'Shape', 'Proxy',
148
                      'AddSubShape', 'FullyConstrained', 'VisualLayerList']
149
    skipObjProp = [
150
        ('Sketcher::SketchObject', 'Geometry'),
151
        ('Sketcher::SketchObject', 'Constraints'),
152
        ('PartDesign::Body', 'Origin'),
153
        ('PartDesign::Body', 'Tip'),
154
    ]
155

156
    if obj.TypeId == 'Spreadsheet::Sheet':
157
        addSpreadsheet(obj, objname)
158
        return
159

160
    if obj.TypeId == 'Sketcher::SketchObject':
161
        addSketch(obj, objname)
162

163
    for propname in obj.PropertiesList:
164
        if ((propname not in skipProperties)
165
                and ((obj.TypeId, propname) not in skipObjProp)):
166
            prop = obj.getPropertyByName(propname)
167
            val = objectToText(prop, objectlist)
168

169
            try:
170
                refprop = refobj.getPropertyByName(propname)
171
                defaultval = objectToText(refprop, objectlist)
172
            except:
173
                defaultval = None
174

175
            if propname == 'Support':
176
                val = val.replace('XY_Plane', 'doc.XY_Plane')
177
                val = val.replace('XZ_Plane', 'doc.XZ_Plane')
178
                val = val.replace('YZ_Plane', 'doc.YZ_Plane')
179

180
            if propname == 'ExpressionEngine':
181
                for expression in prop:
182
                    add_body_line(objname + '.setExpression' +
183
                                  str(expression).replace('<', '').replace('>', ''))
184

185
            elif propname == '_Body':
186
                add_body_line(f'{val}_Group.append({objname})')
187

188
            elif (obj.TypeId, propname) == ('PartDesign::Body', 'Group'):
189
                add_body_line(f'{objname}_Group = []')
190

191
            elif (obj.TypeId, propname) == ('App::Part', 'Group'):
192
                proplist = []
193
                for pn in prop:
194
                    proplist.append(varname(pn))
195

196
                add_body_line(objname + '.Group = ' + str(proplist))
197

198
            elif objectToText(prop, objectlist) is not None:
199
                if val != defaultval:
200
                    add_body_line(f'{objname}.{propname} = {val}')
201

202

203
def objectToText(obj, objectlist=None):
204
    if objectlist is None:
205
        objectlist = []
206
    if obj in objectlist:
207
        return obj.Label
208

209
    # how can I test if type(obj) is Base.Quantity ?
210
    if hasattr(obj, 'Value') and hasattr(obj, 'Unit'):
211
        return str(obj.Value)
212

213
    if isinstance(obj, str):
214
        return f"'{obj}'"
215

216
    if isinstance(obj, bool):
217
        return 'True' if obj else 'False'
218

219
    if isinstance(obj, int):
220
        return str(obj)
221

222
    if isinstance(obj, float):
223
        return floatstr(obj)
224

225
    if isinstance(obj, Placement):
226
        return f'Placement({objectToText(obj.Base)}, {objectToText(obj.Rotation)})'
227

228
    if isinstance(obj, Part.Point):
229
        return f'Part.Point(Vector({floatstr(obj.X)}, {floatstr(obj.Y)}, {floatstr(obj.Z)}))'
230

231
    if isinstance(obj, Part.LineSegment):
232
        return f'Part.LineSegment({obj.StartPoint}, {obj.EndPoint})'
233

234
    if isinstance(obj, Part.Circle):
235
        return f'Part.Circle({vecstr(obj.Center)}, {obj.Axis}, {floatstr(obj.Radius)})'
236

237
    if isinstance(obj, Part.ArcOfCircle):
238
        return f'Part.ArcOfCircle({objectToText(obj.Circle)}, {obj.FirstParameter}, {obj.LastParameter})'
239

240
    if isinstance(obj, Part.Ellipse):
241
        return f'Part.Ellipse({vecstr(obj.Center)}, {obj.MajorRadius}, {obj.MinorRadius})'
242

243
    if isinstance(obj, Part.BSplineCurve):
244
        poles = ''
245
        komma = False
246
        for p in obj.getPoles():
247
            if komma:
248
                poles += ', '
249
            poles += vecstr(p)
250
            komma = True
251

252
        return f'Part.BSplineCurve([{poles}])'
253

254
    if isinstance(obj, Part.Parabola):
255
        return f'Part.Parabola({vecstr(obj.Focus)}, {vecstr(obj.Location)}, {vecstr(obj.Axis)})'
256

257
    if isinstance(obj, Part.Hyperbola):
258
        return f'Part.Hyperbola({vecstr(obj.Center)}, {obj.MajorRadius}, {obj.MinorRadius})'
259

260
    if isinstance(obj, Part.ArcOfEllipse):
261
        return f'Part.ArcOfEllipse({objectToText(obj.Ellipse)}, {obj.FirstParameter}, {obj.LastParameter})'
262

263
    if isinstance(obj, Part.ArcOfParabola):
264
        return f'Part.ArcOfParabola({objectToText(obj.Parabola)}, {obj.FirstParameter}, {obj.LastParameter})'
265

266
    if isinstance(obj, Part.ArcOfHyperbola):
267
        return f'Part.ArcOfHyperbola({objectToText(obj.Hyperbola)}, {obj.FirstParameter}, {obj.LastParameter})'
268

269
    if isinstance(obj, Vector):
270
        return vecstr(obj)
271

272
    liststart = ''
273
    if isinstance(obj, list):
274
        liststart = '['
275
        listend = ']'
276

277
    if isinstance(obj, tuple):
278
        liststart = '('
279
        listend = ')'
280

281
    if liststart != '':
282
        sline = liststart
283
        comma = False
284
        for listele in obj:
285
            if comma:
286
                sline = sline + ', '
287
            else:
288
                comma = True
289

290
            val = objectToText(listele, objectlist)
291
            if val is not None:
292
                sline = sline + val
293

294
        sline += listend
295
        return sline
296

297
    stringobj = str(obj)
298
    return stringobj
299

300

301
def addSketch(obj, objname):
302
    prop = obj.getPropertyByName('Geometry')
303
    gflist = obj.GeometryFacadeList
304

305
    n = 0
306
    exposing = 0
307
    exposed = []
308
    for geo in prop:
309
        objstr = objectToText(geo)
310

311
        if exposing > 0:
312
            exposed.append(n)
313
            exposing = exposing - 1
314
        else:
315
            add_body_line(f'geo{n} = {objname}.addGeometry({objstr})')
316
            if gflist[n].Construction:
317
                add_body_line(f'{objname}.toggleConstruction(geo{n})')
318

319
            if isinstance(geo, Part.BSplineCurve):
320
                poles = len(geo.getPoles())
321
                exposing = poles - 2
322
                if exposing < 2:
323
                    exposing = 2
324

325
            if isinstance(geo, Part.ArcOfParabola):
326
                exposing = 2
327

328
            if isinstance(geo, Part.ArcOfHyperbola):
329
                exposing = 3
330

331
            if isinstance(geo, Part.ArcOfEllipse):
332
                exposing = 4
333

334
        n = n + 1
335

336
    splinecount = 0
337
    concount = 0
338
    prop = obj.getPropertyByName('Constraints')
339
    for con in obj.Constraints:
340
        conargs = con.Value
341
        contype = con.Type
342
        if con.First < 0:
343
            first = str(con.First)
344
        else:
345
            first = f'geo{con.First}'
346

347
        if con.Second < 0:
348
            sec = str(con.Second)
349
        else:
350
            sec = f'geo{con.Second}'
351

352
        if con.Third < 0:
353
            third = str(con.Third)
354
        else:
355
            third = f'geo{con.Third}'
356

357
        if con.Type == 'Coincident':
358
            conargs = f'{first}, {con.FirstPos}, {sec}, {con.SecondPos}'
359
        elif con.Type == 'PointOnObject':
360
            conargs = f'{first}, {con.FirstPos}, {sec}'
361

362
        elif con.Type == 'Vertical':
363
            if sec[:3] == 'geo':
364
                # Two points.
365
                conargs = f'{first}, {con.FirstPos}, {sec}, {con.SecondPos}'
366
            else:
367
                # Line.
368
                conargs = str(first)
369

370
        elif con.Type == 'Horizontal':
371
            if sec[:3] == 'geo':
372
                # Two points.
373
                conargs = f'{first}, {con.FirstPos}, {sec}, {con.SecondPos}'
374
            else:
375
                # Line.
376
                conargs = str(first)
377
        elif con.Type == 'Parallel':
378
            conargs = f'{first}, {sec}'
379

380
        elif con.Type == 'PerpendicularViaPoint':
381
            conargs = f'{first}, {sec}, {third}, {con.ThirdPos}'
382
        elif con.Type == 'Perpendicular':
383
            if con.FirstPos == 0:
384
                conargs = f'{first}, {sec}'
385
            elif third[:3] == 'geo':
386
                contype = 'PerpendicularViaPoint'  # compatiblity to FreeCAD 2.20
387
                conargs = f'{first}, {sec}, {third}, {con.ThirdPos}'
388
            elif con.SecondPos == 0:
389
                conargs = f'{first}, {con.FirstPos}, {sec}'
390
            else:
391
                conargs = f'{first}, {con.FirstPos}, {sec}, {con.SecondPos}'
392

393
        elif con.Type == 'TangentViaPoint':
394
            conargs = f'{first}, {sec}, {third}, {con.ThirdPos}'
395
        elif con.Type == 'Tangent':
396
            if con.Second < 0:
397
                conargs = f'{first}, {con.FirstPos}'
398
            elif third[:3] == 'geo':
399
                contype = 'TangentViaPoint'  # compatiblity to FreeCAD 2.20
400
                conargs = f'{first}, {sec}, {third}, {con.ThirdPos}'
401
            elif con.SecondPos == 0:
402
                conargs = f'{first}, {con.FirstPos}, {sec}'
403
            else:
404
                conargs = f'{first}, {con.FirstPos}, {sec}, {con.SecondPos}'
405

406
        elif con.Type == 'Equal':
407
            conargs = f'{first}, {sec}'
408

409
        elif con.Type == 'Symmetric':
410
            conargs = f'{first}, {con.FirstPos}, {sec}, {
411
                con.SecondPos}, {third}, {con.ThirdPos}'
412
        elif con.Type == 'Block':
413
            conargs = str(first)
414

415
        elif con.Type == 'Distance':
416
            if sec[:3] != 'geo':
417
                conargs = f'{first}, {floatstr(con.Value)}'
418
            elif con.FirstPos == 0:
419
                conargs = f'{first}, {sec}, {floatstr(con.Value)}'
420
            elif con.SecondPos == 0:
421
                conargs = f'{first}, {con.FirstPos}, {
422
                    sec}, {floatstr(con.Value)}'
423
            else:
424
                conargs = f'{first}, {con.FirstPos}, {sec}, {
425
                    con.SecondPos}, {floatstr(con.Value)}'
426
        elif con.Type == 'DistanceX' or con.Type == 'DistanceY':
427
            if sec[:3] != 'geo':
428
                conargs = f'{first}, {con.FirstPos}, {floatstr(con.Value)}'
429
            else:
430
                conargs = f'{first}, {con.FirstPos}, {sec}, {
431
                    con.SecondPos}, {floatstr(con.Value)}'
432

433
        elif con.Type == 'AngleViaPoint':
434
            conargs = f'{first}, {sec}, {third}, {floatstr(con.Value)}'
435
        elif con.Type == 'Angle':
436
            if con.FirstPos == 0:
437
                conargs = f'{first}, {sec}, {floatstr(con.Value)}'
438
            elif third[:3] == 'geo':
439
                contype = 'AngleViaPoint'  # compatiblity to FreeCAD 2.20
440
                conargs = f'{first}, {sec}, {third}, {floatstr(con.Value)}'
441
            else:
442
                conargs = f'{first}, {con.FirstPos}, {sec}, {
443
                    con.SecondPos}, {floatstr(con.Value)}'
444

445
        elif con.Type == 'Weight' or con.Type == 'Radius' or con.Type == 'Diameter':
446
            conargs = f'{first}, {floatstr(con.Value)}'
447
            splinecount = 0
448

449
        elif con.Type == 'InternalAlignment':
450
            if con.Second > con.First:
451
                contype = 'InternalAlignment:Sketcher::BSplineControlPoint'
452
                conargs = f'{first}, {con.FirstPos}, {sec}, {splinecount}'
453
                splineindex = con.Second
454
                splinecount = splinecount + 1
455
            else:
456
                conargs = None
457

458
        if conargs is not None:
459
            add_body_line(
460
                f"{objname}.addConstraint(Sketcher.Constraint('{contype}', {conargs}))")
461

462
        if con.Name != "":
463
            add_body_line(f"{objname}.renameConstraint({
464
                          concount}, '{con.Name}')")
465

466
        concount = concount + 1
467

468

469
def addSpreadsheet(obj, objname):
470
    for propname in obj.PropertiesList:
471
        match = re.search('[A-Z]+[0-9]+', propname)
472
        if match:
473
            prop = obj.getPropertyByName(propname)
474
            val = objectToText(prop)
475
            add_body_line("{}.set('{}', {})".format(
476
                objname, propname, val.replace("'", '')))
477
            alias = obj.getAlias(propname)
478
            if alias is not None:
479
                add_body_line(f"{objname}.setAlias('{propname}', '{alias}')")
480

481
    add_body_line('doc.recompute()')
482

483

484
def floatstr(f):
485
    # return '{:.2f}'.format(f)
486
    return str(f)
487

488

489
def vecstr(v):
490
    return f'Vector({floatstr(v.x)}, {floatstr(v.y)}, {floatstr(v.z)})'
491

492

493
class O2PDialog:
494
    """Show a dialog for ObjectsToPython"""
495

496
    def __init__(self):
497
        macro_dir = app.getUserMacroDir(True)
498
        self.ui_file = os.path.join(
499
            macro_dir, 'ObjectsToPython/ObjectsToPython.ui')
500
        self.form = gui.PySideUic.loadUi(self.ui_file)
501
        self.form.pushButtonClose.pressed.connect(self.close_callback)
502
        self.form.show()
503

504
    def close_callback(self):
505
        self.form.close()
506

507

508
dialog = O2PDialog()
509
exportObjectsToPython()
510

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.