Ejemplo n.º 1
0
        def button_row(channel=None):
            row = QtGui.QWidget()
            row.setLayout(QtGui.QHBoxLayout())
            row.layout().setContentsMargins(0, 0, 0, 0)
            row.layout().setSpacing(1)

            label = '"%s"' % channel if channel else 'All'
            button = QtGui.QPushButton(silk_icon('arrow_refresh', 12), 'Fuzz')
            button.setFixedSize(button.sizeHint().boundedTo(
                QtCore.QSize(1000, 20)))
            button.setToolTip('Fuzzy Match %s' % label)
            channels = [channel] if channel else None
            button.clicked.connect(
                lambda *args: self._on_fuzzy_match(channels=channels))
            row.layout().addWidget(button)

            button = QtGui.QPushButton(silk_icon('cross', 12), 'Unlink')
            button.setFixedSize(button.sizeHint().boundedTo(
                QtCore.QSize(1000, 20)))
            button.setToolTip('Unlink %s' % label)
            button.clicked.connect(
                lambda *args: self._on_unlink(channels=channels))
            row.layout().addWidget(button)

            return row
Ejemplo n.º 2
0
def silk_widget(name, size=16, tooltip=None):
    icon = QtGui.QIcon(silk(name))
    label = QtGui.QLabel()
    label.setPixmap(icon.pixmap(size, size))
    label.setFixedSize(QtCore.QSize(size, size))
    if tooltip:
        label.setToolTip(tooltip)
    return label
Ejemplo n.º 3
0
 def submit(self, func, *args, **kwargs):
     self._queue.put((func, args, kwargs))
     with self._worker_lock:
         if len(self._workers) < self._max_workers:
             worker = QtCore.QThread()
             worker.run = functools.partial(self._target, worker)
             worker.start()
             self._workers.append(worker)
Ejemplo n.º 4
0
    def _setup_ui(self):
        self.setWindowTitle('Geocache Import')
        self.setWindowFlags(Qt.Tool)
        self.setMinimumWidth(900)
        self.setMinimumHeight(550)

        main_layout = QtGui.QVBoxLayout()
        self.setLayout(main_layout)

        self._scroll_widget = area = QtGui.QScrollArea()
        main_layout.addWidget(area)
        area.setFrameShape(QtGui.QFrame.NoFrame)
        area.setWidgetResizable(True)

        area_widget = QtGui.QWidget()
        area.setWidget(area_widget)
        self._scroll_layout = QtGui.QVBoxLayout()
        area_widget.setLayout(self._scroll_layout)
        self._scroll_layout.addStretch()

        button_layout = QtGui.QHBoxLayout()
        main_layout.addLayout(button_layout)

        button = QtGui.QPushButton("Add Geocache...")
        button.setMinimumSize(button.sizeHint().expandedTo(QtCore.QSize(
            100, 0)))
        button_layout.addWidget(button)
        button.clicked.connect(self._on_add_geocache)

        self._sync_timeline_checkbox = QtGui.QCheckBox("Sync Timeline")
        self._sync_timeline_checkbox.setChecked(True)
        button_layout.addWidget(self._sync_timeline_checkbox)

        button_layout.addStretch()

        button = QtGui.QPushButton("Apply")
        button.setMinimumSize(button.sizeHint().expandedTo(QtCore.QSize(
            100, 0)))
        button_layout.addWidget(button)
        button.clicked.connect(self._on_apply_clicked)

        button = QtGui.QPushButton("Save")
        button.setMinimumSize(button.sizeHint().expandedTo(QtCore.QSize(
            100, 0)))
        button_layout.addWidget(button)
        button.clicked.connect(self._on_save_clicked)
Ejemplo n.º 5
0
    def _setup_post_ui(self):

        self.layout().setContentsMargins(0, 0, 0, 0)
        self.setContentsMargins(0, 0, 0, 0)

        button = QtGui.QPushButton(silk_icon('arrow_switch', 12), "Edit")
        button.clicked.connect(self._on_mapping_clicked)
        button.setFixedSize(button.sizeHint().boundedTo(QtCore.QSize(1000,
                                                                     22)))
        self._main_layout.addWidget(button)

        button = QtGui.QPushButton(silk_icon('delete', 12), "Delete")
        button.clicked.connect(self._on_delete)
        button.setFixedSize(button.sizeHint().boundedTo(QtCore.QSize(1000,
                                                                     22)))
        self._main_layout.addWidget(button)

        # Finally setup the mapping UI. This requires self.meshes() to work.
        self._cache_changed()
