在QGraphicsView上实时捕获和绘制时出现闪烁

移动开发 2026-07-12

我在使用Qt 6.6开发一个颜色选择器小部件,QWidgetQGraphicsView,遇到了两个问题:

问题1)

QGraphicsView 没有缩放、窗口被移动且图像被绘制时,它会出现闪烁。我不确定这是否可以称为闪烁、鬼影,还是其他什么,我找不到导致图像像这样抖动的原因:

https://i.imgur.com/NCCFeiq.gif

为尽量提高捕获速度,我尝试了以下几种做法:

  • QTimer TimerType 设置为 Qt::PreciseTimer
  • 将定时器间隔设置为0
  • 在Qt的窗口抓取上使用 BitBlt
  • QImage 位缓存到DIBSection(ensureCaptureSurface
  • QGraphicsView viewportUpdateMode设置为 SmartViewportUpdate
  • QGraphicsView 设置为 RenderHint(QPainter::SmoothPixmapTransform, false);

问题2)

GrapchisView 放大时,我想让鼠标或窗口移动得慢一些,因为像素变大,鼠标快速移动时会跳跃像素,无法精确地移动到某个具体像素上:

https://i.imgur.com/m4mjNx9.gif

为实现这一点,我在 mouseProcHookCallback 函数中返回false,使位于 CALLBACK MouseProc 的鼠标钩子返回1;这阻止了输入消息继续传递。随后我尝试使用 SendInput 来操作光标位置,如上面的GIF所示,这也会导致闪烁,可能是问题1 的闪烁再加上我修改光标位置的尝试所致。

我还尝试使用Windows API的 ClipCursor,它降低了闪烁,但光标移动仍然看起来很怪,显得不自然。

即使在发布模式下测试且不做任何日志记录,两个问题依然存在。

我该如何停止这些闪烁问题?

(注:我尝试在这里添加这两个GIF,但失败了,请管理员帮忙修复吗?)

Main.cpp

#include "ColorPicker.h"



static bool createColorPicker(WPARAM wParam, LPARAM lParam)
{
    if (wParam == WM_KEYDOWN && ((KBDLLHOOKSTRUCT*)lParam)->vkCode == VK_F4)
        new ColorPicker();
    return true;
}



int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    qApp->setQuitOnLastWindowClosed(false);

    setInputHooks();
    setKeyboardHookCallback(new QObject(), createColorPicker);

    new ColorPicker();

    return app.exec();
}

ColorPicker.h

#pragma once
#include <QtWidgets>



inline HHOOK g_keyboardHook  = NULL;
inline HHOOK g_mouseHook     = NULL;
inline QMap<QObject*, std::function<bool(WPARAM, LPARAM)>> g_keyboardHookCallbacks;
inline QMap<QObject*, std::function<bool(WPARAM, LPARAM)>> g_mouseHookCallbacks;



inline LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode != HC_ACTION)
        return CallNextHookEx(g_keyboardHook, nCode, wParam, lParam);

    for (const auto& callback : g_keyboardHookCallbacks.values())
    {
        if (!callback(wParam, lParam))
            return 1; // Block the event if any callback returns false
    }

    return CallNextHookEx(g_keyboardHook, nCode, wParam, lParam);
}



inline LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode != HC_ACTION)
        return CallNextHookEx(g_mouseHook, nCode, wParam, lParam);

    MSLLHOOKSTRUCT* mouseStruct = (MSLLHOOKSTRUCT*)lParam;

    for (const auto& callback : g_mouseHookCallbacks.values())
    {
        if (!callback(wParam, lParam))
            return 1; // Block the event if any callback returns false
    }

    return CallNextHookEx(g_mouseHook, nCode, wParam, lParam);
}



template<typename T>
static void setKeyboardHookCallback(T* obj, bool(T::*callback)(WPARAM, LPARAM))
{
    QObject::connect(obj, &QObject::destroyed, [obj]
    {
        g_keyboardHookCallbacks.remove(obj);
    });
    g_keyboardHookCallbacks[obj] = ([obj, callback](WPARAM wParam, LPARAM lParam) { return (obj->*callback)(wParam, lParam); });
}



