Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Igors от Май 28, 2016, 19:11



Название: Рисование поверх дочерних виджетов
Отправлено: Igors от Май 28, 2016, 19:11
Добрый день

Есть виджет, в нем layout в котором чайлд виджеты соединенные "проводами". И вот эти провода надо рисовать поверх чайлдов, иначе возникают непонятки. Делать провода тоже виджетами - дороговато и бестолково. Ладно, ознакомился здесь (http://www.wiki.crossplatform.ru/index.php/%D0%A0%D0%B8%D1%81%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85_%D0%B4%D0%BE%D1%87%D0%B5%D1%80%D0%BD%D0%B8%D1%85_%D0%B2%D0%B8%D0%B4%D0%B6%D0%B5%D1%82%D0%BE%D0%B2). Все ясно, наверное и работать будет - но ведь это все-таки неграмотно повторять и повторять рисование парента для каждого чайлда которых сотни.

Ну а как грамотно?

Спасибо


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Bepec от Май 28, 2016, 23:26
Мы это уже проходили. Виджет стекло, на котором вы будете рисовать линии, не заморачиваясь "а где" "а что" "а когда".
Данные об детях есть, координаты начала-конца линий есть, рисуй не хочу.


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Racheengel от Май 29, 2016, 00:42
Я бы делал через QGraphicsScene.
В статье какой-то хак описан, не факт, что будет везде работать...


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Igors от Май 29, 2016, 07:15
Мы это уже проходили. Виджет стекло, на котором вы будете рисовать линии, не заморачиваясь "а где" "а что" "а когда".
Данные об детях есть, координаты начала-конца линий есть, рисуй не хочу.
Смотрел вчера. Точнее открыл хедер - и быстренько закрыл
Цитата: cpp
#ifndef SBGLASS_H
#define SBGLASS_H

/*
****************************************
[5/22/2013 Bepec]Специально для prog.org.ru
Часть кода взята из статьи "Qt. Накрываем виджеты стеклом" (http://www.quizful.net/post/glass-qt-component)
****************************************
*/

#include <QObject>
#include <QLabel>
#include <QMovie>
#include <QEvent>
#include <QBrush>
#include <QDebug>
#include <QGraphicsOpacityEffect>


class SBGlass : public QObject
{
   Q_OBJECT

public:
   SBGlass(QObject *parent = 0);
   ~SBGlass();
   virtual void install(QWidget* w);
   virtual void remove();

   void enableColor(const QColor& color = QColor(111, 111, 100));
   void disableColor();

   void enableOpacity(qreal opacity = 0.5);
   void disableOpacity();

   void enableInfoBlock(QMovie* movie = 0, const QString& text = QString::null);
   void disableInfoBlock();

   void enableAnimationBlock(QMovie* movie = 0);
   void disableAnimationBlock();

   void enableInfoTextBlock(const QString& text = QString::null);
   void disableInfoTextBlock();
   QLabel& getInfoTextBlock();
   void setMovie(QMovie* movie);

protected:
   bool eventFilter(QObject* object, QEvent* event);

private:
   void showInfoTextBlock(const QString& text = QString::null);
   void showAnimationBlock(QMovie* movie = 0);
   void infoBlockPositioning();

private:
   QLabel* wGlass_;
   QLabel* wAnimationContainer_;
   QLabel* wInfoTextContaiter_;
   QMovie* movie_;
   QMovie* defaultMovie_;
   QColor glassColor_;

   bool flagInfoTextBlockEnabled_;
   bool flagAnimationBlockEnabled_;
};

#endif // SBGLASS_H
Что-то явно "не то"


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Igors от Май 29, 2016, 07:20
Я бы делал через QGraphicsScene.
Эти классы всегда казались мне воплощением тупости (возможно я не объективен)

В статье какой-то хак описан, не факт, что будет везде работать...
Рисование "foreground" - нормальная вещь/практика, должно делаться просто и естественно, без всяких хаков.

Ладно, разберусь - отпишусь.


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Bepec от Май 29, 2016, 09:58
Не нравится - не пользуйтесь, однако функцию свою он выполняет.
По поводу тупости - покажите мне, непрофессионалу, как надо сделать такой класс. Мне будет очень интересно.

PS почему "не то"? Вы уж если критикуйте, то с аргументами. А то на пустозвона похожи, да и какой вы тогда профессионал, если не наставляете молодых да ранних :)


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Racheengel от Май 29, 2016, 11:33
Верес, честно говоря, по интерфейсу класса не понятно, как с его помощью можно выполнять кастомное рисование?
Там можно только показать текст и видео проиграть, но нигде нет доступа к паинтеру.


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Igors от Май 29, 2016, 11:51
PS почему "не то"? Вы уж если критикуйте, то с аргументами.
Вот вверху Ваш хедер - какие-то QMovie, infoBlock и др. Разбираться где в этой куче "рациональное зерно" - нафиг нужно. 

