How to convert QTableWidget data to PDF using PyQt5 Python - python

I am working on a python project where I have data stored in QTableWidget. I have to export this data into excel sheet and PDF. I have been able to export data to excel sheet using below code. But unable to understand how can I convert it to PDF.
filename, _ = QFileDialog.getSaveFileName(self, 'Save File', '', ".xls(*.xls)")
wbk = xlwt.Workbook()
sheet = wbk.add_sheet("sheet", cell_overwrite_ok=True)
style = xlwt.XFStyle()
font = xlwt.Font()
font.bold = True
style.font = font
model = self.home_ui.reports_table.model()
for c in range(model.columnCount()):
text = model.headerData(c, QtCore.Qt.Horizontal)
first_col = sheet.col(c+1)
l = len(text)
first_col.width = (256 * l) + 1000
sheet.write(0, c + 1, text, style=style)
for r in range(model.rowCount()):
text = model.headerData(r, QtCore.Qt.Vertical)
sheet.write(r + 1, 0, text, style=style)
for c in range(model.columnCount()):
for r in range(model.rowCount()):
text = model.data(model.index(r, c))
sheet.write(r + 1, c + 1, text)
wbk.save(filename)
Above code is working fine and saving data to excel.
I have looked into other questions with same topic but all of them are in c++. I am looking for python equivalent.
Can anyone give me some good suggestion on how to convert data to PDF. Please help. Thanks

When you review an answer you should not only see the code but the solution itself, that is, the logic that is in the background. In this particular case the solution is to create an HTML that shows the table content, and use QTextDocument with QPrinter to print the HTML to PDF.
Considering the above, it is not necessary to do the translation since it is enough to implement it from scratch since the logic is clear.
from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport
app = QtWidgets.QApplication([])
w = QtWidgets.QTableWidget(10, 10)
for i in range(10):
for j in range(10):
it = QtWidgets.QTableWidgetItem("{}-{}".format(i, j))
w.setItem(i, j, it)
filename = "table.pdf"
model = w.model()
printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.PrinterResolution)
printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat)
printer.setPaperSize(QtPrintSupport.QPrinter.A4)
printer.setOrientation(QtPrintSupport.QPrinter.Landscape)
printer.setOutputFileName(filename)
doc = QtGui.QTextDocument()
html = """<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
</head>"""
html += "<table><thead>"
html += "<tr>"
for c in range(model.columnCount()):
html += "<th>{}</th>".format(model.headerData(c, QtCore.Qt.Horizontal))
html += "</tr></thead>"
html += "<tbody>"
for r in range(model.rowCount()):
html += "<tr>"
for c in range(model.columnCount()):
html += "<td>{}</td>".format(model.index(r, c).data() or "")
html += "</tr>"
html += "</tbody></table>"
doc.setHtml(html)
doc.setPageSize(QtCore.QSizeF(printer.pageRect().size()))
doc.print_(printer)

Related

Reportlab paragraph frame split doesn't work

