Son birkaç haftadır Python için bir Sürükle ve Bırak oluşturucu üzerinde çalışıyorum.
Bunu şu adreste kontrol edebilirsiniz:
Kaynak kodu:
İnşaatçı ne yapabilir?
Kısaca, Python için hızlı bir şekilde kullanıcı arayüzü oluşturmanıza ve Tkinter ve customtkinter dahil olmak üzere birden fazla kütüphane/çerçevede kullanıcı arayüzü kodu oluşturmanıza yardımcı olabilir. Daha fazla bilgi için şuraya bakabilirsiniz:
Ama sadece bir proje başlatmak istemiyorum, aynı zamanda deneyimimi sizinle paylaşmak istiyorum. Bu blogda, düşünce sürecimi ve uygulamayı nasıl oluşturduğuma dair üst düzey bir genel bakışı ele alacağım.
Yaygın inanışın aksine, Python genellikle hızlı uygulamalar oluşturmak için kullanılır, özellikle veri bilimi, otomasyon, komut dosyası görevleri vb. alanlarda çalışan geliştiriciler arasında popülerdir. Birçok dahili araç ve GUI, özellikle bilimsel ve araştırma ortamlarında, basitliği ve Tkinter, PyQt ve diğerleri gibi çerçevelerin kullanılabilirliği nedeniyle Python ile oluşturulur.
Şimdi, web için çok sayıda Sürükle ve bırak oluşturucu vardı, ancak Python GUI'leri için çok azdı, özellikle de tkinter için. Birkaç tane gördüm, ancak sorun şu ki çok sınırlı sayıda widget'ları vardı veya Python'da kullanıcı arayüzü geliştiriyorsanız ideal olmayan XML formatında kod üretiyorlardı.
Yani, ilk başta sadece Tkinter için uygun bir sürükle ve bırak kullanıcı arayüzü oluşturucusu oluşturmak istedim.
İdeal bir GUI oluşturucu fikri üzerinde durmaya devam ettim (kasıtlı bir kelime oyunu değil). Canva'nın kullanıcı arayüzünden ilham aldım ve GUI'mi ideal hale getirecek birkaç özellik buldum.
Bu yüzden Temmuz ayının sonuna doğru proje üzerinde çalışmaya başlamaya karar verdim
Başlangıçta tkbuilder olarak adlandırılıyordu, bu da Tkinter UI kütüphanesi için bir GUI oluşturucu olduğunu gösteriyordu.
Ancak, fark ettiyseniz aynı fikri birden fazla Python GUI framework'ünü ve kütüphanesini destekleyecek şekilde de genişletebilirim, çünkü her şey bir eklenti gibi yapılmış ve tam olarak yapmayı planladığım şey buydu.
İlk sürüm için, kullanıcıları bunaltacak çok fazla özellik eklemek istemedim. Bunu, kullanan kişilerin geri bildirimlerine dayanarak oluşturmak istedim. Bu şekilde, insanların istemediği şeyleri oluşturmak için zaman kaybetmiyorum.
En başından beri bir arka uç veya herhangi bir kayıt formu olmamasına karar verdim. Bu şekilde hem benim için hem de onu kullanan kullanıcılar için geliştirme çok daha basit oluyor. Sadece insanların başlayabileceği basit bir ön uç istiyordum.
Evet, bu üzerinde epeyce düşündüğüm bir şeydi, Python için çoğu GUI oluşturucu Python kullanılarak oluşturulmuştu. Python için ilk tercihim PySide'dı.
PyQt/Pyside kullanarak oluşturduğum en karmaşık GUI tabanlı uygulama şuydu:
Ancak ilk versiyonu derlemek için Python kullanmanın sınırlamalarını kısa sürede fark ettim.
Typescript de bir seçenekti, ancak Typescript'i her zaman çok ayrıntılı buldum
Bunlar ilk dikkatimi çeken şeylerdi, dolayısıyla ilk tercihim JS kullanmak oldu.
Not: Daha sonra TS ile başlamadığıma pişman oldum ama bu başka bir yazının konusu.
En rahat ettiğim framework benzeri kütüphane React.js'dir, ancak bir soyutlama oluşturmak sınıfların kullanılmasını gerektirir ve kancaların tanıtılmasından bu yana bu önerilmemektedir.
Bir çerçeve kullanmamanın sorunu, her şeyi kendim inşa etmek zorunda kalmam ve React'in sunduğu geniş bileşen kütüphanelerine erişimimin olmamasıydı.
Her ikisinin de artıları ve eksileri vardı, ama React sınıfları hala kullanılabiliyordu, bu yüzden benim için bariz bir tercih oldu.
Ağustos ayının başında temel ve kenar çubuğunu inşa ederek başladım ve fon eksikliğinden dolayı durmak zorunda kaldım, bu yüzden ne yazık ki son tutarı ödemeyen bir müşterinin işini üstlendim. Kitle fonlaması denedim ama orada da şanslı değildim.
Bu yüzden, Eylül ayında elimde kalan az miktardaki parayla bu projeye tümüyle girişmeye karar verdim. 9 Eylül civarında işe yeniden başladım.
Zamanın büyük bir kısmı, ihtiyaçları karşılayacak şekilde ölçeklendirilebilecek temel soyutlamayı düşünmeye harcandı.
Figma'ya benzer şekilde yakınlaştırılıp kaydırılabilen bir Canvas istiyordum.
Diğer tüm bileşenlerin genişletilebileceği temel bir bileşen.
Kullanıcı arayüzü öğelerini tuvale sürükleyip bırakmanızı sağlayan Sürükle ve Bırak özelliği.
React ile bir yapı inşa etmek için, onu belirli bir şekilde düşünmeniz ve inşa etmeniz gerekir, bunun bir kütüphane mi yoksa bir framework mü olduğu konusundaki tartışmalara rağmen, her zaman bir kütüphaneden çok bir Framework'e benziyor.
Canva'nın kenar çubuğunu nasıl oluşturduğunu her zaman beğenmişimdir, sürükle ve bırak oluşturucum için de benzer bir şeye sahip olmak istemiştim.
Aklımdakileri bir kağıda çizdim. En iyi sanatçı değilim 🙄
Peki, sürükleme, yeniden boyutlandırma, seçme işlerinden kim sorumlu olmalı? Tuval mi yoksa temel widget mı? Widget içindeki widget'lar nasıl işlenecek?
Temel widget çocuklarını bilecek mi yoksa tuvalin kendisi tarafından tek bir veri yapısıyla mı yönetilecek? Çocukları çocukların içinde nasıl işleyeceğim?
Tuval ve diğer widget'ların içinde sürükle ve bırak nasıl çalışacak?
Düzenler nasıl yönetilecek?
Bunlar, her şeyi inşa etmeden önce sormaya başladığım sorulardan bazılarıydı.
Artık kullanıcı arayüzü daha basit görünse de, tabanın oluşturulmasına çok fazla emek verildiği için kullanıcılar için çok daha basit görünüyor.
Tuval tabanlı yaklaşım
Artık HTML'de varsayılan bir Canvas öğesi var, bu öğe çizim, resim ekleme ve benzeri birçok şey yapmanıza olanak sağlıyor, artık programım için kullanabileceğim ideal bir öğe gibi görünüyor.
Yani, sürükle ve bırak, yeniden boyutlandırma, yakınlaştırma ve kaydırma gibi mevcut bir uygulama varsa kontrol etmeye başladım.
Fabric.Js ile denemeler yaptım ve her şeyi fabric.js'de uygulamaya çalıştım, bunu görebilirsiniz
Tuval tabanlı olmayan yaklaşım
Şimdi deneyip gördükten sonra, varsayılan düzen yöneticisine erişimim olduğu ve ölçekleme sırasında bu ideal seçeneği oluşturacak birçok önceden oluşturulmuş kullanıcı arayüzü bileşeni bulunduğu için, tuval dışı yaklaşımın daha iyi göründüğünü fark ettim.
Canvas'ı iki farklı div kullanarak simüle etmeyi planladım, bir iç div ve dış kapsayıcı div.
Artık yakınlaştırma ve uzaklaştırma oluşturmak oldukça kolaydı çünkü CSS'de zaten dönüştürme, ölçekleme ve çevirme vardı.
İlk olarak, bunu uygulamak için, bir tuval tutan bir konteynere sahip olmam gerekiyordu. Şimdi bu tuval görünmez bir öğedir (gizli taşma olmadan), tüm öğelerin bırakıldığı yer burasıdır ve ölçekleme ve çeviri uygulanır.
Yakınlaştırmak için ölçeği artırmam, uzaklaştırmak için ise azaltmam gerekti.
Bu basit örneği deneyin. ( +
tuşu yakınlaştırmak için, -
tuşu uzaklaştırmak için)
Panlama da benzer şekilde çalıştı
Başlarken birkaç kütüphanede araştırma yapmıştım, örneğin:
Araştırdıktan sonra react-beautiful-dnd'nin artık sürdürülmediğini gördüm ve React dnd-kit ile başladım. Başlamış bir yapı olarak, dnd-kit'in dokümantasyonunun yaptığım şey için oldukça sınırlı olduğunu gördüm. Ayrıca, kütüphanede büyük değişiklikler içeren yeni bir sürüm yakında çıkacaktı, bu yüzden react-dnd-kit'i büyük sürüme kadar bırakmaya karar verdim.
DND-kit'i HTML'nin Sürükle ve Bırak API'siyle kullandığım kısımları yeniden yazdım. Yerel Sürükle ve Bırak API'siyle ilgili tek sınırlama, bazı dokunmatik cihazlar tarafından hala desteklenmemesiydi, bu benim için önemli değildi çünkü dokunmatik olmayan cihazlar için oluşturuyordum.
böyle bir uygulama oluştururken, tüm değişkenleri ve değişiklikleri takip etmek kolaylaşabilir. Bu yüzden, aynı bilgi parçasını takip eden birden fazla değişkenim olamaz.
Her widget'ın bilgisi/durumu ya tuvalde ya da widget'ın kendisinde tutulmalı, böylece istek üzerine bilgi iletilmelidir.
Ya da redux gibi bir durum yönetim kütüphanesi kullanabilirsiniz
Farklı yaklaşımları denedikten sonra, widget'larla ilgili tüm bilgilerin Canvas bileşeni tarafından yönetilmesini seçtim.
Veri yapısı aşağı yukarı şöyle görünüyor.
[ { id: "", // id of the widget widgetType: WidgetClass, // base widget children: [], // children will also have the same datastructure as the parent parent: "", // id of the parent of the current widget initialData: {} // information about the widget's data that's about to be rendered eg: backgroundColor, foregroundColor etc. } ]
Şimdi varlıkların widget'ların araç çubuğuyla erişilebilen kenar çubuğuna yüklenmesini istedim. Ancak her seferinde yan sekmeleri değiştirdiğimde, yeniden işleme yüklenen varlıkların kaybolmasına neden oldu.
Redux'un en büyük kısıtlamalarından biri yalnızca serileştirilebilir verileri depolayabilmenizdir. Görüntü, video, diğer varlıklar gibi serileştirilemeyen veriler redux'ta depolanamaz. Bu, ortak verileri farklı bileşenler arasında geçirmeyi zorlaştırır.
Bunun üstesinden gelmenin bir yolu React Context kullanmaktır. Kısaca, React Context, her seviyede manuel olarak props iletmek zorunda kalmadan bileşen ağacından veri geçirmenin bir yolunu sağlar.
Verileri farklı bileşenlerde tutmak için yapmam gereken tek şey, bunları bir React bağlam sağlayıcısının etrafına sarmaktı.
İki şey için kendi bağlam sağlayıcılarımı oluşturdum:
İşte Sürükle ve bırak için React bağlamını nasıl kullandığıma dair basit bir örnek.
import React, { createContext, useContext, useState } from 'react' const DragWidgetContext = createContext() export const useDragWidgetContext = () => useContext(DragWidgetContext) // Provider component to wrap around parts that need drag-and-drop functionality export const DragWidgetProvider = ({ children }) => { const [draggedElement, setDraggedElement] = useState(null) const onDragStart = (element) => { setDraggedElement(element) } const onDragEnd = () => { setDraggedElement(null) } return ( <DragWidgetContext.Provider value={{ draggedElement, onDragStart, onDragEnd }}> {children} </DragWidgetContext.Provider> ) }
Evet! işte bu kadar. Şimdi yapmam gereken tek şey, onu içeriğe ihtiyaç duyduğum bileşenin etrafına sarmaktı, benim durumumda bu Canvas ve kenar çubuğu üzerindeydi.
Her widget'ın farklı davranması ve kendine özgü nitelikleri olması nedeniyle, widget'ların kendi kodlarını üretmekten sorumlu olmaları gerektiğine ve bir kod motorunun yalnızca değişken adı çakışmalarını ve kodu bir araya getirmeyi ele alacağına karar verdim.
Bu sayede birçok hazır widget'ı ve bazı 3. parti UI eklentilerini destekleyecek şekilde kolayca genişletebildim.
Bir arka ucum veya kaydım yoktu ve statik sayfalar için ücretsiz barındırma sağlayan birçok şirket vardı. İlk önce Vercel'i seçmeye karar verdim, ancak çok fazla istek olduğunda Vercel'in ücretsiz lastiğinin düştüğünü sık sık gördüm.
İşte o zaman öğrendim ki
Tek olumsuz yanı yapım sürelerinin oldukça yavaş olması ve çok fazla dokümantasyon eksikliğiydi.
Yapım aşamasının en can sıkıcı kısmı yapı başarısızlığıydı. Vercel'de çalıştı ama Cloudflare sayfalarında çalışmadı??? Günlükler de o kadar net değildi. Ayrıca ücretsiz lastiklerimiz var ve ayda sadece 500 yapı var, bu yüzden çok fazla şey israf etmek istemedim.
Saatlerce denedim sonra sürekli entegrasyonu boş dizeye ayarlamaya karar verdim
CI='' npm install
Tüm bunları halka açık bir şekilde inşa ediyorum. Eğer bunun basit bir kenar çubuğundan tam teşekküllü bir Sürükle ve bırak oluşturucuya nasıl ilerlediğini görmek istiyorsanız, tümünü kontrol edebilirsiniz
#kamuoyundainşaat
Ah! Güncellemeler için takip etmeyi unutmayın
Eğer bu tarz içerikleri beğendiyseniz, daha detaylı olarak planlama ve yapım aşamalarını anlattığım daha fazla blog yazısı yazacağım. Takip etmek için substack bültenime abone olabilirsiniz :)