template<typename T>
static void setMouseHookCallback(T* obj, bool(T::*callback)(WPARAM, LPARAM))
{
    QObject::connect(obj, &QObject::destroyed, [obj]
    {
        g_mouseHookCallbacks.remove(obj);
    });
    g_mouseHookCallbacks[obj] = ([obj, callback](WPARAM wParam, LPARAM lParam) { return (obj->*callback)(wParam, lParam); });
}



inline void setKeyboardHookCallback(QObject* obj, std::function<bool(WPARAM, LPARAM)> callback)
{
    QObject::connect(obj, &QObject::destroyed, [obj]
    {
        g_keyboardHookCallbacks.remove(obj);
    });
    g_keyboardHookCallbacks[obj] = callback;
}



inline void setInputHooks()
{
    QSettings settings("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat);
    settings.setValue("LowLevelHooksTimeout", 100);
    settings.sync();

    g_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0);
    g_mouseHook    = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, 0);
    if (g_keyboardHook == NULL || g_mouseHook == NULL)
        MessageBox(NULL, L"Failed to set input hook", L"Error", MB_ICONERROR);
}



class GridView : public QGraphicsView
{
public:
    QGraphicsScene*      m_scene = nullptr;
    QPixmap              m_pixmap; // Pixmap set on the pixmapItem
    QGraphicsPixmapItem* m_pixmapItem;
    QGraphicsItemGroup*  m_gridGroup;
    bool m_enabled     = false;
    bool m_gridEnabled = false;
    QRect  m_sceneRect;
    QPoint m_lastMousePos;
    qreal  m_baseScale     = 1.0;
    qreal  m_currentScale  = 1.0;

    GridView(QWidget* p = nullptr);

    void setPixmap(const QPixmap& pixmap);
    void setGraphicsViewEnabled();
    void setGridEnabled();
    void resetView();
    void centerScene();
    void updateGrid();

    void wheelEvent(QWheelEvent* event) override;
};



class ColorPicker : public QWidget
{
    Q_OBJECT
public:
    const int GRID_WIDTH  = 256;
    const int GRID_HEIGHT = 256;

    QTimer*   m_captureScreenTimer = nullptr;
    GridView* m_gridView = nullptr;

    QPointF   m_cursorAccum;
    QPoint    m_lastCursorPos;
    QSize     m_captureSize;

    qreal     m_captureDpr       = 0.0;
    HDC       m_captureDC        = nullptr;
    HBITMAP   m_captureBitmap    = nullptr;
    HGDIOBJ   m_captureOldBitmap = nullptr;

    QImage    m_captureImage;

    ColorPicker();
    ~ColorPicker();

    void destroyCaptureSurface();
    bool ensureCaptureSurface();

    void lockCursor();
    void unlockCursor();
    void updateOverlayPosition(const QPoint& pt);

    void captureScreenTimerTimeout();

    bool keyboardProcHookCallback(WPARAM wParam, LPARAM lParam);
    bool mouseProcHookCallback(WPARAM wParam, LPARAM lParam);
};

ColorPicker.cpp

#include "ColorPicker.h"
#include <fstream>
#include <limits>
#include <sstream>



static std::ofstream& getLog()
{
    static std::ofstream log("D:\\colorpicker_debug.log", std::ios::trunc);
    return log;
}



void logCaptureState(const QPoint& cursorPos, const QPoint& windowPos, const QSize& captureSize, qreal dpr)
{
    static QPoint lastCursorPos(std::numeric_limits<int>::min(), std::numeric_limits<int>::min());
    static QPoint lastWindowPos(std::numeric_limits<int>::min(), std::numeric_limits<int>::min());
    static QSize  lastCaptureSize;
    static qreal  lastDpr = 0.0;

    if (cursorPos == lastCursorPos && windowPos == lastWindowPos && captureSize == lastCaptureSize && dpr == lastDpr)
        return;

    lastCursorPos   = cursorPos;
    lastWindowPos   = windowPos;
    lastCaptureSize = captureSize;
    lastDpr = dpr;

    auto& log = getLog();
    log << "CAPTURE cursorPos=(" << cursorPos.x()       << "," << cursorPos.y() << ")"
        << " winPos=("           << windowPos.x()       << "," << windowPos.y() << ")"
        << " captureSize=("      << captureSize.width() << "x" << captureSize.height() << ")"
        << " dpr=" << dpr
        << '\n';
}



