Plot a live index graph using matplotlib - python - python-2.7

I'm trying to plot a lines graph using matplotlib - python.
The graph should look like below image:
I used patches.PathPatch to graph the image above, with specific vertices and codes, but I'm facing some technical issues when trying to plot live data above what the desired graph. For each reading, the graph gets regenerated (I get total of 8 figures for 8 unique readings). How do I stop this from happening? I just need the image above to be my background graph (as index graph), and live data to be plotted above that graph.... Also, any way to simplify below code? Any help would be greatly appreciated
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
import serial # import Serial Library
import numpy # Import numpy
from drawnow import *
tempF= []
pressure=[]
arduinoData = serial.Serial('/dev/cu.AdafruitEZ-Link64b9-SPP', 9600) #Creating our serial object named arduinoData
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt=0
fig = plt.figure()
ax = fig.add_subplot(111)
def makeFig(): #Create a function that makes our desired plot
codes = [Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
]
verts1 = [
(0, 0), # left, bottom
(0, 23), # left, top
(1, 18), # right, top
(1, 0), # right, bottom
(0, 0), # ignored
(1, 0), # left, bottom
(1, 18), # left, top
(2, 16), # right, top
(2, 0), # right, bottom
(1, 0), # ignored
(2, 0), # left, bottom
(2, 16), # left, top
(3, 15), # right, top
(3, 0), # right, bottom
(2, 0), # ignored
(3, 0), # left, bottom
(3, 15), # left, top
(4, 14), # right, top
(4, 0), # right, bottom
(3, 0), # ignored
(4, 0), # left, bottom
(4, 14), # left, top
(5, 13), # right, top
(5, 0), # right, bottom
(4, 0), # ignored
(5, 0), # left, bottom
(5, 13), # left, top
(6, 12), # right, top
(6, 0), # right, bottom
(5, 0), # ignored
(6, 0), # left, bottom
(6, 12), # left, top
(7, 10), # right, top
(7, 0), # right, bottom
(6, 0), # ignored
]
verts2 = [
(0, 23), # left, bottom
(0, 45), # left, top
(1, 39), # right, top
(1, 18), # right, bottom
(0, 23), # ignored
(1, 18), # left, bottom
(1, 39), # left, top
(2, 38), # right, top
(2, 16), # right, bottom
(1, 18), # ignored
(2, 16), # left, bottom
(2, 38), # left, top
(3, 34), # right, top
(3, 15), # right, bottom
(2, 16), # ignored
(3, 15), # left, bottom
(3, 34), # left, top
(4, 30), # right, top
(4, 14), # right, bottom
(3, 15), # ignored
(4, 14), # left, bottom
(4, 30), # left, top
(5, 23), # right, top
(5, 13), # right, bottom
(4, 14), # ignored
(5, 13), # left, bottom
(5, 23), # left, top
(6, 19), # right, top
(6, 12), # right, bottom
(5, 13), # ignored
(6, 12), # left, bottom
(6, 19), # left, top
(7, 16), # right, top
(7, 10), # right, bottom
(6, 12), # ignored
]
verts3 = [
(0, 45), # left, bottom
(0, 50), # left, top
(1, 50), # right, top
(1, 39), # right, bottom
(0, 45), # ignored
(1, 39), # left, bottom
(1, 50), # left, top
(2, 50), # right, top
(2, 38), # right, bottom
(1, 39), # ignored
(2, 38), # left, bottom
(2, 50), # left, top
(3, 50), # right, top
(3, 34), # right, bottom
(2, 38), # ignored
(3, 34), # left, bottom
(3, 50), # left, top
(4, 46), # right, top
(4, 30), # right, bottom
(3, 34), # ignored
(4, 30), # left, bottom
(4, 46), # left, top
(5, 32), # right, top
(5, 23), # right, bottom
(4, 30), # ignored
(5, 23), # left, bottom
(5, 32), # left, top
(6, 28), # right, top
(6, 19), # right, bottom
(5, 23), # ignored
(6, 19), # left, bottom
(6, 28), # left, top
(7, 24), # right, top
(7, 16), # right, bottom
(6, 19), # ignored
]
verts4 = [
(0, 50), # left, bottom
(0, 60), # left, top
(1, 60), # right, top
(1, 50), # right, bottom
(0, 50), # ignored
(1, 50), # left, bottom
(1, 60), # left, top
(2, 60), # right, top
(2, 50), # right, bottom
(1, 50), # ignored
(2, 50), # left, bottom
(2, 60), # left, top
(3, 60), # right, top
(3, 50), # right, bottom
(2, 50), # ignored
(3, 50), # left, bottom
(3, 60), # left, top
(4, 60), # right, top
(4, 46), # right, bottom
(3, 50), # ignored
(4, 46), # left, bottom
(4, 60), # left, top
(5, 60), # right, top
(5, 32), # right, bottom
(4, 46), # ignored
(5, 32), # left, bottom
(5, 60), # left, top
(6, 60), # right, top
(6, 28), # right, bottom
(5, 32), # ignored
(6, 28), # left, bottom
(6, 60), # left, top
(7, 60), # right, top
(7, 24), # right, bottom
(6, 19), # ignored
]
path = Path(verts1, codes)
path2 = Path(verts2, codes)
path3 = Path(verts3, codes)
path4 = Path(verts4, codes)
patch = patches.PathPatch(path, facecolor='green', alpha=0.8)
patch2 = patches.PathPatch(path2, facecolor='orange', alpha=0.8)
patch3 = patches.PathPatch(path3, facecolor='red', alpha=0.8)
patch4 = patches.PathPatch(path4, facecolor='purple', alpha=0.8)
#ax.axhline(5, linestyle='--', color='k') # horizontal lines
#ax.axvline(0, linestyle='--', color='k') # vertical lines
ax.add_patch(patch)
ax.add_patch(patch2)
ax.add_patch(patch3)
ax.add_patch(patch4)
ax.set_xlim(0,7)
ax.set_ylim(0,60)
plt.grid()
plt.show()
plt.plot(tempF, 'ro-', label='sensor 1') #plot the temperature
plt.legend(loc='upper left') #plot the legend
plt.plot(pressure, 'bo-', label='sensor 2') #plot the temperature
plt.legend(loc='upper left')
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
arduinoString = arduinoData.readline() #read the line of text from the serial port
dataArray = arduinoString.split(',') #Split it into an array called dataArray
print dataArray[0]
print dataArray[1]
temp = float( dataArray[0]) #Convert first element to floating number and put in temp
P = float( dataArray[1]) #Convert second element to floating number and put in P
tempF.append(temp) #Build our tempF array by appending temp readings
pressure.append(P) #Building our pressure array by appending P readings
drawnow(makeFig) #Call drawnow to update our live graph
plt.pause(.000001) #Pause Briefly. Important to keep drawnow from crashing
cnt=cnt+1
if(cnt>150): #If you have 50 or more points, delete the first one from the array
tempF.pop(0) #This allows us to just see the last 50 data points
pressure.pop(0)

