jim mako
jim mako

Reputation: 561

PyQt - Blink Background Color based on Value Update

I have a problem setting the background color of a QWidgetItem based on a value changed.

I have the following set up that is simply generating random numbers into a QTableWidget based on a click of a button.

I would like to make it that the background of a cell changes based if the new value is higher or lower than the old value. For example, if new value is higher, flash blue for a second (or half a second) or if new value is lower to flash/blink for yellow for some time.

I'm quite lost on where to start in this process.

Many thanks

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
from random import randint

class App(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(100, 100, 350, 380)
        self.createTable()
        self.button = QPushButton('Update Values', self)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)
        self.button.clicked.connect(self.on_click)
        self.show()

    def createTable(self):
        self.tableWidget = QTableWidget()
        self.nrows=10
        self.ncols=3
        self.tableWidget.setRowCount(self.nrows)
        self.tableWidget.setColumnCount(self.ncols)

        for i in range(self.nrows):
            for j in range(self.ncols):
                self.tableWidget.setItem(i, j, QTableWidgetItem('{}'.format(randint(0,9))))

        self.tableWidget.move(0,0)
        self.tableWidget.doubleClicked.connect(self.on_click)

    @pyqtSlot()
    def on_click(self):
        for i in range(self.nrows):
            for j in range(self.ncols):
                self.tableWidget.setItem(i, j, QTableWidgetItem('{}'.format(randint(0,9))))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Upvotes: 1

Views: 2288

Answers (1)

ekhumoro
ekhumoro

Reputation: 120698

There are probably many different ways to implement this. One way is to create a sub-class of QTableWidgetItem and override the setData method. This will allow you to monitor which values are changing for a specific item data role. (If you used a signal like QTableWidgem.itemChanged(), this wouldn't be possible, because it doesn't give you the role).

The only thing to bear in mind about doing things this way, is that it can only monitor changes after the item has been added to the table. So you will need to add blank items first, and then update all the values afterwards.

The mechanism for changing the background color is much simpler, since it only requires a single-shot timer.

Here is a demo of all of the above based on your example:

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon, QColor
from PyQt5.QtCore import pyqtSlot, Qt, QTimer
from random import randint

class TableWidgetItem(QTableWidgetItem):
    def setData(self, role, value):
        if role == Qt.DisplayRole:
            try:
                newvalue = int(value)
                oldvalue = int(self.data(role))
            except (ValueError, TypeError):
                pass
            else:
                if newvalue != oldvalue:
                    if newvalue > oldvalue:
                        color = QColor('aliceblue')
                    elif newvalue < oldvalue:
                        color = QColor('lightyellow')
                    def update_background(color=None):
                        super(TableWidgetItem, self).setData(
                            Qt.BackgroundRole, color)
                    update_background(color)
                    QTimer.singleShot(2000, update_background)
            super(TableWidgetItem, self).setData(role, value)

class App(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(700, 100, 350, 380)
        self.createTable()
        self.button = QPushButton('Update Values', self)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)
        self.button.clicked.connect(self.populateTable)
        self.show()

    def createTable(self):
        self.tableWidget = QTableWidget()
        self.nrows=10
        self.ncols=3
        self.tableWidget.setRowCount(self.nrows)
        self.tableWidget.setColumnCount(self.ncols)

        for i in range(self.nrows):
            for j in range(self.ncols):
                self.tableWidget.setItem(i, j, TableWidgetItem())

        self.tableWidget.move(0,0)
        self.tableWidget.doubleClicked.connect(self.populateTable)
        self.populateTable()

    @pyqtSlot()
    def populateTable(self):
        for i in range(self.nrows):
            for j in range(self.ncols):
                self.tableWidget.item(i, j).setText('{}'.format(randint(0,9)))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions