File indexing completed on 2023-09-24 07:46:04
0001 /************************************************************************************* 0002 * Copyright (C) 2007-2008 by Aleix Pol <aleixpol@kde.org> * 0003 * Copyright (C) 2012-2013 by Percy Camilo T. Aucahuasi <percy.camilo.ta@gmail.com> * 0004 * * 0005 * This program is free software; you can redistribute it and/or * 0006 * modify it under the terms of the GNU General Public License * 0007 * as published by the Free Software Foundation; either version 2 * 0008 * of the License, or (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the Free Software * 0017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 0018 *************************************************************************************/ 0019 0020 #include "plotsview2d.h" 0021 0022 #include <QSvgGenerator> 0023 #include <QList> 0024 #include <QFile> 0025 #include <QDebug> 0026 #include <QTimer> 0027 #include <QPropertyAnimation> 0028 #include <QItemSelectionModel> 0029 #include <QCoreApplication> 0030 #include <QApplication> 0031 #include <QClipboard> 0032 0033 #include <analitza/analyzer.h> 0034 #include <analitzaplot/plotsmodel.h> 0035 #include <cmath> 0036 0037 using namespace Analitza; 0038 0039 PlotsView2D::PlotsView2D(QWidget *parent) 0040 : QWidget(parent) 0041 , Plotter2D(size()) 0042 , valid(false) 0043 , mode(None) 0044 , m_framed(false) 0045 , m_readonly(false) 0046 , m_selection(nullptr) 0047 { 0048 this->setFocusPolicy(Qt::ClickFocus); 0049 this->setCursor(Qt::CrossCursor); 0050 0051 this->setMouseTracking(!m_readonly); 0052 this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0053 this->setMinimumSize(128, 128); 0054 0055 //BEGIN setup plotter2d grid 0056 defViewport = QRectF(QPointF(-10.0, 10.0), QSizeF(20.0, -20.0)); 0057 resetViewport(); 0058 0059 //colors 0060 QColor bgcolor = palette().color(QPalette::Base); 0061 setBackgroundColor(bgcolor); 0062 0063 QColor gridcolor = palette().color(QPalette::Midlight); 0064 setGridColor(gridcolor); 0065 //END 0066 0067 this->setAutoFillBackground(false); 0068 } 0069 0070 PlotsView2D::~PlotsView2D() {} 0071 0072 void PlotsView2D::paintEvent(QPaintEvent * ) 0073 { 0074 if (!valid) 0075 { 0076 if (buffer.isNull() || buffer.size()!=size()) 0077 buffer = QPixmap(size()); 0078 buffer.fill(backgroundColor()); 0079 0080 drawFunctions(&buffer); 0081 0082 valid=true; 0083 } 0084 QPainter p(this); 0085 p.drawPixmap(QRect(QPoint(0,0), size()), buffer); 0086 0087 QPen ccursor; 0088 0089 // finestra.setRenderHint(QPainter::Antialiasing, true); 0090 0091 //NOTE GSOC 2012 better guards : model()->rowCount() > 0 && !mark.isNull() ... si no se cumplen que no dibuje las lineas rojas 0092 if(!m_readonly && mode==None) { 0093 //QPointF ultim = toWidget(mark); 0094 QPointF ultim(cursorPos); 0095 0096 //Draw derivative 0097 if (!mark.isNull()) 0098 { 0099 ultim = toWidget(mark); // si no es nulo apunto al lugar del punto de pendiente 0100 0101 ccursor.setColor(palette().color(QPalette::Active, QPalette::Link)); 0102 ccursor.setStyle(Qt::SolidLine); 0103 QLineF sl=slope(mark); 0104 sl.translate(mark); 0105 0106 p.setPen(ccursor); 0107 p.setRenderHint(QPainter::Antialiasing, true); 0108 #ifndef Q_CC_MSVC 0109 if(!sl.isNull() && !std::isnan(sl.length())) 0110 #else 0111 if(!sl.isNull() && !_isnan(sl.length())) 0112 #endif 0113 p.drawLine(toWidget(sl)); 0114 p.setRenderHint(QPainter::Antialiasing, false); 0115 } 0116 //EOderivative 0117 0118 ccursor.setColor(QColor(0xc0,0,0)); 0119 ccursor.setStyle(Qt::SolidLine); 0120 0121 p.setPen(ccursor); 0122 p.drawLine(QPointF(0.,ultim.y()), QPointF(size().width(), ultim.y())); 0123 p.drawLine(QPointF(ultim.x(),0.), QPointF(ultim.x(), size().height())); 0124 0125 int w=p.fontMetrics().boundingRect(m_posText).width()+15, h=p.fontMetrics().height(); 0126 0127 if(ultim.x()+w > size().width()) 0128 ultim.setX(size().width()-w); 0129 if(ultim.y()+h > size().height()) 0130 ultim.setY(size().height()-h); 0131 if(ultim.y() < 0.) 0132 ultim.setY(0.); 0133 0134 p.setPen(QPen(Qt::black)); 0135 p.drawText(QPointF(ultim.x()+15., ultim.y()+15.), m_posText); 0136 } else if(!m_readonly && mode==Selection) { 0137 //NOTE GSOC 2012 accessibility code, the selection follow system rules :) 0138 0139 // ccursor.setColor(QColor(0xc0,0,0)); 0140 QColor selcol = QPalette().highlight().color(); 0141 ccursor.setColor(QPalette().highlightedText().color()); 0142 ccursor.setStyle(Qt::SolidLine); 0143 p.setPen(ccursor); 0144 // p.setBrush(QColor(0xff,0xff, 0,0x90)); 0145 selcol.setAlpha(0x90); 0146 p.setBrush(selcol); 0147 p.drawRect(QRect(press,last)); 0148 } 0149 0150 if(m_framed) { 0151 QPen bord(Qt::black); 0152 p.setPen(bord); 0153 p.drawRect(QRect(QPoint(0,0), size()-QSize(2,2))); 0154 } 0155 p.end(); 0156 0157 // qDebug() << "xxx2 " << viewport; 0158 } 0159 0160 void PlotsView2D::wheelEvent(QWheelEvent *e) 0161 { 0162 QRectF viewport=currentViewport(); 0163 int steps = e->angleDelta().y()/(8*15); 0164 qreal d = (-0.03*steps) + 1; 0165 0166 if(d>0 || (viewport.width()+d > 2 && viewport.height()+d < 2)) { 0167 scaleViewport(d, e->position().toPoint()); 0168 } 0169 } 0170 0171 void PlotsView2D::mousePressEvent(QMouseEvent *e) 0172 { 0173 if(!m_readonly && (e->button()==Qt::LeftButton || e->button()==Qt::MiddleButton)) { 0174 last = press = e->pos(); 0175 ant = toViewport(e->pos()); 0176 this->setCursor(QCursor(Qt::PointingHandCursor)); 0177 if(e->button()==Qt::MiddleButton || (e->button()==Qt::LeftButton && e->modifiers()&Qt::ControlModifier)) 0178 mode=Pan; 0179 else if(e->button()==Qt::LeftButton) 0180 mode=Selection; 0181 } 0182 } 0183 0184 void PlotsView2D::mouseReleaseEvent(QMouseEvent *e) 0185 { 0186 if(m_readonly) 0187 this->setCursor(Qt::ArrowCursor); 0188 else 0189 this->setCursor(Qt::CrossCursor); 0190 0191 if(!m_readonly && mode==Selection) { 0192 QPointF pd = toViewport(e->pos())-toViewport(press); 0193 0194 QPoint pd2 = e->pos()-press; 0195 QRect rr(press, QSize(pd2.x(), pd2.y())); 0196 0197 if(rr.width()>20 && rr.height()>20) { //if selection is not very small 0198 QRectF r(fromWidget(press), QSizeF(pd.x(), pd.y())); 0199 0200 if(r.top()<r.bottom()) { 0201 double aux = r.bottom(); 0202 r.setBottom(r.top()); 0203 r.setTop(aux); 0204 } 0205 0206 if(r.right()<r.left()) { 0207 double aux = r.left(); 0208 r.setLeft(r.right()); 0209 r.setRight(aux); 0210 } 0211 0212 setViewport(r); 0213 } else 0214 sendStatus(QCoreApplication::tr("Selected viewport too small")); 0215 } 0216 0217 mode = None; 0218 this->repaint(); 0219 } 0220 0221 void PlotsView2D::mouseMoveEvent(QMouseEvent *e) 0222 { 0223 cursorPos = e->pos(); 0224 QPair<QPointF, QString> img=calcImage(fromWidget(e->pos())); 0225 mark=img.first; 0226 0227 if(!m_readonly && mode==Pan && ant != toViewport(e->pos())){ 0228 moveViewport(e->pos() - press); 0229 0230 press = e->pos(); 0231 } else if(e->buttons()&Qt::LeftButton) { 0232 last = e->pos(); 0233 } else if(e->buttons()==0) { 0234 if(img.second.isEmpty()) { 0235 mark = fromWidget(e->pos()); 0236 sendStatus(QCoreApplication::tr("x=%1 y=%2").arg(mark.x()).arg(mark.y())); 0237 } else 0238 sendStatus(img.second); 0239 } 0240 0241 this->repaint(); 0242 } 0243 0244 void PlotsView2D::keyPressEvent(QKeyEvent * e) 0245 { 0246 //TODO use grid info (inc) here, made a public method to return current scale increment 0247 const double xstep=currentViewport().width()/12., ystep=currentViewport().height()/10.; 0248 0249 switch(e->key()) { 0250 case Qt::Key_Right: 0251 setViewport(lastUserViewport().translated(xstep, 0)); 0252 break; 0253 case Qt::Key_Left: 0254 setViewport(lastUserViewport().translated(-xstep, 0)); 0255 break; 0256 case Qt::Key_Down: 0257 setViewport(lastUserViewport().translated(0, -ystep)); 0258 break; 0259 case Qt::Key_Up: 0260 setViewport(lastUserViewport().translated(0, ystep)); 0261 break; 0262 case Qt::Key_Minus: 0263 zoomOut(); 0264 break; 0265 case Qt::Key_Plus: 0266 zoomIn(); 0267 break; 0268 default: 0269 return; 0270 } 0271 } 0272 0273 void PlotsView2D::resizeEvent(QResizeEvent * ev) 0274 { 0275 if (ev->size() != buffer.size()) { 0276 buffer = QPixmap(ev->size()); 0277 setPaintedSize(ev->size()); 0278 } 0279 } 0280 0281 bool PlotsView2D::toImage(const QString &path, Format f) 0282 { 0283 Q_ASSERT(!path.isEmpty()); 0284 bool b=false; 0285 0286 switch(f) { 0287 case SVG: { 0288 QFile f(path); 0289 QSvgGenerator gen; 0290 gen.setOutputDevice(&f); 0291 gen.setSize(this->size()); 0292 drawFunctions(&gen); 0293 b=true; 0294 forceRepaint(); 0295 } break; 0296 case PNG: 0297 this->repaint(); 0298 b=buffer.save(path, "PNG"); 0299 break; 0300 } 0301 0302 return b; 0303 } 0304 0305 void PlotsView2D::snapshotToClipboard() 0306 { 0307 QClipboard *cb = QApplication::clipboard(); 0308 0309 cb->setImage(buffer.toImage()); 0310 } 0311 0312 void Analitza::PlotsView2D::modelChanged() 0313 { 0314 } 0315 0316 void PlotsView2D::setReadOnly(bool ro) 0317 { 0318 m_readonly=ro; 0319 setCursor(ro ? Qt::ArrowCursor : Qt::CrossCursor); 0320 setMouseTracking(!ro); 0321 } 0322 0323 QRectF PlotsView2D::definedViewport() const 0324 { 0325 return lastUserViewport(); 0326 } 0327 0328 void PlotsView2D::viewportChanged() 0329 { 0330 QRectF userViewport=lastUserViewport(), viewport=currentViewport(); 0331 0332 sendStatus(QStringLiteral("(%1, %2)-(%3, %4)") 0333 .arg(viewport.left()).arg(viewport.top()).arg(viewport.right()).arg(viewport.bottom())); 0334 emit viewportChanged(userViewport); 0335 } 0336 0337 int PlotsView2D::currentFunction() const 0338 { 0339 if (!model()) 0340 return -1; 0341 0342 int ret=-1; 0343 if(m_selection) { 0344 ret=m_selection->currentIndex().row(); 0345 } 0346 0347 return ret; 0348 } 0349 0350 void PlotsView2D::setSelectionModel(QItemSelectionModel* selection) 0351 { 0352 Q_ASSERT(selection->model() == model()); 0353 0354 if (m_selection) 0355 disconnect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(forceRepaint())); 0356 0357 m_selection = selection; 0358 0359 connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(forceRepaint())); 0360 }