Using how your input is currently styled:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
path1 = pd.DataFrame([(0,0),(0,23),(1,18),(1,0),(0,0),(1,0),(1,18),(2,16),(2,0),(1,0),(2,0),(2,16),(3,15),(3,0),(2,0),(3,0),(3,15),(4,14),(4,0),(3,0),(4,0),(4,14),(5,13),(5,0),(4,0),(5,0),(5,13),(6,12),(6,0),(5,0),(6,0),(6,12),(7,10),(7,0),(6,0),])
path2 = pd.DataFrame([(0,23),(0,45),(1,39),(1,18),(0,23),(1,18),(1,39),(2,38),(2,16),(1,18),(2,16),(2,38),(3,34),(3,15),(2,16),(3,15),(3,34),(4,30),(4,14),(3,15),(4,14),(4,30),(5,23),(5,13),(4,14),(5,13),(5,23),(6,19),(6,12),(5,13),(6,12),(6,19),(7,16),(7,10),(6,12),])
path3 = pd.DataFrame([(0,45),(0,50),(1,50),(1,39),(0,45),(1,39),(1,50),(2,50),(2,38),(1,39),(2,38),(2,50),(3,50),(3,34),(2,38),(3,34),(3,50),(4,46),(4,30),(3,34),(4,30),(4,46),(5,32),(5,23),(4,30),(5,23),(5,32),(6,28),(6,19),(5,23),(6,19),(6,28),(7,24),(7,16),(6,19),])
path4 = pd.DataFrame([(0,50),(0,60),(1,60),(1,50),(0,50),(1,50),(1,60),(2,60),(2,50),(1,50),(2,50),(2,60),(3,60),(3,50),(2,50),(3,50),(3,60),(4,60),(4,46),(3,50),(4,46),(4,60),(5,60),(5,32),(4,46),(5,32),(5,60),(6,60),(6,28),(5,32),(6,28),(6,60),(7,60),(7,24),(6,19),])
p1y = list(path1[1][1::5])
p1y.append(path1[1][32])
p2y = list(path2[1][1::5])
p2y.append(path2[1][32])
p3y = list(path3[1][1::5])
p3y.append(path3[1][32])
p4y = list(path4[1][1::5])
p4y.append(path4[1][32])
xline = np.array(range(len(p1y)))
ax.fill_between(xline, np.zeros(len(p1y)), p1y, facecolor='green', alpha=0.8)
ax.fill_between(xline, p1y, p2y, facecolor='orange', alpha=0.8)
ax.fill_between(xline, p2y, p3y, facecolor='red', alpha=0.8)
ax.fill_between(xline, p3y, p4y, facecolor='purple', alpha=0.8)
ax.set_xlim(0,7)
ax.set_ylim(0,60)
plt.grid()
plt.show()
However, if you can simplify your incoming verts data:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
path1 = [23, 18, 16, 15, 14, 13, 12, 10]
path2 = [45, 39, 38, 34, 30, 23, 19, 16]
path3 = [50, 50, 50, 50, 46, 32, 28, 24]
path4 = [60, 60, 60, 60, 60, 60, 60, 60]
xline = np.array(range(len(path1)))
ax.fill_between(xline, np.zeros(len(path1)), path1, facecolor='green', alpha=0.8)
ax.fill_between(xline, path1, path2, facecolor='orange', alpha=0.8)
ax.fill_between(xline, path2, path3, facecolor='red', alpha=0.8)
ax.fill_between(xline, path3, path4, facecolor='purple', alpha=0.8)
ax.set_xlim(0,7)
ax.set_ylim(0,60)
plt.grid()
plt.show()
A note of difference, you are using Python 2.6 and this was done using Python 3.6.0 and Matplotlib 2.0.0

