class BaseRigToolBoxWidget(base.BaseFrame, object): emitInfo = Signal(str) emitWarning = Signal(str) emitError = Signal(str) setInfo = Signal(str, str, str) def __init__(self, title, parent=None): self._title = title super(BaseRigToolBoxWidget, self).__init__(parent=parent) @property def title(self): return self._title def get_main_layout(self): main_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) main_layout.setAlignment(Qt.AlignTop) return main_layout def ui(self): super(BaseRigToolBoxWidget, self).ui() self._content_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self.main_layout.addLayout(self._content_layout)
class _CommandSignals(QObject, object): command = None startCommand = Signal(str) progressCommand = Signal(int, str) endCommand = Signal(bool)
class CollapsibleSplitter(QSplitter, object): doExpand = Signal() doCollapse = Signal() def __init__(self, orientation=Qt.Vertical, parent=None): self._handle = None super(CollapsibleSplitter, self).__init__(parent) self.setHandleWidth(16) self.setOrientation(orientation) def createHandle(self): self._handle = CollapsibleSplitterHandle(self.orientation(), self) self._handle.buttonClicked.connect(self._on_update_splitter) return self._handle def _on_update_splitter(self, expand): if expand: self.setSizes([1, 1]) else: self.setSizes([1, 0]) def expand(self): self._handle.expand() self.doExpand.emit() def collapse(self): self._handle.collapse() self.doCollapse.emit()
class JointsLabelModel(QObject, object): leftSideChanged = Signal(str) rightSideChanged = Signal(str) def __init__(self): super(JointsLabelModel, self).__init__() self._left_side = '*_l_*' self._right_side = '*_r_*' @property def left_side(self): return self._left_side @left_side.setter def left_side(self, value): self._left_side = str(value) self.leftSideChanged.emit(self._left_side) @property def right_side(self): return self._right_side @right_side.setter def right_side(self, value): self._right_side = str(value) self.rightSideChanged.emit(self._right_side)
class skywindBatchWindow(batchTools_ui.batchWindow): onBatch = Signal(list, str, str, str, str) onGetSelected = Signal(QtWidgets.QWidget) def __init__(self, *args, **kwargs): batchTools_ui.batchWindow.__init__(self, *args, **kwargs) self.setWindowTitle('Skywind Batch Retarget') def _setupSettings(self): # Set up the base settings batchTools_ui.batchWindow._setupSettings(self) # Add a setting for selecting a rig file self.rig = batchTools_ui.fileDirectoryWidget(self.settingsGroup) self.settingsLayout.addRow('Rig File', self.rig) # Add a setting for selecting a between batch script self.script = batchTools_ui.fileDirectoryWidget(self.settingsGroup) self.settingsLayout.addRow('Batch Script', self.script) self.root_name = batchTools_ui.nodeNameWidget(self.settingsGroup) self.root_name.OnGetSelected.connect(self.onGetSelected) self.settingsLayout.addRow('Root Name', self.root_name) def onBatchClicked(self): self.onBatch.emit(self.source.value, self.destination.value, self.rig.value, self.script.value, self.root_name.value)
class Launcher(QObject): standard_output_signal = Signal(str) standard_error_signal = Signal(str) def start(self, script: str, binding: str) -> None: process = QProcess(self) process.readyReadStandardError.connect(self._handle_standard_error) process.readyReadStandardOutput.connect(self._handle_standard_output) process.stateChanged.connect(print) env = QProcessEnvironment.systemEnvironment() env.insert("QT_DEBUG_PLUGINS", "1") env.insert("PYTHONPATH", os.path.join(CURRENT_DIR, "vendor")) env.insert("QT_PREFERRED_BINDING", binding) process.setProcessEnvironment(env) process.start(sys.executable, [script]) @Slot() def _handle_standard_error(self): process: QProcess = self.sender() err = process.readAllStandardError().data().decode() self.standard_error_signal.emit(err) @Slot() def _handle_standard_output(self): process: QProcess = self.sender() out = process.readAllStandardOutput().data().decode() self.standard_output_signal.emit(out)
class Tag(QLabel, object): closed = Signal() clicked = Signal() def __init__(self, text='', parent=None): super(Tag, self).__init__(text=text, parent=parent) self._is_pressed = False
class ReplacerWidgetModel(QObject, object): searchReplaceCheckChanged = Signal(bool) searchChanged = Signal(str) replaceChanged = Signal(str) def __init__(self): super(ReplacerWidgetModel, self).__init__() self._global_data = dict() self._search_replace_check = False self._search = '' self._replace = '' @property def global_data(self): return self._global_data @global_data.setter def global_data(self, global_data_dict): self._global_data = global_data_dict @property def search_replace_check(self): return self._search_replace_check @search_replace_check.setter def search_replace_check(self, value): self._search_replace_check = bool(value) self.searchReplaceCheckChanged.emit(self._search_replace_check) @property def search(self): return self._search @search.setter def search(self, value): self._search = str(value) self.searchChanged.emit(self._search) @property def replace(self): return self._replace @replace.setter def replace(self, value): self._replace = str(value) self.replaceChanged.emit(self._replace) @property def rename_settings(self): search_str = self.search if self.search_replace_check else '' replace_str = self.replace if self.search_replace_check else '' return { 'search': search_str, 'replace': replace_str }
class ListView(BaseListView, object): """ Extends basic QListView functionality """ current_selection_changed = Signal(object, object) middle_clicked = Signal(QModelIndex) def __init__(self, parent=None): super(ListView, self).__init__(parent) # region Public Functions def appendItem(self, item): """ Adds a new item to the list view model :param item: variant """ if self.model(): self.model().appendItem(item) # endregion # region Override Functions def selectionChanged(self, selected, deselected): """ Override selectionChanged behavior :param selected: list<variant>, new selection :param deselected: list<variant>, previous selection :warning: This method is override to avoid a Qt 4.7.0 QModelIndexList destructor crash. See ItemSelection for more information. """ self.current_selection_changed.emit(models.ItemSelection(selected), models.ItemSelection(deselected)) super(ListView, self).selectionChanged(models.ItemSelection(selected), models.ItemSelection(deselected)) def selectedIndexes(self): """ Override selectedIndexes behavior :warning: This method is override to avoid a Qt 4.7.0 QModelIndexList destructor crash. See ItemSelection for more information. """ item_selection = models.ItemSelection(self.selectionModel().selection()) return item_selection.indexes() def mousePressEvent(self, event): """ Override mousePressEvent behavior to add a custom middle click signal :param event: QMouseEvent """ if event.button() == Qt.MidButton: model_index = self.indexAt(event.pos()) if model_index.row() >= 0: self.middle_clicked.emit(model_index) super(ListView, self).mousePressEvent(event)
class NamespaceWidgetModel(QObject, object): namespaceCheckChanged = Signal(bool) namespaceChanged = Signal(str) namespaceOptionChanged = Signal(int) def __init__(self): super(NamespaceWidgetModel, self).__init__() self._global_data = dict() self._namespace_check = True self._namespace = '' self._namespace_option = 0 @property def global_data(self): return self._global_data @global_data.setter def global_data(self, global_data_dict): self._global_data = global_data_dict @property def namespace_check(self): return self._namespace_check @namespace_check.setter def namespace_check(self, flag): self._namespace_check = bool(flag) self.namespaceCheckChanged.emit(self._namespace_check) @property def namespace(self): return self._namespace @namespace.setter def namespace(self, value): self._namespace = str(value) self.namespaceChanged.emit(self._namespace) @property def namespace_option(self): return self._namespace_option @namespace_option.setter def namespace_option(self, value): self._namespace_option = int(value) self.namespaceOptionChanged.emit(self._namespace_option) @property def rename_settings(self): namespace = self.namespace if self.namespace_check else '' namespace_option = self.namespace_option return {'namespace': namespace, 'namespace_option': namespace_option}
class BaseItemCommunicator(QObject, object): sendCommandData = Signal(str, list, list, list, str) itemChanged = Signal(str) sizeChanged = Signal() redefineMember = Signal(QGraphicsItem) changeMember = Signal(QGraphicsItem, str, bool, str) editRemote = Signal(QGraphicsItem) mousePressed = Signal() mouseReleased = Signal() aboutToRemove = Signal(QGraphicsItem)
class ClickBrowserFolderButton(buttons.BaseButton, object): folderChanged = Signal(str) foldersChanged = Signal(list) _on_browse_folder = browse_folder def __init__(self, text='', multiple=False, parent=None): super(ClickBrowserFolderButton, self).__init__(text=text, parent=parent) self._path = None self._multiple = multiple self.setToolTip('Click to browse folder') self.clicked.connect(self._on_browse_folder) def _get_path(self): """ Returns last browse file path :return: str """ return self._path def _set_path(self, value): """ Sets browse start path :param value: str """ self._path = value def _get_multiple(self): """ Returns whether or not browse can select multiple files :return: bool """ return self._multiple def _set_multiple(self, flag): """ Sets whether or not browse can select multiple files :param flag: bool """ self._multiple = flag path = Property(str, _get_path, _set_path) multiple = Property(bool, _get_multiple, _set_multiple)
class InterpolateItModel(QObject, object): addInterpolatorWidget = Signal(object) removeInterpolatorWidget = Signal(object) prepareLoad = Signal() def __init__(self): super(InterpolateItModel, self).__init__() self._interpolator_widgets = list() @property def interpolator_widgets(self): return self._interpolator_widgets
class TextInput(QWidget): # used when input text inputChanged = Signal() okPressed = Signal() cancelPressed = Signal() def __init__(self, parent=None): super(TextInput, self).__init__(parent) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint) self.mainLayout = QVBoxLayout() self.textArea = QTextEdit(self) self.buttonArea = QWidget(self) self.buttonLayout = QHBoxLayout() self.cancelButton = QPushButton('Cancel', self) self.okButton = QPushButton('Ok', self) self.buttonLayout.addWidget(self.cancelButton) self.buttonLayout.addWidget(self.okButton) self.buttonArea.setLayout(self.buttonLayout) self.mainLayout.addWidget(self.textArea) self.mainLayout.addWidget(self.buttonArea) self.setLayout(self.mainLayout) self.textArea.textChanged.connect(self.textChanged_) self.okButton.clicked.connect(self.okButtonClicked) self.cancelButton.clicked.connect(self.cancelPressed) def getText(self): return self.textArea.toPlainText() def getFocus(self): self.setFocus() self.textArea.setFocus() def clearText(self): self.textArea.clear() # slots def textChanged_(self): self.inputChanged.emit() def cancelButtonClicked(self): self.cancelPressed.emit() def okButtonClicked(self): self.okPressed.emit()
class RadioButtonGroup(BaseButtonGroup, object): checkedChanged = Signal(int) def __init__(self, orientation=Qt.Horizontal, parent=None): super(RadioButtonGroup, self).__init__(orientation=orientation, parent=parent) self._button_group.setExclusive(True) self.set_spacing(15) def setup_signals(self): self._button_group.buttonClicked.connect(self.checkedChanged) def create_button(self, data_dict): """ Implements BaseButtonGroup create_button abstract function :param data_dict: :return: """ return buttons.BaseRadioButton() def _get_checked(self): return self._button_group.checkedId() def _sert_checked(self, value): btn = self._button_group.button(value) if btn: btn.setChecked(True) self.checkedChanged.emiet(value) checked = Property(int, _get_checked, _sert_checked, notify=checkedChanged)
class BaseSpinBoxNumber(BaseNumberWidget): enterPressed = Signal() def __init__(self, name='', parent=None): super(BaseSpinBoxNumber, self).__init__(name=name, parent=parent) self._setup_spin_widget() def keyPressEvent(self, event): if event.key() in [Qt.Key_Return, Qt.Key_Enter]: self.enterPressed.emit() def get_number_widget(self): spin_box = BaseSpinBox() spin_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) return spin_box def _setup_spin_widget(self): if hasattr(self._number_widget, 'CorrectToNearestValue'): self._number_widget.setCorrectionMode( self._number_widget.CorrectToNearestValue) if hasattr(self._number_widget, 'setWrapping'): self._number_widget.setWrapping(False) if hasattr(self._number_widget, 'setDecimals'): self._number_widget.setDecimals(3) self._number_widget.setMaximum(100000000) self._number_widget.setMinimum(-100000000) # self._number_widget.setButtonSymbols(self._number_widget.NoButtons) self._number_widget.valueChanged.connect(self._on_value_changed)
class ConnectionWidget(QWidget): deleted = Signal(str) def __init__(self, port, connection, system): super(ConnectionWidget, self).__init__() uiPath = os.path.join(os.path.dirname(__file__), "ui", "connectionWidget.ui") QtCompat.loadUi(uiPath, self) self._system = system self._connection = connection self._port = port Widget = getConnectionWidgetClass(connection.type()) widget = Widget(connection, system) self.uiNameGRP.layout().addWidget(widget) # Set Title title = "{k} [{t}]".format(k=self._port, t=self._connection.type().title()) self.uiNameGRP.setTitle(title) # Connect self.uiDeleteBTN.clicked.connect(self.delete) def delete(self): self.deleted.emit(self._port)
class TitleFrame(QFrame, object): clicked = Signal() def __init__(self, title='', icon=None, collapsed=False, parent=None): super(TitleFrame, self).__init__(parent=parent) self.setMinimumHeight(24) # self.move(QPoint(24, 0)) # self.setStyleSheet('border 1px solid rgb(41, 41, 41);') CHANGED layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self.setLayout(layout) self._arrow = ExpandableArrow(collapsed=collapsed) # self._arrow.setStyleSheet('border:0px;') CHANGED self._title = label.BaseLabel(title, parent=self).strong() self._title.setMinimumHeight(24) # self._title.move(QPoint(24, 0)) # self._title.setStyleSheet('border: 0px;') CHANGED layout.addWidget(self._arrow) layout.addWidget(self._title) def mousePressEvent(self, event): self.clicked.emit() return super(TitleFrame, self).mousePressEvent(event) def set_title(self, title): self._title.setText(title)
class DirectoryWidget(base.BaseWidget, object): directoryChanged = Signal(object) def __init__(self, name, parent=None): self._name = name super(DirectoryWidget, self).__init__(parent=parent) def ui(self): super(DirectoryWidget, self).ui() self.directory_widget = directory.GetDirectoryWidget() self.main_layout.addWidget(self.directory_widget) def setup_signals(self): self.directory_widget.directoryChanged.connect( self.directoryChanged.emit) def get_directory(self): return self.directory_widget.get_directory() def set_directory(self, value): self.directory_widget.set_directory(value) def get_label_text(self): return self._name
class ObservableProxy(QObject): """ Observer class that allows us to invoke callbacks in UI threads from non UI threads. """ observerSignal = Signal(str, object) def __init__(self): super(ObservableProxy, self).__init__() self._mapping = dict() self.observerSignal.connect(self._on_call) # ================================================================================================================= # BASE # ================================================================================================================= def add_mapping(self, callback): callback_uuid = str(uuid4()) proxy_callback = partial(self.observerSignal.emit, callback_uuid) self._mapping[callback_uuid] = callback return proxy_callback # ================================================================================================================= # CALLBACKS # ================================================================================================================= def _on_call(self, uuid, *args, **kwargs): if uuid in self._mapping: self._mapping[uuid](args, kwargs)
class CollapsibleSplitterButton(buttons.HoverButton, object): clickedButton = Signal() def __init__(self, icon=None, hover_icon=None, pressed_icon=None, parent=None): super(CollapsibleSplitterButton, self).__init__(icon=icon, hover_icon=hover_icon, pressed_icon=pressed_icon, parent=parent) self._can_emit_signal = True def mouseMoveEvent(self, event): super(CollapsibleSplitterButton, self).mouseMoveEvent(event) self._can_emit_signal = False if self._mouse_pressed: if self._pressed_icon: self.setIcon(self._pressed_icon) else: self.setIcon(self._idle_icon) def mouseReleaseEvent(self, event): if self.rect().contains(event.pos()) and self._can_emit_signal: self.clickedButton.emit() self._can_emit_signal = True super(CollapsibleSplitterButton, self).mouseReleaseEvent(event)
class BaseFrame(QFrame, object): mouseReleased = Signal(object) def __init__(self, *args, **kwargs): super(BaseFrame, self).__init__(*args, **kwargs) self.ui() self.setup_signals() def mouseReleaseEvent(self, event): self.mouseReleased.emit(event) return super(BaseFrame, self).mouseReleaseEvent(event) def get_main_layout(self): """ Function that generates the main layout used by the widget Override if necessary on new widgets :return: QLayout """ return layouts.VerticalLayout(spacing=2, margins=(2, 2, 2, 2)) def ui(self): """ Function that sets up the ui of the widget Override it on new widgets (but always call super) """ self.main_layout = self.get_main_layout() self.setLayout(self.main_layout) def setup_signals(self): pass
class ClickTimer(QObject, object): executed = Signal() def __init__(self): super(ClickTimer, self).__init__() self.timer_id = None self.init_data() def set_data(self, button, modifier, pos, selected): self.button = button self.modifiers = modifier self.pos = pos self.is_selected = selected def init_data(self): self.button = None self.modifiers = None self.pos = None self.is_selected = False def start(self, interval): self.timer_id = self.startTimer(interval) def remove_timer(self): if self.timer_id: self.killTimer(self.timer_id) self.timer_id = None return def timerEvent(self, event): if self.timer_id == event.timerId(): self.executed.emit() self.remove_timer()
class batchWindow(QtWidgets.QMainWindow): onBatch = Signal(list, str) onGetSelected = Signal() def __init__(self, *args, **kwargs): QtWidgets.QMainWindow.__init__(self, *args, **kwargs) # Set the window title self.setWindowTitle('Batch Export') # Create the central widget self.mainWidget = QtWidgets.QWidget(self) self.setCentralWidget(self.mainWidget) # Create the window layout self.mainLayout = QtWidgets.QVBoxLayout() self.mainWidget.setLayout(self.mainLayout) # Create the settings widgets self._setupSettings() # The button to initiate the batch batchButton = QtWidgets.QPushButton('Batch', self) batchButton.clicked.connect(self.onBatchClicked) self.mainLayout.addWidget(batchButton) def _setupSettings(self): # Create a groupBox to house the settings self.settingsGroup = QtWidgets.QGroupBox('Settings', self.mainWidget) self.mainLayout.addWidget(self.settingsGroup) # Create a layout for the settings self.settingsLayout = QtWidgets.QFormLayout() self.settingsGroup.setLayout(self.settingsLayout) # Source directory widget self.source = multiFileDirectoryWidget(self.mainWidget) self.settingsLayout.addRow('Source Files', self.source) # Destination directory widget self.destination = directoryWidget(self.mainWidget) self.settingsLayout.addRow('Destination', self.destination) def onBatchClicked(self): self.onBatch.emit(self.source.value, self.destination.value)
class ComboWidget(base.BaseWidget, object): valueChanged = Signal(object) def __init__(self, name, parent=None): self._name = name super(ComboWidget, self).__init__(parent=parent) def get_main_layout(self): main_layout = layouts.HorizontalLayout() main_layout.setSpacing(2) main_layout.setContentsMargins(2, 2, 2, 2) return main_layout def ui(self): super(ComboWidget, self).ui() self._label = label.BaseLabel(self._name) self._label.setAlignment(Qt.AlignRight) self._label.setMinimumWidth(75) self._label.setAttribute(Qt.WA_TransparentForMouseEvents) self._combo_widget = combobox.BaseComboBox() self._combo_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.main_layout.addWidget(self._label) self.main_layout.addWidget(self._combo_widget) def get_value(self): items_text = [ self._combo_widget.itemText(i) for i in range(self._combo_widget.count()) ] return [ items_text, [ self._combo_widget.currentIndex(), self._combo_widget.currentText() ] ] def set_value(self, value): items = value[0] current_list = value[1] if len(value) > 1 else None for value in items: self._combo_widget.addItem(value) if current_list: index = current_list[0] if index >= 0: self._combo_widget.setCurrentIndex(index) def get_name(self): return self._label.text() def set_name(self, value): self._label.setText(value) def setup_signals(self): self._combo_widget.currentIndexChanged.connect(self.valueChanged.emit)
class CookieWidget(QWidget, Ui_CookieWidget): def __init__(self, cookie: QNetworkCookie, parent: QWidget = None) -> None: super().__init__(parent) self.setupUi(self) self.setAutoFillBackground(True) self.m_nameLabel.setText(cookie.name().data().decode()) self.m_domainLabel.setText(cookie.domain()) self.m_viewButton.clicked.connect(self.viewClicked) self.m_deleteButton.clicked.connect(self.deleteClicked) def setHighlighted(self, enabled: bool) -> None: p = self.palette() p.setColor(self.backgroundRole(), QColor(0xF0, 0xF8, 0xFF) if enabled else Qt.white) self.setPalette(p) deleteClicked = Signal() viewClicked = Signal()
class RenamerWidgetModel(QObject, object): checkChanged = Signal(bool) nameChanged = Signal(str) doRename = Signal() def __init__(self): super(RenamerWidgetModel, self).__init__() self._global_data = dict() self._check = True self._name = '' @property def global_data(self): return self._global_data @global_data.setter def global_data(self, global_data_dict): self._global_data = global_data_dict @property def check(self): return self._check @check.setter def check(self, flag): self._check = bool(flag) self.checkChanged.emit(self._check) @property def name(self): return self._name @name.setter def name(self, value): self._name = str(value) self.nameChanged.emit(self._name) @property def rename_settings(self): text = str(self.name.strip()) if self.check else '' return {'name': text}
class ListItemWidget(base.BaseWidget, object): valueChanged = Signal(object) itemRemoved = Signal(object) def __init__(self, name=None, parent=None): self._value = name self._garbage = None super(ListItemWidget, self).__init__(parent=parent) def get_main_layout(self): main_layout = layouts.HorizontalLayout() main_layout.setAlignment(Qt.AlignRight) main_layout.setSpacing(0) main_layout.setContentsMargins(0, 0, 0, 0) return main_layout def ui(self): super(ListItemWidget, self).ui() self._value_str = text.TextWidget() self._value_str.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self._value_str.set_use_button(False) if self._value is not None: self._value_str.set_text(str(self._value)) self._value_str.set_placeholder('Set a value') self._remove_btn = buttons.BaseToolButton().image('delete').icon_only() self.main_layout.addWidget(self._value_str) self.main_layout.addSpacing(10) self.main_layout.addWidget(self._remove_btn) def setup_signals(self): self._value_str.textChanged.connect(self.valueChanged.emit) self._remove_btn.clicked.connect(self._on_remove_item) def get_value(self): return self._value_str.get_text() def _on_remove_item(self): self._garbage = True self.itemRemoved.emit(self)
class RotationAxistModel(QObject, object): rotationAxisChanged = Signal(int) affectChildrenChanged = Signal(bool) def __init__(self): super(RotationAxistModel, self).__init__() self._available_rotation_axis = OrderedDict({ 'Wrist': 'YXZ', 'Finger': 'XYZ', 'Spine': 'ZYX', 'Hips': 'ZYX', 'Root': 'ZYX', 'Upper Leg': 'ZYX', 'Knee': 'YXZ', 'Ankle': 'XZY' }) self._rotation_axis = 0 self._affect_children = False @property def available_rotation_axis(self): return self._available_rotation_axis @property def rotation_axis(self): return self._rotation_axis @rotation_axis.setter def rotation_axis(self, value): self._rotation_axis = int(value) self.rotationAxisChanged.emit(value) @property def affect_children(self): return self._affect_children @affect_children.setter def affect_children(self, flag): self._affect_children = bool(flag) self.affectChildrenChanged.emit(self._affect_children)
class ExternalCodeList(QListWidget): directoriesChanged = Signal(object) def __init__(self, parent=None): super(ExternalCodeList, self).__init__(parent) self.setAlternatingRowColors(True) self.setSelectionMode(self.NoSelection) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self._on_item_menu) self._create_context_menu() def get_directories(self): count = self.count() found = list() if not count: return found for i in range(count): item = self.item(i) if not item: continue found.append(str(item.text())) return found def refresh(self, directories): directories = python.force_list(directories) self.clear() for diretory_found in directories: name = diretory_found if not path_utils.is_dir(diretory_found): name = 'Directory Not Valid! {}'.format(diretory_found) item = QListWidgetItem() item.setText(name) item.setSizeHint(QSize(20, 25)) self.addItem(item) def _create_context_menu(self): self._context_menu = QMenu() remove_action = self._context_menu.addAction('Remove') remove_action.setIcon(resources.icon('trash')) remove_action.triggered.connect(self._on_remove_action) def _on_item_menu(self, pos): item = self.itemAt(pos) if not item: return self._context_menu.exec_(self.viewport().mapToGlobal(pos)) def _on_remove_action(self): index = self.currentIndex() self.takeItem(index.row()) self.directoriesChanged.emit(self.get_directories())