22 Mart 2025 Cumartesi

WxRuby3 ile Masaüstü Uygulama Geliştirmek 4

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

     Selam WxRuby ile masa üstü uygulamalara gelişmiş widget'larla devam ediyoruz.


    WxRuby Gelişmiş Widget'lar

    Bu bölümde Wx::ListBox, Wx::HTML::HtmlWindow ve Wx::ListCtrl widget'larını göreceğiz. 

    Wx kütüphanesinde gelişmiş elemanlar var, tree widget, bir HTML window, bir grid widget, bir listbox widget, bir list widget, veya gelişmiş stillendirme özelliklerine sahip editor widget gibi.



    Wx::ListBox

    ListBox nesnesi bir liste olarak verilen değerleri göstermek için kullanılır. ListBox nesnesi iki farklı şekilde üretilebilir, tek seçimli ya da çoklu seçimli olarak. Default olan tek seçimli olandır.

    ListBox nesnesinin iki önemli olayı vardır. evt_listbox olayı listeden bir eleman seçince veya seçim değiştirilince tetiklenir. evt_listbox_dclick olayı ise listbox'a çift tıklanınca oluşur. Elemanların (seçeneklerin) index numarası sıfırdan başlar. Kaydırma çubukları eğer gerekirse otomatik gösterilir.

    listbox.rb

    require "wx"

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

        panel = Wx::Panel.new self
        hbox = Wx::BoxSizer.new Wx::HORIZONTAL

        @listbox = Wx::ListBox.new panel
        hbox.add @listbox, 1, Wx::EXPAND | Wx::ALL, 20

        btn_panel = Wx::Panel.new panel
        vbox = Wx::BoxSizer.new Wx::VERTICAL
        new_btn = Wx::Button.new btn_panel, Wx::ID_ANY, 'Yeni', size:[90, 30]
        ren_btn = Wx::Button.new btn_panel, Wx::ID_ANY, 'Değiştir', size:[90, 30]
        del_btn = Wx::Button.new btn_panel, Wx::ID_ANY, 'Sil', size:[90, 30]
        clr_btn = Wx::Button.new btn_panel, Wx::ID_ANY, 'Temizle', size:[90, 30]

        evt_button new_btn, :new_item
        evt_button ren_btn, :on_rename
        evt_button del_btn, :on_delete
        evt_button clr_btn, :on_clear
        evt_listbox_dclick @listbox, :on_rename

        vbox.add 0, 20
        vbox.add new_btn
        vbox.add ren_btn, 0, Wx::TOP, 5
        vbox.add del_btn, 0, Wx::TOP, 5
        vbox.add clr_btn, 0, Wx::TOP, 5

        btn_panel.sizer = vbox
        hbox.add btn_panel, 1, Wx::EXPAND | Wx::RIGHT, 20
        panel.sizer = hbox
      end
      def new_item(e)
        text = Wx.get_text_from_user 'Yeni eleman değeri giriniz',
         'Yeni Eleman Diyaloğu'
       
        unless text == ""
          @listbox.append text
        end
      end
      def on_rename(e)
        sel = @listbox.selection
        text = @listbox.string sel
        renamed = Wx.get_text_from_user 'Eleman yeni değeri giriniz',
            'Değiştirme Diyaloğu', text

        if renamed != ''
          @listbox.delete sel
          item_id = @listbox.insert renamed, sel
          @listbox.selection = item_id
        end
      end
      def on_delete(e)
        sel = @listbox.selection
        if sel != -1
          @listbox.delete sel
        end
      end
      def on_clear(e)
        @listbox.clear
      end
    end

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


    Örnekte bir ListBox nesnesine yeni eleman eklenmesi, değiştirilmesi, silinmesi gösteriliyor. 

        @listbox = Wx::ListBox.new panel
        hbox.add @listbox, 1, Wx::EXPAND | Wx::ALL, 20

    ListBox nesnesi yatay boyutlandırıcıda dikey olarak yayılmış, her tarafında 20 piksel boşluk olarak yerleştiriliyor. 

        evt_listbox_dclick @listbox, :on_rename

    LisBox elemanına çift tıklanınca da "Değiştir" butonu ile aynı metod çağrılıp değer değiştirme diyaloğu açılıyor.

      def new_item(e)
        text = Wx.get_text_from_user 'Yeni eleman değeri giriniz',
         'Yeni Eleman Diyaloğu'
       
        unless text == ""
          @listbox.append text
        end
      end

    @listbox.append text ile listeye yeni satır ekleniyor. Wx.get_text_from_user sınıf metodu birçok hazır Wx diyaloğundan biri. 

      def on_rename(e)
        sel = @listbox.selection
        text = @listbox.string sel
        renamed = Wx.get_text_from_user 'Eleman yeni değeri giriniz',
            'Değiştirme Diyaloğu', text

        if renamed != ''
          @listbox.delete sel
          item_id = @listbox.insert renamed, sel
          @listbox.selection = item_id
        end
      end

    En karmaşık olan metod on_rename metodu. Önce @listbox.selection ile listeden seçili değerin index değerini alıyoruz. Eğer açılan diyalogdan bir değer girilirse önce eskisi silinip , sonra yeni değer @listbox.insert metodu ile aynı noktaya enjekte ediliyor. 

      def on_delete(e)
        sel = @listbox.selection
        if sel != -1
          @listbox.delete sel
        end
      end

    Kod gösteriyor ki değer seçilmemişse selection değeri -1 geliyor ve delete metodu verilen index'teki değeri siliyor. 




    Wx::HTML::HtmlWindow

    Bu widget HTML sayfaları gösterir. Tam teşekküllü bir tarayıcı değildir. 

    Örneğin aşağıdaki program bazı temel istatistikleri verir.

    page.html

    <!DOCTYPE html>
    <html>
    <body bgcolor="#8e8e95">
      <table cellspacing="5" border="0" width="250">
        <tr width="200" align="left">
        <td bgcolor="#e7e7e7">&nbsp;&nbsp;En büyük</td>
        <td bgcolor="#aaaaaa">&nbsp;&nbsp;<b>9000</b></td>
        </tr>
        <tr align="left">
        <td bgcolor="#e7e7e7">&nbsp;&nbsp;Ortalama</td>
        <td bgcolor="#aaaaaa">&nbsp;&nbsp;<b>6076</b></td>
        </tr>
        <tr align="left">
        <td bgcolor="#e7e7e7">&nbsp;&nbsp;En küçük</td>
        <td bgcolor="#aaaaaa">&nbsp;&nbsp;<b>3800</b></td>
        </tr>
        <tr align="left">
        <td bgcolor="#e7e7e7">&nbsp;&nbsp;Ortanca</td>
        <td bgcolor="#aaaaaa">&nbsp;&nbsp;<b>6000</b></td>
        </tr>
        <tr align="left">
        <td bgcolor="#e7e7e7">&nbsp;&nbsp;Standart Sapma</td>
        <td bgcolor="#aaaaaa">&nbsp;&nbsp;<b>6076</b></td>
        </tr>
      </table>
    </body>
    </html>

    Bu gösterilecek olan HTML sayfa ve kodu.

    htmlwin.rb

    require "wx"

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

        panel = Wx::Panel.new self
        hbox = Wx::BoxSizer.new Wx::HORIZONTAL
        vbox = Wx::BoxSizer.new Wx::VERTICAL

        htmlwin = Wx::HTML::HtmlWindow.new panel, Wx::ID_ANY, style: Wx::NO_BORDER
        htmlwin.set_standard_fonts
        htmlwin.load_file "page.html"

        vbox.add -1, 10, 0
        vbox.add htmlwin, 1, Wx::EXPAND | Wx::ALL, 9

        btn_ok = Wx::Button.new panel, Wx::ID_ANY, 'Ok'

        evt_button(btn_ok) { close }

        hbox.add 100, -1, 1, Wx::EXPAND
        hbox.add btn_ok, 0, Wx::TOP | Wx::BOTTOM | Wx::RIGHT, 10
        vbox.add hbox, 0, Wx::EXPAND

        panel.sizer = vbox
      end
    end

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

    Programı çalıştırınca Türkçe karakterlerin çıkmadığını gördüm. Sadece page.html dosyası kodlamasını notepad++ kullanarak ANSI'ye çevirince düzgün göründü.


    Fakat ben ANSI'ye çevirmeden Türkçe karakterleri görmek istiyorum. Bu yüzden işi Ruby'ye bıraktım, dosya okumasını o yapsın.

        #htmlwin.load_file "page.html"
        htmlwin.set_page(File.read("page.html"))

    Bu şekilde set_page metoduna içeriği string olarak verebiliyoruz, dosyayı okumayı Wx kütüphanesine değil Ruby'ye yaptırıyoruz. 




    Help penceresi

    Wx::HTML::HtmlWindow nesnesini uygulamamızda yardım penceresi gösterirken de kullanabiliriz. Bağımsız bir pencere kullanabileceğimiz gibi uygulamanın bir parçası olan bir pencere de kullanabiliriz. Aşağıdaki örnek betik ikinci tekniği gösteriyor.

    helpwindow.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [450, 350]
        set_title "Yardım Penceresi"
        centre

        toolbar = create_tool_bar
        toolbar.add_tool 1, 'Çıkış', Wx::ArtProvider.get_bitmap(Wx::ART_QUIT)
        toolbar.add_tool 2, 'Yardım',Wx::ArtProvider.get_bitmap(Wx::ART_HELP)
        toolbar.realize

        @splitter = Wx::SplitterWindow.new self
        @panel_left = Wx::Panel.new @splitter, Wx::ID_ANY, style: Wx::BORDER_SUNKEN
        @panel_right = Wx::Panel.new @splitter
        vbox2 = Wx::BoxSizer.new Wx::VERTICAL
        header = Wx::Panel.new @panel_right, Wx::ID_ANY

        header.background_colour = '#6f6a59'
        header.foreground_colour = 'white'

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

        st = Wx::StaticText.new header, Wx::ID_ANY, 'Yardım'
        font = st.font
        font.family = Wx::FONTFAMILY_ROMAN
        font.point_size = 11
        st.font = font

        hbox.add st, 1, Wx::TOP | Wx::BOTTOM | Wx::LEFT, 8

        close_btn = Wx::BitmapButton.new header, Wx::ID_ANY,
            Wx::ArtProvider.get_bitmap(Wx::ART_CLOSE), style: Wx::NO_BORDER
        close_btn.background_colour = '#6f6a59'

        hbox.add close_btn, 0, Wx::TOP | Wx::BOTTOM, 8
        header.sizer = hbox

        vbox2.add header, 0, Wx::EXPAND

        @help_win = Wx::HTML::HtmlWindow.new @panel_right, style:Wx::NO_BORDER
        @help_win.set_page(File.read("help.html"))

        vbox2.add @help_win, 1, Wx::EXPAND

        @panel_right.sizer = vbox2
        @panel_left.set_focus

        @splitter.split_vertically @panel_left, @panel_right
        @splitter.unsplit

        evt_button close_btn, :close_help
        evt_tool(1) { close }
        evt_tool 2, :on_help

        @panel_left.evt_key_down { |e| on_key_pressed e }

        evt_html_link_clicked @help_win, :link_clicked

        create_status_bar
      end
      def close_help(e)
        @splitter.unsplit
        @panel_left.set_focus
      end
      def on_help(e)
        @splitter.split_vertically @panel_left, @panel_right
        @panel_left.set_focus
      end
      def on_key_pressed(e)
        keycode = e.key_code
        #p keycode

        if keycode == Wx::KeyCode::K_F1
          @splitter.split_vertically @panel_left, @panel_right
          @panel_left.set_focus
        end
      end
      def link_clicked(e)
        link = e.link_info.href
        if link.start_with? "http"
          p "dış link"
          system "start #{link}"
        else
          @help_win.load_page link
        end
      end
    end

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

    ve help.html

    <!DOCTYPE html>
    <html>

    <body bgcolor="#ababab">
    <h4>İçindekiler</h4>

    <ul>
    <li><a href="#temel">Uygulama Temelleri</a></li>
    <li><a href="#gelişmiş">Gelişmiş Özellikler</a></li>
    <li><a href="#sabitler">Kullanılan sabitler</a></li>
    <li><a href="#info">Nasıl Yardım Alınır</a></li>
    </ul>

    <p>
    <a name="temel">
    <h6>Uygulama Temelleri</h6>
    Uygulamamız güzel bir yugulamadır. Her işe yarar.
    Birçok özelliği vardır.
    </a>
    </p>

    <p>
    <a name="gelişmiş">
    <h6>Gelişmiş Özellikler</h6>
    Birçok gelişmiş özelliklerden en önemlileri şunlardır:
    <ul>
      <li>Veri tabanı</li>
      <li>PNG export</li>
      <li>xml import</li>
    </ul>
    </a>
    </p>

    <p>
    <a name="sabitler">
    <h6>Kullanılan sabitler</h6>
    Wx::ART_QUIT , Wx::ART_HELP , Wx::ART_CLOSE
    </a>
    </p>

    <p>
    <a name="info">
    <h6>Nasıl Yardım Alınır</h6>
    Blog <a href="https://ujk-ujk.blogspot.com/2025/03/wxruby3-ile-masaustu-
    uygulama_22.html" target="_blank">
      sayfasında</a> yorum yaparak yardım alabilirsiniz.

    </a>
    </p>

    </body>
    </html>


    Yardım penceresi başlangıçta gizli. Pencereyi araç kutusunda butona tıklayarak ya da F1 tuşuna basarak açabiliriz. Yardım penceresini kapatmak için sağ üstteki kapatma düğmesine tıklayabiliriz.

        @splitter.split_vertically @panel_left, @panel_right
        @splitter.unsplit

    Bir Splitter nesnesi içeriğinde solda ve sağda olmak üzere 2 panel yerleştiriyoruz. Sağdaki panelde yardım penceresi içeriğimiz var. unsplit metodu ile sadece soldaki panel kalıyor. 

        close_btn = Wx::BitmapButton.new header, Wx::ID_ANY,
            Wx::ArtProvider.get_bitmap(Wx::ART_CLOSE), style: Wx::NO_BORDER
        close_btn.background_colour = '#6f6a59'

    Yardım penceresi bir HtmlWindow nesnesi ve üst köşesinde bir kapatma butonu var , görselini Wx standart görsellerinden seçtik.

        @help_win = Wx::HTML::HtmlWindow.new @panel_right, style:Wx::NO_BORDER
        @help_win.set_page(File.read("help.html"))

    Yardım penceresi içeriğini help.html dosyasından okuyoruz. Önceki örnekte de gördüğümüz gibi , Türkçe karakter sorunu yaşamamak için dosyayı Ruby File.read metoduna okutup set_page metodu ile HtmlWindow nesnesine koyuyoruz. 

      @panel_left.evt_key_down { |e| on_key_pressed e }

    Fokus sol paneldeyken klavyeden tuş basılırsa on_key_pressed olay metoduna gidiyoruz, orada da F1 tuşuna basılmışsa yardım penceresinin görünmesini sağlıyoruz. evt_key_down olay metodu bir widget nesnesine bağlı çalıştığı için olay metodu yönlendirmesi blok içinde bu gösterildiği şekilde yapılabiliyor. 

        evt_html_link_clicked @help_win, :link_clicked

    .....

      def link_clicked(e)
        link = e.link_info.href
        if link.start_with? "http"
          p "dış link"
          system "start #{link}"
        else
          @help_win.load_page link
        end
      end

    Eğer yardım penceresinde bir link tıklandıysa ve harici bir adrese link ise system "start #{link}" satırı ile bağlantıyı sistemin default tarayıcısında açtırıyoruz. Aslında kullanılan sisteme göre bu satır değişik oluyor. Bu örnek Windows sistemine göre yazılmış.

          if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
            system "start #{link}"
          elsif RbConfig::CONFIG['host_os'] =~ /darwin/
            system "open #{link}"
          elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
            system "xdg-open #{link}"
          end

    Şeklinde yazılması daha doğruymuş.






    Wx::ListCtrl

    ListCtrl elemanlar listesinin grafiksel gösterimi için kullanılır. ListBox sadece bir sütun halinde elemanları gösterirken ListCtrl bir'den fazla sütun gösterebilir. Örneğin dosya yöneticisi dizinler ve içlerindeki dosyaları ListCtrl kullanarak gösterebilir. 

    Bir ListCtrl 3 değişik formatta kullanılabilir. Liste görünümü, rapor görünümü ve ikon görünümü. Bu formatları belirlemek için  Wx::LC_REPORT, Wx::LC_LIST ve Wx::LC_ICON stilleri kullanılır.


    Wx::ListCtrl stilleri

    • Wx::LC_LIST
    • Wx::LC_REPORT
    • Wx::LC_VIRTUAL
    • Wx::LC_ICON
    • Wx::LC_SMALL_ICON
    • Wx::LC_ALIGN_LEFT
    • Wx::LC_EDIT_LABELS
    • Wx::LC_NO_HEADER
    • Wx::LC_SORT_ASCENDING
    • Wx::LC_SORT_DESCENDING
    • Wx::LC_HRULES
    • Wx::LC_VRULES


    Örneğimizde ListCtrl nesnesinin temel özelliklerini kullanıyoruz.

    actresses.rb

    require "wx"

    $data = [['Jessica Alba', 'Pomona', '1981'],
    ['Sigourney Weaver', 'New York', '1949'],
      ['Angelina Jolie', 'los angeles', '1975'],
    ['Natalie Portman', 'Jerusalem', '1981'],
      ['Rachel Weiss', 'London', '1971'],
    ['Scarlett Johansson', 'New York', '1984']]

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

        hbox = Wx::BoxSizer.new Wx::HORIZONTAL
        panel = Wx::Panel.new self

        list = Wx::ListCtrl.new panel, Wx::ID_ANY, style:Wx::LC_REPORT
        list.insert_column 0, 'isim', Wx::LIST_FORMAT_LEFT, 130
        list.insert_column 1, 'yer', Wx::LIST_FORMAT_LEFT, 90
        list.insert_column 2, 'yıl', Wx::LIST_FORMAT_RIGHT, 90

        idx = 0

        $data.each do |i|
            list.insert_item idx, i[0]
            list.set_item idx, 1, i[1]
            list.set_item idx, 2, i[2]
            idx += 1
        end

        hbox.add list, 1, Wx::EXPAND
        panel.sizer = hbox
      end
    end

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


    Kodumuz aktristlerin bir listesini ListCtrl içinde gösteriyor.

        list = Wx::ListCtrl.new panel, Wx::ID_ANY, style:Wx::LC_REPORT

    ListCtrl nesnemizi rapor formatında üretiyoruz. 

        list.insert_column 0, 'isim', Wx::LIST_FORMAT_LEFT, 130
        list.insert_column 1, 'yer', Wx::LIST_FORMAT_LEFT, 90
        list.insert_column 2, 'yıl', Wx::LIST_FORMAT_RIGHT, 90

    3 sütunumuzun başlıkları, stilleri ve genişlikleri. 

        idx = 0

        $data.each do |i|
            list.insert_item idx, i[0]
            list.set_item idx, 1, i[1]
            list.set_item idx, 2, i[2]
            idx += 1
        end

    ListCtrl listesine değerleri iki metodla giriyoruz. Her satırın başında insert_item ile ilk hücre içeriğini yerleştiriyoruz. Daha sonra set_item ile diğer sütunlardaki hücre değerlerini giriyoruz. 






    Drag - Drop

    Masaüstü uygulamalarda drag-and-drop bir nesneyi mouse ile tutup çekerek başka bir konuma bırakmayı ifade ediyor. Bu sürükle-bırak eylemleri karmaşık işleri görsel olarak yapabilme imkanı sunar. 

    Sürükle-bırak eyleminde bir veriyi bir kaynak verisinden çekip bir hedef veri içine bırakıyoruz. O zaman ihtiyacımız olan şeyler.

    • Bir veri
    • Bir veri kaynağı
    • Bir veri hedefi

    Wx kütüphanesinde tanımlı iki veri hedefi var: Wx::TextDropTarget ve Wx::FileDropTarget.



    Wx::TextDropTarget

    TextDropTarget nesnesi ön tanımlı bir text verisi bırakma hedefi.

    dragdrop_text.rb

    require "wx"

    class MyTextDropTarget < Wx::TextDropTarget

      def initialize(object)
        super()   # parantezler mecbur
        @object = object
      end

      def on_drop_text(x, y, data)
        @object.insert_item 0, data
        return true
      end

    end

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [400, 400]
        set_title "Text sürükle-bırak"
        centre

        splitter1 = Wx::SplitterWindow.new self, style: Wx::SP_3D
        splitter2 = Wx::SplitterWindow.new splitter1, style: Wx::SP_3D

        home_dir = Dir.home
       
        @dir_wid = Wx::GenericDirCtrl.new splitter1, dir: home_dir,
                    style: Wx::DIRCTRL_DIR_ONLY

        @lc1 = Wx::ListCtrl.new splitter2, style: Wx::LC_LIST
        @lc2 = Wx::ListCtrl.new splitter2, style: Wx::LC_LIST

        dt = MyTextDropTarget.new @lc2
        @lc2.drop_target = dt

        evt_list_begin_drag @lc1, :on_drag_init

        tree = @dir_wid.tree_ctrl

        splitter2.split_horizontally @lc1, @lc2, 150
        splitter1.split_vertically @dir_wid, splitter2, 200

        evt_tree_sel_changed tree, :on_select

        on_select 0
      end

      def on_drag_init(e)
        text = @lc1.get_item_text e.index
        tdo = Wx::TextDataObject.new text
        tds = Wx::DropSource.new @lc1

        tds.data = tdo
        tds.do_drag_drop
      end

      def on_select(e)
        list = Dir.children(@dir_wid.path)

        @lc1.clear_all
        @lc2.clear_all

        list.each do |item|
          unless item.start_with? '.'
            @lc1.insert_item 0, item
          end
        end
      end
    end

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

    Örnekte bir dosya sistemini Wx::GenericDirCtrl nesnesi ile gösteriyoruz. Seçilen dizin içeriği sağ üstteki listede gösterilir. Dosya ve dizin isimleri sürüklenerek sağ alttaki listeye bırakılabilir. 

      def on_drop_text(x, y, data)
        @object.insert_item 0, data
        return true
      end

    Wx::TextDropTarget nesnesine bir şey sürüklenip bırakılınca ne yapılacağını belirtmek için on_drop_text metodu üzerine yazılır. Burada da @object bir ListCtrl nesnesi ve sürüklenip bırakılan text insert_item metodu ile listeye ekleniyor. 

        dt = MyTextDropTarget.new @lc2
        @lc2.drop_target = dt

    Bir bırakma hedefi belirleniyor ve bu hedef  drop_target= (ya da set_drop_target) metodu ile sağ alt ListCtrl nesnesi yapılıyor. 

        evt_list_begin_drag @lc1, :on_drag_init

    Sürükleme işlemi @lc1 nesnesinde başladığında on_drag_init olay işleme metodu çağrılıyor.

      def on_drag_init(e)
        text = @lc1.get_item_text e.index
        tdo = Wx::TextDataObject.new text
        tds = Wx::DropSource.new @lc1
    ....

    Sürükleme başladığında tutulmakta olan yazıyı içeren Wx::TextDataObject nesnesi oluşturuyoruz. Bırakma kaynağı olarak @lc1 listesini belirliyoruz. 

        tds.data = tdo
        tds.do_drag_drop

    Bırakma kaynağı data değeri olarak sürüklenmekte olan text'i belirtiyoruz ve do_drag_drop ile sürükle-bırak eylemini başlatıyoruz. Yani verimiz tdo , veri kaynağımız tds, veri hedefimiz dt nesneleri olarak bir sürükle-bırak tasarlıyoruz. 





    Wx::FileDropTarget

    FileDropTarget bir dosya yöneticisinden sürüklenen dosyaları bırakmak için kullanılan bir nesne.

    dragdrop_file.rb

    require "wx"

    class FileDrop < Wx::FileDropTarget
      def initialize(window)
        super()   # parantezler mecbur
        @window = window
      end

      def on_drop_files(x, y, filenames)
        for name in filenames
          @window.write_text File.read(name)
        end
        return true
      end
    end

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [400, 400]
        set_title "Dosya sürükle-bırak"
        centre

        text = Wx::TextCtrl.new self, style: Wx::TE_MULTILINE
        dt = FileDrop.new text

        text.set_drop_target dt
      end
    end

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


    Uygulama penceresine Wiindows gezgininden bir dosyayı sürüklersek dosya içeriğini TextCtrl nesnesi içeriğine ekler.

      def on_drop_files(x, y, filenames)
        for name in filenames
    ....

    Bir kerede bir'den fazla dosya sürükleyebilirsiniz. 

          @window.write_text File.read(name)

    Text dosyası içeriği okunup uygulamaızın penceresine yazılır. 

        text = Wx::TextCtrl.new self, style: Wx::TE_MULTILINE
        dt = FileDrop.new text

        text.set_drop_target dt

    TextCtrl nesnemiz sürükle-bırak hedefi. 






    WxRuby Grafikleri

    Eğlenceli bir konuya geldik. Başarılı olursak güzel grafik çizimleri yapacağız. GDI (Graphics Device Interface) grafikler çalışmak için bir arabirim. Monitör, printer veya bir dosya gibi grafik nesneleri ile etkileşimi sağlar. GDI yardımıyla yazılımcı bir monitör ya da printer'da verilerini donanımdan bağımsız gösterebilir. Yazılımcı tarafından bakılırsa GDI grafiklerle çalışabileceği bir kısım sınıftan ibarettir. GDI'da 2D vektör grafikler, image ve font'lar vardır.

    Çizim yapabilmek için bir device context (DC) nesnesi üretmemiz gerekir. WxRuby'de bu bir Wx::DC nesnesi. Ancak çizimlerimiz için Wx::DC nesnesi kullanmayız, kullanacağımız çıktı cihazına göre türetilmiş alt sınıflarından birini kullanırız. 

    • Wx::BufferedDC
    • Wx::BufferedPaintDC
    • Wx::PostScriptDC
    • Wx::MemoryDC
    • Wx::PrinterDC
    • Wx::ScreenDC
    • Wx::ClientDC
    • Wx::PaintDC
    • Wx::WindowDC


    Wx::ScreenDC nesnesi ekranda herhangi bir yere çizim yapabilir. Wx::WindowDC tüm pencere geneline çizim yapar (sadece Windows'ta). Wx::ClientDC pencerenin kullanım alanına çizim yapar (title ve border hariç alan). Wx::PaintDC de kullanım alanına çizer ama PaintDC ile Wx::PaintEvent'den çizim yapılabilir (olay işleyici içinde) ClientDC tam tersi. Wx::MemoryDC ile bitmap olarak çizim yapılır. Wx::PostScriptDC ile PostScript dosyalarına çizim yapılır. Wx::PrinterDC ile printer'a çizim yapılır (sadece Windows'ta).



    Basit bir çizgi çizmek

    İlk örneğimizde pencere kullanım alanına bir çizgi çizeceğiz. draw_line(x1, y1, x2, y2) metodunu kullanacağız. 

    draw_line.rb

    require "wx"

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

        tmr = Wx::Timer.new self
        tmr.start_once(2000)
        evt_timer tmr, :draw_line

      end
      def draw_line(e)
        Wx::ClientDC.draw_on self do |dc|
          dc.draw_line 50, 60, 190, 60
        end
      end
    end

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

    Pencere kullanım alanında program başladıktan 2 saniye sonra yatay bir çizgi çiziliyor.

        tmr = Wx::Timer.new self
        tmr.start_once(2000)
        evt_timer tmr, :draw_line

    Bir Wx::Timer nesnesini 2000 milisaniye sonra bir kereliğine tetiklenecek şekilde ayarlıyoruz. Zamanlayıcı dolunca draw_line olay işleme metodumuzu çağırıyoruz. Metod adının çizim yapan DC metoduyla benzerliği sadece tesadüf. 

      def draw_line(e)
        Wx::ClientDC.draw_on self do |dc|
          dc.draw_line 50, 60, 190, 60
        end
      end

    Bu yapıyı ClientDC dokümanından öğrendim. ClientDC nesnesinin çizimi yapacağı yer self, yani penceremizin kendisi. Blok içinde dc değişkeninde çizim metodlarını çağırabiliriz. Sol kenardan 50 piksel , yukarı kenardan 60 piksel içerden başlayarak soldan 190, yukarıdan 60 içeriye kadar çizgi çiziliyor, yani yatay bir çizgi. 

    Şunu belirtelim, pencere boyutlarını çizgiyi örtecek kadar küçültürsek çizgi kaybolur. Pencere boyutu değiştirilince özellikle büyütürken yeni açılan yerler tekrar boyanır. Minimize maksimize edince , üzerine başka pencere gelince vs. Bu yok olma sorununu çözmek için her seferinde çizgiyi yeniden çizmemiz gerekir, bu amaçla Wx::PaintEvent nesnesi kullanılabilir. 




    draw_line2.rb

    require "wx"

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

        evt_paint :draw_line
      end
      def draw_line(e)
        self.paint do |dc|
          dc.draw_line 50, 60, 190, 60
        end
      end
    end

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

    Aynı çizgiyi çiziyoruz ama bu sefer pencere yeniden boyanınca. 

        evt_paint :draw_line

    Pencerenin yeniden boyanması olayını draw_line metodumuza bağlıyoruz.

      def draw_line(e)
        self.paint do |dc|
          dc.draw_line 50, 60, 190, 60
        end
      end

    Bu sefer çizimi yapmak için PaintDC kullanıyoruz. 




    Bilgisayar grafikleri

    İki çeşit grafik çizme tekniği vardır: vektör ve raster grafikler. Raster grafikler resimleri piksellerden oluşturur. Vektör grafikler geometrik grafik nesnelerini kullanırlar, örneğin noktalar, çizgiler, eğriler, çokgenler vs. ile resmi oluştururlar. Bu geometrik temel şekiller matematik hesaplarla çizilir. 

    Her iki tip bilgisayar grafiğinin de avantaj ve dezavantajları vardır. Vektör grafiğin, rastere göre üstünlükleri:

    • Küçük hafıza işgal ederler
    • Sınırsız zoom yapılabilir
    • Kaydırma , boyut değiştirme, renk değiştirme ve döndürme resim kalitesini değiştirmez


    Temel şekiller

    Aşağıdakiler temel şekillerden bazıları

    • Noktalar
    • Çizgiler
    • Çoklu çizgiler
    • Çokgenler
    • Daireler
    • Elipsler
    • Eğriler



    DC öznitelikleri

    Device context nesnelerinin bağlı özellikleri var. Örneğin pen ya da font gibi. Wx::Brush alanları renklendirmek için kullanılan bir araç. Şekillerin içlerinin boyamak için kullanılırlar. Bir rengi ve bir stili vardır. Wx::Pen şekillerin kenar çizgilerini belirler.  Rengi, genişliği ve stili vardır. Wx::Font yazıların görünüşünü belirler. 



    Temel elemanlar

    Aşağıdaki satırlarda çeşitli nesneleri göreceğiz, 


    Colour

    Colour kırmızı, yeşil, mavi (RGB) renklerin parlaklık değerlerinin birleştirilmesiyle oluşan bir renktir. Geçerli parlaklık değeri 0-255 arasında olur. Renk belirlemenin 4 değişik yolu vardır. Bir Wx::Colour nesnesi oluştururuz ve oluştururken RGB değerini veririz. Bu değeri girmenin 4 yöntemi vardır,

    Wx::Colour.new 0, 0, 255
    Wx::Colour.new "blue"
    Wx::Colour.new "#0000FF"
    Wx::Colour.new Wx::BLUE


    Hazır tanımlı birçok renk adı var.

    AQUAMARINEBLACK BLUEBLUE VIOLET BROWNCADET BLUE
    CORALCORNFLOWER
    BLUE
    CYANDARK GREY DARK GREENDARK OLIVE
    GREEN
    DARK ORCHIDDARK SLATE
    BLUE
    DARK SLATE
    GREY
    DARK
    TURQUOISE
    DIM GREYFIREBRICK
    FOREST GREENGOLD GOLDENRODGREEN GREEN YELLOWINDIAN RED
    KHAKILIGHT BLUE LIGHT GREYLIGHT STEEL
    BLUE
    LIME GREENMAGENTA
    MAROONMEDIUM
    AQUAMARINE
    MEDIUM BLUEMEDIUM ORCHID MEDIUM SEA
    GREEN
    MEDIUM
    SLATE BLUE
    MEDIUM
    SPRING GREEN
    MEDIUM
    TURQUOISE
    MEDIUM
    VIOLET RED
    MIDNIGHT BLUE NAVYORANGE
    ORANGE REDORCHID PALE GREENPINK PLUMPURPLE
    REDSALMON SEA GREENSIENNA SKY BLUESLATE BLUE
    SPRING GREENSTEEL BLUE TANTHISTLE TURQUOISEVIOLET
    WHEATWHITE YELLOWYELLOW GREEN


    Aşağıdaki örnek bazı renkleri kullanıyor.

    colours.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.pen = Wx::Pen.new '#d4d4d4'

          dc.brush = Wx::Brush.new '#c56c00'
          dc.draw_rectangle 10, 15, 90, 60

          dc.brush = Wx::Brush.new '#1ac500'
          dc.draw_rectangle 130, 15, 90, 60

          dc.brush = Wx::Brush.new '#539e47'
          dc.draw_rectangle 250, 15, 90, 60

          dc.brush = Wx::Brush.new '#004fc5'
          dc.draw_rectangle 10, 105, 90, 60

          dc.brush = Wx::Brush.new '#c50024'
          dc.draw_rectangle 130, 105, 90, 60

          dc.brush = Wx::Brush.new '#9e4757'
          dc.draw_rectangle 250, 105, 90, 60

          dc.brush = Wx::Brush.new '#5f3b00'
          dc.draw_rectangle 10, 195, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c'
          dc.draw_rectangle 130, 195, 90, 60

          dc.brush = Wx::Brush.new '#785f36'
          dc.draw_rectangle 250, 195, 90, 60
        end
      end
    end

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


    9 dikdörtgen çiziyoruz hepsi başka renk

          dc.brush = Wx::Brush.new '#785f36'
          dc.draw_rectangle 250, 195, 90, 60

    brush nesnesi rengini hexa desimal formatta veriyoruz. brush değeri şekillerin içinin dolgu rengi. Dikdörtgen çizmek için draw_rectangle metodu kullanılır.





    Wx::Pen

    Pen nesnesi grafik çiziminin ana elemanlarındandır. Çizgiler , eğriler , dikdörtgenler , elipsler ve dairelerin kenarları onunla çizilir. 

    Wx::Pen.new colour, width = 1, style = Wx::PenStyle::PENSTYLE_SOLID

    Wx::Pen nesnesi üretici metodu 3 parametre değeri alır, renk, genişlik ve stil. Başlıca çizgi stilleri şunlar:

    • Wx::PENSTYLE_SOLID
    • Wx::PENSTYLE_DOT
    • Wx::PENSTYLE_LONG_DASH
    • Wx::PENSTYLE_SHORT_DASH
    • Wx::PENSTYLE_DOT_DASH
    • Wx::PENSTYLE_TRANSPARENT

    pens.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.pen = Wx::Pen.new '#4c4c4c', 3, Wx::PENSTYLE_SOLID
          dc.draw_rectangle 10, 15, 90, 60

          dc.pen = Wx::Pen.new '#4c4c4c', 3, Wx::PENSTYLE_DOT
          dc.draw_rectangle 130, 15, 90, 60

          dc.pen = Wx::Pen.new '#4c4c4c', 3, Wx::PENSTYLE_LONG_DASH
          dc.draw_rectangle 250, 15, 90, 60

          dc.pen = Wx::Pen.new '#4c4c4c', 3, Wx::PENSTYLE_SHORT_DASH
          dc.draw_rectangle 10, 105, 90, 60

          dc.pen = Wx::Pen.new '#4c4c4c', 3, Wx::PENSTYLE_DOT_DASH
          dc.draw_rectangle 130, 105, 90, 60

          dc.pen = Wx::Pen.new '#4c4c4c', 3, Wx::PENSTYLE_TRANSPARENT
          dc.draw_rectangle 250, 105, 90, 60
        end
      end
    end

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


    Eğer bir brush değeri vermezsek default renkte (beyaz) brush kullanılır. Dikdörtgenlerin kenar çizgileri seçtiğimiz Wx::Pen değeri ile çizilir. Sonuncuda kenar çizgisi yok , çünkü transparent.




    Join ve Cap

    Bir Pen nesnesinin Join ve Cap adında iki özelliği daha vardır. Join çizgilerin birleştiği yerin şeklini belirler ve set_join (veya join =) metodu ile belirtilir. Değerler şunlar.

    • Wx::JOIN_MITER
    • Wx::JOIN_BEVEL
    • Wx::JOIN_ROUND

    JOIN_MITER kullanınca çizgiler tam köşe şeklinde birleşir. JOIN_BEVEL kullanınca köşeye çapraz bir kesik atılır. JOIN_ROUND kullanınca köşeler yuvarlatılmış olur. 

    Cap çizgi uçlarının nasıl olacağını belirler ve set_cap (veya cap= ) metodu ile ayarlanır . Opsiyonlar

    • Wx::CAP_ROUND
    • Wx::CAP_PROJECTING
    • Wx::CAP_BUTT

    CAP_ROUND kullanınca çizgi uçları yuvarlatılmış olur. CAP_PROJECTING ve CAP_BUTT kullanınca çizgi uçları köşeli olur. Aradaki fark, CAP_PROJECTING kullanınca çizgi boyu verilen sınırı çizgi kalınlığının yarısı kadar aşar. CAP_ROUND da kullanınca çizgi boyu aynı şekilde aşma olur. 

    joins_caps.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          pen = Wx::Pen.new '#4c4c4c', 10, Wx::PENSTYLE_SOLID

          pen.join = Wx::JOIN_MITER
          dc.pen = pen
          dc.draw_rectangle 15, 15, 80, 50

          pen.join = Wx::JOIN_BEVEL
          dc.pen = pen
          dc.draw_rectangle 125, 15, 80, 50

          pen.join = Wx::JOIN_ROUND
          dc.pen = pen
          dc.draw_rectangle 235, 15, 80, 50

          pen.cap = Wx::CAP_BUTT
          dc.pen = pen
          dc.draw_line 30, 150,  150, 150

          pen.cap = Wx::CAP_PROJECTING
          dc.pen = pen
          dc.draw_line 30, 190,  150, 190

          pen.cap = Wx::CAP_ROUND
          dc.pen = pen
          dc.draw_line 30, 230,  150, 230

          pen2 = Wx::Pen.new '#4c4c4c', 1, Wx::PENSTYLE_SOLID
          dc.pen = pen2
          dc.draw_line 30, 130, 30, 250
          dc.draw_line 150, 130, 150, 250
          dc.draw_line 155, 130, 155, 250
        end
      end
    end

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


          pen = Wx::Pen.new '#4c4c4c', 10, Wx::PENSTYLE_SOLID

    Join ve Cap özellikleri için çizgi kalınlığımız 1 pikselden fazla olmalı. 

          dc.draw_line 150, 130, 150, 250
          dc.draw_line 155, 130, 155, 250

    Çizgilerin sonuna denk gelecek aralarına 5 piksel olan iki ince çizgi çiziyoruz ki etkileri görelim. 


    3. dikdörtgenin köşelerinin yuvarlak olması gerekiyordu, ama olmadı. Sanırım Windows'ta bu işi beceremedi. Ama şöyle yapınca oluyor.

          pen1.set_join Wx::JOIN_ROUND
          pen1.cap = Wx::CAP_BUTT
          dc.pen = pen1
          dc.draw_rectangle 235, 15, 80, 50




    Gradyenler

    Bilgisayar grafiklerinde gradyen karanlıktan parlağa ya da bir renkten diğerine yumuşak geçişi ifade eder. 2 boyutlu çizim programlarında gradyenler 3 boyutluymuş gibi derinlikler verme amacıyla çok kullanılır. 

    gradient_fill_linear(rect, initialColour, destColour,
    nDirection = Wx::Direction::RIGHT)

    Bu metod rect ile verilen dikdörtgen alanı boyar. Renk initialColour renginden başlar, destColour renginde biter. Rengin değişim yönü nDirection ile belirtilir, default yönü sağa doğrudur. 

    gradients.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.gradient_fill_linear Wx::Rect.new(20, 20, 180, 40),
            '#ffec00', '#000000', Wx::NORTH
          dc.gradient_fill_linear Wx::Rect.new(20, 80, 180, 40),
            '#ffec00', '#000000', Wx::SOUTH
          dc.gradient_fill_linear Wx::Rect.new(20, 140, 180, 40),
            '#ffec00', '#000000', Wx::EAST
          dc.gradient_fill_linear Wx::Rect.new(20, 200, 180, 40),
            '#ffec00', '#000000', Wx::WEST
          dc.gradient_fill_linear Wx::Rect.new(220, 20, 50, 120),
            '#cccccc', '#222222', Wx::WEST
          dc.gradient_fill_linear Wx::Rect.new(270, 20, 50, 120),
            '#cccccc', '#222222', Wx::EAST
        end
      end
    end

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


    Ben parametrede Wx::Rect.new(20, 20, 180, 40) gibi yeni nesne üretme kodlarını sevmiyorum, sanki C++ kod yazıyormuşum gibi geliyor. Bu amaçla gradient_fill_linear metodu tanımlamasına baktım. Metodun kaynak dosyası yerini view source linkini tıklayıp öğrendim , 

    # File 'lib/wx/doc/gen/dc.rb', line 698

    Bu dosyayı Ruby kurulumumun WxRuby3 gem klasöründe buldum , benim kurulumumda tam olarak C:\Ruby34-x64\lib\ruby\gems\3.4.0\gems\wxruby3-1.5.1\lib\wx\doc\gen\dc.rb dosyası. İlgili satırları buldum.

        # Fill the area specified by rect with a linear gradient, starting
    from initialColour and eventually fading to destColour.
        #
        # The nDirection specifies the direction of the colour change,
    default is to use initialColour on the left part of the rectangle and
    destColour on the right one.
        # @param rect [Wx::Rect]
        # @param initialColour [Wx::Colour,String,Symbol]
        # @param destColour [Wx::Colour,String,Symbol]
        # @param nDirection [Wx::Direction]
        # @return [void]
        def gradient_fill_linear(rect, initialColour, destColour,
    nDirection=Wx::Direction::RIGHT) end

    Malesef rect parametresinin sadece Wx::Rect nesnesi aldığını yazıyor. Ama renkler sembol değer alıyormuş, hemen denedim. 

          dc.gradient_fill_linear Wx::Rect.new(20, 20, 180, 40),
            :red, :pink, Wx::NORTH

    İnanır mısınız çalışıyor. İsmi verilen renkleri kullanabiliyoruz. Kaynak koduna bakmak öğrenmek için faydalı demek ki. rect parametresine gelince , bu konuda bir yardımcı kod yazmaya karar verdim. Başından beri takip ediyorsanız Wx::GridBagSizer sınıfı koduna pos ve span değerlerini array olarak girmek amacıyla bir yardımcı kod yazdığım my_wx_helper.rb dosyam vardı, ona ilave yapacağım.

    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

    Benzer bir taklayı Wx::DC sınıfındaki gradient_fill_linear metoduna yapacağı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

    class Wx::DC
      wx_gradient_fill_linear = instance_method :gradient_fill_linear
      wx_redefine_method :gradient_fill_linear do |*args|
        if args[0].class == Array && args[0].length == 4
          args[0] = Wx::Rect.new args[0][0], args[0][1], args[0][2], args[0][3]
        end
        wx_gradient_fill_linear.bind(self).call(*args)
      end
    end

    Artık kodumuz daha kısa olabilir.

    require "wx"
    require "./my_wx_helper"
    .....
      def on_paint(e)
        self.paint do |dc|
          dc.gradient_fill_linear [20, 20, 180, 40], "Medium Blue", :cyan, Wx::NORTH
          dc.gradient_fill_linear [20, 80, 180, 40], '#ffec00', '#000000', Wx::SOUTH
          dc.gradient_fill_linear [20, 140, 180, 40], '#ffec00', '#000000', Wx::EAST
          dc.gradient_fill_linear [20, 200, 180, 40], '#ffec00', '#000000', Wx::WEST
          dc.gradient_fill_linear [220, 20, 50, 120], '#cccccc', '#222222', Wx::WEST
          dc.gradient_fill_linear [270, 20, 50, 120], '#cccccc', '#222222', Wx::EAST
        end
      end
    .....





    Wx::Brush

    Brush temel elemanlardan biri, şekillerin içlerini boyamak için kullanılır. Olası brush tipleri:

    • Wx::BRUSHSTYLE_SOLID
    • Wx::BRUSHSTYLE_STIPPLE
    • Wx::BRUSHSTYLE_BDIAGONAL_HATCH
    • Wx::BRUSHSTYLE_CROSSDIAG_HATCH
    • Wx::BRUSHSTYLE_FDIAGONAL_HATCH
    • Wx::BRUSHSTYLE_CROSS_HATCH
    • Wx::BRUSHSTYLE_HORIZONTAL_HATCH
    • Wx::BRUSHSTYLE_VERTICAL_HATCH
    • Wx::BRUSHSTYLE_TRANSPARENT


    brushes.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_CROSS_HATCH
          dc.draw_rectangle 10, 15, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_SOLID
          dc.draw_rectangle 130, 15, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_BDIAGONAL_HATCH
          dc.draw_rectangle 250, 15, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_CROSSDIAG_HATCH
          dc.draw_rectangle 10, 105, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_FDIAGONAL_HATCH
          dc.draw_rectangle 130, 105, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_HORIZONTAL_HATCH
          dc.draw_rectangle 250, 105, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_VERTICAL_HATCH
          dc.draw_rectangle 10, 195, 90, 60

          dc.brush = Wx::Brush.new '#4c4c4c', Wx::BRUSHSTYLE_TRANSPARENT
          dc.draw_rectangle 130, 195, 90, 60
        end
      end
    end

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


    Bu örnekte 8 değişik brush tipi kullanılıyor.




    Kandi paternimiz

    Ön tanımlı paternleri kullanmak zorunda değiliz, kendi paternimizi tanımlayabiliriz. 

    custom_patterns.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [400, 150]
        set_title "Kendi Paternimiz"
        centre

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.pen = Wx::Pen.new '#C7C3C3'

          brush1 = Wx::Brush.new(Wx::Bitmap.new('bardejov.jpg'))
          dc.brush = brush1
          dc.draw_rectangle 10, 15, 90, 60

          brush2 = Wx::Brush.new(Wx::Bitmap.new('mincol.jpg'))
          dc.brush = brush2
          dc.draw_rectangle 130, 15, 90, 60

          brush3 = Wx::Brush.new(Wx::Bitmap.new('patern.png'))
          dc.brush = brush3
          dc.draw_rectangle 250, 15, 90, 60
        end
      end
    end

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

    Resimleri patern olarak kullanabiliriz. 

          brush3 = Wx::Brush.new(Wx::Bitmap.new('patern.png'))
          dc.brush = brush3
          dc.draw_rectangle 250, 15, 90, 60

    Son resimdeki küçük bir resim dosyası ve alanı doldurana kadar tekrarlanarak çoğaltılıyor.




    draw_point metodu

    En basit grafik nesnesi bir noktadır. 

    draw_point(x, y)

    Bu metod x, y koordinatına bir nokta çizer.

    points.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.pen = Wx::Pen.new :red

          1000.times do
            w, h = size
            x = rand(1...w)
            y = rand(1...h)
            dc.draw_point x, y
          end
        end
      end
    end

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

    Bir tek noktayı görmek zor , bu yüzden 1000 tane çiziyoruz. 

          dc.pen = Wx::Pen.new :red

    Noktalarımız kırmızı renkte olacak 

            w, h = size
            x = rand(1...w)
            y = rand(1...h)

    Pencere genişlik ve yüksekliğini w ve h değişkenlerine alıyoruz, sonra da bu alan içinde rastgele bir koordinat belirliyoruz. 




    Şekiller

    Şekiller daha gelişmiş grafik nesneler, örneğimizde çeşitli şekiller çiziyoruz.

    shapes.rb

    require "wx"

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [380, 320]
        set_title "Şekiller"
        centre

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.pen = Wx::Pen.new "#777"
          dc.brush = Wx::Brush.new "#777"

          dc.draw_ellipse 20, 20, 90, 60
            # x, y, width, height
          dc.draw_rounded_rectangle 130, 20, 90, 60, 10
            # x, y, width, height, radius
          dc.draw_arc 240, 40, 340, 40, 290, 20
            # xStart, yStart, xEnd, yEnd, xcenter, ycenter

          dc.draw_rectangle 20, 120, 80, 50
            # x, y, width, height
          dc.draw_polygon [[130, 140], [180, 170], [180, 140], [220, 110], [140, 100]]
            # points_arrays, xoffset, yoffset, fill_style
          dc.draw_spline [[240, 170], [280, 170], [285, 110], [325, 110]]
            # points_arrays

          dc.draw_lines [[20, 260], [100, 260], [20, 210], [100, 210]]
            # points_arrays, xoffset, yoffset
          dc.draw_circle 170, 230, 35
            # x, y, radius
          dc.draw_rectangle 250, 200, 60, 60
        end
      end
    end

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




    Bölgeler

    Device context region adı verilen parçalara bölünebilir. Bir bölge dikdörtgen ya da daire gibi bir şekil olabilir. Union, Intersect, Substract ve Xor işlemleri ile karmaşık bölgeler oluşturabiliriz. Bölgeler çevreleme , doldurma ve kırpma işlerinde kullanılabilir. 

    Bölgeleri üç şekilde oluştururuz. En kolayı dik dörtgen bir bölge oluşturmaktır. Diğer bölgeler noktalardan ve bitmap resimlerden oluşturulabilir. 

    Bölgelere girmeden önce küçük bir örnek oluşturacağız. Bu kısmı kolay anlaşılabilir parçalara ayıracağız. Okul matematiğimizi tekrar gözden geçirmek gerekebilir, burada güzel bir makale var.

    lines.rb

    require "wx"

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          size_x, size_y = get_client_size
          dc.set_device_origin size_x/2, size_y/2

          radius = Math.hypot size_x/2, size_y/2
          angle = 0

          while (angle < 2 * Math::PI) do
            x = radius * Math.cos(angle)
            y = radius * Math.sin(angle)
            dc.draw_line [0, 0], [x, y]
            angle = angle + 2 * Math::PI / 360
          end
        end
      end
    end

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


    Örneğimizde 1 derece aralıklı 360 tane çizgiyi tüm pencereye dolduruyoruz. 

    include Math

    Math modülünü dahil ederek PI sabitinin , sin, cos ve hypot metodlarının başına sürekli Math:: yazmaktan kurtulabiliriz. 

          dc.set_device_origin size_x/2, size_y/2

    Alanın koordinat merkezini sol üst köşeden pencere ortasına alıyoruz.

          radius = hypot size_x/2, size_y/2

    En uzun çizgi boyu olarak köşeye olan uzaklığı hipotenüs hesabı ile buluyoruz. 

            x = radius * cos(angle)
            y = radius * sin(angle)

    Çizginin bir ucu merkezde olacak diğer ucu da sinüs ve kosinüs ile açıya göre hesaplanan noktalarda. 



    Kırpmak

    Kırpma sayesinde çizim alanını belli bir bölgeye sınırlarız. Çizim alanını kırpmak için set_clipping_region (veya clipping_region = ) metodunu kullanırız. 

    Aşağıdaki örnekte bir önceki örneğimizi kırpma sayesinde geliştiriyoruz. 

    region.rb

    require "wx"
    include Math

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          size_x, size_y = get_client_size
          dc.set_device_origin size_x/2, size_y/2

          region = Wx::Region.new [10, 130], [130, 10]
          dc.device_clipping_region = region

          radius = hypot size_x/2, size_y/2
          angle = 0

          while (angle < 2 * PI) do
            x = radius * cos(angle)
            y = radius * sin(angle)
            dc.draw_line [0, 0], [x, y]
            angle = angle + 2 * PI / 360
          end

          dc.destroy_clipping_region
        end
      end
    end

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

    Yine aynı çizgileri çiziyoruz ama bu sefer pencerenin belli bir kısmında çizim gerçekleşebiliyor. 

          region = Wx::Region.new [10, 130], [130, 10]
          dc.device_clipping_region = region

    2 nokta ile bir dikdörtgenin köşelerini belirliyoruz ve device_clipping_region = metodu ile (ya da set_device_clipping_region) çizim yapılabilecek alanı belirliyoruz. 

          dc.destroy_clipping_region

    Çizim bitince bölgemizi yok ediyoruz.



    Bölge operasyonları

    Bölgeler karmaşık şekiller üretmekte kullanılabilir. 4 işlemimiz var, union, intersect, substract, xor

    Örneğimiz dört işlemi de kullanıyor.

    region_operations.rb

    require "wx"
    include Math

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

        evt_paint :on_paint
      end
      def on_paint(e)
        self.paint do |dc|
          dc.pen = Wx::Pen.new '#d4d4d4'

          dc.draw_rectangle 20, 20, 50, 50
          dc.draw_rectangle 30, 40, 50, 50

          dc.brush = Wx::Brush.new '#ffffff'
          dc.draw_rectangle 100, 20, 50, 50
          dc.draw_rectangle 110, 40, 50, 50
          region1 = Wx::Region.new 100, 20, 50, 50
          region2 = Wx::Region.new 110, 40, 50, 50
          region1.intersect region2

          rect = region1.box
          dc.device_clipping_region = region1
          dc.brush = Wx::Brush.new '#ff0000'
          dc.draw_rectangle rect
          dc.destroy_clipping_region

          dc.brush = Wx::Brush.new '#ffffff'
          dc.draw_rectangle 180, 20, 50, 50
          dc.draw_rectangle 190, 40, 50, 50
          region1 = Wx::Region.new 180, 20, 50, 50
          region2 = Wx::Region.new 190, 40, 50, 50
          region1.union region2
          dc.device_clipping_region = region1

          rect = region1.box
          dc.brush = Wx::Brush.new '#fa8e00'
          dc.draw_rectangle rect
          dc.destroy_clipping_region

          dc.brush = Wx::Brush.new '#ffffff'
          dc.draw_rectangle 20, 120, 50, 50
          dc.draw_rectangle 30, 140, 50, 50
          region1 = Wx::Region.new 20, 120, 50, 50
          region2 = Wx::Region.new 30, 140, 50, 50
          region1.xor region2

          rect = region1.box
          dc.device_clipping_region = region1
          dc.brush = Wx::Brush.new '#619e1b'
          dc.draw_rectangle rect
          dc.destroy_clipping_region

          dc.brush = Wx::Brush.new '#ffffff'
          dc.draw_rectangle 100, 120, 50, 50
          dc.draw_rectangle 110, 140, 50, 50
          region1 = Wx::Region.new 100, 120, 50, 50
          region2 = Wx::Region.new 110, 140, 50, 50
          region1.subtract region2

          rect = region1.box
          dc.device_clipping_region = region1
          dc.brush = Wx::Brush.new '#715b33'
          dc.draw_rectangle rect
          dc.destroy_clipping_region

          dc.brush = Wx::Brush.new '#ffffff'
          dc.draw_rectangle 180, 120, 50, 50
          dc.draw_rectangle 190, 140, 50, 50
          region1 = Wx::Region.new 180, 120, 50, 50
          region2 = Wx::Region.new 190, 140, 50, 50
          region2.subtract region1

          rect = region2.box
          dc.device_clipping_region = region2
          dc.brush = Wx::Brush.new '#0d0060'
          dc.draw_rectangle rect
          dc.destroy_clipping_region
        end
      end
    end

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


    Örnekte 6 değişik region operasyonu yapıyoruz.

          region1 = Wx::Region.new 100, 20, 50, 50
          region2 = Wx::Region.new 110, 40, 50, 50
          region1.intersect region2

    Bu operasyon iki region kesişimini alıyor.





    Ölçü birimleri

    DC nesnesine bir yazı ya da şekil çizerken aslında biz piksel değil mantıksal boyutlar veriyoruz. Default ölçü birimi piksel olduğu için yazdığımız sayılar piksel olarak değerlendiriliyor. Mantıksal birimler ve DC nesnesinin birimleri farklı olabilir. İnsanların kullandığı birim milimetre iken , ekran piksel kullanır, printer için 1200 dpi (dots per inch - inç başına nokta sayısı) olabilir. 

    Bizim girdiğimiz koordinatları kullanılan DC birimlerine çevirme işlemine mapping mode deniyor. 


    Mapping ModeLogical Unit
    Wx::MM_TEXT1 piksel
    Wx::MM_METRIC1 milimetre
    Wx::MM_LOMETRICMilimetreni 1/10'u
    Wx::MM_POINTS1 point, inç'in 1/72'si
    Wx::MM_TWIPSpoint'in 1/20'si veya inç'in 1/1440'ı


    Defaulr mapping mode Wx::MM_TEXT , bu durumda yazdığımız her birim ekranda bir piksele karşı geliyor. İnsanlar genelde ekranda bir şeyi pozisyonlamak için ya da web sayfası tasarımlarında hep piksel kullanırlar. Eğer milimetre düşünmek istersek elimizde 2 mod var, Wx::MM_METRIC ve Wx::MM_LOMETRIC  

    Başka moda geçmak için set_map_mode (ya da map_mode= ) metodu kullanılır. 



    Cetvel örneği

    Cetvel ile ekranda milimetrik ölçüm yapmaya çalışacağız.

    ruler.rb

    require "wx"

    RW = 701 # cetvel genişlik
    RM = 10  # cetvel marjin
    RH = 80  # cetvel yükseklik

    class Örnek < Wx::Frame
      def initialize(*args)
        super(*args)
        init_UI
      end
      def init_UI
        set_size [RW + 2*RM, RH]
        @font = Wx::Font.new 7, Wx::FONTFAMILY_DEFAULT, Wx::FONTSTYLE_NORMAL,
                Wx::FONTWEIGHT_BOLD, false, 'Courier 10 Pitch'
        set_title "Cetvel"
        centre

        evt_paint :on_paint
        evt_left_down :on_left_down
        evt_motion :on_mouse_move
        evt_left_up :on_left_up
        evt_right_down :on_right_down
      end

      def on_paint(e)
        self.paint do |dc|
          #dc.map_mode = Wx::MM_LOMETRIC
          dc.brush = Wx::Brush.new Wx::Bitmap.new('patern.png')
          dc.draw_rectangle 0, 0, RW+2*RM, RH
          dc.font = @font

          dc.pen = Wx::Pen.new '#F8FF25'
          dc.text_foreground = '#F8FF25'

          for i in (0..RW)

            if (i % 100 == 0)
              dc.draw_line i+RM, 0, i+RM, 10
              w, h = dc.text_extent(i.to_s)
              dc.draw_text i.to_s, i+RM-w/2, 11

            elsif (i % 20 == 0)
              dc.draw_line i+RM, 0, i+RM, 8

            elsif (i % 2 == 0)
              dc.draw_line i+RM, 0, i+RM, 4
            end
          end
        end
      end

      def on_left_down(e)
        x, y = client_to_screen(e.position)
        ox, oy = position

        dx = x - ox
        dy = y - oy

        @delta = [dx, dy]
      end

      def on_mouse_move(e)
        if e.dragging and e.left_is_down

          set_cursor Wx::Cursor.new(Wx::CURSOR_HAND)

          x, y = client_to_screen e.get_position
          fp = [x - @delta[0], y - @delta[1]]
          move fp
        end
      end

      def on_left_up(e)
        set_cursor Wx::Cursor.new(Wx::CURSOR_ARROW)
      end

      def on_right_down(e)
        close
      end
    end

    Wx::App.run {
      Örnek.new(nil, style: Wx::FRAME_NO_TASKBAR | Wx::NO_BORDER | Wx::STAY_ON_TOP).show
    }


    Bir cetvel yapıyoruz. Cetvelimiz default olan piksel modunda çalışıyor. 

    Wx::App.run {
      Örnek.new(nil,
    style: Wx::FRAME_NO_TASKBAR | Wx::NO_BORDER | Wx::STAY_ON_TOP).show
    }

    Stil olarak görev çubuğunda görünmeyen , etiket barı olmayan ve her şeyin üstünde duran bir pencere yaptık. 

          dc.brush = Wx::Brush.new Wx::Bitmap.new('patern.png')
          dc.draw_rectangle 0, 0, RW+2*RM, RH

    Arkaplana bir patern koyduk. 

              w, h = dc.text_extent(i.to_s)
              dc.draw_text i.to_s, i+RM-w/2, 11

    text_extent metodu çizim alanının kullandığı yazı ölçüleriyle o argümanındaki yazının kapladığı boyutu döndürür. 


      def on_mouse_move(e)
        if e.dragging and e.left_is_down

          set_cursor Wx::Cursor.new(Wx::CURSOR_HAND)

          x, y = client_to_screen e.get_position
          fp = [x - @delta[0], y - @delta[1]]
          move fp
        end
      end

    Penceremizin etiket barı olmadığı için mouse ile tutup çekerek kaydırıyoruz.

      def on_right_down(e)
        close
      end

    Sağ tıklama ile uygulamayı kapatabiliriz.

    Grafikler kısmı burada bitiyor. Sonraki bölümde kendi widget'larımızı oluşturacağız inşallah. Şimdilik kalın sağlıcakla..








    Hiç yorum yok:

    Yorum Gönder