paint-brush
Python 用の Webflow のような UI ビルダーを構築する方法@paulfreeman
350 測定値
350 測定値

Python 用の Webflow のような UI ビルダーを構築する方法

Paul10m2024/10/05
Read on Terminal Reader

長すぎる; 読むには

Python 用のドラッグ アンド ドロップ UI ビルダーを構築する際の私の思考プロセスと経験を共有します
featured image - Python 用の Webflow のような UI ビルダーを構築する方法
Paul HackerNoon profile picture
0-item
1-item


私はここ数週間、Python 用のドラッグ アンド ドロップ ビルダーに取り組んできました。


こちらからご覧いただけますPyUIビルダー

ソースコード: https://github.com/PaulleDemon/PyUIBuilder


ビルダーは何ができるでしょうか?

簡単に言うと、Python用のUIを素早く構築し、Tkinterやcustomtkinterを含む複数のライブラリ/フレームワークでUIコードを生成するのに役立ちます。詳細については、機能セクション


しかし、私はプロジェクトを立ち上げるだけではなく、皆さんと私の経験を共有したいと思っています。このブログでは、私の思考プロセスと、アプリの構築方法の概要について説明します。

アイデアを思いつく。

一般に信じられていることとは反対に、Python は迅速なアプリケーションの構築によく使用され、特にデータ サイエンス、自動化、スクリプト タスクなどに携わる開発者の間で人気があります。特に科学研究の分野では、多くの内部ツールや GUI が Python で構築されています。これは、Python のシンプルさと、Tkinter、PyQt などのフレームワークが利用できるためです。

アイデア

現在、Web 用のドラッグ アンド ドロップ ビルダーは多数ありますが、Python GUI、特に tkinter 用のものはほとんどありません。いくつか見たことがありますが、ウィジェットの数が非常に限られていたり、XML 形式でコードが生成されたりするという問題がありました。これは、Python で UI を開発する場合に理想的ではありません。


そのため、最初は、Tkinter 専用の適切なドラッグ アンド ドロップ UI ビルダーを構築したいと考えていました。


私は理想的な GUI ビルダー (しゃれではありません) のアイデアをあれこれ考え続けました。Canva の UI に触発され、自分の GUI を理想的なものにするいくつかの機能を思いつきました。


  1. すべてのウィジェットはプラグインのように作成する必要があります。
  2. プラグインの形式でサードパーティの UI ウィジェットをサポートする必要があります。
  3. 画像やビデオなどのアセットをアップロードできる必要があります。
  4. Python でコードが生成されるはずです。

そこで、7月末頃にプロジェクトに取り掛かることにしました

アイデアの拡大

当初は tkbuilder と呼ばれ、Tkinter UI ライブラリ用の GUI ビルダーであることを示していました。


しかし、お気づきかもしれませんが、すべてがプラグインのように作成されており、まさにそれが私が計画していたことなので、同じアイデアを拡張して複数の Python GUI フレームワークとライブラリをサポートすることもできます。

初期バージョンの計画。

最初のバージョンでは、ユーザーを圧倒するような機能をあまり追加したくありませんでした。使用しているユーザーからのフィードバックに基づいて構築したいと考えました。こうすることで、ユーザーが望んでいないものを構築することに時間を無駄にすることがなくなります。


最初から、バックエンドやサインアップ フォームは用意しないことに決めました。こうすることで、私にとっても、それを使用するユーザーにとっても、開発がずっと簡単になります。私は、人々がすぐに使い始められるシンプルなフロントエンドを望んでいたのです。

言語の選択 JS、TS、Python

言語の選択

はい、これは私がかなり長い間考えていたことです。世の中にある Python 用の GUI ビルダーのほとんどは Python を使用して構築されています。私が最初に選んだ Python は PySide でした。


私がPyQt/Pysideを使って作った最も複雑なGUIベースのアプリはノードベースのエディター数年前。


