PDA

View Full Version : Painting problem



Mel
8th May 2007, 16:05
I'm new to C++ and Qt. I've been trying to port some code from an app
using Qt 3 to Qt 4. I'm currently using Ubuntu and Qt 4.2.3. The code
below runs under Qt 3, but not Qt 4. The problem occurs in the drawPBS
method in the Main_Widget class when trying to paint pbs. All I get is the error message:
"QPainter::begin: Widget painting can only begin as a result of a
paintEvent."

I'm sure I'm missing something fundamental, but just haven't been able
to find it. Any pointers (no pun intended) would be greatly
appreciated.

This is my first post, so if I've sent too much information, let me
know.

Cheers, Mel

Code follows:
.................................................. ..................

#include <qapplication.h>
#include <qfontdatabase.h>
#include "main_widget.h"


int main (int argc, char **argv)
{
QApplication app(argc, argv);
Main_Widget *w = new Main_Widget;;
app.setStyleSheet("QFrame {border : 1px solid rgb(255,200,55)}");
w->show();
return app.exec();
}
#ifndef SDXCVR_MAINWIDGET_H
#define SDXCVR_MAINWIDGET_H

#include <qwidget.h>
#include <qapplication.h>
#include <qfont.h>
#include <qlistview.h>
#include <QLabel>
#include <qfile.h>
#include <qpainter.h>
#include <QPaintDevice>
#include <qimage.h>
#include <qdir.h>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPalette>
#include <QFrame>

#include "pbs.h"
#include "sm.h"

class Main_Widget : public QWidget
{


Q_OBJECT

private:

Sm *sm;
PBS *pbs;
QFrame *sFrame;

void updateLayout();
void drawPBS();

public:
Main_Widget(QWidget *parent = 0);

protected:

void paintEvent( QPaintEvent * );


};
#endif

#include "main_widget.h"

Main_Widget::Main_Widget(QWidget *parent)
: QWidget(parent)
{

setFocusPolicy( Qt::TabFocus );
setPalette(QPalette(QColor(0, 0, 0)));
setAutoFillBackground(true);
setMinimumWidth( 650 );
setMinimumHeight( 300 );

setWindowTitle(" Test " );

// sFrame

sFrame = new QFrame( this );
sFrame->setFrameStyle( QFrame::StyledPanel | QFrame::Plain );

// sm
sm = new Sm( sFrame );
sm->setPalette(QColor(20, 20, 50) );
sm->setAutoFillBackground(true);

// PBS
pbs = new PBS( sFrame );
QPalette r(pbs->palette());
r.setColor(QPalette::Background, QColor(100, 0, 0));
pbs->setPalette(r);
pbs->setAutoFillBackground(true);

}

void Main_Widget::paintEvent( QPaintEvent * )
{
printf("paintEvent() \n");

updateLayout();
drawPBS();

}

void Main_Widget::updateLayout()
{
printf("updateLayout() \n");

sFrame->setGeometry(
1,
35,
width() - 2,
height() - 18 - 36 );

sm->setGeometry(
2,
2,
sFrame->width() - 4,
sFrame->height() - 4 - 120 - 15 );

pbs->setGeometry(
2,
sm->height() + 2,
sFrame->width() - 4,
15 );
printf("updateLayout() pbs Geometry \n");
printf(" 2, %d, %d, 15\n", sm->height() + 2, sFrame->width() - 4);
}



void Main_Widget::drawPBS()
{

QPainter p;
p.begin(pbs);
p.eraseRect( 0, 0, pbs->width(), pbs->height() );
p.setPen( Qt::yellow );
p.drawLine(252, 50, 252, 190);
p.drawLine(4,115,70,115);
p.end();

}
#ifndef SDXCVR_PBS_H
#define SDXCVR_PBS_H

#include <qwidget.h>
#include <QMouseEvent>
#include <QPainter>
#include <QPaintDevice>


class PBS : public QWidget
{
Q_OBJECT

public:

PBS(QWidget *parent = 0);

private:
int x0;

protected:
void mousePressEvent( QMouseEvent * );
void mouseMoveEvent( QMouseEvent * );

signals:
void set_lower_pb( int );
void set_upper_pb( int );
void movement( int );
};
#endif