GridView::GridView(QWidget* p) : QGraphicsView(p) 
{
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setRenderHint(QPainter::SmoothPixmapTransform, false);
    setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
    setFrameShape(QFrame::NoFrame);
    setTransformationAnchor(QGraphicsView::NoAnchor);
    setResizeAnchor(QGraphicsView::NoAnchor);
    setBackgroundBrush(Qt::NoBrush);
    setAlignment(Qt::AlignLeft | Qt::AlignTop);
    setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing);
    setOptimizationFlag(QGraphicsView::DontSavePainterState);
    setContentsMargins(0, 0, 0, 0);
    viewport()->setContentsMargins(0, 0, 0, 0);    
    setMouseTracking(true);

    m_scene = new QGraphicsScene(this);
    m_scene->installEventFilter(this);

    m_pixmapItem = new QGraphicsPixmapItem();
    m_scene->addItem(m_pixmapItem);

    m_gridGroup = new QGraphicsItemGroup();
    m_scene->addItem(m_gridGroup);
    m_gridGroup->setZValue(1); // Ensure grid is above image

    setScene(m_scene);
};



void GridView::setPixmap(const QPixmap& pixmap)
{
    QSize oldSize = m_pixmap.size();
    m_pixmap = pixmap;
    m_pixmapItem->setPixmap(pixmap);

    if (oldSize != pixmap.size())
    {
        QRectF bounds = m_pixmapItem->boundingRect();
        m_scene->setSceneRect(bounds);
        centerOn(bounds.center());
    }
}



void GridView::setGraphicsViewEnabled()
{
    if (m_enabled)
        resetView();
    else if (m_gridEnabled)
        updateGrid();
    m_enabled = !m_enabled;
}



void GridView::setGridEnabled()
{
    m_gridEnabled = !m_gridEnabled;
    if (m_gridEnabled)
    {
        if (m_currentScale > 3.0)
        {
            m_gridGroup->setVisible(true);
            updateGrid();
        }
    }
    else
    {
        m_gridGroup->setVisible(false);
        delete m_gridGroup->childItems().first();
    }
}



void GridView::resetView()
{
    // Reset transform
    setTransform(QTransform());

    // Calculate scale to fit view
    QRectF viewRect = viewport()->rect();
    QRectF imageRect = m_pixmapItem->boundingRect();

    qreal scaleX = viewRect.width() / imageRect.width();
    qreal scaleY = viewRect.height() / imageRect.height();
    m_baseScale = qMin(scaleX, scaleY);
    m_currentScale = m_baseScale;

    // Center the pixmap in the view
    m_pixmapItem->setPos((viewRect.width() - imageRect.width() * m_baseScale) / 2.0,
        (viewRect.height() - imageRect.height() * m_baseScale) / 2.0);

    // Apply base scale
    QTransform transform;
    transform.scale(m_baseScale, m_baseScale);
    setTransform(transform);

    // Reset grid visibility
    m_gridGroup->setVisible(m_currentScale > 3.0);
}



void GridView::centerScene()
{ 
    // First check if the top left corner is in the middle of the view.
    QPointF topLeft = mapFromScene(0, 0);
    if (topLeft.x() > 0 || topLeft.y() > 0)
    {
        QTransform t;
        t.translate(
            topLeft.x() > 0 ? -topLeft.x() / m_currentScale : 0,
            topLeft.y() > 0 ? -topLeft.y() / m_currentScale : 0
        );
        setTransform(t, true);
    }
    else
    { 
        // If not, then check if perhaps the bottom right corner is.
        QPointF bottomRight = mapFromScene(width(), height());
        if (bottomRight.x() < width() || bottomRight.y() < height())
        {
            QTransform t;
            t.translate(
                bottomRight.x() < width() ? - (bottomRight.x() - width())  / m_currentScale : 0,
                bottomRight.y() < height() ? -(bottomRight.y() - height()) / m_currentScale : 0
            );
            setTransform(t, true);
        }
    }

    QPolygonF point = mapToScene(viewport()->rect());
    if (!point.isEmpty())
        m_sceneRect = QRect(point[0].toPoint(), point[2].toPoint());
}



