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. QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash
Forum Updated to NodeBB v4.3 + New Features

QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash

Scheduled Pinned Locked Moved Unsolved General and Desktop
7 Posts 2 Posters 876 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.
  • l3u_L Offline
    l3u_L Offline
    l3u_
    wrote on last edited by
    #1

    Hi all :-)

    I'm implementing a model/view interface for a table. The table contains row and column spans.

    As far as I got it, there's currently no way to pass spans directly from the model to the view. So I played around how to do it. I though that reimplementing dataChanged would be the right place to do so.

    My first attempt was:

    void DrawView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                            const QVector<int> &roles)
    {
        QTableView::dataChanged(topLeft, bottomRight, roles);
    
        clearSpans();
    
        for (int row = 0; row < m_model->rowCount(); row += 2) {
            setSpan(row, 0, 2, 1);
    
            if (m_model->isTableEmpty(row)) {
                setSpan(row, 1, 2, 2);
            }
        }
    
        resizeColumnsToContents();
    }
    

    This worked:

    1.png

    but as soon as there are not only row spans, but also column ones, the resizing doesn't work as expected anymore:

    2.png

    So I put the setSpan and resizing stuff into a QTimer::singleShot:

    void DrawView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                            const QVector<int> &roles)
    {
        QTableView::dataChanged(topLeft, bottomRight, roles);
    
        QTimer::singleShot(0, [this]
        {
            clearSpans();
    
            for (int row = 0; row < m_model->rowCount(); row += 2) {
                setSpan(row, 0, 2, 1);
    
                if (m_model->isTableEmpty(row)) {
                    setSpan(row, 1, 2, 2);
                }
            }
    
            resizeColumnsToContents();
        });
    }
    

    This made it work also for column spans:

    3.png

    But: (and now, it gets weird ;-) in some cases, the program now crashes when being closed. It's a quite complicated code base, and it's very hard to track this down … however, the program contains network functionality. And as soon as it becomes a server or a client (a tcp server is started or it connects to one), the program crashes when closing:

    #0  0x00007ffff7d1f310 in ?? () from /usr/lib64/libQt5Widgets.so.5
    #1  0x00007ffff7d1f3d1 in QTableView::clearSpans() () from /usr/lib64/libQt5Widgets.so.5
    #2  0x00005555556d8b2f in operator() (__closure=0x55555684afd0)
        at /home/tobias/tmp/git/muckturnier/src/DrawOverviewPage/DrawView.cpp:37
    #3  0x00005555556d8e55 in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, DrawView::dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)::<lambda()> >::call(struct {...} &, void **) (f=..., arg=0x5555563a7fd8)
        at /usr/include/qt5/QtCore/qobjectdefs_impl.h:146
    #4  0x00005555556d8e27 in QtPrivate::Functor<DrawView::dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)::<lambda()>, 0>::call<QtPrivate::List<>, void>(struct {...} &, void *, void **) (f=..., arg=0x5555563a7fd8) at /usr/include/qt5/QtCore/qobjectdefs_impl.h:256
    #5  0x00005555556d8df6 in QtPrivate::QFunctorSlotObject<DrawView::dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)::<lambda()>, 0, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this_=0x55555684afc0, r=0x55555596d0f0, 
        a=0x5555563a7fd8, ret=0x0) at /usr/include/qt5/QtCore/qobjectdefs_impl.h:443
    #6  0x00007ffff6e4efc4 in QObject::event(QEvent*) () from /usr/lib64/libQt5Core.so.5
    #7  0x00007ffff7a441de in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
    from /usr/lib64/libQt5Widgets.so.5
    #8  0x00007ffff6e23ef8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) ()
    from /usr/lib64/libQt5Core.so.5
    #9  0x00007ffff6e27463 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
    from /usr/lib64/libQt5Core.so.5
    #10 0x00007ffff6e76023 in ?? () from /usr/lib64/libQt5Core.so.5
    #11 0x00007ffff58a0d22 in ?? () from /usr/lib64/libglib-2.0.so.0
    #12 0x00007ffff58a3ed7 in ?? () from /usr/lib64/libglib-2.0.so.0
    #13 0x00007ffff58a44fc in g_main_context_iteration () from /usr/lib64/libglib-2.0.so.0
    #14 0x00007ffff6e75b26 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)
        () from /usr/lib64/libQt5Core.so.5
    #15 0x00007ffff6e2291b in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
    from /usr/lib64/libQt5Core.so.5
    #16 0x00007ffff6e2ac1d in QCoreApplication::exec() () from /usr/lib64/libQt5Core.so.5
    #17 0x00005555555d7995 in main (argc=1, argv=0x7fffffffd6a8)
        at /home/tobias/tmp/git/muckturnier/src/main.cpp:168
    

    The interesing thing is that this table view doesn't even have anything to do with the network stuff … also I can't figure out what exactly goes wrong reading the above backtrace … no crash when no server runs.

    However, I suppose it's simply the wrong way to do it.

    After some trying, I ended up just putting the resizing into a timer call and clearing the spans before I update the view, like that:

    void DrawView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                            const QVector<int> &roles)
    {
        clearSpans();
    
        QTableView::dataChanged(topLeft, bottomRight, roles);
    
        for (int row = 0; row < m_model->rowCount(); row += 2) {
            setSpan(row, 0, 2, 1);
    
            if (m_model->isTableEmpty(row)) {
                setSpan(row, 1, 2, 2);
            }
        }
    
        QTimer::singleShot(0, this, &QTableView::resizeColumnsToContents);
    }
    

    This doesn't cause a crash when closign anymore, and it seems to work.

    However, I don't really get it. Can somebody explain to me what's happening here, and tell me if this is the right way to set spans and resize the columns?

    Thanks for all hellp and expertise :-)

    Cheers, Tobias

    Christian EhrlicherC 1 Reply Last reply
    0
    • l3u_L l3u_

      Hi all :-)

      I'm implementing a model/view interface for a table. The table contains row and column spans.

      As far as I got it, there's currently no way to pass spans directly from the model to the view. So I played around how to do it. I though that reimplementing dataChanged would be the right place to do so.

      My first attempt was:

      void DrawView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                              const QVector<int> &roles)
      {
          QTableView::dataChanged(topLeft, bottomRight, roles);
      
          clearSpans();
      
          for (int row = 0; row < m_model->rowCount(); row += 2) {
              setSpan(row, 0, 2, 1);
      
              if (m_model->isTableEmpty(row)) {
                  setSpan(row, 1, 2, 2);
              }
          }
      
          resizeColumnsToContents();
      }
      

      This worked:

      1.png

      but as soon as there are not only row spans, but also column ones, the resizing doesn't work as expected anymore:

      2.png

      So I put the setSpan and resizing stuff into a QTimer::singleShot:

      void DrawView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                              const QVector<int> &roles)
      {
          QTableView::dataChanged(topLeft, bottomRight, roles);
      
          QTimer::singleShot(0, [this]
          {
              clearSpans();
      
              for (int row = 0; row < m_model->rowCount(); row += 2) {
                  setSpan(row, 0, 2, 1);
      
                  if (m_model->isTableEmpty(row)) {
                      setSpan(row, 1, 2, 2);
                  }
              }
      
              resizeColumnsToContents();
          });
      }
      

      This made it work also for column spans:

      3.png

      But: (and now, it gets weird ;-) in some cases, the program now crashes when being closed. It's a quite complicated code base, and it's very hard to track this down … however, the program contains network functionality. And as soon as it becomes a server or a client (a tcp server is started or it connects to one), the program crashes when closing:

      #0  0x00007ffff7d1f310 in ?? () from /usr/lib64/libQt5Widgets.so.5
      #1  0x00007ffff7d1f3d1 in QTableView::clearSpans() () from /usr/lib64/libQt5Widgets.so.5
      #2  0x00005555556d8b2f in operator() (__closure=0x55555684afd0)
          at /home/tobias/tmp/git/muckturnier/src/DrawOverviewPage/DrawView.cpp:37
      #3  0x00005555556d8e55 in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, DrawView::dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)::<lambda()> >::call(struct {...} &, void **) (f=..., arg=0x5555563a7fd8)
          at /usr/include/qt5/QtCore/qobjectdefs_impl.h:146
      #4  0x00005555556d8e27 in QtPrivate::Functor<DrawView::dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)::<lambda()>, 0>::call<QtPrivate::List<>, void>(struct {...} &, void *, void **) (f=..., arg=0x5555563a7fd8) at /usr/include/qt5/QtCore/qobjectdefs_impl.h:256
      #5  0x00005555556d8df6 in QtPrivate::QFunctorSlotObject<DrawView::dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)::<lambda()>, 0, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this_=0x55555684afc0, r=0x55555596d0f0, 
          a=0x5555563a7fd8, ret=0x0) at /usr/include/qt5/QtCore/qobjectdefs_impl.h:443
      #6  0x00007ffff6e4efc4 in QObject::event(QEvent*) () from /usr/lib64/libQt5Core.so.5
      #7  0x00007ffff7a441de in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
      from /usr/lib64/libQt5Widgets.so.5
      #8  0x00007ffff6e23ef8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) ()
      from /usr/lib64/libQt5Core.so.5
      #9  0x00007ffff6e27463 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
      from /usr/lib64/libQt5Core.so.5
      #10 0x00007ffff6e76023 in ?? () from /usr/lib64/libQt5Core.so.5
      #11 0x00007ffff58a0d22 in ?? () from /usr/lib64/libglib-2.0.so.0
      #12 0x00007ffff58a3ed7 in ?? () from /usr/lib64/libglib-2.0.so.0
      #13 0x00007ffff58a44fc in g_main_context_iteration () from /usr/lib64/libglib-2.0.so.0
      #14 0x00007ffff6e75b26 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)
          () from /usr/lib64/libQt5Core.so.5
      #15 0x00007ffff6e2291b in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
      from /usr/lib64/libQt5Core.so.5
      #16 0x00007ffff6e2ac1d in QCoreApplication::exec() () from /usr/lib64/libQt5Core.so.5
      #17 0x00005555555d7995 in main (argc=1, argv=0x7fffffffd6a8)
          at /home/tobias/tmp/git/muckturnier/src/main.cpp:168
      

      The interesing thing is that this table view doesn't even have anything to do with the network stuff … also I can't figure out what exactly goes wrong reading the above backtrace … no crash when no server runs.

      However, I suppose it's simply the wrong way to do it.

      After some trying, I ended up just putting the resizing into a timer call and clearing the spans before I update the view, like that:

      void DrawView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                              const QVector<int> &roles)
      {
          clearSpans();
      
          QTableView::dataChanged(topLeft, bottomRight, roles);
      
          for (int row = 0; row < m_model->rowCount(); row += 2) {
              setSpan(row, 0, 2, 1);
      
              if (m_model->isTableEmpty(row)) {
                  setSpan(row, 1, 2, 2);
              }
          }
      
          QTimer::singleShot(0, this, &QTableView::resizeColumnsToContents);
      }
      

      This doesn't cause a crash when closign anymore, and it seems to work.

      However, I don't really get it. Can somebody explain to me what's happening here, and tell me if this is the right way to set spans and resize the columns?

      Thanks for all hellp and expertise :-)

      Cheers, Tobias

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @l3u_ said in QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash:

      QTimer::singleShot(0, [this]

      You should pass a context to this so the lambda/connect gets deleted when the view gets deleted. Never use a connect without a context. Best define QT_NO_CONTEXTLESS_CONNECT to disable them completely.

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

      l3u_L 2 Replies Last reply
      0
      • Christian EhrlicherC Christian Ehrlicher

        @l3u_ said in QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash:

        QTimer::singleShot(0, [this]

        You should pass a context to this so the lambda/connect gets deleted when the view gets deleted. Never use a connect without a context. Best define QT_NO_CONTEXTLESS_CONNECT to disable them completely.

        l3u_L Offline
        l3u_L Offline
        l3u_
        wrote on last edited by l3u_
        #3

        @Christian-Ehrlicher Okay, so it's the wrong approach – as I supposed ;-)

        I never heard about QT_NO_CONTEXTLESS_CONNECT until now … could you give me a hint about how to pass a context? Or where I can read about this? Thanks in advance!

        Edit: Is this this one? https://doc.qt.io/qt-5/qobject.html#connect-5

        So that I would not write

        QTimer::singleShot(0, [this] { doSomething(); });
        

        but instead

        QTimer::singleShot(0, this, [this] { doSomething(); });
        

        so that the current object is defined as the receiver/context?

        1 Reply Last reply
        0
        • Christian EhrlicherC Christian Ehrlicher

          @l3u_ said in QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash:

          QTimer::singleShot(0, [this]

          You should pass a context to this so the lambda/connect gets deleted when the view gets deleted. Never use a connect without a context. Best define QT_NO_CONTEXTLESS_CONNECT to disable them completely.

          l3u_L Offline
          l3u_L Offline
          l3u_
          wrote on last edited by
          #4

          @Christian-Ehrlicher
          After adding -DQT_NO_CONTEXTLESS_CONNECT to my CMakeLists.txt's add_definitions(), I can still compile QTimer::singleShot(0, [this]? Using Qt 5.15.11?

          Christian EhrlicherC 1 Reply Last reply
          0
          • l3u_L l3u_

            @Christian-Ehrlicher
            After adding -DQT_NO_CONTEXTLESS_CONNECT to my CMakeLists.txt's add_definitions(), I can still compile QTimer::singleShot(0, [this]? Using Qt 5.15.11?

            Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #5

            @l3u_ said in QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash:

            Using Qt 5.15.11

            This was added later. You should have mentioned this in your problem description.

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

            l3u_L 1 Reply Last reply
            0
            • Christian EhrlicherC Christian Ehrlicher

              @l3u_ said in QTableView, clearSpans, setSpan and resizeColumnsToContents: How to do it and weird crash:

              Using Qt 5.15.11

              This was added later. You should have mentioned this in your problem description.

              l3u_L Offline
              l3u_L Offline
              l3u_
              wrote on last edited by
              #6

              @Christian-Ehrlicher Okay, so it seems QT_NO_CONTEXTLESS_CONNECT is a Qt 6 thing. However, if I build my stuff using 6.6.1, I still can do

              QTimer::singleShot(0, [this] { doSomething(); });
              

              Seems like the macro only affects the connect template itself, but not a QTimer::singleShot call?

              1 Reply Last reply
              0
              • l3u_L Offline
                l3u_L Offline
                l3u_
                wrote on last edited by
                #7

                Yeah, okay, I think I got it now.

                Simply never connect to a lambda as such, but always provide an object that will receive the signal. Otherwise, the catched this may already have been deleted, thus causing a segfault.

                However, my intial question still remains: Is the approach posted above the correct way to update spans and resize columns of a QTableView?

                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