4.6がやってきた-Qt最新事情2010

第8回グラフィックスイフェクト

Qt 4.6に関する連載の最終回は、グラフィックスイフェクトについての説明です。地味な機能ですが、気の利いたユーザインターフェースを作るのに便利な機能です。

グラフィックスイフェクトを用いたサンプル

Qtのインストール先のexamples/effectsにグラフィックスイフェクトを用いたサンプルが3つ用意されています。

アプリケーションピッカーでは、アプリケーションのアイコンを輪状に表示させ、矢印キー操作でアイコンを回転させています。奥にあるアイコンほどぼかしがかかったようにして、カレントのアイコンをわかりやすく見せるようにしています。

アプリケーションピッカー

イフェクト付きポップアップメッセージでは、メッセージが表示されるときには、背景をアニメーションをかけながら緑色に色づけをして、メッセージが目立つようにしています。

イフェクト付きポップアップメッセージ

ライティングとシャドーでは、回転する光源が作る影をアニメーションで表示しています。

ライティングとシャドー

グラフィックスイフェクトのクラス

図1は、グラフィックスイフェクトに用意されているクラスです。主要なクラスの機能概要は、表1のようになります。

図1 グラフィックスイフェクトに用意されているクラスの階層図
図1 グラフィックスイフェクトに用意されているクラスの階層図
表1 グラフィックスイフェクトのクラス名と機能の概要
クラス名機能概要
QGraphicsEffectグラフィックスイフェクト機能の純粋仮想クラス。
QGraphicsBlurEffectぼかし効果用グラフィックスイフェクト
QGraphicsColorizeEffect色付け効果用グラフィックスイフェクト
QGraphicsDropShadowEffect影付け効果用グラフィックスイフェクト
QGraphicsOpacityEffect半透明効果用グラフィックスイフェクト

グラフィックスイフェクトは、上表にあるように、ぼかし、色付け、影付け、半透明化などの効果をグラフィックスアイテム(QGraphicsItem)とウィジェット(QWidget)に付加する機能です。つまり、通常の描画にこれらを適用して、その結果が表示されます。

グラフィックスイフェクトの使い方は以下の2ステップです。使い方はとても簡単で、うまく使えばよいインターフェースを作れます。

①グラフィックスイフェクトのインスタンスを生成し、必要に応じてプロパティを設定する。

グラフィックスイフェクトに関連する主なプロパティは表2の通りです。

表2 グラフィックスイフェクト関連のプロパティ
グラフィックスイフェクトプロパティ説明
QGraphicsBlurEffectblurRadius: qrealぼかし半径。デフォルトは5ピクセル。
QGraphicsColorizeEffectcolor: QColor色付けの色。デフォルトはQColor(0, 0, 192)。
strength: qrealイフェクトの強度。1.0で完全着色、0.0で無着色。デフォルトは1.0。
QGraphicsDropShadowEffectblurRadius: qreal影のぼかし半径。デフォルトは1ピクセル。
color: QColor影の色。デフォルトはQColor(63, 63, 63, 180)。
QGraphicsOpacityEffectopacity: qreal不透明度。デフォルトは0.7。

②グラフィックスアイテムかウィジェットにグラフィックスイフェクトを設定する。

以下のメソッドを用います。設定したグラフィックスイフェクトのオーナーは、設定先のオブジェクトになります。

QGraphicsItem::setGraphicsEffect( QGraphicsEffect* effect );
QWidget::setGraphicsEffect( QGraphicsEffect* effect );

これらのメソッドでの設定後にグラフィックスイフェクトのプロパティの変更をすれば、表示にすぐに適用されます。また、enabled プロパフィで、グラフィックスイフェクトを設定したままで、有効無効を切替えられます。

今回紹介するアニメーションプログラムのソースリストはこちらからダウンロードしてください。

グラフィックスアイテムへのぼかし効果の適用

ぼかし効果をどのようにして、グラフィックスアイテムへの適用するかを説明します。ぼかし付きが図2ぼかしなしは図3です。

