27 Eylül 2010 Pazartesi

Qt

"Service Manager" kodlarını incelemeye koyulmadan önce yapmam gereken Qt yi anlamaktı. Bu sadece "Service Manager" kodları için değil projenin tamamı için bana lazım olan şey. Bunun için "Qt4 ile C++ Gui programlama" (C++ GUI Programming with Qt4) adlı GPL lisanslı kitabı okumaya başladım. Çalıştığım ve anladığım kısımlar özet olarak şöyle:

Merhaba Qt

1. #include QApplication
2. #include QLabel
3.
4. int main(int argc, char *argv[])
5. {
6. QApplication app(argc,argv);
7. QLabel *label= new QLabel("Hello Qt!");
8. label-> show();
9. return app.exec();
10.
11. }

1. ve 2. satırlarda QApplication ve QLabel sınıfları dahil ediliyor. QApplication sınıfı uygulamayı yönetmek için kullanılıyor. QLabel sınıfı "Hello Qt!" yazdıracak olan "widget"i oluşturmamızı sağlıyor.

8.satır ekrana çıktı veren fonksiyonun çağrıldığı kısım.
9. ise uygulama kontrolü Qt ye geçer ve uygulama bir olay döngüsü içine girer.

Olay Döngüsü (event loop): Programın bir kullanıcı eylemi (fare tıklaması veya bir tuşa basmak gibi) için beklediği bir tür bekleme modudur.

Bağlantılar Kurma

Kitapta şöyle bir örnek yer alıyor: Kullanıcının "Quit" tuşuna bastığında programdan çıkan bir pencere...

Burada bir kullanıcı girdisi bekleme durumu var. Bunun için bağlantı kurmak gerekir.

Bunu şöyle yapıyoruz:
1 #include QApplication
2 #include QPushButton
3 int main(int argc, char *argv[])
4 {
5 QApplication app(argc, argv);
6 QPushButton *button = new QPushButton("Quit");
7 QObject::connect(button, SIGNAL(clicked()), &app, SLOT(quit()));
8 button->show();
9 return app.exec();
10 }

Öncelikle bir önceki örnekten farklı olarak QPushButton sınıfından buton örneği oluşturuluyor. Bu butonu QObject sınıfının connect() metoduyla butona tıklandığında quit() metodunu çağırması sağlanıyor.
SIGNAL() : Kullanıcıdan beklenen olay.
SLOT() : Bu olay karşısında verilecek tepki.

Widgetleri Yerleştirme

Bunun için kullanıcıya yaşını soran ve kullanıcının yaşını girebilmesi için bir spinbox ve bir slider sağlayan bir uygulama örneği var kitapta.
Birden fazla Widget'i bir pencerede gösterebilmek yerleşimlerini ayarlamak için Layoutlar kullanılır.
#include QApplication
#include QHBoxLayout
#include QSlider
#include QSpinBox

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

QApplication app(argc, argv); /*Uygulamayı yönetmek için oluşturuluyor.

QWidget *window = new QWidget; /* Yeni bir pencere
window->setWindowTitle("Enter Your Age"); /* Pencerenin başlığı

QSpinBox *spinBox = new QSpinBox; /* Spinbox örneği oluşturuluyor
QSlider *slider = new QSlider(Qt::Horizontal); /* Slider örneği

spinBox->setRange(0, 130); /* Spinbox yapılandırması
slider->setRange(0, 130); /* Slider yapılandırması

/*Bağlantı kurulumu*/
QObject::connect(spinBox, SIGNAL(valueChanged(int)),slider, SLOT(setValue(int)));
QObject::connect(slider, SIGNAL(valueChanged(int)),spinBox, SLOT(setValue(int)));
spinBox->setValue(35); /*Spinbox 'a öntanımlı değer verme*/

/*Widgetleri Yerleştirme*/
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(spinBox);
layout->addWidget(slider);
window->setLayout(layout);

/*Göster*/
window->show();

