From 9e49bc8149d1fb9c81bc5ae127bf15341fd88fca Mon Sep 17 00:00:00 2001 From: Brian Cipriano Date: Mon, 8 Feb 2021 18:52:10 +0000 Subject: [PATCH] Lint CueGUI code. (#904) --- ci/pylintrc_main | 2 +- ci/pylintrc_test | 6 +- ci/run_python_tests.sh | 3 + cuegui/cuegui/AbstractDialog.py | 22 +- cuegui/cuegui/AbstractDockWidget.py | 14 +- cuegui/cuegui/AbstractTreeWidget.py | 94 ++-- cuegui/cuegui/AbstractWidgetItem.py | 37 +- cuegui/cuegui/Action.py | 51 +- cuegui/cuegui/ApplicationConfig.py | 25 - cuegui/cuegui/BugReportDialog.py | 25 - cuegui/cuegui/Comments.py | 14 +- cuegui/cuegui/ConfirmationDialog.py | 11 +- cuegui/cuegui/Constants.py | 12 +- cuegui/cuegui/CreatorDialog.py | 16 +- cuegui/cuegui/CueJobMonitorTree.py | 164 +++--- cuegui/cuegui/CueStateBarWidget.py | 15 +- cuegui/cuegui/Cuedepend.py | 129 ++--- cuegui/cuegui/DarkPalette.py | 13 +- cuegui/cuegui/DependDialog.py | 21 +- cuegui/cuegui/DependMonitorTree.py | 42 +- cuegui/cuegui/DependWizard.py | 258 +++++---- cuegui/cuegui/EmailDialog.py | 49 +- cuegui/cuegui/FilterDialog.py | 216 +++++--- cuegui/cuegui/FrameMonitor.py | 95 ++-- cuegui/cuegui/FrameMonitorTree.py | 204 ++++--- cuegui/cuegui/FrameRangeSelection.py | 95 ++-- cuegui/cuegui/GarbageCollector.py | 42 +- cuegui/cuegui/GraphSubscriptionsWidget.py | 117 ---- cuegui/cuegui/GroupDialog.py | 61 +- cuegui/cuegui/HostMonitor.py | 78 +-- cuegui/cuegui/HostMonitorTree.py | 55 +- cuegui/cuegui/ItemDelegate.py | 158 +++--- cuegui/cuegui/JobMonitorTree.py | 67 ++- cuegui/cuegui/LayerDialog.py | 80 +-- cuegui/cuegui/LayerMonitorTree.py | 44 +- cuegui/cuegui/LimitSelectionWidget.py | 29 +- cuegui/cuegui/LimitsWidget.py | 222 ++++---- cuegui/cuegui/LocalBooking.py | 80 +-- cuegui/cuegui/Main.py | 31 +- cuegui/cuegui/MainWindow.py | 87 ++- cuegui/cuegui/MenuActions.py | 520 +++++++++++------- cuegui/cuegui/MiscDialog.py | 9 +- cuegui/cuegui/Plugins.py | 128 +++-- cuegui/cuegui/PreviewWidget.py | 40 +- cuegui/cuegui/ProcMonitor.py | 41 +- cuegui/cuegui/ProcMonitorTree.py | 94 ++-- cuegui/cuegui/ProgressDialog.py | 17 +- cuegui/cuegui/Redirect.py | 98 ++-- cuegui/cuegui/ServiceDialog.py | 12 +- cuegui/cuegui/ShowDialog.py | 9 +- cuegui/cuegui/ShowsWidget.py | 23 +- cuegui/cuegui/SplashWindow.py | 11 +- cuegui/cuegui/Style.py | 19 +- cuegui/cuegui/SubscriptionGraphWidget.py | 61 +- cuegui/cuegui/SubscriptionsWidget.py | 55 +- cuegui/cuegui/TagsWidget.py | 16 +- cuegui/cuegui/TasksDialog.py | 54 +- cuegui/cuegui/TextEditDialog.py | 13 +- cuegui/cuegui/ThreadPool.py | 47 +- cuegui/cuegui/UnbookDialog.py | 120 ++-- cuegui/cuegui/Utils.py | 220 ++++---- cuegui/cuegui/__init__.py | 2 +- cuegui/cuegui/__main__.py | 9 +- cuegui/cuegui/eta.py | 328 +++++------ cuegui/cuegui/plugins/AllocationsPlugin.py | 33 +- cuegui/cuegui/plugins/AttributesPlugin.py | 50 +- cuegui/cuegui/plugins/LimitsPlugin.py | 32 +- cuegui/cuegui/plugins/LogViewPlugin.py | 36 +- cuegui/cuegui/plugins/MonitorCuePlugin.py | 41 +- cuegui/cuegui/plugins/MonitorHostsPlugin.py | 6 +- .../cuegui/plugins/MonitorJobDetailsPlugin.py | 13 +- cuegui/cuegui/plugins/MonitorJobsPlugin.py | 68 ++- cuegui/cuegui/plugins/RedirectPlugin.py | 5 + cuegui/cuegui/plugins/ServicePlugin.py | 8 +- cuegui/cuegui/plugins/ShowsPlugin.py | 6 +- .../plugins/SubscriptionsGraphPlugin.py | 22 +- cuegui/cuegui/plugins/SubscriptionsPlugin.py | 6 +- cuegui/tests/CueJobMonitorTree_tests.py | 12 +- cuegui/tests/FilterDialog_tests.py | 27 +- cuegui/tests/FrameMonitorTree_tests.py | 17 +- cuegui/tests/MenuActions_tests.py | 97 ++-- cuegui/tests/Redirect_tests.py | 10 +- cuegui/tests/Utils_tests.py | 6 +- cuegui/tests/images/images_tests.py | 4 + cuegui/tests/plugins/LogViewPlugin_tests.py | 32 +- cuegui/tests/test_utils.py | 4 + pycue/opencue/wrappers/service.py | 16 + 87 files changed, 3094 insertions(+), 2187 deletions(-) delete mode 100644 cuegui/cuegui/ApplicationConfig.py delete mode 100644 cuegui/cuegui/BugReportDialog.py delete mode 100644 cuegui/cuegui/GraphSubscriptionsWidget.py diff --git a/ci/pylintrc_main b/ci/pylintrc_main index f154d667b..b4510f658 100644 --- a/ci/pylintrc_main +++ b/ci/pylintrc_main @@ -3,7 +3,7 @@ # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. -extension-pkg-whitelist= +extension-pkg-whitelist=PySide2 # Specify a score threshold to be exceeded before program exits with error. fail-under=10.0 diff --git a/ci/pylintrc_test b/ci/pylintrc_test index dc175b0b3..a436236f2 100644 --- a/ci/pylintrc_test +++ b/ci/pylintrc_test @@ -3,7 +3,7 @@ # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. -extension-pkg-whitelist= +extension-pkg-whitelist=PySide2 # Specify a score threshold to be exceeded before program exits with error. fail-under=10.0 @@ -60,7 +60,8 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=duplicate-code, +disable=arguments-differ, + duplicate-code, fixme, invalid-name, locally-disabled, @@ -69,6 +70,7 @@ disable=duplicate-code, no-self-use, protected-access, raise-missing-from, + too-many-lines, too-many-locals, too-many-public-methods, unused-argument, diff --git a/ci/run_python_tests.sh b/ci/run_python_tests.sh index a4797092f..7f0e9c0a4 100755 --- a/ci/run_python_tests.sh +++ b/ci/run_python_tests.sh @@ -31,5 +31,8 @@ if [[ "$1" == "--lint" ]]; then cd cueadmin && PYTHONPATH=../pycue python -m pylint --rcfile=../ci/pylintrc_main cueadmin && cd .. cd cueadmin && PYTHONPATH=../pycue python -m pylint --rcfile=../ci/pylintrc_test tests && cd .. + + cd cuegui && PYTHONPATH=../pycue python -m pylint --rcfile=../ci/pylintrc_main cuegui --ignore=cuegui/images,cuegui/images/crystal && cd .. + cd cuegui && PYTHONPATH=../pycue python -m pylint --rcfile=../ci/pylintrc_test tests && cd .. fi diff --git a/cuegui/cuegui/AbstractDialog.py b/cuegui/cuegui/AbstractDialog.py index 13563a947..613b3d83c 100644 --- a/cuegui/cuegui/AbstractDialog.py +++ b/cuegui/cuegui/AbstractDialog.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Base class for dialog windows.""" + + from __future__ import division from __future__ import print_function from __future__ import absolute_import @@ -23,18 +26,14 @@ class AbstractDialog(QtWidgets.QDialog): + """Base class for dialog windows.""" + def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) - def _newCheckBoxSelectionMatrix(self, - title, - allowedOptions, - checkedOptions, - parent=None): - return CheckBoxSelectionMatrix(title, - allowedOptions, - checkedOptions, - parent) + @staticmethod + def _newCheckBoxSelectionMatrix(title, allowedOptions, checkedOptions, parent=None): + return CheckBoxSelectionMatrix(title, allowedOptions, checkedOptions, parent) def _newDialogButtonBox(self, buttons, orientation=QtCore.Qt.Horizontal): buttonBox = QtWidgets.QDialogButtonBox(buttons, orientation, self) @@ -50,6 +49,8 @@ def _addWidgetRow(self, *widgets): class CheckBoxSelectionMatrix(QtWidgets.QWidget): + """Widget for displaying a matrix of checkboxes.""" + def __init__(self, title, allowedOptions, checkedOptions, parent=None): QtWidgets.QWidget.__init__(self, parent) layout = QtWidgets.QVBoxLayout(self) @@ -70,11 +71,14 @@ def __init__(self, title, allowedOptions, checkedOptions, parent=None): layout.addStretch() def checkedBoxes(self): + """Gets all checked boxes.""" return [cb for cb in self.__checkBoxes if cb.isChecked()] def checkedOptions(self): + """Gets text value of all checked boxes.""" return [str(cb.text()) for cb in self.__checkBoxes if cb.isChecked()] def checkBoxes(self, names): + """Sets checked state for checkboxes representing the named values.""" for box in self.__checkBoxes: box.setChecked(str(box.text()) in names) diff --git a/cuegui/cuegui/AbstractDockWidget.py b/cuegui/cuegui/AbstractDockWidget.py index 457c59e70..15857d5a2 100644 --- a/cuegui/cuegui/AbstractDockWidget.py +++ b/cuegui/cuegui/AbstractDockWidget.py @@ -13,9 +13,9 @@ # limitations under the License. -""" -Extends QDockWidget to provide a standard setup -""" +"""Base class for all CueGUI widgets. + +Extends QDockWidget to provide a standard setup.""" from __future__ import absolute_import @@ -29,6 +29,9 @@ class AbstractDockWidget(cuegui.Plugins.Plugin, QtWidgets.QDockWidget): + """Base class for all CueGUI widgets. + + Extends QDockWidget to provide a standard setup.""" closed = QtCore.Signal(object) enabled = QtCore.Signal() @@ -40,7 +43,8 @@ def __init__(self, parent, name, area = QtCore.Qt.LeftDockWidgetArea): self.parent = parent self.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) - self.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable | QtWidgets.QDockWidget.DockWidgetMovable) + self.setFeatures( + QtWidgets.QDockWidget.DockWidgetClosable | QtWidgets.QDockWidget.DockWidgetMovable) self.setObjectName(name) parent.addDockWidget(area, self) @@ -53,9 +57,11 @@ def __init__(self, parent, name, area = QtCore.Qt.LeftDockWidgetArea): self.widget().setLayout(self.__layout) def closeEvent(self, event): + del event self.closed.emit(self) def showEvent(self, event): + del event self.enabled.emit() def layout(self): diff --git a/cuegui/cuegui/AbstractTreeWidget.py b/cuegui/cuegui/AbstractTreeWidget.py index 2ce367696..cf74590ef 100644 --- a/cuegui/cuegui/AbstractTreeWidget.py +++ b/cuegui/cuegui/AbstractTreeWidget.py @@ -13,9 +13,9 @@ # limitations under the License. -""" -Provides extended QTreeWidget functionality. -""" +"""Base class for CueGUI tree widgets. + +Provides extended QTreeWidget functionality.""" from __future__ import absolute_import @@ -48,13 +48,15 @@ COLUMN_TOOLTIP = 5 COLUMN_INFO_LENGTH = 6 -DEFAULT_LAMBDA = lambda s:"" +DEFAULT_LAMBDA = lambda s: "" DEFAULT_NAME = "" DEFAULT_WIDTH = 0 class AbstractTreeWidget(QtWidgets.QTreeWidget): - """Forms the basis for all TreeWidgets""" + """Base class for CueGUI tree widgets. + + Provides extended QTreeWidget functionality.""" itemDoubleClicked = QtCore.Signal(QtWidgets.QTreeWidgetItem, int) itemSingleClicked = QtCore.Signal(QtWidgets.QTreeWidgetItem, int) @@ -101,7 +103,9 @@ def __init__(self, parent): self.itemClicked.connect(self.__itemSingleClickedEmitToApp) self.itemDoubleClicked.connect(self.__itemDoubleClickedEmitToApp) self._timer.timeout.connect(self.updateRequest) + # pylint: disable=no-member QtGui.qApp.request_update.connect(self.updateRequest) + # pylint: enable=no-member self.updateRequest() self.setUpdateInterval(10) @@ -117,6 +121,7 @@ def closeEvent(self, event): event.accept() + # pylint: disable=attribute-defined-outside-init def startColumnsForType(self, itemType): """Start column definitions for the given item type. The first call to this defines the primary column type used to populate the column headers. @@ -131,6 +136,7 @@ def startColumnsForType(self, itemType): self.__columnInfoByType[itemType] = [] self.__columnCurrent = itemType + # pylint: disable=redefined-builtin def addColumn(self, name, width, id=0, default=True, data=DEFAULT_LAMBDA, sort=None, delegate=None, tip=""): @@ -178,7 +184,7 @@ def __setupColumns(self): # Setup column delegates if primaryColumnInfo[col][COLUMN_DELEGATE]: - self.setItemDelegateForColumn(col, primaryColumnInfo[col][COLUMN_DELEGATE](self)) + self.setItemDelegateForColumn(col, primaryColumnInfo[col][COLUMN_DELEGATE](self)) # Setup column name list if columnInfo[COLUMN_NAME].startswith("_"): @@ -214,13 +220,14 @@ def startTicksUpdate(self, updateInterval, self.ticksWithoutUpdate = 999 def tickNeedsUpdate(self): + """Gets whether enough time has passed for contents to need an update.""" if self.ticksWithoutUpdate >= self.updateInterval: if self.window().isMinimized(): if self.__maxUpdateInterval is not None and \ self.ticksWithoutUpdate >= self.__maxUpdateInterval: # Sufficient maximum interval return True - elif not self.__updateWhenMinimized: + if not self.__updateWhenMinimized: # Sufficient interval, except minimized return False # Sufficient interval, set to update when minimized @@ -236,6 +243,7 @@ def __tick(self): if not self.ticksLock.tryLock(): return try: + # pylint: disable=broad-except try: self.tick() except Exception as e: @@ -244,6 +252,9 @@ def __tick(self): self.ticksLock.unlock() def tick(self): + """Determines whether an update is needed and initiates updating logic. + + Must be defined by inheriting classes.""" raise NotImplementedError def getColumnInfo(self, columnType = None): @@ -259,16 +270,21 @@ def getColumnInfo(self, columnType = None): return self.__columnInfoByType[columnType] return self.__columnInfoByType[self.__columnPrimaryType] - def __itemSingleClickedEmitToApp(self, item, col): + @staticmethod + def __itemSingleClickedEmitToApp(item, col): """When an item is single clicked on: emits "single_click(PyQt_PyObject)" to the app @type item: QTreeWidgetItem @param item: The item single clicked on @type col: int @param col: Column number single clicked on""" + del col + # pylint: disable=no-member QtGui.qApp.single_click.emit(item.rpcObject) + # pylint: enable=no-member - def __itemDoubleClickedEmitToApp(self, item, col): + @staticmethod + def __itemDoubleClickedEmitToApp(item, col): """Handles when an item is double clicked on. emits "double_click(PyQt_PyObject)" to the app emits "view_object(PyQt_PyObject)" to the app @@ -276,8 +292,11 @@ def __itemDoubleClickedEmitToApp(self, item, col): @param item: The item double clicked on @type col: int @param col: Column number double clicked on""" + del col + # pylint: disable=no-member QtGui.qApp.view_object.emit(item.rpcObject) QtGui.qApp.double_click.emit(item.rpcObject) + # pylint: enable=no-member def addObject(self, rpcObject): """Adds or updates an rpcObject in the list using the _createItem function @@ -326,8 +345,6 @@ def _removeItem(self, item): item.setSelected(False) if item.parent(): - #This allowed more segfaults - #item.parent().removeChild(item) self.invisibleRootItem().removeChild(item) self.takeTopLevelItem(self.indexOfTopLevelItem(item)) objectClass = item.rpcObject.__class__.__name__ @@ -335,6 +352,7 @@ def _removeItem(self, item): del self._items['{}.{}'.format(objectClass, objectId)] def removeAllItems(self): + """Removes all items from the tree.""" self._itemsLock.lockForWrite() try: self._items = {} @@ -368,7 +386,10 @@ def _update(self): updated""" self._lastUpdate = time.time() if hasattr(QtGui.qApp, "threadpool"): - QtGui.qApp.threadpool.queue(self._getUpdate, self._processUpdate, "getting data for %s" % self.__class__) + # pylint: disable=no-member + QtGui.qApp.threadpool.queue( + self._getUpdate, self._processUpdate, "getting data for %s" % self.__class__) + # pylint: enable=no-member else: logger.warning("threadpool not found, doing work in gui thread") self._processUpdate(None, self._getUpdate()) @@ -382,6 +403,7 @@ def _processUpdate(self, work, rpcObjects): @type work: from ThreadPool @param rpcObjects: A list of rpc objects @type rpcObjects: list """ + del work self._itemsLock.lockForWrite() try: updated = [] @@ -416,22 +438,20 @@ def updateSoon(self): def redraw(self): """Forces the displayed items to be redrawn""" if not self.window().isMinimized(): + # pylint: disable=broad-except try: self.scheduleDelayedItemsLayout() - # This setDirtyRegion works but can give this error sometimes: - # "underlying C/C++ object has been deleted" - #self.setDirtyRegion(QtGui.QRegion(self.viewport().rect())) except Exception as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def getColumnWidths(self): - """Return the column widths + """Gets the column widths. @rtype: list @return: A list of column widths""" return [self.columnWidth(index) for index in range(self.columnCount())] def setColumnWidths(self, widths): - """Set the column widths if thecorrect number are provided, but ignore + """Sets the column widths if the correct number are provided, but ignore the last one since it is stretched to the end. @type widths: list @param widths: The desired width for each column""" @@ -439,9 +459,10 @@ def setColumnWidths(self, widths): for index, width in enumerate(widths[:-1]): self.setColumnWidth(index, width) -################################################################################ -# Optionally limit updates when user scrolls -################################################################################ + ################################################################################ + # Optionally limit updates when user scrolls + ################################################################################ + def _limitUpdatesDuringScrollSetup(self, skipsAllowed = 1, delay = 1.0): """Allows the ability to skip updates when the user is scrolling @type skipsAllowed: int @@ -459,6 +480,7 @@ def _limitUpdatesDuringScrollSetup(self, skipsAllowed = 1, delay = 1.0): def __userScrolled(self, val): """Stores the time when the user last scrolled""" + del val self.__lastScrollTime = time.time() def __limitUpdatesDuringScrollAllowUpdate(self): @@ -469,17 +491,18 @@ def __limitUpdatesDuringScrollAllowUpdate(self): if not hasattr(self, "limitUpdatesDuringScroll"): return True - if time.time() - self.__lastScrollTime > self.__allowedSkipDelay or \ - self.__updateSkipCount >= self.__allowedSkipCount: + if (time.time() - self.__lastScrollTime > self.__allowedSkipDelay or + self.__updateSkipCount >= self.__allowedSkipCount): self.__updateSkipCount = 0 return True - else: - self.__updateSkipCount += 1 - return False -################################################################################ -# Allow the user to show or hide columns -################################################################################ + self.__updateSkipCount += 1 + return False + + ################################################################################ + # Allow the user to show or hide columns + ################################################################################ + def __setupColumnMenu(self): self.__dropdown = QtWidgets.QToolButton(self) self.__dropdown.setFocusPolicy(QtCore.Qt.NoFocus) @@ -518,22 +541,25 @@ def __handleColumnMenu(self, action): self.setColumnWidth(col, width) def getColumnVisibility(self): + """Gets table column visibility.""" settings = [] for col in range(self.columnCount()): settings.append(self.isColumnHidden(col)) return settings def setColumnVisibility(self, settings): + """Sets table column visibility.""" if settings: - for col in range(len(settings)): + for col, setting in enumerate(settings): if col <= self.columnCount(): - self.setColumnHidden(col, settings[col]) + self.setColumnHidden(col, setting) -################################################################################ -# Allow the user to move columns and remember position -################################################################################ + ################################################################################ + # Allow the user to move columns and remember position + ################################################################################ def getColumnOrder(self): + """Gets table column order.""" settings = {} header = self.header() for col in range(header.count()): @@ -541,7 +567,9 @@ def getColumnOrder(self): return settings def setColumnOrder(self, settings): + """Sets table column order.""" header = self.header() + # pylint: disable=unnecessary-lambda cols = sorted(settings.keys(), key=lambda x: int(x)) for col in cols: old_col = header.visualIndex(settings[col]) diff --git a/cuegui/cuegui/AbstractWidgetItem.py b/cuegui/cuegui/AbstractWidgetItem.py index 3ba6604a6..fec1dc5fe 100644 --- a/cuegui/cuegui/AbstractWidgetItem.py +++ b/cuegui/cuegui/AbstractWidgetItem.py @@ -13,9 +13,9 @@ # limitations under the License. -""" -Provides extended QWidgetItem functionality. -""" +"""Base class for CueGUI widget items. + +Provides extended QWidgetItem functionality.""" from __future__ import absolute_import @@ -27,11 +27,13 @@ from PySide2 import QtCore from PySide2 import QtWidgets +import opencue +import opencue.wrappers.job + import cuegui.Constants import cuegui.Logger import cuegui.Style -import opencue logger = cuegui.Logger.getLogger(__file__) @@ -42,17 +44,21 @@ class AbstractWidgetItem(QtWidgets.QTreeWidgetItem): - def __init__(self, itemType, object, parent, source=None): + """Base class for CueGUI widget items. + + Provides extended QWidgetItem functionality.""" + + def __init__(self, itemType, rpcObject, parent, source=None): QtWidgets.QTreeWidgetItem.__init__(self, parent, itemType) self.column_info = self.treeWidget().getColumnInfo(itemType) self._cache = {} self._source = source - self.rpcObject = object + self.rpcObject = rpcObject - def update(self, object=None, parent=None): + def update(self, rpcObject=None, parent=None): """Updates visual representation with latest data - @type object: Object - @param object: The object that contains updated information + @type rpcObject: Object + @param rpcObject: The object that contains updated information @type parent: QTreeWidgetItem @param parent: Changes the current parent to this parent if different""" # Changes parent if needed @@ -60,8 +66,8 @@ def update(self, object=None, parent=None): self.parent().removeChild(self) parent.addChild(self) - if object: - self.rpcObject = object + if rpcObject: + self.rpcObject = rpcObject self._cache = {} def data(self, col, role): @@ -75,12 +81,12 @@ def data(self, col, role): if role == QtCore.Qt.DisplayRole: return self.column_info[col][DISPLAY_LAMBDA](self.rpcObject) - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: if cuegui.Style.ColorTheme is None: cuegui.Style.init() return cuegui.Style.ColorTheme.COLOR_JOB_FOREGROUND - elif role == QtCore.Qt.UserRole: + if role == QtCore.Qt.UserRole: return self.type() return cuegui.Constants.QVARIANT_NULL @@ -91,8 +97,9 @@ def __lt__(self, other): column = self.treeWidget().sortColumn() if sortLambda and isinstance(other.rpcObject, opencue.wrappers.job.Job): + # pylint: disable=broad-except try: return sortLambda(self.rpcObject) < sortLambda(other.rpcObject) - except: - logger.warning("Sort failed on column {}, using text sort.".format(column)) + except Exception: + logger.warning("Sort failed on column %s, using text sort.", column) return str(self.text(column)) < str(other.text(column)) diff --git a/cuegui/cuegui/Action.py b/cuegui/cuegui/Action.py index e320249e5..853e6a087 100644 --- a/cuegui/cuegui/Action.py +++ b/cuegui/cuegui/Action.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -utility functions for creating QActions -""" +"""Utility functions for creating QActions.""" from __future__ import absolute_import @@ -49,12 +47,12 @@ def create(parent, text, tip, callback=None, icon=None): return a -def createAction(parent, id, text, tip, callback=None, icon=None): - """create(QtWidgets.QWidget, string text, string tip, callable callback=None, string icon=None) - creates a QtGui.QAction and optionally connects it to a slot - """ - if id in Actions: - raise Exception("Action %s has already been created" % (id)) +def createAction(parent, action_id, text, tip, callback=None, icon=None): + """Creates a QtGui.QAction and optionally connects it to a slot. + + create(QtWidgets.QWidget, string text, string tip, callable callback=None, string icon=None)""" + if action_id in Actions: + raise Exception("Action %s has already been created" % (action_id)) a = QtWidgets.QAction(parent) a.setText(text) @@ -64,42 +62,41 @@ def createAction(parent, id, text, tip, callback=None, icon=None): a.setIcon(QtGui.QIcon(":/images/%s.png" % icon)) if callback: connectActionSlot(a,callback) - Actions[id] = a + Actions[action_id] = a return a -def getAction(id): - return Actions[id] +def getAction(action_id): + """Gets an action by ID.""" + return Actions[action_id] -def createActionGroup(parent, id, actions): +def createActionGroup(parent, action_id, actions): + """Creates an action group.""" g = QtWidgets.QActionGroup(parent) for action in actions: g.addAction(action) - Groups[id] = g + Groups[action_id] = g -def getActionGroup(id): - return Groups[id] +def getActionGroup(group_id): + """Gets an action group.""" + return Groups[group_id] -def applyActionGroup(id, menu): - for act in getActionGroup(id).actions(): +def applyActionGroup(group_id, menu): + """Add all actions in a group to the given menu.""" + for act in getActionGroup(group_id).actions(): menu.addAction(act) -def connectActionSlot(action, callable): - """connectActionSlot - connects an action's triggered() signal to a callable object - """ - action.triggered.connect(callable) +def connectActionSlot(action, actionCallable): + """Connects an action's triggered() signal to a callable object.""" + action.triggered.connect(actionCallable) class Refresh(QtWidgets.QAction): - """Refresh - - refresh something - """ + """Refreshes something.""" def __init__(self,callback=None, parent=None): QtWidgets.QAction.__init__(self,parent) diff --git a/cuegui/cuegui/ApplicationConfig.py b/cuegui/cuegui/ApplicationConfig.py deleted file mode 100644 index 3b4638168..000000000 --- a/cuegui/cuegui/ApplicationConfig.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright Contributors to the OpenCue Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import - -from PySide2 import QtWidgets - - -class ApplicationConfig(QtWidgets.QWidget): - def __init__(self): - pass \ No newline at end of file diff --git a/cuegui/cuegui/BugReportDialog.py b/cuegui/cuegui/BugReportDialog.py deleted file mode 100644 index 6cae58d91..000000000 --- a/cuegui/cuegui/BugReportDialog.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright Contributors to the OpenCue Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import - -from PySide2 import QtWidgets - - -class BugReportWidget(QtWidgets.QWidget): - def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self) diff --git a/cuegui/cuegui/Comments.py b/cuegui/cuegui/Comments.py index 0af3092e7..dc493433f 100644 --- a/cuegui/cuegui/Comments.py +++ b/cuegui/cuegui/Comments.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Dialog for displaying a comment list.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -37,9 +40,11 @@ class CommentListDialog(QtWidgets.QDialog): - """A dialog to display a comment list""" + """Dialog for displaying a comment list.""" + def __init__(self, source, parent=None): - """Initialize the dialog + """Initialize the dialog. + @type source: Job or Host @param source: The source to get the comments from @type parent: QWidget @@ -103,6 +108,7 @@ def __init__(self, source, parent=None): def __textEdited(self, text=None): """Called when the text boxes are modified, enables the save button""" + del text self.__btnSave.setEnabled(True) def __close(self): @@ -198,8 +204,10 @@ def refreshComments(self): def __macroLoad(self): """Loads the defined comment macros from settings""" + # pylint: disable=no-member self.__macroList = pickle.loads( str(QtGui.qApp.settings.value("Comments", pickle.dumps({})))) + # pylint: enable=no-member self.__macroRefresh() def __macroRefresh(self): @@ -213,7 +221,9 @@ def __macroRefresh(self): def __macroSave(self): """Saves the current comment macros to settings""" + # pylint: disable=no-member QtGui.qApp.settings.setValue("Comments", pickle.dumps(self.__macroList)) + # pylint: enable=no-member def __macroHandle(self, selection): """Called when the comment macro combo box is selected diff --git a/cuegui/cuegui/ConfirmationDialog.py b/cuegui/cuegui/ConfirmationDialog.py index 3d7e8a850..0c71e88e3 100644 --- a/cuegui/cuegui/ConfirmationDialog.py +++ b/cuegui/cuegui/ConfirmationDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A confirmation dialog -""" +"""Confirmation dialog.""" from __future__ import print_function @@ -28,8 +26,11 @@ class ConfirmationDialog(QtWidgets.QDialog): - def __init__(self, title, text, items = [], parent = None): - """A confirmation dialog + """Confirmation dialog.""" + + # pylint: disable=dangerous-default-value + def __init__(self, title, text, items=[], parent=None): + """ @type title: string @param title: The title for the confirmation dialog @type text: string diff --git a/cuegui/cuegui/Constants.py b/cuegui/cuegui/Constants.py index 27503f363..bb38e9495 100644 --- a/cuegui/cuegui/Constants.py +++ b/cuegui/cuegui/Constants.py @@ -32,7 +32,8 @@ import opencue -possible_version_path = os.path.join(os.path.abspath(os.path.join(__file__ , "../../..")), 'VERSION.in') +possible_version_path = os.path.join( + os.path.abspath(os.path.join(__file__ , "../../..")), 'VERSION.in') if os.path.exists(possible_version_path): with open(possible_version_path) as fp: VERSION = fp.read().strip() @@ -70,9 +71,10 @@ EMAIL_BODY_SUFFIX = "\n\n" EMAIL_DOMAIN = "" +GITHUB_CREATE_ISSUE_URL = 'https://github.com/AcademySoftwareFoundation/OpenCue/issues/new' URL_USERGUIDE = "https://www.opencue.io/docs/" -URL_SUGGESTION = "https://github.com/AcademySoftwareFoundation/OpenCue/issues/new?labels=enhancement&template=enhancement.md" -URL_BUG = "https://github.com/AcademySoftwareFoundation/OpenCue/issues/new?labels=bug&template=bug_report.md" +URL_SUGGESTION = "%s?labels=enhancement&template=enhancement.md" % GITHUB_CREATE_ISSUE_URL +URL_BUG = "%s?labels=bug&template=bug_report.md" % GITHUB_CREATE_ISSUE_URL if platform.system() == "Windows": DEFAULT_EDITOR = "notepad" @@ -129,6 +131,8 @@ QT_MAX_INT = 2147483647 -LOG_HIGHLIGHT_ERROR = ['error', 'aborted', 'fatal', 'failed', 'killed', 'command not found', 'no licenses could be found', 'killMessage'] +LOG_HIGHLIGHT_ERROR = [ + 'error', 'aborted', 'fatal', 'failed', 'killed', 'command not found', + 'no licenses could be found', 'killMessage'] LOG_HIGHLIGHT_WARN = ['warning', 'not found'] LOG_HIGHLIGHT_INFO = ['info:', 'rqd cmd:'] diff --git a/cuegui/cuegui/CreatorDialog.py b/cuegui/cuegui/CreatorDialog.py index 2ed8b96d0..0e436b201 100644 --- a/cuegui/cuegui/CreatorDialog.py +++ b/cuegui/cuegui/CreatorDialog.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Dialog for creating a subscription.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -26,14 +29,13 @@ class SubscriptionCreator(QtWidgets.QWidget): + """Widget for creating a subscription.""" + def __init__(self, show=None, parent=None): QtWidgets.QWidget.__init__(self, parent) show_name = "" if show: - try: - show_name = show.data.name - except Exception: - show_name = str(show) + show_name = show.data.name self.__shows = opencue.api.getShows() self.__allocs = opencue.api.getAllocations() @@ -70,7 +72,7 @@ def create(self): show.createSubscription(alloc, float(self.sizeBox.value()), float(self.burstBox.value())) - except Exception as e: + except opencue.exception.CueException as e: QtWidgets.QMessageBox.warning( self, "Create Subscription", @@ -79,7 +81,11 @@ def create(self): class SubscriptionCreatorDialog(QtWidgets.QDialog): + """Dialog for creating a subscription.""" + def __init__(self, show=None, parent=None): + del parent + QtWidgets.QDialog.__init__(self) self.__creator = SubscriptionCreator(show, self) diff --git a/cuegui/cuegui/CueJobMonitorTree.py b/cuegui/cuegui/CueJobMonitorTree.py index fec7d9759..8d1307771 100644 --- a/cuegui/cuegui/CueJobMonitorTree.py +++ b/cuegui/cuegui/CueJobMonitorTree.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Tree widget for displaying a show/job hierarchy.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -26,6 +29,8 @@ from PySide2 import QtWidgets import opencue +import opencue.compiled_proto.job_pb2 +import opencue.wrappers.group import cuegui.AbstractTreeWidget import cuegui.AbstractWidgetItem @@ -47,6 +52,7 @@ def getEta(stats): + """Gets estimated time remaining for a job.""" if stats.runningFrames: remaining = (((stats.pendingFrames - 1) * stats.avgFrameSec) + stats.highFrameSec) if remaining: @@ -55,6 +61,7 @@ def getEta(stats): class CueJobMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a show/job hierarchy.""" view_object = QtCore.Signal(object) single_click = QtCore.Signal(object) @@ -65,15 +72,15 @@ def __init__(self, parent): self.currtime = time.time() self.startColumnsForType(cuegui.Constants.TYPE_JOB) - self.addColumn("Job", 550, id=1, - data=lambda job: job.data.name, - tip="The name of the job: show-shot-user_uniqueName\n\n" - "The color behind the job will change to:\n" - "Blue \t if it is paused\n" - "Red \t if it has dead frames\n" - "Green \t if it has no running frames with frames waiting\n" - "Purple \t if all remaining frames depend on something\n" - "Yellow \t if the maxRss is over %sKb" % cuegui.Constants.MEMORY_WARNING_LEVEL) + self.addColumn( + "Job", 550, id=1, data=lambda job: job.data.name, + tip="The name of the job: show-shot-user_uniqueName\n\n" + "The color behind the job will change to:\n" + "Blue \t if it is paused\n" + "Red \t if it has dead frames\n" + "Green \t if it has no running frames with frames waiting\n" + "Purple \t if all remaining frames depend on something\n" + "Yellow \t if the maxRss is over %sKb" % cuegui.Constants.MEMORY_WARNING_LEVEL) self.addColumn("_Comment", 20, id=2, sort=lambda job: job.data.has_comment, tip="A comment icon will appear if a job has a comment. You\n" @@ -103,7 +110,7 @@ def __init__(self, parent): data=lambda job: job.data.job_stats.total_frames, sort=lambda job: job.data.job_stats.total_frames, tip="The total number of frames.") - self.addColumn("_Booking Bar", 150, id=9, + self.addColumn("_Booking Bar", 150, id=9, delegate=cuegui.ItemDelegate.JobBookingBarDelegate) self.addColumn("Min", 38, id=10, data=lambda job: "%.0f" % job.data.min_cores, @@ -115,10 +122,11 @@ def __init__(self, parent): sort=lambda job: job.data.max_cores, tip="The maximum number of running cores that the cuebot\n" "will allow.") - self.addColumn("Age", 50, id=12, - data=lambda job: cuegui.Utils.secondsToHHHMM(self.currtime - job.data.start_time), - sort=lambda job: self.currtime - job.data.start_time, - tip="The HOURS:MINUTES since the job was launched.") + self.addColumn( + "Age", 50, id=12, + data=lambda job: cuegui.Utils.secondsToHHHMM(self.currtime - job.data.start_time), + sort=lambda job: self.currtime - job.data.start_time, + tip="The HOURS:MINUTES since the job was launched.") self.addColumn("Pri", 30, id=13, data=lambda job: job.data.priority, sort=lambda job: job.data.priority, @@ -162,7 +170,8 @@ def __init__(self, parent): self.addColumn("", 0, id=9, data=lambda group: (group.data.min_cores or "")) self.addColumn("", 0, id=10, - data=lambda group: (group.data.max_cores > 0 and group.data.max_cores or "")) + data=lambda group: ( + group.data.max_cores > 0 and group.data.max_cores or "")) self.addColumn("", 0, id=11) self.addColumn("", 0, id=12) self.addColumn("", 0, id=13) @@ -185,7 +194,9 @@ def __init__(self, parent): self.__menuActions = cuegui.MenuActions.MenuActions( self, self.updateSoon, self.selectedObjects) + # pylint: disable=no-member QtGui.qApp.facility_changed.connect(self.removeAllShows) + # pylint: enable=no-member self.itemClicked.connect(self.__itemSingleClickedCopy) self.itemClicked.connect(self.__itemSingleClickedComment) @@ -201,9 +212,12 @@ def __itemSingleClickedCopy(self, item, col): @param item: The item clicked on @type col: int @param col: The column clicked on""" + del item + del col selected = [job.data.name for job in self.selectedObjects() if cuegui.Utils.isJob(job)] if selected: - QtWidgets.QApplication.clipboard().setText(" ".join(selected), QtGui.QClipboard.Selection) + QtWidgets.QApplication.clipboard().setText( + " ".join(selected), QtGui.QClipboard.Selection) def __itemSingleClickedComment(self, item, col): """If the comment column is clicked on, and there is a comment on the @@ -250,17 +264,17 @@ def dropEvent(self, event): if item and item.type() in (cuegui.Constants.TYPE_ROOTGROUP, cuegui.Constants.TYPE_GROUP): job_ids = cuegui.Utils.dropEvent(event, "application/x-job-ids") group_ids = cuegui.Utils.dropEvent(event, "application/x-group-ids") - job_names = cuegui.Utils.dropEvent(event, "application/x-job-names") - group_names = cuegui.Utils.dropEvent(event, "application/x-group-names") if job_ids or group_ids: body = "" if group_ids: - body += "Groups:\n" + "\n".join(cuegui.Utils.dropEvent(event, "application/x-group-names")) + body += "Groups:\n" + "\n".join( + cuegui.Utils.dropEvent(event, "application/x-group-names")) if group_ids and job_ids: body += "\n\n" if job_ids: - body += "Jobs:\n" + "\n".join(cuegui.Utils.dropEvent(event, "application/x-job-names")) + body += "Jobs:\n" + "\n".join( + cuegui.Utils.dropEvent(event, "application/x-job-names")) result = QtWidgets.QMessageBox.question( self, @@ -290,8 +304,8 @@ def addShow(self, show, update=True): if show not in self.__shows: try: self.__shows[show] = opencue.api.findShow(show) - except: - logger.warning("This show does not exist: %s" % show) + except opencue.exception.EntityNotFoundException: + logger.warning("This show does not exist: %s", show) if update: self._update() @@ -332,9 +346,9 @@ def __getCollapsed(self): def __setCollapsed(self, collapsed): self.expandAll() - for id in collapsed: - if id in self._items: - self._items[id].setExpanded(False) + for itemId in collapsed: + if itemId in self._items: + self._items[itemId].setExpanded(False) def _getUpdate(self): """Returns a list of NestedGroup from the cuebot for the monitored shows @@ -349,27 +363,28 @@ def _getUpdate(self): for group in groups: nestedGroups.append(opencue.wrappers.group.NestedGroup(group)) allIds.extend(self.__getNestedIds(group)) - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return None return [nestedGroups, allIds] - def _processUpdate(self, work, results): + def _processUpdate(self, work, rpcObjects): """Adds or updates jobs and groups. Removes those that do not get updated @type work: from threadpool @param work: from threadpool - @type results: [list, set(str)] - @param results: List that contains updated nested groups and a set + @type rpcObjects: [list, set(str)] + @param rpcObjects: List that contains updated nested groups and a set of all updated item ids""" - if results is None: + if rpcObjects is None: return self._itemsLock.lockForWrite() + # pylint: disable=broad-except try: current = set(self._items.keys()) - if current == set(results[1]): + if current == set(rpcObjects[1]): # Only updates - self.__processUpdateHandleNested(self.invisibleRootItem(), results[0]) + self.__processUpdateHandleNested(self.invisibleRootItem(), rpcObjects[0]) self.redraw() else: # (Something removed) or (Something added) @@ -378,10 +393,11 @@ def _processUpdate(self, work, results): scrolled = self.verticalScrollBar().value() self._items = {} self.clear() - self.__processUpdateHandleNested(self.invisibleRootItem(), results[0]) + self.__processUpdateHandleNested(self.invisibleRootItem(), rpcObjects[0]) self.__setCollapsed(collapsed) self.verticalScrollBar().setValue(scrolled) - [self._items[id_].setSelected(True) for id_ in selected_ids if id_ in self._items] + list(map(lambda id_: self._items[id_].setSelected(True), + [id_ for id_ in selected_ids if id_ in self._items])) except Exception: logger.warning("Failed to process update.", exc_info=True) finally: @@ -394,17 +410,17 @@ def __getNestedIds(self, group): @rtype: list @return: The list of all child ids""" updated = [] - for group in group.groups.nested_groups: - updated.append(group.id) + for innerGroup in group.groups.nested_groups: + updated.append(innerGroup.id) # If group has groups, recursively call this function - for g in group.groups.nested_groups: + for g in innerGroup.groups.nested_groups: updated_g = self.__getNestedIds(g) if updated_g: updated.extend(updated_g) # If group has jobs, update them - for jobId in group.jobs: + for jobId in innerGroup.jobs: updated.append(jobId) return updated @@ -428,7 +444,9 @@ def __processUpdateHandleNested(self, parent, groups): else: self._items[group.id()] = groupItem = RootGroupWidgetItem(group, parent) - nestedGroups = [opencue.wrappers.group.NestedGroup(nestedGroup) for nestedGroup in group.data.groups.nested_groups] + nestedGroups = [ + opencue.wrappers.group.NestedGroup(nestedGroup) + for nestedGroup in group.data.groups.nested_groups] self.__processUpdateHandleNested(groupItem, nestedGroups) for jobId in group.data.jobs: @@ -439,9 +457,11 @@ def __processUpdateHandleNested(self, parent, groups): else: self._items[job.id()] = JobWidgetItem(job, groupItem) except RuntimeError: - logger.warning("Failed to create tree item. RootView might be closed", exc_info=True) + logger.warning( + "Failed to create tree item. RootView might be closed", exc_info=True) - def mouseDoubleClickEvent(self,event): + def mouseDoubleClickEvent(self, event): + del event objects = self.selectedObjects() if objects: self.view_object.emit(objects[0]) @@ -513,7 +533,6 @@ def contextMenuEvent(self, e): if counts["job"] == 1: self.__menuActions.jobs().addAction(menu, "reorder") self.__menuActions.jobs().addAction(menu, "stagger") - #Broken: self.__menuActions.jobs().addAction(menu, "testCloBook") menu.addSeparator() if jobTypes["unpaused"]: self.__menuActions.jobs().addAction(menu, "pause") @@ -554,9 +573,17 @@ def actionResumeSelectedItems(self): """Resume selected jobs""" self.__menuActions.jobs().resume() + def tick(self): + pass + + class RootGroupWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): + """Widget item representing a single root group.""" + __initialized = False - def __init__(self, object, parent): + + # pylint: disable=protected-access + def __init__(self, rpcObject, parent): if not self.__initialized: if cuegui.Style.ColorTheme is None: cuegui.Style.init() @@ -567,7 +594,7 @@ def __init__(self, object, parent): self.__class__.__type = cuegui.Constants.TYPE_ROOTGROUP cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_ROOTGROUP, object, parent) + self, cuegui.Constants.TYPE_ROOTGROUP, rpcObject, parent) def data(self, col, role): """Returns the proper display data for the given column and role @@ -580,16 +607,16 @@ def data(self, col, role): if role == QtCore.Qt.DisplayRole: return self.column_info[col][cuegui.Constants.COLUMN_INFO_DISPLAY](self.rpcObject) - elif role == QtCore.Qt.FontRole: + if role == QtCore.Qt.FontRole: return FONT_BOLD - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: return self.__foregroundColor - elif role == QtCore.Qt.BackgroundRole: + if role == QtCore.Qt.BackgroundRole: return self.__backgroundColor - elif role == QtCore.Qt.DecorationRole: + if role == QtCore.Qt.DecorationRole: if col == 0: return self.__icon @@ -610,8 +637,11 @@ def __ne__(self, other): class GroupWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): """Represents a group entry in the MonitorCue widget.""" + __initialized = False - def __init__(self, object, parent): + + # pylint: disable=protected-access + def __init__(self, rpcObject, parent): if not self.__initialized: self.__class__.__initialized = True self.__class__.__icon = QtGui.QIcon(":group.png") @@ -620,7 +650,7 @@ def __init__(self, object, parent): self.__class__.__type = cuegui.Constants.TYPE_GROUP cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_GROUP, object, parent) + self, cuegui.Constants.TYPE_GROUP, rpcObject, parent) def data(self, col, role): """Returns the proper display data for the given column and role @@ -633,19 +663,19 @@ def data(self, col, role): if role == QtCore.Qt.DisplayRole: return self.column_info[col][cuegui.Constants.COLUMN_INFO_DISPLAY](self.rpcObject) - elif role == QtCore.Qt.FontRole: + if role == QtCore.Qt.FontRole: return FONT_BOLD - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: return self.__foregroundColor - elif role == QtCore.Qt.BackgroundRole: + if role == QtCore.Qt.BackgroundRole: return self.__backgroundColor - elif role == QtCore.Qt.DecorationRole and col == 0: + if role == QtCore.Qt.DecorationRole and col == 0: return self.__icon - elif role == QtCore.Qt.UserRole: + if role == QtCore.Qt.UserRole: return self.__type return cuegui.Constants.QVARIANT_NULL @@ -659,19 +689,23 @@ def __lt__(self, other): def __ne__(self, other): if hasattr(other, 'rpcObject'): return other.rpcObject != self.rpcObject - else: - return True + return True class JobWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): """Represents a job entry in the MonitorCue widget.""" + __initialized = False - def __init__(self, object, parent): + + # pylint: disable=protected-access + def __init__(self, rpcObject, parent): if not self.__initialized: self.__class__.__initialized = True self.__class__.__commentIcon = QtGui.QIcon(":comment.png") self.__class__.__eatIcon = QtGui.QIcon(":eat.png") + # pylint: disable=no-member self.__class__.__backgroundColor = QtGui.qApp.palette().color(QtGui.QPalette.Base) + # pylint: enable=no-member self.__class__.__foregroundColor = cuegui.Style.ColorTheme.COLOR_JOB_FOREGROUND self.__class__.__pausedColor = cuegui.Style.ColorTheme.COLOR_JOB_PAUSED_BACKGROUND self.__class__.__finishedColor = cuegui.Style.ColorTheme.COLOR_JOB_FINISHED_BACKGROUND @@ -681,10 +715,10 @@ def __init__(self, object, parent): self.__class__.__highMemoryColor = cuegui.Style.ColorTheme.COLOR_JOB_HIGH_MEMORY self.__class__.__type = cuegui.Constants.TYPE_JOB - object.parent = None + rpcObject.parent = None cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_JOB, object, parent) + self, cuegui.Constants.TYPE_JOB, rpcObject, parent) def data(self, col, role): """Returns the proper display data for the given column and role @@ -700,13 +734,13 @@ def data(self, col, role): self.column_info[col][cuegui.Constants.COLUMN_INFO_DISPLAY](self.rpcObject) return self._cache.get(col, cuegui.Constants.QVARIANT_NULL) - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: return self.__foregroundColor - elif role == QtCore.Qt.BackgroundRole: + if role == QtCore.Qt.BackgroundRole: if col == COLUMN_MAXRSS and \ self.rpcObject.data.job_stats.max_rss > cuegui.Constants.MEMORY_WARNING_LEVEL: - return self.__highMemoryColor + return self.__highMemoryColor if self.rpcObject.data.is_paused: return self.__pausedColor if self.rpcObject.data.job_stats.dead_frames: @@ -720,10 +754,10 @@ def data(self, col, role): return self.__noRunningColor return self.__backgroundColor - elif role == QtCore.Qt.DecorationRole: + if role == QtCore.Qt.DecorationRole: if col == COLUMN_COMMENT and self.rpcObject.data.has_comment: return self.__commentIcon - elif col == COLUMN_EAT and self.rpcObject.data.auto_eat: + if col == COLUMN_EAT and self.rpcObject.data.auto_eat: return self.__eatIcon elif role == QtCore.Qt.UserRole: diff --git a/cuegui/cuegui/CueStateBarWidget.py b/cuegui/cuegui/CueStateBarWidget.py index f77a1e5e1..3bf8be62d 100644 --- a/cuegui/cuegui/CueStateBarWidget.py +++ b/cuegui/cuegui/CueStateBarWidget.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Widget that graphically displays the state of all jobs displayed.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -32,22 +35,29 @@ class CueStateBarWidget(QtWidgets.QWidget): - """Creates a bar that graphically displays the state of all jobs displayed""" + """Widget that graphically displays the state of all jobs displayed.""" + __colorInvalid = QtGui.QColor() __brushPattern = QtGui.QBrush(QtCore.Qt.Dense4Pattern) - def __init__(self, sourceTree, parent = None): + + def __init__(self, sourceTree, parent=None): """CueStateBar init @type sourceTree: QTreeWidget @param sourceTree: The tree to get the jobs from @type parent: QWidget @param parent: The parent widget""" QtWidgets.QWidget.__init__(self, parent) + + self.__background = None + self.setContentsMargins(8, 1, 1, 1) self.setFixedWidth(22) self.__sourceTree = weakref.proxy(sourceTree) self.__colors = [] + # pylint: disable=no-member self.__baseColor = QtGui.qApp.palette().color(QtGui.QPalette.Base) + # pylint: enable=no-member self.__colorsLock = QtCore.QReadWriteLock() self.__timer = QtCore.QTimer(self) self.__lastUpdate = 0 @@ -84,6 +94,7 @@ def paintEvent(self, event): """Called when the widget is being redrawn @type event: QEvent @param event: The draw event""" + del event assert threading.currentThread().getName() == "MainThread" self.__colorsLock.lockForWrite() try: diff --git a/cuegui/cuegui/Cuedepend.py b/cuegui/cuegui/Cuedepend.py index e16ea56ce..2997513e5 100644 --- a/cuegui/cuegui/Cuedepend.py +++ b/cuegui/cuegui/Cuedepend.py @@ -13,7 +13,8 @@ # limitations under the License. -""" +"""Utility functions for creating depends. + Dependency Types: The long and short version of dependency types are valid. In most cases its not required to actually specify a dependency type. @@ -22,25 +23,6 @@ LayerOnJob / loj LayerOnLayer / lol LayerOnFrame / lof FrameOnJob / foj FrameOnLayer / fol FrameOnFrame / fof FrameByFRame / fbf HardDepend / hd - -Examples: - Create a hard depend between two jobs (all layers) - cuedepend -c -t hd -job pipe-dev.cue-chambers_shell_v1 -on-job pipe-dev.cue-chambers_shell_v2 - - Create a frame by frame dependency between two layers in the same job - cuedepend -t fbf -c pipe-dev.cue-chambers_shell_v1 -layer pass_1 -on-layer pass_2 - - Create a frame by frame dependency between two layers in different jobs - cuedepend -c -t fbf -job pipe-dev.cue-chambers_comp_v1 -layer comp -on-job pipe-dev.cue-chambers_render_v1 -on-layer bty_pass - - Create a depependency between a layer and a frame - cuedepend -c -job pipe-dev.cue-chambers_j1 -layer render -on-layer setup -on-frame 1 - - Drop a dependency using the unique id: - cuedepend -d baafaadc-498c-4100-a74d-42abd2b8e6b9 - - Drop all dependencies a job is waiting for - cuedepend -drop-all -j pipe-dev.cue-chambers_comp_v1 """ @@ -54,29 +36,35 @@ from opencue.compiled_proto import depend_pb2 -logger = logging.getLogger("opencue.tools.cuedepend") +logger = logging.getLogger(__file__) + -ERR_INVALID_ON_JOB = "Error, a dependency of this type requires a valid job name to depend on. See -on-job." -ERR_INVALID_ON_LAYER = "Error, a dependency of this type requires a valid layer name to depend on. See -on-layer." -ERR_INVALID_ON_FRAME = "Error, a dependency of this type requries a valid frame name to depend on. See -on-frame." -ERR_INVALID_ER_JOB = "Error, a dependency of this type requires a valid job name to depend on. See -job." -ERR_INVALID_ER_LAYER = "Error, a dependency of this type requires a valid layer name to depend on. See -layer." -ERR_INVALID_ER_FRAME = "Error, a dependency of this type requries a valid frame name to depend on. See -frame." +ERR_INVALID_ON_JOB = ( + "Error, a dependency of this type requires a valid job name to depend on. See -on-job.") +ERR_INVALID_ON_LAYER = ( + "Error, a dependency of this type requires a valid layer name to depend on. See -on-layer.") +ERR_INVALID_ON_FRAME = ( + "Error, a dependency of this type requries a valid frame name to depend on. See -on-frame.") +ERR_INVALID_ER_JOB = ( + "Error, a dependency of this type requires a valid job name to depend on. See -job.") +ERR_INVALID_ER_LAYER = ( + "Error, a dependency of this type requires a valid layer name to depend on. See -layer.") +ERR_INVALID_ER_FRAME = ( + "Error, a dependency of this type requries a valid frame name to depend on. See -frame.") def __is_valid(value, error): - """A couple sanity checks before. The gRpc library takes - care of everything else""" + """Minor depend validation. The gRPC library takes care of everything else.""" if not value: raise ValueError(error) if isinstance(value, str) and len(value) < 1: - raise ValueError(error) + raise ValueError(error) -def createDepend(type, job, layer, frame, onjob, onlayer, onframe): +def createDepend(depend_type, job, layer, frame, onjob, onlayer, onframe): """Creates a new dependency of the specified type. - @type type: string - @param type: The type of dependency + @type depend_type: string + @param depend_type: The type of dependency @type job: string @param job: The name of the dependant job @type layer: string @@ -93,13 +81,14 @@ def createDepend(type, job, layer, frame, onjob, onlayer, onframe): @return: The newly created dependency""" if not onjob and not onlayer and not onframe: - raise ValueError("You must specify something to depend on, see -on-job, -on-layer, -on-frame") + raise ValueError( + "You must specify something to depend on, see -on-job, -on-layer, -on-frame") if not onjob: logger.debug("assuming internal depend") onjob = job - typeName = depend_pb2.DependType.Name(type) + typeName = depend_pb2.DependType.Name(depend_type) if typeName in ("HARD_DEPEND", "hd"): depend = createHardDepend(job, onjob) elif typeName in ("JOB_ON_JOB", "joj"): @@ -125,10 +114,11 @@ def createDepend(type, job, layer, frame, onjob, onlayer, onframe): elif typeName in ("LAYER_ON_SIM_FRAME", "los"): depend = createLayerOnSimFrameDepend(job, layer, onjob, onlayer, onframe) else: - raise Exception("invalid dependency type: %s" % (type)) + raise Exception("invalid dependency type: %s" % depend_type) return depend + def createHardDepend(job, onjob): """Creates a frame by frame dependency for all non-preprocess/refshow layers (Hard Depend) @@ -144,15 +134,15 @@ def createHardDepend(job, onjob): depends = [] - logger.debug("creating hard depend from %s to %s" % (job, onjob)) + logger.debug("creating hard depend from %s to %s", job, onjob) onLayers = opencue.api.findJob(onjob).getLayers() for depend_er_layer in opencue.api.findJob(job).getLayers(): - for depend_on_layer in onLayers: - if depend_er_layer.data.type == depend_on_layer.data.type: - depends.append(depend_er_layer.createFrameByFrameDependency(depend_on_layer, - False)) + for depend_on_layer in onLayers: + if depend_er_layer.data.type == depend_on_layer.data.type: + depends.append(depend_er_layer.createFrameByFrameDependency(depend_on_layer, False)) return depends + def createJobOnJobDepend(job, onjob): """Creates a job on job dependency. (Soft Depend) @@ -165,10 +155,11 @@ def createJobOnJobDepend(job, onjob): __is_valid(job, ERR_INVALID_ER_JOB) __is_valid(onjob, ERR_INVALID_ON_JOB) - logger.debug("creating joj depend from %s to %s" % (job, onjob)) + logger.debug("creating joj depend from %s to %s", job, onjob) depend_er_job = opencue.api.findJob(job) return depend_er_job.createDependencyOnJob(opencue.api.findJob(onjob)) + def createJobOnLayerDepend(job, onjob, onlayer): """Creates a job on layer dependency @type job: string @@ -183,11 +174,12 @@ def createJobOnLayerDepend(job, onjob, onlayer): __is_valid(onjob, ERR_INVALID_ON_JOB) __is_valid(onlayer, ERR_INVALID_ON_LAYER) - logger.debug("creating jol depend from %s to %s/%s" % (job, onjob, onlayer)) + logger.debug("creating jol depend from %s to %s/%s", job, onjob, onlayer) depend_er_job = opencue.api.findJob(job) depend_on_layer = opencue.api.findLayer(onjob, onlayer) return depend_er_job.createDependencyOnLayer(depend_on_layer) + def createJobOnFrameDepend(job, onjob, onlayer, onframe): """Creates a job on frame dependency @type job: string @@ -205,12 +197,12 @@ def createJobOnFrameDepend(job, onjob, onlayer, onframe): __is_valid(onlayer, ERR_INVALID_ON_LAYER) __is_valid(onframe, ERR_INVALID_ON_FRAME) - logger.debug("creating jof depend from %s to %s/%s-%04d" - % (job, onjob, onlayer, onframe)) + logger.debug("creating jof depend from %s to %s/%s-%04d", job, onjob, onlayer, onframe) depend_er_job = opencue.api.findJob(job) depend_on_frame = opencue.api.findFrame(onjob, onlayer, onframe) return depend_er_job.createDependencyOnFrame(depend_on_frame) + def createLayerOnJobDepend(job, layer, onjob): """Creates a layer on job dependency @type job: string @@ -226,10 +218,11 @@ def createLayerOnJobDepend(job, layer, onjob): __is_valid(layer, ERR_INVALID_ER_LAYER) __is_valid(onjob, ERR_INVALID_ON_JOB) - logger.debug("creating loj depend from %s/%s to %s" % (job, layer, onjob)) + logger.debug("creating loj depend from %s/%s to %s", job, layer, onjob) depend_er_layer = opencue.api.findLayer(job, layer) return depend_er_layer.createDependencyOnJob(opencue.api.findJob(onjob)) + def createLayerOnLayerDepend(job, layer, onjob, onlayer): """Creates a layer on layer dependency @type job: string @@ -248,12 +241,12 @@ def createLayerOnLayerDepend(job, layer, onjob, onlayer): __is_valid(onjob, ERR_INVALID_ON_JOB) __is_valid(onlayer, ERR_INVALID_ON_LAYER) - logger.debug("creating lol depend from %s/%s to %s/%s" - % (job, layer, onjob, onlayer)) + logger.debug("creating lol depend from %s/%s to %s/%s", job, layer, onjob, onlayer) depend_er_layer = opencue.api.findLayer(job,layer) depend_on_layer = opencue.api.findLayer(onjob, onlayer) return depend_er_layer.createDependencyOnLayer(depend_on_layer) + def createLayerOnFrameDepend(job, layer, onjob, onlayer, onframe): """Creates a layer on frame dependency @type job: string @@ -275,12 +268,13 @@ def createLayerOnFrameDepend(job, layer, onjob, onlayer, onframe): __is_valid(onlayer, ERR_INVALID_ON_LAYER) __is_valid(onframe, ERR_INVALID_ON_FRAME) - logger.debug("creating lof depend from %s/%s to %s/%s-%04d" - % (job, layer, onjob, onlayer, onframe)) + logger.debug( + "creating lof depend from %s/%s to %s/%s-%04d", job, layer, onjob, onlayer, onframe) depend_er_layer = opencue.api.findLayer(job,layer) depend_on_frame = opencue.api.findFrame(onjob, onlayer, onframe) return depend_er_layer.createDependencyOnFrame(depend_on_frame) + def createFrameOnJobDepend(job, layer, frame, onjob): """Creates a frame on job dependency @type job: string @@ -299,11 +293,11 @@ def createFrameOnJobDepend(job, layer, frame, onjob): __is_valid(frame, ERR_INVALID_ER_FRAME) __is_valid(onjob, ERR_INVALID_ON_JOB) - logger.debug("creating foj depend from %s/%s-%04d to %s" - % (job, layer, frame, onjob)) + logger.debug("creating foj depend from %s/%s-%04d to %s", job, layer, frame, onjob) depend_er_frame = opencue.api.findFrame(job, layer, frame) return depend_er_frame.createDependencyOnJob(opencue.api.findJob(onjob)) + def createFrameOnLayerDepend(job, layer, frame, onjob, onlayer): """Creates a frame on layer dependency @type job: string @@ -324,12 +318,12 @@ def createFrameOnLayerDepend(job, layer, frame, onjob, onlayer): __is_valid(onjob, ERR_INVALID_ON_JOB) __is_valid(onlayer, ERR_INVALID_ON_LAYER) - logger.debug("creating fol depend from %s/%s-%04d to %s/%s" - % (job, layer, frame, onjob, onlayer)) + logger.debug("creating fol depend from %s/%s-%04d to %s/%s", job, layer, frame, onjob, onlayer) depend_er_frame = opencue.api.findFrame(job, layer, frame) depend_on_layer = opencue.api.findLayer(onjob, onlayer) return depend_er_frame.createDependencyOnLayer(depend_on_layer) + def createFrameOnFrameDepend(job, layer, frame, onjob, onlayer, onframe): """Creates a frame on frame dependency @type job: string @@ -354,12 +348,14 @@ def createFrameOnFrameDepend(job, layer, frame, onjob, onlayer, onframe): __is_valid(onlayer, ERR_INVALID_ON_LAYER) __is_valid(onframe, ERR_INVALID_ON_FRAME) - logger.debug("creating fof depend from %s/%s-%04d to %s/%s-%04d" - % (job, layer, frame, onjob, onlayer,onframe)) + logger.debug( + "creating fof depend from %s/%s-%04d to %s/%s-%04d", + job, layer, frame, onjob, onlayer, onframe) depend_er_frame = opencue.api.findFrame(job, layer, frame) depend_on_frame = opencue.api.findFrame(onjob, onlayer, onframe) return depend_er_frame.createDependencyOnFrame(depend_on_frame) + def createFrameByFrameDepend(job, layer, onjob, onlayer): """Creates a frame by frame dependency @type job: string @@ -378,13 +374,13 @@ def createFrameByFrameDepend(job, layer, onjob, onlayer): __is_valid(onjob, ERR_INVALID_ON_JOB) __is_valid(onlayer, ERR_INVALID_ON_LAYER) - logger.debug("creating fbf depend from %s/%s to %s/%s" - % (job, layer, onjob, onlayer)) + logger.debug("creating fbf depend from %s/%s to %s/%s", job, layer, onjob, onlayer) depend_er_layer = opencue.api.findLayer(job, layer) depend_on_layer = opencue.api.findLayer(onjob, onlayer) return depend_er_layer.createFrameByFrameDependency(depend_on_layer, False) + def createLayerOnSimFrameDepend(job, layer, onjob, onlayer, onframe): """Creates a layer on sim frame dependency @type job: string @@ -406,22 +402,19 @@ def createLayerOnSimFrameDepend(job, layer, onjob, onlayer, onframe): __is_valid(onlayer, ERR_INVALID_ON_LAYER) __is_valid(onframe, ERR_INVALID_ON_FRAME) - logger.debug("creating los depend from %s/%s to %s/%s-%04d" - % (job, layer, onjob, onlayer, onframe)) + logger.debug( + "creating los depend from %s/%s to %s/%s-%04d", job, layer, onjob, onlayer, onframe) depend_er_layer = opencue.api.findLayer(job,layer) depend_on_frame = opencue.api.findFrame(onjob, onlayer, onframe) - # if createSimDependencyOnFrame existed, we would use it here: - #return depend_er_layer.createSimDependencyOnFrame(depend_on_frame) - depends = [] for depend_er_frame in depend_er_layer.getFrames(): depends.append(depend_er_frame.createDependencyOnFrame(depend_on_frame)) return depends -def dropDepend(id): - """deactivates a dependency by GUID - @type id: string - @param id: the GUID of the dependency""" - opencue.api.getDepend(id).satisfy() +def dropDepend(depend_id): + """deactivates a dependency by GUID + @type depend_id: string + @param depend_id: the GUID of the dependency""" + opencue.api.getDepend(depend_id).satisfy() diff --git a/cuegui/cuegui/DarkPalette.py b/cuegui/cuegui/DarkPalette.py index 304f334a0..2b15bd66a 100644 --- a/cuegui/cuegui/DarkPalette.py +++ b/cuegui/cuegui/DarkPalette.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -The dark widget color scheme used by image viewing applications. -""" +"""The dark widget color scheme used by image viewing applications.""" from __future__ import absolute_import @@ -34,6 +32,7 @@ def init(): """Convenience function that takes the QApplication object for the application and configures the palette and style for the Plastique color scheme""" + # pylint: disable=no-member QtGui.qApp.setPalette(DarkPalette()) if platform.system() in ['Darwin', 'Linux']: setDarkStyleSheet() @@ -44,13 +43,13 @@ def init(): def setDarkStyleSheet(): + """Sets the stylesheet.""" + # pylint: disable=no-member QtGui.qApp.setStyleSheet(open(cuegui.Constants.DARK_STYLE_SHEET).read()) def DarkPalette(): - """The dark widget color scheme used by image viewing applications - at Imageworks. - """ + """The dark widget color scheme used by image viewing applications.""" p = QtGui.QPalette() c = GreyF(0.175) @@ -103,12 +102,14 @@ def DarkPalette(): def GreyF(value): + """Creates a grey color.""" c = QtGui.QColor() c.setRgbF(value, value, value) return c def ColorF(r, g, b): + """Creates an RGB color.""" c = QtGui.QColor() c.setRgbF(r, g, b) return c diff --git a/cuegui/cuegui/DependDialog.py b/cuegui/cuegui/DependDialog.py index 26e78e333..ce94f526f 100644 --- a/cuegui/cuegui/DependDialog.py +++ b/cuegui/cuegui/DependDialog.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Dialog displaying a list of dependencies for an object.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -29,7 +32,9 @@ class DependDialog(QtWidgets.QDialog): - def __init__(self, object, parent=None): + """Dialog displaying a list of dependencies for an object.""" + + def __init__(self, rpcOject, parent=None): super(DependDialog, self).__init__(parent) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setSizeGripEnabled(True) @@ -37,18 +42,18 @@ def __init__(self, object, parent=None): self.resize(1000, 600) name = "Dependencies for " - if cuegui.Utils.isJob(object): - name += "Job: %s" % object.data.name - elif cuegui.Utils.isLayer(object): - name += "Layer: %s" % object.data.name - elif cuegui.Utils.isFrame(object): - name += "Frame: %s" % object.data.name + if cuegui.Utils.isJob(rpcOject): + name += "Job: %s" % rpcOject.data.name + elif cuegui.Utils.isLayer(rpcOject): + name += "Layer: %s" % rpcOject.data.name + elif cuegui.Utils.isFrame(rpcOject): + name += "Frame: %s" % rpcOject.data.name self.setWindowTitle(name) self.hlayout = QtWidgets.QHBoxLayout(self) - self._depend = cuegui.DependMonitorTree.DependMonitorTree(self, object) + self._depend = cuegui.DependMonitorTree.DependMonitorTree(self, rpcOject) self.hlayout.addWidget(self._depend) self.setLayout(self.hlayout) diff --git a/cuegui/cuegui/DependMonitorTree.py b/cuegui/cuegui/DependMonitorTree.py index 7516a1347..fed246a36 100644 --- a/cuegui/cuegui/DependMonitorTree.py +++ b/cuegui/cuegui/DependMonitorTree.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Tree for displaying a list of depends.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -22,6 +25,7 @@ from PySide2 import QtWidgets from opencue.compiled_proto import depend_pb2 +import opencue.exception import cuegui.AbstractTreeWidget import cuegui.AbstractWidgetItem @@ -35,40 +39,35 @@ class DependMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): - def __init__(self, parent, object): + """Tree for displaying a list of depends.""" + + def __init__(self, parent, rpcObject): self.startColumnsForType(cuegui.Constants.TYPE_DEPEND) self.addColumn("Type", 130, id=1, data=lambda depend: depend_pb2.DependType.Name(depend.type())) self.addColumn("Target", 60, id=2, data=lambda depend: depend_pb2.DependTarget.Name(depend.target())) self.addColumn("Active", 50, id=3, - data=lambda depend:(depend.isActive())) -# self.addColumn("Job", 230, id=4, -# data=lambda depend:(depend.dependErJob())) -# self.addColumn("Layer", 50, id=5, -# data=lambda depend:(depend.dependErLayer())) -# self.addColumn("Frame", 100, id=6, -# data=lambda depend:(depend.dependErFrame())) + data=lambda depend: (depend.isActive())) self.addColumn("OnJob", 300, id=7, - data=lambda depend:(depend.dependOnJob())) + data=lambda depend: (depend.dependOnJob())) self.addColumn("OnLayer", 200, id=8, - data=lambda depend:(depend.dependOnLayer())) + data=lambda depend: (depend.dependOnLayer())) self.addColumn("OnFrame", 100, id=9, - data=lambda depend:(depend.dependOnFrame())) + data=lambda depend: (depend.dependOnFrame())) - self.rpcObject = object + self.rpcObject = rpcObject cuegui.AbstractTreeWidget.AbstractTreeWidget.__init__(self, parent) - # Used to build right click context menus self.__menuActions = cuegui.MenuActions.MenuActions( self, self.updateSoon, self.selectedObjects) self.setUpdateInterval(60) - def _createItem(self, object): + def _createItem(self, rpcObject): """Creates and returns the proper item""" - return DependWidgetItem(object, self) + return DependWidgetItem(rpcObject, self) def _getUpdate(self): """Returns the proper data from the cuebot""" @@ -76,7 +75,7 @@ def _getUpdate(self): if hasattr(self.rpcObject, "getDepends"): return self.rpcObject.getDepends() return self.rpcObject.getWhatThisDependsOn() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] @@ -86,13 +85,16 @@ def contextMenuEvent(self, e): menu = QtWidgets.QMenu() self.__menuActions.dependencies().addAction(menu, "satisfy") - #self.__menuActions.dependencies().addAction(menu, "unsatisfy") menu.exec_(e.globalPos()) -################################################################################ + def tick(self): + pass + class DependWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single depend.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_DEPEND, object, parent) \ No newline at end of file + self, cuegui.Constants.TYPE_DEPEND, rpcObject, parent) diff --git a/cuegui/cuegui/DependWizard.py b/cuegui/cuegui/DependWizard.py index 1c31721da..6910ee685 100644 --- a/cuegui/cuegui/DependWizard.py +++ b/cuegui/cuegui/DependWizard.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Wizard interface to setting up dependencies. -""" +"""Wizard interface for setting up dependencies.""" from __future__ import absolute_import @@ -84,17 +82,27 @@ PROGRESS_TEXT = "Are you sure you want to cancel setting up these dependencies?\n\n" + \ "The dependencies that are already partially setup will still remain." + class DependWizard(QtWidgets.QWizard): - def __init__(self, parent, jobs, layers = [], frames = []): + """Wizard interface for setting up dependencies.""" + + def __init__(self, parent, jobs, layers=None, frames=None): QtWidgets.QWizard.__init__(self, parent) # Only allow jobs from one show jobs = [job for job in jobs if job.data.show == jobs[0].data.show] self.jobs = jobs - self.layers = [layer.data.name for layer in layers] - self.layerOptions = layers - self.frames = [frame.data.name for frame in frames] + if layers is None: + self.layers = [] + self.layerOptions = [] + else: + self.layers = [layer.data.name for layer in layers] + self.layerOptions = layers + if frames is None: + self.frames = [] + else: + self.frames = [frame.data.name for frame in frames] self.dependType = None self.onJobOptions = [] @@ -104,14 +112,14 @@ def __init__(self, parent, jobs, layers = [], frames = []): self.onFrame = [] # Create the pages - self.__pages = {} - self.__pages [PAGE_SELECT_DEPEND_TYPE] = PageDependType(self, jobs, layers, frames) - self.__pages [PAGE_SELECT_JOB_LAYER] = PageSelectLayer(self) - self.__pages [PAGE_SELECT_JOB_FRAME] = PageSelectFrame(self) - self.__pages [PAGE_SELECT_ONJOB] = PageSelectOnJob(self) - self.__pages [PAGE_SELECT_ONLAYER] = PageSelectOnLayer(self) - self.__pages [PAGE_SELECT_ONFRAME] = PageSelectOnFrame(self) - self.__pages [PAGE_CONFIRMATION] = PageConfirmation(self, jobs, layers, frames) + self.__pages = {} + self.__pages[PAGE_SELECT_DEPEND_TYPE] = PageDependType(self, jobs, layers, frames) + self.__pages[PAGE_SELECT_JOB_LAYER] = PageSelectLayer(self) + self.__pages[PAGE_SELECT_JOB_FRAME] = PageSelectFrame(self) + self.__pages[PAGE_SELECT_ONJOB] = PageSelectOnJob(self) + self.__pages[PAGE_SELECT_ONLAYER] = PageSelectOnLayer(self) + self.__pages[PAGE_SELECT_ONFRAME] = PageSelectOnFrame(self) + self.__pages[PAGE_CONFIRMATION] = PageConfirmation(self, jobs, layers, frames) # Add the pages to the wizard for key in self.__pages : @@ -128,27 +136,27 @@ def __init__(self, parent, jobs, layers = [], frames = []): self.show() def _onJobOptionsPopulate(self): - """Populates self.onJobOptions to contain a list of job names for the - given jobs show""" + """Populates self.onJobOptions to contain a list of job names for the given job's show.""" self.onJobOptions = [] try: show = self.jobs[0].data.name.split('-')[0] self.onJobOptions = [name for name in sorted(opencue.api.getJobNames()) if name.startswith(show)] - except Exception as e: + except opencue.exception.CueException as e: logger.critical("Failed getting list of jobs") list(map(logger.critical, cuegui.Utils.exceptionOutput(e))) -################################################################################ class AbstractWizardPage(QtWidgets.QWizardPage): + """Base class for the depend wizard pages.""" + def __init__(self, parent): QtWidgets.QWizardPage.__init__(self, parent) self.setLayout(QtWidgets.QGridLayout(self)) self._widgets = [] - def _addLabel(self, text, row, col, rowSpan = 1, columnSpan = 1, align = QtCore.Qt.AlignLeft): + def _addLabel(self, text, row, col, rowSpan=1, columnSpan=1, align=QtCore.Qt.AlignLeft): """Adds a QLabel to the current WizardPage @type text: str @param text: The text to display in the edit box @@ -170,7 +178,7 @@ def _addLabel(self, text, row, col, rowSpan = 1, columnSpan = 1, align = QtCore. self._widgets.append(label) return label - def _addTextEdit(self, row, col, text, height = None): + def _addTextEdit(self, row, col, text, height=None): """Adds a QTextEdit to the current WizardPage @type row: int @param row: The row to place the widget @@ -205,22 +213,22 @@ def _addLineEdit(self, row, col, text): self._widgets.append(edit) return edit - def _addSpinBox(self, row, col, min, max, value): + def _addSpinBox(self, row, col, min_val, max_val, value): """Adds a line edit box to the current WizardPage @type row: int @param row: The row to place the widget @type col: int @param col: The column to place the widget - @type min: int - @param min: The minimum number to allow - @type max: int - @param max: The maximum number to allow + @type min_val: int + @param min_val: The minimum number to allow + @type max_val: int + @param max_val: The maximum number to allow @type value: int @param value: The value to display initially @rtype: QLineEdit @return: A reference to the new widget""" spin = QtWidgets.QSpinBox(self) - spin.setRange(min, max) + spin.setRange(min_val, max_val) spin.setValue(value) self.layout().addWidget(spin, row, col) self._widgets.append(spin) @@ -239,7 +247,7 @@ def _addCombo(self, row, col): self._widgets.append(combo) return combo - def _addListWidget(self, row, col, selection = None): + def _addListWidget(self, row, col, selection=None): """Adds a QListWidget to the current WizardPage. @type row: int @param row: The row to place the widget @@ -249,14 +257,15 @@ def _addListWidget(self, row, col, selection = None): @param selection: Allowed selection type @rtype: QListWidget @return: A reference to the new widget""" - list = QtWidgets.QListWidget(self) + list_widget = QtWidgets.QListWidget(self) if selection: - list.setSelectionMode(selection) - self.layout().addWidget(list, row, col) - self._widgets.append(list) - return list + list_widget.setSelectionMode(selection) + self.layout().addWidget(list_widget, row, col) + self._widgets.append(list_widget) + return list_widget - def _getNames(self, items): + @staticmethod + def _getNames(items): """Returns a list of names for all items provided. @type items: str, list, list, list or list @param items: Any items to return the names of @@ -266,9 +275,13 @@ def _getNames(self, items): return [] if isinstance(items, str): return [items] - return [item.data.name for item in items if hasattr(item, "data") and hasattr(item.data, "name")] + \ - [str(item) for item in items if not hasattr(item, "data")] + names_of_items_with_data = [ + item.data.name for item in items + if hasattr(item, "data") and hasattr(item.data, "name")] + names_of_items_without_data = [str(item) for item in items if not hasattr(item, "data")] + return names_of_items_with_data + names_of_items_without_data + # pylint: disable=inconsistent-return-statements def _displayItems(self, name, items, row): """Displays a label description and a list of items. If more than one item is given @@ -309,20 +322,26 @@ def _removeAllWidgets(self): self._widgets.remove(widget) widget.hide() -################################################################################ class PageDependType(AbstractWizardPage): - """This page asks the user for the type of dependency to setup + """This page asks the user for the type of dependency to create. + PAGE_SELECT_DEPEND_TYPE""" - def __init__(self, parent, jobs, layers = [], frames = []): + + def __init__(self, parent, jobs, layers=None, frames=None): AbstractWizardPage.__init__(self, parent) self.setTitle("Select Dependency Type") - # this should come from a field self.jobs = jobs - self.layers = layers - self.frames = frames + if layers is None: + self.layers = [] + else: + self.layers = layers + if frames is None: + self.frames = [] + else: + self.frames = frames self._displayItems("Job", jobs, 0) self._displayItems("Layer", layers, 1) @@ -353,13 +372,14 @@ def __init__(self, parent, jobs, layers = [], frames = []): self.layout().addWidget(self.__groupBox, 3, 0, 1, -1) + # pylint: disable=inconsistent-return-statements def __msg(self): for item in [("frame", self.wizard().frames), ("layer", self.wizard().layers), ("job", self.wizard().jobs)]: if len(item[1]) > 1: return "these %ss" % item[0] - elif item[1]: + if item[1]: return "this %s" % item[0] def initializePage(self): @@ -381,27 +401,29 @@ def nextId(self): @rtype: int""" if not self.wizard().dependType: return PAGE_SELECT_DEPEND_TYPE - elif self.frames: + if self.frames: return PAGE_SELECT_ONJOB - elif len(self.layers) == 1 and \ + if len(self.layers) == 1 and \ self.wizard().dependType in (FOJ, FOL, FOF): return PAGE_SELECT_JOB_FRAME - elif self.layers: + if self.layers: return PAGE_SELECT_ONJOB - elif len(self.jobs) == 1 and \ - self.wizard().dependType in (LOJ, LOL, LOF, FOJ, FOL, FOF, FBF, LOS): + if len(self.jobs) == 1 and \ + self.wizard().dependType in (LOJ, LOL, LOF, FOJ, FOL, FOF, FBF, LOS): return PAGE_SELECT_JOB_LAYER - elif self.jobs: + if self.jobs: return PAGE_SELECT_ONJOB - else: - logger.critical("error, no place to go: jobs:%s layers:%s frames:%s type:%s" % (len(self.jobs), len(self.layers), len(self.frames), self.wizard().dependType)) - raise RuntimeError() + logger.critical( + "error, no place to go: jobs:%s layers:%s frames:%s type:%s", + len(self.jobs), len(self.layers), len(self.frames), self.wizard().dependType) + raise RuntimeError() -################################################################################ class PageSelectLayer(AbstractWizardPage): - """This page asks the user for the layer that should depend on something + """This page asks the user for the layer that should depend on something. + PAGE_SELECT_JOB_LAYER""" + def __init__(self, parent): AbstractWizardPage.__init__(self, parent) @@ -418,10 +440,11 @@ def initializePage(self): QtWidgets.QWizardPage.initializePage(self) self.__layerList.clear() - self.__layerList.addItems([layer for layer in self._getNames(self.wizard().layerOptions)]) + self.__layerList.addItems(self._getNames(self.wizard().layerOptions)) for num in range(self.__layerList.count()): - self.__layerList.item(num).setSelected(str(self.__layerList.item(num).text()) in self._getNames(self.wizard().onLayer)) + self.__layerList.item(num).setSelected( + str(self.__layerList.item(num).text()) in self._getNames(self.wizard().onLayer)) def validatePage(self): self.wizard().layers = [] @@ -431,11 +454,10 @@ def validatePage(self): if self.wizard().layers: return True - QtWidgets.QMessageBox.warning(self, - "Warning", - "Please select one or more layers or go back " - "and change the dependency type", - QtWidgets.QMessageBox.Ok) + QtWidgets.QMessageBox.warning( + self, "Warning", + "Please select one or more layers or go back and change the dependency type", + QtWidgets.QMessageBox.Ok) return False def nextId(self): @@ -444,14 +466,14 @@ def nextId(self): @rtype: int""" if self.wizard().dependType in (FOJ, FOL, FOF): return PAGE_SELECT_JOB_FRAME - else: - return PAGE_SELECT_ONJOB + return PAGE_SELECT_ONJOB -################################################################################ class PageSelectFrame(AbstractWizardPage): - """This page asks the user for the frames that should depend on something + """This page asks the user for the frames that should depend on something. + PAGE_SELECT_JOB_FRAME""" + def __init__(self, parent): AbstractWizardPage.__init__(self, parent) @@ -468,6 +490,7 @@ def initializePage(self): def validatePage(self): frames = str(self.field("frame")) if frames: + # pylint: disable=broad-except try: fs = FileSequence.FrameSet(frames) fs.normalize() @@ -483,11 +506,12 @@ def nextId(self): @rtype: int""" return PAGE_SELECT_ONJOB -################################################################################ class PageSelectOnJob(AbstractWizardPage): - """This page asks the user for the job should be depended on + """This page asks the user for the job that should be depended on. + PAGE_SELECT_ONJOB""" + def __init__(self, parent): AbstractWizardPage.__init__(self, parent) @@ -502,14 +526,18 @@ def __init__(self, parent): self.__jobList = self._addListWidget(3, 0) def filterJobs(self, text): - # Exlcude job names that would cause a job to depend on itself + """Pre-filters the list of possible jobs. + + Excludes job names that would cause a job to depend on itself.""" exclude = [] if self.wizard().dependType in (JOJ, LOJ, FOJ, JFBF): for job in self.wizard().jobs: exclude.append(job.data.name) self.__jobList.clear() - self.__jobList.addItems([job for job in self.wizard().onJobOptions if re.search(str(text), job, re.IGNORECASE) and not job in exclude]) + self.__jobList.addItems( + [job for job in self.wizard().onJobOptions + if re.search(str(text), job, re.IGNORECASE) and job not in exclude]) def initializePage(self): # If the filter edit box is empty, populate it with SHOW-SHOT-USER_ @@ -523,7 +551,8 @@ def initializePage(self): self.__jobList.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) for num in range(self.__jobList.count()): - self.__jobList.item(num).setSelected(str(self.__jobList.item(num).text()) in self.wizard().onJob) + self.__jobList.item(num).setSelected( + str(self.__jobList.item(num).text()) in self.wizard().onJob) QtWidgets.QWizardPage.initializePage(self) @@ -548,14 +577,14 @@ def nextId(self): @rtype: int""" if self.wizard().dependType in (JOL, JOF, LOL, LOF, FOL, FOF, FBF, LOS): return PAGE_SELECT_ONLAYER - else: - return PAGE_CONFIRMATION + return PAGE_CONFIRMATION -################################################################################ class PageSelectOnLayer(AbstractWizardPage): - """This page asks the user for the layer should be depended on: + """This page asks the user for the layer that should be depended on. + PAGE_SELECT_ONLAYER""" + def __init__(self, parent): AbstractWizardPage.__init__(self, parent) @@ -571,7 +600,11 @@ def initializePage(self): self.wizard().onLayerOptions = opencue.api.findJob(self.wizard().onJob[0]).getLayers() if self.wizard().dependType in (LOS,): - self.wizard().onLayerOptions = [layer for layer in self.wizard().onLayerOptions if 'simulation' in layer.data.services or 'simulationhi' in layer.data.services or 'houdini' in layer.data.services] + self.wizard().onLayerOptions = [ + layer for layer in self.wizard().onLayerOptions + if 'simulation' in layer.data.services or + 'simulationhi' in layer.data.services or + 'houdini' in layer.data.services] if self.wizard().dependType in (JOL, LOL, FOL, FBF, JOF, LOF, FOF): self.__onLayerList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) @@ -579,10 +612,11 @@ def initializePage(self): self.__onLayerList.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) self.__onLayerList.clear() - self.__onLayerList.addItems([layer for layer in self._getNames(self.wizard().onLayerOptions)]) + self.__onLayerList.addItems(self._getNames(self.wizard().onLayerOptions)) for num in range(self.__onLayerList.count()): - self.__onLayerList.item(num).setSelected(str(self.__onLayerList.item(num).text()) in self._getNames(self.wizard().onLayer)) + self.__onLayerList.item(num).setSelected( + str(self.__onLayerList.item(num).text()) in self._getNames(self.wizard().onLayer)) def validatePage(self): self.wizard().onLayer = [] @@ -592,11 +626,10 @@ def validatePage(self): if self.wizard().onLayer: return True - QtWidgets.QMessageBox.warning(self, - "Warning", - "Please select one or more layers or go back " - "and change the dependency type", - QtWidgets.QMessageBox.Ok) + QtWidgets.QMessageBox.warning( + self, "Warning", + "Please select one or more layers or go back and change the dependency type", + QtWidgets.QMessageBox.Ok) return False def nextId(self): @@ -605,14 +638,14 @@ def nextId(self): @rtype: int""" if self.wizard().dependType in (JOF, LOF, FOF, LOS): return PAGE_SELECT_ONFRAME - else: - return PAGE_CONFIRMATION + return PAGE_CONFIRMATION -################################################################################ class PageSelectOnFrame(AbstractWizardPage): - """This page asks the user for the frame should be depended on: + """This page asks the user for the frame that should be depended on. + PAGE_SELECT_ONFRAME""" + def __init__(self, parent): AbstractWizardPage.__init__(self, parent) @@ -631,6 +664,7 @@ def initializePage(self): def validatePage(self): frames = str(self.field("onFrame")) if frames: + # pylint: disable=broad-except try: fs = FileSequence.FrameSet(frames) fs.normalize() @@ -646,14 +680,21 @@ def nextId(self): @rtype: int""" return PAGE_CONFIRMATION -################################################################################ class PageConfirmation(AbstractWizardPage): - """ + """Page to collect final confirmation of depend details before creating it. + PAGE_CONFIRMATION""" + def __init__(self, parent, jobs, layers, frames): + del jobs + del layers + del frames + AbstractWizardPage.__init__(self, parent) + self.work = [] + self.setTitle("Confirmation") self.setSubTitle("Are you sure?") @@ -680,6 +721,7 @@ def initializePage(self): if self.wizard().dependType in (JOF, LOF, FOF, LOS): self._displayItems("Frame", self.wizard().onFrame, 9) + # pylint: disable=too-many-nested-blocks def validatePage(self): # Just names: jobs = self._getNames(self.wizard().jobs) @@ -702,16 +744,11 @@ def validatePage(self): self.__addDependWork(layer, onLayer) cuegui.ProgressDialog.ProgressDialog( - "Setting up Hard Depend", - self.__createFrameByFrameDepend, - self.work, - 2, - PROGRESS_TITLE, - PROGRESS_TEXT, - self.parent()) + "Setting up Hard Depend", self.__createFrameByFrameDepend, self.work, 2, + PROGRESS_TITLE, PROGRESS_TEXT, self.parent()) return True - elif frames: + if frames: for onJob in onJobs: for onLayer in onLayers: for framelayer in frames: @@ -721,41 +758,44 @@ def validatePage(self): frame = framelayer layer = layers[0] for onFrame in onFrames: - self.__addDependWork(self.wizard().dependType, jobs[0], layer, int(frame), onJob, onLayer, onFrame) + self.__addDependWork( + self.wizard().dependType, jobs[0], layer, int(frame), + onJob, onLayer, onFrame) elif layers: for onJob in onJobs: for onLayer in onLayers: for layer in layers: for onFrame in onFrames: - self.__addDependWork(self.wizard().dependType, jobs[0], layer, None, onJob, onLayer, onFrame) + self.__addDependWork( + self.wizard().dependType, jobs[0], layer, None, + onJob, onLayer, onFrame) elif jobs: for onJob in onJobs: for onLayer in onLayers: for job in jobs: for onFrame in onFrames: - self.__addDependWork(self.wizard().dependType, job, None, None, onJob, onLayer, onFrame) + self.__addDependWork( + self.wizard().dependType, job, None, None, onJob, onLayer, onFrame) cuegui.ProgressDialog.ProgressDialog( - "Setting up dependencies", - cuegui.Cuedepend.createDepend, - self.work, - 2, - PROGRESS_TITLE, - PROGRESS_TEXT, - self.parent()) + "Setting up dependencies", cuegui.Cuedepend.createDepend, self.work, 2, PROGRESS_TITLE, + PROGRESS_TEXT, self.parent()) return True def __addDependWork(self, *args): - """Adds arguements for a call to Cuedepend.createDepend to a list + """Adds arguments for a call to Cuedepend.createDepend to a list. + @type args: string, string, string, int, string, string, int @param args: The arguements required by Cuedepend.createDepend""" self.work.append(args) - def __createFrameByFrameDepend(self, layer, onLayer): + @staticmethod + def __createFrameByFrameDepend(layer, onLayer): """A function callback provided to the ProgressDialog that sets up a - frame by frame dependency + frame by frame dependency. + @type layer: opencue.wrappers.layer.Layer @param layer: The layer that contains the frames that will have the dependency @type onLayer: opencue.wrappers.layer.Layer diff --git a/cuegui/cuegui/EmailDialog.py b/cuegui/cuegui/EmailDialog.py index e12ee777a..4775553d9 100644 --- a/cuegui/cuegui/EmailDialog.py +++ b/cuegui/cuegui/EmailDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Displays the email dialog when emailing an artist. -""" +"""Dialog for emailing a job owner.""" from __future__ import absolute_import @@ -55,12 +53,14 @@ class EmailDialog(QtWidgets.QDialog): - def __init__(self, job, format, parent = None): + """Dialog for emailing a job owner.""" + + def __init__(self, job, parent=None): QtWidgets.QDialog.__init__(self, parent) try: self.__frames = job.getFrames(state=[opencue.api.job_pb2.DEAD]) - except: + except opencue.exception.CueException: self.__frames = [] self.setWindowTitle("Email For: %s" % job.data.name) @@ -68,7 +68,7 @@ def __init__(self, job, format, parent = None): self.setSizeGripEnabled(True) self.setFixedSize(1000,600) - self.__email = EmailWidget(job, format, self) + self.__email = EmailWidget(job, self) self.__appendDeadFrameInfo(job) self.__logView = LogViewWidget(job, self.__frames, self) @@ -96,13 +96,13 @@ def __appendDeadFrameInfo(self, job): cuegui.Utils.secondsToHHMMSS(frame.runTime()), frame.retries())) i_total_render_time += frame.retries() * frame.runTime() i_total_retries += frame.retries() - try: - self.__email.appendToBody("\nEstimated Proc Hours: %0.2f\n\n" % \ - ((i_total_render_time / 3600.0))) - except: - pass + self.__email.appendToBody( + "\nEstimated Proc Hours: %0.2f\n\n" % (i_total_render_time / 3600.0)) + class LogViewWidget(QtWidgets.QWidget): + """Widget for displaying a log within the email dialog.""" + def __init__(self, job, frames, parent=None): QtWidgets.QWidget.__init__(self, parent) QtWidgets.QVBoxLayout(self) @@ -133,16 +133,19 @@ def __init__(self, job, frames, parent=None): self.__sel_frames.activated.connect(self.switchLogEvent) self.__txt_find.returnPressed.connect(self.findEvent) + # pylint: disable=inconsistent-return-statements def __getFrame(self, name): for frame in self.__frames: if frame.data.name == name: return frame def switchLogEvent(self, str_frame): + """Displays the log for the given frame.""" + # pylint: disable=broad-except try: self.__txt_log.clear() log_file_path = cuegui.Utils.getFrameLogFile(self.__job, self.__getFrame(str_frame)) - fp = open(log_file_path,"r") + fp = open(log_file_path, "r") if os.path.getsize(log_file_path) > 1242880: fp.seek(0, 2) fp.seek(-1242880, 1) @@ -155,7 +158,7 @@ def switchLogEvent(self, str_frame): except Exception as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) - logger.info("error loading frame: %s, %s" % (str_frame, e)) + logger.info("error loading frame: %s, %s", str_frame, e) def findEvent(self): """attempts to find the text from the find text box, @@ -164,17 +167,19 @@ def findEvent(self): cursor = document.find( str(self.__txt_find.text()).strip(), self.__txt_log.textCursor().position(), - QtWidgets.QTextDocument.FindBackward) + QtGui.QTextDocument.FindBackward) if cursor.position() > 1: self.__txt_log.setTextCursor(cursor) + class EmailWidget(QtWidgets.QWidget): + """Widget for displaying an email form.""" send = QtCore.Signal() cancel = QtCore.Signal() - def __init__(self, job, format, parent=None): - QtWidgets.QWidget.__init__(self) + def __init__(self, job, parent=None): + QtWidgets.QWidget.__init__(self, parent=parent) self.__job = job @@ -210,7 +215,7 @@ def __init__(self, job, format, parent=None): self.__email_bcc = QtWidgets.QLineEdit(__default_bcc, self) self.__email_subject = QtWidgets.QLineEdit(__default_subject, self) - # Main Virtical Layout + # Main Vertical Layout vlayout = QtWidgets.QVBoxLayout(self) # Top Grid Layout @@ -246,6 +251,7 @@ def __init__(self, job, format, parent=None): self.__btnCancel.clicked.connect(self.cancel.emit) def giveFocus(self): + """Initializes widget state when the widget gains focus.""" self.__email_body.setFocus(QtCore.Qt.OtherFocusReason) self.__email_body.moveCursor(QtGui.QTextCursor.Start) self.__email_body.moveCursor(QtGui.QTextCursor.Down) @@ -254,30 +260,39 @@ def giveFocus(self): self.__email_body.moveCursor(QtGui.QTextCursor.Down) def email_from(self): + """Gets the email sender.""" return "%s" % self.__email_from.text() def email_to(self): + """Gets the email recipient.""" return "%s" % self.__email_to.text() def email_cc(self): + """Gets the email CC field.""" return "%s" % self.__email_cc.text() def email_bcc(self): + """Gets the email BCC field.""" return "%s" % self.__email_bcc.text() def email_subject(self): + """Gets the email subject.""" return "%s" % self.__email_subject.text() def email_body(self): + """Get the email body text.""" return "%s" % self.__email_body.toPlainText().toAscii() def appendToBody(self, txt): + """Appends text to the email body.""" self.__email_body.append(txt) def setBody(self, txt): + """Sets the value of the email body.""" self.__email_body.setText(txt) def sendEmail(self): + """Sends the email.""" self.send.emit() msg = MIMEText(self.email_body()) diff --git a/cuegui/cuegui/FilterDialog.py b/cuegui/cuegui/FilterDialog.py index b121be897..f130eb77e 100644 --- a/cuegui/cuegui/FilterDialog.py +++ b/cuegui/cuegui/FilterDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Handles the dialog to display/modify a show's filters, matchers and actions -""" +"""Dialog to display/modify a show's filters, matchers and actions.""" from __future__ import absolute_import @@ -55,6 +53,8 @@ class FilterDialog(QtWidgets.QDialog): + """Dialog to display/modify a show's filters, matchers and actions.""" + def __init__(self, show, parent=None): """ Creates an instance of the FilterDialog. @@ -130,16 +130,20 @@ def __createFilter(self): def __refresh(self): """Calls update on the widgets""" + # pylint: disable=protected-access self.__filters._update() self.__matchers._update() self.__actions._update() def __itemSingleClicked(self, item, col): - filter = item.rpcObject - self.__matchers.setObject(filter) - self.__actions.setObject(filter) + del col + self.__matchers.setObject(item.rpcObject) + self.__actions.setObject(item.rpcObject) + class FilterMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree displaying a list of filters.""" + def __init__(self, show, parent): self.startColumnsForType(cuegui.Constants.TYPE_FILTER) self.addColumn("Order", 100, id=1, @@ -163,9 +167,9 @@ def __init__(self, show, parent): self, self.updateSoon, self.selectedObjects) self._timer.stop() - def _createItem(self, object): - """Creates and returns the proper item""" - return FilterWidgetItem(object, self) + def _createItem(self, filter_object): + """Creates and returns a widget item for the given filter.""" + return FilterWidgetItem(filter_object, self) def _processUpdate(self, work, rpcObjects): """Adds the feature of forcing the items to be sorted by the first @@ -176,7 +180,7 @@ def _getUpdate(self): """Returns the proper data from the cuebot""" try: return self.__show.getFilters() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] @@ -196,8 +200,14 @@ def contextMenuEvent(self, e): menu.exec_(e.globalPos()) + def tick(self): + pass + + class MatcherMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): - def __init__(self, filter, parent): + """Tree for displaying a list of filter matchers.""" + + def __init__(self, parent_filter, parent): self.startColumnsForType(cuegui.Constants.TYPE_MATCHER) self.addColumn("Matcher Subject", 130, id=1, data=lambda matcher:(matcher.subject())) @@ -207,7 +217,7 @@ def __init__(self, filter, parent): data=lambda matcher:(matcher.input())) self.addColumn("", 20, id=4) - self.__filter = filter + self.__filter = parent_filter cuegui.AbstractTreeWidget.AbstractTreeWidget.__init__(self, parent) @@ -216,26 +226,25 @@ def __init__(self, filter, parent): self, self.updateSoon, self.selectedObjects) self._timer.stop() - def setObject(self, object): + def setObject(self, matcher_object): """Sets the Matcher object to monitor - @type object: Matcher - @param object: The Matcher object to monitor""" - self.__filter = object + @type matcher_object: Matcher + @param matcher_object: The Matcher object to monitor""" + self.__filter = matcher_object self.sortByColumn(2, QtCore.Qt.AscendingOrder) self._update() - def _createItem(self, object): - """Creates and returns the proper item""" - item = MatcherWidgetItem(object, self) - + def _createItem(self, matcher_object): + """Creates and returns a widget item for the given matcher.""" + item = MatcherWidgetItem(matcher_object, self) return item def _getUpdate(self): - """Returns the proper data from the cuebot""" + """Returns the selected filter's matchers.""" try: if self.__filter: return self.__filter.getMatchers() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] @@ -262,24 +271,28 @@ def createMatcher(self): if not choice: return - (input, choice) = QtWidgets.QInputDialog.getText(self, "Create Matcher", - "Please enter the string to match", - QtWidgets.QLineEdit.Normal, "") + (matchQuery, choice) = QtWidgets.QInputDialog.getText( + self, + "Create Matcher", + "Please enter the string to match", + QtWidgets.QLineEdit.Normal, + "") if not choice: return self.addObject(self.__filter.createMatcher( opencue.compiled_proto.filter_pb2.MatchSubject.Value(str(matchSubject)), opencue.compiled_proto.filter_pb2.MatchType.Value(str(matchType)), - str(input))) + str(matchQuery))) def deleteAllMatchers(self): - """Prompts the user and then deletes all matchers""" + """Deletes all matchers.""" if self.__filter: - result = QtWidgets.QMessageBox.question(self, - "Delete All Matchers?", - "Are you sure you want to delete all matchers?", - QtWidgets.QMessageBox.Yes|QtWidgets.QMessageBox.No) + result = QtWidgets.QMessageBox.question( + self, + "Delete All Matchers?", + "Are you sure you want to delete all matchers?", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if result == QtWidgets.QMessageBox.Yes: self._itemsLock.lockForWrite() try: @@ -341,20 +354,29 @@ def __bulkAddMatchers(self, title, deleteExisting): self._update() - def __parseShotList(self, text): + @staticmethod + def __parseShotList(text): return [line.split()[0].strip().lower() for line in str(text).splitlines() if line.split()] + def tick(self): + pass + class ActionMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): - def __init__(self, show, filter, parent): + """Tree for displaying a list of actions.""" + + def __init__(self, show, parent_filter, parent): self.startColumnsForType(cuegui.Constants.TYPE_ACTION) - self.addColumn("Action Type", 210, id=1, - data=lambda action:(opencue.compiled_proto.filter_pb2.ActionType.Name(action.type()))) + self.addColumn( + "Action Type", + 210, + id=1, + data=lambda action: (opencue.compiled_proto.filter_pb2.ActionType.Name(action.type()))) self.addColumn("", 180, id=2) self.addColumn("", 20, id=3) self.__show = show - self.__filter = filter + self.__filter = parent_filter cuegui.AbstractTreeWidget.AbstractTreeWidget.__init__(self, parent) @@ -369,28 +391,28 @@ def __init__(self, show, filter, parent): self, self.updateSoon, self.selectedObjects) self._timer.stop() - def setObject(self, object): + def setObject(self, action_object): """Sets the Action object to monitor - @type object: Action - @param object: The Action object to monitor""" - self.__filter = object + @type action_object: Action + @param action_object: The Action object to monitor""" + self.__filter = action_object self._update() - def _createItem(self, object): - """Creates and returns the proper item""" - return ActionWidgetItem(object, self) + def _createItem(self, action_object): + """Creates and returns the item associated with the given object.""" + return ActionWidgetItem(action_object, self) def _getUpdate(self): """Returns the proper data from the cuebot""" try: if self.__filter: return self.__filter.getActions() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] def contextMenuEvent(self, e): - """When right clicking on an item, this raises a context menu""" + """When right clicking on an item, this raises a context menu.""" menu = QtWidgets.QMenu() menu.addSeparator() @@ -399,10 +421,15 @@ def contextMenuEvent(self, e): menu.exec_(e.globalPos()) def createAction(self): - """Prompts the user to create a new action""" + """Prompts the user to create a new action.""" if self.__filter: (actionType, choice) = QtWidgets.QInputDialog.getItem( - self, "Create Action", "Please select the type of action to add:", ACTIONTYPE, 0, False) + self, + "Create Action", + "Please select the type of action to add:", + ACTIONTYPE, + 0, + False) if choice: value = None actionType = getattr(opencue.api.filter_pb2, str(actionType).replace(" ", "")) @@ -511,32 +538,39 @@ def deleteAllActions(self): self._itemsLock.unlock() self.removeAllItems() -################################################################################ + def tick(self): + pass + class FilterWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single filter.""" + + def __init__(self, filter_object, parent): self.__widgets = {} cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_FILTER, object, parent) + self, cuegui.Constants.TYPE_FILTER, filter_object, parent) self.updateWidgets() - def update(self, object = None, parent = None): + def update(self, rpcObject=None, parent=None): """Adds a call to updateWidgets()""" - cuegui.AbstractWidgetItem.AbstractWidgetItem.update(self, object, parent) + cuegui.AbstractWidgetItem.AbstractWidgetItem.update(self, rpcObject, parent) self.updateWidgets() - def setType(self, filterType): - self.rpcObject.setType(filterType) + def setType(self, filter_type): + """Sets the filter's type.""" + self.rpcObject.setType(filter_type) def setEnabled(self, value): + """Enables or disables the filter.""" self.rpcObject.setEnabled(bool(value)) def delete(self): - result = QtWidgets.QMessageBox.question(self.treeWidget(), - "Delete Filter?", - "Are you sure you want to delete this filter?\n\n%s" % - self.rpcObject.name(), - QtWidgets.QMessageBox.Yes|QtWidgets.QMessageBox.No) + """Deletes the filter.""" + result = QtWidgets.QMessageBox.question( + self.treeWidget(), + "Delete Filter?", + "Are you sure you want to delete this filter?\n\n%s" % self.rpcObject.name(), + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if result == QtWidgets.QMessageBox.Yes: self.rpcObject.delete() QtCore.QTimer.singleShot(0, self.__delete) @@ -545,6 +579,7 @@ def __delete(self): self.treeWidget().removeItem(self) def updateWidgets(self): + """Refreshes the displayed information.""" if not self.__widgets: combo = QtWidgets.QCheckBox(self.parent()) combo.setFocusPolicy(QtCore.Qt.NoFocus) @@ -565,35 +600,43 @@ def updateWidgets(self): state = QtCore.Qt.Unchecked self.__widgets["enabled"].setCheckState(state) + class MatcherWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single matcher.""" + + def __init__(self, rpcObject, parent): self.__widgets = {} cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_MATCHER, object, parent) + self, cuegui.Constants.TYPE_MATCHER, rpcObject, parent) self.updateWidgets() - def update(self, object = None, parent = None): - """Adds a call to updateWidgets()""" - cuegui.AbstractWidgetItem.AbstractWidgetItem.update(self, object, parent) + def update(self, rpcObject=None, parent=None): + """Refreshes the widget display.""" + cuegui.AbstractWidgetItem.AbstractWidgetItem.update(self, rpcObject, parent) self.updateWidgets() def setType(self, matcherType): + """Sets the matcher type.""" self.rpcObject.setType(matcherType) def setSubject(self, matcherSubject): + """Sets the matcher subject.""" self.rpcObject.setSubject(matcherSubject) def setInput(self): + """Sets the matcher input.""" text = str(self.__widgets["input"].text()) if self.rpcObject.input() != text: self.rpcObject.setInput(text) - def delete(self, checked = False): - result = QtWidgets.QMessageBox.question(self.treeWidget(), - "Delete Matcher?", - "Are you sure you want to delete this matcher?\n\n%s" % - self.rpcObject.name(), - QtWidgets.QMessageBox.Yes|QtWidgets.QMessageBox.No) + def delete(self, checked=False): + """Deletes the matcher.""" + del checked + result = QtWidgets.QMessageBox.question( + self.treeWidget(), + "Delete Matcher?", + "Are you sure you want to delete this matcher?\n\n%s" % self.rpcObject.name(), + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if result == QtWidgets.QMessageBox.Yes: self.rpcObject.delete() QtCore.QTimer.singleShot(0, self.__delete) @@ -602,6 +645,7 @@ def __delete(self): self.treeWidget().removeItem(self) def updateWidgets(self): + """Refreshes the widget display.""" if not self.__widgets: parent = self.parent() treeWidget = self.treeWidget() @@ -635,24 +679,29 @@ def updateWidgets(self): not self.__widgets["input"].isModified(): self.__widgets["input"].setText(self.rpcObject.input()) + class ActionWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single action.""" + + def __init__(self, action_object, parent): self.__widgets = {} cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_ACTION, object, parent) + self, cuegui.Constants.TYPE_ACTION, action_object, parent) self.updateWidgets() - def update(self, object = None, parent = None): - """Adds a call to updateWidgets()""" - cuegui.AbstractWidgetItem.AbstractWidgetItem.update(self, object, parent) + def update(self, rpcObject=None, parent=None): + """Updates the displayed content.""" + cuegui.AbstractWidgetItem.AbstractWidgetItem.update(self, rpcObject, parent) self.updateWidgets() - def delete(self, checked = False): - result = QtWidgets.QMessageBox.question(self.treeWidget(), - "Delete Action?", - "Are you sure you want to delete this action?\n\n%s" % - self.rpcObject.name(), - QtWidgets.QMessageBox.Yes|QtWidgets.QMessageBox.No) + def delete(self, checked=False): + """Deletes an action.""" + del checked + result = QtWidgets.QMessageBox.question( + self.treeWidget(), + "Delete Action?", + "Are you sure you want to delete this action?\n\n%s" % self.rpcObject.name(), + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if result == QtWidgets.QMessageBox.Yes: self.rpcObject.delete() QtCore.QTimer.singleShot(0, self.__delete) @@ -660,8 +709,8 @@ def delete(self, checked = False): def __delete(self): self.treeWidget().removeItem(self) - def __setValue(self, value = None): - """Sets the value from the widget""" + def __setValue(self, value=None): + """Sets the action value.""" widget = self.__widgets["ActionValue"] # Get the proper value from the widget @@ -698,6 +747,7 @@ def __setValue(self, value = None): self.rpcObject.setTypeAndValue(self.rpcObject.type(), value) def updateWidgets(self): + """Updates the action display.""" if not self.__widgets: widget = None diff --git a/cuegui/cuegui/FrameMonitor.py b/cuegui/cuegui/FrameMonitor.py index 09b42c114..fc83fe1a4 100644 --- a/cuegui/cuegui/FrameMonitor.py +++ b/cuegui/cuegui/FrameMonitor.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Widget for displaying a list of frames with controls at the top.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -28,6 +31,7 @@ from opencue.compiled_proto import job_pb2 import cuegui.FrameMonitorTree +import cuegui.FrameRangeSelection import cuegui.Logger @@ -35,7 +39,7 @@ class FrameMonitor(QtWidgets.QWidget): - """This contains the frame list table with controls at the top""" + """Widget for displaying a list of frames with controls at the top.""" handle_filter_layers_byLayer = QtCore.Signal(list) @@ -60,7 +64,6 @@ def __init__(self, parent): self._selectStatusSetup(hlayout) # Menu to select frames by status self._filterLayersSetup(hlayout) # Menu to filter layers self._filterStatusSetup(hlayout) # Menu to filter frames by status - #For the filter range setup: self._filterRangeSetup(hlayout) hlayout.addStretch() hlayout.addWidget(QtWidgets.QLabel("(Limited to 1000 frames)")) hlayout.addStretch() @@ -72,41 +75,50 @@ def __init__(self, parent): self._frameRangeSelectionFilterSetup(self.layout()) def updateRequest(self): + """Requests an update of the frame list.""" self.frameMonitorTree.updateRequest() def updateChangedRequest(self): + """Updates the frame list if sufficient time has passed since last updated.""" self.frameMonitorTree.updateChangedRequest() def setJob(self, job): + """Sets the current job.""" self.frameMonitorTree.setJob(job) def getColumnWidths(self): + """Gets the table column widths.""" return self.frameMonitorTree.getColumnWidths() def setColumnWidths(self, widths): + """Sets the table column widths.""" self.frameMonitorTree.setColumnWidths(widths) def getColumnVisibility(self): + """Gets the table column visibility.""" return self.frameMonitorTree.getColumnVisibility() def setColumnVisibility(self, settings): + """Sets the table column visibility.""" self.frameMonitorTree.setColumnVisibility(settings) def getColumnOrder(self): + """Gets the table column order.""" return self.frameMonitorTree.getColumnOrder() def setColumnOrder(self, settings): + """Sets the table column order.""" self.frameMonitorTree.setColumnOrder(settings) def filterLayersFromDoubleClick(self, layerNames): + """Event handler for filtering layers.""" self._filterLayersHandleByLayer(layerNames) -# ============================================================================== -# Frame range bar to filter by frame range -# ============================================================================== + # ============================================================================== + # Frame range bar to filter by frame range + # ============================================================================== def _frameRangeSelectionFilterSetup(self, layout): - from .FrameRangeSelection import FrameRangeSelectionWidget - widget = FrameRangeSelectionWidget(self) + widget = cuegui.FrameRangeSelection.FrameRangeSelectionWidget(self) layout.addWidget(widget) widget.selectionChanged.connect(self._frameRangeSelectionFilterHandle) self.frameRangeSelection = widget @@ -146,33 +158,9 @@ def _frameRangeSelectionFilterHandle(self, start, end): self.frameMonitorTree.frameSearch.options['range'] = "%s-%s" % (start, end) self.frameMonitorTree.updateRequest() -# ============================================================================== -# Widgets to filter by frame range -# ============================================================================== - def _filterRangeSetup(self, layout): - btn = QtWidgets.QSpinBox(self) - btn.setValue(1) - layout.addWidget(btn) - self.filter_range_start_box = btn - - btn = QtWidgets.QSpinBox(self) - btn.setValue(1000) - layout.addWidget(btn) - self.filter_range_end_box = btn - - btn = QtWidgets.QPushButton("Set Frame Range") - btn.setFocusPolicy(QtCore.Qt.NoFocus) - layout.addWidget(btn) - self.filter_range_btn = btn - btn.clicked.connect(self._filterRangeHandle) - - def _filterRangeHandle(self): - value = "%s-%s" % (self.filter_range_start_box.value(), self.filter_range_end_box.value()) - self.frameMonitorTree.frameSearch.setOptions(range=value) - -# ============================================================================== -# Button to refresh -# ============================================================================== + # ============================================================================== + # Button to refresh + # ============================================================================== def _refreshButtonSetup(self, layout): """Sets up the refresh button, adds it to the given layout @param layout: The layout to add the button to @@ -192,9 +180,9 @@ def _refreshButtonDisableHandle(self): self.btn_refresh.setEnabled(False) QtCore.QTimer.singleShot(5000, self._refreshButtonEnableHandle) -# ============================================================================== -# Button to clear all filters -# ============================================================================== + # ============================================================================== + # Button to clear all filters + # ============================================================================== def _clearButtonSetup(self, layout): """Sets up the clear button, adds it to the given layout @param layout: The layout to add the button to @@ -212,9 +200,9 @@ def _clearButtonHandle(self): self._frameRangeSelectionFilterUpdate() self.frameMonitorTree.clearFilters() -# ============================================================================== -# Widgets to Load previous/next page -# ============================================================================== + # ============================================================================== + # Widgets to Load previous/next page + # ============================================================================== def _pageButtonSetup(self, layout): '''Sets up the page flipping buttons and the page # label @param layout: The layout to add the buttons & label to @@ -294,9 +282,9 @@ def _updatePageButtonState(self): self.page_label.setText('{0}' .format(page_label_text)) -# ============================================================================== -# Menu to select frames by status -# ============================================================================== + # ============================================================================== + # Menu to select frames by status + # ============================================================================== def _selectStatusSetup(self, layout): """Sets up the select status menu, adds it to the given layout @param layout: The layout to add the menu to @@ -328,9 +316,9 @@ def _selectStatusHandle(self, action): else: self.frameMonitorTree.selectByStatus(action.text()) -# ============================================================================== -# Menu to filter frames by layers -# ============================================================================== + # ============================================================================== + # Menu to filter frames by layers + # ============================================================================== def _filterLayersSetup(self, layout): """Sets up the filter layers menu, adds it to the given layout @param layout: The layout to add the menu to @@ -423,9 +411,9 @@ def _filterLayersHandleByLayer(self, layer_list): self.frameMonitorTree.updateRequest() self._updatePageButtonState() -# ============================================================================== -# Menu to filter frames by status -# ============================================================================== + # ============================================================================== + # Menu to filter frames by status + # ============================================================================== def _filterStatusSetup(self, layout): """Sets up the filter status menu, adds it to the given layout @param layout: The layout to add the menu to @@ -496,9 +484,9 @@ def _filterStatusHandle(self, action): self._updatePageButtonState() self.frameMonitorTree.updateRequest() -# ============================================================================== -# QLabel that displays the job name -# ============================================================================== + # ============================================================================== + # QLabel that displays the job name + # ============================================================================== def _displayJobNameSetup(self, layout): """Sets up the displaying the name of the currently job. @param layout: The layout to add the label to @@ -512,6 +500,7 @@ def _displayJobNameSetup(self, layout): def _displayJobNameUpdate(self): """Updates the display job name label with the name of the current job.""" if self.frameMonitorTree.getJob(): - self._displayJobNameLabel.setText(" %s " % self.frameMonitorTree.getJob().data.name) + self._displayJobNameLabel.setText( + " %s " % self.frameMonitorTree.getJob().data.name) else: self._displayJobNameLabel.clear() diff --git a/cuegui/cuegui/FrameMonitorTree.py b/cuegui/cuegui/FrameMonitorTree.py index 9d5c35857..33ccccad9 100644 --- a/cuegui/cuegui/FrameMonitorTree.py +++ b/cuegui/cuegui/FrameMonitorTree.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A frame list based on AbstractTreeWidget -""" +"""Tree widget for displaying a list of frames.""" from __future__ import absolute_import @@ -45,6 +43,7 @@ import cuegui.Logger import cuegui.MenuActions import cuegui.Style +import cuegui.ThreadPool import cuegui.Utils @@ -64,6 +63,7 @@ class FrameMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a list of frames.""" job_changed = QtCore.Signal() handle_filter_layers_byLayer = QtCore.Signal(list) @@ -97,7 +97,7 @@ def __init__(self, parent): "Depend: \t The frame depends on another frame or job.\n" "Dead: \t The frame failed with an error.") self.addColumn("Cores", 55, id=5, - data=lambda job, frame: (self.getCores(frame, True) or ""), + data=lambda job, frame: (self.getCores(frame, format_as_string=True) or ""), sort=lambda job, frame: (self.getCores(frame)), tip="The number of cores a frame is using") self.addColumn("Host", 120, id=6, @@ -151,9 +151,10 @@ def __init__(self, parent): "frame for most types of jobs") self.addColumn("Memory", 60, id=12, - data=lambda job, frame: (frame.data.state == opencue.api.job_pb2.RUNNING and - cuegui.Utils.memoryToString(frame.data.used_memory) or - cuegui.Utils.memoryToString(frame.data.max_rss)), + data=lambda job, frame: ( + frame.data.state == opencue.api.job_pb2.RUNNING and + cuegui.Utils.memoryToString(frame.data.used_memory) or + cuegui.Utils.memoryToString(frame.data.max_rss)), sort=lambda job, frame: (frame.data.state == opencue.api.job_pb2.RUNNING and frame.data.used_memory or frame.data.max_rss), tip="If a frame is running:\n" @@ -195,13 +196,14 @@ def __init__(self, parent): cuegui.AbstractTreeWidget.AbstractTreeWidget.__init__(self, parent) self.__sortByColumnCache = {} + self.ticksWithoutUpdate = 999 + self.__lastUpdateTime = None self.itemClicked.connect(self.__itemSingleClickedCopy) self.itemClicked.connect(self.__itemSingleClickedViewLog) self.itemDoubleClicked.connect(self.__itemDoubleClickedViewLog) self.header().sortIndicatorChanged.connect(self.__sortByColumnSave) - self.__load = None self.startTicksUpdate(20) @@ -219,7 +221,7 @@ def tick(self): self.ticksWithoutUpdate = 0 self._update() return - elif self.ticksWithoutUpdate > self.updateInterval: + if self.ticksWithoutUpdate > self.updateInterval: logger.info("doing changed update") self.ticksWithoutUpdate = 0 self._updateChanged() @@ -233,19 +235,23 @@ def tick(self): if not self.ticksWithoutUpdate % 2: self.redraw() - def getCores(self, frame, format=False): + @staticmethod + def getCores(frame, format_as_string=False): + """Gets the number of cores a frame is using.""" cores = None - m = re.search(".*\/(\d+\.?\d*)", frame.data.last_resource) + m = re.search(r".*\/(\d+\.?\d*)", frame.data.last_resource) if m: cores = float(m.group(1)) - if format: + if format_as_string: cores = "{:.2f}".format(cores) return cores - def getTimeString(self, timestamp): + @staticmethod + def getTimeString(timestamp): + """Gets a timestamp formatted as a string.""" tstring = None if timestamp and timestamp > 0: tstring = datetime.datetime.fromtimestamp(timestamp).strftime("%m/%d %H:%M") @@ -254,6 +260,7 @@ def getTimeString(self, timestamp): def redrawRunning(self): """Forces the running frames to be redrawn with current values""" + # pylint: disable=broad-except try: items = self.findItems("Running", QtCore.Qt.MatchExactly, @@ -276,7 +283,7 @@ def __sortByColumnSave(self, logicalIndex, order): def __sortByColumnLoad(self): """Loads the last used sort column and order for the current job, or uses default ascending dispatch order""" - key = self.__job and cuegui.Utils.getObjectKey(self.__job) or None + key = cuegui.Utils.getObjectKey(self.__job) if self.__job else None settings = self.__sortByColumnCache.get(key, (0, QtCore.Qt.AscendingOrder)) self.sortByColumn(settings[0], settings[1]) @@ -287,7 +294,10 @@ def __itemSingleClickedCopy(self, item, col): @param item: The item single clicked on @type col: int @param col: Column number single clicked on""" - selected = [frame.data.name for frame in self.selectedObjects() if cuegui.Utils.isFrame(frame)] + del item + del col + selected = [ + frame.data.name for frame in self.selectedObjects() if cuegui.Utils.isFrame(frame)] if selected: QtWidgets.QApplication.clipboard().setText(" ".join(selected)) @@ -297,6 +307,7 @@ def __itemSingleClickedViewLog(self, item, col): @param item: The item single clicked on @type col: int @param col: Column number single clicked on""" + del col current_log_file = cuegui.Utils.getFrameLogFile(self.__job, item.rpcObject) try: old_log_files = sorted(glob.glob('%s.*' % current_log_file), @@ -304,7 +315,9 @@ def __itemSingleClickedViewLog(self, item, col): reverse=True) except ValueError: pass + # pylint: disable=no-member QtGui.qApp.display_log_file_content.emit([current_log_file] + old_log_files) + # pylint: enable=no-member def __itemDoubleClickedViewLog(self, item, col): """Called when a frame is double clicked, views the frame log in a popup @@ -312,13 +325,16 @@ def __itemDoubleClickedViewLog(self, item, col): @param item: The item double clicked on @type col: int @param col: Column number double clicked on""" + del col frame = item.rpcObject if frame.data.state == opencue.api.job_pb2.RUNNING: cuegui.Utils.popupFrameTail(self.__job, frame) else: cuegui.Utils.popupFrameView(self.__job, frame) + # pylint: disable=inconsistent-return-statements def setJob(self, job): + """Sets the current job.""" if job is None: return self.__setJob(None) job = cuegui.Utils.findJob(job) @@ -344,13 +360,15 @@ def getJob(self): return self.__job def clearFilters(self): + """Clears any user-defined filters or sorting, restores default state.""" self.clearSelection() self.frameSearch = opencue.search.FrameSearch() self.sortByColumn(0, QtCore.Qt.AscendingOrder) self.updateRequest() def selectByStatus(self, status): - """Selects all frames that match the given status + """Selects all frames that match the given status. + @type status: string @param status: A frame status to match""" items = self.findItems(str(status), @@ -365,18 +383,18 @@ def selectByStatus(self, status): # Scroll to the first item self.scrollToItem(items[0], QtWidgets.QAbstractItemView.PositionAtTop) - def _createItem(self, object): + def _createItem(self, rpcObject): """Creates and returns the proper item""" - return FrameWidgetItem(object, self, self.__job) + return FrameWidgetItem(rpcObject, self, self.__job) -# -# updateRequest -> _update -> _getUpdate -> _processUpdate -# updateChangedRequest -> _updateChanged -> _getUpdateChanged -> _processUpdateChanged -# -# autoUpdate -> updateRequest -# updateAll -> updateRequest -# _updateAll -> _update -# + # + # updateRequest -> _update -> _getUpdate -> _processUpdate + # updateChangedRequest -> _updateChanged -> _getUpdateChanged -> _processUpdateChanged + # + # autoUpdate -> updateRequest + # updateAll -> updateRequest + # _updateAll -> _update + # def updateRequest(self): """Updates the items in the TreeWidget if sufficient time has passed @@ -394,7 +412,10 @@ def _update(self): logger.info("_update") self._lastUpdate = time.time() if hasattr(QtGui.qApp, "threadpool"): - QtGui.qApp.threadpool.queue(self._getUpdate, self._processUpdate, "getting data for %s" % self.__class__) + # pylint: disable=no-member + QtGui.qApp.threadpool.queue( + self._getUpdate, self._processUpdate, "getting data for %s" % self.__class__) + # pylint: enable=no-member else: logger.warning("threadpool not found, doing work in gui thread") self._processUpdate(None, self._getUpdate()) @@ -405,7 +426,11 @@ def _updateChanged(self): logger.info("_updateChanged") self._lastUpdate = time.time() if hasattr(QtGui.qApp, "threadpool"): - QtGui.qApp.threadpool.queue(self._getUpdateChanged, self._processUpdateChanged, "getting data for %s" % self.__class__) + # pylint: disable=no-member + QtGui.qApp.threadpool.queue( + self._getUpdateChanged, self._processUpdateChanged, + "getting data for %s" % self.__class__) + # pylint: enable=no-member else: logger.warning("threadpool not found, doing work in gui thread") self._processUpdateChanged(None, self._getUpdateChanged()) @@ -418,7 +443,7 @@ def _getUpdate(self): self.__lastUpdateTime = int(time.time()) return self.__job.getFrames(**self.frameSearch.options) return [] - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def _getUpdateChanged(self): @@ -430,7 +455,7 @@ def _getUpdateChanged(self): (self.__jobState and self.__jobState == opencue.api.job_pb2.FINISHED): logger.debug("no job or job is finished, bailing") return [] - logger.info(" + Nth update = %s" % self.__class__) + logger.info(" + Nth update = %s", self.__class__) updatedFrames = [] try: updated_data = self.__job.getUpdatedFrames(self.__lastUpdateTime) @@ -438,16 +463,17 @@ def _getUpdateChanged(self): self.__jobState = updated_data.state updatedFrames = updated_data.updated_frames.updated_frames - except opencue.EntityNotFoundException as e: + except opencue.EntityNotFoundException: self.setJobObj(None) - except Exception as e: - if hasattr(e, "message") and e.message.find("timestamp cannot be over a minute off") != -1: - logger.warning("Forcing a full update due to: %s" % e.message) + except opencue.exception.CueException as e: + # pylint: disable=no-member + if hasattr(e, "message") and 'timestamp cannot be over a minute off' in e.message: + logger.warning("Forcing a full update due to: %s", e.message) return None - else: - list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) + # pylint: enable=no-member + list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) - logger.info(" - %s" % self.__class__) + logger.info(" - %s", self.__class__) return updatedFrames def _processUpdate(self, work, rpcObjects): @@ -465,10 +491,11 @@ def _processUpdate(self, work, rpcObjects): self._items = {} if rpcObjects: for rpcObject in rpcObjects: - self._items[cuegui.Utils.getObjectKey(rpcObject)] = self._createItem(rpcObject) + self._items[cuegui.Utils.getObjectKey(rpcObject)] = \ + self._createItem(rpcObject) finally: self._itemsLock.unlock() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def _processUpdateChanged(self, work, rpcObjects): @@ -478,6 +505,7 @@ def _processUpdateChanged(self, work, rpcObjects): @type work: @param rpcObjects: A list of rpcObjects @type rpcObjects: list """ + del work logger.info("_processUpdateChanged") try: if rpcObjects is None: @@ -496,7 +524,7 @@ def _processUpdateChanged(self, work, rpcObjects): logger.info("_processUpdateChanged calling redraw") self.redraw() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def _updateFrame(self, updatedFrame): @@ -524,11 +552,17 @@ def _actionFilterSelectedLayers(self): class FrameWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): + """Widget item representing a single frame.""" + __initialized = False - def __init__(self, object, parent, job): + + # pylint: disable=protected-access + def __init__(self, rpcObject, parent, job): if not self.__initialized: self.__class__.__initialized = True + # pylint: disable=no-member self.__class__.__backgroundColor = QtGui.qApp.palette().color(QtGui.QPalette.Base) + # pylint: enable=no-member self.__class__.__foregroundColor = cuegui.Style.ColorTheme.COLOR_JOB_FOREGROUND self.__class__.__foregroundColorBlack = QCOLOR_BLACK self.__class__.__foregroundColorGreen = QCOLOR_GREEN @@ -539,7 +573,7 @@ def __init__(self, object, parent, job): self.__class__.__rgbFrameState[key] = cuegui.Constants.RGB_FRAME_STATE[key] self.__class__.__type = cuegui.Constants.TYPE_FRAME cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_FRAME, object, parent, job) + self, cuegui.Constants.TYPE_FRAME, rpcObject, parent, job) self.__show = job.data.show def data(self, col, role): @@ -554,18 +588,17 @@ def data(self, col, role): return self.column_info[col][cuegui.Constants.COLUMN_INFO_DISPLAY]( self._source, self.rpcObject) - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: if col == STATUS_COLUMN: return self.__foregroundColorBlack - elif col == PROC_COLUMN and self.rpcObject.data.last_resource.startswith(LOCALRESOURCE): + if col == PROC_COLUMN and self.rpcObject.data.last_resource.startswith(LOCALRESOURCE): return self.__foregroundColorGreen - else: - return self.__foregroundColor + return self.__foregroundColor - elif role == QtCore.Qt.BackgroundRole and col == STATUS_COLUMN: + if role == QtCore.Qt.BackgroundRole and col == STATUS_COLUMN: return self.__rgbFrameState[self.rpcObject.data.state] - elif role == QtCore.Qt.DecorationRole and col == CHECKPOINT_COLUMN: + if role == QtCore.Qt.DecorationRole and col == CHECKPOINT_COLUMN: if self.rpcObject.data.checkpoint_state == opencue.api.job_pb2.ENABLED: return QtGui.QIcon(":markdone.png") elif role == QtCore.Qt.TextAlignmentRole: @@ -582,7 +615,8 @@ def data(self, col, role): def __lt__(self, other): """Custom sorting for columns that have a function defined for sorting""" - sortLambda = self.column_info[self.treeWidget().sortColumn()][cuegui.AbstractWidgetItem.SORT_LAMBDA] + sortLambda = self.column_info[ + self.treeWidget().sortColumn()][cuegui.AbstractWidgetItem.SORT_LAMBDA] if sortLambda: return sortLambda(self._source, self.rpcObject) < sortLambda( other._source, other.rpcObject) @@ -607,9 +641,7 @@ class FrameLogDataBuffer(object): # default to 0% if nothing found unless already a previous value def __init__(self): - from .ThreadPool import ThreadPool - - self.__threadPool = ThreadPool(self.maxThreads, self.maxQueue) + self.__threadPool = cuegui.ThreadPool.ThreadPool(self.maxThreads, self.maxQueue) self.__currentJob = None self.__cache = {} self.__queue = {} @@ -626,6 +658,7 @@ def __init__(self): def getLastLineData(self, job, frame): """Returns the last line and LLU of the log file or queues a request to update it""" + # pylint: disable=broad-except try: __now = time.time() jobKey = cuegui.Utils.getObjectKey(job) @@ -635,6 +668,7 @@ def getLastLineData(self, job, frame): self.__queue.clear() self.__currentJob = jobKey + # pylint: disable=protected-access if len(self.__queue) > len(self.__threadPool._q_queue): # Everything is hung up, start over self.__cache.clear() @@ -651,25 +685,27 @@ def getLastLineData(self, job, frame): self.__threadPool.queue(self.__doWork, self.__saveWork, "getting data for %s" % self.__class__) # Return the cached results anyway - return (__cached[self.__LINE], __cached[self.__LLU]) - else: - __path = cuegui.Utils.getFrameLogFile(job, frame) - # Cache a blank entry until it is filled in - self.__cache[frameKey] = [__now + 60, - __path, - self.__defaultLine, - self.__defaultLLU] - # Queue an update - self.__queue[frameKey] = __path - self.__threadPool.queue(self.__doWork, self.__saveWork, - "getting data for %s" % self.__class__) - # Since nothing is updated yet, return an empty string - return (self.__defaultLine, self.__defaultLLU) + return __cached[self.__LINE], __cached[self.__LLU] + + __path = cuegui.Utils.getFrameLogFile(job, frame) + # Cache a blank entry until it is filled in + self.__cache[frameKey] = [__now + 60, + __path, + self.__defaultLine, + self.__defaultLLU] + # Queue an update + self.__queue[frameKey] = __path + self.__threadPool.queue(self.__doWork, self.__saveWork, + "getting data for %s" % self.__class__) + # Since nothing is updated yet, return an empty string + return self.__defaultLine, self.__defaultLLU except Exception as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) + # pylint: disable=inconsistent-return-statements def __doWork(self): """Pops work from the queue and returns the proxy and last log line""" + # pylint: disable=broad-except try: if self.__queue: (proxy, path) = self.__queue.popitem() @@ -677,13 +713,14 @@ def __doWork(self): return (proxy, cuegui.Utils.getLastLine(path), cuegui.Utils.secondsToHHMMSS(time.time() - os.stat(path).st_mtime)) - else: - return None + return None except Exception as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def __saveWork(self, work, results): """Stores the resulting last log line to the cache with the proxy key""" + del work + # pylint: disable=broad-except try: if results: __cached = self.__cache[results[0]] @@ -699,15 +736,13 @@ def __saveWork(self, work, results): class FrameEtaDataBuffer(object): """A cached and threaded interface to reading the last log line""" + maxCacheTime = 60 maxThreads = 2 maxQueue = 501 - def __init__(self): - from .ThreadPool import ThreadPool - - self.__threadPool = ThreadPool(self.maxThreads, self.maxQueue) + self.__threadPool = cuegui.ThreadPool.ThreadPool(self.maxThreads, self.maxQueue) self.__currentJob = None self.__cache = {} @@ -717,13 +752,16 @@ def __init__(self): self.__ETA = 1 def getEtaFormatted(self, job, frame): + """Gets frame ETA formatted as a string.""" result = self.getEta(job, frame) if result: return cuegui.Utils.secondsToHHMMSS(result) return False def getEta(self, job, frame): + """Gets frame ETA as a number of seconds.""" __now = time.time() + # pylint: disable=broad-except try: jobKey = cuegui.Utils.getObjectKey(job) if self.__currentJob != jobKey: @@ -738,16 +776,18 @@ def getEta(self, job, frame): if __cached[self.__TIME] < __now - self.maxCacheTime: # It is an old cache, queue an update, reset the time until updated self.__cache[frameKey][0] = __now + 60 - self.__threadPool.queue(self.__doWork, self.__saveWork, - "getting data for %s" % self.__class__, frameKey, job, frame) + self.__threadPool.queue( + self.__doWork, self.__saveWork, "getting data for %s" % self.__class__, + frameKey, job, frame) # Return the cached results anyway if __cached[self.__ETA] is not None: return max(__cached[self.__ETA] - __now + __cached[self.__TIME], 0) else: # Queue an update, cache a blank entry until updated self.__cache[frameKey] = [__now + 60, None] - self.__threadPool.queue(self.__doWork, self.__saveWork, - "getting data for %s" % self.__class__, frameKey, job, frame) + self.__threadPool.queue( + self.__doWork, self.__saveWork, "getting data for %s" % self.__class__, + frameKey, job, frame) # Since nothing is updated yet, return a default except Exception as e: self.__cache[frameKey] = [__now, @@ -758,14 +798,17 @@ def getEta(self, job, frame): def __doWork(self, proxy, job, frame): """Pops work from the queue and returns the proxy and last log line""" + # pylint: disable=broad-except try: - return (proxy, cuegui.eta.ETASeconds(job, frame)) + return proxy, cuegui.eta.ETASeconds(job, frame) except Exception as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) - return (proxy, self.__defaultETA) + return proxy, self.__defaultETA def __saveWork(self, work, results): """Stores the resulting last log line to the cache with the proxy key""" + del work + # pylint: disable=broad-except try: if results: __cached = self.__cache[results[0]] @@ -779,6 +822,8 @@ def __saveWork(self, work, results): class FrameContextMenu(QtWidgets.QMenu): + """Context menu for frames.""" + def __init__(self, widget, filterSelectedLayersCallback): super(FrameContextMenu, self).__init__() @@ -801,8 +846,10 @@ def __init__(self, widget, filterSelectedLayersCallback): self.__menuActions.frames().addAction(self, "useLocalCores") + # pylint: disable=no-member if QtGui.qApp.applicationName() == "CueCommander": self.__menuActions.frames().addAction(self, "viewHost") + # pylint: enable=no-member depend_menu = QtWidgets.QMenu("&Dependencies", self) self.__menuActions.frames().addAction(depend_menu, "viewDepends") @@ -828,4 +875,3 @@ def __init__(self, widget, filterSelectedLayersCallback): self.__menuActions.frames().addAction(self, "eat") self.__menuActions.frames().addAction(self, "kill") self.__menuActions.frames().addAction(self, "eatandmarkdone") - diff --git a/cuegui/cuegui/FrameRangeSelection.py b/cuegui/cuegui/FrameRangeSelection.py index 06da309d1..851e2aab2 100644 --- a/cuegui/cuegui/FrameRangeSelection.py +++ b/cuegui/cuegui/FrameRangeSelection.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -From katana -""" +"""Widget for displaying and selecting within a frame range.""" from __future__ import division @@ -33,20 +31,21 @@ class FrameRangeSelectionWidget(QtWidgets.QWidget): - """The Timeline Widget is a rectangular area with ticks to represent units - of frames. A frame range may be selected. - This widget emits the following signals: - * startFrameChanged(int) - user clicks new time or program changes time - * endFrameChanged(int) - user clicks new time or program changes time - * frameRangeChanged(int,int) - program changes viewed frame range - * selectionChanged(int,int) - user releases a drag that selects a frame range - """ + """Widget for displaying and selecting within a frame range. + + A rectangular area with ticks to represent units of frames. + + This widget emits the following signals: + * startFrameChanged(int) - user clicks new time or program changes time + * endFrameChanged(int) - user clicks new time or program changes time + * frameRangeChanged(int,int) - program changes viewed frame range + * selectionChanged(int,int) - user releases a drag that selects a frame range + """ endFrameChanged = QtCore.Signal(int) startFrameChanged = QtCore.Signal(int) frameRangeChanged = QtCore.Signal(int, int) selectionChanged = QtCore.Signal(int, int) - def __init__(self, parent, *args): QtWidgets.QWidget.__init__(self, parent, *args) self.setMinimumWidth(100) @@ -54,12 +53,11 @@ def __init__(self, parent, *args): self.__layout.addStretch(100) self.__right_margin = 25 - self.__layout.setContentsMargins(0,0,0,0) + self.__layout.setContentsMargins(0, 0, 0, 0) toolButton = QtWidgets.QToolButton() toolButton.setArrowType(QtCore.Qt.UpArrow) toolButton.setFocusPolicy(QtCore.Qt.NoFocus) - toolButton.setFixedSize(20,20) - #toolButton.setContentsMargins(0,0,0,0) + toolButton.setFixedSize(20, 20) self.__layout.addWidget(toolButton) self.setLayout(self.__layout) @@ -93,42 +91,47 @@ def __init__(self, parent, *args): self.default_select_size = 1000 def endFrame(self): + """Returns the current end frame displayed in the timeline.""" return self.__endFrame def setEndTime(self, t, final = True): - if (self.__endFrame == t and self.__endFrameFinal == final): return + """Sets the current end frame displayed in the timeline.""" + if self.__endFrame == t and self.__endFrameFinal == final: + return self.__endFrame = int(t) self.__endFrameFinal = final - #mine self.__selectionRange = (self.__startFrame, self.__endFrame) self.update() self.endFrameChanged.emit(int(t)) - # The current time displayed in the timeline. def startFrame(self): + """Returns the current start frame displayed in the timeline.""" return self.__startFrame def setStartTime(self, t, final = True): + """Sets the current start frame displayed in the timeline.""" if self.__startFrame == t and self.__startFrameFinal == final: return self.__startFrame = int(t) self.__startFrameFinal = final if self.__endFrame == self.__startFrame: - self.setEndTime(min(self.__startFrame + self.default_select_size - 1, self.__frameRange[1]), True) + self.setEndTime( + min(self.__startFrame + self.default_select_size - 1, self.__frameRange[1]), True) self.__selectionRange = (self.__startFrame, self.__endFrame) self.update() self.startFrameChanged.emit(int(t)) - # The viewed range of time in the timeline. def frameRange(self): + """Returns the viewed range of time in the timeline.""" return self.__frameRange def setFrameRange(self, frameRange): + """Sets the viewed frame range in the timeline.""" frameRange = tuple(sorted(map(int, frameRange))) self.__floatTime = None @@ -140,11 +143,12 @@ def setFrameRange(self, frameRange): self.frameRangeChanged.emit(self.__frameRange[0], self.__frameRange[1]) self.update() - # The selected (highlighted) range of time in the timeline. def selectedFrameRange(self): + """Returns the selected (highlighted) range of time in the timeline.""" return self.__selectionRange def setSelectedFrameRange(self, selectedRange): + """Sets the selected (highlighted) range of time in the timeline.""" if selectedRange != self.__selectionRange: self.__selectionRange = selectedRange @@ -186,15 +190,19 @@ def mouseReleaseEvent(self, mouseEvent): self.setEndTime(max(hitTime, self.startFrame()), True) self.setStartTime(min(hitTime, self.startFrame()), True) - self.__selectionRange = (min(self.__selectionRange[0], self.__selectionRange[1]), max(self.__selectionRange[0], self.__selectionRange[1])) + self.__selectionRange = ( + min(self.__selectionRange[0], self.__selectionRange[1]), + max(self.__selectionRange[0], self.__selectionRange[1])) self.setSelectedFrameRange(self.__selectionRange) self.update() def mouseDoubleClickEvent(self, mouseEvent): + del mouseEvent self.__double = True def paintEvent(self, paintEvent): + del paintEvent painter = QtGui.QPainter(self) self.__paintBackground(painter) @@ -209,20 +217,26 @@ def paintEvent(self, paintEvent): self.__paintEndTime(painter) def leaveEvent(self, event): + del event self.__floatTime = None self.update() def __getTickAreaExtent(self): - return QtCore.QRect(10, -self.height() // 2, self.width() - self.__right_margin - 20, self.height()) + return QtCore.QRect( + 10, -self.height() // 2, self.width() - self.__right_margin - 20, self.height()) def __getTickArea(self, time): tickArea = self.__getTickAreaExtent() - tickSpacing = float(self.__getTickAreaExtent().width()) / max(1,(self.__frameRange[1] - self.__frameRange[0])) + tickSpacing = ( + float(self.__getTickAreaExtent().width()) / + max(1, (self.__frameRange[1] - self.__frameRange[0]))) return QtCore.QRect(tickArea.left() + tickSpacing * (time - self.__frameRange[0]), tickArea.top(), tickSpacing, tickArea.height()) def __getTimeFromLocalPoint(self, x): - tickSpacing = float(self.__getTickAreaExtent().width()) / max(1,(self.__frameRange[1] - self.__frameRange[0])) + tickSpacing = ( + float(self.__getTickAreaExtent().width()) / + max(1, (self.__frameRange[1] - self.__frameRange[0]))) deltaX = x - self.__getTickAreaExtent().left() hitTime = int(deltaX / tickSpacing + 0.5) + self.__frameRange[0] hitTime = int(max(self.__frameRange[0], min(hitTime, self.__frameRange[1]))) @@ -230,9 +244,9 @@ def __getTimeFromLocalPoint(self, x): def __getLabelPeriod(self): delta = self.__frameRange[1] - self.__frameRange[0] - if (delta < 20): + if delta < 20: return 2 - if (delta < 10000): + if delta < 10000: base = 5 offset = 2 else: @@ -245,7 +259,12 @@ def __paintBackground(self, painter): bgBrush = self.palette().window() painter.fillRect(0, 0, self.width() - self.__right_margin, self.height(), bgBrush) highlightBrush = QtGui.QBrush(QtGui.QColor(75, 75, 75)) - painter.fillRect(0, self.height() // 2, self.width() - self.__right_margin+5, self.height() // 2, highlightBrush) + painter.fillRect( + 0, + self.height() // 2, + self.width() - self.__right_margin + 5, + self.height() // 2, + highlightBrush) def __paintTickmarks(self, painter): tickExtent = self.__getTickAreaExtent() @@ -267,7 +286,8 @@ def __paintLabels(self, painter): tickExtent = self.__getTickAreaExtent() labelHeight = tickExtent.height() // 3 labelPeriod = self.__getLabelPeriod() - if labelPeriod == 0: return + if labelPeriod == 0: + return firstLabel = self.__frameRange[0] + labelPeriod - 1 firstLabel = firstLabel - (firstLabel % labelPeriod) @@ -332,17 +352,19 @@ def __paintEndTime(self, painter): metric = QtGui.QFontMetrics(painter.font()) frameString = str(int(endFrame)) xPos = timeExtent.left() - metric.width(frameString) // 2 - yPos = metric.ascent() + 1 + yPos = metric.ascent() + 1 painter.drawText(xPos, yPos, frameString) painter.setPen(oldPen) def __paintFloatTime(self, painter): - if self.__floatTime == None: return + if self.__floatTime is None: + return timeExtent = self.__getTickArea(self.__floatTime) oldPen = painter.pen() painter.setPen(QtGui.QColor(90, 90, 90)) - painter.drawLine(timeExtent.left(), timeExtent.top(), timeExtent.left(), timeExtent.bottom()) + painter.drawLine( + timeExtent.left(), timeExtent.top(), timeExtent.left(), timeExtent.bottom()) if self.__selectionRange: painter.setPen(QtGui.QColor(255,255,255)) @@ -351,13 +373,16 @@ def __paintFloatTime(self, painter): metric = QtGui.QFontMetrics(painter.font()) frameString = str(self.__floatTime) xPos = timeExtent.left() - metric.width(frameString) // 2 - yPos = timeExtent.top() + metric.ascent() + yPos = timeExtent.top() + metric.ascent() painter.drawText(xPos, yPos, frameString) painter.setPen(oldPen) def __paintSelection(self, painter): - if self.__selectionRange == None: return - selection = (min(self.__selectionRange[0], self.__selectionRange[1]), max(self.__selectionRange[0], self.__selectionRange[1])) + if self.__selectionRange is None: + return + selection = ( + min(self.__selectionRange[0], self.__selectionRange[1]), + max(self.__selectionRange[0], self.__selectionRange[1])) leftExtent = self.__getTickArea(selection[0]) rightExtent = self.__getTickArea(selection[1] - 1) diff --git a/cuegui/cuegui/GarbageCollector.py b/cuegui/cuegui/GarbageCollector.py index 6b3971244..ec0587dbd 100644 --- a/cuegui/cuegui/GarbageCollector.py +++ b/cuegui/cuegui/GarbageCollector.py @@ -1,15 +1,38 @@ -from PySide2 import QtCore +# Copyright Contributors to the OpenCue Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Custom garbage collector class. + +Disables automatic garbage collection and instead collect manually every INTERVAL milliseconds. + +This is done to ensure that garbage collection only happens in the GUI thread, as otherwise Qt +can crash.""" + + import gc +from PySide2 import QtCore + + class GarbageCollector(QtCore.QObject): + """Custom garbage collector class. - ''' - Disable automatic garbage collection and instead collect manually - every INTERVAL milliseconds. + Disables automatic garbage collection and instead collect manually every INTERVAL milliseconds. - This is done to ensure that garbage collection only happens in the GUI - thread, as otherwise Qt can crash. - ''' + This is done to ensure that garbage collection only happens in the GUI thread, as otherwise Qt + can crash.""" INTERVAL = 5000 @@ -25,7 +48,10 @@ def __init__(self, parent, debug=False): self.timer.start(self.INTERVAL) def check(self): + """Runs the garbage collector. + + This method is run every INTERNAL seconds.""" gc.collect() if self.debug: for obj in gc.garbage: - print (obj, repr(obj), type(obj)) + print(obj, repr(obj), type(obj)) diff --git a/cuegui/cuegui/GraphSubscriptionsWidget.py b/cuegui/cuegui/GraphSubscriptionsWidget.py deleted file mode 100644 index 39aa6f613..000000000 --- a/cuegui/cuegui/GraphSubscriptionsWidget.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright Contributors to the OpenCue Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import - -from builtins import range - -from PySide2 import QtCore -from PySide2 import QtGui -from PySide2 import QtWidgets - -import opencue - - -class GraphSubscriptionsWidget(QtWidgets.QWidget): - def __init__(self, parent): - QtWidgets.QWidget.__init__(self, parent) - - self.__color = QtGui.QColor(55,200,55) - self.__brush = QtGui.QBrush() - self.__brush.setColor(self.__color) - self.__brush.setStyle(QtCore.Qt.SolidPattern) - - self.__show = opencue.api.findShow("clo") - self.__history = [0]*100 - self.__line = 575 - self.__max = max(self.__line * 1.2, 80) - - self.__timer = QtCore.QTimer(self) - self.__timer.timeout.connect(self.addNumber) - - self.__timer.start(10000) - - def addNumber(self): - for sub in self.__show.getSubscriptions(): - if sub.name() == "clo.General": - val = sub.runningCores() / 100 - self.__history.append(val) - self.__max = max(self.__max, val + 80) - - if len(self.__history) > 100: - self.__history.pop(0) - - self.update() - - def paintEvent(self, event): - QtWidgets.QWidget.paintEvent(self, event) - - #Skip this if too small, if splitter is all the way over - - painter = QtGui.QPainter(self) - try: - rect = self.contentsRect().adjusted(5, 5, -5, -5) - painter.save() - - painter.fillRect(rect, - QtGui.qApp.palette().color(QtGui.QPalette.Base)) - - - - - if len(self.__history) > 1: - stepWidth = rect.width() / float(len(self.__history) - 1) - ratioHeight = (rect.height() - 2) / float(self.__max) - - # Box outline - painter.setPen(QtCore.Qt.black) - painter.drawRect(rect) - - painter.setPen(self.__color) - points = QtGui.QPolygon(len(self.__history) + 2) - - # Bottom left - #points.setPoint(0, rect.bottomLeft()) - points.setPoint(0, rect.left() + 1, rect.bottom()) - - # All the data points - num = 1 - for i in range(len(self.__history)): - points.setPoint(num, - max(rect.x() + 1, rect.x() - 1 + stepWidth * i), - rect.bottom() - ratioHeight * self.__history[i]) - num += 1 - - # Bottom right - points.setPoint(num, rect.bottomRight()) - - # Draw filled in - painter.setBrush(self.__brush) - painter.drawPolygon(points) - - # Draw subscription size line - painter.setPen(QtCore.Qt.red) - height = rect.bottom() - ratioHeight * self.__line - painter.drawLine(rect.left() + 1, - height, - rect.right() - 1, - height) - - finally: - painter.restore() - painter.end() - del painter diff --git a/cuegui/cuegui/GroupDialog.py b/cuegui/cuegui/GroupDialog.py index 332e51236..2c59f405c 100644 --- a/cuegui/cuegui/GroupDialog.py +++ b/cuegui/cuegui/GroupDialog.py @@ -14,6 +14,9 @@ # limitations under the License. +"""Dialog for creating or modifying a group.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -27,6 +30,8 @@ class GroupDialog(QtWidgets.QDialog): + """Base class for group dialogs.""" + def __init__(self, parentGroup, modifyGroup, defaults, parent): QtWidgets.QDialog.__init__(self, parent) layout = QtWidgets.QGridLayout(self) @@ -38,7 +43,7 @@ def __init__(self, parentGroup, modifyGroup, defaults, parent): self._departments = opencue.api.getDepartmentNames() try: self._departments = opencue.api.getDepartmentNames() - except Exception as e: + except opencue.exception.CueException: self._departments = ["Unknown"] __title = defaults["title"] @@ -88,7 +93,8 @@ def __init__(self, parentGroup, modifyGroup, defaults, parent): self.__createButtons( QtWidgets.QDialogButtonBox.Save | QtWidgets.QDialogButtonBox.Cancel, 8, 3) - def __createToggleDoubleSpinBox(self, text, row, startEnabled = False, currentValue = 0, minValue = 0): + def __createToggleDoubleSpinBox( + self, text, row, startEnabled = False, currentValue = 0, minValue = 0): inputWidget = QtWidgets.QDoubleSpinBox(self) inputWidget.setEnabled(startEnabled) inputWidget.setRange(minValue, 30000) @@ -96,7 +102,8 @@ def __createToggleDoubleSpinBox(self, text, row, startEnabled = False, currentVa inputWidget.setValue(currentValue) return self.__createToggleInput(text, row, inputWidget, startEnabled) - def __createToggleSpinBox(self, text, row, startEnabled = False, currentValue = 0, minValue = 0): + def __createToggleSpinBox( + self, text, row, startEnabled = False, currentValue = 0, minValue = 0): inputWidget = QtWidgets.QSpinBox(self) inputWidget.setEnabled(startEnabled) inputWidget.setRange(minValue, 30000) @@ -164,8 +171,8 @@ def accept(self): self.close() - def __setValue(self, checkBox, setter, newValue, currentValue, disableValue): - result = None + @staticmethod + def __setValue(checkBox, setter, newValue, currentValue, disableValue): if checkBox.isChecked(): result = newValue else: @@ -173,29 +180,37 @@ def __setValue(self, checkBox, setter, newValue, currentValue, disableValue): if result is not None and result != currentValue: setter(result) + class ModifyGroupDialog(GroupDialog): + """Dialog for modifying an existing group.""" + def __init__(self, modifyGroup, parent=None): modifyGroup = opencue.api.getGroup(modifyGroup.id()) - defaults = {"title": "Modify Group: %s" % modifyGroup.data.name, - "message": "Modifying the group %s" % modifyGroup.data.name, - "name": modifyGroup.data.name, - "department": modifyGroup.data.department, - "defaultJobPriority": modifyGroup.data.default_job_priority, - "defaultJobMinCores": modifyGroup.data.default_job_min_cores, - "defaultJobMaxCores": modifyGroup.data.default_job_max_cores, - "minCores": modifyGroup.data.min_cores, - "maxCores": modifyGroup.data.max_cores} + defaults = { + "title": "Modify Group: %s" % modifyGroup.data.name, + "message": "Modifying the group %s" % modifyGroup.data.name, + "name": modifyGroup.data.name, + "department": modifyGroup.data.department, + "defaultJobPriority": modifyGroup.data.default_job_priority, + "defaultJobMinCores": modifyGroup.data.default_job_min_cores, + "defaultJobMaxCores": modifyGroup.data.default_job_max_cores, + "minCores": modifyGroup.data.min_cores, + "maxCores": modifyGroup.data.max_cores} GroupDialog.__init__(self, None, modifyGroup, defaults, parent) + class NewGroupDialog(GroupDialog): + """Dialog for creating a new group.""" + def __init__(self, parentGroup, parent=None): - defaults = {"title": "Create New Group", - "message": "Group to be created as a child of the group %s" % parentGroup.name(), - "name": "", - "department": "Unknown", - "defaultJobPriority": 0, - "defaultJobMinCores": 1.0, - "defaultJobMaxCores": 1.0, - "minCores": 0.0, - "maxCores": 1.0} + defaults = { + "title": "Create New Group", + "message": "Group to be created as a child of the group %s" % parentGroup.name(), + "name": "", + "department": "Unknown", + "defaultJobPriority": 0, + "defaultJobMinCores": 1.0, + "defaultJobMaxCores": 1.0, + "minCores": 0.0, + "maxCores": 1.0} GroupDialog.__init__(self, parentGroup, None, defaults, parent) diff --git a/cuegui/cuegui/HostMonitor.py b/cuegui/cuegui/HostMonitor.py index 3ef2d1eed..0c467d004 100644 --- a/cuegui/cuegui/HostMonitor.py +++ b/cuegui/cuegui/HostMonitor.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Widget for displaying a list of hosts with admin controls.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -36,10 +39,12 @@ class HostMonitor(QtWidgets.QWidget): - """This contains the frame list table with controls at the top""" + """Widget for displaying a list of hosts with admin controls.""" + def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) + self.__filterByHostNameLastInput = None self.hostMonitorTree = cuegui.HostMonitorTree.HostMonitorTree(self) # Setup main vertical layout @@ -52,40 +57,47 @@ def __init__(self, parent): # This hlayout would contain any filter/control buttons hlayout = QtWidgets.QHBoxLayout() - self.__filterByHostNameSetup(hlayout) # Menu to filter by host name - self.__filterAllocationSetup(hlayout) # Menu to filter by allocation - self.__filterHardwareStateSetup(hlayout) # Menu to filter by hardware state + self.__filterByHostNameSetup(hlayout) + self.__filterAllocationSetup(hlayout) + self.__filterHardwareStateSetup(hlayout) hlayout.addStretch() - self.__refreshToggleCheckBoxSetup(hlayout) # Checkbox to enable/disable auto refresh - self.__refreshButtonSetup(hlayout) # Button to refresh - self.__clearButtonSetup(hlayout) # Button to clear all filters + self.__refreshToggleCheckBoxSetup(hlayout) + self.__refreshButtonSetup(hlayout) + self.__clearButtonSetup(hlayout) self.layout().addLayout(hlayout) self.layout().addWidget(self.hostMonitorTree) - self.__viewHostsSetup() # For view_hosts signal - - if bool(int(QtGui.qApp.settings.value("AutoRefreshMonitorHost", 1))): # For refresh on launch + self.__viewHostsSetup() + + # pylint: disable=no-member + if bool(int(QtGui.qApp.settings.value("AutoRefreshMonitorHost", 1))): self.updateRequest() + # pylint: enable=no-member def updateRequest(self): + """Requests an update of the displayed information.""" self.hostMonitorTree.updateRequest() def getColumnVisibility(self): + """Gets table column visibility.""" return self.hostMonitorTree.getColumnVisibility() def setColumnVisibility(self, settings): + """Sets table column visibility.""" self.hostMonitorTree.setColumnVisibility(settings) def getColumnOrder(self): + """Gets the table column order.""" return self.hostMonitorTree.getColumnOrder() def setColumnOrder(self, settings): + """Sets the table column order.""" self.hostMonitorTree.setColumnOrder(settings) -# ============================================================================== -# Text box to filter by host name -# ============================================================================== + # ============================================================================== + # Text box to filter by host name + # ============================================================================== def __filterByHostNameSetup(self, layout): btn = QtWidgets.QLineEdit(self) btn.setMaximumHeight(FILTER_HEIGHT) @@ -124,9 +136,9 @@ def __filterByHostNameClear(self): self.__filterByHostName.setText("") self.hostMonitorTree.hostSearch.options['regex'] = [] -# ============================================================================== -# Menu to filter by allocation -# ============================================================================== + # ============================================================================== + # Menu to filter by allocation + # ============================================================================== def __filterAllocationSetup(self, layout): self.__filterAllocationList = sorted( [alloc.name() for alloc in opencue.api.getAllocations()]) @@ -188,9 +200,9 @@ def __filterAllocationHandle(self, action): self.hostMonitorTree.updateRequest() -# ============================================================================== -# Menu to filter by hardware state -# ============================================================================== + # ============================================================================== + # Menu to filter by hardware state + # ============================================================================== def __filterHardwareStateSetup(self, layout): self.__filterHardwareStateList = sorted(opencue.api.host_pb2.HardwareState.keys()) @@ -252,9 +264,9 @@ def __filterHardwareStateHandle(self, action): self.hostMonitorTree.updateRequest() -# ============================================================================== -# Checkbox to toggle auto-refresh -# ============================================================================== + # ============================================================================== + # Checkbox to toggle auto-refresh + # ============================================================================== def __refreshToggleCheckBoxSetup(self, layout): checkBox = QtWidgets.QCheckBox("Auto-refresh", self) layout.addWidget(checkBox) @@ -267,11 +279,13 @@ def __refreshToggleCheckBoxSetup(self, layout): def __refreshToggleCheckBoxHandle(self, state): self.hostMonitorTree.enableRefresh = bool(state) + # pylint: disable=no-member QtGui.qApp.settings.setValue("AutoRefreshMonitorHost", int(bool(state))) + # pylint: enable=no-member -# ============================================================================== -# Button to refresh -# ============================================================================== + # ============================================================================== + # Button to refresh + # ============================================================================== def __refreshButtonSetup(self, layout): """Sets up the refresh button, adds it to the given layout @param layout: The layout to add the button to @@ -292,9 +306,9 @@ def __refreshButtonDisableHandle(self): self.btn_refresh.setEnabled(False) QtCore.QTimer.singleShot(5000, self.__refreshButtonEnableHandle) -# ============================================================================== -# Button to clear all filters -# ============================================================================== + # ============================================================================== + # Button to clear all filters + # ============================================================================== def __clearButtonSetup(self, layout): """Sets up the clear button, adds it to the given layout @param layout: The layout to add the button to @@ -314,11 +328,13 @@ def __clearButtonHandle(self): self.__filterByHostNameClear() self.hostMonitorTree.clearFilters() -# ============================================================================== -# Monitors and handles the view_hosts signal -# ============================================================================== + # ============================================================================== + # Monitors and handles the view_hosts signal + # ============================================================================== def __viewHostsSetup(self): + # pylint: disable=no-member QtGui.qApp.view_hosts.connect(self.__viewHostsHandle) + # pylint: enable=no-member def __viewHostsHandle(self, hosts): self.__clearButtonHandle() diff --git a/cuegui/cuegui/HostMonitorTree.py b/cuegui/cuegui/HostMonitorTree.py index 6b88f8241..f9e5a7c90 100644 --- a/cuegui/cuegui/HostMonitorTree.py +++ b/cuegui/cuegui/HostMonitorTree.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A frame list based on AbstractTreeWidget -""" +"""Tree widget for displaying a list of hosts.""" from __future__ import absolute_import @@ -23,7 +21,6 @@ from __future__ import print_function from builtins import map -from builtins import str import time from PySide2 import QtCore @@ -31,6 +28,9 @@ from PySide2 import QtWidgets import opencue +from opencue.compiled_proto.host_pb2 import HardwareState +from opencue.compiled_proto.host_pb2 import LockState +from opencue.compiled_proto.host_pb2 import ThreadMode import cuegui.AbstractTreeWidget import cuegui.AbstractWidgetItem @@ -41,10 +41,6 @@ import cuegui.Style import cuegui.Utils -from opencue.compiled_proto.host_pb2 import HardwareState -from opencue.compiled_proto.host_pb2 import LockState -from opencue.compiled_proto.host_pb2 import ThreadMode - logger = cuegui.Logger.getLogger(__file__) @@ -52,6 +48,8 @@ class HostMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a list of hosts.""" + def __init__(self, parent): self.startColumnsForType(cuegui.Constants.TYPE_HOST) @@ -160,12 +158,17 @@ def __init__(self, parent): self.itemClicked.connect(self.__itemSingleClickedComment) # Don't use the standard space bar to refresh + # pylint: disable=no-member QtGui.qApp.request_update.connect(self.updateRequest) + # pylint: enable=no-member self.startTicksUpdate(40) # Don't start refreshing until the user sets a filter or hits refresh self.ticksWithoutUpdate = -1 + + # pylint: disable=no-member self.enableRefresh = bool(int(QtGui.qApp.settings.value("AutoRefreshMonitorHost", 1))) + # pylint: enable=no-member def tick(self): if self.ticksWithoutUpdate >= self.updateInterval and \ @@ -196,6 +199,8 @@ def __itemSingleClickedCopy(self, item, col): @param item: The item clicked on @type col: int @param col: The column clicked on""" + del item + del col selected = [host.data.name for host in self.selectedObjects() if cuegui.Utils.isHost(host)] if selected: QtWidgets.QApplication.clipboard().setText(",".join(selected)) @@ -212,6 +217,7 @@ def __itemSingleClickedComment(self, item, col): self.__menuActions.hosts().viewComments([host]) def clearFilters(self): + """Clears any sorting and filtering, restoring the default view.""" self.clearSelection() self.hostSearch = opencue.search.HostSearch() self.sortByColumn(0, QtCore.Qt.AscendingOrder) @@ -229,21 +235,21 @@ def _getUpdate(self): # Sorting by name here incase that makes displaying it faster hosts.sort(key=lambda host: host.data.name) return hosts - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] - def _createItem(self, object, parent=None): + def _createItem(self, rpcObject, parent=None): """Creates and returns the proper item - @type object: Host - @param object: The object for this item + @type rpcObject: Host + @param rpcObject: The object for this item @type parent: QTreeWidgetItem @param parent: Optional parent for this item @rtype: QTreeWidgetItem @return: The created item""" if not parent: parent = self - return HostWidgetItem(object, parent) + return HostWidgetItem(rpcObject, parent) def contextMenuEvent(self, e): """When right clicking on an item, this raises a context menu""" @@ -274,20 +280,25 @@ def dragMoveEvent(self, event): class HostWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): + """Widget item representing a single host.""" + __initialized = False - def __init__(self, object, parent): + # pylint: disable=protected-access + def __init__(self, rpcObject, parent): if not self.__initialized: cuegui.Style.init() self.__class__.__initialized = True self.__class__.__commentIcon = QtGui.QIcon(":comment.png") + # pylint: disable=no-member self.__class__.__backgroundColor = QtGui.qApp.palette().color(QtGui.QPalette.Base) + # pylint: enable=no-member self.__class__.__foregroundColor = cuegui.Style.ColorTheme.COLOR_JOB_FOREGROUND self.__class__.__pausedColor = cuegui.Style.ColorTheme.COLOR_JOB_PAUSED_BACKGROUND self.__class__.__dyingColor = cuegui.Style.ColorTheme.COLOR_JOB_DYING_BACKGROUND self.__class__.__type = cuegui.Constants.TYPE_HOST cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_HOST, object, parent) + self, cuegui.Constants.TYPE_HOST, rpcObject, parent) def data(self, col, role): """Returns the proper display data for the given column and role @@ -303,32 +314,32 @@ def data(self, col, role): self.column_info[col][cuegui.Constants.COLUMN_INFO_DISPLAY](self.rpcObject) return self._cache.get(col, cuegui.Constants.QVARIANT_NULL) - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: return self.__foregroundColor - elif role == QtCore.Qt.BackgroundRole: + if role == QtCore.Qt.BackgroundRole: if not self.rpcObject.data.state == opencue.api.host_pb2.UP: return self.__dyingColor if self.rpcObject.data.lock_state == opencue.api.host_pb2.LOCKED: return self.__pausedColor return self.__backgroundColor - elif role == QtCore.Qt.DecorationRole: + if role == QtCore.Qt.DecorationRole: if col == COMMENT_COLUMN and self.rpcObject.data.has_comment: return self.__commentIcon - elif role == QtCore.Qt.UserRole: + if role == QtCore.Qt.UserRole: return self.__type - elif role == QtCore.Qt.UserRole + 1: + if role == QtCore.Qt.UserRole + 1: return [self.rpcObject.data.total_swap - self.rpcObject.data.free_swap, self.rpcObject.data.total_swap] - elif role == QtCore.Qt.UserRole + 2: + if role == QtCore.Qt.UserRole + 2: return [self.rpcObject.data.total_memory - self.rpcObject.data.free_memory, self.rpcObject.data.total_memory] - elif role == QtCore.Qt.UserRole + 3: + if role == QtCore.Qt.UserRole + 3: return [self.rpcObject.data.total_gpu - self.rpcObject.data.free_gpu, self.rpcObject.data.total_gpu] diff --git a/cuegui/cuegui/ItemDelegate.py b/cuegui/cuegui/ItemDelegate.py index 2a554485e..27aff67ec 100644 --- a/cuegui/cuegui/ItemDelegate.py +++ b/cuegui/cuegui/ItemDelegate.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Custom delegate classes for drawing items in a tree widget.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -28,6 +31,7 @@ import opencue import cuegui.Constants +import cuegui.Style RGB_FRAME_STATE = {opencue.api.job_pb2.SUCCEEDED: QtGui.QColor(55, 200, 55), @@ -50,14 +54,17 @@ class AbstractDelegate(QtWidgets.QItemDelegate): - """Handles drawing of items for the TreeWidget. Provides special handling + """Base delegate class. + + Handles drawing of items for the TreeWidget. Provides special handling for selected jobs in order to still display background color.""" + __colorInvalid = QtGui.QColor() __brushSelected = QtGui.QBrush(QtCore.Qt.Dense4Pattern) __colorUsed = QtGui.QColor(255, 0, 0) __colorFree = QtGui.QColor(0, 255, 0) - def __init__(self, parent, jobProgressBarColumn = None, *args): + def __init__(self, parent, *args): QtWidgets.QItemDelegate.__init__(self, parent, *args) def paint(self, painter, option, index): @@ -89,7 +96,8 @@ def _paintDifferenceBar(self, painter, option, index, used, total): painter.restore() del painter - def _drawProgressBar(self, painter, rect, frameStateTotals): + @staticmethod + def _drawProgressBar(painter, rect, frameStateTotals): """Returns the list that defines the column. @type painter: QPainter @param painter: The painter to draw with @@ -131,7 +139,8 @@ def _paintSelected(self, painter, option, index): painter.restore() del painter - def _drawBackground(self, painter, option, index): + @staticmethod + def _drawBackground(painter, option, index): # Draw the background color painter.setPen(NO_PEN) role = index.data(QtCore.Qt.BackgroundRole) @@ -152,6 +161,8 @@ def _drawSelectionOverlay(self, painter, option): class JobBookingBarDelegate(AbstractDelegate): + """Delegate for the job booking bar.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -159,58 +170,61 @@ def paint(self, painter, option, index): # Only if job if index.data(QtCore.Qt.UserRole) == cuegui.Constants.TYPE_JOB and \ option.rect.width() > 30: - # This itemFromIndex could cause problems - # I need: minCores, maxCores, totalRunning, totalWaiting - job = self.parent().itemFromIndex(index).rpcObject + # This itemFromIndex could cause problems + # I need: minCores, maxCores, totalRunning, totalWaiting + job = self.parent().itemFromIndex(index).rpcObject - rect = option.rect.adjusted(12, 6, -12, -6) + rect = option.rect.adjusted(12, 6, -12, -6) - painter.save() - try: - self._drawBackground(painter, option, index) + painter.save() + try: + self._drawBackground(painter, option, index) + try: + jobRunning = job.data.job_stats.running_frames + jobWaiting = job.data.job_stats.waiting_frames + # pylint: disable=broad-except try: - jobRunning = job.data.job_stats.running_frames - jobWaiting = job.data.job_stats.waiting_frames - try: - cores_per_frame = float(job.data.job_stats.reserved_cores / jobRunning) - except: - cores_per_frame = float(6 / 1) - jobMin = int(job.data.min_cores / cores_per_frame) - jobMax = int(job.data.max_cores / cores_per_frame) - ratio = rect.width() / float(jobRunning + jobWaiting) - - if jobWaiting: - painter.fillRect( - rect.adjusted(0, 2, 0, -2), - RGB_FRAME_STATE[opencue.api.job_pb2.WAITING]) - - if jobRunning: - painter.fillRect( - rect.adjusted(0, 0, -int(ceil(ratio * jobWaiting)), 0), - RGB_FRAME_STATE[opencue.api.job_pb2.RUNNING]) - - painter.setPen(cuegui.Style.ColorTheme.PAUSE_ICON_COLOUR) - x = min(rect.x() + ratio * jobMin, option.rect.right() - 9) - painter.drawLine(x, option.rect.y(), x, - option.rect.y() + option.rect.height()) - - painter.setPen(cuegui.Style.ColorTheme.KILL_ICON_COLOUR) - x = min(rect.x() + ratio * jobMax, option.rect.right() - 6) - painter.drawLine(x, option.rect.y(), x, - option.rect.y() + option.rect.height()) - - except ZeroDivisionError: - pass - - finally: - painter.restore() - del painter + cores_per_frame = float(job.data.job_stats.reserved_cores / jobRunning) + except Exception: + cores_per_frame = float(6 / 1) + jobMin = int(job.data.min_cores / cores_per_frame) + jobMax = int(job.data.max_cores / cores_per_frame) + ratio = rect.width() / float(jobRunning + jobWaiting) + + if jobWaiting: + painter.fillRect( + rect.adjusted(0, 2, 0, -2), + RGB_FRAME_STATE[opencue.api.job_pb2.WAITING]) + + if jobRunning: + painter.fillRect( + rect.adjusted(0, 0, -int(ceil(ratio * jobWaiting)), 0), + RGB_FRAME_STATE[opencue.api.job_pb2.RUNNING]) + + painter.setPen(cuegui.Style.ColorTheme.PAUSE_ICON_COLOUR) + x = min(rect.x() + ratio * jobMin, option.rect.right() - 9) + painter.drawLine(x, option.rect.y(), x, + option.rect.y() + option.rect.height()) + + painter.setPen(cuegui.Style.ColorTheme.KILL_ICON_COLOUR) + x = min(rect.x() + ratio * jobMax, option.rect.right() - 6) + painter.drawLine(x, option.rect.y(), x, + option.rect.y() + option.rect.height()) + + except ZeroDivisionError: + pass + + finally: + painter.restore() + del painter else: AbstractDelegate.paint(self, painter, option, index) class SubBookingBarDelegate(AbstractDelegate): + """Delegate for the subscription booking bar.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -261,6 +275,8 @@ def paint(self, painter, option, index): class JobThinProgressBarDelegate(AbstractDelegate): + """Delegate for a small job progress bar.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -286,6 +302,8 @@ def paint(self, painter, option, index): class JobProgressBarDelegate(AbstractDelegate): + """Delegate for the fullsize job progress bar.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -295,22 +313,20 @@ def paint(self, painter, option, index): frameStateTotals = index.data(QtCore.Qt.UserRole + 1) complete_tasks = frameStateTotals[opencue.api.job_pb2.SUCCEEDED] - total_tasks = sum([i for i in frameStateTotals.values()]) + total_tasks = sum(frameStateTotals.values()) proc = float(complete_tasks) * 100 / float(total_tasks) line = "{0:d} % ({1:d}/{2:d})".format(int(proc), int(complete_tasks), int(total_tasks)) - - state = index.data(QtCore.Qt.UserRole + 2) - paused = index.data(QtCore.Qt.UserRole + 3) painter.save() try: + # pylint: disable=broad-except try: self._drawProgressBar(painter, option.rect.adjusted(0, 2, 0, -2), frameStateTotals) painter.setPen(QtCore.Qt.black) painter.drawText(option.rect, QtCore.Qt.AlignCenter, line) - except Exception as e: + except Exception: painter.setPen(QtCore.Qt.red) painter.drawText(option.rect, QtCore.Qt.AlignCenter, "Gui Error") finally: @@ -321,6 +337,8 @@ def paint(self, painter, option, index): class HostSwapBarDelegate(AbstractDelegate): + """Delegate for the host swap field.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -333,6 +351,8 @@ def paint(self, painter, option, index): class HostMemBarDelegate(AbstractDelegate): + """Delegate for the host memory field.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -345,6 +365,8 @@ def paint(self, painter, option, index): class HostGpuBarDelegate(AbstractDelegate): + """Delegate for the host GPU field.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -357,15 +379,17 @@ def paint(self, painter, option, index): class HostHistoryDelegate(AbstractDelegate): -#To use this delegate, the host item must have this: -#in __init__: -# self.coresHistory = [object.coresReserved()] -# def update(self, object = None, parent = None): -# if object: -# self.coresHistory.append(object.coresReserved()) -# if len(self.coresHistory) > 40: -# self.coresHistory.pop(0) -# AbstractWidgetItem.update(self, object, parent) + """Delegate for the host history field.""" + + # To use this delegate, the host item must have this: + # in __init__: + # self.coresHistory = [object.coresReserved()] + # def update(self, object = None, parent = None): + # if object: + # self.coresHistory.append(object.coresReserved()) + # if len(self.coresHistory) > 40: + # self.coresHistory.pop(0) + # AbstractWidgetItem.update(self, object, parent) def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -396,7 +420,9 @@ def paint(self, painter, option, index): points.setPoint(0, option.rect.bottomLeft()) num = 1 for i in range(len(hostItem.coresHistory)): - points.setPoint(num, option.rect.x() + stepWidth * i, option.rect.bottom() - ratioHeight * hostItem.coresHistory[i]) + points.setPoint( + num, option.rect.x() + stepWidth * i, + option.rect.bottom() - ratioHeight * hostItem.coresHistory[i]) num += 1 points.setPoint(num, option.rect.bottomRight()) @@ -414,6 +440,8 @@ def paint(self, painter, option, index): class ItemDelegate(AbstractDelegate): + """Generic item delegate class.""" + def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -422,6 +450,7 @@ def paint(self, painter, option, index): class ProgressDelegate(AbstractDelegate): + """Delegate for displaying layer progress.""" def __init__(self, parent, *args): AbstractDelegate.__init__(self, parent, *args) @@ -440,9 +469,12 @@ def paint(self, painter, option, index): opts.text = "{0:d} %".format(progress) opts.textVisible = True - QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_ProgressBar, opts, painter) + QtWidgets.QApplication.style().drawControl( + QtWidgets.QStyle.CE_ProgressBar, opts, painter) else: AbstractDelegate.paint(self, painter, option, index) def sizeHint(self, option, index): + del option + del index return QtCore.QSize(12, 12) diff --git a/cuegui/cuegui/JobMonitorTree.py b/cuegui/cuegui/JobMonitorTree.py index 45318e77e..58773c0ac 100644 --- a/cuegui/cuegui/JobMonitorTree.py +++ b/cuegui/cuegui/JobMonitorTree.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A monitored job list based on AbstractTreeWidget -""" +"""Tree widget to display a list of monitored jobs.""" from __future__ import absolute_import @@ -70,10 +68,14 @@ def displayState(job): class JobMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget to display a list of monitored jobs.""" + __loadMine = True view_object = QtCore.Signal(object) def __init__(self, parent): + self.ticksWithoutUpdate = 0 + self.startColumnsForType(cuegui.Constants.TYPE_JOB) self.addColumn("Job", 470, id=1, data=lambda job: job.data.name, @@ -87,8 +89,7 @@ def __init__(self, parent): tip="If the job has auto eating enabled, a pac-man icon\n" "will appear here and all frames that become dead will\n" "automatically be eaten.") -# self.addColumn("Group", 70, id=3, -# data=lambda job:("%s [%d]" % (job.group(), job.priority()))) + # pylint: disable=unnecessary-lambda self.addColumn("State", 80, id=4, data=lambda job: displayState(job), tip="The state of each job.\n" @@ -186,9 +187,12 @@ def __itemSingleClickedCopy(self, item, col): @param item: The item clicked on @type col: int @param col: The column clicked on""" - selected = [job.data.name for job in self.selectedObjects() if cuegui.Utils.isJob(job)] + del item + del col + selected = [job.data.name for job in self.selectedObjects() if cuegui.Utils.isJob(job)] if selected: - QtWidgets.QApplication.clipboard().setText(" ".join(selected), QtGui.QClipboard.Selection) + QtWidgets.QApplication.clipboard().setText( + " ".join(selected), QtGui.QClipboard.Selection) def __itemSingleClickedComment(self, item, col): """If the comment column is clicked on, and there is a comment on the @@ -235,13 +239,16 @@ def addJob(self, job): self.ticksLock.unlock() def getJobProxies(self): + """Gets a list of IDs of monitored jobs.""" return list(self._items.keys()) def _removeItem(self, item): """Removes an item from the TreeWidget without locking @param item: A tree widget item @type item: AbstractTreeWidgetItem""" + # pylint: disable=no-member QtGui.qApp.unmonitor.emit(item.rpcObject) + # pylint: enable=no-member cuegui.AbstractTreeWidget.AbstractTreeWidget._removeItem(self, item) self.__jobTimeLoaded.pop(item.rpcObject, "") @@ -249,7 +256,9 @@ def removeAllItems(self): """Notifies the other widgets of each item being unmonitored, then calls the the AbstractTreeWidget.removeAllItems like normal""" for proxy in list(self._items.keys()): + # pylint: disable=no-member QtGui.qApp.unmonitor.emit(proxy) + # pylint: enable=no-member if proxy in self.__jobTimeLoaded: del self.__jobTimeLoaded[proxy] cuegui.AbstractTreeWidget.AbstractTreeWidget.removeAllItems(self) @@ -390,25 +399,26 @@ def _getUpdate(self): objectKey = cuegui.Utils.getObjectKey(job) jobs[objectKey] = job - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return None return jobs - def _processUpdate(self, work, jobObjects): - if jobObjects is None: + def _processUpdate(self, work, rpcObjects): + if rpcObjects is None: return self._itemsLock.lockForWrite() # include rpcObjects from self._items that are not in jobObjects for proxy, item in list(self._items.items()): - if not proxy in jobObjects: - jobObjects[proxy] = item.rpcObject + if not proxy in rpcObjects: + rpcObjects[proxy] = item.rpcObject try: - selectedKeys = [cuegui.Utils.getObjectKey(item.rpcObject) for item in self.selectedItems()] + selectedKeys = [ + cuegui.Utils.getObjectKey(item.rpcObject) for item in self.selectedItems()] scrolled = self.verticalScrollBar().value() # Store the creation time for the current item @@ -418,7 +428,7 @@ def _processUpdate(self, work, jobObjects): self._items = {} self.clear() - for proxy, job in list(jobObjects.items()): + for proxy, job in list(rpcObjects.items()): self._items[proxy] = JobWidgetItem(job, self.invisibleRootItem(), self.__jobTimeLoaded.get(proxy, None)) @@ -426,14 +436,18 @@ def _processUpdate(self, work, jobObjects): self._items[proxy].setUserColor(self.__userColors[proxy]) self.verticalScrollBar().setValue(scrolled) - [self._items[key].setSelected(True) for key in selectedKeys if key in self._items] - except Exception as e: + list(map(lambda key: self._items[key].setSelected(True), + [key for key in selectedKeys if key in self._items])) + + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) finally: self._itemsLock.unlock() + class JobWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): """Represents a job entry in the CueJobTreeWidget.""" + __initialized = False __commentIcon = None __eatIcon = None @@ -447,14 +461,18 @@ class JobWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): __centerAlign = None __type = None __userColor = None - def __init__(self, object, parent, created): + + # pylint: disable=protected-access + def __init__(self, rpcObject, parent, created): if not self.__initialized: if cuegui.Style.ColorTheme is None: cuegui.Style.init() self.__class__.__initialized = True self.__class__.__commentIcon = QtGui.QIcon(":comment.png") self.__class__.__eatIcon = QtGui.QIcon(":eat.png") + # pylint: disable=no-member self.__class__.__backgroundColor = QtGui.qApp.palette().color(QtGui.QPalette.Base) + # pylint: enable=no-member self.__class__.__foregroundColor = cuegui.Style.ColorTheme.COLOR_JOB_FOREGROUND self.__class__.__pausedColor = cuegui.Style.ColorTheme.COLOR_JOB_PAUSED_BACKGROUND self.__class__.__dyingColor = cuegui.Style.ColorTheme.COLOR_JOB_DYING_BACKGROUND @@ -470,33 +488,34 @@ def __init__(self, object, parent, created): self.created = created or time.time() cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_JOB, object, parent) + self, cuegui.Constants.TYPE_JOB, rpcObject, parent) def setUserColor(self, color): + """Sets the color scheme.""" self.__userColor = color def data(self, col, role): if role == QtCore.Qt.DisplayRole: return self.column_info[col][cuegui.Constants.COLUMN_INFO_DISPLAY](self.rpcObject) - elif role == QtCore.Qt.ForegroundRole: + if role == QtCore.Qt.ForegroundRole: if col == 0: if self.created > time.time() - 5: return self.__newJobColor return self.__foregroundColor - elif role == QtCore.Qt.BackgroundRole and col == COLUMN_STATE: + if role == QtCore.Qt.BackgroundRole and col == COLUMN_STATE: if self.rpcObject.data.state == opencue.api.job_pb2.FINISHED: return self.__finishedColor - elif self.rpcObject.data.is_paused: + if self.rpcObject.data.is_paused: return self.__pausedColor - elif self.rpcObject.data.job_stats.dead_frames: + if self.rpcObject.data.job_stats.dead_frames: return self.__dyingColor return self.__backgroundColor - elif role == QtCore.Qt.BackgroundRole and self.__userColor: + if role == QtCore.Qt.BackgroundRole and self.__userColor: return self.__userColor - elif role == QtCore.Qt.FontRole and col == COLUMN_NAME: + if role == QtCore.Qt.FontRole and col == COLUMN_NAME: if self.created > time.time() - 5: return self.__newJobFont diff --git a/cuegui/cuegui/LayerDialog.py b/cuegui/cuegui/LayerDialog.py index a091cdba2..989df17cd 100644 --- a/cuegui/cuegui/LayerDialog.py +++ b/cuegui/cuegui/LayerDialog.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Dialog for editing a layer.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -30,15 +33,14 @@ def warning(msg, parent=None): - """ - Utility method for poping up a warning. - """ + """Utility method for popping up a warning.""" box = QtWidgets.QMessageBox(parent) box.setText(msg) box.exec_() class EnableableItem(QtWidgets.QWidget): + """General class for widget items which can be enabled and disabled.""" def __init__(self, widget, enable, parent=None): QtWidgets.QWidget.__init__(self, parent) self.__widget = widget @@ -53,21 +55,22 @@ def __init__(self, widget, enable, parent=None): layout.addWidget(self.__widget) def getWidget(self): + """Gets the wrapped widget.""" return self.__widget def isEnabled(self): + """Gets the enabled state.""" return self.__checkbox.isChecked() - def enable(self, b): - self.__checkbox.setChecked(b) - self.__widget.setDisabled(b == False) + def enable(self, is_enabled): + """Sets the enabled state.""" + self.__checkbox.setChecked(is_enabled) + self.__widget.setDisabled(is_enabled is False) class LayerPropertiesItem(QtWidgets.QWidget): - """ - An key/value widget for populating a dialog box. - """ - def __init__(self, label, widget, stretch=True, help=None, parent=None): + """An key/value widget for populating a dialog box.""" + def __init__(self, label, widget, stretch=True, help_widget=None, parent=None): QtWidgets.QWidget.__init__(self, parent) self.__label = label self.__widget = widget @@ -78,14 +81,12 @@ def __init__(self, label, widget, stretch=True, help=None, parent=None): if stretch: layout.addStretch() layout.addWidget(self.__widget) - if help: - layout.addWidget(help) + if help_widget: + layout.addWidget(help_widget) class SlideSpinner(QtWidgets.QWidget): - """ - A QSlider and QSpinBox - """ + """A QSlider and QSpinBox.""" def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent) @@ -101,12 +102,11 @@ def __init__(self, parent=None): class LayerPropertiesDialog(QtWidgets.QDialog): - """ - A dialog box for editing a layer. - """ + """Dialog for editing a layer.""" + def __init__(self, layers, parent=None): QtWidgets.QDialog.__init__(self, parent) - self.__layers = [opencue.api.getLayer(opencue.id(l)) for l in layers] + self.__layers = [opencue.api.getLayer(opencue.id(layer)) for layer in layers] self.setWindowTitle("Layer Properties") @@ -126,7 +126,7 @@ def __init__(self, layers, parent=None): self.__group = QtWidgets.QGroupBox("Resource Options", self) - ## Memory + # Memory self.__mem = SlideSpinner(self) self.__mem.slider.setMinimumWidth(200) self.__mem.slider.setRange(self.mem_min_kb, self.mem_max_kb) @@ -135,20 +135,22 @@ def __init__(self, layers, parent=None): self.__mem.spinner.setSuffix(" GB") self.__mem.spinner.setRange(self.mem_min_gb, self.mem_max_gb) - ## Cores + # Cores self.__core = QtWidgets.QDoubleSpinBox(self) self.__core.setDecimals(1) self.__core.setRange(0, int(self._cfg().get('max_cores', 16))) self.__core.setSingleStep(1) - ## Max cores + # Max cores self.__max_cores = QtWidgets.QSpinBox(self) self.__max_cores.setRange(0, int(self._cfg().get('max_cores', 16))) self.__max_cores.setSingleStep(1) - ## Disable this for everything except commander. + # Disable this for everything except commander. + # pylint: disable=no-member if QtGui.qApp.applicationName() != "CueCommander": self.__core.setDisabled(True) + # pylint: enable=no-member # Threads self.__thread = QtWidgets.QCheckBox(self) @@ -174,7 +176,7 @@ def __init__(self, layers, parent=None): # Tags self.__tags = LayerTagsWidget(self.__layers, self) - + # Limits self.__limits = LayerLimitsWidget(self.__layers, self) @@ -255,17 +257,18 @@ def __init__(self, layers, parent=None): self.layout().addWidget(self.__buttons) def _cfg(self): - ''' + """ Loads (if necessary) and returns the config values. Warns and returns an empty dict if there's a problem reading the config @return: The keys & values stored in the config file @rtype: dict - ''' + """ if not hasattr(self, '__config'): self.__config = cuegui.Utils.getResourceConfig() return self.__config + # pylint: disable=inconsistent-return-statements def verify(self): """ Verify the contents of all widgets. @@ -312,6 +315,7 @@ def apply(self): self.close() def getMaxMemory(self): + """Gets the layer max memory.""" result = 0 for layer in self.__layers: if layer.data.min_memory > result: @@ -319,9 +323,11 @@ def getMaxMemory(self): return result def getMaxGpu(self): + """Gets the layer max GPU.""" return max([layer.data.min_gpu // self.gpu_tick_kb for layer in self.__layers]) def getMinCores(self): + """Gets the layer min cores.""" result = 0 for layer in self.__layers: if layer.data.min_cores > result: @@ -329,6 +335,7 @@ def getMinCores(self): return result def getMaxCores(self): + """Gets the layer max cores.""" result = 0 for layer in self.__layers: if layer.data.max_cores > result: @@ -336,6 +343,7 @@ def getMaxCores(self): return result def getThreading(self): + """Gets whether the layer is threadable.""" result = False for layer in self.__layers: if layer.data.is_threadable: @@ -344,6 +352,7 @@ def getThreading(self): return result def getTimeout(self): + """Gets the layer timeout.""" result = 0 for layer in self.__layers: if layer.data.timeout > result: @@ -351,6 +360,7 @@ def getTimeout(self): return result def getTimeoutLLU(self): + """Gets the layer LLU timeout.""" result = 0 for layer in self.__layers: if layer.data.timeout_llu > result: @@ -358,6 +368,7 @@ def getTimeoutLLU(self): return result def getMemoryOptSetting(self): + """Gets whether the layer has memory optimizer enabled.""" result = False for layer in self.__layers: if layer.data.memory_optimizer_enabled: @@ -409,10 +420,10 @@ def apply(self): for layer in self.__layers: layer.setTags(tags) except opencue.CueException as e: - warning = QtWidgets.QMessageBox(self) - warning.setText("Error applying layer tags.") - warning.setDetailedText("%s" % e) - warning.exec_() + warning_dialog = QtWidgets.QMessageBox(self) + warning_dialog.setText("Error applying layer tags.") + warning_dialog.setDetailedText("%s" % e) + warning_dialog.exec_() def verify(self): """ @@ -422,7 +433,7 @@ def verify(self): warning("You must have at least 1 tag selected.") return False return True - + class LayerLimitsWidget(QtWidgets.QWidget): """ @@ -459,12 +470,14 @@ def apply(self): class LayerTagsDialog(QtWidgets.QDialog): + """Dialog for displayer a layer's tags.""" + def __init__(self, layers, parent=None): QtWidgets.QDialog.__init__(self, parent) self._tags_widget = LayerTagsWidget(layers=layers, parent=parent) - self.__warning = QtWidgets.QLabel("Warning: Changing these tags may cause " - "your job to not run any frames") + self.__warning = QtWidgets.QLabel( + 'Warning: Changing these tags may cause your job to not run any frames') self.__buttons = QtWidgets.QDialogButtonBox( QtWidgets.QDialogButtonBox.Save | QtWidgets.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, @@ -476,4 +489,3 @@ def __init__(self, layers, parent=None): def accept(self): self._tags_widget.apply() self.close() - diff --git a/cuegui/cuegui/LayerMonitorTree.py b/cuegui/cuegui/LayerMonitorTree.py index 19f21b78e..fdf0c249e 100644 --- a/cuegui/cuegui/LayerMonitorTree.py +++ b/cuegui/cuegui/LayerMonitorTree.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A layer list based on AbstractTreeWidget -""" +"""Tree widget for displaying a list of layers.""" from __future__ import absolute_import @@ -35,12 +33,14 @@ def displayRange(layer): + """Returns a string representation of a layer's frame range.""" if layer.data.chunk_size != 1: return '%s chunked %s' % (layer.data.range, layer.data.chunk_size) return layer.data.range class LayerMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a list of layers.""" handle_filter_layers_byLayer = QtCore.Signal(list) @@ -58,6 +58,7 @@ def __init__(self, parent): self.addColumn("Limits", 100, id=4, data=lambda layer: ",".join(layer.data.limits), tip="The limits that have been applied to this layer's frames.") + # pylint: disable=unnecessary-lambda self.addColumn("Range", 150, id=5, data=lambda layer: displayRange(layer), tip="The range of frames that the layer should render.") @@ -79,11 +80,12 @@ def __init__(self, parent): tip="The amount of gpu memory each frame in this layer\n" "will reserve for its use. Note that we may not have\n" "machines as much gpu memory as you request.") - self.addColumn("MaxRss", 60, id=9, - data=lambda layer: cuegui.Utils.memoryToString(layer.data.layer_stats.max_rss), - sort=lambda layer: layer.data.layer_stats.max_rss, - tip="Maximum amount of memory used by any frame in\n" - "this layer at any time since the job was launched.") + self.addColumn( + "MaxRss", 60, id=9, + data=lambda layer: cuegui.Utils.memoryToString(layer.data.layer_stats.max_rss), + sort=lambda layer: layer.data.layer_stats.max_rss, + tip="Maximum amount of memory used by any frame in\n" + "this layer at any time since the job was launched.") self.addColumn("Total", 40, id=10, data=lambda layer: layer.data.layer_stats.total_frames, sort=lambda layer: layer.data.layer_stats.total_frames, @@ -112,11 +114,11 @@ def __init__(self, parent): data=lambda layer: layer.data.layer_stats.dead_frames, sort=lambda layer: layer.data.layer_stats.dead_frames, tip="Total number of dead frames in this layer.") - self.addColumn("Avg", 65, id=17, - data=lambda layer: cuegui.Utils.secondsToHHMMSS(layer.data.layer_stats.avg_frame_sec), - sort=lambda layer: layer.data.layer_stats.avg_frame_sec, - tip="Average number of HOURS:MINUTES:SECONDS per frame\n" - "in this layer.") + self.addColumn( + "Avg", 65, id=17, + data=lambda layer: cuegui.Utils.secondsToHHMMSS(layer.data.layer_stats.avg_frame_sec), + sort=lambda layer: layer.data.layer_stats.avg_frame_sec, + tip="Average number of HOURS:MINUTES:SECONDS per frame\nin this layer.") self.addColumn("Tags", 100, id=18, data=lambda layer: " | ".join(layer.data.tags), tip="The tags define what resources may be booked on\n" @@ -147,6 +149,7 @@ def __init__(self, parent): self.__load = None self.startTicksUpdate(20, False, 60*60*24) + # pylint: disable=attribute-defined-outside-init def tick(self): if self.__load: self.__job = self.__load @@ -169,8 +172,10 @@ def updateRequest(self): since last updated""" self.ticksWithoutUpdate = 9999 + # pylint: disable=inconsistent-return-statements def setJob(self, job): - """Sets the current job + """Sets the current job. + @param job: Job can be None, a job object, or a job name. @type job: job, string, None""" if job is None: @@ -188,6 +193,7 @@ def __setJob(self, job): self.removeAllItems() def getJob(self): + """Gets the current job.""" return self.__job def _createItem(self, obj): @@ -221,7 +227,7 @@ def contextMenuEvent(self, e): if len(__selectedObjects) == 1: menu.addSeparator() self.__menuActions.layers().addAction(menu, "useLocalCores") - if len(set([layer.data.range for layer in __selectedObjects])) == 1: + if len({layer.data.range for layer in __selectedObjects}) == 1: self.__menuActions.layers().addAction(menu, "reorder") self.__menuActions.layers().addAction(menu, "stagger") @@ -238,9 +244,13 @@ def contextMenuEvent(self, e): menu.exec_(e.globalPos()) def __itemDoubleClickedFilterLayer(self, item, col): + del col self.handle_filter_layers_byLayer.emit([item.rpcObject.data.name]) + class LayerWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single layer.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_LAYER, object, parent) + self, cuegui.Constants.TYPE_LAYER, rpcObject, parent) diff --git a/cuegui/cuegui/LimitSelectionWidget.py b/cuegui/cuegui/LimitSelectionWidget.py index 84c0c30f4..8a37e370e 100644 --- a/cuegui/cuegui/LimitSelectionWidget.py +++ b/cuegui/cuegui/LimitSelectionWidget.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A Widget for displaying and editing Limits -""" +"""A widget for displaying and editing limits.""" from __future__ import absolute_import @@ -30,32 +28,28 @@ class LimitSelectionWidget(QtWidgets.QWidget): - """ - A Widget for displaying and editing Limits. Includes checkboxes for the given - list of limit options. - """ + """A widget for displaying and editing limits. + + Includes checkboxes for the given list of limit options.""" + def __init__(self, limits=None, parent=None): """ - A Widget for displaying and editing Limits - @param limits: The list of limits to include as checkboxes. @type limits: list @param parent: The parent widget for this LimitSelectionWidget. Default is None @type parent: QtWidgets.QWidget """ - QtWidgets.QWidget.__init__(self, parent) layout = QtWidgets.QGridLayout(self) - + self.limits = cuegui.AbstractDialog.CheckBoxSelectionMatrix( 'Limits', limits, [], self) layout.addWidget(self.limits, 0, 0, 1, 2) layout.setContentsMargins(0, 0, 0, 0) - + def enable_limits(self, limits_to_enable=None): - """ - Set the limit value based on the given list of limits. - + """Sets the limit value based on the given list of limits. + @param limits_to_enable: The list of limits to enable @type limits_to_enable: iter """ @@ -63,9 +57,8 @@ def enable_limits(self, limits_to_enable=None): self.limits.checkBoxes(limits_to_enable) def get_selected_limits(self): - """ - Returns the list of selected limits. - + """Returns the list of selected limits. + @return: The list of selected limits @rtype: list """ diff --git a/cuegui/cuegui/LimitsWidget.py b/cuegui/cuegui/LimitsWidget.py index 360eaec70..b7eaccd3b 100644 --- a/cuegui/cuegui/LimitsWidget.py +++ b/cuegui/cuegui/LimitsWidget.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Widget for managing limits.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -28,115 +31,134 @@ import cuegui.Constants import cuegui.Logger import cuegui.MenuActions +import cuegui.Utils logger = cuegui.Logger.getLogger(__file__) class LimitsWidget(QtWidgets.QWidget): - def __init__(self, parent): - QtWidgets.QWidget.__init__(self, parent) - - self.__btnRefresh = QtWidgets.QPushButton("Refresh", self) - self.__btnRefresh.setFocusPolicy(QtCore.Qt.NoFocus) - self.__btnAddLimit = QtWidgets.QPushButton("Add Limit", self) - self.__btnAddLimit.setFocusPolicy(QtCore.Qt.NoFocus) - - self.__monitorLimits = LimitsTreeWidget(self) - - layout = QtWidgets.QGridLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - layout.addWidget(self.__btnAddLimit, 0, 3) - layout.addWidget(self.__btnRefresh, 0, 2) - layout.addWidget(self.__monitorLimits, 2, 0, 3, 4) - - self.__btnAddLimit.clicked.connect(self.__addLimit) - self.__btnRefresh.clicked.connect(self.updateSoon) - - self.__menuActions = cuegui.MenuActions.MenuActions(self, self.updateSoon, list) - - def updateSoon(self): - self.__monitorLimits._update() - - def __addLimit(self): - self.__menuActions.limits().create() - self.updateSoon() - - def getColumnVisibility(self): - return self.__monitorLimits.getColumnVisibility() - - def setColumnVisibility(self, settings): - self.__monitorLimits.setColumnVisibility(settings) - - def getColumnOrder(self): - return self.__monitorLimits.getColumnOrder() - - def setColumnOrder(self, settings): - self.__monitorLimits.setColumnOrder(settings) + """Widget for managing limits.""" + + def __init__(self, parent): + QtWidgets.QWidget.__init__(self, parent) + + self.__btnRefresh = QtWidgets.QPushButton("Refresh", self) + self.__btnRefresh.setFocusPolicy(QtCore.Qt.NoFocus) + self.__btnAddLimit = QtWidgets.QPushButton("Add Limit", self) + self.__btnAddLimit.setFocusPolicy(QtCore.Qt.NoFocus) + + self.__monitorLimits = LimitsTreeWidget(self) + + layout = QtWidgets.QGridLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + layout.addWidget(self.__btnAddLimit, 0, 3) + layout.addWidget(self.__btnRefresh, 0, 2) + layout.addWidget(self.__monitorLimits, 2, 0, 3, 4) + + self.__btnAddLimit.clicked.connect(self.__addLimit) + self.__btnRefresh.clicked.connect(self.updateSoon) + + self.__menuActions = cuegui.MenuActions.MenuActions(self, self.updateSoon, list) + + def updateSoon(self): + """Requests a refresh of the limits list.""" + # pylint: disable=protected-access + self.__monitorLimits._update() + + def __addLimit(self): + self.__menuActions.limits().create() + self.updateSoon() + + def getColumnVisibility(self): + """Gets the table column visibility.""" + return self.__monitorLimits.getColumnVisibility() + + def setColumnVisibility(self, settings): + """Sets the table column visibility.""" + self.__monitorLimits.setColumnVisibility(settings) + + def getColumnOrder(self): + """Gets the table column order.""" + return self.__monitorLimits.getColumnOrder() + + def setColumnOrder(self, settings): + """Sets the table column order.""" + self.__monitorLimits.setColumnOrder(settings) class LimitsTreeWidget(cuegui.AbstractTreeWidget.AbstractTreeWidget): - def __init__(self, parent): - self.startColumnsForType(cuegui.Constants.TYPE_LIMIT) - self.addColumn("Limit Name", 90, id=1, - data=lambda limit: limit.name()) - self.addColumn("Max Value", 80, id=2, - data=lambda limit: ("%d" % limit.maxValue()), - sort=lambda limit: limit.maxValue()) - self.addColumn("Current Running", 80, id=2, - data=lambda limit: ("%d" % limit.currentRunning()), - sort=lambda limit: limit.currentRunning()) - - cuegui.AbstractTreeWidget.AbstractTreeWidget.__init__(self, parent) - - # Used to build right click context menus - self.__menuActions = cuegui.MenuActions.MenuActions( - self, self.updateSoon, self.selectedObjects) - - self.itemClicked.connect(self.__itemSingleClickedToDouble) - QtGui.qApp.facility_changed.connect(self.__facilityChanged) - - self.setUpdateInterval(60) - - def __facilityChanged(self): - """Called when the facility is changed""" - self.removeAllItems() - self._update() - - def __itemSingleClickedToDouble(self, item, col): - """Called when an item is clicked on. Causes single clicks to be treated - as double clicks. - @type item: QTreeWidgetItem - @param item: The item single clicked on - @type col: int - @param col: Column number single clicked on""" - self.itemDoubleClicked.emit(item, col) - - def _createItem(self, object): - """Creates and returns the proper item""" - item = LimitWidgetItem(object, self) - return item - - def _getUpdate(self): - """Returns the proper data from the cuebot""" - try: - return opencue.api.getLimits() - except Exception as e: - logger.critical(e) - return [] - - def contextMenuEvent(self, e): - """When right clicking on an item, this raises a context menu""" - menu = QtWidgets.QMenu() - self.__menuActions.limits().addAction(menu, "editMaxValue") - menu.addSeparator() - self.__menuActions.limits().addAction(menu, "delete") - self.__menuActions.limits().addAction(menu, "rename") - menu.exec_(QtCore.QPoint(e.globalX(), e.globalY())) + """Tree widget for displaying a list of limits.""" + + def __init__(self, parent): + self.startColumnsForType(cuegui.Constants.TYPE_LIMIT) + self.addColumn("Limit Name", 90, id=1, + data=lambda limit: limit.name()) + self.addColumn("Max Value", 80, id=2, + data=lambda limit: ("%d" % limit.maxValue()), + sort=lambda limit: limit.maxValue()) + self.addColumn("Current Running", 80, id=2, + data=lambda limit: ("%d" % limit.currentRunning()), + sort=lambda limit: limit.currentRunning()) + + cuegui.AbstractTreeWidget.AbstractTreeWidget.__init__(self, parent) + + # Used to build right click context menus + self.__menuActions = cuegui.MenuActions.MenuActions( + self, self.updateSoon, self.selectedObjects) + + self.itemClicked.connect(self.__itemSingleClickedToDouble) + # pylint: disable=no-member + QtGui.qApp.facility_changed.connect(self.__facilityChanged) + # pylint: enable=no-member + + self.setUpdateInterval(60) + + def __facilityChanged(self): + """Called when the facility is changed""" + self.removeAllItems() + self._update() + + def __itemSingleClickedToDouble(self, item, col): + """Called when an item is clicked on. Causes single clicks to be treated + as double clicks. + @type item: QTreeWidgetItem + @param item: The item single clicked on + @type col: int + @param col: Column number single clicked on""" + self.itemDoubleClicked.emit(item, col) + + def _createItem(self, rpcObject): + """Creates and returns the proper item""" + item = LimitWidgetItem(rpcObject, self) + return item + + # pylint: disable=no-self-use + def _getUpdate(self): + """Returns the proper data from the cuebot""" + try: + return opencue.api.getLimits() + except opencue.exception.CueException as e: + list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) + return [] + + def contextMenuEvent(self, e): + """When right clicking on an item, this raises a context menu""" + menu = QtWidgets.QMenu() + self.__menuActions.limits().addAction(menu, "editMaxValue") + menu.addSeparator() + self.__menuActions.limits().addAction(menu, "delete") + self.__menuActions.limits().addAction(menu, "rename") + menu.exec_(QtCore.QPoint(e.globalX(), e.globalY())) + + def tick(self): + pass class LimitWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): - cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_LIMIT, object, parent) + """Widget item for displaying a single limit.""" + + def __init__(self, rpcObject, parent): + cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( + self, cuegui.Constants.TYPE_LIMIT, rpcObject, parent) diff --git a/cuegui/cuegui/LocalBooking.py b/cuegui/cuegui/LocalBooking.py index 351f049b6..475f0536f 100644 --- a/cuegui/cuegui/LocalBooking.py +++ b/cuegui/cuegui/LocalBooking.py @@ -13,6 +13,9 @@ # limitations under the License. +"""A widget for creating RenderPartitions, otherwise known as local core booking.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -37,10 +40,7 @@ class LocalBookingWidget(QtWidgets.QWidget): - """ - A widget for creating opencue RenderParitions, otherwise know - as local core booking. - """ + """A widget for creating RenderPartitions, otherwise known as local core booking.""" hosts_changed = QtCore.Signal() @@ -65,7 +65,7 @@ def __init__(self, target, parent=None): for host in owner.getHosts(): if host.data.lockState != opencue.api.host_pb2.OPEN: self.__select_host.addItem(host.data.name) - except Exception: + except opencue.exception.CueException: pass self.__deed_button = None @@ -82,10 +82,10 @@ def __init__(self, target, parent=None): self.__text_target = QtWidgets.QLabel(self.__target.data.name, self) self.__num_threads = QtWidgets.QSpinBox(self) - self.__num_threads.setValue(1); + self.__num_threads.setValue(1) self.__num_cores = QtWidgets.QLineEdit(self) - self.__num_cores.setText("1"); + self.__num_cores.setText("1") self.__num_cores.setReadOnly(True) self.__num_frames = QtWidgets.QSpinBox(self) @@ -94,7 +94,7 @@ def __init__(self, target, parent=None): self.__frame_warn = QtWidgets.QLabel(self) self.__num_mem = QtWidgets.QSlider(self) - self.__num_mem.setValue(4); + self.__num_mem.setValue(4) self.__num_mem.setOrientation(QtCore.Qt.Horizontal) self.__num_mem.setTickPosition(QtWidgets.QSlider.TicksBelow) self.__num_mem.setTickInterval(1) @@ -124,7 +124,6 @@ def __init__(self, target, parent=None): self.__btn_clear = QtWidgets.QPushButton("Clear", self) - # # Setup the signals. # @@ -182,21 +181,23 @@ def __init__(self, target, parent=None): self.layout().addLayout(self.__stack) - ## Set initial values. + # Set initial values. self.__host_changed(self.__select_host.currentText()) self.resize(400, 400) def getTargetJobName(self): + """Gets the job name of the target.""" + if cuegui.Utils.isJob(self.__target): return self.__target.data.name - elif cuegui.Utils.isLayer(self.__target): + if cuegui.Utils.isLayer(self.__target): return self.__target.name - elif cuegui.Utils.isFrame(self.__target): + if cuegui.Utils.isFrame(self.__target): return self.__parent.getJob().data.name - else: - return '' + return '' def hostAvailable(self): + """Gets whether the host has cores available.""" return self.__select_host.count() > 0 def __host_changed(self, hostname): @@ -206,7 +207,7 @@ def __host_changed(self, hostname): host = opencue.api.findHost(str(hostname)) try: rp = [r for r in host.getRenderPartitions() if r.job == self.jobName] - + if rp: rp = rp[0] self.__stack.setCurrentIndex(1) @@ -223,15 +224,16 @@ def __host_changed(self, hostname): self.__num_threads.setRange(1, host.data.idleCores) self.__num_mem.setRange(1, int(host.data.totalMemory / 1024 / 1024)) self.__num_threads.setRange(1, host.data.idleCores) - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def deedLocalhost(self): + """Deeds the target to the local host.""" show_name = os.environ.get("SHOW", "pipe") try: _show = opencue.api.findShow(show_name) - except Exception as e: + except opencue.exception.CueException as e: msg = QtWidgets.QMessageBox(self) msg.setText("Error %s, please setshot and rerun cuetopia", e) msg.exec_() @@ -243,7 +245,7 @@ def deedLocalhost(self): except opencue.EntityNotFoundException as e: # Owner does not exist owner = _show.createOwner(user) - + hostname = gethostname() try: host = opencue.api.findHost(hostname.rsplit(".",2)[0]) @@ -259,14 +261,14 @@ def deedLocalhost(self): self.__msg_widget = None self.hosts_changed.emit() - except Exception as e: + except opencue.exception.CueException: msg = QtWidgets.QMessageBox(self) msg.setText("Unable to determine your machine's hostname. " + "It is not setup properly for local booking") - msg.exec_() def __calculateCores(self, ignore): + del ignore frames = self.__num_frames.value() threads = self.__num_threads.value() @@ -284,16 +286,18 @@ def __hasError(self): if frames * threads > self.__num_frames.maximum(): return True - elif frames == 0: + if frames == 0: return True - elif cores % threads > 0: + if cores % threads > 0: return True - elif threads > cores: + if threads > cores: return True return False def clearCurrentHost(self): + """Clears the current host.""" + hostname = str(self.__select_host.currentText()) if not hostname: return @@ -305,23 +309,25 @@ def clearCurrentHost(self): rp = [r for r in host.getRenderPartitions() if r.job == self.jobName] if rp: rp = rp[0] - + rp.delete() - ## Wait for hosts to clear out, then switch - ## back to the booking widget - for i in range(0, 10): + # Wait for hosts to clear out, then switch back to the booking widget. + for _ in range(0, 10): + # pylint: disable=broad-except try: rp = [r for r in host.getRenderPartitions() if r.job == self.jobName][0] time.sleep(1) - except: + except Exception: break self.__host_changed(hostname) - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def bookCurrentHost(self): + """Books the current host.""" + if self.__hasError(): return @@ -333,17 +339,14 @@ def bookCurrentHost(self): int(self.__run_mem.value()) * 1024 * 1024, 0) else: - self.__target.addRenderPartition(str(self.__select_host.currentText()), - int(self.__num_threads.value()), - int(self.__num_cores.text()), - int(self.__num_mem.value() * 1048576), - 0) + self.__target.addRenderPartition( + str(self.__select_host.currentText()), int(self.__num_threads.value()), + int(self.__num_cores.text()), int(self.__num_mem.value() * 1048576), 0) class LocalBookingDialog(QtWidgets.QDialog): - """ - A dialog to wrap a LocalBookingWidget. Provides action buttons. - """ + """A dialog to wrap a LocalBookingWidget. Provides action buttons.""" + def __init__(self, target, parent=None): QtWidgets.QDialog.__init__(self, parent) QtWidgets.QVBoxLayout(self) @@ -372,6 +375,8 @@ def __updateOkButtion(self): self.__btn_ok.setDisabled(not self.__booking.hostAvailable()) def doLocalBooking(self): + """Performs the booking of local cores..""" + # pylint: disable=broad-except try: self.__booking.bookCurrentHost() self.close() @@ -382,4 +387,3 @@ def doLocalBooking(self): 'that your job has waiting frames.') msg.setDetailedText(str(e)) msg.exec_() - diff --git a/cuegui/cuegui/Main.py b/cuegui/cuegui/Main.py index 12575c82b..b2c540c74 100644 --- a/cuegui/cuegui/Main.py +++ b/cuegui/cuegui/Main.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Main entry point for the application. -""" +"""Main entry point for the application.""" from __future__ import absolute_import @@ -23,6 +21,8 @@ from __future__ import division import os +import shutil +import signal from PySide2 import QtCore from PySide2 import QtGui @@ -42,6 +42,7 @@ class CueGuiApplication(QtWidgets.QApplication): + """The CueGUI application.""" # Global signals display_log_file_content = QtCore.Signal(object) @@ -56,19 +57,20 @@ class CueGuiApplication(QtWidgets.QApplication): status = QtCore.Signal() quit = QtCore.Signal() - def __init__(self, *args, **kwargs): - super(CueGuiApplication, self).__init__(*args, **kwargs) - def cuetopia(argv): + """Starts the Cuetopia window.""" startup("Cuetopia", cuegui.Constants.VERSION, argv) def cuecommander(argv): + """Starts the CueCommander window.""" startup("CueCommander", cuegui.Constants.VERSION, argv) def startup(app_name, app_version, argv): + """Starts an application window.""" + app = CueGuiApplication(argv) # Start splash screen @@ -76,7 +78,6 @@ def startup(app_name, app_version, argv): app, app_name, app_version, cuegui.Constants.RESOURCE_PATH) # Allow ctrl-c to kill the application - import signal signal.signal(signal.SIGINT, signal.SIG_DFL) # Load window icon @@ -99,20 +100,21 @@ def startup(app_name, app_version, argv): cuegui.Style.init() - # If the config file does not exist, copy over the default + # If the config file does not exist, copy over the default + # pylint: disable=broad-except if not os.path.exists(local): default = os.path.join(cuegui.Constants.DEFAULT_INI_PATH, "%s.ini" % app_name.lower()) - logger.warning('Not found: %s\nCopying: %s' % (local, default)) + logger.warning('Not found: %s\nCopying: %s', local, default) try: os.mkdir(os.path.dirname(local)) except Exception as e: logger.debug(e) try: - import shutil shutil.copy2(default, local) except Exception as e: logger.debug(e) settings.sync() + # pylint: enable=broad-except mainWindow = cuegui.MainWindow.MainWindow(app_name, app_version, None) mainWindow.displayStartupNotice() @@ -125,10 +127,12 @@ def startup(app_name, app_version, argv): # End splash screen splash.hide() - + # TODO(#609) Refactor the CueGUI classes to make this garbage collector # replacement unnecessary. + # pylint: disable=unused-variable gc = cuegui.GarbageCollector.GarbageCollector(parent=app, debug=False) + # pylint: enable=unused-variable app.aboutToQuit.connect(closingTime) app.exec_() @@ -136,5 +140,8 @@ def startup(app_name, app_version, argv): def closingTime(): """Window close callback.""" logger.info("Closing all threads...") - for thread in QtGui.qApp.threads: + # pylint: disable=no-member + threads = QtGui.qApp.threads + # pylint: enable=no-member + for thread in threads: cuegui.Utils.shutdownThread(thread) diff --git a/cuegui/cuegui/MainWindow.py b/cuegui/cuegui/MainWindow.py index 50565e8c6..35891e2fd 100644 --- a/cuegui/cuegui/MainWindow.py +++ b/cuegui/cuegui/MainWindow.py @@ -13,9 +13,9 @@ # limitations under the License. -""" -All windows are an instance of this MainWindow. -""" +"""The main window of the application. Multiple windows may exist. + +All CueGUI windows are an instance of this MainWindow.""" from __future__ import absolute_import @@ -26,6 +26,7 @@ from builtins import str from builtins import range import sys +import time from PySide2 import QtCore from PySide2 import QtGui @@ -44,6 +45,7 @@ class MainWindow(QtWidgets.QMainWindow): """The main window of the application. Multiple windows may exist.""" + windows = [] windows_names = [] windows_titles = {} @@ -52,9 +54,15 @@ class MainWindow(QtWidgets.QMainWindow): def __init__(self, app_name, app_version, window_name, parent = None): QtWidgets.QMainWindow.__init__(self, parent) - # Setup variables + self.__actions_facility = {} + self.facility_default = None + self.facility_dict = None + self.windowMenu = None + self.qApp = QtGui.qApp + # pylint: disable=no-member self.settings = QtGui.qApp.settings + # pylint: enable=no-member self.windows_names = [app_name] + ["%s_%s" % (app_name, num) for num in range(2, 5)] self.app_name = app_name self.app_version = app_version @@ -80,18 +88,22 @@ def __init__(self, app_name, app_version, window_name, parent = None): self.__createMenus() # Setup plugins + # pylint: disable=no-member self.__plugins = cuegui.Plugins.Plugins(self, self.name) + # pylint: enable=no-member self.__plugins.setupPluginMenu(self.PluginMenu) # Restore saved settings self.__restoreSettings() + # pylint: disable=no-member QtGui.qApp.status.connect(self.showStatusBarMessage) + # pylint: enable=no-member self.showStatusBarMessage("Ready") def displayStartupNotice(self): - import time + """Displays the application startup notice.""" now = int(time.time()) lastView = int(self.settings.value("LastNotice", 0)) if lastView < cuegui.Constants.STARTUP_NOTICE_DATE: @@ -100,26 +112,35 @@ def displayStartupNotice(self): self.settings.setValue("LastNotice", now) def showStatusBarMessage(self, message, delay=5000): + """Shows a message on the status bar.""" self.statusBar().showMessage(str(message), delay) def displayAbout(self): + """Displays about text.""" msg = self.app_name + "\n\nA opencue tool\n\n" msg += "Qt:\n%s\n\n" % QtCore.qVersion() msg += "Python:\n%s\n\n" % sys.version QtWidgets.QMessageBox.about(self, "About", msg) - def openSuggestionPage(self): + @staticmethod + def openSuggestionPage(): + """Opens the suggestion page URL.""" cuegui.Utils.openURL(cuegui.Constants.URL_SUGGESTION) - def openBugPage(self): + @staticmethod + def openBugPage(): + """Opens the bug report page.""" cuegui.Utils.openURL(cuegui.Constants.URL_BUG) - def openUserGuide(self): + @staticmethod + def openUserGuide(): + """Opens the user guide page.""" cuegui.Utils.openURL(cuegui.Constants.URL_USERGUIDE) -################################################################################ -# Handles facility menu -################################################################################ + ################################################################################ + # Handles facility menu + ################################################################################ + def __facilityMenuSetup(self, menu): """Creates the facility menu actions @param menu: The QMenu that the actions should be added to @@ -163,10 +184,12 @@ def __facilityMenuHandle(self, action): for facility in list(self.__actions_facility.values()): if facility.isChecked(): opencue.Cuebot.setFacility(str(facility.text())) + # pylint: disable=no-member QtGui.qApp.facility_changed.emit() + # pylint: enable=no-member return -################################################################################ + ################################################################################ def __createMenus(self): """Creates the menus at the top of the window""" @@ -186,11 +209,11 @@ def __createMenus(self): self.fileMenu.addAction(close) # Menu Bar: File -> Exit Application - exit = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), 'E&xit Application', self) - exit.setShortcut('Ctrl+Q') - exit.setStatusTip('Exit application') - exit.triggered.connect(self.__windowCloseApplication) - self.fileMenu.addAction(exit) + exitAction = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), 'E&xit Application', self) + exitAction.setShortcut('Ctrl+Q') + exitAction.setStatusTip('Exit application') + exitAction.triggered.connect(self.__windowCloseApplication) + self.fileMenu.addAction(exitAction) self.__windowMenuSetup(self.windowMenu) @@ -222,9 +245,9 @@ def __createMenus(self): about.triggered.connect(self.displayAbout) self.helpMenu.addAction(about) -################################################################################ -# Handles adding windows -################################################################################ + ################################################################################ + # Handles adding windows + ################################################################################ def __windowMenuSetup(self, menu): """Creates the menu items for dealing with multiple main windows""" @@ -336,19 +359,26 @@ def windowMenuOpenWindow(self, name): def __windowOpened(self): """Called from __init__ on window creation""" + # pylint: disable=no-member self.qApp.quit.connect(self.close) self.windows.append(self) self.qApp.closingApp = False + # pylint: enable=no-member def __windowClosed(self): """Called from closeEvent on window close""" # Disconnect to avoid multiple attempts to close a window + # pylint: disable=no-member self.qApp.quit.connect(self.close) + # pylint: enable=no-member # Save the fact that this window is open or not when the app closed + # pylint: disable=no-member self.settings.setValue("%s/Open" % self.name, self.qApp.closingApp) + # pylint: enable=no-member + # pylint: disable=bare-except try: self.windows.remove(self) except: @@ -362,14 +392,17 @@ def __windowCloseWindow(self): def __windowCloseApplication(self): """Called when the entire application should exit. Signals other windows to exit.""" + # pylint: disable=no-member self.qApp.closingApp = True self.qApp.quit.emit() + # pylint: enable=no-member -################################################################################ + ################################################################################ def __toggleFullscreenSetup(self, menu): # Menu Bar: Window -> Toggle Full-Screen - fullscreen = QtWidgets.QAction(QtGui.QIcon('icons/fullscreen.png'), 'Toggle Full-Screen', self) + fullscreen = QtWidgets.QAction( + QtGui.QIcon('icons/fullscreen.png'), 'Toggle Full-Screen', self) fullscreen.setShortcut('Ctrl+F') fullscreen.setStatusTip('Toggle Full-Screen') fullscreen.triggered.connect(self.__toggleFullscreen) @@ -383,16 +416,20 @@ def __toggleFullscreen(self): else: self.showFullScreen() -################################################################################ + ################################################################################ + def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Space: + # pylint: disable=no-member QtGui.qApp.request_update.emit() + # pylint: enable=no-member event.accept() def closeEvent(self, event): """Called when the window is closed @type event: QEvent @param event: The close event""" + del event self.__saveSettings() self.__windowClosed() @@ -411,7 +448,7 @@ def __restoreSettings(self): def __saveSettings(self): """Saves the windows settings""" - logger.info('Saving: %s' % self.settings.fileName()) + logger.info('Saving: %s', self.settings.fileName()) self.__plugins.saveState() @@ -427,7 +464,7 @@ def __saveSettings(self): self.size()) self.settings.setValue("%s/Position" % self.name, self.pos()) - + def __revertLayout(self): """Revert back to default window layout""" result = QtWidgets.QMessageBox.question( diff --git a/cuegui/cuegui/MenuActions.py b/cuegui/cuegui/MenuActions.py index 8f6fac07f..5c9b63139 100644 --- a/cuegui/cuegui/MenuActions.py +++ b/cuegui/cuegui/MenuActions.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Provides actions and functions for right click menu items. -""" +"""Provides actions and functions for right click menu items.""" from __future__ import absolute_import @@ -38,6 +36,7 @@ import opencue import opencue.compiled_proto.job_pb2 +# pylint: disable=cyclic-import import cuegui.Action import cuegui.Comments import cuegui.Constants @@ -64,11 +63,15 @@ TOOLTIP = 1 ICON = 2 -# New icons are here: /usr/share/icons/crystalsvg/16x16 + +# pylint: disable=missing-function-docstring,no-self-use,unused-argument class AbstractActions(object): + """Parent class for all job-specific actions classes.""" + __iconCache = {} + def __init__(self, caller, updateCallable, selectedRpcObjectsCallable, sourceCallable): self._caller = caller self.__selectedRpcObjects = selectedRpcObjectsCallable @@ -109,13 +112,15 @@ def _getOnlyProcObjects(self, rpcObjects): def _getOnlyTaskObjects(self, rpcObjects): return list(filter(cuegui.Utils.isTask, self._getSelected(rpcObjects))) - def createAction(self, menu, title, tip = None, callback = None, icon = None): + def createAction(self, menu, title, tip=None, callback=None, icon=None): + """Creates a context menu action.""" if not tip: tip = title menu.addAction(cuegui.Action.create(menu, title, tip, callback, icon)) def addAction(self, menu, actionName, callback = None): - """Adds the requested menu item to the menu + """Adds the requested menu item to the menu. + @param menu: The menu that the action will be added to @type menu: QMenu @param actionName: The name of the action to add to the menu @@ -139,6 +144,7 @@ def addAction(self, menu, actionName, callback = None): else: icon_key = info[ICON] if icon_key not in self.__iconCache: + # pylint: disable=unidiomatic-typecheck if type(info[ICON]) is QtGui.QColor: pixmap = QtGui.QPixmap(100, 100) pixmap.fill(info[ICON]) @@ -158,30 +164,31 @@ def addAction(self, menu, actionName, callback = None): menu.addAction(self.__actionCache[key]) - def cuebotCall(self, callable, errorMessageTitle, *args): - """Makes the given call to the cuebot and if an exception occurs display - a critical message box. - @type callable: function - @param callable: The cuebot function to call. + def cuebotCall(self, functionToCall, errorMessageTitle, *args): + """Makes the given call to the Cuebot, displaying exception info if needed. + + @type functionToCall: function + @param functionToCall: The cuebot function to call. @type errorMessageTitle: string @param errorMessageTitle: The text to display in the title of the error message box. - @type callable: Variable arguments - @param callable: The arguments to pass to the callable + @type args: list + @param args: The arguments to pass to the callable @rtype: callable return type @return: Returns any results from the callable or None on exception""" try: - return callable(*args) - except Exception as e: + return functionToCall(*args) + except opencue.exception.CueException as e: logger.exception('Failed Cuebot call') QtWidgets.QMessageBox.critical(self._caller, errorMessageTitle, - e.details(), + str(e), QtWidgets.QMessageBox.Ok) return None def getText(self, title, body, default): """Prompts the user for text input. + @type title: string @param title: The title to display in the input dialog @type body: string @@ -190,40 +197,46 @@ def getText(self, title, body, default): @param default: The default text to provide in the input dialog @rtype: tuple(str, bool) @return: (input, choice)""" - (input, choice) = QtWidgets.QInputDialog.getText(self._caller, - title, - body, - QtWidgets.QLineEdit.Normal, - default) - return str(input), choice + (user_input, choice) = QtWidgets.QInputDialog.getText( + self._caller, title, body, QtWidgets.QLineEdit.Normal, default) + return str(user_input), choice class JobActions(AbstractActions): + """Actions for jobs.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) unmonitor_info = ["Unmonitor", "Unmonitor selected jobs", "eject"] + def unmonitor(self, rpcObjects=None): self._caller.actionRemoveSelectedItems() view_info = ["View Job", None, "view"] + def view(self, rpcObjects=None): for job in self._getOnlyJobObjects(rpcObjects): + # pylint: disable=no-member QtGui.qApp.view_object.emit(job) + # pylint: enable=no-member viewDepends_info = ["&View Dependencies...", None, "log"] + def viewDepends(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) cuegui.DependDialog.DependDialog(jobs[0], self._caller).show() emailArtist_info = ["Email Artist...", None, "mail"] + def emailArtist(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: - cuegui.EmailDialog.EmailDialog(jobs[0], [], self._caller).show() + cuegui.EmailDialog.EmailDialog(jobs[0], self._caller).show() setMinCores_info = ["Set Minimum Cores...", "Set Job(s) Minimum Cores", "configure"] + def setMinCores(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -240,6 +253,7 @@ def setMinCores(self, rpcObjects=None): self._update() setMaxCores_info = ["Set Maximum Cores...", "Set Job(s) Maximum Cores", "configure"] + def setMaxCores(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -256,6 +270,7 @@ def setMaxCores(self, rpcObjects=None): self._update() setPriority_info = ["Set Priority...", None, "configure"] + def setPriority(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -272,11 +287,13 @@ def setPriority(self, rpcObjects=None): self._update() setMaxRetries_info = ["Set Max Retries...", None, "configure"] + def setMaxRetries(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: title = "Set Max Retries" - body = "Please enter the number of retries that a frame should be allowed before it becomes dead:" + body = ('Please enter the number of retries that a frame should be ' + 'allowed before it becomes dead:') (value, choice) = QtWidgets.QInputDialog.getInt(self._caller, title, body, 0, 0, 10, 1) @@ -286,6 +303,7 @@ def setMaxRetries(self, rpcObjects=None): self._update() pause_info = ["&Pause", None, "pause"] + def pause(self, rpcObjects=None): """pause selected jobs""" jobs = self._getOnlyJobObjects(rpcObjects) @@ -295,6 +313,7 @@ def pause(self, rpcObjects=None): self._update() resume_info = ["&Unpause", None, "unpause"] + def resume(self, rpcObjects=None): """resume selected jobs""" jobs = self._getOnlyJobObjects(rpcObjects) @@ -304,6 +323,7 @@ def resume(self, rpcObjects=None): self._update() kill_info = ["&Kill", None, "kill"] + def kill(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -315,6 +335,7 @@ def kill(self, rpcObjects=None): self._update() eatDead_info = ["Eat dead frames", None, "eat"] + def eatDead(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -326,6 +347,7 @@ def eatDead(self, rpcObjects=None): self._update() autoEatOn_info = ["Enable auto eating", None, "eat"] + def autoEatOn(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -335,6 +357,7 @@ def autoEatOn(self, rpcObjects=None): self._update() autoEatOff_info = ["Disable auto eating", None, "eat"] + def autoEatOff(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -343,6 +366,7 @@ def autoEatOff(self, rpcObjects=None): self._update() retryDead_info = ["Retry dead frames", None, "retry"] + def retryDead(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -355,6 +379,7 @@ def retryDead(self, rpcObjects=None): self._update() dropExternalDependencies_info = ["Drop External Dependencies", None, "kill"] + def dropExternalDependencies(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -366,6 +391,7 @@ def dropExternalDependencies(self, rpcObjects=None): self._update() dropInternalDependencies_info = ["Drop Internal Dependencies", None, "kill"] + def dropInternalDependencies(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -377,12 +403,14 @@ def dropInternalDependencies(self, rpcObjects=None): self._update() viewComments_info = ["Comments...", None, "comment"] + def viewComments(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: cuegui.Comments.CommentListDialog(jobs[0], self._caller).show() dependWizard_info = ["Dependency &Wizard...", None, "configure"] + def dependWizard(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -399,58 +427,60 @@ def __getJobRange(self, job): return (min(__minRange), max(__maxRange)) reorder_info = ["Reorder Frames...", None, "configure"] + def reorder(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) - if not jobs: return + if not jobs: + return __job = jobs[0] (__minRange, __maxRange) = self.__getJobRange(__job) title = "Reorder %s" % __job.data.name body = "What frame range should be reordered?" - (range, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) - if not choice: return + (frame_range, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) + if not choice: + return - body = "What order should the range %s take?" % range + body = "What order should the range %s take?" % frame_range items = list(opencue.compiled_proto.job_pb2.Order.keys()) - (order, choice) = QtWidgets.QInputDialog.getItem(self._caller, - title, - body, - sorted(items), - 0, - False) + (order, choice) = QtWidgets.QInputDialog.getItem( + self._caller, title, body, sorted(items), 0, False) if not choice: return self.cuebotCall( __job.reorderFrames, "Reorder Frames Failed", - range, getattr(opencue.compiled_proto.job_pb2, str(order))) + frame_range, getattr(opencue.compiled_proto.job_pb2, str(order))) stagger_info = ["Stagger Frames...", None, "configure"] + def stagger(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) - if not jobs: return + if not jobs: + return __job = jobs[0] (__minRange, __maxRange) = self.__getJobRange(__job) title = "Stagger %s" % __job.data.name body = "What frame range should be staggered?" - (range, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) - if not choice: return + (frameRange, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) + if not choice: + return - body = "What increment should the range %s be staggered?" % range - (increment, choice) = QtWidgets.QInputDialog.getInt(self._caller, - title, body, - 1, - 1, 100000, 1) + body = "What increment should the range %s be staggered?" % frameRange + (increment, choice) = QtWidgets.QInputDialog.getInt( + self._caller, title, body, 1, 1, 100000, 1) - if not choice: return + if not choice: + return self.cuebotCall(__job.staggerFrames, "Stagger Frames Failed", - range, int(increment)) + frameRange, int(increment)) unbook_info = ["Unbook Frames...", None, "kill"] + def unbook(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: @@ -459,33 +489,28 @@ def unbook(self, rpcObjects=None): self._update() sendToGroup_info = ["Send To Group...", None, "configure"] + def sendToGroup(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if not jobs: return title = "Send jobs to group" - groups = dict([(group.data.name, group) for group in opencue.api.findShow( - jobs[0].data.show).getGroups()]) + groups = { + group.data.name: group for group in opencue.api.findShow(jobs[0].data.show).getGroups()} body = "What group should these jobs move to?\n" + \ "\n".join([job.data.name for job in jobs]) - (group, choice) = QtWidgets.QInputDialog.getItem(self._caller, - title, - body, - sorted(groups.keys()), - 0, - False) + (group, choice) = QtWidgets.QInputDialog.getItem( + self._caller, title, body, sorted(groups.keys()), 0, False) if not choice: return groups[str(group)].reparentJobs(jobs) self._update() - - useLocalCores_info = ["Use local cores...", - "Set a single job to use the local desktop cores", - "configure"] + useLocalCores_info = [ + "Use local cores...", "Set a single job to use the local desktop cores", "configure"] def useLocalCores(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) @@ -495,56 +520,73 @@ def useLocalCores(self, rpcObjects=None): dialog.exec_() copyLogFileDir_info = ["Copy log file directory", None, "configure"] + def copyLogFileDir(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: paths = [job.data.log_dir for job in jobs] - QtWidgets.QApplication.clipboard().setText(" ".join(paths), - QtGui.QClipboard.Clipboard) + QtWidgets.QApplication.clipboard().setText( + " ".join(paths), QtGui.QClipboard.Clipboard) + + setUserColor1_info = [ + "Set Color 1", "Set user defined background color", cuegui.Constants.COLOR_USER_1] - setUserColor1_info = ["Set Color 1", "Set user defined background color", cuegui.Constants.COLOR_USER_1] def setUserColor1(self, rpcObjects=None): self._caller.actionSetUserColor(cuegui.Constants.COLOR_USER_1) - setUserColor2_info = ["Set Color 2", "Set user defined background color", cuegui.Constants.COLOR_USER_2] + setUserColor2_info = [ + "Set Color 2", "Set user defined background color", cuegui.Constants.COLOR_USER_2] + def setUserColor2(self, rpcObjects=None): self._caller.actionSetUserColor(cuegui.Constants.COLOR_USER_2) - setUserColor3_info = ["Set Color 3", "Set user defined background color", cuegui.Constants.COLOR_USER_3] + setUserColor3_info = [ + "Set Color 3", "Set user defined background color", cuegui.Constants.COLOR_USER_3] + def setUserColor3(self, rpcObjects=None): self._caller.actionSetUserColor(cuegui.Constants.COLOR_USER_3) - setUserColor4_info = ["Set Color 4", "Set user defined background color", cuegui.Constants.COLOR_USER_4] + setUserColor4_info = [ + "Set Color 4", "Set user defined background color", cuegui.Constants.COLOR_USER_4] + def setUserColor4(self, rpcObjects=None): self._caller.actionSetUserColor(cuegui.Constants.COLOR_USER_4) clearUserColor_info = ["Clear", "Clear user defined background color", None] + def clearUserColor(self, rpcObjects=None): self._caller.actionSetUserColor(None) class LayerActions(AbstractActions): + """Actions for layers.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) view_info = ["View Layer", None, "view"] + def view(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: self._caller.handle_filter_layers_byLayer.emit([layer.data.name for layer in layers]) viewDepends_info = ["&View Dependencies...", None, "log"] + def viewDepends(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) cuegui.DependDialog.DependDialog(layers[0], self._caller).show() - setMinCores_info = ["Set Minimum Cores", "Set the number of cores required for this layer", "configure"] + setMinCores_info = [ + "Set Minimum Cores", "Set the number of cores required for this layer", "configure"] + def setMinCores(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: current = max([layer.data.min_cores for layer in layers]) title = "Set minimum number of cores required" - body = "Please enter the new minimum number of cores that frames in the selected layer(s) should require:" + body = ('Please enter the new minimum number of cores that frames in the ' + 'selected layer(s) should require:') (value, choice) = QtWidgets.QInputDialog.getDouble(self._caller, title, body, current, @@ -554,13 +596,16 @@ def setMinCores(self, rpcObjects=None): layer.setMinCores(float(value)) self._update() - setMinMemoryKb_info = ["Set Minimum Memory", "Set the amount of memory required for this layer", "configure"] + setMinMemoryKb_info = [ + "Set Minimum Memory", "Set the amount of memory required for this layer", "configure"] + def setMinMemoryKb(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: current = max([layer.data.min_memory / 1048576 for layer in layers]) title = "Set minimum amount of memory required" - body = "Please enter the new minimum amount of memory in GB that frames in the selected layer(s) should require:" + body = ('Please enter the new minimum amount of memory in GB that frames ' + 'in the selected layer(s) should require:') (value, choice) = QtWidgets.QInputDialog.getDouble( self._caller, title, body, current, 0.01, 64.0, 1) if choice: @@ -568,9 +613,8 @@ def setMinMemoryKb(self, rpcObjects=None): layer.setMinMemory(int(value * 1048576)) self._update() - useLocalCores_info = ["Use local cores...", - "Set a single layer to use the local desktop cores.", - "configure"] + useLocalCores_info = [ + "Use local cores...", "Set a single layer to use the local desktop cores.", "configure"] def useLocalCores(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) @@ -579,8 +623,8 @@ def useLocalCores(self, rpcObjects=None): dialog = cuegui.LocalBooking.LocalBookingDialog(layer, self._caller) dialog.exec_() - setProperties_info = ["Properties", None, "configure"] + def setProperties(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -589,6 +633,7 @@ def setProperties(self, rpcObjects=None): self._update() setTags_info = ["Set Tags", None, "configure"] + def setTags(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -597,6 +642,7 @@ def setTags(self, rpcObjects=None): self._update() kill_info = ["&Kill", None, "kill"] + def kill(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -608,6 +654,7 @@ def kill(self, rpcObjects=None): self._update() eat_info = ["&Eat", None, "eat"] + def eat(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -619,6 +666,7 @@ def eat(self, rpcObjects=None): self._update() retry_info = ["&Retry", None, "retry"] + def retry(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -630,6 +678,7 @@ def retry(self, rpcObjects=None): self._update() retryDead_info = ["Retry dead frames", None, "retry"] + def retryDead(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -641,6 +690,7 @@ def retryDead(self, rpcObjects=None): self._update() markdone_info = ["Mark done", None, "markdone"] + def markdone(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: @@ -652,18 +702,22 @@ def markdone(self, rpcObjects=None): self._update() dependWizard_info = ["Dependency &Wizard...", None, "configure"] + def dependWizard(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: - cuegui.DependWizard.DependWizard(self._caller, [self._getSource()], layers) + cuegui.DependWizard.DependWizard(self._caller, [self._getSource()], layers=layers) reorder_info = ["Reorder Frames...", None, "configure"] + def reorder(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) - if not layers: return + if not layers: + return # Only allow multiple layers with the same range - if len(set([layer.data.range for layer in layers])) != 1: return + if len({layer.data.range for layer in layers}) != 1: + return __layer = layers[0] fs = FileSequence.FrameSet(__layer.data.range) @@ -679,29 +733,27 @@ def reorder(self, rpcObjects=None): else: title = "Reorder layer %s" % __layer.data.name - (range, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) + (frameRange, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) if not choice: return - body = "What order should the range %s take?" % range + body = "What order should the range %s take?" % frameRange items = list(opencue.compiled_proto.job_pb2.Order.keys()) - (order, choice) = QtWidgets.QInputDialog.getItem(self._caller, - title, - body, - sorted(items), - 0, - False) + (order, choice) = QtWidgets.QInputDialog.getItem( + self._caller, title, body, sorted(items), 0, False) if not choice: return for layer in layers: self.cuebotCall(layer.reorderFrames, "Reorder Frames Failed", - range, getattr(opencue.compiled_proto.job_pb2, str(order))) + frameRange, getattr(opencue.compiled_proto.job_pb2, str(order))) stagger_info = ["Stagger Frames...", None, "configure"] + def stagger(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) - if not layers: return + if not layers: + return __layer = layers[0] fs = FileSequence.FrameSet(__layer.data.range) @@ -711,26 +763,28 @@ def stagger(self, rpcObjects=None): title = "Stagger %s" % __layer.data.name body = "What frame range should be staggered?" - (range, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) - if not choice: return - - body = "What increment should the range %s be staggered?" % range - (increment, choice) = QtWidgets.QInputDialog.getInt(self._caller, - title, body, - 1, - 1, 100000, 1) + (frameRange, choice) = self.getText(title, body, "%s-%s" % (__minRange, __maxRange)) + if not choice: + return - if not choice: return + body = "What increment should the range %s be staggered?" % frameRange + (increment, choice) = QtWidgets.QInputDialog.getInt( + self._caller, title, body, 1, 1, 100000, 1) + if not choice: + return self.cuebotCall(__layer.staggerFrames, "Stagger Frames Failed", - range, int(increment)) + frameRange, int(increment)) class FrameActions(AbstractActions): + """Actions for frames.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) view_info = ["&View Log", None, "log"] + def view(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: @@ -742,6 +796,7 @@ def view(self, rpcObjects=None): cuegui.Utils.popupFrameView(job, frame) tail_info = ["&Tail Log", None, "log"] + def tail(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: @@ -753,12 +808,14 @@ def tail(self, rpcObjects=None): cuegui.Utils.popupFrameTail(job, frame) viewLastLog_info = ["View Last Log", None, "loglast"] + def viewLastLog(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: job = self._getSource() path = cuegui.Utils.getFrameLogFile(job, frames[0]) - files = dict((int(j.split(".")[-1]), j) for j in glob.glob("%s.*" % path) if j[-1].isdigit()) + files = dict( + (int(j.split(".")[-1]), j) for j in glob.glob("%s.*" % path) if j[-1].isdigit()) if files: cuegui.Utils.popupView(files[sorted(files.keys())[-1]]) else: @@ -776,57 +833,71 @@ def useLocalCores(self, rpcObjects=None): dialog.exec_() xdiff2_info = ["View xdiff of 2 logs", None, "log"] + def xdiff2(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if len(frames) >= 2: cuegui.Utils.popupFrameXdiff(self._getSource(), frames[0], frames[1]) xdiff3_info = ["View xdiff of 3 logs", None, "log"] + def xdiff3(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if len(frames) >= 3: cuegui.Utils.popupFrameXdiff(self._getSource(), frames[0], frames[1], frames[2]) viewHost_info = ["View Host", None, "log"] + def viewHost(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) - hosts = list(set([frame.data.last_resource.split("/")[0] for frame in frames if frame.data.last_resource])) + hosts = list({frame.data.last_resource.split("/")[0] + for frame in frames if frame.data.last_resource}) if hosts: + # pylint: disable=no-member QtGui.qApp.view_hosts.emit(hosts) QtGui.qApp.single_click.emit(opencue.api.findHost(hosts[0])) + # pylint: enable=no-member getWhatThisDependsOn_info = ["print getWhatThisDependsOn", None, "log"] + def getWhatThisDependsOn(self, rpcObjects=None): frame = self._getOnlyFrameObjects(rpcObjects)[0] - logger.info("type", "target", "anyFrame", "active", "dependErJob", "dependErLayer", "dependErFrame", "dependOnJob", "dependOnLayer", "dependOnFrame") for item in frame.getWhatThisDependsOn(): logger.info(item.data.type, item.data.target, item.data.any_frame, item.data.active) - logger.info("This:", item.data.depend_er_job, item.data.depend_er_layer, item.data.depend_er_frame, - "On:", item.data.depend_on_job, item.data.depend_on_layer, item.data.depend_on_frame) + logger.info( + "This: %s %s %s", item.data.depend_er_job, item.data.depend_er_layer, + item.data.depend_er_frame) + logger.info( + "On: %s %s %s", item.data.depend_on_job, item.data.depend_on_layer, + item.data.depend_on_frame) viewDepends_info = ["&View Dependencies...", None, "log"] + def viewDepends(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) cuegui.DependDialog.DependDialog(frames[0], self._caller).show() getWhatDependsOnThis_info = ["print getWhatDependsOnThis", None, "log"] + def getWhatDependsOnThis(self, rpcObjects=None): frame = self._getOnlyFrameObjects(rpcObjects)[0] logger.info(frame.getWhatDependsOnThis()) retry_info = ["&Retry", None, "retry"] + def retry(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: job = self._getSource() - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Retry selected frames?", - names): + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Retry selected frames?", names): job.retryFrames(name=names) self._update() previewMain_info = ["Preview Main", None, "previewMain"] + + # pylint: disable=broad-except def previewMain(self, rpcObjects=None): try: job = self._getSource() @@ -839,6 +910,8 @@ def previewMain(self, rpcObjects=None): "Error displaying preview frames, %s" % e) previewAovs_info = ["Preview All", None, "previewAovs"] + + # pylint: disable=broad-except def previewAovs(self, rpcObjects=None): try: job = self._getSource() @@ -850,6 +923,7 @@ def previewAovs(self, rpcObjects=None): QtWidgets.QMessageBox.critical(None, "Preview Error", "Error displaying preview frames, %s" % e) eat_info = ["&Eat", None, "eat"] + def eat(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: @@ -860,6 +934,7 @@ def eat(self, rpcObjects=None): self._update() kill_info = ["&Kill", None, "kill"] + def kill(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: @@ -870,6 +945,7 @@ def kill(self, rpcObjects=None): self._update() markAsWaiting_info = ["Mark as &waiting", None, "configure"] + def markAsWaiting(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: @@ -881,6 +957,7 @@ def markAsWaiting(self, rpcObjects=None): self._update() dropDepends_info = ["D&rop depends", None, "configure"] + def dropDepends(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) names = [frame.data.name for frame in frames] @@ -894,43 +971,44 @@ def dropDepends(self, rpcObjects=None): self._update() dependWizard_info = ["Dependency &Wizard...", None, "configure"] + def dependWizard(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: - cuegui.DependWizard.DependWizard(self._caller, [self._getSource()], [], frames) + cuegui.DependWizard.DependWizard(self._caller, [self._getSource()], frames=frames) markdone_info = ["Mark done", None, "markdone"] + def markdone(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: frameNames = [frame.data.name for frame in frames] - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Mark done all selected frames?\n" - "(Drops any dependencies that are waiting on these frames)", - frameNames): + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", + 'Mark done all selected frames?\n' + '(Drops any dependencies that are waiting on these frames)', frameNames): self._getSource().markdoneFrames(name=frameNames) self._update() reorder_info = ["Reorder...", None, "configure"] + def reorder(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) - if not frames: return + if not frames: + return __job = self._getSource() title = "Reorder %s" % __job.data.name body = "How should these frames be reordered?" items = list(opencue.compiled_proto.job_pb2.Order.keys()) - (order, choice) = QtWidgets.QInputDialog.getItem(self._caller, - title, - body, - sorted(items), - 0, - False) - if not choice: return + (order, choice) = QtWidgets.QInputDialog.getItem( + self._caller, title, body, sorted(items), 0, False) + if not choice: + return # Store the proxy and a place for the frame numbers keyed to the layer name - __layersDict = dict([(layer.data.name, (layer, [])) for layer in __job.getLayers()]) + __layersDict = {layer.data.name: (layer, []) for layer in __job.getLayers()} # For each frame, store the number in the list for that layer for frame in frames: @@ -948,27 +1026,31 @@ def reorder(self, rpcObjects=None): getattr(opencue.compiled_proto.job_pb2, str(order))) copyLogFileName_info = ["Copy log file name", None, "configure"] + def copyLogFileName(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) - if not frames: return + if not frames: + return job = self._getSource() paths = [cuegui.Utils.getFrameLogFile(job, frame) for frame in frames] QtWidgets.QApplication.clipboard().setText(paths, QtGui.QClipboard.Clipboard) eatandmarkdone_info = ["Eat and Mark done", None, "eatandmarkdone"] + def eatandmarkdone(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: frameNames = [frame.data.name for frame in frames] - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Eat and Mark done all selected frames?\n" - "(Drops any dependencies that are waiting on these frames)\n\n" - "If a frame is part of a layer that will now only contain\n" - "eaten or succeeded frames, any dependencies on the\n" - "layer will be dropped as well.", - frameNames): + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", + "Eat and Mark done all selected frames?\n" + "(Drops any dependencies that are waiting on these frames)\n\n" + "If a frame is part of a layer that will now only contain\n" + "eaten or succeeded frames, any dependencies on the\n" + "layer will be dropped as well.", + frameNames): # Mark done the layers to drop their dependencies if the layer is done @@ -989,17 +1071,23 @@ def eatandmarkdone(self, rpcObjects=None): # Warning: The below assumes that eaten frames are desired to be markdone - # Wait for the markDoneFrames to be processed, then drop the dependencies on the layer if all frames are done + # Wait for the markDoneFrames to be processed, then drop the dependencies on + # the layer if all frames are done. layerNames = [frame.data.layer_name for frame in frames] time.sleep(1) for layer in self._getSource().getLayers(): if layer.data.name in layerNames: - if layer.data.layer_stats.eaten_frames + layer.data.layer_stats.succeeded_frames == layer.data.layer_stats.total_frames: + if ( + layer.data.layer_stats.eaten_frames + + layer.data.layer_stats.succeeded_frames == + layer.data.layer_stats.total_frames): layer.markdone() self._update() class ShowActions(AbstractActions): + """Actions for shows.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1011,7 +1099,8 @@ def properties(self, rpcObjects=None): createSubscription_info = ["Create Subscription...", None, "configure"] def createSubscription(self, rpcObjects=None): - d = cuegui.CreatorDialog.SubscriptionCreatorDialog(show=self._getOnlyShowObjects(rpcObjects)[0]) + d = cuegui.CreatorDialog.SubscriptionCreatorDialog( + show=self._getOnlyShowObjects(rpcObjects)[0]) d.exec_() viewTasks_info = ["View Tasks...", None, "view"] @@ -1022,6 +1111,8 @@ def viewTasks(self, rpcObjects=None): class RootGroupActions(AbstractActions): + """"Actions for root groups.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1029,7 +1120,8 @@ def __init__(self, *args): def properties(self, rpcObjects=None): rootgroups = self._getOnlyRootGroupObjects(rpcObjects) if rootgroups: - cuegui.ShowDialog.ShowDialog(opencue.api.findShow(rootgroups[0].data.name), self._caller).show() + cuegui.ShowDialog.ShowDialog( + opencue.api.findShow(rootgroups[0].data.name), self._caller).show() groupProperties_info = ["Group Properties...", None, "view"] def groupProperties(self, rpcObjects=None): @@ -1048,7 +1140,8 @@ def setCuewho(self, rpcObjects=None): (name, choice) = self.getText(title, body, cuegui.Utils.getUsername()) if choice: for rootgroup in rootgroups: - logger.info(subprocess.check_output("cuewho -s %s -who %s" % (rootgroup.data.name, name))) + logger.info(subprocess.check_output( + "cuewho -s %s -who %s" % (rootgroup.data.name, name))) showCuewho_info = ["Display Cuewho", None, "configure"] def showCuewho(self, rpcObjects=None): @@ -1058,11 +1151,9 @@ def showCuewho(self, rpcObjects=None): for rootgroup in rootgroups: cuewho = cuegui.Utils.getCuewho(rootgroup.data.name) extension = cuegui.Utils.getExtension(cuewho) - message.append("Cuewho for %s is: %s %s" % (rootgroup.data.name, cuewho, extension )) - QtWidgets.QMessageBox.information(self._caller, - "Show Cuewho", - '\n'.join(message), - QtWidgets.QMessageBox.Ok) + message.append("Cuewho for %s is: %s %s" % (rootgroup.data.name, cuewho, extension)) + QtWidgets.QMessageBox.information( + self._caller, "Show Cuewho", '\n'.join(message), QtWidgets.QMessageBox.Ok) createGroup_info = ["Create Group...", None, "configure"] def createGroup(self, rpcObjects=None): @@ -1092,6 +1183,8 @@ def serviceProperties(self, rpcObjects=None): class GroupActions(AbstractActions): + """Actions for groups.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1123,6 +1216,8 @@ def deleteGroup(self, rpcObjects=None): class SubscriptionActions(AbstractActions): + """Actions for subscriptions.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1137,9 +1232,9 @@ def editSize(self, rpcObjects=None): "contact the resource department." minSize = 0 decimalPlaces = 0 - (value, choice) = QtWidgets.QInputDialog.getDouble(self._caller, title, body, current/100.0, - minSize, cuegui.Constants.QT_MAX_INT, - decimalPlaces) + (value, choice) = QtWidgets.QInputDialog.getDouble( + self._caller, title, body, current/100.0, minSize, cuegui.Constants.QT_MAX_INT, + decimalPlaces) if choice: msg = QtWidgets.QMessageBox() msg.setText( @@ -1166,9 +1261,9 @@ def editBurst(self, rpcObjects=None): "subscription should be allowed to reach:" minSize = 0 decimalPlaces = 0 - (value, choice) = QtWidgets.QInputDialog.getDouble(self._caller, title, body, current/100.0, - minSize, cuegui.Constants.QT_MAX_INT, - decimalPlaces) + (value, choice) = QtWidgets.QInputDialog.getDouble( + self._caller, title, body, current/100.0, minSize, cuegui.Constants.QT_MAX_INT, + decimalPlaces) if choice: for sub in subs: self.cuebotCall(sub.setBurst, @@ -1190,28 +1285,37 @@ def delete(self, rpcObjects=None): class AllocationActions(AbstractActions): + """Actions for allocations.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) class HostActions(AbstractActions): + """Actions for hosts.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) viewComments_info = ["Comments...", None, "comment"] + def viewComments(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) if hosts: cuegui.Comments.CommentListDialog(hosts[0], self._caller).show() viewProc_info = ["View Procs", None, "log"] + def viewProc(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) - hosts = list(set([host.data.name for host in hosts])) + hosts = list({host.data.name for host in hosts}) if hosts: + # pylint: disable=no-member QtGui.qApp.view_procs.emit(hosts) + # pylint: enable=no-member lock_info = ["Lock Host", None, "lock"] + def lock(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) for host in hosts: @@ -1219,6 +1323,7 @@ def lock(self, rpcObjects=None): self._update() unlock_info = ["Unlock Host", None, "lock"] + def unlock(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) for host in hosts: @@ -1226,6 +1331,7 @@ def unlock(self, rpcObjects=None): self._update() delete_info = ["Delete Host", "Delete host from cuebot", "kill"] + def delete(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) title = "Confirm" @@ -1244,6 +1350,7 @@ def delete(self, rpcObjects=None): self._update() rebootWhenIdle_info = ["Reboot when idle", None, "retry"] + def rebootWhenIdle(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) title = "Confirm" @@ -1260,6 +1367,7 @@ def rebootWhenIdle(self, rpcObjects=None): self._update() addTags_info = ["Add Tags...", None, "configure"] + def addTags(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) if hosts: @@ -1275,6 +1383,7 @@ def addTags(self, rpcObjects=None): self._update() removeTags_info = ["Remove Tags...", None, "configure"] + def removeTags(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) if hosts: @@ -1290,40 +1399,39 @@ def removeTags(self, rpcObjects=None): self._update() renameTag_info = ["Rename Tag...", None, "configure"] + def renameTag(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) if hosts: title = "Rename tag" body = "What tag should be renamed?" - (oldTag, choice) = QtWidgets.QInputDialog.getItem(self._caller, - title, body, - hosts[0].data.tags, - 0, False) - if not choice: return + (oldTag, choice) = QtWidgets.QInputDialog.getItem( + self._caller, title, body, hosts[0].data.tags, 0, False) + if not choice: + return oldTag = str(oldTag) title = "Rename tag" body = "What is the new name for the tag?" (newTag, choice) = self.getText(title, body, oldTag) - if not choice: return + if not choice: + return for host in hosts: - self.cuebotCall(host.renameTag, - "Rename Tags on %s Failed" % host.data.name, - oldTag, newTag) + self.cuebotCall( + host.renameTag, "Rename Tags on %s Failed" % host.data.name, oldTag, newTag) self._update() changeAllocation_info = ["Change Allocation...", None, "configure"] + def changeAllocation(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) if hosts: - allocations = dict([(alloc.data.name, alloc) for alloc in opencue.api.getAllocations()]) + allocations = {alloc.data.name: alloc for alloc in opencue.api.getAllocations()} title = "Move host to allocation" body = "What allocation should the host(s) be moved to?" - (allocationName, choice) = QtWidgets.QInputDialog.getItem(self._caller, - title, body, - sorted(allocations.keys()), - 0, False) + (allocationName, choice) = QtWidgets.QInputDialog.getItem( + self._caller, title, body, sorted(allocations.keys()), 0, False) if choice: allocation = allocations[str(allocationName)] for host in hosts: @@ -1333,6 +1441,7 @@ def changeAllocation(self, rpcObjects=None): self._update() setRepair_info = ["Set Repair State", None, "configure"] + def setRepair(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) repair = opencue.api.host_pb2.REPAIR @@ -1342,6 +1451,7 @@ def setRepair(self, rpcObjects=None): self._update() clearRepair_info = ["Clear Repair State", None, "configure"] + def clearRepair(self, rpcObjects=None): hosts = self._getOnlyHostObjects(rpcObjects) repair = opencue.api.host_pb2.REPAIR @@ -1353,36 +1463,45 @@ def clearRepair(self, rpcObjects=None): class ProcActions(AbstractActions): + """Actions for procs.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) view_info = ["&View Job", None, "view"] + def view(self, rpcObjects=None): - for job in list(set([proc.data.job_name for proc in self._getOnlyProcObjects(rpcObjects)])): + for job in list({proc.data.job_name for proc in self._getOnlyProcObjects(rpcObjects)}): try: + # pylint: disable=no-member QtGui.qApp.view_object.emit(opencue.api.findJob(job)) - except Exception: - logger.warning("Unable to load: %s" % job) + # pylint: enable=no-member + except opencue.exception.CueException: + logger.warning("Unable to load: %s", job) kill_info = ["&Kill", None, "kill"] + def kill(self, rpcObjects=None): procs = self._getOnlyProcObjects(rpcObjects) if procs: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Kill selected frames?", - ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) for proc in procs]): + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Kill selected frames?", + ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) + for proc in procs]): for proc in procs: self.cuebotCall(proc.kill, "Kill Proc %s Failed" % proc.data.name) self._update() unbook_info = ["Unbook", None, "eject"] + def unbook(self, rpcObjects=None): procs = self._getOnlyProcObjects(rpcObjects) if procs: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Unbook selected frames?", - ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) for proc in procs]): + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Unbook selected frames?", + ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) + for proc in procs]): for proc in procs: self.cuebotCall(proc.unbook, "Unbook Proc %s Failed" % proc.data.name, @@ -1390,12 +1509,14 @@ def unbook(self, rpcObjects=None): self._update() unbookKill_info = ["Unbook and Kill", None, "unbookkill"] + def unbookKill(self, rpcObjects=None): procs = self._getOnlyProcObjects(rpcObjects) if procs: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Unbook and Kill selected frames?", - ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) for proc in procs]): + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Unbook and Kill selected frames?", + ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) + for proc in procs]): for proc in procs: self.cuebotCall(proc.unbook, "Unbook and Kill Proc %s Failed" % proc.data.name, @@ -1404,10 +1525,13 @@ def unbookKill(self, rpcObjects=None): class DependenciesActions(AbstractActions): + """Actions for depends.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) satisfy_info = ["Satisfy Dependency", None, "kill"] + def satisfy(self, rpcObjects=None): dependencies = self._getSelected(rpcObjects) for dependency in dependencies: @@ -1415,6 +1539,7 @@ def satisfy(self, rpcObjects=None): self._update() unsatisfy_info = ["Unsatisfy Dependency", None, "retry"] + def unsatisfy(self, rpcObjects=None): dependencies = self._getSelected(rpcObjects) for dependency in dependencies: @@ -1423,10 +1548,13 @@ def unsatisfy(self, rpcObjects=None): class FilterActions(AbstractActions): + """Actions for filters.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) rename_info = ["Rename...", None, ""] + def rename(self, rpcObjects=None): filters = self._getSelected(rpcObjects) if filters: @@ -1439,45 +1567,51 @@ def rename(self, rpcObjects=None): self._update() delete_info = ["Delete", None, "kill"] + def delete(self, rpcObjects=None): filters = self._getSelected(rpcObjects) if filters: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Delete selected filters?", - [filter.data.name for filter in filters]): - for filter in filters: - filter.delete() + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Delete selected filters?", + [selectedFilter.data.name for selectedFilter in filters]): + for filterToDelete in filters: + filterToDelete.delete() self._update() raiseOrder_info = ["Raise Order", None, ""] + def raiseOrder(self, rpcObjects=None): filters = self._getSelected(rpcObjects) - for filter in filters: - filter.raiseOrder() + for selectedFilter in filters: + selectedFilter.raiseOrder() self._update() lowerOrder_info = ["Lower Order", None, ""] + def lowerOrder(self, rpcObjects=None): filters = self._getSelected(rpcObjects) - for filter in filters: - filter.lowerOrder() + for selectedFilter in filters: + selectedFilter.lowerOrder() self._update() orderFirst_info = ["Order First", None, ""] + def orderFirst(self, rpcObjects=None): filters = self._getSelected(rpcObjects) - for filter in filters: - filter.orderFirst() + for selectedFilter in filters: + selectedFilter.orderFirst() self._update() orderLast_info = ["Order Last", None, ""] + def orderLast(self, rpcObjects=None): filters = self._getSelected(rpcObjects) - for filter in filters: - filter.orderLast() + for selectedFilter in filters: + selectedFilter.orderLast() self._update() setOrder_info = ["Set Order...", None, ""] + def setOrder(self, rpcObjects=None): filters = self._getSelected(rpcObjects) if filters: @@ -1493,6 +1627,8 @@ def setOrder(self, rpcObjects=None): class MatcherActions(AbstractActions): + """Actions for matchers.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1521,6 +1657,8 @@ def setValue(self, rpcObjects=None): class ActionActions(AbstractActions): + """Actions for filter actions.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1537,6 +1675,8 @@ def delete(self, rpcObjects=None): class TaskActions(AbstractActions): + """Actions for tasks.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) @@ -1556,7 +1696,8 @@ def setMinCores(self, rpcObjects=None): task.setMinCores(float(value)) self._update() - clearAdjustment_info = ["Clear Minimum Core Adjustment", "Clear Task(s) Minimum Core Adjustment", "configure"] + clearAdjustment_info = [ + "Clear Minimum Core Adjustment", "Clear Task(s) Minimum Core Adjustment", "configure"] def clearAdjustment(self, rpcObjects=None): tasks = self._getSelected(rpcObjects) for task in tasks: @@ -1573,17 +1714,19 @@ def delete(self, rpcObjects=None): for task in tasks: task.delete() self._update() - + class LimitActions(AbstractActions): + """Actions for limits.""" + def __init__(self, *args): AbstractActions.__init__(self, *args) - create_info = ["Creaet Limit", None, "configure"] + create_info = ["Create Limit", None, "configure"] def create(self, rpcObjects=None): title = "Add Limit" body = "Enter a name for the new limit." - + (limit, choice) = self.getText(title, body, "") if choice: limit = limit.strip() @@ -1620,7 +1763,7 @@ def editMaxValue(self, rpcObjects=None): "Set Max Value on Limit %s Failed" % limit.data.name, int(value)) self._update() - + rename_info = ["Rename", None, "configure"] def rename(self, rpcObjects=None): limits = self._getSelected(rpcObjects) @@ -1633,9 +1776,12 @@ def rename(self, rpcObjects=None): self._update() +# pylint: disable=attribute-defined-outside-init class MenuActions(object): + """Provides access to common right click actions.""" + def __init__(self, caller, updateCallable, selectedRpcObjectsCallable, sourceCallable = None): - """This object provides access to common right click actions + """ @param caller: The Widget that is creating the menu @type caller: QWidget @param updateCallable: A callable that will update the display @@ -1731,7 +1877,7 @@ def tasks(self): if not hasattr(self, "_tasks"): self._tasks = TaskActions(*self.__getArgs()) return self._tasks - + def limits(self): if not hasattr(self, "_limits"): self._limits = LimitActions(*self.__getArgs()) diff --git a/cuegui/cuegui/MiscDialog.py b/cuegui/cuegui/MiscDialog.py index e10b5e554..c3b1185f8 100644 --- a/cuegui/cuegui/MiscDialog.py +++ b/cuegui/cuegui/MiscDialog.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Miscellaneous dialogs.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -23,6 +26,8 @@ class RunLocalDialog(cuegui.AbstractDialog.AbstractDialog): + """Dialog for running a job on the user's local desktop cores.""" + def __init__(self, job, parent=None): cuegui.AbstractDialog.AbstractDialog.__init__(self, parent) layout = QtWidgets.QVBoxLayout(self) @@ -46,7 +51,8 @@ def __init__(self, job, parent=None): self.__localOnlyLabel = QtWidgets.QLabel("Only use local cores for this job?", self) self.__localOnlyCheckBox = QtWidgets.QCheckBox(self) - self.__buttons = self._newDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) + self.__buttons = self._newDialogButtonBox( + QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) layout.addWidget(self.__descriptionLabel) self._addWidgetRow(self.__amountLabel, self.__amountSpinBox) @@ -55,4 +61,5 @@ def __init__(self, job, parent=None): layout.addWidget(self.__buttons) def results(self): + """Gets the user input results.""" return self.__amountSpinBox.value(), self.__localOnlyCheckBox.isChecked() diff --git a/cuegui/cuegui/Plugins.py b/cuegui/cuegui/Plugins.py index c57e64964..2b67ebd7e 100644 --- a/cuegui/cuegui/Plugins.py +++ b/cuegui/cuegui/Plugins.py @@ -83,9 +83,12 @@ class Plugins(object): + """Main class responsible for loading and managing plugins.""" + # Keyed to name. each is a dictionary with CLASS, DESCRIPTION and optionally CATEGORY __plugins = {} _loadedPaths = [] + def __init__(self, mainWindow, name): """Plugins class initialization. @param mainWindow: Application main window reference @@ -99,7 +102,9 @@ def __init__(self, mainWindow, name): self.__menu_separator = " \t-> " # Load plugin paths from the config file + # pylint: disable=no-member __pluginPaths = QtGui.qApp.settings.value("Plugin_Paths", []) + # pylint: enable=no-member for path in cuegui.Constants.DEFAULT_PLUGIN_PATHS + __pluginPaths: self.loadPluginPath(str(path)) @@ -118,74 +123,92 @@ def loadConfigFilePlugins(self, configGroup): The imported module must have an init function and a QMainWindow will be passed to it. """ + # pylint: disable=no-member __plugins = QtGui.qApp.settings.value("%s/Plugins" % configGroup, []) + # pylint: enable=no-member for plugin in __plugins: path = os.path.dirname(str(plugin)) if path: - logger.info("adding path " + path) + logger.info("adding path %s", path) sys.path.append(path) for plugin in __plugins: module = os.path.basename(str(plugin)) if module: - logger.info("loading module " + module) + logger.info("loading module %s", module) s_class = module.split(".")[-1] + # pylint: disable=broad-except try: m = __import__(module, globals(), locals(), [s_class]) m.init(self.mainWindow) - logger.info("plugin loaded %s" % module) + logger.info("plugin loaded %s", module) except Exception as e: - logger.warning("Failed to load plugin: %s" % s_class) + logger.warning("Failed to load plugin: %s", s_class) list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) - def __closePlugin(self, object): - """When a running plugin is closed, this is called and the running - plugin is deleted. If it is a dock widget then it is removed from the - main window. - @type object: Object - @param object: The object created by loadin""" + def __closePlugin(self, pluginBeingClosed): + """Event handler for when a plugin is closed. + + When a running plugin is closed, this is called and the running plugin is deleted. If it is + a dock widget then it is removed from the main window. + + @type pluginBeingClosed: Object + @param pluginBeingClosed: the plugin widget being closed""" for item in self.__running: - if item[1] == object: - if isinstance(object, QtWidgets.QDockWidget): - self.mainWindow.removeDockWidget(object) + if item[1] == pluginBeingClosed: + if isinstance(pluginBeingClosed, QtWidgets.QDockWidget): + self.mainWindow.removeDockWidget(pluginBeingClosed) self.__running.remove(item) return def runningList(self): - """Lists all running plugins + """Lists all running plugins. + @return: [("Class_Name_1", PluginClass1_Instance), ("Class_Name_2", PluginClass2_Instance)] @rtype: list""" return self.__running def saveState(self): """Saves the names of all open plugins. - Calls .saveSettings (if available) on all plugins.""" + + Calls .saveSettings (if available) on all plugins.""" opened = [] for plugin in self.__running: + # pylint: disable=broad-except try: if hasattr(plugin[1], "pluginSaveState"): opened.append("%s::%s" % (plugin[0], json.dumps(plugin[1].pluginSaveState()))) except Exception as e: - logger.warning("Error saving plugin state for: %s\n%s" % (plugin[0], e)) + logger.warning("Error saving plugin state for: %s\n%s", plugin[0], e) + # pylint: disable=no-member QtGui.qApp.settings.setValue("%s/Plugins_Opened" % self.name, opened) + # pylint: enable=no-member def restoreState(self): - """Loads any user defined pluggin directories. - Restores all open plugins. - Calls .restoreSettings (if available) on all plugins.""" - # Loads any user defined pluggin directories - for path in QtGui.qApp.settings.value("Plugins/Paths", []): + """Loads any user defined plugin directories and restores all open plugins. + + Calls .restoreSettings (if available) on all plugins.""" + # Loads any user defined plugin directories + # pylint: disable=no-member + pluginPaths = QtGui.qApp.settings.value("Plugins/Paths", []) + # pylint: enable=no-member + + for path in pluginPaths: self.loadPluginPath(str(path)) # Runs any plugins that were saved to the settings - for plugin in (QtGui.qApp.settings.value("%s/Plugins_Opened" % self.name) or []): + # pylint: disable=no-member + openPlugins = QtGui.qApp.settings.value("%s/Plugins_Opened" % self.name) or [] + # pylint: enable=no-member + for plugin in openPlugins: if '::' in plugin: plugin_name, plugin_state = str(plugin).split("::") self.launchPlugin(plugin_name, plugin_state) def launchPlugin(self, plugin_name, plugin_state): - """Launches the desired plugin + """Launches the desired plugin. + @param plugin_name: The name of the plugin as provided by PLUGIN_NAME @type plugin_name: string @param plugin_state: The state of the plugin's tab @@ -193,19 +216,23 @@ def launchPlugin(self, plugin_name, plugin_state): try: plugin_class = self.__plugins[plugin_name][CLASS] except KeyError: - logger.warning("Unable to launch previously open plugin, it no longer exists: %s" % plugin_name) + logger.warning( + "Unable to launch previously open plugin, it no longer exists: %s", plugin_name) return + # pylint: disable=broad-except try: plugin_instance = plugin_class(self.mainWindow) self.__running.append((plugin_name, plugin_instance)) plugin_instance.closed.connect(self.__closePlugin, QtCore.Qt.QueuedConnection) except Exception: - logger.warning("Failed to load plugin module: %s\n%s" % (plugin_name, - ''.join(traceback.format_exception(*sys.exc_info())) )) + logger.warning( + "Failed to load plugin module: %s\n%s", + plugin_name, ''.join(traceback.format_exception(*sys.exc_info()))) return if hasattr(plugin_instance, "pluginRestoreState"): + # pylint: disable=broad-except try: try: if plugin_state: @@ -223,11 +250,13 @@ def launchPlugin(self, plugin_name, plugin_state): else: state = None except Exception as e: - logger.warning("Failed to load state information stored as %s for %s, error was: %s" % (plugin_state, plugin_name, e)) + logger.warning( + "Failed to load state information stored as %s for %s, error was: %s", + plugin_state, plugin_name, e) state = None plugin_instance.pluginRestoreState(state) except Exception as e: - logger.warning("Error restoring plugin state for: %s" % plugin_name) + logger.warning("Error restoring plugin state for: %s", plugin_name) list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) def loadPluginPath(self, plugin_dir): @@ -245,22 +274,23 @@ def loadPluginPath(self, plugin_dir): for p in os.listdir(plugin_dir): name, ext = os.path.splitext(p) - if ext == ".py" and not name in ["__init__","Manifest","README"]: + if ext == ".py" and not name in ["__init__", "Manifest", "README"]: self.loadPlugin(name) sys.path = orig_sys_path else: - logger.warning("Unable to read the plugin path: %s" % plugin_dir) + logger.warning("Unable to read the plugin path: %s", plugin_dir) def loadPlugin(self, name): """Loads a single plugin that must be in the python path @param name: Name of the python module that contains a plugin @type name: string""" + # pylint: disable=broad-except try: - logger.info("Importing: %s" % name) + logger.info("Importing: %s", name) module = __import__(name, globals(), locals()) - logger.info("Has: %s" % dir(module)) - logger.info("Name: %s" % module.PLUGIN_NAME) - logger.info("Provides: %s" % module.PLUGIN_PROVIDES) + logger.info("Has: %s", dir(module)) + logger.info("Name: %s", module.PLUGIN_NAME) + logger.info("Provides: %s", module.PLUGIN_PROVIDES) # If a plugin requires a different app, do not use it # TODO: accept a list also, log it @@ -269,7 +299,7 @@ def loadPlugin(self, name): return newPlugin = {} - newPlugin[CLASS] = getattr(module, module.PLUGIN_PROVIDES) + newPlugin[CLASS] = getattr(module, module.PLUGIN_PROVIDES) newPlugin[DESCRIPTION] = str(module.PLUGIN_DESCRIPTION) if hasattr(module, "PLUGIN_CATEGORY"): @@ -277,9 +307,10 @@ def loadPlugin(self, name): self.__plugins[module.PLUGIN_NAME] = newPlugin - except Exception as e: - logger.warning("Failed to load plugin %s\n%s" % (name, - ''.join(traceback.format_exception(*sys.exc_info())) )) + except Exception: + logger.warning( + "Failed to load plugin %s\n%s", + name, ''.join(traceback.format_exception(*sys.exc_info()))) def setupPluginMenu(self, menu): """Adds a plugin menu option to the supplied menubar @@ -290,8 +321,9 @@ def setupPluginMenu(self, menu): # Create the submenus (ordered) submenus = {} menu_locations = {"root": []} - for category in set([plugin[CATEGORY] for plugin in list(self.__plugins.values()) - if CATEGORY in plugin]): + plugin_categories = { + plugin[CATEGORY] for plugin in list(self.__plugins.values()) if CATEGORY in plugin} + for category in plugin_categories: submenus[category] = QtWidgets.QMenu(category, menu) menu.addMenu(submenus[category]) menu_locations[category] = [] @@ -319,19 +351,23 @@ def _handlePluginMenu(self, action): plugin_name = str(action.text()).split("%s" % self.__menu_separator)[0] self.launchPlugin(plugin_name, "") + class Plugin(object): + """Represents a single plugin.""" + def __init__(self): self.__settings = [] - def pluginRestoreState(self, saved): + def pluginRestoreState(self, saved_settings): """Called on plugin start with any previously saved state. - @param settings: Last state of the plugin instance - @type settings: dict""" - if self.__settings and saved and isinstance(saved, dict): + + @param saved_settings: Last state of the plugin instance + @type saved_settings: dict""" + if self.__settings and saved_settings and isinstance(saved_settings, dict): for setting in self.__settings: item = setting[SETTINGS_KEY] - if item in saved: - setting[SETTINGS_SET](saved[item]) + if item in saved_settings: + setting[SETTINGS_SET](saved_settings[item]) def pluginSaveState(self): """Called on application exit and returns plugin state information. diff --git a/cuegui/cuegui/PreviewWidget.py b/cuegui/cuegui/PreviewWidget.py index e913cb56e..6e60a0a6a 100644 --- a/cuegui/cuegui/PreviewWidget.py +++ b/cuegui/cuegui/PreviewWidget.py @@ -13,16 +13,24 @@ # limitations under the License. +"""Widget for displaying a preview of a frame in an image viewer.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division + +# pylint: disable=wrong-import-position from future import standard_library standard_library.install_aliases() +# pylint: enable=wrong-import-position import os -import time -import urllib.request, urllib.error, urllib.parse import tempfile +import time +import urllib.error +import urllib.parse +import urllib.request import xml.etree.ElementTree as Et from PySide2 import QtCore @@ -59,7 +67,7 @@ def __init__(self, job, frame, aovs=False, parent=None): self.__itvFile = None layout = QtWidgets.QVBoxLayout(self) - + self.__msg = QtWidgets.QLabel("Waiting for preview images...", self) self.__progbar = QtWidgets.QProgressBar(self) @@ -67,14 +75,15 @@ def __init__(self, job, frame, aovs=False, parent=None): layout.addWidget(self.__progbar) def process(self): + """Opens the image viewer.""" items = [] http_host = self.__frame.resource().split("/")[0] http_port = self.__findHttpPort() - + aovs = "" if self.__aovs: aovs = "/aovs" - + playlist = urllib.request.urlopen("http://%s:%d%s" % (http_host, http_port, aovs)).read() for element in Et.fromstring(playlist).findall("page/edit/element"): items.append(element.text) @@ -84,25 +93,32 @@ def process(self): self.__itvFile = self.__writePlaylist(playlist) self.__previewThread = PreviewProcessorWatchThread(items, self) + # pylint: disable=no-member QtGui.qApp.threads.append(self.__previewThread) + # pylint: enable=no-member self.__previewThread.start() self.__progbar.setRange(0, len(items)) self.__previewThread.existCountChanged.connect(self.updateProgressDialog) self.__previewThread.timeout.connect(self.processTimedOut) - def updateProgressDialog(self, current, max): - if max != current: + def updateProgressDialog(self, current, max_progress): + """Updates the progress dialog.""" + if max_progress != current: self.__progbar.setValue(current) else: self.close() - + def processTimedOut(self): + """Event handler when the process has timed out.""" self.close() - QtWidgets.QMessageBox.critical(self, "Preview Timeout", "Unable to preview images, " + - "timed out while waiting for images to be copied.") + QtWidgets.QMessageBox.critical( + self, + "Preview Timeout", + "Unable to preview images, timed out while waiting for images to be copied.") - def __writePlaylist(self, data): + @staticmethod + def __writePlaylist(data): (fh, name) = tempfile.mkstemp(suffix=".itv", prefix="playlist") os.close(fh) fp = open(name, "w") @@ -140,7 +156,7 @@ class PreviewProcessorWatchThread(QtCore.QThread): def __init__(self, items, parent=None): QtCore.QThread.__init__(self, parent) self.__items = items - self.__timeout = 60 + (30 * len(items)) + self.__timeout = 60 + (30 * len(items)) def run(self): """ diff --git a/cuegui/cuegui/ProcMonitor.py b/cuegui/cuegui/ProcMonitor.py index 64b9fd2fa..63035d7da 100644 --- a/cuegui/cuegui/ProcMonitor.py +++ b/cuegui/cuegui/ProcMonitor.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Widget for displaying a list of procs.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -35,10 +38,13 @@ class ProcMonitor(QtWidgets.QWidget): - """This contains the frame list table with controls at the top""" + """Widget for displaying a list of procs.""" + def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) + self.__filterByHostNameLastInput = None + self.procMonitorTree = cuegui.ProcMonitorTree.ProcMonitorTree(self) # Setup main vertical layout @@ -51,36 +57,43 @@ def __init__(self, parent): # This hlayout would contain any filter/control buttons hlayout = QtWidgets.QHBoxLayout() - self.__filterByHostNameSetup(hlayout) # Menu to filter by proc name + self.__filterByHostNameSetup(hlayout) hlayout.addStretch() - self.__refreshToggleCheckBoxSetup(hlayout) # Checkbox to enable/disable auto refresh - self.__refreshButtonSetup(hlayout) # Button to refresh - self.__clearButtonSetup(hlayout) # Button to clear all filters + self.__refreshToggleCheckBoxSetup(hlayout) + self.__refreshButtonSetup(hlayout) + self.__clearButtonSetup(hlayout) self.layout().addLayout(hlayout) self.layout().addWidget(self.procMonitorTree) - self.__viewProcsSetup() # For view_hosts signal - self.__hostDoubleClickedSetup() # Views procs when a host is double clicked + self.__viewProcsSetup() + self.__hostDoubleClickedSetup() + + self.__viewHostsSetup() - self.__viewHostsSetup() # For view_hosts signal - - if bool(int(QtGui.qApp.settings.value("AutoRefreshMonitorProc", 1))): # For refresh on launch + # pylint: disable=no-member + if bool(int(QtGui.qApp.settings.value("AutoRefreshMonitorProc", 1))): self.updateRequest() + # pylint: enable=no-member def updateRequest(self): + """Requests an update to the widget's contents.""" self.procMonitorTree.updateRequest() def getColumnVisibility(self): + """Gets a list of whether table columns are visible.""" return self.procMonitorTree.getColumnVisibility() def setColumnVisibility(self, settings): + """Sets whether table columns are visible.""" self.procMonitorTree.setColumnVisibility(settings) def getColumnOrder(self): + """Gets table column order.""" return self.procMonitorTree.getColumnOrder() def setColumnOrder(self, settings): + """Sets table column order.""" self.procMonitorTree.setColumnOrder(settings) # ============================================================================== @@ -130,7 +143,9 @@ def __refreshToggleCheckBoxSetup(self, layout): def __refreshToggleCheckBoxHandle(self, state): self.procMonitorTree.enableRefresh = bool(state) + # pylint: disable=no-member QtGui.qApp.settings.setValue("AutoRefreshMonitorProc", int(bool(state))) + # pylint: enable=no-member # ============================================================================== # Button to refresh @@ -179,7 +194,9 @@ def __clearButtonHandle(self): # Monitors and handles the view_procs signal # ============================================================================== def __viewProcsSetup(self): + # pylint: disable=no-member QtGui.qApp.view_procs.connect(self.__viewProcsHandle) + # pylint: enable=no-member def __viewProcsHandle(self, hosts): self.procMonitorTree.procSearch.options['host'] = hosts @@ -189,7 +206,9 @@ def __viewProcsHandle(self, hosts): # Views procs when a host is double clicked # ============================================================================== def __hostDoubleClickedSetup(self): + # pylint: disable=no-member QtGui.qApp.view_object.connect(self.__hostDoubleClickedHandle) + # pylint: enable=no-member def __hostDoubleClickedHandle(self, rpcObject): if cuegui.Utils.isHost(rpcObject): @@ -200,7 +219,9 @@ def __hostDoubleClickedHandle(self, rpcObject): # Monitors and handles the view_hosts signal # ============================================================================== def __viewHostsSetup(self): + # pylint: disable=no-member QtGui.qApp.view_hosts.connect(self.__viewHostsHandle) + # pylint: enable=no-member def __viewHostsHandle(self, hosts): if hosts: diff --git a/cuegui/cuegui/ProcMonitorTree.py b/cuegui/cuegui/ProcMonitorTree.py index a784fff37..efbf0c86f 100644 --- a/cuegui/cuegui/ProcMonitorTree.py +++ b/cuegui/cuegui/ProcMonitorTree.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A frame list based on AbstractTreeWidget -""" +"""Tree widget for displaying a list of procs.""" from __future__ import absolute_import @@ -43,36 +41,41 @@ class ProcMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a list of procs.""" + def __init__(self, parent): self.startColumnsForType(cuegui.Constants.TYPE_PROC) - self.addColumn("Name", 150, id=1, - data=lambda proc: proc.data.name, - tip="Name of the running proc.") - self.addColumn("Cores", 50, id=2, - data=lambda proc: ("%.2f" % proc.data.reserved_cores), - tip="The number of cores reserved.") - self.addColumn("Mem Reserved", 100, id=3, - data=lambda proc: cuegui.Utils.memoryToString(proc.data.reserved_memory), - tip="The amount of memory reserved.") - self.addColumn("Mem Used", 100, id=4, - data=lambda proc: cuegui.Utils.memoryToString(proc.data.used_memory), - tip="The amount of memory used.") - self.addColumn("GPU Used", 100, id=5, - data=lambda proc: cuegui.Utils.memoryToString(proc.data.reserved_gpu), - tip="The amount of gpu memory used.") - self.addColumn("Age", 60, id=6, - data=lambda proc: cuegui.Utils.secondsToHHHMM(time.time() - proc.data.dispatch_time), - tip="The age of the running frame.") - self.addColumn("Unbooked", 80, id=7, - data=lambda proc: proc.data.unbooked, - tip="If the proc has been unbooked.\n If it is unbooked then" - "when the frame finishes the job will stop using this proc") - self.addColumn("Name", 300, id=8, - data=lambda proc: proc.data.frame_name , - tip="The name of the proc, includes frame number and layer name.") - self.addColumn("Job", 50, id=9, - data=lambda proc: proc.data.job_name , - tip="The job that this proc is running on.") + self.addColumn( + "Name", 150, id=1, data=lambda proc: proc.data.name, tip="Name of the running proc.") + self.addColumn( + "Cores", 50, id=2, data=lambda proc: ("%.2f" % proc.data.reserved_cores), + tip="The number of cores reserved.") + self.addColumn( + "Mem Reserved", 100, id=3, + data=lambda proc: cuegui.Utils.memoryToString(proc.data.reserved_memory), + tip="The amount of memory reserved.") + self.addColumn( + "Mem Used", 100, id=4, + data=lambda proc: cuegui.Utils.memoryToString(proc.data.used_memory), + tip="The amount of memory used.") + self.addColumn( + "GPU Used", 100, id=5, + data=lambda proc: cuegui.Utils.memoryToString(proc.data.reserved_gpu), + tip="The amount of gpu memory used.") + self.addColumn( + "Age", 60, id=6, + data=lambda proc: cuegui.Utils.secondsToHHHMM(time.time() - proc.data.dispatch_time), + tip="The age of the running frame.") + self.addColumn( + "Unbooked", 80, id=7, data=lambda proc: proc.data.unbooked, + tip="If the proc has been unbooked.\n If it is unbooked then" + "when the frame finishes the job will stop using this proc") + self.addColumn( + "Name", 300, id=8, data=lambda proc: proc.data.frame_name, + tip="The name of the proc, includes frame number and layer name.") + self.addColumn( + "Job", 50, id=9, data=lambda proc: proc.data.job_name, + tip="The job that this proc is running on.") self.procSearch = opencue.search.ProcSearch() @@ -86,12 +89,17 @@ def __init__(self, parent): self.itemDoubleClicked.connect(self.__itemDoubleClickedViewLog) # Don't use the standard space bar to refresh + # pylint: disable=no-member QtGui.qApp.request_update.connect(self.updateRequest) + # pylint: enable=no-member self.startTicksUpdate(40) # Don't start refreshing until the user sets a filter or hits refresh self.ticksWithoutUpdate = -1 + + # pylint: disable=no-member self.enableRefresh = bool(int(QtGui.qApp.settings.value("AutoRefreshMonitorProc", 1))) + # pylint: enable=no-member def tick(self): if self.ticksWithoutUpdate >= self.updateInterval and \ @@ -117,20 +125,27 @@ def __itemSingleClickedCopy(self, item, col): @param item: The item clicked on @type col: int @param col: The column clicked on""" + del item + del col selected = [proc.data.name for proc in self.selectedObjects() if cuegui.Utils.isProc(proc)] if selected: QtWidgets.QApplication.clipboard().setText(",".join(selected)) + # pylint: disable=no-self-use def __itemDoubleClickedViewLog(self, item, col): """Called when a proc is double clicked @type item: QTreeWidgetItem @param item: The item double clicked on @type col: int @param col: Column number double clicked on""" + del col job_name = item.rpcObject.data.job_name + # pylint: disable=no-member QtGui.qApp.view_object.emit(opencue.api.findJob(job_name)) + # pylint: enable=no-member def clearFilters(self): + """Removes all sorting and filtering to restore default state.""" self.clearSelection() self.procSearch = opencue.search.ProcSearch() self.sortByColumn(0, QtCore.Qt.AscendingOrder) @@ -141,6 +156,7 @@ def updateRequest(self): since last updated""" self.ticksWithoutUpdate = 999 + # pylint: disable=too-many-boolean-expressions def _getUpdate(self): """Returns the proper data from the cuebot""" try: @@ -155,21 +171,21 @@ def _getUpdate(self): not self.procSearch.options.get('durationRange'): return [] return opencue.api.getProcs(**self.procSearch.options) - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] - def _createItem(self, object, parent = None): + def _createItem(self, rpcObject, parent=None): """Creates and returns the proper item - @type object: Proc - @param object: The object for this item + @type rpcObject: Proc + @param rpcObject: The object for this item @type parent: QTreeWidgetItem @param parent: Optional parent for this item @rtype: QTreeWidgetItem @return: The created item""" if not parent: parent = self - return ProcWidgetItem(object, parent) + return ProcWidgetItem(rpcObject, parent) def contextMenuEvent(self, e): """When right clicking on an item, this raises a context menu""" @@ -182,6 +198,8 @@ def contextMenuEvent(self, e): class ProcWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item representing a single proc.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_PROC, object, parent) + self, cuegui.Constants.TYPE_PROC, rpcObject, parent) diff --git a/cuegui/cuegui/ProgressDialog.py b/cuegui/cuegui/ProgressDialog.py index b2fcee2ef..399a1c64a 100644 --- a/cuegui/cuegui/ProgressDialog.py +++ b/cuegui/cuegui/ProgressDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A progress dialog that accepts a list of work units and displays the progress. -""" +"""A progress dialog that accepts a list of work units and displays the progress.""" from __future__ import absolute_import @@ -37,8 +35,10 @@ class ProgressDialog(QtWidgets.QDialog): + """A progress dialog that accepts a list of work units and displays the progress.""" + def __init__(self, title, function, work, concurrent, cancelTitle, - cancelText, parent = None): + cancelText, parent=None): """Creates, displays and starts the progress bar. @type title: str @param title: The title for the progress bar @@ -87,7 +87,8 @@ def __init__(self, title, function, work, concurrent, cancelTitle, self.show() - for thread in range(max(concurrent, 1)): + # Submit a new unit of work to the threadpool for each concurrent thread. + for _ in range(max(concurrent, 1)): self._submitWork() def closeEvent(self, event): @@ -124,6 +125,7 @@ def __doWork(self): self.__workLock.unlock() if work: + # pylint: disable=broad-except try: self.__function(*work) except Exception as e: @@ -136,6 +138,9 @@ def __doneWork(self, work, result): @param work: From threadpool (unused) @type result: @param result: From threadpool (unused)""" + del work + del result + self.__count -= 1 self.__bar.setValue(self.__bar.value() + 1) @@ -157,9 +162,11 @@ def _submitWork(self): self.__count += 1 if hasattr(QtGui.qApp, "threadpool"): + # pylint: disable=no-member QtGui.qApp.threadpool.queue(self.__doWork, self.__doneWork, "getting data for %s" % self.__class__) + # pylint: enable=no-member else: logger.warning("threadpool not found, doing work in gui thread") self.__doneWork(None, self.__doWork()) diff --git a/cuegui/cuegui/Redirect.py b/cuegui/cuegui/Redirect.py index 88c6f1738..cdbaa5b35 100644 --- a/cuegui/cuegui/Redirect.py +++ b/cuegui/cuegui/Redirect.py @@ -13,17 +13,18 @@ # limitations under the License. -""" -An interface for redirecting resources from one -job to another job. -""" +"""An interface for redirecting resources from one job to another job. + +The concept here is that there is a target job that needs procs. The user would choose the job. +The highest core/memory value would be detected and would populate 2 text boxes for cores and +memory. The user could then adjust these and hit search. The search will find all hosts that have +frames running that can be redirected to the target job.""" from __future__ import absolute_import from __future__ import print_function from __future__ import division -from past.builtins import cmp from builtins import str from builtins import range import os @@ -39,14 +40,6 @@ import cuegui.Utils -# The concept here is that there is a target job that needs -# procs. The user would choose the job. The highest core/memory -# value would be dected and would populate 2 text boxes for cores -# and memory. The user could then adjust these and hit search. -# -# The search will find all hosts that have frames running that can be -# redirected to the target job. - class ShowCombo(QtWidgets.QComboBox): """ A combo box for show selection @@ -57,6 +50,7 @@ def __init__(self, selected="pipe", parent=None): self.setCurrentIndex(self.findText(selected)) def refresh(self): + """Refreshes the show list.""" self.clear() shows = opencue.api.getActiveShows() shows.sort(key=lambda x: x.data.name) @@ -65,6 +59,7 @@ def refresh(self): self.addItem(show.data.name, show) def getShow(self): + """Gets the selected show.""" return str(self.setCurrentText()) @@ -88,9 +83,7 @@ def __init__(self, parent=None): self.__menu.triggered.connect(self.__afterClicked) def refresh(self): - """ - Refresh the full list of allocations. - """ + """Refreshes the full list of allocations.""" allocs = opencue.api.getAllocations() allocs.sort(key=lambda x: x.data.name) @@ -133,6 +126,7 @@ def __afterClicked(self, action): """ Execute after an allocation has been selected for filtering. """ + del action self.__setSelected() self.setText("Allocations (%d)" % len(self.__selected)) @@ -148,6 +142,7 @@ def __init__(self, parent=None): self.refresh() def refresh(self): + """Refreshes the list of job names.""" slist = opencue.api.getJobNames() slist.sort() @@ -172,8 +167,10 @@ def __init__(self, show, name, parent=None): self.__menu.aboutToShow.connect(self.__populate_menu) + # pylint: disable=inconsistent-return-statements def __loadShow(self, show): - self.__actions = { } + self.__actions = {} + # pylint: disable=bare-except try: if show: return show @@ -181,6 +178,7 @@ def __loadShow(self, show): return opencue.api.findShow(show.name()) def showChanged(self, show): + """Loads a new show.""" self.__show = self.__loadShow(show) def __populate_menu(self): @@ -196,6 +194,7 @@ def __populate_menu(self): self.__menu.addAction(action) def getChecked(self): + """Gets a list of action text for all selected actions.""" return [str(action.text()) for action in list(self.__actions.values()) if action.isChecked()] @@ -298,14 +297,18 @@ def _cfg(self): return self.__config def showChanged(self, show_index): + """Load a new show.""" + del show_index show = self.__show_combo.currentText() self.__current_show = opencue.api.findShow(str(show)) self.__include_group_btn.showChanged(self.__current_show) def detect(self, name=None): + """Populates initial values when the job name is changed.""" + del name try: job = opencue.api.findJob(str(self.__job_box.text())) - except: + except opencue.exception.CueException: return layers = job.getLayers() @@ -323,48 +326,63 @@ def detect(self, name=None): self.__show_combo.setCurrentIndex(self.__show_combo.findText(job.data.show)) def getJob(self): + """Gets the current job name.""" return str(self.__job_box.text()) def getCores(self): + """Gets the core count.""" return int(self.__cores_spin.value()) def getMemory(self): + """Gets the memory amount.""" return int(self.__mem_spin.value() * 1048576.0) def getJobBox(self): + """Gets the job box widget.""" return self.__job_box def getUpdateButton(self): + """Gets the update button widget.""" return self.__update_btn def getRedirectButton(self): + """Gets the redirect button widget.""" return self.__redirect_btn def getSelectAllButton(self): + """Gets the select all button widget.""" return self.__select_all_btn def getClearButton(self): + """Gets the clear button widget.""" return self.__clear_btn def getShow(self): + """Gets the current show.""" return self.__current_show def getAllocFilter(self): + """Gets the allocation filter.""" return self.__alloc_filter def getLimit(self): + """Gets the limit.""" return self.__limit_spin.value() def getCutoffTime(self): + """Gets the cutoff time.""" return int(self.__prh_spin.value() * 3600.0) def getRequiredService(self): + """Gets the required service name.""" return str(self.__require_services.text()).strip() def getJobNameExcludeRegex(self): + """Gets the regex of job name to exclude.""" return str(self.__exclude_regex.text()).strip() def getIncludedGroups(self): + """Gets the value of the include groups checkbox.""" return self.__include_group_btn.getChecked() @@ -416,24 +434,24 @@ def __get_selected_procs_by_alloc(self, selected_items): entry = self.__hosts.get(str(item.text())) alloc = entry.get('alloc') alloc_procs = procs_by_alloc.get(alloc, []) - alloc_procs.extend([proc for proc in entry["procs"]]) + alloc_procs.extend(list(entry["procs"])) procs_by_alloc[alloc] = alloc_procs return procs_by_alloc def __warn(self, msg): - ''' + """ Displays the given message for the user to acknowledge @param msg: The message to display @type msg: str - ''' + """ message = QtWidgets.QMessageBox(self) message.setText(msg) message.exec_() def __is_cross_show_safe(self, procs, target_show): - ''' + """ Determines whether or not it's safe to redirect cores from a show to another, based on user response to the warning message @@ -446,7 +464,7 @@ def __is_cross_show_safe(self, procs, target_show): @return: Whether or not it's safe to redirect the given procs to the target show @rtype: bool - ''' + """ xshow_jobs = [proc.getJob() for proc in procs if not proc.getJob().show() == target_show] @@ -464,7 +482,7 @@ def __is_cross_show_safe(self, procs, target_show): in xshow_jobs]) def __is_burst_safe(self, alloc, procs, show): - ''' + """ Determines whether or not it's safe to redirect cores by checking the burst target show burst and the number of cores being redirected. If there's a number of cores that may not be possible to pick up by the @@ -483,10 +501,12 @@ def __is_burst_safe(self, alloc, procs, show): @return: Whether or not it's safe to kill these cores based on the subscription burst of the target show @rtype: bool - ''' + """ # Skip if this check is disabled in the config + # pylint: disable=protected-access cfg = self.__controls._cfg() + # pylint: enable=protected-access wc_ok = cfg.get('wasted_cores_threshold', 100) if wc_ok < 0: return True @@ -524,12 +544,12 @@ def __is_burst_safe(self, alloc, procs, show): return False def redirect(self): - ''' + """ Redirect the selected procs to the target job, after running a few checks to verify it's safe to do that. @postcondition: The selected procs are redirected to the target job - ''' + """ # Get selected items items = [self.__model.item(row) for row @@ -567,11 +587,11 @@ def redirect(self): for item in selected_items: entry = self.__hosts.get(str(item.text())) procs = entry["procs"] + # pylint: disable=broad-except try: host = entry["host"] host.redirectToJob(procs, job) except Exception as e: - print(e) errors.append(str(e)) item.setIcon(QtGui.QIcon(QtGui.QPixmap(":retry.png"))) item.setEnabled(False) @@ -612,7 +632,7 @@ def update(self): self.__controls.getLimit(), self) progress.setWindowModality(QtCore.Qt.WindowModal) - for num, proc in enumerate(procs): + for proc in procs: if progress.wasCanceled(): break @@ -647,7 +667,7 @@ def update(self): cue_host = opencue.api.findHost(name) hosts[name] = { "host": cue_host, - "procs":[], + "procs": [], "mem": cue_host.data.idle_memory, "cores": int(cue_host.data.idle_cores), "time": 0, @@ -661,16 +681,15 @@ def update(self): host["procs"].append(proc) host["mem"] = host["mem"] + proc.data.reserved_memory host["cores"] = host["cores"] + proc.data.reserved_cores - host["time"] = host["time"] + (int(time.time()) - proc.data.dispatch_time); + host["time"] = host["time"] + (int(time.time()) - proc.data.dispatch_time) if host["cores"] >= self.__controls.getCores() and \ - host["mem"] >= self.__controls.getMemory() and \ - host["time"] < self.__controls.getCutoffTime(): - self.__addHost(host) - host["ok"] = True - ok = ok + 1 - progress.setValue(ok) - + host["mem"] >= self.__controls.getMemory() and \ + host["time"] < self.__controls.getCutoffTime(): + self.__addHost(host) + host["ok"] = True + ok = ok + 1 + progress.setValue(ok) progress.setValue(self.__controls.getLimit()) # Save this for later on @@ -692,7 +711,8 @@ def __addHost(self, entry): for proc in procs: checkbox.appendRow([QtGui.QStandardItem(proc.data.job_name), QtGui.QStandardItem(str(proc.data.reserved_cores)), - QtGui.QStandardItem("%0.2fGB" % (proc.data.reserved_memory / 1048576.0)), + QtGui.QStandardItem( + "%0.2fGB" % (proc.data.reserved_memory / 1048576.0)), QtGui.QStandardItem(cuegui.Utils.secondsToHHMMSS(time.time() - proc.data.dispatch_time)), QtGui.QStandardItem(proc.data.group_name), diff --git a/cuegui/cuegui/ServiceDialog.py b/cuegui/cuegui/ServiceDialog.py index 2eccc6d70..04f1b1a89 100644 --- a/cuegui/cuegui/ServiceDialog.py +++ b/cuegui/cuegui/ServiceDialog.py @@ -13,7 +13,7 @@ # limitations under the License. -"""Service related widgets.""" +"""Dialog for displaying and editing services.""" from __future__ import absolute_import @@ -34,9 +34,8 @@ class ServiceForm(QtWidgets.QWidget): - """ - An Widget for displaying and editing a service. - """ + """Widget for displaying and editing a service.""" + saved = QtCore.Signal(object) def __init__(self, parent=None): @@ -100,7 +99,6 @@ def __init__(self, parent=None): self.__buttons.accepted.connect(self.save) - def _cfg(self): """ Loads (if necessary) and returns the config values. @@ -178,7 +176,7 @@ def save(self): class ServiceManager(QtWidgets.QWidget): """ Wraps the ServiceForm widget with the logic and controls needed - to add, update, and detete services. + to add, update, and delete services. """ def __init__(self, show, parent=None): QtWidgets.QWidget.__init__(self, parent) @@ -218,6 +216,8 @@ def selected(self, item, old_item): """ Executes if an item is selected """ + del old_item + self.__new_service = False if not item: diff --git a/cuegui/cuegui/ShowDialog.py b/cuegui/cuegui/ShowDialog.py index 569e723ab..865e75aa6 100644 --- a/cuegui/cuegui/ShowDialog.py +++ b/cuegui/cuegui/ShowDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Displays the show dialog with show configuration options -""" +"""A dialog displaying show configuration options.""" from __future__ import absolute_import @@ -31,6 +29,8 @@ class ShowDialog(QtWidgets.QDialog): + """A dialog displaying show configuration options.""" + def __init__(self, show, parent=None): QtWidgets.QDialog.__init__(self, parent) @@ -127,8 +127,6 @@ def __createStatisticsPage(self): text.setPlainText("%s" % self.__show.data.show_stats) page.layout().addWidget(text) - #page.layout().setRowStretch(10, 100) - return page def __createRawShowDataPage(self): @@ -147,6 +145,7 @@ def __createRawShowDataPage(self): def __valueChanged(self, value=None): """Called when something changes to enable the save button""" + del value self.__btnSave.setEnabled(True) def __closeDialog(self): diff --git a/cuegui/cuegui/ShowsWidget.py b/cuegui/cuegui/ShowsWidget.py index 8dbe22cc2..b0343834f 100644 --- a/cuegui/cuegui/ShowsWidget.py +++ b/cuegui/cuegui/ShowsWidget.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Tree widget for displaying a list of shows.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -34,6 +37,8 @@ class ShowsWidget(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a list of shows.""" + def __init__(self, parent): self.startColumnsForType(cuegui.Constants.TYPE_SHOW) self.addColumn("Show Name", 90, id=1, @@ -58,7 +63,9 @@ def __init__(self, parent): self, self.updateSoon, self.selectedObjects) self.itemClicked.connect(self.__itemSingleClickedToDouble) + # pylint: disable=no-member QtGui.qApp.facility_changed.connect(self.__facilityChanged) + # pylint: enable=no-member self.setUpdateInterval(60) @@ -76,16 +83,17 @@ def __itemSingleClickedToDouble(self, item, col): @param col: Column number single clicked on""" self.itemDoubleClicked.emit(item, col) - def _createItem(self, object): + def _createItem(self, rpcObject): """Creates and returns the proper item""" - item = ShowWidgetItem(object, self) + item = ShowWidgetItem(rpcObject, self) return item + # pylint: disable=no-self-use def _getUpdate(self): """Returns the proper data from the cuebot""" try: return opencue.api.getActiveShows() - except Exception as e: + except opencue.exception.CueException as e: logger.critical(e) return [] @@ -103,8 +111,13 @@ def contextMenuEvent(self, e): menu.exec_(QtCore.QPoint(e.globalX(), e.globalY())) + def tick(self): + pass + class ShowWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item representing a single show.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_SHOW, object, parent) + self, cuegui.Constants.TYPE_SHOW, rpcObject, parent) diff --git a/cuegui/cuegui/SplashWindow.py b/cuegui/cuegui/SplashWindow.py index 511967375..29c268c7a 100644 --- a/cuegui/cuegui/SplashWindow.py +++ b/cuegui/cuegui/SplashWindow.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Splash screen displayed on initial application launch.""" + + from __future__ import division from __future__ import print_function from __future__ import absolute_import @@ -30,6 +33,8 @@ class SplashWindow(object): + """Splash screen displayed on initial application launch.""" + def __init__(self, app, app_name, version, resource_path): self.app = app @@ -64,19 +69,23 @@ def _findSplash(self, app_name, version, resource_path): break if image is None: + # pylint: disable=broad-except try: image = self._GenerateMissingSplash(app_name) except Exception: return None + # pylint: disable=broad-except try: self._StampVersion(image, version) except Exception: pass return image - def _generateSplashFromImage(self, imagePath): + @staticmethod + def _generateSplashFromImage(imagePath): if os.path.isfile(imagePath): + # pylint: disable=broad-except try: return imagePath and QtGui.QImage(imagePath) except Exception: diff --git a/cuegui/cuegui/Style.py b/cuegui/cuegui/Style.py index 236054ff5..3f25bbed0 100644 --- a/cuegui/cuegui/Style.py +++ b/cuegui/cuegui/Style.py @@ -13,7 +13,7 @@ # limitations under the License. -"""a module for handling global style setup""" +"""Module for handling global style setup.""" from __future__ import print_function @@ -30,35 +30,42 @@ DEFAULT_FONT = "Luxi Sans" DEFAULT_FONT_SIZE = 10.0 +# pylint: disable=global-statement + ColorTheme = None IconTheme = None Font = None def loadColorTheme(name): - """changes the running color scheme of the app""" + """Changes the running color scheme of the app.""" global ColorTheme ColorTheme = importlib.import_module('.%s' % name, package='cuegui') ColorTheme.init() def setIconTheme(name): - """stes the icon theme for the app, not sure if this - can be changed on the fly yet""" + """Sets the icon theme for the app. + + Not sure if this can be changed on the fly yet.""" global IconTheme IconTheme = importlib.import_module('.icons_rcc', package='cuegui.images.%s' % name) def setFont(font): - """sets the application font""" + """Sets the application font.""" global Font Font = font + # pylint: disable=no-member QtGui.qApp.setFont(font) + # pylint: enable=no-member def init(): - """initialize the global style settings""" + """Initializes the global style settings.""" + # pylint: disable=no-member settings = QtGui.qApp.settings + # pylint: enable=no-member loadColorTheme(settings.value("Style/colorTheme", DEFAULT_COLOR)) setIconTheme(settings.value("Style/iconTheme", DEFAULT_ICON)) diff --git a/cuegui/cuegui/SubscriptionGraphWidget.py b/cuegui/cuegui/SubscriptionGraphWidget.py index 25981bf31..2e122328c 100644 --- a/cuegui/cuegui/SubscriptionGraphWidget.py +++ b/cuegui/cuegui/SubscriptionGraphWidget.py @@ -1,9 +1,26 @@ +# Copyright Contributors to the OpenCue Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Widget for displaying graph of subscription usage.""" + + from builtins import str import opencue from PySide2 import QtCore -from PySide2 import QtGui from PySide2 import QtWidgets import cuegui.AbstractTreeWidget @@ -17,6 +34,8 @@ class SubscriptionGraphWidget(QtWidgets.QWidget): + """Widget for displaying graph of subscription usage.""" + def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) @@ -53,6 +72,7 @@ def __init__(self, parent): layout.addWidget(scroll) def create_widgets(self): + """Creates all of the contained widgets.""" self.clearLayout(self.mainLayout) for show in self.__shows: widget = QtWidgets.QWidget() @@ -70,7 +90,9 @@ def create_widgets(self): self.__timer.start() - def clearLayout(self, layout): + @staticmethod + def clearLayout(layout): + """Clears the widget layout.""" while layout.count() > 0: item = layout.takeAt(0) if not item: @@ -82,8 +104,9 @@ def clearLayout(self, layout): def __showMenuHandle(self, action): if action.text() == 'All Shows': try: - self.__shows = sorted(set([job.show() for job in opencue.api.getJobs(include_finished=True)])) - except Exception as e: + self.__shows = sorted( + {job.show() for job in opencue.api.getJobs(include_finished=True)}) + except opencue.exception.CueException: self.__shows = [] self.__showMenuUpdate() elif action.text() == 'Clear': @@ -110,8 +133,8 @@ def __showMenuUpdate(self): self.__showMenu.addSeparator() try: - shows = sorted(set([job.show() for job in opencue.api.getJobs(include_finished=True)])) - except Exception as e: + shows = sorted({job.show() for job in opencue.api.getJobs(include_finished=True)}) + except opencue.exception.CueException: shows = [] for show in shows: @@ -130,11 +153,15 @@ def __showMenuCheck(self): self.__showMenuUpdate() def update_data(self): + """Refreshes the displayed data.""" + # pylint: disable=protected-access for sub_bar in self.__subBars: sub_bar._getUpdate() class SubGraphTreeWidget(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree for displaying a subscription graph.""" + def __init__(self, parent): self.startColumnsForType(cuegui.Constants.TYPE_SUB) self.addColumn("_Name", 110, id=0, @@ -156,6 +183,7 @@ def __init__(self, parent): # self.setUpdateInterval(30) def setShow(self, show=None): + """Sets the current show.""" self._itemsLock.lockForWrite() try: if not show: @@ -165,18 +193,19 @@ def setShow(self, show=None): elif isinstance(show, str): try: self.__show = opencue.api.findShow(show) - except: + except opencue.exception.CueException: pass self._update() finally: self._itemsLock.unlock() def getShow(self): + """Gets the current show.""" return self.__show - def _createItem(self, object): - """Creates and returns the proper item""" - return SubscriptionWidgetItem(object, self) + def _createItem(self, rpcObject): + """Creates a widget item for the current subscription.""" + return SubscriptionWidgetItem(rpcObject, self) def _getUpdate(self): """Returns the proper data from the cuebot""" @@ -189,7 +218,7 @@ def _getUpdate(self): self._itemsLock.unlock() def contextMenuEvent(self, e): - """When right clicking on an item, this raises a context menu""" + """Event handler for showing the context menu.""" menu = QtWidgets.QMenu() self.__menuActions.subscriptions().addAction(menu, "editSize") @@ -204,11 +233,17 @@ def contextMenuEvent(self, e): menu.exec_(QtCore.QPoint(e.globalX(),e.globalY())) def createSubscription(self): + """Shows a dialog for creating a new subscription.""" d = cuegui.CreatorDialog.SubscriptionCreatorDialog(show=self.__show) d.exec_() + def tick(self): + pass + class SubscriptionWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single subscription.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_SUB, object, parent) \ No newline at end of file + self, cuegui.Constants.TYPE_SUB, rpcObject, parent) diff --git a/cuegui/cuegui/SubscriptionsWidget.py b/cuegui/cuegui/SubscriptionsWidget.py index ea01d1946..3acb97921 100644 --- a/cuegui/cuegui/SubscriptionsWidget.py +++ b/cuegui/cuegui/SubscriptionsWidget.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Widget for listing and managing subscriptions.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -34,10 +37,13 @@ class SubscriptionsWidget(QtWidgets.QWidget): + """Widget for listing and managing subscriptions.""" + def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) self.__show = None + self.__shows = None self.__comboShows = QtWidgets.QComboBox(self) self.__comboShows.setFocusPolicy(QtCore.Qt.NoFocus) @@ -61,8 +67,10 @@ def __init__(self, parent): self.__btnShowProperties.clicked.connect(self.__showProperties) self.__btnAddSubscription.clicked.connect(self.__addSubscription) self.__comboShows.currentIndexChanged.connect(self.setShow) + # pylint: disable=no-member QtGui.qApp.view_object.connect(self.setShow) QtGui.qApp.facility_changed.connect(self.changeFacility) + # pylint: enable=no-member self.__menuActions = cuegui.MenuActions.MenuActions( self, self.updateSoon, self.selectedObjects) @@ -70,17 +78,18 @@ def __init__(self, parent): self.changeFacility() def changeFacility(self): + """Changes the active facility.""" try: - self.__shows = dict([(show.name(), show) for show in opencue.api.getActiveShows()]) - except Exception: + self.__shows = {show.name(): show for show in opencue.api.getActiveShows()} + except opencue.exception.CueException: self.__shows = {} self.__comboShows.clear() - self.__comboShows.addItems(["Select Show:"] + - sorted(self.__shows.keys())) + self.__comboShows.addItems(["Select Show:"] + sorted(self.__shows.keys())) self.setShow() def setShow(self, show=""): - """Sets the show for the subscription list and combo box + """Sets the active show. + @type show: str or Show @param show: The show to monitor""" if isinstance(show, int): @@ -110,17 +119,22 @@ def setShow(self, show=""): self.__monitorSubscriptions.setShow(self.__show) def getShow(self): + """Gets the active show.""" return self.__show def getShowName(self): + """Gets the active show name.""" if self.__show: return self.__show.data.name return None def updateSoon(self): + """Requests an update of the subscriptions list.""" + # pylint: disable=protected-access self.__monitorSubscriptions._update() def selectedObjects(self): + """Gets a list of the active show(s).""" return [opencue.api.findShow(self.__show.name())] def __showProperties(self): @@ -136,28 +150,34 @@ def __addSubscription(self): self.updateSoon() def getColumnVisibility(self): + """Gets the table column visibility.""" return self.__monitorSubscriptions.getColumnVisibility() def setColumnVisibility(self, settings): + """Sets the table column visibility.""" self.__monitorSubscriptions.setColumnVisibility(settings) def getColumnOrder(self): + """Gets the table column order.""" return self.__monitorSubscriptions.getColumnOrder() def setColumnOrder(self, settings): + """Sets the table column order.""" self.__monitorSubscriptions.setColumnOrder(settings) class SubscriptionsTreeWidget(cuegui.AbstractTreeWidget.AbstractTreeWidget): - def __init__(self, parent): + """Tree widget for displaying a list of subscriptions.""" + def __init__(self, parent): self.startColumnsForType(cuegui.Constants.TYPE_SUB) self.addColumn("Alloc", 160, id=1, data=lambda sub: sub.data.allocation_name) self.addColumn("Usage", 70, id=2, - data=lambda sub: (sub.data.size and - ("%.2f%%" % (sub.data.reserved_cores / sub.data.size * 100)) - or 0), + data=lambda sub: ( + sub.data.size and + ("%.2f%%" % (sub.data.reserved_cores / sub.data.size * 100)) + or 0), sort=lambda sub: (sub.data.size and sub.data.reserved_cores / sub.data.size or 0)) self.addColumn("Size", 70, id=3, @@ -181,6 +201,7 @@ def __init__(self, parent): self.setUpdateInterval(30) def setShow(self, show=None): + """Sets the active show.""" self._itemsLock.lockForWrite() try: if not show: @@ -190,18 +211,19 @@ def setShow(self, show=None): elif isinstance(show, str): try: self.__show = opencue.api.findShow(show) - except: + except opencue.exception.CueException: pass self._update() finally: self._itemsLock.unlock() def getShow(self): + """Gets the active show.""" return self.__show - def _createItem(self, object): + def _createItem(self, rpcObject): """Creates and returns the proper item""" - return SubscriptionWidgetItem(object, self) + return SubscriptionWidgetItem(rpcObject, self) def _getUpdate(self): """Returns the proper data from the cuebot""" @@ -223,8 +245,13 @@ def contextMenuEvent(self, e): self.__menuActions.subscriptions().addAction(menu, "delete") menu.exec_(QtCore.QPoint(e.globalX(),e.globalY())) + def tick(self): + pass + class SubscriptionWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item representing a single subscription.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_SUB, object, parent) + self, cuegui.Constants.TYPE_SUB, rpcObject, parent) diff --git a/cuegui/cuegui/TagsWidget.py b/cuegui/cuegui/TagsWidget.py index 77d9867eb..cde10b910 100644 --- a/cuegui/cuegui/TagsWidget.py +++ b/cuegui/cuegui/TagsWidget.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A Widget for displaying and editing Tags -""" +"""A Widget for displaying and editing tags.""" from __future__ import absolute_import @@ -32,11 +30,11 @@ class TagsWidget(QtWidgets.QWidget): - """ - A Widget for displaying and editing Tags.Includes checkboxes for the given - list of standard tag options, and a textfield for the user to enter one or - more custom tags - """ + """A Widget for displaying and editing tags. + + Includes checkboxes for the given list of standard tag options, and a textfield for the + user to enter one or more custom tags.""" + def __init__(self, allowed_tags=None, parent=None): """ A Widget for displaying and editing Tags @@ -127,7 +125,7 @@ def get_tags(self): if self.__enable_custom.isChecked(): tags = str(self.__custom.text()) - tags = re.split('[\s,|]+', tags) + tags = re.split(r'[\s,|]+', tags) else: tags = [str(t.text()) for t in self.standard_tags.checkedBoxes()] return [tag.strip() for tag in tags if tag.strip().isalnum()] diff --git a/cuegui/cuegui/TasksDialog.py b/cuegui/cuegui/TasksDialog.py index 9fabb6f44..f624fcf6e 100644 --- a/cuegui/cuegui/TasksDialog.py +++ b/cuegui/cuegui/TasksDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Handles the dialog to display/modify a show's tasks -""" +"""Dialog to display/modify a show's tasks.""" from __future__ import absolute_import @@ -28,6 +26,8 @@ from PySide2 import QtCore from PySide2 import QtWidgets +import opencue.exception + import cuegui.AbstractTreeWidget import cuegui.AbstractWidgetItem import cuegui.Constants @@ -42,7 +42,9 @@ class TasksDialog(QtWidgets.QDialog): - def __init__(self, show, parent = None): + """Dialog to display/modify a show's tasks.""" + + def __init__(self, show, parent=None): QtWidgets.QDialog.__init__(self, parent) self.__show = show @@ -84,6 +86,7 @@ def __init__(self, show, parent = None): self.getDepartments() def getDepartments(self): + """Gets a list of deparments.""" selected = self.__comboDepartments.currentText() self.__departments = self.__show.getDepartments() @@ -94,15 +97,18 @@ def getDepartments(self): self.__comboDepartments.setCurrentIndex(departmentNames.index(selected)) def setDepartment(self, departmentName): + """Sets the department.""" for department in self.__departments: if department.data.name == departmentName: self.__tasks.setDepartment(department) self.__btnAddTask.setEnabled(not department.data.tiManaged) self.__checkManaged.setChecked(department.data.tiManaged) - self.__btnMinCores.setText(MANAGED_CORES_PREFIX + "%.02f" % department.data.minCores) + self.__btnMinCores.setText( + MANAGED_CORES_PREFIX + "%.02f" % department.data.minCores) return def setMinCores(self): + """Sets the department's min cores.""" __department = self.__tasks.department() if __department: (managedCores, choice) = self.__askManagedCores(__department) @@ -112,10 +118,12 @@ def setMinCores(self): self.getDepartments() def setManaged(self, checked): + """Sets the departments management.""" __department = self.__tasks.department() if not __department.data.tiManaged and checked: title = "Manage Department" - body = "What tiTask should be used to manage the %s department?" % __department.data.name + body = ("What tiTask should be used to manage the %s department?" % + __department.data.name) (tiTask, choice) = QtWidgets.QInputDialog.getText(self, title, body, QtWidgets.QLineEdit.Normal, @@ -126,9 +134,9 @@ def setManaged(self, checked): __department.enableTiManaged(str(tiTask), managedCores) if __department.data.tiManaged and not checked: - if cuegui.Utils.questionBoxYesNo(self, - "Confirm", - "Disable management of the %s department?" % __department.data.name): + if cuegui.Utils.questionBoxYesNo( + self, "Confirm", + "Disable management of the %s department?" % __department.data.name): __department.disableTiManaged() self.getDepartments() @@ -143,9 +151,13 @@ def __askManagedCores(self, department): return (managedCores, choice) def refresh(self): + """Requests a refresh of the tasks list.""" self.__tasks.updateRequest() + class TaskMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Tree widget for displaying a list of tasks.""" + def __init__(self, department, parent): self.startColumnsForType(cuegui.Constants.TYPE_TASK) self.addColumn("Shot", 100, id=1, @@ -165,18 +177,22 @@ def __init__(self, department, parent): self.__menuActions = cuegui.MenuActions.MenuActions( self, self.updateSoon, self.selectedObjects) self._timer.stop() + + self.__department = None self.setDepartment(department) def department(self): + """Gets the department.""" return self.__department def setDepartment(self, department): + """Sets the department.""" self.__department = department self._update() - def _createItem(self, object): + def _createItem(self, rpcObject): """Creates and returns the proper item""" - return TaskWidgetItem(object, self) + return TaskWidgetItem(rpcObject, self) def _update(self): """Adds the feature of forcing the items to be sorted by the first @@ -189,9 +205,8 @@ def _getUpdate(self): try: if self.__department: return self.__department.getTasks() - else: - return [] - except Exception as e: + return [] + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] @@ -211,6 +226,7 @@ def contextMenuEvent(self, e): menu.exec_(e.globalPos()) def createTask(self): + """Creates a task.""" if self.__department: title = "Create Task" body = "What shot is this task for? " @@ -227,7 +243,13 @@ def createTask(self): self.__department.addTask(str(shot), float(minCores)) self._update() + def tick(self): + pass + + class TaskWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget item for displaying a single task.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_TASK, object, parent) + self, cuegui.Constants.TYPE_TASK, rpcObject, parent) diff --git a/cuegui/cuegui/TextEditDialog.py b/cuegui/cuegui/TextEditDialog.py index a53351976..d6a14526d 100644 --- a/cuegui/cuegui/TextEditDialog.py +++ b/cuegui/cuegui/TextEditDialog.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -A TextEdit dialog -""" +"""A TextEdit dialog.""" from __future__ import print_function @@ -27,8 +25,10 @@ class TextEditDialog(QtWidgets.QDialog): - def __init__(self, title, text, default = "", parent = None): - """A confirmation dialog + """A TextEdit dialog.""" + + def __init__(self, title, text, default="", parent=None): + """ @type title: string @param title: The title for the confirmation dialog @type text: string @@ -64,4 +64,5 @@ def __init__(self, title, text, default = "", parent = None): self.__textEdit.setFocus(QtCore.Qt.OtherFocusReason) def results(self): - return self.__textEdit.toPlainText() \ No newline at end of file + """Gets the dialog results as plaintext.""" + return self.__textEdit.toPlainText() diff --git a/cuegui/cuegui/ThreadPool.py b/cuegui/cuegui/ThreadPool.py index 7989df27b..73d791d50 100644 --- a/cuegui/cuegui/ThreadPool.py +++ b/cuegui/cuegui/ThreadPool.py @@ -62,6 +62,7 @@ def systemCpuCount(): """systemCpuCount()->int returns the # of procs on the system, linux only """ + # pylint: disable=bare-except try: return len([p for p in os.listdir("/sys/devices/system/cpu") if p.startswith("cpu")]) except: @@ -69,10 +70,7 @@ def systemCpuCount(): class ThreadPool(QtCore.QObject): - """ThreadPool(QtCore.QObject) - - ThreadPool is a general purpose work queue class - """ + """A general purpose work queue class.""" def __init__(self, num_threads, max_queue=20, parent=None): QtCore.QObject.__init__(self, parent=parent) @@ -86,33 +84,35 @@ def __init__(self, num_threads, max_queue=20, parent=None): self._q_queue = [] def start(self): + """Initializes the thread pool and starts running work.""" if self.__started: return self.__started = True for i in range(0, self.__num_threads): thread = ThreadPool.WorkerThread(i, self) + # pylint: disable=no-member QtGui.qApp.threads.append(thread) + # pylint: enable=no-member self.__threads.append(thread) self.__threads[i].start() self.__threads[i].workComplete.connect(self.runCallback, QtCore.Qt.BlockingQueuedConnection) - def queue(self, callable, callback, comment, *args): - """queue(callable work, callable callback, str comment, * args) - queue up a callable to be run from within a separate thread of execution - """ + def queue(self, callable_to_queue, callback, comment, *args): + """Queues up a callable to be run from within a separate thread of execution.""" self._q_mutex.lock() if not self.__started: self.start() if len(self._q_queue) <= self.__max_queue: - self._q_queue.append((callable,callback,comment,args)) + self._q_queue.append((callable_to_queue, callback, comment, args)) else: - logger.warning("Queue length exceeds %s" % self.__max_queue) + logger.warning("Queue length exceeds %s", self.__max_queue) self._q_mutex.unlock() self._q_empty.wakeAll() - def local(self, callable, callback, comment, *args): - work = (callable, callback, comment, args) + def local(self, callable_to_queue, callback, comment, *args): + """Executes a callable then immediately executes a callback, if given.""" + work = (callable_to_queue, callback, comment, args) if work[3]: result = work[0](*work[3]) else: @@ -120,19 +120,17 @@ def local(self, callable, callback, comment, *args): if work[1]: self.runCallback(work, result) + # pylint: disable=no-self-use def runCallback(self, work, result): - """runCallback(tuple work, object result) - runs the callback function - """ + """Runs the callback function.""" if work[1]: work[1](work, result) class WorkerThread(QtCore.QThread): - """WorkerThread + """A thread for parsing job log files. - A thread for parsing job log files. The log file is parsed - using SpiCue.cueprofile and emits a "parsingComplete" signal - when complete. + The log file is parsed using SpiCue.cueprofile and emits a "parsingComplete" signal + when complete. """ workComplete = QtCore.Signal(object, object) @@ -141,23 +139,28 @@ def __init__(self, name, parent): QtCore.QThread.__init__(self, parent) self.__parent = parent self.__name = name + self.__running = False + # pylint: disable=protected-access def run(self): self.__running = True while self.__running: work = None self.__parent._q_mutex.lock() + # pylint: disable=bare-except try: work = self.__parent._q_queue.pop(0) except: self.__parent._q_empty.wait(self.__parent._q_mutex) + # pylint: enable=bare-except self.__parent._q_mutex.unlock() if not work: continue + # pylint: disable=broad-except try: if work[3]: result = work[0](*work[3]) @@ -167,10 +170,12 @@ def run(self): self.workComplete.emit(work, result) del result except Exception as e: - logger.info("Error processing work:' %s ', %s" % (work[2], e)) - logger.info("Done:' %s '" % work[2]) + logger.info("Error processing work:' %s ', %s" , work[2], e) + # pylint: enable=broad-except + logger.info("Done:' %s '", work[2]) logger.debug("Thread Stopping") def stop(self): + """Stops the worker thread.""" self.__running = False self.__parent._q_empty.wakeAll() diff --git a/cuegui/cuegui/UnbookDialog.py b/cuegui/cuegui/UnbookDialog.py index 722ff3ab0..e36d3e6ac 100644 --- a/cuegui/cuegui/UnbookDialog.py +++ b/cuegui/cuegui/UnbookDialog.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Dialog for unbooking frames.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -31,17 +34,22 @@ class UnbookDialog(cuegui.AbstractDialog.AbstractDialog): + """Dialog for unbooking frames.""" + def __init__(self, jobs, parent=None): cuegui.AbstractDialog.AbstractDialog.__init__(self, parent) layout = QtWidgets.QVBoxLayout(self) self.setWindowTitle("Unbook matching frames") - __descriptionLabel = QtWidgets.QLabel("Unbook and optionally kill the matching frames from the following jobs:", self) + __descriptionLabel = QtWidgets.QLabel( + "Unbook and optionally kill the matching frames from the following jobs:", self) self.__show = opencue.api.findShow(jobs[0].data.name.split("-")[0]) - self.__jobs = [job.data.name for job in jobs if job.data.name.startswith(self.__show.data.name)] - self.__subscriptions = [sub.data.name.split(".")[1] for sub in self.__show.getSubscriptions()] + self.__jobs = [ + job.data.name for job in jobs if job.data.name.startswith(self.__show.data.name)] + self.__subscriptions = [ + sub.data.name.split(".")[1] for sub in self.__show.getSubscriptions()] # Show list of jobs selected self.__jobList = QtWidgets.QTextEdit(self) @@ -85,11 +93,12 @@ def __init__(self, jobs, parent=None): self.__memoryRangeBox = self.__createRangeBox(layout, "Memory requirement", "Mb", 32768) # checkbox and LineEdit for amount or range of runtime - self.__runtimeRangeBox = self.__createRangeBox(layout, "Runtime requirement", "Minutes", 10000) + self.__runtimeRangeBox = self.__createRangeBox( + layout, "Runtime requirement", "Minutes", 10000) layout.addWidget(self.__buttons) - def __createRangeBox(self, layout, name, units, max): + def __createRangeBox(self, layout, name, units, max_frame): __group = QtWidgets.QGroupBox(name) __group.setCheckable(True) __group.setChecked(False) @@ -106,7 +115,7 @@ def __createRangeBox(self, layout, name, units, max): __layout.addWidget(__range, 1, 2) __min = QtWidgets.QSpinBox(self) - __min.setRange(0, max) + __min.setRange(0, max_frame) __layout.addWidget(__min, 0, 0) __minLabel = QtWidgets.QLabel(units) @@ -116,7 +125,7 @@ def __createRangeBox(self, layout, name, units, max): __layout.addWidget(__toLabel, 0, 2, QtCore.Qt.AlignHCenter) __max = QtWidgets.QSpinBox(self) - __max.setRange(0, max) + __max.setRange(0, max_frame) __layout.addWidget(__max, 0, 3) __maxLabel = QtWidgets.QLabel(units) @@ -136,7 +145,8 @@ def __createRangeBox(self, layout, name, units, max): return RangeBox(__group, __moreThan, __lessThan, __range, __min, __max) - def handleIntCriterion(self, mixed, convert=None): + @staticmethod + def handleIntCriterion(mixed, convert=None): """handleIntCriterion returns the proper subclass of IntSearchCriterion based on input from the user. There are a few formats which are accepted. @@ -162,16 +172,16 @@ def _convert(val): result = opencue.api.criterion_pb2.LessThanIntegerSearchCriterion( _convert(mixed[2:])) elif mixed.find("-") > -1: - min,max = mixed.split("-", 1) + min_frame, max_frame = mixed.split("-", 1) result = opencue.api.criterion_pb2.InRangeIntegerSearchCriterion( - _convert(min), _convert(max)) + _convert(min_frame), _convert(max_frame)) else: try: result = opencue.api.criterion_pb2.GreaterThanIntegerSearchCriterion( _convert(mixed)) except ValueError: raise Exception("invalid int search input value: " + str(mixed)) - elif issubclass(mixed.__class__, opencue.api.criterion_pb2.IntegerSearchCriterion): + elif issubclass(mixed.__class__, opencue.api.criterion_pb2.EqualsIntegerSearchCriterion): result = mixed elif not mixed: return [] @@ -190,23 +200,20 @@ def accept(self): procSearch.allocs = [str(checkedBox.text()) for checkedBox in self.__matrix.checkedBoxes()] memoryRange = self.__memoryRangeBox.result() if memoryRange: - procSearch.memoryRange = self.handleIntCriterion(memoryRange, lambda mb:(mb*1024)) + procSearch.memoryRange = self.handleIntCriterion(memoryRange, lambda mb: (mb*1024)) runtimeRange = self.__runtimeRangeBox.result() if runtimeRange: - procSearch.durationRange = self.handleIntCriterion(runtimeRange, lambda min:(min*60)) + procSearch.durationRange = self.handleIntCriterion( + runtimeRange, lambda rangeMin: (rangeMin*60)) if self.__redirect.isChecked(): # Select the show to redirect to title = "Select show" body = "Redirect to what show?" - shows = dict([(show.data.name, show) for show in opencue.api.getActiveShows()]) + shows = {show.data.name: show for show in opencue.api.getActiveShows()} items = [self.__jobs[0].split("-")[0]] + sorted(shows.keys()) - (show, choice) = QtWidgets.QInputDialog.getItem(self, - title, - body, - items, - 0, - False) + (show, choice) = QtWidgets.QInputDialog.getItem( + self, title, body, items, 0, False) if not choice: return show = shows[str(show)] @@ -215,23 +222,19 @@ def accept(self): title = "Select Redirection Type" body = "Redirect to a job or a group?" items = ["Job", "Group"] - (redirectTo, choice) = QtWidgets.QInputDialog.getItem(self, - title, - body, - items, - 0, - False) + (redirectTo, choice) = QtWidgets.QInputDialog.getItem( + self, title, body, items, 0, False) if not choice: return job = group = None if redirectTo == "Job": - jobs = dict([(job.data.name, job) for job in opencue.api.getJobs( - show=[show.data.name])]) - dialog = SelectItemsWithSearchDialog(self, - "Redirect to which job?", - list(jobs.keys()), - QtWidgets.QAbstractItemView.SingleSelection) + jobs = {job.data.name: job for job in opencue.api.getJobs(show=[show.data.name])} + dialog = SelectItemsWithSearchDialog( + self, + "Redirect to which job?", + list(jobs.keys()), + QtWidgets.QAbstractItemView.SingleSelection) dialog.exec_() selected = dialog.selected() if selected: @@ -242,13 +245,9 @@ def accept(self): elif redirectTo == "Group": title = "Select Redirection Group" body = "Redirect to which group?" - groups = dict([(group.data.name, group) for group in show.getGroups()]) - (group, choice) = QtWidgets.QInputDialog.getItem(self, - title, - body, - sorted(groups.keys()), - 0, - False) + groups = {group.data.name: group for group in show.getGroups()} + (group, choice) = QtWidgets.QInputDialog.getItem( + self, title, body, sorted(groups.keys()), 0, False) if not choice: return group = groups[str(group)] @@ -265,7 +264,7 @@ def accept(self): elif group: proc.redirectToGroup(group, kill) amount += 1 - except Exception: + except opencue.exception.CueException: pass self.__informationBox("Redirected procs", "Number of redirected procs: %d" % amount) @@ -278,7 +277,14 @@ def accept(self): if dialog.result(): self.close() else: - amount = opencue.api.unbookProcs(procSearch, False) + procs = opencue.api.getProcs(procSearch) + amount = 0 + for proc in procs: + try: + proc.unbook() + amount += 1 + except opencue.exception.CueException: + pass self.__informationBox("Unbooked frames", "Number of frames unbooked: %d" % amount) self.close() @@ -291,6 +297,8 @@ def __informationBox(self, title, message): class SelectItemsWithSearchDialog(cuegui.AbstractDialog.AbstractDialog): + """Dialog for selecting items via search.""" + def __init__(self, parent, header, items, selectionMode=QtWidgets.QAbstractItemView.MultiSelection): cuegui.AbstractDialog.AbstractDialog.__init__(self, parent) @@ -310,14 +318,17 @@ def __init__(self, parent, header, items, self.layout().addWidget(self.__buttons) def selected(self): + """Gets whether the item is selected.""" if self.result(): return self.__widget.selected() return None class SelectItemsWithSearchWidget(QtWidgets.QWidget): - def __init__(self, parent, header, items, - selectionMode=QtWidgets.QAbstractItemView.MultiSelection): + """Widget for selecting items via search.""" + + def __init__( + self, parent, header, items, selectionMode=QtWidgets.QAbstractItemView.MultiSelection): QtWidgets.QWidget.__init__(self, parent) QtWidgets.QGridLayout(self) @@ -339,11 +350,14 @@ def __init__(self, parent, header, items, self.filterJobs(None) def filterJobs(self, text): + """Filter the list of jobs by text match.""" self.__list.clear() - items = [item for item in self.__items if not text or re.search(str(text), item, re.IGNORECASE)] + items = [ + item for item in self.__items if not text or re.search(str(text), item, re.IGNORECASE)] self.__list.addItems(items) def selected(self): + """Gets whether the item is selected.""" selected = [] for num in range(self.__list.count()): if self.__list.item(num).isSelected(): @@ -354,15 +368,16 @@ def selected(self): class RangeBox(object): """Stores the parts the make up the range box and provides a way to query for the result""" - def __init__(self, group, moreThan, lessThan, range, min, max): + def __init__(self, group, moreThan, lessThan, rangeButton, minBox, maxBox): self.__group = group self.__moreThan = moreThan self.__lessThan = lessThan - self.__range = range - self.__min = min - self.__max = max + self.__range = rangeButton + self.__min = minBox + self.__max = maxBox def result(self): + """Gets the formatted string result.""" if self.__group.isChecked(): if self.__moreThan.isChecked(): return "gt%d" % self.__min.value() @@ -374,6 +389,8 @@ def result(self): class KillConfirmationDialog(QtWidgets.QDialog): + """Dialog for confirming frames should be killed.""" + def __init__(self, procSearch, parent=None): QtWidgets.QDialog.__init__(self, parent) layout = QtWidgets.QVBoxLayout(self) @@ -394,7 +411,9 @@ def __init__(self, procSearch, parent=None): # Show list of jobs selected self.__jobList = QtWidgets.QTextEdit(self) - self.__jobList.setText("\n".join(["%s %s" % (proc.data.jobName, proc.data.frameName) for proc in self.__procs])) + self.__jobList.setText( + "\n".join( + ["%s %s" % (proc.data.jobName, proc.data.frameName) for proc in self.__procs])) self.__jobList.setReadOnly(True) self.__jobList.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)) @@ -412,10 +431,11 @@ def __init__(self, procSearch, parent=None): self.__buttons.rejected.connect(self.reject) def accept(self): + """Kills the procs.""" for proc in self.__procs: try: proc.kill() - except Exception: + except opencue.exception.CueException: pass if self.__amount == 1: diff --git a/cuegui/cuegui/Utils.py b/cuegui/cuegui/Utils.py index f41076f09..98ef4a6fb 100644 --- a/cuegui/cuegui/Utils.py +++ b/cuegui/cuegui/Utils.py @@ -13,9 +13,7 @@ # limitations under the License. -""" -Utility functions. -""" +"""Utility functions.""" from __future__ import print_function @@ -32,6 +30,7 @@ import sys import time import traceback +import webbrowser from PySide2 import QtCore from PySide2 import QtGui @@ -53,8 +52,10 @@ __USERNAME = None -def questionBoxYesNo(parent, title, text, items = []): - """A simple yes/no alert box +# pylint: disable=dangerous-default-value +def questionBoxYesNo(parent, title, text, items=[]): + """A simple yes/no alert box. + @type parent: QObject @param parent: The parent for this object @type title: string @@ -68,33 +69,35 @@ def questionBoxYesNo(parent, title, text, items = []): def countObjectTypes(objects): + """Given a list of objects, returns a count of how many of each type there are.""" results = {"rootgroup": 0, "group": 0, "job": 0} - for object in objects: - if isJob(object): + for obj in objects: + if isJob(obj): results["job"] += 1 - elif isGroup(object): + elif isGroup(obj): results["group"] += 1 - elif isRootGroup(object): + elif isRootGroup(obj): results["rootgroup"] += 1 results["total"] = len(objects) return results def countJobTypes(objects): + """Given a list of jobs, returns a count of how many jobs have each status.""" results = {"paused": False, "unpaused": False, "hasDead": False, "autoEating": False, "notEating": False} - for object in objects: - if isJob(object): - if isinstance(object, opencue.wrappers.job.NestedJob): - object = object.asJob() - if object.data.is_paused: + for obj in objects: + if isJob(obj): + if isinstance(obj, opencue.wrappers.job.NestedJob): + obj = obj.asJob() + if obj.data.is_paused: results["paused"] = True else: results["unpaused"] = True - if object.data.job_stats.dead_frames: + if obj.data.job_stats.dead_frames: results["hasDead"] = True - if object.data.auto_eat: + if obj.data.auto_eat: results["autoEating"] = True else: results["notEating"] = True @@ -102,81 +105,83 @@ def countJobTypes(objects): def qvarToString(qv): - """converts a QVariant to a python string""" + """converts a QVariant to a python string.""" return str(qv) def qvarToFloat(qv): - """converts a Qvariant to a python float""" + """converts a Qvariant to a python float.""" return float(qv) -def isJob(object): +def isJob(obj): """Returns true of the object is a job, false if not @return: If the object is a job @rtype: bool""" - return object.__class__.__name__ in ["Job", "NestedJob"] + return obj.__class__.__name__ in ["Job", "NestedJob"] -def isLayer(object): +def isLayer(obj): """Returns true if the object is a layer, false if not @return: If the object is a layer @rtype: bool""" - return object.__class__.__name__ == "Layer" + return obj.__class__.__name__ == "Layer" -def isFrame(object): +def isFrame(obj): """Returns true if the object is frame, false if not @return: If the object is a frame @rtype: bool""" - return object.__class__.__name__ == "Frame" + return obj.__class__.__name__ == "Frame" -def isShow(object): +def isShow(obj): """Returns true if the object is a show, false if not @return: If the object is a show @rtype: bool""" - return object.__class__.__name__ == "Show" + return obj.__class__.__name__ == "Show" -def isRootGroup(object): +def isRootGroup(obj): """Returns true if the object is a root, false if not @return: If the object is a root group @rtype: bool""" - return isinstance(object, opencue.wrappers.group.NestedGroup) and not object.hasParent() + return isinstance(obj, opencue.wrappers.group.NestedGroup) and not obj.hasParent() -def isGroup(object): +def isGroup(obj): """Returns true if the object is a group, false if not @return: If the object is a group @rtype: bool""" + # isinstance is needed here due to NestedGroup's inheritance + # pylint: disable=unidiomatic-typecheck return ( - type(object) == opencue.wrappers.group.Group or - (isinstance(object, opencue.wrappers.group.NestedGroup) and object.hasParent())) + type(obj) == opencue.wrappers.group.Group or + (isinstance(obj, opencue.wrappers.group.NestedGroup) and obj.hasParent())) -def isHost(object): +def isHost(obj): """Returns true of the object is a host, false if not @return: If the object is a host @rtype: bool""" - return object.__class__.__name__ in ["NestedHost", "Host"] + return obj.__class__.__name__ in ["NestedHost", "Host"] -def isProc(object): +def isProc(obj): """Returns true if the object is a proc, false if not @return: If the object is a proc @rtype: bool""" - return object.__class__.__name__ in ["Proc", "NestedProc"] + return obj.__class__.__name__ in ["Proc", "NestedProc"] -def isTask(object): +def isTask(obj): """Returns true if the object is a task, false if not @return: If the object is a task @rtype: bool""" - return object.__class__.__name__ == "Task" + return obj.__class__.__name__ == "Task" -__REGEX_ID = re.compile("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}") +__REGEX_ID = re.compile(r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}") def isStringId(value): @@ -188,6 +193,7 @@ def isStringId(value): return __REGEX_ID.match(value) +# pylint: disable=broad-except def getCuewho(show): """Returns the username that is cuewho for the given show @param show: Show name @@ -198,10 +204,11 @@ def getCuewho(show): file = open("/shots/%s/home/cue/cuewho.who" % show, "r") return file.read() except Exception as e: - logger.warning("Failed to update cuewho: %s\n%s" % (show, e)) + logger.warning("Failed to update cuewho: %s\n%s", show, e) return "Unknown" +# pylint: disable=global-statement def getUsername(): """Returns the username that this process is running under""" global __USERNAME @@ -210,19 +217,19 @@ def getUsername(): return __USERNAME +# pylint: disable=broad-except def getExtension(username): + """Gets a user's phone extension.""" try: - # TODO: Replace this with a direct call to the phone util that the - # phone widget uses once code is stable results = subprocess.check_output(['phone', username]) for line in results.splitlines(): - if line.find('Extension') != -1 and len(line.split()) == 2: + if 'Extension' in line and len(line.split()) == 2: return line.split()[-1] return "Unknown" except Exception as e: - logger.warning("Failed to update extension: %s\n%s" % (username, e)) + logger.warning("Failed to update extension: %s\n%s", username, e) return "" @@ -252,17 +259,18 @@ def findJob(job): return None if isStringId(job): return opencue.api.getJob(job) - if not re.search("^([a-z0-9\_]+)\-([a-z0-9\.\_]+)\-", job, re.IGNORECASE): + if not re.search(r"^([a-z0-9\_]+)\-([a-z0-9\.\_]+)\-", job, re.IGNORECASE): return None try: return opencue.api.findJob(job) except Exception as e: - logger.warning("Error loading job: %s" % job) - logger.debug("Error loading job: %s\n%s" % (job, e)) + logger.warning("Error loading job: %s", job) + logger.debug("Error loading job: %s\n%s", job, e) return None def shellOut(cmd): + """Runs a command in an external shell.""" os.system("%s &" % cmd) @@ -308,6 +316,7 @@ def exceptionOutput(e): def handleExceptions(function): + """Custom exception handler.""" def new(*args): try: return function(*args) @@ -320,9 +329,9 @@ def __splitTime(sec): """Takes an amount of seconds and returns a tuple for hours, minutes and seconds. @rtype: tuple(int, int, int) @return: A tuple that contains hours, minutes and seconds""" - min, sec = divmod(sec, 60) - hour, min = divmod(min, 60) - return (hour, min, sec) + minutes, sec = divmod(sec, 60) + hour, minutes = divmod(minutes, 60) + return (hour, minutes, sec) def secondsToHHMMSS(sec): @@ -370,14 +379,14 @@ def dateToMMDDHHMM(sec): return time.strftime("%m/%d %H:%M", time.localtime(sec)) -def memoryToString(kmem, unit = None): +def memoryToString(kmem, unit=None): + """Returns an amount of memory in a human-friendly string.""" k = 1024 if unit == "K" or not unit and kmem < k: return "%dK" % kmem - if unit == "M" or not unit and kmem < pow(k,2): + if unit == "M" or not unit and kmem < pow(k, 2): return "%dM" % (kmem // k) - if unit == "G" or not unit and kmem < pow(k,3): - return "%.01fG" % (float(kmem) / pow(k,2)) + return "%.01fG" % (float(kmem) / pow(k, 2)) def getResourceConfig(path=None): @@ -391,7 +400,6 @@ def getResourceConfig(path=None): @return: The entries in the given yaml file @rtype: dict """ - config = {} if not path: path = '{}/cue_resources.yaml'.format(cuegui.Constants.DEFAULT_INI_PATH) @@ -399,8 +407,7 @@ def getResourceConfig(path=None): with open(path, 'r') as fileObject: config = yaml.load(fileObject, Loader=yaml.SafeLoader) except (IOError, ScannerError) as e: - print ('WARNING: Could not read config file %s: %s' - % (path, e)) + print('WARNING: Could not read config file %s: %s' % (path, e)) return config @@ -409,12 +416,15 @@ def getResourceConfig(path=None): ################################################################################ def getFrameLogFile(job, frame): + """Get the log file associated with a frame.""" return os.path.join(job.data.log_dir, "%s.%s.rqlog" % (job.data.name, frame.data.name)) def getFrameLLU(job, frame): + """Get a frame's last log update time.""" __now = time.time() if __now - getattr(frame, "getFrameLLUTime", 0) >= 5: + # pylint: disable=bare-except try: frame.getFrameLLU = __now - os.stat(getFrameLogFile(job, frame)).st_mtime except: @@ -424,6 +434,7 @@ def getFrameLLU(job, frame): def getFrameLastLine(job, frame): + """Get the last line of a frame log.""" __now = time.time() if __now - getattr(frame, "getFrameLastLineTime", 0) >= 5: frame.getFrameLastLine = getLastLine(getFrameLogFile(job, frame)) @@ -432,11 +443,12 @@ def getFrameLastLine(job, frame): def getLastLine(path): - """Reads the last line from the file""" - ansiEscape = '(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]' + """Reads the last line from the file.""" + + ansiEscape = r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]' try: - fp=open(path, 'r') + fp = open(path, 'r') fp.seek(0, 2) backseek = min(4096, fp.tell()) @@ -454,47 +466,55 @@ def getLastLine(path): def popupTail(file, facility=None): + """Opens an xterm window showing the tail of the given file.""" if file and not popupWeb(file, facility): - JOB_LOG_CMD = "/usr/bin/xterm -sb -sl 4096 -n RQLOG -geometry 200x50+0+0 -title %s -e '/usr/bin/tail -n+0 -f %s'" % (os.path.basename(file), file) - shellOut(JOB_LOG_CMD) + job_log_cmd = ( + "/usr/bin/xterm -sb -sl 4096 -n RQLOG -geometry 200x50+0+0 -title %s " + "-e '/usr/bin/tail -n+0 -f %s'" % (os.path.basename(file), file)) + shellOut(job_log_cmd) def popupView(file, facility=None): + """Opens the given file in your editor.""" if file and not popupWeb(file, facility): editor_from_env = os.getenv('EDITOR') + # pylint: disable=no-member if editor_from_env: job_log_cmd = editor_from_env.split() elif QtGui.qApp.settings.contains('LogEditor'): job_log_cmd = QtGui.qApp.settings.value("LogEditor") else: job_log_cmd = cuegui.Constants.DEFAULT_EDITOR.split() + # pylint: enable=no-member job_log_cmd.append(str(file)) checkShellOut(job_log_cmd) def openURL(url): - import webbrowser + """Opens a URL.""" webbrowser.open_new(url) return True def popupWeb(file, facility=None): + """Opens a web browser.""" client = os.getenv('FACILITY', 'unknown') if client in ['yvr'] or (facility == 'yvr' and client in ['lax']): - import webbrowser webbrowser.open_new('' + file) return True return False -def popupFrameTail(job, frame, logNumber = 0): +def popupFrameTail(job, frame, logNumber=0): + """Opens a tail of a frame log.""" path = getFrameLogFile(job, frame) if logNumber: path += ".%s" % logNumber popupTail(path, job.data.facility) -def popupFrameView(job, frame, logNumber = 0): +def popupFrameView(job, frame, logNumber=0): + """Opens a frame.""" path = getFrameLogFile(job, frame) if logNumber: path += ".%s" % logNumber @@ -502,6 +522,7 @@ def popupFrameView(job, frame, logNumber = 0): def popupFrameXdiff(job, frame1, frame2, frame3 = None): + """Opens a frame xdiff.""" for command in ['/usr/bin/xxdiff', '/usr/local/bin/xdiff']: if os.path.isfile(command): @@ -511,50 +532,14 @@ def popupFrameXdiff(job, frame1, frame2, frame3 = None): shellOut(command) -def getOutputFromLayers(job, layers): - """Returns the output paths from the frame logs - @type job: Job - @param job: A job object - @type layers: list - @param layers: A list of at least one later - @rtype: list - @return: The path to the proxy SVI or the primary output.""" - paths = [] - for layer in layers: - svi_found = False - outputs = layer.getOutputPaths() - if outputs: - for path in outputs: - if path.find("_svi") != -1: - paths.append(path) - svi_found = True - break - if not svi_found: - paths.append(outputs[0]) - return paths - - -def getOutputFromFrame(job, layer, frame): - """Returns the output paths from a single frame - @type job: Job - @param job: A job object - @type frame: Frame - @param frame: This frame's log is checked for known output paths - @rtype: list - @return: A list of output paths""" - try: - main_output = layer.getOutputPaths()[0] - main_output = main_output.replace("#", "%04d" % frame.data.number) - return [main_output] - except IndexError: - return [] - - ################################################################################ # Drag and drop functions ################################################################################ def startDrag(dragSource, dropActions, objects): + """Event handler for when a drag starts.""" + del dropActions + mimeData = QtCore.QMimeData() mimeData.setText("\n".join(["%s" % job.data.name for job in objects])) @@ -587,39 +572,45 @@ def startDrag(dragSource, dropActions, objects): drag.exec_(QtCore.Qt.MoveAction) -def dragEnterEvent(event, format = "application/x-job-names"): - if event.mimeData().hasFormat(format): +def dragEnterEvent(event, mime_format="application/x-job-names"): + """Event handler for when a drag enters an area.""" + if event.mimeData().hasFormat(mime_format): event.accept() else: event.ignore() -def dragMoveEvent(event, format = "application/x-job-names"): - if event.mimeData().hasFormat(format): +def dragMoveEvent(event, mime_format="application/x-job-names"): + """Event handler for when a drag is moved.""" + if event.mimeData().hasFormat(mime_format): event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.ignore() -def dropEvent(event, format = "application/x-job-names"): - if event.mimeData().hasFormat(format): - item = event.mimeData().data(format) +# pylint: disable=inconsistent-return-statements +def dropEvent(event, mime_format="application/x-job-names"): + """Event handler for when a drop occurs.""" + if event.mimeData().hasFormat(mime_format): + item = event.mimeData().data(mime_format) stream = QtCore.QDataStream(item, QtCore.QIODevice.ReadOnly) names = stream.readQString() event.accept() return [name for name in str(names).split(":") if name] -def mimeDataAdd(mimeData, format, objects): +def mimeDataAdd(mimeData, mimeFormat, objects): + """Sets mime data.""" data = QtCore.QByteArray() stream = QtCore.QDataStream(data, QtCore.QIODevice.WriteOnly) text = ":".join(objects) stream.writeQString(text) - mimeData.setData(format, data) + mimeData.setData(mimeFormat, data) def showErrorMessageBox(text, title="ERROR!", detailedText=None): + """Displays an error dialog.""" messageBox = QtWidgets.QMessageBox() messageBox.setIcon(QtWidgets.QMessageBox.Critical) messageBox.setText(text) @@ -629,7 +620,8 @@ def showErrorMessageBox(text, title="ERROR!", detailedText=None): messageBox.setStandardButtons(QtWidgets.QMessageBox.Close) return messageBox.exec_() + def shutdownThread(thread): - """Shutdown a WorkerThread.""" + """Shuts down a WorkerThread.""" thread.stop() return thread.wait(1500) diff --git a/cuegui/cuegui/__init__.py b/cuegui/cuegui/__init__.py index 196c475ba..d9c80f13d 100644 --- a/cuegui/cuegui/__init__.py +++ b/cuegui/cuegui/__init__.py @@ -10,4 +10,4 @@ # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -# limitations under the License. \ No newline at end of file +# limitations under the License. diff --git a/cuegui/cuegui/__main__.py b/cuegui/cuegui/__main__.py index fcba4cb3f..770f951e2 100755 --- a/cuegui/cuegui/__main__.py +++ b/cuegui/cuegui/__main__.py @@ -15,6 +15,9 @@ # limitations under the License. +"""Entrypoint for the CueGUI application.""" + + from __future__ import absolute_import from __future__ import print_function from __future__ import division @@ -25,9 +28,9 @@ def main(): - cuegui.Main.cuecommander(sys.argv) + """Entrypoint for the CueGUI application.""" + cuegui.Main.cuecommander(sys.argv) if __name__ == '__main__': - main() - + main() diff --git a/cuegui/cuegui/eta.py b/cuegui/cuegui/eta.py index 0975f8b15..3a91a77e8 100644 --- a/cuegui/cuegui/eta.py +++ b/cuegui/cuegui/eta.py @@ -15,6 +15,9 @@ # limitations under the License. +"""Functions for estimating time remaining on a frame.""" + + from __future__ import division from __future__ import print_function from __future__ import absolute_import @@ -31,29 +34,29 @@ import opencue -############################################### -# Parses log files and job data for ETC info -############################################### - class FrameEtaGenerator(object): + """Parses log files and job data for ETC info.""" + def __init__(self): self.buildTimeCache = {} self.startTimeCache = {} - self.frame_results={'time_left':0,'total_completion':0} - self.time_left=0 - self.total_completion=0 - self.scene_build_time=0 - self.scene_build_seconds=0 - self.percents=[] - self.simTimes=[] - self.log='' - self.log_lines=0 - + self.frame_results = {'time_left': 0, 'total_completion': 0} + self.time_left = 0 + self.total_completion = 0 + self.scene_build_time = 0 + self.scene_build_seconds = 0 + self.percents = [] + self.simTimes = [] + self.log = '' + self.log_lines = 0 + + # pylint: disable=bare-except def GetFrameEta(self, job, frame): + """Gets ETA for the given frame.""" self.log = opencue.util.logPath(job, frame) if os.path.isfile(self.log): - self.log_lines=len(open(self.log).readlines()) - buildTime = self.GetFrameBuildTime(frame) + self.log_lines = len(open(self.log).readlines()) + self.GetFrameBuildTime(frame) try: layer = opencue.api.findLayer(job.data.name, frame.data.layer_name) if 'tango' in layer.data.services: @@ -64,118 +67,128 @@ def GetFrameEta(self, job, frame): self.Arnold(frame) except: pass - self.frame_results['time_left']=self.time_left - self.frame_results['total_completion']=self.total_completion - self.frame_results['scene_build_time']=self.scene_build_time - self.frame_results['scene_build_seconds']=self.scene_build_seconds + self.frame_results['time_left'] = self.time_left + self.frame_results['total_completion'] = self.total_completion + self.frame_results['scene_build_time'] = self.scene_build_time + self.frame_results['scene_build_seconds'] = self.scene_build_seconds try: - self.frame_results['percent_complete']=self.percents[0][0] + self.frame_results['percent_complete'] = self.percents[0][0] except: - self.frame_results['percent_complete']=0 + self.frame_results['percent_complete'] = 0 linecache.clearcache() return self.frame_results - def Tango(self, frame): - simTimes=[] + """Calculates ETA for a Tango frame.""" + del frame + + simTimes = [] if os.path.isfile(self.log): - line='' - line_counter=0 + line = '' + line_counter = 0 while 'Done with Frame' not in line: - line=linecache.getline(self.log,self.log_lines-line_counter) - line_counter+=1 + line = linecache.getline(self.log, self.log_lines-line_counter) + line_counter += 1 if line_counter == self.log_lines: break frameTime = line.split("=")[1].split(",")[0].strip(" ") # Extract frame time if float(frameTime) > 0: - simTimes.append(float(frameTime)) + simTimes.append(float(frameTime)) lastFrame = line.split('#')[1].split(".")[0].strip(" ") # Extract the last frame - line_counter=100 - line='' + line_counter = 100 + line = '' while 'Loading XML file:' not in line: - line=linecache.getline(self.log,line_counter) - line_counter+=1 + line = linecache.getline(self.log, line_counter) + line_counter += 1 if line_counter == self.log_lines: break xml_loc = line.split(":")[1].strip(" ").rstrip("\n") # Extract the XML from the line start_frame, end_frame = self.GetSimFrameRange(xml_loc) # Get the start and end points - simTimes=sorted(simTimes,reverse=True) + simTimes = sorted(simTimes, reverse=True) framesLeft = int(end_frame) - int(lastFrame) self.time_left = float(simTimes[0]) * float(framesLeft) - self.percents.append(((float(start_frame)/int(end_frame))*100,self.time_left)) - self.percents.append((0,0)) - self.percents=sorted(self.percents, reverse=True) + self.percents.append(((float(start_frame) / int(end_frame)) * 100, self.time_left)) + self.percents.append((0, 0)) + self.percents = sorted(self.percents, reverse=True) if len(self.percents) > 1: - self.total_completion = (self.percents[0][1] - self.percents[-1][1]) * (100 / (self.percents[0][0] - self.percents[-1][0])) - + self.total_completion = ( + (self.percents[0][1] - self.percents[-1][1]) * + (100 / (self.percents[0][0] - self.percents[-1][0]))) + def Svea(self, frame): + """Calculates ETA for a Svea frame.""" + del frame if os.path.isfile(self.log): - line='' + line = '' for line in reversed(open(self.log).readlines()): - #checks log directory for a percentage complete in reverse to limit time in log + # Checks log directory for a percentage complete in reverse to limit time in log. if 'Running generator batch' in line: + # pylint: disable=bare-except try: - time_on_log=self.GetSeconds(line) - line=line.split(' ') - current=float(line[16]) - total=float(line[18].split('\n')[0]) - percent=float(current / total) * 100 - self.percents.append((percent,time_on_log)) + time_on_log = self.GetSeconds(line) + line = line.split(' ') + current = float(line[16]) + total = float(line[18].split('\n')[0]) + percent = float(current / total) * 100 + self.percents.append((percent, time_on_log)) if len(self.percents) > 1: break except: pass if len(self.percents) > 1: self.percents = sorted(self.percents, reverse=True) - self.total_completion = (self.percents[0][1] - self.percents[-1][1]) * (100 / (self.percents[0][0] - self.percents[-1][0])) - self.time_passed = self.percents[0][1] + self.total_completion = ( + (self.percents[0][1] - self.percents[-1][1]) * + (100 / (self.percents[0][0] - self.percents[-1][0]))) self.time_left = self.total_completion * ((100-self.percents[0][0]) / 100) else: - self.percents.append((0,0)) + self.percents.append((0, 0)) def Arnold(self, frame): + """Calculates ETA for an Arnold frame.""" if os.path.isfile(self.log): buildTime = self.GetFrameBuildTime(frame) - self.scene_build_seconds=buildTime['scene_build_seconds'] - self.scene_build_time=buildTime['scene_build_time'] - if self.scene_build_seconds!=0: - #doesn't look for percentages if it can't find a scenebuild + self.scene_build_seconds = buildTime['scene_build_seconds'] + self.scene_build_time = buildTime['scene_build_time'] + if self.scene_build_seconds != 0: + # Doesn't look for percentages if it can't find a scenebuild. line = self.GetFrameStartTime(frame) - if line !='': - #doesn't look for anything else if it can't find a first % + if line != '': + # Doesn't look for anything else if it can't find a first %. self.GetPercent(line) - line_counter=0 - line='' + line_counter = 0 + line = '' while '% done' not in line: - line=linecache.getline(self.log,self.log_lines-line_counter) - line_counter+=1 + line = linecache.getline(self.log,self.log_lines-line_counter) + line_counter += 1 if line_counter == self.log_lines: break if line_counter != self.log_lines: self.GetPercent(line) if len(self.percents) > 1: - self.percents=sorted(self.percents, reverse=True) - if len(self.percents)==1 and self.percents[0][0] % 5 == 0: - self.total_completion=(self.percents[0][1])*(20) + self.percents = sorted(self.percents, reverse=True) + if len(self.percents) == 1 and self.percents[0][0] % 5 == 0: + self.total_completion = self.percents[0][1] * 20 else: - if self.percents[0][0]==self.percents[-1][0]: + if self.percents[0][0] == self.percents[-1][0]: self.percents[-1]=(self.percents[0][0]-5,self.scene_build_seconds) - self.total_completion = (self.percents[0][1] - self.percents[-1][1]) * (100 / (self.percents[0][0] - self.percents[-1][0])) - time_passed=self.percents[0][1] + self.total_completion = ( + (self.percents[0][1] - self.percents[-1][1]) * + (100 / (self.percents[0][0] - self.percents[-1][0]))) self.time_left = self.total_completion * ((100 - self.percents[0][0]) / 100) else: - self.percents.append((0,0)) - + self.percents.append((0, 0)) def GetFrameStartTime(self, frame): + """Gets a frame start time.""" key = (frame, frame.data.start_time) if key in self.startTimeCache: return self.startTimeCache[key] - # read the logFile here for time - result='' + # Read the logFile here for time. + result = '' for line in open(self.log): - if '% done'in line: - result=line + if '% done' in line: + result = line break if not result: return result @@ -183,46 +196,53 @@ def GetFrameStartTime(self, frame): return result def GetFrameBuildTime(self, frame): + """Gets a frame build time.""" key = (frame, frame.data.start_time) if key in self.buildTimeCache: return self.buildTimeCache[key] - # read the logFile here for time - result='' + # Read the logFile here for time. + result_line = None for line in open(self.log): - if 'Building scene done'in line: - result=line + if 'Building scene done' in line: + result_line = line break - if result != '': - result={'scene_build_seconds':self.GetSeconds(line), 'scene_build_time':line.split(' ')[3]} + if result_line is not None: + result = { + 'scene_build_seconds': self.GetSeconds(result_line), + 'scene_build_time': result_line.split(' ')[3]} else: - result={'scene_build_seconds':0, 'scene_build_time':0} + result = {'scene_build_seconds': 0, 'scene_build_time': 0} return result if not result: return result self.buildTimeCache[key] = result return result - def GetPercent(self,line): + def GetPercent(self, line): + """Gets a percentage from a given log line.""" + # pylint: disable=bare-except try: - percent_location=line.find('%') - percent=float(line[percent_location-3]+line[percent_location-2]+line[percent_location-1]) - time_on_log=self.GetSeconds(line) - self.percents.append((percent,time_on_log)) + percent_location = line.find('%') + percent = float( + line[percent_location-3] + line[percent_location-2] + line[percent_location-1]) + time_on_log = self.GetSeconds(line) + self.percents.append((percent, time_on_log)) except: pass - - def GetSeconds(self,line): - time_str=re.search('([0-9]+):([0-9]{2}):([0-9]{2})', line) - hour=int(time_str.group(1)) - minute=int(time_str.group(2)) - second=int(time_str.group(3)) - seconds=(hour*3600) + (minute*60) + second + + @staticmethod + def GetSeconds(line): + """Gets a number of seconds from a timestamp found in a log line.""" + time_str = re.search('([0-9]+):([0-9]{2}):([0-9]{2})', line) + hour = int(time_str.group(1)) + minute = int(time_str.group(2)) + second = int(time_str.group(3)) + seconds = (hour * 3600) + (minute * 60) + second return seconds - ############################################################################### - # Reads the SimRender XML to get the frame range - ############################################################################### - def GetSimFrameRange(self, xml_loc): + @staticmethod + def GetSimFrameRange(xml_loc): + """Reads the SimRender XML to get the frame range.""" try: name = xml.dom.minidom.parse(xml_loc) except IOError: @@ -233,25 +253,31 @@ def GetSimFrameRange(self, xml_loc): end_frame = global_tag.getElementsByTagName('end')[0].childNodes[0].nodeValue return start_frame, end_frame + def ETAString(job, frame): - eta=FrameEtaGenerator() - time_left=eta.GetFrameEta(job, frame)['time_left'] + """Calculates ETA and returns it as a formatted string.""" + eta = FrameEtaGenerator() + time_left = eta.GetFrameEta(job, frame)['time_left'] t = datetime.datetime.now() - now_epoch=time.mktime(t.timetuple()) - time_left=datetime.datetime.fromtimestamp(time_left+now_epoch).strftime('%m/%d %H:%M:%S') + now_epoch = time.mktime(t.timetuple()) + time_left = datetime.datetime.fromtimestamp(time_left+now_epoch).strftime('%m/%d %H:%M:%S') return time_left + def ETADateTime(job, frame): - eta=FrameEtaGenerator() - time_left=eta.GetFrameEta(job, frame)['time_left'] + """Calculates ETA and returns it as a datetime.""" + eta = FrameEtaGenerator() + time_left = eta.GetFrameEta(job, frame)['time_left'] t = datetime.datetime.now() - now_epoch=time.mktime(t.timetuple()) - time_left=datetime.datetime.fromtimestamp(time_left+now_epoch) + now_epoch = time.mktime(t.timetuple()) + time_left = datetime.datetime.fromtimestamp(time_left + now_epoch) return time_left + def ETASeconds(job, frame): - eta=FrameEtaGenerator() - time_left=eta.GetFrameEta(job, frame)['time_left'] + """Calculates ETA and returns it as a number of seconds.""" + eta = FrameEtaGenerator() + time_left = eta.GetFrameEta(job, frame)['time_left'] return time_left @@ -261,95 +287,19 @@ def __init__(self, func): self.func = func self.memoized = {} self.method_cache = {} - + def __call__(self, *args): - return self.cache_get(self.memoized, args, - lambda: self.func(*args)) - + return self.__cache_get(self.memoized, args, lambda: self.func(*args)) + def __get__(self, obj, objtype): - return self.cache_get(self.method_cache, obj, - lambda: self.__class__(functools.partial(self.func, obj))) - - def cache_get(self, cache, key, func): + return self.__cache_get( + self.method_cache, obj, lambda: self.__class__(functools.partial(self.func, obj))) + + @staticmethod + def __cache_get(cache, key, func): try: return cache[key] except KeyError: result = func() cache[key] = result return result -""" -class BackwardsReader: - #Read a file line by line, backwards - BLKSIZE = 4096 - - def readline(self): - while 1: - newline_pos = string.rfind(self.buf, "\n") - pos = self.file.tell() - if newline_pos != -1: - # Found a newline - line = self.buf[newline_pos+1:] - self.buf = self.buf[:newline_pos] - if pos != 0 or newline_pos != 0 or self.trailing_newline: - line += "\n" - return line - else: - if pos == 0: - # Start-of-file - return "" - else: - # Need to fill buffer - toread = min(self.BLKSIZE, pos) - self.file.seek(-toread, 1) - self.buf = self.file.read(toread) + self.buf - self.file.seek(-toread, 1) - if pos - toread == 0: - self.buf = "\n" + self.buf - - def __init__(self, file): - self.file = file - self.buf = "" - self.file.seek(-1, 2) - self.trailing_newline = 0 - lastchar = self.file.read(1) - if lastchar == "\n": - self.trailing_newline = 1 - self.file.seek(-1, 2) - - br = BackwardsReader(open(self.log)) - while 1: - line = br.readline() - if '% done'in line: - self.GetPercent(line) - break - if not line: - break - - for line in reversed(open(self.log).readlines()): - if '% done'in line: - self.GetPercent(line) - break - - file=PipeIt('tac %s' % self.log)[0] - for line in file:#os.popen('tac %s' % self.log): PipeIt('tac %s' % self.log)[0]: - if '% done'in line: - self.GetPercent(line) - break - - while '% done' not in line: - line=linecache.getline(self.log,self.log_lines-line_counter) - line_counter+=1 - if line_counter == self.log_lines: - break - if line_counter != self.log_lines: - self.GetPercent(line) - line_counter=0 - line='' - while '% done' not in line: - line=linecache.getline(self.log,line_counter) - line_counter+=1 - if line_counter == self.log_lines: - break - if line_counter != self.log_lines: - self.GetPercent(line) - """ diff --git a/cuegui/cuegui/plugins/AllocationsPlugin.py b/cuegui/cuegui/plugins/AllocationsPlugin.py index 896557a4f..b181a350b 100644 --- a/cuegui/cuegui/plugins/AllocationsPlugin.py +++ b/cuegui/cuegui/plugins/AllocationsPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for managing allocations.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -40,8 +43,10 @@ PLUGIN_REQUIRES = "CueCommander" PLUGIN_PROVIDES = "AllocationsDockWidget" + class AllocationsDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Widget that lists allocations and allows management of them.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) @@ -55,11 +60,15 @@ def __init__(self, parent): self.__monitorAllocations.getColumnOrder, self.__monitorAllocations.setColumnOrder)]) + ################################################################################ # Allocations ################################################################################ + class MonitorAllocations(cuegui.AbstractTreeWidget.AbstractTreeWidget): + """Inner widget that builds and displays the allocation list.""" + def __init__(self, parent): self.startColumnsForType(cuegui.Constants.TYPE_ALLOC) self.addColumn("Name", 150, id=1, @@ -95,25 +104,28 @@ def __init__(self, parent): self.setDragEnabled(True) self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) + # Signals are defined in code, so pylint thinks they don't exist. + # pylint: disable=no-member QtGui.qApp.facility_changed.connect(self._update) + # pylint: enable=no-member self.setUpdateInterval(60) - def _createItem(self, object): + def _createItem(self, rpcObject): """Creates and returns the proper item""" - return AllocationWidgetItem(object, self) + return AllocationWidgetItem(rpcObject, self) + # pylint: disable=no-self-use def _getUpdate(self): """Returns the proper data from the cuebot""" try: return opencue.api.getAllocations() - except Exception as e: + except opencue.exception.CueException as e: list(map(logger.warning, cuegui.Utils.exceptionOutput(e))) return [] def contextMenuEvent(self, e): """When right clicking on an item, this raises a context menu""" - pass def dragEnterEvent(self, event): cuegui.Utils.dragEnterEvent(event, "application/x-host-ids") @@ -135,7 +147,14 @@ def dropEvent(self, event): item.rpcObject.reparentHostIds(hostIds) self.updateSoon() + def tick(self): + # tick is unused in this widget + pass + + class AllocationWidgetItem(cuegui.AbstractWidgetItem.AbstractWidgetItem): - def __init__(self, object, parent): + """Widget element representing a single allocation.""" + + def __init__(self, rpcObject, parent): cuegui.AbstractWidgetItem.AbstractWidgetItem.__init__( - self, cuegui.Constants.TYPE_ALLOC, object, parent) + self, cuegui.Constants.TYPE_ALLOC, rpcObject, parent) diff --git a/cuegui/cuegui/plugins/AttributesPlugin.py b/cuegui/cuegui/plugins/AttributesPlugin.py index c12a4a374..da4693d06 100644 --- a/cuegui/cuegui/plugins/AttributesPlugin.py +++ b/cuegui/cuegui/plugins/AttributesPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin that lists attributes of the selected object.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -26,7 +29,8 @@ from PySide2 import QtWidgets import opencue -import opencue.compiled_proto +import opencue.compiled_proto.depend_pb2 +import opencue.wrappers.job import cuegui.AbstractDockWidget import cuegui.Logger @@ -42,15 +46,18 @@ class AttributesPlugin(cuegui.AbstractDockWidget.AbstractDockWidget): + """Plugin that lists attributes of the selected object.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__( self, parent, PLUGIN_NAME, QtCore.Qt.RightDockWidgetArea) self.__attributes = Attributes(self) self.layout().addWidget(self.__attributes) + def getDependsForm(depends): - """This is a temporary method unil a new widget factory can be made - that creates uber hawt dependency widgets""" + """This is a temporary method until a new widget factory can be made + that creates uber hawt dependency widgets.""" if not depends: return None @@ -72,6 +79,7 @@ def getDependsForm(depends): return result + class Attributes(QtWidgets.QWidget): """Attributes The Attributes widget contains a path or some text @@ -98,10 +106,13 @@ def __init__(self, parent=None): self.__scrollWidget.layout().addWidget(self.__stack) self.__scrollArea.setWidget(self.__scrollWidget) layout.addWidget(self.__scrollArea) + # pylint: disable=no-member QtGui.qApp.single_click.connect(self.setWidget) + # pylint: enable=no-member self.__load = None + # pylint: disable=inconsistent-return-statements def setWidget(self, item): """If the item is a known object, then it will be displayed. @type item: any known object, otherwise ignored @@ -129,15 +140,18 @@ def setWidget(self, item): if hasattr(function, "preload"): if hasattr(QtGui.qApp, "threadpool"): self.__load = {"item": item, "function": function} + # pylint: disable=no-member QtGui.qApp.threadpool.queue(self.__getUpdate, self.__processResults, "getting data for %s" % self.__class__) + # pylint: enable=no-member else: logger.warning("threadpool not found, doing work in gui thread") return self.__createItemAttribute(item, function, function.preload(item)) else: return self.__createItemAttribute(item, function, None) + # pylint: disable=broad-except,inconsistent-return-statements def __getUpdate(self): """Called from the worker thread, gets results from preload""" try: @@ -151,6 +165,7 @@ def __getUpdate(self): def __processResults(self, work, result): """Unpacks the worker thread results and calls function to create widget""" + del work if result: self.__createItemAttribute(result["item"], result["function"], @@ -171,6 +186,12 @@ def __createItemAttribute(self, item, function, preload): class AbstractAttributes(QtWidgets.QTreeWidget): + """Abstract class for listing object attributes. + + Inheriting classes build on this, adding additional logic depending on the type of the + selected object. + """ + def __init__(self, rpcObject, preload, parent=None): QtWidgets.QTreeWidget.__init__(self, parent) @@ -206,6 +227,7 @@ def addData(parent, value): self.itemSelectionChanged.connect(self.itemSingleClickedCopy) def itemSingleClickedCopy(self): + """Event handler that copies the text of the selected line to the clipboard on click.""" selected = self.selectedItems() if selected: @@ -213,13 +235,18 @@ def itemSingleClickedCopy(self): class LayerAttributes(AbstractAttributes): + """Class for listing attributes of a layer.""" + NAME = "LayerAttributes" @staticmethod def preload(layer): + """Prepopulates needed layer information.""" return {"depends": layer.getWhatThisDependsOn()} + # pylint: disable=no-self-use def dataSource(self, layer, preload): + """Returns layer information structured as needed for the attributes list.""" d = { "id": opencue.util.id(layer), "layer": layer.data.name, @@ -263,9 +290,9 @@ def dataSource(self, layer, preload): "depends": getDependsForm(preload["depends"]), } - for num, output in enumerate(layer.getOutputPaths()): # Try to formulate a unique name the output. + # pylint: disable=bare-except try: # Outline only puts outputs in as filespecs, # so we're just going to assume it is. @@ -274,19 +301,26 @@ def dataSource(self, layer, preload): rep = "%s #%d" % (rep, num) except: rep = "output #%d" % num + # pylint: enable=bare-except d["outputs"][rep] = output return d + class JobAttributes(AbstractAttributes): + """Class for listing attributes of a job.""" + NAME = "JobAttributes" @staticmethod def preload(jobObject): + """Prepopulates needed job information.""" return {"depends": jobObject.getWhatThisDependsOn()} + # pylint: disable=no-self-use def dataSource(self, job, preload): + """Returns job information structured as needed for the attributes list.""" if isinstance(job, opencue.wrappers.job.NestedJob): job = job.asJob() d = { @@ -348,6 +382,7 @@ def dataSource(self, job, preload): for num, output in enumerate(outputs): # Try to formulate a unique name the output. + # pylint: disable=bare-except try: # Outline only puts outputs in as filespecs, # so we're just going to assume it is. @@ -356,14 +391,21 @@ def dataSource(self, job, preload): rep = "%s #%d" % (rep, num) except: rep = "output #%d" % num + # pylint: enable=bare-except entry[rep] = output return d + class HostAttributes(AbstractAttributes): + """Class for listing attributes of a host.""" + NAME = "Host" + # pylint: disable=no-self-use def dataSource(self, host, preload): + """Returns host information structured as needed for the attributes list.""" + del preload return {"hostname": host.data.name, "id": opencue.util.id(host), "alloc": host.data.alloc_name, diff --git a/cuegui/cuegui/plugins/LimitsPlugin.py b/cuegui/cuegui/plugins/LimitsPlugin.py index 07565822b..4a5947f54 100644 --- a/cuegui/cuegui/plugins/LimitsPlugin.py +++ b/cuegui/cuegui/plugins/LimitsPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for managing limits.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -29,17 +32,18 @@ class LimitsDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" - def __init__(self, parent): - super(LimitsDockWidget, self).__init__(parent, PLUGIN_NAME) - - self.__limitsWidget = cuegui.LimitsWidget.LimitsWidget(self) - - self.layout().addWidget(self.__limitsWidget) - - self.pluginRegisterSettings([("columnVisibility", - self.__limitsWidget.getColumnVisibility, - self.__limitsWidget.setColumnVisibility), - ("columnOrder", - self.__limitsWidget.getColumnOrder, - self.__limitsWidget.setColumnOrder)]) + """Widget for managing limits.""" + + def __init__(self, parent): + super(LimitsDockWidget, self).__init__(parent, PLUGIN_NAME) + + self.__limitsWidget = cuegui.LimitsWidget.LimitsWidget(self) + + self.layout().addWidget(self.__limitsWidget) + + self.pluginRegisterSettings([("columnVisibility", + self.__limitsWidget.getColumnVisibility, + self.__limitsWidget.setColumnVisibility), + ("columnOrder", + self.__limitsWidget.getColumnOrder, + self.__limitsWidget.setColumnOrder)]) diff --git a/cuegui/cuegui/plugins/LogViewPlugin.py b/cuegui/cuegui/plugins/LogViewPlugin.py index f49957538..fb40734e4 100644 --- a/cuegui/cuegui/plugins/LogViewPlugin.py +++ b/cuegui/cuegui/plugins/LogViewPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for viewing logs.""" + + from __future__ import division from __future__ import print_function from __future__ import absolute_import @@ -86,9 +89,10 @@ def __init__(self, parent): @param parent: The parent widget @type parent: QtWidgets.QWidget """ - super(LogTextEdit, self).__init__(parent) + self._context_menu = None + # Use a Fixed-Width font for easier debugging self.font = self.document().defaultFont() self.font.setFamily('Courier New') @@ -117,7 +121,6 @@ def context_menu(self): A custom context menu to pop up when the user Right-Clicks in the Log View. Triggered by the customContextMenuRequested signal """ - self._context_menu = QtWidgets.QMenu(self) self._context_menu.addAction(self.copy_action) self._context_menu.exec_(QtGui.QCursor.pos()) @@ -155,8 +158,9 @@ def copy_selection(self, mode): Copy (Ctrl + C) action. Stores the currently selected text in the clipboard. - @param mode: The QClipboard mode value (QtGui.QClipboard.Clipboard = GLOBAL - QtGui.QClipboard.Selection = Selection (middle-mouse)) + @param mode: The QClipboard mode value + (QtGui.QClipboard.Clipboard = GLOBAL + QtGui.QClipboard.Selection = Selection (middle-mouse)) @type mode: int """ selection = self.textCursor().selection() @@ -286,9 +290,7 @@ def line_number_area_paint_event(self, event): class LogViewWidget(QtWidgets.QWidget): - """ - Displays the log file for the selected frame - """ + """Displays the log file for the selected frame.""" def __init__(self, parent=None): """ @@ -421,7 +423,10 @@ def __init__(self, parent=None): self._cursor = self._content_box.textCursor() pos = QtCore.QPoint(0, 0) self._highlight_cursor = self._content_box.cursorForPosition(pos) + # Signals are defined in code, so pylint thinks they don't exist. + # pylint: disable=no-member QtGui.qApp.display_log_file_content.connect(self._set_log_files) + # pylint: enable=no-member self._log_scrollbar = self._content_box.verticalScrollBar() self._log_scrollbar.valueChanged.connect(self._set_scrollbar_value) @@ -511,7 +516,7 @@ def _set_log_file(self): log_index_txt = ('Log %s of %s%s' % (str(self._current_log_index+1), len(self._log_files), - (' (current)' if self._current_log_index == 0 + (' (current)' if self._current_log_index == 0 else ''))) self._log_index_label.setText(log_index_txt) @@ -723,10 +728,11 @@ def _clear_search_data(self): if not self._log_file: return - format = QtGui.QTextCharFormat() + charFormat = QtGui.QTextCharFormat() self._highlight_cursor.setPosition(QtGui.QTextCursor.Start) - self._highlight_cursor.movePosition(QtGui.QTextCursor.End, mode=QtGui.QTextCursor.KeepAnchor) - self._highlight_cursor.setCharFormat(format) + self._highlight_cursor.movePosition( + QtGui.QTextCursor.End, mode=QtGui.QTextCursor.KeepAnchor) + self._highlight_cursor.setCharFormat(charFormat) self._highlight_cursor.clearSelection() def _set_scrollbar_value(self, val): @@ -833,14 +839,15 @@ def _update_log(self): # Update the content in the gui (if necessary) current_text = (self._content_box.toPlainText() or '') new_text = content.lstrip(str(current_text)) - [x for x in new_text if x in PRINTABLE] if new_text: if self._new_log: self._content_box.setPlainText(content) else: self._content_box.appendPlainText(new_text) self._content_timestamp = time.time() + # pylint: disable=no-member QtGui.qApp.processEvents() + # pylint: enable=no-member # Adjust scrollbar value (if necessary) self._scrollbar_max = self._log_scrollbar.maximum() @@ -852,7 +859,7 @@ def _update_log(self): class LogViewPlugin(cuegui.AbstractDockWidget.AbstractDockWidget): """ Plugin for displaying the log file content for the selected frame with - the ability to perform regex-based search + the ability to perform regex-based search. """ def __init__(self, parent=None): @@ -869,6 +876,8 @@ def __init__(self, parent=None): class Highlighter(QtGui.QSyntaxHighlighter): + """Color-codes log text according to log content.""" + def __init__(self, parent=None): super(Highlighter, self).__init__(parent) @@ -894,7 +903,6 @@ def __init__(self, parent=None): self.completeFormat.setFontWeight(QtGui.QFont.Bold) self.completeFormat.setForeground(cuegui.Style.ColorTheme.LOG_COMPLETE) - def highlightBlock(self, text): if not self.on: return diff --git a/cuegui/cuegui/plugins/MonitorCuePlugin.py b/cuegui/cuegui/plugins/MonitorCuePlugin.py index f242aa5b9..6f07b80e0 100644 --- a/cuegui/cuegui/plugins/MonitorCuePlugin.py +++ b/cuegui/cuegui/plugins/MonitorCuePlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for general administration of the show/job hierarchy.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -47,10 +50,13 @@ class MonitorCueDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Plugin for general administration of the show/job hierarchy.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) + self.__showMenuActions = None + self.__monitorCue = cuegui.CueJobMonitorTree.CueJobMonitorTree(self) self.__toolbar = QtWidgets.QToolBar(self) self.__showMenuSetup() @@ -72,7 +78,9 @@ def __init__(self, parent): self.layout().addLayout(self.__hlayout) + # pylint: disable=no-member self.__monitorCue.view_object.connect(QtGui.qApp.view_object.emit) + # pylint: enable=no-member self.pluginRegisterSettings([("shows", self.__monitorCue.getShowNames, @@ -90,7 +98,10 @@ def __init__(self, parent): self.addShows([os.getenv('SHOW')]) def __cueStateBarSetup(self, layout): - if QtGui.qApp.settings.value("CueStateBar", False): + # pylint: disable=no-member + cueStateBarEnabled = QtGui.qApp.settings.value("CueStateBar", False) + # pylint: enable=no-member + if cueStateBarEnabled: self.__cueStateBar = cuegui.CueStateBarWidget.CueStateBarWidget(self.__monitorCue, self) layout.addWidget(self.__cueStateBar) @@ -149,9 +160,10 @@ def __buttonSetup(self, layout): btn.clicked.connect(self.__monitorCue.actionResumeSelectedItems) -################################################################################ -# Show selection menu -################################################################################ + ################################################################################ + # Show selection menu + ################################################################################ + def __showMenuSetup(self): """Sets up the show selection menu""" self.__showMenuBtn = QtWidgets.QPushButton("Shows ",self) @@ -164,7 +176,9 @@ def __showMenuSetup(self): self.__showMenuBtn.setFocusPolicy(QtCore.Qt.NoFocus) self.__showMenu.setFont(cuegui.Constants.STANDARD_FONT) self.__showMenu.triggered.connect(self.__showMenuHandle) + # pylint: disable=no-member QtGui.qApp.facility_changed.connect(self.__showMenuUpdate) + # pylint: enable=no-member self.__showMenuUpdate() @@ -205,7 +219,7 @@ def __showMenuUpdate(self): try: shows = sorted([show.name() for show in opencue.api.getActiveShows()]) - except Exception as e: + except opencue.exception.CueException as e: logger.critical(e) shows = [] @@ -310,24 +324,29 @@ def __jobSelectedHandle(self, job): else: self.__jobSelectedLineEdit.setText("") ################################################################################ + def addShows(self, shows): + """Adds a list of shows to be monitored.""" for show in shows: if show in self.__showMenuActions: self.__monitorCue.addShow(show, False) self.__showMenuActions[show].setChecked(True) - def pluginRestoreState(self, settings): + def pluginRestoreState(self, saved_settings): """Called on plugin start with any previously saved state. - @param settings: Last state of the plugin instance - @type settings: any""" - cuegui.AbstractDockWidget.AbstractDockWidget.pluginRestoreState(self, settings) + @param saved_settings: Last state of the plugin instance + @type saved_settings: any""" + cuegui.AbstractDockWidget.AbstractDockWidget.pluginRestoreState(self, saved_settings) + # pylint: disable=protected-access self.__monitorCue._update() + # pylint: enable=protected-access QtCore.QTimer.singleShot(1000, self.__monitorCue.expandAll) class JobSelectEditBox(QtWidgets.QLineEdit): - """An edit box intended for selecting matching jobs""" + """An edit box for selecting matching jobs.""" + def __init__(self, parent): QtWidgets.QLineEdit.__init__(self) self.parent = weakref.proxy(parent) diff --git a/cuegui/cuegui/plugins/MonitorHostsPlugin.py b/cuegui/cuegui/plugins/MonitorHostsPlugin.py index 1bf67bfc3..cee8413fe 100644 --- a/cuegui/cuegui/plugins/MonitorHostsPlugin.py +++ b/cuegui/cuegui/plugins/MonitorHostsPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for viewing the list of hosts and performing administrative tasks.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -33,7 +36,8 @@ class HostMonitorDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Plugin for viewing the list of hosts and performing administrative tasks.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) diff --git a/cuegui/cuegui/plugins/MonitorJobDetailsPlugin.py b/cuegui/cuegui/plugins/MonitorJobDetailsPlugin.py index f163be449..b731736c4 100644 --- a/cuegui/cuegui/plugins/MonitorJobDetailsPlugin.py +++ b/cuegui/cuegui/plugins/MonitorJobDetailsPlugin.py @@ -13,6 +13,11 @@ # limitations under the License. +"""Plugin for listing details of the selected job. + +Job selection is triggered by other plugins using the application's view_object signal.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -40,6 +45,7 @@ class MonitorLayerFramesDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): """This builds a display that can monitor the layers and frames of a job.""" + def __init__(self, parent): """Creates the dock widget and docks it to the parent. @param parent: The main window to dock to @@ -58,14 +64,14 @@ def __init__(self, parent): self.__splitter.addWidget(self.__monitorLayers) self.__splitter.addWidget(self.__monitorFrames) - #Signals in: + # pylint: disable=no-member QtGui.qApp.view_object.connect(self.__setJob) QtGui.qApp.unmonitor.connect(self.__unmonitor) QtGui.qApp.facility_changed.connect(self.__setJob) + # pylint: enable=no-member self.__monitorLayers.handle_filter_layers_byLayer.connect(self.handleLayerFilter) self.__splitter.splitterMoved.connect(self.__splitterMoved) - self.pluginRegisterSettings([("splitterSize", self.__splitter.sizes, self.__splitter.setSizes), @@ -89,9 +95,11 @@ def __init__(self, parent): self.__monitorLayers.setColumnOrder)]) def handleLayerFilter(self, names): + """Event handler for filtering layers.""" self.__monitorFrames.filterLayersFromDoubleClick(names) def __splitterMoved(self, pos, index): + del index self.__monitorLayers.disableUpdate = not bool(pos) def dragEnterEvent(self, event): @@ -121,6 +129,7 @@ def __setJob(self, job = None): def __unmonitor(self, proxy): """Unmonitors the current job if it matches the supplied proxy. + @param proxy: A job proxy @type proxy: proxy""" if self.__job and self.__job == proxy: diff --git a/cuegui/cuegui/plugins/MonitorJobsPlugin.py b/cuegui/cuegui/plugins/MonitorJobsPlugin.py index 154a9d73c..c2f970b59 100644 --- a/cuegui/cuegui/plugins/MonitorJobsPlugin.py +++ b/cuegui/cuegui/plugins/MonitorJobsPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for listing active jobs and managing them.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -45,27 +48,31 @@ class MonitorJobsDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Plugin for listing active jobs and managing them.""" view_object = QtCore.Signal(object) def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) + self.__loadFinishedJobsCheckBox = None + self.jobMonitor = cuegui.JobMonitorTree.JobMonitorTree(self) self.__toolbar = QtWidgets.QToolBar(self) self._regexLoadJobsSetup(self.__toolbar) - #self.__toolbar.addSeparator() self._buttonSetup(self.__toolbar) self.layout().addWidget(self.__toolbar) self.layout().addWidget(self.jobMonitor) - #Signals in: + # Signals in + # pylint: disable=no-member QtGui.qApp.view_object.connect(self.addJob) QtGui.qApp.facility_changed.connect(self.jobMonitor.removeAllItems) - #Signals out: + # pylint: enable=no-member + + # Signals out self.jobMonitor.view_object.connect(self.view_object.emit) self.pluginRegisterSettings([("regexText", @@ -84,45 +91,49 @@ def __init__(self, parent): self.jobMonitor.getColumnOrder, self.jobMonitor.setColumnOrder)]) - def addJob(self, object): - if cuegui.Utils.isProc(object): - object = cuegui.Utils.findJob(object.data.job_name) - elif not cuegui.Utils.isJob(object): + def addJob(self, rpcObject): + """Adds a job to be monitored.""" + if cuegui.Utils.isProc(rpcObject): + rpcObject = cuegui.Utils.findJob(rpcObject.data.job_name) + elif not cuegui.Utils.isJob(rpcObject): return - self.jobMonitor.addJob(object) + self.jobMonitor.addJob(rpcObject) self.raise_() def getJobIds(self): + """Returns a list of the IDs of all jobs being monitored.""" return list(map(opencue.id, self.jobMonitor.getJobProxies())) def restoreJobIds(self, jobIds): + """Monitors a list of jobs.""" for jobId in jobIds: try: self.jobMonitor.addJob(jobId) - except opencue.EntityNotFoundException as e: + except opencue.EntityNotFoundException: logger.warning("Unable to load previously loaded job since " "it was moved to the historical " - "database: %s" % jobId) + "database: %s", jobId) - def pluginRestoreState(self, settings): + def pluginRestoreState(self, saved_settings): """Called on plugin start with any previously saved state. - @param settings: Last state of the plugin instance - @type settings: any""" - if isinstance(settings, dict): - cuegui.AbstractDockWidget.AbstractDockWidget.pluginRestoreState(self, settings) - elif settings: + @param saved_settings: Last state of the plugin instance + @type saved_settings: any""" + if isinstance(saved_settings, dict): + cuegui.AbstractDockWidget.AbstractDockWidget.pluginRestoreState(self, saved_settings) + + elif saved_settings: # old method that needs to go away - if len(settings) >= 1: - self.__regexLoadJobsEditBox.setText(settings[0]) - if len(settings) >= 2 and settings[1]: - for jobId in settings[1]: + if len(saved_settings) >= 1: + self.__regexLoadJobsEditBox.setText(saved_settings[0]) + if len(saved_settings) >= 2 and saved_settings[1]: + for jobId in saved_settings[1]: try: self.jobMonitor.addJob(jobId) - except opencue.EntityNotFoundException as e: + except opencue.EntityNotFoundException: logger.warning("Unable to load previously loaded job since" "it was moved to the historical " - "database: %s" % jobId) + "database: %s", jobId) def _regexLoadJobsSetup(self, layout): """Selects jobs by name substring. @@ -156,7 +167,8 @@ def _regexLoadJobsHandle(self): if cuegui.Utils.isStringId(substring): # If a uuid is provided, load it self.jobMonitor.addJob(substring) - elif load_finished_jobs or re.search("^([a-z0-9_]+)\-([a-z0-9\.]+)\-", substring, re.IGNORECASE): + elif load_finished_jobs or re.search( + r"^([a-z0-9_]+)\-([a-z0-9\.]+)\-", substring, re.IGNORECASE): # If show and shot is provided, or if "load finished" checkbox is checked, load all jobs for job in opencue.api.getJobs(substr=[substring], include_finished=True): self.jobMonitor.addJob(job) @@ -240,7 +252,10 @@ def _buttonSetup(self, layout): layout.addWidget(unpauseButton) unpauseButton.clicked.connect(self.jobMonitor.actionResumeSelectedItems) + class JobLoadFinishedCheckBox(QtWidgets.QCheckBox): + """Checkbox for controlling whether finished jobs appear in the list.""" + def __init__(self, parent): QtWidgets.QCheckBox.__init__(self, 'Load Finished') self.parent = weakref.proxy(parent) @@ -250,7 +265,10 @@ def __init__(self, parent): self.setToolTip(toolTip) + class JobRegexLoadEditBox(QtWidgets.QLineEdit): + """Textbox for searching for jobs to add to the list of monitored jobs.""" + def __init__(self, parent): QtWidgets.QLineEdit.__init__(self) self.parent = weakref.proxy(parent) @@ -290,12 +308,14 @@ def contextMenuEvent(self, e): menu.exec_(QtCore.QPoint(e.globalX(), e.globalY())) def actionClear(self): + """Clears the textbox.""" self.setText("") def _actionLoad(self): self.returnPressed.emit() def toggleReadOnly(self): + """Toggles the textbox readonly setting.""" self.setReadOnly(not self.isReadOnly()) def keyPressEvent(self, event): diff --git a/cuegui/cuegui/plugins/RedirectPlugin.py b/cuegui/cuegui/plugins/RedirectPlugin.py index 5af78a52c..1122e6baf 100644 --- a/cuegui/cuegui/plugins/RedirectPlugin.py +++ b/cuegui/cuegui/plugins/RedirectPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for redirecting procs from one job to another.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -29,6 +32,8 @@ class RedirectWidget(cuegui.AbstractDockWidget.AbstractDockWidget): + """Plugin for redirecting procs from one job to another.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) self.layout().addWidget(cuegui.Redirect.RedirectWidget(self)) diff --git a/cuegui/cuegui/plugins/ServicePlugin.py b/cuegui/cuegui/plugins/ServicePlugin.py index b69d89f36..cc2a99bef 100644 --- a/cuegui/cuegui/plugins/ServicePlugin.py +++ b/cuegui/cuegui/plugins/ServicePlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for listing services and performing administrative tasks.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -31,10 +34,13 @@ class ServicesDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Plugin for listing services and performing administrative tasks.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) self.setWindowTitle("Facility Service Defaults") self.__serviceManager = cuegui.ServiceDialog.ServiceManager(None, self) self.layout().addWidget(self.__serviceManager) + # pylint: disable=no-member QtGui.qApp.facility_changed.connect(self.__serviceManager.refresh) + # pylint: enable=no-member diff --git a/cuegui/cuegui/plugins/ShowsPlugin.py b/cuegui/cuegui/plugins/ShowsPlugin.py index e1abd60eb..89ef37754 100644 --- a/cuegui/cuegui/plugins/ShowsPlugin.py +++ b/cuegui/cuegui/plugins/ShowsPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for listing shows and performing administrative tasks.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -29,7 +32,8 @@ class ShowsDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Plugin for listing shows and performing administrative tasks.""" + def __init__(self, parent): super(ShowsDockWidget, self).__init__(parent, PLUGIN_NAME) diff --git a/cuegui/cuegui/plugins/SubscriptionsGraphPlugin.py b/cuegui/cuegui/plugins/SubscriptionsGraphPlugin.py index 9e895aaf5..80dde33ca 100644 --- a/cuegui/cuegui/plugins/SubscriptionsGraphPlugin.py +++ b/cuegui/cuegui/plugins/SubscriptionsGraphPlugin.py @@ -1,3 +1,21 @@ +# Copyright Contributors to the OpenCue Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Plugin for listing a show's subscriptions and a visualization of what's being consumed.""" + + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -8,6 +26,7 @@ import cuegui.AbstractDockWidget import cuegui.SubscriptionGraphWidget + PLUGIN_NAME = 'Subscription Graphs' PLUGIN_CATEGORY = "Cuecommander" PLUGIN_DESCRIPTION = "An administrator interface to subscriptions" @@ -16,7 +35,8 @@ class SubscriptionGraphDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Containing widget for this plugin.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) diff --git a/cuegui/cuegui/plugins/SubscriptionsPlugin.py b/cuegui/cuegui/plugins/SubscriptionsPlugin.py index f0cd946a7..7c0d11731 100644 --- a/cuegui/cuegui/plugins/SubscriptionsPlugin.py +++ b/cuegui/cuegui/plugins/SubscriptionsPlugin.py @@ -13,6 +13,9 @@ # limitations under the License. +"""Plugin for managing show subscriptions.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import @@ -32,7 +35,8 @@ class SubscriptionDockWidget(cuegui.AbstractDockWidget.AbstractDockWidget): - """This builds what is displayed on the dock widget""" + """Widget that lists shows and the subscriptions they have.""" + def __init__(self, parent): cuegui.AbstractDockWidget.AbstractDockWidget.__init__(self, parent, PLUGIN_NAME) diff --git a/cuegui/tests/CueJobMonitorTree_tests.py b/cuegui/tests/CueJobMonitorTree_tests.py index 28859001f..87dd0a635 100644 --- a/cuegui/tests/CueJobMonitorTree_tests.py +++ b/cuegui/tests/CueJobMonitorTree_tests.py @@ -13,20 +13,24 @@ # limitations under the License. -import mock +"""Tests for cuegui.CueJobMonitorTree.""" + + import unittest +import mock import PySide2.QtCore import PySide2.QtGui import PySide2.QtWidgets -import cuegui.CueJobMonitorTree -import cuegui.plugins.MonitorCuePlugin -import cuegui.Style import opencue.compiled_proto.job_pb2 import opencue.compiled_proto.show_pb2 import opencue.wrappers.show +import cuegui.CueJobMonitorTree +import cuegui.plugins.MonitorCuePlugin +import cuegui.Style + from . import test_utils diff --git a/cuegui/tests/FilterDialog_tests.py b/cuegui/tests/FilterDialog_tests.py index 52d6b926a..071a2dab9 100644 --- a/cuegui/tests/FilterDialog_tests.py +++ b/cuegui/tests/FilterDialog_tests.py @@ -13,21 +13,26 @@ # limitations under the License. -import mock +"""Tests for cuegui.FilterDialog.""" + + import unittest +import mock + import PySide2.QtCore import PySide2.QtGui import PySide2.QtWidgets import PySide2.QtTest -import cuegui.FilterDialog -import cuegui.Style import opencue.compiled_proto.show_pb2 import opencue.compiled_proto.filter_pb2 import opencue.wrappers.filter import opencue.wrappers.show +import cuegui.FilterDialog +import cuegui.Style + from . import test_utils @@ -45,8 +50,9 @@ def setUp(self, getStubMock): id='filter-one-id', name='filterOne', order=1, enabled=True) self.filter = opencue.wrappers.filter.Filter(filterProto) - getStubMock.return_value.GetFilters.return_value = opencue.compiled_proto.show_pb2.ShowGetFiltersResponse( - filters=opencue.compiled_proto.filter_pb2.FilterSeq(filters=[filterProto])) + getStubMock.return_value.GetFilters.return_value = \ + opencue.compiled_proto.show_pb2.ShowGetFiltersResponse( + filters=opencue.compiled_proto.filter_pb2.FilterSeq(filters=[filterProto])) self.parentWidget = PySide2.QtWidgets.QWidget() self.filterDialog = cuegui.FilterDialog.FilterDialog(self.show, parent=self.parentWidget) @@ -159,8 +165,9 @@ def setUp(self, getStubMock): opencue.compiled_proto.filter_pb2.Filter( id='filter-two-id', name='filterTwo', order=2, enabled=False), ] - getStubMock.return_value.GetFilters.return_value = opencue.compiled_proto.show_pb2.ShowGetFiltersResponse( - filters=opencue.compiled_proto.filter_pb2.FilterSeq(filters=filters)) + getStubMock.return_value.GetFilters.return_value = \ + opencue.compiled_proto.show_pb2.ShowGetFiltersResponse( + filters=opencue.compiled_proto.filter_pb2.FilterSeq(filters=filters)) self.parentWidget = PySide2.QtWidgets.QWidget() self.filterDialog = cuegui.FilterDialog.FilterDialog(show, parent=self.parentWidget) @@ -257,8 +264,10 @@ def test_shouldAddMatcher(self, getItemMock, getTextMock): self.filter.createMatcher.assert_called_with(matcherSubject, matcherType, matcherText) self.assertEqual(3, self.matcherMonitorTree.topLevelItemCount()) matcherWidget = self.matcherMonitorTree.topLevelItem(0) - self.assertEqual('FACILITY', self.matcherMonitorTree.itemWidget(matcherWidget, 0).currentText()) - self.assertEqual('CONTAINS', self.matcherMonitorTree.itemWidget(matcherWidget, 1).currentText()) + self.assertEqual( + 'FACILITY', self.matcherMonitorTree.itemWidget(matcherWidget, 0).currentText()) + self.assertEqual( + 'CONTAINS', self.matcherMonitorTree.itemWidget(matcherWidget, 1).currentText()) self.assertEqual(matcherText, self.matcherMonitorTree.itemWidget(matcherWidget, 2).text()) @mock.patch('PySide2.QtWidgets.QInputDialog.getText') diff --git a/cuegui/tests/FrameMonitorTree_tests.py b/cuegui/tests/FrameMonitorTree_tests.py index ff7dee5ab..e28e8229f 100644 --- a/cuegui/tests/FrameMonitorTree_tests.py +++ b/cuegui/tests/FrameMonitorTree_tests.py @@ -13,23 +13,27 @@ # limitations under the License. -import mock +"""Tests for cuegui.FrameMonitorTree.""" + + import unittest +import mock import PySide2.QtCore import PySide2.QtGui import PySide2.QtTest import PySide2.QtWidgets +import opencue.compiled_proto.job_pb2 +import opencue.wrappers.frame +import opencue.wrappers.job + import cuegui.Constants import cuegui.FrameMonitor import cuegui.FrameMonitorTree import cuegui.Main import cuegui.plugins.MonitorJobDetailsPlugin import cuegui.Style -import opencue.compiled_proto.job_pb2 -import opencue.wrappers.frame -import opencue.wrappers.job from . import test_utils @@ -115,10 +119,11 @@ def test_tickFullUpdate(self, getFramesMock, getUpdatedFramesMock): getUpdatedFramesMock.assert_not_called() def test_getCores(self): - frame = opencue.wrappers.frame.Frame(opencue.compiled_proto.job_pb2.Frame(last_resource='foo/125.82723')) + frame = opencue.wrappers.frame.Frame( + opencue.compiled_proto.job_pb2.Frame(last_resource='foo/125.82723')) self.assertEqual(125.82723, self.frameMonitorTree.getCores(frame)) - self.assertEqual('125.83', self.frameMonitorTree.getCores(frame, format=True)) + self.assertEqual('125.83', self.frameMonitorTree.getCores(frame, format_as_string=True)) @mock.patch.object(cuegui.FrameMonitorTree.FrameContextMenu, 'exec_') def test_rightClickItem(self, execMock): diff --git a/cuegui/tests/MenuActions_tests.py b/cuegui/tests/MenuActions_tests.py index a1490da44..87282eefe 100644 --- a/cuegui/tests/MenuActions_tests.py +++ b/cuegui/tests/MenuActions_tests.py @@ -13,21 +13,19 @@ # limitations under the License. +"""Tests for cuegui.MenuActions.""" + + from __future__ import print_function from __future__ import division from __future__ import absolute_import -from builtins import * -import mock import unittest +import mock import PySide2.QtGui import PySide2.QtWidgets -import cuegui.Constants -import cuegui.CueJobMonitorTree -import cuegui.Main -import cuegui.MenuActions import opencue.compiled_proto.depend_pb2 import opencue.compiled_proto.facility_pb2 import opencue.compiled_proto.filter_pb2 @@ -50,6 +48,11 @@ import opencue.wrappers.subscription import opencue.wrappers.task +import cuegui.Constants +import cuegui.CueJobMonitorTree +import cuegui.Main +import cuegui.MenuActions + _GB_TO_KB = 1024 * 1024 @@ -94,7 +97,7 @@ def test_emailArtist(self, emailDialogMock): self.job_actions.emailArtist(rpcObjects=[job]) - emailDialogMock.assert_called_with(job, [], self.widgetMock) + emailDialogMock.assert_called_with(job, self.widgetMock) emailDialogMock.return_value.show.assert_called() @mock.patch('PySide2.QtWidgets.QInputDialog.getDouble') @@ -534,7 +537,8 @@ def test_view(self): @mock.patch('cuegui.DependDialog.DependDialog') def test_viewDepends(self, dependDialogMock): - layer = opencue.wrappers.layer.Layer(opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) + layer = opencue.wrappers.layer.Layer( + opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) self.layer_actions.viewDepends(rpcObjects=[layer]) @@ -641,7 +645,8 @@ def test_setTags(self, layerTagsDialogMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'kill') @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) def test_kill(self, yesNoMock, killMock): - layer = opencue.wrappers.layer.Layer(opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) + layer = opencue.wrappers.layer.Layer( + opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) self.layer_actions.kill(rpcObjects=[layer]) @@ -725,7 +730,7 @@ def test_dependWizard(self, dependWizardMock): self.layer_actions.dependWizard(rpcObjects=layers) - dependWizardMock.assert_called_with(self.widgetMock, [self.job], layers) + dependWizardMock.assert_called_with(self.widgetMock, [self.job], layers=layers) @mock.patch.object(opencue.wrappers.layer.Layer, 'reorderFrames', autospec=True) @mock.patch('PySide2.QtWidgets.QInputDialog.getItem') @@ -954,7 +959,7 @@ def test_dependWizard(self, dependWizardMock): self.frame_actions.dependWizard(rpcObjects=frames) - dependWizardMock.assert_called_with(self.widgetMock, [self.job], [], frames) + dependWizardMock.assert_called_with(self.widgetMock, [self.job], frames=frames) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) def test_markdone(self, yesNoMock): @@ -980,7 +985,8 @@ def test_reorder(self, getItemMock, reorderFramesMock): self.frame_actions.reorder(rpcObjects=[frame]) - reorderFramesMock.assert_called_with(layer, str(frame_num), opencue.compiled_proto.job_pb2.REVERSE) + reorderFramesMock.assert_called_with( + layer, str(frame_num), opencue.compiled_proto.job_pb2.REVERSE) @mock.patch('PySide2.QtWidgets.QApplication.clipboard') @mock.patch('cuegui.Utils.getFrameLogFile') @@ -998,8 +1004,10 @@ def test_copyLogFileName(self, getFrameLogFileMock, clipboardMock): def test_eatandmarkdone(self, yesNoMock, markdoneMock): layer_name = 'layer-name' frames = [ - opencue.wrappers.frame.Frame(opencue.compiled_proto.job_pb2.Frame(name='frame1', layer_name=layer_name)), - opencue.wrappers.frame.Frame(opencue.compiled_proto.job_pb2.Frame(name='frame2', layer_name=layer_name))] + opencue.wrappers.frame.Frame( + opencue.compiled_proto.job_pb2.Frame(name='frame1', layer_name=layer_name)), + opencue.wrappers.frame.Frame( + opencue.compiled_proto.job_pb2.Frame(name='frame2', layer_name=layer_name))] layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer( name=layer_name, layer_stats=opencue.compiled_proto.job_pb2.LayerStats( @@ -1133,6 +1141,7 @@ def test_delete(self): class AllocationActionsTests(unittest.TestCase): + # pylint: disable=attribute-defined-outside-init def test_init(self): self.widgetMock = mock.Mock() cuegui.MenuActions.AllocationActions(self.widgetMock, mock.Mock(), None, None) @@ -1386,66 +1395,66 @@ def setUp(self): @mock.patch('PySide2.QtWidgets.QInputDialog.getText') def test_rename(self, getTextMock): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.setName = mock.MagicMock() + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.setName = mock.MagicMock() newName = 'newFilterName' getTextMock.return_value = (newName, True) - self.filter_actions.rename(rpcObjects=[filter]) + self.filter_actions.rename(rpcObjects=[filter_wrapper]) - filter.setName.assert_called_with(newName) + filter_wrapper.setName.assert_called_with(newName) @mock.patch('cuegui.Utils.questionBoxYesNo', new=mock.Mock(return_value=True)) def test_delete(self): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.delete = mock.MagicMock() + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.delete = mock.MagicMock() - self.filter_actions.delete(rpcObjects=[filter]) + self.filter_actions.delete(rpcObjects=[filter_wrapper]) - filter.delete.assert_called() + filter_wrapper.delete.assert_called() def test_raiseOrder(self): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.raiseOrder = mock.MagicMock() + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.raiseOrder = mock.MagicMock() - self.filter_actions.raiseOrder(rpcObjects=[filter]) + self.filter_actions.raiseOrder(rpcObjects=[filter_wrapper]) - filter.raiseOrder.assert_called() + filter_wrapper.raiseOrder.assert_called() def test_lowerOrder(self): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.lowerOrder = mock.MagicMock() + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.lowerOrder = mock.MagicMock() - self.filter_actions.lowerOrder(rpcObjects=[filter]) + self.filter_actions.lowerOrder(rpcObjects=[filter_wrapper]) - filter.lowerOrder.assert_called() + filter_wrapper.lowerOrder.assert_called() def test_orderFirst(self): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.orderFirst = mock.MagicMock() + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.orderFirst = mock.MagicMock() - self.filter_actions.orderFirst(rpcObjects=[filter]) + self.filter_actions.orderFirst(rpcObjects=[filter_wrapper]) - filter.orderFirst.assert_called() + filter_wrapper.orderFirst.assert_called() def test_orderLast(self): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.orderLast = mock.MagicMock() + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.orderLast = mock.MagicMock() - self.filter_actions.orderLast(rpcObjects=[filter]) + self.filter_actions.orderLast(rpcObjects=[filter_wrapper]) - filter.orderLast.assert_called() + filter_wrapper.orderLast.assert_called() @mock.patch('PySide2.QtWidgets.QInputDialog.getInt') def test_setOrder(self, getTextMock): - filter = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) - filter.setOrder = mock.MagicMock() - newOrder = 47 - getTextMock.return_value = (newOrder, True) + filter_wrapper = opencue.wrappers.filter.Filter(opencue.compiled_proto.filter_pb2.Filter()) + filter_wrapper.setOrder = mock.MagicMock() + new_order = 47 + getTextMock.return_value = (new_order, True) - self.filter_actions.setOrder(rpcObjects=[filter]) + self.filter_actions.setOrder(rpcObjects=[filter_wrapper]) - filter.setOrder.assert_called_with(newOrder) + filter_wrapper.setOrder.assert_called_with(new_order) @mock.patch('opencue.cuebot.Cuebot.getStub', new=mock.Mock()) diff --git a/cuegui/tests/Redirect_tests.py b/cuegui/tests/Redirect_tests.py index e4432d843..d1e7ce5dd 100644 --- a/cuegui/tests/Redirect_tests.py +++ b/cuegui/tests/Redirect_tests.py @@ -13,17 +13,21 @@ # limitations under the License. -import mock +"""Tests for cuegui.Redirect.""" + + import unittest +import mock import PySide2.QtCore import PySide2.QtGui -import cuegui.Redirect -import cuegui.Style import opencue.compiled_proto.show_pb2 import opencue.wrappers.show +import cuegui.Redirect +import cuegui.Style + from . import test_utils diff --git a/cuegui/tests/Utils_tests.py b/cuegui/tests/Utils_tests.py index 0d9bd39b3..dddf46423 100644 --- a/cuegui/tests/Utils_tests.py +++ b/cuegui/tests/Utils_tests.py @@ -13,9 +13,13 @@ # limitations under the License. -import mock +"""Tests for cuegui.Utils.""" + + import unittest +import mock + import opencue.compiled_proto.job_pb2 import opencue.wrappers.job import cuegui.Utils diff --git a/cuegui/tests/images/images_tests.py b/cuegui/tests/images/images_tests.py index 6e76baad4..60f893bef 100644 --- a/cuegui/tests/images/images_tests.py +++ b/cuegui/tests/images/images_tests.py @@ -13,8 +13,12 @@ # limitations under the License. +"""Tests for cuegui.images.""" + + import unittest +# pylint: disable=unused-import import cuegui.images.icons_rcc import cuegui.images.bluecurve.icons_rcc import cuegui.images.crystal.icons_rcc diff --git a/cuegui/tests/plugins/LogViewPlugin_tests.py b/cuegui/tests/plugins/LogViewPlugin_tests.py index 74ffa3847..9ceb4a5b6 100644 --- a/cuegui/tests/plugins/LogViewPlugin_tests.py +++ b/cuegui/tests/plugins/LogViewPlugin_tests.py @@ -13,10 +13,14 @@ # limitations under the License. +"""Tests for cuegui.plugins.LogViewPlugin.""" + + import os -import mock import unittest +import mock + import pyfakefs.fake_filesystem_unittest import PySide2.QtCore import PySide2.QtGui @@ -47,6 +51,7 @@ Donec porta gravida eros id vulputate. Phasellus vel nisl arcu.''' +# pylint: disable=no-member class LogViewPluginTests(pyfakefs.fake_filesystem_unittest.TestCase): @mock.patch('opencue.cuebot.Cuebot.getStub', new=mock.MagicMock()) def setUp(self): @@ -79,19 +84,25 @@ def test_shouldUpdateLogFile(self): def test_shouldHighlightAllSearchResults(self): PySide2.QtGui.qApp.display_log_file_content.emit([self.logPath1, self.logPath2]) - self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState(PySide2.QtCore.Qt.CheckState.Unchecked) + self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState( + PySide2.QtCore.Qt.CheckState.Unchecked) self.logViewPlugin.logview_widget._search_box.setText('lorem') self.logViewPlugin.logview_widget._search_button.click() matches = self.logViewPlugin.logview_widget._matches self.assertEqual([(0, 5), (127, 5)], matches) - self.assertTrue(self.__isHighlighted(self.logViewPlugin.logview_widget._content_box, matches[0][0], matches[0][1])) - self.assertTrue(self.__isHighlighted(self.logViewPlugin.logview_widget._content_box, matches[1][0], matches[1][1])) + self.assertTrue( + self.__isHighlighted( + self.logViewPlugin.logview_widget._content_box, matches[0][0], matches[0][1])) + self.assertTrue( + self.__isHighlighted( + self.logViewPlugin.logview_widget._content_box, matches[1][0], matches[1][1])) def test_shouldMoveCursorToSecondSearchResult(self): PySide2.QtGui.qApp.display_log_file_content.emit([self.logPath1, self.logPath2]) - self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState(PySide2.QtCore.Qt.CheckState.Unchecked) + self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState( + PySide2.QtCore.Qt.CheckState.Unchecked) self.logViewPlugin.logview_widget._search_box.setText('lorem') self.logViewPlugin.logview_widget._search_button.click() @@ -104,7 +115,8 @@ def test_shouldMoveCursorToSecondSearchResult(self): def test_shouldMoveCursorLastSearchResult(self): PySide2.QtGui.qApp.display_log_file_content.emit([self.logPath1, self.logPath2]) - self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState(PySide2.QtCore.Qt.CheckState.Unchecked) + self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState( + PySide2.QtCore.Qt.CheckState.Unchecked) self.logViewPlugin.logview_widget._search_box.setText('lorem') self.logViewPlugin.logview_widget._search_button.click() @@ -117,14 +129,17 @@ def test_shouldMoveCursorLastSearchResult(self): def test_shouldPerformCaseInsensitiveSearch(self): PySide2.QtGui.qApp.display_log_file_content.emit([self.logPath1, self.logPath2]) - self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState(PySide2.QtCore.Qt.CheckState.Checked) + self.logViewPlugin.logview_widget._case_stv_checkbox.setCheckState( + PySide2.QtCore.Qt.CheckState.Checked) self.logViewPlugin.logview_widget._search_box.setText('lorem') self.logViewPlugin.logview_widget._search_button.click() matches = self.logViewPlugin.logview_widget._matches self.assertEqual([(127, 5)], matches) - self.assertTrue(self.__isHighlighted(self.logViewPlugin.logview_widget._content_box, matches[0][0], matches[0][1])) + self.assertTrue( + self.__isHighlighted( + self.logViewPlugin.logview_widget._content_box, matches[0][0], matches[0][1])) @staticmethod def __isHighlighted(textBox, startPosition, selectionLength): @@ -135,5 +150,6 @@ def __isHighlighted(textBox, startPosition, selectionLength): selectionLength) return cursor.charFormat().background() == PySide2.QtCore.Qt.red + if __name__ == '__main__': unittest.main() diff --git a/cuegui/tests/test_utils.py b/cuegui/tests/test_utils.py index 5e2685049..81f22f6cc 100644 --- a/cuegui/tests/test_utils.py +++ b/cuegui/tests/test_utils.py @@ -13,12 +13,16 @@ # limitations under the License. +"""Common utility functions for CueGUI test code.""" + + import cuegui.Main __QAPPLICATION_SINGLETON = None +# pylint: disable=global-statement def createApplication(): global __QAPPLICATION_SINGLETON if __QAPPLICATION_SINGLETON is None: diff --git a/pycue/opencue/wrappers/service.py b/pycue/opencue/wrappers/service.py index acacb233a..1a941772b 100644 --- a/pycue/opencue/wrappers/service.py +++ b/pycue/opencue/wrappers/service.py @@ -192,3 +192,19 @@ def setTags(self, tags): :param: new list of service tags """ self.data.tags[:] = tags + + def timeout(self): + """Gets the default service timeout.""" + return self.data.timeout + + def setTimeout(self, timeout): + """Sets the default service timeout.""" + self.data.timeout = timeout + + def timeoutLLU(self): + """Gets the default service LLU timeout.""" + return self.data.timeout + + def setTimeoutLLU(self, timeout_llu): + """Sets the default service LLU timeout.""" + self.data.timeout_llu = timeout_llu