Compare commits

..

21 commits

Author SHA1 Message Date
Morgan 'ARR\!' Allen
c76b49e7b9 big refactor of polygon references in Boards and feature references in API 2025-07-10 14:18:27 -07:00
Morgan 'ARR\!' Allen
34adb50b9f remove prints 2025-07-10 14:17:41 -07:00
Morgan 'ARR\!' Allen
b191a28a0a add save_keys functionality to BaseViewProvider (mixin?) and fixed icon loading 2025-07-10 12:11:46 -07:00
Morgan 'ARR\!' Allen
417a7065de small cleanup of README 2025-07-10 12:11:12 -07:00
Morgan 'ARR\!' Allen
5fc603ab50 Merge branch 'development' of git.oit.cloud:morgan/kiconnect into development 2025-07-10 11:58:26 -07:00
Morgan 'ARR\!' Allen
ebd6e5709c move kicad icons to their own dir and include original license 2025-07-10 11:57:49 -07:00
Morgan 'ARR\!' Allen
6311cdef48 move kicad icons to their own dir and include original license 2025-07-10 11:55:46 -07:00
Morgan 'ARR\!' Allen
93eb0d0483 more cleanup on Board > BoardSketch. sync_from after onDocumentLoaded now works 2025-07-10 11:44:14 -07:00
Morgan 'ARR\!' Allen
c85d038700 enable easy dumps/loads on BaseObject via .save_keys 2025-07-10 11:42:34 -07:00
Morgan 'ARR\!' Allen
bdd7ea4684 migrate Board creation from Project > API. Project will be either removed or converted to PythonFeature if proper App::Part emulation can be firgured out. 2025-07-10 11:41:33 -07:00
Morgan 'ARR\!' Allen
d2bee101ea return tuple from Board.makeBoard, now including the polygon_id 2025-07-10 11:36:58 -07:00
Morgan 'ARR\!' Allen
8e9d8d7a38 remove overly complicated substrate/sketch getters
Update getting sketch by BoardSketch name
2025-07-10 11:36:24 -07:00
Morgan 'ARR\!' Allen
934aaaf354 more prefs 2025-06-10 22:12:07 -07:00
Morgan 'ARR\!' Allen
7ee3ed89c6 toolbar button to open preferences 2025-06-10 18:01:25 -07:00
Morgan 'ARR\!' Allen
b19c95cd2e added preferences
fixes #8
2025-06-10 17:53:30 -07:00
Morgan 'ARR\!' Allen
205254a4f3 make parts use sync_from 2025-05-19 21:23:11 -07:00
Morgan 'ARR\!' Allen
6137b50208 improving sync_from 2025-05-19 21:22:43 -07:00
Morgan 'ARR\!' Allen
0a95b5529e more migration to BoardSketch 2025-05-19 21:22:18 -07:00
Morgan 'ARR\!' Allen
a162b98000 Type doesnt want to be writable anymore 2025-05-19 21:21:50 -07:00
Morgan 'ARR\!' Allen
c61020242d make KiConnect::BoardSketch syncable 2025-05-19 21:20:25 -07:00
Morgan 'ARR\!' Allen
638fda2daf ensure doc is recompute after updates 2025-05-19 21:18:39 -07:00
18 changed files with 357 additions and 84 deletions

View file