I have the report lab document, which is a mix of regular strings drawed with drawstring method and frames. I will get the table data from request, and i need to have the possibility to split the table to another page, because i don't know the size of the page. It works fine when the table is not big, but when the amount of rows is too big, it dust doesn't display anyting on the page. As per the reportlab lib, I simple need to use frame.split(), but it doesn't work. The code:
def generate_pdf(request: Request):
buffer = io.BytesIO()
pdfmetrics.registerFont(TTFont("Poppins-bold", f"static/fonts/poppins-800.ttf"))
pdfmetrics.registerFont(TTFont("Poppins-medium", "static/fonts/poppins-medium.ttf"))
pdfmetrics.registerFont(TTFont("Poppins-semibold", "static/fonts/poppins-semibold.ttf"))
pdfmetrics.registerFont(TTFont("Poppins-regular", "static/fonts/poppins-regular.ttf"))
doc = canvas.Canvas(buffer)
customColor = colors.Color(red=(39.0 / 255), green=(71.0 / 255), blue=(114.0 / 255))
doc.setFillColor(customColor)
doc.setFont(psfontname="Poppins-bold", size=20)
if request.data["type"] == "Purchase Order":
doc.drawString(50, 780, "Purchase Order")
else:
doc.drawString(50, 780, "Work Order")
doc = add_image(doc)
doc = draw_invoice_start_end(doc, request)
doc = draw_order_by_order_to(doc, request)
table_data = []
for i in range(50):
table_data.append(["item"])
frame1 = Frame(width=800, height=300, showBoundary=1, x1=50, y1=200)
story = []
table = Table(data=table_data, colWidths=None, rowHeights=None, style=None, splitByRow=1, repeatRows=0,
repeatCols=0,
rowSplitRange=None, spaceBefore=None, spaceAfter=None, cornerRadii=None)
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name="Rectangles",
fontName="Poppins-semibold",
textColor=customColor,
fontSize=10,
leading=12,
backColor="white",
borderRadius=5)
)
styleN = styles["Rectangles"]
story.append(table)
paragraph = Paragraph(text="Lorem impsum" * 5000, style=styleN)
story.append(paragraph)
for el in story:
if frame1.add(el, doc) == 0:
frame1.split(el, doc)
doc.showPage()
frame1 = Frame(0.5 * inch, inch, 7 * inch, 10.5 * inch, showBoundary=1)
doc.save()
pdf = buffer.getvalue()
file_data = ContentFile(pdf)
buffer.close()
return file_data
The only way I can think of, is using the frame.split() method, but it didn't work. Also, it looks like the SimpleDocTemplate could help me, but I will need to change the whole code to make it work.
This is what i get with this code: outcome

Reportlab - How to add margin between Tables?

So i am trying to create three tables per page, the following code will collide all three tables together with 0 margin between them. I would like some white space between two tables. Is there a configuration for that?
doc = SimpleDocTemplate("my.pdf", pagesize=A4)
elements = []
i = 0
for person in persons:
data = get_data()
t = Table(data, colWidths=col_widths, rowHeights=row_heights)
elements.append(t)
i = i + 1
if i % 3 == 0:
elements.append(PageBreak())
doc.build(elements)
You could try using the Spacer function to add space between the tables. An example of its use from the documentation is:
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
def go():
doc = SimpleDocTemplate("hello.pdf")
Story = [Spacer(1,2*inch)]
for i in range(100):
bogustext = ("This is Paragraph number %s. " % i) *20
p = Paragraph(bogustext, style)
Story.append(p)
Story.append(Spacer(1,0.2*inch))
doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages)

Generating a low res image with clear text

I'm trying to create a table (width = 190px, height = 128px) that is filled with some text and save that as an image. I managed to do that both with matplotlib and by simply generating a HTML table however with both methods I run to the same problem the text gets "compressed" and is blurry. Blurry letters
I would like to generate a pic like this 32x16 pic hand drawn in paint. What would be the best way to achieve this. At the moment I am using python to generate the HTML table and then I am saving it using the html2image library. The table needs to be generated programmatically though. Furthermore I tried to sample only some of the pixels above a threshold but then the text is even worse. The image can be saved as .pgn, .jpg or .bmp Any advice is appreciated! The code below is how I am generating and saving the table ATM.
def printTable(data):
htmlTable = ""
htmlTable += '<table>'
htmlTable += '<tr>'
htmlTable += '<td>%s</td>'% "NAME"
htmlTable += '<td>%s</td>'% "LAST NAME"
htmlTable += '<td>%s</td>'% "PASSWORD"
htmlTable += '<td>%s</td>'% "USERNAME"
htmlTable+= '</tr>'
for element in data.itertuples():
htmlTable += '<tr>'
htmlTable += ('<td>%s</td>'% element.NAME)
htmlTable += ('<td>%s</td>'% element.LAST NAME)
htmlTable += ('<td>%s</td>'% element.PASSWORD)
htmlTable += ('<td>%s</td>'% element.USERNAME)
htmlTable += '</tr>'
htmlTable += '</table>'
return htmlTable
tableString = printTable(df1)
print(tableString)
from html2image import Html2Image
hti = Html2Image(size=(190, 128))
#hti = Html2Image()
html = tableString
css = "\
#font-face {\
font-family: myFirstFont;\
src: url(Dogica Pixel Regular.ttf);\
}\
table, th, td{ \
border:1px solid red; \
border-collapse: collapse; \
background-color: black; \
color:red \
}\
table{ \
widht: 190px;\
font-size: 8px;\
font-family:myFirstFont\
}\
"
hti.screenshot(html_str=html, css_str=css, save_as='htmlTable.png')
Original image
Scaled image
P.S. I really hope hand drawing every pixel on a canvas isn't the only solution xd.

