Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Get DC from QPaintEngine



  • Hi,

    I am working on a code that was written in Qt4. I am working in Qt5. The program needs to display a chart, and it uses a QPainter object to do so.

    This is the problematic line of the original code:

    HDC PaintDC = p.paintEngine()->getDC();
    

    The getDC() has changed in Qt5 and now needs a HWND object.
    So I tried this:

    HWND hwnd = (HWND)p.paintEngine();
    HDC PaintDC = GetDC(hwnd);
    

    But the chart doesn't display. What can I do to fix that?

    Thanks in advance.



  • Hi, the capability that came with Qt4's getDC(), i.e. to be able to draw using vintage Win32 ::MoveTo(). ::LineTo() etc. in Qt's paintEvent() seems to be gone in Qt5. If you browse Qt5's source you'll still see mention of a getDC(), but it's only present in the private headers :-(

    Your code will run fine, but not in the context of a paintEvent(), i.e. inside it you will be fighting with Qt and flicker will occur for sure.

    But I got an idea that might help, you can use a QTimer to reschedule your drawing to immediately after the paintEvent, that way paintEvent() will be done and you're free to draw, say like this:

    #include "qtimer.h"
    void CalcWnd::paintEvent(QPaintEvent *e)
    {
        QRect r = e->rect();
        QTimer::singleShot(0,[this,r] { DoRedrawPixmap(r); });
    }
    


  • Hi, try:

    auto w = (QWidget*) p.device();
    if (!w)
        return;  // skip this if there was no QWidget
    
    HWND hWnd = reinterpret_cast<HWND>(w->winId());
    HDC paintDC = GetDC(hWnd);
    


  • @lucbaz Hi, HWND is the Windows platform handle type of a window / widget, so you cannot cast the result of paintEngine() to HWND, because it is not.



  • I replaced the code with this:

    HWND hwnd = reinterpret_cast<HWND>(this->winId());
    HDC PaintDC = GetDC(hwnd);
    

    And I can only see the chart for a fraction of a second when I resize or move the window. So this part of the code is probably not causing the error.
    What could be the cause for the disappearance of the chart?

    If it helps, the code mentioned above is part of a private function that is called by paintEvent of the window in question.



  • Hmm sounds like different DC's fighting for the same hWnd, are you using Qt's paintEvent or?



  • Yes, I am using

    void Window::paintEvent(QPaintEvent *e){...}
    

    Actually, there is 4 windows that uses QPainter to display their interfaces. Each of them uses their own paintEvent. Is it possible that they interfere with each other?



  • This is the code of the paintEvent for one of the 4 windows:

    void CalcWnd::paintEvent(QPaintEvent *e)
    {
       DoRedrawPixmap(e->rect());
    }
    
    void CalcWnd::DoRedrawPixmap(const QRect &rect)
    {
       int i;
       LOGFONT LogFont;
       HFONT Font, OldFont;
       HPEN Pen, OldPen;
       char buf[100];
       int XI = 50, YI = 50;
       Calc *C;
    
       // Calculate the size of the widget for holding all items
       // Each item is 17 pixel long
       int ContentY = 51+CalcData.size()*17;
       int WndY = Wnd->calcWnd->size().height()-48;
    
       this->resize(800, max(ContentY, WndY));
    
       HWND hwnd = reinterpret_cast<HWND>(this->effectiveWinId());
       HDC PaintDC = GetDC(hwnd);
    
       QPoint wnd = mapTo(window(), QPoint(0,0));
       POINT org;
       GetDCOrgEx(PaintDC, &org);
       SetViewportOrgEx(PaintDC, wnd.x(), wnd.y(), &org);
    
       qDebug() << "Time: " << QTime::currentTime();
       qDebug() << "wnd: " << wnd;
       qDebug() << "size(): " << size();
    
       // Set the clipping region
       HRGN hrgn = CreateRectRgn(rect.left()+wnd.x(), rect.top()+wnd.y(),
       	rect.right()+wnd.x()+1, rect.bottom()+wnd.y()+1);
       SelectClipRgn(PaintDC, hrgn);
    
       LogFont.lfHeight = 15;
       LogFont.lfWidth = 0;
       LogFont.lfEscapement = 0;
       LogFont.lfOrientation = 0;
       LogFont.lfWeight = FW_NORMAL;
       LogFont.lfItalic = 0;
       LogFont.lfUnderline = 0;
       LogFont.lfStrikeOut = 0;
       LogFont.lfCharSet = ANSI_CHARSET;
       LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
       LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
       LogFont.lfQuality = DEFAULT_QUALITY;
       LogFont.lfPitchAndFamily = VARIABLE_PITCH | FF_DONTCARE;
       strcpy(LogFont.lfFaceName, "TIMES");
       Font = CreateFontIndirect(&LogFont);        // create font
       Pen = CreatePen(PS_SOLID, 3, 0x00000000);	// create pen
       OldPen = (HPEN) SelectObject(PaintDC, Pen);
       MoveToEx(PaintDC, 2, 35, 0);
       LineTo(PaintDC, 1000, 35);
       MoveToEx(PaintDC, 325, 25, 0);
       LineTo(PaintDC, 325, max(ContentY, WndY));
       SelectObject(PaintDC, OldPen);
       DeleteObject(Pen);                          // delete pen
       OldFont = (HFONT) SelectObject(PaintDC, Font);
       strcpy(buf, "hkl");
       TextOut(PaintDC, 50, 15, buf, strlen(buf));
       strcpy(buf, "E calc");
       TextOut(PaintDC, 100, 15, buf, strlen(buf));
       strcpy(buf, "E obs");
       TextOut(PaintDC, 150, 15, buf, strlen(buf));
       strcpy(buf, "I calc");
       TextOut(PaintDC, 200, 15, buf, strlen(buf));
       strcpy(buf, "I obs");
       TextOut(PaintDC, 250, 15, buf, strlen(buf));
       strcpy(buf, "hkl");
       TextOut(PaintDC, 350, 15, buf, strlen(buf));
       strcpy(buf, "E calc");
       TextOut(PaintDC, 400, 15, buf, strlen(buf));
       strcpy(buf, "E obs");
       TextOut(PaintDC, 450, 15, buf, strlen(buf));
       strcpy(buf, "I calc");
       TextOut(PaintDC, 500, 15, buf, strlen(buf));
       strcpy(buf, "I obs");
       TextOut(PaintDC, 550, 15, buf, strlen(buf));
    
       int calcSize;
       CALC::const_iterator iter;
       for (i = 0, iter = CalcData.begin(), calcSize = CalcData.size();
       	iter != CalcData.end() && i < 0.5*calcSize;
       	iter++, calcSize++)
       {
       	C = (*iter).get();
       	sprintf(buf, "%d%d%d", C->h, C->k, C->l);
       	TextOut(PaintDC, XI, YI, buf, strlen(buf));
       	sprintf(buf, "%6.3f", C->Ecalc);
       	TextOut(PaintDC, XI+50, YI, buf, strlen(buf));
       	if (C->Eobs != 0)
       		sprintf(buf, "%6.3f", C->Eobs);
       	else strcpy(buf, "  -");
       	TextOut(PaintDC, XI+100, YI, buf, strlen(buf));
       	sprintf(buf, "%6.3f", C->Icalc);
       	TextOut(PaintDC, XI+150, YI, buf, strlen(buf));
       	if (C->Iobs != 0)
       		sprintf(buf, "%6.3f", C->Iobs);
       	else strcpy(buf, "  -");
       	TextOut(PaintDC, XI+200, YI, buf, strlen(buf));
       	YI += 17;
       }
       XI = 350;
       YI = 50;
       for (; iter != CalcData.end(); iter++)
       {
       	C = (*iter).get();
       	sprintf(buf, "%d%d%d", C->h, C->k, C->l);
       	TextOut(PaintDC, XI, YI, buf, strlen(buf));
       	sprintf(buf, "%6.3f", C->Ecalc);
       	TextOut(PaintDC, XI+50, YI, buf, strlen(buf));
       	if (C->Eobs != 0)
       		sprintf(buf, "%6.3f", C->Eobs);
       	else strcpy(buf, "  -");
       	TextOut(PaintDC, XI+100, YI, buf, strlen(buf));
       	sprintf(buf, "%6.3f", C->Icalc);
       	TextOut(PaintDC, XI+150, YI, buf, strlen(buf));
       	if (C->Iobs != 0)
       		sprintf(buf, "%6.3f", C->Iobs);
       	else strcpy(buf, "  -");
       	TextOut(PaintDC, XI+200, YI, buf, strlen(buf));
       	YI += 17;
       }
       SelectObject(PaintDC, OldFont);
       DeleteObject(Font);									// delete font
       
       // Restore viewport state
       SetViewportOrgEx(PaintDC, org.x, org.y, 0);
    
       // Release backbuffer handle
       ReleaseDC(hwnd, PaintDC);
    
    }
    


  • Hi, the capability that came with Qt4's getDC(), i.e. to be able to draw using vintage Win32 ::MoveTo(). ::LineTo() etc. in Qt's paintEvent() seems to be gone in Qt5. If you browse Qt5's source you'll still see mention of a getDC(), but it's only present in the private headers :-(

    Your code will run fine, but not in the context of a paintEvent(), i.e. inside it you will be fighting with Qt and flicker will occur for sure.

    But I got an idea that might help, you can use a QTimer to reschedule your drawing to immediately after the paintEvent, that way paintEvent() will be done and you're free to draw, say like this:

    #include "qtimer.h"
    void CalcWnd::paintEvent(QPaintEvent *e)
    {
        QRect r = e->rect();
        QTimer::singleShot(0,[this,r] { DoRedrawPixmap(r); });
    }
    


  • Thank you so much for your help.


Log in to reply