ReportLab make a multipage table with header and footer - python

I'm trying to make a simple report that has a header/footer and table in the middle with a ot of rows.
I'm new to ReportLab and the reason I switched from WeasyPrint is because I wanted the engine to handle page breaks nicely.
Below is my code :
def print_users(self):
buffer = self.buffer
doc = BaseDocTemplate(buffer,
rightMargin=20,
leftMargin=20,
topMargin=20,
bottomMargin=20,
pagesize=landscape(self.pagesize))
frame = Frame(
doc.leftMargin,
doc.bottomMargin,
doc.width,
doc.height - inch * 0.5,
id='normal',
showBoundary=1)
template = PageTemplate(id='all_pages', frames=frame, onPage=self._header_footer)
doc.addPageTemplates([template])
styles=getSampleStyleSheet()
# Our container for 'Flowable' objects
elements = []
elements.append(Spacer(1, 3*units.cm))
title = self.event.name
elements.append(Paragraph(self.event.name, styles["Normal"]))
elements.append(Spacer(1, 1*units.cm))
data = []
titles = ['First name', 'Last name', 'Position', 'Institution']
data.append(titles)
for invitation in self.invitations:
line = []
line.append(invitation.person.firstname)
line.append(invitation.person.lastname)
line.append(invitation.person.position)
line.append(invitation.institution.name)
data.append(line)
t=Table(data)
t.setStyle(TableStyle(
[('LINEBELOW',(0,0),(-1,-1),1, colors.gray)]))
table_story = [t]
elements.append(t)
# t_keep = KeepInFrame(0, 0, table_story, hAlign='CENTER', vAlign='MIDDLE', fakeWidth=False)
# write the document to disk
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
# elements.append(t_keep)
doc.build(elements)
The header/footer are showing fine and so is the table , but when we go to the second page, the table overrides the header.
I tried to add a frame with KeepInFrame ut the table becomes so small to fit inside the frame in one page.
It seems like a simple task, but i can't find a way to simply take into consideration the header on all pages. (Or maybe i'm taking the wrong approach?)

I face the same issue but in my case was because my header take like 20 mm of space meanwhile my topMargin was only 10mm. Changing the topMargin to more than 20 fix the issue.

Related

Multiselect reloads the page with Streamlit

I am facing an issue that when I select any items from the st.multiselect, the page is reloaded, and the selected item is also reset.
I tried to add st.session_state, but it did not help much. Can anyone have experience in this case? or any comment would be highly appreciated.
Below is my code
def load_data(self, db_result):
# Load full dataset of RecordSet
df_record_set, count_record_set = self.load_record_set_data(record_set_col)
st.write('### Full RecordSet', df_record_set)
st.info('Total items: ' + str(count_record_set))
record_set_selected_indices = st.selectbox('Select rows:', df_record_set.index)
record_set_selected_rows = df_record_set.loc[record_set_selected_indices]
st.write('### Selected Rows', record_set_selected_rows)
# Load full dataset of Exporting Template
df_exp_tem, count_exp_tem = self.load_exp_tem_data(exp_tem_col)
st.write('### Full Exporting Template', df_exp_tem)
st.info('Total items: ' + str(count_exp_tem))
exp_tem_selected_indices = st.selectbox('Select rows:', df_exp_tem.index)
exp_tem_selected_rows = df_exp_tem.loc[exp_tem_selected_indices]
st.write('### Selected Rows', exp_tem_selected_rows)
# Add a 'Generate channel' button
generate_clicked = st.button('Generate channel')
if generate_clicked or st.session_state.generate_channel:
st.session_state.generate_channel = True
# Get RecordSet Id to find related ECG with channels
record_set_id=ObjectId(record_set_selected_rows[cons.HEADER_ID])
# Query to get list of ECG channels based on RecordSet Id
result = self.load_channel_list_from_record_set(ecg_col, record_set_col, record_set_id)
# Nested For Loop
ecg_data = [
ECG(id=y[cons.ECG_ID_SHORT],
file_name=y[cons.ECG_FILE_NAME],
channel=y[cons.ECG_CHANNEL])
for x in result for y in x[ecg_col.name]
]
# Get text of channels based on selected exporting template
list_channels_str = 'Channel ' + exp_tem_selected_rows[cons.HEADER_CHANNEL]
# Widgets will be genrated by the number of ECG records
# This is used for mapping channel between ECG record and Exporting template
for x in ecg_data:
st.write(list_channels_str + ' from selected exporting template')
st.multiselect('Record: ' + x.file_name, x.channel, key=str(x))
st.success('Clicked!')
map_clicked = st.button('Extract data')
if map_clicked:
st.write('test - ''Extract data'' button is clicked')
Screenshot:
Current: When 'V5' is selected, the page is reloaded and no item is selected.
Expectation: When 'V5' is selected, the page remains stable (no reload), and the item is added to the multiselect widget. Then, other items are also can be added.
Open question: Is that possible to have 2 different st.session_state to handle 2 different business processes in a function? If not, which is the best practice?
Thanks for your reading.

