Clientを作って外部からMayaを操作する方法!!




takkun
どうも!たっくんです!

pipelineツールなどを作っていると、Maya専用っというよりは、スタンドアローンなアプリケーションにしたいなっと思いませんか!?

しかし、スタンドアローンなアプリケーションだと、、、

どうやって外部から
Mayaを操作するんだ!

と思う方もいらっしゃるかもしれません。

そんな時に便利なのが、Mayaコマンド「commandPort」を使って、TCPで通信してしまうっという方法です!なんだか難しそうに聞こえるかもしれませんが、、、意外と簡単にできてしまいます!(*´ω`*)

必要なimport

今回は、Qt(PySide/PyQt)を主軸に作っていきたいと思います!Pythonには「socket」というモジュールがあってTCPで通信できちゃいますが、あえて使いません!(*´ω`*)

import sys
from PySide import QtCore, QtGui, QtNetwork

クラスの骨格

まずは、QWidgetを継承して、ウインドウを作りたいと思います!MELコマンドが入力できるように「QTextEdit」と、送信するための「QPushButton」も合わせて用意しておきます(`・ω・´)ゞ

import sys
from PySide import QtCore, QtGui, QtNetwork

class Client(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(Client, self).__init__(*args, **kwargs)
		
		mainLayout = QtGui.QVBoxLayout(self)
		
		self.__editor = QtGui.QTextEdit(self)
		self.__editor.setEnabled(False)
		mainLayout.addWidget(self.__editor)
		
		button = QtGui.QPushButton('Send', self)
		mainLayout.addWidget(button)
Mayaに接続できたときだけ、MELコマンドを入力できるように「QTextEdit」はデフォルトではいじれないようにしておきます!

TCPで通信するための準備

TCPで通信するために使うのが「QTcpSocket」です!そのまんまの名前!笑

QTcpSocket」のインスタンスを作成したら、直打ちになりますが「自分自身と、8888番のポートを使って通信」できるようにコネクションしちゃいます!

import sys
from PySide import QtCore, QtGui, QtNetwork

class Client(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(Client, self).__init__(*args, **kwargs)
		
		mainLayout = QtGui.QVBoxLayout(self)
		
		self.__editor = QtGui.QTextEdit(self)
		self.__editor.setEnabled(False)
		mainLayout.addWidget(self.__editor)
		
		button = QtGui.QPushButton('Send', self)
		mainLayout.addWidget(button)
		
		self.__tcpSocket = QtNetwork.QTcpSocket(self)
		self.__tcpSocket.connectToHost('127.0.0.1', 8888)

通信状況に応じたSignal & Slotの設定

サーバー(Maya)」に接続できた場合は「QTextEdit」を使えるようし、何らかのエラーが発生した時にエラーメッセージを出す「Signal & Slot」の設定をします!

import sys
from PySide import QtCore, QtGui, QtNetwork

class Client(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(Client, self).__init__(*args, **kwargs)
		
		mainLayout = QtGui.QVBoxLayout(self)
		
		self.__editor = QtGui.QTextEdit(self)
		self.__editor.setEnabled(False)
		mainLayout.addWidget(self.__editor)
		
		button = QtGui.QPushButton('Send', self)
		button.clicked.connect(self.send)
		mainLayout.addWidget(button)
		
		self.__tcpSocket = QtNetwork.QTcpSocket(self)
		self.__tcpSocket.error.connect(self.displayError)
		self.__tcpSocket.connected.connect(self.connected)
		self.__tcpSocket.connectToHost('127.0.0.1', 8888)
	
	def connected(self):
		self.__editor.setEnabled(True)
		
	def displayError(self, socketError):
		QtGui.QMessageBox.information(self, 'Client', self.__tcpSocket.errorString())
Signalとのコネクションは、「connectToHost」より前にやらないと接続に失敗した時のエラーを取りこぼす可能性がありますのでご注意ください!

送信する処理

ボタンをクリックされたら、「QTextEdit」に書かれた内容を送信するようにしていきます。データを送信するためには「QTcpSocket」の「write」を使えばOKですが、データを「QByteArray」にしておく必要があります!

import sys
from PySide import QtCore, QtGui, QtNetwork

class Client(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(Client, self).__init__(*args, **kwargs)
		
		mainLayout = QtGui.QVBoxLayout(self)
		
		self.__editor = QtGui.QTextEdit(self)
		self.__editor.setEnabled(False)
		mainLayout.addWidget(self.__editor)
		
		button = QtGui.QPushButton('Send', self)
		button.clicked.connect(self.send)
		mainLayout.addWidget(button)
		
		self.__tcpSocket = QtNetwork.QTcpSocket(self)
		self.__tcpSocket.error.connect(self.displayError)
		self.__tcpSocket.connected.connect(self.connected)
		self.__tcpSocket.connectToHost('127.0.0.1', 8888)
	
	def connected(self):
		self.__editor.setEnabled(True)
		
	def displayError(self, socketError):
		QtGui.QMessageBox.information(self, 'Client', self.__tcpSocket.errorString())
		
	def send(self):
		command = self.__editor.toPlainText()
		data = QtCore.QByteArray(command)
		self.__tcpSocket.write(data)

起動時の処理

Pythonファイルが直接実行された時に、ウインドウを表示するようにします!これは、よく見かけるパターンですね!(*´ω`*)

import sys
from PySide import QtCore, QtGui, QtNetwork

class Client(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(Client, self).__init__(*args, **kwargs)
		
		mainLayout = QtGui.QVBoxLayout(self)
		
		self.__editor = QtGui.QTextEdit(self)
		self.__editor.setEnabled(False)
		mainLayout.addWidget(self.__editor)
		
		button = QtGui.QPushButton('Send', self)
		button.clicked.connect(self.send)
		mainLayout.addWidget(button)
		
		self.__tcpSocket = QtNetwork.QTcpSocket(self)
		self.__tcpSocket.error.connect(self.displayError)
		self.__tcpSocket.connected.connect(self.connected)
		self.__tcpSocket.connectToHost('127.0.0.1', 8888)
	
	def connected(self):
		self.__editor.setEnabled(True)
		
	def displayError(self, socketError):
		QtGui.QMessageBox.information(self, 'Client', self.__tcpSocket.errorString())
		
	def send(self):
		command = self.__editor.toPlainText()
		data = QtCore.QByteArray(command)
		self.__tcpSocket.write(data)
		
		
if __name__ == '__main__':
	app = QtGui.QApplication(sys.argv)
	window = Client()
	window.show()
	sys.exit(app.exec_())

実験!

拡張子を「pyw」で保存しておき余計なコンソールウインドウが出ない状態で実験してみました!