Вернуться наверх
aco.ifmo.ru photonic
вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


Пример 4.
Пример рисования графика и полутонового изображения выборки при помощи QCustomPlot

4.2. Реализация функций в диалоге (файл dqtplotdialog.cpp)

///////////////////////////////////////////////////////////////////////////////////////////////////
// dqtplotdialog.cpp
// Пример диалогового окна для рисования графика и полутонового изображения выборки
// при помощи QCustomPlot (http://www.qcustomplot.com/index.php/introduction).
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
///////////////////////////////////////////////////////////////////////////////////////////////////

#include "dqtplotdialog.h"
#include "qcustomplot.h"
#include "sample_complex.h"

using namespace std;

//-------------------------------------------------------------------------------------------------
// конструктор
DQtPlotDialog::DQtPlotDialog(QWidget* parent, Qt::WindowFlags f)
    : QDialog(parent, f)
{
    m_ui.setupUi(this);

    // создаем связь между сигналом(событием) от объекта и функцией-обработкой события
    connect(m_ui.m_qPB_Draw, SIGNAL(clicked()), this, SLOT(onBtnDraw()));
}
//-------------------------------------------------------------------------------------------------
DQtPlotDialog::~DQtPlotDialog()
{
}
//-------------------------------------------------------------------------------------------------
// перехватывается событие Resize для диалогового окна
// (это нужно чтобы сохранить одинаковоые масштабы по осям Х и Y для полутонового отображения)
void DQtPlotDialog::resizeEvent(QResizeEvent *event)
{
    // на случай, если размер виджета не квадратный - задаем одинаковый масштаб по ося X и Y
    RescaleCustomPlot(m_ui.m_qW_Grayscale);
    // отрисовка
    m_ui.m_qW_Grayscale->replot();
}
//-------------------------------------------------------------------------------------------------
// отклик на нажатие кнопки "нарисовать"
void DQtPlotDialog::onBtnDraw()
{
    // сoздаем выборку и заполняем ее какими-то значениями
    int dim=1024; // размер выборки
    double dx=0.01; // шаг по x
    SampleComplex sample(dim);
    for(int i=0; i<dim; ++i)
    {
        double x = sample.CalcCoord(i, dx);
        double value_x=1.;
        if(x!=0)
            value_x=sin(PI*x)/(PI*x);

        for(int j=0; j<dim; ++j)
        {
            double y = sample.CalcCoord(j, dx);
            double value_y=1.;
            if(y!=0)
                value_y=sin(2*PI*y)/(2*PI*y);

            sample(i,j)=value_x*value_y;
        }
    }
    // рисуем график
    DrawGraph(sample, m_ui.m_qW_Graph, dx, "x", "Sinc(x)*Sinc(2*y)");
    // рисуем полутоновое отображение
    DrawGrayscale(sample, m_ui.m_qW_Grayscale, dx, "x", "y");
}
//-------------------------------------------------------------------------------------------------
// рисование графика центральных сечений по Х и Y для двумерного массива
// sample - массив комплексных чисел, отображается только вещественная часть
// qGraph - имя виджета, на котором рисовать
// параметры графика:
// dx - шаг по оси Х
// sXName, sYName - подписи к осям Х, Y
void DQtPlotDialog::DrawGraph(SampleComplex& sample, QCustomPlot *qGraph, double dx, QString sXName, QString sYName)
{
    // размер выборки
    int dim = sample.GetSize();

    // очищаем предыдущий нарисованный график
    qGraph->clearGraphs();
    // рисование легенды
    qGraph->legend->setVisible(true);

    // параметры графика сечения по Х
    qGraph->addGraph();
    qGraph->graph(0)->setPen(QPen(Qt::red));
    qGraph->graph(0)->setName("Section X");

    // параметры графика сечения по Y
    qGraph->addGraph();
    qGraph->graph(1)->setPen(QPen(Qt::blue));
    qGraph->graph(1)->setName("Section Y");

    // параметры оси Х
    qGraph->xAxis->setLabel(sXName);
    // максимальное и минимальное значение по оси
    double min = sample.CalcCoord(0, dx);
    double max = sample.CalcCoord(dim, dx);
    qGraph->xAxis->setRange(min, max);
    // можно задать цену деления по оси (можно оставить автоматическое опеределение)
    qGraph->xAxis->setAutoTickStep(false);
    qGraph->xAxis->setTickStep(1.);

    // задаем значения графиков
    min=max=0;
    for (int i = 0; i < sample.GetSize(); i++)
    {
        // координата точки по Х
        double x = sample.CalcCoord(i, dx);

        // значание в сечении по Х
        double value_x = sample(i, dim/2).real();
        qGraph->graph(0)->addData(x, value_x);

        // значание в сечении  по Y
        double value_y = sample(dim/2, i).real();
        qGraph->graph(1)->addData(x, value_y);

        // вычисляем min значение по оси Y
        if(min>value_x)
            min=value_x;
        if(min>value_y)
            min=value_y;
    }
    // параметры оси Y
    qGraph->yAxis->setLabel(sYName);
    // максимальное и минимальное значение по оси
    // (min вычислен, max предполагаем равным 1 (для интенсивности)
    qGraph->yAxis->setRange(min, 1.);
    // можно задать цену деления по оси (можно оставить автоматическое опеределение)
    qGraph->yAxis->setAutoTickStep(false);
    qGraph->yAxis->setTickStep(0.2);
    // нулевое значение по осям X и Y рисуем толстой линией
    QPen qAxisPen;
    qAxisPen.setWidth(2.);
    qGraph->xAxis->grid()->setZeroLinePen(qAxisPen);
    qGraph->yAxis->grid()->setZeroLinePen(qAxisPen);

    // рисуем сами графики
    qGraph->replot();
}
//-------------------------------------------------------------------------------------------------
// рисование полутонового отображения двумерного массива
// sample - массив комплексных чисел, отображается только вещественная часть
// qGraph - имя виджета, на котором рисовать
// параметры графика:
// dx - шаг по оси Х
// sXName, sYName - подписи к осям Х, Y
void DQtPlotDialog::DrawGrayscale(SampleComplex& sample, QCustomPlot *qGrayscale, double dx, QString sXName, QString sYName)
{
    // размер выборки
    int dim = sample.GetSize();

    // очищаем предыдущий нарисованный график
    qGrayscale->clearPlottables();
    // создаем карту уровней для отрисовки и задаем размерность
    QCPColorMap *qMap = new QCPColorMap(qGrayscale->xAxis, qGrayscale->yAxis);
    qMap->data()->setSize(dim, dim);

    // задаем максимальное и минимальное значение по осям
    double min = sample.CalcCoord(0, dx);
    double max = sample.CalcCoord(dim, dx);
    qMap->data()->setRange(QCPRange(min, max), QCPRange(min, max));
    // заполняем карту уровней значениями из массива sample
    for (int i = 0; i < sample.GetSize(); i++)
    {
        for (int j = 0; j < sample.GetSize(); j++)
        {
            qMap->data()->setCell(i, j, sample(i, j).real());
        }
    }
    // устанавливаем тип карты уровней - полутоновое отображение
    qMap->setGradient(QCPColorGradient::gpGrayscale);
    qMap->rescaleDataRange();

    // параметры оси X
    qGrayscale->xAxis->setLabel(sXName);
    // можно задать цену деления по оси (можно оставить автоматическое опеределение)
    qGrayscale->xAxis->setAutoTickStep(false);
    qGrayscale->xAxis->setTickStep(1);

    // параметры оси X
    qGrayscale->yAxis->setLabel(sYName);
    // можно задать цену деления по оси (можно оставить автоматическое опеределение)
    qGrayscale->yAxis->setAutoTickStep(false);
    qGrayscale->yAxis->setTickStep(1);

    // задаем заполненную карту уровней в QCustomPlot для отрисовки
    qGrayscale->addPlottable(qMap);
    // пересчитываем оси QCustomPlot под заданную карту уровней
    qGrayscale->rescaleAxes();
    // на случай, если размер виджета не квадратный - задаем одинаковый масштаб по осям X и Y
    RescaleCustomPlot(qGrayscale);
    // отрисовка
    qGrayscale->replot();
}
//-------------------------------------------------------------------------------------------------
// функция, задающая одинаковый масштаб по осям X и Y
// должна вызываться при первоначальной отрисовке и при масштабировании
void DQtPlotDialog::RescaleCustomPlot(QCustomPlot *qPlot)
{
    // определяем ширину и высоту области для отрисовки
    QSize r=qPlot->axisRect()->size();
    // если ширина больше высоты - меняем ось X
    if(r.width() > r.height())
    {
        qPlot->xAxis->setScaleRatio(qPlot->yAxis, 1.);
    }
    // если ширина меньше высоты - меняем ось X
    else if(r.width() < r.height())
    {
        qPlot->yAxis->setScaleRatio(qPlot->xAxis, 1.);
    }
}
//-------------------------------------------------------------------------------------------------