Изменение маски региона QPushButton в ее подклассе для создания RoundButton - PullRequest
0 голосов
/ 25 октября 2018

Я пытаюсь создать круглую кнопку путем создания подклассов и установки маски региона, чтобы я мог повторно использовать ее в своем проекте.Я знаю, что мы можем переопределить метод paintEvent и нарисовать круг, чтобы показать его в виде круглой кнопки.Но проблема этого подхода заключается в том, что если пользователь щелкает за пределами круга (но внутри прямоугольника кнопки), он будет рассматриваться как нажатие кнопки.Эту проблему мы не видим, когда устанавливаем маску региона.

Я пытался установить регион, вызывая метод setmask внутри resizeEvent / paintEvent.В любом случае кнопка будет пустой.Я пытаюсь выяснить место внутри подкласса для установки маски региона.

RoundAnimatingButton.h ->

#include <QPushButton>

namespace Ui {
class CRoundAnimatingBtn;
}

class CRoundAnimatingBtn : public QPushButton
{
    Q_OBJECT

public:
    explicit CRoundAnimatingBtn(QWidget *parent = nullptr);
    ~CRoundAnimatingBtn();

    void StartAnimation(QColor r);
    void StopAnimation();

public slots:    
    void timerEvent(QTimerEvent *e);


private:
    Ui::CRoundAnimatingBtn *ui;


    bool        m_Spinning;

    // QWidget interface
protected:
    void resizeEvent(QResizeEvent *event) override;
    void paintEvent(QPaintEvent * e) override;
};

#endif // ROUNDANIMATINGBTN_H

RoundAnimatingButton.cpp

CRoundAnimatingBtn::CRoundAnimatingBtn(QWidget *parent)
    : QPushButton (parent)
    , ui(new Ui::CRoundAnimatingBtn)
    , m_Spinning(false)
{
    ui->setupUi(this);
}



CRoundAnimatingBtn::~CRoundAnimatingBtn()
{
    delete ui;
}

void CRoundAnimatingBtn::paintEvent(QPaintEvent *e)
{
    QPushButton::paintEvent(e);

    if(m_Spinning)
    {
    // Animating code
    }
}

void CRoundAnimatingBtn::StartAnimation(QColor r)
{
    m_Spinning=true;
    startTimer(5);

}

void CRoundAnimatingBtn::StopAnimation()
{
    m_Spinning=false;
    this->update();
}


void CRoundAnimatingBtn::timerEvent(QTimerEvent *e)
{
    if(m_Spinning)
        this->update();
    else
        killTimer(e->timerId());
}

void CRoundAnimatingBtn::DrawRing()
{

 }


void CRoundAnimatingBtn::resizeEvent(QResizeEvent *event)
{

      // -----------------------------------
      // This code didn't work
      // -----------------------------------
        QRect rect = this->geometry();
        QRegion region(rect, QRegion::Ellipse);
        qDebug() << "PaintEvent Reound button - " << region.boundingRect().size();
        this->setMask(region);

        // ----------------------------------

        // ------------------------------------
        // This code worked
        // -------------------------------------
        int side = qMin(width(), height());
        QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side,
                             side, QRegion::Ellipse);
        setMask(maskedRegion);
}

1 Ответ

0 голосов
/ 25 октября 2018

Qt Doc.предоставляет пример для «непрямоугольных» виджетов - Пример Shaped Clock .

(Un-) К счастью, я запомнил это не раньше, чем запустил свой собственный образец.

Я начал в Qt doc.with

void QWidget::setMask(const QBitmap &bitmap)

Вызывает отображение только тех пикселей виджета, для которых bitmap имеет соответствующий 1 бит.Если область включает пиксели за пределами rect () виджета, элементы управления оконной системой в этой области могут или не могут быть видны, в зависимости от платформы.

Обратите внимание, что этот эффект может бытьмедленный, если область особенно сложная.

Следующий код показывает, как изображение с альфа-каналом может использоваться для создания маски для виджета:

QLabel topLevelLabel;
QPixmap pixmap(":/images/tux.png");
topLevelLabel.setPixmap(pixmap);
topLevelLabel.setMask(pixmap.mask());