Displaying an excel table into PyQt gui window

I need bit of assistance with my python code. I am a newbie to python so not very good at it.
I have an excel spreadsheet with a bunch of lecture times and I am using the code below;
df = pd.read_excel('/home/pi/timetable1.xlsx')
df['Date'] = pd.to_datetime(df['Date']).dt.strftime("%d-%m-%Y")
now = pd.to_datetime('today').strftime("%d-%m-%Y")
print(df[df['Date'] == now])
which displays a simple line of timetable text such as below [xx:xx is time in 24 hr format];
Lesson 1: Lesson 2: Lesson 3: Lession 4: Lesson 5:
xx:xx xx:xx xx:xx xx:xx xx:xx
I have configured it so that the above displayed times only shows the times for the "Current Date".
What I am trying to acheive is that I want to use PyQt4 to display this information on a graphical window.
So for example, the below displays "HELLO WORLD" text in the gui window;
def wxfinished():
attribution3.setText("HELLO WORLD")
attribution3 = QtGui.QLabel(foreGround)
attribution3.setObjectName("attribution3")
attribution3.setStyleSheet("#attribution3 { " +
"background-color: transparent; color: " +
Config.textcolor +
"; font-size: " +
str(int(50 * xscale)) + #50 is the size of the text
"px; " +
Config.fontattr +
"}")
attribution3.setAlignment(Qt.AlignTop)
attribution3.setGeometry(450 * xscale, 670 * yscale, 1000 * xscale, 1000)
w.show()
w.showFullScreen()
sys.exit(app.exec_())
How can I change it so that instead of "HELLO WORLD" I can print out the timetable output?
While I'm not that familiar with Pandas, here's an MRE to help you. In this, a table is made, the labels are set to the excel's labels (lesson x), and the singular row is filled out with the dates.
from PyQt4.QtGui import QApplication, QTableWidget, QLabel, QFont
from PyQt4.QtCore import Qt
import sys
import pandas
CELL_FONT = QFont()
CELL_FONT.setPointSize(8)
def getLessions(): # assuming the code you provided works
"""
Returns Pandas DataFrame object w/ today's date info
:return:
"""
data = pandas.read_excel("path")
date = pandas.to_datatime(data["Date"]).dtftime("%d-%m-%Y")
now = pandas.to_datetime('today').strftime("%d-%m-%Y")
return data[date == now]
def makeWidget():
datum = getLessions()
columns = len(datum.columns)
# Use table to display information
table = QTableWidget()
table.setRowCount(1)
table.setColumnCount(columns)
table.setHorizontalHeaderLabels(datum.columns) # I think datum.columns returns [str], I could be wrong
table.setVerticalHeaderLabels([""])
# Row will be filled with Labels w/ times
for i in range(columns):
lession_time = str(datum.iloc[0,i]) # edit: iloc returns a Timestamp object
label = QLabel()
label.setFont(CELL_FONT)
label.setText(lession_time)
label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
table.setCellWidget(0, i, label)
return table
def makeWidgetDummy(): # Doesn't use Pandas
columns = 5
table = QTableWidget()
table.setRowCount(1)
table.setColumnCount(columns)
horizontal_lbls = ["Lesson {}".format(i + 1) for i in range(columns)]
table.setHorizontalHeaderLabels(horizontal_lbls)
table.setVerticalHeaderLabels([""])
for i in range(columns):
lession_time = "XX:XX"
label = QLabel()
label.setFont(CELL_FONT)
label.setText(lession_time)
label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
table.setCellWidget(0, i, label)
def main():
app = QApplication(sys.argv)
widget = makeWidget()
# widget = makeWidgetDummy()
widget.show()
app.exec_()
if __name__ == "__main__":
main()

Define a function to generate html pages in python

