PDA

View Full Version : QWidget::setMask: bug &/or help for workaround



ataffard
21st May 2008, 19:57
Hi,

I'm working on a set of classes implementing various button shapes that can be re-oriented (rotated & translated) for various set of layout (eg table, circles etc..)
In order to do this, the classes inherit from QPushButton and the paintButton method is reimplemented.

Since only the shape of the button (ellipse, trapezoid etc..) should be active when the mouse goes over it etc... and since the button can be rotated on itself to arrange it on a layout, I'm making use of setMask function to make only the shape of the button visible.

This approach works fine for rectangle, trapezoid shapes but for circle or ellipse shapes the application become extremely slow and the button have some hatched marks (see attached snapshots). The problem clearly come from setMask since when commented out the app. runs fine and the buttons look ok.

I'm using Qt 4.3.0 and also tried with 4.4.0 in case it was a bug and got fixed, but the problem remain.

Could anyone help me to determine if I'm doing something wrong (see attached code snippet)
or if this is a Qt issue in which case any advice/suggestion on how to workaround this without loosing the functionality and a smooth running app. would be more than welcome.

One possible workaround would be not to not use a mask but use a function that figure out if the mouse is within the button shape boundaries. Unfortunately, this would cause problem when the button are rotated and placed onto a layout due to the overlap in the QWidget rectangle. So it doesn't seem that this approach would workout.

Thanks in advance for the help.
Anyes







//__________________________________________________ ________________________________________________
void ShapedButtonEllipse::paintButton(QPaintEvent* )
{
updateButtonSize();

QPointF center = QPointF(width()/2., height()/2.);

QStylePainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);

painter.save();//save the painter state
painter.translate(center); //translate coordinate system to center of shape

/*
Test for state changes
*/
QColor button_color;
if(this->isEnabled())
{
m_hovered ? button_color = b_highlight : button_color = b_color;
if(m_pressed)
button_color = b_highlight.darker(250);
}
else
button_color = QColor(50, 50, 50);

/*
Rotate painter wrt to button center if the button has been rotated
*/
if(rot_angle != 0)
painter.rotate(-rot_angle);

/*
Draw the button
*/
painter.setPen(QPen(QBrush(Qt::black), bw, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); //button outline
QLinearGradient gradient(0., 0., 0., height()); //button inside color gradient
gradient.setColorAt(0., button_color);
gradient.setSpread(QGradient::PadSpread);
painter.setBrush(gradient);

QPainterPath outline;
outline.addEllipse(-s_width/2, -s_height/2, s_width, s_height); //button outline shape

/*
Define visible/active region (add 2pixel extra all around)
*/
const qreal b = bw/2 + 4;
QRegion maskedRegion((int)(-s_width/2 -b ), (int)(-s_height/2 -b ),
(int)( s_width +b*2), (int)( s_height +b*2), QRegion::Ellipse);
if (rot_angle != 0)
{
QMatrix matrix;
matrix.rotate(-rot_angle);
maskedRegion = maskedRegion * matrix;
maskedRegion.translate(center.toPoint());
}
else
{
maskedRegion.translate((int)center.x(),(int)center .y());
}

setMask(maskedRegion); //Funny looking after masking, very slow ????
painter.drawPath(outline);

painter.restore();

/*
Add button text if any
*/
QString text = this->text();
if(!text.isNull())
{
QFont font = this->font();
painter.setFont(font);
painter.setPen(t_color);
painter.setOpacity(1.0);
painter.drawText(0, 0, width(), height(), Qt::AlignCenter, text);
}

}

wysota
22nd May 2008, 11:11
What do you need the mask for here? Just paint the button and calculate the hit shape, should be very easy...

ataffard
23rd May 2008, 00:24
Hi,

Am I understanding correctly that what you are suggesting is to re-implement in whatever method tells the QWidget that the mouse is over it, the active/visible region to the given shape (which by default the QWidget::geometry(), ie rectangle containing the button shape)
How would one do that (ie which method(s) would need to be re-written) ?

What would happen in the case where a button (the part return by QWidget::geometry but not the active region) is on top of the active region of another button and the mouse is moved over the active region of the bottom button? Wouldn't it be the QWidget on top that would receive the click?

Thanks
-a

wysota
23rd May 2008, 08:34
How would one do that (ie which method(s) would need to be re-written) ?
mouse{Press,Release}Event().


Wouldn't it be the QWidget on top that would receive the click?
Yes, it would. If it would ignore it, the click would be propagated to parent and not to the other widget. If you want to retain the "clickability" of the other button you have to do some event passing in the parent or use QGraphicsView instead of widgets, it might be a good choice in your situation.