I wrote a class that makes a window, part of this class is a function dynamic_table_view that creates a TableView widget with a possibility to view data and edit it right in that same window:
def dynamic_table_view(self, cur, table_name, edit=True, add=True, layout=None):
if layout is None:
layout = self.main_layout
df = pd.read_sql(f"SELECT * FROM {table_name}", conn)
col_types = parse_types(cur, table_name)
model = QStandardItemModel(df.shape[0], df.shape[1])
model.setHorizontalHeaderLabels(list(df.columns))
for row in range(df.shape[0]):
for col in range(df.shape[1]):
print(col, df.columns, df.columns[col])
item = QStandardItem(str(df.iat[row, col]))
if edit and df.columns[col] != 'id':
item.setEditable(True)
else:
item.setEditable(False)
model.setItem(row, col, item)
table_view = QTableView()
table_view.setModel(model)
if edit:
table_view.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked | QAbstractItemView.EditTrigger.SelectedClicked)
table_view.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
table_view.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
table_view.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
self.add_widget(table_view, layout)
#changing selected item onclick
def on_item_changed(item):
row = item.row()
col = item.column()
new_value = item.text()
column_name = df.columns[col]
row_id = df.iloc[row]["id"]
if col_types[column_name] == "integer":
query = f"UPDATE {table_name} SET {column_name} = {new_value} WHERE id = {row_id}"
else:
query = f"UPDATE {table_name} SET {column_name} = '{new_value}' WHERE id = {row_id}"
execute_query(cur, query)
model.itemChanged.connect(on_item_changed)
Basically, I need to make all cells in a specific column to act like QComboBox. I've read that I'd need to use a delegate for that, but I don't really understand how to implement it. From what I've seen, it could be done with creating a separate class for the delegate or overriding it somehow, but I was wondering if there's a way to make it shorter because I would only need that to be created in that one specific piece of code (for example, I wouldn't create a class for creating windows if I only needed to write one instance of that code).
setIndexWidget(), but you'd need to properly connect signals, possibly using a lambda, in order to set the model data whenever each combo changes index. Be careful, though, as lambdas need special care when created in for loops; in reality, using a custom delegate could actually be more appropriate (other than reliable and more consistent), and you could take it as an opportunity to learn something new (and often useful to know).