Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Programmatically force redraw of QHeaderView
Forum Updated to NodeBB v4.3 + New Features

Programmatically force redraw of QHeaderView

Scheduled Pinned Locked Moved Solved General and Desktop
20 Posts 2 Posters 2.6k Views 1 Watching
  • 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.
  • M Offline
    M Offline
    marcbf
    wrote on last edited by
    #1

    Hi there.

    I'm writing a generalized version of the Frozen Column example. While I've gotten quite far, there is one particular problem for which a solution eludes me.

    A "frozen" view basically consists of two independent (table) views stacked on top of each other. This includes header views. The problem is that if the header view resizes in the other direction than its orientation (e.g. the width of vertical or the height of horizontal header view) there is no (official) way of catching that.

    The reason I need to catch it is to force the "frozen" header view to have the same size as the "non-frozen" in the direction that isn't frozen. For example: I have a vertical header view which as item text contains a row number. As that number gets bigger (e.g. from 9 to 10 or from 99 to 100) the non-frozen header view will automaticallly resize itself (get wider). The frozen header view won't, because there's not event or signal triggered. At least none that I've been able to identify.

    Now, I've (sort of) managed to crack the issue of detecting the resize on the non-frozen header view (abusing the virtual QHeaderView::sectionSizeFromContents() method), but now I can't find a way to tell the frozen header view about it.

    I've tried pretty much everything I can think of: update(), updateGeometry(), updateGeometries() on both the widget and the viewport, where applicable. Nothing seems to work.

    I'd be truly happy for any idea or suggestion.

    1 Reply Last reply
    0
    • Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      I'm not sure if I'm on the right track but doesn't QHeaderView::setSectionSize() or QHeaderView::resize() help here?

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      M 1 Reply Last reply
      2
      • Christian EhrlicherC Christian Ehrlicher

        I'm not sure if I'm on the right track but doesn't QHeaderView::setSectionSize() or QHeaderView::resize() help here?

        M Offline
        M Offline
        marcbf
        wrote on last edited by
        #3

        @Christian-Ehrlicher Thanks for your input, but I need the orthogonal size. The section size only reflects the size for the orientation of the header view, i.e. the width for horizontal header views and the height for vertical header views. Seems the Qt developers didn't think of a use case for the orthogonal sizes.

        1 Reply Last reply
        0
        • Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #4

          The you should try setMiniumWidth/Height.

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          M 1 Reply Last reply
          0
          • Christian EhrlicherC Christian Ehrlicher

            The you should try setMiniumWidth/Height.

            M Offline
            M Offline
            marcbf
            wrote on last edited by
            #5

            @Christian-Ehrlicher Sadly, that won't help me either. I need to react to the changes to the underlying non-frozen header view, so they both stay in sync.

            1 Reply Last reply
            0
            • Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by
              #6

              So why can't you modify the size of the other one with setMin/MaxWidth/Height?

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              M 1 Reply Last reply
              0
              • Christian EhrlicherC Christian Ehrlicher

                So why can't you modify the size of the other one with setMin/MaxWidth/Height?

                M Offline
                M Offline
                marcbf
                wrote on last edited by
                #7

                Because it has no effect whatsoever.

                1 Reply Last reply
                0
                • Christian EhrlicherC Offline
                  Christian EhrlicherC Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  The please provide a minmal example. For me this sounds like https://bugreports.qt.io/browse/QTBUG-34095 which is fixed in 5.12

                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                  Visit the Qt Academy at https://academy.qt.io/catalog

                  M 1 Reply Last reply
                  2
                  • Christian EhrlicherC Christian Ehrlicher

                    The please provide a minmal example. For me this sounds like https://bugreports.qt.io/browse/QTBUG-34095 which is fixed in 5.12

                    M Offline
                    M Offline
                    marcbf
                    wrote on last edited by
                    #9

                    @Christian-Ehrlicher It seems you're right about that bug. It does look very similar to my issue. However, even if it's fixed in 5.12 that would be of no use to me. We're currently on 5.4.1 (commercial) and planning to move to 5.9.7 when it gets available. At the very least this fix should be backported to the 5.9 branch (long term support and all that), preferably 5.9.7. Although I could probably reimplement QHeaderView::resizeEvent() since I'm subclassing anyway, as stated below.

                    I wasn't aware you're working on Qt (by the way, Thorbjørn is a former colleague of mine and (hopefully still :-) a friend).

                    As for the minimal example, sadly I cannot provide one. The code in question is several thousand lines long. What I can do is post my subclassed version of QHeaderView, FrozenHeaderView. It should be possible to plug this in to the Frozen Column example mentioned initially.

                    The FrozenHeaderView class works on two levels. At the base level it's just a wrapper on the normal QHeaderView with added code to try and catch resizing in "the other direction" (i.e. width for vertical headers and height for horizontal headers). That's the one without a "base header". As you can see, I've tried hooking into both sectionSizeFromContents() and paintSection() to get the size change. Both work equally well; that is, the respective signals/slots are emitted/called, but applying the changed width/height in updateSize() to the "frozen" header yields no result, and that seems to be my main issue. I can see that the updatedGeometries() signal is more elegant, but alas, it does not work. And even if, I still can't get the changed size applied.

                    The other level is the "frozen" header. Its sizeHint () method calls the base header's sizeHint() and adjusts the applicable width/height. That bit seems to work OK, but apparently sizeHint() isn't called often. At least not in a way that is helpful to my current problem.

                    Declaration:

                    class FrozenHeaderView : public QHeaderView
                    {
                      Q_OBJECT
                    
                    public:
                      explicit FrozenHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr);
                      explicit FrozenHeaderView(QHeaderView *baseheader, QWidget *parent = nullptr);
                      virtual  ~FrozenHeaderView();
                    
                      void setBaseHeader(QHeaderView *baseheader);
                      QHeaderView* baseHeader() const;
                    
                      virtual QSize sizeHint() const override;
                      virtual QSize minimumSizeHint() const override;
                    
                    Q_SIGNALS:
                      void frozenHeaderResized(int section, int size);
                      void sizeHintChanged(QSize) const;
                      void frozenSizeHintChanged(QSize) const;
                      void sizeChanged(int) const;
                      void sizeUpdated();
                    
                    protected Q_SLOTS:
                      void headerResized(int logicalIndex, int oldSize, int newSize);
                      void updateSize(int);
                    
                    protected:
                      //virtual QSize sectionSizeFromContents(int logicalIndex) const override;
                      virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override;
                    
                      FrozenHeaderView *m_baseheader;
                      mutable QSize m_previoussize;
                      mutable QSize m_cachedsizehint;
                      mutable int   m_basecachedsize;
                      bool m_frozen;
                    };
                    
                    

                    Implementation:

                    FrozenHeaderView::FrozenHeaderView(Qt::Orientation orientation, QWidget *parent)
                    : QHeaderView(orientation, parent), m_baseheader(nullptr), m_frozen(false), m_basecachedsize(0)
                    {
                      connect(this, &QHeaderView::sectionResized, this, &FrozenHeaderView::headerResized);
                      m_cachedsizehint = QHeaderView::sizeHint();
                    }
                    
                    FrozenHeaderView::FrozenHeaderView(QHeaderView *baseheader, QWidget *parent)
                    : QHeaderView(baseheader->orientation(), parent), m_baseheader(nullptr), m_frozen(true), m_basecachedsize(0)
                    {
                      setBaseHeader(baseheader);
                      connect(this, &QHeaderView::sectionResized, this, &FrozenHeaderView::headerResized);
                      m_cachedsizehint = QHeaderView::sizeHint();
                    }
                    
                    FrozenHeaderView::~FrozenHeaderView()
                    {
                      disconnect(this, &QHeaderView::sectionResized, this, &FrozenHeaderView::headerResized);
                      if (m_baseheader)
                        disconnect(m_baseheader, &FrozenHeaderView::sizeChanged, this, &FrozenHeaderView::updateSize);
                    }
                    
                    void FrozenHeaderView::setBaseHeader(QHeaderView *baseheader)
                    {
                      if (baseheader == m_baseheader)
                        return;
                    
                      if (m_baseheader)
                        disconnect(m_baseheader, &FrozenHeaderView::sizeChanged, this, &FrozenHeaderView::updateSize);
                    
                      m_baseheader = qobject_cast<FrozenHeaderView*>(baseheader);
                    
                      if (m_baseheader)
                        connect(m_baseheader, &FrozenHeaderView::sizeChanged, this, &FrozenHeaderView::updateSize, Qt::QueuedConnection);
                    }
                    
                    QHeaderView* FrozenHeaderView::baseHeader() const
                    {
                      return m_baseheader;
                    }
                    
                    QSize FrozenHeaderView::sizeHint() const
                    {
                      QSize frozenSize = QHeaderView::sizeHint();
                    
                      if (m_baseheader)
                      {
                        QSize baseSize = m_baseheader->sizeHint();
                    
                        if (orientation() == Qt::Horizontal)
                          frozenSize.setHeight(baseSize.height());
                        else
                          frozenSize.setWidth(baseSize.width());
                    
                        if (frozenSize != m_previoussize)
                        {
                          m_previoussize = frozenSize;
                          emit sizeHintChanged(frozenSize);
                        }
                      }
                    
                      return frozenSize;
                    }
                    
                    QSize FrozenHeaderView::minimumSizeHint() const
                    {
                      return sizeHint();
                    }
                    
                    void FrozenHeaderView::headerResized(int logicalIndex, int oldSize, int newSize)
                    {
                      emit frozenHeaderResized(logicalIndex, newSize);
                    }
                    
                    void FrozenHeaderView::updateSize(int size)
                    {
                      if (m_baseheader)
                      {
                        int w = width();
                        QRect rect = geometry();
                        if (orientation() == Qt::Horizontal)
                          rect.setHeight(size);
                        else
                          rect.setWidth(size);
                        setGeometry(rect);
                        setMinimumSize(rect.size());
                        setMaximumSize(rect.size());
                        setAttribute(Qt::WA_Resized);
                        emit sizeUpdated();
                      }
                    }
                    
                    //QSize FrozenHeaderView::sectionSizeFromContents(int logicalIndex) const
                    //{
                    //  QSize s = QHeaderView::sectionSizeFromContents(logicalIndex);
                    //  if (m_baseheader == nullptr) // this is the base header
                    //  {
                    //    int newsize = orientation() == Qt::Horizontal ? s.height() : s.width();
                    //
                    //    if (newsize != m_basecachedsize)
                    //    {
                    //      m_basecachedsize = newsize;
                    //      emit sizeChanged(newsize);
                    //    }
                    //  }
                    //  
                    //  return s;
                    //}
                    
                    void FrozenHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
                    {
                      QHeaderView::paintSection(painter, rect, logicalIndex);
                    
                      if (m_baseheader == nullptr) // this is the base header
                      {
                        int newsize = orientation() == Qt::Horizontal ? rect.height() : rect.width();
                    
                        if (newsize != m_basecachedsize)
                        {
                          m_basecachedsize = newsize;
                          emit sizeChanged(newsize);
                        }
                      }
                    }
                    
                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      marcbf
                      wrote on last edited by
                      #10

                      Hmm... Reimplementing resizeEvent() is a much cleaner solution than (ab)using either sectionSizeFromContents() or paintSection(). I'm not sure why I did not go this route earlier. However, I still cannot get the new size applied to the frozen header... :S

                      1 Reply Last reply
                      0
                      • Christian EhrlicherC Offline
                        Christian EhrlicherC Offline
                        Christian Ehrlicher
                        Lifetime Qt Champion
                        wrote on last edited by
                        #11

                        I would try to mimic the behavior of the patch by catching QEvent::Resize and then doing something so d->invalidateCachedSizeHint(); is callled. You could call this directly but then you need to use private headers which should be avoided... maybe by calling dataChanged() with invalid indexes (it calls invalidateCachedSizeHint() without checking the indexes).

                        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                        Visit the Qt Academy at https://academy.qt.io/catalog

                        1 Reply Last reply
                        0
                        • M Offline
                          M Offline
                          marcbf
                          wrote on last edited by
                          #12

                          Dumb question: Should I call dataChanged() on the frozen header or on the view that contains the frozen header? I'm asking because the former doesn't seem to work (as everything else I've tried so far).

                          1 Reply Last reply
                          0
                          • Christian EhrlicherC Offline
                            Christian EhrlicherC Offline
                            Christian Ehrlicher
                            Lifetime Qt Champion
                            wrote on last edited by
                            #13

                            On the headerView which you wanted to resize. Maybe you should first play around with the testcase in the bugreport to find a solution there - so we have a common base and a minimal testcase.

                            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                            Visit the Qt Academy at https://academy.qt.io/catalog

                            1 Reply Last reply
                            0
                            • M Offline
                              M Offline
                              marcbf
                              wrote on last edited by
                              #14

                              Sorry for taking so long to update this. I had to do some changes to the code from the bug, so it would work with Qt 5.4.1. Also, I needed to change it so I could add one line at a time to the view. Well, and regular work. ;-)

                              I wanted to upload the code, but apparently I do not have enough privileges.

                              Well, to make it short, everything works in the test case. Now I just need to figure out why that is and why my (substantially larger) version doesn't. I'll keep this thread updated with my findings.

                              1 Reply Last reply
                              0
                              • Christian EhrlicherC Offline
                                Christian EhrlicherC Offline
                                Christian Ehrlicher
                                Lifetime Qt Champion
                                wrote on last edited by
                                #15

                                At least good to hear that the testcase works :)
                                Uploading to the bug is only possible for bug-owners. You have to upload it here in the forum.

                                Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                Visit the Qt Academy at https://academy.qt.io/catalog

                                M 1 Reply Last reply
                                0
                                • Christian EhrlicherC Christian Ehrlicher

                                  At least good to hear that the testcase works :)
                                  Uploading to the bug is only possible for bug-owners. You have to upload it here in the forum.

                                  M Offline
                                  M Offline
                                  marcbf
                                  wrote on last edited by
                                  #16

                                  That's what I meant. I'm not allowed to upload the code here.

                                  While I'm at it, I've made some progress. Essentially it boils down to me having done too much instead of too little. What I needed to do was simply to emit a signal in the frozen header resize event and then in the main view call updateFrozenTableGeometry() instead of trying to force resizing inside the frozen header itself.

                                  I'm not completly there yet. Still have a few issues I need to iron out.

                                  1 Reply Last reply
                                  0
                                  • M Offline
                                    M Offline
                                    marcbf
                                    wrote on last edited by
                                    #17

                                    Well, it all seems to work reasonably well now. Now I only need to figure out how to synchronize two frozen views (one upper and one lower) so that everything stays in sync. It used to work previously when the upper view didn't resize itself. Now it does, but the size change isn't propagated to the lower one.

                                    1 Reply Last reply
                                    0
                                    • M Offline
                                      M Offline
                                      marcbf
                                      wrote on last edited by
                                      #18

                                      Eureka! Seems like I finally cracked the last piece of the puzzle! I need to call updateGeometries() on the linked (lower) table view. That will call sizeHint() on the header views associated with it and thus propagate the size change and sync the upper and lower table views.

                                      @Christian-Ehrlicher thanks for all your input and pointing me in the direction of that bug report. (I still think that should be backported to 5.9.x. :-)

                                      1 Reply Last reply
                                      0
                                      • Christian EhrlicherC Offline
                                        Christian EhrlicherC Offline
                                        Christian Ehrlicher
                                        Lifetime Qt Champion
                                        wrote on last edited by
                                        #19

                                        @marcbf nice to hear that it's working now. But I don't think I can convince them to merge it back to 5.9 since it's really old, not prioritized and not much people complained about it. So you've a good argument updating to 5.12 soon :)

                                        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                        Visit the Qt Academy at https://academy.qt.io/catalog

                                        1 Reply Last reply
                                        0
                                        • M Offline
                                          M Offline
                                          marcbf
                                          wrote on last edited by
                                          #20

                                          @Christian-Ehrlicher the reason I'm banging on about 5.9 is that it's the latest long time support branch. Since I'm not doing my own thing with Qt but rather working at a company long term support it quite important to us. We don't have the luxury to upgrade to a newer Qt version every month or so. But maybe I should just get in touch with commercial support about this. :-)

                                          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