JavaScript aktifken sayfa daha iyi gözükür.

#2 Go'da Hava Durumu Uygulaması Yazalım

 ·  ☕ 9 dk okuma süresi  ·  🐧 ksckaan1

Önceki yazıda Hava Durumu uygulamamızın başlangıcını yaptık. Yani JSON verimizi nasıl indireceğimizi ve parse edeceğimizi gördük.

Bu bölümde ise Webview kütüphanesini kullarak uygulama penceresini oluşturacağız ve HTML, CSS, JavaScript gibi teknolojiler ile tasarım ve interaktiflik katacağız.

Öncelikle kaynak kodlarımızın düzenli gözükmesi adına main() fonksiyonumuzdaki kodları fonksiyonlar.go altında oluşturacağımız şehirBilgi() adlı fonksiyona taşıyalım. Böylece aşağıdaki gibi bir şehirBilgi() fonksiyonumuz olacak.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func şehirBilgi(şehirİsmi string) {
	
	//Bu kısmı değiştirdik.
	URL := "https://api.weatherapi.com/v1/current.json?key=8421aae0c3c74077b37164423201210&q=" + strings.ToLower(şehirİsmi)
	cevap, _ := http.Get(URL)

	defer cevap.Body.Close()
	jsonVeri, _ := ioutil.ReadAll(cevap.Body)

	//Şehir tipinde şehirVeri değişkeni oluşturalım.
	//Ayrıştırdığımız JSON verisini bu
	//değişkene atayacağız.
	var şehirVeri Şehir

	//Unmarshall fonksiyonunda byte tipindeki jsonVeri'yi
	//şehirVeri nesnesini & ile bellek adresini veriyoruz.
	hata := json.Unmarshal(jsonVeri, &şehirVeri)

	//hata sorgulama
	if hata != nil {
		log.Println(hata)
	}

	//Ekrana şehirVeri'deki ülke bilgisini bastıralım.
	fmt.Println("ülke:", şehirVeri.Knm.Ülke)

	//Diğer Örnekler
	fmt.Println("şehir:", şehirVeri.Knm.İsim)
	fmt.Println("derece:", şehirVeri.Drm.Sıcaklık)
	fmt.Println("metin:", şehirVeri.Drm.HDrumu.Metin)
	fmt.Println("icon_adresi:", şehirVeri.Drm.HDrumu.İkon)
}

Dikkat ettiyseniz şehirBilgi() fonksiyonumuz şehirİsmi adında string tipinde bir değer alıyor. Hemen altında URL değişkeninin içersine API adresimiz ve şehirİsmi parametremiz strings.ToLower() fonksiyonu ile küçük harflere çevrilerek atanıyor.

Böylece aşağıdaki gibi main() fonksiyonu içinde şehirBilgi() fonksiyonumuza parametre vererek çağırabileceğiz. Tam olarak şöyle:

1
şehirBilgi("Trabzon")

Kullandığımız strings.ToLower() fonksiyonu ile Trabzon değerimiz trabzon şeklinde işlenecektir.

Kodlarımızı bir kademe daha düzenli tutmak için main.go dosyamızdaki struct’ları yapilar.go isimli dosya oluşturarak içerisine taşıyalım. yapilar.go dosyamız aşağıdaki gibi olacak.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

type Şehir struct {
	Knm Konum `json:"location"`
	Drm Durum `json:"current"`
}
type Konum struct {
	İsim string `json:"name"`
	Ülke string `json:"country"`
}
type Durum struct {
	Sıcaklık float64    `json:"temp_c"`
	HDrumu   HavaDurumu `json:"condition"`
}
type HavaDurumu struct {
	Metin string `json:"text"`
	İkon  string `json:"icon"`
}

Kodlarımıza bir düzen çektiğimiz göre Webview kodlama işlemlerine başlayabiliriz.

Webview ile Devam Edelim!

Öncelikle Webview kütüphanesine sahip değilseniz, go get github.com/webview/webview komutu ile kurabilirsiniz.

Daha sonra import "github.com/webview/webview" yazarak projenize dahil edebilirsiniz.