void GridView::updateGrid()
{
    if (!m_gridEnabled)
        return;

    // Clear existing grid
    while (!m_gridGroup->childItems().isEmpty()) {
        delete m_gridGroup->childItems().first();
    }

    // Get visible area in scene coordinates
    QRectF visibleRect = mapToScene(viewport()->rect()).boundingRect();
    QRectF imageRect   = m_pixmapItem->boundingRect();
    QRectF gridRect    = visibleRect.intersected(imageRect);

    // Calculate grid spacing in scene coordinates
    qreal gridSpacing = 1.0; // One pixel in image coordinates

    QPen gridPen(QColor(200, 200, 200, 255), 0); // Width 0 for single pixel line

    // Calculate grid line positions
    qreal startX = std::ceil(gridRect.left() / gridSpacing) * gridSpacing;
    qreal startY = std::ceil(gridRect.top() / gridSpacing) * gridSpacing;

    // Draw vertical lines
    for (qreal x = startX; x <= gridRect.right(); x += gridSpacing)
    {
        QGraphicsLineItem* line = new QGraphicsLineItem(x, gridRect.top(), x, gridRect.bottom());
        line->setPen(gridPen);
        m_gridGroup->addToGroup(line);
    }

    // Draw horizontal lines
    for (qreal y = startY; y <= gridRect.bottom(); y += gridSpacing)
    {
        QGraphicsLineItem* line = new QGraphicsLineItem(gridRect.left(), y, gridRect.right(), y);
        line->setPen(gridPen);
        m_gridGroup->addToGroup(line);
    }
}



void GridView::wheelEvent(QWheelEvent* event)
{
    if (m_pixmapItem->pixmap().isNull() || !m_enabled)
        return QGraphicsView::wheelEvent(event);

    // Store cursor position relative to scene
    QPointF mousePos        = event->position(),
            mousePosCurrent = mapToScene(mousePos.x(), mousePos.y());

    // Calculate zoom factor
    double factor = pow(1.5, event->angleDelta().y() / 240.0);
    double newScale = m_currentScale * factor;
    if (newScale < 1.) { // Do not allow zooming out (in absolute)
        factor /= newScale;
        newScale = 1.;
    }
    else if (newScale > 50.) { // Do not allow zoom > 50x
        factor = factor * 50. / newScale;
        newScale = 50.;
    }

    // Zoom centered on the mouse cursor
    QTransform t;
    t.translate(mousePosCurrent.x(), mousePosCurrent.y());
    t.scale(factor, factor);
    t.translate(-mousePosCurrent.x(), -mousePosCurrent.y());
    setTransform(t, true);

    m_currentScale = newScale;
    centerScene();

    // Update grid based on zoom level
    if (m_gridEnabled)
    {
        m_gridGroup->setVisible(m_currentScale > 3.0);
        if (m_currentScale > 3.0)
            updateGrid();
    }

    event->accept();
}



ColorPicker::ColorPicker() : QWidget()
{
    setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);

    setKeyboardHookCallback(this, &ColorPicker::keyboardProcHookCallback);
    setMouseHookCallback(this,    &ColorPicker::mouseProcHookCallback);

    QWidget* widget = new QWidget(this);
    widget->setFixedSize(GRID_WIDTH + 4, GRID_HEIGHT + 5);
    widget->setStyleSheet("background-color: red;");
    widget->move(0, 0);

    m_gridView = new GridView(this);
    m_gridView->setGraphicsViewEnabled();
    m_gridView->setGridEnabled();
    m_gridView->setFixedSize(GRID_WIDTH, GRID_HEIGHT);
    m_gridView->move(2, 3);

    m_captureScreenTimer = new QTimer(this);
    m_captureScreenTimer->setTimerType(Qt::PreciseTimer);
    m_captureScreenTimer->setInterval(0);
    m_captureScreenTimer->start();
    connect(m_captureScreenTimer, &QTimer::timeout, this, &ColorPicker::captureScreenTimerTimeout);

    m_lastCursorPos = QCursor::pos();

    HWND hwnd    = (HWND)winId();
    LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
    SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT);
    SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);

    SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
    ensureCaptureSurface();

    show();
}



