Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Alex Custov от Июль 31, 2008, 17:19



Название: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 17:19
Имеется (упрощённо) вот такая система layout'ов:

Код:
  QWidget
+------------------------------------------+
| QHBoxLayout                              |
| +--------------------------------------+ |
| | QScrollArea                          | |
| | +----------------------------------+ | |
| | | QWidget                          | | |
| | | +------------------------------+ | | |
| | | | QHBoxLayout                  | | | |
| | | | +--------------------------+ | | | |
| | | | | QWidget                  | | | | |
| | | | | +----------------------+ | | | | |
| | | | | |                      | | | | | |
| | | | | |                      | | | | | |
| | | | | |                      | | | | | |
| | | | | |                      | | | | | |
| | | | | +----------------------+ | | | | |
| | | | +--------------------------+ | | | |
| | | +------------------------------+ | | |
| | +----------------------------------+ | |
| +--------------------------------------+ |
+------------------------------------------+

Самый верхний виджет имеет фон в виде пиксмапа, задаваемого в CSS. Необходимо, чтобы все виджеты, начиная с самого вложенного, были прозрачными. Вложенный виджет отрисовывается сам через paintEvent().

До того, как я начал использовать QScrollArea, система была немного проще: QWidget->QHBoxLayout->QWidget->QHBoxLayout->QWidget, и всё работало. Т.е. было видно, что на главном окне расположено несколько компонентов, промежуточных виджетов видно не было (по-мойму это особенность CSS в Qt -виджеты имеют прозрачный фон по умолчанию), и это было отлично.

Теперь я добавил QScrollArea, код наподобие

Код:
    area = new QScrollArea;
    area->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    area->setWidget(new QWidget);
    area->setWidgetResizable(true);

и теперь рисуется белый фон. Попробовал установить атрибут:

Код:
    area->widget()->setAttribute(Qt::WA_NoSystemBackground);

Вроде бы всё стало опять прозрачно, но самый вложенный виджет перестал автозаполняться фоном при перерисовке, т.е. изначально он прозрачен, но все дальнейшие вызовы paintEvent() накладыватся один на один, превращая виджет в кашу.

Ну и наконец вопрос - что посоветуете, чтобы заново добиться прозрачности? Спасибо.

P.S. QScrollArea убирать нельзя


Название: Re: прозрачный фон
Отправлено: EhTemka от Июль 31, 2008, 17:56
А просто использовать setWindowOpacity для QScrollArea виджета нельзя, потому что свой paintEvent()? 


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 19:01
setWindowOpacity будет работать только с композитом в иксах, которого нет.

Тут проблема в том, что самый вложенный виджет не перересовывается как надо внутри своего paintEvent().


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 19:36
очень странно. Глюк отловил. Собственно, сам scrollarea реализован как подсказал sprit вот тут - http://prog.org.ru/forum/index.php/topic,7526.0.html (http://prog.org.ru/forum/index.php/topic,7526.0.html). Глюк случается, когда самый вложенный виджет добавляется после старта event loop'a. Глюк не воспроизводится, если виджет добавляется перед QApplication::exec() - тогда он отрисовывается как надо. Ну что за беда-то такая?  :(


Название: Re: прозрачный фон
Отправлено: spirit от Июль 31, 2008, 19:39
пиши репорт троллям  :)


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 20:17
Потестите пожалуйста аттач. У меня Qt 4.3.3. Компиляция как обычно:

Код:
# qmake
# make
# ./qs

Там создаётся два прозрачных виджета, при наведении мышью они должны подсвечиваться сеткой. В самом верху файла main.cpp есть define, закоментируйте его, чтобы посмотреть как должно работать. Если #define вернуть обратно, виджеты будут смазываться.


Название: Re: прозрачный фон
Отправлено: spirit от Июль 31, 2008, 20:21
Потестите пожалуйста аттач. У меня Qt 4.3.3. Компиляция как обычно:

Код:
# qmake
# make
# ./qs

Там создаётся два прозрачных виджета, при наведении мышью они должны подсвечиваться сеткой. В самом верху файла main.cpp есть define, закоментируйте его, чтобы посмотреть как должно работать. Если #define вернуть обратно, виджеты будут смазываться.
смазываение есть, если закомментить макрос, но в тоже время , если много раз по виджету мышакой поводить, то все становится однин в один как в случае с раскоменченным макросом.


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 20:43
Хм, на моей версии Qt с закоментированным макросом всё работает как надо...


Название: Re: прозрачный фон
Отправлено: spirit от Июль 31, 2008, 20:51
а что должно получится в результате?


Название: Re: прозрачный фон
Отправлено: spirit от Июль 31, 2008, 20:56
у меня походу вообще нифига не меняется есть макрос или нет.


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 21:00
В результате каждый вложенный виджет должен подсвечиваться при наведении мышкой, и быть прозрачным иначе. Блин, без scrollarea всё пучком, но без него нельзя - виджетов может быть много, и они могут не вместиться.

