6 Mart 2025 Perşembe

WxRuby3 ile Masaüstü Uygulama Geliştirmek 1

https://ujk-ujk.blogspot.com/2025/03/wxruby3-ile-masaustu-uygulama.html
İçindekiler +

     Selam Ruby ile masaüstü program yazmak üzerine neler yapabilirim bir göreyim dedim ve başladım denemelere. Denemelerimi Windows'ta yaptım. Windows üzerinde bende kurulu olan versiyon ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [x64-mingw-ucrt] olarak geliyor. Ruby installer sitesinden Ruby+Devkit 3.4.2-1 (x64) versiyonu indirip kurdum. 


    WxRuby3 Kurulumu

    WxRuby3 GUI kütüphanesini yüklemek için komut isteminde 

    gem install wxruby3

    komutunu girdim ve sıkıntısız yüklendi. Sizde sorun olurssa bendeki versiyon Ruby ile deneme yaparsınız. 

    Hemen deneme yapmak için hello.rb adında bir dosya üretip içine şu kodu yazdım.

    require 'wx'
    Wx::App.run { puts 'Merhaba Dünya!' }


    Aslında bu kod pek bir şey yapmıyor, eğer çalışırsa konsola Merhaba Dünya! yazıp bitiyor. Bu programın amacı WxRuby kütüphanesi kullanımının başarılı olduğunu görmek. Programı incelersek 

    require 'wx' komutu kurduğumuz WxRuby3 Gem dosyalarının programımızda kullanılacağını belirtiyor. 

    Wx::App sınıfı , ana Wx programı sınıfı. Bu sınıfın run metodunu çağırarak Wx uygulaması çalıştırılıyor. run metoduna verilen blok içindeki program bir Wx uygulaması olarak çalıştırılıyor. Dökümanına bakarsak run metodu Wx::App sınıfının bir oluşumunu üretiyor ve verilen kod bloğunu da bu nesnenin #on_init olayına bağlıyor, Yani nesne üretilince bloktaki kod çalışıyor. 

    Şimdi bir pencere üreterek ekranımızda gösteren bir kod yazalım bloğun içine. 

    require 'wx'
    Wx::App.run { Wx::Frame.new(nil, title: 'Merhaba Dünya!').show }


    Bu kod jenerik bir Frame (pencere) nesnesi üretiyor ve onun show metodunu çağırıyor. new metodunun ilk argümanı nil olarak verilmiş , bu Frame nesnesinin parent değeri. Bu değerin nil olması penceremizin bağımsız bir pencere olduğunu gösteriyor. Eğer buraya bir Wx::Window nesnesi beliritilirse penceremiz o nesneyle beraber minimize ve restore olacakmış (tabi kendi bağımsız olarak minimize de edilebilecek). İkinci argüman ise isimlendirilmiş olarak :title değerini belirtiyor, adından da anlaşılabileceği gibi pencere etiketinde yazan yazıyı belirliyor. İlerleyen zamanda başka argümanlar da kullanacağız. 

    show metodu uygulandığı pencereyi göstermek (ya da gizlemek) için kullanılıyor. 

    Şimdi programı çalıştıralım. 


    Karşımıza default boyutta ve default pozisyonda bir pencere açılacaktır. 


    Uygulama sınıfı tanımlamak

    Daha karmaşık uygulamalar yazarken Wx::App sınıfından türetilmiş kendi sınıfımızı tanımlamak çok daha faydalı olacaktır.  Bu sayede #initialize, #on_init ve #on_exit metodlarının üzerine yazarak istediğimiz davranışı göstermesini yönetebiliriz. 

    hello2.rb

    require 'wx'

    class MyApp < Wx::App
      def initialize
        super
        @frame = nil
      end
      attr_reader :frame
     
      # run deyince çalışacak
      def on_init
        @frame = Wx::Frame.new(nil, title: 'Merhaba Dünya!')
        @frame.show
      end
     
      # pencereyi kapatınca çalışacak
      def on_exit
        puts 'Çıkıyoruz.'
      end
    end

    MyApp.run


    Burada #on_init ve #on_exit metodlarının üzerine yazılmıyor, orjinal Wx::App sınıfında bu metodlar yok. Bu yüzden tanımlamalarında super ifadesi kullanılmıyor. 

    Eğer sınıfta on_init tanımlı değilse run metodu argümanında verilen blok çalıştırılır. Eğer on_init metodu tanımlanmışsa (ki bizim ürettiğimiz sınıfta mevcut) o zaman run metodu bu on_init metodunu çalıştıracaktır. Bu yüzden kod bloğu bermeden sadece MyApp.run demek yeterli olmuştur.

    Uygulamanın oluşum nesnesine uygulama hala aktifken 

     Wx.get_app

    olarak erişebiliriz. 



    Pencere sınıfı tanımlamak

    Kendi pencere sınıfımızı tanımlayıp onu da kullanabiliriz. Böylece pencere içindeki elemanları daha düzenli kodlayabiliriz. 

    hello3.rb

    require 'wx'

    class TheFrame < Wx::Frame
      def initialize(title)
        super(nil, title: title)
        panel = Wx::Panel.new(self)
        button = Wx::Button.new(panel, label: 'Bana Tıklayınız')
        button.evt_button(Wx::ID_ANY) do
          Wx.message_box('Merhaba. Bana tıkladığın için teşekkürler!',
            'Buton olayı')
        end
      end
    end

    class MyApp < Wx::App
      def initialize
        super
        @frame = nil
      end
      attr_reader :frame
     
      # run deyince çalışacak
      def on_init
        @frame = TheFrame.new('Merhaba Dünya!')
        @frame.show
      end
     
      # pencereyi kapatınca çalışacak
      def on_exit
        puts 'Çıkıyoruz.'
      end
    end

    MyApp.run

    ve çıktısı



    Wx::Frame

    Wx::Frame sınıfı oluşum kodu kalıbı

    Wx::Frame.new(parent, id, title, pos = Wx::DEFAULT_POSITION,
        size = Wx::DEFAULT_SIZE, style = Wx::DEFAULT_FRAME_STYLE,
        name = Wx::FRAME_NAME_STR)

    id : Pencereyi simgeleyen sayı , default -1

    pos (Array(Integer, Integer), Wx::Point) : Pencrenin sol üst köşesinin ekrana göre pozisyonu - x, y

      frame = Wx::Frame.new(nil, pos: [150, 50])


    size (Array(Integer, Integer), Wx::Size)  : Pencere boyutu - genişlik, yükseklik

      frame = Wx::Frame.new(nil, size: [400, 200])


    style (Integer) : Bir çok flag bitlerinden oluşan stil sayısı default olarak 

        MINIMIZE_BOX | MAXIMIZE_BOX | RESIZE_BORDER | SYSTEM_MENU | CAPTION |
    CLOSE_BOX | CLIP_CHILDREN


    Örnek :

    no_minimize.rb

    require "wx"

    Wx::App.run {
      Wx::Frame.new(nil,
        style: Wx::MAXIMIZE_BOX | Wx::RESIZE_BORDER |
              Wx::SYSTEM_MENU | Wx::CAPTION |  Wx::CLOSE_BOX).show
    }




    Boyut ve Pozisyon

    Pencere boyutunu iki şekilde belirtiriz. Ya pencereyi oluştururken ya da sonradan set_size metodunu çağırarak. 

    set_size.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(parent, title)
        super(parent, title: title,
            size: [350, 250])
      end
    end

    Wx::App.run {
      Örnek.new(nil, 'Boyutlandırma').show
    }


    Sonradan boyutlandırırsak

    require "wx"

    class Örnek < Wx::Frame
      def initialize(parent, title)
        super(parent, title: title)
      end
    end

    Wx::App.run {
      p = Örnek.new(nil, 'Boyutlandırma')
      p.set_size 100,200,350,250
      p.show
    }
    # x=100 - y=200 - w=350 - h=250

    ya da pozisyon belirtmeden

      p.set_size [350, 250]


    Pencereyi istediğimiz yere kaydırabiliriz. 

    move.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(parent, title)
        super(parent, title: title,
            size: [300, 200])
        move [800, 200]
      end
    end

    Wx::App.run {
      Örnek.new(nil, 'Kaydırma').show
    }




    Ekranı Ortalamak

    Pencerenin ekranı ortalaması için centre metodunu kullanırız. 

    merkezleme.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(parent, title)
        super(parent, title: title,
            size: [300, 200])
        centre
      end
    end

    Wx::App.run {
      Örnek.new(nil, 'Ortalama').show
    }


    Yatay ya da dikey olarak da ortalamak mümkün.

        centre  Wx::Orientation::VERTICAL
        centre  Wx::Orientation::HORIZONTAL




    Menüler, Toolbarlar

    Bir GUI uygulamasının temel öğelerinden biri menülerdir. Bir MenuBar nesnesi içinde Menu nesneleri barındırır. 


    Basit Menü

    simple_menu.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [300, 200]
        set_title "Basit Menü"
        centre

        menubar = Wx::MenuBar.new
        file_menu = Wx::Menu.new
        file_item = file_menu.append(Wx::ID_EXIT, "&Çıkış", "Uygulamadan çıkış")
        menubar.append file_menu, "&Dosya"
        set_menu_bar menubar

        evt_menu(file_item) { close }
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }



    Önce bir MenuBar nesnesi oluşturuyoruz. menubar = Wx::MenuBar.new

    Sonra üzerine eklemek için bir Menu nesnesi ekliyoruz. file_menu = Wx::Menu.new

    Menu nesnesine eleman ekliyoruz (popup içinde görünen)  file_item = file_menu.append(Wx::ID_EXIT, "&Çıkış", "Uygulamadan çıkış")

    & karakteri hangi harfin önüne gelirse Alt tuşuna basıldığında o harfin altı çizili olur (tuşlar ile menü seçme standardı)

    Daha sonra Menu nesnemizi MenuBar nesnemize başlığını belirterek ekliyoruz. menubar.append file_menu, "&Dosya"

    Son olarak Frame#set_menu_bar metodu ile MenuBar nesnemizi penceremize ekliyoruz. set_menu_bar menubar

    Eklediğimiz menüye tıklanınca ne olacağını da evt_menu olay işleme metoduna verdiğimiz kod bloğunda belirtiyoruz. evt_menu(file_item) { close }

    Burada tıklanınca Frame#close metodu çağrılıyor, yani pencere kapatılıp uygulama bitiriliyor. 



    İkonlar ve kısayollar

    Sıradaki örneğimiz temelde önceki ile aynı, ancak bu sefer Wx::MenuItem oluşum nesnesini elle oluşturacağız.

    icons_shortcuts.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [300, 200]
        set_title "İkonlar ve Kısayollar"
        centre

        menubar = Wx::MenuBar.new
        file_menu = Wx::Menu.new
        qmi = Wx::MenuItem.new(file_menu, Wx::ID_EXIT, "&Çıkış\tCTRL+Q")
        qmi.set_bitmap(Wx::Bitmap.new('exit_16.png'))
        file_menu.append qmi
        menubar.append file_menu, "&Dosya"
        set_menu_bar menubar

        evt_menu(Wx::ID_EXIT) { close }
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    qmi nesnesini oluştururken standart Wx::ID_EXIT id değerini kullandık. Wx::StandardID sınıfında tanımlı birçok hazır ID değeri var. Kendimizde bir sayı girebiliriz ama karışıklık olmasın diye hazırlardan işimize uyan varsa onu kullanmak daha iyidir. 

    Logo için resim dosyasını internetten bulup koydum. 

    Olay işleyici satırına 

        evt_menu(qmi) { close }

    de yazsak çalışır, id de versek çalışır. "&Çıkış\tCTRL+Q" değeri tuş takımı yardımıyla Alt+d+ç kombinasyonu ile çıkabileceğimiz gibi, hiç menü açmadan Ctrl+q basarak da uygulamadan çıkabiliriz. \t ile kısayol tuşu bildiriliyor.




    Alt menü ve seperatörler

    Her menüde alt menüler de olabilir. Bir de mesela Yeni Aç, Kaydet gibi menü elemanları ile Çıkış elemanı arasına bir ayırıcı (separator) koysak iyi olur.

    submenu.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Alt Menü"
        centre

        menubar = Wx::MenuBar.new
        file_menu = Wx::Menu.new

        file_menu.append Wx::ID_NEW, '&Yeni'
        file_menu.append Wx::ID_OPEN, '&Aç'
        file_menu.append Wx::ID_SAVE, '&Kaydet'
        file_menu.append_separator

        imp = Wx::Menu.new
        imp.append Wx::ID_ANY, "Haber listesi al..."
        imp.append Wx::ID_ANY, "Yer imlerini al..."
        imp.append Wx::ID_ANY, "Eposta al..."

        file_menu.append_sub_menu imp, "I&mport"

        file_menu.append Wx::ID_EXIT, "&Çıkış\tCTRL+Q"

        menubar.append file_menu, "&Dosya"
        set_menu_bar menubar

        evt_menu(Wx::ID_EXIT) { close }
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Alt menümüz yine bir Wx::Menu nesnesi ve üst menüye append_sub_menu metodu ile ekleniyor. İlk argüman eklenecek olan Wx::Menu nesnesi ikinci de menü başlık yazısı. 

    Ayrıcı eklemek içinse append_separator metodu kullanılıyor.


    İstersek hazır bulunan ikonları menülere ekleyebiliriz. 

        file_menu.append(Wx::ID_NEW, '&Yeni').
            set_bitmap(Wx::ArtProvider.get_bitmap(Wx::ART_NEW))
        file_menu.append(Wx::ID_OPEN, '&Aç').
            set_bitmap(Wx::ArtProvider.get_bitmap(Wx::ART_FILE_OPEN))
        file_menu.append(Wx::ID_SAVE, '&Kaydet').
            set_bitmap(Wx::ArtProvider.get_bitmap(Wx::ART_FILE_SAVE))


    Dökümanda sabitler listesinde ART_ ile başlayan sabitleri deneyebilirsiniz. 





    Checkbox menü elemanı

    3 çeşit menü elemanı vardır. 

    • normal eleman
    • check eleman
    • radio eleman

    Aşağıdaki örnekte check menü elemanı görülüyor. Check menü tiklenebilir bir menü elemanı görüntüsü verir. 

    checkmenu_item.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Check Menü Elemanı"
        centre

        toolbar = create_tool_bar
        toolbar.add_tool Wx::ID_EXIT, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_QUIT)
        toolbar.realize

        statusbar = create_status_bar
        statusbar.set_status_text "Ready"

        menubar = Wx::MenuBar.new
        view_menu = Wx::Menu.new

        shst = view_menu.append Wx::ID_ANY, "Durum çubuğunu göster",
            "Araç çubuğunu göster", Wx::ITEM_CHECK
        shtl = view_menu.append Wx::ID_ANY, "Araç çubuğunu göster",
            "Araç çubuğunu göster", Wx::ITEM_CHECK

        view_menu.check shst.id, true
        view_menu.check shtl.id, true

        menubar.append view_menu, "&Görünüm"
        set_menu_bar menubar

        evt_menu(shst) { shst.checked? ? statusbar.show : statusbar.hide }
        evt_menu(shtl) { shtl.checked? ? toolbar.show : toolbar.hide }
        evt_menu(Wx::ID_EXIT) { close }
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Görünüm menümüzün içine iki tane check menü elemanı ekledik. Bu iki menü elemanı ile durum çubuğu ve araç çubuğunu gösterip gizliyoruz. 

        shst = view_menu.append Wx::ID_ANY, "Durum çubuğunu göster",
            "Araç çubuğunu göster", Wx::ITEM_CHECK
        shtl = view_menu.append Wx::ID_ANY, "Araç çubuğunu göster",
            "Araç çubuğunu göster", Wx::ITEM_CHECK

    view_menu nesnemizin append metodu ilk iki parametresini daha önce kullandık, id numarası ve menüde yazan yazı. Üçüncü parametre yardım yazısı, dördüncü ise kind (yani tür) bilgisi. Dördüncü argümanda Wx::ITEM_CHECK girerek menü elemanının bir tiklemeli menü elemanı olduğunu bildiriyoruz. Sonrasında 

        view_menu.check shst.id, true
        view_menu.check shtl.id, true

    satırları ile her iki menü elemanının da başlangıçta tiklenmiş olarak gelmesini sağlıyoruz. Bu arada deneme yapalım derken basit de olsa durum ve araç çubuğu eklemesini de gördük .

    Menülere tıklanınca yapılacaklar

        evt_menu(shst) { shst.checked? ? statusbar.show : statusbar.hide }
        evt_menu(shtl) { shtl.checked? ? toolbar.show : toolbar.hide }

    satırlarındaki olay işleme kodları ile belirleniyor. checked? metodu menü elemanının o anda seçili olup olmadığını test ediyor. show ve hide metodları ise hemen tüm kontrollerdeki gibi uygulandığı nesnenin görünmesi ya da gizlenmesini sağlıyor.



    Bağlamsal menüler

    Bağlamsal menü (context menu) değişik bağlamlarda değişik menülerin açılması şeklinde oluşan menülerdir. Örneğin tarayıcınızda sayfa içine sağ tıklarsanız başka menü, adrese sağ tıklarsanız başka bir menü, pencere başlığına tıklayınca başka bir menü açılır. Bunlara bazen popup menü de denir. 

    context_menu.rb

    require "wx"

    class MyPopupMenu < Wx::Menu

      def initialize(*args, parent)
        super(*args)

        @parent = parent

        mmi = Wx::MenuItem.new(self, Wx::ID_ANY, 'Minimize')
        append mmi
        evt_menu(mmi) { on_minimize }

        cmi = Wx::MenuItem.new(self, Wx::ID_ANY, 'Kapat')
        append cmi
        evt_menu(cmi) { on_close }
      end

      def on_minimize
        @parent.iconize
      end

      def on_close
        @parent.close
      end
    end

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Bağlamsal Menü"
        centre

        evt_right_down { |e| on_right_down(e) }
      end

      def on_right_down(e)
        popup_menu MyPopupMenu.new(self), e.get_position
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Öncelikle Wx::Menu sınıfından kendi MyPopupMenu sınıfımızı üretiyoruz. 

        mmi = Wx::MenuItem.new(self, Wx::ID_ANY, 'Minimize')
        append mmi
        evt_menu(mmi) { on_minimize }

    Bu satırlarla menümüze yeni bir menü elemanı ekliyoruz. Olay işlemeyi eğer kod bloğu içinde yapmayacaksak dışarıdan başka bir oluşum metodu da çağırabiliriz. 

        evt_right_down { |e| on_right_down(e) }
      end

      def on_right_down(e)
        popup_menu MyPopupMenu.new(self), e.get_position
      end

    Burada görülense dışarıdan çağrılan metoda olay bilgisini içeren bir nesne göndermek. Frame üzerinde sağ tıklanan yeri bulmak için get_position metodu ile olay bilgisinden çekiyoruz, ve popup orada açıyoruz. 

    Şimdi çalıştırıp pencere içinde bir yere sağ tıklarsak.




    Araç Kutuları

    Menülerde bir hiyerarşi içinde uygulamamıza gerekli tüm işlemleri menülerimizde toplarız. Araç kutularında ise çok kullanılan bazı işlemleri hızlıca erişilebilir biçimde penceremize koyarız. Wx::Frame nesnesinde araç kutusu oluşturmak için create_tool_bar metodunu çağırırız.

    toolbar.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Basit Araç Kutusu"
        centre

        toolbar = create_tool_bar
        qtool = toolbar.add_tool Wx::ID_EXIT, "Çıkış",
          Wx::ArtProvider.get_bitmap(Wx::ART_QUIT)
        toolbar.realize

        evt_menu(qtool) { close }
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Örnekte araç kutumuzda bir tane araç butonu var ve uygulamadan çıkılmasını sağlıyor. 

        toolbar = create_tool_bar

    Araç kutumuzu üretir. Default olarak araç kutumuz yatay olur, sınır çizgileri yoktur ve ikonlar gösterir. 

        qtool = toolbar.add_tool Wx::ID_EXIT, "Çıkış",
          Wx::ArtProvider.get_bitmap(Wx::ART_QUIT)

    Bu satır da bir araç butonu ekliyor. add_tool metodu ilk parametresi id değeri, ikinci ise etiket yazısı (ki bu yazı görünmez), üçüncüsü de butondaki resim. 

        toolbar.realize

    Bütün araç butonlarımız bittikten sonra realize metodu ile aktif hale getiriyoruz. 


    Eğer bir'den fazla araç kutusu olacaksa farklı davranmak gerekir. 

    toolbars.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Araç Kutuları"
        centre

        vbox = Wx::VBoxSizer.new

        toolbar1 = Wx::ToolBar.new(self)
        toolbar1.add_tool Wx::ID_NEW, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_NEW)
        toolbar1.add_tool Wx::ID_OPEN, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_FILE_OPEN)
        toolbar1.add_tool Wx::ID_SAVE, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_FILE_SAVE)
        toolbar1.realize

        toolbar2 = Wx::ToolBar.new(self)
        toolbar2.add_tool Wx::ID_EXIT, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_QUIT)
        toolbar2.realize

        vbox.add toolbar1, 0, Wx::EXPAND
        vbox.add toolbar2, 0, Wx::EXPAND
        set_sizer vbox

        evt_tool(Wx::ID_EXIT) { close }
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Bu örnekte iki tane yatay araç kutusu üretiyoruz.

        toolbar1 = Wx::ToolBar.new(self)
    ...
        toolbar2 = Wx::ToolBar.new(self)

    Bu iki araç kutusunu dikey bir yerleşim yardımcısına yerleştirdik.




    Aktif-Pasif araç butonları

    Aşağıdaki örnekte araç butonlarının aktif veya pasif olmasını göreceğiz. Ayrıca araç kutumuza bir ayırıcı da ekleyeceğiz.

    undo_redo.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Undo Redo"
        centre

        @count = 5

        vbox = Wx::VBoxSizer.new

        @toolbar = create_tool_bar
        tundo = @toolbar.add_tool Wx::ID_UNDO, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_UNDO)
        tredo = @toolbar.add_tool Wx::ID_REDO, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_REDO)
        @toolbar.enable_tool Wx::ID_REDO, false
        @toolbar.add_separator
        texit = @toolbar.add_tool Wx::ID_EXIT, "",
          Wx::ArtProvider.get_bitmap(Wx::ART_QUIT)
        @toolbar.realize

        evt_tool(Wx::ID_UNDO) { on_undo }
        evt_tool(Wx::ID_REDO) { on_redo }
        evt_tool(Wx::ID_EXIT) { close }
      end

      def on_undo
        if @count > 1 and @count <= 5
          @count -= 1
        end

        if @count == 1
            @toolbar.enable_tool Wx::ID_UNDO, false
        end

        if @count == 4
            @toolbar.enable_tool Wx::ID_REDO, true
        end
      end

      def on_redo
        if @count < 5 and @count >= 1
          @count += 1
        end

        if @count == 5
            @toolbar.enable_tool Wx::ID_REDO, false
        end

        if @count == 2
            @toolbar.enable_tool Wx::ID_UNDO, true
        end
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Uygulamamızda 3 tane araç butonu ekledik , biri uygulamadan çıkmak için diğerleri de undo-redo (geri al - yinele) işlemlerini simüle etmek için. 

        @toolbar.enable_tool Wx::ID_REDO, false
        @toolbar.add_separator

    İlk başta redo butonu pasif durumda olacak , pasif yapmak için enable_tool metodunu kullanıyoruz. Araç butonu gruplarını birbirinden ayırmak için add_separator metodunu kullanıyoruz. Bu bazı işletim sistemlerinde bir çizgi eklerken mesela bende sadece butonlar arasını biraz boşlukla açıyor. 

      def on_undo
        if @count > 1 and @count <= 5
          @count -= 1
        end

        if @count == 1
            @toolbar.enable_tool Wx::ID_UNDO, false
        end

        if @count == 4
            @toolbar.enable_tool Wx::ID_REDO, true
        end
      end

    Undo ve redo işlemlerinin simülasyonunu yapıyoruz. 4 tane değişimimiz varmış ve geri alacak bir şey kalmayınca undo butonu pasif oluyor. İlk değişikliği geri alınca redo butonunu aktif ediyoruz. Benzer lojik redo butonu için de geçerli. 

    Bu bölümde WxRuby3 uygulama geliştirmede menüleri ve araç kutularını inceledik. Pencere yerleşimi ile devam edelim.




    WxRuby'de Yerleşim Yönetimi

    Tipik bir uygulamada birçok widget bulunur (ben bunlara 'zımbırtı' diyorum, bazen 'kontrol' dendiği de olur). Bu widget'lar başka barındırıcı widget'lar içine konarak yerleşimleri yönetilir. Wx kütüphanesinde yerleşimi sabit pozisyonlama ile ya da boyutlandırıcılar ile yönetmek mümkün, ve bu biraz karmaşık bir iş.


    Mutlak pozisyonlama

    Programlayıcı her kontrolün yerleşeceği yeri ve boyutlarını piksel olarak bildirir. Mutlak pozisyonlamanın bazı dezavantajları vardır.

    • Pencere boyutu değişince kontrollerin boyutu ve yerleşimi orantılı olarak değişmez. Bu yüzden genellikle boyutu değiştirilemeyen bilgilendirme pencerelerinde falan kullanılır.
    • Değişik işletim sistemlerinde (ve hatta ayarları değişik olan aynı sistemlerde bile) farklı görünen uygulamalar oluşur. 
    • Uygulamanızda yazı font'unu değiştirmek yerleşimde bozulmalara sebep olabilir.
    • Yerleşimi değiştirmeye karar verdiğimizde tüm yerleşimi elden geçirmemiz gerekir ki bu hem sıkıcı hem de yorucu olur.

    Bazen , mesela denemeler yaparken , mutlak pozisyonlama ile hızlıca deneme yapabiliriz. Ancak gerçek dünya uygulamaları, boyutlandırıcıların kullanılmasını gerektirir.

    Uygulama örneğimizde resimler olacak ve pencere boyutu değiştirilince resimlerin pozisyonunun otomatik değişmediğini göreceğiz. 

    absolute.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 300]
        set_title "Mutlak Pozisyonlama"
        centre

        @panel = Wx::Panel.new(self)
        @panel.background_colour = "gray"

        load_images

        @mincol.position = [20, 20]
        @bardejov.position = [40, 160]
        @rotunda.position = [170, 50]
      end

      def load_images
        @mincol = Wx::StaticBitmap.new(@panel, Wx::ID_ANY,
                Wx::Bitmap.new("mincol.jpg", Wx::BITMAP_TYPE_ANY))

        @bardejov = Wx::StaticBitmap.new(@panel, Wx::ID_ANY,
                Wx::Bitmap.new("bardejov.jpg", Wx::BITMAP_TYPE_ANY))

        @rotunda = Wx::StaticBitmap.new(@panel, Wx::ID_ANY,
                Wx::Bitmap.new("rotunda.jpg", Wx::BITMAP_TYPE_ANY))
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }






    Boyutlandırıcı kullanmak

    Boyutlandırıcılar mutlak pozisyonlama için verdiğimiz tüm dezavantajları halleder. WxRuby kütüphanesinde şu boyutlandırıcılar var.

    • Wx::BoxSizer
    • Wx::StaticBoxSizer
    • Wx::GridSizer
    • Wx::FlexGridSizer
    • Wx::GridBagSizer


    Wx::BoxSizer

    Wx::BoxSizer boyutlandırıcı içine kontrollerimizi yatay ya da dikey olarak sıralayabiliriz. Karmaşık yerleşimler oluşturmak için bir boyutlandırıcı içine diğerini koyarak kombinasyonlar üretebiliriz. 

        box = Wx::BoxSizer(integer orient)
        box.add(Wx::Window window, integer proportion=0,
                integer flag=0, integer border=0)


    Yeni boyutlandırıcı tanımlarken verilen orient parametresi yatay ya da dikey yerleşim için kullanılacağını belirtir. Buraya Wx::VERTICAL veya Wx::HORIZONTAL girilir. Boyutlandırıcıya bir kontrol eklemek için add metodunu kullanırız. Parametrelere bir bakalım.

    proportion parametresi eklenen kontrolün boyutlandırıcının verilen yönünde nasıl yayılacağını ve boyutlandırıcı boyutu değişince nasıl davranacağını belirler. Değer 0 ise boyutunu değiştirmez, 1 ise diğer 1 olanlarla beraber orantılı yayılır, 2 ise diğerlerine oranla 2 katı yayılır. 

    flag parametresi ise kontrolün boyutlandırıcı içindeki davranışını daha ileri seviyelerde ayarlarız. Kontroller arasında çizilecek sınırları belirleyebiliriz, kontroller arasında verilecek boşluk belirleyebiliriz. Bir liste dökümanda var. Sınır bilgisi border değerinin hangi kenarlara uygulanacağı da bu flag içinde belirtilir. Flag bitleri birbirine veya işareti | ile bağlanır (ki zaten tamsayı değerler veya ediliyor). Örneğin Wx::LEFT | Wx::BOTTOM ile solda ve altta border değerini uygulayacağımızı belirtiriz. 

    • Wx::LEFT
    • Wx::RIGHT
    • Wx::BOTTOM
    • Wx::TOP
    • Wx::ALL

    Panele boyutlandırıcı eklerken set_sizer metodu kullanılır. 

    border.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 300]
        set_title "Border"
        centre

        panel = Wx::Panel.new(self)
        panel.background_colour = "#4f5049"

        vbox = Wx::BoxSizer.new(Wx::VERTICAL)

        midPan = Wx::Panel.new(panel)
        midPan.background_colour = "#ededed"

        vbox.add midPan, Wx::ID_ANY, Wx::EXPAND | Wx::ALL, 20
        panel.set_sizer vbox
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Yukarıdaki kodda içteki paneli boyutlandırıcıya eklerken etrafında biraz boşluk bıraktık. 

        vbox.add midPan, Wx::ID_ANY, Wx::EXPAND | Wx::ALL, 20

    midPan panelini (bu arada isimi keşke mid_pan yazsam daha Ruby olurdu), vbox boyutlandırıcısına eklerken 20 piksel sınır belirledik. Sınırın her tarafta olması da flag değerine girilen Wx::ALL biti tarafından belirtiliyor.

    Eğer Wx::EXPAND bayrağını kullanırsak widget'imiz boyutlandırıcı içinde yayılabildiği kadar her yöne yayılır. Son olarak widget'imizin yaslanmasını da bayraklar ile belirleyebiliriz.

    • Wx::ALIGN_LEFT
    • Wx::ALIGN_RIGHT
    • Wx::ALIGN_TOP
    • Wx::ALIGN_BOTTOM
    • Wx::ALIGN_CENTER_VERTICAL
    • Wx::ALIGN_CENTER_HORIZONTAL
    • Wx::ALIGN_CENTER

    Çalıştıralım




    Sınıfına git örneği

    Önümüzdeki örnekte farklı birçok yöntem göreceğiz. 

    goto_class.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        #set_size [350, 300]
        set_title "Sınıfına Git"
        centre

        panel = Wx::Panel.new(self)

        font = Wx::SystemSettings.get_font(Wx::SYS_SYSTEM_FONT)
        font.point_size = 9

        vbox = Wx::BoxSizer.new(Wx::VERTICAL)

        hbox1 = Wx::BoxSizer.new(Wx::HORIZONTAL)
        st1 = Wx::StaticText.new(panel, label: 'Class Adı :')
        st1.font = font
        hbox1.add st1, 0, Wx::RIGHT, 8
        tc = Wx::TextCtrl.new(panel)
        hbox1.add tc, 1
        vbox.add hbox1, 0, Wx::EXPAND|Wx::LEFT|Wx::RIGHT|Wx::TOP, 10

        vbox.add -1, 10

        hbox2 = Wx::BoxSizer.new(Wx::HORIZONTAL)
        st2 = Wx::StaticText.new(panel, label: 'Eşleşen Sınıflar :')
        st2.set_font font
        hbox2.add st2
        vbox.add hbox2, 0, Wx::LEFT | Wx::TOP, 10

        vbox.add -1, 10

        hbox3 = Wx::BoxSizer.new(Wx::HORIZONTAL)
        tc2 = Wx::TextCtrl.new(panel, style: Wx::TE_MULTILINE)
        hbox3.add tc2, 1, Wx::EXPAND
        vbox.add hbox3, 1, Wx::LEFT|Wx::RIGHT|Wx::EXPAND, 10

        vbox.add -1, 25

        hbox4 = Wx::BoxSizer.new(Wx::HORIZONTAL)
        cb1 = Wx::CheckBox.new(panel, label: 'Büyük/Küçük Duyarlı')
        cb1.font = font
        hbox4.add cb1
        cb2 = Wx::CheckBox.new(panel, label: 'İçiçe Sınıflar')
        cb2.font = font
        hbox4.add cb2, 0, Wx::LEFT, 10
        cb3 = Wx::CheckBox.new(panel, label: 'Non-Project Sınıflar')
        cb3.font = font
        hbox4.add cb3, 0, Wx::LEFT, 10
        vbox.add hbox4, 0, Wx::LEFT, 10

        vbox.add -1, 25

        hbox5 = Wx::BoxSizer.new(Wx::HORIZONTAL)
        btn1 = Wx::Button.new(panel, label: 'Tamam', size: [70, 30])
        hbox5.add btn1
        btn2 = Wx::Button.new(panel, label: 'Kapat', size: [70, 30])
        hbox5.add btn2, 0, Wx::LEFT|Wx::BOTTOM, 5
        vbox.add hbox5, 0, Wx::ALIGN_RIGHT|Wx::RIGHT, 10

        panel.set_sizer vbox
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Gördüğünüz üzere yerleşimleri kombinasyon yaptık. Bir dikey boyutlandırıcı, 5 yatay boyutlandırıcı var.

        font = Wx::SystemSettings.get_font(Wx::SYS_SYSTEM_FONT)
        font.point_size = 9

    Sistemden SYS_SYSTEM_FONT yazı tipini alıp boyutunu 9 olarak ayarlayıp kontrollerimizde kullanıyoruz. Wx::SystemFont dökümanında sistemden alınan font isimleri var. 

        vbox.add hbox1, 0, Wx::EXPAND|Wx::LEFT|Wx::RIGHT|Wx::TOP, 10

        vbox.add -1, 10

    Boyutlandırıcı içine eleman eklerken kenarlarında istediğimiz boşlukları bayrak bitleri ve border argümanı ile ayarlayabiliyorduk. Yukarıdaki satırda hbox1 eklenirken solu, sağı ve üstünde 10 piksel boşluk veriliyor.

    Bir diğer yöntem boyutlandırıcı nesnenin add metodu ile direk olarak eklenen elemanlar arasına boşluk eklemek. Burada vbox.add -1, 10 satırı genişliği -1 yüksekliği 10 piksel bir boşluk ekliyor, yani dikey olarak bir sonra eklenecek elemanla araya 10 piksel boşluk ekliyor. 

        vbox.add hbox5, 0, Wx::ALIGN_RIGHT|Wx::RIGHT, 10

    Bu satır hbox5 içindeki iki butonu yerleşime eklerken sağa yaslayarak yerleştiriyor. Burada 3 konu önemli, orantı bilgisi (proportion), yaslanma biti ve Wx::EXPAND. Proportion sıfır olmalıdır ki butonlar pencereyle beraber büyümeye kalkmasın ne kadarsa o kadar kalsın. Wx::EXPAND kullanmamalıyız, butonlar yalnızca kendilerine ayrılan alanı kaplasın. Son olarak da Wx::ALIGN_RIGHT bayrağını da kullanmalıyız ki butonlar pencerenin sağ tarafına yerleştirilsin. 

    Çalıştıralım.




    Wx::GridSizer

    Wx::GridSizer kontrolleri iki boyutlu tablo şeklinde yerleştirir. Tablodaki her hücre eşit boyutlardadır. 

    Wx:GridSizer.new(rows, cols, vgap, hgap)
     

    Üretici metodda satır sayısı, sütun sayısı, hücreler arası dikeydeki boşluk (satırlar arası) ve hücreler arası yataydaki boşluk değerlerini argüman olarak veriyoruz. 

    Örneğimizde bir hesap makinesinin görselini hazırlıyoruz.

    calculator.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 300]
        set_title "Hesap Makinesi"
        centre

        menubar = Wx::MenuBar.new
        file_menu = Wx::Menu.new
        menubar.append file_menu, '&File'
        set_menu_bar menubar

        vbox = Wx::BoxSizer.new(Wx::VERTICAL)
        display = Wx::TextCtrl.new(self, style: Wx::TE_RIGHT)
        vbox.add display, 0, Wx::EXPAND|Wx::TOP|Wx::BOTTOM, 4
        gs = Wx::GridSizer.new(5, 4, 5, 5)

        gs.add Wx::Button.new(self, label: 'Cls'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: 'Bck'), 0, Wx::EXPAND
        gs.add Wx::StaticText.new, 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: 'Close'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '7'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '8'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '9'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '/'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '4'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '5'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '6'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '*'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '1'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '2'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '3'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '-'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '0'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '.'), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '='), 0, Wx::EXPAND
        gs.add Wx::Button.new(self, label: '+'), 0, Wx::EXPAND

        vbox.add gs, 1, Wx::EXPAND

        set_sizer vbox
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Bck ve Close butonları arasındaki grid hücresini nasıl boş bıraktığımıza dikkat edin , sadece boş bir Wx::StaticText nesnesi koyduk. 

    Kontroller grid boyutlandırıcıya eklendikleri sırayla görüntüde yerlerini alıyorlar. Soldan sağa, yukarıdan aşağı yönde ilerleniyor. Satır bittikçe alt satırın başına geçiliyor.

     




    Wx::FlexGridSizer

    Bu boyutlandırıcı Wx::GridSizer'a çok benzer. Bu da kontrolleri 2 boyutlu bir tabloda yerleştirir. Ama hücre boyutlarına biraz esneklik getirir. FlexGridSizer'da aynı satırdaki tüm hücreler aynı yüksekliktedir ve aynı sütundaki tüm hücreler de aynı genişliktedir. Ancak satırların yükseklikleri ve sütunların genişlikleri değişebilir. 

    Wx::FlexGridSizer.new(rows, cols, vgap, hgap)


    Yeni nesne üretme metodu aynı argümanları alıyor. 

    Geliştiriciler sıklıkla verileri göstermek ve değiştirmek amaçlı diyalog pencereleri hazırlarlar. Wx::FlexGridSizer boyutlandırıcı bu diyalogların yerleşimini hazırlarken çok işe yarar.  

    review.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 250]
        set_title "Eleştiri"
        centre

        panel = Wx::Panel.new self

        hbox = Wx::BoxSizer.new Wx::HORIZONTAL

        fgs = Wx::FlexGridSizer.new 3, 2, 9, 25

        başlık = Wx::StaticText.new panel, label: "Başlık :"
        yazar = Wx::StaticText.new panel, label: "Yazar :"
        eleştiri = Wx::StaticText.new panel, label: "Eleştiri :"

        tc1 = Wx::TextCtrl.new panel
        tc2 = Wx::TextCtrl.new panel
        tc3 = Wx::TextCtrl.new panel, style: Wx::TE_MULTILINE

        fgs.add başlık
        fgs.add tc1, 1, Wx::EXPAND
        fgs.add yazar
        fgs.add tc2, 1, Wx::EXPAND
        fgs.add eleştiri, 1, Wx::EXPAND
        fgs.add tc3, 1, Wx::EXPAND

        fgs.add_growable_row 2
        fgs.add_growable_col 1

        hbox.add fgs, 1, Wx::ALL|Wx::EXPAND, 15
        panel.set_sizer hbox
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Yukarıdaki örnekte eleştiri giriş diyaloğunu FlexGridSizer ile oluşturduk 

        hbox = Wx::BoxSizer.new Wx::HORIZONTAL
    ....
        hbox.add fgs, 1, Wx::ALL|Wx::EXPAND, 15

    Yatay boyutlandırıcıdan kenarlara 15 piksel boşluk bırakıyoruz. 

        fgs = Wx::FlexGridSizer.new 3, 2, 9, 25

    3 satır , 2 sütundan oluşuyor. Satırlar arasında 9 piksel , sütunlar arasında 25 piksel boşluk var. 

        fgs.add başlık
    ....
        fgs.add tc3, 1, Wx::EXPAND

    Kontrolleri hücrelere aynı GridSizer gibi sırayla ekliyoruz. 

        fgs.add_growable_row 2
        fgs.add_growable_col 1

    3. satır (index=2) ve ikinci sütunu genişleyebilir olarak belirtiyoruz. Bunları yapmazsak kontroller birbirine ayak uydurur ama boyutlandırıcı genişlemediği için pencereyle beraber büyümezler. Ayrıca büyümesini istediğimiz kontrolleri boyutlandırıcıya eklerken Wx::EXPAND bayrağını eklemeyi unutmayın. 




    Wx::GridBagSizer

    GridBagSizer Wx kütüphanesinin en esnek boyutlandırıcısı. 

    Bu boyutlandırıcı elemanların ayrıca pozisyonlanmasına imkan tanır. Elemanlar bir'den fazla satır veya sütuna da yayılabilirler. 

        Wx::GridBagSizer.new  vgap, hgap

    Nesne oluşturucusu çok basittir. Argümanlar satırlar arası dikey boşluk ve sütunlar arası yatay boşluk değeri. Kontrolleri boyutlandırıcıya add metodu ile ekleriz. 

    add window, pos, span = Wx::DEFAULT_SPAN, flag = 0, border = 0, userData = nil

    window boyutlandırıcıya eklenecek olan widget, pos satır-sütun olarak pozisyon değeri (Wx::GBPosition nesnesi), span kaç satır , kaç sütun işgal edeceği (Wx::GBSpan nesnesi), bayrafklar ve kenar boşluğu. 




    İsim değiştir diyalog örneği

    İlk örneğimizde isim değiştirmek için bir diyalog penceresi çizelim. Sadece bir Wx::StaticText, bir Wx::TextCtrl ve iki Wx::Button widget var. 

    rename.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 150]
        set_title "İsim Değiştir"
        centre

        panel = Wx::Panel.new self
        sizer = Wx::GridBagSizer.new 4, 4

        text = Wx::StaticText.new panel, label: "Yeni isim :"
        sizer.add text, Wx::GBPosition.new(0, 0), Wx::DEFAULT_SPAN,
                Wx::TOP|Wx::LEFT|Wx::BOTTOM, 5

        tc = Wx::TextCtrl.new panel
        sizer.add tc, Wx::GBPosition.new(1, 0), Wx::GBSpan.new(1, 4),
                Wx::EXPAND|Wx::LEFT|Wx::RIGHT, 5

        button_ok = Wx::Button.new panel, label: "Tamam", size: [90, 28]
        button_close = Wx::Button.new panel, label: "Kapat", size: [90, 28]
        sizer.add button_ok, Wx::GBPosition.new(3, 2)
        sizer.add button_close, Wx::GBPosition.new(3, 3), Wx::DEFAULT_SPAN,
                Wx::RIGHT|Wx::BOTTOM, 10

        sizer.add_growable_col 1
        sizer.add_growable_row 2
        panel.set_sizer sizer
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }

    Pencereye büyük bir grid tablosu olarak bakmalıyız.

        text = Wx::StaticText.new panel, label: "Yeni isim :"
        sizer.add text, Wx::GBPosition.new(0, 0), Wx::DEFAULT_SPAN,
                Wx::TOP|Wx::LEFT|Wx::BOTTOM, 5

    "Yeni isim :" yazısı sol üst köşeye yerleştiriliyor, pozisyonu (0. 0). Solunda , üstünde ve altında da biraz boşluk bırakıyoruz.

        tc = Wx::TextCtrl.new panel
        sizer.add tc, Wx::GBPosition.new(1, 0), Wx::GBSpan.new(1, 4),
                Wx::EXPAND|Wx::LEFT|Wx::RIGHT, 5

    Wx::TextCtrl nesnesi ikinci satırın ilk sütununa yerleştiriliyor (1, 0). Yayılma olarak 1 satır ve 4 sütuna yayılıyor (1, 4). Ve iki yanında 5 piksel boşluk bırakıyoruz.

        sizer.add button_ok, Wx::GBPosition.new(3, 2)
        sizer.add button_close, Wx::GBPosition.new(3, 3), Wx::DEFAULT_SPAN,
                Wx::RIGHT|Wx::BOTTOM, 10

    Butonlarımızı 4. satıra yerleştiriyoruz. 3. satırı boş bırakıyoruz TextCtrl ve butonlar arasında mesafe kalsınistiyoruz. "Tamam" butonunu 3. sütuna (3, 2) ve "Kapat" butonunu da 4. sütuna (3, 3) yerleştiriyoruz. Sadece "Kapat" butonuna sınır boşluğu veriliyor ama altında olan boşluk diğer butona da etki edecektir. 

        sizer.add_growable_col 1
        sizer.add_growable_row 2

    Son yapacağımız şey , yerleşimin boyut değişimlerine tepkisi. 2. sütun ve 3. satırı genişleyebilir yaptık. Bu iki satırı yoruma atın da ne olacağını görün. 



    Yeni sınıf diyaloğu örneği

    Şimdiki örneğimizde JDeveloper'da bulunan yeni Java sınıf tanımlama diyaloğunu taklit edeceğiz. 

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 450]
        set_title "Create Java Class"
        centre

        panel = Wx::Panel.new self
        sizer = Wx::GridBagSizer.new 8, 5

        text1 = Wx::StaticText.new panel, label: "Java Class"
        sizer.add text1, Wx::GBPosition.new(0, 0), Wx::DEFAULT_SPAN,
              Wx::TOP|Wx::LEFT|Wx::BOTTOM, 15

        icon = Wx::StaticBitmap.new panel, label: Wx::Bitmap.new('exec.jpg')
        sizer.add icon, Wx::GBPosition.new(0, 4), Wx::DEFAULT_SPAN,
              Wx::TOP|Wx::RIGHT|Wx::ALIGN_RIGHT, 5

        line = Wx::StaticLine.new panel
        sizer.add line, Wx::GBPosition.new(1, 0), Wx::GBSpan.new(1, 5),
              Wx::EXPAND|Wx::BOTTOM, 10

        text2 = Wx::StaticText.new panel, label: "Name"
        sizer.add text2, Wx::GBPosition.new(2, 0), Wx::DEFAULT_SPAN,
              Wx::LEFT, 10

        tc1 = Wx::TextCtrl.new panel
        sizer.add tc1, Wx::GBPosition.new(2, 1), Wx::GBSpan.new(1, 3),
              Wx::TOP|Wx::EXPAND

        text3 = Wx::StaticText.new panel, label: "Package"
        sizer.add text3, Wx::GBPosition.new(3, 0), Wx::DEFAULT_SPAN,
              Wx::LEFT|Wx::TOP, 10

        tc2 = Wx::TextCtrl.new panel
        sizer.add tc2, Wx::GBPosition.new(3, 1), Wx::GBSpan.new(1, 3),
              Wx::TOP|Wx::EXPAND, 5

        button1 = Wx::Button.new panel, label: "Browse..."
        sizer.add button1, Wx::GBPosition.new(3, 4), Wx::DEFAULT_SPAN,
              Wx::TOP|Wx::RIGHT, 5

        text4 = Wx::StaticText.new panel, label: "Extends"
        sizer.add text4, Wx::GBPosition.new(4, 0), Wx::DEFAULT_SPAN,
              Wx::TOP|Wx::LEFT, 10

        combo = Wx::ComboBox.new panel
        sizer.add combo, Wx::GBPosition.new(4, 1), Wx::GBSpan.new(1, 3),
              Wx::TOP|Wx::EXPAND, 5

        button2 = Wx::Button.new panel, label: "Browse..."
        sizer.add button2, Wx::GBPosition.new(4, 4), Wx::DEFAULT_SPAN,
              Wx::TOP|Wx::RIGHT, 5

        sb = Wx::StaticBox.new panel, label: "Optional Attributes"

        boxsizer = Wx::StaticBoxSizer.new sb, Wx::VERTICAL
        boxsizer.add Wx::CheckBox.new(panel, label: "Public"), 0,
              Wx::LEFT|Wx::TOP, 5
        boxsizer.add Wx::CheckBox.new(panel, label: "Generate Default Constructor"), 0,
              Wx::LEFT, 5
        boxsizer.add Wx::CheckBox.new(panel, label: "Generate Main Method"), 0,
              Wx::LEFT|Wx::BOTTOM, 5
        sizer.add boxsizer, Wx::GBPosition.new(5, 0), Wx::GBSpan.new(1, 5),
              Wx::EXPAND|Wx::TOP|Wx::LEFT|Wx::RIGHT , 10

        button3 = Wx::Button.new panel, label: 'Help'
        sizer.add button3, Wx::GBPosition.new(7, 0), Wx::DEFAULT_SPAN,
              Wx::LEFT, 10

        button4 = Wx::Button.new panel, label: "Ok"
        sizer.add button4, Wx::GBPosition.new(7, 3)

        button5 = Wx::Button.new panel, label: "Cancel"
        sizer.add button5, Wx::GBPosition.new(7, 4), Wx::GBSpan.new(1, 1),
              Wx::BOTTOM|Wx::RIGHT, 10

        sizer.add_growable_col 2
        sizer.add_growable_row 6

        panel.set_sizer sizer
        sizer.fit self
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Bu daha karmaşık bir yerleşim , hem GridBoxSizer hem de StaticBoxSizer kullanıyoruz. 

        line = Wx::StaticLine.new panel
        sizer.add line, Wx::GBPosition.new(1, 0), Wx::GBSpan.new(1, 5),
              Wx::EXPAND|Wx::BOTTOM, 10

    Bu yerleşimde iki grup elemanı birbirinden ayıran çizgi. 2. satıra yatay olarak tüm satırı kaplayacak şekilde yerleştiriyoruz. 

        icon = Wx::StaticBitmap.new panel, label: Wx::Bitmap.new('exec.jpg')
        sizer.add icon, Wx::GBPosition.new(0, 4), Wx::DEFAULT_SPAN,
              Wx::TOP|Wx::RIGHT|Wx::ALIGN_RIGHT, 5

    İlk satıra sağa yanaşmış olarak bir resimi StaticBitmap nesnesi olarak ekliyoruz.

        sb = Wx::StaticBox.new panel, label: "Optional Attributes"

        boxsizer = Wx::StaticBoxSizer.new sb, Wx::VERTICAL

    StaticBox üzerinde bir etiket yazısı olan panel gibi düşünülebilir. Burada bir şey dikkatimi çekti , sb içine boxsizer'ı ekliyoruz ama daha sonra yerleşime kapsayıcı olan sb'yi değil bozsizer'ı ekliyoruz. Biraz araştırdım, aslında birinci satıra gerek olamadan sadece

    boxsizer = Wx::StaticBoxSizer.new Wx::VERTICAL, panel, "Optional Attributes"

    şeklinde yazarak boxsizer nesnesine zaten başlık ekleyebiliyoruz. Bu daha mantıklı ve kısa.


    Çalıştı ama şu GridBagSizer#add metodunun argümanlarında bulunan Wx::GBPosition ve Wx::GBSpan nesnesi üretmelerine gıcık oldum, her seferinde bunları yazmak çok gıcık, araştırdım bir kısaltmasını da bulamadım. Oturdum kendime bir yardımcı kod dosyası hazırladım.

    my_wx_helper.rb

    class Wx::GridBagSizer
      wx_add = instance_method :add
      wx_redefine_method :add do |*args|
        if args[1].class == Array && args[1].length == 2
          args[1] = Wx::GBPosition.new args[1][0], args[1][1]
        end
        if args[2].class == Array && args[2].length == 2
          args[2] = Wx::GBSpan.new args[2][0], args[2][1]
        end
        wx_add.bind(self).call(*args)
      end
    end

    Bu kodu programlarıma dahil edersem pos ve span değerlerini array içinde verebiliriz. Kodumu bu yardımcı dosyamı kullanacak şekilde değiştirince birazcık sadeleşti.

    require "wx"
    require "./my_wx_helper"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [350, 450]
        set_title "Create Java Class"
        centre

        panel = Wx::Panel.new self
        sizer = Wx::GridBagSizer.new 8, 5

        text1 = Wx::StaticText.new panel, label: "Java Class"
        sizer.add text1, [0, 0], [1, 1],
              Wx::TOP|Wx::LEFT|Wx::BOTTOM, 15

        icon = Wx::StaticBitmap.new panel, label: Wx::Bitmap.new('exec.jpg')
        sizer.add icon, [0, 4], [1, 1],
              Wx::TOP|Wx::RIGHT|Wx::ALIGN_RIGHT, 5

        line = Wx::StaticLine.new panel
        sizer.add line, [1, 0], [1, 5], Wx::EXPAND|Wx::BOTTOM, 10

        text2 = Wx::StaticText.new panel, label: "Name"
        sizer.add text2, [2, 0], [1, 1], Wx::LEFT, 10

        tc1 = Wx::TextCtrl.new panel
        sizer.add tc1, [2, 1], [1, 3], Wx::TOP|Wx::EXPAND

        text3 = Wx::StaticText.new panel, label: "Package"
        sizer.add text3, [3, 0], [1, 1], Wx::LEFT|Wx::TOP, 10

        tc2 = Wx::TextCtrl.new panel
        sizer.add tc2, [3, 1], [1, 3], Wx::TOP|Wx::EXPAND, 5

        button1 = Wx::Button.new panel, label: "Browse..."
        sizer.add button1, [3, 4], [1, 1], Wx::TOP|Wx::RIGHT, 5

        text4 = Wx::StaticText.new panel, label: "Extends"
        sizer.add text4, [4, 0], [1, 1], Wx::TOP|Wx::LEFT, 10

        combo = Wx::ComboBox.new panel
        sizer.add combo, [4, 1], [1, 3], Wx::TOP|Wx::EXPAND, 5

        button2 = Wx::Button.new panel, label: "Browse..."
        sizer.add button2, [4, 4], [1, 1], Wx::TOP|Wx::RIGHT, 5

        boxsizer = Wx::StaticBoxSizer.new Wx::VERTICAL, panel, "Optional Attributes"
        boxsizer.add Wx::CheckBox.new(panel, label: "Public"), 0,
              Wx::LEFT|Wx::TOP, 5
        boxsizer.add Wx::CheckBox.new(panel, label: "Generate Default Constructor"), 0,
              Wx::LEFT, 5
        boxsizer.add Wx::CheckBox.new(panel, label: "Generate Main Method"), 0,
              Wx::LEFT|Wx::BOTTOM, 5
        sizer.add boxsizer, [5, 0], [1, 5],
              Wx::EXPAND|Wx::TOP|Wx::LEFT|Wx::RIGHT , 10

        button3 = Wx::Button.new panel, label: 'Help'
        sizer.add button3, [7, 0], [1, 1], Wx::LEFT, 10

        button4 = Wx::Button.new panel, label: "Ok"
        sizer.add button4, [7, 3]

        button5 = Wx::Button.new panel, label: "Cancel"
        sizer.add button5, [7, 4], [1, 1], Wx::BOTTOM|Wx::RIGHT, 10

        sizer.add_growable_col 2
        sizer.add_growable_row 6

        panel.set_sizer sizer
        sizer.fit self
      end
    end

    Wx::App.run {
      Örnek.new(nil).show
    }


    Yerleşimler üzerine yazacaklarım bu kadar, sonraki sayfada olay işlemelere gireceğiz inşallah.

    Şimdilik kalın sağlıcakla..







    Hiç yorum yok:

    Yorum Gönder