QTreeView::visualRegionForSelection() でQtアプリのUIを強化する方法

2025-05-16

この関数は、QTreeViewクラスが持つメソッドで、現在選択されているアイテム群がビューポート上で占める領域をQRegionとして返します。

具体的に見ていきましょう。

  • visualRegionForSelection(const QItemSelection &selection) const: このメソッドは、QAbstractItemViewクラス(QTreeViewの基底クラス)で定義されている仮想関数で、QTreeViewでオーバーライドされています。

    • 引数としてQItemSelectionオブジェクトを受け取ります。これは、ビュー内で選択されているアイテムの集合を表します。
    • 戻り値としてQRegionを返します。このQRegionは、引数で渡されたQItemSelectionがビューポート上で実際に表示されている領域(ピクセル座標での矩形集合)を表します。
  • QTreeView: QTreeViewは、QtのModel/Viewアーキテクチャにおけるビューコンポーネントの一つで、ツリー構造のデータを表示するために使用されます。ファイルシステムのエクスプローラのような、階層的なデータを表示する際に利用されます。

  • QRegion: QRegionは、矩形(長方形)の集合を表すQtのクラスです。複数の不連続な矩形を効率的に管理し、それらの結合や共通部分の算出などを行うことができます。グラフィックス描画において、特定の領域を塗りつぶしたり、クリッピング領域として使用したりする際に便利です。

この関数がなぜ役立つのか?

  • スクロール処理の最適化: 選択されているアイテムがビューポート外にスクロールされた際に、そのアイテムが完全にビューポートに入るようにスクロール位置を調整するなどの処理に役立ちます。
  • ドラッグ&ドロップの視覚的フィードバック: ドラッグ中のアイテムがどの領域にドロップされるかを示す視覚的なインジケータを表示する際に、選択領域の情報を利用できます。
  • 部分的な再描画: 選択範囲が変更された際に、変更された部分(新しい選択範囲と古い選択範囲)だけを効率的に再描画するのに利用できます。これにより、描画パフォーマンスが向上します。
  • 選択範囲のハイライト: 選択されているアイテムの領域を取得し、その領域を特定の色でハイライト表示する際に使用できます。


この関数自体が直接エラーを発生させることは稀ですが、期待通りの結果が得られない、あるいは関連する描画処理で問題が発生するといったケースが考えられます。

