Qt ile Kodlama

Endorfin35+

Kayıtsız Üye
Katılım
1 Mayıs 2020
Mesajlar
4,193
Bu başlık altında Qt ile haşır neşir olarak bir yandan kendime not tutarken bir yandan da takip etmek isteyen arkadaşlara bilgiler aktaracağım. Daha önce python ile qt kullanımı konusunda bir miktar tecrübe edindim aslında ancak ulaşmak istediğim noktaya varamadım. Başvurduğum kaynaklarda da sürekli c++ ile örnekler karşıma çıkınca pythonu şimdilik bir kenara koyup c++ ile yola devam etme kararı aldım.

Sizde benim gibi anca arduino kodlayacak kadar c biliyor iseniz öncelikle c konusunda biraz yol alınmalı diye düşünüyorum. Qt de herşey sınıflardan oluşuyor ve nesne yönelimli programlamaya bir tık hakim olmak gerekli...

Bu başlık altında C/C++ hakkında aldığım notları bulabilirsiniz.

Şu Başlık altında da Qt Creator (IDE) hakkında bazı paylaşımlar bulunmakta,


Haydi başlayalım.

Yeni bir Qt Console Application projesi oluşturduğumuzda main dosyamıza şu kodlar (açıklamaları ben ekledim) otomatik olarak geliyor;
C++:
#include <QCoreApplication> // QCoreApp.. sınıfını dahil et.

int main(int argc, char *argv[])   // argc,argv main fonksiyonun parametre alırsa diye yazılmış.
{
    QCoreApplication a(argc, argv);   // QCoreApp.. sınıfından bir nesne "a" üret. main fonk. aldığı parametre var ise nesnenin yapıcısına aktar.

   
   
    return a.exec();   // program sonu. a nesnesinin exec fonksiyonunun dönüş değerinin döndür...
}

Bu aşamada a nesnesi ile bir işimiz yok. main parametreleri ile de işimiz yok. Gelin c++ ın iostream sınıfı ile bir çıktı almak için kodumuzu şöyle düzenleyelim;
C++:
#include <QCoreApplication>
#include <iostream>

int main()
{
 
    std::cout << "Merhaba Qt" << std::endl;

    return 0;
}
Program Çıktısı:
Merhaba Qt


Qt, C++ sınıflarına alternatif olarak Q harfi ile başlayan kendi sınıflarına sahiptir ve Qt nin kendi sınıflarının kullanılması tavsiye edilmektedir. Tabi bu durumdan c++ sınıfları kullanılamaz gibi bir anlam çıkarılmamalıdır.

Qt Sınıfları : https://doc-snapshots.qt.io/qt6-dev/qtcore-module.html

Qt Sınıflarını kullanarak Merhaba örneğimizi tekrar yazalım;
C++:
#include <QCoreApplication>

int main()
{

    QString Cumle ="Merhaba QT";  // c++ string alternatifi...


    qDebug() << "Mekatronik.Org : " << Cumle;  // c++ cout alternatifi...

    return 0;
}
Program Çıktısı:
Mekatronik.Org :  "Merhaba QT"
 

Sınıfların Kullanımı :

Qt dokümantasyonunu referans alarak sınıfları nasıl kullanırızı biraz kurcalayalım. Örnek olması açısından QString sınıfını seçiyorum. Öncelikle referans için linki paylaşalım.


Header:#include <QString>
CMake:find_package(Qt6 COMPONENTS Core REQUIRED)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmake:QT += core

Qt nin kaynak dokümanında en başta yukarıdaki bilgiler paylaşılmış. Header satırında QString kullanmak için nasıl include yapmamız gerektiği gösterilmiş. CMake ve qmake sınıfların/kütüphanelerin derleme sırasında koda eklenmesini sağlayan derleme yardımcı araçları. Ve bu araçlar birbirine alternatif olarak kullanılıyor. CMake daha genel bir araç iken qmake Qt nin kendi aracı. Bu satırlarda proje/derleme dosyasına nelerin dahil edilmesi gerektiği bilgisi verilmiş. Ben şimdilik oyumu qmake ten yana kullanıyorum. O halde QString sınıfını kullanabilmek için *.pro dosyamızın içersinde "QT += core" ifadesi olmalı.

Şimdi yeni bir Qt console application projesi oluşturduğumda pro dosyası içerisinde QT += core yazmadığı halde bu sınıfı kullanabildiğimizi görüyorum. Bu duruma yorumum demek ki mevcut ayarlar bir şekilde bu sınıfı kapsıyor. Zaten aksi durumda main dosyanıza #include <QString> yazdığınız anda IDE bu sınıf bulunamadı hatası veriyor. Yöntem basitçe şöyle olmalı; include sırasında hata alınmıyorsa sorun yok. Fakat hata alınıyorsa pro dosyasına gerekli ekleme yapılmalı ve Build menüsünden Run qmake seçilerek yeni konfigürasyonun etkin olması sağlanmalıdır.

Yok ben bu qmake ile daha da uğraşırım diyorsanız buyrun;



Hadi biraz kodlayalım.
C++:
#include <QDebug>
#include <QString>

int main()
{

    QString kelime_1="Merhaba ";

    QString kelime_2("QT ");

    QString *kelime_3;

    kelime_3=&kelime_1;

    QString *kelime_4 = new QString("Napan Bakim?");

    qDebug() <<kelime_1 << kelime_2 << *kelime_3<< *kelime_4;

    delete kelime_4;

    return 0;
}
Program Çıktısı:
"Merhaba " "QT " "Merhaba " "Napan Bakim?"

Dört farklı string i yine dört farklı şekilde tanımlayarak kullandık (aslında üç kelime ). Stringlerimiz birer değişken değil birer nesne...

kelime_1 : operatör (=) aşırı yüklemesi ile yapıcı fonksiyonu çağırdık,
kelime_2 : parametre ile yapıcı fonksiyonu çağırdık,
kelime_3 : QString türünden pointer oluşturup kelime1 in adresini atadık.
kelime_4 : Dinamik hafıza kullanarak kelimemizi heap bölümünde oluşturduk.

Oluşturduğumuz stringlerimiz birer nesne olduğuna göre fonksiyonları da olmalı... Örneğin;
C++:
#include <QDebug>
#include <QString>

int main()
{

    QString kelime_1="Merhaba ";

    qDebug() <<kelime_1;

    kelime_1 = kelime_1.toUpper();

    qDebug() <<kelime_1;

    kelime_1 = kelime_1.toLower();

    qDebug() <<kelime_1;
 
    return 0;
}
Program Çıktısı:
"Merhaba "
"MERHABA "
"merhaba "

Tekrar hatırlatalım. QString sınıfından bir nesne (string) oluşturduğumuzda hangi hazır fonksiyonlarımız var öğrenmek için referansımız :

 
Son düzenleme:
Arayüz (GUI) Programlama :

Önceki mesajda Qt nin yardımcı sınıflarından bahsederek QString sınıfını örneklemiştik. QString dışında Qt nin bir çok farklı yardımcı sınıfı daha bulunmaktadır. İlerleyen konularda karşımıza çıktıkça bu yardımcı sınıfları nasıl kullandığımızı tekrar gözden geçiririz. Ancak amacımız kullanıcı arayüzü oluşturmak olduğu için bir an önce bunu nasıl yaparız öğrenmeye çalışalım.

Hazır proje temalarını kullanmak yerine öncelikle boş bir Qt projesi oluşturalım. Boş proje oluşturma yöntemi şurada anlatılmıştır;



Boş projemizi hazır ise gelin pata küte ilk denemeyi yapalım...

*.pro dosyanızı aşağıdaki gibi düzenleyin ve ardından build/run qmake yapın
Kod:
QT      += widgets
SOURCES += \
    main.cpp


main dosyanızı aşağıdaki gibi düzenleyin ve programı çalıştırın (ctrl+r).

C++:
#include <QApplication>
#include <QLabel>

