Qooxdoo JavaScript / AJAX Framework İle Merhaba Dünya'dan Biraz Öteye

0
muhuk

Aşağıdaki eğitsel burada yayınlanan (İngilizce) aslından çeviridir. Gerek benim yeteneksizliğim gerekse Türkçe'nin bilişim terimleri konusunda zayıf olması sebebiyle kötü bir çeviri olmuştur. Çeviri için özür diliyor ve mümkünse aslını okumanızı rica ediyorum.

Qooxdoo özellikle masaüstü benzeri GKA'lar yaratmakta kuvvetli bir AJAX çatısı. Tkinter veya GTK gibi, ama daha çok swing'e benziyor. İyi hazırlanmış belgeler ve temiz bir API ile geliyor. Yapılandırma ve ilklendirme için küçük bir Python programı var. Çok geniş bir kütüphaneye sahip olduğu için geliştirme sürecinde kısmen derlenmiş kaynak üzerinde çalışıp, bitirdiğinizde yine bu programı kullanıp tek (aslında iki, bir de yükleyici oluşturuyor), az yer kaplayan bir dosya oluşturabilirsiniz.

Aşağıdaki Qooxdoo (kısaca qx) için giriş seviyesinde ama Merhaba Dünya'dan biraz öteye gitmeye çalışan bir eğitseldir. Bu nesne yönelimli programlama kavramlarını açıklayan bir eğitsel değildir, bunları zaten bildiğinizi varsayıyorum.

Taslağı Yaratalım

Basit, hesap makinesi benzeri bir uygulama yaratacağız. Kaynağı indirdiğinizi (bugün itibariyle 0.8 sürümü) ve qxtut gibi bir dizine açtığınızı varsayıyorum. İlk yapacağımız uygulamamız için aşağıdaki komutla bir taslak yaratmak:

./qooxdoo-0.8-sdk/tool/bin/create-application.py --name basicmath

Bu komut bizim için ./basicmath içerisinde aşağıdaki dizin yapısını oluşturdu:

qxtut/
  qooxdoo-0.8-sdk/
  basicmath/
    source/
    build/
    cache/
    api/
    config.json
    Manifest.json

Aslında, bazı dizinler (build & cache) henüz yok, ama devam ettikçe oluşacaklar. Şimdilik sadece uygulamanın dosya yapısına dikkatinizi çekmek istiyorum. config.json ve Manifest.json yapılandırma aracı için ayar dosyaları. Bu eğitsel için ayar dosyalarında değişiklik yapmamıza gerek yok.

İlk yapılandırma (veya derleme) işlemimizi gerçekleştirelim:

cd basicmath
./generate.py source

