PDA

View Full Version : QRubberBand painting in the scroll area widget



SkripT
10th January 2006, 16:24
Hi all, I have subclassed QScrollArea because I want to draw a selection rectangle with QRubberband when the user clicks the left mouse button on the viewport. With the code below, the program does exactly what I want (the rubberband is well drawn respect the mouse icon and the scrollbars of the scroll area moves when the user is making a selection and is at the margins of the viewport). The only problem is that the rubberband is painted respect the scrollarea and what I want is paint it respect the widget of the scroll area. So now the rubber band is always painted in the same position even if the scroll bars are moved. Can anybody tell me how to I have to modify the code below to draw the rubberband respect the position of the widget of the scroll area? Thanks


void FotoAreaEditorFotos::mousePressEvent(QMouseEvent *event)
{
if (event -> button() != Qt::LeftButton) return;

iniSeleccioX = event -> globalX();
iniSeleccioY = event -> globalY();

areaSeleccio = new QRubberBand(QRubberBand::Rectangle);

areaSeleccio -> setGeometry(iniSeleccioX, iniSeleccioY, 1, 1);

areaSeleccio -> show();
}


void FotoAreaEditorFotos::mouseMoveEvent(QMouseEvent *event)
{
int x, y, w, h;
QScrollBar *scrollBar;

if (event ->buttons() == Qt::LeftButton)
{
int auxX = event -> globalX();
int auxY = event -> globalY();

QPoint posMax = mapToGlobal(QPoint(viewport() -> width() - 1,
viewport() -> height() - 1));
QPoint posMin = mapToGlobal(QPoint(0 , 0));

if (auxX < iniSeleccioX)
{
if (auxX <= posMin.x())
{
scrollBar = horizontalScrollBar();
scrollBar -> setValue(scrollBar -> value() - scrollBar -> singleStep());
x = posMin.x();
}
else
x = auxX;

w = iniSeleccioX - x + 1;
}
else
{
if (auxX >= posMax.x())
{
scrollBar = horizontalScrollBar();
scrollBar -> setValue(scrollBar -> value() + scrollBar -> singleStep());
w = posMax.x() - iniSeleccioX + 1;
}
else
w = auxX - iniSeleccioX + 1;

x = iniSeleccioX;
}

if (auxY < iniSeleccioY)
{
if (auxY < posMin.y())
{
scrollBar = verticalScrollBar();
scrollBar -> setValue(scrollBar -> value() - scrollBar -> singleStep());
y = posMin.y();
}
else
y = auxY;

h = iniSeleccioY - y + 1;
}
else
{
if (auxY > posMax.y())
{
scrollBar = verticalScrollBar();
scrollBar -> setValue(scrollBar -> value() + scrollBar -> singleStep());
h = posMax.y() - iniSeleccioY + 1;
}
else
h = auxY - iniSeleccioY + 1;

y = iniSeleccioY;
}

areaSeleccio -> setGeometry(x, y, w, h);
}
}


void FotoAreaEditorFotos::mouseReleaseEvent(QMouseEvent *event)
{
if (event -> button() == Qt::LeftButton)
delete areaSeleccio;
}

axeljaeger
10th January 2006, 16:34
I think the main purpose of QRubberBand is to show a rubberband above widgets i.e. show a rubberband for resizing a window.

However, it should also work with QRubberBand. I think the problem is that you are translating to global coordinates. So you are translating the offset of the scrollview away.

The simplest solution will be to draw the selection rectangle on your own using QPainter. If you draw the rectangle yourself, you can also have semitransparent background. This will look nice. :cool:

Edit: QtDesigner uses such a semi transparent rubberband. You can have a look at the sourcecode there, however, it is a lot of code.

