QT 4 Ile C++ GUI Programlama

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 224

Qt 4 ile C++ GUI Programlama

YASAL AIKLAMALAR
Bu belgenin, Qt 4 ile C++ GUI Programlama evirisinin telif hakk 2010 Ufuk Uzuna, zgn ngilizce srmnn telif hakk 2008 Trolltech ASAe aittir. Bu belgeyi, Open Publication Licence lisansnn 1.0 ya da daha sonraki srmnn koullarna bal kalarak kopyalayabilir, databilir ve/veya deitirebilirsiniz. Bu Lisansn zgn kopyasn http://www.opencontent.org/openpub/ adresinde bulabilirsiniz. BU BELGE "CRETSZ" OLARAK RUHSATLANDII N, ERD BLGLER N LGL KANUNLARIN ZN VERD LDE HERHANG BR GARANT VERLMEMEKTEDR. AKS YAZILI OLARAK BELRTLMED MDDETE TELF HAKKI SAHPLER VE/VEYA BAKA AHISLAR BELGEY "OLDUU GB", AKAR VEYA ZIMNEN, SATILABLRL VEYA HERHANG BR AMACA UYGUNLUU DA DAHL OLMAK ZERE HBR GARANT VERMEKSZN DAITMAKTADIRLAR. BLGNN KALTES LE LGL TM SORUNLAR SZE ATTR. HERHANG BR HATALI BLGDEN DOLAYI DOABLECEK OLAN BTN SERVS, TAMR VEYA DZELTME MASRAFLARI SZE ATTR. LGL KANUNUN CBAR ETT DURUMLAR VEYA YAZILI ANLAMA HARCNDE HERHANG BR EKLDE TELF HAKKI SAHB VEYA YUKARIDA ZN VERLD EKLDE BELGEY DETREN VEYA YENDEN DAITAN HERHANG BR K, BLGNN KULLANIMI VEYA KULLANILAMAMASI (VEYA VER KAYBI OLUMASI, VERNN YANLI HALE GELMES, SZN VEYA NC AHISLARIN ZARARA URAMASI VEYA BLGLERN BAKA BLGLERLE UYUMSUZ OLMASI) YZNDEN OLUAN GENEL, ZEL, DORUDAN YA DA DOLAYLI HERHANG BR ZARARDAN, BYLE BR TAZMNAT TALEB TELF HAKKI SAHB VEYA LGL KYE BLDRLM OLSA DAH, SORUMLU DELDR. Tm telif haklar aksi zellikle belirtilmedii srece sahibine aittir. Belge iinde geen herhangi bir terim, bir ticari isim ya da kuruma itibar kazandrma olarak alglanmamaldr. Bir rn ya da markann kullanlm olmas ona onay verildii anlamnda grlmemelidir.

Qt 4 ile C++ GUI Programlama

EVRMENN NOTU
Merhaba, Bu eviri eserin ieriinden bahsetmeden nce, onun var olma sebebini ve servenimi anlatmak istiyorum: Getiimiz Mays aynda, Sakarya niversitesi Bilgisayar Mhendislii blmnde henz daha 1. Snf rencisiyken ve hl konsol ekranda alan programlar yazmakla megulken, gerek arkadalarm gerekse ben grafik arayz programlamak iin can atyorduk. Dolaysyla hemen aratrmaya koyuldum. Hli hazrda C/C++ ile programlar yazdmdan, doal olarak C/C++ kullanarak grafik arayz programlamann yollarn aryordum. Biraz aratrmadan sonra Qt ile tantm. Qt hakkndaki kstl sayda Trke kaynak bile, Qt ile grafik arayz programlamann en iyi tercihlerden biri olduuna beni ikna etti. Ancak demin de bahsettiim gibi Trke kaynaklarn kstl olmas nedeniyle Qtu renmemin zorlayc bir sre olacan kestirebiliyordum. ngilizce kaynaklara ynelmem gerekiyordu. Ancak benim gibi daha nce ngilizce bir kaynaktan yararlanarak bir eyler renmeye kalkmam biri iin bu i hite kolay olmayacakt. Daha sonra C++ GUI Programming With Qt 4; 2nd Edition kitabnn e-kitap versiyonunu edindim. Kitaba yle bir gz gezdirince, anlalr ve yararl bir kaynak olduuna karar verdim. Kitab okumaya balaynca, ilk izlenimlerimde hakl olduumu anladm. Ancak, servenimin en banda yaadm Trke kaynak skntsn dnnce, yapmam gereken ey apak ortadayd: Bu kitab Trkeye evirmek. Tabi bunda kitabn Open Publication Lisence ile yaynlam olmas ve ngilizcemin ok ok iyi olmamasna ramen benim iin bile anlalr olmas(baz ksmlar hari) da etkili oldu. Bylece ie koyuldum. Fakat dndmden yava ilerliyordum. eviri konusundaki tecrbesizliimin de bunda etkiliydi elbette. Yine de, konu hakknda geni bir Trke kaynan olmayn ve ak kaynak dnyasna bir katkda bulunabilme ihtimalini dnmek, baladm ii bitirmemi salad. Bu kitab (C++ GUI Programming With Qt 4; 2nd Edition) evirmemin nedeninden biraz bahsetmi olsam da, en nemli nedenden bahsetmedim; o da, bu kitabn uan iin piyasadaki en gncel kaynak olmas (2008 ylnda yaynlanmasna ramen). Dier taraftan, Amazon.comdan takip ettiim kadaryla biri Mays biride Austos aynda olmak zere iki kitap yolda. Umarm bu kitaplarn Trkeye kazandrlmas bu kadar gecikmez. Kitabn ierii hakknda ise unlar syleyebilirim: Bu eviri eser, bahsi geen kitabn tamamnn deil, (ufak eksiklerle birlikte) 12 blmnn evirisini iermektedir. evirdiim blmleri belirlerken, temel ve orta seviyede Qt programlama renmek isteyenlere hitap etme amacn gttm. eviriyle alkal ise, eksikliklerin ve yanllklarn olabileceini belirtmeliyim. Ancak, bunlarn olabildiince az olmas iin elimden geleni yaptm da bilmelisiniz. Son olarak, eviri konusunda beni cesaretlendiren babama, Salt Okunur E-Dergide yazdm yazlar aksattmda bana ar tepki gstermeyen dergi ekibime ve zellikle attm mesaja karlk, kitabn evirmemden memnuniyet duyacan bildirerek beni mutlu eden, kitabn yazarlarndan Jasmin Blanchettea teekkrlerimi sunmak isterim. Okuyan herkese faydal olmas dileiyle, Ufuk Uzun stanbul, Trkiye Mart 2010 ufuk.uzun@qtturk.tk

Qt 4 ile C++ GUI Programlama

EDTRN NOTU
Sevgili Okuyucu, alan bir programc olarak, her gn Qtu kullanrm ve Qtun C++ programclarna getirdii organizasyon, tasarm ve gten gerekten etkilenmi bulunuyorum. Qt hayata bir apraz-platform GUI ara-takm olarak balamasna ramen, gndelik programlamann her cephesi iin tanabilirlii salayacak ekilde geniletildi: dosyalar, ilemler, a ve veritaban eriimi bunlardan bazlardr. Qtun bu geni uygulanabilirlii sayesinde, sahiden kodunuzu bir kez yazarsnz ve sadece onu baka platformlar zerinde yeniden derleyerek, uygulamanz o platformlarda da altrabilirsiniz. Bu, mterilerinizin, sizin yazlmlarnz baka platformlar zerinde kullanmas icap ettiinde fevkalade deerlidir. Elbette, eer bir ak kaynak gelitiricisiyseniz, ak kaynak lisans da mevcut olan Qtun sunduu her olanaktan siz de faydalanabilirsiniz. Qt kapsaml bir evrimii yardm salar ve bu, yneleceiniz ilk bavuru kaynanz olacaktr. rnek programlar yararldr, fakat tersine mhendislik ve sadece rnekleri okumak Qtu doru kullanabilmek iin yeterli olmayacaktr. te bu noktada, bu kitap resmin iine giriyor. Bu gerekten derli toplu ve dzgn bir kitaptr. ncelikle, Qt zerine yazlm resmi ve en ok bahsi geen kitap. Ve ayn zamanda harika bir kitap: iyi organize edilmi, iyi yazlm ve takip edilmesi ve renilmesi kolay. Bu kitab okurken, tpk benim gibi ok eleneceinizi ve ondan ok ey reneceinizi umuyorum. Arnold Robbins Nof Ayalon, srail Kasm 2007

Qt 4 ile C++ GUI Programlama

NSZ
Neden Qt? Neden programclar bizim gibi Qtu seerler? Elbette cevaplar aktr: Qtun tek kaynak(singlesource) uyumluluu, zelliklerinin zenginlii, C++ performans, kaynak kodlarnn kullanlrl, dokmantasyonu, kaliteli teknik destei ve Trolltechin parlak pazarlama gereleri arasnda bahsedilen dier eler. Bunlarn hepsi ok iyi, fakat en nemli noktay karyor: Qt baarl nk programclar onu seviyor. Nasl olurda programclar bir teknolojiyi sever, fakat dierini sevmez? Kiisel olarak ben, yazlm mhendislerinin iyi hissettiren teknolojilerden holandklarn, fakat yle olmayan dier her eyden nefret ettiklerini dnyorum. Byle olmasa baka nasl aklayabilirdik; en parlak programclardan bazlarnn bir video kaydediciyi programlamak iin yardma ihtiya duymalarn, ya da birok mhendisin irketlerindeki telefon sisteminin ileyiinden dolay zgn grnmelerini? Trolltechte telefon sistemimiz, dier kiinin dhili numarasn girmeye msaade etmeden nce bizi iki saniye boyunca *a basmaya zorluyor. Eer bunu yapmay unutur ve hemen dhili numaray girmeye balarsanz btn numaray yeniden girmek zorunda kalyorsunuz. Peki neden *? Neden # ya da 1 ya da 5 ya da telefonun stndeki dier 20 tutan herhangi biri deil? Ve neden iki saniye, neden bir ya da ya da bir buuk deil? Neden bunlardan hibiri deil? Bu yzden telefonu sinir bozucu bulurum ve mmknse kullanmaktan kanrm. Ben de dhil hi kimse geliigzel eyler yapmak zorunda olmaktan holanmaz, zellikle bu geliigzel eyler, ayn dzeyde geliigzel olaylara bal olduunda. Programlama da bizim telefon sistemimizi kullanmaya olduka benzer, yalnz daha ktsdr. Ve Qtun bizi kurtard ey tam olaraktan budur. Qt farkldr. Bir kere, Qt mantkldr. Ve baka, Qt elencelidir. imize younlamamza izin verir. Qtun orijinal mimarlar bir problemle karlatklarnda, sadece iyi bir zm ya da hzl bir zm veyahut basit bir zm aramazlar. Onlar doru zm arar ve sonra bu zm belgelerler. Evet, hatalar da yaptlar, ve evet, baz tasarm kararlar testi geemedi, fakat bir ok doru ey de yaptlar. Bizim iin, Qtda almak bir sorumluluk ve bir ayrcalktr. Ayrca sizin profesyonelliinizin ve ak kaynak yaamnzn daha kolay ve daha elenceli olmasna yardm etmekten de gurur duyuyoruz. Qtu kullanmaktan memnun olmamzn bir sebebi de onun evrimii dokmantasyonudur. Fakat bu dokmantasyonlarn oda ncelikli olarak, karmak gerek dnya yazlmlarnn nasl ina edildiine dair bize ok az ey gsteren, kendi bireysel snflardr. Ve bu mkemmel kitap, bu boluu doldurur. Qtun size sunduklarn, Qt tarz programlamay ve Qtdan daha fazlasn almay gsterir. Kitap iyi rnekler, tavsiyeler ve aklamalar ierir. Gnmzde ok sayda ticari ve cretsiz Qt uygulamas, satn alma veya indirme iin hazr. Qt ile gelitirilmi uygulamalar grmek bize gurur veriyor ve Qtu daha iyi yapmak iin ilham kaynamz oluyor. Ve bu kitabn yardmyla, her zamankinden daha yksek kaliteli Qt uygulamalar gelitirileceinden de eminim. Matthias Ettrich Berlin, Almanya Kasm 2007

Qt 4 ile C++ GUI Programlama

NDEKLER
BLM 1: BALARKEN......................................................................................................................................... 9 Merhaba Qt ..................................................................................................................................................... 9 Balantlar Kurma .......................................................................................................................................... 11 Paracklar Yerletirme ................................................................................................................................. 11 BLM 2: DYALOGLAR OLUTURMA ............................................................................................................... 15 QDialog Altsnf Tretme .............................................................................................................................. 15 Derinlemesine: Sinyaller ve Yuvalar .............................................................................................................. 20 Hzl Diyalog Tasarm..................................................................................................................................... 22 ekil Deitiren Diyaloglar ............................................................................................................................. 28 Dinamik Diyaloglar......................................................................................................................................... 33 Yerleik Parack ve Diyalog Snflar .............................................................................................................. 34 BLM 3: ANA PENCERELER OLUTURMA ....................................................................................................... 39 QMainWindow Altsnf Tretme .................................................................................................................. 39 Menler ve Ara ubuklar Oluturma .......................................................................................................... 43 Durum ubuunu Ayarlama .......................................................................................................................... 47 File Mensn Gerekletirme ..................................................................................................................... 49 Diyaloglar Kullanma ...................................................................................................................................... 54 Uygulama Ayarlarn Saklama ........................................................................................................................ 59 oklu Dokman leme .................................................................................................................................. 60 Al Ekranlar ............................................................................................................................................... 63 BLM 4: UYGULAMA LEVLERN GEREKLETRME ..................................................................................... 65 Merkez Parack............................................................................................................................................. 65 QTableWidget Altsnf Tretme .................................................................................................................... 66 Ykleme ve Kaydetme ................................................................................................................................... 71 Edit Mensn Gerekletirme ..................................................................................................................... 73 Dier Menleri Gerekletirme ..................................................................................................................... 77 QTableWidgetItem Altsnf Tretme ............................................................................................................ 80 BLM 5: ZEL PARACIKLAR OLUTURMA .................................................................................................... 88 Qt Paracklarn zelletirme ....................................................................................................................... 88 QWidget Altsnf Tretme............................................................................................................................. 90 zel Paracklar Qt Designera Entegre Etme............................................................................................... 98 BLM 6: YERLEM YNETM ...................................................................................................................... 102 Paracklar Bir Form zerine Yerletirme ................................................................................................... 102 6

Qt 4 ile C++ GUI Programlama Yl Yerleimler .......................................................................................................................................... 107 Blcler...................................................................................................................................................... 108 Kaydrlabilir Alanlar .................................................................................................................................... 111 Yapkan Pencereler ve Ara ubuklar ....................................................................................................... 112 oklu Dokman Arayz ............................................................................................................................. 115 BLM 7: OLAY LEME .................................................................................................................................. 123 Olay leyicilerini Uyarlama ......................................................................................................................... 123 Olay Filtreleri Kurma.................................................................................................................................... 128 Youn lem Srasnda Duyarl Kalma........................................................................................................... 130 BLM 8: SRKLE-BIRAK .............................................................................................................................. 133 Srkle-Brak Etkinletirme ........................................................................................................................ 133 zel Srkleme Tiplerini Destekleme.......................................................................................................... 137 Pano leme ................................................................................................................................................. 142 BLM 9: E GRNTLEME SINIFLARI ..................................................................................................... 143 e Grntleme Uygunluk Snflarn Kullanma ........................................................................................ 144 nceden Tanmlanm Modelleri Kullanma ................................................................................................ 150 zel Modeller Gerekletirme..................................................................................................................... 155 zel Delegeler Gerekletirme .................................................................................................................... 167 BLM 10: KONTEYNER SINIFLARI.................................................................................................................. 172 Ardk Konteynerler .................................................................................................................................... 172 Birleik Konteynerler ................................................................................................................................... 179 Genel Algoritmalar ...................................................................................................................................... 181 Karakter Katarlar, Byte Dizileri ve Variantlar .............................................................................................. 183 BLM 11: GRD/IKTI ................................................................................................................................... 190 kili Veri Okuma ve Yazma ........................................................................................................................... 190 Metin Okuma ve Yazma............................................................................................................................... 195 Dizinler ......................................................................................................................................................... 200 Gml Kaynaklar ....................................................................................................................................... 201 Sreler Aras letiim .................................................................................................................................. 201 BLM 12: VERTABANLARI ............................................................................................................................ 207 Balanma ve Sorgulama .............................................................................................................................. 207 Tablolar Grntleme ................................................................................................................................ 213 Formlar Kullanarak Kaytlar Dzenleme .................................................................................................... 215 Veriyi Tablo Biiminde Formlar inde Sunma............................................................................................. 218 7

Qt 4 ile C++ GUI Programlama

Qt 4 ile C++ GUI Programlama

BLM 1: BALARKEN

Bu blm temel C++ ve Qtun salad ilevsellii birletirerek birka kk grafik kullanc arayz(GUI) uygulamas oluturmay gsterir. Bu blm ayrca Qtun en nemli iki fikri ile tantrr: sinyal/yuva mekanizmas(signal/slot mechanism) ve yerleimler(layouts). Blm 2ye geldiimizde ise daha derinlere ineceiz ve Blm 3te geree daha uygun bir uygulama gelitirmeye balayacaz.

Merhaba Qt
ok basit bir Qt program ile balayalm. nce onu satr satr inceleyeceiz ve sonra nasl derleyip altracamz reneceiz.
1 #include <QApplication> 2 #include <QLabel> 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 QLabel *label = new QLabel("Hello Qt!"); 7 label->show(); 8 return app.exec(); 9 }

Satr 1 ve 2 QApplication ve QLabel snflarnn tanmlarn programa dhil eder. Her Qt snf iin, o snfn tanmn ieren ve onunla ayn isimde olan bir balk dosyas vardr. Satr 5 uygulama kaynaklarn ynetmek iin bir QApplication nesnesi oluturur. QApplicationun kurucusu argc ve argv argmanlarn ister, nk Qt kendine ait birka komut-satr argmann (command-line argument) destekler. Satr 6 Hello Qt!u grntleyen bir QLabel parac(widget) oluturur. Qt ve Unix terminolojisinde, bir parack kullanc arayznde grsel bir edir. Bu terimin kayna window gadgettir ve bu terim Windows terminolojisindeki control ve container ile ayn anlamdadr. Butonlar(button), menler(menu), kaydrma ubuklar(scroll bar) ve ereveler(frame) paraca birer rnektirler. Paracklar baka paracklar ierebilirler; rnein, bir uygulama penceresi genellikle bir QMenuBar, birka QToolBar, bir QStatusBar, ve baz dier paracklar ieren bir paracktr. ou uygulama, uygulama penceresi olarak bir QMainWindow veya bir QDialog kullanr, fakat Qt olduka esnektir, bylece herhangi bir parack pencere olarak kullanlabilir. rneimizde, QLabel parac uygulama penceremizdir. Satr 7 etiketi(label) grnr yapar. Paracklar her zaman gizlenmi olarak oluturulurlar, bylece onlar gstermeden nce isteimize gre uyarlayabiliriz. Satr 8de uygulamann kontrol Qta geer. Bu noktada, program olay dngsne(event loop) girer. Bu, programn bir kullanc eylemi iin bekledii (fare tkladnda veya bir tua basmak gibi) bir tr 9

Qt 4 ile C++ GUI Programlama bekleme(stand-by) modudur. Kullanc eylemleri, programn cevaplayabildii olaylar(mesajlar diye de geer) retir ve bunlar genellikle bir ya da daha fazla fonksiyonun yrtlmesidir. rnein, kullanc bir parac tkladnda, bir fare butonuna baslma(mouse press) ve bir fare serbest(mouse release) olaylar retilir. Bu adan GUI uygulamalar, genellikle girdi ileyen, sonular reten ve bir insan mdahalesi olmadan sonlandrlan geleneksel toplu i programlarndan(batch programs) byk lde farkldrlar. Sadelik olsun diye, main() fonksiyonunun sonunda QLabel nesnesi iin deletei armadk. Bu bellek sznts(memory leak) bunun gibi kk bir program sz konusu olduunda zararszdr. Program sonlandrldnda QLabel nesnesi iin ayrlm olan bellek iletim sistemi tarafndan geri alnacaktr. Artk program kendi makinenizde deneyebilirsiniz. ekil 1.1deki gibi grnmelidir. Ondan nce, eer makinenizde Qt ykl deilse, Qt 4.3.2 veya daha yeni bir srmn bilgisayarnza yklemelisiniz. (letim sisteminize uygun bir Qt 4 kopyasn http://www.qtsoftware.com/downloads adresinden temin edebilirsiniz.) Bundan sonras iin, Qt 4n bir kopyasn doru olarak makinenize yklediinizi varsayacam. Ayrca program kodlarnn iinde olduu hello.cpp adl dosyann hello adl bir dizinin altnda olmas gerekir.

ekil 1.1

Bir komut isteminden(command prompt) dizini helloya ayarlayn ve platformdan bamsz proje dosyasn(hello.pro) oluturmak iin aadakini yazn:
qmake -project

Proje dosyasndan, platforma zg bir makefile oluturmak iin aadakini yazn:


qmake hello.pro

imdi, program derlemek iin make, ardndan da program altrmak iin hello yazn. Bir sonraki rneimize gemeden nce biraz elenelim: u satr
QLabel *label = new QLabel("Merhaba Qt!");

u kod paras ile deitirelim


QLabel *label = new QLabel("<h2><i>Hello </i> " "<font color=red>Qt!</font></h2>");

ve yine ayn admlar izleyerek program derleyip altralm. Uygulamamz altnda ekil 1.2deki gibi grnecektir. Bu rnein de gsterdii gibi, bir Qt uygulamasnn kullanc arayzne daha ho bir hava katmak, basit HTML etiketleri(tag) kullanarak bile mmkn.

ekil 1.2

10

Qt 4 ile C++ GUI Programlama

Balantlar Kurma
kinci rnek kullanc eylemlerine nasl yant verildiini gsterir. Uygulama kullancnn tklayp programdan kabilecei bir butondan oluuyor. Uygulamann kaynak kodu, bir nceki rneinki ile -ana paracmz QLabel yerine QPushButton kullanmamz ve bunu bir kullanc eylemine(butona tklama) balamamz dnda- ok bezerdir. Uygulamann alan hali ekil 1.3de gsteriliyor. te kaynak kodu:
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()), 8 &app, SLOT(quit())); 9 button->show(); 10 return app.exec(); 11 }

ekil 1.3

Qt paracklar bir kullanc eylemini ya da ortaya kan bir durum deiikliini iaret etmek iin sinyaller(signal) yayar.[*] rnein, QPushButton kullanc butona tkladnda bir clicked() sinyali yayar. Bir sinyal bir yuvaya balanm olabilir, bylece bir sinyal yayldnda, yuva otomatik olarak yrtlr. Bizim rneimizde, butonun clicked() sinyalini QApplication nesnesinin quit() yuvasna baladk. Buradaki SIGNAL() ve SLOT() makrolar szdiziminin birer paralardr.
[*]

Qt sinyalleri Unix sinyalleriyle balantszdr. Bu kitapta, biz yalnzca Qt sinyalleriyle ilgileneceiz.

Bir nceki rnekte izlediimiz admlar izleyerek kodu derleyip altrdnzda, eer Quit butonuna tklarsanz uygulamann sona erdirileceini greceksiniz.

Paracklar Yerletirme
Blm 1in bu ksmnda bir penceredeki paracklarn geometrisini, yerleimleri(layouts) kullanarak nasl yneteceimizi ve sinyalleri ve yuvalar kullanarak iki parac nasl senkronize edebileceimizi gsteren kk bir uygulama oluturacaz. Uygulama kullancnn yan sorar. Kullanc, yan bir dndrme kutusu(spin box) ya da bir kaydrc(slider) yardmyla girebilir.

ekil 1.4

Uygulama 3 paracktan meydana gelir: bir QSpinBox, bir QSlider, ve bir QWidget. QWidget uygulamann ana penceresidir. QSpinBox ve QSlider QWidget iinde yorumlanr; onlar QWidgetn ocuklardrlar(children). Bir baka deyile, QWidget QSpinBox ve QSlidern ebeveynidir(parent) 11

Qt 4 ile C++ GUI Programlama diyebiliriz. QWidget ise ebeveyne sahip deildir nk zaten kendisi bir st-seviye pencere olarak kullanlmaktadr. QWidget ve onun tm altsnflar iin kurucular ebeveyn parac(parent widget) belirten bir QWidget * parametresi alr. te kaynak kodu: Kod Grnm:
1 2 3 4 #include #include #include #include <QApplication> <QHBoxLayout> <QSlider> <QSpinBox>

5 int main(int argc, char *argv[]) 6 { 7 QApplication app(argc, argv); 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 } QWidget *window = new QWidget; window->setWindowTitle("Enter Your Age"); QSpinBox *spinBox = new QSpinBox; QSlider *slider = new QSlider(Qt::Horizontal); spinBox->setRange(0, 130); slider->setRange(0, 130); QObject::connect(spinBox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int))); QObject::connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int))); spinBox->setValue(35); QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(spinBox); layout->addWidget(slider); window->setLayout(layout); window->show(); return app.exec();

Satr 8 ve 9 QWidget uygulamann ana penceresi olarak hizmet verecek ekilde ayarlar. Pencerenin balk ubuunda grntlenen metni ayarlamak iin setWindowTitle() fonksiyonunu arrz. Satr 10 ve 11 bir QSpinBox ve bir QSlider oluturur, ve satr 12 ve 13 onlarn geerli deiim aralklarn ayarlar. Bir kullancnn en fazla 130 yanda olabileceini rahatlkla varsayabiliriz. Burada windowu QSpinBox ve QSlidern kurucularna, bu paracn(window) onlarn(QSpinBox ve QSlider) ebeveyni olduunu belirtmek iin aktarabilirdik, fakat burada gereksizdir nk yerleim sistemi(layout system) bunu bizzat kendisi dnr ve dndrme kutusunun ve kaydrcnn ebeveynini otomatik olarak ayarlar. Satr 14 ve 16da grnen iki QObject::connect() ars dndrme kutusunu ve kaydrcy senkronize ederek her zaman ayn deeri gstermelerini salar. Bir paracn deeri deitiinde, onun valueChanged(int) sinyali yaylr ve dier paracn setValue(int) yuvas yeni deer ile arlr.

12

Qt 4 ile C++ GUI Programlama Satr 18 dndrme kutusunun deerini 35e ayarlar. Bu olduunda, QSpinBox 35 deerinde bir tamsay argman ile valueChanged(int) sinyalini yayar. Bu argman, kaydrcnn deerini 35e ayarlayan QSlidern setValue(int) yuvasna aktarlr. Sonra kaydrc valueChanged(int) sinyalini yayar nk kendi deeri deimitir, bu da dndrme kutusunun setValue(int) yuvasn tetikler. Fakat bu noktada, setValue(int) hibir sinyal yaymaz nk zaten deeri 35tir. Bu, sonsuz zyinelemeyi nler. ekil 1.5 bu durumu zetliyor.

ekil 1.5

Satr 19, 20, 21 ve 22de, dndrme kutusu ve kaydrc paracklarn yerleim yneticisini(layout manager) kullanarak yerletiririz. Bir yerleim yneticisi paracklarn boyutunu ve konumunu ayarlayan bir nesnedir. Qt ana yerleim yneticisi snfna sahiptir: QHBoxLayout, paracklar dikey(horizontal) olarak soldan saa doru yerletirir(baz kltrlerde sadan sola). QVBoxLayout, paracklar dey(vertical) olarak en stten aa doru yerletirir. QGridLayout, paracklar bir zgaraya(grid) yerletirir. Satr 22deki QWidget::setLayout() ars yerleim yneticisini pencereye ykler. Perdenin arkasnda, QSpinBox ve QSlider yerleimin yklendii paracn ocuklar olarak ayarlanrlar ve bu sebeple yerleimin iine konacak bir parac olutururken aka bir ebeveyn belirtmemiz gerekmez.

ekil 1.6

Hibir paracn boyutunu ya da konumunu aka ayarlamadmz halde, QSpinBox ve QSlider yan yana ho bir ekilde yerlemi grnyor. Bunun nedeni, QHBoxLayoutun paracklara makul lleri amayacak ekilde, otomatik olarak boyut ve konum tahsis etmesidir. Yerleim yneticileri bizi uygulamalarmzn ekran konumlarn kodlama angaryasndan kurtarr ve pencerenin dzgnce yeniden boyutlandrlmasn salar.

13

Qt 4 ile C++ GUI Programlama Qtun kullanc arayzleri yaratma yaklam anlamas basit ve ok esnektir. Qt programclarnn en ok kullandklar ortak modele gre; o an ihtiya duyulan paracklar oluturulur ve sonra eer gerekirse zellikleri yine ihtiyalar dorultusunda ayarlanr. Daha sonra paracklar programclar tarafndan, boyutlarnn ve konumlarnn otomatik olarak ayarlanaca yerleimlere eklenirler. Kullanc arayz davran ise Qtun sinyal/yuva mekanizmas kullanlarak balanan paracklar tarafndan ynetilir.

14

Qt 4 ile C++ GUI Programlama

BLM 2: DYALOGLAR OLUTURMA

Bu blm, Qtu kullanarak iletiim kutularnn(dialog boxes) nasl oluturulduunu retecek. letiim kutular kullanclara seenekler sunar, kullanclara istedikleri deerleri verme ve kendi seimlerini yapma izni verir. letiim kutular veya daha yaygn kullanlan ismiyle diyaloglar, bir anlamda kullanclar ile uygulamalarn birbirleriyle konumalarn salar. Pek ok GUI uygulamas ana pencereyle birlikte bir men ubuu(menu bar), bir ara ubuu(toolbar) ve dzinelerce diyalogdan meydana gelir. Kullancnn seimlerine direkt cevap veren diyalog uygulamalar oluturmak da mmkndr(hesap makinesi uygulamas gibi). lk diyaloumuzu, nasl yapldn gstermek iin, yalnzca kod yazarak oluturacaz. Daha sonra, diyaloglarn Qtun grsel tasarm arac Qt Designer ile nasl oluturulduunu greceiz. Qt Designer kullanmak elle kod yazmaktan ok daha hzldr ve farkl tasarmlar test etmeyi ve daha sonra tasarmlar deitirmeyi kolaylatrr.

QDialog Altsnf Tretme


lk rneimiz Find, tamamen C++ ile yazlm bir diyalogdur. ekil 2.1de grebilirsiniz. Diyalou, uygun bir snf oluturarak gerekletireceiz. Byle yaparak, onu kendi sinyal ve yuvalaryla bamsz bir bileen yapacaz.

ekil 2.1

Kaynak kodu iki dosyaya yaylr: finddialog.h ve finddialog.cpp. finddialog.h ile balayacaz.
1 #ifndef FINDDIALOG_H 2 #define FINDDIALOG_H 3 #include <QDialog> 4 5 6 7 class class class class QCheckBox; QLabel; QLineEdit; QPushButton;

Satr 1 ve 2 (ve 27) balk dosyasn birden fazla dhil etmeye kar korur. Satr 3, Qtda diyaloglar iin temel snf olan QDialogun tanmn dhil eder. Qdialog, QWidgettan tretilmitir. 15

Qt 4 ile C++ GUI Programlama Satr 4, 5, 6 ve 7, diyalou gerekletirirken kullanacamz Qt snflarnn n bildirimlerini yapar. Bir n bildirim, C++ derleyicisini bir snfn varlndan -snf tanmnn tm detaylarn vermeden(snf tanmlar genellikle kendi balk dosyasnda yer alr)- haberdar eder. Sonra, QDialogun bir altsnf olarak FindDialogu tanmlarz:
8 class FindDialog : public QDialog 9 { 10 Q_OBJECT 11 public: 12 FindDialog(QWidget *parent = 0);

Snf tanmnn balangcndaki Q_OBJECT makrosu, sinyaller veya yuvalar tanmlanan tm snflarda gereklidir. FindDialogun kurucusu Qt parack snflarnn tipik bir rneidir. parent parametresi ebeveyn parac belirtir. Varsaylan deeri, bu diyaloun ebeveyne sahip olmad anlamna gelen bo(null) bir iaretidir.
13 signals: 14 void findNext(const QString &str, Qt::CaseSensitivity cs); 15 void findPrevious(const QString &str, Qt::CaseSensitivity cs);

signals ksmnda, kullanc Find butonuna tkladnda diyaloun yayaca 2 sinyalin bildirimi yaplr. Eer Search backward seenei seilmise, diyalog findPrevious() sinyalini, aksi halde findNext() sinyalini yayar. signals aslnda bir makrodur. C++ nilemcisi, derleyici onu grmeden nce, onu standart C++a dntrr. Qt::CaseSensitivity ise, Qt::Sensitive ve Qt::Insensitive deerlerini alabilen bir enum tipidir.
16 private slots: 17 void findClicked(); 18 void enableFindButton(const QString &text); 19 private: 20 QLabel *label; 21 QLineEdit *lineEdit; 22 QCheckBox *caseCheckBox; 23 QCheckBox *backwardCheckBox; 24 QPushButton *findButton; 25 QPushButton *closeButton; 26 }; 27 #endif

Snfn private(zel) ksmnda 2 yuva bildirimi yaparz. Yuvalar gerekletirmek iin, diyaloun birok ocuk paracna(child widget) erimemiz gerekecek, bu nedenle de onlar iaretiler olarak tutarz. slots anahtar kelimesi de signals gibi bir makrodur. private deikenler iin, snflarnn n bildirimlerini kullandk. Bu mmknd nk hepsi iareti ve onlara balk dosyas iinde erimeyeceiz, bu nedenle derleyicinin snf tanmlarnn tamamna ihtiyac yoktur.

16

Qt 4 ile C++ GUI Programlama Uygun balk dosyalarn(<QCheckBox>, <QLabel>, vs.) dhil edebilirdik, fakat n bildirimleri kullanmak bir miktar daha hzl derlenme salar. imdi FindDialog snfnn gerekletirimini ieren finddialog.cppye bakacaz.
1 #include <QtGui> 2 #include "finddialog.h"

lk olarak, Qtun GUI snflarnn tanmn ieren balk dosyasn, yani <QtGui>yi dhil ederiz. Qt, her biri kendi ktphanesinde bulunan deiik modllerden meydana gelir. En nemli modller QtCore, QtGui, QtNetwork, QtOpenGL, QtScript, QtSql, QtSv ve QtXmldir. <QtGui> balk dosyas, QtCore ve QtGui modllerinin paras olan tm snflarn tanmndan meydana gelir. Bu balk dosyasn dhil etmek bizi her snf tek tek dhil etme zahmetinden kurtarr. finddialog.h iine <QDialog>u dhil etme ve QCheckBox, QLabel, QLineEdit ve QPushButtonn n bildirimlerini kullanmak yerine, basite <QtGui>yi dhil edebilirdik. Ancak genellikle, bylesine byk bir balk dosyasn dhil etmek kt bir tarzdr, zelliklede daha byk uygulamalarda.
3 FindDialog::FindDialog(QWidget *parent) 4 : QDialog(parent) 5 { 6 label = new QLabel(tr("Find &what:")); 7 lineEdit = new QLineEdit; 8 label->setBuddy(lineEdit); 9 10 11 12 13 14 caseCheckBox = new QCheckBox(tr("Match &case")); backwardCheckBox = new QCheckBox(tr("Search &backward")); findButton = new QPushButton(tr("&Find")); findButton->setDefault(true); findButton->setEnabled(false); closeButton = new QPushButton(tr("Close"));

Satr 4de temel snfn kurucusuna parent parametresini aktarrz. Sonra, ocuk paracklar olutururuz. Karakter katar deyimleriyle yaplan tr() fonksiyonu arlar, karakter katar deyimlerini dier dillere evirmek iin iaretler. Bu fonksiyon, QObject iinde ve Q_OBJECT makrosunu ieren her altsnfta bildirilmitir. Uygulamalarnz dier dillere evirmek iin acil bir plannz olmasa bile, kullancya grnen karakter katar deyimlerini tr() ile evrelemek iyi bir alkanlktr. Karakter katar deyimlerinde, ksayol tular belirtmek iin & kullanrz. rnein, Satr 11 kullancnn ksayollar destekleyen platformlarda- Alt+F tu kombinasyonunu kullanarak aktif hale getirebilecei bir Find butonu oluturur. &, oda kontrol etmek iin de kullanlabilir: Satr 6da ksayol tuuyla(Alt+W) birlikte bir etiket olutururuz ve Satr 8de de etiketin arkadan(labels buddy) satr editr(line editor) olarak ayarlarz. Arkada(buddy), etiketin ksayol tuuna basldnda oda stlenecek olan paracktr. Bylece, kullanc Alt+W tu kombinasyonuna bastnda, odak satr editrne (etiketin arkada) kayar. Satr 12de setDefault(true)yu ararak Find butonunu diyaloun varsaylan butonu(default button) yaparz. Varsaylan buton, kullanc Entera bastnda etkilenecek butondur. Satr 13de Find butonunu devred brakrz. Bir parack devred brakldnda, genelde gri gsterilir ve kullanc etkileimine yant vermez. 17

Qt 4 ile C++ GUI Programlama


15 16 17 18 19 20 connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableFindButton(const QString &))); connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked())); connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));

private yuva enableFindButton(const QString &), satr editrndeki metin deitiinde, dier private yuva findClicked(), kullanc Find butonuna tkladnda arlr. Kullanc Closea tkladnda da diyalog kendini kapatr. close() yuvas QWidgetten miras alnr ve varsaylan davran parac -onu silmeden- gizlemektir. enableFindButton() ve findClicked() yuvalarnn kodlarna daha sonra bakacaz. QObject, FindDialogun atalarndan biri olduu iin, connect() arlarnn nne QObject:: nekini koymay ihmal edebiliriz.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 QHBoxLayout *topLeftLayout = new QHBoxLayout; topLeftLayout->addWidget(label); topLeftLayout->addWidget(lineEdit); QVBoxLayout *leftLayout = new QVBoxLayout; leftLayout->addLayout(topLeftLayout); leftLayout->addWidget(caseCheckBox); leftLayout->addWidget(backwardCheckBox); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addWidget(findButton); rightLayout->addWidget(closeButton); rightLayout->addStretch(); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout);

Sonra, yerleim yneticilerini(layout managers) kullanarak ocuk paracklar yerletiririz. Yerleimler (Layouts), paracklar ve dier yerleimleri ierebilirler. QHBoxLayoutlarn, QVBoxLayoutlarn ve QGridLayoutlarn eitli kombinasyonlarn i ie koyarak ok karmak diyaloglar oluturmak mmkndr. Find diyalou iin, ekil 2.2de de grld gibi iki QHBoxLayout ve iki QVBoxLayout kullanrz. Dtaki yerleim, satr 35te FindDialoga yklenen ve diyaloun btn alanndan sorumlu olan ana yerleimdir(mainLayout). Dier yerleim alt-yerleimlerdir(leftLayout, rightLayout, topLeftLayout). ekil 2.2nin sa altndaki kk yay bir ara paradr(spacer/stretch item). Find ve Close butonlarnn altndaki anlamsz boluu doldurur ve butonlarn, yerleimlerinin stne oturmasn salar.

18

Qt 4 ile C++ GUI Programlama

ekil 2.2

Yerleim yneticisi snflar parack deildir. QObjectten tretilmi olan QLayouttan tretilmilerdir. alan bir uygulamada, yerleimler grnmezdir. Alt-yerleimler ebeveyn yerleime eklendiklerinde(satr 25, 33 ve 34) otomatik olarak ebeveyne sahip olmu olurlar. Sonra, ana-yerleim diyaloa yklendiinde(satr 35), ana-yerleim de diyaloun bir ocuu olur ve bylece yerleimlerdeki tm paracklar ve yerleimler diyaloun ocuklar olmu olurlar. Bu ebeveyn-ocuk hiyerarisi ekil 2.3te gsterilmitir.
36 37 38 } setWindowTitle(tr("Find")); setFixedHeight(sizeHint().height());

ekil 2.3

Son olarak, diyaloun balk ubuunda grntlenecek olan pencere baln ve deimez(fixed) bir pencere ykseklii ayarlarz. QWidget::sizeHint() fonksiyonu ise bir paracn ideal boyutunu dndrr. Bylelikle FindDialogun kurucusunun kritiini bitirmi olduk. Diyaloun paracklarn ve yerleimlerini new kullanarak oluturduumuz iin, oluturduumuz her parack ve yerleim iin, delete diye adlandrlan bir yokedici(destructor) yazmamz gerekiyormu gibi grlebilir. Fakat bu gereksizdir, nk ebeveyn yok edildiinde, ocuk nesneler de Qt tarafndan otomatik olarak silinirler. imdi diyaloun yuvalarna bakacaz:
39 void FindDialog::findClicked() 40 { 41 QString text = lineEdit->text(); 42 Qt::CaseSensitivity cs = 43 caseCheckBox->isChecked() ? Qt::CaseSensitive 44 : Qt::CaseInsensitive; 45 if (backwardCheckBox->isChecked()) { 46 emit findPrevious(text, cs);

19

Qt 4 ile C++ GUI Programlama


47 48 49 50 } } else { emit findNext(text, cs); }

51 void FindDialog::enableFindButton(const QString &text) 52 { 53 findButton->setEnabled(!text.isEmpty()); 54 }

findClicked() yuvas, kullanc Find butonuna tkladnda arlr. Search backward seeneine bal olarak findPrevious() veya findNext() sinyalini yayar. emit anahtar kelimesi Qta zgdr; dier Qt geniletmeleri(extensions) gibi C++ nilemcisi tarafndan standart C++a evrilir. enableFindButton() yuvas, satr editrndeki metin deitiinde arlr. Eer satr editrnde metin varsa Find butonunu etkinletirir, aksi halde devred brakr. Bu iki yuvayla diyalou tamamlam olduk. Artk FindDialog paracn test etmek iin main.cpp dosyasn oluturabiliriz:
1 #include <QApplication> 2 #include "finddialog.h" 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 FindDialog *dialog = new FindDialog; 7 dialog->show(); 8 return app.exec(); 9 }

Program derlemek iin her zamanki gibi qmakei altrn. Sonra program altrn. Eer platformunuzda ksayol tular grnyorsa, ksayollar kullanmay deneyin. Paracklar arasnda gezinmek iin Tab tuuna basn. Varsaylan tab sras(tab order) paracklar oluturulduunda otomatik olarak dzenlenir. QWidget::setTabOrder()i kullanarak tab srasn deitirebilirsiniz.

Derinlemesine: Sinyaller ve Yuvalar


Sinyal/yuva mekanizmas Qt programlamann temelidir. Uygulama programclarna, birbiri hakknda hibir ey bilmeyen nesneleri birbirine balama olana verir. Biz imdiden baz sinyal ve yuvalar birbirine baladk, kendi sinyal ve yuvalarmzn bildirimini yaptk, kendi yuvalarmz gerekletirdik ve kendi sinyallerimizi yaydk. imdide bu mekanizmaya daha yakndan bakalm. Yuvalar, tipik C++ ye fonksiyonlaryla hemen hemen zdetirler. Sanal olabilirler, ar yklenebilirler, public, protected ya da private olabilirler, dier C++ ye fonksiyonlar gibi direkt olarak arlabilirler ve parametreleri her tipten olabilir. Farkl olarak bir yuva bir sinyale balanabilir. connect() ifadesi u ekildedir:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));

20

Qt 4 ile C++ GUI Programlama Burada, sender ve receiver QObject iaretileri, signal ve slot parametre ismi olmakszn fonksiyon adlardr. SIGNAL() ve SLOT() makrolar ise esasen argmanlarn bir karakter katarna dntrrler. imdiye kadar grdmz rneklerde hep farkl sinyalleri farkl yuvalara baladk. Baka olaslklar da vardr: Bir sinyal birden ok yuvaya balanm olabilir:
connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int))); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateStatusBarIndicator(int)));

Sinyal yayldnda, -zellikle belirtilmi bir sra yokken- yuvalar birbiri ardna arlrlar. Birden ok sinyal ayn yuvaya balanm olabilir:
connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError())); connect(calculator, SIGNAL(divisionByZero()), this, SLOT(handleMathError()));

Sinyallerden herhangi biri yayldnda yuva arlr. Bir sinyal baka bir sinyale balanm olabilir:
connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(updateRecord(const QString &)));

lk sinyal yayldnda ikinci sinyal de yaylr. Dier taraftan, sinyal-sinyal balantlar sinyal-yuva balantlarndan farkszdrlar. Balantlar ortadan kaldrlabilir:
disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));

Buna ok nadir ihtiya duyulur. nk Qt, bir nesne silindiinde, onunla ilikili tm balantlar otomatik olarak ortadan kaldrr. Bir sinyali bir yuvaya(ya da baka bir sinyale) baarl bir ekilde balamak istiyorsak, ikisi de ayn parametre tiplerine ayn srada sahip olmaldrlar.
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(processReply(int, const QString &)));

stisna olarak, bir sinyal balanaca yuvadan daha fazla parametreye sahipse, fazladan parametreler nemsenmezler.
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(checkErrorCode(int)));

Eer parametre tipleri uyumuyorsa, ya da sinyal veya yuva mevcut deilse, ve eer uygulama hata ayklama(debug) modunda ina edilmise, Qt alma srasnda uyar verecektir. Benzer ekilde, eer parametre isimleri yazlmsa, Qt yine uyar verecektir. 21

Qt 4 ile C++ GUI Programlama imdiye kadar, sinyal ve yuvalar paracklarla kullandk. Fakat mekanizmann kendisi QObject iinde gerekletirilmitir ve GUI programlamayla snrl deildir. Mekanizma her QObject altsnf tarafndan kullanlabilir:
class Employee : public QObject { Q_OBJECT public: Employee() { mySalary = 0; } int salary() const { return mySalary; } public slots: void setSalary(int newSalary); signals: void salaryChanged(int newSalary); private: int mySalary; }; void Employee::setSalary(int newSalary) { if (newSalary != mySalary) { mySalary = newSalary; emit salaryChanged(mySalary); } }

setSalary() yuvasnn nasl gerekletirildiine dikkat edin. salaryChanged() sinyalini sadece newSalary != mySalary olduunda yaydk. Bu, evrimsel balantlarn sonsuz dngler halini almamasn salar.

Hzl Diyalog Tasarm


Qt elle kod yazmay keyifli ve sezgisel yapacak ekilde tasarlanmtr ve bunu, yalnzca C++ kodlar yazarak Qt uygulamalar gelitiren programclar iyi bilirler. Hl, pek ok programc formlar tasarlarken grsel bir yaklam kullanmay tercih ederler, nk bunu elle kod yazmaktan daha doal ve hzl bulurlar. Denemeler yapabilmek ve tasarmlarn, elle kod yazarak oluturulan formlara gre daha hzl ve kolay deitirebilmek isterler. Qt Designer, programclara grsel tasarm yetenei sunarak onlarn seeneklerini geniletir. Qt Designer, bir uygulamann tm formalarnn ya da sadece birka formunun gelitirilmesinde kullanlabilir. Qt Designer ile oluturulan formlar en nihayetinde yine C++ kodlardr, bu nedenlede Qt Designer geleneksel ara zinciri ile kullanlabilir ve derleyiciye hibir zel gereksinim yklemez. Bu ksmda, Qt Designer ekil 2.4de grlen Go to Cell diyalounu oluturmakta kullanacaz. ster kodla yapalm ister Qt Designerla, bir diyalog oluturmak her zaman ayn temel admlar atmamz gerektirir: 1. 2. 3. 4. ocuk paracklar olutur ve ilk kullanma hazrla ocuk paracklar yerleimler iine koy Tab srasn ayarla Sinyal-yuva balantlar kur 22

Qt 4 ile C++ GUI Programlama 5. Diyaloun zel yuvalarn gerekletir

ekil 2.4

Qt Designer altrn. Qt Designer aldnda karnza ablonlarn(templates) listesi gelecektir. Widgeta, sonrada Createe tklayn. ("Dialog with Buttons Bottom" ablonu cazip gelebilir, fakat bu rnekte OK ve Cancel butonlarn -nasl yapldn grmek iin- elle kodlayarak oluturacaz) Artk Untitled isimli bir pencereye sahip olmu olmalsnz. lk adm, ocuk paracklar oluturmak ve onlar formun zerine yerletirmek. Bir etiket(label), bir satr editr(line editor), bir yatay ara para(horizontal spacer) ve iki butonu(push button) oluturun. Her bir para iin, Qt Designern parack kutusundaki(Widget Box) ismine ya da ikonuna tklayarak formun stne srkleyip, formda olmasn istediimiz yere brakalm. ekil 2.5deki gibi bir form elde etmeye aln. Fakat paralarn formdaki konumlarn ayarlamak iin okta fazla zaman harcamayn; Qtun yerleim yneticileri onlar daha sonra kusursuz olarak yerletirecektirler.

ekil 2.5

Her bir paracn niteliklerini(properties) Qt Designern nitelik editrn(property editor) kullanarak ayarlayn: 1. TextLabela tklayn. objectName niteliinin label olduundan emin olun ve text niteliini &Cell Location olarak ayarlayn. 2. Satr editrne tklayn. objectName niteliinin lineEdit olduundan emin olun. 3. Birinci butona tklayn. objectName niteliini okButton, enabled niteliini false, text niteliini OK ve default niteliini de true olarak ayarlayn. 4. kinci butona tklayn. objectName niteliini cancelButton, text niteliini Cancel olarak ayarlayn. 5. Formun kendisini semek iin formun arka planna tklayn. objectName niteliini GoToCellDialog, windowTitle niteliini Go to Cell olarak ayarlayn. imdi &Cell Location eklinde grnen metin etiketi(text label) dndaki tm paracklar gzel grnyor. Arkadalar(buddies) ayarlamaya izin veren zel bir moda girmek iin Edit > Edit Buddies e tklayn. Sonra, etiketi tklayn ve krmz ok iaretini satr editrne srkleyip brakn. Etiket artk ekil 2.6daki gibi, Cell Location olarak grnmeli. Arkada modundan kmak iin Edit > Edit Widgets tklayn.

23

Qt 4 ile C++ GUI Programlama

ekil 2.6

Sradaki adm paracklar form zerine yerletirmek: 1. Cell Location etiketine tklayn ve Ctrlye basl tutarak satr editrne de tklayn, ikisini de semi olacaksnz. Sonra, Form > Lay Out Horizontallyye tklayn. 2. Ara paraya tklayn ve Ctrlye basl tutarak OK ve Cancel butonlarna da tklayn. Sonra yine Form > Lay Out Horizontallyye tklayn. 3. Formun arka plann tklayarak seili olan paralar seimden kartn, sonrada Form > Lay Out Verticallyye tklayn. 4. Formu yeniden boyutlandrmak iin Form > Adjust Sizea tklayn. ekil 2.7de grld gibi, yerleimler oluturulan formlar zerinde krmz izgiler grnr. Form alrken bu izgiler grnmezler.

ekil 2.7

imdi Edit > Edit Tab Ordera tklayn. Burada, ekil 2.8de de grebileceiniz mavi dikdrtgenlere tklayarak, tab srasn dzenleyebilirsiniz. Tab srasn dzenledikten sonra, tab srasn dzenleme modundan kmak iin Edit > Edit Widgetsa tklayn.

ekil 2.8

Diyalou nizlemek iin, Form > Prreview men seeneini tklayn. Tekrar tekrar Tab tuuna basarak tab srasn kontrol edin. Diyalou balk ubuundaki kapatma butonunu kullanarak kapatn. Diyalou gotocell adl bir klasre gotocelldialog.ui olarak kaydedin ve ayn klasr iinde bir metin editr kullanarak main.cppyi oluturun:
#include <QApplication> #include <QDialog> #include "ui_gotocelldialog.h" int main(int argc, char *argv[]) {

24

Qt 4 ile C++ GUI Programlama


QApplication app(argc, argv); Ui::GoToCellDialog ui; QDialog *dialog = new QDialog; ui.setupUi(dialog); dialog->show(); return app.exec(); }

imdi, .pro dosyas ve makefile oluturmak iin qmakei altrn(qmake -project; qmake gotocell.pro). qmake arac, kullanc arayz dosyas gotocelldialog.ui fark edecek ve Qtun kullanc arayz derleyicisi(user interface compiler) uic aracak derecede zekidir. uic arac gotocelldialog.ui C++a evirir ve sonucu ui_gotocelldialog.h iine yerletirir. Oluturulan ui_gotocelldialog.h dosyas Ui::GoToCellDialog snfnn tanmn ierir. Snf, formun ocuk paracklar ve yerleimleri iin tutulan ye deikenlerin ve formu balatan setupUi() fonksiyonunun bildirimlerini yapar. Oluturulan snf una benzer:
class Ui::GoToCellDialog { public: QLabel *label; QLineEdit *lineEdit; QSpacerItem *spacerItem; QPushButton *okButton; QPushButton *cancelButton; ... void setupUi(QWidget *widget) { ... } };

Oluturulan snf, herhangi bir ana snfa sahip deildir. Formu main.cpp iinde kullanrken bir QDialog oluturur ve setupUi()a aktarrz. Eer program altrrsanz, diyalog alacak, fakat kesinlikle istediimiz gibi almayacaktr: OK butonu her zaman devreddr. Cancel butonu hibir ey yapmaz. Satr editr, yalnzca doru hcre konumlarn(cell locations) kabul edeceine, her metni kabul eder. Diyaloun doru drst almasn birka kod yazarak salayabiliriz. En doru yaklam, QDialog ve Ui::GoToCellDialog snflarndan tretilen yeni bir snf oluturmak ve eksik fonksiyonellii gerekletirmek olacaktr. Adlandrma kurallarmza(naming convention) gre bu yeni snf uicin oluturduu snf ile ayn isimde olmaldr, fakat Ui:: neki olmakszn. Bir metin editr kullanarak, gotocelldialog.h adnda ve aadaki kodlar ieren bir dosya oluturun:
#ifndef GOTOCELLDIALOG_H #define GOTOCELLDIALOG_H #include <QDialog>

25

Qt 4 ile C++ GUI Programlama


#include "ui_gotocelldialog.h" class GoToCellDialog : public QDialog, public Ui::GoToCellDialog { Q_OBJECT public: GoToCellDialog(QWidget *parent = 0); private slots: void on_lineEdit_textChanged(); }; #endif

Burada public kaltm kullandk, nk diyaloun paracklarna diyalog dndan da erimek istiyoruz. Snfn gerekletirimi gotocelldialog.cpp dosyas iindedir:
#include <QtGui> #include "gotocelldialog.h" GoToCellDialog::GoToCellDialog(QWidget *parent) : QDialog(parent) { setupUi(this); QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); lineEdit->setValidator(new QRegExpValidator(regExp, this)); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); } void GoToCellDialog::on_lineEdit_textChanged() { okButton->setEnabled(lineEdit->hasAcceptableInput()); }

Kurucuda, formu balatmak iin setupUi() ardk. Burada oklu kaltma teekkr etmemiz gerekir, nk onun sayesinde Ui::GoToCellDialogun ye fonksiyonlarna direkt olarak eriebiliyoruz. Kullanc arayzn oluturduktan sonra, setupUi(), on_objectName_signalName() adlandrma kuralna uyan her yuvay, uygun objectNamein signalName() sinyaline otomatik olarak balayacak. Bizim rneimizde bu, setupUi()n u balanty otomatik olarak kuraca anlamna gelir:
connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(on_lineEdit_textChanged()));

Ayrca kurucuda, girdinin kapsamna snrlama getiren bir geerlilik denetleyicisi(validator) ayarladk. Qt bize yerleik olarak adet geerlilik denetleyicisi snf salar: QIntValidator, QDoubleValidator ve QRegExpValidator. Burada, bir QRegExpValidator "[A-Za-z][1-9][0-9]{0,2}" dzenli ifadesiyle kullandk ve bu u anlama gelmektedir: bir byk veya bir kk harf, onu takip eden 1le 9 arasnda bir rakam ve onu da takip eden 0la 9 arasnda 0, 1 veya 2 rakama izin ver.

26

Qt 4 ile C++ GUI Programlama this i QRegExpValidatorn kurucusuna aktararak, QRegExpValidator GoToCellDialog nesnesinin ocuu yaptk. Byle yaparak, QRegExpValidatorn silinmesi hakkndaki endielerimizi ortadan kaldrdk; yle ki ebeveyni silindiinde o da otomatik olarak silinmi olacak. Qtun ebeveyn-ocuk mekanizmas QObject iinde gerekletirilir. Ebeveyni olan bir nesne oluturduumuzda (bir parack, geerlilik denetleyicisi ya da herhangi baka trde bir ey), ebeveyn nesneyi ocuklarnn listesine ekler. Ebeveyn silindiinde, listedeki her bir ocuk da silinir. ocuklar da kendi ocuklarn siler ve bu hi ocuk kalmayncaya dek devam eder. Ebeveyn-ocuk mekanizmas bellek ynetimini(memory management) ok kolaylatrr ve bellek szntlar riskini azaltr. deletei armamz gereken nesneler yalnzca new kullanarak oluturduumuz ebeveyni olmayan nesnelerdir. Ve ayrca eer bir ocuk nesneyi ebeveyninden nce silersek, Qt o nesneyi otomatik olarak ebeveyninin ocuklarnn listesinden de silecektir. Paracklar iin ebeveynlerin ek bir anlam daha vardr: ocuk paracklar ebeveynin alan iinde grnrler. Ebeveyn parac sildiimizde, ocuk parack yalnzca bellekten silinmez, ayn zamanda ekrandan da silinir. Kurucunun sonunda, OK butonunu QDialogun accept() yuvasna, Cancel butonunu da reject() yuvasna balarz. Her iki yuva da diyalou kapatr, fakat accept(), diyaloun sonu deerini 1e eit olan QDialog::Acceptede, reject() ise diyaloun sonu deerini 0a eit olan QDialog::Rejectede ayarlar. Diyalou kullanrken, bu sonucu kullancnn OKe tkladn grmek ve ona gre davranmak iin kullanabiliriz. on_lineEdit_textChanged() yuvas OK butonunu satr editrnn geerli bir hcre konumu iermesine gre etkinletirir ya da devred brakr. OLineEdit::hasAcceptableInput() kurucuda ayarladmz geerlilik denetleyicisini kullanr. Bylece diyalou tamamlam olduk. imdi main.cppyi diyalou kullanmak iin yeniden yazabiliriz:
#include <QApplication> #include "gotocelldialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); GoToCellDialog *dialog = new GoToCellDialog; dialog->show(); return app.exec(); }

gotocell.proyu yeniden oluturmak iin qmake -projecti kullann, makefile gncellemek iin qmake gotocell.proyu altrn, makei altrarak uygulamay yeniden derleyin ve altrn. Qt Designer kullanmann bir gzellii de programclara kaynak kodlarn deitirme zorluu olmadan formlarn deitirme imkn vermesidir. Saf C++ kodlar yazarak bir form gelitirdiinizde, tasarm deitirmeniz olduka zaman kaybettirici olabilir. Eer formunuzu Qt Designerla oluturduysanz, uic deitirdiiniz her form iin yeniden kaynak kodu olutururken hi zaman kaybetmezsiniz.

27

Qt 4 ile C++ GUI Programlama

ekil Deitiren Diyaloglar


Her zaman ayn paracklar gsteren diyaloglar oluturmay grdk. Baz durumlarda, diyaloglarn ekil deitirebilmesi arzu edilir. ekil deitiren diyaloglarn en ok bilinen iki tr genileyen diyaloglar(extension dialogs) ve ok sayfal diyaloglardr(multi-page dialogs). Her iki trden diyalog da Qt ile oluturulabilir. Genileyen diyaloglar genellikle basit bir grnm sunarlar fakat kullancya basit ve geniletilmi grnmler arasnda gei yapabilme imknn veren iki konumlu butona(toggle button) sahiptir. Genileyen diyaloglar ounlukla hem sradan kullanclara hem de ileri dzey kullanclara hitap etmesi istenen uygulamalarda kullanlr. Bu ksmda, Qt Designer ekil 2.9da grnen genileyen diyalou oluturmakta kullanacaz.

ekil 2.9

Diyalog, bir hesap izelgesi(spreadsheet) uygulamas iindeki bir ya da birka stunu sralamak iin kullanabilecei bir Sort(sralama) diyaloudur. Diyaloun basit grnm kullancya tek bir sralama anahtar girmesine imkn verir. Geniletilmi grnm ise iki ekstra sralama anahtar daha salar. Bir More butonu kullancnn basit ve geniletilmi grnmler arasnda gei yapmasna izin verir. nce geniletilmi grnme sahip bir parac Qt Designerda oluturacaz, ve sonra ikincil ve ncl anahtarlar gizleyeceiz. Parack karmak grnebilir, fakat Qt Designerda bunu yapmak olduka kolaydr. in srr nce birincil anahtar ksmn yapp sonrada onu iki kere kopyalayarak ikincil ve ncl anahtarlar elde etmekte: 1. File > New Formu tklayn ve Dialog without Buttons ablonunu sein. 2. Bir OK butonu oluturun ve formun sa st kesine tayn. objectName niteliini okButton olarak deitirin ve default niteliini true olarak ayarlayn. 3. Bir Cancel butonu oluturun ve OK butonunun altna tayn. objectName niteliini cancelButton olarak deitirin. 4. Bir dikey ara para oluturun ve Cancel butonunun altna tayn, sonrada bir More butonu oluturup dikey ara parann altna tayn. More butonunun objectName niteliini moreButton olarak deitirin, text niteliini &More olarak ayarlayn ve checkable niteliini true olarak ayarlayn. 5. OK butonuna tklayn ve sonra Ctrlye basl tutaraktan Cancel butonunu, dikey ara paray ve More butonunu da tklayarak sein, sonrada Form > Lay Out Verticallyye tklayn. 6. Bir Group Box, iki Label, iki Combo Box, ve bir Horizontal Spacer oluturun ve form zerinde herhangi bir yere koyun. 28

Qt 4 ile C++ GUI Programlama 7. Group Boxn sa alt kesinden tutun ve biraz geniletin. Sonra dier paracklar Group Box iine tayn ve yaklak olarak ekil 2.10(a)daki gibi yerletirin. 8. kinci Combo Boxn sa kenarndan tutarak, geniliini birinci Combo Boxn geniliinin iki kat olacak kadar arttrn. 9. Group Boxn title niteliini &Primary Key, birinci Labeln text niteliini Column: ve ikinci Labeln text nitaliini Order: olarak ayarlayn. 10. Birinci Combo Boxa sa tklayn ve Qt Designern Combo Box editrne ulamak iin Edit Itemsi sein. Bir None esi oluturun. 11. kinci Combo Boxa sa tklayn ve Edit Items sein. Bir Ascendind, bir de Descending paras oluturun. 12. Group Boxa tklayn, sonra Form > Lay Out in a Gridi tklayn. Group Boxa tekrar tklayn ve Form > Adjust Sizea tklayn. Bu, yerleimi ekil 2.10(b)deki gibi gsterecek.

ekil 2.10

imdide Secondary Key ve Tertiary Key Group Boxlarn ekleyeceiz. 1. Diyalog penceresini ekstra blmler iin yeterince yksek yapn. 2. Primary Key ve iindekilerin bir kopyasn oluturmak iin Ctrl tuuna basl tutaraktan Primary Key Group Boxn tklayn ve srkleyip kendisinin altna brakn. Bu ilemi bir kez de oluturduunuz kopyann stnde uygulayn ve nc Group Box da oluturun. 3. title niteliklerini &Secondary Key ve &Tertiary Key olarak deitirin. 4. Bir Vertical Spacer oluturun ve Secondary Key Group Box ile Primary Key Group Box arasna yerletirin. 5. Paracklar ekil 2.11(a)da grnd gibi, -hayali- bir zgara kalb iinde dzenleyin. 6. Seili paracklar seimden karmak iin formu tklayn, sonra Form > Lay Out in a Gridi tklayn. imdi, form ekil 2.11(b) ile elemeli. 7. ki Vertical Spacern da sizeHint niteliini *20, 0+ olarak ayarlayn.

29

Qt 4 ile C++ GUI Programlama

ekil 2.11

Formu SortDialog olarak yeniden adlandrn ve window title niteliini Sort olarak deitirin. ocuk paracklarn isimlerini ekil 2.12de gsterildii gibi deitirin.

ekil 2.12

Edit > Edit Tab Ordera tklayn. Batan aa srayla tm Combo Boxlar tklayn, sonrada sa taraftaki OK, Cancel ve More butonlarna srayla tklayn. Tab sralama modundan kmak iin Edit > Edit Widgetsa tklayn. Bylece formumuz tasarlanm oldu. Artk onu birka sinyal-yuva balants kurarak fonksiyonel yapmak iin hazrz. Qt Designer paracklar arasnda balantlar kurmamza imkn verir. ki adet balant kurmay ihtiyacmz var. Qt Designern balant moduna girmek iin Edit > Edit Signals/Slotsa tklayn. Balantlar, ekil 2.13te gsterildii gibi, formun paracklar arasndaki mavi oklarla temsil edilir ve Qt Designern Signal/Slot Editor penceresinde listelenir. ki parack arasnda balant kurmak iin gnderici(sender) parac tklayn ve krmz oku alc(receiver) paraca tayn ve brakn. Sinyal ve yuva balantlar semenize imkn veren bir diyalog beliriverecek.

30

Qt 4 ile C++ GUI Programlama

ekil 2.13

lk balant okButton ile formun accept() yuvas arasnda yaplacak. okButtona tklayn ve krmz oku formun bo bir yerine tayp brakn, sonra beliren Configure Connection diyalounda sinyal olarak clicked()i yuva olarak accept()i sein ve sonrada OKe tklayn.

ekil 2.14

kinci balant iin, cancelButtona tklayn ve krmz oku formun bo bir yerine tayp brakn, ve Configure Connection diyalounda butonun clicked() sinyalini formun reject() yuvasna balayn. ncs moreButton ile secondaryGroupBox arasndaki balantdr. moreButtona tklayn ve krmz oku secondaryGroupBoxa tayp brakn. Sinyal olarak toggled(bool)u, yuva olarak setVisible (bool)u sein. Varsaylan olarak, Qt Designer setVisible(bool)u listelemez, ancak Show all signals and slots seeneini seili hale getirirseniz listelenecektir. Drdnc ve son balant moreButtonn toggled(bool) sinyali ile tertiaryGroupBox'n setVisible (bool) yuvas arasndadr. Bu balanty da yapar yapmaz, Edit > Edit Widgetsa tklayarak balant modundan kn.

31

Qt 4 ile C++ GUI Programlama Diyalou sortdialog.ui adyla, sort adl bir klasre kaydedin. Forma kod eklemek iin, Go to Cell diyalounda kullandmz gibi, burada da ayn oklu kaltm yaklamn kullanacaz. nce, aadaki ierikle sortdialog.h dosyasn oluturun:
#ifndef SORTDIALOG_H #define SORTDIALOG_H #include <QDialog> #include "ui_sortdialog.h" class SortDialog : public QDialog, public Ui::SortDialog { Q_OBJECT public: SortDialog(QWidget *parent = 0); void setColumnRange(QChar first, QChar last); }; #endif

imdi, sortdialog.cpp dosyasn oluturun: Kod Grnm:


1 #include <QtGui> 2 #include "sortdialog.h" 3 SortDialog::SortDialog(QWidget *parent) 4 : QDialog(parent) 5 { 6 setupUi(this); 7 8 9 10 11 } secondaryGroupBox->hide(); tertiaryGroupBox->hide(); layout()->setSizeConstraint(QLayout::SetFixedSize); setColumnRange('A', 'Z');

12 void SortDialog::setColumnRange(QChar first, QChar last) 13 { 14 primaryColumnCombo->clear(); 15 secondaryColumnCombo->clear(); 16 tertiaryColumnCombo->clear(); 17 18 19 20 21 22 23 24 25 secondaryColumnCombo->addItem(tr("None")); tertiaryColumnCombo->addItem(tr("None")); primaryColumnCombo->setMinimumSize( secondaryColumnCombo->sizeHint()); QChar ch = first; while (ch <= last) { primaryColumnCombo->addItem(QString(ch)); secondaryColumnCombo->addItem(QString(ch)); tertiaryColumnCombo->addItem(QString(ch));

32

Qt 4 ile C++ GUI Programlama


26 27 28 } ch = ch.unicode() + 1; }

Kurucu, diyaloun ikincil ve ncl blmlerini gizler. Ayn zamanda, diyaloun kullanc tarafndan yeniden boyutlandrlamamas iin formun yerleiminin sizeConstraint niteliini QLayout::SetFixedSizea ayarlar. Bylece yerleim yeniden boyutlandrma sorumluluunu zerine alr ve ocuk paracklar grndnde ya da gizlendiinde diyalou otomatik olarak yeniden boyutlandrr. Bu da diyaloun her zaman ideal boyutta olmasn salar. setColumnRange() yuvas, hesap izelgesinde seilen stunlara gre, Combo Boxlarn ieriini yeniler. Bir de istee bal ikincil ve ncl anahtarlar iin birer None esi ekleriz. Satr 19 ve 20 ho bir yerleim sunar. QWidget::sizeHint() fonsiyonu bir paracn ideal boyutunu dndrr. Bu, farkl trde paracklarn ya da farkl ierikli benzer paracklarn yerleim sistemi tarafndan neden farkl boyutlandrldn aklar. te, C, F ve aralarndaki stunlar Combo Boxn seeneklerine katan ve sonra diyalou gsteren test amal bir main() fonksiyonu:
#include <QApplication> #include "sortdialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); SortDialog *dialog = new SortDialog; dialog->setColumnRange('C', 'F'); dialog->show(); return app.exec(); }

Bylece genileyen diyaloumuzu bitirdik. Bu rnekte de grld zere, genileyen bir diyalog tasarlamak, dz bir diyalog tasarlamaktan daha zor deildir: Tm ihtiyacmz iki konumlu(toggle) bir buton, birka ekstra sinyal-yuva balants ve yeniden boyutlandrlamayan(non-resizable) bir yerleim. Uygulamalarda, genilemeyi kontrol eden butonun basit grnmdeyken Advanced >>> metni ile grntlenmesi, geniletilmi grnmdeyken Advanced <<< metni ile grntlenmesi yaygndr. Qt iinde bunu gerekletirmek, QPustButton her tklandnda setText()i armak suretiyle ok kolaydr. ekil deitiren diyaloglarn bir dier yaygn tipi, yine Qt ile oluturulmas olduka kolay olan ok-sayfal diyaloglardr. Bu tr diyaloglar ok farkl yollarla oluturulabilir.

Dinamik Diyaloglar
Dinamik diyaloglar(dynamic dialogs) alma srasnda Qt Designer .ui dosyalarndan oluturulan diyaloglardr. .ui dosyalarn uic kullanarak C++ koduna dntrmek yerine, dosyay alma srasnda QUiLoader snfn kullanarak ykleyebiliriz:
QUiLoader uiLoader; QFile file("sortdialog.ui"); QWidget *sortDialog = uiLoader.load(&file); if (sortDialog) { ...

33

Qt 4 ile C++ GUI Programlama


}

ocuk paracklara QObject::findChild<T>() kullanarak eriebiliriz:


QComboBox *primaryColumnCombo = sortDialog->findChild<QComboBox *>("primaryColumnCombo"); if (primaryColumnCombo) { ... }

findChild<T>() fonksiyonu, verilen isim ve tiple eleen ocuk nesneleri dndren bir ablon ye fonksiyondur(template member function). QUiLoader snf ayr bir ktphanede yer alr. Bir Qt uygulamasnda QUiLoader kullanabilmek iin, uygulamann .pro dosyasna u satr eklememiz gerekir:
CONFIG += uitools

Dinamik diyaloglar, uygulamay yeniden derlemeksizin, formun yerleimini deitirebilmemizi mmkn klar.

Yerleik Parack ve Diyalog Snflar


Qt, birok durumda yaygn olarak ihtiya duyulan paracklar ve diyaloglar bize yerleik olarak salar. Bu ksmda size birounun ekran grntsn sunuyoruz:

ekil 2.15

ekil 2.16

ekil 2.17

34

Qt 4 ile C++ GUI Programlama

ekil 2.18

ekil 2.19

35

Qt 4 ile C++ GUI Programlama

ekil 2.20

ekil 2.21

ekil 2.22

36

Qt 4 ile C++ GUI Programlama

ekil 2.23

ekil 2.24

Qt bize drt eit buton salar: QPushButton, QToolButton, QCheckBox ve QRadioButton (ekil 2.15). QPushButton ve QToolButton, ounlukla tklandklar zaman bir eylem balatmak iin kullanlrlar, fakat iki konumlu buton olarak da davranabilirler. QCheckBox bamsz on/off seenekleri iin kullanlabilir. Hlbuki QRadioButtonlar genellikle birbirini dlayandrlar. Qtun konteyner paracklar(container widgets) dier paracklar kapsayan(iine alan) paracklardr (ekil 2.16 ve 2.17). QTabWidget ve QToolBox ok-sayfal paracklardr. Her bir sayfa bir ocuk paracktr ve sayfalar 0dan balayarak numaralandrlrlar. QTabWidgetlarn ekilleri ve tablarnn konumlar ayarlanabilir. e grntleme snflar(item view classes), byk miktarda verileri ilemede kullanlr ve sklkla kaydrma ubuklar(scroll bars) kullanrlar (ekil 2.18). Kaydrma ubuu mekanizmas, e grntleme snflar ve dier kaydrlabilir(scrollable) parack eitleri iin temel snf olan QAbstractScrollArea iinde gerekletirilir. Qt ktphanesi biimlendirilmi metni grntlemede ve dzenlemede kullanabilecek zengin bir metin motoru(text engine) ierir. Bu motor, font zelliklerini, metin hizalanlarn, listeleri, tablolar, resimleri ve kprleri(hyperlinks) destekler. Qt bize, yalnzca bilgi grntlemede kullanlan birka parack salar (ekil 19). Bunlarn en nemlisi QLabeldr. QLabel dz metin, HTML ve resim grntlemede kullanlabilir. 37

Qt 4 ile C++ GUI Programlama QTextBrowser, biimlendirilmi metin grntleyebilen, bir saltokunur(read-only) QTextEdit altsnfdr. Bu snf, byk biimlendirilmi metin dokmanlar iin QLabeldan daha ok tercih edilir, nk QLabeln aksine, gerektiinde otomatik olarak kaydrma ubuklar salayabilirken ayn zamanda klavye ve fare navigasyonu iin kapsaml bir destek de salar. Qt Assistant 4.3 kullanclara dokmantasyonu sunarken QTextBrowser kullanr. Qt, ekil 2.20de de grld zere veri girii iin birka parack salar. QLineEdit bir girdi maskesi(input mask) veya bir geerlilik denetleyicisi ya da her ikisini de kullanarak girdileri snrlayabilir. QTextEdit QAbstractScrollAreann, byk miktardaki metinleri dzenlemede yetenekli bir altsnfdr. Bir QTextEdit dz metin ya da zengin metin dzenlemeye ayarlanabilir. kinci durumda, Qtun zengin metin motorunun destekledii btn eleri grntleyebilir. Hem QLineEdit hem de QTextEdit pano(clipboard) ile tamamen entegredir. Qt, ok ynl bir mesaj kutusu(message box) ve bir hata(error) diyalou salar(ekil 2.21). Zaman alan ilemlerin ilerlemesi QProgressDialog ya da QProgressBar kullanlarak gsterilebilir. QInputDialog, kullancnn tek bir satr metin ya da tek bir say girmesi istendiinde ok kullanldr. Qt, kullancnn renk, font veya dosya semesini veya bir dokman yazdrmasn kolaylatran diyaloglardan standart bir set salar. Bunlar ekil 2.22 ve 2.23de gsteriliyor. Son olarak, QWizard sihirbazlar(wizards) oluturmak iin bir at(framework) salar. Sihirbazlar, kullancnn renmeyi zor bulabilecei karmak ya da az karlalan grevler iin kullanldrlar. Bir sihirbaz rnei ekil 2.24de gsteriliyor. Birok kullanma hazr ilev yerleik paracklar ve diyaloglar tarafndan salanr. Daha zelletirilmi gereksinimler, parack niteliklerini ayarlayarak ya da sinyaller ve yuvalar balamak ve yuvalarda zel davranlar gerekletirmek suretiyle giderilebilirler. Baz durumlarda, sfrdan bir zel parack(custom widget) oluturmak uygun olabilir. Qt bunu basitletirir ve zel paracklar da tpk Qtun yerleik paracklar gibi platform bamszlna sahip olurlar. zel paracklar da Qt Designera entegre edilebilirler ve bylece Qtun yerleik paracklaryla ayn ekilde kullanlabilirler.

38

Qt 4 ile C++ GUI Programlama

BLM 3: ANA PENCERELER OLUTURMA

Bu blm, Qtu kullanarak nasl ana pencereler(main windows) oluturulduunu retecek. Sonunda, bir uygulamann kullanc arayzn tam olarak oluturabileceksiniz. Bir uygulamann ana penceresi, uygulamann kullanc arayznn stne ina edildii bir at(framework) salar. ekil 3.1de grnen Spreadsheet(Hesap izelgesi) uygulamasnn ana penceresi, bu blmn belkemiini oluturacak. Spreadsheet uygulamasnda, Blm 2de oluturduumuz Find, Go to Cell ve Sort diyaloglar da kullanlr.

ekil 3.1

QMainWindow Altsnf Tretme


Bir uygulamann ana penceresi QMainWindowdan altsnf treterek oluturulur. Mademki QDialog ve QMainWindow QWidgettan tretilmitir, o halde, Blm 2de diyaloglar oluturmakta kullandmz teknikler, ana pencereler oluturmak iin de uygundurlar. Ana pencereler Qt Designer kullanlarak da oluturulabilirler, fakat bu blmde, nasl yapldn rnekle aklamak amacyla, her eyi kodlayarak yapacaz. Eer siz daha grsel bir yaklam tercih ediyorsanz, Qt Designern evrimii klavuzundaki Creating Main Windows in Qt Designer adl blme bakabilirsiniz. Spreadsheet uygulamasnn ana penceresinin kaynak kodlar, mainwindow.h ve mainwindow.cpp dosyalarna yaylr. Balk dosyas ile balayalm:
#ifndef MAINWINDOW_H #define MAINWINDOW_H

39

Qt 4 ile C++ GUI Programlama


#include <QMainWindow> class class class class QAction; QLabel; FindDialog; Spreadsheet;

class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); protected: void closeEvent(QCloseEvent *event);

MainWindow snfn, QMainWindowun bir altsnf olarak tanmlarz. Kendi sinyal ve yuvalarn salad iin Q_OBJECT makrosunu ierir. closeEvent() fonksiyonu, kullanc pencereyi kapattnda otomatik olarak arlan, QWidget iindeki sanal(virtual) bir fonksiyondur. MainWindow iinde yeniden gerekletirilmitir ve bu sayede kullancya, deiiklikleri diske kaydetmek isteyip istemediini sorabiliriz.
private slots: void newFile(); void open(); bool save(); bool saveAs(); void find(); void goToCell(); void sort(); void about();

File > New ve Help > About gibi baz men seenekleri MainWindow iinde private yuvalar olarak gerekletirilirler. Birok yuva iin dn deeri voiddir, fakat save() ve saveAs() yuvalar bir bool deer dndrrler. Bir yuva bir sinyale cevaben iletildiinde, dn deeri grmezden gelinir, fakat bir yuvay bir fonksiyon olarak ardmzda, dn deerini, herhangi bir C++ fonksiyonunun dn deeri gibi kullanmamz da mmkndr.
void openRecentFile(); void updateStatusBar(); void spreadsheetModified(); private: void createActions(); void createMenus(); void createContextMenu(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool okToContinue(); bool loadFile(const QString &fileName); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); void updateRecentFileActions(); QString strippedName(const QString &fullFileName);

40

Qt 4 ile C++ GUI Programlama

Ana pencere, kullanc arayzn desteklemek iin birka private yuvaya ve birka private fonksiyona daha ihtiya duyar.
Spreadsheet *spreadsheet; FindDialog *findDialog; QLabel *locationLabel; QLabel *formulaLabel; QStringList recentFiles; QString curFile; enum { MaxRecentFiles = 5 }; QAction *recentFileActions[MaxRecentFiles]; QAction *separatorAction; QMenu *fileMenu; QMenu *editMenu; ... QToolBar *fileToolBar; QToolBar *editToolBar; QAction *newAction; QAction *openAction; ... QAction *aboutQtAction; }; #endif

Private yuvalara ve fonksiyonlara ek olarak, MainWindow birok private deikene sahiptir. Onlarn hepsini, onlar kullanrken aklayacaz. imdi gerekletirimi inceleyelim:
#include <QtGui> #include #include #include #include #include "finddialog.h" "gotocelldialog.h" "mainwindow.h" "sortdialog.h" "spreadsheet.h"

Altsnflarmzda kullandmz tm Qt snflarnn tanmlar ieren <QtGui> balk dosyasn dhil ederek balarz. Baz zel balk dosyalarn daha dhil ederiz, bilhassa Blm 2deki finddialog.h, gotocelldialog.h ve sortdialog.h.
MainWindow::MainWindow() { spreadsheet = new Spreadsheet; setCentralWidget(spreadsheet); createActions(); createMenus(); createContextMenu(); createToolBars(); createStatusBar(); readSettings(); findDialog = 0;

41

Qt 4 ile C++ GUI Programlama

setWindowIcon(QIcon(":/images/icon.png")); setCurrentFile(""); }

Kurucuya, bir Spreadsheet parac oluturarak ve onu ana pencerenin merkez parac(central widget) olacak ekilde ayarlayarak balarz. Merkez parack ana pencerenin ortasna oturur(ekil 3.2). Spreadsheet snf, baz hesap izelgesi kabiliyetleri olan (hesap izelgesi formlleri iin destek gibi) bir QTableWidget altsnfdr.

ekil 3.2

Ana pencerenin geri kalann kurmak iin createActions(), createMenus(), createContextMenu(), createToolBars() ve createStatusBar() private fonksiyonlarn arrz. Uygulamann depolanm ayarlarn okumak iin readSettings() private fonksiyonunu arrz. findDialog iaretisini bo(null) bir iareti olarak ilklendiririz. MainWindow::find() ilk kez arldnda, FindDialog nesnesini oluturacaz. Kurucunun sonunda, pencerenin simgesi(icon) olarak icon.pngyi ayarlarz. Qt, BMP, GIF, JPEG, PNG, PNM, SVG, TIFF, XBM ve XPM de dhil olmak zere birok resim formatn destekler. QWidget::setWindowIcon() ars, pencerenin sol-st kesinde grnen simgeyi ayarlar. GUI uygulamalar genelde birok resim kullanrlar. Uygulamaya resimler salamann farkl metotlar vardr. En yaygnlar unlardr: Resimleri dosyalarda depolamak ve alma srasnda yklemek. Kaynak koduna XPM dosyalar dhil etmek.(Bu ie yarar, nk XPM dosyalar geerli C++ dosyalardr.) Qtun kaynak mekanizmasn(resource mechanism) kullanmak. Burada, Qtun kaynak mekanizmasn kullanrz, nk bu dosyalar alma srasnda yklemekten daha kullanldr ve desteklenen her dosya formatyla baarl olur. Biz resimleri bir kaynak aac(source tree) iinde, images adl bir altdizinde saklamay tercih ettik.

42

Qt 4 ile C++ GUI Programlama Qtun kaynak sistemini kullanmak iin, bir kaynak dosyas oluturmal ve kaynak dosyasn belirten bir satr .pro dosyasna eklemeliyiz. Bu rnekte kaynak dosyasna spreadsheet.qrc ismini verdik ve bu nedenle.pro dosyasna u satr ekleriz:
RESOURCES = spreadsheet.qrc

Kaynak dosyas basit bir XML format kullanr. te kullandmz kaynak dosyasndan bir alnt:
<RCC> <qresource> <file>images/icon.png</file> ... <file>images/gotocell.png</file> </qresource> </RCC>

Kaynak dosyalar uygulamann yrtlebilir haline derlenir, bylece kaybolmazlar. Kaynaklardan yararlanrken, yol neki(path prefix) :/ kullanrz. Kaynaklar herhangi bir dosya olabilirler (yani sadece resimler deil), ve kaynaklar Qtun bir dosya ismi bekledii birok yerde kullanabiliriz.

Menler ve Ara ubuklar Oluturma


Birok modern GUI uygulamas menler, balam menleri(context menus) ve ara ubuklar salar. Menler, kullanclarn uygulamay kefetmesine ve yeni eyler yapmay renmesine olanak verirken, balam menleri ve ara ubuklar sk kullanlan ilevlere hzl erimeyi salar. ekil 3.3 Spreadsheet uygulamasnn menlerini gsteriyor.

ekil 3.3

Qt, menler ve ara ubuklar programlamay eylem(action) kavram sayesinde basitletirir. Bir eylem, menler ve ara ubuklar eklenebilen bir edir. Qtda menler ve ara ubuklar oluturmak u admlar gerektirir: Eylemler olutur ve ayarla. Menler olutur ve eylemlere yerletir. Ara ubuklar olutur ve eylemlere yerletir. Spreadsheet uygulamasnda, eylemler createActions() iinde oluturulur:
void MainWindow::createActions() { newAction = new QAction(tr("&New"), this); newAction->setIcon(QIcon(":/images/new.png")); newAction->setShortcut(QKeySequence::New); newAction->setStatusTip(tr("Create a new spreadsheet file")); connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));

43

Qt 4 ile C++ GUI Programlama New eylemi bir hzlandrcya (New), bir ebeveyne(ana pencere), bir simgeye, bir ksayol tuuna ve bir durum ipucuna(status tip) sahiptir. Birok pencere sistemi, belirli eylemler iin standartlatrlm klavye ksayollarna sahiptir. rnein, New eylemi Windows, KDE ve GNOMEda Ctrl+N, Mac OS Xte Command+N ksayoluna sahiptir. Uygun QKeySequence::StandardKey enum deerini kullanarak, Qtun uygulamann alt platforma gre doru ksayollar salamasn garantiye alrz. Eylemin triggered() sinyalini ana pencerenin private newFile() yuvasna balarz. Bu balant, kulanc File > New men esini setiinde ya da New ara ubuu butonunu tkladnda ya da Ctrl+N ksayoluna bastnda newFile() yuvasnn arlmasn salar. Open, Save ve Save As eylemleri New eylemiyle ok benzerdirler, bu nedenle doruca File mensnn recent opened files(son alan dosyalar) blmne geeceiz:
... for (int i = 0; i < MaxRecentFiles; ++i) { recentFileActions[i] = new QAction(this); recentFileActions[i]->setVisible(false); connect(recentFileActions[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); }

recentFileActions dizisini eylemlere ekledik. Her eylem gizlidir ve openRecentFile() yuvasna balanmtr. Daha sonra, son alan dosyalar eylemlerinin nasl grnr yapldn ve kullanldn greceiz.
exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(tr("Ctrl+Q")); exitAction->setStatusTip(tr("Exit the application")); connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));

Exit eylemi daha nce grdklerimizden az da olsa farkldr. Bir uygulamay sonlandrmak iin standartlatrlm bir tu dizisi yoktur, bu nedenle tu dizisini kendimiz aka belirtiriz. Bir dier farkllkta, pencerenin close() yuvasna balam olmamzdr. imdi, Select All eylemine geebiliriz:
... selectAllAction = new QAction(tr("&All"), this); selectAllAction->setShortcut(QKeySequence::SelectAll); selectAllAction->setStatusTip(tr("Select all the cells in the " "spreadsheet")); connect(selectAllAction, SIGNAL(triggered()), spreadsheet, SLOT(selectAll()));

selectAll() yuvas, QTableWidget'n bir atas QAbstractItemView tarafndan salanr, bu nedenle onu kendimiz gerekletirmemiz gerekmez. Hadi ileri -Options mensndeki Show Grid eylemine- rayalm:
... showGridAction = new QAction(tr("&Show Grid"), this); showGridAction->setCheckable(true); showGridAction->setChecked(spreadsheet->showGrid()); showGridAction->setStatusTip(tr("Show or hide the spreadsheet's " "grid"));

44

Qt 4 ile C++ GUI Programlama


connect(showGridAction, SIGNAL(toggled(bool)), spreadsheet, SLOT(setShowGrid(bool)));

Show Grid checkable bir eylemdir. Checkable actionlar mende bir kontrol-imi(check-mark) ile sunulur ve ara ubuklarnda iki konumlu butonlar gibi gerekletirilirler. Eylem ak olduunda, Spreadsheet bileeni bir zgara(grid) gsterir. Show Grid ve Auto-Recalculate eylemleri bamsz checkable eylemlerdir. Qt, QActionGroup snf sayesinde birbirini dlayan/ayrk eylemleri de destekler.
... aboutQtAction = new QAction(tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); }

About Qt eylemi iin(ekil 3.4), qApp global deikeni sayesinde eriilebilir olan, QApplication nesnesinin aboutQt() yuvasn kullanrz.

ekil 3.4

Eylemleri oluturduumuza gre imdi de onlar ieren bir men sistemi ina etme aamasna ilerleyebiliriz:
void MainWindow::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAction); fileMenu->addAction(openAction); fileMenu->addAction(saveAction); fileMenu->addAction(saveAsAction); separatorAction = fileMenu->addSeparator(); for (int i = 0; i < MaxRecentFiles; ++i) fileMenu->addAction(recentFileActions[i]); fileMenu->addSeparator(); fileMenu->addAction(exitAction);

Qtda, menler QMenu rnekleridirler. addMenu() fonksiyonu, belirtilen metin ile bir QMenu parac oluturur ve men ubuuna ekler. QMainWindow::menuBar() fonksiyonu bir QMenuBar iaretisi dndrr. Men ubuu ilk menuBar() arsnda oluturulur. File mensn oluturarak balarz. Sonra ona New, Open, Save ve Save As eylemlerini ekleriz. Benzer eleri grsel olarak birlikte gruplandrmak iin bir ayrc(separator) ekleriz. (Balangta gizli olan)Eylemleri 45

Qt 4 ile C++ GUI Programlama recentfileActions dizisinden eklemek iin bir for dngs kullanrz ve en sona exitAction eylemini ekleriz. Ayrclardan birini iareti olarak tutarz. Bylece iki ayrcnn arasnda hibir ey yokken yani hi son alan dosya yokken, ayrclardan birini gizleyebiliriz.
editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(cutAction); editMenu->addAction(copyAction); editMenu->addAction(pasteAction); editMenu->addAction(deleteAction); selectSubMenu = editMenu->addMenu(tr("&Select")); selectSubMenu->addAction(selectRowAction); selectSubMenu->addAction(selectColumnAction); selectSubMenu->addAction(selectAllAction); editMenu->addSeparator(); editMenu->addAction(findAction); editMenu->addAction(goToCellAction);

Sonra, Edit mensn oluturur, File mens iin yaptmz gibi, istediimiz konumlara QMenu::addAction() ile eylemler, QMenu::addMenu() ile altmenler(submenu) ekleriz. Altmenler de menler gibi QMenu rnekleridirler.
toolsMenu = menuBar()->addMenu(tr("&Tools")); toolsMenu->addAction(recalculateAction); toolsMenu->addAction(sortAction); optionsMenu = menuBar()->addMenu(tr("&Options")); optionsMenu->addAction(showGridAction); optionsMenu->addAction(autoRecalcAction); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAction); helpMenu->addAction(aboutQtAction); }

Benzer biimde Tools, Options ve Help menlerini de olutururuz.


void MainWindow::createContextMenu() { spreadsheet->addAction(cutAction); spreadsheet->addAction(copyAction); spreadsheet->addAction(pasteAction); spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu); }

Her Qt parac QActionlarla ilikilendirilmi bir listeye sahip olabilir. Uygulamaya bir balam mens salamak iin Spreadsheet paracna istenilen eylemleri ekleriz ve paracn balam men politikasn(context menu policy) bu eylemleri ieren bir balam men gstermeye ayarlarz. Balam menler, bir paraca sa tklayarak ya da platforma zg bir tua basarak arlrlar. Spreadsheet uygulamasnn balam mens ekil 3.5te gsteriliyor.

46

Qt 4 ile C++ GUI Programlama

ekil 3.5

Balam menler salamann daha sofistike bir yolu da QWidget::contextMenuEvent() fonksiyonunu uyarlamaktr: Bir QMenu parac oluturulur, istenilen eylemler eklenir ve exec() fonksiyonu arlr.
void MainWindow::createToolBars() { fileToolBar = addToolBar(tr("&File")); fileToolBar->addAction(newAction); fileToolBar->addAction(openAction); fileToolBar->addAction(saveAction); editToolBar = addToolBar(tr("&Edit")); editToolBar->addAction(cutAction); editToolBar->addAction(copyAction); editToolBar->addAction(pasteAction); editToolBar->addSeparator(); editToolBar->addAction(findAction); editToolBar->addAction(goToCellAction); }

Ara ubuklar oluturmak menler oluturmakla ok benzerdir. Bir File ara ubuu ve bir Edit ara ubuu olutururuz. ekil 3.6da grld zere, tpk bir men gibi, bir ara ubuu da ayrclara sahip olabilir.

ekil 3.6

Durum ubuunu Ayarlama


Menlerin ve ara ubuklarnn tamamlanmasyla birlikte, artk Spreadsheet uygulamasnn durum ubuunu(status bar) ele almaya hazrz. Normal durumda, durum ubuu iki gsterge ierir: geerli hcrenin konumu ve geerli hcrenin forml. Durum ubuu, durum ipularn ve dier geici mesajlar gstermede de kullanlr. ekil 3.7 durum ubuunun her halini gsterir.

ekil 3.7

Durum ubuunu ayarlamak iin MainWindow kurucusu createStatusBar() fonksiyonunu arr:


void MainWindow::createStatusBar() { locationLabel = new QLabel(" W999 "); locationLabel->setAlignment(Qt::AlignHCenter); locationLabel->setMinimumSize(locationLabel->sizeHint());

47

Qt 4 ile C++ GUI Programlama


formulaLabel = new QLabel; formulaLabel->setIndent(3); statusBar()->addWidget(locationLabel); statusBar()->addWidget(formulaLabel, 1); connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateStatusBar())); connect(spreadsheet, SIGNAL(modified()), this, SLOT(spreadsheetModified())); updateStatusBar(); }

QMainWindow::statusBar() fonksiyonu durum ubuuna bir iareti dndrr. (Durum ubuu statusBar() ilk arldnda oluturulur.) Durum gstergeleri, gerektiinde metnini deitirdiimiz basit QLabellardr. QLabellar durum ubuuna eklendiinde, durum ubuunun ocuklar yaplmak zere otomatik olarak ebeveyn edindirilirler. ekil 3.7 bu iki etiketin(label) farkl boluk gereksinimleri olduunu gsterir. Hcre konum gstergesi ok kk bolua ihtiya duyar ve pencere yeniden boyutlandrldnda, her ekstra boluk sadaki hcre forml gstergesine gitmelidir. Bu, forml etiketinin QStatusBar::addWidget() ars iinde genileme katsays(stretch factor) 1 olarak belirtilerek baarlr. Konum gstergesi varsaylan olarak 0 genileme katsaysna sahiptir. Bu, konum gstergesinin genilemesinin tercih edilmedii anlamna gelir. QStatusBar gsterge paracklarn yerletirirken, her parack iin QWidget::sizeHint() tarafndan belirlenen ideal boyuta uymay dener ve sonra genileyebilen her parac boluklar doldurmak iin geniletir. Bir paracn ideal boyutu paracn ieriine baldr ve biz ierii deitiimizde deiir. Konum gstergesini srekli yeniden boyutlandrlmasn nlemek iin, minimum boyutunu ierebilecei en geni metnin (W999) genilii ve bir ekstra boluk olarak ayarlarz. Ayrca hizalann(alignment) Qt::AlingHCenter ile yatay ortalanm metin olarak ayarlarz. Fonksiyonun bitimine yakn, Spreadsheetin iki sinyalini MainWindowun iki yuvasna balarz: updateStatusBar() ve spreadsheetModified().
void MainWindow::updateStatusBar() { locationLabel->setText(spreadsheet->currentLocation()); formulaLabel->setText(spreadsheet->currentFormula()); }

updateStatusBar() yuvas hcre konum ve hcre forml gstergelerini gnceller. Kullanc, hcre imlecini yeni bir hcreye her tadnda arlr. Yuva, createStatusBar()n sonunda gstergeleri sfrlamak iin tipik bir fonksiyon olarak da kullanlr. Bu gereklidir, nk Spreadsheet balangta currentCellChanged() sinyalini yaymaz.
void MainWindow::spreadsheetModified() { setWindowModified(true); updateStatusBar(); }

48

Qt 4 ile C++ GUI Programlama spreadsheetModified() yuvas windowModified niteliini true olarak ayarlar ve balk ubuunu(title bar) gnceller. Fonksiyon ayrca konum ve forml gstergelerini gnceller, bylece gstergeler geerli durumu yanstrlar.

File Mensn Gerekletirme


Bu ksmda, File mensnn seeneklerinin almas ve son alan dosyalar listesinin dzenlenmesi iin gerekli olan yuvalar ve private fonksiyonlar gerekletireceiz.
void MainWindow::newFile() { if (okToContinue()) { spreadsheet->clear(); setCurrentFile(""); } }

newFile() yuvas kullanc File > New men seeneini ya da New ara ubuu butonunu tkladnda arlr. okToContinue() private fonksiyonu, eer kaydedilmemi bir deiiklik varsa Do you want to save your changes? diyalounun (ekil 3.8) grntlenmesini salar. Diyalog, eer kullanc Yes veya No seeneklerinden birini seerse true, Cancel seerse false dndrr. Spreadsheet::clear() fonksiyonu hesap izelgesinin tm hcrelerini ve formllerini temizler. setCurrentFile() private fonksiyonu pencere baln gnceller, ek olarak curFile private deikenini ayarlar ve son alan dosyalar listesini gnceller.

ekik 3.8 bool MainWindow::okToContinue() { if (isWindowModified()) { int r = QMessageBox::warning(this, tr("Spreadsheet"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (r == QMessageBox::Yes) { return save(); } else if (r == QMessageBox::Cancel) { return false; } } return true; }

okToContinue() iinde, windowModified niteliinin durumunu kontrol ederiz. Eer true ise, ekil 3.8de gsterilen mesaj kutusunu grntleriz. Mesaj kutusu Yes, No ve Cancel butonlarna sahiptir. QMessageBox birok standart buton salar ve otomatik olarak butonlardan birini varsaylan buton(default button; kullanc Entera bastnda aktif olan) yapmay, birini de k butonu(escape button; kullanc Esce 49

Qt 4 ile C++ GUI Programlama bastnda aktif olan) yapmay dener. Farkl butonlar varsaylan buton ve k butonu olarak semek ve buton metinlerini deitirmek de mmkndr. warning() ars ilk bakta bir para korkutucu grnebilir, fakat genel szdizimi basittir:
QMessageBox::warning(parent, title, message, buttons);

warning()e ek olarak, QMessageBox her biri farkl simgeye sahip, information(), question() ve critical() fonksiyonlarn salar. Bu simgeler ekil 3.9da gsteriliyor.

ekil 3.9 void MainWindow::open() { if (okToContinue()) { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Spreadsheet"), ".", tr("Spreadsheet files (*.sp)")); if (!fileName.isEmpty()) loadFile(fileName); } }

open() yuvas File > Opena tekabl eder. newFile() gibi, nce kaydedilmemi deiiklik varln denetlemek iin okToContinue() fonksiyonunu arr. Kullancdan yeni dosya ismini elde etmek iin statik uygunluk fonsiyonu QFileDialog::getOpenFileName()i kullanr. Fonksiyon bir dosya diyalounun grntlenmesini salar, kullancya bir dosya semesi imknn verir ve bir dosya ismi ya da eer kullanc Cancela tkladysa, bo bir karakter katar dndrr. QFileDialog::getOpenFileName()in ilk argman ebeveyn paracktr. Diyaloglar iin ebeveynocuk ilikisi, dier paracklarnkiyle ayn anlamda deildir. Bir diyalog her zaman bir penceredir, fakat eer bir ebeveyne sahipse, varsaylan olarak ebeveynin en stnde ortalanr. Bir ocuk diyalog ebeveyniyle ayn grev ubuu(taskbar) girdisini paylar. kinci argman diyaloun baldr. nc argman hangi dizinden balanacan syler, bizim durumumuzda bu, geerli dizindir. Drdnc argman dosya filtreleri belirtir. Bir dosya filtresi, bir tanmlayc metin ve bir zel sembol kalbndan meydana gelir. Spreadsheetin kendi dosya formatna ek olarak, Comma-separated values ve Lotus 1-2-3 dosyalarn da desteklemesini isteseydik, yle bir filtre kullanmamz gerekirdi:
tr("Spreadsheet files (*.sp)\n" "Comma-separated values files (*.csv)\n" "Lotus 1-2-3 files (*.wk1 *.wks)")

open() iinde, dosyalar yklemek iin loadFile() private fonksiyonu arld. Onu bamsz bir fonksiyon yaptk, nk ayn ileve son alan dosyalar yklerken de ihtiyacmz olacak:
bool MainWindow::loadFile(const QString &fileName) {

50

Qt 4 ile C++ GUI Programlama


if (!spreadsheet->readFile(fileName)) { statusBar()->showMessage(tr("Loading canceled"), 2000); return false; } setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); return true; }

Diskten dosya okumak iin Spreadsheet::readFile() kullanrz. Eer ykleme baarlysa, pencere baln gncellemek iin setCurrentFile() arrz; aksi halde, Spreadsheet::readFile() bir mesaj kutusu araclyla kullancya problemi bildirecektir. Her iki durumda da, durum ubuunda, uygulamann ne yapt hakknda kullancy bilgilendirmek iin 2 saniyeliine(2000 milisaniye) bir mesaj grntlenir.
bool MainWindow::save() { if (curFile.isEmpty()) { return saveAs(); } else { return saveFile(curFile); } } bool MainWindow::saveFile(const QString &fileName) { if (!spreadsheet->writeFile(fileName)) { statusBar()->showMessage(tr("Saving canceled"), 2000); return false; } setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); return true; }

save() yuvas File > Savee tekabl eder. Eer dosya zaten bir isme sahipse yani daha nce alm ya da zaten kaydedilmise, save() saveFile() bu isimle arr; aksi halde, saveAs()i arr.
bool MainWindow::saveAs() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save Spreadsheet"), ".", tr("Spreadsheet files (*.sp)")); if (fileName.isEmpty()) return false; return saveFile(fileName); }

saveAs() yuvas File > Save Ase tekabl eder. Kullancdan bir dosya ismi elde etmek iin QFileDialog::getSaveFileName()i arrz. Eer kullanc Cancela tklarsa, false dndrlr. Eer dosya zaten varsa, getSaveFileName() fonksiyonu kullancya zerine yazmak isteyip istemediini soracaktr. Bu davran getSaveFileName()e ek bir argman olarak QFileDialog::DontConfirmOverwrite aktararak deitirilebilir.

51

Qt 4 ile C++ GUI Programlama


void MainWindow::closeEvent(QCloseEvent *event) { if (okToContinue()) { writeSettings(); event->accept(); } else { event->ignore(); } }

Kullanc File > Exita ya da pencerenin balk ubuundaki kapatma butonuna tkladnda QWidget::close() yuvas arlr. Bu, paraca bir kapat(close) olay gnderir. QWidget::closeEvent()i uyarlayarak, ana pencereyi kapatma giriimini nleyebiliriz ve pencereyi gerekten kapatp kapatmayacamz belirleyebiliriz. Eer kaydedilmemi deiiklikler varsa ve kullanc Cancel semise, olay nemsemeyiz(ignore) ve bylece pencere etkilenmez. Normal durumda ise olay stleniriz, sonu olarak Qt pencereyi gizler. Ayrca uygulamann geerli ayarlarn kaydetmek iin writeSettings() private fonksiyonunu arrz. Uygulama, son pencere kapatldnda sonlandrlr. Gerekirse bu davran, QApplicationn quitOnLastWindowClosed niteliini false olarak ayarlayarak devre d brakabiliriz. Bu durumda uygulama biz QApplication::quit()i arana kadar almaya devam eder.
void MainWindow::setCurrentFile(const QString &fileName) { curFile = fileName; setWindowModified(false); QString shownName = tr("Untitled"); if (!curFile.isEmpty()) { shownName = strippedName(curFile); recentFiles.removeAll(curFile); recentFiles.prepend(curFile); updateRecentFileActions(); } setWindowTitle(tr("%1[*] - %2").arg(shownName) .arg(tr("Spreadsheet"))); } QString MainWindow::strippedName(const QString &fullFileName) { return QFileInfo(fullFileName).fileName(); }

setCurrentFile() iinde, curFile private deikenini dzenlenen dosyann adn saklamas iin ayarlarz. Dosya ismini balk ubuunda gstermeden nce, dosyann yolunu strippedName() ile sileriz. Her QWidget, eer pencerenin dokman kaydedilmemi deiikliklere sahipse, true olarak ayarlanmas gereken bir windowModified niteliine sahiptir. Mac OS Xde, kaydedilmemi dokmanlar pencerenin balk ubuundaki kapatma butonu iindeki bir nokta ile, dier platformlarda, dosya ismini takip eden bir asteriks(*) ile belirtilirler. Qt, biz windowModified niteliini gncel tuttuumuz ve **+ iaretini asteriksin grnmesini istediimiz yere yerletirdiimiz srece, otomatik olarak bu davrana bakar. setWindowTitle() fonksiyonuna u metni aktardk:

52

Qt 4 ile C++ GUI Programlama


tr("%1[*] - %2").arg(shownName) .arg(tr("Spreadsheet"))

arg() iki %n parametresiyle kullanlr. arg()n ilk ars %1 yerine, ikinci ar %2 yerine konur. Eer dosya ismi budget.sp ise ve eviri dosyas yklenmemise, sonu karakter katar budget.sp**+ Spreadsheet olmaldr. unu yazmas daha kolay olacaktr:
setWindowTitle(shownName + tr("[*] - Spreadsheet"));

Fakat arg() kullanm evirmenler iin daha fazla esneklik salar. Eer bir dosya ismi varsa, uygulamann son alan dosyalar listesi recentFiles gncelleriz. Kopyalar nlemek amacyla, listede bulunan dosya isimlerini silmek iin removeAll() fonksiyonunu, sonrasnda ilk e olarak dosya ismini eklemek iin prepend()i arrz. Listeyi gncelledikten sonra, File mensndeki girdileri gncellemek iin updateRecentFileActions() private fonksiyonunu arrz.
void MainWindow::updateRecentFileActions() { QMutableStringListIterator i(recentFiles); while (i.hasNext()) { if (!QFile::exists(i.next())) i.remove(); } for (int j = 0; j < MaxRecentFiles; ++j) { if (j < recentFiles.count()) { QString text = tr("&%1 %2") .arg(j + 1) .arg(strippedName(recentFiles[j])); recentFileActions[j]->setText(text); recentFileActions[j]->setData(recentFiles[j]); recentFileActions[j]->setVisible(true); } else { recentFileActions[j]->setVisible(false); } } separatorAction->setVisible(!recentFiles.isEmpty()); }

Daha fazla var olmayacak her dosyay bir Java-tarz yineleyici(Java-style iterator) kullanmak suretiyle silerek balarz. Baz dosyalar nceki bir oturumda kullanlm fakat silinmi olabilir. recentFiles deikeni QStringList(QStringlerin listesi) tipindedir. Sonra tekrar, bu sefer dizi-tarz indeksleme(array-style indexing) kullanarak, dosyalarn bir listesini yaparz. Her bir dosya iin, & iaretinden oluan bir karakter katar olutururuz; bir rakam (j + 1), bir boluk ve dosya ismi (yolu olmadan). Uygun eylemleri bu metinleri kullanarak ayarlarz. rnein, eer ilk dosya C:\My Documents\tab04.sp olsayd, ilk eylemin metni &1 tab04.sp olacakt. ekil 3.10 sonuta oluan meny gsteriyor.

53

Qt 4 ile C++ GUI Programlama

ekil 3.10

Her eylem, QVariant tipinde bir birlemi veri(data) esine sahip olabilir. QVariant tipi birok C++ ve Qt tipinin deerlerini tutabilir. Burada, eylemin veri esinde dosyann tam ismini depolarz, bylece daha sonra kolaylkla eriebiliriz. Ayn zamanda, eylemi grnr yaparz. Eer son alan dosyalardan daha fazla dosya eylemi varsa, ekstra eylemleri aka gizleriz. Son olarak, eer en az bir son alan dosya varsa, ayrcy grnr yaparz.
void MainWindow::openRecentFile() { if (okToContinue()) { QAction *action = qobject_cast<QAction *>(sender()); if (action) loadFile(action->data().toString()); } }

Kullanc bir son alan dosyay setiinde, openRecentFile() yuvas arlr. okToContinue() fonksiyonu, herhangi bir kaydedilmemi deiikliin olduu durumda kullanlr. QObject::sender() kullanarak yuvann hangi eylem tarafndan arldn reniriz. qobject_cast<T>() fonksiyonu, moc (meta object complier; Qtun meta object derleyicisi) tarafndan retilmi meta-information temelli bir dinamik dnm(dynamic cast) yerine getirir. Standart C++ dynamic_cast<T>() fonksiyonuna benzemez. Qtun qobject_cast<T>() fonksiyonu dorudan dinamik ktphane snrlar stnde alr. Bizim rneimizde, qobject_cast<T>() bir QObject iaretisini bir QAction iaretisine dntrmekte kullanrz. Eer dnm baarlysa (ki olmal), eylemin verisinden saladmz tam dosya ismi ile loadFile() arrz. Bu arada, gndericinin(sender) bir QAction olduunu bildiimiz iin, static_cast<T>() ya da geleneksel bir C-tarz dnm(C-style cast) kullansak da program alacaktr.

Diyaloglar Kullanma
Bu ksmda, diyaloglar Qt iinde nasl kullanacamz (onlar oluturmay, ilk kullanma hazrlamay, altrmay ve kullancnn onlarla etkilemesi sonucu ortaya kan seimlere cevap vermeyi) aklayacaz. Blm 2de oluturduumuz Find, Go to Cell ve Sort diyaloglarn kullanacaz. Ayn zamanda bir tane de basit bir About box oluturacaz.

54

Qt 4 ile C++ GUI Programlama ekil 3.11de gsterilen Find diyalou ile balayacaz. Kullancnn ana Spreadsheet penceresi ile Find diyalou arasnda istedii gibi gei yapmasn istediimiz iin, Find diyalou modeless olmaldr. Modeless pencere, uygulama iinde dier pencerelerden bamsz hareket eden bir penceredir.

ekik 3.11

Modeless bir diyalog oluturulduunda, normalde, kullanc etkileimine cevap veren, yuvalara balanm sinyallere sahiptirler.
void MainWindow::find() { if (!findDialog) { findDialog = new FindDialog(this); connect(findDialog, SIGNAL(findNext(const QString &, Qt::CaseSensitivity)), spreadsheet, SLOT(findNext(const QString &, Qt::CaseSensitivity))); connect(findDialog, SIGNAL(findPrevious(const QString &, Qt::CaseSensitivity)), spreadsheet, SLOT(findPrevious(const QString &, Qt::CaseSensitivity))); } findDialog->show(); findDialog->raise(); findDialog->activateWindow(); }

Find diyalou, kullancnn hesap izelgesi iinde metin arayabilecei bir penceredir. Kullanc Edit > Finda tkladnda find() yuvas arlr ve Find diyalou belirir. Bu noktada, birka senaryo mmkndr: Bu, kullancnn Find diyalounu ilk ardr. Find diyalou daha nce arlmtr, fakat kapatlmtr. Find diyalou daha nce arlmtr ve hl aktr. Eer Find diyalou hlihazrda bulunmuyorsa, onu olutururuz ve findNext() ve findPrevious() sinyallerini uygun Spreadsheet yuvalarna balarz. Diyalou MainWindow kurucusu ierisinde oluturabilirdik, fakat onun oluumunu ertelemek uygulama balangcn daha hzl yapar. stelik eer diyalog hi kullanlmazsa, hi oluturulmaz, bylece hem zaman ve hem de bellek tasarrufu salanr. Sonra, pencerenin dierlerinin stnde ve aktif olarak almasn salamak iin show(), raise() ve ativeWindow()u arrz. Bir show() ars tek bana gizlenmi bir pencereyi grnr, en stte ve aktif yapar, fakat Find diyalou zaten grnrken arlm olabilir. Bu durumda, show() hibir ey yapmayacaktr ve bizde pencereyi en stte ve aktif yapmak iin raise() ve activateWindow()u arrz. Bir alternatif yle yazlabilir:
if (findDialog->isHidden()) {

55

Qt 4 ile C++ GUI Programlama


findDialog->show(); } else { findDialog->raise(); findDialog->activateWindow(); }

imdi, ekil 3.12de gsterilen Go to Cell diyalouna bakacaz. Kullancnn, uygulamadaki dier pencerelerle onun arasnda gei yapmasna izin vermeden, onu amasn, kullanmasn ve kapatmasn istiyoruz. Bunun anlam; Go to Cell modal olmaldr. Modal pencere, arldnda beliren ve uygulamay bloke eden, kapanana kadar dier ilemlere veya etkileimlere engel olan bir penceredir. Daha evvel, dosya diyaloglarn ve mesaj kutularn modal olarak kullanmtk.

ekil 3.12

Bir diyalog, eer show() kullanlarak arlyorsa, modelesstr (deilse modal yapmak iin peinen setModal() arrz); eer exec() kullanlarak arlyorsa modaldr.
void MainWindow::goToCell() { GoToCellDialog dialog(this); if (dialog.exec()) { QString str = dialog.lineEdit->text().toUpper(); spreadsheet->setCurrentCell(str.mid(1).toInt() - 1, str[0].unicode() - 'A'); } }

QDialog::exec() fonksiyonu, eer diyalog kabul edilirse bir true deeri (QDialog::Accepted) dndrr, aksi halde bir false deeri (QDialog::Rejected) dndrr. Blm 2de Qt Desginer kullanarak Go to Cell diyalounu oluturduumuzda, OKi accept()e, Cancel da reject()e baladmz hatrlayn. Eer kullanc OKi seerse, geerli hcreyi, satr editr iindeki deere ayarlarz. QTableWidget::setCurrentCell() fonksiyonu iki argman kabul eder: bir satr indeksi ve bir stun indeksi. Spreadsheet uygulamasnda, A1 (0, 0) hcresi, B27 ise (26, 1) hcresidir. QLineEdit::text() tarafndan dndrlen QStringden satr indeksi elde etmek iin, QString::mid()i (balang pozisyonundan karakter katarnn sonuna kadar olan bir alt-karakter katar dndrr) kullanarak satr numarasn ekeriz, QString::toInt()i kullanarak satr numarasn (bir alt-karakter katar) bir tamsayya(integer) dntrrz ve son olarak elde ettiimiz tamsay deerden 1 kartrz. Stun numaras iin, karakter katarnn ilk karakterinin byk harf formunun saysal deerinden A karakterinin saysal deerini kartrz. imdi, Sort diyalouna dnyoruz. Sort diyalou modal bir diyalogdur. ekil 3.13 bir sralama rnei gsterir.

56

Qt 4 ile C++ GUI Programlama

ekil 3.13

Kod Grnm:
void MainWindow::sort() { SortDialog dialog(this); QTableWidgetSelectionRange range = spreadsheet->selectedRange(); dialog.setColumnRange('A' + range.leftColumn(), 'A' + range.rightColumn()); if (dialog.exec()) { SpreadsheetCompare compare; compare.keys[0] = dialog.primaryColumnCombo->currentIndex(); compare.keys[1] = dialog.secondaryColumnCombo->currentIndex() - 1; compare.keys[2] = dialog.tertiaryColumnCombo->currentIndex() - 1; compare.ascending[0] = (dialog.primaryOrderCombo->currentIndex() == 0); compare.ascending[1] = (dialog.secondaryOrderCombo->currentIndex() == 0); compare.ascending[2] = (dialog.tertiaryOrderCombo->currentIndex() == 0); spreadsheet->sort(compare); } }

sort()un kodlar gotoCell() iin kullandmz ayn modeli izler: Yn stnde bir diyalog olutururuz ve ilk kullanma hazrlarz. Diyalou exec() kullanarak aarz. Eer kullanc OKe tklarsa, kullanc tarafndan girilen deerleri alrz. setColumnRange() ars, seilen stunlar sralama iin hazr hale getirir. rnein, ekil 3.13te gsterilen seimi kullanarak, range.leftColumn() 0 (A + 0 = A), range.rightColumn() 2 (A + 2 = C) dndrecektir. compare nesnesi birincil, ikincil ve ncl sralama anahtarlarn ve onlarn sralama dzenini depolar. (SpreadsheetCompare snfnn tanmn bir sonraki blmde greceiz.) Nesne Spreadsheet::sort() tarafndan iki satr karlatrmak iin kullanlr. keys dizisi anahtarlarn stun numaralarn depolar. rnein, seim C2den E5e kadarsa, C stunu 0 konumuna sahip olur. ascending dizisi dzenle ilikili her anahtar bir bool deer olarak tutar. QComboBox::currentIndex() o anda seilmi olan enin indeksini -0dan balayarak- dndrr. kincil ve ncl anahtarlar iin, geerli eden (None esi nedeniyle) 1 kartrz. 57

Qt 4 ile C++ GUI Programlama sort() fonksiyonu ii yapar, fakat biraz krlgandr. Sort diyalounun belirli bir yoldan (Combo Boxlar ve None eleriyle) gerekletiriliini stlenir. Bunun anlam; eer Sort diyalounu yeniden tasarlarsanz, kodlar yeniden yazmanz da gerekebilir. Bu yaklam, bir diyalog iin eer sadece bir yerden arlyorsa elverilidir. Daha salkl bir yaklam, bir SpreadsheetCompare nesnesi oluturarak SortDialog snfn daha akll yapmaktr. Bu, MainWindow::sort()u nemli derecede yalnlatracaktr:
void MainWindow::sort() { SortDialog dialog(this); QTableWidgetSelectionRange range = spreadsheet->selectedRange(); dialog.setColumnRange('A' + range.leftColumn(), 'A' + range.rightColumn()); if (dialog.exec()) spreadsheet->performSort(dialog.comparisonObject()); }

Bu yaklam gevek bal bileenlere yol aar ve hemen hemen her zaman birden fazla yerden arlacak diyaloglar iin en doru seimdir. Daha radikal bir yaklam ise, Spreadsheet nesnesine SortDialog nesnesi balatldnda bir iareti aktarmak ve bylece diyaloun Spreadsheet zerinde direkt olarak kullanlmasna imkn vermek olacaktr. Bu, sadece paracn belli bir rnei zerinde alld iin SortDialogu daha az genel yapar, hatta SortDialog::setColumnRange() fonksiyonunu ortadan kaldrarak kodu basitletirir. Bylece MainWindow::sort() u hali alr:
void MainWindow::sort() { SortDialog dialog(this); dialog.setSpreadsheet(spreadsheet); dialog.exec(); }

Bu yaklam ilk olarak unu salar: arann, diyaloun zel bilgilerine ihtiya duymas yerine; diyalog, aran tarafndan salanan veri yaplarnn bilgisine ihtiya duyar. Bu yaklam, diyaloun, deiiklikleri canl canl uygulamas gerektiinde kullan olabilir. Fakat ilk yaklamn kullanlmasnda karlatmz krlganlk gibi; bu nc yaklam da, eer veri yaplar deiirse krlr. Baz gelitiriciler, diyaloglar iin sadece bir yaklam seerler ve ona sadk kalrlar. Bu, tm diyaloglarnda ayn modeli takip ettikleri iin, yatknlk ve kolaylk avantajlarna sahiptir, fakat kullanlmayan yaklamlarn avantajlarn karr. deal olan ise, kullanlan yaklama, diyaloa gre karar verilmesidir. Bu ksm Hakknda kutusu(About box) ile toparlayacaz. Uygulama hakkndaki bilgileri sunmak iin Find veya Go to Cell diyaloglarn yaptmz gibi yine zel bir diyalog oluturabilirdik, fakat Hakknda kutular sklkla kullanldklar iin, Qt daha basit bir zm sunar.
void MainWindow::about() { QMessageBox::about(this, tr("About Spreadsheet"), tr("<h2>Spreadsheet 1.1</h2>" "<p>Copyright &copy; 2008 Software Inc."

58

Qt 4 ile C++ GUI Programlama


"<p>Spreadsheet is a small application that " "demonstrates QAction, QMainWindow, QMenuBar, " "QStatusBar, QTableWidget, QToolBar, and many other " "Qt classes.")); }

Hakknda kutusu, bir statik uygunluk fonksiyonu QMessageBox::about() arlarak salanr. Fonksiyon, QMessageBox::warning()in ebeveyn pencerenin simgesi olarak standart warning simgesini kullanmasnn dnda, QMessageBox::warning() ile ok benzerdir. Sonuta elde ettiimiz diyalog ekil 3.14te gsteriliyor.

ekil 3.14

imdiye kadar, QMessageBox ve QFileDialogtan birka statik uygunluk fonksiyonu kullandk. Bu fonksiyonlar bir diyalog oluturur, onu ilk kullanma hazrlar ve exec() ile arr. Daha az pratik olsa da, tpk dier paracklar gibi bir QMessageBox ya da bir QFileDialog oluturmak ve aka exec() ile ve hatta show() ile armak da mmkndr.

Uygulama Ayarlarn Saklama


MainWindow kurucusu iinde, uygulamann depolanm ayarlarn yklemek iin readSettings()i ardk. Benzer ekilde, closeEvent() iinde, ayarlar kaydetmek iin writeSettings()i ardk. Bu iki fonksiyon, gerekletirilmesi gereken son MainWindow ye fonksiyonlardr.
void MainWindow::writeSettings() { QSettings settings("Software Inc.", "Spreadsheet"); settings.setValue("geometry", saveGeometry()); settings.setValue("recentFiles", recentFiles); settings.setValue("showGrid", showGridAction->isChecked()); settings.setValue("autoRecalc", autoRecalcAction->isChecked()); }

writeSettings() fonksiyonu ana pencerenin geometrisini (konum ve boyut), son alan dosyalar listesini ve Show-Grid ve Auto-Recalculate seeneklerinin durumunu kaydeder. Varsaylan olarak, QSettings uygulama ayarlarn platforma zg yerlerde saklar. Windowsta sistem kayt defterini(system registry) kullanr; Unixte veriyi metin dosyalarnda saklar; Mac OS Xte Core Foundation Preferences APIyi kullanr. Kurucu argmanlar organizasyonun ismini ve uygulamann ismini belirtir. Bu bilgi -platforma zg bir yollaayarlar iin bir yer bulmada kullanlr.

59

Qt 4 ile C++ GUI Programlama QSettings, ayarlar anahtar-deer(key-value) iftleri halinde saklar. Anahtar bir dosyann sistem yoluna benzer. Altanahtarlar(subkeys), yol belirtmeye benzer bir szdizimiyle (findDialog/matchCase gibi) ya da beginGroup() ve endGroup() kullanlarak belirtilebilirler.
settings.beginGroup("findDialog"); settings.setValue("matchCase", caseCheckBox->isChecked()); settings.setValue("searchBackward", backwardCheckBox->isChecked()); settings.endGroup();

Deer; bir int, bir bool, bir double, bir QString, bir QStringList ya da QVariantn destekledii herhangi bir trde olabilir.
void MainWindow::readSettings() { QSettings settings("Software Inc.", "Spreadsheet"); restoreGeometry(settings.value("geometry").toByteArray()); recentFiles = settings.value("recentFiles").toStringList(); updateRecentFileActions(); bool showGrid = settings.value("showGrid", true).toBool(); showGridAction->setChecked(showGrid); bool autoRecalc = settings.value("autoRecalc", true).toBool(); autoRecalcAction->setChecked(autoRecalc); }

readSettings() fonksiyonu writeSettings() tarafndan kaydedilen ayarlar ykler. value() fonksiyonunun ikinci argman, mevcut ayar olmad durumda varsaylan bir deer belirtir. Varsaylan deerler uygulama ilk altnda kullanlrlar. Geometri ya da son alan dosyalar listesi iin verilen ikinci argman olmad iin pencere keyfi fakat mantkl bir boyuta ve konuma sahip olacaktr, ve son alan dosyalar listesi ilk almada bo bir liste olacaktr. MainWindowda, readSettings() ve writeSettings() iindeki QSettings ilikili tm kodlarla tercih ettiimiz bu dzenleme yaklam, birok olas yaklamdan yalnzca biridir. Bir QSettings nesnesi, uygulamann almas srasnda, herhangi bir zamanda ve kodun herhangi bir yerinde, baz ayarlar sorgulamak ve deitirmek iin de oluturulabilir. Bylece MainWindowun gerekletirimini tamamladk. leriki ksmlarda, Spreadsheet uygulamasnn oklu dokman(multiple document) ileyebilmesini ve bir al ekran(splash screen) gerekletirmeyi ele alacaz. leriki blmde de ilevlerini tamamlayacaz.

oklu Dokman leme


Artk, Spreadsheet uygulamasnn main() fonksiyonunu kodlamak iin hazrz:
#include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow mainWin;

60

Qt 4 ile C++ GUI Programlama


mainWin.show(); return app.exec(); }

Bu main() fonksiyon imdiye kadar yazdklarmzdan biraz farkldr: MainWindow rneini new kullanmak yerine, yndaki bir deikenmi gibi oluturduk. MainWindow rnei fonksiyon sona erdirildiinde otomatik olarak yok edilecektir. main() fonksiyon ile gsterildii gibi, Spreadsheet uygulamas bir tek ana pencere salar ve ayn zamanda yalnzca bir dokman ileyebilir. Eer ayn anda birden fazla dokman dzenlemek istersek, Spreadsheet uygulamasnn oklu rneklerini balatabiliriz. Fakat bu, kullanclar iin -uygulamann tek bir rneiyle birok ana pencere salayabiliyorken- uygun deildir. Spreadsheet uygulamasn, oklu dokmanlar ileyebilmesi iin deitireceiz. ncelikle, biraz farkl bir File mensne ihtiyacmz var: File > New, var olan ana pencereyi kullanmak yerine, bo bir dokmanla yeni bir ana pencere oluturur. File > Close geerli ana pencereyi kapatr. File > Exit tm pencereleri kapatr. File mensnn orijinal versiyonunda, Close seenei yoktu nk Exit ile ayn olurdu. Yeni File mens ekil 3.15te gsteriliyor.

ekil 3.15

Bu da yeni main() fonksiyonudur:


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

oklu pencerelerle, new ile MainWindow oluturmak mantkldr, nk o zaman bir ana pencereyi delete kullanarak yok ederek bellek kurtarabiliriz. Bu da yeni MainWindow::newFile() yuvas:
void MainWindow::newFile() { MainWindow *mainWin = new MainWindow; mainWin->show();

61

Qt 4 ile C++ GUI Programlama


}

Basite yeni bir MainWindow rnei olutururuz. Yeni pencereyi herhangi bir iaretide tutmamamz tuhaf grnebilir, fakat Qt tm pencerelerin izini bizim iin tuttuundan, bir sorun oluturmaz. Bunlar da Close ve Exit iin eylemlerdir:
void MainWindow::createActions() { ... closeAction = new QAction(tr("&Close"), this); closeAction->setShortcut(QKeySequence::Close); closeAction->setStatusTip(tr("Close this window")); connect(closeAction, SIGNAL(triggered()), this, SLOT(close())); exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(tr("Ctrl+Q")); exitAction->setStatusTip(tr("Exit the application")); connect(exitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); ... }

QApplication::closeAllWindows() yuvas uygulamann tm pencerelerini kapatr, ancak bir tanesi kapat olayn(close event) reddeder. Burada bu davrana kesinlikle ihtiyacmz vardr. Kaydedilmemi deiiklikler iin endielenmemiz gerekmez, nk ne zaman bir pencere kapansa MainWindow::closeEvent() iletilir. Sanki uygulamann oklu pencereleri ileme yeteneini salamz gibi grnyor. Ama ne yazk ki, gizli bir problem vardr: Eer kullanc ana pencereleri oluturup kapatmay srdrrse, sonunda makinenin bellei tkenebilir. nk newFile()da MainWindow paracklar oluturmay srdryoruz fakat onlar hi silmiyoruz. Kullanc bir ana pencere kapattnda, varsaylan davran onu gizlemektir, bu nedenle yine de bellekte durur. Birok ana pencereyle bu bir problem olabilir. zm, kurucuda Qt::WA_DeleteOnClose niteliini ayarlamaktr:
MainWindow::MainWindow() { ... setAttribute(Qt::WA_DeleteOnClose); ... }

Bu, Qta, pencere kapandnda onu silmesini syler. Qt::WA_DeleteOnClose nitelii, bir QWidgetn davrann etkilemek iin ayarlanabilen birok bayraktan(flag) biridir. Bellek sznts ilgilenmemiz gereken tek sorun deildir. Orijinal uygulamamzn tasarmnda yalnzca bir ana pencereye sahip olacaktk. oklu pencerelerle birlikte, her bir pencere kendi son alan dosyalar listesine ve kendi seeneklerine sahip olur. Dolaysyla, son alan dosyalar listesi tm uygulama iin global olmaldr. Bunu, recentFiles deikenini statik olarak bildirerek baarabiliriz. Bylece, tm uygulama iin tek bir recentFiles rnei olmu olur. Fakat sonra, File mensn gncellemek iin ardmz updateRecentFileActions() tm ana pencerelerden arlmasn salamalyz. Bunu salayan kod u ekildedir: 62

Qt 4 ile C++ GUI Programlama


foreach (QWidget *win, QApplication::topLevelWidgets()) { if (MainWindow *mainWin = qobject_cast<MainWindow *>(win)) mainWin->updateRecentFileActions(); }

Kod, uygulamann tm pencerelerini yineleyerek MainWindow tipindeki tm paracklar iin updateRecentFileActions() armak iin Qtun foreach yapsn kullanr. Benzer bir kod, Show Grid ve Auto-Recalculate seeneklerini ezamanl yapmak iin ya da ayn dosyann iki kere yklenmediinden emin olmak iin de kullanlabilir. Ana pencere bana bir dokman salayan uygulamalara SDI(single document interface) uygulamalar denir. Windows zerindeki yaygn bir alternatifi olan MDI(multiple document interface), orta blgesinde oklu dokman pencerelerinin ynetildii tek bir ana pencereye sahiptir. Qt, destekleyen tm platformlarda, hem SDI hem de MDI uygulamalar oluturmak iin kullanlabilir.

ekil 3.16

Al Ekranlar
Uygulamalarn birou balangta ekil 3.17deki gibi bir al ekran(splash screen) sunar. Baz gelitiriciler yava bir balangc gizlemek iin bir al ekran kullanrken, bazlar da bunu pazarlama departmanlarn memnun etmek iin yaparlar. Qt uygulamalarna QSplashScreen snfn kullanarak bir al ekran eklemek ok kolaydr.

ekil 3.17

QSplashScreen snf, ana pencere grnmeden nce bir resim gsterir. Uygulamann balatlma srecinin ilerlemesi hakknda kullancy bilgilendirmek iin resim stne mesajlar da yazabilir. Genelde, al ekran kodu main() iinde yer alr(QApplication::exec() arsndan nce). 63

Qt 4 ile C++ GUI Programlama QSplashScreen snfn kullanarak kullancya, balangta modlleri ykleyen ve a balantlar kuran bir al ekran salayan rnek main() fonksiyonunun kodlar u ekildedir:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QSplashScreen *splash = new QSplashScreen; splash->setPixmap(QPixmap(":/images/splash.png")); splash->show(); Qt::Alignment topRight = Qt::AlignRight | Qt::AlignTop; splash->showMessage(QObject::tr("Setting up the main window..."), topRight, Qt::white); MainWindow mainWin; splash->showMessage(QObject::tr("Loading modules..."), topRight, Qt::white); loadModules(); splash->showMessage(QObject::tr("Establishing connections..."), topRight, Qt::white); establishConnections(); mainWin.show(); splash->finish(&mainWin); delete splash; return app.exec(); }

Bylelikle Spreadsheet uygulamasnn kullanc arayzn tamamlam olduk. Sonraki blmde, ekirdek hesap izelgesi ilevlerini gerekletirerek uygulamay tamamlayacaz.

64

Qt 4 ile C++ GUI Programlama

BLM 4: UYGULAMA LEVLERN GEREKLETRME

nceki iki blmde, Spreadsheet uygulamasnn arayznn nasl oluturulduunu akladk. Bu blmde, temel ilevleri kodlayarak program tamamlayacaz. Dier eylerin yan sra, dosyalar yklemeyi ve kaydetmeyi, veriyi bellekte saklamay, pano(clipboard) ilemlerini gerekletirmeyi ve QTableWigdetn hesap izelgesi formlleri iin destek vermesini salamay greceiz.

Merkez Parack
Bir QMainWindowun merkez alan her trden parack tarafndan tutulabilir. te tm bu olaslklara genel bir bak: 1. Standart bir Qt parac kullanmak QTableWidget veya QTextEdit gibi standart bir parack merkez parack olarak kullanlabilir. Bu durumda, dosyalar ykleme ve kaydetme gibi uygulama ilevleri baka bir yerde gerekletirilmelidir (rnein bir QMainWindow altsnfnda). 2. zel bir parack kullanmak zel amal uygulamalarda, genelde veriyi zel bir parackta gstermek gerekir. rnein, bir simge editr(icon editor) programnda merkez parack bir IconEditor parac olacaktr. Qtda zel paracklarn nasl oluturulduu Blm 5te aklanmaktadr. 3. Bir yerleim yneticisiyle sade bir QWidget kullanmak Bazen, uygulamann merkez alan birok parack tarafndan tutulur. Bu, bir QWidget dier paracklarn ebeveyni olarak kullanarak yaplabilir. Yerleim yneticisiyle de ocuk paracklarn boyutlar ve konumlar ayarlanr. 4. Bir blc(splitter) kullanmak oklu paracklar birlikte kullanmann bir dier yolu da bir QSplitter kullanmaktr. QSplitter ocuk paracklar dikey ya da yatay olarak dzenler. Ayrca, blc kullanmak kullancya bir miktar boyutlandrma kontrol verir. Blcler, dier blclerde dhil olmak zere her trl parac ierebilirler. 5. MDI alan kullanmak Eer uygulama MDI kullanrsa, merkez alan bir QMdiArea parac tarafndan tutulur ve her MDI penceresi bu paracn bir ocuudur. Yerleimler, blcler ve MDI alanlar standart Qt paracklar ya da zel paracklarla birlikte olabilirler. Spreadsheet uygulamas iin, bir QTableWidget altsnf merkez parack olarak kullanlabilir. QTableWidget snf zaten hesap izelgesi yeteneklerinin birounu salar, fakat pano ilemlerini desteklemez ve =A1+A2+A3 gibi hesap izelgesi formllerinden anlamaz. Bu eksik ilevleri Spreadsheet snf ierisinde gerekletireceiz. 65

Qt 4 ile C++ GUI Programlama

QTableWidget Altsnf Tretme


Spreadsheet snf, ekil 4.1de grld gibi QTableWidgettan tretilmitir. Kullanc bo bir hcreye metin girdiinde, QTableWidget metni saklamak iin otomatik olarak bir QTableWidgetItem oluturur.

ekil 4.1

QTablewWidget, model/grn(model/view) snflarndan biri olan QTableViewden tretilmitir. Balk dosyasndan balayarak Spreadsheeti gerekletirmeye balayalm:
#ifndef SPREADSHEET_H #define SPREADSHEET_H #include <QTableWidget> class Cell; class SpreadsheetCompare;

Balk dosyas Cell ve SpreadsheetCompare snflarnn n bildirimleriyle balar. Bir QTableWidget hcresinin nitelikleri (metni, hizalan vb.) bir QTableWidgetItem iinde saklanr. QTableWidgettan farkl olarak, QTableWidgetItem bir parack snf deil, bir veri snfdr. Cell snf QTableWidgetItemdan tretilir.
class Spreadsheet : public QTableWidget { Q_OBJECT public: Spreadsheet(QWidget *parent = 0); bool autoRecalculate() const { return autoRecalc; } QString currentLocation() const; QString currentFormula() const; QTableWidgetSelectionRange selectedRange() const; void clear(); bool readFile(const QString &fileName); bool writeFile(const QString &fileName); void sort(const SpreadsheetCompare &compare);

autoRecalculate() fonksiyonu yalnzca otomatik hesaplamann(auto-recalculation) geerliliini dndrd iin satr ii(inline) gerekletirilmitir. Blm 3te, MainWindowu gerekletirirken Spreadsheet iindeki baz public fonksiyonlara gvenmitik. rnein, MainWindow::newFile()dan, hesap izelgesini sfrlamak iin clear()

66

Qt 4 ile C++ GUI Programlama fonksiyonunu ardk. QTableWidgettan miras alnan baz fonksiyonlar da kullandk, bilhassa setCurrrentCell() ve setShowGrid()i.
public slots: void cut(); void copy(); void paste(); void del(); void selectCurrentRow(); void selectCurrentColumn(); void recalculate(); void setAutoRecalculate(bool recalc); void findNext(const QString &str, Qt::CaseSensitivity cs); void findPrevious(const QString &str, Qt::CaseSensitivity cs); signals: void modified();

Spreadsheet, Edit, Tools ve Options menlerinin eylemlerini gerekletiren bir ok yuva, ve bir de herhangi bir deiiklik meydana geldiini bildiren modified() sinyalini salar.
private slots: void somethingChanged();

Spreadsheet snf tarafndan dhilen kullanlan bir private yuva tanmlarz.


private: enum { MagicNumber = 0x7F51C883, RowCount = 999, ColumnCount = 26 }; Cell *cell(int row, int column) const; QString text(int row, int column) const; QString formula(int row, int column) const; void setFormula(int row, int column, const QString &formula); bool autoRecalc; };

Snfn private ksmnda, sabit, drt fonksiyon ve bir deiken bildiririz.


class SpreadsheetCompare { public: bool operator()(const QStringList &row1, const QStringList &row2) const; enum { KeyCount = 3 }; int keys[KeyCount]; bool ascending[KeyCount]; }; #endif

Balk dosyas SpreadsheetCompare snfnn tanmyla biter. Bunu Spreadsheet::sort()un kritiini yaparken aklayacaz. imdi gerekletirime bakacaz:
#include <QtGui>

67

Qt 4 ile C++ GUI Programlama

#include "cell.h" #include "spreadsheet.h" Spreadsheet::Spreadsheet(QWidget *parent) : QTableWidget(parent) { autoRecalc = true; setItemPrototype(new Cell); setSelectionMode(ContiguousSelection); connect(this, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(somethingChanged())); clear(); }

Normal olarak, kullanc bo bir hcreye metin girdiinde, QTableWidget metni tutmak iin otomatik olarak bir QTableWidgetItem oluturur. Bizim hesap izelgemizde, onun yerine Cell esinin oluturulmasn istiyoruz. Bu, kurucu iinde setItemPrototype() arlarak gerekletirilir. Dhilen, QTableWidget, ilk rnek(prototype) olarak aktarlan eyi, yeni bir e gerektiinde klonlar. Kurucuda ayrca, seim modunu(selection mode) tek bir dikdrtgen semeye izin veren QAbstractItemView::ContiguousSelection olarak ayarlarz. Tablo paracnn itemChanged() sinyalini private somethingChanged() yuvasna balarz; bu, kullanc bir hcreyi dzenlediinde somethinChanged() yuvasnn arlmasn salar. Son olarak, tabloyu yeniden boyutlandrmak ve stun yksekliklerini ayarlamak iin clear() arrz.
void Spreadsheet::clear() { setRowCount(0); setColumnCount(0); setRowCount(RowCount); setColumnCount(ColumnCount); for (int i = 0; i < ColumnCount; ++i) { QTableWidgetItem *item = new QTableWidgetItem; item->setText(QString(QChar('A' + i))); setHorizontalHeaderItem(i, item); } setCurrentCell(0, 0); }

clear() fonksiyonu, hesap izelgesini sfrlamak iin Spreadsheet kurucusundan arlr. Ayrca MainWindow::newFile()dan da arlr. Tm seimleri ve eleri temizlemek iin QTableWidget::clear() kullanabilirdik, fakat bu, balklar(header) geerli boyutlarnda brakacakt. Bunun yerine, tabloyu 0 x 0a yeniden boyutlandrrz. Bu, balklar da dhil tm hesap izelgesini temizler. Ondan sonra, tabloyu ColumnCount x RowCount(26 x 999)a yeniden boyutlandrrz ve yatay balklar, QTableWidgetItemlarn ierdii stun isimleri A, B, , Z ile doldururuz. Dikey balk etiketlerini ayarlamamza gerek yoktur, nk varsaylanlar 1, 2, , 999dur. En sonunda hcre gstergesini A1 hcresine tarz.

68

Qt 4 ile C++ GUI Programlama Bir QTableWidget birka ocuk paracktan ibarettir. En stte yatay bir QHeaderViewe, solda dikey bir QHeaderViewe ve iki de QScrollBara sahiptir. Ortadaki alan, QTableWidgetn zerine hcreleri izdii grnt kaps(viewport) adnda zel bir parack tarafndan tutulur. Farkl ocuk paracklar QTableWidget ve QAbstractScrollAreadan miras alnan fonksiyonlara direkt olarak eriebilirler (ekil 4.2). QAbstractScrollArea, alp kapatlabilen, kaydrlabilir bir grnt kaps ve iki kaydrma ubuu salar.

ekil 4.2 Cell *Spreadsheet::cell(int row, int column) const { return static_cast<Cell *>(item(row, column)); }

cell() private fonksiyonu, belli bir satr ve stun iin bir Cell nesnesi dndrr. Bir QTableWidgetItem iaretisi yerine bir Cell iaretisi dndrmesi dnda, hemen hemen QTableWidget::item() ile ayndr.
QString Spreadsheet::text(int row, int column) const { Cell *c = cell(row, column); if (c) { return c->text(); } else { return ""; } }

text() private fonksiyonu, belli bir hcre iin bir metin dndrr. Eer cell() bo bir iareti dndrrse, hcre botur, dolaysyla bo bir karakter katar dndrrz.
QString Spreadsheet::formula(int row, int column) const { Cell *c = cell(row, column); if (c) { return c->formula(); } else { return ""; } }

formula() fonksiyonu hcrenin formln dndrr. Birok durumda, forml ve metin ayndr; rnein, Hello forml Hello karakter katarna eittir, bu nedenle eer kullanc hcre iine Hello girerse ve Entera basarsa, hcre Hello metinini gsterir. Fakat birka istisna vardr:

69

Qt 4 ile C++ GUI Programlama Eer forml bir say ise, yle yorumlanr. rnein, 1.50 forml 1.5 double deerine eittir ve hesap izelgesinde saa yasl duruma getirilir. Eer forml tek trnakla balyorsa, formln geri kalan metin olarak yorumlanr. rnein, 12345 forml 12345 karakter katarna eittir. Eer forml eittir iaretiyle(=) balyorsa, forml aritmetik bir forml olarak yorumlanr. rnein, eer A1 hcresinin ierii 12 ve A2 hcresinin ierii 6 ise, =A1+A2 formlnn sonucu 18e eittir. Bir forml bir deer dntrme grevi Cell snf tarafndan yerine getirilir. uan iin aklda kalmas gereken ey, hcrede gsterilen metnin formln kendisi deil, formln sonucu olduudur.
void Spreadsheet::setFormula(int row, int column, const QString &formula) { Cell *c = cell(row, column); if (!c) { c = new Cell; setItem(row, column, c); } c->setFormula(formula); }

setFormula() private fonksiyonu belli bir hcre iin forml ayarlar. Eer hcre zaten bir Cell nesnesine sahipse, onu yeniden kullanrz. Aksi halde, yeni bir Cell nesnesi olutururuz ve tabloya eklemek iin QTableWidget::setItem() arrz. Sonda, eer hcre ekranda gsterilirse, hcrenin yeniden izilmesine neden olacak olan setFormula() fonksiyonunu arrz. Cell nesnesinin daha sonra silinmesi hakknda endielenmemiz gereksizdir; QTableWidget hcrenin sahipliini stlenir ve doru zamanda hcreyi otomatik olarak siler.
QString Spreadsheet::currentLocation() const { return QChar('A' + currentColumn()) + QString::number(currentRow() + 1); }

currentLocation() fonksiyonu, hesap izelgesinin allm formatna uygun bir ekilde (stun harfini takiben satr numaras) geerli hcre konumunu dndrr. Dnen deer, MainWindow::updateStatusBar() tarafndan, konumu durum ubuunda gstermede kullanlr.
QString Spreadsheet::currentFormula() const { return formula(currentRow(), currentColumn()); }

currentFormula() fonksiyonu, geerli MainWindow::updateStatusBar()dan arlr.


void Spreadsheet::somethingChanged() { if (autoRecalc) recalculate(); emit modified(); }

hcre

formln

dndrr.

70

Qt 4 ile C++ GUI Programlama somethingChanged() private yuvas, eer otomatik hesaplama etkinse, tm hesap izelgesini yeniden hesaplar. Ayrca modified() sinyalini yayar.

Ykleme ve Kaydetme
Artk, bir zel ikili(binary) format kullanarak, Spreadsheet dosyalarn ykleme ve kaydetmeyi gerekletireceiz. Bunu, birlikte platformdan bamsz ikili I/O salayan QFile ve QDataStream snflarn kullanarak yapacaz. Bir Spreadsheet dosyas yazmakla balayacaz: Kod Grnm:
bool Spreadsheet::writeFile(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { QMessageBox::warning(this, tr("Spreadsheet"), tr("Cannot write file %1:\n%2.") .arg(file.fileName()) .arg(file.errorString())); return false; } QDataStream out(&file); out.setVersion(QDataStream::Qt_4_3); out << quint32(MagicNumber); QApplication::setOverrideCursor(Qt::WaitCursor); for (int row = 0; row < RowCount; ++row) { for (int column = 0; column < ColumnCount; ++column) { QString str = formula(row, column); if (!str.isEmpty()) out << quint16(row) << quint16(column) << str; } } QApplication::restoreOverrideCursor(); return true; }

writeFile() fonksiyonu, dosyay diske yazmak iin MainWindow::saveFile()dan arlr. Baarlysa true, hatalysa false dndrr. Verilen dosya ismi ile bir QFile nesnesi olutururuz ve yazmak iin dosyay aarz. Ayrca QFile stnde ilemler yapan bir QDataStream nesnesi olutururuz ve onu veri yazmada kullanrz. Veri yazmadan hemen nce, uygulamann imlecini(cursor) standart bekleme imleciyle (genellikle bir kum saatidir) deitiririz ve tm veri yazldnda da normal imlece geri dneriz. QDataStream, Qt tiplerinin birounu destekledii gibi temel C++ tiplerini de destekler. Szdiziminde Standart C++ <iostream> snf rnek alnmtr. rnein,
out << x << y << z;

x, y ve z deikenlerini bir aka(stream) yazarken, 71

Qt 4 ile C++ GUI Programlama


in >> x >> y >> z;

bir aktan onlar okur. nk C++ ilkel tamsay(integer) tipleri farkl platformlarda farkl boyutlarda olabilirler, en salam bu deerleri, boyutlarnn tannabilir olmasn garanti eden qint8, quint8, qint16, quint16, qint32, quint32, qint64 ve quint64den birine dntrmektir. Spreadsheet uygulamasnn dosya format olduka basittir. Bir Spreadsheet dosyas dosya formatn tanmlayan 32-bitlik bir say ile balar(MagicNumber, spreadsheet.h iinde 0x7F51C883 olarak tanmlanan, istee bal rastgele bir saydr). Sonrasnda her bir hcrenin satrn, stununu ve formln ieren bloklarn bir serisi gelir. Boluklar kaydetmek iin bo hcreleri yazmayz. Format ekil 4.3te gsteriliyor.

ekil 4.3

Veri tiplerinin ikili gsteriminin ak hali QDataStream tarafndan belirlenir. rnein, bir qint16 2 byte olarak big-endian dzende, bir QString uzunluunca Unicode karakteri olarak saklanr. Qt tiplerinin ikili gsterimi, Qt 1.0dan buyana olduka gelitirildi. Gelecekteki Qt datmlarnda da gelitirilmeye devam edilecee benziyor. Varsaylan olarak, QDataStream ikili formatn en son versiyonunu kullanr (Qt 4.3te versiyon 9), fakat eski versiyonlar okumak iin de ayarlanabilir. Uygulamay daha sonra daha yeni bir Qt datm kullanlarak yeniden derlenebileceini dnerek, herhangi bir uyumluluk problemini nlemek iin, QDataStreame aka, Qtun versiyonuna aldrmadan versiyon 9u kullanmasn szleriz. (QDataStream::Qt_4_3 9a eit olan uygunluk sabitidir.) QDataStream ok ynldr. Bir QFileda, bir QBufferda, bir QProcesste, bir QTcpSockette, bir QUdpSockette ya da bir QSslSockette kullanlabilir. Qt ayrca, metin dosyalarn okumak ve yazmak iin QDataStream yerine kullanlabilecek bir QTextStream snf sunar. Kod Grnm:
bool Spreadsheet::readFile(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr("Spreadsheet"), tr("Cannot read file %1:\n%2.") .arg(file.fileName()) .arg(file.errorString())); return false; } QDataStream in(&file); in.setVersion(QDataStream::Qt_4_3); quint32 magic; in >> magic; if (magic != MagicNumber) { QMessageBox::warning(this, tr("Spreadsheet"), tr("The file is not a Spreadsheet file.")); return false; }

72

Qt 4 ile C++ GUI Programlama


clear(); quint16 row; quint16 column; QString str; QApplication::setOverrideCursor(Qt::WaitCursor); while (!in.atEnd()) { in >> row >> column >> str; setFormula(row, column, str); } QApplication::restoreOverrideCursor(); return true; }

readFile() fonksiyonu writeFile() ile ok benzerdir. Dosyadan okumak iin QFile kullanrz,fakat bu sefer QIODevice::WriteOnlyyi kullanmaktansa, QIODevice::ReadOnly bayran kullanrz. Sonra, QDataStream versiyonunu 9a ayarlarz. Okuma format her zaman yazma ile ayn olmaldr. Eer dosya, balangcnda doru sihirli sayya(magic number) sahipse, hesap izelgesindeki tm hcreleri silmek iin clear() arrz ve sonrasnda hcre verisini okuruz. Dosya sadece bo olmayan hcrelerin verisini ierdii iin, okumadan tm hcrelerin temizlenmi olmasn salamak zorundayz.

Edit Mensn Gerekletirme


Artk, uygulamann Edit mensne ilikin yuvalar gerekletirmeye hazrz. Men ekil 4.4te gsteriliyor.

ekil 4.4 void Spreadsheet::cut() { copy(); del(); }

cut() yuvas Edit > Cuta tekabl eder. Cut, Copy ve onu takip eden bir Delete ilemi olduu iin gerekletirimi basittir.
void Spreadsheet::copy() { QTableWidgetSelectionRange range = selectedRange(); QString str; for (int i = 0; i < range.rowCount(); ++i) { if (i > 0) str += "\n"; for (int j = 0; j < range.columnCount(); ++j) {

73

Qt 4 ile C++ GUI Programlama


if (j > 0) str += "\t"; str += formula(range.topRow() + i, range.leftColumn() + j); } } QApplication::clipboard()->setText(str); }

copy() yuvas Edit > Copyye tekabl eder. Geerli seimi yineler. Her bir seili hcrenin forml bir QStringe eklenir, satrlar newline(\n) karakteri kullanlarak, stunlar tab(\t) karakteri kullanlarak ayrlrlar. Bu, ekil 4.5te bir rnekle aklanmtr.

ekil 4.5

Sistem panosu Qtda QApplication::clipboard() statik fonksiyonu sayesinde kolaylkla kullanlabilinir. QClipboard::setText()i ararak, bu uygulama ve dier uygulamalarn destekledii dz metni pano zerinde kullanlabilir yaparz. Formatmz, ayrc olarak kullanlan tab ve newline karakterleri sayesinde, Microsoft Excelde dhil olmak zere eitli uygulamalar tarafndan anlalrdr. QTableWidget::selectedRanges() fonksiyonu seim aralklarnn bir listesini dndrr. Birden fazla olamayacan biliyoruz nk kurucuda seim modunu QAbstractItemView::ContiguousSelection olarak ayarlamtk. Kolaylk salamas iin, seim araln dndren bir selectedRange() fonksiyonu tanmlarz.
QTableWidgetSelectionRange Spreadsheet::selectedRange() const { QList<QTableWidgetSelectionRange> ranges = selectedRanges(); if (ranges.isEmpty()) return QTableWidgetSelectionRange(); return ranges.first(); }

Eer sadece bir seim varsa, aka ve sadece ilkini dndrrz. ContiguousSelection modu geerli hcreye seilmi muamelesi yapt iin her zaman bir seim vardr. Fakat hibir hcrenin geerli olmamas gibi bir hata olmas olaslna kar korunmak iin, bu durumunda stesinden geliriz. Kod Grnm:
void Spreadsheet::paste() { QTableWidgetSelectionRange range = selectedRange(); QString str = QApplication::clipboard()->text(); QStringList rows = str.split('\n'); int numRows = rows.count(); int numColumns = rows.first().count('\t') + 1; if (range.rowCount() * range.columnCount() != 1 && (range.rowCount() != numRows

74

Qt 4 ile C++ GUI Programlama


|| range.columnCount() != numColumns)) { QMessageBox::information(this, tr("Spreadsheet"), tr("The information cannot be pasted because the copy " "and paste areas aren't the same size.")); return; } for (int i = 0; i < numRows; ++i) { QStringList columns = rows[i].split('\t'); for (int j = 0; j < numColumns; ++j) { int row = range.topRow() + i; int column = range.leftColumn() + j; if (row < RowCount && column < ColumnCount) setFormula(row, column, columns[j]); } } somethingChanged(); }

paste() yuvas Edit > Pastee tekabl eder. Metni pano stne geiririz ve karakter katarn QStringList haline getirmek iin QString::split() statik fonksiyonunu arrz. Her bir satr listede bir karakter katar halini alr. Sonra, kopyalama alannn boyutunu belirleriz. Satrlarn says QStringList iindeki karakter katarlarnn says, stunlarn says ilk satrdaki tab karakterlerinin says art 1dir. Eer sadece bir hcre seilmise, onu yaptrma alannn sol st kesi olarak kullanrz; aksi halde, geerli seimi yaptrma alan olarak kullanrz. Yaptrma ilemini yerine getirmek iin satrlar yineler ve QString::split() kullanarak hcrelere ayrrz, fakat bu sefer tab karakterini ayrc olarak kullanrz. ekil 4.6 bu admlar bir rnekle aklyor.

ekil 4.6 void Spreadsheet::del() { QList<QTableWidgetItem *> items = selectedItems(); if (!items.isEmpty()) { foreach (QTableWidgetItem *item, items) delete item; somethingChanged(); } }

del() yuvas Edit > Deletee tekabl eder. Eer seili eler varsa, fonksiyon onlar siler ve somethingChanged() fonksiyonunu arr. Seimdeki Cell nesnelerinin her birini hcrelerden temizlemek iin 75

Qt 4 ile C++ GUI Programlama delete kullanmak yeterlidir. QTableWidget, QTableWidgetItemlarnn silindiinin farkna varr ve eer elerin hibiri grnr deilse otomatik olarak kendisini yeniden izer. Eer cell()i silinmi bir hcrenin konumuyla arrsak, bo bir iareti dndrecektir.
void Spreadsheet::selectCurrentRow() { selectRow(currentRow()); } void Spreadsheet::selectCurrentColumn() { selectColumn(currentColumn()); }

selectCurrentRow() ve selectCurrentColumn() fonksiyonlar Edit > Select > Row ve Edit > Select > Column men seeneklerine tekabl ederler. Gerekletirimleri QTableWidgetn selectRow() ve selectColumn() fonksiyonlarna dayanr. Edit > Select > All ardndaki ilev, QTableWidgettan miras alnan QAbstractItemView::selectAll() fonksiyonuyla saland iin bizim gerekletirmemiz gerekmez.
void Spreadsheet::findNext(const QString &str, Qt::CaseSensitivity cs) { int row = currentRow(); int column = currentColumn() + 1; while (row < RowCount) { while (column < ColumnCount) { if (text(row, column).contains(str, cs)) { clearSelection(); setCurrentCell(row, column); activateWindow(); return; } ++column; } column = 0; ++row; } QApplication::beep(); }

findNext() yuvas u ekilde iler: Eer geerli hcre C24 ise, D24, E24, , Z24, sonra A25, B25, C25, , Z25, ve Z999a kadar arar. Eer bir eleme bulursak, geerli seimi temizler, hcre gstergesini eleen hcreye tar ve Spreadsheet penceresini aktif ederiz. Eer hibir eleme bulunmazsa, ses kararak (beep) aramann baarsz sonulandn bildiririz.
void Spreadsheet::findPrevious(const QString &str, Qt::CaseSensitivity cs) { int row = currentRow(); int column = currentColumn() - 1; while (row >= 0) { while (column >= 0) { if (text(row, column).contains(str, cs)) {

76

Qt 4 ile C++ GUI Programlama


clearSelection(); setCurrentCell(row, column); activateWindow(); return; } --column; } column = ColumnCount - 1; --row; } QApplication::beep(); }

findPrevious() yuvas, geriye doru yinelemesi ve A1 hcresinde durmas dnda findNext() ile benzerdir.

Dier Menleri Gerekletirme


imdi Tools ve Options menlerinin yuvalarn gerekletireceiz. Bu menler ekil 4.7de gsteriliyor.

ekil 4.7 void Spreadsheet::recalculate() { for (int row = 0; row < RowCount; ++row) { for (int column = 0; column < ColumnCount; ++column) { if (cell(row, column)) cell(row, column)->setDirty(); } } viewport()->update(); }

recalculate() yuvas Tools > Recalculatee tekabl eder. Gerektiinde Spreadsheet tarafndan otomatik olarak arlr. Tm hcreler zerinde yineler ve her hcreyi yeniden hesaplanmas gerekli(requiring recalculation) ile iaretlemek iin setDirty()yi arrz. QTableWidget, hesap izelgesinde gsterecei deeri elde etmek iin bir Cell zerinde text()i ardnda, deer yeniden hesaplanm olacaktr. Sonra, tm hesap izelgesini yeniden izmesi iin grnt kaps zerinde update()i arrz. QTableWidgettaki yeniden iz kodu(repaint code) daha sonra, her grnr hcrenin grntleyecei deeri elde etmek iin text()i arr. nk her hcre iin setDirty()yi ardk; text() arlar henz hesaplanm bir deeri kullanacak. Hesaplama, grnmeyen hcrelerin de yeniden hesaplanmasn gerektirebilir ve Cell snf tarafndan yaplr.
void Spreadsheet::setAutoRecalculate(bool recalc) { autoRecalc = recalc; if (autoRecalc) recalculate(); }

77

Qt 4 ile C++ GUI Programlama

setAutoRecalculate() yuvas Options > Auto-Recalculatee tekabl eder. zellik almsa, tm hesap izelgesini, gncel olduundan emin olmak iin derhal yeniden hesaplarz; daha sonra, recalculate() somethingChanged()den otomatik olarak arlr. Options > Show Grid iin herhangi bir ey gerekletirmemiz gerekmez, nk QTableWidget zaten QTableViewden miras alnan bir setShowGrid() yuvasna sahiptir. Tek kalan, MainWindow::sort()tan arlan Spreadsheet::sort()tur:
void Spreadsheet::sort(const SpreadsheetCompare &compare) { QList<QStringList> rows; QTableWidgetSelectionRange range = selectedRange(); int i; for (i = 0; i < range.rowCount(); ++i) { QStringList row; for (int j = 0; j < range.columnCount(); ++j) row.append(formula(range.topRow() + i, range.leftColumn() + j)); rows.append(row); } qStableSort(rows.begin(), rows.end(), compare); for (i = 0; i < range.rowCount(); ++i) { for (int j = 0; j < range.columnCount(); ++j) setFormula(range.topRow() + i, range.leftColumn() + j, rows[i][j]); } clearSelection(); somethingChanged(); }

Sralama geerli seim zerinde iler ve compare nesnesinde saklanan sralama anahtar ve sralama dzenine gre satrlar yeniden dzenler. Verinin her bir satrn bir QStringList ile ifade ederiz ve seimi satrlarn bir listesi eklinde saklarz. Qtun qStableSort() algoritmasn kullanrz ve basitlik olsun diye deere gre sralamaktansa formle gre sralarz. lem, ekil 4.8 ve 4.9da rneklendirilerek gsteriliyor.

ekil 4.8

ekil 4.9

78

Qt 4 ile C++ GUI Programlama qSableSort() fonksiyonu bir balama yineleyicisi(begin iterator), bir bitme yineleyicisi(end iterator) ve bir karlatrma fonksiyonu kabul eder. Karlatrma fonksiyonu, iki argman alan(iki QStringList) alan ve eer ilk argman ikinci argmandan daha kk ise true, aksi halde false dndren bir fonksiyondur. Karlatrma fonksiyonu olarak aktardmz compare nesnesi gerek bir fonksiyon deildir, bir fonksiyonmu gibi kullanlabilir. qStableSort()u uyguladktan sonra, veriyi tablonun gerisine tar, seimi temizler ve somethingChanged()i arrz. spreadsheet.h iinde, SpreadsheetCompare yle tanmlanmt:
class SpreadsheetCompare { public: bool operator()(const QStringList &row1, const QStringList &row2) const; enum { KeyCount = 3 }; int keys[KeyCount]; bool ascending[KeyCount]; };

SpreadsheetCompare snf zeldir, nk bir () operatr gerekletirir. Bu bize snf bir fonksiyonmu gibi kullanma imkn verir. Byle snflara fonksiyon nesne (function object) ya da functors ad verilir. Bir fonksiyon nesnenin nasl altn anlamak iin basit bir rnekle balayacaz:
class Square { public: int operator()(int x) const { return x * x; } }

Square snf, parametresinin karesini dndren bir operator()(int) fonksiyonu salar. Fonksiyonu compute(int) yerine operator()(int) olarak isimlendirerek, Square tipindeki bir nesneyi bir fonksiyonmu gibi kullanabilme yetenei kazandk.
Square square; int y = square(5); // y equals 25

imdi SpreadsheetComparei ieren bir rnek grelim:


QStringList row1, row2; SpreadsheetCompare compare; ... if (compare(row1, row2)) { // row1 is less than row2 }

compare nesnesi, yaln bir compare() fonksiyonuymu gibi kullanlabilir. Ek olarak gerekletirimi ye deikenlerinde saklanan tm sralama anahtarlarna ve sralama dzenlerine eriebilir.

79

Qt 4 ile C++ GUI Programlama Bu tasarya bir alternatif, sralama anahtarlarn ve sralama dzenlerini global deikenlerde saklamak ve yaln bir compare() fonksiyonu kullanmak olacaktr. Ancak, global deikenlerle hesaplama yapmak irkindir ve ho hatalara yol aabilir. te, iki hesap izelgesi satrn karlatrmakta kullanlan fonksiyonun gerekletirimi:
bool SpreadsheetCompare::operator()(const QStringList &row1, const QStringList &row2) const { for (int i = 0; i < KeyCount; ++i) { int column = keys[i]; if (column != -1) { if (row1[column] != row2[column]) { if (ascending[i]) { return row1[column] < row2[column]; } else { return row1[column] > row2[column]; } } } } return false; }

Operatr, eer ilk satr ikinci satrdan daha kk ise true; aksi halde false dndrr. qStableSort() fonksiyonu sralamay yapmak iin bu fonksiyonun sonucunu kullanr. SpreadsheetCompare nesnesinin key ve ascending dizileri MainWindow::sort() fonksiyonu iinde doldurulurlar. Her bir anahtar bir stun indeksi ya da -1(None) tutar. ki satrdaki ilikili hcre girdilerini her bir anahtar iin srayla karlatrrz. Bir farkllk bulur bulmaz, true ve false deerlerinden uygun olan birini dndrrz. Eer tm karlatrmalar eit kyorsa, false dndrrz. Bylece, Spreadsheet snfn tamamladk. Sradaki ksmda Cell snfn irdeleyeceiz.

QTableWidgetItem Altsnf Tretme


Cell snf QTableWidgetItem snfndan tretilir. Snf, Spreadsheet ile iyi almas iin tasarlanr, fakat bu snfa zel bir bamll yoktur ve teoride her QTableWidget iinde kullanlabilir. te balk dosyas: Kod Grnm:
#ifndef CELL_H #define CELL_H #include <QTableWidgetItem> class Cell : public QTableWidgetItem { public: Cell(); QTableWidgetItem *clone() const; void setData(int role, const QVariant &value);

80

Qt 4 ile C++ GUI Programlama


QVariant data(int role) const; void setFormula(const QString &formula); QString formula() const; void setDirty(); private: QVariant QVariant QVariant QVariant value() const; evalExpression(const QString &str, int &pos) const; evalTerm(const QString &str, int &pos) const; evalFactor(const QString &str, int &pos) const;

mutable QVariant cachedValue; mutable bool cacheIsDirty; }; #endif

Cell snf QTableWidgetItem iki private deiken ekleyerek geniletir: cachedValue hcre deerlerini bir QVariant olarak nbellekler. Eer nbelleklenen deer gncel deilse, cacheIsDirty truedur. QVariant kullanrz, nk baz hcreler QString bir deere sahipken, baz hcreler double bir deere sahiptir. cachedValue ve cacheIsDirty deikenleri C++ mutable anahtar kelimesi kullanlarak bildirilirler. Bu bize bu deikenleri const fonksiyonlarla deitirebilme imkn verir. Alternatif olarak, deeri text() her arldnda yeniden hesaplayabilirdik, fakat bu gereksiz ve verimsiz olacaktr. Snf tanmnda hi Q_OBJECT makrosu olmadna dikkat edin. Cell, sinyalleri ya da yuvalar olmayan sade bir C++ snfdr. Aslnda, Cell snfnda sinyallere ve yuvalara sahip olamayz, nk QTableWidgetItem, QObjectten tretilmemitir. Qtun e(item) snflar QObjectten tretilmez. Eer sinyallere ve yuvalara ihtiya duyulursa, eleri ieren paracklar iinde ya da istisnai olarak QObject oklu miras ile gerekletirilirler. te, cell.cppnin balangc:
#include <QtGui> #include "cell.h" Cell::Cell() { setDirty(); }

Kurucuda tek ihtiyacmz nbellei bozuk(dirty) olarak ayarlamaktr. Bir ebeveyn aktarmamz gerekmez; hcre setItem() ile bir QTableWidget iine eklendiinde, QTableWidget onun sahipliini otomatik olarak stlenecektir. Her QTableWidgetItem, her bir veri rol(role) iin bir QVarianta kadar veri tutabilir. En yaygn kullanlan roller Qt::EditRole ve Qt::DisplayRoledr. Dzenleme rol(edit role) dzenlenecek veri iin, grntleme rol(display role) grntlenecek veri iin kullanlr. Her ikisi iin de veri genellikle

81

Qt 4 ile C++ GUI Programlama ayndr, fakat Cellde dzenleme rol hcrenin formlne, grntleme rol hcrenin deerine (forml deerlendirmesinin sonucu) tekabl eder.
QTableWidgetItem *Cell::clone() const { return new Cell(*this); }

clone() fonksiyonu QTableWidget tarafndan yeni bir hcre oluturmas gerektiinde arlr; mesela, kullanc daha nce kullanmad bo bir hcreye bir eyler yazmaya baladnda. QTableWidgetItem::setItemPrototype()a aktarlan rnek, klonlanan edir.
void Cell::setFormula(const QString &formula) { setData(Qt::EditRole, formula); }

setFormula() fonksiyonu hcrenin formln ayarlar. Dzenleme rol ile yaplan setData() arsndan dolay, bir uygunluk fonksiyonudur. Spreadsheet::setFormula()dan arlr.
QString Cell::formula() const { return data(Qt::EditRole).toString(); }

formula() fonksiyonu Spreadsheet::formula()dan arlr. setFormula() gibi, fakat bu sefer enin EditRole verisine erien bir uygunluk fonksiyonudur.
void Cell::setData(int role, const QVariant &value) { QTableWidgetItem::setData(role, value); if (role == Qt::EditRole) setDirty(); }

Eer yeni bir formle sahipsek, cacheIsDirtyyi bir dahaki text() arsnda hcrenin yeniden hesaplanmasn salamak iin true olarak ayarlarz. text() fonksiyonu QTableWidgetItem tarafndan salanan bir uygunluk fonksiyonudur ve data(Qt::DisplayRole).tostring() arsna denktir.
void Cell::setDirty() { cacheIsDirty = true; }

setDirty() fonksiyonu bir hcrenin deerini yeniden hesaplanmaya zorlamak iin arlr. Sadece cacheIsDirtyyi true olarak ayarlar ve bu cachedValue artk gncel deil anlamn tar. Yeniden hesaplama, gerekli oluncaya kadar icra edilmez.
QVariant Cell::data(int role) const { if (role == Qt::DisplayRole) { if (value().isValid()) { return value().toString(); } else { return "####"; }

82

Qt 4 ile C++ GUI Programlama


} else if (role == Qt::TextAlignmentRole) { if (value().type() == QVariant::String) { return int(Qt::AlignLeft | Qt::AlignVCenter); } else { return int(Qt::AlignRight | Qt::AlignVCenter); } } else { return QTableWidgetItem::data(role); } }

data() fonksiyonu QTableWidgetItemdan uyarlanr. Eer Qt::DisplayRole ile arlmsa hesap izelgesi iinde gsterilecek metni, eer Qt:EditRole ile arlmsa forml dndrr. Eer Qt::TextAlignmentRole ile arlmsa, uygun bir hizalan dndrr. DisplayRole durumunda, hcrenin deerini hesaplamada value()ya gvenir. Eer deer geersizse (nk forml yanltr), #### dndrrz. data() iinde kullanlan Cell::value() fonksiyonu bir QVariant dndrr. Bir QVariant, rnein double ve QString gibi farkl tipteki deerleri saklayabilir ve fonksiyonlarn deikeni dier tiplere dntrmesini salar. rnein, double bir deiken iin toString() ars, double temsil eden bir karakter katar retir. Varsaylan kurucu kullanlarak oluturulmu bir QVariant bo (invalid) bir deikendir. Kod Grnm:
const QVariant Invalid; QVariant Cell::value() const { if (cacheIsDirty) { cacheIsDirty = false; QString formulaStr = formula(); if (formulaStr.startsWith('\'')) { cachedValue = formulaStr.mid(1); } else if (formulaStr.startsWith('=')) { cachedValue = Invalid; QString expr = formulaStr.mid(1); expr.replace(" ", ""); expr.append(QChar::Null); int pos = 0; cachedValue = evalExpression(expr, pos); if (expr[pos] != QChar::Null) cachedValue = Invalid; } else { bool ok; double d = formulaStr.toDouble(&ok); if (ok) { cachedValue = d; } else { cachedValue = formulaStr; } } } return cachedValue; }

83

Qt 4 ile C++ GUI Programlama value() private fonksiyonu hcrenin deerini dndrr. Eer cacheIsDirty true ise, deeri yeniden hesaplamamz gerekir. Eer forml tek trnakla balarsa (rnein 12345), tek trnak 0 konumundadr ve deer, karakter katarnn 1 konumundan sonuna kadar olan ksmdr. Eer forml eittir iareti(=) ile balarsa, karakter katarn 1 konumundan alrz ve ierebilecei her boluu sileriz. Sonra deyimin deerini hesaplamak iin evalExpression() arrz. pos argman referans olarak aktarlr ve zmlemeye balanacak yerdeki karakterin konumu belirtir. evalExpression() arsndan sonra, eer zmleme baarlmsa, pos konumundaki karakter eklediimiz QChar::Null karakteri olmaldr. Eer zmleme baarszsa, cachedValueyu Invalid olarak ayarlarz. Eer forml tek trnak ya da bir eittir iareti ile balamyorsa, toDouble() kullanarak forml bir kayan noktal(floating-point) sayya dntrmeyi deneriz. Eer dntrme ie yararsa, cachedValueyu sonutaki say olarak ayarlarz; aksi halde, cahedValueyu forml karakter katar olarak ayarlarz. rnein, 1.50 forml toDouble()n oki true olarak ayarlamasna ve 1.5 dndrmesine neden olurken, Word Population forml toDouble()n oki false olarak ayarlamasna ve 0.0 dndrmesine neden olur. toDouble()a bir bool iaretisi vererek, dnmn baarsn kontrol edebiliriz. value() fonksiyonu const bildirilir. Derleyicinin bize cachedValue ve cacheIsValidi const fonksiyonlarda deitirebilmemize izin vermesi iin, onlar mutable deikenler olarak bildirdik. value()yu non-const yapmak ve mutable anahtar kelimelerini silmek cazip gelebilir, fakat bu derlenmeyecektir nk value()yu const bir fonksiyon olan data()dan arrz. __________________________________________________________________________________ Formllerin ayrtrlmas hari, artk Spreadsheet uygulamasn tamamladk. Bu ksmn geri kalan evalExpression() fonksiyonunu ve ona yardmc iki fonksiyon olan evalTerm() ve evalFactor() fonksiyonlarn kapsar. Kod biraz karmaktr, fakat uygulamay tamamlanmas asndan gereklidir. Kod GUI programlamayla ilgili olmad iin, gnl rahatlyla bu ksm geip Blm 5ten okumaya devam edebilirsiniz. evalExpression() fonksiyonu bir hesap izelgesi deyiminin(expression) deerini dndrr. Bir deyim, + veya - operatrlerince ayrlm bir ya da daha fazla terim(term) olarak tanmlanr. Terimler ise * veya / operatrleriyle ayrlm bir ya da birden fazla faktr(factor) olarak tanmlanrlar. Deyimleri terimlere, terimleri faktrlere ayrarak, operatrlere doru ncelik sras uygulanmasn salarz. rnein 2*C5+D6, birinci terimi 2*C5, ikinci terimi D6 olan bir deyimdir. 2*C5 terimi birinci faktr olarak 2ye ikinci faktr olarak da C5e sahiptir. D6 terimi ise tek bir faktrden oluur: D6. Bir faktr; bir say (2), bir hcre adresi (C5) ya da parantez iinde bir deyim olabilir. Hesap izelgesi deyimlerinin szdizimi ekil 4.10da tanmlanmtr. Gramerdeki her bir sembol iin, onu ayrtran bir uygunluk ye fonksiyonu vardr. Ayrtrclar, recursive-descent parsers olarak adlandrlan tarzda yazlrlar.

84

Qt 4 ile C++ GUI Programlama

ekil 4.10

Bir Deyimi ayrtran evalExpression() fonksiyonuyla balayalm: Kod Grnm:


QVariant Cell::evalExpression(const QString &str, int &pos) const { QVariant result = evalTerm(str, pos); while (str[pos] != QChar::Null) { QChar op = str[pos]; if (op != '+' && op != '-') return result; ++pos; QVariant term = evalTerm(str, pos); if (result.type() == QVariant::Double && term.type() == QVariant::Double) { if (op == '+') { result = result.toDouble() + term.toDouble(); } else { result = result.toDouble() - term.toDouble(); } } else { result = Invalid; } } return result; }

nce, ilk terimin deerini almak iin evalTerm()i arrz. Sonraki karakter + ya da - ise, ikinci defa evalTerm()i ararak devam ederiz; aksi halde, deyim tek bir terimden oluuyor demektir ve bu durumda onun deerini tm deyimin deeri olarak dndrrz. lk iki terimin deerini elde ettikten sonra operatre gre ilemin sonucunu hesaplarz. Eer iki terim de bir double ise, sonucu bir double olarak hesaplarz; aksi halde, sonucu Invalid olarak ayarlarz. Hi terim kalmayncaya kadar byle devam ederiz. Bu doru alr, nk toplama ve karma sol birleimli(left-associative) aritmetik ilemlerdir. Yani 1-2-3, 1-(2-3)e deil, (1-2)-3e eittir. Kod Grnm:
QVariant Cell::evalTerm(const QString &str, int &pos) const { QVariant result = evalFactor(str, pos); while (str[pos] != QChar::Null) { QChar op = str[pos]; if (op != '*' && op != '/') return result; ++pos; QVariant factor = evalFactor(str, pos);

85

Qt 4 ile C++ GUI Programlama


if (result.type() == QVariant::Double && factor.type() == QVariant::Double) { if (op == '*') { result = result.toDouble() * factor.toDouble(); } else { if (factor.toDouble() == 0.0) { result = Invalid; } else { result = result.toDouble() / factor.toDouble(); } } } else { result = Invalid; } } return result; }

evalTerm(), arpma ve blme ile uramas dnda, evalExpression()a ok benzer. evalTerm()deki tek incelik, -baz ilemcilerde hataya neden olduu iin- sfra blmeden kanmaktr. Kod Grnm:
QVariant Cell::evalFactor(const QString &str, int &pos) const { QVariant result; bool negative = false; if (str[pos] == '-') { negative = true; ++pos; } if (str[pos] == '(') { ++pos; result = evalExpression(str, pos); if (str[pos] != ')') result = Invalid; ++pos; } else { QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); QString token; while (str[pos].isLetterOrNumber() || str[pos] == '.') { token += str[pos]; ++pos; } if (regExp.exactMatch(token)) { int column = token[0].toUpper().unicode() - 'A'; int row = token.mid(1).toInt() - 1; Cell *c = static_cast<Cell *>( tableWidget()->item(row, column)); if (c) { result = c->value(); } else { result = 0.0; } } else { bool ok; result = token.toDouble(&ok);

86

Qt 4 ile C++ GUI Programlama


if (!ok) result = Invalid; } } if (negative) { if (result.type() == QVariant::Double) { result = -result.toDouble(); } else { result = Invalid; } } return result; }

evalFactor() fonksiyonu evalExpression() ve evalTerm()e gre biraz daha karmaktr. Faktrn negatif olup olmadn not ederek balarz. Sonra, bir ama paranteziyle balayp balamadna bakarz. Eer yleyse, evalExpression() ararak, parantezin ieriinin bir deyim olup olmadna bakarz. Parantez iine alnm deyimi ayrtrrken, evalExpression() evalTerm()i, evalTerm() evalFactor(), o da tekrar evalExpression() arr. Bu, ayrtrc iinde bir zyineleme ortaya karr. Eer faktr i ie bir deyim deilse, sradaki dizgecii(token) ayklarz (bir hcre adresi ya da bir say olmaldr). Eer dizgecik QRegExp ile eleirse, onu bir hcre referans olarak alrz ve verilen adresteki hcre iin value()yu arrz. Hcre hesap izelgesi iinde herhangi bir yerde olabilir ve dier hcrelerle bamll olabilir. Bamllk bir problem deildir; sadece daha fazla value() arsn ve (bozuk(dirty) hcreler iin) tm bal hcreler hesaplanm olana dek daha fazla ayrtrmay tetikleyecektir. Eer dizgecik bir hcre adresi deilse, onu bir say olarak kabul ederiz.

87

Qt 4 ile C++ GUI Programlama

BLM 5: ZEL PARACIKLAR OLUTURMA

Bu blm Qtu kullanarak zel paracklar(custom widgets) oluturmay aklar. zel paracklar, var olan bir Qt paracndan altsnf treterek ya da direkt olarak QWidgettan altsnf treterek oluturulabilirler. Biz her iki yaklam da rnekle aklayacaz. Ayrca, zel paracklar Qt Designera entegre ederek yerleik Qt paracklar gibi kullanabilmeyi de greceiz.

Qt Paracklarn zelletirme
Bazen, bir Qt paracnn niteliklerinin, Qt Designerda ayarlayarak ya da fonksiyonunu ararak, daha fazla zelletirilmeye ihtiya duyulduu durumlarla karlarz. Basit ve direkt bir zm, ilgili parack snfndan altsnf tretmek ve ihtiyalara uygun olarak uyarlamaktr. Bu ksmda, ekil 5.1de grlen onaltl(hexadecimal) dndrme kutusunu(spin box) gelitireceiz. QSpinBox sadece onlu(decimal) tamsaylar destekler, fakat ondan bir altsnf treterek, onaltl deerleri kabul etmesini ve gstermesini salamak olduka kolaydr.

ekil 5.1 #ifndef HEXSPINBOX_H #define HEXSPINBOX_H #include <QSpinBox> class QRegExpValidator; class HexSpinBox : public QSpinBox { Q_OBJECT public: HexSpinBox(QWidget *parent = 0); protected: QValidator::State validate(QString &text, int &pos) const; int valueFromText(const QString &text) const; QString textFromValue(int value) const; private: QRegExpValidator *validator; }; #endif

HexSpinBox, ilevlerinin birounu QSpinBoxtan miras alr. Tipik bir kurucu salar ve QSpinBoxtan ald sanal fonksiyonu uyarlar.
#include <QtGui>

88

Qt 4 ile C++ GUI Programlama


#include "hexspinbox.h" HexSpinBox::HexSpinBox(QWidget *parent) : QSpinBox(parent) { setRange(0, 255); validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this); }

Varsaylan aral, QSpinBoxn varsaylan aral(0dan 99a) yerine bir onaltl dndrme kutusu iin daha uygun olan 0dan 255e (0x00dan 0xFFye) olarak ayarlarz. Kullanc, bir dndrme kutusunun deerini onun yukar/aa oklarn tklayarak ya da satr editrne bir deer girerek deitirebilir. Son olarak, kullanc girdilerini onaltl saylarla snrlandrmak istiyoruz. Bunu baarmak iin; bir ile sekiz arasnda, her biri ,0, , 9, A, , F, a, , f- kmesinden olan karakterler kabul eden bir QRegExpValidator kullanrz.
QValidator::State HexSpinBox::validate(QString &text, int &pos) const { return validator->validate(text, pos); }

Bu fonksiyon, metnin doru girildiini grmek iin QSpinBox tarafndan arlr. olas sonu vardr: Invalid (metin dzenli deyimle elemiyordur), Intermediate (metin geerli bir deerin olas bir parasdr), ve Acceptable (metin geerlidir). QRegExpValidator uygun bir validate() fonksiyonuna sahiptir, bylece basite ona yaplan arnn sonucunu dndrrz. Teoride, dndrme kutusunun aralnn dna taan deerler iin Invalid ya da Intermediate dndrmeliyiz, fakat QSpinBox bu durumu yardm olmakszn fark edecek derecede zekidir.
QString HexSpinBox::textFromValue(int value) const { return QString::number(value, 16).toUpper(); }

textFromValue() fonksiyonu bir tamsay deeri bir karakter katarna dntrr. QSpinbox bu fonksiyonu, kullanc dndrme kutusunun yukar/aa oklarna bastnda, satr editrn gncellemek iin arr. Deeri, kk harfli onaltlya dntrmek iin ikinci bir 16 argman ile QString::number() statik fonksiyonunu arrz ve sonucu byk harfli yapmak iin QString::toUpper() arrz.
int HexSpinBox::valueFromText(const QString &text) const { bool ok; return text.toInt(&ok, 16); }

valueFromText() fonksiyonu, ters dnm (bir karakter katarndan bir tamsay deere) yerine getirir. QSpinBox bu fonksiyonu, kullanc dndrme kutusunun editr blmne bir deer girdiinde ve Entera bastnda arr. QString::toInt() fonksiyonunu, yine 16 tabann kullanarak, bir metni bir tamsay deere dntrmede kullanrz. Eer karakter katar geerli bir onaltl say deilse, ok false olarak ayarlanr ve toInt() 0 dndrr. Burada, bu olasl dnmek zorunda deiliz, nk geerlilik denetleyicisi sadece geerli onaltl karakter katarlar girilmesine izin verir. Aptal bir deikenin(ok) adresini ayrtrmak yerine, toInt()e ilk argman olarak bo bir iareti de aktarabilirdik. 89

Qt 4 ile C++ GUI Programlama Bylece, onaltl dndrme kutusunu bitirdik. Dier Qt paracklarn zelletirme de ayn modeli izler: Uygun bir Qt parac se, ondan altsnf tret ve baz sanal fonksiyonlarn davranlarn deitirerek uyarla.

QWidget Altsnf Tretme


Birok zel parack, var olan paracklarn bir birleimidir. Var olan paracklar birletirerek ina edilen zel paracklar genelde Qt Designerda gelitirilirler: Widget ablonunu kullanarak yeni bir form olutur. Gerekli paracklar forma ekle ve onlar yerletir. Sinyal ve yuva balantlarn ayarla. Eer sinyal ve yuvalarn baarabildiinin tesinde bir davran lazmsa, gerekli kodu QWidget ve uic-retilmi snflarndan tretilen bir snf iine yaz. Elbette var olan paracklarn birletirilmesi tamamyla kod iinde de yaplabilir. Hangi yaklam seilirse seilsin, sonuta ortaya kacak snf bir QWidget altsnfdr. Eer parack kendi sinyal ve yuvalarna sahip deilse ve herhangi bir sanal fonksiyon da uyarlanmamsa, parac, var olan paracklar bir altsnf olmakszn birletirerek toparlamak da mmkndr. Bu yaklam Blm 1de, bir QWidget, bir QSpinBox ve bir QSlider ile Age uygulamasn olutururken kullanmtk. Yine de, kolaylkla QWidgettan bir altsnf tretebilir ve altsnfn kurucusu iinde QSpinBox ve QSlider oluturabilirdik. Qt paracklarnn hibiri eldeki ie uygun olmadnda ve arzu edilen sonucu elde etmek iin var olan paracklar birletirmenin ya da uyarlamann hibir yolu yoksa dahi, istediimiz parac oluturabiliriz. Bu, QWidgettan altsnf treterek ve birka olay ileyiciyi(event handler) parac izmeye ve fare tklamalarn yantlamaya uyarlayarak baarlabilir. Bu yaklam bize, paracmzn grnmn ve davrann tanmlamada ve kontrol etmede tam bir bamszlk salar. Qtun yerleik paracklar (rnein QLabel, QPushButton ve QTableWidget ) bu yolla gerekletirilmilerdir. Eer Qtun iinde var olmasalard, kendimiz, QWidget tarafndan salanan, tamamyla platformdan bamsz public fonksiyonlar kullanarak, oluturmamz da mmknd. Bir zel paracn bu yaklam kullanarak nasl oluturduunu aklamak iin, ekil 5.2de grlen IconEditor paracn oluturacaz. IconEditor, simge(icon) dzenleme program olarak kullanlabilecek bir paracktr.

ekil 5.2

Balk dosyasnn kritiini yaparak balayalm.


#ifndef ICONEDITOR_H #define ICONEDITOR_H #include <QColor>

90

Qt 4 ile C++ GUI Programlama


#include <QImage> #include <QWidget> class IconEditor : public QWidget { Q_OBJECT Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor) Q_PROPERTY(QImage iconImage READ iconImage WRITE setIconImage) Q_PROPERTY(int zoomFactor READ zoomFactor WRITE setZoomFactor) public: IconEditor(QWidget *parent = 0); void setPenColor(const QColor &newColor); QColor penColor() const { return curColor; } void setZoomFactor(int newZoom); int zoomFactor() const { return zoom; } void setIconImage(const QImage &newImage); QImage iconImage() const { return image; } QSize sizeHint() const;

IconEditor snf adet zel nitelik(property) bildirmek iin Q_PROPERTY() makrosunu kullanr: penColor, iconImage ve zoomFactor. Her bir nitelik bir veri tipine, bir oku(read) fonksiyonuna ve istee bal bir yaz(write) fonksiyonuna sahiptir. rnein, penColor niteliinin veri tipi QColordr ve penColor() ve setPenColor() fonksiyonlar kullanlarak okunabilir ve yazlabilir. Parac Qt Designer kullanarak oluturduumuzda, zel nitelikler Qt Designern nitelik editrnde QWidgettan miras alnan niteliklerin altnda grnr. Nitelikler QVariant tarafndan desteklenen her tipte olabilir. Q_OBJECT makrosu, nitelikler tanmlayan snflar iin gereklidir.
protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); private: void setImagePixel(const QPoint &pos, bool opaque); QRect pixelRect(int i, int j) const; QColor curColor; QImage image; int zoom; }; #endif

IconEditor QWidgettan ald protected fonksiyonu yeniden uyarlar ve ayn zamanda birka private fonksiyon ve deikene sahiptir. private deiken niteliin deerlerini tutar. Gerekletirim dosyas IconEditorn kurucusuyla balar:
#include <QtGui> #include "iconeditor.h" IconEditor::IconEditor(QWidget *parent) : QWidget(parent) {

91

Qt 4 ile C++ GUI Programlama


setAttribute(Qt::WA_StaticContents); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); curColor = Qt::black; zoom = 8; image = QImage(16, 16, QImage::Format_ARGB32); image.fill(qRgba(0, 0, 0, 0)); }

Kurucu, Qt::WA_StaticContents nitelii ve setSizePolicy() ars gibi baz incelikli ynlere sahiptir. Onlar ksaca ele alacaz. Kalem rengi siyah(black) olarak ayarlanr. Yaknlatrma katsays(zoom factor) 8 olarak ayarlanr; bu, simgedeki her bir pikselin 8 x 8lik bir kare olarak yorumlanaca anlamna gelir. Simge verisi image ye deikeninde saklanr ve setIconImage() ve iconImage() fonksiyonlarndan dorudan eriilebilirdir. Bir simge dzenleme program, genellikle kullanc bir simge dosyasn atnda setIconImage() ve kullanc onu kaydetmek istediinde simgeye erimek iin iconImage() aracaktr. image deikeni QImage tipindedir. Onu, 16 x 16 piksele ve yar saydaml(semitransparency) destekleyen 32-bit ARGB formatna ilklendiririz. Resim verisini, resmi saydam bir renkle doldurarak temizleriz. QImage snf, bir resmi donanmdan bamsz bir tarzda saklar. 1-bit, 8-bit ve 32-bit derinliklerini kullanamaya ayarlanabilir. 32-bit derinliinde bir resim bir pikselin krmz, yeil ve mavi bileenlerinden her biri iin 8 bit kullanr. Kalan 8 bit pikselin alfa bileenini (opacity) saklar. rnein, katksz bir krmz renginin krmz, yeil, mavi ve alfa bileenleri 255, 0, 0 ve 255 deerlerine sahiptir. Qtda bu renk u ekilde belirtilebilir:
QRgb red = qRgba(255, 0, 0, 255);

Ya da, madem renk saydam deil, drdnc argman olmadan u ekilde de gsterilebilir:
QRgb red = qRgb(255, 0, 0);

QRgb sadece unsigned int deerler tutabilen bir tip tanmdr ve qRgb() ve qRgba fonksiyonlar, argmanlarn tek bir 32-bit ARGB tamsay deer halinde birletiren yerel fonksiyonlardr. Dolaysyla yle yazmak da mmkndr:
QRgb red = 0xFFFF0000;

Birinci FF alfa bileenine ikinci FF krmz bileenine tekabl eder. IconEditorn kurucusunda alfa bileeni olarak 0 olarak kullanarak QImage saydam bir renkle doldururuz. Qt, renkleri saklamak iin iki tip salar: QRgb ve QColor. QColor birok kullanl fonksiyonuyla Qt iinde renkleri saklamada yaygn olarak kullanlan bir snfken, QRgb sadece QImage iinde 32-bit piksel veriyi saklamada kullanlmak zere tanmlanm bir tiptir. IconEditor paracnda, QRgbyi sadece QImage ile ilgilendiimizde kullanrz. penColor nitelii de dhil olmak zere dier her ey iin QColor kullanrz.
QSize IconEditor::sizeHint() const { QSize size = zoom * image.size(); if (zoom >= 3)

92

Qt 4 ile C++ GUI Programlama


size += QSize(1, 1); return size; }

sizeHint() fonksiyonu QWidgettan uyarlanmtr ve bir paracn ideal boyutunu dndrr. Burada ideal boyutu, resim boyutunun yaknlatrma katsaysyla arpm ile yaknlatrma katsaysnn 3 veya daha fazla olduu durumlarda zgaraya yerletirebilmek iin her bir ynden ekstra bir piksel olarak alrz. Bir paracn boyut ipucu(size hint) genelde yerleimlerle birlemede kullanldr. Qtun yerleim yneticileri, bir formun ocuk paracklarn yerletirdiklerinde, paracn boyut ipucuna mmkn olduunca uymaya alrlar. IconEditor iyi bir yerleime sahip olmak iin gvenilir bir boyut ipucu bildirmelidir. Boyut ipucuna ek olarak, paracklar, yerleim sistemine nasl genileyip kleceklerini syleyen bir boyut politikasna(size policy) sahiptirler. Kurucuda, yatay ve dikey boyut politikalarn ayarlamak amacyla setSizePolicy()yi QSizePolicy::Minimum argmanlar ile ararak, her yerleim yneticisine, boyut ipucunun bu paracn minimum boyutu olduunu sylemi oluruz. Baka bir deyile, parack eer gerekirse geniletilebilir, fakat boyut ipucundan daha aza kltlmemelidir. Bu, Qt Designerda paracn sizePolicy niteliini ayarlayarak da yaplabilir.
void IconEditor::setPenColor(const QColor &newColor) { curColor = newColor; }

setPenColor() fonksiyonu geerli kalem rengini ayarlar. Renk, yeni izilmi pikseller iin kullanlabilir.
void IconEditor::setIconImage(const QImage &newImage) { if (newImage != image) { image = newImage.convertToFormat(QImage::Format_ARGB32); update(); updateGeometry(); } }

setIconImage() fonksiyonu, resmi dzenlemeye hazrlar. Eer resim bir alfa tampon ile 32-bit yaplmamsa, convertToFormat() arrz. Kodun baka bir yerinde resim verisinin 32-bit ARGB deerler olarak saklandn varsayacaz. image deikenini ayarladktan sonra, paracn yeni resim kullanlarak yeniden izilmesi iin QWidget::update()i arrz. Sonra, paracn ierdii her yerleime paracn boyut ipucunun deitiini sylemek iin QWidget::updateGeometry()yi arrz. Ondan sonra, yerleim otomatik olarak yeni boyut ipucuna uydurulacaktr.
void IconEditor::setZoomFactor(int newZoom) { if (newZoom < 1) newZoom = 1; if (newZoom != zoom) { zoom = newZoom; update(); updateGeometry(); }

93

Qt 4 ile C++ GUI Programlama


}

setZoomFactor()yaknlatrma katsaysn ayarlar. Sfra blmeyi nlemek iin, 1in altndaki her deeri 1e eitleriz. Sonra, parac yeniden izmek ve yerleim yneticilerini boyut ipucu deiiklii hakknda bilgilendirmek iin update() ve updateGeometry()yi arrz. penColor(), iconImage() ve zoomFactor() fonksiyonlar balk dosyasnda yerel fonksiyonlar olarak gerekletirilir. imdi, paintEvent() fonksiyonu iin olan kodlarn kritiini yapacaz. Bu fonksiyon IconEditorn en nemli fonksiyonudur. Paracn yeniden izilmesi gerektii her zaman arlr. QWidgettaki varsaylan gerekletirim hibir ey yapmaz, parac bo brakr. Tpk Blm 3te tantmz closeEvent() gibi, paintEvent() de bir olay ileyicidir. Qt, her biri farkl tipte bir olayla ilikili olan birok olay ileyiciye sahiptir. Bir iz olay(paint event) retildiinde ve paintEvent() arldnda birok durum vardr. rnein: Bir parack ilk kez gsterildiinde, sistem parac kendini izmeye zorlamak iin otomatik olarak bir iz olay retir. Parack yeniden boyutlandrldnda sistem bir iz olay retir. Bir parack baka bir pencere tarafndan gizlendiinde ve sonra tekrar aa ktnda, gizlenen alan iin bir iz olay retilir (eer pencere sistemi alan saklamamsa). QWidget::update() ya da QWidget::repaint()i ararak da bir iz olayn zorlayabiliriz. Bu iki fonksiyon arasndaki farllk; repaint() derhal yeniden izmeye zorlarken, update()in sadece bir iz olayn Qtun olaylar ileyecei zamana zamanlamasdr. (Her iki fonksiyonda eer parack ekranda grnr deilse hibir ey yapmaz.) Eer update() birok kez arlrsa, Qt titremeyi nlemek iin ardk iz olaylarn tek bir iz olay iine sktrr. IconEditorda, her zaman update()i kullanrz. te kod: Kod Grnm:
void IconEditor::paintEvent(QPaintEvent *event) { QPainter painter(this); if (zoom >= 3) { painter.setPen(palette().foreground().color()); for (int i = 0; i <= image.width(); ++i) painter.drawLine(zoom * i, 0, zoom * i, zoom * image.height()); for (int j = 0; j <= image.height(); ++j) painter.drawLine(0, zoom * j, zoom * image.width(), zoom * j); } for (int i = 0; i < image.width(); ++i) { for (int j = 0; j < image.height(); ++j) { QRect rect = pixelRect(i, j); if (!event->region().intersect(rect).isEmpty()) { QColor color = QColor::fromRgba(image.pixel(i, j)); if (color.alpha() < 255) painter.fillRect(rect, Qt::white);

94

Qt 4 ile C++ GUI Programlama


painter.fillRect(rect, color); } } } }

Parack zerinde bir QPainter nesnesi oluturarak balarz. Eer yaknlatrma katsays 3 veya daha bykse, QPainter::drawLine() fonksiyonunu kullanarak zgara eklinde yatay ve dikey satrlar izeriz. Bir QPainter::drawLine() ars aadaki szdizimine sahiptir:
painter.drawLine(x1, y1, x2, y2);

Buradaki (x1, y1) bir ucun konumu, (x2, y2) dier ucun konumudur. Ayrca, fonksiyonun, drt int deer yerine iki QPoint alan ar yklenmi bir versiyonu da mevcuttur. Bir Qt paracnn sol st pikseli (0, 0) konumuna, sa alt pikseli (width() - 1, height() - 1) konumuna yerlemitir. Bu, geleneksel Kartezyen koordinat sistemi ile ayndr, fakat ekil 5.3te de grld zere, terstir.

ekil 5.3

QPainter stnde drawLine() armadan nce, setPen()i kullanarak satrn rengini ayarlarz. Rengi kodlayabilirdik, siyah veya gri gibi, fakat daha iyi bir yaklam paracn renk paletini kullanmaktr. Her parack, ne iin hangi rengin kullanlmas gerektiini belirten bir palet ile donatlmtr. rnein, paracklarn arkaplan rengi (genellikle ak gri) iin bir palet girdisi ve bir tane de arkaplann stndeki metnin rengi (genellikle siyah) iin bir palet girdisi mevcuttur. Varsaylan olarak, bir paracn paleti, pencere sisteminin renk dzenini benimser. Paletten renkler kullanarak, IconEditorn kullanc tercihlerine uymasn salarz. Bir paracn paleti renk grubundan meydana gelir: aktif(active), inaktif(inactive) ve devre d(disabled). Kullanlacak renk, paracn geerli durumuna baldr: Active grubu mevcut durumda aktif olan penceredeki paracklar iin kullanlr. Inactive grubu dier pencerelerdeki paracklar iin kullanlr. Disabled grubu herhangi bir penceredeki devre d kalm/braklm paracklar iin kullanlr. QWidget::palette() fonksiyonu, paracn paletini bir QPalette nesnesi olarak dndrr. Renk gruplar, QPalette::ColorGroup tipinin enumlar olarak belirtilirler. izmek iin uygun bir fra(brush) veya renk semek istediimizde, doru yaklam QWidget::palette()den elde ettiimiz geerli paleti ve gerekli rol kullanmaktr, rnein, QPalette::foreground(). Her bir rol fonksiyonu bir fra dndrr, fakat eer sadece renk gerekiyorsa, paintEvent()te yaptmz 95

Qt 4 ile C++ GUI Programlama gibi, rengi fradan elde edebiliriz. Fralar varsaylan olarak, paracn durumuna uygun fray dndrrler, bu nedenle bir renk grubu belirtmemiz gerekmez. paintEvent() fonksiyonu, resmi izerek biter. IconEditor::pixelRect(), yeniden izilecek alan tanmlayan bir QRect dndrr. (ekil 5.4 bir dikdrtgenin nasl izildiini gsteriyor.) Basit bir optimizasyon olarak, bu alann dnda kalan pikselleri yeniden izmeyiz.

ekil 5.4

Yaknlatrlm bir pikseli izmek iin QPainter::fillRect()i arrz. QPainter::fillRect() bir QRect ve bir QBrush alr. Fra olarak bir QColor aktararak, bir kat dolgu modeli elde ederiz. Eer renk tamamyla k geirmez(opaque) deilse, yani alfa kanal 255ten az ise, nce beyaz bir arka plan izeriz.
QRect IconEditor::pixelRect(int i, int j) const { if (zoom >= 3) { return QRect(zoom * i + 1, zoom * j + 1, zoom - 1, zoom - 1); } else { return QRect(zoom * i, zoom * j, zoom, zoom); } }

pixelRect() fonksiyonu QPainter::fillRect() iin uygun bir QRect dndrr. i ve j parametreleri QImage iindeki piksel koordinatlardr (parack iindeki deil). Eer yaknlatrma katsays 1 ise, iki koordinat sistemi tam olarak uyar. QRect kurucusu QRect(x , y, width, height) eklinde bir szdizimine sahiptir; burada (x, y) dikdrtgenin sol st kesinin konumu, width x height dikdrtgenin boyutudur. Eer yaknlatrma katsays 3 veya daha fazla ise, boyutu yatay ve dikeyde birer piksel azaltrz, bu nedenle dolgu(fill), zgara satrlar zerine izilmez.
void IconEditor::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { setImagePixel(event->pos(), true); } else if (event->button() == Qt::RightButton) { setImagePixel(event->pos(), false); } }

Kullanc bir fare butonuna bastnda, sistem bir fare butonuna basma(mouse press) olay retir. QWidget::mousePressEvent()i uyarlayarak bu olay cevaplandrabiliriz ve bylece, resmin fare imlecinin altndaki piksellerini dzenleyebilir ya da silebiliriz. Eer kullanc farenin sol butonuna basarsa, setImagePixel() private fonksiyonunu, ikinci argman olarak trueyu gndererek arrz. (Bylece pikselin, geerli kalem rengi ile dzenlenmesi gerektiini 96

Qt 4 ile C++ GUI Programlama bildiririz.) Eer kullanc farenin sa butonuna basarsa, yine setImagePixel()i arrz, fakat bu sefer ikinci argman olarak, pikselleri temizlemeyi emreden falseu gndererek.
void IconEditor::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { setImagePixel(event->pos(), true); } else if (event->buttons() & Qt::RightButton) { setImagePixel(event->pos(), false); } }

mouseMoveEvent() fare tanma olaylarn iler. Bu olaylar varsaylan olarak, kullanc bir fare butonuna basl tuttuunda retilirler. Bu davran, QWidget::setMouseTracking() ararak deitirmek mmkndr, fakat bu rnek iin byle yapmamz gerekmez. Farenin sa ya da sol butonuna basarak bir pikselin dzenlenebilmesi veya temizlenebilmesi gibi, bir pikselin zerinde butona basl tutmak ve duraksamak da bir pikseli dzenlemek veya temizlemek iin yeterlidir. Bir seferde birden ok butona basl tutmak mmkn olduu iin, QMouseEvent::buttons() tarafndan dndrlen deer fare butonlarnn bitsel bir ORudur. Belli bir butona baslp baslmadn & operatrn kullanarak test ederiz ve eer baslmsa setImagePixel()i arrz.
void IconEditor::setImagePixel(const QPoint &pos, bool opaque) { int i = pos.x() / zoom; int j = pos.y() / zoom; if (image.rect().contains(i, j)) { if (opaque) { image.setPixel(i, j, penColor().rgba()); } else { image.setPixel(i, j, qRgba(0, 0, 0, 0)); } update(pixelRect(i, j)); } }

setImagePixel() fonksiyonu, bir pikseli dzenlemek ya da temizlemek iin mousePressEvent() ve mouseMoveEvent()ten arlr. pos parametresi, farenin parack zerindeki konumudur. lk adm fare konumunu parack koordinatlarndan resim koordinatlarna evirmektir. Bu, fare konumunun x() ve y() bileenlerini yaknlatrma katsaysna blerek yaplr. Sonra, noktann doru alan iinde olup olmadn kontrol ederiz. Kontrol, QImage::rect() ve QRect::contains() kullanarak kolaylkla yaplr; bu, inin 0 ve image.width() - 1 arasnda olup olmadn ve jnin 0 ve image.height() - 1 arasnda olup olmadn etkin bir ekilde kontrol eder. opaque parametresine bal olarak, resimdeki pikseli dzenler ya da temizleriz. Bir pikseli temizlemek aslnda onu saydam olarak dzenlemektir. QImage::setPixel() ars iin, kalemin QColorn bir 32bit ARGB deere dntrmeliyiz. Sonunda, yeniden izilmesi gereken alann bir QRecti ile update()i arrz. ye fonksiyonlar bitirdik, imdi kurucuda kullandmz Qt::WA_StaticContents niteliine dneceiz. Bu nitelik Qta, parack yeniden boyutlandrldnda ieriinin deimemesini ve paracn sol st 97

Qt 4 ile C++ GUI Programlama kesinde konumlanm olarak kalmasn syler. Qt bu bilgiyi, parack yeniden boyutlandrldnda zaten grnen alanlarn gereksizce yeniden izilmesinin nne gemede kullanr. Bu durum ekil 5.5te bir rnekle aklanyor.

ekil 5.5

Bir parack yeniden boyutlandrldnda, Qt, varsaylan olarak, paracn tm grnr alanlar iin bir iz olay retir. Fakat eer parack Qt::WA_StaticContents niteliiyle oluturulmusa, iz olaynn alan, daha nce grnmeyen piksellerle snrlandrlr. Bu, parack daha kk bir boyuta yeniden boyutlandrldnda hibir iz olay retilmeyecei anlamna gelir. Artk IconEditor parac tamamland. Daha nceki blmlerdeki bilgileri ve rnekleri kullanarak, IconEditor bir pencere olarak, bir QMainWindowda merkez parack olarak ya da bir yerleim veya bir QScrollArea iinde ocuk parack olarak kullanan bir kod yazabilirdik. Sradaki ksmda, parac Qt Designera entegre etmeyi greceiz.

zel Paracklar Qt Designera Entegre Etme


zel paracklar Qt Designer iinde kullanmadan nce, Qt Designer onlardan haberdar etmeliyiz. Bunu yapmak iin iki teknik vardr: Promotion(Terfi) yaklam ve Plugin(Eklenti) yaklam. Terfi yaklam en hzl ve kolay olandr.

ekil 5.6

te, bu yaklam kullanarak bir HexSpinBox, bir form iine adm adm u ekilde eklenir: 1. Qt Designern parack kutusundan formun stne srkleyerek bir QSpinBox oluturun. 2. Dndrme kutusuna(spin box) sa tklayn ve balam menden Prote to Custom Widget sein. 3. Beliren diyaloun snf ismi ksmna HexSpinBox, balk dosyas ksmna hexspinbox.h yazn.

98

Qt 4 ile C++ GUI Programlama uic tarafndan retilen kod, <QSpinBox> yerine hexspinbox.h ierecek ve bylelikle bir HexSpinBox somutlatrm olacak. HexSpinBox parac Qt Designerda, her zelliini ayarlamamza imkn veren bir QSpinBox tarafndan temsil edilecek. Terfi yaklamnn glkleri, zel paracn spesifik zelliklerine Qt Designer iinde eriilememesi ve paracn kendi olarak yorumlanmamasdr. Her iki problem de Eklenti yaklam kullanlarak alabilir. Eklenti yaklam, Qt Designern alma srasnda ykleyebildii ve paracn rneini oluturmakta kullanabildii bir eklenti ktphanesi kreasyonu gerektirir. Qt Designer formu dzenleme ve nizlemede gerek parac kullanr, Qt meta-object sistemine krler olsun ki, Qt Designer, zelliklerinin listesini dinamik olarak oluturabilir. Nasl altn grmek iin, nceki ksmdaki IconEditor bir eklenti olarak Qt Designera entegre edeceiz. nce, bir QDesignerCustomWidgetInterface altsnf tretmeliyiz ve baz sanal fonksiyonlar uyarlamalyz. Eklentinin kaynak kodunun iconeditorplugin adnda bir dizinde, IconEditorn kaynak kodunun ise iconeditor adnda paralel bir dizinde yer aldn varsayacaz. te snf tanm:
#include <QDesignerCustomWidgetInterface> class IconEditorPlugin : public QObject, public QDesignerCustomWidgetInterface { Q_OBJECT Q_INTERFACES(QDesignerCustomWidgetInterface) public: IconEditorPlugin(QObject *parent = 0); QString name() const; QString includeFile() const; QString group() const; QIcon icon() const; QString toolTip() const; QString whatsThis() const; bool isContainer() const; QWidget *createWidget(QWidget *parent); };

IconEditorPlugin altsnf, IconEditor paracn ieren bir factory snftr. QObject ve QDesignerCustonWidgetInterfaceten tretilmitir ve moca ikinci ana snfn bir eklenti arayz olduunu bildirmek iin Q_INTERFACES makrosunu kullanr. Qt Designer, snfn rneklerini oluturmak ve snf hakknda bilgi edinmek iin, u fonksiyonlar kullanr:
IconEditorPlugin::IconEditorPlugin(QObject *parent) : QObject(parent) { }

Kurucu nemsizdir.
QString IconEditorPlugin::name() const { return "IconEditor";

99

Qt 4 ile C++ GUI Programlama


}

name() fonksiyonu eklenti tarafndan salanan paracn adn dndrr.


QString IconEditorPlugin::includeFile() const { return "iconeditor.h"; }

includeFile() fonksiyonu, eklentinin ierdii paracn balk dosyasnn adn dndrr.


QString IconEditorPlugin::group() const { return tr("Image Manipulation Widgets"); }

group() fonksiyonu, zel paracn yesi olduu parack kutusu grubunun ismini dndrr. Eer isim henz kullanmda deilse, Qt Designer parack iin yeni bir grup oluturacaktr.
QIcon IconEditorPlugin::icon() const { return QIcon(":/images/iconeditor.png"); }

icon() fonksiyonu, Qt Designern parack kutusunda zel parac temsil etmekte kullanlacak olan simgeyi dndrr.
QString IconEditorPlugin::toolTip() const { return tr("An icon editor widget"); }

toolTip() fonksiyonu, Qt Designern parack kutusunda zel parack zerine fare ile gelinip beklendiinde grnecek olan ara ipucunu(tooltip) dndrr.
QString IconEditorPlugin::whatsThis() const { return tr("This widget is presented in Chapter 5 of <i>C++ GUI " "Programming with Qt 4</i> as an example of a custom Qt " "widget."); }

whatsThis() fonksiyonu, Qt Designern gstermesi iin Whats This?(Bu Nedir?) metnini dndrr.
bool IconEditorPlugin::isContainer() const { return false; }

isContainer() fonksiyonu eer parack dier paracklar ierebiliyorsa true; aksi halde, false dndrr. rnein, QFrame dier paracklar ierebilen bir paracktr. Genelde, her Qt parac dier paracklar ierebilir, fakat Qt Designer, isContainer() false dndrdnde buna izin vermez.
QWidget *IconEditorPlugin::createWidget(QWidget *parent) { return new IconEditor(parent); }

100

Qt 4 ile C++ GUI Programlama

Qt Designer, bir parack snfnn bir rneini oluturmak iin, createWidget() fonksiyonunu belirtilen ebeveynle arr.
Q_EXPORT_PLUGIN2(iconeditorplugin, IconEditorPlugin)

Eklenti snfn gerekletiren kaynak kodu dosyasnn sonunda, eklentiyi Qt Designerca kullanlabilir yapmak iin Q_EXPORT_PLUGIN2() makrosunu kullanmalyz. lk argman, eklentiye vermek istediimiz isimdir; ikinci argman ise onu gerekletiren snfn ismidir. Eklenti iin oluturulan .pro dosyas una benzer:
TEMPLATE CONFIG HEADERS SOURCES RESOURCES DESTDIR = lib += designer plugin release = ../iconeditor/iconeditor.h \ iconeditorplugin.h = ../iconeditor/iconeditor.cpp \ iconeditorplugin.cpp = iconeditorplugin.qrc = $$[QT_INSTALL_PLUGINS]/designer

qmake arac, baz n tanml deikenlere sahiptir. Onlardan biri, Qtun yklendii dizindeki plugins dizininin yolunu tutan $$[QT_INSTALL_PLUGINS]tir. Siz eklentiyi ina etmek iin make veya nmake girdiinizde, eklenti kendini otomatik olarak Qtun plugins/Designer dizinine ykleyecektir. Eklenti bir kere ina edildiinde, IconEditor parac Qt Designer iinde artk Qtun yerleik bir paracym gibi kullanlabilir. Eer birka zel parac Qt Designer ile birletirmek isterseniz, ya her biri iin bir eklenti oluturursunuz, ya da tmn QDesignerCustomWidgetCollectionInterfaceten tretilen tek bir eklentide birletirirsiniz.

101

Qt 4 ile C++ GUI Programlama

BLM 6: YERLEM YNETM

Bir forma yerletirilmi her parack, verilen uygun bir boyutta ve konumda olmaldr. Qt, paracklar bir forma yerletiren birka snf salar: QHBoxLayout, QVBoxLayout, QGridLayout ve QStackedLayout. Bu snflar ok kullanldr, yle ki hemen hemen her Qt gelitiricisi onlar ya direkt kaynak kodu iinde ya da Qt Designer iinden kullanr. Qtun yerleim snflarn(layout classes) kullanmann bir dier nedeni de, formlarn otomatik olarak farkl yaz tiplerine, dillere ve platformlara uymalarn salamalardr. Tm bu snflar, kullancnn deitirebilecei esnek bir yerleim salarlar. Bu blmde onlardan bahsedeceiz.

Paracklar Bir Form zerine Yerletirme


Bir form zerindeki ocuk paracklarn yerleimini ynetmenin basit yolu vardr: mutlak konumlama(absolute positioning), manel yerleim(manual layout) ve yerleim yneticileri(layout managers). Bu yaklamlarn her birini srasyla, ekil 6.1de gsterilen Find File diyalounda kullanrken inceleyeceiz.

ekil 6.1

Mutlak konumlama, paracklar yerletirmenin en ilkel yoludur. Tm konum ve boyut bilgileri elle kodlanarak girilir. te, mutlak konumlama kullanlarak yazlan bir FindFileDialog kurucusu:
FindFileDialog::FindFileDialog(QWidget *parent) : QDialog(parent) { ... namedLabel->setGeometry(9, 9, 50, 25); namedLineEdit->setGeometry(65, 9, 200, 25); lookInLabel->setGeometry(9, 40, 50, 25); lookInLineEdit->setGeometry(65, 40, 200, 25); subfoldersCheckBox->setGeometry(9, 71, 256, 23); tableWidget->setGeometry(9, 100, 256, 100); messageLabel->setGeometry(9, 206, 256, 25); findButton->setGeometry(271, 9, 85, 32); stopButton->setGeometry(271, 47, 85, 32);

102

Qt 4 ile C++ GUI Programlama


closeButton->setGeometry(271, 84, 85, 32); helpButton->setGeometry(271, 199, 85, 32); setWindowTitle(tr("Find Files or Folders")); setFixedSize(365, 240); }

Mutlak konumlamann birok dezavantaj vardr: Kullanc pencereyi yeniden boyutlandramaz. Eer kullanc olaand geni bir yaz tipi seerse ya da uygulama baka bir dile evrilirse, baz metinler kesik grnebilir. Paracklar baz stiller iin uygun olmayan boyutlara sahip olabilir. Konumlar ve boyutlar elle hesaplanmaldr. Bu skc ve hataya eilimlidir ve bakm zahmetli hale getirir. Mutlak konumlamaya bir alternatif manel yerleimdir. Manel yerleim ile paracklarn konumlar hl mutlaktr, fakat boyutlar pencerenin boyutuna orantl yaplr. Bu, formun resizeEvent() fonksiyonunu ocuk paracklarnn geometrisini ayarlamas iin uyarlayarak gerekletirilir: Kod Grnm:
FindFileDialog::FindFileDialog(QWidget *parent) : QDialog(parent) { ... setMinimumSize(265, 190); resize(365, 240); } void FindFileDialog::resizeEvent(QResizeEvent * /* event */) { int extraWidth = width() - minimumWidth(); int extraHeight = height() - minimumHeight(); namedLabel->setGeometry(9, 9, 50, 25); namedLineEdit->setGeometry(65, 9, 100 + extraWidth, 25); lookInLabel->setGeometry(9, 40, 50, 25); lookInLineEdit->setGeometry(65, 40, 100 + extraWidth, 25); subfoldersCheckBox->setGeometry(9, 71, 156 + extraWidth, 23); tableWidget->setGeometry(9, 100, 156 + extraWidth, 50 + extraHeight); messageLabel->setGeometry(9, 156 + extraHeight, 156 + extraWidth, 25); findButton->setGeometry(171 + extraWidth, 9, 85, 32); stopButton->setGeometry(171 + extraWidth, 47, 85, 32); closeButton->setGeometry(171 + extraWidth, 84, 85, 32); helpButton->setGeometry(171 + extraWidth, 149 + extraHeight, 85, 32); }

FindFileDialog kurucusunda, formun minimum boyutunu 265 x 190, ilk boyutunu 365 x 240 olarak ayarlarz. resizeEvent() ileyicisinde, bymesini istediimiz paraca ekstra boluk veririz. Bu, kullanc formu yeniden boyutlandrdnda, formun dzgnce leklenmesini salar.

103

Qt 4 ile C++ GUI Programlama Tpk mutlak yerletirme gibi, manel yerleim de, oka elle kod yazmay ve birok sabitin programc tarafndan hesaplanmasn gerektirir. Byle kod yazmak yorucudur, zelliklede tasarm deitiinde. Ve hl metnin kesik grnme riski vardr. Bu riskin nn almann bir yolu vardr, fakat o da kodu daha fazla karmaklatracaktr. Paracklar bir form zerine yerletirmenin en pratik yolu, Qtun yerleim yneticilerini kullanmaktr. Yerleim yneticileri, her tipten parack iin mantkl varsaylanlar salar ve her bir paracn yaztipine, stiline ve ieriine bal olan boyut ipucunu hesaba katar. Yerleim yneticileri minimum ve maksimum boyutlara da sayg gsterir ve yaztipi ve ierik deiikliklerine ve pencerenin yeniden boyutlandrlmasna karlk yerleimi otomatik olarak ayarlar. Find File diyalounun yeniden boyutlandrlabilir bir versiyonu ekil 6.2de gsteriliyor.

ekil 6.2

En nemli yerleim yneticisi QHBoxLayout, QVBoxLayout ve QGridLayouttur. Bu snflar, yerleimler iin salanan temel at(framework) olan QLayouttan tretilmitirler. Tm bu snflar, Qt Designer tarafndan tam olarak desteklenir ve ayrca kod iinde direkt olarak kullanlabilir. te, yerleim yneticilerini kullanarak yazlm FindFileDialog: Kod Grnm:
FindFileDialog::FindFileDialog(QWidget *parent) : QDialog(parent) { ... QGridLayout *leftLayout = new QGridLayout; leftLayout->addWidget(namedLabel, 0, 0); leftLayout->addWidget(namedLineEdit, 0, 1); leftLayout->addWidget(lookInLabel, 1, 0); leftLayout->addWidget(lookInLineEdit, 1, 1); leftLayout->addWidget(subfoldersCheckBox, 2, 0, 1, 2); leftLayout->addWidget(tableWidget, 3, 0, 1, 2); leftLayout->addWidget(messageLabel, 4, 0, 1, 2); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addWidget(findButton); rightLayout->addWidget(stopButton); rightLayout->addWidget(closeButton); rightLayout->addStretch(); rightLayout->addWidget(helpButton); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout);

104

Qt 4 ile C++ GUI Programlama


setWindowTitle(tr("Find Files or Folders")); }

Yerleim bir QHBoxLayout, bir QGridLayout ve bir QVBoxLayout tarafndan idare edilir. Soldaki QGridLayout ve sadaki QVBoxLayout dtaki QHBoxLayout tarafndan yan yana yerletirilmitir. Diyaloun evresindeki pay ve ocuk paracklar arasndaki boluk, geerli parack stiline bal olan varsaylan deerlere ayarlanr; dier taraftan QLayout::setContentsMargins() ve QLayout::setSpacing() kullanlarak deitirilebilirler.

ekil 6.3

Ayn diyalog, ocuk paracklar benzer konumlara yerletirilerek, grsel olarak Qt Designerda da oluturulabilir. Bu yaklam Blm 2de Spreadsheet uygulamasnn Go to Cell ve Sort diyaloglarn olutururken kullanmtk. QHBoxLayout ve QVBoxLayoutu kullanmak olduka kolaydr, fakat QGridLayoutu kullanmak biraz daha karktr. QGridLayout, iki boyutlu bir hcreler zgaras zerinde alr. Yerleimin sol st kesindeki QLabel (0, 0) konumundadr ve onunla ilikili QLineEdit (0, 1) konumundadr. QCheckBox iki stun kaplar; (2, 0) ve (2, 1) konumlarndaki hcrelere oturur. Onun altndaki QTreeWidget ve QLabel da iki stun kaplar. QGridLayout::addWidget() arsnn szdizimi yledir:
layout->addWidget(widget, row, column, rowSpan, columnSpan);

Burada, widget yerleim iine eklenecek olan ocuk parack, (row, column) parack tarafndan tutulan sol st hcre, rowSpan parack tarafndan tutulan satrlarn says, columnSpan ise parack tarafndan tutulan stunlarn saysdr. hmal edildiinde, rowSpan ve columnSpan argmanlarnn varsaylan 1dir. addStrecth() ars dikey yerleim yneticisine yerleim iinde o noktadaki boluun yok edilmesini syler. Bir genileme esi(stretch item) ekleyerek, yerleim yneticisine Close ve Help butonlar arasndaki fazlalk boluun tketilmesini syledik. Qt Designerda, ayn etkiyi bir ara para(spacer) ekleyerek meydana getirebiliriz. Ara paralar Qt Designerda mavi bir yay gibi grnr. Yerleim yneticilerini kullanmak, imdiye kadar da sz ettiimiz gibi, ek avantajlar salar. Eer bir parac bir yerleime eklersek ya da bir parac bir yerleimden silersek, yerleim otomatik olarak yeni duruma uyarlanacaktr. Bir ocuk parack zerinde hide() ya da show()u arrsak da ayns uygulanr. Eer bir ocuk paracn boyut ipucu deiirse, yerleim, yeni boyut ipucu dikkate alnarak otomatik olarak yeniden

105

Qt 4 ile C++ GUI Programlama yaplacaktr. Ayrca, yerleim yneticileri, formun ocuk paracklarnn minimum boyutlarn ve boyut ipularn dikkate alarak, otomatik olarak formun tm iin minimum bir boyut da ayarlar. imdiye kadar ortaya konulan rneklerde, sadece paracklar yerleimlerin iine koyduk ve fazlalk boluu tketmek iin de ara paralar kullandk. Baz durumlarda, yerleimin tamamen istediimiz gibi grnmesinde bu yeterli olmaz. Bu gibi durumlarda, yerletirmi olduumuz paracklarn boyut politikalarn(size policy) ve boyut ipularn(size hint) deitirerek, yerleimi belirleyebiliriz. Bir paracn boyut politikas, yerleim sistemine nasl genilemesi ve klmesini gerektiini syler. Qt, yerleik paracklarnn tm iin mantkl varsaylan boyut politikalar salar, fakat her olas yerleime tekabl edebilecek tek bir varsaylan olmad iin, gelitiriciler arasnda, form zerindeki bir ya da iki paracn boyut politikalarn deitirmek hl yaygndr. Bir QSizePolicy hem bir yatay hem de bir dikey bileene sahip olabilir. te, en kullanl deerler: Fixed, paracn byyp klemeyecei anlamna gelir. Parack daima boyut ipucunun belirttii boyutta kalr. Minimum, paracn minimum boyutunda olduu anlamna gelir. Parack boyut ipucunun daha altnda bir boyuta kltlemez, fakat eer gerekliyse kullanlabilir boluu doldurana kadar genileyebilir. Maximum, paracn maksimum boyutunda olduu anlamna gelir. Parack minimum boyut ipucuna kadar kltlebilir. Preferred paracn boyut ipucunun onun tercih edilen boyutu olduu anlamna gelir, fakat parack eer gerekliyse hl geniletilebilir ya da kltlebilir. Expanding paracn geniletilebilir ve kltlebilir olduu anlamna gelir. ekil 6.4, farkl boyut politikalarnn anlamlarn zetliyor.

ekil 6.4

ekilde, Preferred ve Expanding ayn tarzda resmedilmitir. yleyse, farkllk nedir? Hem Preferred hem de Expanding paracklar ieren bir form yeniden boyutlandrldnda, Preferred paracklar boyut ipularnn belirttii boyutta kalrken, ekstra boluk Expanding paracklara verilir. ki boyut politikas daha vardr: MinimumExpanding ve Ignored. Birincisi Qtun daha eski versiyonlarnda baz seyrek karlaan durumlarda gerekliydi, fakat fazla kullanl deildir; tercih edilen yaklam Expanding kullanmak ve mimimumSizeHint()i uygun ekilde uyarlamaktr. kincisi ise, paracn boyut ipucunu ve minimum boyut ipucunu grmezden gelmesi dnda, Expanding ile benzerdir.

106

Qt 4 ile C++ GUI Programlama Boyut politikalarnn yatay ve dikey bileenlerine ek olarak, QSizePolicy snf bir yatay ve bir de dikey genileme katsays(stretch factor) saklar. Bu genileme katsays, form genilediinde farkl ocuk paracklarn farkl lde genilemesi istediinizde, bunu belirtmede kullanlabilir. rnein, bir QTextEdit zerinde bir QTreeWidgeta sahipsek ve QTextEditin QTreeWidgetn iki kat uzunluunda olmasn istiyorsak, QTextEditin dikey genileme katsayn 2, QTreeWidgetnkini 1 olarak ayarlayabiliriz.

Yl Yerleimler
QStackedLayout snf, ocuk paracklarn bir setini veya sayfalar(pages) yerletirir ve bir seferde sadece birini gsterir, dierlerini kullancdan gizler. QStackedLayoutun kendisi grnmezdir. ekil 6.5teki kk oklar ve koyu gri ereve(frame), Qt Designer tarafndan yerleimi, bu yerleim yneticisiyle tasarlamay daha kolay hale getirmek iin salanr. Kolaylk olsun diye, Qt, yerleik bir QStackedLayout ile bir QWidget salayan, QStackedWidget da ierir.

ekil 6.5

Sayfalar 0dan balayarak numaralandrlr. Belirli bir ocuk parac grnr yapmak iin, setCurrentIndex()i bir sayfa numaras ile arabiliriz. Bir ocuk paracn sayfa numarasna indexOf() kullanlarak eriilebilir. ekil 6.6da gsterilen Preferences diyalou QStackedLayoutu kullanan bir rnektir. Diyalog, solda bir QListWidget ve sada bir QStackedLayouttan meydana gelir. QListWidgettaki her bir e QStackedLayouttaki farkl bir sayfaya tekabl eder. te, diyaloun kurucusuyla alkal kod:
PreferenceDialog::PreferenceDialog(QWidget *parent) : QDialog(parent) { ... listWidget = new QListWidget; listWidget->addItem(tr("Appearance")); listWidget->addItem(tr("Web Browser")); listWidget->addItem(tr("Mail & News")); listWidget->addItem(tr("Advanced")); stackedLayout = new QStackedLayout; stackedLayout->addWidget(appearancePage); stackedLayout->addWidget(webBrowserPage); stackedLayout->addWidget(mailAndNewsPage); stackedLayout->addWidget(advancedPage); connect(listWidget, SIGNAL(currentRowChanged(int)), stackedLayout, SLOT(setCurrentIndex(int))); ... listWidget->setCurrentRow(0); }

107

Qt 4 ile C++ GUI Programlama

ekil 6.6

Bir QListWidget olutururuz ve onu sayfa isimleriyle doldururuz. Sonra bir QStackedLayout olutururuz ve her bir sayfa iin addWidget() arrz. Liste paracnn currentRowChanged(int) sinyalini -sayfa deitirmeyi gerekletirmek iin- yl yerleimin setCurrentIndex(int) yuvasna balarz ve kurucunun sonunda, 0. sayfadan balamak iin, liste parac zerinde setCurrentRow()u arrz. Bunun gibi formalar Qt Designer kullanarak oluturmak da ok kolaydr: 1. Dialog ablonlarn ya da Widget ablonunu baz alan yeni bir form oluturun. 2. Forma bir QListWidget ve bir QStackedWidget ekleyin. 3. Her bir sayfay ocuk paracklar ve yerleimlerle doldurun. (Yeni bir sayfa oluturmak iin, sa tklayn ve Insert Pagei sein; sayfalar deitirmek iin, QStackedWidgetn sa stndeki oklara tklayn.) 4. Paracklar bir yatay yerleim kullanarak yan yana yerletirin. 5. Liste paracnn currentRowChanged(int) sinyalini yl yerleimin setCurrentIndex(int) yuvasna balayn. 6. Paracn currentRow niteliinin deerini 0 olarak ayarlayn. Sayfa deitirmeyi nceden tanmlanm sinyal ve yuvalar kullanarak geekletirdiimiz iin, diyalog Qt Designerda nizlendiinde doru davran sergileyecektir. Sayfalarn says kk olduu ve byk ihtimalle kk kalaca durumlar iin, bir QStackedWidget ve QListWidget kullanmaya daha basit bir alternatif bir QTabWidget kullanmaktr.

Blcler
Bir QSplitter dier paracklar ieren bir paracktr. Bir blc(splitter) iindeki paracklar, blc kollar(splitter handles) tarafndan ayrlmlardr. Kullanclar blc kollar srkleyerek, blcnn ocuk paraclarnn boyutlarn deitirebilirler. Blcler sklkla, kullancya daha fazla kontrol salamak iin, yerleimlere bir alternatif olarak da kullanlrlar. Bir QSplittern ocuk paracklar, oluturulma sralarna gre yan yana (ya da biri dierinin altnda) komu paracklar arasndaki blc kollarla otomatik olarak yerletirilirler. te, ekil 6.7de gsterilen pencereyi oluturmak iin kod:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTextEdit *editor1 = new QTextEdit;

108

Qt 4 ile C++ GUI Programlama


QTextEdit *editor2 = new QTextEdit; QTextEdit *editor3 = new QTextEdit; QSplitter splitter(Qt::Horizontal); splitter.addWidget(editor1); splitter.addWidget(editor2); splitter.addWidget(editor3); ... splitter.show(); return app.exec(); }

ekil 6.7

rnek, bir QSplitter parac tarafndan yatay olarak yerletirilmi QTextEditten oluur. Bu, ekil 6.8de ematik olarak gsterilmitir. QSplitter, QWidgettan tretilmitir ve bu nedenle dier her parack gibi kullanlabilir.

ekil 6.8

Yatay ve dikey QSplitterlar i ie koyarak karmak yerleimler meydana getirilebilir. rnein, ekil 6.9da gsterilen Mail Client uygulamas, dikey bir QSplitter ieren yatay bir QSplitterdan meydana gelir. Yerleimi, ekil 6.10da ematik olarak gsteriliyor.

ekil 6.9

109

Qt 4 ile C++ GUI Programlama

ekil 6.10

te, Mail Client uygulamasnn QMainWindow altsnfnn kurucusundaki kod:


MailClient::MailClient() { ... rightSplitter = new QSplitter(Qt::Vertical); rightSplitter->addWidget(messagesTreeWidget); rightSplitter->addWidget(textEdit); rightSplitter->setStretchFactor(1, 1); mainSplitter = new QSplitter(Qt::Horizontal); mainSplitter->addWidget(foldersTreeWidget); mainSplitter->addWidget(rightSplitter); mainSplitter->setStretchFactor(1, 1); setCentralWidget(mainSplitter); setWindowTitle(tr("Mail Client")); readSettings(); }

Grntlemek istediimiz parac oluturduktan sonra, dikey bir blc olutururuz (rightSplitter) ve sada grntlemek istediimiz iki parac ona ekleriz. Sonra da yatay bir blc olutururuz (mainSplitter) ve solda grntlemek istediimiz parac ve rightSplitter ona ekleriz. Sonrasnda, mainSplitter QMainWindowun merkez parac yaparz. Kullanc bir pencereyi yeniden boyutlandrdnda, QSplitter normal olarak boluu datr, bylece ocuk paracklarn greceli boyutlar ayn kalr. Mail Client rneinde, bu davran istemeyiz; yerine, QTreeWidget ve QTableWidgetn boyutlarnn ayn kalmasn ve ekstra boluun QTextedite verilmesini isteriz. Bu, iki setScrerchFactor() ars sayesinde baarlr. lk argman blcnn ocuk paracnn indeksi, ikinci argman ayarlamak istediimiz genileme katsaysdr (varsaylan 0dr). lk setStretchFactor() ars rightSplitter zerindedir ve 1 konumundaki paracn (textEdit) genileme katsaysn 1 olarak ayarlar. kinci setStretchFactor() ars mainSplitter zerindedir ve bir konumundaki paracn (rightSplitter) genileme katsaysn 1 olarak ayarlar. Bu, textEditin kullanlabilir fazlalk boluun tmn almasn salar. Uygulama balatldnda, QSplitter ocuk paracklarn ilk boyutlarna bal olarak (ya da eer ilk boyut yoksa boyut ipularna bal olarak) uygun boyutlar verir. Blc kollarn programlanabilir bir biimde QSplitter::setSizes() ararak hareket ettirebiliriz. QSplitter snf ayn zamanda kendi

110

Qt 4 ile C++ GUI Programlama durumunun kaydedilmesi ve saklanmas iin bir ara da salar. te, Mail Clientn ayarlarn kaydeden writeSettings() fonksiyonu:
void MailClient::writeSettings() { QSettings settings("Software Inc.", "Mail Client"); settings.beginGroup("mainWindow"); settings.setValue("geometry", saveGeometry()); settings.setValue("mainSplitter", mainSplitter->saveState()); settings.setValue("rightSplitter", rightSplitter->saveState()); settings.endGroup(); }

Ve ite, ona uygun bir readSettings() fonksiyonu:


void MailClient::readSettings() { QSettings settings("Software Inc.", "Mail Client"); settings.beginGroup("mainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); mainSplitter->restoreState( settings.value("mainSplitter").toByteArray()); rightSplitter->restoreState( settings.value("rightSplitter").toByteArray()); settings.endGroup(); }

Qt Designer QSplitter tamamen destekler. Paracklar bir blc iine koymak iin, paracklar arzu edilen konumlarna yaklak olarak yerletirin, onlar sein ve Form > Lay Out Horizontally in Splitter ya da Form > Lay Out Vertically in Splitter sein.

Kaydrlabilir Alanlar
QScrollArea snf bir kaydrlabilir grnt kaps(scrollable viewport) ve iki kaydrma ubuu(scroll bar) salar. Eer bir paraca kaydrma ubuklar eklemek istersek, kendi QScrollBarlarmz somutlatrmak ve kaydrma ilevlerini kendimiz gerekletirmektense, bir QScrollArea kullanmak daha basittir. QScrollArea kullanmann yolu, kaydrma ubuu eklemek istediimiz parack ile setWidget() armaktr. QScrollArea, parac otomatik olarak grnt kapsnn (QScrollArea::viewport() sayesinde eriilebilir) bir ocuu yapar, tabi eer henz yaplmamsa. rnein, eer IconEditor parac etrafnda kaydrma ubuklar istiyorsak, unu yazabiliriz:
int main(int argc, char *argv[]) { QApplication app(argc, argv); IconEditor *iconEditor = new IconEditor; iconEditor->setIconImage(QImage(":/images/mouse.png")); QScrollArea scrollArea; scrollArea.setWidget(iconEditor); scrollArea.viewport()->setBackgroundRole(QPalette::Dark); scrollArea.viewport()->setAutoFillBackground(true); scrollArea.setWindowTitle(QObject::tr("Icon Editor")); scrollArea.show();

111

Qt 4 ile C++ GUI Programlama


return app.exec(); }

ekil 6.11

QScrollArea (ekil 6.12de ematik olarak gsteriliyor), parac, paracn geerli boyutunda ya da eer parack henz yeniden boyutlandrlmamsa boyut ipucunu kullanarak sunar. setWidgetResizable(true)yu ararak, QSrollAreaya, boyut ipucu haricindeki ekstra boluun avantajn elde etmesi iin, parac otomatik olarak yeniden boyutlandrmasn syleyebiliriz.

ekil 6.12

Varsaylan olarak, kaydrma ubuklar sadece grnt kaps ocuk paracktan kk olduunda gsterilir. Kaydrma ubuu politikalarn ayarlayarak, kaydrma ubuklarnn her zaman gsterilmesini mecbur klabiliriz.
scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

QScrollArea birok ilevini QAbstractScrollAreadan miras almtr. QTextEdit ve QAbstractItemView (Qtun e grntleme snflarnn temel snf) gibi snflar da QAbstractScrollAreadan tretilmitir, bu nedenle kayrma ubuklar edinmek iin onlar bir QScrollArea iinde paketlememiz gerekmez.

Yapkan Pencereler ve Ara ubuklar


Yapkan pencereler(dock windows) bir QMainWindow ierisinde, yapkan pencere alanlarna(dock window area) yapabilen ya da bamsz olarak dolaabilen pencerelerdir. QMainWindow, merkez paracn yukarsnda, altnda, solunda ve sanda olmak zere toplam drt adet yapkan pencere alan salar. Microsoft Visual Studio ve Qt Linguist gibi uygulamalar esnek bir kullanc arayz salamak iin yapkan pencereleri geni lde kullanrlar. Qtda yapkan pencereler, QDockWidgetn rnekleridirler. ekil 6.13te ara ubuklar ve bir yapkan penceresi olan bir Qt uygulamas gsteriliyor. 112

Qt 4 ile C++ GUI Programlama

6.13

Her yapkan pencere, yaptrldnda dahi kendi balk ubuuna sahiptir. Kullanclar yapkan bir pencereyi balk ubuundan srkleyerek, bir yapma alanndan bir dierine tayabilirler. Ayrca, yapkan pencereyi herhangi bir yapma alannn dna tayarak, yapkan pencerenin bamsz bir pencere olarak dolamas da salanlabilir. Serbest yzen(Free-floating) yapkan pencereler her zaman ana pencerelerinin en st seviyesindedirler. Kullanclar bir QDockWidget pencerenin balk ubuundaki kapatma butonuna tklayarak kapatabilirler. Bu zelliklerin herhangi biri QDockWidget::setFeatures() arlarak devre d braklabilir. Qtun daha evvelki versiyonlarnda, ara ubuklar yapkan pencereler gibi davranrdlar ve ayn yapma alanlarn kullanrlard. Qt 4 ile balayarak, ara ubuklar merkez parack etrafndaki kendi alanlarna otururlar (ekil 6.14te grld gibi) ve kendi alanlarndan kopartlamazlar. Eer yzen bir ara ubuu gerekirse, onu kolaylkla bir QDockWidget ierisine koyabiliriz.

ekil 6.14

113

Qt 4 ile C++ GUI Programlama Noktal izgilerle gsterilen keler, bitiik yapma alanlarndan her ikisine de ait olabilir. rnein, QMainWindow::setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea)y ararak sol st keyi sol yapma alanna ait yapabilirdik. Sradaki kod paras, var olan bir paracn (bu durumda bir QTreeWidget) bir QDockWidget iine nasl paketleneceini ve sa yapma alan iine nasl ekleneceini gsterir:
QDockWidget *shapesDockWidget = new QDockWidget(tr("Shapes")); shapesDockWidget->setObjectName("shapesDockWidget"); shapesDockWidget->setWidget(treeWidget); shapesDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::RightDockWidgetArea, shapesDockWidget);

setAllowedAreas() ars, yapma alanlarnn yapkan pencere kabul edebilme kstlamalarn aka belirtir. Burada, kullancnn sadece, paracn makul bir ekilde grntlenmesi iin yeterli dikey bolua sahip olan sol ve sa yapma alanlarna yapkan pencere srklemesine izin verdik. Eer izin verilmeyen alanlar ak bir ekilde belirtilmemise, kullanc yapkan pencereleri drt alana da srkleyebilir. Her QObjecte bir nesne ismi(object name) verilebilir. Bu isim hata ayklamada kullanl olabilir ve baz test aralar tarafndan kullanlabilir. Normalde, paracklara nesne isimleri verme zahmetine girmeyiz, fakat yapkan pencere ve ara ubuklar oluturduumuzda, eer yapkan pencere ve ara ubuklarnn geometrilerini ve konumlarn kaydetmek ve geri yklemek iin QMainWindow::saveState() ve QMainWindow::restoreState()i kullanmak istiyorsak, bunu yapmamz gereklidir. te, QMainWindow altsnfnn kurucusundan, bir QComboBox, bir QSpinBox ve birka QToolButtons ieren bir ara ubuunun nasl oluturulduu gsteren kod paras:
QToolBar *fontToolBar = new QToolBar(tr("Font")); fontToolBar->setObjectName("fontToolBar"); fontToolBar->addWidget(familyComboBox); fontToolBar->addWidget(sizeSpinBox); fontToolBar->addAction(boldAction); fontToolBar->addAction(italicAction); fontToolBar->addAction(underlineAction); fontToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); addToolBar(fontToolBar);

Eer tm yapkan pencerelerin ve ara ubuklarnn konumlarn kaydedebilmek ve bylece uygulamann bir sonraki almasnda onlar geri ykleyebilmek istersek, bir QSplittern konumunu, QMainWindowun saveState() ve restoreState() fonksiyonlarn kullanarak kaydetmekte kullandmz koda benzer bir kod yazabiliriz:
void MainWindow::writeSettings() { QSettings settings("Software Inc.", "Icon Editor"); settings.beginGroup("mainWindow"); settings.setValue("geometry", saveGeometry()); settings.setValue("state", saveState()); settings.endGroup(); }

114

Qt 4 ile C++ GUI Programlama


void MainWindow::readSettings() { QSettings settings("Software Inc.", "Icon Editor"); settings.beginGroup("mainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("state").toByteArray()); settings.endGroup(); }

Son olarak, QMainWindow, tm yapkan pencereleri ve ara ubuklarn listeleyen bir balam men salar. Bu men ekil 6.15te gsteriliyor. Kullanc bu meny kullanarak yapkan pencereleri kapatabilir ve geri ykleyebilir ya da ara ubuklarn gizleyebilir ve geri ykleyebilir.

ekil 6.15

oklu Dokman Arayz


Ana penceresinin merkez alan iinde oklu dokmanlar salayan uygulamalara oklu dokman arayz uygulamalar, ya da ksaca ngilizce karlnn ksaltmas olan MDI(Multiple Document Interface) uygulamalar ad verilir. Qtda, MDI uygulamalar merkez parack olarak QMdiArea kullanarak ve her bir dokman penceresini bir QMdiArea altpenceresi(subwindow) yaparak oluturulur. MDI uygulamalar iin, hem pencereleri hem de pencerelerin listesini ynetmek iin baz komutlar ieren bir Window mens salamak geleneksel bir davrantr. Aktif pencere bir onay imi(check mark) ile belirtilir. Kullanc herhangi bir pencerenin Window mensndeki girdisine tklayarak onu aktif edebilir. Bu ksmda, bir MDI uygulamasnn nasl oluturulduunu ve Window mensnn nasl gerekletirildiini gstermek iin, ekil 6.16da grnen MDI Editor uygulamasn gelitireceiz. Uygulamann tm menleri ekil 6.17de gsteriliyor.

115

Qt 4 ile C++ GUI Programlama


ekil 6.16

ekil 6.17

Uygulama iki snftan meydana gelir: MainWindow ve Editor. Kodun ou Spreadsheet uygulamasnnkinin ayns ya da benzeri olduu iin, biz sadece MDI ile alkal kodu sunacaz. MainWindow snf ile balayalm.
MainWindow::MainWindow() { mdiArea = new QMdiArea; setCentralWidget(mdiArea); connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); createActions(); createMenus(); createToolBars(); createStatusBar(); setWindowIcon(QPixmap(":/images/icon.png")); setWindowTitle(tr("MDI Editor")); QTimer::singleShot(0, this, SLOT(loadFiles())); }

MainWindow kurucusunda, bir QMdiArea olutururuz ve onu merkez parack yaparz. QMdiAreann subWindowActivated() sinyalini, pencere mensn gncel tutmada kullanacamz yuvaya balarz ve eylemlerin, uygulamann durumuna bal olarak etkin ya da devre d olmasn salarz. Kurucunun sonunda, bir tek at zamanlaycsn(single-shot timer) 0 milisaniyelik bir arayla loadFiles() fonksiyonunu armas iin ayarlarz. Bunun gibi zamanlayclar, olay dngs bota olduunda mola verir. Pratikte bunun anlam; kurucu bitecek ve ana pencere gsterildikten sonra loadFiles() arlacak. Eer bunu yapmazsak ve yklenecek ok byk dosyalar varsa, kurucu tm dosyalar yklenene kadar bitmeyecektir ve bu arada, kullanc ekranda hibir ey grmeyecektir ve belki de uygulamann balamasnn baarsz olduunu dnecektir.
void MainWindow::loadFiles() { QStringList args = QApplication::arguments(); args.removeFirst(); if (!args.isEmpty()) { foreach (QString arg, args) openFile(arg); mdiArea->cascadeSubWindows(); } else { newFile();

116

Qt 4 ile C++ GUI Programlama


} mdiArea->activateNextSubWindow(); }

Eer kullanc uygulamay komut satrnda(command line) bir ya da daha fazla dosya ismiyle balatrsa, bu fonksiyon her bir dosyay yklemeyi dener. yle ki altpencereler ellesinin sonunda kullanc onlar kolaylkla grebilir. Qtun kendine zg -style ve -font gibi komut satr seenekleri QApplicatin kurucusu tarafndan argman listesinden otomatik olarak silinir. Bu nedenle, eer komut satrna unu yazarsak:
mdieditor -style motif readme.txt

QApplication::arguments() iki e(mdieditor ve readme.txt) ieren bir QStringList dndrr ve MDI Editor uygulamas, readme.txt dokman ile birlikte alr. Eer komut satrnda hibir dosya belirtilmemise, yeni, bo bir editr altpenceresi oluturulur, yle ki kullanc hemen yazmaya balayabilir. activateNextSubWindow() ars, bir editr penceresine odaklanld anlamndadr ve updateActions() fonksiyonunun Window mensn gncellemesini ve uygulamann durumuna gre eylemlerin etkinletirilmesini veya devre d brakmasn salar.
void MainWindow::newFile() { Editor *editor = new Editor; editor->newFile(); addEditor(editor); }

newFile() yuvas File > New men seeneine tekabl eder. Bir Editor parac oluturur ve onu addEditor() private fonksiyonuna aktarr.
void MainWindow::open() { Editor *editor = Editor::open(this); if (editor) addEditor(editor); }

open() fonksiyonu File > Opena tekabl eder. Bir dosya diyalou belirmesini salayan Editor::open() statik fonksiyonunu arr. Eer kullanc bir dosya seerse, yeni bir Editor oluturulur, dosyann metni iine okunur ve eer okuma baarl ise, Editora iaret eden bir iareti dndrlr. Eer kullanc dosya diyalounu iptal ederse, bo bir iareti dndrlr ve kullanc bundan haberdar edilir. Dosya operasyonlarn Editor snf iinde gerekletirmek, MainWindow snf iinde gerekletirmekten daha mantkldr, nk her bir Editorn kendi bamsz durumunu srdrmesi gerekir.
void MainWindow::addEditor(Editor *editor) { connect(editor, SIGNAL(copyAvailable(bool)), cutAction, SLOT(setEnabled(bool))); connect(editor, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool))); QMdiSubWindow *subWindow = mdiArea->addSubWindow(editor); windowMenu->addAction(editor->windowMenuAction()); windowActionGroup->addAction(editor->windowMenuAction()); subWindow->show(); }

117

Qt 4 ile C++ GUI Programlama

addEditor() private fonksiyonu yeni bir Editor paracnn ilk kullanma hazrlanmasn tamamlamak iin newFile() ve open()dan arlr. ki sinyal-yuva balants kurarak balar. Bu balantlar, Edit > Cut ve Edit > Copy men seeneklerinin, seilmi metin olup olmamasna bal olarak etkinletirmesini ya da devre d braklmasn salar. MDI kullanmamzdan dolay, birok Editor paracnn kullanmda olacak olmas ihtimal dhilindedir. Biz sadece, aktif Editor penceresinden gelecek copyAvaiable(bool) sinyalinin yantyla ilgilendiimiz ve dierlerinden gelecek cevapla ilgilenmediimiz iin, bu kayglanlacak bir eydir. Fakat bu sinyaller sadece ve daima aktif pencereler tarafndan yaylabilirler; dolaysyla bu, pratikte bir sorun oluturmaz. QMdiArea::addSubWindow() fonksiyonu yeni bir QMdiSubWindow oluturur, parac altpencere iine bir parametre olarak aktararak ykler ve altpencereyi dndrr. Sonra, Window mensnde, pencereyi temsil eden bir QAction olutururuz. Eylem, biraz sonra deineceimiz Editor snf tarafndan salanr. Ayrca eylemi bir QActionGroup nesnesine ekleriz. QActionGroup bir seferde sadece bir Window mens esinin iaretlenmi olmasn salar. Son olarak, yeni QMdiSubWindowu grnr yapmak iin zerinde show() fonksiyonunu arrz.
void MainWindow::save() { if (activeEditor()) activeEditor()->save(); }

save() yuvas, eer aktif bir editr varsa, o aktif editr zerinde Editor::save()i arr. Yine, kodun yapt gerek i Editor snf iine yerletirilmitir.
Editor *MainWindow::activeEditor() { QMdiSubWindow *subWindow = mdiArea->activeSubWindow(); if (subWindow) return qobject_cast<Editor *>(subWindow->widget()); return 0; }

activeEditor() private fonksiyonu aktif altpencere iinde tutulan parac bir Editor iaretisi olarak dndrr. Eer aktif altpencere yoksa bo bir iareti dndrr.
void MainWindow::cut() { if (activeEditor()) activeEditor()->cut(); }

cut() yuvas aktif editr zerinde Editor::cut() arr. Burada copy() ve paste() yuvalarn gstermiyoruz nk onlar da ayn modeli izliyorlar.
void MainWindow::updateActions() { bool hasEditor = (activeEditor() != 0); bool hasSelection = activeEditor() && activeEditor()->textCursor().hasSelection(); saveAction->setEnabled(hasEditor);

118

Qt 4 ile C++ GUI Programlama


saveAsAction->setEnabled(hasEditor); cutAction->setEnabled(hasSelection); copyAction->setEnabled(hasSelection); pasteAction->setEnabled(hasEditor); closeAction->setEnabled(hasEditor); closeAllAction->setEnabled(hasEditor); tileAction->setEnabled(hasEditor); cascadeAction->setEnabled(hasEditor); nextAction->setEnabled(hasEditor); previousAction->setEnabled(hasEditor); separatorAction->setVisible(hasEditor); if (activeEditor()) activeEditor()->windowMenuAction()->setChecked(true); }

subWindowActivated() sinyali, yeni bir altpencere aktif olduunda ya da son altpencere kapatldnda (bu durumda, parametresi bo bir iaretidir) yaylr. Bu sinyal updateActions() yuvasna balanr. Men seeneklerinin ou eer aktif bir pencere varsa anlam kazanr, bu nedenle, aktif bir pencere olmadnda, onlar devre d brakrz. Sonda, aktif pencereyi temsil eden QAction zerinde setChecked()i arrz. QActionGroupa krler olsun ki, nceki aktif pencerenin onay imini aka kaldrmamz gerekmez.
void MainWindow::createMenus() { ... windowMenu = menuBar()->addMenu(tr("&Window")); windowMenu->addAction(closeAction); windowMenu->addAction(closeAllAction); windowMenu->addSeparator(); windowMenu->addAction(tileAction); windowMenu->addAction(cascadeAction); windowMenu->addSeparator(); windowMenu->addAction(nextAction); windowMenu->addAction(previousAction); windowMenu->addAction(separatorAction); ... }

createMenus() private fonksiyonu, Window mensn eylemlerle doldurur. Tm eylemler, menlerin tipik bir rnekleridirler ve QMdiAreann closeActiveSubWindow(), closeAllSubWindows(), tileSubWindows() ve cascadeSubWindows() yuvalar kullanlarak kolayca gerekletirilebilirler. Kullancnn at her yeni pencere Window mensnn eylemler listesine eklenir. (Bu, addEditor() fonksiyonu iinde yaplr.) Kullanc bir editr penceresi kapattnda, pencereye ilikin eylem silinir (eylem, editr penceresi tarafndan sahiplenildiinden dolay) ve bylece eylem otomatik olarak Window mensnden de silinmi olur.
void MainWindow::closeEvent(QCloseEvent *event) { mdiArea->closeAllSubWindows(); if (!mdiArea->subWindowList().isEmpty()) { event->ignore(); } else { event->accept(); }

119

Qt 4 ile C++ GUI Programlama


}

closeEvent() fonksiyonu, tm altpencereleri kapatmaya uyarlanr. Eer alt-pencerelerden biri kendi kapatma olayn yok sayarsa(ignore) (nk kullanc kaydedilmemi deiiklikler mesaj kutusunu iptal etmitir), MainWindow iin olan kapatma olayn yok sayarz; aksi halde, kabul ederiz, ve sonucunda da tm uygulama kapatlr. Eer MainWindow iinde closeEvent()i uyarlamasaydk, kullancya kaydedilmemi deiiklikleri kaydetme frsat verilmeyecekti. Artk MainWindow kritiini bitirdik; demek ki Editorn gerekletirimine ilerleyebiliriz. Editor snf bir altpencereyi temsil eder. Metin dzenleme ilevleri salayan QTextEditten tretilmitir. Her Qt paracnn bamsz (stand-alone) bir pencere olarak kullanlabilmesi gibi, her Qt parac bir QMdiSubWindow iine yerletirilebilir ve MDI alan iinde bir altpencere olarak kullanlabilir. te, snf tanm: Kod Grnm:
class Editor : public QTextEdit { Q_OBJECT public: Editor(QWidget *parent = 0); void newFile(); bool save(); bool saveAs(); QSize sizeHint() const; QAction *windowMenuAction() const { return action; } static Editor *open(QWidget *parent = 0); static Editor *openFile(const QString &fileName, QWidget *parent = 0); protected: void closeEvent(QCloseEvent *event); private slots: void documentWasModified(); private: bool okToContinue(); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); bool readFile(const QString &fileName); bool writeFile(const QString &fileName); QString strippedName(const QString &fullFileName); QString curFile; bool isUntitled; QAction *action; };

Spreadsheet uygulamasnn MainWindow snfndaki private fonksiyonlarn drd Editor snf iinde de yer alyor: okToContinue(), saveFile(), setCurrentFile() ve strippedName().

120

Qt 4 ile C++ GUI Programlama


Editor::Editor(QWidget *parent) : QTextEdit(parent) { action = new QAction(this); action->setCheckable(true); connect(action, SIGNAL(triggered()), this, SLOT(show())); connect(action, SIGNAL(triggered()), this, SLOT(setFocus())); isUntitled = true; connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); setWindowIcon(QPixmap(":/images/document.png")); setWindowTitle("[*]"); setAttribute(Qt::WA_DeleteOnClose); }

nce, uygulamann Window mensnde editr temsil eden bir QAction olutururuz ve eylemi show() ve setFocus() yuvalarna balarz. Kullancya istedii kadar editr penceresi oluturma imkn verdiimiz iin, onlar isimlendirmek iin baz hazrlklar yapmalyz. Bunu kvrmann yaygn bir yolu, bir say ieren isimlerle onlar ayrmaktr (document1.txt gibi). Kullancdan salanan isim ile bizim programlanabilir bir biimde oluturduumuz ismi ayrt etmek iin isUntitled deikenini kullanrz. Metin belgesinin contentsChanged() sinyalini, documentWasModified() yuvasna balarz. Bu yuva sadece setWindowModified(true)yu arr. Son olarak, kullanc bir Editor penceresini kapattnda, bellek szntlarna engel olmak iin Qt::WA_DeleteOnClose niteliini ayarlarz.
void Editor::newFile() { static int documentNumber = 1; curFile = tr("document%1.txt").arg(documentNumber); setWindowTitle(curFile + "[*]"); action->setText(curFile); isUntitled = true; ++documentNumber; }

newFile() fonksiyonu yeni dokman iin document1.txt gibi bir isim retir. Kod, kurucu yerine, newFile() iinde yer alr, nk var olan bir dokman amak iin open() ardmzda, numaralar ziyan etmek istemeyiz. documentNumber statik bildirildii iin, tm Editor rnekleri arasnda paylalr. Yeni dosya oluturmaya ek olarak, kullanclar sklkla, bir dosya diyaloundan ya da son alan dosyalar listesi gibi bir listeden seerek, var olan dosyalar amak ister. ki statik fonksiyon bunlar salar: open() dosya sisteminden bir dosya ismi semeye yarar ve openFile() bir Editor rnei oluturur ve belirtilen dosyann ieriini onun iine okur.
Editor *Editor::open(QWidget *parent) { QString fileName =

121

Qt 4 ile C++ GUI Programlama


QFileDialog::getOpenFileName(parent, tr("Open"), "."); if (fileName.isEmpty()) return 0; return openFile(fileName, parent); }

open()statik fonksiyonu, kullancnn, iinden bir dosya seebilecei bir dosya diyalou belirmesini salar. Eer bir dosya seilmise, bir Editor oluturmas iin openFile() arlr ve dosyann ierii onun iine okunur.
Editor *Editor::openFile(const QString &fileName, QWidget *parent) { Editor *editor = new Editor(parent); if (editor->readFile(fileName)) { editor->setCurrentFile(fileName); return editor; } else { delete editor; return 0; } }

Statik fonksiyon yeni bir Editor parac oluturarak balar ve sonra belirtilen sayfay iine okumay dener. Eer okuma baarlysa, Editor dndrlr; aksi halde, kullanc problem hakknda bilgilendirilir (readFile() iinde), editr silinir ve bo bir iareti dndrlr.
bool Editor::save() { if (isUntitled) { return saveAs(); } else { return saveFile(curFile); } }

save() fonksiyonu saveFile()m, yoksa saveAs()imi aracan belirlemek iin isUntitled deikenini kullanr.
void Editor::closeEvent(QCloseEvent *event) { if (okToContinue()) { event->accept(); } else { event->ignore(); } }

closeEvent() fonksiyonu kullancya kaydedilmemi deiiklikleri kaydetme imkn vermek iin uyarlanr. Mantk, kullancya Do you want to save your changes? sorusunu ynelten bir mesaj kutusu belirmesini salayan okToContinue() fonksiyonu iinde kodlanr. Eer okToContinue() true dndrrse, kapatma olayn kabul ederiz; aksi halde, olay yok sayarz ve pencereye herhangi bir etkide bulunmadan, olaydan vazgeeriz.
void Editor::setCurrentFile(const QString &fileName) {

122

Qt 4 ile C++ GUI Programlama


curFile = fileName; isUntitled = false; action->setText(strippedName(curFile)); document()->setModified(false); setWindowTitle(strippedName(curFile) + "[*]"); setWindowModified(false); }

setCurrentFile() fonksiyonu, curFile ve isUntitled deikenlerini gncellemek, pencere baln ve eylem metnini ayarlamak ve dokmann modified(deitirilmi) bayran false olarak ayarlamak iin openFile() ve saveFile()dan arlr. Kullanc, editr iindeki metni deitirdiinde, sz konusu QTextDocuments, contentsChanged() sinyalini yayar ve i modified bayran true olarak ayarlar.
QSize Editor::sizeHint() const { return QSize(72 * fontMetrics().width('x'), 25 * fontMetrics().lineSpacing()); }

Son olarak, sizeHint() fonksiyonu, x harfinin geniliini ve bir metin satrnn yksekliini baz alan bir boyut dndrr. QMdiArea, boyut ipucunu, pencereye ilk boyutunu vermede kullanr.

BLM 7: OLAY LEME

Olaylar(events), pencere sistemi ya da Qtun kendisi tarafndan, eitli olulara karlk retilirler. Kullanc klavyeden bir tua ya da bir fare butonuna bastnda ya da onu serbest braktnda, bir tu olay(key event) ya da bir fare olay(mouse event) retilir; bir pencere ilk kez gsteriliinde, pencere iin bir iz olay retilir. Olaylarn birou kullanc eylemlerine cevaben retilirler, fakat zamanlayc olaylar(timer events) gibi bazlar, sistemden bamsz olarak retilirler. Qt ile programlarken, olaylar hakknda nadiren dnmemiz gerekir, nk Qt paracklar nemli bir ey olduunda sinyaller yayarlar. Olaylar, kendi zel paracklarnz yazdnzda ya da var olan Qt paracklarnn davrann deitirmek istediinizde kullanl olurlar. Olaylar sinyallerle kartrlmamaldr. Bir kural olarak, sinyaller bir parac kullanrken kullanldrlar, oysa olaylar bir parack gerekletirirken kullanldrlar. rnein, QPushButton kullanyorken, fare ya da klavye olaylar yerine, sinyal yaylabilmesi iin onun clicked() sinyaliyle ilgileniriz. Fakat QPushButton gibi bir snf gerekletiriyorsak, fare ve tu olaylarn ileyen ve gerektiinde clicked() sinyalini yayan kodu yazmamz gerekir.

Olay leyicilerini Uyarlama


Qtda, bir olay, bir QEvent altsnf rneidir. Qt, her biri bir enum deer olarak tanmlanm yzden fazla olay tipini iler. rnein, QEvent::type() fare butonuna basma olaylar iin QEvent::MouseButtonPressi dndrr. 123

Qt 4 ile C++ GUI Programlama Olay tiplerinin birou, sade bir QEvent nesnesinin saklayabileceinden daha fazla bilgi gerektirir; rnein, fare butonuna basma olaylar, olay hangi fare butonunun tetiklendiini saklamaya ihtiya duymas bir tarafa olay meydana geldiinde fare iaretisinin konumunun pozisyonunun neresi olduunu dahi saklamaya ihtiya duyar. Bu ek bilgi, QMouseEvent gibi zel QEvent altsnflarnda saklanr. Olaylar, nesneleri, QObjectten miras aldklar event() fonksiyonu sayesinde bilgilendirir. QWidget iindeki event() gerekletirimi, mousePressEvent(), keyPressEvent() ve paintEvent() gibi en yaygn olay tiplerini zel olay ileyicilere(event handler) gnderir. Biz zaten, daha evvelki blmlerde MainWindow ve IconEditor gerekletirirken, olay ileyicilerin birounu grmtk. Dier olay tipleri, QEvent referans dokmantasyonunda listelenmitir ve ayrca zel olay tipleri oluturmak da mmkndr. Burada, daha fazla aklamay hak eden yaygn iki olayn kritiini yapacaz: tu olaylar ve zamanlayc olaylar. Tu olaylar, keyPressEvent() ve keyReleaseEvent() uyarlanarak ilenebilir. Normalde, tamamlayc tular Ctrl, Shift ve Alt keyPressEvent() iinde QKeyEvent::modifiers kullanlarak kontrol edilebildii iin sadece keyPressEvent()i uyarlamamz gerekir. rnein, bir CodeEditor parac gerekletiriyorsak, Home ve Ctrl + Home arasndaki fark ayrt eden keyPressEvent() unun gibi grnecektir:
void CodeEditor::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Home: if (event->modifiers() & Qt::ControlModifier) { goToBeginningOfDocument(); } else { goToBeginningOfLine(); } break; case Qt::Key_End: ... default: QWidget::keyPressEvent(event); } }

Tab ve Backtab(Shift + Tab) tular zel durumlardr. QWidget::event(), keyPressEvent()i armadan nce, odan hangi paraca kayacayla ilgilenir. Bu davran genellikle bizim istediimizdir, fakat bir CodeEditor paracnda, Tab ile satr ba yapmak isteyebiliriz. event() uyarlan artk yle grnecektir:
bool CodeEditor::event(QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Tab) { insertAtCurrentPosition('\t'); return true; } } return QWidget::event(event); }

124

Qt 4 ile C++ GUI Programlama Eer olay bir tua basma ise, QEvent nesnesini bir QKeyEvente dntrrz ve hangi tua basldn kontrol ederiz. Eer tu Tab ise, baz ilemler yaparz ve Qta olay ilediimizi bildirmek iin true dndrrz. Eer false dndrseydik, Qt, olay ebeveyn paraca aktaracakt. Tu balantlarn(key bindings) gerekletirmek iin bir dier yaklam, bir QAction kullanmaktr. rnein, eer goToBeginningOfLine() ve goToBeginningOfDocument(), CodeEditor parac iinde public yuvalar ise ve CodeEditor, bir MainWindow snf iinde merkez parack olarak kullanlyorsa, tu balantlarn u kodla ekleyebiliriz:
MainWindow::MainWindow() { editor = new CodeEditor; setCentralWidget(editor); goToBeginningOfLineAction = new QAction(tr("Go to Beginning of Line"), this); goToBeginningOfLineAction->setShortcut(tr("Home")); connect(goToBeginningOfLineAction, SIGNAL(activated()), editor, SLOT(goToBeginningOfLine())); goToBeginningOfDocumentAction = new QAction(tr("Go to Beginning of Document"), this); goToBeginningOfDocumentAction->setShortcut(tr("Ctrl+Home")); connect(goToBeginningOfDocumentAction, SIGNAL(activated()), editor, SLOT(goToBeginningOfDocument())); ... }

Bu, bir men veya ara ubuuna komutlar eklemeyi kolaylatrr. Tu balantlar varsaylan olarak, onu ieren pencere aktif olduunda etkinleen bir parack zerinde QAction veya QShortcut kullanlarak ayarlanr. Bu, QAction::setShortcutContext() ya da QShortcut::setContext() kullanlarak deitirilebilir. Bir baka yaygn olay tipi, zamanlayc olaydr. Birok olay tipi, bir kullanc eyleminin sonucu olarak meydana gelirken, zamanlayc olaylar, uygulamaya dzenli zaman aralklarnda ilem gerekletirme imkn verir. Zamanlayc olaylar, yanp snen imleler ve dier animasyonlar gerekletirmede ya da sadece grnty tazelemede kullanlabilirler. Zamanlayc olaylarn rnekle aklamak iin, ekil 7.1de grlen Ticker paracn gerekletireceiz. Bu parack, her 30 milisaniyede bir piksel sola kayan bir ana balk metni gsterir. Eer parack metinden daha geni ise, metin paracn btn geniliini doldurmas iin gerektii kadar tekrar edilir.

ekil 7.1

te, balk dosyas: Kod Grnm:


#ifndef TICKER_H #define TICKER_H

125

Qt 4 ile C++ GUI Programlama


#include <QWidget> class Ticker : public QWidget { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText) public: Ticker(QWidget *parent = 0); void setText(const QString &newText); QString text() const { return myText; } QSize sizeHint() const; protected: void paintEvent(QPaintEvent *event); void timerEvent(QTimerEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event); private: QString myText; int offset; int myTimerId; }; #endif

Tickerda drt olay ileyicisini uyarlarz. Bunlardan n daha nce grmemitik: timerEvent(), showEvent() ve hideEvent(). imdi, gerekletirimin kritiini yapalm:
include <QtGui> #include "ticker.h" Ticker::Ticker(QWidget *parent) : QWidget(parent) { offset = 0; myTimerId = 0; }

Kurucu offset deikenini 0a ilklendirir. Metnin izildii x-koordinat offset deikeninden salanr. Zamanlayc IDleri her zaman 0dan farkldr, bu nedenle, hibir zamanlayc balatlmadn belirtmek iin 0 kullanrz.
void Ticker::setText(const QString &newText) { myText = newText; update(); updateGeometry(); }

setText() fonksiyonu, metnin grntleniini ayarlar. Bir yeniden boyama istemek iin update()i ve Ticker parac hakknda bir boyut ipucu deiikliinden sorumlu yerleim yneticisini bilgilendirmek iin updateGeometry()i arr. 126

Qt 4 ile C++ GUI Programlama


QSize Ticker::sizeHint() const { return fontMetrics().size(0, text()); }

sizeHint() fonksiyonu, paracn ideal boyutu olarak, metin ihtiya duyduu boluu dndrr. QWidget::fontMetrics(), paracn yaztipine ilikin bilgiyi salamada kullanlabilen bir QFontMetrics nesnesi dndrr. Bu durumda onu, metnimize uygun boyutu renmede kullanrz. QFontMetrics::size()n ilk argman, basit karakter katarlar iin gerekmeyen bir bayraktr, bu nedenle sadece 0 aktarrz.
void Ticker::paintEvent(QPaintEvent * /* event */) { QPainter painter(this); int textWidth = fontMetrics().width(text()); if (textWidth < 1) return; int x = -offset; while (x < width()) { painter.drawText(x, 0, textWidth, height(), Qt::AlignLeft | Qt::AlignVCenter, text()); x += textWidth; } }

paintEvent() fonksiyonu, QPainter::drawText()i kullanarak metni izer. Metnin gerektirdii yatay boluu belirlemek iin fontMetrics()i kullanr ve sonra offseti hesaba katarak, metni gerekirse birka sefer- izer.
void Ticker::showEvent(QShowEvent * /* event */) { myTimerId = startTimer(30); }

showEvent() fonksiyonu bir zamanlayc balatr. QObject::startTimer()a ar, daha sonra zamanlaycy tanmak iin kullanabileceimiz bir ID numaras dndrr. QObject, her biri kendi zaman aral olan oklu-bamsz zamanlayclar destekler. startTimer() arsndan sonra, Qt, yaklak olarak her 30 milisaniyede bir, bir zamanlayc olay retir; doruluk, programn alt iletim sistemine baldr. startTimer() Ticker kurucusu iinde arabilirdik, fakat bu ekilde Qtun sadece, parack grnr olduunda zamanlayc olaylar retmesini salayarak, biraz kaynak tasarrufu yaparz.
void Ticker::timerEvent(QTimerEvent *event) { if (event->timerId() == myTimerId) { ++offset; if (offset >= fontMetrics().width(text())) offset = 0; scroll(-1, 0); } else { QWidget::timerEvent(event); } }

127

Qt 4 ile C++ GUI Programlama Sistem timerEvent() fonksiyonunu aralklarla arr. Hareketi simle etmek iin offseti 1 arttrr. Sonra, QWidget::scroll()u kullanarak paracn ieriini bir piksel sola kaydrr. scroll() yerine update()i armak yeterli olacaktr, fakat scroll() daha efektiftir, nk ekranda var olan pikselleri hareket ettirir ve sadece paracn aa kan yeni alan iin bir iz olay retir (bu durumda 1 piksel genilik kar). Eer zamanlayc olay bizim ilgilendiimiz zamanlayc iin deilse, onu, temel snfa aktarrz.
void Ticker::hideEvent(QHideEvent * /* event */) { killTimer(myTimerId); myTimerId = 0; }

hideEvent() fonksiyonu, zamanlaycy durdurmak iin QObject::killTimer arr. Zamanlayc olaylar dk-seviyelidir ve eer oklu zamanlayclara ihtiyacmz varsa, tm zamanlayc IDlerinin izini srmek klfetli olabilir. Bu gibi durumlarda, her zamanlayc iin bir QTimer nesnesi oluturmak genellikle daha kolaydr. QTimer her zaman aralnda timeout() sinyalini yayar. Ayrca, QTimer tek at zamanlayclar iin kullanl bir arayz de salar.

Olay Filtreleri Kurma


Qtun olay modelinin gerekten gl bir zellii, bir QObject rneinin, baka bir QObject rneinin olaylarn -sonraki nesneler onlar grmeden nce- izlemesi iin ayarlanabilinmesidir. Farz edelim ki, birka QLineEditten oluan bir CustomerInfoDialog paracna sahibiz ve Space tuunu kullanarak oda sonraki QLineEdite kaydrmak istiyoruz. Bu standart d davran bir kurum ii uygulama(in-house application) iin uygun olabilir. Kolay bir zm, bir QLineEdit altsnf tretmek ve keyPressEvent()i, focusNextChild() armaya uyarlamaktr. Tpk unun gibi:
void MyLineEdit::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Space) { focusNextChild(); } else { QLineEdit::keyPressEvent(event); } }

Bu yaklam bir dezavantaja sahiptir: Eer formda paracklarn birka farkl trn kullanrsak (QComboBoxlar ve QSpinBoxlar gibi), onlar da ayn davran sergilemeye uyarlamamz gerekir. Daha iyi bir yaklam, CustomerInfoDialogunun, ocuk paracklarnn tu basma olaylarn izlemesini salamak ve gereken davranlar bir izleme kodu(monitoring code) iinde gerekletirmektir. Bir olay filtresi(event filter) kurmak iki adm gerektirir: 1. zleme nesnesini(monitoring object), hedef nesne zerinde installEventFilter()i ararak kayda geirin. 2. Hedef nesnenin olaylarn, gzlemcinin(monitor) eventFilter() fonksiyonu iinde ileyin. zleme nesnesini kayda geirmek iin kurucu uygun iyi bir yerdir:
CustomerInfoDialog::CustomerInfoDialog(QWidget *parent)

128

Qt 4 ile C++ GUI Programlama


: QDialog(parent) { ... firstNameEdit->installEventFilter(this); lastNameEdit->installEventFilter(this); cityEdit->installEventFilter(this); phoneNumberEdit->installEventFilter(this); }

Olay filtresi bir kez kayda geirilince, firstNameEdit, lastNameEdit, cityEdit ve phoneNumberEdit paracklarna gnderilen olaylar, istenilen hedeflerine gnderilmeden nce, CustomerInfoDialog'un eventFilter() fonksiyonuna gnderilirler. te, olaylar alan eventFilter() fonksiyonu:
bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event) { if (target == firstNameEdit || target == lastNameEdit || target == cityEdit || target == phoneNumberEdit) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Space) { focusNextChild(); return true; } } } return QDialog::eventFilter(target, event); }

nce, hedef paracn, QLineEditlerden biri olup olmadn kontrol ederiz. Eer olay bir tu basma olay ise, onu QKeyEvente dntrrz ve hangi tuun basldn kontrol ederiz. Eer baslan tu Space ise, oda, odak zincirindeki sonraki paraca kaydrmak iin focusNextChild() arrz ve Qta olay ilediimizi bildirmek iin true dndrrz. Eer false dndrseydik, Qt olay istenilen hedefe gnderecekti ve bunun sonucu olarak, QLineEdit iine sahte bir boluk karakteri eklenmi olacakt. Eer hedef parack bir QLineEdit deilse ya da olay bir Space tuuna basma deilse, kontrol eventFilter()in temel snfnn gerekletirimine geiririz. Qt, olaylarn ilenmesinde ve filtrelenmesinde be seviye sunar: 1. Belirli bir olay ileyicisini uyarlayabiliriz. mousePressEvent(), keyPressEvent() ve paintEvent() gibi olay ileyicilerini uyarlamak, olaylar ilemenin en yaygn yoludur. Zaten bunun birok rneini grdk. 2. QObject::event()i uyarlayabiliriz. event() fonksiyonunu uyarlayarak, olaylar, belirli olay ileyicilerine ulamadan nce onlar ileyebiliriz. Bu yaklama en ok Tab tuunun varsaylan manasn hkmsz klmada ihtiya duyulur. Ayrca, belirli olay ileyicisi var olmayan nadir olay tiplerini ilemede de kullanlr(QEvent::HoweverEnter gibi). event()i uyarladmzda, aka ilemediimiz durumlar iin temel snfn event() fonksiyonunu armalyz. 129

Qt 4 ile C++ GUI Programlama

3. Bir olay ileyicisini tek bir QObject zerine kurabiliriz. Bir nesne, instalEventFilter() kullanlarak bir kere kayda geirildiinde, hedef nesnenin tm olaylar, nce izleme nesnesinin eventFilter() fonksiyonuna gnderilir. Eer ayn nesne zerine birok olay filtresi kurulmusa, filtreler, en son kurulandan ilk kurulana doru, srayla aktifletirilirler. 4. Bir olay ileyicisini bir QApplication nesnesi zerine kurabiliriz. Bir olay filtresi, qApp (tek QApplication nesnesi) iin bir kere kayda geirildiinde, uygulama iindeki herhangi bir nesnenin her olay, dier olay filtrelerine gnderilmeden nce eventFilter()e gnderilir. Bu yaklam hata ayklama iin ok kullanldr. Ayrca, normalde QApplicationn yok sayaca, devre d olan paracklara gnderilen fare olaylarn ilemede de kullanlabilir. 5. Bir QApplication altsnf tretebilir ve notify() uyarlayabiliriz. Qt, bir olay yaymak iin QApplication::notify() arr. Bu fonksiyonu uyarlamak, olay ileyicileri olaylar elde etmeden nce, tm olaylar elde etmenin tek yoludur. Olay filtreleri genellikle daha kullanldrlar, nk birok e zamanl olay filtresi olabilir, fakat sadece bir notify() fonksiyonu vardr. Birok olay tipi, fare ve tu olaylar da dhil, nakledilebilirler. Eer olay, hedef nesneye giderken ya da hedef nesnenin kendi tarafndan ilenmemise, olay ileme sreci tekrar edilir, fakat bu sefer hedef, hedef nesnenin ebeveynidir. Bu byle, ebeveynden ebeveyne devam eder gider, ta ki olay ileninceye ya da en st seviyedeki nesneye eriilinceye kadar. ekil 7.2, bir tua basma olaynn, bir diyalog iinde ocuktan ebeveyne nasl nakledildiini gsteriyor. Kullanc bir tua bastnda, olay nce odan olduu paraca gnderilir, bu durumda sa alttaki QCheckBoxtr. Eer QCheckBox olay ilemezse, Qt, olay nce QGroupBoxa, son olarak ta QDialog nesnesine gnderir.

ekil 7.2

Youn lem Srasnda Duyarl Kalma


QApplication::exec()i ardmzda, Qtun olay dngsn balatrz. Qt balangta, paracklar gstermek ve izmek iin birka olay sonulandrr. Ondan sonra olay dngs alr; ska, bir olayn meydana gelip gelmediini ve bu olaylarn uygulama iinde QObjectlere gnderilip gnderilmediini kontrol eder. Bir olay ilenirken, ona ek olaylar retilebilir ve bu olaylar Qtun olay kuyruuna(event queue) eklenebilir. Eer belli bir olay ilemek iin ok fazla zaman harcyorsak, kullanc arayz tepkisizleecektir. rnein, 130

Qt 4 ile C++ GUI Programlama uygulama bir dosyay kaydederken, pencere sistemi tarafndan retilen hibir olay, dosya kaydedilene kadar ilenmeyecektir. Kaydetme srasnda, uygulama, pencere sisteminin kendini yeniden izme isteklerine cevap vermeyecektir. Basit bir zm, dosya kaydetme kodu iinde sk sk QApplication::processEvents()i armay salamaktr. Bu fonksiyon Qta, beklemede olan olaylar(pending events) bildirir ve ardndan kontrol arana iade eder. te, processEvents() kullanarak, kullanc arayzn nasl duyarl tutacamzn bir rnei (Spreadsheetin dosya kaydetme koduna dayanyor):
bool Spreadsheet::writeFile(const QString &fileName) { QFile file(fileName); ... QApplication::setOverrideCursor(Qt::WaitCursor); for (int row = 0; row < RowCount; ++row) { for (int column = 0; column < ColumnCount; ++column) { QString str = formula(row, column); if (!str.isEmpty()) out << quint16(row) << quint16(column) << str; } qApp->processEvents(); } QApplication::restoreOverrideCursor(); return true; }

Bu yaklamn tehlikesi, kullancnn, uygulama daha kaydetmeyi bitirmeden, ana pencereyi kapatabilecek olmasdr, ya da File > Savee ikinci bir kez daha tklayabilecek olmasdr. Bu problem iin en kolay zm
qApp->processEvents();

yerine
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);

yazarak, Qta fare ve tu olaylarn yok saymasn bildirmektir. ou kez, uzun sren bir ilemin olduu yerde bir QProgressDialog gstermek isteriz. QProgressDialog, kullancy uygulamann ilerlemesi hakknda bilgilendiren bir ilerleme ubuuna(progress bar) sahiptir. QProgressDialog ayrca, kullancya ilemi iptal etme imkn veren bir Cancel butonu da salar. te, bir Spreadsheet dosyasn kaydederken, bu yaklam kullanan rneimiz: Kod Grnm:
bool Spreadsheet::writeFile(const QString &fileName) { QFile file(fileName); ... QProgressDialog progress(this); progress.setLabelText(tr("Saving %1").arg(fileName)); progress.setRange(0, RowCount); progress.setModal(true); for (int row = 0; row < RowCount; ++row) {

131

Qt 4 ile C++ GUI Programlama


progress.setValue(row); qApp->processEvents(); if (progress.wasCanceled()) { file.remove(); return false; } for (int column = 0; column < ColumnCount; ++column) { QString str = formula(row, column); if (!str.isEmpty()) out << quint16(row) << quint16(column) << str; } } return true; }

Admlarn toplam says olarak NumRows ile bir QProgressDialog olutururuz. Sonra, her bir satr iin setValue()yu ararak, ilerleme ubuunu gncel tutarz. QProgressDialog otomatik olarak geerli ilerleme deerini, admlarn toplam saysna blerek, bir yzde hesaplar. Yeniden iz olaylarn ya da kullanc tklamalarn ya da tua basma olaylarn iemesi iin QApplication::processEvents()i arrz. Eer kullanc Cancela tklarsa, kaydetmeyi iptal ederiz ve dosyay sileriz. QProgressDialog zerinde show()u armayz, nk ilerleme diyaloglar bunu kendileri iin yaparlar. Eer ilem ksa ise, QProgress bunu fark eder ve kendini gstermez. Uzun sren ilemler iin tamamen farkl bir yol daha vardr: Kullanc istediinde ilemi icra etmek yerine, ilemi uygulama bota kalncaya kadar erteleyebiliriz. Qtda bu yaklam, bir 0 milisaniye zamanlaycs(0-millisecond timer) kullanarak gerekletirilebilir. Bu zamanlayclar, eer beklemede olan olaylar yoksa iletilirler. te, botayken ileme(idle processing) yaklamn gsteren bir timerEvent() gerekletirimi rnei:
void Spreadsheet::timerEvent(QTimerEvent *event) { if (event->timerId() == myTimerId) { while (step < MaxStep && !qApp->hasPendingEvents()) { performStep(step); ++step; } } else { QTableWidget::timerEvent(event); } }

hasPendingevents() true dndrrse, ilemi durdururuz ve kontrol Qta iade ederiz. Qt, beklemede olan olaylarn tmn ilendiinde, ilem devam edecektir.

132

Qt 4 ile C++ GUI Programlama

BLM 8: SRKLE-BIRAK

Srkle-brak(Drag and drop), bir uygulama dhilinde ya da farkl uygulamalar arasnda bilgi transfer etmenin modern ve sezgisel bir yoludur. ou kez, veri tamak ve kopyalamak iin ek olarak pano(clipboard) destei salanr. Bu blmde, bir uygulamaya srkle-brak destei eklemeyi ve zel biimler elde etmeyi greceiz. Sonra, srkle-brak kodunu, pano destei ekleyerek yeniden kullanmay gstereceiz. Bu kodu yeniden kullanmak mmkndr, nk her iki mekanizma da veriyi birka biimde temin eden bir snf olan QMimeDataya dayanr.

Srkle-Brak Etkinletirme
Srkle-brak iki ayr eylem ierir: srkleme ve brakma. Qt paracklar, srkleme blgesi(drag site), brakma blgesi(drop site) ya da her ikisi olarak hizmet verebilirler. Bizim rneimiz, bir Qt uygulamasnn baka bir uygulama tarafndan srklenen eyi kabul etmesini gsterir. Qt uygulamas, merkez parac bir QTextEdit olan bir ana penceredir. Kullanc, masa stnden ya da bir dosya gezgininden bir metin dosyas srklediinde ve onu uygulama zerine braktnda, uygulama, dosyay QTextEdit iine ykler. te, rnein MainWindow snfnn tanm:
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); protected: void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); private: bool readFile(const QString &fileName); QTextEdit *textEdit; };

MainWindow snf, QWidgettan dragEnterEvent() ve dropEvent()i uyarlar. rnein amac srkle-brak gstermek olduu iin, ilevlerin ounun bir ana pencere snf iinde olduunu varsayarak, ihmal edeceiz.
MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit);

133

Qt 4 ile C++ GUI Programlama

textEdit->setAcceptDrops(false); setAcceptDrops(true); setWindowTitle(tr("Text Editor")); }

Kurucuda, bir QTextEdit olutururuz ve onu merkez parack olarak ayarlarz. Varsaylan olarak, QTextEdit dier uygulamalardan metinsel srkle-braklar kabul eder ve eer kullanc onun zerine bir dosyay brakrsa, dosya ismini metne ekler. Brakma olaylar(drop event) ocuktan ebeveyne nakledildii iin, QTextEdit zerinde brakmay devre d brakarak ve ana pencere zerinde etkinletirerek, MainWindow iinde, tm pencerenin brakma olaylarn elde ederiz.
void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/uri-list")) event->acceptProposedAction(); }

dragEnterEvent(), kullanc bir nesneyi bir parack zerine srklediinde arlr. Eer acceptProposedAction() bir olay zerinde arrsak, kullancnn, srklenen nesneyi bu parack zerine brakabileceini belirtmi oluruz. Varsaylan olarak, parack srklemeyi kabul etmeyecektir. Qt, kullancya paracn meru brakma alan olup olmadn bildirmek iin otomatik olarak fare imlecini deitirir. Burada kullancnn dosya srklemesine izin vermek isteriz. Bunu yapmak iin, srklenenin MIME tipini kontrol ederiz. text/uri-list MIME tipi; dosya isimleri, URLler ya da dier global kaynak tanmlayclar olabilen, uniform kaynak tanmlayclarnn(URI) bir listesini saklamada kullanlr. Standart MIME tipleri, Internet Assigned Numbers Authority(IANA) tarafndan tanmlanr. Bir tip ve slash(/) ile ayrlm bir alttipten(subtype) oluurlar. Pano ve srkle-brak sistemi, verinin farkl tiplerini belirlemek iin MIME tiplerini kullanr. MIME tiplerinin resmi listesine http://www.iana.org/assignments/media-types/ adresinden ulalabilir.
void MainWindow::dropEvent(QDropEvent *event) { QList<QUrl> urls = event->mimeData()->urls(); if (urls.isEmpty()) return; QString fileName = urls.first().toLocalFile(); if (fileName.isEmpty()) return; if (readFile(fileName)) setWindowTitle(tr("%1 - %2").arg(fileName) .arg(tr("Drag File"))); }

dropEvent(), kullanc bir nesneyi bir paracn zerine braktnda arlr. QUrllerin bir listesini elde etmek iin QMimeData::urls()i arrz. Genellikle, kullanclar bir seferde sadece bir dosya brakrlar, fakat birok dosyay ayn anda srklemek de olasdr. Eer bir URLden fazlas var ise, ya da URL bir yerel dosya ismi deil ise, derhal geri dneriz.

134

Qt 4 ile C++ GUI Programlama QWidget ayrca, dragMoveevent() ve dragLeaveEvent()i de salar, fakat birok uygulama iin onlar uyarlamak gerekmez. kinci rnek, bir srklemeyi sezmeyi ve bir brakmay kabul etmeyi gsterir. Srkle-brak destekleyen bir QListWidget altsnf oluturacaz ve onu ekil 8.1de grnen Project Chooser uygulamasnda bir bileen olarak kullanacaz.

ekil 8.1

Project Chooser uygulamas, kullancya isimlerle doldurulmu iki liste parac sunar. Her bir liste parac bir projeyi temsil eder. Kullanc bir kiiyi bir projeden dierine tamak iin liste parac iindeki isimleri srkleyip brakabilir. Srkle-brak kodunun tm QListWidget altsnf iine yerletirilir. te, snf tanm:
class ProjectListWidget : public QListWidget { Q_OBJECT public: ProjectListWidget(QWidget *parent = 0); protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); private: void performDrag(); QPoint startPos; };

ProjectListWidget snf, QWidgetta bildirilmi be olay ileyicisini uyarlar.


ProjectListWidget::ProjectListWidget(QWidget *parent) : QListWidget(parent) { setAcceptDrops(true); }

Kurucuda, liste parac zerinde srklemeyi etkinletiririz. 135

Qt 4 ile C++ GUI Programlama


void ProjectListWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) startPos = event->pos(); QListWidget::mousePressEvent(event); }

Kullanc farenin sol butonuna bastnda, farenin konumunu startPos private deikeni iinde saklarz. QListWidgetn, fare butonuna basma olaylarn her zamanki gibi ileme frsatna sahip olmasn salamak iin QListWidgetn mousePressEvent() gerekletirimini arrz.
void ProjectListWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { int distance = (event->pos() - startPos).manhattanLength(); if (distance >= QApplication::startDragDistance()) performDrag(); } QListWidget::mouseMoveEvent(event); }

Kullanc farenin sol butonuna basyorken fare imlecini hareket ettirirse, bir srklemenin baladn dnrz. Farenin geerli konumu ile sol fare butonuna basld andaki konumu arasndaki mesafeyi hesaplarz. Eer mesafe QApplicationn nerilen srklemeye balama mesafesinden (normalde drt piksel) bykse ya da ona eitse, srklemeyi balatmak iin performDrag() private fonksiyonunu arrz.
void ProjectListWidget::performDrag() { QListWidgetItem *item = currentItem(); if (item) { QMimeData *mimeData = new QMimeData; mimeData->setText(item->text()); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(QPixmap(":/images/person.png")); if (drag->exec(Qt::MoveAction) == Qt::MoveAction) delete item; } }

performDrag()ta, ebeveyni this olan bir QDrag nesnesi olutururuz. QDrag nesnesi, veriyi bir QMimeData nesnesi iinde saklar. rnein, QMimeData::setText() kullanarak, veriyi bir text/plain karakter katar olarak salarz. QMimeData, en yaygn srklenen tiplerini (resimler, URLler, renkler, vb.) ilemek iin birka fonksiyon salar ve QByteArrayler olarak ifade edilen istee bal MIME tiplerini ileyebilir. QDrag::setPixmap() ars, nesne srklenirken onu takip eden fare imlecini ayarlar. QDrag::exec() ars, srkleme ilemini balatr. Argman olarak desteklenen srkleme eylemlerinin (Qt::CopyAction, Qt::MoveAction ve Qt::LinkAction) bir kombinasyonunu alr ve gerekletirilen srkleme ilemini (ya da gerekletirilmemi ise Qt::IgnoreAction) dndrr. Hangi eylemin gerekletirilecei, kaynak paracn izinlerine, hedefin desteklediklerine ve srkleme meydana

136

Qt 4 ile C++ GUI Programlama geldiinde hangi tamamlayc tulara basldna baldr. exec() arsndan sonra, Qt srklenen nesnenin sahipliini stlenir ve ona daha fazla ihtiya duyulmadnda da siler.
void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event) { ProjectListWidget *source = qobject_cast<ProjectListWidget *>(event->source()); if (source && source != this) { event->setDropAction(Qt::MoveAction); event->accept(); } }

ProjectListWidget parac, eer srklemeler ayn uygulama iinde baka bir ProjectListWidgettan geliyorsa, onlar da kabul eder. QDragEnterEvent::source(), srklenen parack eer ayn uygulamann bir paras ise, bir iareti dndrr; aksi halde, bo bir iareti dndrr. Srklenenin bir ProjectListWigettan geldiinden emin olmak iin qobject_cast<T>() kullanrz. Eer hepsi uygun ise, eylemi bir tama eylemi olarak kabul etmeye hazr olduumuzu Qta bildiririz.
void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event) { ProjectListWidget *source = qobject_cast<ProjectListWidget *>(event->source()); if (source && source != this) { event->setDropAction(Qt::MoveAction); event->accept(); } }

dragMoveEvent()teki kod, dragEnterEvent() iindekinin ayndr. Bu gereklidir nk fonksiyonun QListWidget (aslnda, QAbstractItemView) gerekletirimini hkmsz klmamz gerekir.
void ProjectListWidget::dropEvent(QDropEvent *event) { ProjectListWidget *source = qobject_cast<ProjectListWidget *>(event->source()); if (source && source != this) { addItem(event->mimeData()->text()); event->setDropAction(Qt::MoveAction); event->accept(); } }

dropEvent()te, QMimeData::text() kullanarak, srklenen metne eriiriz ve o metin ile bir e olutururuz. Ayrca, srklenen enin orijinal versiyonunu silebilen kaynak parac bildirmek iin olay bir tama eylemi(move action) olarak almamz gerekir.

zel Srkleme Tiplerini Destekleme


imdiye kadarki rneklerde, yaygn MIME tipleri iin QMimeDatann desteine gvendik. Nitekim, bir metin srklemesi oluturmak iin QMimeData::setText()i ardk ve bir text/uri-list srklemesinin ieriine erimek iin QMimeData::urls()i kullandk. Eer dz bir metin, HTML metni, resim, URL ya da renk srklemek istersek, baka bir eye ihtiya duymakszn QMimeDatay kullanabiliriz. Fakat eer zel veriler srklemek istersek, u alternatifler arasndan seim yapmalyz:

137

Qt 4 ile C++ GUI Programlama 1. QMimeData::setData()y kullanarak bir QByteArray olarak istee bal veri salayabiliriz ve daha sonra QMimeData::data()y kullanarak onu elde edebiliriz. 2. Bir QMimeData altsnf tretebilir ve zel veri tiplerini ilemek iin formats() ve retrieveData()y uyarlayabiliriz. 3. Tek bir uygulama dhilindeki srkle-brak ilemleri iin, bir QMimeData altsnf tretebilir ve veriyi istediimiz herhangi bir veri yapsn kullanarak saklayabiliriz. lk yaklam altsnf tretmeyi gerektirmez, fakat baz sakncalara sahiptir: Eninde sonunda srkleme kabul edilmeyecek olsa bile veri yapmz bir QByteArraye dntrmemiz gerekir ve eer uygulamann geni bir alan ile etkilemek iin birka MIME tipi salamak istiyorsak, veriyi birka kere saklamamz gerekir (her MIME tipini bir kere). Eer veri byk ise, bu gereksiz yere uygulamay yavalatabilir. kinci ve nc yaklamlar bu problemi nleyebilir ya da minimize edebilirler. Kontrol tamamen bize verirler ve birlikte kullanlabilirler. Uygulamann nasl altn gstermek iin, bir QTableWidgeta srkle-brak yetenekleri eklemeyi gstereceiz. Srkleme, u MIME tiplerini destekleyecek: text/plain, text/html ve text/csv. lk yaklam kullanarak bir srklemeye balamak unu gibi grnr: Kod Grnm:
void MyTableWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { int distance = (event->pos() - startPos).manhattanLength(); if (distance >= QApplication::startDragDistance()) performDrag(); } QTableWidget::mouseMoveEvent(event); } void MyTableWidget::performDrag() { QString plainText = selectionAsPlainText(); if (plainText.isEmpty()) return; QMimeData *mimeData = new QMimeData; mimeData->setText(plainText); mimeData->setHtml(toHtml(plainText)); mimeData->setData("text/csv", toCsv(plainText).toUtf8()); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); if (drag->exec(Qt::CopyAction | Qt::MoveAction) == Qt::MoveAction) deleteSelection(); }

performDrag() private fonksiyonu, dikdrtgen bir seimi srklemeye balamak iin MouseMoveEvent()ten arlr. setText() ve setHtml()i kullanarak, text/plain ve text/html MIME tiplerini ayarlarz ve setData()y kullanarak, istee bal bir MIME tipi ve bir QByteArray alan text/csv tipini ayarlarz.
QString MyTableWidget::toCsv(const QString &plainText) {

138

Qt 4 ile C++ GUI Programlama


QString result = plainText; result.replace("\\", "\\\\"); result.replace("\"", "\\\""); result.replace("\t", "\", \""); result.replace("\n", "\"\n\""); result.prepend("\""); result.append("\""); return result; } QString MyTableWidget::toHtml(const QString &plainText) { QString result = Qt::escape(plainText); result.replace("\t", "<td>"); result.replace("\n", "\n<tr><td>"); result.prepend("<table>\n<tr><td>"); result.append("\n</table>"); return result; }

toCsv() ve toHtml() fonksiyonlar, bir tablar ve yenisatrlar(newlines) karakter katarn bir CSV (comma-separated values) ya da bir HTML karakter katarna dntrr. rnein,
Red Cyan Green Yellow Blue Magenta

verisini
"Red", "Green", "Blue" "Cyan", "Yellow", "Magenta"

ya ya da
<table> <tr><td>Red<td>Green<td>Blue <tr><td>Cyan<td>Yellow<td>Magenta </table>

a dntrr. Dntrme en basit yolla yaplr; QString::replace() kullanmak. HTMLin zel karakterlerinden kurtulmak iin Qt::escape()i kullanrz.
void MyTableWidget::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat("text/csv")) { QByteArray csvData = event->mimeData()->data("text/csv"); QString csvText = QString::fromUtf8(csvData); ... event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("text/plain")) { QString plainText = event->mimeData()->text(); ... event->acceptProposedAction(); } }

139

Qt 4 ile C++ GUI Programlama Veriyi farkl biimde temin ettiimiz halde, dropEvent() iinde sadece onlardan ikisini kabul ederiz. Eer kullanc, hcreleri bir QTableWidgettan bir HTML editrne srklerse, hcrelerin bir HTML tablosuna dntrlmesini isteriz. Fakat eer kullanc, keyfi bir HTMLi bir QTableWidget iine srklerse, kabul etmek istemeyiz. Bu rnei alr yapmak iin ayriyeten MyTableWidget kurucusu iinde setAcceptDrops(true) ve setSelectionMode(ContiguousSelection) armamz gerekir. imdi, uygulamay yeniden yapacaz, fakat bu sefer QTableWidgetItem ve QByteArray arasndaki dntrmeleri ertelemek ya da kanmak iin, bir QMimeData altsnf treteceiz. te altsnfmzn tanm:
class TableMimeData : public QMimeData { Q_OBJECT public: TableMimeData(const QTableWidget *tableWidget, const QTableWidgetSelectionRange &range); const QTableWidget *tableWidget() const { return myTableWidget; } QTableWidgetSelectionRange range() const { return myRange; } QStringList formats() const; protected: QVariant retrieveData(const QString &format, QVariant::Type preferredType) const; private: static QString toHtml(const QString &plainText); static QString toCsv(const QString &plainText); QString text(int row, int column) const; QString rangeAsPlainText() const; const QTableWidget *myTableWidget; QTableWidgetSelectionRange myRange; QStringList myFormats; };

Asl veriyi saklamak yerine, hangi hcrelerin srklendii belirten ve QTableWidgeta bir iareti salayan, bir QTableWidgetSelectionRange saklarz. formats() ve retrieveData() fonksiyonlar, QMineDatadan uyarlanrlar.
TableMimeData::TableMimeData(const QTableWidget *tableWidget, const QTableWidgetSelectionRange &range) { myTableWidget = tableWidget; myRange = range; myFormats << "text/csv" << "text/html" << "text/plain"; }

Kurucuda, private deikenleri ilk kullanma hazrlarz.


QStringList TableMimeData::formats() const { return myFormats;

140

Qt 4 ile C++ GUI Programlama


}

formats() fonksiyonu, MIME veri nesnesi tarafndan salanan MIME tiplerinin bir listesini dndrr. Biimlerin sras genellikle nemsizdir, fakat en iyi biimleri ilk sraya koymak iyi bir alkanlktr. Birok biimi destekleyen uygulamalar, bazen biimlerin ilk eleenini kullanrlar.
QVariant TableMimeData::retrieveData(const QString &format, QVariant::Type preferredType) const { if (format == "text/plain") { return rangeAsPlainText(); } else if (format == "text/csv") { return toCsv(rangeAsPlainText()); } else if (format == "text/html") { return toHtml(rangeAsPlainText()); } else { return QMimeData::retrieveData(format, preferredType); } }

retrieveData() fonksiyonu, verilen MIME tipi iin veriyi bir QVariant olarak dndrr. format parametresinin deeri normalde formats() tarafndan dndrlen karakter katarlarnn biridir, fakat tm uygulamalar, MIME tipini formats() ile kontrol etmedii iin bunu varsayamayz. QMimeData tarafndan salanan text(), html(), urls(), imageData(), colorData() ve data() tutucu fonksiyonlar, retrieveData()ya dayanarak gerekletirilirler. preferredType parametresi bize, QVariant iine hangi tipi koymamz gerektiinin ipucunu verir. Burada, onu yok sayarz ve eer gerekliyse, dnen deeri arzu edilen tipe dntrmede QMimeDataya gveniriz.
void MyTableWidget::dropEvent(QDropEvent *event) { const TableMimeData *tableData = qobject_cast<const TableMimeData *>(event->mimeData()); if (tableData) { const QTableWidget *otherTable = tableData->tableWidget(); QTableWidgetSelectionRange otherRange = tableData->range(); ... event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("text/csv")) { QByteArray csvData = event->mimeData()->data("text/csv"); QString csvText = QString::fromUtf8(csvData); ... event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("text/plain")) { QString plainText = event->mimeData()->text(); ... event->acceptProposedAction(); } QTableWidget::mouseMoveEvent(event); }

dropEvent() fonksiyonu, bu ksmda daha nce yaptmz ile benzerdir, fakat bu sefer nce, QMimeData nesnesini bir TableMimeDataya gvenli bir ekilde dntrp dntremeyeceimizi kontrol ederekten onu optimize ederiz. Eer qobject_cast<T>() alrsa, bunun anlam; srkleme 141

Qt 4 ile C++ GUI Programlama ayn uygulama iinde bir MyTableWidgettan kaynaklanmtr ve tablo verisine, QMimeDatann APIna sunmadan, dorudan olarak eriebiliriz. Eer dnm baarsz olursa, veriyi standart yolla elde ederiz. Bu rnekte, UTF-8 kodlamasn kullanarak CSV metnini kodladk. Eer doru kodlamann kullanldndan emin olmak istiyorsak, belli bir kodlama belirtmek iin text/plain MIME tipinin charset parametresini kullanabilirdik. te birka rnek:
text/plain;charset=US-ASCII text/plain;charset=ISO-8859-1 text/plain;charset=Shift_JIS text/plain;charset=UTF-8

Pano leme
ou uygulama Qtun yerleik pano ilemesini(clipboard handling) kullanr. rnein QTextEdit snf hem cut(), copy() ve paste() yuvalarn hem de klavye ksayollarn salar, ou zaman ek kod gerekmeden. Kendi snflarmz yazdmzda, uygulamann QClipboard nesnesine bir iareti dndren QApplication::clipboard() sayesinde panoya eriebiliriz. Sistem panosunu ilemek kolaydr: Veriyi panoya yerletirmek iin setText(), setImage() ya da setPixmap()i ar, panodan veriye erimek iinse text(), image() ya da pixmap() ar. Pano kullanmann rneini zaten Blm 4te Spreadheet uygulamas iinde grmtk. Baz uygulamalar iin yerleik ilevler yeterli olmayabilir. rnein, sadece metinden ya da sadece bir resimden ibaret olmayan bir veriyi temin etmek isteyebiliriz, ya da veriyi, dier uygulamalarla maksimum alabilirlik iin birok farkl biimde temin etmek isteyebiliriz. Sorun, daha nce srkle-brak ile karlatmz sorun ile ok benzerdir; yle ki cevab da benzerdir: Bir QMimeData altsnf tretebilir ve birka sanal fonksiyonu uyarlayabiliriz. Eer uygulamalarmz, srkle-brak zel bir QMimeData altsnf sayesinde destekliyorsa, QMimeData altsnfn yeniden kullanabilir ve setMimeData()y kullanarak, onu panoya yerletirebiliriz. Veriye erimek iinse pano zerinde mimeData()y arabiliriz.

142

Qt 4 ile C++ GUI Programlama

BLM 9: E GRNTLEME SINIFLARI

Birok uygulama kullancya, bir veri setine ait zel eleri arama, grntleme ve dzenleme izni verir. Veri, dosyalarda tutuluyor ya da ona veritabanndan veya bir a sunucusundan eriiliyor olabilir. Bunun gibi veri setleri ile uramann standart yaklam, Qtun e grntleme snflardr(item view classes). Qtun daha nceki versiyonlarnda, e grntleme paracklar, bir veri setinin tm ierii ile doldurulurdu; kullanclar tm aramalarn ve tutulan veri zerinde dzenlemeleri parack iinde yaparlard ve baz noktalarda, deiiklikler veri kaynana geri yazlrd. Anlamas ve kullanmas basit olmasna ramen, bu yaklam, byk veri setlerini iyi lekleyemez ve ayn verinin iki ya da daha fazla farkl parackta grntlenmesini istediimiz durumlarda kendini dn veremez. Smalltalk dili, byk veri setlerinin grselletirilmesi iin esnek bir yaklam poplerletirmitir: Model-ViewContoller(MVC yani Model-Grn-Kontrolr). MVC yaklamnda, Model, veri setini temsil eder ve hem grntleme iin gereken veriyi getirmekten hem de deiiklikleri kaydetmekten sorumludur. Veri setinin her bir tipi kendi modeline sahiptir. Grn, veriyi kullancya sunar. Her veri seti ile bir seferde sadece limitli bir miktarda veri grnr olacaktr, bu nedenle bu sadece grntlenmesi istenen veridir. Kontrolr, kullanc ve Grn arasnda araclk yapar. Qt, esin kayna MVC yaklam olan bir Model/Grn mimarisi salar (ekil 9.1). Qtda Model, klasik MVC iin yaptnn aynn yapar. Fakat Kontrolr yerine, Qt biraz farkl bir soyutlama kullanr: Delege(Delegate). Delege, elerin yorumlanmasnda ve dzenlenmesinde mkemmel bir kontrol salamada kullanlr. Qt, Grnn her tipi iin varsaylan bir Delege salar. Bu pek ok uygulama iin yeterlidir, bu nedenle genellikle onunla fazla ilgilenmeyiz.

ekil 9.1

Qtun Model/Grn mimarisini kullanrken, Modeli sadece Grn iinde o an iin grntlenmesi gereken veriyi getirmede kullanabiliriz, ki byle yapmak performanstan dn vermeden ok byk veri setlerini ilemeyi mmkn klar. Ve bir Modeli iki ya da daha fazla Grn ile kayda geirerek, kullancya, kk bir yk ile veriyi grntleme ve farkl ekillerde onunla etkileme frsat verebiliriz. Qt, oklu Grnleri otomatik olarak, ezamanl iletir; birindeki deiiklikler dier tmne yanstlr (ekil 9.2). Model/Grn mimarisinin faydalarna bir ekleme daha: Esas verinin nasl saklanacan deitirmeye karar verdiimizde, sadece Modeli deitirmemiz gerekir; Grn, ayn davran sergilemeye devam edecektir.

143

Qt 4 ile C++ GUI Programlama

ekil 9.2

Birok durumda, kullancya az sayda e sunmamz gerekir. Byle yaygn durumlarda, Qtun e grntleme uygunluk snflarn (QListWidget, QTableWidget ve QTreeWidget) kullanabiliriz ve onlar direkt olarak elerle doldurabiliriz. Bu snflar, Qtun nceki versiyonlar tarafndan salanan e grntleme snflar ile ayn ekilde davranrlar. Verilerini eler(items) iinde saklarlar (bir QTableWidgetn QTableWidgetItemlar iermesi gibi). Uygunluk snflar ayrca, eleri Grnlere salayan zel modeller kullanr. Byk veri setleri iin, veriyi kopyalamak ou kez bir seenek deildir. Bu gibi durumlarda, Qtun Grnlerini (QListWidget, QTableWidget ve QTreeWidget), zel bir Model ya da Qtun nceden tanml Modellerinden biri ile birleim iinde kullanabiliriz. rnein, eer veri seti bir veritaban(database) iinde tutuluyorsa, bir QTableView ile bir QSqlTableModel birletirebiliriz.

e Grntleme Uygunluk Snflarn Kullanma


Qtun e grntleme uygunluk altsnflarn kullanmak, zel bir Model tanmlamaktan daha basittir ve Grn ve Modelin ayrlmasnn faydalarna ihtiyacmz olmad zamanlar iin biilmi kaftandr. Bu teknii, Blm 4te hesap izelgesi ilevlerini gerekletirmek iin QTableWidget ve QTableWidgetItem altsnflar tretirken kullanmtk. Bu ksmda, eleri grntlemek iin e grntleme uygunluk snflarn kullanmay gstereceiz. lk rnek, saltokunur(read-only) bir QListWidget (ekil 9.3), ikinci rnek, dzenlenebilir bir QTableWidget (ekil 9.4), ve nc rnek, saltokunur bir QTreeWidget (ekil 9.5) gsterir.

ekil 9.3

144

Qt 4 ile C++ GUI Programlama

ekil 9.4

ekil 9.5

Kullancya, bir listeden bir ak diyagram(flowchart) sembol semeye izin veren basit bir diyalogla balayacaz. Her bir e; bir simge, bir metin ve zgn bir IDden meydana gelir. Diyaloun balk dosyasndan bir alnt ile balayalm:
class FlowChartSymbolPicker : public QDialog { Q_OBJECT public: FlowChartSymbolPicker(const QMap<int, QString> &symbolMap, QWidget *parent = 0); int selectedId() const { return id; } void done(int result); ... };

Diyalou ina etiimiz zaman, ona bir QMap<int, QString> aktarmalyz. Onu aktardktan sonra, selectedId()yi ararak seilen IDye eriebiliriz.
FlowChartSymbolPicker::FlowChartSymbolPicker( const QMap<int, QString> &symbolMap, QWidget *parent) : QDialog(parent) { id = -1;

145

Qt 4 ile C++ GUI Programlama

listWidget = new QListWidget; listWidget->setIconSize(QSize(60, 60)); QMapIterator<int, QString> i(symbolMap); while (i.hasNext()) { i.next(); QListWidgetItem *item = new QListWidgetItem(i.value(), listWidget); item->setIcon(iconForSymbol(i.value())); item->setData(Qt::UserRole, i.key()); } ... }

id'nin ilk deerini -1 olarak atarz. Sonra, bir e grntleme uygunluk parac olan bir QListWidget olutururuz. Ak diyagram sembol haritas zerinde her bir eyi tekrarlarz ve her birini temsilen birer QListWidgetItem olutururuz. QListWidgetItem kurucusu, grntlenecek metni ifade eden bir QString alr. Sonra, enin simgesini ayarlarz ve istee bal IDmizi QListWidgetItem iinde saklamak iin setData()y arrz. iconForSymbol() private fonksiyonu, verilen bir sembol ismi iin bir QIcon dndrr. QListWidgetItemlar birka role ve birlemi bir QVarianta sahiptirler. En yaygn roller, Qt::DisplayRole, Qt::EditRole ve Qt::IconRoledr, fakat baka roller de vardr. Ayn zamanda, Qt::UserRoleun bir nmerik deerini ya da daha ykseini belirterek, zel roller de tanmlayabiliriz. Bizim rneimizde, her bir enin IDsini saklamak iin Qt::UserRole kullanrz. Kurucunun, butonlar oluturma, paracklar yerletirme ve pencere baln ayarlamayla ilgili blmleri ihmal edilir.
void FlowChartSymbolPicker::done(int result) { id = -1; if (result == QDialog::Accepted) { QListWidgetItem *item = listWidget->currentItem(); if (item) id = item->data(Qt::UserRole).toInt(); } QDialog::done(result); }

done() fonksiyonu QDialogtan uyarlanmtr. Kullanc OK ya da Cancela tkladnda arlr. Eer kullanc OKi tklamsa, ilgili eye eriiriz ve data() fonksiyonunu kullanarak ID bilgisini alrz. Eer enin metni ile ilgileniyor olsaydk, item->data(Qt::DisplayRole).toString()i ya da item>text()i ararak ona eriebilirdik. Varsaylan olarak, QListWidget saltokunurdur. Eer kullancnn eleri dzenlemesini isteseydik, QAbstractItemView::setEditTriggers() kullanarak Grnn dzenleme tetiklerini(edit triggers) ayarlayabilirdik; rnein, QAbstractItemView::AnyKeyPressed ayar kullancnn, klavyeden tulamayarak, bir eyi dzenlemeye balayabilecei anlamna gelir. Alternatif olarak, dzenleme

146

Qt 4 ile C++ GUI Programlama ilemlerini programlanabilir bir ekilde ileyebilmek iin, bir Edit butonu (ve belki Add ve Delete butonlar) salayabilir ve sinyal-yuva balantlarn kullanabilirdik. Hazr, e grntleme uygunluk snflarn grntleme ve seme iin kullanmay grmken, imdi de, veriyi dzenleyebildiimiz bir rnee bakalm. Yine bir diyalog kullanyoruz, fakat bu sefer kullancnn dzenleyebildii, bir (x, y) koordinatlar seti sunan bir diyalog. nceki rnekteki gibi, kurucu ile balayarak, yine yalnzca e grntleme ile ilgili kodlara odaklanacaz.
CoordinateSetter::CoordinateSetter(QList<QPointF> *coords, QWidget *parent) : QDialog(parent) { coordinates = coords; tableWidget = new QTableWidget(0, 2); tableWidget->setHorizontalHeaderLabels( QStringList() << tr("X") << tr("Y")); for (int row = 0; row < coordinates->count(); ++row) { QPointF point = coordinates->at(row); addRow(); tableWidget->item(row, 0)->setText(QString::number(point.x())); tableWidget->item(row, 1)->setText(QString::number(point.y())); } ... }

QTableWidget kurucusu, grntleyecei satrlarn ve stunlarn ilk saysn alr. Yatay ve dikey balk eleri de dhil olmak zere, bir QTableWidget iindeki her e, bir QTableWidgetItem tarafndan temsil edilir. setHorizontalHeaderLabels() fonksiyonu, her bir yatay tablo parac esinin metnini ayarlar. QTableWidget varsaylan olarak, tam da isteimiz gibi 1den balayarak etiketlenmi satrlar ile bir dikey balk sunar, bylece dikey balk etiketlerini el ile ayarlamamz gerekmez. Stun etiketlerini oluturur oluturmaz, verilen koordinat verisine dayanarak yineleriz. Her (x, y) ifti iin yeni bir satr ekleriz (addRow() private fonksiyonunu kullanarak) ve her bir satrn stunlarna uygun metni ayarlarz. QTableWidget varsaylan olarak, dzenlemeye izin verir. Kullanc, tablodaki her hcreyi, gezinerek ve sonrasnda da F2ye basarak ya da sadece bir eyler yazarak dzenleyebilir. Kullancnn Grn iinde yapt tm deiiklikler otomatik olarak QTableWidgetItemlar iine yanstlacaktr. Dzenlemeye engel olmak iin, setEditTriggers(QAbstractItemView::NoEditTriggers) arrz.
void CoordinateSetter::addRow() { int row = tableWidget->rowCount(); tableWidget->insertRow(row); QTableWidgetItem *item0 = new QTableWidgetItem; item0->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); tableWidget->setItem(row, 0, item0); QTableWidgetItem *item1 = new QTableWidgetItem; item1->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);

147

Qt 4 ile C++ GUI Programlama


tableWidget->setItem(row, 1, item1); tableWidget->setCurrentItem(item0); }

addRow() yuvas, kullanc Add Row butonuna tkladnda arlr; ayrca kurucuda da kullanlr. QTableWidget::insertRow() kullanarak yeni bir satr ekleriz. Sonra, iki QTableWidgetItem olutururuz ve eye ek olarak, bir satr ve stun alan QTableWidget::setItem() kullanarak, onlar tabloya ekleriz. Son olarak, geerli eyi ayarlarz, bylece kullanc yeni satrn ilk esinden dzenlemeye balayabilir.
void CoordinateSetter::done(int result) { if (result == QDialog::Accepted) { coordinates->clear(); for (int row = 0; row < tableWidget->rowCount(); ++row) { double x = tableWidget->item(row, 0)->text().toDouble(); double y = tableWidget->item(row, 1)->text().toDouble(); coordinates->append(QPointF(x, y)); } } QDialog::done(result); }

Kullanc OKe tkladnda, diyaloa aktarlan koordinatlar temizleriz ve QTableWidgetn elerindeki koordinatlara dayanarak yeni bir set olutururuz. Qtun e grntleme kolaylk paracklarnn nc ve son rnei iin, bir QTreeWidget kullanarak, bir Qt uygulamasnn ayarlarn gsteren bir uygulamadan baz ufak paralara bakacaz. QTreeWidget varsaylan olarak saltokunurdur. te, kurucudan bir alnt:
SettingsViewer::SettingsViewer(QWidget *parent) : QDialog(parent) { organization = "Trolltech"; application = "Designer"; treeWidget = new QTreeWidget; treeWidget->setColumnCount(2); treeWidget->setHeaderLabels( QStringList() << tr("Key") << tr("Value")); treeWidget->header()->setResizeMode(0, QHeaderView::Stretch); treeWidget->header()->setResizeMode(1, QHeaderView::Stretch); ... setWindowTitle(tr("Settings Viewer")); readSettings(); }

Bir uygulamann ayarlarna erimek iin, organizasyonun ve uygulamann ad parametrelerine gre bir QSettings nesnesi oluturulmaldr. Varsaylan isimleri (Designer, Trolltech) ayarlarz ve sonra yeni bir QTreeWidget ina ederiz. Aa paracnn(tree widget) balk Grn(header view), aacn stunlarnn boyutlarn ynetir. Stunlarn yeniden boyutlandrma modunu(resize mode) Stretche ayarlarz. Bu, balk Grnnn daima stunlarn kullanlabilir boluklarn doldurmasn emreder. Bu modda, stunlar kullanc tarafndan ya da programlanabilir bir ekilde yeniden boyutlandrlamaz. Kurucunun sonunda, aa paracn doldurmak iin readSettings() fonksiyonunu arrz. 148

Qt 4 ile C++ GUI Programlama


void SettingsViewer::readSettings() { QSettings settings(organization, application); treeWidget->clear(); addChildSettings(settings, 0, ""); treeWidget->sortByColumn(0); treeWidget->setFocus(); setWindowTitle(tr("Settings Viewer - %1 by %2") .arg(application).arg(organization)); }

Uygulama ayarlar, anahtarlar ve deerlerin bir hiyerarisinde saklanr. addChildSettings() private fonksiyonu, bir ayar nesnesi(settings object), ebeveyn olarak bir QTreeWidgetItem ve geerli grup(group)u alr. Bir grup, bir dosya sistemi dizininin, QSettings olarak edeeridir. addChildSettings() fonksiyonu, istee bal bir aa yapsna(tree structure) gei yapmak iin zyinelemeli olarak kendini arabilir. readSettings() fonksiyonundan ilk ar, kk(root) temsil etmek iin ebeveyn e olarak bo bir iareti aktarr.
void SettingsViewer::addChildSettings(QSettings &settings, QTreeWidgetItem *parent, const QString &group) { if (!parent) parent = treeWidget->invisibleRootItem(); QTreeWidgetItem *item; settings.beginGroup(group); foreach (QString key, settings.childKeys()) { item = new QTreeWidgetItem(parent); item->setText(0, key); item->setText(1, settings.value(key).toString()); } foreach (QString group, settings.childGroups()) { item = new QTreeWidgetItem(parent); item->setText(0, group); addChildSettings(settings, item, group); } settings.endGroup(); }

addChildSettings() fonksiyonu, tm QTreeWidgetItemlar oluturmakta kullanlr. Ayar hiyerarisinde(settings hierarchy) geerli seviyedeki btn anahtarlar zerinde yinelenir ve her bir anahtar iin bir QTableWidgetItem oluturur. Eer ebeveyn e olarak bo bir iareti aktarlmsa, eyi, st seviye(top-level) bir e yaparak, QTreeWidget::invisibleRootItem()n bir ocuu olarak olutururuz. lk stun anahtarn ismine, ikinci stun o anahtara ilikin deere ayarlanr. Sonra, fonksiyon geerli seviyedeki her grup zerinde yinelenir. Her bir grup iin, yeni bir QTreeWidgetItem oluturulur ve ilk stunu gurubun ismine ayarlanr. Fonksiyon daha sonra, QTreeWidget grubun ocuk eleriyle doldurmak iin, grup esi ile (ebeveyn olarak) kendini zyinelemeli olarak arr. Bu ksmda gsterilen e grntleme paracklar bize, Qtun nceki versiyonlarndaki gibi bir programlama stili kullanma imkn verir: btn veri setini e grntleme parac iine okuma, veri elemanlarn temsil 149

Qt 4 ile C++ GUI Programlama etmede e nesnelerini kullanma ve (eer eler dzenlenebilir ise) bunlar veri kaynana geri yazma. Takip eden ksmlarda, bu basit yaklamn tesine geeceiz ve Qtun Model/Grn mimarisinin btn avantajndan faydalanacaz.

nceden Tanmlanm Modelleri Kullanma


Qt, grntleme snflar ile kullanmak iin birka model salar: QStringListModel QStandardItemModel QDirModel QSqlQueryModel QSqlTableModel QSqlRelationalTableModel QSortFilterProxyModel Karakter katarlarnn bir listesini saklar stee bal hiyerarik veriyi saklar Yerel dosya sistemini ierir Bir SQL sonu seti ierir Bir SQL tablosu ierir D(foreign) anahtarlarla bir SQL tablosu ierir and/or filtrelerini baka bir Modele ayrr

Bu ksmda, QStringListModel, QDirModel ve QSortFilterProxyModeln nasl kullanldna bakacaz. Her karakter katarnn bir takm liderini temsil ettii bir QStringList zerinde ekleme, silme ve dzenleme ilemlerini yapmakta kullanabileceimiz basit bir diyalogla balayalm. Diyalog, ekil 9.6da gsteriliyor.

ekil 9.6

te, kurucudan konu ile ilgili bir alnt:


TeamLeadersDialog::TeamLeadersDialog(const QStringList &leaders, QWidget *parent) : QDialog(parent) { model = new QStringListModel(this); model->setStringList(leaders); listView = new QListView; listView->setModel(model); listView->setEditTriggers(QAbstractItemView::AnyKeyPressed | QAbstractItemView::DoubleClicked); ... }

150

Qt 4 ile C++ GUI Programlama Bir QStringListModel oluturarak ve onu doldurarak balarz. Sonra, bir QListView olutururuz ve oluturduumuz Modeli onun Modeli olarak ayarlarz. Ayrca kullancya, bir karakter katarn sadece onun zerinde yazmaya balayarak ya da ona ift tklayarak dzenleme imkn vermek iin baz dzenleme tetiklerini ayarlarz. Varsaylan olarak, bir QListView zerinde hibir dzenleme tetii ayarlanmaz.
void TeamLeadersDialog::insert() { int row = listView->currentIndex().row(); model->insertRows(row, 1); QModelIndex index = model->index(row); listView->setCurrentIndex(index); listView->edit(index); }

Kullanc Insert butonuna tkladnda, insert() yuvas arlr. Yuva, liste Grnnn geerli esi iin satr numarasna erierek balar. Bir Model iindeki her veri esi, bir QModelIndex nesnesi tarafndan temsil edilen bir Model indeksine sahiptir. Model indekslerine detaylca bir dahaki ksmda bakacaz, fakat imdilik bir indeksin ana bileene sahip olduunu bilmeniz yeterli: bir satr, bir stun ve ait olduu Modeli iaret eden bir iareti. Bir boyutlu(one-dimensional) bir liste Modeli iin, stun daima 0dr. Satr numarasn elde eder etmez, o konumda yeni bir satr ekleriz. Ekleme, Model zerinde de yaplr ve Model otomatik olarak liste Grnn gnceller. Sonra, liste Grnnn geerli indeksini az nce eklediimiz bo satra ayarlarz. Son olarak, liste Grnn, yeni satr zerinde dzenleme moduna ayarlarz.
void TeamLeadersDialog::del() { model->removeRows(listView->currentIndex().row(), 1); }

Kurucuda, Delete butonunun clicked() sinyali del() yuvasna balanr. Sadece geerli satr sildiimiz iin, removeRows()u, geerli indeks konumu ve bir satr says (burada 1) ile bir kere arrz. Tpk ekleme gibi, Modelin benzer ekilde Grn gncelleyeceine gveniriz.
QStringList TeamLeadersDialog::leaders() const { return model->stringList(); }

Son olarak, leaders() fonksiyonu, diyalog kapandnda, dzenlenmi karakter katarlarnn geriye okunmasn salar. Sradaki rnek (ekil 9.7), bilgisayarn dosya sistemini ieren ve eitli dosya niteliklerini gsterebilen (ve gizleyebilen) QDirModel snfn kullanr. Bu Model, gsterilen dosya sistemi girdilerini kstlamak iin bir filtre uygulayabilir ve girdileri eitli yollarla sralayabilir.

151

Qt 4 ile C++ GUI Programlama

ekil 9.7

Directory Viewer diyalounun kurucusunda Model ve Grnn oluturulmasna ve kurulmasna bakarak balayacaz:
DirectoryViewer::DirectoryViewer(QWidget *parent) : QDialog(parent) { model = new QDirModel; model->setReadOnly(false); model->setSorting(QDir::DirsFirst | QDir::IgnoreCase | QDir::Name); treeView = new QTreeView; treeView->setModel(model); treeView->header()->setStretchLastSection(true); treeView->header()->setSortIndicator(0, Qt::AscendingOrder); treeView->header()->setSortIndicatorShown(true); treeView->header()->setClickable(true); QModelIndex index = model->index(QDir::currentPath()); treeView->expand(index); treeView->scrollTo(index); treeView->resizeColumnToContents(0); ... }

Model ina edilir edilmez, Modeli dzenlenebilir yaparz ve Modelin eitli ilk sralama dzeni niteliklerini ayarlarz. Sonra, Modelin verisini grntleyecek olan QTreeView olutururuz. QTreeViewn bal, kullanc kontrolnde sralamay salamada kullanlabilir. Bal tklanabilir yaparak, kullancnn herhangi bir stun baln tklayarak o stunu sralayabilmesi salanr. Aa Grnn bal kurulur kurulmaz, geerli dizinin Model indeksini alrz ve eer gerekliyse ebeveynini expand() kullanarak genileterek bu dizinin grnr olduundan emin oluruz, ve scrollTo() kullanarak onu kaydrrz. Sonra da, ilk stunun tm girdilerini eksilti() olmadan gsterecek genilikte olduundan emin oluruz. Kurucunun burada gsterilmeyen parasnda, Create Directory ve Remove butonlarn, eylemleri yerine getirmek iin yuvalara baladk. Kullanclar F2ye basarak ve bir eyler yazarak yeniden adlandrabildikleri iin Rename butonuna ihtiyacmz yok.
void DirectoryViewer::createDirectory() { QModelIndex index = treeView->currentIndex(); if (!index.isValid()) return;

152

Qt 4 ile C++ GUI Programlama

QString dirName = QInputDialog::getText(this, tr("Create Directory"), tr("Directory name")); if (!dirName.isEmpty()) { if (!model->mkdir(index, dirName).isValid()) QMessageBox::information(this, tr("Create Directory"), tr("Failed to create the directory")); } }

Eer kullanc, girdi diyalounda bir dizin ismi girerse, geerli dizinin bir ocuu olacak ekilde ve bu isimde bir dizin oluturmay deneriz. QDirModel::mkdir() fonksiyonu, ebeveyn dizinin indeksini ve yeni dizinin ismini alr, ve oluturduu dizinin Model indeksini dndrr. Eer ilem baarsz olursa, geersiz bir Model indeksi dndrr.
void DirectoryViewer::remove() { QModelIndex index = treeView->currentIndex(); if (!index.isValid()) return; bool ok; if (model->fileInfo(index).isDir()) { ok = model->rmdir(index); } else { ok = model->remove(index); } if (!ok) QMessageBox::information(this, tr("Remove"), tr("Failed to remove %1").arg(model->fileName(index))); }

Eer kullanc Removea tklarsa, geerli e ile ilikili dosya ya da dizini silmeyi deneriz. Bunu yapmak iin QDiri kullanabilirdik, fakat QDirModel, QModelIndexler zerinde alan uygunluk fonksiyonlar salar. Bu ksmdaki son rnek (ekil 9.8), QSortFilterProxyModeln kullanln rnekle aklyor. Dier nceden tanmlanm Modellerden farkl olarak, bu Model, var olan bir Modeli ierir ve esas Model ve Grn arasnda aktarlan veriyi iler. rneimizde, esas Model, Qtun tand renk isimlerinin bir listesi (QColor::colorNames() sayesinde elde edilmi) ile ilklendirilen bir QStringListModeldr. Kullanc, bir QLineEdit iine bir filtre karakter katar yazabilir ve bu karakter katarnn nasl yorumlanacan bir alan kutu(combobox) kullanarak belirtebilir.

153

Qt 4 ile C++ GUI Programlama

ekil 9.8

te, ColorNamesDialog kurucusundan bir alnt:


ColorNamesDialog::ColorNamesDialog(QWidget *parent) : QDialog(parent) { sourceModel = new QStringListModel(this); sourceModel->setStringList(QColor::colorNames()); proxyModel = new QSortFilterProxyModel(this); proxyModel->setSourceModel(sourceModel); proxyModel->setFilterKeyColumn(0); listView = new QListView; listView->setModel(proxyModel); ... syntaxComboBox = new QComboBox; syntaxComboBox->addItem(tr("Regular expression"), QRegExp::RegExp); syntaxComboBox->addItem(tr("Wildcard"), QRegExp::Wildcard); syntaxComboBox->addItem(tr("Fixed string"), QRegExp::FixedString); ... }

QStringListModel allm yolla oluturulur ve doldurulur. Bunu, QSortFilterProxyModeln inas izler. Esas Modeli setSourceModel() kullanarak aktarrz ve vekile(proxy) orijinal Modelin 0. stununa dayanarak filtre etmesini syleriz. QComboBox::addItem() fonksiyonu, QVariant tipinde istee bal bir veri(data) argman kullanr; bunu, her bir enin metnine tekabl eden QRegExp::PatternSyntax deerini saklamada kullanrz.
void ColorNamesDialog::reapplyFilter() { QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax(syntaxComboBox->itemData( syntaxComboBox->currentIndex()).toInt()); QRegExp regExp(filterLineEdit->text(), Qt::CaseInsensitive, syntax); proxyModel->setFilterRegExp(regExp); }

reapplyFilter() yuvas, kullanc, filtre karakter katarn ya da rnek szdizimi(pattern syntax) ama kutusunu deitirdiinde arlr. Satr editrndeki metni kullanarak bir QRegExp olutururuz. Sonra, onun rnek szdizimini, szdizimi ama kutusunun geerli esinin verisinde saklananlardan biri olarak ayarlarz.

154

Qt 4 ile C++ GUI Programlama setFilterRegExp()yi ardmzda, yeni filtre aktif olur. Bunun anlam; filtre ile elemeyen karakter katarlarn atacak ve Grn otomatik olarak gncellenmi olacak.

zel Modeller Gerekletirme


Qtun nceden tanmlanm Modelleri, veriyi ilemede ve grntlemede bir pratiklik sunar. Ancak, baz veri kaynaklar nceden tanmlanm Modelleri kullanarak etkili biimde kullanlamaz ve bu durumdan dolay temelini oluturan veri kayna iin optimize edilmi zel Modeller oluturmak gereklidir. zel Modeller oluturmaya balamadan nce, Qtun Model/Grn mimarisindeki anahtar kavramlara bir gz atalm. Bir Model, iindeki her veri eleman bir Model indeksine ve roller(roles) denen ve istee bal deerler alabilen bir nitelikler setine sahiptir. Daha nceki blmlerde en yaygn kullanlan rollerden, Qt::DisplayRole ve Qt::EditRole grdk. Dier roller, tamamlayc veriler iin (rnein, Qt::ToolTipRole, Qt::StatusTipRole ve Qt::WhatsThisRole), bazlar da temel grntleme niteliklerini kontrol etmek iin kullanlrlar (Qt::FontRole, Qt::TextAlignmentRole, Qt::TextColorRole ve Qt::BackgroundColorRole gibi). Bir liste Modeli iin, tek indeks bileeni, QModelIndex::row()dan eriilebilen satr numarasdr. Bir tablo modeli iin, indeks bileenleri, QModelIndex::row() ve QModelIndex::column()dan eriilebilen, satr ve stun numaralardr. Hem liste hem de tablo Modelleri iin, her enin ebeveyni, geersiz bir QModelIndex ile temsil edilen kktr. Bu ksmdaki ilk iki rnek, zel tablo Modelleri gerekletirmeyi gsterir. Bir aa Modeli, baz farllklarla birlikte, bir tablo Modeli ile benzerdir. Bir tablo Modeli gibi, st seviye elerinin ebeveyni kktr (geersiz bir QModelIndex), fakat dier elerinin ebeveynleri, hiyerarideki baz dier elerdir. Ebeveynlere, QModelIndex:parent()dan eriilebilir. Her e, kendi rol verisine ve sfr ya da daha fazla ocua sahiptir. eler, dier elere ocuk olarak sahip olabildikleri iin, bu ksmda gstereceimiz son rnekteki gibi zyinelemeli veri yaplar tarif etmek mmkndr. Farkl Modellerin bir emas ekil 9.9da gsteriliyor.

ekil 9.9

Bu ksmdaki ilk rnek, para birimlerini(currency) birbiriyle ilikili olarak gsteren, saltokunur bir tablo Modelidir. Uygulama ekil 9.10da gsteriliyor.

155

Qt 4 ile C++ GUI Programlama

ekil 9.10

Uygulama, basit bir tablo kullanarak gerekletirilebilirdi, fakat zel bir Modelin avantajlarn kullanmak isteriz. Eer halen ilem gren 162 para birimini bir tabloda saklamak isteseydik, saklamak iin 162 x 162 = 26244 deere ihtiyacmz olacakt; bu ksmda gsterilen zel CurrencyModel ile sadece 162 deere ihtiyacmz oluyor (her bir para biriminin deeri Amerikan dolar(U.S dollar) ile ilikili olarak). CurrencyModel snf standart bir QTableView ile kullanlacak. CurrencyModel bir QMap<QString, double> ile doldurulur; her bir anahtar bir para birimi kodu ve her bir deer para biriminin Amerikan dolar olarak karl. te, haritann(map) nasl doldurulduunu ve Modelin nasl kullanldn gsteren ufak bir kod paras:
QMap<QString, double> currencyMap; currencyMap.insert("AUD", 1.3259); currencyMap.insert("CHF", 1.2970); ... currencyMap.insert("SGD", 1.6901); currencyMap.insert("USD", 1.0000); CurrencyModel currencyModel; currencyModel.setCurrencyMap(currencyMap); QTableView tableView; tableView.setModel(&currencyModel); tableView.setAlternatingRowColors(true);

imdi, balyla balayarak Modelin gerekletirimine bakabiliriz:


class CurrencyModel : public QAbstractTableModel { public: CurrencyModel(QObject *parent = 0); void setCurrencyMap(const QMap<QString, double> &map); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; private: QString currencyAt(int offset) const; QMap<QString, double> currencyMap; };

Veri kaynamz ile en ok eleen Model olduu iin altsnfn tretmek iin QAbstractTableModel setik. Qt, QAbstractListModel, QAbstractTableModel ve QAbstractItemModel da ieren 156

Qt 4 ile C++ GUI Programlama birka temel snf salar; ekil 9.11e bakn. QAbstractItemModel snf, zyinelemeli veri yaplarna dayananlar da kapsayan, Modellerin birok eidini desteklemede kullanlrken, QAbstractListModel ve QAbstractTableModel snflar bir ya da iki boyutlu veri setleri kullanldnda kolaylk iin salanrlar.

ekil 9.11

Saltokunur bir tablo Modeli iin, fonksiyonu uyarlamalyz: rowCount(), columnCount() ve data(). Bu durumda, ayrca headerData()y da uyarlarz ve veriyi ilklendirmek iin bir fonksiyon salarz (setCurrencyMap()).
CurrencyModel::CurrencyModel(QObject *parent) : QAbstractTableModel(parent) { }

Kurucuda, parent parametresini temel snfa aktarmann dnda hibir ey yapmamz gerekmez.
int CurrencyModel::rowCount(const QModelIndex & /* parent */) const { return currencyMap.count(); } int CurrencyModel::columnCount(const QModelIndex & /* parent */) const { return currencyMap.count(); }

Bu tablo Modeli iin, satr ve stun saylar, para birimi haritasndaki para birimlerinin saysdr. parent parametresi, bir tablo modeli iin hibir anlam ifade etmez; vardr, nk rowCount() ve coloumnCount(), daha genelleyici olan ve hiyerarileri destekleyen QAbstractItemModel temel snfndan miras alnrlar.
QVariant CurrencyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::TextAlignmentRole) { return int(Qt::AlignRight | Qt::AlignVCenter); } else if (role == Qt::DisplayRole) { QString rowCurrency = currencyAt(index.row()); QString columnCurrency = currencyAt(index.column()); if (currencyMap.value(rowCurrency) == 0.0) return "####"; double amount = currencyMap.value(columnCurrency) / currencyMap.value(rowCurrency); return QString("%1").arg(amount, 0, 'f', 4); } return QVariant(); }

157

Qt 4 ile C++ GUI Programlama

data() fonksiyonu, bir enin rollerinden herhangi birinin deerini dndrr. e, bir QModelIndex olarak belirtilir. Bir tablo Modeli iin, bir QModelIndexin ilgi ekici bileenleri, row() ve column() kullanarak elde edilebilen satr ve stun numaralardr. Eer rol Qt::TextAlignmentRole ise, saylar iin uygun bir hizalan dndrrz. Eer rol Qt::DisplayRole ise, her bir para birimi iin deeri arayp buluruz ve kurunu hesaplarz. Hesaplanm deeri bir double olarak dndrebilirdik, fakat daha sonra ka tane ondalk hane gsterilecei zerinde kontrole sahip olmayacaktk (zel bir Delege kullanmadka). Onun yerine, deeri, isteimize gre biimlenmi bir karakter katar olarak dndrrz.
QVariant CurrencyModel::headerData(int section, Qt::Orientation /* orientation */, int role) const { if (role != Qt::DisplayRole) return QVariant(); return currencyAt(section); }

headerData() fonksiyonu, Grn tarafndan yatay ve dikey balklar doldurmak iin arlr. section parametresi, satr ya da stun numarasdr (ynelime(orientation) bal olarak). Satrlar ve stunlar ayn para birimi kodlarna sahip olduklar iin ynelimi nemsemeyiz ve sadece, verilen blme says iin para biriminin kodunu dndrrz.
void CurrencyModel::setCurrencyMap(const QMap<QString, double> &map) { currencyMap = map; reset(); }

aran, setCurrencyMap()i kullanarak para birimi haritasn deitirebilir. QAbstractItemModel::reset() ars, Model kullanan her Grne, tm verilerinin geersiz olduunu bildirir; bu onlar, grnr eleri iin taze veri istemeye zorlar.
QString CurrencyModel::currencyAt(int offset) const { return (currencyMap.begin() + offset).key(); }

currencyAt() fonksiyonu, para birimi haritasnda verilen greli konuma(offset), anahtar (para birimi kodu) dndrr. eyi bulmak iin bir STL-tarz yineleyici(STL-style iterator) kullanrz ve zerinde key()i arrz. Grld gibi, esas verinin doasna bal kalarak, saltokunur Modeller oluturmakta bir zorluk yoktur ve iyi tasarlanm bir Model ile bellek tasarrufu ve hz salamak mmkndr. Sradaki rneimiz; ekil 9.12de gsterilen Cities uygulamas da tablo temellidir, fakat bu sefer tm veri kullanc tarafndan girilir.

158

Qt 4 ile C++ GUI Programlama

ekil 9.12

Bu uygulama, herhangi iki ehir(city) arasndaki mesafeyi gsteren deerleri saklamada kullanlr. Bir nceki rnekteki gibi sadece bir QTableWidget kullanabilir ve her ehir ifti iin bir e saklayabilirdik. Ancak, zel bir Model daha etkili olabilir, nk herhangi bir A ehrinden herhangi farkl bir B ehrine olan mesafe Adan Bye veya Bden Aya seyahatlerde ayndr, bu nedenle eler ana kegen boyunca ayndr. zel bir Model ile basit bir tablo arasndaki fark grmek iin ehrimiz olduunu farz edelim, A, B ve C. Eer her bir kombinasyon iin bir deer saklasak, dokuz deer saklamamz gerekecekti. Dikkatlice tasarlanm bir model sadece u eyi gerektirir: (A, B), (A, C) ve (B, C). te, modelin kurgulanmas ve kullanlmas:
QStringList cities; cities << "Arvika" << "Boden" << "Eskilstuna" << "Falun" << "Filipstad" << "Halmstad" << "Helsingborg" << "Karlstad" << "Kiruna" << "Kramfors" << "Motala" << "Sandviken" << "Skara" << "Stockholm" << "Sundsvall" << "Trelleborg"; CityModel cityModel; cityModel.setCities(cities); QTableView tableView; tableView.setModel(&cityModel); tableView.setAlternatingRowColors(true);

nceki rnek iin yaptmz gibi, ayn fonksiyonlar yine uyarlamalyz. Ek olarak, Modeli dzenlenebilir yapmak iin setData() ve flags() da uyarlamalyz. te, snf tanm:
class CityModel : public QAbstractTableModel { Q_OBJECT public: CityModel(QObject *parent = 0); void setCities(const QStringList &cityNames); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex &index, const QVariant &value, int role); QVariant headerData(int section, Qt::Orientation orientation, int role) const;

159

Qt 4 ile C++ GUI Programlama


Qt::ItemFlags flags(const QModelIndex &index) const; private: int offsetOf(int row, int column) const; QStringList cities; QVector<int> distances; };

Bu model iin iki veri yaps kullanyoruz: ehir isimlerini tutmada QStringList tipinde cities, ve her bir benzersiz ehirler ifti arasndaki mesafeyi tutmada QVektor<int> tipinde distances.
CityModel::CityModel(QObject *parent) : QAbstractTableModel(parent) { }

Kurucu, temel snfa parent parametresini aktarmann tesinde hibir ey yapmaz.


int CityModel::rowCount(const QModelIndex & /* parent */) const { return cities.count(); } int CityModel::columnCount(const QModelIndex & /* parent */) const { return cities.count(); }

ehirlerin kare bir zgarasna sahip olduumuz iin, satrlarn ve stunlarn says, listemizdeki ehirlerin saysdr.
QVariant CityModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::TextAlignmentRole) { return int(Qt::AlignRight | Qt::AlignVCenter); } else if (role == Qt::DisplayRole) { if (index.row() == index.column()) return 0; int offset = offsetOf(index.row(), index.column()); return distances[offset]; } return QVariant(); }

data() fonksiyonu CurrencyModelda yaptmzla benzerdir. Eer stun ve satr ayn ise 0 dndrr, nk iki ehrin ayn olduu durumlardr; aksi halde, distances vektr(vector) iinde verilen satr ve stun iin girdiyi bulur ve ehir iftine ait mesafeyi dndrr.
QVariant CityModel::headerData(int section, Qt::Orientation /* orientation */, int role) const { if (role == Qt::DisplayRole) return cities[section]; return QVariant(); }

160

Qt 4 ile C++ GUI Programlama

headerData() fonksiyonu basittir, nk her satrn zde bir stun balna sahip olduu, kare bir tabloya sahibiz. Basite, verilen greli konuma gre cities karakter katar listesinden ehrin adn dndrrz.
bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) { int offset = offsetOf(index.row(), index.column()); distances[offset] = value.toInt(); QModelIndex transposedIndex = createIndex(index.column(), index.row()); emit dataChanged(index, index); emit dataChanged(transposedIndex, transposedIndex); return true; } return false; }

setData() fonksiyonu, kullanc bir eyi dzenlediinde arlr. Salanan Model indeksi geerli, iki ehir farkl ve dzenlenecek veri eleman Qt::EditRole ise, fonksiyon kullancnn girdii deeri distances vektr iinde saklar. createIndex() fonksiyonu, bir Model indeksi retmede kullanlr. Deien enin Model indeksi ile dataChanged() sinyalini yayarz. Sinyal, iki Model indeksi alr. dataChanged() sinyalini ayrca, Grnn eyi tazeleyeceini garantiye almak amacyla, indeksi aktarmak iin de yayarz. Son olarak, dzenlemenin baarl olup olmadn bildirmek iin true ya da false dndrrz.
Qt::ItemFlags CityModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.row() != index.column()) flags |= Qt::ItemIsEditable; return flags; }

Model, bir e ile ne yaplabilineceini (rnein, dzenlenebilir olup olmadn) iletmede flags() fonksiyonunu kullanr. QAbstractTableModeldaki varsaylan gerekletirim, Qt::ItemIsSelectable | Qt::ItemIsEnabled dndrr. Biz, kegen zerinde bulunanlar dndaki (her zaman 0 olanlar) tm eler iin Qt::ItemIsEditable bayran ekleriz.
void CityModel::setCities(const QStringList &cityNames) { cities = cityNames; distances.resize(cities.count() * (cities.count() - 1) / 2); distances.fill(0); reset(); }

161

Qt 4 ile C++ GUI Programlama Eer yeni bir ehirler listesi verilmise, private QStringListi yeni listeye ayarlarz; distance vektrn yeniden boyutlandrr ve temizleriz, ve grnr eleri gncellenmesi gereken her Grn haberdar etmek iin QAbstractItemModel::reset()i arrz.
int CityModel::offsetOf(int row, int column) const { if (row < column) qSwap(row, column); return (row * (row - 1) / 2) + column; }

offsetOf() private fonksiyonu, verilen ehir iftinin distances vektr iindeki indeksini hesaplar. rnein, A, B, C ve D ehirlerine sahipsek ve kullanc, satr 3, stun 1i (Bden Dye) gncellemise, greli konum 3 x (3 - 1)/2 + 1 = 4 olacaktr. Eer kullanc onun yerine satr 1, stun 3 (Dden Bye) gncellemi olsayd, qSwap()a krler olsun ki, tamamen ayn hesaplama yaplacakt ve ayn greli konum dndrlecekti. ekil 9.13, ehirler, mesafeler ve ilgili tablo modeli arasndaki ilikileri resmeder.

ekil 9.13

Bu ksmdaki son rnek, verilen bir Boolean deyim iin ayrtrma aac(parse tree) gsteren bir modeldir. Bir Boolean deyim, ya bravo gibi basit alfanmerik bir belirtetir ya da &&, ||, veya ! operatrleri kullanlarak, daha basit deyimlerden ina edilmi karmak bir deyimdir. rnein, a || (b && !c) bir Boolean deyimdir. Boolean Parser uygulamas(ekil 9.14), drt snftan meydana gelir: BooleanWindow, kullancnn bir Boolean deyim girmesine izin veren ve ilgili ayrtrma aacn gsteren bir penceredir. BooleanParser, bir Boolean deyimden bir ayrtrma aac retir. BooleanModel, bir ayrtrma aac ieren bir aa modelidir. Node, bir ayrtrma aacndaki bir eyi temsil eder.

162

Qt 4 ile C++ GUI Programlama

ekil 9.14

Node snf ile balayalm:


class Node { public: enum Type { Root, OrExpression, AndExpression, NotExpression, Atom, Identifier, Operator, Punctuator }; Node(Type type, const QString &str = ""); ~Node(); Type type; QString str; Node *parent; QList<Node *> children; };

Her dm(node) bir tipe, bir karakter katarna (bo(empty) olabilen), bir ebeveyn (bo(null) olabilen) ve bir ocuk dmler listesine (bo(empty) olabilen) sahiptir.
Node::Node(Type type, const QString &str) { this->type = type; this->str = str; parent = 0; }

Kurucu sadece dmn tipine ve karakter katarna ilk deer atar ve ebeveyni bo olarak (ebeveyn yok anlamnda) ayarlar. nk tm veri publictir ve bylece Node snf, tipi, karakter katarn, ebeveyni ve ocuklar dorudan ileyebilir.
Node::~Node() { qDeleteAll(children); }

qDeleteAll() fonksiyonu, bir iaretiler konteyneri(container of pointers) zerinde yinelenir ve her biri zerinde deletei arr. 163

Qt 4 ile C++ GUI Programlama Artk, veri elerimizi tanmladk (her biri bir Node tarafndan temsil edilir) , bir Model oluturmaya hazrz:
class BooleanModel : public QAbstractItemModel { public: BooleanModel(QObject *parent = 0); ~BooleanModel(); void setRootNode(Node *node); QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &child) const; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; private: Node *nodeFromIndex(const QModelIndex &index) const; Node *rootNode; };

Bu sefer ana snf olarak, QAbstractItemModeln uygunluk altsnf olan QAbstractTableModel kullanmak yerine, kendisini kullandk, nk biz hiyerarik bir Model oluturmak istiyoruz. Modelin verisini ayarlamak iin bir ayrtrma aacnn kk dm(root node) ile arlmas gereken setRootNode() fonksiyonuna sahibiz.
BooleanModel::BooleanModel(QObject *parent) : QAbstractItemModel(parent) { rootNode = 0; }

Modelin kurucusunda sadece, kk dm gvenli bir bo(null) deere ayarlamamz ve ana snfa parent aktarmamz gerekir.
BooleanModel::~BooleanModel() { delete rootNode; }

Yokedicide, kk dm sileriz. Eer kk dm ocuklara sahip ise, bunlarn her biri, zyinelemeli olarak Node yokedicisi tarafndan silinir.
void BooleanModel::setRootNode(Node *node) { delete rootNode; rootNode = node; reset(); }

Yeni bir kk dm ayarlandnda, nceki kk dm (ve onun tm ocuklarn) silerek balarz. Sonra yeni kk dm ayarlarz ve her grnr esini gncellemesi gereken Grnleri haberdar etmek iin reset()i arrz. 164

Qt 4 ile C++ GUI Programlama


QModelIndex BooleanModel::index(int row, int column, const QModelIndex &parent) const { if (!rootNode || row < 0 || column < 0) return QModelIndex(); Node *parentNode = nodeFromIndex(parent); Node *childNode = parentNode->children.value(row); if (!childNode) return QModelIndex(); return createIndex(row, column, childNode); }

index() fonksiyonu, QAbstractItemModeldan uyarlanmtr. Model ya da Grn, belirli bir ocuk e (ya da eer parent geersiz bir QModelIndex ise, bir st-seviye e) iin bir QModelIndex oluturmas gerektiinde arlr. Tablo ve liste Modelleri iin bu fonksiyonu uyarlamamz gerekmez, nk QAbstractListModeln ve QAbstractTableModeln varsaylan gerekletirimleri genellikle yeterli olur. Bizim index() gerekletirimimizde, eer hi ayrtrma aac ayarlanmamsa, geersiz bir QModelIndex dndrrz. Aksi halde, verilen satr, stun ve ocuk iin bir Node * ile bir QModelIndex olutururuz. Hiyerarik Modeller iin, bir enin, ebeveynine gre greli satr ve stununu bilmek onu tanmak iin yeterli deildir; ebeveyninin kim olduunu da bilmeliyiz. Bunu zmek iin, QModelIndex iinde, dhili dme iaret eden bir iareti saklayabiliriz. QModelIndex bize, satr ve stun numaralarna ek olarak bir void * ya da bir int saklama seenei verir. ocuk iin Node *, ebeveyn dmn children listesi sayesinde elde edilir. Ebeveyn dm, nodeFromIndex() private fonksiyonunu kullanarak, parent Model indeksinden elde edilir:
Node *BooleanModel::nodeFromIndex(const QModelIndex &index) const { if (index.isValid()) { return static_cast<Node *>(index.internalPointer()); } else { return rootNode; } }

nodeFromIndex() fonksiyonu, verilen indeksin void *ini Node *a dntrr, ya da, eer indeks geersiz ise, geersiz bir Model indeksi bir Modelde kk temsil etmekte kullanld iin, kk dm dndrr.
int BooleanModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; Node *parentNode = nodeFromIndex(parent); if (!parentNode) return 0; return parentNode->children.count(); }

Verilen bir e iin satrlarn says sadece sahip olduu ocuklar kadardr.
int BooleanModel::columnCount(const QModelIndex & /* parent */) const {

165

Qt 4 ile C++ GUI Programlama


return 2; }

Stunlarn says 2ye sabitlenmitir. lk stun dm tiplerini, ikinci stun dm deerlerini tutar.
QModelIndex BooleanModel::parent(const QModelIndex &child) const { Node *node = nodeFromIndex(child); if (!node) return QModelIndex(); Node *parentNode = node->parent; if (!parentNode) return QModelIndex(); Node *grandparentNode = parentNode->parent; if (!grandparentNode) return QModelIndex(); int row = grandparentNode->children.indexOf(parentNode); return createIndex(row, 0, parentNode); }

Bir ocuktan ebeveyn QModelIndexe erimek, bir ebeveynin ocuunu bulmaktan biraz daha zahmetlidir. nodeFromIndex()i kullanarak kolaylkla ebeveyn dme eriebilir ve Nodeun ebeveyn iaretisini kullanarak ykselebiliriz, fakat satr numarasn elde etmek iin, byk-ebeveyne(grandparent) geri gitmemiz ve onun ocuklarnn ebeveyn listesinde (rnein, ocuun byk-ebeveyninin) ebeveynin indeks konumunu bulmamz gerekir. Kod Grnm:
QVariant BooleanModel::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole) return QVariant(); Node *node = nodeFromIndex(index); if (!node) return QVariant(); if (index.column() == 0) { switch (node->type) { case Node::Root: return tr("Root"); case Node::OrExpression: return tr("OR Expression"); case Node::AndExpression: return tr("AND Expression"); case Node::NotExpression: return tr("NOT Expression"); case Node::Atom: return tr("Atom"); case Node::Identifier: return tr("Identifier"); case Node::Operator: return tr("Operator"); case Node::Punctuator: return tr("Punctuator"); default: return tr("Unknown"); }

166

Qt 4 ile C++ GUI Programlama


} else if (index.column() == 1) { return node->str; } return QVariant(); }

data()da, istenen e iin Node *a eriiriz ve esas veriye erimek iin onu kullanrz. Eer aran, Qt::DisplayRole dnda herhangi bir rol iin bir deer isterse ya da verilen Model indeksi iin bir Nodea eriemezsek, geersiz bir QVariant dndrrz. Eer stun 0 ise, dmn tipinin ismini, eer stun 1 ise, dmn deerini (karakter katarn) dndrrz.
QVariant BooleanModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) { return tr("Node"); } else if (section == 1) { return tr("Value"); } } return QVariant(); }

Bizim headerData() uyarlamamzda, uygun yatay balk etiketlerini dndrrz. Hiyerarik Modeller iin kullanlan QTreeView snf, dikey bala sahip deildir, bu nedenle bu olasl yoksayarz. Artk, Node ve BooleanModel snflarna deindik; imdi, kullanc satr editrndeki metni deitirdiinde kk dmn nasl oluturulduunu grelim:
void BooleanWindow::booleanExpressionChanged(const QString &expr) { BooleanParser parser; Node *rootNode = parser.parse(expr); booleanModel->setRootNode(rootNode); }

Kullanc, uygulamann satr editrndeki metni deitirdiinde, ana pencerenin booleanExpressionChanged() yuvas arlr. Bu yuvada, kullancnn metni ayrtrlr ve ayrtrc, ayrtrma aacnn kk dmne bir iareti dndrr. BooleanParser snfn gstermedik nk GUI ya da Model/Grn programlama ile ilgili deildir. Yine de rnein tam kaynan rnekler dizininde bulabilirsiniz. BooleanModel gibi aa modelleri gerekletirildiinde, QTreeViewn tuhaf davranlar sergilemesiyle sonulanabilecek hatalar yapmak olduka kolaydr. zel veri modellerinde problemleri bulmaya yardm etmek ve zmek iin bir ModelTest snf mevcuttur.

zel Delegeler Gerekletirme


Grnler iindeki zel eler, Delegeler(delegates) kullanlarak yorumlanr ve dzenlenirler. ou durumda, bir Grn tarafndan salanan varsaylan Delege yeterlidir. Eer elerin yorumlanmas zerinde daha ince bir kontrole sahip olmak istiyorsak, isteimizi basite zel bir Model kullanarak gerekletirebiliriz: data() gerekletirimimizde, Qt::FontRole, Qt::TextAlignmentRole, 167

Qt 4 ile C++ GUI Programlama Qt::TextColorRole ve Qt::BackgroundColorRole ileyebiliriz ve bunlar varsaylan Delege tarafndan kullanlabilir. rnein, daha nce grdmz Cities ve Currencies rneklerinde, Qt::TextAlignmentRolen saa hizal saylar elde etmede kullanmtk. Eer daha da byk derecede kontrol istiyorsak, kendi Delege snfmz oluturabilir ve onu, kullanmak istediimiz Grnler zerinde ayarlayabiliriz. ekil 9.15te gsterilen Track Editor diyalou, zel bir Delege kullanr. Mzik paralarnn balklarn(titles) ve srelerini gsterir. Model tarafndan tutulan veri sadece QStringler (balklar) ve intler (saniyeler) olacak, fakat sreler dakika ve saniyelere ayrlm olacak ve bir QTimeEdit kullanlarak dzenlenebilir olacak.

ekil 9.15

Track Editor diyalou, QTableWidgetItemlar zerinde iletilen bir e grntleme uygunluk altsnf olan, bir QTableWidget kullanr. Veri, Tracklarn bir listesi olarak salanr:
class Track { public: Track(const QString &title = "", int duration = 0); QString title; int duration; };

te, kurucudan, tablo paracnn oluumunu ve dolduruluunu gsteren bir alnt:


TrackEditor::TrackEditor(QList<Track> *tracks, QWidget *parent) : QDialog(parent) { this->tracks = tracks; tableWidget = new QTableWidget(tracks->count(), 2); tableWidget->setItemDelegate(new TrackDelegate(1)); tableWidget->setHorizontalHeaderLabels( QStringList() << tr("Track") << tr("Duration")); for (int row = 0; row < tracks->count(); ++row) { Track track = tracks->at(row); QTableWidgetItem *item0 = new QTableWidgetItem(track.title); tableWidget->setItem(row, 0, item0); QTableWidgetItem *item1 = new QTableWidgetItem(QString::number(track.duration));

168

Qt 4 ile C++ GUI Programlama


item1->setTextAlignment(Qt::AlignRight); tableWidget->setItem(row, 1, item1); } ... }

Kurucu, bir tablo parac oluturur ve varsaylan Delegeyi kullanmak yerine, bizim zel TrackDelegate Delegemizi kullanrz, ve onu zaman verisini tutan stuna aktarrz. Stun balklarn ayarlayarak balarz ve sonrada veriyi batan sona yineleriz. Satrlar ise her bir parann isim ve sresiyle doldururuz. Kurucunun ve TrackEditor snfnn geri kalan hibir srpriz barndrmaz, bu nedenle imdi, gereklemeyi ve mzik paras verisinin dzenlenmesini ele alan TrackDelegatee bakacaz.
class TrackDelegate : public QItemDelegate { Q_OBJECT public: TrackDelegate(int durationColumn, QObject *parent = 0); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; private slots: void commitAndCloseEditor(); private: int durationColumn; };

Ana snfmz olarak QItemDelegatei kullanrz, bylece varsaylan Delege gerekletiriminden yararlanrz. Ayrca, eer en batan balamak isteseydik, QAbstractItemDelegatei de kullanabilirdik. Veriyi dzenleyebilen bir Delege ihtiyacn karlamak iin, createEditor(), setEditorData() ve setModelData()y da gerekletirmemiz gerekir. Ayrca, sre stununun gereklenmesini deitirmek iin paint()i gerekletirmeliyiz.
TrackDelegate::TrackDelegate(int durationColumn, QObject *parent) : QItemDelegate(parent) { this->durationColumn = durationColumn; }

DurationColumn parametresi kurucuya, parann sresini tutan stunu bildirir.


void TrackDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.column() == durationColumn) { int secs = index.model()->data(index, Qt::DisplayRole).toInt(); QString text = QString("%1:%2")

169

Qt 4 ile C++ GUI Programlama


.arg(secs / 60, 2, 10, QChar('0')) .arg(secs % 60, 2, 10, QChar('0')); QStyleOptionViewItem myOption = option; myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; drawDisplay(painter, myOption, myOption.rect, text); drawFocus(painter, myOption, myOption.rect); } else{ QItemDelegate::paint(painter, option, index); } }

Sreyi dakika : saniye biiminde sunmak istediimiz iin paint() fonksiyonunu uyarlarz. arg() ars, bir karakter katar olarak sunmak iin bir tamsay, karakter katarnn uzunluunu, tamsaynn tabann (onlu iin 10) ve dolgu(padding) karakterini (QChar(0)) alr. Saa hizal metin iin, geerli stil seeneklerini kopyalarz ve varsaylan hizalann zerine yazarz. Sonra, metni izmek iin, e bir odaa sahip olduunda, dikdrtgen bir odak izecek, aksi halde hibir ey yapmayacak olan QItemDelegate::drawFocus()un ardndan QItemDelegate::drawDisplay()i arrz. drawDisplay()i kullanmak ok pratiktir, zelliklede kendi stil seeneklerimizle kullanldnda. Dorudan, izici kullanarak da izebilirdik.
QWidget *TrackDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.column() == durationColumn) { QTimeEdit *timeEdit = new QTimeEdit(parent); timeEdit->setDisplayFormat("mm:ss"); connect(timeEdit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseEditor())); return timeEdit; } else { return QItemDelegate::createEditor(parent, option, index); } }

Sadece parann sresinin dzenlenmesini kontrol etmek, para isminin dzenlenmesini varsaylan Delegeye brakmak istiyoruz. Bunu, Delegenin hangi stun iin bir editr saladn kontrol ederek baarabiliriz. Eer sre stunu ise, bir QTimeEdit olutururuz, grntleme biimini(display format) uygun olarak ayarlarz ve onun editingFinished() sinyalini, bizim commitAndCloseEditor() yuvamza balarz. Dier herhangi bir stun iin, dzenleme idaresini varsaylan Delegeye aktarrz.
void TrackDelegate::commitAndCloseEditor() { QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender()); emit commitData(editor); emit closeEditor(editor); }

Eer kullanc Entera basarsa ya da oda QTimeEditin dna tarsa (fakat Esce basmadan), editingFinished() sinyali yaylr ve commitAndCloseEditor() yuvas arlr. Bu yuva, Grne dzenlenmi verinin varln bildirmek ve bylece dzenlenmi verinin var olan verinin yerini almas iin, commitData() sinyalini yayar. Ayrca, Grne bu editre daha fazla gerek duyulmadn bildirmek iin 170

Qt 4 ile C++ GUI Programlama closeEditor() sinyalini de yayar; bu durumda Model onu silecektir. Editre, yuvay tetikleyen sinyali yayan nesneyi dndren QObject::sender() kullanlarak eriilir. Eer kullanc iptal ederse (Esce basarak), Grn sadece editr silecektir.
void TrackDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.column() == durationColumn) { int secs = index.model()->data(index, Qt::DisplayRole).toInt(); QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor); timeEdit->setTime(QTime(0, secs / 60, secs % 60)); } else { QItemDelegate::setEditorData(editor, index); } }

Kullanc dzenlemeyi balattnda, Grn bir editr oluturmak iin createEditor() arr ve sonra editr ilk kullanma hazrlamak iin setEditorData()y enin geerli verisi ile arr. Eer editr sre stunu iinse, parann sresini saniye olarak salarz ve QTimeEditin zamann, dakikalarn ve saniyelerin yerini tutan saylara ayarlarz; aksi halde, ilk kullanma hazrlamay varsaylan Delegenin idaresine brakrz.
void TrackDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.column() == durationColumn) { QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor); QTime time = timeEdit->time(); int secs = (time.minute() * 60) + time.second(); model->setData(index, secs); } else { QItemDelegate::setModelData(editor, model, index); } }

Eer kullanc dzenlemeyi iptal etmektense onu tamamlarsa (editr paracnn dna tklayarak ya da Enter veya Taba basarak), Model editrn verisi ile gncellenmelidir. Eer sre dzenlenmise, QTimeEditten dakikalar ve saniyeleri salarz ve veriyi saniyelerin yerini tutan saylara ayarlarz. Bu durumda gerekli olmasa da, bir Model iindeki herhangi bir verinin dzenlenmesini ve sunulmasn tmyle kontrol eden zel bir delege oluturmak da mmkndr. Biz, belirli bir stunun kontroln elde etmeyi setik, fakat QModelIndex, uyarladmz btn fonksiyonlara aktarld iin, stunun yannda, satrn, dikdrtgen blgenin, ebeveynin ya da bunlarn herhangi bir kombinasyonunun, yani ihtiyacmz olan tm elerin kontroln de elde edebiliriz.

171

Qt 4 ile C++ GUI Programlama

BLM 10: KONTEYNER SINIFLARI

Konteyner snflar, verilen bir tipteki eleri bellekte saklayan genel amal ablon snflardr. C++ hlihazrda, Standart C++ ktphanesine dhil olan Standart ablon Ktphanesi(Standard Template Library STL) ile birok konteyner sunar. Qt kendi konteyner snflarn sunar, fakat Qt programlar iin hem Qt hem de STL konteynerlerini kullanabiliriz. Qt konteynerlerinin balca avantajlar, tm platformlarda ayn ekilde davranmalar ve rtl olarak paylalmalardr. rtk paylam(Implicit sharing) ya da copy on write, kayda deer herhangi bir performans kayb olmadan, konteynerlerin tmn deerler gibi aktarmay mmkn klan bir optimizasyondur. Qt, hem QVector<T>, QLinkedList<T> ve QList<T> gibi ardk(sequential) konteynerler, hem de QMap<K, T> ve QHash<K, T> gibi birleik(associative) konteynerler sunar. Kavramsal olarak, ardk konteynerler eleri birbiri ardna saklarken, birleik konteynerler anahtar-deer(key-value) iftlerini saklarlar. Qt ayrca, istee bal konteynerler zerinde ilemler yapan genel algoritmalar(generic algorithms) salar. rnein, qSort() algoritmas ardk bir konteyneri sralar ve qBinaryFind() sralanm bir ardk konteyner zerinde ikili(binary) arama yapar. Bu algoritmalar, STL tarafndan sunulanlarla benzerdirler. Eer zaten STL konteynerlerine ainaysanz ve hedef platformlarnzda STL kullanlabilir durumdaysa, onlar, Qt konteynerlerinin yerine ya da onlara ek olarak kullanmak isteyebilirsiniz. STL snflar ve fonksiyonlar hakknda daha fazla bilgi edinmeye balamak iin SGIn STL web sitesi iyi bir yerdir: http:://www.sgi.com/tech/stl/. Bu blmde, konteynerler ile birok ortaklklar olduu iin, QString, QByteArray ve QVarianta da bakacaz. QString, Qtun APInn her tarafnda kullanlan 16-bit Unicode bir karakter katardr. QByteArray, ham(raw) ikili veriyi saklamak iin faydal, 8-bitlik charlarn bir dizisidir. QVariant, birok C++ ve Qt deer tiplerini saklayabilen bir tiptir.

Ardk Konteynerler
QVector<T>, elerini hafzada bitiik konumlarda saklayan, dizi benzeri bir veri yapsdr (ekil 10.1). Bir vektr, kendi boyutunu bilmesi ve yeniden boyutlandrlabilmesi ile sade bir C++ dizisinden ayrlr. Bir vektrn sonuna ekstra eler eklemek olduka verimlidir, oysa bir vektrn nne ya da ortasna eler eklemek pahalya mal olabilir.

ekil 10.1

172

Qt 4 ile C++ GUI Programlama Eer peinen ka eye ihtiyacmz olacan biliyorsak, onu tanmlarken bir ilk boyut verebiliriz ve elere bir deer atamakta [] operatrn kullanabiliriz; aksi halde, vektr daha sonra yeniden boyutlandrmalyz ya da eleri sona eklemeliyiz (append() fonksiyonunu kullanarak). te, ilk boyutunu belirttiimiz bir rnek:
QVector<double> vect(3); vect[0] = 1.0; vect[1] = 0.540302; vect[2] = -0.416147;

Bu da ayns, fakat bu sefer bo bir vektrle balarz ve append() fonksiyonunu kullanarak eleri sona ekleriz:
QVector<double> vect; vect.append(1.0); vect.append(0.540302); vect.append(-0.416147);

append() fonksiyonunun yerine << operatrn de kullanabiliriz:


vect << 1.0 << 0.540302 << -0.416147;

Vektrn elerini yinelemenin bir yolu da, [] ve count() kullanmaktr:


double sum = 0.0; for (int i = 0; i < vect.count(); ++i) sum += vect[i];

Belli bir deer atamas olmakszn oluturulan vektr girdileri, enin varsaylan kurucusu kullanlarak ilk kullanma hazrlanr. Temel tipler ve iareti tipleri 0a ilklendirilirler. Bir QVector<T>n balangcna ya da ortasna eler eklemek, ya da bu konumlardan eler silmek, byk vektrler iin verimsiz olabilir. Bu sebeple Qt, elerini hafzada ekil 10.2de gsterildii gibi, bitiik olmayan konumlarda saklayan bir veri yaps olan QLinkedList<T>i de sunar. Vektrlerden farkl olarak, bal listeler(linked lists) rastgele eriimi desteklemezler, fakat sabit zaman(constant time) eklemeleri ve silmeleri salarlar.

ekil 10.2

Bal listeler [] operatr salamazlar, bu nedenle yineleyiciler onlarn elerini apraz gei yaparak kullanmaldrlar. Yineleyiciler, elerin konumlarn belirlemede de kullanlrlar. rnein, sradaki kod, Tote Hosen karakter katarn, Clash ve Ramonesin arasna ekler:
QLinkedList<QString> list; list.append("Clash"); list.append("Ramones"); QLinkedList<QString>::iterator i = list.find("Ramones"); list.insert(i, "Tote Hosen");

Yineleyicilere bu ksmda daha sonra detaylca bakacaz.

173

Qt 4 ile C++ GUI Programlama QList<T> ardk konteyneri, QVector<T> ve QLinkedList<T>in en nemli faydalarn tek bir snfta birletiren bir dizi listesidir(array-list). Rastgele eriimi destekler ve arabirimi QVectorlerin ki gibi indeks temellidir. Bir QList<T>in sonundaki bir eyi silmek ya da bir QList<T>in sonuna bir e eklemek ok hzldr. Ortaya ekleme ise, bin kadar esi olan listeler iin yine ok hzldr. Qtun APInda da ska kullanlan QStringList snf, QList<QString>in bir altsnfdr. Temel snfndan miras ald fonksiyonlara ek olarak, snf, karakter katar ilemede daha becerikli yapan ekstra fonksiyonlar salar. QStringList ile ayrntl olarak bu blmn son ksmnda ilgileneceiz. QStack<T> ve QQueue<T>, uygunluk altsnflarnn iki rnekleridirler. QStack<T>, push(), pop() ve top() fonksiyonlarn salayan bir vektrdr. QQueue<T> ise, enqueue(), dequeue() ve head() fonksiyonlarn salayan bir listedir. imdiye kadar grdmz tm konteyner snflar iin, deer tipi T; int veya double gibi basit bir tip, bir iareti tipi, ya da bir varsaylan kurucuya (argman almayan bir kurucu), bir kopyalama kurucusuna ve bir atama operatrne sahip olan bir snf olabilir. QByteArray, QDataTime, QRegExp, QString ve QVariant snflarna da bu hak verilmitir. Fakat QObjectten tretilmi snflara bu hak tannmaz, nk onlar bir kopyalama kurucusundan ve bir atama operatrnden yoksundurlar. Fakat biz, nesnelerin kendilerini saklamak yerine, QObject tiplerine iaret eden iaretileri sakladmz iin, bu, pratikte bir sorun oluturmaz. Deer tipi T bir konteyner de olabilir, byle bir durumda birbirini izleyen keli parantezleri boluklarla ayrmay unutmamamz gerekir; aksi halde, derleyici onun bir >> operatr olduunu sanarak tkanacaktr. rnein:
QList<QVector<double> > list;

Bahsedilmi olan tiplere ek olarak, bir konteynerin deer tipi, daha evvel tanmlanm kriterleri karlayan herhangi bir zel snf da olabilir. te, byle bir snfn rnei:
class Movie { public: Movie(const QString &title = "", int duration = 0); void setTitle(const QString &title) { myTitle = title; } QString title() const { return myTitle; } void setDuration(int duration) { myDuration = duration; } QString duration() const { return myDuration; } private: QString myTitle; int myDuration; };

Snf, hi argman gerektirmeyen (buna ramen iki adet alabilen) bir kurucuya sahiptir. Her biri dolayl olarak C++ tarafndan salanan, bir kopyalama kurucu fonksiyonu ve bir atama operatrne de sahiptir. Bu snf iin, ye-ye kopyalama(member-by-member copy) yeterlidir, bylece kendi kopyalama kurucu fonksiyonumuzu ve atama operatrmz gerekletirmemiz gerekmez.

174

Qt 4 ile C++ GUI Programlama Qt, bir konteynerde saklanan eleri ele almak iin yineleyicilerin iki kategorisini salar: Java-tarz yineleyiciler ve STL-tarz yineleyiciler. Java-tarz yineleyicilerin kullanm daha kolayken, STL-tarz yineleyiciler, Qtun ve STLin genel algoritmalaryla kombine edilebilirler ve daha gldrler. Her bir konteyner snf iin, iki tip Java-tarz yineleyici vardr: bir sadece-oku(read-only) ve bir de okuyaz(read-write) yineleyici. Geerli konumlar ekil 10.3te gsteriliyor. Sadece-oku yineleyici snflar, QVectorIterator<T>, QLinkedListIterator<T> ve QListIterator<T>dr. Bunlara karlk gelen oku-yaz yineleyiciler isimlerinde Mutablea sahiptirler (QMutableVectorIterator<T> gibi). Burada, QListin yineleyicilerine younlaacaz.

ekil 10.3

Java-tarz yineleyicileri kullanrken aklnzda bulundurmanz gereken ilk ey, onlarn, elere direkt olarak iaret etmedikleridir. Onun yerine, ilk eden nce veya son eden sonra ya da bu iki enin arasnda bir yerde yer alrlar. Tipik bir yineleme(iteration) dngs yle grnr:
QList<double> list; ... QListIterator<double> i(list); while (i.hasNext()) { do_something(i.next()); }

Yineleyici, ele alnacak konteyner ile ilk kullanma hazrlanr. Bu noktada, yineleyici tam olarak ilk eden nce yer alr. Eer yineleyicinin sanda bir e varsa, hasNext() ars true dndrr. next() fonksiyonu yineleyicinin sandaki eyi dndrr ve yineleyiciyi bir sonraki geerli konuma ilerletir. Geriye doru yineleme ise, yineleyiciyi son eden sonrasna konumlandrmak iin ilk nce toBack()i armak zorunda olmamz dnda benzerdir.
QListIterator<double> i(list); i.toBack(); while (i.hasPrevious()) { do_something(i.previous()); }

hasPrevious() fonksiyonu eer enin sol tarafnda e varsa, true dndrr; previous(), yineleyicinin solundaki eyi dndrr ve yineleyiciyi bir adm geriye tar. next() ve previous() yineleyicilerini tasavvur etmenin bir dier yolu da, zerlerinden atladklar eleri dndrdklerini dnmektir, tpk ekil 10.4te gsterildii gibi.

ekil 10.4

Mutable yineleyiciler, yineleme srasnda eleri dzenlemeyi ve silmeyi ve eler eklemeyi salayan fonksiyonlar salarlar. Sradaki dng, tm negatif saylar bir listeden siler:

175

Qt 4 ile C++ GUI Programlama


QMutableListIterator<double> i(list); while (i.hasNext()) { if (i.next() < 0.0) i.remove(); }

remove() fonksiyonu, daima zerinden atlad son e stnde ilem yapar. Ayrca, geriye doru yinelemede de alr:
QMutableListIterator<double> i(list); i.toBack(); while (i.hasPrevious()) { if (i.previous() < 0.0) i.remove(); }

Benzer ekilde, mutable Java-tarz yineleyiciler, zerinden atlad son eyi dzenleyen bir setValue() fonksiyonu salar. te, negatif saylar, onlarn mutlak deerleriyle deitiren bir rnek:
QMutableListIterator<double> i(list); while (i.hasNext()) { int val = i.next(); if (val < 0.0) i.setValue(-val); }

Ayn zamanda, insert()i ararak, geerli yineleyici konumuna bir e eklemek de mmkndr. Ekleme sonrasnda, yineleyici yeni e ile sonraki e arasna ilerletilir. Java-tarz yineleyicilere ek olarak, her ardk konteyner snf C<T>, iki STL-tarz yineleyici tipine sahiptir: C<T>::iterator ve C<T>::const_iterator. kisi arasndaki fark, const_iteratorn veriyi dzenlemeye izin vermemesidir. Bir konteynerin begin() fonksiyonu, konteynerin ilk esine sevk eden bir STL-tarz yineleyici dndrrken (list[0] gibi), end() fonksiyonu, sondan bir sonraki eye sevk eden bir yineleyici dndrr (boyutu be olan bir liste iin list[5]tir). ekil 10.5, STL-tarz yineleyiciler iin geerli konumlar gsteriyor. Eer bir konteyner bo ise, begin(), end()e eittir. Bu, konteynerde e olup olmadn grmek iin kullanlabilir, geri isEmpty() ars bu ama iin genellikle daha uygundur.

ekil 10.5

STL-tarz yineleyicilerin szdiziminde, C++ iaretilerin ki rnek alnmtr. Sonraki ya da nceki eye gemek iin ++ ve -- operatrlerini, geerli eye erimek iin birli(unary) * operatrn kullanabiliriz. QVector<T> iin, iterator ve const_iterator tipleri, srf T * ve const T * iin tanmlanm tiplerdir. (Bu mmkndr, nk QVector<T>, elerini ardk bellek konumlarnda saklar.) Sradaki rnek, bir QList<double> iindeki her bir deeri, bu deerlerin mutlak deerleri ile deitirir:
QList<double>::iterator i = list.begin(); while (i != list.end()) {

176

Qt 4 ile C++ GUI Programlama


*i = qAbs(*i); ++i; }

Az miktarda Qt fonksiyonu bir konteyner dndrr. Eer STL-tarz yineleyici kullanan bir fonksiyonun dnen deeri zerinde yineleme yapmak istiyorsak, konteynerin bir kopyasn almalyz ve bu kopya zerinde yineleme yapmalyz. rnein, sradaki kod, QSplitter::sizes() tarafndan dndrlen QList<int> zerinde yineleme yapmak iin doru yoldur:
QList<int> list = splitter->sizes(); QList<int>::const_iterator i = list.begin(); while (i != list.end()) { do_something(*i); ++i; }

u kod ise yanltr:


// WRONG QList<int>::const_iterator i = splitter->sizes().begin(); while (i != splitter->sizes().end()) { do_something(*i); ++i; }

Bunun nedeni, QSplitter::sizes() her arldnda, yeni bir QList<int>i deer olarak dndrmesidir. Eer dnen deeri saklamazsak, C++ onu yinelemeye balamadan nce otomatik olarak yok eder, bizi ie yaramaz bir yineleyici ile kar karya brakr. Daha da kts, dngnn alt her zaman, QSplitter::sizes(), listenin yeni bir kopyasn retmek zorundadr, nk splitter>sizes().end() arlr. zet olarak: STL-tarz yineleyicileri kullanrken, her zaman, deer olarak dndrlen bir konteynerin bir kopyas zerinde yineleme yapn. Sadece-oku Java-tarz yineleyicilerle, bir kopya almamza gerek yoktur. Yineleyici bizim iin, perde arkasnda bir kopya alr ve bu, her zaman fonksiyondan ilk dnen veri zerinde yineleme yapmamz salar. rnein:
QListIterator<int> i(splitter->sizes()); while (i.hasNext()) { do_something(i.next()); }

Bir konteyneri kopyalamak pahalya mal olabilecek gibi grnr, fakat -rtl paylam sayesinde- yle deildir. Bunun anlam, bir Qt konteynerini kopyalamak, tek bir iaretiyi kopyalamak kadar hzldr. Yalnz, eer kopyalardan birinin verisi deitirilirse gerekten kopyalanr ve tm ilemler otomatik olarak perde arkasnda yaplr. rtl paylamn gzellii, programc mdahalesine gerek duymakszn basite alan bir optimizasyon olmasdr. rtl paylam ayn zamanda, nesneler deer olarak dndrld yerde temiz bir programlamay tevik eder. u fonksiyonu dikkat edin:
QVector<double> sineTable() { QVector<double> vect(360); for (int i = 0; i < 360; ++i)

177

Qt 4 ile C++ GUI Programlama


vect[i] = std::sin(i / (2 * M_PI)); return vect; }

Fonksiyona ar aadaki gibidir:


QVector<double> table = sineTable();

STL, bizi, fonksiyonun dn deeri bir deikende saklandnda, kopyalamay nlemek iin, vektr sabit olmayan bir referans olarak aktarmaya tevik eder:
void sineTable(std::vector<double> &vect) { vect.resize(360); for (int i = 0; i < 360; ++i) vect[i] = std::sin(i / (2 * M_PI)); }

ar, ondan sonra, yazmas ve okumas daha can skc bir hal alr:
std::vector<double> table; sineTable(table);

Qt, rtl paylam, tm konteynerleri iin ve QByteArray, QBrush, QFont, QImage, QPixmap ve QString de dhil birok snf iin kullanr. Bu, bu snflar deer olarak aktarmada ok verimli yapar. rtl paylam, Qtdan, bir verinin biz onu dzenlemek istemediimiz takdirde kopyalanmayacana dair bir garantidir. Qt, ardk bir konteyner iinde yineleme yapmak iin son bir metot daha salar: foreach dngs.
QLinkedList<Movie> list; ... foreach (Movie movie, list) { if (movie.title() == "Citizen Kane") { std::cout << "Found Citizen Kane" << std::endl; break; } }

foreach szde anahtar kelimesi, standart for dngsnce gerekletirilir. Dngnn her bir yinelenmesinde, yineleme deikeni (movie) yeni bir eye yerletirilir, konteynerdeki ilk eden balanr ve sonrakine ilerlenir. foreach dngs, dngye girildiinde, otomatik olarak konteynerin bir kopyasn alr ve bu nedenle eer konteyner yineleme srasnda dzenlenirse, dng bundan etkilenmez. break ve continue dng ifadeleri desteklenir. Eer gvde tek bir ifadeden oluuyorsa, ssl parantezler gereksizdir. Tpk bir for ifadesi gibi, yineleme deikeni dng dnda tanmlanabilir:
QLinkedList<Movie> list; Movie movie; ... foreach (movie, list) { if (movie.title() == "Citizen Kane") { std::cout << "Found Citizen Kane" << std::endl; break; }

178

Qt 4 ile C++ GUI Programlama


}

Birleik Konteynerler
Birleik konteynerler, ayn tip elerden istee bal sayda kadarn tutar, bir anahtar ile indeksler. Qt, iki ana birleik konteyner snf salar: QMap<K, T> ve QHash<K, T>. Bir QMap<K, T>, anahtar-deer iftlerini artan anahtar sras iinde saklayan bir veri yapsdr (ekil 10.6). Bu dzen, iyi bir arama ve ekleme performans ve anahtar-sra yinelemesi salar. Aslen, QMap<K, T> bir srama listesi(skip-list) olarak gerekletirilmitir.

ekil 10.6

Bir haritaya(map) eler eklemenin basit bir yolu insert() armaktr:


QMap<QString, int> map; map.insert("eins", 1); map.insert("sieben", 7); map.insert("dreiundzwanzig", 23);

Alternatif olarak, aadaki gibi basite bir deeri, verilen bir anahtara atayabiliriz:
map["eins"] = 1; map["sieben"] = 7; map["dreiundzwanzig"] = 23;

[] operatr, hem ekleme hem de eriim iin kullanlabilir. Eer [] operatr, mutable bir harita ierisinde, var olmayan bir anahtardaki bir veriye erimede kullanlrsa, verilen anahtar ve bo bir deer ile yeni bir e oluturulacaktr. Kazara bo deerler oluturmay nlemek iin, elere eriirken value() fonksiyonunu kullanabiliriz.
int val = map.value("dreiundzwanzig");

Eer anahtar bulunmuyorsa, deer tipinin varsaylan kurucusu kullanlarak varsaylan bir deer dndrlr ve yeni bir deer oluturulmaz. Temel ve iareti tipleri iin, sfr dndrlr. value()ya ikinci bir argman aktararak baka bir varsaylan deer belirtebiliriz, rnein:
int seconds = map.value("delay", 30);

Bu, una denktir


int seconds = 30; if (map.contains("delay")) seconds = map.value("delay");

Bir QMap<K, T>n K ve T veri tipleri, int ve double gibi temel veri tipleri, iareti tipleri ya da bir varsaylan kurucuya, bir kopyalama kurucu fonksiyonuna ve bir atama operatrne sahip snflar olabilir. Ek

179

Qt 4 ile C++ GUI Programlama olarak, K tipi, QMap<K, T>,< operatrn eleri artan anahtar sras iinde saklamada kulland iin, bir operator<() salamaldr. QMap<K, T>, bir ift uygunluk fonksiyonuna sahiptir. keys() ve values(), zellikle kk veri setleriyle ilgilenirken ok kullanldrlar. Haritann anahtar ve deerlerinin QListlerini dndrrler. Haritalar normalde tek deerlidirler(singled-valued): Eer var olan bir anahtara yeni bir deer atanrsa, yeni deer eski deerin yerine konur; bunu salamak iin de, hibir e baka bir e ile ayn anahtar paylamaz. Bunun yannda, insertMulti() fonksiyonunu ya da QMultiMap<K, T> uygunluk altsnfn kullanarak, oklu anahtar-deer iftlerine sahip olmak mmkndr. QMap<K, T>, verilen anahtarn tm deerlerinin bir QListini dndren ar yklenmi bir values(const K &) fonksiyonuna da sahiptir. rnein:
QMultiMap<int, QString> multiMap; multiMap.insert(1, "one"); multiMap.insert(1, "eins"); multiMap.insert(1, "uno"); QList<QString> vals = multiMap.values(1);

Bir QHash<K, T>, anahtar-deer iftlerini bir anahtarl tablo(hash table) iinde saklayan bir veri yapsdr. Arayz hemen hemen QMap<K, T>n ki ile ayndr, fakat K ablon tipi iin farkl gerekliliklere sahiptir ve genellikle QMap<K, T>n baarabildiinden daha hzl aramalar salar. Bir baka farkllk ise, QHash<K, T> sral deildir. Bir konteyner iinde saklanan her veri tipi iin istenen standart gerekliliklere ek olarak, bir QHash<K, T>n K tipi, bir operator==() salamaldr ve bir anahtar iin bir hash deeri dndren global bir qHash() tarafndan destekleniyor olmaldr. Qt, hlihazrda, tamsay tipleri, iareti tipleri, QChar, QString ve QByteArray iin qHash() fonksiyonlar salamaktadr. reserve(), hash iinde saklanmas beklenen elerin saysn belirtmek iin, squeeze()i de geerli elerin saysna bal olarak anahtarl tabloyu kltmek iin ararak, performans ince-ayar yapmak mmkndr. Yaygn bir kullanm ekli, reserve() -beklediimiz- maksimum e says ile armak, sonra verileri eklemek ve son olarak, eer beklenenden daha az e varsa, hafza kullanm azaltmak iin squeeze()i armaktr. Anahtarl tablolar normalde tek deerlidirler, fakat ayn anahtara oklu deerler atama, insertMulti() fonksiyonunu ya da QMultiHash<K, T> uygunluk altsnfn kullanmak suretiyle yaplabilmektedir. QHash<K, T>ten baka, Qt, nbellek(cache) nesnelerini bir anahtar ile birletirmede kullanlabilen bir QCache<K, T> snf ve sadece anahtarlar saklayan bir QSet<K> konteyneri de salar. Aslen, ikisi de QHash<K, T>e dayanrlar ve ikisi de K tipi iin QHash<K, T> ile ayn gerekliliklere sahiptirler. Birleik bir konteyner iinde saklanan tm anahtar-deer iftleri boyunca yineleme yapmann en kolay yolu Java-tarz bir yineleyici kullanmaktr. nk yineleyiciler hem bir anahtara hem de bir deere erimelidirler, ve Java-tarz yineleyiciler, birleik konteynerler iin, onlarn ardk emsallerine gre, bir para farkl alrlar. Ana farkllk, next() ve previous() fonksiyonlarnn, bir anahtar-deer iftini temsil eden bir nesne ya da tercihen sadece bir deer dndrmesidir. Anahtar ve deer bileenlerine, bu nesneden, key() ve value() kullanlarak eriilebilir. rnein:

180

Qt 4 ile C++ GUI Programlama


QMap<QString, int> map; ... int sum = 0; QMapIterator<QString, int> i(map); while (i.hasNext()) sum += i.next().value();

Eer hem anahtara hem de deere erimemiz gerekiyorsa, aka next() ya da previous()n dn deerlerini yoksayabilir ve yineleyicinin key() ve value() fonksiyonlarn kullanabiliriz.
QMapIterator<QString, int> i(map); while (i.hasNext()) { i.next(); if (i.value() > largestValue) { largestKey = i.key(); largestValue = i.value(); } }

Mutable yineleyiciler, geerli e ile ilgili deeri dzenleyen setValue() fonksiyonuna sahiptir:
QMutableMapIterator<QString, int> i(map); while (i.hasNext()) { i.next(); if (i.value() < 0.0) i.setValue(-i.value()); }

STL-tarz yineleyiciler de key() ve value() fonksiyonlarn salarlar. Sabit olmayan(non-const) yineleyici tipleriyle, value() sabit olmayan bir referans dndrr ve bize, yineleme yaparken deeri deitirme imkn verir. Bu yineleyiciler STL-tarz olarak adlandrlsalar da, std::pair<K, T> esas alan std::map<K, T> yineleyicilerinden nemli derecede ayrlrlar. foreach dngs, birleik konteynerler zerinde de alr, fakat anahtar-deer iftlerinin sadece deer bileeni zerinde. Eer hem anahtar hem de deer bileenlerine ihtiyacmz varsa, aadaki gibi, i ie foreach dngleri iinde keys() ve values(const K &) fonksiyonlarn kullanabiliriz:
QMultiMap<QString, int> map; ... foreach (QString key, map.keys()) { foreach (int value, map.values(key)) { do_something(key, value); } }

Genel Algoritmalar
<QtAlgorithms> bal, konteynerler zerinde temel algoritmalar gerekletiren global ablon fonksiyonlarnn bir setini bildirir. Bu fonksiyonlarn ou, STL-tarz yineleyiciler zerinde alrlar. STL <algorithm> bal, genel algoritmalarn daha eksiksiz bir setini salar. Bu algoritmalar, Qt konteynerleri zerinde, STL konteynerleriymi gibi kullanlabilirler. STL gerekletirimleri tm platformlarnda mevcutsa, Qt denk bir algoritma sunmadnda, STL algoritmalarn kullanmay engelleyecek bir neden muhtemelen yoktur. Burada, en nemli Qt algoritmalarn tantacaz.

181

Qt 4 ile C++ GUI Programlama qFind() algoritmas, bir konteyner iinde belirli bir deeri arar. Bir begin ve bir end yineleyicisi alr ve eleen ilk eye iaret eden bir yineleyici, ya da hi eleen yok ise, end dndrr. Aadaki rnekte, i, list.begin() + 1 olarak belirlenirken, j, list.end() olarak belirlenir:
QStringList list; list << "Emma" << "Karl" << "James" << "Mariette"; QStringList::iterator i = qFind(list.begin(), list.end(), "Karl"); QStringList::iterator j = qFind(list.begin(), list.end(), "Petra");

qBinaryFind() algoritmas, tpk qFind() gibi bir arama gerekletirir, ancak qFind() gibi dorusal arama yapmaktansa elerin artan dzende sralandn varsayarak, hzl ikili aramay(fast binary searching) kullanr. qFill() algoritmas, bir konteyneri belirli bir deer ile doldurur:
QLinkedList<int> list(10); qFill(list.begin(), list.end(), 1009);

Dier yineleyici temelli algoritmalar gibi, qFill()i farkl argmanlar kullanarak, konteynerin bir paras zerinde de kullanabiliriz. Aadaki kod paras, bir vektrn ilk be esini 1009a, son 5 esini de 2013e ilklendiriyor:
QVector<int> vect(10); qFill(vect.begin(), vect.begin() + 5, 1009); qFill(vect.end() - 5, vect.end(), 2013);

qCopy() algoritmas, deerleri bir konteynerden bir dierine kopyalar:


QVector<int> vect(list.count()); qCopy(list.begin(), list.end(), vect.begin());

qCopy(), kaynan menzili ile hedefin menzili akmad srece, deerleri ayn konteyner iinde kopyalamakta da kullanlabilir. Sradaki kod parasnda, onu, bir listenin ilk iki esini, listenin son iki esi zerine yazmada kullanyoruz:
qCopy(list.begin(), list.begin() + 2, list.end() - 2);

qSort() algoritmas, konteynerin elerini artan dzende sralar:


qSort(list.begin(), list.end());

qSort() varsaylan olarak, eleri karlatrmak iin < operatrn kullanr. eleri azalan dzende sralamak iin, nc argman olarak qGreater<T> (T yerine konteynerin deer tipini yazarak) aktarn:
qSort(list.begin(), list.end(), qGreater<int>());

nc parametreyi, zel sralama kriteri tanmlamada da kullanabiliriz. rnein burada, QStringleri byk/kk harf duyarsz bir yntemle karlatran, bir karlatrma fonksiyonu:
bool insensitiveLessThan(const QString &str1, const QString &str2) { return str1.toLower() < str2.toLower(); }

182

Qt 4 ile C++ GUI Programlama yleyse qSort() ars u hali alr:


QStringList list; ... qSort(list.begin(), list.end(), insensitiveLessThan);

qStableSort() algoritmas ise, karlatrlan eler arasnda eit grnenlerin sralama sonrasnda, sralama ncesindeki gibi ayn srada olmalarn garanti eder. Bu, eer sralama kriteri sadece deerin paralarn hesaba katyorsa ve sonular kullanc tarafndan grlyorsa, kullanldr. qStableSort()u Blm 4te, Spreadsheet uygulamas iinde sralamay gerekletirirken kullanmtk. qDeleteAll() algoritmas, bir konteyner iinde saklanan her iareti iin deletei arr. qDeletAll()u kullanmak, sadece, deer tipi bir iareti tipi olan konteynerler zerinde mantkldr. ardan sonra, eler konteyner iinde asl kalm iaretiler olarak varlklarn srdrrler ve bu nedenle normal olarak, konteyner zerinde clear() da armamz gerekir. rnein:
qDeleteAll(list); list.clear();

qSwap() algoritmas, iki deikenin deerini dei toku eder. rnein:


int x1 = line.x1(); int x2 = line.x2(); if (x1 > x2) qSwap(x1, x2);

Son olarak, tm dier Qt balklar tarafndan dhil edilen <QtGlobal> bal, argmannn mutlak deerini dndren qAbs() fonksiyonu ve iki deerin minimum ya da maksimumunu dndren qMin() ve QMax() fonksiyonlar da dhil olmak zere, birka kullanl tanm salar.

Karakter Katarlar, Byte Dizileri ve Variantlar


QString, QByteArray ve QVariant, konteynerlerle benzer birok eye sahip olan ve baz durumlarda konteynerlere alternatif olarak kullanlabilen snftr. Ayrca, konteynerler gibi bu snflar da, bir bellek ve hz optimizasyonu olarak rtl paylam kullanrlar. QString ile balayacaz. Her GUI program karakter katarlarn(string) kullanr. C++, iki eit karakter katar salar: geleneksel C-tarz karakter katarlar ve std::string snf. Bunlardan farkl olarak, QString, 16bit Unicode deerleri tutar. Unicode, bir altkme olarak, ASCII ve Latin-1i de kapsar. Fakat QString 16-bit olduu iin, dnya dillerinin ounu yazabilmek iin dier binlerce karakteri de ifade edebilir. QString kullandmzda, yeterli bellein ayrlmas ya da verinin \0 ile sonlandrlmasn salamak gibi detaylar hakknda endielenmemiz gerekmez. Kavramsal olarak, QStringler, QCharlarn bir vektr olarak dnlebilir. Bir QString, \0 karakterleri ierebilir. length() fonksiyonu tm karakter katarnn boyutunu -ierdii \0 karakterleri de dhil olmak zere- dndrr. QString, iki karakter katarn birletirmek iin bir ikili + operatr, bir karakter katarn bir dierine ilitirmek iin bir += operatr salar. nk QString otomatik olarak, karakter katar verisinin sonunda belek ayrr ve karakter katarn, karakterleri ok hzlca ve aralksz olarak ona ilitirerek oluturur. te, + ve += operatrlerini birletiren bir rnek:
QString str = "User: ";

183

Qt 4 ile C++ GUI Programlama


str += userName + "\n";

Ayrca, += operatr ile ayn ii yapan bir QString::append() fonksiyonu da vardr:


str = "User: "; str.append(userName); str.append("\n");

Karakter katarlarn birletirmenin tamamen farkl bir yolu QStringin sprintf() fonksiyonunu kullanmaktr:
str.sprintf("%s %.1f%%", "perfect competition", 100.0);

Bu fonksiyon, C++ ktphanesindeki sprintf() fonksiyonu ile ayn format belirtelerini destekler. nceki rnekte, str, perfect competition 100.0% deerini alr. Dier karakter katarlarndan ya da saylardan bir karakter katar oluturmann bir baka yolu ise, arg() fonksiyonunu kullanmaktr:
str = QString("%1 %2 (%3s-%4s)") .arg("permissive").arg("society").arg(1950).arg(1970);

Bu rnekte, permissive, %1in yerini, society, %2nin yerini, 1950 %3n yerini, 1970 de %4n yerini alr. Sonu, permissive society (1950s-1970s)tir. eitli veri tiplerini ilemek iin, arg() fonksiyonunun ar yklenmi versiyonlar da vardr. arg()n ar yklenmi baz versiyonlar alan geniliini, saysal taban ya da kayan nokta hassasiyetini kontrol etmek iin ekstra parametrelere sahiptirler. Genelde arg(), sprintf()ten daha iyi bir zmdr, nk tip gvenlii salar, Unicodeu tam destekler ve evirmenlere, %n parametrelerini yeniden dzenleme imkn verir. QString, QString::number() dntrebilir:
str = QString::number(59.6);

statik

fonksiyonunu kullanarak, saylar

karakter

katarlarna

Ya da setNum() fonksiyonunu kullanarak:


str.setNum(59.6);

Bir karakter katarndan bir sayya dnm ise, toInt(), toLongLong(), toDouble(), vs kullanlarak gerekletirilir. rnein:
bool ok; double d = str.toDouble(&ok);

Bu fonksiyonlar, istee bal olarak, bir bool deikene iaret eden bir iareti alr ve dnmn baarsna bal olarak deikeni true ya da false olarak ayarlarlar. Eer dnm baarsz olursa, bu fonksiyonlar sfr dndrr. Bir karakter katarna sahip olduumuzda, sk sk onun paralarn seerek almak isteriz. mid() fonksiyonu, verilen konumdan (ilk argman) balayarak, verilen uzunlua (ikinci argman) kadar olan altkarakter katarn(substring) dndrr. rnein, aadaki kod, konsola pays yazdrr:
QString str = "polluter pays principle";

184

Qt 4 ile C++ GUI Programlama


qDebug() << str.mid(9, 4);

Eer ikinci argman ihmal edersek, mid() verilen konumdan balayarak, karakter katarnn sonuna kadar olan altkarakter katarn dndrr. rnein, aadaki kod, konsola pays principle yazdrr:
QString str = "polluter pays principle"; qDebug() << str.mid(9);

Benzer ii yapan left() ve right() fonksiyonlar da vardr. Her ikisi de, karakterlerin saysn(n) alr ve karakter katarnn ilk ya da son n sayda karakterini dndrr. rnein, aadaki kod, konsola pollutter principle yazdrr:
QString str = "polluter pays principle"; qDebug() << str.left(8) << " " << str.right(9);

Eer bir karakter katarnn, belirli bir karakteri, altkarakter katarn ya da dzenli ifadeyi(regular expression) ierip iermediini renmek istersek, QStringin indexOf() fonksiyonlarndan birini kullanabiliriz:
QString str = "the middle bit"; int i = str.indexOf("middle");

Burada i, 4 olarak belirlenir. Baarszlk durumunda, indexOf() -1 dndrr ve istee bal olarak, balama konumu ve byk/kk harf duyarllk bayra(case-sensitivity flag) alabilir. Eer sadece, bir karakter katarnn bir eyle balayp balamadn ya da bitip bitmediini kontrol etmek istiyorsak, startsWith() ve endsWith() fonksiyonlarn kullanabiliriz:
if (url.startsWith("http:") && url.endsWith(".png")) ...

nceki kod, aadakinden hem basit hem de daha hzldr:


if (url.left(5) == "http:" && url.right(4) == ".png") ...

== operatr ile yaplan karakter katar karlatrmalar byk/kk harf duyarldr. Eer kullanc tarafndan grlebilir karakter katarlarn karlatryorsak, localeAwareCompare() genellikle doru seimdir ve eer karlatrmalar byk/kk harf duyarl yapmak istiyorsak, toUpper() ya da toLower() kullanabiliriz. rnein:
if (fileName.toLower() == "readme.txt") ...

Eer bir karakter katarnn belirli bir parasn, baka bir karakter katar ile deitirmek istiyorsak, replace() fonksiyonunu kullanabiliriz:
QString str = "a cloudy day"; str.replace(2, 6, "sunny");

Sonu a sunny day olacaktr. Kod, remove() ve insert() kullanlarak yeniden yazlabilir:
str.remove(2, 6); str.insert(2, "sunny");

185

Qt 4 ile C++ GUI Programlama nce, 2 konumundan balayarak 6 karakter sileriz, karakter katar a day halini alr, ve sonrada 2 konumuna sunnyyi ekleriz. replace() fonksiyonunun, birinci argmana eit olan her eyi, ikinci argman ile deitiren ar yklenmi versiyonlar vardr. rnein burada, bir karakter katar iindeki tm &larn, &amp ile deitirilii gsteriliyor:
str.replace("&", "&amp;");

ok sk grlen bir ihtiya da, bir karakter katarndan, alfabe d karakterleri (boluk, tab, yeni satr karakterleri gibi) karmaktr. QString, bir karakter katarnn bandaki ve sonundaki alfabe d karakterleri karan bir fonksiyona sahiptir:
QString str = " BOB \t THE \nDOG \n"; qDebug() << str.trimmed();

str karakter katar u ekilde tasvir edilebilir:


B O B \t T H E \n D O G \n

trimmed() tarafndan dndrlen karakter katar ise yledir:


B O B \t T H E \n D O G

Kullanc girdisi ilenirken, karakter katarnn bandaki ve sonundaki alfabe d karakterleri karmaya ek olarak, sk sk, karakter katar iindeki bir ya da daha fazla alfabe d karakterler dizisini, birer boluk ile deitirmek de isteriz. Bu isteimizi simplified() fonksiyonu yapar:
QString str = " BOB \t THE \nDOG \n"; qDebug() << str.simplified();

simplified() tarafndan dndrlen karakter katar yledir:


B O B T H E D O G

Bir karakter katar, QString:.split() kullanlarak, altkarakter katarlarnn bir QStringListine ayrlabilir:
QString str = "polluter pays principle"; QStringList words = str.split(" ");

nceki rnekte, polluter pays principle karakter katarn, altkarakter katarna ayrdk: polluter, pays ve principle. Bir QStingList iindeki eler, join() kullanlarak, tek bir karakter katarnda birletirilebilirler. join()e aktarlan argman, karakter katar iftleri arasna eklenir.
words.sort(); str = words.join("\n");

Karakter katarlaryla ilgilenirken, sk sk, bir karakter katarnn bo olup olmadn bilmeye ihtiya duyarz. Bu, isEmpty() kullanlarak ya da length() ile karakter katarnn uzunluunun 0 olup olmad kontrol edilerek yaplr. 186

Qt 4 ile C++ GUI Programlama const char * karakter katarlarndan QStringe dnm, ou durumda otomatiktir, rnein:
str += " (1870)";

Burada, bir const char * bir QStringe, formalite olmakszn ekledik. Bir const char * bir QStringe ak bir ekilde dntrmek iin, basite bir QString evirim(cast) kullanabilir veya fromAscii() ya da fromLatin1() arlabilir. Bir QStringi bir const char *a dntrmek iinse toAscii() ya da toLatin1() kullanlr. Bu fonksiyonlar, QByteArray::data() ya da QByteArray::constData() kullanlarak bir const char *a dntrlebilen, bir QByteArray dndrrler.
printf("User: %s\n", str.toAscii().data());

Kolaylk olsun diye, Qt, toAscii().constData() ikilisiyle ayn ii yapan qPrintable() makrosunu salar:
printf("User: %s\n", qPrintable(str));

Bir QByteArray zerinde data() ya da constData()y ardmzda, QByteArray nesnesi tarafndan sahip olunan bir karakter katar dndrr. Bunun anlam, bellek sznts hakknda endielenmememiz gerektiidir; Qt bellein iadesini bizim iin isteyecektir. Bir taraftan da, iaretiyi uzun sre kullanmama konusunda dikkatli olmalyz. Eer QByteArray bir deikende saklanmyorsa, ifade sonunda otomatik olarak silinecektir. QByteArray snf, QString ile ok benzer bir APIa sahiptir. left(), right(), mid(), toLower(), toUpper(), trimmed() ve simplified() gibi fonksiyonlar, QString snfndaki emsalleri ile ayn anlamda, QByteArray snfnda da vardr. QByteArray, ham ikili verileri ve 8-bit ile kodlanm metinleri/karakter katarlarn saklamak iin olduka kullanldr. Genelde, metinleri saklamak iin, QByteArray yerine QString kullanmay neririz, nk QString Unicode destekler. Kolaylk olmas iin, QByteArray otomatik olarak, sondan bir sonraki karakterin \0 olmasn salar, bylece bir QByteArrayin bir fonksiyona bir const char * alarak aktarlmas kolaylar. QByteArray de gml \0 karakterlerini destekler ve bize, istee bal ikili veriler saklamada kullanma imkn verir. Baz durumlarda, verilerin farkl tiplerini ayn deiken ierisinde saklamamz gerekebilir. Bir yaklam, veriyi bir QByteArray ya da bir QString olarak kodlamaktr. rnein bir karakter katar, bir metinsel deeri ya da bir nmerik deeri, karakter katar formunda tutabilir. Bu yaklamlar tam bir esneklik verir, fakat baz C++ yararlarn ortadan kaldrr. Qt, farkl tipleri tutabilen deikenleri ilemenin daha iyi bir yolunu salar: QVariant. QVariant snf, QBrush, QColor, QCursor, QDateTime, QFont, QKeySequence, QPalette, QPen, QPixmap, QPoint, QRect, QRegion, QSize ve QString dhil olmak zere birok Qt tipinin deerlerinin tutabildii gibi, double ve int gibi temel C++ nmerik tiplerini de tutabilir. QVariant snf, konteynerleri de tutabilir: QMap<QString, QVariant>, QStringList ve QList<QVariant>.

187

Qt 4 ile C++ GUI Programlama e grntleme snflar, veritaban modlleri ve QSettings, yaygn olarak variantlar kullanrlar ve bu bize, e verisini, veritaban verisini ve kullanc seeneklerini her QVariant uyumlu tip iin okuma ve yazma imkn verir. Bunun bir rneini Blm 3te grmtk. Konteyner tiplerinin i ie deerleri ile QVariant kullanarak, istee bal karmak veri yaplar oluturmak mmkndr:
QMap<QString, QVariant> pearMap; pearMap["Standard"] = 1.95; pearMap["Organic"] = 2.25; QMap<QString, QVariant> fruitMap; fruitMap["Orange"] = 2.10; fruitMap["Pineapple"] = 3.85; fruitMap["Pear"] = pearMap;

Burada, karakter katar anahtarlar (rn ad) ve kayan noktal saylar (fiyatlar) ya da haritalar olabilen deerler ile bir harita oluturduk. st seviye harita anahtar ierir: Orange, Pear ve Pineapple. Pear anahtar ile birletirilen deer bir haritadr ve iki anahtar ierir (Standart ve Organic). Variant deerler tayan bir harita zerinde yineleme yaparken, tipi kontrol etmek iin type() fonksiyonunu kullanmamz gerekir, bylece ona uygun ekilde davranabiliriz. Bunun gibi veri yaplar oluturmak, veriyi istediimiz her yolla organize edebildiimiz iin, ok ekici olabilir. Fakat QVariantn bu kolayl, okunurluk ve verimlilik asndan zorluklar da beraberinde getirir. Bir kural olarak, genellikle verilerimizi saklamay, eer mmknse uygun bir C++ snf tanmlayarak gerekletirmek daha uygundur. QVariant, Qtun meta-object sistemi tarafndan kullanlr ve bu nedenle de QtCore modlnn parasdr. Bununla beraber, QtGui modln baladmzda, QVariant, GUIye ilikin tipleri de saklayabilir (QColor, QFont, QIcon, QImage ve QPixmap gibi):
QIcon icon("open.png"); QVariant variant = icon;

Bir QVarianttan, GUIye ilikin bir tipin deerine erimek iin, QVariant::value<T>() ablon ye fonksiyonu, aadaki gibi, kullanlabilir:
QIcon icon = variant.value<QIcon>();

value<T>() fonksiyonu ayn zamanda, GUIye ilikin olmayan veri tipleri ile QVariant arasnda dnm gerekletirmek iin de kullanlr, fakat pratikte, biz zaten GUIye ilikin olmayan tipler iin to() dnm fonksiyonlarn kullanmaktayz. QVariant ayrca, zel veri tiplerini saklamak iin de kullanlr, ancak onlarn bir varsaylan kurucu fonksiyon ve bir kopyalama kurucu fonksiyonu saladklarn varsayarak. Bunun almas iin nce zel tipimizi, Q_DECLARE_METATYPE() makrosunu kullanarak kayda geirmeliyiz (genelde bir balk dosyas iinde snf tanmnn altnda):
Q_DECLARE_METATYPE(BusinessCard)

Bu bize, aadaki gibi kod yazma olana verir: 188

Qt 4 ile C++ GUI Programlama


BusinessCard businessCard; QVariant variant = QVariant::fromValue(businessCard); ... if (variant.canConvert<BusinessCard>()) { BusinessCard card = variant.value<BusinessCard>(); ... }

Derleyici kstlamas nedeniyle, bu ablon ye fonksiyonlar MSVC 6 iin kullanlr deildir. Eer bu derleyiciyi kullanmanz gerekiyorsa, qVariantFromValue(), qVariantValue<T>() ve qVariantCanConvert<T>() global fonksiyonlarn kullanabilirsiniz. Eer zel veri tipi, bir QDataStreamdan yazma ve okuma iin << ve >> operatrlerine sahipse, onlar qRegisterMetaTypeStreamOperators<T>() kullanarak kayda geirebiliriz. Bu, zel veri tiplerinin tercihlerini, QSettings kullanarak dier eylerin arasnda saklamay mmkn klar. rnein:
qRegisterMetaTypeStreamOperators<BusinessCard>("BusinessCard");

189

Qt 4 ile C++ GUI Programlama

BLM 11: GRD/IKTI

Bir dosyadan okuma ya da bir dosyaya yazma ihtiyac, hemem hemen her uygulama iin geerlidir. Qt, QIODevice sayesinde, I/O(Input/Output yani Girdi/kt) iin mkemmel bir destek salar. Qt, aadaki QIODevice altsnflarn ierir: QFile QTemporaryFile QBuffer QProcess QTcpSocket QUdpSocket QSslSocket Yerel dosya sisteminde ve gml kaynaklar iindeki dosyalara eriir Yerel dosya sisteminde geici dosyalar oluturur ve geici dosyalara eriir Bir QByteArraye veri yazar ya da bir QByteArrayden veri okur Harici programlar altrr ve sreler aras iletiimi iler Bir veri akn, TCP kullanarak a zerinden transfer eder A zerinden UDP veri iletileri(datagrams) gnderir ya da alr ifrelenmi bir veri akn, a zerinden SSL/TSL kullanarak transfer eder

QProcess, QTcpSocket, QUdpSocket ve QSslSocket, ardk(sequential) aygtlardr(device), bu, veriye sadece bir kere eriilebilecei anlamna gelir; ilk bytetan balanr ve seri bir ekilde son bytea ilerlenir. QFile, QTemporaryFile ve QBuffer, rastgele eriimli(random access) aygtlardr, bylece bytelar, herhangi bir konumdan, istenilen defa eriilebilirdirler; dosya iaretisini yeniden konumlandrmak iin QIODevice::seek() fonksiyonunu salarlar. Aygt snflarna ek olarak, Qt, herhangi bir I/O aygtndan okumada ve herhangi bir I/O aygtna yazmada kullanabildiimiz, iki tane yksek seviyeli ak snf daha salar: ikili veriler iin QDataStream, metinler iin QTextStream. Bu snflar, byte dzenleme ve metin kodlama gibi meselelerle de ilgilenirler ve bu, farkl platformlarda ya da farkl lkelerde alan Qt uygulamalarnn, dierlerinin dosyalarn okuyabilmelerini ve onlara yazabilmelerini salar. Bu, Qtun I/O snflarn, emsal Standart C++ snflarndan (biraz nce bahsedilen meselelerin zmn uygulama programcsna ykleyen) daha kullanl yapar. QFile, ayr ayr dosyalara eriimi kolaylatrr, tabi eer dosya sistemi iinde ya da uygulamann yrtlebilir dosyas iinde gmllerse. zerinde allan dosya setlerinin kimliini saptamas gereken uygulamalar iin, Qt, dizinleri ileyebilen ve ierisindeki dosyalar hakknda bilgi temin edebilen QDir ve QFileInfo snflarn salar. QProcess snf bize, harici programlar balatma ve onlarla, onlarn standart girdi, kt ve hata kanallar (cin, cout ve cerr) sayesinde haberleme imkn verir. Harici programn kullanaca ortam deikenlerini ve alma dizinini ayarlayabiliriz.

kili Veri Okuma ve Yazma


Qt ile ikili veri yklemenin ve kaydetmenin en basit yollu; bir QFile somutlatrmak, dosyay amak ve ona bir QDataStream nesnesi sayesinde erimektir. QDataStream, int ve double gibi temel C++ tiplerini ve birok Qt veri tipini destekleyen, platformdan bamsz bir depolama format salar. te, bir QImage, bir QMap<QString, QColor> ve bir tamsaynn(int), facts.dat adl bir dosyada saklanmas: 190

Qt 4 ile C++ GUI Programlama


QImage image("philip.png"); QMap<QString, QColor> map; map.insert("red", Qt::red); map.insert("green", Qt::green); map.insert("blue", Qt::blue); QFile file("facts.dat"); if (!file.open(QIODevice::WriteOnly)) { std::cerr << "Cannot open file for writing: " << qPrintable(file.errorString()) << std::endl; return; } QDataStream out(&file); out.setVersion(QDataStream::Qt_4_3); out << quint32(0x12345678) << image << map;

Eer dosyay aamazsak, kullancy bilgilendirir ve dneriz(return). qPrintable() makrosu, bir QString iin bir const char * dndrr. (Bir baka yaklam da, ar yklenmi << operatrne sahip <iostream> iin bir std::string dndren QString::toStdString() kullanmaktr.) Eer dosya baaryla almsa, bir QDataStream olutururuz ve versiyon numarasn ayarlarz. Qt 4.3te, en kapsaml format, versiyon 9dur. Ya 9 sabitini elle kodlarz ya da QDataStream::Qt_4_3 sembolik ad kullanrz. 0x12345678 saysnn tm platformlar zerinde, bir iaretsiz(unsigned) 32-bit tamsay olarak yazldn garantiye almak iin, onu, kesin olarak 32-bit olduunu garanti eden bir veri tipine (quint32) dntrrz. Birlikte alabilirlii salamak iin, QDataStream varsaylan olarak, dk son haneliye(big-endian) ayarlanr; bu, setByteOrder() arlarak deitirilebilir. QFile deikeni, kapsam(scope) dna ktnda otomatik olarak silindii iin, dosyay aka kapatmamz gerekmez. Eer verinin gerekten yazldn dorulamak istiyorsak, flush() fonksiyonunu arabiliriz ve dndrd deeri kontrol edebiliriz (baarl ise true). Veri yansmalarn(data mirrors) okumak iin yazdmz kod:
quint32 n; QImage image; QMap<QString, QColor> map; QFile file("facts.dat"); if (!file.open(QIODevice::ReadOnly)) { std::cerr << "Cannot open file for reading: " << qPrintable(file.errorString()) << std::endl; return; } QDataStream in(&file); in.setVersion(QDataStream::Qt_4_3); in >> n >> image >> map;

QDataStream versiyonu, okumak iin kullandmzn aynsdr. Her zaman bu ekilde olmaldr. Versiyonu belirterek, uygulamann veriyi her zaman okuyabilmesini ve yazabilmesini garantiye alm oluyoruz. 191

Qt 4 ile C++ GUI Programlama QDataStream, veriyi, onu kesintisiz bir ekilde geri okuyabilmemizi salayan bir yolla saklar. QDataStream ayn zamanda, ham bytelar okumada ve yazmada da kullanlabilir (readRawBytes() ve writeRawBytes() kullanarak). Bir QDataStreamden okurken, hatalar ilemek olduka kolaydr. Ak(stream), QDataStream::Ok, QDataStream::ReadPastEnd ya da QDataStream::ReadCorruptData olabilen bir status() deerine sahiptir. Bir hata ortaya ktnda, >> operatr daima sfr ya da bo deerler okur. Bunun anlam, potansiyel hatalar hakknda endielenmeksizin ve sonda, okumann geerliliini hakknda bilgi edinmek iin status() deerini kontrol etmeksizin, bir dosyay okumaya kalkmamamz gerektiidir. QDataStream eitli C++ ve Qt veri tiplerini iler. Ayrca << ve >> operatrlerini ar ykleyerek, kendi zel tiplerimiz iin destek de ekleyebiliriz. te, QDataStream ile kullanlabilen zel bir veri tipinin tanm:
class Painting { public: Painting() { myYear = 0; } Painting(const QString &title, const QString &artist, int year) { myTitle = title; myArtist = artist; myYear = year; } void setTitle(const QString &title) { myTitle = title; } QString title() const { return myTitle; } ... private: QString myTitle; QString myArtist; int myYear; }; QDataStream &operator<<(QDataStream &out, const Painting &painting); QDataStream &operator>>(QDataStream &in, Painting &painting);

te, << operatrnn gerekletirilmesi:


QDataStream &operator<<(QDataStream &out, const Painting &painting) { out << painting.title() << painting.artist() << quint32(painting.year()); return out; }

Bir Painting kts iin, sadece iki QString ve bir quint32 kts alrz. Fonksiyonun sonunda ak dndrrz. Bu, bir kt ak ile bir << operatrler zincirini kullanma imkn veren, yaygn bir C++ deyimidir. rnein:
out << painting1 << painting2 << painting3;

operator>>()n gerekletirimi, operator<<()nkine benzerdir:


QDataStream &operator>>(QDataStream &in, Painting &painting) { QString title;

192

Qt 4 ile C++ GUI Programlama


QString artist; quint32 year; in >> title >> artist >> year; painting = Painting(title, artist, year); return in; }

Ak operatrlerini(streaming operators) zel veriler iin de salamann birka faydas vardr. Onlardan biri, zel tipleri kullanan konteynerleri aka katma imkn vermesidir. rnein:
QList<Painting> paintings = ...; out << paintings;

Konteynerlerin iine okumay da kolaylkla yapabiliriz:


QList<Painting> paintings; in >> paintings;

Eer Painting tipi, << ya da >> operatrlerinden birini desteklemiyorsa, bu ilemlerin sonucunda derleyici hata verecektir. zel tipler iin ak operatrleri salamann bir baka faydas, bu tiplerin deerlerini QVariantlar olarak saklayabilmemizdir. Bu, Blm 10de akland gibi, nceden qRegisterMetaTypeStreamOperators<T>() kullanlarak kayda geirilmi tipleri iin geerlidir. QDataStream kullandmzda, Qt, istenilen sayda e ieren konteynerler de dhil olmak zere, her bir tipin okunmas ve yazlmas ile ilgilenir. Bu bizi, neyi yazacamz planlamaktan ve okuduumuz zerinde ayrtrmann herhangi bir trn icra etmekten kurtarr. Tek ykmllmz, tm tipleri, yazdmz srada okumaktr, detaylarla baa kmay Qta brakrz. QDataStream, hem zel uygulamalarmzn dosya formatlar iin hem de standart ikili formatlar iin olduka kullanldr. Standart ikili formatlar, temel tipler zerinde (quint16 ya da float gibi) ak operatrleri ya da readRawBytes() ve writeRawBytes() kullanarak okuyabilir ve yazabiliriz. Eer QDataStream sadece temel C++ veri tiplerini okumakta ve yazmakta kullanlyorsa, setVersion() arsna bile ihtiya duymayz. imdiye kadar, verileri akn versiyonunu elle kodlayarak (QDataStream::Qt_4_3 gibi) ykledik ve kaydettik. Bu yaklam basit ve gvenilirdir, fakat bir sakncas vardr: yeni ya da gncellenmi formatlarn avantajndan yararlanamayz. rnein, eer Qtun daha sonraki bir versiyonu QFonta yeni bir nitelik eklemise ve biz versiyon numarasn elle kodlamsak, bu nitelik kaydedilemeyecek ya da yklenemeyecektir. ki zm vardr. lk yaklam, QDataStream versiyon numarasn dosya iine gmmektir:
QDataStream out(&file); out << quint32(MagicNumber) << quint16(out.version());

(MagicNumber, dosya tipini tanmlayan zgn bir sabittir.) Bu yaklam, veriyi daima QDataStreamin en son versiyonunu kullanarak yazmay garanti eder. Dosyay okuyacamz zaman, ak versiyonunu da okuruz:
quint32 magic; quint16 streamVersion; QDataStream in(&file); in >> magic >> streamVersion;

193

Qt 4 ile C++ GUI Programlama

if (magic != MagicNumber) { std::cerr << "File is not recognized by this application" << std::endl; } else if (streamVersion > in.version()) { std::cerr << "File is from a more recent version of the " << "application" << std::endl; return false; } in.setVersion(streamVersion);

Ak versiyonu, uygulama tarafndan kullanlann versiyonuna eit ya da ondan kk olduu srece, veriyi okuyabiliriz; aksi halde, bir hata ile karlarz. Eer bir dosya format kendine ait bir versiyon numaras ieriyorsa, ak versiyonunu aka saklamak yerine onu kullanabiliriz. rnein, dosya formatnn, uygulamamzn 1.3 versiyonu iin olduunu varsayalm. Veriyi aadaki gibi yazabiliriz:
QDataStream out(&file); out.setVersion(QDataStream::Qt_4_3); out << quint32(MagicNumber) << quint16(0x0103);

Geri okurken de, kullanlacak QDataStream versiyonunu uygulamann versiyon numarasna bal olarak belirleyebiliriz:
QDataStream in(&file); in >> magic >> appVersion; if (magic != MagicNumber) { std::cerr << "File is not recognized by this application" << std::endl; return false; } else if (appVersion > 0x0103) { std::cerr << "File is from a more recent version of the " << "application" << std::endl; return false; } if (appVersion < 0x0103) { in.setVersion(QDataStream::Qt_3_0); } else { in.setVersion(QDataStream::Qt_4_3); }

zet olarak, QDataStream versiyonlarn ele almak iin yaklam vardr: versiyon numarasn elle kodlama, versiyon numarasn aka yazma ve okuma, ve elle kodlamann farkl bir versiyonu olarak versiyon numarasn uygulamann versiyonuna bal olarak belirleme. QDataStream versiyonlarn ele almak iin bu yaklamlardan herhangi birini setikten sonra, Qt kullanarak ikili verileri yazma ve okuma hem basit hem de gvenilirdir. Eer bir dosyay bir seferde okumak ya da yazmak istersek, QDataStreami kullanmaktan kanabilir ve yerine QIODevicen write() ve readAll() fonksiyonlarn kullanabiliriz. rnein:
bool copyFile(const QString &source, const QString &dest) {

194

Qt 4 ile C++ GUI Programlama


QFile sourceFile(source); if (!sourceFile.open(QIODevice::ReadOnly)) return false; QFile destFile(dest); if (!destFile.open(QIODevice::WriteOnly)) return false; destFile.write(sourceFile.readAll()); return sourceFile.error() == QFile::NoError && destFile.error() == QFile::NoError; }

readAll()u ardmz satrda, girdi dosyasnn tm -sonradan kt dosyasna yazlmas iin write() fonksiyonuna aktarlacak olan- bir QByteArray iine okunur. Tm veriyi bir QByteArray iinde tutmak, e e okumaktan daha fazla bellek gerektirir, fakat baz avantajlar vardr. rnein, daha sonra veriyi sktrmak ya da sktrlm veriyi amak iin qCompress() ve qUncompress() fonksiyonlarn arabiliriz.

Metin Okuma ve Yazma


kili dosya formatlar genellikle, metin tabanl formatlardan daha derli toplu olduklar halde, insan tarafndan okunabilir ve dzenlenebilir halde deildirler. Bunun bir sorun olduu durumlarda, ikili formatlar yerine metin formatlarn kullanabiliriz. Qt, dz metin dosyalarn okumak ya da dz metin dosyalarna yazmak ve HTML, XML ve kaynak kod gibi dier metin formatlarn kullanan dosyalar iin QTextStream snfn salar. QTextStream, sistemin yerel kodlamas ya da dier herhangi bir kodlama ile Unicode arasndaki dnmle ilgilenir ve farkl iletim sistemleri tarafndan kullanlan farkl satr sonlandrma kurallarn elden geirir (Windowsta \r\n, Unix ve Mac OS Xte \n). QTextStream, verinin temel birimi olarak 16-bitlik QChar tipini kullanr. Karakterlere ve karakter katarlarna ek olarak, QTextStream, karakter katarlarna dntrlen ve karakter katarlarndan dntrlen, C++n temel nmerik tiplerini de destekler. rnein aadaki kod, sf-book.txt dosyasna Thomas M. Disch: 334\n yazar:
QFile file("sf-book.txt"); if (!file.open(QIODevice::WriteOnly)) { std::cerr << "Cannot open file for writing: " << qPrintable(file.errorString()) << std::endl; return; } QTextStream out(&file); out << "Thomas M. Disch: " << 334 << endl;

Metin yazma ok kolaydr, fakat metin okuma zorlayc olabilir, nk metinsel veri (QDataStream kullanlarak yazlan ikili veriden farkl olarak) temelde belirsizdir. imdi aadaki rnek zerinde dnelim:
out << "Denmark" << "Norway";

Eer out bir QTextStream ise, veri DenmarkNorway eklinde yazlacaktr. Aadaki kodun bu veriyi doru okumasn kesinlikle bekleyemeyiz:
in >> str1 >> str2;

195

Qt 4 ile C++ GUI Programlama Gerek u ki, olan ey udur: str1 tm kelimeyi (DenmarkNorway) alr ve str2 hibir deer alamaz. Bu problem, QDataStream ile ortaya kmaz nk QDataStream her bir karakter katarnn uzunluunu karakter verisinin nnde saklar. Karmak dosya formatlar iin, bir full-blown ayrtrc(parser) gerekebilir. Bu tr ayrtrclar, veriyi, bir QChar zerinde >> kullanarak, karakter karakter ya da QTextStream::readLine() kullanarak, satr satr okuyabilirler. Bu ksmn sonunda, iki kk rnek greceiz; biri, bir girdi dosyasn satr satr okur, dieri, onu karakter karakter okur. Btn bir metin zerinde alan ayrtrclar iin, eer bellek kullanm hakknda kaygl deilsek, dosyann tamamn tek seferde QTextStream::readAll() kullanarak okuyabiliriz. QTextStream varsaylan olarak, okuma ve yazma iin sistemin yerel kodlamasn kullanr (ISO 8859-1 ya da ISO 8859-15 gibi). Bu, aadaki gibi setCodec() kullanlarak deitirilebilir:
stream.setCodec("UTF-8");

rnekte kullanlan UTF-8 kodlamas, tm Unicode karakter setini sunan, popler bir ASCII uyumlu kodlamadr. QTextStream eitli seeneklere sahiptir. Bunlar, ak maniplatrleri(stream manipulators) denilen zel nesnelere aktarlarak ya da aada listelenen fonksiyonlar arlarak ayarlanabilirler. Aadaki rnek, 12345678 tamsaysnn ktsn almadan nce showbase, uppercasedigits ve hex seeneklerini ayarlamak suretiyle, 0xBC614E metinini retir:
out << showbase << uppercasedigits << hex << 12345678;

setIntegerBase(int) 0 neke bal olarak otomatik belirlenir (okuma srasnda) 2 kili 8 Sekizli 10 Onlu 16 Onaltl setNumberFlags(NumberFlags) ShowBase Bir nek gsterir (eer taban 2 ise ("0b"), 8 ise ("0"), 16 ise ("0x")) ForceSign Gerek saylarda daima iareti gsterir ForcePoint Saylarda daima ondalk ayrc gsterir UppercaseBase Daima taban neklerinin byk harfli formatlarn kullanr ("0B", "0X") UppercaseDigits Onaltl saylarda byk harf kullanr setRealNumberNotation(RealNumberNotation) FixedNotation Sabit noktal gsterim (rnein, "0.000123") ScientificNotation Bilimsel gsterim (rnein, "1.234568e-04") SmartNotation Sabit noktal ya da bilimsel gsterimden hangisi daha uygunsa setRealNumberPrecision(int) Gerek saylarn duyarlln ayarlar (varsaylan olarak 6dr) setFieldWidth(int) Bir alann minimum boyutunu ayarlar (varsaylan olarak 0dr)

196

Qt 4 ile C++ GUI Programlama setFieldAlignment(FieldAlignment) AlignLeft Alann sa tarafn doldurur AlignRight Alann sol tarafn doldurur AlignCenter Alann her iki tarafn da doldurur AlignAccountingStyle aret ve say arasn doldurur setPadChar(QChar) Doldurma iin kullanlacak karakteri ayarlar (varsaylan olarak boluk karakteridir) Seenekler, ye fonksiyonlar kullanlarak da ayarlanabilirler:
out.setNumberFlags(QTextStream::ShowBase | QTextStream::UppercaseDigits); out.setIntegerBase(16); out << 12345678;

QDataStream gibi, QTextStream de bir QIODevice altsnf (QFile, QTemporaryFile, QBuffer, QProcess, QTcpSocket, QUdpSocket ya da QSslSocket) ile ilem yapar. Ek olarak, direkt olarak bir QString ile de kullanlabilir. rnein:
QString str; QTextStream(&str) << oct << 31 << " " << dec << 25 << endl;

Bu, onlu 31 says, sekizli 37 saysna denk geldii iin, strnin ieriini 37 25\n yapar. Bu durumda, QString daima Unicode olduu iin, ak zerinde bir kodlama ayarlamamz gerekmez. imdi, bir metin tabanl dosya formatnn basit bir rneine bakalm. Spreadsheet uygulamasnda, Spreadsheet verisini saklamak iin bir ikili format kullanmtk. Veri satr, stun ve forml lsnn bir dizisinden oluuyordu. Veriyi metin olarak yazmak basittir; burada Spreadsheet::writeFile()n revize edilmi bir versiyonunun bir ksm var:
QTextStream out(&file); for (int row = 0; row < RowCount; ++row) { for (int column = 0; column < ColumnCount; ++column) { QString str = formula(row, column); if (!str.isEmpty()) out << row << " " << column << " " << str << endl; } }

Her bir satrn bir hcreyi temsil ettii ve satr ile stn, stun ile forml arasnda boluklarn olduu basit bir format kullandk. Forml, boluklar ierebilir, fakat hi \n karakteri (satrlar sonlandrmada kullandmz karakter) iermediini varsayabiliriz. imdi de, okuma kodunun revize edilmi versiyonuna bakalm:
QTextStream in(&file); while (!in.atEnd()) { QString line = in.readLine(); QStringList fields = line.split(' '); if (fields.size() >= 3) { int row = fields.takeFirst().toInt(); int column = fields.takeFirst().toInt(); setFormula(row, column, fields.join(' ')); } }

197

Qt 4 ile C++ GUI Programlama Spreadsheet verisi iinden bir seferde bir satr okuruz. readLine() fonksiyonu, ard ardna gelen \n karakterlerinden birini siler. QString::split() bir karakter katar listesi dndrr, karakter katarn ayrrken verilen ayrcy kullanr. rnein, 5 19 Total value satr iin sonu drt eli bir listedir: *5, 19, Total, value+. Eer en az alana(field) sahipsek, veri ekmeye hazrzdr. QStringList::takeFirst() fonksiyonu, bir listedeki ilk eyi siler ve sildii eyi dndrr. Onu, satr ve stun numaralarn elde etmede kullanrz. Hata kontrol yapmayz; eer tamsay olmayan bir satr ya da stun deeri okuduysak, QString::toInt() 0 dndrr. setFormula() fonksiyonunu arrken, kalan alanlar tek bir karakter katar iine eklemeliyiz. kinci QTextStream rneimizde, bir metin dosyasn okuyan ve kt olarak ayn metni fakat satrlardan ard ardna gelen boluklar silinmi ve tm tablar yerine boluklar yerletirilmi ekilde reten bir program gerekletirmek iin bir karakter karakter okuma yaklam kullanacaz. Programn iini tidyFile() fonksiyonu yapar: Kod Grnm:
void tidyFile(QIODevice *inDevice, QIODevice *outDevice) { QTextStream in(inDevice); QTextStream out(outDevice); const int TabSize = 8; int endlCount = 0; int spaceCount = 0; int column = 0; QChar ch; while (!in.atEnd()) { in >> ch; if (ch == '\n') { ++endlCount; spaceCount = 0; column = 0; } else if (ch == '\t') { int size = TabSize - (column % TabSize); spaceCount += size; column += size; } else if (ch == ' ') { ++spaceCount; ++column; } else { while (endlCount > 0) { out << endl; --endlCount; column = 0; } while (spaceCount > 0) { out << ' '; --spaceCount; ++column; } out << ch; ++column;

198

Qt 4 ile C++ GUI Programlama


} } out << endl; }

Fonksiyona aktarlan QIODevicelara bal olarak bir QTextStream girdisi ve bir QTextStream kts olutururuz. Geerli karaktere ek olarak, adet durum izleme deikenine(state-tracking variable) bakarz: biri yenisatrlar sayar, biri boluklar sayar, ve biri de geerli satrdaki geerli stunu iaretler (tablar doru sayda bolua dntrmek iin). Ayrtrma, girdi dosyasndaki her karakter zerinde (bir seferde biri zerinde) yinelenen bir while dngs iinde yaplr.
int main() { QFile inFile; QFile outFile; inFile.open(stdin, QFile::ReadOnly); outFile.open(stdout, QFile::WriteOnly); tidyFile(&inFile, &outFile); return 0; }

Bu rnek iin, bir QApplication nesnesine ihtiyacmz yoktur, nk sadece Qtun ara snflarn(tool classes) kullanyoruz. Programn bir filtre olarak kullanldn varsaydk, rnein:
tidy < cool.cpp > cooler.cpp

Program, komut satr zerinde verilen dosya isimlerini ileyebilecek ekilde geniletmek kolay olacaktr. Bu bir konsol uygulamas olduu iin, GUI uygulamalar iin olanlardan biraz farkl bir .pro dosyasna sahiptir:
TEMPLATE QT CONFIG CONFIG SOURCES = = += -= = app core console app_bundle tidy.cpp

Hibir GUI ilevi kullanmadmz iin sadece QtCorea balarz. Sonra, Windows zerinde konsol ktsna izin vermek istediimizi belirtiriz. Dz ASCII ya da ISO 8859-1 (Latin-1) dosyalarn okumak ve yazmak iin, QTextStream kullanmak yerine direkt olarak QIODevicen APIn kullanmak da mmkndr. Bu, nadiren kullanlan bir yntemdir. Eer yinede bir QIODevicea direkt olarak metin yazmak istiyorsanz, open() fonksiyonu arsnda QIODevice::Text bayran aka belirtmek zorundasnz, rnein:
file.open(QIODevice::WriteOnly | QIODevice::Text);

Yazarken, bu bayrak QIODevicea, Windows zerinde \n karakterlerini \r\n dizisine dntrmesini syler. Okurken, bu bayrak aygta, tm platformlar zerinde \r karakterlerini yok saymasn syler.

199

Qt 4 ile C++ GUI Programlama

Dizinler
QDir snf, dizinleri platformdan bamsz bir ekilde ele almay salar ve dosyalar hakknda bilgilere eriir. QDirin nasl kullanldn grmek iin, belli bir dizin ve onun tm altdizinlerindeki tm grseller tarafndan kullanlan alan hesaplayan kk bir konsol uygulamas yazacaz. Uygulamann kalbi, verilen bir dizindeki grsellerin toplam boyutunu zyinelemeli olarak hesaplayan imageSpace() fonksiyonudur:
qlonglong imageSpace(const QString &path) { QDir dir(path); qlonglong size = 0; QStringList filters; foreach (QByteArray format, QImageReader::supportedImageFormats()) filters += "*." + format; foreach (QString file, dir.entryList(filters, QDir::Files)) size += QFileInfo(dir, file).size(); foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) size += imageSpace(path + QDir::separator() + subDir); return size; }

Verilen yollu(path) kullanarak bir QDir nesnesi oluturarak balarz. entryList() fonksiyonuna iki argman aktarrz. lki, dosya isim filtrelerinin bir listesidir. Bunlar, * ve ? genel arama karakterlerini ierebilirler. Bu rnekte, sadece QImagen okuyabilecei dosya formatlarn filtreleriz. kincisi,istediimiz girdilerin(entry) eidini (normal dosyalar, dizinler, srcler, vs) belirtir. Dosya listesi zerinde yineleme yapar ve dosyalarn boyutlarn toplarz. QFileInfo snf, bir dosyann boyutu, izinleri, sahibi gibi zniteliklerine erime imkn verir. kinci entryList() ars bu dizindeki tm altdizinlere eriir. Onlarn zerinde yineleme yapar ve toplam boyutlarn renmek iin zyinelemeli olarak imageSpace() fonksiyonunu arrz. Her bir altdizinin yolunu u ekilde olutururken, geerli dizinin yolunu -araya bir slash koyarak- altdizinin ismi ile birletiririz. Yollar kullancya sunarken, slashlar platforma zg ayrcya dntrmek iin QDir::toNativeSeparators() statik fonksiyonunu arabiliriz. imdide kk programmza bir main() fonksiyonu ekleyelim:
int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QStringList args = QCoreApplication::arguments(); QString path = QDir::currentPath(); if (args.count() > 1) path = args[1]; std::cout << "Space used by images in " << qPrintable(path) << " and its subdirectories is "

200

Qt 4 ile C++ GUI Programlama


<< (imageSpace(path) / 1024) << " KB" << std::endl; return 0; }

Yolu, geerli dizine ilklendirmek iin QDir::currentPath() fonksiyonunu kullanrz. Alternatif olarak, yolu, kullancnn ev dizinine(home directory) ilklendirmek iin QDir::homePath() fonksiyonunu kullanabilirdik. Eer kullanc, komut satrnda aka belirtilmi bir yola sahipse, ilki yerine bunu kullanrz. Son olarak, grsellerin ne kadar alan kapladklarn hesaplamak iin imageSpace() fonksiyonunu arrz. QDir snf, entryInfoList() (QFileInfo nesnelerinin bir listesini dndrr), rename(), exists(), mkdir() ve rmdir() gibi baka dosya (ve dizin) ilikili fonksiyonlar da salar. QFile snf, remove() ve exists() gibi statik uygunluk fonksiyonlar salar. Ve QFileSystemWatcher snf, directoryChanged() ve fileChanged() sinyallerini yayarak, bir dizin ya da bir dosyada bir deiiklik meydana geldiinde bizi bilgilendirir.

Gml Kaynaklar
Bu blmde imdiye kadar, harici aygtlar iindeki veriye erimek hakknda konutuk, fakat Qt ile, uygulamann altrlabilir dosyas iine ikili veri ya da metin gmmek de mmkndr. Bu, Qtun kaynak sistemi(resource system) kullanlarak baarlr. Dier blmlerde, kaynak dosyalarn, altrlabilir dosya iine grseller gmmek iin kullandk, fakat her trl dosyay gmmek mmkndr. Gml dosyalar, tpk dosya sistemindeki normal dosyalar gibi QFile kullanlarak okunabilirler. Kaynaklar, Qtun kaynak derleyicisi rcc tarafndan C++ koduna dntrlrler. .pro dosyasna bu satr ekleyerek qmakee rccyi yrtmede zel kurallar dhil edebiliriz:
RESOURCES = myresourcefile.qrc

myresourcefile.qrc dosyas, altrlabilir dosya iine gml dosyalar listeleyen bir XML dosyasdr. letiim bilgilerini tutan bir uygulama yazdmz hayal edelim. Kullanclarmza kolaylk olmas iin, uluslar aras telefon kodlarn altrlabilir dosya iine gmmek istiyoruz. Eer dosya, uygulamann ina edildii dizindeki datafiles dizininin iinde olursa, kaynak dosyas(resource file) una benzeyecekti:
<RCC> <qresource> <file>datafiles/phone-codes.dat</file> </qresource> </RCC>

Kaynaklar, uygulama iinde :/ yol neki ile tannrlar. Bu rnekte, telefon kodlar dosyas :/datafiles/ phone-codes.dat yoluna sahiptir ve dier herhangi bir dosya gibi QFile kullanlarak okunabilirler.

Sreler Aras letiim


QProcess snf bize, harici programlar altrma ve onlar ile etkileme olana verir. Snf, zaman uyumsuz alr fakat ilerini arka planda yapt iin, kullanc arayz uyumu srdrr. QProcess, harici ilem bir veriye sahip olduunda ya da sonlandnda, bizi bilgilendirmek iin sinyaller yayar.

201

Qt 4 ile C++ GUI Programlama Bir harici resim dntrme programna, bir kullanc arayz salayan kk bir uygulamann kodlarn inceleyeceiz. Bu rnek iin, tm platformlar iin mevcut olan ImageMagick dntrme programna bel balyoruz. Kullanc arayzmz ekil 11.1de gsteriliyor.

ekil 11.1

Kullanc arayz Qt Designerda oluturulmutur. Biz burada sadece balk dosyas ile balayarak, uic tarafndan retilen Ui::ConvertDialog snfndan tretilmi altsnfa odaklanacaz:
#ifndef CONVERTDIALOG_H #define CONVERTDIALOG_H #include <QDialog> #include <QProcess> #include "ui_convertdialog.h" class ConvertDialog : public QDialog, private Ui::ConvertDialog { Q_OBJECT public: ConvertDialog(QWidget *parent = 0); private slots: void on_browseButton_clicked(); void convertImage(); void updateOutputTextEdit(); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void processError(QProcess::ProcessError error); private: QProcess process; QString targetFile; }; #endif

Balk dosyas, Qt Designer formlarnn altsnflar iin olan modele yakn bir modeli takip eder. Dier rneklerin bazlarndan kk bir fark olarak, Ui::ConvertDialog snf iin private kaltm kullandk. Bu, form paracklarna formun fonksiyonlar haricindeki eriimlerin nne geer. Qt Designern otomatik 202

Qt 4 ile C++ GUI Programlama balama mekanizmasna krler olsun ki, on_browseButton_clicked() yuvas, Browse butonunun clicked() sinyaline otomatik olarak balanr.
ConvertDialog::ConvertDialog(QWidget *parent) : QDialog(parent) { setupUi(this); QPushButton *convertButton = buttonBox->button(QDialogButtonBox::Ok); convertButton->setText(tr("&Convert")); convertButton->setEnabled(false); connect(convertButton, SIGNAL(clicked()), this, SLOT(convertImage())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(&process, SIGNAL(readyReadStandardError()), this, SLOT(updateOutputTextEdit())); connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus))); connect(&process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); }

setupUi() ars, formun tm paracklarn oluturur, yerletirir ve on_browseButton_clicked() yuvas iin sinyal-yuva balantsn kurar. Buton kutusunun(Button Box) OK butonuna bir iareti ediniriz ve ona daha uygun bir metin veririz. Ayrca balangta dntrmek iin resim mevcut olmadndan, butonu devred brakrz ve convertImage() yuvasna balarz. Sonra, buton kutusunun rejected() sinyalini (Close butonu tarafndan yaylan), diyaloun reject() yuvasna balarz. Bundan sonra, QProcess nesnesinden sinyali, private yuvaya balarz. Harici ilem, cerr zerinde veriye sahip olduunda, onu updateOutputTextEdit() iinde ileyeceiz.
void ConvertDialog::on_browseButton_clicked() { QString initialName = sourceFileEdit->text(); if (initialName.isEmpty()) initialName = QDir::homePath(); QString fileName = QFileDialog::getOpenFileName(this, tr("Choose File"), initialName); fileName = QDir::toNativeSeparators(fileName); if (!fileName.isEmpty()) { sourceFileEdit->setText(fileName); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } }

setupUi() tarafndan, Browse butonunun clicked() sinyali, on_browseButton_clicked() yuvasna otomatik olarak balanmt. Eer kullanc nceden bir dosya semise, dosya diyalounu dosyann ismi ile balatrz; aksi halde, kullancnn ev dizinini kullanrz.
void ConvertDialog::convertImage() { QString sourceFile = sourceFileEdit->text(); targetFile = QFileInfo(sourceFile).path() + QDir::separator() + QFileInfo(sourceFile).baseName() + "." + targetFormatComboBox->currentText().toLower();

203

Qt 4 ile C++ GUI Programlama


buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); outputTextEdit->clear(); QStringList args; if (enhanceCheckBox->isChecked()) args << "-enhance"; if (monochromeCheckBox->isChecked()) args << "-monochrome"; args << sourceFile << targetFile; process.start("convert", args); }

Kullanc, Convert butonuna tkladnda, kaynak dosyann ismini kopyalarz ve uzantsn hedef dosya formatnn ki ile deitiririz. Slashlar elle kodlamak yerine platforma zg dizin ayrcs kullanrz, nk dosya ismi kullancya grnr olacak. Sonra, kullancnn kazayla oklu dntrmeler balatmasn nlemek iin Convert butonunu devred brakrz ve durum bilgisini gstermede kullandmz metin editrn temizleriz. Harici sreci balatmak iin, altrmak istediimiz programn ismi (convert) ve ihtiyac olan argmanlar ile QProcess::start() arrz. Bu durumda, eer kullanc uygun seenekleri iaretlediyse, -enhance ve -monochrome bayraklarn, ardndan da kaynak ve hedef dosya isimlerini aktarrz. convert program, gerekli dnm dosya uzantlarna bakarak anlar.
void ConvertDialog::updateOutputTextEdit() { QByteArray newData = process.readAllStandardError(); QString text = outputTextEdit->toPlainText() + QString::fromLocal8Bit(newData); outputTextEdit->setPlainText(text); }

Harici sre cerre yazdnda, updateOutputTextEdit() yuvas arlr. Hata metnini okur ve onu QTextEditin varolan metnine ekleriz.
void ConvertDialog::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus == QProcess::CrashExit) { outputTextEdit->append(tr("Conversion program crashed")); } else if (exitCode != 0) { outputTextEdit->append(tr("Conversion failed")); } else { outputTextEdit->append(tr("File %1 created").arg(targetFile)); } buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); }

Sre sonlandnda, kullancnn bunu bilmesini salarz ve Convert butonunu etkinletiririz.


void ConvertDialog::processError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { outputTextEdit->append(tr("Conversion program not found")); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); }

204

Qt 4 ile C++ GUI Programlama


}

Eer sre balatlamazsa, QProcess, finished() yerine error() sinyalini yayar. Hatay rapor ederiz ve Click butonunu etkinletiririz. Bu rnekte, zaman uyumsuz dosya dntrmeleri yaptk, QProcess ile convert programn altrmaktan ve ardndan kontrol tekrar uygulamaya iade etmekten bahsettik. Bu, sre arkaplanda ilerken, kullanc arayzn cevap verebilir halde tutar. Fakat baz durumlarda, uygulamamzda baka ilemler yapabilmek iin harici srecin sonlanmas, baz durumlarda ise QProcessi ezamanl iletmemiz gerekir. Ezamanl davranmann ho olduu yaygn bir rnek, dz metinleri dzenlemede kullanlan metin editr uygulamalardr. Bu, apak bir QProcess uygulamasdr. rnein, diyelim ki bir QTextEdit iinde dz metne sahibiz ve kullancnn tklayabildii ve bir edit() yuvasna balanm bir Edit butonu salyoruz:
void ExternalEditor::edit() { QTemporaryFile outFile; if (!outFile.open()) return; QString fileName = outFile.fileName(); QTextStream out(&outFile); out << textEdit->toPlainText(); outFile.close(); QProcess::execute(editor, QStringList() << options << fileName); QFile inFile(fileName); if (!inFile.open(QIODevice::ReadOnly)) return; QTextStream in(&inFile); textEdit->setPlainText(in.readAll()); }

QTemporaryFile, benzersiz(unique) bir isimle bo bir dosya oluturmakta kullanrz. Varsaylan olarak oku-yaz moduna uygun ald iin QTemporaryFile::open()a argman aktarmayz. Metin editrnn ieriini geici dosyaya(temporary file) yazarz ve sonra dosyay kapatrz, nk baz metin editrleri ak olan dosyalar zerinde alamazlar. QProcess::execute() statik fonksiyonu, harici bir sreci yrtr ve sre sonlanana kadar uygulamay bloke eder. editor argman, altrlabilir bir editrn ismini (rnein gvin) tutan bir QStringdir. options argman ise, bir QStringListtir (eer gvim kullanyorsak, bir e ierir, -f). Kullanc, metin editrn kapattktan sonra, sre sonlanr ve execute() sonular arr. Sonra, geici dosyay aarz ve ieriini QTextEdit iine okuruz. QTemporaryFile, nesne kapsamn dna ktnda, geici dosyay otomatik olarak siler. QProcess ezamanl kullanldnda, sinyal-yuva balantlarna gerek yoktur. Eer execute() statik fonksiyonunun saladndan daha iyi bir kontrol gerekliyse, alternatif bir yaklam kullanabiliriz. Bu, bir QProcess nesnesi oluturmay ve stnde start() armay gerektirir, sonra QProcess::waitForStarted() ararak uygulamay bloke etmeye zorlarz, ve eer bu baarl olursa, 205

Qt 4 ile C++ GUI Programlama QProcess::waitForFinished() arlr. Bu yaklam kullanan bir rnek iin, QProcess referans dokmantasyonuna bakabilirsiniz.

206

Qt 4 ile C++ GUI Programlama

BLM 12: VERTABANLARI

QtSql modl, SQL veritabanlarna erimek iin, platformdan (ve veritabanndan) bamsz bir arayz salar. Bu arayz, kullanc arayz ile veritaban btnlemesini salayan Qt Model/Grn mimarisini kullanan bir snf seti tarafndan desteklenir. Bu blm, Blm 9da zerinde durulan Qt Model/Grn snflarna aina olduunuzu varsayar. Bir veritaban balants, bir QSqlDatabase nesnesi ile ifade edilir. Qt, eitli veritaban APIlar ile haberlemek iin srcler(drivers) kullanr. Qt Desktop Edition aadaki srcleri ierir: Driver QDB2 QIBASE QMYSQL QOCI QODBC QPSQL QSQLITE QSQLITE2 QTDS Database IBM DB2 versiyon 7.1 ve sonras Borland InterBase MySQL Oracle (Oracle Call Interface) ODBC (Microsoft SQL Server ierir) PortgreSQL 7.3 ve sonras SQLite versiyon 3 SQLite versiyon 2 Sybase Adaptive Server

Lisans kstlamalar nedeniyle, Qt Open Source Edition ile tm srcler salanmaz. SQL szdizimi ile rahat olan kullanclar iin, QSqlQuery snf, keyfi SQL ifadelerini dorudan altrmay ve sonularn ele almay salar. SQL szdiziminden kanarak daha yksek seviyeli bir veritaban arayzn tercih eden kullanclar iin QSqlTableModel ve QSqlRelationalTableModel uygun soyutlamay salar. Bu snflar, Qtun dier Model snflar ile ayn ekilde, bir SQL tablosunu temsil ederler. Veriyi kod iinde travers etmede ve dzenlemede bamsz olarak kullanlabilirler ya da son kullanclarn veriyi grntleyebildii ve dzenleyebildii Grnlere dorudan bal olabilirler. Qt ayrca, master-detail ve drill-down gibi yaygn veritaban deyimlerini programlamay basitletirir ve veritabann grntlemek iin formlar ya da bu blmde rneklendirilecei gibi GUI tablolar kullanr.

Balanma ve Sorgulama
SQL sorgularn altrmak iin, ilk nce bir veritaban ile balant kurmalyz. Veritaban balantlar genellikle, uygulamann balangcnda ardmz ayr bir fonksiyon iinde ayarlanr. rnein:
bool createConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("mozart.konkordia.edu"); db.setDatabaseName("musicdb");

207

Qt 4 ile C++ GUI Programlama


db.setUserName("gbatstone"); db.setPassword("T17aV44"); if (!db.open()) { QMessageBox::critical(0, QObject::tr("Database Error"), db.lastError().text()); return false; } return true; }

ncelikle, bir QSqlDatabase nesnesi oluturmak iin QSqlDatabase::addDatabase()i arrz. addDatabase()in ilk argman, Qtun veritabanna erimede kullanaca veritaban srcsdr. Bu rnekte, MySQLi kullanyoruz. Sonra, veritaban ana makine adn(host name), veritaban adn(database name), kullanc adn(user name) ve ifreyi(password) ayarlar, balanty aarz. Eer open() baarsz olursa, bir hata mesaj gsteririz. Genellikle, main() iinde createConnection() arrdk:
int main(int argc, char *argv[]) { QApplication app(argc, argv); if (!createConnection()) return 1; ... return app.exec(); }

Balant bir kere kurulduunda, kullandmz veritabannn destekledii herhangi bir SQL ifadesini altrmada QSqlQueryyi kullanabiliriz. rnein burada bir SELECT ifadesinin altrlmas gsteriliyor:
QSqlQuery query; query.exec("SELECT title, year FROM cd WHERE year >= 1998");

exec() arsndan sonra, sorgunun sonu seti boyunca dolaabiliriz:


while (query.next()) { QString title = query.value(0).toString(); int year = query.value(1).toInt(); std::cerr << qPrintable(title) << ": " << year << std::endl; }

QSqlQueryyi sonu setinin ilk kaydna(record) konumlandrmak iin next()i bir kere arrz. Sonraki next() arlar, en sona ulaana kadar, kayt iaretisini her seferinde bir kayt ilerletir, sona geldiinde ise next(), false dndrr. Eer sonu seti bo ise (ya da sorgulama baarsz olduysa), ilk next() arsnda false dnecektir. value() fonksiyonu bir alann deerini bir QVaiant olarak dndrr. Alanlar 0dan balayarak, SELECT ifadesinde verilen srayla numaralandrlrlar. QVariant snf, int ve QString de dhil olmak zere birok C++ ve Qt tipini tutabilir. Bir veritabannda saklanabilecek farkl tiplerde veriler, uygun C++ ve Qt tipleri ile eletirilirler ve QVariantlar iinde saklanrlar. rnein, bir VARCHAR bir QString olarak, bir DATETIME bir QDateTime olarak ifade edilir.

208

Qt 4 ile C++ GUI Programlama QSqlQuery, sonu seti boyunca dolamak iin baka fonksiyonlar da salar: first(), last(), previous() ve seek(). Bu fonksiyonlar kullanldr, fakat baz veritabanlar iin next()ten daha yava ve bellek canavar olabilirler. Byk veri setleri zerinde ilemler yaparken, basit bir optimizasyon olarak, exec()i armadan nce QSqlQuery::setForwardOnly(true)yu arabilir ve sonu seti boyuca dolamak iin sadece next()i kullanabiliriz. Daha evvel, SQL sorgusunu QSqlQuery::exec()e bir argman olarak aktarmtk, fakat onu dorudan kurucusuna da aktarabiliriz:
QSqlQuery query("SELECT title, year FROM cd WHERE year >= 1998");

Sorgu zerinde isActive()i ararak hata kontrol yapabiliriz:


if (!query.isActive()) QMessageBox::warning(this, tr("Database Error"), query.lastError().text());

Eer hi hata olumamsa, sorgu aktifleecek ve next()i kullanarak sonu seti boyunca dolaabileceiz. Bir INSERT ilemi yapmak, hemen hemen bir SELECT sorgusu gerekletirmek kadar kolaydr:
QSqlQuery query("INSERT INTO cd (id, artistid, title, year) " "VALUES (203, 102, 'Living in America', 2002)");

Bundan sonra, numRowsAffected(), SQL ifadesi tarafndan etkilenmi satrlarn saysn dndrr (ya da hata olduunda -1). Eer birok kayt eklememiz(insert) gerekiyorsa ya da deerlerin karakter katarlarna dnmesinin nne gemek istiyorsak, sorgunun, yer tutucular(placeholders) ieren bir sorgu olduunu belirtmek iin prepare() kullanabilir, sonrasnda da eklemek istediimiz deerleri tutturabiliriz(bind). Qt, tm veritabanlarna, yer tutucular iin hem Oracle tarz hem de ODBC tarz szdizimi destei verir. te, Oracle tarz szdizimi kullanan bir rnek:
QSqlQuery query; query.prepare("INSERT INTO cd (id, artistid, title, year) " "VALUES (:id, :artistid, :title, :year)"); query.bindValue(":id", 203); query.bindValue(":artistid", 102); query.bindValue(":title", "Living in America"); query.bindValue(":year", 2002); query.exec();

Bu da ayn rnein ODBC tarz szdizimi kullanan versiyonu:


QSqlQuery query; query.prepare("INSERT INTO cd (id, artistid, title, year) " "VALUES (?, ?, ?, ?)"); query.addBindValue(203); query.addBindValue(102); query.addBindValue("Living in America"); query.addBindValue(2002); query.exec();

exec() arsndan sonra, yeni deerler tutturmak iin bindValue() ya da addBindValue()yu arabiliriz, sonrada sorguyu yeni deerler ile altrmak iin exec()i tekrar arrz. 209

Qt 4 ile C++ GUI Programlama Yer tutucular sklkla, ikili veriler ya da ASCII veya Latin-1 karakterler iermeyen karakter katarlarn belirtmede kullanlrlar. Qt, kullanlabilir olduklar yerlerde, veritabanlar zerinde SQL etkileimlerini(transactions) destekler. Bir etkileim balatmak iin, veritaban balantsn temsil eden QSqlDatabase nesnesi zerinde transaction() arrz. Etkileimi sonlandrmak iinse, commit() ya da rollback()i arrz. rnein burada, bir d anahtar nasl arayacamz ve bir etkileim iinde bir INSERT ifadesini nasl altracamz gsteriliyor:
QSqlDatabase::database().transaction(); QSqlQuery query; query.exec("SELECT id FROM artist WHERE name = 'Gluecifer'"); if (query.next()) { int artistId = query.value(0).toInt(); query.exec("INSERT INTO cd (id, artistid, title, year) " "VALUES (201, " + QString::number(artistId) + ", 'Riding the Tiger', 1997)"); } QSqlDatabase::database().commit();

QSqlDatabese::database() fonksiyonu, createConnection() iinde oluturduumuz bir balanty temsil eden bir QSqlDatabase nesnesi dndrr. Eer etkileim balatlamazsa, QSqlDatabase::transaction(), false dndrr. Baz veritabanlar etkileimleri desteklemezler. Onlar iin, transaction(), commit() ve rollback() fonksiyonlar hibir ey yapmazlar. Bir veritabannn etkileimleri destekleyip desteklemediini anlamak iin, veritabanna ilikin QSqlDriver zerinde hasFeature() arabiliriz:
QSqlDriver *driver = QSqlDatabase::database().driver(); if (driver->hasFeature(QSqlDriver::Transactions)) ...

BLOB(binary large object), Unicode ve hazr sorgular gibi farkl veritaban zellikleri de test edilebilir. imdiye kadarki rneklerde, uygulamann tek bir veritaban balants kullandn varsaydk. Eer oklu balantlar oluturmak istersek, addDatabase()e ikinci argman olarak bir isim aktarabiliriz. rnein:
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL", "OTHER"); db.setHostName("saturn.mcmanamy.edu"); db.setDatabaseName("starsdb"); db.setUserName("hilbert"); db.setPassword("ixtapa7");

Sonra, QSqlDatabase::datebase()e ismi aktararak QSqlDatabase nesnesine bir iareti edinebiliriz:


QSqlDatabase db = QSqlDatabase::database("OTHER");

Dier balanty kullanarak sorgular altrmak iin, QSqlQuery kurucusuna QSqlDatabase nesnesini aktarabiliriz:
QSqlQuery query(db); query.exec("SELECT id FROM artist WHERE name = 'Mando Diao'");

210

Qt 4 ile C++ GUI Programlama oklu balantlar, her bir balant sadece bir etkileimi ileyebildii iin, ayn anda birden fazla etkileim gerekletirmek istediimizde ok kullanldrlar. oklu veritaban balantlarn kullanrken, isimsiz bir balantya sahip olabiliriz, yle ki QSqlQuery, eer hibiri belirtilmemise, bu balanty kullanr. QSqlQueryye ek olarak, Qt, bizi en yaygn SQL ilemlerini (SELECT, INSERT, UPDATE ve DELETE) gerekletirmek iin saf SQL kullanmaktan kanma imkn veren, daha yksek seviyeli bir arayz olarak QSqlTableModel snfn salar. Bu snf, bir veritabann GUIden bamsz olarak deitirmede, ya da bir veritabann QListView veya QTableView iin bir veri kayna olarak ayarlamada kullanlabilir. te, bir SELECT sorgusu gerekletirmede QSqlTableModeln kullanld bir rnek:
QSqlTableModel model; model.setTable("cd"); model.setFilter("year >= 1998"); model.select();

Bu, u sorguya denktir:


SELECT * FROM cd WHERE year >= 1998

Sonu seti boyunca dolama, verilen bir kayda QSqlTableModel::record() kullanarak, alanlara ise value() kullanarak erierek yaplr:
for (int i = 0; i < model.rowCount(); ++i) { QSqlRecord record = model.record(i); QString title = record.value("title").toString(); int year = record.value("year").toInt(); std::cerr << qPrintable(title) << ": " << year << std::endl; }

QSqlRecord::value() fonksiyonu, bir alan ismi ya da bir alan indeksi alr. Byk veri setleri zerinde alrken, bu alanlara, onlar belirten indeksler ile eriilmesi nerilir. rnein:
int titleIndex = model.record().indexOf("title"); int yearIndex = model.record().indexOf("year"); for (int i = 0; i < model.rowCount(); ++i) { QSqlRecord record = model.record(i); QString title = record.value(titleIndex).toString(); int year = record.value(yearIndex).toInt(); std::cerr << qPrintable(title) << ": " << year << std::endl; }

Bir veritaban tablosuna bir kayt eklemek iin, yeni, bo bir satr (kayt) oluturmas iin insertRow()u arrz ve her bir stunun (alan) deerini ayarlamak iin setData()y kullanrz:
QSqlTableModel model; model.setTable("cd"); int row = 0; model.insertRows(row, 1); model.setData(model.index(row, model.setData(model.index(row, model.setData(model.index(row, model.setData(model.index(row, model.submitAll();

0), 1), 2), 3),

113); "Shanghai My Heart"); 224); 2003);

211

Qt 4 ile C++ GUI Programlama submitAll() arsndan sonra kayt, tablonun nasl sralandna bal olarak, farkl bir satr konumuna tanabilir. Eer ekleme baarl olmazsa, submitAll() ars false dndrecektir. Bir SQL Modeli ile standart bir Model arasndaki nemli bir fark, bir SQL Modelinde, deiikliklerin veritabanna yazlmas iin submitAll()u armak zorunda olmamzdr. Bir kayd gncellemek iin ilk nce QSqlTableModel, dzenlemek istediimiz kayda konumlandrmalyz (rnein select()i kullanarak). Sonra, deitirmek istediimiz alanlar gnceller ve deiikliklerimizi veritabanna geri yazarz:
QSqlTableModel model; model.setTable("cd"); model.setFilter("id = 125"); model.select(); if (model.rowCount() == 1) { QSqlRecord record = model.record(0); record.setValue("title", "Melody A.M."); record.setValue("year", record.value("year").toInt() + 1); model.setRecord(0, record); model.submitAll(); }

Eer belirtilen filtreyle eleen bir kayt varsa, QSqlTableModel::record()u kullanarak ona eriiriz. Deiikliklerimizi uygular ve dzenlenmi kaydmz orijinal kayt zerine yazarz. SQL Modeli olmayan Modeller iin yaptmz gibi, setData() kullanarak da bir gncelleme gerekletirme mmkndr:
model.select(); if (model.rowCount() == 1) { model.setData(model.index(0, 1), "Melody A.M."); model.setData(model.index(0, 3), model.data(model.index(0, 3)).toInt() + 1); model.submitAll(); }

Bir kayd silmek, gncellemekle benzerdir:


model.setTable("cd"); model.setFilter("id = 125"); model.select(); if (model.rowCount() == 1) { model.removeRows(0, 1); model.submitAll(); }

removeRows() ars, silinecek ilk kaydn satr numarasn ve silinecek kaytlarn saysn alr. Sradaki rnek, filtre ile eleen tm kaytlar siler:
model.setTable("cd"); model.setFilter("year < 1990"); model.select(); if (model.rowCount() > 0) { model.removeRows(0, model.rowCount()); model.submitAll(); }

212

Qt 4 ile C++ GUI Programlama QSqlQuery ve QSqlTableModel snflar, Qt ve bir SQL veritaban arasnda bir arayz salar. Bu snflar kullanarak, kullanclara veriler sunan ve kullanclara kayt ekleme, gncelleme ve silme izni veren formlar oluturabiliriz. SQL snflar kullanan projeler iin, .pro dosyamza u satr eklemeliyiz:
QT += sql

Tablolar Grntleme
nceki ksmda, bir veritaban ile QSqlQuery ve QSqlTableModel kullanarak nasl etkileeceimizi grdk. Bu ksmda, bir QSqlTableModel bir QTableView parac iinde sunmay greceiz. ekil 12.1de grnen Scooters uygulamas, scooter Modellerinin bir tablosunu sunar. rnek, aadaki gibi tanmlanan tek bir tabloya (scooter) dayanr:
CREATE TABLE scooter ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(40) NOT NULL, maxspeed INTEGER NOT NULL, maxrange INTEGER NOT NULL, weight INTEGER NOT NULL, description VARCHAR(80) NOT NULL);

ekil 12.1

id alannn deeri, veritaban (bu rnekte SQLite) tarafndan otomatik retilir. Dier veritabanlar, bunun iin farkl bir szdizimi kullanabilir. Bakm kolayl iin, stun indekslerine anlaml isimler vermede bir enum kullanrz:
enum { Scooter_Id = 0, Scooter_Name = 1, Scooter_MaxSpeed = 2, Scooter_MaxRange = 3, Scooter_Weight = 4, Scooter_Description = 5 };

te, bir QSqlTableModel, scooter tablosunu grntlemek iin ayarlamada gerekli tm kodlar:

213

Qt 4 ile C++ GUI Programlama


model = new QSqlTableModel(this); model->setTable("scooter"); model->setSort(Scooter_Name, Qt::AscendingOrder); model->setHeaderData(Scooter_Name, Qt::Horizontal, tr("Name")); model->setHeaderData(Scooter_MaxSpeed, Qt::Horizontal, tr("MPH")); model->setHeaderData(Scooter_MaxRange, Qt::Horizontal, tr("Miles")); model->setHeaderData(Scooter_Weight, Qt::Horizontal, tr("Lbs")); model->setHeaderData(Scooter_Description, Qt::Horizontal, tr("Description")); model->select();

Modeli oluturma, bir nceki ksmda grdmzn benzeridir. Tek farkllk, kendi stun balklarmz salamamzdr. Eer byle yapmazsak, ham alan isimleri kullanlacaktr. Ayrca setSort() kullanarak, bir sralama dzeni belirtiriz; perde arkasnda bu, bir ORDER_BY cmlecii tarafndan gerekletirilir. Bylece bir Model oluturduk ve select() kullanarak onu veri ile doldurduk. imdi, onu sunmak iin bir Grn oluturabiliriz:
view = new QTableView; view->setModel(model); view->setSelectionMode(QAbstractItemView::SingleSelection); view->setSelectionBehavior(QAbstractItemView::SelectRows); view->setColumnHidden(Scooter_Id, true); view->resizeColumnsToContents(); view->setEditTriggers(QAbstractItemView::NoEditTriggers); QHeaderView *header = view->horizontalHeader(); header->setStretchLastSection(true);

Blm 9da, QTableView bir QAbstractItemModeldaki veriyi bir tablo iinde sunmada kullanmay grmtk. QSqlTableModel, QAbstractItemModeldan tretildii iin, kolayca bir QTableViewn veri kayna olarak kullanlabilir. setModel() ars, tamamen Grn Modele balamak iin gereklidir. Geri kalan kod, sadece tabloyu daha kullanc dostu yapmak iin tabloyu isteimize gre uyarlar. Seme modu(selection mode) kullancnn neyi seebileceini belirtir; burada, hcreleri (alanlar) seilebilir yaptk. Bu seim genellikle seili hcrenin etrafnda kesikli izgi ile gsterilir. Seme davran(selection behavior) seimlerin grsel olarak nasl ileneceini belirtir; bu rnekte, tm satr grselletirilecektir. Bu seim genellikle farkl bir arkaplan rengi kullanlarak gsterilir. ID stununu gizlemeyi setik, nk IDler kullanc iin pek anlaml deildirler. Ayrca, tablo grntlemesini saltokunur yapmak iin NoEditTriggers ayarlarz. Saltokunur tablolar sunmak iin bir alternatif de QSqlTableModeln ana snf QSqlQueryModel kullanmaktr. Bu snf, setQuery() fonksiyonunu salar, bylece kompleks SQL sorgular yazmak mmkn olur. Scooters veritabanndan farkl olarak, ou veritaban, birok tabloya ve d anahtar ilikilerine(foreign key relationships) sahiptir. Qt, d anahtarlar ile tablolar grntlemede ve dzenlemede kullanlabilen, QSqlTableModeln bir altsnf olan QSqlRelationalTableModel salar. Bir QSqlRelationalTableModel, bu Modele her bir d anahtar iin bir QSqlRelation ekleyebilmemiz dnda, bir QSqlTableModela ok benzer.

214

Qt 4 ile C++ GUI Programlama

Formlar Kullanarak Kaytlar Dzenleme


Bu ksmda, bir seferde bir kayt grntleyen bir diyalog form oluturmay greceiz. Diyalog, kayt eklemede, silmede ve dzenlemede ve bir tablo iindeki tm kaytlar boyunca dolamada kullanlabilir. Bu konseptleri Staff Manager uygulamas kapsamnda rneklendireceiz. Uygulama, hangi departman personelinin(employee) ierde olduunu takip eder, departmanlarn nerede olduklarn ve personelin dhili telefon kodlar gibi personel hakkndaki baz temel bilgileri saklar. Uygulama aadaki tabloyu kullanr:
CREATE TABLE location ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(40) NOT NULL)); CREATE TABLE department ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(40) NOT NULL, locationid INTEGER NOT NULL, FOREIGN KEY (locationid) REFERENCES location)); CREATE TABLE employee ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(40) NOT NULL, departmentid INTEGER NOT NULL, extension INTEGER NOT NULL, email VARCHAR(40) NOT NULL, startdate DATE NOT NULL, FOREIGN KEY (departmentid) REFERENCES department));

Tablolar ve onlarn ilikileri ekil 12.2de ematik olarak gsteriliyor. Her bir mevki(location) herhangi bir sayda departmana sahip olabilir, ve her bir departman herhangi bir sayda personele sahip olabilir. D anahtarlar iin belirtilen szdizimi SQLite 3 iindir ve belki dier veritabanlar iin farkl olabilir.

ekil 12.2

Bu ksmda, personeli ynetmek iin kullanlan EmployeeForm diyalounun zerine odaklanacaz. Bir sonraki ksmda, departmanlarn ve personelin master-detail grnn salayan MainForm ile ilgileneceiz. Form arldnda, eer geerli bir personel IDsi verilmise, onu; aksi halde, ilk personeli gsterir. (Form, ekil 12.3te gsteriliyor.) Kullanclar, personel kaytlar boyunca dolaabilir, personel bilgilerini dzenleyebilir, var olan personelleri silebilir ya da yeni personeller ekleyebilirler.

215

Qt 4 ile C++ GUI Programlama

ekil 12.3

Aadaki enum, employeeform.h iinde, stun indekslerine anlaml isimler vermek iin tanmladk:
enum { Employee_Id = 0, Employee_Name = 1, Employee_DepartmentId = 2, Employee_Extension = 3, Employee_Email = 4, Employee_StartDate = 5 };

Balk dosyasnn kalannda EmployeeForm snf tanmlanr:


class EmployeeForm : public QDialog { Q_OBJECT public: EmployeeForm(int id, QWidget *parent = 0); void done(int result); private slots: void addEmployee(); void deleteEmployee(); private: QSqlRelationalTableModel *tableModel; QDataWidgetMapper *mapper; QLabel *nameLabel; ... QDialogButtonBox *buttonBox; };

Veritabanna erimek iin, yaln bir QSqlTableModel yerine bir QSqlRelationalTableModel kullanrz, nk d anahtarlar zmemiz gerekir. QDataWidgetMapper, bir form iindeki paracklar uygun stunlar ile eletirme imkn veren bir snftr. Formun kurucusu olduka uzundur, bu nedenle onu paralar halinde ve yerleim kodu alakasz olduu iin yerleim kodunu atlayarak inceleyeceiz. 216

Qt 4 ile C++ GUI Programlama Kod Grnm:


EmployeeForm::EmployeeForm(int id, QWidget *parent) : QDialog(parent) { nameEdit = new QLineEdit; nameLabel = new QLabel(tr("Na&me:")); nameLabel->setBuddy(nameEdit); departmentComboBox = new QComboBox; departmentLabel = new QLabel(tr("Depar&tment:")); departmentLabel->setBuddy(departmentComboBox); extensionLineEdit = new QLineEdit; extensionLineEdit->setValidator(new QIntValidator(0, 99999, this)); extensionLabel = new QLabel(tr("E&xtension:")); extensionLabel->setBuddy(extensionLineEdit); emailEdit = new QLineEdit; emailLabel = new QLabel(tr("&Email:")); emailLabel->setBuddy(emailEdit); startDateEdit = new QDateEdit; startDateEdit->setCalendarPopup(true); QDate today = QDate::currentDate(); startDateEdit->setDateRange(today.addDays(-90), today.addDays(90)); startDateLabel = new QLabel(tr("&Start Date:")); startDateLabel->setBuddy(startDateEdit);

Her bir alan iin bir dzenleme parac(editing widget) oluturarak balarz. Ayrca, uygun alanlar belirlemek iin her bir dzenleme paracnn yanna bir etiket(label) yerletiririz. Extension satr editrnn sadece geerli telefon kodlarn kabul etmesini salamak iin bir QIntValidator kullanrz, bu rnekte saylar 0-99999 aralndadr. Ayrca, Start Date editr iin bir zaman aral ayarlarz, ve editr bir takvim(calendar) salamaya ayarlarz. Alr kutuyu(Combo box) dorudan doldurmayz; daha sonra ona, kendi kendini doldurabilmesi iin bir Model vereceiz.
firstButton = new QPushButton(tr("<< &First")); previousButton = new QPushButton(tr("< &Previous")); nextButton = new QPushButton(tr("&Next >")); lastButton = new QPushButton(tr("&Last >>")); addButton = new QPushButton(tr("&Add")); deleteButton = new QPushButton(tr("&Delete")); closeButton = new QPushButton(tr("&Close")); buttonBox = new QDialogButtonBox; buttonBox->addButton(addButton, QDialogButtonBox::ActionRole); buttonBox->addButton(deleteButton, QDialogButtonBox::ActionRole); buttonBox->addButton(closeButton, QDialogButtonBox::AcceptRole);

Diyaloun st ksmnda, birlikte gruplanm navigasyon butonlar (<< First, < Previous, Next > ve Last >>) olutururuz. Sonra, dier butonlar (Add, Delete ve Close) olutururuz ve onlar diyaloun alt ksmna 217

Qt 4 ile C++ GUI Programlama yerlemi bir QDialogButtonBox iine koyarz. Yerleimleri oluturan kod basittir, o yzden onu incelemeyeceiz. Buraya kadar, kullanc arayznn paracklarn ayarladk, bu nedenle artk dikkatimizi temel ilevsellie verebiliriz.
tableModel = new QSqlRelationalTableModel(this); tableModel->setTable("employee"); tableModel->setRelation(Employee_DepartmentId, QSqlRelation("department", "id", "name")); tableModel->setSort(Employee_Name, Qt::AscendingOrder); tableModel->select(); QSqlTableModel *relationModel = tableModel->relationModel(Employee_DepartmentId); departmentComboBox->setModel(relationModel); departmentComboBox->setModelColumn( relationModel->fieldIndex("name"));

Model, daha nce grdmz QSqlTableModel ile hemen hemen ayn ekilde ina edilir ve ayarlanr, fakat bu sefer bir QSqlRelationalTableModel kullanrz ve bir d anahtar ilikisi ayarlarz. setRelation() fonksiyonu, bir d anahtar alannn indeksini ve bir QSqlRelation alr. QSqlRelation kurucusu, bir tablo ismi (d anahtarn tablosu), d anahtar alannn adn ve d anahtar alannn deerini temsil ederken grntleyecei alann adn alr. Bir QComboBox, bir QListWidget gibi veri elerini tutmak iin dhili bir Modele sahiptir. Bu Model yerine kendi Modelimizi yerletirebiliriz ve burada yaptmz da tam olarak budur; ona, bir QSqlRelationalTableModel tarafndan kullanlan liki Modeli(Relation Model) vermek. liki, iki stuna sahiptir, bu nedenle alr kutunun hangisini gstermesi gerektiini belirtmeliyiz. liki Modeli, setRelation() ardmzda oluturulmutu, bu nedenle name stununun indeksini bilmiyoruz. Bu nedenle de alr kutunun departman isimlerini gsterebilmesi iin doru indekse ulamak amacyla fieldIndex() fonksiyonunu alan ismi ile arrz. QSqlRelationalTableModela krler olsun ki, alr kutu departman IDleri yerine departman isimlerini gsterecek.
mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit); mapper->setModel(tableModel); mapper->setItemDelegate(new QSqlRelationalDelegate(this)); mapper->addMapping(nameEdit, Employee_Name); mapper->addMapping(departmentComboBox, Employee_DepartmentId); mapper->addMapping(extensionLineEdit, Employee_Extension); mapper->addMapping(emailEdit, Employee_Email); mapper->addMapping(startDateEdit, Employee_StartDate);

Veriyi Tablo Biiminde Formlar inde Sunma


Bu ksmda, Staff Maneger uygulamasnn, bir master-detail iliki iinde iki QTableView ieren ana formunu inceleyeceiz. (Form, ekil 12.4te gsteriliyor.) Master Grn, departmanlarn bir listesidir. Detail Grn, geerli departman iindeki personelin bir listesidir. Sunduklar iki veritaban tablosu da d anahtar alanlarna sahip olduklar iin, her iki Grn de QSqlRelationalTableModel kullanr. Konu ile ilgili CREATE TABLE ifadeleri bir nceki ksmda gsterilmitir.

218

Qt 4 ile C++ GUI Programlama

ekil 12.4

Her zamanki gibi, stun indekslerine anlaml isimler vermek iin bir enum kullanrz:
enum { Department_Id = 0, Department_Name = 1, Department_LocationId = 2 };

MainForm snfnn, balk dosyas iindeki tanmlanmasna bakarak balayacaz:


class MainForm : public QWidget { Q_OBJECT public: MainForm(); private slots: void updateEmployeeView(); void addDepartment(); void deleteDepartment(); void editEmployees(); private: void createDepartmentPanel(); void createEmployeePanel(); QSqlRelationalTableModel *departmentModel; QSqlRelationalTableModel *employeeModel; QWidget *departmentPanel; ... QDialogButtonBox *buttonBox; };

Bir master-detail iliki ayarlamak iin, kullancnn farkl bir kayd (satr) setiinden emin olmalyz. Detail tablosunu sadece ilgili kaytlar gsterecek ekilde gncelleriz. Bu, updateEmployeeView() private yuvas ile baarlr. Dier yuva, kurucu iin yardmclardr. 219

Qt 4 ile C++ GUI Programlama Kurucuya ait kodun ou kullanc arayzn oluturmakla ve uygun sinyal-yuva balantlarn ayarlamakla ilgilidir. Biz sadece veritaban programlama ile ilgili paralaryla ilgileneceiz.
MainForm::MainForm() { createDepartmentPanel(); createEmployeePanel();

Kurucu, iki yardmc fonksiyonu armakla balar. lki, departman Modeli ve Grnn oluturur ve ayarlar, ikincisi, aynlarn personel(employee) Modeli ve Grn iin yapar. Bu fonksiyonlarn konu ile ilgili paralarna kurucuyu incelemeyi bitirdikten sonra bakacaz. Kurucunun sradaki paras, iki tablo Grnn ieren bir blc(splitter) ve formun butonlarn ayarlar. Bunlarn tmn atlayacaz.
... connect(addButton, SIGNAL(clicked()), this, SLOT(addDepartment())); connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteDepartment())); connect(editButton, SIGNAL(clicked()), this, SLOT(editEmployees())); connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); ... departmentView->setCurrentIndex(departmentModel->index(0, 0)); }

Butonlar diyalog iindeki yuvalara balarz ve geerli enin ilk departman olmasn salarz. Bylece kurucuyu grm olduk. imdi, departman Modelini ve Grnn ayarlayan createDepartmanPanel() yardmc fonksiyonunun kodlarna bakacaz: Kod Grnm:
void MainForm::createDepartmentPanel() { departmentPanel = new QWidget; departmentModel = new QSqlRelationalTableModel(this); departmentModel->setTable("department"); departmentModel->setRelation(Department_LocationId, QSqlRelation("location", "id", "name")); departmentModel->setSort(Department_Name, Qt::AscendingOrder); departmentModel->setHeaderData(Department_Name, Qt::Horizontal, tr("Dept.")); departmentModel->setHeaderData(Department_LocationId, Qt::Horizontal, tr("Location")); departmentModel->select(); departmentView = new QTableView; departmentView->setModel(departmentModel); departmentView->setItemDelegate(new QSqlRelationalDelegate(this)); departmentView->setSelectionMode( QAbstractItemView::SingleSelection); departmentView->setSelectionBehavior(QAbstractItemView::SelectRows); departmentView->setColumnHidden(Department_Id, true); departmentView->resizeColumnsToContents(); departmentView->horizontalHeader()->setStretchLastSection(true); departmentLabel = new QLabel(tr("Depar&tments"));

220

Qt 4 ile C++ GUI Programlama


departmentLabel->setBuddy(departmentView); connect(departmentView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateEmployeeView())); ... }

Kod, bir nceki ksmda employee tablosu iin bir Model ayarlarken grdmz ile benzer bir ekilde balar. Grn, standart bir QTableViewdr, fakat bir d anahtara sahip olduumuz iin QSqlRelationalDelegate kullanmalyz. Bylece d anahtarn metni Grn iinde grnr ve ID yerine bir alan kutu ile deitirilebilir. Kullanc iin anlaml olmad iin departmann ID alann gizlemeyi tercih ettik. Departman Grn, QAbstractItemView::SingleSelectiona ayarlanm kendi seme moduna sahiptir ve seme davran QAbstractItemView::SelectRowsa ayarlanmtr. Mod ayar, kullancnn tablodaki hcreler boyunca dolaabilecei anlamna, davran ayar ise kullanc hcreler boyunca dolarken tm satrn vurgulanaca anlamna gelir. Grnn seme modelinin currentRowChanged() sinyalini, updateEmployeeView() yuvasna balarz. Bu balant, master-detail ilikisinin yapt itir, ve personel Grnnde daima departman Grnnde vurgulanan departmana ait personeli gstermeyi salar.
void MainForm::createEmployeePanel() { employeePanel = new QWidget; employeeModel = new QSqlRelationalTableModel(this); employeeModel->setTable("employee"); employeeModel->setRelation(Employee_DepartmentId, QSqlRelation("department", "id", "name")); employeeModel->setSort(Employee_Name, Qt::AscendingOrder); employeeModel->setHeaderData(Employee_Name, Qt::Horizontal, tr("Name")); employeeModel->setHeaderData(Employee_Extension, Qt::Horizontal, tr("Ext.")); employeeModel->setHeaderData(Employee_Email, Qt::Horizontal, tr("Email")); employeeView = new QTableView; employeeView->setModel(employeeModel); employeeView->setSelectionMode(QAbstractItemView::SingleSelection); employeeView->setSelectionBehavior(QAbstractItemView::SelectRows); employeeView->setEditTriggers(QAbstractItemView::NoEditTriggers); employeeView->horizontalHeader()->setStretchLastSection(true); employeeView->setColumnHidden(Employee_Id, true); employeeView->setColumnHidden(Employee_DepartmentId, true); employeeView->setColumnHidden(Employee_StartDate, true); employeeLabel = new QLabel(tr("E&mployees")); employeeLabel->setBuddy(employeeView); ... }

221

Qt 4 ile C++ GUI Programlama Personel Grnnn dzenleme tetikleri(edit triggers) QAbstractItemView::NoEditTriggersa ayarlanr ve sonu olarak Grn saltokunur yaplr. Bu uygulamada, kullanc, bir nceki ksmda gelitirdiimiz EmployeeFormu aran Edit Employees butonuna tklayarak, personel kayd ekleyebilir, dzenleyebilir ve silebilir. Bu sefer, bir deil, stun gizleriz. id stununu gizleriz, nk yine kullanc iin bir anlam ifade etmez. Ayrca, departmanid stununu da gizleriz, nk seili departman iinde sadece personeller gsterilir. Son olarak, startdate stununu gizleriz, nk konu ile az ilgilidir ve zaten Edit Employees butonuna tklayarak eriilebilir.
void MainForm::updateEmployeeView() { QModelIndex index = departmentView->currentIndex(); if (index.isValid()) { QSqlRecord record = departmentModel->record(index.row()); int id = record.value("id").toInt(); employeeModel->setFilter(QString("departmentid = %1").arg(id)); employeeLabel->setText(tr("E&mployees in the %1 Department") .arg(record.value("name").toString())); } else { employeeModel->setFilter("departmentid = -1"); employeeLabel->setText(tr("E&mployees")); } employeeModel->select(); employeeView->horizontalHeader()->setVisible( employeeModel->rowCount() > 0); }

Her ne zaman departman deiirse (balang dhil), bu yuva arlr. Eer geerli departman uygunsa, fonksiyon, departmann IDsine eriir ve personel Modeli zerinde bir filtre ayarlar. Bu, eleen bir departman ID d anahtarnn personellerinin gsterilmesini mecbur klar. (Bir filtre sadece, WHERE anahtar kelimesi olmayan bir WHERE cmleciidir.) Ayrca, employee tablosunun zerinde gsterilen etiketi, personelin iinde bulunduu departmann ismini gsterecek ekilde gncelleriz. Eer geerli departman uygun deilse (mesela veritaban bosa), herhangi bir kaytla elememesini salamak iin filtreyi var olmayan bir departman IDsine ayarlarz. Sonra, filtreyi Modele uygulamak iin Model stnde select()i arrz. Bu, srayla, Grnn kendisini gncellemesiyle sonulanacak sinyaller yayacak. Son olarak, personel olup olmamasna bal olarak, employee tablosunun stun balklarn gsterir ya da gizleriz.
void MainForm::addDepartment() { int row = departmentModel->rowCount(); departmentModel->insertRow(row); QModelIndex index = departmentModel->index(row, Department_Name); departmentView->setCurrentIndex(index); departmentView->edit(index); }

Eer kullanc Add Dept. butonuna tklarsa, department tablosunun sonuna bir satr ekler, bu satr geerli satr yapar ve kullanc sanki F2ye basm ya da ona ift tklam gibi, department name stununun dzenlenmesini balatrz. Eer baz varsaylan deerler salamamz gerekseydi, insertRow() arsndan hemen sonra setData()y ararak yapabilirdik. 222

Qt 4 ile C++ GUI Programlama Kendimizi, yeni kaytlar iin esiz(unique) anahtarlar oluturmaya mecbur etmemiz gerekmiyordu, nk stunlar ele alrken, bunu bizim iin gerekletirmesi iin otomatik artan(auto-incrementing) bir stun kullanmtk. Eer bu yaklam mmkn ya da uygun deilse, Modelin beforeInsert() sinyalini balayabiliriz. Bu, kullancnn dzenlemesinden sonra, ekleme, veritabannda yerini almadan hemen nce yaylr. Bu, IDler ilave etmek ya da kullanc verilerini ilemek iin en ideal zamandr. Benzer ekilde, beforeDelete() ve beforeUpdate() sinyalleri de vardr; bunlar denetim izleri(audit trails) oluturmak iin kullanldrlar. Kod Grnm:
void MainForm::deleteDepartment() { QModelIndex index = departmentView->currentIndex(); if (!index.isValid()) return; QSqlDatabase::database().transaction(); QSqlRecord record = departmentModel->record(index.row()); int id = record.value(Department_Id).toInt(); int numEmployees = 0; QSqlQuery query(QString("SELECT COUNT(*) FROM employee " "WHERE departmentid = %1").arg(id)); if (query.next()) numEmployees = query.value(0).toInt(); if (numEmployees > 0) { int r = QMessageBox::warning(this, tr("Delete Department"), tr("Delete %1 and all its employees?") .arg(record.value(Department_Name).toString()), QMessageBox::Yes | QMessageBox::No); if (r == QMessageBox::No) { QSqlDatabase::database().rollback(); return; } query.exec(QString("DELETE FROM employee " "WHERE departmentid = %1").arg(id)); } departmentModel->removeRow(index.row()); departmentModel->submitAll(); QSqlDatabase::database().commit(); updateEmployeeView(); departmentView->setFocus(); }

Eer kullanc bir departman silmek isterse ve eer departmanda hi personel yoksa, bir formalite olmakszn yapmasna izin veririz. Fakat departmanda personel varsa, kullancdan silme ilemini onaylamasn isteriz ve eer onaylarsa, veritabannn ilikisel btnln salamak iin basamak basamak silme(cascading delete) yaparz. Bunu baarmak iin, en azndan SQLite 3 gibi bizi ilikisel btnl iin zorlamayan veritabanlar iin, bir etkileim(transaction) kullanmak zorundayz. Etkileim balatlr balatlmaz, departman iinde ka personel olduunu bulmak iin bir sorgu altrrz. Eer en az bir tane varsa, onay almak iin bir mesaj kutusu gsteririz. Eer kullanc hayr derse, etkileimi keseriz ve geri dneriz. Aksi halde, departmandaki tm personeli ve departmann kendini siler ve etkileimi ileriz. 223

Qt 4 ile C++ GUI Programlama


void MainForm::editEmployees() { int employeeId = -1; QModelIndex index = employeeView->currentIndex(); if (index.isValid()) { QSqlRecord record = employeeModel->record(index.row()); employeeId = record.value(Employee_Id).toInt(); } EmployeeForm form(employeeId, this); form.exec(); updateEmployeeView(); }

editEmployee() yuvas, kullanc Edit Employee butonuna tkladnda arlr. Geersiz bir personel IDsi atayarak balarz. Sonra, eer mmknse, bunun zerine geerli bir personel IDsi yazarz. Sonra, EmployeeFormu kurarz ve onu gsteririz. Son olarak, deitirilmi personeller olabileceinden, ana formun detail tablosunun Grnnn kendisini yenilemesini salamak iin updateEmployeeView() yuvasn arrz.

224

You might also like