Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1143 from pypeclub/feature/launcher_animation
Browse files Browse the repository at this point in the history
Animated launch in launcher tool
  • Loading branch information
mkolar authored Mar 17, 2021
2 parents fc1a93f + 34c8a11 commit 05f4b77
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 15 deletions.
12 changes: 12 additions & 0 deletions pype/tools/launcher/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from Qt import QtCore


ACTION_ROLE = QtCore.Qt.UserRole
GROUP_ROLE = QtCore.Qt.UserRole + 1
VARIANT_GROUP_ROLE = QtCore.Qt.UserRole + 2
ACTION_ID_ROLE = QtCore.Qt.UserRole + 3
ANIMATION_START_ROLE = QtCore.Qt.UserRole + 4
ANIMATION_STATE_ROLE = QtCore.Qt.UserRole + 5


ANIMATION_LEN = 10
57 changes: 57 additions & 0 deletions pype/tools/launcher/delegates.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import time
from Qt import QtCore, QtWidgets, QtGui
from .constants import (
ANIMATION_START_ROLE,
ANIMATION_STATE_ROLE
)


class ActionDelegate(QtWidgets.QStyledItemDelegate):
Expand All @@ -9,8 +14,60 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, group_roles, *args, **kwargs):
super(ActionDelegate, self).__init__(*args, **kwargs)
self.group_roles = group_roles
self._anim_start_color = QtGui.QColor(178, 255, 246)
self._anim_end_color = QtGui.QColor(5, 44, 50)

def _draw_animation(self, painter, option, index):
grid_size = option.widget.gridSize()
x_offset = int(
(grid_size.width() / 2)
- (option.rect.width() / 2)
)
item_x = option.rect.x() - x_offset
rect_offset = grid_size.width() / 20
size = grid_size.width() - (rect_offset * 2)
anim_rect = QtCore.QRect(
item_x + rect_offset,
option.rect.y() + rect_offset,
size,
size
)

painter.save()

painter.setBrush(QtCore.Qt.transparent)
painter.setRenderHint(QtGui.QPainter.Antialiasing)

gradient = QtGui.QConicalGradient()
gradient.setCenter(anim_rect.center())
gradient.setColorAt(0, self._anim_start_color)
gradient.setColorAt(1, self._anim_end_color)

time_diff = time.time() - index.data(ANIMATION_START_ROLE)

# Repeat 4 times
part_anim = 2.5
part_time = time_diff % part_anim
offset = (part_time / part_anim) * 360
angle = (offset + 90) % 360

gradient.setAngle(-angle)

pen = QtGui.QPen(QtGui.QBrush(gradient), rect_offset)
pen.setCapStyle(QtCore.Qt.RoundCap)
painter.setPen(pen)
painter.drawArc(
anim_rect,
-16 * (angle + 10),
-16 * offset
)

painter.restore()

def paint(self, painter, option, index):
if index.data(ANIMATION_STATE_ROLE):
self._draw_animation(painter, option, index)

super(ActionDelegate, self).paint(painter, option, index)
is_group = False
for group_role in self.group_roles:
Expand Down
27 changes: 18 additions & 9 deletions pype/tools/launcher/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import uuid
import copy
import logging
import collections

from . import lib
from .constants import (
ACTION_ROLE,
GROUP_ROLE,
VARIANT_GROUP_ROLE,
ACTION_ID_ROLE
)
from .actions import ApplicationAction
from Qt import QtCore, QtGui
from avalon.vendor import qtawesome
Expand Down Expand Up @@ -109,10 +116,6 @@ def headerData(self, section, orientation, role):


class ActionModel(QtGui.QStandardItemModel):
ACTION_ROLE = QtCore.Qt.UserRole
GROUP_ROLE = QtCore.Qt.UserRole + 1
VARIANT_GROUP_ROLE = QtCore.Qt.UserRole + 2

def __init__(self, dbcon, parent=None):
super(ActionModel, self).__init__(parent=parent)
self.dbcon = dbcon
Expand All @@ -123,6 +126,7 @@ def __init__(self, dbcon, parent=None):
self.default_icon = qtawesome.icon("fa.cube", color="white")
# Cache of available actions
self._registered_actions = list()
self.items_by_id = {}

def discover(self):
"""Set up Actions cache. Run this for each new project."""
Expand All @@ -134,6 +138,7 @@ def discover(self):
actions.extend(app_actions)

self._registered_actions = actions
self.items_by_id.clear()

def get_application_actions(self):
actions = []
Expand Down Expand Up @@ -180,6 +185,7 @@ def filter_actions(self):
# Validate actions based on compatibility
self.clear()

self.items_by_id.clear()
self._groups.clear()

actions = self.filter_compatible_actions(self._registered_actions)
Expand Down Expand Up @@ -235,16 +241,16 @@ def filter_actions(self):

