Skip to content

Commit

Permalink
stm_layout_tk: Add Tkinter-based version of stm_layout.
Browse files Browse the repository at this point in the history
  • Loading branch information
tgree committed Feb 13, 2022
1 parent ac8ce80 commit 5d00de6
Show file tree
Hide file tree
Showing 10 changed files with 686 additions and 4 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ MODULE_VERS := 0.1.3
MODULE_DEPS := \
setup.cfg \
setup.py \
stm_layout/*.py
stm_layout/*.py \
stm_layout/tk/*.py \

FLAKE_MODULES := stm_layout
LINT_MODULES := stm_layout
Expand Down
13 changes: 11 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Curses-based tool for configuring STM32 pins.
=============================================
Curses- and Tkinter-based tool for configuring STM32 pins.
==========================================================

This tool uses a fork of the amazing curated .xml from the modm-devices
project. The modm-devices project provides metadata about all STM32 devices
Expand Down Expand Up @@ -29,3 +29,12 @@ queries in the search bar. In any pane but the search pane::
The stm32_pinout.txt is an attempt to configure all the GPIO registers for
your chip; it is woefully incomplete for anything except the H7 and G4 chips
I have access to.

Usage::

stm_layout_tk -c <chip_name>

This command launches a Tkinter-based version of stm_layout that allows mouse
navigation and a more compact display for larger MCUs. It does not attempt to
implement the pin-configuration "feature" that the curses-based tool does and
is more useful as a simple reference.
5 changes: 4 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ classifiers =

[options]
python_requires = >=3.6
packages = stm_layout
packages =
stm_layout
stm_layout.tk
install_requires =
modm-devices
tgcurses

[options.entry_points]
console_scripts =
stm_layout = stm_layout.stm_layout:_main
stm_layout_tk = stm_layout.stm_layout_tk:_main
58 changes: 58 additions & 0 deletions stm_layout/stm_layout_tk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
import argparse
import sys

from stm_layout import chip_db, chip_stm, chip_package
import stm_layout.tk


FONT = ('Monaco', 10)
FONT_PIN_NUM = ('Monaco', 9)
FONT_INFO = ('Monaco', 10)
RECT_FILL = 'white'
HILITE_FILL = 'lightblue'
SELECT_FILL = 'yellow'
RE_FILL = 'lightgreen'


def main(chip, regex):
if isinstance(chip.chip, chip_package.LQFP):
cls = stm_layout.tk.LQFPWorkspace
elif isinstance(chip.chip, chip_package.BGA):
cls = stm_layout.tk.BGAWorkspace
elif isinstance(chip.chip, chip_package.TSSOP):
cls = stm_layout.tk.TSSOPWorkspace
else:
raise Exception('Unsupported chip package.')

ws = cls(chip, FONT, FONT_PIN_NUM, FONT_INFO, RECT_FILL, HILITE_FILL,
SELECT_FILL, RE_FILL)
if regex:
ws.set_regex(regex)

ws.mainloop()


def _main():
parser = argparse.ArgumentParser()
parser.add_argument('--chip', '-c', required=True)
parser.add_argument('--regex')
rv = parser.parse_args()

parts = chip_db.find(rv.chip)
if not parts:
print('No devices found for "%s"' % rv.chip)
sys.exit(1)
part = next( (p for p in parts if rv.chip == p.partname), None)
if part is None:
print('Multiple devices found for "%s"' % rv.chip)
for p in parts:
print('%s - %s' % (p, chip_db.package(p)))
sys.exit(1)
else:
chip = chip_stm.make_chip(part)
main(chip, rv.regex)


if __name__ == '__main__':
_main()
9 changes: 9 additions & 0 deletions stm_layout/tk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .tk_bga import BGAWorkspace
from .tk_lqfp import LQFPWorkspace
from .tk_tssop import TSSOPWorkspace


__all__ = ['BGAWorkspace',
'LQFPWorkspace',
'TSSOPWorkspace',
]
44 changes: 44 additions & 0 deletions stm_layout/tk/tk_bga.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from . import tk_workspace


PIN_DIAM = 30
PIN_SPACE = 22
PIN_DELTA = (PIN_DIAM + PIN_SPACE)


class BGAWorkspace(tk_workspace.Workspace):
def __init__(self, *args):
super().__init__(*args)

cw = self.chip.width
ch = self.chip.height
w = cw*PIN_DELTA + PIN_SPACE
h = ch*PIN_DELTA + PIN_SPACE + self.label_font.metrics('ascent')
pad = 15
self.set_geometry(50, 50, w + 2*pad + self.info_width,
max(h + 2*pad, self.info_height))

c = self.mcu_canvas = self.add_canvas(w + 2*pad, h + 2*pad)
self._root.columnconfigure(0, weight=1)
self._root.rowconfigure(0, weight=1)

m = c.add_rectangle(pad, pad, w, h, fill=self.elem_fill)
for x in range(cw):
for y in range(ch):
p = self.chip.chip.pins[x][y]
if p is None:
continue

o = c.add_oval(
m.x + PIN_SPACE + x*PIN_DELTA,
m.y + PIN_SPACE + y*PIN_DELTA,
PIN_DIAM, PIN_DIAM,
fill=self.elem_fill)
self.pin_elems.append(o)
c.add_text(
o.x + o.width / 2, o.y + o.height,
font=self.label_font, text=p.name, anchor='n')
c.add_text(
o.x + o.width / 2 + 1, o.y + o.height / 2,
font=self.pin_font, text=p.key, anchor='c')
o.pin = p
140 changes: 140 additions & 0 deletions stm_layout/tk/tk_elems.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import tkinter


class Elem:
def __init__(self, elem_id):
self._elem_id = elem_id


class CanvasElem(Elem):
def __init__(self, canvas, elem_id, x, y, width=None, height=None):
super().__init__(elem_id)
self._canvas = canvas
self.x = x
self.y = y
if width is not None:
self.width = width
if height is not None:
self.height = height

def bbox(self):
return self._canvas._bbox(self)

def tag_lower(self, bottom_elem):
self._canvas._tag_lower(self, bottom_elem)

def set_fill(self, fill):
self._canvas._set_fill(self, fill)

def contains(self, x, y):
w = getattr(self, 'width', 0)
h = getattr(self, 'height', 0)
return self.x <= x <= self.x + w and self.y <= y <= self.y + h

def distance_squared(self, x, y):
cx = self.x + getattr(self, 'width', 0) / 2
cy = self.y + getattr(self, 'height', 0) / 2
dx = (x - cx)
dy = (y - cy)
return dx*dx + dy*dy

def move_to(self, x, y):
self.x = x
self.y = y
if self.height is not None:
self._canvas._move_to(self, x, y, x + self.width, y + self.height)
else:
self._canvas._move_to(self, x, y)

def resize(self, x, y, width, height):
assert self.width is not None
assert self.height is not None
self.x = x
self.y = y
self.width = width
self.height = height
self._canvas._move_to(self, x, y, width, height)


class TextElem(CanvasElem):
def set_text(self, text):
self._canvas._set_text(self, text)


class Widget:
def __init__(self, widget):
self._widget = widget


class Entry(Widget):
def focus_set(self):
self._widget.focus_set()


class Canvas:
def __init__(self, canvas):
self._canvas = canvas

def _bbox(self, elem):
return self._canvas.bbox(elem._elem_id)

def _tag_lower(self, bottom_elem, top_elem):
self._canvas.tag_lower(bottom_elem._elem_id, top_elem._elem_id)

def _set_fill(self, elem, fill):
self._canvas.itemconfig(elem._elem_id, fill=fill)

def _move_to(self, elem, *args):
self._canvas.coords(elem._elem_id, *args)

def _set_text(self, elem, text):
self._canvas.itemconfig(elem._elem_id, text=text)

def add_rectangle(self, x, y, width, height, **kwargs):
elem_id = self._canvas.create_rectangle(
(x, y, x + width, y + height), **kwargs)
return CanvasElem(self, elem_id, x, y, width=width, height=height)

def add_oval(self, x, y, width, height, **kwargs):
elem_id = self._canvas.create_oval(
(x, y, x + width, y + height), **kwargs)
return CanvasElem(self, elem_id, x, y, width=width, height=height)

def add_text(self, x, y, **kwargs):
elem_id = self._canvas.create_text((x, y), **kwargs)
return TextElem(self, elem_id, x, y)

def add_window(self, x, y, widget, **kwargs):
self._canvas.create_window(x, y, window=widget._widget, **kwargs)

def add_entry(self, **kwargs):
return Entry(tkinter.Entry(self._canvas, **kwargs))


class TKBase:
def __init__(self):
self._root = tkinter.Tk()

def set_geometry(self, x, y, width, height):
self._root.geometry('%ux%u+%u+%u' % (width, height, x, y))

def mainloop(self):
self._root.mainloop()

def add_canvas(self, width, height, column=0, row=0, sticky=None):
c = tkinter.Canvas(self._root, bd=0, highlightthickness=0, width=width,
height=height)
c.grid(column=column, row=row, sticky=sticky)
return Canvas(c)

def register_handler(self, event_type, handler):
self._root.bind(event_type, lambda e: handler(self, e, e.x, e.y))

def register_mouse_moved(self, handler):
self.register_handler('<Motion>', handler)

def register_mouse_down(self, handler):
self.register_handler('<Button-1>', handler)

def register_mouse_up(self, handler):
self.register_handler('<ButtonRelease-1>', handler)
Loading

0 comments on commit 5d00de6

Please sign in to comment.