IT Notes

QStackedWidget: Вариант реализации Прокси-виджета для блокировки Ui

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

В этом случае можно заблокировать все виджеты формы (поля ввода, ползунки, и т.д.) явно с помощью вызова setDisabled(). В Qt для этих целей предусмотрено вполне универсальное и эффективное решение через цикл. Однако большую свободу можно получить, если организовать блокировку более радикальными методами.

Существует несколько подходов. Первый из них заключается в отображении одного виджета над другим. Еще один вариант сводится к использованию QStackedWidget. На последнем мы и сосредоточимся.

Готовое решение будет выглядеть следующим образом (слева - активный виджет, а справа - заблокированный):

qstacked-widget-proxy-demo-thumbnail

Для создания эффекта блокировки мы:

  1. Делаем скриншот виджета (он расположен на первой странице QStackedWidget), который собираемся заблокировать, с помощью QProxy::grabWidget();
  2. Закрашиваем получившееся изображение полупрозрачным серым цветом;
  3. Устанавливаем изображение в QLabel, который находится на соседней странице QStackedWidget;
  4. Переключаемся на страницу с подготовленным на предыдущем шаге QLabel.

В примере переход между режимами осуществляется с помощью гиперссылок Active и Passive.

Реализация не намного сложнее идеи. Файл proxywidgetdemo.h:

#ifndef PROXYWIDGETDEMO_H
#define PROXYWIDGETDEMO_H

#include <QWidget>

namespace Ui {
class ProxyWidgetDemo;
}

class ProxyWidgetDemo : public QWidget {
    Q_OBJECT

public:
    explicit ProxyWidgetDemo( QWidget* parent = 0 );
    ~ProxyWidgetDemo();

protected:
    void resizeEvent( QResizeEvent* e );

private slots:
    void onHyper( const QString& href );

private:
    Ui::ProxyWidgetDemo* ui;
};

#endif // PROXYWIDGETDEMO_H

Файл proxywidgetdemo.cpp:

#include "proxywidgetdemo.h"
#include "ui_proxywidgetdemo.h"

#include <QPainter>

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

    connect( ui->lbProxyMode, SIGNAL( linkActivated( QString ) ), SLOT( onHyper( QString ) ) );
}

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

void ProxyWidgetDemo::resizeEvent( QResizeEvent* ) {
    if( ui->stackedWidget->currentWidget() == ui->page_Passive ) {
        onHyper( "show-active" );
        onHyper( "show-passive" );
    }
}

void ProxyWidgetDemo::onHyper( const QString& href ) {
    if( href == "show-active" ) {
        ui->lbProxyView->clear();
        ui->stackedWidget->setCurrentWidget( ui->page_Active );
    } else if( href == "show-passive" ) {
        QPixmap pix = QPixmap::grabWidget( ui->page_Active );
        QImage proxyImg( pix.size(), QImage::Format_ARGB32 );
        QPainter p;
        p.begin( &proxyImg );
        p.drawPixmap( 0, 0, pix );
        p.fillRect( proxyImg.rect(), QColor( 0, 0, 0, 130 ) );
        p.end();
        ui->lbProxyView->setPixmap( QPixmap::fromImage( proxyImg ) );
        ui->stackedWidget->setCurrentWidget( ui->page_Passive );
    }
}

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

  1. Нам приходится явно обрабатывать событие resizeEvent(), когда мы находимся в режиме блокировки, поскольку само по себе изображение не обновится;
  2. QLabel, в который мы выводим "заблокированное" изображение, пришлось поместить в QScrollArea с отключенными полосами прокрутки, поскольку иначе уменьшение окна в режиме блокировки становилось невозможным. Кроме того, для QLabel установлен режим выравнивания по левому верхнему краю для предотвращения "скачков" картинки при изменении размеров окна.

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

Исходники

 Скачать пример использования QStackedWidget для создания эффекта блокировки Ui-форм

Похожие публикации

Комментарии

А нельзя ли установить пустой виджет с прозрачным фоном, чтобы не делать скрин ?

Anonymous:

А нельзя ли установить пустой виджет с прозрачным фоном, чтобы не делать скрин ?

Можно пойти и таким путем. Представленное в статье решение является лишь одним из возможных вариантов.