/*Olay döngüsü*/
return app.exec();
}

QHBoxLayout kütüphanesi widgetleri yerleştirmemize imkan verir. QSlider slider, QSpinbox ise spinbox oluşturmaya yarar.

Widgetlerin yerleşimiyle ilgili üç ana sınıf:
  • QHBoxLayout : Parçacıkları horizontal olarak soldan sağa doğru yerleştirir.
  • QVBoxLayout : Parçacıkları düşey olarak üstten aşağıya doğru sıralar.
  • QGridLayout: Parçacıkları bir ızgaraya yerleştirir.

Diyalog Oluşturma
Diyalog : Kullanıcı ile programın iletişim kurmasını sağlar. Bir diyalog örneği:
Bu örnekte kodlar finddialog.h ve finddialog.cpp dosyalarından oluşuyor. Bunun sebebi diyaloğu bir sınıfla meydana getirmek. Böylece kendi signal ve slot ları olan bağımsız bir bileşen olacak.

finddialog.h

#ifndef FINDDIALOG_H
#define FINDDIALOG_H
#include QDialog

class QCheckBox; /*seçim kutusu oluşturmak için sınıf*/
class QLabel; /*kullanıcıya bir şeyler söylemek için Label */
class QLineEdit; /*kullanıcının bize bir şeyler söylemesi için yazı kutucuğu*/
class QPushButton; /*tıklamak için buton*/

class FindDialog : public QDialog /* FindDialog sınıfı QDialog sınıfından türetilir.*/
{
Q_OBJECT
/*Q_OBJECT: sinyaller ve slotların bulunduğu tüm sınıflarda bulunması gereken makro*/

public:
FindDialog(QWidget *parent = 0);
/*parent parametresi sınıfın , türetildiği sınıfı gösterir. Varsayılan değeri "null"dır.*/

signals:
void findNext(const QString &str, Qt::CaseSensitivity cs); /*ileri sinyali*/
void findPrevious(const QString &str, Qt::CaseSensitivity cs);/*geri sinyali*/

private slots: /*yuvaların tanımlanması*/
void findClicked();
void enableFindButton(const QString &text);
private:
/*Burada sınıf tanımlamalarını önce yaptık. Bunu yaparken işaretçi kullandık. Böylece derleme işinin daha kolay olması sağlanmış oldu.*/
QLabel *label;
QLineEdit *lineEdit;
QCheckBox *caseCheckBox;
QCheckBox *backwardCheckBox;
QPushButton *findButton;
QPushButton *closeButton;
};
#endif

Şimdi sıra *.h uzantılı dosyanın import edileceği koda geldi. Burada tanımladığımız sınıfları, makroları (signal, slot) kullanıyoruz.
finddialog.cpp

#include QtGui /*Qt arayüz kitaplığı. Bu kitaplık QtGui ve QtCore modüllerinin parçası olan tüm sınıfların tanımlarını içerir. Bunu başta import etmek bizi her sınıf için ayrı ayrı dahil etme sıkıntısından kurtarır.*/

#include "finddialog.h" /*Bizim tanımladığımız kütüphane */

