Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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)
Exemplo n.º 5
0
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()
Exemplo n.º 7
0
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()
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
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:
Exemplo n.º 11
0
class Worker(QtCore.QThread):
    trigger = QtCore.Signal()

    def __init__(self):
        super(Worker, self).__init__()

    def run(self):
        self.trigger.emit()
        logger.info('Emit done!!!')
Exemplo n.º 12
0
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()
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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
Exemplo n.º 17
0
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)
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
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)
Exemplo n.º 21
0
            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)
Exemplo n.º 22
0
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)
Exemplo n.º 23
0
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
Exemplo n.º 24
0
    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
Exemplo n.º 25
0
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
Exemplo n.º 26
0
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)
Exemplo n.º 27
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)
Exemplo n.º 28
0
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"])
Exemplo n.º 29
0
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:
Exemplo n.º 30
0
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