Eğer şimdi ./source/index.html'yi tarayıcınızda açarsanız bir Merhaba Dünya uygulaması göreceksiniz. Birazdan bunu kendi programımızla değiştireceğiz. Ancak başlamadan birkaç noktaya dikkatinizi çekmek isterim:

  • Sınıflarımızı tanımlarken qx.Class nesnesi üzerindeki define fonksiyonunu kullanıyoruz ve (tam) sınıf adımızla birlikte içeriği parametre olarak veriyoruz. Yani Qooxdoo'nun nesne yönelimli programlama özelliklerinden faydalanmak için sınıflarımızı prototype kullanarak tanımlamıyoruz.
  • Qooxdoo tekli kalıtım (mixin'lerle birlikte) destekliyor. Sınıfımızın içeriğinde, eğer varsa, extend anahtarını kullanarak üst sınıfımızı belirliyoruz.
  • Nesne üyeleri members anahtarı içinde, sınıf üyeleri de statics anahtarı içinde tanımlanıyor.
  • construct ve destruct, ilklendirme ve temizlik için iki özel fonksiyon.
  • Qooxdoo property'leri de destekliyor, ancak bu eğitselin kapsamı dışındalar.
  • Son olarak "#asset(basicmath/*)" satırı yapılandırma programına qxtut/basicmath/source/resource/basicmath dizinindeki kaynakları (resim vs) dahil etmesini söylüyor.

Kendi Sınıflarımız

Uygulamamızı inşa etmeye başlayalım. Application.js'nin fazlalıklarından arındırılmış hali aşağıdadır:

/* ************************************************************************
#asset(basicmath/*)
************************************************************************ */

qx.Class.define("basicmath.Application", {
    extend: qx.application.Standalone,
    members: {
        main: function()
        {
            this.base(arguments);
            if (qx.core.Variant.isSet("qx.debug", "on")) {
                qx.log.appender.Native;
                qx.log.appender.Console;
            }
            // Our code will come here
        }
    }
});

Şimdi Operation adındaki kendi sınıfımızı yaratacağız. Bu sınıf içerisinde iki değişken, bir (temel) işlem ve işlemin değişkenler üzerinde uygulanmasıyla elde edilen sonuç olacak. Daha sonra bu sonucu raporlama yeteneğini de ekleyeceğiz. Aşağıdaki kodu Operation.js olarak yapıştırın:

/* ************************************************************************
#asset(basicmath/*)
************************************************************************ */

qx.Class.define("basicmath.Operation", {
    extend: qx.ui.container.Composite,
    construct: function() {
        this.base(arguments);
        var layout = new qx.ui.layout.HBox(6);
        this.setLayout(layout);
        this.operand1 = new qx.ui.form.TextField("0");
        this.operator = new qx.ui.form.SelectBox();
        this.operator.add(new qx.ui.form.ListItem("add"));
        this.operator.add(new qx.ui.form.ListItem("subtract"));
        this.operator.add(new qx.ui.form.ListItem("multiply"));
        this.operator.add(new qx.ui.form.ListItem("divide"));
        this.operand2 = new qx.ui.form.TextField("0");
        this.result = new qx.ui.basic.Label("0");
        this.add(this.operand1);
        this.add(this.operator);
        this.add(this.operand2);
        this.add(this.result);
    },
    members: {
        operand1: null,
        operator: null,
        operand2: null,
        result: null
    }
});

Kodun açıklama gerektirmemesi gerek. Burada operand1, operator, operand2 ve result üyelerine dikkatinizi çekmek istiyorum. Bunlara sınıf gövdesinde (members içinde) değil de constructor içinde ilk değerlerini atıyoruz. Bunun nedeni aldıkları değerlerin primitif olmayan Object tipinden türemiş olması. Eğer üyelere sınıf gövdesinde primitif olmayan değerler (örneğin [1, 2, 3] dizisini) atarsak bu sınıftan yaratılan tüm nesneler aynı değerleri (nesneleri) gösterecektir.

Şimdi bu sınıfı uygulamamıza bağlayalım. "// Our code will come here" satırını aşağıdaki gibi değiştirin:

this.getRoot().add(new basicmath.Operation);

Tekrar derleyip index.html'i çalıştırdığımızda bileşenleri görmemiz gerekiyor.

Basit Davranışlar

Bileşenimizin işlemi hesaplamasını ve sonucu göstermesini istiyoruz. Operation'ın üyeler kısmını aşağıdaki gibi değiştirelim:

members: {
    operand1: null,
    operator: null,
    operand2: null,
    result: null,
    updateResult: function() {
        var v1 = this.cleanField(this.operand1);
        var v2 = this.cleanField(this.operand2);
        var r;
        switch(this.operator.getValue()) {
            case "add": r = v1+v2; break;
            case "subtract": r = v1-v2; break;
            case "multiply": r = v1*v2; break;
            case "divide": r = v1/v2; break;
        }
        this.result.setContent(String(r));
        this.operand1.setValue(String(v1));
        this.operand2.setValue(String(v2));
    },
    cleanField: function(field) {
        var val = parseInt(field.getValue());
        return isNaN(val) ? 0 : val;
    }
}

updateResult ve cleanField diye iki yeni fonksiyon ekledik. Şimdi construct'ı aşağıdaki gibi değiştirerek bunlardan faydalanalım:

construct: function() {
    this.base(arguments);
    var layout = new qx.ui.layout.HBox(6);
    this.setLayout(layout);
    this.operand1 = new qx.ui.form.TextField("0");
    this.operand1.addListener("input", this.updateResult, this);
    this.operator = new qx.ui.form.SelectBox();
    this.operator.add(new qx.ui.form.ListItem("add"));
    this.operator.add(new qx.ui.form.ListItem("subtract"));
    this.operator.add(new qx.ui.form.ListItem("multiply"));
    this.operator.add(new qx.ui.form.ListItem("divide"));
    this.operator.addListener("changeValue", this.updateResult, this);
    this.operand2 = new qx.ui.form.TextField("0");
    this.operand2.addListener("input", this.updateResult, this);
    this.result = new qx.ui.basic.Label("0");
    this.add(this.operand1);
    this.add(this.operator);
    this.add(this.operand2);
    this.add(this.result);
}

Değişkenler veya işlem türü değiştiğinde sonucu güncellemek için üç dinleyici (event listener) eklemiş olduk:

this.operand1.addListener("input", this.updateResult, this);
this.operator.addListener("changeValue", this.updateResult, this);
this.operand2.addListener("input", this.updateResult, this);

Dinleyiciye verdiğimiz son parametre (this) çağrının (ikinci parametre) kapsamını belirliyor.

Tekrar derleyip çalıştıralım. İşlemin sonucunun değişiklik yaptıkca güncelleniyor olmalı.

Hepsini Olaylarla Bağlayalım

Önce kodun bitmiş halini verip, daha sonra detayları üzerinde durmak istiyorum. Aşağıdaki Operation.js:

/* ************************************************************************
#asset(basicmath/*)
#asset(qx/icon/Oxygen/*)
************************************************************************ */

qx.Class.define("basicmath.Operation", {
    extend: qx.ui.container.Composite,
    construct: function() {
        this.base(arguments);
        var layout = new qx.ui.layout.HBox(6);
        this.setLayout(layout);
        this.operand1 = new qx.ui.form.TextField("0");
        this.operand1.addListener("input", this.updateResult, this);
        this.operator = new qx.ui.form.SelectBox();
        this.operator.add(new qx.ui.form.ListItem("add"));
        this.operator.add(new qx.ui.form.ListItem("subtract"));
        this.operator.add(new qx.ui.form.ListItem("multiply"));
        this.operator.add(new qx.ui.form.ListItem("divide"));
        this.operator.addListener("changeValue", this.updateResult, this);
        this.operand2 = new qx.ui.form.TextField("0");
        this.operand2.addListener("input", this.updateResult, this);
        this.result = new qx.ui.form.TextField("0");
        this.result.setReadOnly(true);
        var close_button = new qx.ui.form.Button(null, "qx/icon/Oxygen/16/actions/application-exit.png");
        close_button.addListener("execute", function(e) {
            this.fireDataEvent("changeResult", 0, parseFloat(this.result.getValue()), false);
            this.destroy();
        }, this);
        this.add(this.operand1);
        this.add(this.operator);
        this.add(this.operand2);
        this.add(new qx.ui.basic.Label("="));
        this.add(this.result);
        this.add(new qx.ui.core.Spacer(8));
        this.add(close_button);
    },
    events: {
        "changeResult": "qx.event.type.Data"
    },
    members: {
        operand1: null,
        operator: null,
        operand2: null,
        result: null,
        updateResult: function() {
            var v1 = this.cleanField(this.operand1);
            var v2 = this.cleanField(this.operand2);
            var r;
            switch(this.operator.getValue()) {
                case "add": r = v1+v2; break;
                case "subtract": r = v1-v2; break;
                case "multiply": r = v1*v2; break;
                case "divide": r = v1/v2; break;
            }
            this.fireDataEvent("changeResult", r, parseFloat(this.result.getValue()), false);
            this.result.setValue(String(r));
            this.operand1.setValue(String(v1));
            this.operand2.setValue(String(v2));
        },
        cleanField: function(field) {
            var val = parseInt(field.getValue());
            return isNaN(val) ? 0 : val;
        }
    }
});

Ve Application.js;

/* ************************************************************************
#asset(basicmath/*)
#asset(qx/icon/Oxygen/*)
************************************************************************ */

qx.Class.define("basicmath.Application", {
    extend : qx.application.Standalone,
    members : {
        main : function()
        {
            this.base(arguments);
            if (qx.core.Variant.isSet("qx.debug", "on")) {
                qx.log.appender.Native;
                qx.log.appender.Console;
            }
            var layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(8));
            var layout_footer = new qx.ui.container.Composite(new qx.ui.layout.HBox(6));
            var total = new qx.ui.basic.Label("0");
            var add_button = new qx.ui.form.Button("Add New", "qx/icon/Oxygen/16/actions/list-add.png");
            add_button.addListener("execute", function(e) {
                var new_operation = new basicmath.Operation();
                layout.addBefore(new_operation, layout_footer);
                new_operation.addListener("changeResult", function(e) {
                    var old_total = parseFloat(total.getContent());
                    var new_total = old_total - e.getOldData() + e.getData();
                    total.setContent(String(new_total));
                }, this)
            }, this);
            layout_footer.add(add_button);
            layout_footer.add(total);
            layout.add(layout_footer);
            add_button.execute();
            this.getRoot().add(layout);
        }
    }
});

