ayon-core/openpype/hosts/harmony/api/TB_sceneOpened.js
Petr Kalis 372d686024 Fix - Harmony creator issue
Creator failed with 'str' object does not support item assignment
2022-03-15 14:24:36 +01:00

531 lines
15 KiB
JavaScript

/* global QTcpSocket, QByteArray, QDataStream, QTimer, QTextCodec, QIODevice, QApplication, include */
/* global QTcpSocket, QByteArray, QDataStream, QTimer, QTextCodec, QIODevice, QApplication, include */
/*
Avalon Harmony Integration - Client
-----------------------------------
This script implements client communication with Avalon server to bridge
gap between Python and QtScript.
*/
/* jshint proto: true */
var LD_OPENHARMONY_PATH = System.getenv('LIB_OPENHARMONY_PATH');
LD_OPENHARMONY_PATH = LD_OPENHARMONY_PATH + '/openHarmony.js';
LD_OPENHARMONY_PATH = LD_OPENHARMONY_PATH.replace(/\\/g, "/");
include(LD_OPENHARMONY_PATH);
this.__proto__['$'] = $;
function Client() {
var self = this;
/** socket */
self.socket = new QTcpSocket(this);
/** receiving data buffer */
self.received = '';
self.messageId = 1;
self.buffer = new QByteArray();
self.waitingForData = 0;
/**
* pack number into bytes.
* @function
* @param {int} num 32 bit integer
* @return {string}
*/
self.pack = function(num) {
var ascii='';
for (var i = 3; i >= 0; i--) {
ascii += String.fromCharCode((num >> (8 * i)) & 255);
}
return ascii;
};
/**
* unpack number from string.
* @function
* @param {string} numString bytes to unpack
* @return {int} 32bit unsigned integer.
*/
self.unpack = function(numString) {
var result=0;
for (var i = 3; i >= 0; i--) {
result += numString.charCodeAt(3 - i) << (8 * i);
}
return result;
};
/**
* prettify json for easier debugging
* @function
* @param {object} json json to process
* @return {string} prettified json string
*/
self.prettifyJson = function(json) {
var jsonString = JSON.stringify(json);
return JSON.stringify(JSON.parse(jsonString), null, 2);
};
/**
* Log message in debug level.
* @function
* @param {string} data - message
*/
self.logDebug = function(data) {
var message = typeof(data.message) != 'undefined' ? data.message : data;
MessageLog.trace('(DEBUG): ' + message.toString());
};
/**
* Log message in info level.
* @function
* @param {string} data - message
*/
self.logInfo = function(data) {
var message = typeof(data.message) != 'undefined' ? data.message : data;
MessageLog.trace('(DEBUG): ' + message.toString());
};
/**
* Log message in warning level.
* @function
* @param {string} data - message
*/
self.logWarning = function(data) {
var message = typeof(data.message) != 'undefined' ? data.message : data;
MessageLog.trace('(INFO): ' + message.toString());
};
/**
* Log message in error level.
* @function
* @param {string} data - message
*/
self.logError = function(data) {
var message = typeof(data.message) != 'undefined' ? data.message : data;
MessageLog.trace('(ERROR): ' +message.toString());
};
/**
* Show message in Harmony GUI as popup window.
* @function
* @param {string} msg - message
*/
self.showMessage = function(msg) {
MessageBox.information(msg);
};
/**
* Implement missing setTimeout() in Harmony.
* This calls once given function after some interval in milliseconds.
* @function
* @param {function} fc function to call.
* @param {int} interval interval in milliseconds.
* @param {boolean} single behave as setTimeout or setInterval.
*/
self.setTimeout = function(fc, interval, single) {
var timer = new QTimer();
if (!single) {
timer.singleShot = true; // in-case if setTimout and false in-case of setInterval
} else {
timer.singleShot = single;
}
timer.interval = interval; // set the time in milliseconds
timer.singleShot = true; // in-case if setTimout and false in-case of setInterval
timer.timeout.connect(this, function(){
fc.call();
});
timer.start();
};
/**
* Process recieved request. This will eval recieved function and produce
* results.
* @function
* @param {object} request - recieved request JSON
* @return {object} result of evaled function.
*/
self.processRequest = function(request) {
var mid = request.message_id;
if (typeof request.reply !== 'undefined') {
self.logDebug('['+ mid +'] *** received reply.');
return;
}
self.logDebug('['+ mid +'] - Processing: ' + self.prettifyJson(request));
var result = null;
if (typeof request.script !== 'undefined') {
self.logDebug('[' + mid + '] Injecting script.');
try {
eval.call(null, request.script);
} catch (error) {
self.logError(error);
}
} else if (typeof request["function"] !== 'undefined') {
try {
var _func = eval.call(null, request["function"]);
if (request.args == null) {
result = _func();
} else {
result = _func(request.args);
}
} catch (error) {
result = 'Error processing request.\n' +
'Request:\n' +
self.prettifyJson(request) + '\n' +
'Error:\n' + error;
}
} else {
self.logError('Command type not implemented.');
}
return result;
};
/**
* This gets called when socket received new data.
* @function
*/
self.onReadyRead = function() {
var currentSize = self.buffer.size();
self.logDebug('--- Receiving data ( '+ currentSize + ' )...');
var newData = self.socket.readAll();
var newSize = newData.size();
self.buffer.append(newData);
self.logDebug(' - got ' + newSize + ' bytes of new data.');
self.processBuffer();
};
/**
* Process data received in buffer.
* This detects messages by looking for header and message length.
* @function
*/
self.processBuffer = function() {
var length = self.waitingForData;
if (self.waitingForData == 0) {
// read header from the buffer and remove it
var header_data = self.buffer.mid(0, 6);
self.buffer = self.buffer.remove(0, 6);
// convert header to string
var header = '';
for (var i = 0; i < header_data.size(); ++i) {
// data in QByteArray come out as signed bytes.
var unsigned = header_data.at(i) & 0xff;
header = header.concat(String.fromCharCode(unsigned));
}
// skip 'AH' and read only length, unpack it to integer
header = header.substr(2);
length = self.unpack(header);
}
var data = self.buffer.mid(0, length);
self.logDebug('--- Expected: ' + length + ' | Got: ' + data.size());
if (length > data.size()) {
// we didn't received whole message.
self.waitingForData = length;
self.logDebug('... waiting for more data (' + length + ') ...');
return;
}
self.waitingForData = 0;
self.buffer.remove(0, length);
for (var j = 0; j < data.size(); ++j) {
self.received = self.received.concat(String.fromCharCode(data.at(j)));
}
// self.logDebug('--- Received: ' + self.received);
var to_parse = self.received;
var request = JSON.parse(to_parse);
var mid = request.message_id;
// self.logDebug('[' + mid + '] - Request: ' + '\n' + JSON.stringify(request));
self.logDebug('[' + mid + '] Recieved.');
request.result = self.processRequest(request);
self.logDebug('[' + mid + '] Processing done.');
self.received = '';
if (request.reply !== true) {
request.reply = true;
self.logDebug('[' + mid + '] Replying.');
self._send(JSON.stringify(request));
}
if ((length < data.size()) || (length < self.buffer.size())) {
// we've received more data.
self.logDebug('--- Got more data to process ...');
self.processBuffer();
}
};
/**
* Run when Harmony connects to server.
* @function
*/
self.onConnected = function() {
self.logDebug('Connected to server ...');
self.lock = false;
self.socket.readyRead.connect(self.onReadyRead);
var app = QCoreApplication.instance();
app.avalonClient.send(
{
'module': 'openpype.lib',
'method': 'emit_event',
'args': ['application.launched']
}, false);
};
self._send = function(message) {
var data = new QByteArray();
var outstr = new QDataStream(data, QIODevice.WriteOnly);
outstr.writeInt(0);
data.append('UTF-8');
outstr.device().seek(0);
outstr.writeInt(data.size() - 4);
var codec = QTextCodec.codecForUtfText(data);
var msg = codec.fromUnicode(message);
var l = msg.size();
var coded = new QByteArray('AH').append(self.pack(l));
coded = coded.append(msg);
self.socket.write(new QByteArray(coded));
self.logDebug('Sent.');
};
self.waitForLock = function() {
if (self._lock === false) {
self.logDebug('>>> Unlocking ...');
return;
} else {
self.logDebug('Setting timer.');
self.setTimeout(self.waitForLock, 300);
}
};
/**
* Send request to server.
* @param {object} request - json encoded request.
*/
self.send = function(request) {
request.message_id = self.messageId;
if (typeof request.reply == 'undefined') {
self.logDebug("[" + self.messageId + "] sending:\n" + self.prettifyJson(request));
}
self._send(JSON.stringify(request));
};
/**
* Executed on disconnection.
*/
self.onDisconnected = function() {
self.socket.close();
};
/**
* Disconnect from server.
*/
self.disconnect = function() {
self.socket.close();
};
self.socket.connected.connect(self.onConnected);
self.socket.disconnected.connect(self.onDisconnected);
}
/**
* Entry point, creating Avalon Client.
*/
function start() {
var self = this;
/** hostname or ip of server - should be localhost */
var host = '127.0.0.1';
/** port of the server */
var port = parseInt(System.getenv('AVALON_HARMONY_PORT'));
// Attach the client to the QApplication to preserve.
var app = QCoreApplication.instance();
if (app.avalonClient == null) {
app.avalonClient = new Client();
app.avalonClient.socket.connectToHost(host, port);
}
var menuBar = QApplication.activeWindow().menuBar();
var actions = menuBar.actions();
app.avalonMenu = null;
for (var i = 0 ; i < actions.length; i++) {
label = System.getenv('AVALON_LABEL');
if (actions[i].text == label) {
app.avalonMenu = true;
}
}
var menu = null;
if (app.avalonMenu == null) {
menu = menuBar.addMenu(System.getenv('AVALON_LABEL'));
}
// menu = menuBar.addMenu('Avalon');
/**
* Show creator
*/
self.onCreator = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['creator']
}, false);
};
var action = menu.addAction('Create...');
action.triggered.connect(self.onCreator);
/**
* Show Workfiles
*/
self.onWorkfiles = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['workfiles']
}, false);
};
if (app.avalonMenu == null) {
action = menu.addAction('Workfiles...');
action.triggered.connect(self.onWorkfiles);
}
/**
* Show Loader
*/
self.onLoad = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['loader']
}, false);
};
// add Loader item to menu
if (app.avalonMenu == null) {
action = menu.addAction('Load...');
action.triggered.connect(self.onLoad);
}
/**
* Show Publisher
*/
self.onPublish = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['publish']
}, false);
};
// add Publisher item to menu
if (app.avalonMenu == null) {
action = menu.addAction('Publish...');
action.triggered.connect(self.onPublish);
}
/**
* Show Scene Manager
*/
self.onManage = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['sceneinventory']
}, false);
};
// add Scene Manager item to menu
if (app.avalonMenu == null) {
action = menu.addAction('Manage...');
action.triggered.connect(self.onManage);
}
/**
* Show Subset Manager
*/
self.onSubsetManage = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['subsetmanager']
}, false);
};
// add Subset Manager item to menu
if (app.avalonMenu == null) {
action = menu.addAction('Subset Manager...');
action.triggered.connect(self.onSubsetManage);
}
/**
* Show Experimental dialog
*/
self.onExperimentalTools = function() {
app.avalonClient.send({
'module': 'openpype.hosts.harmony.api.lib',
'method': 'show',
'args': ['experimental_tools']
}, false);
};
// add Subset Manager item to menu
if (app.avalonMenu == null) {
action = menu.addAction('Experimental Tools...');
action.triggered.connect(self.onExperimentalTools);
}
// FIXME(antirotor): We need to disable `on_file_changed` now as is wreak
// havoc when "Save" is called multiple times and zipping didn't finished yet
/*
// Watch scene file for changes.
app.onFileChanged = function(path)
{
var app = QCoreApplication.instance();
if (app.avalonOnFileChanged){
app.avalonClient.send(
{
'module': 'avalon.harmony.lib',
'method': 'on_file_changed',
'args': [path]
},
false
);
}
app.watcher.addPath(path);
};
app.watcher = new QFileSystemWatcher();
scene_path = scene.currentProjectPath() +"/" + scene.currentVersionName() + ".xstage";
app.watcher.addPath(scenePath);
app.watcher.fileChanged.connect(app.onFileChanged);
app.avalonOnFileChanged = true;
*/
app.onFileChanged = function(path) {
// empty stub
return path;
};
}
function ensureSceneSettings() {
var app = QCoreApplication.instance();
app.avalonClient.send(
{
"module": "openpype.hosts.harmony.api",
"method": "ensure_scene_settings",
"args": []
},
false
);
}
function TB_sceneOpened()
{
start();
}