【Qt】QTreeViewのpaintEventを徹底解説!描画カスタマイズの基本と応用

2025-05-16

Qtプログラミングにおけるvoid QTreeView::paintEvent(QPaintEvent *event)は、QTreeViewウィジェットが自身を描画する際に呼び出されるイベントハンドラです。

これは、Qtのグラフィックスビューアーフレームワーク(QGraphicsViewなどとは異なり、主にモデル/ビュープログラミングで使用されるウィジェットベースのUIコンポーネント)において、ウィジェットの見た目をカスタマイズするための重要なメカニズムです。

  1. 描画要求の処理:

    • ウィジェットの一部または全体を再描画する必要がある場合に、QtシステムからpaintEventが自動的に呼び出されます。これは以下のような様々な状況で発生します。
      • update()repaint()関数が明示的に呼び出された場合。
      • ウィジェットが隠れていて、再度表示された場合。
      • ウィジェットのサイズが変更された場合。
      • データモデルが変更され、ツリービューの表示内容を更新する必要がある場合。
      • 他のウィジェットによって隠されていた部分が露出した場合。
    • 引数として渡されるQPaintEvent *eventは、どの領域(矩形または領域)が再描画する必要があるかといった情報を含んでいます。
  2. カスタム描画の実現:

    • QTreeViewは、デフォルトでモデル内のデータをツリー構造として表示します。しかし、開発者がツリービューの要素(アイテム、背景、罫線など)の見た目を標準とは異なるものにしたい場合、QTreeViewクラスを継承し、このpaintEventメソッドをオーバーライドします。
    • オーバーライドされたpaintEvent内では、QPainterオブジェクトを使用して、ツリービューの描画を自由に行うことができます。QPainterは、線、図形、テキスト、画像などを描画するための豊富な機能を提供します。
  3. 注意点:

    • paintEvent内で描画を行う際は、必ずQPainterオブジェクトをインスタンス化し、そのコンストラクタに描画対象のウィジェット(通常はthis)を渡します。
      void MyTreeView::paintEvent(QPaintEvent *event) {
          QPainter painter(this->viewport()); // QTreeViewの場合はviewport()が推奨される場合が多い
          // ここにカスタム描画コードを記述
          // 例: painter.drawText(rect, Qt::AlignCenter, "カスタムテキスト");
      
          // 基底クラスのpaintEventを呼び出すことで、デフォルトの描画が行われます。
          // これを呼び出さないと、ツリービューの標準的な表示が失われる可能性があります。
          QTreeView::paintEvent(event);
      }
      
    • パフォーマンスの観点から、paintEvent内で行う描画処理は可能な限り軽量に保つべきです。不必要な再描画を防ぐために、event->rect()event->region()を利用して、更新が必要な領域のみを描画することが推奨されます。
    • Qtのモデル/ビューアーキテクチャでは、個々のアイテムの描画は通常、QAbstractItemDelegateを継承したカスタムデリゲートで行う方が一般的です。paintEventは、ツリービュー全体の背景や、アイテムの描画とは直接関係のない部分(例えば、ツリーが空の場合の「アイテムがありません」メッセージの表示など)のカスタマイズに適しています。


基底クラスのpaintEvent()の呼び出し忘れ

エラー/問題点:

  • カラムヘッダーが正しく描画されない。
  • スクロールバーが機能しない、またはスクロールしても内容が更新されない。
  • アイテムの選択状態や展開/折りたたみアイコンなどが正しく描画されない。
  • ツリービューのアイテムが表示されない、または一部しか表示されない。

原因: paintEvent()をオーバーライドする際、必ず基底クラス(QTreeView::paintEvent(event))を呼び出す必要があります。これを呼び出さないと、QTreeViewのデフォルトの描画ロジック(データモデルからアイテムを取得し、それらを適切に配置・描画する処理など)が実行されません。カスタム描画はそのデフォルト描画の上に追加される形になるため、基底クラスの描画は必須です。

