Welcome to BDP¶
BDP (Block Diagrams in Python) aims to become a Python fronted for TikZ when it comes to drawing block diagrams in order to facilitate the process. BDP wraps the TikZ statements into the Python objects so that users can describe diagrams in pure Python. However, inserting raw TikZ in BDP is also possible. Figure below shows an BDP example image which represents the BDP compilation process.
Figure can be rendered with the Python code provided below, which is also available in repository inside compile_process.py BDP diagram. It can be rendered into the PNG with BDP via command line:
# bdp compile_process.py -p
For a complete list of command line options please take a look at Rendering chapter.
Contents¶
Why BDP?¶
BDP brings following benefits:
- Diagram description in Python which should render it more readable
- Step-by-step debugging of the diagram description
- Use the tools and design environments available for Python development (debugging, code completion, refactoring, documentation utilities...)
- Use vast Python library of packages
BDP features¶
BDP package comprises:
- Python classes that wrap the Tikz statements
- Class for rendering PDF and PNG images from the Python description
- Shell entry point for rendering BDP images from command line
- Sphinx extensions for embedding BDP images into the Sphinx documents
Image below is a more complex example, which shows how power of Python programming can be used to generate diagrams with BDP. Image shows an UML-like diagram of few major BDP templates.
Figure can be rendered with the Python code provided below.
Where to start?¶
Installation¶
BDP package currently supports only Python 3. Following are alternative ways to install BDP.
Install BDP using pip:
pip3 install bdp
Install BDP using easy_install:
easy_install3 bdp
Install BDP from source:
python3 setup.py install
BDP requires TeX Live, which could be installed on a Debian or a Debian-derived systems, with:
# sudo apt-get install texlive
For converting PDF to PNG, pdftoppm, pnmcrop and pnmtopng are needed, which could be installed on a Debian or a Debian-derived systems, with:
# sudo apt-get install poppler-utils
# sudo apt-get install netpbm
Read the documentation¶
Start with the short tutorial BDP short tutorial
Checkout the examples¶
BDP images used in documentation are located in the images repository documentation folder.
Get involved¶
Pull your copy from github repository
Source codes for the examples¶
from bdp import *
block.size=(6,3)
block.nodesep=(3,3)
BDP = block(r"BDP", alignment='nw', group='tight', group_margin=p(1,1.5), dashed=True)
fig << block(r"Python \\ Description")
BDP['tikz'] = prev(r"TikZ \\ Renderer").right()
BDP['pdf'] = prev(r"PDF \\ Renderer").below()
BDP['png'] = prev(r"PNG \\ Renderer").below()
fig << prev(r"TeX Live", size=(6,9)).right(BDP['tikz'])
fig << block(r"pdftoppm \\ pnmtopng").below(fig['Te*'])
fig << BDP
cap.length = 1
cap.width = 1
path.line_width = 0.5
path.double = True
fig << path(fig['Pyt*'].e(0.5), BDP['tikz'].w(0.5), style=('',cap))
fig << path(fig['Tik*'].s(0.5), fig['PDF*'].n(0.5), style=('',cap))
fig << text('TeX').align(fig[-1].pos(0.5), prev().w(0.5, -0.1))
fig << path(fig['PDF*'].s(0.5), fig['PNG*'].n(0.5), style=('',cap))
fig << text('PDF').align(fig[-1].pos(0.5), prev().w(0.5, -0.1))
fig << path(fig['PNG*'].s(0.5), poffy(3), style=('',cap))
fig << text('PNG').align(fig[-1].pos(0.9), prev().w(0.5, -0.1))
fig << path(BDP['tikz'].e(0.5), poffx(3), style=(cap,cap))
fig << path(fig['PDF*'].e(0.5), poffx(3), style=(cap,cap))
fig << path(fig['PNG*'].e(0.5), poffx(3), style=(cap,cap))
from bdp import *
from bdp.group import Group
import inspect
def fill_group(group, fields, template):
for name,text in fields:
text = text.replace('_', '\_')
try:
group[name] = template(text).align(group[-1].s())
except IndexError:
group[name] = template(text).align(group.n())
def uml_for_obj(obj, parent=object):
# extract methods and attributes for diagram
attrs = [(k, '+' + k) for k in sorted(obj.__dict__) if (k[0] != '_') and (not hasattr(parent, k))]
methods = [(k, '+' + k[0] + '()')
for k in inspect.getmembers(obj, predicate=inspect.ismethod)
if (k[0][0] != '_') and (not hasattr(parent, k[0]))]
# populate BDP blocks
uml = block(r'\textbf{' + obj.__class__.__name__ + '}', alignment='tc', border=False, group='tight')
field = block(size=(7,None), alignment='cw', border=False, text_margin=(0.2,0.1))
uml['attrs'] = block(group='tight').align(uml.n())
fill_group(uml['attrs'], attrs, field)
uml['methods'] = block(group='tight').align(uml['attrs'].s())
fill_group(uml['methods'], methods, field)
return uml
block = block(nodesep = (4,2))
# generate UML components
element_uml = uml_for_obj(group(), Group())
shape_uml = uml_for_obj(shape(), group())
block_uml = uml_for_obj(block(), shape())
text_uml = uml_for_obj(text(), group())
# organize components in the diagram
shape_uml.right(element_uml).aligny(element_uml.n(), shape_uml.w(-1.0))
text_uml.below(shape_uml).aligny(element_uml.s(), text_uml.w(2.0))
block_uml.right(text_uml).aligny(midy(text_uml.n(), shape_uml.n()))
# render the components
fig << element_uml << shape_uml << block_uml << text_uml
# generate and render the wiring
fig << path(text_uml.w(0.5), element_uml.e(0.6), style='-open triangle 45')
fig << path(shape_uml.w(0.5), element_uml.e(0.4), style='-open triangle 45')
fig << path(block_uml.w(0.5), shape_uml.e(0.4), style='-open triangle 45')
fig << path(block_uml['attrs']['text'].e(0.5), poff(1,0), text_uml.e(0.5), style='open diamond-', routedef='|-')