<?xml version="1.0" encoding="UTF-8"?>
<pythonPanelDocument>
<!-- This file contains definitions of Python interfaces and the
interfaces menu. It should not be hand-edited when it is being
used by the application. Note, that two definitions of the
same interface or of the interfaces menu are not allowed
in a single file. -->
<interface name="LinkedParmsExample" label="Linked Parameters Example" icon="MISC_python">
<script><![CDATA[import math
from hutil.Qt import QtCore, QtWidgets
class LinkedParmsExample(QtWidgets.QFrame):
def __init__(self):
QtWidgets.QFrame.__init__(self)
self.nodePathField = QtWidgets.QLineEdit()
self.nodePathField.setDragEnabled(True)
# Set focus to "ClickFocus" to preserve "Tab" key
# functionality in network view panes.
self.nodePathField.setFocusPolicy(QtCore.Qt.ClickFocus)
self.nodePathField.editingFinished.connect(self._onFinishedEditingNodePath)
self.tx = TextFieldAndSlider("tx")
self.ty = TextFieldAndSlider("ty")
self.tz = TextFieldAndSlider("tz")
nodeLabel = QtWidgets.QLabel('Node')
nodeLabel.setFocusPolicy(QtCore.Qt.NoFocus)
nodeSelector = QtWidgets.QToolButton(self)
nodeSelector.clicked.connect(self._onShowNodeChooser)
nodeSelector.setText("Choose a node")
nodeSelector.setIconSize(QtCore.QSize(18, 18))
nodeSelector.setFocusPolicy(QtCore.Qt.NoFocus)
selectorLayout = QtWidgets.QHBoxLayout()
selectorLayout.addWidget(nodeLabel)
selectorLayout.addWidget(self.nodePathField)
selectorLayout.addWidget(nodeSelector)
selectorLayout.addStretch(3)
MasterLayout = QtWidgets.QVBoxLayout()
MasterLayout.addLayout(selectorLayout)
MasterLayout.addWidget(self.tx)
MasterLayout.addWidget(self.ty)
MasterLayout.addWidget(self.tz)
MasterLayout.addStretch(1)
self.setLayout(MasterLayout)
def __del__(self):
# QtWidgets.QFrame does not have a __del__ method
# so we do not have to call it here.
# Disconnect the text fields and sliders
# if they are currently attached to a node.
self.tx.disconnectFromNode()
self.ty.disconnectFromNode()
self.tz.disconnectFromNode()
def _onShowNodeChooser(self):
path = hou.ui.selectNode(None, None, hou.nodeTypeFilter.Obj)
self._linkToNode(path)
def _onFinishedEditingNodePath(self):
path = self.nodePathField.text()
self._linkToNode(path)
def _linkToNode(self, path):
self.nodePathField.setText(path)
node = hou.node(path)
self.tx.setNode(node)
self.ty.setNode(node)
self.tz.setNode(node)
class TextFieldAndSlider(QtWidgets.QWidget):
def __init__(self, parameter):
QtWidgets.QWidget.__init__(self)
self.node = None
self.parameterString = parameter
self.parameterLabel = QtWidgets.QLabel(parameter)
self.parameterLabel.setFocusPolicy(QtCore.Qt.NoFocus)
self.textField = QtWidgets.QLineEdit(self)
self.textField.editingFinished.connect(self._onTextFieldFinishedEditing)
self.textField.textChanged.connect(self._onTextFieldChanged)
self.textField.setFocusPolicy(QtCore.Qt.ClickFocus)
self.textField.setEnabled(False)
self.valueSlider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
self.valueSlider.setTickPosition(QtWidgets.QSlider.TicksBothSides)
self.valueSlider.setTickInterval(1)
self.valueSlider.sliderMoved.connect(self._onSliderChanged)
self.valueSlider.setFocusPolicy(QtCore.Qt.NoFocus)
self.valueSlider.setEnabled(False)
# Limit values between -10 and 10.
self.valueSlider.setRange(-10, 10)
self.layout = QtWidgets.QHBoxLayout()
self.layout.addWidget(self.parameterLabel)
self.layout.addWidget(self.textField)
self.layout.addWidget(self.valueSlider, 2, 0)
self.setLayout(self.layout)
def setNode(self, selectedNode):
self.disconnectFromNode()
if selectedNode is None or selectedNode.parm(self.parameterString) is None:
self.textField.setEnabled(False)
self.valueSlider.setEnabled(False)
return
self.textField.setEnabled(True)
self.valueSlider.setEnabled(True)
self.node = selectedNode
self.textField.setText(
str(self.node.parm(self.parameterString).eval()))
#####################################################################
# Register an event callback with the selected node.
# The callback will listen for node parameter value changes.
# This mimics a channel reference from the PySide text field
# and slider to the node parameter.
#####################################################################
self.node.addEventCallback(
(hou.nodeEventType.ParmTupleChanged,), self._onNodeChange)
def disconnectFromNode(self):
if self.node:
self.node.removeEventCallback(
(hou.nodeEventType.ParmTupleChanged,), self._onNodeChange)
self.node = None
def _onNodeChange(self, **kwargs):
# Iterate through the kwargs["parm_tuple"] to find the matching
# parameter and update the value field.
if kwargs["event_type"] == hou.nodeEventType.ParmTupleChanged \
and kwargs["parm_tuple"] is not None:
for parms in kwargs["parm_tuple"]:
if(self.parameterString == parms.name()):
self.textField.setText(str(parms.eval()))
def _onTextFieldChanged(self, text):
try:
value = float(self.textField.text())
self.valueSlider.setValue(value)
except ValueError:
pass
def _onTextFieldFinishedEditing(self):
try:
value = float(self.textField.text())
except ValueError:
return
#####################################################################
# Update the node parameter that the PySide text field is linked to.
# This mimics a channel reference from the node parameter to the
# PySide text field.
#####################################################################
self.node.setParms({self.parameterString: value})
def _onSliderChanged(self, value):
try:
text_field_value = float(self.textField.text())
needs_update = (value != math.floor(text_field_value))
except:
needs_update = True
#####################################################################
# Update the node parameter that the PySide slider is linked to.
# This mimics a channel reference from the node parameter to the
# PySide slider.
#####################################################################
if needs_update:
self.node.setParms({self.parameterString:value})
def onCreateInterface():
interface = LinkedParmsExample()
return interface
]]></script>
</interface>
</pythonPanelDocument>