views:

233

answers:

3

Say there is a QPushButton named "Draw", a QLineEdit and a QFrame. On clicking the button I want to take a number from QLineEdit and draw a circle in a QFrame. How can I do this? Please provide me with the code.

P.S. The problem is that draw methods of the QPainter should be called in drawEvent method.

+1  A: 

You don't draw diectly onto a frame.
Start here graphicsview, it looks complicated at first - but GUI program is a big leap when you first encounter it

In most GUIs (Qt, OpenGL etc) you build up a list of elements you want to draw in your program and store them somehow - then there is a draw() function that gets called when the computer needs to draw your picture - eg when it is moved or another window is moved in front of it. The OnDraw or OnRepaint etc function then gets called and you have to draw the list of objects.

Another way to do this is to draw them all to an image (QOimage or QPixmap) and copy that to the screen in OnDraw or OnRepaint - you might do this for a graphics package for example.

Martin Beckett
"You don't draw directly onto a frame." Why not?
Troubadour
"The QFrame class is the base class of widgets that can have a frame"
Martin Beckett
You can still draw directly onto a `QFrame` or any subclass thereof. See [my anwser](http://stackoverflow.com/questions/3309708/qt-draw-in-a-qframe-on-clicking-a-button/3310728#3310728) for a working example.
Troubadour
ok, but should you? Rather than use a graphics view?
Martin Beckett
I see that QGraphicsView and QGraphicsScene is what I need, but I can't place a text in a QGraphicsScene where I want. addText() method of that class has no argumen that specipies coordinates. How can I do this?
Narek
@Martin Beckett: That's what _I'm_ asking _you_ to justify. I used to work on a Qt application that was written well before `QGraphicsView` existed and we never bothered to port across to it as everything worked just fine. `QGraphicsView` is a nice new part of the GUI but I wouldn't say it was mandatory.
Troubadour
@Troubadour - my mistake, I thought it pretty much was mandatory if you weren't trying to do some odd effect. I tend to just draw into OpenGL widget mostly.
Martin Beckett
+3  A: 

If you want your frame to do the drawing, then it needs a way to know that it should draw something, so create a slot that will receive notification:

/* slot */ void drawCircle(QPoint origin, int radius) {
    addCircle(origin, radius);
    update(); // update the UI
}

void addCircle(QPoint origin, int radius) {
    circleList.add(new Circle(origin,radius));
}

Then, your frame subclass you need to override paintEvent() to draw the circle:

void paintEvent(QPaintEvent *event) {
    QFrame::paintEvent(event);
    QPainter painter(this);
    foreach (Circle c, circleList) { // understand foreach requirements
        painter.drawEllipse(c.origin(), c.radius(), c.radius());
    }
}

As long as the slot responding to the button's clicked() signal emits a signal that calls the drawCircle slot with the correct arguments everything should work correctly.

Kaleb Pederson
+3  A: 

If @Kaleb Pederson's answer is not enough for you then here's a complete solution for a simple set-up matching what you describe. Tested with Qt 4.5.2 on Linux. I had some spare time... ;)

main.cpp:

#include <QApplication>
#include "window.h"

int main( int argc, char** argv )
{
    QApplication qapp( argc, argv );

    Window w;
    w.show();

    return qapp.exec();
}

window.h

#pragma once

class QLineEdit;
class QPushButton;
#include <QWidget>

class Frame;

class Window : public QWidget
{
Q_OBJECT

public:
    Window();

private slots:
    void onButtonClicked();

private:
    QLineEdit*   m_lineEdit;
    QPushButton* m_pushButton;
    Frame*       m_frame;
};

window.cpp:

#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>

#include "frame.h"
#include "window.h"

Window::Window()
    : m_lineEdit  ( new QLineEdit( this ) )
    , m_pushButton( new QPushButton( tr( "Draw" ), this ) )
    , m_frame     ( new Frame( this ) )
{
    connect( m_pushButton, SIGNAL( clicked() )
           , SLOT( onButtonClicked() ) );

    QHBoxLayout*const hLayout = new QHBoxLayout;
    hLayout->addWidget( m_lineEdit );
    hLayout->addWidget( m_pushButton );

    QVBoxLayout*const vLayout = new QVBoxLayout( this );
    vLayout->addLayout( hLayout );
    m_frame->setFixedSize( 300, 400 );
    vLayout->addWidget( m_frame );

    setLayout( vLayout );
}

void Window::onButtonClicked()
{
    const int r = m_lineEdit->text().toInt(); // r == 0 if invalid
    m_frame->setCircleRadius( r );
    m_frame->update();
}

frame.h:

#pragma once

#include <QFrame>

class Frame : public QFrame
{
Q_OBJECT

public:
    Frame( QWidget* );

    void setCircleRadius( int );

protected:
    void paintEvent( QPaintEvent* );

private:
    int m_radius;
};

frame.cpp:

#include <QPainter>

#include "frame.h"

Frame::Frame( QWidget* parent )
    : QFrame( parent )
    , m_radius( 0 )
{
    setFrameStyle( QFrame::Box );
}

void Frame::setCircleRadius( int radius )
{
    m_radius = radius;
}

void Frame::paintEvent( QPaintEvent* pe )
{
    QFrame::paintEvent( pe );

    if ( m_radius > 0 )
    {
        QPainter p( this );
        p.drawEllipse( rect().center(), m_radius, m_radius );
    }
}
Troubadour
This is greate that you have done all this. Thank you so much!!!
Narek