Add a generic, text based front panel generator

This adds a front panel generator that lets you automatically generate
all the cutouts and text placements needed in a box.

Eventually, it might be great it integrate this kind of feature into the
box generators, but that sounds complicated.  At this point, I'm happy]
to have this.
This commit is contained in:
caleb crome 2023-01-26 17:43:06 -08:00 committed by Florian Festi
parent 4ef6c5c5ae
commit 4c9830315d
5 changed files with 222 additions and 0 deletions

View File

@ -0,0 +1,217 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2017 Florian Festi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from boxes import *
import io
import shlex
def str_to_bool(s):
if (s.lower() in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'uh-huh']):
return True
else:
return False
class FrontPanel(Boxes):
"""Mounting Holes and cutouts for all your holy needs."""
description = f"""
<script type="module" src="https://md-block.verou.me/md-block.js"></script>
<md-block>
This will help you create font (and side and top) panels for your
boxes that are pre-configured for all the bits and bobs you'd like to
install
The layout can create several types of holes including rectangles,
circles and mounting holes. The default shows an example layout with all
currently supported objects.
####
`rect x y w h [cr=0] [cx=True] [cy=True]`
x: x position
y: y position
w: width
h: height
cr: optional, Corner radius, default=0
cx: optional, Center x. the x position denotes the center of the rectangle.
accepts t, T, 1, or other true-like values.
cy: optional, Center y. the y position denotes the center of the rectangle.
#### outline
`rect w h`
w: width
h: height
`outline` has a special meaning: You can create multiple panel outlines with one command.
This has the effect of making it easy to manage all the holes on all the sides of
your boxes.
#### circle
`circle x y r`
x: x position
y: y position
r: radius
#### mountinghole
mountinghole x y d_shaft [d_head=0] [angle=0]
x: x position
y: y position
d_shaft: diameter of the shaft part of the mounting hole
d_head: optional. diameter of the head
angle: optional. angle of the mounting hole
#### text
`text x y size "some text" [angle=0] [align=bottom|left]`
x: x position
y: y position
size: size, in mm
text: text to render. This *must* be in quotation marks
angle: angle (in degrees)
align: string with combinations of (top|middle|bottom) and (left|center|right),
separated by '|'. Default is 'bottom|left'
#### nema
`nema x y size [screwhole_size=0]`
x: x position (center of shaft)
y: y position (center of shaft)
size: nema size. One of [{', '.join([f'{x}' for x in Boxes.nema_sizes])}]
screw: screw size, in mm. Optional. Default=0, which means the default size
</md-block>
"""
ui_group = "Holes"
def __init__(self) -> None:
Boxes.__init__(self)
self.argparser.add_argument(
"--layout", action="store", type=str,
default="""
outline 100 100
rect 50 60 80 30 3 True False
text 50 91 7 "Super Front Panel With Buttons!" 0 bottom|center
circle 10 45 3.5
circle 30 45 3.5
circle 50 45 3.5
circle 70 45 3.5
circle 90 45 3.5
text 10 40 3 "BTN_1" 0 top|center
text 35 45 3 "BTN_2" 90 top|center
text 50 50 3 "BTN_3" 180 top|center
text 65 45 3 "BTN_4" 270 top|center
text 90 45 3 "5" 0 middle|center
mountinghole 5 85 3 6 90
mountinghole 95 85 3 6 90
# Start another panel, 30x50
outline 30 50
rect 15 25 15 15 1 True True
text 15 25 3 "__Fun!" 0 bottom|left
text 15 25 3 "__Fun!" 45 bottom|left
text 15 25 3 "__Fun!" 90 bottom|left
text 15 25 3 "__Fun!" 135 bottom|left
text 15 25 3 "__Fun!" 180 bottom|left
text 15 25 3 "__Fun!" 225 bottom|left
text 15 25 3 "__Fun!" 270 bottom|left
text 3 10 2 "Another panel, for fun" 0 top|left
# Let's create another panel with a nema motor on it
outline 40 40
nema 20 20 17
""")
def applyOffset(self, x, y):
return (x+self.offset[0], y+self.offset[1])
def drawRect(self, x, y, w, h, r=0, center_x="True", center_y="True"):
x, y, w, h, r = [float(i) for i in [x, y, w, h, r]]
x, y = self.applyOffset(x, y)
center_x = str_to_bool(center_x)
center_y = str_to_bool(center_y)
self.rectangularHole(x, y, w, h, r, center_x, center_y)
return
def drawCircle(self, x, y, r):
x, y, r = [float(i) for i in [x, y, r]]
x, y = self.applyOffset(x, y)
self.hole(x, y, r)
return
def drawMountingHole(self, x, y, d_shaft, d_head=0.0, angle=0):
x, y, d_shaft, d_head, angle = [float(i) for i in [x, y, d_shaft, d_head, angle]]
x, y = self.applyOffset(x, y)
self.mountingHole(x, y, d_shaft, d_head, angle)
return
def drawOutline(self, w, h):
w, h = [float(i) for i in [w, h]]
if self.outline is not None:
self.offset = self.applyOffset(self.outline[0]+10, 0)
self.outline = (w, h) # store away for next time
x = 0
y = 0
x, y = self.applyOffset(x, y)
border = [(x, y), (x+w, y), (x+w, y+h), (x, y+h), (x, y)]
self.showBorderPoly( border )
return
def drawText(self, x, y, size, text, angle=0, align='bottom|left'):
x, y, size, angle = [float(i) for i in [x, y, size, angle]]
x, y = self.applyOffset(x, y)
align = align.replace("|", " ")
self.text(text=text, x=x, y=y, fontsize=size, angle=angle, align=align)
def drawNema(self, x, y, size, screwhole_size=0):
x, y, size, screwhole_size = [float(i) for i in [x, y, size, screwhole_size]]
if size in self.nema_sizes:
x, y = self.applyOffset(x, y)
self.NEMA(size, x, y, screwholes=screwhole_size)
def parse_layout(self, layout):
f = io.StringIO(layout)
line = 0
objects = {
'outline': self.drawOutline,
'rect': self.drawRect,
'circle': self.drawCircle,
'mountinghole': self.drawMountingHole,
'text': self.drawText,
'nema': self.drawNema,
}
for l in f.readlines():
line += 1
l = re.sub('#.*$', '', l) # remove comments
l = l.strip()
la = shlex.split(l, comments=True, posix=True)
if len(la) > 0 and la[0].lower() in objects:
objects[la[0]](*la[1:])
return
def render(self):
self.offset = (0.0, 0.0)
self.outline = None # No outline yet
self.parse_layout(self.layout)

View File

@ -0,0 +1,4 @@
outline 60 60
squre 10 20 25 34
circle 10 20 40

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -116,3 +116,4 @@ a7ee83b42685295fd02db666841984c55af2f9632540c651f5dea42088a3c170 ../static/samp
ab47e02fb9736d20b457964aa640f50852ac97bb2857cea753fa4c3067a60d41 ../static/samples/Spool.jpg
cfc0782f3a952dd0c6ceb0fb7998050f3bdea135d791fa32b731808371514e63 ../static/samples/GearBox.jpg
75733dfdfd601ace1521bddfea28546ca34d8281acbeb6ec44f15b2b942cb944 ../static/samples/Arcade.jpg
a21471512fd73c15e1d8a11aa3bd4ef807791895855c2f1d30a00bd207d79919 ../static/samples/FrontPanel.jpg