25 Şubat 2015 Çarşamba

Rails 2015

Bu sefer www.railstutorial.org adresinden.

Başlıyoruz, önce Cloud9 adresinde hesabımız yoksa açıyoruz. Yeni bir workspace oluşturup adını "rails-tutorial" koyup proje sembolü seçeneklerinden "Rails Tutorial" ikonu seçiyoruz (RubyOnRails değil).

Sırada Rails kurulumu var, Start editing düğmesini tıkladıktan sonra aşağıda açılan terminale:
$ gem install rails -v 4.2.0
 satırını giriyoruz. Gereken Ruby gemler yüklendikten sonra tekrar komut satırına geri döner. Yeni bir uygulama üretmek için "rails new" komutunu kullanacağız:
$ rails _4.2.0_ new hello_app
      create
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/javascripts/application.js
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      .
      .
      .
      create  test/test_helper.rb
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/javascripts
      create  vendor/assets/javascripts/.keep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep
         run  bundle install
Fetching gem metadata from https://rubygems.org/..........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.3.2
Using i18n 0.6.11
.
.
.
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
         run  bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted
 Bir süre sabırla bekledikten sonra uygulamamızın tüm gereken dosyaları kurulur ve gereken gemler "bundle install" ile otomatik olarak kurulur. Şöyle bir dosya yapısına bakarsak:

Dosya/KlasörAmacı
app/Ana uygulama (app) kodu, model, view, controller, ve helper dosyalarını içerir
app/assetsUygulamanın değerleri, CSS dosyaları, JavaScript dosyaları, ve resimler
bin/Binary işlem dosyaları
config/Uygulama konfigürasyonu
db/Veritabanı dosyaları
doc/Uygulama için dökümentasyon
lib/Kütüphane modülleri
lib/assetsKütüphane değerler CSS, JavaScript dosyaları ve resimler gibi
log/Uygulama log kayıtları
public/Erişime açık veriler (web tarayıcıların erişebileceği dosyalar), mesela error sayfaları
bin/railsKodu üreten, konsol oturumu açan ya da yerel bir server açan program
test/Uygulama testleri
tmp/Geçici dosyalar
vendor/Plugin ya da gem gibi üçüncü parti dosyalar
vendor/assetsÜçüncü parti değerler, CSS, JavaScript dosyaları, ve resimler
README.rdocUygulamanın kısa bir açıklaması
RakefileRake komutuyla kullanılabilen utility işlemler
GemfileBu uygulamada kullanılacak Gem listesi
Gemfile.lockA list of gems used to ensure that all copies of the app use the same gem versions
config.ru Rack middleware için konfigürasyon dosyası
.gitignore Git tarafından dikkate alınmayacak dosyalar paterni



Gemfile içinde uygulamamızda kullanılacak gemlerin bir listesi var. Burada değişik versiyonlarda gemlerden korunmak için otomatik olarak üretilen gem listesini aşağıdaki ile değiştirerek versiyonları sabitleyelim:
source 'https://rubygems.org'

gem 'rails',                '4.2.0'
gem 'sass-rails',           '5.0.1'
gem 'uglifier',             '2.5.3'
gem 'coffee-rails',         '4.1.0'
gem 'jquery-rails',         '4.0.3'
gem 'turbolinks',           '2.3.0'
gem 'jbuilder',             '2.2.3'
gem 'sdoc',                 '0.4.0', group: :doc

group :development, :test do
  gem 'sqlite3',     '1.3.9'
  gem 'byebug',      '3.4.0'
  gem 'web-console', '2.0.0.beta3'
  gem 'spring',      '1.1.3'
end
Gemfile dosyasındaki bu değişiklikleri yaptıktan sonra dosyayı kaydedelim ve konsolda :
$ bundle install
komutunu girip bu listedeki gemlerin yüklenmesini sağlayalım. Hadi bakalım server'ımızı çalıştıralım:
$ rails server
Bunu yeni bir terminal tab'ı açıp yaparsak diğer terminalde komut çalıştırmaya devam edebiliriz. Kendi bilgisayarınızda server çalıştırınca http://localhost:3000/ adresinde uygulamayı görüyorsunuz. Ama C9 editöründe uygulama geliştirirken
$ rails server -b $IP -p $PORT
komutu girilir ve "window - share..." menüsünde "Application" yanındaki linke tıklayarak uygulamanızın çalışan sayfasını görebiliriz.  “About your application’s environment” linkini tıklayıp uygulamanızın kullandığı toollar hakkında kısa bilgi sahibi olabilirsiniz.

Şimdi app/controllers/application_controller.rb dosyasını açalım ve içine "hello" metodunu ekleyelim:
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  def hello
    render text: "hello, world!"
  end
end
uygulamamızın root dosyasının bu metodu çağırması içinse config/routes.rb dosyasında şu ilaveyi yapalım:
Rails.application.routes.draw do
  .
  .
  .
  # You can have the root of your site routed with "root"
  root 'application#hello'
  .
  .
  .
