In QDialog, resize the window to contain all QTableView columns

I am writing a simple program to display the contents of an SQL database table in QDialog (PySide). The goal is to have a method that expands the window to display all the columns, so the user does not need to resize to see everything. This problem was considered in a slightly different context:

Set TableView Width to Content Width

Based on this, I wrote the following method:

def resizeWindowToColumns(self): frameWidth = self.view.frameWidth() * 2 vertHeaderWidth = self.view.verticalHeader().width() horizHeaderWidth =self.view.horizontalHeader().length() vertScrollWidth = self.view.style().pixelMetric(QtGui.QStyle.PM_ScrollBarExtent) fudgeFactor = 6 #not sure why this is needed newWidth = frameWidth + vertHeaderWidth + horizHeaderWidth + vertScrollWidth + fudgeFactor 

It works great. But notice that I had to add fudgeFactor. With fiction, it works fine. But this suggests that I lost the track of six pixels, and I'm very curious where they came from. It doesn't seem to matter how many columns are displayed or their individual widths: fudgeFactor 6 always works.

System Information

Python 2.7 (Spyder / Anaconda). PySide version 1.2.2, Qt version 4.8.5. Laptop Windows 7 with a touch screen (touch screens sometimes hang things in PySide).

Full working example

 # -*- coding: utf-8 -*- import os import sys from PySide import QtGui, QtCore, QtSql class DatabaseInspector(QtGui.QDialog): def __init__(self, tableName, parent = None): QtGui.QDialog.__init__(self, parent) #define model self.model = QtSql.QSqlTableModel(self) self.model.setTable(tableName) self.model.select() #View of model self.view = QtGui.QTableView() self.view.setModel(self.model) #Sizing self.view.resizeColumnsToContents() #Resize columns to fit content self.resizeWindowToColumns() #resize window to fit columns #Quit button self.quitButton = QtGui.QPushButton("Quit"); self.quitButton.clicked.connect(self.reject) #Layout layout = QtGui.QVBoxLayout() layout.addWidget(self.view) #table view layout.addWidget(self.quitButton) #pushbutton self.setLayout(layout) self.show() def resizeEvent(self, event): #This is just to see what going on print "Size set to ({0}, {1})".format(event.size().width(), event.size().height()) def resizeWindowToColumns(self): #Based on: https://stackoverflow.com/a/20807145/1886357 frameWidth = self.view.frameWidth() * 2 vertHeaderWidth = self.view.verticalHeader().width() horizHeaderWidth =self.view.horizontalHeader().length() vertScrollWidth = self.view.style().pixelMetric(QtGui.QStyle.PM_ScrollBarExtent) fudgeFactor = 6 #not sure why this is needed newWidth = frameWidth + vertHeaderWidth + horizHeaderWidth + vertScrollWidth + fudgeFactor if newWidth <= 500: self.resize(newWidth, self.height()) else: self.resize(500, self.height()) def populateDatabase(): print "Populating table in database..." query = QtSql.QSqlQuery() if not query.exec_("""CREATE TABLE favorites ( id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, category VARCHAR(40) NOT NULL, number INTEGER NOT NULL, shortdesc VARCHAR(20) NOT NULL, longdesc VARCHAR(80))"""): print "Failed to create table" return False categories = ("Apples", "Chocolate chip cookies", "Favra beans") numbers = (1, 2, 3) shortDescs = ("Crispy", "Yummy", "Clarice?") longDescs = ("Healthy and tasty", "Never not good...", "Awkward beans for you!") query.prepare("""INSERT INTO favorites (category, number, shortdesc, longdesc) VALUES (:category, :number, :shortdesc, :longdesc)""") for category, number, shortDesc, longDesc in zip(categories, numbers, shortDescs, longDescs): query.bindValue(":category", category) query.bindValue(":number", number) query.bindValue(":shortdesc", shortDesc) query.bindValue(":longdesc", longDesc) if not query.exec_(): print "Failed to populate table" return False return True def main(): import site app = QtGui.QApplication(sys.argv) #Connect to/initialize database dbName = "food.db" tableName = "favorites" site_pack_path = site.getsitepackages()[1] QtGui.QApplication.addLibraryPath('{0}\\PySide\\plugins'.format(site_pack_path)) db = QtSql.QSqlDatabase.addDatabase("QSQLITE") fullFilePath = os.path.join(os.path.dirname(__file__), dbName) #;print fullFilePath dbExists = QtCore.QFile.exists(fullFilePath) #does it already exist in directory? db.setDatabaseName(fullFilePath) db.open() if not dbExists: populateDatabase() #Display database dataTable = DatabaseInspector(tableName) sys.exit(app.exec_()) #Close and delete database (not sure this is needed) db.close() del db if __name__ == "__main__": main() 
+1
qt resize pyqt pyside qtableview
source share
1 answer

The difference between the related question and your example is that the former resizes the widget in the layout, while the latter resizes the top-level window.

The top-level window is usually decorated with a frame. On your system, the width of this frame seems to be three pixels on each side, which is six pixels.

You can calculate this value programmatically with:

  self.frameSize().width() - self.width() 

where self is the top-level window.

However, there may be an additional problem that has to be dealt with, and precisely when to calculate this value. On my Linux system, the frame is not drawn until the window is fully displayed, so the calculation during __init__ does not work.

I worked on this issue as follows:

 dataTable = DatabaseInspector(tableName) dataTable.show() QtCore.QTimer.singleShot(10, dataTable.resizeWindowToColumns) 

but I'm not sure if this is portable (or even necessarily the best way to do this).

PS

It seems that the latter problem may be specific to X11 - see the Window Geometry section in Qt docs.

UPDATE

The above explanation and calculation are incorrect!

Window decoration only makes sense when placing windows. The resize() and setGeometry() functions always exclude the window frame, therefore, when calculating the full width, it does not need to be taken into account.

The difference between resizing a widget in a layout and resizing a top-level window is that the latter must consider the layout of the layout.

So, the correct calculation is as follows:

  margins = self.layout().contentsMargins() self.resize(( margins.left() + margins.right() + self.view.frameWidth() * 2 + self.view.verticalHeader().width() + self.view.horizontalHeader().length() + self.view.style().pixelMetric(QtGui.QStyle.PM_ScrollBarExtent) ), self.height()) 

But note that this always allows you to place a vertical scrollbar.

In the sample script, not enough lines were added to show the vertical scroll bar, therefore it is misleading in this respect - if more lines are added, the full width exactly matches.

+2
source share

All Articles