PDA

View Full Version : dock title bar crash on close



Alundra
26th May 2014, 02:48
Hi all,
I have a dock widget custom class who use a custom titlebar.
All works fine but when I close the application and one dock is floating, I have a crash.
Without the custom titlebar I have 0 crash, I tried to find but all looks fine to me.
Code :


CDockWidget::CDockWidget( const QString& Title, QWidget* Parent ) :
QDockWidget( Title, Parent )
{
m_Titlebar = new CTitlebarWidget( this );
setTitleBarWidget( m_Titlebar );
installEventFilter( this );
}

bool CDockWidget::eventFilter( QObject* watched, QEvent* event )
{
switch( event->type() )
{
case QEvent::WindowTitleChange :
{
m_Titlebar->SetTitle( windowTitle() );
return true;
}

case QEvent::NonClientAreaMouseButtonDblClick :
{
if( isMaximized() )
showNormal();
else
showMaximized();
return true;
}
}
return false;
}



CTitlebarWidget::CTitlebarWidget( QWidget* Parent ) :
QFrame( Parent )
{
// Set the object name.
setObjectName( "Titlebar" );

// Connect the dock signal when the floating state change.
connect( Parent, SIGNAL( topLevelChanged( bool ) ), this, SLOT( ChangeFloatingState( bool ) ) );

// Create the title label.
m_TitleLabel = new QLabel( Parent->windowTitle() );
m_TitleLabel->setObjectName( "TitlebarLabel" );

// Minimize button.
m_MinimizeButton = new QToolButton;
m_MinimizeButton->setObjectName( "TitlebarMinimizeButton" );
connect( m_MinimizeButton, SIGNAL( clicked() ), this, SLOT( MinimizeButton() ) );

// Maximize button.
m_MaximizeButton = new QToolButton;
m_MaximizeButton->setObjectName( "TitlebarMaximizeButton" );
connect( m_MaximizeButton, SIGNAL( clicked() ), this, SLOT( ChangeParentMaximizeState() ) );

// Float button.
m_FloatButton = new QToolButton;
m_FloatButton->setObjectName( "TitlebarFloatButton" );
connect( m_FloatButton, SIGNAL( clicked() ), this, SLOT( FloatButton() ) );

// Close button.
m_CloseButton = new QToolButton;
m_CloseButton->setObjectName( "TitlebarCloseButton" );
connect( m_CloseButton, SIGNAL( clicked() ), Parent, SLOT( close() ) );

// Create the control layout.
QHBoxLayout* ControlLayout = new QHBoxLayout;
ControlLayout->setMargin( 0 );
ControlLayout->setSpacing( 0 );
ControlLayout->addWidget( m_MinimizeButton );
ControlLayout->addWidget( m_MaximizeButton );
ControlLayout->addWidget( m_FloatButton );
ControlLayout->addWidget( m_CloseButton );

// Set the floating state.
CDockWidget* ParentDock = static_cast< CDockWidget* >( Parent );
ChangeFloatingState( ParentDock->isFloating() );

// Create the layout.
QHBoxLayout* Layout = new QHBoxLayout;
Layout->setMargin( 4 );
Layout->addWidget( m_TitleLabel );
Layout->addLayout( ControlLayout );

// Set the layout.
setLayout( Layout );
}

void CTitlebarWidget::SetTitle( const QString& Title )
{
m_TitleLabel->setText( Title );
}

QString CTitlebarWidget::GetTitle() const
{
return m_TitleLabel->text();
}

void CTitlebarWidget::mouseDoubleClickEvent( QMouseEvent* event )
{
ChangeParentMaximizeState();
}

void CTitlebarWidget::mouseMoveEvent( QMouseEvent* event )
{
// Base class.
QFrame::mouseMoveEvent( event );

// Check if we move with the left button.
if( event->buttons() == Qt::MouseButton::LeftButton )
{
// Cast the parent.
CDockWidget* Parent = static_cast< CDockWidget* >( parent() );

// Change parent state.
if( Parent->isMaximized() )
Parent->showNormal();
}
}

void CTitlebarWidget::MinimizeButton()
{
CDockWidget* Parent = static_cast< CDockWidget* >( parent() );
Parent->setFloating( false );
}

void CTitlebarWidget::FloatButton()
{
CDockWidget* Parent = static_cast< CDockWidget* >( parent() );
Parent->setFloating( true );
}

void CTitlebarWidget::ChangeParentMaximizeState()
{
// Cast the parent.
CDockWidget* Parent = static_cast< CDockWidget* >( parent() );

// Change parent state.
if( Parent->isMaximized() )
Parent->showNormal();
else
Parent->showMaximized();
}

