0

Main Question:

Is there a known threading problem when trying to print something inside a paint event of a QStyledItemDelegate?

Script Files:

style.css:

QTableWidget {
    border: 1px solid #ddd;
}

QTableWidget::item {
    background-color: white;
}

QTableWidget::item:alternate {
    background-color: #f1f1f1;
}

QTableView::item:selected, QTableView::item:alternate:selected {
    background-color: #92b7d1;
}

QHeaderView::section {
    background-color: lightgray;
}

QHeaderView::section:hover {
    background-color: silver;
}

delegate.py:

from PySide2.QtWidgets import QStyledItemDelegate

class TableWidgetDelegate (QStyledItemDelegate):
    def __init__ (self, table):
        QStyledItemDelegate.__init__(self)
        self.table = table

    def paint (self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, index)
        print ('Something')

tablewidget.py

from PySide2.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView
from PySide2.QtCore import Qt
from PySide2.QtGui import QColor
from delegate import TableWidgetDelegate

class TableWidget (QTableWidget):
    def __init__ (self, header=None):
        QTableWidget.__init__(self)
        self.delegate = TableWidgetDelegate(self)
        self.setItemDelegate(self.delegate)

        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setDragDropMode(QAbstractItemView.NoDragDrop)
        self.setFocusPolicy(Qt.NoFocus)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.setAlternatingRowColors(True)
        self.setShowGrid(False)

        tv = self.verticalHeader()
        tv.setVisible(False)
        tv.setSectionsClickable(False)
        tv.setSectionResizeMode(QHeaderView.Fixed)
        tv.setDefaultAlignment(Qt.AlignHCenter)
        
        th = self.horizontalHeader()
        th.setHighlightSections(False)
        th.setStretchLastSection(True)
        th.setSectionResizeMode(QHeaderView.Stretch)

        self.header = None
        self.setHeaders(header)

    def setHeaders (self, header):
        self.setColumnCount(len(header))

        col = 0
        for h in header:
            item = QTableWidgetItem(h)
            item.setTextColor(QColor('black'))
            item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            self.setHorizontalHeaderItem(col, item)
            col += 1

        self.header = header

    def getColor (self, color):
        if (isinstance(color, QColor)):
            return QColor
        else:
            if (type(color) is str):
                return QColor(color)
            elif (type(color) is tuple):
                channels = len(color)
                if (channels == 3):
                    return QColor(color[0], color[1], color[2], 255)
                elif (channels == 4):
                    return QColor(color[0], color[1], color[2], color[3])
            else:
                return QColor('black')

    def setCellData (self, r, c, value):
        item = self.item(r, c)
        if (item is None):
            item = QTableWidgetItem()
            self.setItem(r, c, item)

        item.setText(str(value))

    def insert (self, index, data):
        nrows = self.rowCount()
        ncols = self.columnCount()

        if (index < 0):
            index += (nrows + 1)

        self.insertRow(index)

        columns = range(ncols)
        for c in columns:
            self.setCellData(index, c, data[c])

main.py

from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
from tablewidget import TableWidget

class Scene (QWidget):
    def __init__ (self, win):
        QWidget.__init__(self)
        layout = QVBoxLayout()
        self.window = win
        self.setLayout(layout)

        self.table = TableWidget(['Name', 'Race', 'Class', 'Level'])
        self.table.insert(-1, ['Albert', 'Human', 'Wizard', '3'])
        self.table.insert(-1, ['Dain', 'Dwarf', 'Warrior', '5'])
        self.table.insert(-1, ['Lya', 'Elf', 'Thief', '2'])
        self.table.insert(-1, ['Francis', 'Human', 'Ranger', '4'])

        layout.addWidget(self.table)
        self.window.setWindowTitle('From Scene Widget')

class MainWindow (QMainWindow):
    def __init__ (self, app):
        QMainWindow.__init__(self)
        self.app = app

        # Setting CSS
        try:
            file = open('style.css', 'rt')
        except:
            print ('Failed to load css')
        else:
            data = file.read()
            file.close()

            self.app.setStyleSheet(data)

        # Setting Current Scene
        self.scene = Scene(self)
        self.setCentralWidget(self.scene)

def main ():
    app = QApplication()
    win = MainWindow(app)
    win.show()

    app.exec_()

if __name__ == '__main__':
    main()

Running the Application:

When I type python main.py into my cmd console, this is what shows up: enter image description here

Until here, it's all fine.

However, if you randomly select lines on the console and put the it in front of the Main Window, for some reason, after a few seconds of randomly selecting those lines with the mouse, the application will stop responding. enter image description here

Then, when you finally click enter on the console, nothing shows up. No log, no error messages, no nothing. I believe this is some kind of segmentation fault from the looks of it.

Additional Notes:

I am using Windows 10, Python 3.10, and PySide2.

I also have tried using the faulthandler module to try to get the segmentation fault error, but nothing shows up on my console. It's a silent crash.

Does anyone here knows why it happens? Or maybe it's my own fault, maybe I forgot something? I'm really just starting using QStyledItemDelegates, that's why I'm asking it.

Carl HR
  • 776
  • 5
  • 12
  • 1
    This has nothing to do with painting (which doesn't involve threading, by the way, since all UI operations always happen in the main thread), but to an annoying feature of the Windows 10 console: if you select text, the program will hang until the selection is cleared. (btw, please don't add spaces between function/class names and their parentheses: while it works, it's highly discouraged for styling and readability reasons; read more about this and other important aspects on the official [Style Guide for Python Code](//python.org/dev/peps/pep-0008)) – musicamante May 09 '22 at 20:28
  • Ok. I would never thought of that. Thanks. – Carl HR May 09 '22 at 21:19
  • About your tip of the parentheses @musicamante, no one ever told me it was something bad to add an space after the function name.. Until now I thought it looked cooler this way, that's why I did it all those years. Thanks I guess. – Carl HR May 09 '22 at 21:24
  • If that answer solved your issue, please accept the duplicate request that should appear on top. About the other point: it's not *that* bad, it doesn't create issues from the programming point of view, but it is indeed considered a bad practice for readability purposes: obviously, you can code in whatever style you want, but, when sharing code with others, we should always ignore our preferences and follow the general conventions. Spaces are used not because they look cool, but because they have a syntactic meaning and allow better reading: when parentheses are used to create instances or to-> – musicamante May 09 '22 at 21:36
  • ->call functions, they are a contextual part of the class/function they follow. When the parentheses are, instead, a mathematical/logical aspect, they are actually a different component, and in that case the space allows the reader to easily tell apart those components. `Function(argument)` is not the same as `a + (b * c)`. – musicamante May 09 '22 at 21:39

0 Answers0