FindDialog::FindDialog(QWidget *parent): QDialog(parent) /*Sınıfın yapıcısına parent parametresini girerek çocuk widgetler oluşturulur.*/
{
label = new QLabel(tr("Find &what:")); /*tr() fonksiyonu içene aldığı cümleyi başka dillere çevirmek için kullanılır. Ampersan işareti kısayol tanımlamak için kullanılır. Alt+ W ye basıldığı zaman what kelimesinin üstü işaretlenecektir.*/
lineEdit = new QLineEdit;
label->setBuddy(lineEdit); /*setBuddy fonksiyonu da aynı işi yapmaktadır.*/
/*Check box oluşturuluyor*/
caseCheckBox = new QCheckBox(tr("Match &case"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
/*find butonu oluşturuluyor.*/
findButton = new QPushButton(tr("&Find"));
findButton->setDefault(true);
findButton->setEnabled(false);
/*kapama tuşu*/
closeButton = new QPushButton(tr("Close"));

connect(lineEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(enableFindButton(const QString &))); /*metin düzenleyicideki metin değiştiği zaman enabeFindButton fonksiyonu çağırılır.*/
connect(findButton, SIGNAL(clicked()),this, SLOT(findClicked())); /*find butonuna tıklandığında findClicked() fonksiyonu çağırılır.*/
connect(closeButton, SIGNAL(clicked()),this, SLOT(close())); /*close butonuna tıklandığında close() fonksiyonu çağırılır.*/

/*label ve lineedit parçacıklarının soldan sağa yerleştirilmesi*/

QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);

/*checkboxlar ve yukarıdaki topLeftLayout'un sol tarafta yukarıdan aşağıya yerleştirilmesi */
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(backwardCheckBox);

/*butonların sağ tarafta yukarıdan aşağıya yerleştirilmesi*/

QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(findButton);
rightLayout->addWidget(closeButton);
rightLayout->addStretch();
/*sol taraf ile sağ tarafın soldan sağa hizalanması*/
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);

setWindowTitle(tr("Find"));/*pencerenin başında gösterilecek metin düzenleniyor.*/
setFixedHeight(sizeHint().height()); /*parçacıkları ideal boyutlarına getiriyor.*/
}
void FindDialog::findClicked() /*find butonuna tıklandığında yapılacak olan işler*/
{
QString text = lineEdit->text(); /*metin düzenleyiciye yazılan yazılar text değişkenine atanıyor.*/
Qt::CaseSensitivity cs =caseCheckBox->isChecked() ? Qt::CaseSensitive
:Qt::CaseInsensitive;
if (backwardCheckBox->isChecked()) {
emit findPrevious(text, cs); /*emit anahtar kelimesi Qt'ye özeldir. Dışa aktarmak anlamına gelir.*/
} else {
emit findNext(text, cs);
}
}

void FindDialog::enableFindButton(const QString &text)/*bu fonksiyon düzenleyiciye bir şeyler yazıldığı zaman çağrılır. Find butonunu seçilir kılar.*/
{
findButton->setEnabled(!text.isEmpty());
}

Şimdi de FindDialog parçacığını test etmek için main.cpp dosyasını oluşturuyoruz.

#include QApplication
#include "finddialog.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
FindDialog *dialog = new FindDialog;
dialog->show();
return app.exec();
}

26 Eylül 2010 Pazar

Qt'ye Giriş

İlk olarak şimdiye kadar hiç bir bilgimin olmadığı "Qt" hakkında internette bilgi toplamaya başladım. Genelde grafiksel arayüz tasarımında kullanıldığını öğrendim. Ardından paket yöneticisinden Qt demo isimli yazılımı indirdim. Qt ile neler yapılabileceğini bu yazılımı karıştırarak öğrendim.

Bu ve buna benzer birkaç kısa bir bilgi edindikten sonra Qt'yi kendim deneyip öğrenmem gerektiği kanısına vardım ve bunu da Qt designer yazılımıyla yapabileceğime karar verdim. Hemen paket menajerinde aratıp bilgisayarıma kurdum.


Qt designer'ı çalıştırdığımda karşıma ilk olarak New Form isimli pencere geldi. İstenilen form (main window/widget vs) seçildikten sonra layout kullanarak veya elle objeleri forma yerleştirdim Bu objeleri forma yerleştirirken sadece sürükleyip bırakmak yeterli oluyor. Formun görünümü bittikten sonra kaydettim. Oluşan dosya .ui uzantılı(user interface) bir dosyaydı. Dosyayı kwrite ile açıp nasıl kaydedildiğini inceledim.Dosya xml formatında kaydedilmişti.

