diff --git a/doc/dox/jkqtplotter.dox b/doc/dox/jkqtplotter.dox
index 84e9780849..d5732e4fda 100644
--- a/doc/dox/jkqtplotter.dox
+++ b/doc/dox/jkqtplotter.dox
@@ -283,6 +283,9 @@ This group assembles graphs that show their data with symbols and optionally wit
\image html symbols_and_styles_small.png
| JKQTPXYLineGraph, JKQTPXYLineErrorGraph
diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox
index 2ddf28b93d..b42b5535ed 100644
--- a/doc/dox/whatsnew.dox
+++ b/doc/dox/whatsnew.dox
@@ -37,6 +37,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
NEW: improved plotting speed for scatter-graphs by not calling draw functions for symbols outside the plot window (e.g. in JKQTPXYLineGraph)
NEW: added JKQTBasePlotter::grabPixelImage() and JKQTPlotter::grabPixelImage(), which grab the plotter into a QImage
NEW: added option to not display the preview dialog to JKQTBasePlotter::copyPixelImage() and JKQTPlotter::copyPixelImage()
+ NEW: added simple scatter plot JKQTPXYScatterGraph
JKQTMathText:
diff --git a/lib/jkqtplotter/graphs/jkqtpscatter.cpp b/lib/jkqtplotter/graphs/jkqtpscatter.cpp
index 992f079d03..2698097d01 100644
--- a/lib/jkqtplotter/graphs/jkqtpscatter.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpscatter.cpp
@@ -48,6 +48,211 @@
+JKQTPXYScatterGraph::JKQTPXYScatterGraph(JKQTPlotter* parent):
+ JKQTPXYScatterGraph(parent->getPlotter())
+{
+}
+
+JKQTPXYScatterGraph::JKQTPXYScatterGraph(JKQTBasePlotter* parent):
+ JKQTPXYGraph(parent)
+{
+ sortData=JKQTPXYGraph::Unsorted;
+
+ initSymbolStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
+}
+
+void JKQTPXYScatterGraph::draw(JKQTPEnhancedPainter& painter) {
+#ifdef JKQTBP_AUTOTIMER
+ JKQTPAutoOutputTimer jkaaot("JKQTPXYScatterGraph::draw");
+#endif
+ if (parent==nullptr) return;
+ const JKQTPDatastore* datastore=parent->getDatastore();
+ if (datastore==nullptr) return;
+
+ //qDebug()<<"JKQTPXYScatterGraph::draw();";
+
+ drawErrorsBefore(painter);
+ {
+ //qDebug()<<"JKQTPXYScatterGraph::draw(): "<<1;
+ painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
+ //qDebug()<<"JKQTPXYScatterGraph::draw(): "<<2;
+
+ const auto symType=getSymbolType();
+ const double xmin=transformX(parent->getXAxis()->getMin());
+ const double xmax=transformX(parent->getXAxis()->getMax());
+ const double ymin=transformY(parent->getYAxis()->getMin());
+ const double ymax=transformY(parent->getYAxis()->getMax());
+ const double symbolSize=parent->pt2px(painter, getSymbolSize());
+ const QMarginsF clipMargins=(symType==JKQTPNoSymbol)?QMarginsF(0,0,0,0):QMarginsF(symbolSize,symbolSize,symbolSize,symbolSize);
+ const QRectF cliprect=QRectF(qMin(xmin,xmax),qMin(ymin,ymax),fabs(xmax-xmin),fabs(ymax-ymin))+clipMargins;
+
+
+ int imax=0;
+ int imin=0;
+ if (getIndexRange(imin, imax)) {
+ for (int iii=imin; iiiget(static_cast(xColumn),static_cast(i));
+ const double yv=datastore->get(static_cast(yColumn),static_cast(i));
+ const double x=transformX(xv);
+ const double y=transformY(yv);
+ //qDebug()<<"JKQTPXYScatterGraph::draw(): (xv, yv) = ( "<pt2px(painter, symbolSize*1.5), parent->pt2px(painter, symbolWidth*parent->getLineWidthMultiplier()), penSelection.color(), penSelection.color());
+ //}
+ if ((!parent->getXAxis()->isLogAxis() || xv>0.0) && (!parent->getYAxis()->isLogAxis() || yv>0.0) ) {
+ if (symType!=JKQTPNoSymbol && cliprect.contains(x,y)) plotStyledSymbol(parent, painter, x, y);
+ }
+ }
+ }
+
+ }
+ }
+ //qDebug()<<"JKQTPXYScatterGraph::draw(): "<<7;
+ drawErrorsAfter(painter);
+ //qDebug()<<"JKQTPXYScatterGraph::draw() ... done";
+}
+
+void JKQTPXYScatterGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) {
+ const double minSize=qMin(rect.width(), rect.height());
+ const double maxSize=qMax(rect.width(), rect.height());
+ double symbolSize=parent->pt2px(painter, this->getSymbolSize());
+ if (symbolSize>minSize*0.9) symbolSize=minSize*0.9;
+ double symbolWidth=parent->pt2px(painter, this->getSymbolLineWidth()*parent->getLineWidthMultiplier());
+ if (symbolWidth>0.3*symbolSize) symbolWidth=0.3*symbolSize;
+
+ painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
+ double y=rect.top()+rect.height()/2.0;
+ JKQTPPlotSymbol(painter, rect.left()+rect.width()/2.0, rect.top()+rect.height()/2.0, getSymbolType(), symbolSize, symbolWidth, getKeyLabelColor(), getSymbolFillColor());
+
+}
+
+QColor JKQTPXYScatterGraph::getKeyLabelColor() const {
+ return getSymbolColor();
+}
+
+void JKQTPXYScatterGraph::setColor(QColor c)
+{
+ setSymbolColor(c);
+ setSymbolFillColor(JKQTPGetDerivedColor(parent->getCurrentPlotterStyle().graphsStyle.defaultGraphStyle.fillColorDerivationMode, c));
+ c.setAlphaF(0.5);
+}
+
+
+
+JKQTPXYScatterErrorGraph::JKQTPXYScatterErrorGraph(JKQTBasePlotter *parent):
+ JKQTPXYScatterGraph(parent)
+{
+ setErrorColorFromGraphColor(getSymbolColor());
+ initErrorStyle(parent, parentPlotStyle);
+}
+
+JKQTPXYScatterErrorGraph::JKQTPXYScatterErrorGraph(JKQTPlotter *parent):
+ JKQTPXYScatterErrorGraph(parent->getPlotter())
+{
+
+}
+
+bool JKQTPXYScatterErrorGraph::getXMinMax(double &minx, double &maxx, double &smallestGreaterZero) {
+ if (xErrorColumn<0 || xErrorStyle==JKQTPNoError) {
+ return JKQTPXYScatterGraph::getXMinMax(minx, maxx, smallestGreaterZero);
+ } else {
+ bool start=true;
+ minx=0;
+ maxx=0;
+ smallestGreaterZero=0;
+
+ if (parent==nullptr) return false;
+
+ const JKQTPDatastore* datastore=parent->getDatastore();
+ int imax=0;
+ int imin=0;
+ if (getIndexRange(imin, imax)) {
+ for (int i=imin; iget(static_cast(xColumn),static_cast(i))+getXErrorU(i, datastore);
+ if (JKQTPIsOKFloat(xv)) {
+ if (start || xv>maxx) maxx=xv;
+ if (start || xvget(static_cast(xColumn),static_cast(i))-getXErrorL(i, datastore);
+ if (JKQTPIsOKFloat(xv)) {
+ if (start || xv>maxx) maxx=xv;
+ if (start || xvgetDatastore();
+ int imax=0;
+ int imin=0;
+ if (getIndexRange(imin, imax)) {
+ for (int i=imin; iget(static_cast(yColumn),static_cast(i))+getYErrorU(i, datastore);
+ if (JKQTPIsOKFloat(yv)) {
+ if (start || yv>maxy) maxy=yv;
+ if (start || yvget(static_cast(yColumn),static_cast(i))-getYErrorL(i, datastore);
+ if (JKQTPIsOKFloat(yv)) {
+ if (start || yv>maxy) maxy=yv;
+ if (start || yv |