int main(int args , char *argv[])
{
    QApplication Uygulamam(args,argv);

    QLabel mesaj("Merhaba QT");

    mesaj.show();

    return Uygulamam.exec();
}

İşte ilk uygulamamızı oluşturduk..! Vaaay Muhteşem..!

1633260365571.png
 
Son düzenleme:
İlk Projeyi oluşturduk ama nasıl?

ilk projede neler döndüğünü anlamaya çalışalım. Öncelikle Proje dosyamıza bakalım;

INI:
# *.pro dosyası içerisinde açıklama satıları "#" karakteri ile başlar.

# widgets kitaplıklarını ekle
QT      += widgets

# kaynak kodu dosyasını belirt
SOURCES += \
    main.cpp

Kod içerisine yeterli açıklamaları ekledim. Bu nedenle esas bizi ilgilendiren main.cpp dosyamıza yoğunlaşalım.

C++:
#include <QApplication>  // Qt de uygulamalar bu sınıftan oluşturulur.
#include <QLabel>        // QWidget sınıfından türetilmiş Label sınıfı


int main(int args , char *argv[])
{
    // Uygulamam adında nesne oluştur.
    QApplication Uygulamam(args,argv);
    
    // mesaj adında nesne oluştur.
    QLabel mesaj("Merhaba QT");
    
    // mesaj nesnesinin show fonksiyonunu çağır.
    mesaj.show();

    // uygulamayı çalıştır.
    // uygulmanın exec fonksiyonunu çağır.
    // fonksiyon dönüş sağladında programı bitir.
    return Uygulamam.exec();
}

Bir bakışta sınıfların ilişkilerini hazmetmek çok kolay görünmüyor. Adım adım açıklamaya çalışalım.

QApplication sınıfı oluşturulan arayüz uygulaması için bir temel oluşturur uygulamanın akışını kontrol eder. main fonksiyonun içinde öncelikle bu sınıftan bir "uygulamam" adında bir nesne oluşturduk. Sonrasında arayüzü kodladık. En son satırda ise ugyulama nesnesinin exec() fonksiyonunu çağırarak qt ugyulamasını (arayüzü) çalıştırdık. Ekrana bir pencere geldi (arka planda gizlide olabilir) ve kullanıcı pencere üzerinden programı kullanmaya başladı. Kullanıcı işlemlerine göre fonksiyonlar oradan oraya dallandı, işlemler yapıldı, kodlar çalıştı... Ne zaman uygulama sonlandırılır ise exec fonksiyonu değer döndürdü ve C kodumuz sonladı...

Özetleyelim : Main bloğu içince bir QApplication nesnesi oluşturuyoruz ve return satırında nesnenin exec() fonksiyonu ile uygulama çalışıyor. Aslında çalışan sadece exec() fonksiyonu ama biz görsel uygulama çalışıyor gibi düşünebiliriz. exec() fonksiyonu çağrılmadan önce nasıl bir arayüz istiyorsak öncesinde kodlamamız gerekiyor.

QLabel pencere kontrol nesnesi üreten sınıflardan biridir. QLabel sınıfı label nesnesi üretir. Label form üzerinde kullanıcıya bilgi aktarmak için kullanılan bir araçtır. Kullanıcı tarafından label üzerinden bilgi girişi yapılmaz. label için kontrol nesnesi ve araç ifadesini kullandık. Her iki tanıma karşın Qt de bu nesneye ve diğer kontrol (buton, menu, kaydırma çubuğu vb) nesnelerine Widget denilmektedir. Widget arayüz üzerinde görsel bir araçtır. Widget kelimesi “window gadget” kelimelirnden türetilmiştir. Widget microsoft windows jargonundaki “control” ve “container” ile aynı anlamı ifade eder ancak Widget daha geniş bir kapsama sahiptir. Ayrıca widget sınıflarının türlerinin türetildiği üst sınıf olan QWidget adında bir sınıf da bulunmaktadır.

1633265368305.png


Kodumuza dönecek olursak mesaj adında bir QLabel nesnesi oluşturuyoruz. Bu esnada parantez içinde yazdığımız "Merhaba QT" nesnenin yapıcı fonksiyonuna parametre olarak gönderiliyor.

mesaj.show() fonksiyonu ile nesnemizin görünür olmasına izin veriyoruz. Sonrasında uygulamamızı çalıştırıyoruz.


Bu aşamada gui ile daha önce biraz uğraşanların dikkatini çekmiş olduğunu tahmin ettiğim bir durum var. Bir form ( pencere ) oluşturmadık ama uygulamada bir pencereye nasıl sahip olabildik? Bu sorunun cevabını QWidget sınıfı veriyor. QWidget sınııfı pencereleri üreten üst sınıf olduğu ve QLabel bu sınıftan türetildiği için QLabel nesnemiz pencere özelliklerine de sahip oluyor. Örneğin kodumuza söyle bir ekleme yapalım.
C++:
mesaj.setWindowTitle("Mekatronik");

1633267419109.png


setWindowTitle fonksiyonu ile Pencerenin başlığını oluşturduk. Bu foksiyon QWidget sınıfından miras alınmış olduğu için mesaj nesnemizin bir fonksiyonu olarak kullanabildik.

1633267708488.png


Qt nin sınıfları arasındaki hiyerarşiyi tam olarak gösteren bir grafik bulamadım. Bunun sebebi ise bir biri ile ilişkili oldukça fazla sınıf olması. Kullanacağımız sınıflar arasındaki ilişkiyi öğrenmek istiyorsak yine Qt nin referansı bize yol gösterici oluyor.


1633267959886.png

Inherits : Sınıfın türetildiği üst sınıflar...
Inherited By : Bu sınıftan türetilen alt sınıflar...

Sınıf isimlerine tıklayarak hangi sınıf hangi sınıftan türemiş öğrenmek mümkün oluyor.


Umarım konuyu biraz aydınlatabilmişimdir. Pencereleri nasıl üretiriz, nasıl kontrol ederiz konuları ile devam edeceğiz...
 
Pencere Oluşturma ve Kontrol Ekleme Yöntemleri :

Gelin çoğunlukla şu ana kadar öğrendiklerimiz ile bir pencere uygulaması oluşturalım.
C++:
#include <QApplication>  // Qt de uygulamalar bu sınıftan oluşturulur.
#include <QWidget>       // QWidget pencere oluşturma sınıfı
#include <QLabel>        // label sınıfı
#include <QPushButton>   // buton sınıfı
#include <QHBoxLayout>   // Yatay yerleşim kutusu

int main(int args , char *argv[])
{
    QApplication Uygulamam(args,argv);


    QWidget pencere;
    pencere.setWindowTitle("Mekatronig.org");

    QHBoxLayout *panel = new QHBoxLayout();

    QLabel *mesaj=new QLabel("Merhaba Dünyalı...");

    QPushButton *buton = new QPushButton("KAPAT");

    panel->addWidget(mesaj);
    panel->addWidget(buton);
    pencere.setLayout(panel);

    pencere.show();

    return Uygulamam.exec();
}

1633276888076.png


QWidget sınıfı ile ile bir pencere oluşturduk, QLabel ile bir label, QPushbutton ile bir buton oluşturduk, birde QHBoxLayout sınıfından panel adında bir yatay yerleşim kutusu oluşturduk. Bu kutu widgetlerin yerleşim düzenini kontrol etmemizi sağlayan yardımcı bir araç. İlk başta bu nesnelerimiz birbiri ile ilişkili değildi. Widgetleri, addWidget() fonksiyonuyla yerleşim nesnesi ile ilişkilendirdik(panel üzerine ekledik). Sonrasında Bu yerleşim nesnesini setLayout() fonksiyonu ile penceremiz ile ilişkilendirdik(form üzerine ekledik). Panel üzerine önce Label sonra buton eklediğimiz için form üzerinde soldan sağa önce label sonra buton görüntülenmekte. Yatay yerleşim kutusu kullandığımız için yerleşim yan yana yapıldı. Butonumuzun şimdilik sadece görsel, herhangi bir fonksiyonu yok...

