pyblish lite only freezes unity the first time you launch it.
when you close and relaunch it, it works fine.
Magic! (and this is consistant behaviour)
if you want it to work from first launch, you can do the following overcomplicated setup:
reusing the camera pyside2 sample code, we can launch lite first time without freezing unity.
(the proper non magic way)
(honestly way easier to just launch pyblish and close re-open. )
reloading python modules needs restarting unity.
not sure yet how to reload python modules in unity which really slows things down.
files: (most code is frankensteined from the unity example)
import logging
import os
import sys
import traceback
from unity_python.common.scheduling import exec_on_main_thread, exec_on_main_thread_async
# This is the C# System.dll not the Python sys module.
import System
import UnityEngine
import UnityEditor
from UnityEditor.Scripting.Python.Samples import PySideExample
import pyblish.api
import pyblish_qml
from pyblish_qml import api
# Get PySide2
try:
from PySide2 import QtCore, QtUiTools, QtWidgets
# from Qt import QtCore, QtWidgets # lite doesnt launch with Qt :(
except ModuleNotFoundError:
UnityEngine.Debug.LogError("Please install PySide2 to use the PySideExample")
raise
### Globals
_PYSIDE_UI = None
_qApp = None
### UI class
class PySidePyblishUI():
# If we use slots we need to include weakref support
__slots__ = [ '_dialog', '__weakref__' ]
def __init__(self):
self._dialog = None
try:
import pyblish_lite
import pyblish.api
pyblish.api.register_gui("pyblish_lite")
window = pyblish_lite.show()
self._dialog = window # lite returns a dialog, QML returns a server :P
except:
log('Got an exception while creating the dialog.', logging.ERROR, traceback.format_exc())
raise
# For logging, we don't need to wait for the log to occur before returning
# control: we can asynchronously execute it.
@exec_on_main_thread_async
def log(what, level=logging.INFO, traceback=None):
"""
Short-hand method to log a message in Unity. At logging.DEBUG it prints
into the Editor's log file (https://docs.unity3d.com/Manual/LogFiles.html)
At level logging.INFO, logging.WARN and logging.ERROR it uses
UnityEngine.Debug.Log, UnityEngine.Debug.LogWarning and
UnityEngine.Debug.LogError, respectively.
"""
message = "{}".format(what)
if traceback:
message += "\nStack:\n{}".format(traceback)
if level == logging.DEBUG:
System.Console.WriteLine(message)
elif level == logging.INFO:
UnityEngine.Debug.Log(message)
elif level == logging.WARN:
UnityEngine.Debug.LogWarning(message)
else:
UnityEngine.Debug.LogError(message)
def create_or_reinitialize():
# Create the QApplication if not already created
global _qApp
# added this so we dont rely on the poor global implimentation,
# avoid a lot of exceptions when anything goes wrong.
app = QtWidgets.QApplication.instance()
if app:
_qApp = app
if not _qApp:
# Important: on mac, disable the native menu bar handling -- otherwise
# the Unity menus will disappear and you risk a crash when Unity exits.
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_MacPluginApplication)
_qApp = QtWidgets.QApplication([sys.executable])
# Create the window if not already created; show it if it was
# previously created but was hidden.
global _PYSIDE_UI
if not _PYSIDE_UI:
_PYSIDE_UI = PySidePyblishUI()
else:
_PYSIDE_UI._dialog.show()
def on_update():
QtWidgets.QApplication.processEvents()
using System.IO;
using UnityEditor;
using UnityEditor.Scripting.Python;
using UnityEngine;
using Python.Runtime;
namespace UnityEditor.Scripting.Python.Pyblish
{
public class PyblishLauncher
{
const string kStateName = "com.unity.scripting.python.pyblish";
/// <summary>
/// Hack to get the current files directory
/// </summary>
/// <param name="fileName">Leave it blank to the current files directory</param>
/// <returns></returns>
private static string __DIR__([System.Runtime.CompilerServices.CallerFilePath] string fileName = "")
{
return Path.GetDirectoryName(fileName);
}
/// <summary>
/// Menu to launch the client
/// </summary>
[MenuItem("Python/Examples/pyblish")]
public static void OnMenuClick()
{
CreateOrReinitialize();
}
static void CreateOrReinitialize()
{
// You can manually add the sample directory to your sys.path in
// the Python Settings under site-packages. Or you can do it
// programmatically like so.
string dir = __DIR__();
PythonRunner.EnsureInitialized();
using (Py.GIL())
{
dynamic sys = PythonEngine.ImportModule("sys");
if ((int)sys.path.count(dir) == 0)
{
sys.path.append(dir);
}
}
// Now that weve set up the path correctly, we can import the
// Python side of this example as a module:
PythonRunner.RunString(@"
import pyblish_unity
# reload(pyblish_unity)
pyblish_unity.create_or_reinitialize()
# pyblish_unity.setup()
# pyblish_unity.show()
");
// A domain reload happens when you change C# code or when you
// launch into play mode (unless you selected the option not to
// reload then).
//
// When it happens, your C# state is entirely reinitialized. The
// Python state, however, remains as it was.
//
// To store information about what happened in the previous domain,
// Unity provides the SessionState. Alternately we could have
// stored the data in a variable in Python.
//
SessionState.SetBool(kStateName, true);
}
/// <summary>
/// Reconnect to the PySide UI upon a domain reload, if we created it
/// in a previous domain.
///
/// This is also called when Unity starts, in which case we wont have
/// previously created the PySide UI.
/// </summary>
[InitializeOnLoadMethod]
static void OnDomainLoad()
{
if (SessionState.GetBool(kStateName, false))
{
CreateOrReinitialize();
}
}
static void OnUpdate()
{
// This is another way to call Python, handy if you want to mix and match
// languages. Best practice: dont store references to objects from Python
// longer than you need to -- let them be garbage collected.
//
// If you have unexplained crashes when running in this mode, often
// its because you forgot to take the GIL.
using (Py.GIL())
{
dynamic module = PythonEngine.ImportModule("pyblish_unity");
module.on_update();
}
}
}
}