【问题标题】:In QDialog, resize window to contain all columns of QTableView在 QDialog 中,调整窗口大小以包含 QTableView 的所有列
【发布时间】:2015-01-13 14:47:18
【问题描述】:

我正在编写一个简单的程序来在 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()

【问题讨论】:

    标签: qt pyqt resize pyside qtableview


    【解决方案1】:

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

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

    您可以通过以下方式以编程方式计算此值:

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

    其中self 是顶级窗口。

    但是,可能还有一个额外的问题需要处理,那就是选择 when 来计算这个值。在我的 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())
    

    但请注意,这总是为垂直滚动条留出空间。

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

    【讨论】:

    • 太棒了!做了两件事来拼凑解决方案。将分布在两个 cmets 上。 首先,为了获得正确的宽度,我做了三件事:a) 从 frameWidth 中删除因子 2,所以我们有 frameWidth = self.view.frameWidth()。 b) 使用你的技巧:decoratorWidth = self.frameSize().width()-self.width(),和 c) 在 resizeWindowToColumns 中包含两个宽度。 (我不确定是否应该在上一篇文章中更改 2 的因子(也许它只是因为前面示例中的 decoratorWidth == frameWidth 才有效?)。
    • [comment continue] 第二,为了让它在init中工作,而不是在main中工作,在init中调用show之后,首先调用@ 987654335@ 将所有内容推入队列。只有then 调用resize 方法(resizeWindowToColumns)。瞧!它似乎在我有限的测试用例中完美运行。
    • 虽然上面的方法有效,但感觉还是有点像货物崇拜编程,因为我不太明白我在做什么。人们总是参考的文档中的那个“Windows Geometry”页面?它有多大帮助,真的吗?我觉得这主要是一个令人沮丧的挑逗。它调用的属性甚至没有出现在上面的代码中。文档说要获取包含窗口框架的值,请使用 .x 和 .y。我们在上面根本不使用它。似乎整个部分都可以通过示例进行重大升级,这些示例详尽地探索了不同场景中发生的事情。你可以做到的,嗯 :)
    • @neuronet。抱歉,但我认为该页面没有太大问题。 x()y() 在这里不相关,因为它们与窗口的位置有关,而不是与它的尺寸有关。我的代码使用width()(已提及)和frameSize(),这只是frameGeometry()(也已提及)的一个小变种。话虽如此,我对目前的计算仍然不完全满意,所以我明天可能会重新审视它。
    • 在 x, y 上还不错,我的错。我确实希望文档对此更加详尽。我总觉得我在该页面上找到了一些新属性。
    猜你喜欢
    • 2016-09-14
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 1970-01-01
    相关资源
    最近更新 更多