How to enable depth test with QOpenGLWidget? It renders black screen - qt

Currently I am writing a small application with Qt and OpenGl and I choosed QOpenGLWidget for rendering graphics.
That's how I declared my widget:
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions{
// Methods, slots, e.t.c
}
And that's the constructor:
GLWidget::GLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
/* Tried this, it didn't help.
QSurfaceFormat format;
format.setDepthBufferSize(24);
this->setFormat(format);
*/
}
In my init function I set GL_CULL_FACE and GL_DEPTH_TEST:
void GLWidget::initializeGL()
{
/* Some initialization stuff regarding the scene */
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
And I really don't know why the widget renders the black screen.
Here are some pictures.
First one: with disabled GL_CULL_FACE and disabled GL_DEPTH_TEST.
Second one: with enabled GL_CULL_FACE and disabled GL_DEPTH_TEST. Maybe it's not a good picture, but I can assure you, you can see some surfaces through the other.
Third one: with enabled GL_CULL_FACE and enabled GL_DEPTH_TEST. Actually, it doesn't matter if GL_CULL_FACE is enabled. Anyway it renders the black screen.
And here's the image without any shading just to show you that the model is fine.
I tried to set the format manually, but it didn't help. Still the black screen:
QSurfaceFormat format;
format.setDepthBufferSize(24);
this->setFormat(format);
Oh and yes, I set glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); at the beginning of my paintGL() function.

Related

GL render does not show because of the QPainter

When I create a QPainter object in paintGL() function the rendered scene does not shown. Here is my code:
void D3Display::paintGL(){
makeCurrent();
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
// some adjustment stuff here...
glPushMatrix();
// some rendering stuff here...
glPopMatrix();
QPainter painter(this);
// some overpainting stuff here...
}
Now the problem is: when I create the QPainter object, screen suddenly turns into black and does not show anything (including render and overpaint stuff). As you can see, I want to overpaint something on a QGLWidget , I am trying some stuff but I couldn't work it out. This function has been written similarly with the QT Overlay example project.

Creating chroma key for qwebview in Qt5

I'm trying to create a chroma key for a qwebview in Qt5. This means I need to make a specific color be transparent (other widgets should be visible through webview's pixels with that color). I've found that it can be done using QPainter::CompositionMode operations, but can't make it work.
For example, I need to make all black pixels of a webview be transparent (the source color should be changed in runtime).
I've reimplemented QWebView::paintEvent in my class (get a part of a code from Qt sources), but don't know what to do next
WebView::paintEvent(QPaintEvent *event) {
if (!page()) return;
QWebFrame *frame = page()->mainFrame();
QPainter painter(this);
painter.setRenderHints(renderHints());
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
frame->render(&painter, event->region());
}
I found a way how to make any source color be white with the following code:
QWebFrame *frame = page()->mainFrame();
QImage source_image(size(), QImage::Format_ARGB32_Premultiplied);
QImage result_image(size(), QImage::Format_ARGB32_Premultiplied);
QPainter imagePainter(&source_image);
imagePainter.setRenderHints(renderHints());
frame->render(&imagePainter, event->region());
imagePainter.end();
QImage mask = source_image.createMaskFromColor(qRgb(0x00,0x00,0x00)); // Source color
QPainter resultPainter(&result_image);
resultPainter.drawImage(source_image.rect(), source_image);
resultPainter.setCompositionMode(QPainter::CompositionMode_Screen);
resultPainter.drawImage(source_image.rect(), mask);
QPainter painter(this);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawImage(0, 0, result_image);
But I don't know how to convert a white color to transparent.
I found a solution, but it consumes a lot of CPU.
First it's required to set
setStyleSheet("QWebView { background: transparent }");
setAttribute(Qt::WA_OpaquePaintEvent, true);
somewhere in WebView's constructor (I just forgot to mention that in the first message). Then reimplement paintEvent:
void WebView::paintEvent(QPaintEvent *event)
{
if (!page())
return;
QWebFrame *frame = page()->mainFrame();
QPainter painter(this);
QColor chroma_color(0, 0, 0); // A color that should be transparent
float opacity_level = 0.9; // WebView opacity
m_render_pixmap.fill(Qt::transparent);
QPainter pixmapPainter(&m_render_pixmap);
pixmapPainter.setRenderHints(renderHints());
frame->render(&pixmapPainter, event->region());
pixmapPainter.end();
m_render_pixmap.setMask(m_render_pixmap.createMaskFromColor(
chroma_color, Qt::MaskInColor));
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.setOpacity(opacity_level);
painter.drawPixmap(QPoint(event->rect().left(), event->rect().top()), m_render_pixmap, event->rect());
painter.end();
}
m_render_pixmap is an instance of QPixmap. I don't want to recreate it every time paintEvent is called. I just recreate it on resizeEvent
void WebView::resizeEvent(QResizeEvent *event)
{
QWebView::resizeEvent(event);
m_render_pixmap = QPixmap(size());
}
The code above work great but in my case I want to render a video widget below a webview. So WebView::paintEvent calls about 25 times per second and each call takes about 20-25 ms in windowed mode on my PC. And it takes about 100% of one of CPU cores in a fullscreen mode.

