Qwt и Qt Creator. Быстро и просто. Часть 2: элементы отображения и управления

в 10:20, , рубрики: Без рубрики

Qwt и Qt Creator. Быстро и просто. Часть 2: элементы отображения и управления

В примерах использованы Qt Creator 3.0.0 (MinGW) и Qwt-6.1.0.
Для понимания этой статьи читателю желательно:

  • иметь начальный опыт разработки windows-приложений в среде Qt Creator;
  • понимать концепцию «сигнал-слот»;
  • познакомиться с частью №1 цикла моих статей про Qwt: habrahabr.ru/post/211204/

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

В части №1 (постепенно разрастающегося) цикла статей мы:
• подключили Qwt к Qt Creator;
• построили график;
• настроили оси координат;
• изменяли масштаб графика (приближали/удаляли его);
• перемещались по полю графика;
• отображали координаты рядом с курсором по щелчку мышкой.

В части №2 мы расширим функциональность нашего визуализатора:
• добавим строку состояния;
• сохраним координаты клика в переменных и отобразим их в строке состояния;
• добавим кнопку на панель управления;
• добавим на панель управления QwtCounter (поле для номера, значение которого можно изменять стрелками, см. картинку).
• зададим с помощью QwtCounter смещение графика x;
• нажатием на кнопку сместим график на ранее заданную величину х.

Примечание: В рамках этой статьи при добавлении элементов управления GUI не используется.

Подготовка: разбиение кода, представленного в статье №1, на функции

В предыдущей статье код шел целиком в конструкторе MainWindow. Теперь разделим код на функции. Присутствует небольшое изменение: если раньше мы прописывали непосредственно координаты точек кривой, то теперь мы их читаем из массива (см. комментарий).
Важно: если вы создаете новый проект, то не забудьте добавить в .pro файл строчку
CONFIG += qwt 

и после этого запустить qmake.

Вот такое содержание должно быть у файла 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:
    Ui::MainWindow *ui;

    QwtPlot *d_plot;
    void setPlot();

    QwtPlotGrid *grid;
    void setPlotGrid();


    QwtPlotCurve *curve;
    QwtSymbol *symbol;

    void setCurveParameters();

    // новый массив точек кривой
	double pointArray[5][2]; 
    QPolygonF points;
    void addPointsToCurveAndShow();

    QwtPlotMagnifier *magnifier;
    void enableMagnifier();


    QwtPlotPanner *d_panner;
    void enableMovingOnPlot();

    QwtPlotPicker *d_picker;
    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);

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

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

    // Кривая
    setCurveParameters();
    addPointsToCurveAndShow();

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

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

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

}

void MainWindow::setPlot()
{
    // (this) - разместить поле на текущем окне
    // #include <qwt_plot.h>
    d_plot = new QwtPlot( this );

    setCentralWidget(d_plot); // привязать поле к границам окна

    d_plot->setTitle( "Qwt demonstration" ); // заголовок
    d_plot->setCanvasBackground( Qt::white ); // цвет фона

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

void MainWindow::setPlotGrid()
{
    // #include <qwt_plot_grid.h>
    grid = new QwtPlotGrid();
    grid->setMajorPen(QPen( Qt::gray, 2 )); // цвет линий и толщина
    grid->attach( d_plot ); // добавить сетку к полю графика
}


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

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


void MainWindow::addPointsToCurveAndShow()
{

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

        points << QPointF( pointArray[i][0], pointArray[i][1]);
    }

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

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


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

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

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

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

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

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

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

Добавим строку состояния

Прописываем прототип функции в mainwindow.h:

void setStatusBar();

Добавляем код в mainwindow.cpp

void MainWindow::setStatusBar()
{

#ifndef QT_NO_STATUSBAR
    ( void )statusBar();
#endif

}

Вызываем функцию из конструктора MainWindow:

setStatusBar();

Сохраним координаты клика в переменных и отобразим их в строке состояния

Отображать координаты можно как по клику мышкой, так и в режиме реального времени. Так как в реальном времени координаты у нас уже отображаются возле курсора (небольшая поправочка кода части №1), то реализуем первый вариант.
Клик – это событие. Следовательно, создадим слот, который будет принимать это событие (сигнал). В mainwindow.h добавим новую приватную секцию и следующий код:

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

Реализуем слот (функцию) в mainwindow.cpp:

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

    QString info = "x= " + QString::number(x) +
    "; y = " + QString::number(y);

    // отображаем информацию в строке состояния
    statusBar()->showMessage(info);
}

В конструкторе MainWindow создаем пару «сигнал-слот».

// коннектить нужно именно к d_picker, но не к d_plot!
    connect( d_picker, SIGNAL( appended( const QPoint & ) ),
        SLOT( click_on_canvas( const QPoint & ) ) );

Откомпилируем и кликнем в любом месте экрана (показан нижний кусочек графика):
Qwt и Qt Creator. Быстро и просто. Часть 2: элементы отображения и управления

Как вы уже поняли, координаты клика, записанные в переменные double, можно использовать как угодно.

Создадим панель управления

Добавляем приватную переменную в mainwindow.h

