mirror of https://github.com/rusefi/KiCad-Diff.git
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:
parent
9519e00015
commit
f432f8a904
|
@ -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;
|
||||
}
|
173
kidiff_gui.py
173
kidiff_gui.py
|
@ -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">
|
||||
|
||||
<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" x="0%" y="0%" width="100%" height="100%">
|
||||
<feColorMatrix result="original" id="c1" type="matrix" values="1 0 0 0 0
|
||||
<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>
|
||||
<filter id="f2" x="0%" y="0%" width="100%" height="100%">
|
||||
<feColorMatrix result="original" id="c2" type="matrix" values="1 0 0 1 0
|
||||
</defs>
|
||||
<image x="0" y="0" height="100%" width="100%" filter="url(#f1)" xlink:href="../../{diff1}/{layername}" />
|
||||
</svg>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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)" />
|
||||
<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>
|
||||
// Don't use window.onLoad like this in production, because it can only listen to one function.
|
||||
window.onload = function() {
|
||||
|
||||
window.zoomBoard = svgPanZoom('#diff1', {
|
||||
// Expose variable for testing purposes
|
||||
window.panZoomDiff = svgPanZoom('#svg-id', {
|
||||
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,
|
||||
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 = '''
|
||||
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue