Question

I have a Qt widget with four partitions, separated with splitters. The top level is a vertical splitter, changing the heights of two horizontal splitters, topSplitter and bottomSplitter.

How can I keep both horizontal splitters positions equal, as if it was just one horizontal splitter? I looked at linking signal for splitterMoved, and connecting it to a slot on the other splitter but there are no equivalent slots in the splitter class.

This would obviously have to avoid the issue of an infinite loop where one splitter's position updates the second, which updates the first.

Was it helpful?

Solution

It's pretty simple. Initialization (splitter1 and splitter2 are the splitters that need to be syncronized):

connect(ui->splitter1, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved()));
connect(ui->splitter2, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved()));

The slot:

void MainWindow::splitterMoved() {
  QSplitter* senderSplitter = static_cast<QSplitter*>(sender());
  QSplitter* receiverSplitter = senderSplitter == ui->splitter1 ? 
     ui->splitter2 : ui->splitter1;
  receiverSplitter->blockSignals(true);
  receiverSplitter->setSizes(senderSplitter->sizes());
  receiverSplitter->blockSignals(false);
}

blockSignals ensures that calls will not go to infinite recursion. Actually, setSizes doesn't cause emitting splitterMoved, so you can remove both blockSigals calls and the code will still work. However, there is no note about this in the documentation, so I wouldn't rely on that.

OTHER TIPS

In the containing widget you can create slots to connect to the splitterMoved signal.

There needs to be one slot for each splitter, where it needs to check if the splitter is already the correct size (to avoid the infinite loop) then update the size if necessary.

I am only including one of the example slots and connections, but one will be needed for each splitter that needs to be linked. Putting the following content into the new slots for updating the splitter positions.

QList<int> OtherSize,Current;
OtherSize=topSplitter->sizes();
Current=bottomSplitter->sizes();
if(OtherSize!=Current)
{
    bottomSplitter->setSizes(OtherSize);
}

This will create two lists ready to hold the sizes for the splitters. It gets the current sizes of both splitters, and compares them. The comparison is necessary to avoid the infinite loop. Then, if they are different, it sets the sizes to match that of the other splitter.

Connecting that slot to the appropriate splitterMoved signal should work. This connection is used in the constructor of the containing widget (where you created the new slots).

connect(topSplitter,SIGNAL(splitterMoved(int,int)),this,SLOT(updateBottomSplitter()));

It is safe to ignore the position and index supplied by the signal, because we check the sizes in a different way in this slot.

This will set all sizes, so all splitter bars will match position. If there are only two, it doesn't matter but if there are more than two, when any bar is moved, all will be updated to match.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top