def add_element(self, field=None, field_layout=None): """Adds a new field to the Array. :param field: Optional field to add. If omitted, a copy of the last element will be added. """ if field is None: if not self.fields: raise RuntimeError('No default field set in the ArrayField.') field = copy.deepcopy(self.fields[-1]) self.fields.append(field) if field_layout: element_widget = QtWidgets.QWidget() field_layout.insertWidget(field_layout.count() - 1, element_widget) hbox = QtWidgets.QHBoxLayout(element_widget) hbox.setContentsMargins(0, 0, 0, 0) field_widget = field.widget() hbox.addWidget(field_widget) action = QtWidgets.QAction('Remove', field_layout) action.triggered.connect( partial(self.remove_element, field_layout, element_widget)) icon = QtGui.QIcon(QtGui.QPixmap(':/smallTrash.png')) action.setIcon(icon) action.setToolTip('Remove') action.setStatusTip('Remove') delete_button = QtWidgets.QToolButton() delete_button.setDefaultAction(action) hbox.addWidget(delete_button)
class TestCaptureStream(object): """Allows the output of the tests to be displayed in a QTextEdit.""" success_color = QtGui.QColor(92, 184, 92) fail_color = QtGui.QColor(240, 173, 78) error_color = QtGui.QColor(217, 83, 79) skip_color = QtGui.QColor(88, 165, 204) normal_color = QtGui.QColor(200, 200, 200) def __init__(self, text_edit): self.text_edit = text_edit def write(self, text): """Write text into the QTextEdit.""" # Color the output if text.startswith('ok'): self.text_edit.setTextColor(TestCaptureStream.success_color) elif text.startswith('FAIL'): self.text_edit.setTextColor(TestCaptureStream.fail_color) elif text.startswith('ERROR'): self.text_edit.setTextColor(TestCaptureStream.error_color) elif text.startswith('skipped'): self.text_edit.setTextColor(TestCaptureStream.skip_color) self.text_edit.insertPlainText(text) self.text_edit.setTextColor(TestCaptureStream.normal_color) def flush(self): pass
def image(cls, size=32): """Get the image QPixmap of the Component. :param size: Desired dimension of the image. :return: The QPixmap of the icon image. """ return QtGui.QPixmap(cls.image_path()).scaled(size, size)
def mouseMoveEvent(self, event): contains = self.grab_rect().contains(event.pos()) if contains and not self.over_grab_hotspot: QtWidgets.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor)) self.over_grab_hotspot = True elif not contains and self.over_grab_hotspot: restore_cursor() self.over_grab_hotspot = False
def paintEvent(self, event): """Override the paintEvent to draw the grab hotspot. :param event: """ super(ComponentWidget, self).paintEvent(event) painter = QtGui.QPainter(self) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(self.color) painter.drawRect(self.grab_rect())
def mousePressEvent(self, event): child = self.childAt(event.pos()) if not child: return if not isinstance(child, ComponentWidget): return pos = child.mapFromParent(event.pos()) if not child.grab_rect().contains(pos): return # Create the drag object with the component data we are moving mime_data = QtCore.QMimeData() mime_data.setText(json.dumps(child.comp.data())) drag = QtGui.QDrag(self) drag.setMimeData(mime_data) hotspot = event.pos() - child.pos() drag.setHotSpot(hotspot) # Resize the indicator so it has the same height as the ComponentWidget we are dragging self.drop_indicator.setFixedHeight(child.height()) QtWidgets.qApp.setOverrideCursor( QtGui.QCursor(QtCore.Qt.ClosedHandCursor)) index = self.get_component_index_at_position(event.pos()) component_widget_index = self.queue_layout.indexOf(child) self.queue_layout.takeAt(component_widget_index) child.hide() self.place_indicator(index) if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: # The drag reorder was accepted so do the actual move drop_indicator_index = self.queue_layout.indexOf( self.drop_indicator) self.hide_indicator() self.queue_layout.insertWidget(drop_indicator_index, child) child.show() self.queue.clear() components = self.get_ordered_component_widgets() for widget in components: self.queue.add(widget.comp) else: self.queue_layout.insertWidget(component_widget_index, child) child.show() restore_cursor()
def __init__(self, *args, **kwargs): super(MayaTestRunnerDialog, self).__init__(*args, **kwargs) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowTitle('CMT Unit Test Runner') self.resize(1000, 600) self.rollback_importer = RollbackImporter() menubar = self.menuBar() menu = menubar.addMenu('Settings') action = menu.addAction('Buffer Output') action.setToolTip('Only display output during a failed test.') action.setCheckable(True) action.setChecked(mayaunittest.Settings.buffer_output) action.toggled.connect(mayaunittest.set_buffer_output) action = menu.addAction('New Scene Between Test') action.setToolTip('Creates a new scene file after each test.') action.setCheckable(True) action.setChecked(mayaunittest.Settings.file_new) action.toggled.connect(mayaunittest.set_file_new) menu = menubar.addMenu('Help') action = menu.addAction('Documentation') action.triggered.connect(documentation) toolbar = self.addToolBar('Tools') action = toolbar.addAction('Run All Tests') action.setIcon(QtGui.QIcon(QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_run_all_tests.png')))) action.triggered.connect(self.run_all_tests) action.setToolTip('Run all tests.') action = toolbar.addAction('Run Selected Tests') action.setIcon(QtGui.QIcon(QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_run_selected_tests.png')))) action.setToolTip('Run all selected tests.') action.triggered.connect(self.run_selected_tests) action = toolbar.addAction('Run Failed Tests') action.setIcon(QtGui.QIcon(QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_run_failed_tests.png')))) action.setToolTip('Run all failed tests.') action.triggered.connect(self.run_failed_tests) widget = QtWidgets.QWidget() self.setCentralWidget(widget) vbox = QtWidgets.QVBoxLayout(widget) splitter = QtWidgets.QSplitter(orientation=QtCore.Qt.Horizontal) self.test_view = QtWidgets.QTreeView() self.test_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) splitter.addWidget(self.test_view) self.output_console = QtWidgets.QTextEdit() self.output_console.setReadOnly(True) splitter.addWidget(self.output_console) vbox.addWidget(splitter) splitter.setStretchFactor(1, 4) self.stream = TestCaptureStream(self.output_console) test_suite = mayaunittest.get_tests() root_node = TestNode(test_suite) self.model = TestTreeModel(root_node, self) self.test_view.setModel(self.model) self.expand_tree(root_node)
def set_color(self): """Open a dialog to set the override RGB color of the selected nodes.""" nodes = cmds.ls(sl=True) or [] if nodes: color = cmds.getAttr('{0}.overrideColorRGB'.format(nodes[0]))[0] color = QtGui.QColor(color[0] * 255, color[1] * 255, color[2] * 255) color = QtWidgets.QColorDialog.getColor(color, self, 'Set Curve Color') if color.isValid(): color = [color.redF(), color.greenF(), color.blueF()] for node in nodes: cmds.setAttr('{0}.overrideEnabled'.format(node), True) cmds.setAttr('{0}.overrideRGBColors'.format(node), True) cmds.setAttr('{0}.overrideColorRGB'.format(node), *color)
def widget(self): """Get the QWidget of the Field.""" widget = QtWidgets.QWidget() hbox = QtWidgets.QHBoxLayout(widget) hbox.setContentsMargins(0, 0, 0, 0) validator = QtGui.QDoubleValidator(-999999.0, 999999.0, self.precision) widget_x = QtWidgets.QLineEdit(str(self._value[0])) widget_x.setToolTip(self.help_text) widget_x.setValidator(validator) widget_x.textChanged.connect(self.set_value_x) hbox.addWidget(widget_x) widget_y = QtWidgets.QLineEdit(str(self._value[1])) widget_y.setToolTip(self.help_text) widget_y.setValidator(validator) widget_y.textChanged.connect(self.set_value_y) hbox.addWidget(widget_y) widget_z = QtWidgets.QLineEdit(str(self._value[2])) widget_z.setToolTip(self.help_text) widget_z.setValidator(validator) widget_z.textChanged.connect(self.set_value_z) hbox.addWidget(widget_z) return widget
def paintEvent(self, event): painter = QtGui.QPainter(self) color = QtGui.QColor(72, 170, 181) painter.setPen(color) painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
def __init__(self, comp, queue, parent=None): super(ComponentWidget, self).__init__(parent) self.setMouseTracking(True) self.queue_layout = parent.queue_layout self.queue = queue self.comp = comp vbox = QtWidgets.QVBoxLayout(self) self.setFrameStyle(QtWidgets.QFrame.StyledPanel) self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) self.over_grab_hotspot = False self.color = ComponentWidget.normal_color # Header hbox = QtWidgets.QHBoxLayout() self.header_layout = hbox hbox.setContentsMargins(16, 4, 4, 4) vbox.addLayout(hbox) # Expand toggle expand_action = QtWidgets.QAction('Toggle', self) expand_action.setCheckable(True) icon = QtGui.QIcon(QtGui.QPixmap(':/arrowDown.png')) expand_action.setIcon(icon) expand_action.setToolTip('Toggle details') expand_action.setStatusTip('Toggle details') button = QtWidgets.QToolButton() button.setDefaultAction(expand_action) hbox.addWidget(button) # Enable checkbox enabled = QtWidgets.QCheckBox() enabled.setToolTip('Enable/Disable Component') hbox.addWidget(enabled) # Breakpoint self.break_point_action = QtWidgets.QAction('Breakpoint', self) self.break_point_action.setCheckable(True) self.break_point_action.setIcon(self.break_point_disabled_icon) self.break_point_action.setToolTip('Set break point at component.') self.break_point_action.setStatusTip('Set break point at component.') self.break_point_action.toggled.connect(self.set_break_point) button = QtWidgets.QToolButton() button.setDefaultAction(self.break_point_action) hbox.addWidget(button) # Execute button action = QtWidgets.QAction('Execute', self) icon = QtGui.QIcon(QtGui.QPixmap(':/timeplay.png')) action.setIcon(icon) action.setToolTip('Execute the component') action.setStatusTip('Execute the component') action.triggered.connect( partial(self.execute_component, on_error=parent.on_component_execution_error)) button = QtWidgets.QToolButton() button.setDefaultAction(action) hbox.addWidget(button) # Image label label = QtWidgets.QLabel() label.setPixmap(comp.image(size=24)) label.setToolTip(comp.__class__.__doc__) hbox.addWidget(label) # Name label label = QtWidgets.QLabel(comp.name().split('.')[-1]) label.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) label.setToolTip(comp.__class__.__doc__) font = QtGui.QFont() font.setPointSize(14) label.setFont(font) hbox.addWidget(label) hbox.addStretch() if comp.help_url(): action = QtWidgets.QAction('Help', self) icon = QtGui.QIcon(QtGui.QPixmap(':/help.png')) action.setIcon(icon) action.setToolTip('Open help documentation.') action.setStatusTip('Open help documentation.') action.triggered.connect(partial(webbrowser.open, comp.help_url())) button = QtWidgets.QToolButton() button.setDefaultAction(action) hbox.addWidget(button) action = QtWidgets.QAction('Delete', self) icon = QtGui.QIcon(QtGui.QPixmap(':/smallTrash.png')) action.setIcon(icon) message = 'Delete Component' action.setToolTip(message) action.setStatusTip(message) action.triggered.connect(partial(self.remove, prompt=True)) button = QtWidgets.QToolButton() button.setDefaultAction(action) hbox.addWidget(button) content_vbox = QtWidgets.QVBoxLayout() content_vbox.setContentsMargins(16, 0, 0, 0) vbox.addLayout(content_vbox) content_widget = comp.widget() content_vbox.addWidget(content_widget) enabled.toggled.connect(content_widget.setEnabled) enabled.setChecked(comp.enabled) enabled.toggled.connect(comp.set_enabled) expand_action.toggled.connect(content_widget.setVisible) content_widget.setVisible(False)
class ComponentWidget(QtWidgets.QFrame): """The widget used to display a Component in the ComponentQueue.""" normal_color = QtGui.QColor(72, 170, 181) error_color = QtGui.QColor(217, 83, 79) break_point_disabled_icon = QtGui.QIcon(QtGui.QPixmap(':/stopClip.png')) break_point_enabled_icon = QtGui.QIcon(QtGui.QPixmap(':/timestop.png')) def __init__(self, comp, queue, parent=None): super(ComponentWidget, self).__init__(parent) self.setMouseTracking(True) self.queue_layout = parent.queue_layout self.queue = queue self.comp = comp vbox = QtWidgets.QVBoxLayout(self) self.setFrameStyle(QtWidgets.QFrame.StyledPanel) self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) self.over_grab_hotspot = False self.color = ComponentWidget.normal_color # Header hbox = QtWidgets.QHBoxLayout() self.header_layout = hbox hbox.setContentsMargins(16, 4, 4, 4) vbox.addLayout(hbox) # Expand toggle expand_action = QtWidgets.QAction('Toggle', self) expand_action.setCheckable(True) icon = QtGui.QIcon(QtGui.QPixmap(':/arrowDown.png')) expand_action.setIcon(icon) expand_action.setToolTip('Toggle details') expand_action.setStatusTip('Toggle details') button = QtWidgets.QToolButton() button.setDefaultAction(expand_action) hbox.addWidget(button) # Enable checkbox enabled = QtWidgets.QCheckBox() enabled.setToolTip('Enable/Disable Component') hbox.addWidget(enabled) # Breakpoint self.break_point_action = QtWidgets.QAction('Breakpoint', self) self.break_point_action.setCheckable(True) self.break_point_action.setIcon(self.break_point_disabled_icon) self.break_point_action.setToolTip('Set break point at component.') self.break_point_action.setStatusTip('Set break point at component.') self.break_point_action.toggled.connect(self.set_break_point) button = QtWidgets.QToolButton() button.setDefaultAction(self.break_point_action) hbox.addWidget(button) # Execute button action = QtWidgets.QAction('Execute', self) icon = QtGui.QIcon(QtGui.QPixmap(':/timeplay.png')) action.setIcon(icon) action.setToolTip('Execute the component') action.setStatusTip('Execute the component') action.triggered.connect( partial(self.execute_component, on_error=parent.on_component_execution_error)) button = QtWidgets.QToolButton() button.setDefaultAction(action) hbox.addWidget(button) # Image label label = QtWidgets.QLabel() label.setPixmap(comp.image(size=24)) label.setToolTip(comp.__class__.__doc__) hbox.addWidget(label) # Name label label = QtWidgets.QLabel(comp.name().split('.')[-1]) label.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) label.setToolTip(comp.__class__.__doc__) font = QtGui.QFont() font.setPointSize(14) label.setFont(font) hbox.addWidget(label) hbox.addStretch() if comp.help_url(): action = QtWidgets.QAction('Help', self) icon = QtGui.QIcon(QtGui.QPixmap(':/help.png')) action.setIcon(icon) action.setToolTip('Open help documentation.') action.setStatusTip('Open help documentation.') action.triggered.connect(partial(webbrowser.open, comp.help_url())) button = QtWidgets.QToolButton() button.setDefaultAction(action) hbox.addWidget(button) action = QtWidgets.QAction('Delete', self) icon = QtGui.QIcon(QtGui.QPixmap(':/smallTrash.png')) action.setIcon(icon) message = 'Delete Component' action.setToolTip(message) action.setStatusTip(message) action.triggered.connect(partial(self.remove, prompt=True)) button = QtWidgets.QToolButton() button.setDefaultAction(action) hbox.addWidget(button) content_vbox = QtWidgets.QVBoxLayout() content_vbox.setContentsMargins(16, 0, 0, 0) vbox.addLayout(content_vbox) content_widget = comp.widget() content_vbox.addWidget(content_widget) enabled.toggled.connect(content_widget.setEnabled) enabled.setChecked(comp.enabled) enabled.toggled.connect(comp.set_enabled) expand_action.toggled.connect(content_widget.setVisible) content_widget.setVisible(False) # content_widget = QtWidgets.QWidget() # content_layout = QtWidgets.QVBoxLayout(content_widget) # content_layout.setContentsMargins(16, 8, 0, 0) # vbox.addWidget(content_widget) # comp.draw(content_layout) # enabled.toggled.connect(content_widget.setEnabled) # enabled.setChecked(comp.enabled) # enabled.toggled.connect(comp.set_enabled) # expand_action.toggled.connect(content_widget.setVisible) # content_widget.setVisible(False) # content_layout.addStretch() def execute_component(self, on_error): self.set_color(ComponentWidget.normal_color) self.comp.capture_execute(on_error=on_error) def set_break_point(self, value): self.comp.break_point = value icon = self.break_point_enabled_icon if value else self.break_point_disabled_icon self.break_point_action.setIcon(icon) def grab_rect(self): """Get the rectangle describing the grab hotspot.""" return QtCore.QRect(0, 0, 16, self.height() - 1) def set_color(self, color): """Set the color of status bar on the widget. :param color: The new color. """ self.color = color self.update() def paintEvent(self, event): """Override the paintEvent to draw the grab hotspot. :param event: """ super(ComponentWidget, self).paintEvent(event) painter = QtGui.QPainter(self) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(self.color) painter.drawRect(self.grab_rect()) def mouseMoveEvent(self, event): contains = self.grab_rect().contains(event.pos()) if contains and not self.over_grab_hotspot: QtWidgets.qApp.setOverrideCursor( QtGui.QCursor(QtCore.Qt.OpenHandCursor)) self.over_grab_hotspot = True elif not contains and self.over_grab_hotspot: restore_cursor() self.over_grab_hotspot = False def leaveEvent(self, event): if self.over_grab_hotspot: restore_cursor() self.over_grab_hotspot = False def move(self, new_index): """Move the component to the specified index in the queue. :param new_index: Index to move to. """ index = self.index() # Reorder the Component in the Queue self.queue.remove(index) if new_index and new_index > self.queue.length(): # When we are moving a component to the bottom, the index may get greater than the max allowed new_index = self.queue.length() self.queue.insert(new_index, self.comp) # Reorder the ComponentWidget in the layout self.queue_layout.takeAt(index) self.queue_layout.insertWidget(new_index, self) def index(self): """Get the index of the Component. """ return self.queue.index(self.comp) def remove(self, prompt=False): """Remove this Component from the queue. :param prompt: True to display a message box confirming the removal of the Component. """ if prompt: msg_box = QtWidgets.QMessageBox() msg_box.setIcon(QtWidgets.QMessageBox.Question) msg_box.setText('Are you sure you want to remove this component?') msg_box.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) if msg_box.exec_() != QtWidgets.QMessageBox.Yes: return index = self.queue.index(self.comp) self.queue.remove(index) self.queue_layout.takeAt(index) self.deleteLater()
class TestNode(shortcuts.BaseTreeNode): """A node representing a Test, TestCase, or TestSuite for display in a QTreeView.""" success_icon = QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_test_success.png')) fail_icon = QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_test_fail.png')) error_icon = QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_test_error.png')) skip_icon = QtGui.QPixmap(os.path.join(ICON_DIR, 'cmt_test_skip.png')) def __init__(self, test, parent=None): super(TestNode, self).__init__(parent) self.test = test self.tool_tip = str(test) self.status = TestStatus.not_run if isinstance(self.test, unittest.TestSuite): for test_ in self.test: if isinstance(test_, unittest.TestCase) or test_.countTestCases(): self.add_child(TestNode(test_, self)) def name(self): """Get the name to print in the view.""" if isinstance(self.test, unittest.TestCase): return self.test._testMethodName elif isinstance(self.child(0).test, unittest.TestCase): return self.child(0).test.__class__.__name__ else: return self.child(0).child(0).test.__class__.__module__ def path(self): """Gets the import path of the test. Used for finding the test by name.""" if self.parent() and self.parent().parent(): return '{0}.{1}'.format(self.parent().path(), self.name()) else: return self.name() def get_status(self): """Get the status of the TestNode. Nodes with children like the TestSuites, will get their status based on the status of the leaf nodes (the TestCases). @return: A status value from TestStatus. """ if not self.children: return self.status result = TestStatus.not_run for child in self.children: child_status = child.get_status() if child_status == TestStatus.error: # Error status has highest priority so propagate that up to the parent return child_status elif child_status == TestStatus.fail: result = child_status elif child_status == TestStatus.success and result != TestStatus.fail: result = child_status elif child_status == TestStatus.skipped and result != TestStatus.fail: result = child_status return result def get_icon(self): """Get the status icon to display with the Test.""" status = self.get_status() return [None, TestNode.success_icon, TestNode.fail_icon, TestNode.error_icon, TestNode.skip_icon][status]
def __init__(self, parent=None): super(ControlWindow, self).__init__(parent) self.setWindowTitle('CMT Control Creator') self.resize(300, 500) vbox = QtWidgets.QVBoxLayout(self) size = 20 label_width = 60 icon_left = QtGui.QIcon( QtGui.QPixmap(':/nudgeLeft.png').scaled(size, size)) icon_right = QtGui.QIcon( QtGui.QPixmap(':/nudgeRight.png').scaled(size, size)) validator = QtGui.QDoubleValidator(-180.0, 180.0, 2) grid = QtWidgets.QGridLayout() vbox.addLayout(grid) # Rotate X label = QtWidgets.QLabel('Rotate X') label.setMaximumWidth(label_width) grid.addWidget(label, 0, 0, QtCore.Qt.AlignRight) b = QtWidgets.QPushButton(icon_left, '') b.released.connect(partial(self.rotate_x, direction=-1)) grid.addWidget(b, 0, 1) self.offset_x = QtWidgets.QLineEdit('45.0') self.offset_x.setValidator(validator) grid.addWidget(self.offset_x, 0, 2) b = QtWidgets.QPushButton(icon_right, '') b.released.connect(partial(self.rotate_x, direction=1)) grid.addWidget(b, 0, 3) # Rotate Y label = QtWidgets.QLabel('Rotate Y') label.setMaximumWidth(label_width) grid.addWidget(label, 1, 0, QtCore.Qt.AlignRight) b = QtWidgets.QPushButton(icon_left, '') b.released.connect(partial(self.rotate_y, direction=-1)) grid.addWidget(b, 1, 1) self.offset_y = QtWidgets.QLineEdit('45.0') self.offset_y.setValidator(validator) grid.addWidget(self.offset_y, 1, 2) b = QtWidgets.QPushButton(icon_right, '') b.released.connect(partial(self.rotate_y, direction=1)) grid.addWidget(b, 1, 3) # Rotate Z label = QtWidgets.QLabel('Rotate Z') label.setMaximumWidth(label_width) grid.addWidget(label, 2, 0, QtCore.Qt.AlignRight) b = QtWidgets.QPushButton(icon_left, '') b.released.connect(partial(self.rotate_z, direction=-1)) grid.addWidget(b, 2, 1) self.offset_z = QtWidgets.QLineEdit('45.0') self.offset_z.setValidator(validator) grid.addWidget(self.offset_z, 2, 2) b = QtWidgets.QPushButton(icon_right, '') b.released.connect(partial(self.rotate_z, direction=1)) grid.addWidget(b, 2, 3) grid.setColumnStretch(2, 2) hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) b = QtWidgets.QPushButton('Export Selected') b.released.connect(self.dump_controls) hbox.addWidget(b) b = QtWidgets.QPushButton('Set Color') b.released.connect(self.set_color) hbox.addWidget(b) hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) b = QtWidgets.QPushButton('Create Selected') b.released.connect(self.create_selected) hbox.addWidget(b) b = QtWidgets.QPushButton('Remove Selected') b.released.connect(self.remove_selected) hbox.addWidget(b) hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) self.stack_count = QtWidgets.QSpinBox() self.stack_count.setValue(2) hbox.addWidget(self.stack_count) b = QtWidgets.QPushButton('Create Transform Stack') b.released.connect(self.create_transform_stack) b.setToolTip('Creates a transform stack above each selected node.') hbox.addWidget(b) self.control_list = QtWidgets.QListWidget() self.control_list.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection) vbox.addWidget(self.control_list) self.populate_controls()