更改其子类中的 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 &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
作为***小部件(即主窗口)
- QPushButton
s 图标渲染和掩码在位置或大小方面可能不匹配。
没有深入挖掘,我更改了代码并在下一次尝试中修复了这两个问题:
制作派生按钮(如 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 与同一类中的函数连接