Python ile Terminalde Canlı İnternet Hız İzleyici

Kutay Utku
9 dk okuma

Bilgisayarınızın o an ne kadar veri indirip yüklediğini merak ettiğiniz olmuştur. Görev Yöneticisi’ndeki ağ grafiği çoğu zaman yetersiz kalır; ne geçmiş ortalamayı gösterir ne de terminalde çalışırken göz ucuyla takip etmenize izin verir. Bu yazıda, tamamen Python ile yazılmış, terminal (konsol) içinde çalışan, renkli ve grafikli bir download/upload izleyici olan download_upload.py’yi tanıtıyor ve nasıl kurup çalıştıracağınızı adım adım anlatıyoruz.

  1. Araç Ne Yapıyor?

download_upload.py, işletim sisteminin ağ sayaçlarını (psutil.net_io_counters) her saniye okuyarak o saniye içinde kaç byte indirildiğini ve kaç byte yüklendiğini hesaplar. Elde ettiği bu verilerle:

  • Anlık indirme ve yükleme hızını insan tarafından okunabilir birimlerle (b, Kb, Mb, Gb) gösterir,
  • Hıza göre renk değiştiren bir doluluk çubuğu (yeşil / sarı / kırmızı) çizer,
  • Toplam çalışma süresi, toplam indirilen/yüklenen veri ve ortalama hızları hesaplar,
  • Son 50 saniyelik indirme ve yükleme geçmişini iki ayrı çubuk grafik (histogram) olarak ekrana çizer.

Tüm bu arayüz, Python’un standart kütüphanesindeki curses modülü kullanılarak terminal içinde canlı olarak güncellenir; ekstra bir GUI kütüphanesine ihtiyaç duymaz.

  1. Gereksinimler

Aracı çalıştırmadan önce bilgisayarınızda Python kurulu olması gerekir (Python 3.8 ve üzeri önerilir). Ayrıca iki bağımlılık kurulmalıdır:

python -m pip install psutil windows-curses

Not: curses modülü Linux ve macOS’ta Python ile birlikte gelir, fakat Windows’ta gelmez. Windows’ta terminal arayüzünün çalışabilmesi için windows-curses paketinin ayrıca kurulması şarttır; aksi halde “No module named ‘_curses'” hatası alırsınız.

  1. Kurulum ve Çalıştırma Adımları

1) Aşağıdaki kodu download_upload.py adıyla bir dosyaya kaydedin (masaüstünde veya istediğiniz bir klasörde).
2) Komut istemini (PowerShell / cmd) açıp dosyanın bulunduğu klasöre gidin.
3) Gerekli paketleri kurun: python -m pip install psutil windows-curses
4) Aracı çalıştırın:

python.exe .\download_upload.py

Bu komutu çalıştırdığınızda terminal ekranınız temizlenir ve karşınıza aşağıdaki bölümlerden oluşan canlı bir panel gelir:

  • Üstte başlık ve o anki tarih/saat,
  • Anlık DOWNLOAD ve UPLOAD hızları ile renkli doluluk çubukları,
  • Çalışma süresi, toplam indirilen/gönderilen veri ve ortalama hız istatistikleri,
  • Son 50 saniyeyi kapsayan iki adet dikey çubuk grafik (biri indirme, biri yükleme için),
  • Alt kısımda kısayol bilgisi ve renk açıklaması.

Panel her saniye otomatik olarak yenilenir. Çıkmak için klavyeden Q veya q tuşuna basmanız yeterlidir; program curses ekranını düzgünce kapatıp terminale “Kapatildi.” mesajını yazdırır.

  1. Kod Nasıl Çalışıyor? (Kısa Teknik Özet)
  • bytes_to_human(): Byte cinsinden bir sayıyı b/Kb/Mb/Gb birimlerine çeviren yardımcı fonksiyon.
  • hiz_cubugu(): Anlık hızı, o ana kadarki en yüksek hıza (max_val) oranlayarak dolu (█) ve boş (░) karakterlerden oluşan bir çubuk üretir.
  • grafik_ciz(): deque içinde tutulan son 50 saniyelik veriyi, curses ekranına satır satır dikey çubuklar (▌) çizerek bir histograma dönüştürür.
  • draw(): curses.wrapper() ile çağrılan asıl döngü. Her saniyede bir net_io_counters() farkını alarak o saniyelik indirme/yükleme miktarını hesaplar, istatistikleri günceller ve tüm arayüzü yeniden çizer.
  • main(): curses.wrapper(draw) çağırarak terminali “raw” moda alır, program bitince (Q tuşu veya Ctrl+C) terminali normal haline güvenli şekilde geri döndürür.
  1. Geliştirme Fikirleri

