Incorporated svg-pan-zoom in main window. Needed to add Python webserver to permit svg-pan-zoom to work in subsidiary windows. Still issues about filter coloring entire paired window div

This commit is contained in:
John Pateman 2020-03-01 20:57:06 +00:00
parent 9519e00015
commit f432f8a904
4 changed files with 545 additions and 108 deletions

433
Plots/web/style.css Normal file
View File

@ -0,0 +1,433 @@
/* style.css */
body {
background-color: #282C35;
}
.gallery {
border: 1px solid #ccc;
}
.gallery:hover {
border: 1px solid #777;
}
.gallery img {
width: 100%;
height: auto;
}
.desc {
padding: 15px;
text-align: center;
font: 15px arial, sans-serif;
}
.title {
padding: 15px;
text-align: left;
font: 25px arial, sans-serif;
color: #202b34;
}
.subtitle {
padding: 5px;
text-align: left;
font: 20px arial, sans-serif;
color: #000000;
}
.details {
padding: 20px;
text-align: left;
font: 15px arial, sans-serif;
color: #000000;
}
.differences{
padding: 5px;
text-align: left;
font: 18px courier, monospace;
color: #000000;
}
.th {
padding: 5px;
text-align: left;
font: 20px arial, sans-serif;
font-weight: bold;
color: #000000;
}
.td {
padding: 5px;
text-align: left;
font: 15px arial, sans-serif;
color: #000000;
}
* {
box-sizing: border-box;
}
.responsive {
padding: 0 6px;
float: left;
width: 19.99999%;
margin: 6px 0;
}
@media only screen and (max-width: 700px) {
.responsive {
width: 49.98%;
margin: 6px 0;
}
}
@media only screen and (max-width: 500px) {
.responsive {
width: 100%;
margin: 6px 0;
}
}
.clearfix:after {
content: "";
display: table;
clear: both;
}
.box {
float: left;
width: 20px;
height: 20px;
margin: 5px;
border: 1px solid rgba(0, 0, 0, .2);
}
.red {
background: #F40008;
}
.green {
background: #43ff01;
}
.white {
background: #ffffff;
}
.added {
color: #5EB6C4;
}
.removed {
color: #BA312D;
}
@import url(http://fonts.googleapis.com/css?family=Inconsolata);
@import url(http://fonts.googleapis.com/css?family=PT+Sans);
@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700);
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
audio,
canvas,
video {
display: inline-block;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden] {
display: none;
}
html {
font-family: sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
body {
margin: 0;
}
a:focus {
outline: thin dotted;
}
a:active,
a:hover {
outline: 0;
}
h1 {
font-size: 2em;
}
abbr[title] {
border-bottom: 1px dotted;
}
b,
strong {
font-weight: bold;
}
dfn {
font-style: italic;
}
mark {
background: #ff0;
color: #000;
}
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
img {
border: 0;
}
svg:not(:root) {
overflow: hidden;
}
figure {
margin: 0;
}
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
legend {
border: 0;
padding: 0;
}
button,
input,
select,
textarea {
font-family: inherit;
font-size: 100%;
margin: 0;
}
button,
input {
line-height: normal;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button;
cursor: pointer;
}
button[disabled],
input[disabled] {
cursor: default;
}
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box;
padding: 0;
}
input[type="search"] {
-webkit-appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
textarea {
overflow: auto;
vertical-align: top;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
html {
font-family: 'PT Sans', sans-serif;
}
pre,
code {
font-family: 'Inconsolata', sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'PT Sans Narrow', sans-serif;
font-weight: 700;
}
html {
background-color: #073642;
color: #839496;
margin: 1em;
}
body {
background-color: #002b36;
margin: 0 auto;
max-width: 40cm;
border: 1pt solid #586e75;
padding: 1em;
}
code {
background-color: #073642;
padding: 2px;
}
a {
color: #b58900;
}
a:visited {
color: #cb4b16;
}
a:hover {
color: #cb4b16;
}
h1 {
color: #d33682;
}
h2,
h3,
h4,
h5,
h6 {
color: #859900;
}
pre {
background-color: #002b36;
color: #839496;
border: 1pt solid #586e75;
padding: 1em;
box-shadow: 5pt 5pt 8pt #073642;
}
pre code {
background-color: #002b36;
}
h1 {
font-size: 2.8em;
}
h2 {
font-size: 2.4em;
}
h3 {
font-size: 1.8em;
}
h4 {
font-size: 1.4em;
}
h5 {
font-size: 1.3em;
}
h6 {
font-size: 1.15em;
}
.tag {
background-color: #073642;
color: #d33682;
padding: 0 0.2em;
}
.todo,
.next,
.done {
color: #002b36;
background-color: #dc322f;
padding: 0 0.2em;
}
.tag {
-webkit-border-radius: 0.35em;
-moz-border-radius: 0.35em;
border-radius: 0.35em;
}
.TODO {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #2aa198;
}
.NEXT {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #268bd2;
}
.ACTIVE {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #268bd2;
}
.DONE {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #859900;
}
.WAITING {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #cb4b16;
}
.HOLD {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #d33682;
}
.NOTE {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #d33682;
}
.CANCELLED {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #859900;
}

View File

@ -4,7 +4,7 @@
# held in a suitable version control repository and produce a graphical diff
# of generated svg files in a web browser.
# TODO Place all template text/css text in exteranl files
# TODO Place all template text/css text in external files
import os
import time
@ -17,6 +17,8 @@ from tkinter import filedialog, ttk
from tkinter.messagebox import showinfo
import tkUI
from tkUI import *
import http.server
import socketserver
# NOTE Adjust these paths to suit your setup
@ -28,6 +30,9 @@ webDir = '/web'
diffProg = '/usr/bin/diff'
plotProg = '/usr/local/bin/plotPCB2_DIMS.py'
PORT = 9090
Handler = http.server.SimpleHTTPRequestHandler
layerCols = {
'F_Cu': "#952927",
@ -200,115 +205,98 @@ div.responsive {{
<div class="title">{prj}</div>
<div class="subtitle">{layer}</div>
<div class="responsivefull">
<div class="gallery">
<svg id="composite" width="100%" height="100%" viewBox="0 0 11693 8268" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="f1" x="0%" y="0%" width="100%" height="100%">
<feColorMatrix result="original" id="c1" type="matrix" values="1 0 0 0 0
0 1 0 1 0
0 0 1 1 0
0 0 0 1 0 " />
</filter>
<filter id="f2" x="0%" y="0%" width="100%" height="100%">
<feColorMatrix result="original" id="c2" type="matrix" values="1 0 0 1 0
0 1 0 0 0
0 0 1 0 0
0 0 0 .5 0" />
</filter>
<pattern id="smallGrid" width="75" height="75" patternUnits="userSpaceOnUse">
<path d="M 75 0 L 0 0 0 75" fill="none" stroke="#444444" stroke-width="2" />
</pattern>
<div id="compo-container" style="width: 100%; height: 800px;">
<svg id="svg-id" xmlns="http://www.w3.org/2000/svg" style="display: inline; width: inherit; min-width: inherit; max-width: inherit; height: inherit; min-height: inherit; max-height: inherit;" version="1.1">
<g>
<svg id="compo">
<defs>
<filter id="f1">
<feColorMatrix id="c1" type="matrix" values="1 0 0 0 0
0 1 0 1 0
0 0 1 1 0
0 0 0 1 0 " />
</filter>
</defs>
<image x="0" y="0" height="100%" width="100%" filter="url(#f1)" xlink:href="../../{diff1}/{layername}" />
</svg>
<pattern id="grid" width="750" height="750" patternUnits="userSpaceOnUse">
<rect width="750" height="750" fill="url(#smallGrid)" />
<path d="M 750 0 L 0 0 0 750" fill="none" stroke="#666666" stroke-width="5" />
</pattern>
</defs>
<image x="0" y="0" height="100%" width="100%" filter="url(#f1)" xlink:href="../../{diff2}/{layername}" />
<image x="0" y="0" height="100%" width="100%" filter="url(#f2)" xlink:href="../../{diff1}/{layername}" />
<rect width="100%" height="100%" fill="url(#grid)" />
<svg id="compo2">
<defs>
<filter id="f2">
<feColorMatrix id="c2" type="matrix" values="1 0 0 1 0
0 1 0 0 0
0 0 1 0 0
0 0 0 .5 0" />
</filter>
</defs>
<image x="0" y="0" height="100%" width="100%" filter="url(#f2)" xlink:href="../../{diff2}/{layername}" />
</svg>
</g>
</svg>
</div>
<div id="sbs-container" width=100%; height=100% >
<embed id="diff1" class="{layer}" type="image/svg+xml" style="width: 50%; float: left; border:1px solid black;" src="../../{diff1}/{layername}" />
<embed id="diff2" class="{layer}" type="image/svg+xml" style="width: 50%; border:1px solid black;" src="../../{diff2}/{layername}" />
</div>
<div class="responsive" >
<div class="gallery" >
<a target="_blank" href={prj}-{layer}.html>
<a href=../../{diff1}/{layername}>
<embed id="diff1" class="{layer}" type="image/svg+xml" src=../../{diff1}/{layername} width="100%">
</a>
</a>
<div class="desc added">{diff1}</div>
</div>
</div>
<div class="responsive" >
<div class="gallery" >
<a target="_blank" href = {prj}-{layer}.html>
<a href=../../{diff2}/{layername} >
<embed id="diff2" class="{layer}" type="image/svg+xml" src=../../{diff2}/{layername} width="100%">
</a>
</a>
<div class="desc removed">{diff2}</div>
</div>
</div>
'''
twopane='''
<script>
window.onload = function() {
window.zoomBoard = svgPanZoom('#diff1', {
zoomEnabled: true,
controlIconsEnabled: true,
maxZoom:20,
minZoom:0.5,
});
window.zoomBoard2 = svgPanZoom('#diff2', {
zoomEnabled: true,
controlIconsEnabled: true,
maxZoom:20,
minZoom:0.5,
});
zoomBoard.setOnZoom(function(level) {
zoomBoard2.zoom(level)
zoomBoard2.pan(zoomBoard.getPan())
})
zoomBoard.setOnPan(function(point) {
zoomBoard2.pan(point)
})
zoomBoard2.setOnZoom(function(level) {
zoomBoard.zoom(level)
zoomBoard.pan(zoomBoard2.getPan())
})
zoomBoard2.setOnPan(function(point) {
zoomBoard.pan(point)
})
var panZoomB1 = svgPanZoom("#diff1");
panZoomB1.zoomAtPoint(2, {
x: -50,
y: -50
})
window.zoomComposite = svgPanZoom('#composite', {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true,
// Don't use window.onLoad like this in production, because it can only listen to one function.
window.onload = function() {
// Expose variable for testing purposes
window.panZoomDiff = svgPanZoom('#svg-id', {
zoomEnabled: true,
controlIconsEnabled: true,
center: true,
minZoom: 1.5,
maxZoom: 20,
});
};
// Expose variable to use for testing
window.zoomDiff = svgPanZoom('#diff1', {
zoomEnabled: true,
controlIconsEnabled: true,
minZoom: 1.5,
maxZoom: 20,
// Uncomment this in order to get Y asis synchronized pan
// beforePan: function(oldP, newP) {return {y:false}},
});
// Expose variable to use for testing
window.zoomDiff2 = svgPanZoom('#diff2', {
zoomEnabled: true,
controlIconsEnabled: true,
minZoom: 1.5,
maxZoom: 20,
});
zoomDiff.setOnZoom(function(level) {
zoomDiff2.zoom(level)
zoomDiff2.pan(zoomDiff.getPan())
})
zoomDiff.setOnPan(function(point) {
zoomDiff2.pan(point)
})
zoomDiff2.setOnZoom(function(level) {
zoomDiff.zoom(level)
zoomDiff.pan(zoomDiff2.getPan())
})
zoomDiff2.setOnPan(function(point) {
zoomDiff.pan(point)
})
};
</script>
</body>
</html>
'''
css = '''
@ -1298,7 +1286,7 @@ def makeOutput(diffDir1, diffDir2, prjctName, prjctPath, times, dim1, dim2):
diff1Txt.wait()
#sed -e 's/(layer {mod}*)//g' |
mod = layer.replace("_",".")
# diffCmnd2 = diffProg + ''' --suppress-common-lines {prjctPath}{plotDir}/{diff2}/*.kicad_pcb {prjctPath}{plotDir}/{diff1}/*.kicad_pcb | grep {mod} | sed 's/> /<\/div><div class="differences added">/g' | sed 's/< /<\/div><div class="differences removed">/g' | sed 's/\/n/<\/div>/g' | sed 's/(status [1-9][0-9])//g' '''.format(
# diffCmnd2 = diffProg + ''' --suppress-common-lines {prjctPath}{plotDir}/{diff2}/*.kicad_pcb {prjctPath}{plotDir}/{diff1}/*.kicad_pcb | grep {mod} | sed 's/> /<\/div><div class="differences added">/g' | sed 's/< /<\/div><div class="differences removed">/g' | sed 's/\/n/<\/div>/g' | sed 's/(status [1-9][0-9])//g' '''.format(
diffCmnd2 = diffProg + ''' --suppress-common-lines {prjctPath}{plotDir}/{diff2}/*.kicad_pcb {prjctPath}{plotDir}/{diff1}/*.kicad_pcb | grep {mod} | sed 's/(status [1-9][0-9])//g' '''.format(
layername=filename,
plotDir=plotDir,
@ -1365,10 +1353,10 @@ def processDiff(diffText, mod):
"[\"]":r'',
"[**]":r'',
}
# (pad 25 thru_hole oval (at 7.62 7.62 90) (size 1.6 1.6) (drill 0.8) (layers *.Cu *.Mask)
# (pad 25 thru_hole oval (at 7.62 7.62 90) (size 1.6 1.6) (drill 0.8) (layers *.Cu *.Mask)
# "[0-9] smd":"<td>Surface</td>",
# "[0-9] smd":"<td>Surface</td>",
final =""
content = ""
output = ""
@ -1378,7 +1366,7 @@ def processDiff(diffText, mod):
tbR = ""
checked = "checked"
top1='''<input name='tabbed' id='tabbed{tabn}' type='radio' {checked}><section><h1><label for='tabbed{tabn}'>{label}</label></h1><div>{content}</div></section>'''
tsl='''<div class='responsive'>
<div class = 'tbl'>
@ -1390,9 +1378,9 @@ def processDiff(diffText, mod):
</div>
<div style='padding:6px;'>
</div>'''
for indx,layerInfo in enumerate(keywords):
combined = tbL = tbR = ""
for indx2,parameter in enumerate(layerInfo[2]):
@ -1417,7 +1405,7 @@ def processDiff(diffText, mod):
tbL = tbL + "<tr>" + output[1:]
elif output[0] == "<":
tbR = tbR + "<tr>" + output[1:]
combined = tsl + tbL + "</table></div></div>" + tsr + tbR + "</table></div></div>"
content = top1.format(tabn=indx,content=combined,label=layerInfo[1],checked=checked)
checked=""
@ -1433,6 +1421,11 @@ def popup_showinfo(progress):
p.pack()
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=os.path.realpath(prjctPath + plotDir), **kwargs)
class Splash(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
@ -1446,6 +1439,14 @@ class Splash(tk.Toplevel):
if action == "cancel":
self.quit()
def startWebServer():
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
webbrowser.open('http://127.0.0.1:' + str(PORT) + '/web/index.html')
httpd.serve_forever()
if __name__ == "__main__":
gui = tk.Tk()
@ -1501,5 +1502,7 @@ if __name__ == "__main__":
makeOutput(svgDir1, svgDir2, prjctName, prjctPath, times, boardDims1, boardDims2)
startWebServer()
webbrowser.open(
'file://' + os.path.realpath(prjctPath + plotDir + webDir + '/index.html'))
'http://127.0.0.1:' + str(PORT) + '/web/index.html')

View File

@ -1,4 +1,4 @@
#!/Applications/kicad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python
#!/Applications/KiCad/kicad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python
'''
Kicad plot pcb file.
@ -22,7 +22,8 @@ print(reqLayers)
board = LoadBoard(boardName)
nets = pcbnew.NETINFO_LIST(board)
print(nets)
pctl = pcbnew.PLOT_CONTROLLER(board)
pctl.SetColorMode(True)

View File

@ -1,4 +1,4 @@
#!/Applications/Kicad/kicad.app/Contents/Applications/pcbnew.app/Contents/MacOS/Python
#!/Applications/KiCad/kicad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python
'''
Kicad plot pcb file.