main() fonksiyonumuzun içerisine aşağıdakileri yazalım.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
	//Webview nesnesi oluşturuyoruz.
	//Parametre olarak true yazarak debug araçları ile
	//oluşturmasını istiyoruz.
	pencere := webview.New(true)

	//Pencere başlığını girelim.
	pencere.SetTitle("Hava Durumu Uygulamam")

	//Responsive tasarıma uğraşmayacağım için
	//Boyutu değiştirilemeyen pencere istiyorum
	//SetSize(genişlik, yükseklik, pencere-modu)
	pencere.SetSize(400, 300, webview.HintFixed)

	//Gösterilecek adres
	//Bu kısmı sonra değiştireceğiz.
	pencere.Navigate("https://kaanksc.com")

	//Ve son olarak pencereyi başlatıyoruz.
	pencere.Run()

}

Şöyle bir pencere ile karşılaşacağız.

Örnek pencere

Örnek olarak böyle kodlar yazdım. Tabi ki bu kodlar ile işimiz bitmedi. Plan yapalım!

Uygulayacağımız Adımlar

  • Webview penceremizin gösterebilmesi için mantıklı bir sebep olarak bir web sunucuya ihtiyacımız var. Bu sunucumuzu yine Go ile oluşturacağız.
  • Sunucumuzun dosyaları bir klasör içinde olacak ve sunucumuzu bu klasöre bağlayacağız.
  • Daha sonra JavaScript ile Backend-Frontend arası iletişimi sağlayacağız.

Sunucu Oluşturalım

Öncelikle düşünmemiz gereken ilk nokta Webview’in ve web sunucumuzun aynı anda çalışması gerektiği. Bunun için Goroutine’den faydalanacağız. Sağlıklı olması açısından web sunucumuzu Goroutine ile, Webview’i de ana iş parçacığında çalıştıracağız. Eğer bu anlattıklarım size karışık geliyorsa, kod yazma kısmında fikriniz değişecek. Aslında çok basit.

Web sunucu dosyalarımızı barındırmak için projemizin ana dizinine sunucu adında bir klasör oluşturalım. sunucu klasörünün içine ise index.html, style.css ve uygulama.js adında dosyalar oluşturalım. Yani aşağıdaki gibi bir proje yapımız olacak.

.
├── fonksiyonlar.go
├── main.go
├── sunucu
│   ├── index.html
│   ├── style.css
│   └── uygulama.js
└── yapilar.go

sunucu klasörü ile web sunucumuzun içeriğini oluşturacağız.

index.html dosyamızı düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <!-- Style dosyamızı ekleyelim -->
    <link rel="stylesheet" href="./style.css">
</head>
<body>

    <h1>Şehir İsmi girin:</h1><br>

    <!-- Şehir ismini yazacağımızın yazı kutusu -->
    <input type="text" id="sehir">

    <!-- Ve şehir ismini yollayacağımız butonumuz -->
    <button id="onayla">Onayla</button>

    <!-- JavaScript Dosyamızı da ekleyelim -->
    <script src="./uygulama.js"></script>
</body>
</html>

Yukarıdaki gibi minimum seviyede bir arayüz oluşturduk.

Sıra geldi bu web sayfamızı Go ile oluşturacağımız sunucuda göstermeye. main.go dosyamızı aşağıdaki gibi düzenleyelim. Sunucu oluşturacağımız için net/http paketini eklemeyi unutmayın.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
	"net/http"

	"github.com/webview/webview"
)

func main() {

	//Burada hangi klasörü kullanacağımızı belirtiyoruz.
	klasör := http.FileServer(http.Dir("sunucu/"))

	//localhost'un "/" yani anasayfasını klasöre bağlıyoruz.
	http.Handle("/", http.StripPrefix("/", klasör))

    //Web sunucunun hangi portta çalışacağını belirtiyoruz.
    //Başına "go" yazarak asenkron olarak başlattık.
	go http.ListenAndServe(":5555", nil)

	pencere := webview.New(true)
	pencere.SetTitle("Hava Durumu Uygulamam")
	pencere.SetSize(400, 300, webview.HintFixed)

	//Bu kısıma da sunucumuzun adresini ve portunu giriyoruz.
	pencere.Navigate("http://localhost:5555")
	pencere.Run()
}