end
Dosyaları kaydettikten sonra uygulamamızı izlediğimiz tarayıcı sayfasını yenileyince "hello, world!" yazısı karşımıza çıkacaktır.






28 Ocak 2015 Çarşamba

Docpad İle Statik Web Sayfası Hazırlamak

Web sitesi hazırlarken çoğu zaman statik bir web sitesi işimizi görecektir, hem server daha ucuz temin edilebilir. Ancak statik web sitesi hazırlarken en büyük sıkıntı, her sayfada yinelemek zorunda kalınılan menüler yerleşimler vs.

Bu konuda kısa bir araştırma yaptım, Docpad uygulamasını erb yapılara benzerliği yüzünden tercih ettim. Hadi beraber tutorial üzerinden gidelim.

Docpad node.js ile çalıştırılıyor , bu yüzden öncelikle sisteminizde node.js yüklü olmalıdır. Tavsiye en son sürüme yükseltip kullanmanız, bende 0.10.35 var.

Docpad Kurulumu

# npm install -g docpad@6.69
 Linux kullanıyorsanız sudo yetki gerekir.

Standart Proje Yapısını Oluşturmak

Web sitemizin içeriğine dalmadan önce proje klasörümüzü Docpad'in standart yapısına uygun olarak üretmemiz gerekiyor. Bunu yapmak için konsolda şunları yazın:
mkdir websitem cd websitem docpad run
ilk kez "docpad run" komutu girildiğinde lisans sözleşmesi çıkar ve "yes" için "y" girmenizi ister. Bu arada "docpad run" komutu sudo olarak çalıştırılmalı. Sonra sizden bir skeleton (iskelet yapı) kalıp seçmenizi ister. Biz "No Skeleton" seçeneği seçeceğiz. Birsürü yazılar geçtikten sonra sorun yoksa tüm klasör ve dosyaları hazırlayıp bir de server otomatik olarak çalışacaktır. Server yayınını http://localhost:9778 portundan yapmaya başlayacaktır. Tarayıcıyı çalıştırırsanız şimdilik sayfa olmadığı için "not found" dönecektir. Server'ı ne zaman durdurmak isterseniz Ctrl+C tuşlamanız yeterli. 


Anasayfa Eklenmesi

Hadi ilk sayfamızı oluşturalım. src/render/index.html dosyasını üretin ve içine şunları yazın:
<html>
<head>
    <title>Hoşgeldiniz! | Websitem</title>
</head>
<body>
    <h1>Hoşgeldiniz!</h1>
    <p>Websiteme hoşgeldiniz!</p>
</body>
</html>

Şimdi tarayıcıda http://localhost:9778 sayfasını yenilerseniz, kodunu yazdığınız sayfa görünür.

"Eee, ne oldu şimdi?, biz zaten html ile sayfa yazıyorduk" diyenler acık sabretsin.


Hakkında Sayfası ve bir Yerleşim Eklenmesi

Şimdi bir de "hakkımızda" sayfası koyalım ki , sitemizi ziyaret edenler kim olduğumuz konusunda bilgileri bizden alsın. Bu amaçla src/render/about.html adında yeni bir html döküman üretiyoruz.
<html>

<head>
    <title>Hakkımda | Websitem</title>
</head>
<body>
    <h1>Ben Kimim?</h1>
    <p>Her türlü program yazmayı severim. <strong>Ha bir de DocPad 
Kullanırım!</strong></p>
</body>
</html>

Yaşasııın, ikinci html sayfayı da yazdık. Tarayıcıda http://localhost:9778/about.html açınca bu sayfayı da görüntüleyebiliriz.

Fakat gördüğümüz gibi bir alt yapı var ve onu tekrarlayıp duruyoruz. 100 tane sayfa olsa biz de bu altyapıyı değiştirmeye kalksak 100 sayfada da değişiklik yapmak zorunda kalacağız. Mesela <head> kısmının içeriğini değiştirmek istiyoruz, bu durumda bütün dosyalarda değişiklik yapmamız gerekecek.

Layout'lar (yerleşimler) tüm dökümanı kapsarlar. Yerleşim değişince tüm dökümanlarda değişim olur. Uygulamamıza bir yerleşim tanımlamak için src/layouts/default.html.eco dosyasını üretip içine şunları yazalım:
<html>
<head>
    <title><%= @document.title %> | Websitem</title>
</head>
<body>
    <h1><%= @document.title %></h1>
    <%- @content %>
</body>
</html>

src/render/index.html
---
title: "Hoşgeldiniz!"
layout: "default"
isPage: true
---

<p>Websiteme hoşgeldiniz!</p>

src/render/about.html
---
title: "Hakkımda"
layout: "default"
isPage: true
---

