Keyboard: add shared settings with new options
This commit is contained in:
parent
0c89ad2b17
commit
61b85a6aed
|
@ -14,11 +14,12 @@ class Atreus21(Boxes, Keyboard):
|
|||
half_btn = btn_size / 2
|
||||
border = 6
|
||||
|
||||
row_offsets=[3, 6, 11, 5, 0, btn_size * .5]
|
||||
row_keys=[4, 4, 4, 4, 4, 1]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.add_common_keyboard_parameters(
|
||||
# By default, columns from Atreus 21
|
||||
default_columns_definition='4@3/4@6/4@11/4@5/4@0/1@{}'.format(self.btn_size * 0.5)
|
||||
)
|
||||
|
||||
def render(self):
|
||||
"""Renders the keyboard."""
|
||||
|
@ -85,36 +86,35 @@ class Atreus21(Boxes, Keyboard):
|
|||
self.moveTo(0, b)
|
||||
|
||||
def half(self, hole_cb=None, reverse=False):
|
||||
row_offsets=self.row_offsets
|
||||
row_keys=self.row_keys
|
||||
scheme = list(zip(row_offsets, row_keys))
|
||||
if hole_cb == None:
|
||||
hole_cb = self.key
|
||||
self.moveTo(self.half_btn, self.half_btn)
|
||||
self.apply_callback_on_columns(
|
||||
hole_cb,
|
||||
scheme,
|
||||
self.columns_definition,
|
||||
self.STANDARD_KEY_SPACING,
|
||||
reverse,
|
||||
)
|
||||
self.moveTo(-self.half_btn, -self.half_btn)
|
||||
|
||||
def support(self):
|
||||
self.outer_hole()
|
||||
self.configured_plate_cutout(support=True)
|
||||
|
||||
def hotplug(self):
|
||||
self.pcb_holes()
|
||||
self.pcb_holes(
|
||||
with_hotswap=self.hotswap_enable,
|
||||
with_pcb_mount=self.pcb_mount_enable,
|
||||
with_diode=self.diode_enable,
|
||||
with_led=self.led_enable,
|
||||
)
|
||||
|
||||
def key(self):
|
||||
self.castle_shaped_plate_cutout()
|
||||
self.configured_plate_cutout()
|
||||
|
||||
# get case sizes
|
||||
def _case_x_y(self):
|
||||
margin = self.STANDARD_KEY_SPACING - self.btn_size
|
||||
x = len(self.row_offsets) * self.STANDARD_KEY_SPACING - margin
|
||||
y = sum([
|
||||
max(self.row_keys) * self.STANDARD_KEY_SPACING, # total button sizes
|
||||
max(self.row_offsets), # offset of highest row
|
||||
-margin,
|
||||
])
|
||||
spacing = Keyboard.STANDARD_KEY_SPACING
|
||||
margin = spacing - self.btn_size
|
||||
x = len(self.columns_definition) * spacing - margin
|
||||
y = max(offset + keys * spacing for (offset, keys) in self.columns_definition) - margin
|
||||
return x, y
|
||||
|
|
|
@ -15,24 +15,133 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import argparse
|
||||
from boxes import Boxes, boolarg
|
||||
|
||||
class Keyboard:
|
||||
"""
|
||||
Code to manage Cherry MX compatible switches and Kailh hotswap socket.
|
||||
|
||||
Reference :
|
||||
* https://www.cherrymx.de/en/dev.html
|
||||
* https://cdn.sparkfun.com/datasheets/Components/Switches/MX%20Series.pdf
|
||||
* https://www.kailhswitch.com/uploads/201815927/PG151101S11.pdf
|
||||
"""
|
||||
|
||||
STANDARD_KEY_SPACING = 19
|
||||
SWITCH_CASE_SIZE = 15.6
|
||||
FRAME_CUTOUT = 14
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def pcb_holes(self):
|
||||
def add_common_keyboard_parameters(
|
||||
self,
|
||||
add_hotswap_parameter=True,
|
||||
add_pcb_mount_parameter=True,
|
||||
add_led_parameter=True,
|
||||
add_diode_parameter=True,
|
||||
add_cutout_type_parameter=True,
|
||||
default_columns_definition=None,
|
||||
):
|
||||
if add_hotswap_parameter:
|
||||
self.argparser.add_argument(
|
||||
"--hotswap_enable",
|
||||
action="store",
|
||||
type=boolarg,
|
||||
default=True,
|
||||
help=("enlarge switches holes for hotswap pcb sockets"),
|
||||
)
|
||||
if add_pcb_mount_parameter:
|
||||
self.argparser.add_argument(
|
||||
"--pcb_mount_enable",
|
||||
action="store",
|
||||
type=boolarg,
|
||||
default=True,
|
||||
help=("adds holes for pcb mount switches"),
|
||||
)
|
||||
if add_led_parameter:
|
||||
self.argparser.add_argument(
|
||||
"--led_enable",
|
||||
action="store",
|
||||
type=boolarg,
|
||||
default=False,
|
||||
help=("adds pin holes under switches for leds"),
|
||||
)
|
||||
if add_diode_parameter:
|
||||
self.argparser.add_argument(
|
||||
"--diode_enable",
|
||||
action="store",
|
||||
type=boolarg,
|
||||
default=False,
|
||||
help=("adds pin holes under switches for diodes"),
|
||||
)
|
||||
if add_cutout_type_parameter:
|
||||
self.argparser.add_argument(
|
||||
"--cutout_type",
|
||||
action="store",
|
||||
type=str,
|
||||
default="castle",
|
||||
help=(
|
||||
"Shape of the plate cutout: 'castle' allows for modding, and 'simple' is a tighter and simpler square"
|
||||
),
|
||||
)
|
||||
if default_columns_definition:
|
||||
self.argparser.add_argument(
|
||||
"--columns_definition",
|
||||
type=self.argparseColumnsDefinition,
|
||||
default=default_columns_definition,
|
||||
help=(
|
||||
"Each column is separated by '/', and is in the form 'nb_rows @ offset x repeat_count'. "
|
||||
"Nb_rows is the number of rows for this column. "
|
||||
"The offset is in mm and optional. "
|
||||
"Repeat_count is optional and repeats this column multiple times. "
|
||||
"Spaces are not important."
|
||||
"For example '3x2 / 4@11' means we want 3 columns, the two first with "
|
||||
"3 rows without offset, and the last with 4 rows starting at 11mm high."
|
||||
),
|
||||
)
|
||||
|
||||
def argparseColumnsDefinition(self, s):
|
||||
"""
|
||||
Parse columns definition parameter
|
||||
|
||||
:param s: string to parse
|
||||
|
||||
Each column is separated by '/', and is in the form 'nb_rows @ offset x repeat_count'.
|
||||
Nb_rows is the number of rows for this column.
|
||||
The offset is in mm and optional.
|
||||
Repeat_count is optional and repeats this column multiple times.
|
||||
Spaces are not important.
|
||||
For example '3x2 / 4@11' means we want 3 columns, the two first with
|
||||
3 rows without offset, and the last with 4 rows starting at 11mm high
|
||||
|
||||
"""
|
||||
result = []
|
||||
try:
|
||||
for column_string in s.split("/"):
|
||||
m = re.match(r"^\s*(\d+)\s*@?\s*(\d*\.?\d*)(?:\s*x\s*(\d+))?\s*$", column_string)
|
||||
keys_count = int(m.group(1))
|
||||
offset = float(m.group(2)) if m.group(2) else 0
|
||||
n = int(m.group(3)) if m.group(3) else 1
|
||||
result.extend([(offset, keys_count)]*n)
|
||||
except:
|
||||
raise argparse.ArgumentTypeError("Don't understand columns definition string")
|
||||
|
||||
return result
|
||||
|
||||
def pcb_holes(
|
||||
self, with_hotswap=True, with_pcb_mount=True, with_led=False, with_diode=False
|
||||
):
|
||||
grid_unit = 1.27
|
||||
main_hole_size = 4
|
||||
pcb_mount_size = 1.7
|
||||
led_hole_size = 1
|
||||
pin_hole_size = 2.9
|
||||
if with_hotswap:
|
||||
pin_hole_size = 2.9
|
||||
else:
|
||||
pin_hole_size = 1.5
|
||||
|
||||
def grid_hole(x, y, d):
|
||||
self.hole(grid_unit * x, grid_unit * y, d=d)
|
||||
|
@ -44,8 +153,17 @@ class Keyboard:
|
|||
grid_hole(-3, 2, pin_hole_size)
|
||||
grid_hole(2, 4, pin_hole_size)
|
||||
|
||||
grid_hole(-4, 0, pcb_mount_size)
|
||||
grid_hole(4, 0, pcb_mount_size)
|
||||
if with_pcb_mount:
|
||||
grid_hole(-4, 0, pcb_mount_size)
|
||||
grid_hole(4, 0, pcb_mount_size)
|
||||
|
||||
if with_led:
|
||||
grid_hole(-1, -4, led_hole_size)
|
||||
grid_hole(1, -4, led_hole_size)
|
||||
|
||||
if with_diode:
|
||||
grid_hole(-3, -4, led_hole_size)
|
||||
grid_hole(3, -4, led_hole_size)
|
||||
|
||||
def apply_callback_on_columns(self, cb, columns_definition, spacing, reverse=False):
|
||||
if reverse:
|
||||
|
@ -102,4 +220,64 @@ class Keyboard:
|
|||
self.moveTo(-self.burn-0.81, -0.81)
|
||||
|
||||
if centered:
|
||||
self.moveTo(half_size, half_size)
|
||||
self.moveTo(half_size, half_size)
|
||||
|
||||
def configured_plate_cutout(self, support=False):
|
||||
"""
|
||||
Choose which cutout to use based on configured type.
|
||||
|
||||
support: if true, not the main cutout, but one to glue against the first
|
||||
1.5mm cutout to strengthen it, without the clipping part.
|
||||
"""
|
||||
if self.cutout_type.lower() == "castle":
|
||||
if support:
|
||||
self.outer_hole()
|
||||
else:
|
||||
self.castle_shaped_plate_cutout()
|
||||
else:
|
||||
self.simple_plate_cutout(with_notch=support)
|
||||
|
||||
def simple_plate_cutout(self, radius=0.2, with_notch=False):
|
||||
"""
|
||||
A simple plate cutout, a 14mm rectangle, as specified in this reference sheet
|
||||
https://cdn.sparkfun.com/datasheets/Components/Switches/MX%20Series.pdf
|
||||
|
||||
With_notch shoul be used for a secondary lower plate, strengthening the first one.
|
||||
A notch is added to let the hooks grasp the main upper plate.
|
||||
|
||||
Current position should be switch center.
|
||||
|
||||
Radius should be lower or equal to 0.3 mm
|
||||
"""
|
||||
size = Keyboard.FRAME_CUTOUT
|
||||
half_size = size / 2
|
||||
|
||||
if with_notch:
|
||||
notch_length = 5
|
||||
notch_depth = 1
|
||||
straight_part = 0.5 * (size - 2 * radius - 2 * notch_depth - notch_length)
|
||||
|
||||
self.moveTo(-half_size + self.burn, 0, 90)
|
||||
polyline_quarter = [
|
||||
half_size - radius,
|
||||
(-90, radius),
|
||||
straight_part,
|
||||
(90, notch_depth / 2),
|
||||
0,
|
||||
(-90, notch_depth / 2),
|
||||
notch_length / 2,
|
||||
]
|
||||
polyline = (
|
||||
polyline_quarter
|
||||
+ [0]
|
||||
+ list(reversed(polyline_quarter))
|
||||
+ [0]
|
||||
+ polyline_quarter
|
||||
+ [0]
|
||||
+ list(reversed(polyline_quarter))
|
||||
)
|
||||
self.polyline(*polyline)
|
||||
self.moveTo(0, 0, -90)
|
||||
self.moveTo(half_size - self.burn)
|
||||
else:
|
||||
self.rectangularHole(0, 0, size, size, r=radius)
|
|
@ -21,14 +21,6 @@ class Keypad(Boxes, Keyboard):
|
|||
'--h', action='store', type=int, default=30,
|
||||
help='height of the box'
|
||||
)
|
||||
self.argparser.add_argument(
|
||||
'--btn_x', action='store', type=int, default=3,
|
||||
help='number of buttons per row'
|
||||
)
|
||||
self.argparser.add_argument(
|
||||
'--btn_y', action='store', type=int, default=4,
|
||||
help='number of buttons per column'
|
||||
)
|
||||
self.argparser.add_argument(
|
||||
'--top1_thickness', action='store', type=float, default=1.5,
|
||||
help=('thickness of the button hold layer, cherry like switches '
|
||||
|
@ -37,19 +29,31 @@ class Keypad(Boxes, Keyboard):
|
|||
self.argparser.add_argument(
|
||||
'--top2_enable', action='store', type=boolarg, default=False,
|
||||
help=('enables another top layer that can hold CPG151101S11 '
|
||||
'sockets')
|
||||
'hotswap sockets')
|
||||
)
|
||||
self.argparser.add_argument(
|
||||
'--top2_thickness', action='store', type=float, default=1.5,
|
||||
help=('thickness of the hotplug layer, CPG151101S11 sockets '
|
||||
'need 1.2mm to 1.5mm')
|
||||
help=('thickness of the hotplug layer, CPG151101S11 hotswap '
|
||||
'sockets need 1.2mm to 1.5mm')
|
||||
)
|
||||
|
||||
# Add parameter common with other keyboard projects
|
||||
self.add_common_keyboard_parameters(
|
||||
# Hotswap already depends on top2_enable setting, a second parameter
|
||||
# for it would be useless
|
||||
add_hotswap_parameter=False,
|
||||
# By default, 3 columns of 4 rows
|
||||
default_columns_definition="4x3"
|
||||
)
|
||||
|
||||
self.addSettingsArgs(FingerJointSettings, surroundingspaces=1)
|
||||
|
||||
def _get_x_y(self):
|
||||
"""Gets the keypad's size based on the number of buttons."""
|
||||
x = self.btn_x * (self.btn_size) + (self.btn_x - 1) * self.space_between_btn + 2*self.box_padding
|
||||
y = self.btn_y * (self.btn_size) + (self.btn_y - 1) * self.space_between_btn + 2*self.box_padding
|
||||
spacing = self.btn_size + self.space_between_btn
|
||||
border = 2*self.box_padding - self.space_between_btn
|
||||
x = len(self.columns_definition) * spacing + border
|
||||
y = max(offset + keys * spacing for (offset, keys) in self.columns_definition) + border
|
||||
return x, y
|
||||
|
||||
def render(self):
|
||||
|
@ -91,26 +95,29 @@ class Keypad(Boxes, Keyboard):
|
|||
)
|
||||
|
||||
def to_grid_callback(self, inner_callback):
|
||||
scheme = [(0, self.btn_y)]*self.btn_x
|
||||
def callback():
|
||||
# move to first key center
|
||||
key_margin = self.box_padding + self.btn_size / 2
|
||||
self.moveTo(key_margin, key_margin)
|
||||
self.apply_callback_on_columns(
|
||||
inner_callback, scheme, self.btn_size + self.space_between_btn
|
||||
inner_callback, self.columns_definition, self.btn_size + self.space_between_btn
|
||||
)
|
||||
|
||||
return [callback]
|
||||
|
||||
def hotplug(self):
|
||||
"""Callback for the key stabelizers."""
|
||||
self.pcb_holes()
|
||||
self.pcb_holes(
|
||||
with_pcb_mount=self.pcb_mount_enable,
|
||||
with_diode=self.diode_enable,
|
||||
with_led=self.led_enable,
|
||||
)
|
||||
|
||||
def support_hole(self):
|
||||
self.outer_hole()
|
||||
self.configured_plate_cutout(support=True)
|
||||
|
||||
def key_hole(self):
|
||||
self.castle_shaped_plate_cutout()
|
||||
self.configured_plate_cutout()
|
||||
|
||||
# stolen form electronics-box
|
||||
def wallx_cb(self):
|
||||
|
|
Loading…
Reference in New Issue