diff --git a/pype/api.py b/pype/api.py index 747ad425f8..5230a41405 100644 --- a/pype/api.py +++ b/pype/api.py @@ -15,7 +15,7 @@ from .action import ( RepairContextAction ) -from app.api import Logger +from pypeapp.api import Logger from . import ( Anatomy, diff --git a/pype/avalon_apps/__init__.py b/pype/avalon_apps/__init__.py new file mode 100644 index 0000000000..845f94a330 --- /dev/null +++ b/pype/avalon_apps/__init__.py @@ -0,0 +1,5 @@ +from .avalon_app import AvalonApps + + +def tray_init(tray_widget, main_widget): + return AvalonApps(main_widget, tray_widget) diff --git a/pype/avalon_apps/avalon_app.py b/pype/avalon_apps/avalon_app.py new file mode 100644 index 0000000000..efad3f3ce0 --- /dev/null +++ b/pype/avalon_apps/avalon_app.py @@ -0,0 +1,63 @@ +import os +import argparse +from Qt import QtGui, QtWidgets +from avalon.tools import libraryloader +from pypeapp.api import Logger +from avalon import io +from launcher import launcher_widget, lib as launcher_lib + + +class AvalonApps: + def __init__(self, main_parent=None, parent=None): + self.log = Logger().get_logger(__name__) + self.main_parent = main_parent + self.parent = parent + self.app_launcher = None + + # Definition of Tray menu + def tray_menu(self, parent_menu=None): + # Actions + if parent_menu is None: + if self.parent is None: + self.log.warning('Parent menu is not set') + return + elif self.parent.hasattr('menu'): + parent_menu = self.parent.menu + else: + self.log.warning('Parent menu is not set') + return + + icon = QtGui.QIcon(launcher_lib.resource("icon", "main.png")) + aShowLauncher = QtWidgets.QAction(icon, "&Launcher", parent_menu) + aLibraryLoader = QtWidgets.QAction("Library", parent_menu) + + aShowLauncher.triggered.connect(self.show_launcher) + aLibraryLoader.triggered.connect(self.show_library_loader) + + parent_menu.addAction(aShowLauncher) + parent_menu.addAction(aLibraryLoader) + + def show_launcher(self): + # if app_launcher don't exist create it/otherwise only show main window + if self.app_launcher is None: + parser = argparse.ArgumentParser() + parser.add_argument("--demo", action="store_true") + parser.add_argument( + "--root", default=os.environ["AVALON_PROJECTS"] + ) + kwargs = parser.parse_args() + + root = kwargs.root + root = os.path.realpath(root) + io.install() + APP_PATH = launcher_lib.resource("qml", "main.qml") + self.app_launcher = launcher_widget.Launcher(root, APP_PATH) + self.app_launcher.window.show() + + def show_library_loader(self): + libraryloader.show( + parent=self.main_parent, + icon=self.parent.icon, + show_projects=True, + show_libraries=True + ) diff --git a/pype/ftrack/__init__.py b/pype/ftrack/__init__.py index bf18979e91..922de28e16 100644 --- a/pype/ftrack/__init__.py +++ b/pype/ftrack/__init__.py @@ -1,2 +1,7 @@ from .lib import * from .ftrack_server import * +from .ftrack_module import FtrackModule + + +def tray_init(tray_widget, main_widget): + return FtrackModule(main_widget, tray_widget) diff --git a/pype/ftrack/actions/action_application_loader.py b/pype/ftrack/actions/action_application_loader.py index 3202c19d40..9cca5ea047 100644 --- a/pype/ftrack/actions/action_application_loader.py +++ b/pype/ftrack/actions/action_application_loader.py @@ -2,10 +2,10 @@ import toml import time from pype.ftrack import AppAction from avalon import lib -from app.api import Logger +from pypeapp.api import Logger from pype import lib as pypelib -log = Logger.getLogger(__name__) +log = Logger().get_logger(__name__) def registerApp(app, session): diff --git a/pype/ftrack/actions/action_asset_delete.py b/pype/ftrack/actions/action_asset_delete.py index c47c8ac4ac..684b3862a8 100644 --- a/pype/ftrack/actions/action_asset_delete.py +++ b/pype/ftrack/actions/action_asset_delete.py @@ -1,7 +1,7 @@ import sys import argparse import logging -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_client_review_sort.py b/pype/ftrack/actions/action_client_review_sort.py index 1e2f37ec74..b06a928007 100644 --- a/pype/ftrack/actions/action_client_review_sort.py +++ b/pype/ftrack/actions/action_client_review_sort.py @@ -2,7 +2,7 @@ import sys import argparse import logging -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_component_open.py b/pype/ftrack/actions/action_component_open.py index 579d8ebe85..c40a04b2fd 100644 --- a/pype/ftrack/actions/action_component_open.py +++ b/pype/ftrack/actions/action_component_open.py @@ -3,7 +3,7 @@ import argparse import logging import subprocess import os -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py index fb57221ccd..09749cf2c5 100644 --- a/pype/ftrack/actions/action_create_cust_attrs.py +++ b/pype/ftrack/actions/action_create_cust_attrs.py @@ -2,10 +2,11 @@ import os import sys import argparse import json -import ftrack_api import arrow import logging +from pype.vendor import ftrack_api from pype.ftrack import BaseAction, get_ca_mongoid +from pypeapp.lib.config import get_presets """ This action creates/updates custom attributes. @@ -122,11 +123,6 @@ class CustomAttributes(BaseAction): def __init__(self, session): super().__init__(session) - templates = os.environ['PYPE_STUDIO_TEMPLATES'] - path_items = [ - templates, 'presets', 'ftrack', 'ftrack_custom_attributes.json' - ] - self.filepath = os.path.sep.join(path_items) self.types = {} self.object_type_ids = {} self.groups = {} @@ -230,22 +226,12 @@ class CustomAttributes(BaseAction): self.process_attribute(data) def custom_attributes_from_file(self, session, event): - try: - with open(self.filepath) as data_file: - json_dict = json.load(data_file) - except Exception as e: - msg = ( - 'Loading "Custom attribute file" Failed.' - ' Please check log for more information' - ) - self.log.warning("{} - {}".format(msg, str(e))) - self.show_message(event, msg) - return + presets = get_presets()['ftrack']['ftrack_custom_attributes'] - for cust_attr_name in json_dict: + for cust_attr_name in presets: try: data = {} - cust_attr = json_dict[cust_attr_name] + cust_attr = presets[cust_attr_name] # Get key, label, type data.update(self.get_required(cust_attr)) # Get hierachical/ entity_type/ object_id diff --git a/pype/ftrack/actions/action_create_folders.py b/pype/ftrack/actions/action_create_folders.py index 7ce5526164..cc4023342d 100644 --- a/pype/ftrack/actions/action_create_folders.py +++ b/pype/ftrack/actions/action_create_folders.py @@ -4,7 +4,7 @@ import argparse import sys import errno -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction import json from pype import api as pype diff --git a/pype/ftrack/actions/action_delete_asset.py b/pype/ftrack/actions/action_delete_asset.py index 7a4c15e9fb..838a77570f 100644 --- a/pype/ftrack/actions/action_delete_asset.py +++ b/pype/ftrack/actions/action_delete_asset.py @@ -2,7 +2,7 @@ import sys import logging from bson.objectid import ObjectId import argparse -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction from avalon.tools.libraryloader.io_nonsingleton import DbConnector diff --git a/pype/ftrack/actions/action_delete_asset_byname.py b/pype/ftrack/actions/action_delete_asset_byname.py index ee6f875ad3..9da60ce763 100644 --- a/pype/ftrack/actions/action_delete_asset_byname.py +++ b/pype/ftrack/actions/action_delete_asset_byname.py @@ -1,7 +1,7 @@ import sys import logging import argparse -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction from avalon.tools.libraryloader.io_nonsingleton import DbConnector diff --git a/pype/ftrack/actions/action_delete_unpublished.py b/pype/ftrack/actions/action_delete_unpublished.py index 018a70b423..377e118ffb 100644 --- a/pype/ftrack/actions/action_delete_unpublished.py +++ b/pype/ftrack/actions/action_delete_unpublished.py @@ -1,7 +1,7 @@ import sys import argparse import logging -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_djvview.py b/pype/ftrack/actions/action_djvview.py index d8e6996db4..4e027b740b 100644 --- a/pype/ftrack/actions/action_djvview.py +++ b/pype/ftrack/actions/action_djvview.py @@ -5,12 +5,12 @@ import json import logging import subprocess from operator import itemgetter -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseHandler from app.api import Logger from pype import lib -log = Logger.getLogger(__name__) +log = Logger().get_logger(__name__) class DJVViewAction(BaseHandler): diff --git a/pype/ftrack/actions/action_job_killer.py b/pype/ftrack/actions/action_job_killer.py index d8d0e81cb1..008b36b1c1 100644 --- a/pype/ftrack/actions/action_job_killer.py +++ b/pype/ftrack/actions/action_job_killer.py @@ -1,10 +1,8 @@ -# :coding: utf-8 -# :copyright: Copyright (c) 2017 ftrack import sys import argparse import logging -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_set_version.py b/pype/ftrack/actions/action_set_version.py index 3954733041..f6e745b3ec 100644 --- a/pype/ftrack/actions/action_set_version.py +++ b/pype/ftrack/actions/action_set_version.py @@ -1,7 +1,7 @@ import sys import argparse import logging -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_sync_to_avalon_local.py b/pype/ftrack/actions/action_sync_to_avalon_local.py index 68c55be652..d3fb140cc4 100644 --- a/pype/ftrack/actions/action_sync_to_avalon_local.py +++ b/pype/ftrack/actions/action_sync_to_avalon_local.py @@ -4,7 +4,7 @@ import argparse import logging import json -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction, lib as ftracklib diff --git a/pype/ftrack/actions/action_test.py b/pype/ftrack/actions/action_test.py index ad97cba487..36adb99074 100644 --- a/pype/ftrack/actions/action_test.py +++ b/pype/ftrack/actions/action_test.py @@ -6,7 +6,7 @@ import os import json import re -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction from avalon import io, inventory, schema diff --git a/pype/ftrack/actions/action_thumbToChildern.py b/pype/ftrack/actions/action_thumbToChildern.py index 2ff6faec48..5b63ec264f 100644 --- a/pype/ftrack/actions/action_thumbToChildern.py +++ b/pype/ftrack/actions/action_thumbToChildern.py @@ -3,7 +3,7 @@ import argparse import logging import json -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/action_thumbToParent.py b/pype/ftrack/actions/action_thumbToParent.py index 98124aca70..eb5623328e 100644 --- a/pype/ftrack/actions/action_thumbToParent.py +++ b/pype/ftrack/actions/action_thumbToParent.py @@ -2,7 +2,7 @@ import sys import argparse import logging import json -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseAction diff --git a/pype/ftrack/actions/event_collect_entities.py b/pype/ftrack/actions/event_collect_entities.py index d5a34b0153..71f2d26ff3 100644 --- a/pype/ftrack/actions/event_collect_entities.py +++ b/pype/ftrack/actions/event_collect_entities.py @@ -1,4 +1,4 @@ -import ftrack_api +from pype.vendor import ftrack_api from pype.ftrack import BaseEvent diff --git a/pype/ftrack/credentials.py b/pype/ftrack/credentials.py index 89353ea984..30d503c534 100644 --- a/pype/ftrack/credentials.py +++ b/pype/ftrack/credentials.py @@ -77,7 +77,6 @@ def _check_credentials(username=None, apiKey=None): session = ftrack_api.Session() session.close() except Exception as e: - print(e) return False return True diff --git a/pype/ftrack/ftrack_module.py b/pype/ftrack/ftrack_module.py new file mode 100644 index 0000000000..127b39d2fc --- /dev/null +++ b/pype/ftrack/ftrack_module.py @@ -0,0 +1,328 @@ +import os +import json +import threading +import time +from Qt import QtCore, QtGui, QtWidgets + +from pype.vendor import ftrack_api +from pypeapp import style +from pype.ftrack import FtrackServer, credentials, login_dialog as login_dialog + +from pype import api as pype + + +log = pype.Logger().get_logger("FtrackModule", "ftrack") + + +class FtrackModule: + def __init__(self, main_parent=None, parent=None): + + self.parent = parent + self.widget_login = login_dialog.Login_Dialog_ui(self) + self.action_server = FtrackServer('action') + self.thread_action_server = None + self.thread_timer = None + + self.bool_logged = False + self.bool_action_server = False + self.bool_timer_event = False + + def show_login_widget(self): + self.widget_login.show() + + def validate(self): + validation = False + cred = credentials._get_credentials() + try: + if 'username' in cred and 'apiKey' in cred: + validation = credentials._check_credentials( + cred['username'], + cred['apiKey'] + ) + if validation is False: + self.show_login_widget() + else: + self.show_login_widget() + + except Exception as e: + log.error("We are unable to connect to Ftrack: {0}".format(e)) + + validation = credentials._check_credentials() + if validation is True: + log.info("Connected to Ftrack successfully") + self.loginChange() + else: + log.warning("Please sign in to Ftrack") + self.bool_logged = False + self.set_menu_visibility() + + return validation + + # Necessary - login_dialog works with this method after logging in + def loginChange(self): + self.bool_logged = True + self.set_menu_visibility() + self.start_action_server() + + def logout(self): + credentials._clear_credentials() + self.stop_action_server() + + log.info("Logged out of Ftrack") + self.bool_logged = False + self.set_menu_visibility() + + # Actions part + def start_action_server(self): + if self.thread_action_server is None: + self.thread_action_server = threading.Thread( + target=self.set_action_server + ) + self.thread_action_server.daemon = True + self.thread_action_server.start() + + log.info("Ftrack action server launched") + self.bool_action_server = True + self.set_menu_visibility() + + def set_action_server(self): + try: + self.action_server.run_server() + except Exception: + msg = 'Ftrack Action server crashed! Please try to start again.' + log.error(msg) + # TODO show message to user + self.bool_action_server = False + self.set_menu_visibility() + + def reset_action_server(self): + self.stop_action_server() + self.start_action_server() + + def stop_action_server(self): + try: + self.action_server.stop_session() + if self.thread_action_server is not None: + self.thread_action_server.join() + self.thread_action_server = None + + log.info("Ftrack action server stopped") + self.bool_action_server = False + self.set_menu_visibility() + except Exception as e: + log.error("During Killing action server: {0}".format(e)) + + # Definition of Tray menu + def tray_menu(self, parent_menu): + # Menu for Tray App + self.menu = QtWidgets.QMenu('Ftrack', parent_menu) + self.menu.setProperty('submenu', 'on') + + # Actions - server + self.smActionS = self.menu.addMenu("Action server") + + self.aRunActionS = QtWidgets.QAction( + "Run action server", self.smActionS + ) + self.aResetActionS = QtWidgets.QAction( + "Reset action server", self.smActionS + ) + self.aStopActionS = QtWidgets.QAction( + "Stop action server", self.smActionS + ) + + self.aRunActionS.triggered.connect(self.start_action_server) + self.aResetActionS.triggered.connect(self.reset_action_server) + self.aStopActionS.triggered.connect(self.stop_action_server) + + self.smActionS.addAction(self.aRunActionS) + self.smActionS.addAction(self.aResetActionS) + self.smActionS.addAction(self.aStopActionS) + + # Actions - basic + self.aLogin = QtWidgets.QAction("Login", self.menu) + self.aLogin.triggered.connect(self.validate) + self.aLogout = QtWidgets.QAction("Logout", self.menu) + self.aLogout.triggered.connect(self.logout) + + self.menu.addAction(self.aLogin) + self.menu.addAction(self.aLogout) + + self.bool_logged = False + self.set_menu_visibility() + + parent_menu.addMenu(self.menu) + + self.validate() + + # Definition of visibility of each menu actions + def set_menu_visibility(self): + + self.smActionS.menuAction().setVisible(self.bool_logged) + self.aLogin.setVisible(not self.bool_logged) + self.aLogout.setVisible(self.bool_logged) + + if self.bool_logged is False: + if self.bool_timer_event is True: + self.stop_timer_thread() + return + + self.aRunActionS.setVisible(not self.bool_action_server) + self.aResetActionS.setVisible(self.bool_action_server) + self.aStopActionS.setVisible(self.bool_action_server) + + if self.bool_timer_event is False: + self.start_timer_thread() + + def start_timer_thread(self): + try: + if self.thread_timer is None: + self.thread_timer = FtrackEventsThread(self) + self.bool_timer_event = True + self.thread_timer.signal_timer_started.connect( + self.timer_started + ) + self.thread_timer.signal_timer_stopped.connect( + self.timer_stopped + ) + self.thread_timer.start() + except Exception: + pass + + def stop_timer_thread(self): + try: + if self.thread_timer is not None: + self.thread_timer.terminate() + self.thread_timer.wait() + self.thread_timer = None + + except Exception as e: + log.error("During Killing Timer event server: {0}".format(e)) + + def process_modules(self, modules): + if 'TimersManager' in modules: + self.timer_manager = modules['TimersManager'] + self.timer_manager.add_module(self) + + def start_timer_manager(self, data): + if self.thread_timer is not None: + self.thread_timer.ftrack_start_timer(data) + + def stop_timer_manager(self): + if self.thread_timer is not None: + self.thread_timer.ftrack_stop_timer() + + def timer_started(self, data): + if hasattr(self, 'timer_manager'): + self.timer_manager.start_timers(data) + + def timer_stopped(self): + if hasattr(self, 'timer_manager'): + self.timer_manager.stop_timers() + + +class FtrackEventsThread(QtCore.QThread): + # Senders + signal_timer_started = QtCore.Signal(object) + signal_timer_stopped = QtCore.Signal() + + def __init__(self, parent): + super(FtrackEventsThread, self).__init__() + cred = credentials._get_credentials() + self.username = cred['username'] + self.user = None + self.last_task = None + + def run(self): + self.timer_session = ftrack_api.Session(auto_connect_event_hub=True) + self.timer_session.event_hub.subscribe( + 'topic=ftrack.update and source.user.username={}'.format( + self.username + ), + self.event_handler) + + user_query = 'User where username is "{}"'.format(self.username) + self.user = self.timer_session.query(user_query).one() + + timer_query = 'Timer where user.username is "{}"'.format(self.username) + timer = self.timer_session.query(timer_query).first() + if timer is not None: + self.last_task = timer['context'] + self.signal_timer_started.emit( + self.get_data_from_task(self.last_task) + ) + + self.timer_session.event_hub.wait() + + def get_data_from_task(self, task_entity): + data = {} + data['task_name'] = task_entity['name'] + data['task_type'] = task_entity['type']['name'] + data['project_name'] = task_entity['project']['full_name'] + data['hierarchy'] = self.get_parents(task_entity['parent']) + + return data + + def get_parents(self, entity): + output = [] + if entity.entity_type.lower() == 'project': + return output + output.extend(self.get_parents(entity['parent'])) + output.append(entity['name']) + + return output + + def event_handler(self, event): + try: + if event['data']['entities'][0]['objectTypeId'] != 'timer': + return + except Exception: + return + + new = event['data']['entities'][0]['changes']['start']['new'] + old = event['data']['entities'][0]['changes']['start']['old'] + + if old is None and new is None: + return + + timer_query = 'Timer where user.username is "{}"'.format(self.username) + timer = self.timer_session.query(timer_query).first() + if timer is not None: + self.last_task = timer['context'] + + if old is None: + self.signal_timer_started.emit( + self.get_data_from_task(self.last_task) + ) + elif new is None: + self.signal_timer_stopped.emit() + + def ftrack_stop_timer(self): + try: + self.user.stop_timer() + self.timer_session.commit() + self.signal_timer_stopped.emit() + except Exception as e: + log.debug("Timer stop had issues: {}".format(e)) + + def ftrack_start_timer(self, input_data): + if self.user is None: + return + if ( + input_data['task_name'] == self.last_task['name'] and + input_data['hierarchy'][-1] == self.last_task['parent']['name'] + ): + return + task_query = ( + 'Task where name is "{task_name}"' + ' and parent.name is "{entity_name}"' + ' and project.full_name is "{project_name}"' + ).format(**input_data) + + task = self.timer_session.query(task_query).one() + self.last_task = task + self.user.start_timer(task) + self.timer_session.commit() + self.signal_timer_started.emit( + self.get_data_from_task(self.last_task) + ) diff --git a/pype/ftrack/ftrack_run.py b/pype/ftrack/ftrack_run.py deleted file mode 100644 index a722f8d3fe..0000000000 --- a/pype/ftrack/ftrack_run.py +++ /dev/null @@ -1,624 +0,0 @@ -import os -import json -import threading -import time -import ftrack_api -from app import style -from app.vendor.Qt import QtCore, QtGui, QtWidgets - -from pype.ftrack import credentials, login_dialog as login_dialog - -from pype.vendor.pynput import mouse, keyboard -from . import FtrackServer - -from pype import api as pype - - -# load data from templates -pype.load_data_from_templates() - -log = pype.Logger.getLogger(__name__, "ftrack") - - -class FtrackRunner: - def __init__(self, main_parent=None, parent=None): - - self.parent = parent - self.widget_login = login_dialog.Login_Dialog_ui(self) - self.widget_timer = StopTimer(self) - self.action_server = FtrackServer('action') - self.thread_action_server = None - self.thread_timer = None - self.thread_timer_coundown = None - - # self.signal_start_timer.connect(self.timerStart) - - self.bool_logged = False - self.bool_action_server = False - self.bool_timer_event = False - - def show_login_widget(self): - self.widget_login.show() - - def validate(self): - validation = False - cred = credentials._get_credentials() - try: - if 'username' in cred and 'apiKey' in cred: - validation = credentials._check_credentials( - cred['username'], - cred['apiKey'] - ) - if validation is False: - self.show_login_widget() - else: - self.show_login_widget() - - except Exception as e: - log.error("We are unable to connect to Ftrack: {0}".format(e)) - - validation = credentials._check_credentials() - if validation is True: - log.info("Connected to Ftrack successfully") - self.loginChange() - else: - log.warning("Please sign in to Ftrack") - self.bool_logged = False - self.set_menu_visibility() - - return validation - - # Necessary - login_dialog works with this method after logging in - def loginChange(self): - self.bool_logged = True - self.set_menu_visibility() - self.start_action_server() - - def logout(self): - credentials._clear_credentials() - self.stop_action_server() - - log.info("Logged out of Ftrack") - self.bool_logged = False - self.set_menu_visibility() - - # Actions part - def start_action_server(self): - if self.thread_action_server is None: - self.thread_action_server = threading.Thread( - target=self.set_action_server - ) - self.thread_action_server.daemon = True - self.thread_action_server.start() - - log.info("Ftrack action server launched") - self.bool_action_server = True - self.set_menu_visibility() - - def set_action_server(self): - try: - self.action_server.run_server() - except Exception: - msg = 'Ftrack Action server crashed! Please try to start again.' - log.error(msg) - # TODO show message to user - self.bool_action_server = False - self.set_menu_visibility() - - def reset_action_server(self): - self.stop_action_server() - self.start_action_server() - - def stop_action_server(self): - try: - self.action_server.stop_session() - if self.thread_action_server is not None: - self.thread_action_server.join() - self.thread_action_server = None - - log.info("Ftrack action server stopped") - self.bool_action_server = False - self.set_menu_visibility() - except Exception as e: - log.error("During Killing action server: {0}".format(e)) - - # Definition of Tray menu - def trayMenu(self, parent): - # Menu for Tray App - self.menu = QtWidgets.QMenu('Ftrack', parent) - self.menu.setProperty('submenu', 'on') - self.menu.setStyleSheet(style.load_stylesheet()) - - # Actions - server - self.smActionS = self.menu.addMenu("Action server") - - self.aRunActionS = QtWidgets.QAction( - "Run action server", self.smActionS - ) - self.aResetActionS = QtWidgets.QAction( - "Reset action server", self.smActionS - ) - self.aStopActionS = QtWidgets.QAction( - "Stop action server", self.smActionS - ) - - self.aRunActionS.triggered.connect(self.start_action_server) - self.aResetActionS.triggered.connect(self.reset_action_server) - self.aStopActionS.triggered.connect(self.stop_action_server) - - self.smActionS.addAction(self.aRunActionS) - self.smActionS.addAction(self.aResetActionS) - self.smActionS.addAction(self.aStopActionS) - - # Actions - basic - self.aLogin = QtWidgets.QAction("Login", self.menu) - self.aLogin.triggered.connect(self.validate) - self.aLogout = QtWidgets.QAction("Logout", self.menu) - self.aLogout.triggered.connect(self.logout) - - self.menu.addAction(self.aLogin) - self.menu.addAction(self.aLogout) - - self.bool_logged = False - self.set_menu_visibility() - - return self.menu - - # Definition of visibility of each menu actions - def set_menu_visibility(self): - - self.smActionS.menuAction().setVisible(self.bool_logged) - self.aLogin.setVisible(not self.bool_logged) - self.aLogout.setVisible(self.bool_logged) - - if self.bool_logged is False: - if self.bool_timer_event is True: - self.stop_timer_thread() - return - - self.aRunActionS.setVisible(not self.bool_action_server) - self.aResetActionS.setVisible(self.bool_action_server) - self.aStopActionS.setVisible(self.bool_action_server) - - if self.bool_timer_event is False: - self.start_timer_thread() - - def start_timer_thread(self): - try: - if self.thread_timer is None: - self.thread_timer = FtrackEventsThread(self) - self.bool_timer_event = True - self.thread_timer.signal_timer_started.connect( - self.timer_started - ) - self.thread_timer.signal_timer_stopped.connect( - self.timer_stopped - ) - self.thread_timer.start() - except Exception: - pass - - def stop_timer_thread(self): - try: - if self.thread_timer is not None: - self.thread_timer.terminate() - self.thread_timer.wait() - self.thread_timer = None - - except Exception as e: - log.error("During Killing Timer event server: {0}".format(e)) - - def start_countdown_thread(self): - if self.thread_timer_coundown is None: - self.thread_timer_coundown = CountdownThread(self) - self.thread_timer_coundown.signal_show_question.connect( - self.show_widget_timer - ) - self.thread_timer_coundown.signal_send_time.connect( - self.change_count_widget - ) - self.thread_timer_coundown.signal_stop_timer.connect( - self.timer_stop - ) - self.thread_timer_coundown.start() - - def stop_countdown_thread(self): - if self.thread_timer_coundown is not None: - self.thread_timer_coundown.runs = False - self.thread_timer_coundown.terminate() - self.thread_timer_coundown.wait() - self.thread_timer_coundown = None - - def show_widget_timer(self): - self.widget_timer.show() - self.widget_timer.setWindowState(QtCore.Qt.WindowMinimized) - self.widget_timer.setWindowState(QtCore.Qt.WindowActive) - # self.widget_timer.activateWindow() - - def change_count_widget(self, time): - str_time = str(time).replace(".0", "") - self.widget_timer.lbl_rest_time.setText(str_time) - - def timer_started(self): - self.start_countdown_thread() - - def timer_stopped(self): - self.stop_countdown_thread() - - def timer_stop(self): - if self.thread_timer is not None: - self.widget_timer.main_context = False - self.widget_timer.refresh_context() - self.thread_timer.signal_stop_timer.emit() - if self.thread_timer_coundown is not None: - self.stop_countdown_thread() - - def timer_restart(self): - if self.thread_timer is not None: - self.thread_timer.signal_restart_timer.emit() - - self.timer_started() - - def timer_continue(self): - if self.thread_timer_coundown is not None: - self.thread_timer_coundown.signal_continue_timer.emit() - - -class FtrackEventsThread(QtCore.QThread): - # Senders - signal_timer_started = QtCore.Signal() - signal_timer_stopped = QtCore.Signal() - # Listeners - signal_stop_timer = QtCore.Signal() - signal_restart_timer = QtCore.Signal() - - def __init__(self, parent): - super(FtrackEventsThread, self).__init__() - cred = credentials._get_credentials() - self.username = cred['username'] - self.signal_stop_timer.connect(self.ftrack_stop_timer) - self.signal_restart_timer.connect(self.ftrack_restart_timer) - self.user = None - self.last_task = None - - def run(self): - self.timer_session = ftrack_api.Session(auto_connect_event_hub=True) - self.timer_session.event_hub.subscribe( - 'topic=ftrack.update and source.user.username={}'.format( - self.username - ), - self.event_handler) - - user_query = 'User where username is "{}"'.format(self.username) - self.user = self.timer_session.query(user_query).one() - - timer_query = 'Timer where user.username is "{}"'.format(self.username) - timer = self.timer_session.query(timer_query).first() - if timer is not None: - self.last_task = timer['context'] - self.signal_timer_started.emit() - - self.timer_session.event_hub.wait() - - def event_handler(self, event): - try: - if event['data']['entities'][0]['objectTypeId'] != 'timer': - return - except Exception: - return - - new = event['data']['entities'][0]['changes']['start']['new'] - old = event['data']['entities'][0]['changes']['start']['old'] - - if old is None and new is None: - return - - timer_query = 'Timer where user.username is "{}"'.format(self.username) - timer = self.timer_session.query(timer_query).first() - if timer is not None: - self.last_task = timer['context'] - - if old is None: - self.signal_timer_started.emit() - elif new is None: - self.signal_timer_stopped.emit() - - def ftrack_stop_timer(self): - try: - self.user.stop_timer() - self.timer_session.commit() - except Exception as e: - log.debug("Timer stop had issues: {}".format(e)) - - def ftrack_restart_timer(self): - try: - if (self.last_task is not None) and (self.user is not None): - self.user.start_timer(self.last_task) - self.timer_session.commit() - except Exception as e: - log.debug("Timer stop had issues: {}".format(e)) - - -class CountdownThread(QtCore.QThread): - # Senders - signal_show_question = QtCore.Signal() - signal_send_time = QtCore.Signal(object) - signal_stop_timer = QtCore.Signal() - signal_stop_countdown = QtCore.Signal() - # Listeners - signal_reset_timer = QtCore.Signal() - signal_continue_timer = QtCore.Signal() - - def __init__(self, parent): - super(CountdownThread, self).__init__() - - self.runs = True - self.over_line = False - config_data = self.load_timer_values() - self.count_length = config_data['full_time']*60 - self.border_line = config_data['message_time']*60 + 1 - self.reset_count() - self.signal_reset_timer.connect(self.reset_count) - self.signal_continue_timer.connect(self.continue_timer) - - def continue_timer(self): - self.over_line = False - self.reset_count() - - def reset_count(self): - if self.over_line is True: - self.actual = self.border_line - else: - self.actual = self.count_length - - def stop(self): - self.runs = False - - def run(self): - thread_mouse = MouseThread(self) - thread_mouse.start() - thread_keyboard = KeyboardThread(self) - thread_keyboard.start() - while self.runs: - if self.actual == self.border_line: - self.signal_show_question.emit() - self.over_line = True - - if self.actual <= self.border_line: - self.signal_send_time.emit(self.actual) - - time.sleep(1) - self.actual -= 1 - - if self.actual == 0: - self.runs = False - self.signal_stop_timer.emit() - - thread_mouse.signal_stop.emit() - thread_mouse.terminate() - thread_mouse.wait() - thread_keyboard.signal_stop.emit() - thread_keyboard.terminate() - thread_keyboard.wait() - - def load_timer_values(self): - templates = os.environ['PYPE_STUDIO_TEMPLATES'] - path_items = [templates, 'presets', 'ftrack', 'ftrack_config.json'] - filepath = os.path.sep.join(path_items) - data = dict() - try: - with open(filepath) as data_file: - json_dict = json.load(data_file) - data = json_dict['timer'] - except Exception as e: - msg = ( - 'Loading "Ftrack Config file" Failed.' - ' Please check log for more information.' - ' Times are set to default.' - ) - log.warning("{} - {}".format(msg, str(e))) - - data = self.validate_timer_values(data) - - return data - - def validate_timer_values(self, data): - # default values - if 'full_time' not in data: - data['full_time'] = 15 - if 'message_time' not in data: - data['message_time'] = 0.5 - - # minimum values - if data['full_time'] < 2: - data['full_time'] = 2 - # message time is earlier that full time - if data['message_time'] > data['full_time']: - data['message_time'] = data['full_time'] - 0.5 - return data - - -class MouseThread(QtCore.QThread): - signal_stop = QtCore.Signal() - - def __init__(self, parent): - super(MouseThread, self).__init__() - self.parent = parent - self.signal_stop.connect(self.stop) - self.m_listener = None - - def stop(self): - if self.m_listener is not None: - self.m_listener.stop() - - def on_move(self, posx, posy): - self.parent.signal_reset_timer.emit() - - def run(self): - self.m_listener = mouse.Listener(on_move=self.on_move) - self.m_listener.start() - - -class KeyboardThread(QtCore.QThread): - signal_stop = QtCore.Signal() - - def __init__(self, parent): - super(KeyboardThread, self).__init__() - self.parent = parent - self.signal_stop.connect(self.stop) - self.k_listener = None - - def stop(self): - if self.k_listener is not None: - self.k_listener.stop() - - def on_press(self, key): - self.parent.signal_reset_timer.emit() - - def run(self): - self.k_listener = keyboard.Listener(on_press=self.on_press) - self.k_listener.start() - - -class StopTimer(QtWidgets.QWidget): - - SIZE_W = 300 - SIZE_H = 160 - - def __init__(self, parent=None): - - super(StopTimer, self).__init__() - - self.main_context = True - self.parent = parent - self.setWindowIcon(self.parent.parent.icon) - self.setWindowFlags( - QtCore.Qt.WindowCloseButtonHint | - QtCore.Qt.WindowMinimizeButtonHint - ) - - self._translate = QtCore.QCoreApplication.translate - - self.font = QtGui.QFont() - self.font.setFamily("DejaVu Sans Condensed") - self.font.setPointSize(9) - self.font.setBold(True) - self.font.setWeight(50) - self.font.setKerning(True) - - self.resize(self.SIZE_W, self.SIZE_H) - self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H)) - self.setMaximumSize(QtCore.QSize(self.SIZE_W+100, self.SIZE_H+100)) - self.setStyleSheet(style.load_stylesheet()) - - self.setLayout(self._main()) - self.refresh_context() - self.setWindowTitle('Pype - Stop Ftrack timer') - - def _main(self): - self.main = QtWidgets.QVBoxLayout() - self.main.setObjectName('main') - - self.form = QtWidgets.QFormLayout() - self.form.setContentsMargins(10, 15, 10, 5) - self.form.setObjectName('form') - - msg_info = 'You didn\'t work for a long time.' - msg_question = 'Would you like to stop Ftrack timer?' - msg_stopped = ( - 'Your Ftrack timer was stopped. Do you want to start again?' - ) - - self.lbl_info = QtWidgets.QLabel(msg_info) - self.lbl_info.setFont(self.font) - self.lbl_info.setTextFormat(QtCore.Qt.RichText) - self.lbl_info.setObjectName("lbl_info") - self.lbl_info.setWordWrap(True) - - self.lbl_question = QtWidgets.QLabel(msg_question) - self.lbl_question.setFont(self.font) - self.lbl_question.setTextFormat(QtCore.Qt.RichText) - self.lbl_question.setObjectName("lbl_question") - self.lbl_question.setWordWrap(True) - - self.lbl_stopped = QtWidgets.QLabel(msg_stopped) - self.lbl_stopped.setFont(self.font) - self.lbl_stopped.setTextFormat(QtCore.Qt.RichText) - self.lbl_stopped.setObjectName("lbl_stopped") - self.lbl_stopped.setWordWrap(True) - - self.lbl_rest_time = QtWidgets.QLabel("") - self.lbl_rest_time.setFont(self.font) - self.lbl_rest_time.setTextFormat(QtCore.Qt.RichText) - self.lbl_rest_time.setObjectName("lbl_rest_time") - self.lbl_rest_time.setWordWrap(True) - self.lbl_rest_time.setAlignment(QtCore.Qt.AlignCenter) - - self.form.addRow(self.lbl_info) - self.form.addRow(self.lbl_question) - self.form.addRow(self.lbl_stopped) - self.form.addRow(self.lbl_rest_time) - - self.group_btn = QtWidgets.QHBoxLayout() - self.group_btn.addStretch(1) - self.group_btn.setObjectName("group_btn") - - self.btn_stop = QtWidgets.QPushButton("Stop timer") - self.btn_stop.setToolTip('Stop\'s Ftrack timer') - self.btn_stop.clicked.connect(self.stop_timer) - - self.btn_continue = QtWidgets.QPushButton("Continue") - self.btn_continue.setToolTip('Timer will continue') - self.btn_continue.clicked.connect(self.continue_timer) - - self.btn_close = QtWidgets.QPushButton("Close") - self.btn_close.setToolTip('Close window') - self.btn_close.clicked.connect(self.close_widget) - - self.btn_restart = QtWidgets.QPushButton("Start timer") - self.btn_restart.setToolTip('Timer will be started again') - self.btn_restart.clicked.connect(self.restart_timer) - - self.group_btn.addWidget(self.btn_continue) - self.group_btn.addWidget(self.btn_stop) - self.group_btn.addWidget(self.btn_restart) - self.group_btn.addWidget(self.btn_close) - - self.main.addLayout(self.form) - self.main.addLayout(self.group_btn) - - return self.main - - def refresh_context(self): - self.lbl_question.setVisible(self.main_context) - self.lbl_rest_time.setVisible(self.main_context) - self.lbl_stopped.setVisible(not self.main_context) - - self.btn_continue.setVisible(self.main_context) - self.btn_stop.setVisible(self.main_context) - self.btn_restart.setVisible(not self.main_context) - self.btn_close.setVisible(not self.main_context) - - def stop_timer(self): - self.parent.timer_stop() - self.close_widget() - - def restart_timer(self): - self.parent.timer_restart() - self.close_widget() - - def continue_timer(self): - self.parent.timer_continue() - self.close_widget() - - def closeEvent(self, event): - event.ignore() - if self.main_context is True: - self.continue_timer() - else: - self.close_widget() - - def close_widget(self): - self.main_context = True - self.refresh_context() - self.hide() diff --git a/pype/ftrack/ftrack_server/event_server.py b/pype/ftrack/ftrack_server/event_server.py index e824d1d899..2b3acad076 100644 --- a/pype/ftrack/ftrack_server/event_server.py +++ b/pype/ftrack/ftrack_server/event_server.py @@ -1,10 +1,10 @@ import sys from pype.ftrack import credentials, login_dialog as login_dialog from pype.ftrack.ftrack_server import FtrackServer -from app.vendor.Qt import QtWidgets +from Qt import QtWidgets from pype import api -log = api.Logger.getLogger(__name__, "ftrack-event-server") +log = api.Logger().get_logger(__name__, "ftrack-event-server") class EventServer: diff --git a/pype/ftrack/ftrack_server/event_server_cli.py b/pype/ftrack/ftrack_server/event_server_cli.py index a466bf5723..2e9519df26 100644 --- a/pype/ftrack/ftrack_server/event_server_cli.py +++ b/pype/ftrack/ftrack_server/event_server_cli.py @@ -1,9 +1,9 @@ import sys from pype.ftrack import credentials from pype.ftrack.ftrack_server import FtrackServer -from app import api +from pypeapp import api -log = api.Logger.getLogger(__name__, "ftrack-event-server-cli") +log = api.Logger().get_logger(__name__, "ftrack-event-server-cli") possible_yes = ['y', 'yes'] possible_no = ['n', 'no'] diff --git a/pype/ftrack/ftrack_server/ftrack_server.py b/pype/ftrack/ftrack_server/ftrack_server.py index 91caff216e..27207edc48 100644 --- a/pype/ftrack/ftrack_server/ftrack_server.py +++ b/pype/ftrack/ftrack_server/ftrack_server.py @@ -2,12 +2,12 @@ import os import sys import types import importlib -import ftrack_api +from pype.vendor import ftrack_api import time import logging -from app.api import Logger +from pypeapp.api import Logger -log = Logger.getLogger(__name__) +log = Logger().get_logger(__name__) """ # Required - Needed for connection to Ftrack diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index 6c3c9a0be4..56fe5f1ed2 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -8,11 +8,11 @@ import avalon import avalon.api from avalon import schema from avalon.vendor import toml, jsonschema -from app.api import Logger +from pypeapp.api import Logger ValidationError = jsonschema.ValidationError -log = Logger.getLogger(__name__) +log = Logger().get_logger(__name__) def get_ca_mongoid(): diff --git a/pype/ftrack/lib/ftrack_base_handler.py b/pype/ftrack/lib/ftrack_base_handler.py index a823394bb9..6d56fcb010 100644 --- a/pype/ftrack/lib/ftrack_base_handler.py +++ b/pype/ftrack/lib/ftrack_base_handler.py @@ -1,7 +1,7 @@ -import ftrack_api import functools import time from pype import api as pype +from pype.vendor import ftrack_api class MissingPermision(Exception): @@ -27,7 +27,7 @@ class BaseHandler(object): def __init__(self, session): '''Expects a ftrack_api.Session instance''' self._session = session - self.log = pype.Logger.getLogger(self.__class__.__name__) + self.log = pype.Logger().get_logger(self.__class__.__name__) # Using decorator self.register = self.register_decorator(self.register) diff --git a/pype/ftrack/login_dialog.py b/pype/ftrack/login_dialog.py index 2828afe539..04ebd59ae4 100644 --- a/pype/ftrack/login_dialog.py +++ b/pype/ftrack/login_dialog.py @@ -1,7 +1,7 @@ import os import requests -from app.vendor.Qt import QtCore, QtGui, QtWidgets -from app import style +from Qt import QtCore, QtGui, QtWidgets +from pypeapp import style from . import credentials, login_tools diff --git a/pype/ftrack/login_tools.py b/pype/ftrack/login_tools.py index 592ec152ee..b259f2d2ed 100644 --- a/pype/ftrack/login_tools.py +++ b/pype/ftrack/login_tools.py @@ -5,7 +5,7 @@ import webbrowser import functools import pype import inspect -from app.vendor.Qt import QtCore +from Qt import QtCore class LoginServerHandler(BaseHTTPRequestHandler): diff --git a/pype/services/__init__.py b/pype/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pype/services/idle_manager/__init__.py b/pype/services/idle_manager/__init__.py new file mode 100644 index 0000000000..7c07d3ebee --- /dev/null +++ b/pype/services/idle_manager/__init__.py @@ -0,0 +1,7 @@ +from .idle_manager import IdleManager + + +def tray_init(tray_widget, main_widget): + manager = IdleManager() + manager.start() + return manager diff --git a/pype/services/idle_manager/idle_manager.py b/pype/services/idle_manager/idle_manager.py new file mode 100644 index 0000000000..e8ba246121 --- /dev/null +++ b/pype/services/idle_manager/idle_manager.py @@ -0,0 +1,111 @@ +import time +from Qt import QtCore, QtGui, QtWidgets +from pype.vendor.pynput import mouse, keyboard +from pypeapp import Logger + + +class IdleManager(QtCore.QThread): + """ Measure user's idle time in seconds. + Idle time resets on keyboard/mouse input. + Is able to emit signals at specific time idle. + """ + time_signals = {} + idle_time = 0 + signal_reset_timer = QtCore.Signal() + + def __init__(self): + super(IdleManager, self).__init__() + self.log = Logger().get_logger(self.__class__.__name__) + self.signal_reset_timer.connect(self._reset_time) + self._is_running = False + + def add_time_signal(self, emit_time, signal): + """ If any module want to use IdleManager, need to use add_time_signal + :param emit_time: time when signal will be emitted + :type emit_time: int + :param signal: signal that will be emitted (without objects) + :type signal: QtCore.Signal + """ + if emit_time not in self.time_signals: + self.time_signals[emit_time] = [] + self.time_signals[emit_time].append(signal) + + @property + def is_running(self): + return self._is_running + + def _reset_time(self): + self.idle_time = 0 + + def stop(self): + self._is_running = False + + def run(self): + self.log.info('IdleManager has started') + self._is_running = True + thread_mouse = MouseThread(self.signal_reset_timer) + thread_mouse.start() + thread_keyboard = KeyboardThread(self.signal_reset_timer) + thread_keyboard.start() + while self._is_running: + self.idle_time += 1 + if self.idle_time in self.time_signals: + for signal in self.time_signals[self.idle_time]: + signal.emit() + time.sleep(1) + + thread_mouse.signal_stop.emit() + thread_mouse.terminate() + thread_mouse.wait() + thread_keyboard.signal_stop.emit() + thread_keyboard.terminate() + thread_keyboard.wait() + self.log.info('IdleManager has stopped') + + +class MouseThread(QtCore.QThread): + """Listens user's mouse movement + """ + signal_stop = QtCore.Signal() + + def __init__(self, signal): + super(MouseThread, self).__init__() + self.signal_stop.connect(self.stop) + self.m_listener = None + + self.signal_reset_timer = signal + + def stop(self): + if self.m_listener is not None: + self.m_listener.stop() + + def on_move(self, posx, posy): + self.signal_reset_timer.emit() + + def run(self): + self.m_listener = mouse.Listener(on_move=self.on_move) + self.m_listener.start() + + +class KeyboardThread(QtCore.QThread): + """Listens user's keyboard input + """ + signal_stop = QtCore.Signal() + + def __init__(self, signal): + super(KeyboardThread, self).__init__() + self.signal_stop.connect(self.stop) + self.k_listener = None + + self.signal_reset_timer = signal + + def stop(self): + if self.k_listener is not None: + self.k_listener.stop() + + def on_press(self, key): + self.signal_reset_timer.emit() + + def run(self): + self.k_listener = keyboard.Listener(on_press=self.on_press) + self.k_listener.start() diff --git a/pype/services/timers_manager/__init__.py b/pype/services/timers_manager/__init__.py new file mode 100644 index 0000000000..a6c4535f3d --- /dev/null +++ b/pype/services/timers_manager/__init__.py @@ -0,0 +1,6 @@ +from .timers_manager import TimersManager +from .widget_user_idle import WidgetUserIdle + + +def tray_init(tray_widget, main_widget): + return TimersManager(tray_widget, main_widget) diff --git a/pype/services/timers_manager/timers_manager.py b/pype/services/timers_manager/timers_manager.py new file mode 100644 index 0000000000..6f10a0ec68 --- /dev/null +++ b/pype/services/timers_manager/timers_manager.py @@ -0,0 +1,175 @@ +from Qt import QtCore +from .widget_user_idle import WidgetUserIdle +from pypeapp.lib.config import get_presets +from pypeapp import Logger + + +class Singleton(type): + """ Signleton implementation + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super( + Singleton, cls + ).__call__(*args, **kwargs) + return cls._instances[cls] + + +class TimersManager(metaclass=Singleton): + """ Handles about Timers. + + Should be able to start/stop all timers at once. + If IdleManager is imported then is able to handle about stop timers + when user idles for a long time (set in presets). + """ + modules = [] + is_running = False + last_task = None + + def __init__(self, tray_widget, main_widget): + self.log = Logger().get_logger(self.__class__.__name__) + self.tray_widget = tray_widget + self.main_widget = main_widget + self.widget_user_idle = WidgetUserIdle(self) + + def set_signal_times(self): + try: + timer_info = get_presets()['services']['timers_manager']['timer'] + full_time = int(timer_info['full_time'])*60 + message_time = int(timer_info['message_time'])*60 + self.time_show_message = full_time - message_time + self.time_stop_timer = full_time + return True + except Exception: + self.log.warning('Was not able to load presets for TimersManager') + return False + + def add_module(self, module): + """ Adds module to context + + Module must have implemented methods: + - ``start_timer_manager(data)`` + - ``stop_timer_manager()`` + """ + self.modules.append(module) + + def start_timers(self, data): + ''' + :param data: basic information needed to start any timer + :type data: dict + ..note:: + Dictionary "data" should contain: + - project_name(str) - Name of Project + - hierarchy(list/tuple) - list of parents(except project) + - task_type(str) + - task_name(str) + + Example: + - to run timers for task in + 'C001_BackToPast/assets/characters/villian/Lookdev BG' + - input data should contain: + .. code-block:: Python + data = { + 'project_name': 'C001_BackToPast', + 'hierarchy': ['assets', 'characters', 'villian'], + 'task_type': 'lookdev', + 'task_name': 'Lookdev BG' + } + ''' + self.last_task = data + for module in self.modules: + module.start_timer_manager(data) + self.is_running = True + + def restart_timers(self): + if self.last_task is not None: + self.start_timers(self.last_task) + + def stop_timers(self): + if self.is_running is False: + return + self.widget_user_idle.bool_not_stopped = False + self.widget_user_idle.refresh_context() + for module in self.modules: + module.stop_timer_manager() + self.is_running = False + + def process_modules(self, modules): + """ Gives ability to connect with imported modules from TrayManager. + + :param modules: All imported modules from TrayManager + :type modules: dict + """ + self.s_handler = SignalHandler(self) + + if 'IdleManager' in modules: + if self.set_signal_times() is True: + self.register_to_idle_manager(modules['IdleManager']) + + def register_to_idle_manager(self, man_obj): + self.idle_man = man_obj + # Times when idle is between show widget and stop timers + show_to_stop_range = range( + self.time_show_message-1, self.time_stop_timer + ) + for num in show_to_stop_range: + self.idle_man.add_time_signal( + num, + self.s_handler.signal_change_label + ) + # Times when widget is already shown and user restart idle + shown_and_moved_range = range( + self.time_stop_timer - self.time_show_message + ) + for num in shown_and_moved_range: + self.idle_man.add_time_signal( + num, + self.s_handler.signal_change_label + ) + # Time when message is shown + self.idle_man.add_time_signal( + self.time_show_message, + self.s_handler.signal_show_message + ) + # Time when timers are stopped + self.idle_man.add_time_signal( + self.time_stop_timer, + self.s_handler.signal_stop_timers + ) + + def change_label(self): + if self.is_running is False: + return + if self.widget_user_idle.bool_is_showed is False: + return + if not hasattr(self, 'idle_man'): + return + + if self.idle_man.idle_time > self.time_show_message: + value = self.time_stop_timer - self.idle_man.idle_time + else: + value = 1 + ( + self.time_stop_timer - + self.time_show_message - + self.idle_man.idle_time + ) + self.widget_user_idle.change_count_widget(value) + + def show_message(self): + if self.is_running is False: + return + if self.widget_user_idle.bool_is_showed is False: + self.widget_user_idle.show() + + +class SignalHandler(QtCore.QObject): + signal_show_message = QtCore.Signal() + signal_change_label = QtCore.Signal() + signal_stop_timers = QtCore.Signal() + def __init__(self, cls): + super().__init__() + self.signal_show_message.connect(cls.show_message) + self.signal_change_label.connect(cls.change_label) + self.signal_stop_timers.connect(cls.stop_timers) diff --git a/pype/services/timers_manager/widget_user_idle.py b/pype/services/timers_manager/widget_user_idle.py new file mode 100644 index 0000000000..b65ffd40ba --- /dev/null +++ b/pype/services/timers_manager/widget_user_idle.py @@ -0,0 +1,155 @@ +from pypeapp import style, Logger +from Qt import QtCore, QtGui, QtWidgets + + +class WidgetUserIdle(QtWidgets.QWidget): + + SIZE_W = 300 + SIZE_H = 160 + + def __init__(self, parent): + + super(WidgetUserIdle, self).__init__() + + self.bool_is_showed = False + self.bool_not_stopped = True + + self.parent_widget = parent + self.setWindowIcon(parent.tray_widget.icon) + self.setWindowFlags( + QtCore.Qt.WindowCloseButtonHint | + QtCore.Qt.WindowMinimizeButtonHint + ) + + self._translate = QtCore.QCoreApplication.translate + + self.font = QtGui.QFont() + self.font.setFamily("DejaVu Sans Condensed") + self.font.setPointSize(9) + self.font.setBold(True) + self.font.setWeight(50) + self.font.setKerning(True) + + self.resize(self.SIZE_W, self.SIZE_H) + self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H)) + self.setMaximumSize(QtCore.QSize(self.SIZE_W+100, self.SIZE_H+100)) + self.setStyleSheet(style.load_stylesheet()) + + self.setLayout(self._main()) + self.refresh_context() + self.setWindowTitle('Pype - Stop timers') + + def _main(self): + self.main = QtWidgets.QVBoxLayout() + self.main.setObjectName('main') + + self.form = QtWidgets.QFormLayout() + self.form.setContentsMargins(10, 15, 10, 5) + self.form.setObjectName('form') + + msg_info = 'You didn\'t work for a long time.' + msg_question = 'Would you like to stop Timers?' + msg_stopped = ( + 'Your Timers were stopped. Do you want to start them again?' + ) + + self.lbl_info = QtWidgets.QLabel(msg_info) + self.lbl_info.setFont(self.font) + self.lbl_info.setTextFormat(QtCore.Qt.RichText) + self.lbl_info.setObjectName("lbl_info") + self.lbl_info.setWordWrap(True) + + self.lbl_question = QtWidgets.QLabel(msg_question) + self.lbl_question.setFont(self.font) + self.lbl_question.setTextFormat(QtCore.Qt.RichText) + self.lbl_question.setObjectName("lbl_question") + self.lbl_question.setWordWrap(True) + + self.lbl_stopped = QtWidgets.QLabel(msg_stopped) + self.lbl_stopped.setFont(self.font) + self.lbl_stopped.setTextFormat(QtCore.Qt.RichText) + self.lbl_stopped.setObjectName("lbl_stopped") + self.lbl_stopped.setWordWrap(True) + + self.lbl_rest_time = QtWidgets.QLabel("") + self.lbl_rest_time.setFont(self.font) + self.lbl_rest_time.setTextFormat(QtCore.Qt.RichText) + self.lbl_rest_time.setObjectName("lbl_rest_time") + self.lbl_rest_time.setWordWrap(True) + self.lbl_rest_time.setAlignment(QtCore.Qt.AlignCenter) + + self.form.addRow(self.lbl_info) + self.form.addRow(self.lbl_question) + self.form.addRow(self.lbl_stopped) + self.form.addRow(self.lbl_rest_time) + + self.group_btn = QtWidgets.QHBoxLayout() + self.group_btn.addStretch(1) + self.group_btn.setObjectName("group_btn") + + self.btn_stop = QtWidgets.QPushButton("Stop timer") + self.btn_stop.setToolTip('Stop\'s All timers') + self.btn_stop.clicked.connect(self.stop_timer) + + self.btn_continue = QtWidgets.QPushButton("Continue") + self.btn_continue.setToolTip('Timer won\'t stop') + self.btn_continue.clicked.connect(self.continue_timer) + + self.btn_close = QtWidgets.QPushButton("Close") + self.btn_close.setToolTip('Close window') + self.btn_close.clicked.connect(self.close_widget) + + self.btn_restart = QtWidgets.QPushButton("Start timers") + self.btn_restart.setToolTip('Timer will be started again') + self.btn_restart.clicked.connect(self.restart_timer) + + self.group_btn.addWidget(self.btn_continue) + self.group_btn.addWidget(self.btn_stop) + self.group_btn.addWidget(self.btn_restart) + self.group_btn.addWidget(self.btn_close) + + self.main.addLayout(self.form) + self.main.addLayout(self.group_btn) + + return self.main + + def refresh_context(self): + self.lbl_question.setVisible(self.bool_not_stopped) + self.lbl_rest_time.setVisible(self.bool_not_stopped) + self.lbl_stopped.setVisible(not self.bool_not_stopped) + + self.btn_continue.setVisible(self.bool_not_stopped) + self.btn_stop.setVisible(self.bool_not_stopped) + self.btn_restart.setVisible(not self.bool_not_stopped) + self.btn_close.setVisible(not self.bool_not_stopped) + + def change_count_widget(self, time): + str_time = str(time) + self.lbl_rest_time.setText(str_time) + + def stop_timer(self): + self.parent_widget.stop_timers() + self.close_widget() + + def restart_timer(self): + self.parent_widget.restart_timers() + self.close_widget() + + def continue_timer(self): + self.close_widget() + + def closeEvent(self, event): + event.ignore() + if self.bool_not_stopped is True: + self.continue_timer() + else: + self.close_widget() + + def close_widget(self): + self.bool_is_showed = False + self.bool_not_stopped = True + self.refresh_context() + self.hide() + + def showEvent(self, event): + self.bool_is_showed = True diff --git a/pype/templates.py b/pype/templates.py index c5578a983c..92dad30e7e 100644 --- a/pype/templates.py +++ b/pype/templates.py @@ -3,8 +3,9 @@ import re from avalon import io from avalon import api as avalon from . import lib -from app.api import (Templates, Logger, format) -log = Logger.getLogger(__name__, +# from pypeapp.api import (Templates, Logger, format) +from pypeapp.api import Logger +log = Logger().get_logger(__name__, os.getenv("AVALON_APP", "pype-config")) SESSION = None