Merge branch 'main' of https://github.com/frappe/community into main
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
import websocket
|
import websocket
|
||||||
import json
|
import json
|
||||||
import drawSvg as draw
|
from .svg import SVG
|
||||||
|
|
||||||
def livecode_to_svg(livecode_ws_url, code, *, timeout=1):
|
def livecode_to_svg(livecode_ws_url, code, *, timeout=1):
|
||||||
"""Renders the code as svg.
|
"""Renders the code as svg.
|
||||||
@@ -22,7 +22,7 @@ def livecode_to_svg(livecode_ws_url, code, *, timeout=1):
|
|||||||
messages = _read_messages(ws)
|
messages = _read_messages(ws)
|
||||||
commands = [m['cmd'] for m in messages if m['msgtype'] == 'draw']
|
commands = [m['cmd'] for m in messages if m['msgtype'] == 'draw']
|
||||||
img = draw_image(commands)
|
img = draw_image(commands)
|
||||||
return img.asSvg()
|
return img.tostring()
|
||||||
|
|
||||||
def _read_messages(ws):
|
def _read_messages(ws):
|
||||||
messages = []
|
messages = []
|
||||||
@@ -37,8 +37,12 @@ def _read_messages(ws):
|
|||||||
return messages
|
return messages
|
||||||
|
|
||||||
def draw_image(commands):
|
def draw_image(commands):
|
||||||
img = draw.Drawing(300, 300, fill='none', stroke='black')
|
img = SVG(width=300, height=300, viewBox="0 0 300 300", fill='none', stroke='black')
|
||||||
for c in commands:
|
for c in commands:
|
||||||
if c['function'] == 'circle':
|
if c['function'] == 'circle':
|
||||||
img.append(draw.Circle(cx=c['x'], cy=c['y'], r=c['d']/2))
|
img.circle(cx=c['x'], cy=c['y'], r=c['d']/2)
|
||||||
|
elif c['function'] == 'line':
|
||||||
|
img.line(x1=c['x1'], y1=c['y1'], x2=c['x2'], y2=c['y2'])
|
||||||
|
elif c['function'] == 'rect':
|
||||||
|
img.rect(x=c['x'], y=c['y'], width=c['w'], height=c['h'])
|
||||||
return img
|
return img
|
||||||
|
|||||||
143
community/lms/doctype/lms_sketch/svg.py
Normal file
143
community/lms/doctype/lms_sketch/svg.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
"""SVG rendering library.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
from svg import SVG
|
||||||
|
|
||||||
|
svg = SVG(width=200, height=200)
|
||||||
|
svg.circle(cx=100, cy=200, r=50)
|
||||||
|
print(svg.tostring())
|
||||||
|
"""
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
|
TAGNAMES = set([
|
||||||
|
"circle", "ellipse",
|
||||||
|
"line", "path", "rect", "polygon", "polyline",
|
||||||
|
"text", "textPath", "title",
|
||||||
|
"marker", "defs",
|
||||||
|
"g"
|
||||||
|
])
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
"""SVG Node"""
|
||||||
|
def __init__(self, tag, **attrs):
|
||||||
|
self.tag = tag
|
||||||
|
self.attrs = dict((k.replace('_', '-'), str(v)) for k, v in attrs.items())
|
||||||
|
self.children = []
|
||||||
|
|
||||||
|
def node(self, tag, **attrs):
|
||||||
|
n = Node(tag, **attrs)
|
||||||
|
self.children.append(n)
|
||||||
|
return n
|
||||||
|
|
||||||
|
def apply(self, func):
|
||||||
|
"""Applies a function to this node and
|
||||||
|
all the children recursively.
|
||||||
|
"""
|
||||||
|
func(self)
|
||||||
|
for n in self.children:
|
||||||
|
n.apply(func)
|
||||||
|
|
||||||
|
def clone(self):
|
||||||
|
node = Node(self.tag, **self.attrs)
|
||||||
|
node.children = [n.clone() for n in self.children]
|
||||||
|
return node
|
||||||
|
|
||||||
|
def add_node(self, node):
|
||||||
|
if not isinstance(node, Node):
|
||||||
|
node = Text(node)
|
||||||
|
self.children.append(node)
|
||||||
|
|
||||||
|
def __getattr__(self, tag):
|
||||||
|
if tag not in TAGNAMES:
|
||||||
|
raise AttributeError(tag)
|
||||||
|
return lambda **attrs: self.node(tag, **attrs)
|
||||||
|
|
||||||
|
def translate(self, x, y):
|
||||||
|
return self.g(transform="translate(%s, %s)" % (x, y))
|
||||||
|
|
||||||
|
def scale(self, *args):
|
||||||
|
return self.g(transform="scale(%s)" % ", ".join(str(a) for a in args))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s .../>" % self.tag
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def build_tree(self, builder):
|
||||||
|
builder.start(self.tag, self.attrs)
|
||||||
|
for node in self.children:
|
||||||
|
node.build_tree(builder)
|
||||||
|
return builder.end(self.tag)
|
||||||
|
|
||||||
|
def _indent(self, elem, level=0):
|
||||||
|
"""Indent etree node for prettyprinting."""
|
||||||
|
|
||||||
|
i = "\n" + level*" "
|
||||||
|
if len(elem):
|
||||||
|
if not elem.text or not elem.text.strip():
|
||||||
|
elem.text = i + " "
|
||||||
|
if not elem.tail or not elem.tail.strip():
|
||||||
|
elem.tail = i
|
||||||
|
for elem in elem:
|
||||||
|
self._indent(elem, level+1)
|
||||||
|
if not elem.tail or not elem.tail.strip():
|
||||||
|
elem.tail = i
|
||||||
|
else:
|
||||||
|
if level and (not elem.tail or not elem.tail.strip()):
|
||||||
|
elem.tail = i
|
||||||
|
|
||||||
|
def save(self, filename, encoding='utf-8'):
|
||||||
|
f = open(filename, 'w')
|
||||||
|
f.write(self.tostring())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def tostring(self, encoding='utf-8'):
|
||||||
|
builder = ElementTree.TreeBuilder()
|
||||||
|
self.build_tree(builder)
|
||||||
|
e = builder.close()
|
||||||
|
self._indent(e)
|
||||||
|
return ElementTree.tostring(e, encoding).decode(encoding)
|
||||||
|
|
||||||
|
class Text(Node):
|
||||||
|
"""Text Node
|
||||||
|
|
||||||
|
>>> p = Node("p")
|
||||||
|
>>> p.add_node("hello, world!")
|
||||||
|
>>> p.tostring()
|
||||||
|
'<p>hello, world!</p>'
|
||||||
|
"""
|
||||||
|
def __init__(self, text):
|
||||||
|
Node.__init__(self, "__text__")
|
||||||
|
self._text = text
|
||||||
|
|
||||||
|
def build_tree(self, builder):
|
||||||
|
builder.data(str(self._text))
|
||||||
|
|
||||||
|
class SVG(Node):
|
||||||
|
"""
|
||||||
|
>>> svg = SVG(width=200, height=200)
|
||||||
|
>>> svg.rect(x=0, y=0, width=200, height=200, fill="blue")
|
||||||
|
<rect .../>
|
||||||
|
>>> with svg.translate(-50, -50) as g:
|
||||||
|
... g.rect(x=0, y=0, width=50, height=100, fill="red")
|
||||||
|
... g.rect(x=50, y=0, width=50, height=100, fill="green")
|
||||||
|
<rect .../>
|
||||||
|
<rect .../>
|
||||||
|
>>> print(svg.tostring())
|
||||||
|
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0" y="0" width="200" height="200" fill="blue" />
|
||||||
|
<g transform="translate(-50, -50)">
|
||||||
|
<rect x="0" y="0" width="50" height="100" fill="red" />
|
||||||
|
<rect x="50" y="0" width="50" height="100" fill="green" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, **attrs):
|
||||||
|
attrs['xmlns'] = "http://www.w3.org/2000/svg"
|
||||||
|
Node.__init__(self, 'svg', **attrs)
|
||||||
|
|
||||||
@@ -62,3 +62,8 @@
|
|||||||
.sketch-header {
|
.sketch-header {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sketch-card .sketch-title a {
|
||||||
|
font-weight: bold;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_section_text(s) %}
|
{% macro render_section_text(s) %}
|
||||||
{{ s.contents | markdown }}
|
{{ frappe.utils.md_to_html(s.contents) }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{%- block script %}
|
{%- block script %}
|
||||||
|
|||||||
@@ -19,14 +19,19 @@
|
|||||||
<div class="row row-cols-1 row-cols-xl-5 row-cols-lg-4 row-cols-md-3 row-cols-sm-2 ">
|
<div class="row row-cols-1 row-cols-xl-5 row-cols-lg-4 row-cols-md-3 row-cols-sm-2 ">
|
||||||
{% for sketch in sketches %}
|
{% for sketch in sketches %}
|
||||||
<div class="col mb-4">
|
<div class="col mb-4">
|
||||||
<div class="card" style="width: 200px;">
|
<div class="card sketch-card" style="width: 200px;">
|
||||||
<div class="card-img-top">
|
<div class="card-img-top">
|
||||||
<a href="/sketches/sketch?sketch={{sketch.name}}">
|
<a href="/sketches/sketch?sketch={{sketch.name}}">
|
||||||
{{ sketch.to_svg() }}
|
{{ sketch.to_svg() }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
By {{sketch.get_owner_name()}}
|
<div class="sketch-title">
|
||||||
|
<a href="sketches/sketch?sketch={{sketch.name}}">{{sketch.title}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="sketch-author">
|
||||||
|
by {{sketch.get_owner_name()}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
frappe
|
frappe
|
||||||
websocket_client
|
websocket_client
|
||||||
drawSvg
|
|
||||||
|
|||||||
Reference in New Issue
Block a user