QSplitter becoming undistinguishable between QWidget and QTabWidget
Asked Answered
M

6

18

I am puting a QWidget and a QTabWidget next to each other in one horisontal splitter. And the splitter loses it's shape, you can know that there is a splitter only by hovering mouse on it. How to make it visible?

Thanks.

Middlebuster answered 30/3, 2010 at 13:34 Comment(0)
C
30

Since the QSplitterHandle (which is what most people think of as the 'splitter') is derived from QWidget, you can add other widgets to it. Here is what I have done to solve this exact problem in the past:

// Now add the line to the splitter handle
// Note: index 0 handle is always hidden, index 1 is between the two widgets
QSplitterHandle *handle = pSplitter->handle(1);
QVBoxLayout *layout = new QVBoxLayout(handle);
layout->setSpacing(0);
layout->setMargin(0);

QFrame *line = new QFrame(handle);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
layout->addWidget(line);

This adds a sunken line to the splitter handle. You can, of course, choose another style for the frame line or use something entirely different as the widget you add to the splitter handle.

Callimachus answered 1/4, 2010 at 21:56 Comment(2)
This is fantastic. I would only add that I prefer a QHBoxLayout because then I can assign a maximumWidth (for horizontal splitters) to the horizontal line.Daves
Upvoted because it does work, but it really feels like a clumsy workaround for a Qt bug...Billiebilling
N
12

I based this on the above code but it handles both splitter orientations. I just preferred non-opaque resizing and non-collapsible children. The grip consists of three parallel lines. You can play with the handle width, but grip at 7 looks good on Windows; haven't checked in Linux or Mac.

void helper::decorateSplitter(QSplitter* splitter, int index)
{
    Q_ASSERT(splitter != NULL);

    const int gripLength = 15; 
    const int gripWidth = 1;
    const int grips = 3;

    splitter->setOpaqueResize(false);
    splitter->setChildrenCollapsible(false);

    splitter->setHandleWidth(7);
    QSplitterHandle* handle = splitter->handle(index);
    Qt::Orientation orientation = splitter->orientation();
    QHBoxLayout* layout = new QHBoxLayout(handle);
    layout->setSpacing(0);
    layout->setMargin(0);

    if (orientation == Qt::Horizontal)
    {
        for (int i=0;i<grips;++i)
        {
            QFrame* line = new QFrame(handle);
            line->setMinimumSize(gripWidth, gripLength);
            line->setMaximumSize(gripWidth, gripLength);
            line->setFrameShape(QFrame::StyledPanel);
            layout->addWidget(line);
        }
    }
    else
    {
        //this will center the vertical grip
        //add a horizontal spacer
        layout->addStretch();
        //create the vertical grip
        QVBoxLayout* vbox = new QVBoxLayout;
        for (int i=0;i<grips;++i)
        {
            QFrame* line = new QFrame(handle);
            line->setMinimumSize(gripLength, gripWidth);
            line->setMaximumSize(gripLength, gripWidth);
            line->setFrameShape(QFrame::StyledPanel);
            vbox->addWidget(line);
        }
        layout->addLayout(vbox);
        //add another horizontal spacer
        layout->addStretch();
    }
}
Negrophobe answered 22/11, 2012 at 13:21 Comment(2)
Probably the best solution here even using Qt5.6. Thanks!Melano
Had to set gripWidth = 2 for the grips to be visible (Win7 Classic look). Otherwise nice solution for a problem that should've been fixed in Qt years ago.Nelly
M
9

This is true for every splitter at least with WinXP and the default Luna thema (changing to classic solves the problem). If you want to stay with Luna you may change the way the splitters are rendered, e.g. by changing the background color of the handle.

int main(int argc, char *argv[])    {

    QApplication a(argc, argv);
    a.setStyleSheet("QSplitter::handle { background-color: gray }");
    MainWindow w;
    w.show();
    return a.exec();
}

You may find more on Qt style sheets at https://doc.qt.io/qt-5/stylesheet-reference.html

