Pull to refresh

Qwt и Qt Creator. Часть 3: график как элемент Designer Form

Reading time 6 min
Views 24K


В примерах использованы Qt Creator 3.0.0 (MinGW) и Qwt-6.1.0.

Для понимания этой статьи читатель должен иметь начальный опыт разработки windows-приложений в среде Qt Creator, понимать концепцию «сигнал-слот». Также рекомендуется познакомиться с частью №1 и №2 цикла моих статей про Qwt:


habrahabr.ru/post/211204
habrahabr.ru/post/211867

Qwt – графическая библиотека, позволяющая значительно упростить процесс визуализации данных в программе. Упрощение заключается в следующем: нет необходимости вручную прописывать элементы отображения, такие как шкалы координат, сетки, кривые данных и проч. Следует лишь задавать параметры этих элементов.

В предыдущих частях цикла статей элементы управления графиком добавлялись ручным кодированием. Думаю, большинство программистов предпочли бы пользоваться средствами Qt Creator.

В части №3 мы сделаем следующее:

• добавим виджет для отображения графика в Designer Form, что позволит использовать элементы управления Qt Creator;
• построим демонстрационную кривую, реализуем базовые удобства работы с графиком: возможность перемещения по полю графика, его приближение/удаление, отобразим координаты курсора при его перемещении;
• отобразим координат клика в строке состояния Designer Form;
• переместим кривую вдоль оси х, используя стандартные элементы управления из Qt Creator.

Содержание этой статьи в некоторой степени дублирует содержание части №2. Это сделано специально, так как хотелось сделать статьи независимыми друг от друга.

Добавим виджет для отображения графика в Designer Form
1. Создаем проект с Mainwindow. Напомню обязательную процедуру для использования Qwt: добавить строчку «CONFIG += qwt» в файл .pro проекта и запустить qmake через контекстное меню (правой кнопкой мыши на корневую папку проекта).

2. Открываем редактор формы и находим виджет «Widget». Размещаем его на форме.


3. Изменяем имя добавленного «Widget» на «Qwt_Widget».
4. Правой кнопкой щелкаем на «Qwt_Widget» и выбираем «Promote to…»


5. В поле Promoted class name печатаем «QwtPlot». В поле Header file меняем «qwtplot.h» на «qwt_plot.h». Нажимаем Add и закрываем окно.




Построим демонстрационную кривую, реализуем базовые удобства работы с графиком
Ниже приведен код, во многом аналогичный опубликованному в части №2. Код немного улучшен, удалены фрагменты, отвечающие за «ручное» добавление элементов управления.
Обратите особое внимание на следующее. Раньше мы создавали приватную переменную QwtPlot *d_plot, а затем использовали такую конструкцию, как, например:
d_plot->setTitle( "Qwt demonstration" ); 


Теперь для настроек свойств графика мы будем использовать следующую конструкцию:
ui->Qwt_Widget->setTitle( "Qwt demonstration" );


Причем, если в коде отсутствуют такие конструкции, то «Qwt_Widget» является пустым. После добавления строчки с настройкой этот виджет начинает отображать поле графика.

Содержание файла mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <qwt_plot.h>
#include <qwt_plot_grid.h>

#include <qwt_legend.h>

#include <qwt_plot_curve.h>
#include <qwt_symbol.h>

#include <qwt_plot_magnifier.h>

#include <qwt_plot_panner.h>

#include <qwt_plot_picker.h>

#include <qwt_picker_machine.h>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private Q_SLOTS:
    void click_on_canvas( const QPoint &pos );

private:

    Ui::MainWindow *ui;

    void addPlot();
    void addPlotGrid();

    QwtPlotCurve *curve;
    QPolygonF points;
    void addCurve();

    void enableMagnifier();
    void enableMovingOnPlot();

    void enablePicker();
};

#endif // MAINWINDOW_H



