更改其子类中的 QPushButton 区域掩码以创建 RoundButton

Posted

技术标签:

【中文标题】更改其子类中的 QPushButton 区域掩码以创建 RoundButton【英文标题】:Changing the QPushButton region mask in its subclass to create a RoundButton 【发布时间】:2018-10-25 05:51:04 【问题描述】:

我正在尝试通过子类化和设置区域掩码来创建一个圆形按钮,以便我可以在我的项目中重用它。我知道我们可以重写paintEvent 方法并画一个圆来将它显示为一个圆形按钮。但是这种方法的问题是,如果用户在圆圈外(但在按钮矩形内)单击,它将被视为按钮单击。设置区域掩码时我们没有看到这个问题。

我尝试通过在 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);

【问题讨论】:

请编辑您的问题以显示您到目前为止所做的尝试——最好是minimal reproducible example。 【参考方案1】:

Qt 文档。为“非矩形”小部件提供了一个示例——Shaped Clock Example。

(Un-)幸运的是,在我运行自己的示例之前,我并没有记住这一点。

我从 Qt 文档开始。与

void QWidget::setMask(const QBitmap &amp;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

为此我从我的一个应用程序转换了 SVG。

我尝试将它应用到QPushButton 作为图标和掩码。这看起来很奇怪。我不太确定到底是什么问题: - 使用相应的。 QPushButton 作为***小部件(即主窗口) - QPushButtons 图标渲染和掩码在位置或大小方面可能不匹配。

没有深入挖掘,我更改了代码并在下一次尝试中修复了这两个问题:

制作派生按钮(如 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()

关于输出:

    我点击了按钮的四个角。 我点击了按钮的中心。

【讨论】:

在参考 Shaped Clock 示例并更改 resizeEvent 方法中的代码后,它起作用了。谢谢。 我还有一个问题。我在 paintEvent 方法中绘制了一条弧线,但我想将 RadialGradient 设置为弧线。绘制圆弧时如何设置或传递 QRadialGradient? @user3863360 请将此作为一个新问题提出。这增加了正确答案的机会。我自己从未这样做过我会您可以通过QBrush 应用渐变。 Qt 文档:Basic Drawing Example. @user3863360 我错了。我已经为一个矩形做过一次,我的猜测是正确的。构造QRadialGradient 对象。将其包装成QBrush。其余部分来自QPainter 文档:设置画笔以将其与QPainter::drawPie()QPainter::drawChord() 一起使用。 感谢 cmets。我正在绘制弧线,但使用 QPainter::drawPie() 或 QPainter::drawChord 没有得到我想要的。我会在新问题中提问。

以上是关于更改其子类中的 QPushButton 区域掩码以创建 RoundButton的主要内容,如果未能解决你的问题,请参考以下文章

何创建文本框掩码以仅允许 Office Access 表单上的文本

如何正确使用位掩码以正确使用数字io端口(USB-AD14f)

Maya Pyside2 UI,无法让 QPushButton 与同一类中的函数连接

子 QMenu 中的 QPushButton,悬停时更改背景

更改 Restlet 发回的状态码以进行身份​​验证

如何访问其子类中的类的私有变量?