名付けて cydeer

https://github.com/ousttrue/cydeer

python, cython, dear imgui の組み合わせで名前を付けようと思ったのだが、既に先人がいっぱいいて名前が被るので適当に決めた。🦌

https://github.com/HankiDesign/awesome-dear-imgui#languages

最初、DearPyGui のサイトが強そうだったので試そうと思ったのだけど用途が違いそうだった。 次に、しばらく pyimgui を使っていい感じだったので、 docking ブランチ対応を見たら開発ブランチならば動いたので、自前ビルドを改造して使っていた。 PR も送ってみたのだが、 どうせなら自分で作ろうという機運が高まったので、作った。 cydeer は pyOpenGL とともに使う 薄い ImGui ラッパーという路線である。 ctypes を併用することでポインタを直接扱う。 camel casesnake case の変換を含めて何も変えない。

unknown: table => {"type":"table","align":[null,null,null,null,null],"children":[{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"lib","position":{"start":{"line":17,"column":3,"offset":500},"end":{"line":17,"column":6,"offset":503}}}],"position":{"start":{"line":17,"column":1,"offset":498},"end":{"line":17,"column":21,"offset":518}}},{"type":"tableCell","children":[{"type":"text","value":"binder","position":{"start":{"line":17,"column":23,"offset":520},"end":{"line":17,"column":29,"offset":526}}}],"position":{"start":{"line":17,"column":21,"offset":518},"end":{"line":17,"column":72,"offset":569}}},{"type":"tableCell","children":[{"type":"text","value":"imgui","position":{"start":{"line":17,"column":74,"offset":571},"end":{"line":17,"column":79,"offset":576}}}],"position":{"start":{"line":17,"column":72,"offset":569},"end":{"line":17,"column":95,"offset":592}}},{"type":"tableCell","children":[{"type":"text","value":"window & graphincs","position":{"start":{"line":17,"column":97,"offset":594},"end":{"line":17,"column":115,"offset":612}}}],"position":{"start":{"line":17,"column":95,"offset":592},"end":{"line":17,"column":138,"offset":635}}},{"type":"tableCell","children":[{"type":"text","value":"コメント","position":{"start":{"line":17,"column":140,"offset":637},"end":{"line":17,"column":144,"offset":641}}}],"position":{"start":{"line":17,"column":138,"offset":635},"end":{"line":17,"column":207,"offset":704}}}],"position":{"start":{"line":17,"column":1,"offset":498},"end":{"line":17,"column":207,"offset":704}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"(python)cydeer","position":{"start":{"line":19,"column":3,"offset":918},"end":{"line":19,"column":17,"offset":932}}}],"position":{"start":{"line":19,"column":1,"offset":916},"end":{"line":19,"column":21,"offset":936}}},{"type":"tableCell","children":[{"type":"text","value":"cython + ctypes(generate using libclang.cindex )","position":{"start":{"line":19,"column":23,"offset":938},"end":{"line":19,"column":71,"offset":986}}}],"position":{"start":{"line":19,"column":21,"offset":936},"end":{"line":19,"column":72,"offset":987}}},{"type":"tableCell","children":[{"type":"text","value":"imgui docking branch","position":{"start":{"line":19,"column":74,"offset":989},"end":{"line":19,"column":94,"offset":1009}}}],"position":{"start":{"line":19,"column":72,"offset":987},"end":{"line":19,"column":95,"offset":1010}}},{"type":"tableCell","children":[{"type":"text","value":"glfw など + pyOpenGL でがんばる","position":{"start":{"line":19,"column":97,"offset":1012},"end":{"line":19,"column":121,"offset":1036}}}],"position":{"start":{"line":19,"column":95,"offset":1010},"end":{"line":19,"column":131,"offset":1046}}},{"type":"tableCell","children":[{"type":"text","value":"可能な限りAPIの改変をしない。ポインタは ctypes で作る","position":{"start":{"line":19,"column":133,"offset":1048},"end":{"line":19,"column":165,"offset":1080}}}],"position":{"start":{"line":19,"column":131,"offset":1046},"end":{"line":19,"column":183,"offset":1098}}}],"position":{"start":{"line":19,"column":1,"offset":916},"end":{"line":19,"column":183,"offset":1098}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"(python)pyimgui","position":{"start":{"line":20,"column":3,"offset":1101},"end":{"line":20,"column":18,"offset":1116}}}],"position":{"start":{"line":20,"column":1,"offset":1099},"end":{"line":20,"column":21,"offset":1119}}},{"type":"tableCell","children":[{"type":"text","value":"cython","position":{"start":{"line":20,"column":23,"offset":1121},"end":{"line":20,"column":29,"offset":1127}}}],"position":{"start":{"line":20,"column":21,"offset":1119},"end":{"line":20,"column":72,"offset":1170}}},{"type":"tableCell","children":[{"type":"text","value":"imgui","position":{"start":{"line":20,"column":74,"offset":1172},"end":{"line":20,"column":79,"offset":1177}}}],"position":{"start":{"line":20,"column":72,"offset":1170},"end":{"line":20,"column":95,"offset":1193}}},{"type":"tableCell","children":[{"type":"text","value":"glfw など + pyOpenGL でがんばる","position":{"start":{"line":20,"column":97,"offset":1195},"end":{"line":20,"column":121,"offset":1219}}}],"position":{"start":{"line":20,"column":95,"offset":1193},"end":{"line":20,"column":131,"offset":1229}}},{"type":"tableCell","children":[{"type":"text","value":"ポインタ引数(p_openなど)による返り値を、tuple による複値で表現","position":{"start":{"line":20,"column":133,"offset":1231},"end":{"line":20,"column":171,"offset":1269}}}],"position":{"start":{"line":20,"column":131,"offset":1229},"end":{"line":20,"column":180,"offset":1278}}}],"position":{"start":{"line":20,"column":1,"offset":1099},"end":{"line":20,"column":180,"offset":1278}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"(python)DearPyGui","position":{"start":{"line":21,"column":3,"offset":1281},"end":{"line":21,"column":20,"offset":1298}}}],"position":{"start":{"line":21,"column":1,"offset":1279},"end":{"line":21,"column":21,"offset":1299}}},{"type":"tableCell","children":[{"type":"text","value":"未確認","position":{"start":{"line":21,"column":23,"offset":1301},"end":{"line":21,"column":26,"offset":1304}}}],"position":{"start":{"line":21,"column":21,"offset":1299},"end":{"line":21,"column":69,"offset":1347}}},{"type":"tableCell","children":[{"type":"text","value":"未確認","position":{"start":{"line":21,"column":71,"offset":1349},"end":{"line":21,"column":74,"offset":1352}}}],"position":{"start":{"line":21,"column":69,"offset":1347},"end":{"line":21,"column":89,"offset":1367}}},{"type":"tableCell","children":[{"type":"text","value":"DirectX11。python からアクセスできない?","position":{"start":{"line":21,"column":91,"offset":1369},"end":{"line":21,"column":119,"offset":1397}}}],"position":{"start":{"line":21,"column":89,"offset":1367},"end":{"line":21,"column":120,"offset":1398}}},{"type":"tableCell","children":[{"type":"text","value":"imgui をラップして独自 API。python で OpenGL するという目的には使えぬ","position":{"start":{"line":21,"column":122,"offset":1400},"end":{"line":21,"column":169,"offset":1447}}}],"position":{"start":{"line":21,"column":120,"offset":1398},"end":{"line":21,"column":171,"offset":1449}}}],"position":{"start":{"line":21,"column":1,"offset":1279},"end":{"line":21,"column":171,"offset":1449}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"(c)cimgui","position":{"start":{"line":22,"column":3,"offset":1452},"end":{"line":22,"column":12,"offset":1461}}}],"position":{"start":{"line":22,"column":1,"offset":1450},"end":{"line":22,"column":21,"offset":1470}}},{"type":"tableCell","children":[{"type":"text","value":"未確認","position":{"start":{"line":22,"column":23,"offset":1472},"end":{"line":22,"column":26,"offset":1475}}}],"position":{"start":{"line":22,"column":21,"offset":1470},"end":{"line":22,"column":69,"offset":1518}}},{"type":"tableCell","children":[{"type":"text","value":"未確認","position":{"start":{"line":22,"column":71,"offset":1520},"end":{"line":22,"column":74,"offset":1523}}}],"position":{"start":{"line":22,"column":69,"offset":1518},"end":{"line":22,"column":89,"offset":1538}}},{"type":"tableCell","children":[],"position":{"start":{"line":22,"column":89,"offset":1538},"end":{"line":22,"column":132,"offset":1581}}},{"type":"tableCell","children":[{"type":"text","value":"imgui を ","position":{"start":{"line":22,"column":134,"offset":1583},"end":{"line":22,"column":142,"offset":1591}}},{"type":"inlineCode","value":"extern C","position":{"start":{"line":22,"column":142,"offset":1591},"end":{"line":22,"column":152,"offset":1601}}},{"type":"text","value":" にラップしたもの。他言語バインド向け","position":{"start":{"line":22,"column":152,"offset":1601},"end":{"line":22,"column":171,"offset":1620}}}],"position":{"start":{"line":22,"column":132,"offset":1581},"end":{"line":22,"column":186,"offset":1635}}}],"position":{"start":{"line":22,"column":1,"offset":1450},"end":{"line":22,"column":186,"offset":1635}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"(rust)imgui-rs","position":{"start":{"line":23,"column":3,"offset":1638},"end":{"line":23,"column":17,"offset":1652}}}],"position":{"start":{"line":23,"column":1,"offset":1636},"end":{"line":23,"column":21,"offset":1656}}},{"type":"tableCell","children":[{"type":"text","value":"未確認","position":{"start":{"line":23,"column":23,"offset":1658},"end":{"line":23,"column":26,"offset":1661}}}],"position":{"start":{"line":23,"column":21,"offset":1656},"end":{"line":23,"column":69,"offset":1704}}},{"type":"tableCell","children":[{"type":"text","value":"未確認","position":{"start":{"line":23,"column":71,"offset":1706},"end":{"line":23,"column":74,"offset":1709}}}],"position":{"start":{"line":23,"column":69,"offset":1704},"end":{"line":23,"column":89,"offset":1724}}},{"type":"tableCell","children":[],"position":{"start":{"line":23,"column":89,"offset":1724},"end":{"line":23,"column":132,"offset":1767}}},{"type":"tableCell","children":[{"type":"text","value":"builder パターンで Default 引数を代替","position":{"start":{"line":23,"column":134,"offset":1769},"end":{"line":23,"column":161,"offset":1796}}}],"position":{"start":{"line":23,"column":132,"offset":1767},"end":{"line":23,"column":195,"offset":1830}}}],"position":{"start":{"line":23,"column":1,"offset":1636},"end":{"line":23,"column":195,"offset":1830}}}],"position":{"start":{"line":17,"column":1,"offset":498},"end":{"line":23,"column":195,"offset":1830}}}