トラブルシューティング: paintEvent()のオーバーライドの最後に、基底クラスのメソッドを呼び出しているか確認してください。

void MyTreeView::paintEvent(QPaintEvent *event) {
    QPainter painter(viewport()); // または this
    // ここにカスタム描画ロジック

    // **重要**: 必ず基底クラスのpaintEventを呼び出す
    QTreeView::paintEvent(event);
}

QPainterオブジェクトのライフサイクルと対象ウィジェット

エラー/問題点:

  • アプリケーションがクラッシュする、または未定義の動作を示す。
  • 描画内容が意図しない場所に表示される。
  • 何も描画されない、または描画が途切れる。

原因: QPainterは、描画を行う際にその描画対象となるデバイス(QWidgetQPixmapなど)をコンストラクタで受け取ります。

  • QTreeViewの場合、アイテムの描画領域はビューポート(viewport())内で行われます。直接thisQTreeView自身)に描画すると、スクロール時に問題が発生したり、描画領域が正しくない場合があります。
  • QPainterオブジェクトは、必ずスタック上で作成し、paintEventのスコープ内で完結させるべきです。メンバー変数として持つべきではありません。

トラブルシューティング: QPainterを以下のように正しく初期化しているか確認してください。

void MyTreeView::paintEvent(QPaintEvent *event) {
    // QTreeViewの場合、通常はviewport()に対して描画します。
    // viewport()はQAbstractScrollArea::viewport()から継承されます。
    QPainter painter(viewport()); 
    
    // または、QTreeView全体に描画したい場合は this
    // QPainter painter(this); 

    // カスタム描画
    painter.drawText(10, 20, "Hello Custom Paint!");

    QTreeView::paintEvent(event);
}

QPainterはデストラクタで自動的にend()を呼び出すため、明示的にpainter.begin()painter.end()を呼び出す必要は通常ありません(コンストラクタでデバイスを渡した場合)。

無駄な再描画によるパフォーマンス問題

エラー/問題点:

  • CPU使用率が高くなる。
  • ツリービューのスクロールやリサイズ時にUIがカクつく、遅延が発生する。

原因: paintEventは非常に頻繁に呼び出される可能性があるため、その内部で重い処理を行うとパフォーマンスに悪影響を与えます。特に、以下のような場合に問題が発生します。

  • 大量の画像をその都度ロードし直す。
  • paintEvent内でファイルI/O、ネットワーク通信、複雑な計算などを行う。
  • 必要のない領域まで描画しようとする。

トラブルシューティング:

  • 重い処理の回避:
    • paintEvent内では、描画に必要な最低限の処理のみを行い、他の重い処理は別のスレッドや、イベントループの別のタイミングで実行するように設計を見直してください。
  • 描画対象のキャッシュ:
    • 静的な背景画像や複雑な図形など、頻繁に変わらない描画内容はQPixmapなどに事前にレンダリングしておき、paintEvent内ではそのQPixmapdrawPixmap()で描画するだけにすることで、パフォーマンスを向上できます。
  • event->rect()またはevent->region()の活用:
    • paintEventに渡されるQPaintEventオブジェクトは、実際に再描画が必要な領域(dirty region)の情報を持っています。この情報を使って、必要な部分だけを描画するようにロジックを最適化してください。 <!-- end list -->
    void MyTreeView::paintEvent(QPaintEvent *event) {
        QPainter painter(viewport());
        // 描画が必要な領域のみをクリップする
        painter.setClipRect(event->rect()); 
    
        // このクリップ領域内でカスタム描画を行う
        // 例: 特定のアイテムのカスタム背景を描画する場合など
    
        QTreeView::paintEvent(event);
    }
    

QAbstractItemDelegateとの役割の混同

エラー/問題点:

  • モデルのデータ変更に対する描画更新がうまくいかない。
  • 選択状態、ホバー状態などのQtが提供する基本的な描画機能が失われる、または重複して描画される。
  • アイテムごとの描画カスタマイズが複雑になりすぎる。