Yaptıklarımız sonrasında go run . ile kodlarımızı çalıştırdığımızda aşağıdaki gibi bir görünüm ile karşılacaksınız.

Çok çirkin bir tasarım

Sayfa Etkileşimi

Bu çirkin arayüz üzerinde bir etkileşim oluşturabilmek için birazcık JavaScript yazmamız gerekecek.

uygulama.js dosyamızı düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//öncelikle sayfadaki butonu id'sini girerek tanıtıyoruz.
var onaylaButon = document.querySelector("#onayla")

//Bu kısımda onayla butonuna tıklanınca olacakları yazıyoruz.
onaylaButon.addEventListener("click",()=>{
    //yazı kutusundan, onayla butonuna tıklandığı andaki
    //değeri almak istediğimiz için bu fonksiyonun
    //içine yazıyoruz.
    var sehirIsmi = document.querySelector("#sehir").value;
    
    //Ve son olarak şehir ismini uyarı penceresinde göstermesini istedik.
    alert(sehirIsmi)
})

Kodlarımızın çalıştığını test etmek için böyle bir örnek oluşturduk. Tabi ki bu kodlar bizim işimizi görmeyecek. Şehir ismini uyarı kutusunda göstermek yerine şehir ismini Go tarafında gönderip JSON verisini almamız gerekiyor.

Şehir ismini uyarı kutusunda gösterdiğimiz yeri silerek uygulama.js dosyasını aşağıdaki gibi güncelleyelim.

1
2
3
4
5
6
7
8
9
var onaylaButon = document.querySelector("#onayla")

onaylaButon.addEventListener("click",()=>{
    var sehirIsmi = document.querySelector("#sehir").value;

    //JavaScript tarafından tanımlanmamış bir fonksiyonu
    //şehir ismi parametresi ile çağırdık.
    sehirSorgu(sehirIsmi)
})

Fonksiyon Yakalama

Normalde JavaScript yorumlayıcı onayla butonuna tıkladığında hata verecektir. Sebebi de sehirSorgu adında bir fonksiyonun olmamasıdır. Fakat biz Webview’e sehirSorgu fonksiyonunu tanıtıp, bu fonksiyon ile Go tarafında belirli işlemlerin tetiklenmesi için kullanacağız.

Bu işlemi de Bind() fonksiyonu ile yapabiliriz. main.go dosyamızı aşağıdaki gibi düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
	"net/http"

	"github.com/webview/webview"
)

func main() {
	klasör := http.FileServer(http.Dir("sunucu/"))
	http.Handle("/", http.StripPrefix("/", klasör))
	go http.ListenAndServe(":5555", nil)

	pencere := webview.New(true)
	pencere.SetTitle("Hava Durumu Uygulamam")
	pencere.SetSize(400, 300, webview.HintFixed)
	pencere.Navigate("http://localhost:5555")

	//Bu kısma dikkat edelim. Bind() fonksiyonunu mutlaka Run()
	//fonksiyonundan önce yazmalıyız. Yoksa JavaScript fonksiyonlarını
	//daha sonra tanıtamayız.
	//Bind(fonksiyon-ismi, çalışacak-fonksiyon)
	pencere.Bind("sehirSorgu", func(şehirİsmi string) {
		//fonksiyonlar.go'daki fonksiyonumuzu çağıralım.
		şehirBilgi(şehirİsmi)
	})
	pencere.Run()
}

Programımızı çalıştıralım. Şehir ismi yazıp, onayla butonuna bastığımızda komut satırına yazdığımız şehirin hava durumu bilgileri bastırılacaktır.

Şuana kadar yazdığımız programın mantığını daha sade anlatabilmek için tasarım detaylarına çok önem vermemeye çalıştım. Asıl önemli olan kısmı mantığını anlamanız. Geri kalan kısmını da eforsuz ve çirkin tasarımımla anlatmaya çalışacağım.

