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 upload_thumbnail_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...') upload_thumbnail_action = \ update_sub_menu.addAction( u'\uf03e Upload Thumbnail...' ) 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 is upload_thumbnail_action: from anima.ui import utils as ui_utils thumbnail_full_path = ui_utils.choose_thumbnail(self) # if the thumbnail_full_path is empty do not do anything if thumbnail_full_path == "": return # get the current task ui_utils.upload_thumbnail(entity, thumbnail_full_path) 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 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 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 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