原因: QTreeView::paintEvent()は、ツリービュー全体の背景や、アイテムに依存しないグローバルな描画(例:空のツリービューに表示される「アイテムがありません」メッセージなど)をカスタマイズするのに適しています。 個々のアイテムの描画(テキストの色、アイコン、進捗バーなど)をカスタマイズしたい場合は、QAbstractItemDelegateを継承したカスタムデリゲートを作成し、setItemDelegate()でツリービューに設定する方が適切です。デリゲートのpaint()メソッドがアイテムごとに呼び出されます。

トラブルシューティング:

  • 目的の明確化:
    • 行いたいカスタマイズが「ツリービュー全体の見た目」なのか、「各アイテムの見た目」なのかを明確に区別してください。
    • 「各アイテムの見た目」であれば、QAbstractItemDelegateの利用を検討してください。QAbstractItemDelegateを使うと、モデルのデータ変更に応じてアイテムの描画が自動的に更新されるなど、モデル/ビューアーキテクチャの利点を最大限に活かせます。

エラー/問題点:

  • 描画がおかしくなるが、原因が特定しにくい。
  • 特定の描画要素の色やフォント、変換行列などが、意図しない他の要素にも影響を与えてしまう。

原因: QPainterは、描画設定(色、フォント、ペン、ブラシ、変換行列など)を状態として保持しています。カスタム描画を行う際にこれらの設定を変更し、その後元の設定に戻さないと、後続の描画(特に基底クラスのpaintEventによる描画)に影響を与えてしまいます。

トラブルシューティング: カスタム描画でQPainterの状態を変更する場合は、変更前にpainter.save()を呼び出し、カスタム描画の完了後にpainter.restore()を呼び出して状態を元に戻す習慣をつけましょう。

void MyTreeView::paintEvent(QPaintEvent *event) {
    QPainter painter(viewport());
    painter.setClipRect(event->rect());

    // カスタム描画
    painter.save(); // 現在の描画状態を保存
    painter.setPen(Qt::red);
    painter.setFont(QFont("Arial", 20));
    painter.drawText(10, 30, "Custom Title");
    painter.restore(); // 描画状態を元に戻す

    // 基底クラスの描画(ここでは元のペンやフォントで描画される)
    QTreeView::paintEvent(event);
}


例1: ツリービューの背景に画像を描画する

この例では、QTreeView のビューポートの背景に画像をタイル状に描画します。

MyTreeView.h

#ifndef MYTREEVIEW_H
#define MYTREEVIEW_H

#include <QTreeView>
#include <QPainter>
#include <QPixmap>
#include <QPaintEvent>

class MyTreeView : public QTreeView
{
    Q_OBJECT

public:
    explicit MyTreeView(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    QPixmap m_backgroundPixmap;
};

#endif // MYTREEVIEW_H

MyTreeView.cpp

#include "MyTreeView.h"
#include <QDebug> // デバッグ用

MyTreeView::MyTreeView(QWidget *parent)
    : QTreeView(parent)
{
    // 背景画像をロード
    // 実際には、プロジェクトのパスに合わせて調整してください
    if (!m_backgroundPixmap.load(":/images/background_tile.png")) {
        qWarning() << "背景画像のロードに失敗しました!";
    }
}

void MyTreeView::paintEvent(QPaintEvent *event)
{
    QPainter painter(viewport()); // ビューポートに描画

    // 背景画像をタイル状に描画
    if (!m_backgroundPixmap.isNull()) {
        const QRect rectToPaint = event->rect(); // 描画が必要な領域
        for (int x = rectToPaint.left(); x < rectToPaint.right(); x += m_backgroundPixmap.width()) {
            for (int y = rectToPaint.top(); y < rectToPaint.bottom(); y += m_backgroundPixmap.height()) {
                painter.drawPixmap(x, y, m_backgroundPixmap);
            }
        }
    }

    // 重要: 基底クラスのpaintEventを呼び出して、デフォルトの描画を実行する
    QTreeView::paintEvent(event);
}

main.cpp (使用例)

#include <QApplication>
#include <QStandardItemModel>
#include "MyTreeView.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyTreeView treeView;