よくある問題と原因

    • 原因:
      • 何も選択されていない: 最も単純な原因です。QTreeViewで何もアイテムが選択されていない場合、visualRegionForSelection()は空のQRegionを返します。
      • 選択されたアイテムがビューポート外にある: 選択されたアイテムがスクロールによって現在のビューポート(表示領域)から完全に外れている場合、そのアイテムは視覚的に存在しないため、空のQRegionが返されます。
      • 無効なモデル/インデックス: QTreeViewが有効なモデルを持っていなかったり、選択しているインデックスがモデル上で無効になっていたりする場合、正しい領域が計算できないことがあります。
      • QTreeViewの可視性: QTreeView自体が非表示(hide()されている、親ウィジェットが非表示、isVisible()falseなど)の場合、視覚的な領域は存在しません。
    • トラブルシューティング:
      • QTreeView::selectionModel()->selectedIndexes()QTreeView::selectionModel()->selectedRows()などを呼び出して、実際に選択されているインデックスがあるかを確認します。
      • QTreeView::scrollTo(index, QAbstractItemView::PositionAtCenter)などで、選択されているアイテムがビューポート内に見えるようにスクロールしてからvisualRegionForSelection()を呼び出してみます。
      • QTreeViewのモデルが正しく設定されているか、データが有効であるかを確認します。
      • QTreeViewとその親ウィジェットが可視状態であるかを確認します。
  1. QRegionが期待よりも小さい、あるいは大きい

    • 原因:
      • 部分的にのみ表示されているアイテム: アイテムがビューポートの端にあり、完全に表示されていない場合、返されるQRegionはその表示されている部分のみとなります。
      • ヘッダー/スクロールバーの領域: QRegionが計算される際に、ヘッダーやスクロールバーの領域が意図せず含まれたり、除外されたりする場合があります。通常、visualRegionForSelection()はアイテム自体のみを考慮しますが、複雑なレイアウトでは予期せぬオフセットが生じることがあります。
      • 高DPI設定: 高DPI環境では、ピクセル値がスケールされるため、直接ピクセル値を扱う場合にズレが生じる可能性があります。Qtは通常これを透過的に処理しますが、手動でピクセル座標を計算している場合に問題となることがあります。
    • トラブルシューティング:
      • 選択されているアイテムが完全に表示されている状態でテストします。
      • QTreeView::viewport()->mapToGlobal()QTreeView::mapToGlobal()などを使用して、QRegionの座標系が意図した通りになっているか(ローカル座標かグローバル座標か)を確認します。
      • 高DPI環境で問題が発生する場合は、QtのDPIスケーリング設定が適切に適用されているか確認します。
  2. 描画結果がQRegionと一致しない

    • 原因:
      • 描画コンテキストの不一致: QRegionを取得した後に、異なるウィジェットや描画デバイスに対して描画を行っている場合、座標系が一致しないためズレが生じます。
      • 再描画のトリガー忘れ: QRegionを使って領域をハイライト表示する場合など、update()repaint()を呼び出してウィジェットの再描画を明示的にトリガーしないと、変更が画面に反映されません。
      • カスタムデリゲートとの相互作用: カスタムデリゲートを使用している場合、アイテムの描画領域が標準のQTreeViewの計算と異なることがあります。
    • トラブルシューティング:
      • QRegionを適用する描画処理が、QTreeViewのビューポート(QTreeView::viewport())またはQTreeView自体に対して行われていることを確認します。
      • 描画コードの後にQTreeView->viewport()->update(region)QTreeView->update(region)など、必要な領域のみを再描画する呼び出しを追加します。
      • カスタムデリゲートを使用している場合は、QStyledItemDelegate::sizeHint()QStyledItemDelegate::paint()の実装がQTreeViewのレイアウトと整合しているかを確認します。
  3. パフォーマンスの問題

    • 原因:
      • visualRegionForSelection()自体は通常高速ですが、非常に多くのアイテムが選択されており、かつそれらが不連続に配置されている場合、QRegionの計算コストが大きくなる可能性があります。
      • 取得したQRegionに対して、複雑なグラフィックス操作を頻繁に行っている場合、その操作自体がボトルネックとなることがあります。
    • トラブルシューティング:
      • 不必要なvisualRegionForSelection()の呼び出しを避けます。選択範囲の変更時など、必要なときにのみ呼び出すようにします。
      • QRegionが非常に複雑になるようなケースでは、描画の最適化を検討します。例えば、選択範囲が広すぎる場合は、QRegion全体をハイライトするのではなく、境界線のみを描画するなど。
  • Qtのドキュメント: QTreeViewQRegionQAbstractItemViewの公式ドキュメントを再確認し、特にvisualRegionForSelection()に関する注意点や関連する関数を調べます。
  • 最小限の再現コード: 問題が発生する最小限のコードスニペットを作成し、問題を切り分けます。
  • イベントフィルタ: QEvent::Paintなどのイベントを監視し、QTreeViewの描画がいつ、どのように行われているかを把握します。
  • デバッグ出力: qDebug()を使って、QRegionboundingRect()rects()を調べ、返された領域の座標やサイズが期待通りかを確認します。
    QRegion selectionRegion = treeView->visualRegionForSelection(treeView->selectionModel()->selection());
    if (selectionRegion.isEmpty()) {
        qDebug() << "Selection region is empty.";
    } else {
        qDebug() << "Selection region bounding rect:" << selectionRegion.boundingRect();
        // より詳細な矩形のリストも確認できます (複雑なQRegionの場合)
        for (const QRect& rect : selectionRegion.rects()) {
            qDebug() << "  Rect:" << rect;
        }
    }
    


選択範囲のカスタムハイライト(QTreeViewのサブクラス化)

QTreeViewをサブクラス化し、paintEventをオーバーライドして、選択されたアイテムの周囲にカスタムの枠線を描画する例です。

MyTreeView.h

#ifndef MYTREEVIEW_H
#define MYTREEVIEW_H

#include <QTreeView>
#include <QStandardItemModel> // 例としてQStandardItemModelを使用

