Grafiksel arayüz (GUI) programlamada veya olay tabanlı (event-driven) mimarilerde farklı nesnelerin birbirleriyle gevşek bağlı (loosely coupled) şekilde haberleşmesi en kritik tasarım gereksinimidir. Qt framework’ünün kalbinde yer alan Qt signals and slots mekanizması, geleneksel C stili geri çağırım (callback) fonksiyonlarına kıyasla tür güvenli (type-safe), modüler ve okunabilir bir iletişim kanalı sunar.
Bu rehberde, signals ve slots kavramlarını, Qt’nin Meta-Object Compiler (MOC) yapısını, modern C++ connect sözdizimini ve lambda fonksiyonlarıyla kullanımını derinlemesine ele alıyoruz.
Signals ve Slots Kavramı Nedir?
Geleneksel arayüz geliştirmede bir butona tıklandığında ne olacağını belirlemek için genellikle fonksiyon işaretçileri (function pointer) kullanılırdı. Ancak bu yaklaşım tür güvenliğini bozabilir ve nesnelerin birbirini doğrudan tanımasını zorunlu kılar.
Qt bu sorunu iki ana bileşenle çözer:
- Signal (Sinyal): Bir nesnede belirli bir olay meydana geldiğinde (örneğin bir butona tıklandığında veya ağdan veri geldiğinde) dış dünyaya yayınlanan bildirimdir. Sinyalin gövdesi (body) kodlanmaz; yalnızca tanımlanır ve
emitanahtar kelimesiyle tetiklenir. - Slot (Yuva): Bir sinyale yanıt olarak çalışan sıradan C++ üye fonksiyonudur. Bir sinyale bağlanabildiği gibi, doğrudan normal bir fonksiyon gibi de çağrılabilir.
Sinyali yayan nesne, sinyali kimin dinlediğini bilmek zorunda değildir; dinleyen nesne de sinyalin nereden geldiğiyle ilgilenmez. Bu durum mükemmel bir bileşen bağımsızlığı sağlar.
Q_OBJECT Makrosu ve MOC (Meta-Object Compiler)
Bir C++ sınıfında sinyal ve slot tanımlayabilmek için iki temel şart vardır:
- Sınıfın doğrudan veya dolaylı olarak
QObjectsınıfından türetilmesi gerekir. - Sınıf gövdesinin hemen başında
Q_OBJECTmakrosu yer almalıdır.
#include <QObject>
class Termometre : public QObject {
Q_OBJECT
public:
explicit Termometre(QObject *parent = nullptr) : QObject(parent), m_sicaklik(0.0) {}
void sicaklikGuncelle(double yeniDeger) {
if (m_sicaklik != yeniDeger) {
m_sicaklik = yeniDeger;
// Sinyali yayıyoruz
emit sicaklikDegisti(m_sicaklik);
}
}
signals:
// Sinyalin gövdesi yazılmaz, MOC tarafından üretilir
void sicaklikDegisti(double yeniSicaklik);
private:
double m_sicaklik;
};
Not:
Q_OBJECTmakrosu, Qt’nin dahili derleme aracı olan MOC (Meta-Object Compiler) için sınıfın çalışma zamanı tür bilgisi (RTTI) ve sinyal/slot tablolarını üretmesini sağlar.
Modern QObject::connect Sözdizimi
Qt 5 ve Qt 6 ile birlikte gelen modern connect sözdizimi, üye fonksiyon işaretçileri (pointer-to-member-function) kullanır. Bu sayede parametre uyumsuzlukları derleme anında (compile-time) yakalanır.
Temel kullanım kalıbı:
QObject::connect(gonderici, &GondericiSinifi::sinyalAdi,
alici, &AliciSinifi::slotAdi);
Örnek Bağlantı:
Aşağıda sıcaklık değiştiğinde ekrana uyarı basan bir gösterge sınıfı ve bağlantısı yer almaktadır:
#include <QCoreApplication>
#include <QDebug>
class Gosterge : public QObject {
Q_OBJECT
public slots:
void ekrandaGoster(double sicaklik) {
qDebug() << "Guncel Sicaklik Degeri:" << sicaklik << "°C";
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
Termometre sensor;
Gosterge ekran;
// Sinyali Slota Bagliyoruz
QObject::connect(&sensor, &Termometre::sicaklikDegisti,
&ekran, &Gosterge::ekrandaGoster);
// Tetikleme yapalım
sensor.sicaklikGuncelle(24.5);
sensor.sicaklikGuncelle(26.2);
return 0;
}
Lambda İfadeleri ile Slot Bağlama
Modern C++ (C++11 ve sonrası) sayesinde ayrı bir slot fonksiyonu yazmak zorunda kalmadan, sinyali doğrudan bir C++ Lambda İfadesine bağlayabilirsiniz:
QObject::connect(&sensor, &Termometre::sicaklikDegisti, [](double yeniSicaklik) {
if (yeniSicaklik > 30.0) {
qDebug() << "KRITIK UYARI: Yuksek sicaklik tespit edildi!" << yeniSicaklik;
}
});
Eğer lambda içinde bir QObject nesnesine başvuruyorsanız, nesnenin yaşam döngüsünü korumak için üçüncü parametre olarak bağlam nesnesini (context object) eklemek en iyi pratiktir:
QObject::connect(&sensor, &Termometre::sicaklikDegisti, &ekran, [&ekran](double deger) {
ekran.ekrandaGoster(deger);
});
Bağlantı Türleri (Qt::ConnectionType)
Farklı iş parçacıkları (thread) arasında sinyal gönderirken connect fonksiyonunun 5. parametresi büyük önem taşır:
| Bağlantı Türü | Çalışma Mantığı | Kullanım Senaryosu |
|---|---|---|
Qt::AutoConnection | Gönderici ve alıcı aynı thread’deyse Direct, farklı thread’deyse Queued davranır (Varsayılandır). | Çoğu standart senaryo |
Qt::DirectConnection | Sinyal yayınlandığı an alıcının slotu doğrudan göndericinin thread’inde çağrılır. | Eşzamanlı ve aynı iş parçacığı içi |
Qt::QueuedConnection | Sinyal alıcının olay kuyruğuna (Event Loop) atılır ve alıcının kendi thread’inde işlenir. | Çoklu iş parçacığı (Multi-threading) |
Qt::UniqueConnection | Aynı sinyal ve slot arasında mükerrer (çift) bağlantı kurulmasını engeller. | Tekrarlayan bağlantı riskleri |
Bağlantıyı Koparmak (disconnect)
İhtiyaç kalmadığında bir bağlantıyı devre dışı bırakmak için QObject::disconnect fonksiyonu kullanılır:
// 1. Belirli bir baglantiyi sonlandirma:
QObject::disconnect(&sensor, &Termometre::sicaklikDegisti, &ekran, &Gosterge::ekrandaGoster);
// 2. Bir nesneye ait tum sinyalleri tek seferde koparma:
sensor.disconnect();
Konsol çıktılarında ve hata ayıklama süreçlerinde karşılaşılan sorunlar için sitemizdeki Manjaro QDebug Hatasının Çözümü rehberimizi inceleyebilirsiniz.
Daha ayrıntılı meta-nesne hiyerarşisi ve kurallar için resmi Qt Signals & Slots Dokümantasyonu sayfasını ziyaret edebilirsiniz.