LaTeX mapping in pyQt / pySide QTableWidget

I would like to add mathematical expressions to the table labels (for example: 2 ^ 3 should be formatted correctly)

Here is a simple table example: http://thomas-cokelaer.info/blog/2012/10/pyqt4-example-of-tablewidget-usage/

setHorizontalHeaderLabels accepts only a string. I wonder if it is possible to somehow implement this matplotlib approach: matplotlib - write TeX on a Qt form

Are there any other options?

+4
source share
1 answer

I also tried for a while to display complex labels in the QTableWidget header. I was able to do this by overriding the paintSection QHeaderView method and manually drawing a label with a QTextDocument, as described in the section in Qt Center .

However, this solution was somewhat limited compared to what could be done with LaTex. I thought it might be a good idea to try the approach you suggested in your OP, i.e. Using matplotlib's ability to render LaTex in PySide.

1. Convert matplotlib drawing to QPixmap

The first thing that is required in this approach is the ability to convert a matplotlib shape into a format that can be easily drawn on any QWidget. Below is a function that takes a mathTex expression as input and converts it through matplotlib to QPixmap.

import sys import matplotlib as mpl from matplotlib.backends.backend_agg import FigureCanvasAgg from PySide import QtGui, QtCore def mathTex_to_QPixmap(mathTex, fs): #---- set up a mpl figure instance ---- fig = mpl.figure.Figure() fig.patch.set_facecolor('none') fig.set_canvas(FigureCanvasAgg(fig)) renderer = fig.canvas.get_renderer() #---- plot the mathTex expression ---- ax = fig.add_axes([0, 0, 1, 1]) ax.axis('off') ax.patch.set_facecolor('none') t = ax.text(0, 0, mathTex, ha='left', va='bottom', fontsize=fs) #---- fit figure size to text artist ---- fwidth, fheight = fig.get_size_inches() fig_bbox = fig.get_window_extent(renderer) text_bbox = t.get_window_extent(renderer) tight_fwidth = text_bbox.width * fwidth / fig_bbox.width tight_fheight = text_bbox.height * fheight / fig_bbox.height fig.set_size_inches(tight_fwidth, tight_fheight) #---- convert mpl figure to QPixmap ---- buf, size = fig.canvas.print_to_buffer() qimage = QtGui.QImage.rgbSwapped(QtGui.QImage(buf, size[0], size[1], QtGui.QImage.Format_ARGB32)) qpixmap = QtGui.QPixmap(qimage) return qpixmap 

2. Color the QPixmaps in the QTableWidget header

The next step is to draw a QPixmap in the QTableWidget header. As shown below, I did this by subclassing QTableWidget and overriding the setHorizontalHeaderLabels method, which is used to convert mathTex expressions for labels in QPixmap and pass it as a list to a subclass of QHeaderView. Then the QPixmap is colored by reimplementing the paintSection QHeaderView method, and the height of the header is adjusted to match the height of the mathTex expression in the overriding of the sizeHint methods.

 class MyQTableWidget(QtGui.QTableWidget): def __init__(self, parent=None): super(MyQTableWidget, self).__init__(parent) self.setHorizontalHeader(MyHorizHeader(self)) def setHorizontalHeaderLabels(self, headerLabels, fontsize): qpixmaps = [] indx = 0 for labels in headerLabels: qpixmaps.append(mathTex_to_QPixmap(labels, fontsize)) self.setColumnWidth(indx, qpixmaps[indx].size().width() + 16) indx += 1 self.horizontalHeader().qpixmaps = qpixmaps super(MyQTableWidget, self).setHorizontalHeaderLabels(headerLabels) class MyHorizHeader(QtGui.QHeaderView): def __init__(self, parent): super(MyHorizHeader, self).__init__(QtCore.Qt.Horizontal, parent) self.setClickable(True) self.setStretchLastSection(True) self.qpixmaps = [] def paintSection(self, painter, rect, logicalIndex): if not rect.isValid(): return #------------------------------ paint section (without the label) ---- opt = QtGui.QStyleOptionHeader() self.initStyleOption(opt) opt.rect = rect opt.section = logicalIndex opt.text = "" #---- mouse over highlight ---- mouse_pos = self.mapFromGlobal(QtGui.QCursor.pos()) if rect.contains(mouse_pos): opt.state |= QtGui.QStyle.State_MouseOver #---- paint ---- painter.save() self.style().drawControl(QtGui.QStyle.CE_Header, opt, painter, self) painter.restore() #------------------------------------------- paint mathText label ---- qpixmap = self.qpixmaps[logicalIndex] #---- centering ---- xpix = (rect.width() - qpixmap.size().width()) / 2. + rect.x() ypix = (rect.height() - qpixmap.size().height()) / 2. #---- paint ---- rect = QtCore.QRect(xpix, ypix, qpixmap.size().width(), qpixmap.size().height()) painter.drawPixmap(rect, qpixmap) def sizeHint(self): baseSize = QtGui.QHeaderView.sizeHint(self) baseHeight = baseSize.height() if len(self.qpixmaps): for pixmap in self.qpixmaps: baseHeight = max(pixmap.height() + 8, baseHeight) baseSize.setHeight(baseHeight) self.parentWidget().repaint() return baseSize 

3. Application

The following is an example of a simple application of the above.

 if __name__ == '__main__': app = QtGui.QApplication(sys.argv) w = MyQTableWidget() w.verticalHeader().hide() headerLabels = [ '$C_{soil}=(1 - n) C_m + \\theta_w C_w$', '$k_{soil}=\\frac{\\sum f_j k_j \\theta_j}{\\sum f_j \\theta_j}$', '$\\lambda_{soil}=k_{soil} / C_{soil}$'] w.setColumnCount(len(headerLabels)) w.setHorizontalHeaderLabels(headerLabels, 25) w.setRowCount(3) w.setAlternatingRowColors(True) k = 1 for j in range(3): for i in range(3): w.setItem(i, j, QtGui.QTableWidgetItem('Value %i' % (k))) k += 1 w.show() w.resize(700, 200) sys.exit(app.exec_()) 

that leads to:

enter image description here

The solution is not perfect, but it is a good starting point. I will update it when I improve it for my own application.

+9
source

All Articles