xlsxwriter's set_header not working as expected

I am using xlsxwriter 1.1.0 and Microsoft Excel 2016 I copied the example listed here (my version pasted below):
######################################################################
#
# This program shows several examples of how to set up headers and
# footers with XlsxWriter.
#
# The control characters used in the header/footer strings are:
#
# Control Category Description
# ======= ======== ===========
# &L Justification Left
# &C Center
# &R Right
#
# &P Information Page number
# &N Total number of pages
# &D Date
# &T Time
# &F File name
# &A Worksheet name
#
# &fontsize Font Font size
# &"font,style" Font name and style
# &U Single underline
# &E Double underline
# &S Strikethrough
# &X Superscript
# &Y Subscript
#
# &[Picture] Images Image placeholder
# &G Same as &[Picture]
#
# && Miscellaneous Literal ampersand &
#
# See the main XlsxWriter documentation for more information.
#
# Copyright 2013-2018, John McNamara, jmcnamara#cpan.org
#
import xlsxwriter
workbook = xlsxwriter.Workbook('E:\\headers_footers.xlsx')
preview = 'Select Print Preview to see the header and footer'
######################################################################
#
# A simple example to start
#
worksheet1 = workbook.add_worksheet('Simple')
header1 = '&CHere is some centered text.'
footer1 = '&LHere is some left aligned text.'
worksheet1.set_header(header1)
worksheet1.set_footer(footer1)
worksheet1.set_column('A:A', 50)
worksheet1.write('A1', preview)
######################################################################
#
# Insert a header image.
#
worksheet2 = workbook.add_worksheet('Image')
header2 = '&L&G'
# Adjust the page top margin to allow space for the header image.
worksheet2.set_margins(top=1.3)
worksheet2.set_header(header2, {'image_left': 'logo.png'})
worksheet2.set_column('A:A', 50)
worksheet2.write('A1', preview)
######################################################################
#
# This is an example of some of the header/footer variables.
#
worksheet3 = workbook.add_worksheet('Variables')
header3 = '&LPage &P of &N' + '&CFilename: &F' + '&RSheetname: &A'
footer3 = '&LCurrent date: &D' + '&RCurrent time: &T'
worksheet3.set_header(header3)
worksheet3.set_footer(footer3)
worksheet3.set_column('A:A', 50)
worksheet3.write('A1', preview)
worksheet3.write('A21', 'Next sheet')
worksheet3.set_h_pagebreaks([20])
######################################################################
#
# This example shows how to use more than one font
#
worksheet4 = workbook.add_worksheet('Mixed fonts')
header4 = '&C&"Courier New,Bold"Hello &"Arial,Italic"World'
footer4 = '&C&"Symbol"e&"Arial" = mc&X2'
worksheet4.set_header(header4)
worksheet4.set_footer(footer4)
worksheet4.set_column('A:A', 50)
worksheet4.write('A1', preview)
######################################################################
#
# Example of line wrapping
#
worksheet5 = workbook.add_worksheet('Word wrap')
header5 = "&CHeading 1\nHeading 2"
worksheet5.set_header(header5)
worksheet5.set_column('A:A', 50)
worksheet5.write('A1', preview)
######################################################################
#
# Example of inserting a literal ampersand &
#
worksheet6 = workbook.add_worksheet('Ampersand')
header6 = '&CCuriouser && Curiouser - Attorneys at Law'
worksheet6.set_header(header6)
worksheet6.set_column('A:A', 50)
worksheet6.write('A1', preview)
workbook.close()
On line 40, I changed the destination of the file and on line 68 I changed the name of the image; I have changed nothing apart from that. When I run the said program I don't see the images as well as the fonts. I have attached a screenshot below:
The program finished correctly without throwing any exception. Most of the other functions like set_column, insert_image etc. are working. But set_header isn't.
I don't know if any more information is required. Please feel free to demand for the same if required.
Headers and Footers in Excel don't show up in the normal view of the worksheet. As the output from the example says "Select Print Preview to see the header and footer".
In Print Preview mode the output looks like this:
Or in "Page Layout" view it looks like this:

Gtk TextView dynamic styling

I have a textview widget and I can apply tags such as bold, underline etc.
self.tags = {}
self.tags['bold'] = self.textbuffer.create_tag("bold", weight=Pango.Weight.BOLD)
self.tags['italic'] = self.textbuffer.create_tag("italic", style=Pango.Style.ITALIC)
self.tags['underline'] = self.textbuffer.create_tag("underline", underline=Pango.Underline.SINGLE)
self.tags['ubuntu'] = self.textbuffer.create_tag("ubuntu", family = "Ubuntu Mono")
self.tags['size'] = self.textbuffer.create_tag("size", font_desc=Pango.FontDescription.from_string("32"))
The problem is with the last one, If I apply the font size tag, then the rest don't work.
The function that handles the insert-text signal is the following :
def insert_with_tags(self, buffer, start_iter, data, data_len):
if data_len == 1:
start_iter.backward_char()
end = self.textbuffer.props.cursor_position
end_iter = self.textbuffer.get_iter_at_offset(end)
self.textbuffer.apply_tag(self.tags['size'], start_iter, end_iter)
for tag in self.format_toolbar.buttons:
if self.format_toolbar.buttons[tag].get_active():
self.textbuffer.apply_tag(self.tags[tag], start_iter, end_iter)
If I remove the line that applies the font size everything works, but I want to let the user to be able to modify the font (here im testing it with just 1 font, but its the same when I add tags for the rest) while typing and not just modify it for the entire buffer.

