# # Copyright 2016 Pixar # # Licensed under the Apache License, Version 2.0 (the "Apache License") # with the following modification; you may not use this file except in # compliance with the Apache License and the following modification to it: # Section 6. Trademarks. is deleted and replaced with: # # 6. Trademarks. This License does not grant permission to use the trade # names, trademarks, service marks, or product names of the Licensor # and its affiliates, except as required to comply with Section 4(c) of # the License and to reproduce the content of the NOTICE file. # # You may obtain a copy of the Apache License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the Apache License with the above modification is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the Apache License for the specific # language governing permissions and limitations under the Apache License. # from .qt import QtCore, QtGui, QtWidgets from .adjustClippingUI import Ui_AdjustClipping from .common import FixableDoubleValidator class AdjustClipping(QtWidgets.QDialog): """The dataModel provided to this VC must conform to the following interface: Editable properties: overrideNear (float or None, which indicates the override is disabled) overrideFar (float or None, which indicates the override is disabled) Readable properties: cameraFrustum (Gf.Frustum, or struct that has a Gf.Range1d 'nearFar' member) Signals: signalFrustumChanged() - whenever the near/far clipping values may have changed. """ def __init__(self, parent, dataModel): QtWidgets.QDialog.__init__(self,parent) self._ui = Ui_AdjustClipping() self._ui.setupUi(self) self._dataModel = dataModel clipRange = self._dataModel.cameraFrustum.nearFar self._nearCache = self._dataModel.overrideNear or clipRange.min self._farCache = self._dataModel.overrideFar or clipRange.max self._dataModel.signalFrustumChanged.connect(self.update) # When the checkboxes change, we want to update instantly self._ui.overrideNear.stateChanged.connect(self._overrideNearToggled) self._ui.overrideFar.stateChanged.connect(self._overrideFarToggled) # we also want to update the clipping planes as the user is typing self._ui.nearEdit.textChanged.connect(self._nearChanged) self._ui.farEdit.textChanged.connect(self._farChanged) def AddValidation(lineEdit): dv = FixableDoubleValidator(lineEdit) dv.setDecimals(3) dv.setBottom(0) lineEdit.setValidator(dv) # Make sure only human-readable doubles can be typed in the text boxes AddValidation(self._ui.nearEdit) AddValidation(self._ui.farEdit) # Set the checkboxes to their initial state self._ui.overrideNear.setChecked(self._dataModel.overrideNear \ is not None) self._ui.overrideFar.setChecked(self._dataModel.overrideFar \ is not None) # load the initial values for the text boxes, but first deactivate them # if their corresponding checkbox is off. self._ui.nearEdit.setEnabled(self._ui.overrideNear.isChecked()) self._ui.nearEdit.validator().fixup(str(self._nearCache)) self._ui.farEdit.setEnabled(self._ui.overrideFar.isChecked()) self._ui.farEdit.validator().fixup(str(self._farCache)) def _updateEditorsFromDataModel(self): """Read the dataModel-computed clipping planes and put them in the text boxes when they are deactivated.""" clipRange = self._dataModel.cameraFrustum.nearFar if (not self._ui.overrideNear.isChecked()) and \ self._nearCache != clipRange.min : self._nearCache = clipRange.min nearStr = str(self._nearCache) self._ui.nearEdit.validator().fixup(nearStr) if (not self._ui.overrideFar.isChecked()) and \ self._farCache != clipRange.max : self._farCache = clipRange.max farStr = str(self._farCache) self._ui.farEdit.validator().fixup(farStr) def paintEvent(self, paintEvent): """Overridden from base class so we can perform JIT updating of editors to limit the number of redraws we perform""" self._updateEditorsFromDataModel() super(AdjustClipping, self).paintEvent(paintEvent) def _overrideNearToggled(self, state): """Called when the "Override Near" checkbox is toggled""" self._ui.nearEdit.setEnabled(state) if state: self._dataModel.overrideNear = self._nearCache else: self._dataModel.overrideNear = None def _overrideFarToggled(self, state): """Called when the "Override Far" checkbox is toggled""" self._ui.farEdit.setEnabled(state) if state: self._dataModel.overrideFar = self._farCache else: self._dataModel.overrideFar = None def _nearChanged(self, text): """Called when the Near text box changed. This can happen when we are updating the value but the widget is actually inactive - don't do anything in that case.""" if len(text) == 0 or not self._ui.nearEdit.isEnabled(): return try: self._dataModel.overrideNear = float(text) except ValueError: pass def _farChanged(self, text): """Called when the Far text box changed. This can happen when we are updating the value but the widget is actually inactive - don't do anything in that case.""" if len(text) == 0 or not self._ui.farEdit.isEnabled(): return try: self._dataModel.overrideFar = float(text) except ValueError: pass def closeEvent(self, event): # Ensure that even if the dialog doesn't get destroyed right away, # we'll stop doing work. self._dataModel.signalFrustumChanged.disconnect(self.update) event.accept() # Since the dialog is the immediate-edit kind, we consider # window-close to be an accept, so our clients can know the dialog is # done by listening for the finished(int) signal self.accept()