    // モデルを設定
    QStandardItemModel model;
    QStandardItem *parentItem = model.invisibleRootItem();

    QStandardItem *item1 = new QStandardItem("Root Item 1");
    parentItem->appendRow(item1);

    QStandardItem *subItem1 = new QStandardItem("Sub Item A");
    item1->appendRow(subItem1);

    QStandardItem *subItem2 = new QStandardItem("Sub Item B");
    item1->appendRow(subItem2);

    QStandardItem *item2 = new QStandardItem("Root Item 2");
    parentItem->appendRow(item2);

    treeView.setModel(&model);
    treeView.expandAll(); // 全て展開

    treeView.resize(400, 300);
    treeView.show();

    return a.exec();
}
  • viewport() を使用して描画しているのは、QTreeView がスクロール可能な領域(ビューポート)とスクロールバーなどの非スクロール領域を持つためです。アイテムはビューポート内に描画されます。
  • ":/images/background_tile.png" はQtのリソースシステムを使用していることを示します。プロジェクトに images フォルダを作成し、その中に background_tile.png を配置し、.qrc ファイル(例: resources.qrc)を更新してQMake/CMakeに含める必要があります。

例2: ツリービューが空の場合にメッセージを表示する

この例では、モデルにアイテムがない場合に、「アイテムがありません」というメッセージをツリービューの中央に表示します。

MyEmptyTreeView.h

#ifndef MYEMPTYTREEVIEW_H
#define MYEMPTYTREEVIEW_H

#include <QTreeView>
#include <QPainter>
#include <QPaintEvent>
#include <QStandardItemModel> // 例示用

class MyEmptyTreeView : public QTreeView
{
    Q_OBJECT

public:
    explicit MyEmptyTreeView(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
};

#endif // MYEMPTYTREEVIEW_H

MyEmptyTreeView.cpp

#include "MyEmptyTreeView.h"

MyEmptyTreeView::MyEmptyTreeView(QWidget *parent)
    : QTreeView(parent)
{
}

void MyEmptyTreeView::paintEvent(QPaintEvent *event)
{
    // まず基底クラスのpaintEventを呼び出し、デフォルトの描画(背景など)を行う
    QTreeView::paintEvent(event);

    // モデルにアイテムがない場合のみカスタム描画を行う
    if (model() && model()->rowCount() == 0) {
        QPainter painter(viewport()); // ビューポートに描画

        // テキストの描画設定
        painter.setPen(Qt::gray);
        painter.setFont(QFont("メイリオ", 12));

        // 描画するテキスト
        QString message = "データがありません。";

        // テキストを描画する矩形(ビューポート全体)
        QRect rect = viewport()->rect();

        // テキストを中央に描画
        painter.drawText(rect, Qt::AlignCenter, message);
    }
}

main.cpp (使用例)

#include <QApplication>
#include <QStandardItemModel>
#include "MyEmptyTreeView.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyEmptyTreeView treeView;

    // 空のモデルを設定して、「データがありません」メッセージが表示されることを確認
    QStandardItemModel emptyModel;
    treeView.setModel(&emptyModel);

    // デモのために少しサイズを大きく設定
    treeView.resize(300, 200);
    treeView.setWindowTitle("Empty Tree View Example");
    treeView.show();

    // もう一つ、データがある場合のツリービュー(メッセージは表示されない)
    MyEmptyTreeView treeViewWithData;
    QStandardItemModel dataModel;
    dataModel.appendRow(new QStandardItem("データあり"));
    treeViewWithData.setModel(&dataModel);
    treeViewWithData.resize(300, 200);
    treeViewWithData.setWindowTitle("Tree View With Data Example");
    treeViewWithData.show();

    return a.exec();
}

MyGradientTreeView.h

