Вывод графиков в Qt 5 при помощи QCustomPlot

Одной из распространенных задач программирования является — построение графиков.
Вы конечно можете создать новый проект и используя библиотеки вроде freeglut набросать программку для отрисовки графика, но зачем заниматься изобретением велосипеда? Зачем рисовать оси самостоятельно, если это можно сделать одной строкой при помощи библиотеки, которая не особо то и раздуем ваш код.

Мой выбор для таких вещей — QCustomPlot. Это библиотека для Qt.

Прежде всего скачайте библиотеку с сайта QCustomPlot. Там всего два файла, которые нужно добавить к проекту.
Я просто создаю новый проект Qt и ложу их в папку с исходниками, после чего нажимаю правой клавишей по проекту — Добавить существующие файлы и выбираю qcustomplot.h и qcustomplot.cpp.

Теперь идем в настройки проекта (pro файл).
Тут нужно добавить

QT       += printsupport

А также, проконтролировать появились ли добавленные файлы в SOURCES и HEADERS.

Теперь переходим к редактированию формы.

Добавляем QWidget и нажав по нем правой клавишей мыши — Преобразовать в, преобразовываем в QCustomPlot

Можно собрать. Вы увидите оси координат.

Теперь давайте «нарисем» синусоиду.
Для этого заинклудте math.h, мы будем использовать число пи и функцию синуса из этой библиотеки.

Добавим в конструктор окна такой код:

    // генерируем данные
    QVector<double> x(360), y(360); // строить будем до 360 градусов
    for (int i=0; i<360; ++i)
    {
      x[i] = i;
      y[i] = 10*sin(x[i]*M_PI/180); // вычисляем синус
    }
    // создаем график и добавляем данные:
    ui->widget->addGraph();
    ui->widget->graph(0)->setData(x, y);
    // задаем имена осей координат
    ui->widget->xAxis->setLabel("x");
    ui->widget->yAxis->setLabel("y");
    // задаем размеры осей
    ui->widget->xAxis->setRange(0, 360);
    ui->widget->yAxis->setRange(-10, 10);
    ui->widget->replot();

После компиляции вы увидите результат:

Все классно! Да вот подписи на осях нам не сильно подходят. Я хочу чтобы шаг был 30 градусов.
В принципе для этого достаточно изменить вектор TickVector.

    QVector<double> Ticks;  // вектор с шагом в 30 градусов
    int i = 0;
    while(i<=360) {
        Ticks << i;
        i+=30;
    }
    ui->widget->xAxis->setAutoTicks(false); // выключаем автоматические отсчеты
    ui->widget->xAxis->setTickVector(Ticks);  // задаем созданный нами вектор

После этого вы увидите такие оси:

Но мы зайдем немного дальше и изменим градусы на доли числа Пи.
Для этого необходимо сформировать вектор TickVectorLabels из QString.
С этим массивом все не так просто. Пришлось заполнить его вручную.

    QVector<QString> Labels;
    Labels << "0" << QString::fromUtf8("π/6") << QString::fromUtf8("π/3")
           << QString::fromUtf8("π/2") << QString::fromUtf8("2π/3")
           << QString::fromUtf8("5π/6") << QString::fromUtf8("π")
           << QString::fromUtf8("7π/6") << QString::fromUtf8("4π/3")
           << QString::fromUtf8("3π/2") << QString::fromUtf8("5π/3")
           << QString::fromUtf8("11π/6") << QString::fromUtf8("2π");

    ui->widget->xAxis->setAutoTickLabels(false);
    ui->widget->xAxis->setTickVectorLabels(Labels);

Вот как это выглядит:

