diff --git a/kidiff_gui.py b/kidiff_gui.py
index 14bd851..4833ead 100755
--- a/kidiff_gui.py
+++ b/kidiff_gui.py
@@ -4,7 +4,13 @@
# 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 external files
+# TODO [ ] Place all template text/css text in external files
+# TODO [ ] Improve display of artifacts in diff choice window
+# TODO [ ] Consider changing GUI elements to wxPython
+# TODO [*] Manage any missing SCM paths - reflect available SCM progs in splash screen
+# TODO [ ] Adjust
for three pane output to have white outer border, not filter colour
+# TODO [ ] Improve three pane output layout, perhaps with diff tree on LHS and not underneath
+#
import os
import time
@@ -19,8 +25,14 @@ import tkUI
from tkUI import *
import http.server
import socketserver
+socketserver.TCPServer.allow_reuse_address = True
+# -------------------------------------------------------------------------
# NOTE Adjust these paths to suit your setup
+# If you do not use one (or more) of these SCMs, please set to ''
+# This program attempts to auto-identify which SCM is in use.
+# In the event of multiple SCMs being in use in one repository, the order of priority
+# is Fossil > Git > SVN.
gitProg = '/usr/bin/git'
fossilProg = '/usr/local/bin/fossil'
@@ -30,9 +42,15 @@ webDir = '/web'
diffProg = '/usr/bin/diff'
plotProg = '/usr/local/bin/plotPCB2_DIMS.py'
-PORT = 9090
-Handler = http.server.SimpleHTTPRequestHandler
+# -------------------------------------------------------------------------
+# NOTE Adjust this port to suit your requirements. Must be >1000.
+
+PORT = 9090
+
+
+# -------------------------------------------------------------------------
+# NOTE Please adjust these colours to suit your requirements.
layerCols = {
'F_Cu': "#952927",
@@ -59,6 +77,14 @@ layerCols = {
'F_CrtYd': "#A7A7A7",
}
+Handler = http.server.SimpleHTTPRequestHandler
+
+
+# ------------------------------------------HTML Template Blocks-------------------------------------------
+#
+# NOTE These should go into external files to clean up and seperate the code
+#
+
tail = '''
@@ -937,49 +963,52 @@ def getSCM(prjctPath):
scm = ''
# Check if git
- gitCmd = 'cd ' + prjctPath + ' && ' + gitProg + ' status'
- git = Popen(
- gitCmd,
- shell=True,
- stdin=PIPE,
- stdout=PIPE,
- stderr=PIPE,
- close_fds=True)
- stdout, stderr = git.communicate()
- git.wait()
- if ((stdout.decode('utf-8') != '') & (stderr.decode('utf-8') == '')):
- scm = 'Git'
+ if (gitProg != ''):
+ gitCmd = 'cd ' + prjctPath + ' && ' + gitProg + ' status'
+ git = Popen(
+ gitCmd,
+ shell=True,
+ stdin=PIPE,
+ stdout=PIPE,
+ stderr=PIPE,
+ close_fds=True)
+ stdout, stderr = git.communicate()
+ git.wait()
+ if ((stdout.decode('utf-8') != '') & (stderr.decode('utf-8') == '')):
+ scm = 'Git'
# check if Fossil
- fossilCmd = 'cd ' + prjctPath + ' && ' + fossilProg + ' status'
- fossil = Popen(
- fossilCmd,
- shell=True,
- stdin=PIPE,
- stdout=PIPE,
- stderr=PIPE,
- close_fds=True)
- stdout, stderr = fossil.communicate()
- fossil.wait()
- print(stdout.decode('utf-8'),"stdERROR=", stderr.decode('utf-8'))
- # if ((stdout.decode('utf-8') != '') & (stderr.decode('utf-8') == '')):
+ if (fossilProg != ''):
+ fossilCmd = 'cd ' + prjctPath + ' && ' + fossilProg + ' status'
+ fossil = Popen(
+ fossilCmd,
+ shell=True,
+ stdin=PIPE,
+ stdout=PIPE,
+ stderr=PIPE,
+ close_fds=True)
+ stdout, stderr = fossil.communicate()
+ fossil.wait()
+ print(stdout.decode('utf-8'),"stdERROR=", stderr.decode('utf-8'))
+ # if ((stdout.decode('utf-8') != '') & (stderr.decode('utf-8') == '')):
- if (stdout.decode('utf-8') != ''):
- scm = 'Fossil'
+ if (stdout.decode('utf-8') != ''):
+ scm = 'Fossil'
# check if SVN
- svnCmd = 'cd ' + prjctPath + ' && ' + svnProg + ' log | perl -l40pe "s/^-+/\n/"'
- svn = Popen(
- svnCmd,
- shell=True,
- stdin=PIPE,
- stdout=PIPE,
- stderr=PIPE,
- close_fds=True)
- stdout, stderr = svn.communicate()
- svn.wait()
- if ((stdout.decode('utf-8') != '') & (stderr.decode('utf-8') == '')):
- scm = 'SVN'
+ if (svnProg != ''):
+ svnCmd = 'cd ' + prjctPath + ' && ' + svnProg + ' log | perl -l4svn log0pe "s/^-+/\n/"'
+ svn = Popen(
+ svnCmd,
+ shell=True,
+ stdin=PIPE,
+ stdout=PIPE,
+ stderr=PIPE,
+ close_fds=True)
+ stdout, stderr = svn.communicate()
+ svn.wait()
+ if ((stdout.decode('utf-8') != '') & (stderr.decode('utf-8') == '')):
+ scm = 'SVN'
return scm
@@ -988,7 +1017,18 @@ def fossilDiff(path, kicadPCB):
'''Returns list of Fossil artifacts from a directory containing a
*.kicad_pcb file.'''
+ # NOTE Assemble a list of artefacts. Unfortunatly, Fossil doesn't give any easily configurable length.
+ # NOTE Using the -W option results in multiline diffs
+ # NOTE 'fossil -finfo' looks like this
+ # 2017-05-19 [21d331ea6b] Preliminary work on CvPCB association and component values (user: johnpateman, artifact: [1100d6e077], branch: Ver_3V3)
+ # 2017-05-07 [2d1e20f431] Initial commit (user: johnpateman, artifact: [24336219cc], branch: trunk)
+ # NOTE 'fossil -finfo -b' looks like this
+ # 21d331ea6b 2017-05-19 johnpate Ver_3V3 Preliminary work on CvPCB association a
+ # 2d1e20f431 2017-05-07 johnpate trunk Initial commit
+ # TODO Consider parsing the output of fossil finfo and split off date, artifactID, mesage, user and branch
+
fossilCmd = 'cd ' + path + ' && ' + fossilProg + ' finfo -b ' + kicadPCB
+
fossil = Popen(
fossilCmd,
shell=True,
@@ -999,7 +1039,8 @@ def fossilDiff(path, kicadPCB):
stdout, _ = fossil.communicate()
fossil.wait()
line = (stdout.decode('utf-8').splitlines())
- fArtifacts = [a.replace(' ', ' ', 4) for a in line]
+ # fArtifacts = [a.replace(' ', ' ', 4) for a in line]
+ fArtifacts = [a.replace(' ', '\t', 4) for a in line]
return fArtifacts
@@ -1042,10 +1083,8 @@ def svnDiff(path, kicadPCB):
def makeSVG(d1, d2, prjctName, prjctPath, reqLayers):
'''Hands off required .kicad_pcb files to "plotPCB2.py"
- and generate .svg files. Does not use the 'reqLayers as the plotting routine
- is written in python2 and can't seem to pass layer list easily. Routine is
- v quick so no major overhead in plotting unescessay layers to svg. Easiest to
- write it to a 'layers' file in the output directory'''
+ and generate .svg files. Routine is
+ v quick so all layers are plotted to svg.'''
print("Generating .svg files")
@@ -1421,13 +1460,23 @@ def popup_showinfo(progress):
p = Label(gui, Text=display)
p.pack()
+def scmAvailable():
+ SCMS = ''
+ if (fossilProg != ''):
+ SCMS = SCMS + "Fossil \n"
+ if (gitProg != ''):
+ SCMS = SCMS + "Git \n"
+ if (svnProg != ''):
+ SCMS = SCMS + "SVN "
+
+ return (SCMS)
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=os.path.realpath(prjctPath + plotDir), **kwargs)
-class Splash(tk.Toplevel):
+class Select(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
tk.Toplevel.withdraw(self)
@@ -1435,7 +1484,7 @@ class Splash(tk.Toplevel):
action = messagebox.askokcancel(
self,
message="Select a *.kicad_pcb file under version control",
- detail="Git, Fossil or SVN supported")
+ detail="Available: \n\n" + SCMS)
self.update()
if action == "cancel":
self.quit()
@@ -1450,11 +1499,16 @@ def startWebServer():
if __name__ == "__main__":
- gui = tk.Tk()
+ SCMS = scmAvailable()
+ print(SCMS)
+ if (SCMS == ""):
+ print("You need to have at least one SCM program path identified in lines 32 - 40")
+ exit()
+ gui = tk.Tk(SCMS)
gui.withdraw()
gui.update()
- splash = Splash(gui)
- splash.destroy()
+ Select = Select(gui)
+ Select.destroy()
prjctPath, prjctName = getProject()
gui.update()
gui.deiconify()
@@ -1472,13 +1526,12 @@ if __name__ == "__main__":
if scm == 'SVN':
artifacts = svnDiff(prjctPath, prjctName)
if scm == '':
- print("This project does not appear to be under version control")
+ print("This project is either not under version control or you have not set the path to the approriate SCM program in lines 32-40")
sys.exit(0)
- dpi, d1, d2, layers = tkUI.runGUI(artifacts, prjctName, prjctPath, scm)
+ d1, d2 = tkUI.runGUI(artifacts, prjctName, prjctPath, scm)
- print("Resolution (dpi) : ", dpi.get())
print("Commit1", d1)
print("Commit2", d2)
diff --git a/tkUI.py b/tkUI.py
index df7576c..bc1b814 100644
--- a/tkUI.py
+++ b/tkUI.py
@@ -5,7 +5,7 @@ import tkinter as tk
from tkinter import *
from tkinter import filedialog, ttk, messagebox
-global resolution, buttons, root, commitTop, commitBottom
+global root, commitTop, commitBottom
def runProgram():
@@ -13,16 +13,16 @@ def runProgram():
root.quit()
-class Splash(tk.Toplevel):
- def __init__(self, parent):
- tk.Toplevel.__init__(self, parent)
- self.title("Kicad Visual Diff")
- action = messagebox.askokcancel(self,
- message="Select a *.kicad_pcb file under version control", detail="Git, Fossil or SVN supported")
+# class Splash(tk.Toplevel):
+# def __init__(self, parent):
+# tk.Toplevel.__init__(self, parent)
+# self.title("Kicad Visual Diff")
+# action = messagebox.askokcancel(self,
+# message="Select a *.kicad_pcb file under version control", detail="Git, Fossil or SVN supported")
- self.update()
- if action == "cancel":
- self.quit()
+# self.update()
+# if action == "cancel":
+# self.quit()
def CurSelect(event):
@@ -32,16 +32,16 @@ def CurSelect(event):
picked = widget.get(selection)
source = ((str(widget).split('.'))[1])[-1:]
# TOP window is 3
- if source == '3':
+ if source == '2':
commitTop = picked
# BOTTOM window is 4
- elif source == '4':
+ elif source == '3':
commitBottom = picked
def runGUI(checkouts_top, prjctName, prjctPath, scm):
- global resolution, buttons, root, commitTop, commitBottom
+ global root, commitTop, commitBottom
checkouts_bottom = checkouts_top[:]
@@ -50,27 +50,24 @@ def runGUI(checkouts_top, prjctName, prjctPath, scm):
root.configure(background='#ececec')
root.title("Kicad Visual Diff")
- root.geometry('1200x700')
+ # root.geometry('1200x700')
frame1 = tk.LabelFrame(root, text=scm, width=1000,
height=50, bd=1, background='#ececec')
- frame2 = tk.LabelFrame(root, text="Layers", width=200,
+ frame2 = tk.LabelFrame(root, text="Commit 1", width=1000,
height=200, bd=1, background='#ececec')
- frame3 = tk.LabelFrame(root, text="Commit 1", width=400,
+ frame3 = tk.LabelFrame(root, text="Commit 2", width=1000,
height=200, bd=1, background='#ececec')
- frame4 = tk.LabelFrame(root, text="Commit 2", width=400,
- height=200, bd=1, background='#ececec')
- frame5 = tk.LabelFrame(root, text="Resolution (dpi)", width=1000,
+ frame4 = tk.LabelFrame(root, width=1000,
height=50, bd=0, background='#ececec')
- frame1.grid(row=0, column=0, columnspan=2, padx=5, sticky='N E W S')
- frame2.grid(row=1, column=0, rowspan=2, padx=5, sticky='N E W S')
- frame3.grid(row=1, column=1, padx=5, sticky='N E W S')
- frame4.grid(row=2, column=1, padx=5, sticky='N E W S')
- frame5.grid(row=3, column=0, columnspan=2, padx=5, sticky='N E W S')
+ frame1.grid(row=0, column=0, padx=25, sticky='N E W S')
+ frame2.grid(row=1, column=0, padx=25, sticky='N E W')
+ frame3.grid(row=2, column=0, padx=25, sticky='N EW')
+ frame4.grid(row=3, column=0, padx=25, sticky='N E W S')
- root.grid_columnconfigure(0, weight=1)
- root.grid_columnconfigure(1, weight=10)
+ # root.grid_columnconfigure(0, weight=1)
+ # root.grid_columnconfigure(1, weight=10)
root.grid_rowconfigure(0, minsize=50, weight=1)
root.grid_rowconfigure(1, minsize=200, weight=2)
@@ -80,42 +77,17 @@ def runGUI(checkouts_top, prjctName, prjctPath, scm):
tk.Label(frame1, text=prjctPath, bg='#ececec').pack(side=LEFT, padx=10)
tk.Label(frame1, text=prjctName, bg='#ececec').pack(side=LEFT, padx=10)
- buttons = {'Top layer': '1',
- 'Bottom layer': '1',
- 'Paste bottom': '1',
- 'Paste top': '1',
- 'Silk top': '1',
- 'Silk bottom': '1',
- 'Mask top': '1',
- 'Mask bottom': '1',
- 'Edge cuts': '1',
- 'Margin': '1',
- 'Inner1': '1',
- 'Inner2': '1',
- 'Dwgs_User': '1',
- 'Comments_User': '1',
- 'ECO1': '1',
- 'ECO2': '1',
- 'Fab bottom': '1',
- 'Fab top': '1',
- 'Adhesive bottom': '1',
- 'Adhesive top': '1',
- 'Courtyard bottom': '1',
- 'Courtyard top': '1'
- }
- initLayers = []
-
- for b in buttons:
- buttons[b] = Variable()
- buttons[b].set(1)
- l = ttk.Checkbutton(
- frame2, text=b, variable=buttons[b], onvalue=1, offvalue=0).pack(anchor='w')
commitTop = Variable()
- listTop = Listbox(frame3, bd=0, selectmode=SINGLE, exportselection=False, font='TkFixedFont')
+ listTop = Listbox(
+ frame2,
+ bd=0,
+ selectmode=SINGLE,
+ exportselection=False,
+ font='TkFixedFont')
listTop.grid(column=0, row=0, sticky=(N, E, W))
- scrollTop = ttk.Scrollbar(frame3, orient=VERTICAL, command=listTop.yview)
+ scrollTop = ttk.Scrollbar(frame2, orient=VERTICAL, command=listTop.yview)
scrollTop.grid(column=1, row=0, sticky=(N, E, W))
listTop['yscrollcommand'] = scrollTop.set
@@ -123,59 +95,38 @@ def runGUI(checkouts_top, prjctName, prjctPath, scm):
commitBottom = Variable()
listBottom = Listbox(
- frame4,
+ frame3,
bd=0,
selectmode=SINGLE,
exportselection=False,
font='TkFixedFont')
listBottom.grid(column=0, row=0, sticky=(N, E, W))
scrollBottom = ttk.Scrollbar(
- frame4, orient=VERTICAL, command=listBottom.yview)
+ frame3, orient=VERTICAL, command=listBottom.yview)
scrollBottom.grid(column=1, row=0, sticky=(N, E, W))
listBottom['yscrollcommand'] = scrollBottom.set
listBottom.bind('<
>', CurSelect)
+ frame2.grid_columnconfigure(0, weight=1)
+ frame2.grid_columnconfigure(1, weight=0)
+ frame2.grid_rowconfigure(0, weight=1)
+
frame3.grid_columnconfigure(0, weight=1)
frame3.grid_columnconfigure(1, weight=0)
frame3.grid_rowconfigure(0, weight=1)
- frame4.grid_columnconfigure(0, weight=1)
- frame4.grid_columnconfigure(1, weight=0)
- frame4.grid_rowconfigure(0, weight=1)
-
buttonOK = ttk.Button(
- frame5, text="OK", command=runProgram, default='active')
- buttonOK.grid(column=7, row=0, sticky='w', pady=10)
+ frame4, text="OK", command=runProgram, default='active')
+ buttonOK.grid(column=2, row=0, sticky='w', pady=10)
- buttonCancel = ttk.Button(frame5, text="Cancel", command=quit)
- buttonCancel.grid(column=6, row=0, sticky='e', pady=10)
+ buttonCancel = ttk.Button(frame4, text="Cancel", command=quit)
+ buttonCancel.grid(column=1, row=0, sticky='e', pady=10)
- resolution = IntVar()
- resolution.set(300)
- button100 = ttk.Radiobutton(
- frame5, text="100", variable=resolution, value=100)
- button300 = ttk.Radiobutton(
- frame5, text="300", variable=resolution, value=300, )
- button600 = ttk.Radiobutton(
- frame5, text="600", variable=resolution, value=600)
- button1000 = ttk.Radiobutton(
- frame5, text="1000", variable=resolution, value=1000)
+ frame4.grid_columnconfigure(0, weight=0)
+ frame4.grid_columnconfigure(1, weight=0)
+ frame4.grid_columnconfigure(2, weight=10)
- button100.grid(column=1, row=0)
- button300.grid(column=2, row=0)
- button600.grid(column=3, row=0)
- button1000.grid(column=4, row=0)
-
- frame5.grid_columnconfigure(0, weight=0)
- frame5.grid_columnconfigure(1, weight=0)
- frame5.grid_columnconfigure(2, weight=0)
- frame5.grid_columnconfigure(3, weight=0)
- frame5.grid_columnconfigure(4, weight=0)
- frame5.grid_columnconfigure(5, weight=5)
- frame5.grid_columnconfigure(6, weight=0)
- frame5.grid_columnconfigure(7, weight=1)
- frame5.grid_rowconfigure(0, weight=1)
for line in checkouts_top:
listTop.insert(END, line)
@@ -198,4 +149,4 @@ def runGUI(checkouts_top, prjctName, prjctPath, scm):
root.update()
root.mainloop()
- return(resolution, commitTop, commitBottom, buttons)
+ return(commitTop, commitBottom)