Bu örnekte nesneleri oluşturduk ve sonradan ilişkilendirdik. Peki kendi sınıfımızı yazarak bir pencere oluşturmak isteseydik nasıl olmalı bakalım. Haydi aynı pencereyi oluşturan kendi sınıfımızı yazalım.

C++:
#include <QApplication>  // Qt de uygulamalar bu sınıftan oluşturulur.
#include <QWidget>       // QWidget pencere oluşturma sınıfı
#include <QLabel>        // label sınıfı
#include <QPushButton>   // buton sınıfı
#include <QHBoxLayout>   // Yatay yerleşim kutusu


class pencerem:public QWidget
{
private:
    QHBoxLayout *panel;
    QLabel *mesaj;
    QPushButton *buton;

public:
    pencerem()
    {
        setWindowTitle("Mekatronik.Org");
        panel = new QHBoxLayout();
        mesaj=new QLabel("Merhaba Dünyalı...");
        buton = new QPushButton("KAPAT");
        panel->addWidget(mesaj);
        panel->addWidget(buton);
        setLayout(panel);
    }
    ~pencerem()
    {
        delete panel;
        delete mesaj;
        delete buton;
    }
};

int main(int args , char *argv[])
{
    QApplication Uygulamam(args,argv);

    pencerem pencere;

    pencere.show();

    return Uygulamam.exec();
}

1633278743143.png


Sınıf oluşturarak aynı pencereyi elde ettik. Hangi yöntemin daha avantajlı olduğundan şu an çok emin değilim. Kendimi sınıf kullanma yöntemine yakın hissediyorum. Birde QtDesigner ile pencere oluşturma var ancak o konuyu sonraya bırakıyorum.
 
Son düzenleme:
Projenin Header ve Source olarak Düzenlenmesi :

Bu konuya mümkün olduğunca girmek istemiyor olsamda bu aşamadan sonra başka çaremiz kalmadığı için projeyi başlık (header) ve kaynak (source) dosyası şeklinde yazarak devam edeceğiz. Daha önce oluşturduğumuz boş projeye "add new" seçeneği ile dosya eklemekten bahsetmiştik. Aynı yöntem ile en son projemize "uygulama.h" ve "uygulama.cpp" dosyalarını ekleyelim. Dosyalarımızı ekleme işi tamamlandığında *.pro dosyamıza da yeni dosya adları otomatik olarak eklenecektir.

Temel olarak h dosyası içinde sınıflar ve fonksiyonlar prototip olarak tanımlanır. Cpp dosyasında ise tanımlanmış olan prototiplerin gövdesi yer alır.

uygulama.h dosyasını şöyle düzenleyelim;
uygulama.h:
#ifndef UYGULAMA_H
#define UYGULAMA_H

#include <QWidget>
#include <QLabel>        // label sınıfı
#include <QPushButton>   // buton sınıfı
#include <QHBoxLayout>   // Yatay yerleşim kutusu

class Pencerem : public QWidget
{

private:
    QHBoxLayout *panel;
    QLabel *mesaj;
    QPushButton *buton;

public:
    Pencerem();
    ~Pencerem();
};


#endif // UYGULAMA_H

uygulama.cpp dosyamız ise şöyle olmalı;
uygulama.cpp:
#include "uygulama.h"

Pencerem::Pencerem()
{
    setWindowTitle("Mekatronik.Org");
    panel = new QHBoxLayout();
    mesaj=new QLabel("Merhaba Dünyalı...");
    buton = new QPushButton();
    buton->setText("Kapat");

    panel->addWidget(mesaj);
    panel->addWidget(buton);
    setLayout(panel);
}

Pencerem::~Pencerem()
{
    delete panel;
    delete mesaj;
    delete buton;
}

Son olarak main.cpp dosyamız;
main.cpp:
#include "uygulama.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Pencerem anapencere;
    anapencere.show();
    return a.exec();
}

Programımızı çalıştırdığımızda yine aynı sonucu alıyoruz.

1633288985756.png


Bu aşamaya kadar olan kısmı iyi sindirmek gerekli. Aksi durumda bundan sonra olay biraz daha karmaşık ve zor takip edilir bir hal almaya başlayacak.
 
Otomatik Hafıza Yönetimi ( I. Bölüm ) :

Daha önce bahsettiğimiz üzere Qt tamamen sınıflara ve bu sınıfların miras ilişkilerine bağlı olarak çalışır. Qt deki tüm sınıflar QObject sınıfından kalıtım alır. Biz Qt nin türetilmiş sınıflarından bazı nesneleri hafızanın dinamik (heap) alanında oluşturmak durumundayız. Dolayısı ile nesneye ihtiyaç kalmadığında ilgili hafıza alanını da boşa çıkartmalıyız. Yüzlerce nesne oluşturduk ve sonrasında bazı nesneler için ilgili hafıza alanlarını boşa çıkartmayı unuttuk. Programımız belki sorunsuz çalışacaktır fakat hafızada boşa çıkartmayı unuttuğumuz alanlar hala rezerve olduğundan kaynak azalması söz konusu olacaktır. İşte bu noktada Qt yi geliştiren arkadaşlar bu soruna çözüm bulmuşlar. Nasıl olduğunu örnekleyerek adım adım anlamaya çalışalım....

Öncelikle yeni bir Qt console Application projesi oluşturalım. Projenin main dosyası aşağıdaki gibidir.
C++:
#include <QCoreApplication>

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

    return a.exec();
}

Öncelikle QCoreApplication sınıfından bahsedelim. İsim benzerliği olduğu için belki dikkatinizden kaçmıştır, belki de fark etmişsinizdir. önceki mesajlarda bazı kodlarda QCoreApplication, bazı kodlarda ise QApplication sınıfı kullanılmıştır. Bu konuyu aydınlatalım;

QCoreApplication : Temel uygulama sınıfıdır. Konsol uygulamalarında bu sınıf kullanılır.
QGuiApplication : QCoreApplication sınıfından türetilmiştir. Widget ile ilişkili olamayan gui uygulamalarında (Örn. OpenGL) kullanılır.
QApplication : QGuiApplication sınıfından türetilmiştir. Widget uygulamalarında kullanılır.

Peki, konumuza dönelim. Main fonksiyona baktığımızda,
C++:
QCoreApplication a(argc, argv);
a nesnemiz hafızanın stack bölümünde oluşturulmuş. Program kapanında kendi kendine silinecek burada bir sıkıntımız yok. Bu arada QCoreApplication sınıfınında QObject sınıfından türetildiğini hatırlatalım...

Şimdi projemize yeni bir sınıf ekleyerek QObject sınıfından kalıtım alan kendi sınıfımızı yazalım. Sınıf adını istediğiniz gibi belirleyebilirsiniz. Ben "MemTest" adını kullanacağım. Sınıf ekleme için bakınız;

MemTest.h dosyamızın içeriğine bakalım. Bu kodları IDE oluşturdu henüz bir şey kodlamadık.
C++:
#ifndef MEMTEST_H
#define MEMTEST_H

#include <QObject>

class MemTest : public QObject
{
    Q_OBJECT
public:
    explicit MemTest(QObject *parent = nullptr);

signals:

};

#endif // MEMTEST_H

Başlangıç için olması gereken her şeyi IDE bizim için yazmış. Bu kodların içinde şu ana kadar kullanmadığımız veya bahsetmediğimiz üç adet ifade var.
C++:
Q_OBJECT
Q_OBJECT Makrosu : Qt nin kendine has yapısının çalışabilmesi için eklenmiştir. İhtiyaç olursa daha detaylı inceleriz. Şimdilik yazılmasının zorunlu olduğunu bilelim yeter.
C++:
explicit MemTest(QObject *parent = nullptr);
Buraya dikkat... Bizi ilgilendiren nokta burası... MemTest sınıfımızın yapıcı (constructor) fonksiyon prototipi. Fonksiyoumuz bir adet parametre alıyor. Aldığı parametre QObject türünden bir pointer ve parametre verilmez ise varsayılan olarak nullptr alıyor.
C++:
signals:
Bu satırda yine qt nin özel bir yapısına ait bu yapıyı ayrıca inceleyeceğiz.


