1. #!trpst#trp-gettext data-...
  2. #!trpst#trp-gettext data-...
  3. QT
  4. Genel
  5. Model/View Mimarisi: QTableView ve QAbstractTableModel ile Büyük Veri Yönetimi

Model/View Mimarisi: QTableView ve QAbstractTableModel ile Büyük Veri Yönetimi

Print

Qt GUI uygulamalarında tablo ve listelerle çalışırken acemi geliştiricilerin düştüğü en büyük tuzak QTableWidget kullanmaktır. 10.000 veya 100.000 satırlık bir veri kümesini QTableWidget içerisine tek tek QTableWidgetItem nesneleri olarak eklemeye kalktığınızda, yüz binlerce C++ nesnesi oluşturulur, bellek tükenir ve arayüz saniyelerce donar. Kurumsal ve yüksek performanslı uygulamalarda Qt Model View mimarisi kullanmak; veriyi arayüzden tamamen ayırarak yalnızca ekranda o an görünen hücrelerin çizilmesini (Lazy Loading / Virtualization) sağlar.

Bu rehberde; Model/View/Delegate bileşenlerini, QAbstractTableModel sınıfından özel bir model türetmeyi, rowCount, columnCount, data ve headerData fonksiyonlarını implemente etmeyi ve QTableView ile milyonlarca satırı sıfır gecikmeyle listelemeyi öğreneceğiz.


Model / View / Delegate Mantığı

Qt’nin Model/View mimarisi üç temel sacayağından oluşur:

  1. Model (QAbstractItemModel): Verinin kaynağıdır (Vektör, Veritabanı, Ağ akışı). Veriyi saklar ve View’a standart bir indeks (QModelIndex) üzerinden sunar.
  2. View (QTableView, QListView, QTreeView): Veriyi ekranda temsil eder. Verinin nerede ve nasıl saklandığını bilmez; yalnızca ekranda görünen hücreler için Model’den veri talep eder.
  3. Delegate (QStyledItemDelegate): Hücrelerin nasıl çizileceğini (ProgressBar, Renkli İkon vb.) ve kullanıcı düzenleme (Edit) yaptığında hangi editörün açılacağını (SpinBox, ComboBox) kontrol eder.
KarşılaştırmaKolaylık Odaklı (QTableWidget)Profesyonel Mimari (QTableView + Model)
Bellek TüketimiÇok Yüksek (Her hücre için ayrı nesne)Minimum (Sadece ham C++ struct belleği)
Büyük Veri Hızı5.000 satırda takılır1.000.000 satırda dahi 60 FPS akıcı
Veri EntegrasyonuVeriyi tabloya kopyalamak gerekirMevcut std::vector doğrudan bağlanabilir
Mimari AyrımVeri ve Arayüz birbirine karışırTemiz MVVM / MVC ayrımı sağlanır

Adım Adım Özel Tablo Modeli Geliştirme

Örnek Senaryo: Sistemde izlenen ağ loglarını veya telemetri paketlerini (PacketLog) yüksek performansla listeleyen bir model kuralım.

1. Veri Yapısı (Data Struct)

#include <QString>
#include <QDateTime>

struct PacketLog {
    quint64 id;
    QDateTime timestamp;
    QString sourceIp;
    QString destinationIp;
    int port;
    int payloadSize;
};

2. Model Başlık Dosyası: packetlogmodel.h

#ifndef PACKETLOGMODEL_H
#define PACKETLOGMODEL_H

#include <QAbstractTableModel>
#include <vector>

class PacketLogModel : public QAbstractTableModel {
    Q_OBJECT

public:
    enum Column {
        ColId = 0,
        ColTime,
        ColSource,
        ColDestination,
        ColPort,
        ColSize,
        ColumnCount
    };

    explicit PacketLogModel(QObject *parent = nullptr);

    // QAbstractTableModel zorunlu sanal fonksiyonları
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

    // Veri yönetimi fonksiyonları
    void setLogs(const std::vector<PacketLog> &logs);
    void addLog(const PacketLog &log);
    void clear();

private:
    std::vector<PacketLog> m_logs;
};

#endif // PACKETLOGMODEL_H

3. Model Kaynak Dosyası: packetlogmodel.cpp

#include "packetlogmodel.h"
#include <QColor>

PacketLogModel::PacketLogModel(QObject *parent) : QAbstractTableModel(parent) {}

int PacketLogModel::rowCount(const QModelIndex &parent) const {
    if (parent.isValid()) return 0;
    return static_cast<int>(m_logs.size());
}

