PDA

View Full Version : QPaint over Qlabel ?



eth0
13th May 2010, 21:23
Hi there,
I am new to QT Programming so I realize this problem seem rather stupid. I apologize in advance.

So basically I am trying to draw on an image using QPainter.
I have a QLabel on a QScrollArea:



imageLabel = new QLabel;
scrollArea = new QScrollArea;
scrollArea->setWidget(imageLabel);

after which I go ahead and load an image as a pixmap:



imageLabel->setPixmap(QPixmap::fromImage(image));

Now I have tried creating a function:


void Planning::paintNodes(QPaintEvent *){QPainter paintme(this);
paintme.drawLine(QLineF(10.0, 80.0, 90.0, 20.0));
}

In addition, i tried creating an action like so:

drawNodesAct = new QAction(tr("Draw &Nodes"),this);drawNodesAct->setShortcut(tr("Ctrl+N"));drawNodesAct->setEnabled(false);connect(drawNodesAct, SIGNAL(triggered()), this, SLOT(paintNodes(image.rect())));

and I try to call paintNodes from another function.
I am a little confused as to what I should define the parameter 'QPaintEvent *' to pass in as?
I read up a little and it said I should pass a QRect. How would I be able to get this code to work to merely draw a line on the loaded image?

I'm working on my senior design for electrical engineering and trying to create a GUI for our A* Planning algorithm.
Any help will be appreciated!

tbscope
13th May 2010, 21:29
What kind of object is Planning?
A widget, or a picture/pixmap/image?

If it is a widget, is paintNodes being called from withint the paintEvent ?
If not: check out this warning:


Warning: When the paintdevice is a widget, QPainter can only be used inside a paintEvent() function or in a function called by paintEvent(); that is unless the Qt::WA_PaintOutsidePaintEvent widget attribute is set. On Mac OS X and Windows, you can only paint in a paintEvent() function regardless of this attribute's setting.

Lykurg
13th May 2010, 21:31
I am a little confused as to what I should define the parameter 'QPaintEvent *' to pass in as?Then why do you define such an argument? There is no need for it.

EDIT: And you can't pass values in a connect statement!

eth0
13th May 2010, 22:36
Hi,
Thanks for the quick response.
Plannin inherits QMainWindow:


class Planning : public QMainWindow

I have changed the action so that it does not pass a parameter.
I have also realized that to trigger the paintEvent I need to QWidget::repaint():


void Planning::paintNodes()
{
PaintNodes=true;
QWidget::repaint();
}

void Planning::paintConnections()
{
PaintConnections=true;
QWidget::repaint();
}

My paintEvent is as follows:

void Planning::paintEvent(QPaintEvent *event)
{
QPainter paintme(this);
paintme.drawLine(QLineF(10.0, 80.0, 90.0, 20.0));
}

and it is defined in the header as such:

protected:
void paintEvent(QPaintEvent * event);

Now the program seems to compile but the line does not get drawn. It does not draw on the base window or even after loading of an image as a background pixmap.
Any thoughts?

eth0
13th May 2010, 22:42
Oh I forgot to mention,
Here is the constructor for planning and the central widget is a QScrollArea:


Planning::Planning()
{
//glWidget = new GLWidget;
imageLabel = new QLabel;
imageLabel->setBackgroundRole(QPalette::Base);
imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
imageLabel->setScaledContents(true);

scrollArea = new QScrollArea;
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setWidget(imageLabel);
scrollArea->setMouseTracking(true);
setCentralWidget(scrollArea);

createActions();
createMenus();
statusBar()->showMessage(tr("Planning v1.0"));

setWindowTitle(tr("Planning"));
resize(640, 480);}

aamer4yu
14th May 2010, 06:10
Can you in simple words explain what you want to achieve ?

You seem to be complicating things over simple requirements.

Also take some time to check the Qt Demos,,am sure you will find something related to you, and will help you better understanding how painting in Qt works.
Then only you will be able to get proper help from here,,, else you may not learn if solution is directly given to you

eth0
14th May 2010, 06:20
Hi,
Basically I am writing a gui interface to do path planning. We have a robot that autonomously navigates using gps, computer vision and basic microcontroller counts. The planning software should basically load an image of my school (be able to pan and zoom), draw points (i.e. nodes) and connections between the nodes. I am familiar with opengl, but cant seem to integrate into properly with qt4, so i figured i'd try to use qtpaint to do the same. When I load the image now I cant seem to draw on it. And trust me, I am an EE major, I have spent the better part of the last two weeks trying to figure out this triviality. This is why I need the help =(
I am attaching an image for a better idea.
http://img684.imageshack.us/img684/3876/screenshothcf.png

tbscope
14th May 2010, 06:30
The painting should be done in your label or glwidget, or any other widget that is actually displaying what you want.
Now you paint in the mainwindow and put a scrollview with label on top of it, that's why you do not see anything

aamer4yu
14th May 2010, 06:32
Ok fine... got the idea and you background...
I guess using QGraphicsView will be best for your case, since zooming is very easy in that.
Now coming to drawing points, you will need to override mousePressEvent of the view, maintain a list of the points,,, and then draw those points in the view's paintEvent or you may even add those lines as items in the scene.

To make sense what I am talking, have a look at Qt Demos-> Graphics View -> Diagram Scene example.. you will get an idea how to draw a line and make it movable.
Once you get to know how to draw lines... you can do something like -


mousePressEvent()
{
startPoint = store point clicked;
}

paintEvent()
{
draw line from startPoint to current mouse point.
}

