Here is the sample code:
from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem, \
QMainWindow
from PyQt5.QtCore import QSize
import sys
DATA = {
f'col{i}': [f'{i * j}' for j in range(1, 10)] for i in range(1, 10)
}
class Table(QTableWidget):
def __init__(self, d):
m = len(d[next(iter(d))])
n = len(DATA)
super().__init__(m, n)
hor_headers = []
for n, (key, values) in enumerate(DATA.items()):
hor_headers.append(key)
for m, item in enumerate(values):
qtitem = QTableWidgetItem(item)
self.setItem(m, n, qtitem)
self.setHorizontalHeaderLabels(hor_headers)
# the sizeHint works fine if I disable this line
self.setVerticalHeaderLabels(f'row{i}' for i in range(1, m + 2))
self.resizeColumnsToContents()
self.resizeRowsToContents()
# improves the situation but still the window is smaller than the table
def sizeHint(self):
hh = self.horizontalHeader()
vh = self.verticalHeader()
fw = self.frameWidth() * 2
return QSize(
hh.length() + vh.width() + fw,
vh.length() + hh.height() + fw)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('<TITLE>')
table = Table(DATA)
self.setCentralWidget(table)
# did not work
# self.setFixedSize(self.layout().sizeHint())
def main(args):
app = QApplication(args)
main_win = MainWindow()
main_win.show()
raise SystemExit(app.exec_())
if __name__ == "__main__":
main(sys.argv)
Here is the result:
The 9th row and the 9th column are not shown and there are scroll bars.
If I comment out the self.setVerticalHeaderLabels(f'row{i}' for i in range(1, m + 2))
line then it will work:
How can I perfectly fit the main window to the table widget while having vertical header labels?
As you can see in code comments, I have tried the solutions suggested at python qt : automatically resizing main window to fit content but they are not are not working.
The problem is that when a complex widget like an item view is not yet "mapped", the actual size of its children (headers and scroll bars) is not yet updated. Only when the view is finally shown and possibly added to a layout, then it will resize itself again in order to properly resize its children using updateGeometries
.
This means that, until that point, the size of each header is based on its default basic contents (the row number for a vertical header).
The solution is simple: don't use the header size, but their hints, which are computed using the actual text that is going to be displayed:
def sizeHint(self):
hh = self.horizontalHeader()
vh = self.verticalHeader()
fw = self.frameWidth() * 2
return QSize(
hh.length() + vh.sizeHint().width() + fw,
vh.length() + hh.sizeHint().height() + fw)