Birde MemTest.cpp dosyamıza bakalım;
C++:
#include "memtest.h"

MemTest::MemTest(QObject *parent) : QObject(parent)
{

}

Bu kodlarda IDE tarafından oluşturuldu. Henüz bu dosyaya da bir şey yazmadık. Koda baktığımızda MemTest sınıfımızın yapıcı fonksiyonunun burada oluşturulduğunu görüyoruz. Fonksiyon yazılırken Constructor Delegation ile QObject sınıfının yapıcı fonksiyonuna parent adlı değişken (pointer) atanmış. C deki kalıtım konusundan hatırlayalım. Türetilmiş sınıftan bir nesne oluşturulduğunda hem üst sınıfın hem de türetilmiş sınıfın yapıcı fonksiyonu çalışıyordu. Yani biz MemTest sınıfından bir nesne üretirken bir adres vermez isek yapıcı fonksiyonumuz nullptr parametresi alacak ve bunu da QObject in yapıcına iletecek. Ancak MemTest sınıfından bir nesne üretirken bir adress verirsek yapıcı fonksiyonumuz nullptr yerine verdiğimiz adresi alacak ve yine buadresi QObject in yapıcına iletecek...

Bu bölümde kodlama yapmadan sadece mevcut kodların yapısı hakkında bilgi edindik. İkinci bölümde biraz kodlama da yaparak hafıza yönetiminin nasıl olduğundan bahsedeceğiz...
 
Otomatik Hafıza Yönetimi ( II. Bölüm ) :

Önceki mesajda hazırladığımız projenin header dosyasına ekleme yaparak devam edelim,
C++:
#ifndef MEMTEST_H
#define MEMTEST_H

#include <QObject>
#include <QDebug> // çıktı almak için ekliyoruz..

class MemTest : public QObject
{
    Q_OBJECT
public:
    // Yapıcı Fonksiyon
    explicit MemTest(QObject *parent = nullptr);
   
    // Yıkıcı Fonksiyon
    ~MemTest();
signals:

};

#endif // MEMTEST_H

QDebug sınıfını ve yıkıcı fonksiyon prototipimizi ekledik. Şimdide cpp dosyamızı şu şekilde düzenleyelim,

C++:
#include "memtest.h"

MemTest::MemTest(QObject *parent) : QObject(parent)
{
    qInfo() << this << " Oluşturuldu : " << parent;
}

MemTest::~MemTest()
{
    qInfo() << this << " Silindi : " << parent();
}

kodlarda yer alan;
"qInfo" daha önce kullandığımız qDebug a benzer şekilde konsoldan çıktı almamızı sağlayan bir fonksiyon.
"this" sınıftan oluşan nesne pointerı. Nesnenin oluştuğu adresi verir.
"parent" yapıcı fonksiyon içinde parametre olarak tanımladığımız pointer değişkeni.
"parent()" ise QObject sınıfının bir fonksiyonu. Üst sınıfın adresini döndürür.

Şimdi son olarak main dosyamızı düzenleyim ardından neler oluyor bakalım...
C++:
#include <QCoreApplication>
#include <QTimer>       // zamanlayıcı sınıfı
#include <memtest.h>

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


    QTimer timer;   // bir zamanlayıcı oluştuduk...
    // zamanlayıcımız 2 saniye sonra uygulamayı kapatacak...
    timer.singleShot(2000,&a,&QCoreApplication::quit);
    qInfo() << "Program Başladı" ;

    // Nesneler...

    
    

    // exec() fonksiyonun dönüş değerini bir değişken üzerinden
    // return ederek araya giriyoruz.
    int val = a.exec();
    qInfo() << "Program Sonu. Kod : "<< val ;  // 2 sn sonra...

    return val;
}
Bu blokta programı 2 saniye sonra sonlandıracak şekilde bir zamanlayıcı kurduk fakat konumuz bu olmadığı için detaylara girmiyorum. Bize programı sonlandıracak bir fonksiyon lazımdı timer kullandık. Bu hali ile çıktımıza bakalım.

Kod:
Program Başladı
Program Sonu. Kod :  0

Şimdi memtest sınıfından bir nesne oluşturalım.
C++:
    // Nesneler...
    
    // Stack Bölümünde Nesne
    MemTest Nesne_1;
    Nesne_1.setObjectName("Nesne 1");  // nesnelere ayrıca isim verebiliyoruz.
Program Çıktısı:
Program Başladı
MemTest(0x7efd60)  Oluşturuldu :  QObject(0x0)
Program Sonu. Kod :  0
MemTest(0x7efd60, name = "Nesne 1")  Silindi :  QObject(0x0)

Nesneyi Stack üzerinde oluşturduğumuz için herhangi bir sorun yok. Bir nesne daha ekleyelim ama heap üzerinde olsun...
C++:
    // Stack Bölümünde Nesne
    MemTest Nesne_1;
    Nesne_1.setObjectName("Nesne 1");  // nesnelere ayrıca isim verebiliyoruz.

    // Heap Bölümünde Nesne
    MemTest *Nesne_2 = new MemTest;
    Nesne_2->setObjectName("Nesne 2");  // nesnelere ayrıca isim verebiliyoruz.
Program Çıktısı:
MemTest(0x7efd40)  Oluşturuldu :  QObject(0x0)
MemTest(0x11f8c10)  Oluşturuldu :  QObject(0x0)
Program Sonu. Kod :  0
MemTest(0x7efd40, name = "Nesne 1")  Silindi :  QObject(0x0)
Nesne 2 yi oluşturduk ama silmeyi unuttuk. Normalde delete ile silmemiz gerekliydi.. Bu arada çıktıda "QObject(0x0)" görmemizin nedeni QObject in nullptr alması...

Peki Qt nin bu duruma çözümünü üçüncü bir nesne ile görelim,
C++:
    // Nesneler...

    // Stack Bölümünde Nesne
    MemTest Nesne_1;
    Nesne_1.setObjectName("Nesne 1"); 

    // Heap Bölümünde Nesne
    MemTest *Nesne_2 = new MemTest;
    Nesne_2->setObjectName("Nesne 2");

    // Heap Bölümünde Nesne (argüman ile)
    MemTest *Nesne_3 = new MemTest(&a);
    Nesne_3->setObjectName("Nesne 3");
Program Çıktısı:
Program Başladı
MemTest(0x7efd10)  Oluşturuldu :  QObject(0x0)
MemTest(0xf38a70)  Oluşturuldu :  QObject(0x0)
MemTest(0xf38b70)  Oluşturuldu :  QCoreApplication(0x7efd30)
Program Sonu. Kod :  0
MemTest(0x7efd10, name = "Nesne 1")  Silindi :  QObject(0x0)
MemTest(0xf38b70, name = "Nesne 3")  Silindi :  QObject(0x7efd30)

Nesne 3 ü oluştururken,
C++:
QCoreApplication a(argc, argv);
uygulama ("a") nesnemizin adresini yapıcı fonksiyona parametre olarak veriyoruz. QObject artık nullptr yerine a nın adresini alıyor. Ve bu sayede (Qt nin özelliği) program sonunda delete fonksiyonunu kullanmamamıza rağmen nesne 3 ün hafızadan silindiğini görüyoruz. Kural basit : nesne oluştururken uygulamanın adresini ver sonra silmeye gerek kalmasın...