Ejemplo n.º 6
0
    def _setup_ui(self):

        self._field = QtGui.QLineEdit()
        self._field.editingFinished.connect(self._on_field_changed)
        self._main_layout.addWidget(self._field)

        button = QtGui.QPushButton(silk_icon("arrow_refresh", 12), "Update")
        button.clicked.connect(self._on_update)
        button.setFixedSize(button.sizeHint().boundedTo(QtCore.QSize(1000,
                                                                     20)))
        self._main_layout.addWidget(button)

        super(Selection, self)._setup_ui()
Ejemplo n.º 7
0
Archivo: model.py Proyecto: vfxetc/sgfs
    def index(self, row, col, parent):

        if col > 0:
            #print 'HERE2', QtCore.QModelIndex()
            return QtCore.QModelIndex()

        node = self.node_from_index(parent)
        try:
            child = node.children()[row]
        except IndexError:
            #print 'HERE3', QtCore.QModelIndex()
            return QtCore.QModelIndex()

        if child.index is None:
            debug('child.index is None: %r -> %r', child, child.state)
            child.index = self.createIndex(row, col, child)
            if child.parent is None:
                debug('\tchild.parent is also None')
                child.parent = node

        #print 'HERE1', child.index
        return child.index
Ejemplo n.º 8
0
    def _setup_ui(self):

        # Lots of layouts...
        self.setLayout(QtGui.QVBoxLayout())

        ## Cache widgets

        self._cache_selector = CacheSelector(parent=self)
        self._cache_selector.path_changed = lambda path: self._cache_changed()

        self.layout().addLayout(self._cache_selector)

        ## Geometry widgets

        self.layout().addWidget(QtGui.QLabel("Geometry & References"))

        self._geometry_layout = QtGui.QVBoxLayout()
        self.layout().addLayout(self._geometry_layout)

        ## Buttons.

        button_layout = QtGui.QHBoxLayout()
        self.layout().addLayout(button_layout)

        button = QtGui.QPushButton(silk_icon('link_add', 12),
                                   "Add Reference Link")
        button.clicked.connect(self._on_add_reference_link)
        button.setMaximumHeight(22)
        button_layout.addWidget(button)

        button = QtGui.QPushButton(silk_icon('link_add', 12),
                                   "Add Selection Link")
        button.clicked.connect(self._on_add_selection_link)
        button.setMaximumHeight(22)
        button_layout.addWidget(button)

        button_layout.addStretch()

        button = QtGui.QPushButton(silk_icon('cross', 12), "")
        button.clicked.connect(self._on_ignore_button)
        button.setFixedSize(button.sizeHint().boundedTo(QtCore.QSize(1000,
                                                                     22)))
        button_layout.addWidget(button)

        # Mappings.
        self._mapping_layout = QtGui.QFormLayout()
        self.layout().addLayout(self._mapping_layout)
Ejemplo n.º 9
0
def standard_setup():
    """Non-standalone user setup."""

    # Setup background check every time a new scene is opened.
    def check_references(*args):
        import metatools.imports
        import sgpublish.check.maya
        metatools.imports.autoreload(sgpublish.check.maya)
        sgpublish.check.maya.start_background_check()

    for name, type_ in [
        ('after_open', OpenMaya.MSceneMessage.kAfterOpen),

            # TODO: Re-enable this one when we know the general uitools.threads stuff
            # is stable, since a crash while saving is tremendously bad.

            # ('after_save', OpenMaya.MSceneMessage.kAfterSave),

            # We used to have kAfterCreateRererence and kAfterLoadReference in here
            # too, but for now it is
            # a relatively corner case for it to not be paired with creating a
            # reference, and the logic is clever enough to stack several update
            # requests together.
    ]:
        __mayatools_usersetup__['name'] = OpenMaya.MSceneMessage.addCallback(
            type_,
            check_references,
        )

    # Setup background check every 5 minutes.
    try:
        from uitools.qt import QtCore
        import sgpublish.check.maya
    except ImportError as e:
        print '[sgpublish] Missing dependency:', e
    else:
        __mayatools_usersetup__['timer'] = timer = QtCore.QTimer()
        timer.timeout.connect(sgpublish.check.maya.start_background_check)
        timer.setInterval(1000 * 60 * 5)  # Every 5 minutes.
        timer.start()