図2 ぼかし付き
図2 ぼかし付き
図3 ぼかしなし
図3 ぼかしなし
リスト1 triangles.cpp(部分)
18: Harness::Harness()
19:     : QWidget( 0 )
20: {
21:     resize( 680, 680 );
22: 
23:     QTime midnight(0, 0, 0);
24:     qsrand( midnight.secsTo( QTime::currentTime() ) );
25: 
26:     QGraphicsScene* scene = new QGraphicsScene( -200, -200, 400, 400, this );
27:     scene->setItemIndexMethod( QGraphicsScene::NoIndex );
28: 
29:     for ( int n = 0; n 

31行目でグラフィックスアイテムを生成しています。このグラフィックスアイテムにグラフィックスイフェクトを設定します。

33:         QGraphicsBlurEffect* blurEffect = new QGraphicsBlurEffect;
34:         blurEffect->setBlurRadius( 0.5 + ( qrand() / double( RAND_MAX ) ) * 2 );
35:         polygonItem->setGraphicsEffect( blurEffect );

ぼかし効果を付けるためにQGraphicsBlurEffectのインスタンスを生成し、ぼかし半径を乱数設定しています。

37:         polygonItem->rotate( qrand() % 360 );
38:         polygonItem->setPen( Qt::NoPen );
39:         QColor color;
40:         while ( true ) {
41:             color = QColor( qrand() % 256, qrand() % 256, qrand() % 256 );
42:             if ( color.value() > 80 )
43:                 break;
44:         }
45:         color.setAlpha( 100 + qrand() % 156 );
46:         polygonItem->setBrush( color );
47:         scene->addItem( polygonItem );
48:     }
49: 
50:     QGraphicsView* graphicsView = new QGraphicsView( scene );
51:     graphicsView->setRenderHint( QPainter::Antialiasing );
52: 
53:     QVBoxLayout* topLayout = new QVBoxLayout;
54:     topLayout->addWidget( graphicsView );
55: 
56:     setLayout( topLayout );
57: }

ウィジェットへの影付け効果の適用

ウィジェットにもグラフィックスイフェクトを付けられ、影を付けると図4のようになります。

図4 ウィジェットに影を付ける
図4 ウィジェットに影を付ける
リスト2 widgetdropshadow.cpp(部分)
 8: int main( int argc, char** argv )
 9: {
10:     QApplication app( argc, argv );
11:     
12:     QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect;
13:     shadowEffect->setBlurRadius( 12 );
14:     shadowEffect->setOffset( 6, 6 );
15:     shadowEffect->setColor( QColor( 20, 20, 20 ) );

まず、影を付けるためにQGraphicsDropShadowEffectのインスタンスを生成します。影のぼかし半径を12ピクセル、影のオフセットを6ピクセルずつ、濃いめの灰色の影を付けています。

17:     QLabel* label = new QLabel( "Hello!" );
18:     QFont font = label->font();
19:     font.setPointSize( 72 );
20:     label->setFont( font );
21:     QPalette palette = label->palette();
22:     palette.setColor( QPalette::Window, QColor( "SteelBlue" ) );
23:     label->setAutoFillBackground( true );
24:     label->setPalette( palette );
25:     label->setGraphicsEffect( shadowEffect );

25行目でウィジェットにsetGraphicsEffectで、グラフィックスイフェクトを設定しています。このように、グラフィックスイフェクトを使うのはとても簡単です。

27:     QHBoxLayout* labelLayout = new QHBoxLayout;
28:     labelLayout->addStretch();
29:     labelLayout->addWidget( label );
30:     labelLayout->addStretch();
31: 
32:     QVBoxLayout* topLayout = new QVBoxLayout;
33:     topLayout->addStretch();
34:     topLayout->addLayout( labelLayout );
35:     topLayout->addStretch();
36: 
37:     QWidget top;
38:     top.setLayout( topLayout );
39: 
40:     top.show();
41: 
42:     return app.exec();
43: }

Mac OS Xではウィジェットには、どのグラフィックスイフェクトも適用できないというバグがあり、このサンプルコードを動かしてもグラフィックスイフェクトは無効です。

グラフィックスイフェクトのアニメーション

