QCustomPlot Discussion and Comments

plotLayout()->addElement() poor performanceReturn to overview

Hi,

I am adding a title to a plot and I have noticed performance is very poor just at this one point of plotLayout()->addElement()

QElapsedTimer timer;
timer.start();

/* add axes, gridlines, legend, graphs etc here, no problem, very fast */

plotLayout()->insertRow(0);
plotLayout()->addElement(0, 0, new QCPTextElement(this, "Title", QFont("sans", 12, QFont::Bold)));

QCustomPlot::replot();

qDebug() << "The slow operation took" << timer.elapsed() << "milliseconds";

Without plotLayout()->addElement(), the whole function takes <10ms. Adding this line makes the function take in the order of 500ms (even in release mode).

Is there anything I can do to speed this up?

Thanks.

Using Qt 6.2.4 and QCustomPlot 2.1.0.

Actually, seems to be just the first replot() after the addElement() that takes ages.

It seems to be the same old problem as has previously been discussed, that having the font makes the text lines thicker than 1px, which makes them polygons, which Qt struggles to draw. If I remove the font, it's fast again.

Turned off antialiasing on the element, made no difference.

Enabled OpenGL in QCP (which was a huge pain, messing with include paths in qcustomplot.h, and in the end having to remove the glClearColor and glClear lines from void QCPPaintBufferGlPbuffer::clear(const QColor &color) to quickly bypass unresolved externals just so I could see if it was worth bothering). And performance is far worse with OpenGL than without, so I've undone all those changes. I guess my problems with getting QCP to use OpenGL are a result of my combination of Qt6 and QCP2. After all, I did have to use this patch to get QCP2.1.0 to compile at all on Qt 6.2.4: https://www.qcustomplot.com/index.php/support/forum/2380 .

It just seems weird to me that replot can take over a second to run the very first time that a QCPTextElement with a font is added to the very first plot, but after that, adding and removing it from this plot or from any other plots in my application takes no time at all, and performance elsewhere in the app or elsewhere in the plot such as range dragging isn't affected by that element being there.

My workaround so far is to add it to a separate function which runs after the plot is constructed and plotted which is less intrusive to the user as at least they can see a plot without a title for the first second. And I guess I won't be having any fonts or lines within my plots thicker than 1px since my plots need to be interactive, which kinda sucks. Maybe I can take lines down to 1px and remove fonts during interactions and then make them pretty again afterwards. Haven't tried that yet. I might also make the title a QLabel outside of the plot.

Just to understand correctly: It is always only the very first rendering of the font (call to replot) in the application that takes so long? This sounds like either your operating system or Qt needs to prepare some caches, load the font file or so, unrelated to QCP. Unfortunately I've not observed this behavior yet.

Did you observe the same on Qt5?

Yes, it's only the first replot after adding the text element with a font that is slow.

Thanks, I hadn't considered it was because the font wasn't cached but it looks like that probably is the issue because,
- If I use plot->font() (which would be the base QWidget's default font) in the new QCPTextElement, it's fast.
- If I make a QFont instance, set it in the QCustomPlot constructor and apply it to the new QCPTextElement, the plot constructor is slow and the new QCPTextElement is fast.
- If I apply the same font to the main widget as well, so that it has been used elsewhere before the plot is constructed, both the plot and the new QCPTextElement are fast.

So now the question is can I / how do I pre-cache fonts in a QtWidgets application? I had made it a global static in the hope that it would be cached on startup, but it still seems to be lazily cached only when the first use of it is shown. Googling for font caching in Qt doesn't come up with much (except 1 mention of Glyph Caching in the context of QtQuick for embedded microcontrollers)

The default font of QWidget on Windows 10 is "Segoe UI". If I use that fontFamily, it's all fast, even for new QFont instances of different pointSize and fontWeight, but of course that's a Windows only font. And yet even though QCP already uses "sans serif" font elsewhere for axis labels etc., if I try to reuse that for the new QCPTextElement it's still slow.

It's Qt's font matching algorithm. If I put in the exact name of a font in QFontDatabase::families(), (i.e. a named font installed on my system) it will be fast. If I put in anything else such as "sans", it has to go through the font matching algorithm which is very slow.

Here's my workaround:

QFont fastFont(const QString& family, const QWidget* w)
{
	// bypass very slow Qt font matching algorithm by first 
	// checking for an exact match before constructing font.
	if (QFontDatabase::families().contains(family))
		return QFont(family);

	// if not fount, return a font of the family used in an 
	// existing widget, which will also be an exact match.
	return QFont(QFontInfo(w->font()).family());
}