Package: pero
PeroThe main motivation behind theperolibrary is to provide unified API for multiple drawing backends likePyQt5,PyQt6,PySide2,PySide6,wxPython,PyCairo,PyMuPDF,Pythonista(and possibly more),
which is easy to understand and use. Beside the common drawing capabilities, numerous pre-build glyphs are available,
as well as an easy-to-use path, matrix transformations etc. Depending on available backend libraries, drawings can be
viewed directly or exported into various image formats.Ever since I discovered the wonderfuld3jsJavaScript library, I wanted to have the same amazing
concept of dynamic properties within Python drawings. In fact, this has been the trigger to start working on theperolibrary. Finally, it is now available.Please see theexamplesfolder or in-code documentation of classes
and functions to learn more about theperolibrary capabilities.Consider also checking a small derived library providing some basic plotting functionalities, like profiles, bars, pie
charts and Venn diagrams, calledperrot.importperoimg=pero.Image(width=200,height=200)img.line_cap=pero.ROUNDimg.line_join=pero.ROUND# fillimg.fill("w")# bodyimg.line_width=2img.line_color=pero.colors.Orange.darker(.1)img.fill_color=pero.colors.Orangeimg.draw_circle(100,100,75)# shadowimg.line_color=Noneimg.fill_color=pero.colors.White.darker(.1)img.draw_ellipse(100,185,70,10)# eyesimg.fill_color=pero.colors.Blackimg.draw_circle(70,85,15)img.draw_circle(130,85,15)# eyebrowsimg.line_color=pero.colors.Blackimg.fill_color=Noneimg.line_width=3img.draw_arc(70,85,23,pero.rads(-100),pero.rads(-20))img.draw_arc(130,85,23,pero.rads(200),pero.rads(280))# mouthimg.line_width=5img.draw_arc(100,100,50,pero.rads(40),pero.rads(80))# highlightimg.line_color=pero.colors.Orange.lighter(.3)img.draw_arc(100,100,68,pero.rads(220),pero.rads(260))# hatpath=pero.Path(pero.WINDING)path.ellipse(100,27,40,10)path.ellipse(100,17,30,10)path.rect(85,17,30,10)mat=pero.Matrix().rotate(pero.rads(20),100,100)path.transform(mat)img.line_color=Noneimg.fill_color=pero.colors.Blackimg.draw_path(path)# show imageimg.show()RequirementsPython 3.6+NumpyPIL (Pillow)Supported BackendsPyQt5PyQt6PySide2PySide6wxPythonPyCairoPyMuPDFPythonista iOS AppInstallationTheperolibrary is fully implemented in Python. No additional compiler is necessary. After downloading the source
code just run the following command from theperofolder:$ python setup.py installor simply use pip$ pip install peroDisclaimerThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.Please note that theperolibrary is still in an alpha state. Any changes in its API may occur.UsageUsing default backendIf you just want to draw an image using whatever the default backend is (for requested format), or show the image
directly (requiresPyQt5,PyQt6,PySide2,PySide6,wxPythonorPythonista iOS App),
just create animageand use it as any otherperocanvas:importpero# init sizewidth=200height=200# init imageimg=pero.Image(width=width,height=height)# draw graphicsimg.line_color="b"img.fill_color="w"img.fill()img.draw_circle(100,100,75)# save to fileimg.export('image.png')# show in viewerimg.show()Using PyQt5, PyQt6, PySide2 or PySide6Inside aQWidgetyou can create aQPainterand encapsulate it into theperocanvas:importperofromPyQt5.QtGuiimportQPainter# init sizewidth=200height=200# init painterqp=QPainter()qp.begin(self)qp.setRenderHint(QPainter.RenderHint.Antialiasing)# init canvascanvas=pero.qt.QtCanvas(qp,width=width,height=height)# draw graphicscanvas.line_color="b"canvas.fill_color="w"canvas.fill()canvas.draw_circle(100,100,75)# end drawingqp.end()Using wxPythonInside awxAppyou can use just about anywxDCyou want and encapsulate it into theperocanvas:importperoimportwx# init sizewidth=200height=200# create DCbitmap=wx.Bitmap(width,height)dc=wx.MemoryDC()dc.SelectObject(bitmap)# use GCDCif'wxMac'notinwx.PlatformInfo:dc=wx.GCDC(dc)# init canvascanvas=pero.wx.WXCanvas(dc,width=width,height=height)# draw graphicscanvas.line_color="b"canvas.fill_color="w"canvas.fill()canvas.draw_circle(100,100,75)Using PyCairoDepending on the final image format, choose appropriatecairosurface, get the drawing context and encapsulate it into
theperocanvas:importperoimportcairo# init sizewidth=200height=200# create cairo drawing contextsurface=cairo.PSSurface('image.eps',width,height)dc=cairo.Context(surface)# init canvascanvas=pero.cairo.CairoCanvas(dc,width=width,height=height)# draw graphicscanvas.line_color="b"canvas.fill_color="w"canvas.fill()canvas.draw_circle(100,100,75)# save to filedc.show_page()Using PyMuPDFCreate a document, add new page and encapsulate it into theperocanvas:importperoimportfitz# init sizewidth=200height=200# init documentdoc=fitz.open()page=doc.newPage(width=width,height=height)# init canvascanvas=pero.mupdf.MuPDFCanvas(page)# draw graphicscanvas.line_color="b"canvas.fill_color="w"canvas.fill()canvas.draw_circle(100,100,75)# save to filedoc.save('image.pdf')doc.close()Using SVGTheperolibrary implements its own way to draw and save SVG files. Just create aperocanvas:importpero# init sizewidth=200height=200# init canvascanvas=pero.svg.SVGCanvas(width=width,height=height)# draw graphicscanvas.line_color="b"canvas.fill_color="w"canvas.fill()canvas.draw_circle(100,100,75)# save to filewithopen('test.svg','w',encoding='utf-8')asf:f.write(canvas.get_xml())Using PythonistaInitialize a newui.ImageContextand create aperocanvas:importperoimportui# init sizewidth=200height=200# open contextwithui.ImageContext(width,height)asctx:# init canvascanvas=pero.pythonista.UICanvas(width=width,height=height)# draw graphicscanvas.line_color="b"canvas.fill_color="w"canvas.fill()canvas.draw_circle(100,100,75)# show imageimg=ctx.get_image()img.show()Using glyphs and dynamic propertiesSimilar tod3jsJavaScript library, most of the properties of pre-buildpero.Glyphsobjects can
be specified as a function, to which given data source is automatically provided. Together withpero.scales(and maybe
thepero.Axis)this can be used to make simple plots easily.importperoimportnumpy# init sizewidth=400height=300padding=50# init datax_data=numpy.linspace(-numpy.pi,numpy.pi,50)y_data=numpy.sin(x_data)# init scalesx_scale=pero.LinScale(in_range=(min(x_data),max(x_data)),out_range=(padding,width-padding))y_scale=pero.LinScale(in_range=(-1,1),out_range=(height-padding,padding))color_scale=pero.GradientScale(in_range=(-1,1),out_range=pero.colors.Spectral)# init markermarker=pero.Circle(size=8,x=lambdad:x_scale.scale(d[0]),y=lambdad:y_scale.scale(d[1]),line_color=lambdad:color_scale.scale(d[1]).darker(.2),fill_color=lambdad:color_scale.scale(d[1]))# init imageimage=pero.Image(width=width,height=height)# fillimage.fill("w")# draw pointsmarker.draw_many(image,zip(x_data,y_data))# show imageimage.show()ExamplesIn theexamplesfolder you will find sample codes to generate and
understand all the following images. Check the image name and find corresponding python draw file.