QToolBar *toolBar;

Прописываем прототип функции в mainwindow.h:

void setToolBar ();

Добавляем код в mainwindow.cpp

void MainWindow::setToolBar()
{
    toolBar = new QToolBar( this );

    addToolBar( toolBar );
}

Вызываем функцию из конструктора MainWindow:

setToolBar();

Результат: в верхней части появилась тонкая полоска — панель инструментов
(пока пустая).

Добавим кнопку на панель управления

В начало mainwindow.h добавляем:

#include <QToolButton>

Добавляем приватные переменные в mainwindow.h и приватную функцию-прототип:

QToolButton *toolButton;
void addCorrectionButton();

Добавляем код в mainwindow.cpp

void MainWindow::addCorrectionButton()
{
    toolButton = new QToolButton( toolBar );

    toolButton->setText( "Change x" );
    toolButton->setCheckable( true );

    toolBar->addWidget( toolButton ); // добавить кнопку на панель инструментов
}

Вызываем функцию из конструктора MainWindow:

addCorrectionButton();
Добавим на панель управления QwtCounter

В начало mainwindow.h добавляем:

#include <QHBoxLayout>
#include <qwt_counter.h>

Добавляем приватные переменные в mainwindow.h и приватную функцию-прототип:

QWidget     *hBox;
QHBoxLayout *layout;
QwtCounter  *cntDamp;
void addQwtCounter();

Добавляем код в mainwindow.cpp

void MainWindow::addQwtCounter()
{
    // настраиваем параметры установщика смещения
    cntDamp = new QwtCounter();
    cntDamp->setRange( -50, 50 );

    // шаг изменения числа при нажатии одинарной стрелки
    // при нажатии двойной стрелки число изменяется на порядок больше
    cntDamp->setSingleStep( 1.0 );
    cntDamp->setValue( 0 ); // начальное значение

    cntDamp->setEnabled(true);

    // Размещение элемента на панели инструментов

    // новый виджет
    hBox = new QWidget();

    // "контейнер", организущий виджеты в горизонтальной последовательности
    // ассоциируется с объектом типа QWidget.
    layout = new QHBoxLayout( hBox );

    // помещаем в контейнер установщик смещения
    layout->addWidget(cntDamp);

    // без этой строчки установщик смещения будет растянут на всю панель
    layout->addWidget( new QWidget(hBox) , 10 ); // spacer

    // помещаем виджет на уже имеющуюся панель инструментов
    ( void )toolBar->addWidget( hBox );
}

Вызываем функцию из конструктора MainWindow:

addQwtCounter();

Результат:
Qwt и Qt Creator. Быстро и просто. Часть 2: элементы отображения и управления

Зададим с помощью QwtCounter смещение графика x

В mainwindow.h в секцию private Q_SLOTS добавим еще один слот:

void setPlotCorrection( double coeff );

В mainwindow.h добавим приватную переменную:

double changeXValue;

Реализуем слот (функцию) в mainwindow.cpp:

void MainWindow::setPlotCorrection( double coeff )
{
    changeXValue = coeff;
}

В конструкторе MainWindow инициализируем добавленную переменную и создаем пару «сигнал-слот»:

changeXValue = 0.0;
connect( cntDamp, SIGNAL( valueChanged( double ) ),
      SLOT( setPlotCorrection( double ) ) );

Нажатием на кнопку сместим график на ранее заданную величину х

В mainwindow.h в секцию private Q_SLOTS добавим еще один слот:

void changeX();

реализуем слот (функцию) в mainwindow.cpp:

void MainWindow::changeX()
{
    // очистить контейнер точек
    points.clear();

    for (int i = 0; i < 5; i++) {
        pointArray[i][0] += changeXValue;

        points << QPointF( pointArray[i][0], pointArray[i][1]);
    }

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

    d_plot->replot();

}

После добавления этих строчек в нашем коде появляется дублирование. Однако, программа имеет демонстрационный характер, поэтому рефакторинг не производился.

В конструкторе MainWindow создаем пару «сигнал-слот»:

connect( toolButton, SIGNAL(toggled(bool)),
SLOT( changeX() ) );

Примечание: кнопка имеет два различных положения (нажата/не нажата), однако, в программе это состояние не отслеживается.

Результаты и выводы

Запустим программу. Установим стрелочками любое число и нажмем на кнопку. График успешно смещается по полю на заданную величину.

Выводы:
В данной статье на простом примере показано, как добавлять элементы управления вручную, и с помощью них производить модификацию графика.
Очевидно, добавление управляющих элементов вручную –процесс, требующий дополнительного ручного кодирования. Хотелось бы использовать GUI. Именно этому будет посвящена моя следующая статья.

Спасибо за внимание!

P.S. Просьба оставить комментарии по поводу оформления статьи. Удобно ли было ее читать? Может быть, изменить представление материала? Как?

Ссылки:

Архив с исходниками данного примера: yadi.sk/d/TcMglWvAHWvxT

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

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

Часть №1 цикла статей про Qwt: habrahabr.ru/post/211204/

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

Автор: HotFire

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js