Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature xlsx #57

Merged
merged 8 commits into from
May 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions KiBOM_CLI.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@package
KiBOM - Bill of Materials generation for KiCad

Generate BOM in xml, csv, txt, tsv or html formats.
Generate BOM in xml, csv, txt, tsv, html or xlsx formats.

- Components are automatically grouped into BoM rows (grouping is configurable)
- Component groups count number of components and list component designators
Expand All @@ -24,6 +24,16 @@

import argparse

# Optional modules

try:
import xlsxwriter
except:
xlsxwriter_available = False
else:
xlsxwriter_available = True

#
here = os.path.abspath(os.path.dirname(sys.argv[0]))

sys.path.append(here)
Expand All @@ -47,7 +57,9 @@ def say(*arg):

def isExtensionSupported(filename):
result = False
extensions = [".xml",".csv",".txt",".tsv",".html"]
extensions = [".xml",".csv",".txt",".tsv",".html"]
if xlsxwriter_available:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have a default extensions array, and then optionally append '.xlsx' if xlsxwriter_available is True

extensions.append(".xlsx")
for e in extensions:
if filename.endswith(e):
result = True
Expand Down Expand Up @@ -94,6 +106,9 @@ def isExtensionSupported(filename):
pref.Read(config_file)
say("Config:",config_file)

#pass available modules
pref.xlsxwriter_available = xlsxwriter_available

#pass various command-line options through
pref.verbose = verbose
if args.number is not None:
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ optional arguments:
* If a suffix is not specified, CSV output format will be used
* HTML output can be specified within KiCad as: "%O.html" or "%O_BOM.html" (etc)
* XML output can be specified within KiCad as: "%O.xml" (etc)
* XSLX output can be specified within KiCad as: "%O.xlsx" (etc)

**-n --number** Specify number of boards for calculating part quantities

Expand Down Expand Up @@ -181,6 +182,7 @@ Multiple BoM output formats are supported:
* TXT (Text file output with tab separated values)
* XML
* HTML
* XLSX (Needs XlsxWriter Python module)

Output file format selection is set by the output filename. e.g. "bom.html" will be written to a HTML file, "bom.csv" will be written to a CSV file.

Expand Down Expand Up @@ -352,6 +354,10 @@ An XML file output can be generated simply by changing the file extension
<group Datasheet="http://www.ti.com/lit/ds/symlink/max232.pdf" Description="Dual RS232 driver/receiver, 5V supply, 120kb/s, 0C-70C" Footprint="DIP-16_W7.62mm" Notes="Do not fit" Part="MAX232" Quantity="1 (DNF)" Rating="" References="U1" Value="MAX232" Vendor=""/>
</KiCad_BOM>

### XLSX Output
An XLSX file output can be generated simply by changing the file extension


## Contributors

With thanks to the following contributors:
Expand All @@ -365,3 +371,4 @@ With thanks to the following contributors:
* https://github.com/marcelobarrosalmeida
* https://github.com/fauxpark
* https://github.com/Swij
* https://github.com/Ximi1970
8 changes: 8 additions & 0 deletions bomlib/bom_writer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from bomlib.csv_writer import WriteCSV
from bomlib.xml_writer import WriteXML
from bomlib.html_writer import WriteHTML
from bomlib.xlsx_writer import WriteXLSX

