/
MaximU
/
arraywidget
Обзор
Документация
Войти
/
MaximU
/
arraywidget
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
plotsetwidget.cpp
763 строки
27 KB
user
Упорядочены #include.
27 июл 2026, 14:34
27 июл 2026, 14:34
f2675ea
Код
Авторство
О чём код?
#include "plotsetwidget.h" #include <QMouseEvent> #include <QPainter> #include <QPushButton> #include <qwt_plot_canvas.h> #include <qwt_scale_div.h> #include <qwt_scale_widget.h> #include "customdrawqwtplotitem.h" #include "ui_plotsetwidget.h" const QColor MapableScaleColor = Qt::darkGreen; //!< Цвет оси с изменяемыми значениями. /*! * \brief Конструктор. */ PlotSetWidget::PlotSetWidget(QWidget *parent) : QWidget(parent), ui(new Ui::PlotSetWidgetClass) { ui->setupUi(this); connect(ui->buttonClose, &QPushButton::clicked, this, &PlotSetWidget::closeRequest); ui->widgetTitle->installEventFilter(this); ui->buttonResize->installEventFilter(this); ui->centralPlot->canvas()->installEventFilter(this); ui->leftPlot->canvas()->installEventFilter(this); ui->bottomPlot->canvas()->installEventFilter(this); setupCentralPlot(); setupLeftPlot(); setupBottomPlot(); } /*! * \brief Деструктор. */ PlotSetWidget::~PlotSetWidget() { delete ui; } /*! * \brief Фильтр событий. * \param obj Объект, для которого обрабатывается событие. * \param event Событие. * \return true, если событие обработано. */ bool PlotSetWidget::eventFilter(QObject *obj, QEvent *event) { if (obj == ui->widgetTitle) { if (dynamic_cast<QPaintEvent *>(event) != nullptr) { QPainter painter; painter.begin(ui->widgetTitle); QString text; switch (mValue.type()) { case QVariant::Int: text = mTitleFormat.arg(mValue.toInt()); break; default: text = mTitleFormat.arg("<not implemented>"); break; } painter.setPen(ui->widgetTitle->palette().color(QPalette::WindowText)); painter.setFont(ui->widgetTitle->font()); QRect rect = ui->widgetTitle->geometry(); rect.setRect(-rect.height(), 0, rect.height(), rect.width()); painter.rotate(-90); painter.drawText(rect, Qt::AlignCenter , text); painter.end(); return true; } } else { if (auto *mouseEvent = dynamic_cast<QMouseEvent *>(event)) { if (obj == ui->buttonResize) { static QPoint point; if (mouseEvent->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::LeftButton) { point = mouseEvent->globalPos(); } if (mouseEvent->type() == QEvent::MouseMove && mouseEvent->buttons().testFlag(Qt::LeftButton)) { QPoint pt = mouseEvent->globalPos(); int dx = mLeftPlotVisible ? pt.x() - point.x() : 0; int dy = mBottomPlotVisible ? pt.y() - point.y() : 0; ui->buttonResize->move(ui->buttonResize->x() + dx, ui->buttonResize->y() + dy); point = pt; } if (mouseEvent->type() == QEvent::MouseButtonRelease && mouseEvent->button() == Qt::LeftButton) { QRect clientRect = rect(); clientRect.adjust(ui->layoutWidget->width(), 0, 0, 0); float w = qMin(0.9f, qMax(0.1f, (float)(ui->buttonResize->x() + ui->buttonResize->width() / 2 - clientRect.left()) / clientRect.width())); float h = qMin(0.9f, qMax(0.1f, (float)(ui->buttonResize->y() + ui->buttonResize->height() / 2 - clientRect.top()) / clientRect.height())); if (!mLeftPlotVisible) { w = mDivision.width(); } if (!mBottomPlotVisible) { h = mDivision.height(); } setDivision(QSizeF(w, h)); // Если plotset не в контейнере, то emit divisionChanged(DefaultDivision) для него не сработает. Поэтому - принудительный вызов. emit divisionChanged(); } if (mouseEvent->type() == QEvent::MouseButtonDblClick && mouseEvent->button() == Qt::LeftButton) { setDivision(PlotSetDefaultDivision); // Если plotset не в контейнере, то emit divisionChanged(DefaultDivision) для него не сработает. Поэтому - принудительный вызов. emit divisionChanged(); } } if (obj == ui->centralPlot->canvas()) { QPointF pt = mouseEvent->pos(); if (!ui->centralPlot->itemList().isEmpty()) { QwtPlotItem *item = ui->centralPlot->itemList().first(); pt.setX(ui->centralPlot->invTransform(item->xAxis(), pt.x())); pt.setY(ui->centralPlot->invTransform(item->yAxis(), pt.y())); } switch (mouseEvent->type()) { case QEvent::MouseButtonPress: emit mousePress(this, PlotIDCentralPlot, pt, mouseEvent->buttons()); break; case QEvent::MouseMove: emit mouseMove(this, PlotIDCentralPlot, pt, mouseEvent->buttons()); break; case QEvent::MouseButtonRelease: emit mouseRelease(this, PlotIDCentralPlot, pt, mouseEvent->buttons()); break; default: break; } } if (obj == ui->leftPlot->canvas()) { QPointF pt = mouseEvent->pos(); if (!ui->leftPlot->itemList().isEmpty()) { QwtPlotItem *item = ui->leftPlot->itemList().first(); pt.setX(ui->leftPlot->invTransform(item->xAxis(), pt.x())); pt.setY(ui->leftPlot->invTransform(item->yAxis(), pt.y())); } switch (mouseEvent->type()) { case QEvent::MouseButtonPress: emit mousePress(this, PlotIDLeftPlot, pt, mouseEvent->buttons()); break; case QEvent::MouseMove: emit mouseMove(this, PlotIDLeftPlot, pt, mouseEvent->buttons()); break; case QEvent::MouseButtonRelease: emit mouseRelease(this, PlotIDLeftPlot, pt, mouseEvent->buttons()); break; default: break; } } if (obj == ui->bottomPlot->canvas()) { QPointF pt = mouseEvent->pos(); if (!ui->bottomPlot->itemList().isEmpty()) { QwtPlotItem *item = ui->bottomPlot->itemList().first(); pt.setX(ui->bottomPlot->invTransform(item->xAxis(), pt.x())); pt.setY(ui->bottomPlot->invTransform(item->yAxis(), pt.y())); } switch (mouseEvent->type()) { case QEvent::MouseButtonPress: emit mousePress(this, PlotIDBottomPlot, pt, mouseEvent->buttons()); break; case QEvent::MouseMove: emit mouseMove(this, PlotIDBottomPlot, pt, mouseEvent->buttons()); break; case QEvent::MouseButtonRelease: emit mouseRelease(this, PlotIDBottomPlot, pt, mouseEvent->buttons()); break; default: break; } } } } return QWidget::eventFilter(obj, event); } /*! * \brief Вызывается при изменении размеров виджета. Устанавливает размеры и положение графиков. * \param event Событие изменения размера. */ void PlotSetWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event) QRect clientRect = rect(); if (mTitleVisible) { clientRect.adjust(ui->layoutWidget->width(), 0, 0, 0); ui->layoutWidget->resize(ui->layoutWidget->width(), clientRect.height()); } //// Размещение графиков. QSizeF division = mDivision; if (!mLeftPlotVisible) { division.setWidth(0); } if (!mBottomPlotVisible) { division.setHeight(1); } QRect rectCentre; rectCentre.setLeft(clientRect.left() + clientRect.width() * division.width()); rectCentre.setTop(clientRect.top() + 0); rectCentre.setWidth(clientRect.width() * (1 - division.width())); rectCentre.setHeight(clientRect.height() * division.height()); ui->centralPlot->move(rectCentre.topLeft()); ui->centralPlot->setMinimumSize(rectCentre.size()); ui->centralPlot->setMaximumSize(rectCentre.size()); QRect rectLeft; rectLeft.setLeft(clientRect.left() + 0); rectLeft.setTop(clientRect.top() + 0); rectLeft.setWidth(clientRect.width() * division.width()); rectLeft.setHeight(clientRect.height() * division.height()); ui->leftPlot->move(rectLeft.topLeft()); ui->leftPlot->setMinimumSize(rectLeft.size()); ui->leftPlot->setMaximumSize(rectLeft.size()); ui->leftPlot->setVisible(mLeftPlotVisible); QRect rectTop; rectTop.setLeft(clientRect.left() + clientRect.width() * division.width()); rectTop.setTop(clientRect.top() + clientRect.height() * division.height()); rectTop.setWidth(clientRect.width() * (1 - division.width())); rectTop.setHeight(clientRect.height() * (1 - division.height())); ui->bottomPlot->move(rectTop.topLeft()); ui->bottomPlot->setMinimumSize(rectTop.size()); ui->bottomPlot->setMaximumSize(rectTop.size()); ui->bottomPlot->setVisible(mBottomPlotVisible); ui->buttonResize->move(clientRect.left() + clientRect.width() * division.width() - ui->buttonResize->width() / 2, clientRect.top() + clientRect.height() * division.height() - ui->buttonResize->height() / 2); ui->buttonResize->setVisible(mLeftPlotVisible || mBottomPlotVisible); } /*! * \brief Инициализирует центральный график. */ void PlotSetWidget::setupCentralPlot() { //// Установка видимости осей. ui->centralPlot->enableAxis(QwtPlot::yLeft, true); ui->centralPlot->enableAxis(QwtPlot::yRight, true); ui->centralPlot->enableAxis(QwtPlot::xBottom, true); ui->centralPlot->enableAxis(QwtPlot::xTop, true); //// Установка вертикального текста для осей. ui->centralPlot->setAxisLabelRotation(QwtPlot::yLeft, -90); ui->centralPlot->setAxisLabelRotation(QwtPlot::yRight, -90); //// Установка шрифта для осей. QFont font = QApplication::font(); font.setPointSize(7); ui->centralPlot->setAxisFont(QwtPlot::yLeft, font); ui->centralPlot->setAxisFont(QwtPlot::xTop, font); ui->centralPlot->setAxisFont(QwtPlot::yRight, font); ui->centralPlot->setAxisFont(QwtPlot::xBottom, font); //// Установка цвета оси. QPalette palette; palette = ui->centralPlot->axisWidget(QwtPlot::yLeft)->palette(); palette.setColor(QPalette::Text, MapableScaleColor); palette.setColor(QPalette::Foreground, MapableScaleColor); ui->centralPlot->axisWidget(QwtPlot::yLeft)->setPalette(palette); palette = ui->centralPlot->axisWidget(QwtPlot::xBottom)->palette(); palette.setColor(QPalette::Text, MapableScaleColor); palette.setColor(QPalette::Foreground, MapableScaleColor); ui->centralPlot->axisWidget(QwtPlot::xBottom)->setPalette(palette); //// Установка шкал графика по умолчанию. ui->centralPlot->setAxisScale(QwtPlot::yLeft, -0.5, 0.5, 1); ui->centralPlot->setAxisScale(QwtPlot::yRight, -0.5, 0.5, 1); ui->centralPlot->setAxisScale(QwtPlot::xBottom, -0.5, 0.5, 1); ui->centralPlot->setAxisScale(QwtPlot::xTop, -0.5, 0.5, 1); //// Пикер отображения координат на графике. ui->centralPlot->initDisplayCoordinatePicker(QwtPlot::xTop, QwtPlot::yRight, QwtPlotExt::PickerDragPointMachine); connect(ui->centralPlot, &QwtPlotExt::getTrackerText, this, &PlotSetWidget::slot_pickerPlotCentral_getTrackerText); //// Добавление отрисовки переднего плана графика. CustomDrawQwtPlotItem *foregroundItem = new CustomDrawQwtPlotItem(); foregroundItem->setYAxis(QwtPlot::yRight); foregroundItem->setXAxis(QwtPlot::xTop); connect(foregroundItem, &CustomDrawQwtPlotItem::drawContent, this, &PlotSetWidget::centralPlotDrawForeground); foregroundItem->attach(ui->centralPlot); } /*! * \brief Инициализирует левый график. */ void PlotSetWidget::setupLeftPlot() { //// Установка видимости осей. ui->leftPlot->enableAxis(QwtPlot::yLeft, true); ui->leftPlot->enableAxis(QwtPlot::yRight, false); ui->leftPlot->enableAxis(QwtPlot::xBottom, true); ui->leftPlot->enableAxis(QwtPlot::xTop, true); //// Установка вертикального текста для осей. ui->leftPlot->setAxisLabelRotation(QwtPlot::yLeft, -90); ui->leftPlot->setAxisLabelRotation(QwtPlot::yRight, -90); //// Установка шрифта для осей. QFont font = QApplication::font(); font.setPointSize(7); ui->leftPlot->setAxisFont(QwtPlot::yLeft, font); ui->leftPlot->setAxisFont(QwtPlot::xTop, font); ui->leftPlot->setAxisFont(QwtPlot::yRight, font); ui->leftPlot->setAxisFont(QwtPlot::xBottom, font); //// Установка шкал графика по умолчанию. ui->leftPlot->setAxisScale(QwtPlot::yLeft, -0.5, 0.5, 1); ui->leftPlot->setAxisScale(QwtPlot::yRight, -0.5, 0.5, 1); ui->leftPlot->setAxisScale(QwtPlot::xBottom, -0.5, 0.5, 1); ui->leftPlot->setAxisScale(QwtPlot::xTop, -0.5, 0.5, 1); //// Пикер отображения координат на графике. ui->leftPlot->initDisplayCoordinatePicker(QwtPlot::xBottom, QwtPlot::yLeft, QwtPlotExt::PickerDragPointMachine); connect(ui->leftPlot, &QwtPlotExt::getTrackerText, this, &PlotSetWidget::slot_pickerPlotLeft_getTrackerText); //// Инициализация координатной сетки графиков. ui->leftPlot->initGrid(); //// Добавление отрисовки заднего плана графика. CustomDrawQwtPlotItem *backgroundItem = new CustomDrawQwtPlotItem(); backgroundItem->setYAxis(QwtPlot::yRight); backgroundItem->setXAxis(QwtPlot::xBottom); connect(backgroundItem, &CustomDrawQwtPlotItem::drawContent, this, &PlotSetWidget::leftPlotDrawBackground); backgroundItem->attach(ui->leftPlot); //// Добавление отрисовки переднего плана графика. CustomDrawQwtPlotItem *foregroundItem = new CustomDrawQwtPlotItem(); foregroundItem->setYAxis(QwtPlot::yRight); foregroundItem->setXAxis(QwtPlot::xBottom); connect(foregroundItem, &CustomDrawQwtPlotItem::drawContent, this, &PlotSetWidget::leftPlotDrawForeground); foregroundItem->attach(ui->leftPlot); } /*! * \brief Инициализирует нижний график. */ void PlotSetWidget::setupBottomPlot() { //// Установка видимости осей. ui->bottomPlot->enableAxis(QwtPlot::yLeft, true); ui->bottomPlot->enableAxis(QwtPlot::yRight, true); ui->bottomPlot->enableAxis(QwtPlot::xBottom, true); ui->bottomPlot->enableAxis(QwtPlot::xTop, false); //// Установка вертикального текста для осей. ui->bottomPlot->setAxisLabelRotation(QwtPlot::yLeft, -90); ui->bottomPlot->setAxisLabelRotation(QwtPlot::yRight, -90); //// Установка шрифта для осей. QFont font = QApplication::font(); font.setPointSize(7); ui->bottomPlot->setAxisFont(QwtPlot::yLeft, font); ui->bottomPlot->setAxisFont(QwtPlot::xTop, font); ui->bottomPlot->setAxisFont(QwtPlot::yRight, font); ui->bottomPlot->setAxisFont(QwtPlot::xBottom, font); //// Установка шкал графика по умолчанию. ui->bottomPlot->setAxisScale(QwtPlot::yLeft, -0.5, 0.5, 1); ui->bottomPlot->setAxisScale(QwtPlot::yRight, -0.5, 0.5, 1); ui->bottomPlot->setAxisScale(QwtPlot::xBottom, -0.5, 0.5, 1); ui->bottomPlot->setAxisScale(QwtPlot::xTop, -0.5, 0.5, 1); //// Пикер отображения координат на графике. ui->bottomPlot->initDisplayCoordinatePicker(QwtPlot::xBottom, QwtPlot::yLeft, QwtPlotExt::PickerDragPointMachine); connect(ui->bottomPlot, &QwtPlotExt::getTrackerText, this, &PlotSetWidget::slot_pickerPlotBottom_getTrackerText); //// Инициализация координатной сетки графиков. ui->bottomPlot->initGrid(); //// Добавление отрисовки заднего плана графика. CustomDrawQwtPlotItem *backgroundItem = new CustomDrawQwtPlotItem(); backgroundItem->setYAxis(QwtPlot::yRight); backgroundItem->setXAxis(QwtPlot::xBottom); connect(backgroundItem, &CustomDrawQwtPlotItem::drawContent, this, &PlotSetWidget::bottomPlotDrawBackground); backgroundItem->attach(ui->bottomPlot); //// Добавление отрисовки переднего плана графика. CustomDrawQwtPlotItem *foregroundItem = new CustomDrawQwtPlotItem(); foregroundItem->setYAxis(QwtPlot::yRight); foregroundItem->setXAxis(QwtPlot::xBottom); connect(foregroundItem, &CustomDrawQwtPlotItem::drawContent, this, &PlotSetWidget::bottomPlotDrawForeground); foregroundItem->attach(ui->bottomPlot); } /*! * \brief Устанавливает специальное значение. * \param value Новое специальное значение. */ void PlotSetWidget::setValue(QVariant value) { if (mValue != value) { mValue = value; ui->widgetTitle->update(); updateView(); } } /*! * \brief Возвращает текущее специальное значение. * \return Специальное значение. */ QVariant PlotSetWidget::value() const { return mValue; } /*! * \brief Устанавливает формат текста заголовка. * \param format Формат текста заголовка. */ void PlotSetWidget::setTitleFormat(const QString &format) { mTitleFormat = format; ui->widgetTitle->update(); } /*! * \brief Устанавливает пропорции ширины и высоты графиков. * \param division Новые пропорции ширины и высоты графиков. */ void PlotSetWidget::setDivision(const QSizeF &division) { mDivision = division; resizeEvent(nullptr); } /*! * \brief Возвращает пропорции ширины и высоты графиков. * \return Пропорции ширины и высоты графиков. */ QSizeF PlotSetWidget::division() const { return mDivision; } /*! * \brief Устанавливает признак отображения заголовка. * \param visible Признак отображения заголовка. */ void PlotSetWidget::setTitleVisible(bool visible) { mTitleVisible = visible; ui->layoutWidget->setVisible(mTitleVisible); resizeEvent(nullptr); } /*! * \brief Устанавливает признак отображения левого графика набора. * \param visible Признак отображения левого графика набора. */ void PlotSetWidget::setLeftPlotVisible(bool visible) { mLeftPlotVisible = visible; resizeEvent(nullptr); } /*! * \brief Возвращает признак отображения левого графика набора. * \return Признак отображения левого графика набора. */ bool PlotSetWidget::isLeftPlotVisible() const { return mLeftPlotVisible; } /*! * \brief Устанавливает признак отображения верхнего графика набора. * \param visible Признак отображения верхнего графика набора. */ void PlotSetWidget::setTopPlotVisible(bool visible) { mBottomPlotVisible = visible; resizeEvent(nullptr); } /*! * \brief Возвращает признак отображения верхнего графика набора. * \return Признак отображения верхнего графика набора. */ bool PlotSetWidget::isTopPlotVisible() const { return mBottomPlotVisible; } /*! * \brief Возвращает указатель на график из набора, соответствующий идентификатору. * \param plotId Идентификатор запрашиваемого объекта plot. * \return Объект plot, соответствующий plotId. */ QwtPlotExt *PlotSetWidget::plot(PlotIDs plotId) { switch (plotId) { case PlotIDCentralPlot: return ui->centralPlot; case PlotIDLeftPlot: return ui->leftPlot; case PlotIDBottomPlot: return ui->bottomPlot; } return nullptr; } /*! * \brief Возвращает объект отображения, соответствующий идентификатору. * \param plotId Идентификатор запрашиваемого объекта plot. * \param key Ассоциативный ключ запрашиваемого объекта отображения. * \return Объект отображения, соответствующий ключу. */ QwtPlotItem *PlotSetWidget::plotItem(PlotIDs plotId, void *key) { switch (plotId) { case PlotIDCentralPlot: return ui->centralPlot->itemList().first(); case PlotIDLeftPlot: return mItemMapLeft[key]; case PlotIDBottomPlot: return mItemMapTop[key]; } return nullptr; } /*! * \brief Добавляет элемент отображения в соответствующий объект plot. * \param plotItem Добавляемый элемент отображения. * \param plotId Идентификатор объекта plot. * \param key Ассоциативный ключ запрашиваемого объекта отображения. * \return Признак успешного выполнения. */ bool PlotSetWidget::addPlotItem(QwtPlotItem *plotItem, PlotIDs plotId, void *key) { switch (plotId) { case PlotIDCentralPlot: plotItem->attach(ui->centralPlot); return true; case PlotIDLeftPlot: if (!mItemMapLeft.contains(key)) { mItemMapLeft.insert(key, plotItem); plotItem->attach(ui->leftPlot); return true; } break; case PlotIDBottomPlot: if (!mItemMapTop.contains(key)) { mItemMapTop.insert(key, plotItem); plotItem->attach(ui->bottomPlot); return true; } break; } return false; } /*! * \brief Устанавливает формат текста заголовка, пропорции высоты и ширины графиков и их видимость и вызывает сигнал initRequest, в котором производится инициализация состава отображения. * \param format Формат текста заголовка. * \param division Пропорции высоты и ширины графиков. * \param leftPlotVisible Видимость левого графика. * \param topPlotVisible Видимость верхнего графика. */ void PlotSetWidget::init(const QString &format, QSizeF division, bool leftPlotVisible, bool bottomPlotVisible) { mTitleFormat = format; mDivision = division; mLeftPlotVisible = leftPlotVisible; mBottomPlotVisible = bottomPlotVisible; QwtPlotItem *item1 = ui->leftPlot->itemList().last(); QwtPlotItem *item2 = ui->bottomPlot->itemList().last(); emit initRequest(); item1->setZ(ui->leftPlot->itemList().last()->z() + 1); item2->setZ(ui->bottomPlot->itemList().last()->z() + 1); resizeEvent(nullptr); } /*! * \brief Вызывает сигнал updateRequest, в котором производится обновление состава отображения. */ void PlotSetWidget::updateView() { emit updateRequest(); } /*! * \brief Устанавливает оси графика. * \param id Идентификатор графика. * \param rect1 Интервалы осей left-top. * \param rect2 Интервалы осей right-bottom. */ void PlotSetWidget::setZoomBase(PlotIDs id, const QRectF &rect1, const QRectF &rect2) { plot(id)->setZoomBase(rect1, rect2); } /*! * \brief Устанавливает текст координат положения пикера на центральном графике. * \param point Координаты положения пикера на графике. * \param text Координаты в текстовом формате. */ void PlotSetWidget::slot_pickerPlotCentral_getTrackerText(const QPointF &point, QwtText &text) { text.setText(QString("%1, %2").arg(int(point.y() + 0.5)).arg(int(point.x() + 0.5))); } /*! * \brief Устанавливает текст координат положения пикера на левом графике. * \param point Координаты положения пикера на графике. * \param text Координаты в текстовом формате. */ void PlotSetWidget::slot_pickerPlotLeft_getTrackerText(const QPointF &point, QwtText &text) { text.setText(QString("%1, %2").arg(int(point.y() + 0.5)).arg(point.x())); } /*! * \brief Устанавливает текст координат положения пикера на нижнем графике. * \param point Координаты положения пикера на графике. * \param text Координаты в текстовом формате. */ void PlotSetWidget::slot_pickerPlotBottom_getTrackerText(const QPointF &point, QwtText &text) { text.setText(QString("%1, %2").arg(int(point.x() + 0.5)).arg(point.y())); } /*! * \brief Сбрасывает стек масштабирования. */ void PlotSetWidget::resetZoomStack() { ui->centralPlot->resetZoomStack(); ui->leftPlot->resetZoomStack(); ui->bottomPlot->resetZoomStack(); }