Qt designer'da kullandığımız her objenin özelliklerini (size , font, text vs) Property Editor'de
belirtebiliriz. Bu objenin özelliklerine oluşan ui uzantılı dosyayı metin düzenleyicisiyle açıp bakabiliriz.
Fakat oluşan ui dosyası sadece objelerini text , visible/invisible , font ,size gibi özellikleri tutması yetmezdi. Bu nesneler istenilen herhangi bir durumda oluşan olay karşısında tepki vermesi gerekmekteydi. Programı biraz karıştırdıktan sonra edit menüsünde signals/slots ile yapılabileceğimi buldum(Bu moda F4 tuşuyla da geçilebilir). Objeler arasında ilişkiyi kurdum ve kaydettim. Oluşan dosyayı kwrite ile açtım. Oluşturduğum 2 dosya arasında etiketinde bir farklılık oluştu.

pushButton
clicked()
radioButton
click()



Burada sinyali oluşturacak nesne pushButton nesnesidir. Bu nesnenin sinyali ise clicked()'dir. Bu sinyale bağlanan yuvanın nesnesi de radioButton , yuva ise click()dir. Yani pushButton nesnesi tıklandığında radioButton nesnesinin click fonksiyonu çalıştırılacak.

Buraya kadar Qt nesneleri , Qt designer yazılımına giriş yaptım. Service managerın kaynak kodlarını az da olsa Qt nesneleri hakkında bilgi edindikten sonra indirmeyi düşündüm. Bu dosyaları indirdikten sonra ui klasörünün içindeki dosyaları Qt designer ile açıp xml formatlı dosyalar ve python dosyaları arasındaki ilişkiyi anlamaya çalışacağım.

KdeServiceManager'da Kullanılan Bazı Qt Sınıfları

Qt her biri kendi kütüphanesinde bulunan modüllerden meydana gelir.Bunlardan en önemlileri QtGui, QtCore,QtNetwork,QtOpenGL,QtScript,QtSql,QtSv ve QtXmldir.
QtGui başlık dosyası QtCore ve QtGui modüllerinin parçası olan tüm sınıfların tanımından meydana gelir.Bu başlık dosyasını dahil etmek bizi her sınıfı tek tek dahil etme zahmetinden kurtarır. QCheckBox QLabel QLineEdit ve QPushbutton'ı tek tek dahil etmektense QtGui'yi tek başına dahil edebiliriz.
QtCore Containerlar ,Giriş-Çıkış ve Multithreating gibi birçok temel sınıf sağlamaktadır.

QListWidgetItem Bir item QListWidgette bulunan bir öğeyi temsil eder.Her öğe bir çok eleman tutar ve bunu en uygun şekilde temsil eder.Liste widget belirtilerek inşa edildiğinde elemanlar bir listeye otomatik olarak eklenirler.
QWidget bütün kullanıcı arayüzü sınıfları için temel bir sınıftır.Widget kullanıcı arayüzünün atomudur.Pencere sisteminde klavye ,mouse ve diğer olayları alır, ekranda kendisinin bir temsilini gösterir.Her bir widget bir dikdörtgendir ve Adan Zye sıralanırlar.Bir widget parent'ı veya önündeki bir widget tarafından kırpılabilir. Ayrıntılı bilgi için [1] adresinden yararlanabilirsiniz.

[1]PySide Modules
Kaynak Kodu, Arayüz, Dil Dosyaları

