Search code examples
pythonpyqtpyside

Correct table scaling


My code creates a scene for QGraphicsView that contains a QGraphicsProxyWidget inside of which is a QTableWidget. I have tried to scale the table and QGraphicsProxyWidget depending on the size of columns and rows, but I cannot achieve that the table always corresponds to the size of its contents and does not have scrolling, which hides rows or columns that simply do not fit.

from PySide6.QtCore import Qt
from PySide6.QtGui import QIcon, QPixmap, QBrush
from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsRectItem, QGraphicsTextItem, \
    QTableWidget, QTableWidgetItem, QGraphicsProxyWidget, QStyledItemDelegate
import sys


class ImageDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super(ImageDelegate, self).__init__(parent)

    def paint(self, painter, option, index):
        pixmap = index.data(Qt.DecorationRole)
        if pixmap:
            rect = option.rect
            scaled_pixmap = pixmap.scaled(rect.width(), rect.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
            x = rect.x() + (rect.width() - scaled_pixmap.width()) / 2
            y = rect.y() + (rect.height() - scaled_pixmap.height()) / 2
            painter.drawPixmap(x, y, scaled_pixmap)
        else:
            super().paint(painter, option, index)


class Table(QTableWidget):
    def __init__(self, rows, columns):
        super().__init__(rows, columns)
        self.setItemDelegate(ImageDelegate(self))

        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)


        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        for i in range(rows):
            for j in range(columns):
                if i == 0 and j == 0:  # Add an image to the first cell
                    item = QTableWidgetItem()
                    pixmap = QPixmap('D:\\User\\AaCePbBf.png')  # Replace with the path to your image
                    if not pixmap.isNull():  # Check if the pixmap was successfully created
                        item.setData(Qt.DecorationRole, pixmap)
                    self.setItem(i, j, item)
                else:
                    self.setItem(i, j, QTableWidgetItem(f"({j}, {i})"))

        total_width = sum([self.columnWidth(i) for i in range(self.columnCount())])
        total_height = sum([self.rowHeight(i) for i in range(self.rowCount())])

        # Set the total width and height
        self.setMinimumWidth(total_width)
        self.setMinimumHeight(total_height)


app = QApplication(sys.argv)

scene = QGraphicsScene()

table = Table(10, 10)
proxy = QGraphicsProxyWidget()
proxy.setWidget(table)
proxy.setMinimumSize(table.minimumWidth(), table.minimumHeight())
scene.addItem(proxy)

view = QGraphicsView()
view.setScene(scene)
view.setSceneRect(0, 0, 5000, 5000)
view.show()

sys.exit(app.exec())

I know it's not normal to ask 2 questions, but is my mechanism for adding an image to a table cell OK? It seems to me that even small images lose a lot of quality due to image scaling.


Solution

  • from PySide6.QtCore import Qt, QSize
    from PySide6.QtGui import QIcon, QPixmap, QBrush
    from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsRectItem, QGraphicsTextItem, \
        QTableWidget, QTableWidgetItem, QGraphicsProxyWidget, QStyledItemDelegate
    import sys
    
    
    class ImageDelegate(QStyledItemDelegate):
        def __init__(self, parent=None):
            super(ImageDelegate, self).__init__(parent)
    
        def sizeHint(self, option, index):
            icon = index.data(Qt.DecorationRole)
            if icon:
                return QSize(128, 128)  # Set the size of the icon to 128x128 pixels
            else:
                return super().sizeHint(option, index)
    
        def paint(self, painter, option, index):
            icon = index.data(Qt.DecorationRole)
            if icon:
                rect = option.rect
                icon.paint(painter, rect, Qt.AlignCenter)
            else:
                super().paint(painter, option, index)
    
    
    class Table(QTableWidget):
        def __init__(self, rows, columns):
            super().__init__(rows, columns)
            self.setItemDelegate(ImageDelegate(self))
    
            self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    
            for i in range(rows):
                for j in range(columns):
                    if i == 0 and j == 0:  # Add an image to the first cell
                        item = QTableWidgetItem()
                        pixmap = QPixmap('D:\\User\\10101_gacksung_profile.png')  # Replace with the path to your image
                        if not pixmap.isNull():  # Check if the pixmap was successfully created
                            icon = QIcon(pixmap)
                            item.setData(Qt.DecorationRole, icon)
                        self.setItem(i, j, item)
                    else:
                        self.setItem(i, j, QTableWidgetItem(f"({j}, {i})"))
    
            self.resizeColumnsToContents()
            self.resizeRowsToContents()
    
            total_width = sum([self.columnWidth(i) for i in
                               range(self.columnCount())]) + self.verticalHeader().width() + 2 * self.frameWidth()
            total_height = sum([self.rowHeight(i) for i in
                                range(self.rowCount())]) + self.horizontalHeader().height() + 2 * self.frameWidth()
    
            self.setMinimumWidth(total_width)
            self.setMinimumHeight(total_height)
    
    
    app = QApplication(sys.argv)
    
    scene = QGraphicsScene()
    
    table = Table(15, 15)
    proxy = QGraphicsProxyWidget()
    proxy.setWidget(table)
    scene.addItem(proxy)
    
    view = QGraphicsView()
    view.setScene(scene)
    view.setSceneRect(0, 0, 5000, 5000)
    view.show()
    
    sys.exit(app.exec())
    

    I was able to fix both problems although I'm not sure I did it correctly and in the best possible way. What I changed:

    1. Reworked the minimum table size calculation to take into account headers and frame size
    2. changed the image additions to the QIcon using paint. (The image looks stretched if you set its original size using QSize)

    If you have anything to add or suggest, please write.