@ -6,6 +6,7 @@ KiCAD 9 API Workbench
## What works
* Create board
* Bidirectional syncing
* Add Vias
* Add footprint pads
* Import footprint 3d models
@ -19,14 +20,11 @@ KiCAD 9 API Workbench
## In Progress / To be ported
### Tracks
Tracks can be loaded and drawn as simple `LineSegment` but more work is needed to draw properly 'widthed', and particularly whilte trying to eliminate overlapping lines.
Tracks can be loaded and drawn as simple `LineSegment` but more work is needed to draw properly 'widthed', and particularly while trying to eliminate overlapping lines.
### More Pad Shapes
Only rectangle get drawn right now, would like to figure out how to get Sketcher to do the heavy lifting
### Sync to KiCAD
In theory this code is mostly working but there seems to be an [issue in kicad-python](https://gitlab.com/kicad/code/kicad-python/-/issues/34)
## Plans
### Add more board features

View file

@ -5,12 +5,15 @@ from kipy import KiCad
from kipy.proto.common.types import DocumentType
from . import settings
from . import board as Board
from .bases import BaseObject, BaseViewProvider
class APIObject(BaseObject):
TYPE = 'KiConnect::API'
def __init__(self, feature):
self.boards = {}
self.polygons = {}
self.kicad = KiCad()
super(APIObject, self).__init__(feature)
@ -21,21 +24,49 @@ class APIObject(BaseObject):
self.onDocumentRestored(feature)
self.ping_connection(feature)
self.ping_connection()
self.kicad_board = self.kicad.get_board()
polygons = Board.extract_polygons(self.kicad_board)
for polygon in polygons:
board, polygon_id = Board.makeBoard(self.feature.getParent(), self.kicad_board, polygon)
self.polygons[polygon_id] = polygon
self.boards[polygon_id] = board
def onDocumentRestored(self, feature):
super(APIObject, self).onDocumentRestored(feature)
if not hasattr(self, 'polygons'):
setattr(self, 'polygons', {})
if not hasattr(self, 'boards'):
setattr(self, 'boards', {})
self.kicad = KiCad()
self.ping_connection(feature)
self.refresh_polygons()
parent = feature.getParent()
if not parent: return
# XXX This gets all of the KiConnect::Board features but then does nothing with them
# future multi-board support?
boards = [ board for board in parent.Group if hasattr(board, 'Type') and board.Type == 'KiConnect::Board' ]
for board in boards:
print(board)
self.boards[board.PolygonId] = board
def refresh_polygons(self):
self.ping_connection()
if self.is_connected:
self.kicad_board = self.kicad.get_board()
polygons = Board.extract_polygons(self.kicad_board)
print('>', polygons)
for poly in polygons:
self.polygons[poly.id.value] = poly
else:
print('*****NOT CONNECTED')
@property
def is_connected(self):
@ -45,7 +76,12 @@ class APIObject(BaseObject):
return self.feature.Connected
def ping_connection(self, feature):
def get_polygon(self, polygon_id):
print(polygon_id, self.polygons)
self.refresh_polygons()
return self.polygons.get(polygon_id)
def ping_connection(self):
'''
Ping the KiCAD API to determine if it's connected
'''
@ -56,29 +92,29 @@ class APIObject(BaseObject):
try:
self.kicad.ping()
feature.Connected = True
self.feature.Connected = True
connection_status = 'Connected'
except Exception as e:
feature.Connected = False
self.feature.Connected = False
connection_status = 'Disconnected'
if feature.Connected:
if self.feature.Connected:
try:
docs = self.kicad.get_open_documents(DocumentType.DOCTYPE_PCB)
document_status = f'{len(docs)} Documents'
feature.DocumentCount = len(docs)
self.feature.DocumentCount = len(docs)
except Exception as e:
print(e)
feature.DocumentCount = 0
self.feature.DocumentCount = 0
feature.Label2 = f'{connection_status} ({document_status})'
self.feature.Label2 = f'{connection_status} ({document_status})'
return feature.Connected
return self.feature.Connected
class APIViewProvider(BaseViewProvider):
ICON = 'icon_footprint_browser.svg'
ICON = 'kicad/icon_footprint_browser.svg'
TYPE = 'KiConnect::API'
def __init__(self, viewprovider):

View file

@ -2,6 +2,8 @@ class BaseObject:
EXTENSIONS = []
TYPE = None
save_keys = []
def __init__(self, feature):
self.feature = feature
@ -15,9 +17,7 @@ class BaseObject:
self.sync_from()
def execute(self, feature):
# TODO this might not be the right move
print(self, 'BaseObject.execute')
#self.onDocumentRestored(feature)
pass
def get_api(self):
p = self.feature
@ -54,7 +54,7 @@ class BaseObject:
self.feature.addExtension(ext)
def setup_properties(self, feature):
feature.addProperty('App::PropertyString', 'Type', 'KiConnect', 'Internal KiConnect Type', read_only=True, hidden=True)
feature.addProperty('App::PropertyString', 'Type', 'KiConnect', 'Internal KiConnect Type', hidden=True)
def sync_from(self):
pass
@ -62,5 +62,21 @@ class BaseObject:
def sync_to(self):
pass
def __getstate__(self):
return None
def dumps(self):
data = [ getattr(self, 'TYPE') ]
if len(self.save_keys) > 0:
for key in self.save_keys:
try:
data.append(getattr(self, key))
except Exception as e:
#XXX logging
print(e)
return tuple(data)
def loads(self, state):
self.Type = state[0]
for idx, key in enumerate(self.save_keys):
setattr(self, key, state[idx + 1])

View file

@ -9,6 +9,8 @@ class BaseViewProvider:
TYPE = None
EXTENSIONS = []
save_keys = []
def __init__(self, viewprovider):
self.viewprovider = viewprovider
self.feature = viewprovider.Object.Proxy.feature
@ -46,11 +48,21 @@ class BaseViewProvider:
'''Return the name of the default display mode. It must be defined in getDisplayModes.'''
return 'Standard'
def __getstate__(self):
return {
'icon': self.icon
}
def dumps(self):
data = [ getattr(self, 'icon') ]
def __setstate__(self, props):
for prop in props:
setattr(self, prop, props[prop])
if len(self.save_keys) > 0:
for key in self.save_keys:
try:
data.append(getattr(self, key))
except Exception as e:
#XXX logging
print(e)
return tuple(data)
def loads(self, state):
self.icon = state[0]
for idx, key in enumerate(self.save_keys):
setattr(self, key, state[idx + 1])

View file

@ -16,11 +16,15 @@ from . import board_sketch as BoardSketch
class BoardObject(BaseObject):
TYPE = 'KiConnect::Board'
save_keys = [ 'polygon_id' ]
def __init__(self, feature, kicad_board, board_polygon):
self.feature = feature
self.substrate_body = None
self.board_sketch = None
self.kicad_board = kicad_board
# TODO add this to FreeCAD Preferences and Property?
self.do_offset = True
# TODO needs to be resotred in onDocumentRestored
#self.board_polygon = board_polygon
self.polygon_id = board_polygon.id.value
@ -31,6 +35,7 @@ class BoardObject(BaseObject):
self.feature.PolygonId = board_polygon.id.value
self.substrate_body = self.create_substrate_body()
self.board_sketch = BoardSketch.makeBoardSketch(self.substrate_body, kicad_board, board_polygon)
self.create_substrate_pad()
@ -41,17 +46,8 @@ class BoardObject(BaseObject):
def onDocumentRestored(self, feature):
super(BoardObject, self).onDocumentRestored(feature)
self.board_sketch = self.feature.getObject('BoardSketch')
self.kicad_board = self.get_api().kicad.get_board()
@property
def substrate_body(self):
body = self.feature.getObject('Substrate')
if not body:
body = self.create_substrate_body()
return body
self.board_sketch = self.feature.getObject('Substrate').getObject('BoardSketch')
def create_substrate_body(self):
substrate_body = App.ActiveDocument.addObject('PartDesign::Body', 'Substrate')
@ -72,7 +68,10 @@ class BoardObject(BaseObject):
return self.board_sketch
def get_boardpoly(self, polygon_id=None):
def get_polygon(self, kiid):
return [ s for s in self.kicad_board.get_shapes() if s.id.value == kiid ][0]
def get_boardpoly(self):
# TODO remove in favor of extract_polygons class method
board = self.kicad_board
@ -80,10 +79,8 @@ class BoardObject(BaseObject):
edge_cuts = [ edge for edge in board.get_shapes() if edge.layer == BoardLayer.BL_Edge_Cuts ]
polygons = [ edge for edge in edge_cuts if isinstance(edge, BoardPolygon) ]
if polygon_id:
return [ p for p in polygons if p.id.value == polygon_id ][0]
else:
return polygons
# XXX only single board supported at the moment
return polygons[0]
def pocket_vias(self):
board = self.kicad_board
@ -130,17 +127,8 @@ class BoardObject(BaseObject):
Pulls outline from KiCAD PolygonBoard and saves points as Vectors
'''
# bit of a quick hack to keep the board in one place, needs more testing
if self.do_offset and not self.feature.BoardOffset:
bb = self.board_polygon.bounding_box()
self.feature.BoardOffset.Base = (App.Vector(bb.pos.x, -bb.pos.y) + App.Vector(bb.size.x, -bb.size.y) / 2) / 1000000.0
vectors = []
for node in self.board_polygon.polygons[0].outline:
vectors.append(self.point_to_vector(node.point))
self.feature.Vectors = vectors
if self.board_sketch:
self.board_sketch.Proxy.sync_from()
def sync_to(self):
board = self.kicad_board
@ -155,7 +143,7 @@ class BoardObject(BaseObject):
poly = boardpoly.polygons[0]
poly.outline.clear()
geom = self.feature.getObject('Substrate').getObject('Sketch').Geometry
geom = self.feature.getObject('Substrate').getObject('BoardSketch').Geometry
segments = [l for l in geom if isinstance(l, Part.LineSegment)]
for line in segments:
@ -183,6 +171,7 @@ class BoardObject(BaseObject):
feature.addProperty('App::PropertyVectorList', 'Vectors', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True)
feature.Doc = kicad_board.name
feature.Label2 = kicad_board.name
if not feature.Thickness:
feature.Thickness = 1.6
@ -192,7 +181,7 @@ class BoardObject(BaseObject):
class BoardViewProvider(BaseViewProvider):
TYPE = 'KiConnect::Board'
ICON = 'board.svg'
ICON = 'kicad/board.svg'
def makeBoard(parent, kicad_board, polygon):
feature = App.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'Board')
@ -203,7 +192,7 @@ def makeBoard(parent, kicad_board, polygon):
Parts.makeParts(feature)
return feature
return feature, feature.Proxy.polygon_id
def extract_polygons(board):
# find polygons of Edge Cuts

View file

@ -11,11 +11,18 @@ from kipy.util.board_layer import BoardLayer
from .bases import BaseObject, BaseViewProvider
class BoardPolyNotFoundException(Exception):
pass
class BoardSketchObject(BaseObject):
TYPE = 'KiConnect::BoardSketch'
save_keys = [ 'polygon_id' ]
def __init__(self, feature, kicad_board, board_polygon):
self.board_polygon = board_polygon
self.polygon_id = board_polygon.id.value
super(BoardSketchObject, self).__init__(feature)
#feature.Visibility = False
@ -39,7 +46,13 @@ class BoardSketchObject(BaseObject):
feature = self.feature
vectors = []
for node in self.board_polygon.polygons[0].outline:
# board.get_shapes needs to be called to ensure polygons are actually up to date
board_polygon = self.get_api().get_polygon(self.polygon_id)
if not board_polygon:
raise BoardPolyNotFoundException('Board Polygon not found: ' + self.polygon_id)
for node in board_polygon.polygons[0].outline:
vectors.append(self.point_to_vector(node.point))
self.feature.Vectors = vectors
@ -65,12 +78,9 @@ class BoardSketchObject(BaseObject):
feature.recompute()
def __getstate__(self):
return None
class BoardSketchViewProvider(BaseViewProvider):
TYPE = 'KiConnect::BoardSketch'
#ICON = 'board.svg'
#ICON = 'kicad/board.svg'
def makeBoardSketch(parent, kicad_board, polygon):
feature = App.ActiveDocument.addObject('Sketcher::SketchObjectPython', 'BoardSketch')

View file

@ -1,7 +1,8 @@
import FreeCAD as App
import FreeCADGui as Gui
class Syncable:
SYNCABLES = [ 'KiConnect::Project', 'KiConnect::Board', 'KiConnect::Parts', 'KiConnect::BoardBody', ]
SYNCABLES = [ 'KiConnect::Project', 'KiConnect::Board', 'KiConnect::Parts', 'KiConnect::BoardBody', 'KiConnect::BoardSketch' ]
def IsActive(self):
sel = Gui.Selection.getSelection()
@ -41,3 +42,5 @@ class Syncable:
getattr(feature, self.method)()
s.recompute()
App.ActiveDocument.recompute()

View file

@ -0,0 +1,24 @@
import importlib
import FreeCADGui as Gui
import os
import sys
from .. import settings
from ..project import Project
class EditPrefs:
def GetResources(self):
tooltip = '<p>EditPrefs KiConnect Workbench for development.\nNOTE: Does not reload toolbars.</p>'
iconFile = 'preferences-system.svg'
return {
'MenuText': 'Edit Preferences',
'ToolTip': tooltip,
'Pixmap' : iconFile
}
def Activated(self):
Gui.showPreferences("KiConnect")
Gui.addCommand('cmd_edit_prefs', EditPrefs())

View file

@ -19,7 +19,7 @@ class CopperObject(BaseObject):
TYPE = 'KiConnect::Copper'
class CopperViewProvider(BaseViewProvider):
ICON = 'show_all_copper_layers.svg'
ICON = 'kicad/show_all_copper_layers.svg'
TYPE = 'KiConnect::Copper'
class Copper():

View file

@ -7,7 +7,7 @@ sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), '..
import FreeCADGui as Gui
import FreeCAD as App
from .commands import cmd_new_pcb, cmd_reload, cmd_sync_from, cmd_sync_to
from .commands import cmd_edit_prefs, cmd_new_pcb, cmd_reload, cmd_sync_from, cmd_sync_to
from . import settings
translate=App.Qt.translate
@ -19,12 +19,18 @@ TRANSLATIONSPATH = os.path.join(os.path.dirname(__file__), "resources", "transla
Gui.addLanguagePath(TRANSLATIONSPATH)
Gui.updateLocale()
default_preferences = [
('Bool', 'debug_reload', True),
('Bool', 'prefs_toolbar', True),
('Float', 'default_thickness', 1.6),
]
class KiConnect(Gui.Workbench):
MenuText = translate("Workbench", "KiConnect")
ToolTip = translate("Workbench", "KiConnect PCB Workbench")
Icon = os.path.join(settings.ICONPATH, "kiconnect.svg")
toolbox = [ 'kiconn_new', 'kiconn_reload', 'kiconn_sync_to', 'kiconn_sync_from' ]
toolbox = [ 'kiconn_new', 'kiconn_sync_to', 'kiconn_sync_from' ]
def GetClassName(self):
return "Gui::PythonWorkbench"
@ -35,11 +41,27 @@ class KiConnect(Gui.Workbench):
here is the place to import all the commands
"""
# setup default preferences on first load
if settings.preferences.IsEmpty():
for pref in default_preferences:
print('setting pref: ', f'Set{pref[0]}', pref[1], pref[2])
getattr(settings.preferences, f'Set{pref[0]}')(pref[1], pref[2])
# add debug reload button if enabled
if settings.preferences.GetBool('debug_reload'):
self.toolbox.append('kiconn_reload')
if settings.preferences.GetBool('prefs_toolbar'):
self.toolbox.append('cmd_edit_prefs')
print('setting up toolbar')
# NOTE: Context for this commands must be "Workbench"
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "KiConnect"), self.toolbox)
self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "KiConnect"), self.toolbox)
Gui.addPreferencePage(os.path.join(settings.UIPATH, 'preferences.ui'), 'KiConnect')
def Activated(self):
App.Console.PrintMessage(translate("Log", "Workbench KiConnect activated.") + "\n")

View file

@ -15,12 +15,13 @@ from .bases import BaseObject, BaseViewProvider
class PartsObject(BaseObject):
TYPE = 'KiConnect::Parts'
def __init__(self, feature):
super(PartsObject, self).__init__(feature)
def execute(self, feature):
super(PartsObject, self).execute(feature)
self.import_footprints()
def import_footprints(self):
def sync_from(self):
kiconn_board = self.feature.getParentGroup()
kicad_board = self.get_api().kicad.get_board()
@ -72,7 +73,7 @@ class PartsObject(BaseObject):
print(e)
class PartsViewProvider(BaseViewProvider):
ICON = 'icon_footprint_browser.svg'
ICON = 'kicad/icon_footprint_browser.svg'
TYPE = 'KiConnect::Parts'
def makeParts(parent):

View file

@ -28,16 +28,6 @@ class Project:
self.API = api.makeAPI(self.feature)
if self.API.Proxy.is_connected and self.API.DocumentCount > 0:
kicad_board = self.API.Proxy.kicad.get_board()
polygons = Board.extract_polygons(kicad_board)
for polygon in polygons:
self.board = Board.makeBoard(self.feature, kicad_board, polygon)
#self.copper = Copper(kicad_board, self.board)
#self.board.feature.addObject(self.copper.feature)
feature.ProcessTime = time.time() - start_time
App.ActiveDocument.recompute()

View file

@ -0,0 +1,9 @@
ICONS:
Original KiCad Icon work by Inigo Zuluaga and Fabrizio Tappero among others
KiCad icons were redesigned in 2020 by Aleksandr Zyrianov
KiCad nightly icon reworked by Rafael Silva based on the 2020 redesign
License: CC-BY-SA 4.0

View file

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KiConnect::DlgSettingsKiConnect</class>
<widget class="QWidget" name="KiConnect::DlgSettingsKiConnect">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1151</width>
<height>663</height>
</rect>
</property>
<property name="maximumSize">
<size>
<width>1151</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>General</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="defaults_group">
<property name="title">
<string>Defaults</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Default Board Thickness</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefUnitSpinBox" name="spin_thickness">
<property name="unit" stdset="0">
<string>mm</string>
</property>
<property name="value">
<double>1.600000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>default_thickness</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/KiConnect</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="check_load_parts">
<property name="text">
<string>Load Parts</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>parts_load</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/KiConnect</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="debug_group">
<property name="title">
<string>Debug and Development</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="Gui::PrefCheckBox" name="check_debug_reload">
<property name="font">
<font>
<pointsize>10</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Enable Reload</string>
</property>
<property name="tristate">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>debug_reload</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/KiConnect</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox">
<property name="text">
<string> Preferences from toolbar</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>Mod/KiConnect</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>prefs_toolbar</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QAbstractSpinBox</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefUnitSpinBox</class>
<extends>Gui::QuantitySpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -1,5 +1,10 @@
import os
import FreeCAD as App
BOARD_THICKNESS = 0.80
ICONPATH = os.path.join(os.path.dirname(__file__), "resources", 'icons')
UIPATH = os.path.join(os.path.dirname(__file__), "resources", 'ui')
KICAD9_3DMODEL_DIR = '/usr/share/kicad/3dmodels/'
PREF_PATH = 'User parameter:BaseApp/Preferences/Mod/KiConnect'
preferences = App.ParamGet(PREF_PATH)