Yukarıdan aşağıya doğru değişiklikleri incelemeye başlayalım. Application.js içinde:

var layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(8));
this.getRoot().add(layout);

Bileşenlerin davranışlarını özelleştirmemiz için her seferinde alt sınıf yaratmamız gerekmiyor. Operation'ı tekrar tekrar kullanmak istediğimden dolayı ayrı bir sınıf oluşturdum. Diğer yandan uygulamanın yerleşimi için Application.main içerisinde birkaç nesne yaratıp bunları özelleştirdim. Burada layout kök bileşen, diğer tüm bileşenleri onun içerisine yerleştiriyoruz. Bunlar bir veya daha fazla Operation ve en altta genel toplamı gösteren bir alan. VBox yerleşim modeli alt bileşenleri üstüste yerleştiriyor, HBox ise yan yana.

var layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(8));
var layout_footer = new qx.ui.container.Composite(new qx.ui.layout.HBox(6));
var total = new qx.ui.basic.Label("0");
var add_button = new qx.ui.form.Button("Add New", "qx/icon/Oxygen/16/actions/list-add.png");
add_button.addListener("execute", function(e) {
    var new_operation = new basicmath.Operation();
    layout.addBefore(new_operation, layout_footer);
    new_operation.addListener("changeResult", function(e) {
        var old_total = parseFloat(total.getContent());
        var new_total = old_total - e.getOldData() + e.getData();
        total.setContent(String(new_total));
    }, this)
}, this);

