Splitters

A splitter is a widget that contains other widgets and that separates them with splitter handles. Users can change the sizes of a splitter's child widgets by dragging the handles. Splitters can often be used as an alternative to layout managers, to give more control to the user.

Qt supports splitters with the QSplitter widget. The child widgets of a QSplitter are automatically placed side by side (or one below the other) in the order in which they are created, with splitter bars between adjacent widgets. Here's the code for creating the window depicted in Figure 6.5:

Figure 6.5. The Splitter application's widgets

#include #include #include int main(int argc, char *argv[]) { QApplication app(argc, argv); QSplitter splitter(Qt::Horizontal); splitter.setCaption(QObject::tr("Splitter")); app.setMainWidget(&splitter); QTextEdit *firstEditor = new QTextEdit(&splitter); QTextEdit *secondEditor = new QTextEdit(&splitter); QTextEdit *thirdEditor = new QTextEdit(&splitter); splitter.show(); return app.exec(); }

The example consists of three QTextEdits laid out horizontally by a QSplitter widget. Unlike layout managers, which simply lay out a form's child widgets, QSplitter inherits from QWidget and can be used like any other widget.

A QSplitter can lay out its child widgets either horizontally or vertically. Complex layouts can be achieved by nesting horizontal and vertical QSplitters. For example, the Mail Client application shown in Figure 6.6 consists of a horizontal QSplitter that contains a vertical QSplitter on its right side.

Figure 6.6. The Mail Client application on Mac OS X

Here's the code in the constructor of the Mail Client application's QMainWindow subclass:

MailClient::MailClient(QWidget *parent, const char *name) : QMainWindow(parent, name) { horizontalSplitter = new QSplitter(Horizontal, this); setCentralWidget(horizontalSplitter); foldersListView = new QListView(horizontalSplitter); foldersListView->addColumn(tr("Folders")); foldersListView->setResizeMode(QListView::AllColumns); verticalSplitter = new QSplitter(Vertical, horizontalSplitter); messagesListView = new QListView(verticalSplitter); messagesListView->addColumn(tr("Subject")); messagesListView->addColumn(tr("Sender")); messagesListView->addColumn(tr("Date")); messagesListView->setAllColumnsShowFocus(true); messagesListView->setShowSortIndicator(true); messagesListView->setResizeMode(QListView::AllColumns); textEdit = new QTextEdit(verticalSplitter); textEdit->setReadOnly(true); horizontalSplitter->setResizeMode(foldersListView, QSplitter::KeepSize); verticalSplitter->setResizeMode(messagesListView, QSplitter::KeepSize); ... readSettings(); }

We create the horizontal QSplitter first and set it to be the QMainWindow's central widget. Then we create the child widgets and their child widgets.

When the user resizes a window, QSplitter normally distributes the space so that the relative sizes of the child widgets stay the same. In the Mail Client example, we don't want this behavior; instead we want the two QListViews to maintain their size and we want to give any extra space to the QTextEdit. This is achieved by the two setResizeMode() calls near the end.

When the application is started, QSplitter gives the child widgets appropriate sizes based on their initial sizes. We can move the splitter handles programmatically by calling QSplitter::setSizes(). The QSplitter class also provides a means of saving and restoring its state the next time the application is run. Here's the writeSettings() function that saves the Mail Client's settings:

void MailClient::writeSettings() { QSettings settings; settings.setPath("software-inc.com", "MailClient"); settings.beginGroup("/MailClient"); QString str; QTextOStream out1(&str); out1 >> *horizontalSplitter; settings.writeEntry("/horizontalSplitter", str); QTextOStream out2(&str); out2 >> *verticalSplitter; settings.writeEntry("/verticalSplitter", str); settings.endGroup(); }

Here's the corresponding readSettings() function:

void MailClient::readSettings() { QSettings settings; settings.setPath("software-inc.com", "MailClient"); settings.beginGroup("/MailClient"); QString str1 = settings.readEntry("/horizontalSplitter"); QTextIStream in1(&str1); in1 >> *horizontalSplitter; QString str2 = settings.readEntry("/verticalSplitter"); QTextIStream in2(&str2); in2 >> *verticalSplitter; settings.endGroup(); }

These functions rely on QTextIStream and QTextOStream, two QTextStream convenience subclasses.

By default, a splitter handle is shown as a rubber band while the user is dragging it, and the widgets on either side of the splitter handle are resized only when the user releases the mouse button. To make QSplitter resize the child widgets in real time, we would call setOpaqueResize(true).

QSplitter is fully supported by Qt Designer. To put widgets into a splitter, place the child widgets approximately in their desired positions, select them, and click Layout|Lay Out Horizontally (in Splitter) or Layout|Lay Out Vertically (in Splitter).

Категории