After creating a custom QLabel class that opens a QInputDiaglog popup when double clicked to change the value displayed in the cell, I realized that a QTableWidgetItem can be double clicked to edit its value. I prefer this over the popup from a user perspective. I need to add a new property "init_val" and override a method (not sure which one, maybe write()) to change the background color and insert an entry into the changes_dict property of the main window when the edit is completed.
I looked through the documentation and used "help(QTableWidgetItem)" in console, but still could not figure it out. How can I figure this out on my own in the future?
You can ignore most of the code since it causes a lot of errors. I just wanted to give it a shot before asking for help.
from PyQt6.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QHBoxLayout, QVBoxLayout, QHeaderView, QPushButton, QScrollArea, QLabel, QMainWindow, QInputDialog, QLineEdit, QFormLayout, QDialogButtonBox, QDialog
from PyQt6 import QtCore import sys
class MyQTableWidgetItem(QTableWidgetItem):
clicked = QtCore.pyqtSignal()
def __init__(self, text, parent=None):
super().__init__(text)
self.init_val = text
def write(self, out: QtCore.QDataStream) -> None:
print(out, type(out))
return super().write(out)
def mouseDoubleClickEvent(self, event):
print('twas double clicked')
self.clicked.emit()
class Window(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.column_names = ['1', '2', '3']
self.load_table()
self.btn = QPushButton('Insert Row')
self.layout.addWidget(self.btn)
self.btn.clicked.connect(lambda: self.onClickMe())
self.show()
def load_table(self):
if not hasattr(self, 'table'):
self.table = QTableWidget()
self.table.verticalHeader().setVisible(False)
self.layout.addWidget(self.table)
data = [
[1, 1, 1],
[2, 2, 2],
[3, 3, 3]
]
rows = len(data)
cols = len(data[0])
self.table.setRowCount(rows)
self.table.setColumnCount(cols)
self.table.setHorizontalHeaderLabels(self.column_names)
for i, lis in enumerate(data):
for j, item in enumerate(lis):
print(i, lis, j, item)
item_widget = MyQTableWidgetItem(str(item))
self.table.setItem(i, j, QTableWidgetItem(str(item)))
item_widget.clicked.connect(lambda item_widget=item_widget: self.on_double_click(item_widget, i, j))
def on_double_click(self, item_widget, r, c):
# previously I called a QInputDialog in this function, which automatically returned the text and
# True if the Ok button was clicked. I checked new text vs item_widget.init_val and if they were
# different changed background color and added change to self.changes_dict.
#
# I don't know how I would pull the new text once the QTableWidgetItem value is changed.
text = ...
key = ...
self.changes_dict[key] = text
...
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec())
With this code I get the following error:
File "C:\Users\ngreen\Desktop\Projects\wip_inv_adjustment_app\testing_qtablewidgetitem.py", line 65, in load_table item_widget.clicked.connect(lambda item_widget=item_widget: self.on_double_click(item_widget, i, j)) TypeError: MyQTableWidgetItem cannot be converted to PyQt6.QtCore.QObject
Previously I used self.table.setCellWidget() with a custom class inheriting QLabel instead of self.table.setItem() with a custom class inheriting QTableWidget, so this may be part of the problem.
Using @musicmante's suggestion I tried to override setData() as follows:
def setData(self, role: int, value: typing.Any) -> None:
if value != self.init_val:
self.setBackground(QtGui.QColor(127,255,212))
else:
self.setBackground(QtGui.QColor(255,255,255))
return super().setData(role, value)
but got the following error. Perhaps I cannot change background color in the middle of the setData method?
Error in sys.excepthook:
Original exception was: Unhandled Python exception
setData(). Also, table widget items are not QObjects, so they don't support signals, if you need to call a function when an item is changed, connect to the table'sitemChangedsignal. Finally, items are not even widgets, so themouseDoubleClickEvent()function is useless.setBackground()callssetData(), and you must always check for the role anyway. Addif role in (Qt.DisplayRole, Qt.EditRole):before checking the value and setting anything, otherwise you'll get recursion. Also take your time to read more about Qt model/view programming in order to understand these concepts.