ColorPicker::~ColorPicker()
{
    if (m_captureScreenTimer != nullptr)
        m_captureScreenTimer->stop();
    destroyCaptureSurface();
    unlockCursor();
}



void ColorPicker::destroyCaptureSurface()
{
    m_captureImage = QImage();

    if (m_captureDC != nullptr && m_captureOldBitmap != nullptr)
    {
        SelectObject(m_captureDC, m_captureOldBitmap);
        m_captureOldBitmap = nullptr;
    }

    if (m_captureBitmap != nullptr)
    {
        DeleteObject(m_captureBitmap);
        m_captureBitmap = nullptr;
    }

    if (m_captureDC != nullptr)
    {
        DeleteDC(m_captureDC);
        m_captureDC = nullptr;
    }

    m_captureSize = QSize();
    m_captureDpr  = 0.0;
}



bool ColorPicker::ensureCaptureSurface()
{
    qreal dpr = m_gridView->devicePixelRatioF();
    QSize captureSize(qRound(256 * dpr), qRound(256 * dpr));
    if (captureSize.isEmpty())
        return false;

    if (m_captureBitmap != nullptr && captureSize == m_captureSize && dpr == m_captureDpr)
        return true;

    destroyCaptureSurface();

    BITMAPINFO bmi              = {};
    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth       = captureSize.width();
    bmi.bmiHeader.biHeight      = -captureSize.height();
    bmi.bmiHeader.biPlanes      = 1;
    bmi.bmiHeader.biBitCount    = 32;
    bmi.bmiHeader.biCompression = BI_RGB;

    void* dibBits   = nullptr;
    m_captureBitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &dibBits, nullptr, 0);
    if (m_captureBitmap == nullptr || dibBits == nullptr)
    {
        destroyCaptureSurface();
        return false;
    }

    m_captureDC = CreateCompatibleDC(nullptr);
    if (m_captureDC == nullptr)
    {
        destroyCaptureSurface();
        return false;
    }

    m_captureOldBitmap = SelectObject(m_captureDC, m_captureBitmap);
    m_captureImage     = QImage(static_cast<uchar*>(dibBits), captureSize.width(), captureSize.height(), captureSize.width() * 4, QImage::Format_ARGB32);
    m_captureImage.setDevicePixelRatio(dpr);
    m_captureSize = captureSize;
    m_captureDpr  = dpr;
    return !m_captureImage.isNull();
}



void ColorPicker::lockCursor()
{
    POINT pt;
    GetCursorPos(&pt);
    RECT rc = { pt.x, pt.y, pt.x + 1, pt.y + 1 };
    ClipCursor(&rc);
}



void ColorPicker::unlockCursor()
{
    ClipCursor(NULL);
}



void ColorPicker::updateOverlayPosition(const QPoint& cursorPos)
{
    QPoint newPos = QPoint(cursorPos.x() - (size().width() / 2) + 9,
        cursorPos.y() - (size().height() / 2));
    if (pos() != newPos)
        move(newPos);
}



bool ColorPicker::keyboardProcHookCallback(WPARAM wParam, LPARAM lParam)
{
    KBDLLHOOKSTRUCT* kbStruct = (KBDLLHOOKSTRUCT*)lParam;
    if (wParam == WM_KEYDOWN && kbStruct->vkCode == VK_ESCAPE)
    {
        close();
        deleteLater();
        return false;
    }
    return true;
}



