私はここ数週間、Python 用のドラッグ アンド ドロップ ビルダーに取り組んできました。
こちらからご覧いただけます
ソースコード:
ビルダーは何ができるでしょうか?
簡単に言うと、Python用のUIを素早く構築し、Tkinterやcustomtkinterを含む複数のライブラリ/フレームワークでUIコードを生成するのに役立ちます。詳細については、
しかし、私はプロジェクトを立ち上げるだけではなく、皆さんと私の経験を共有したいと思っています。このブログでは、私の思考プロセスと、アプリの構築方法の概要について説明します。
一般に信じられていることとは反対に、Python は迅速なアプリケーションの構築によく使用され、特にデータ サイエンス、自動化、スクリプト タスクなどに携わる開発者の間で人気があります。特に科学研究の分野では、多くの内部ツールや GUI が Python で構築されています。これは、Python のシンプルさと、Tkinter、PyQt などのフレームワークが利用できるためです。
現在、Web 用のドラッグ アンド ドロップ ビルダーは多数ありますが、Python GUI、特に tkinter 用のものはほとんどありません。いくつか見たことがありますが、ウィジェットの数が非常に限られていたり、XML 形式でコードが生成されたりするという問題がありました。これは、Python で UI を開発する場合に理想的ではありません。
そのため、最初は、Tkinter 専用の適切なドラッグ アンド ドロップ UI ビルダーを構築したいと考えていました。
私は理想的な GUI ビルダー (しゃれではありません) のアイデアをあれこれ考え続けました。Canva の UI に触発され、自分の GUI を理想的なものにするいくつかの機能を思いつきました。
そこで、7月末頃にプロジェクトに取り掛かることにしました
当初は tkbuilder と呼ばれ、Tkinter UI ライブラリ用の GUI ビルダーであることを示していました。
しかし、お気づきかもしれませんが、すべてがプラグインのように作成されており、まさにそれが私が計画していたことなので、同じアイデアを拡張して複数の Python GUI フレームワークとライブラリをサポートすることもできます。
最初のバージョンでは、ユーザーを圧倒するような機能をあまり追加したくありませんでした。使用しているユーザーからのフィードバックに基づいて構築したいと考えました。こうすることで、ユーザーが望んでいないものを構築することに時間を無駄にすることがなくなります。
最初から、バックエンドやサインアップ フォームは用意しないことに決めました。こうすることで、私にとっても、それを使用するユーザーにとっても、開発がずっと簡単になります。私は、人々がすぐに使い始められるシンプルなフロントエンドを望んでいたのです。
はい、これは私がかなり長い間考えていたことです。世の中にある Python 用の GUI ビルダーのほとんどは Python を使用して構築されています。私が最初に選んだ Python は PySide でした。
私がPyQt/Pysideを使って作った最も複雑なGUIベースのアプリは
しかし、私はすぐに、初期バージョンを構築するために Python を使用することの限界に気付きました。
Typescriptも選択肢の一つでしたが、Typescriptでは冗長すぎると感じていました。
私がすぐに気づいたのはこれらだけだったので、JS を使用することが私の第一選択になりました。
追記:後になって、TS を始めなかったことを後悔するようになりましたが、それはまた別の機会にお話ししましょう。
私が最も使い慣れているフレームワークのようなライブラリは React.js ですが、抽象化を作成するにはクラスを使用する必要があり、フックが導入されて以来推奨されていません。
フレームワークを使用しない場合の問題は、すべてを自分で構築する必要があり、React が提供する膨大なコンポーネント ライブラリにアクセスできないことです。
どちらにもトレードオフがありましたが、React クラスは引き続き使用できるため、私にとっては当然の選択となりました。
8 月の初めにベースとサイドバーの構築を開始しましたが、資金不足のため中断せざるを得ませんでした。そこで、クライアントの仕事を引き受けましたが、残念ながら最終金額を支払ってもらえませんでした。クラウド ファンディングも試みましたが、これも運がありませんでした。
そこで、9月に残ったわずかな資金で、このプロジェクトに全力を注ぐことにしました。9月9日頃に作業を再開しました。
ニーズに合わせて拡張できる基本的な抽象化について考えるのに多くの時間を費やしました。
Figma のようにズームやパンが可能なキャンバスが欲しかったです。
他のすべてのウィジェットが拡張できる基本ウィジェット。
UI 要素をキャンバスにドラッグ アンド ドロップするドラッグ アンド ドロップ機能。
React を使用して構築するには、特定の方法で考えて構築する必要があります。ライブラリなのかフレームワークなのかという議論はありますが、常にライブラリというよりもフレームワークのように感じられます。
私はいつも Canva のサイドバーの構築方法が好きだったので、ドラッグ アンド ドロップ ビルダーにも同様のものを取り入れたいと思っていました。
頭の中にあることを紙に書きました。最高のアーティストではありません🙄
では、ドラッグ、サイズ変更、選択は誰が担当するべきでしょうか。キャンバスかベース ウィジェットか。ウィジェット内のウィジェットはどのように処理されるのでしょうか。
ベース ウィジェットは子ウィジェットを認識しますか、それともキャンバス自体によって単一のデータ構造で管理されますか。子ウィジェット内で子ウィジェットをレンダリングするにはどうすればよいでしょうか。
キャンバスやその他のウィジェット内でのドラッグ アンド ドロップはどのように機能しますか?
レイアウトはどのように管理されますか?
これらは、全体を構築する前に私が問い始めた質問の一部です。
UI はよりシンプルに見えますが、ベースの構築には多くの考慮が払われているため、ユーザーにとってさらにシンプルに見えます。
キャンバスベースのアプローチ
現在、HTML にはデフォルトの Canvas 要素があり、描画、画像の追加など、さまざまな操作を実行できます。これは、私のプログラムで使用するのに理想的な要素のように見えます。
そこで、ドラッグアンドドロップ、サイズ変更、ズーム、パンの既存の実装があるかどうかを調べ始めました。
私はFabric.Jsを試してみて、これを見ればわかるようにすべてをfabric.jsで実装しようとしました。
キャンバスベースではないアプローチ
実験した結果、キャンバスを使用しないアプローチの方が優れているように思われました。提供されているデフォルトのレイアウト マネージャーにアクセスできることに加え、スケーリング時に理想的な選択肢となる、事前に構築された UI コンポーネントが多数用意されているからです。
2 つの異なる div (内部 div と外部コンテナー div) を使用してキャンバスをシミュレートすることを計画しました。
CSS にはすでに transform、scale、translate が含まれていたため、ズームとパンの作成は実装が非常に簡単になりました。
まず、これを実装するには、キャンバスを保持するコンテナーが必要でした。このキャンバスは非表示の要素 (オーバーフロー非表示なし) であり、ここにすべての要素がドロップされ、スケーリングと変換が適用されます。
ズームインするにはスケールを増やし、ズームアウトするにはスケールを減らす必要がありました。
この簡単な例を試してください。( +
キーでズーム、 -
でズームアウト)
パンも同様に機能した
始めた頃、私はいくつかのライブラリを調べました。
調査した結果、 react-beautiful-dnd はメンテナンスされなくなったことがわかり、React dnd-kit を使い始めました。構築を始めたところ、私が構築していたものに対して dnd-kit のドキュメントがかなり限られていることに気付きました。さらに、ライブラリに大きな変更を加えた新しいリリースが間もなくリリースされる予定だったので、メジャー リリースまで react-dnd-kit を中止することにしました。
DND-kit を使用する部分を HTML のドラッグ アンド ドロップ API で書き直しました。ネイティブのドラッグ アンド ドロップ API の唯一の制限は、一部のタッチ デバイスではまだサポートされていないことですが、非タッチ デバイス用に構築していたので、これは問題ではありませんでした。
このようなアプリを構築すると、すべての変数と変更を簡単に追跡できなくなります。そのため、同じ情報を追跡する複数の変数を持つことはできません。
各ウィジェットの情報/状態は、キャンバスまたはウィジェット自体によって保持され、リクエストに応じて情報が渡される必要があります。
あるいは、reduxのような状態管理ライブラリを使用することもできます
さまざまなアプローチを試した後、ウィジェットに関するすべての情報を Canvas コンポーネントで管理することを選択しました。
データ構造は次のようになります。
[ { 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. } ]
ここで、サイドバーにアップロードされたアセットをウィジェットのツールバーからアクセスできるようにしたいと考えました。しかし、サイドタブを切り替えるたびに、再レンダリングによってアップロードされたアセットが消えてしまいました。
Redux の最大の制限の 1 つは、シリアル化可能なデータしか保存できないことです。画像、ビデオ、その他のアセットなどのシリアル化不可能なデータは、Redux に保存できません。これにより、異なるコンポーネント間で共通データを渡すことが難しくなります。
これを克服する 1 つの方法は、React Context を使用することです。簡単に言うと、React Context は、各レベルで手動でプロパティを渡すことなく、コンポーネント ツリーを介してデータを渡す方法を提供します。
異なるコンポーネントにデータを配置するために必要なことは、それを React コンテキスト プロバイダーにラップすることだけです。
私は次の 2 つの目的で独自のコンテキスト プロバイダーを作成しました。
ドラッグ アンド ドロップに React コンテキストを使用する方法の簡単な例を示します。
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> ) }
はい、これで完了です。あとは、コンテキストが必要なコンポーネントの周りにそれをラップするだけです。私の場合は、Canvas とサイドバーの上です。
各ウィジェットは動作が異なり、独自の属性を持っているため、ウィジェットは独自のコードを生成する必要があり、コード エンジンは変数名の競合を処理してコードをまとめるだけであると判断しました。
この方法により、多くの事前構築済みウィジェットやサードパーティの UI プラグインをサポートするように簡単に拡張することができました。
バックエンドもサインアップもありませんでしたし、静的ページ用の無料ホスティングを提供している会社はたくさんありました。最初は Vercel にしようと決めていましたが、リクエストが多すぎると Vercel の無料タイヤがダウンすることがよくありました。
その時私は
唯一の欠点は、ビルド時間が非常に遅く、ドキュメントがかなり不足していたことです。
ビルドステップで最も面倒だったのはビルドの失敗でした。Vercelでは機能しましたが、cloudflareページでは機能しませんでした???ログもそれほど明確ではありませんでした。また、無料のタイヤは月に500ビルドしかないので、あまり無駄にしたくありませんでした。
何時間も試した後、継続的インテグレーションを空の文字列に設定することにしました
CI='' npm install
私はこれを公開して構築してきました。単純なサイドバーから本格的なドラッグアンドドロップビルダーに進化した様子をご覧になりたい場合は、全体をご覧ください。
#公共の場での建設
ああ!最新情報をフォローするのを忘れないでください
この種のコンテンツが気に入ったら、私がどのように計画し構築するかについてさらに詳しく説明するブログをもっと書く予定です。フォローするには、私のサブスタックニュースレターを購読してください :)