QT change QImage alpha

I need to change the alpha of a QImage I have so it blends with the other QImages behind and infront of it. This needs to be toggled quickly on and off.
Previously I've had to recreate every single image and give them new colors with just a different alpha value. But now I want to retain that same original image instead of redrawing and painting it.
I'm trying to do it like this now:
QImage image;
unsigned int rgb;
for(int y=0;y<image.height();y++){
for(int x=0;x<image.width();x++){
rgb=image.pixel(x,y);
image.setPixel(x,y,qRgba(qRed(rgb),qGreen(rgb),qRed(rgb),120));
}
}
I'm getting some fairly unpredictable behavior. When I toggle the image sometimes I lose colors or the alpha isn't changed. And if the alpha did get changed when I toggle back (I hardcode the alpha 255 elsewhere instead of 120) it doesn't go back to normal.
This doesn't seem like the right way to do this anyways, it shouldn't be this difficult. It seems like there should be a single function call on an image to change the alpha but I haven't found one yet.
If you are using the QImage in QGraphicsView or in some other QWidget, you should look into this QGraphicsEffect:
http://qt-project.org/doc/qt-4.8/qgraphicsopacityeffect.html
http://doc-snapshot.qt-project.org/4.8/qwidget.html#setGraphicsEffect
http://doc-snapshot.qt-project.org/4.8/qgraphicsitem.html#setGraphicsEffect
If you are using a QLabel, I would try this out:
#include <QLabel>
#include <QPainter>
class TransparentQLabel : public QLabel
{
Q_OBJECT
public:
explicit TransparentQLabel() : QLabel() {}
~TransparentQLabel(){}
void setOpacity(const qreal & val)
{
if (this->pixmap() == null || this->pixmap().isNull())
return;
QPixmap result(this->pixmap()->size());
result.fill(Qt::transparent);
QPainter painter;
painter.begin(&result);
painter.setOpacity(val);
painter.drawPixmap(0, 0, *(this->pixmap()));
painter.end();
QLabel::setPixmap(result);
}
};
This next bit is only slightly related to your question, but it is nice to know. If you are layering outside of your QApplication onto the operating system, you need some things like this:
this->setWindowFlags( Qt::WindowStaysOnTopHint |
Qt::FramelessWindowHint | Qt::Tool);
this->setAttribute(Qt::WA_TranslucentBackground, true);
this->setAttribute (Qt::WA_TransparentForMouseEvents, true);
Here is an example of this stuff in action:
http://qt-project.org/wiki/QSplashScreen-Replacement-for-Semitransparent-Images
Hope that helps.

Qt menu without shadow?