..мне, непрофессионалу,
"Профессионал" - совсем не значит "крутой" (как часто думают). Насколько мне известно, Вы зарабатываете на жизнь программированием - стало быть, Вы профессионал. А вот какой: хороший или хреновый - это уже др вопрос :)

.. если не наставляете молодых да ранних  :)
Так это запросто (аттач)


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Bepec от Май 30, 2016, 20:07
Видимой мудрости в исходнике не вижу. А я надеялся :)

И да, критика проекта "закрывает виджет и отображает текстовый блок и гифку" по теме "он не рисует поверх виджета", это ммм... это несколько несуразно, не находите? :P


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Racheengel от Май 30, 2016, 21:33
И да, критика проекта "закрывает виджет и отображает текстовый блок и гифку" по теме "он не рисует поверх виджета", это ммм... это несколько несуразно, не находите? :P

Это не критика скорее, а вопрос, как данное решение соответствует требованию рисования поверх виджета?

Вот смотрим публичный интерфейс:

Код:
   SBGlass(QObject *parent = 0);
   ~SBGlass();
   virtual void install(QWidget* w);
   virtual void remove();

   void enableColor(const QColor& color = QColor(111, 111, 100));
   void disableColor();

   void enableOpacity(qreal opacity = 0.5);
   void disableOpacity();

   void enableInfoBlock(QMovie* movie = 0, const QString& text = QString::null);
   void disableInfoBlock();

   void enableAnimationBlock(QMovie* movie = 0);
   void disableAnimationBlock();

   void enableInfoTextBlock(const QString& text = QString::null);
   void disableInfoTextBlock();
   QLabel& getInfoTextBlock();
   void setMovie(QMovie* movie);

ЧТО здесь должен вызвать пользователь, чтобы нарисовать несколько линий?
или хотя бы получить доступ к виджету-"закрывашке"?


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Bepec от Май 31, 2016, 08:18
Igors, Racheengel - будьте внимательнее пожалуйста :)
Ни разу я не говорил о виджете, который был выложен мной на форуме. Его задача в другом - затемнять и показывать индикатор загрузки с текстом. Точка.

Виджет стекло я упоминал как концепцию, а не готовое решение. Вы уж не придумывайте того, чего нет :D

PS а по поводу того, что проходили - в одной из тем Igors уже задавался этим вопросом. И вот в той далекой теме с каким то названием лежит класс для рисования поверх ) с той же концепцией виджет стекло :)


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Igors от Май 31, 2016, 13:04
PS а по поводу того, что проходили - в одной из тем Igors уже задавался этим вопросом. И вот в той далекой теме с каким то названием лежит класс для рисования поверх ) с той же концепцией виджет стекло :)
Там речь была совсем о другом - рисовать "синхронно".

Видимой мудрости в исходнике не вижу. А я надеялся :)
В чем же по-Вашему "мудрость"?  :) Ну или "что Вы ожидали увидеть?"

Возвращаясь к теме: а ведь все не так уж просто как может показаться, тут есть о чем поговорить. Я реализовал простую конструкцию

Парент (здесь viewport)
  - чайлд 1 "содержимое" (то что накрываем), часто имеет много своих чайлдов
  - чайлд 2 "верхний слой" (стекло) имеет WA_TranslucentBackground