Tüm işlemlerin genel toplamını göstermesi için total isminde bir Label tanımlıyoruz ve yeni işlemler yaratabilmekiçin bir düğme ekliyoruz. 7. satırdaki closure'a dikkat; Qooxdoo'nun sunduğu nesne yönelimli programlama avantajlarından yararlanabilmek için kendimizi biraz sınırlandırsak da dinamik bir ortamdayız. Son olarak tüm bileşenleri birbirlerine bağlıyor ve layout'u uygulamanın köküne ekliyoruz.

add_button.execute();

Bu bizim için ilk işlemi ilklendiriyor. Aynı kodu iki kere yazmamak için bunu düğmeyi ateşleyerek yapıyoruz (bkz. add_button'ın "execute" dinleyicisi).

Şimdi Operation.js'deki değişikliklere göz atalım. result Label'ını bir TextField ile değiştirdim (bağımlılıklar her değiştiğinde. "generate.py source"u çalıştırmayı unutmayın). TextField'in changeValue olayındaki getOldData fonksiyonundan yararlanmak istedim. Fakat görünüşe göre bu olay eski verileri içermiyor. Yine de (salt okunur olarak ayarlayıp) TextField olarak bıraktım.

Sonra Operation'ın sonuç değiştikçe bir sinyal vermesine karar verdim ve changeResult adındaki kendi olayımı ekledim:

events: {
    "changeResult": "qx.event.type.Data"
}

Bu olay Operation.updateResult içerisinde tetikleniyor:

this.fireDataEvent("changeResult", r, parseFloat(this.result.getValue()), false);

İkinci parametre e.getData ile, üçüncü parametre ise e.getOldData ile döndürülüyor. Böylece genel toplamı tüm Operation'ları ziyaret etmeden aşağıdaki gibi hesaplayabiliyoruz:

var new_total = old_total - e.getOldData() + e.getData();

Bir Operation'u yoketmeden önce changeResult'u ateşleyip genel toplamı düzeltmek önemli bir nokta:

close_button.addListener("execute", function(e) {
    this.fireDataEvent("changeResult", 0, parseFloat(this.result.getValue()), false);
    this.destroy();
}, this);

Toplayacak Olursak

Şimdi uygulamamız istediğimiz gibi çalışıyor olmalı. Tam bir derleme yapalım:

./generate.py build

Bu komut bir yükleyici (~150kb) ve bir uygulama betiği (~400kb) oluşturur. Artık Qooxdoo kaynak dosyalarını ihtiyacımız kalmadı, build dizinindeki dosyaları sunucumuza yüklememiz uygulamamızın çalışması için yeterli.

Böylece Qooxdoo ile Merhaba Dünya'dan biraz öteye eğitselimizi tamamlamış oluyoruz. Kolay gelsin :)

Görüşler

0
FZ
Bizi kırmayıp kendi yazını tercüme ettiğin ve buraya yolladığın için çok teşekkürler ve tabii ki eline sağlık! :)
0
KiPSOFT_
Bence Qooxdoo geliştirilmiş en sağlam Frameworklerden biri ve JavaScript'e güç katıyor ayrı yeten Delphi For Php 'ye verdiği destekle Php içinde güzel şeyler gelişmesine yardımcı oluyor.