I copied the question description below from other asked but not answered question, because this is the exactly the same one I wanna ask.
I have a QMenu with a translucent background and rounded edges (border-radius). Unfortunately, Windows 7 draws a drop shadow for this menu, which does not fit to the rounded edges. Its the shadow that would be drawn for normal rectangular menus.
Is there either - a way to completely disable drawing drop shadows for QMenu or - a way to make the shadow fit to the rounded edges ?
Here is a minimalistic example where it occurs:
QPushButton b("press me");
QMenu m;
m.addAction("hello"); m.addAction("world");
m.setWindowFlags(m.windowFlags() | Qt::FramelessWindowHint);
m.setAttribute(Qt::WA_TranslucentBackground);
m.setStyleSheet("background:rgba(255,0,0,50%); border-radius:5px;");
b.setMenu(&m);
b.show();
Right now I have to turn off the menu shadow in Windows Control panel manually to get rid of that shadow.
Actually what I want to achieve is a menu like Qt's pie menu or a menu like this one:
http://upload.wikimedia.org/wikipedia/commons/8/85/Blender_2.36_Screenshot.jpg
I tried the popup widget, but it gets the shadow artifact described above.
Could anyone help this out?
On Windows Vista and higher, I wanted a menu with normal window shadow. So I had to do two things:
Remove CS_DROPSHADOW from the menu HWND's WNDCLASS that Qt is adding deep down in the core.
Add shadow using DWM APIs.
The trick is to capture QEvent::WinIdChange to get the HWND handle to the menu window, and then to use GetClassLong / SetClassLong to remove CS_DROPSHADOW flag. I'm doing this only once (by using a static bool), as theWNDCLASS is always the same for all menus. This might lead into a problem if part of your app wants to show the menu shadows and other does not.
I have subclassed the QMenu and I'm always using my overriden class when creating menus
Menu * my_menu = new Menu(tr("&File"));
mainMenu->addMenu(my_menu);
Here's the whole code, enjoy:
menu.h
#ifndef MENU_H
#define MENU_H
#include <QMenu>
class Menu : public QMenu
{
Q_OBJECT
public:
explicit Menu(QWidget *parent = 0);
explicit Menu(const QString & title);
protected:
virtual bool event(QEvent *event);
signals:
public slots:
};
#endif // MENU_H
menu.cpp
#include "menu.h"
#pragma comment( lib, "dwmapi.lib" )
#include "dwmapi.h"
Menu::Menu(QWidget *parent) :
QMenu(parent)
{
}
Menu::Menu(const QString &title) :
QMenu(title)
{
}
bool Menu::event(QEvent *event)
{
static bool class_amended = false;
if (event->type() == QEvent::WinIdChange)
{
HWND hwnd = reinterpret_cast<HWND>(winId());
if (class_amended == false)
{
class_amended = true;
DWORD class_style = ::GetClassLong(hwnd, GCL_STYLE);
class_style &= ~CS_DROPSHADOW;
::SetClassLong(hwnd, GCL_STYLE, class_style);
}
DWMNCRENDERINGPOLICY val = DWMNCRP_ENABLED;
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &val, sizeof(DWMNCRENDERINGPOLICY));
// This will turn OFF the shadow
// MARGINS m = {0};
// This will turn ON the shadow
MARGINS m = {-1};
HRESULT hr = ::DwmExtendFrameIntoClientArea(hwnd, &m);
if( SUCCEEDED(hr) )
{
//do more things
}
}
return QWidget::event(event);
}
I just remove the Qt::popup flag to get rid of the shadow.
And I have to add close codes to any other background UI. These have been more extra work, but I got what I want :)

Copy Bits to QtImage in QGLWidget

Maybe someone can help me with the following problem:
I want to draw the content of a QImage in a QGLWidget, but the widget is painted in black.
class QGLCanvas {
public:
QGLCanvas(QWidget* parent) : QGLWidget(parent) {
}
void setImage(const QImage* image) {
img = image;
}
void paintEvent(QPaintEvent*) {
// From Painter Documentation Qt
QPainter p(this);
p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
p.drawImage(this->rect(), *img);
p.end();
}
public slots:
void rgb_data(const void *data) {
memcpy((void *)img->bits(), data, img->byteCount()); // data will be copied (sizes are known)
// img.save("text.png"); // saves right image
this->update(); // calls repaint, but does not draw the image.
}
private:
QImage *img;
}
The Bug:
When the slot is called, the memory is copied to the image. If the image is saved, the content is correct. But the repaint method just draws black content to the widget.
The Fix:
If the memcpy line is implemented outside the slot, the image content is drawn to the widget. This fix increased code complexity a lot. Thus, i've got the following question:
The Question:
Why does the memcpy not work within the slot? Is this a general problem with Qt?
There's nothing special about a slot which would stop your code from working.
What's probably the issue is that when you call update(), a repaint is scheduled but happens asynchronously. From the code you've provided the most likely cause is img being modified between the calls to rbg_data and paintEvent
You want to be sure about the format of the QImage. When you call bits and are expecting it to be RGB, you need to check the format.
if( img->format() != QImage::Format_RGB888 )
{
// convert the image format to RGB888
*img = img->convertToFormat(QImage::Format_RGB888);
}
This way, Qt will know the image format when trying to paint it. If you fill it with RGB data but the QImage is "formatted" as ARGB, you will get some painting errors.

Resources