#ifndef MYGRADIENTTREEVIEW_H
#define MYGRADIENTTREEVIEW_H

#include <QTreeView>
#include <QPainter>
#include <QLinearGradient>
#include <QPaintEvent>

class MyGradientTreeView : public QTreeView
{
    Q_OBJECT

public:
    explicit MyGradientTreeView(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
};

#endif // MYGRADIENTTREEVIEW_H

MyGradientTreeView.cpp

#include "MyGradientTreeView.h"

MyGradientTreeView::MyGradientTreeView(QWidget *parent)
    : QTreeView(parent)
{
}

void MyGradientTreeView::paintEvent(QPaintEvent *event)
{
    QPainter painter(viewport()); // ビューポートに描画

    // リニアグラデーションを作成
    QLinearGradient gradient(event->rect().topLeft(), event->rect().bottomRight());
    gradient.setColorAt(0.0, QColor(200, 220, 255)); // 明るい青
    gradient.setColorAt(1.0, QColor(150, 180, 220)); // 少し濃い青

    // グラデーションで背景を塗りつぶす
    painter.fillRect(event->rect(), gradient);

    // 重要: 基底クラスのpaintEventを呼び出して、デフォルトの描画を実行する
    QTreeView::paintEvent(event);
}

main.cpp (使用例)

#include <QApplication>
#include <QStandardItemModel>
#include "MyGradientTreeView.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyGradientTreeView treeView;

    QStandardItemModel model;
    QStandardItem *parentItem = model.invisibleRootItem();

    QStandardItem *item1 = new QStandardItem("グラデーション背景");
    parentItem->appendRow(item1);

    QStandardItem *subItem1 = new QStandardItem("アイテム A");
    item1->appendRow(subItem1);

    QStandardItem *subItem2 = new QStandardItem("アイテム B");
    item1->appendRow(subItem2);

    QStandardItem *item2 = new QStandardItem("さらに別のアイテム");
    parentItem->appendRow(item2);

    treeView.setModel(&model);
    treeView.expandAll();

    treeView.resize(400, 300);
    treeView.setWindowTitle("Gradient Background Tree View");
    treeView.show();

    return a.exec();
}

これらの例からわかるように、QTreeView::paintEvent() をオーバーライドすることで、QPainter を使ってツリービューの背景や、特定の条件での追加情報を描画できます。

重要なポイント:

  • 役割分担: 個々のツリービューアイテムの見た目をカスタマイズしたい場合は、QAbstractItemDelegate を継承したカスタムデリゲートを使用する方が、よりQtのモデル/ビューアーキテクチャに適しており、推奨されます。paintEvent() は、ビュー全体にわたる描画(背景、オーバーレイ、空の状態のメッセージなど)に特化しています。
  • event->rect() の活用: 描画が必要な領域 (event->rect()) にのみ描画することで、パフォーマンスを最適化できます。
  • 基底クラスの呼び出し: QTreeView::paintEvent(event); を呼び出すことを忘れないでください。これは、ツリービューのデフォルトの描画(アイテム、罫線、アイコンなど)が正しく行われるために不可欠です。通常、カスタム描画は基底クラスの描画の「前」または「後」に行います。上記の例では「前」に描画することで、デフォルトのアイテム描画の上にカスタム描画が重ならないようにしています(背景画像やグラデーションの場合)。空のメッセージの例では「後」に描画し、基底クラスの描画によっても消えないようにしています。
  • QPainter の初期化: QPainter painter(viewport()); を使用して、描画がツリービューのスクロール可能な領域内で行われるようにします。


QAbstractItemDelegate を使用する (最も一般的で推奨される方法)

目的: ツリービューの個々のアイテムの描画(テキストの色、フォント、アイコン、進捗バーなど)をカスタマイズしたい場合。