Sıra geldi, hava durumu bilgilerini komut satırına değil de, asıl gözükmesi gereken yer olan pencere yollamaya. Bu işlemi yapmak için Go tarafından Webview penceresinde JavaScript kodu çalıştırmamıza yarayan Eval() fonksiyonunu kullanacağız.

Uygulama mantığımız ise şöyle olacak:

  • Go tarafından JavaScript tarafına, içerisinde hava durumu bilgileri parametre olarak verilmiş fonksiyon çalıştıracağız.
  • JavaScript tarafında ise bu fonksiyona gelen parametreleri, sayfa tasarımımızdaki belirli alanlarda göstereceğiz.

Öncelikle bu tasarım alanlarını oluşturalım. index.html dosyamızı aşağıdaki gibi düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <!-- Style dosyamızı ekleyelim -->
    <link rel="stylesheet" href="./style.css">
</head>
<body>

    <h1>Şehir İsmi girin:</h1><br>
    <input type="text" id="sehir">
    <button id="onayla">Onayla</button>

    <!-- Hava Durumu Sonucu için alttaki gibi
    bir alan oluşturalım -->
    <div id="sonuc">
        <h1 id="sehirIsmi">Şehir İsmi</h1><br>
        <h2 id="ulkeIsmi">Ülke İsmi</h2><br>
        <p>Sıcaklık: <span id="sicaklik">20</span></p>
        <p>Durum: <span id="durum">Bulutlu</span></p>
        <p><img id="ikon" src=""></p>
    </div>

    <script src="./uygulama.js"></script>
</body>
</html>

HTML sayfamıza eklediğimiz şeyleri <script src="./uygulama.js"></script> kodundan önce eklemeyi unutmayın. Bu koddan sonra eklenenleri JavaScript dosyasında kullanamayız.

Tasarımın daha güzel görünmesi için style.css dosyasını aşağıdaki gibi düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
*{
    margin: 0;
    box-sizing: border-box;
    text-align: center;
}
body{
    padding: 10px;
}
#sonuc{
    margin-top: 5px;
    border-top: 1px solid darkgray;
}

Şöyle bir tasarımımız olacak.

Daha az çirkin bir tasarım

Tabi ki şehir ismi girmeden hava durumu bilgilerinin görünmemesi gerekiyor. Bunun için style.css dosyasında #sonuc bloğunun içerisine display: none; ekliyoruz. Daha sonra Go tarafından hava durumu bilgisini yolladığımızda görünmesini sağlayacağız.

Bu kısımdan sonra Go’dan gelecek veriyi tasarımdaki konumlarına yerleştirecek JavaScript kodunu yazalım. uygulama.js dosyamızın içinde veriGoster() adında fonksiyon oluşturalım.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var onaylaButon = document.querySelector("#onayla")

onaylaButon.addEventListener("click",()=>{
    var sehirIsmi = document.querySelector("#sehir").value;
    sehirSorgu(sehirIsmi)
})

function veriGoster(sehirBilgi, ulkeBilgi, sicaklikBilgi, durumBilgi, ikonBilgi) {
    //Öncelikle index.html'deki elementleri tanıtalım.
    var sonucDiv = document.querySelector("#sonuc")
    var sehir = document.querySelector("#sehirIsmi")
    var ulke = document.querySelector("#ulkeIsmi")
    var sicaklik = document.querySelector("#sicaklik")
    var durum = document.querySelector("#durum")
    var ikon = document.querySelector("#ikon")

     //Ve yerleştirme işlemleri
    sehir.textContent = sehirBilgi;
    ulke.textContent = ulkeBilgi;
    sicaklik.textContent = sicaklikBilgi;
    durum.textContent = durumBilgi;

    var ikonAdres="https:"+ikonBilgi;
    //img etiketindeki src
    ikon.setAttribute("src",ikonAdres);

    //Daha sonra hava durumu verisi bu fonksiyona geldiği için index.html'deki
    //"sonuc" id'li elementi gizli halden çıkaralım.
    sonucDiv.style.display = "block";
}