Peki bir güzellik daha yapalım. Nesne 2 yi oluşturduk ama sonradan bu oyuna dahil edelim.
C++:
    // Nesneler...

    // Stack Bölümünde Nesne
    MemTest Nesne_1;
    Nesne_1.setObjectName("Nesne 1");

    // Heap Bölümünde Nesne
    MemTest *Nesne_2 = new MemTest;
    Nesne_2->setObjectName("Nesne 2");

    // Heap Bölümünde Nesne (argüman ile)
    MemTest *Nesne_3 = new MemTest(&a);
    Nesne_3->setObjectName("Nesne 3");

    Nesne_2->setParent(Nesne_3);  // Nesne_2 yi Nesne_3 ün ailesine dahil et.
Program Çıktısı:
Program Başladı
MemTest(0x7efd10)  Oluşturuldu :  QObject(0x0)
MemTest(0x1128b70)  Oluşturuldu :  QObject(0x0)
MemTest(0x1128cf0)  Oluşturuldu :  QCoreApplication(0x7efd30)
Program Sonu. Kod :  0
MemTest(0x7efd10, name = "Nesne 1")  Silindi :  QObject(0x0)
MemTest(0x1128cf0, name = "Nesne 3")  Silindi :  QObject(0x7efd30)
MemTest(0x1128b70, name = "Nesne 2")  Silindi :  QObject(0x1128cf0, name = "Nesne 3")

setParent() fonksiyonu ile Nesne_2 ye de aileye dahil ettik ve bu sayede program sonunda nesne 2 yi de delete kullanmadan hafızadan kaldırdık.

Qt genellikle referanslar üzerinden işlem yaptığı için nesneleri hesap bölümünde (pointer olarak) oluşturmamızı istiyor. İlk başta bakışta belki karışık gelmiş olabilir ama konuyu özümseyince Qt nin bu özelliğini ben oldukça faydalı ve başarılı buldum... Bu konu daha da derinlere inmeye devam ediyor ancak bu kadarının şimdilik yeterli olduğunu düşünüyorum. Bundan sonra kodlarda şöyle ifade görünce
C++:
public:
    explicit MemTest(QObject *parent = nullptr);

// ve/veya

MemTest::MemTest(QObject *parent) : QObject(parent)
{

}
Şairin burada anlatmak istediği duyguyu anlıyor olmalıyız...

Referanslar :
 
Q_DISABLE_COPY :

Hafıza yönetimi ile ilgisi olduğundan bu konuya da değinmek istiyorum. Bu ifade kendisine verilen sınıfın atama operatörlerini ve kopya yapıcı fonksiyonlarını devre dışı bırakır. Önceki mesajda oluşturduğumuz MemTest sınıfı QObject sınıfından kalıtım alıyordu. Hadi bir deneme yapalım. MemTest sınıfından bir nesne oluşturup MemTest türünden parametre alan bir fonksiyona gönderelim..
C++:
#include <QCoreApplication>
#include <memtest.h>

void deneme(MemTest nesne)
{
   // fonksiyonumuz burada...
}

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

    MemTest Nesne_1;
    deneme(Nesne_1);    // hata verir...
   
    return a.exec();
}

Normalde nesnemizi deneme fonksiyonuna gönderebiliriz. Ancak referans kullanmadığımız için hafızada nesne_1 in kopyası oluşturularak bu işlem yapılır. Bu durum Qt nin hafıza yönetimi ilkelerine uygun olmadığı için Q_Disable_Copy sınıfı ile bu durumun önüne geçilir. Yukarıdaki kodu denerseniz hata aldığınızı göreceksiniz. Hatanın sebebi QObject.h dosyasının Q_DISABLE_COPY nin özelliklerini kullanıyor olmasıdır.

1633703696693.png


Sarı okla gösterdiğim alandaki hata oluşan noktaları gösteren mavi ile vurgulanmış linklere tıklarsanız hatanın hangi kod bağlantıları ile oluştuğunu görebilirsiniz.

1633703612360.png


Resimde görüldüğü üzere QObject sınıfı Q_DISABLE_COPY makrosunu ihtiva ettiği için fonksiyonumuza nesnemizi (kopya) olarak gönderemiyoruz.


Peki Nasıl olacak? Referans yada pointer kullanacağız. İki yöntemi de aşağıda örnekliyorum...
C++:
#include <QCoreApplication>
#include <memtest.h>

void yontem_1(MemTest &nesne)
{
   // fonksiyonumuz burada...
}

void yontem_2(MemTest *nesne)
{
   // fonksiyonumuz burada...
}


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

    MemTest Nesne_1;
   
    // iki yöntemde olur...
    yontem_1(Nesne_1);  
    yontem_2(&Nesne_1);
   
    return a.exec();
}
 

Ekler

  • 1633703658124.png
    1633703658124.png
    16.2 KB · Görüntüleme: 122
Son düzenleme:
Q_OBJECT Makrosu ve MOC :

Öncelikle makro kavramına açıklık getirelim. Bakmayın öyle ismin havalı olduğuna... Bildiğiniz #define ile kod tanımlamaktan başka bir şey değil... Basit bir selamla makrosu yazalım,
C++:
#include "stdio.h"

# define selamla \
            printf("Merhaba \n"); \
            printf("Makro \n");
int main()
{
    selamla

    return 0;
}
Program Çıktısı:
Merhaba
Makro

Q_OBJECT makrosuda QObject sınıfından kalıtım alan sınıflar içerisine yerleştirilen bir makrodur. Qt nin özelliklerinin sağlıklı çalışması için gerekli kodları ihtiva eder. İçeriğini değiştirme ihtiyacı duyulmadığı için makro şeklinde oluşturulmuştur. Böylece hem bir sürü tanımlama yapmaktan kurtuluruz hemde kod anlaşılırlığı artar... Qt kullanılıyorsak eğer standart olarak kabul edip kullanmalıyız...

Qt , Q_OBJECT makrosu ve Qt nin diğer C++ eklentilerini işleyen Moc (Meta-Object Compiler) adında programa sahiptir. Derleme sırasında Moc, Qt nin fonksiyonlarının çalışması için kodumuza ve içindeki Qt makrolarına göre derleme yaparak programımıza eklemler yapar...
 
Son düzenleme:
Sinyal ve Slot Kullanımı :

Bu konu yine Qt ye özgü kavramlardan birisi. Qt ile uğraşmaya başladığım zaman daha önceden visual studio ile kodlama yapmış biri olarak Qt ninde çok farklı olmadığı düşünmüştüm. Belki de çok farklı değil... Belki birazcık farklı :) Visual Studio da geliştirme yaparken form üzerine bir kontrol eklersiniz, üzerine çift tıklayıp kod penceresine geçer, doğrudan ilgili event i seçip kodlama yaparsınız. Olay bu kadar sadedir. Qt de ise bu çalışma senaryosu daha farklı işliyor. Bir pencere üzerindeki kontrollerin (bu kontrollere Qt de widget diyoruz) fonksiyonlarını yazmak veya tanımlamak için sinyal slot bağlantısını kullanıyoruz. Sinyal slot kavramı sadece görsel arayüz ile ilgili olmamasına karşın arayüz üzerinden örneklendirmek istiyorum.

Öncelikle biraz genel arayüz kültürü olarak event (olay/olgu/olan şey) kavramından bahsedelim. (Konudan genel olarak bahsedeceğim için Qt de aynı kavram farklı bir kelime ile ifade ediliyor olabilir). Asında windows kullandığımız için hepimiz event kavramlarına aşinayız. Şimdi bazı örnekler verelim ve konuyu anlayalım.

Click : kontrol nesnesine tıkladığımızda oluşan durum,
KeyPress : Bir tuşa bastığımızda oluşan durum,
Change : Text değiştiğinde oluşan durum,
MouseMove : kontrol üzerinde mouse hareket ettiğinde oluşan durum.
Focus : kontrolun aktif olduğu durum. (Programlarda tab tuşu cursoru atlatırız ve Cursorun atladığı yer aktif olur.)

Bunlar ilk alıma gelenler. Daha onlarca event var. Fakat bu kadar örnek ile event kavramını sanırım herkes anlamıştır.