item = QtGui.QStandardItem(icon, label)
item.setData(label, QtCore.Qt.ToolTipRole)
item.setData(actions, self.ACTION_ROLE)
item.setData(True, self.VARIANT_GROUP_ROLE)
item.setData(actions, ACTION_ROLE)
item.setData(True, VARIANT_GROUP_ROLE)
items_by_order[order].append(item)

for action in single_actions:
icon = self.get_icon(action)
label = lib.get_action_label(action)
item = QtGui.QStandardItem(icon, label)
item.setData(label, QtCore.Qt.ToolTipRole)
item.setData(action, self.ACTION_ROLE)
item.setData(action, ACTION_ROLE)
items_by_order[action.order].append(item)

for group_name, actions in grouped_actions.items():
Expand All @@ -263,13 +269,16 @@ def filter_actions(self):
icon = self.default_icon

item = QtGui.QStandardItem(icon, group_name)
item.setData(actions, self.ACTION_ROLE)
item.setData(True, self.GROUP_ROLE)
item.setData(actions, ACTION_ROLE)
item.setData(True, GROUP_ROLE)

items_by_order[order].append(item)

for order in sorted(items_by_order.keys()):
for item in items_by_order[order]:
item_id = str(uuid.uuid4())
item.setData(item_id, ACTION_ID_ROLE)
self.items_by_id[item_id] = item
self.appendRow(item)

self.endResetModel()
Expand Down
58 changes: 52 additions & 6 deletions pype/tools/launcher/widgets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import copy
import time
import collections
from Qt import QtWidgets, QtCore, QtGui
from avalon.vendor import qtawesome
Expand All @@ -7,6 +8,15 @@
from . import lib
from .models import TaskModel, ActionModel, ProjectModel
from .flickcharm import FlickCharm
from .constants import (
ACTION_ROLE,
GROUP_ROLE,
VARIANT_GROUP_ROLE,
ACTION_ID_ROLE,
ANIMATION_START_ROLE,
ANIMATION_STATE_ROLE,
ANIMATION_LEN
)


class ProjectBar(QtWidgets.QWidget):
Expand Down Expand Up @@ -105,7 +115,7 @@ def __init__(self, dbcon, parent=None):

# TODO better group delegate
delegate = ActionDelegate(
[model.GROUP_ROLE, model.VARIANT_GROUP_ROLE],
[GROUP_ROLE, VARIANT_GROUP_ROLE],
self
)
view.setItemDelegate(delegate)
Expand All @@ -115,6 +125,13 @@ def __init__(self, dbcon, parent=None):
self.model = model
self.view = view

self._animated_items = set()

animation_timer = QtCore.QTimer()
animation_timer.setInterval(50)
animation_timer.timeout.connect(self._on_animation)
self._animation_timer = animation_timer

# Make view flickable
flick = FlickCharm(parent=view)
flick.activateOn(view)
Expand All @@ -132,18 +149,46 @@ def filter_actions(self):
def set_row_height(self, rows):
self.setMinimumHeight(rows * 75)

def _on_animation(self):
time_now = time.time()
for action_id in tuple(self._animated_items):
item = self.model.items_by_id.get(action_id)
if not item:
self._animated_items.remove(action_id)
continue

start_time = item.data(ANIMATION_START_ROLE)
if (time_now - start_time) > ANIMATION_LEN:
item.setData(0, ANIMATION_STATE_ROLE)
self._animated_items.remove(action_id)

if not self._animated_items:
self._animation_timer.stop()

self.update()

def _start_animation(self, index):
action_id = index.data(ACTION_ID_ROLE)
item = self.model.items_by_id.get(action_id)
if item:
item.setData(time.time(), ANIMATION_START_ROLE)
item.setData(1, ANIMATION_STATE_ROLE)
self._animated_items.add(action_id)
self._animation_timer.start()

def on_clicked(self, index):
if not index.isValid():
if not index or not index.isValid():
return

is_group = index.data(self.model.GROUP_ROLE)
is_variant_group = index.data(self.model.VARIANT_GROUP_ROLE)
is_group = index.data(GROUP_ROLE)
is_variant_group = index.data(VARIANT_GROUP_ROLE)
if not is_group and not is_variant_group:
action = index.data(self.model.ACTION_ROLE)
action = index.data(ACTION_ROLE)
self._start_animation(index)
self.action_clicked.emit(action)
return

actions = index.data(self.model.ACTION_ROLE)
actions = index.data(ACTION_ROLE)

menu = QtWidgets.QMenu(self)
actions_mapping = {}
Expand Down Expand Up @@ -203,6 +248,7 @@ def on_clicked(self, index):
result = menu.exec_(QtGui.QCursor.pos())
if result:
action = actions_mapping[result]
self._start_animation(index)
self.action_clicked.emit(action)


Expand Down

0 comments on commit 05f4b77

Please sign in to comment.