File indexing completed on 2024-05-12 15:27:36
0001 /*************************************************************************** 0002 File : DatapickerImageView.cpp 0003 Project : LabPlot 0004 Description : DatapickerImage view for datapicker 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) 0007 Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) 0008 0009 ***************************************************************************/ 0010 /*************************************************************************** 0011 * * 0012 * This program is free software; you can redistribute it and/or modify * 0013 * it under the terms of the GNU General Public License as published by * 0014 * the Free Software Foundation; either version 2 of the License, or * 0015 * (at your option) any later version. * 0016 * * 0017 * This program is distributed in the hope that it will be useful, * 0018 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0020 * GNU General Public License for more details. * 0021 * * 0022 * You should have received a copy of the GNU General Public License * 0023 * along with this program; if not, write to the Free Software * 0024 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0025 * Boston, MA 02110-1301 USA * 0026 * * 0027 ***************************************************************************/ 0028 0029 #include "commonfrontend/datapicker/DatapickerImageView.h" 0030 #include "backend/worksheet/Worksheet.h" 0031 #include "backend/datapicker/DatapickerPoint.h" 0032 #include "backend/datapicker/Datapicker.h" 0033 #include "backend/datapicker/Transform.h" 0034 #include "backend/datapicker/DatapickerCurve.h" 0035 #include "backend/datapicker/DatapickerImage.h" 0036 0037 #include <limits> 0038 0039 #include <QMenu> 0040 #include <QMessageBox> 0041 #include <QToolBar> 0042 #include <QDesktopWidget> 0043 #include <QWheelEvent> 0044 #include <QPrinter> 0045 #include <QSvgGenerator> 0046 #include <QImage> 0047 #include <QToolButton> 0048 #include <QTimeLine> 0049 0050 #include <KLocalizedString> 0051 0052 /** 0053 * \class DatapickerImageView 0054 * \brief Datapicker/DatapickerImage view 0055 */ 0056 0057 /*! 0058 Constructur of the class. 0059 Creates a view for the DatapickerImage \c image and initializes the internal model. 0060 */ 0061 DatapickerImageView::DatapickerImageView(DatapickerImage* image) : QGraphicsView(), 0062 m_image(image), 0063 m_datapicker(dynamic_cast<Datapicker*>(m_image->parentAspect())), 0064 m_transform(new Transform()) { 0065 0066 setScene(m_image->scene()); 0067 0068 setRenderHint(QPainter::Antialiasing); 0069 setRubberBandSelectionMode(Qt::ContainsItemBoundingRect); 0070 setTransformationAnchor(QGraphicsView::AnchorUnderMouse); 0071 setResizeAnchor(QGraphicsView::AnchorViewCenter); 0072 setMinimumSize(16, 16); 0073 setFocusPolicy(Qt::StrongFocus); 0074 0075 viewport()->setAttribute(Qt::WA_OpaquePaintEvent); 0076 viewport()->setAttribute(Qt::WA_NoSystemBackground); 0077 setCacheMode(QGraphicsView::CacheBackground); 0078 0079 initActions(); 0080 initMenus(); 0081 m_image->setSegmentsHoverEvent(true); 0082 setInteractive(true); 0083 0084 changeZoom(zoomOriginAction); 0085 currentZoomAction = zoomInViewAction; 0086 0087 if (m_image->plotPointsType() == DatapickerImage::PointsType::AxisPoints) 0088 setAxisPointsAction->setChecked(true); 0089 else if (m_image->plotPointsType() == DatapickerImage::PointsType::CurvePoints) 0090 setCurvePointsAction->setChecked(true); 0091 else 0092 selectSegmentAction->setChecked(true); 0093 0094 handleImageActions(); 0095 changeRotationAngle(); 0096 0097 //signal/slot connections 0098 //for general actions 0099 connect(m_image, &DatapickerImage::requestProjectContextMenu, this, &DatapickerImageView::createContextMenu); 0100 connect(m_image, &DatapickerImage::requestUpdate, this, &DatapickerImageView::updateBackground); 0101 connect(m_image, &DatapickerImage::requestUpdateActions, this, &DatapickerImageView::handleImageActions); 0102 connect(m_datapicker, &Datapicker::requestUpdateActions, this, &DatapickerImageView::handleImageActions); 0103 connect(m_image, &DatapickerImage::rotationAngleChanged, this, &DatapickerImageView::changeRotationAngle); 0104 0105 //resize the view to make the complete scene visible. 0106 //no need to resize the view when the project is being opened, 0107 //all views will be resized to the stored values at the end 0108 if (!m_image->isLoading()) { 0109 float w = Worksheet::convertFromSceneUnits(sceneRect().width(), Worksheet::Unit::Inch); 0110 float h = Worksheet::convertFromSceneUnits(sceneRect().height(), Worksheet::Unit::Inch); 0111 w *= QApplication::desktop()->physicalDpiX(); 0112 h *= QApplication::desktop()->physicalDpiY(); 0113 resize(w*1.1, h*1.1); 0114 } 0115 0116 //rescale to the original size 0117 static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch)); 0118 static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch)); 0119 setTransform(QTransform::fromScale(hscale, vscale)); 0120 } 0121 0122 DatapickerImageView::~DatapickerImageView() { 0123 delete m_transform; 0124 } 0125 0126 void DatapickerImageView::initActions() { 0127 auto* zoomActionGroup = new QActionGroup(this); 0128 auto* mouseModeActionGroup = new QActionGroup(this); 0129 navigationActionGroup = new QActionGroup(this); 0130 magnificationActionGroup = new QActionGroup(this); 0131 0132 //Zoom actions 0133 zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), zoomActionGroup); 0134 zoomInViewAction->setShortcut(Qt::CTRL+Qt::Key_Plus); 0135 0136 zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), zoomActionGroup); 0137 zoomOutViewAction->setShortcut(Qt::CTRL+Qt::Key_Minus); 0138 0139 zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original Size"), zoomActionGroup); 0140 zoomOriginAction->setShortcut(Qt::CTRL+Qt::Key_1); 0141 0142 zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to Height"), zoomActionGroup); 0143 zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to Width"), zoomActionGroup); 0144 0145 // Mouse mode actions 0146 navigationModeAction = new QAction(QIcon::fromTheme("input-mouse"), i18n("Navigate"), mouseModeActionGroup); 0147 navigationModeAction->setCheckable(true); 0148 navigationModeAction->setData(static_cast<int>(MouseMode::Navigation)); 0149 0150 zoomSelectionModeAction = new QAction(QIcon::fromTheme("page-zoom"), i18n("Select and Zoom"), mouseModeActionGroup); 0151 zoomSelectionModeAction->setCheckable(true); 0152 zoomSelectionModeAction->setData(static_cast<int>(MouseMode::ZoomSelection)); 0153 0154 setAxisPointsAction = new QAction(QIcon::fromTheme("labplot-plot-axis-points"), i18n("Set Axis Points"), mouseModeActionGroup); 0155 setAxisPointsAction->setCheckable(true); 0156 setAxisPointsAction->setData(static_cast<int>(MouseMode::ReferencePointsEntry)); 0157 0158 setCurvePointsAction = new QAction(QIcon::fromTheme("labplot-xy-curve-points"), i18n("Set Curve Points"), mouseModeActionGroup); 0159 setCurvePointsAction->setCheckable(true); 0160 setCurvePointsAction->setData(static_cast<int>(MouseMode::CurvePointsEntry)); 0161 0162 selectSegmentAction = new QAction(QIcon::fromTheme("labplot-xy-curve-segments"), i18n("Select Curve Segments"), mouseModeActionGroup); 0163 selectSegmentAction->setCheckable(true); 0164 selectSegmentAction->setData(static_cast<int>(MouseMode::CurveSegmentsEntry)); 0165 0166 addCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("New Curve"), this); 0167 0168 shiftLeftAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("Shift Left"), navigationActionGroup); 0169 shiftLeftAction->setShortcut(Qt::Key_Right); 0170 0171 shiftRightAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("Shift Right"), navigationActionGroup); 0172 shiftRightAction->setShortcut(Qt::Key_Left); 0173 0174 shiftUpAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("Shift Up"), navigationActionGroup); 0175 shiftUpAction->setShortcut(Qt::Key_Up); 0176 0177 shiftDownAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("Shift Down"), navigationActionGroup); 0178 shiftDownAction->setShortcut(Qt::Key_Down); 0179 0180 noMagnificationAction = new QAction(QIcon::fromTheme("labplot-1x-zoom"), i18n("No Magnification"), magnificationActionGroup); 0181 noMagnificationAction->setCheckable(true); 0182 noMagnificationAction->setChecked(true); 0183 0184 twoTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-2x-zoom"), i18n("2x Magnification"), magnificationActionGroup); 0185 twoTimesMagnificationAction->setCheckable(true); 0186 0187 threeTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-3x-zoom"), i18n("3x Magnification"), magnificationActionGroup); 0188 threeTimesMagnificationAction->setCheckable(true); 0189 0190 fourTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-4x-zoom"), i18n("4x Magnification"), magnificationActionGroup); 0191 fourTimesMagnificationAction->setCheckable(true); 0192 0193 fiveTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-5x-zoom"), i18n("5x Magnification"), magnificationActionGroup); 0194 fiveTimesMagnificationAction->setCheckable(true); 0195 0196 //set some default values 0197 currentZoomAction = zoomInViewAction; 0198 currentMagnificationAction = noMagnificationAction; 0199 0200 switch(m_image->plotPointsType()) { 0201 case DatapickerImage::PointsType::AxisPoints: 0202 currentPlotPointsTypeAction = setAxisPointsAction; 0203 setAxisPointsAction->setChecked(true); 0204 mouseModeChanged(setAxisPointsAction); 0205 break; 0206 case DatapickerImage::PointsType::CurvePoints: 0207 currentPlotPointsTypeAction = setCurvePointsAction; 0208 setCurvePointsAction->setChecked(true); 0209 mouseModeChanged(setCurvePointsAction); 0210 break; 0211 case DatapickerImage::PointsType::SegmentPoints: 0212 currentPlotPointsTypeAction = selectSegmentAction; 0213 selectSegmentAction->setChecked(true); 0214 mouseModeChanged(selectSegmentAction); 0215 } 0216 0217 //signal-slot connections 0218 connect(mouseModeActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::mouseModeChanged); 0219 connect(zoomActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::changeZoom); 0220 connect(addCurveAction, &QAction::triggered, this, &DatapickerImageView::addCurve); 0221 connect(navigationActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::changeSelectedItemsPosition); 0222 connect(magnificationActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::magnificationChanged); 0223 } 0224 0225 void DatapickerImageView::initMenus() { 0226 m_viewMouseModeMenu = new QMenu(i18n("Mouse Mode"), this); 0227 m_viewMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse")); 0228 m_viewMouseModeMenu->addAction(setAxisPointsAction); 0229 m_viewMouseModeMenu->addAction(setCurvePointsAction); 0230 m_viewMouseModeMenu->addAction(selectSegmentAction); 0231 m_viewMouseModeMenu->addSeparator(); 0232 m_viewMouseModeMenu->addAction(navigationModeAction); 0233 m_viewMouseModeMenu->addAction(zoomSelectionModeAction); 0234 0235 m_zoomMenu = new QMenu(i18n("Zoom View"), this); 0236 m_zoomMenu->setIcon(QIcon::fromTheme("zoom-draw")); 0237 m_zoomMenu->addAction(zoomInViewAction); 0238 m_zoomMenu->addAction(zoomOutViewAction); 0239 m_zoomMenu->addAction(zoomOriginAction); 0240 m_zoomMenu->addAction(zoomFitPageHeightAction); 0241 m_zoomMenu->addAction(zoomFitPageWidthAction); 0242 0243 m_navigationMenu = new QMenu(i18n("Move Last Point"), this); 0244 m_navigationMenu->addAction(shiftLeftAction); 0245 m_navigationMenu->addAction(shiftRightAction); 0246 m_navigationMenu->addAction(shiftUpAction); 0247 m_navigationMenu->addAction(shiftDownAction); 0248 0249 m_magnificationMenu = new QMenu(i18n("Magnification"), this); 0250 m_magnificationMenu->setIcon(QIcon::fromTheme("zoom-in")); 0251 m_magnificationMenu->addAction(noMagnificationAction); 0252 m_magnificationMenu->addAction(twoTimesMagnificationAction); 0253 m_magnificationMenu->addAction(threeTimesMagnificationAction); 0254 m_magnificationMenu->addAction(fourTimesMagnificationAction); 0255 m_magnificationMenu->addAction(fiveTimesMagnificationAction); 0256 } 0257 0258 /*! 0259 * Populates the menu \c menu with the image and image-view relevant actions. 0260 * The menu is used 0261 * - as the context menu in DatapickerImageView 0262 * - as the "datapicker menu" in the main menu-bar (called form MainWin) 0263 * - as a part of the image context menu in project explorer 0264 */ 0265 void DatapickerImageView::createContextMenu(QMenu* menu) const { 0266 Q_ASSERT(menu); 0267 0268 QAction* firstAction = nullptr; 0269 // if we're populating the context menu for the project explorer, then 0270 //there're already actions available there. Skip the first title-action 0271 //and insert the action at the beginning of the menu. 0272 if (menu->actions().size()>1) 0273 firstAction = menu->actions().at(1); 0274 0275 menu->insertAction(firstAction, addCurveAction); 0276 menu->insertSeparator(firstAction); 0277 menu->insertMenu(firstAction, m_navigationMenu); 0278 menu->insertSeparator(firstAction); 0279 menu->insertMenu(firstAction, m_viewMouseModeMenu); 0280 menu->insertMenu(firstAction, m_zoomMenu); 0281 menu->insertMenu(firstAction, m_magnificationMenu); 0282 menu->insertSeparator(firstAction); 0283 } 0284 0285 void DatapickerImageView::fillToolBar(QToolBar* toolBar) { 0286 toolBar->addSeparator(); 0287 toolBar->addAction(setAxisPointsAction); 0288 toolBar->addAction(setCurvePointsAction); 0289 toolBar->addAction(selectSegmentAction); 0290 toolBar->addAction(navigationModeAction); 0291 toolBar->addAction(zoomSelectionModeAction); 0292 0293 toolBar->addSeparator(); 0294 toolBar->addAction(addCurveAction); 0295 0296 toolBar->addSeparator(); 0297 toolBar->addAction(shiftRightAction); 0298 toolBar->addAction(shiftLeftAction); 0299 toolBar->addAction(shiftUpAction); 0300 toolBar->addAction(shiftDownAction); 0301 0302 tbZoom = new QToolButton(toolBar); 0303 tbZoom->setPopupMode(QToolButton::MenuButtonPopup); 0304 tbZoom->setMenu(m_zoomMenu); 0305 tbZoom->setDefaultAction(currentZoomAction); 0306 toolBar->addSeparator(); 0307 toolBar->addWidget(tbZoom); 0308 0309 tbMagnification = new QToolButton(toolBar); 0310 tbMagnification->setPopupMode(QToolButton::MenuButtonPopup); 0311 tbMagnification->setMenu(m_magnificationMenu); 0312 tbMagnification->setDefaultAction(currentMagnificationAction); 0313 toolBar->addWidget(tbMagnification); 0314 } 0315 0316 void DatapickerImageView::setScene(QGraphicsScene* scene) { 0317 QGraphicsView::setScene(scene); 0318 setTransform(QTransform()); 0319 } 0320 0321 void DatapickerImageView::drawForeground(QPainter* painter, const QRectF& rect) { 0322 if (m_mouseMode == MouseMode::ZoomSelection && m_selectionBandIsShown) { 0323 painter->save(); 0324 const QRectF& selRect = mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(); 0325 painter->setPen(QPen(Qt::black, 5/transform().m11())); 0326 painter->drawRect(selRect); 0327 painter->setBrush(Qt::blue); 0328 painter->setOpacity(0.2); 0329 painter->drawRect(selRect); 0330 painter->restore(); 0331 } 0332 QGraphicsView::drawForeground(painter, rect); 0333 } 0334 0335 void DatapickerImageView::drawBackground(QPainter* painter, const QRectF& rect) { 0336 painter->save(); 0337 0338 QRectF scene_rect = sceneRect(); 0339 if (!scene_rect.contains(rect)) 0340 painter->fillRect(rect, Qt::lightGray); 0341 0342 // canvas 0343 if (m_image->isLoaded) { 0344 if (m_image->plotImageType() == DatapickerImage::PlotImageType::OriginalImage) { 0345 const QImage& todraw = m_image->originalPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0346 painter->drawImage(scene_rect.topLeft(), todraw); 0347 } else if (m_image->plotImageType() == DatapickerImage::PlotImageType::ProcessedImage) { 0348 const QImage& todraw = m_image->processedPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0349 painter->drawImage(scene_rect.topLeft(), todraw); 0350 } else { 0351 painter->fillRect(scene_rect, Qt::white); 0352 } 0353 } else { 0354 painter->setBrush(QBrush(Qt::gray)); 0355 painter->drawRect(scene_rect); 0356 } 0357 0358 invalidateScene(rect, QGraphicsScene::BackgroundLayer); 0359 painter->restore(); 0360 } 0361 0362 //############################################################################## 0363 //#################################### Events ############################### 0364 //############################################################################## 0365 void DatapickerImageView::wheelEvent(QWheelEvent* event) { 0366 //https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView 0367 if (m_mouseMode == MouseMode::ZoomSelection || (QApplication::keyboardModifiers() & Qt::ControlModifier)) { 0368 int numDegrees = event->delta() / 8; 0369 int numSteps = numDegrees / 15; // see QWheelEvent documentation 0370 zoom(numSteps); 0371 } else 0372 QGraphicsView::wheelEvent(event); 0373 } 0374 0375 void DatapickerImageView::zoom(int numSteps) { 0376 m_numScheduledScalings += numSteps; 0377 if (m_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings 0378 m_numScheduledScalings = numSteps; 0379 0380 auto* anim = new QTimeLine(350, this); 0381 anim->setUpdateInterval(20); 0382 0383 connect(anim, &QTimeLine::valueChanged, this, &DatapickerImageView::scalingTime); 0384 connect(anim, &QTimeLine::finished, this, &DatapickerImageView::animFinished); 0385 anim->start(); 0386 } 0387 0388 void DatapickerImageView::scalingTime() { 0389 qreal factor = 1.0 + qreal(m_numScheduledScalings) / 300.0; 0390 scale(factor, factor); 0391 } 0392 0393 void DatapickerImageView::animFinished() { 0394 if (m_numScheduledScalings > 0) 0395 m_numScheduledScalings--; 0396 else 0397 m_numScheduledScalings++; 0398 sender()->~QObject(); 0399 } 0400 0401 void DatapickerImageView::mousePressEvent(QMouseEvent* event) { 0402 //prevent the deselection of items when context menu event 0403 //was triggered (right button click) 0404 if (event->button() == Qt::RightButton) { 0405 event->accept(); 0406 return; 0407 } 0408 0409 if (event->button() == Qt::LeftButton && m_mouseMode == MouseMode::ZoomSelection) { 0410 m_selectionStart = event->pos(); 0411 m_selectionBandIsShown = true; 0412 return; 0413 } 0414 0415 QPointF eventPos = mapToScene(event->pos()); 0416 bool entryMode = (m_mouseMode == MouseMode::ReferencePointsEntry 0417 || m_mouseMode == MouseMode::CurvePointsEntry || m_mouseMode == MouseMode::CurveSegmentsEntry); 0418 0419 //check whether there is a point item under the cursor 0420 bool pointsUnderCursor = false; 0421 auto items = this->items(event->pos()); 0422 for (auto* item : items) { 0423 if (item != m_image->m_magnificationWindow) { 0424 pointsUnderCursor = true; 0425 break; 0426 } 0427 } 0428 0429 if (entryMode && !pointsUnderCursor && m_image->isLoaded && sceneRect().contains(eventPos)) { 0430 if ( m_image->plotPointsType() == DatapickerImage::PointsType::AxisPoints ) { 0431 int childCount = m_image->childCount<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden); 0432 if (childCount < 3) 0433 m_datapicker->addNewPoint(eventPos, m_image); 0434 } else if ( m_image->plotPointsType() == DatapickerImage::PointsType::CurvePoints && m_datapicker->activeCurve() ) { 0435 m_datapicker->addNewPoint(eventPos, m_datapicker->activeCurve()); 0436 } 0437 0438 if (m_image->m_magnificationWindow && m_image->m_magnificationWindow->isVisible()) 0439 updateMagnificationWindow(); 0440 } 0441 0442 // make sure the datapicker (or its currently active curve) is selected in the project explorer if the view was clicked. 0443 // We need this for the case when we change from the project-node in the project explorer to the datapicker node by clicking the view. 0444 if (m_datapicker->activeCurve() && m_image->plotPointsType() != DatapickerImage::PointsType::AxisPoints) { 0445 m_datapicker->setSelectedInView(false); 0446 m_datapicker->activeCurve()->setSelectedInView(true); 0447 } else { 0448 if (m_datapicker->activeCurve()) 0449 m_datapicker->activeCurve()->setSelectedInView(false); 0450 m_datapicker->setSelectedInView(true); 0451 } 0452 0453 QGraphicsView::mousePressEvent(event); 0454 } 0455 0456 void DatapickerImageView::mouseReleaseEvent(QMouseEvent* event) { 0457 if (event->button() == Qt::LeftButton && m_mouseMode == MouseMode::ZoomSelection) { 0458 m_selectionBandIsShown = false; 0459 viewport()->repaint(QRect(m_selectionStart, m_selectionEnd).normalized()); 0460 0461 //don't zoom if very small region was selected, avoid occasional/unwanted zooming 0462 m_selectionEnd = event->pos(); 0463 if ( abs(m_selectionEnd.x()-m_selectionStart.x())>20 && abs(m_selectionEnd.y()-m_selectionStart.y())>20 ) 0464 fitInView(mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(), Qt::KeepAspectRatio); 0465 } 0466 0467 QGraphicsView::mouseReleaseEvent(event); 0468 } 0469 0470 void DatapickerImageView::mouseMoveEvent(QMouseEvent* event) { 0471 //show the selection band 0472 if (m_selectionBandIsShown) { 0473 QRect rect = QRect(m_selectionStart, m_selectionEnd).normalized(); 0474 m_selectionEnd = event->pos(); 0475 rect = rect.united(QRect(m_selectionStart, m_selectionEnd).normalized()); 0476 int penWidth = 5/transform().m11(); 0477 rect.setX(rect.x()-penWidth); 0478 rect.setY(rect.y()-penWidth); 0479 rect.setHeight(rect.height()+2*penWidth); 0480 rect.setWidth(rect.width()+2*penWidth); 0481 viewport()->repaint(rect); 0482 return; 0483 } 0484 0485 QPointF pos = mapToScene(event->pos()); 0486 0487 //show the current coordinates under the mouse cursor in the status bar 0488 if (m_image->plotPointsType() == DatapickerImage::PointsType::CurvePoints) { 0489 QVector3D logicalPos = m_transform->mapSceneToLogical(pos, m_image->axisPoints()); 0490 if (m_image->axisPoints().type == DatapickerImage::GraphType::Ternary) { 0491 emit statusInfo( "a =" + QString::number(logicalPos.x()) + ", b =" + QString::number(logicalPos.y()) + ", c =" + QString::number(logicalPos.z())); 0492 } else { 0493 QString xLabel('x'); 0494 QString yLabel('y'); 0495 if (m_image->axisPoints().type == DatapickerImage::GraphType::PolarInDegree) { 0496 xLabel = 'r'; 0497 yLabel = "y(deg)"; 0498 } else if (m_image->axisPoints().type == DatapickerImage::GraphType::PolarInRadians) { 0499 xLabel = 'r'; 0500 yLabel = "y(rad)"; 0501 } 0502 0503 if (m_datapicker->activeCurve()) { 0504 QString statusText = i18n("%1, active curve \"%2\": %3=%4, %5=%6", m_datapicker->name(), m_datapicker->activeCurve()->name(), xLabel, QString::number(logicalPos.x()), yLabel, QString::number(logicalPos.y())); 0505 emit statusInfo(statusText); 0506 } 0507 } 0508 } 0509 0510 //show the magnification window 0511 if ( magnificationFactor && m_image->isLoaded && sceneRect().contains(pos) ) { 0512 0513 if (!m_image->m_magnificationWindow) { 0514 m_image->m_magnificationWindow = new QGraphicsPixmapItem; 0515 scene()->addItem(m_image->m_magnificationWindow); 0516 m_image->m_magnificationWindow->setZValue(std::numeric_limits<int>::max()); 0517 } 0518 0519 updateMagnificationWindow(); 0520 } else if (m_image->m_magnificationWindow) { 0521 m_image->m_magnificationWindow->setVisible(false); 0522 } 0523 0524 QGraphicsView::mouseMoveEvent(event); 0525 } 0526 0527 void DatapickerImageView::updateMagnificationWindow() { 0528 m_image->m_magnificationWindow->setVisible(false); 0529 QPointF pos = mapToScene(mapFromGlobal(QCursor::pos())); 0530 0531 //copy the part of the view to be shown magnified 0532 const int size = Worksheet::convertToSceneUnits(2.0, Worksheet::Unit::Centimeter)/transform().m11(); 0533 const QRectF copyRect(pos.x() - size/(2*magnificationFactor), pos.y() - size/(2*magnificationFactor), size/magnificationFactor, size/magnificationFactor); 0534 QPixmap px = grab(mapFromScene(copyRect).boundingRect()); 0535 px = px.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0536 0537 //draw the bounding rect 0538 QPainter painter(&px); 0539 const QPen pen = QPen(Qt::lightGray, 2/transform().m11()); 0540 painter.setPen(pen); 0541 QRect rect = px.rect(); 0542 rect.setWidth(rect.width()-pen.widthF()/2); 0543 rect.setHeight(rect.height()-pen.widthF()/2); 0544 painter.drawRect(rect); 0545 0546 //set the pixmap 0547 m_image->m_magnificationWindow->setPixmap(px); 0548 m_image->m_magnificationWindow->setPos(pos.x()- px.width()/2, pos.y()- px.height()/2); 0549 0550 m_image->m_magnificationWindow->setVisible(true); 0551 } 0552 0553 void DatapickerImageView::contextMenuEvent(QContextMenuEvent* e) { 0554 Q_UNUSED(e); 0555 //no need to propagate the event to the scene and graphics items 0556 QMenu *menu = new QMenu(this); 0557 this->createContextMenu(menu); 0558 menu->exec(QCursor::pos()); 0559 } 0560 0561 //############################################################################## 0562 //#################################### SLOTs ############################### 0563 //############################################################################## 0564 void DatapickerImageView::mouseModeChanged(QAction* action) { 0565 m_mouseMode = (DatapickerImageView::MouseMode)action->data().toInt(); 0566 0567 if (action == navigationModeAction) { 0568 setInteractive(false); 0569 setDragMode(QGraphicsView::ScrollHandDrag); 0570 m_image->setSegmentsHoverEvent(false); 0571 } else if (action == zoomSelectionModeAction){ 0572 setInteractive(false); 0573 setDragMode(QGraphicsView::NoDrag); 0574 m_image->setSegmentsHoverEvent(false); 0575 setCursor(Qt::ArrowCursor); 0576 } else { 0577 setInteractive(true); 0578 setDragMode(QGraphicsView::NoDrag); 0579 m_image->setSegmentsHoverEvent(true); 0580 setCursor(Qt::CrossCursor); 0581 0582 if (currentPlotPointsTypeAction != action) { 0583 if (action == setAxisPointsAction) { 0584 int count = m_image->childCount<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden); 0585 if (count) { 0586 auto button = QMessageBox::question(this, i18n("Remove existing reference points?"), 0587 i18n("All available reference points will be removed. Do you want to continue?")); 0588 if (button != QMessageBox::Yes) { 0589 currentPlotPointsTypeAction->setChecked(true); 0590 return; 0591 } 0592 } 0593 0594 m_image->setPlotPointsType(DatapickerImage::PointsType::AxisPoints); 0595 } else if (action == setCurvePointsAction) 0596 m_image->setPlotPointsType(DatapickerImage::PointsType::CurvePoints); 0597 else if (action == selectSegmentAction) 0598 m_image->setPlotPointsType(DatapickerImage::PointsType::SegmentPoints); 0599 0600 currentPlotPointsTypeAction = action; 0601 } 0602 } 0603 } 0604 0605 void DatapickerImageView::changeZoom(QAction* action) { 0606 if (action == zoomInViewAction) 0607 zoom(1); 0608 else if (action == zoomOutViewAction) 0609 zoom(-1); 0610 else if (action == zoomOriginAction) { 0611 static const float hscale = QApplication::desktop()->physicalDpiX()/(25.4*Worksheet::convertToSceneUnits(1, Worksheet::Unit::Millimeter)); 0612 static const float vscale = QApplication::desktop()->physicalDpiY()/(25.4*Worksheet::convertToSceneUnits(1, Worksheet::Unit::Millimeter)); 0613 setTransform(QTransform::fromScale(hscale, vscale)); 0614 m_rotationAngle = 0; 0615 } else if (action == zoomFitPageWidthAction) { 0616 float scaleFactor = viewport()->width()/scene()->sceneRect().width(); 0617 setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); 0618 m_rotationAngle = 0; 0619 } else if (action == zoomFitPageHeightAction) { 0620 float scaleFactor = viewport()->height()/scene()->sceneRect().height(); 0621 setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); 0622 m_rotationAngle = 0; 0623 } 0624 0625 currentZoomAction = action; 0626 if (tbZoom) 0627 tbZoom->setDefaultAction(action); 0628 0629 //change and set angle if tranform reset 0630 changeRotationAngle(); 0631 } 0632 0633 void DatapickerImageView::changeSelectedItemsPosition(QAction* action) { 0634 if (scene()->selectedItems().isEmpty()) 0635 return; 0636 0637 QPointF shift(0, 0); 0638 if (action == shiftLeftAction) 0639 shift.setX(1); 0640 else if (action == shiftRightAction) 0641 shift.setX(-1); 0642 else if (action == shiftUpAction) 0643 shift.setY(-1); 0644 else if (action == shiftDownAction) 0645 shift.setY(1); 0646 0647 m_image->beginMacro(i18n("%1: change position of selected DatapickerPoints.", m_image->name())); 0648 const QVector<DatapickerPoint*> axisPoints = m_image->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden); 0649 for (auto* point : axisPoints) { 0650 if (!point->graphicsItem()->isSelected()) 0651 continue; 0652 0653 QPointF newPos = point->position(); 0654 newPos = newPos + shift; 0655 point->setPosition(newPos); 0656 0657 int pointIndex = m_image->indexOfChild<DatapickerPoint>(point, AbstractAspect::ChildIndexFlag::IncludeHidden); 0658 if (pointIndex == -1) continue; 0659 DatapickerImage::ReferencePoints points = m_image->axisPoints(); 0660 points.scenePos[pointIndex].setX(point->position().x()); 0661 points.scenePos[pointIndex].setY(point->position().y()); 0662 m_image->setUndoAware(false); 0663 m_image->setAxisPoints(points); 0664 m_image->setUndoAware(true); 0665 } 0666 0667 for (auto* curve : m_image->parentAspect()->children<DatapickerCurve>()) { 0668 for (auto* point : curve->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden)) { 0669 if (!point->graphicsItem()->isSelected()) 0670 continue; 0671 0672 QPointF newPos = point->position(); 0673 newPos = newPos + shift; 0674 point->setPosition(newPos); 0675 } 0676 } 0677 0678 m_image->endMacro(); 0679 if (m_image->m_magnificationWindow && m_image->m_magnificationWindow->isVisible()) 0680 updateMagnificationWindow(); 0681 } 0682 0683 void DatapickerImageView::magnificationChanged(QAction* action) { 0684 if (action == noMagnificationAction) 0685 magnificationFactor = 0; 0686 else if (action == twoTimesMagnificationAction) 0687 magnificationFactor = 2; 0688 else if (action == threeTimesMagnificationAction) 0689 magnificationFactor = 3; 0690 else if (action == fourTimesMagnificationAction) 0691 magnificationFactor = 4; 0692 else if (action == fiveTimesMagnificationAction) 0693 magnificationFactor = 5; 0694 } 0695 0696 void DatapickerImageView::addCurve() { 0697 m_datapicker->beginMacro(i18n("%1: add new curve.", m_datapicker->name())); 0698 DatapickerCurve* curve = new DatapickerCurve(i18n("Curve")); 0699 curve->addDatasheet(m_image->axisPoints().type); 0700 m_datapicker->addChild(curve); 0701 m_datapicker->endMacro(); 0702 0703 setCurvePointsAction->setChecked(true); 0704 mouseModeChanged(setCurvePointsAction); 0705 } 0706 0707 void DatapickerImageView::changeRotationAngle() { 0708 this->rotate(m_rotationAngle); 0709 this->rotate(-m_image->rotationAngle()); 0710 m_rotationAngle = m_image->rotationAngle(); 0711 updateBackground(); 0712 } 0713 0714 void DatapickerImageView::handleImageActions() { 0715 if (m_image->isLoaded) { 0716 magnificationActionGroup->setEnabled(true); 0717 setAxisPointsAction->setEnabled(true); 0718 0719 int pointsCount = m_image->childCount<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden); 0720 if (pointsCount > 0) 0721 navigationActionGroup->setEnabled(true); 0722 else 0723 navigationActionGroup->setEnabled(false); 0724 0725 if (pointsCount > 2) { 0726 addCurveAction->setEnabled(true); 0727 if (m_datapicker->activeCurve()) { 0728 setCurvePointsAction->setEnabled(true); 0729 selectSegmentAction->setEnabled(true); 0730 } else { 0731 setCurvePointsAction->setEnabled(false); 0732 selectSegmentAction->setEnabled(false); 0733 } 0734 } else { 0735 addCurveAction->setEnabled(false); 0736 setCurvePointsAction->setEnabled(false); 0737 selectSegmentAction->setEnabled(false); 0738 } 0739 } else { 0740 navigationActionGroup->setEnabled(false); 0741 magnificationActionGroup->setEnabled(false); 0742 setAxisPointsAction->setEnabled(false); 0743 addCurveAction->setEnabled(false); 0744 setCurvePointsAction->setEnabled(false); 0745 selectSegmentAction->setEnabled(false); 0746 } 0747 } 0748 0749 void DatapickerImageView::exportToFile(const QString& path, const WorksheetView::ExportFormat format, const int resolution) { 0750 QRectF sourceRect; 0751 sourceRect = scene()->sceneRect(); 0752 0753 //print 0754 if (format == WorksheetView::ExportFormat::PDF) { 0755 QPrinter printer(QPrinter::HighResolution); 0756 printer.setOutputFormat(QPrinter::PdfFormat); 0757 printer.setOutputFileName(path); 0758 int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter); 0759 int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter); 0760 printer.setPaperSize( QSizeF(w, h), QPrinter::Millimeter); 0761 printer.setPageMargins(0,0,0,0, QPrinter::Millimeter); 0762 printer.setPrintRange(QPrinter::PageRange); 0763 printer.setCreator( QLatin1String("LabPlot ") + LVERSION ); 0764 0765 QPainter painter(&printer); 0766 painter.setRenderHint(QPainter::Antialiasing); 0767 QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height()); 0768 painter.begin(&printer); 0769 exportPaint(&painter, targetRect, sourceRect); 0770 painter.end(); 0771 } else if (format == WorksheetView::ExportFormat::SVG) { 0772 QSvgGenerator generator; 0773 generator.setFileName(path); 0774 int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter); 0775 int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter); 0776 w = w*QApplication::desktop()->physicalDpiX()/25.4; 0777 h = h*QApplication::desktop()->physicalDpiY()/25.4; 0778 0779 generator.setSize(QSize(w, h)); 0780 QRectF targetRect(0, 0, w, h); 0781 generator.setViewBox(targetRect); 0782 0783 QPainter painter; 0784 painter.begin(&generator); 0785 exportPaint(&painter, targetRect, sourceRect); 0786 painter.end(); 0787 } else { 0788 //PNG 0789 //TODO add all formats supported by Qt in QImage 0790 int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter); 0791 int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter); 0792 w = w*resolution/25.4; 0793 h = h*resolution/25.4; 0794 QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied); 0795 image.fill(Qt::transparent); 0796 QRectF targetRect(0, 0, w, h); 0797 0798 QPainter painter; 0799 painter.begin(&image); 0800 painter.setRenderHint(QPainter::Antialiasing); 0801 exportPaint(&painter, targetRect, sourceRect); 0802 painter.end(); 0803 0804 image.save(path, "png"); 0805 } 0806 } 0807 0808 void DatapickerImageView::exportPaint(QPainter* painter, const QRectF& targetRect, const QRectF& sourceRect) { 0809 painter->save(); 0810 painter->scale(targetRect.width()/sourceRect.width(), targetRect.height()/sourceRect.height()); 0811 drawBackground(painter, sourceRect); 0812 painter->restore(); 0813 m_image->setPrinting(true); 0814 scene()->render(painter, QRectF(), sourceRect); 0815 m_image->setPrinting(false); 0816 } 0817 0818 void DatapickerImageView::print(QPrinter* printer) { 0819 const QRectF scene_rect = sceneRect(); 0820 int w = Worksheet::convertFromSceneUnits(scene_rect.width(), Worksheet::Unit::Millimeter); 0821 int h = Worksheet::convertFromSceneUnits(scene_rect.height(), Worksheet::Unit::Millimeter); 0822 printer->setPaperSize( QSizeF(w, h), QPrinter::Millimeter); 0823 printer->setPageMargins(0,0,0,0, QPrinter::Millimeter); 0824 printer->setPrintRange(QPrinter::PageRange); 0825 printer->setCreator( QString("LabPlot ") + LVERSION ); 0826 0827 QPainter painter(printer); 0828 QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height()); 0829 painter.setRenderHint(QPainter::Antialiasing); 0830 painter.begin(printer); 0831 painter.save(); 0832 painter.scale(targetRect.width()/scene_rect.width(), targetRect.height()/scene_rect.height()); 0833 0834 // canvas 0835 if (m_image->isLoaded) { 0836 if (m_image->plotImageType() == DatapickerImage::PlotImageType::OriginalImage) { 0837 QImage todraw = m_image->originalPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0838 painter.drawImage(scene_rect.topLeft(), todraw); 0839 } else if (m_image->plotImageType() == DatapickerImage::PlotImageType::ProcessedImage) { 0840 QImage todraw = m_image->processedPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0841 painter.drawImage(scene_rect.topLeft(), todraw); 0842 } else { 0843 painter.fillRect(scene_rect, Qt::white); 0844 } 0845 } else { 0846 painter.setBrush(QBrush(Qt::gray)); 0847 painter.drawRect(scene_rect); 0848 } 0849 0850 painter.restore(); 0851 m_image->setPrinting(true); 0852 scene()->render(&painter, QRectF(), scene_rect); 0853 m_image->setPrinting(false); 0854 painter.end(); 0855 } 0856 0857 void DatapickerImageView::updateBackground() { 0858 invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer); 0859 }