Merge branch 'feature/context_dialog_selection' into feature/royalrender-integration

This commit is contained in:
Ondrej Samohel 2021-11-02 14:46:35 +01:00
commit 8a2806beef
No known key found for this signature in database
GPG key ID: 02376E18990A97C6
9 changed files with 809 additions and 146 deletions

View file

@ -282,6 +282,34 @@ def projectmanager():
PypeCommands().launch_project_manager()
@main.command()
@click.argument("output_path")
@click.option("--project", help="Define project context")
@click.option("--asset", help="Define asset in project (project must be set)")
@click.option(
"--strict",
is_flag=True,
help="Full context must be set otherwise dialog can't be closed."
)
def contextselection(
output_path,
project,
asset,
strict
):
"""Show Qt dialog to select context.
Context is project name, asset name and task name. The result is stored
into json file which path is passed in first argument.
"""
PypeCommands.contextselection(
output_path,
project,
asset,
strict
)
@main.command(
context_settings=dict(
ignore_unknown_options=True,

View file

@ -310,6 +310,12 @@ class PypeCommands:
project_manager.main()
@staticmethod
def contextselection(output_path, project_name, asset_name, strict):
from openpype.tools.context_dialog import main
main(output_path, project_name, asset_name, strict)
def texture_copy(self, project, asset, path):
pass

View file

@ -0,0 +1,10 @@
from .window import (
ContextDialog,
main
)
__all__ = (
"ContextDialog",
"main"
)

View file

@ -0,0 +1,423 @@
import os
import json
from Qt import QtWidgets, QtCore, QtGui
from avalon.api import AvalonMongoDB
from openpype import style
from openpype.tools.utils.lib import center_window
from openpype.tools.utils.widgets import AssetWidget
from openpype.tools.utils.constants import (
TASK_NAME_ROLE,
PROJECT_NAME_ROLE
)
from openpype.tools.utils.models import (
ProjectModel,
ProjectSortFilterProxy,
TasksModel,
TasksProxyModel
)
class ContextDialog(QtWidgets.QDialog):
"""Dialog to select a context.
Context has 3 parts:
- Project
- Aseet
- Task
It is possible to predefine project and asset. In that case their widgets
will have passed preselected values and will be disabled.
"""
def __init__(self, parent=None):
super(ContextDialog, self).__init__(parent)
self.setWindowTitle("Select Context")
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
# Enable minimize and maximize for app
window_flags = QtCore.Qt.Window
if not parent:
window_flags |= QtCore.Qt.WindowStaysOnTopHint
self.setWindowFlags(window_flags)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
dbcon = AvalonMongoDB()
# UI initialization
main_splitter = QtWidgets.QSplitter(self)
# Left side widget containt project combobox and asset widget
left_side_widget = QtWidgets.QWidget(main_splitter)
project_combobox = QtWidgets.QComboBox(left_side_widget)
# Styled delegate to propagate stylessheet
project_delegate = QtWidgets.QStyledItemDelegate(project_combobox)
project_combobox.setItemDelegate(project_delegate)
# Project model with only active projects without default item
project_model = ProjectModel(
dbcon,
only_active=True,
add_default_project=False
)
# Sorting proxy model
project_proxy = ProjectSortFilterProxy()
project_proxy.setSourceModel(project_model)
project_combobox.setModel(project_proxy)
# Assets widget
assets_widget = AssetWidget(
dbcon, multiselection=False, parent=left_side_widget
)
left_side_layout = QtWidgets.QVBoxLayout(left_side_widget)
left_side_layout.setContentsMargins(0, 0, 0, 0)
left_side_layout.addWidget(project_combobox)
left_side_layout.addWidget(assets_widget)
# Right side of window contains only tasks
task_view = QtWidgets.QListView(main_splitter)
task_model = TasksModel(dbcon)
task_proxy = TasksProxyModel()
task_proxy.setSourceModel(task_model)
task_view.setModel(task_proxy)
# Add widgets to main splitter
main_splitter.addWidget(left_side_widget)
main_splitter.addWidget(task_view)
# Set stretch of both sides
main_splitter.setStretchFactor(0, 7)
main_splitter.setStretchFactor(1, 3)
# Add confimation button to bottom right
ok_btn = QtWidgets.QPushButton("OK", self)
buttons_layout = QtWidgets.QHBoxLayout()
buttons_layout.setContentsMargins(0, 0, 0, 0)
buttons_layout.addStretch(1)
buttons_layout.addWidget(ok_btn, 0)
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(main_splitter, 1)
main_layout.addLayout(buttons_layout, 0)
# Timer which will trigger asset refresh
# - this is needed because asset widget triggers
# finished refresh before hides spin box so we need to trigger
# refreshing in small offset if we want re-refresh asset widget
assets_timer = QtCore.QTimer()
assets_timer.setInterval(50)
assets_timer.setSingleShot(True)
assets_timer.timeout.connect(self._on_asset_refresh_timer)
project_combobox.currentIndexChanged.connect(
self._on_project_combo_change
)
assets_widget.selection_changed.connect(self._on_asset_change)
assets_widget.refresh_triggered.connect(self._on_asset_refresh_trigger)
assets_widget.refreshed.connect(self._on_asset_widget_refresh_finished)
task_view.selectionModel().selectionChanged.connect(
self._on_task_change
)
ok_btn.clicked.connect(self._on_ok_click)
self._dbcon = dbcon
self._project_combobox = project_combobox
self._project_model = project_model
self._project_proxy = project_proxy
self._project_delegate = project_delegate
self._assets_widget = assets_widget
self._task_view = task_view
self._task_model = task_model
self._task_proxy = task_proxy
self._ok_btn = ok_btn
self._strict = False
# Values set by `set_context` method
self._set_context_project = None
self._set_context_asset = None
# Requirements for asset widget refresh
self._assets_timer = assets_timer
self._rerefresh_assets = True
self._assets_refreshing = False
# Set stylehseet and resize window on first show
self._first_show = True
# Helper attributes for handling of refresh
self._ignore_value_changes = False
self._refresh_on_next_show = True
# Output of dialog
self._context_to_store = {
"project": None,
"asset": None,
"task": None
}
def closeEvent(self, event):
"""Ignore close event if is in strict state and context is not done."""
if self._strict and not self._ok_btn.isEnabled():
event.ignore()
return
if self._strict:
self._confirm_values()
super(ContextDialog, self).closeEvent(event)
def set_strict(self, strict):
"""Change strictness of dialog."""
self._strict = strict
self._validate_strict()
def _set_refresh_on_next_show(self):
"""Refresh will be called on next showEvent.
If window is already visible then just execute refresh.
"""
self._refresh_on_next_show = True
if self.isVisible():
self.refresh()
def _refresh_assets(self):
"""Trigger refreshing of asset widget.
This will set mart to rerefresh asset when current refreshing is done
or do it immidietely if asset widget is not refreshing at the time.
"""
if self._assets_refreshing:
self._rerefresh_assets = True
else:
self._on_asset_refresh_timer()
def showEvent(self, event):
"""Override show event to do some callbacks."""
super(ContextDialog, self).showEvent(event)
if self._first_show:
self._first_show = False
# Set stylesheet and resize
self.setStyleSheet(style.load_stylesheet())
self.resize(600, 700)
center_window(self)
if self._refresh_on_next_show:
self.refresh()
def refresh(self):
"""Refresh all widget one by one.
When asset refresh is triggered we have to wait when is done so
this method continues with `_on_asset_widget_refresh_finished`.
"""
# Change state of refreshing (no matter how refresh was called)
self._refresh_on_next_show = False
# Ignore changes of combobox and asset widget
self._ignore_value_changes = True
# Get current project name to be able set it afterwards
select_project_name = self._dbcon.Session.get("AVALON_PROJECT")
# Trigger project refresh
self._project_model.refresh()
# Sort projects
self._project_proxy.sort(0)
# Disable combobox if project was passed to `set_context`
if self._set_context_project:
select_project_name = self._set_context_project
self._project_combobox.setEnabled(False)
else:
# Find new project to select
self._project_combobox.setEnabled(True)
if (
select_project_name is None
and self._project_proxy.rowCount() > 0
):
index = self._project_proxy.index(0, 0)
select_project_name = index.data(PROJECT_NAME_ROLE)
self._ignore_value_changes = False
idx = self._project_combobox.findText(select_project_name)
if idx >= 0:
self._project_combobox.setCurrentIndex(idx)
self._dbcon.Session["AVALON_PROJECT"] = (
self._project_combobox.currentText()
)
# Trigger asset refresh
self._refresh_assets()
def _on_asset_refresh_timer(self):
"""This is only way how to trigger refresh asset widget.
Use `_refresh_assets` method to refresh asset widget.
"""
self._assets_widget.refresh()
def _on_asset_widget_refresh_finished(self):
"""Catch when asset widget finished refreshing."""
# If should refresh again then skip all other callbacks and trigger
# assets timer directly.
self._assets_refreshing = False
if self._rerefresh_assets:
self._rerefresh_assets = False
self._assets_timer.start()
return
self._ignore_value_changes = True
if self._set_context_asset:
self._dbcon.Session["AVALON_ASSET"] = self._set_context_asset
self._assets_widget.setEnabled(False)
self._assets_widget.select_assets(self._set_context_asset)
self._set_asset_to_task_model()
else:
self._assets_widget.setEnabled(True)
self._assets_widget.set_current_asset_btn_visibility(False)
# Refresh tasks
self._task_model.refresh()
# Sort tasks
self._task_proxy.sort(0, QtCore.Qt.AscendingOrder)
self._ignore_value_changes = False
self._validate_strict()
def _on_project_combo_change(self):
if self._ignore_value_changes:
return
project_name = self._project_combobox.currentText()
if self._dbcon.Session.get("AVALON_PROJECT") == project_name:
return
self._dbcon.Session["AVALON_PROJECT"] = project_name
self._refresh_assets()
self._validate_strict()
def _on_asset_refresh_trigger(self):
self._assets_refreshing = True
self._on_asset_change()
def _on_asset_change(self):
"""Selected assets have changed"""
if self._ignore_value_changes:
return
self._set_asset_to_task_model()
def _on_task_change(self):
self._validate_strict()
def _set_asset_to_task_model(self):
# filter None docs they are silo
asset_docs = self._assets_widget.get_selected_assets()
asset_ids = [asset_doc["_id"] for asset_doc in asset_docs]
asset_id = None
if asset_ids:
asset_id = asset_ids[0]
self._task_model.set_asset_id(asset_id)
self._task_proxy.sort(0, QtCore.Qt.AscendingOrder)
def _confirm_values(self):
"""Store values to output."""
self._context_to_store["project"] = self.get_selected_project()
self._context_to_store["asset"] = self.get_selected_asset()
self._context_to_store["task"] = self.get_selected_task()
def _on_ok_click(self):
# Store values to output
self._confirm_values()
# Close dialog
self.accept()
def get_selected_project(self):
"""Get selected project."""
return self._project_combobox.currentText()
def get_selected_asset(self):
"""Currently selected asset in asset widget."""
asset_name = None
for asset_doc in self._assets_widget.get_selected_assets():
asset_name = asset_doc["name"]
break
return asset_name
def get_selected_task(self):
"""Currently selected task."""
task_name = None
index = self._task_view.selectionModel().currentIndex()
if index.isValid():
task_name = index.data(TASK_NAME_ROLE)
return task_name
def _validate_strict(self):
if not self._strict:
if not self._ok_btn.isEnabled():
self._ok_btn.setEnabled(True)
return
enabled = True
if not self._set_context_project and not self.get_selected_project():
enabled = False
elif not self._set_context_asset and not self.get_selected_asset():
enabled = False
elif not self.get_selected_task():
enabled = False
self._ok_btn.setEnabled(enabled)
def set_context(self, project_name=None, asset_name=None):
"""Set context which will be used and locked in dialog."""
if project_name is None:
asset_name = None
self._set_context_project = project_name
self._set_context_asset = asset_name
self._context_to_store["project"] = project_name
self._context_to_store["asset"] = asset_name
self._set_refresh_on_next_show()
def get_context(self):
"""Result of dialog."""
return self._context_to_store
def main(
path_to_store,
project_name=None,
asset_name=None,
strict=True
):
# Run Qt application
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication([])
window = ContextDialog()
window.set_strict(strict)
window.set_context(project_name, asset_name)
window.show()
app.exec_()
# Get result from window
data = window.get_context()
# Make sure json filepath directory exists
file_dir = os.path.dirname(path_to_store)
if not os.path.exists(file_dir):
os.makedirs(file_dir)
# Store result into json file
with open(path_to_store, "w") as stream:
json.dump(data, stream)

View file

@ -0,0 +1,10 @@
from Qt import QtCore
DEFAULT_PROJECT_LABEL = "< Default >"
PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 101
PROJECT_IS_ACTIVE_ROLE = QtCore.Qt.UserRole + 102
TASK_NAME_ROLE = QtCore.Qt.UserRole + 301
TASK_TYPE_ROLE = QtCore.Qt.UserRole + 302
TASK_ORDER_ROLE = QtCore.Qt.UserRole + 403

View file

@ -13,6 +13,16 @@ from openpype.api import get_project_settings
from openpype.lib import filter_profiles
def center_window(window):
"""Move window to center of it's screen."""
desktop = QtWidgets.QApplication.desktop()
screen_idx = desktop.screenNumber(window)
screen_geo = desktop.screenGeometry(screen_idx)
geo = window.frameGeometry()
geo.moveCenter(screen_geo.center())
window.move(geo.topLeft())
def format_version(value, hero_version=False):
"""Formats integer to displayable version name"""
label = "v{0:03d}".format(value)

View file

@ -8,6 +8,14 @@ from Qt import QtCore, QtGui
from avalon.vendor import qtawesome
from avalon import style, io
from . import lib
from .constants import (
PROJECT_IS_ACTIVE_ROLE,
PROJECT_NAME_ROLE,
DEFAULT_PROJECT_LABEL,
TASK_ORDER_ROLE,
TASK_TYPE_ROLE,
TASK_NAME_ROLE
)
log = logging.getLogger(__name__)
@ -498,3 +506,311 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel):
return super(
RecursiveSortFilterProxyModel, self
).filterAcceptsRow(row, parent)
class ProjectModel(QtGui.QStandardItemModel):
def __init__(
self, dbcon=None, only_active=True, add_default_project=False,
*args, **kwargs
):
super(ProjectModel, self).__init__(*args, **kwargs)
self.dbcon = dbcon
self._only_active = only_active
self._add_default_project = add_default_project
self._default_item = None
self._items_by_name = {}
# Model was at least once refreshed
# - for `set_dbcon` method
self._refreshed = False
def set_default_project_available(self, available=True):
if available is None:
available = not self._add_default_project
if self._add_default_project == available:
return
self._add_default_project = available
if not available and self._default_item is not None:
root_item = self.invisibleRootItem()
root_item.removeRow(self._default_item.row())
self._default_item = None
def set_only_active(self, only_active=True):
if only_active is None:
only_active = not self._only_active
if self._only_active == only_active:
return
self._only_active = only_active
if self._refreshed:
self.refresh()
def set_dbcon(self, dbcon):
"""Change mongo connection."""
self.dbcon = dbcon
# Trigger refresh if was already refreshed
if self._refreshed:
self.refresh()
def project_name_is_available(self, project_name):
"""Check availability of project name in current items."""
return project_name in self._items_by_name
def refresh(self):
# Change '_refreshed' state
self._refreshed = True
new_items = []
# Add default item to model if should
if self._add_default_project and self._default_item is None:
item = QtGui.QStandardItem(DEFAULT_PROJECT_LABEL)
item.setData(None, PROJECT_NAME_ROLE)
item.setData(True, PROJECT_IS_ACTIVE_ROLE)
new_items.append(item)
self._default_item = item
project_names = set()
if self.dbcon is not None:
for project_doc in self.dbcon.projects(
projection={"name": 1, "data.active": 1},
only_active=self._only_active
):
project_name = project_doc["name"]
project_names.add(project_name)
if project_name in self._items_by_name:
item = self._items_by_name[project_name]
else:
item = QtGui.QStandardItem(project_name)
self._items_by_name[project_name] = item
new_items.append(item)
is_active = project_doc.get("data", {}).get("active", True)
item.setData(project_name, PROJECT_NAME_ROLE)
item.setData(is_active, PROJECT_IS_ACTIVE_ROLE)
if not is_active:
font = item.font()
font.setItalic(True)
item.setFont(font)
root_item = self.invisibleRootItem()
for project_name in tuple(self._items_by_name.keys()):
if project_name not in project_names:
item = self._items_by_name.pop(project_name)
root_item.removeRow(item.row())
if new_items:
root_item.appendRows(new_items)
class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(ProjectSortFilterProxy, self).__init__(*args, **kwargs)
self._filter_enabled = True
def lessThan(self, left_index, right_index):
if left_index.data(PROJECT_NAME_ROLE) is None:
return True
if right_index.data(PROJECT_NAME_ROLE) is None:
return False
left_is_active = left_index.data(PROJECT_IS_ACTIVE_ROLE)
right_is_active = right_index.data(PROJECT_IS_ACTIVE_ROLE)
if right_is_active == left_is_active:
return super(ProjectSortFilterProxy, self).lessThan(
left_index, right_index
)
if left_is_active:
return True
return False
def filterAcceptsRow(self, source_row, source_parent):
index = self.sourceModel().index(source_row, 0, source_parent)
if self._filter_enabled:
result = self._custom_index_filter(index)
if result is not None:
return result
return super(ProjectSortFilterProxy, self).filterAcceptsRow(
source_row, source_parent
)
def _custom_index_filter(self, index):
is_active = bool(index.data(PROJECT_IS_ACTIVE_ROLE))
return is_active
def is_filter_enabled(self):
return self._filter_enabled
def set_filter_enabled(self, value):
self._filter_enabled = value
self.invalidateFilter()
class TasksModel(QtGui.QStandardItemModel):
"""A model listing the tasks combined for a list of assets"""
def __init__(self, dbcon, parent=None):
super(TasksModel, self).__init__(parent=parent)
self.dbcon = dbcon
self._default_icon = qtawesome.icon(
"fa.male",
color=style.colors.default
)
self._no_tasks_icon = qtawesome.icon(
"fa.exclamation-circle",
color=style.colors.mid
)
self._cached_icons = {}
self._project_task_types = {}
self._last_asset_id = None
self.refresh()
def refresh(self):
if self.dbcon.Session.get("AVALON_PROJECT"):
self._refresh_task_types()
self.set_asset_id(self._last_asset_id)
else:
self.clear()
def _refresh_task_types(self):
# Get the project configured icons from database
project = self.dbcon.find_one(
{"type": "project"},
{"config.tasks"}
)
tasks = project["config"].get("tasks") or {}
self._project_task_types = tasks
def _try_get_awesome_icon(self, icon_name):
icon = None
if icon_name:
try:
icon = qtawesome.icon(
"fa.{}".format(icon_name),
color=style.colors.default
)
except Exception:
pass
return icon
def headerData(self, section, orientation, role):
# Show nice labels in the header
if (
role == QtCore.Qt.DisplayRole
and orientation == QtCore.Qt.Horizontal
):
if section == 0:
return "Tasks"
return super(TasksModel, self).headerData(section, orientation, role)
def _get_icon(self, task_icon, task_type_icon):
if task_icon in self._cached_icons:
return self._cached_icons[task_icon]
icon = self._try_get_awesome_icon(task_icon)
if icon is not None:
self._cached_icons[task_icon] = icon
return icon
if task_type_icon in self._cached_icons:
icon = self._cached_icons[task_type_icon]
self._cached_icons[task_icon] = icon
return icon
icon = self._try_get_awesome_icon(task_type_icon)
if icon is None:
icon = self._default_icon
self._cached_icons[task_icon] = icon
self._cached_icons[task_type_icon] = icon
return icon
def set_asset_id(self, asset_id):
asset_doc = None
if asset_id:
asset_doc = self.dbcon.find_one(
{"_id": asset_id},
{"data.tasks": True}
)
self.set_asset(asset_doc)
def set_asset(self, asset_doc):
"""Set assets to track by their database id
Arguments:
asset_doc (dict): Asset document from MongoDB.
"""
self.clear()
if not asset_doc:
self._last_asset_id = None
return
self._last_asset_id = asset_doc["_id"]
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
items = []
for task_name, task_info in asset_tasks.items():
task_icon = task_info.get("icon")
task_type = task_info.get("type")
task_order = task_info.get("order")
task_type_info = self._project_task_types.get(task_type) or {}
task_type_icon = task_type_info.get("icon")
icon = self._get_icon(task_icon, task_type_icon)
label = "{} ({})".format(task_name, task_type or "type N/A")
item = QtGui.QStandardItem(label)
item.setData(task_name, TASK_NAME_ROLE)
item.setData(task_type, TASK_TYPE_ROLE)
item.setData(task_order, TASK_ORDER_ROLE)
item.setData(icon, QtCore.Qt.DecorationRole)
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
items.append(item)
if not items:
item = QtGui.QStandardItem("No task")
item.setData(self._no_tasks_icon, QtCore.Qt.DecorationRole)
item.setFlags(QtCore.Qt.NoItemFlags)
items.append(item)
self.invisibleRootItem().appendRows(items)
class TasksProxyModel(QtCore.QSortFilterProxyModel):
def lessThan(self, x_index, y_index):
x_order = x_index.data(TASK_ORDER_ROLE)
y_order = y_index.data(TASK_ORDER_ROLE)
if x_order is not None and y_order is not None:
if x_order < y_order:
return True
if x_order > y_order:
return False
elif x_order is None and y_order is not None:
return True
elif y_order is None and x_order is not None:
return False
x_name = x_index.data(QtCore.Qt.DisplayRole)
y_name = y_index.data(QtCore.Qt.DisplayRole)
if x_name == y_name:
return True
if x_name == tuple(sorted((x_name, y_name)))[0]:
return True
return False

View file

@ -14,13 +14,15 @@ from avalon.tools import lib as tools_lib
from avalon.tools.widgets import AssetWidget
from avalon.tools.delegates import PrettyTimeDelegate
from .model import (
from openpype.tools.utils.constants import (
TASK_NAME_ROLE,
TASK_TYPE_ROLE,
FilesModel,
TASK_TYPE_ROLE
)
from openpype.tools.utils.models import (
TasksModel,
TasksProxyModel
)
from .model import FilesModel
from .view import FilesView
from openpype.lib import (
@ -359,6 +361,7 @@ class TasksWidget(QtWidgets.QWidget):
self._last_selected_task = current
self._tasks_model.set_asset(asset_doc)
self._tasks_proxy.sort(0, QtCore.Qt.AscendingOrder)
if self._last_selected_task:
self.select_task(self._last_selected_task)

View file

@ -9,10 +9,6 @@ from avalon.tools.models import TreeModel, Item
log = logging.getLogger(__name__)
TASK_NAME_ROLE = QtCore.Qt.UserRole + 1
TASK_TYPE_ROLE = QtCore.Qt.UserRole + 2
TASK_ORDER_ROLE = QtCore.Qt.UserRole + 3
class FilesModel(TreeModel):
"""Model listing files with specified extensions in a root folder"""
@ -155,142 +151,3 @@ class FilesModel(TreeModel):
return "Date modified"
return super(FilesModel, self).headerData(section, orientation, role)
class TasksProxyModel(QtCore.QSortFilterProxyModel):
def lessThan(self, x_index, y_index):
x_order = x_index.data(TASK_ORDER_ROLE)
y_order = y_index.data(TASK_ORDER_ROLE)
if x_order is not None and y_order is not None:
if x_order < y_order:
return True
if x_order > y_order:
return False
elif x_order is None and y_order is not None:
return True
elif y_order is None and x_order is not None:
return False
x_name = x_index.data(QtCore.Qt.DisplayRole)
y_name = y_index.data(QtCore.Qt.DisplayRole)
if x_name == y_name:
return True
if x_name == tuple(sorted((x_name, y_name)))[0]:
return False
return True
class TasksModel(QtGui.QStandardItemModel):
"""A model listing the tasks combined for a list of assets"""
def __init__(self, dbcon, parent=None):
super(TasksModel, self).__init__(parent=parent)
self.dbcon = dbcon
self._default_icon = qtawesome.icon(
"fa.male",
color=style.colors.default
)
self._no_tasks_icon = qtawesome.icon(
"fa.exclamation-circle",
color=style.colors.mid
)
self._cached_icons = {}
self._project_task_types = {}
self._refresh_task_types()
def _refresh_task_types(self):
# Get the project configured icons from database
project = self.dbcon.find_one(
{"type": "project"},
{"config.tasks"}
)
tasks = project["config"].get("tasks") or {}
self._project_task_types = tasks
def _try_get_awesome_icon(self, icon_name):
icon = None
if icon_name:
try:
icon = qtawesome.icon(
"fa.{}".format(icon_name),
color=style.colors.default
)
except Exception:
pass
return icon
def headerData(self, section, orientation, role):
# Show nice labels in the header
if (
role == QtCore.Qt.DisplayRole
and orientation == QtCore.Qt.Horizontal
):
if section == 0:
return "Tasks"
return super(TasksModel, self).headerData(section, orientation, role)
def _get_icon(self, task_icon, task_type_icon):
if task_icon in self._cached_icons:
return self._cached_icons[task_icon]
icon = self._try_get_awesome_icon(task_icon)
if icon is not None:
self._cached_icons[task_icon] = icon
return icon
if task_type_icon in self._cached_icons:
icon = self._cached_icons[task_type_icon]
self._cached_icons[task_icon] = icon
return icon
icon = self._try_get_awesome_icon(task_type_icon)
if icon is None:
icon = self._default_icon
self._cached_icons[task_icon] = icon
self._cached_icons[task_type_icon] = icon
return icon
def set_asset(self, asset_doc):
"""Set assets to track by their database id
Arguments:
asset_doc (dict): Asset document from MongoDB.
"""
self.clear()
if not asset_doc:
return
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
items = []
for task_name, task_info in asset_tasks.items():
task_icon = task_info.get("icon")
task_type = task_info.get("type")
task_order = task_info.get("order")
task_type_info = self._project_task_types.get(task_type) or {}
task_type_icon = task_type_info.get("icon")
icon = self._get_icon(task_icon, task_type_icon)
label = "{} ({})".format(task_name, task_type or "type N/A")
item = QtGui.QStandardItem(label)
item.setData(task_name, TASK_NAME_ROLE)
item.setData(task_type, TASK_TYPE_ROLE)
item.setData(task_order, TASK_ORDER_ROLE)
item.setData(icon, QtCore.Qt.DecorationRole)
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
items.append(item)
if not items:
item = QtGui.QStandardItem("No task")
item.setData(self._no_tasks_icon, QtCore.Qt.DecorationRole)
item.setFlags(QtCore.Qt.NoItemFlags)
items.append(item)
self.invisibleRootItem().appendRows(items)