Added SCM detection logic and list configured SCMs in splash screen

This commit is contained in:
John Pateman 2020-03-02 14:49:59 +00:00
parent 8c730411dc
commit fb0e6b2f56
2 changed files with 151 additions and 147 deletions

View File

@ -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 <div> 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 = '''
<div class="clearfix"></div>
<div style="padding:6px;"></div>
@ -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)

137
tkUI.py
View File

@ -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('<<ListboxSelect>>', 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)