Ejemplo n.º 10
0
class PlayblastTable(QtGui.QTableWidget):
    refresh = QtCore.pyqtSignal()

    def __init__(self, parent = None):
        super(PlayblastTable, self).__init__(parent)
        self.setColumnCount(3)
        self.setColumnWidth(0, 100)
        self.verticalHeader().hide()
        self.horizontalHeader().setStretchLastSection(True)
        self.setHorizontalHeaderLabels(['First Frame', 'Name', 'Creation Time'])
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(self.SelectRows)
        self.setSortingEnabled(True)
        self.sortItems(2, Qt.DescendingOrder)
        self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.itemDoubleClicked .connect(lambda x: self.flipbook_playblast())

    def add_playblasts(self, playblasts):
        for playblast in sorted(playblasts, key = lambda pb: (' None' if pb.user_category == 'none' else pb.user_category, pb.name)):
            row = self.rowCount()
            self.setRowCount(row + 1)
            self.setRowHeight(row, 57)

            thumb = PlayblastThumbnail(playblast.first_frame)
            thumb.playblast = playblast
            self.setCellWidget(row, 0, thumb)

            name = QtGui.QTableWidgetItem(playblast.name)
            self.setItem(row, 1, name)

            date = QtGui.QTableWidgetItem(playblast.created_at.isoformat(' '))
            self.setItem(row, 2, date)

    def contextMenuEvent(self, event):
        menu = QtGui.QMenu(self)
        flipbook_action = menu.addAction("Flipbook")
        flipbook_action.triggered.connect(self.flipbook_playblast)
        qt_action = menu.addAction("Make Quicktime")
        qt_action.triggered.connect(self.make_quicktime)
        refresh_action = menu.addAction("Refresh")
        refresh_action.triggered.connect(self.refresh.emit)
        delete_action = menu.addAction("Delete")
        delete_action.triggered.connect(self.delete_playblasts)
        action = menu.exec_(event.globalPos())

    def current_playblast(self):
        row = self.currentRow()
        thumb = self.cellWidget(row, 0) if row is not None else None
        playblast = thumb and thumb.playblast
        return playblast

    def current_path(self):
        playblast = self.current_playblast()
        path = playblast and playblast.directory
        return path if path and os.path.exists(path) else None

    def delete_playblasts(self):
        rows = []
        for item in self.selectedItems():
            if not item.row() in rows:
                rows.append(item.row())

        for row in reversed(sorted(rows)):
            dirname = None
            thumb = self.cellWidget(row, 0) if row is not None else None
            if thumb and thumb.playblast and thumb.playblast.directory and os.path.exists(thumb.playblast.directory):
                dirname = thumb.playblast.directory

            print "rm", dirname, row
            self.removeRow(row)
            if dirname:
                shutil.rmtree(dirname)

    def flipbook_playblast(self):
        playblast = self.current_playblast()

        cmd = ['rv', '[', os.path.join(playblast.directory, '*.jpg'), '-fps', str(24), ']']
        if playblast.audio:
            cmd.extend(['-over', '[', playblast.audio, ']'])

        # fix for launching rv from maya on mac
        # http://www.tweaksoftware.com/static/documentation/rv/current/html/maya_tools_help.html#_osx_maya_2014
        env = dict(os.environ)
        if 'QT_MAC_NO_NATIVE_MENUBAR' in env:
            del env['QT_MAC_NO_NATIVE_MENUBAR']

        print subprocess.list2cmdline(cmd)
        proc = subprocess.Popen(cmd, env = env)

    def make_quicktime(self):
        playblast = self.current_playblast()

        cmd = ['make_quicktime', playblast.first_frame]

        if playblast.audio:
            cmd.extend(['--audio', playblast.audio])

        if playblast.maya_file:
            cmd.extend(['--shotdir', playblast.maya_file])

        print subprocess.list2cmdline(cmd)
        subprocess.Popen(cmd)