Как видите все получилось 😉 Успехов в построении графиков.

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий

  1. Fugla

    вместо "Для этого заинклудте math.h, мы будем использовать число пи и функцию синуса из этой библиотеки."

    можно включить в mainwindow.h строку:

    #include <qmath.h>

    Ответить
  2. Иван

    Пожалуйста расскажите подробно, для чего нужен replot()?

    Ответить
  3. lamazavr

    Этот метод полностью перерисовывает график. Буржуи пишут, что его надо вызывать каждый раз для отображения изменения в отрисовке осей или точек графика.

    Ответить
  4. Вася

    Есть вопрос, я примерно по вашему примеру нарисовал графики, но у меня не получается сделать масштабирование по разным осям, то есть, есть график который рисуется по кнопке PushButton, и есть radiobutton в зависимости от того стоит галочка в radiobutton или нет масштабирование идет либо по оси x либо по оси y. Минус в том что без перезапуска программы по кнопке PushButton масштабирование не меняется (с оси х на ось у). Можно ли решить эту проблему? Чтобы без перезапуска программы можно было масштабировать либо по оси х либо по оси у?

    Текст программы:
    #include "plot.h"

    #include "ui_plot.h"

    #include "math.h"

    #include <QMessageBox>
    int vs=1;

    Plot::Plot(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::Plot)

    {

    ui->setupUi(this);

    ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

    }
    Plot::~Plot()

    {

    delete ui;

    }
    void Plot::on_ScrollButton_pressed()

    {

    if(vs==0) vs=1;

    else vs=0;

    }
    void Plot::on_pushButton_clicked()

    {

    int a,b;

    a=ui->lineEdit->text().toInt();

    b=ui->lineEdit_2->text().toInt();

    if (a<(-360)||b>360)

    {if (a<(-360))

    {

    QMessageBox::information(this,"Warnig!", "Change left border>-360!",QMessageBox::Ok | QMessageBox::Escape );

    this->close();

    } else

    {QMessageBox::information(this,"Warnig!", "Change right border<360!",QMessageBox::Ok | QMessageBox::Escape );

    this->close();

    }

    }

    QVector<double> Ticks; // вектор с шагом в 30 градусов

    int i = -360;

    while(i<=360) {

    Ticks << i;

    i+=30;

    }

    int s=b-a+1;

    ui->widget->xAxis->setTickVector(Ticks); // задаем созданный нами вектор

    QVector<double> x(s), y(s); // строить будем до 360 градусов

    if (ui->List->currentText()=="Sin")

    { for (int i=0; i<s; i++)

    {

    x[i] = i-abs(a);

    y[i] = sin(x[i]*M_PI/180); // вычисляем синус

    }

    }

    else if (ui->List->currentText()=="Cos")

    {for (int i=0; i<s; i++)

    {

    x[i] = i-abs(a);

    y[i] = cos(x[i]*M_PI/180);

    }

    }

    else if (ui->List->currentText()=="Tg")

    {for (int i=0; i<(s); ++i)

    {

    x[i] = i-abs(a);

    y[i] = tan(x[i]*M_PI/180);

    }

    }

    else if (ui->List->currentText()=="Ctg")

    {for (int i=0; i<(s); ++i)

    {

    x[i] = i-abs(a);

    y[i] = 1/(tan(x[i]*M_PI/180));

    }

    }
    ui->widget->addGraph();// создаем график и добавляем данные:

    ui->widget->graph(0)->setData(x, y);

    ui->widget->xAxis->setLabel("x");// задаем имена осей координат

    ui->widget->yAxis->setLabel("y");

    ui->widget->xAxis->setRange(a, b);// задаем размеры осей

    ui->widget->yAxis->setRange(-1, 1);

    if(vs==0)

    {

    ui->widget->axisRect()->setRangeZoom(Qt::Vertical);

    }

    else

    {

    ui->widget->axisRect()->setRangeZoom(Qt::Horizontal);

    }

    ui->widget->replot();

    }

    Ответить
  5. lamazavr

    С перерисовкой думаю поможет метод rescale().

    А вот с ресайзом по конкретной оси не подскажу. Возможно поможет вот это: http://www.qcustomplot.com/documentation/classQCPAxis.html

    Ответить
  6. Лена

    Добрый день! У меня возникла необходимость в поле name для окон и графиков qCustomPlot использовать подстрочный и надстрочный текст ( индексы и степени). Не подскажете ли, как жто можно слелать?

    Ответить
    1. lamazavr

      Нет, таким не занимался, предположу что должна быть возможность запихать туда html

      Ответить
  7. Михаил

    Не могли бы вы конкретней описать куда вставлять // генерируем данные

    QVector<double> x(360), y(360); // строить будем до 360 градусов

    for (int i=0; i<360; ++i)

    {

    x[i] = i;

    y[i] = 10*sin(x[i]*M_PI/180); // вычисляем синус

    }

    // создаем график и добавляем данные:

    ui->widget->addGraph();

    ui->widget->graph(0)->setData(x, y);

    // задаем имена осей координат

    ui->widget->xAxis->setLabel("x");

    ui->widget->yAxis->setLabel("y");

    // задаем размеры осей

    ui->widget->xAxis->setRange(0, 360);

    ui->widget->yAxis->setRange(-10, 10);

    ui->widget->replot();

    и что из этого в конечном варианте выйдет?

    Ответить