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

  • Tutorial


В примерах использованы 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.

Как вы предпочитаете добавлять элементы управления (виджеты)?

Выберите предпочтительное содержание последующих статей

Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 2

    0
    Интересно — судя по результатам опроса, виджеты вручную кодирует достаточно большое количество человек? Почему? На первый взгляд Designer использовать удобнее…
      0
      Ввиду, без преувеличения, божественной системы лэйаутов в Qt кодирование без использования Designer не сильно сложней. Впрочем, в интерфейсах где множество элементов использую Designer для набросков, типа «посмотреть как оно». А когда оно уже всё задизайнено, явно выделены группы элементов (например есть готовый эскиз интерфейса), то кодирование вручную или использование Designer — суть перенос готового решения, разница небольшая.

    Only users with full accounts can post comments. Log in, please.