├── .github
└── pull_request_template.md
├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.md
├── archCable.py
├── archCableBox.py
├── archCableConnector.py
├── archCableLightPoint.py
├── doc
├── box.png
├── box_max.png
├── cable.png
├── cable_max.png
├── electric_inst.png
└── electric_inst_max.png
├── examples
├── WireFlex examples
│ └── Cables_WireFlex_Example1.FCStd
├── example1.FCStd
├── example2.FCStd
└── example3.FCStd
├── freecad
└── cables
│ ├── __init__.py
│ ├── archCable.py
│ ├── archCableBox.py
│ ├── archCableConnector.py
│ ├── archCableLightPoint.py
│ ├── cableMaterial.py
│ ├── cableProfile.py
│ ├── cableSupport.py
│ ├── cablesCommands.py
│ ├── init_gui.py
│ ├── resources
│ ├── icons
│ │ ├── .gitkeep
│ │ ├── CablesLogo.svg
│ │ ├── classArchCable.svg
│ │ ├── classArchCableBox.svg
│ │ ├── classArchCableConnector.svg
│ │ ├── classArchCableLightPoint.svg
│ │ ├── classWireFlex.svg
│ │ ├── cmdAddVertex.svg
│ │ ├── cmdAttVertex.svg
│ │ ├── cmdDelVertex.svg
│ │ ├── cmdNewCable.svg
│ │ ├── cmdNewCableBox.svg
│ │ ├── cmdNewCableConnector.svg
│ │ ├── cmdNewCableLightPoint.svg
│ │ ├── cmdNewCableMaterial.svg
│ │ ├── cmdNewCableProfile.svg
│ │ ├── cmdNewSupportLine.svg
│ │ ├── cmdNewSupportPoint.svg
│ │ ├── cmdNewWire.svg
│ │ └── cmdRmAttVertex.svg
│ ├── presets
│ │ └── profiles.csv
│ ├── translations
│ │ ├── Cables.ts
│ │ ├── Cables_de.qm
│ │ ├── Cables_de.ts
│ │ ├── Cables_es-AR.qm
│ │ ├── Cables_es-AR.ts
│ │ ├── Cables_es-ES.qm
│ │ ├── Cables_es-ES.ts
│ │ ├── Cables_pl.qm
│ │ ├── Cables_pl.ts
│ │ ├── README.md
│ │ └── updateTranslations.py
│ └── ui
│ │ ├── .gitkeep
│ │ └── profile.ui
│ ├── version.py
│ ├── wireFlex.py
│ └── wireutils.py
├── package.xml
├── setup.py
└── wireFlex.py
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include freecad/cables/resources *
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FreeCAD Cables Workbench
2 |
3 | Electrical cables drawing tools workbench for [FreeCAD](https://freecad.org)
4 |
5 |  
6 | 
7 |
8 | ## The goal and idea
9 | The Cables Workbench has the following goals:
10 |
11 | 1. Creation and use of ready-made elements and cable profiles for modeling electrical installations in architectural design projects
12 | 2. Simplification of cable drawing in architectural models with the possibility of easy changes (like repositioning wall sockets or ligt switches with cables attached to them)
13 | 3. Enable quick and easy connection of single wires if that detail level is needed (e.g. connecting wires to connectors in electrical boxes or swichboards)
14 | 4. To be compatible with [BIM Workbench](https://wiki.freecad.org/BIM_Workbench)
15 |
16 | The main idea of modeling cables (which are flexible in nature) as a static 3D elements is based on Wire Flex objects.
17 | These objects are modified [Draft Wire](https://wiki.freecad.org/Draft_Wire) objects with additional features like the possibility of attaching any Wire Flex vertices to external objects. This allows to automatically change the cable shape and length while changing the placement of external elements like boxes, switches, light points, walls, ceilings etc.
18 |
19 | All elements (cables, boxes, connectors etc.) in this workbench are [Arch Component](https://wiki.freecad.org/Arch_Component) elements. The cable element is based on [Arch Pipe](https://wiki.freecad.org/Arch_Pipe) class.
20 |
21 | ## Wiki
22 | This workbench has a documentation which is a part of FreeCAD Wiki: [Cables Workbench Wiki](https://wiki.freecad.org/Cables_Workbench).
23 |
24 | You can also install the workbench and see [examples](https://github.com/sargo-devel/Cables/tree/master/examples) to have a closer look into details. These files contain Info text with some additional hints.
25 |
26 | ## Installation
27 | This workbench can be installed via the [Addon Manager](https://github.com/FreeCAD/FreeCAD-addons) (for details see [Addon Manager Wiki](https://wiki.freecad.org/Std_AddonMgr)).
28 |
29 | It can also be installed manually by copying its entire `Cables/` folder to the FreeCAD local `Mod/` folder. Details: [Installing more workbenches](https://wiki.freecad.org/Installing_more_workbenches).
30 |
31 | ### Note
32 | This workbench is currently at the alpha stage. You can expect some bugs which can make your model broken.
33 | Some properties of models can change in the future and break models created with current version.
34 |
35 | ### Compatibility
36 | Cables Workbench was created for [FreeCAD](https://freecad.org) version 1.0.0. No compatibility checks with previous versions were made.
37 |
38 | ### Contributions
39 | Any code contributions are welcome. Please add all of your pull requests to the dev branch.
40 |
41 | ### References
42 | * Development repo: https://github.com/sargo-devel/Cables
43 | * FreeCAD Forum announcement/discussion [thread](https://forum.freecad.org/viewtopic.php?t=94090)
44 | * Authors: [@SargoDevel](https://github.com/sargo-devel)
45 |
46 | ### Translations
47 | It is possible to translate Cables Workbench. See [here](https://github.com/sargo-devel/Cables/tree/master/freecad/cables/resources/translations/README.md) for details.
48 |
49 | Available translations:
50 |
51 | * German
52 | * translator: [@maxwxyz](https://github.com/maxwxyz)
53 | * Polish
54 | * translators: @kaktus, @mgr_wojtal, @Piter
55 | * Spanish
56 | * translator: [@hasecilu](https://github.com/hasecilu)
57 |
58 | Big thanks to the translators for their work!
59 |
60 | ### Release notes:
61 | * v0.1.4 15 Mar 2025
62 | * Added translations: German, Polish, Spanish
63 | * ArchCable class: added option to create cable without profile (single wire cable)
64 | * Workbench files structure has been migrated to "namespaced workbench" new style
65 | * Some minor fixes and corrections
66 | * v0.1.3 27 Feb 2025
67 | * Minor fixes and corrections, code prepared for translation
68 | * v0.1.2 25 Jan 2025
69 | * Added profile selection
70 | * v0.1.1 21 Jan 2025
71 | * Fixed cable rotation problem
72 | * v0.1.0 21 Jan 2025
73 | * Initial version
74 |
75 | ## License
76 | LGPLv3 (see [LICENSE](LICENSE))
77 |
--------------------------------------------------------------------------------
/archCable.py:
--------------------------------------------------------------------------------
1 | # old module
2 | from freecad.cables import archCable
3 |
4 | ArchCable = archCable.ArchCable
5 | ViewProviderCable = archCable.ViewProviderCable
6 |
--------------------------------------------------------------------------------
/archCableBox.py:
--------------------------------------------------------------------------------
1 | # old module
2 | from freecad.cables import archCableBox
3 |
4 | ArchCableBox = archCableBox.ArchCableBox
5 | ViewProviderCableBox = archCableBox.ViewProviderCableBox
6 |
--------------------------------------------------------------------------------
/archCableConnector.py:
--------------------------------------------------------------------------------
1 | # old module
2 | from freecad.cables import archCableConnector
3 |
4 | ArchCableConnector = archCableConnector.ArchCableConnector
5 | ViewProviderCableConnector = archCableConnector.ViewProviderCableConnector
6 |
--------------------------------------------------------------------------------
/archCableLightPoint.py:
--------------------------------------------------------------------------------
1 | # old module
2 | from freecad.cables import archCableLightPoint
3 |
4 | ArchCableLightPoint = archCableLightPoint.ArchCableLightPoint
5 | ViewProviderCableLightPoint = archCableLightPoint.ViewProviderCableLightPoint
6 |
--------------------------------------------------------------------------------
/doc/box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/doc/box.png
--------------------------------------------------------------------------------
/doc/box_max.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/doc/box_max.png
--------------------------------------------------------------------------------
/doc/cable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/doc/cable.png
--------------------------------------------------------------------------------
/doc/cable_max.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/doc/cable_max.png
--------------------------------------------------------------------------------
/doc/electric_inst.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/doc/electric_inst.png
--------------------------------------------------------------------------------
/doc/electric_inst_max.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/doc/electric_inst_max.png
--------------------------------------------------------------------------------
/examples/WireFlex examples/Cables_WireFlex_Example1.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/examples/WireFlex examples/Cables_WireFlex_Example1.FCStd
--------------------------------------------------------------------------------
/examples/example1.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/examples/example1.FCStd
--------------------------------------------------------------------------------
/examples/example2.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/examples/example2.FCStd
--------------------------------------------------------------------------------
/examples/example3.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/examples/example3.FCStd
--------------------------------------------------------------------------------
/freecad/cables/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import FreeCAD as App
3 |
4 | translate=App.Qt.translate
5 | QT_TRANSLATE_NOOP=App.Qt.QT_TRANSLATE_NOOP
6 |
7 | _dir = os.path.dirname(__file__)
8 | iconPath = os.path.join(_dir, "resources/icons")
9 | uiPath = os.path.join(_dir, "resources/ui")
10 | presetsPath = os.path.join(_dir, "resources/presets")
11 | languagePath = os.path.join(_dir, "resources", "translations")
12 |
--------------------------------------------------------------------------------
/freecad/cables/archCableBox.py:
--------------------------------------------------------------------------------
1 | """ArchCableBox
2 | """
3 |
4 | import os
5 | from math import pi
6 | import FreeCAD
7 | import ArchComponent
8 | import Part
9 | import DraftVecUtils
10 | from freecad.cables import iconPath
11 | from freecad.cables import translate
12 | from freecad.cables import QT_TRANSLATE_NOOP
13 |
14 |
15 | CLASS_CABLEBOX_ICON = os.path.join(iconPath, "classArchCableBox.svg")
16 |
17 |
18 | class ArchCableBox(ArchComponent.Component):
19 | """The ArchCableBox object
20 | """
21 | def __init__(self, obj):
22 | ArchComponent.Component.__init__(self, obj)
23 | self.setProperties(obj)
24 | from ArchIFC import IfcTypes
25 | if "Cable Fitting" in IfcTypes:
26 | obj.IfcType = "Cable Fitting"
27 | else:
28 | # IFC2x3 does not know a Cable Fitting
29 | obj.IfcType = "Building Element Proxy"
30 | obj.addExtension('Part::AttachExtensionPython')
31 |
32 | def setProperties(self, obj):
33 | pl = obj.PropertiesList
34 | if "Diameter" not in pl:
35 | obj.addProperty("App::PropertyLength", "Diameter", "CableBox",
36 | QT_TRANSLATE_NOOP(
37 | "App::Property", "The inner diameter of " +
38 | "this box"))
39 | if "Thickness" not in pl:
40 | obj.addProperty("App::PropertyLength", "Thickness", "CableBox",
41 | QT_TRANSLATE_NOOP(
42 | "App::Property", "The wall thickness of " +
43 | "this box"))
44 | if "Height" not in pl:
45 | obj.addProperty("App::PropertyLength", "Height", "CableBox",
46 | QT_TRANSLATE_NOOP(
47 | "App::Property", "The inner height of this " +
48 | "box"))
49 | if "BoxBodyHidden" not in pl:
50 | obj.addProperty("App::PropertyBool", "BoxBodyHidden", "CableBox",
51 | QT_TRANSLATE_NOOP(
52 | "App::Property", "Hide the box body to have " +
53 | "better access to helpers"))
54 | if "HelperRingsHidden" not in pl:
55 | obj.addProperty("App::PropertyBool", "HelperRingsHidden",
56 | "CableBoxHelpers",
57 | QT_TRANSLATE_NOOP(
58 | "App::Property", "Hide the helper rings if " +
59 | "they are not needed"))
60 | if "Ring1Diameter" not in pl:
61 | obj.addProperty("App::PropertyLength", "Ring1Diameter",
62 | "CableBoxHelpers",
63 | QT_TRANSLATE_NOOP(
64 | "App::Property", "The diameter of helper " +
65 | "ring 1"))
66 | if "Ring1Height" not in pl:
67 | obj.addProperty("App::PropertyLength", "Ring1Height",
68 | "CableBoxHelpers",
69 | QT_TRANSLATE_NOOP(
70 | "App::Property", "The height below lid of " +
71 | "helper ring 1"))
72 | if "Ring2Diameter" not in pl:
73 | obj.addProperty("App::PropertyLength", "Ring2Diameter",
74 | "CableBoxHelpers",
75 | QT_TRANSLATE_NOOP(
76 | "App::Property", "The diameter of helper " +
77 | "ring 2"))
78 | if "Ring2Height" not in pl:
79 | obj.addProperty("App::PropertyLength", "Ring2Height",
80 | "CableBoxHelpers",
81 | QT_TRANSLATE_NOOP(
82 | "App::Property", "The height below lid of " +
83 | "helper ring 2"))
84 | self.Type = "CableBox"
85 |
86 | def onChanged(self, obj, prop):
87 | pass
88 |
89 | def execute(self, obj):
90 | # FreeCAD.Console.PrintMessage("ArchBox.execute: start\n")
91 | pl = obj.Placement
92 | shapes = []
93 | shapes.append(self.makeSupportLines(obj))
94 | if not obj.HelperRingsHidden:
95 | shapes.append(self.makeHelperRings(obj))
96 | if not obj.BoxBodyHidden:
97 | shapes.append(self.makeBox(obj))
98 | sh = Part.makeCompound(shapes)
99 | obj.Shape = self.processSubShapes(obj, sh, pl)
100 | obj.Placement = pl
101 | # FreeCAD.Console.PrintMessage("ArchBox.execute: end\n")
102 |
103 | def makeSupportLines(self, obj):
104 | # support crosses
105 | x0 = obj.Diameter.Value/2
106 | x = 6
107 | y0 = obj.Diameter.Value/2
108 | y = 6
109 | z0 = -2*obj.Height.Value/3
110 | z = 6
111 | cross1 = [(x0, 0, z0),
112 | (x0, -y, z0),
113 | (x0, y, z0),
114 | (x0, 0, z0+z),
115 | (x0, 0, z0-z)]
116 | cross2 = [(-x0, 0, z0),
117 | (-x0, y, z0),
118 | (-x0, -y, z0),
119 | (-x0, 0, z0+z),
120 | (-x0, 0, z0-z)]
121 | cross3 = [(0, y0, z0),
122 | (x, y0, z0),
123 | (-x, y0, z0),
124 | (0, y0, z0+z),
125 | (0, y0, z0-z)]
126 | cross4 = [(0, -y0, z0),
127 | (-x, -y0, z0),
128 | (x, -y0, z0),
129 | (0, -y0, z0+z),
130 | (0, -y0, z0-z)]
131 | crosses = [cross1, cross2, cross3, cross4]
132 | lines = []
133 | for cross in crosses:
134 | v = []
135 | for c in cross:
136 | v.append(FreeCAD.Vector(*c))
137 | lines.append(Part.LineSegment(v[1], v[0]))
138 | lines.append(Part.LineSegment(v[0], v[2]))
139 | lines.append(Part.LineSegment(v[3], v[0]))
140 | lines.append(Part.LineSegment(v[0], v[4]))
141 | s = Part.Shape(lines)
142 | return s
143 |
144 | def makeHelperRings(self, obj):
145 | # helper rings
146 | lines = []
147 | v1 = FreeCAD.Vector(0, obj.Ring1Diameter.Value/2,
148 | -obj.Ring1Height.Value)
149 | v2 = FreeCAD.Vector(0, obj.Ring2Diameter.Value/2,
150 | -obj.Ring2Height.Value)
151 | vh = [v1, v2]
152 | for nr in range(len(vh)):
153 | v = [vh[nr]]
154 | for i in range(12):
155 | angle = i*2*pi/12
156 | vn = DraftVecUtils.rotate2D(vh[nr], angle)
157 | v.append(vn)
158 | v.append(vh[nr])
159 | poly = Part.makePolygon(v)
160 | lines.append(poly)
161 | s = Part.Shape(lines)
162 | return s
163 |
164 | def makeBox(self, obj):
165 | vc = FreeCAD.Vector(0, 0, 0)
166 | vn = FreeCAD.Vector(0, 0, -1)
167 | ext_diameter = obj.Diameter.Value + 2*obj.Thickness.Value
168 | ext_height = obj.Height.Value + obj.Thickness.Value
169 | box = Part.makeCylinder(ext_diameter/2, ext_height, vc, vn)
170 | inner_box = Part.makeCylinder(ext_diameter/2-obj.Thickness.Value,
171 | ext_height-obj.Thickness.Value, vc, vn)
172 | box = box.cut(inner_box)
173 | vn_list = [FreeCAD.Vector(1, 0, 0), FreeCAD.Vector(-1, 0, 0),
174 | FreeCAD.Vector(0, 1, 0), FreeCAD.Vector(0, -1, 0)]
175 | vc = FreeCAD.Vector(0, 0, -2*obj.Height.Value/3)
176 | hole_radius = 10
177 | hole_hight = ext_diameter/2
178 | for vn in vn_list:
179 | hole = Part.makeCylinder(hole_radius, hole_hight, vc, vn)
180 | box = box.cut(hole)
181 | return box
182 |
183 |
184 | class ViewProviderCableBox(ArchComponent.ViewProviderComponent):
185 | """A View Provider for the ArchCableBox object
186 | """
187 |
188 | def __init__(self, vobj):
189 | ArchComponent.ViewProviderComponent.__init__(self, vobj)
190 |
191 | def getIcon(self):
192 | return CLASS_CABLEBOX_ICON
193 |
194 |
195 | def makeCableBox(baseobj=None, diameter=0, height=0, placement=None,
196 | name=None):
197 | """Creates a cable box object from the given base object
198 | """
199 | if not FreeCAD.ActiveDocument:
200 | FreeCAD.Console.PrintError(translate(
201 | "Cables", "No active document. Aborting") + "\n")
202 | return
203 | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CableBox")
204 | obj.Label = name if name else translate("Cables", "CableBox")
205 | ArchCableBox(obj)
206 | if FreeCAD.GuiUp:
207 | ViewProviderCableBox(obj.ViewObject)
208 | if baseobj:
209 | baseobj.ViewObject.hide()
210 | if baseobj:
211 | obj.Base = baseobj
212 | else:
213 | obj.Thickness = 2
214 | obj.Diameter = diameter if diameter else 60
215 | obj.Height = height if height else 62
216 | # obj.Width = obj.Diameter
217 | obj.Ring1Diameter = 45
218 | obj.Ring1Height = 40
219 | obj.Ring2Diameter = 30
220 | obj.Ring2Height = 20
221 | obj.BoxBodyHidden = False
222 | obj.HelperRingsHidden = False
223 | if placement:
224 | obj.Placement = placement
225 | return obj
226 |
--------------------------------------------------------------------------------
/freecad/cables/archCableConnector.py:
--------------------------------------------------------------------------------
1 | """ArchCableConnector
2 | """
3 |
4 | import os
5 | import math
6 | import FreeCAD
7 | import ArchComponent
8 | import Part
9 | from freecad.cables import iconPath
10 | from freecad.cables import translate
11 | from freecad.cables import QT_TRANSLATE_NOOP
12 |
13 |
14 | CLASS_CABLECONNECTOR_ICON = os.path.join(iconPath,
15 | "classArchCableConnector.svg")
16 |
17 |
18 | class ArchCableConnector(ArchComponent.Component):
19 | """The ArchCableConnector object
20 | """
21 | def __init__(self, obj):
22 | ArchComponent.Component.__init__(self, obj)
23 | self.setProperties(obj)
24 | from ArchIFC import IfcTypes
25 | if "Cable Fitting" in IfcTypes:
26 | obj.IfcType = "Cable Fitting"
27 | else:
28 | # IFC2x3 does not know a Cable Fitting
29 | obj.IfcType = "Building Element Proxy"
30 | obj.addExtension('Part::AttachExtensionPython')
31 |
32 | def setProperties(self, obj):
33 | pl = obj.PropertiesList
34 | if "HoleSize" not in pl:
35 | obj.addProperty("App::PropertyFloat", "HoleSize",
36 | "CableConnector",
37 | QT_TRANSLATE_NOOP(
38 | "App::Property", "The size of single " +
39 | "hole in [mm2]"))
40 | if "Thickness" not in pl:
41 | obj.addProperty("App::PropertyLength", "Thickness",
42 | "CableConnector",
43 | QT_TRANSLATE_NOOP(
44 | "App::Property", "The wall thickness"))
45 | if "Height" not in pl:
46 | obj.addProperty("App::PropertyLength", "Height", "CableConnector",
47 | QT_TRANSLATE_NOOP(
48 | "App::Property", "The height of this " +
49 | "connector"))
50 | if "NumberOfHoles" not in pl:
51 | obj.addProperty("App::PropertyInteger", "NumberOfHoles",
52 | "CableConnector",
53 | QT_TRANSLATE_NOOP(
54 | "App::Property", "The number of holes for " +
55 | "cables"))
56 | self.Type = "CableConnector"
57 |
58 | def onChanged(self, obj, prop):
59 | pass
60 |
61 | def execute(self, obj):
62 | pl = obj.Placement
63 | shapes = []
64 | shapes.append(self.makeSupportPoints(obj))
65 | shapes.append(self.makeBox(obj))
66 | sh = Part.makeCompound(shapes)
67 | obj.Shape = self.processSubShapes(obj, sh, pl)
68 | obj.Placement = pl
69 | # FreeCAD.Console.PrintMessage("ArchCableConnector.execute: end\n")
70 |
71 | def makeSupportPoints(self, obj):
72 | hole_diameter = math.sqrt(obj.HoleSize/math.pi)*2
73 | ln = obj.NumberOfHoles * \
74 | (hole_diameter + obj.Thickness.Value) + \
75 | obj.Thickness.Value
76 | z = 2
77 | y = 0
78 | vertexes1 = []
79 | vertexes2 = []
80 | for i in range(obj.NumberOfHoles):
81 | x = -ln/2 + obj.Thickness.Value + hole_diameter/2 + \
82 | i*(hole_diameter + obj.Thickness.Value)
83 | vertexes1.append(Part.Vertex(x, y, z))
84 | vertexes2.append(Part.Vertex(x, y, -obj.Height.Value-z))
85 | s = Part.Shape(vertexes1 + vertexes2)
86 | return s
87 |
88 | def makeBox(self, obj):
89 | hole_diameter = math.sqrt(obj.HoleSize/math.pi)*2
90 | vc = FreeCAD.Vector(0, 0, 0)
91 | vn = FreeCAD.Vector(0, 0, -1)
92 | ln = obj.NumberOfHoles * \
93 | (hole_diameter + obj.Thickness.Value) + \
94 | obj.Thickness.Value
95 | w = hole_diameter + 2*obj.Thickness.Value
96 | h = obj.Height
97 | vb = vc + FreeCAD.Vector(ln/2, -w/2, 0)
98 | box = Part.makeBox(ln, w, h, vb, vn)
99 | for i in range(obj.NumberOfHoles):
100 | x = -ln/2 + obj.Thickness.Value + hole_diameter/2 + \
101 | i*(hole_diameter + obj.Thickness.Value)
102 | hc = vc + FreeCAD.Vector(x, 0, 0)
103 | hn = vn
104 | hole = Part.makeCylinder(hole_diameter/2, obj.Height, hc, hn)
105 | box = box.cut(hole)
106 | return box
107 |
108 |
109 | class ViewProviderCableConnector(ArchComponent.ViewProviderComponent):
110 | """A View Provider for the ArchCableBox object
111 | """
112 | def __init__(self, vobj):
113 | ArchComponent.ViewProviderComponent.__init__(self, vobj)
114 |
115 | def getIcon(self):
116 | return CLASS_CABLECONNECTOR_ICON
117 |
118 |
119 | def makeCableConnector(baseobj=None, nrofholes=0, holesize=0, thickness=0,
120 | height=0, placement=None, name=None):
121 | """makeCableConnector([baseobj],[nrofholes],[holesize],[thickness],
122 | [height],[placement],[name]):
123 | creates a cable connector object from the given base object
124 | or from scratch"""
125 | if not FreeCAD.ActiveDocument:
126 | FreeCAD.Console.PrintError(translate(
127 | "Cables", "No active document. Aborting") + "\n")
128 | return
129 | return
130 | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",
131 | "CableConnector")
132 | obj.Label = name if name else translate("Cables", "CableConnector")
133 | ArchCableConnector(obj)
134 | if FreeCAD.GuiUp:
135 | ViewProviderCableConnector(obj.ViewObject)
136 | if baseobj:
137 | baseobj.ViewObject.hide()
138 | if baseobj:
139 | obj.Base = baseobj
140 | else:
141 | obj.NumberOfHoles = nrofholes if nrofholes else 3
142 | obj.HoleSize = holesize if holesize else 2.0
143 | obj.Thickness = thickness if thickness else 1.0
144 | obj.Height = height if height else 5.0
145 | if placement:
146 | obj.Placement = placement
147 | return obj
148 |
--------------------------------------------------------------------------------
/freecad/cables/archCableLightPoint.py:
--------------------------------------------------------------------------------
1 | """ArchCableLightPoint
2 | """
3 |
4 | import os
5 | import FreeCAD
6 | import ArchComponent
7 | import Part
8 | from freecad.cables import iconPath
9 | from freecad.cables import translate
10 | from freecad.cables import QT_TRANSLATE_NOOP
11 |
12 |
13 | CLASS_CABLELIGHTPOINT_ICON = os.path.join(iconPath,
14 | "classArchCableLightPoint.svg")
15 |
16 |
17 | class ArchCableLightPoint(ArchComponent.Component):
18 | """The ArchCableLightPoint object
19 | """
20 | def __init__(self, obj):
21 | ArchComponent.Component.__init__(self, obj)
22 | self.setProperties(obj)
23 | from ArchIFC import IfcTypes
24 | if "Cable Fitting" in IfcTypes:
25 | obj.IfcType = "Cable Fitting"
26 | else:
27 | # IFC2x3 does not know a Cable Fitting
28 | obj.IfcType = "Building Element Proxy"
29 | obj.addExtension('Part::AttachExtensionPython')
30 |
31 | def setProperties(self, obj):
32 | pl = obj.PropertiesList
33 | if "Diameter" not in pl:
34 | obj.addProperty("App::PropertyLength", "Diameter",
35 | "CableLightPoint",
36 | QT_TRANSLATE_NOOP(
37 | "App::Property", "The diameter of the light " +
38 | "point fitting"))
39 | if "Thickness" not in pl:
40 | obj.addProperty("App::PropertyLength", "Thickness",
41 | "CableLightPoint",
42 | QT_TRANSLATE_NOOP(
43 | "App::Property", "The wall thickness of the " +
44 | "light point fitting"))
45 | if "Height" not in pl:
46 | obj.addProperty("App::PropertyLength", "Height", "CableLightPoint",
47 | QT_TRANSLATE_NOOP(
48 | "App::Property", "The height of the light " +
49 | "point fitting"))
50 | self.Type = "LightPoint"
51 |
52 | def onChanged(self, obj, prop):
53 | pass
54 |
55 | def execute(self, obj):
56 | pl = obj.Placement
57 | shapes = []
58 | shapes.append(self.makeSupportPoints(obj))
59 | shapes.append(self.makeBox(obj))
60 | sh = Part.makeCompound(shapes)
61 | obj.Shape = self.processSubShapes(obj, sh, pl)
62 | obj.Placement = pl
63 |
64 | def makeSupportPoints(self, obj):
65 | z = 2
66 | y = 0
67 | vertexes1 = []
68 | vertexes2 = []
69 | x = 0
70 | vertexes1.append(Part.Vertex(x, y, z))
71 | vertexes2.append(Part.Vertex(x, y, -obj.Height.Value-z))
72 | s = Part.Shape(vertexes1 + vertexes2)
73 | return s
74 |
75 | def makeBox(self, obj):
76 | vc = FreeCAD.Vector(0, 0, 0)
77 | vn = FreeCAD.Vector(0, 0, -1)
78 | cyl1 = Part.makeCylinder(obj.Diameter/2, obj.Height, vc, vn)
79 | cyl2 = Part.makeCylinder(obj.Diameter/2-obj.Thickness, obj.Height,
80 | vc, vn)
81 | box = cyl1.cut(cyl2)
82 | return box
83 |
84 |
85 | class ViewProviderCableLightPoint(ArchComponent.ViewProviderComponent):
86 | """A View Provider for the ArchCableBox object
87 | """
88 | def __init__(self, vobj):
89 | ArchComponent.ViewProviderComponent.__init__(self, vobj)
90 |
91 | def getIcon(self):
92 | return CLASS_CABLELIGHTPOINT_ICON
93 |
94 |
95 | def makeCableLightPoint(baseobj=None, diameter=0, thickness=0, height=0,
96 | placement=None, name=None):
97 | """creates a cable light point object from the given base object
98 | or from scratch"""
99 | if not FreeCAD.ActiveDocument:
100 | FreeCAD.Console.PrintError(translate(
101 | "Cables", "No active document. Aborting") + "\n")
102 | return
103 | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",
104 | "CableLightPoint")
105 | obj.Label = name if name else translate("Cables", "CableLightPoint")
106 | ArchCableLightPoint(obj)
107 | if FreeCAD.GuiUp:
108 | ViewProviderCableLightPoint(obj.ViewObject)
109 | if baseobj:
110 | baseobj.ViewObject.hide()
111 | if baseobj:
112 | obj.Base = baseobj
113 | else:
114 | obj.Diameter = diameter if diameter else 20
115 | obj.Thickness = thickness if thickness else 2
116 | obj.Height = height if height else 5
117 | if placement:
118 | obj.Placement = placement
119 | return obj
120 |
--------------------------------------------------------------------------------
/freecad/cables/cableMaterial.py:
--------------------------------------------------------------------------------
1 | """cableMaterial
2 | """
3 |
4 | import os
5 | import FreeCAD
6 | import importFCMat
7 | import Arch
8 |
9 | multi_names3 = {'J': 0, 'L1': 2, 'N': 4, 'PE': 5, 'CU': 6}
10 | multi_names5 = {'J': 0, 'L1': 1, 'L2': 2, 'L3': 3, 'N': 4, 'PE': 5, 'CU': 6}
11 | multi_list = {'cableMultiMat3w': multi_names3, 'cableMultiMat5w': multi_names5}
12 | mat_table = [
13 | {'name': 'InsulJacket',
14 | 'mat_name': 'PVC-Generic',
15 | 'color': (215, 251, 255)
16 | },
17 | {'name': 'InsulBrown',
18 | 'mat_name': 'PVC-Generic',
19 | 'color': (80, 40, 0)
20 | },
21 | {'name': 'InsulBlack',
22 | 'mat_name': 'PVC-Generic',
23 | 'color': (54, 54, 54)
24 | },
25 | {'name': 'InsulGray',
26 | 'mat_name': 'PVC-Generic',
27 | 'color': (160, 160, 160)
28 | },
29 | {'name': 'InsulBlue',
30 | 'mat_name': 'PVC-Generic',
31 | 'color': (0, 85, 255)
32 | },
33 | {'name': 'InsulYellow',
34 | 'mat_name': 'PVC-Generic',
35 | 'color': (255, 255, 0)
36 | },
37 | {'name': 'Cu',
38 | 'mat_name': 'Copper-Generic',
39 | 'color': (184, 115, 51)
40 | }
41 | ]
42 |
43 |
44 | def get_material_from_lib(name):
45 | path = os.path.join(FreeCAD.getResourceDir(), "Mod", "Material")
46 | result = []
47 | for current_folder, subfolders, files in os.walk(path):
48 | for f in files:
49 | if f.endswith('.FCMat') or f.endswith('.FCMAT'):
50 | if name in f:
51 | result.append(current_folder+os.sep+f)
52 | return result
53 |
54 |
55 | def makeMaterial(name, mat_name, color):
56 | result = get_material_from_lib(mat_name)
57 | mat = importFCMat.read(result[0]) if result else None
58 | material = Arch.makeMaterial()
59 | if mat:
60 | material.Material = mat
61 | material.Label = name
62 | material.Color = color
63 | material.Transparency = 0
64 | return material
65 |
66 |
67 | def makeCableMaterials(table=mat_table, multi_list=multi_list):
68 | materials = []
69 | for m in table:
70 | materials.append(makeMaterial(m['name'], m['mat_name'], m['color']))
71 | multi_names = multi_names3
72 | for multi_names in multi_list.items():
73 | multi = Arch.makeMultiMaterial()
74 | lmat = []
75 | for i in multi_names[1].values():
76 | lmat.append(materials[i])
77 | multi.Materials = lmat
78 | multi.Names = list(multi_names[1].keys())
79 | thickness = [0.0] * len(multi.Names)
80 | multi.Thicknesses = thickness
81 | multi.Label = multi_names[0]
82 | return materials
83 |
--------------------------------------------------------------------------------
/freecad/cables/cableProfile.py:
--------------------------------------------------------------------------------
1 | """cableProfile
2 | """
3 |
4 | import os
5 | import csv
6 | import math
7 | import FreeCAD
8 | import FreeCADGui
9 | import Part
10 | import Sketcher
11 | import ProfileLib.RegularPolygon
12 | from freecad.cables import uiPath, presetsPath
13 |
14 |
15 | translate = FreeCAD.Qt.translate
16 | ui_profile = os.path.join(uiPath, "profile.ui")
17 | gauge_mm2_list = ['custom', '0.2', '0.5', '0.75', '1', '1.5', '2.5', '4', '6',
18 | '10', '16', '25', '35', '50', '70', '95', '120']
19 |
20 | # Presets in the form: Name, Profile class, Voltage Class, [profile data]
21 | # Search for profiles.csv in presets and in the user path
22 | profilefiles = [os.path.join(presetsPath, "profiles.csv"),
23 | os.path.join(FreeCAD.getUserAppDataDir(), "Cables",
24 | "profiles.csv")]
25 |
26 |
27 | class TaskPanelProfile:
28 | def __init__(self, gauge_list=gauge_mm2_list):
29 | self.form = FreeCADGui.PySideUic.loadUi(ui_profile)
30 | self.presets = readCablePresets()
31 | profile_list = [i[1]+'_'+i[3] for i in self.presets]
32 | self.form.comboProfile.addItems(profile_list)
33 | self.form.comboWireGauge.addItems(gauge_list)
34 |
35 | def accept(self):
36 | idx = self.form.comboProfile.currentIndex()
37 | profile = self.presets[idx]
38 | nr_of_wires = self.form.NumberOfWires.value()
39 | try:
40 | wire_gauge_mm2 = float(
41 | self.form.comboWireGauge.currentText())
42 | except ValueError:
43 | wire_gauge_mm2 = self.form.customWireGauge.value()
44 | makeCableProfile(profile, nr_of_wires, wire_gauge_mm2)
45 | FreeCADGui.Control.closeDialog()
46 |
47 |
48 | def makeCableProfile(profile=[1, 'YDYp', 'F', '750V', 1.45, 0.7, 0.1],
49 | nr_of_wires=3, wire_gauge_mm2=1.5):
50 | """Makes cable profile
51 | profile=[idx,name,profile_class,voltage_class,jacket_thickness,
52 | single_insulation_thickness,insul_dist]
53 | """
54 | label = f"{profile[1]}{nr_of_wires}x{wire_gauge_mm2}_{profile[3]}"
55 | # FreeCAD.Console.PrintMessage(f"Label: {label}\n")
56 | if nr_of_wires < 1 or wire_gauge_mm2 == 0:
57 | FreeCAD.Console.PrintError(translate(
58 | "Cables", "Cable needs to have number of wires > 0 and nonzero " +
59 | "wire gauge") + "\n")
60 | return None
61 | if profile[2] == 'F':
62 | makeCableProfileF(label, profile[4:], nr_of_wires, wire_gauge_mm2)
63 | if profile[2] == 'R':
64 | makeCableProfileR(label, profile[4:], nr_of_wires, wire_gauge_mm2)
65 |
66 |
67 | def makeCableProfileF(label, insul=[1.45, 0.7, 0.1], nr_of_wires=3,
68 | wire_gauge_mm2=1.5):
69 | """Profile for flat cable
70 | insul=[jacket_thickness,single_insulation_thickness,insul_dist]
71 | """
72 | if nr_of_wires < 2:
73 | FreeCAD.Console.PrintError(translate(
74 | "Cables", "Flat cable needs to have at least 2 wires")
75 | + "\n")
76 | return None
77 | profile = FreeCAD.activeDocument().addObject('Sketcher::SketchObject',
78 | 'Sketch')
79 | profile.Placement = FreeCAD.Placement(FreeCAD.Vector(0, 0, 0),
80 | FreeCAD.Rotation(0, 0, 0, 1))
81 | profile.MapMode = "Deactivated"
82 | profile.Label = label
83 | App = FreeCAD
84 | v1 = App.Vector(-20, 10, 0)
85 | v2 = App.Vector(20, 10, 0)
86 | v3 = App.Vector(20, -10, 0)
87 | v4 = App.Vector(-20, -10, 0)
88 | vc1 = App.Vector(-30, 0, 0)
89 | vc2 = App.Vector(30, 0, 0)
90 |
91 | # Create Wire0 (cable jacket)
92 | geoList = []
93 | geoList.append(Part.LineSegment(v1, v2))
94 | geoList.append(Part.Arc(v2, vc2, v3))
95 | geoList.append(Part.LineSegment(v3, v4))
96 | geoList.append(Part.Arc(v4, vc1, v1))
97 | profile.addGeometry(geoList, False)
98 | constraintList = []
99 | constraintList.append(Sketcher.Constraint('Tangent', 0, 2, 1, 2))
100 | constraintList.append(Sketcher.Constraint('Tangent', 1, 1, 2, 1))
101 | constraintList.append(Sketcher.Constraint('Tangent', 2, 2, 3, 2))
102 | constraintList.append(Sketcher.Constraint('Tangent', 3, 1, 0, 1))
103 | constraintList.append(Sketcher.Constraint('Horizontal', 0))
104 | constraintList.append(Sketcher.Constraint('Equal', 1, 3))
105 | constraintList.append(Sketcher.Constraint('Symmetric', 3, 3, 1, 3, -1, 1))
106 | profile.addConstraint(constraintList)
107 | # Create inner wires
108 | norm = App.Vector(0, 0, 1)
109 | wo, wi, p, nr = createProfileSubWires(profile, norm, nr_of_wires, 4)
110 |
111 | # Calculate base dimensions
112 | wire_diameter = math.sqrt(wire_gauge_mm2/math.pi)*2
113 | # distance between centers of adjacent wires:
114 | p_dist = wire_diameter + 2*insul[1] + insul[2]
115 | # x position of 1st point:
116 | p1_xpos = -(nr_of_wires-1)*p_dist/2
117 | # single insulation diameter (outer ring):
118 | wo_d = wire_diameter + 2*insul[1]
119 | # wire diameter (inner ring):
120 | wi_d = wire_diameter
121 | # jacket arc diameter:
122 | j_d = wo_d + 2*insul[0]
123 | # FreeCAD.Console.PrintMessage(f"nr,wo,wi,p({nr},{wo},{wi},{p})\n")
124 | constraintList = []
125 | # 1st point placement:
126 | constraintList.append(
127 | Sketcher.Constraint('DistanceX', p, 1, -1, 1, -p1_xpos))
128 | constraintList.append(Sketcher.Constraint('Diameter', 3, j_d))
129 | constraintList.append(Sketcher.Constraint('Diameter', wo, wo_d))
130 | constraintList.append(Sketcher.Constraint('Diameter', wi, wi_d))
131 | for i in range(nr-1):
132 | # nth point distance:
133 | constraintList.append(
134 | Sketcher.Constraint('DistanceX', p+i, 1, p+i+1, 1, p_dist))
135 | # points on x axis (except 1st to avoid redundancy later):
136 | constraintList.append(
137 | Sketcher.Constraint('PointOnObject', p+i+1, 1, -1))
138 | # insulations equality:
139 | constraintList.append(Sketcher.Constraint('Equal', wo, wo+i+1))
140 | # wires equality:
141 | constraintList.append(Sketcher.Constraint('Equal', wi, wi+i+1))
142 | for i in range(nr):
143 | # insulation (outer ring) on point:
144 | constraintList.append(
145 | Sketcher.Constraint('Coincident', p+i, 1, wo+i, 3))
146 | # wire (inner ring) on point:
147 | constraintList.append(
148 | Sketcher.Constraint('Coincident', p+i, 1, wi+i, 3))
149 | # jacket on 1st point (1st point is not on x-axis to avoid redundancy):
150 | constraintList.append(Sketcher.Constraint('Coincident', p, 1, 3, 3))
151 | profile.addConstraint(constraintList)
152 | return profile
153 |
154 |
155 | def makeCableProfileR(label, insul=[1.45, 0.7, 0.1], nr_of_wires=3,
156 | wire_gauge_mm2=1.5):
157 | """Profile for round cable
158 | insul=[jacket_thickness,single_insulation_thickness,insul_dist]
159 | """
160 | if nr_of_wires < 1:
161 | FreeCAD.Console.PrintError(translate(
162 | "Cables", "Round cable needs to have at least 1 wire")
163 | + "\n")
164 | return None
165 | profile = FreeCAD.activeDocument().addObject('Sketcher::SketchObject',
166 | 'Sketch')
167 | profile.Placement = FreeCAD.Placement(FreeCAD.Vector(0, 0, 0),
168 | FreeCAD.Rotation(0, 0, 0, 1))
169 | profile.MapMode = "Deactivated"
170 | profile.Label = label
171 | App = FreeCAD
172 | # Create Wire0 (cable jacket)
173 | norm = App.Vector(0, 0, 1)
174 | geoList = []
175 | geoList.append(Part.Circle(App.Vector(0, 0, 0), norm, 30))
176 | profile.addGeometry(geoList, False)
177 | constraintList = []
178 | constraintList.append(Sketcher.Constraint('Coincident', 0, 3, -1, 1))
179 | profile.addConstraint(constraintList)
180 | # Create inner wires
181 | wo, wi, p, nr = createProfileSubWires(profile, norm, nr_of_wires, 1)
182 |
183 | # Calculate base dimensions
184 | wire_diameter = math.sqrt(wire_gauge_mm2/math.pi)*2
185 | # distance between centers of adjacent wires:
186 | p_dist = wire_diameter + 2*insul[1] + insul[2]
187 | # y position of 1st point (radius of construction circle):
188 | if nr > 2:
189 | p1_ypos = p_dist/(2*math.sin(math.pi/nr))
190 | elif nr == 2:
191 | p1_ypos = 0.5*wire_diameter + insul[1] + 0.5*insul[2]
192 | elif nr == 1:
193 | p1_ypos = 0
194 | # single insulation diameter (outer ring):
195 | wo_d = wire_diameter + 2*insul[1]
196 | # wire diameter (inner ring):
197 | wi_d = wire_diameter
198 | # jacket ring diameter:
199 | j_d = 2*p1_ypos + wo_d + 2*insul[0]
200 |
201 | # Create construction polygon
202 | cl = p + nr # number of 1st polygon line
203 | cc = cl + nr # number of polygon circle
204 | if nr > 2:
205 | ProfileLib.RegularPolygon.makeRegularPolygon(
206 | profile, nr, App.Vector(0, 0, 0), App.Vector(0, p1_ypos, 0), True)
207 | elif nr == 2:
208 | geoList = []
209 | v1 = App.Vector(0, p1_ypos, 0)
210 | v2 = App.Vector(0, -p1_ypos, 0)
211 | v0 = App.Vector(0, 0, 0)
212 | geoList.append(Part.LineSegment(v1, v0))
213 | geoList.append(Part.LineSegment(v2, v0))
214 | profile.addGeometry(geoList, True)
215 | elif nr == 1:
216 | geoList = []
217 | geoList.append(Part.Point(App.Vector(0, 0, 0)))
218 | profile.addGeometry(geoList, True)
219 |
220 | constraintList = []
221 | if nr > 2:
222 | constraintList.append(Sketcher.Constraint('Coincident', -1, 1, cc, 3))
223 | elif nr == 2:
224 | constraintList.append(
225 | Sketcher.Constraint('PointOnObject', cl+1, 1, -2))
226 | constraintList.append(
227 | Sketcher.Constraint('DistanceY', cl+1, 1, -1, 1, p1_ypos))
228 | constraintList.append(
229 | Sketcher.Constraint('Coincident', -1, 1, cl, 2))
230 | constraintList.append(
231 | Sketcher.Constraint('Coincident', -1, 1, cl+1, 2))
232 | elif nr == 1:
233 | constraintList.append(Sketcher.Constraint('Coincident', -1, 1, cl, 1))
234 | constraintList.append(Sketcher.Constraint('Diameter', 0, j_d))
235 | constraintList.append(Sketcher.Constraint('Diameter', wo, wo_d))
236 | constraintList.append(Sketcher.Constraint('Diameter', wi, wi_d))
237 | if nr > 1:
238 | constraintList.append(
239 | Sketcher.Constraint('DistanceY', -1, 1, cl, 1, p1_ypos))
240 | constraintList.append(Sketcher.Constraint('PointOnObject', cl, 1, -2))
241 | for i in range(nr-1):
242 | # insulations equality:
243 | constraintList.append(Sketcher.Constraint('Equal', wo, wo+i+1))
244 | # wires equality:
245 | constraintList.append(Sketcher.Constraint('Equal', wi, wi+i+1))
246 | for i in range(nr):
247 | # insulation (outer ring) on point:
248 | constraintList.append(
249 | Sketcher.Constraint('Coincident', p+i, 1, wo+i, 3))
250 | # wire (inner ring) on point:
251 | constraintList.append(
252 | Sketcher.Constraint('Coincident', p+i, 1, wi+i, 3))
253 | # point on construction vertex:
254 | constraintList.append(
255 | Sketcher.Constraint('Coincident', cl+i, 1, p+i, 1))
256 | profile.addConstraint(constraintList)
257 | return profile
258 |
259 |
260 | def createProfileSubWires(profile, norm, nr_of_wires, wo_nr):
261 | App = FreeCAD
262 | # Create Wire1, Wire2, Wire3, ... WireNr (single insulations)
263 | nr = nr_of_wires
264 | wo = wo_nr # number of 1st single wire insulation (outer ring)
265 | geoList = []
266 | for i in range(nr):
267 | geoList.append(Part.Circle(App.Vector(i, 0, 0), norm, 2))
268 | profile.addGeometry(geoList, False)
269 |
270 | # Create WireNr+1, WireNr+2, WireNr+3, ... WireNr+Nr (single wire)
271 | wi = wo + nr # number of 1st single wire (inner ring)
272 | geoList = []
273 | for i in range(nr):
274 | geoList.append(Part.Circle(App.Vector(i, 0, 0), norm, 1))
275 | profile.addGeometry(geoList, False)
276 |
277 | # Create Point1, Point2, Point3, PointNr (centers of single wires)
278 | p = wi + nr # number of 1st point
279 | geoList = []
280 | for i in range(nr):
281 | geoList.append(Part.Point(App.Vector(i, 0, 0)))
282 | profile.addGeometry(geoList, False)
283 | return wo, wi, p, nr
284 |
285 |
286 | # function copied from archProfile.py
287 | def readCablePresets(pfiles=profilefiles):
288 | Presets = []
289 | bid = 1 # Unique index
290 | for profilefile in pfiles:
291 | if os.path.exists(profilefile):
292 | try:
293 | with open(profilefile, "r") as csvfile:
294 | beamreader = csv.reader(csvfile)
295 | for row in beamreader:
296 | if (not row) or row[0].startswith("#"):
297 | continue
298 | try:
299 | r = [bid, row[0], row[1], row[2]]
300 | for i in range(3, len(row)):
301 | r = r + [float(row[i])]
302 | if r not in Presets:
303 | Presets.append(r)
304 | bid = bid + 1
305 | except ValueError:
306 | FreeCAD.Console.PrintError(translate(
307 | "Cables", "Skipping bad line:")
308 | + " " + str(row) + "\n")
309 | except IOError:
310 | FreeCAD.Console.PrintError(translate(
311 | "Cables", "Could not open"), profilefile, "\n")
312 | return Presets
313 |
--------------------------------------------------------------------------------
/freecad/cables/cableSupport.py:
--------------------------------------------------------------------------------
1 | """cableSupport
2 | """
3 |
4 | import FreeCAD
5 | import Draft
6 | from freecad.cables import translate
7 |
8 |
9 | def makeSupportPoint(placement=None, name=None):
10 | """creates a support point
11 | """
12 | if not FreeCAD.ActiveDocument:
13 | FreeCAD.Console.PrintError(translate(
14 | "Cables", "No active document. Aborting") + "\n")
15 | return
16 | if placement:
17 | point = Draft.make_point(placement.Base)
18 | else:
19 | point = Draft.make_point(0.0, 0.0, 0.0)
20 | point.Label = name if name else translate("Cables", "SupportPoint")
21 | point.ViewObject.PointSize = 8
22 | point.ViewObject.PointColor = (255, 85, 0)
23 | Draft.autogroup(point)
24 | point.addExtension('Part::AttachExtensionPython')
25 | return point
26 |
27 |
28 | def makeSupportLine(p1=None, p2=None, name=None):
29 | """creates a support line
30 | """
31 | if not FreeCAD.ActiveDocument:
32 | FreeCAD.Console.PrintError(translate(
33 | "Cables", "No active document. Aborting") + "\n")
34 | return
35 | pl = FreeCAD.Placement()
36 | dist = 30.0
37 | if p1 and p2:
38 | pl.Base = (p1+p2)/2
39 | elif p1:
40 | center = p1
41 | p1 = center + FreeCAD.Vector(-dist, 0.0, 0.0)
42 | p2 = center + FreeCAD.Vector(dist, 0.0, 0.0)
43 | pl.Base = center
44 | else:
45 | p1 = FreeCAD.Vector(-dist, 0.0, 0.0)
46 | p2 = FreeCAD.Vector(dist, 0.0, 0.0)
47 | pl.Base = (p1+p2)/2
48 | line = Draft.make_wire([p1, p2], placement=pl, closed=False,
49 | face=False, support=None)
50 | line.Label = name if name else translate("Cables", "SupportLine")
51 | #line.addExtension('Part::AttachExtensionPython')
52 | line.Subdivisions = 1
53 | line.ViewObject.PointSize = 8
54 | line.ViewObject.PointColor = (255, 85, 0)
55 | line.ViewObject.LineColor = (255, 170, 127)
56 | Draft.autogroup(line)
57 | return line
58 |
--------------------------------------------------------------------------------
/freecad/cables/init_gui.py:
--------------------------------------------------------------------------------
1 | # ***************************************************************************
2 | # * Copyright 2024 SargoDevel *
3 | # * *
4 | # * This program is free software; you can redistribute it and/or modify *
5 | # * it under the terms of the GNU Lesser General Public License (LGPL) *
6 | # * as published by the Free Software Foundation; either version 2 of *
7 | # * the License, or (at your option) any later version. *
8 | # * for detail see the LICENSE text file. *
9 | # * *
10 | # * This program is distributed in the hope that it will be useful, *
11 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 | # * GNU Lesser General Public License for more details. *
14 | # * *
15 | # * You should have received a copy of the GNU Library General Public *
16 | # * License along with this program; if not, write to the Free Software *
17 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
18 | # * USA *
19 | # * *
20 | # ***************************************************************************/
21 |
22 | import os
23 | from draftguitools import gui_grid
24 | from draftguitools import gui_selectplane
25 | import FreeCADGui as Gui
26 | from freecad.cables import QT_TRANSLATE_NOOP
27 | from freecad.cables import translate
28 |
29 | # Add translations path
30 | from freecad.cables import languagePath
31 | Gui.addLanguagePath(languagePath)
32 | Gui.updateLocale()
33 |
34 |
35 | class CablesWorkbench (Gui.Workbench):
36 |
37 | MenuText = translate("Workbench", "Cables")
38 | ToolTip = translate("Workbench", "Create cable connections")
39 | from freecad.cables import iconPath
40 | Icon = os.path.join(iconPath, "CablesLogo.svg")
41 |
42 | def Initialize(self):
43 | """This function is executed when the workbench is first activated.
44 | It is executed once in a FreeCAD sessionfollowed by the Activated
45 | function.
46 | """
47 | from freecad.cables import cablesCommands
48 |
49 | self.list_wires = ["Cables_WireFlex",
50 | "Cables_AddVertex",
51 | "Cables_DelVertex",
52 | "Cables_AttachVertex",
53 | "Cables_RemoveVertexAttachment"]
54 | self.list_cables = ["Cables_Profile",
55 | "Cables_Cable",
56 | "Cables_CableBox",
57 | "Cables_CableConnector",
58 | "Cables_CableLightPoint",
59 | "Cables_Material"]
60 | self.list_support = ["Cables_SupportPoint",
61 | "Cables_SupportLine"]
62 | self.list_draft = ["Draft_ToggleGrid",
63 | # "Draft_SelectPlane"]
64 | ]
65 | self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Cable Wires"),
66 | self.list_wires)
67 | self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Cables"),
68 | self.list_cables)
69 | self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Cable Support"),
70 | self.list_support)
71 | self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft Tools"),
72 | self.list_draft)
73 | self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "Cable Wires"),
74 | self.list_wires)
75 | self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "Cables"),
76 | self.list_cables)
77 | self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "Cable Support"),
78 | self.list_support)
79 | # self.appendMenu(["An existing Menu", "My submenu"], self.list) # appends a submenu to an existing menu
80 |
81 | def Activated(self):
82 | """This function is executed whenever the workbench is activated"""
83 | import WorkingPlane
84 | WorkingPlane.get_working_plane()
85 | return
86 |
87 | def Deactivated(self):
88 | """This function is executed whenever the workbench is deactivated"""
89 | return
90 |
91 | def ContextMenu(self, recipient):
92 | """This function is executed whenever the user right-clicks on screen
93 | """
94 | # "recipient" will be either "view" or "tree"
95 | self.appendContextMenu(QT_TRANSLATE_NOOP("Workbench", "Cable Wires"),
96 | self.list_wires)
97 | self.appendContextMenu(QT_TRANSLATE_NOOP("Workbench", "Cables"),
98 | self.list_cables)
99 | self.appendContextMenu(QT_TRANSLATE_NOOP("Workbench", "Cable Support"),
100 | self.list_support)
101 |
102 | def GetClassName(self):
103 | # This function is mandatory if this is a full Python workbench
104 | # This is not a template,
105 | # the returned string should be exactly "Gui::PythonWorkbench"
106 | return "Gui::PythonWorkbench"
107 |
108 |
109 | Gui.addWorkbench(CablesWorkbench())
110 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/freecad/cables/resources/icons/.gitkeep
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/CablesLogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
149 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/classArchCable.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
166 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/classArchCableBox.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
236 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/classArchCableConnector.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
303 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/classArchCableLightPoint.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
241 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/classWireFlex.svg:
--------------------------------------------------------------------------------
1 |
2 |
129 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdAddVertex.svg:
--------------------------------------------------------------------------------
1 |
2 |
167 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdAttVertex.svg:
--------------------------------------------------------------------------------
1 |
2 |
157 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdDelVertex.svg:
--------------------------------------------------------------------------------
1 |
2 |
158 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewCableBox.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
212 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewCableConnector.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
232 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewCableLightPoint.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
219 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewCableMaterial.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
333 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewCableProfile.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
125 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewSupportLine.svg:
--------------------------------------------------------------------------------
1 |
2 |
140 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewSupportPoint.svg:
--------------------------------------------------------------------------------
1 |
2 |
96 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdNewWire.svg:
--------------------------------------------------------------------------------
1 |
2 |
179 |
--------------------------------------------------------------------------------
/freecad/cables/resources/icons/cmdRmAttVertex.svg:
--------------------------------------------------------------------------------
1 |
2 |
167 |
--------------------------------------------------------------------------------
/freecad/cables/resources/presets/profiles.csv:
--------------------------------------------------------------------------------
1 | # Data structure:
2 |
3 | # Name, Profile class, Voltage Class, geometric data...
4 |
5 | # All measures must be in millimeters
6 |
7 | # Possible profile classes are:
8 |
9 | # R= Round cable
10 | # F= Flat cable
11 |
12 | # Additional profile types must be implemented in cableProfile.py first
13 |
14 | ###########################################
15 |
16 | # F-profile
17 | # Name,F,Voltage Class,jacket_thickness,single_insulation_thickness,insul_dist
18 |
19 | YDYp,F,750V,1.45,0.7,0.1
20 |
21 | # R-profile
22 | # Name,R,Voltage Class,jacket_thickness,single_insulation_thickness,insul_dist
23 |
24 | YDY,R,750V,1.45,0.7,0.1
25 | UTP5e,R,LAN,0.7,0.3,0.05
26 |
--------------------------------------------------------------------------------
/freecad/cables/resources/translations/Cables_de.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/freecad/cables/resources/translations/Cables_de.qm
--------------------------------------------------------------------------------
/freecad/cables/resources/translations/Cables_es-AR.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/freecad/cables/resources/translations/Cables_es-AR.qm
--------------------------------------------------------------------------------
/freecad/cables/resources/translations/Cables_es-ES.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/freecad/cables/resources/translations/Cables_es-ES.qm
--------------------------------------------------------------------------------
/freecad/cables/resources/translations/Cables_pl.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/freecad/cables/resources/translations/Cables_pl.qm
--------------------------------------------------------------------------------
/freecad/cables/resources/translations/README.md:
--------------------------------------------------------------------------------
1 | # About translating Cables Workbench
2 |
3 | ## Translators:
4 |
5 | Translations for this workbench is done by visiting the **FreeCAD-addons**
6 | project on CrowdIn platform at webpage,
7 | then find your language, look for the **Cables** project and do the translation.
8 |
9 | ## Maintainers:
10 |
11 | > [!NOTE]
12 | > All commands **must** be run in `./resources/translations` directory.
13 |
14 | ### Updating translations template file
15 |
16 | To update the template file from source files you should use this command:
17 |
18 | ```shell
19 | ./updateTranslations.py updatets
20 | ```
21 |
22 | ### Sending translation template file to crowdin
23 |
24 | To send the template ts file to crowdin use this command:
25 |
26 | ```shell
27 | ./updateTranslations.py upload
28 | ```
29 |
30 | ### Building zip file on crowdin
31 |
32 | To build a ready-for-download zip file on crowdin use this command:
33 |
34 | ```shell
35 | ./updateTranslations.py build
36 | ```
37 |
38 | ### Download zip file from crowdin
39 |
40 | To download zip file created in previous step use this command:
41 |
42 | ```shell
43 | ./updateTranslations.py download
44 | ```
45 |
46 | ### Installing translations files
47 |
48 | To unzip downloaded translations from crowdin and to create corresponding qm files use this command:
49 |
50 | ```shell
51 | ./updateTranslations.py install
52 | ```
53 |
54 | Use this only for tests:
55 |
56 | ```shell
57 | lrelease Cables_*.ts
58 | ```
59 |
--------------------------------------------------------------------------------
/freecad/cables/resources/translations/updateTranslations.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | # ***************************************************************************
4 | # * *
5 | # * Copyright (c) 2021 Yorik van Havre *
6 | # * *
7 | # * This program is free software; you can redistribute it and/or modify *
8 | # * it under the terms of the GNU Library General Public License (LGPL) *
9 | # * as published by the Free Software Foundation; either version 2 of *
10 | # * the License, or (at your option) any later version. *
11 | # * for detail see the LICENCE text file. *
12 | # * *
13 | # * This program is distributed in the hope that it will be useful, *
14 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 | # * GNU Library General Public License for more details. *
17 | # * *
18 | # * You should have received a copy of the GNU Library General Public *
19 | # * License along with this program; if not, write to the Free Software *
20 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21 | # * USA *
22 | # * *
23 | # ***************************************************************************
24 |
25 | """
26 | This script allows you to manage the translations
27 | of your workbench with crowdin.
28 |
29 | USAGE:
30 |
31 | updateTranslations [command]
32 |
33 | Possible commands are:
34 |
35 | * updatets: Updates the ts files found in your WB
36 | * upload: Sends the ts file to crowdin
37 | * build: Creates a ready-for-download zip file on crowdin
38 | * download: Download the zip file created in previous step
39 | * install: Unzips the above file and creates corresponding qm files
40 |
41 | Prior to using this script, you must have been made an manager on crowdin
42 | freecad-addons project (ask Yorik), and you must have added your secret key
43 | in a simple text file named ~/.crowdin-freecadaddons
44 | You must also adapt the config values below. There is nothing else to do if
45 | your workbench includes only .py and .ui files. They will all be gathered
46 | automatically under one single ts file by this script.
47 |
48 | """
49 |
50 | from __future__ import print_function
51 |
52 | import sys
53 | import os
54 | import xml.sax
55 | import pycurl
56 | import io
57 | import tempfile
58 | import shutil
59 | import zipfile
60 | from pathlib import Path
61 |
62 |
63 | ### CONFIGURATION
64 |
65 | # adapt the following values to your workbench
66 |
67 | # the name of your workench, and also the .ts file. Ex: "BIM" means the ts file is BIM.ts
68 | MODULENAME = "Cables"
69 |
70 | # the account name on crowdin
71 | USERNAME = "SargoDevel"
72 |
73 | # The base path of your module, relative to this file
74 | BASEPATH = "../.."
75 |
76 | # the path to the translations files location, inside your module folder
77 | TRANSLATIONSPATH = "resources/translations"
78 |
79 | # the list of languages to install
80 | # LANGUAGES = None to use all found in the zip file
81 | LANGUAGES = "af ar ca cs de el es-ES eu fi fil fr gl hr hu id it ja kab ko lt nl no pl pt-BR pt-PT ro ru sk sl sr sv-SE tr uk val-ES vi zh-CN zh-TW"
82 |
83 | # List of supported languages FreeCADGui.supportedLocales().values()
84 | LANG = ['en', 'af', 'ar', 'eu', 'be', 'bg', 'ca', 'zh-CN', 'zh-TW', 'hr', 'cs', 'da', 'nl', 'fil', 'fi', 'fr', 'gl', 'ka', 'de', 'el', 'hu', 'id', 'it', 'ja', 'kab', 'ko', 'lt', 'no', 'pl', 'pt-PT', 'pt-BR', 'ro', 'ru', 'sr', 'sr-CS', 'sk', 'sl', 'es-ES', 'es-AR', 'sv-SE', 'tr', 'uk', 'val-ES', 'vi']
85 |
86 |
87 | # pylupdate util to use (pylupdae5, pyside2-lupdate,...)
88 | ##PYLUPDATE = "pyside2-lupdate -verbose"
89 | PYLUPDATE = "lupdate"
90 |
91 | ### END CONFIGURATION
92 |
93 |
94 | class ResponseHandler(xml.sax.ContentHandler):
95 | "handler for the command responses"
96 |
97 | def __init__(self):
98 | self.current = ""
99 | self.data = ""
100 | self.translated = 1
101 | self.total = 1
102 |
103 | def startElement(self, tag, attributes):
104 | self.current = tag
105 | if tag == "file":
106 | self.data += attributes["status"]
107 | elif tag == "error":
108 | self.data == "Error: "
109 |
110 | def endElement(self, tag):
111 | if self.current in ["language", "success", "error"]:
112 | self.data = ""
113 | self.translated = 1
114 | self.total = 1
115 | self.current = ""
116 |
117 | def characters(self, content):
118 | if self.current == "name":
119 | self.data += content
120 | elif self.current == "phrases":
121 | self.total = int(content)
122 | elif self.current == "translated":
123 | self.translated = int(content)
124 | pc = int((float(self.translated) / self.total) * 100)
125 | self.data += " : " + str(pc) + "%\n"
126 | elif self.current == "message":
127 | self.data += content
128 |
129 |
130 | def doLanguage(tempfolder, translationsfolder, lncode):
131 | "copies and compiles a single ts file"
132 |
133 | if lncode == "en":
134 | # never treat "english" translation... For now :)
135 | return
136 | p = Path(tempfolder)
137 | ts_list = list(p.glob('*.ts'))
138 | tsfilepath = None
139 | for f in ts_list:
140 | if lncode in f.stem.split('_')[1]:
141 | tsfilepath = f.as_posix()
142 | break
143 | if not tsfilepath:
144 | return
145 | newtspath = os.path.join(translationsfolder, MODULENAME + "_" + lncode + ".ts")
146 | newqmpath = os.path.join(translationsfolder, MODULENAME + "_" + lncode + ".qm")
147 | # print(tsfilepath)
148 | # print(newtspath)
149 | shutil.copyfile(tsfilepath, newtspath)
150 | os.system("lrelease " + newtspath)
151 | if not os.path.exists(newqmpath):
152 | print("ERROR: unable to create", newqmpath, ", aborting")
153 | sys.exit()
154 | if os.stat(newqmpath).st_size < 100:
155 | print(f"{newqmpath} not translated, deleting...")
156 | os.remove(newtspath)
157 | os.remove(newqmpath)
158 |
159 |
160 | if __name__ == "__main__":
161 | "main thread"
162 |
163 | # only one argument allowed
164 | arg = sys.argv[1:]
165 | if len(arg) != 1:
166 | print(__doc__)
167 | sys.exit()
168 | arg = arg[0]
169 |
170 | # getting API key stored in ~/.crowdin-freecad
171 | configfile = os.path.expanduser("~") + os.sep + ".crowdin-freecadaddons"
172 | if arg != "install":
173 | if not os.path.exists(configfile):
174 | print("Config file not found!")
175 | sys.exit()
176 | f = open(configfile)
177 | url = "https://api.crowdin.com/api/project/freecad-addons/"
178 | key = "?login=" + USERNAME + "&account-key=" + f.read().strip()
179 | f.close()
180 |
181 | basepath = os.path.abspath(BASEPATH)
182 | transpath = os.path.join(basepath, TRANSLATIONSPATH)
183 |
184 | if arg == "updatets":
185 | os.chdir(basepath)
186 | # os.system("lupdate *.ui -ts "+os.path.join(transpath,"uifiles.ts"))
187 | # os.system(PYLUPDATE+" *.py -ts "+os.path.join(transpath,"pyfiles.ts"))
188 | # os.system("lconvert -i "+os.path.join(transpath,"uifiles.ts")+" "+os.path.join(transpath,"pyfiles.ts")+" -o "+os.path.join(transpath,MODULENAME+".ts"))
189 | # os.system("rm "+os.path.join(transpath,"uifiles.ts"))
190 | # os.system("rm "+os.path.join(transpath,"pyfiles.ts"))
191 | cmd = (
192 | PYLUPDATE
193 | + ' `find ./ -name "*.py"` `find ./ -name "*.ui"` -ts '
194 | + os.path.join(transpath, MODULENAME + ".ts")
195 | )
196 | os.system(cmd)
197 | print("Updated", os.path.join(transpath, MODULENAME + ".ts"))
198 |
199 | elif arg == "build":
200 | print(
201 | "Building (warning, this can be invoked only once per 30 minutes)... ",
202 | end="",
203 | )
204 | c = pycurl.Curl()
205 | c.setopt(pycurl.URL, url + "export" + key)
206 | b = io.BytesIO()
207 | c.setopt(pycurl.WRITEDATA, b)
208 | c.perform()
209 | c.close()
210 | handler = ResponseHandler()
211 | xml.sax.parseString(b.getvalue(), handler)
212 | print(handler.data)
213 |
214 | elif arg == "download":
215 | print("Downloading all.zip in current directory...")
216 | cmd = "wget -O freecad-addons.zip " + url + "download/all.zip" + key
217 | os.system(cmd)
218 |
219 | elif arg == "upload":
220 | print("Sending " + MODULENAME + ".ts... ", end="")
221 | c = pycurl.Curl()
222 | fields = [
223 | (
224 | "files[" + MODULENAME + ".ts]",
225 | (c.FORM_FILE, os.path.join(transpath, MODULENAME + ".ts")),
226 | )
227 | ]
228 | c.setopt(pycurl.URL, url + "update-file" + key)
229 | c.setopt(pycurl.HTTPPOST, fields)
230 | b = io.BytesIO()
231 | c.setopt(pycurl.WRITEDATA, b)
232 | c.perform()
233 | c.close()
234 | handler = ResponseHandler()
235 | xml.sax.parseString(b.getvalue(), handler)
236 | print(handler.data)
237 |
238 | elif arg == "install":
239 | zippath = os.path.join(
240 | os.path.abspath(os.path.dirname(__file__)), "freecad-addons.zip"
241 | )
242 | tempfolder = tempfile.mkdtemp()
243 | print("creating temp folder " + tempfolder)
244 | os.chdir(tempfolder)
245 | if not os.path.exists(zippath):
246 | print("ERROR: " + zippath + " not found")
247 | sys.exit()
248 | shutil.copy(zippath, tempfolder)
249 | zfile = zipfile.ZipFile(os.path.join(tempfolder, os.path.basename(zippath)))
250 | print("extracting zip...")
251 | zfile.extractall()
252 | os.chdir(transpath)
253 | # if not LANGUAGES:
254 | # LANGUAGES = " ".join([n[:-1] for n in zfile.namelist() if n.endswith("/")])
255 | for ln in LANG:
256 | tpath = os.path.join(tempfolder, MODULENAME)
257 | if not os.path.exists(tpath):
258 | print("ERROR: language path", path, "not found!")
259 | else:
260 | doLanguage(tpath, transpath, ln)
261 |
262 | else:
263 | print(__doc__)
264 |
--------------------------------------------------------------------------------
/freecad/cables/resources/ui/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sargo-devel/Cables/09d5ab767dc63fc10600f46e84fe61db1483cb07/freecad/cables/resources/ui/.gitkeep
--------------------------------------------------------------------------------
/freecad/cables/resources/ui/profile.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | TaskPanelNewProfile
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 360
11 |
12 |
13 |
14 | Add a new cable profile
15 |
16 |
17 | -
18 |
19 |
20 | Profile
21 |
22 |
23 |
-
24 |
25 |
-
26 |
27 |
28 | Standard wire gauge [mm2]
29 |
30 |
31 |
32 | -
33 |
34 |
35 |
36 |
37 | -
38 |
39 |
-
40 |
41 |
42 | Number of wires
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 | -
55 |
56 |
57 | Profile type
58 |
59 |
60 |
61 | -
62 |
63 |
-
64 |
65 |
66 | Custom wire gauge [mm2]
67 |
68 |
69 |
70 | -
71 |
72 |
73 | 2.000000000000000
74 |
75 |
76 | 0.050000000000000
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/freecad/cables/version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.4"
--------------------------------------------------------------------------------
/freecad/cables/wireFlex.py:
--------------------------------------------------------------------------------
1 | """wireFlex based on Draft.Wire
2 | """
3 |
4 | # ***************************************************************************
5 | # * Copyright 2024 SargoDevel *
6 | # * *
7 | # * This program is free software; you can redistribute it and/or modify *
8 | # * it under the terms of the GNU Lesser General Public License (LGPL) *
9 | # * as published by the Free Software Foundation; either version 2 of *
10 | # * the License, or (at your option) any later version. *
11 | # * for detail see the LICENSE text file. *
12 | # * *
13 | # * This program is distributed in the hope that it will be useful, *
14 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 | # * GNU Lesser General Public License for more details. *
17 | # * *
18 | # * You should have received a copy of the GNU Library General Public *
19 | # * License along with this program; if not, write to the Free Software *
20 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21 | # * USA *
22 | # * *
23 | # ***************************************************************************/
24 |
25 |
26 | import os
27 | import FreeCAD
28 | import Draft
29 | import Part
30 | from freecad.cables import wireutils
31 | from freecad.cables import iconPath
32 | from freecad.cables import QT_TRANSLATE_NOOP
33 |
34 |
35 | CLASS_WIREFLEX_ICON = os.path.join(iconPath, "classWireFlex.svg")
36 |
37 |
38 | class WireFlex(Draft.Wire):
39 | """The WireFlex class
40 | """
41 | def __init__(self, obj):
42 | """Add the properties"""
43 | obj.addProperty("App::PropertyLinkSub", "Vrtx_start", "WireFlex",
44 | QT_TRANSLATE_NOOP("App::Property", "First Vertex"))
45 | obj.addProperty("App::PropertyLinkSub", "Vrtx_end", "WireFlex",
46 | QT_TRANSLATE_NOOP("App::Property", "Last Vertex"))
47 | obj.addProperty("App::PropertyLinkSubList", "Vrtxs_mid", "WireFlex",
48 | QT_TRANSLATE_NOOP(
49 | "App::Property", "List of middle vertexes"))
50 | obj.addProperty("App::PropertyIntegerList", "Vrtxs_mid_idx",
51 | "WireFlex",
52 | QT_TRANSLATE_NOOP(
53 | "App::Property", "Point indexes for list of " +
54 | "middle vertexes"))
55 | pl = obj.PropertiesList
56 | proplist = ["Start", "End", "MakeFace", "ChamferSize", "Closed",
57 | "Subdivisions", "FilletRadius"]
58 | for prop in proplist:
59 | if (prop in pl) and \
60 | (str(obj.getPropertyStatus(prop)[0]) != "Hidden"):
61 | obj.setPropertyStatus(prop, "Hidden")
62 | obj.Proxy = self
63 | self.Type = 'Wire'
64 | obj.Label = 'WireFlex'
65 |
66 | def get_vlist(self, obj):
67 | """It gets vector list of all attached wire points
68 | """
69 | vstart = wireutils.getVector(obj, "Vrtx_start", "Vertex")
70 | vend = wireutils.getVector(obj, "Vrtx_end", "Vertex")
71 | vmid_lst = wireutils.getVector(obj, "Vrtxs_mid", "Vertex")
72 | vlist = [None] * len(obj.Points)
73 | if vmid_lst:
74 | for element in zip(obj.Vrtxs_mid_idx, vmid_lst):
75 | vlist[element[0]-1] = element[1]
76 | vlist[0] = vstart
77 | vlist[-1] = vend
78 | return vlist
79 |
80 | def update_vrtxs_mid(self, obj, vlist):
81 | """It recalculates Vrtxs_mid and Vrtxs_mid_idx lists
82 | after a point addition or deletion
83 | """
84 | new_vrtxs_mid_idx = []
85 | old_vrtxs_mid_idx = obj.Vrtxs_mid_idx
86 | for id, vect in enumerate(vlist[1:-1]):
87 | if vect:
88 | new_vrtxs_mid_idx.append(id+2)
89 | if len(old_vrtxs_mid_idx) > len(new_vrtxs_mid_idx): # point deleted
90 | diff = [i for i in old_vrtxs_mid_idx if i not in new_vrtxs_mid_idx]
91 | # assumption len(diff) == 1
92 | idx = old_vrtxs_mid_idx.index(diff[0])
93 | vrtxs_mid = wireutils.getFlatLinkSubList(obj, 'Vrtxs_mid')
94 | vrtxs_mid.pop(idx)
95 | obj.Vrtxs_mid = vrtxs_mid
96 | obj.Vrtxs_mid_idx = new_vrtxs_mid_idx
97 |
98 | def recalculate_points(self, obj):
99 | """It recalculates all points from obj.Points
100 | """
101 | vlist = self.get_vlist(obj)
102 | pts = obj.Points
103 | for idx, vect in enumerate(vlist):
104 | if vect:
105 | pts[idx] = vect - obj.Placement.Base
106 | obj.Points = pts
107 |
108 | def execute(self, obj):
109 | # FreeCAD.Console.PrintMessage(f"Execute started({obj.Label})" + "\n")
110 | super().execute(obj)
111 | self.recalculate_points(obj)
112 | super().execute(obj)
113 |
114 | def onChanged(self, obj, prop):
115 | # FreeCAD.Console.PrintMessage(f"Changed property: {prop} \n")
116 | super().onChanged(obj, prop)
117 |
118 |
119 | class ViewProviderWireFlex(Draft.ViewProviderWire):
120 | """A base View Provider for the WireFlex object.
121 | """
122 | def __init__(self, vobj):
123 | super().__init__(vobj)
124 | vobj.PointColor = (0, 102, 0)
125 | vobj.PointSize = 8
126 | vobj.LineColor = (176, 176, 176)
127 | vobj.LineWidth = 2
128 |
129 | def getIcon(self):
130 | return CLASS_WIREFLEX_ICON
131 |
132 | def attach(self, vobj):
133 | super().attach(vobj)
134 |
135 | def updateData(self, obj, prop):
136 | super().updateData(obj, prop)
137 |
138 | def onChanged(self, vobj, prop):
139 | super().onChanged(vobj, prop)
140 |
141 |
142 | def make_wireflex(plist=None):
143 | """
144 | It creates a new object of WireFlex
145 |
146 | Parameters
147 | ----------
148 | plist : list
149 | List of type [(obj, subelement_name), ...]
150 | If None, processGuiSelection function is used internally to create
151 | plist
152 | """
153 | if not plist:
154 | plist = wireutils.processGuiSelection(
155 | single=False, subshape_class=Part.Vertex, obj_proxy_class=None)
156 | if not plist:
157 | return None
158 | pl = FreeCAD.Placement()
159 | pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
160 | pl.Base = wireutils.getVector(plist[0])
161 | vpoints = []
162 | for plink in plist:
163 | vpoints.append(wireutils.getVector(plink))
164 | base_wire = Draft.make_wire(vpoints, placement=pl, closed=False,
165 | face=False, support=None)
166 | if plist[0][1]:
167 | base_wire.AttachmentSupport = [plist[0]]
168 | base_wire.MapMode = 'Translate'
169 | WireFlex(base_wire)
170 | ViewProviderWireFlex(base_wire.ViewObject)
171 | base_wire.Vrtx_start = plist[0]
172 | base_wire.Vrtx_end = plist[-1]
173 | vrtxs_mid = wireutils.getFlatLinkSubList(base_wire, 'Vrtxs_mid')
174 | vrtxs_mid_idx = base_wire.Vrtxs_mid_idx
175 | for nr, vrtx_new in enumerate(plist[1:-1]):
176 | vrtxs_mid.append(vrtx_new)
177 | vrtxs_mid_idx.append(nr+2)
178 | base_wire.Vrtxs_mid = vrtxs_mid
179 | base_wire.Vrtxs_mid_idx = vrtxs_mid_idx
180 | return base_wire
181 |
182 |
183 | def make_wireflex_from_vectors(vectorlist):
184 | """
185 | It creates a new object of WireFlex
186 |
187 | Parameters
188 | ----------
189 | vectorlist : list
190 | List of Vectors used to create WireFlex
191 | No attachments are made
192 |
193 | Returns
194 | -------
195 | WireFlex object
196 | """
197 | pl = FreeCAD.Placement()
198 | pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
199 | pl.Base = vectorlist[0]
200 | vpoints = vectorlist
201 | base_wire = Draft.make_wire(vpoints, placement=pl, closed=False,
202 | face=False, support=None)
203 | WireFlex(base_wire)
204 | ViewProviderWireFlex(base_wire.ViewObject)
205 | return base_wire
206 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Cables Workbench
4 | Electrical cables drawing tools workbench for FreeCAD.
5 | 0.1.4
6 | 2025-03-15
7 | SargoDevel
8 | LGPL-3.0-or-later
9 | https://github.com/sargo-devel/Cables
10 | https://github.com/sargo-devel/Cables/blob/master/README.md
11 | https://wiki.freecad.org/Cables_Workbench
12 | freecad/cables/resources/icons/CablesLogo.svg
13 |
14 |
15 |
16 | CablesWorkbench
17 | ./
18 | cables
19 | electric
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | import os
3 |
4 | version_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
5 | "freecad", "cables", "version.py")
6 | with open(version_path) as fp:
7 | exec(fp.read())
8 |
9 | setup(name='freecad.cables',
10 | version=str(__version__),
11 | packages=['freecad',
12 | 'freecad.cables'],
13 | maintainer="SargoDevel",
14 | maintainer_email="sargo-devel@o2.pl",
15 | url="https://github.com/sargo-devel/Cables",
16 | description="Electrical cables drawing tools workbench for FreeCAD",
17 | include_package_data=True)
18 |
--------------------------------------------------------------------------------
/wireFlex.py:
--------------------------------------------------------------------------------
1 | # old module
2 | from freecad.cables import wireFlex
3 |
4 | WireFlex = wireFlex.WireFlex
5 | ViewProviderWireFlex = wireFlex.ViewProviderWireFlex
6 |
--------------------------------------------------------------------------------