Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. How to make QTableView to adjust the height of it's horizontal header automatically in PyQt?

How to make QTableView to adjust the height of it's horizontal header automatically in PyQt?

Scheduled Pinned Locked Moved Unsolved Qt for Python
pyside2qt for python
13 Posts 2 Posters 7.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    ThePyGuy
    wrote on 1 May 2021, 01:57 last edited by ThePyGuy 6 Sept 2021, 10:16
    #1

    The thing is I have long sting for header, typically from 4 characters to somewhere 100 characters but the header is dynamic.

    I found that we can make the header wrap using default alignments as Qt.AlignCenter | Qt.Alignment(Qt.TextWordWrap), however it does not make the horizontal height auto adjust based on the contents, I have some initial widths for each of the column. Is there a way to make the header heights auto adjustable whenever a column is resized?

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 1 May 2021, 19:08 last edited by
      #2

      Hi and welcome to devnet,

      100 chars for a header ? That seems a bit excessive. Can you explain your use case ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      T 1 Reply Last reply 8 Jun 2021, 16:39
      0
      • T Offline
        T Offline
        ThePyGuy
        wrote on 8 Jun 2021, 13:10 last edited by ThePyGuy 6 Aug 2021, 13:19
        #3

        Hi @SGaist , thanks for the welcome and sorry for late response, I didn't know that I have received some response regarding my post, you can consider following code that I wrote for PySide2 for my use case.

        from PySide2.QtCore import *
        from PySide2.QtWidgets import *
        
        
        class HeaderView(QHeaderView):
            def __init__(self, parent):
                super(HeaderView, self).__init__(Qt.Horizontal, parent)
                self.setStyleSheet(
                    "QHeaderView::section{background-color: #ffffff; "
                    "font-weight: bold; "
                    "padding-left: 2px; "
                    "color: #3467ba; "
                    "border:0px; "
                    "border-left: 1px solid #ababab; "
                    "border-bottom: 1px solid gray;}")
                self.setStretchLastSection(True)
                self.setDefaultAlignment(Qt.AlignCenter|Qt.Alignment(Qt.TextWordWrap))
                sizePol = QSizePolicy()
                sizePol.setVerticalPolicy(QSizePolicy.Maximum)
                sizePol.setHorizontalPolicy(QSizePolicy.Maximum)
                self.setSizePolicy(sizePol)
        
        class TableView(QTableView):
            def __init__(self, parent=None):
                super(TableView, self).__init__(parent)
                self.setAlternatingRowColors(True)
                self.setShowGrid(False)
                model = DataModel(self)
                self.setModel(model)
                self.setWordWrap(True)
                self._headerView = HeaderView(self)
                self._headerView.sectionResized.connect(self.resizeColumns)
                self.setHorizontalHeader(self._headerView)
                self.resize(800,400)
        
            def resizeColumns(self, idx, oldSize, newSize):
        
                minHeight =(round(max(self._headerView.sectionSizeFromContents(i).width()/(self._headerView.sectionSize(i))
                                for i in range(self.model().columnCount())))
                            *(self.fontMetrics().height())+5
                )
                self._headerView.setMinimumHeight(minHeight)
        
        class DataModel(QAbstractTableModel):
            def __init__(self, parent):
                super(DataModel, self).__init__(parent)
                #Setting some static data
                self._data = [['Dummy-01-0001', 'M', '41.01026694045174', 'NOT HISPANIC OR LATINO', 'Albumin (g/L)', 'N'] for _ in range(5)]+\
                             [['Dummy-01-0001', 'F', '41.01026694045174', 'HISPANIC OR LATINO', 'Chloride (mmol/L)', 'Y'] for _ in range(5)]
                self._header = ['Unique Subject Identifier (USUBJID)', 'Sex (SEX)', 'Age (AGE)', 'Ethnicity (ETHNIC)', 'Parameter (PARAM)', 'Analysis Reference Range Indicator (ANRIND)']
        
            def rowCount(self, parent=None):
                return len(self._data)
        
            def columnCount(self, parent=None):
                return len(self._header)
        
            def data(self, index, role=Qt.DisplayRole):
                if index.isValid() and role==Qt.DisplayRole:
                    return str(self._data[index.row()][index.column()])
        
            def headerData(self, section, orientation:Qt.Horizontal = Qt.Horizontal, role=Qt.DisplayRole):
                if orientation == Qt.Horizontal:
                    if role == Qt.DisplayRole:
                        return self._header[section]
                    # elif role == Qt.TextAlignmentRole and section==0:
                    #     return Qt.AlignLeft|Qt.Alignment(Qt.TextWordWrap)
        
            def flags(self, index):
                if index.isValid():
                    return Qt.ItemIsEnabled | Qt.ItemIsSelectable
        
        
        if __name__ == "__main__":
            import sys
            app = QApplication()
            widg = TableView()
            widg.show()
            sys.exit(app.exec_())
        

        It has some sample data, as you can see, the header for the sample data is a large string, so I can not show them in a Single line, I need to split it to multiple lines which self.setDefaultAlignment(Qt.AlignCenter|Qt.Alignment(Qt.TextWordWrap) is already doing for me, but the problem is, the header height doesn't change with respect to wrapping of the text in the header. I tried connecting resizeSection signal of headerView to a function where I'm doing some estimation of the required height, and then trying to set the minimum height to this estimated height every time user resizes the width of the header column, but I'm not sure if this is the right way to implement what I'm trying to do, also, since it is manual estimation, it is often going to fail when the header string has some too short or too large words. Wrapping is fine for me, the way string splits up on empty space , I just want the height to be increasing and decreasing accordingly.

        I asked this question on stackoverflow as well QTableView with auto-adjusting height for the header, but to me, it seems that, there isn't much community support for PyQt/PySide on stackoverflow.

        1 Reply Last reply
        0
        • S SGaist
          1 May 2021, 19:08

          Hi and welcome to devnet,

          100 chars for a header ? That seems a bit excessive. Can you explain your use case ?

          T Offline
          T Offline
          ThePyGuy
          wrote on 8 Jun 2021, 16:39 last edited by
          #4

          @SGaist Hi SGiast, you seem to appear online, I'd be very grateful if you help me out, and point me to the right direction.

          S 1 Reply Last reply 8 Jun 2021, 18:26
          0
          • T ThePyGuy
            8 Jun 2021, 16:39

            @SGaist Hi SGiast, you seem to appear online, I'd be very grateful if you help me out, and point me to the right direction.

            S Offline
            S Offline
            SGaist
            Lifetime Qt Champion
            wrote on 8 Jun 2021, 18:26 last edited by
            #5

            One thing which is not good in your model is that you never call the base class implementation of the methods you override for the cases you do not handle. This means that you do not let the standard flow happen.

            I don't have a machine at hand right now, I'll test this later.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            T 1 Reply Last reply 8 Jun 2021, 18:40
            0
            • S SGaist
              8 Jun 2021, 18:26

              One thing which is not good in your model is that you never call the base class implementation of the methods you override for the cases you do not handle. This means that you do not let the standard flow happen.

              I don't have a machine at hand right now, I'll test this later.

              T Offline
              T Offline
              ThePyGuy
              wrote on 8 Jun 2021, 18:40 last edited by
              #6

              @SGaist I tried to take out the code from the actual project to be used as sample, so I might have forgot to implement calling the base classes for the overridden method for the cases that I'm not handling, anyways, please let me know when you are done with the testing, Thank you for your response! I'll be looking forward to hearing from you.

              1 Reply Last reply
              0
              • S Offline
                S Offline
                SGaist
                Lifetime Qt Champion
                wrote on 9 Jun 2021, 07:38 last edited by
                #7

                Here is a simple implementation that gets you what you want.

                Note that you can nuke your resizeColumns parts completely.

                class HeaderView(QHeaderView):
                    def __init__(self, parent=None):
                        super().__init__(Qt.Horizontal, parent=parent)
                        self.setStyleSheet(
                            "QHeaderView::section{background-color: #ffffff; "
                            "font-weight: bold; "
                            "padding-left: 2px; "
                            "color: #3467ba; "
                            "border:0px; "
                            "border-left: 1px solid #ababab; "
                            "border-bottom: 1px solid gray;}"
                        )
                        self.setStretchLastSection(True)
                        self.setDefaultAlignment(Qt.AlignCenter | Qt.Alignment(Qt.TextWordWrap))
                
                    def sectionSizeFromContents(self, logicalIndex):
                        text = self.model().headerData(logicalIndex, self.orientation(), Qt.DisplayRole)
                        alignment = self.defaultAlignment()
                        metrics = QFontMetrics(self.fontMetrics())
                        rect = metrics.boundingRect(QRect(), alignment, text)
                        return rect.size()
                

                On a side note TableView is really not needed here. In fine, you only "configure" it, so there's no need to subclass it.

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                T 1 Reply Last reply 9 Jun 2021, 08:13
                0
                • S SGaist
                  9 Jun 2021, 07:38

                  Here is a simple implementation that gets you what you want.

                  Note that you can nuke your resizeColumns parts completely.

                  class HeaderView(QHeaderView):
                      def __init__(self, parent=None):
                          super().__init__(Qt.Horizontal, parent=parent)
                          self.setStyleSheet(
                              "QHeaderView::section{background-color: #ffffff; "
                              "font-weight: bold; "
                              "padding-left: 2px; "
                              "color: #3467ba; "
                              "border:0px; "
                              "border-left: 1px solid #ababab; "
                              "border-bottom: 1px solid gray;}"
                          )
                          self.setStretchLastSection(True)
                          self.setDefaultAlignment(Qt.AlignCenter | Qt.Alignment(Qt.TextWordWrap))
                  
                      def sectionSizeFromContents(self, logicalIndex):
                          text = self.model().headerData(logicalIndex, self.orientation(), Qt.DisplayRole)
                          alignment = self.defaultAlignment()
                          metrics = QFontMetrics(self.fontMetrics())
                          rect = metrics.boundingRect(QRect(), alignment, text)
                          return rect.size()
                  

                  On a side note TableView is really not needed here. In fine, you only "configure" it, so there's no need to subclass it.

                  T Offline
                  T Offline
                  ThePyGuy
                  wrote on 9 Jun 2021, 08:13 last edited by
                  #8

                  @SGaist Thanks for your response, Yes, I don't need to subclass QTableView, but I need to use the same table at multiple places that is why I sub-classed it. This function sectionSizeFromContents that you overwrote is able to provide the bounding rectangle but it is always constant even if the width of the header section is resized, which is understandable because it is taking only the text and alignment into account and trying to estimate the bounding rectangle for it, and I think it is useful to define some minimum width.
                  What I want is to increase/decrease the height of the header whenever the width of a header section is changed; each section will have their own width, but the height needs to be the maximum of the heights required by the sections.
                  i.e. for example, let's say there are 5 columns with widths: 60, 100, 80, 75, 90, and height required as: 30,40,50,30,30, then the height of the header needs to be 50 i.e. maximum among 30,40,50,30,30.
                  Now let's say user resized a section (2nd section for example) from 100 to 160, then required height for this section will obviously decrease since width for this section increased, let's say required height changed from 50 to 35, then the required heights are 30,40,35,30,30, and maximum of it is 40, so the new height for the header will be 40.

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on 9 Jun 2021, 20:09 last edited by
                    #9

                    What about getting the size of the other sections to create the reference size ?

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    T 2 Replies Last reply 9 Jun 2021, 20:13
                    0
                    • S SGaist
                      9 Jun 2021, 20:09

                      What about getting the size of the other sections to create the reference size ?

                      T Offline
                      T Offline
                      ThePyGuy
                      wrote on 9 Jun 2021, 20:13 last edited by ThePyGuy 6 Sept 2021, 20:18
                      #10

                      @SGaist At a time, the user is able to resize only a single column since the resizing is going to happen through mouse, so anytime a column width is resized, the minimum required height for the resized column needs to be calculated, and it should be checked against the minimum height required for all other columns, and whichever is the maximum value, that becomes the current new height of the header.

                      1 Reply Last reply
                      0
                      • T Offline
                        T Offline
                        ThePyGuy
                        wrote on 10 Jun 2021, 19:09 last edited by
                        #11
                        This post is deleted!
                        1 Reply Last reply
                        0
                        • S SGaist
                          9 Jun 2021, 20:09

                          What about getting the size of the other sections to create the reference size ?

                          T Offline
                          T Offline
                          ThePyGuy
                          wrote on 10 Jun 2021, 19:24 last edited by
                          #12

                          @SGaist Can you please help me with this? You seem to be the only hope I have.

                          1 Reply Last reply
                          0
                          • S Offline
                            S Offline
                            SGaist
                            Lifetime Qt Champion
                            wrote on 15 Jun 2021, 12:30 last edited by
                            #13

                            Try with:

                            MAX_HEIGHT = 4096  # Arbitrary value
                            
                            class HeaderView(QHeaderView):
                                def __init__(self, parent=None):
                                    super().__init__(Qt.Horizontal, parent=parent)
                                    self.setStyleSheet(
                                        "QHeaderView::section{background-color: #ffffff; "
                                        "font-weight: bold; "
                                        "padding-left: 2px; "
                                        "color: #3467ba; "
                                        "border:0px; "
                                        "border-left: 1px solid #ababab; "
                                        "border-bottom: 1px solid gray;}"
                                    )
                                    self.setStretchLastSection(True)
                                    self.setDefaultAlignment(Qt.AlignCenter | Qt.Alignment(Qt.TextWordWrap))
                            
                                def sectionSizeFromContents(self, logicalIndex):
                                    text = self.model().headerData(logicalIndex, self.orientation(), Qt.DisplayRole)
                                    alignment = self.defaultAlignment()
                                    metrics = QFontMetrics(self.fontMetrics())
                                    width = metrics.boundingRect(QRect(), alignment, text).width()
                            
                                    heights = []
                                    for i in range(self.count()):
                                        text = self.model().headerData(i, self.orientation(), Qt.DisplayRole)
                                        size = self.sectionSize(i)
                                        rect = QRect(0, 0, size, MAX_HEIGHT)
                                        heights.append(metrics.boundingRect(rect, alignment, text).height())
                                    height = sorted(heights)[-1]
                                    return QSize(width, height)
                            

                            Interested in AI ? www.idiap.ch
                            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                            1 Reply Last reply
                            0

                            • Login

                            • Login or register to search.
                            • First post
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved