【问题标题】:Changing the QPushButton region mask in its subclass to create a RoundButton更改其子类中的 QPushButton 区域掩码以创建 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);
}

【问题讨论】:

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


【解决方案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()

关于输出:

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

【讨论】:

  • 在参考 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 没有得到我想要的。我会在新问题中提问。
猜你喜欢
  • 1970-01-01
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 2020-06-16
  • 2020-09-28
  • 2016-12-25
  • 2012-11-14
  • 2021-04-14
相关资源
最近更新 更多