1. Home
  2. Docs
  3. QT
  4. Genel
  5. Signals ve Slots Mekanizması: Nesneler Arası Temiz İletişim

Signals ve Slots Mekanizması: Nesneler Arası Temiz İletişim

Print

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:

  1. 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 emit anahtar kelimesiyle tetiklenir.
  2. 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:

  1. Sınıfın doğrudan veya dolaylı olarak QObject sınıfından türetilmesi gerekir.
  2. Sınıf gövdesinin hemen başında Q_OBJECT makrosu 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_OBJECT makrosu, 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::AutoConnectionGönderici ve alıcı aynı thread’deyse Direct, farklı thread’deyse Queued davranır (Varsayılandır).Çoğu standart senaryo
Qt::DirectConnectionSinyal 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::QueuedConnectionSinyal 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::UniqueConnectionAynı 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.

Tags , , , , , ,

How can we help?