class MyTreeView : public QTreeView
{
    Q_OBJECT
public:
    explicit MyTreeView(QWidget *parent = nullptr);

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

#endif // MYTREEVIEW_H

MyTreeView.cpp

#include "MyTreeView.h"
#include <QPainter>
#include <QDebug>
#include <QPaintEvent>
#include <QRegion>
#include <QPen>

MyTreeView::MyTreeView(QWidget *parent)
    : QTreeView(parent)
{
    // モデルを設定 (例としてシンプルな文字列リスト)
    QStandardItemModel *model = new QStandardItemModel(0, 1, this);
    model->setHeaderData(0, Qt::Horizontal, "アイテム");
    for (int i = 0; i < 5; ++i) {
        QStandardItem *item = new QStandardItem(QString("アイテム %0").arg(i + 1));
        if (i == 1 || i == 3) { // 複数のアイテムに子を追加
            item->appendRow(new QStandardItem(QString("サブアイテム %0.1").arg(i + 1)));
            item->appendRow(new QStandardItem(QString("サブアイテム %0.2").arg(i + 1)));
        }
        model->appendRow(item);
    }
    setModel(model);

    // 複数選択を可能にする
    setSelectionMode(QAbstractItemView::ExtendedSelection);
}

void MyTreeView::paintEvent(QPaintEvent *event)
{
    // デフォルトの描画処理を呼び出す
    QTreeView::paintEvent(event);

    QPainter painter(viewport()); // ビューポートに描画
    painter.setRenderHint(QPainter::Antialiasing);

    // 現在の選択範囲のQItemSelectionを取得
    QItemSelection selection = selectionModel()->selection();

    // 選択範囲の視覚的な領域(QRegion)を取得
    QRegion selectedRegion = visualRegionForSelection(selection);

    if (!selectedRegion.isEmpty()) {
        qDebug() << "Selected Region Bounding Rect:" << selectedRegion.boundingRect();

        // カスタムハイライトの描画
        QPen pen(Qt::red); // 赤色のペン
        pen.setWidth(2);    // 太さを2ピクセルに
        painter.setPen(pen);
        painter.setBrush(Qt::NoBrush); // 塗りつぶしなし

        // QRegionを構成する各矩形を辿って描画
        for (const QRect &rect : selectedRegion.rects()) {
            painter.drawRect(rect.adjusted(0, 0, -1, -1)); // 1px内側に描画して見た目を調整
        }
    }
}

main.cpp

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

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

    MyTreeView treeView;
    treeView.setWindowTitle("QTreeView Custom Selection Highlight");
    treeView.resize(400, 300);
    treeView.show();

    return a.exec();
}

説明

  1. MyTreeViewQTreeViewから派生させます。
  2. paintEvent(QPaintEvent *event)をオーバーライドします。
  3. まず、QTreeView::paintEvent(event);を呼び出し、Qtが提供する通常のツリービュー描画を行わせます。
  4. selectionModel()->selection()で現在の選択モデルからQItemSelectionを取得します。
  5. visualRegionForSelection(selection)を呼び出して、この選択範囲が画面上で占めるQRegionを取得します。
  6. QRegionが空でないことを確認し、QPainterを使用してその領域にカスタムの描画(この例では赤い枠線)を行います。QRegionは複数の矩形から構成される場合があるため、rects()で個々の矩形を取得し、それぞれを描画しています。

ユーザーが選択したアイテムがビューポートの端にある場合、そのアイテムが完全に表示されるように自動的にスクロールする例です。これは、visualRegionForSelection()を直接使うよりも、QAbstractItemView::scrollTo()を使う方が一般的ですが、visualRegionForSelection()の情報を元にカスタムなスクロールロジックを実装することも可能です。