Aracı temel haliyle kullanabileceğiniz gibi, ihtiyacınıza göre şu şekillerde geliştirebilirsiniz:

  • Belirli bir hız eşiğini aştığında (örneğin 50 Mb/s üzeri) sistem bildirimi veya ses uyarısı ekleyebilirsiniz.
  • İstatistikleri bir CSV veya SQLite dosyasına kaydederek gün/hafta bazlı ağ kullanım raporları oluşturabilirsiniz.
  • Birden fazla ağ arayüzünü (Wi-Fi, Ethernet, VPN) ayrı ayrı izlemek için psutil.net_io_counters(pernic=True) parametresini kullanabilirsiniz.
  • GECMIS sabitini (şu an 50) değiştirerek grafiklerde daha uzun veya daha kısa bir geçmiş penceresi gösterebilirsiniz.
  • curses yerine rich veya textual kütüphaneleriyle daha modern bir terminal arayüzüne taşıyabilirsiniz.

Sonuç

download_upload.py, tek bir Python dosyasıyla, harici bir GUI’ye ihtiyaç duymadan terminalde canlı ve görsel olarak zengin bir ağ trafiği izleyicisi sunuyor. psutil ile sistem verilerini okuyup curses ile bunları anlık olarak ekrana çizen bu küçük araç, hem günlük kullanım için pratik hem de kendi terminal araçlarınızı geliştirmek isteyenler için iyi bir başlangıç noktası.

Aracın Tam Kodu

import psutil
import time
import curses
from datetime import datetime
from collections import deque

GECMIS = 50

def bytes_to_human(n):
    if n < 1024:
        return f"{n:.0f} b"
    elif n < 1024 * 1024:
        return f"{n/1024:.1f} Kb"
    elif n < 1024 * 1024 * 1024:
        return f"{n/1024/1024:.2f} Mb"
    else:
        return f"{n/1024/1024/1024:.2f} Gb"

def hiz_cubugu(val, max_val, genislik=20):
    if max_val == 0:
        dolu = 0
    else:
        dolu = int((val / max_val) * genislik)
    dolu = min(dolu, genislik)
    return "█" * dolu + "░" * (genislik - dolu)

def grafik_ciz(stdscr, baslangic_row, baslangic_col, veriler, genislik, yukseklik, renk):
    if not veriler:
        return
    max_val = max(veriler) if max(veriler) > 0 else 1
    satirlar = list(veriler)[-genislik:]
    for x, val in enumerate(satirlar):
        bar = int((val / max_val) * yukseklik)
        for y in range(yukseklik):
            ekran_row = baslangic_row + (yukseklik - 1 - y)
            ekran_col = baslangic_col + x
            try:
                if y < bar:
                    stdscr.addstr(ekran_row, ekran_col, "▌", curses.color_pair(renk) | curses.A_BOLD)
                else:
                    stdscr.addstr(ekran_row, ekran_col, " ", curses.color_pair(5))
            except:
                pass

