Merge branch 'main' of https://github.com/frappe/community into main
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"""
|
||||
import websocket
|
||||
import json
|
||||
import drawSvg as draw
|
||||
from .svg import SVG
|
||||
|
||||
def livecode_to_svg(livecode_ws_url, code, *, timeout=1):
|
||||
"""Renders the code as svg.
|
||||
@@ -22,7 +22,7 @@ def livecode_to_svg(livecode_ws_url, code, *, timeout=1):
|
||||
messages = _read_messages(ws)
|
||||
commands = [m['cmd'] for m in messages if m['msgtype'] == 'draw']
|
||||
img = draw_image(commands)
|
||||
return img.asSvg()
|
||||
return img.tostring()
|
||||
|
||||
def _read_messages(ws):
|
||||
messages = []
|
||||
@@ -37,8 +37,12 @@ def _read_messages(ws):
|
||||
return messages
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
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 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.sketch-card .sketch-title a {
|
||||
font-weight: bold;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_section_text(s) %}
|
||||
{{ s.contents | markdown }}
|
||||
{{ frappe.utils.md_to_html(s.contents) }}
|
||||
{% endmacro %}
|
||||
|
||||
{%- 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 ">
|
||||
{% for sketch in sketches %}
|
||||
<div class="col mb-4">
|
||||
<div class="card" style="width: 200px;">
|
||||
<div class="card sketch-card" style="width: 200px;">
|
||||
<div class="card-img-top">
|
||||
<a href="/sketches/sketch?sketch={{sketch.name}}">
|
||||
{{ sketch.to_svg() }}
|
||||
</a>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
frappe
|
||||
websocket_client
|
||||
drawSvg
|
||||
|
||||
Reference in New Issue
Block a user