しかし、私はすぐに、初期バージョンを構築するために Python を使用することの限界に気付きました。


  • Python UI ライブラリには、初期バージョンをすばやく構築するのに役立つサードパーティのウィジェットがあまりありません。
  • Python アプリを exe ファイルとして配布するのは簡単ではありませんが、JS を使用すると、electron アプリの形式で配布できます。
  • ほとんどの人は、よく知らない Web サイトから実行可能ファイルをダウンロードするよりも、Web を使用することを好みます。


Typescriptも選択肢の一つでしたが、Typescriptでは冗長すぎると感じていました。


私がすぐに気づいたのはこれらだけだったので、JS を使用することが私の第一選択になりました。


追記:後になって、TS を始めなかったことを後悔するようになりましたが、それはまた別の機会にお話ししましょう。

フレームワークの有無。

私が最も使い慣れているフレームワークのようなライブラリは React.js ですが、抽象化を作成するにはクラスを使用する必要があり、フックが導入されて以来推奨されていません。


フレームワークを使用しない場合の問題は、すべてを自分で構築する必要があり、React が提供する膨大なコンポーネント ライブラリにアクセスできないことです。


どちらにもトレードオフがありましたが、React クラスは引き続き使用できるため、私にとっては当然の選択となりました。

不安定なスタート

8 月の初めにベースとサイドバーの構築を開始しましたが、資金不足のため中断せざるを得ませんでした。そこで、クライアントの仕事を引き受けましたが、残念ながら最終金額を支払ってもらえませんでした。クラウド ファンディングも試みましたが、これも運がありませんでした。


そこで、9月に残ったわずかな資金で、このプロジェクトに全力を注ぐことにしました。9月9日頃に作業を再開しました。

事前に計画する...

事前に計画する

ニーズに合わせて拡張できる基本的な抽象化について考えるのに多くの時間を費やしました。


  1. Figma のようにズームやパンが可能なキャンバスが欲しかったです。

  2. 他のすべてのウィジェットが拡張できる基本ウィジェット。

  3. UI 要素をキャンバスにドラッグ アンド ドロップするドラッグ アンド ドロップ機能。


React を使用して構築するには、特定の方法で考えて構築する必要があります。ライブラリなのかフレームワークなのかという議論はありますが、常にライブラリというよりもフレームワークのように感じられます。

UIデザイン

私はいつも Canva のサイドバーの構築方法が好きだったので、ドラッグ アンド ドロップ ビルダーにも同様のものを取り入れたいと思っていました。

頭の中にあることを紙に書きました。最高のアーティストではありません🙄

UIデザイン

キャンバスとウィジェットの相互作用に関する私の思考プロセス。

事前に計画します...

では、ドラッグ、サイズ変更、選択は誰が担当するべきでしょうか。キャンバスかベース ウィジェットか。ウィジェット内のウィジェットはどのように処理されるのでしょうか。


ベース ウィジェットは子ウィジェットを認識しますか、それともキャンバス自体によって単一のデータ構造で管理されますか。子ウィジェット内で子ウィジェットをレンダリングするにはどうすればよいでしょうか。


キャンバスやその他のウィジェット内でのドラッグ アンド ドロップはどのように機能しますか?


レイアウトはどのように管理されますか?


これらは、全体を構築する前に私が問い始めた質問の一部です。


UI はよりシンプルに見えますが、ベースの構築には多くの考慮が払われているため、ユーザーにとってさらにシンプルに見えます。

HTML キャンバス ベースのアプローチまたは非キャンバス アプローチ。

キャンバスベースのアプローチ

現在、HTML にはデフォルトの Canvas 要素があり、描画、画像の追加など、さまざまな操作を実行できます。これは、私のプログラムで使用するのに理想的な要素のように見えます。


そこで、ドラッグアンドドロップ、サイズ変更、ズーム、パンの既存の実装があるかどうかを調べ始めました。ファブリックJsこれは私のユースケースにとって素晴らしいライブラリのように思えました。


