#include "graphic.h" #include "qad_types.h" #include "uglwidget.h" #include "ui_graphic.h" #include "ui_graphic_conf.h" #include #include #include #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) # include #endif #ifndef Q_OS_ANDROID # define HAS_GL #endif #ifdef HAS_GL # ifndef GL_MULTISAMPLE # define GL_MULTISAMPLE 0x809D # endif #endif __GraphicRegistrator__ __graphic_registrator__; Graphic::Graphic(QWidget * parent): QFrame(parent), canvas(0), line_x_min(this), line_x_max(this), line_y_min(this), line_y_max(this) { canvas_gl = 0; gesture_angle = 45.; leg_update = true; visible_update = fullscr = need_mouse_pan = false; gestures = #ifdef Q_OS_ANDROID true; #else false; #endif ui = new Ui::Graphic(); ui->setupUi(this); QActionGroup * agroup = new QActionGroup(this); agroup->addAction(ui->actionGuidesFree ); agroup->addAction(ui->actionGuidesTraceX); agroup->addAction(ui->actionGuidesTraceY); ui->actionGuidesFree ->setProperty("_value", (int)Free ); ui->actionGuidesTraceX->setProperty("_value", (int)TraceX); ui->actionGuidesTraceY->setProperty("_value", (int)TraceY); ui->actionGuidesFree->setChecked(true); connect(agroup, SIGNAL(triggered(QAction*)), this, SLOT(actionGuidesTriggered(QAction*))); ui->checkGuides->addAction(ui->actionGuidesFree ); ui->checkGuides->addAction(ui->actionGuidesTraceX); ui->checkGuides->addAction(ui->actionGuidesTraceY); ui->buttonAutofit->addAction(ui->actionExpandX); ui->buttonAutofit->addAction(ui->actionExpandY); line_x_min.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); line_x_max.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); ((QBoxLayout * )ui->widgetLY->layout())->insertWidget(0, &line_y_min); ((QBoxLayout * )ui->widgetLY->layout())->addWidget(&line_y_max); ((QBoxLayout * )ui->widgetLX->layout())->insertWidget(0, &line_x_min); ((QBoxLayout * )ui->widgetLX->layout())->addWidget(&line_x_max); tm.restart(); grid_numbers_x = grid_numbers_y = 1; LN10 = qLn(10.); line_x_min.setClearButtonVisible(true); line_x_max.setClearButtonVisible(true); line_y_min.setClearButtonVisible(true); line_y_max.setClearButtonVisible(true); connect(&line_x_min, SIGNAL(valueChanged(double)), this, SLOT(lineXMinChanged(double))); connect(&line_x_max, SIGNAL(valueChanged(double)), this, SLOT(lineXMaxChanged(double))); connect(&line_y_min, SIGNAL(valueChanged(double)), this, SLOT(lineYMinChanged(double))); connect(&line_y_max, SIGNAL(valueChanged(double)), this, SLOT(lineYMaxChanged(double))); connect(ui->canvas_raster, SIGNAL(paintEvent(QPaintEvent * )), this, SLOT(canvasPaintEvent())); prepareCanvas(ui->canvas_raster); #ifdef HAS_GL canvas_gl = new UGLWidget(); ui->layoutCanvas->addWidget(canvas_gl); connect(canvas_gl, SIGNAL(paintSignal()), this, SLOT(canvasPaintEvent())); prepareCanvas(canvas_gl); #endif icon_exp_x = QIcon(":/icons/expand_x.png"); icon_exp_y = QIcon(":/icons/expand_y.png"); icon_exp_sx = QIcon(":/icons/expand_s_x.png"); icon_exp_sy = QIcon(":/icons/expand_s_y.png"); icon_pause_b = QImage(":/icons/pause-back.png"); icon_pause_f = QImage(":/icons/pause-front.png"); aupdate = grid = isFit = navigation = true; aalias = bufferActive = isOGL = cancel = guides = hasLblX = hasLblY = isHover = false; pause_ = only_expand_x = only_expand_y = false; limit_.setCoords(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); eminx = eminy = DBL_MAX; emaxx = emaxy = DBL_MIN; grad_x = grad_y = Auto; axis_type_x = Numeric; floating_axis_type = Free; min_repaint_int = 25; inc_x = 1.; buffer = 0; gridx = gridy = 1.; history = 5.; visible_time = -1.; thick = lineThickness(); pause_phase = 0.; def_rect.setRect(0., 0., 1., 1.); selrect = def_rect; margins_.setRect(4, 4, 4, 4); curaction = gaMove; selbrush.setStyle(Qt::SolidPattern); selbrush.setColor(QColor(60, 175, 255, 100)); text_color = palette().color(QPalette::WindowText); grid_pen = QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 0., Qt::DotLine); graphics.append(GraphicType()); curGraphic = 0; selpen = palette().color(QPalette::WindowText); selpen.setStyle(Qt::DashLine); back_color = palette().color(QPalette::Base); buttons_ = AllButtons; setOpenGL(false); setButtonsPosition(Graphic::Left); setAntialiasing(false); setCaption(""); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ui->layoutButtons->update(); updateLegend(); setRectToLines(); conf = new GraphicConf(graphics, this); } Graphic::~Graphic() { delete conf; if (buffer != 0) delete buffer; } void Graphic::changeEvent(QEvent * e) { QFrame::changeEvent(e); if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); return; } } void Graphic::resizeEvent(QResizeEvent *) { if (leg_update) updateLegend(false); } void Graphic::timerEvent(QTimerEvent * ) { pause_phase += 0.02; if (pause_phase > 1.) pause_phase -= 1.; update(); } bool Graphic::eventFilter(QObject * o, QEvent * e) { if (o == canvas) { switch (e->type()) { case QEvent::Gesture: if (!navigation || !gestures) break; foreach (QGesture * g, ((QGestureEvent*)e)->gestures()) procGesture(g); break; case QEvent::KeyPress: { int k = ((QKeyEvent*)e)->key(); if ((k == Qt::Key_Back || k == Qt::Key_Escape) && fullscr) { leaveFullscreen(); return true; } } break; case QEvent::TouchBegin: if (!navigation || !gestures) break; need_mouse_pan = true; break; case QEvent::TouchUpdate: { if (!navigation || !gestures) break; QList tpl = ((QTouchEvent*)e)->touchPoints(); if (tpl.size() == 2) { need_mouse_pan = false; QPointF dp = tpl[0].scenePos() - tpl[1].scenePos(); gesture_angle = rad2deg_qpie * qAtan2(qAbs(dp.y()), qAbs(dp.x())); } } break; default: break; } } return QFrame::eventFilter(o, e); } void Graphic::prepareCanvas(QWidget * w) { connect(w, SIGNAL(mouseMoveEvent(QMouseEvent * )), this, SLOT(canvasMouseMoveEvent(QMouseEvent * ))); connect(w, SIGNAL(mousePressEvent(QMouseEvent * )), this, SLOT(canvasMousePressEvent(QMouseEvent * ))); connect(w, SIGNAL(mouseReleaseEvent(QMouseEvent * )), this, SLOT(canvasMouseReleaseEvent(QMouseEvent * ))); connect(w, SIGNAL(mouseDoubleClickEvent(QMouseEvent*)), this, SLOT(canvasMouseDoubleClickEvent(QMouseEvent * ))); connect(w, SIGNAL(wheelEvent(QWheelEvent * )), this, SLOT(canvasWheelEvent(QWheelEvent * ))); connect(w, SIGNAL(leaveEvent(QEvent * )), this, SLOT(canvasLeaveEvent(QEvent * ))); connect(w, SIGNAL(keyPressEvent(QKeyEvent * )), this, SLOT(canvasKeyPressEvent(QKeyEvent * ))); w->grabGesture(Qt::TapAndHoldGesture); w->grabGesture(Qt::PanGesture); w->grabGesture(Qt::PinchGesture); w->setMouseTracking(true); w->installEventFilter(this); } void Graphic::procGesture(QGesture * g) { if (!g) return; switch (g->gestureType()) { case Qt::PanGesture: { if (need_mouse_pan) break; QPanGesture * pg = (QPanGesture*)g; QPointF dp = -pg->delta(); dp.rx() /= getScaleX(); dp.ry() /= getScaleY(); selrect.translate(dp); totalUpdate(); } break; case Qt::PinchGesture: { QPinchGesture * pg = (QPinchGesture*)g; Qt::KeyboardModifiers km = Qt::NoModifier; if (gesture_angle <= 20.) km = Qt::ControlModifier; if (gesture_angle >= 70.) km = Qt::ShiftModifier; QPoint cp = pg->centerPoint().toPoint(); if (!fullscr) cp = mapFromGlobal(cp); procZoom(cp, (pg->scaleFactor() - 1.) * 500., km); totalUpdate(); } break; case Qt::TapAndHoldGesture: { QTapAndHoldGesture * pg = (QTapAndHoldGesture*)g; if (pg->state() == Qt::GestureStarted) QMetaObject::invokeMethod(this, "enterFullscreen", Qt::QueuedConnection); } break; default: break; } } void Graphic::procZoom(QPointF view_center, double dzoom, Qt::KeyboardModifiers km) { double scl, wid = canvas->width() - gridborder.x() - margins_.width() - margins_.left(), hei = canvas->height() - gridborder.y() - margins_.height() - margins_.top(); double px = view_center.x() - gridborder.x() - margins_.left(), py = hei - view_center.y() + margins_.height(); px = px / wid * selrect.width() + selrect.x(); py = py / hei * selrect.height() + selrect.y(); scl = 1. - dzoom / 500.; if (km == Qt::NoModifier) selrect.setRect(px - (px - selrect.x()) * scl, py - (py - selrect.y()) * scl, selrect.width() * scl, selrect.height() * scl); else { if (km == Qt::ControlModifier) selrect.setRect(px - (px - selrect.x()) * scl, selrect.y(), selrect.width() * scl, selrect.height()); if (km == Qt::ShiftModifier) selrect.setRect(selrect.x(), py - (py - selrect.y()) * scl, selrect.width(), selrect.height() * scl); } } void Graphic::totalUpdate() { isFit = false; emit visualRectChanged(); update(true); setRectToLines(); } void Graphic::canvasPaintEvent() { if (is_lines_update) return; int wid = canvas->width(), hei = canvas->height(); if (canvas->isHidden() || wid <= 1 || hei <= 1) return; lastw = wid; lasth = hei; font_sz = fontMetrics().size(0, "0"); font_sz.setHeight(font_sz.height() * 1.); #ifdef Q_OS_ANDROID font_sz.setWidth(font_sz.width() * 6); #else font_sz.setWidth(font_sz.width() * 8); #endif thick = lineThickness(); if (buffer != 0) if (buffer->width() != wid || buffer->height() != hei) {delete buffer; buffer = 0;} if (buffer == 0) buffer = new QImage(wid, hei, QImage::Format_RGB32); if (bufferActive) { QPainter p(canvas); p.drawImage(0, 0, *buffer); painter = &p; fp_size.clear(); if (curpos != startpos) drawAction(); drawGuides(); return; } QPainter p; #ifdef HAS_GL if (isOGL) { p.fillRect(canvas->rect(), Qt::black); glClearColor(0.f, 0.f, 0.f, 0.f); p.begin(canvas); } else #endif p.begin(buffer); p.fillRect(canvas->rect(), back_color); painter = &p; p.setFont(font()); gridborder = QPoint(5, 5); if (grid) { gridborder += QPoint(font_sz.width(), font_sz.height()); if (hasLblY) gridborder += QPoint(font_sz.height(), 0); if (hasLblX) gridborder += QPoint(0, font_sz.height()); } painter->setClipping(true); painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y())); emit beforeGraphicPaintEvent(painter); painter->setClipping(false); if (grid) drawGrid(); p.setRenderHint(QPainter::Antialiasing, aalias); #ifndef ANDROID if (isOGL) { if (aalias) glEnable(GL_MULTISAMPLE); else glDisable(GL_MULTISAMPLE); } #endif fp_size.clear(); if (!aalias) p.translate(-0.5, -0.5); drawGraphics(); drawGuides(); if (pause_) drawPause(); emit graphicPaintEvent(painter); p.end(); if (isOGL) return; p.begin(canvas); p.drawImage(0, 0, *buffer); p.end(); } void Graphic::canvasMouseMoveEvent(QMouseEvent * e) { isHover = true; curpos = e->pos(); curpos_r = canvas2real(curpos); QPointF dp; QString cursorstr = tr("Cursor: ") + pointCoords(curpos_r); emit graphicMouseMoveEvent(curpos_r, e->buttons()); if (e->buttons() == Qt::NoButton) { ui->status->setText(cursorstr); if (guides) update(); return; } if (!navigation) return; if (gestures) { if (!need_mouse_pan) return; curaction = gaMove; } else if (curaction != gaMove && (e->buttons() & Qt::RightButton) == Qt::RightButton) return; switch (curaction) { case gaZoomInRect: ui->status->setText(tr("Selection") + ": " + pointCoords(startpos_r) + " -> " + pointCoords(curpos_r) + ", " + tr("Size") + ": " + pointCoords(absPoint(curpos_r - startpos_r))); repaintCanvas(true); break; case gaZoomRangeX: ui->status->setText(tr("Range") + ": " + QString::number(startpos_r.x(), 'f', 3) + " -> " + QString::number(curpos_r.x(), 'f', 3) + ", " + tr("Length") + ": " + QString::number(qAbs(curpos_r.x() - startpos_r.x()), 'f', 3)); repaintCanvas(true); break; case gaZoomRangeY: ui->status->setText(tr("Range") + ": " + QString::number(startpos_r.y(), 'f', 3) + " -> " + QString::number(curpos_r.y(), 'f', 3) + ", " + tr("Length") + ": " + QString::number(qAbs(curpos_r.y() - startpos_r.y()), 'f', 3)); repaintCanvas(true); break; case gaMove: dp = e->pos() - prevpos; dp.rx() *= selrect.width() / double(gridborder.x() + 5 - lastw); dp.ry() *= selrect.height() / double(lasth - gridborder.y() - 5); if (e->modifiers() == Qt::ControlModifier) dp.setY(0.); if (e->modifiers() == Qt::ShiftModifier) dp.setX(0.); selrect.translate(dp); totalUpdate(); break; default: break; } prevpos = e->pos(); } void Graphic::canvasMousePressEvent(QMouseEvent * e) { emit graphicMousePressEvent(canvas2real(QPointF(e->pos())), e->button()); if (!navigation) return; if (gestures && !need_mouse_pan) return; setGuidesCursor(); prevpos = e->pos(); startpos = prevpos; startpos_r = canvas2real(startpos); if (cancel || gestures) return; if (e->button() == Qt::MidButton) curaction = gaMove; if (e->button() == Qt::RightButton) { if (bufferActive) { curpos = startpos; curpos_r = canvas2real(curpos); repaintCanvas(true); swapToNormal(); cancel = true; return; } else { prevaction = curaction; setCurrentAction(gaMove); return; } } if (e->button() == Qt::LeftButton) { if (e->modifiers() == Qt::ControlModifier) curaction = gaZoomRangeX; else if (e->modifiers() == Qt::ShiftModifier) curaction = gaZoomRangeY; else curaction = gaZoomInRect; switch (curaction) { case gaZoomInRect: case gaZoomRangeX: case gaZoomRangeY: swapToBuffer(); break; default: break; } } setCurrentAction(curaction); } void Graphic::canvasMouseReleaseEvent(QMouseEvent * e) { emit graphicMouseReleaseEvent(canvas2real(QPointF(e->pos())), e->button()); if (gestures) return; need_mouse_pan = false; if (!navigation) return; setGuidesCursor(); QPointF tlp, brp; QRect sr; sr = QRect(startpos, curpos).normalized(); if (cancel) { if (e->buttons() == Qt::NoButton) cancel = false; return; } if (e->button() == Qt::RightButton && curaction == gaMove) { curaction = prevaction; return; } if (e->button() == Qt::LeftButton && (e->buttons() & Qt::RightButton) != Qt::RightButton) { if (curpos != startpos) { tlp = canvas2real(sr.topLeft()); brp = canvas2real(sr.bottomRight()); isFit = false; switch (curaction) { case gaZoomInRect: if (sr.width() <= 1 || sr.height() <= 1) break; selrect.setCoords(tlp.x(), brp.y(), brp.x(), tlp.y()); setRectToLines(); break; case gaZoomRangeX: if (sr.width() <= 1) break; findGraphicsRect(tlp.x(), brp.x()); break; case gaZoomRangeY: if (sr.height() <= 1) break; findGraphicsRect(0., 0., brp.y(), tlp.y()); break; default: return; } } swapToNormal(); update(true); } QPointF rp = canvas2real(QPointF(e->pos())); ui->status->setText(tr("Cursor") + ": " + pointCoords(rp)); emit visualRectChanged(); } void Graphic::canvasMouseDoubleClickEvent(QMouseEvent * ) { autofit(); } void Graphic::canvasWheelEvent(QWheelEvent * e) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) emit graphicWheelEvent(canvas2real(e->position()), e->delta()/* TODO: test use angleDelta()*/); #else emit graphicWheelEvent(canvas2real(QPointF(e->pos())), e->delta()); #endif if (gestures) return; if (!navigation) return; #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) procZoom(e->position(), e->delta(), e->modifiers()); #else procZoom(e->pos(), e->delta(), e->modifiers()); #endif totalUpdate(); } void Graphic::zoom(float factor) { double wid = canvas->width() - gridborder.x() - margins_.width() - margins_.left(), hei = canvas->height() - gridborder.y() - margins_.height() - margins_.top(); double px = wid / 2, py = hei / 2; px = px / wid * selrect.width() + selrect.x(); py = py / hei * selrect.height() + selrect.y(); selrect.setRect(px - (px - selrect.x()) * factor, py - (py - selrect.y()) * factor, selrect.width() * factor, selrect.height() * factor); isFit = false; update(true); setRectToLines(); } void Graphic::fullscreen() { if (fullscr) leaveFullscreen(); else enterFullscreen(); } void Graphic::canvasLeaveEvent(QEvent * ) { isHover = false; if (guides) update(true); ui->status->setText(tr("Cursor") + ": ( ; )"); leaveFullscreen(); } void Graphic::canvasKeyPressEvent(QKeyEvent * e) { switch (e->key()) { case Qt::Key_Escape: leaveFullscreen(); default: break; }; } void Graphic::clear() { for (int i = 0; i < graphics.size(); ++i) { graphics[i].polyline.clear(); graphics[i].polyline_pause.clear(); graphics[i].max_x = 0.; graphics[i].cvrect = QRectF(); } if (isFit) on_buttonAutofit_clicked(); } void Graphic::setAntialiasing(bool enabled) { if (aalias == enabled) return; aalias = enabled; update(); } void Graphic::setPaused(bool yes) { pause_ = yes; ui->checkPause->blockSignals(true); ui->checkPause->setChecked(yes); ui->checkPause->blockSignals(false); for (int i = 0; i < graphics.size(); ++i) graphics[i].cvrect = QRectF(); if (!pause_) { killTimer(timer_pause); timer_pause = 0; update(true); return; } for (int i = 0; i < graphics.size(); ++i) { graphics[i].polyline_pause = graphics[i].polyline; graphics[i].polyline_pause.detach(); graphics[i].max_x_pause = graphics[i].max_x; } timer_pause = startTimer(40); } void Graphic::setHistorySize(double val) { history = val; double x; for (int i = 0; i < graphics.size(); ++i) { QPolygonF & pol(graphics[i].polyline); if (pol.isEmpty() || history <= 0.) continue; graphics[i].cvrect = QRectF(); x = pol.back().x() - history; for (int j = pol.size() - 2; j >= 0 ; --j) if (pol[j].x() < x) { pol.erase(pol.begin(), pol.begin() + j); break; } } } void Graphic::setMaxVisibleTime(double val) { visible_time = val; if (isFit) on_buttonAutofit_clicked(); } void Graphic::setOnlyExpandY(bool yes) { ui->actionExpandY->setChecked(yes); } void Graphic::setOnlyExpandX(bool yes) { ui->actionExpandX->setChecked(yes); } void Graphic::setGesturesNavigation(bool yes) { gestures = yes; } Graphic::GraphicsData Graphic::graphicsData() const { GraphicsData ret; ret.resize(graphics.size()); for (int i = 0; i < graphics.size(); ++i) ret[i] = graphics[i].polyline; return ret; } QByteArray Graphic::graphicsDataRaw() const { QByteArray ret; QDataStream s(&ret, QIODevice::WriteOnly); s << graphicsData(); return ret; } void Graphic::setGraphicsData(const Graphic::GraphicsData & gd) { setGraphicsCount(gd.size()); for (int i = 0; i < gd.size(); ++i) setGraphicData(gd[i], i, false); updateGraphics(); } void Graphic::setGraphicsDataRaw(const QByteArray & ba) { if (ba.isEmpty()) { clear(); return; } Graphic::GraphicsData gd; QDataStream s(ba); s >> gd; setGraphicsData(gd); } void Graphic::setButtons(Graphic::Buttons b) { buttons_ = b; ui->buttonAutofit->setVisible(b.testFlag(Autofit)); ui->checkGrid->setVisible(b.testFlag(Grid)); ui->checkGuides->setVisible(b.testFlag(CursorAxis)); ui->buttonFullscreen->setVisible(b.testFlag(Fullscreen)); ui->checkBorderInputs->setVisible(b.testFlag(BorderInputs)); ui->checkLegend->setVisible(b.testFlag(Legend)); ui->buttonClear->setVisible(b.testFlag(Clear)); ui->buttonConfigure->setVisible(b.testFlag(Configure)); ui->buttonSave->setVisible(b.testFlag(Save)); ui->buttonClose->setVisible(b.testFlag(Close)); ui->checkPause->setVisible(b.testFlag(Pause)); if (ui->buttonAutofit->isVisible() || ui->checkGrid->isVisible() || ui->checkGuides->isVisible() || ui->buttonConfigure->isVisible() || ui->buttonSave->isVisible() || ui->checkPause->isVisible()) ui->verticalSpacer->changeSize(0, 30, QSizePolicy::Preferred, QSizePolicy::Preferred); else ui->verticalSpacer->changeSize(0, 0, QSizePolicy::Preferred, QSizePolicy::Preferred); ui->layoutButtons->update(); } void Graphic::setButtonsPosition(Graphic::Alignment a) { align = a; ui->widgetLeft->hide(); ui->widgetRight->hide(); switch (a) { case Graphic::Left: ui->widgetLeft->setLayout(ui->layoutButtons); ui->widgetLeft->show(); break; case Graphic::Right: ui->widgetRight->setLayout(ui->layoutButtons); ui->widgetRight->show(); break; } } void Graphic::addPoint(const QPointF & p, int graphic, bool update_) { if (graphic >= graphics.size() || graphic < 0) return; GraphicType & t(graphics[graphic]); if (!t.cvrect.isNull() && !pause_) { if (t.cvrect.top() < p.y()) t.cvrect.setTop(p.y()); if (t.cvrect.bottom() > p.y()) t.cvrect.setBottom(p.y()); if (t.cvrect.right() < p.x()) t.cvrect.setRight(p.x()); if (t.cvrect.left() > p.x()) t.cvrect.setLeft(p.x()); } if (t.polyline.size() == 0) t.max_x = p.x(); t.polyline << p; if (t.max_x < p.x()) t.max_x = p.x(); tick(graphic, true, update_); } void Graphic::setGraphicData(const QVector & g, int graphic, bool update_) { if (graphic >= graphics.size() || graphic < 0) return; GraphicType & t(graphics[graphic]); t.cvrect = QRectF(); t.polyline.clear(); t.polyline = g; if (t.polyline.size() == 0) { t.max_x = 0.; tick(graphic, false, update_); return; } t.max_x = t.polyline[0].x(); for (int i = 1; i < t.polyline.size(); ++i) if (t.max_x < t.polyline[i].x()) t.max_x = t.polyline[i].x(); tick(graphic, false, update_); } void Graphic::setGraphicProperties(int graphic, const QString & name, const QColor& color, Qt::PenStyle style, double width, bool visible) { if (graphic < 0 || graphic >= graphics.size()) return; graphics[graphic].name = name; graphics[graphic].pen.setColor(color); graphics[graphic].pen.setStyle(style); graphics[graphic].pen.setWidth(width); graphics[graphic].visible = visible; updateLegend(); } void Graphic::addGraphic(const QString & name, const QColor & color, Qt::PenStyle style, double width, bool visible) { graphics << GraphicType(name, color, style, width, visible); updateLegend(); } void Graphic::setVisualRect(const QRectF & rect) { selrect = rect; isFit = false; update(); } void Graphic::setDefaultRect(const QRectF & rect) { def_rect = rect; if (isFit) autofit(); } void Graphic::saveImage() { QString str = QFileDialog::getSaveFileName(this, tr("Save Image"), ppath, "PNG(*.png);;JPEG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tiff *.tif);;PPM(*.ppm)"); if (str == "") return; ppath = str; QPixmap im(canvas->size()); canvas->render(&im); im.save(ppath); update(true); } void Graphic::setOpenGL(bool on) { #ifdef HAS_GL isOGL = on; if (on) { ui->canvas_raster->hide(); canvas_gl->show(); canvas = canvas_gl; } else { canvas_gl->hide(); ui->canvas_raster->show(); canvas = ui->canvas_raster; } #else isOGL = false; ui->canvas_raster->show(); canvas = ui->canvas_raster; #endif update(); } void Graphic::update(bool force) { repaintCanvas(force); } void Graphic::setGraphicsCount(int arg, bool update) { if (arg < 0) return; while (graphics.size() < arg) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) graphics.append(GraphicType(tr("y(x)"), QColor::fromHsv((graphics.size() * 55) % 360, 255, 255 - QRandomGenerator::global()->generate() % 115))); #else graphics.append(GraphicType(tr("y(x)"), QColor::fromHsv((graphics.size() * 55) % 360, 255, 255 - qrand() % 115))); #endif } while (graphics.size() > arg) { delete graphics.back().pb; graphics.pop_back(); } if (update) updateLegend(); } void Graphic::removeGraphic(int arg, bool update) { if (arg < 0 || arg >= graphics.size()) return; delete graphics[arg].pb; graphics.remove(arg); if (update) updateLegend(); } void Graphic::findGraphicsRect(double start_x, double end_x, double start_y, double end_y) { double cx, cy, maxX, minX, maxY, minY, vx; bool isRangeX = (start_x != end_x), isRangeY = (start_y != end_y); bool can_fast = (start_x == 0 && end_x == 0 && start_y == 0 && end_y == 0); bool anyVisible = false, isTimeLimit = (visible_time > 0.) && !(isRangeX || isRangeY); vx = -DBL_MAX; minY = minX = DBL_MAX; maxY = maxX = -DBL_MAX; foreach (const GraphicType & t, graphics) { if (!t.visible) continue; if (vx < (pause_ ? t.max_x_pause : t.max_x)) vx = (pause_ ? t.max_x_pause : t.max_x); } vx -= visible_time; for (int g = 0; g < graphics.size(); g++) { GraphicType & t(graphics[g]); if (!t.visible) continue; const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline); if (pol.isEmpty()) continue; bool f = true; if (t.cvrect.isNull() || !can_fast) { for (int i = 0; i < pol.size(); i++) { cx = pol[i].x(); cy = pol[i].y(); if ((start_x > cx || end_x < cx) && isRangeX) continue; if ((start_y > cy || end_y < cy) && isRangeY) continue; if ((cx < vx) && isTimeLimit) continue; if (f) { t.cvrect.setRect(cx, cy, 0, 0); f = false; } else { if (t.cvrect.top() < cy) t.cvrect.setTop(cy); if (t.cvrect.bottom() > cy) t.cvrect.setBottom(cy); if (t.cvrect.right() < cx) t.cvrect.setRight(cx); if (t.cvrect.left() > cx) t.cvrect.setLeft(cx); } } if (f) continue; } anyVisible = true; if (maxY < t.cvrect.top()) maxY = t.cvrect.top(); if (minY > t.cvrect.bottom()) minY = t.cvrect.bottom(); if (maxX < t.cvrect.right()) maxX = t.cvrect.right(); if (minX > t.cvrect.left()) minX = t.cvrect.left(); if (!can_fast) t.cvrect = QRectF(); } if (!anyVisible) { grect = def_rect; setRectToLines(); return; } if (maxX > limit_.right()) maxX = limit_.right(); if (minX > limit_.right()) minX = limit_.right(); if (minX < limit_.left()) minX = limit_.left(); if (maxX < limit_.left()) maxX = limit_.left(); if (maxY > limit_.bottom()) maxY = limit_.bottom(); if (minY > limit_.bottom()) minY = limit_.bottom(); if (minY < limit_.top()) minY = limit_.top(); if (maxY < limit_.top()) maxY = limit_.top(); if (minX > maxX) qSwap(minX, maxX); if (minY > maxY) qSwap(minY, maxY); if (qAbs(minX - maxX) < 1E-60) {minX -= defaultRect().width()/2; maxX += defaultRect().width()/2;} if (qAbs(minY - maxY) < 1E-60) {minY -= defaultRect().height()/2; maxY += defaultRect().height()/2;} if (only_expand_x) { if (minX > eminx) minX = eminx; if (maxX < emaxx) maxX = emaxx; } if (only_expand_y) { if (minY > eminy) minY = eminy; if (maxY < emaxy) maxY = emaxy; } eminx = minX; emaxx = maxX; eminy = minY; emaxy = maxY; if (isRangeX) selrect.setRect(start_x, minY, end_x - start_x, maxY - minY); else if (isRangeY) selrect.setRect(minX, start_y, maxX - minX, end_y - start_y); else grect.setRect(minX, minY, maxX - minX, maxY - minY); grect = grect.normalized(); if (isFit) { if (visible_time > 0.) { if (grect.width() > visible_time) grect.setLeft(grect.right() - visible_time); } selrect = grect; } setRectToLines(); } void Graphic::drawAction() { int wid = canvas->width(), hei = canvas->height() - gridborder.y(), sx = startpos.x(), sy = startpos.y(), cx = curpos.x(), cy = curpos.y(); painter->setPen(selpen); painter->setBrush(selbrush); switch (curaction) { case gaZoomInRect: { QSizeF rsz = QRectF(startpos_r, curpos_r).normalized().size(); painter->drawRect(QRect(startpos, curpos)); fp_size = " x " + pointCoords(QPointF(rsz.width(), rsz.height())); } break; case gaZoomRangeX: painter->drawLine(sx, hei, sx, 0); painter->drawLine(cx, hei, cx, 0); painter->fillRect(sx, 0, cx - sx, hei, selbrush); fp_size = " x " + pointCoords(QPointF(qAbs(startpos_r.x() - curpos_r.x()), 0.), true, false); break; case gaZoomRangeY: painter->drawLine(gridborder.x(), sy, wid, sy); painter->drawLine(gridborder.x(), cy, wid, cy); painter->fillRect(gridborder.x(), sy, wid - gridborder.x(), cy - sy, selbrush); fp_size = " x " + pointCoords(QPointF(0., qAbs(startpos_r.y() - curpos_r.y())), false, true); break; default: break; } } void Graphic::drawGrid() { int gbx = gridborder.x(), gby = gridborder.y(), cwid = canvas->width(), chei = canvas->height(); double px, py, range, step, start; int wid = cwid - gbx - 5, hei = chei - gby - 5, cx, cy, cnt; QRect rect; QPair str; range = selrect.bottom() - selrect.top(); if (grad_y == Graphic::Auto) step = splitRange(range, hei / gridy / font_sz.height() / 1.4); else step = gridy; start = roundTo(canvas2realY(-hei), step) - step; py = start + step; cy = 0; cx = gbx - 5; grid_pen.setWidth(qMax(qMax(qRound(thick / 1.4), 1), grid_pen.width())); QFont sf = font(); QFont nf = sf; sf.setPointSizeF(qMax(sf.pointSizeF() / 1.6, 7.)); QFontMetrics fm(nf), sfm(sf); if (step > 0.) { cnt = 1000; while (cnt-- > 0) { py -= step; if (fabs(py) < step * .5) py = 0.; cy = real2canvasY(py); if (cy < 0) continue; if (cy > hei + 5) break; painter->setPen(grid_pen); painter->drawLine(gbx, cy, cwid, cy); str = gridMark(py * grid_numbers_y); painter->setPen(text_color); cy += font_sz.height() / 4.; int dx = font_sz.height() / 8.; if (!str.second.isEmpty()) { rect = sfm.boundingRect(str.second); painter->setFont(sf); painter->drawText(cx - rect.width() - dx, cy - font_sz.height() / 2.5, str.second); dx += rect.width() + font_sz.height() / 6.; } rect = fm.boundingRect(str.first); painter->setFont(nf); painter->drawText(cx - rect.width() - dx, cy, str.first); } } cy = real2canvasY(0.); if (cy >= 0 && cy <= (hei + 5)) { QPen _p(grid_pen); _p.setStyle(Qt::SolidLine); painter->setPen(_p); painter->drawLine(gbx, cy, cwid, cy); } if (hasLblY) { painter->setPen(text_color); painter->save(); painter->translate(5, hei); painter->rotate(-90.); painter->drawText(0, 0, hei, font_sz.height(), Qt::AlignCenter, label_y); painter->restore(); } cy = chei - font_sz.height() / 4; if (hasLblX) cy -= font_sz.height(); range = selrect.right() - selrect.left(); QString df; if (axis_type_x == Graphic::Numeric) { if (grad_x == Graphic::Auto) step = splitRange(range, wid / gridx / font_sz.width() * 1.4); else step = gridx; start = roundTo(canvas2realX(wid), step) + step; px = start + step; if (step > 0.) { cnt = 1000; while (cnt-- > 0) { px -= step; if (fabs(px) < step * .5) px = 0.; cx = real2canvasX(px); if (cx > cwid) continue; if (cx < gbx) break; painter->setPen(grid_pen); painter->drawLine(cx, hei + 5, cx, 0); painter->setPen(text_color); int dx = -font_sz.height() / 4.; painter->setFont(nf); str = gridMark(px * grid_numbers_x); rect = fm.boundingRect(str.first); painter->drawText(cx + dx, cy, str.first); dx += rect.width() + font_sz.height() / 6.; if (!str.second.isEmpty()) { rect = sfm.boundingRect(str.second); painter->setFont(sf); painter->drawText(cx + dx, cy - font_sz.height() / 4., str.second); } } } cx = real2canvasX(0.); if (cx <= cwid && cx >= gbx) { QPen _p(grid_pen); _p.setStyle(Qt::SolidLine); painter->setPen(_p); painter->drawLine(cx, hei + 5, cx, 0); } } else { int cur_scl[7] = {0,0,0,0,0,0,0}; step = splitRangeDate(range, wid / gridx / font_sz.width() * 1.4, &df, cur_scl); start = roundTo(canvas2realX(wid), step) + step; px = start + step; QDateTime cd = QDateTime::fromMSecsSinceEpoch(px * grid_numbers_x); roundDateTime(cd, cur_scl); addDateTime(cd, cur_scl); if (step > 0.) { cnt = 1000; while (cnt-- > 0) { addDateTime(cd, cur_scl, -1); cx = real2canvasX(cd.toMSecsSinceEpoch() / grid_numbers_x); if (cx > cwid) continue; if (cx < gbx) break; painter->setPen(grid_pen); painter->drawLine(cx, hei + 5, cx, 0); painter->setPen(text_color); int dx = -font_sz.height() / 4.; painter->setFont(nf); str.first = cd.toString(df); painter->drawText(cx + dx, cy, str.first); } } } painter->setPen(text_color); painter->setFont(nf); if (hasLblX) { painter->setPen(text_color); painter->drawText(gbx, chei - font_sz.height(), wid, font_sz.height(), Qt::AlignCenter, label_x); } painter->setPen(QPen(grid_pen.color(), qMax(thick, grid_pen.width()))); painter->drawRect(gbx, -1, wid + 6, hei + 6); } QPair Graphic::gridMark(double v) const { QPair ret; if ((qAbs(v) >= 1E+4 || qAbs(v) <= 1E-4) && v != 0.) { int p = qFloor(qLn(qAbs(v)) / LN10); v /= qPow(10., p); if (v == 10.) { v = 1.; p += 1; } ret.first = QString::fromUtf8("%1ยท10").arg(v); ret.second = QString::number(p); } else ret.first = QString::number(v); return ret; } void Graphic::drawGraphics() { if (isHover) ui->status->setText(tr("Cursor: ") + pointCoords(canvas2real(QPointF(curpos)))); QPointF srp = -selrect.topLeft(); double sclx, scly, wid = canvas->width(), hei = canvas->height(); sclx = (wid - gridborder.x() - margins_.left() - margins_.width()) / selrect.width(); scly = (hei - gridborder.y() - margins_.top() - margins_.height()) / selrect.height(); painter->setClipping(true); painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y())); painter->translate(gridborder.x() + margins_.left(), hei - gridborder.y() - margins_.top()); painter->scale(sclx, -scly); painter->translate(srp); QTransform mat = painter->transform(); painter->resetTransform(); painter->setWorldMatrixEnabled(false); QPolygonF cpol; QPen pen; for (int i = 0; i < graphics.size(); ++i) { GraphicType & t(graphics[i]); QPolygonF & rpol(pause_ ? t.polyline_pause : t.polyline); if (t.visible && !rpol.isEmpty()) { pen = t.pen; if (qRound(pen.widthF()) == pen.widthF()) pen.setWidth(pen.width()*thick); else pen.setWidthF(pen.widthF()*thick); pen.setCosmetic(true); if (t.lines) { painter->setPen(pen); if (t.fill) { cpol = rpol; painter->setBrush(t.fill_color); painter->drawPolygon(mat.map(cpol)); } else painter->drawPolyline(mat.map(rpol)); } if (t.points) { if (qRound(t.pointWidth) == t.pointWidth) pen.setWidth(qRound(t.pointWidth*thick)); else pen.setWidthF(t.pointWidth*thick); painter->setPen(pen); painter->drawPoints(mat.map(rpol)); } } } painter->setWorldMatrixEnabled(true); } QString Graphic::pointCoords(QPointF point, bool x, bool y) { QString ret = "("; if (x) { if (axis_type_x == Numeric) ret += QString::number(point.x(), 'f', 3); else ret += QDateTime::fromMSecsSinceEpoch(point.x()).toString(); } if (y) { if (ret.size() > 1) ret += " ; "; ret += QString::number(point.y(), 'f', 3); } ret += ")"; return ret; } void Graphic::drawGuides() { if (!guides || !isHover) return; int wid = canvas->width(), hei = canvas->height(); painter->setRenderHint(QPainter::Antialiasing, false); painter->setPen(QPen(grid_pen.color(), qMax(qRound(thick / 1.4), 1))); painter->resetTransform(); painter->setClipping(true); painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y())); QPoint apos = curpos; QPointF rpos = canvas2real(apos); QString str; str = pointCoords(rpos) + fp_size; switch (floating_axis_type) { case TraceX: if (curGraphic >= 0 && curGraphic < graphics.size()) { QPolygonF & pol(pause_ ? graphics[curGraphic].polyline_pause : graphics[curGraphic].polyline); double cursor = rpos.x(), min_dist = -1, dist = 0.; int index = -1; for (int i = 0; i < pol.size(); ++i) { dist = qAbs(pol[i].x() - cursor); if (min_dist > dist || min_dist < 0) { min_dist = dist; index = i; } } if (index >= 0) { rpos = pol[index]; apos = real2canvas(rpos).toPoint(); str = pointCoords(pol[index]) + fp_size; } } break; case TraceY: if (curGraphic >= 0 && curGraphic < graphics.size()) { QPolygonF & pol(pause_ ? graphics[curGraphic].polyline_pause : graphics[curGraphic].polyline); double cursor = rpos.y(), min_dist = -1, dist = 0.; int index = -1; for (int i = 0; i < pol.size(); ++i) { dist = qAbs(pol[i].y() - cursor); if (min_dist > dist || min_dist < 0) { min_dist = dist; index = i; } } if (index >= 0) { rpos = pol[index]; apos = real2canvas(rpos).toPoint(); str = pointCoords(pol[index]) + fp_size; } } break; default: break; } painter->drawLine(0, apos.y(), wid, apos.y()); painter->drawLine(apos.x(), 0, apos.x(), hei); QPoint p = apos + QPoint(font_sz.height() / 4., -font_sz.height() / 4.); QFontMetrics fm(font()); QRect r = fm.boundingRect(str); if (r.width() + apos.x() > wid - font_sz.height() / 2.) p.setX(apos.x() - r.width() - font_sz.height() / 4.); if (apos.y() - r.height() < font_sz.height() / 8.) p.setY(apos.y() + r.height() - font_sz.height() / 8.); painter->setPen(text_color); painter->drawText(p, str); } void Graphic::drawPause() { painter->setClipping(false); painter->save(); painter->resetTransform(); painter->translate(canvas->width() - icon_pause_b.width() - 6, 6); double o = (0.5 - pause_phase) * 2; painter->setOpacity(o*o); painter->drawImage(0, 0, icon_pause_b); painter->setOpacity(1.); painter->drawImage(0, 0, icon_pause_f); painter->restore(); painter->setClipping(true); } double Graphic::splitRange(double range, int count) { double digits, step, tln; range = qAbs(range); tln = qFloor(qLn(range) / LN10); for (int i = 0; i <= 5; ++i) { digits = qPow(10., tln - i); step = qRound(range / count / digits); if (step > 0.) { digits = qPow(10., tln - i - 1); step = qRound(range / count / digits); break; } } double step5 = qRound(step / 5.) * 5., step10 = qRound(step / 10.) * 10.; double err5 = qAbs(step - step5), err10 = qAbs(step - step10); step = (err5 < err10 ? step5 : step10) * digits; return step; } double Graphic::splitRangeDate(double range, int count, QString * format, int step[7]) { double ret = splitRange(range, count); if (ret < 1000. * 1) {*format = "ss.zzz"; step[0] = ret;} else if (ret < 1000. * 60) {*format = "h:m:ss"; step[1] = qRound(ret / 1000);} else if (ret < 1000. * 60 * 60) {*format = "h:mm"; step[2] = qRound(ret / 1000 / 60);} else if (ret < 1000. * 60 * 60 * 24) {*format = "dd(ddd) hh"; step[3] = qRound(ret / 1000 / 60 / 60);} else if (ret < 1000. * 60 * 60 * 24 * 30) {*format = "MMM dd"; step[4] = qRound(ret / 1000 / 60 / 60 / 24);} else if (ret < 1000. * 60 * 60 * 24 * 30 * 12) {*format = "yyyy MMM"; step[5] = qRound(ret / 1000 / 60 / 60 / 24 / 30);} else {*format = "yyyy"; step[6] = qRound(ret / 1000 / 60 / 60 / 24 / 30 / 12);} return ret; } double Graphic::roundTo(double value, double round_to) { if (round_to == 0.) return value; return qRound(value / round_to) * round_to; } void Graphic::roundDateTime(QDateTime & dt, int c[7]) { QDate d(dt.date()); QTime t(dt.time()); if (c[1] != 0) t.setHMS(t.hour(), t.minute(), t.second()); if (c[2] != 0) t.setHMS(t.hour(), t.minute(), 0); if (c[3] != 0) t.setHMS(t.hour(), 0, 0); if (c[4] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), d.day());} if (c[5] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), 1);} if (c[6] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), 1, 1);} dt = QDateTime(d, t); } void Graphic::addDateTime(QDateTime & dt, int c[7], int mul) { if (c[0] != 0) dt = dt.addMSecs(mul * c[0]); if (c[1] != 0) dt = dt.addSecs(mul * c[1]); if (c[2] != 0) dt = dt.addSecs(mul * c[2] * 60); if (c[3] != 0) dt = dt.addSecs(mul * c[3] * 60 * 60); if (c[4] != 0) dt = dt.addDays(mul * c[4]); if (c[5] != 0) dt = dt.addMonths(mul * c[5]); if (c[6] != 0) dt = dt.addYears(mul * c[6]); } double Graphic::canvas2realX(double px) const { int gbx = gridborder.x() + margins_.left(), cwid = lastw, wid = cwid - gbx - margins_.width(); double cx = px - gbx, sclx = selrect.width() / (double)wid; return cx * sclx + selrect.x(); } double Graphic::canvas2realY(double py) const { int gby = gridborder.y() + margins_.top(), chei = lasth, hei = chei - gby - margins_.height(); double cy = chei - py - gby, scly = selrect.height() / (double)hei; return cy * scly + selrect.y(); } double Graphic::real2canvasX(double px) const { int gbx = gridborder.x() + margins_.left(), cwid = lastw, wid = cwid - gbx - margins_.width(); double sclx = selrect.width() / (double)wid; return (px - selrect.x()) / sclx + gbx; } double Graphic::real2canvasY(double py) const { int gby = gridborder.y() + margins_.top(), chei = lasth, hei = chei - gby - margins_.height(); double scly = selrect.height() / (double)hei; return chei - gby - (py - selrect.y()) / scly; } QPolygonF Graphic::real2canvas(const QPolygonF & real_polygon) const { QPolygonF ret; for (int i=0; icanvas_raster->setCursor(cursor); #ifdef HAS_GL canvas_gl->setCursor(cursor); #endif } void Graphic::setGuidesCursor() { if (guides) { setCanvasCursor(floating_axis_type == Free ? Qt::BlankCursor : Qt::CrossCursor); } else setCanvasCursor(Qt::ArrowCursor); } void Graphic::swapToBuffer() { QImage timg; #ifdef HAS_GL if (isOGL) { timg = canvas_gl->grabFrameBuffer(); QPainter p(buffer); p.drawImage(0, 0, timg); p.end(); } #endif bufferActive = true; } void Graphic::setRectToLines() { is_lines_update = true; if (line_x_min.isVisible() && line_x_max.isVisible() && line_y_min.isVisible() && line_y_max.isVisible()) { line_x_min.blockSignals(true); line_x_max.blockSignals(true); line_y_min.blockSignals(true); line_y_max.blockSignals(true); if (!line_x_min.hasFocus()) { if (isFit) line_x_min.setValue(grect.left()); else line_x_min.setValue(selrect.left()); } if (!line_x_max.hasFocus()) { if(isFit) line_x_max.setValue(grect.right()); else line_x_max.setValue(selrect.right()); } if (!line_y_min.hasFocus()) { if(isFit) line_y_min.setValue(grect.bottom()); else line_y_min.setValue(selrect.bottom()); } if (!line_y_max.hasFocus()) { if(isFit) line_y_max.setValue(grect.top()); else line_y_max.setValue(selrect.top()); } line_x_min.setDefaultText(QString::number(grect.left()).toUpper()); line_x_max.setDefaultText(QString::number(grect.right()).toUpper()); line_y_min.setDefaultText(QString::number(grect.bottom()).toUpper()); line_y_max.setDefaultText(QString::number(grect.top()).toUpper()); line_x_min.blockSignals(false); line_x_max.blockSignals(false); line_y_min.blockSignals(false); line_y_max.blockSignals(false); } is_lines_update = false; } void Graphic::checkLines() { isFit = (line_x_min.isCleared() && line_x_max.isCleared() && line_y_min.isCleared() && line_y_max.isCleared()); update(true); } void Graphic::tick(int index, bool slide, bool update_) { if (slide) { GraphicType & t(graphics[index]); if (history > 0.) while (t.polyline.size() > 1) { if (fabs(t.polyline.back().x() - t.polyline.front().x()) <= history) break; /// TODO: [Graphic] fast autofit while addPoint(double y, ...) if (!t.cvrect.isNull()) { QPointF fp(t.polyline.first()); if (qFuzzyCompare(t.cvrect.left(), fp.x()) || qFuzzyCompare(t.cvrect.right(), fp.x()) || qFuzzyCompare(t.cvrect.top(), fp.y()) || qFuzzyCompare(t.cvrect.bottom(), fp.y())) { t.cvrect = QRectF(); } } t.polyline.pop_front(); } } if (!update_) { if (isFit) findGraphicsRect(); return; } if (isFit) findGraphicsRect(); if (!slide) { if (aupdate) update(); return; } if (aupdate) update(); } void Graphic::on_buttonAutofit_clicked() { isFit = true; bool isEmpty = true; foreach (const GraphicType & t, graphics) { const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline); if (!pol.isEmpty()) { isEmpty = false; break; } } if (isEmpty) grect = def_rect; selrect = grect; findGraphicsRect(); update(); } void Graphic::on_buttonConfigure_clicked() { conf->graphicItems.clear(); for (int i = 0; i < graphics.size(); i++) { GraphicConf::GraphicItem item; item.icon = graphics[i].icon; item.name = graphics[i].name; conf->graphicItems.append(item); } conf->ui->colorGrid->setColor(grid_pen.color()); conf->ui->comboStyleGrid->setCurrentIndex((int)grid_pen.style()); conf->ui->spinWidthGrid->setValue(grid_pen.widthF()); conf->ui->checkOGL->setChecked(isOGL); conf->ui->checkAAlias->setChecked(aalias); conf->ui->checkInputs->setChecked(borderInputsVisible()); conf->ui->checkStatus->setChecked(statusVisible()); conf->ui->checkLegend->setChecked(legendVisible()); conf->ui->checkGridAutoX->setChecked(grad_x == Auto); conf->ui->checkGridAutoY->setChecked(grad_y == Auto); conf->ui->colorBackground->setColor(back_color); conf->ui->colorText->setColor(text_color); conf->ui->spinGridStepX->setValue(gridx); conf->ui->spinGridStepY->setValue(gridy); conf->ui->spinMarginL->setValue(margins_.left()); conf->ui->spinMarginT->setValue(margins_.height()); conf->ui->spinMarginR->setValue(margins_.width()); conf->ui->spinMarginB->setValue(margins_.top()); conf->readParams(); if (conf->exec() == QDialog::Rejected) return; grid_pen = QPen(conf->ui->colorGrid->color(), conf->ui->spinWidthGrid->value(), (Qt::PenStyle)conf->ui->comboStyleGrid->currentIndex()); back_color = conf->ui->colorBackground->color(); text_color = conf->ui->colorText->color(); grad_x = conf->ui->checkGridAutoX->isChecked() ? Auto : Fixed; grad_y = conf->ui->checkGridAutoY->isChecked() ? Auto : Fixed; gridx = conf->ui->spinGridStepX->value(); gridy = conf->ui->spinGridStepY->value(); setOpenGL(conf->ui->checkOGL->isChecked()); setAntialiasing(conf->ui->checkAAlias->isChecked()); setBorderInputsVisible(conf->ui->checkInputs->isChecked()); setStatusVisible(conf->ui->checkStatus->isChecked()); setLegendVisible(conf->ui->checkLegend->isChecked()); setMargins(conf->ui->spinMarginL->value(), conf->ui->spinMarginR->value(), conf->ui->spinMarginT->value(), conf->ui->spinMarginB->value()); updateLegend(); update(); } void Graphic::on_checkGuides_toggled(bool checked) { guides = checked; setGuidesCursor(); update(); } void Graphic::updateLegend(bool es) { QPixmap pix(60, 22); for (int i = 0; i < graphics.size(); i++) { pix.fill(back_color); QPainter p(&pix); QPen pen = graphics[i].pen; if (qRound(pen.widthF()) == pen.widthF()) pen.setWidth(pen.width()*thick); else pen.setWidthF(pen.widthF()*thick); p.setPen(pen); p.drawLine(0, pix.height() / 2, pix.width(), pix.height() / 2); p.end(); graphics[i].icon = QIcon(pix); } if (!ui->widgetLegend->isVisibleTo(this)) { if (es) emit graphicSettingsChanged(); return; } leg_update = false; int ps = 100; for (int r = 0; r < ui->layoutLegend->rowCount(); ++r) for (int c = 0; c < ui->layoutLegend->columnCount(); ++c) { QLayoutItem * li = ui->layoutLegend->itemAtPosition(r, c); if (!li) continue; if (!li->widget()) continue; while (li->widget()->actions().isEmpty()) li->widget()->removeAction(li->widget()->actions()[0]); delete li->widget(); } ui->layoutLegend->invalidate(); for (int i = 0; i < graphics.size(); i++) { graphics[i].pb = new QCheckBox(graphics[i].name); graphics[i].pb->setIconSize(pix.size()); graphics[i].pb->setIcon(graphics[i].icon); graphics[i].pb->setChecked(graphics[i].visible); graphics[i].pb->setProperty("graphic_num", i); graphics[i].pb->setContextMenuPolicy(Qt::ActionsContextMenu); QAction * act = new QAction(tr("Check all"), 0); act->setCheckable(true); act->setChecked(true); graphics[i].pb->addAction(act); connect(act, SIGNAL(triggered(bool)), this, SLOT(graphicAllVisibleChange(bool))); connect(graphics[i].pb, SIGNAL(toggled(bool)), this, SLOT(graphicVisibleChange(bool))); int cps = graphics[i].pb->sizeHint().width() + 4; if (cps > ps) ps = cps; } int maxcol = qMax(ui->widgetLegend->width() / ps - 1, 1); int row = 0, col = 0; bool lv = ui->widgetLegend->isVisibleTo(this); ui->widgetLegend->hide(); for (int i = 0; i < graphics.size(); i++) { ui->layoutLegend->addWidget(graphics[i].pb,row,col); graphics[i].pb->show(); col++; if (col > maxcol) {col = 0; row++;} } ui->widgetLegend->setVisible(lv); leg_update = true; if (es) emit graphicSettingsChanged(); } void Graphic::updateLegendChecks() { for (int i = 0; i < graphics.size(); i++) { if (!graphics[i].pb) continue; bool pbs = graphics[i].pb->blockSignals(true); graphics[i].pb->setChecked(graphics[i].visible); graphics[i].pb->blockSignals(pbs); } emit graphicSettingsChanged(); } void Graphic::graphicVisibleChange(bool checked) { if (visible_update) return; QCheckBox * cb = qobject_cast(sender()); int i = cb->property("graphic_num").toInt(); graphics[i].visible = checked; if (isFit) on_buttonAutofit_clicked(); else update(); emit graphicSettingsChanged(); } void Graphic::graphicAllVisibleChange(bool checked) { visible_update = true; for (int i=0; isetChecked(checked); } visible_update = false; if (isFit) on_buttonAutofit_clicked(); else update(); emit graphicSettingsChanged(); } void Graphic::enterFullscreen() { if (fullscr) return; fullscr = true; canvas->hide(); #ifdef Q_OS_ANDROID tm_fscr.restart(); QDialog dlg; dlg.setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); dlg.layout()->setContentsMargins(0, 0, 0, 0); dlg.layout()->addWidget(canvas); QPushButton * btn = new QPushButton("Leave fullscreen"); dlg.layout()->addWidget(btn); connect(btn, SIGNAL(clicked(bool)), this, SLOT(leaveFullscreen())); canvas->show(); dlg.showFullScreen(); dlg.exec(); dlg.layout()->removeWidget(canvas); leaveFullscreen(); #else ui->layoutCanvas->removeWidget(canvas); canvas->setParent(0); canvas->showFullScreen(); canvas->setFocus(); canvas->raise(); #endif } void Graphic::leaveFullscreen() { #ifdef Q_OS_ANDROID if (tm_fscr.elapsed() < 100) return; #endif if (!fullscr) return; fullscr = false; #ifndef Q_OS_ANDROID canvas->showNormal(); canvas->hide(); #endif ui->layoutCanvas->addWidget(canvas); canvas->show(); } QString Graphic::caption() const { return ui->labelCaption->text(); } bool Graphic::borderInputsVisible() const { return ui->widgetLX->isVisible(); } bool Graphic::statusVisible() const { return ui->status->isVisible(); } bool Graphic::legendVisible() const { return ui->widgetLegend->isVisible(); } QByteArray Graphic::save() { // QByteArray ba; // QDataStream s(&ba, QIODevice::ReadWrite); // s << openGL() << antialiasing() << borderInputsVisible() << statusVisible() << legendVisible(); // s << graphics; // return ba; // version '2': ChunkStream cs; cs.add(1, antialiasing()).add(2, openGL()).add(3, borderInputsVisible()).add(4, statusVisible()).add(5, legendVisible()); cs.add(6, backgroundColor()).add(7, textColor()).add(8, margins()); cs.add(9, gridPen()).add(10, graduationX()).add(11, graduationY()).add(12, graduationStepX()).add(13, graduationStepY()); cs.add(14, graphics); cs.add(15, isFit).add(16, visualRect()); if (backgroundColor() == palette().color(QPalette::Base) && textColor() == palette().color(QPalette::WindowText) && gridColor() == palette().color(QPalette::Disabled, QPalette::WindowText)) cs.add(17, true); return cs.data().prepend('2'); } void Graphic::load(QByteArray ba) { if (ba.isEmpty()) return; char ver = ba[0]; switch(ver) { case '2': {// version '2': ba.remove(0, 1); QRectF vrect; ChunkStream cs(ba); bool def_colors = false; while (!cs.atEnd()) { switch (cs.read()) { case 1: setAntialiasing(cs.getData()); break; case 2: setOpenGL(cs.getData()); break; case 3: setBorderInputsVisible(cs.getData()); break; case 4: setStatusVisible(cs.getData()); break; case 5: setLegendVisible(cs.getData()); break; case 6: if (!def_colors) setBackgroundColor(cs.getData()); break; case 7: if (!def_colors) setTextColor(cs.getData()); break; case 8: setMargins(cs.getData()); break; case 9: if (!def_colors) setGridPen(cs.getData()); break; case 10: setGraduationX(cs.getData()); break; case 11: setGraduationY(cs.getData()); break; case 12: setGraduationStepX(cs.getData()); break; case 13: setGraduationStepY(cs.getData()); break; case 14: graphics = cs.getData >(); break; case 15: isFit = cs.getData(); break; case 16: vrect = cs.getData(); break; case 17: if(cs.getData()) { setTextColor(palette().color(QPalette::WindowText)); setGridPen(QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 0., Qt::DotLine)); setBackgroundColor(palette().color(QPalette::Base)); def_colors = true; } break; default: break; } } if (!isFit) setVisualRect(vrect); } break; default: {// old version 0: QDataStream s(ba); bool a; s >> a; setOpenGL(a); s >> a; setAntialiasing(a); s >> a; setBorderInputsVisible(a); s >> a; setStatusVisible(a); s >> a; s >> graphics; setLegendVisible(a); } break; } } void Graphic::setCaption(const QString & str) { ui->labelCaption->setText(str); ui->labelCaption->setVisible(str.length() > 0); if (aupdate) update(); } void Graphic::setGridEnabled(bool enabled) { ui->checkGrid->setChecked(enabled); } void Graphic::setBorderInputsVisible(bool visible) { ui->widgetLX->setVisible(visible); ui->widgetLY->setVisible(visible); ui->checkBorderInputs->setChecked(visible); if (visible) setRectToLines(); } void Graphic::setStatusVisible(bool visible) { ui->status->setVisible(visible); } void Graphic::setLegendVisible(bool visible) { ui->widgetLegend->setVisible(visible); ui->checkLegend->setChecked(visible); updateLegend(); } void Graphic::on_actionExpandX_triggered(bool checked) { only_expand_x = checked; ui->actionExpandX->setIcon(checked ? icon_exp_x : icon_exp_sx); } void Graphic::on_actionExpandY_triggered(bool checked) { only_expand_y = checked; ui->actionExpandY->setIcon(checked ? icon_exp_y : icon_exp_sy); } void Graphic::actionGuidesTriggered(QAction * a) { ui->checkGuides->setChecked(true); setFloatingAxisType((FloatingAxisType)a->property("_value").toInt()); }