Ejemplo n.º 11
0
 def sizeHint(self):
     if self._loaded:
         return self.pixmap().size()
     else:
         return QtCore.QSize(100, 57)
Ejemplo n.º 12
0
Archivo: model.py Proyecto: vfxetc/sgfs
class Model(QtCore.QAbstractItemModel):

    _pixmaps = {}

    # There appears to be a bug in Maya's PySide 2, in which the default
    # arguments aren't registered as metatypes and you get this sort of message:
    #    QObject::connect: Cannot queue arguments of type 'QList<QPersistentModelIndex>'
    #    (Make sure 'QList<QPersistentModelIndex>' is registered using qRegisterMetaType().)
    # You don't get that if you call in the same thread, so we "fix" the problem
    # with these extra signals which just pass on the request.
    _legacy_layoutAboutToBeChanged = QtCore.pyqtSignal()
    _legacy_layoutChanged = QtCore.pyqtSignal()

    def __init__(self, root_state=None, sgfs=None, shotgun=None, session=None):
        super(Model, self).__init__()

        self._root_state = root_state or {}
        self._root = None

        self.sgfs = sgfs or SGFS(shotgun=shotgun, session=session)

        # Force a more reasonable timeout. Note that this does change the
        # global parameters on the shotgun object.
        self.sgfs.session.shotgun.close()
        self.sgfs.session.shotgun.config.timeout_secs = 5
        self.sgfs.session.shotgun.config.max_rpc_attempts = 1

        self.threadpool = ThreadPool(8, lifo=True)

        self._node_types = []

        # See comment above.
        for name in 'layoutChanged', 'layoutAboutToBeChanged':
            old = getattr(self, name)
            new = getattr(self, '_legacy_' + name)
            setattr(self, name, new)
            new.connect(old.emit)

    def _thread_target(self):
        while True:
            try:
                func, args, kwargs = self._thread_queue.get()
                func(*args, **kwargs)
            except Exception:
                traceback.print_exc()

    def register_node_type(self, node_type):
        self._node_types.append(node_type)

    def index_from_state(self, state):

        # debug('index_from_state %r', sorted(state))

        last_match = None
        nodes = [self.root()]
        while True:

            if not nodes:
                break

            node = nodes.pop(0)

            # Skip over groups. It would be nice if the group class would be
            # able to property handle this logic, but we don't want a "positive
            # match" (for the purposes of traversing the group) to result in
            # not selecting something real (because it is at a lower level than
            # the last group).
            if isinstance(node, Group):
                nodes.extend(node.children())
                continue

            # debug('match?: %r <- %r', node, sorted(node.state))

            if node.parent is None or node.parent.child_matches_initial_state(
                    node, state):
                # debug('YES!!')

                # Trigger initial async.
                node.children()

                node.add_raw_children(node.get_temp_children_from_state(state))
                nodes.extend(node.children())

                last_match = node

        if last_match:
            return last_match.index

    def construct_node(self, key, view_data, state):
        for node_type in self._node_types:
            try:
                return node_type(self, key, view_data, state)
            except TypeError:
                pass
        return Leaf(self, key, view_data, state)

    def hasChildren(self, index):
        node = self.node_from_index(index)
        return not node.is_leaf()

    def root(self):
        if self._root is None:
            self._root = self.construct_node(None, {}, self._root_state)
            self._root.index = QtCore.QModelIndex()
            self._root.parent = None
        return self._root

    def node_from_index(self, index):
        return index.internalPointer() if index.isValid() else self.root()

    def rowCount(self, parent):
        node = self.node_from_index(parent)
        return len(node.children())

    def columnCount(self, parent):
        return 1

    def index(self, row, col, parent):

        if col > 0:
            #print 'HERE2', QtCore.QModelIndex()
            return QtCore.QModelIndex()

        node = self.node_from_index(parent)
        try:
            child = node.children()[row]
        except IndexError:
            #print 'HERE3', QtCore.QModelIndex()
            return QtCore.QModelIndex()

        if child.index is None:
            debug('child.index is None: %r -> %r', child, child.state)
            child.index = self.createIndex(row, col, child)
            if child.parent is None:
                debug('\tchild.parent is also None')
                child.parent = node

        #print 'HERE1', child.index
        return child.index

    def parent(self, child):
        node = self.node_from_index(child)
        if node.parent is not None:
            return node.parent.index
        else:
            return QtCore.QModelIndex()

    def flags(self, index):

        if not index.isValid():
            return None
        node = self.node_from_index(index)

        if node.view_data.get('disabled'):
            return Qt.NoItemFlags
        else:
            return super(Model, self).flags(index)

    def data(self, index, role):

        if not index.isValid():
            return None

        node = self.node_from_index(index)

        if role == Qt.DisplayRole:
            try:
                return node.view_data[Qt.DisplayRole]
            except KeyError:
                return None

        if role == Qt.DecorationRole:

            if node.error_count:
                return icon('fatcow/cross')

            if node.is_loading:
                return icon('fatcow/arrow_refresh')

            node = self.node_from_index(index)
            data = node.view_data.get(Qt.DecorationRole)

            if data is None:
                return None

            if isinstance(data, QtGui.QColor):
                key = (data.red(), data.green(), data.blue())
                if key not in self._pixmaps:
                    pixmap = QtGui.QPixmap(16, 16)
                    pixmap.fill(Qt.transparent)
                    painter = QtGui.QPainter(pixmap)
                    brush = QtGui.QBrush(data)
                    painter.setBrush(brush)
                    painter.setPen(data.darker())
                    painter.drawRect(2, 2, 12, 12)
                    painter.end()
                    self._pixmaps[key] = pixmap
                return self._pixmaps[key]

            if isinstance(data, basestring):
                try:
                    return self._pixmaps[data]
                except KeyError:
                    pass
                self._pixmaps[data] = pixmap = icon(data) or QtGui.QPixmap(
                    data)
                return pixmap

            return data

        if role == Qt.FontRole:
            if node.view_data.get(Qt.FontRole) is not None:
                return node.view_data[Qt.FontRole]
            return None

        if role == Qt.ForegroundRole:
            if node.error_count:
                return QtGui.QColor.fromRgb(128, 0, 0)

        if role == HeaderDisplayRole:
            try:
                return node.view_data['header']
            except KeyError:
                pass

        # Passthrough other roles.
        try:
            return node.view_data[role]
        except KeyError:
            return None
