コード例 #1
0
ファイル: common_ui.py プロジェクト: liaokongVFX/bookmarks
    def action(self):
        """Save the entered text to the BookmarkDB."""
        index = self.parent().selectionModel().currentIndex()
        text = u'{}'.format(index.data(common.DescriptionRole))
        if text.lower() == self.text().lower():
            self.hide()
            return

        if not index.data(common.ParentPathRole):
            self.hide()
            return

        p = index.data(QtCore.Qt.StatusTipRole)
        if common.is_collapsed(p):
            k = common.proxy_path(index)
        else:
            k = p

        db = bookmark_db.get_db(
            index.data(common.ParentPathRole)[0],
            index.data(common.ParentPathRole)[1],
            index.data(common.ParentPathRole)[2]
        )
        db.setValue(k, u'description', self.text())

        source_index = index.model().mapToSource(index)
        data = source_index.model().model_data()[source_index.row()]
        data[common.DescriptionRole] = self.text()
        self.parent().update(source_index)
        self.hide()
コード例 #2
0
 def update_description(index, res):
     db = bookmark_db.get_db(
         index.data(common.ParentPathRole)[0],
         index.data(common.ParentPathRole)[1],
         index.data(common.ParentPathRole)[2],
     )
     source_index = self.model().mapToSource(index)
     data = source_index.model().model_data()[source_index.row()]
     self.model().sourceModel().update_description(db, data)
コード例 #3
0
    def setUpClass(cls):
        super(TestSQLite, cls).setUpClass()
        import bookmarks.bookmark_db as bookmark_db

        cls.db = bookmark_db.get_db(
            cls.server,
            cls.job,
            cls.bookmarks[0]
        )
コード例 #4
0
def export_favourites():
    """Saves all favourites including the descriptions and the thumbnails."""
    try:
        import uuid
        import bookmarks.settings as settings
        import bookmarks.bookmark_db as bookmark_db

        res = QtWidgets.QFileDialog.getSaveFileName(
            caption=u'Select where to save your favourites',
            filter=u'*.favourites',
            dir=QtCore.QStandardPaths.writableLocation(
                QtCore.QStandardPaths.HomeLocation),
        )
        destination, _ = res
        if not destination:
            return

        favourites = settings.local_settings.favourites()
        server, job, root = get_favourite_parent_paths()
        db = bookmark_db.get_db(server, job, root)
        zip_path = u'{}/{}/{}/{}.zip'.format(server, job, root, uuid.uuid4())

        # Make sure the temp folder exists
        QtCore.QFileInfo(zip_path).dir().mkpath(u'.')

        with zipfile.ZipFile(zip_path, 'a') as z:
            # Adding thumbnail to zip
            for favourite in favourites:
                file_info = QtCore.QFileInfo(db.thumbnail_path(favourite))
                if not file_info.exists():
                    continue
                z.write(file_info.filePath(), file_info.fileName())
            z.writestr(u'favourites', u'\n'.join(favourites))

        file_info = QtCore.QFileInfo(zip_path)
        if not file_info.exists():
            raise RuntimeError(
                u'Unexpected error occured: could not find the favourites file'
            )

        QtCore.QDir().rename(file_info.filePath(), destination)
        if not QtCore.QFileInfo(destination).exists():
            raise RuntimeError(
                u'Unexpected error occured: could not find the favourites file'
            )
        reveal(destination)

    except Exception as e:
        import bookmarks.log as log
        import bookmarks.common_ui as common_ui
        common_ui.ErrorBox(u'Could not save the favourites.',
                           u'{}'.format(e)).open()
        log.error(u'Exporting favourites failed.')
        raise
