Results 1 to 14 of 14

Thread: QItemDelegate Editor Crash

  1. #1
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Question QItemDelegate Editor Crash

    Greetings,

    I am using Qt4.2.2 on Windows. I am seeing a crash when trying to validate a string in a QLineEdit object created in a QItemDelegate.

    There are 6 characters that are invalid for this string. My thought was to create a delegate that would handle this (based off of the TrackEditor example in the "C++ GUI Programming With Qt4" book.

    If the user enters an invalid character in the string, '(' for example, a message will be displayed to inform them that the string will be changed to replace the '(' with '['. The string is then changed and displayed.

    This all happens after the user presses the Tab or Enter keys. Pressing the Tab key causes this to work as designed. Pressing the Enter key causes a crash. It is crashing after the processing in the SLOT_itemChanged() call, but before the commitData() call in delegate function, commitAndCloseEditor().

    My table also has eventHandler code to determine if the user is exiting the QTableWidgetItem via Tab or Enter key press. This code is not hit, the error occurs before this is called.

    It appears that the pointer to the actual string in the QLineEdit object is getting corrupted by me trying to change it. The fact that it works when a Tab is pressed but not when Enter is pressed is bothersome.

    Has anyone else seen this behavior? Or, might there be a better way to validate a string in a QTableWidget cell?

    Thanks for taking the time to look at this.

    In my table ctor:
    Qt Code:
    1. m_pTableCellKeyHandler = new TableCellKeyHandler( this );
    2.  
    3. setItemDelegate( new DMXTableCellDelegate( this ) );
    4.  
    5. connect( this, SIGNAL( itemChanged( QTableWidgetItem* ) ),
    6. this, SLOT( SLOT_itemChanged( QTableWidgetItem* ) ) );
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. QWidget* DMXTableCellDelegate::createEditor( QWidget* parent,
    2. const QStyleOptionViewItem& option,
    3. const QModelIndex& index ) const
    4. {
    5. if ( index.column() == COL_Name )
    6. {
    7. QLineEdit* lineEdit = new QLineEdit( parent );
    8. lineEdit->setMaxLength( DEVICE_NAME_MAX );
    9.  
    10. connect( lineEdit, SIGNAL( editingFinished() ),
    11. this, SLOT( commitAndCloseEditor() ) );
    12.  
    13. return lineEdit;
    14. }
    15. else if ( index.column() == COL_Next )
    16. {
    17. ...
    18. }
    19. else
    20. return QItemDelegate::createEditor( parent, option, index );
    21. }
    22.  
    23.  
    24. void DMXTableCellDelegate::commitAndCloseEditor( void )
    25. {
    26. QLineEdit* editor = qobject_cast<QLineEdit*>( sender() );
    27. emit commitData( editor );
    28. emit closeEditor( editor );
    29. }
    30.  
    31.  
    32. void DMXTable::SLOT_itemChanged( QTableWidgetItem* pItem )
    33. {
    34. int nRow = pItem->row();
    35. int nCol = pItem->column();
    36.  
    37. CDMXGateway* pDevice = static_cast<CDMXGateway*>( GetDeviceData( nRow ) );
    38. if ( pDevice == NULL )
    39. return;
    40.  
    41. // Turn off sorting
    42. m_pDMXCfgTable->setSortingEnabled( false );
    43.  
    44. // Process device-specific cells
    45. bool bValueChanged = false;
    46. QString sNewName;
    47. bool bNameChanged = false;
    48.  
    49. switch ( nCol )
    50. {
    51. case COL_Name:
    52. {
    53. sNewName = pItem->text();
    54.  
    55. if ( (bNameChanged = updateNameItem( pItem, sNewName, static_cast<CNode*>( pDevice ) )) )
    56. bValueChanged = true;
    57.  
    58. break;
    59. }
    60. ...
    61. }
    62.  
    63. m_pDMXCfgTable->setSortingEnabled( true );
    64. }
    65.  
    66.  
    67. bool Table::updateNameItem( QTableWidgetItem* pItem,
    68. QString& sNewName, CNode* pNode )
    69. {
    70. bool bNameChanged = false;
    71.  
    72. QString sOldName = pNode->GetDeviceName();
    73.  
    74. // Compare new name to old name
    75. if ( sOldName.compare( sNewName, Qt::CaseSensitive ) != 0 )
    76. {
    77. QString sTypedName( sNewName );
    78. if ( !m_parent->ValidateDeviceName( sNewName ) )
    79. {
    80. // Restrict length
    81. if ( sNewName.length() > DEVICE_NAME_MAX )
    82. sNewName.truncate( DEVICE_NAME_MAX );
    83.  
    84. if ( !m_parent->ShowRestrictedCharMsg( sTypedName, sOldName, sNewName ) )
    85. {
    86. pItem->setText( sOldName );
    87. return false;
    88. }
    89.  
    90. pItem->setText( sNewName );
    91. }
    92.  
    93. pNode->SetDeviceName( sNewName );
    94. bNameChanged = true;
    95.  
    96. if ( !m_bIsCfgTable && pNode->IsOnline() ) // update and save now
    97. m_parent->m_pController->UpdateDeviceName( pNode->GetDevType(), sNewName, pNode, true );
    98. }
    99.  
    100. return bNameChanged;
    101. }
    102.  
    103.  
    104. bool MainWin::ValidateDeviceName( QString& sName )
    105. {
    106. bool bCharIsValid = true;
    107. QChar c;
    108.  
    109. // Substitute for illegal Discovery characters
    110. for ( int i = 0; i < sName.length(); i++ )
    111. {
    112. c = sName.at( i );
    113.  
    114. if ( c == '(' )
    115. {
    116. sName.replace( i, 1, "[" );
    117. bCharIsValid = false;
    118. }
    119. ...
    120. }
    121.  
    122. return bCharIsValid;
    123. }
    124.  
    125.  
    126. bool TableCellKeyHandler::eventFilter( QObject* obj, QEvent* pEvent )
    127. {
    128. if ( pEvent->type() == QEvent::KeyPress )
    129. {
    130. QKeyEvent* pKeyEvent = static_cast<QKeyEvent*>( pEvent );
    131. int nKey = pKeyEvent->key();
    132.  
    133. // Check for enter keys: 0x01000004 || 0x01000005
    134. if ( (nKey == Qt::Key_Return) || (nKey == Qt::Key_Enter) )
    135. {
    136. int nRow = m_pParent->currentRow();
    137. int nCol = m_pParent->currentColumn();
    138. qDebug( "eventFilter(): ( %u, %u )", nRow, nCol );
    139.  
    140. ...
    141. }
    142. }
    143. }
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QItemDelegate Editor Crash

    How about simply installing a QValidator subclass instance on the editor and letting it handle the validation for you? Alternatively you can handle validation in the model itself - in its setData() method (or in setData() of the item itself in case you're using convenience widgets and not the model approach).

  3. #3
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Question Re: QItemDelegate Editor Crash

    Thanks for the reply.

    I want to process the entire (finished) string. Using a QValidator subclass forces handling each restricted character as it is typed -- too cumbersome for what I want to do.

    I am somewhat of a newbie with Qt and don't understand how to access the setData() method you reference while using a QTableWidget. Is there an example or reference you could point me to in Qt Assistant or the Qt 4 book I mentioned?

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QItemDelegate Editor Crash

    Quote Originally Posted by mclark View Post
    I am somewhat of a newbie with Qt and don't understand how to access the setData() method you reference while using a QTableWidget. Is there an example or reference you could point me to in Qt Assistant or the Qt 4 book I mentioned?
    You see, there are different ways of manipulating the edited data depending on what exactly you want to achieve. Basically using the model approach gives you more control over what is happening with your data. If you want to stick with the convenience approach (using *Widget instead of *View), you need to implement your own item class and reimplement its setData() method. In that method you can change the data supplied by the user so that it is valid for your model (with the model approach you could also reject the change). Alternatively you can manipulate the data by reimplementing setModelData() method of the delegate to achieve a similar effect.

  5. #5
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Question Re: QItemDelegate Editor Crash

    wysota, thanks for helping me with this.

    Since I inherited the QTableWidget, release deadlines require me to continue its use, although from everything I've read I really need a QTableView. Since I'm already using a delegate to handle other table cell validation it would seem to make sense to explore the setModelData() route.

    Does something like this appear appropriate (provided I don't mess up the validation code)?

    Qt Code:
    1. void DMXTableCellDelegate::setModelData( QWidget* editor, QAbstractItemModel* model,
    2. const QModelIndex& index ) const
    3. {
    4. if ( index.column() == COL_Name )
    5. {
    6. QLineEdit* lineEdit = qobject_cast<QLineEdit*>( editor );
    7.  
    8. if ( lineEdit != NULL && !lineEdit->text().isEmpty() )
    9. {
    10. QString sValidStr = lineEdit->text();
    11.  
    12. // validate will return true if the string was modified
    13. if ( validate( sValidStr ) == true )
    14. {
    15. // update the model with the modified string
    16. model->setData( index, sValidStr );
    17. return;
    18. }
    19. }
    20. }
    21.  
    22. // validate did NOT modify the string
    23. QItemDelegate::setModelData( editor, model, index );
    24. }
    To copy to clipboard, switch view to plain text mode 

  6. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QItemDelegate Editor Crash

    In general this is ok, but I think that setModelData will only be called if the contents of the editor is modified (I'm not sure about it though). But, as I said, your approach is correct. Note that the only problem is, that you have to have a valid piece of data here. At worst you can just ignore setData just like you seem to do. Oh, and remember about using the appropriate role! In most cases (but not always!) it will be Qt::EditRole.

  7. The following user says thank you to wysota for this useful post:

    mclark (11th September 2007)

  8. #7
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QItemDelegate Editor Crash

    Thanks wysota, I'll give this a try.

  9. #8
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Question Re: QItemDelegate Editor Crash

    I subclassed QItemDelegate::setModelData() and I am now getting multiple calls into it when the QTableWidgetItem loses focus. The end result is a crash in a moc_** file when pressing 'Enter'.

    Mouse-clicking, Tab and Enter all cause a double entry into the setModelData() function. Only the Enter key causes a crash.

    I am baffled. Can anyone see something wrong with my implementation of setModelData()?

    Qt Code:
    1. int Table::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
    2. {
    3. --->_id = QTableWidget::qt_metacall(_c, _id, _a);
    4. . . .
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. void DMXTableCellDelegate::setModelData( QWidget* editor, QAbstractItemModel* model,
    2. const QModelIndex& index ) const
    3. {
    4. if ( index.column() == COL_Name )
    5. {
    6. QLineEdit* lineEdit = qobject_cast<QLineEdit*>( editor );
    7.  
    8. // Validate the Name string
    9. if ( (lineEdit != NULL) || !lineEdit->text().isEmpty() )
    10. {
    11. QString sTypedName = lineEdit->text();
    12.  
    13. // Substitute for illegal characters
    14. QString sNewName = "";
    15. bool bIsValid = true;
    16. QChar c;
    17.  
    18. for ( int i = 0; i < sTypedName.length(); i++ )
    19. {
    20. c = sTypedName.at( i );
    21.  
    22. if ( (c == '(') || (c == '<') )
    23. {
    24. c = '[';
    25. bIsValid = false;
    26. }
    27. else if ( (c == ')') || (c == '>') )
    28. {
    29. . . .
    30. }
    31.  
    32. sNewName.append( c );
    33. }
    34.  
    35. if ( !bIsValid )
    36. {
    37. QString sOrigName = index.data( Qt::EditRole ).toString();
    38.  
    39. int rVal = QMessageBox::warning( qobject_cast<QWidget*>( m_parent ),
    40. "Restricted Characters Found"),
    41. "Message String"),
    42. QMessageBox::Ok, QMessageBox::Cancel );
    43. if ( rVal == QMessageBox::Cancel )
    44. model->setData( index, QVariant( sOrigName ) );
    45. else
    46. model->setData( index, QVariant( sNewName ) );
    47. }
    48. }
    49.  
    50. return;
    51. }
    52.  
    53. QItemDelegate::setModelData( editor, model, index );
    54. }
    To copy to clipboard, switch view to plain text mode 

  10. #9
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QItemDelegate Editor Crash

    Try not to fetch any data from the model.

  11. #10
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Post Re: QItemDelegate Editor Crash

    Originally Posted by wysota
    Try not to fetch any data from the model.
    Are you speaking of the QModelIndex object?

    I only use this string, index.data( Qt::EditRole ).toString(), to form the message for the warning. But, even if I don't use this string I get the same results!

  12. #11
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Question Re: QItemDelegate Editor Crash

    All the problems I'm having are related to the QWidget* parameter in the QMessageBox() call that I'm using to warn the user. If I comment out only the QMessageBox call I do not get another call to setModelData(), nor do I get a crash.

    These will cause the crash in setModelData():
    Qt Code:
    1. QMessageBox::warning( editor,
    2. "Message Title",
    3. "Message String",
    4. QMessageBox::Ok, QMessageBox::NoButton );
    5.  
    6. QMessageBox::warning( qobject_cast<QWidget*>( m_parent ),
    7. "Message Title",
    8. "Message String",
    9. QMessageBox::Ok, QMessageBox::NoButton );
    To copy to clipboard, switch view to plain text mode 

    Is the problem that I cannot call QMessageBox from within the setModelData() function?

    If this is NOT the problem, what QWidget* pointer should I be using when I call QMessageBox? The above examples use the QWidget* editor parameter and the delegate owners pointer. What else could I use here?

  13. #12
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QItemDelegate Editor Crash

    In general you shouldn't do that because setModelData can be called without the editor. If you really need such a mechanism, use a custom event or something like that to trigger the message box after the new data is already in the model and the flow returns to the event loop.

  14. #13
    Join Date
    Aug 2006
    Location
    Madison, WI USA
    Posts
    153
    Thanks
    35
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Question Re: QItemDelegate Editor Crash

    I am unfamiliar with customEvent. Since I already have an event filter for my base Table class, can I use it to receive the custom events?

    I'm posting custom events from my setModelData() function but they are never received by my customEvent() handler. Clearly I'm misunderstanding how I need to connect these together. Any suggestions?

    These are my class definitions:
    Qt Code:
    1. class TableEvent : public QEvent
    2. {
    3. public:
    4. TableEvent( Type type, const QString& sOrigValue ) : QEvent( type )
    5. {
    6. m_sOrigValue = sOrigValue;
    7. }
    8.  
    9. public:
    10. QString m_sOrigValue;
    11.  
    12. };
    13.  
    14.  
    15. class TableKeyHandler : public QObject
    16. {
    17. Q_OBJECT
    18.  
    19. public:
    20. TableKeyHandler( Table* parent = 0 ) { m_pParent = parent; }
    21.  
    22. protected:
    23. bool eventFilter( QObject* obj, QEvent* pEvent );
    24. void customEvent( TableEvent* pEvent );
    25.  
    26. private:
    27. Table* m_pParent;
    28.  
    29. };
    To copy to clipboard, switch view to plain text mode 


    And this is how I'm attempting to process events?
    Qt Code:
    1. bool TableKeyHandler::eventFilter( QObject* obj, QEvent* pEvent )
    2. {
    3. if ( pEvent->type() == QEvent::KeyPress )
    4. {
    5. QKeyEvent* pKeyEvent = static_cast<QKeyEvent*>( pEvent );
    6. int nKey = pKeyEvent->key();
    7. . . .
    8. }
    9. }
    10.  
    11.  
    12. void TableCellKeyHandler::customEvent( TableEvent* pEvent )
    13. {
    14. TableEvent* pTableEvent = static_cast<TableEvent*>( pEvent );
    15.  
    16. if ( pEvent->type() == EVENT_NAME_CHANGE )
    17. {
    18. // do validation of Name here
    19. pEvent->accept();
    20. }
    21. else
    22.  
    23. pEvent->ignore();
    24. }
    To copy to clipboard, switch view to plain text mode 

    And this is how I'm attempting to post a custom event:
    Qt Code:
    1. void DMXTableCellDelegate::setModelData( QWidget* editor, QAbstractItemModel* model,
    2. const QModelIndex& index ) const
    3. {
    4. if ( index.column() == COL_Name )
    5. {
    6. QLineEdit* lineEdit = qobject_cast<QLineEdit*>( editor );
    7.  
    8. // Validate the Name string
    9. if ( (lineEdit != NULL) || !lineEdit->text().isEmpty() )
    10. {
    11. QString sOrigName = index.data( Qt::EditRole ).toString();
    12. TableEvent* pEvent = new TableEvent( EVENT_NAME_CHANGE, sOrigName );
    13. QObject* pObj = qobject_cast<QObject*>(m_parent);
    14. static_cast<DMXTable*>( m_parent )->m_parent->m_pApp->postEvent( pObj, pEvent );
    15. }
    16. }
    17. . . .
    18. }
    To copy to clipboard, switch view to plain text mode 

  15. #14
    Join Date
    May 2009
    Posts
    12
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QItemDelegate Editor Crash

    Hello there!

    I know this is an ancient thread, but don't you hate it when a thread is full of questions and not answers?

    I have seen a crash similar to what you describe, but might not be exactly the same.
    The problem is due to the QMessageBox holding up the commit sequence, AND the model being reset at the same time.

    When you call QMessageBox::warning(), it will NOT return from the warning() call until the user has pressed OK on the warning box that appears.

    To make this happen, QMessageBox runs its own QT-event-processor loop within ::warning(), and that processes the gui drawing calls (to make the dialog appear and be interactive) AS WELL AS distributes and processes other events in the system.

    In my case, one of those posted events was updating the data in the table,
    which called model->reset(),
    which reset the editor,
    which called editor->deleteLater(),
    and then the DeleteLater event was processed, my editor was deleted.
    The trap is set.

    When the OK in QMessageBox is pressed, delegate->setModelData() can finally return.
    The very next call (in qabstractitemview.cpp) is editor->installEventFilter().
    So it calls a method on a stale and invalid pointer. FAIL.

    So my solution was to avoid opening a QMessageBox during setModelData().
    Instead, if there are error messages to display, I post a message to the tableview / dialog / something else
    and they can display the error message.

    Takeaway message: setModelData() should not start a new event loop.

Similar Threads

  1. QItemDelegate and QCheckBox
    By KShots in forum Qt Programming
    Replies: 5
    Last Post: 19th November 2008, 00:49
  2. setModelData for subclassed QItemDelegate
    By T4ng10r in forum Qt Programming
    Replies: 7
    Last Post: 27th May 2007, 12:09
  3. Problems with QItemDelegate
    By Jimmy2775 in forum Qt Programming
    Replies: 10
    Last Post: 12th June 2006, 20:18
  4. Replies: 3
    Last Post: 12th May 2006, 19:31

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.