<p>Her türlü program yazmayı severim. <strong>Ha bir de DocPad Kullanırım!</strong></p>
Ancaaak, server'ı kapatıp açıp sayfalarımıza tekrar baktığımızda yerleşime yazdığımız şeyleri görürüz, beklenen gibi kod çalışmaz. Çünkü daha "eco" yerleşim dosyalarını işleyecek eklentiyi yüklemedik.

Bunun dışında sayfalara "isPage" adında bir özellik ekledik. Şimdilik bu özellik DocPad için birşey ifade etmiyor, ilerde menüler vs. için kullanacağız.

Şablon Motorunun Kurulması

Bu klavuzda şablon motoru olarak eco kullanıyoruz. Şimdi bu plugin için konsolda şunları yazın:
docpad install eco
Plugin'i kurup server'ı tekrar çalıştırınca yerleşim şablon kodumuz çalışmaya başlayacaktır.

Peki neden döküman etiketinde (title) <%= kullandık da içeriği ifade ederken <%- kullandık? Sebebi şu, <%=
ile yazı içersindeki kod olabilecek özel karakterleri ayıklama yaparak yayınlar, <%- ise yazıyı olduğu gibi html koda ekler. Ne demeye çalıştığımı en iyi anlama yolu <%- yerine <%= yazarak "about.html" sayfasına bir bakın.                  


Live Reload (Anında Yükleme) plugin Yüklemek ve Bloklar

Her yaptığımız değişiklik için server'ı durdurup çalıştırmak gerekmesin diye "LiveReload" adında bir plugin var. Bunun sayesinde değişiklik yaptıkça tarayıcıda sayfayı tazelemek değişikliklerin işlenmesi için yeterli olacaktır.
docpad install livereload
Gelelim bloklara, bloklar sayesinde script'leri , stilleri ve metadata'ları sayfalarımıza yükleyebiliriz. Haydi default.html.eco yerleşimine 3 standart bloğu ekleyelim:
<html>
<head>
    <title><%= @document.title %> | Websitem</title>
    <%- @getBlock("meta").toHTML() %>
    <%- @getBlock("styles").toHTML() %>
</head>
<body>
    <h1><%= @document.title %></h1>
    <%- @content %>
    <%- @getBlock("scripts").toHTML() %>
</body>
</html>
Bu satırları ekledikten sonra bloklarda yapacağımız tüm değişiklikler otomatik olarak Live Reload sayesinde yüklenecektir.



Asset Eklemek

Resimler

Sayfamızın başına bir logomuzu ekleyelim. DocPad Logosunu indirin ve src/static/images/logo.gif dosyası olarak kaydedin.
  • src/layouts/default.html.eco
<html>
<head>
    <title><%= @document.title %> | Websitem</title>
    <%- @getBlock("meta").toHTML() %>
    <%- @getBlock("styles").toHTML() %>
</head>
<body>
    <img src="/images/logo.gif" />
    <h1><%= @document.title %></h1>
    <%- @content %>
    <%- @getBlock("scripts").toHTML() %>
</body>
</html>
Sayfayı tazeleyince logonun her iki sayfada da en üste geldiğini görürsünüz.

Stiller

Şimdi de h1 başlıklarımızı kırmızı yapalım. Stillerimizi render klasöründe src/render/styles/style.css dosyasına koyacağız. 
h1 {
  color: red;
}
Sonra da bu stili sayfalarımızda aktif etmek için default.html.eco yerleşim dosyasında şu değişikliği yapalım:
<%- @getBlock("styles").add(["/styles/style.css"]).toHTML() %>
Sayfalarımıza tekrar baktığımızda yeni stilimizin etkili olduğunu görürüz.


Scriptler

Şimdi sıra efektler için biraz JavaScript kullanmakta. Bu amaçla JQuery JavaScript kütüphanesini kullanacağız. 

İlk önce jQuery.js dosyasını indirin ve src/static/vendor/jquery.js olarak kaydedin. Şimdi bu faydalı kütüphaneyi websitemizde scriptlerde kullanacağız.

Sayfa yüklenirken yakışıklı bir efekt ekleyelim.  src/render/scripts/script.js dosyasını oluşturup içine :
(function(){
    $("body").hide().fadeIn(2000);
})();
Şimdi koyduğumuz script dosyalarını websitemiz içersin diye default.html.eco yerleşim dosyasında şu satırı değiştirelim:

    <%- @getBlock("scripts").add(["/vendor/jquery.js", "/scripts/script.js"]).toHTML() %>





Ön İşleyicilerin Faydaları

Ön işleyiciler harika şeyler. Dökümanları birtek dilde yazıp birçok değişik şekillere dönüştürmeye yararlar. En büyük faydası zorunlu olduğunuz bir dili değil hoşunuza giden dili kullanmanıza imkan verirler. 



HTML Önişleyicisi Olarak Markdown Kullanmak

Html kodlayarak bir web sayfası hazırlamak oldukça zor , okunması da zor bir iş. Çok şükür ki Markdown en önemli html önişleyici plugin olarak hizmetimizde. 

