Выбрать главу

243 if (!rect.isValid())

244 return;

245 painter->setClipRect(rect.adjusted(+1, +1, -1, -1));

246 QMapIterator<int, QVector<QPointF> > i(curveMap);

247 while (i.hasNext()) {

248 i.next();

249 int id = i.key();

250 const QVector<QPointF> &data = i.value();

251 QPolygonF polyline(data.count());

252 for (int j = 0; j < data.count(); ++j) {

253 double dx = data[j].x() - settings.minX;

254 double dy = data[j].y() - settings.minY;

255 double x = rect.left() + (dx * (rect.width() - 1)

256 / settings.spanX());

257 double у = rect.bottom() - (dy * (rect.height() - 1)

258 / settings.spanY());

259 polyline[j] = QPointF(x, у);

260 }

261 painter->setPen(colorForIds[uint(id) % 6]);

262 painter->drawPolyline(polyline);

263 }

264 }

Функция drawCurves() рисует кривые поверх сетки. Мы начинаем с вызова функции setClipRect для ограничения области отображения QPainter прямоугольником, содержащим кривые (без окаймляющей кромки и рамки вокруг графика). После этого QPainter будет игнорировать вывод пикселей вне этой области.

Затем мы выполняем цикл по всем кривым, используя итератор в стиле Java, и для каждой кривой мы выполняем цикл по ее точкам QPointF. Функция key() позволяет получить идентификатор кривой, а функция value() — данные соответствующей кривой в виде вектора QVector<QPointF>. Внутри цикла for производятся преобразование всех точек QPointF из системы координат построителя графика в систему координат виджета и сохранение их в переменной polyline.

После преобразования всех точек кривой в систему координат виджета мы устанавливаем цвет пера для кривой (используя один из наборов заранее определенных цветов) и вызываем drawPolyline() для вычерчивания линии, которая проходит по всем точкам кривой.

Этим мы завершаем построение класса Plotter. Остается только рассмотреть несколько функций настроек графика PlotSettings.

265 PlotSettings::PlotSettings()

266 {

267 minX = 0.0;

268 maxX = 10.0;

269 numXTicks = 5;

270 minY = 0.0;

271 maxY = 10.0;

272 numYTicks = 5;

273 }

Конструктор PlotSettings инициализирует обе оси координат диапазоном от 0 до 10 с пятью отметками.

274 void PlotSettings::scroll(int dx, int dy)

275 {

276 double stepX = spanX() / numXTicks;

277 minX += dx * stepX;

278 maxX += dx * stepX;

279 double stepY = spanY() / numYTicks;

280 minY += dy * stepY;

281 maxY += dy *stepY;

282 }

Функция scroll() увеличивает (или уменьшает) minX, maxX, minY и maxY на интервал между двух отметок, помноженный на заданное число. Данная функция применяется для реализации скроллинга в функции Plotter::keyPressEvent().

283 void PlotSettings::adjust()

284 {

285 adjustAxis(minX, maxX, numXTicks);

286 adjustAxis(minY, maxY, numYTicks);

287 }

Функция adjust() вызывается из mouseReleaseEvent() для округления значений minX, maxX, minY и maxY, чтобы получить «удобные» значения, и определения количества меток на каждой оси. Закрытая фyнкция adjustAxis() выполняет эти действия отдельно для каждой оси.

288 void PlotSettings::adjustAxis(double &min, double &max, int &numTiсks)

289 {

290 const int MinTicks = 4;

291 double grossStep = (max - min) / MinTicks;

292 double step = pow(10.0, floor(log10(grossStep)));

293 if (5 * step < grossStep) {

294 step *= 5;

295 } else if (2* step < grossStep) {

296 step *= 2;

297 }

298 numTicks = int (ceil(max / step) - floor(min / step));

299 if (numTicks < MinTicks)

300 numTicks = MinTicks;

301 min = floor(min / step) * step;

302 max = ceil(max / step) * step;

303 }

Функция adjustAxis() преобразует свои параметры min и max в «удобные» числа и устанавливает свой параметр numTicks на количество меток, которое, по ее расчету, подходит для заданного диапазона [min, max]. Поскольку в функции adjustAxis() фактически требуется модифицировать переменные (minX, maxX, numXTicks и так далее), а не просто копировать их, для этих параметров не используется модификатор const. Большая часть программного кода в adjustAxis() предназначена просто для определения соответствующего значения интервала между двумя метками (переменная step — шаг). Для получения на оси удобных чисел мы должнытщательно выбирать этот шаг. Например, значение шага 3.8 привело бы к появлению на оси чисел, кратных 3.8, что затрудняет восприятие диаграммы человеком. Для осей с десятичной системой обозначения «удобными» значениями шага являются числа вида 10n, 2 • 10n или 5 • 10n.