Qt tarafına geri dönecek olursak form üzerinde bu bahsettiğimiz olaylar gerçekleştiği anda bu durumla ilgili bir sinyal yayınlanır. Örneğin butona tıklanınca ve bir sinyal yayınlanır fakat herhangi bir tanım yapılmadığı için program da herhangi bir etki oluşmaz. Biz butona tıklandığında bir şeyler olmasını istiyorsak bir tanılama yapmalıyız. Bu tanımlama da sinyal slot bağlantısıdır.

Sinyal form üzerinde gerçekleşen olay ise slot nedir? Slot ta sinyalin ilişkilendirileceği fonksiyondur. Bu fonksiyon nesnelerin hazır fonksiyonları olabileceği gibi kendi yazdığımız bir fonksiyonda olabilir.

Genellikle Constructor bölümünde Sinyal Slot bağlantıları "connect" ifadesi ile yapılır. Herhangi bir anda bağlantı kesilmek istenir ise disconnect ifadesi kullanılır.

C++:
// connect (gönderici, olay, alıcı, fonksiyon);
connect(buton1, SIGNAL (clicked()), label1, SLOT (clear()));

// veya fonksiyonu kendimizde yazabiliriz.

connect(button1, SIGNAL (clicked()), App, SLOT (fonskiyon()));

//...
App::fonskiyon()
{
    label1.clear();
    qInfo () << "Label Temizlendi...";
}

Sinyal ve Slotları kod içerisinde oldukça fazla kullanacağız. Çok detaya girmeden şimdilik kavramı anlayalım yeterli...
 
Basit Ugyulama :

Daha önce bir pencere oluşturmuştuk ama butonumuzu bir fonksiyonu yoktu. Aynı pencereyi oluşturalım ve butonumuz uygulamayı kapatsın.

Yeni bir "Qt Widget Application" projesi oluşturalım. Projeyi oluştururken "base class" olarak "QWidget" i seçelim ve "Generate form" kutusundaki tik i kaldıralım...

widget.h:
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>        // label sınıfı
#include <QPushButton>   // buton sınıfı
#include <QHBoxLayout>   // Yatay yerleşim kutusu

class Widget : public QWidget
{
    Q_OBJECT



public:
    QPushButton *buton;
    QHBoxLayout *panel;
    QLabel *mesaj;

    Widget(QWidget *parent = nullptr);
    ~Widget();

    // bonus :)
private slots:

    void yaz();
};

#endif // WIDGET_H
main.cpp:
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
widget.cpp:
#include "widget.h"

Widget::Widget(QWidget *parent): QWidget(parent)
{

    setWindowTitle("Mekatronik.Org");
    panel = new QHBoxLayout(parent);
    mesaj=new QLabel("Merhaba Dünyalı...",parent);
    buton = new QPushButton(parent);
    buton->setText("Kapat");

    panel->addWidget(mesaj);
    panel->addWidget(buton);
    setLayout(panel);

    // butona görev atayalım...
    connect(buton,SIGNAL(clicked()),this,SLOT(close()));

    //buton a tıklanınca yazı değişsin...
    //connect(buton,SIGNAL(clicked()), this,SLOT(yaz()));
}

Widget::~Widget()
{
}

void Widget::yaz()
{
    mesaj->setText("haha");
}

1633770586054.png


Aynı formu elde ettik ve artık butonumuz çalışıyor. Kodların içine örnek olması için pasif olarak butona basılınca label içeriğini değiştiren bir fonksiyonda ekledim.

Aşağı yukarı bu aşamaya kadar Qt nin temel yapısını öğrendik. Şu ana kadar öğrendiklerimiz ile örneğin hesap makinesi gibi basit uygulamalar yazabiliriz.
 
IDE ile Form Tasarımı:

Konu ilerleyişini seçerken mümkün olduğu kadar ne nereden geliyor arka planda ne oluyor hem anlamaya hem de anlatmaya çalıştım. Önceki mesajda oluşturduğumuz basit uygulamayı şimdi form tasarımı yaparak tekrar oluşturalım.

Yeni bir "Qt Widget Application" projesi oluşturalım. Projeyi oluştururken "base class" olarak "QWidget" i seçelim ve "Generate form" kutusu işaretli kalsın....

1633773895799.png


Proje dosyalarımız içinde önceki projelerimize göre ek olarak "widget.ui" dosyası oluşacaktır. Resimde içeriğin xml olduğu görülmektedir. Dosya üzerine çift tıklayarak veya soldaki design butonuna basarak tasarım sekmesine geçilir.

1633774082480.png


1633774202947.png


Sol tarafta bulunan widget lerden arzu edilen sürükle bırak mantığı ile form üzerine yerleştirilir.

1633774365401.png


Widget üzerine çift tıklanarak veya sağ taraftaki property menusunden text içeriği değiştirilir.

1633774639099.png


Resimde sarı renk ile çerçevelediğim butona basılarak signal slot moduna geçilir. Mouse ile buton üzerine gelindiğinde buton kırmızı olur. Aslında label üzerine gidersek oda kırmızı oluyor. Designer burada bize kırmızı vurgu ile sinyal kaynaklarını gösteriyor...


1633774929263.png


Şimdi buraya dikkat... Buton üzerine gittik ve buton kırmızı oldu. Mousemuzun tuşuna bastık ve bırakmadık. Mouse u aşağı yönde hareket ettirerek form üzerinde herhangi bir boş alana geldik. Bu esnada resimde görülen kırmızı çizgi oluştu ve mouse un tuşunu bıraktık. Aslına burada yaptığımız şey sinyal kaynağı olarak butonu, slot olarak ise formun kendisini seçmek oldu. Mouse un tuşunu bıraktığımız anda resimde görülen pencere açılacaktır. Buradan hangi sinyalin hangi slota bağlanacağı seçilir..

1633775355290.png


İşlem tamam tek satır kod yazmadan aynı uygulamayı gerçekleştirdik. Programı çalıştırdığınızda butonun kapat fonksiyonunun aktif olduğunu göreceksiniz. Edit sekmesine gidip c++ kodlarına bakarsanız herhangi bir ekleme olmadığını görürsünüz.

1633775610467.png


Kurgumuzun sonucunda xml dosyasının içeriği değişir ve bu kodlar moc tarafından derleme sırasında c++ a dönüştürülerek projeye dahil edilir.

Peki kodlarımızdan ui form elemanlarına erişmek istersek nasıl bir yol izleyeceğiz ona bakalım.

C++:
QPushButton *buton = new QPushButton();
buton->setText("Kapat");

kendimiz kod ile bir buton oluşturduğumuz zaman ki önceki mesajlarda "buton" olarak tanımlayarak oluşturduk. buton nesnesi üzerinden butonumuzun özelliklerine erişiyorduk...

1633776047821.png


Aynı şekilde ui form elemanlarına da erişebiliriz.

1633776126888.png


Şimdi buraya dikkat edelim... Form üzerine widgetler ekledikçe sağ taraftaki obje listesinde bu elemanlar ve ait oldukları sınıf görüntülenir. Örneğin biz botun ekledikçe pushButton, pushButton_2, pushButton_3,... şeklinde elemanlar adlandırılırlar. Kod içerisinden müdahil olabilmek için bu isimleri biliyor olmalıyız. Obje isimlerine daha kolay hakim olmanın yolu ise objeyi ekledikten sonra kendimize göre isim vermektir.

1633776445705.png


label nesnesini önceki örneklerimize uygun şekilde "mesaj" olarak adlandırdım.

Program çalışırken "Merhaba Qt" yerine "Tekrar Merhaba" yazacak şekilde içeriği değiştirmek istiyoruz ve bunu kod yazarak yapalım...
"widget.cpp" dosyasında şu düzenlemeyapılır.
C++:
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent): QWidget(parent) , ui(new Ui::Widget)
{
    ui->setupUi(this);
    
    // sonradan label içeriğini değiştirelim. sadece bu satırı ekledik...
    ui->mesaj->setText("Tekrar Merhaba...");
}

Widget::~Widget()
{
    delete ui;
}

