def doLayout(self, rect, testOnly=False): x = rect.x() y = rect.y() lineHeight = 0 for item in self.itemList: wid = item.widget() spaceX = self.spacing() + wid.style().layoutSpacing( QtWidgets.QSizePolicy.PushButton, QtWidgets.QSizePolicy.PushButton, QtCore.Qt.Horizontal) spaceY = self.spacing() + wid.style().layoutSpacing( QtWidgets.QSizePolicy.PushButton, QtWidgets.QSizePolicy.PushButton, QtCore.Qt.Vertical) nextX = x + item.sizeHint().width() + spaceX if nextX - spaceX > rect.right() and lineHeight > 0: x = rect.x() y = y + lineHeight + spaceY nextX = x + item.sizeHint().width() + spaceX lineHeight = 0 if not testOnly: item.setGeometry( QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint())) x = nextX lineHeight = max(lineHeight, item.sizeHint().height()) return y + lineHeight - rect.y()
def paintEvent(self, event): '''Paint widget.''' painter = QtGui.QPainter() painter.begin(self) try: painter.setRenderHint(QtGui.QPainter.Antialiasing) area = QtCore.QRect(0, 0, painter.device().width(), painter.device().height()) center = QtCore.QPointF(area.width() / 2.0, area.height() / 2.0) # Draw in a normalised centered box. normalisedEdge = 100.0 normalisedArea = QtCore.QRectF(-(normalisedEdge / 2.0), -(normalisedEdge / 2.0), normalisedEdge, normalisedEdge) shortestSide = min(area.width(), area.height()) painter.translate(center) painter.scale(shortestSide / normalisedEdge, shortestSide / normalisedEdge) # Draw logo. svgRenderer = QtSvg.QSvgRenderer() svgRenderer.load(self._logo) logoMargin = 30.0 logoArea = normalisedArea.adjusted(logoMargin, logoMargin, -logoMargin, -logoMargin) svgRenderer.render(painter, logoArea) # Draw spinner at current spin angle. pen = QtGui.QPen() penWidth = 5.0 pen.setWidth(penWidth) gradient = QtGui.QConicalGradient(QtCore.QPoint(0, 0), -self._spinnerAngle) gradient.setColorAt(0.95, QtCore.Qt.transparent) gradient.setColorAt(0, self._spinnerColor) brush = QtGui.QBrush(gradient) pen.setBrush(brush) painter.setPen(pen) spinnerArea = QtCore.QRectF( normalisedArea.top() + (penWidth / 2.0), normalisedArea.left() + (penWidth / 2.0), normalisedArea.width() - penWidth, normalisedArea.height() - penWidth) painter.drawArc( spinnerArea, 0, # Start angle. 360 * 16 # Span angle. ) finally: painter.end()
class ActionSection(flow_layout.ScrollingFlowWidget): '''Action list view.''' #: Emitted before an action is launched with action beforeActionLaunch = QtCore.Signal(dict, name='beforeActionLaunch') #: Emitted after an action has been launched with action and results actionLaunched = QtCore.Signal(dict, list, name='actionLaunched') def clear(self): '''Remove all actions from section.''' items = self.findChildren(action_item.ActionItem) for item in items: item.setParent(None) def addActions(self, actions): '''Add *actions* to section''' for item in actions: actionItem = action_item.ActionItem(item, parent=self) actionItem.actionLaunched.connect(self._onActionLaunched) actionItem.beforeActionLaunch.connect(self._onBeforeActionLaunched) self.addWidget(actionItem) def _onActionLaunched(self, action, results): '''Forward actionLaunched signal.''' self.actionLaunched.emit(action, results) def _onBeforeActionLaunched(self, action): '''Forward beforeActionLaunch signal.''' self.beforeActionLaunch.emit(action)
def postBuild(self): '''Perform post build operations.''' self.locationSignalMapper = QtCore.QSignalMapper(self) self.locationSignalMapper.mapped[int].connect( self.onLocationSelected ) self.actionSignalMapper = QtCore.QSignalMapper(self) self.actionSignalMapper.mapped.connect( self.onActionButtonClicked )
def save(self): '''Save all cookies to settings.''' cookieList = self.allCookies() data = QtCore.QByteArray() for cookie in cookieList: if not cookie.isSessionCookie(): data.append(cookie.toRawForm()) data.append('\n') settings = QtCore.QSettings('ftrack', 'launchpad') settings.setValue('Cookies', data)
def __init__(self, *args, **kw): '''Initialise line edit.''' self._actionButtons = [] self._iconSize = QtCore.QSize( LineEditIconButton.iconSize + 2, LineEditIconButton.iconSize + 2 ) self._iconRegion = QtCore.QSize( self._iconSize.width() + LineEditIconButton.iconMargin, self._iconSize.height() ) super(LineEdit, self).__init__(*args, **kw)
def resizeEvent(self, event): wrapper = self.findChild(QtWidgets.QWidget) flow = wrapper.findChild(FlowLayout) if wrapper and flow: width = self.viewport().width() height = flow.heightForWidth(width) size = QtCore.QSize(width, height) point = self.viewport().rect().topLeft() flow.setGeometry(QtCore.QRect(point, size)) self.viewport().update() super(ResizeScrollArea, self).resizeEvent(event)
def parent(self, index): '''Return parent of *index*.''' if not index.isValid(): return QtCore.QModelIndex() item = index.internalPointer() if not item: return QtCore.QModelIndex() parent = item.parent if not parent or parent == self.root: return QtCore.QModelIndex() return self.createIndex(parent.row, 0, parent)
def apply_theme(widget, baseTheme=None): '''Apply *theme* to *widget*.''' # Set base style. if baseTheme and QtWidgets.QApplication.style().objectName() != baseTheme: QtWidgets.QApplication.setStyle(baseTheme) # Load stylesheet from resource file and apply. fileObject = QtCore.QFile(':/ftrack/pipeline/dark') fileObject.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text) stream = QtCore.QTextStream(fileObject) styleSheetContent = stream.readAll() widget.setStyleSheet(styleSheetContent)
class ChatTextEdit(QtWidgets.QTextEdit): # Signal emitted when return is pressed on it's own. returnPressed = QtCore.Signal() def __init__(self, *args, **kwargs): super(ChatTextEdit, self).__init__(*args, **kwargs) # Install event filter at application level in order to handle # return pressed events application = QtCore.QCoreApplication.instance() application.installEventFilter(self) def eventFilter(self, obj, event): '''Filter *event* sent to *obj*.''' if obj == self: if event.type() == QtCore.QEvent.KeyPress: if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): # If nativeModifiers if not equal to 0 that means return # was pressed in combination with something else. if event.nativeModifiers() == 0: self.returnPressed.emit() return True # Let event propagate. return False
class ApplicationPlugin(QtWidgets.QWidget): '''Base widget for ftrack connect application plugin.''' #: Signal to emit to request focus of this plugin in application. requestApplicationFocus = QtCore.Signal(object) #: Signal to emit to request closing application. requestApplicationClose = QtCore.Signal(object) def getName(self): '''Return name of widget.''' return self.__class__.__name__ def getIdentifier(self): '''Return identifier for widget.''' return self.getName().lower().replace(' ', '.')
class LoginServerThread(QtCore.QThread): '''Login server thread.''' # Login signal. loginSignal = QtCore.Signal(object, object, object) def start(self, url): '''Start thread.''' self.url = url super(LoginServerThread, self).start() def _handle_login(self, api_user, api_key): '''Login to server with *api_user* and *api_key*.''' self.loginSignal.emit(self.url, api_user, api_key) def run(self): '''Listen for events.''' self._server = BaseHTTPServer.HTTPServer( ('localhost', 0), functools.partial( LoginServerHandler, self._handle_login ) ) webbrowser.open_new_tab( '{0}/user/api_credentials?redirect_url=http://localhost:{1}'.format( self.url, self._server.server_port ) ) self._server.handle_request()
class EntityPath(QtWidgets.QLineEdit): '''Entity path widget.''' path_ready = QtCore.Signal(object) def __init__(self, *args, **kwargs): '''Instantiate the entity path widget.''' super(EntityPath, self).__init__(*args, **kwargs) self.setReadOnly(True) self.path_ready.connect(self.on_path_ready) @ftrack_connect.asynchronous.asynchronous def setEntity(self, entity): '''Set the *entity* for this widget.''' names = [] entities = [entity] try: entities.extend(entity.getParents()) except AttributeError: pass for entity in entities: if entity: if isinstance(entity, ftrack.Show): names.append(entity.getFullName()) else: names.append(entity.getName()) # Reverse names since project should be first. names.reverse() self.path_ready.emit(names) def on_path_ready(self, names): '''Set current path to *names*.''' self.setText(' / '.join(names))
class EntityPath(QtWidgets.QLabel): '''Entity path widget.''' path_ready = QtCore.Signal(object) def __init__(self, *args, **kwargs): '''Instantiate the entity path widget.''' super(EntityPath, self).__init__(*args, **kwargs) self.path_ready.connect(self.on_path_ready) @util.asynchronous def setEntity(self, entity): '''Set the *entity* for this widget.''' names = [] session = entity.session parents = _get_entity_parents(entity) for entity in parents: if entity: if isinstance(entity, session.types['Project']): names.append(entity['full_name']) else: names.append(entity['name']) self.path_ready.emit(names) def on_path_ready(self, names): result = ' / '.join(names) result = 'Publish to: <b>{0}</b>'.format(result) self.setText(result)
def _createTreeItem(self, item): t = super(AdvancedHieroItemSpreadsheet, self)._createTreeItem(item) if t: disabled = self._itemIsDisabled(item) icon = self._enabledIcon status = self._enabledText if disabled: icon = self._disabledIcon status = self._disabledText if self._textCallback: status = self._textCallback(item) if self._iconCallback: icon = self._iconCallback(item) if self._iconIndex > -1 and icon: t.setIcon(self._iconIndex, icon) if self._statusIndex > -1: t.setText(self._statusIndex, status) if self._disableItems: t.setDisabled(disabled) t.setSizeHint(self._statusIndex, QtCore.QSize(100, 22)) return t
class ClickableLabel(QtWidgets.QLabel): '''Clickable label class.''' clicked = QtCore.Signal() def mousePressEvent(self, event): '''Override mouse press to emit signal.''' self.clicked.emit()
def __init__(self, specification, context, embedBrowser=False, session=None, parent=None, embedDetails=True): super(ItemCreateDialog, self).__init__(parent=parent) l = FnAssetAPI.l self._specification = specification self._context = context if not session: session = FnAssetAPI.SessionManager.currentSession() self._session = session layout = QtGui.QVBoxLayout() self.setLayout(layout) self.itemCreateWidget = ItemCreateWidget(specification, context, embedBrowser=embedBrowser, embedDetails=embedDetails, session=session) layout.addWidget(self.itemCreateWidget) self._managerOptions = None self._drawOptions(layout) buttons = QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel self._buttons = QtGui.QDialogButtonBox(buttons) if not embedDetails: detailsButton = self._buttons.addButton( "Details...", QtGui.QDialogButtonBox.HelpRole) detailsButton.clicked.connect(self.showDetails) self._buttons.button(QtGui.QDialogButtonBox.Ok).setText(l('{publish}')) self.connect(self._buttons, QtCore.SIGNAL('accepted()'), self.accept) self.connect(self._buttons, QtCore.SIGNAL('rejected()'), self.reject) layout.addWidget(self._buttons)
def sizeHint(self, option, index): '''Return preferred size hint.''' options = QtWidgets.QStyleOptionViewItem(option) self.initStyleOption(options, index) data = index.data(role=QtCore.Qt.UserRole) document = self.getTextDocument(option, data) return QtCore.QSize(document.idealWidth(), document.size().height())
class Chat(QtWidgets.QFrame): '''Chat widget.''' chatMessageSubmitted = QtCore.Signal(object) def __init__(self, parent=None): '''Initiate chat widget with *chatHub*.''' super(Chat, self).__init__(parent) self.setLayout(QtWidgets.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) self.setObjectName('chat-widget') self._chatFeed = Feed(parent) self.layout().addWidget(self._chatFeed, stretch=1) self._messageArea = ChatTextEdit(self) self._messageArea.setMinimumHeight(30) self._messageArea.setMaximumHeight(75) self._messageArea.returnPressed.connect(self.onReturnPressed) self.layout().addWidget(self._messageArea, stretch=0) self._sendMessageButton = QtWidgets.QPushButton('Submit') self.layout().addWidget(self._sendMessageButton, stretch=0) self._sendMessageButton.clicked.connect(self.onReturnPressed) self.busyOverlay = ftrack_connect.ui.widget.overlay.BusyOverlay( self, message='Loading') self.hideOverlay() def load(self, history): '''Load chat *history*''' self._chatFeed.clearItems() for message in history: self.addMessage(message) def onReturnPressed(self): '''Handle return pressed events.''' text = self._messageArea.toPlainText() if text: self.chatMessageSubmitted.emit(text) self._messageArea.setText('') def addMessage(self, message): '''Add *message* to feed.''' self._chatFeed.addMessage(message) def showOverlay(self): '''Show chat overlay.''' self.busyOverlay.show() def hideOverlay(self): '''Show chat overlay.''' self.busyOverlay.hide()
class BaseField(QtWidgets.QWidget): '''Base widget to inherit from.''' #: Signal to emit on value change. value_changed = QtCore.Signal(object) @abc.abstractmethod def value(): '''Return value.'''
def index(self, row, column, parent=None): '''Return index for *row* and *column* under *parent*.''' if parent is None: parent = QtCore.QModelIndex() if not self.hasIndex(row, column, parent): return QtCore.QModelIndex() if not parent.isValid(): item = self.root else: item = parent.internalPointer() try: child = item.children[row] except IndexError: return QtCore.QModelIndex() else: return self.createIndex(row, column, child)
class PanelCommunicator(QtCore.QObject): '''Communcator widget used to broadcast events between plugin dialogs.''' publishProgressSignal = QtCore.Signal(int, name='publishProgressSignal') def __init__(self): '''Initialise panel.''' super(PanelCommunicator, self).__init__() self.refListeners = [] self.swiListeners = [] self.infListeners = [] def refreshListeners(self): '''Call all refresh listeners.''' for listener in self.refListeners: listener() def switchedShotListeners(self): '''Call all shot listeners.''' for listener in self.swiListeners: listener() def infoListeners(self, taskId): '''Call all info listeners with *taskId*.''' for listener in self.infListeners: listener(taskId) def addRefreshListener(self, listener): '''Add refresh *listener*.''' self.refListeners.append(listener) def addSwitchedShotListener(self, listener): '''Add switch shot *listener*.''' self.swiListeners.append(listener) def addInfoListener(self, listener): '''Add info *listener*.''' self.infListeners.append(listener) def emitPublishProgress(self, publishInt): '''Emit publish progress with *publishInt*.''' self.publishProgressSignal.emit(publishInt) def setTotalExportSteps(self, steps): '''Set total export *steps*.''' self.exportSteps = float(steps) self.stepsDone = float(0) def emitPublishProgressStep(self): '''Emit publish progress step.''' self.stepsDone += 1.0 progress = (self.stepsDone / self.exportSteps) * 100.0 self.publishProgressSignal.emit(int(progress))
def __init__(self, parent=None): super(ItemDetailsDialog, self).__init__(parent=parent) self.setWindowTitle("Item Details") self.__items = [] layout = QtGui.QVBoxLayout() self.setLayout(layout) self.itemSpreadsheet = ItemSpreadsheetWidget() self.itemSpreadsheet.setMaxColumnWidth(800) layout.addWidget(self.itemSpreadsheet) self._buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok) layout.addWidget(self._buttons) self.connect(self._buttons, QtCore.SIGNAL('accepted()'), self.accept) self.connect(self._buttons, QtCore.SIGNAL('rejected()'), self.reject)
def paintEvent(self, event): '''Override paint event to make round thumbnails.''' painter = QtGui.QPainter(self) painter.setRenderHints(QtGui.QPainter.Antialiasing, True) brush = QtGui.QBrush(self.pixmap()) painter.setBrush(brush) painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0, 0))) painter.drawEllipse(QtCore.QRectF(0, 0, self.width(), self.height()))
def resizeEvent(self, event): '''Handle resize *event*. Position action buttons. ''' contentRegion = self.rect() widgetGeometry = QtCore.QRect( QtCore.QPoint( contentRegion.width() - self._iconRegion.width(), (contentRegion.height() - self._iconRegion.height()) / 2 ), self._iconSize ) for button in self._actionButtons: button.setGeometry(widgetGeometry) widgetGeometry.moveLeft( widgetGeometry.left() - self._iconRegion.width() )
def loadResource(self, resource): '''Update current pixmap using *resource*.''' svg_renderer = QtSvg.QSvgRenderer(resource) image = QtGui.QImage(self.size, self.size, QtGui.QImage.Format_ARGB32) # Set the ARGB to 0 to prevent rendering artifacts. image.fill(0x00000000) svg_renderer.render(QtGui.QPainter(image), QtCore.QRectF(0, 0, self.size, self.size)) pixmap = QtGui.QPixmap.fromImage(image) self._fillColor(pixmap) self._scaleAndSetPixmap(pixmap)
def __init__(self, *args, **kwargs): '''Instantiate the entity selector widget.''' super(EntitySelector, self).__init__(*args, **kwargs) self._entity = None # Create widget used to select an entity. selectionWidget = QtWidgets.QFrame() selectionWidget.setLayout(QtWidgets.QHBoxLayout()) selectionWidget.layout().setContentsMargins(0, 0, 0, 0) self.insertWidget(0, selectionWidget) self.entityBrowser = _entity_browser.EntityBrowser(parent=self) self.entityBrowser.setMinimumSize(600, 400) self.entityBrowser.selectionChanged.connect( self._onEntityBrowserSelectionChanged) self.entityBrowseButton = QtWidgets.QPushButton('Browse') # TODO: Once the link is available through the API change this to a # combo with assigned tasks. self.assignedContextSelector = QtWidgets.QLineEdit() self.assignedContextSelector.setReadOnly(True) selectionWidget.layout().addWidget(self.assignedContextSelector) selectionWidget.layout().addWidget(self.entityBrowseButton) # Create widget used to present current selection. presentationWidget = QtWidgets.QFrame() presentationWidget.setLayout(QtWidgets.QHBoxLayout()) presentationWidget.layout().setContentsMargins(0, 0, 0, 0) self.insertWidget(1, presentationWidget) self.entityPath = _entity_path.EntityPath() presentationWidget.layout().addWidget(self.entityPath) self.discardEntityButton = QtWidgets.QPushButton() removeIcon = QtGui.QIcon(QtGui.QPixmap(':/ftrack/image/light/remove')) self.discardEntityButton.setIconSize(QtCore.QSize(20, 20)) self.discardEntityButton.setIcon(removeIcon) self.discardEntityButton.setFixedWidth(20) self.discardEntityButton.clicked.connect( self._onDiscardEntityButtonClicked) presentationWidget.layout().addWidget(self.discardEntityButton) self.entityChanged.connect(self.entityPath.setEntity) self.entityChanged.connect(self._updateIndex) self.entityBrowseButton.clicked.connect( self._onEntityBrowseButtonClicked)
def __init__(self, parent, browseMode='Shot'): QtWidgets.QWidget.__init__(self, parent) self.ui = Ui_ExportAssetOptions() self.ui.setupUi(self) self.currentAssetType = None self.currentTask = None self.browseMode = browseMode self.ui.ListAssetsViewModel = QtGui.QStandardItemModel() self.ui.ListAssetsSortModel = QtCore.QSortFilterProxyModel() self.ui.ListAssetsSortModel.setDynamicSortFilter(True) self.ui.ListAssetsSortModel.setFilterKeyColumn(1) self.ui.ListAssetsSortModel.setSourceModel(self.ui.ListAssetsViewModel) self.ui.ListAssetNamesComboBox.setModel(self.ui.ListAssetsSortModel) self.ui.ListAssetsComboBoxModel = QtGui.QStandardItemModel() assetTypeItem = QtGui.QStandardItem('Select AssetType') self.assetTypes = [] self.assetTypes.append('') self.ui.ListAssetsComboBoxModel.appendRow(assetTypeItem) assetHandler = FTAssetHandlerInstance.instance() self.assetTypesStr = sorted(assetHandler.getAssetTypes()) for assetTypeStr in self.assetTypesStr: try: assetType = ftrack.AssetType(assetTypeStr) except: log.warning('{0} not available in ftrack'.format(assetTypeStr)) continue assetTypeItem = QtGui.QStandardItem(assetType.getName()) assetTypeItem.type = assetType.getShort() self.assetTypes.append(assetTypeItem.type) self.ui.ListAssetsComboBoxModel.appendRow(assetTypeItem) self.ui.ListAssetsComboBox.setModel(self.ui.ListAssetsComboBoxModel) self.ui.AssetTaskComboBoxModel = QtGui.QStandardItemModel() self.ui.AssetTaskComboBox.setModel(self.ui.AssetTaskComboBoxModel) self.ui.ListAssetNamesComboBox.currentIndexChanged[str].connect( self.onAssetChanged) if browseMode == 'Task': self.ui.AssetTaskComboBox.hide() self.ui.assetTaskLabel.hide()
def paintEvent(self, event): '''Handle paint *event*.''' painter = QtWidgets.QPainter(self) # Note: isDown should ideally use the 'active' state, but in most styles # this has no proper feedback. state = QtGui.QIcon.Disabled if self.isEnabled(): state = QtGui.QIcon.Normal if self.isDown(): state = QtGui.QIcon.Selected iconPixmap = self.icon().pixmap( QtCore.QSize(self.iconSize, self.iconSize), state, QtGui.QIcon.Off ) iconRegion = QtCore.QRect( 0, 0, iconPixmap.width(), iconPixmap.height() ) iconRegion.moveCenter(self.rect().center()) painter.drawPixmap(iconRegion, iconPixmap)
def setLocationFromIndex(self, index): '''Set location to *index*.''' if index is None: index = QtCore.QModelIndex() currentIndex = self.view.rootIndex() if index == currentIndex: return self.view.setRootIndex(index) self._updateNavigationBar() selectionModel = self.view.selectionModel() selectionModel.clearSelection() self.locationChanged.emit()