diff --git a/KiBOM/bom_writer.py b/KiBOM/bom_writer.py new file mode 100644 index 00000000..c3ab6a6d --- /dev/null +++ b/KiBOM/bom_writer.py @@ -0,0 +1,80 @@ +import csv +import columns +from component import * + +import os, shutil + +#make a tmp copy of a given file +def TmpFileCopy(filename): + + filename = os.path.abspath(filename) + + if os.path.exists(filename) and os.path.isfile(filename): + shutil.copyfile(filename, filename + ".tmp") + +def WriteCSV(filename, groups, source, version, date, headings = columns.ColumnList._COLUMNS_ALL, ignore=[], ignoreDNF=False, numberRows=True): + + filename = os.path.abspath(filename) + + #delimeter is assumed from file extension + if filename.endswith(".csv"): + delimiter = "," + elif filename.endswith(".tsv") or filename.endswith(".txt"): + delimiter = "\t" + else: + return + + headings = [h for h in headings if h not in ignore] #copy across the headings + + try: + #make a copy of the file + TmpFileCopy(filename) + + with open(filename, "w") as f: + + writer = csv.writer(f, delimiter=delimiter, lineterminator="\n") + + if numberRows: + writer.writerow(["Component"] + headings) + else: + writer.writerow(headings) + + count = 0 + rowCount = 1 + + for i, group in enumerate(groups): + if ignoreDNF and not group.isFitted(): continue + + row = group.getKicadRow(headings) + + if numberRows: + row = [rowCount] + row + + writer.writerow(row) + + try: + count += group.getCount() + except: + pass + + rowCount += 1 + + #blank rows + for i in range(5): + writer.writerow([]) + + writer.writerow(["Component Count:",componentCount]) + writer.writerow(["Component Groups:",len(groups)]) + writer.writerow(["Source:",source]) + writer.writerow(["Version:",version]) + writer.writerow(["Date:",date]) + + return True + + except NameError: + pass +# except BaseException as e: +# print(str(e)) + + return False + \ No newline at end of file diff --git a/KiBOM/columns.py b/KiBOM/columns.py index f9908fad..99bf3865 100644 --- a/KiBOM/columns.py +++ b/KiBOM/columns.py @@ -55,7 +55,7 @@ class ColumnList: #default columns #these columns are 'immutable' - _COLUMNS_DEFAULT = [ + _COLUMNS_PROTECTED = [ Column.COL_DESCRIPTION, Column.COL_PART, Column.COL_REFERENCE, @@ -74,7 +74,7 @@ class ColumnList: def __repr__(self): return self.__str__() - def __init__(self, cols=_COLUMNS_DEFAULT): + def __init__(self, cols=_COLUMNS_ALL): self.columns = [] diff --git a/KiBOM/component.py b/KiBOM/component.py index 782c5b62..cc09bc5a 100644 --- a/KiBOM/component.py +++ b/KiBOM/component.py @@ -1,9 +1,11 @@ -from columns import ColumnList +from columns import ColumnList, Column import units from sort import natural_sort +DNF = ["dnf", "do not fit", "nofit", "no stuff", "nostuff", "noload", "do not load"] + class Component(): """Class for a component, aka 'comp' in the xml netlist file. This component class is implemented by wrapping an xmlElement instance @@ -178,7 +180,7 @@ class ComponentGroup(): return str(self.csvFields[field]) def getHarmonizedField(self,field): - + #for protected fields, source from KiCAD if field in CSV_PROTECTED: return self.getField(field) @@ -249,7 +251,7 @@ class ComponentGroup(): #update a given field, based on some rules and such def updateField(self, field, fieldData): - if field in CSV_PROTECTED: return + if field in ColumnList._COLUMNS_PROTECTED: return if (field == None or field == ""): return elif fieldData == "" or fieldData == None: @@ -262,9 +264,9 @@ class ComponentGroup(): print("Conflict:",self.fields[field],",",fieldData) self.fields[field] += " " + fieldData - def updateFields(self): + def updateFields(self, fields = ColumnList._COLUMNS_ALL): - for f in CSV_DEFAULT: + for f in fields: #get info from each field for c in self.components: @@ -272,19 +274,14 @@ class ComponentGroup(): self.updateField(f, c.getField(f)) #update 'global' fields - self.fields["Reference"] = self.getRefs() - - self.fields["Quantity"] = self.getCount() - - self.fields["Value"] = self.components[0].getValue() - - self.fields["Part"] = self.components[0].getPartName() - - self.fields["Description"] = self.components[0].getDescription() - - self.fields["Datasheet"] = self.components[0].getDatasheet() - - self.fields["Footprint"] = self.components[0].getFootprint().split(":")[-1] + self.fields[Column.COL_REFERENCE] = self.getRefs() + self.fields[Column.COL_GRP_QUANTITY] = self.getCount() + self.fields[Column.COL_VALUE] = self.components[0].getValue() + self.fields[Column.COL_PART] = self.components[0].getPartName() + self.fields[Column.COL_DESCRIPTION] = self.components[0].getDescription() + self.fields[Column.COL_DATASHEET] = self.components[0].getDatasheet() + self.fields[Column.COL_FP] = self.components[0].getFootprint().split(":")[-1] + self.fields[Column.COL_FP_LIB] = self.components[0].getFootprint().split(":")[0] #return a dict of the CSV data based on the supplied columns def getCSVRow(self, columns): diff --git a/KiBOM/netlist_reader.py b/KiBOM/netlist_reader.py index 3a73f93e..fe8f51bf 100644 --- a/KiBOM/netlist_reader.py +++ b/KiBOM/netlist_reader.py @@ -78,12 +78,8 @@ ALIASES = [ ["l, l_small", "inductor"] ] -DNF = ["dnf", "do not fit", "nofit", "no stuff", "nostuff", "noload", "do not load"] - #-------------------------------------------------------------------- - - class xmlElement(): """xml element which can represent all nodes of the netlist tree. It can be used to easily generate various output formats by propogating format diff --git a/KiBOM/sort.py b/KiBOM/sort.py index 7cd6718f..9b21aae5 100644 --- a/KiBOM/sort.py +++ b/KiBOM/sort.py @@ -1,3 +1,5 @@ +import re + #'better' sorting function which sorts by NUMERICAL value not ASCII def natural_sort(string): return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)',string)] \ No newline at end of file diff --git a/KiBOM_GUI.py b/KiBOM_GUI.py index dc514d7e..dbd6aa71 100644 --- a/KiBOM_GUI.py +++ b/KiBOM_GUI.py @@ -8,7 +8,9 @@ import wx.grid def Debug(*arg): pass -sys.path.append(os.path.dirname(sys.argv[0])) +here = os.path.abspath(os.path.dirname(sys.argv[0])) + +sys.path.append(here) from KiBOM.columns import ColumnList @@ -64,6 +66,8 @@ class KiBOMFrame(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent,title=title) + wx.Image.AddHandler(wx.PNGHandler()); + self.columns = ColumnList() self.panel = wx.Panel(self) @@ -101,15 +105,25 @@ class KiBOMFrame(wx.Frame): #buttons to move/add/delete columns self.colButtons = wx.BoxSizer(wx.HORIZONTAL) - self.moveColUp = wx.Button(self.panel, label="Up") - self.moveColDown = wx.Button(self.panel, label="Down") - self.newCol = wx.Button(self.panel, label="Add") - self.delCol = wx.Button(self.panel, label="Del") + upImage = wx.Bitmap(here + "/bitmap/up.png", wx.BITMAP_TYPE_ANY) + self.moveUp = wx.BitmapButton(self.panel, bitmap=upImage, size=upImage.GetSize()) +# self.moveUp.SetTip("Move the selected column up") + + downImage = wx.Bitmap(here + "/bitmap/down.png", wx.BITMAP_TYPE_ANY) + self.moveDown = wx.BitmapButton(self.panel, bitmap=downImage, size=downImage.GetSize()) +# self.moveDown.setToolTip("Move the selected column down") + + newImage = wx.Bitmap(here + "/bitmap/add.png", wx.BITMAP_TYPE_ANY) + self.newCol = wx.BitmapButton(self.panel, bitmap=newImage, size=newImage.GetSize()) +# self.newCol.setToolTip("Add a new data column") + + #delImage = wx.Bitmap("bitmap/del.png", wx.BITMAP_TYPE_ANY) + #self.delCol = wx.BitmapButton(self.panel, bitmap=delImage, size=delImage.GetSize()) #add the buttons - self.colButtons.Add(self.moveColUp) - self.colButtons.Add(self.moveColDown) - self.colButtons.Add(self.delCol) + self.colButtons.Add(self.moveUp) + self.colButtons.Add(self.moveDown) + #self.colButtons.Add(self.delCol) self.colButtons.Add(self.newCol) self.colListSizer.Add(self.colButtons)