Merged in feature/2.0/PYPE-245_tray_basic_services (pull request #99)

Feature/2.0/PYPE-245 tray basic services

Approved-by: Milan Kolar <milan@orbi.tools>
This commit is contained in:
Jakub Trllo 2019-03-28 12:43:30 +00:00 committed by Milan Kolar
commit 797ea96b67
38 changed files with 896 additions and 681 deletions

View file

@ -15,7 +15,7 @@ from .action import (
RepairContextAction
)
from app.api import Logger
from pypeapp.api import Logger
from . import (
Anatomy,

View file

@ -0,0 +1,5 @@
from .avalon_app import AvalonApps
def tray_init(tray_widget, main_widget):
return AvalonApps(main_widget, tray_widget)

View file

@ -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
)

View file

@ -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)

View file

@ -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):

View file

@ -1,7 +1,7 @@
import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -2,7 +2,7 @@ import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,7 +1,7 @@
import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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):

View file

@ -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

View file

@ -1,7 +1,7 @@
import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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

View file

@ -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

View file

@ -3,7 +3,7 @@ import argparse
import logging
import json
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseEvent

View file

@ -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

View file

@ -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)
)

View file

@ -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()

View file

@ -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:

View file

@ -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']

View file

@ -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

View file

@ -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():

View file

@ -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)

View file

@ -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

View file

@ -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):

View file

View file

@ -0,0 +1,7 @@
from .idle_manager import IdleManager
def tray_init(tray_widget, main_widget):
manager = IdleManager()
manager.start()
return manager

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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