def hasChildren(self, index): """returns True or False depending on to the index and the item on the index """ logger.debug( 'TaskTreeModel.hasChildren() is started for index: %s' % index) if not index.isValid(): if self.user_tasks_only: if self.user_id: projects_count = db.DBSession.query(ProjectUser.id)\ .filter(ProjectUser.user_id == self.user_id)\ .count() else: projects_count = 0 else: projects_count = db.DBSession.query(Project.id).count() return_value = projects_count > 0 else: item = self.itemFromIndex(index) return_value = False if item: return_value = item.hasChildren() logger.debug( 'TaskTreeModel.hasChildren() is finished for index: %s' % index) return return_value
def initialize_structure(self, version): """Initializes the environment folder structure :return: """ # check version type from stalker import Version if not isinstance(version, Version): raise TypeError( '"version" argument in %s.initialize_structureshould be a ' 'stalker.version.Version instance, not %s' % ( self.__class__.__name__, version.__class__.__name__ ) ) # create the folder in version.absolute_path extension = version.extension version.update_paths() version.extension = extension for folder in self.structure: folder_path = os.path.join(version.absolute_path, folder) logger.debug('creating: %s' % folder_path) try: os.makedirs(folder_path) except OSError: # dir exists pass
def get_version_from_recent_files(self): """It will try to create a :class:`~oyProjectManager.models.version.Version` instance by looking at the recent files list. It will return None if it can not find one. :return: :class:`~oyProjectManager.models.version.Version` """ # full_path = self.fusion_prefs["LastCompFile"] # return self.get_version_from_full_path(full_path) version = None rfm = RecentFileManager() try: recent_files = rfm[self.name] except KeyError: logger.debug('no recent files') recent_files = None if recent_files is not None: for i in range(len(recent_files)): version = self.get_version_from_full_path(recent_files[i]) if version is not None: break logger.debug("version from recent files is: %s" % version) return version
def __init__(self, *args, **kwargs): QtGui.QStandardItemModel.__init__(self, *args, **kwargs) logger.debug('TaskTreeModel.__init__() is started') self.user = None self.root = None self.user_tasks_only = False logger.debug('TaskTreeModel.__init__() is finished')
def __init__(self, flat_view=False, *args, **kwargs): QtGui.QStandardItemModel.__init__(self, *args, **kwargs) logger.debug('VersionTreeModel.__init__() is started') self.root = None self.root_versions = [] self.reference_resolution = None self.flat_view = flat_view logger.debug('VersionTreeModel.__init__() is finished')
def clone(self): """returns a copy of this item """ logger.debug('TaskItem.clone() is started for item: %s' % self.text()) new_item = TaskItem(entity=self.task) new_item.parent = self.parent new_item.fetched_all = self.fetched_all logger.debug('TaskItem.clone() is finished for item: %s' % self.text()) return new_item
def current_take_name(self, take_name): """sets the current take name """ logger.debug('finding take with name: %s' % take_name) items = self.findItems( take_name, QtCore.Qt.MatchExactly ) if items: self.setCurrentItem(items[0])
def hasChildren(self): logger.debug( 'VersionItem.hasChildren() is started for item: %s' % self.text()) if self.version: return_value = bool(self.version.inputs) else: return_value = False logger.debug( 'VersionItem.hasChildren() is finished for item: %s' % self.text()) return return_value
def clone(self): """returns a copy of this item """ logger.debug('VersionItem.clone() is started for item: %s' % self.text()) new_item = VersionItem() new_item.version = self.version new_item.parent = self.parent new_item.fetched_all = self.fetched_all logger.debug('VersionItem.clone() is finished for item: %s' % self.text()) return new_item
def hasChildren(self): logger.debug( 'TaskItem.hasChildren() is started for item: %s' % self.text() ) return_value = self.task.has_children logger.debug( 'TaskItem.hasChildren() is finished for item: %s' % self.text() ) return return_value
def canFetchMore(self): logger.debug( 'VersionItem.canFetchMore() is started for item: %s' % self.text()) if self.version and not self.fetched_all: return_value = bool(self.version.inputs) else: return_value = False logger.debug( 'VersionItem.canFetchMore() is finished for item: %s' % self.text()) return return_value
def fetchMore(self, index): """fetches more elements """ logger.debug( 'TaskTreeModel.canFetchMore() is started for index: %s' % index) if index.isValid(): item = self.itemFromIndex(index) item.fetchMore() logger.debug( 'TaskTreeModel.canFetchMore() is finished for index: %s' % index)
def _show_versions_treeView_context_menu(self, position): """the custom context menu for the versions_treeView """ # convert the position to global screen position global_position = \ self.versions_treeView.mapToGlobal(position) index = self.versions_treeView.indexAt(position) model = self.versions_treeView.model() # get the column 0 item which holds the Version instance # index = self.versions_treeView.model().index(index.row(), 0) item = model.itemFromIndex(index) logger.debug('itemAt(position) : %s' % item) if not item: return if not hasattr(item, 'version'): return version = item.version latest_published_version = None if version: latest_published_version = version.latest_published_version item_action = item.action # if item_action != 'create': # return from stalker import Version if not isinstance(version, Version): return # create the menu menu = QtWidgets.QMenu() # Add "Open..." action # Always open the latest published version absolute_full_path = version.absolute_full_path if absolute_full_path: action = menu.addAction('Open...') if latest_published_version: action.version = latest_published_version else: action.version = version selected_action = menu.exec_(global_position) if selected_action: choice = selected_action.text() if choice == 'Open...': self.open_version(selected_action.version)
def canFetchMore(self, index): logger.debug( 'TaskTreeModel.canFetchMore() is started for index: %s' % index) if not index.isValid(): return_value = False else: item = self.itemFromIndex(index) return_value = item.canFetchMore() logger.debug( 'TaskTreeModel.canFetchMore() is finished for index: %s' % index) return return_value
def get_item_indices_containing_text(cls, text, tree_view): """returns the indexes of the item indices containing the given text """ model = tree_view.model() logger.debug('searching for text : %s' % text) return model.match( model.index(0, 0), 0, text, -1, QtCore.Qt.MatchRecursive )
def __init__(self, *args, **kwargs): QtGui.QStandardItem.__init__(self, *args, **kwargs) logger.debug( 'VersionItem.__init__() is started for item: %s' % self.text()) self.loaded = False self.version = None self.parent = None self.pseudo_model = None self.fetched_all = False self.setEditable(False) logger.debug( 'VersionItem.__init__() is finished for item: %s' % self.text())
def execute(self, context): logger.debug('inside %s.execute()' % self.__class__.__name__) # get the scene and all the shots under it scene = Task.query.get(self.stalker_entity_id) logger.debug('scene: %s' % scene) # generate storyboard strip_gen = StripGenerator() strip_gen.previs(scene) return set(['FINISHED'])
def show(self): """overridden show method """ logger.debug('MainDialog.show is started') self.logged_in_user = self.get_logged_in_user() if not self.logged_in_user: self.close() return_val = None else: return_val = super(MainDialog, self).show() logger.debug('MainDialog.show is finished') return return_val
def canFetchMore(self): logger.debug( 'TaskItem.canFetchMore() is started for item: %s' % self.text() ) # return_value = False if self.task and self.task.id and not self.fetched_all: return_value = self.task.has_children else: return_value = False logger.debug( 'TaskItem.canFetchMore() is finished for item: %s' % self.text() ) return return_value
def update(self, completion_prefix): tasks = Task.query \ .filter(Task.name.ilike('%' + completion_prefix + '%')) \ .all() logger.debug('completer tasks : %s' % tasks) task_names = [task.name for task in tasks] model = QtGui.QStringListModel(task_names) self.setModel(model) # self.setCompletionPrefix(completion_prefix) self.setCompletionPrefix('') # if completion_prefix.strip() != '': self.complete()
def flatten(self, path, project_name='DefaultProject'): """Flattens the given maya scene in to a new default project externally that is without opening it and returns the project path. It will also flatten all the referenced files, textures, image planes and Arnold Scene Source files. :param path: The path to the file which wanted to be flattened :return: """ # create a new Default Project tempdir = tempfile.gettempdir() from stalker import Repository all_repos = Repository.query.all() default_project_path = \ self.create_default_project(path=tempdir, name=project_name) logger.debug( 'creating new default project at: %s' % default_project_path ) ref_paths = \ self._move_file_and_fix_references(path, default_project_path) while len(ref_paths): ref_path = ref_paths.pop(0) if self.exclude_mask \ and os.path.splitext(ref_path)[1] in self.exclude_mask: logger.debug('skipping: %s' % ref_path) continue # fix different OS paths for repo in all_repos: if repo.is_in_repo(ref_path): ref_path = repo.to_native_path(ref_path) new_ref_paths = \ self._move_file_and_fix_references( ref_path, default_project_path, scenes_folder='scenes/refs' ) # extend ref_paths with new ones for new_ref_path in new_ref_paths: if new_ref_path not in ref_paths: ref_paths.append(new_ref_path) return default_project_path
def _setup_signals(self): """sets up the signals """ logger.debug("start setting up interface signals") # Dialog buttons QtCore.QObject.connect( self.dialog_button_box, QtCore.SIGNAL("accepted()"), self.accept ) QtCore.QObject.connect( self.dialog_button_box, QtCore.SIGNAL("rejected()"), self.reject ) # tasks_combo_box QtCore.QObject.connect( self.tasks_combo_box, QtCore.SIGNAL('currentIndexChanged(QString)'), self.tasks_combo_box_index_changed ) # start_time_edit QtCore.QObject.connect( self.start_time_edit, QtCore.SIGNAL('timeChanged(QTime)'), self.start_time_changed ) # end_time_edit QtCore.QObject.connect( self.end_time_edit, QtCore.SIGNAL('timeChanged(QTime)'), self.end_time_changed ) # resource changed QtCore.QObject.connect( self.resource_combo_box, QtCore.SIGNAL('currentIndexChanged(QString)'), self.resource_changed ) # calendar day selected QtCore.QObject.connect( self.calendar_widget, QtCore.SIGNAL("selectionChanged()"), self.calendar_widget_selection_changed )
def _move_file_and_fix_references(self, path, project_path, scenes_folder='scenes', refs_folder='scenes/refs'): """Moves the given maya file to the given project path and moves any references of it to :param str path: The path of the maya file :param str project_path: The project path :param str scenes_folder: The scenes folder to store the original maya scene. :param str refs_folder: The references folder to replace reference paths with. :return list: returns a list of paths """ # fix any env vars path = os.path.expandvars(path) original_file_name = os.path.basename(path) logger.debug('original_file_name: %s' % original_file_name) new_file_path = \ os.path.join(project_path, scenes_folder, original_file_name) ref_paths = [] # only get new ref paths for '.ma' files if path.endswith('.ma'): # read the data of the original file with open(path) as f: data = f.read() ref_paths = self._extract_references(data) # fix all reference paths for ref_path in ref_paths: data = data.replace( ref_path, '%s/%s' % (refs_folder, os.path.basename(ref_path)) ) # now write all the data back to a new temp scene with open(new_file_path, 'w+') as f: f.write(data) else: # just copy the file try: shutil.copy(path, new_file_path) except IOError: pass return ref_paths
def fill_versions_treeView(self): """sets up the versions_treeView """ logger.debug('start filling versions_treeView') logger.debug('creating a new model') version_tree_model = VersionTreeModel() version_tree_model.reference_resolution = self.reference_resolution # populate with all update items version_tree_model.populateTree(self.reference_resolution['root']) self.versions_treeView.setModel(version_tree_model) logger.debug('setting up signals for versions_treeView_changed') # versions_treeView # QtCore.QObject.connect( # self.versions_treeView.selectionModel(), # QtCore.SIGNAL('selectionChanged(const QItemSelection &, ' # 'const QItemSelection &)'), # self.versions_treeView_changed # ) self.versions_treeView.is_updating = False self.versions_treeView_auto_fit_column() logger.debug('finished filling versions_treeView')
def __init__(self, *args, **kwargs): QtGui.QStandardItem.__init__(self, *args, **kwargs) logger.debug( 'TaskItem.__init__() is started for item: %s' % self.text() ) self.loaded = False self.task = None self.parent = None self.fetched_all = False self.setEditable(False) self.user = None self.user_tasks_only = False logger.debug( 'TaskItem.__init__() is finished for item: %s' % self.text() )
def execute(self, context): logger.debug('inside %s.execute()' % self.__class__.__name__) # get the scene and all the shots under it shot = Shot.query.get(self.stalker_entity_id) logger.debug('shot: %s' % shot) # find Previs, Animation, Lighting and Comp tasks # # generate storyboard # strip_gen = StripGenerator() # strip_gen.comp(shot) return set(['FINISHED'])
def clean_up(self): """cleans up the scene """ num_of_items_deleted = pm.mel.eval('MLdeleteUnused') logger.debug('deleting unknown references') delete_nodes_types = ['reference', 'unknown'] for node in pm.ls(type=delete_nodes_types): node.unlock() logger.debug('deleting "delete_nodes_types"') try: pm.delete(pm.ls(type=delete_nodes_types)) except RuntimeError: pass
def canFetchMore(self): logger.debug( 'TaskItem.canFetchMore() is started for item: %s' % self.text() ) return_value = False if self.task and not self.fetched_all: if isinstance(self.task, Task): return_value = self.task.is_container elif isinstance(self.task, Project): return_value = len(self.task.root_tasks) > 0 else: return_value = False logger.debug( 'TaskItem.canFetchMore() is finished for item: %s' % self.text() ) return return_value
def fetchMore(self): logger.debug( 'VersionItem.fetchMore() is started for item: %s' % self.text()) if self.canFetchMore(): # model = self.model() # This will cause a SEGFAULT versions = sorted(self.version.inputs, key=lambda x: x.full_path) for version in versions: self.appendRow( self.generate_version_row(self, self.pseudo_model, version) ) self.fetched_all = True logger.debug( 'VersionItem.fetchMore() is finished for item: %s' % self.text())
def populateTree(self, versions): """populates tree with root versions """ logger.debug('VersionTreeModel.populateTree() is started') self.setColumnCount(7) self.setHorizontalHeaderLabels( ['Do Update?', 'Thumbnail', 'Task', 'Take', 'Current', 'Latest', 'Action', 'Updated By', 'Notes'] ) self.root_versions = versions for version in versions: self.appendRow( VersionItem.generate_version_row(None, self, version) ) logger.debug('VersionTreeModel.populateTree() is finished')
def show_context_menu(self, position): """the custom context menu """ # convert the position to global screen position global_position = self.mapToGlobal(position) index = self.indexAt(position) model = self.model() item = model.itemFromIndex(index) logger.debug('itemAt(position) : %s' % item) task_id = None entity = None # if not item: # return # if item and not hasattr(item, 'task'): # return from anima.ui.models.task import TaskItem #if not isinstance(item, TaskItem): # return if item and item.task: task_id = item.task.id # if not task_id: # return from anima import utils file_browser_name = utils.file_browser_name() # create the menu menu = QtWidgets.QMenu() # Open in browser # sub menus create_sub_menu = menu.addMenu('Create') update_sub_menu = menu.addMenu('Update') # ----------------------------------- # actions created in different scopes create_time_log_action = None create_project_action = None update_task_action = None create_child_task_action = None duplicate_task_hierarchy_action = None delete_task_action = None no_deps_action = None create_project_structure_action = None create_task_structure_action = None update_project_action = None assign_users_action = None open_in_web_browser_action = None open_in_file_browser_action = None copy_url_action = None copy_id_to_clipboard = None fix_task_status_action = None change_status_menu_actions = [] from anima import defaults from stalker import LocalSession local_session = LocalSession() logged_in_user = local_session.logged_in_user from stalker import SimpleEntity, Task, Project # TODO: Update this to use only task_id if task_id: entity = SimpleEntity.query.get(task_id) if defaults.is_power_user(logged_in_user): # create the Create Project menu item create_project_action = \ create_sub_menu.addAction(u'\uf0e8 Create Project...') if isinstance(entity, Project): # this is a project! if defaults.is_power_user(logged_in_user): update_project_action = \ update_sub_menu.addAction(u'\uf044 Update Project...') assign_users_action = \ menu.addAction(u'\uf0c0 Assign Users...') create_project_structure_action = \ create_sub_menu.addAction( u'\uf115 Create Project Structure' ) create_child_task_action = \ create_sub_menu.addAction( u'\uf0ae Create Child Task...' ) if entity: # separate the Project and Task related menu items menu.addSeparator() open_in_web_browser_action = \ menu.addAction(u'\uf14c Open In Web Browser...') open_in_file_browser_action = \ menu.addAction(u'\uf07c Browse Folders...') copy_url_action = menu.addAction(u'\uf0c5 Copy URL') copy_id_to_clipboard = \ menu.addAction(u'\uf0c5 Copy ID to clipboard') if isinstance(entity, Task): # this is a task create_task_structure_action = \ create_sub_menu.addAction( u'\uf115 Create Task Folder Structure' ) task = entity from stalker import Status status_wfd = Status.query.filter(Status.code == 'WFD').first() status_prev = \ Status.query.filter(Status.code == 'PREV').first() status_cmpl = \ Status.query.filter(Status.code == 'CMPL').first() if logged_in_user in task.resources \ and task.status not in [status_wfd, status_prev, status_cmpl]: create_sub_menu.addSeparator() create_time_log_action = \ create_sub_menu.addAction(u'\uf073 Create TimeLog...') # Add Depends To menu menu.addSeparator() depends = task.depends if depends: depends_to_menu = menu.addMenu(u'\uf090 Depends To') for dTask in depends: action = depends_to_menu.addAction(dTask.name) action.task = dTask # Add Dependent Of Menu dependent_of = task.dependent_of if dependent_of: dependent_of_menu = menu.addMenu(u'\uf08b Dependent Of') for dTask in dependent_of: action = dependent_of_menu.addAction(dTask.name) action.task = dTask if not depends and not dependent_of: no_deps_action = menu.addAction(u'\uf00d No Dependencies') no_deps_action.setEnabled(False) # update task and create child task menu items menu.addSeparator() if defaults.is_power_user(logged_in_user): create_sub_menu.addSeparator() update_task_action = \ update_sub_menu.addAction(u'\uf044 Update Task...') create_child_task_action = \ create_sub_menu.addAction( u'\uf0ae Create Child Task...' ) duplicate_task_hierarchy_action = \ create_sub_menu.addAction( u'\uf0c5 Duplicate Task Hierarchy...' ) delete_task_action = \ menu.addAction(u'\uf1f8 Delete Task...') # create the status_menu status_menu = update_sub_menu.addMenu('Status') fix_task_status_action = \ status_menu.addAction(u'\uf0e8 Fix Task Status') assert isinstance(status_menu, QtWidgets.QMenu) status_menu.addSeparator() # get all task statuses from anima import defaults menu_style_sheet = '' defaults_status_colors = defaults.status_colors for status_code in defaults.status_colors: change_status_menu_action = \ status_menu.addAction(status_code) change_status_menu_action.setObjectName('status_%s' % status_code) change_status_menu_actions.append( change_status_menu_action) menu_style_sheet = "%s %s" % ( menu_style_sheet, "QMenu#status_%s { background: %s %s %s}" % ( status_code, defaults_status_colors[status_code][0], defaults_status_colors[status_code][1], defaults_status_colors[status_code][2], )) # change the BG Color of the status status_menu.setStyleSheet(menu_style_sheet) try: # PySide and PySide2 accepted = QtWidgets.QDialog.DialogCode.Accepted except AttributeError: # PyQt4 accepted = QtWidgets.QDialog.Accepted selected_item = menu.exec_(global_position) if selected_item: if create_project_action \ and selected_item == create_project_action: from anima.ui import project_dialog project_main_dialog = project_dialog.MainDialog(parent=self, project=None) project_main_dialog.exec_() result = project_main_dialog.result() # refresh the task list if result == accepted: self.fill() project_main_dialog.deleteLater() if entity: from anima import defaults url = 'http://%s/%ss/%s/view' % ( defaults.stalker_server_internal_address, entity.entity_type.lower(), entity.id) if selected_item is open_in_web_browser_action: import webbrowser webbrowser.open(url) elif selected_item is open_in_file_browser_action: from anima import utils try: utils.open_browser_in_location(entity.absolute_path) except IOError as e: QtWidgets.QMessageBox.critical( self, "Error", "%s" % e, QtWidgets.QMessageBox.Ok) elif selected_item is copy_url_action: clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(url) # and warn the user about a new version is created and the # clipboard is set to the new version full path QtWidgets.QMessageBox.warning( self, "URL Copied To Clipboard", "URL:<br><br>%s<br><br>is copied to clipboard!" % url, QtWidgets.QMessageBox.Ok) elif selected_item is copy_id_to_clipboard: clipboard = QtWidgets.QApplication.clipboard() clipboard.setText('%s' % entity.id) # and warn the user about a new version is created and the # clipboard is set to the new version full path QtWidgets.QMessageBox.warning( self, "ID Copied To Clipboard", "ID %s is copied to clipboard!" % entity.id, QtWidgets.QMessageBox.Ok) elif selected_item is create_time_log_action: from anima.ui import time_log_dialog time_log_dialog_main_dialog = time_log_dialog.MainDialog( parent=self, task=entity, ) time_log_dialog_main_dialog.exec_() result = time_log_dialog_main_dialog.result() time_log_dialog_main_dialog.deleteLater() if result == accepted: # refresh the task list if item.parent: item.parent.reload() else: self.fill() # reselect the same task self.find_and_select_entity_item(entity) elif selected_item is update_task_action: from anima.ui import task_dialog task_main_dialog = task_dialog.MainDialog(parent=self, task=entity) task_main_dialog.exec_() result = task_main_dialog.result() task_main_dialog.deleteLater() # refresh the task list if result == accepted: # just reload the same item if item.parent: item.parent.reload() else: # reload the entire self.fill() self.find_and_select_entity_item(entity) elif selected_item == create_child_task_action: from anima.ui import task_dialog task_main_dialog = task_dialog.MainDialog( parent=self, parent_task=entity) task_main_dialog.exec_() result = task_main_dialog.result() task = task_main_dialog.task task_main_dialog.deleteLater() if result == accepted and task: # reload the parent item if item.parent: item.parent.reload() else: self.fill() self.find_and_select_entity_item(task) elif selected_item is duplicate_task_hierarchy_action: duplicate_task_hierarchy_dialog = \ DuplicateTaskHierarchyDialog( parent=self, duplicated_task_name=item.task.name ) duplicate_task_hierarchy_dialog.exec_() result = duplicate_task_hierarchy_dialog.result() if result == accepted: new_task_name = \ duplicate_task_hierarchy_dialog.line_edit.text() keep_resources = \ duplicate_task_hierarchy_dialog\ .check_box.checkState() from anima import utils from stalker import Task task = Task.query.get(item.task.id) new_task = utils.duplicate_task_hierarchy( task, None, new_task_name, description='Duplicated from Task(%s)' % task.id, user=logged_in_user, keep_resources=keep_resources) if new_task: from stalker.db.session import DBSession DBSession.commit() item.parent.reload() self.find_and_select_entity_item(new_task) elif selected_item is delete_task_action: answer = QtWidgets.QMessageBox.question( self, 'Delete Task?', "Delete the task and children?<br><br>(NO UNDO!!!!)", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from stalker.db.session import DBSession from stalker import Task task = Task.query.get(item.task.id) DBSession.delete(task) DBSession.commit() # reload the parent if item.parent: item.parent.reload() else: self.fill() self.find_and_select_entity_item(item.parent.task) elif selected_item == create_project_structure_action: answer = QtWidgets.QMessageBox.question( self, 'Create Project Folder Structure?', "This will create project folders, OK?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from anima import utils try: utils.create_project_structure(entity) except Exception as e: pass finally: QtWidgets.QMessageBox.information( self, 'Project Folder Structure is created!', 'Project Folder Structure is created!', ) else: return elif selected_item == create_task_structure_action: answer = QtWidgets.QMessageBox.question( self, 'Create Folder Structure?', "This will create task folders, OK?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from anima import utils try: utils.create_task_structure(entity) except Exception as e: pass finally: QtWidgets.QMessageBox.information( self, 'Folder Structure is created!', 'Folder Structure is created!', ) else: return elif selected_item == fix_task_status_action: from stalker import Task if isinstance(entity, Task): from anima import utils utils.fix_task_statuses(entity) from stalker.db.session import DBSession DBSession.add(entity) DBSession.commit() if item.parent: item.parent.reload() elif selected_item == update_project_action: from anima.ui import project_dialog project_main_dialog = project_dialog.MainDialog( parent=self, project=entity) project_main_dialog.exec_() result = project_main_dialog.result() # refresh the task list if result == accepted: self.fill() # reselect the same task self.find_and_select_entity_item(entity) project_main_dialog.deleteLater() elif selected_item == assign_users_action: from anima.ui import project_users_dialog project_users_main_dialog = \ project_users_dialog.MainDialog( parent=self, project=entity ) project_users_main_dialog.exec_() result = project_users_main_dialog.result() project_users_main_dialog.deleteLater() elif selected_item in change_status_menu_actions: # get the status code status_code = selected_item.text() from sqlalchemy import func status = \ Status.query.filter( func.lower(Status.code) == func.lower(status_code) ).first() # change the status of the entity # if it is a leaf task # if it doesn't have any dependent_of # assert isinstance(entity, Task) if isinstance(entity, Task): if entity.is_leaf and not entity.dependent_of: # then we can update it entity.status = status # # fix other task statuses # from anima import utils # utils.fix_task_statuses(entity) # refresh the tree from stalker.db.session import DBSession DBSession.add(entity) DBSession.commit() if item.parent: item.parent.reload() self.find_and_select_entity_item(entity) else: try: # go to the dependencies dep_task = selected_item.task self.find_and_select_entity_item(dep_task, self) except AttributeError: pass
def generate_video_thumbnail(self, file_full_path): """Generates a thumbnail for the given video link :param str file_full_path: A string showing the full path of the video file. """ # TODO: split this in to two different methods, one generating # thumbnails from the video and another one accepting three # images media_info = self.get_video_info(file_full_path) video_info = media_info['video_info'] # get the correct stream video_stream = None for stream in media_info['stream_info']: if stream['codec_type'] == 'video': video_stream = stream nb_frames = video_stream.get('nb_frames') if nb_frames is None or nb_frames == 'N/A': # no nb_frames # first try to use "r_frame_rate" and "duration" frame_rate = video_stream.get('r_frame_rate') if frame_rate is None: # still no frame rate # try to use the video_info and duration # and try to get frame rate frame_rate = float(video_info.get('TAG:framerate', 23.976)) else: # check if it is in Number/Number format if '/' in frame_rate: nominator, denominator = frame_rate.split('/') frame_rate = float(nominator) / float(denominator) # get duration duration = video_stream.get('duration') if duration == 'N/A': # no duration duration = float(video_info.get('duration', 1)) else: duration = float(duration) # at this stage we should have enough info, may not be correct but # we should have something # calculate nb_frames logger.debug('duration : %s' % duration) logger.debug('frame_rate: %s' % frame_rate) nb_frames = int(duration * frame_rate) nb_frames = int(nb_frames) start_thumb_path = tempfile.mktemp(suffix=self.thumbnail_format) mid_thumb_path = tempfile.mktemp(suffix=self.thumbnail_format) end_thumb_path = tempfile.mktemp(suffix=self.thumbnail_format) thumbnail_path = tempfile.mktemp(suffix=self.thumbnail_format) # generate three thumbnails from the start, middle and end of the file start_frame = int(nb_frames * 0.10) mid_frame = int(nb_frames * 0.5) end_frame = int(nb_frames * 0.90) - 1 # start_frame self.ffmpeg( **{ 'i': file_full_path, 'vf': "select='eq(n,0)'", 'vframes': start_frame, 'o': start_thumb_path }) # mid_frame self.ffmpeg( **{ 'i': file_full_path, 'vf': "select='eq(n,%s)'" % mid_frame, 'vframes': 1, 'o': mid_thumb_path }) # end_frame self.ffmpeg( **{ 'i': file_full_path, 'vf': "select='eq(n,%s)'" % end_frame, 'vframes': 1, 'o': end_thumb_path }) # check if all of the thumbnails are present if not os.path.exists(start_thumb_path): if os.path.exists(mid_thumb_path): start_thumb_path = mid_thumb_path elif os.path.exists(end_thumb_path): start_thumb_path = end_thumb_path mid_thumb_path = end_thumb_path if not os.path.exists(mid_thumb_path): if os.path.exists(start_thumb_path): mid_thumb_path = start_thumb_path else: start_thumb_path = end_thumb_path mid_thumb_path = end_thumb_path if not os.path.exists(end_thumb_path): # use the mid frame if available or the start frame if os.path.exists(mid_thumb_path): end_thumb_path = mid_thumb_path else: mid_thumb_path = start_thumb_path end_thumb_path = start_thumb_path # now merge them self.ffmpeg( **{ 'i': [start_thumb_path, mid_thumb_path, end_thumb_path], 'filter_complex': '[0:0]scale=3*%(tw)s/4:-1,pad=%(tw)s:%(th)s[s];' '[1:0]scale=3*%(tw)s/4:-1,fade=out:300:30:alpha=1[m];' '[2:0]scale=3*%(tw)s/4:-1,fade=out:300:30:alpha=1[e];' '[s][e]overlay=%(tw)s/4:%(th)s-h[x];' '[x][m]overlay=%(tw)s/8:%(th)s/2-h/2' % { 'tw': self.thumbnail_width, 'th': self.thumbnail_height }, 'o': thumbnail_path }) # remove the intermediate data try: os.remove(start_thumb_path) except OSError: pass try: os.remove(mid_thumb_path) except OSError: pass try: os.remove(end_thumb_path) except OSError: pass return thumbnail_path
def ldap_server(): """creates a mock ldap server for tests """ global __here__ # set some default ldap settings from anima import defaults defaults.enable_ldap_authentication = True defaults.ldap_server_address = 'localhost' defaults.ldap_base_dn = 'DC=animagpu,DC=local' # create mapping from LDAP groups to Stalker Groups defaults.ldap_user_group_map = { 'Administrators': 'admins', 'Users': 'Normal Users', 'GPU Users': 'Normal Users', 'GPU Users Admin': 'Power Users' } from ldap3 import Server, Connection, MOCK_SYNC test_ldap_server_info_path = os.path.join(__here__, 'data', 'LDAP_server_info.json') test_ldap_server_schema_path = os.path.join(__here__, 'data', 'LDAP_server_schema.json') test_ldap_server_entries_path = os.path.join(__here__, 'data', 'LDAP_server_entries.json') # Mock Server with example data class MockServer(Server): def __init__(self, *args, **kwargs): logger.debug("Initializing the Mock Server!") super(MockServer, self).__init__(*args, **kwargs) # load the data logger.debug("Loading mock server data") self.from_definition('fake_server', test_ldap_server_info_path, test_ldap_server_schema_path) # Mock the Connection class to always use MOCK_SYNC as strategy # and fill it with fake data class MockConnection(Connection): def __init__(self, *args, **kwargs): logger.debug("Initializing the Mock Connection!") kwargs['client_strategy'] = MOCK_SYNC super(MockConnection, self).__init__(*args, **kwargs) # load the mock data logger.debug("Loading mock connection data") logger.debug("test_ldap_server_entries_path: %s" % test_ldap_server_entries_path) self.strategy.entries_from_json(test_ldap_server_entries_path) # load a fake user for Simple binding logger.debug("Loading a fake user for Simple binding") self.strategy.add_entry( defaults.ldap_base_dn, { "cn": "admin", "codePage": 0, "displayName": "admin", "distinguishedName": "CN=admin,OU=GPU Users Admin,DC=animagpu,DC=local", "givenName": "admin", "instanceType": 4, "memberOf": ["CN=GPU Users Admin,CN=Users,DC=animagpu,DC=local"], "name": "admin", "objectCategory": "CN=Person,CN=Schema,CN=Configuration,DC=animagpu,DC=local", # "objectCategory": "CN=Person,CN=Schema,CN=Configuration,%s" % defaults.ldap_base_dn, "objectClass": ["top", "person", "organizationalPerson", "user"], "objectGUID": "{9d96ef4a-14e7-4a77-b5b1-97b2fa239f9f}", "objectSid": "S-1-5-21-2227021422-3894238547-674366654-1131", "primaryGroupID": 513, "revision": 0, "sAMAccountName": "admin", "sAMAccountType": 805306368, "sn": "admin", "uSNChanged": 423892, "uSNCreated": 314308, "userAccountControl": 66048, "userPassword": "******", "userPrincipalName": "*****@*****.**", }) def bind(self, *args, **kwargs): """mock the bind return value """ super(MockConnection, self).bind(*args, **kwargs) # always return True if the user is "pipeline" allowed_users = [ "pipeline", "CN=Pipeline,CN=Users,DC=animagpu,DC=local", "CN=admin,OU=GPU Users Admin,DC=animagpu,DC=local" ] if self.user in allowed_users and self.password == "password": return True return False logger.debug("Replacing original ldap3.Server class") import ldap3 orig_server_class = ldap3.Server ldap3.Server = MockServer logger.debug("ldap3.Server: %s" % ldap3.Server) logger.debug("Replacing original ldap3.Connection class") orig_connection_class = ldap3.Connection ldap3.Connection = MockConnection logger.debug("ldap3.Connection: %s" % ldap3.Connection) yield MockServer, MockConnection # restore the class logger.debug("Restoring original ldap3.Server class") ldap3.Server = orig_server_class logger.debug("ldap3.Server: %s" % ldap3.Server) logger.debug("Restoring original ldap3.Connection class") ldap3.Connection = orig_connection_class logger.debug("ldap3.Connection: %s" % ldap3.Connection)
def show_context_menu(self, position): """the custom context menu """ # convert the position to global screen position global_position = self.mapToGlobal(position) index = self.indexAt(position) model = self.model() item = model.itemFromIndex(index) logger.debug('itemAt(position) : %s' % item) task_id = None entity = None # if not item: # return # if item and not hasattr(item, 'task'): # return # from anima.ui.models.task import TaskItem # if not isinstance(item, TaskItem): # return if item: try: if item.task: task_id = item.task.id except AttributeError: return # if not task_id: # return from anima import utils file_browser_name = utils.file_browser_name() # create the menu menu = QtWidgets.QMenu() # Open in browser # ----------------------------------- # actions created in different scopes create_time_log_action = None create_project_action = None update_task_action = None upload_thumbnail_action = None create_child_task_action = None duplicate_task_hierarchy_action = None delete_task_action = None export_to_json_action = None import_from_json_action = None no_deps_action = None create_project_structure_action = None update_project_action = None assign_users_action = None open_in_web_browser_action = None open_in_file_browser_action = None copy_url_action = None copy_id_to_clipboard = None fix_task_status_action = None change_status_menu_actions = [] from anima import defaults from stalker import LocalSession local_session = LocalSession() logged_in_user = local_session.logged_in_user from stalker import SimpleEntity, Task, Project # TODO: Update this to use only task_id if task_id: entity = SimpleEntity.query.get(task_id) reload_action = menu.addAction(u'\uf0e8 Reload') # sub menus create_sub_menu = menu.addMenu('Create') update_sub_menu = menu.addMenu('Update') if defaults.is_power_user(logged_in_user): # create the Create Project menu item create_project_action = create_sub_menu.addAction( u'\uf0e8 Create Project...') if isinstance(entity, Project): # this is a project! if defaults.is_power_user(logged_in_user): update_project_action = update_sub_menu.addAction( u'\uf044 Update Project...') assign_users_action = menu.addAction( u'\uf0c0 Assign Users...') create_project_structure_action = create_sub_menu.addAction( u'\uf115 Create Project Structure') create_child_task_action = create_sub_menu.addAction( u'\uf0ae Create Child Task...') # Export and Import JSON create_sub_menu.addSeparator() # export_to_json_action = create_sub_menu.addAction(u'\uf1f8 Export To JSON...') import_from_json_action = create_sub_menu.addAction( u'\uf1f8 Import From JSON...') if entity: # separate the Project and Task related menu items menu.addSeparator() open_in_web_browser_action = menu.addAction( u'\uf14c Open In Web Browser...') open_in_file_browser_action = menu.addAction( u'\uf07c Browse Folders...') copy_url_action = menu.addAction(u'\uf0c5 Copy URL') copy_id_to_clipboard = menu.addAction( u'\uf0c5 Copy ID to clipboard') if isinstance(entity, Task): # this is a task create_project_structure_action = create_sub_menu.addAction( u'\uf115 Create Task Folder Structure') task = entity from stalker import Status status_wfd = Status.query.filter(Status.code == 'WFD').first() status_prev = Status.query.filter( Status.code == 'PREV').first() status_cmpl = Status.query.filter( Status.code == 'CMPL').first() if logged_in_user in task.resources and task.status not in [ status_wfd, status_prev, status_cmpl ]: create_sub_menu.addSeparator() create_time_log_action = create_sub_menu.addAction( u'\uf073 Create TimeLog...') # Add Depends To menu menu.addSeparator() depends = task.depends if depends: depends_to_menu = menu.addMenu(u'\uf090 Depends To') for dTask in depends: action = depends_to_menu.addAction(dTask.name) action.task = dTask # Add Dependent Of Menu dependent_of = task.dependent_of if dependent_of: dependent_of_menu = menu.addMenu(u'\uf08b Dependent Of') for dTask in dependent_of: action = dependent_of_menu.addAction(dTask.name) action.task = dTask if not depends and not dependent_of: no_deps_action = menu.addAction(u'\uf00d No Dependencies') no_deps_action.setEnabled(False) # update task and create child task menu items menu.addSeparator() if defaults.is_power_user(logged_in_user): create_sub_menu.addSeparator() update_task_action = update_sub_menu.addAction( u'\uf044 Update Task...') upload_thumbnail_action = update_sub_menu.addAction( u'\uf03e Upload Thumbnail...') # Export and Import JSON create_sub_menu.addSeparator() export_to_json_action = create_sub_menu.addAction( u'\uf1f8 Export To JSON...') import_from_json_action = create_sub_menu.addAction( u'\uf1f8 Import From JSON...') create_sub_menu.addSeparator() create_child_task_action = create_sub_menu.addAction( u'\uf0ae Create Child Task...') duplicate_task_hierarchy_action = create_sub_menu.addAction( u'\uf0c5 Duplicate Task Hierarchy...') delete_task_action = menu.addAction( u'\uf1f8 Delete Task...') menu.addSeparator() # create the status_menu status_menu = update_sub_menu.addMenu('Status') fix_task_status_action = status_menu.addAction( u'\uf0e8 Fix Task Status') assert isinstance(status_menu, QtWidgets.QMenu) status_menu.addSeparator() # get all task statuses from anima import defaults menu_style_sheet = '' defaults_status_colors = defaults.status_colors for status_code in defaults.status_colors: change_status_menu_action = status_menu.addAction( status_code) change_status_menu_action.setObjectName('status_%s' % status_code) change_status_menu_actions.append( change_status_menu_action) menu_style_sheet = "%s %s" % ( menu_style_sheet, "QMenu#status_%s { background: %s %s %s}" % ( status_code, defaults_status_colors[status_code][0], defaults_status_colors[status_code][1], defaults_status_colors[status_code][2], )) # change the BG Color of the status status_menu.setStyleSheet(menu_style_sheet) try: # PySide and PySide2 accepted = QtWidgets.QDialog.DialogCode.Accepted except AttributeError: # PyQt4 accepted = QtWidgets.QDialog.Accepted selected_action = menu.exec_(global_position) if selected_action: if selected_action is reload_action: if isinstance(entity, Project): self.fill() self.find_and_select_entity_item(item.task) else: for item in self.get_selected_task_items(): item.reload() if create_project_action \ and selected_action is create_project_action: from anima.ui import project_dialog project_main_dialog = project_dialog.MainDialog(parent=self, project=None) project_main_dialog.exec_() result = project_main_dialog.result() # refresh the task list if result == accepted: self.fill() project_main_dialog.deleteLater() if entity: from anima import defaults url = 'http://%s/%ss/%s/view' % ( defaults.stalker_server_internal_address, entity.entity_type.lower(), entity.id) if selected_action is open_in_web_browser_action: import webbrowser webbrowser.open(url) elif selected_action is open_in_file_browser_action: from anima import utils try: utils.open_browser_in_location(entity.absolute_path) except IOError as e: QtWidgets.QMessageBox.critical( self, "Error", "%s" % e, QtWidgets.QMessageBox.Ok) elif selected_action is copy_url_action: clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(url) # and warn the user about a new version is created and the # clipboard is set to the new version full path QtWidgets.QMessageBox.warning( self, "URL Copied To Clipboard", "URL:<br><br>%s<br><br>is copied to clipboard!" % url, QtWidgets.QMessageBox.Ok) elif selected_action is copy_id_to_clipboard: clipboard = QtWidgets.QApplication.clipboard() selected_entity_ids = ', '.join( list(map(str, self.get_selected_task_ids()))) clipboard.setText(selected_entity_ids) # and warn the user about a new version is created and the # clipboard is set to the new version full path QtWidgets.QMessageBox.warning( self, "ID Copied To Clipboard", "IDs are copied to clipboard!<br>%s" % selected_entity_ids, QtWidgets.QMessageBox.Ok) elif selected_action is create_time_log_action: from anima.ui import time_log_dialog time_log_dialog_main_dialog = time_log_dialog.MainDialog( parent=self, task=entity, ) time_log_dialog_main_dialog.exec_() result = time_log_dialog_main_dialog.result() time_log_dialog_main_dialog.deleteLater() if result == accepted: # refresh the task list if item.parent: item.parent.reload() else: self.fill() # reselect the same task self.find_and_select_entity_item(entity) elif selected_action is update_task_action: from anima.ui import task_dialog task_main_dialog = task_dialog.MainDialog( parent=self, tasks=self.get_selected_tasks()) task_main_dialog.exec_() result = task_main_dialog.result() task_main_dialog.deleteLater() # refresh the task list if result == accepted: # just reload the same item if item.parent: item.parent.reload() else: # reload the entire self.fill() self.find_and_select_entity_item(entity) elif selected_action is upload_thumbnail_action: from anima.ui import utils as ui_utils thumbnail_full_path = ui_utils.choose_thumbnail( self, start_path=entity.absolute_path, dialog_title="Choose Thumbnail For: %s" % entity.name) # if the thumbnail_full_path is empty do not do anything if thumbnail_full_path == "": return # get the current task anima.utils.upload_thumbnail(entity, thumbnail_full_path) elif selected_action is create_child_task_action: from anima.ui import task_dialog task_main_dialog = task_dialog.MainDialog( parent=self, parent_task=entity) task_main_dialog.exec_() result = task_main_dialog.result() tasks = task_main_dialog.tasks task_main_dialog.deleteLater() if result == accepted and tasks: # reload the parent item if item.parent: item.parent.reload() else: self.fill() self.find_and_select_entity_item(tasks[0]) elif selected_action is duplicate_task_hierarchy_action: duplicate_task_hierarchy_dialog = \ DuplicateTaskHierarchyDialog( parent=self, duplicated_task_name=item.task.name ) duplicate_task_hierarchy_dialog.exec_() result = duplicate_task_hierarchy_dialog.result() if result == accepted: new_task_name = duplicate_task_hierarchy_dialog.line_edit.text( ) keep_resources = duplicate_task_hierarchy_dialog.check_box.checkState( ) from anima import utils from stalker import Task task = Task.query.get(item.task.id) new_task = utils.duplicate_task_hierarchy( task, None, new_task_name, description='Duplicated from Task(%s)' % task.id, user=logged_in_user, keep_resources=keep_resources) if new_task: from stalker.db.session import DBSession DBSession.commit() item.parent.reload() self.find_and_select_entity_item(new_task) elif selected_action is delete_task_action: answer = QtWidgets.QMessageBox.question( self, 'Delete Task?', "Delete the task and children?<br><br>(NO UNDO!!!!)", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from stalker.db.session import DBSession from stalker import Task tasks = self.get_selected_tasks() logger.debug("tasks : %s" % tasks) task = Task.query.get(item.task.id) # get the next sibling or the previous # to select after deletion select_task = item.parent.task if task.parent: all_siblings = list( Task.query.filter( Task.parent == task.parent).order_by( Task.name).all()) if len(all_siblings) > 1: sibling_index = all_siblings.index(task) if sibling_index < len(all_siblings) - 1: # this is not the last task in the list # select next one select_task = all_siblings[sibling_index + 1] elif sibling_index == len(all_siblings) - 1: # this is the last task # select previous task select_task = all_siblings[sibling_index - 1] for task in tasks: DBSession.delete(task) DBSession.commit() # reload the parent unique_parent_items = [] for item in self.get_selected_task_items(): if item.parent and item.parent not in unique_parent_items: unique_parent_items.append(item.parent) if unique_parent_items: for parent_item in unique_parent_items: parent_item.reload() else: self.fill() # either select the next or previous task or the parent self.find_and_select_entity_item(select_task) elif selected_action is export_to_json_action: # show a file browser dialog = QtWidgets.QFileDialog(self, "Choose file") dialog.setNameFilter("JSON Files (*.json)") dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) if dialog.exec_(): file_path = dialog.selectedFiles()[0] if file_path: import os import json from anima.utils import task_hierarchy_io # check file extension parts = os.path.splitext(file_path) if not parts[1]: file_path = '%s%s' % (parts[0], '.json') data = json.dumps( entity, cls=task_hierarchy_io.StalkerEntityEncoder, check_circular=False, indent=4) try: with open(file_path, 'w') as f: f.write(data) except Exception as e: pass finally: QtWidgets.QMessageBox.information( self, 'Task data Export to JSON!', 'Task data Export to JSON!', ) elif selected_action is import_from_json_action: # show a file browser dialog = QtWidgets.QFileDialog(self, "Choose file") dialog.setNameFilter("JSON Files (*.json)") dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) if dialog.exec_(): file_path = dialog.selectedFiles()[0] if file_path: import json with open(file_path) as f: data = json.load(f) from anima.utils import task_hierarchy_io if isinstance(entity, Task): project = entity.project elif isinstance(entity, Project): project = entity parent = None if isinstance(entity, Task): parent = entity decoder = \ task_hierarchy_io.StalkerEntityDecoder( project=project ) loaded_entity = decoder.loads(data, parent=parent) try: from stalker.db.session import DBSession DBSession.add(loaded_entity) DBSession.commit() except Exception as e: QtWidgets.QMessageBox.critical( self, "Error!", "%s" % e, QtWidgets.QMessageBox.Ok) else: item.reload() QtWidgets.QMessageBox.information( self, 'New Tasks are created!', 'New Tasks are created', ) elif selected_action is create_project_structure_action: answer = QtWidgets.QMessageBox.question( self, 'Create Project Folder Structure?', "This will create project folders, OK?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from anima import utils try: for task in self.get_selected_tasks(): utils.create_structure(task) except Exception as e: QtWidgets.QMessageBox.critical( self, "Error", str(e)) finally: QtWidgets.QMessageBox.information( self, 'Project Folder Structure is created!', 'Project Folder Structure is created!', ) else: return elif selected_action is fix_task_status_action: from stalker.db.session import DBSession from stalker import SimpleEntity, Task from anima import utils for entity in self.get_selected_tasks(): if isinstance(entity, Task): utils.fix_task_statuses(entity) utils.fix_task_computed_time(entity) DBSession.add(entity) DBSession.commit() unique_parent_items = [] for item in self.get_selected_task_items(): if item.parent and item.parent not in unique_parent_items: unique_parent_items.append(item.parent) for parent_item in unique_parent_items: parent_item.reload() elif selected_action is update_project_action: from anima.ui import project_dialog project_main_dialog = project_dialog.MainDialog( parent=self, project=entity) project_main_dialog.exec_() result = project_main_dialog.result() # refresh the task list if result == accepted: self.fill() # reselect the same task self.find_and_select_entity_item(entity) project_main_dialog.deleteLater() elif selected_action is assign_users_action: from anima.ui import project_users_dialog project_users_main_dialog = \ project_users_dialog.MainDialog( parent=self, project=entity ) project_users_main_dialog.exec_() result = project_users_main_dialog.result() project_users_main_dialog.deleteLater() elif selected_action in change_status_menu_actions: # get the status code status_code = selected_action.text() from sqlalchemy import func status = Status.query.filter( func.lower(Status.code) == func.lower( status_code)).first() # change the status of the entity # if it is a leaf task # if it doesn't have any dependent_of # assert isinstance(entity, Task) for task in self.get_selected_tasks(): if task.is_leaf and not task.dependent_of: # then we can update it task.status = status # # fix other task statuses # from anima import utils # utils.fix_task_statuses(entity) # refresh the tree from stalker.db.session import DBSession DBSession.add(task) DBSession.commit() if item.parent: item.parent.reload() self.find_and_select_entity_item(entity) else: try: # go to the dependencies dep_task = selected_action.task self.find_and_select_entity_item(dep_task, self) except AttributeError: pass
def draw_stalker_entity_menu_item(self, context): """draws one menu item """ logger.debug('entity_id : %s' % self.stalker_entity_id) logger.debug('entity_name : %s' % self.stalker_entity_name)
# -*- coding: utf-8 -*- # Copyright (c) 2012-2015, Anima Istanbul # # This module is part of anima-tools and is released under the BSD 2 # License: http://www.opensource.org/licenses/BSD-2-Clause from anima import logger from anima.ui import IS_PYSIDE, IS_PYQT4 if IS_PYQT4(): logger.debug('importing PyQt4') import sip sip.setapi('QString', 2) sip.setapi('QVariant', 2) from PyQt4 import QtGui, QtCore elif IS_PYSIDE(): logger.debug('importing PySide') from PySide import QtGui, QtCore
def get_icon(icon_name): """Returns an icon from ui library """ import time start_time = time.time() # get the icon from cache if possible from anima.ui import ICON_CACHE q_icon = ICON_CACHE.get(icon_name) if not q_icon: logger.debug("getting icon from the cache!") # use the local icon cache import os from anima import defaults local_icon_cache_path = os.path.normpath( os.path.expanduser( os.path.join(defaults.local_cache_folder, "icons"))) local_icon_full_path = os.path.join(local_icon_cache_path, "%s.png" % icon_name) logger.debug("local_icon_full_path: %s" % local_icon_full_path) if not os.path.exists(local_icon_full_path): logger.debug("local icon cache not found: %s" % icon_name) logger.debug("retrieving icon from library!") here = os.path.abspath(os.path.dirname(__file__)) images_path = os.path.join(here, 'images') icon_full_path = os.path.join(images_path, "%s.png" % icon_name) logger.debug("icon_full_path: %s" % icon_full_path) # copy to local cache folder try: os.makedirs(local_icon_cache_path) except OSError: pass import shutil try: shutil.copy(icon_full_path, local_icon_full_path) except OSError: # original icon doesn't exist pass q_icon = QtGui.QIcon(local_icon_full_path) ICON_CACHE[icon_name] = q_icon logger.debug("get_icon took: %0.6f s" % (time.time() - start_time)) return q_icon
def load_font(font_filename): """loads extra fonts from the fonts folder """ import time start_time = time.time() # get the font from cache if possible from anima.ui import FONT_CACHE font_family = FONT_CACHE.get(font_filename) if not font_family: logger.debug("font not found in runtime cache!") logger.debug("getting font from local cache!") # use the local font cache import os from anima import defaults local_font_cache_path = os.path.normpath( os.path.expanduser( os.path.join(defaults.local_cache_folder, "fonts"))) local_font_full_path = os.path.join(local_font_cache_path, font_filename) logger.debug("local_font_full_path: %s" % local_font_full_path) if not os.path.exists(local_font_full_path): logger.debug("local font cache not found: %s" % font_filename) logger.debug("retrieving font from library!") here = os.path.dirname(os.path.realpath(__file__)) font_full_path = os.path.join(here, 'fonts', font_filename) logger.debug("font_full_path: %s" % font_full_path) # copy to local cache folder try: os.makedirs(local_font_cache_path) except OSError: pass import shutil try: shutil.copy(font_full_path, local_font_full_path) except OSError: # original font doesn't exist either pass font_id = QtGui.QFontDatabase.addApplicationFont(local_font_full_path) font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id) FONT_CACHE[font_filename] = font_family else: logger.debug("font found in runtime cache!") logger.debug("load_font took: %0.6f s" % (time.time() - start_time)) return font_family
def _move_file_and_fix_references(self, path, project_path, scenes_folder='scenes', refs_folder='scenes/refs'): """Moves the given maya file to the given project path and moves any references of it to :param str path: The path of the maya file :param str project_path: The project path :param str scenes_folder: The scenes folder to store the original maya scene. :param str refs_folder: The references folder to replace reference paths with. :return list: returns a list of paths """ # fix any env vars path = os.path.expandvars(path) original_file_name = os.path.basename(path) logger.debug('original_file_name: %s' % original_file_name) new_file_path = \ os.path.join(project_path, scenes_folder, original_file_name) scenes_folder_lut = { '.ma': 'scenes/refs', # image files '.jpg': 'sourceimages', '.png': 'sourceimages', '.tif': 'sourceimages', '.tga': 'sourceimages', '.exr': 'sourceimages', '.hdr': 'sourceimages', # RSProxy and arnold proxies '.rs': 'sourceimages', '.ass': 'sourceimages', } ref_paths = [] # skip the file if it doesn't exist if not os.path.exists(path): # return early return ref_paths # only get new ref paths for '.ma' files if path.endswith('.ma'): # read the data of the original file with open(path) as f: data = f.read() ref_paths = self._extract_references(data) # fix all reference paths for ref_path in ref_paths: ref_ext = os.path.splitext(ref_path)[-1] data = data.replace( ref_path, '%s/%s' % (scenes_folder_lut.get( ref_ext, refs_folder), os.path.basename(ref_path))) # now write all the data back to a new temp scene with open(new_file_path, 'w+') as f: f.write(data) else: # fix for UDIM texture paths # if the path contains 1001 or u1_v1 than find the other # textures # dirty patch # move image files in to the sourceimages folder # along with the RedshiftProxy files file_extension = os.path.splitext(path)[1] new_file_path = \ os.path.join( project_path, scenes_folder_lut.get( file_extension, refs_folder ), original_file_name ) import glob new_file_paths = [new_file_path] if '1001' in new_file_path or 'u1_v1' in new_file_path.lower(): # get the rest of the textures new_file_paths = glob.glob( new_file_path.replace('1001', '*').replace( 'u1_v1', 'u*_v*').replace('U1_V1', 'U*_V*')) for p in new_file_paths: print(p) # just copy the file for new_file_path in new_file_paths: try: shutil.copy(path, new_file_path) except IOError: pass return ref_paths
def __init__(self, parent=None, task=None, timelog=None): logger.debug("initializing the interface") # store the logged in user self.logged_in_user = None self.no_time_left = False self.extended_hours = None self.extended_minutes = None self.timelog = timelog self.timelog_created = False self.task = task super(MainDialog, self).__init__(parent) self.setupUi(self) # customize the ui elements from anima.ui.widgets import TaskComboBox self.tasks_comboBox = TaskComboBox(self) self.tasks_comboBox.setObjectName("tasks_comboBox") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.tasks_comboBox) # self.start_timeEdit.deleteLater() from anima.ui.widgets import TimeEdit self.start_timeEdit = TimeEdit(self, resolution=timing_resolution) self.start_timeEdit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection) self.start_timeEdit.setCalendarPopup(True) self.start_timeEdit.setObjectName("start_timeEdit") self.start_timeEdit.setWrapping(True) self.formLayout.insertRow(4, self.label, self.start_timeEdit) self.start_timeEdit.setDisplayFormat("HH:mm") # self.end_timeEdit.deleteLater() self.end_timeEdit = TimeEdit(self, resolution=timing_resolution) self.end_timeEdit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection) self.end_timeEdit.setCalendarPopup(True) self.end_timeEdit.setObjectName("end_timeEdit") self.end_timeEdit.setWrapping(True) self.formLayout.insertRow(5, self.label_2, self.end_timeEdit) self.end_timeEdit.setDisplayFormat("HH:mm") current_time = QtCore.QTime.currentTime() # round the minutes to the resolution minute = current_time.minute() hour = current_time.hour() minute = int(minute / float(timing_resolution)) * timing_resolution current_time = QtCore.QTime(hour, minute) self.start_timeEdit.setTime(current_time) self.end_timeEdit.setTime(current_time.addSecs(timing_resolution * 60)) self.calendarWidget.resource_id = -1 # setup signals self._setup_signals() # setup defaults self._set_defaults() # center window self.center_window()
def show_context_menu(self, position): """the custom context menu """ # convert the position to global screen position global_position = self.mapToGlobal(position) index = self.indexAt(position) model = self.model() item = model.itemFromIndex(index) logger.debug('itemAt(position) : %s' % item) task_id = None entity = None # if not item: # return # if item and not hasattr(item, 'task'): # return if item and item.task: task_id = item.task.id # if not task_id: # return from anima import utils file_browser_name = utils.file_browser_name() # create the menu menu = QtWidgets.QMenu() # Open in browser # ----------------------------------- # actions created in different scopes create_time_log_action = None create_project_action = None update_task_action = None create_child_task_action = None duplicate_task_hierarchy_action = None delete_task_action = None no_deps_action = None create_project_structure_action = None create_task_structure_action = None update_project_action = None open_in_web_browser_action = None open_in_file_browser_action = None copy_url_action = None copy_id_to_clipboard = None from anima import defaults from stalker import LocalSession local_session = LocalSession() logged_in_user = local_session.logged_in_user from stalker import SimpleEntity, Task, Project # TODO: Update this to use only task_id if task_id: entity = SimpleEntity.query.get(task_id) if defaults.is_power_user(logged_in_user): # create the Create Project menu item create_project_action = menu.addAction(u'\uf0e8 Create Project...') if isinstance(entity, Project): # this is a project! if defaults.is_power_user(logged_in_user): update_project_action = \ menu.addAction(u'\uf044 Update Project...') create_project_structure_action = \ menu.addAction(u'\uf115 Create Project Structure') create_child_task_action = \ menu.addAction(u'\uf0ae Create Child Task...') if entity: # separate the Project and Task related menu items menu.addSeparator() open_in_web_browser_action = \ menu.addAction(u'\uf14c Open In Web Browser...') open_in_file_browser_action = \ menu.addAction(u'\uf07c Open In %s...' % file_browser_name) copy_url_action = menu.addAction(u'\uf0c5 Copy URL') copy_id_to_clipboard = \ menu.addAction(u'\uf0c5 Copy ID to clipboard') if isinstance(entity, Task): # this is a task create_task_structure_action = \ menu.addAction(u'\uf115 Create Task Structure') task = entity from stalker import Status status_wfd = Status.query.filter(Status.code == 'WFD').first() status_prev = \ Status.query.filter(Status.code == 'PREV').first() status_cmpl = \ Status.query.filter(Status.code == 'CMPL').first() if logged_in_user in task.resources \ and task.status not in [status_wfd, status_prev, status_cmpl]: menu.addSeparator() create_time_log_action = \ menu.addAction(u'\uf073 Create TimeLog...') # update task and create child task menu items if defaults.is_power_user(logged_in_user): menu.addSeparator() update_task_action = \ menu.addAction(u'\uf044 Update Task...') create_child_task_action = \ menu.addAction(u'\uf0ae Create Child Task...') duplicate_task_hierarchy_action = \ menu.addAction(u'\uf0c5 Duplicate Task Hierarchy...') delete_task_action = \ menu.addAction(u'\uf1f8 Delete Task...') menu.addSeparator() # Add Depends To menu depends = task.depends if depends: depends_to_menu = menu.addMenu(u'\uf090 Depends To') for dTask in depends: action = depends_to_menu.addAction(dTask.name) action.task = dTask # Add Dependent Of Menu dependent_of = task.dependent_of if dependent_of: dependent_of_menu = menu.addMenu(u'\uf08b Dependent Of') for dTask in dependent_of: action = dependent_of_menu.addAction(dTask.name) action.task = dTask if not depends and not dependent_of: no_deps_action = menu.addAction(u'\uf00d No Dependencies') no_deps_action.setEnabled(False) try: # PySide and PySide2 accepted = QtWidgets.QDialog.DialogCode.Accepted except AttributeError: # PyQt4 accepted = QtWidgets.QDialog.Accepted selected_item = menu.exec_(global_position) if selected_item: if create_project_action \ and selected_item == create_project_action: from anima.ui import project_dialog project_main_dialog = project_dialog.MainDialog(parent=self, project=None) project_main_dialog.exec_() result = project_main_dialog.result() # refresh the task list if result == accepted: self.fill() project_main_dialog.deleteLater() if entity: from anima import defaults url = 'http://%s/%ss/%s/view' % ( defaults.stalker_server_internal_address, entity.entity_type.lower(), entity.id) if selected_item is open_in_web_browser_action: import webbrowser webbrowser.open(url) elif selected_item is open_in_file_browser_action: from anima import utils utils.open_browser_in_location(entity.absolute_path) elif selected_item is copy_url_action: clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(url) # and warn the user about a new version is created and the # clipboard is set to the new version full path QtWidgets.QMessageBox.warning( self, "URL Copied To Clipboard", "URL:<br><br>%s<br><br>is copied to clipboard!" % url, QtWidgets.QMessageBox.Ok) elif selected_item is copy_id_to_clipboard: clipboard = QtWidgets.QApplication.clipboard() clipboard.setText('%s' % entity.id) # and warn the user about a new version is created and the # clipboard is set to the new version full path QtWidgets.QMessageBox.warning( self, "ID Copied To Clipboard", "ID %s is copied to clipboard!" % entity.id, QtWidgets.QMessageBox.Ok) elif selected_item is create_time_log_action: from anima.ui import time_log_dialog time_log_dialog_main_dialog = time_log_dialog.MainDialog( parent=self, task=entity, ) time_log_dialog_main_dialog.exec_() result = time_log_dialog_main_dialog.result() time_log_dialog_main_dialog.deleteLater() if result == accepted: # refresh the task list if item.parent: item.parent.reload() else: self.fill() # reselect the same task self.find_and_select_entity_item(entity) elif selected_item is update_task_action: from anima.ui import task_dialog task_main_dialog = task_dialog.MainDialog(parent=self, task=entity) task_main_dialog.exec_() result = task_main_dialog.result() task_main_dialog.deleteLater() # refresh the task list if result == accepted: # just reload the same item if item.parent: item.parent.reload() else: # reload the entire self.fill() self.find_and_select_entity_item(entity) elif selected_item == create_child_task_action: from anima.ui import task_dialog task_main_dialog = task_dialog.MainDialog( parent=self, parent_task=entity) task_main_dialog.exec_() result = task_main_dialog.result() task = task_main_dialog.task task_main_dialog.deleteLater() if result == accepted and task: # reload the parent item if item.parent: item.parent.reload() else: self.fill() self.find_and_select_entity_item(task) elif selected_item is duplicate_task_hierarchy_action: new_task_name, result = QtWidgets.QInputDialog.getText( self, "Input Dialog", "Duplicated Task Name:", QtWidgets.QLineEdit.Normal, item.task.name) if result: from anima import utils from stalker import Task task = Task.query.get(item.task.id) new_task = utils.duplicate_task_hierarchy( task, None, new_task_name, description='Duplicated from Task(%s)' % task.id, user=logged_in_user) if new_task: from stalker.db.session import DBSession DBSession.commit() item.parent.reload() self.find_and_select_entity_item(new_task) elif selected_item is delete_task_action: answer = QtWidgets.QMessageBox.question( self, 'Delete Task?', "Delete the task and children?<br><br>(NO UNDO!!!!)", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from stalker.db.session import DBSession from stalker import Task task = Task.query.get(item.task.id) DBSession.delete(task) DBSession.commit() # reload the parent if item.parent: item.parent.reload() else: self.fill() self.find_and_select_entity_item(item.parent.task) elif selected_item == create_project_structure_action: answer = QtWidgets.QMessageBox.question( self, 'Create Project Structure?', "This will create project folders, OK?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from anima import utils try: utils.create_project_structure(entity) except Exception as e: pass finally: QtWidgets.QMessageBox.information( self, 'Project Structure is created!', 'Project Structure is created!', ) else: return elif selected_item == create_task_structure_action: answer = QtWidgets.QMessageBox.question( self, 'Create Task Structure?', "This will create task folders, OK?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.Yes: from anima import utils try: utils.create_task_structure(entity) except Exception as e: pass finally: QtWidgets.QMessageBox.information( self, 'Task Structure is created!', 'Task Structure is created!', ) else: return elif selected_item == update_project_action: from anima.ui import project_dialog project_main_dialog = project_dialog.MainDialog( parent=self, project=entity) project_main_dialog.exec_() result = project_main_dialog.result() # refresh the task list if result == accepted: self.fill() # reselect the same task self.find_and_select_entity_item(entity) project_main_dialog.deleteLater() else: # go to the dependencies dep_task = selected_item.task self.find_and_select_entity_item(dep_task, self)
def generate_gpu(self): """generates the GPU representation of the current scene """ # validate the version first self.version = self._validate_version(self.version) self.open_version(self.version) # load necessary plugins pm.loadPlugin('gpuCache') pm.loadPlugin('AbcExport') pm.loadPlugin('AbcImport') # check if all references have an GPU repr first refs_with_no_gpu_repr = [] for ref in pm.listReferences(): if ref.version and not ref.has_repr('GPU'): refs_with_no_gpu_repr.append(ref) if len(refs_with_no_gpu_repr): raise RuntimeError( 'Please generate the GPU Representation of the references ' 'first!!!\n%s' % '\n'.join(map(lambda x: str(x.path), refs_with_no_gpu_repr)) ) # unload all references for ref in pm.listReferences(): ref.unload() # for local models generate an ABC file output_path = os.path.join( self.version.absolute_path, 'Outputs/alembic/' ).replace('\\', '/') abc_command = \ 'AbcExport -j "-frameRange %(start_frame)s ' \ '%(end_frame)s ' \ '-ro -stripNamespaces ' \ '-uvWrite ' \ '-wholeFrameGeo ' \ '-worldSpace ' \ '-root |%(node)s -file %(file_path)s";' gpu_command = \ 'gpuCache -startTime %(start_frame)s ' \ '-endTime %(end_frame)s ' \ '-optimize -optimizationThreshold 40000 ' \ '-writeMaterials ' \ '-directory "%(path)s" ' \ '-fileName "%(filename)s" ' \ '%(node)s;' start_frame = end_frame = int(pm.currentTime(q=1)) if not self.is_scene_assembly_task(self.version.task): if self.is_vegetation_task(self.version.task): # in vegetation files, we export the GPU files directly from # the Base version, also we use the geometry under # "pfxPolygons" and parent the resulting Stand-In nodes to the # pfxPolygons # load all references for ref in pm.listReferences(): ref.load() # find the _pfxPolygons node pfx_polygons_node = pm.PyNode('kks___vegetation_pfxPolygons') for node in pfx_polygons_node.getChildren(): for child_node in node.getChildren(): child_node_name = child_node.name().split('___')[-1] child_node_shape = child_node.getShape() child_node_shape_name = None if child_node_shape: child_node_shape_name = child_node_shape.name() pm.select(child_node) temp_output_fullpath = \ tempfile.mktemp().replace('\\', '/') temp_output_path, temp_output_filename = \ os.path.split(temp_output_fullpath) output_filename = '%s_%s' % ( self.version.nice_name, child_node_name.split(':')[-1] .replace(':', '_') .replace('|', '_') ) # run the mel command # check if file exists pm.mel.eval( gpu_command % { 'start_frame': start_frame, 'end_frame': end_frame, 'node': child_node.fullPath(), 'path': temp_output_path, 'filename': temp_output_filename } ) cache_file_full_path = \ os.path\ .join(output_path, output_filename + '.abc')\ .replace('\\', '/') # create the intermediate directories try: os.makedirs( os.path.dirname(cache_file_full_path) ) except OSError: # directory exists pass # now move in to its place shutil.move( temp_output_fullpath + '.abc', cache_file_full_path ) # set rotate and scale pivots rp = pm.xform(child_node, q=1, ws=1, rp=1) sp = pm.xform(child_node, q=1, ws=1, sp=1) #child_node.setRotatePivotTranslation([0, 0, 0]) # delete the child and add a GPU node instead pm.delete(child_node) # check if file exists and create nodes if os.path.exists(cache_file_full_path): gpu_node = pm.createNode('gpuCache') gpu_node_tra = gpu_node.getParent() pm.parent(gpu_node_tra, node) gpu_node_tra.rename(child_node_name) if child_node_shape_name is not None: gpu_node.rename(child_node_shape_name) pm.xform(gpu_node_tra, ws=1, rp=rp) pm.xform(gpu_node_tra, ws=1, sp=sp) gpu_node.setAttr( 'cacheFileName', cache_file_full_path, type="string" ) else: print('File not found!: %s' % cache_file_full_path) # clean up other nodes pm.delete('kks___vegetation_pfxStrokes') pm.delete('kks___vegetation_paintableGeos') else: root_nodes = self.get_local_root_nodes() if len(root_nodes): for root_node in root_nodes: # export each child of each root as separate nodes for child_node in root_node.getChildren(): # check if it is a transform node if not isinstance(child_node, pm.nt.Transform): continue if not auxiliary.has_shape(child_node): continue child_name = child_node.name() child_shape = child_node.getShape() child_shape_name = None if child_shape: child_shape_name = child_shape.name() child_full_path = \ child_node.fullPath()[1:].replace('|', '_') temp_output_fullpath = \ tempfile.mktemp().replace('\\', '/') temp_output_path, temp_output_filename = \ os.path.split(temp_output_fullpath) output_filename =\ '%s_%s' % ( self.version.nice_name, child_full_path ) # run the mel command # check if file exists pm.mel.eval( gpu_command % { 'start_frame': start_frame, 'end_frame': end_frame, 'node': child_node.fullPath(), 'path': temp_output_path, 'filename': temp_output_filename } ) cache_file_full_path = \ os.path\ .join( output_path, '%s.abc' % ( output_filename ) )\ .replace('\\', '/') # create the intermediate directories try: os.makedirs( os.path.dirname(cache_file_full_path) ) except OSError: # directory exists pass # now move in to its place shutil.move( temp_output_fullpath + '.abc', cache_file_full_path ) # set rotate and scale pivots rp = pm.xform(child_node, q=1, ws=1, rp=1) sp = pm.xform(child_node, q=1, ws=1, sp=1) # rpt = child_node.getRotatePivotTranslation() # delete the child and add a GPU node instead pm.delete(child_node) # check if file exists if os.path.exists(cache_file_full_path): gpu_node = pm.createNode('gpuCache') gpu_node_tra = gpu_node.getParent() pm.parent(gpu_node_tra, root_node) gpu_node_tra.rename(child_name) if child_shape_name is not None: gpu_node.rename(child_shape_name) pm.xform(gpu_node_tra, ws=1, rp=rp) pm.xform(gpu_node_tra, ws=1, sp=sp) # child_node.setRotatePivotTranslation(rpt) gpu_node.setAttr( 'cacheFileName', cache_file_full_path, type="string" ) # load all references again # convert all references to GPU logger.debug('converting all references to GPU') for ref in pm.listReferences(): # check if this is a Model reference ref.to_repr('GPU') ref.load() # if this is an Exterior/Interior -> Layout -> Hires task flatten it task = self.version.task is_exterior_or_interior_task = self.is_exterior_or_interior_task(task) if is_exterior_or_interior_task: logger.debug('importing all references') # and import all of the references all_refs = pm.listReferences() while len(all_refs) != 0: for ref in all_refs: if not ref.isLoaded(): ref.load() ref.importContents() all_refs = pm.listReferences() # assign lambert1 to all GPU nodes pm.sets('initialShadingGroup', e=1, fe=auxiliary.get_root_nodes()) # clean up self.clean_up() # 6. save the scene as {{original_take}}___GPU # use maya take_name = '%s%s%s' % ( self.base_take_name, Representation.repr_separator, 'GPU' ) v = self.get_latest_repr_version(take_name) self.maya_env.save_as(v) # export the root nodes under the same file if is_exterior_or_interior_task: logger.debug('exporting root nodes') pm.select(auxiliary.get_root_nodes()) pm.exportSelected( v.absolute_full_path, type='mayaAscii', force=True ) logger.debug('renewing scene') # clear scene pm.newFile(force=True)
# -*- coding: utf-8 -*- from anima import logger from anima.ui import IS_PYSIDE, IS_PYSIDE2, IS_PYQT4, IS_QTPY if IS_PYQT4(): logger.debug('importing PyQt4') import sip sip.setapi('QString', 2) sip.setapi('QVariant', 2) from PyQt4 import QtGui, QtCore QtWidgets = QtGui elif IS_PYSIDE(): logger.debug('importing PySide') from PySide import QtGui, QtCore QtWidgets = QtGui elif IS_PYSIDE2(): logger.debug('importing PySide2') from PySide2 import QtGui, QtCore, QtWidgets elif IS_QTPY(): logger.debug('importing Qt.py') from Qt import QtGui, QtCore, QtWidgets
def __init__(self, *args, **kwargs): QtGui.QStandardItemModel.__init__(self, *args, **kwargs) logger.debug('TaskTreeModel.__init__() is started') self.root = None logger.debug('TaskTreeModel.__init__() is finished')
def fetchMore(self): logger.debug( 'TaskItem.fetchMore() is started for item: %s' % self.text() ) if self.canFetchMore(): from sqlalchemy import alias from stalker import Task from stalker.db.session import DBSession inner_tasks = alias(Task.__table__) subquery = DBSession.query(Task.id)\ .filter(Task.id == inner_tasks.c.parent_id) query = DBSession.query( Task.id, Task.name, Task.entity_type, Task.status_id, subquery.exists().label('has_children') ) if self.task.entity_type != 'Project': # query child tasks query = query.filter(Task.parent_id == self.task.id) else: # query only root tasks query = query.filter(Task.project_id == self.task.id)\ .filter(Task.parent_id==None) tasks = query.order_by(Task.name).all() # # model = self.model() # This will cause a SEGFAULT # # TODO: update it later on # start = time.time() from anima import defaults task_items = [] for task in tasks: task_item = TaskItem(0, 3, entity=task) task_item.parent = self # color with task status task_item.setData( QtGui.QColor( *defaults.status_colors_by_id[task.status_id] ), QtCore.Qt.BackgroundRole ) # use black text task_item.setForeground( QtGui.QBrush(QtGui.QColor(0, 0, 0)) ) task_items.append(task_item) if task_items: self.appendRows(task_items) self.fetched_all = True logger.debug( 'TaskItem.fetchMore() is finished for item: %s' % self.text() )
def set_environment_variables(self, version): """sets the environment variables according to the given Version instance """ if not version: return # set the $JOB variable to the parent of version.full_path from anima import logger logger.debug('version: %s' % version) logger.debug('version.path: %s' % version.absolute_path) logger.debug('version.filename: %s' % version.filename) logger.debug('version.full_path: %s' % version.absolute_full_path) logger.debug('version.full_path (calculated): %s' % os.path.join( version.absolute_full_path, version.filename).replace("\\", "/")) job = str(version.absolute_path) hip = job hip_name = os.path.splitext( os.path.basename(str(version.absolute_full_path)))[0] logger.debug('job : %s' % job) logger.debug('hip : %s' % hip) logger.debug('hipName : %s' % hip_name) self.set_environment_variable('JOB', job) self.set_environment_variable('HIP', hip) self.set_environment_variable('HIPNAME', hip_name)
def fetchMore(self): logger.debug( 'TaskItem.fetchMore() is started for item: %s' % self.text() ) if self.canFetchMore(): tasks = [] if self.task_name is None: self.task_name, self.task_entity_type = \ db.DBSession \ .query(SimpleEntity.name, SimpleEntity.entity_type) \ .filter(SimpleEntity.id == self.task_id) \ .first() if self.task_children_data is None: if self.task_entity_type in self.task_entity_types: self.task_children_data = db.DBSession \ .query(Task.id, Task.name, Task.entity_type, Task.status_id) \ .filter(Task.parent_id == self.task_id) \ .order_by(Task.name) \ .all() elif self.task_entity_type == 'Project': self.task_children_data = db.DBSession\ .query(Task.id, Task.name, Task.entity_type, Task.status_id) \ .filter(Task.parent_id == None) \ .filter(Task.project_id == self.task_id) \ .order_by(Task.name) \ .all() tasks = self.task_children_data # # model = self.model() # This will cause a SEGFAULT # # TODO: update it later on # if self.user_tasks_only: # user_tasks_and_parents = [] # # need to filter tasks which do not belong to user # for task in tasks: # for user_task in self.user.tasks: # if task in user_task.parents or \ # task is user_task or \ # task in self.user.projects: # user_tasks_and_parents.append(task) # break # # tasks = user_tasks_and_parents # # tasks = sorted(tasks, key=lambda x: x.name) # start = time.time() task_items = [] for task in tasks: task_item = TaskItem(0, 3) task_item.parent = self task_item.task_id = task[0] task_item.user_id = self.user_id task_item.user_tasks_only = self.user_tasks_only # set the font # name_item = QtGui.QStandardItem(task.name) # entity_type_item = QtGui.QStandardItem(task.entity_type) # task_item.setItem(0, 0, name_item) # task_item.setItem(0, 1, entity_type_item) task_item.setText(task[1]) # make_bold = False # if task_item.canFetchMore(): # make_bold = True # if task[2] in self.task_entity_types: # children_count = \ # db.DBSession.query(Task.id)\ # .filter(Task.parent_id == task[0])\ # .count() # if children_count: # make_bold = True # elif task[2] == 'Project': # make_bold = True # if make_bold: # my_font = task_item.font() # my_font.setBold(True) # task_item.setFont(my_font) # color with task status task_item.setData( QtGui.QColor( *status_colors_by_id[task[3]] ), QtCore.Qt.BackgroundRole ) # use black text task_item.setForeground( QtGui.QBrush(QtGui.QColor(0, 0, 0)) ) task_items.append(task_item) if task_items: self.appendRows(task_items) self.fetched_all = True logger.debug( 'TaskItem.fetchMore() is finished for item: %s' % self.text() )
def close(self): logger.debug('closing the ui') QtWidgets.QDialog.close(self)
def __init__(self, *args, **kwargs): logger.debug("Initializing the Mock Connection!") kwargs['client_strategy'] = MOCK_SYNC super(MockConnection, self).__init__(*args, **kwargs) # load the mock data logger.debug("Loading mock connection data") logger.debug("test_ldap_server_entries_path: %s" % test_ldap_server_entries_path) self.strategy.entries_from_json(test_ldap_server_entries_path) # load a fake user for Simple binding logger.debug("Loading a fake user for Simple binding") self.strategy.add_entry( defaults.ldap_base_dn, { "cn": "admin", "codePage": 0, "displayName": "admin", "distinguishedName": "CN=admin,OU=GPU Users Admin,DC=animagpu,DC=local", "givenName": "admin", "instanceType": 4, "memberOf": ["CN=GPU Users Admin,CN=Users,DC=animagpu,DC=local"], "name": "admin", "objectCategory": "CN=Person,CN=Schema,CN=Configuration,DC=animagpu,DC=local", # "objectCategory": "CN=Person,CN=Schema,CN=Configuration,%s" % defaults.ldap_base_dn, "objectClass": ["top", "person", "organizationalPerson", "user"], "objectGUID": "{9d96ef4a-14e7-4a77-b5b1-97b2fa239f9f}", "objectSid": "S-1-5-21-2227021422-3894238547-674366654-1131", "primaryGroupID": 513, "revision": 0, "sAMAccountName": "admin", "sAMAccountType": 805306368, "sn": "admin", "uSNChanged": 423892, "uSNCreated": 314308, "userAccountControl": 66048, "userPassword": "******", "userPrincipalName": "*****@*****.**", })
def ffmpeg(self, **kwargs): """A simple python wrapper for ``ffmpeg`` command. """ # there is only one special keyword called 'o' # this will raise KeyError if there is no 'o' key which is good to # prevent the rest to execute output = kwargs.get('o') try: kwargs.pop('o') except KeyError: # no output pass # generate args args = [self.ffmpeg_command_path] for key in kwargs: flag = '-' + key value = kwargs[key] if not isinstance(value, list): # append the flag args.append(flag) # append the value args.append(str(value)) else: # it is a multi flag # so append the flag every time you append the key for v in value: args.append(flag) args.append(str(v)) # overwrite output # if output format is not a jpg or png if output.split('.')[-1] not in ['jpg', 'jpeg', 'png', 'tga']: # use all cpus import multiprocessing num_of_threads = multiprocessing.cpu_count() args.append('-threads') args.append('%s' % num_of_threads) # overwrite any file args.append('-y') # append the output if output != '' and output is not None: # for info only args.append(output) logger.debug('calling ffmpeg with args: %s' % args) process = subprocess.Popen(args, stderr=subprocess.PIPE) # loop until process finishes and capture stderr output stderr_buffer = [] while True: stderr = process.stderr.readline() if stderr == '' and process.poll() is not None: break if stderr != '': stderr_buffer.append(stderr) # if process.returncode: # # there is an error # raise RuntimeError(stderr_buffer) logger.debug(stderr_buffer) logger.debug('process completed!') return stderr_buffer
def fetchMore(self): logger.debug('TaskItem.fetchMore() is started for item: %s' % self.text()) if self.canFetchMore(): from sqlalchemy.orm import aliased from sqlalchemy.dialects.postgresql import array_agg from stalker import Task, User from stalker.models.task import Task_Resources from stalker.db.session import DBSession inner_tasks = aliased(Task) subquery = DBSession.query(Task.id) \ .filter(Task.id == inner_tasks.parent_id) query = DBSession.query( Task.id, Task.name, Task.entity_type, Task.status_id, subquery.exists().label('has_children'), array_agg(User.name).label('resources') ) \ .outerjoin(Task_Resources, Task.__table__.c.id == Task_Resources.c.task_id) \ .outerjoin(User, Task_Resources.c.resource_id == User.id) \ .group_by( Task.id, Task.name, Task.entity_type, Task.status_id, subquery.exists().label('has_children') ) if self.task.entity_type != 'Project': # query child tasks query = query.filter(Task.parent_id == self.task.id) else: # query only root tasks query = query.filter(Task.project_id == self.task.id)\ .filter(Task.parent_id==None) tasks = query.order_by(Task.name).all() # # model = self.model() # This will cause a SEGFAULT # # TODO: update it later on # start = time.time() from anima import defaults task_items = [] for task in tasks: task_item = TaskItem(0, 4, task=task) task_item.parent = self # color with task status task_item.setData( QtGui.QColor( *defaults.status_colors_by_id[task.status_id]), QtCore.Qt.BackgroundRole) # use black text task_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0))) task_items.append(task_item) if task_items: # self.appendRows(task_items) for task_item in task_items: # TODO: Create a custom QStandardItem for each data type in different columns entity_type_item = QtGui.QStandardItem() entity_type_item.setData(task_item.task.entity_type, QtCore.Qt.DisplayRole) resources_item = QtGui.QStandardItem() if task_item.task.resources != [None]: resources_item.setData( ', '.join(map(str, task_item.task.resources)), QtCore.Qt.DisplayRole) self.appendRow( [task_item, entity_type_item, resources_item]) self.fetched_all = True logger.debug('TaskItem.fetchMore() is finished for item: %s' % self.text())
def _move_file_and_fix_references(self, path, project_path, scenes_folder='scenes', refs_folder='scenes/refs'): """Moves the given maya file to the given project path and moves any references of it to :param str path: The path of the maya file :param str project_path: The project path :param str scenes_folder: The scenes folder to store the original maya scene. :param str refs_folder: The references folder to replace reference paths with. :return list: returns a list of paths """ # fix any env vars path = os.path.expandvars(path) original_file_name = os.path.basename(path) logger.debug('original_file_name: %s' % original_file_name) new_file_path = \ os.path.join(project_path, scenes_folder, original_file_name) ref_paths = [] # only get new ref paths for '.ma' files if path.endswith('.ma'): # read the data of the original file with open(path) as f: data = f.read() ref_paths = self._extract_references(data) # fix all reference paths for ref_path in ref_paths: data = data.replace( ref_path, '%s/%s' % (refs_folder, os.path.basename(ref_path)) ) # now write all the data back to a new temp scene with open(new_file_path, 'w+') as f: f.write(data) else: # fix for UDIM texture paths # if the path contains 1001 or u1_v1 than find the other # textures import glob new_file_paths = [new_file_path] if '1001' in new_file_path or 'u1_v1' in new_file_path.lower(): # get the rest of the textures new_file_paths = glob.glob( new_file_path .replace('1001', '*') .replace('u1_v1', 'u*_v*') .replace('U1_V1', 'U*_V*') ) for p in new_file_paths: print(p) # just copy the file for new_file_path in new_file_paths: try: shutil.copy(path, new_file_path) except IOError: pass return ref_paths
def update_content(self, versions): """updates the content with the given versions data """ import os import datetime logger.debug('VersionsTableWidget.update_content() is started') self.clear() self.versions = versions self.setRowCount(len(versions)) def set_published_font(item): """sets the font for the given item :param item: the a QTableWidgetItem """ my_font = item.font() my_font.setBold(True) item.setFont(my_font) foreground = item.foreground() foreground.setColor(QtGui.QColor(0, 192, 0)) item.setForeground(foreground) # update the previous versions list from anima import defaults for i, version in enumerate(versions): is_published = version.is_published absolute_full_path = os.path.normpath( os.path.expandvars(version.full_path)).replace('\\', '/') version_file_exists = os.path.exists(absolute_full_path) c = 0 # ------------------------------------ # version_number item = QtWidgets.QTableWidgetItem(str(version.version_number)) # align to center and vertical center item.setTextAlignment(0x0004 | 0x0080) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # ------------------------------------ # created_with item = QtWidgets.QTableWidgetItem() if version.created_with: from anima.ui import utils as ui_utils item.setIcon(ui_utils.get_icon(version.created_with.lower())) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # ------------------------------------ # user.name created_by = '' if version.created_by_id: created_by = defaults.user_names_lut[version.created_by_id] item = QtWidgets.QTableWidgetItem(created_by) # align to left and vertical center item.setTextAlignment(0x0001 | 0x0080) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # ------------------------------------ # user.name updated_by = '' if version.updated_by_id: updated_by = defaults.user_names_lut[version.updated_by_id] item = QtWidgets.QTableWidgetItem(updated_by) # align to left and vertical center item.setTextAlignment(0x0001 | 0x0080) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # ------------------------------------ # file size # get the file size # file_size_format = "%.2f MB" file_size = -1 if version_file_exists: file_size = float( os.path.getsize(absolute_full_path)) / 1048576 from anima import defaults item = QtWidgets.QTableWidgetItem(defaults.file_size_format % file_size) # align to left and vertical center item.setTextAlignment(0x0001 | 0x0080) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # ------------------------------------ # date # get the file date file_date = datetime.datetime.today() if version_file_exists: file_date = datetime.datetime.fromtimestamp( os.path.getmtime(absolute_full_path)) item = QtWidgets.QTableWidgetItem( file_date.strftime(defaults.date_time_format)) # align to left and vertical center item.setTextAlignment(0x0001 | 0x0080) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # ------------------------------------ # description item = QtWidgets.QTableWidgetItem(version.description) # align to left and vertical center item.setTextAlignment(0x0001 | 0x0080) if is_published: set_published_font(item) if not version_file_exists: item.setBackground(QtGui.QColor(64, 0, 0)) self.setItem(i, c, item) c += 1 # ------------------------------------ # resize the first column self.resizeRowsToContents() self.resizeColumnsToContents() self.resizeRowsToContents() logger.debug('VersionsTableWidget.update_content() is finished')
def add_output(self, full_path, name=None, channel=1): """adds the media in the given path to the time line :param str full_path: The path of the file """ logger.debug('adding output from: %s' % full_path) extension = os.path.splitext(full_path)[-1].lower() logger.debug('extension: %s' % extension) output_type = None for key in output_types.keys(): if extension in output_types[key]: output_type = key break if output_type == 'image': logger.debug('output is image') bpy.ops.sequencer.image_strip_add( directory=os.path.dirname(full_path), files=[{ "name": os.path.basename(full_path), # "name": os.path.basename(full_path) }], relative_path=True, frame_start=1, frame_end=26, channel=channel) elif output_type == 'movie': logger.debug('output is movie') logger.debug('full_path: %s' % full_path) bpy.ops.sequencer.movie_strip_add( filepath=full_path, files=[{ "name": os.path.basename(full_path), # "name": os.path.basename(full_path) }], relative_path=True, frame_start=1, channel=channel) else: logger.debug('output_type is unknown: %s' % output_type)
def update_version_inputs(self, parent_ref=None): """updates the references list of the current version :param parent_ref: the parent ref, if given will override the given version argument and a Version instance will be get from the given parent_ref.path. """ logger.debug('parent_ref: %s' % parent_ref) logger.debug('get a version') if not parent_ref: logger.debug('got no parent_ref') version = self.get_current_version() else: logger.debug('have a parent_ref') version = self.get_version_from_full_path(parent_ref.path) if version: logger.debug('got a version: %s' % version.absolute_full_path) # use the original version if it is a Repr version from anima.representation import Representation if Representation.repr_separator in version.take_name \ and version.parent: version = version.parent logger.debug( 'this is a representation switching to its parent: %s' % version ) # update the reference list referenced_versions = self.get_referenced_versions(parent_ref) version.inputs = referenced_versions # commit data to the database from stalker.db.session import DBSession DBSession.add(version) DBSession.commit()
def SET_PYSIDE2(): logger.debug('setting environment to PySide2') global qt_lib qt_lib = PYSIDE2
def SET_PYQT4(): logger.debug('setting environment to PyQt4') global qt_lib qt_lib = PYQT4
def SET_QT(): logger.debug('setting environment to Qt') global qt_lib qt_lib = PYQT4
def _set_defaults(self): """sets the defaults for the ui """ logger.debug("started setting up interface defaults") # Set Default Value for time current_time = QtCore.QTime.currentTime() # round the minutes to the resolution minute = current_time.minute() hour = current_time.hour() minute = int(minute / float(timing_resolution)) * timing_resolution current_time = QtCore.QTime(hour, minute) self.start_time_edit.setTime(current_time) self.end_time_edit.setTime(current_time.addSecs(timing_resolution * 60)) self.calendar_widget.resource_id = -1 # enter revision types revision_types = [ 'Yetistiremedim', 'Ajans', 'Yonetmen', 'Ic Revizyon', ] self.revision_type_combo_box.addItems(revision_types) if not self.logged_in_user: self.logged_in_user = self.get_logged_in_user() # fill the tasks comboBox from stalker import Status, Task status_wfd = Status.query.filter(Status.code == 'WFD').first() status_cmpl = Status.query.filter(Status.code == 'CMPL').first() status_prev = Status.query.filter(Status.code == 'PREV').first() if not self.timelog: # dialog is in create TimeLog mode # if a task has been given just feed that task to the comboBox if self.task: all_tasks = [self.task] else: # no Task is given nor updating a TimeLog # show all the suitable tasks of the logged_in_user all_tasks = Task.query \ .filter(Task.resources.contains(self.logged_in_user)) \ .filter(Task.status != status_wfd) \ .filter(Task.status != status_cmpl) \ .filter(Task.status != status_prev) \ .all() # sort the task labels all_tasks = sorted( all_tasks, key=lambda task: '%s | %s' % ( task.project.name.lower(), ' | '.join(map(lambda x: x.name.lower(), task.parents)) ) ) else: # dialog is working in update TimeLog mode all_tasks = [self.timelog.task] self.tasks_combo_box.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToContentsOnFirstShow ) self.tasks_combo_box.setFixedWidth(360) self.tasks_combo_box.clear() self.tasks_combo_box.addTasks(all_tasks) self.tasks_combo_box.setSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum ) # if a time log is given set the fields from the given time log if self.timelog: # first update Task try: self.tasks_combo_box.setCurrentTask(self.timelog.task) except IndexError as e: return # set the resource # and disable the tasks_combo_box and resource_combo_box self.tasks_combo_box.setEnabled(False) self.resource_combo_box.setEnabled(False) # set the start and end time from anima.utils import utc_to_local start_date = utc_to_local(self.timelog.start) end_date = utc_to_local(self.timelog.end) # set the date self.calendar_widget.setSelectedDate( QtCore.QDate(start_date.year, start_date.month, start_date.day) ) # first reset the start and end time values self.start_time_edit.setTime(QtCore.QTime(0, 0)) self.end_time_edit.setTime(QtCore.QTime(23, 50)) # now set the timelog time self.start_time_edit.setTime( QtCore.QTime( start_date.hour, start_date.minute ) ) self.end_time_edit.setTime( QtCore.QTime( end_date.hour, end_date.minute ) ) self.fill_calendar_with_time_logs() # also trigger an update to the side info bar self.calendar_widget_selection_changed()
def fetchMore(self): logger.debug( 'TaskItem.fetchMore() is started for item: %s' % self.text() ) if self.canFetchMore(): tasks = [] if isinstance(self.task, Task): tasks = self.task.children elif isinstance(self.task, Project): tasks = self.task.root_tasks # model = self.model() # This will cause a SEGFAULT if self.user_tasks_only: user_tasks_and_parents = [] # need to filter tasks which do not belong to user for task in tasks: for user_task in self.user.tasks: if task in user_task.parents or \ task is user_task or \ task in self.user.projects: user_tasks_and_parents.append(task) break tasks = user_tasks_and_parents tasks = sorted(tasks, key=lambda x: x.name) for task in tasks: task_item = TaskItem(0, 3) task_item.parent = self task_item.task = task task_item.user = self.user task_item.user_tasks_only = self.user_tasks_only # set the font # name_item = QtGui.QStandardItem(task.name) # entity_type_item = QtGui.QStandardItem(task.entity_type) # task_item.setItem(0, 0, name_item) # task_item.setItem(0, 1, entity_type_item) task_item.setText(task.name) make_bold = False if isinstance(task, Task): if task.is_container: make_bold = True elif isinstance(task, Project): make_bold = True if make_bold: my_font = task_item.font() my_font.setBold(True) task_item.setFont(my_font) # color with task status task_item.setData( QtGui.QColor( *status_colors[task_item.task.status.code.lower()] ), QtCore.Qt.BackgroundRole ) # use black text task_item.setForeground( QtGui.QBrush(QtGui.QColor(0, 0, 0)) ) self.appendRow(task_item) self.fetched_all = True logger.debug( 'TaskItem.fetchMore() is finished for item: %s' % self.text() )