Related

Change 5th number from list

I have a one function for my calendar
def formatweek(self, theweek, events):
week = ''
for d, weekday in theweek:
#return f'<tr style="color:blue";> {week} </tr>'
week += self.formatday(d, events)
return f'<tr> {week} </tr>'
def formatday(self, day, events):
events_per_day = events.filter(start_time__day=day)
d = ''
for event in events_per_day:
d += f'<li> {event.get_html_url} </li>'
if day != 0:
return f"<td><span class='date'>{day}</span><ul> {d} </ul></td>"
return '<td></td>'
where theweek is a list of pairs:
[(1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6)]
[(8, 0), (9, 1), (10, 2), (11, 3), (12, 4), (13, 5), (14, 6)]
[(15, 0), (16, 1), (17, 2), (18, 3), (19, 4), (20, 5), (21, 6)]
[(22, 0), (23, 1), (24, 2), (25, 3), (26, 4), (27, 5), (28, 6)]
[(29, 0), (30, 1), (31, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
Where the first number(for example(1)) in pairs is day in month (1.januar) and the second is a number for weekday name (0-Monday, 1-Thuesday etc...)
d and weekday become an int in loop. d = days of month (1,2,3,4....)and Weekday = number for days (Mon - 0, Tue - 1 etc...).
It returns this return f'<tr> {week} </tr>'for the table. And I would like to make it return for 5th and 6th weekday numbers (Saturday, Sunday) additionally f'<tr style="color:blue";> {5 (or 6)} </tr>'.
So the simple say: I would like to make a weekend days to become blue.

Only last subplot shows (others are blank) and it is the wrong plot

I'm having trouble setting up subplots in matplotlib. Below is a screenshot of the layout I am trying to achieve. I've only gone up through ax4, as I have encountered two issues. Code is also below. (Note that on their own, the functions plot as expected.) I want to continue using "subplot2grid" instead of the other options, and I'm using Python 2.7
Expected behavior:
Plots ax1, ax2, and ax3 should be a World Shade Relief Map, with position as shown in desired layout below.
Plot ax4 should be a scatter plot, with position as shown in desired layout below.
Actual behavior:
Plots ax1, ax2, and ax3 are all blank
Plot ax4 is not a scatter, but it's actually the map; layout is also wrong.
I thought I was missing a "hold on" type feature, but it looks like that's not how matplotlib works.
I also made sure I defined the plot limits in myplotA function.
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
from mpl_toolkits.basemap import Basemap
from random import randint
def myplotA(plotnum, title):
south = 34.0129656032
north = 34.721878622
west = -116.7615176
east = -116.336918412
center = [(east + west) / 2, (north + south) / 2]
m = Basemap(llcrnrlon=west, llcrnrlat=south, urcrnrlon=east, urcrnrlat=north, resolution='c', epsg=4326, lon_0=center[0], lat_0=center[1], suppress_ticks=False)
img = m.arcgisimage(service="World_Shaded_Relief", xpixels=2000)
img.set_alpha(0.5)
plt.xticks(rotation='horizontal')
plotnum.xaxis.set_major_formatter(ScalarFormatter(useOffset=False))
plotnum.axis([west, east, south, north])
for label in (plotnum.get_xticklabels() + plotnum.get_yticklabels()):label.set_fontsize(9)
plt.gca().set_title(title, fontsize=12)
def myplotB(plotnum, title):
x = [randint(0, 10) for i in range(0, 6)]
y = [randint(0, 10) for i in range(0, 6)]
plotnum.scatter(x, y, s=4)
plotnum.xaxis.set_major_formatter(ScalarFormatter(useOffset=False))
plt.xlabel('xlabel', fontsize=8)
plt.ylabel('ylabel', fontsize=8)
plt.gca().set_title(title, fontsize=12)
fig = plt.figure(figsize=(11, 17))
ax1 = plt.subplot2grid((8, 3), (0, 0), rowspan=4, colspan=1)
ax2 = plt.subplot2grid((8, 3), (0, 1), rowspan=4, colspan=1)
ax3 = plt.subplot2grid((8, 3), (0, 2), rowspan=4, colspan=1)
ax4 = plt.subplot2grid((8, 3), (5, 0), rowspan=1, colspan=2)
myplotA(ax1, 'ax1')
myplotA(ax2, 'ax2')
myplotA(ax3, 'ax3')
myplotB(ax4, 'ax4')
plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=.1, hspace=.1)
fig.savefig(outpath + '\\' + 'mytest.pdf')
Desired Layout
Desired Layout Image
Actual Result
Actual Result Image
You pass the axes to the plotting functions, but inside of those you need to actually use those passed axes. Else all the plt commands will apply to the currently active axes, which is the last one you create.
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
from mpl_toolkits.basemap import Basemap
from random import randint
def myplotA(ax, title):
south = 34.0129656032
north = 34.721878622
west = -116.7615176
east = -116.336918412
center = [(east + west) / 2, (north + south) / 2]
m = Basemap(llcrnrlon=west, llcrnrlat=south, urcrnrlon=east, urcrnrlat=north,
resolution='c', epsg=4326, lon_0=center[0], lat_0=center[1],
suppress_ticks=False, ax=ax)
img = m.arcgisimage(service="World_Shaded_Relief", xpixels=2000)
img.set_alpha(0.5)
plt.setp(ax.get_xticklabels(), rotation='horizontal')
plt.setp(ax.get_xticklabels() + ax.get_yticklabels(), fontsize=9)
ax.xaxis.set_major_formatter(ScalarFormatter(useOffset=False))
ax.axis([west, east, south, north])
ax.set_title(title, fontsize=12)
def myplotB(ax, title):
x = [randint(0, 10) for i in range(0, 6)]
y = [randint(0, 10) for i in range(0, 6)]
ax.scatter(x, y, s=4)
ax.xaxis.set_major_formatter(ScalarFormatter(useOffset=False))
ax.set_xlabel('xlabel', fontsize=8)
ax.set_ylabel('ylabel', fontsize=8)
ax.set_title(title, fontsize=12)
fig = plt.figure(figsize=(11, 8))
ax1 = plt.subplot2grid((8, 3), (0, 0), rowspan=4, colspan=1)
ax2 = plt.subplot2grid((8, 3), (0, 1), rowspan=4, colspan=1)
ax3 = plt.subplot2grid((8, 3), (0, 2), rowspan=4, colspan=1)
ax4 = plt.subplot2grid((8, 3), (5, 0), rowspan=1, colspan=2)
myplotA(ax1, 'ax1')
myplotA(ax2, 'ax2')
myplotA(ax3, 'ax3')
myplotB(ax4, 'ax4')
plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=.1, hspace=.1)
fig.savefig('mytest.pdf')
plt.show()
Thanks #ImportanceOfBeingErnest
I tried your code above, but still same plot as shown in the "actual results" part of the OP. However, your words describe the problem: I'm activating the axis, and then activating the next one before plotting. Below does the trick--perhaps this is what you had anyhow and just mistakenly copy/pasted the original code. Thanks for point me in the right direction.
fig = plt.figure(figsize=(11, 17))
ax1 = plt.subplot2grid((8, 3), (0, 0), rowspan=4, colspan=1)
myplotA(ax1, 'ax1')
ax2 = plt.subplot2grid((8, 3), (0, 1), rowspan=4, colspan=1)
myplotA(ax2, 'ax2')
ax3 = plt.subplot2grid((8, 3), (0, 2), rowspan=4, colspan=1)
myplotA(ax3, 'ax3')
ax4 = plt.subplot2grid((8, 3), (4, 0), rowspan=1, colspan=2)
myplotB(ax4, 'ax4')
plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=.1, hspace=.1)
fig.savefig(outpath + '\\' + 'mytest.pdf')
Link to image of output plot; looks as expected

