问题
I want to proportionally change the column width of all columns in a QTableView widget, so that each column has the same width regardless of the data. For example, if a table has three columns, each column should always have a width of one third of the available horizontal space - and the width should be automatically updated whenever the dialog is resized by the user.
So far I've only managed to resize columns to their contents, which is not what I want. Here's the code I've got so far:
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_())
I've got the following questions:
- How do I change the code to proportionally adjust the column widths?
- Why doesn't
setWordWrap(True)
wrap the text?
回答1:
This can be achieved by setting the section resize mode. To get equal column widths:
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
To wrap the contents vertically:
self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
There is no need to call resizeToContents
, setWordWrap
or setStretchLastSection
. Calling setWordWrap(False)
will switch to eliding the text on the right, rather than wrapping.
Note that since the row/column resizing is done automatically, the sizes can no longer be changed by the user or programmatically.
回答2:
Following example (PySide, using QT 4.8) will proportionally change the column widths to the width of the QTableView. When the user manually resizes the width of a column (double clicking or dragging the section header), from then on the width of that particular column will remain fixed while the other columns proportionally fill the remaining 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)
The section_resized
method which is meant to apply a fixed width for a certain column, should only run in case the sectionResized
signal has been emitted by (manual) user interaction. The dynamic_column_resize
method (which will be executed each time the QTableWidget
changes width) shouldn't trigger the section_resized
method because then there'll be an infinite loop, because at the end of section_resized
the dynamic_column_resize
method is called. Following decorator is used to prevent this:
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
来源:https://stackoverflow.com/questions/48317828/how-to-proportionally-adjust-column-widths-in-a-qtableview