Содержание файла mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // Создать поле со шкалами для отображения графика
    addPlot();

    // Включить масштабную сетку
    addPlotGrid();

    // Кривая
    addCurve();

    // Включить возможность приближения/удаления графика
    enableMagnifier();

    // Включить возможность перемещения по графику
    enableMovingOnPlot();

    // Включить отображение координат курсора и двух перпендикулярных
    // линий в месте его отображения
    enablePicker();

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::addPlot()
{
    // #include <qwt_plot.h>
    ui->Qwt_Widget->setTitle( "Qwt demonstration" );
    ui->Qwt_Widget->setCanvasBackground( Qt::white );

    // Параметры осей координат
    ui->Qwt_Widget->setAxisTitle(QwtPlot::yLeft, "Y");
    ui->Qwt_Widget->setAxisTitle(QwtPlot::xBottom, "X");
    ui->Qwt_Widget->insertLegend( new QwtLegend() );

}

void MainWindow::addPlotGrid()
{
    // #include <qwt_plot_grid.h>
    QwtPlotGrid *grid = new QwtPlotGrid();
    grid->setMajorPen(QPen( Qt::gray, 2 )); // цвет линий и толщина
    grid->attach( ui->Qwt_Widget );
}

void MainWindow::addCurve()
{
    //#include <qwt_plot_curve.h>
    curve = new QwtPlotCurve();
    curve->setTitle( "Demo Curve" );
    curve->setPen( Qt::blue, 6 ); // цвет и толщина кривой

    // Маркеры кривой
    // #include <qwt_symbol.h>
    QwtSymbol *symbol = new QwtSymbol( QwtSymbol::Ellipse,
        QBrush( Qt::yellow ), QPen( Qt::red, 2 ), QSize( 8, 8 ) );
    curve->setSymbol( symbol );

    // Добавить точки на ранее созданную кривую
    // Значения точек записываются в массив, затем считываются
    // из этого массива
    for (int i = 0; i < 5; i++) {
        points << QPointF( 1.0 * i, 1.0 * i); // произвольное заполнение
    }

    curve->setSamples( points ); // ассоциировать набор точек с кривой

    curve->attach( ui->Qwt_Widget ); // отобразить кривую на графике
}

void MainWindow::enableMagnifier()
{
    // #include <qwt_plot_magnifier.h>
    QwtPlotMagnifier *magnifier =
            new QwtPlotMagnifier(ui->Qwt_Widget->canvas());
    // клавиша, активирующая приближение/удаление
    magnifier->setMouseButton(Qt::MidButton);
}

void MainWindow::enableMovingOnPlot()
{
    // #include <qwt_plot_panner.h>
    QwtPlotPanner *d_panner = new QwtPlotPanner( ui->Qwt_Widget->canvas() );
    // клавиша, активирующая перемещение
    d_panner->setMouseButton( Qt::RightButton );
}

void MainWindow::enablePicker()
{
    // #include <qwt_plot_picker.h>
    // настройка функций
    QwtPlotPicker *d_picker =
            new QwtPlotPicker(
                QwtPlot::xBottom, QwtPlot::yLeft, // ассоциация с осями
    QwtPlotPicker::CrossRubberBand, // стиль перпендикулярных линий
    QwtPicker::AlwaysOn, // всегда включен
    ui->Qwt_Widget->canvas() ); // ассоциация с полем

    // Цвет перпендикулярных линий
    d_picker->setRubberBandPen( QColor( Qt::red ) );

    // цвет координат положения указателя
    d_picker->setTrackerPen( QColor( Qt::black ) );

    // непосредственное включение вышеописанных функций
    d_picker->setStateMachine( new QwtPickerDragPointMachine() );
}


Выполненные действия позволяют нам отображать график непосредственно в форме, причем размеры его полотна соответствуют размерам «Qwt_Widget». Попробуйте после компиляции и запуска программы изменить размеры формы. Размеры «Qwt_Widget», естественно, не изменяются. Чтобы это происходило, необходимо воспользоваться «Layout». Эту стандартную (исхоженную и изъезженную) процедуру я решил не описывать в статье.


