Drawing Windows GDI inside Qt

I am trying to use Windows GDI inside a QGraphicsView paintEvent, but noticed some problems with painting, for example, the painting disappears or blinks when I resize the window or minimize and maximize it. When I use Qt instead of GDI, it works fine. Here is the code:

[UPDATED CODE]

#include "CustomView.h" #include <QPainter> #include <QPaintEngine> #include <windows.h> #include <objidl.h> #include <gdiplus.h> using namespace Gdiplus; #pragma comment (lib,"Gdiplus.lib") CustomView::CustomView(QWidget *parent) : QGraphicsView(parent) { setAutoFillBackground(true); setViewportUpdateMode(QGraphicsView::FullViewportUpdate); setAttribute(Qt::WA_NativeWindow, true); } CustomView::~CustomView() { } void CustomView::paintEvent(QPaintEvent * event) { QPainter painter(viewport()); painter.beginNativePainting(); // Drawing by Windows GDI HWND hwnd = (HWND)viewport()->winId(); HDC hdc = GetDC(hwnd); QString text("Test GDI Paint"); RECT rect; GetClientRect(hwnd, &rect); HBRUSH hbrRed = CreateSolidBrush(RGB(0, 255, 0)); FillRect(hdc, &rect, hbrRed); Ellipse(hdc, 50, 50, rect.right - 100, rect.bottom - 100); SetTextAlign(hdc, TA_CENTER | TA_BASELINE); TextOutW(hdc, width() / 2, height() / 2, (LPCWSTR)text.utf16(), text.size()); ReleaseDC(hwnd, hdc); painter.endNativePainting(); QGraphicsView::paintEvent(event); // Drawing the same by Qt // QPainter painter(viewport()); // painter.save() ; // QBrush GreenBrush(Qt::green); // QBrush WhiteBrush(Qt::white); // QRect ViewportRect = viewport()->rect(); // painter.fillRect(ViewportRect, GreenBrush); // painter.drawEllipse(50, 50, ViewportRect.right() - 100, ViewportRect.bottom() - 100); // QPainterPath EllipsePath; // EllipsePath.addEllipse(50, 50, ViewportRect.right() - 100, ViewportRect.bottom() - 100); // painter.fillPath(EllipsePath, WhiteBrush); // painter.drawText(ViewportRect.width() / 2, ViewportRect.height() / 2, "Test Qt Paint"); // painter.restore(); } 

and here is the whole project (Visual Studio 2010 + Qt 5.4.1) [UPDATED ARCHIVE] https://dl.dropboxusercontent.com/u/105132532/Stackoverflow/Qt5TestApplication.7z

Any ideas?