グラフィックスイフェクトのプロパティをアニメーションすれば、各効果が段階的に適用されるようなアニメーション効果を得られます。色付け効果にアニメーションを適用してみましょう。次の動画は第3回「Qt 4.6のアニメーションフレームワーク[後編⁠⁠」で用いた「グラフィックスビューのアイテムのアニメーション」に、グラフィックスイフェクトのアニメーションを追加したサンプルです。黄色いボールが移動しながら段々と赤くなって行きます。

リスト3 animatedeffect.cpp(部分)
 51: Harness::Harness()
 52:     : QWidget( 0 ), d_ptr( new HarnessPrivate )
 53: {
 54:     Q_D( Harness );
 55: 
 56:     QGraphicsScene* graphicsScene = new QGraphicsScene( 0, 0, d->movingAreaSize.width(), d->movingAreaSize.height() );
 57:     MovableGraphicsPixmapItem* yellowBallItem = createBallItem( ":/images/YellowGlassBall.png" );
 58: 
 59:     QGraphicsColorizeEffect* colorizeEffect = new QGraphicsColorizeEffect;
 60:     colorizeEffect->setColor( QColor( 192, 0, 0 ) );
 61:     colorizeEffect->setStrength( 0.0 );
 62:     yellowBallItem->setGraphicsEffect( colorizeEffect );

59行目で、今度は、色付けのためにQGraphicsColorizeEffectのインスタンスを生成しています。薄い赤色が付くように設定していますが、strength プロパティを0.0にしているので、このグラフィックスイフェクトを適用しても赤色にはならず、元々の黄色のままとなります。

 64:     QSize ballSize = yellowBallItem->pixmap().size();
 65:     int ballSpacingY = ( d->movingAreaSize.height() - ballSize.height())  / 2;
 66:     int yellowBallY = ballSpacingY;
 67:     yellowBallItem->setPos( d->horizontalOffset, yellowBallY );
 68:     graphicsScene->addItem( yellowBallItem );
 69: 
 70:     QGraphicsView* graphicsView = new QGraphicsView();
 71:     graphicsView->setScene( graphicsScene );
 72: 
 73:     QPushButton* animateButton = new QPushButton( "Animate" );
 74: 
 75:     QHBoxLayout* buttonLayout = new QHBoxLayout;
 76:     buttonLayout->addStretch();
 77:     buttonLayout->addWidget( animateButton );
 78: 
 79:     QVBoxLayout* topLayout = new QVBoxLayout;
 80:     topLayout->addWidget( graphicsView );
 81:     topLayout->addLayout( buttonLayout );
 82: 
 83:     setLayout( topLayout );
 84:     setFixedSize( sizeHint() );
 85: 
 86:     d->yellowBallAnimation = new QParallelAnimationGroup( this );

yellowBallItemのposとcolorizeEffectのstrengthの各プロパティを並列にアニメーションをすることにします。

 88:     QPropertyAnimation* posAnimation = new QPropertyAnimation( yellowBallItem, "pos", this );
 89:     d->yellowBallAnimation->addAnimation( posAnimation );
 90:     posAnimation->setDuration( 5 * 1000 );
 91:     int movableBallStartX = d->horizontalOffset;
 92:     int movableBallEndX = d->movingAreaSize.width() - ballSize.width() - d->horizontalOffset;
 93:     posAnimation->setStartValue( QPoint( movableBallStartX, yellowBallY ) );
 94:     posAnimation->setEndValue( QPoint( movableBallEndX, yellowBallY ) );
 95:     posAnimation->setEasingCurve( QEasingCurve::OutBounce );

posのアニメーションは、並列アニメーションに入れるようにする他は今まで通りです。

 97:     QPropertyAnimation* strengthAnimation = new QPropertyAnimation( colorizeEffect, "strength", this );
 98:     d->yellowBallAnimation->addAnimation( strengthAnimation );
 99:     strengthAnimation->setDuration( 5 * 1000 );
100:     strengthAnimation->setStartValue( 0.0 );
101:     strengthAnimation->setEndValue( 1.0 );

strengthのアニメーションは、0.0から1.0に変わるようにしています。このようにすれば、薄い黄色から薄い赤に変わるようにアニメーションが行われます。

103:     connect( animateButton, SIGNAL( clicked() ), SLOT( startAnimation() ) );
104: }

おわりに

2009 年末から半年以上に渡って続いた Qt 4.6 関連の連載も今回で一区切りです。しばらく間を置いて、次はQt 4.7のリリース情報やQt 4.7で最も期待されている機能のQMLについて連載をする予定です。

おすすめ記事

記事・ニュース一覧