trying to create several web pages that contains tables from the read csv file and I tried to define a function to create html web pages rather than writing the same codes many times.
read my csv file:
infile = open("new.csv", "r")
data = []
for line in infile:
cols = line.split(",")
Oposition = cols[0]
Winner = cols[1]
Margin = cols[2]
Ground = cols[3]
Year = cols[4]
pair = (Oposition, Winner, Margin, Ground, Year)
data.append(pair)
infile.close()
so far my codes are:
page = """<!DOCTYPE html>
<html>
<head>
<title>abc</title>
<style>
h1 {
text-align: center;
}
body {
background-image: url("2014.png");
background-repeat: no-repeat;
background-position: right top;
background-attachment: fixed;
}
</style>
</head>
<body>
<h1>{{heading}}</h1>
{{paragraph}}
<p>Back to main page</p>
<table border="1"><tr><th>Oposition</th><th>Winner</th><th>Margin</th><th>Ground</th><th>Year</th></tr>
"""
out1=page.format(heading = "2012 Stats", paragraph = "<p>aaaaaaa</p>")
out2 = page.format(heading = "2013 Stats", paragraph = "<p>bbbbb</P>")
out3 = page.format(heading = "2014 Stats", paragraph = "<p>cccc</P>")
out4 = page.format(heading = "2015 Stats", paragraph = "<p>ddddCSK</p>")
def write_html_file(filename, body):
out = open(filename, "w")
out.write(body)
write_html_file("2012.html",out1)
write_html_file("2013.html",out2)
write_html_file("2014.html",out3)
write_html_file("2015.html",out4)
for r in data:
if ''.join(r[4].split()) == "2012":
Oposition = r[0]
Winner = r[1]
Margin = r[2]
Ground=r[3]
Year = r[4]
out1.write("<tr> <td>" + Oposition+ '</td><td> '+ Winner+'</td><td> '+Margin+'</td><td> '+Ground+' </td><td>'+ Year+ " </td></tr>")
elif ''.join(r[4].split()) == "2013":
Oposition = r[0]
Winner = r[1]
Margin = r[2]
Ground=r[3]
Year = r[4]
out2.write("<tr> <td>" + Oposition+ '</td><td> '+ Winner+'</td><td> '+Margin+'</td><td> '+Ground+' </td><td>'+ Year+ " </td></tr>")
elif ''.join(r[4].split()) == "2014":
Oposition = r[0]
Winner = r[1]
Margin = r[2]
Ground=r[3]
Year = r[4]
out3.write("<tr> <td>" + Oposition+ '</td><td> '+ Winner+'</td><td> '+Margin+'</td><td> '+Ground+' </td><td>'+ Year+ " </td></tr>")
elif ''.join(r[4].split()) == "2015":
Oposition = r[0]
Winner = r[1]
Margin = r[2]
Ground=r[3]
Year = r[4]
out4.write("<tr> <td>" + Oposition+ '</td><td> '+ Winner+'</td><td> '+Margin+'</td><td> '+Ground+' </td><td>'+ Year+ " </td></tr>")
def output(a):
a.write("</table> </body>\n")
a.write("</html>\n")
a.close()
output(out1)
output(out2)
output(out3)
output(out4)
im trying to make tables according to years 2012, 2013, 2014, 2015 and make html pages that contains each of them. just cant figure out.
Any help or other option? Much appreciated
I get an error message saying:
---> 25 page1 = page.format(heading = "2012 Stats", paragraph = "<p>aaaa</p>")
KeyError: '\n text-align'
Short Answer
Python's built-in string format syntax is a variable enclosed by single paramthesis. That's why '\n text-align' is regarded as a key whereas it was intended to be a css style.
Solution
You can go ahead to escape your css snippets but I would not recommend it, because the html text will not be readable and will be difficult to maintain.
Hence, please use a template engine to help. There are many template engines: Jinja2, Mako.. etc. Since I am more familiar with the first one, let me show you how to get page1 working::
from jinja2 import Environment
env = Environment()
page_template = env.from_string(page)
page1 = page_template(heading="2012 Stats", paragraph="<p>aaaaaaa</p>")
And you will need to install jinja2:
$ pip install jinja2
Alternative solution
You can use my library pyexcel and pyexcel-text to get a html table rendered for you. The sample code is:
import pyexcel as p
sheet = p.get_sheet(file_name='new.csv')
sheet.colnames = ['Oposition', 'Winner', 'Margin', 'Ground', 'Year']
sheet.name = "2012 Stats"
print(sheet.html)
To run above code, you need to install these two additional packages:
$ pip install pyexcel pyexcel-text

Categories