// 例えば、MainWindowなどのクラスのメンバー関数として
void MyWidget::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
    // MyTreeViewのインスタンスがあると仮定
    if (myTreeView->selectionModel()->hasSelection()) {
        QRegion selectedRegion = myTreeView->visualRegionForSelection(myTreeView->selectionModel()->selection());

        if (!selectedRegion.isEmpty()) {
            QRect selectionRect = selectedRegion.boundingRect(); // 選択領域全体のバウンディングボックス

            // ビューポートの可視領域
            QRect viewportRect = myTreeView->viewport()->rect();

            // 選択領域がビューポート内に完全に収まっているかチェック
            if (!viewportRect.contains(selectionRect)) {
                // 選択領域がビューポート内に完全に収まっていない場合、スクロールを検討

                // 例: 選択領域の左上がビューポート外にある場合、その位置までスクロール
                if (selectionRect.topLeft().x() < viewportRect.topLeft().x()) {
                    myTreeView->horizontalScrollBar()->setValue(myTreeView->horizontalScrollBar()->value() + (selectionRect.topLeft().x() - viewportRect.topLeft().x()));
                }
                if (selectionRect.topLeft().y() < viewportRect.topLeft().y()) {
                    myTreeView->verticalScrollBar()->setValue(myTreeView->verticalScrollBar()->value() + (selectionRect.topLeft().y() - viewportRect.topLeft().y()));
                }
                // 例: 選択領域の右下がビューポート外にある場合
                if (selectionRect.bottomRight().x() > viewportRect.bottomRight().x()) {
                    myTreeView->horizontalScrollBar()->setValue(myTreeView->horizontalScrollBar()->value() + (selectionRect.bottomRight().x() - viewportRect.bottomRight().x()));
                }
                if (selectionRect.bottomRight().y() > viewportRect.bottomRight().y()) {
                    myTreeView->verticalScrollBar()->setValue(myTreeView->verticalScrollBar()->value() + (selectionRect.bottomRight().y() - viewportRect.bottomRight().y()));
                }
            }
        }
    }
}

// 呼び出し元 (例: コンストラクタや初期化関数)
// myTreeView->selectionModel()->selectionChanged.connect(this, &MyWidget::onSelectionChanged);

説明

  1. selectionModel()->selectionChangedシグナルにスロットを接続し、選択が変更されたときにonSelectionChangedが呼び出されるようにします。
  2. visualRegionForSelection()で選択領域のQRegionを取得します。
  3. QRegion::boundingRect()で選択領域全体の最小の矩形を取得します。
  4. QTreeViewのビューポートの矩形(可視領域)と選択領域の矩形を比較し、必要に応じてスクロールバーの値を調整します。
  • カスタムデリゲート: カスタムデリゲートを使用してアイテムの描画サイズや位置を大幅に変更している場合、visualRegionForSelection()が返す領域が期待と異なる可能性があります。Qtの標準的な計算ロジックに基づいているため、デリゲートのsizeHint()paint()の実装がQTreeViewのレイアウトに正確に反映されていることが重要です。
  • パフォーマンス: visualRegionForSelection()の呼び出し自体は比較的軽量ですが、非常に多くのアイテムが選択されている場合や、この関数を頻繁に呼び出すような複雑な描画ロジックを実装する場合は、パフォーマンスへの影響を考慮する必要があります。
  • 描画コンテキスト: visualRegionForSelection()が返すQRegionの座標は、QTreeViewのビューポート(viewport())に対するローカル座標です。この領域を使って描画を行う場合は、QPainterviewport()に対して作成するか、またはQTreeView::mapToParent()QTreeView::mapToGlobal()を使って座標を変換する必要があります。


以下に、visualRegionForSelection() の代替となるプログラミング手法をいくつかご紹介します。

QAbstractItemView::visualRect() を個別に利用する

いつ使うか?

  • 選択されたアイテムが連続しているとわかっている場合(この場合、visualRegionForSelection()でも単一の矩形に近いものが返される可能性はありますが、visualRect()で開始と終了のインデックスの矩形を取得し、その範囲で描画する方が直感的かもしれません)。
  • 選択されたアイテムが比較的少なく、QRegionの結合処理が不要な場合。
  • 個々の選択されたアイテムに対して、より詳細な描画や処理を行いたい場合。


選択された各アイテムの周囲に個別のボーダーを描画する(QTreeViewのサブクラスのpaintEvent内)

#include <QPainter>
#include <QTreeView>
#include <QModelIndex>
#include <QItemSelectionModel>
#include <QDebug>