Yazı için teşekkürler.
0
efendyl1
Bu yorumu yaparken neye dayanarak "Qooxdoo geliştirilmiş en sağlam Frameworklerden biri" dediniz merak ettim. Herhangi bir projenizde hangi ağırlıkta qooxdoo kullandınız veya stabilitesini nasıl ölçtünüz?

İnsanları yanlış yönlendirmeyelim.
Görüş belirtmek için giriş yapın...

İlgili Yazılar

Emacs Bilgileri

malkocoglu

Emacs hakkında bazı yararlı olacağını düşündüğümüz bilgileri sunuyoruz.

Emacs Dış Programları Nasıl İşletir?

Ayrica, son hafta çıkan diğer yazılar:

Web Madenciliği ile Daha İyi Bir E-öğrenme Modeli Mümkün Mü?

FZ

Web madenciliği kısaca Web sayfaları ve servislerinden otomatik olarak bilgi çekip bunlardaki kalıpları keşfetmek için veri madenciliği tekniklerinin kullanılması olarak tanımlanabilir. Acaba makina öğrenme (machine learning) ve veri madenciliği (data mining) tekniklerinin özel bir dalı olan web madenciliği yöntemleri ile daha iyi e-öğrenme sistemleri geliştirmek mümkün müdür?

Missouri-Columbia Üniversitesinden James Laffey ve Jiye Ai'nin "Web Mining as a Tool for Understanding Online Learning" başlıklı makalesi bu ve benzeri sorulara olumlu cevaplar vermenin yanısıra güzel ve pratik bir örnek de gösteriyor: Yazıda somut olarak bir Blackboard (webct) e-öğrenme sisteminin web madenciliği ile nasıl daha da faydalı ve verimli hale getirilebileceği anlatılıyor.

