WhiteAtelier Archives
Another side of life, Behind the real.
2020-07-26 09:19:53 - Category : PySide | Unity | プログラミング
【PySide2】QWidgetにUnityの画面を埋め込む
QWidgetに、Unityの画面を埋め込む方法をまとめました。Windows限定です。

動機

UnityにはUnityEngineUIがあり、ボタンやテキストボックスを実装することができます。

ただ、Unityを使ったエディタを作ろうとしたときに、このUIの仕組みでは少し力不足に感じますし、とても使いにくい印象です。 UIのデザインもデフォルトだと「ザ・Unity」感が出てあまり好きではありません。

そこで、ビューワとしてUnityのStandaloneを使い、UIをQtにて実装できないかと思い調べました。

環境
Unity2019.2.19f1
Python3.7.5
PySide25.14
OSWindows10

注意

  • Windows限定です。
  • Unityが古いと標準出力エラーが得られないようです。
    • Unity2018.4.20f1で最初は試していましたが、標準エラー出力がキャッチできませんでした。
    • Unity2019.2.19f1にバージョンを上げたところ、Unityからの標準エラー出力を受け取ることができるようになりました。

下準備

UnityでPC Standaloneビルドを行います。また、UI側とパイプで通信するため、標準入力を画面に表示し、受け付けた後標準エラー出力へSTDERRをプレフィックスとしてエコーするようなプログラムを作成します。

Windowsの場合、Unityの中から書き出した標準出力はファイルにダンプされます。

(C#コードを載せますが、C#は全く書けないので真似しないほうが良いかも)

using System.Collections.Concurrent;
using UnityEngine;
using System.Threading;
using System;

public class inout : MonoBehaviour
{
	static BlockingCollection<string> queue = new BlockingCollection<string>();
	Thread t;
	[SerializeField] UnityEngine.UI.Text text;

	private static void ThreadProc() {
		try {
			while (true) {
				var stdin = Console.ReadLine();
				if (stdin == null) break;
				queue.TryAdd(stdin, Timeout.Infinite);
			}
		}
		catch (Exception) { }
	}

	private void Start() {
		t = new Thread(new ThreadStart(ThreadProc));
		t.Start();

	}

	private void OnGUI() {
		string data;
		while (queue.TryTake(out data)) {
			text.text += data + "\n";
			Console.Error.WriteLine("STDERR> " + data);
			Console.Error.Flush();  // 念のため
		}
	}

}

GameObject/UI/Textをシーンに配置し、 Inspectorからパラメータを以下のように調整しました。

Textのパラメータ

後は適当なGameObjectを作って配置し、先ほどのスクリプトをアサイン、項目textへ先ほど作成したテキストUIをアタッチします。

スクリプト用GameObject

PySide側

python -m venv testenv
testenv\Scripts\activate
pip install pyside2 pywin32

1ファイルで簡単なUIを構築します。pywin32はwindows apiを使用するために使います。

閉じるときのスレッドの処理はお粗末ですがしていません。

動作

動作の様子

  • ウィンドウサイズを変えると追従します
  • 文字を打ち込むと画面に表示され、そのまま標準エラーへエコーされている様子が確認できます