// ... QTreeViewのサブクラスのpaintEvent内 ...
void MyTreeView::paintEvent(QPaintEvent *event)
{
    QTreeView::paintEvent(event); // デフォルトの描画

    QPainter painter(viewport());
    painter.setPen(Qt::blue); // 青いペン
    painter.setBrush(Qt::NoBrush);

    // 選択モデルから選択されたすべてのインデックスを取得
    QModelIndexList selectedIndexes = selectionModel()->selectedIndexes();

    // 重複を避けるためにセットを使用(特に複数列選択の場合、同じ行のインデックスが複数含まれる可能性があるため)
    // 通常、ツリービューでは行全体が選択されることが多いので、各行の最初の列のインデックスのみを考慮する
    QSet<QPersistentModelIndex> processedRows;

    for (const QModelIndex &index : selectedIndexes) {
        // 重複する行の描画を避ける
        QPersistentModelIndex persistentIndex = index.sibling(index.row(), 0); // その行の最初の列のインデックス
        if (processedRows.contains(persistentIndex)) {
            continue;
        }
        processedRows.insert(persistentIndex);

        // そのアイテムの視覚的な矩形を取得
        // QTreeViewの場合、通常は行全体をカバーする矩形を返します。
        // ただし、列が非表示になっている場合などは注意が必要です。
        QRect itemRect = visualRect(index);

        if (!itemRect.isEmpty() && viewport()->rect().intersects(itemRect)) {
            qDebug() << "Drawing rect for index:" << index.row() << "," << index.column() << ":" << itemRect;
            painter.drawRect(itemRect.adjusted(0, 0, -1, -1)); // 1px内側に描画
        }
    }
}

利点

  • 個々のアイテムに対して異なる描画ロジックを適用できる。
  • 各アイテムの境界を正確に取得できる。

欠点

  • 不連続な選択範囲の全体像を一度に把握するのには向かない。
  • 多数の選択されたアイテムがある場合、ループ処理によるパフォーマンスオーバーヘッドがある。

QStyledItemDelegate を利用したカスタム描画

QStyledItemDelegate をサブクラス化し、paint() メソッドをオーバーライドすることで、アイテムの描画方法を完全に制御できます。これにより、選択状態に応じたカスタムのハイライトを実装できます。

いつ使うか?

  • 選択状態のハイライトだけでなく、アイテム全体にわたるより複雑な描画(例: プログレスバー、チェックボックス以外のカスタムコントロールなど)を行いたい場合。
  • QTreeViewのサブクラス化によるpaintEventのオーバーライドよりも、よりモジュール化された方法で描画をカスタマイズしたい場合。
  • アイテムの見た目(背景色、文字色、アイコンなど)を、選択状態だけでなく、他のモデルデータやカスタムロジックに基づいて変更したい場合。


選択されたアイテムの背景色と文字色を変更する

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 <QApplication>
#include <QPalette> // QPalette::Highlightなどの色を取得するため

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

void MyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem opt = option; // optionはconstなのでコピーして変更
    initStyleOption(&opt, index); // スタイルオプションを初期化

    // 選択されているかチェック
    if (opt.state & QStyle::State_Selected) {
        // 選択された背景色をカスタムで設定
        painter->fillRect(opt.rect, opt.palette.highlight()); // デフォルトのハイライト色を使用
        // あるいは、任意のカスタム色: painter->fillRect(opt.rect, Qt::darkBlue);

        // 選択されたテキスト色をカスタムで設定
        painter->setPen(opt.palette.highlightedText().color()); // デフォルトのハイライトテキスト色を使用
        // あるいは、任意のカスタム色: painter->setPen(Qt::white);
    } else {
        // 通常の背景色とテキスト色
        painter->fillRect(opt.rect, opt.palette.base());
        painter->setPen(opt.palette.text().color());
    }

    // アイコンの描画
    if (!opt.icon.isNull()) {
        QSize iconSize = opt.icon.actualSize(opt.icon.availableSizes().isEmpty() ? opt.decorationSize : opt.icon.availableSizes().first());
        QRect iconRect = QRect(opt.rect.topLeft(), iconSize);
        iconRect.moveLeft(opt.rect.left() + opt.decorationPosition.x()); // アイコンのパディングなど考慮
        iconRect.moveTop(opt.rect.top() + (opt.rect.height() - iconSize.height()) / 2);
        opt.icon.paint(painter, iconRect, opt.decorationAlignment);
    }

    // テキストの描画
    if (!opt.text.isEmpty()) {
        // アイコンの幅とパディングを考慮してテキストを描画する領域を調整
        QRect textRect = opt.rect;
        if (!opt.icon.isNull()) {
            textRect.setLeft(textRect.left() + opt.decorationSize.width() + 4); // 適当なパディング
        }
        painter->drawText(textRect, opt.displayAlignment, opt.text);
    }
    // 注意: カスタムデリゲートで完全に描画を制御する場合、
    // QStyledItemDelegate::paint(painter, option, index); を呼び出すと
    // デフォルトの描画が二重に行われる可能性があるため、通常は呼び出しません。
    // その代わりに、必要な要素(背景、アイコン、テキストなど)を自分で描画します。
}

