PDA

View Full Version : setRasterOp Equivalent



vermarajeev
28th March 2007, 12:16
Hi guys,

What is the equivalent of setRasterOp( NotXorROP ) in Qt4?? I cant see any alternative defined in qtAssistance.

Thanks

wysota
28th March 2007, 13:18
I think currently there is no equivalent. Use of such thing would be highly limited as Qt4 forbids painting outside a paint event.

vermarajeev
28th March 2007, 13:33
I think currently there is no equivalent. Use of such thing would be highly limited as Qt4 forbids painting outside a paint event.

Then I'll have tough time porting to qt4 :confused: . In my application there are many places where I have used setRasterOp, so how should I solve the problem????


Qt4 forbids painting outside a paint event
If qt4 doesnt support setRasterOp, what about projects whose functionality is completely based on QPainter and setRasterOp. It will be a tough time for guy like me who is given tasks to port to qt4.

Is there a way I can solve above problem of setRasterOp with minimun code changes???. Without this my application looks very odd....

Please help!!!!!

wysota
28th March 2007, 14:49
You only have to modify your paintEvent code to apply the changes you'd do using the raster operation.

Uwe
28th March 2007, 19:01
Raster operations are history with Qt4. They don't fit into the graphic pipeline of Qt4, so say good bye to them.

But what did you do with the raster operations ?
The most common use case was the XOR mode to implement rubberbands, crosshairs ... without having to repaint the content below. With Qt4 you always have to repaint the content below - and yes this is sometimes too slow for interactive operations, f.e for a remote X session over a line with a limited bandwidth.

You might have a look at http://qwt.sourceforge.net/class_qwt_picker.html. QwtPicker is implemented as a masked widget, that displays a rubberband on top of your widget. Using it you can implement rubberband supported selections in a couple of lines.

Uwe

vermarajeev
29th March 2007, 05:58
Here is my sample code. How do I solve the setRasterOp problem

//.h

class setRasterOpTest : public QFrame
{
Q_OBJECT

public:
setRasterOpTest(QWidget *parent = 0, Qt::WFlags flags = 0);
~setRasterOpTest();
private:
Ui::setRasterOpTestClass ui;
QPoint firstPoint;
QPoint oldPoint;
bool prevRubberband;
protected:
void mousePressEvent( QMouseEvent *e );
void mouseReleaseEvent( QMouseEvent *e );
void mouseMoveEvent( QMouseEvent *e );
void paintEvent( QPaintEvent *e );
};

//.cpp

#include "setrasteroptest.h"
#include <QMouseEvent>
#include <QPainter>

//Program to draw a straight line using Qt4
//constructor
setRasterOpTest::setRasterOpTest(QWidget *parent, Qt::WFlags flags)
: QFrame(parent, flags)
{
ui.setupUi(this);
QColor color( Qt::white );
QPalette palette;
palette.setColor(backgroundRole(), color);
setPalette(palette);
setMouseTracking(true);
}

setRasterOpTest::~setRasterOpTest()
{}

//Press mouse and remember firstPoint and oldPoint
void setRasterOpTest::mousePressEvent( QMouseEvent *e )
{
if (e->button() == Qt::LeftButton) {
firstPoint = e->pos();
oldPoint = e->pos();
}
}

//release mouse function
//If mouse is released on left button pressed
//draw a line in red color to whever the mouse is released
void setRasterOpTest::mouseReleaseEvent( QMouseEvent *e )
{
if (e->button() == Qt::LeftButton ) {
QPainter paint;
paint.begin( this );
paint.setPen(QPen(Qt::red, 2));
oldPoint = e->pos();
paint.drawLine(firstPoint, oldPoint);
paint.end();
update();
}
}

//mouse move event
//Perform rubber banding
void setRasterOpTest::mouseMoveEvent( QMouseEvent *e )
{
//draw the line in xor mode (rubberbanding)
QPainter painter;
painter.begin( this );
painter.setPen(QPen(Qt::black, 2));

//setRasterOp not defined in Qt4.
//painter.setRasterOp (NotXorROP);
// first draw over the previously drawn line and
// erase it (in NotXorROP mode)
if(prevRubberband){
painter.drawLine(firstPoint, oldPoint );
}
oldPoint = e->pos();
painter.drawLine( firstPoint, oldPoint );
painter.end();
prevRubberband = true;
update();
}
void setRasterOpTest::paintEvent( QPaintEvent *e )
{
}

Thanks for your understanding

wysota
29th March 2007, 06:18
I think the code is broken as calling update() from mouseMove when using a raster op would not make any sense.... Anyway:


void setRasterOpTest::mousePressEvent *e){
m_firstpt = e->pos();
m_lastpt = e->pos();
}
void setRasterOpTest::mouseMoveEvent( QMouseEvent *e )
{
m_lastpt = e->pos();
update();
}
void setRasterOpTest::mouseReleaseEvent(QMouseEvent *e){
m_firstpt = QPoint();
m_lastpt = QPoint();
update();
}
void setRasterOpTest::paintEvent(QPaintEvent *e){
//... (do regular stuff here)
if(!m_firstpt.isNull()){
painter.save();
painter.setPen(Qt::black);
painter.drawLine(m_firstpt, m_lastpt);
painter.restore();
}
}