コード例 #5
0
ファイル: todo_editor.py プロジェクト: liaokongVFX/bookmarks
    def refresh(self):
        """Populates the list from the database."""
        if not self.parent():
            return
        if not self.index.isValid():
            return
        if not self.index.data(common.FileInfoLoaded):
            return

        db = bookmark_db.get_db(
            self.index.data(common.ParentPathRole)[0],
            self.index.data(common.ParentPathRole)[1],
            self.index.data(common.ParentPathRole)[2])
        if self.index.data(common.TypeRole) == common.FileItem:
            k = self.index.data(QtCore.Qt.StatusTipRole)
        elif self.index.data(common.TypeRole) == common.SequenceItem:
            k = common.proxy_path(self.index)

        v = db.value(k, u'notes')
        if not v:
            return

        try:
            v = base64.b64decode(v)
            d = json.loads(v)
        except:
            log.error(u'Error decoding notes from JSON')
            return

        if not v:
            return

        self.clear()

        keys = sorted(d.keys())
        try:
            for k in keys:
                self.add_item(text=d[k][u'text'], checked=d[k][u'checked'])
        except:
            log.error(u'Error adding notes')
            common_ui.ErrorBox(u'Error refreshing the data', u'').open()
            raise
コード例 #6
0
    def init_database_values(self, compare=False):
        """Load the current settings from the database and apply them to the UI
        controls.

        """
        def set_saved(k):
            v = db.value(1, k.replace(u'_editor', u''), table=u'properties')
            if not v:
                return
            if compare:
                return v
            getattr(self, k).setText(unicode(v) if v else u'')

        def emit_text(k):
            getattr(self, k).textEdited.emit(getattr(self, k).text())

        controls = (u'framerate_editor', u'width_editor', u'height_editor',
                    u'prefix_editor', u'startframe_editor', u'duration_editor',
                    u'identifier_editor', u'slacktoken_editor')

        db = bookmark_db.get_db(self.server, self.job, self.root)

        d = {}
        with db.transactions():
            try:
                for control in controls:
                    d[control] = set_saved(control)
                    if compare:
                        continue
                    emit_text(control)
            except Exception as e:
                common_ui.ErrorBox(
                    u'Could not save the properties.',
                    u'There seems to be an error with the database:\n{}'.
                    format(e),
                ).open()
                log.error(u'Error saving properties to the database')
                raise

        return d
コード例 #7
0
ファイル: todo_editor.py プロジェクト: liaokongVFX/bookmarks
    def save_settings(self):
        """Saves the current list of todo items to the assets configuration file."""
        if not self.index.isValid():
            return

        data = {}
        for n in xrange(len(self.todoeditors_widget.items)):
            item = self.todoeditors_widget.items[n]
            editor = item.findChild(TodoItemEditor)
            checkbox = item.findChild(CheckBoxButton)
            if not editor.document().toPlainText():
                continue
            data[n] = {
                u'checked': not checkbox.checked,
                u'text': editor.document().toHtml(),
            }

        k = common.proxy_path(self.index)
        db = bookmark_db.get_db(
            self.index.data(common.ParentPathRole)[0],
            self.index.data(common.ParentPathRole)[1],
            self.index.data(common.ParentPathRole)[2])

        try:
            v = json.dumps(data, ensure_ascii=False, encoding='utf-8')
            v = base64.b64encode(v.encode('utf-8'))
        except:
            s = u'Error saving notes.'
            log.error(s)
            common_ui.ErrorBox(u'Error saving notes.', s).open()
            raise

        db.setValue(k, u'notes', v)
        todo_count = len([k for k in data if not data[k][u'checked']])
        self.index.model().setData(self.index,
                                   todo_count,
                                   role=common.TodoCountRole)
コード例 #8
0
    def save_values_to_database(self, compare=False):
        """Save the current UI values to the database.

        """
        def save(k, db):
            """Performs the save to the database."""
            v = getattr(self, k).text()
            if compare:
                return v
            k = k.replace(u'_editor', u'')
            db.setValue(1, k, v, table=u'properties')
            return None

        controls = (u'framerate_editor', u'width_editor', u'height_editor',
                    u'prefix_editor', u'startframe_editor', u'duration_editor',
                    u'identifier_editor', u'slacktoken_editor')

        db = bookmark_db.get_db(self.server, self.job, self.root)

        d = {}
        with db.transactions():
            for control in controls:
                d[control] = save(control, db)
        return d