Ejemplo n.º 13
0
 def indexAt(self, point):
     return QtCore.QModelIndex()
Ejemplo n.º 14
0
 def moveCursor(self, action, modifiers):
     #debug('moveCursor(%r, %r)', action, modifiers)
     return self.model().index(0, 0, QtCore.QModelIndex())
Ejemplo n.º 15
0
class Host(QtCore.QThread):

    # (message)
    executor_message = QtCore.pyqtSignal([object])

    # (worker, type, message)
    worker_message = QtCore.pyqtSignal([object, object, object])

    def __init__(self, conn):
        super(Host, self).__init__()

        # Will be set to None if the connection is closed.
        self.conn = conn

        # Set by a "config" message from the executor.
        self.max_workers = None

        # All workers we ever see, by uuid.
        self.workers = {}

        # All workers with are non of ACTIVE, FAILED, or DEPENDENCY_FAILED.
        self.unfinished_workers = []

    def run(self):
        try:

            while True:

                # Trigger state changes across all workers. We don't need to
                # bother cascading state changes to earlier workers since
                # dependencies can only be to previous workers.

                active_count = sum(
                    int(w.state == ACTIVE) for w in self.unfinished_workers)
                for worker in self.unfinished_workers:

                    # Don't bother poking anyone who is waiting if we have
                    # already reached max_workers.
                    allow_start = (self.max_workers is None
                                   or active_count < self.max_workers)

                    old_state = worker.state
                    worker.poke(allow_start)
                    if worker.state != old_state:

                        # Adjust active count.
                        active_count -= int(old_state == ACTIVE)
                        active_count += int(worker.state == ACTIVE)

                        # Send state transition message.
                        self.worker_message.emit(
                            worker, "state_changed",
                            dict(
                                old=old_state,
                                new=worker.state,
                            ))

                # Prune all complete workers.
                self.unfinished_workers = [
                    w for w in self.unfinished_workers
                    if w.state not in finished_states
                ]

                rlist = [
                    worker.conn for worker in self.unfinished_workers
                    if worker.conn is not None
                ]
                if self.conn is not None:
                    rlist.append(self.conn)

                if not rlist:

                    # Wait for changes if there is something that failed, as
                    # the user may hit "Retry".
                    if any(x.state in failed_states
                           for x in self.workers.itervalues()):
                        time.sleep(0.25)
                        continue

                    # There is nothing left to do, and the executor is closed.
                    break

                rlist, _, _ = select.select(rlist, [], [])
                for conn in rlist:

                    if conn is self.conn:
                        owner_type = 'executor'
                    else:
                        owner_type = 'worker'
                        worker = [
                            x for x in self.unfinished_workers
                            if x.conn is conn
                        ][0]

                    # Get a message, turning EOFs into shutdown messages.
                    # TODO: should these actually be "eof"?
                    try:
                        msg = conn.recv()
                    except EOFError:
                        msg = {'type': 'shutdown'}

                    type_ = msg.pop('type', None)
                    # debug('Host: %r sent %r:\n%s', owner_type, type_, pprint.pformat(msg))

                    # Send the message to methods on ourself, as well as to
                    # the workers.
                    handler = getattr(
                        self, 'do_%s_%s' % (owner_type, type_ or 'unknown'),
                        None)
                    if owner_type == 'executor':
                        if handler:
                            handler(**msg)
                        self.executor_message.emit(msg)
                    else:
                        if handler:
                            handler(worker, **msg)
                        self.worker_message.emit(worker, type_, msg)

        except:
            traceback.print_exc()
            QtGui.QApplication.exit(1)

        finally:
            if self.conn is not None:
                self.conn.send(dict(type='shutdown'))

        # debug("AT THE END")
        if not any(w.state in failed_states
                   for w in self.workers.itervalues()):
            QtGui.QApplication.exit(0)

    def send(self, msg):
        if self.conn is not None:
            self.conn.send(msg)

    def do_executor_config(self, max_workers=NotSet, **msg):
        # debug('config: max_workers=%r', max_workers)
        if max_workers is not NotSet:
            self.max_workers = max_workers

    def do_executor_submit(self, uuid, **msg):

        worker = Worker(self, uuid, **msg)
        self.workers[uuid] = worker
        self.unfinished_workers.append(worker)
        self.worker_message.emit(worker, "new", msg)

    def do_executor_shutdown(self, **msg):
        # debug('Host: executor shut down')
        self.conn = None

    def do_worker_notify(self, worker, **msg):
        msg.setdefault('icon', worker.icon)
        msg.setdefault('title', worker.name)
        utils.notify(**msg)

    def do_worker_result(self, worker, **msg):

        self.worker_message.emit(worker, 'state_changed',
                                 dict(old=worker.state, new=COMPLETE))
        worker.state = COMPLETE

        # Forward the message.
        msg['type'] = 'result'
        msg['uuid'] = worker.uuid
        self.send(msg)

    def do_worker_exception(self, worker, **msg):

        self.worker_message.emit(worker, 'state_changed',
                                 dict(old=worker.state, new=FAILED))
        worker.state = FAILED

        # Forward the message.
        msg['type'] = 'exception'
        msg['uuid'] = worker.uuid
        self.send(msg)
        msg.setdefault('exception_name', 'Unknown')
        msg.setdefault('exception_message', 'unknown')
        msg.setdefault('exception_traceback', '')
        utils.notify(
            title='Job Failed: %s' % (worker.name or 'Untitled'),
            message=
            '{exception_name}: {exception_message}\n{exception_traceback}'.
            format(**msg),
            sticky=True,
            icon=utils.icon(worker.icon) if worker.icon else None,
        )

    def do_worker_shutdown(self, worker):

        # It wasn't done it's job.
        if worker.state not in finished_states:
            self.do_worker_exception(
                worker,
                **dict(
                    type='exception',
                    exception_name='RuntimeError',
                    exception_message='worker shutdown unexpectedly; was %r' %
                    worker.state,
                    exception=RuntimeError(
                        'worker shutdown unexpectedly; was %r' % worker.state),
                ))