vermarajeev
29th March 2007, 08:38
Wow!!!

The code written has reduced a lot and very efficient too. Now I'm getting why trolltech decided to make such a change. Awesome!!!!

I have one doubt from above code. When I draw a line it disappears once the mouse it released. What I want is, the line has to appear after the mouse is released such that I can draw two or more lines.

I tried setting some boolean values, but didnt help.

Thanks!

wysota
29th March 2007, 10:48
Change the two QPoint members into a list or vector of points and don't null out the vector on mouse release. Then in the paint event iterate over the list and draw all the points.

vermarajeev
29th March 2007, 13:01
Change the two QPoint members into a list or vector of points and don't null out the vector on mouse release. Then in the paint event iterate over the list and draw all the points.

SOmething like this. But unlucky


void setRasterOpTest::mousePressEvent( QMouseEvent *e ){
m_vector<<e->pos();
}

void setRasterOpTest::mouseMoveEvent( QMouseEvent *e ){
m_vector<<e->pos();
update();
}

void setRasterOpTest::mouseReleaseEvent(QMouseEvent *e){
m_vector<<e->pos();
update();
}

void setRasterOpTest::paintEvent(QPaintEvent *e){
//... (do regular stuff here)

if( m_vector.count() <= 0 )
return;

QPainter painter;
painter.begin(this);

painter.setPen( Qt::black );
painter.drawLines ( m_vector );
painter.end();
}

Also let me explain, I dont want a continuous flow of line as in scribble example but only straight line. I start from a point and when I release the mouse a straight line should be drawn. Then again I click on some other point and when I release the mouse another straight line should be drawn.

Thanks for your understanding

vermarajeev
30th March 2007, 04:12
Hi wysota!,
Can you please help me with this???

Waiting for a reply!!!!!!

vermarajeev
30th March 2007, 05:12
The below solution draws only a single line. How to draw two or more lines???


void setRasterOpTest::mousePressEvent( QMouseEvent *e ){
if (e->button() == Qt::LeftButton) {
m_firstpt = e->pos();
m_lastpt = e->pos();
}
}

void setRasterOpTest::mouseMoveEvent( QMouseEvent *e ){
if ((e->buttons() & Qt::LeftButton) ){
m_lastpt = e->pos();
update();
}
}

void setRasterOpTest::mouseReleaseEvent(QMouseEvent *e){
if (e->button() == Qt::LeftButton ) {
m_lastpt = e->pos();
update();
}
}

void setRasterOpTest::paintEvent(QPaintEvent *e){
QPainter painter(this);
painter.setPen( Qt::black );
painter.drawLine(m_firstpt, m_lastpt);
}

vermarajeev
30th March 2007, 06:02
One more solution, Now this draws so many lines.. I'm trying and let you know if I get it...
Here is the program...


void setRasterOpTest::mousePressEvent( QMouseEvent *e ){
if (e->button() == Qt::LeftButton) {
m_firstpt = e->pos();
m_lastpt = e->pos();
}
}

void setRasterOpTest::mouseMoveEvent( QMouseEvent *e ){
if ((e->buttons() & Qt::LeftButton) ){
m_lastpt = e->pos();
update();
}
}

void setRasterOpTest::mouseReleaseEvent(QMouseEvent *e){
if (e->button() == Qt::LeftButton ) {
m_lastpt = e->pos();
update();
}
}

void setRasterOpTest::paintEvent(QPaintEvent *e){
QPainter painter(this);
painter.setPen( Qt::black );
m_vectorLine << QLine( m_firstpt, m_lastpt );
painter.drawLines( m_vectorLine );
}

If you get it before please let me know...
Thanks

wysota
30th March 2007, 11:02
Sorry, I've been unavailable.

Maybe you should change your concept a little and make it so that you click and drag with left mouse button and when you want to finish a line segment, you click with right mouse button.

It would look more or less like so:

void setRasterOpTest::mousePressEvent *e){
if(e->button()==Qt::LeftButton){
m_list.clear();
m_list << e->pos();
m_lastpt = e->pos();
} else if(e->button()==Qt::RightButton){
m_list << e->pos();
m_lastpt = e->pos();
}
}
void setRasterOpTest::mouseMoveEvent( QMouseEvent *e )
{
m_lastpt = e->pos();
update();
}
void setRasterOpTest::mouseReleaseEvent(QMouseEvent *e){
if(e->button()==Qt::LeftButton()){
if(m_list.size()==1 && (e->pos()-m_list[0]).manhattanLength()<5){
m_list.clear();
update();
return;
}
m_list << e->pos();
m_lastpt = QPoint();
update();
}
}
void setRasterOpTest::paintEvent(QPaintEvent *e){
//... (do regular stuff here)
if(!m_list.isEmpty()){
painter.save();
painter.setPen(Qt::black);
painter.drawLines(m_list);
painter.setPen(Qt::red);
painter.drawLine(m_list[m_list.size()-1], m_lastpt);
painter.restore();
}
}


EDIT: A complete working example is attached.