Осталоcь только обеспечить чтобы стекло покрывало все что нужно - и "voila". Ведь виджеты рисуются в нужном порядке. Но не тут-то было :) Оказывается такое стекло можно разбить:

- в примере уберите вызов update и запустите
- тыкните 1 раз в скролл бар - левая часть стекла исчезает (Qt 5.4.2)

После неск часов изучения исходников мне так и не удалось понять почему так. Может просто буги-вуги? Ладно, залатал


Название: Re: Рисование поверх дочерних виджетов
Отправлено: GreatSnake от Май 31, 2016, 15:06
Вот работающий, правда с некоторыми хинтами, вариант
Код
C++ (Qt)
#include <QWidget>
#include <QBackingStore>
#include <QPainter>
#include <QGridLayout>
 
class GridManager : public QWidget
{
bool in_paint_ = false;
public:
GridManager( QWidget* parent = nullptr ) : QWidget( parent )
{
setLayout( new QGridLayout() );
}
void addWidget( QWidget* w, int row, int column )
{
qobject_cast< QGridLayout* >( layout() )->addWidget( w, row, column );
}
 
protected:
bool event( QEvent* e )
{
if( e->type() == QEvent::ChildAdded )
static_cast< QChildEvent* >( e )->child()->installEventFilter( this );
}
bool eventFilter( QObject* o, QEvent* e )
{
if( e->type() == QEvent::Paint )
{
if( !in_paint_ )
{
drawChild( qobject_cast< QWidget* >( o ) );
return true;
}
}
return QWidget::eventFilter( o, e );
}
void drawChild( QWidget* w )
{
w->setAttribute( Qt::WA_WState_InPaintEvent, false );
auto bs = window()->backingStore();
auto r = w->rect().translated( - w->mapFrom( w->window(), QPoint( 0, 0 ) ) );
bs->beginPaint( QRegion( r ) );
QPainter p( bs->paintDevice() );
p.translate( r.topLeft() );
in_paint_ = true;
w->render( &p );
in_paint_ = false;
drawOverChild( &p, w );
bs->endPaint();
}
virtual void drawOverChild( QPainter* p, QWidget* w )
{
p->setBrush( QColor( 0xff, 0xff, 0xff, 200 ) );
p->drawRect( w->rect() );
}
};
 

Перегрузите GridManager::drawOverChild() и уже поверх чайлда рисуйте всё что хотите.


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Igors от Июнь 01, 2016, 08:19
Вот работающий, правда с некоторыми хинтами, вариант
Это близко к приводимой статье, по существу эффект тот же как унаследоваться от чайлда и перекрыть его paintEvent. Предложу еще один способ
Код
C++ (Qt)
class Dummy : public QWidget {
public:
using QWidget::paintEvent;
};
 
bool eventFilter( QObject* o, QEvent* e )
{
if (e->type() == QEvent::Paint) {
Dummy * w = static_cast<Dummy*> (o);
w->paintEvent(static_cast<QPaintEvent *> (e));
QPainter painter(w);
drawOverChild(&painter, w);
return true;
}
return QWidget::eventFilter( o, e );
}
Но это решает проблему с одним чайлдом, мне же хотелось бы создать "слой" и рисовать его один раз, не заботясь сколько чего "под ним"


Название: Re: Рисование поверх дочерних виджетов
Отправлено: GreatSnake от Июнь 01, 2016, 10:55
Это близко к приводимой статье, по существу эффект тот же как унаследоваться от чайлда и перекрыть его paintEvent. Предложу еще один способ
Сей способ в отличие от моего не решает такой запрос:
 - чайлд 1 "содержимое" (то что накрываем), часто имеет много своих чайлдов

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


Название: Re: Рисование поверх дочерних виджетов
Отправлено: Igors от Июнь 01, 2016, 17:44
Сей способ в отличие от моего не решает такой запрос:
Если имеется ввиду флажок QWidget::DrawChildren - то нарисовать весь "нижний слой" одним вызовом render все равно не удается, еще чайлды будут рисоваться