如何按比例调整 QTableView 中的列宽?
How to proportionally adjust column widths in a QTableView?
我想按比例更改QTableView 小部件中所有列的列宽,以便无论数据如何,每列都具有相同的宽度。例如,如果 table 有三列,则每列的宽度应始终为可用水平宽度的三分之一 space - 并且只要用户调整对话框大小时宽度就会自动更新。
到目前为止,我只能根据内容调整列的大小,这不是我想要的。这是我目前得到的代码:
main.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>624</width>
<height>329</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="tableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="btnPopulate">
<property name="text">
<string>Populate</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>btnPopulate</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
test.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from PyQt5 import uic
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QDialog, QApplication, QHeaderView
class GUI(QDialog):
def __init__(self):
super(GUI, self).__init__()
dirname = os.path.dirname(os.path.abspath(__file__))
uic.loadUi(os.path.join(dirname,'main.ui'), self)
# button
self.btnPopulate.clicked.connect(self.populate)
# table model
self.header = ['col1', 'col2', 'col3']
self.QSModel = QStandardItemModel()
self.QSModel.setColumnCount(3)
self.QSModel.setHorizontalHeaderLabels(self.header)
# table view
self.tableView.setModel(self.QSModel)
self.tableView.setWordWrap(True)
self.tableView.horizontalHeader().setStretchLastSection(False)
def populate(self):
self.longtext = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ac tellus nunc. Phasellus imperdiet leo metus, et gravida lacus. Donec metus ligula, elementum at pellentesque pellentesque, suscipit ac nunc.'''
row = self.QSModel.rowCount()
for x in range(7):
self.QSModel.insertRow(row)
self.QSModel.setData(self.QSModel.index(row, 0), 'Lorem ipsum')
self.QSModel.setData(self.QSModel.index(row, 1), self.longtext)
self.QSModel.setData(self.QSModel.index(row, 2), 'Lorem ipsum')
self.tableView.resizeColumnsToContents()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = GUI()
window.show()
sys.exit(app.exec_())
我有以下问题:
- 如何更改代码以按比例调整列宽?
- 为什么
setWordWrap(True)
不换行?
这可以通过setting the section resize mode来实现。要获得相等的列宽:
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
要垂直环绕内容:
self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
不需要调用resizeToContents
、setWordWrap
或setStretchLastSection
。调用 setWordWrap(False)
将切换为省略右侧的文本,而不是换行。
请注意,由于 row/column 调整大小是自动完成的,因此无法再由用户或以编程方式更改大小。
以下示例(PySide,使用 QT 4.8)将按比例将列宽更改为 QTableView 的宽度。当用户手动调整列的宽度(双击或拖动部分 header)时,从那时起该特定列的宽度将保持固定,而其他列按比例填充剩余的 space。
from PySide.QtGui import *
from PySide.QtCore import QEvent
class CustomTableView(QTableView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.verticalHeader().hide()
self.horizontalHeader().sectionResized.connect(self.section_resized)
self.dynamically_resized = False
self.fixed_section_widths = dict()
@disconnect_section_resized
def dynamic_column_resize(self):
flexible_width = self.width() - 2 - sum(self.fixed_section_widths.values())
column_count = self.model().columnCount()
flexible_column_count = column_count - len(self.fixed_section_widths)
column_width = flexible_width // flexible_column_count if flexible_column_count else 1
last_flexible_column_width = column_width + flexible_width % column_width
for column_index in range(column_count):
if column_index not in self.fixed_section_widths:
width = column_width if flexible_column_count > 1 else last_flexible_column_width
flexible_column_count = flexible_column_count - 1
else:
width = self.fixed_section_widths[column_index]
self.setColumnWidth(column_index, width)
self.dynamically_resized = True
def section_resized(self, column_index, old_size, new_size):
if not self.dynamically_resized:
return
self.fixed_section_widths[column_index] = self.columnWidth(column_index)
self.dynamic_column_resize()
def eventFilter(self, obj, event):
if event.type() == QEvent.Resize:
self.dynamic_column_resize()
return True
return super(QTableView, self).eventFilter(obj, event)
section_resized
方法旨在为特定列应用固定宽度,仅应 运行 以防(手动)用户交互发出 sectionResized
信号. dynamic_column_resize
方法(每次 QTableWidget
更改宽度时都会执行)不应触发 section_resized
方法,因为那样会出现无限循环,因为在 section_resized
dynamic_column_resize
方法被调用。以下装饰器用于防止这种情况:
def disconnect_section_resized(func):
def wrapper(self):
self.horizontalHeader().sectionResized.disconnect(self.section_resized)
func(self)
self.horizontalHeader().sectionResized.connect(self.section_resized)
return wrapper
我想按比例更改QTableView 小部件中所有列的列宽,以便无论数据如何,每列都具有相同的宽度。例如,如果 table 有三列,则每列的宽度应始终为可用水平宽度的三分之一 space - 并且只要用户调整对话框大小时宽度就会自动更新。
到目前为止,我只能根据内容调整列的大小,这不是我想要的。这是我目前得到的代码:
main.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>624</width>
<height>329</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="tableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="btnPopulate">
<property name="text">
<string>Populate</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>btnPopulate</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
test.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from PyQt5 import uic
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QDialog, QApplication, QHeaderView
class GUI(QDialog):
def __init__(self):
super(GUI, self).__init__()
dirname = os.path.dirname(os.path.abspath(__file__))
uic.loadUi(os.path.join(dirname,'main.ui'), self)
# button
self.btnPopulate.clicked.connect(self.populate)
# table model
self.header = ['col1', 'col2', 'col3']
self.QSModel = QStandardItemModel()
self.QSModel.setColumnCount(3)
self.QSModel.setHorizontalHeaderLabels(self.header)
# table view
self.tableView.setModel(self.QSModel)
self.tableView.setWordWrap(True)
self.tableView.horizontalHeader().setStretchLastSection(False)
def populate(self):
self.longtext = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ac tellus nunc. Phasellus imperdiet leo metus, et gravida lacus. Donec metus ligula, elementum at pellentesque pellentesque, suscipit ac nunc.'''
row = self.QSModel.rowCount()
for x in range(7):
self.QSModel.insertRow(row)
self.QSModel.setData(self.QSModel.index(row, 0), 'Lorem ipsum')
self.QSModel.setData(self.QSModel.index(row, 1), self.longtext)
self.QSModel.setData(self.QSModel.index(row, 2), 'Lorem ipsum')
self.tableView.resizeColumnsToContents()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = GUI()
window.show()
sys.exit(app.exec_())
我有以下问题:
- 如何更改代码以按比例调整列宽?
- 为什么
setWordWrap(True)
不换行?
这可以通过setting the section resize mode来实现。要获得相等的列宽:
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
要垂直环绕内容:
self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
不需要调用resizeToContents
、setWordWrap
或setStretchLastSection
。调用 setWordWrap(False)
将切换为省略右侧的文本,而不是换行。
请注意,由于 row/column 调整大小是自动完成的,因此无法再由用户或以编程方式更改大小。
以下示例(PySide,使用 QT 4.8)将按比例将列宽更改为 QTableView 的宽度。当用户手动调整列的宽度(双击或拖动部分 header)时,从那时起该特定列的宽度将保持固定,而其他列按比例填充剩余的 space。
from PySide.QtGui import *
from PySide.QtCore import QEvent
class CustomTableView(QTableView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.verticalHeader().hide()
self.horizontalHeader().sectionResized.connect(self.section_resized)
self.dynamically_resized = False
self.fixed_section_widths = dict()
@disconnect_section_resized
def dynamic_column_resize(self):
flexible_width = self.width() - 2 - sum(self.fixed_section_widths.values())
column_count = self.model().columnCount()
flexible_column_count = column_count - len(self.fixed_section_widths)
column_width = flexible_width // flexible_column_count if flexible_column_count else 1
last_flexible_column_width = column_width + flexible_width % column_width
for column_index in range(column_count):
if column_index not in self.fixed_section_widths:
width = column_width if flexible_column_count > 1 else last_flexible_column_width
flexible_column_count = flexible_column_count - 1
else:
width = self.fixed_section_widths[column_index]
self.setColumnWidth(column_index, width)
self.dynamically_resized = True
def section_resized(self, column_index, old_size, new_size):
if not self.dynamically_resized:
return
self.fixed_section_widths[column_index] = self.columnWidth(column_index)
self.dynamic_column_resize()
def eventFilter(self, obj, event):
if event.type() == QEvent.Resize:
self.dynamic_column_resize()
return True
return super(QTableView, self).eventFilter(obj, event)
section_resized
方法旨在为特定列应用固定宽度,仅应 运行 以防(手动)用户交互发出 sectionResized
信号. dynamic_column_resize
方法(每次 QTableWidget
更改宽度时都会执行)不应触发 section_resized
方法,因为那样会出现无限循环,因为在 section_resized
dynamic_column_resize
方法被调用。以下装饰器用于防止这种情况:
def disconnect_section_resized(func):
def wrapper(self):
self.horizontalHeader().sectionResized.disconnect(self.section_resized)
func(self)
self.horizontalHeader().sectionResized.connect(self.section_resized)
return wrapper