Go’dan JavaScript’e Veri Gönderelim

İşleyiş adımlarımızın son kısmı olan Go tarafında neler yapacağımıza bakalım.

Yukarıdaki JavaScript kodlarında veriGoster() fonksiyonumuz 5 adet parametre alıyor. Bizim de Go tarafından fonksiyonumuza bu parametreleri yollamamız gerekiyor. Bu durumda da fonksiyonlar.go dosyamızdaki şehirBilgi() fonksiyonu bize bu 5 adet değeri yollaması lazım. JavaScript tarafındaki veriGoster() fonksiyonundaki parametre sırasını göz önünde bulundurarak, Go tarafındaki şehirBilgi() fonksiyonunun bu sıralamaya göre değer döndürmesini sağlayalım.

fonksiyonlar.go dosyasını aşağıdaki gibi düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

//öncelikle fonksiyonun 5 adet değer döndürüceğini belirtiyorum.
func şehirBilgi(şehirİsmi string) (string, string, float64, string, string) {

	URL := "https://api.weatherapi.com/v1/current.json?key=8421aae0c3c74077b37164423201210&q=" + strings.ToLower(şehirİsmi)
	cevap, _ := http.Get(URL)

	defer cevap.Body.Close()
	jsonVeri, _ := ioutil.ReadAll(cevap.Body)
	var şehirVeri Şehir
	hata := json.Unmarshal(jsonVeri, &şehirVeri)
	if hata != nil {
		log.Println(hata)
	}

	//Komut satırına bastırdığımız tarafların kodları sildim.
	return şehirVeri.Knm.İsim, şehirVeri.Knm.Ülke, şehirVeri.Drm.Sıcaklık, şehirVeri.Drm.HDrumu.Metin, şehirVeri.Drm.HDrumu.İkon
}

Artık şehirBilgi() fonksiyonumuz değer döndürüyor. Geri kalan ise dönen değerleri JavaScript koduna göndermek. Az öncede bahsettiğim gibi bu işlemi Eval() fonksiyonu ile yapıyoruz.

main.go dosyamızdaki main() fonksiyonumuzu aşağıdaki gibi düzenleyelim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    package main

import (
	"fmt"
	"net/http"

	"github.com/webview/webview"
)

func main() {
	klasör := http.FileServer(http.Dir("sunucu/"))
	http.Handle("/", http.StripPrefix("/", klasör))
	go http.ListenAndServe(":5555", nil)

	pencere := webview.New(true)
	pencere.SetTitle("Hava Durumu Uygulamam")
	pencere.SetSize(400, 300, webview.HintFixed)
	pencere.Navigate("http://localhost:5555")

	//Bu kısma dikkat edelim. Bind() fonksiyonunu mutlaka Run()
	//fonksiyonundan önce yazmalıyız. Yoksa JavaScript fonksiyonlarını
	//daha sonra tanıtamayız.
	//Bind(fonksiyon-ismi, çalışacak-fonksiyon)
	pencere.Bind("sehirSorgu", func(şehirİsmi string) {
		//fonksiyonlar.go'daki fonksiyonumuzu çağıralım.
		a, b, c, d, e := şehirBilgi(şehirİsmi)
		jsKodumuz := fmt.Sprintf("veriGoster('%s','%s','%.1f','%s','%s')", a, b, c, d, e)
		pencere.Eval(jsKodumuz)
	})
	pencere.Run()
}

Komut satırına go run . yazarak kodlarımızı deneyelim. Şöyle bir görüntü ile karşılaşacağız.

Çirkin ama işe yarar uygulama

Bu işlemlerin sonucunda çirkin bir tasarımı olsa da artık amacına hizmet eden bir uygulamamız oldu. Güle güle kullanın.

Bir sonraki bölümde Statik kütüphanesi ile açıkta kalan sunucu adlı klasörümüzü uygulamanın içine gömmeyi göreceğiz.

Şurada Paylaş