更改其子类中的 QPushButton 区域掩码以创建 RoundButton
Changing the QPushButton region mask in its subclass to create a RoundButton
我正在尝试通过子类化和设置区域遮罩来创建一个圆形按钮,以便我可以在我的项目中重复使用它。我知道我们可以覆盖 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);
}
Qt 文档。提供“非矩形”小部件的示例 – Shaped Clock Example.
(Un-)幸运的是,在我拿到自己的样品之前我还记得这个 运行。
我从 Qt 文档开始。与
void QWidget::setMask(const QBitmap &bitmap)
Causes only the pixels of the widget for which bitmap has a corresponding 1 bit to be visible. If the region includes pixels outside the rect() of the widget, window system controls in that area may or may not be visible, depending on the platform.
Note that this effect can be slow if the region is particularly complex.
The following code shows how an image with an alpha channel can be used to generate a mask for a widget:
QLabel topLevelLabel;
QPixmap pixmap(":/images/tux.png");
topLevelLabel.setPixmap(pixmap);
topLevelLabel.setMask(pixmap.mask());
The label shown by this code is masked using the image it contains, giving the appearance that an irregularly-shaped image is being drawn directly onto the screen.
Masked widgets receive mouse events only on their visible portions.
See also mask(), clearMask(), windowOpacity(), and Shaped Clock Example.
(读到这里,还是漏掉了link的例子。)
起初,我准备了一个适合我目的的像素图 – dialog-error.png
:
为此我从我的一个应用程序转换了一个 SVG。
我尝试将它应用到 QPushButton
作为图标和蒙版。这看起来很奇怪。我不太确定到底是什么问题:
- 使用resp。 QPushButton
作为顶层小部件(即主要 window)
- QPushButton
s 图标渲染和掩码在位置或大小方面可能不匹配。
没有深入挖掘,我更改了代码并在下次尝试中解决了这两个问题:
- 制作派生按钮(如 OP 所述)
- 将按钮用作非顶层小部件。
很快就见效了。我加了一些代码让效果更明显:
- main 的鼠标按下事件处理程序 window 以显示形状是否被正确考虑
- 一个信号处理程序,用于显示是否正确接收了对按钮(形状)的点击。
所以,我得到了下面的示例 – 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()
关于输出:
- 我点进了按钮的四个角。
- 我点击了按钮的中心。
我正在尝试通过子类化和设置区域遮罩来创建一个圆形按钮,以便我可以在我的项目中重复使用它。我知道我们可以覆盖 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);
}
Qt 文档。提供“非矩形”小部件的示例 – Shaped Clock Example.
(Un-)幸运的是,在我拿到自己的样品之前我还记得这个 运行。
我从 Qt 文档开始。与
void QWidget::setMask(const QBitmap &bitmap)
Causes only the pixels of the widget for which bitmap has a corresponding 1 bit to be visible. If the region includes pixels outside the rect() of the widget, window system controls in that area may or may not be visible, depending on the platform.
Note that this effect can be slow if the region is particularly complex.
The following code shows how an image with an alpha channel can be used to generate a mask for a widget:
QLabel topLevelLabel; QPixmap pixmap(":/images/tux.png"); topLevelLabel.setPixmap(pixmap); topLevelLabel.setMask(pixmap.mask());
The label shown by this code is masked using the image it contains, giving the appearance that an irregularly-shaped image is being drawn directly onto the screen.
Masked widgets receive mouse events only on their visible portions.
See also mask(), clearMask(), windowOpacity(), and Shaped Clock Example.
(读到这里,还是漏掉了link的例子。)
起初,我准备了一个适合我目的的像素图 – dialog-error.png
:
为此我从我的一个应用程序转换了一个 SVG。
我尝试将它应用到 QPushButton
作为图标和蒙版。这看起来很奇怪。我不太确定到底是什么问题:
- 使用resp。 QPushButton
作为顶层小部件(即主要 window)
- QPushButton
s 图标渲染和掩码在位置或大小方面可能不匹配。
没有深入挖掘,我更改了代码并在下次尝试中解决了这两个问题:
- 制作派生按钮(如 OP 所述)
- 将按钮用作非顶层小部件。
很快就见效了。我加了一些代码让效果更明显:
- main 的鼠标按下事件处理程序 window 以显示形状是否被正确考虑
- 一个信号处理程序,用于显示是否正确接收了对按钮(形状)的点击。
所以,我得到了下面的示例 – 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()
关于输出:
- 我点进了按钮的四个角。
- 我点击了按钮的中心。