Solution (edited code after Cuba Aubert's answer)

 #include "CustomView.h" #include <QPainter> #include <QPaintEngine> #include <windows.h> #include <objidl.h> #include <gdiplus.h> using namespace Gdiplus; #pragma comment (lib,"Gdiplus.lib") CustomView::CustomView(QWidget *parent) : QGraphicsView(parent) { // This brings the original paint engine alive. QGraphicsView::paintEngine(); setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_PaintOnScreen); setRenderHint(QPainter::Antialiasing); } CustomView::~CustomView() { } QPaintEngine* CustomView::paintEngine() const { return NULL; } bool CustomView::event(QEvent * event) { if (event->type() == QEvent::Paint) { bool result = QGraphicsView::event(event); drawGDI(); return result; } else if (event->type() == QEvent::UpdateRequest) { bool result = QGraphicsView::event(event); drawGDI(); return result; } return QGraphicsView::event(event); } void CustomView::drawGDI() { // Drawing by Windows GDI HWND hwnd = (HWND)viewport()->winId(); HDC hdc = GetDC(hwnd); QString text("Test GDI Paint"); RECT rect; GetClientRect(hwnd, &rect); HBRUSH hbrRed = CreateSolidBrush(RGB(0, 255, 0)); FillRect(hdc, &rect, hbrRed); Ellipse(hdc, 50, 50, rect.right - 100, rect.bottom - 100); SetTextAlign(hdc, TA_CENTER | TA_BASELINE); TextOutW(hdc, width() / 2, height() / 2, (LPCWSTR)text.utf16(), text.size()); ReleaseDC(hwnd, hdc); } 
+5
source share
1 answer

A QWidget that intends to paint through GDI should only:

  • Repeat paintEngine to return nullptr .

  • Set the WA_PaintOnScreen attribute.

  • Set the WA_NativeWindow attribute WA_NativeWindow . This speeds up the first redraw of the widget.

  • Repeat QObject::event and intercept the Paint and UpdateRequest . These events should lead to a call to a method that does GDI painting. Events should not be redirected to the base class.

In addition, a QWidget that draws through GDI on top of the content painted through paintEvent / QPainter must additionally:

  • Calling the base class method paintEngine() once in the constructor. This will create an instance of the inline drawing engine for the widget.

  • In the QObject::event implementation, the base QObject::event class must be called before executing the GDI drawing. This will cause the content to be drawn using the bitmap drawing mechanism and return to you to overload other content on top of it.

The example below shows how to recolor over a picture made by the Qt paint system. Of course, since the picture runs on top of the content already drawn by Qt, it flickers there.

screenshot of the example

Running a GDI drawing on top of the Qt support store, avoiding flickering, is also possible, but requires a slightly different approach.

 #include <QApplication> #include <QGraphicsObject> #include <QPropertyAnimation> #include <QGraphicsView> #include <QPainter> #include <QPaintEvent> #include <windows.h> class View : public QGraphicsView { public: View(QWidget *parent = 0) : QGraphicsView(parent) { // This brings the original paint engine alive. QGraphicsView::paintEngine(); //setViewportUpdateMode(QGraphicsView::FullViewportUpdate); setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_PaintOnScreen); setRenderHint(QPainter::Antialiasing); } QPaintEngine * paintEngine() const Q_DECL_OVERRIDE { return 0; } bool event(QEvent * event) Q_DECL_OVERRIDE { if (event->type() == QEvent::Paint) { bool result = QGraphicsView::event(event); paintOverlay(); return result; } if (event->type() == QEvent::UpdateRequest) { bool result = QGraphicsView::event(event); paintOverlay(); return result; } return QGraphicsView::event(event); } void resizeEvent(QResizeEvent *) { fitInView(-2, -2, 4, 4, Qt::KeepAspectRatio); } virtual void paintOverlay(); }; void View::paintOverlay() { // We're called after the native painter has done its thing HWND hwnd = (HWND)viewport()->winId(); HDC hdc = GetDC(hwnd); HBRUSH hbrGreen = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 255, 0)); RECT rect; GetClientRect(hwnd, &rect); SetBkMode(hdc, TRANSPARENT); SelectObject(hdc, hbrGreen); Rectangle(hdc, 0, 0, rect.right, rect.bottom); SelectObject(hdc, GetStockObject(NULL_BRUSH)); Ellipse(hdc, 50, 50, rect.right - 100, rect.bottom - 100); QString text("Test GDI Paint"); SetTextAlign(hdc, TA_CENTER | TA_BASELINE); TextOutW(hdc, width() / 2, height() / 2, (LPCWSTR)text.utf16(), text.size()); DeleteObject(hbrGreen); ReleaseDC(hwnd, hdc); } class EmptyGraphicsObject : public QGraphicsObject { public: EmptyGraphicsObject() {} QRectF boundingRect() const { return QRectF(0, 0, 0, 0); } void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} }; void setupScene(QGraphicsScene &s) { QGraphicsObject * obj = new EmptyGraphicsObject; QGraphicsRectItem * rect = new QGraphicsRectItem(-1, 0.3, 2, 0.3, obj); QPropertyAnimation * anim = new QPropertyAnimation(obj, "rotation", &s); s.addItem(obj); rect->setPen(QPen(Qt::darkBlue, 0.1)); anim->setDuration(2000); anim->setStartValue(0); anim->setEndValue(360); anim->setEasingCurve(QEasingCurve::InBounce); anim->setLoopCount(-1); anim->start(); } int main(int argc, char *argv[]) { QApplication a(argc, argv); QGraphicsScene s; setupScene(s); View view; view.setScene(&s); view.show(); return a.exec(); } 
+5
source

Source: https://habr.com/ru/post/1214306/


All Articles