Marked Markdown Plugin yükleyerek başlayalım.
docpad install marked
Şimdi daha önce yazdığımız  render/about.html dosyasının adını render/about.html.md olarak değiştirelim. Böylece dosya içeriğinin Markdown için yazıldığını belirtiyoruz. Dosya içeriğini de şöyle düzenleyelim:
Her türlü program yazmayı severim. **Ha bir de DocPad Kullanırım!**
Gördünüz mü bir sürü html tag yerine daha anlaşılabilir sayfa içeriklerini Markdown formatında yazabiliriz. İşte statik web sitesi ile DocPad arasında en önemli farklardan ve avantajlardan biri.

Artık gazı aldık, sırada CSS stilleri kolaylaştırmak var.



CSS Önişleyici Olarak Stylus

Stylus başta gelen CSS önişleyicilerdendir.İşe Stylus Plugin yükleyerek başlayalım:
docpad install stylus
Sonra da  src/render/styles/style.css dosyasının adını src/render/styles/style.css.styl olarak değiştirelim. Neden bunları render klasörüne koyduk ta static klasörüne koymadık demişseniz, cevap burada. Static klasörü içine konan dosyalar önişleyicilerde işlenmeden aynen "out" klasörüne aktarılırlar. Şimdi stil dosyamızın içeriğini şöyle değiştirelim:
h1
  color: red
Sonuç aynı ama stil dosyamızda da işçiliklerden kurtulmaya başladık.




JavaScript Önişleyici Olarak CoffeeScript

Biliyorsunuz bu JavaScript yazımı çok karmaşık. Bir tane noktalı virgül unutursan arada , yandın. Bu karmaşıklığı önlemek için CoffeeScript icat edildi. 

CoffeeScript Plugin yükleyerek başlayalım:
docpad install coffeescript
Ardından  render/scripts/script.js dosyasının adını render/scripts/script.js.coffee olarak değiştirelim.

İçeriğini de şöyle değiştirelim:
$("body").hide().fadeIn(2000)
 Sonuç aynı ama artık CoffeeScript'in avantajları ve ferahlığıyla bir fincan kahve içebiliriz.






Bir Konfigürasyon Dosyası Yardımıyla Şablon Verisi ve Şablon Yardımcıları Eklemek

Konfigürasyon Dosyasının Amacı

DocPad konfigürasyon dosyası , bizim DocPad oluşumu websitemizin konfigüre etmemize yarar. Olayları takip eder ve yararlı faaliyetlerde bulunur.

Örneğin diyelim dökümana title tanımlamadık , bu durumda etiketimiz | Websitem şeklinde olacaktır. Böyle yarım yamalak olacağına etiketin "Benim Websitem" olmasını tercih ederiz. Bu amaçla "default.html.eco" yerleşim dosyamızda etiket satırını şöyle değiştirelim:
    <title><%= if @document.title then "#{@document.title} | Websitem" else "Benim Websitem" %> </title>
Bu kod değişikliği işimizi kısa yoldan halleder. Ama benzer olayların sayısı arttıkça hangisi hangi dosya içindeydi karmaşası başlayacaktır. Bunu önlemek için buna benzer işlemleri tek bir konfigürasyon dosyasına toplayıp sitemize bu dosyadan işlem yapmasını bildirmeliyiz.

DocPad'in tüm uygulama genelinde kullandığı konfigürasyon dosyası projemizin kök klasörü içindeki  /docpad.coffee dosyasıdır. Bu dosya DocPad ilk çalıştığında üretilmiş olmalıdır, eğer yoksa da bu isimde bir dosya üretip içine şunları yazın:
# DocPad Configuration File
# http://docpad.org/docs/config

# Define the DocPad Configuration
docpadConfig = {
 # ...
}

# Export the DocPad Configuration
module.exports = docpadConfig
Dikkat ederseniz bu dosya CoffeeScript ile yazılmış.


Şablon Verilerinin Kullanımı

Şablonlarımızda kullanılan tüm veriler konfigürasyon içinde templateData adıyla tanımlanır. Örneğin @document bizim şablon verimizin bir parçası. Şimdi etiket için bir şablon veri tanımlıyalım. Konfigürasyon dosyası içine şunları yazalım:
# Define the DocPad Configuration
docpadConfig = {
  templateData:
    site:
      title: "Benim Websitem"
}
Şimdi default.html.eco dosyamızı şu hale getirebiliriz:
    <title><%= if @document.title then "#{@document.title} | Websitem" else @site.title %> </title>
Ancak tabii ki biz bu lojiği de konfigürasyon içine kaydırmak isteyebiliriz.




Şablon Yardımcıları ile Lojiği Tanımlamak

