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