説明: Qtのモデル/ビューフレームワークでは、データの表示(ビュー)とデータの描画(デリゲート)が分離されています。QAbstractItemDelegate は、モデルから提供されたデータをどのように描画するかを定義するためのインターフェースです。

  • createEditor() など: アイテムの編集(ダブルクリックなどでエディタを表示)もデリゲートが担当します。
  • sizeHint() メソッド: アイテムの推奨サイズを返すことで、レイアウトに影響を与えることができます。
  • paint() メソッド: デリゲートの paint() メソッドをオーバーライドすることで、特定の QModelIndex に対応するアイテムの描画ロジックを記述できます。このメソッドは、アイテムがビューポートに表示されるたびに呼び出されます。

利点:

  • Qtの機能との連携: アイテムの選択状態、ホバー状態、フォーカスなど、Qtが提供するデフォルトの描画機能と自然に連携します。
  • パフォーマンス: Qtが描画を最適化し、必要なアイテムのみを効率的に再描画します。
  • 再利用性: 作成したデリゲートは、QTreeView だけでなく QListViewQTableView など、他のアイテムビューでも再利用できます。
  • 責任の分離: 描画ロジックとビューロジックが明確に分離されるため、コードの保守性が向上します。

欠点:

  • ツリービュー全体の背景や、アイテムに依存しない描画(例: 空のツリービューに表示される「データがありません」メッセージなど)には適していません。

コード例:

// MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H

#include <QStyledItemDelegate>
#include <QPainter>

class MyItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit MyItemDelegate(QObject *parent = nullptr);

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};

#endif // MYITEMDELEGATE_H

// MyItemDelegate.cpp
#include "MyItemDelegate.h"
#include <QDebug>

MyItemDelegate::MyItemDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{
}

void MyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    // 通常のアイテム描画
    QStyledItemDelegate::paint(painter, option, index);

    // カスタム描画を追加
    if (index.column() == 0) { // 最初のカラムにのみカスタム描画
        QVariant data = index.data();
        if (data.isValid() && data.toString().contains("重要")) {
            // "重要"というテキストが含まれるアイテムに赤い四角を描画
            painter->save();
            painter->setBrush(QColor(255, 0, 0, 100)); // 半透明の赤
            painter->drawRect(option.rect.adjusted(option.rect.width() - 20, 5, -5, -5)); // 右端に小さな四角
            painter->restore();
        }
    }
}

// main.cpp (MyTreeViewの代わりにMyItemDelegateを使用)
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include "MyItemDelegate.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTreeView treeView;
    QStandardItemModel model;

    QStandardItem *root = model.invisibleRootItem();
    QStandardItem *item1 = new QStandardItem("通常のアイテム");
    root->appendRow(item1);

    QStandardItem *item2 = new QStandardItem("重要なアイテム");
    root->appendRow(item2);

    QStandardItem *item3 = new QStandardItem("サブアイテム (重要)");
    item2->appendRow(item3);

    treeView.setModel(&model);
    treeView.expandAll();

    // デリゲートを設定
    treeView.setItemDelegate(new MyItemDelegate(&treeView)); // ツリービューのアイテム描画をカスタマイズ

    treeView.resize(400, 300);
    treeView.show();

    return a.exec();
}

スタイルシート (CSSのようなもの)

目的: ツリービューの一般的な見た目(背景色、ボーダー、フォント、行の高さなど)を、コードで描画ロジックを書かずに変更したい場合。

説明: Qtのスタイルシートは、ウィジェットの見た目をCSSに似た構文で定義できる強力な機能です。特定のウィジェットタイプ、ID、クラスなどに基づいてスタイルを適用できます。

利点:

  • クロスプラットフォーム: 一貫した見た目を容易に実現できます。
  • 柔軟性: アプリケーションの実行中にスタイルを変更することも可能です。
  • デザインとロジックの分離: UIデザイナーとプログラマーの分業が容易になります。
  • コード量削減: C++コードでの描画ロジックが不要になります。

欠点:

  • QPainterを使った直接描画ほどの細かな制御はできません。
  • 複雑なカスタム描画(例: 画像のタイル描画、動的なグラデーション、特定の条件に基づく複雑な形状の描画など)には限界があります。