Konfigürasyonu CoffeeScript dosyası ile tanımladığımızdan fonksiyon tanımlamalarını da burada yapabiliriz. Bunlara Şablon Yardımcıları (Template Helpers) denir. Bunlar sayesinde lojiği de ayrı olarak konfigürasyona kaydırabiliriz. Şimdi yerleşim dosyasındaki lojiği konfigürasyona taşıyalım. /docpad.coffee dosyasına şunu ekleyelim:
# Define the DocPad Configuration
docpadConfig = {
  templateData:
    site:
      title: "Benim Websitem"
   
    getPreparedTitle: -> if @document.title then "#{@document.title} | Websitem" else @site.title
}
Ardından da default.html.eco yerleşim dosyamızda:
    <title><%= @getPreparedTitle() %> </title>
değişikliğini yaptıkmı ilk şablon yardımcımızı yazmış ve de kullanmış oluruz.







Sayfalarımıza Menü Listeleri Eklemek

Bir menümüz olsa eklediğimiz sayfalar otomatikman menüye eklense ne güzel olur dimi? Tabiiki olur, hadi yapalım.

Yerleşimi Değiştirelim

src/layouts/default.html.eco yerleşim dosyamızı değiştireceğiz. h1 tag'i öncesine şunları ekleyelim:
    <img src="/images/logo.gif" />
    <ul>
        <% for page in @getCollection("html").findAll({isPage:true}).toJSON(): %>
            <li class="<%= if page.id is @document.id then 'active' else 'inactive' %>">
                <a href="<%= page.url %>">
                    <%= page.title %>
                </a>
            </li>
        <% end %>
    </ul>
    <h1><%= @document.title %></h1>
Menü listesi geldi. Tabii liste olarak , biraz CSS ister. Şimdi "isPage" şablon verisinin ne işe yaradığını gördük, o değer "true" olmazsa sayfa menülere çıkmaz.

Burada her sayfa için yerleşim içindeki bu koddan dolayı ayrı ayrı sayfa listesi sorgulanıp menü oluşturulacaktır. Halbuki sayfadan sayfaya geçerken değişen bir bilgi yok, o zaman bu sorgulamayı bir kere yapıp sonuçları sayfa kodunda değerlendirelim.


Sorgunun Konfigürasyon Dosyasında yapılması

DocPad konfigürasyon dosyamıza geri dönelim:
docpadConfig = {

    templateData:
        site:
            title"Benim Websitem"        

    getPreparedTitle: -> if @document.title then "#{@document.title} | Websitem" else @site.title

    collections:
        pages: ->
            @getCollection("html").findAllLive({isPage:true})
}

Sonra da yerleşim dosyamıza gidip "getCollection" satırını düzenliyoruz:
        <% for page in @getCollection("pages").toJSON(): %>

Arada farka dikkat ettiniz mi? Önce "finfAll" şimdi "findAllLive" kullandık. Böyle yapınca sorgumuz bir kere yapılıp sonrasında hep aynı bilgi kullanılıyor.

Sayfalarımız için Default Metadata oluşturmak

Şimdi her sayfaya yerleşim için "layout: default" yazmak istemezsek bunu da konfigürasyon dosyası içinde şöyle yaparız:
docpadConfig = {
    collections:
        pages: ->
            @getCollection("html").findAllLive({isPage:true}).on "add", (model) ->
                model.setMetaDefaults({layout:"default"})

}

Evet, her sayfaya "layout: default" yazmaktan da kurtulduk, başımız tavana erdi.


Benden bu kadar, "out" klasörü içindeki oluşturulan dosyaları herhangi bir web server'a yükledinizmi kullanıma hazır.

31 Aralık 2014 Çarşamba

WxPython Örnekler

İlk önce en basit uygulama :

# -*- coding: utf-8 -*-
import wx

app = wx.App()

frame = wx.Frame(None, -1, "BASİT")
frame.Show()

app.MainLoop()

 önce uygulama tanımlanıyor, sonra frame ile pencere tanımölanıyor. Sonrada frame görünür yapılıp, uygulama başlatılıyor.

Şimdi yazdığımız uygulamanın boyutunu ayarlayan satırlar. Bu sefer biraz daha yapısal bir uygulama yazılımı gösteriyoruz :

# -*- coding: utf-8 -*-
import wx

class Deneme(wx.Frame):
  
  def __init__(self, parent, title):
    super(Deneme, self).__init__(parent, title=title, size=(250, 200))
    
    self.Show()
    
if __name__ == "__main__" :
  app = wx.App()
  Deneme(None, title="Size")
  app.MainLoop()

Önce yukarıda Deneme adında yeni bir sınıfı Frame sınıfından üretip özelliklerinde boyutunu 250x200 pixel olarak ayarlıyoruz, sonra aşağıda uygulamayı çalıştırmaya yönelik kodları koyuyoruz. Bu aşağıdaki kısımda değişiklik yapmadan açılan pencere içeriğini yukarda sınıf tanımında yapmayı planlıyoruz.
Şimdi de ekranı ortalayalım :

