Skip to content

Commit

Permalink
Major plugin overhaul to use shared relic module
Browse files Browse the repository at this point in the history
asset drops and plugin integrations are now compatible with Arnold and are far more stable in nuke.
  • Loading branch information
swillisart committed Nov 6, 2022
1 parent daf4a78 commit b6c077f
Show file tree
Hide file tree
Showing 23 changed files with 544 additions and 591 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Relic defines categorization interactively with classification of items and thei
2. [Relic Application](h) - The interface for users to interact with the catalog; checking out items into local copies to utilize.
3. [Relic Plugins](h) - The extension of third-party host applications to author and operate on assets following the specification.

## What Relic is **NOT**
## **Scope**: What Relic is not
---
While Relic can be used for these purposes it is not built for them and will not be extended to solve their specific problems:

Expand Down
4 changes: 2 additions & 2 deletions build_all.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ if (Test-Path -path $dst ){
./scripts/activate
pyinstaller --noconfirm ./Relic.spec

Copy-item -path './Lib/site-packages/cv2/data' -destination ($dst + '/cv2/data')
Copy-item -path './Lib/site-packages/imagine/libraw/libraw.dll' -destination $dst
Copy-item -path './gifski.exe' -destination $dst
Copy-item -path './hdr_create.exe' -destination $dst
Expand All @@ -15,7 +14,8 @@ Copy-item -path './README.md' -destination $dst
robocopy /E /xc './dist/Peak' './dist/Relic'
robocopy /E /xc './dist/Capture' './dist/Relic'
robocopy /E /xc './Lib/site-packages/freetype' ($dst + '/freetype')
robocopy /E /xc './viewer/luts' ($dst + '/luts')
robocopy /E /xc './Lib/site-packages/cv2/data' ($dst + '/cv2/data')
robocopy /E /xc './viewer/luts' ($dst + '/luts')

remove-item './dist/Peak' -r -Force -Confirm:$False
remove-item './dist/Capture' -r -Force -Confirm:$False
2 changes: 1 addition & 1 deletion capture/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def __init__(self, *args, **kwargs):
tree.resizeColumnToContents(0)
tree.sortByColumn(1, Qt.AscendingOrder)

group = ExpandableGroup(tree, self.historyGroupBox)
group = ExpandableGroup(tree, parent=self.historyGroupBox)
group.collapseExpand.connect(self.onGroupCollapse)
group.iconButton.setIconSize(QSize(18,18))
ExpandableGroup.BAR_HEIGHT -= 1
Expand Down
1 change: 1 addition & 0 deletions capture/history_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class TypesIndicator(IconIndicator):
Video = ':type/video.png'
Animated = ':type/gif.png'


class HistoryTreeFilter(QSortFilterProxyModel):
def __init__(self, filter_id):
super(HistoryTreeFilter, self).__init__()
Expand Down
File renamed without changes.
24 changes: 24 additions & 0 deletions docs/2022.4_release_notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Release Notes

### **Big** New Features :
- Visually group Assets by Type, Classification or Category.
- Group counts update with item filtering
- Maya Arnold drop-in support for lights.

### **Major** Changes :
- Relic Plugin Panel modern overhaul.

### **Small** New Features :
- Relic will auto-launch itself if any action attempts to communicate and an existing session is not detected.

#### **Minor** Changes & Tweaks :
- Asset plugin download progression is calculated by size instead of number of files.
- Indicators used for assets plugin status.
- Downloads and transfers are faster and more reliable.

### **Bug Fixes** :
1. Fixed critical bug in all plugins when downloading multiple interdependent assets.
2. Upstream items display with checkboxes unintentionally after entering link view.


### Development notes :
128 changes: 87 additions & 41 deletions library/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,34 @@
QTimer, Slot, QSize)
from PySide6.QtGui import QIcon, QPixmap, Qt, QImage
from PySide6.QtWidgets import (QApplication, QMainWindow, QSizePolicy,
QSystemTrayIcon, QToolButton, QWidget)
QSystemTrayIcon, QToolButton, QWidget, QLabel)

from qtshared6.utils import polymorphicItem
from relic.local import Relational
from relic.local import Relational, Grouping
from relic.scheme import Table, AssetType, Classification
from relic.qt.widgets import DockTitle
from relic.qt.widgets import DockTitle, GroupView, GroupBox
from relic.qt.delegates import BaseItemDelegate, ItemDispalyModes
from relic.qt.util import loadStylesheet
from relic.qt.util import loadStylesheet, polymorphicItem
from relic.qt.expandable_group import ExpandableGroup

