QTableView and horizontalHeader()->restoreState()
Asked Answered
C

5

3

I can't narrow down this bug, however I seem to have the following problem:

  • saveState() of a horizontalHeader()
  • restart app
  • modify model so that it has one less column
  • restoreState()
  • Now, for some reason, the state of the headerview is totally messed up. I cannot show or hide any new columns, nor can I ever get a reasonable state back

I know, this is not very descriptive but I'm hoping others have had this problem before.

Carilla answered 22/7, 2009 at 4:13 Comment(2)
If you've got PyQt4, I can reproduce the problem here: codepad.org/SUl44mvp Execute each button in turn.Carilla
Hmm, the above code doesn't reproduce the exact problem.Carilla
L
2

For QMainWindow, the save/restoreState takes a version number. QTableView's restoreState() does not, so you need to manage this case yourself.

If you want to restore state even if the model doesn't match, you have these options:

  • Store the state together with a list of the columns that existed in the model upon save, so you can avoid restoring from the data if the columns don't match, and revert to defualt case
  • Implement your own save/restoreState functions that handle that case (ugh)
  • Add a proxy model that has provides bogus/dummy columns for state that is being restored, then remove those columns just afterwards.
Longevity answered 10/12, 2009 at 15:5 Comment(0)
D
1

I personally never use saveState()/restoreState() in any Qt widget, since they just return a binary blob anyway. I want my config files to be human-readable, with simple types. That also gets rid of these kind of problems.

In addition, QHeaderView has the naughty problem that restoreState() (or equivalents) only ever worked for me when the model has already been set, and then some time. I ended up connecting to the QHeaderView::sectionCountChanged() signal and setting the state in the slot called from it.

Dictation answered 22/7, 2009 at 4:49 Comment(1)
Sure, I'd love to but I'm pretty sure that QHeaderView does not expose enough information for me to do the same things as saveState would do :-(Carilla
C
1

Here is the solution I made using Boost Serialization.

It handles new and removed columns, more or less. Works for my use cases.

  // Because QHeaderView sucks
  struct QHeaderViewState
  {
    explicit QHeaderViewState(ssci::CustomTreeView const & view):
      m_headers(view.header()->count())
    {
      QHeaderView const & headers(*view.header());
      // Stored in *visual index* order
      for(int vi = 0; vi < headers.count();++vi)
      {
        int           li     = headers.logicalIndex(vi);
        HeaderState & header = m_headers[vi];

        header.hidden               = headers.isSectionHidden(li);
        header.size                 = headers.sectionSize(li);
        header.logical_index        = li;
        header.visual_index         = vi;
        header.name                 = view.model()->headerData(li,Qt::Horizontal).toString();
        header.view                 = &view;
      }
      m_sort_indicator_shown   = headers.isSortIndicatorShown();
      if(m_sort_indicator_shown)
      {
        m_sort_indicator_section = headers.sortIndicatorSection();
        m_sort_order             = headers.sortIndicatorOrder();
      }
    }

    QHeaderViewState(){}

    template<typename Archive>
    void serialize(Archive & ar, unsigned int)
    {
      ar & m_headers;
      ar & m_sort_indicator_shown;
      if(m_sort_indicator_shown)
      {
        ar & m_sort_indicator_section;
        ar & m_sort_order;
      }
    }

    void
    restoreState(ssci::CustomTreeView & view) const
    {
      QHeaderView & headers(*view.header());

      const int max_columns = std::min(headers.count(),
                                       static_cast<int>(m_headers.size()));      

      std::vector<HeaderState> header_state(m_headers);
      std::map<QString,HeaderState *> map;
      for(std::size_t ii = 0; ii < header_state.size(); ++ii)
        map[header_state[ii].name] = &header_state[ii];

      // First set all sections to be hidden and update logical
      // indexes
      for(int li = 0; li < headers.count(); ++li)
      {
        headers.setSectionHidden(li,true);
        std::map<QString,HeaderState *>::iterator it =
          map.find(view.model()->headerData(li,Qt::Horizontal).toString());
        if(it != map.end())
          it->second->logical_index = li;
      }

      // Now restore
      for(int vi = 0; vi < max_columns; ++vi)
      {
        HeaderState const & header = header_state[vi];
        const int li = header.logical_index;
        SSCI_ASSERT_BUG(vi == header.visual_index);
        headers.setSectionHidden(li,header.hidden);
        headers.resizeSection(li,header.size);
        headers.moveSection(headers.visualIndex(li),vi);
      }
      if(m_sort_indicator_shown)
        headers.setSortIndicator(m_sort_indicator_section,
                                 m_sort_order);
    }

    struct HeaderState
    {
      initialize<bool,false>  hidden;
      initialize<int,0>       size;
      initialize<int,0>       logical_index;
      initialize<int,0>       visual_index;
      QString                 name;
      CustomTreeView const  *view;

      HeaderState():view(0){}

      template<typename Archive>
      void serialize(Archive & ar, unsigned int)
      {
        ar & hidden & size & logical_index & visual_index & name;
      }
    };

    std::vector<HeaderState> m_headers;
    bool                     m_sort_indicator_shown;
    int                      m_sort_indicator_section;
    Qt::SortOrder            m_sort_order; // iff m_sort_indicator_shown
  };
Carilla answered 3/4, 2010 at 22:39 Comment(0)
J
0

I would expect it to break if you change the model! Those functions save and restore private class member variables directly without any sanity checks. Try restoring the state and then changing the model.

Jewbaiting answered 22/7, 2009 at 4:23 Comment(2)
I've tried this as well but when I change the model, then all the previous header view state disappears!Carilla
I would expect that to be the case. How should the view of a one model be automatically adapted to a different model? The Qt library has know way of knowing the relationship between the new sections.Jewbaiting
K
-1

I'm attempting to fix this issue for Qt 5.6.2, after hitting the same issue. See this link for a Qt patch under review, which makes restoreState() handle the case where the number of sections (e.g. columns) in the saved state does not match the number of sections in the current view.

Karie answered 29/5, 2016 at 21:0 Comment(1)
A link to a potential solution is always welcome, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline. Take into account that being barely more than a link to an external site is a possible reason as to Why and how are some answers deleted?.Agle

© 2022 - 2024 — McMap. All rights reserved.