# -*- coding: utf-8 -*-
import wx

class Deneme(wx.Frame):
  
  def __init__(self, parent, title):
    super(Deneme, self).__init__(parent, title=title, size=(300, 200))
    
    self.Centre()
    self.Show()
    
if __name__ == "__main__" :
  app = wx.App()
  Deneme(None, title="Merkezde")
  app.MainLoop()

İlk Menümüzü kullanalım bakalım :

# -*- coding: utf-8 -*-
import wx

class Deneme(wx.Frame):
  
  def __init__(self, *args, **kwargs):
    super(Deneme, self).__init__(*args, **kwargs)
    
    self.InitUI()
    
  def InitUI(self):
    menubar = wx.MenuBar()
    filemenu = wx.Menu()
    fitem = filemenu.Append(wx.ID_EXIT, "Çık", "Uygulamadan Çık")
    menubar.Append(filemenu, "&Dosya")
    self.SetMenuBar(menubar)
    
    self.Bind(wx.EVT_MENU, self.OnQuit, fitem)
    
    self.SetSize((300,200))
    self.SetTitle('Basit Menü')
    self.Centre()
    self.Show()
    
  def OnQuit(self, e):
    self.Close()
    
    
    
if __name__ == "__main__" :
  app = wx.App()
  Deneme(None)
  app.MainLoop()

Önce bir MenuBar tanımlıyoruz ki bu menünün tamamını oluşturur. İkinci olarak bir menü tanımlıyoruz, bu da menüde pencerede görünen menü başlıklarıdır. Son olarak menü içine eklenecek bir item tanımlıyoruz. wx.ID_EXIT ile standart uygulama çıkış menüsü item olarak ekleniyor. Menübar'a "Dosya" menüsü eklendikten sonra uygulamamıza işte bu senin menübar'ın diye gösteriyoruz.

Bind komutu ile tanımladığımız menü item tıklama olayını OnQuit metoduna bağlıyoruz. OnQuit metodu tanımlamasına da Close() yazarak uygulamadan çıkılmasını sağlıyoruz. Gerisi bildiğiniz şeyler :



İkonlar ve Kısayollar

# -*- coding: utf-8 -*-
import wx

APP_EXIT = 1

class Deneme(wx.Frame):
  
  def __init__(self, *args, **kwargs):
    super(Deneme, self).__init__(*args, **kwargs)
    
    self.InitUI()
    
  def InitUI(self):
    menubar = wx.MenuBar()
    filemenu = wx.Menu()
    qmi = wx.MenuItem(filemenu, APP_EXIT, "Çı&k\tCtrl+Q")
    qmi.SetBitmap(wx.Bitmap('images/exit.png'))
    filemenu.AppendItem(qmi)
    
    self.Bind(wx.EVT_MENU, self.OnQuit, id=APP_EXIT)
    
    menubar.Append(filemenu, '&Dosya')
    self.SetMenuBar(menubar)
    
    self.SetSize((300,200))
    self.SetTitle('İkonlar Kısayollar')
    self.Centre()
    self.Show()
    
  def OnQuit(self, e):
    self.Close()
    
    
    
if __name__ == "__main__" :
  app = wx.App()
  Deneme(None)
  app.MainLoop()


Ne fark var? bir wx.MenuItem tanımlıyoruz "k" harfine veya Ctrl+Q tuşlamasıyla da bu menü aktivasyonunun çalıştırılabileceği yönünde kısayol tanımları yapıyoruz. Bu hareketler bildiğiniz gibi tüm pencereli programlarda kullandığınız şeyler. Menüye bir de resim tanımlıyoruz. Bu esimleri nerde bulurum derseniz , linux kullananlar "/usr/share/" klasörü içinde "exit.png" aratırsanız diğer uygulamaların kullandığı ikonlardan bulabilirsiniz.findicons.com da bir sürü ikon var mesela böyle internetten de bulunabilir. Daha sonra Bind ile bu menüye "id" si üzerinden OnQuit metodunu bağlıyoruz.

Burada APP_EXIT ismi verilen bir sabit tanımlanmış ve onu kullanarak "id" üzerinden bağlantının nasıl yapıldığı gösterilmiştir. Burada APP_EXIT yerine isterseniz temsil ettiği "1" sayısını da kullanabilirsiniz, bu programın çalışmasını değiştirmez. Burada amaç hem size "id" üzerinden bağlantının nasıl kurulduğunu göstermek, aynı zamanda ilerde bissürü menü elemanınız olunca her birine ayrı numaraları verirken karıştırmamak için tüm "id" numaralandırmalarını programın bir noktasında toplamak.

Şunu unutmayın iş yapan bir uygulama yazmaya kalktığınızda bazen binlerce satır kod yazarsınız , burda kodun içinde kaybolmamak için bu şekil amacı belirten değişken isimleri ile çalışmak çok önemlidir. Değişkenlerin adı ne kadar uzun olursa olsun açıklayıcı olsun, nasıl olsa editörler iki-üç harf yazınca size önceden yazdığınız kelimeleri hatırlatmaya başlıyor.


