Qt Начинающий QPainter и QRect

Как мне нарисовать прямоугольник?

Я пробовал два разных способа;

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    QRect rect = QRect(290, 20, 70, 40);
    painter.drawText(rect, Qt::AlignCenter,
                      "Data");
    painter.drawRect(rect);
}

Что отлично работает (хотя параметр не назван и не используется), но я не хочу использовать QPaintEvent * Мне это не нужно.

Поэтому я попытался просто переименовать свою функцию;

void MyWidget::draw()
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    QRect rect = QRect(290, 20, 70, 40);
    painter.drawText(rect, Qt::AlignCenter,
                      "Data");
    painter.drawRect(rect);
}

Это ничего не отображает (пока нет ошибок).

Почему это не сработает, если я не использую QPaintEvent * ??


person Ash    schedule 31.12.2012    source источник
comment
Почему вы ожидаете, что функция со случайным именем будет вызываться для каких-либо действий? Ваш первый метод в порядке. Отсутствие именования параметров, которые вам (пока) не нужны, допустимо и идиоматично C++.   -  person Mat    schedule 31.12.2012
comment
Хорошо, можно ли было бы заставить работать первый метод, если бы у меня не было параметра, но все остальное осталось прежним?   -  person Ash    schedule 31.12.2012
comment
Нет, этот параметр необходим независимо от того, используете вы его или нет. Я не понимаю, почему вы хотите удалить его - вам ничего не стоит его игнорировать.   -  person Mat    schedule 31.12.2012
comment
Это вызывает у меня проблемы позже в моем коде, у меня есть другие параметры, например const QString &data. Есть ли альтернативный способ рисования прямоугольника без использования параметров события? Спасибо за ваши ответы.   -  person Ash    schedule 31.12.2012
comment
Сигнатура для событийных функций фиксированная, изменить их вообще нельзя (кроме, возможно, добавления параметров со значениями по умолчанию, но я бы тоже не стал этого делать, даже если бы это работало). Вам нужно более подробно описать вашу проблему в вашем вопросе, похоже, вы пытаетесь сделать что-то не так.   -  person Mat    schedule 31.12.2012
comment
У меня есть detailsForm.cpp, где пользователь вводит данные пакета. Когда это отправляется в mainwindow.cpp, детали пакета отображаются на новой вкладке. У меня все работало нормально, когда я просто использовал QTextEdit. Я хотел изменить это на QGraphicsView и отображать детали пакета внутри прямоугольников, чтобы пакет выглядел более реалистично. Вы правы насчет того, что я поступаю неправильно. Я просто бездельничаю, пытаясь разобраться.   -  person Ash    schedule 31.12.2012
comment
Если вы переключаетесь с виджетов на использование qgraphics, то почему бы просто не использовать QGraphicsRectItem или подкласс графического элемента?   -  person jdi    schedule 01.01.2013


Ответы (5)


Событие рисования — это метод, который вызывается системой рисования, когда виджет необходимо перерисовать. Вот почему просто назвать собственный метод не получится. Он никогда не вызывается системой рисования.

Вы действительно должны использовать QPaintEvent. Это дает вам прямоугольник, который нужно нарисовать. Этот прямоугольник будет основан на размере виджета, поэтому вместо использования явного прямоугольника в событии рисования установите для виджета правильный размер. Событие рисования будет сгенерировано, если ваш виджет когда-либо переместится, изменит размер и т. д.

void MyWidget::paintEvent(QPaintEvent *event)
{
    QRect rect = event->rect();
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    painter.drawText(rect, Qt::AlignCenter,
                      "Data");
    painter.drawRect(rect);
}

Теперь, если вы хотите разделить свою логику рисования на другой метод, это нормально. Но вам нужно будет вызвать его из события рисования:

void MyWidget::paintEvent(QPaintEvent *event)
{
    QRect rect = event->rect();
    draw(rect);
}

void MyWidget::draw(QRect &rect)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    painter.drawText(rect, Qt::AlignCenter,
                      "Data");
    painter.drawRect(rect);
}

Если вы хотите полностью обойти события рисования, как вы сказали, и хотите создать только статический прямоугольник для отображения, один из способов - просто нарисовать его один раз в растровое изображение и отобразить в QLabel:

QPixMap pix(200,100);
QPainter painter(&pix);
// do paint operations
painter.end()
someLabel.setPixmap(pix)
person jdi    schedule 31.12.2012
comment
Вау, спасибо за советы :) ты звезда!! это именно то, что я ищу! - person Ash; 02.01.2013
comment
Хотя ваше первое рабочее решение выглядит элегантно, ваше решение на самом деле просто переводит область, унаследованную от события, и просто увеличивает вызов в стеке, пользователь всегда должен настраивать ваши данные вне функции, поэтому я думаю, что этот двойной вызов, нет быть полезным. Извините, что не согласен с вашим первым решением. Ваше второе простое решение выглядит хорошо. - person RTOSkit; 02.01.2013

