I am using xlsxwriter and I have created a class where I have created a workbook. Then I have added 2 worksheets to it.
Now I have written a method that's writing data to one of the worksheets, but now I would like to use it on both worksheets. This is a simple example to explain the situation:
import xlsxwriter
class ExcelWorkbook():
def __init__(self, filename):
self.wb = xlsxwriter.Workbook(filename)
self.ws1 = self.wb.add_worksheet('Num')
self.ws1LineCount = 0
self.ws2 = self.wb.add_worksheet('Result')
self.ws2LineCount = 0
def write_data(self, data, title):
self.ws1.write_row(self.ws1LineCount, 0, title)
self.ws1LineCount += 1
self.ws1.write_row(self.ws1LineCount, 0, data)
self.ws1LineCount += 1
xlsxWorkbook = ExcelWorkbook('Test2.xlsx')
numArr = (0.000000520593523979187, 13.123456789, 1.789456, 0.002345, 0.00123, 1)
titleBar = ('Date', 'quantity', 'Average [m]', 'Standard Dev [m]', 'Test', 'Success')
xlsxWorkbook.write_data(numArr, titleBar)
Now I'd like to use the write_data method for both worksheets, so I thought I'd pass the worksheet as a parameter, but unfortunately it's not that simple, as I cannot pass the instance variable self.ws1 or self.ws2.
So the question is: how can I do that?
I came up with a very nasty solution, like this:
def write_data(self, data, title, instance = 'ws1'):
if instance == 'ws1':
instance = self.ws1
lineCounter = self.ws1LineCount
elif instance == 'ws2':
instance = self.ws2
lineCounter = self.ws2LineCount
instance.write_row(self.ws1LineCount, 0, title)
lineCounter += 1
instance.write_row(self.ws1LineCount, 0, data)
lineCounter += 1
but honestly I don't like it. Is there a proper way to do it, or is it like a completely unreasonable thing?
Instead of the if block, better use workbook.get_worksheet_by_name() method
def write_data(self, data, title, ws = 'Num'):
wsheet = self.wb.get_worksheet_by_name(ws)
wsheet.write_row(self.ws1LineCount, 0, title)
lineCounter += 1
wsheet.write_row(self.ws1LineCount, 0, data)
lineCounter += 1
EDIT: or you can use getattr() function, e.g.
def write_data(self, data, title, ws = 'ws1'):
wsheet = getattr(self, ws, self.ws1))
wsheet.write_row(self.ws1LineCount, 0, title)
lineCounter += 1
wsheet.write_row(self.ws1LineCount, 0, data)
lineCounter += 1
I'd do something like this:
from collections import defaultdict
import xlsxwriter
class ExcelWorkbook():
def __init__(self, filename):
self.wb = xlsxwriter.Workbook(filename)
self.ws1 = self.wb.add_worksheet('Num')
self.ws2 = self.wb.add_worksheet('Result')
self._line_counts = defaultdict(int)
def write_data(self, ws, data, title):
self._write_row(ws, title)
self._write_row(ws, data)
def _write_row(self, ws, content):
ws.write_row(self._line_counts[ws], 0, content)
self._line_counts[ws] += 1
xlsxWorkbook = ExcelWorkbook('Test2.xlsx')
numArr = (0.000000520593523979187, 13.123456789, 1.789456, 0.002345, 0.00123, 1)
titleBar = ('Date', 'quantity', 'Average [m]', 'Standard Dev [m]', 'Test', 'Success')
xlsxWorkbook.write_data(xlsxWorkbook.ws1, numArr, titleBar)
xlsxWorkbook.write_data(xlsxWorkbook.ws2, numArr, titleBar)
By using defaultdict, you don't need to explicitly keep a line count variable for each worksheet. The write_data function accepts a new parameter ws so that you can set the worksheet that should be written to. Writing a row can be factored into a separate method to reduce code duplication.
Does this answer your question?
Edit:
a default value cannot access self, but you can work around this quite easily:
def write_data(self, data, title, ws=None):
if ws is None:
ws = self.ws1
self._write_row(ws, title)
self._write_row(ws, data)
Maybe you should consider to have an extra class ExcelWorksheet, in order to put all logic related to them inside:
class ExcelWorksheet(object):
def __init__(self, workbook, name):
self.wb = workbook
self.ws = self.wb.add_worksheet(name)
self.wsLineCount = 0
def write_data(self, data, title):
self.ws.write_row(self.wsLineCount, 0, title)
self.wsLineCount += 1
self.ws.write_row(self.wsLineCount, 0, data)
self.wsLineCount += 1
This way, you can refactor your code to this:
class ExcelWorkbook(object):
def __init__(self, filename):
self.wb = xlsxwriter.Workbook(filename)
self.ws1 = ExcelWorksheet(self.wb, 'Num')
self.ws2 = ExcelWorksheet(self.wb, 'Result')
xlsxWorkbook = ExcelWorkbook('Test2.xlsx')
numArr = (0.000000520593523979187, 13.123456789, 1.789456, 0.002345, 0.00123, 1)
titleBar = ('Date', 'quantity', 'Average [m]', 'Standard Dev [m]', 'Test', 'Success')
xlsxWorkbook.ws1.write_data(numArr, titleBar)
xlsxWorkbook.ws2.write_data(numArr, titleBar)
With vars(self), you can access any element of the object as a dictionnary.
Then vars(self)["ws1"] is like self.ws1 and vars(self)["ws2"] is like self.ws2
Then you just have to pass a key argument "ws1" or "ws2" in write_data.
def write_data(self, data, title, key='ws1'):
instance = vars(self)[key]
lineCounter = vars(self)[key+"LineCount"]
instance.write_row(lineCounter , 0, title)
lineCounter += 1
instance.write_row(lineCounter , 0, data)
lineCounter += 1
Related
I have a program that is "divided" in 6 classes, the first class is the Main Window where I can choose where I'd like to go (I have 4 choices, a page to visualize all the data, one to modify and the other 2 to visualize data with filters), each of theese pages is a QDialog that contains a QTableWidget, this table is connected to a SQL Server database and shows data from a specific table (to be honest it shows data from 2 tables).
My problem is:
In the 2 class ("modificaviaggi") I connected some signals to some action, first of all I connected the itemChanged signal to a method that stores the items written by the user and saves them in the SQL DB, the signal signal I used is the clicked signal, I connected this signal to a method that verifies if the cell clicked it's the 8 or 9, in case it's 8 or 9 it shows another QDialog (class "articoli") where the user can insert some data regarding weight of every product, this weight data are stored in a second table in the SQL Server DB, I use this table to get the weight for every product when I show my table.
So I get all the data from the table SQL "Viaggi" and then I verify if there are some values in the SQL table "Pesi" for each row of the table "Viaggi" using the row ID.
Now the complicated part is that when the user click the columns 8 or 9 it shows another Qdialog, I show this Qdialog everytime the user clicks the columns 8 or 9, then the user can write data on the table and those data will be stored in the table "Pesi", thanks to the variable "lastID" declared on the "modificaviggi" class I know for which row the user clicked the columns 8 or 9, thanks to that I store the values in the SQL table and for each value I have their row ID, now that the situation is more clear the real question is: How can "Re-load" the QTableWidget "tabella_registrazioni" inside the class "modificaviaggi" everytime the user clicks the QPushButton "get_back_button"? Because in the class "articoli" the user modifies/adds values for each row in the QTableWidget "tabella_registrazioni" but I can't reload the data inside here's my code :
c = conn.cursor()
d = conndue.cursor()
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__() # chiamare le inherited classi con metodo __init__
uic.loadUi('pagina_scelta.ui', self) # caricare file.ui
self.show() # mostrarlo
bottonefinestramodifica = self.findChild(QtWidgets.QPushButton, 'bottone_modifica')
bottonefinestramodifica.clicked.connect(self.finestramodifica)
def finestramodifica(self):
self.finestramodifica = modificaviaggi()
self.finestramodifica.show()
class MyDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
return
class modificaviaggi(QtWidgets.QDialog):
lastid = [0]
recall = [2]
def finestraarticoli(self):
self.finestraarticoli = articoli()
self.finestraarticoli.show()
def chiusura(self):
self.accept()
def __init__(self):
super(modificaviaggi, self).__init__()
uic.loadUi('modifica_reg.ui', self)
bottonetorna = self.bottone_torna
bottonetorna.clicked.connect(self.chiusura)
self.reloading()
self.datitabella()
def reloading(self):
if 1 == 1:
if self.recall[0] == 1:
self.datitabella()
self.recall[0] == 2
def datitabella(self):
delegate = MyDelegate(self)
tablerow = 0
conteggio = c.execute('SELECT COUNT(ID) FROM Viaggi').fetchone()[0]
print(conteggio)
customers = [" "]
self.tabella_registrazioni.blockSignals(True)
self.customers_combo(customers)
self.tabella_registrazioni.setRowCount(conteggio)
for row in c.execute("SELECT * FROM Viaggi "):
rigavalore = 0
self.tabella_registrazioni.setRowCount(conteggio)
for r in range(0, 19):
self.tabella_registrazioni.setItem(tablerow, rigavalore, QtWidgets.QTableWidgetItem(str(row[rigavalore])))
rigavalore+= 1
tablerow+= 1
self.combocreation(customers)
self.combocreation(customers)
self.tabella_registrazioni.blockSignals(False)
self.tabella_registrazioni.itemChanged.connect(self.changeIcon)
self.tabella_registrazioni.setItemDelegateForColumn(0, delegate)
self.tabella_registrazioni.clicked.connect(self.pressed)
self.combo_customers.activated.connect(self.customer_combo_selection)
righe = self.tabella_registrazioni.rowCount()
tablerow1= 0
for i in range(0, righe):
idrow = self.tabella_registrazioni.item(i, 0).text()
netweight = c.execute("SELECT SUM(Peso_netto) FROM Pesi WHERE ID_registrazione = ?", idrow)
if netweight != None or not netweight.isspace():
netweight = float(str(netweight.fetchone()[0]))
else:
print("Nessun peso")
grossweight = c.execute("SELECT SUM(Peso_lordo) FROM Pesi WHERE ID_registrazione = ?", idrow)
if grossweight != None or not grossweight.isspace():
grossweight = float(str(grossweight.fetchone()[0]))
self.tabella_registrazioni.setItem(tablerow1, 8, QtWidgets.QTableWidgetItem(str(netweight)))
self.tabella_registrazioni.setItem(tablerow1, 9, QtWidgets.QTableWidgetItem(str(grossweight)))
tablerow1+= 1
def combocreation(self, customers):
self.combo_customers = QComboBox()
self.combo_customers.setEditable(True)
def customers_combo(self, customers):
for cu in d.execute("SELECT ANDESCRI FROM VALL_CONTI WHERE ANTIPCON = 'C' "):
customers.append(cu[0].strip())
def changeIcon(self, item):
row = item.row()
col = item.column()
zero = self.tabella_registrazioni.item(row, 0).text()
uno = self.tabella_registrazioni.item(row, 1).text()
custcode = self.tabella_registrazioni.item(row, 3).text()
custname = self.tabella_registrazioni.item(row, 4).text()
c.execute('UPDATE Viaggi SET Codice_cliente = ?, Ragione_sociale = ? WHERE ID = ?',
(custcode, custname, zero))
c.commit()
def pressed(self, item):
row = item.row()
col = item.column()
customers = [" "]
self.lastid[0] = self.tabella_registrazioni.item(row, 0).text()
if col == 8 or col == 9:
self.finestraarticoli()
self.customers_combo(customers)
self.combocreation(customers)
textcell = self.tabella_registrazioni.item(row, col).text()
self.combo_customers.setCurrentText(textcell)
self.combo_customers.activated.connect(self.customer_combo_selection)
if col == 3:
self.tabella_registrazioni.setCellWidget(row, 3, self.combo_customers)
def customer_combo_selection(self):
customer_name = self.combo_customers.currentText()
row = self.rrow
self.tabella_registrazioni.setItem(self.rrow, self.ccolumn, QtWidgets.QTableWidgetItem(str(customer_name)))
id = self.tabella_registrazioni.item(row, 0).text()
c.execute("UPDATE Viaggi SET Ragione_Sociale=? WHERE ID = ?", (customer_name, id))
c.commit()
class articoli(QtWidgets.QDialog):
def changevariable(self):
modificaviaggi.recall[0] = 1
self.accept()
def __init__(self):
super(articoli, self).__init__()
uic.loadUi('Inserimento_articoli.ui', self)
self.get_back_button.clicked.connect(self.changevariable)
self.tabellapesi()
def tabellapesi(self):
self.IDpressed = modificaviaggi.lastid
tablerow = 0
self.tabella_articoli.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
self.conteggio = int(str(c.execute("SELECT COUNT(ID_registrazione) FROM Pesi WHERE ID_registrazione = ? ", self.IDpressed[0]).fetchone()[0]))
for row in c.execute("SELECT * FROM Pesi WHERE ID_registrazione = ? ORDER BY ID ", self.IDpressed[0]):
rigavalore = 0
self.tabella_articoli.setRowCount(self.conteggio)
for r in range(0, 6):
self.tabella_articoli.setItem(tablerow, rigavalore, QtWidgets.QTableWidgetItem(str(row[rigavalore])))
rigavalore+= 1
tablerow+= 1
def insert(self):
rows = self.tabella_articoli.rowCount()
tablerow = 0
for row in range (0, rows):
articlecode = self.tabella_articoli.item(tablerow, 1).text()
articledesc = self.tabella_articoli.item(tablerow, 2).text()
netweight = float(self.tabella_articoli.item(tablerow, 3).text())
grossweight = float(self.tabella_articoli.item(tablerow, 4).text())
price = float(self.tabella_articoli.item(tablerow, 5).text())
if self.tabella_articoli.item(tablerow, 0) == None:
IDweight = ""
else:
IDweight = int(self.tabella_articoli.item(tablerow, 0).text())
queryinsert = "INSERT INTO Pesi ( Codice_articolo, Descrizione_articolo, Peso_netto, Peso_lordo, ID_registrazione, Prezzo) VALUES (?,?,?,?,?,?)"
values = [articlecode, articledesc, netweight, grossweight, self.IDpressed[0], price]
queryupdate = "UPDATE Pesi SET Peso_lordo = ?, Prezzo = ?, Peso_netto = ? , Codice_articolo = ?, Descrizione_articolo = ? WHERE ID = ? "
valuesupdate = [grossweight, price, netweight, articlecode, articledesc, IDweight]
exist = str(c.execute("IF EXISTS (SELECT * FROM CARICO_VIAGGI..Pesi WHERE ID = ?) BEGIN SELECT 1 END ELSE BEGIN SELECT 2 END", (IDweight)).fetchone()[0])
if exist == "1":
c.execute(queryupdate, valuesupdate)
c.commit()
print(type(IDweight))
elif exist == "2":
c.execute(queryinsert, values)
c.commit()
else:
print("Impossibile salvare gli articoli, riprovare")
tablerow+= 1
self.tabella_articoli.clearContents()
tablerow = 0
self.conteggiox = int(str(c.execute("SELECT COUNT(ID_registrazione) FROM Pesi WHERE ID_registrazione = ? ", self.IDpressed[0]).fetchone()[0]))
for row in c.execute("SELECT * FROM Pesi WHERE ID_registrazione = ? ORDER BY ID", self.IDpressed[0]):
rigavalore = 0
self.tabella_articoli.setRowCount(self.conteggiox)
for r in range(0, 6):
self.tabella_articoli.setItem(tablerow, rigavalore, QtWidgets.QTableWidgetItem(str(row[rigavalore])))
rigavalore+= 1
tablerow+= 1
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
One solution could be to set up signals that would update the table as the user makes changes so there would be no need to refresh at all.
For example:
Set up a signal in your dialog class that will be emitted whenever the user changes the data. Then in the original dialog with the table you want to update, connect to that signal after initializing the dialog.
here is a minimal example:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def populate_table(table):
for i in range(10):
table.insertRow(table.rowCount())
for j in range(2):
item = QTableWidgetItem(type=0)
item.setText(f"({i}, {j})")
table.setItem(i,j,item)
class Dialog2(QDialog):
tableInfoChanged = pyqtSignal([int, int, str])
def __init__(self,parent=None):
super().__init__(parent=parent)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.table = QTableWidget()
self.table.setColumnCount(2)
self.layout.addWidget(self.table)
populate_table(self.table)
self.table.cellChanged.connect(self.emitChangedInfo)
def emitChangedInfo(self, row, col):
text = self.table.item(row, col).text()
self.tableInfoChanged.emit(row, col, text)
class Dialog1(QDialog):
def __init__(self,parent=None):
super().__init__(parent=parent)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.table = QTableWidget()
self.table.setColumnCount(2)
self.button = QPushButton("Push to open dialog2")
self.layout.addWidget(self.table)
self.layout.addWidget(self.button)
populate_table(self.table)
self.button.clicked.connect(self.openDialog2)
def updateTable(self, row, col, text):
self.table.item(row,col).setText(text)
def openDialog2(self):
self.dialog2 = Dialog2()
self.dialog2.tableInfoChanged.connect(self.updateTable)
self.dialog2.exec()
app = QApplication(sys.argv)
window = Dialog1()
window.show()
sys.exit(app.exec_())
I have tested this code and should function as is.
How can I print my data from my database (sqlite) into a notepad /word document from my table in my GUI (using the same formatting). Here is my code for the table which is represented on my gui.
class Table(QtGui.QDialog):
def __init__(self):
super(Table, self).__init__()
with sqlite3.connect('database.db') as db:
cursor=db.cursor()
cursor.execute('select* from Receipt Order BY ReceiptID ASC')
title = [cn[0] for cn in cursor.description]
rows = [cn[0] for cn in cursor.description]
cur=cursor.fetchall()
layout = QtGui.QGridLayout()
self.table = QtGui.QTableWidget()
self.setGeometry(500,500,500,400)
qr = self.frameGeometry()
cp = QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
self.label2=QtGui.QLabel(self)
self.label2.setPixmap(QtGui.QPixmap('receipt_pic.jpg'))
self.label2.setGeometry(0,0,500,400)
self.table.setColumnCount(2)
self.table.setHorizontalHeaderLabels(title)
for i,row in enumerate(cur):
self.table.insertRow(self.table.rowCount())
for j,val in enumerate(row):
self.table.setItem(i, j, QtGui.QTableWidgetItem(str(val)))
layout.addWidget(self.table, 0, 0)
self.setLayout(layout)
self.setWindowTitle('Receipt Table')
I want to be able to click a button which copies this information (which would appear as a table with filled columns and rows) into a seperate notepad file / or any text document (where I can send the table to the printer to be printed).
It would probably be easier to just print the table directly, rather than use an intermediary file.
To do this, you can use a QTextDocument to create a printable representation of the table, and then use the built-in print dialogs to do the rest.
So, first add some buttons:
...
layout.addWidget(self.table, 0, 0, 1, 2)
self.buttonPrint = QtGui.QPushButton('Print', self)
self.buttonPrint.clicked.connect(self.handlePrint)
self.buttonPreview = QtGui.QPushButton('Preview', self)
self.buttonPreview.clicked.connect(self.handlePreview)
layout.addWidget(self.buttonPrint, 1, 0)
layout.addWidget(self.buttonPreview, 1, 1)
self.setLayout(layout)
...
then some handlers for the print and print-preview dialogs:
def handlePrint(self):
dialog = QtGui.QPrintDialog()
if dialog.exec_() == QtGui.QDialog.Accepted:
self.handlePaintRequest(dialog.printer())
def handlePreview(self):
dialog = QtGui.QPrintPreviewDialog()
dialog.paintRequested.connect(self.handlePaintRequest)
dialog.exec_()
and finally some methods for creating the document and printing it:
def handlePaintRequest(self, printer):
document = self.makeTableDocument()
document.print_(printer)
def makeTableDocument(self):
document = QtGui.QTextDocument()
cursor = QtGui.QTextCursor(document)
rows = self.table.rowCount()
columns = self.table.columnCount()
table = cursor.insertTable(rows + 1, columns)
format = table.format()
format.setHeaderRowCount(1)
table.setFormat(format)
format = cursor.blockCharFormat()
format.setFontWeight(QtGui.QFont.Bold)
for column in range(columns):
cursor.setCharFormat(format)
cursor.insertText(
self.table.horizontalHeaderItem(column).text())
cursor.movePosition(QtGui.QTextCursor.NextCell)
for row in range(rows):
for column in range(columns):
cursor.insertText(
self.table.item(row, column).text())
cursor.movePosition(QtGui.QTextCursor.NextCell)
return document
If you prefer to save to a file, you can use QTextDocument.toHtml to dump the table as html.
The printed results are quite basic, but if you want something more fancy, you can always build the document using html/css.
I use this function
def LoadDatabase(self):
self.banco = sqlite3.connect ( 'Vendas.db' )
self.cursor = banco.cursor ( )
query = "SELECT * FROM Produtos"
result = self.banco.execute ( query )
self.listaprodutos.setRowCount ( 0 )
for row_number, row_data in enumerate ( result ):
self.listaprodutos.insertRow ( row_number )
for colum_number, data in enumerate ( row_data ):
self.listaprodutos.setItem ( row_number, colum_number, QtWidgets.QTableWidgetItem ( str ( data ) ) )
I'm using Python 2.7.6 and Django 1.5.5.
How I can write a line in SimpleDocTemplate?
I'm tryng this:
#login_required
def report(request):
rep = Report(request.user.username + "_cities.pdf")
# Title
rep.add_header("Cities")
line = Line(0, 100, 500, 100)
rep.add(line)
# Body
columns = ("City")
cities = [(p.name) for p in City.objects.all()]
table = Table([columns] + cities, style=GRID_STYLE)
table.hAlign = "LEFT"
table.setStyle([('BACKGROUND', (1, 1), (-2, -2), colors.lightgrey)])
rep.add(table)
rep.build()
return rep.response
The Line() is from reportlab.graphics.shapes import Line.
The class Report is only a wrapper class to SimpleDocTemplate:
class Report:
styles = None
response = None
document = None
elements = []
def __init__(self, report_file_name):
self.styles = styles.getSampleStyleSheet()
self.styles.add(ParagraphStyle(name='Title2',
fontName="Helvetica",
fontSize=12,
leading=14,
spaceBefore=12,
spaceAfter=6,
alignment=TA_CENTER),
alias='title2')
self.response = HttpResponse(mimetype="application/pdf")
self.response["Content-Disposition"] = "attachment; filename=" + report_file_name
self.document = SimpleDocTemplate(self.response, topMargin=5, leftMargin=2, rightMargin=1, bottomMargin=1)
self.document.pagesize = portrait(A4)
return
def add_header(self, header_text):
p = Paragraph(header_text, self.styles['Title2'])
self.elements.append(p)
def add(self, paragraph):
self.elements.append(paragraph)
def build(self):
self.document.build(self.elements)
When I call report function, I receive the error message:
Line instance has no attribute 'getKeepWithNext'
hen I remove/comment the lines with Line(), the error don't ocurre.
You can help me?
How to write that line?
Just adding Line to the list of elements doesn't work: you can only pass Flowables to SimpleDocTemplate.build(). But you can wrap it in a Drawing, which is a Flowable:
d = Drawing(100, 1)
d.add(Line(0, 0, 100, 0))
rep.add(d)
Pandas package offers DataFrame.to_html() method. This method takes data in a tabular format and displays it as html table.
I would like to modify the behavior of this method. However, I don't want to change the source code of the package, I would like to extend it.
To clarify this, I would like to keep method DataFrame.to_html() intact and create a new method DataFrame.to_html2().
The problem here is that, the behavior of this method is defined in another method of a class which is an indirect superclass of of DataFrame.
This is the structure:
class DataFrame
method to_html # This method is called in my script
instance of a class DataFrameFormatter # This instance is initiated inside method to_html
PLEASE IGNORE THE INDENTATION ISSUE WITH CLASS AND DEF:
class DataFrame(NDFrame):
#Appender(fmt.docstring_to_string, indents=1)
def to_html(self, buf=None, columns=None, col_space=None, colSpace=None,
header=True, index=True, na_rep='NaN', formatters=None,
float_format=None, sparsify=None, index_names=True,
justify=None, force_unicode=None, bold_rows=True,
classes=None, escape=True):
"""
to_html-specific options
bold_rows : boolean, default True
Make the row labels bold in the output
classes : str or list or tuple, default None
CSS class(es) to apply to the resulting html table
escape : boolean, default True
Convert the characters <, >, and & to HTML-safe sequences.
Render a DataFrame as an HTML table.
"""
import warnings
if force_unicode is not None: # pragma: no cover
warnings.warn("force_unicode is deprecated, it will have no "
"effect", FutureWarning)
if colSpace is not None: # pragma: no cover
warnings.warn("colSpace is deprecated, use col_space",
FutureWarning)
col_space = colSpace
formatter = fmt.DataFrameFormatter(self, buf=buf, columns=columns,
col_space=col_space, na_rep=na_rep,
formatters=formatters,
float_format=float_format,
sparsify=sparsify,
justify=justify,
index_names=index_names,
header=header, index=index,
bold_rows=bold_rows,
escape=escape)
formatter.to_html(classes=classes)
if buf is None:
return formatter.buf.getvalue()
class DataFrameFormatter
method to_html of class DataFrameFormatter
instance of a class HTMLFormatter # This instance is initiated inside method to_html
class DataFrameFormatter(TableFormatter):
def to_html(self, classes=None):
"""
Render a DataFrame to a html table.
"""
html_renderer = HTMLFormatter(self, classes=classes)
if hasattr(self.buf, 'write'):
html_renderer.write_result(self.buf)
elif isinstance(self.buf, basestring):
with open(self.buf, 'w') as f:
html_renderer.write_result(f)
else:
raise TypeError('buf is not a file name and it has no write '
' method')
def _get_formatted_column_labels(self):
from pandas.core.index import _sparsify
def is_numeric_dtype(dtype):
return issubclass(dtype.type, np.number)
if isinstance(self.columns, MultiIndex):
fmt_columns = self.columns.format(sparsify=False, adjoin=False)
fmt_columns = zip(*fmt_columns)
dtypes = self.frame.dtypes.values
need_leadsp = dict(zip(fmt_columns, map(is_numeric_dtype, dtypes)))
str_columns = zip(*[[' ' + y
if y not in self.formatters and need_leadsp[x]
else y for y in x]
for x in fmt_columns])
if self.sparsify:
str_columns = _sparsify(str_columns)
str_columns = [list(x) for x in zip(*str_columns)]
else:
fmt_columns = self.columns.format()
dtypes = self.frame.dtypes
need_leadsp = dict(zip(fmt_columns, map(is_numeric_dtype, dtypes)))
str_columns = [[' ' + x
if not self._get_formatter(i) and need_leadsp[x]
else x]
for i, (col, x) in
enumerate(zip(self.columns, fmt_columns))]
if self.show_index_names and self.has_index_names:
for x in str_columns:
x.append('')
return str_columns
class HTMLFormatter
method write_td # This is the method which i have to modify
class HTMLFormatter(TableFormatter):
indent_delta = 2
def __init__(self, formatter, classes=None):
self.fmt = formatter
self.classes = classes
self.frame = self.fmt.frame
self.columns = formatter.columns
self.elements = []
self.bold_rows = self.fmt.kwds.get('bold_rows', False)
self.escape = self.fmt.kwds.get('escape', True)
def write(self, s, indent=0):
rs = com.pprint_thing(s)
self.elements.append(' ' * indent + rs)
def write_th(self, s, indent=0, tags=None):
if (self.fmt.col_space is not None
and self.fmt.col_space > 0):
tags = (tags or "")
tags += 'style="min-width: %s;"' % self.fmt.col_space
return self._write_cell(s, kind='th', indent=indent, tags=tags)
def write_td(self, s, indent=0, tags=None):
return self._write_cell(s, kind='td', indent=indent, tags=tags)
The question is about - how to handle this problem?
I was thinking of creating second version of each class involved, and modify this part of the code which is responsible for initiating instances.
Is this the most efficient way of handling this?
You can change the pandas.core.format.HTMLFormatter class to your own class when call to_html2:
import pandas as pd
import functools
class HTMLFormatter2(pd.core.format.HTMLFormatter):
def write_th(self, s, indent=0, tags=None):
super(HTMLFormatter2, self).write_th(s.upper(), indent, tags)
def replace_html_formatter(func, formatter):
#functools.wraps(func)
def wrapped_f(*args, **kw):
try:
old, pd.core.format.HTMLFormatter = pd.core.format.HTMLFormatter, formatter
return func(*args, **kw)
finally:
pd.core.format.HTMLFormatter = old
return wrapped_f
pd.DataFrame.to_html2 = replace_html_formatter(pd.DataFrame.to_html, HTMLFormatter2)
df = pd.DataFrame([[1,2,3],[4,5,6]], columns=["a", "b", "c"])
print df.to_html2()
Ok, let's get straight to the point. I got 2 questions
1. Why self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet) not working?
2. Why the custom lexer is not applied?
This is the source code
import wx, re, keyword
from wx import stc
class BaseLexer(object):
"""Defines simple interface for custom lexer objects"""
def __init__(self):
super(BaseLexer, self).__init__()
def StyleText(self, event):
raise NotImplementedError
class STC_LEX_VOTONUSA(BaseLexer):
# Define some style IDs
STC_VOTONUSA_DEFAULT = wx.NewId()
STC_VOTONUSA_VALUE = wx.NewId()
STC_VOTONUSA_OBJECT = wx.NewId()
STC_VOTONUSA_TYPE = wx.NewId()
STC_VOTONUSA_OPERATION = wx.NewId()
STC_VOTONUSA_COMMENT =wx.NewId()
STC_VOTONUSA_QUALIFIER = wx.NewId()
def __init__(self):
super(STC_LEX_VOTONUSA, self).__init__()
# Attributes
self.Comment = re.compile("""
//
""", re.X)
self.Types = re.compile("""
\\b
(void|integer|shortint|(short)?word|float|boolean|char|record|program|module)
\\b
""", re.I|re.X)
self.Operation = re.compile("""
(read|write)(line)?|if|main|case|while|use|return|exit|in_case|repeat_until
(\+|-|\*|/|%|\*\*|:|!|<|>)?=?|
(\{|\}|\(|\)|\[|\])
""", re.I|re.X)
self.Value = re.compile("""
[(`\d*)+\'\w*\']*|\'.*\'|[\+-]*\d*\.?\d*
""", re.I|re.X)
self.Qualifier = re.compile("""
interface|implementation
""", re.I|re.X)
self.Object = re.compile("""
s
""", re.I|re.X)
def GetLastWord(self, Line, CaretPos):
"""
Get the last word from a line
"""
LastWord = re.search(
"""
\s*
(
".*"| # String data type
\w*| # Any letter/number
(\+|-|\*|/|%|\*\*|:|!|<|>)?=?| # Assignment, Comparison & Mathematical
(\{|\}|\(|\)|\[|\]) # Brackets
)
\s*\Z
""",
Line[:CaretPos], re.VERBOSE)
return LastWord
def StyleText(self, event):
"""Handle the EVT_STC_STYLENEEDED event"""
Obj = event.GetEventObject()
# Get Last Correctly Styled
LastStyledPos = Obj.GetEndStyled()
# Get Styling Range
Line = Obj.LineFromPosition(LastStyledPos)
StartPos = Obj.PositionFromLine(Line)
EndPos = event.GetPosition()
# Walk the Range and Style Them
while StartPos < EndPos:
Obj.StartStyling(StartPos, 0x1f)
LastWord = self.GetLastWord(Line, CaretPos)
if self.Comment.search(LastWord):
# Set Comment Keyword style
Style = self.STC_VOTONUSA_COMMENT
elif self.Type.search(LastWord):
# Set Type Keyword style
Style = self.STC_VOTONUSA_TYPE
elif self.Operation.search(LastWord):
# Set Operation Keyword style
Style = self.STC_VOTONUSA_OPERATION
elif self.Value.search(LastWord):
# Set Value Keyword style
Style = self.STC_VOTONUSA_VALUE
elif self.Qualifier.search(LastWord):
# Set Qualifier Keyqord style
Style = self.STC_VOTONUSA_QUALIFIER
elif self.Object.search(LastWord):
# Set Object Keyword style
Style = self.STC_VOTONUSA_OBJECT
# Set the styling byte information for length of LastWord from
# current styling position (StartPos) with the given style.
Obj.SetStyling(len(LastWord), Style)
StartPos += len(LastWord)
class CustomSTC(stc.StyledTextCtrl):
def __init__(self, parent):
super(CustomSTC, self).__init__(parent)
# Attributes
self.custlex = None
Font = wx.Font(12, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
Face = Font.GetFaceName()
Size = Font.GetPointSize()
# Setup
kwlist = u" ".join(keyword.kwlist)
self.SetKeyWords(0, kwlist)
self.StyleClearAll()
self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
self.EnableLineNumbers()
FontSet = "face:%s, size:%d" % (Face, Size)
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, FontSet)
# Set Default to Black
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet)
# Set Comment to Pink
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
# Set Value to Green
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_VALUE, "fore:#00ff00" + FontSet)
# Set Object to Brown
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OBJECT, "fore:#a52a2a" + FontSet)
# Set Type to Red
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_TYPE, "fore:#ff0000" + FontSet)
# Set Operation to Blue
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OPERATION, "fore:#0000ff" + FontSet)
# Set Qualifier to Orange
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_QUALIFIER, "fore:#cc3232" + FontSet)
# Event Handlers
self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
def EnableLineNumbers(self, enable=True):
"""Enable/Disable line number margin"""
if enable:
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginMask(1, 0)
self.SetMarginWidth(1, 25)
else:
self.SetMarginWidth(1, 0)
def OnStyle(self, event):
# Delegate to custom lexer object if one exists
if self.custlex:
self.custlex.StyleText(event)
else:
event.Skip()
def SetLexer(self, lexerid, lexer=None):
"""
Overrides StyledTextCtrl.SetLexer
Adds optional param to pass in custom container
lexer object.
"""
self.custlex = lexer
super(CustomSTC, self).SetLexer(lexerid)
class NUSAIPT(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, None, wx.ID_ANY, 'VOTO NUSA IPT')
self.TextArea = CustomSTC(self)
self.Show()
app = wx.App()
frame = NUSAIPT()
app.MainLoop()
If I change
self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
into
self.SetLexer(stc.STC_LEX_CPP)
and
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
into
self.StyleSetSpec(stc.STC_C_COMMENT, "fore:#ff007f" + FontSet)
the comment highlighting worked. so the mistake should be in self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA) or STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT
Thanks in advance. Hope to see some answer soon.
Just stuck with same problem yesterday, here are my results. Hope it's not too late :)
First, i set lexer to this one, and internal lexer as you do:
self.SetLexer(stc.STC_LEX_CONTAINER)
self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
Bind to event and OnStyle method are same.
And also i changed id of styles from wx.newId() to just numbers starting from 1.
Without it i didn't see any styling at all. Also stc.STC_STYLE_DEFAULT started to work too.
Full listing:
class SCT_LEX_ERLANG_IDNOISE(BaseLexer):
STC_ERLANG_IDNOISE_DEFAULT = 1
STC_ERLANG_IDNOISE_VARIABLE = 2
STC_ERLANG_IDNOISE_ATOM = 3
STC_ERLANG_IDNOISE_MODULE = 4
STC_ERLANG_IDNOISE_KEYWORD = 5
STC_ERLANG_IDNOISE_COMMENT = 6
STC_ERLANG_IDNOISE_MACROS = 7
STC_ERLANG_IDNOISE_NUMBER = 8
def __init__(self, control):
super(SCT_LEX_ERLANG_IDNOISE, self).__init__(control)
self.typeFormatDict = {}
self.typeFormatDict["other"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
self.typeFormatDict["variable"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE
self.typeFormatDict["atom"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM
self.typeFormatDict["module"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE
self.typeFormatDict["keyword"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD
self.typeFormatDict["comment"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT
self.typeFormatDict["macros"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS
self.typeFormatDict["number"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER
def StyleText(self, event):
start = self.control.GetEndStyled()
end = event.GetPosition()
line = self.control.LineFromPosition(start)
start = self.control.PositionFromLine(line)
text = self.control.GetTextRange(start, end)
self.control.StartStyling(start, 0x1f)
lastEnd = 0
for type, start, end, value in getHighlightRules(text):
style = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
if start > lastEnd:
self.control.SetStyling(start - lastEnd, style)
if type in self.typeFormatDict:
style = self.typeFormatDict[type]
self.control.SetStyling(len(value), style)
lastEnd = end
class CustomSTC(stc.StyledTextCtrl):
def __init__(self, parent):
super(CustomSTC, self).__init__(parent)
self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
#self.SetKeyWords(0, kwlist)
self.SetLexer(stc.STC_LEX_CONTAINER)
self.EnableLineNumbers()
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, ColorSchema.formats["other"])
self.StyleClearAll()
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, ColorSchema.lineFont)
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT, ColorSchema.formats["other"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE, ColorSchema.formats["variable"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM, ColorSchema.formats["atom"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE, ColorSchema.formats["module"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD, ColorSchema.formats["keyword"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT, ColorSchema.formats["comment"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS, ColorSchema.formats["macros"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER, ColorSchema.formats["number"])
# Event Handlers
self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
def EnableLineNumbers(self, enable=True):
"""Enable/Disable line number margin"""
if enable:
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginMask(1, 0)
self.SetMarginWidth(1, 35)
else:
self.SetMarginWidth(1, 0)
def OnStyle(self, event):
if self.custlex:
self.custlex.StyleText(event)
else:
event.Skip()