ilk olarak Qt4 Tasarım aracından bahsetmek istiyorum. Pardus paketleri arasında bulunan qt-designeri sistemimize kurarak başlayabiliriz. Qt designer Visual studio,Netbeans gibi alıştığımız ortamlara çok benzeyen sürükle bırak mantığı üzerine kurulmuş arayüz tasarlamak için kolay, kullanışlı bir araçtır. Qt degisnerdan bahsetmemin sebebi svn üzerinden ve http://svn.pardus.org.tr/uludag/trunk/kde/network-manager/manager/ adresinden indirdiğim ve kaynak kodlarını inceleyeceğim network manageri arayüzünü qt designer aracı ile incelemiş olmam.Elbette bir çok araç bu işi görür ama bana kolay gelen bu oldu. Svnden indirdiğimiz network manager klasörü içerisindeki manager klasörü altındaki src klasoründe inceleyeceğimiz programın kaynak kodlarına erişebiliriz. Peki arayüzü nasıl kod aşamasında görebiliriz. Bunun için network manager/manager altındaki ui klasoründeki ui uzantılı kodları qt designer ile açıp ctrl+R tuşuna basarak çalıştırabiliriz. Bu noktada Qtdesigner önemlidir. Kaynak kodları .py uzantılı olduğu için Kwrite gibi bir araçla da açıp okuyabiliriz. Yine manager içerisindeki po klasorunden dil paketlerine ulaşabiliriz. Burada referans olarak ingilizce alınmış ve İngilizce üzerinden birçok dile destek verilmiştir. Türkçeye tr.po yu Kwrite ile açarak ulaşabilriz.

Qt Designer

Amacımız herhangi bir aracın nasıl çalışacağını anlatmak degil elbet ama Qt Designer üzerinden basit bir arayüzün nasıl tasarlanacağından bahsetmek istiyorum çünkü burada oluşan kod c++ kodu olacak ve bunu python koduna çevirmemiz gerekecek.Projemizi Main Window açıyoruz ve kontrollerimizi açılan formumuza sürüklüyoruz.


Bu aşamada F4 ve tuşuna basarak butonlarımıza olay ekleyebiliriz.(F3 ile bu moddan çıkabiliriz)




Ctrl+R ile programımızı çalıştırabiliriz. Oluşturduğumuz programın kodlarına sol üst köşedeki menüden form->view code diyerek erişebiliriz. Oluşan code c++ kodudur. Bizim bunu python koduna çevirmemiz gerekir. Programımızı kaydettigimiz(.ui uzantısı ile) yere giderek konsol üzerinden;
pyuic4 deneme.ui -o deneme.py komutu ile python koduna dönüştürebiliyoruz.

Kaynak Kodun İncelenmesi

Network Manager altındaki src klasorunden bahsetmiştim. Kaynak kodu incelemeye bu klasör altındaki base.py herhangi bir metin editörü ile inceleyerek başlamanın kolaylık olduğunu düşünüyorum. Kaynak kodu okurken pythondan aşina olduğumuz terimlerin sadece anlaşılması biraz zor olsa da anlayabileceğimiz şekilde olduğunu görüyoruz.






Qt'yi anlamak...

İlk olarak http://websvn.pardus.org.tr/pardus/2011/devel/desktop/kde/admin/service-manager/ adresinden service-managerın kaynak kodlarını indirebileceğim adrese giderek service-managerı indirdim kodlara göz atarken bir yandan da qt ye bakabileceğimi düşündüm. service-managerın kaynak dosyasını açtığımda içinden çıkanları incelemeye koyuldum.

data PKG-INFO po README setup.py src ui (data, po src ve ui dizindir.)

src dizini içindeki .py uzantılı dosyalara kısaca göz attıktan sonra Qt ile programlama ile ilgili bulduğum kaynağı okumaya başladım. Qt ile ilgili kaynak olarak "http://qt.comu.edu.tr/qtgiris/teksayfa/index.html#d0e638" sayfasını kullandım.

burada arayüz ile ilgili kodlar ui(user interface) klasörü içinde yer almakta ve bu dizin içinde dosyalar .ui uzantılı olduğundan bunları .py uzantısına çevirdim buradaki main.ui dosyasında temel olarak arayüzde bulunacak araçların po dizininde ise dil ile .po uzantılı dosyalara erişebiliriz. Bunlardan tr.po uzantısını konsoldan vi editörü ile açarak inceledim. Bu dosyanın içinde main.ui ve item.ui dosyaları içerisindeki arayüzdeki yazı kısımlarının türkçe karşılıkları yazılıyor. Bu tabikide sadece tr için geçerli farklı dil dosyalarıda bulunmakta. Bundan sonra service-manager içinde src klasöründeki py uzantılı kaynak kodları incelemek geliyor. Ancak bunu yapabilmek için service-manager da kullanılan kodlarda ne iş yaptığını bilmediğim kodların ne yaptığını öğrenerek devam edeceğim.