コード例 #9
0
    def __initdata__(self):
        """Collects the data needed to populate the bookmarks model.

        Bookmarks are made up of a tuple of ``(server, job, root)`` values and
        are stored in the local user system settings, eg. the Registry
        in under windows. Each bookmarks can be associated with a thumbnail,
        custom description and a list of comments, todo items.

        Note:
            This model does not have threads associated with it as fetching
            necessary data is relatively inexpensive.

        """
        def dflags():
            """The default flags to apply to the item."""
            return (QtCore.Qt.ItemIsDropEnabled
                    | QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled
                    | QtCore.Qt.ItemIsSelectable)

        task_folder = self.task_folder()

        active_paths = settings.local_settings.verify_paths()
        favourites = settings.local_settings.favourites()
        bookmarks = settings.local_settings.value(u'bookmarks')
        bookmarks = bookmarks if bookmarks else {}

        _height = self.ROW_SIZE.height() - common.ROW_SEPARATOR()

        for k, v in bookmarks.iteritems():
            if not all(v.values()):
                continue

            file_info = QtCore.QFileInfo(k)
            exists = file_info.exists()

            if exists:
                flags = dflags()
                pixmap = images.ImageCache.get_rsc_pixmap(
                    u'bookmark_sm', common.ADD, _height)
            else:
                flags = dflags() | common.MarkedAsArchived
                pixmap = images.ImageCache.get_rsc_pixmap(
                    u'failed', common.REMOVE, _height)
            placeholder_image = pixmap
            default_thumbnail_image = pixmap

            filepath = file_info.filePath().lower()

            # Active Flag
            if all((v[u'server'] == active_paths[u'server'],
                    v[u'job'] == active_paths[u'job'],
                    v[u'root'] == active_paths[u'root'])):
                flags = flags | common.MarkedAsActive
            # Favourite Flag
            if filepath in favourites:
                flags = flags | common.MarkedAsFavourite

            text = u'{}  |  {}'.format(
                v[u'job'],
                v[u'root'],
                # v[u'root'].split(u'/').pop(),
            )

            data = self.INTERNAL_MODEL_DATA[task_folder][common.FileItem]
            idx = len(data)

            data[idx] = common.DataDict({
                QtCore.Qt.DisplayRole:
                text,
                QtCore.Qt.EditRole:
                text,
                QtCore.Qt.StatusTipRole:
                filepath,
                QtCore.Qt.ToolTipRole:
                filepath,
                QtCore.Qt.SizeHintRole:
                self.ROW_SIZE,
                #
                common.TextSegmentRole:
                self.get_text_segments(text),
                #
                common.EntryRole: [],
                common.FlagsRole:
                flags,
                common.ParentPathRole: (v[u'server'], v[u'job'], v[u'root']),
                common.DescriptionRole:
                u'',
                common.TodoCountRole:
                0,
                common.FileDetailsRole:
                None,
                common.SequenceRole:
                None,
                common.EntryRole: [],
                common.FileInfoLoaded:
                False,
                common.StartpathRole:
                None,
                common.EndpathRole:
                None,
                #
                common.ThumbnailLoaded:
                False,
                #
                common.TypeRole:
                common.FileItem,
                #
                common.SortByNameRole:
                text,
                common.SortByLastModifiedRole:
                file_info.lastModified().toMSecsSinceEpoch(),
                common.SortBySizeRole:
                file_info.size(),
                #
                common.AssetCountRole:
                0,
                #
                common.IdRole:
                idx
            })

            db = None
            n = 0
            db = bookmark_db.get_db(
                v[u'server'],
                v[u'job'],
                v[u'root'],
            )
            with db.transactions():
                # Item flags
                flags = data[idx][common.FlagsRole]
                v = db.value(data[idx][QtCore.Qt.StatusTipRole], u'flags')
                flags = flags | v if v is not None else flags
                data[idx][common.FlagsRole] = flags

                # Todos are a little more convoluted - the todo count refers to
                # all the current outstanding todos af all assets, including
                # the bookmark itself
                n = 0
                for v in db.values(u'notes').itervalues():
                    if not v:
                        continue
                    if v[u'notes']:
                        try:
                            v = base64.b64decode(v[u'notes'])
                            d = json.loads(v)
                            n += len([
                                k for k in d
                                if not d[k][u'checked'] and d[k][u'text']
                            ])
                        except (ValueError, TypeError):
                            log.error(u'Error decoding JSON notes')

                data[idx][common.TodoCountRole] = n
                self.update_description(db, data[idx])

        self.activeChanged.emit(self.active_index())