vermarajeev
30th March 2007, 11:36
Hi wysota,
My application is something like QPainter in MS where you can draw different kind of molecules (in chemistry term). The product is now to be ported to qt4. If I change the functionality (which is not a good idea) of drawing operations, I'll have tough time with my client.

The above sample program is the first step (where I should be able to draw two or more straight lines) to move forward.

I think you can understand my problem so please help me to achieve the same functionality as setRasterOp in qt3 performs.

Waiting eagerly for a reply....

vermarajeev
30th March 2007, 11:39
How about Uwe's idea...Will that help????

vermarajeev
30th March 2007, 11:50
One more thing...
I checked the output for example you have given. It is not that I want.

Your case:
You draw a line with left mouse button pressed, it draws a red line, then you press the right mouse button, It draws a continous line.

My case:
You click at a point start dragging and release the mouse button. A straight line should be drawn. Then again if I mousePress at some other point another line has to be drawn with the previous one shown.


Hope I'm clear!!

Thanks for your understanding

wysota
30th March 2007, 12:09
Oh... I see... well, this is even easier. Just make a vector of QLine and add a line composed of m_firstPt and m_lastPt on mouse release and don't clear the vector on mouse press. The rest should remain the same as my first example.

Uwe was speaking about more or less the same thing [qtclass]QRubberBand[qtclass] does. It won't be useful in your case.

vermarajeev
30th March 2007, 14:01
And here is the code that I want....


#include "setrasteroptest.h"
#include <QMouseEvent>
#include <QPainter>

//Program to draw a straight line using Qt4
//constructor
setRasterOpTest::setRasterOpTest(QWidget *parent, Qt::WFlags flags)
: QFrame(parent, flags)
{
ui.setupUi(this);
QColor color( Qt::white );
QPalette palette;
palette.setColor(backgroundRole(), color);
setPalette(palette);
setMouseTracking(true);
m_bflag = false;
}

setRasterOpTest::~setRasterOpTest()
{}

void setRasterOpTest::mousePressEvent( QMouseEvent *e ){
if (e->button() == Qt::LeftButton){
m_firstpt = e->pos();
m_lastpt = e->pos();
}
}

void setRasterOpTest::mouseMoveEvent( QMouseEvent *e ){
if ((e->buttons() & Qt::LeftButton) ){
m_lastpt = e->pos();
m_bflag = true;
update();
}
}

void setRasterOpTest::mouseReleaseEvent(QMouseEvent *e){
if (e->button() == Qt::LeftButton ) {
m_lastpt = e->pos();
m_vectorLine << QLine( m_firstpt, m_lastpt );
update();
}
}

void setRasterOpTest::paintEvent(QPaintEvent *e){
QPainter painter(this);
painter.setPen( Qt::black );
painter.drawLines( m_vectorLine );

if( m_bflag ){
painter.drawLine( m_firstpt, m_lastpt );
m_bflag = false;
}
}

Thanks wysota!!!!

Uwe
30th March 2007, 14:36
I don't know the details of your applications, but in general there are two types of lines. One that is supporting an interaction ( what is often called a rubberband ), the other one is a permanent line - the result of the interaction. Raster operations (XOR mode) are only useful for the first type of line (dragging), so I´m pretty sure, that your code is about rubberbanding - even if you are not aware about this yet.

The trick about painting in XOR mode is, that painting the same thing twice, erases the first painting. This makes it possible to drag an object, without having to repaint the content of the widget. With Qt4 you always (!) have to repaint the content to erase the previous position of the line - and in many environments, you will see that the dragged object can't follow mouse movements in time.

Implementing rubberbands (dragging) without raster operations leads to very different code with worse results. This is something you and your client have to accept. The only choice you have is if you want to merge the painting code of rubberband and content (that´s what the Qt widgets do), or if you isolate the painting code of the rubberband to an masked 2. widget, where changes of the mask lead to paint events for the content widget. The main advantage of the second implementation is that you can implement it once and can use it with any type of content widget - that´s what f.e. QwtPicker is about.

Uwe

By the way: QRubberBand is a styled line/rect, but absolutely no replacement for the XOR mode.

wysota
30th March 2007, 15:14
What is the m_bflag for? No matter what it is, you use it incorrectly. You surely shouldn't zero the flag in a paint event.

vermarajeev
31st March 2007, 07:16
What is the m_bflag for? No matter what it is, you use it incorrectly. You surely shouldn't zero the flag in a paint event.

Hi wysota,
The m_bflag helps me to see the line on mouseMove. Without this the line is not visible when I move the mouse.

I have attached the complete solution.

Let me know if I'm doing anything wrong.

Thanks

wysota
31st March 2007, 13:13
What happens if you drag a line and suddenly some window from another application pops up obscuring a part of your widget? A paint event will come and as your flag will be set to false by the previous paint event, the "dynamic" line will not be redrawn. Instead you should clear the flag in mouseReleaseEvent. And don't use the flag - just clear m_lastPt - it'll return true on QPoint::isNull() call. Then it's only a matter of checking that point in the paint event and drawing the line only if isNull() returns false. Just look at one of my previous examples.