Create partial sublists by index, discarding some data [(x,y,z),...] to [(x,y),...]

I got a list1 which has N items in each item it has (x,y,z). For z in list2, I need to create a new list which has items from list1 with only (x,y) this need to be done for list3, list4 too.
list1 = [(1250, 1442, 0), (1280, 1655, 1), (1029, 1680, 2), (624, 1573, 3), (732, 1159, 4), (1530, 1634, 5), (1885, 1628, 6), (2152, 1834, 7), (1252, 2459, 8), (1309, 3023, 9), (1376, 3585, 10), (1571, 2388, 11), (1682, 2952, 12), (1686, 3579, 13), (1184, 1391, 14), (1291, 1382, 15), (1117, 1440, 16), (1361, 1400, 17)]
list2 = [0,1,14,15,16,17]
list3 = [2,3,4,5,6,7,8,11]
list4 = [9,10,12,13]
For example, list5 which is between list1 and list2 looks like
list5 = [(1250, 1442),(1280, 1655),(1184, 1391)......]
Can anyone suggest a fast way to do it? Thank you
Easy enough:
def getXYfromIndex(l, indexes):
"""Returns x,y from bigger list 'l' containing (x,y,z).
Uses only those elements (by index) of 'l' that are in 'indexes'"""
# list comprehension: returns x,y for each index in 'l' that is in 'indexes'
return [(x,y) for x,y,_ in (l[i] for i in indexes)]
list1 = [(1250, 1442, 0), (1280, 1655, 1), (1029, 1680, 2), (624, 1573, 3), (732, 1159, 4),
(1530, 1634, 5), (1885, 1628, 6), (2152, 1834, 7), (1252, 2459, 8),
(1309, 3023, 9), (1376, 3585, 10), (1571, 2388, 11), (1682, 2952, 12),
(1686, 3579, 13), (1184, 1391, 14), (1291, 1382, 15), (1117, 1440, 16),
(1361, 1400, 17)]
list2 = [0,1,14,15,16,17]
list3 = [2,3,4,5,6,7,8,11]
list4 = [9,10,12,13]
print(getXYfromIndex(list1,list2)) # use list5 = getXYfromIndex(list1,list2)
print(getXYfromIndex(list1,list3)) # to work with those (x,y) - I just print them
print(getXYfromIndex(list1,list4))
Output:
[(1250, 1442), (1280, 1655), (1184, 1391), (1291, 1382), (1117, 1440), (1361, 1400)]
[(1029, 1680), (624, 1573), (732, 1159), (1530, 1634), (1885, 1628), (2152, 1834),
(1252, 2459), (1571, 2388)]
[(1309, 3023), (1376, 3585), (1682, 2952), (1686, 3579)]