コード例 #10
0
def import_favourites(source=None):
    """Import a previously exported favourites file.

    Args:
        source (unicode): Path to a file. Defaults to `None`.

    """
    try:
        import bookmarks.settings as settings
        import bookmarks.bookmark_db as bookmark_db

        if not isinstance(source, unicode):
            res = QtWidgets.QFileDialog.getOpenFileName(
                caption=u'Select the favourites file to import',
                filter=u'*.favourites'
                # options=QtWidgets.QFileDialog.ShowDirsOnly
            )
            source, _ = res
            if not source:
                return

        current_favourites = settings.local_settings.favourites()
        create_temp_dir()

        with zipfile.ZipFile(source) as zip:
            namelist = zip.namelist()
            namelist = [f.lower() for f in namelist]

            if u'favourites' not in namelist:
                import bookmarks.log as log
                import bookmarks.common_ui as common_ui
                s = u'The favourites list is missing from the archive.'
                common_ui.ErrorBox(
                    u'Invalid ".favourites" file',
                    s,
                ).open()
                log.error(s)
                raise RuntimeError(s)

            with zip.open(u'favourites') as f:
                favourites = f.readlines()
                favourites = [unicode(f).strip().lower() for f in favourites]

            server, job, root = get_favourite_parent_paths()
            db = bookmark_db.get_db(server, job, root)
            for favourite in favourites:
                file_info = QtCore.QFileInfo(db.thumbnail_path(favourite))
                if file_info.fileName().lower() in namelist:
                    dest = u'{}/{}/{}/.bookmark'.format(server, job, root)
                    zip.extract(file_info.fileName(), dest)

                if favourite not in current_favourites:
                    current_favourites.append(favourite)

            current_favourites = sorted(list(set(current_favourites)))
            settings.local_settings.setValue(u'favourites', current_favourites)

    except Exception as e:
        import bookmarks.log as log
        import bookmarks.common_ui as common_ui
        common_ui.ErrorBox(u'Could not import the favourites.',
                           u'{}'.format(e)).open()
        log.error(u'Import favourites failed.')
        raise