#include "pbs.h"
#include "main_widget.h"

PBS::PBS(QWidget *parent) : QWidget(parent)
{
setMouseTracking( true );
}

void PBS::mouseMoveEvent( QMouseEvent *e )
{
emit movement( e->x() );
}

void PBS::mousePressEvent( QMouseEvent *e )
{
x0 = e->x();

if ( e->button() == Qt::LeftButton )
emit set_lower_pb( x0 );
if ( e->button() == Qt::RightButton )
emit set_upper_pb( x0 );
}
#ifndef SDXCVR_SM_H
#define SDXCVR_SM_H

#include <qwidget.h>
#include <QMouseEvent>

class Sm : public QWidget
{
Q_OBJECT

public:
;
Sm(QWidget *parent = 0);
private:
int mouseMoving;

protected:
void mouseReleaseEvent( QMouseEvent * );
void mouseMoveEvent( QMouseEvent * );

signals:
void tune1( int );
void tune2( int );
void plot( int );
void movement( int );
};
#endif
#ifndef SDXCVR_SM_H
#define SDXCVR_SM_H

#include "sm.h"


Sm::Sm(QWidget *parent) : QWidget(parent)
{
//setMouseTracking( true );

mouseMoving = 0;
}

void Sm::mouseReleaseEvent( QMouseEvent *e )
{
if ( !mouseMoving && e->button() == Qt::LeftButton )
emit tune1( e->x() );

if ( !mouseMoving && e->button() == Qt::RightButton )
emit plot( e->y() );

mouseMoving = false;
}

void Sm::mouseMoveEvent( QMouseEvent *e )
{
static int x0 = 0;
int output;

mouseMoving = true;

if ( x0 - e->x() >= 0 )
output = 1;
else
output = -1;

if ( e->button() == Qt::LeftButton )
emit tune2( output );
else if ( e->button() == Qt::RightButton )
emit tune2( output * 10 );
else if ( e->button() == Qt::MidButton )
emit tune2( output * 100 );
else
emit movement( e->x() );

x0 = e->x();
}

high_flyer
8th May 2007, 16:40
The code
below runs under Qt 3, but not Qt 4. The problem occurs in the drawPBS
method in the Main_Widget class when trying to paint pbs.
I am surprised it compiles at all!
Mixing Qt3 and Qt4 is BAD!
Specially when dealing with painting, since painting (among other things) has been drastically changed in Qt4, one of the changes is that painting in Qt4 is only done in paintEvent() - which will explain the error you are getting.

You should port your code to Qt4 - proper!

marcel
8th May 2007, 16:50
You have a big problem here:
You're trying to paint on a widget from another widget. This will never work in Qt4( did it really work in 3? ).

Why don't you move the code from Main_widget::paintEvent in PBS::paintEvent()? It should be no problem, practically the coordinates will be the same.


Another issue is updateLayout. You shouldn't reposition widgets in a paint event (don't you get any flicker? ). Instead you should move them in mouseMoveEvent(), and let them paint themselves.

To do this you have a "little" redesigning to do. Anyway, once you get started, you can ask here if you run in any trouble. But you really should rewrite.

Regards

Mel
8th May 2007, 17:40
Thanks to all for the replies.

The code is compiled in Qt 4 only.

Marcel,

I had actually created a paintEvent in the PBS class before posting my question, but it didn't work either. The code is below. Nothing is painted when this event is called either, but at least I don't get the original error message. The program I'm trying to port works quite nicely in Qt 3, and displays both spectrum and waterfall displays. I've never noticed any flickering. I have no problems with rewriting some of the code, if I can ever get the painting problem solved. Seems like I'm still missing something here ...

Note that nothing is painted anywhere on the screen when the paintEvent in PBS is executed.

Code follows ....................................

#include "pbs.h"
#include "main_widget.h"

PBS::PBS(QWidget *parent) : QWidget(parent)
{
setMouseTracking( true );
}