Отобразим координат клика в строке состояния Designer Form
1. Добавим код в прототип класса MainWindow в файле mainwindow.h
private Q_SLOTS:
void click_on_canvas( const QPoint &pos );


реализуем слот (функцию) в mainwindow.cpp:
void MainWindow::click_on_canvas( const QPoint &pos )
{
    // считываем значения координат клика
    double x = ui->Qwt_Widget->invTransform(QwtPlot::xBottom, pos.x());
    double y = ui->Qwt_Widget->invTransform(QwtPlot::yLeft, pos.y());

    statusBar()->showMessage("x= " + QString::number(x) +
                             "; y = " + QString::number(y));
}


В функцию enablePicker() добавляем следующую строчку:
    connect( d_picker, SIGNAL( appended( const QPoint & ) ),
            SLOT( click_on_canvas( const QPoint & ) ) );


Как вариант, можно было бы объявить d_picker приватной переменной класса MainWindow и делать вышеуказанный коннект «сигнал-слот» в конструкторе, либо в специальном методе.


Переместим кривую вдоль оси х
Здесь, так как визуализатор добавлен с помощью «Widget», все просто и знакомо:
1. Добавим элемент управления «Double Spin box» на форму и переименуем его на «changeXSpinBox».
2. Добавим элемент управления «PushButton» на форму и переименуем его на «moveByXButton». Изменим текст на кнопке на «Change x».
3. Для «moveByXButton» выполним команду «Go to slot»->clicked.
4. Функция должна выглядеть следующим образом:

void MainWindow::on_moveByXButton_clicked()
{
    double x = 0.0;
    // Выполняется преобразование ',' на '.' для того, чтобы
    // текст spinBox мог бы быть преобразован в double
    QString spinBoxText = ui->changeXSpinBox->text().replace(
                QLocale().decimalPoint(), QLatin1Char('.'));

    double xChangeValue = spinBoxText.toDouble();

    for (int i = 0; i <points.size(); i++) {
        x = points[i].x();
        points[i].setX(x + xChangeValue);
    }

    curve->setSamples(points);
    ui->Qwt_Widget->replot();
}




Выводы: мы успешно воспользовались дизайнерскими средствами Qt Creator для создания визуализатора и его демонстрационных элементов управления. С помощью spinBox и кнопки мы можем изменять координату x точек отображаемой кривой.
В части №4 цикла статей мы реализуем дополнительные способы увеличения/уменьшения графика, выборку точек с помощью мышки, средства редактирования отображения графика и кривых, а также экспорт графика.

P.S. Выражаю огромную признательность Riateche за ценные замечания по статьям №1 и №2, которые были учтены при написании этой статьи.

Ссылки:

Исходники:
github.com/VladimirSap/Qwt_Tutorial

Официальный ресурс:
qwt.sourceforge.net

Сборник решений разнообразных пробем c Qwt:
www.qtcentre.org/archive/index.php/f-23.html

Вариант библиотеки, альтернативный Qwt (спасибо, GooRoo)
www.qcustomplot.com

Ссылки на предыдущие статьи цикла:
Часть 1: habrahabr.ru/post/211204
Часть 2: habrahabr.ru/post/211867
Only registered users can participate in poll. Log in, please.
Как вы предпочитаете добавлять элементы управления (виджеты)?
56.44% С помощью Designer 57
39.6% Кодирую вручную 40
3.96% Лично сам GUI не занимаюсь 4
101 users voted. 18 users abstained.
Only registered users can participate in poll. Log in, please.
Выберите предпочтительное содержание последующих статей
32.08% С интерфейсом, выполненным с помощью Designer 17
22.64% С ручным кодированием интерфейса 12
39.62% Пусть в статье присутствуют оба варианта 21
5.66% Не имеет значения 3
53 users voted. 8 users abstained.
Tags:
Hubs:
+4
Comments 2
Comments Comments 2

Articles