コード例 #11
0
ファイル: threads.py プロジェクト: liaokongVFX/bookmarks
    def process_data(self, ref):
        """Populates the item with the missing file information.

        Args:
            ref (weakref): An internal model data DataDict instance's weakref.

        Returns:
            bool: `True` if all went well, `False` otherwise.

        """
        def is_valid():
            return False if not ref() or self.interrupt or ref()[
                common.FileInfoLoaded] else True

        if not is_valid():
            return False

        try:
            pp = ref()[common.ParentPathRole]
            db = bookmark_db.get_db(pp[0], pp[1], pp[2])

            if not is_valid():
                return False

            collapsed = common.is_collapsed(ref()[QtCore.Qt.StatusTipRole])
            seq = ref()[common.SequenceRole]

            if not is_valid():
                return False
            proxy_k = common.proxy_path(ref())
            if collapsed:
                k = proxy_k
            else:
                if not is_valid():
                    return False
                k = ref()[QtCore.Qt.StatusTipRole]

            # Issues SQLite "BEGIN"
            with db.transactions():
                # Description
                v = db.value(k, u'description')
                if v:
                    if not is_valid():
                        return False
                    ref()[common.DescriptionRole] = v

                v = db.value(k, u'notes')
                count = 0
                if v:
                    try:
                        v = base64.b64decode(v)
                        v = json.loads(v)
                        count = [
                            k for k in v
                            if v[k][u'text'] and not v[k][u'checked']
                        ]
                        count = len(count)
                    except:
                        log.error(u'Could not read notes')

                if not is_valid():
                    return False
                ref()[common.TodoCountRole] = count

                # Item flags
                if not is_valid():
                    return False
                flags = ref(
                )[common.
                  FlagsRole] | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled

                v = db.value(k, u'flags')
                if v:
                    flags = flags | v
                v = db.value(proxy_k, u'flags')
                if v:
                    flags = flags | v

                if not is_valid():
                    return False
                ref()[common.FlagsRole] = flags

            # For sequences we will work out the name of the sequence based on
            # the frames.
            if not is_valid():
                return False
            if ref()[common.TypeRole] == common.SequenceItem:
                if not is_valid():
                    return False
                frs = ref()[common.FramesRole]
                intframes = [int(f) for f in frs]
                padding = len(frs[0])
                rangestring = common.get_ranges(intframes, padding)

                if not is_valid():
                    return False
                seq = ref()[common.SequenceRole]
                startpath = \
                    seq.group(1) + \
                    unicode(min(intframes)).zfill(padding) + \
                    seq.group(3) + \
                    u'.' + \
                    seq.group(4)
                endpath = \
                    seq.group(1) + \
                    unicode(max(intframes)).zfill(padding) + \
                    seq.group(3) + \
                    u'.' + \
                    seq.group(4)
                seqpath = \
                    seq.group(1) + \
                    u'[' + rangestring + u']' + \
                    seq.group(3) + \
                    u'.' + \
                    seq.group(4)
                seqname = seqpath.split(u'/')[-1]

                # Setting the path names
                if not is_valid():
                    return False
                ref()[common.StartpathRole] = startpath
                if not is_valid():
                    return False
                ref()[common.EndpathRole] = endpath
                if not is_valid():
                    return False
                ref()[QtCore.Qt.StatusTipRole] = seqpath
                if not is_valid():
                    return False
                ref()[QtCore.Qt.ToolTipRole] = seqpath
                if not ref():
                    return False
                ref()[QtCore.Qt.DisplayRole] = seqname
                if not is_valid():
                    return False
                ref()[QtCore.Qt.EditRole] = seqname
                if not is_valid():
                    return False
                # We saved the DirEntry instances previously in `__initdata__` but
                # only for the thread to extract the information from it.
                if not is_valid():
                    return False
                er = ref()[common.EntryRole]
                if er:
                    mtime = 0
                    for entry in er:
                        stat = entry.stat()
                        mtime = stat.st_mtime if stat.st_mtime > mtime else mtime
                        if not is_valid():
                            return False
                        ref()[common.SortBySizeRole] += stat.st_size
                    if not is_valid():
                        return False
                    ref()[common.SortByLastModifiedRole] = mtime
                    mtime = common.qlast_modified(mtime)

                    if not is_valid():
                        return False
                    info_string = \
                        unicode(len(intframes)) + u'f;' + \
                        mtime.toString(u'dd') + u'/' + \
                        mtime.toString(u'MM') + u'/' + \
                        mtime.toString(u'yyyy') + u' ' + \
                        mtime.toString(u'hh') + u':' + \
                        mtime.toString(u'mm') + u';' + \
                        common.byte_to_string(ref()[common.SortBySizeRole])
                    if not is_valid():
                        return False
                    ref()[common.FileDetailsRole] = info_string

            if not is_valid():
                return False
            if ref()[common.TypeRole] == common.FileItem:
                if not is_valid():
                    return False
                er = ref()[common.EntryRole]
                if er:
                    stat = er[0].stat()
                    mtime = stat.st_mtime
                    ref()[common.SortByLastModifiedRole] = mtime
                    mtime = common.qlast_modified(mtime)
                    ref()[common.SortBySizeRole] = stat.st_size
                    info_string = \
                        mtime.toString(u'dd') + u'/' + \
                        mtime.toString(u'MM') + u'/' + \
                        mtime.toString(u'yyyy') + u' ' + \
                        mtime.toString(u'hh') + u':' + \
                        mtime.toString(u'mm') + u';' + \
                        common.byte_to_string(ref()[common.SortBySizeRole])
                    if not is_valid():
                        return False
                    ref()[common.FileDetailsRole] = info_string
                if not is_valid():
                    return False

            # Finally, set flag to mark this loaded
            if not is_valid():
                return False
            return True
        except:
            log.error(u'Error processing file info.')
        finally:
            if ref():
                ref()[common.FileInfoLoaded] = True
