Help us understand the problem. What is going on with this article?

QIconでSVGを表示する

More than 1 year has passed since last update.

はじめに

Qt widgetでアイコンを表すクラスQIconは、SVGをサポートしています

QIcon Classによれば、

Note: Since Qt 4.2, an icon engine that supports SVG is included.

早速やっていきましょう。

なぜSVGを使うのか

  • ベクター画像のため、サイズに依存しない高品質な描画が可能(特に高解像度ディスプレイで効果を発揮する)
  • アイコンにおいてもDRY1を推進できる(かもしれない)

前者はよく言われていますが、今回は後者の利点に着目してみます。

アイコンにおけるDRY

わかりやすさの点、あるいはデザインの省力化の点から、しばしばアイコンのデザインは流用されます。例えば、ある要素のアイコンに+xを重ねて"追加"、"削除"を表現したり、同じデザインのアイコンを回転させて方向を表現したりします。

ソースコードにおけるコピー&ペーストと同様、このような使い回しはデザインの変更を困難にします :no_good:

SVGは<use>要素によって他のSVGファイルから図形を取り込むことができます。また、色やグラデーションといったスタイルに名前を付けて取り込むこともできます。

SVGをアイコンに使えば、これらの機能を活用してDRYをやっていくことができます :ok_woman:

SVGをQIconに表示する

以下のコードでicon.svgをアクションのアイコンとしてツールバーに表示できます。

#include <QApplication>
#include <QWidget>
#include <QToolBar>
#include <QAction>
#include <QIcon>
#include <QMainWindow>
#include <memory>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    auto window = std::make_unique<QMainWindow>();

    QToolBar* toolbar = window->addToolBar("Tools");
    toolbar->addAction(new QAction(QIcon("icon.svg"), "action", window.get()));

    window->show();
    return app.exec();
}

use要素を使う

QIcon Classには書いていないですが、SVGの描画にはQt SVGが使われているはずです。

Rendering SVG Filesを見ると、Qt SVGはSVG 1.2 Tinyをサポートしていると書かれています。

Qt supports the static features of SVG 1.2 Tiny. ECMA scripts and DOM manipulation are currently not supported.

SVG 1.2 Tiny仕様の14.1.4 Reference restrictionsを見ると、SVG 1.2 Tinyで<use>は使える上に、外部ファイルのリンクも可能なようです。

Qt SVGは実装しているのでしょうか?

結論から言うと、同一ファイル内の要素は指定できますが、別のSVGファイルにある要素は指定できませんでした2

つまり、xlink:href="#id"のような指定は有効ですが、xlink:href="external.svg#id"のような指定は(SVG 1.2 Tinyとしては有効のはずだが)動かないという事です。

ソースを見てみよう

use要素のハンドラーを見てみると……

qsvghandler.cpp
static QSvgNode *createUseNode(QSvgNode *parent,
                               const QXmlStreamAttributes &attributes,
                               QSvgHandler *handler)
{
    QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
    const QStringRef xStr = attributes.value(QLatin1String("x"));
    const QStringRef yStr = attributes.value(QLatin1String("y"));
    QSvgStructureNode *group = 0;
    /*略*/
}

1行目からして明らかに#idの形式しか想定していないですね。
QtでアイコンのDRYをやっていくのは現状では難しいようです:no_good:

しかし、image要素の方はちゃんと外部ファイルを読み込むように実装してあるので、同じようにSVGを再帰的にロードすれば動かせるような気がします3

qsvghandler.cpp
static QSvgNode *createImageNode(QSvgNode *parent,
                                 const QXmlStreamAttributes &attributes,
                                 QSvgHandler *handler)
{
    /*略*/
    QStringRef filename = attributes.value(QLatin1String("xlink:href"));
    /*略*/
    if (filename.startsWith(QLatin1String("data"))) {
        /*略*/
    } else
        image = QImage(filename.toString());
    /*略*/
}

まとめ

  • QIconはSVGを扱えます
  • SVG 1.2 Tinyを部分的にサポートしています
  • 外部SVGファイルをリンクすることはできませんでした4
    • もしかしたら意外と簡単に実装できるかも

  1. Don't repeat yourself; 同じことを繰り返さない 

  2. static featuresではないということでしょうか? 

  3. 循環参照を排除したりといった例外処理は必要 

  4. コードから判断すると、useだけでなく全体的に外部のSVGを使うことはできない模様 

tetsurom
主にC++, Qtでアプリケーション開発をしています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away