1 /***************************************************************************
2     File                 : ScreenPickerTool.cpp
3     Project              : QtiPlot
4     --------------------------------------------------------------------
5     Copyright            : (C) 2006,2007 by Ion Vasilief, Knut Franke
6     Email (use @ for *)  : ion_vasilief*yahoo.fr, knut.franke*gmx.de
7     Description          : Tool for selecting arbitrary points on a plot.
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  *                                                                         *
13  *  This program is free software; you can redistribute it and/or modify   *
14  *  it under the terms of the GNU General Public License as published by   *
15  *  the Free Software Foundation; either version 2 of the License, or      *
16  *  (at your option) any later version.                                    *
17  *                                                                         *
18  *  This program is distributed in the hope that it will be useful,        *
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
21  *  GNU General Public License for more details.                           *
22  *                                                                         *
23  *   You should have received a copy of the GNU General Public License     *
24  *   along with this program; if not, write to the Free Software           *
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
26  *   Boston, MA  02110-1301  USA                                           *
27  *                                                                         *
28  ***************************************************************************/
29 #include "ScreenPickerTool.h"
30 #include <ApplicationWindow.h>
31 #include <Table.h>
32 #include <Matrix.h>
33 #include <Graph.h>
34 #include <PlotCurve.h>
35 #include <MultiLayer.h>
36 #include <SymbolBox.h>
37 #include <qwt_symbol.h>
38 #include <qwt_scale_widget.h>
39 
40 #include <QLayout>
41 #include <QApplication>
42 
43 ScreenPickerTool::ScreenPickerTool(Graph *graph, const QObject *status_target, const char *status_slot)
44 	: QwtPlotPicker(graph->canvas()),
45 	PlotToolInterface(graph, status_target, status_slot),
46 	d_move_restriction(NoRestriction)
47 {
48 	d_selection_marker.setLineStyle(QwtPlotMarker::Cross);
49 	d_selection_marker.setLinePen(QPen(Qt::red, 1));
50 	setTrackerMode(QwtPicker::AlwaysOn);
51 	setSelectionFlags(QwtPicker::PointSelection | QwtPicker::ClickSelection);
52 	d_graph->canvas()->setCursor(QCursor(QPixmap(":/cursor.png"), -1, -1));
53 
54 	if (status_target)
plot()55 		connect(this, SIGNAL(statusText(const QString&)), status_target, status_slot);
56 	emit statusText(tr("Click on plot or move cursor to display coordinates!"));
formatString()57 }
58 
59 ScreenPickerTool::~ScreenPickerTool()
60 {
61 	d_selection_marker.detach();
62 	d_graph->canvas()->unsetCursor();
63 	d_graph->replot();
64 }
65 
labelNumericPrecision()66 void ScreenPickerTool::append(const QPoint &point)
67 {
68 	append(invTransform(point));
69 }
setMajorTicksStyle(TicksStyle type)70 
71 void ScreenPickerTool::append(const QwtDoublePoint &pos)
72 {
73 	double x0 = d_selection_marker.xValue();//old position
74 	double y0 = d_selection_marker.yValue();
75 
76 	switch(d_move_restriction){
77 		case NoRestriction:
78 			d_selection_marker.setValue(pos);
79 		break;
80 
81 		case Vertical:
82 			d_selection_marker.setYValue(pos.y());
83 		break;
84 
85 		case Horizontal:
86 			d_selection_marker.setXValue(pos.x());
87 		break;
88 	}
89 
90 	double x = d_selection_marker.xValue();
91 	double y = d_selection_marker.yValue();
92 	double dx = fabs(x - x0);
93 	double dy = fabs(y - y0);
94 	if (d_selection_marker.plot() == NULL){
95 		d_selection_marker.attach(d_graph);
96 		dx = 0;
97 		dy = 0;
98 	}
99 
100 	QLocale locale = d_graph->multiLayer()->locale();
101 	if (d_move_restriction)
102 		emit statusText(QString("x=%1; y=%2")
103 			.arg(locale.toString(x, 'G', 14))
104 			.arg(locale.toString(y, 'G', 14)));
105 	else
106 		emit statusText(QString("x=%1; y=%2; dx=%3; dy=%4")
107 				.arg(locale.toString(x, 'G', 14))
108 				.arg(locale.toString(y, 'G', 14))
109 				.arg(locale.toString(dx, 'G', 14))
110 				.arg(locale.toString(dy, 'G', 14)));
111 
112 	d_graph->replot();
113 }
114 
115 bool ScreenPickerTool::eventFilter(QObject *obj, QEvent *event)
116 {
117 	switch(event->type()) {
118 		case QEvent::MouseButtonDblClick:
119 			emit selected(d_selection_marker.value());
120 			return true;
121 
122 		case QEvent::KeyPress:
123 			{
124 				QKeyEvent *ke = (QKeyEvent*) event;
125 				switch(ke->key()) {
126 					case Qt::Key_Enter:
127 					case Qt::Key_Return:
128 					{
129                         QwtDoublePoint pos = invTransform(canvas()->mapFromGlobal(QCursor::pos()));
130 						append(pos);
131 						emit selected(pos);
132 						return true;
133 					}
134 					default:
135 						break;
136 				}
137 			}
138 		default:
139 			break;
140 	}
141 	return QwtPlotPicker::eventFilter(obj, event);
142 }
143 
144 DrawPointTool::DrawPointTool(ApplicationWindow *app, Graph *graph, const QObject *status_target, const char *status_slot)
145 	: ScreenPickerTool(graph, status_target, status_slot),
146 	d_app(app)
147 {
148 	d_curve = NULL;
149 	d_table = NULL;
150 }
151 
152 void DrawPointTool::appendPoint(const QwtDoublePoint &pos)
153 {
154 	if (!d_app)
155 		return;
156 
157 	QLocale locale = d_app->locale();
158 	int prec = d_app->d_decimal_digits;
159 	emit statusText(QString("x=%1; y=%2")
160 					.arg(locale.toString(pos.x(), 'G', prec))
161 					.arg(locale.toString(pos.y(), 'G', prec)));
162 
163 	if (!d_table){
164 		d_table = d_app->newHiddenTable(d_app->generateUniqueName(tr("Draw")), "", 30, 2, "");
165 		d_app->modifiedProject();
166 	}
167 
168 	int rows = 0;
169 	if (d_curve)
170 		rows = d_curve->dataSize();
171 
172 	if (d_table->numRows() <= rows)
173 		d_table->setNumRows(rows + 10);
174 
175 	d_table->setCell(rows, 0, pos.x());
176 	d_table->setCell(rows, 1, pos.y());
177 
178 	if (!d_curve){
179 		d_curve = new DataCurve(d_table, d_table->colName(0), d_table->colName(1));
180 		d_curve->setAxis(QwtPlot::xBottom, QwtPlot::yLeft);
181 		d_curve->setPen(QPen(Qt::black, d_app->defaultCurveLineWidth));
182 		d_curve->setSymbol(QwtSymbol(QwtSymbol::Ellipse, QBrush(Qt::black),
183 						  QPen(Qt::black, d_app->defaultCurveLineWidth),
184 						  QSize(d_app->defaultSymbolSize, d_app->defaultSymbolSize)));
185 		d_graph->insertPlotItem(d_curve, Graph::LineSymbols);
186 	}
187 
188 	d_curve->setFullRange();
189 	d_graph->updatePlot();
190 }
191 
192 bool DrawPointTool::eventFilter(QObject *obj, QEvent *event)
193 {
194 	switch(event->type()) {
195 		case QEvent::MouseButtonDblClick:
196 			appendPoint(d_selection_marker.value());
197 			return true;
198 		case QEvent::KeyPress:
199 			{
200 				QKeyEvent *ke = (QKeyEvent*) event;
201 				switch(ke->key()) {
202 					case Qt::Key_Enter:
203 					case Qt::Key_Return:
204 					{
205                         QwtDoublePoint pos = invTransform(canvas()->mapFromGlobal(QCursor::pos()));
206                         d_selection_marker.setValue(pos);
207                         if (d_selection_marker.plot() == NULL)
208                             d_selection_marker.attach(d_graph);
209                         d_graph->replot();
210 						emit selected(d_selection_marker.value());
211 
212 						appendPoint(pos);
213 						return true;
214 					}
215 					default:
216 						break;
217 				}
218 			}
219 		default:
220 			break;
221 	}
222 	return QwtPlotPicker::eventFilter(obj, event);
223 }
224 
225 ImageProfilesTool::ImageProfilesTool(ApplicationWindow *app, Graph *graph, Matrix *m, Table *horTable, Table *verTable)
226 	: ScreenPickerTool(graph, app->infoLineEdit(), SLOT(setText(const QString&))),
227 	d_app(app),
228 	d_matrix(m),
229 	d_hor_table(horTable),
230 	d_ver_table(verTable),
231 	d_box(NULL)
232 {
233 	d_selection_marker.setAxis(QwtPlot::xTop, QwtPlot::yLeft);
234 
235 	if (d_matrix){
236 		double xVal = 0.5*(m->xStart() + m->xEnd());
237 		double yVal = 0.5*(m->yStart() + m->yEnd());
238 		if (d_graph){
239 			connect(d_matrix, SIGNAL(destroyed()), d_graph, SLOT(disableImageProfilesTool()));
240 			connect(d_matrix, SIGNAL(modifiedData(Matrix *)), this, SLOT(modifiedMatrix(Matrix *)));
241 
242 			averageBox = new QSpinBox;
243 			averageBox->setMinimum(1);
244 			averageBox->setSuffix(" " + tr("pixels"));
245 
246 			horSpinBox = new DoubleSpinBox('g');
247 			horSpinBox->setMinimumWidth(80);
248 			horSpinBox->setSingleStep(1.0);
249 			horSpinBox->setLocale(QLocale());
250 			horSpinBox->setDecimals(6);
251 
252 			vertSpinBox = new DoubleSpinBox('g');
253 			vertSpinBox->setMinimumWidth(80);
254 			vertSpinBox->setSingleStep(1.0);
255 			vertSpinBox->setLocale(QLocale());
256 			vertSpinBox->setDecimals(6);
257 
258 			zLabel = new QLabel();
259 			zLabel->setMinimumWidth(80);
260 			zLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
261 
262 			append(QwtDoublePoint(xVal, yVal));
263 
264 			connect(averageBox, SIGNAL(valueChanged(int)), this, SLOT(updateCursorWidth(int)));
265 			connect(horSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updateCursorPosition()));
266 			connect(vertSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updateCursorPosition()));
267 
268 			MultiLayer *plot = d_graph->multiLayer();
269 			if (plot){
270 				QPalette pal = plot->palette();
271 				pal.setColor(QPalette::Window, QColor(Qt::lightGray));
272 				plot->setPalette(pal);
273 
274 				d_box = new QWidget();
275 				QHBoxLayout *hl = new QHBoxLayout(d_box);
276 				hl->setSpacing(5);
277 				hl->addWidget(new QLabel(tr("Average")));
278 				hl->addWidget(averageBox);
279 				hl->addWidget(new QLabel(tr("Position") + ": " + tr("x")));
280 				hl->addWidget(horSpinBox);
281 				hl->addWidget(new QLabel(tr("y")));
282 				hl->addWidget(vertSpinBox);
283 				hl->addWidget(new QLabel(tr("Z-Value")));
284 				hl->addWidget(zLabel);
285 
286 				plot->toolBox()->insertWidget(0, d_box);
287 			}
288 		}
289 
290 		MatrixModel *mm = d_matrix->matrixModel();
291 		if (d_hor_table){
292 			for (int i = 0; i < d_hor_table->numRows(); i++)
293 				d_hor_table->setCell(i, 0, mm->x(i));
294 		}
295 		if (d_ver_table){
296 			for (int i = 0; i < d_ver_table->numRows(); i++)
297 				d_ver_table->setCell(i, 0, mm->y(i));
298 		}
299 	}
300 }
301 
302 void ImageProfilesTool::connectPlotLayers()
303 {
304 	if (!d_graph)
305 		return;
306 
307 	MultiLayer *plot = d_graph->multiLayer();
308 	if (!plot)
309 		return;
310 
311 	Graph *gHor = plot->layer(2);
312 	if (gHor)
313 		gHor->addCurves(d_hor_table, QStringList(d_hor_table->colName(1)));
314 
315 	Graph *gVert = plot->layer(3);
316 	if (gVert){
317 		DataCurve *c = gVert->insertCurve(d_ver_table, d_ver_table->colName(1), d_ver_table->colName(0), Graph::Line);
318 		if (c){
319 			c->setAxis(QwtPlot::xTop, QwtPlot::yLeft);
320 			c->setCurveType(QwtPlotCurve::Xfy);
321 		}
322 	}
323 }
324 
325 void ImageProfilesTool::updateCursorPosition()
326 {
327 	append(QwtDoublePoint(horSpinBox->value(), vertSpinBox->value()));
328 
329 	if (d_graph)
330 		d_graph->replot();
331 }
332 
333 void ImageProfilesTool::setAveragePixels(int pixels)
334 {
335 	averageBox->blockSignals(true);
336 	averageBox->setValue(pixels);
337 	averageBox->blockSignals(false);
338 	setCursorWidth(pixels);
339 }
340 
341 void ImageProfilesTool::setCursorWidth(int width)
342 {
343 	QBrush br = QBrush(Qt::red);
344 	if (width > 1){
345 		QColor c = Qt::red;
346 		c.setAlphaF(0.25);
347 		br.setColor(c);
348 	}
349 	d_selection_marker.setLinePen(QPen(br, width));
350 }
351 
352 void ImageProfilesTool::updateCursorWidth(int width)
353 {
354 	setCursorWidth(width);
355 	if (d_graph)
356 		d_graph->replot();
357 	append(QwtDoublePoint(horSpinBox->value(), vertSpinBox->value()));
358 }
359 
360 void ImageProfilesTool::modifiedMatrix(Matrix *m)
361 {
362 	if (!m)
363 		return;
364 
365 	double mmin, mmax;
366 	m->range(&mmin, &mmax);
367 	mmin = floor(mmin);
368 	mmax = ceil(mmax);
369 
370 	MatrixModel *mm = m->matrixModel();
371 	if (d_hor_table){
372 		d_hor_table->setNumRows(m->numCols());
373 		for (int i = 0; i < d_hor_table->numRows(); i++)
374 			d_hor_table->setCell(i, 0, mm->x(i));
375 	}
376 	if (d_ver_table){
377 		d_ver_table->setNumRows(m->numRows());
378 		for (int i = 0; i < d_ver_table->numRows(); i++)
379 			d_ver_table->setCell(i, 0, mm->y(i));
380 	}
381 
382 	if (d_graph){
383 		d_graph->setScale(QwtPlot::yLeft, QMIN(m->yStart(), m->yEnd()), QMAX(m->yStart(), m->yEnd()),
384 					0.0, 5, 5, Graph::Linear, true);
385 		d_graph->setScale(QwtPlot::xTop, QMIN(m->xStart(), m->xEnd()), QMAX(m->xStart(), m->xEnd()));
386 		d_graph->replot();
387 
388 		MultiLayer *plot = d_graph->multiLayer();
389 		Graph *gHor = plot->layer(2);
390 		if (gHor){
391 			gHor->setScale(QwtPlot::xBottom, QMIN(m->xStart(), m->xEnd()), QMAX(m->xStart(), m->xEnd()));
392 			gHor->setScale(QwtPlot::yLeft, mmin, mmax);
393 			gHor->replot();
394 		}
395 
396 		Graph *gVert = plot->layer(3);
397 		if (gVert){
398 			gVert->setScale(QwtPlot::xTop, mmin, mmax);
399 			gVert->setScale(QwtPlot::yLeft, QMIN(m->yStart(), m->yEnd()), QMAX(m->yStart(), m->yEnd()),
400 					0.0, 5, 5, Graph::Linear, true);
401 			gVert->replot();
402 		}
403 	}
404 
405 	append(QwtDoublePoint(d_selection_marker.xValue(), d_selection_marker.yValue()));
406 }
407 
408 ImageProfilesTool* ImageProfilesTool::clone(Graph *g)
409 {
410 	if (!d_matrix || !d_app)
411 		return 0;
412 
413 	Table *hTable = d_app->newHiddenTable(QString::null, QString::null, d_matrix->numCols(), 2);
414 	Table *vTable = d_app->newHiddenTable(QString::null, QString::null, d_matrix->numRows(), 2);
415 
416 	ImageProfilesTool *tool = new ImageProfilesTool(d_app, g, d_matrix, hTable, vTable);
417 	tool->setAveragePixels(averageBox->value());
418 	if (g && g->multiLayer()){
419 		Graph *gHor = g->multiLayer()->layer(2);
420 		if (gHor)
421 			gHor->removeCurve(0);
422 
423 		Graph *gVert = g->multiLayer()->layer(3);
424 		if (gVert)
425 			gVert->removeCurve(0);
426 	}
427 	tool->connectPlotLayers();
428 	tool->append(QwtDoublePoint(xValue(), yValue()));
429 	return tool;
430 }
431 
432 void ImageProfilesTool::append(const QwtDoublePoint &pos)
433 {
434 	ScreenPickerTool::append(pos);
435 
436 	if (!d_app || !d_matrix)
437 		return;
438 
439 	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
440 
441 	MultiLayer *plot = d_graph->multiLayer();
442 	Graph *gHor = plot->layer(2);
443 	if (gHor)
444 		gHor->enableAutoscaling(false);
445 
446 	Graph *gVert = plot->layer(3);
447 	if (gVert){
448 		gVert->enableAutoscaling(false);
449 		QwtPlotCurve *c = gVert->curve(0);
450 		if (c)
451 			c->setCurveType(QwtPlotCurve::Xfy);
452 	}
453 
454 	double x = pos.x();
455 	double y = pos.y();
456 
457 	horSpinBox->setValue(x);
458 	vertSpinBox->setValue(y);
459 
460 	x += 0.5*d_matrix->dx();
461 	y -= 0.5*d_matrix->dy();
462 
463 	int row = qRound(fabs((y - d_matrix->yStart())/d_matrix->dy()));
464 	int col = qRound(fabs((x - d_matrix->xStart())/d_matrix->dx()));
465 
466 	int rows = d_matrix->numRows();
467 	int cols = d_matrix->numCols();
468 
469 	if (row < 0)
470 		row = 0;
471 	else if (row >= rows)
472 		row = rows - 1;
473 
474 	if (col < 0)
475 		col = 0;
476 	else if (col >= cols)
477 		col = cols - 1;
478 
479 	zLabel->setText(QLocale().toString(d_matrix->cell(row, col)));
480 
481 	int pixels = averageBox->value();
482 	if (d_hor_table){
483 		if (d_hor_table->numRows() != cols)
484 			d_hor_table->setNumRows(cols);
485 		if (pixels > 1){
486 			int endPixel = (pixels%2) ? row + pixels/2 : row + pixels/2 - 1;
487 			for (int i = 0; i < cols; i++){
488 				double val = 0.0;
489 				int n = pixels;
490 				for (int j = row - pixels/2; j <= endPixel; j++){
491 					if (j < 0 || j >= rows){
492 						n--;
493 						continue;
494 					}
495 					val += d_matrix->cell(j, i);
496 				}
497 				d_hor_table->setCell(i, 1, val/(double)n);
498 			}
499 		} else {
500 			for (int i = 0; i < cols; i++)
501 				d_hor_table->setCell(i, 1, d_matrix->cell(row, i));
502 		}
503 		d_hor_table->notifyChanges();
504 	}
505 
506 	if (d_ver_table){
507 		if (d_ver_table->numRows() != rows)
508 			d_ver_table->setNumRows(rows);
509 		if (pixels > 1){
510 			int endPixel = (pixels%2) ? col + pixels/2 : col + pixels/2 - 1;
511 			for (int i = 0; i < rows; i++){
512 				double val = 0.0;
513 				int n = pixels;
514 				for (int j = col - pixels/2; j <= endPixel; j++){
515 					if (j < 0 || j >= cols){
516 						n--;
517 						continue;
518 					}
519 					val += d_matrix->cell(i, j);
520 				}
521 				d_ver_table->setCell(i, 1, val/(double)n);
522 			}
523 		} else {
524 			for (int i = 0; i < rows; i++)
525 				d_ver_table->setCell(i, 1, d_matrix->cell(i, col));
526 		}
527 		d_ver_table->notifyChanges();
528 	}
529 
530 	QLocale locale = d_app->locale();
531 	int prec = d_app->d_decimal_digits;
532 	emit statusText(QString("x=%1; y=%2; z=%3")
533 					.arg(locale.toString(pos.x(), 'G', prec))
534 					.arg(locale.toString(pos.y(), 'G', prec))
535 					.arg(locale.toString(d_matrix->cell(row, col), 'G', prec)));
536 
537 	QApplication::restoreOverrideCursor();
538 }
539 
540 ImageProfilesTool::~ImageProfilesTool()
541 {
542 	if (d_hor_table){
543 		d_hor_table->askOnCloseEvent(false);
544 		d_hor_table->close();
545 	}
546 
547 	if (d_ver_table){
548 		d_ver_table->askOnCloseEvent(false);
549 		d_ver_table->close();
550 	}
551 
552 	if (d_box){
553 		delete d_box;
554 		d_box = 0;
555 	}
556 }
557