int PacketLogModel::columnCount(const QModelIndex &parent) const {
    if (parent.isValid()) return 0;
    return ColumnCount;
}

QVariant PacketLogModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid() || index.row() >= static_cast<int>(m_logs.size()))
        return QVariant();

    const PacketLog &item = m_logs[index.row()];

    // 1. Ekranda görünecek metin (DisplayRole)
    if (role == Qt::DisplayRole) {
        switch (index.column()) {
            case ColId:          return item.id;
            case ColTime:        return item.timestamp.toString("yyyy-MM-dd hh:mm:ss.zzz");
            case ColSource:      return item.sourceIp;
            case ColDestination: return item.destinationIp;
            case ColPort:        return item.port;
            case ColSize:        return QString("%1 B").arg(item.payloadSize);
            default: break;
        }
    }

    // 2. Metin hizalaması (TextAlignmentRole)
    if (role == Qt::TextAlignmentRole) {
        if (index.column() == ColId || index.column() == ColPort || index.column() == ColSize)
            return int(Qt::AlignRight | Qt::AlignVCenter);
        return int(Qt::AlignLeft | Qt::AlignVCenter);
    }

    // 3. Arka plan rengiyle kritik paketleri vurgulama (BackgroundRole)
    if (role == Qt::BackgroundRole) {
        // Eğer port 22 (SSH) veya tehlikeli ise açık kırmızı ton yap
        if (item.port == 22) {
            return QColor(255, 235, 238); // Açık kırmızı
        }
    }

    return QVariant();
}

QVariant PacketLogModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
        switch (section) {
            case ColId:          return "Paket ID";
            case ColTime:        return "Zaman";
            case ColSource:      return "Kaynak IP";
            case ColDestination: return "Hedef IP";
            case ColPort:        return "Port";
            case ColSize:        return "Boyut";
            default: break;
        }
    }
    return QVariant();
}

void PacketLogModel::setLogs(const std::vector<PacketLog> &logs) {
    // Model verisi kökten değiştiğinde View'a bildirilir
    beginResetModel();
    m_logs = logs;
    endResetModel();
}

void PacketLogModel::addLog(const PacketLog &log) {
    // Yeni tek bir satır eklenirken performansı korumak için beginInsertRows kullanılır!
    int newRow = static_cast<int>(m_logs.size());
    beginInsertRows(QModelIndex(), newRow, newRow);
    m_logs.push_back(log);
    endInsertRows();
}

void PacketLogModel::clear() {
    beginResetModel();
    m_logs.clear();
    endResetModel();
}

Modeli QTableView’e Bağlamak ve Optimizasyonlar

Model hazır olduktan sonra arayüzde bir QTableView ile eşleştirmek birkaç satırdır:

#include <QTableView>
#include <QHeaderView>

// Model ve View nesnelerini oluştur
PacketLogModel *model = new PacketLogModel(this);
QTableView *tableView = new QTableView(this);

// Modeli View'a ata
tableView->setModel(model);

// Performans Ayarları (Büyük veri için kritik!)
tableView->verticalHeader()->setVisible(false); // Satır numaralarını gizle (performans artar)
tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // Tüm satırı seç
tableView->setSelectionMode(QAbstractItemView::SingleSelection);
tableView->horizontalHeader()->setStretchLastSection(true); // Son sütunu alana yay

// Alternatif satır renklendirmesi (Zebra efekti)
tableView->setAlternatingRowColors(true);

Önemli İpucu: Eğer canlı olarak saniyede yüzlerce paket akıyorsa, her paket için tek tek addLog çağırmak yerine gelen paketleri bir kuyrukta toplayıp (batching) saniyede 10-20 defa toplu eklemek CPU yükünü sıfıra indirir.


Sıralama ve Arama: QSortFilterProxyModel

Veritabanına veya orijinal vektöre dokunmadan arayüzde sütun başlığına tıklandığında sıralama (sort) veya filtreleme (search box) eklemek için araya bir QSortFilterProxyModel yerleştirilir:

#include <QSortFilterProxyModel>

QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(model);

// View'a artık ham model yerine proxy model atanır
tableView->setModel(proxyModel);
tableView->setSortingEnabled(true); // Sütunlara tıklayarak sıralamayı aç

Özet

QTableView ve QAbstractTableModel ikilisi, veri odaklı Qt uygulamalarında hızın ve kararlılığın anahtarıdır. Doğru roller (DisplayRole, TextAlignmentRole, BackgroundRole) ve beginInsertRows sinyalleriyle yüz binlerce satırı bellek şişmesi yaşamadan profesyonelce sunabilirsiniz.

Tags , , , , ,

How can we help?