在QDialog中,调整窗口大小以包含QTableView的所有列

时间:2014-11-16 17:39:23

标签: qt pyqt resize pyside qtableview

我正在编写一个简单的程序来在QDialog(PySide)中显示SQL数据库表的内容。目标是有一个扩展窗口以显示所有列的方法,因此用户不必调整大小以查看所有内容。在稍微不同的背景下解决了这个问题:

Fit width of TableView to width of content

基于此,我写了以下方法:

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

效果很好。但请注意,我必须添加一个fudgeFactor。有软糖,它完美地运作。但它表明我已经失去了六个像素的轨迹,并且非常好奇他们来自哪里。显示多少列或它们各自的宽度似乎并不重要:fudgeFactor 6似乎总能工作。

系统详情

Python 2.7(Spyder / Anaconda)。 PySide版本1.2.2,Qt版本4.8.5。带触摸屏的Windows 7笔记本电脑(触摸屏有时会在PySide中搞砸了)。

完整的工作示例

# -*- 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's 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 个答案:

答案 0 :(得分:2)

链接问题与您的示例之间的区别在于,前者是在布局中调整窗口小部件的大小,而后者是调整顶级窗口的大小。

顶级窗口通常用框架装饰。在你的系统上,这个帧的宽度似乎是每边三个像素,总共六个像素。

您可以使用以下方式以编程方式计算此值:

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

其中self是顶级窗口。

但是,可能还有一个额外的问题需要处理,那就是选择来计算这个值。在我的Linux系统上,框架在窗口完全显示之前不会被绘制 - 所以在__init__期间计算不起作用。

我解决了这个问题:

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

但我不确定这是否可移植(或者甚至是最好的方式)。

<强> PS

似乎后一个问题可能特定于X11 - 请参阅Qt文档中的Window Geometry部分。

<强>更新

上述说明和计算不正确!

窗口装饰仅在位置窗口时相关。 resize()setGeometry()函数始终排除窗口框架,因此在计算总宽度时不需要考虑它。

在布局中调整窗口小部件的大小与调整顶级窗口的大小之间的区别在于,后者需要考虑布局边距

所以正确的计算是:

    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())

但请注意,这总是允许空间用于垂直滚动条。

示例脚本没有添加足够的行来显示垂直滚动条,因此在这方面会产生误导 - 如果添加更多行,则总宽度完全正确。