- Extension開発で変に詰まったところをテンプレ化していく
- 公式ドキュメント
- 3.10をすんなり導入できるのはWindowsくらい
- ColabやROCmなどの一部環境で3.8ユーザーが残っている
- condaユーザーは3.9までしか上げられない
- pipとxformersの両立が難しいっぽい
- 3.8のEoLは2024年10月らしい...
3.8と3.9と3.10の違い
- 3.8の__file__は相対パス os.path.abspath(file) で絶対パスに出来る
- 3.9以前はmatch構文が使えないのでifで代用する
- 勝手に lfs をインストールしたいが、やりすぎな感もある。
- 11.6でも大丈夫そう
- 本体バージョンに従うのが無難
- 本体が勝手にバージョンアップするが従わざるを得ない
- ハイフンは使わずアンダーバーを使ったほうが無難。
- ハイフンを使ったディレクトリのインポートは importlib が必要になる。
- 1111本体の機能として指定可能だが対応が結構難しい。
- たとえば sd_dreambooth_extension は動作しない。
- 作者としては可能な限りで対応する。
- ユーザーとしては名前を変更しないで使うほうが無難。
- インストール時と起動時に毎回呼ばれる。
- なので重たい処理は厳禁。
基本は launch.pyを使って pip install を行う程度。
import launch
if not launch.is_installed("yaml"):
launch.run_pip("install yaml", desc='yaml')
込み入った処理は、成功時にフラグとなるファイルを置くと良い。
import pathlib
p = pathlib.Path(__file__).parts[-3:-1]
checked_path = os.path.join(p[0], p[1], 'install.checked')
if not os.path.exists(checked_path):
# 込み入った処理...
pathlib.Path(checked_path).write_text('1')
- install.pyの中ではscripts.basedir()は使えない
- sd_dreambooth_extensionの作者が要望してつけた機能。
- 基本的には使わないほうが良い。
- コマンドラインオプションを追加してからExtensionを外すと1111本体が起動しなくなる。
- 自分自身の場所を知る方法が無い。
-
ファイル名は何でもよい。
-
種類としては以下のように分かれる(他にもあるかも)。
- API: script_callbacks.on_app_started(APIfunc)
- --apiオプションをつけて起動するもの
- Tab: script_callbacks.on_ui_tabs(on_ui_tabs)
- 画面にタブとして表示されるUI
- Script: class Script(scripts.Script)
- txt2img/img2imgのScriptドロップダウンで選択するUI
- その他: 上記3つの指定が無いもの
- API: script_callbacks.on_app_started(APIfunc)
-
おそらく gradio の起動時に全ファイルが一度読み込まれる。
-
タブが最も使われている
- タブだらけになって邪魔なので、せめてタブ名は短くしましょう
from scripts import foo, bar, baz
- scripts/foo.py ごとgitリポジトリに入れるだけ
- あとは1111本体がやってくれる
- たとえば py/ ディレクトリを作りたいとする。
- scriptsからpyディレクトリを見るには from py で済む
from py import foo, bar, baz
from . import foo, bar, baz
from .foo import foofanc
- しないほうが楽
def example_func():
import pathlib
p = pathlib.Path(__file__).parts[-4:-2]
import importlib
example3 = importlib.import_module(f"{p[0]}.{p[1]}.py2.example3")
- ファイルを持ってきて設置する
- 可能ならリポジトリ名のハイフンをアンダーバーに直す
- 全部のimport文を相対パス指定に書き換える
- __main__の内容を自作のUIからimportかimportlibで呼び出す
- sys.argvやそのparser部分をUIかファイルで入力できるようにする
- Extensionの中に入れたほうが綺麗ではある。
def get_input_dir():
import pathlib
p = pathlib.Path(__file__).parts[-4:-2]
input_dir = os.path.join(p[0], p[1], 'input')
return input_dir
- ファイルはgitに入れない(pull時に上書きして初期化してしまうので)
- ディレクトリを作ったほうがベター
def get_config_path():
import pathlib
p = pathlib.Path(__file__).parts[-4:-2]
config_path = os.path.join(p[0], p[1], 'json', 'config.json')
return config_path
- 私は出来ない
- 恐らく1111ごと読み込んだプロジェクトとして編集する必要がありそう
- 開発ツールを入れるとvenvが汚れるので注意する
- ファイルを更新しても反映されない
- 同じコードを2度実行するとエラーになるような処理は書いてはいけない
- 疑似Constクラスとかは作らないほうが良い
- --debug-uiでモデルの読み込みを飛ばせるけどモデルが必要なExtensionでは使えない
- 起動時間はnull.safetensorsでも5秒くらいしか短縮されない
- せめて速いSSDで1111を再起動しましょう
- RamDiskに入れても劇的な変化は無かったので出来る範囲で
- VC++ビルドツールやCUDAに依存してると影響がでかいので注意する
- 開発用とは別に動作確認用のVMがあると理想的
- Gradio Docsに書いてないことが多すぎる。
- 基本的には大量のwithで掘り下げていく
- Tabにはイベントが無いはず
- Boxにはlabelが無く、最初のオブジェクトのlabelが使われる
- すべてのオブジェクトは gradio 起動時に描画される
- 重いリスト表示とかはボタンを押してから読み込む仕組みにしたほうが無難
- 外からのオブジェクトのアップデートはどうもうまくいかない
- イベントのoutputsに指定すれば動くが挙動が限定的
- 凝ったUIを思いついたときはそれが本当に実装できるか最初に試す
- 多重定義をすると無限ループを起こす
- たとえばドロップダウンAを選択するとドロップダウンBの内容が変わり、ドロップダウンBを選択するとテキストボックスに値が入る、というのは無理
- そういう処理はボタンを挟む
- linesは内容に応じた可変にはならない
- interactive=Falseでもリサイズできる
- 文字列は勝手にunescape()される。この仕様はWindowsでpathを扱うときにとても困る
- 有効な回避策は入力時点でスラッシュにしておくこと
- change()を使っていいのはそれで作業フローが終わる時だけ
- アップロード完了後にクリアして別のファイルをアップロードするにはユーザーが×ボタンを押すしかない
- サイズがでかくてうざい
- ファイルとして存在しないPIL Imageを返すと正しく表示されない?
- ファイルに保存するのが無難
- 1ファイルしか再生できないので非常に不便
- ユーザーにダウンロードを促す方法はこれしかない
- 自動的にダウンロードを開始することもできない
- 表示内容をいじることもできない
- 処理するとtmpフォルダにファイルのコピーをとる
- 複数ファイルに対応できるが全ファイルのコピーをとるので要注意
- gradioにはオブジェクトつきの一覧表示をする機能が無い
- tableタグとstyle.cssとjavascriptで頑張って実装することになる
- .click() とか
- インデントレベルは with gr.Blocks() と同じ階層
- ドキュメントにない引数
- javascriptの関数名を入れる
- fnより先に呼ばれる
- returnの内容がinputs[]に上書きされる
- returnの要素数が足りないぶんはinputs[]の値が使われる
引数は使えないが無名関数が使える。
_js="function(){return rows('"+tab1.lower()+"_"+tab2.lower()+"')}",
クラス名とメソッド名に変数を使いたい場合は getattr と globals を組み合わせる。
fn=getattr(globals()[f"FilerGroup{tab1}"], f"download_{tab2.lower()}"),
- 空のvalueがNoneになる時は指定することができない
- Textboxに対して''を返して空文字列を表示させることはできる
- Imageに対してNoneを返して画像をクリアすることはできない