1633776881248.png


Kod a dikkat edilirse bir form tasarımı kullandığımız için artık nesnelere "ui" üzerinden erşiyoruz. label ı mesaj olarak adlandırdığımız içinde mesaj nesnesinin setText fonksiyonu ile değişikliği sağlıyoruz... Tasarım aşamasında nesnelere isim vermeyip designerin atadığı varsayılan isimleri kullansak ta aynı sonucu alırdık.

Temel olarak form tasarım konusu da bu kadar...
 
Çoklu Pencereye Sahip Uygulamalar ( I. Bölüm ) :

Konuya başlamadan önce pencere sınıflarına bakalım. QWidget pencere oluşturan temel sınıf ancak qt ile uğraştığınızda QMainWindow ve QDialog sınıfları da karışınıza çıkacak... Aslında olayın çok fazla kapsamlı bir yönü yok. QWidget sınıfından türetilmiş içine bazı widgetlerin hazır yerleştirildiği sınıflar bunlar. Örneğin QMainWindow hazır olarak status bar ve üst menuye sahip... Bunları sonradan kendinizde ekleyebilirsiniz..

Bir uygulamanın bir ana (açılış) penceresi olur ve diğer pencereler bu pencere ile ilişkilendirilir. Ana pencere herhangi bir sınıftan olabilir...

Yeni bir Widgets projesi oluşturalım,

1633785168178.png


QWidget sınıfından kalıtım alacağım ve sonradan ekleyeceğim pencere ile karışmaması için main_page şeklinde adlandırdım...

Form tasarımımız şu şekilde olsun,

1633786937826.png


main_page.h:
#ifndef MAIN_PAGE_H
#define MAIN_PAGE_H

#include <QWidget>
#include <QTimer>  // ekledik...

QT_BEGIN_NAMESPACE
namespace Ui { class main_page; }
QT_END_NAMESPACE

class main_page : public QWidget
{
    Q_OBJECT
    int sayac;   // ekledik...
    QTimer * tiktak; // ekledik...

public:
    main_page(QWidget *parent = nullptr);
    ~main_page();

private:
    Ui::main_page *ui;

private slots:   // ekledik...
    void zamanla();  // ekledik...
};
#endif // MAIN_PAGE_H

main_page.cpp:
#include "main_page.h"
#include "ui_main_page.h"

main_page::main_page(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::main_page)
{
    ui->setupUi(this); // formu oluştur.

    // title barı kaldır...
    setWindowFlags(Qt::Window | Qt::FramelessWindowHint);

    // timerı ayarla  ve başlat.
    sayac=0;
    tiktak = new QTimer(this);
    tiktak->start(20);

    // timer her timeout olduğunda slota bağlan
    connect(tiktak, SIGNAL(timeout()),this,SLOT(zamanla()));


}

main_page::~main_page()
{
    delete ui;
}

void main_page::zamanla()  // timerı karşılayan slot...
{
    sayac++;

    ui->progressBar->setValue(sayac);
    if (sayac >=100)
    {
        tiktak->stop();
        sayac=0;
    }
    //qInfo() << sayac;
}

1633787280670.png


Uygulamamızın birinci aşamasını tamamladık. Açılınca kendi kendine progressbar ı dolduran bir penceremiz var. bu bizim başlangıç penceremiz... Tabi şimdilik bir kapatma şeklimiz olmadığından IDE nin stop butonu ile uygulamayı kapatıyoruz.

Bu pencerede yeni olarak QTimer ile zamanlama yaparak 100 e kadar saydık ve her artış sırasında progressbarın değerini güncelledik. Ayrıca formun başlık kısmını kaldırdık....

II.Bölümde diğer penceremizi tasarlayacağız...
 
Çoklu Pencereye Sahip Uygulamalar ( II. Bölüm ) :

Projemize "add new" seçeneği ile "Qt Designer Form Class" ekleyelim.

1633787803478.png


Karşımıza çıkan template seçimde Widget i seçelim...

1633787873836.png


Sınıf ismini second_page olarak belirleyelim ve ekleme işlemini tamamlayalım...

1633787950829.png


Projemize ilgili h,cpp ve ui dosyaları eklenecektir.

1633788082289.png


ikinci penceremizin tasarımı şu şekilde olsun;

1633788325925.png


Kapat butonu için bağlantıyı da designer içinde hallettik. Hakkında butonu ile de üçüncü sayfamızı açacağız...

Şimdi kodlarda ne yapmamız gerekiyor bakalım. Öncelikle main fonksiyonumuza bir kodlama yapmıyor olsak da içeriğine bir bakalım...

C++:
#include "main_page.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    main_page w;
    w.show();
    return a.exec();
}

main_page sınıfından bir nesne oluşturulmuş ve show() fonksiyonu ile görünür hale getirilmiş....
O halde bizde bir yerlerde second_page sınıfından bir nesne oluşturmalıyız ve sonrasında görünür hale getirmeliyiz. Bu işleri yapacağımız yer ise main_page in kodları...

C++:
#ifndef MAIN_PAGE_H
#define MAIN_PAGE_H

#include <QWidget>
#include <QTimer>  // ekledik...

#include <second_page.h> // ikinci pencere için headerı ekliyoruz.

QT_BEGIN_NAMESPACE
namespace Ui { class main_page; }
QT_END_NAMESPACE

class main_page : public QWidget
{
    Q_OBJECT
    int sayac;   // ekledik...
    QTimer * tiktak; // ekledik...

public:
    main_page(QWidget *parent = nullptr);
    ~main_page();

private:
    Ui::main_page *ui;
    second_page  *w2;  // ikinci pencere için pointer tanımlıyoruz.

private slots:   // ekledik...
    void zamanla();  // ekledik...
};
#endif // MAIN_PAGE_H

main_page.h dosyamızda eklediğimiz sadece iki satır var.

C++:
#include <second_page.h> // ikinci pencere için headerı ekliyoruz.

second_page  *w2;  // ikinci pencere için pointer tanımlıyoruz.

main_page.cpp dosyanın içeriği ise şöyle olmalıdır.
C++:
#include "main_page.h"
#include "ui_main_page.h"

main_page::main_page(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::main_page)
{
    ui->setupUi(this); // formu oluştur.

    // title barı kaldır...
    setWindowFlags(Qt::Window | Qt::FramelessWindowHint);

    // timerı ayarla  ve başlat.
    sayac=0;
    tiktak = new QTimer(this);
    tiktak->start(20);

    // timer her timeout olduğunda slota bağlan
    connect(tiktak, SIGNAL(timeout()),this,SLOT(zamanla()));


}

main_page::~main_page()
{
    delete ui;
    delete w2;
}

void main_page::zamanla()  // timerı karşılayan slot...
{
    sayac++;

    ui->progressBar->setValue(sayac);
    if (sayac >=100)
    {
        tiktak->stop();
        sayac=0;

        // ikinci pencereyi nesne olarak oluşturuyoruz.
        w2 = new second_page();
        w2->show(); // ikinciyi göster
        hide();     // birinciyi gizle...
    }
}

Yine burada da eklediğimiz üç satır kod var. Hazır bir connect bağlantımız ve if yapımız vardı. if bloğunun içine kodumuzu ekledik. w2 nesnesi için remden yer ayırmayı constructor içinde de yapabilirdik. ben ihtiyaç olduğu anda yapmayı tercih ettim...

C++:
        w2 = new second_page();

        w2->show(); // ikinciyi göster

        hide();     // birinciyi gizle...

Bu aşamanın adımları bu kadar. Ekran görüntülerine bakalım.

1633804918618.png


ilk pencere açılıyor. %100 e ulaşınca, ilk pencere kapanıp ikinci pencere açılıyor.

1633805004972.png


Kapat butonuna basarsak uygulama kapanıyor...
 
Çoklu Pencereye Sahip Uygulamalar ( III. Bölüm ) :

Yine Projemize "add new" seçeneği ile QDialog dan kalıtım alan "Qt Designer Form Class" ekleyelim. Sınıf adımız "info_page" olsun.

