- •Содержание
- •Глава 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.4. Создание диалоговых окон
Теперь обратимся к методологии создания диалоговых окон с использованием средств разработки Qt. Диалоговые окна предоставляют пользователю возможность задавать необходимые значения параметров и выбирать определенные режимы работы, то есть реагировать на действия пользователя. Большинство приложений с графическим пользовательским интерфейсом имеют главное окно с панелью меню и инструментальной панелью, а также десятки диалоговых окон, естественно дополняющих главное окно. Можно также создать приложение из одного диалогового окна, которое будет непосредственно реагировать на выбор пользователя, выполняя соответствующие действия (например, таким приложением может быть калькулятор).
Диалоговое окно мы создадим полностью вручную, чтобы было ясно, как выглядит исходный код такой программы. После этого, рекомендуется ознакомиться с Qt Designer, который является средством визуального проектирования в Qt. Использование Qt Designer позволяет получать результат значительно быстрее, чем при ручном кодировании, и полученные в нем различные варианты проектов легче тестировать и изменять в будущем.
Реализуем программу regressDialog, выполняющую нелинейный регрессионный анализ. Обратимся к теории.
Существенным этапом в химических исследованиях является построение математических моделей, описывающих экспериментальные данные. Обычно эти модели включают функцию одной независимой переменной и ряда подгоночных параметров:
Здесь
y
и x
– измеряемые зависимая и независимая
переменные соответственно, а
–
параметры, подлежащие определению.
Нелинейный регрессионный анализ заключается в нахождении параметров, обеспечивающих наилучшую аппроксимацию экспериментальных данных. Данный метод использует минимизацию взвешенной суммы квадратов отклонений
где wi – веса, соответствующие результатам измерений (xi, yi), F(xi) – значение сглаживающей функции при x=xi. Процедура минимизации
приводит к системе p нелинейных уравнений. Один из подходов к решению задачи заключается в использовании ряда Тейлора в качестве сглаживающей функции, в котором сохранен только линейный член. Пусть заданы начальные приближения параметров
Тогда сглаживающая функция
принимает вид
Здесь Fi0 – значение функции F, вычисленное при xi, a10, a20 и т.д., (∂Fi/∂aj)0 – значение производной функции F по aj, вычисленное при xi, a10, a20 и т.д.
Для простоты положим p=2 и введем обозначения
Тогда
Сумма квадратов отклонений при единичном взвешивании равна
Эта функция линейна по отношению к поправочным членам ∆a1 и ∆a2. Минимизация по этим членам дает следующую систему уравнений:
После перестановки членов получается система линейных уравнений:
Эту систему можно решить относительно ∆a1 и ∆a2. Тогда, если процедура сходится, наилучшие аппроксимации параметров определяются следующим образом:
Соответственно для случая p параметров имеем
Сходимость задается условием
где
ε – малое положительное число. Пусть
система
линейных
уравнений задана в виде
PA=Z
Здесь P – матрица частных производных
A – неизвестный вектор поправочных членов
и Z – вектор правых частей, r-ый элемент которого имеет вид
Матрица P – симметричная. Решение системы можно получить путем обращения матрицы:
Дисперсия адекватности выражается формулой
Здесь
–
r-ый
диагональный элемент обратной матрицы.
Итак, процедура нелинейного регрессионного анализа заключается в нижеследующем.
Пусть заданы: набор экспериментальных точек (xi, yi, i=1, 2, …, N), подходящая сглаживающая функция F (x; a1, a2, … , ap), а также начальные приближения к параметрам (a10, a20, … , ap0). Тогда требуется выполнить следующие шаги:
Вычислить функцию и p частных производных
Сформировать матрицу P и вектор Z.
Вычислить обратную матрицу и вектор поправочных членов A
Проверить критерий сходимости. Если критерий не выполняется, то повторить процесс.
Приведенная ниже программа позволяет проводить построение нелинейной сглаживающей кривой.
Работа программы regressDialog иллюстрируется на следующем примере из химической кинетики
где k1, k2 – прямые константы скорости реакции, а k3, k4 – обратные.
Решение этой кинетической системы можно получить в аналитической форме.
|
(4.1) |
где
|
(4.2) |
В
рассматриваемом примере зависимой
переменной является концентрация
промежуточного вещества B,а
независимой переменной – время. Данную
систему определяет четыре параметра
.
Функция
имеет вид
|
(4.3) |
Очевидно, что функция B нелинейна по параметрам.
Исходный код программы содержится в четырех файлах: regressDialog.h, widget.h, regressDialog.срр и widget.cpp. Сначала рассмотрим файл widget.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#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); double k1,k2,k3,k4,T; bool draw; protected: void paintEvent(QPaintEvent *event); }; #endif |
Здесь вам уже должно быть всё знакомо, k1,k2,k3,k4,T – параметры расчёта, которые будут вводить пользователь в процессе работы с программой. Булевое значение draw – служит для определения, выводить ли график на экран.
Теперь рассмотрим структуру Widget.cpp:
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 34 35 36 37 38 39 40 41 42 43 44 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#include "widget.h" #include<math.h> using namespace std; void func(int N,int M, double *A,double *F,double **G,double T) { double k1,k2,k3,k4; k1=A[0]; k2=A[1]; k3=A[2]; k4=A[3]; double P=k1+k2+k3+k4; double Q=sqrt(P*P-4.0*(k1*k2+k3*k4+k1*k3)); double l2=(P+Q)/2.0; double l3=(P-Q)/2.0; double dQ1,dQ2,dQ3,dQ4; dQ1=(k1-k2-k3+k4)/Q;//частная производная Q по k1 dQ2=(-k1+k2+k3+k4)/Q; //частная производная Q по k2 dQ3=-dQ1; //частная производная Q по k3 dQ4=(k1+k2-k3+k4)/Q; //частная производная Q по k4 double Del=l2*l3*(l2-l3); for(int i=1;i<=N;++i) { double t=double(i)*(T/double(N)); double dop1, dop2, dop3; double Upper=k1*k3*(l2-l3)+exp(-l2*t)*k1*l3*(k3-l2)+exp(-l3*t)*k1*l2*(l3-k3); F[i-1]=(k1*k3*(l2-l3)+l3*k1*(k3-l2)*exp(-l2*t)+l2*k1*(l3-k3)*exp(-l3*t))/Del;//вычисление самой функции dop1=k3*(l2-l3+k1*dQ1); dop2=exp(-l2*t)*(l3*(k3-l2)+(k1/2.0)*(l3*(t*(l2-k3)-1.0)-l2+k3+dQ1*(l3*(t*(l2-k3)-1.0)+l2-k3))); dop3=exp(-l3*t)*(l2*(l3-k3)+(k1/2.0)*(l2*(t*(k3-l3)+1.0)-k3+l3-dQ1*(l2*(t*(k3-l3)+1.0)+k3-l3))); //вычисление частной производной по k1 G[0][i-1]=(dop1+dop2+dop3)/Del-Upper*(l2*l2-l3*l3-((l2-l3)*(l2-l3)-2*l2*l3)*dQ1)/(2*Del*Del dop1=k1*k3*dQ2; dop2=(k1/2.0)*exp(-l2*t)*(l3*(t*(l2-k3)-1.0)-l2+k3+(l3*(t*(l2-k3)-1.0)+l2-k3)*dQ2); dop3=(k1/2.0)*exp(-l3*t)*(l2*(t*(k3-l3)+1.0)-k3+l3-dQ2*(l2*(t*(k3-l3)+1.0)+k3-l3)); //вычисление частной производной по k2 G[1][i-1]=(dop1+dop2+dop3)/Del-Upper*(l2*l2-l3*l3-((l2-l3)*(l2-l3)-2*l2*l3)*dQ2)/(2*Del*Del); dop1=k1*(l2-l3+k3*dQ3); dop2=(k1/2.0)*exp(-l2*t)*(l3*(t*(l2-k3)+1)-l2+k3+(l3*(t*(l2-k3)-1.0)+l2-k3)*dQ3); dop3=(k1/2.0)*exp(-l3*t)*(l2*(t*(k3-l3)-1.0)-k3+l3-dQ3*(l2*(t*(k3-l3)+1.0)+k3-l3)); //вычисление частной производной по k3 G[2][i-1]=(dop1+dop2+dop3)/Del-Upper*(l2*l2-l3*l3-((l2-l3)*(l2-l3)-2*l2*l3)*dQ3)/(2*Del*Del); dop1=k1*k3*dQ4; dop2=(k1/2.0)*exp(-l2*t)*(l3*(t*(l2-k3)-1.0)-l2+k3+(l3*(t*(l2-k3)-1.0)+l2-k3)*dQ4); dop3=(k1/2.0)*exp(-l3*t)*(l2*(t*(k3-l3)+1.0)-k3+l3-dQ4*(l2*(t*(k3-l3)+1.0)+k3-l3)); //вычисление частной производной по k4 G[3][i-1]=(dop1+dop2+dop3)/Del-Upper*(l2*l2-l3*l3-((l2-l3)*(l2-l3)-2*l2*l3)*dQ4)/(2*Del*Del); } } |
Функция func выполняет подсчёт концентрации (F) и частные производные(G) на каждом временном шаге (данные формулы получатся, если аккуратно продифференцировать уравнение (4.1) с учетом (4.2)).
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
double val(double t,double *A) { double k1,k2,k3,k4; k1=A[0]; k2=A[1]; k3=A[2]; k4=A[3]; double P=k1+k2+k3+k4; double Q=sqrt(P*P-4.0*(k1*k2+k3*k4+k1*k3)); double l2=(P+Q)/2.0; double l3=(P-Q)/2.0; double Del=l2*l3*(l2-l3); return (k1*k3*(l2-l3)+l3*k1*(k3-l2)*exp(-l2*t)+l2*k1*(l3-k3)*exp(-l3*t))/Del;
} void obr_mat(int M,double **f,double **a) { double constA; //прямой ход for(int i=0;i<M;i++) for(int j=0;j<M;j++) { if(i==j) f[i][j]=1; else f[i][j]=0; } for(int i=0;i<M-1;i++) for(int k=i+1;k<M;k++) { constA=a[k][i]; for(int j=0;j<M;j++) { a[k][j]=a[k][j]-constA*(a[i][j]/a[i][i]); if(j<=M-1) f[k][j]=f[k][j]-constA*(f[i][j]/a[i][i]) ; } } //вычисление детерминанта и обратный ход double detA=1; for(int i=0;i<M;i++) { detA=detA*a[i][i]; } for(int i=0;i<M;i++) { constA=a[i][i]; for(int j=0;j<4;j++) { a[i][j]=a[i][j]/constA; f[i][j]=f[i][j]/constA; } } for(int i=M-1;i>=0;i--) for(int j=i-1;j>=0;j--) { constA=a[j][i]; for(int k=0;k<M;++k) { a[j][k]=a[j][k]-constA*a[i][k] ; f[j][k]=f[j][k]-constA*f[i][k] ; } } } |
Функция val возвращает значение функции, в зависимости от времени, а функция obr_mat – вычисляет обратную матрицу (методом Гаусса).
Конструктор класса останется прежним:
126 127 128 129 130 131 132 133 134 |
Widget::Widget(QWidget *parent) : QWidget(parent) { setFixedSize(680, 320); QPalette p1(Qt::lightGray); setPalette(p1); setAutoFillBackground(true); draw=false;//вначале нечего отображать, входных данных нет } |
Рассмотрим функцию PaintEvent:
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
void Widget::paintEvent(QPaintEvent *event) { if(draw==true) { 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=1.0,min_X=0,min_Y=0; double X=fabs(max_X)+fabs(min_X); double Y_=fabs(max_Y)+fabs(min_Y); double x_n=10.0; double dx=0.05; double size_w=width()-80; double size_h=height()-20; 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(40.0,25.0),QPointF(45.0,0.0)); painter.drawLine(QPointF(50.0,25.0),QPointF(45.0,0.0)); painter.drawLine(QPointF(45.0,size_h),QPointF(size_w+70,size_h)); 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"); |
Аналогично тому, что рассматривалось в разделе 4.3, отображаем оси координат, сетку, и максимальные и минимальные значения.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
int N,M;//N –кол-во экспериментальных точек, M – кол-во //параметров, в данном случае 4: k1-k4 N=10; M=4; //динамическое создание массивов //A – значения k1-k4 на текущем шаге //F – вычисленные значения функции //Y – экспериментальные значения //Z – вектор правых частей double *A=new double[M],*F=new double[N],*Y=new double[N],*Z=new double[M],delta; //f – обратная матрица к a //G – производные //a – матрица частных производных double **f,**G,**a; a=new double*[M]; G=new double*[M]; f=new double*[M]; for(int i=0;i<M;++i) { a[i]=new double[M]; G[i]=new double[N]; f[i]=new double[M]; } A[0]=k1; A[1]=k2; A[2]=k3; A[3]=k4; //y0-y9, экспериментальные данные Y[0]=0.40931 ; Y[1]=0.41220 ; Y[2]=0.37137 ; Y[3]=0.33922 ; Y[4]=0.31844 ; Y[5]=0.30561 ; Y[6]=0.29779 ; Y[7]=0.29304 ; Y[8]=0.29304 ; Y[9]=0.28841 ; int l; for(l=0;l<20;++l)//считаем не более 20 раз { func(N,M,A,F,G,T); for(int i=0;i<M;++i) { Z[i]=0.0; for(int j=0;j<M;++j) a[i][j]=0.0; } for(int i=0;i<4;++i) for(int j=0;j<4;++j) { for(int k=0;k<N;++k) a[i][j]=a[i][j]+G[i][k]*G[j][k]; //считаем матрицу частных производных } obr_mat(M,f,a);//находим обратную for(int i=0;i<M;++i) for(int j=0;j<N;++j) { Z[i]=Z[i]+(Y[j]-F[j])*G[i][j]; //подсчет Z } double test=0.0; for(int i=0;i<M;++i) { delta=0.0; for(int j=0;j<M;++j) { delta=delta+f[i][j]*Z[j]; } test=test+fabs(delta/A[i]);//test – критерий //сходимости A[i]=A[i]+delta;//подправляем коэффициенты } if(test<0.00001) break;//если критерий сходимости //выполняетcя, выходим из цикла } |
В конце расчета, получаем новые параметры k1, k2, k3, k4. Графически убедимся, что функция (4.3), хорошо описывает эксперимент.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
for(double x=0;x<x_n+0.05;x=x+dx) { painter.setPen(QPen(Qt::blue,3,Qt::SolidLine,Qt::RoundCap)); painter.drawLine(QPointF(45+x*(size_w/X),fabs((size_h/Y_)*val(x,A)-size_h)),QPointF(45+(x+dx)*(size_w/X),fabs((size_h/Y_)*val(x+dx,A)-size_h))); //рисуем функцию, с новыми параметрами k1,k2,k3,k4 (они в массиве A) } painter.drawText(QPointF(50.0,fabs((size_h/Y_)*val(5.0,A)-size_h)),"cB"); // подписываем кривую painter.setPen(QPen(Qt::red,3,Qt::SolidLine,Qt::RoundCap)); for(int i=1;i<=N;++i) { double t=double(i)*(T/double(N)); painter.drawEllipse(45+t*(size_w/X),fabs((size_h/Y_)*Y[i-1]-size_h),5,5); //обозначаем экспериментальные значения } //подчищаем за собой delete []A; delete []Z; delete []F; delete []Y; for(int i=0; i<M; ++i) { delete []a[i]; delete []G[i]; delete []f[i]; } delete []a; delete []G; delete []f;
painter.end(); } } |
Описание класса Widget закончено. Теперь обратимся к regressDialog.cpp и regressDialog.h:
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 34 |
#ifndef FINDDIALOG_H #define FINDDIALOG_H
#include <QDialog> #include"widget.h"
class QLabel; class QLineEdit; class QPushButton; class QWidget;
class regressDialog : public QDialog { Q_OBJECT public: regressDialog(QWidget *parent = 0); private slots: void start(); private: QLabel *k1Label; QLabel *k2Label; QLabel *k3Label; QLabel *k4Label; QLabel *tLabel; QLineEdit *k1Edit; QLineEdit *k2Edit; QLineEdit *k3Edit; QLineEdit *k4Edit; QLineEdit *tEdit; Widget *drawWidget;// обратите внимание, объявлеям класс, //описанный нами в widget.h QPushButton *startButton; QPushButton *closeButton; }; #endif |
В строках с7-й по 10-ю даются предварительные объявления классов Qt, использующихся для реализации диалогового окна. Предварительное объявление (forward declaration) указывает компилятору C++ только на существование класса, не давая подробного определения этого класса.
Затем мы определяем regressDialog как подкласс QDialog.
В закрытой (private) секции класса мы объявляем один слот. Для его реализации слотов нам потребуется большинство дочерних виджетов диалогового окна, поэтому мы резервируем для них соответствующие переменные-указатели. Ключевое слово slots, так же как и signals, является макросом, который преобразуется в последовательность инструкций, понятных компилятору C++.
Для private-переменных мы использовали предварительные объявления их классов. Это допустимо, потому что все они являются указателями, и мы не используем их в заголовочном файле – поэтому компилятору не требуется иметь полные определения классов. Мы могли бы воспользоваться соответствующими заголовочными файлами (<QEdit>, <QLabel> и т. д.), но при использовании предварительных объявлений компилятор работает немного быстрее.
Содержание файла regressDialog.cpp:
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 34 35 36 37 38 39 40 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 85 86 87 88 89 90 |
#include <QtGui> #include "regressDialog.h" #include "widget.h"
regressDialog::regressDialog(QWidget *parent) : QDialog(parent) { resize(800,600); k1Label = new QLabel(tr("k&1:")); k1Edit = new QLineEdit; k1Label->setBuddy(k1Edit); k1Edit->setText("1.3");//так же задаем начальные параметры, //можно и другие ввести уже самим пользователем k2Label = new QLabel(tr("k&2:")); k2Edit = new QLineEdit; k2Label->setBuddy(k2Edit); k2Edit->setText("0.65");
k3Label = new QLabel(tr("k&3:")); k3Edit = new QLineEdit; k3Label->setBuddy(k3Edit); k3Edit->setText("0.175");
k4Label = new QLabel(tr("k&4:")); k4Edit = new QLineEdit; k4Label->setBuddy(k4Edit); k4Edit->setText("0.36");
tLabel = new QLabel(tr("&Time:")); tEdit = new QLineEdit; tLabel->setBuddy(tEdit); tEdit->setText("10.0");
drawWidget =new Widget(this);
startButton = new QPushButton(tr("&Start")); startButton->setDefault(true);
closeButton = new QPushButton(tr("&Close"));
connect(startButton, SIGNAL(clicked()), this, SLOT(start())); connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
QHBoxLayout *k1Layout = new QHBoxLayout; k1Layout->addWidget(k1Label); k1Layout->addWidget(k1Edit);
QHBoxLayout *k2Layout = new QHBoxLayout; k2Layout->addWidget(k2Label); k2Layout->addWidget(k2Edit);
QHBoxLayout *k3Layout = new QHBoxLayout; k3Layout->addWidget(k3Label); k3Layout->addWidget(k3Edit);
QHBoxLayout *k4Layout = new QHBoxLayout; k4Layout->addWidget(k4Label); k4Layout->addWidget(k4Edit);
QHBoxLayout *tLayout = new QHBoxLayout; tLayout->addWidget(tLabel); tLayout->addWidget(tEdit);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(startButton); buttonLayout->addWidget(closeButton);
QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addLayout(tLayout); rightLayout->addLayout(k1Layout); rightLayout->addLayout(k2Layout); rightLayout->addLayout(k3Layout); rightLayout->addLayout(k4Layout); rightLayout->addStretch(); rightLayout->addLayout(buttonLayout); rightLayout->addStretch();//”пружинка”-расширитель
QVBoxLayout *leftLayout = new QVBoxLayout; leftLayout->addWidget(drawWidget);
QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addWidget(drawWidget); mainLayout->addLayout(rightLayout); setLayout(mainLayout); setWindowTitle(tr("Regress"));//задаем название окна } |
В данном примере мы используем знак амперсанда ('&') для управления фокусом: в строке 9 мы создаем текстовую метку с клавишей быстрого доступа (Alt+1), а в строке 11 устанавливаем строку редактирования в качестве «партнера» этой текстовой метки. Партнером (buddy) называется виджет, на который передается фокус при нажатии клавиши быстрого доступа текстовой метки. Поэтому при нажатии пользователем сочетания клавиш Alt+T (клавиша быстрого доступа текстовой метки) фокус переходит на строку редактирования (которая является партнером текстовой метки).
Также знак амперсанда ('&') используется для задания клавиш быстрого доступа. Например, в строке 36 создается кнопка startButton, которая может быть активирована нажатием пользователем сочетания клавиш Alt+F. В следующей строке мы делаем кнопку startButton используемой по умолчанию, вызывая функцию setDefault(true). Кнопка, для которой задан режим использования по умолчанию, будет срабатывать при нажатии пользователем клавиши Enter.
Private slot start вызывается при нажатии startButton.
Само диалоговое окно закрывается при нажатии пользователем кнопки Close (закрыть). Слот close наследуется от класса QWidget, и по умолчанию он делает виджет невидимым (но не удаляет его).
Программный код слота start рассмотрим позже.
Затем для размещения виджетов в окне мы используем менеджеры компоновки (layout managers). Менеджеры компоновки могут содержать как виджеты, так и другие менеджеры компоновки. Используя различные вложенные комбинации менеджеров компоновки QHBoxLayout, QVBoxLayout и QGridLayout, можно построить очень сложные диалоговые окна.
Для диалогового окна мы используем семь менеджеров горизонтальной компоновки QHBoxLayout и два менеджера вертикальной компоновки QVBoxLayout. Менеджер mainLayout является основным. Одна из особенностей классов менеджеров компоновки заключается в том, что они не являются виджетами. Они происходят от класса QLayout, который в свою очередь происходит от класса QObject.
Далее представлено описание функции start:
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
void regressDialog::start() { double count=false; QString myString=k1Edit->text(); if(myString.toDouble()>0.0)//проверка, что ввели что-нибудь //осмысленное { drawWidget->k1=myString.toDouble(); count=true; } //дальше идут аналогичные проверки myString=k2Edit->text(); if(myString.toDouble()>0.0) { drawWidget->k2=myString.toDouble(); count=true; } myString=k3Edit->text(); if(myString.toDouble()>0.0) { drawWidget->k3=myString.toDouble(); count=true; } myString=k4Edit->text(); if(myString.toDouble()>0.0) { drawWidget->k4=myString.toDouble(); count=true; } myString=tEdit->text(); if(myString.toDouble()>0.0) { drawWidget->T=myString.toDouble(); count=true; }
drawWidget->draw=count; drawWidget->repaint();//вызываем функцию paintEvent, //если count=true, то нарисуем график, иначе ничего не //произойдет } |
Далее создаём main.cpp со следующим содержанием:
1 2 3 4 5 6 7 8 9 10 |
#include <QApplication> #include "regressDialog.h"
int main(int argc, char *argv[]) { QApplication app(argc, argv); regressDialog *dialog = new regressDialog; dialog->show(); return app.exec(); } |
Файлы regressDialog.h, widget.h, regressDialog.срр и widget.cpp, а также main.cpp помещаем в папку regressDialog. В консольном режиме заходим в нее и последовательно выполняем
qmake -project qmake regressDialog.pro make |
И, выполнив ./regressDialog, получим:
Упражнения и контрольные вопросы к главе 4
Что представляет собой библиотека Qt?
Для чего предназначены утилиты a) Qt Assistant, б) Qt Creator, в) Qt Designer?
Какие классы содержит в себе модуль QtGui( 8-10 примеров)?
Найдите ошибку в приведенном ниже примере и объясните её суть:
-
1
2
3
4
5
6
7
8
9
#include<QtGui/QApplication>
#include<QtGui/QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
QLabel label = new QLabel(QObject::tr("В данном примере допущена грубая ошибка"));
label->show();
return app.exec();
}
В каких целях используется класс QPainter?
Когда происходит вызов функции QPaintEvent?
В чем заключается суть регрессионного анализа?
Чему равна сумма квадратов отклонений при единичном взвешивании?
Опишите процедуру нелинейного регрессионного анализа.
Что такое менеджер компоновки? Назовите три основных менеджера компоновки в Qt 4.
Опишите технологию сигналов и слотов.
Для реакций, приведенных в задании 13 ко второй главе, создайте программу, реализующую численное решение дифференциальных уравнений методом Рунге-Кутты, и выводящую график зависимостей концентраций веществ, участвующих в реакции, в зависимости от времени (используйте пример, приведенный в 4.3).
Используя пример, приведенный в 4.4, проведите процедуру регрессионного анализа для любого вещества, участвующего в реакциях, приведенных в задании 13. В качестве входных данных используйте результаты программы из задания 12 данной главы (для численно полученной концентрации прибавьте 5% ошибку, t=10, количество значений концентраций данного вещества – 10).
