class _Worker(QtCore.QObject): """ Thread worker that just does work when requested. """ # Signal emitted when a task has completed successfully task_completed = QtCore.Signal(object, object)# task, result # Signal emitted when a task has failed task_failed = QtCore.Signal(object, object, object)# task, message, stacktrace def __init__(self): """ Construction """ QtCore.QObject.__init__(self, None) def do_task(self, task): """ Run a single task. """ try: # run the task: result = task.run() # emit result: self.task_completed.emit(task, result) except Exception, e: # something went wrong so emit failed signal: tb = traceback.format_exc() self.task_failed.emit(task, str(e), tb)
class MessageEmitter(QtCore.QObject): """ Container QObject for Qt signals fired when messages requesting certain actions take place in Python arrive from the remote process. :signal logging_received(str, str): Fires when a logging call has been received. The first string is the logging level (debug, info, warning, or error) and the second string is the message. :signal command_received(int): Fires when an engine command has been received. The integer value is the unique id of the engine command that was requested to be executed. :signal run_tests_request_received: Fires when a request for unit tests to be run has been received. :signal state_requested: Fires when the remote process requests the current state. :signal active_document_changed(str): Fires when alerted to a change in active document by the RPC server. The string value is the path to the new active document, or an empty string if the active document is unsaved. """ logging_received = QtCore.Signal(str, str) command_received = QtCore.Signal(int) run_tests_request_received = QtCore.Signal() state_requested = QtCore.Signal() active_document_changed = QtCore.Signal(str)
class SiteCommunication(QtCore.QObject, CommunicationBase): """ Communication channel for the site engine to the project engine. """ proxy_closing = QtCore.Signal() proxy_created = QtCore.Signal() def __init__(self, engine): """ :param engine: Toolkit engine. """ QtCore.QObject.__init__(self) CommunicationBase.__init__(self, engine) def _create_proxy(self, pipe, authkey): """ Connects to the other process's RPC server. """ CommunicationBase._create_proxy(self, pipe, authkey) self.proxy_created.emit() def start_server(self): """ Sets up a server to communicate with the background process. """ self._create_server() self.register_function(self._create_proxy, "create_app_proxy") self.register_function(self._destroy_proxy, "destroy_app_proxy") self.register_function(self._proxy_log, "proxy_log") def _notify_proxy_closure(self): """ Disconnect from the app proxy. """ self.proxy_closing.emit() try: self.call_no_response("signal_disconnect") except Exception as e: logger.warning("Error while sending signal to proxy to disconnect: %s", e) else: logger.debug("Proxy was signaled that we are disconnecting.") def _proxy_log(self, level, msg, args): """ Outputs messages from the proxy into the application's logs. :param level: Level of the message. :param msg: Message to log. :param args: Arguments to log. """ try: logger.log(level, "[PROXY] %s" % msg, *args) except Exception: logger.exception("Unexpected error when logging proxy message:") raise
class GlobalSearchWidget(ShotgunSearchWidget): """ A QT Widget deriving from :class:`~PySide.QtGui.QLineEdit` that creates a global search input box with auto completion. :signal: ``entity_selected(str, int)`` - Fires when someone selects an entity inside the search results. The returned parameters are entity type and entity id. :signal: ``entity_activated(str, int, str)`` - Fires when someone selects an entity inside the search results. Similar to ``entity_selected``, with the addition of the ``name`` of the activated entity being supplied. """ # emitted when shotgun has been updated entity_selected = QtCore.Signal(str, int) entity_activated = QtCore.Signal(str, int, str) def __init__(self, parent): """ Uses the :class:`~search_completer.GlobalSearchCompleter` as the completer for searching SG entities. :param parent: Qt parent object :type parent: :class:`~PySide.QtGui.QWidget` """ # first, call the base class and let it do its thing. super(GlobalSearchWidget, self).__init__(parent) # configure our popup completer self.setCompleter(search_completer.GlobalSearchCompleter(self)) # forward the completer's activated/selected signals self.completer().entity_selected.connect(self.entity_selected.emit) self.completer().entity_activated.connect(self.entity_activated.emit) # When the node is activated it queues an event to put the selection into the line edit. # Queueing the event like this ensures we clean up the line edit after. # Taken from: # http://stackoverflow.com/questions/11865129/fail-to-clear-qlineedit-after-selecting-item-from-qcompleter # Only need to listen to one of the two events as both are always emitted by the completer. self.completer().entity_activated.connect(self.clear, QtCore.Qt.QueuedConnection) def set_searchable_entity_types(self, types_dict): """ Specify a dictionary of entity types with optional search filters to limit the breadth of the widget's search. See the documentation for `GlobalSearchCompleter.set_searchable_entity_types` for the default values if this method is not called on the widget. :param types_dict: A dictionary of searchable types with optional filters """ self.completer().set_searchable_entity_types(types_dict)
class RunSetupThread(QtCore.QThread): """ Simple thread to run the wizard in the background """ success = QtCore.Signal() failure = QtCore.Signal(str) def __init__(self, wizard, parent=None): QtCore.QThread.__init__(self, parent) self._wizard = wizard def run(self): try: self._wizard.execute() self.success.emit() except Exception, e: self.failure.emit(str(e))
class StdinRedirector(QtCore.QObject): """Handles redirecting stdin. Sends an input signal when stdin is read from. """ input_requested = QtCore.Signal(str) def __init__(self, readline_callback, parent=None): """Initialize the redirection object. :param parent: The parent qt object. """ super(StdinRedirector, self).__init__(parent) self._handle = None self._readline_callback = readline_callback def __enter__(self): """Begin redirection. Temporarily assigns stdin to this object for writing. """ self._handle = sys.stdin sys.stdin = self return self def __exit__(self, type, value, traceback): """Finish redirection. Repoint sys.stdin to the original handle. """ sys.stdin = self._handle def readline(self): return self._readline_callback()
class SetupProject(QtGui.QWidget): setup_finished = QtCore.Signal(bool) def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = setup_project.Ui_SetupProject() self.ui.setupUi(self) self.ui.button.clicked.connect(self.do_setup) self._parent = parent self.project = None filter = ResizeEventFilter(self._parent) filter.resized.connect(self._on_parent_resized) self._parent.installEventFilter(filter) self.setVisible(False) def do_setup(self): setup = adminui.SetupProjectWizard(self.project, self) ret = setup.exec_() self.setup_finished.emit(ret == setup.Accepted) def _on_parent_resized(self): """ Special slot hooked up to the event filter. When associated widget is resized this slot is being called. """ # resize overlay self.resize(self._parent.size())
class GroupWidgetBase(QtGui.QWidget): """ Base interface for a group widget that will be used in the :class:`GroupedListView` custom view :signal toggle_expanded(bool): Emitted when the group's expansion state is toggled. Includes a boolean to indicate if the group is expanded or not. """ # True if expanded, False if collapsed toggle_expanded = QtCore.Signal(bool) def set_item(self, model_idx): """ Set the item this widget should be associated with. This should be implemented in derived classes :param model_idx: The index of the item in the model :type model_index: :class:`~PySide.QtCore.QModelIndex` """ raise NotImplementedError() def set_expanded(self, expand=True): """ Set if this widget is expanded or not. This should be implemented in derived classes :param expand: True if the widget should be expanded, False if it should be collapsed. :type expand: bool """ raise NotImplementedError()
class MyTasksForm(EntityTreeForm): """ My Tasks widget class """ # emitted when an entity is double clicked task_double_clicked = QtCore.Signal(object) def __init__(self, tasks_model, allow_task_creation, parent): """ Construction :param model: The Shotgun Model this widget should connect to :param parent: The parent QWidget for this control """ EntityTreeForm.__init__( self, tasks_model, "My Tasks", allow_task_creation, tasks_model.extra_display_fields, parent, ) # There is no need for the my tasks toggle. self._ui.my_tasks_cb.hide() # Sets an item delete to show a list of tiles for tasks instead of nodes in a tree. self._item_delegate = None # create the item delegate - make sure we keep a reference to the delegate otherwise # things may crash later on! self._item_delegate = MyTaskItemDelegate( tasks_model.extra_display_fields, self._ui.entity_tree) monitor_qobject_lifetime(self._item_delegate) self._ui.entity_tree.setItemDelegate(self._item_delegate) self._ui.entity_tree.doubleClicked.connect(self._on_double_clicked) def shut_down(self): """ Clean up as much as we can to help the gc once the widget is finished with. """ signals_blocked = self.blockSignals(True) try: EntityTreeForm.shut_down(self) # detach and clean up the item delegate: self._ui.entity_tree.setItemDelegate(None) if self._item_delegate: self._item_delegate.setParent(None) self._item_delegate.deleteLater() self._item_delegate = None finally: self.blockSignals(signals_blocked) def _on_double_clicked(self, idx): """ Emits the entity that was double clicked. """ entity_details = self._get_entity_details(idx) self.task_double_clicked.emit(entity_details)
class SiteCommunication(QtCore.QObject, CommunicationBase): """ Communication channel for the site engine to the project engine. """ proxy_closing = QtCore.Signal() def __init__(self, engine): """ :param engine: Toolkit engine. """ QtCore.QObject.__init__(self) CommunicationBase.__init__(self, engine) def start_server(self): """ Sets up a server to communicate with the background process. """ self._create_server() self.register_function(self._create_proxy, "create_app_proxy") self.register_function(self._destroy_proxy, "destroy_app_proxy") self.register_function(self._proxy_log, "proxy_log") def _notify_proxy_closure(self): """ Disconnect from the app proxy. """ self.proxy_closing.emit() try: self.call_no_response("signal_disconnect") except Exception, e: logger.warning( "Error while sending signal to proxy to disconnect: %s", e) else:
class Worker(QtCore.QThread): trigger = QtCore.Signal() def __init__(self): super(Worker, self).__init__() def run(self): self.trigger.emit() logger.info('Emit done!!!')
class ClickableLabel(QtGui.QLabel): """ A clickable QLabel """ clicked = QtCore.Signal() def __init__(self, parent=None): super(ClickableLabel, self).__init__(parent=parent) def mouseReleaseEvent(self, event): self.clicked.emit()
class TaskAssigneeModel(ShotgunModel): """ Model holds data about a list of task assignees """ # signals thumbnail_updated = QtCore.Signal(dict, QtGui.QImage) def __init__(self, parent): """ Constructor :param parent: QT parent object """ # init base class ShotgunModel.__init__(self, parent, bg_load_thumbs=True) self._app = sgtk.platform.current_bundle() self._task_model = parent # connect it with the request_user_thumbnails signal on the # task model, so that whenever that model requests thumbnails, this # model starts loading that data. self._task_model.request_user_thumbnails.connect( self._load_user_thumbnails) def _load_user_thumbnails(self, user_ids): """ Load thumbnails for all user ids """ fields = ["image"] self._load_data("HumanUser", [["id", "in", user_ids]], ["id"], fields) self._refresh_data() def _populate_thumbnail_image(self, item, field, image, path): """ Called whenever a thumbnail for an item has arrived on disk. In the case of an already cached thumbnail, this may be called very soon after data has been loaded, in cases when the thumbs are downloaded from Shotgun, it may happen later. This method will be called only if the model has been instantiated with the download_thumbs flag set to be true. It will be called for items which are associated with shotgun entities (in a tree data layout, this is typically leaf nodes). This method makes it possible to control how the thumbnail is applied and associated with the item. The default implementation will simply set the thumbnail to be icon of the item, but this can be altered by subclassing this method. Any thumbnails requested via the _request_thumbnail_download() method will also resurface via this callback method. :param item: QStandardItem which is associated with the given thumbnail :param field: The Shotgun field which the thumbnail is associated with. :param path: A path on disk to the thumbnail. This is a file in jpeg format. """ self._current_pixmap = utils.create_round_thumbnail(image) sg_data = item.get_sg_data() self.thumbnail_updated.emit(sg_data, image)
class CloseEventFilter(QtCore.QObject): """ Event filter which emits a parent_closed signal whenever the monitored widget closes. """ parent_closed = QtCore.Signal(str) parent_dirty = QtCore.Signal(str) def set_associated_widget(self, widget_id): """ Set the widget that should be closed :param widget_id: Object name of widget to close """ self._widget_id = widget_id def eventFilter(self, obj, event): """ QT Event filter callback :param obj: The object where the event originated from :param event: The actual event object :returns: True if event was consumed, False if not """ # peek at the message if event.type() == QtCore.QEvent.Close: # make sure the associated widget is still a descendant of the object parent = _find_widget(self._widget_id) while parent: if parent == obj: # re-broadcast the close event self.parent_closed.emit(self._widget_id) break parent = parent.parent() if event.type() == QtCore.QEvent.LayoutRequest: # this event seems to be fairly representatative # (without too many false positives) of when a tab # needs to trigger a UI redraw of content self.parent_dirty.emit(self._widget_id) # pass it on! return False
class OpenPublishActionManager(ActionManager): """ Specialisation of the base ActionManager class that limits the actions that the loader can perform to just opening a publish. This also provides a mechanism for the default action (e.g. when double clicking on a publish) to signal the calling code. """ # signal that is emitted when the default action is triggered default_action_triggered = QtCore.Signal(object) def __init__(self, publish_types): """ Construction :param publish_types: The list of publish types that can be opened. This list is used to filter the list of publishes presented to the user. """ ActionManager.__init__(self) self.__publish_types = publish_types def has_actions(self, publish_type): """ Returns true if the given publish type has any actions associated with it. For the open dialog, this returns true if the file can be opened (is one of the valid publish types the action manager was initialised with). :param publish_type: A Shotgun publish type (e.g. 'Maya Render') :returns: True if the current actions setup knows how to handle this. """ return not self.__publish_types or publish_type in self.__publish_types def get_default_action_for_publish(self, sg_data, ui_area): """ Get the default action for the specified publish data. For the open dialog, the default action is to open the publish the action is triggered for. :param sg_data: Shotgun data for a publish :param ui_area: Indicates which part of the UI the request is coming from. Currently one of UI_AREA_MAIN, UI_AREA_DETAILS and UI_AREA_HISTORY :returns: The QAction object representing the default action for this publish """ # create the default action: action = QtGui.QAction(None, None) # connect the default action so that the default_action_triggered # is emitted: default_action_cb = lambda sg=sg_data: self.default_action_triggered.emit( sg) action.triggered[()].connect(default_action_cb) return action
class SgLatestPublishProxyModel(QtGui.QSortFilterProxyModel): """ Filter model to be used in conjunction with SgLatestPublishModel """ # signal which is emitted whenever a filter changes filter_changed = QtCore.Signal() def __init__(self, parent): QtGui.QSortFilterProxyModel.__init__(self, parent) self._valid_type_ids = None self._show_folders = True def set_filter_by_type_ids(self, type_ids, show_folders): """ Specify which type ids the publish model should allow through """ self._valid_type_ids = type_ids self._show_folders = show_folders # tell model to repush data self.invalidateFilter() self.filter_changed.emit() def filterAcceptsRow(self, source_row, source_parent_idx): """ Overridden from base class. This will check each row as it is passing through the proxy model and see if we should let it pass or not. """ if self._valid_type_ids is None: # accept all! return True model = self.sourceModel() current_item = model.invisibleRootItem().child( source_row) # assume non-tree structure is_folder = current_item.data(SgLatestPublishModel.IS_FOLDER_ROLE) if is_folder: return self._show_folders else: # get the type id sg_type_id = current_item.data(SgLatestPublishModel.TYPE_ID_ROLE) if sg_type_id is None: # no type. So always show. return True elif sg_type_id in self._valid_type_ids: return True else: return False
class BannerWidget(QtGui.QWidget): """ Shows a notification in the banner. :signals: dismissed() Invoked when the banner is dismissed by the user. """ dismissed = QtCore.Signal(object) def __init__(self, notifications_mgr, notification, parent=None): """ :param notifications_mgr: ``NotificationsManager`` instance. :param notification: ``Notification`` instance to display. :param parent: Parent widget """ super(BannerWidget, self).__init__(parent) self.ui = Ui_BannerWidget() self.ui.setupUi(self) # If we want the background color to apply to the entire surface of the widget # in PySide instead of just under its children we need to set this flag. self.setAttribute(QtCore.Qt.WA_StyledBackground, True) self._current_message_id = None self.ui.message.setText(notification.message) self._notifications_mgr = notifications_mgr self._notification = notification self.ui.close_button.clicked.connect(self._on_dismiss_message) self.ui.message.linkActivated.connect(self._on_link_clicked) def _on_link_clicked(self, url): """ Opens the URL when clicked, launches it in the browser and dismisses the message. """ if url: QtGui.QDesktopServices.openUrl(url) self._on_dismiss_message() @property def unique_id(self): """ Returns the unique identifier of a notification. """ return self._notification.unique_id def _on_dismiss_message(self): """ Dismisses the message and hides the banner. """ self._notifications_mgr.dismiss(self._notification) self.dismissed.emit(self)
class SettingsWidgetController(QtGui.QWidget): """ Controller that creates the widgets for each setting. """ # Signal for when a setting value has changed setting_changed = QtCore.Signal() def __init__(self, parent, hook, tasks): QtGui.QWidget.__init__(self, parent) plugin = hook.plugin self._layout = QtGui.QFormLayout(self) # since tasks will be of same type it's safe to assume they will all # share the same list of settings task_settings = tasks[0].settings if tasks else {} # TODO: these should probably be exceptions for setting in plugin.settings["Settings To Display"]: kwargs = setting.value setting_name = kwargs.pop("name", None) if not setting_name: plugin.logger.error( "Entry in 'Settings To Display' is missing its 'name' attribute" ) continue setting = task_settings.get(setting_name) if not setting: plugin.logger.error( "Unknown setting: {}".format(setting_name)) continue widget_type = kwargs.pop("type", None) if not widget_type: plugin.logger.error( "No defined widget type for setting: {}".format( setting_name)) continue if not hasattr(hook, widget_type): plugin.logger.error( "Cannot find widget class: {}".format(widget_type)) continue # Instantiate the widget class widget_class = getattr(hook, widget_type) display_name = kwargs.get("display_name", setting_name) setting_widget = widget_class(self, hook, tasks, setting_name, **kwargs) setting_widget.setObjectName(setting_name) # Add a row entry self._layout.addRow(display_name, setting_widget)
class SetupProject(QtGui.QWidget): setup_finished = QtCore.Signal(bool) def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = setup_project.Ui_SetupProject() self.ui.setupUi(self) self.ui.button.clicked.connect(self.do_setup) self._parent = parent self.project = None filter = ResizeEventFilter(self._parent) filter.resized.connect(self._on_parent_resized) self._parent.installEventFilter(filter) self.setVisible(False) def do_setup(self, show_help=False): # Only toggle top window if it used to be off (to avoid un-necessary window flicker in case it was already off) is_on_top = self._is_on_top() try: if is_on_top: self._set_top_window_on_top(False) # First check to see if the current user self._validate_user_permissions() if show_help: # Display the Help popup if requested. self.show_help_popup() setup = adminui.SetupProjectWizard(self.project, self) ret = setup.exec_() self.setup_finished.emit(ret == setup.Accepted) except TankErrorProjectIsSetup, e: error_dialog = ErrorDialog( "Toolkit Setup Error", "You are trying to set up a project which has already been set up\n\n" "To re-setup a project, in a terminal window type: tank setup_project --force\n\n" "Alternatively, you can go into shotgun and clear the Project.tank_name field\n" "and delete all pipeline configurations for your project.") error_dialog.exec_() except TankUserPermissionsError, e: error_dialog = ErrorDialog( "Toolkit Setup Error", "You do not have sufficient permissions in Shotgun to setup Toolkit for " "project '%s'.\n\nContact a site administrator for assistance." % self.project["name"]) error_dialog.exec_()
class StdoutRedirector(QtCore.QObject): """Handles redirecting stdout. Sends an output signal when stdout is written to. """ output = QtCore.Signal(str) def __init__(self, tee=True, parent=None): """Initialize the redirection object. :param tee: Also write to sys stdout when True. :param parent: The parent qt object. """ super(StdoutRedirector, self).__init__(parent) self._handle = None self._tee = tee def __enter__(self): """Begin redirection. Temporarily assigns stdout to this object for writing. """ self._handle = sys.stdout sys.stdout = self return self def __exit__(self, type, value, traceback): """Finish redirection. Repoint sys.stdout to the original handle. """ sys.stdout = self._handle def flush(self): """Nothing to emit for the redirector. Flush the original if tee'ing.""" if self._tee: self._handle.flush() def write(self, msg): """Forward the written output to the output signal. If tee, then also write to stdout. """ if isinstance(msg, unicode): msg = unicode.encode(msg, "utf-8") self.output.emit(msg) QtCore.QCoreApplication.processEvents() if self._tee and self._handle: self._handle.write(msg)
class DockWidget(QtGui.QDockWidget): """ Widget used for docking app panels that ensures the widget is closed when the dock is closed """ closed = QtCore.Signal(QtCore.QObject) def closeEvent(self, event): widget = self.widget() if widget: widget.close() self.setParent(None) self.closed.emit(self)
class NotificationThread(QtCore.QThread): """ Main thread loop querying Shotgun to get new data to notify""" notification_message = QtCore.Signal(unicode) notification_url = QtCore.Signal(unicode) def __init__(self, parent): super(NotificationThread, self).__init__(parent) self.parent = parent def start(self): log('Starting thread...') super(NotificationThread, self).start() self.setPriority(QtCore.QThread.LowPriority) def run(self): # Run the event filter self.parent._event_filter.run() # loop all filters results notifications = [] for _filter in self.parent._event_filter.filters(): for notification in _filter.get_notifications(): notifications.append(notification) # Return if we got nothing if not notifications: return # Show the message or the number of notification since the last update if len(notifications) == 1: msg = notifications[0].get_message() url = notifications[0].get_url() else: msg = '%d new activity in task %s' % ( len(notifications), self.parent.context.task['name']) url = '' # Emit the url first because the message emit will show the notification widget self.notification_url.emit(url) self.notification_message.emit(msg)
class ResizeEventFilter(QtCore.QObject): """ Event filter which emits a resized signal whenever the monitored widget resizes. This is so that the overlay wrapper class can be informed whenever the Widget gets a resize event. """ resized = QtCore.Signal() def eventFilter(self, obj, event): # peek at the message if event.type() == QtCore.QEvent.Resize: # re-broadcast any resize events self.resized.emit() # pass it on! return False
class PropertyWidgetBaseClass(PluginBase.ValueWidgetBaseClass): """ Base Class for creating any custom properties widgets. """ # Signal for when a property value has changed value_changed = QtCore.Signal() def __init__(self, parent, hook, items, name, **kwargs): self._items = items # Get the list of non None, sorted values values = [item.properties.get(name) for item in self._items] values = filter(lambda x: x is not None, values) values.sort(reverse=True) # Get the number of unique values value_type = kwargs.pop( "type", type(values[0]).__name__ if len(values) else "nothing") if value_type == "list": num_values = len(set([tuple(l) for l in values])) elif value_type == "dict": # there can be a list within the dictionary too and frozenset errors out with non-hashable TypeError # so we will assume two dicts are different if we encounter TypeError try: num_values = len( set([frozenset(d.items()) for d in values])) except TypeError: num_values = len(values) else: num_values = len(set(values)) if num_values > 1: value = self.MultiplesValue elif num_values < 1: value = None else: value = values.pop() super(CollectorPlugin.PropertyWidgetBaseClass, self).__init__(parent, hook, name, value, value_type, **kwargs) @property def items(self): return self._items
class ResizeEventFilter(QtCore.QObject): """ Utility and helper. Event filter which emits a resized signal whenever the monitored widget resizes. You use it like this: # create the filter object. Typically, it's # it's easiest to parent it to the object that is # being monitored (in this case self.ui.thumbnail) filter = ResizeEventFilter(self.ui.thumbnail) # now set up a signal/slot connection so that the # __on_thumb_resized slot gets called every time # the widget is resized filter.resized.connect(self.__on_thumb_resized) # finally, install the event filter into the QT # event system self.ui.thumbnail.installEventFilter(filter) """ resized = QtCore.Signal() def eventFilter(self, obj, event): """ Event filter implementation. For information, see the QT docs: http://doc.qt.io/qt-4.8/qobject.html#eventFilter This will emit the resized signal (in this class) whenever the linked up object is being resized. :param obj: The object that is being watched for events :param event: Event object that the object has emitted :returns: Always returns False to indicate that no events should ever be discarded by the filter. """ # peek at the message if event.type() == QtCore.QEvent.Resize: # re-broadcast any resize events self.resized.emit() # pass it on! return False
class BaseIconList(QtGui.QWidget): """ Base class for a list of icons inside a section. It provides the command_triggered signal and customizes the layout. """ command_triggered = QtCore.Signal(str) def __init__(self, parent, layout): """ :param parent: Parent widget. :param layout: Qt layout for this widget. """ super(BaseIconList, self).__init__(parent) self._layout = layout self.setLayout(layout) self._layout.setContentsMargins(0, 0, 0, 0)
class PropertiesWidgetController(QtGui.QWidget): """ Controller that creates the widgets for each item property. """ # Signal for when a property value has changed property_changed = QtCore.Signal() def __init__(self, parent, hook, items): QtGui.QWidget.__init__(self, parent) plugin = hook.plugin self._layout = QtGui.QFormLayout(self) for setting in plugin.settings["Properties To Display"]: kwargs = setting.value # TODO: this should probably be an exception name = kwargs.pop("name", None) if not name: plugin.logger.error( "Entry in 'Properties To Display' is missing its 'name' attribute" ) continue widget_type = kwargs.pop("type", None) if not widget_type: plugin.logger.error( "No defined widget type for setting: {}".format(name)) continue if not hasattr(hook, widget_type): plugin.logger.error( "Cannot find widget class: {}".format(widget_type)) continue # Instantiate the widget class widget_class = getattr(hook, widget_type) display_name = kwargs.get("display_name", name) property_widget = widget_class(self, hook, items, name, **kwargs) property_widget.setObjectName(name) # Add a row entry self._layout.addRow(display_name, property_widget)
class UserThumbnail(ClickableLabel): """ Subclassed QLabel to represent a shotgun user. """ # signal that fires on click entity_requested = QtCore.Signal(str, int) def __init__(self, parent): """ Constructor :param parent: QT parent object :type parent: :class:`PySide.QtGui.QWidget` """ ClickableLabel.__init__(self, parent) # make this user clickable self._sg_data = None def set_shotgun_data(self, sg_data): """ Set the shotgun data associated with this user :param sg_data: Shotgun user data """ self._sg_data = sg_data ### AMG begin if sg_data: user_name = sg_data.get("name") or "Unknown User" else: user_name = "Undefined User" ### AMG end self.setToolTip(user_name) def mousePressEvent(self, event): """ Fires when the mouse is pressed """ ClickableLabel.mousePressEvent(self, event) if self._sg_data: self.entity_requested.emit(self._sg_data["type"], self._sg_data["id"])
class SetupProject(QtGui.QWidget): setup_finished = QtCore.Signal(bool) def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = setup_project.Ui_SetupProject() self.ui.setupUi(self) self.ui.button.clicked.connect(self.do_setup) self._parent = parent self.project = None filter = ResizeEventFilter(self._parent) filter.resized.connect(self._on_parent_resized) self._parent.installEventFilter(filter) self.setVisible(False) def do_setup(self): # Only toggle top window if it used to be off (to avoid un-necessary window flicker in case it was already off) is_on_top = self._is_on_top() try: if is_on_top: self._set_top_window_on_top(False) setup = adminui.SetupProjectWizard(self.project, self) ret = setup.exec_() self.setup_finished.emit(ret == setup.Accepted) except TankErrorProjectIsSetup, e: error_dialog = ErrorDialog( "Toolkit Setup Error", "You are trying to set up a project which has already been set up\n\n" "To re-setup a project, in a terminal window type: tank setup_project --force\n\n" "Alternatively, you can go into shotgun and clear the Project.tank_name field\n" "and delete all pipeline configurations for your project.") ret = error_dialog.exec_() finally:
class CloseEventFilter(QtCore.QObject): """ Event filter which emits a parent_closed signal whenever the monitored widget closes. """ parent_closed = QtCore.Signal() def eventFilter(self, obj, event): """ QT Event filter callback :param obj: The object where the event originated from :param event: The actual event object :returns: True if event was consumed, False if not """ # peek at the message if event.type() == QtCore.QEvent.Close: # re-broadcast any close events self.parent_closed.emit() # pass it on! return False