resizeColumnsToContents not working with QSortFilterProxyModel and TableView

Hello. In my app I have a TableView which obtains data from a Sqlite db. If I connect the QSqlTableModel to the view, things work as expected with regards to the resizeColumnsToContents setting. However, when I add QSortFilterProxyModel into the mix, the columns are not resized as expected. I’m probably missing something simple, but I can’t find it.

#!/usr/bin/env python

import sys
import os
from PyQt6.QtSql import *
from PyQt6.QtCore import Qt, QSortFilterProxyModel, QRegularExpression
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QTableView, QMessageBox, QLineEdit, QWidget)

class MainWindow(QMainWindow):
  def __init__(self):
    super().__init__()

    # set up main window
    self.setWindowTitle("Materials")
    self.resize(1024,700)

    self.searchBox = QLineEdit("", clearButtonEnabled=True)

    layout = QVBoxLayout()
    layout.addWidget(self.searchBox)

    container = QWidget()
    container.setLayout(layout)

    # Set up the materials model and view
    self.model_materials = QSqlTableModel()
    self.model_materials.setTable("materials")
      self.model_materials.setEditStrategy(QSqlTableModel.EditStrategy.OnFieldChange)
    self.model_materials_proxy = QSortFilterProxyModel()
    self.model_materials_proxy.setSourceModel(self.model_materials)
    self.model_materials_proxy.setFilterKeyColumn(1)
    self.model_materials_proxy.sort(1, Qt.SortOrder.AscendingOrder)
    self.model_materials_proxy.setFilterRegularExpression(QRegularExpression(self.searchBox.text()))
    self.searchBox.textChanged.connect(self.searchBox.update)
    self.searchBox.textChanged.connect(self.model_materials_proxy.setFilterRegularExpression)
    self.view = QTableView()
    self.view.setModel(self.model_materials_proxy)
    self.view.resizeColumnsToContents()
    self.view.setSortingEnabled(True)
    self.view.sortByColumn(1, Qt.SortOrder.AscendingOrder)
    self.view.verticalHeader().setVisible(False)

    self.model_materials.select()
    layout.addWidget(self.view)
    self.setCentralWidget(container)


# Establish connection to our database.
def dbconnect():
  con = QSqlDatabase.addDatabase("QSQLITE")
  con.setDatabaseName(os.path.abspath(os.path.join(os.path.dirname(__file__), 'feed.sqlite')))
  if not con.open():
    QMessageBox.critical(
        None,
        "Equine Feed Planner - Error!",
        "Database Error: %s" % con.lastError().databaseText(),
    )
    sys.exit(1)

def main():
  app = QApplication(sys.argv)
  dbconnect()
  w = MainWindow()
  w.show()
  app.exec()

if __name__ == '__main__':
    main()

Hi @peterloron welcome to the forum.

Looking at your example I noticed that the resize wasn’t happening at startup. My first instinct was to capture the layoutChanged signal from the proxy and use this to trigger a resize as the filter updates. Interestingly this isn’t fired and I think this might be the root of the issue here – the proxy isn’t emitting layout change signals to the view. I’d need to dig more into how the proxies work.

There is a workaround though. Since updating the text box changes the filter, and so the contents, we can trigger the refresh of the resize from that signal directly.

self.searchBox.textChanged.connect(self.view.resizeColumnsToContents)

With that in place, entering text into the box will adjust the size of the columns as you type.

If that’s not what you want, i.e. you want to set the column widths on all the data & keep them static. You can fire this same thing off using a singleshot timer after creation. This will push the update into the event queue & give the view time to populate.

QTimer.singleShot(0, self.view.resizeColumnsToContents)

If I find a more elegant solution I’ll post an update here.