c++qtqt5qmake

Custom mouse cursor in C++ using the Qt toolkit - constant flicker


I have implemented a custom cursor in C++ using the Qt 5.15 toolkit, as shown in this small test program containing three classes:

A mainwindow, a testwidget and a box.

The testwidget and box are both subclasses of QLabel.

The mainwindow contains the testwidget, the testwidget captures mouse events which move the box to follow the mouse.

QtCursorExample.h

#include <QMainWindow>
#include <QWidget>
#include <QLabel>

class CTestWidget;
class CMainWindow;

class CBox : public QLabel
{
Q_OBJECT
public:
  CBox(CTestWidget *parent);
  void followMouse(void);
  void mouseEntered(bool enter) { mouseEnteredFlag=enter;}
private:
  bool mouseEnteredFlag;
};


class CTestWidget : public QLabel
{
  Q_OBJECT
public:
  CTestWidget(CMainWindow *parent);
protected:
  void mouseMoveEvent(QMouseEvent *event);
  void enterEvent(QEvent *event);
  void leaveEvent(QEvent *event);
private:
  CBox *box;
};


class CMainWindow : public QMainWindow
{
  Q_OBJECT

public:
  CMainWindow(QWidget *parent=0);
private:
  CTestWidget *widget;
};

QtCursorExample.cpp

#include <QApplication>
#include <QMainWindow>
#include <QWidget>
#include <QLabel>
#include <QMouseEvent>

#include "QtCursorExample.h"

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  CMainWindow window;
  window.show();
  return app.exec();
}

CMainWindow::CMainWindow(QWidget *parent) : QMainWindow(parent)
{
  widget=new CTestWidget(this);
  setCentralWidget(widget);

  resize(400,400);
  move(100,100);
}
            

CTestWidget::CTestWidget(CMainWindow *parent) : QLabel(parent)
{
  setMouseTracking(true);     //receive mouse events even when no buttons are pressed

  box=new CBox(this);        //box attached to the cursor

  setStyleSheet("background-color: yellow;");
  setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
  setText("THIS IS A TEST");

  setMinimumSize(200,200);  
  setMaximumSize(200,200);
}



void CTestWidget::enterEvent(QEvent  *event)
{
  box->show();
  box->mouseEntered(true);

  event->accept();
}


void CTestWidget::leaveEvent(QEvent  *event)
{
  box->hide();
  box->mouseEntered(false);

  event->accept();
}



void CTestWidget::mouseMoveEvent(QMouseEvent  *event)
{
  box->followMouse(); 

  event->accept();
}


CBox::CBox(CTestWidget *parent) : QLabel(parent)
{
  mouseEnteredFlag=false;

  setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint|Qt::Tool);
  setAttribute(Qt::WA_TranslucentBackground);      //see through

  setFrameShape(QFrame::Box);
  setAlignment(Qt::AlignCenter);

  setLineWidth(2);

    setMinimumSize(15,15);
    setMaximumSize(15,15);
}


void CBox::followMouse(void)
{
  if(mouseEnteredFlag==true)
    {
    QPoint globalCursorPos=QCursor::pos();
                               //centre the box on cursor position
    globalCursorPos.setX(globalCursorPos.x()-10/2);
    globalCursorPos.setY(globalCursorPos.y()-10/2);

    move(globalCursorPos);
  }
}

PRO file

TARGET    = QtCursorExample
TEMPLATE  = app
CONFIG   += qt
QT       += widgets

SOURCES = QtCursorExample.cpp
HEADERS = QtCursorExample.h
QMAKE_CXXFLAGS += -Wall

However, even when the mouse is not moved, the box is continually redrawn and flickers constantly.

I have also tried setting the cursor to Qt::BlankCursor, but this has no effect, the box flickers constantly.

Any advice?


Solution

  • Edited according to recommendations. I did not erase the previous version.

    #include <QLabel>
    #include <QMouseEvent>
    #include <QGridLayout>
    
    using BaseClass= QWidget;
    class CTestWidget : public BaseClass
    {
        Q_OBJECT
    
    private:
        QLabel  *box, *pix;
    
    public:
        using BaseClass::BaseClass;
        CTestWidget(QWidget *parent=nullptr) : BaseClass(parent)
        {
            setMouseTracking(true);
            setFixedSize(200,200);
    
            pix=new QLabel;
            pix->setStyleSheet("background-color: yellow;");
            pix->setAlignment(Qt::AlignCenter);
            pix->setText("THIS IS A TEST");
            pix->setAttribute(Qt::WA_TransparentForMouseEvents,true);
    
            box=new QLabel(pix);
            box->hide();
            box->setAttribute(Qt::WA_TranslucentBackground,true);
            box->setAttribute(Qt::WA_TransparentForMouseEvents,true);
            box->setFrameShape(QFrame::Box);
            box->setLineWidth(2);
            box->setFixedSize(15,15);
    
            auto layout=new QGridLayout;
            setLayout(layout);
            layout->addWidget(pix, 0,0);        
        }
    
    protected:
        void enterEvent(QEnterEvent *event)override{
            BaseClass::enterEvent(event);
            box->show();
        }
        void leaveEvent(QEvent *event)override{
            BaseClass::leaveEvent(event);
            box->hide();
        }
        void mouseMoveEvent(QMouseEvent  *event)override{
            BaseClass::mouseMoveEvent(event);
            setUpdatesEnabled(false);
            box->move(pix->mapFromGlobal(event->globalPos() - box->rect().center()));
            setUpdatesEnabled(true);
        }
    };