С макросом отрисовка превращается в кашу.


Название: Re: прозрачный фон
Отправлено: pastor от Июль 31, 2008, 21:03
У меня что с макросом что без него поведение совершенно одинаково. Сетка рисуется каждый раз поверх предыдущей


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 21:05
а на какой версии Qt?


Название: Re: прозрачный фон
Отправлено: spirit от Июль 31, 2008, 21:12
4.3.5


Название: Re: прозрачный фон
Отправлено: pastor от Июль 31, 2008, 21:15
4.4.1 под вендой

Через мин 10 проверю под линуксом на 4.4.0


Название: Re: прозрачный фон
Отправлено: Alex Custov от Июль 31, 2008, 21:25
вот переделал без scrollarea, всё работает хорошо. Посмотрите и этот пример пожалуйста. Там 3 вложенных виджета в hboxlayout'е, каждый должен подсвечиваться нормально. Под виндой результат конечно может отличаться, но мне бы заставить работать хотя бы в Linux :)


Название: Re: прозрачный фон
Отправлено: pastor от Июль 31, 2008, 21:35
Интересная ситуация получаеться. Под линуксом пример со scrollarea работает нормально как с макросом так и без него. Ситуация полностью противоположная чем винде.

openSuse 10.3, Qt 4.4.0


Название: Re: прозрачный фон
Отправлено: spirit от Июль 31, 2008, 21:47
да уж, странно. этот пример пашет на ура под виндой. очень интересно.


Название: Re: прозрачный фон
Отправлено: Alex03 от Август 01, 2008, 08:07
У меня WinXP Qt 4.3.3
Исходный пример не работал в обоих случаях

Подправил до такого (поиграйтесь со значениями TEST, да и прочими дефайнами):
Код:
#define TEST 2

#if TEST == 1
// Исходный подправленный до рабочего в моём случае вариант
#define FIXED_GEOMETRY 1
//#define FIXED_INTERNAL_WIDGETS_GEOMETRY 1
#define RESET_AUTO_FILL_BG
#define USE_BG_STYLE 1
#define MARGIN 0
#define SPACING 0

#elif TEST == 2
// То же что и 1 только более наглядно
//#define FIXED_GEOMETRY 1
#define FIXED_INTERNAL_WIDGETS_GEOMETRY 1
#define RESET_AUTO_FILL_BG
#define USE_BG_STYLE 1
#define MARGIN 10
#define SPACING 10

#elif TEST == 3
// А вот тут то оно и вылазит
//#define FIXED_GEOMETRY 1
#define FIXED_INTERNAL_WIDGETS_GEOMETRY 1
//#define RESET_AUTO_FILL_BG
#define USE_BG_STYLE 1
#define MARGIN 10
#define SPACING 10

#elif TEST == 4
// То же что и 3, но без использования стилей, работает, но фон со смещением
//#define FIXED_GEOMETRY 1
#define FIXED_INTERNAL_WIDGETS_GEOMETRY 1
//#define RESET_AUTO_FILL_BG
//#define USE_BG_STYLE 1
#define MARGIN 10
#define SPACING 10

#else
#error Unknown TEST number
#endif

//#define INHERIT_SCROLL_AREA 1

#include <QApplication>
#include <QHBoxLayout>
#include <QScrollArea>
#include <QTimer>
#include <QWidget>
#include <QPainter>

/******************************************************/

#ifndef INHERIT_SCROLL_AREA
class ScrollArea : public QWidget
#else
class ScrollArea : public QScrollArea
#endif
{
    Q_OBJECT

    public:
        ScrollArea(QWidget *p);

        void addWidget(QWidget *, int stretch);

    signals:
        void layoutChanged();

    private slots:
        void slotUpdateLayout();

    private:
        QHBoxLayout *layout;
#ifndef INHERIT_SCROLL_AREA
        QScrollArea *area;
#endif
};

// Implementation
#ifndef INHERIT_SCROLL_AREA
ScrollArea::ScrollArea(QWidget *p) : QWidget(p)
#else
ScrollArea::ScrollArea(QWidget *p) : QScrollArea(p)
#endif
{
#ifndef INHERIT_SCROLL_AREA
    QHBoxLayout *mainlayout = new QHBoxLayout;
    mainlayout->setContentsMargins(MARGIN, MARGIN, MARGIN, MARGIN);
    mainlayout->setSpacing(SPACING);
    setLayout(mainlayout);
    area = new QScrollArea;
#else
    QScrollArea* area = this;
#endif
    area->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    area->setWidget(new QWidget);
#ifndef FIXED_INTERNAL_WIDGETS_GEOMETRY
    area->setWidgetResizable(true);
#endif

////    area->setAttribute(Qt::WA_NoSystemBackground);
//    area->viewport()->setAttribute(Qt::WA_NoSystemBackground);
//    area->widget()->setAttribute(Qt::WA_NoSystemBackground);

////    area->setAutoFillBackground(false);
#ifdef RESET_AUTO_FILL_BG
    area->viewport()->setAutoFillBackground(false);
    area->widget()->setAutoFillBackground(false);
#endif

//    area->widget()->setBackgroundRole(QPalette::NoRole);
//    area->setBackgroundRole(QPalette::NoRole);

    layout = new QHBoxLayout;
    layout->setContentsMargins(MARGIN, MARGIN, MARGIN, MARGIN);
    layout->setSpacing(SPACING);
    area->widget()->setLayout(layout);

#ifndef INHERIT_SCROLL_AREA
    mainlayout->addWidget(area, 1);
#endif

    connect(this, SIGNAL(layoutChanged()), SLOT(slotUpdateLayout()), Qt::QueuedConnection);
}