SkripT
10th January 2006, 16:49
Thanks for the advise. But I think that with QRubberBand I could have semitransparent background changing it with setPalette (It's just a supposition) ;)

axeljaeger
10th January 2006, 17:41
I don't think so because semitransparent background is a bit tricky and I doubt that it as easy in a toplevel-widget as using a QPainter and draw on a window.

SkripT
11th January 2006, 11:34
Finally I have solved the problem painting the selection rectangle with qpainter in the paintEvent of the widget. If anybody is interesed in the code ask here for it.

sunil.thaha
14th January 2006, 06:13
Ofcourse Everyone needs the code
We have a working example to refer to Don't we ?

SkripT
14th January 2006, 11:46
Ok here's the code:


void FotoEditorFotos::mousePressEvent(QMouseEvent *event)
{
if (event -> button() != Qt::LeftButton) return;

iniSeleccioX = event -> x();
iniSeleccioY = event -> y();

seleccio = true;
x = iniSeleccioX;
y = iniSeleccioY;
w = 1;
h = 1;

update();
}


void FotoEditorFotos::mouseMoveEvent(QMouseEvent *event)
{
if (event -> buttons() == Qt::LeftButton)
{
int auxX = event -> x();
int auxY = event -> y();
int posMaxX = width() - 1;
int posMaxY = height() - 1;

if (auxX < iniSeleccioX)
{
if (auxX < 0)
x = 0;
else
x = auxX;

w = iniSeleccioX - x + 1;
}
else
{
if (auxX >= posMaxX)
w = posMaxX - iniSeleccioX + 1;
else
w = auxX - iniSeleccioX + 1;

x = iniSeleccioX;
}

if (auxY < iniSeleccioY)
{
if (auxY < 0)
y = 0;
else
y = auxY;

h = iniSeleccioY - y + 1;
}
else
{
if (auxY >= posMaxY)
h = posMaxY - iniSeleccioY + 1;
else
h = auxY - iniSeleccioY + 1;

y = iniSeleccioY;
}

update();
}

//emit UpdateMouseCoords(event->x(), event->y());*/
event -> ignore();
}


void FotoEditorFotos::mouseReleaseEvent(QMouseEvent *event)
{
if (event -> button() == Qt::LeftButton)
// Do something with the selection rectangle...
seleccio = false;
}


void FotoEditorFotos::paintEvent(QPaintEvent *event)
{
int pixX, pixY;

if (pixmap.width() < width())
pixX = (width() - pixmap.width()) / 2;
else
pixX = 0;
if (pixmap.height() < height())
pixY = (height() - pixmap.height()) / 2;
else
pixY = 0;

QPainter painter;
painter.begin(this);
QPen pen(Qt::blue, 2, Qt::DashLine);
painter.setPen(pen);
//painter.rotate(5);
//painter.setBrush(Qt::Dense1Pattern);
painter.drawPixmap(pixX, pixY, pixmap);
if (seleccio)
painter.drawRect(x, y, w, h);
painter.end();
}

That's the code for the widget that I insert in the scroll area and the code for the scroll area is the next:


FotoAreaEditorFotos::FotoAreaEditorFotos(EditorFot os *parent)
: QScrollArea(parent)
{
foto = new FotoEditorFotos;

setBackgroundRole(QPalette::Dark);
setWidget(foto);
}


void FotoAreaEditorFotos::mouseMoveEvent(QMouseEvent *event)
{
QScrollBar *scrollBar;

if (event ->buttons() != Qt::LeftButton) return;

int auxX = event -> x();
int auxY = event -> y();
int posMaxX = viewport() -> width() - 1;
int posMaxY = viewport() -> height() - 1;

if (auxX <= 0)
{
scrollBar = horizontalScrollBar();
scrollBar -> setValue(scrollBar -> value() - scrollBar -> singleStep());
}
else if (auxX >= posMaxX)
{
scrollBar = horizontalScrollBar();
scrollBar -> setValue(scrollBar -> value() + scrollBar -> singleStep());
}

if (auxY <= 0)
{
scrollBar = verticalScrollBar();
scrollBar -> setValue(scrollBar -> value() - scrollBar -> singleStep());
}
else if (auxY >= posMaxY)
{
scrollBar = verticalScrollBar();
scrollBar -> setValue(scrollBar -> value() + scrollBar -> singleStep());
}
}


void FotoAreaEditorFotos::paintEvent(QPaintEvent *event)
{
//I put directly the size of the pixmap (256, 259) because it's a test version, in the final version I have to make a method to return the pixmap size ;)
if (viewport() -> width() <= 259)
{
setWidgetResizable(false);
foto -> resize(259, viewport() -> height());
}
else
setWidgetResizable(true);

if (viewport() -> height() <= 256)
{
setWidgetResizable(false);
foto -> resize(qMax(259, viewport() -> width()), 256);
}
else
setWidgetResizable(widgetResizable());
}

All that code paint the image centered in the scroll area and if the image is larger thn the scroll area shows the scroll bars. Moreover if the user press the mouse under the viewport a selection rectangle is shown and if the rectangle is at the limits of the viewport of the scroll area, the scroll bars move automaticaly. Enjoy it.

SkripT
17th January 2006, 17:48
Hi all. These days I have made a new version for the code above. Now the image can be scaled and rotated and every time the image is painted automaticly at the center (applying the little bit of trigonometry that I remember ;) ). Well, all goes perfect but there's a little problem: the updating of the rubberband is very slow :( . The cause is that every time that i call the update function of the widget, paintEvent has to scale and rotate the pixmap to paint it again and, after, paint the rectangle of the rubber band under the pixmap. This means that when I move the mouse, the rubber band takes a little time in being painted under the cursor (the rubber band is delayed respect the cursor when I move the mouse a little fast). Could anyone tell me if there's a solution for it? Something like paint only the viewport of the scroll area or paint only the portion of the image that has to be updated? Thanks