generating a multi dict with random numbers

I have a data structure defined as follow :
reqList[i] = [multidict({
1: ['type1', randint(1, 5), randint(1, 5), randint(1, 5)],
2: ['type2', randint(1, 5), randint(1, 5), randint(1, 5)],
3: ['type3', randint(1, 5), randint(1, 5), randint(1, 5)],
4: ['type4', randint(1, 5), randint(1, 5), randint(1, 5)]
}),
multidict({
(1, 2): randint(500, 1000),
(2, 3): randint(500, 1000),
(3, 4): randint(500, 1000)
})]
I want to make the creation of this data structure automatic in a for loop for example. I did this:
nodes = {}
for j in range(1, randint(2, 5)):
nodes[j] = ['type%d' % j, randint(1, 5), randint(1, 5), randint(1, 5)]
edges = {}
for kk in range(1, len(nodes)):
edges[(kk, kk + 1)] = randint(500, 1000)
print "EDGES", edges
reqList[i] = [multidict(nodes),
multidict(edges)]
del (nodes, edges)
when I look into the outputed edges the order of the keys is not kept ! For example I am getting this:
EDGES {(1, 2): 583, (3, 4): 504, (2, 3): 993}
I want it to be :
EDGES {(1, 2): 583, (2, 3): 993, (3, 4): 504}
Does the way I am coding it is correct ? if not, could you suggest a better way knowing that I need to get the same result as in the first example?
Dictionary in 2.7 is unordered and you can not keep the order of insert, unless you manually keep a reference on what key got inserted and when in a separate list. The module collections contains a class called OrderedDict that acts like a dictionary but keeps the inserts in ordered, which is what you could use (it also uses a list to keep track of the keys insert but uses a double link list to speed up deletion of keys).
There's no other way other than these two method.
from collections import OrderedDict
nodes = {}
for j in range(1, randint(2, 5)):
nodes[j] = ['type%d' % j, randint(1, 5), randint(1, 5), randint(1, 5)]
edges = OrderedDict()
for kk in range(1, len(nodes)):
edges[(kk, kk + 1)] = randint(500, 1000)
print "EDGES", edges # EDGES OrderedDict([((1, 2), 898), ((2, 3), 814)])
print edges[(1,2)] # still yields the correct number
You can read more about OrderedDict here

