Skip to content
Open
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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## Qucs manual

This is a demo/exploration for automatic generation of a component reference manual for Qucs.
It makes use of commands built in the Qucs comannd line interface to generate component icons and data.
It makes use of commands built in the Qucs command line interface to generate component icons and data.
A Python script transforms the generated data into a reStructured markup language.
The generated files are included into a Sphinx documentation generation project to create `html` and `pdf` outputs.

Expand All @@ -30,9 +30,9 @@ The command generates all the icons of the registered components as `.png` into

- `$ qucs -doc`

The command generares the following text files:
The command generates the following text files:

- `caterories.txt` containig the name of the caterories (same order as in Qucs)
- `categories.txt` containing the name of the categories (same order as in Qucs)
- `./[first category]/`
- `01_data.csv` contains the data of the component object
- `01_prop.csv` contains the properties of the component
Expand All @@ -41,7 +41,7 @@ The command generares the following text files:

The document generation makes use of Sphinx and reStructured markup language.

Based on the above data obtained automatically the *Quick Reference* and *Component Reference* sections of the manual are automatically created.
Based on the above data obtained automatically the *Quick Reference*, *Component Reference* and *Categories Reference* sections of the manual are automatically created.

The icons are copied into the `source` directory

Expand All @@ -66,11 +66,11 @@ To generate the final `html` and `pdf` documentation:
make html
make latexpdf

Outpus can be found in the `build/` directory.
Outputs can be found in the `build/` directory.

## Translations

Most of *Quick Reference* and *Component Reference* translations can be retrieved from the Qucs TS files.
Most of *Quick Reference*, *Component Reference* and *Categories Reference* translations can be retrieved from the Qucs TS files.

See scripts:

Expand Down
248 changes: 173 additions & 75 deletions source/component_data/build_component_doc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@

import os
from glob import glob
from collections import defaultdict

# generates
# quick_reference.rst : Quick Reference
# component_reference.rst : Component Reference
# categories_reference.rst : Categories Reference
# from the component icons and data generated by Qucs