def draw(stdscr):
    curses.start_color()
    curses.use_default_colors()
    curses.init_pair(1, curses.COLOR_CYAN,    -1)
    curses.init_pair(2, curses.COLOR_GREEN,   -1)
    curses.init_pair(3, curses.COLOR_YELLOW,  -1)
    curses.init_pair(4, curses.COLOR_RED,     -1)
    curses.init_pair(5, curses.COLOR_WHITE,   -1)
    curses.init_pair(6, curses.COLOR_MAGENTA, -1)
    curses.init_pair(7, curses.COLOR_BLUE,    -1)
    curses.curs_set(0)
    stdscr.nodelay(True)

    dl_gecmis = deque(maxlen=GECMIS)
    ul_gecmis = deque(maxlen=GECMIS)

    toplam_dl = 0
    toplam_ul = 0
    max_dl    = 1
    max_ul    = 1
    baslangic = time.time()
    net1      = psutil.net_io_counters()

    while True:
        time.sleep(1)
        net2 = psutil.net_io_counters()
        dl   = net2.bytes_recv - net1.bytes_recv
        ul   = net2.bytes_sent - net1.bytes_sent
        net1 = net2

        dl_gecmis.append(dl)
        ul_gecmis.append(ul)
        toplam_dl += dl
        toplam_ul += ul
        if dl > max_dl: max_dl = dl
        if ul > max_ul: max_ul = ul

        gecen   = int(time.time() - baslangic)
        ort_dl  = toplam_dl // max(1, gecen)
        ort_ul  = toplam_ul // max(1, gecen)

        stdscr.erase()
        h, w = stdscr.getmaxyx()
        row  = 0

        def yaz(metin, renk=5, bold=False, col=0):
            nonlocal row
            if row >= h - 1:
                return
            attr = curses.color_pair(renk)
            if bold:
                attr |= curses.A_BOLD
            try:
                stdscr.addstr(row, col, str(metin)[:w-1-col], attr)
            except:
                pass
            row += 1

        def bos():
            yaz("")

        def baslik_cizgi():
            yaz("╔" + "═" * (min(w-2, 76)) + "╗", 1, bold=True)

        def alt_cizgi():
            yaz("╚" + "═" * (min(w-2, 76)) + "╝", 1, bold=True)

        def orta_cizgi():
            yaz("╠" + "═" * (min(w-2, 76)) + "╣", 1, bold=True)

        def ince_cizgi():
            yaz("  " + "─" * (min(w-4, 74)), 1)

        # ══ BASLIK ══
        baslik_cizgi()
        baslik = f"  ▶  DOWNLOAD / UPLOAD IZLEYICI  ◀     {datetime.now().strftime('%d.%m.%Y   %H:%M:%S')}"
        yaz(baslik, 1, bold=True)
        orta_cizgi()

        # ══ ANLIK HIZ ══
        dl_renk = 2 if dl < 1*1024*1024 else (3 if dl < 10*1024*1024 else 4)
        ul_renk = 2 if ul < 1*1024*1024 else (3 if ul < 10*1024*1024 else 4)

        yaz(f"  📥 DOWNLOAD   {bytes_to_human(dl):>10}/s    [{hiz_cubugu(dl, max_dl, 25)}]    Maks: {bytes_to_human(max_dl)}/s", dl_renk, bold=True)
        yaz(f"  📤 UPLOAD     {bytes_to_human(ul):>10}/s    [{hiz_cubugu(ul, max_ul, 25)}]    Maks: {bytes_to_human(max_ul)}/s", ul_renk, bold=True)
        ince_cizgi()

        # ══ ISTATISTIK ══
        dakika = gecen // 60
        saniye = gecen % 60
        yaz(f"  ⏱  Calisma Suresi    :  {dakika} dakika  {saniye} saniye", 5)
        yaz(f"  ⬇  Toplam Alinan     :  {bytes_to_human(toplam_dl)}", 2)
        yaz(f"  ⬆  Toplam Gonderilen :  {bytes_to_human(toplam_ul)}", 3)
        yaz(f"  ∅  Ort. Download     :  {bytes_to_human(ort_dl)}/s", 2)
        yaz(f"  ∅  Ort. Upload       :  {bytes_to_human(ort_ul)}/s", 3)
        orta_cizgi()

        # ══ GRAFiKLER ══
        grafik_y   = 8
        grafik_x   = min(GECMIS, w - 30)
        sol_bosluk = 4

        # Download grafigi baslik
        yaz(f"  📊 DOWNLOAD GRAFiGi  (Son {grafik_x} saniye)", 2, bold=True)

        # Y ekseni etiketleri + grafik
        dl_grafik_row = row
        for i in range(grafik_y):
            try:
                oran  = 1 - (i / (grafik_y - 1))
                etiket = bytes_to_human(max_dl * oran)
                stdscr.addstr(row + i, 0, f"{etiket:>10}", curses.color_pair(2))
            except:
                pass

        grafik_ciz(stdscr, dl_grafik_row, sol_bosluk + 10, dl_gecmis, grafik_x, grafik_y, 2)
        row += grafik_y

        # X ekseni
        try:
            stdscr.addstr(row, sol_bosluk + 10, "└" + "─" * grafik_x, curses.color_pair(1))
            stdscr.addstr(row, sol_bosluk + 10 + grafik_x - 6, "Simdi▶", curses.color_pair(1))
        except:
            pass
        row += 1
        bos()

        # Upload grafigi baslik
        yaz(f"  📊 UPLOAD GRAFiGi  (Son {grafik_x} saniye)", 3, bold=True)

        ul_grafik_row = row
        for i in range(grafik_y):
            try:
                oran  = 1 - (i / (grafik_y - 1))
                etiket = bytes_to_human(max_ul * oran)
                stdscr.addstr(row + i, 0, f"{etiket:>10}", curses.color_pair(3))
            except:
                pass

        grafik_ciz(stdscr, ul_grafik_row, sol_bosluk + 10, ul_gecmis, grafik_x, grafik_y, 3)
        row += grafik_y

        try:
            stdscr.addstr(row, sol_bosluk + 10, "└" + "─" * grafik_x, curses.color_pair(1))
            stdscr.addstr(row, sol_bosluk + 10 + grafik_x - 6, "Simdi▶", curses.color_pair(1))
        except:
            pass
        row += 1

        # Alt bilgi
        bos()
        alt_cizgi()
        yaz("  [Q] Cikis   |   Her saniye guncellenir   |   Yesil=Normal  Sari=Yuksek  Kirmizi=Cok Yuksek", 3)
        alt_cizgi()

        stdscr.refresh()

        key = stdscr.getch()
        if key in [ord('q'), ord('Q')]:
            break

def main():
    try:
        curses.wrapper(draw)
    except KeyboardInterrupt:
        pass
    print("\nKapatildi.")

if __name__ == "__main__":
    main()

💬 Yorum Bırak