Reputation: 20780
In older version of Qt there was QGLWidget, with a nice function called renderText. Now I'm using QOpenGLWidget class and the functionality for rendering text is missing.
Is there a easy way to render text using QOpenGLWidget? I won't like to build the whole text rendering with OpenGL from scratch...
Upvotes: 16
Views: 18422
Reputation: 1
I had this issue when I needed to draw axis and corresponding ticks. In Qt4, I can use the following code:
for (int i = _Ymin; i <= _Ymax; i = i + _yGrid)
{
double yPos = (_Ymax-i)*_yscaleF*_yscalingF;
double xPos = -_xmin*_xscaleF-_shiftX;
if(yPos < (_Ymax-_Ymin)*_yscaleF-_shiftY+1)
{
glBegin(GL_LINES);
glVertex2f(xPos-2, yPos);
glVertex2f(xPos+2, yPos);
glEnd();
renderText (xPos - offset,yPos+5 , 0, QString::number(i/10.0));
}
}
glScalef(_xscalingF,_yscalingF,0);
However, when I try to migrate my code to Qt6, there's no renderText
anymore.
After searching on the web and investigating the source code of renderText
, finally I found the solution, one needs to save/load gl status:
glBegin(GL_LINES);
glVertex2f(xPos-4, yPos);
glVertex2f(xPos+4, yPos);
glEnd();
qt_save_gl_state();
renderText(xPos - offset,yPos+5 , QString::number(i/10.0));
qt_restore_gl_state();
Here are the def of qt_save_gl_state, renderText, qt_restore_gl_state:
static void qt_save_gl_state()
{
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
glPushAttrib(GL_ALL_ATTRIB_BITS);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glShadeModel(GL_FLAT);
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
static void qt_restore_gl_state()
{
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glPopAttrib();
glPopClientAttrib();
}
void GLWidgetnew::renderText(double x, double y, const QString text)
{
GLdouble textPosX = x, textPosY = y;
// Retrieve last OpenGL color to use as a font color
GLdouble glColor[4];
glGetDoublev(GL_CURRENT_COLOR, glColor);
QColor fontColor = QColor(glColor[0]*255, glColor[1]*255,
glColor[2]*255, glColor[3]*255);
// Render text
QPainter painter(this);
painter.translate(float(_shiftX),float(_shiftY)); //This is for my own mouse event (scaling)
painter.setPen(fontColor);
QFont f;
f.setPixelSize(10);
painter.setFont(f);
painter.drawText(textPosX, textPosY, text);
painter.end();
}
src code of the project can be found here QtSignalProcessing
Upvotes: 0
Reputation: 11369
For future reference for anybody coming here with a similar issue - I had a similar issue as described in Urs's answer: When showing text on the OpenGL widget with drawText
, some letters, sometimes whole words, were missing. Yet the mentioned glPixelStorei
call did not change anything.
What I noticed when reading the QOpenGLWidget painting documentation was the QOpenGLFunction
related stuff - and indeed the affected widget in our case did not use QOpenGLFunction yet, but apparently called OpenGL functions directly - and this lead to the weird text display.
Deriving from QOpenGLFunction
and calling initializeOpenGLFunctions
in initializeGL
seems to have fixed the problems.
Edit: No luck - My problem seems to be an intermittent one that is only appearing occasionally (it's not reproducible every time I run the program). I guess I'll look into Qt OpenGL context debugging next.
Upvotes: 0
Reputation: 1
QPainter::drawText on a QOpenGLWidget relies on GL_UNPACK_ALIGNMENT being set to 4, otherwise characters will look corrupt/scrambled.
If you have this problem in your application, make sure to call
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
before drawing the text. (see https://bugreports.qt.io/browse/QTBUG-65496 for more info)
Upvotes: 0
Reputation: 775
You can implement this functionality by yourself based on the old Qt source code.
In your OpenGL widget class inherited from QOpenGLWidget (in this example it's GLBox) you have to implement the following methods:
renderText:
void GLBox::renderText(D3DVECTOR &textPosWorld, QString text)
{
int width = this->width();
int height = this->height();
GLdouble model[4][4], proj[4][4];
GLint view[4];
glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
glGetIntegerv(GL_VIEWPORT, &view[0]);
GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;
project(textPosWorld.x, textPosWorld.y, textPosWorld.z,
&model[0][0], &proj[0][0], &view[0],
&textPosX, &textPosY, &textPosZ);
textPosY = height - textPosY; // y is inverted
QPainter painter(this);
painter.setPen(Qt::yellow);
painter.setFont(QFont("Helvetica", 8));
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
painter.drawText(textPosX, textPosY, text); // z = pointT4.z + distOverOp / 4
painter.end();
}
project:
inline GLint GLBox::project(GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble model[16], const GLdouble proj[16],
const GLint viewport[4],
GLdouble * winx, GLdouble * winy, GLdouble * winz)
{
GLdouble in[4], out[4];
in[0] = objx;
in[1] = objy;
in[2] = objz;
in[3] = 1.0;
transformPoint(out, model, in);
transformPoint(in, proj, out);
if (in[3] == 0.0)
return GL_FALSE;
in[0] /= in[3];
in[1] /= in[3];
in[2] /= in[3];
*winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
*winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
*winz = (1 + in[2]) / 2;
return GL_TRUE;
}
and finally transformPoint:
inline void GLBox::transformPoint(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
{
#define M(row,col) m[col*4+row]
out[0] =
M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
out[1] =
M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
out[2] =
M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
out[3] =
M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}
If you need renderText() because you have to port your Qt4 application to Qt5 just make sure to change the signature of the function provided here to
void GLBox::renderText(double x, double y, double z, const QString & str, const QFont & font = QFont(), int listBase = 2000)
and you don't have to worry about this anymore.
Upvotes: 5
Reputation: 51
I ended up doing a solution similar to what @jaba wrote. I also noticed some graphical corruption unless I called painter.end() at the end of the method.
void MapCanvas::renderText(double x, double y, double z, const QString &str, const QFont & font = QFont()) {
// Identify x and y locations to render text within widget
int height = this->height();
GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;
project(x, y, 0f, &textPosX, &textPosY, &textPosZ);
textPosY = height - textPosY; // y is inverted
// Retrieve last OpenGL color to use as a font color
GLdouble glColor[4];
glGetDoublev(GL_CURRENT_COLOR, glColor);
QColor fontColor = QColor(glColor[0], glColor[1], glColor[2], glColor[3]);
// Render text
QPainter painter(this);
painter.setPen(fontColor);
painter.setFont(font);
painter.drawText(textPosX, textPosY, text);
painter.end();
}
Upvotes: 5
Reputation: 1068
As you seem to be wanting to draw 2D text, use QPainter::drawText(). See here for info about using QPainter on a QOpenGLWidget. For using antialiasing for text rendering on QOpenGLWidgets see here.
If you want to draw 2.5D text (2D text moving with the 3D scene) it is not "too hard" to roll your own classes. Use QFont and QFontMetricsF to build a texture for your font glyphs, build some quads for each glyph into a VBO and draw the proper quads for the glyphs in a string...
Upvotes: 3