void ScrollArea::addWidget(QWidget *w, int stretch)
{
    layout->addWidget(w, stretch);
    w->show();

    emit layoutChanged();
}

void ScrollArea::slotUpdateLayout()
{
#ifdef INHERIT_SCROLL_AREA
    QScrollArea* area = this;
#endif
    int h = area->widget()->sizeHint().height();
    int w = area->widget()->sizeHint().width();
//    area->widget()->resize(w < area->viewport()->width() ? area->viewport()->width() : w, h);
    area->widget()->resize(w, h);
}


ScrollArea *a;

/******************************************************/

class TestWidget : public QWidget
{
    private:
        bool on;

    public:
        TestWidget(QWidget *parent = 0) : QWidget(parent), on(false)
        {
        }

        void enterEvent(QEvent *)
        {
            on = true;
            update();
        }

        void leaveEvent(QEvent *)
        {
            on = false;
            update();
        }

        void paintEvent(QPaintEvent *)
        {
            QPainter p(this);

            static QColor colorB = QColor(255, 255, 0, 127);

            if(on)
                p.fillRect(rect(), QBrush(colorB, Qt::Dense6Pattern));
            else
            {
                p.setPen(QPen(colorB));
                p.drawRect(rect().adjusted(0, 0, -1, -1));
            }

        }
};

/******************************************************/

int main(int argc, char ** argv)
{
    Q_INIT_RESOURCE(qrc);

    QApplication app(argc, argv);
#ifdef FIXED_GEOMETRY
    QWidget *wgt = new QWidget(0, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
#else
    QWidget *wgt = new QWidget();
#endif


#ifdef USE_BG_STYLE
    wgt->setObjectName("TestMain");
    wgt->setStyleSheet("QWidget#TestMain"
"{"
"    background-image:    url(:/background.png);"
"    background-position: top left;"
//"    background-repeat:   repeat-x;"
"}"
);
#else
    QPalette pal = wgt->palette();
    pal.setBrush(QPalette::Window, QBrush(QPixmap( ":/background.png")));
    wgt->setPalette(pal);
#endif

#ifdef FIXED_GEOMETRY
    wgt->resize(500, 40);
    wgt->setFixedHeight(40);
#endif

    QHBoxLayout *hb = new QHBoxLayout;
    hb->setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN);
    hb->setSpacing(SPACING);

    wgt->setLayout(hb);

    a = new ScrollArea(wgt);

    hb->addWidget(a, 1);

    wgt->show();

    TestWidget *w = new TestWidget;
#ifdef FIXED_INTERNAL_WIDGETS_GEOMETRY
    w->setFixedSize(100, 100);
#endif
    a->addWidget(w, 1);

    TestWidget *w2 = new TestWidget;
#ifdef FIXED_INTERNAL_WIDGETS_GEOMETRY
    w2->setFixedSize(100, 100);
#endif
    a->addWidget(w2, 1);

    return app.exec();
}

#include "main.moc"

Ещё я не понимаю зачем генерить такой бутерброд, можно ж потоньше, раскоментарьте
Код:
//#define INHERIT_SCROLL_AREA 1


Также непонятно желание совместно использовать
Код:
    area->setWidgetResizable(true);
и
Код:
    area->widget()->resize(w < area->viewport()->width() ? area->viewport()->width() : w, h);


Ну и ИМХО если уж тользовать SсrollХХХХ то пользовать, а отрисовку всяко полезно отлаживать в различныз геометриях, а не с фикседсайз. Да и нулевые MARGIN-ы/SPACING-и тоже при отладке не есть гуд.


Название: Re: прозрачный фон
Отправлено: Alex Custov от Август 01, 2008, 09:51
Хм... весьма странно. Проблема решилась заменой

Код:
    area->widget()->setAttribute(Qt::WA_NoSystemBackground);

Код:
    area->viewport()->setAutoFillBackground(false);
    area->widget()->setAutoFillBackground(false);

в оригинальном примере. наверно сыграло роль то, что setAutoFillBackground не совсем хорошо документирован, поэтому я его не учёл :) Отдельное спасибо Alex03 за проделанную работу, которая и навела меня на мысль :)

Такой бутерброд необходим, т.к. самый верхний виджет и его layout - не мои, я туда вставляю свой плагин, который и будет наследником QScrollArea.