Ejemplo n.º 16
0
Archivo: model.py Proyecto: vfxetc/sgfs
 def parent(self, child):
     node = self.node_from_index(child)
     if node.parent is not None:
         return node.parent.index
     else:
         return QtCore.QModelIndex()
Ejemplo n.º 17
0
class Picker(QtGui.QTabWidget):
    
    pathChanged = QtCore.pyqtSignal(object)
    
    def __init__(self, parent = None, selection_mode = QtGui.QTableWidget.SingleSelection,):
        super(Picker, self).__init__(parent)
        
        self._playblasts = []
        self._selection_mode = selection_mode
        self._find_legacy_playblasts()
        self._tables_by_name = {}
        self._setup_ui()

    
    def _setup_ui(self):
        self.currentChanged.connect(self._current_tab_changed)
        tables = self._tables_by_name
        for playblast in sorted(self._playblasts,
            key=lambda pb: (' None' if pb.user_category == 'none' else pb.user_category, pb.name)
        ):
            if playblast.user_category not in tables:

                table = PlayblastTable()
                table.itemSelectionChanged.connect(self._table_selection_changed)
                table.refresh.connect(self.refresh)
                if self._selection_mode:
                    table.setSelectionMode(self._selection_mode)
                tables[playblast.user_category] = table                
                self.addTab(table, "Playblasts" if playblast.user_category == "none" else playblast.user_category.title())

            table = tables[playblast.user_category]
            table.add_playblasts([playblast])
        
        for table in tables.itervalues():
            table.resizeColumnToContents(1)
            table.resizeColumnToContents(2)

    def refresh(self):
        for table in self._tables_by_name.itervalues():
            table.clearContents()
            table.setRowCount(0)
        self._playblasts = []
        self._find_legacy_playblasts()
        self._setup_ui()

    def _find_legacy_playblasts(self):

        # This is the folder that they are stored in.
        if not os.path.exists('/var/tmp/srv_playblast'):
            return

        for name in os.listdir('/var/tmp/srv_playblast'):

            directory = os.path.join('/var/tmp/srv_playblast', name)

            # Try to grab the first frame.
            try:
                file_names = os.listdir(directory)
            except OSError as e:
                if e.errno == 20: # Not a folder.
                    continue
                raise

            frame_gen = (x for x in sorted(file_names) if os.path.splitext(x)[1] in ('.jpg', '.jpeg'))
            first_frame = next(frame_gen, None)
            if first_frame is None:
                continue

            audio = None
            maya_file = None
            audio_text = next((x for x in sorted(file_names) if os.path.splitext(x)[1] in ('.txt',)))
            if audio_text:
                audio, maya_file, frame_rate = parse_audio_txt(os.path.join(directory, audio_text))

            first_frame = os.path.join(directory, first_frame)

            user_category_path = os.path.join(directory, 'approval_status')
            user_category = open(user_category_path).read() if os.path.exists(user_category_path) else None
            user_category = str(user_category).lower()

            self._playblasts.append(PlayblastInfo(
                name = name,
                directory = directory,
                user_category = user_category,
                first_frame = first_frame,
                created_at = datetime.datetime.fromtimestamp(os.path.getctime(first_frame)),
                audio = audio,
                maya_file = maya_file
            ))

    def autoSetMinimumWidth(self):
        width = 0
        for table in self._tables_by_name.itervalues():
            width = max(width, sum(table.columnWidth(i) for i in xrange(table.columnCount())))
        if width:
            self.setMinimumWidth(width)
    
    def _table_selection_changed(self):
        path = self.currentPath()
        self.pathChanged.emit(path)
    
    def _current_tab_changed(self):
        path = self.currentPath()
        self.pathChanged.emit(path)
        
    def currentPath(self):
        table = self.currentWidget()
        return table.current_path()
Ejemplo n.º 18
0
Archivo: model.py Proyecto: vfxetc/sgfs
 def root(self):
     if self._root is None:
         self._root = self.construct_node(None, {}, self._root_state)
         self._root.index = QtCore.QModelIndex()
         self._root.parent = None
     return self._root