def _makeRunButtons(self, _btmLayout): self._resetAllBtn = uiutils.makeIconButton(self._resetIcon, self) self._resetAllBtn.setToolTip( "Reset the test item states, icons and stats and collapse the tree view items in a certain level." ) self._resetAllBtn.clicked.connect(self._view.resetAllItemsToNormal) _btmLayout.addWidget(self._resetAllBtn, 1) self._runSelectedBtn = QtWidgets.QPushButton("Run &Selected", self) self._runSelectedBtn.setToolTip("Run the selected tests in the view.") self._runSelectedBtn.clicked.connect(self._runViewSelectedTests) self._runSelectedBtn.setIcon(self._view._runSelectedIcon) _btmLayout.addWidget(self._runSelectedBtn, 1) self._runAllBtn = QtWidgets.QPushButton("Run &All", self) self._runAllBtn.setToolTip( "Run all the tests, including those filtered from the view.") self._runAllBtn.setIcon(self._view._runAllIcon) self._runAllBtn.clicked.connect(self._runAllTests) _btmLayout.addWidget(self._runAllBtn, 1) self._reimportAndRerunBtn = QtWidgets.QPushButton( "&Reload && Rerun", self) self._reimportAndRerunBtn.setToolTip( "Reimport all changed python modules and rerun the last tests.") self._reimportAndRerunBtn.clicked.connect(self._reimportPyAndRerun) self._reimportAndRerunBtn.setIcon(self._view._reimportAndRunIcon) _btmLayout.addWidget(self._reimportAndRerunBtn, 1) _runMoreBtn = self._makeRunMoreButton() _btmLayout.addWidget(_runMoreBtn, 0)
def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("{} Preference".format(constants.APP_NAME)) self._mainLayout = uiutils.makeMainLayout(self) self._formLayout = uiutils.makeMinorLayout(QtWidgets.QFormLayout) self._mainLayout.addLayout(self._formLayout) self._formLayout.setLabelAlignment(QtCore.Qt.AlignRight) # Code viewing config self._codeEditorLE = QtWidgets.QLineEdit(self) editorSetting = gotocode.CodeLineVisitor.config() self._codeEditorLE.setText(editorSetting) self._codeEditorLE.editingFinished.connect( self._onCodeEditorEditFinished) self._codeEditorLE.setPlaceholderText( "Example: pargram $file -argToLine $line") self._codeEditorLE.setToolTip( "Input the command to jump to the code at the line. \n" "$file is the placeholder for file path, $line is the line number." ) _annoText = QtWidgets.QLabel("Default: {}".format( constants.CONFIG_KEY_CODE_EDITOR_DEFAULT)) _annoText.setEnabled(False) self._formLayout.addRow("Go To Code Line", self._codeEditorLE) self._formLayout.addRow("", _annoText) self.setMinimumWidth(400) self.setMinimumHeight(100) self.destroyed.connect(self._onDialogDeleted)
def _createSplitterContent(self): wgt = QtWidgets.QWidget(self) splitterLay = QtWidgets.QVBoxLayout(wgt) splitterLay.setSpacing(3) splitterLay.setContentsMargins(0, 0, 0, 0) self._splitter.addWidget(wgt) return wgt, splitterLay
def makeMenuToolButton(icon=None, toolTip="", parent=None): btn = QtWidgets.QToolButton(parent) if icon: btn.setIcon(icon) btn.setToolTip(toolTip) btn.setContentsMargins(0, 0, 0, 0) btn.setIconSize(QtCore.QSize(20, 20)) btn.setPopupMode(btn.InstantPopup) menu = QtWidgets.QMenu(btn) btn.setMenu(menu) return (btn, menu)
def _makeDirWidgets(self): lbl = QtWidgets.QLabel("Test Root") lbl.setToolTip( "Input a python module path or an absolute dir path in the lineEdit for the tests." ) self._rootDirLE = rootpathedit.RootPathEdit(self) self._rootDirLE.rootPathChanged.connect(self._switchToTestRootPath) self._rootDirLE.rootDirPicked.connect(self._onBrowseTestsRootDir) self._rootDirLE.topDirPicked.connect(self._onBrowseTopDir) self._rootDirLE.saveCurrentDirSettings.connect( self._onSaveCurrentDirSettings) self._rootDirLE.loadSavedDirPair.connect(self._loadSavedDirPair) self._rootDirLE.deleteCurrentDirSettings.connect( self._onDeleteCurrentDirSettings) self._panelVisBtn = uiutils.makeIconButton(self._panelStateIconSet[-1], self) self._panelVisBtn.setToolTip( "Switch the test tree view and the log browser visibility.") self._panelVisBtn.clicked.connect(self._onPanelVisButtonClicked) self._makeRunnerConfigButton() self._dirLayout.addWidget(lbl) self._dirLayout.addWidget(self._rootDirLE) self._dirLayout.addWidget(self._panelVisBtn) self._dirLayout.addWidget(self._runnerConfigBtn) self._updateDirUI()
def makeIconButton(icon, parent=None): btn = QtWidgets.QPushButton("", parent) btn.setIcon(icon) btn.setFlat(True) btn.setIconSize(QtCore.QSize(20, 20)) btn.setFixedSize(QtCore.QSize(24, 24)) return btn
def __init__(self, parent): QtWidgets.QTreeWidget.__init__(self, parent) self._pan = scrollareapan.ScrollAreaPan(self, scrollFactor=0.05) self._pan.installEventFilterOn(self.viewport()) self._uiStream = uistream.UiStream() self._codeVisitor = gotocode.CodeLineVisitor(self) self._codeVisitor.errorIssued.connect(self._onGoToCodeError) self.setItemDelegate(self._TreeItemDelegate(self)) self.setTextElideMode(QtCore.Qt.ElideLeft) self.setColumnCount(2) self._setHeaderStretch() self.setHeaderHidden(True) self.setAlternatingRowColors(True) self.setExpandsOnDoubleClick(False) self.setSelectionMode(self.ExtendedSelection) self.itemDoubleClicked.connect(self.onItemDoubleClicked) self.setIndentation(10) self._rootTestItem = None self._testCases = [] self._allItemsIdMap = {} self._initAllIcons() self._testManager = None self._viewStates = ViewStates(self) self._contextMenu = QtWidgets.QMenu(self) self._makeContextMenu() self.setToolTip( "This view lists out the tests, <b>Double Click</b> on them to run them, <b>Ctrl+C</b> to copy the python module path;" + "<br><br><b>MMB Dragging</b> to pan the view around." )
def _makeLogBrowserTopWidgets(self, layout): _console = QtWidgets.QLabel("Log Browser") self._logSearchLE = btnLineEdit.InlineButtonLineEdit( withClearButton=True, parent=self) self._logSearchLE.setToolTip( "Enter to search next keyword in the log browser, ctrl+Enter to search backward." ) self._logSearchLE.setPlaceholderText("Input to search") self._logSearchLE.returnPressed.connect(self._applyLoggingSearchText) self._wholeWordBtn = uiutils.makeIconButton(self._wholeWordIcon) self._wholeWordBtn.setCheckable(True) self._logSearchLE.addButton("wholeWord", self._wholeWordBtn) self._sensitiveBtn = uiutils.makeIconButton(self._caseSensitiveIcon) self._sensitiveBtn.setCheckable(True) self._logSearchLE.addButton("caseSensitive", self._sensitiveBtn) self._clearLogBtn = uiutils.makeIconButton(self._clearLogIcon, self) self._clearLogBtn.setToolTip("Clear the log browser logging.") self._clearLogBtn.clicked.connect(self._logBrowser.clear) layout.addWidget(_console, 0) layout.addStretch(1) layout.addWidget(self._logSearchLE, 0) layout.addWidget(self._clearLogBtn, 0)
def _makeContextMenu(self): self._initAllIcons() self._runSetupOnlyAct = QtWidgets.QAction("Run setUp( ) Only", self) self._runSetupOnlyAct.triggered.connect(self._atRunSetupOnly) self._runSetupOnlyAct.setIcon(self._runPartialIcon) self._runWithoutTearDownAct = QtWidgets.QAction("Run Without tearDown( )", self) self._runWithoutTearDownAct.triggered.connect(self._atRunWithoutTearDown) self._runWithoutTearDownAct.setIcon(self._runPartialIcon) self._runSelectedAct = QtWidgets.QAction("Run Selected", self) self._runSelectedAct.triggered.connect(self._atRunSelected) self._runSelectedAct.setIcon(self._runSelectedIcon) self._runAllAct = QtWidgets.QAction("Run All", self) self._runAllAct.triggered.connect(self._atRunAll) self._runAllAct.setIcon(self._runAllIcon) self._copyPathAct = QtWidgets.QAction("Copy Selected Path", self) self._copyPathAct.triggered.connect(self.copyFirstSelectedTestId) self._goToCodeAct = QtWidgets.QAction("Go To Code...", self) self._goToCodeAct.triggered.connect(self._atGoToCode) self._reloadModulesAct = QtWidgets.QAction("Reload Selected Modules", self) self._reloadModulesAct.setVisible( importutils.isReimportFeatureAvailable(silentCheck=True) ) self._reloadModulesAct.triggered.connect(self._atReloadSelectedModules) self._reloadModulesAct.setIcon(self._reimportAndRunIcon) self._contextMenu.addAction(self._runAllAct) self._contextMenu.addAction(self._runSelectedAct) self._contextMenu.addSeparator() self._contextMenu.addAction(self._runSetupOnlyAct) self._contextMenu.addAction(self._runWithoutTearDownAct) self._contextMenu.addSeparator() self._contextMenu.addAction(self._copyPathAct) self._contextMenu.addAction(self._goToCodeAct) self._contextMenu.addSeparator() self._contextMenu.addAction(self._reloadModulesAct) self._logLevelMenu = QtWidgets.QMenu("Set Logging Level") self._contextMenu.addMenu(self._logLevelMenu) self._logLevelActionGrp = QtWidgets.QActionGroup(self._logLevelMenu) self._logLevelActionGrp.setExclusive(True)
def _makeRunnerConfigButton(self): currentRunner = self._setInitialTestMode() currentRunnerMode = currentRunner.mode() self._runnerConfigBtn, self._configMenu = uiutils.makeMenuToolButton( parent=self) self._updateConfigButton(currentRunner) for runner in self._testManager.iterAllRunners(): icon = runner.icon() toolTip = "Run tests using {}.".format(runner.name()) runnerMode = runner.mode() act = QtWidgets.QAction(runner.name(), self) act.setIcon(icon) act.setEnabled(runner.isValid()) act.setToolTip(toolTip) act.setData(runnerMode) act.triggered.connect(self._onTestRunnerSwitched) self._updateLabelOfTestRunnerAct(act, currentRunnerMode) self._testRunnerActions.append(act) self._configMenu.addAction(act) self._configMenu.addSeparator() # Stop on error act: self._stopOnErrorAct, stopOnError = self._addToggleConfigAction( "Stop On Error", self._stopAtErrorIcon, "Stop the tests running once it encounters a test error/failure.", configKey=constants.CONFIG_KEY_STOP_ON_ERROR, slot=self._onStopOnErrorActionToggled, ) self._testManager.setStopOnError(stopOnError) # auto filer on run act: self._autoFilterAct, stopOnError = self._addToggleConfigAction( "Only Show Tests that Run", self._autoFilterIcon, "Apply a ':ran' filter automatically after every test run so that only the run tests shown in the view.", configKey=constants.CONFIG_KEY_AUTO_FILTERING_STATE, slot=self._onAutoFilterActionToggled, ) # auto clear log on run act: self._clearLogOnRunAct, stopOnError = self._addToggleConfigAction( "Clear LogBrowser On Run", self._clearLogOnRunIcon, "Clear the log browser automatically before every test run.", configKey=constants.CONFIG_KEY_AUTO_CLEAR_LOG_STATE, slot=self._onAutoClearLogActionToggled, ) self._configMenu.addSeparator() act = self._configMenu.addAction("Preference..") act.setIcon(self._configIcon) act.triggered.connect(self._showConfigWindow)
def _addToggleConfigAction(self, lbl, icon, tooltip, configKey, slot): # Cannot use self._configMenu.addAction() directly here since over-zealous garbage collection in some DCC apps: act = QtWidgets.QAction(lbl, self) act.setIcon(icon) act.setCheckable(True) act.setToolTip(tooltip) act.toggled.connect(slot) value = appsettings.get().simpleConfigBoolValue(configKey) act.setChecked(value) self._configMenu.addAction(act) return (act, value)
def _makeRunMoreButton(self): _runMoreBtn = QtWidgets.QToolButton(self) _runMoreBtn.setIcon(self._view._runPartialIcon) _runMoreBtn.setAutoRaise(True) _runMoreBtn.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) _runMoreBtn.setPopupMode(_runMoreBtn.InstantPopup) self._runSetupAct = QtWidgets.QAction("Run setUp( ) Only", self) self._runSetupAct.setIcon(self._view._runPartialIcon) self._runSetupAct.triggered.connect(self._runTestSetupOnly) self._runNoTearDown = QtWidgets.QAction("Run without tearDown( )", self) self._runNoTearDown.setIcon(self._view._runPartialIcon) self._runNoTearDown.triggered.connect(self._runTestWithoutTearDown) menu = QtWidgets.QMenu(_runMoreBtn) menu.addAction(self._runSetupAct) menu.addAction(self._runNoTearDown) _runMoreBtn.setMenu(menu) return _runMoreBtn
def addSeparatorToLayout(layout, direction): sep = QtWidgets.QFrame() sep.setStyleSheet("color:black") policy = sep.sizePolicy() if direction == QtCore.Qt.Horizontal: sep.setFrameShape(sep.HLine) sep.setSizePolicy(policy.Minimum, policy.Preferred) else: sep.setFrameShape(sep.VLine) sep.setSizePolicy(policy.Preferred, policy.Minimum) sep.setLineWidth(1) layout.addWidget(sep)
def __init__(self, parent=None): btnLineEdit.InlineButtonLineEdit.__init__(self, withClearButton=False, parent=parent) self._initIcons() self.editingFinished.connect(self.onEditFinished) self._initPath = None self._browseBtn = uiutils.makeIconButton(self._moreIcon, self) self._browseBtn.clicked.connect(self._onBrowseBtnClicked) self._browseMenu = QtWidgets.QMenu(self._browseBtn) self.addButton("Browse", self._browseBtn) self.regenerateBrowseMenu()
def reload(self, keepUiStates=True): if keepUiStates: self._viewStates.save() self._testCases = [] self._rootTestItem = None self._allItemsIdMap = {} self.clear() startDirOrModule = self._testManager.startDirOrModule() if not startDirOrModule: return self._rootTestItem = QtWidgets.QTreeWidgetItem([startDirOrModule]) self._rootTestItem.setToolTip(0, startDirOrModule) self._rootTestItem.setData(0, QtCore.Qt.UserRole, constants.ITEM_CATEGORY_ALL) self._setItemIconState(self._rootTestItem, constants.TEST_RESULT_NONE) self.addTopLevelItem(self._rootTestItem) self._allItemsIdMap[startDirOrModule] = self._rootTestItem isModule = not pathutils.isPath(startDirOrModule) headingCount = 0 heading = "" if isModule: headingCount = len(startDirOrModule) + 1 heading = startDirOrModule + "." testCount = 0 for test in self._testManager.iterAllTestIds(): testCount = testCount + 1 if isModule and test.startswith(startDirOrModule): test = test[headingCount:] testPaths = test.split(".") cParent = self._rootTestItem for i, p in enumerate(testPaths): path = heading + ".".join(testPaths[0 : (i + 1)]) if path in self._allItemsIdMap: cParent = self._allItemsIdMap[path] continue item = QtWidgets.QTreeWidgetItem(cParent) item.setText(0, p) item.setToolTip(0, path) item.setSizeHint(0, QtCore.QSize(20, 20)) self._allItemsIdMap[path] = item cParent = item testCase = cParent testCase.setData(0, QtCore.Qt.UserRole, constants.ITEM_CATEGORY_TEST) self._setItemIconState(testCase, constants.TEST_RESULT_NONE) self._testCases.append(cParent) caseItem = testCase.parent() caseItem.setData(0, QtCore.Qt.UserRole, constants.ITEM_CATEGORY_SUITE) self._setItemIconState(caseItem, constants.TEST_RESULT_NONE) moduleItem = caseItem.parent() moduleItem.setData(0, QtCore.Qt.UserRole, constants.ITEM_CATEGORY_MODULE) self._setItemIconState(moduleItem, constants.TEST_RESULT_NONE) packageItem = moduleItem.parent() while packageItem is not self._rootTestItem and packageItem: packageItem.setData( 0, QtCore.Qt.UserRole, constants.ITEM_CATEGORY_PACKAGE ) self._setItemIconState(packageItem, constants.TEST_RESULT_NONE) packageItem = packageItem.parent() self._uiStream.write("\n{}\n".format("-" * 20)) if not self._testManager.hasLastListerError(): self._uiStream.write("{} tests collected.\n\n".format(testCount)) else: with self._uiStream.resultCtx(constants.TEST_RESULT_ERROR): msg = "Error collecting tests from {}.\n\n".format( self._testManager.startDirOrModule() ) self._uiStream.write(msg) if keepUiStates: self._viewStates.restore() else: self._resetExpandStates(self._rootTestItem)
def makeMainLayout(hostWidget=None): _lay = QtWidgets.QVBoxLayout(hostWidget) _lay.setContentsMargins(6, 6, 6, 6) _lay.setSpacing(3) return _lay
def __init__(self, startDirOrModule=None, topDir=None, parent=None): parent = parent or dcc.findParentWindow() QtWidgets.QWidget.__init__(self, parent) self._uiStream = uistream.UiStream() self._initIcons() self._testManager = testmanager.TestManager(self, startDirOrModule, topDir) self._mainLay = uiutils.makeMainLayout(self) self.setContentsMargins(0, 0, 0, 0) # Top layout ------------------------------ self._dirLayout = uiutils.makeMinorHorizontalLayout() self._testRunnerActions = [] self._makeDirWidgets() self._mainLay.addLayout(self._dirLayout) self._splitter = QtWidgets.QSplitter(self) self._mainLay.addWidget(self._splitter, 1) # left layout ----------------------------------- self._leftWidget, leftLay = self._createSplitterContent() _topLayout = uiutils.makeMinorHorizontalLayout() self._makeTreeTopWidgets(_topLayout) leftLay.addLayout(_topLayout) self._view = unittesttree.UnitTestTreeView(self) self._view.runAllTest.connect(self._runAllTests) self._view.runTests.connect(self._runTests) self._view.runSetupOnly.connect(self._runTestSetupOnly) self._view.runWithoutTearDown.connect(self._runTestWithoutTearDown) self._view.searchNeeded.connect(self._prepareTreeSearch) self._view.itemSelectionChanged.connect(self._viewSelectionChanged) leftLay.addWidget(self._view) # right layout ----------------------------------- self._rightWidget, rightLay = self._createSplitterContent() _logTopLayout = uiutils.makeMinorHorizontalLayout() self._logBrowser = logbrowser.LogBrowser(self) self._logBrowser.searchNeeded.connect(self._prepareLogSearch) self._makeLogBrowserTopWidgets(_logTopLayout) rightLay.addLayout(_logTopLayout, 0) rightLay.addWidget(self._logBrowser, 1) self._splitter.setSizes([230, 500]) self._splitter.setStretchFactor(0, 0) self._splitter.setStretchFactor(1, 1) # bottom ----------------------------------- self._statusLbl = statuslabel.StatusLabel(self) self._mainLay.addWidget(self._statusLbl) _btmLayout = uiutils.makeMinorHorizontalLayout() self._makeRunButtons(_btmLayout) self._mainLay.addLayout(_btmLayout) self.resize(QtCore.QSize(900, 500)) self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) # for reimport convenience. self.setTabOrder(self._rootDirLE, self._treeFilterLE) self.setTabOrder(self._treeFilterLE, self._view) self.setTabOrder(self._view, self._logBrowser) self.setWindowIcon(self._iutestIcon) self.setWindowFlags(QtCore.Qt.Window) self._updateWindowTitle() self._loadLastDirsFromSettings() self._restorePanelVisState() self.resetUiTestManager() QtCore.QTimer.singleShot(0, self._initialLoad)