Любые данные, которые нужны вашему paintEvent(), должны быть доступны как поля содержащего класса, в вашем случае, частные поля MyWidget. Эти закрытые поля могут быть открыты для клиентов MyWidget через «сеттеры», которые будут устанавливать значения данных перед вызовом update() на MyWidget, что вызовет вызов paintEvent().

person Arnold Spence    schedule 31.12.2012

Этот плейлист содержит лучшие учебные пособия по Qt , вам будет полезно начать руководство 74 (Qpainter и QPen), учебник 75 посвящен рисованию прямоугольников с помощью QRect.

person Ahmed Kato    schedule 31.12.2012
comment
Спасибо, я посмотрю. - person Ash; 31.12.2012

Кроме того, @Mat сказал вам: "событие" — это правильный способ запуска рисовальщика.
QPainter может быть вызван только после события QPaintEvent, которое переносит безопасную область, где объект можно рисовать.

Таким образом, вы должны найти другую стратегию для передачи ваших данных, чтобы помочь, я предложу простой метод, который можно приспособить ко многим случаям.

widget.cpp

#include <QtGui>
#include "widget.h"

#define MIN_DCX    (0.1)
#define MAX_DCX    (5.0)

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{    
    dcx=MIN_DCX;
    setFixedSize(170, 100);
}

void Widget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event); 
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    pcx=dcx*2;
    QRect rect = QRect(50-dcx,25-dcx,60+pcx,40+pcx);
    painter.drawText(rect, Qt::AlignCenter,printData);
    painter.drawRect(rect);
    painter.end();

}

void Widget::setPrintData(QString value){
   printData = value;
   dcx=(dcx>MAX_DCX)?MIN_DCX:dcx+MIN_DCX;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent);
    void setPrintData(QString value);

protected:
    void paintEvent(QPaintEvent *event);

private:
    QString printData;
    float dcx;
    float pcx;
};


#endif

window.cpp

#include <QtGui>
#include "widget.h"
#include "window.h"

#define MAX_SDCX  20

Window::Window()
    : QWidget()
{
    gobject = new Widget(this);

    textMode=1;
    rectMode=1;
    gobject->setPrintData(msgs[textMode]);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(gobject, 0, 0);
    setLayout(layout);

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(dataOnAir()));
    timer->start(10);

    setWindowTitle(tr("Rect Shaking"));
}



void Window::dataOnAir(){
    if((++rectMode)>MAX_SDCX){
        rectMode=0;
        textMode^=1;
    }
    gobject->setPrintData(msgs[textMode]);
    gobject->repaint();
}

window.h

#ifndef WINDOW_H
#define WINDOW_H

#include <QWidget>
#include "widget.h"

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private slots:
    void dataOnAir();

private:
    Widget *gobject;
    const QString msgs[2] = {"Hello","World"};
    int textMode;
    int rectMode;
};

#endif

main.cpp

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

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Window window;
    window.show();
    return app.exec();
}

Как видите в коде выполняется таймер вне объекта "виджет"

каждые 10 мс отправляет перерисовку виджета, чтобы перерисовать «прямоугольник» с другим размером, и каждые 20 циклов (200 мс) меняет текст «привет» на «мир».

В этом примере вы можете видеть, что в любом случае необходимо перезаписать архитектуру QPainterDevice.

Вы также можете заметить, что "событие" внутри "paintEvent" отключено и не используется напрямую, но важно выполнить последовательность QPainter.

person RTOSkit    schedule 01.01.2013
comment
Прошу прощения, если какой-то аргумент может показаться переупорядоченным, я начал писать ответ вчера в 22:00 по центральноевропейскому времени, но тогда мне нужно идти на ужин к Новому году, и только сейчас я вернулся, и публикую полный ответ. Я не видел, чтобы другие писали.. Извините. Счастливого года всем! - person RTOSkit; 01.01.2013
comment
С новым годом! Спасибо за вашу помощь :) - person Ash; 02.01.2013

Переопределение функции paintEvent() виджета позволяет вам настроить виджет, и эта функция периодически вызывается для перерисовки виджета. Поэтому любой рисунок должен быть сделан в этой функции. Однако переопределение paintEvent() может вызвать некоторые проблемы с производительностью. Я бы предпочел использовать QGraphicsScene и QGraphicsView, тогда я бы добавил прямоугольник к сцене, что является распространенным способом рисования такого рода вещей. Пожалуйста, проверьте GraphicsView Framework

http://qt-project.org/doc/qt-4.8/graphicsview.html

person fatma.ekici    schedule 31.12.2012