Görsel tasarımımız şöyledir.

1633806189185.png


Bu sefer farklı bir yöntem kullanacağız. Bu nedenle işlemlerimizde farklı olacak.

Öncelikle ikinci pencereden, üçüncü pencereyi çağıracağımız için second_page.cpp dosyasına header olarak info_page i ekliyoruz.
second_page.cpp:
#include "second_page.h"
#include "ui_second_page.h"
#include "info_page.h"

second_page::second_page(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::second_page)
{
    ui->setupUi(this);
}

second_page::~second_page()
{
    delete ui;
}

Şimdi hakkında butona basıldığında info page in açılmasını istiyoruz. Ozaman bir bağlantı kurmalıyız. Kod yazarak bunu yapabiliriz ama alternatif yöntemleri de öğrenelim. O halde buton üzerinde sağ tıklayarak "go to slot" u seçiyoruz.

1633807065428.png


Clicked i seçip tamam diyoruz...

1633807222614.png


Bu işlem sonunda second_page.h dosyasında slot tanımı otomatik olarak ekleniyor. second_page.cpp dosyasında ise slot için fonksiyon oluşuyor ve kendimizi fonksiyon bloğunun içinde buluyoruz. Yapmamız gereken sadece ne olacağını kodlamak.

C++:
void second_page::on_pushButton_clicked()
{
 info_page w3;                // w3 nesnesini oluşturduk (stack üzerinde)
  w3.exec();                     // exec fonksiyonu ile nesneyi çağırdık.
}

Son olarak info page üzerindeki tamam butonu için yine goto slot yöntemi ile butonun fonksiyonunu yazalım.

C++:
void info_page::on_pushButton_clicked()
{
    close();
}

Ve uygulamamızı test ediyoruz....

1633808693340.png


Hakkında penceresi için farklı bir yöntem kullandık. ikinci sayfadaki yöntemi de kullanabilirdik. Bu yöntemin bariz bir farkı var ve bu yöntemi sadece dialog sınıfından oluşturulan pencereler destekliyor. Son penceremizi açtığımızda ikinci penceremizi gizlemedik. Ancak hakkında penceresi açıkken alta kalan pencere bloke oldu. hakkında penceresi kapatmasan diğer pencere üzerindeki kontrolleri kullanamıyoruz....


Projenin son halini Ekte bulabilirsiniz.


Bu mesajla birlikte bu yazı dizisine son veriyorum. Eğer Qt ye meraklanıp bu aşamaya kadar gelirseniz bence gerisi bence çorap söküğü gibi gelecektir. Bundan sonraki paylaşımlarımda sadece kodları ile birlikte örnek proje olarak paylaşım yapmayı düşünüyorum. Gerekli açıklamayı kod içine eklerim.
 

Ekler

  • Multi_Window.zip
    7.5 KB · Görüntüleme: 109
Demo Uygulama 2 :

Bu uygulama decimal-hex-binary sayıları birbirine dönüştürür.

1633856947741.png


main_form.h:
#ifndef MAIN_FORM_H
#define MAIN_FORM_H

#include <QWidget>
//#include <QString>

namespace Ui {
class main_form;
}

class main_form : public QWidget
{
    Q_OBJECT

public:
    explicit main_form(QWidget *parent = nullptr);
    ~main_form();

private slots:
    void on_box_dec_textChanged(const QString &arg1);
    void on_box_hex_textChanged(const QString &arg1);
    void on_box_bin_textChanged(const QString &arg1);
    void on_pushButton_clicked();

private:
    Ui::main_form *ui;
    int val;
    bool ok;
    QString str;

};

#endif // MAIN_FORM_H
main_form.cpp:
#include "main_form.h"
#include "ui_main_form.h"

main_form::main_form(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::main_form)
{
    ui->setupUi(this);

    setFixedSize(350,120);      // pencere boyutunu sabitle
    on_pushButton_clicked();    // açılışta kutuları temizle

    setWindowTitle("Demo 2...");

}

main_form::~main_form()
{
    delete ui;
}

void main_form::on_box_dec_textChanged(const QString &arg1)
{
     str=arg1; // olmasada olur. Sırf arg1 i kullanmadın demesin diye..

    // desimal giriş oldu.
    // desimal kutusunu int e çevirelim.
    val=ui->box_dec->text().toInt();
    // int değeri 16 lı gösterim ile string e çeivrelim.
    str = str.setNum(val,16).toUpper();
    // hex kutusunda gösterelim.
    ui->box_hex->setText(str);

    // int değeri binary  gösterim ile string e çeivrelim.
    str = str.setNum(val,2);
    // binary  kutusunda gösterelim.
    ui->box_bin->setText(str);
}





void main_form::on_box_hex_textChanged(const QString &arg1)
{
    str=arg1; // olmasada olur. Sırf arg1 i kullanmadın demesin diye..

    // hex giriş oldu.
    // hex kutusunu int e çevirelim.
    val=ui->box_hex->text().toInt(&ok,16);
    // int değeri decimal gösterim ile string e çeivrelim.
    str = str.setNum(val,10);
    // decimal kutusunda gösterelim.
    ui->box_dec->setText(str);

    // int değeri binary  gösterim ile string e çeivrelim.
    str = str.setNum(val,2);
    // binary  kutusunda gösterelim.
    ui->box_bin->setText(str);
}



void main_form::on_box_bin_textChanged(const QString &arg1)
{
    str=arg1; // olmasada olur. Sırf arg1 i kullanmadın demesin diye..

    // binary giriş oldu.
    // binary kutusunu int e çevirelim.
    val=ui->box_bin->text().toInt(&ok,2);
    // int değeri decimal gösterim ile string e çeivrelim.
    str = str.setNum(val,10);
    // decimal kutusunda gösterelim.
    ui->box_dec->setText(str);

    // int değeri 16 lı gösterim ile string e çeivrelim.
    str = str.setNum(val,16).toUpper();
    // hex kutusunda gösterelim.
    ui->box_hex->setText(str);
}

void main_form::on_pushButton_clicked()
{
    // temizle butonu
    ui->box_bin->setText("0");
    ui->box_dec->setText("0");
    ui->box_hex->setText("0");
}
 

Ekler

  • demo_2.zip
    5 KB · Görüntüleme: 105
1639339685433.png


Qt nin çapraz platform özelliği sayesinde windows için yaptığım projeyi android için tekrar düzenleyerek telefon üzerinde çalıştırdım. Qt ile android kullanımı konusunda ilk adımı atmış oldum.

Nasıl yapıldığına konusuna blog sayfam üzerinden ulaşabilirsiniz...

 

Forum istatistikleri

Konular
5,789
Mesajlar
99,015
Üyeler
2,464
Son üye
s4met

Son kaynaklar

Son profil mesajları

cemalettin keçeci wrote on HaydarBaris's profile.
barış kardeşim bende bu sene akıllı denizaltı projesine girdim ve sensörleri arastırıyorum tam olarak hangi sensör ve markaları kullandınız yardımcı olabilir misin?
m.white wrote on Altair's profile.
İyi akşamlar.Arabanız ne marka ve sorunu nedir.Ben araba tamircisi değilim ama tamirden anlarım.
* En mühim ve feyizli vazifelerimiz millî eğitim işleridir. Millî eğitim işlerinde mutlaka muzaffer olmak lâzımdır. Bir milletin hakikî kurtuluşu ancak bu suretle olur. (1922)
Kesici/Spindle hızı hesaplamak için SpreadSheet UDF'leri kullanın, hesap makinesi çok eski kalan bir yöntem :)
Dr. Bülent Başaran,
Elektrik ve Elektronik Mühendisi
Yonga Tasarım Özdevinimcisi
Üç güzel "çocuk" babası
Ortahisar/Ürgüp/Konya/Ankara/Pittsburgh/San Francisco/Atlanta/Alaçatı/Taşucu...

Back
Top