Altmenüler ve Separatörler

# -*- coding: utf-8 -*-
import wx

class Deneme(wx.Frame):
  
  def __init__(self, *args, **kwargs):
    super(Deneme, self).__init__(*args, **kwargs)
    
    self.InitUI()
    
  def InitUI(self):
    menubar = wx.MenuBar()
    
    filemenu = wx.Menu()
    filemenu.Append(wx.ID_NEW, '&Yeni')
    filemenu.Append(wx.ID_OPEN, '&Aç')
    filemenu.Append(wx.ID_SAVE, '&Kaydet')
    filemenu.AppendSeparator()
    
    imp = wx.Menu()
    imp.Append(wx.ID_ANY, 'Newsfeed listesi al...')
    imp.Append(wx.ID_ANY, 'Bookmark al...')
    imp.Append(wx.ID_ANY, 'Mail al...')
    
    filemenu.AppendMenu(wx.ID_ANY, 'İçeri A&l', imp)
    qmi = wx.MenuItem(filemenu, wx.ID_EXIT, "Çı&k\tCtrl+Q")
    filemenu.AppendItem(qmi)
    
    self.Bind(wx.EVT_MENU, self.OnQuit, qmi)
    
    menubar.Append(filemenu, '&Dosya')
    self.SetMenuBar(menubar)
    
    self.SetSize((300,200))
    self.SetTitle('Alt Menü')
    self.Centre()
    self.Show()
    
  def OnQuit(self, e):
    self.Close()
    
    
    
if __name__ == "__main__" :
  app = wx.App()
  Deneme(None)
  app.MainLoop()



Bu uygulamada xwWidgets'in orjinal tanımlı menüleri ve ikonları kullanılmıştır. Bir listesi için tıklayın. Gördüğünüz gibi alt menü yapmak için yeni bir menü tanımlanıyor be bu menü sanki bir menü item gibi üst menüye ekleniyor. Bir de yeni olarak AddSeparator() komutu ile menüye bir grup ayırıcı yatay separatör konuyor.

Check Menü Elemanı

Üç çeşit menü elemanı vardır :
  1. Normal eleman
  2. Check Eleman
  3. Radio eleman
Şimdiye kadar kullandıklarımız normal menü elemanlarıydı. Şimdi Check menü elemanı örneği :

# -*- coding: utf-8 -*-
import wx

class Deneme(wx.Frame):
  
 def __init__(self, *args, **kwargs):
  super(Deneme, self).__init__(*args, **kwargs)
    
  self.InitUI()
    
 def InitUI(self):
  menubar = wx.MenuBar()
  filemenu = wx.Menu()
  viewmenu = wx.Menu()
    
  self.shst = viewmenu.Append(wx.ID_ANY, 'Statusbar Göster', 
              kind=wx.ITEM_CHECK)
  self.shtl = viewmenu.Append(wx.ID_ANY, 'Toolbar Göster', 
              kind=wx.ITEM_CHECK)
  
  viewmenu.Check(self.shst.GetId(), True)
  viewmenu.Check(self.shtl.GetId(), True)
    
  self.Bind(wx.EVT_MENU, self.ToggleStatusBar, self.shst)
  self.Bind(wx.EVT_MENU, self.ToggleToolBar, self.shtl)
  
  menubar.Append(filemenu, '&Dosya')
  menubar.Append(viewmenu, '&Görüntü')
  self.SetMenuBar(menubar)
  
  self.toolbar = self.CreateToolBar()
  self.toolbar.AddLabelTool(1, '', wx.Bitmap('images/exit.png'))
  self.toolbar.Realize()
  
  self.statusbar = self.CreateStatusBar()
  self.statusbar.SetStatusText('Hazır')
  
  self.SetSize((300,200))
  self.SetTitle('Check Menü Elemanı')
  self.Centre()
  self.Show()
    
 def ToggleStatusBar(self, e):
  if self.shst.IsChecked():
   self.statusbar.Show()
  else:
   self.statusbar.Hide()

 def ToggleToolBar(self, e):
  if self.shtl.IsChecked():
   self.toolbar.Show()
  else:
   self.toolbar.Hide()
  
  
  
if __name__ == "__main__" :
 app = wx.App()
 Deneme(None)
 app.MainLoop()

Oh oh herşey var, statusbar nasıl tanımlanır, toolbar nasıl tanımlanır, check menü elemanları.. Bir önemli ve kritik nokta daha var, bisürü "self" kelimesi neden? InitUi metodu içinde tanımladığımız elemanların özelliklerini diğer metodların içinde değiştirmek istiyoruz, o zaman bu elemanlara metod dışından erişilebilmesi için bunları sınıfa ait değişkenler olarak herbirinin başına "self" kelimesi koyarak tanımlıyoruz. Daha önce de aynı şekilde dışarıda tanımlanmış metodları çağırıyorduk hatırlarsanız.




