Генерирует 3D-модель подставки для хранения свёрел по набору хвостовиков (диаметр, длина, тип)
и экспортирует её в .FCStd, .STL и .STEP. Свёрла удерживаются печатными листовыми
пружинами-детентами в стенках гнёзд, чтобы не болтались. Поддерживаются круглые и шестигранные
(hex) хвостовики, опциональные крышка-колпак и устойчивое основание.
- Гнёзда под круглые (по диаметру сверла) и шестигранные (
hex) хвостовики. Hex-гнездо шестигранное, по стандартному размеру «под ключ» (socket.hex_size, по умолчанию 1/4"); диаметр сверла на hex-гнездо не влияет — только на подпись. - Листовые пружины-детенты в стенке гнезда (и круглого, и hex): продольные пропилы выделяют балку-лепесток, посередине — утолщение внутрь к хвостовику; 1–2 пружины по длине удерживают сверло по оси (без покупных деталей), чтобы оно не выпадало. У круглого лепестки идут по окружности, у hex — на гранях призмы (грань держит от проворота, детент — от выпадания; детентов не больше 6 граней). Если не помещаются — число авто-уменьшается.
- Раскладка: ряд, сетка, наклонная подставка или auto (выбор по набору свёрел).
Раскладка адаптивная: шаг — по реальным размерам гнёзд (мелкое сверло не занимает место
крупного), а высота каждого ряда сетки — по самому крупному гнезду в нём. В режиме grid ряды
центрируются по общей оси и растягиваются на общую ширину равными промежутками вокруг гнёзд (как
CSS flex
space-around). Упаковка учитывает и место под подписи: вынос текста за гнездо резервируется и между рядами, и внутри ряда, и в габарите корпуса — подписи не налезают на соседние гнёзда. Выравниваниеalign(center/front/back) работает в обоих режимах — по вертикали внутри каждого ряда. Флагlayout.serpentineраскладывает сетку «змейкой»: нечётные ряды разворачиваются (слева направо → справа налево). Свёрла отсортированы по размеру, поэтому змейка делает их размерный ряд непрерывным — конец ряда и начало следующего оказываются соседями по вертикали (без скачка размера на переносе). Габарит корпуса при этом тот же, что у обычной сетки, — это про удобство поиска сверла, а не про уменьшение размера. - Глубина гнезда — доля от длины сверла (с ограничением min/max).
- Крышка-колпак над свёрлами: съёмная или откидная (на боковых осях корпуса).
- Устойчивое основание: ручное (вынос плиты) или авто (по центру тяжести и высоте свёрел).
- Опциональные подписи диаметров — гравировкой (вырез вглубь) или выдавливанием (рельеф наружу); с выбранной стороны отверстия, отступ от его края или от центра.
- Опциональные 3D-модели свёрел (
drill_geometry, по умолчанию выкл) — со спиральной режущей частью или гладким цилиндром без канавок (style), с коническим кончиком и хвостовиком, собранные в отдельную детальDrills(не сливается с корпусом). Для визуализации набора в подставке; печатать эту деталь не нужно. - Экспорт в любой набор форматов:
FCStd,STL,STEP(детали — отдельными файлами).
Нужен только FreeCAD ≥ 1.0 (проверено на 1.2). Сторонних библиотек нет.
freecadcmd лежит внутри приложения, например на macOS:
/Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd
- Откройте
holder_config.pyи впишите свои свёрла и параметры (файл подробно прокомментирован). - Запустите сборку через launcher
./drill-holder— он сам находитfreecadcmd:
./drill-holder # holder_config.py рядом со скриптом + диалог правки (по умолчанию)
./drill-holder my_drills.py # свой конфиг + диалог
./drill-holder --no-gui # собрать сразу по конфигу, без диалога (скрипты/headless)По умолчанию открывается Qt-диалог правки (см. ниже). Для скриптовой/пакетной сборки добавьте
--no-gui — тогда модель строится сразу по конфигу.
Windows. Тот же запуск — обёртками drill-holder.cmd (cmd) и drill-holder.ps1 (PowerShell);
аргументы те же, freecadcmd ищется в PATH и в C:\Program Files\FreeCAD*\bin:
drill-holder.cmd --no-gui REM из cmd.exe
.\drill-holder.ps1 my_drills.py # из PowerShellПуть к freecadcmd при необходимости задаётся переменной FREECADCMD (set FREECADCMD=... в cmd,
$env:FREECADCMD='...' в PowerShell). Bash-launcher ./drill-holder тоже работает под Git Bash/MSYS.
Launcher ищет freecadcmd в PATH и типовых местах установки (в т.ч. внутри
FreeCAD.app на macOS). Если нашёлся не тот / не нашёлся — укажите путь явно:
FREECADCMD="/Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd" ./drill-holderПоявятся файлы drill_holder.FCStd, drill_holder.stl, drill_holder.step
(имя и каталог задаются в output; при крышке детали пишутся по отдельному файлу).
Запуск напрямую тоже работает:
freecadcmd build_holder.py holder_config.py.
При запуске без --no-gui открывается Qt-диалог, предзаполненный из holder_config.py. Форма
покрывает каждый параметр конфига и разложена на 5 смысловых вкладок:
- Свёрла — таблица свёрел (Ø/длина/хвостовик/подпись) + хвостовик по умолчанию для новых строк.
Удобства таблицы:
- массовый ввод: вставьте список диаметров через запятую/пробел (
1, 1.5, 2, 2.5, 3 …) в поле над таблицей и нажмите «Добавить из списка» — они допишутся строками (с авто-длиной, подписью и хвостовиком по умолчанию), отсортированные по возрастанию; дубликаты сохраняются, нераспознанные куски пропускаются с сообщением; - длина по умолчанию —
auto: при вводе диаметра ячейка показываетauto (N), гдеN— стандартная длина по DIN 338 (в конфиг при этом сохраняется именно"auto"); можно ввести и конкретное число — оно не будет затёрто; - подпись формируется из диаметра сразу после его ввода, если поле подписи пустое;
- кнопка «– удалить» активна только при выделенной строке.
- массовый ввод: вставьте список диаметров через запятую/пробел (
- Раскладка и корпус, Гнёзда и пружины, Крышка и основание, Подписи и экспорт — все остальные параметры по секциям. У каждого поля — подсказка (tooltip) с пояснением; шрифт задаётся именем системного шрифта (кнопка «Система…») или путём к файлу («Файл…»).
- Конфиг — read-only превью сгенерированного
holder_config.py(что именно уйдёт в сборку).
Зависимости параметров — вживую. Неприменимое при текущих значениях поле дизейблится
(напр. угол наклона — только для раскладки angled; параметры крышки — недоступны для angled;
тело пружин — при выключенных пружинах) с пояснением в подсказке. Недопустимое значение
подсвечивается красным с сообщением «почему + совет». Пока конфиг невалиден, кнопка
«Создать» неактивна, а внизу показана причина.
По «Создать» текущая форма записывается в holder_config.py (через render_config,
с комментариями-документацией) и по ней же строится модель — файл и сборка всегда отражают
текущие значения. Отмена ничего не пишет.
Кнопка «Калибровочный купон…» собирает не подставку, а тестовую деталь для подбора
socket.clearance под ваш принтер (та же гребёнка гнёзд, что и calibration.py, см.
Посадка свёрел и калибровка зазора). Ваш конфиг и
holder_config.py при этом не меняются.
Диалог работает двумя путями:
- Из командной строки (по умолчанию):
freecadcmdсам поднимает Qt-окно. Если дисплея нет (например, CI сQT_QPA_PLATFORM=offscreen), окно показать нельзя — сборка идёт по конфигу с предупреждением. Для заведомо безоконной сборки используйте--no-gui. - Как макрос в FreeCAD: откройте
build_holder.pyв FreeCAD (меню Macro) и запустите — диалог появится автоматически.
Единственный источник истины — holder_config.py (обычный Python-файл с dict CONFIG).
Каждый параметр снабжён комментарием. Любой пропущенный ключ берётся из значений по умолчанию
(drillholder/config.py: DEFAULTS). Ключевые блоки:
| Блок | Что задаёт |
|---|---|
default_shank |
тип хвостовика по умолчанию (round/hex) |
drills |
список свёрел: d, length (число или "auto"/опустить), опц. shank, label |
socket |
зазоры, глубина (доля длины), пределы глубины, размер/зазор/ориентация hex |
springs |
листовые пружины: число, ряды, размеры лепестка/пропилов, детент |
layout |
режим (auto/row/grid/angled), сетка «змейкой» (serpentine), угол наклона, шаги, отступы, выравнивание ряда (align: center/front/back) |
base |
толщина дна и минимальная высота корпуса |
lid |
крышка: none/removable/hinged, стенки, зазоры, оси/защёлка |
foot |
основание: none/manual/auto, толщина, вынос, угол опрокидывания |
labels |
подписи: вкл/выкл, режим (гравировка/выдавливание), положение, привязка, размер, шрифт, многоцветная печать |
rings |
декоративные колечки вокруг гнёзд: вкл/выкл, ширина, глубина, зазор от отверстия |
output |
каталог, имя, набор форматов, точность меша STL |
Для
hexгнездо шестигранное и берётся изsocket.hex_size(стандарт «под ключ», 6.35 = 1/4"); полеdу hex — диаметр сверла, влияет только на подпись.shankу сверла указывают только если он отличается отdefault_shank.labels.mode—engrave(текст вырезается вглубь грани наdepth) илиemboss(текст выступает рельефом наружу наdepth); для выдавливания закладывайте зазор до соседей и крышки.labels.position— сторона от отверстия (front/back/left/right);labels.reference— откуда отсчитывается отступ:edge(от края отверстия — зазорoffsetодинаков при любом размере гнезда) илиcenter(от центра отверстия — подписи на фиксированном расстоянии от центра выстраиваются ровно даже в сетке с гнёздами разного размера).labels.fontпринимает имя системного шрифта (Arial,DejaVu Sans— ищется файл в системных каталогах) или путь к файлу .ttf/.otf. Если пусто — берётся первый доступный системный шрифт (это штатное «авто», без предупреждения). Жёлтое предупреждение появляется, только если запрошенный шрифт не найден; подписи пропускаются, лишь если шрифтов нет вообще.labels.multicolor— для мультиматериального принтера (QIDI BOX и т.п.): подписи выводятся отдельной детальюLabels, а в корпусе вырезается посадочное место под текст (и для гравировки, и для выдавливания). Загрузите<name>_holder.*и<name>_labels.*в слайсер как один объект и назначьте им разные филаменты — получите надписи другим цветом. Выключено — текст слит с корпусом одним цветом.rings— декоративные колечки-инкрустации вокруг гнёзд (чистая косметика, на посадку сверла не влияют). Вокруг каждого гнезда в верхнюю грань заподлицо утоплено плоское кольцо ширинойrings.widthи глубинойrings.depth; внутренняя кромка отсчитывается от самого отверстия (с зазоромrings.gap), поэтому кольцо облегает дырку, а не внешний край ниш пружин. Кольца выводятся отдельной детальюRingsпод печать вторым пластиком (тем же, что и подписи): в корпусе под них вырезаются выемки, в слайсере<name>_rings.*загружают вместе с<name>_holder.*как один объект и назначают кольцам другой филамент. Раскладка автоматически резервирует место под ширину колец, чтобы они не наезжали на соседей. Крышка поддерживается только для прямых раскладок (неangled). При нескольких деталях STL/STEP пишутся по файлу на деталь (<name>_holder.*,<name>_labels.*,<name>_rings.*,<name>_lid.*).
Код разбит на модули по принципу Clean Architecture (Dependency Rule): чистое ядро не зависит от FreeCAD и тестируется обычным Python; вызовы FreeCAD изолированы в адаптерах.
holder_config.py # конфиг (данные, правится вручную)
build_holder.py # точка входа: связывает слои
drillholder/
model.py # ядро: Value Objects (dataclasses) и Enums
config.py # ядро: загрузка, слияние с дефолтами, валидация
layout.py # ядро: расчёт раскладки и глубин (чистая математика)
lengths.py # ядро: справочник стандартных длин свёрел (DIN 338)
fonts.py # ядро: подбор системного шрифта для подписей
geometry/ # адаптер к FreeCAD (единственное место с import Part/Draft/Mesh)
base.py # корпус (прямой блок / клин)
socket.py # гнездо + листовые пружины-детенты (round/hex)
labels.py # гравировка подписей
foot.py # устойчивое основание (manual/auto по центру тяжести)
lid.py # крышка-колпак (съёмная/откидная)
assembly.py # сборка: корпус − гнёзда − подписи + основание + крышка
export.py # адаптер: запись FCStd/STL/STEP
gui/ # опциональный PySide-диалог правки (форма на все параметры)
fields.py # ядро: декларации полей (без Qt)
dependencies.py # ядро: применимость/валидность/проверка (без Qt)
widgets.py # Qt-виджеты полей
drill_table.py # Qt-таблица свёрел
preview.py # Qt read-only превью конфига
dialog.py # Qt-диалог + show_dialog
gui_highlighter.py # подсветка синтаксиса превью (Qt)
tests/ # тесты ядра — без FreeCAD
Чистые слои (model/config/layout) тестируются без FreeCAD:
python3 -m pytest tests/ -qГеометрию и экспорт проверяйте сборкой через freecadcmd (см. выше) и открытием .FCStd
в FreeCAD.
Бор гнезда — свободная посадка (сверло проскальзывает), а удерживают его от выпадания детенты-пружины (осевая фиксация). Это сознательное разделение: бор не должен зажимать сверло трением, иначе мелкие свёрла клинит.
FDM печатает отверстия меньше номинала (усадка + раздув бисера на тугих радиусах — на ~0.2–0.4 мм
по Ø), и величина у каждого принтера/пластика/потока своя — её нельзя зашить в модель. Поэтому
зазор вынесен в один параметр socket.clearance (радиальный, дефолт 0.20 мм — типичный FDM-зазор
свободной посадки) и калибруется под вашу машину:
- Мелкие свёрла не лезут / входят с клином → поднимите
socket.clearanceшагом 0.05. - Свёрла болтаются в боре (до защёлкивания детентов) → опустите
socket.clearance. - Поднимая
clearance, при желании поднимите иsprings.bump_protrusionна столько же — чтобы сохранить силу зажима детента (≈bump_protrusion − clearance).
Точную цифру даёт калибровочный купон calibration.py — гребёнка гнёзд под несколько реальных
свёрел, раскатанная по зазорам (пружины выкл, подписи вида «3·20» = сверло 3 мм, зазор 0.20):
./drill-holder --no-gui calibration.py # печать ~10–15 мин, мало пластикаВставьте реальное сверло каждого размера, найдите минимальный зазор без клина и без люфта,
впишите его в socket.clearance. Размеры свёрел и шаг зазоров правятся вверху calibration.py.
springs.slot_widthдержите ≥ диаметра сопла (обычно ≥ 0.6–0.8 мм).- Сила зажима ≈
springs.bump_protrusion − socket.clearance; начните с дефолтов и калибруйте. Мягче вход — большеsprings.bump_radius; мягче пружина — длиннееleaf_lengthили тоньшеleaf_thickness. - Для тонких/мелких свёрел пружины могут не помещаться по окружности/глубине — тогда сборка
сама уменьшает
count/rowsдо влезающего и пишет жёлтое предупреждение (а не падает). Хард-ошибка — только если не лезет даже одна пружина/один ряд (уменьшитеsprings.leaf_width/leaf_length/slot_width, увеличьте диаметр/глубину или выключитеsprings.enabled). - Крышка: подбирайте
lid.clearanceпод усадку принтера; для откидной важенlid.hinge_clearance(зазор отверстие/ось) — обычно 0.3–0.5 мм.