void PBS::mouseMoveEvent( QMouseEvent *e )
{
emit movement( e->x() );
}

void PBS::mousePressEvent( QMouseEvent *e )
{
x0 = e->x();

if ( e->button() == Qt::LeftButton )
emit set_lower_pb( x0 );
if ( e->button() == Qt::RightButton )
emit set_upper_pb( x0 );
}
void PBS::paintEvent( QPaintEvent * )
{
printf("PBS paintEvent() \n");

QPainter p;
p.begin(this);
// p.eraseRect( 0, 0, this->width(), this->height() );
p.setPen( Qt::yellow );
p.drawLine(252, 50, 252, 190);
p.drawLine(4,115,34,115);
p.end();


}

marcel
8th May 2007, 17:49
Yes, I believe you don't see anything on the screen :)
This is because you are painting off-widget... You first must take a look at what rect() returns and draw everything relative to rect().topLeft and such that everything will fit in the widget rect.

My guess is that your widget(PBS, possibly the others) doesn't have a minimum size. You should reimplement minimumSize and sizeHint to return the minimum size and the preferred size for your widget(s).

Or, if you know these sizes, you can just set them in the widget constructor. Not setting them, your widget will have a size of (-1, -1).

What errors did you get?

Try getting the code between CODE tags, because it is very hard top read as simple text.


regards

Mel
8th May 2007, 19:36
Marcel,

Thanks for your patience.

I get no errors when running the last piece of code I sent; it just doesn't paint. I printed out this->width() and this->height just before the call to eraseRect, and it shows the size of pbs to be (2, 109, 644, 15). I removed the line that was clearly out of the print area, compiled, and ran it again. Still no painting.

WRT the size of the widgets, I would think that's set in updateLayout(). I didn't write the original code, so I'm not sure why the author chose to implement it that way.

I'll have many changes in arithmetic to make in the program, but I feel that if I can get the toy I posted to run I'll be able to conquer the rest.

Thanks.

Cheers,
Mel

marcel
8th May 2007, 19:40
Instead of drawing the lines try p.fillRect( rect(), Qt::red ) ;
See if anything is being painted now. Perhaps you need to set a width for the pen.

regards

Mel
8th May 2007, 20:43
Marcel,

p.fillRect( rect(), Qt::red ) ;

filled pbs with a bright red.

The pen width is set to 0, so a 1 pixel width line should be drawn.

I removed the background from pbs, but still no line.

Thus far the only thing I've been able to display on pbs is your fillRect suggestion.
I also tried
p.fillRect(4,115,34,6,Qt::red);
which should have filled a small rectangle, but nothing happened.

It's probably late in your area, but do you have any last suggestions for today?

Thanks again.

Cheers,
Mel

marcel
8th May 2007, 21:02
OK.
A painter draws in paint device (widget in this case ) local coordinates.
Meaning that when you used my previous suggestion, something got filled, but not the right thing :).

Thus, everything must be drawn relative to (0,0).
Calling p.fillRect( 0, 0, size().width(), size().height(), Qt:red ) will fill the ENTIRE widget.
For what you were trying to achieve call p.fillRect( 0, 0, 30, 6, Qt:red ). This will fill a small rect in the top left corner of the widget.

To be sure just do a drawRect( 0, 0, size().width(), size().height() ) - draw a frame around the widget.

If this works ( and it will http://www.qtcentre.org/forum/images/icons/icon12.png ), draw everything relative to (0, 0 ) - lines, etc.


It's probably late in your area, but do you have any last suggestions for today?
It's 11 PM, not so late.

Regards

Mel
8th May 2007, 21:36
Sigh ... Of course it's relative! I knew there was some fundamental point I was missing! I adjusted my numbers, and painted the rectangle and the line I was trying to paint.

I can't thank you enough for your help, Marcel. I have a lot of work still ahead, but your help has removed a big roadblock. I put my question out to this group, because they had advertised a 95% success rate in solving users' problems. Mine can certainly be added to the success list thanks to your efforts.

Thanks again for your time and patience.

Cheers,
Mel

marcel
8th May 2007, 21:38
Happy to be of some help!

Regards