PyQt

Qt, Norveçli firma Trolltech tarafından geliştirilmektedir. Dikkatleri ilk kez KDE masaüstü ortamının bu araç takımını kullanması ile çekmiştir. İlk sürümleri özgür olmayan bir lisansla dağıtılmaktadır. Linux gibi hızla büyüyen bir özgür işletim sisteminin en popüler masaüstü ortamının özgür olmayan bir araç takımı kullanması elbette hoş karşılanmamıştır. Tepki olarak kullanarak GTK+ ve GNOME masaüstü geliştirilmeye başlanmıştır. Bununla birlikte Harmony denen ve Qt'ye benzeyen bir araç takımı da geliştirilmeye başlanmıştır.

Trolltech, bu tepkilere karşı Qt'yi QPL denen, GPL benzeri bir lisansla yayınlamıştır. Bunu da beğenmeyen özgür yazılım takipçileri sonunda Trolltech'e Qt'yi GPL olarak dağıtmasından başka çare bırakmamıştır. Eğer olur da Trolltech'in başına bir şey gelirse diye, hem Qt hem de KDE kütüphanelerinin gelişimini devir alacak KDE Free Qt Foundation kurulmuştur.

Yazmis oldugum şu iki paragrafı Qt'nin wiki sayfasından aldım Qt ile gtk arasındaki ilişkiyi okudugum zaman Linux ve Windows, Java ve C# , Silverlight ile Flash arasındaki rekabeti gözümün önüne getirdi..

Biz de Tübitak ortakli yapacağımız çalışmalar ile python ve Qt üzerine yazılmış birden fazla uygulamayi Pardus üzerinde Gnome masasüstünün de bulunması için python ve gtk üzerine yapacağız tabiki de gnome için gerekli olan bağımlılıkları da unutmamak gerek..Elbette Gnome Masaüstü ortamını sadece bağımlılıklarını sisteme yükleyerek bir pardus gnome ikilisi yaratabilirdik fakat pardus'un kde ortami ile beraber gelen uygulamaları(network manager,service manager v.s.) gnomeda da calismasını sağlamak gerekirdi.

Şimdi az da olsa kod tarafına geçelim.Qt ile Python arasındaki ilişkiyi bir nevi Java ve Swing ikilisine veya .Net ortamında C# ve WinForms ikilemesine benzetebiliriz.Qt bileşenleri ile hazirladığımız arayüz .ui uzantılı olup pyqt modulunu sistemimize dahil ederek python tarafından kullanabiliriz.(Eğer Windows ortamında calısıyorsak pyqt modulunu python 2.5,2.6,2.7 hangi sürümü altında derlemişssek o sürüm altında calistirabiliriz.Linux da böyle bi sorun yok işletim sisteminden kaynaklanan bir sorun bu.) Qt ile hali hazirda kullanacağımız baya metotda bulunmakta bunu Qt ortamında signal slot'larımızı düzenleyerek bu metotlardan da yararlanabilriz.Elbette bizim yazacağım python fonksiyolarini signal slotlarımız ile ilişkilendirebiliriz.Signal Slot kavramı .Net ortamındaki Event Handler programlama mantıgı ile benzer yanlar gostermektedir.

ve son olarak Qt ile hazirlayacağımız .ui(user interface) arayüzü default olarak c++ ile gelmekte. (Bunu Qt Designer-> Form -> View Code sekmesinden gorebiliriz.) Hazırladıgımız bu arayüz bileşenleri kullanmak için pyuic4 komutu ile bir python sınıfına donusturmemiz gerekir.

Kullanımı:

pyuic4 deneme.ui -o deneme.py