Mckoy answered 30/3, 2010 at 16:53 Comment(3)
Thanks. This changes color but I would like to change the splitter relief. Is that possible?Middlebuster
The default behavior of splitter under windows is to draw background color. This is bad when the splitted components have bg-color too by default (QWidget). The windows way is to give them a different color and/or a sunken border (QFrame supports borders). If you want to use relief you have to change the style (forget about style sheet). Use setStyle() to change the style. Reliefs is supported e.g. by QPlastiqueStyle. You may apply setStyle() to the QApplication object or only to the splitter (not preferred). I would propose the native solution mentioned first.Mckoy
You can override QSplitter::createHandle() to return handle with custom drawingDavison
D
1

You can subclass QSplitter and override it's protected virtual QSplitterHandle * QSplitter::createHandle() to return anything that makes you happy.

For example, from that overloaded method you can return subclass of QSplitterHandle with custom drawing.

Davison answered 30/3, 2014 at 9:41 Comment(0)
S
0

Thanks to Merula's answer... I tried this and now my splitters are visible and look very nice without looking obtrusive. This code is for Python using PyQt or PySide.

app = QtGui.QApplication(sys.argv)
app.setStyle("Plastique")   # set style (using this style shows splitters! :)
Stepson answered 24/6, 2014 at 16:49 Comment(2)
what language is this? This does not look like c++Egress
oops.. sorry this is python using pyqt or pysideStepson
E
0

Call splitter_handles {} with the QSplitter you want to add handles to:

#include "splitter_handles.h"
...
QSplitter spl {};
... // widgets added to 'spl'
plitter_handles {spl}; // adding handles
...

Result:

enter image description here

splitter_handles.h

#ifndef SPLITTER_HANDLES_H
#define SPLITTER_HANDLES_H

#include <QLayout>
#include <QPainter>
#include <QSplitter>

class handle : public QWidget
{
    Q_OBJECT

protected:
    void paintEvent(QPaintEvent *e) {
        Q_UNUSED(e);
        QPainter painter {this};
        painter.setPen(Qt::NoPen);
        painter.setBrush(Qt::Dense4Pattern);
        painter.drawRect(this->rect());
    }
};

class splitter_handles {
public:
    splitter_handles(QSplitter * spl) {
        const int width {7};
        spl->setHandleWidth(width);
        for(int h_idx {1}; h_idx < spl->count(); h_idx++) {
            auto l_handle {new handle {}};
            l_handle->setMaximumSize(width*2, width*2);

            auto layout {new QHBoxLayout {spl->handle(h_idx)}};
            layout->setSpacing(0);
            layout->setMargin(1);
            layout->addWidget(l_handle);
        }
    }
};

#endif // SPLITTER_HANDLES_H

main.c

#include <QApplication>
#include <QGroupBox>
#include <QLayout>
#include <QSplitter>

#include "splitter_handles.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    auto spl_v {new QSplitter {Qt::Vertical}};
    spl_v->addWidget(new QGroupBox {"box 1"});
    spl_v->addWidget(new QGroupBox {"box 2"});
    spl_v->addWidget(new QGroupBox {"box 3"});
    splitter_handles {spl_v}; // set handles

    auto wdg  {new QWidget {}};
    auto v_lt {new QVBoxLayout {wdg}};
    v_lt->addWidget(spl_v);
    v_lt->setMargin(0);

    auto spl_h {new QSplitter {}};
    spl_h->addWidget(wdg);
    spl_h->addWidget(new QGroupBox {"box 4"});
    spl_h->addWidget(new QGroupBox {"box 5"});
    splitter_handles {spl_h};

    auto h_lt {new QHBoxLayout {}};
    h_lt->addWidget(spl_h);

    QWidget w {};
    w.setLayout(h_lt);
    w.setGeometry(100,100,500,300);
    w.show();

    return a.exec();
}
Echinus answered 28/1, 2020 at 14:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.