void CTitlebarWidget::ChangeFloatingState( bool Floating )
{
if( Floating )
{
m_MinimizeButton->setVisible( true );
m_MaximizeButton->setVisible( true );
m_FloatButton->setVisible( false );
}
else
{
m_MinimizeButton->setVisible( false );
m_MaximizeButton->setVisible( false );
m_FloatButton->setVisible( true );
}
}

Is it a possible bug of Qt ?
Thanks

anda_skoa
26th May 2014, 07:19
What's the stack trace when you get the crash?
Could m_Titlebar be invalid in the even filter?

Cheers,
_

P.S.: instead of installing an event filter on yourself, how about implementing the event() method?
Should be the same result but much cleaner code wise.

yuzhouzhiwai
26th May 2014, 09:06
you can debug your program with the stacktrace .
A probable reason is violence memory access

Alundra
26th May 2014, 16:45
What's the stack trace when you get the crash?
P.S.: instead of installing an event filter on yourself, how about implementing the event() method?
Should be the same result but much cleaner code wise.
Thanks for the tip, I changed to :


bool CDockWidget::event( QEvent* event )
{
switch( event->type() )
{
case QEvent::WindowTitleChange :
{
m_Titlebar->SetTitle( windowTitle() );
return true;
}

case QEvent::NonClientAreaMouseButtonDblClick :
{
if( isMaximized() )
showNormal();
else
showMaximized();
return true;
}
}
return QDockWidget::event( event );
}

I still searching where the crash is from, maybe something missing in the custom QDockWidget.

Alundra
26th May 2014, 20:03
I have found the crash, it's here :


void CMainEditorWindow::closeEvent( QCloseEvent* event )
{
DE::CEngine::Shutdown();
SDL_Quit();
}

Apparently it's because I use a QTimer to refresh my central widget who is a 3D render.
When the application close, manager clear data but the render is called who should not.
Is it a way to avoid this problem ? Maybe a way to know the app is closing ?
Here the code of the widget who call the QTimer :


IRenderWidget::IRenderWidget( QWidget* Parent ) :
QWidget( Parent ),
m_Initialized( false ),
m_RenderWindow( NULL )
{
// Initialize the widget.
setMinimumSize( 320, 240 );
setAttribute( Qt::WA_PaintOnScreen );
setAttribute( Qt::WA_OpaquePaintEvent );
setAttribute( Qt::WA_NoSystemBackground );
setFocusPolicy( Qt::StrongFocus );
setMouseTracking( true );

// Set the timer interval.
m_Timer.setInterval( static_cast< int >( (1.0f / 60.0f) * 1000.0f ) );
}

Thanks

EDIT: I have found a solution, say me if it's the only one possible :


void IRenderWidget::closeEvent( QCloseEvent* event )
{
disconnect( &m_Timer, SIGNAL( timeout() ), this, SLOT( UpdateOneFrame() ) );
}

void CMainEditorWindow::closeEvent( QCloseEvent* event )
{
centralWidget()->close();
DE::CEngine::Shutdown();
SDL_Quit();
}

d_stranz
26th May 2014, 20:43
I still searching where the crash is from, maybe something missing in the custom QDockWidget.

Learn how to use a debugger. As others have suggested, the stack trace will tell you exactly where the crash occurs.

Edit -- I see our posts crossed... this is what I originally said:

I would not be surprised to find that it is this line:


CDockWidget* Parent = static_cast< CDockWidget* >( parent() );

How do you know that parent() returns a CDockWidget? You don't check, you just assume that the static_cast<> returns a pointer to one. QDockWidget might have several layers of widgets between itself and its contents to help with layout (like QFrame, QAbstractScrollArea, and so forth). Better code is:



CDockWidget* pParent = qobject_cast< CDockWidget* >( parent() );
if ( pParent )
{
// Non-null, so it really is a CDockWidget and it is safe to do something with it
}

To answer your other question:



void IRenderWidget::closeEvent( QCloseEvent* event )
{
disconnect( &m_Timer, SIGNAL( timeout() ), this, SLOT( UpdateOneFrame() ) );
}


Can't you just call QTimer::stop() instead? "mTimer" looks like it is a member variable in your IRenderWidget class, so it will be disconnected and destroyed when the IRenderWidget instance is destroyed.

Alundra
26th May 2014, 20:51
Learn how to use a debugger.

I saw it, but sounded too weird to me, couldn't believe it.


How do you know that parent() returns a CDockWidget?

Because CTitleBarWidget is always on a CDockWidget, it's the custom titlebar widget.


Can't you just call QTimer::stop() instead? "mTimer" looks like it is a member variable in your IRenderWidget class, so it will be disconnected and destroyed when the IRenderWidget instance is destroyed.

The problem is this render widget is central widget and if I don't do that I have the crash, I must stop myself ...
Image to show you how it is : http://uppix.com/f-DreamEditor1453839b9d00168cf5.png.
It's weird that I only had crash when floating, but the leak was always there.