- •Содержание
- •Глава 1. Введение в информационно-аналитическую систему обратных задач химической кинетики1
- •1.1. Введение
- •1.2. Методологические основы изучения объектов химической кинетики
- •1.3. Структура информационно-аналитической системы обратных задач химической кинетики
- •Глава 2. Составление и анализ кинетических моделей химических реакций
- •2.1. Основные понятия и определения
- •2.2. Гомогенные и гетерогенные реакции
- •2.3. Замкнутые и открытые системы
- •2.4. Скорость химической реакции. Кинетическое уравнение химического процесса
- •2.5. Константа скорости химической реакции. Энергия активации
- •2.6. Схема сложного химического процесса
- •2.7. Система кинетических уравнений сложного химического процесса
- •2.8. Основные показатели эффективности проведения химических реакций
- •2.9. Кинетические модели химических реакций
- •2.10. Прямая и обратная задачи
- •Глава 3. Дифференциальные уравнения в задачах химической кинетики
- •3.1. Кинетика простых реакций.
- •3.2. Кинетика сложных реакций
- •3.2.1. Обратимые реакции
- •3.2.3 Последовательные реакции a→r→s. Аналитическое решение прямой кинетической задачи.
- •3.3. Численные методы решения кинетических уравнений.
- •3.3.1. Метод Эйлера
- •3.3.2.Метод Рунге-Кутты
- •Глава 4. Qt4. Создание пользовательского интерфейса.
- •4.1. Введение в Qt
- •4.2. Первое знакомство
- •4.3. Использование графики в приложениях
- •4.4. Создание диалоговых окон
- •Глава 5. Разработка иас озхк при моделировании детального механизма гидроалюминирования олефинов
- •5.1. Механизм гидроалюминирования олефинов
- •5.2. Математическое описание общей реакции гидроалюминирования олефинов алкилаланами
- •К входным информационным потокам относятся:
- •5.3. Кинетическая модель каталитической реакции гидроалюминирования олефинов алкилаланами в присутствии Сp2ZrCl2. Результаты вычислительного эксперимента
- •Выводы:
- •Информационно-аналитическая система обратных задач химической кинетики
4.3. Использование графики в приложениях
Основу используемых в Qt средств графики 2D составляет класс QPainter (рисовальщик Qt). Этот класс может использоваться для рисования геометрических фигур (точек, линий, прямоугольников, эллипсов, дуг, сегментов и секторов окружности, многоугольников и кривых Безье), а также пиксельных карт, изображений и текста. Кроме того, QPainter поддерживает такие продвинутые функции, как сглаживание соединений линий (antialiasing) при начертании фигур и букв в тексте, альфа-смешение (alpha blending), плавный переход цветов (gradient filling) и цепочки графических элементов (vector paths). QPainter также поддерживает линейные преобразования, такие как трансляция, поворот, обрезание и масштабирование. Путем изменения реализации функции QWidget: :paintEvent(), мы можем создавать пользовательские виджеты и осуществлять полный контроль над их внешним видом. Типичным требованием является необходимость отображать большое количество «легких» произвольных элементов определенной формы, с которыми пользователь может осуществлять взаимодействие на двухмерной канве.
Использование класса QPainter рассмотрим на примере программы, реализующей решение задачи, рассмотренной во второй главе.
Реализацию задачи оформим в виде виджета, для отображения изменения концентрации участвующих в реакции веществ в виде графиков. Исходный код программы содержится в двух файлах: widget.h и widget.cpp. Сначала приведем файл widget.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifndef WIDGET_H #define WIDGET_H #include <QtGui> QT_BEGIN_NAMESPACE class QPaintEvent; QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent=0); protected: void paintEvent(QPaintEvent *event); }; #endif |
Строки 1 и 2 (а также строка 16) предотвращают многократное включение в программу этого заголовочного файла.
Строки 4 , 6 помогают избежать коллизий при компоновке.
А в 5 строке происходит предварительное объявление классов Qt, использующихся для реализации виджета. Предварительное объявление (forward declaration) указывает компилятору C++ только на существование класса, не давая подробного определения этого класса (обычно определение класса содержится в его собственном заголовочном файле).
Затем мы определяем класс Widget как подкласс QWidget.
Макрос Q_0BJECT необходимо задавать в начале определения любого класса, содержащего сигналы или слоты. И, хотя в нашем примере они и не используются, объявим его, если у вас возникнет желание расширить функциональность виджета.
Конструктор Widget() является типичным для классов виджетов в Qt. В параметре parent (родитель) указывается родительский виджет. По умолчанию задается нулевой указатель, что говорит о том, что у данного диалога нет родительского виджета.
Функция PaintEvent вызывается всякий раз, когда требуется перерисовать виджет. Используемая по умолчанию ее реализация в QWidget ничего не делает, оставляя виджет пустым. Поэтому нам потребуются переопределить данную функцию.
Теперь рассмотрим файл widget.срр, в котором находится реализация класса Widget.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include "widget.h" #include<math.h> void Difur(double dA[3],double a[3],double k1,double k2) { //k1 и k2 – константы скорости dA[0]=-k1*a[0]; dA[1]=k1*a[0]-k2*a[1]; dA[2]=k2*a[1]; } void RK_4(double dA[3],double A_pred[3],double a[3],double k1,double k2,double dx) { Difur(dA,a,k1,k2); double r[3]; for(int i=0; i <3; ++i) { r[i]=dA[i]; a[i]=A_pred[i]+r[i]*(dx/2.0); } Difur(dA,a,k1,k2); for(int i=0; i <3; ++i) { r[i]=dA[i]; a[i]=A_pred[i]+r[i]*(dx/2.0); } Difur(dA,a,k1,k2); for(int i=0; i <3; ++i) { r[i]=dA[i]; a[i]=A_pred[i]+r[i]*(dx); } Difur(dA,a,k1,k2); } |
Функции Difur и RK_4, реализуют решение дифференциальных уравнений (68) методом Рунге-Кутты. Надеемся, что пояснение их работы не требуется.
34 35 36 37 38 39 40 |
Widget::Widget(QWidget *parent) : QWidget(parent) { setFixedSize(680, 320); QPalette p1(Qt::lightGray); setPalette(p1); setAutoFillBackground(true); } |
В 36 строке фиксируется размер виджета. Класс QPalette содержит цветовую группу для каждого состояния виджета. В данном случае мы задаем фиксированный цвет для виджета.
В 39 строке указываем, что виджет должен отрисовывать свой фон автоматически.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
void Widget::paintEvent(QPaintEvent *event) { QPainter painter; painter.begin(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(QPen(Qt::red,3,Qt::SolidLine,Qt::RoundCap)); double max_X=10.0,max_Y=0.25,min_X=0,min_Y=0; double X=fabs(max_X)+fabs(min_X); double Y=fabs(max_Y)+fabs(min_Y); double size_w=width()-80; double size_h=height()-20; double dA[3],a[3]={0.2,0.0,0.0},Apr[3]; //изменение концентраций, значение концентрации на текущем и на прошлом шаге соответственно double dx=0.05;//шаг double k1=0.5,k2=0.2;//константы скорости painter.setPen(QPen(Qt::black,3,Qt::SolidLine,Qt::RoundCap)); painter.drawLine(QPointF(45.0,0.0),QPointF(45.0,size_h)); painter.drawLine(QPointF(45.0,size_h),QPointF(size_w+70,size_h )); painter.drawLine(QPointF(40.0,25.0),QPointF(45.0,0.0)); painter.drawLine(QPointF(50.0,25.0),QPointF(45.0,0.0)); painter.drawLine(QPointF(size_w+70.0,size_h),QPointF(size_w+45.0,size_h+5.0)); painter.drawLine(QPointF(size_w+70.0,size_h),QPointF(size_w+45.0,size_h-5.0)); painter.setPen(QPen(Qt::darkGray,2,Qt::DashDotDotLine,Qt::RoundCap)); for(int i=1;i<5;++i) { painter.drawLine(QPointF(45.0,(size_h/5.0)*i),QPointF(45.0+size_w,(size_h/5.0)*i)); painter.drawLine(QPointF(45.0+i*(size_w/5.0),0.0),QPointF(45.0+i*(size_w/5.0),size_h)); } painter.setPen(QPen(Qt::black,2,Qt::SolidLine,Qt::RoundCap)); painter.drawLine(QPointF(40.0,size_h/5.0),QPointF(50.0,size_h/5.0)); painter.drawText(QPointF(15.0,size_h/5.0),QString::number(max_Y)); painter.drawText(QPointF(45.0,size_h+20),QString::number(0)); painter.drawText(QPointF(45.0+size_w,size_h+20),QString::number(max_X)); painter.drawLine(QPointF(45.0+size_w,size_h+5),QPointF(45.0+size_w,size_h-5)); painter.drawText(QPointF(45.0+size_w/2,size_h+20),"t"); painter.drawText(QPointF(15.0,size_h/2.0),"C"); |
В 43-44 строке объявляем объект типа QPainter и связываем его с виджетом.
Функция setRenderHint определяет стиль сглаживания, указывая QPainter использовать цвет различной интенсивности около границ форм, чтобы уменьшить визуальные искажения, которые имеют место, когда происходит формирование пикселей на границах формы.
Тремя основными параметрами настройки рисовальщика являются перо, кисть и шрифт.
• Перо (Pen) используется для отображения прямых линий и контуров фигур. Оно имеет цвет, толщину, стиль линии, стиль окончания линии и стиль соединения линий.
•Кисть (Brush) представляет собой шаблон, который используется для заполнения геометрических фигур. Он обычно имеет цвет и стиль, но может также представлять собой текстуру (пиксельную карту, повторяющуюся бесконечно) или цветовой градиент.
• Шрифт используется для отображения текста. Шрифт имеет много атрибутов, в том числе название и размер.
Эти настройки можно в любое время модифицировать при помощи функций setPen, setBrush и setFont, вызываемых для объектов QPen, QBrush или QFont. Так, например, в строке 46 мы устанавливаем перо Qt::Red (красного цвета ), стиль пера – Qt::SolidLine (сплошная), толщину в 3, а Qt::RoundCap – означает, что у линии будет скругленное окончание.
В строках 47-49 задаем область определения, область значений функции и их длину. Функции width и height возвращают ширину и высоту виджета в пикселях, а size_w и size_h объявляем для того, чтобы график не занимал весь виджет.
В строках 58-60 рисуем оси ординат и абсцисс соответственно. QPointF служат для задания положения точки в двухмерной системе координат. Строки 61-66 служат для отображения направления осей. Далее рисуем сетку и добавляем максимальные и минимальные значения по осям.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
for(double x=0;x<max_X;x=x+dx) { Difur(dA,a,k1,k2); for(int i=0;i<3;++i) Apr[i]=a[i]; RK_4(dA,Apr,a,k1,k2,dx); painter.setPen(QPen(Qt::red,3,Qt::SolidLine,Qt::RoundCap)); painter.drawLine(QPointF(45+x*(size_w/X),fabs((size_h/Y)*Apr[0]-size_h)),QPointF(45+(x+dx)*(size_w/X),fabs((size_h/Y)*a[0]-size_h))); if(x==0.0) painter.drawText(QPointF(50.0,fabs((size_h/Y)*Apr[0]-size_h)),"cA"); painter.setPen(QPen(Qt::blue,3,Qt::SolidLine,Qt::RoundCap)); painter.drawLine(QPointF(45+x*(size_w/X),fabs((size_h/Y)*Apr[1]-size_h)),QPointF(45+(x+dx)*(size_w/X),fabs((size_h/Y)*a[1]-size_h))); if(x==0.0) painter.drawText(QPointF(50,size_h-50),"cR"); painter.setPen(QPen(Qt::darkGreen,3,Qt::SolidLine,Qt::RoundCap)); painter.drawLine(QPointF(45+x*(size_w/X), fabs((size_h/Y)*Apr[2]-size_h)),QPointF(45+(x+dx)*(size_w/X), fabs((size_h/Y)*a[2]-size_h))); if(x==0.0) painter.drawText(QPointF(90,size_h-20),"cS"); } painter.end(); } |
В данной части кода происходит расчет методом Рунге-Кутты, и отображение значений.
Вызовом end заканчиваем рисование: все используемые для рисования ресурсы освобождаются.
Теперь обратимся к файлу runge.cpp.
1 2 3 4 5 6 7 8 9 |
#include <QtGui> #include”widget.h” int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget runge_w; runge_w.show(); return a.exec(); } |
Аналогичный код мы уже рассматривали, только вместо Widget использовали QLabel.
Скомпилируем и выполним программу, и получим следующий результат:
Рис. 6. График зависимости концентраций веществ с изменением времени