コード例:

// main.cpp
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTreeView treeView;
    QStandardItemModel model;

    QStandardItem *root = model.invisibleRootItem();
    root->appendRow(new QStandardItem("アイテム 1"));
    root->appendRow(new QStandardItem("アイテム 2"));
    root->child(1)->appendRow(new QStandardItem("サブアイテム 2-1"));
    root->appendRow(new QStandardItem("アイテム 3"));

    treeView.setModel(&model);
    treeView.expandAll();

    // スタイルシートを設定
    treeView.setStyleSheet(
        "QTreeView {"
        "    background-color: #F0F8FF; /* 淡い青の背景 */"
        "    alternate-background-color: #E6F0FF; /* 交互の行の色 */"
        "    border: 2px solid #A0A0A0; /* 灰色のボーダー */"
        "    font-family: Arial; font-size: 14px;"
        "}"
        "QTreeView::item {"
        "    padding: 3px;"
        "}"
        "QTreeView::item:selected {"
        "    background-color: #AACCFF; /* 選択時の背景色 */"
        "    color: #333333; /* 選択時のテキスト色 */"
        "}"
        "QTreeView::branch:closed:has-children {"
        "    image: url(:/icons/plus.png); /* 閉じたブランチのアイコン */"
        "}"
        "QTreeView::branch:open:has-children {"
        "    image: url(:/icons/minus.png); /* 開いたブランチのアイコン */"
        "}"
    );
    // 補足: アイコンを使用する場合、プロジェクトにリソースファイルとアイコン画像が必要です。

    treeView.resize(400, 300);
    treeView.show();

    return a.exec();
}

QPalette を使用する

目的: ウィジェットの基本的な色設定(背景色、テキスト色、選択色など)を、OSのスタイルに沿って変更したい場合。

説明: QPalette は、Qtウィジェットが描画に使用する色の集合です。各ウィジェットは独自のパレットを持っており、そのパレットの色を使って描画を行います。QPalette を変更すると、そのウィジェットのデフォルトの描画に影響を与えます。

利点:

  • シンプルさ: 複雑な描画ロジックを記述する必要がありません。
  • システムの見た目との整合性: OSのテーマに沿った色の変更が容易です。

欠点:

  • スタイルシートの方がより柔軟で強力です。
  • 変更できるのは色のみで、複雑なグラフィック描画には向きません。

コード例:

// main.cpp (抜粋)
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTreeView treeView;
    QStandardItemModel model;
    // ... モデルのセットアップ ...

    QPalette palette = treeView.palette();
    palette.setColor(QPalette::Base, QColor(240, 255, 240)); // 背景色(緑がかった白)
    palette.setColor(QPalette::AlternateBase, QColor(220, 255, 220)); // 交互の行の背景色
    palette.setColor(QPalette::Text, Qt::darkGreen); // テキスト色
    palette.setColor(QPalette::Highlight, QColor(150, 200, 150)); // 選択時の背景色
    palette.setColor(QPalette::HighlightedText, Qt::white); // 選択時のテキスト色
    treeView.setPalette(palette);

    // ... ツリービューの表示 ...
    return a.exec();
}

目的: paintEvent() または QAbstractItemDelegate::paint() 内で、複雑な形状や曲線の描画を効率的に行いたい場合。

説明: QPainterPath は、閉じた図形や複雑な線分、曲線などを表現するためのクラスです。パスを作成し、それを QPainter で塗りつぶしたり、描画したりすることで、効率的に複雑なグラフィックをレンダリングできます。これは直接的な代替方法というよりは、paintEvent() やデリゲートの paint() メソッド内で使用する「ツール」の一つです。

利点:

  • 描画のパフォーマンス向上。
  • 複雑な形状の組み合わせや操作が可能。
  • アンチエイリアシングが適用された滑らかな描画が容易。

欠点:

  • QPainterPath の概念を理解する必要がある。