import bomlib.columns as columns
from bomlib.component import *
Expand Down Expand Up @@ -69,6 +70,13 @@ def WriteBoM(filename, groups, net, headings = columns.ColumnList._COLUMNS_DEFAU
else:
print("Error writing XML output")

elif ( ext in ["xlsx"] ) and prefs.xlsxwriter_available:
if WriteXLSX(filename, groups, net, headings, prefs):
print("XLSX Output -> {fn}".format(fn=filename))
result = True
else:
print("Error writing XLSX output")

else:
print("Unsupported file extension: {ext}".format(ext=ext))

Expand Down
3 changes: 3 additions & 0 deletions bomlib/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def __init__(self):
self.separatorCSV = None
self.includeVersionNumber = True

self.xlsxwriter_available = False
self.xlsxwriter2_available = False

# Default fields used to group components
self.groups = [
ColumnList.COL_PART,
Expand Down
148 changes: 148 additions & 0 deletions bomlib/xlsx_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# _*_ coding:latin-1 _*_

try:
import xlsxwriter
except:
def WriteXLSX(filename, groups, net, headings, prefs):
return False
else:
import bomlib.columns as columns
from bomlib.component import *
import os, shutil
from bomlib.preferences import BomPref

"""
Write BoM out to a XLSX file
filename = path to output file (must be a .xlsx file)
groups = [list of ComponentGroup groups]
net = netlist object
headings = [list of headings to display in the BoM file]
prefs = BomPref object
"""

def WriteXLSX(filename, groups, net, headings, prefs):

filename = os.path.abspath(filename)

if not filename.endswith(".xlsx"):
return False

nGroups = len(groups)
nTotal = sum([g.getCount() for g in groups])
nFitted = sum([g.getCount() for g in groups if g.isFitted()])
nBuild = nFitted * prefs.boards

workbook = xlsxwriter.Workbook(filename)
worksheet = workbook.add_worksheet()

if prefs.numberRows:
row_headings = ["Component"] + headings
else:
row_headings = headings

cellformats = {}
column_widths = {}
for i in range(len(row_headings)):
cellformats[i] = workbook.add_format({'align':'center_across'})
column_widths[i] = len(row_headings[i]) + 10

if not prefs.hideHeaders:
worksheet.write_string( 0, i, row_headings[i], cellformats[i])

count = 0
rowCount = 1

for i, group in enumerate(groups):
if prefs.ignoreDNF and not group.isFitted(): continue

row = group.getRow(headings)

if prefs.numberRows:
row = [str(rowCount)] + row

for columnCount in range(len(row)):

cell = row[columnCount].decode('utf-8')

worksheet.write_string(rowCount,columnCount,cell,cellformats[columnCount])

if len(cell) > column_widths[columnCount] - 5:
column_widths[columnCount] = len(cell) + 5

try:
count += group.getCount()
except:
pass

rowCount += 1

if not prefs.hideHeaders:
#blank rows
for i in range(5):
rowCount += 1

cellformat_left = workbook.add_format({'align':'left'})

worksheet.write_string( rowCount, 0, "Component Groups:", cellformats[0])
worksheet.write_number( rowCount, 1, nGroups, cellformat_left)
rowCount += 1

worksheet.write_string( rowCount, 0, "Component Count:", cellformats[0])
worksheet.write_number( rowCount, 1, nTotal, cellformat_left)
rowCount += 1

worksheet.write_string( rowCount, 0, "Fitted Components:", cellformats[0])
worksheet.write_number( rowCount, 1, nFitted, cellformat_left)
rowCount += 1

worksheet.write_string( rowCount, 0, "Number of PCBs:", cellformats[0])
worksheet.write_number( rowCount, 1, prefs.boards, cellformat_left)
rowCount += 1

worksheet.write_string( rowCount, 0, "Total components:", cellformats[0])
worksheet.write_number( rowCount, 1, nBuild, cellformat_left)
rowCount += 1

worksheet.write_string( rowCount, 0, "Schematic Version:", cellformats[0])
worksheet.write_string( rowCount, 1, net.getVersion(), cellformat_left)
rowCount += 1

if len(net.getVersion()) > column_widths[1]:
column_widths[1] = len(net.getVersion())


worksheet.write_string( rowCount, 0, "Schematic Date:", cellformats[0])
worksheet.write_string( rowCount, 1, net.getSheetDate(), cellformat_left)
rowCount += 1

if len(net.getSheetDate()) > column_widths[1]:
column_widths[1] = len(net.getSheetDate())


worksheet.write_string( rowCount, 0, "BoM Date:", cellformats[0])
worksheet.write_string( rowCount, 1, net.getDate(), cellformat_left)
rowCount += 1

if len(net.getDate()) > column_widths[1]:
column_widths[1] = len(net.getDate())

worksheet.write_string( rowCount, 0, "Schematic Source:", cellformats[0])
worksheet.write_string( rowCount, 1, net.getSource(), cellformat_left)
rowCount += 1

if len(net.getSource()) > column_widths[1]:
column_widths[1] = len(net.getSource())

worksheet.write_string( rowCount, 0, "KiCad Version:", cellformats[0])
worksheet.write_string( rowCount, 1, net.getTool(), cellformat_left)
rowCount += 1

if len(net.getTool()) > column_widths[1]:
column_widths[1] = len(net.getTool())

for i in range(len(column_widths)):
worksheet.set_column( i, i, column_widths[i])

workbook.close()

return True