How to create a table using report lab that can change the number of rows dynamically

I am creating a PDF that has a table in it except the table rows will differ from PDF to PDF
table = Table(data, colWidths=[3.05 * cm, 4.5 * cm, 4 * cm,3* cm, 3 * cm])
table.setStyle(TableStyle([
('INNERGRID', (0,0), (-1,-1), 0.25, black),
('BOX', (0,0), (-1,-1), 0.25, black),
]))
table.wrapOn(self.pdf, self.width_table, self.height_table)
table.drawOn(self.pdf, *self.coord(1.0, 18.6,cm))
That is what I am using to create the table with now.
Thanks,
It's a central question for anyone starting out.
Below is a complete workable code to start build from. The answer is, as you wrote yourself in the comment, is to loop. Problems, though, that anyone later will discover is how you deal with multi-page tables and design of those tables. There are various solutions worth looking into.
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.platypus import Frame, PageTemplate, KeepInFrame
from reportlab.lib.units import cm
from reportlab.platypus import (Table, TableStyle, BaseDocTemplate)
########################################################################
def create_pdf():
"""
Create a pdf
"""
# Create a frame
text_frame = Frame(
x1=3.00 * cm, # From left
y1=1.5 * cm, # From bottom
height=19.60 * cm,
width=15.90 * cm,
leftPadding=0 * cm,
bottomPadding=0 * cm,
rightPadding=0 * cm,
topPadding=0 * cm,
showBoundary=1,
id='text_frame')
# Create a table
test_table = []
data = []
for i in range(11, 1, -1):
column1data = f'Column_1 on row {i}'
column2data = f'Column_2 on row {i}'
data.append([column1data, column2data])
data_table = Table(data, 15.90 * cm / 2)
data_table.setStyle(TableStyle([
# Title
('LEFTPADDING', (0, 0), (1, 0), 0),
('RIGHTPADDING', (0, 0), (1, 0), 0),
('VALIGN', (0, 0), (1, 0), 'TOP'),
('ALIGN', (0, 0), (1, 0), 'CENTRE'),
# DataTable
('ALIGN', (0, 1), (1, -1), 'CENTRE'),
('SIZE', (0, 1), (-1, -1), 7),
('LEADING', (0, 1), (-1, -1), 8.4),
('VALIGN', (0, 1), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 1), (-1, -1), 2.6),
('BOTTOMPADDING', (0, 1), (-1, -1), 2.6),
('LINEBELOW', (0, 1), (-1, -1), 0.3, colors.gray),
]))
test_table.append(data_table)
test_table = KeepInFrame(0, 0, test_table, mode='overflow')
# Building the story
story = [test_table] # adding test_table table (alternative, story.add(test_table))
# Establish a document
doc = BaseDocTemplate("Example_output.pdf", pagesize=letter)
# Creating a page template
frontpage = PageTemplate(id='FrontPage',
frames=[text_frame]
)
# Adding the story to the template and template to the document
doc.addPageTemplates(frontpage)
# Building doc
doc.build(story)
# ----------------------------------------------------------------------
if __name__ == "__main__":
create_pdf() # Printing the pdf