0

I have the following code to align text of QTableWidget's column.

import sys

from PyQt5           import QtWidgets, QtCore
from PyQt5.QtCore    import Qt
from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem, QStyledItemDelegate

class AlignRightDelegate(QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(AlignRightDelegate, self).initStyleOption(option, index)
        option.displayAlignment = Qt.AlignRight

class Table(QTableWidget):
    def __init__(self, data, alignColumns = 1, *args):
        QTableWidget.__init__(self, *args)
        self.setRowCount(len(data))
        self.setColumnCount(3)

        self.setData(data)
        self.resizeColumnsToContents()

        for colIndex in range(1, 1 + alignColumns):
            print("Align Column [", colIndex, "]", sep="")
            self.setItemDelegateForColumn(colIndex, AlignRightDelegate())

    def setData(self, data): 
        horizontalHeaders = []
        for m, rowContent in enumerate(data):
            for n, cellContent in enumerate(data[m]):
                if (m == 0):
                    horizontalHeaders.append(cellContent)
                else:
                    self.setItem(m - 1, n, QTableWidgetItem(cellContent))
        self.setHorizontalHeaderLabels(horizontalHeaders)

if __name__ == "__main__":
    data = [["col1", "col2", "col3"],
            [   "1",   "1", "a"],
            [  "-1",   "2", "b"], 
            [   "0",   "3", "c"]]
    print("python QTableWidgetAlignRight.py[ <AlignColumnCount=1]")
    app = QApplication(sys.argv)
    table = Table(data, int(sys.argv[1])) if (len(sys.argv) > 1) else Table(data)
    table.show()
    sys.exit(app.exec_())

If I execute the above code with python QTableWidgetAlignRight.py 1 it sort of works as it should as shown below with col2 aligned to the right but apparently to the top also:

Works

However when I execute the same code with python QTableWidgetAlignRight.py 2 where I try to align 2 columns to the right, I ran into Python has stopped working error. The following screenshot is actually on my Japanese Windows OS (Win 10 Pro 20H2 (OS Build 19402.1165))

Doesn't Work (Error Message in Japanese)

However I searched the net for the same error message in English and I found a screenshot albeit not due to my code above (burrowed from this page: Python has stopped working).

Same Error Message in English

So what is the correct way of aligning 2 columns of my QTableWidget (without an error and without aligning to the top vertically)? Also is this a bug in PyQt5?

For your information, I am using the following: Python 3.7.6, conda 4.8.2, pyqt 5.9.2 py37h6538335_2. For column alignment, I looked at this for reference: How to align all items in a column to center in QTableWidget

mak
  • 197
  • 9

1 Answers1

2

Due to the way python and PyQt keep references to objects, only one unparented and unreferenced delegate can theoretically be set for each view.

But, while it is possible to set one delegate like this, it should not be done: delegates should always have a persistent reference or valid parent since the view does not take ownership of the delegate (see the docs for all setItemDelegate* functions).

Also, considering that you are using the same delegate for multiple columns, there's no point in creating more than one.

    self.alignDelegate = AlignRightDelegate(self)
    for colIndex in range(1, 1 + alignColumns):
        print("Align Column [", colIndex, "]", sep="")
        self.setItemDelegateForColumn(colIndex, self.alignDelegate)

Note that you could either use the parent argument (self) or create an instance attribute:

self.alignDelegate = AlignRightDelegate()
# or
alignDelegate = AlignRightDelegate(self)

Using both, though, is not an issue, and specifying the parent is a good choice anyway when dealing with Qt objects.
In any case, at least one of the methods above should always be used.

You get a wrong alignment because you only set the horizontal alignment: use option.displayAlignment = Qt.AlignRight | Qt.AlignVCenter.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • 1
    Are there typos in that first paragraph? As it stands, it seems contradictory. – ekhumoro Sep 24 '21 at 15:16
  • @ekhumoro maybe I should rephrase that: what I meant is that in PyQt it is possible to use an unreferenced delegate instance, but just one: as soon as it is attempted to create another one for the same view, the program will freeze. So, while it technically works, it should not be done. – musicamante Sep 24 '21 at 15:29
  • 1
    So it should be "one" rather than "an". But still, I feel the advice should always be to keep a reference - particularly when the docs state that Qt doesn't take ownership. Saying it "can" be done in PyQt suggests that it's explicitly supported in some sense, rather than being a mere implementation detail. – ekhumoro Sep 24 '21 at 16:16
  • 1
    @ekhumoro good point, again. I've updated the answer, I believe it should be more clear now. – musicamante Sep 25 '21 at 18:58