imgui ラップには、 関数オーバーロード , デフォルト引数 , メンバー関数 という難所がある。 要するに c++ 要素なのだけど、c++ 要素含めての imgui の使い勝手なので。各言語バインディングで悩ましいところです。 たとえば、 rust は関数オーバーロードやデフォルト引数が無いので API を変えてます。

C# とかでも、 const ImVec2 pos& = ImVec2(0, 0) のような引数を解決するのは手間がかかったりする。 DLLImport 定義に対するデフォルト引数では解決できないので、 C# 側で一時変数を作ってポインターを取得する必要がある。

デフォルト引数 は cython で普通に解決した。 clang.cindex から値を取れれば難しくない。 メンバー関数ctypescython のメソッドを定義して、 selfthis pointer に cast して呼び出すコードを作った(ImGuiFontAtlas)。 関数オーバーロードcython でディスパッチするのはつらいので、MenuItem_2 のような suffix をつけて人間が選ぶようにした。

忘れていたが、もっとも問題になるのが 構造体の値渡し・返し だった(C++に限らない?)。 D言語 , rust ともにこれができない(vcのコンパイラと互換性がない?)ので注意が必要だった。コンパイルは通るが動作がおかしかったような。 ImGui の ImVec2 を値返しする関数でヒットする。 cydeer は、 cython を採用したので、cython 関数の出口で python 型に入れ替えるだけである。

あと、 cydeerpyi 標準装備でいい感じである(一部実際のpython型と齟齬があるが・・・)。

実装上の課題

cython の cimport の扱いがやっかいで、imgui, imgui.internal に分割しようとするとうまくいかなかった。 cydeer に関しては巨大な単一のモジュールで行くのが無難かもしれない。 internal やノードエディターとか追加するときに分けたいのだけど。

現状、Windows + python-3.10 しか試していない。 Windows11 の wslg + wayland で動くようにしたい。