mouseDoubleClicked()
{
stop drawing
}

Hope you get the idea :)

eth0
17th May 2010, 00:27
Thanks for the ideas! I appreciate it.
aamer4yu, I have implemented your mousePressEvent idea, thanks!

tbcope, you said:

The painting should be done in your label or glwidget
I would like to futher this idea.
How exactly would I pass the paint event to the label?
Currently I have:

void Planning::paintEvent(QPaintEvent *event)
{
QPainter paintme(this);
paintme.drawLine(QLineF(10.0, 80.0, 90.0, 20.0));
}

I tried adding the following to the end of the paintEvent, but it doesn't seem to work:

QLabel::paintEvent(event);

How exactly would I pass the paint operation to take place on the QLabel so that it appears on the pixmap?

tbscope
17th May 2010, 06:00
Subclass a label widget (for example).
In your subclass, handle the paintEvent, like you did for your main window. Do not draw lines on your label from within the main window paint event.
If you want to paint on a widget, use the paint event of that exact widget.

Create properties or get/set functions to set or get new points or lines.
Then, whenever you set a new point or line, update or repaint that part of the label.

eth0
19th May 2010, 18:30
I wish my c++ skills were better.
This is what I understood from what you said:


class Label : public QLabel
{
Q_OBJECT

protected:
void paintEvent(QPaintEvent *);
};

class Planning : public QMainWindow
{
Q_OBJECT
public:
<other non-essential code>
Planning();
Label *imageLabel;
QScrollArea *scrollArea;

QPrinter printer;
};

#endif

This however results in:

planning.h:20: error: invalid use of incomplete type ‘struct QLabel’
planning.h:13: error: forward declaration of ‘struct QLabel’


I apologise in advance for my lack of knowledge about this matter.

tbscope
19th May 2010, 18:35
There's a lot wrong I'm afraid.
I'll write you an example, but that will take a couple of minutes.
Nevertheless, I suggest reading a little bit about C++ and classes.
The error you get is because you didn't include QLabel in your code.

eth0
19th May 2010, 18:49
I appreciate all your help tbscope!

eth0
19th May 2010, 18:56
So I added the following in the header file:

#include <QLabel>

and changed the paintfunction in the cpp file as such:

void Label::paintEvent(QPaintEvent *event)
{
QPainter paintme(this);
paintme.drawLine(QLineF(10.0, 80.0, 90.0, 20.0));
}

It does compile now, but seg-faults instantly.
An example would be greatly appreciated!
Thanks.

tbscope
19th May 2010, 19:01
Here's a quick example.
This is just to demonstrate how you can create a custom label and paint on top of the existing content.

I created a new standard gui project with creator.
Then added two new classes:
1. Planning, based on QScrollArea
2. CustomLabel, based on QLabel

I added the label to the scrollarea from within the Planning class itself. To me this seems logical as I guess the Planning class will tell the CustomLabel what to do.
If not, you can add the CustomLabel to the Planning scroll area from within your main window too, if that makes it easier for you.

mainwindow.h



#include <QMainWindow>

#include "planning.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();

protected:
void changeEvent(QEvent *e);

private:
Ui::MainWindow *ui;

Planning *myPlanning;
};

#endif // MAINWINDOW_H

mainwindow.cpp


#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QGridLayout>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

QGridLayout *layout = new QGridLayout;

myPlanning = new Planning(this);
layout->addWidget(myPlanning);

ui->centralWidget->setLayout(layout);
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}

planning.h


#ifndef PLANNING_H
#define PLANNING_H

#include <QWidget>
#include <QScrollArea>

#include "customlabel.h"

class Planning : public QScrollArea
{
Q_OBJECT
public:
explicit Planning(QWidget *parent = 0);

signals:

public slots:

private:
CustomLabel *customLabel;
};

#endif // PLANNING_H

planning.cpp


#include "planning.h"
#include <QPixmap>

Planning::Planning(QWidget *parent) :
QScrollArea(parent)
{
customLabel = new CustomLabel(this);
setWidget(customLabel);

QPixmap pixmap(...); // use your pixmap here
customLabel->setPixmap(pixmap);
customLabel->resize(pixmap.size());
}


customlabel.h


#ifndef CUSTOMLABEL_H
#define CUSTOMLABEL_H

#include <QLabel>

class CustomLabel : public QLabel
{
Q_OBJECT
public:
explicit CustomLabel(QWidget *parent = 0);

signals:

public slots:

protected:
void paintEvent(QPaintEvent *);
};

#endif // CUSTOMLABEL_H


customlabel.cpp


#include "customlabel.h"
#include <QPainter>
#include <QPaintEvent>
#include <QPoint>
#include <QPen>

CustomLabel::CustomLabel(QWidget *parent) :
QLabel(parent)
{
}

void CustomLabel::paintEvent(QPaintEvent *event)
{
// Assuming you want to paint on top of the existing content of the label.
// First draw the label
QLabel::paintEvent(event);

QPainter painter(this);
QPoint start(0,0);
QPoint end(100,100);
QPen pen(qRgb(255,0,0));
painter.setPen(pen);
painter.drawLine(start, end);
}

Off course you want to rename the CustomLabel class to something more appropriate.
And you want to create some setters and getters to set and get data. Maybe create a list to keep track of data when updating and painting etc...


EDIT:
Ohh, I used a jpg I have on my computer in the code, I edited that out.

eth0
22nd May 2010, 23:25
Thank you soo much!
Perfectly what I need.