私はFabric.Jsを試してみて、これを見ればわかるようにすべてをfabric.jsで実装しようとしました。実装しかし、キャンバスに関しては私が予期していなかったことがありました。


  1. キャンバスを構築するときにフックベースのアプローチを試し始めましたが、fabric.js の dispose 関数は非同期であったため、フックとうまく連携しませんでした。
  2. Canvas には Div などの子要素やその他の要素を含めることができないため、レイアウト マネージャーの構築が少し難しくなります。
  3. キャンバス上の何かをデバッグするのは、キャンバスの内部要素が開発者ツールの要素検査に表示されないため、非常に困難です。


キャンバスベースではないアプローチ


実験した結果、キャンバスを使用しないアプローチの方が優れているように思われました。提供されているデフォルトのレイアウト マネージャーにアクセスできることに加え、スケーリング時に理想的な選択肢となる、事前に構築された UI コンポーネントが多数用意されているからです。


2 つの異なる div (内部 div と外部コンテナー div) を使用してキャンバスをシミュレートすることを計画しました。


CSS にはすでに transform、scale、translate が含まれていたため、ズームとパンの作成は実装が非常に簡単になりました。


まず、これを実装するには、キャンバスを保持するコンテナーが必要でした。このキャンバスは非表示の要素 (オーバーフロー非表示なし) であり、ここにすべての要素がドロップされ、スケーリングと変換が適用されます。

容器

ズームインするにはスケールを増やし、ズームアウトするにはスケールを減らす必要がありました。

この簡単な例を試してください。( +キーでズーム、 -でズームアウト)


パンも同様に機能した

ドラッグアンドドロップ

ドラッグアンドドロップ

始めた頃、私はいくつかのライブラリを調べました。リアクト美しいDnd React Dnd-kitそしてリアクトスワッピー


調査した結果、 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. } ]

React コンテキストマネージャー

ここで、サイドバーにアップロードされたアセットをウィジェットのツールバーからアクセスできるようにしたいと考えました。しかし、サイドタブを切り替えるたびに、再レンダリングによってアップロードされたアセットが消えてしまいました。


Redux の最大の制限の 1 つは、シリアル化可能なデータしか保存できないことです。画像、ビデオ、その他のアセットなどのシリアル化不可能なデータは、Redux に保存できません。これにより、異なるコンポーネント間で共通データを渡すことが難しくなります。


これを克服する 1 つの方法は、React Context を使用することです。簡単に言うと、React Context は、各レベルで手動でプロパティを渡すことなく、コンポーネント ツリーを介してデータを渡す方法を提供します。


異なるコンポーネントにデータを配置するために必要なことは、それを React コンテキスト プロバイダーにラップすることだけです。


私は次の 2 つの目的で独自のコンテキスト プロバイダーを作成しました。

  1. ドラッグ アンド ドロップ - サイドバーからのドラッグ アンド ドロップと子要素内でのドラッグ アンド ドロップを有効にします。
  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 の無料タイヤがダウンすることがよくありました。


その時私はCloudflares ページ提供されているもの。彼らの無料タイヤには、ほぼすべてが無制限に含まれていました。そのため、cloudflare を使用することが私の第一の選択肢になりました。


唯一の欠点は、ビルド時間が非常に遅く、ドキュメントがかなり不足していたことです。


ビルドステップで最も面倒だったのはビルドの失敗でした。Vercelでは機能しましたが、cloudflareページでは機能しませんでした???ログもそれほど明確ではありませんでした。また、無料のタイヤは月に500ビルドしかないので、あまり無駄にしたくありませんでした。


何時間も試した後、継続的インテグレーションを空の文字列に設定することにしました

CI='' npm install


そしてついに公開されました。
ライブ

数か月にわたってどのように進歩したかを確認したいですか?

私はこれを公開して構築してきました。単純なサイドバーから本格的なドラッグアンドドロップビルダーに進化した様子をご覧になりたい場合は、全体をご覧ください。タイムラインはここ


#公共の場での建設


ああ!最新情報をフォローするのを忘れないでください

スターレポ⭐️


この種のコンテンツが気に入ったら、私がどのように計画し構築するかについてさらに詳しく説明するブログをもっと書く予定です。フォローするには、私のサブスタックニュースレターを購読してください :)