БлогNot. Qt: строим графики без QWT

Qt: строим графики без QWT

Базовый комплект библиотек QT не содержит удобных средств для построения графиков и диаграмм, похожих на Chart из Studio. Тем не менее, для решения задачи вывода графика в QT можно использовать, как минимум, 3 подхода:

1. Написать всё самому, это не так сложно, как кажется. Если сделать класс-потомок QWidget, включить в его заголовочный файл widget.h прототип обработчика события paintEvent

protected:
    void paintEvent(QPaintEvent *);

и вверху widget.h заинклудить включаемый файл QPainter,

#include <QWidget>
#include <QPainter>

то в коде файла widget.cpp можно добавить свой обработчик события перерисовки окна виджета, например, такой:

void Widget::paintEvent(QPaintEvent *) {
    QPainter p(this); // Создаём новый объект рисовальщика
    p.setPen(QPen(Qt::red,1,Qt::SolidLine)); // Настройки рисования
    p.drawLine(0,0,width(),height()); // Рисование линии
}

Здесь мы просто рисуем красную линию из левого верхнего угла окна в правый нижний. Добавьте сотню-другую строк с расчётами - и получите график :)

 Скачать архив .zip с этим примером (проект QT5) (2 Кб)

2. Можно скачать и настроить QWT - дополнительную библиотеку для графического представления числовых данных. Тогда задачу можно решить на её основе, правда, опять повозитесь с путями :)

3. Наконец, мы можем использовать альтернативные решения, которые не потребуют работы с настройками qwt или кропотливого "ручного" программирования классов. Поможет нам маленький open-source класс QCustomPlot. Скачав исходники по ссылке (QCustomPlot-source.tar.gz), разверните архив, из него нужны всего 2 файла: qcustomplot.h и qcustomplot.cpp. Проще всего создать новый проект-виджет на основе базового класса QMainWindow и добавить туда оба файла (проверьте, что они появились в списках "Заголовочные" и "Исходники" диспетчера проекта).

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

QT       += printsupport

Теперь самое интересное. Если мы создали приложение на основе базового класса QMainWindow, у нас уже есть главное окно приложения (файлы mainwindow.*) со встроенным меню, кнопочным тулбаром, строкой статуса и основной областью окна, представляющей собой объект класса QWidget с именем centralWidget. Всё это можно увидеть в списке объектов и классов, переключившись в режим дизайна формы (откройте список Формы, файл mainwindow.ui). Поэтому мы не будем делать новый виджет, в котором отобразится график, а используем для этого centralWidget. Щёлкнув правой кнопкой мыши на объекте centralWidget, поменяем ему тип на QCustomPlot:

Смена класса виджета в QT5, шаг 1
Смена класса виджета в QT5, шаг 1
Смена класса виджета в QT5, шаг 2
Смена класса виджета в QT5, шаг 2

На втором рисунке не забудьте нажать "Добавить" и "Преобразовать", проверьте в списке с первого рисунка, что класс виждета действительно изменился. Приложение можно собрать, при запуске мы увидим сетку и оси координат будущего графика.

Теперь нарисуем что-нибудь реальное, например, синусоиду.

В mainwindow.h добавим в описание класса меню и пункт для него, а также слот для обработки события "щелчок по пункту меню":

private:
    QMenu * fileMenu;
    QAction * newAct;
private slots:
     void newFile(void);

В mainwindow.cpp подключим файл math.h, чтобы получить доступ к функции синуса, а также в конструкторе окна создадим элементы интерфейса и подключим слот, начало файла получится таким:

#include <math.h>
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    newAct = new QAction(tr("&Новый"), this);
    fileMenu = this->menuBar()->addMenu(tr("&Файл"));
    fileMenu->addAction(newAct);
    connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
}

Всё остальное будем делать по выбору пункта меню Файл - Новый, то есть, внутри добавленной в класс функции:

void MainWindow::newFile() {
 //...вот здесь
}

Всю эту работу по созданию пункта меню можно было сделать и в режиме дизайна: откройте редактор действий и правой кнопкой мыши выберите "Перейти к слоту..." - triggered(), в файле mainwindow.cpp создастся соответствующий обработчик события:
void MainWindow::on_action_triggered()
{
}
в котором и пишется дальнейший код.

Код ниже показывает, как легко составить таблицу данных и добавить её на график:

//1. Подготовить данные x,y:
     const int size = 360;
     QVector <double> x(size),y(size);
     for (int i=0; i<size; i++) {
      x[i] = i;
      y[i] = sin(qDegreesToRadians(x[i]));
     }
//2. Создать график и добавить данные:
     ui->centralWidget->addGraph();
     ui->centralWidget->graph(0)->setData(x,y);
//3. Настроить оси и подписи:
     ui->centralWidget->xAxis->setLabel("Ось x");
     ui->centralWidget->xAxis->setRange(0,size); //1
     ui->centralWidget->yAxis->setLabel("f(x)");
     ui->centralWidget->yAxis->setRange(-1,1);
//4. Показать всё это:
     ui->centralWidget->replot();

Любой из шагов можно улучшить, например, сделать подписи оси x с нужным нам шагом (код после оператора //1):

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

или даже в долях числа Пи (предполагается, что кодировка проекта - Юникод):

//Оси в долях числа Пи
    QVector <QString> Labels;
    for (int i=0; i<=size; i+=size/9) Labels << ClipFraction(i,size/2) + QChar(960);
    ui->centralWidget->xAxis->setAutoTickLabels(false);
    ui->centralWidget->xAxis->setTickVectorLabels(Labels);

Здесь служебная функция ClipFraction занимается сокращением дроби и конвертированием её в QString:

int GetNod(int ch,int zn) { //Найти НОД
 return (ch ? GetNod(zn%ch,ch) : zn);
 }

QString ClipFraction (int ch,int zn) { //Сократить, если есть НОД>1
 int nod = GetNod(ch,zn);
 if (nod>1) { ch/=nod; zn/=nod; }
 QString result;
 if (ch==0) result.setNum(0);
 else if (ch==zn) result.setNum(1);
 else if (zn==1) result.setNum(ch);
 else result = QString("%1/%2").arg(ch).arg(zn);
 return result;
}

Вот что получилось, при этом график нормально перерисовывается при масштабировании окна (так как класс контролирует обработку события перерисовки):

Окно виджета построения графика на QT без QWT
Окно виджета построения графика на QT без QWT

 Скачать этот проект QT5 в архиве .zip (187 Кб)

15.05.2014, 23:14 [37411 просмотров]


теги: qt c++ графика программирование

К этой статье пока нет комментариев, Ваш будет первым