Engauge Digitizer 2
Loading...
Searching...
No Matches
GraphicsView.cpp
Go to the documentation of this file.
1/******************************************************************************************************
2 * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3 * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4 * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5 ******************************************************************************************************/
6
7#include "DataKey.h"
8#include "EngaugeAssert.h"
10#include "GraphicsItemType.h"
11#include "GraphicsView.h"
12#include "LoadFileInfo.h"
13#include "Logger.h"
14#include "MainWindow.h"
15#include "Point.h"
16#include <QApplication>
17#include <QContextMenuEvent>
18#include <QDebug>
19#include <QDropEvent>
20#include <QGraphicsPixmapItem>
21#include <QGraphicsPolygonItem>
22#include <QGraphicsScene>
23#include <QMimeData>
24#include <QMouseEvent>
25#include <QScrollBar>
26#include "QtToString.h"
27
28extern const QString AXIS_CURVE_NAME;
29
30GraphicsView::GraphicsView(QGraphicsScene *scene,
31 MainWindow &mainWindow) :
32 QGraphicsView (scene)
33{
34 connect (this, SIGNAL (signalContextMenuEventAxis (QString)), &mainWindow, SLOT (slotContextMenuEventAxis (QString)));
35 connect (this, SIGNAL (signalContextMenuEventGraph (QStringList)), &mainWindow, SLOT (slotContextMenuEventGraph (QStringList)));
36 connect (this, SIGNAL (signalDraggedDigFile (QString)), &mainWindow, SLOT (slotFileOpenDraggedDigFile (QString)));
37 connect (this, SIGNAL (signalDraggedImage (QImage)), &mainWindow, SLOT (slotFileImportDraggedImage (QImage)));
38 connect (this, SIGNAL (signalDraggedImageUrl (QUrl)), &mainWindow, SLOT (slotFileImportDraggedImageUrl (QUrl)));
39 connect (this, SIGNAL (signalKeyPress (Qt::Key, bool)), &mainWindow, SLOT (slotKeyPress (Qt::Key, bool)));
40 connect (this, SIGNAL (signalMouseMove(QPointF)), &mainWindow, SLOT (slotMouseMove (QPointF)));
41 connect (this, SIGNAL (signalMousePress (QPointF)), &mainWindow, SLOT (slotMousePress (QPointF)));
42 connect (this, SIGNAL (signalMouseRelease (QPointF)), &mainWindow, SLOT (slotMouseRelease (QPointF)));
43 connect (this, SIGNAL (signalViewZoomIn ()), &mainWindow, SLOT (slotViewZoomInFromWheelEvent ()));
44 connect (this, SIGNAL (signalViewZoomOut ()), &mainWindow, SLOT (slotViewZoomOutFromWheelEvent ()));
45
46 setMouseTracking (true);
47 setAcceptDrops (true);
48 setEnabled (true);
49 setRenderHints(QPainter::Antialiasing);
50 setBackgroundBrush (QBrush (QColor (Qt::gray)));
51 verticalScrollBar()->setCursor (QCursor (Qt::ArrowCursor));
52 horizontalScrollBar()->setCursor (QCursor (Qt::ArrowCursor));
53
54 // Skip setStatusTip here since that will overwrite much more important messages, and trigger gratuitous showing of status bar
55 setWhatsThis (tr ("Main Window\n\n"
56 "After an image file is imported, or an Engauge Document opened, an image appears in this area. "
57 "Points are added to the image.\n\n"
58 "If the image is a graph with two axes and one or more curves, then three axis points must be "
59 "created along those axes. Just put two axis points on one axis and a third axis point on the other "
60 "axis, as far apart as possible for higher accuracy. Then curve points can be added along the curves.\n\n"
61 "If the image is a map with a scale to define length, then two axis points must be "
62 "created at either end of the scale. Then curve points can be added.\n\n"
63 "Zooming the image in or out is performed using any of several methods:\n"
64 "1) rotating the mouse wheel when the cursor is outside of the image\n"
65 "2) pressing the minus or plus keys\n"
66 "3) selecting a new zoom setting from the View/Zoom menu"));
67}
68
72
74{
75 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::contextMenuEvent"
76 << " selectedCount=" << scene()->selectedItems().count();
77
79 const QList<QGraphicsItem*> &items = scene()->selectedItems();
81
82 if (pointIdentifiers.count() > 0) {
83
84 if (graphicsItemsExtractor.allSelectedItemsAreEitherAxisOrGraph (items,
85 GRAPH_POINTS)) {
86
87 // One or more graph points are selected so edit their coordinates
89
90 } else if (graphicsItemsExtractor.allSelectedItemsAreEitherAxisOrGraph (items,
91 AXIS_POINTS) && pointIdentifiers.count() == 1) {
92
93 // A single axis point is selected so edit it
95
96 }
97 }
98
99 QGraphicsView::contextMenuEvent (event);
100}
101
103{
104 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dragEnterEvent " << (event->mimeData ()->hasUrls () ? "urls" : "non-urls");
105
106 if (event->mimeData ()->hasImage () ||
107 event->mimeData ()->hasUrls ()) {
108 event->acceptProposedAction();
109 }
110}
111
113{
114 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dragMoveEvent";
115
116 if (event->mimeData ()->hasImage () ||
117 event->mimeData ()->hasUrls ()) {
118 event->acceptProposedAction();
119 }
120}
121
123{
124 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dropEvent";
125
126 // Urls from text/uri-list
127 QList<QUrl> urlList = event->mimeData ()->urls ();
128
129 const QString MIME_FORMAT_TEXT_PLAIN ("text/plain");
130 QString textPlain (event->mimeData()->data (MIME_FORMAT_TEXT_PLAIN));
131
133 if (event->mimeData ()->hasUrls () &&
134 urlList.count () > 0) {
135 urlFirst = urlList.at (0);
136 }
137
138 QImage image;
139 if (event->mimeData()->hasImage()) {
140 image = qvariant_cast<QImage> (event->mimeData ()->imageData ());
141 }
142
143 if (handleDropEvent (textPlain,
144 event->mimeData ()->hasUrls (),
145 urlFirst,
146 event->mimeData ()->hasImage (),
147 image)) {
148
149 event->acceptProposedAction();
150
151 } else {
152
153 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dropEvent dropped";
154 QGraphicsView::dropEvent (event);
155 }
156}
157
158bool GraphicsView::handleDropEvent (const QString &possibleDigFileName,
159 bool hasUrl,
160 const QUrl &urlFirst,
161 bool hasImage,
162 const QImage &image)
163{
164 bool willAccept = false;
165
167 if (loadFileInfo.loadsAsDigFile (possibleDigFileName)) {
168
169 // Branch that applies when a dig file name has been dropped
170 LOG4CPP_INFO_S ((*mainCat)) << "QGraphicsView::handleDropEvent dig file";
172 emit signalDraggedDigFile (url.toLocalFile());
173 willAccept = true;
174
175 } else if (hasImage) {
176
177 // Branch that applies when an image selected within another application (e.g. LibreOffice Draw) has been dropped
178 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::handleDropEvent image";
179 emit signalDraggedImage (image);
180 willAccept = true;
181
182 } else if (hasUrl) {
183
184 // Branch that applies when a local file name or internet url has been dropped
185 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::handleDropEvent url=" << urlFirst.toString ().toLatin1 ().data ();
187 willAccept = true;
188 }
189
190 return willAccept;
191}
192
193bool GraphicsView::inBounds (const QPointF &posScreen)
194{
195 QRectF boundingRect = scene()->sceneRect();
196
197 return 0 <= posScreen.x () &&
198 0 <= posScreen.y () &&
199 posScreen.x () < boundingRect.width() &&
200 posScreen.y () < boundingRect.height();
201}
202
204{
205 LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::keyPressEvent";
206
207 // Intercept up/down/left/right if any items are selected
208 Qt::Key key = static_cast<Qt::Key> (event->key());
209
210 bool atLeastOneSelectedItem = (scene ()->selectedItems ().count () > 0);
211
212 if (key == Qt::Key_Down ||
213 key == Qt::Key_Left ||
214 key == Qt::Key_Right ||
215 key == Qt::Key_Up) {
216
218 event->accept();
219
220 } else {
221
222 QGraphicsView::keyPressEvent (event);
223
224 }
225}
226
228{
229// LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::mouseMoveEvent cursor="
230// << QtCursorToString (cursor().shape()).toLatin1 ().data ();
231
232 QPointF posScreen = mapToScene (event->pos ());
233
234 if (!inBounds (posScreen)) {
235
236 // Set to out-of-bounds value
237 posScreen = QPointF (-1.0, -1.0);
238 }
239
240 emit signalMouseMove (posScreen);
241
242 QGraphicsView::mouseMoveEvent (event);
243}
244
246{
247 LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::mousePressEvent";
248
249 QPointF posScreen = mapToScene (event->pos ());
250
251 if (!inBounds (posScreen)) {
252
253 // Set to out-of-bounds value
254 posScreen = QPointF (-1.0, -1.0);
255 }
256
257 emit signalMousePress (posScreen);
258
259 QGraphicsView::mousePressEvent (event);
260}
261
263{
264 LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::mouseReleaseEvent signalMouseRelease";
265
266 QPointF posScreen = mapToScene (event->pos ());
267
268 if (!inBounds (posScreen)) {
269
270 // Set to out-of-bounds value
271 posScreen = QPointF (-1.0, -1.0);
272 }
273
274 // Send a signal, unless this is a right click. We still send if out of bounds since
275 // a click-and-drag often ends out of bounds (and user is unlikely to expect different
276 // behavior when endpoint is outside, versus inside, the image boundary)
277 int bitFlag = (unsigned (event->buttons ()) & Qt::RightButton);
278 bool isRightClick = (bitFlag != 0);
279
280 if (!isRightClick) {
281
282 emit signalMouseRelease (posScreen);
283
284 }
285
286 QGraphicsView::mouseReleaseEvent (event);
287}
288
289QStringList GraphicsView::pointIdentifiersFromSelection (const QList<QGraphicsItem*> &items) const
290{
291 // This method assumes that all specified items are points
292
294
295 QList<QGraphicsItem*>::const_iterator itr;
296 for (itr = items.begin(); itr != items.end(); itr++) {
297
298 QGraphicsItem *item = *itr;
299 GraphicsItemType type = static_cast<GraphicsItemType> (item->data (DATA_KEY_GRAPHICS_ITEM_TYPE).toInt ());
301
302 QString pointIdentifier = item->data (DATA_KEY_IDENTIFIER).toString ();
304 }
305
306 return pointIdentifiers;
307}
308
310{
311 // Regression test with local file or internet url specified
313 bool hasUrl = true;
314 QUrl url (urlText); // Works as is for internet url
315 if (!urlText.contains ("http")) {
316 url = QUrl::fromLocalFile (urlText);
317 }
318 bool hasImage = false;
320
321 handleDropEvent (emptyDigFileName,
322 hasUrl,
323 url,
324 hasImage,
325 emptyImage);
326}
327
329{
330 const int ANGLE_THRESHOLD = 15; // From QWheelEvent documentation
331 const int DELTAS_PER_DEGREE = 8; // From QWheelEvent documentation
332
333 QPoint numDegrees = event->angleDelta() / DELTAS_PER_DEGREE;
334
335 LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::wheelEvent"
336 << " degrees=" << numDegrees.y()
337 << " phase=" << event->phase();
338
339 // Criteria:
340 // 1) User has enabled wheel zoom control (but that is not known here so MainWindow will handle that part)
341 // in slotViewZoomInFromWheelEvent and slotViewZoomOutFromWheelEvent
342 // 2) Angle is over a threshold to eliminate false events from just touching wheel
343 if ((event->modifiers() & Qt::ControlModifier) != 0) {
344
345 if (numDegrees.y() >= ANGLE_THRESHOLD) {
346
347 // Rotated backwards towards the user, which means zoom in
349
350 } else if (numDegrees.y() <= -ANGLE_THRESHOLD) {
351
352 // Rotated forwards away from the user, which means zoom out
354
355 }
356
357 // Accept the event as long as Control key was used and we are capturing wheel event
358 event->accept();
359
360 } else {
361
362 // Let non-Control events manage scrolling
363 QGraphicsView::wheelEvent (event);
364
365 }
366}
@ DATA_KEY_GRAPHICS_ITEM_TYPE
‍Unique identifier for QGraphicsItem object
Definition DataKey.h:15
@ DATA_KEY_IDENTIFIER
Definition DataKey.h:14
const int INNER_RADIUS_MIN
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
GraphicsItemType
Runtime type identification (RTTI) for QGraphicsItem objects.
@ GRAPHICS_ITEM_TYPE_POINT
const QString AXIS_CURVE_NAME
log4cpp::Category * mainCat
Definition Logger.cpp:14
This class consolidates utility routines that deal with graphics items that are getting extracted fro...
QStringList selectedPointIdentifiers(const QList< QGraphicsItem * > &items) const
Return list of selected point identifiers.
virtual void mouseReleaseEvent(QMouseEvent *event)
Intercept mouse release events to move one or more Points.
void signalDraggedImage(QImage)
Send dragged image to MainWindow for import. This typically comes from dragging a file.
virtual void wheelEvent(QWheelEvent *event)
Convert wheel events into zoom in/out.
virtual void dropEvent(QDropEvent *event)
Intercept mouse drop event to support drag-and-drop. This initiates asynchronous loading of the dragg...
void slotDropRegression(QString)
Receive drag and drop regression test url.
virtual ~GraphicsView()
void signalKeyPress(Qt::Key, bool atLeastOneSelectedItem)
Send keypress to MainWindow for eventual processing by DigitizeStateAbstractBase subclasses.
void signalMouseRelease(QPointF)
Send mouse release to MainWindow for moving Points.
virtual void dragEnterEvent(QDragEnterEvent *event)
Intercept mouse drag event to support drag-and-drop.
virtual void keyPressEvent(QKeyEvent *event)
Intercept key press events to handle left/right/up/down moving.
void signalViewZoomOut()
Send wheel event to MainWindow for zooming out.
virtual void dragMoveEvent(QDragMoveEvent *event)
Intercept mouse move event to support drag-and-drop.
void signalDraggedDigFile(QString)
Send dragged dig file to MainWindow for import. This comes from dragging an engauge dig file.
virtual void mousePressEvent(QMouseEvent *event)
Intercept mouse press events to create one or more Points.
void signalContextMenuEventAxis(QString pointIdentifier)
Send right click on axis point to MainWindow for editing.
void signalViewZoomIn()
Send wheel event to MainWindow for zooming in.
GraphicsView(QGraphicsScene *scene, MainWindow &mainWindow)
Single constructor.
virtual void mouseMoveEvent(QMouseEvent *event)
Intercept mouse move events to populate the current cursor position in StatusBar.
void signalMousePress(QPointF)
Send mouse press to MainWindow for creating one or more Points.
void signalDraggedImageUrl(QUrl)
Send dragged url to MainWindow for import. This typically comes from dragging an image from a browser...
void signalContextMenuEventGraph(QStringList pointIdentifiers)
Send right click on graph point(s) to MainWindow for editing.
void signalMouseMove(QPointF)
Send mouse move to MainWindow for eventual display of cursor coordinates in StatusBar.
virtual void contextMenuEvent(QContextMenuEvent *event)
Intercept context event to support point editing.
Returns information about files.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition MainWindow.h:92
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18
#define LOG4CPP_DEBUG_S(logger)
Definition convenience.h:20