PyQT中如何让TableView指定列单元格表现为QComboBox而非QLineEdit
给QTableView特定列添加QComboBox风格下拉选择
我写了一个生成可编辑TableView的函数dynamic_table_view,现在需要让其中特定列的所有单元格变成下拉选择框(类似QComboBox)的样式。知道需要用委托,但不想单独写一个全局类,希望在这段代码里实现简洁的方案。
现有代码如下:
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)
解决方案:局部委托类实现
可以在函数内部定义局部委托类,不用单独写全局类,既满足需求又保持代码紧凑。具体实现如下:
修改后的完整代码
def dynamic_table_view(self, cur, table_name, edit=True, add=True, layout=None, combo_columns=None): # 默认空列表,可传入要设置下拉框的列名或索引 if combo_columns is None: combo_columns = [] 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]): 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) # ------------------- 新增下拉框委托部分 ------------------- class ComboDelegate(QStyledItemDelegate): def __init__(self, options, parent=None): super().__init__(parent) self.options = options # 下拉框的选项列表 def createEditor(self, parent, option, index): combo = QComboBox(parent) combo.addItems(self.options) return combo def setEditorData(self, editor, index): # 把当前单元格的值同步到下拉框 current_value = index.data(Qt.DisplayRole) if current_value in self.options: editor.setCurrentText(current_value) def setModelData(self, editor, model, index): # 把下拉框选中的值同步回模型 selected_value = editor.currentText() model.setData(index, selected_value, Qt.EditRole) # 遍历目标列,设置对应委托 for col_info in combo_columns: options = [] if isinstance(col_info, str): # 传入的是列名,转为索引 col_idx = df.columns.get_loc(col_info) # 这里可以根据列名动态获取选项(比如从数据库查枚举值) if col_info == "status": options = ["待处理", "已完成", "已取消"] elif col_info == "category": options = ["分类A", "分类B", "分类C"] elif isinstance(col_info, int): # 传入的是列索引 col_idx = col_info col_name = df.columns[col_idx] if col_name == "status": options = ["待处理", "已完成", "已取消"] if options: delegate = ComboDelegate(options, table_view) table_view.setItemDelegateForColumn(col_idx, delegate) # ------------------- 下拉框委托部分结束 ------------------- #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)
使用方式
调用函数时,通过combo_columns参数指定要设置下拉框的列(支持列名或列索引):
# 示例:给"status"和"category"列设置下拉框 self.dynamic_table_view(cur, "your_table_name", combo_columns=["status", "category"]) # 或者按列索引设置(比如第2、3列) # self.dynamic_table_view(cur, "your_table_name", combo_columns=[2, 3])
内容的提问来源于stack exchange,提问作者polya
相关产品推荐
相关产品推荐

