0

So, I'm working on an application and I'm using QTableWidget to generate a table. I want to put the table at the center of the window but I can't find a way to do this, it takes way too much space and its stucked at the top left of the window. I'm putting the table and a button in a QVBoxLayout, there's some blank space after the table (at the bottom and the right) and then the button, far away from the table.

Thats how its looking like

And thats how i want it to be

Right now my code is like this:

from PyQt5.QtWidgets import  QHeaderView, QPushButton, QMainWindow, QApplication, QMenuBar, QAction, QFileDialog, QWidget, QTableView, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem
from PyQt5.QtCore import QAbstractTableModel, Qt
from PyQt5 import QtGui
import sys

class MyApp(QMainWindow):
    def __init__(self):
        super().__init__()

        self.createWindow()

        self.show()


    def createWindow(self):
        self.setWindowTitle('Pós Graduação')
        self.setWindowIcon(QtGui.QIcon('icon.ico'))
        self.setGeometry(300, 100, 700, 600)

        self.table_widget = TableWidget(self)
        self.setCentralWidget(self.table_widget)


class TableWidget(QWidget):

    def __init__(self, parent):        
        super(TableWidget, self).__init__(parent)
        self.layout = QVBoxLayout(self)
        self.creatingTable(parent)
        #self.setLayout(self.layout)


    def creatingTable(self, parent):

        tableWidget = QTableWidget()


        tableWidget.setRowCount(6)
        tableWidget.setColumnCount(4)

        tableWidget.horizontalHeader().setVisible(False)
        tableWidget.verticalHeader().setVisible(False)

        header = tableWidget.horizontalHeader()       
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(3, QHeaderView.ResizeToContents)

        self.layout.addWidget(tableWidget)
        self.button1 = QPushButton("Button 1")
        self.layout.addWidget(self.button1)
        self.setLayout(self.layout)


if __name__ == '__main__':
    App = QApplication(sys.argv)
    App.setStyle('Fusion')
    window = MyApp()
    sys.exit(App.exec())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 1
    You could explain yourself better, maybe an image or scheme of what you want to get helps to understand you. – eyllanesc Feb 12 '20 at 17:40
  • [Check here](https://stackoverflow.com/questions/18385916/how-to-keep-a-qwidget-or-qdialog-centered-to-its-parent-widget). The answer is a C++ answer but the idea is to get the parent and use its dimensions to centre the child. – Clement Osei Tano Feb 12 '20 at 17:48

1 Answers1

0

An item view has a default size "hint" for Qt, which is the size that the widget suggests as the best to show its contents and make it as usable as possible. Also, each widget has a sizePolicy property, which tells how the widget behaves when it's part of a layout (should it shrink, grow, have a fixed size, etc).

Item views like QTableWidget (and its ancestor, QTableView) don't explicitly expose their contents, and that's for obvious reasons: a table could have thousands of rows and columns, and the layout shouldn't really care about them.

To be able to resize the table to its contents, the table has to cycle through all of them, and to do so it's better to do it whenever the contents change their sizes. The best approach is probably to connect to the signals the model and header provide, and set a fixed size each time they are fired, which is something that is better done with subclassing.

class TableWidgetSubclass(QTableWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # update the size each time columns or rows are changed
        self.model().columnsInserted.connect(self.updateSize)
        self.model().columnsRemoved.connect(self.updateSize)
        self.model().rowsInserted.connect(self.updateSize)
        self.model().rowsRemoved.connect(self.updateSize)

        # the same, when a section is resized; note that Qt requires some "time"
        # to do so, so the call to the update function has to be delayed
        self.horizontalHeader().sectionResized.connect(lambda: QTimer.singleShot(0, self.updateSize))
        self.verticalHeader().sectionResized.connect(lambda: QTimer.singleShot(0, self.updateSize))

        # ensure that the widget uses only the maximum required size
        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)

        # and disable the scrollbars
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def updateSize(self):
        width = 0
        header = self.horizontalHeader()
        # go through each column and add its size
        for s in range(self.model().columnCount()):
            width += header.sectionSize(s)

        height = 0
        header = self.verticalHeader()
        # the same for rows
        for s in range(self.model().rowCount()):
            height += header.sectionSize(s)

        size = QSize(width, height)
        # since we've connected a lot of signals and the model could still
        # be empty when calling this slot, ensure that the size is valid
        if size.isValid():
            self.setFixedSize(size)

Finally, when adding a widget to a layout, it usually uses as much space as possible. If it doesn't, it's aligned according to the default top-left. To avoid that, add the widget by specifying the alignment:

class TableWidget(QWidget):

    def __init__(self, parent):        
        super(TableWidget, self).__init__(parent)
        QVBoxLayout(self)
        self.creatingTable(parent)

    def creatingTable(self, parent):

        tableWidget = TableWidgetSubclass()
        # ...
        self.layout().addWidget(tableWidget, alignment=Qt.AlignCenter)
        self.button1 = QPushButton("Button 1")
        self.layout().addWidget(self.button1, alignment=Qt.AlignCenter)

the centered table

Note: as you can see, I didn't use self.layout =, and then I used self.layout(). That's for two reasons:

  1. you should never overwrite basic class attributes (layout is a property of every QWidget)
  2. when adding the target attribute to a layout constructor, that layout is automatically applied to the widget, and there's no need to use setLayout again, since it has already implicitly called.
musicamante
  • 41,230
  • 6
  • 33
  • 58