PDA

View Full Version : Dynamic QSplitter



EricF
25th October 2007, 21:26
Hi,

I'm trying to familiarize myself with the splitter object and coded a little program to experiment with it. I have an object that triggers the split. Here's the header


class WidgetSplitter
{
Q_OBJECT

public:
WidgetSplitter( QWidget* parent = 0 );

signals:
void SplitVertical();
void SplitHorizontal();
void Unsplit();

protected slots:
void SplitVerticalButtonClicked();
void SplitHorizontalButtonClicked();
void UnsplitButtonClicked();

};

I then derived QSplitter(MySplitter) to listen for signals WidgetSplitter is sending. The first time MySplitter receives a split signal, it sets the orientation and add another instance of WidgetSplitter. When orientation is set, as long as user click the split with the same orientation, MySplitter adds another instance of WidgetSplitter. When user wants to split in the other direction, MySplitter replaces the current WidgetSplitter instance by another MySplitter and add twos instances of WidgetSplitter. Unfortunately, when this occurs, it crashes.

The only way I know to remove a widget from a QSplitter is by deleting it. I tried reparenting but with no success. To replace it, I first use indexOf to find position and the delete it. I create a new MySplitter, adds the two WidgetSplitter and insert it at the found index. Obviously this triggers a LayoutRequest and when splitter handles it, the previously deleted widget is still part of the layout and it crashes. I tried deleteLater because I thought it was safer but it's still crashing. Why QSplitter doesn't handle it correctly ?

wysota
25th October 2007, 22:53
Reparenting the widget should have done the trick. What did you reparent it to? And what does WidgetSplitter actually do? The header you posted is incorrect - it's not even a QObject. Did you mean it to be a widget or not?

EricF
26th October 2007, 15:35
Yeah forgot the QWidget inheriting but it was in the code. Discovered the setParent for QObject and QWidget aren't the same so now the layout works ok but I can't destroy the instance of WidgetSplitter.

Either normal delete and deleteLater crash the application in the next LayoutRequest handling by QSplitter. QWidget is destroyed but it is still in LayoutStruct in QSplitter and something weird, QSplitter::ChildEvent never gets called with QEvent::ChildRemoved event type. How come ? As I said, if I don't delete it, the layout works but I have a memory leak.

wysota
26th October 2007, 15:37
Could you attach the complete project code?

EricF
26th October 2007, 15:59
Here's the slots and spliting function :


void TestSplitter::SplitVertical()
{
Split( sender(), Qt::Vertical );
}

void TestSplitter::SplitHorizontal()
{
Split( sender(), Qt::Horizontal);
}

void TestSplitter::Split( QObject* sender, Qt::Orientation splitOrientation )
{
// Get sender's parent(QSplitter object).
QSplitter* splitter = qobject_cast<QSplitter*>( sender->parent() );
if ( splitter )
{
// Check if splitter has already been splitted.
QVariant splitted = splitter->property( "Splitted" );
if ( !splitted.toBool() )
{
// Set orientation.
splitter->setOrientation( splitOrientation );

// Add widget splitter object.
WidgetSplitter* widgetSplitter = new WidgetSplitter( splitter );
splitter->addWidget( widgetSplitter );
widgetSplitter->show();

connect( widgetSplitter, SIGNAL(SplitVertical()), this, SLOT(SplitVertical()) );
connect( widgetSplitter, SIGNAL(SplitHorizontal()), this, SLOT(SplitHorizontal()) );
connect( widgetSplitter, SIGNAL(Unsplit()), this, SLOT(Unsplit()) );

splitter->setProperty( "Splitted", true );
}
else if ( splitOrientation == splitter->orientation() )
{
// Add widget splitter object.
WidgetSplitter* widgetSplitter = new WidgetSplitter( splitter );
splitter->addWidget( widgetSplitter );
widgetSplitter->show();

connect( widgetSplitter, SIGNAL(SplitVertical()), this, SLOT(SplitVertical()) );
connect( widgetSplitter, SIGNAL(SplitHorizontal()), this, SLOT(SplitHorizontal()) );
connect( widgetSplitter, SIGNAL(Unsplit()), this, SLOT(Unsplit()) );
}
else
{
// We must destroy the current widget splitter and add replace it
// with a QSplitter containing 2 widget splitter following
// specified orientation.

QWidget* widget = static_cast<QWidget*>(sender);

// Get index of widget splitter.
int index = splitter->indexOf( widget );

widget->setParent( NULL );
widget->deleteLater();

QSplitter* newSplitter = new QSplitter();
newSplitter->setOrientation( splitOrientation );
newSplitter->setChildrenCollapsible( false );

WidgetSplitter* widgetSplitter = new WidgetSplitter( newSplitter );
newSplitter->addWidget( widgetSplitter );
widgetSplitter->show();

connect( widgetSplitter, SIGNAL(SplitVertical()), this, SLOT(SplitVertical()) );
connect( widgetSplitter, SIGNAL(SplitHorizontal()), this, SLOT(SplitHorizontal()) );
connect( widgetSplitter, SIGNAL(Unsplit()), this, SLOT(Unsplit()) );

widgetSplitter = new WidgetSplitter( newSplitter );
newSplitter->addWidget( widgetSplitter );
widgetSplitter->show();

connect( widgetSplitter, SIGNAL(SplitVertical()), this, SLOT(SplitVertical()) );
connect( widgetSplitter, SIGNAL(SplitHorizontal()), this, SLOT(SplitHorizontal()) );
connect( widgetSplitter, SIGNAL(Unsplit()), this, SLOT(Unsplit()) );

splitter->insertWidget( index, newSplitter );
newSplitter->show();
}
}
}

Keep in mind this is a test application and not production code.