バージョン間のイベント処理の比較
QCPLayoutElementの解説に入る前に、バージョン1.3.2と2.0.0のイベント処理の違いを比較します。バージョン1.3.2では、マウスクリックイベントはまずQCPLayoutElementに渡され、以下のような処理が行われていました:
void QCustomPlot::mousePressEvent(QMouseEvent *event)
{
emit mousePress(event);
mMousePressPos = event->pos();
mMouseEventElement = layoutElementAt(event->pos());
if (mMouseEventElement)
mMouseEventElement->mousePressEvent(event);
QWidget::mousePressEvent(event);
}
一方、バージョン2.0.0では、イベント処理が大幅に変更されました。マウスイベントはQCPLayoutElementだけでなく、QCPLayerableを継承するすべての要素で処理可能になりました:
void QCustomPlot::mousePressEvent(QMouseEvent *event)
{
emit mousePress(event);
mMouseHasMoved = false;
mMousePressPos = event->pos();
if (mSelectionRect && mSelectionRectMode != QCP::srmNone)
{
if (mSelectionRectMode != QCP::srmZoom ||
qobject_cast(axisRectAt(mMousePressPos)))
mSelectionRect->startSelection(event);
}
else
{
QList<QVariant> details;
QList candidates = layerableListAt(mMousePressPos, false, &details);
for (int i = 0; i < candidates.size(); ++i)
{
event->accept();
candidates.at(i)->mousePressEvent(event, details.at(i));
if (event->isAccepted())
{
mMouseEventLayerable = candidates.at(i);
mMouseEventLayerableDetails = details.at(i);
break;
}
}
}
event->accept();
}
レイアウト要素の階層構造
QCPLayoutElementから派生する主要なクラスには以下があります:
- QCPAbstractLegendItem:凡例項目
- QCPAxisRect:座標軸領域
- QCPColorScale:カラースケール
- QCPLayout:レイアウトの抽象基底クラス
- QCPTextElement:テキスト要素
これらの要素はすべてQCPLayerableを継承しており、マウスイベントを処理することができます。
QCPLayoutとその派生クラス
QCPLayoutには2つの直接派生クラスがあります:QCPLayoutGridとQCPLayoutInsetです。
QCPLayoutGridの更新プロセス
QCPLayoutGridの更新は3つのフェーズで行われます:
mPlotLayout->update(QCPLayoutElement::upPreparation);
mPlotLayout->update(QCPLayoutElement::upMargins);
mPlotLayout->update(QCPLayoutElement::upLayout);
各フェーズの処理内容:
void QCPLayout::update(UpdatePhase phase)
{
QCPLayoutElement::update(phase);
if (phase == upLayout)
updateLayout();
const int elementCount = getItemCount();
for (int index = 0; index < elementCount; ++index)
{
if (QCPLayoutElement *child = getElement(index))
child->update(phase);
}
}
マージンの計算はQCPLayoutElement::updateで行われます:
void QCPLayoutElement::update(UpdatePhase phase)
{
if (phase == upMargins)
{
if (mAutoMargins != QCP::msNone)
{
QMargins newMargins = mMargins;
QList sides = {QCP::msLeft, QCP::msRight, QCP::msTop, QCP::msBottom};
for (auto side : sides)
{
if (mAutoMargins.testFlag(side))
{
if (mMarginGroups.contains(side))
QCP::setMarginValue(newMargins, side,
mMarginGroups[side]->commonMargin(side));
else
QCP::setMarginValue(newMargins, side,
calculateAutoMargin(side));
if (QCP::getMarginValue(newMargins, side) <
QCP::getMarginValue(mMinimumMargins, side))
QCP::setMarginValue(newMargins, side,
QCP::getMarginValue(mMinimumMargins, side));
}
}
setMargins(newMargins);
}
}
}
複数座標軸の実装例
以下は、新しい座標軸領域を動的に追加する例です:
QCPBars* CustomPlotWidget::addVolumeAxis(bool showVolume)
{
PrivateData* data = m_private;
if (showVolume)
{
if (!data->volumeEnabled)
{
data->volumeEnabled = true;
QCPAxisRect* volumeAxis = new QCPAxisRect(m_customPlot);
// X軸の同期
connect(m_customPlot->xAxis,
static_cast(&QCPAxis::rangeChanged),
volumeAxis->axis(QCPAxis::atBottom),
static_cast(&QCPAxis::setRange));
connect(volumeAxis->axis(QCPAxis::atBottom),
static_cast(&QCPAxis::rangeChanged),
m_customPlot->xAxis,
static_cast(&QCPAxis::setRange));
volumeAxis->setMaximumSize(QSize(QWIDGETSIZE_MAX, 100));
volumeAxis->axis(QCPAxis::atBottom)->setLayer("axes");
volumeAxis->axis(QCPAxis::atBottom)->grid()->setLayer("grid");
volumeAxis->setAutoMargins(QCP::msLeft | QCP::msRight | QCP::msBottom);
volumeAxis->setMargins(QMargins(0, 0, 0, 0));
m_customPlot->plotLayout()->addElement(1, 0, volumeAxis);
volumeAxis->setMarginGroup(QCP::msLeft | QCP::msRight, data->marginGroup);
QCPBars* volumeBars = createBars(volumeAxis->axis(QCPAxis::atBottom),
volumeAxis->axis(QCPAxis::atLeft));
return volumeBars;
}
}
else
{
data->volumeEnabled = false;
m_customPlot->plotLayout()->remove(m_customPlot->plotLayout()->element(1, 0));
}
m_customPlot->plotLayout()->simplify();
return nullptr;
}
QCPLayoutInsetの役割
QCPLayoutInsetは1次元の配列として機能し、主にQCPAxisRect内のouterRectの管理に使用されます。以下のように2つの矩形情報を保持します:
QRect innerRect() const { return mRect; }
QRect outerRect() const { return mOuterRect; }
このクラスにより、座標軸領域の外側に追加の要素を配置することができます。