Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Уч_пос_ГИМн_ИАС_ОЗХК_2011.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
4.53 Mб
Скачать

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). Тогда требуется выполнить следующие шаги:

  1. Вычислить функцию и p частных производных

  1. Сформировать матрицу P и вектор Z.

  2. Вычислить обратную матрицу и вектор поправочных членов A

  1. Проверить критерий сходимости. Если критерий не выполняется, то повторить процесс.

Приведенная ниже программа позволяет проводить построение нелинейной сглаживающей кривой.

Работа программы 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

  1. Что представляет собой библиотека Qt?

  2. Для чего предназначены утилиты a) Qt Assistant, б) Qt Creator, в) Qt Designer?

  3. Какие классы содержит в себе модуль QtGui( 8-10 примеров)?

  4. Найдите ошибку в приведенном ниже примере и объясните её суть:

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();

}

  1. В каких целях используется класс QPainter?

  2. Когда происходит вызов функции QPaintEvent?

  3. В чем заключается суть регрессионного анализа?

  4. Чему равна сумма квадратов отклонений при единичном взвешивании?

  5. Опишите процедуру нелинейного регрессионного анализа.

  6. Что такое менеджер компоновки? Назовите три основных менеджера компоновки в Qt 4.

  7. Опишите технологию сигналов и слотов.

  8. Для реакций, приведенных в задании 13 ко второй главе, создайте программу, реализующую численное решение дифференциальных уравнений методом Рунге-Кутты, и выводящую график зависимостей концентраций веществ, участвующих в реакции, в зависимости от времени (используйте пример, приведенный в 4.3).

  9. Используя пример, приведенный в 4.4, проведите процедуру регрессионного анализа для любого вещества, участвующего в реакциях, приведенных в задании 13. В качестве входных данных используйте результаты программы из задания 12 данной главы (для численно полученной концентрации прибавьте 5% ошибку, t=10, количество значений концентраций данного вещества – 10).