Context Menü

Context menü sağ tuş tıklayınca çıkan menülere verilen addır:
# -*- coding: utf-8 -*-
import wx

class BenimPopupMenu(wx.Menu):
 def __init__(self, parent):
  super(BenimPopupMenu, self).__init__()
  
  self.parent = parent
  
  mmi = wx.MenuItem(self, wx.NewId(), 'Küçült')
  self.AppendItem(mmi)
  self.Bind(wx.EVT_MENU, self.OnMinimize, mmi)
  
  cmi = wx.MenuItem(self, wx.NewId(), 'Kapat')
  self.AppendItem(cmi)
  self.Bind(wx.EVT_MENU, self.OnClose, cmi)
 
 
 def OnMinimize(self, e):
  self.parent.Iconize()
  
 def OnClose(self, e):
  self.parent.Close()

class Deneme(wx.Frame):
  
 def __init__(self, *args, **kwargs):
  super(Deneme, self).__init__(*args, **kwargs)
    
  self.InitUI()
    
 def InitUI(self):
  self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
  
  self.SetSize((250,200))
  self.SetTitle('Context Menü')
  self.Centre()
  self.Show()
  
 def OnRightDown(self, e):
  self.PopupMenu(BenimPopupMenu(self), e.GetPosition())
  
  
if __name__ == "__main__" :
 app = wx.App()
 Deneme(None)
 app.MainLoop()




Toolbar

# -*- coding: utf-8 -*-
import wx


class Deneme(wx.Frame):
  
 def __init__(self, *args, **kwargs):
  super(Deneme, self).__init__(*args, **kwargs)
    
  self.InitUI()
    
 def InitUI(self):
  
  toolbar = self.CreateToolBar()
  qtool = toolbar.AddLabelTool(wx.ID_ANY, 'Çıkış', wx.Bitmap('images/exit.png'))
  toolbar.Realize()
  
  self.Bind(wx.EVT_TOOL, self.OnQuit, qtool)
  
  self.SetSize((250,200))
  self.SetTitle('Basit Toolbar')
  self.Centre()
  self.Show()
  
 def OnQuit(self, e):
  self.Close()
  
  
if __name__ == "__main__" :
 app = wx.App()
 Deneme(None)
 app.MainLoop()

CreateToolBar ile uygulamanın toolbar'ını tanımlıyoruz, ardından AddLabelTool ile çıkış simgesini toolbarımıza ekliyoruz. En son Realize komutuyla toolbar'ı ekranda gerçekleştiriyoruz.



Enable , disable

Toolbar objelerinin tıklanmasına izin vermek ya da vermemek.

# -*- coding: utf-8 -*-
import wx


class Deneme(wx.Frame):
  
 def __init__(self, *args, **kwargs):
  super(Deneme, self).__init__(*args, **kwargs)
    
  self.InitUI()
    
 def InitUI(self):
  
  self.count = 5
  
  self.toolbar = self.CreateToolBar()
  tundo = self.toolbar.AddLabelTool(wx.ID_UNDO,'',wx.Bitmap('images/undo.png'))
  tredo = self.toolbar.AddLabelTool(wx.ID_REDO,'',wx.Bitmap('images/redo.png'))
  self.toolbar.EnableTool(wx.ID_REDO,False)
  self.toolbar.AddSeparator()
  texit = self.toolbar.AddLabelTool(wx.ID_EXIT,'',wx.Bitmap('images/exit.png'))
  self.toolbar.Realize()
  
  self.Bind(wx.EVT_TOOL, self.OnQuit, texit)
  self.Bind(wx.EVT_TOOL, self.OnUndo, tundo)
  self.Bind(wx.EVT_TOOL, self.OnRedo, tredo)
  
  self.SetSize((250,200))
  self.SetTitle('Enable Disable')
  self.Centre()
  self.Show()
  
 def OnQuit(self, e):
  self.Close()
  
 def OnUndo(self, e):
  if self.count > 1 and self.count <= 5:
   self.count = self.count - 1
   
  if self.count == 1:
   self.toolbar.EnableTool(wx.ID_UNDO,False)
   
  if self.count == 4:
   self.toolbar.EnableTool(wx.ID_REDO,True)
   
 def OnRedo(self,e):
  if self.count < 5 and self.count >= 1:
   self.count = self.count + 1
   
  if self.count == 5:
   self.toolbar.EnableTool(wx.ID_REDO,False)
  
  if self.count == 2:
   self.toolbar.EnableTool(wx.ID_UNDO,True)
  
if __name__ == "__main__" :
 app = wx.App()
 Deneme(None)
 app.MainLoop()

Gördüğünüz gibi dışarıdaki metodlardan içerdeki tollbar'a müdahale ettiğimiz için yine bi sürü self kelimesi.