bool ColorPicker::mouseProcHookCallback(WPARAM wParam, LPARAM lParam)
{
    MSLLHOOKSTRUCT* mouseStruct = (MSLLHOOKSTRUCT*)lParam;
    short delta = HIWORD(mouseStruct->mouseData);
    QPoint pt(mouseStruct->pt.x, mouseStruct->pt.y);

    if (wParam == WM_MOUSEMOVE)
    {
        auto& log = getLog();
        qreal scale = m_gridView->m_currentScale;

        if (scale > 1.0)
        {
            // Accept our own injected moves
            if (mouseStruct->flags & LLMHF_INJECTED)
            {
                log << "INJECTED pt=(" << pt.x() << "," << pt.y() << ")"
                    << " flags=0x" << std::hex << mouseStruct->flags << std::dec
                    << " lastPos=(" << m_lastCursorPos.x() << "," << m_lastCursorPos.y() << ")"
                    << " -> accepted\n";
                updateOverlayPosition(pt);
                m_lastCursorPos = pt;
                return true;
            }

            // Compute delta from where we last placed the cursor
            QPoint rawDelta = pt - m_lastCursorPos;
            m_cursorAccum += QPointF(rawDelta) / scale;

            int dx = (int)m_cursorAccum.x();
            int dy = (int)m_cursorAccum.y();
            m_cursorAccum -= QPointF(dx, dy);

            QPoint newPos = m_lastCursorPos + QPoint(dx, dy);
            std::ostringstream line;
            line << "REAL pt=("  << pt.x()              << "," << pt.y() << ")"
                << " flags=0x"   << std::hex            << mouseStruct->flags << std::dec
                << " lastPos=("  << m_lastCursorPos.x() << "," << m_lastCursorPos.y() << ")"
                << " rawDelta=(" << rawDelta.x() << "," << rawDelta.y() << ")"
                << " scale="     << scale
                << " dx="        << dx << " dy=" << dy
                << " accum=("    << m_cursorAccum.x() << "," << m_cursorAccum.y() << ")";

            if (dx != 0 || dy != 0)
            {
                line << " -> SendInput to (" << newPos.x() << "," << newPos.y() << ")";
            }

            line << " -> BLOCKED\n";
            log  << line.str();

            if (dx != 0 || dy != 0)
            {
                INPUT input      = {};
                input.type       = INPUT_MOUSE;
                input.mi.dx      = (LONG)(newPos.x() * 65535.0 / (GetSystemMetrics(SM_CXSCREEN) - 1));
                input.mi.dy      = (LONG)(newPos.y() * 65535.0 / (GetSystemMetrics(SM_CYSCREEN) - 1));
                input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
                SendInput(1, &input, sizeof(INPUT));
            }

            lockCursor();
            QTimer::singleShot(1000, this, &ColorPicker::unlockCursor);
            return false;
        }
        else
        {
            m_lastCursorPos = pt;
            m_cursorAccum   = QPointF(0, 0);
            updateOverlayPosition(pt);
        }
    }
    else if (wParam == WM_MOUSEWHEEL && (GetKeyState(VK_CONTROL) & 0x8000))
    {
        QMetaObject::invokeMethod(this, [this, delta, pt]()
        {
            QPoint angleDelta(0, delta);
            QPointF pos = m_gridView->mapFromGlobal(pt);
            QWheelEvent* wheelEvent = new QWheelEvent(pos, m_gridView->mapToGlobal(pos), QPoint(), angleDelta, Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
            QCoreApplication::postEvent(m_gridView->viewport(), wheelEvent);
        }, Qt::QueuedConnection);
        return false;
    }

    return true;
}



void ColorPicker::captureScreenTimerTimeout()
{
    if (!ensureCaptureSurface())
        return;

    QPoint cursorPos = m_lastCursorPos;
    logCaptureState(cursorPos, pos(), m_captureSize, m_captureDpr);

    HDC hdcScreen = GetDC(nullptr);
    if (hdcScreen == nullptr)
        return;

    BitBlt(m_captureDC, 0, 0, m_captureSize.width(), m_captureSize.height(), hdcScreen,
        cursorPos.x() - m_captureSize.width() / 2,
        cursorPos.y() - m_captureSize.height() / 2,
        SRCCOPY);
    ReleaseDC(nullptr, hdcScreen);

    m_gridView->setPixmap(QPixmap::fromImage(m_captureImage));
}

解决方案

禁用Windows窗口边框和标题栏时可能会出现闪烁问题。这种方法对我有帮助:

void mouseMoveEvent(QMouseEvent  *event)override{
        BaseClass::mouseMoveEvent(event);
        setUpdatesEnabled(false);
        move(...);
        setUpdatesEnabled(true);
    }
};
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章