微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在Qt内部通过Windows GDI绘图

我试图在QGraphicsView paintEvent中使用 Windows GDI但是已经注意到一些绘图问题,例如当我调整窗口大小或最小化和最大化时,绘图消失或闪烁.当我使用Qt代替GDI时,它的工作完美.这是代码

[更新代码]

#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,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,ViewportRect.right() - 100,ViewportRect.bottom() - 100);
    //  QPainterPath EllipsePath;
    //  EllipsePath.addEllipse(50,ViewportRect.bottom() - 100);
    //  painter.fillPath(EllipsePath,WhiteBrush);
    //  painter.drawText(ViewportRect.width() / 2,ViewportRect.height() / 2,"Test Qt Paint");
    //  painter.restore();
}

这是整个项目(Visual Studio 2010 Qt 5.4.1)[UPDATED ARCHIVE]
https://dl.dropboxusercontent.com/u/105132532/Stackoverflow/Qt5TestApplication.7z

有任何想法吗?

解决方案(在Kuba Ober回答后编辑代码)

#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,hdc);
}

解决方法

打算通过GDI绘制的QWidget必须:

>重新实现paintEngine返回nullptr.
>设置WA_PaintOnScreen属性.
>(可选)设置WA_NativeWindow属性.这只会加快小部件的第一次重绘速度.
>重新实现QObject :: event并捕获Paint和UpdateRequest事件.这些事件应该导致调用一个执行GDI绘制的方法.不得将事件转发到基类.

此外,在通过paintEvent / QPainter绘制的内容之上通过GDI绘制的QWidget还必须:

>在构造函数调用基类的paintEngine()方法一次.这将实例化窗口小部件的本机绘制引擎.
>在QObject :: event实现中,必须在执行GDI绘制之前调用基类的事件.这将使用光栅绘制引擎绘制内容,并将控制权返回给您以覆盖其上的一些其他内容.

下面的示例显示了如何在Qt的绘图系统完成的绘图之上进行重绘.当然,由于这幅画是在Qt已经绘制的顶级内容上完成的,因此有闪烁.

在Qt的后备存储上进行GDI绘制,避免闪烁也是可能的,但需要采用稍微不同的方法.

#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,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,0));

   RECT rect;
   GetClientRect(hwnd,&rect);

   SetBkMode(hdc,TRANSPARENT);
   SelectObject(hdc,hbrGreen);
   Rectangle(hdc,rect.right,rect.bottom);

   SelectObject(hdc,GetStockObject(NULL_Brush));
   Ellipse(hdc,rect.bottom - 100);

   QString text("Test GDI Paint");
   SetTextAlign(hdc,TA_CENTER | TA_BASELINE);
   textoutW(hdc,text.size());

   DeleteObject(hbrGreen);
   ReleaseDC(hwnd,hdc);
}

class EmptyGraphicsObject : public QGraphicsObject
{
public:
    EmptyGraphicsObject() {}
    QRectF boundingRect() const { return QRectF(0,0); }
    void paint(QPainter *,const qstyleOptionGraphicsItem *,QWidget *) {}
};

void setupScene(QGraphicsScene &s)
{
    QGraphicsObject * obj = new EmptyGraphicsObject;
    QGraphicsRectItem * rect = new QGraphicsRectItem(-1,0.3,2,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();
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