更改其子类中的QPushButton区域遮罩以创建RoundButton

时间:2018-10-25 05:51:04

标签: c++ qt user-interface qt5 desktop

我试图通过子类化和设置区域掩码来创建圆形按钮,以便可以在项目中重用它。我知道我们可以覆盖paintEvent方法并绘制一个圆圈以将其显示为圆形按钮。但是这种方法的问题在于,如果用户在圆外(但在按钮rect内)单击,则将其视为按钮单击。设置区域遮罩时我们看不到这个问题。

我试图通过在resizeEvent / paintEvent中调用setmask方法来设置区域。无论哪种情况,按钮都将为空白。我试图找出子类内部的位置来设置区域遮罩。

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 :(得分:2)

Qt文档。提供了“非矩形”小部件的示例– Shaped Clock Example

(不幸的是),我还记得在运行自己的样本之前记得这一点。

我从Qt文档开始。与

  

void QWidget::setMask(const QBitmap &bitmap)

     

仅导致其位图具有相应1位可见的小部件的像素。如果该区域包括小部件rect()外部的像素,则取决于平台,该区域中的窗口系统控件可能可见,也可能不可见。

     

请注意,如果该区域特别复杂,此效果可能会很慢。

     

以下代码显示了如何使用具有Alpha通道的图像生成窗口小部件的蒙版:

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

此代码显示的标签被包含在其中的图像遮盖了,使外观看起来像是在屏幕上直接绘制了不规则形状的图像。

     

掩盖的窗口小部件仅在其可见部分接收鼠标事件。

     

另请参见mask()clearMask()windowOpacity()Shaped Clock Example

(阅读本文时,我仍然错过了示例的链接。)

首先,我准备了适合自己的像素图– dialog-error.png

dialog-error.png

我从其中一个应用程序转换了SVG。

我尝试将其作为图标和蒙版应用到QPushButton。这看起来很奇怪。我不太确定到底是什么问题: -使用resp。 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. 我点击了按钮的中心。