Modern Bir TUI Framework’ü İnşa Ederken Öğrendiğim 7 Şey
(textualize.io)- Textual geliştirme süreci, eski terminal protokolleri üzerinde bile akıcı TUI animasyonları oluşturabildiğini gösteriyor; ancak ekran yenileme yöntemi ve çıktı senkronizasyonunun dikkatle ele alınması gerekiyor
- Titremeyi azaltmak için ekranı temizlemek yerine üzerine yazmak, standart çıktıya tek seferde yazmak ve Synchronized Output protokolünden yararlanmak önemli
- Python’un
dict.items()görünümü ve@lru_cache, render değişikliklerini hesaplamak ve küçük ama sık çağrılan fonksiyonları optimize etmek için kullanışlı; CPython’un C implementasyonu önbellek performansını destekliyor - Değiştirilemezliğe yakın nesneler, Unicode kutu karakteri diyagramları ve
fractions.Fraction, sırasıyla akıl yürütme/önbellekleme/test, dokümantasyonu anlama ve yerleşim hesaplamalarında doğruluğu artırıyor - Terminal Emoji’leri, Unicode sürümü, karakter genişliği ve çoklu kod noktası render farkları nedeniyle öngörülmesi zor; pratikte Unicode 9 düzeyindeki basit Emoji’ler daha kararlı
Terminal animasyonları ve titreme kontrolü
- Modern terminal emülatörleri eski protokoller üzerinde çalışsa da, birçok implementasyon video oyunlarında da kullanılan grafik tekniklerinden yararlanır
- Terminal görsel efektleri titreme veya tearing üretmeye yatkındır; ancak birkaç koşul sağlanırsa akıcı animasyonlar elde edilebilir
- Titremeyi azaltmanın ilk yolu, “clear sonra draw” değil, mevcut içeriğin üzerine yazmaktır
- Ekranı temizledikten sonra yeni içerik eklenirse, boş kareler veya yalnızca kısmen çizilmiş kareler kısa süreliğine görünebilir
- İkinci yol, güncellemeyi standart çıktıya tek bir write ile göndermektir
file.writebirden çok kez çağrılırsa kısmi güncellemeler kullanıcıya görünebilir
- Üçüncü yol, Synchronized Output protokolünü kullanmaktır
- Karenin başlangıcı ve sonu terminale bildirilirse, terminal bunu titremesiz güncellemeler için kullanabilir
- Textual, 60fps’yi temel alır; bunun üzerinde fark her zaman belirgin olmayabilir
- Her animasyon aynı değeri taşımaz
- Soldan kayan bir kenar çubuğu gibi efektler hoş görünebilir, ancak kullanıcı deneyimine gerçek bir katkı sağlamayabilir
- Akıcı kaydırma, uzun metinlerde mevcut konumu korumaya yardımcı olur
- Textual bu nedenle belirli animasyonları kapatacak mekanizmalar eklemeyi planlıyor
DictView ile render değişikliklerini bulmak
- Python
dictyapısınınkeys()veitems()metodları sırasıylaKeysViewveItemsViewdöndürür; bunlar kümeye benzer bir arayüz sunar - Textual’in yerleşim süreci, Widget ile ekran konumları arasındaki eşleşmeyi içeren bir render map üretir
- Önceki yaklaşımda, tek bir Widget’ın konumu değişse bile tüm ekran gereksiz yere yeniden çiziliyordu
- İki
ItemsViewarasındaki simetrik fark kullanılarak yeni eklenen veya değişen öğeler elde edilebilir- Bu işlem C seviyesinde yürütülür
- Textual, CSS özellikleri değiştiğinde ekranın değişen bölgelerini bulup optimize edilmiş güncellemeler için kullanır
lru_cache ve küçük fonksiyon optimizasyonları
functoolsstandart kütüphanesindeki@lru_cache, fonksiyon dönüş değerlerini önbelleğe alan bir dekoratördürmaxsizeayarlanarak önbelleğin sınırsız büyümesi engellenebilir- CPython’daki
lru_cache, C implementasyonu kullandığı için hem cache hit hem de miss durumlarında hızlıdır - Textual içinde yavaş olmayan ama çok sık çağrılan küçük fonksiyonlar vardır ve bunların çoğu önbelleğe almak için uygundu
- Genellikle
maxsizedeğerini 1000–4000 aralığında tutmak, çağrıların çoğunu cache hit hâline getirmek için yeterli oldu - Önbellek kullanırken
cache_info()kontrol edilmelidir- Etkili bir önbellekte
hitssayısınınmissessayısından daha hızlı artması beklenir
- Etkili bir önbellekte
- İki dikdörtgen alanın kesişimini hesaplayan
intersectionmetodu, tek başına ağır bir işlem olmasa da binlerce kez çağrıldığı için önbellekten fayda sağlar
Değiştirilemez nesnelerin getirdiği sadelik
- Python’da tam anlamıyla değiştirilemez nesneler yoktur; ancak tuple,
NamedTupleve frozen dataclass ile pek çok avantaj elde edilebilir - Nesnelerin değiştirilememesi ilk bakışta keyfi bir kısıtlama gibi görünebilir, ancak gerçek kodda çoğu zaman yardımcı olur
- Textual’de değiştirilemez nesneler kullanan kodu akıl yürütmek daha kolaydır; ayrıca önbellekleme ve test açısından da avantajlıdır
- Temel neden, yan etkisiz kod yazmayı kolaylaştırmasıdır
- Bir sınıf örneğini fonksiyona geçirirken yan etkilerden aynı şekilde kaçınmak daha zordur
Unicode sanatı ve dokümantasyon
- Teknik içeriği yalnızca sözle anlatmak zor olduğunda, Unicode kutu karakterleriyle hazırlanmış diyagramlar dokümantasyonu anlamayı ciddi biçimde kolaylaştırır
- Textual’in docstring’lerinde, bir alanı dört alt bölgeye ayıran bir metodu açıklamak için kutu karakterli diyagramlar kullanılıyor
- Bu tür diyagramlar iyi yazılmış bir docstring’in yerini tutmaz; ancak birlikte kullanıldığında kavrayışı artırabilir
- Diyagram hazırlamak için monodraw kullanılıyor
- Monodraw yalnızca macOS içindir
- Başka platformlarda da alternatifler olabilir
fractions.Fraction ile yerleşim hatalarını azaltmak
- Python standart kütüphanesindeki
fractionsmodülü Python 2.6’dan beri mevcut Fraction, sayıları temsil etmenin farklı bir yoludur ve oluşturulduktan sonra float yerine kullanılabilir- Kayan noktalı sayıların iyi bilinen sınırlamaları vardır
- Örneğin
0.1 + 0.1 + 0.1 == 0.3ifadesiFalsedöner
- Örneğin
- Textual’de ekranı çeşitli oranlara bölen yerleşimlerde kayan nokta yuvarlama hataları sorun yaratıyordu
- Ekran genişliğinin 1/3’ünü alan bir panel ve kalan 2/3’ün tekrar bölünmesi gibi durumlarda tek karakterlik boşluklar oluşabiliyordu
- Float yerine
Fractionkullanmak bu tür yuvarlama hatalarını önleyebilir- Üç adet
Fraction(1, 10)toplamıFraction(3, 10)ile aynıdır
- Üç adet
- 24 karakterin 7 parçaya bölündüğü örnekte, float sürümünde bir karakter eksik kalırken Fraction sürümü tüm uzunluğu doldurur
Terminal Emoji’lerinin zorlukları
- Emoji desteği Rich’in ilk günlerinden beri süregelen bir sorundu ve Textual de aynı sorunu devraldı
- Terminalde karakterler genellikle bir sütun veya iki sütun yer kaplar; bazı karakterlerin genişliği ise 0’dır
- CJK karakterleri Batı alfabesine kıyasla iki kat fazla yer kaplar
- Ortalamak veya kutu çizmek gibi temel biçimlendirmeler için, çıktı metninin terminalde kaplayacağı genişliği bilmek gerekir
- Bu nedenle terminalde kaplanan genişlik
len(text)ile anlaşılamaz
- Unicode veritabanında tek genişlikli ve çift genişlikli karakterlerin eşlemesi bulunur
- Rich ve Textual, çıktı verdikleri tüm karakterler için bu veritabanını sorgular
- Bu işlem hafif değildir; ancak mühendislik ve önbellekleme sayesinde yeterince hızlı hâle getirilebilir
- Emoji’ler Unicode veritabanında yer alsa bile, her yeni Unicode sürümünde yeni Emoji’ler eklendiği için sonuçlar öngörülemez olabilir
- Yeni bir Emoji terminalde bir sütun ya da iki sütun kaplayabilir
- Hatta düzgün render edilmeyebilir
- Terminal emülatörünün hangi Unicode sürümünü kullandığını güvenilir biçimde tespit etmenin standart bir yolu yoktur
- Standart bir ortam değişkeni de yoktur
- Belirli dizileri yazdırıp imleç konumunu sormaya dayanan sezgisel yöntemler vardır; ancak testlerde, Unicode veritabanı biliniyor varsayılsa bile terminalin Emoji’leri öngörülemez biçimde render ettiği görüldü
- Çoklu kod noktası kullanan Emoji’ler sorunu daha da karmaşıklaştırır
- Örneğin 👨🏻🦰, 4 kod noktasından oluşur
- Bazı terminaller bunu 4 karakter, 2 karakter, 1 karakter, tek sütun, çift sütun ya da 4 adet
?olarak render edebilir
- Pratikte Unicode 9 Emoji’lerini kullanmak, farklı platformlarda görece daha kararlı sonuç verir
- Yeni Emoji’ler ve çoklu kod noktası kullanan karakterler mevcut terminalde iyi görünse bile, bunlardan kaçınmak daha güvenlidir
Textualize işe alım yapıyor
- Textualize, tarayıcının bazı rollerini üstlenebilecek bir TUI framework’ü geliştirmek için işe alım yapıyor
- İş ilanları Jobs sayfasında görülebilir
1 yorum
Hacker News yorumları
Tüm TUI geliştiricilerinin eninde sonunda Unicode’a çarpması; uluslararası karakterleri ve emojileri düzgün ele alma işinin, çoğu zaman asıl TUI projesi kadar büyük ya da ondan da büyük ayrı bir projeye dönüşmesi ilginç
Aynı şey rivo/tview’de de olmuştu; bunun sonucunda oluşturulan rivo/uniseg paketi sayesinde diğer TUI kütüphanesi bakımcılarının da aynı sorunları yaşadığını gördüm
Sonuçta karakter genişliği standartlaştırılmış değil ve terminaller karmaşık olduğu için herkes kendi çözümünü icat ediyor. Yazının yazarı yalnızca Unicode 9’u destekliyor; oysa Unicode şu an 15.1’de, bu yüzden bir gün belirli bir emojinin ya da uluslararası karakterin yanlış render edildiğine dair şikâyetler geleceği için bunun iyi bir çözüm olduğundan emin olmak zor
Bu tekniği kullanan iki TUI kütüphanesinin bakımını yapıyorum; emoji desteği neredeyse harikaydı. Bunlardan biri uniseg kütüphanesini de kullanıyor
https://mitchellh.com/writing/grapheme-clusters-in-terminals
textual ile ilgili en büyük şikâyetim React gibi olmaya çalışması. Birçok kişinin alışık olduğu popüler bir framework olduğu için neden böyle yaptıklarını anlıyorum, ama kullanıcı arayüzü oluşturmanın iyi bir yolu olduğunu düşünmüyorum
Reaktif tasarımın kendisi kanıtlanmış bir yol ve iyi çalıştığı bilinen bir yaklaşıma sistem tasarımını dayandırmak proje riskini azaltmanın iyi bir yolu olabilir
Ama üstüne bir de CSS’in bir türevini kullanmak bana fazla geliyor. React modeli standartlaştırılmış bileşenleri tercih ederken CSS kavramlarını birçok açıdan zaten kırdı; geliştiricilerin bileşenleri CSS ile özelleştirmesi, sağlam bir mimariden çok React’in evriminin bir yan etkisi gibi görünüyor
En son denediğimde CSS kullanmak zorundaydım ve iyi standart bileşenler olmadığı için kendim yapmak zorunda kalmıştım. Bileşenin temiz biçimde kapsüllenmiş tek bir Python sınıfı olması yerine, resmi dokümanlar liste bileşeni oluşturup onu harici bir stil sayfasıyla süsleme şeklindeydi
Python’da bir şeyi yapmanın açık bir yolu, mümkünse tam olarak tek bir yolu olmalı. React’i fazla yakından takip ederken JavaScript topluluğunun en kötü alışkanlıklarını da kopyalıyormuş gibi görünüyor
İyi standart bileşenler olmadığına bakmanızın üzerinden de epey zaman geçmiş gibi: https://textual.textualize.io/widget_gallery/
Kendi bileşenlerimi yapmam gerekmedi ve CSS’i zaten biliyordum. Aksine, küçük yerleşim farkları ve CSS’ten ayrıldığı noktalar bazen kafa karıştırıcıydı
Normal bir GUI penceresinin içindeki bileşenlerden birinin terminal penceresi olduğu bir yapı hayal edin. Böyle bir şey var mı, yoksa sadece sabit genişlikli yazı tipli bir metin görünümü mü kullanmak gerekir merak ediyorum
Bu TUI güzel görünüyor ama gerçekten kullanıp üstüne para ödeyeceğim bir durumu hayal etmek zor. Belki doğru ortamda değilimdir, ama deneyimime göre insanlar ya gerçekten minimal araçlarla yetiniyor ya da kullanıcıyı doğrudan GUI ile memnun etmeye çalışıyor
Örneğin yazıdaki YouTube bağlantısı, bir hücreyi vurgulayan bir tablo gösteriyordu; bunun neden TUI ile yapılması gerektiğini anlamıyorum. Vurgulanan aktif hücreyi taşımak gerekiyorsa sonunda başka pek çok işleve de ihtiyaç duyulur ve nihayetinde doğru düzgün bir GUI gerekir gibi
Ancak “doğru düzgün GUI”nin ne olduğu konusunda pek emin değilim. Terminal için yaygın kullanılan açık standartlar var; bu protokoller herhangi bir GUI framework’ünden daha standart, daha birlikte çalışabilir ve tüm sistemlerde destekleniyor
Buna video gibi unsurlar eklenip bazı boşluklar kapatılırsa, tarayıcıyla rekabet etmek ya da tüm bilişim deneyimini terminale taşımak da makul görünüyor
Elbette terminal video eklerse “doğru düzgün GUI”nin render pipeline’ını kopyalamış olur; ama bu terminal deneyimini iyileştirmek demek ve büyük başarı yakalarsa GUI’ye UNIX tarzı sadelik getirmek olarak da görülebilir
Çalışanlar klavye kısayollarıyla çok verimli çalışıyor; biri AS400’ü modern bir web uygulamasıyla değiştirmeye her kalktığında üretkenlik 100 kat düşüyor
Bu, Vim/Emacs ustası ile görüntüsü cilalı ama klavye kısayolu olmayan GUI arasındaki fark gibi bir durum
Kullanıcıyı sunucuya SSH ile bağlatıp TUI uygulamasını shell gibi kullandırabilirsiniz; bunun biraz zorlama bir senaryo olduğunu kabul ediyorum
Kişisel olarak Textual’ı kullanmak biraz tuhaf geldi ama ncurses’tan iyiydi. Yine de istediğim sonucu vermedi. Eski mainframe tarzı TUI uygulamaları son derece verimli hissettiriyordu
Bazen iyi çalışır; örneğin yüksek bant genişlikli bir bağlantıda X forwarding kullanmak gibi, ama bu sınırlı bir durumdur. Başka durumlarda GUI tamamen hantallaşır
Metin tabanlı GUI iki tarafın avantajlarını alabilecek gibi görünüyor
“İlk püf noktası silmeyip üzerine yazmak” yaklaşımı, DirectX öncesi oyunların yazılma biçimiyle aynı
Frame buffer’a doğrudan yazılırdı; tamamını silip yeniden çizmek yerine değişen kısımlar ve onların çevresi/altı yeniden çizilirdi. Çünkü tüm ekranı zamanında güncelleyecek vakit yoktu
Birincisi başka bir buffer’a yazıp, frame sonunda tüm buffer’ı tek seferde kopyalayarak her pikselin yalnızca bir kez değişmesini sağlamaktı. Bu buffer genelde video RAM değil, normal RAM de olabilirdi
İkincisi, video RAM içindeki başka bir buffer’a yazıp grafik donanımı register’ını değiştirerek monitörde gösterilecek piksellerin o buffer’dan üretilmesini sağlamaktı
Ödünleşimler farklıydı. Tüm buffer’ı kopyalamak pahalıydı, adres register’ını değiştirmek ucuzdu. Ama register ayrıntıları donanıma bağımlı olabiliyordu ve düzgün bir grafik sürücüsü framework’ü de yoktu
Basitçe “buffer flipping” yapmak için ekran dışı buffer’a render ederken de pikselleri video RAM’e göndermek gerekiyordu; hatırladığım kadarıyla video RAM erişimi normal RAM’den daha yavaştı. Fiilen NUMA benzeri bir yapı olduğundan, piksellerin ne kadar sık yeniden çizildiğine bağlı olarak son kopya dahil olsa bile normal RAM’de render etmek toplamda daha hızlı olabiliyordu
Yine de terminale frame’in başlangıcını ve sonunu bildirebilme yeteneğinin çok daha güçlü olduğunu düşünüyorum
Yazılım mühendislerinin TUI’ye neden bu kadar ilgi duyduğunu gerçekten bilmiyorum. İyi komut satırı programlarını severim ama TUI bana çekici gelmiyor
Üstelik hacker havası var. Stil içerikten güçlü biçimde ayrılmış değil ve ayarlanabilir stil seçenekleri de sınırlı; bu yüzden TUI uygulamaları genellikle tek bir stile yakınsıyor. O stil de hacker filmlerini ve havalı bilimkurguyu çağrıştırıyor
Kurumsal tasarımcıların sevdiği bir alan değil; şirketler de TUI’yi dayanak göstererek kurumlara ya da tüketicilere satış kapatmıyor. Böyle ticari baskılar olmadığı için TUI, geliştiriciler tarafından geliştiriciler için tasarlanıyor
Bu yüzden TUI mem’i kendi kendini güçlendiriyor. Geliştiriciler TUI’leri görüp kullandıkça bunun genellikle geliştiriciler düşünülerek yapılmış araçlar olduğunu hissediyor ve tekrar kendi TUI’lerini yapmak istiyor
Çapraz platform desteği neredeyse bedava; piksel mükemmelliğinde bir GUI yapmak gerekmiyor, zaten mümkün de değil; ikon ya da grafik de gerekmiyor, çünkü grafik gösteremiyorsunuz
Tercih ettiğiniz yazı tipini kullanan terminal emülatörünü kullanmaya devam ederken GUI benzeri bir arayüze sahip olabiliyorsunuz
Şimdiye kadar kullandığım TUI uygulamalarının hepsinin tepki süresi düşüktü. GUI uygulamaları da çoğunlukla hızlıdır, ama insanı bunaltacak kadar yavaş olan uygulamaların hepsi GUI uygulamalarıydı
Monodraw alternatifi olarak ascii-draw var
https://github.com/Nokse22/ascii-draw
TurboVision’dan sonra gördüğüm neredeyse her şey ilham almış olsa da pratikte olgunluktan yoksundu. Toolkit’i gerçekten kullanmaya çalışınca sevimli ama yarım kalmış gibi hissettiriyordu
Başka bir deyişle, gördüğüm birçok TUI framework’ünün “güçlü görüşlere sahip” framework’ler olduğunu da söyleyebilirim
Belki ben kalın kafalı ve yaratıcılıktan yoksunumdur ama 90’larda DOS’tan Linux’a geçtikten sonra B800 ile çalıştığım zamanlardaki kadar üretken hiç olmadım
Çift tamponlamanın gerekliliğini, yerel doğrudan metin modu ile terminal arasındaki farkı ve escape sequence’leri anlamam uzun sürmüş olabilir. Yine de ncurses ile doğrudan bir şey yapmaya her çalıştığımda, belirgin bir tatminsizlik yüzünden neredeyse vazgeçtim. B800’ün basit idealiyle yapılabilenlerden tamamen farklıydı
Bakış açımın koca bir şehir kadar kapalı olup olmadığını bilmiyorum ama message pump sonrasındaki GUI framework’lerinin hepsi bana bir türlü anlaşılmaz geldi
Özellikle nesne yönelimli framework’lerin çok iş parçacıklı UI sorununu gerçekte nasıl çözdüğünü anlayamadım. Win32’de ana renderer’ı bir thread’e koyuyor, yardımcı renderer’lar da yanda modeli oluşturup yalnızca farkları güncelliyordu. Kod da öyle başa çıkılamayacak kadar büyümüyordu
kitty çalıştıracaksanız, ondan çok daha fazlasını yapabilirsiniz: https://m.youtube.com/watch?v=ft1Q-DwGWIs
https://notcurses.com/
İkisi de grafikleri göstermek için aynı protokolü kullanabiliyor. Gerçekten grafik gerekiyorsa terminal neredeyse hiçbir zaman doğru çözüm değildir. Yine de ikonlar gibi küçük şeylerde bazen işe yarar
İşe alım yapıyor olmaları ilginç. TUI framework ile nasıl para kazanmayı planladıklarını merak ediyorum
Bilgi olarak, derlenen yaklaşık 12 TUI kütüphanesini değerlendirdim ve FTXUI en kullanımı kolay ve kararlı olanıydı
https://github.com/ArthurSonzogni/FTXUI
Hem klavye hem fare destekleyen etkileşimli panolar oluşturmak için iyi bir araç