Two different pages with reportlab SimpleDocTemplate and Django

I'm using django and generating reports following this example, I need to generate a last page but without headers or footers and different content.
I'm trying to do this:
def print_example(self):
buffer = self.buffer
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=self.pagesize)
elements = []
elements.append(Paragraph('Content for all pages'), my_custom_style)
# ...
doc.build(elements, onFirstPage=self._header_footer, onLaterPages=self._header_footer,
canvasmaker=NumberedCanvas)
doc2 = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=self.pagesize)
elements2 = []
elements2.append(Paragraph('Content for the last page only'), my_custom_style)
doc2.build(elements2, canvasmaker=NumberedCanvas)
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
return pdf
Then only the last content appears and previous content dissapears.
How can I generate the last page with different content?
I don't think it's possible using SimpleDocTemplate but you can achieve this by using BaseDocTemplate and defining your own templates.
Basic example
from reportlab.platypus import PageTemplate, BaseDocTemplate, NextPageTemplate, PageBreak
def headerFooterLayout(canvas, doc):
canvas.saveState()
canvas.setPageSize(self.pagesize)
# add header/footer
canvas.restoreState()
def emptyLayout(canvas, doc):
canvas.saveState()
canvas.setPageSize(self.pagesize)
canvas.restoreState()
pHeight, pWidth = self.pagesize
myFrame = Frame(0, 0, pHeight, pWidth, id='myFrame')
headerFooterTemplate = PageTemplate(id='headerFooterTemplate',
frames=[myFrame],
onPage=headerFooterLayout)
emptyTemplate = PageTemplate(id='emptyTemplate',
frames=[myFrame],
onPage=emptyLayout)
elements = []
elements.append(Paragraph('blah', style))
elements.append(NextPageTemplate('emptyTemplate'))
elements.append(PageBreak())
elements.append(Paragraph('last page', style))
doc = BaseDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72)
doc.addPageTemplates([headerFooterTemplate, emptyTemplate])
doc.build(elements)
It's been quite a while since I used this so there may well be some issues but comment if something doesn't work.
This is all in the user guide but can be hard to find what you're looking for.

ReportLab: Flowable too large on page 1 in frame 'normal' of template 'First'

I build PDF using ReportLab. My program has a MyDocTemplate(SimpleDocTemplate) class with two methods: beforePage(self) and afterPage(self) which add header and footer (as PNG image) on every page. There is also a MyDocStyle class which describe ParagraphStyle.
Main method looks like this:
TITLE = Paragraph(Title, MyDocStyle.h1)
TO = Paragraph(To, MyDocStyle.h2)
FROM = Paragraph(From, MyDocStyle.h2)
SUBJECT = Paragraph(Subject, MyDocStyle.h2)
LONG_PARAGRAPH = Paragraph(Text, MyDocStyle.h3)
...
Elements = [TITLE, TO, FROM, SUBJECT, LONG_PARAGRAPH, ...]
doc = MyDocTemplete('output.pdf', pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=4*cm, bottomMargin=4*cm)
doc.build(Elements)
Data comes from CSV files and GUI. From time to time (depends on data length) I receive an error:
Flowable <Spacer at 0x2631120 frame=normal>...(1 x 5.66929133858) too large
on page 1 in frame 'normal'(469.88976378 x 603.118110236) of template 'First'
This exception stop my program. For short Paragraphs I set in MyDocStyle class h2.keepWithNext = 1 however it's not perfect solution. ReportLab split correctly long paragraph if end of paragraph does not "coincide" with end of page (text area).
How can I deal with it?
This error occurs when ReportLab try to split a Spacer over two pages. It seems that the only way to workaround this issue is wrap your Spacer into a KeepTogether element:
elements.append(KeepTogether(Spacer(width, height)))
Solved. Don't use Spacer (e.g. Spacer(1, 0.2*cm)) as a separator for Paragraph. Instead, define spaceBefore and spaceAfter in ParagraphStyle, for example:
ParagraphStyle(name = 'Normal',
fontName = "Verdana",
fontSize = 11,
leading = 15,
alignment = TA_JUSTIFY,
allowOrphans = 0,
spaceBefore = 20,
spaceAfter = 20,
wordWrap = 1)

Categories

Resources