main.cpp (QTreeViewにデリゲートを設定)

#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include "MyItemDelegate.h" // 作成したデリゲート

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

    QTreeView treeView;
    QStandardItemModel *model = new QStandardItemModel(0, 1, &treeView);
    model->setHeaderData(0, Qt::Horizontal, "アイテム");
    for (int i = 0; i < 5; ++i) {
        QStandardItem *item = new QStandardItem(QString("アイテム %0").arg(i + 1));
        if (i == 1 || i == 3) {
            item->appendRow(new QStandardItem(QString("サブアイテム %0.1").arg(i + 1)));
            item->appendRow(new QStandardItem(QString("サブアイテム %0.2").arg(i + 1)));
        }
        model->appendRow(item);
    }
    treeView.setModel(model);

    // カスタムデリゲートを設定
    MyItemDelegate *delegate = new MyItemDelegate(&treeView);
    treeView.setItemDelegate(delegate);

    treeView.setWindowTitle("QTreeView Custom Delegate Highlight");
    treeView.resize(400, 300);
    treeView.show();

    return a.exec();
}

利点

  • QTreeViewpaintEventを汚染しないため、コードの分離性が高い。
  • 選択状態だけでなく、他のデータや状態に基づく複雑な描画ロジックをカプセル化できる。
  • アイテムの描画を細かく制御できる。

欠点

  • Qtのデフォルト描画ロジック(テーマやスタイルによる見た目)を部分的に再実装する必要があるため、注意が必要。
  • 単純なハイライトの場合、コード量が多くなる可能性がある。

最も手軽に見た目を変更できる方法です。QTreeView::item セレクタに対して selection-background-colorselection-color などのプロパティを設定することで、選択時の見た目を変更できます。

いつ使うか?

  • コード内で複雑な描画ロジックを実装することなく、見た目だけを変更したい場合。 * アプリケーション全体で一貫したスタイルを適用したい場合。


#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>

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

    QTreeView treeView;
    QStandardItemModel *model = new QStandardItemModel(0, 1, &treeView);
    model->setHeaderData(0, Qt::Horizontal, "アイテム");
    for (int i = 0; i < 5; ++i) {
        QStandardItem *item = new QStandardItem(QString("アイテム %0").arg(i + 1));
        if (i == 1 || i == 3) {
            item->appendRow(new QStandardItem(QString("サブアイテム %0.1").arg(i + 1)));
            item->appendRow(new QStandardItem(QString("サブアイテム %0.2").arg(i + 1)));
        }
        model->appendRow(item);
    }
    treeView.setModel(model);

    // QSSを設定
    treeView.setStyleSheet(
        "QTreeView::item:selected {"
        "    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FF6600, stop: 1 #CC3300);" // グラデーション背景
        "    color: white;" // テキスト色
        "}"
        "QTreeView::item:selected:!active {" // フォーカスがないが選択されている場合
        "    background-color: #A0A0A0;"
        "    color: black;"
        "}"
        // 枝のスタイル(任意)
        "QTreeView::branch:open {"
        "    image: url(:/icons/open_branch.png);" // カスタムの開いた枝のアイコン
        "}"
        "QTreeView::branch:closed {"
        "    image: url(:/icons/closed_branch.png);" // カスタムの閉じた枝のアイコン
        "}"
    );

    treeView.setWindowTitle("QTreeView QSS Highlight");
    treeView.resize(400, 300);
    treeView.show();

    return a.exec();
}

利点

  • Qtのスタイルシステムに統合されており、プラットフォーム固有の見た目をある程度維持できる。
  • コードの変更が不要で、スタイルシートファイルとして外部化できる。
  • 最も簡単かつ迅速に見た目を変更できる。
  • 複雑な描画(例: テキストの一部のハイライト、カスタムアニメーションなど)はできない。
  • 描画ロジックに細かく介入することはできない(例えば、選択範囲の特定のピクセルだけを塗りつぶすなど)。
  • QRegionのような厳密なピクセル領域情報を取得することはできない。