from sequence_path.main import SequencePath as Path
from intercom import Server

from library.config import RELIC_PREFS, peakPreview
from library.io.util import LocalThumbnail
from library.objectmodels import (Library, Type, alusers,
from library.objectmodels import (Library, alusers,
attachLinkToAsset, getCategoryConstructor,
relationships, session, subcategory)
# -- Module --
from library.ui.dialog import Ui_RelicMainWindow
from library.widgets import description
from library.widgets import metadata_view, subcategoriesViews, description
from library.widgets.fields import ItemState
from library.widgets.assets_alt import AssetItemModel, AssetListView
from library.widgets.metadata_view import MetadataView
from library.widgets.preference_view import PreferencesDialog, ViewScale
from library.widgets.relationshipView import LinkViewWidget
from library.widgets.subcategoriesViews import CategoryManager
from library.widgets.util import DialogOverlay
from library.io.ingest import DEFAULT_ICONS

class LinkTab(ExpandableGroup):
BASE_HEIGHT = 300

class RelicMainWindow(Ui_RelicMainWindow, QMainWindow):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -68,26 +67,29 @@ def __init__(self, *args, **kwargs):
self.searchDock.setWidget(empty_widget)
#self.resizeDocks([self.searchDock], [0], Qt.Horizontal) # (QTBUG-65592) fixes dock resize
self.library = Library()
self.category_manager = CategoryManager(self)
self.category_manager.onSelection.connect(self.searchLibrary)
self.category_manager.onAssetDrop.connect(self.assetSubcategoryDrop)
self.category_manager.externalFilesDrop.connect(self.externalSubcategoryDrop)
self.category_manager = subcategoriesViews.CategoryManager(self)
self.connect_categories()
self.searchBox.returnPressed.connect(self.searchLibrary)
self.pageSpinBox.valueChanged.connect(self.updateAssetView)
self.buttonGroup.buttonClicked.connect(self.updateAssetView)
self.collectionRadioButton.toggled.connect(self.searchLibrary)

# Creates asset view
self.assets_view = AssetListView(self)
self.assets_view.doubleClicked.connect(self.loadLinkData)

self.asset_item_model = AssetItemModel()
self.assets_view.setModel(self.asset_item_model)
self.assets_view.doubleClicked.connect(self.loadLinkData)
self.assets_view.selectionModel().selectionChanged.connect(self.loadAssetData)
self.assets_view.assetsDeleted.connect(session.updatesubcategorycounts.execute)
self.assets_view.onExecuted.connect(self.open_file)

self.centralwidget.layout().insertWidget(2, self.assets_view)

self.noSearchResultsPage.hide()
self.links_view = LinkViewWidget(self)
self.metadata_view = MetadataView(self)
link_model = AssetItemModel(self)
self.links_view = GroupView(link_model, AssetListView, LinkTab, self)
self.links_view.onGroupsCreated.connect(self.installLinkViewSlots)
self.metadata_view = metadata_view.MetadataView(self)
self.metadata_view.openDescription.connect(self.loadCurrentAssetsDescription)
self.metadata_view.fieldChanged.connect(self.updateAssetFromField)
self.attributesLayout.addWidget(self.metadata_view)
Expand All @@ -109,10 +111,6 @@ def __init__(self, *args, **kwargs):
self.attrExpandButton.toggled.connect(self.attributeDock.widget().setVisible)
self.categoryExpandButton.toggled.connect(self.categoryDock.widget().setVisible)

for view in self.links_view.all_views + [self.assets_view]:
view.selectionModel().selectionChanged.connect(self.loadAssetData)
view.assetsDeleted.connect(session.updatesubcategorycounts.execute)
view.onExecuted.connect(self.open_file)

self.description_window.text_browser.linkToDescription.connect(self.assets_view.clipboardCopy)
self.description_window.text_browser.assetClicked.connect(self.browseTo)
Expand Down Expand Up @@ -141,12 +139,22 @@ def __init__(self, *args, **kwargs):
session.createuser.callback.connect(self.onRelationCreate)
self.selected_assets_by_link = {} # Used for dependency linking
self.selected_assets = [] # Used for dependency linking

self.startup_callbacks = {} # upon completion of connection and subcategories.
self.asset_startup_path = None
self._ingester = None
self.preferences_dialog = None
self.block_search = False

@Slot(list)
def installLinkViewSlots(self, views):
for view in views:
view.selectionModel().selectionChanged.connect(self.loadAssetData)
view.onLinkRemove.connect(self.unlinkAsset)
# on all views
view.doubleClicked.connect(self.loadLinkData)
view.assetsDeleted.connect(session.updatesubcategorycounts.execute)
view.onExecuted.connect(self.open_file)

@Slot(QModelIndex)
def open_file(self, index):
asset = index.data(Qt.UserRole)
Expand Down Expand Up @@ -192,6 +200,12 @@ def setupDockTitles(self):

category_title.filter_line.textChanged.connect(self.category_manager.filterAll)
links_title.filter_line.textChanged.connect(self.links_view.filterAll)
links_title.addSeparator()
link_grouping = GroupBox(Grouping, self.links_view)
link_grouping.setCurrentIndex(0)
link_layout = links_title.subframe.layout()
link_layout.addWidget(QLabel('Group :'))
link_layout.addWidget(link_grouping)

@staticmethod
def detachLinkedAsset(primary, relation):
Expand Down Expand Up @@ -241,13 +255,13 @@ def updateAssetFromField(self, name, value):

@Slot(bytes)
def setVideo(self, data):
views = [view for view in self.links_view.all_views]
views = [view for view in self.links_view.views]
views.insert(0, self.assets_view)

for view in views:
if not view.lastIndex or not view.lastIndex.isValid():
continue
asset = view.lastIndex.data(polymorphicItem.Object)
asset = view.lastIndex.data(Qt.UserRole)
if asset:
on_complete = partial(setattr, asset, 'video')
worker = LocalThumbnail(data, on_complete)
Expand Down Expand Up @@ -279,18 +293,18 @@ def assetSubcategoryDrop(self, new_subcategory):
destination
"""
tree = self.sender()
assets = [index.data(polymorphicItem.Object) for index in self.assets_view.selectedIndexes()]
new = new_subcategory.data(polymorphicItem.Object)
assets = [index.data(Qt.UserRole) for index in self.assets_view.selectedIndexes()]
new = new_subcategory.data(Qt.UserRole)
data = defaultdict(list)
relations = []
for asset in assets:
old = asset.subcategory.data(polymorphicItem.Object)
old = asset.subcategory.data(Qt.UserRole)
if old.id == new.id or old.category != new.category:
# Don't link subcategories to themselves or cross categorize.
continue
subcategory_relation = relationships(
link=asset.links,
category_map=3
category_map=Table.subcategory.index
)
data[asset.categoryName].append(asset.export)
data['old_subcategories'].append(old.export)
Expand Down Expand Up @@ -337,6 +351,9 @@ def onCategories(self, data):
except RuntimeError:pass
#self.tray.showMessage('Connected', 'Relic is now running and connected.', self.app_icon, 2)

[getattr(self, key)(arg) for key, arg in self.startup_callbacks.items()]
self.startup_callbacks = {}

@Slot(dict)
def onUserCreate(self, data):
for category_name, assets in data.items():
Expand Down Expand Up @@ -418,9 +435,24 @@ def showPreferences(self):
self.preferences_dialog = PreferencesDialog()
DialogOverlay(self, self.preferences_dialog, modal=True)

def connect_categories(self):
self.category_manager.onSelection.connect(self.searchLibrary)
self.category_manager.onAssetDrop.connect(self.assetSubcategoryDrop)
self.category_manager.externalFilesDrop.connect(self.externalSubcategoryDrop)

def disconnect_categories(self):
self.category_manager.onSelection.disconnect(self.searchLibrary)
self.category_manager.onAssetDrop.disconnect(self.assetSubcategoryDrop)
self.category_manager.externalFilesDrop.disconnect(self.externalSubcategoryDrop)

@Slot()
def beginIngest(self):
self.scaleView(1)
if not self.category_manager.all_categories:
return
# make compact temporarily
BaseItemDelegate.VIEW_MODE = ItemDispalyModes.COMPACT
self.asset_item_model.endResetModel()
self.links_view.model.endResetModel()
self.assets_view.hide()
self.attributeDock.hide()
self.ingest_form.setCategoryView(self.categoryDock, self.categoryLayout)
Expand All @@ -429,9 +461,14 @@ def beginIngest(self):

@Slot()
def externalPluginCommand(self, asset_data):
# Delay this function call till after the categories are retrieved.
if not self.category_manager.all_categories:
self.startup_callbacks['externalPluginCommand'] = asset_data
return

self.toggleVisibility(QSystemTrayIcon.ActivationReason.Trigger, force=True)
self.beginIngest()
delay_call = lambda : self._ingester.collectAssetsFromPlugin(asset_data)
delay_call = lambda : self.ingest_form.collectAssetsFromPlugin(asset_data)
self.delay_call = QTimer.singleShot(0.5*1000, delay_call)

@Slot()
Expand Down Expand Up @@ -492,6 +529,7 @@ def onIngestClosed(self, dock):
self.attributeDock.show()
self.addDockWidget(Qt.LeftDockWidgetArea, dock)
self.category_manager.blockSignals(False)
self.scaleView(int(ViewScale[RELIC_PREFS.view_scale]))

def hideDocks(self, state):
self.categoryExpandButton.setChecked(state)
Expand Down Expand Up @@ -617,10 +655,7 @@ def onFilterResults(self, filter_results):
if load_icons and asset.classification == Classification.DOCUMENT.value:
asset.icon = DEFAULT_ICONS.document
elif load_icons:
on_complete = partial(setattr, asset, 'icon')
icon_path = asset.network_path.suffixed('_icon', '.jpg')
worker = LocalThumbnail(icon_path, on_complete, item)
self.pool.start(worker)
self.loadIcon(asset)
item_model.appendRow(item)
link_ids.append(asset.links)
session.retrievelinks.execute(link_ids)
Expand Down Expand Up @@ -667,7 +702,7 @@ def loadAssetData(self, selection):
# Links / Dependencies have already been attached to the assets.
view = sender.parent()
indices = view.selectedIndexes()
assets = [index.data(polymorphicItem.Object) for index in indices]
assets = [index.data(Qt.UserRole) for index in indices]
self.selected_assets = assets
self.selected_assets_by_link = {}
if not assets:
Expand Down Expand Up @@ -747,13 +782,13 @@ def onLinksResults(self, data):
_map = maps[i]
_id = ids[i]
for link_item in linked_assets:
link_item_asset = link_item.data(polymorphicItem.Object)
link_item_asset = link_item.data(Qt.UserRole)
if link_item_asset.id == _id and link_item_asset.relationMap == _map:
link_map.append(link_item)
main_view = self.assets_view
model = main_view.model if main_view.isVisible() else self.links_view.model
for i in range(model.rowCount()):
asset = model.index(i, 0).data(polymorphicItem.Object)
asset = model.index(i, 0).data(Qt.UserRole)
for index, link in enumerate(links):
if asset.links == link:
try:
Expand All @@ -767,7 +802,7 @@ def onLinksResults(self, data):
return
# copy upstream icons on empty collections
for asset in assets:
if asset.type == Type.COLLECTION and asset.upstream:
if asset.type == AssetType.COLLECTION and asset.upstream:
icon_path = asset.network_path.suffixed('_icon', '.jpg')
if not icon_path.exists():
copyRelatedIcon(asset)
Expand All @@ -784,7 +819,7 @@ def unlinkAsset(self, index):
"""
# TODO: This needs to be refactored to not rely on the asset selection.
#primary_indices = self.assets_view.selectedIndexes()
asset = index.data(polymorphicItem.Object)
asset = index.data(Qt.UserRole)
for downstream in asset.downstream:
asset.unlinkTo(downstream)
downstream.dependencies -= 1
Expand Down Expand Up @@ -824,12 +859,23 @@ def loadLinkData(self, index):
self.assets_view.selectionModel().select(index, QItemSelectionModel.Deselect)

self.links_view.updateGroups(linked_assets, clear=True)
load_icon = self.loadIcon
[load_icon(x.data(Qt.UserRole)) for x in linked_assets]
if not self.linksDock.isVisible():
self.linksDock.show()

# Fetch the upstream asset sub-links
session.retrievelinks.execute(link_ids)

def loadIcon(self, asset):
on_complete = partial(setattr, asset, 'icon')
icon_path = asset.network_path.suffixed('_icon', '.jpg')
worker = LocalThumbnail(icon_path, on_complete)
self.pool.start(worker)
#asset_obj.fetchIcon()
#asset.setTextAlignment(Qt.AlignLeft | Qt.AlignTop)
#asset.setCheckable(True)

@Slot(QSystemTrayIcon.ActivationReason)
def toggleVisibility(self, reason, force=False):
if reason == QSystemTrayIcon.ActivationReason.Trigger:
Expand All @@ -853,7 +899,7 @@ def browseDocumentation(self):
def copyRelatedIcon(asset):
# TODO: Re-assess if this is really needed.
for item in asset.upstream:
linked_asset = item.data(polymorphicItem.Object)
linked_asset = item.data(Qt.UserRole)
related_icon = linked_asset.network_path.suffixed('_icon', '.jpg')
asset_icon = asset.network_path.suffixed('_icon', '.jpg')
related_icon.copyTo(asset_icon)
Expand Down
Loading

0 comments on commit b6c077f

Please sign in to comment.