mirror of https://github.com/rusefi/KiCad-Diff.git
Added SCM detection logic and list configured SCMs in splash screen
This commit is contained in:
parent
8c730411dc
commit
fb0e6b2f56
161
kidiff_gui.py
161
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 <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
137
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('<<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)
|
||||
|
|
Loading…
Reference in New Issue