def reStSection(fi):
'''Return a dictionary with key values of the object data table.
Expand All @@ -17,29 +23,21 @@ def reStSection(fi):
return out


def reStSymbol(fi, img="png"):
def reStSymbol(comp_data, img="png"):
'''Retuns the reSt code for the component symbol
'''

# get bitmap file name
filename = [line.split(";")[1].strip() for line in open(fi, 'r') if 'Bitmap' in line][0]

string = ("Symbol\n"
'""""""\n\n'
".. image:: _static/bitmap/%s.%s\n"
"\n" %(filename, img) )
"\n" % (comp_data['Bitmap file'], img) )

return string


def reStCompData(fi):
def reStCompData(comp_data):
'''Retuns the reSt code for the component data
'''

skip=1 # header
data = [line.strip() for line in open(fi, 'r')][skip:]
data = [' '*3 + dat for dat in data]

string = ("Component Data\n"
'""""""""""""""\n\n'
".. csv-table:: Component Data\n"
Expand All @@ -49,17 +47,26 @@ def reStCompData(fi):
" :delim: ;\n"
"\n" )

string =string + "\n".join(data) + "\n"
for key, value in comp_data.iteritems():
if (key == 'ID'):
# do not add the (internal) ID key
continue
string = string + ' ' + key + '; ' + value + "\n"

# ground no netlist, sphinx compains
string = string.replace('````', '')

return string


def reStCompProp(fi):
def reStCompProp(comp_data):
'''Retuns the reSt code for the component property list
'''

# name of file containing the component properties
fi = comp_data['ID'] + '_prop.csv'
cat = comp_data['Category']
fi = os.path.join(cat, fi)
props = [line.strip() for line in open(fi, 'r')]

# no properties (just two line header) Ex. GND
Expand All @@ -85,7 +92,23 @@ def reStCompProp(fi):

return string

# this is automate

def reStLabel(label):
if label.startswith('_'):
label = '\\' + label # escape label if it starts with underscore
return ('\n.. _%s:' % label) # cross reference label


def uniquify_dics_list(seq, key):
'''uniqify list of dicts
'''
# see https://stackoverflow.com/questions/4370660/unique-list-of-dicts-based-on-keys#13780915
seen = set()
seen_add = seen.add
return [x for x in seq if x[key] not in seen and not seen_add(x[key])]


# this is automated
noModify = (
"\n"
".. NOTE: This file was generated automatically, modifications will be lost on update\n"
Expand All @@ -97,82 +120,142 @@ def reStCompProp(fi):
cat_file = "categories.txt"
categories = [line.strip() for line in open(cat_file, 'r')]

comps_data = []
# load all components data in a single list
for cat in categories:
# skip diagrams
if (cat == "diagrams"): break
# load component data for this
files = os.listdir(cat)
data = glob(os.path.join(cat,'*data.csv'))
print "Number of components in %s : %d " %(cat, len(data))
for objData in data:
# fech object data as a dictionary
content = reStSection( objData )
comps_data.append(content)
# add ID key to content to easily retrieve the properties file later
ID = label = os.path.basename( objData ).rsplit("_", 1)[0]
content['ID'] = ID

# uniquify, as some components may be in more than one category
uniq_comps_data = uniquify_dics_list(comps_data , 'Bitmap file')
# sort components by Caption (description)
sorted_comps_data = sorted(uniq_comps_data, key=lambda k: k['Caption'])

########################################
# build Categories Reference page
#
categories_data = defaultdict(list)
# build dictionary using Category as key
for comp_data in comps_data:
# build a dictionary using Identifier (component model) as key
# get Identifier of component
id = comp_data['Category'].replace('``', '')
categories_data[id].append(comp_data)

total = 0
reSt = []

# add no modify note
reSt.append( noModify)

reSt.append( noModify )
reSt.append(
"Component Reference\n"
"===================\n\n" )


total = 0

quick_reference =[]

for cat in categories:
"Categories Reference\n"
"====================\n\n" )

# build components list for every category
for key, valueslist in categories_data.iteritems():

# Category title
cat_title = key.title().translate(None,'"').strip()
reSt.append( cat_title )
reSt.append( '-'*len(cat_title) + "\n" )
total += len(valueslist)

# for every component in the category
for idx, value in enumerate(valueslist):
cap = value['Caption']
des = value['Description']
label = value['Bitmap file']
if len(des) > 37:
des = des[:37]+"..."
reSt.append("* :ref:`%s <%s>` : %s" % (cap, label, des))

# skip diagrams
if (cat == "diagrams"): break
reSt.append('') # \n

fo = open('../categories_reference.rst', 'w')
for item in reSt:
fo.write("%s\n" % item)
fo.close()

# Category
cat = cat.title().translate(None,'"').strip()
reSt.append( cat )
reSt.append( '-'*len(cat) + "\n" )
########################################
# build Component Reference page
#
# Components should be grouped by model, since the model defines the properties.
# Ideally the properties section should be common but at present it's not
# since default properties vaalues can be different for components using the
# same model
#
models_data = defaultdict(list)
# build dictionary using Identifier (component model) as key
for comp_data in uniq_comps_data:
# build a dictionary using Identifier (component model) as key
# get Identifier of component
id = comp_data['Identifier'].replace('``', '')
models_data[id].append(comp_data)

# load component list on this category, skip comments
#component_list = os.path.join(cat, "comp_list.txt")
#components = [line.strip() for line in open(component_list, 'r') if "#" not in line]
files = os.listdir(cat)
data = glob(os.path.join(cat,'*data.csv'))
prop = glob(os.path.join(cat,'*prop.csv'))
reSt = []

print "Number of components in %s : %d " %(cat, len(data))
total = total + len(data)
# add no modify note
reSt.append( noModify )

for objData, objProp in zip(data,prop):
reSt.append(
"Component Reference\n"
"===================\n\n" )

# fech object data as a dictionary
content = reStSection( objData )

# _001lump
label = os.path.basename( objData ).split("_")[0]+cat[:3]

# Component Subsection
#descr = content['Caption'].title().translate(None,'"').strip()
descr = content['Caption'].title()
reSt.append( '\n.. _%s:\n' %label ) # cross referece label
# build components description page
# for every model
for key, valueslist in models_data.iteritems():

# for every component using that model
for idx, value in enumerate(valueslist):

# Component Subsection
descr = value['Caption'].title()
# add bitmap file cross-reference label to every component
bf = value['Bitmap file'].lower()
# component model as label
label = key.lower()
# add model cross-reference label only to first component
# and only if this label is different from the component model label
if ((idx == 0) and (label != bf)):
reSt.append( reStLabel(label) ) # cross reference label

reSt.append( reStLabel(bf) ) # cross reference label
reSt.append('') # \n
reSt.append( descr )
reSt.append( '^'*len(descr) + "\n" )

# Symbol
reSt.append( reStSymbol( objData ) )
reSt.append( reStSymbol(value) )

# Data
reSt.append( reStCompData( objData ) )
# default data may be different for components using the same model
# e.g. NPN and PNP BJT
reSt.append( reStCompData(value) )

# Props
reSt.append( reStCompProp( objProp) )
# default properties values may be different for components using the same model
reSt.append( reStCompProp(value) )

# Section label, for crossreference
#reSt.append("\n.. _ref-%s-label:\n" %(symbol))

cap = content['Caption']
sch = content['Schematic entry']
net = content['Netlist entry'].replace('````', '') # gnd has no entry
des = content['Description']
if len(des) > 37:
des = des[:37]+"..."

# build quick reference table
# add cross-reference :ref:`displayed text <label>`
quick_reference.append( " :ref:`%s <%s>`; %s; %s; %s"
%(cap, label, sch, net, des) )
# add additional notes from external file, if available
fn = 'component_notes/%s.rst' % label
if (os.path.isfile('../' + fn) ):
reSt.append( '.. include:: %s' % fn)

# add transition marker (horizontal line)
reSt.append( '\n----\n')

# document may not end with a transition
del reSt[-1]

print "Total number of components: ", total

Expand All @@ -182,10 +265,12 @@ def reStCompProp(fi):
fo.close()



quick_header = []
quick_header.append( noModify )
quick_header.append(
########################################
# build Quick Reference page
#
quick_reference = []
quick_reference.append( noModify )
quick_reference.append(
"Quick Reference\n"
"===============\n\n"
"The table below is a quick summary of the %i components currently available in Qucs.\n"
Expand All @@ -195,9 +280,22 @@ def reStCompProp(fi):
" :widths: 30, 15, 15, 40\n"
" :delim: ;\n\n" %(total) )

for comp_data in sorted_comps_data:
cap = comp_data['Caption']
lbl = comp_data['Bitmap file']
sch = comp_data['Identifier']
net = comp_data['Default name'].replace('````', '') # gnd has no entry
des = comp_data['Description']
if len(des) > 37:
des = des[:37]+"..."

# build quick reference table
# add cross-reference :ref:`displayed text <label>`
# caption<crossref_label>, Schematic Entry, Netlist Entry, Description
quick_reference.append( " :ref:`%s <%s>`; %s; %s; %s"
%(cap, lbl, sch, net, des) )

fo2 = open('../quick_reference.rst', 'w')
for item in quick_header:
fo2.write("%s\n" % item)
for item in quick_reference:
fo2.write("%s\n" % item)
fo2.close()
Loading