Mobil cihazlarda, gömülü dokunmatik panellerde ve modern masaüstü uygulamalarında binlerce satırlık listeleri (rehber, şarkı listesi, telemetri logları, ağ cihazları) parmak hareketleriyle kaydırırken en ufak bir takılma (jank/stutter) kullanıcı deneyimini doğrudan bozar. Bu tür akıcı arayüzlerde QML ListView C++ modeli entegrasyonu; QML’in esnek delegasyon tasarımını C++’ın sıfır ek yüklü bellek performansı ile harmanlayarak kusursuz 60 FPS kaydırma performansı sunar.
Bu rehberde; QAbstractListModel sınıfını türetmeyi, QML’deki değişken adlarını belirleyen roleNames() hash tablosunu kurmayı, ListView ve delegate yapısını ve bellek tüketimini kontrol altında tutan cacheBuffer optimizasyonlarını adım adım işleyeceğiz.
QML ListView Mimarisi
QML’deki bir liste görünümü üç bileşenden meydana gelir:
- Model (
QAbstractListModel): Verilerin C++ tarafında tutulduğu dinamik koleksiyon. - View (
ListView): Kaydırma, fırlatma (flickable) ve ekran içi görünürlük hesaplarını yöneten QML ögesi. - Delegate: Listedeki tek bir satırın ekranda nasıl görüneceğini (kart, buton, ikonlar) belirleyen görsel şablon.
Adım 1: C++ Veri Modeli (DeviceListModel)
Örnek Senaryo: Bir IoT ağında bulunan bağlı akıllı cihazları (Device) listeleyen bir model kuralım.
Veri Yapısı (Device)
#include <QString>
struct Device {
QString name;
QString ipAddress;
bool isOnline;
int batteryLevel;
};
devicelistmodel.h
#ifndef DEVICELISTMODEL_H
#define DEVICELISTMODEL_H
#include <QAbstractListModel>
#include <vector>
class DeviceListModel : public QAbstractListModel {
Q_OBJECT
public:
// QML tarafında doğrudan 'name', 'ip', 'online', 'battery' olarak kullanılacak roller
enum DeviceRoles {
NameRole = Qt::UserRole + 1,
IpRole,
OnlineRole,
BatteryRole
};
explicit DeviceListModel(QObject *parent = nullptr);
// QAbstractListModel Zorunlu Metotları
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
// Veri Manipülasyon Metotları
Q_INVOKABLE void addDevice(const QString &name, const QString &ip, bool online, int battery);
Q_INVOKABLE void removeDevice(int index);
private:
std::vector<Device> m_devices;
};
#endif // DEVICELISTMODEL_H
devicelistmodel.cpp
#include "devicelistmodel.h"
DeviceListModel::DeviceListModel(QObject *parent) : QAbstractListModel(parent) {
// Örnek başlangıç verileri
m_devices.push_back({"Gateway Hub", "192.168.1.1", true, 100});
m_devices.push_back({"Sıcaklık Sensörü", "192.168.1.45", true, 84});
m_devices.push_back({"Akıllı Röle", "192.168.1.80", false, 0});
}
int DeviceListModel::rowCount(const QModelIndex &parent) const {
if (parent.isValid()) return 0;
return static_cast<int>(m_devices.size());
}
QVariant DeviceListModel::data(const QModelIndex &index, int role) const {
if (!index.isValid() || index.row() >= static_cast<int>(m_devices.size()))
return QVariant();
const Device &device = m_devices[index.row()];
switch (role) {
case NameRole: return device.name;
case IpRole: return device.ipAddress;
case OnlineRole: return device.isOnline;
case BatteryRole: return device.batteryLevel;
default: break;
}
return QVariant();
}
// QML'e rollerin hangi string isimle bağlanacağını bildirir (Kritik!)
QHash<int, QByteArray> DeviceListModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[IpRole] = "ipAddress";
roles[OnlineRole] = "isOnline";
roles[BatteryRole] = "batteryLevel";
return roles;
}
void DeviceListModel::addDevice(const QString &name, const QString &ip, bool online, int battery) {
int newIndex = static_cast<int>(m_devices.size());
beginInsertRows(QModelIndex(), newIndex, newIndex);
m_devices.push_back({name, ip, online, battery});
endInsertRows();
}
void DeviceListModel::removeDevice(int index) {
if (index < 0 || index >= static_cast<int>(m_devices.size())) return;
beginRemoveRows(QModelIndex(), index, index);
m_devices.erase(m_devices.begin() + index);
endRemoveRows();
}
Adım 2: Modeli QML’e Kaydetmek (main.cpp)
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "devicelistmodel.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
DeviceListModel deviceModel;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("deviceModel", &deviceModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Adım 3: QML Tarafında ListView ve Delegate Tasarımı
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 450
height: 600
title: "Ağ Cihazları Listesi"
color: "#18191a"
ListView {
id: deviceListView
anchors.fill: parent
anchors.margins: 10
spacing: 8
clip: true // Liste sınırları dışına taşan ögeleri gizle
// C++ Tarafındaki Modeli Bağla
model: deviceModel
// Performans Optimizasyonu: Ekranda olmayan 100 piksellik tamponu hazır tut
cacheBuffer: 200
// Liste Boşken Gösterilecek Mesaj
// (Empty state placeholder)
// TEK BİR SATIRIN GÖRSEL DELEGESİ (DELEGATE)
delegate: Rectangle {
id: card
width: deviceListView.width
height: 70
radius: 10
color: mouseArea.pressed ? "#2d3436" : "#242526"
Behavior on color { ColorAnimation { duration: 150 } }
Row {
anchors.fill: parent
anchors.margins: 15
spacing: 15
// Çevrimiçi/Çevrimdışı Durum Göstergesi
Rectangle {
width: 12
height: 12
radius: 6
anchors.verticalCenter: parent.verticalCenter
color: isOnline ? "#2ecc71" : "#e74c3c"
}
// Cihaz Adı ve IP Adresi
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 4
Text {
text: name // C++ NameRole doğrudan burada!
font.pixelSize: 16
font.bold: true
color: "#ffffff"
}
Text {
text: ipAddress // C++ IpRole
font.pixelSize: 12
color: "#b0b3b8"
}
}
Item { Layout.fillWidth: true; width: 1 } // Araya esnek boşluk
// Pil Seviyesi
Text {
anchors.verticalCenter: parent.verticalCenter
text: isOnline ? (batteryLevel + "% 🔋") : "Kapalı"
color: (batteryLevel < 20) ? "#e74c3c" : "#b0b3b8"
font.bold: true
}
}
// Satıra Tıklama Olayı
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
console.log("Seçilen Cihaz:", name, "Index:", index);
}
}
}
}
}
ListView Performans Kuralları
clip: trueKullanımı:ListViewsınırları dışındaki çizimleri kırpmak ekran kartı (GPU) aşırı çizimini (overdraw) önler.cacheBufferAyarı: Kaydırma sırasında yeni satırların gecikmeden ekrana gelmesi içincacheBuffer: 200gibi makul bir tampon bellek bırakın. Değeri çok yüksek tutmayın; gereksiz RAM tüketir.- Delegateleri Basit Tutun: Bir delege içinde onlarca iç içe
Itemveya ağır görsel efektler (DropShadow,FastBlur) kullanmaktan kaçının.
Özet
QML ListView ve QAbstractListModel kombinasyonu; veri güvenliği, düşük bellek tüketimi ve yüksek animasyon akıcılığını bir araya getirir. roleNames eşleşmesi ile C++ verilerini QML delege elemanlarına bağlayarak modern ve güçlü liste uygulamaları inşa edebilirsiniz.