Araştırmacılar, makalenin başında web madenciliği tekniklerinin başlıca şu üç noktada ciddi fayda getireceğini belirtmişler: devamı burada...

Dil Üstadları ile Araç Ustaları: IDE Ayrımı

FZ

Geliştirici dünyası iki kampa ayrılmıştır. Bir kampta dil üstadları vardır, bu yazılımcılar yüksek seviyeli programlamadan -- birinci-sınıf fonksiyonlar, aşamalı programlama, AOP, MOP, kendi kendini sorgulama -- bahsederler. Araç ustaları ise tümleşik geliştirme ve hata ayıklama araçlarında ustadırlar, kod tamamlama, "refactoring", vs. Dil üstadları Emacs ya da VIM kullanır, bu tür editörler yeni dilleri denemek için daha uygundur. Araç ustaları ise Visual Studio, Eclipse, IntelliJ gibi IDE'leri kullanırlar.

Laszlo ve Groovy gibi yeni diller ya da AOP (Aspect Oriented Programming) gibi dil uzantıları genellikle öncelikli olarak metin-editörü tabanlı yazılım geliştirme ortamlarında ortaya çıkarlar ve ancak ondan bir süre sonra IDE dünyası bu tür desteklere kavuşur. Eğer dil ya da uzantı gerçekten başarılı ise araçlar da bunu desteklemeye başlar. Bu ayrımın tek sebebi araç geliştirmenin dil geliştirmekten zor olması değildir. Asıl mesele bir dile hakim olmak ile bir araç setine hakim olmanın çok farklı iki mantalite olmasıdır, belli bir ölçüye dek bunlar birbirlerini dışlayan alternatiflerdir. Acaba neden? İşte sebepleri...

Oliver Steele'nin The IDE Divide başlıklı makalesini tüm yazılım geliştiricilerin okumasında fayda var. (Not: Şöyle sağlam bir FM üyesi çıksa da bahsi geçen makaleyi Türk diline kazandırsa... hani yani küçük bir olasılık olsa da, belki diyorum, belki biri üstlenir, FM'ye bir katkıda bulunur...)

Türkiye´nin İlk FreeBSD İşletim Sistemi Kitabı Çıktı

honal

Türkiye'nin ilk FreeBSD İşletim Sistemi kitabı çıktı. Son yıllarda gittikçe artan bir ilgi ile karşılaşan FreeBSD hakkında hemen herşeyi bu kitapta bulabileceksiniz.

FreeBSD işletim sistemine olan ilgi son yıllarda oldukça artmıştır. Köklü bir işletim sistemi olmasına rağmen son yıllara kadar tanınmayan BSD ailesi, kararlı ve güvenli işletim sistemi sunmaktadır. Uzun yıllar FreeBSD üzerine yoğunlaşmış uzman bir yazar kadrosu tarafından yazılan kitap, Türkiye'nin ilk ve tek FreeBSD işletim sistemi kitabıdır. Kitap, FreeBSD'yi Türkiye'ye tanıtmak misyonunu üstlenmiştir. Bu nedenle kullanıcıların kurup deneyebilecekleri bir adet FreeBSD 4.9 CD'si ile birlikte gelmektedir.

Ayrıntılı bilgi için: Açıkkod Yayınevi

e-bergi Mart 2009 Sayısı Yayında

ilke444

Günden güne artan okuyucu kitlesi ile tüm bilgisayar bilimi ve özgür yazılım meraklılarına hitap ettiğini kanıtlayan e-bergi, Mart 2009 sayısı ile sizlerle.