コード例 #12
0
    def __initdata__(self):
        """Collects the data needed to populate the bookmarks model by querrying
        the path stored in ``self.parent_path``.

        Note:
            Getting asset information is relatively cheap,
            hence the model does not have any threads associated with it.

        """
        def dflags():
            """The default flags to apply to the item."""
            return (QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled
                    | QtCore.Qt.ItemIsSelectable)

        if not self.parent_path:
            return
        if not all(self.parent_path):
            return

        task_folder = self.task_folder()
        dtype = self.data_type()

        self.INTERNAL_MODEL_DATA[task_folder] = common.DataDict({
            common.FileItem:
            common.DataDict(),
            common.SequenceItem:
            common.DataDict()
        })

        favourites = settings.local_settings.favourites()
        sfavourites = set(favourites)

        activeasset = settings.local_settings.value(u'activepath/asset')
        server, job, root = self.parent_path
        bookmark_path = u'{}/{}/{}'.format(server, job, root)

        # Let's get the identifier from the bookmark database
        db = bookmark_db.get_db(server, job, root)
        ASSET_IDENTIFIER = db.value(1, u'identifier', table='properties')

        nth = 1
        c = 0
        for entry in _scandir.scandir(bookmark_path):
            if entry.name.startswith(u'.'):
                continue
            if not entry.is_dir():
                continue

            filepath = entry.path.replace(u'\\', u'/')

            if ASSET_IDENTIFIER:
                identifier = u'{}/{}'.format(filepath, ASSET_IDENTIFIER)
                if not QtCore.QFileInfo(identifier).exists():
                    continue

            # Progress bar
            c += 1
            if not c % nth:
                self.progressMessage.emit(u'Found {} assets...'.format(c))
                QtWidgets.QApplication.instance().processEvents(
                    QtCore.QEventLoop.ExcludeUserInputEvents)

            filename = entry.name
            flags = dflags()

            if filepath.lower() in sfavourites:
                flags = flags | common.MarkedAsFavourite

            if activeasset:
                if activeasset.lower() == filename.lower():
                    flags = flags | common.MarkedAsActive

            idx = len(self.INTERNAL_MODEL_DATA[task_folder][dtype])
            name = re.sub(ur'[_]{1,}', u' ', filename).strip(u'_')
            self.INTERNAL_MODEL_DATA[task_folder][dtype][
                idx] = common.DataDict({
                    QtCore.Qt.DisplayRole:
                    name,
                    QtCore.Qt.EditRole:
                    filename,
                    QtCore.Qt.StatusTipRole:
                    filepath,
                    QtCore.Qt.SizeHintRole:
                    self.ROW_SIZE,
                    #
                    common.EntryRole: [
                        entry,
                    ],
                    common.FlagsRole:
                    flags,
                    common.ParentPathRole: (server, job, root, filename),
                    common.DescriptionRole:
                    u'',
                    common.TodoCountRole:
                    0,
                    common.FileDetailsRole:
                    u'',
                    common.SequenceRole:
                    None,
                    common.FramesRole: [],
                    common.FileInfoLoaded:
                    False,
                    common.StartpathRole:
                    None,
                    common.EndpathRole:
                    None,
                    #
                    common.ThumbnailLoaded:
                    False,
                    #
                    common.TypeRole:
                    common.FileItem,
                    #
                    common.SortByNameRole:
                    common.namekey(filepath),
                    common.SortByLastModifiedRole:
                    0,
                    common.SortBySizeRole:
                    0,
                    #
                    common.IdRole:
                    idx
                })

        # Explicitly emit signal to notify the other dependent model
        self.activeChanged.emit(self.active_index())