Метка, показанная этимКод маскируется с помощью содержащегося в нем изображения, создавая впечатление, что изображение неправильной формы рисуется непосредственно на экране.

Замаскированные виджеты получают события мыши только на своих видимых частях.

См. Также mask () , clearMask () , windowOpacity () и Пример формы часов .

(Когда я читал это, я все же пропустил ссылку на пример.)

Сначала я подготовил для себя подходящее растровое изображение - dialog-error.png:

dialog-error.png

, для которых я конвертировал SVG из одного из моих приложений.

Я пытался применить его к QPushButton в виде значка и маски.Это выглядело очень странно.Я не совсем уверен, в чем именно проблема: - используя респ.QPushButton как виджет верхнего уровня (т.е. главное окно) - тот факт, что QPushButton рендеринг иконок и маска могут не совпадать в отношении положения или размера.

Не копая глубже, я изменил код и исправил обе проблемыв следующей попытке:

  • создание производной кнопки (как описано в OP)
  • с использованием кнопки в качестве виджета без верхнего уровня.

Это сработало в ближайшее время,Я добавил некоторый код, чтобы сделать эффект более очевидным:

  • обработчик события нажатия кнопки мыши для главного окна, чтобы показать, считается ли фигура правильным
  • обработчик сигнала, чтобы показать, нажимает ли кнопка(в форме) получены правильно.

Итак, я пришел к следующему примеру - testQPushButtonMask.cc:

#include <QtWidgets>

class MainWindow: public QWidget {

  public:
    explicit MainWindow(QWidget *pQParent = nullptr):
      QWidget(pQParent)
    { }
    virtual ~MainWindow() = default;
    MainWindow(const MainWindow&) = delete;
    MainWindow& operator=(const MainWindow&) = delete;

  protected:
    virtual void mousePressEvent(QMouseEvent *pQEvent) override;

};

void MainWindow::mousePressEvent(QMouseEvent *pQEvent)
{
  qDebug() << "MainWindow::mousePressEvent:" << pQEvent->pos();
  QWidget::mousePressEvent(pQEvent);
}

class RoundButton: public QPushButton {
  private:
    QPixmap _qPixmap;

  public:
    RoundButton(const QPixmap &qPixmap, QWidget *pQParent = nullptr):
      QPushButton(pQParent),
      _qPixmap(qPixmap)
    {
      setMask(_qPixmap.mask());
    }
    virtual ~RoundButton() = default;
    RoundButton(const RoundButton&) = delete;
    RoundButton& operator=(const RoundButton&) = delete;

    virtual QSize sizeHint() const override;

  protected:
    virtual void paintEvent(QPaintEvent *pQEvent) override;
};

QSize RoundButton::sizeHint() const { return _qPixmap.size(); }

void RoundButton::paintEvent(QPaintEvent*)
{
  QPainter qPainter(this);
  const int xy = isDown() * -2;
  qPainter.drawPixmap(xy, xy, _qPixmap);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QPixmap qPixmap("./dialog-error.png");
  // setup GUI
  MainWindow qWin;
  qWin.setWindowTitle(QString::fromUtf8("QPushButton with Mask"));
  QVBoxLayout qVBox;
  RoundButton qBtn(qPixmap);
  qVBox.addWidget(&qBtn);
  qWin.setLayout(&qVBox);
  qWin.show();
  // install signal handlers
  QObject::connect(&qBtn, &RoundButton::clicked,
    [](bool) { qDebug() << "RoundButton::clicked()"; });
  // runtime loop
  return app.exec();
}

Соответствующий файл проекта Qt testQPushButtonMask.pro

SOURCES = testQPushButtonMask.cc

QT += widgets

Скомпилировано и протестировано на cygwin64 :

$ qmake-qt5 testQPushButtonMask.pro

$ make && ./testQPushButtonMask
Qt Version: 5.9.4
MainWindow::mousePressEvent: QPoint(23,22)
MainWindow::mousePressEvent: QPoint(62,24)
MainWindow::mousePressEvent: QPoint(62,61)
MainWindow::mousePressEvent: QPoint(22,60)
RoundButton::clicked()

snapshot of testQPushButtonMask

Относительно выхода:

  1. Я нажал на четыре угла кнопки.
  2. Я нажал на центр кнопки.
...