Qt之四方分割器QuadSplitter
在Qt经常会用到分割器QSplitter,可以对多个控件进行水平或者垂直分割,但有一些特殊的需求无法满足,比如:四方分割。。。QuadSplitter是qt-apps里面的一个应用,挺不错的,拿来和大家分享一下,下载地址:QuadSplitter。
效果如下:
![](https://images2017.cnblogs.com/blog/341522/201710/341522-20171011221407730-1718740778.png)
![](https://images2017.cnblogs.com/blog/341522/201710/341522-20171011221417512-237354914.png)
#ifndef QUADSPLITTER_H #define QUADSPLITTER_H #include class QuadSplitterPrivate; class QuadSplitter : public QFrame { Q_OBJECT public: QuadSplitter(QWidget *parent); ~QuadSplitter(); void addWidget(QWidget *, int row, int column); int spacing() const; void setSpacing(int spacing); int splittersSpacing() const; void setSplittersSpacing(int spacing); int centerPartWidth() const; void setCenterPartWidth(int value); int minimumWidgetSize() const; void setMinimumWidgetSize(int value); protected: void resizeEvent(QResizeEvent *); private: QuadSplitterPrivate * const d_ptr; Q_DISABLE_COPY(QuadSplitter) Q_DECLARE_PRIVATE(QuadSplitter); friend class AdvSplitter; }; #endif // QUADSPLITTER_H #include "quadsplitter.h" #include #include #include #include #include #include class AdvSplitter; class QuadSplitterPrivate : public QObject { Q_DECLARE_PUBLIC(QuadSplitter); public: QuadSplitterPrivate(QuadSplitter* _q_ptr); void addWidget(QWidget *widget, int row, int column); void splitterMoveStart(AdvSplitter* splitter, bool center); void splitterMove(AdvSplitter* splitter, QPoint const& offset, bool center); void arrange(); qreal realPercent(Qt::Orientation orientation) const; int spacing() const; void setSpacing(int spacing); int splittersSpacing() const; void setSplittersSpacing(int spacing); int centerPartWidth() const; void setCenterPartWidth(int value); int minimumWidgetSize() const; void setMinimumWidgetSize(int value); private: QuadSplitter *q_ptr; QScopedPointer _horizontalSplitter; QScopedPointer _verticalSplitter; QPointer _grid[2][2]; int _spacing; int _splittersSpacing; QPoint _splittersMovingPos; int _minimumWidgetSize; int _centerPart; int realSpacing() const; int realWidth() const; int realHeight() const; }; ////////////////////////////////////////////////////////////////////////// class AdvSplitter : public QWidget { public: AdvSplitter(QWidget* parent, QuadSplitterPrivate* priv, Qt::Orientation orientation) : QWidget(parent), _percent(0.5), _orientation(orientation), _mouse(false), _center(false), _private(priv) { setMouseTracking(true); if (orientation == Qt::Horizontal) setCursor(Qt::SplitHCursor); else if (orientation == Qt::Vertical) setCursor(Qt::SplitVCursor); } qreal percent() const { return _percent; } void setPercent(qreal val) { _percent = val; } protected: void paintEvent (QPaintEvent *event) { QStylePainter painter(this); QStyleOption opt1, opt2; opt1.init(this); opt2.init(this); opt1.state = opt2.state = QStyle::State_None; if (_orientation == Qt::Horizontal) { opt1.state |= QStyle::State_Horizontal; opt2.state |= QStyle::State_Horizontal; if (_private) { int hiPart = qFloor((qreal)opt1.rect.height() * _private->realPercent(Qt::Vertical) + 0.5); int loPart = opt1.rect.height() - hiPart; opt1.rect.setBottom(opt1.rect.top() + hiPart); opt2.rect.setTop(opt2.rect.bottom() - loPart); } } else if (_private) { int hiPart = qFloor((qreal)opt1.rect.width() * _private->realPercent(Qt::Horizontal) + 0.5); int loPart = opt1.rect.width() - hiPart; opt1.rect.setRight(opt1.rect.left() + hiPart); opt2.rect.setLeft(opt2.rect.right() - loPart); } painter.drawControl(QStyle::CE_Splitter, opt1); if (_private) painter.drawControl(QStyle::CE_Splitter, opt2); } void mouseMoveEvent(QMouseEvent * event) { if (!_mouse && _private) { if (_orientation == Qt::Horizontal) { int hiPart = qFloor((qreal)height() * _private->realPercent(Qt::Vertical) + 0.5); if (event->pos().y() > (hiPart - _private->centerPartWidth() / 2) && event->pos().y() < (hiPart + _private->centerPartWidth() / 2)) setCursor(Qt::SizeAllCursor); else setCursor(Qt::SplitHCursor); } else { int hiPart = qFloor((qreal)width() * _private->realPercent(Qt::Horizontal) + 0.5); if (event->pos().x() > (hiPart - _private->centerPartWidth() / 2) && event->pos().x() < (hiPart + _private->centerPartWidth() / 2)) setCursor(Qt::SizeAllCursor); else setCursor(Qt::SplitVCursor); } } else if (_center) { if (cursor().shape() != Qt::SizeAllCursor) setCursor(Qt::SizeAllCursor); } else if (_orientation == Qt::Horizontal) { if (cursor().shape() != Qt::SplitHCursor) setCursor(Qt::SplitHCursor); } else if (cursor().shape() != Qt::SplitVCursor) setCursor(Qt::SplitVCursor); if (_mouse && (event->buttons() & Qt::LeftButton)) { QPoint pt = mapToParent(event->pos()); if (QuadSplitter* p = qobject_cast(parent())) p->d_ptr->splitterMove(this, pt - _mousePos, _center); } } void mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { _mouse = true; if (_private) { if (_orientation == Qt::Horizontal) { int hiPart = qFloor((qreal)height() * _private->realPercent(Qt::Vertical) + 0.5); if (event->pos().y() > (hiPart - _private->centerPartWidth() / 2) && event->pos().y() < (hiPart + _private->centerPartWidth() / 2)) { _center = true; setCursor(Qt::SizeAllCursor); } else setCursor(Qt::SplitHCursor); } else { int hiPart = qFloor((qreal)width() * _private->realPercent(Qt::Horizontal) + 0.5); if (event->pos().x() > (hiPart - _private->centerPartWidth() / 2) && event->pos().x() < (hiPart + _private->centerPartWidth() / 2)) { _center = true; setCursor(Qt::SizeAllCursor); } else setCursor(Qt::SplitVCursor); } } } _mousePos = mapToParent(event->pos()); if (QuadSplitter* p = qobject_cast(parent())) p->d_ptr->splitterMoveStart(this, _center); } void mouseReleaseEvent(QMouseEvent *event) { _mouse = false; _center = false; } private: bool _mouse; QPoint _mousePos; Qt::Orientation _orientation; qreal _percent; QPointer _private; bool _center; }; ////////////////////////////////////////////////////////////////////////// QuadSplitterPrivate::QuadSplitterPrivate(QuadSplitter* _q_ptr) : q_ptr(_q_ptr), _spacing(5), _splittersSpacing(5), _minimumWidgetSize(30), _centerPart(30) { Q_Q(QuadSplitter); _horizontalSplitter.reset(new AdvSplitter(q, this, Qt::Horizontal)); _verticalSplitter.reset(new AdvSplitter(q, this, Qt::Vertical)); arrange(); } int QuadSplitterPrivate::realSpacing() const { const Q_Q(QuadSplitter); return _spacing + q->frameWidth(); } int QuadSplitterPrivate::realWidth() const { const Q_Q(QuadSplitter); return q->width() - (realSpacing() * 2) - _splittersSpacing; } int QuadSplitterPrivate::realHeight() const { const Q_Q(QuadSplitter); return q->height() - (realSpacing() * 2) - _splittersSpacing; } void QuadSplitterPrivate::arrange() { Q_Q(QuadSplitter); QPair minMaxHorizontalSizes[2] = {QPair(0, QWIDGETSIZE_MAX), QPair(0, QWIDGETSIZE_MAX)}; QPair minMaxVerticalSizes[2] = {QPair(0, QWIDGETSIZE_MAX), QPair(0, QWIDGETSIZE_MAX)}; for (int r = 0; r < 2; ++r) { for (int c = 0; c < 2; ++c) { if (_grid[r][c] && _grid[r][c]->parent() == q) { minMaxHorizontalSizes[c] = QPair( qMax(_grid[r][c]->minimumWidth() < _minimumWidgetSize ? _minimumWidgetSize : _grid[r][c]->minimumWidth(), minMaxHorizontalSizes[c].first), qMin(_grid[r][c]->maximumWidth(), minMaxHorizontalSizes[c].second)); minMaxVerticalSizes[r] = QPair( qMax(_grid[r][c]->minimumHeight() < _minimumWidgetSize ? _minimumWidgetSize : _grid[r][c]->minimumHeight(), minMaxVerticalSizes[r].first), qMin(_grid[r][c]->maximumHeight(), minMaxVerticalSizes[r].second)); } else _grid[r][c] = nullptr; } } //columns int leftColumnWidth = qFloor((qreal)realWidth() * _horizontalSplitter->percent() + 0.5); if (leftColumnWidth < minMaxHorizontalSizes[0].first) leftColumnWidth = minMaxHorizontalSizes[0].first; if (leftColumnWidth > minMaxHorizontalSizes[0].second) leftColumnWidth = minMaxHorizontalSizes[0].second; int columnWidth = leftColumnWidth; int rightColumnWidth = realWidth() - leftColumnWidth; if (rightColumnWidth < minMaxHorizontalSizes[1].first) { rightColumnWidth = minMaxHorizontalSizes[1].first; columnWidth = realWidth() - rightColumnWidth; } if (rightColumnWidth > minMaxHorizontalSizes[1].second) { rightColumnWidth = minMaxHorizontalSizes[1].second; columnWidth = realWidth() - rightColumnWidth; } //rows int topColumnHeight = qFloor((qreal)realHeight() * _verticalSplitter->percent() + 0.5); if (topColumnHeight < minMaxVerticalSizes[0].first) topColumnHeight = minMaxVerticalSizes[0].first; if (topColumnHeight > minMaxVerticalSizes[0].second) topColumnHeight = minMaxVerticalSizes[0].second; int columnHeight = topColumnHeight; int bottomColumnHeight = realHeight() - topColumnHeight; if (bottomColumnHeight < minMaxVerticalSizes[1].first) { bottomColumnHeight = minMaxVerticalSizes[1].first; columnHeight = realHeight() - bottomColumnHeight; } if (bottomColumnHeight > minMaxVerticalSizes[1].second) { bottomColumnHeight = minMaxVerticalSizes[1].second; columnHeight = realHeight() - bottomColumnHeight; } _horizontalSplitter->setGeometry(realSpacing() + columnWidth, realSpacing(), _splittersSpacing, q->height() - realSpacing() * 2); _horizontalSplitter->raise(); _verticalSplitter->setGeometry(realSpacing(), realSpacing() + columnHeight, q->width() - realSpacing() * 2, _splittersSpacing); _verticalSplitter->raise(); if (_grid[0][0]) _grid[0][0]->setGeometry(realSpacing(), realSpacing(), columnWidth, columnHeight); if (_grid[0][1]) _grid[0][1]->setGeometry(realSpacing() + columnWidth + _splittersSpacing, realSpacing(), realWidth() - columnWidth, columnHeight); if (_grid[1][0]) _grid[1][0]->setGeometry(realSpacing(), realSpacing() + columnHeight + _splittersSpacing, columnWidth, realHeight() - columnHeight); if (_grid[1][1]) _grid[1][1]->setGeometry(realSpacing() + columnWidth + _splittersSpacing, realSpacing() + columnHeight + _splittersSpacing, realWidth() - columnWidth, realHeight() - columnHeight); } void QuadSplitterPrivate::addWidget(QWidget *widget, int row, int column) { Q_Q(QuadSplitter); if (row < 0 || column < 0 || row > 1 || column > 1) { qWarning("QGridLayout: Cannot add %s/%s to %s/%s at row %d column %d", widget->metaObject()->className(), widget->objectName().toLocal8Bit().data(), q->metaObject()->className(), q->objectName().toLocal8Bit().data(), row, column); return; } bool needShow = q->isVisible() && !(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide)); if (widget->parentWidget() != q) widget->setParent(q); if (needShow) widget->show(); _grid[row][column] = widget; arrange(); } void QuadSplitterPrivate::splitterMoveStart(AdvSplitter* splitter, bool center) { _splittersMovingPos = QPoint(_horizontalSplitter->x(), _verticalSplitter->y()); } void QuadSplitterPrivate::splitterMove(AdvSplitter* splitter, QPoint const& offset, bool center) { Q_Q(QuadSplitter); if (center) { qreal newX = _splittersMovingPos.x() + offset.x() - realSpacing(); qreal newY = _splittersMovingPos.y() + offset.y() - realSpacing(); _horizontalSplitter->setPercent(newX / (qreal)realWidth()); _verticalSplitter->setPercent(newY / (qreal)realHeight()); } else if (splitter == _horizontalSplitter.data()) { qreal newX = _splittersMovingPos.x() + offset.x() - realSpacing(); splitter->setPercent(newX / (qreal)realWidth()); } else { qreal newY = _splittersMovingPos.y() + offset.y() - realSpacing(); splitter->setPercent(newY / (qreal)realHeight()); } arrange(); } int QuadSplitterPrivate::splittersSpacing() const { return _splittersSpacing; } void QuadSplitterPrivate::setSplittersSpacing(int spacing) { _splittersSpacing = spacing; } qreal QuadSplitterPrivate::realPercent(Qt::Orientation orientation) const { if (orientation == Qt::Horizontal) { qreal newX = _horizontalSplitter->x() - realSpacing(); return newX / realWidth(); } qreal newY = _verticalSplitter->y() - realSpacing(); return newY / realHeight(); } int QuadSplitterPrivate::spacing() const { return _spacing; } void QuadSplitterPrivate::setSpacing(int spacing) { _spacing = spacing; } int QuadSplitterPrivate::centerPartWidth() const { return _centerPart; } void QuadSplitterPrivate::setCenterPartWidth(int value) { _centerPart = value; } int QuadSplitterPrivate::minimumWidgetSize() const { return _minimumWidgetSize; } void QuadSplitterPrivate::setMinimumWidgetSize(int value) { _minimumWidgetSize = value; } ////////////////////////////////////////////////////////////////////////// QuadSplitter::QuadSplitter(QWidget *parent) : QFrame(parent), d_ptr(new QuadSplitterPrivate(this)) { } QuadSplitter::~QuadSplitter() { delete d_ptr; } void QuadSplitter::addWidget(QWidget *widget, int row, int column) { Q_D(QuadSplitter); d->addWidget(widget, row, column); } void QuadSplitter::resizeEvent(QResizeEvent *event) { Q_D(QuadSplitter); d->arrange(); } int QuadSplitter::spacing() const { const Q_D(QuadSplitter); return d->spacing(); } void QuadSplitter::setSpacing(int spacing) { Q_D(QuadSplitter); d->setSpacing(spacing); } int QuadSplitter::splittersSpacing() const { const Q_D(QuadSplitter); return d->splittersSpacing(); } void QuadSplitter::setSplittersSpacing(int spacing) { Q_D(QuadSplitter); d->setSplittersSpacing(spacing); } int QuadSplitter::centerPartWidth() const { const Q_D(QuadSplitter); return d->centerPartWidth(); } void QuadSplitter::setCenterPartWidth(int value) { Q_D(QuadSplitter); d->setCenterPartWidth(value); } int QuadSplitter::minimumWidgetSize() const { const Q_D(QuadSplitter); return d->minimumWidgetSize(); } void QuadSplitter::setMinimumWidgetSize(int value) { Q_D(QuadSplitter); d->setMinimumWidgetSize(value); }
使用方法:(类似于QGridLayout)
QuadSplitter *pSplitter = new QuadSplitter(this); QStringList strList; strList << "#990099" << "#99FFFF" << "#CC0099" << "#CCFF99"; for (int i = 0; i < strList.count(); ++i) { QWidget *pWidget = new QWidget(this); pWidget->setStyleSheet(QString("background:%1").arg(strList.at(i))); pSplitter->addWidget(pWidget, i/2, i%2); }
http://blog.sina.com.cn/s/blog_a6fb6cc90102vkr2.html