def introVideo(): M = QWidget() image_v = ImageViewer() # noinspection PyUnresolvedReferences vid2.VideoSignal2.connect(image_v.setImage) horizontal = QHBoxLayout() horizontal.setContentsMargins(0, 0, 0, 0) horizontal.addWidget(image_v) M.setLayout(horizontal) M.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Window | Qt.CustomizeWindowHint) M.activateWindow() ag = QDesktopWidget().availableGeometry() sg = QDesktopWidget().screenGeometry() widget = QWidget.geometry(QWidget()) x = ag.width() - widget.width() y = 2 * ag.height() - sg.height() - widget.height() M.move(x - 725, y - 240) M.show() M.raise_() push_button2.click() M.close()
def show_widget(self): # pragma: no cover - not testable """ Show and center AppQWidget """ self.center() self.show() QWidget.activateWindow(self)
class DURRAExt(EXTENSION, DURRAExtBase, Ui_durraDialog): def __init__(self, parent): super(DURRAExt, self).__init__(parent) self.backend = DURRABackendExt() def setup(self): self.backend.setup() self.ui = QWidget() self.setupUi(self.ui) # connect signals self.setupConnectionButtons() self.buttonBox.rejected.connect(self.onBtnCancel) self.btnSave.clicked.connect(self.onBtnSave) self.btnInitGit.clicked.connect(self.onBtnInitGit) self.disableButtons() def createActions(self, window): if CONTEXT_KRITA: action = window.createAction(MAIN_KRITA_ID, MAIN_KRITA_MENU_ENTRY) # parameter 1 = the name that Krita uses to identify the action # parameter 2 = the text to be added to the menu entry for this script action.triggered.connect(self.action_triggered) def action_triggered(self): self.backend.output = "" self.initDocument() self.ui.show() self.ui.activateWindow() def onBtnCancel(self): self.ui.close() def initDocument(self): self.txtLog.clear() super().initDocument() if self.backend.durradocument.hasKritaDocument(): self.initUIDocumentInfo() if TESTING: docInfo = self.backend.durradocument.getKritaDocumentInfo() self.backend.output = self.backend.output + "\n\n" + docInfo self.txtLog.setPlainText(self.backend.output) else: self.lblFilename.setText('document not open') self.txtTitle.clear() self.lblEditingTime.clear() self.txtAuthorFullname.clear() self.txtAuthorEmail.clear() self.txtLicense.clear() self.txtRevision.clear() self.txtKeyword.clear() self.lblVersion.clear() self.txtDescription.clear() self.txtLog.clear() def initUIDocumentInfo(self): self.lblFilename.setText(self.backend.durradocument.getFilenameKra()) if self.backend.durradocument.hasKritaDocument(): self.txtTitle.setText(self.backend.durradocument.title) self.lblEditingTime.setText( self.backend.durradocument.getDurationText()) self.txtAuthorFullname.setText( self.backend.durradocument.authorname) self.txtAuthorEmail.setText(self.backend.durradocument.authoremail) self.txtLicense.setText(self.backend.durradocument.license) self.txtRevision.setText(self.backend.durradocument.revisionstr) self.txtKeyword.setText( self.backend.durradocument.getKeywordsStr()) self.lblVersion.setText(self.backend.durradocument.versionstr) self.txtDescription.setText(self.backend.durradocument.description) def onBtnSave(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): self.initUIDocumentInfo() output = super().onBtnSave() self.txtLog.setText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnGenFiles(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): output = super().onBtnGenFiles() self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnCommitMetaFiles(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): extramsg = self.txtMessage.toPlainText() output = super().onBtnCommitMetaFiles(extramsg) self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnCommitFiles(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): extramsg = self.txtMessage.toPlainText() output = super().onBtnCommitFiles(extramsg) self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnNewMajorVersion(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): extramsg = self.txtMessage.toPlainText() output = super().onBtnNewMajorVersion(extramsg) self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnNewMinjorVersion(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): extramsg = self.txtMessage.toPlainText() output = super().onBtnNewMinjorVersion(extramsg) self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnNewPatchedVersion(self): self.txtLog.clear() if self.backend.durradocument.hasKritaDocument(): extramsg = self.txtMessage.toPlainText() output = super().onBtnNewPatchedVersion(extramsg) self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) def onBtnInitGit(self): if self.backend.durradocument.hasKritaDocument(): self.disableButtons() workdir = self.backend.workdir initgit_dir = QFileDialog.getExistingDirectory( self.ui, "Select a Directory to init git...", workdir, QFileDialog.ShowDirsOnly) if initgit_dir: cmds = self.backend.getGitInitCmds(initgit_dir) btnMsg = "Are you sure you want to `init git` in " + initgit_dir + "\n\n" + "Commands to run:\n" for cmd in cmds: btnMsg = btnMsg + '$ ' + ' '.join(cmd) + "\n" buttonReply = QMessageBox.question( self.ui, 'Select a Directory to init git...', btnMsg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.Yes: self.txtLog.clear() output = self.backend.runGitInit(initgit_dir) self.txtLog.setPlainText(output) self.txtLog.moveCursor(QtGui.QTextCursor.End) else: pass if TESTING: docInfo = self.backend.durradocument.getKritaDocumentInfo() self.backend.output = self.backend.output + "\n\n" + docInfo self.txtLog.setToolTip(self.backend.output) self.enableButtons() def disableButtons(self): super().disableButtons() self.btnSave.setEnabled(False) self.btnInitGit.setEnabled(False) def enableButtons(self): super().enableButtons() self.btnSave.setEnabled(True) self.btnInitGit.setEnabled(True) if self.backend.durradocument.hasKritaDocument(): if not self.backend.gitIsRepo(self.backend.workdir): self.btnInitGit.setEnabled(True) else: self.btnInitGit.setEnabled(False) else: self.btnSave.setEnabled(False)
class RingWindow(QMainWindow): image: RingImageQLabel statusbar: QStatusBar def __init__(self): super(RingWindow, self).__init__() path = os.path.join(sys.path[0], __package__) uic.loadUi(os.path.join(path, 'gui_ring.ui'), self) self.ctrl = QWidget() uic.loadUi(os.path.join(path, 'gui_ring_controls.ui'), self.ctrl) self.ctrl.zSpin.valueChanged.connect(self.onZValueChange) self.ctrl.openButton.pressed.connect(self.onOpenButton) self.ctrl.addButton.pressed.connect(self.onAddButton) self.ctrl.plotButton.pressed.connect(self.onPlotButton) self.ctrl.measureButton.pressed.connect(self.onMeasureButton) self.ctrl.dnaSpin.valueChanged.connect(self.onDnaValChange) self.ctrl.actSpin.valueChanged.connect(self.onActValChange) self.ctrl.dnaChk.toggled.connect(self.onImgToggle) self.ctrl.actChk.toggled.connect(self.onImgToggle) self.ctrl.renderChk.stateChanged.connect(self.onRenderChk) self.image.clicked.connect(self.onImgUpdate) self.image.lineUpdated.connect(self.onImgUpdate) self.image.linePicked.connect(self.onLinePickedFromImage) self.image.nucleusPicked.connect(self.onNucleusPickedFromImage) self.image.dnaChannel = self.ctrl.dnaSpin.value() self.image.rngChannel = self.ctrl.actSpin.value() self.grph = GraphWidget() self.grphtimer = QTimer() self.grphtimer.setSingleShot(True) # self.stk = StkRingWidget(self.image, linePicked=self.onLinePickedFromStackGraph) self.grph.linePicked.connect(self.onLinePickedFromGraph) # self.stk.linePicked.connect(self.onLinePickedFromStackGraph) self.grphtimer.timeout.connect(self._graph) self.image.dnaChannel = self.ctrl.dnaSpin.value() self.image.rngChannel = self.ctrl.actSpin.value() self.measure_n = 0 self.selectedLine = None self.line_length = 4 self.currMeasurement = None self.currN = None self.currZ = None self.df = pd.DataFrame() self.file = "/Users/Fabio/data/lab/airyscan/nil.czi" self.show() self.grph.show() # self.stk.show() self.ctrl.show() self.move(0, 0) self.resizeEvent(None) self.moveEvent(None) def resizeEvent(self, event): # this is a hack to resize everything when the user resizes the main window self.grph.setFixedWidth(self.width()) self.image.setFixedWidth(self.width()) self.image.setFixedHeight(self.height()) self.image.resizeEvent(None) self.moveEvent(None) def moveEvent(self, QMoveEvent): self.ctrl.move(self.frameGeometry().topRight()) self.grph.move(self.geometry().bottomLeft()) # self.stk.move(self.ctrl.frameGeometry().topRight()) def closeEvent(self, event): self._saveCurrentFileMeasurements() # if not self.df.empty: # self.df.loc[:, "condition"] = self.ctrl.experimentLineEdit.text() # self.df.loc[:, "l"] = self.df.loc[:, "l"].apply(lambda v: np.array2string(v, separator=',')) # self.df.to_csv(os.path.join(os.path.dirname(self.image.file), "ringlines.csv")) self.grph.close() self.ctrl.close() # self.stk.close() def focusInEvent(self, QFocusEvent): logger.debug('focusInEvent') self.ctrl.activateWindow() self.grph.activateWindow() # self.stk.focusInEvent(None) def showEvent(self, event): self.setFocus() def _saveCurrentFileMeasurements(self): if not self.df.empty: fname = os.path.basename(self.image.file) df = self.df[self.df["file"] == fname] df.loc[:, "condition"] = self.ctrl.experimentLineEdit.text() df.loc[:, "l"] = self.df.loc[:, "l"].apply( lambda v: np.array2string(v, separator=',')) df.to_csv( os.path.join(os.path.dirname(self.image.file), f"{fname}.csv")) def _graphTendency(self): df = pd.DataFrame(self.image.measurements).drop( ['x', 'y', 'c', 'ls0', 'ls1', 'd', 'sum'], axis=1) df.loc[:, "xx"] = df.loc[:, "l"].apply(lambda v: np.arange( start=0, stop=len(v) * self.image.dl, step=self.image.dl)) df = m.vector_column_to_long_fmt(df, val_col="l", ix_col="xx") sns.lineplot(x="xx", y="l", data=df, ax=self.grph.ax, color='k', ci="sd", zorder=20) self.grph.ax.set_ylabel('') self.grph.ax.set_xlabel('') self.grph.canvas.draw() def _graph(self, alpha=1.0): self.grph.clear() lines = self.image.lines(self.image.currNucleusId) if lines.empty: return for ix, me in lines.iterrows(): x = np.arange(start=0, stop=len(me['value']) * self.image.dl, step=self.image.dl) lw = 0.1 if me['li'] != self.image.selectedLine else 0.5 self.grph.ax.plot(x, me['value'], linewidth=lw, linestyle='-', color=me['c'], alpha=alpha, zorder=10, picker=5, label=int( me['li'])) # , marker='o', markersize=1) self.grph.format_ax() # self.statusbar.showMessage("ptp: %s" % ["%d " % me['d'] for me in self.image.lines().iterrows()]) self.grph.canvas.draw() @QtCore.pyqtSlot() def onImgToggle(self): logger.debug('onImgToggle') if self.ctrl.dnaChk.isChecked(): self.image.activeCh = "dna" if self.ctrl.actChk.isChecked(): self.image.activeCh = "act" @QtCore.pyqtSlot() def onRenderChk(self): logger.debug('onRenderChk') self.image.renderMeasurements = self.ctrl.renderChk.isChecked() self.stk.renderMeasurements = self.ctrl.renderChk.isChecked() @QtCore.pyqtSlot() def onOpenButton(self): logger.debug('onOpenButton') # save current file measurements as a backup self._saveCurrentFileMeasurements() qfd = QFileDialog() path = os.path.dirname(self.file) if self.image.file is not None: self.statusbar.showMessage("current file: %s" % os.path.basename(self.image.file)) flt = "Tiff files(*.tif *.tiff);;Zeiss(*.czi)" f = QFileDialog.getOpenFileName(qfd, "Open File", path, flt) if len(f) > 0: self._open(f[0]) def _open(self, fname): assert type(fname) is str and len(fname) > 0, "No filename given!" self.file = fname self.image.file = fname self.image.zstack = self.ctrl.zSpin.value() self.image.dnaChannel = self.ctrl.dnaSpin.value() self.ctrl.nchLbl.setText("%d channels" % self.image.nChannels) self.ctrl.nzsLbl.setText("%d z-stacks" % self.image.nZstack) self.ctrl.nfrLbl.setText( "%d %s" % (self.image.nFrames, "frames" if self.image.nFrames > 1 else "frame")) self.currMeasurement = None self.currN = None self.currZ = None # self.stk.close() # self.stk = StkRingWidget(self.image, # nucleus_id=self.image.currNucleusId, # linePicked=self.onLinePickedFromStackGraph, # line_length=self.line_length, # dl=self.image.dl, # lines_to_measure=self.image._nlin # ) # # self.stk.linePicked.connect(self.onLinePickedFromStackGraph) # self.stk.loadImages(self.image.images, xy=(100, 100), wh=(200, 200)) # self.stk.hide() # self.stk.show() self.moveEvent(None) @QtCore.pyqtSlot() def onImgUpdate(self): # logger.debug(f"onImgUpdate") self.ctrl.renderChk.setChecked(True) self.stk.selectedLineId = self.image.selectedLine if self.image.selectedLine is not None else 0 logger.debug( f"onImgUpdate. Selected line is {self.stk.selectedLineId}") # self.stk.drawMeasurements(erase_bkg=True) self.grphtimer.start(1000) @QtCore.pyqtSlot() def onNucleusPickedFromImage(self): logger.debug('onNucleusPickedFromImage') # self.stk.dnaChannel = self.image.dnaChannel # self.stk.rngChannel = self.image.rngChannel # self.stk.selectedLineId = self.image.selectedLine if self.image.selectedLine is not None else 0 # self.stk.selectedNucId = self.image.currNucleusId if self.image.currNucleusId is not None else 0 # # test rectification code # dl = 4 # ndl = 10 # nth = 100 # ppdl = 1 # ppth = 1 # # tsplaprx = TestSplineApproximation(self.image.currNucleus, self.image) # tsplaprx.test_fit() # tsplaprx.plot_grid() # # trct = TestPiecewiseLinearRectification(tsplaprx, # dl=dl, n_dl=ndl, n_theta=nth, pix_per_dl=ppdl, pix_per_theta=ppth) # trct.plot_rectification() # # tfnrect = TestFunctionRectification(tsplaprx, dl=dl, n_dl=ndl, n_theta=nth, pix_per_dl=ppdl, pix_per_theta=ppth) # tfnrect.plot_rectification() # # minx, miny, maxx, maxy = self.image.currNucleus.bounds # r = int(max(maxx - minx, maxy - miny) / 2) # self.stk.loadImages(self.image.images, xy=[n[0] for n in self.image.currNucleus.centroid.xy], # wh=(r * self.image.pix_per_um, r * self.image.pix_per_um)) # self.stk.measure() # self.stk.drawMeasurements(erase_bkg=True) @QtCore.pyqtSlot() def onMeasureButton(self): logger.debug('onMeasureButton') self.image.paint_measures() self._graph(alpha=0.2) self._graphTendency() @QtCore.pyqtSlot() def onZValueChange(self): logger.debug('onZValueChange') self.image.zstack = self.ctrl.zSpin.value() % self.image.nZstack self.ctrl.zSpin.setValue(self.image.zstack) self._graph() @QtCore.pyqtSlot() def onDnaValChange(self): logger.debug('onDnaValChange') val = self.ctrl.dnaSpin.value() % self.image.nChannels self.ctrl.dnaSpin.setValue(val) self.image.dnaChannel = val if self.ctrl.dnaChk.isChecked(): self.image.activeCh = "dna" self.ctrl.dnaChk.setChecked(True) @QtCore.pyqtSlot() def onActValChange(self): logger.debug('onActValChange') val = self.ctrl.actSpin.value() % self.image.nChannels self.ctrl.actSpin.setValue(val) self.image.rngChannel = val if self.ctrl.actChk.isChecked(): self.image.activeCh = "act" self.ctrl.actChk.setChecked(True) @QtCore.pyqtSlot() def onAddButton(self): logger.debug('onAddButton') if self.currMeasurement is not None and self.currN is not None and self.currZ is not None: new = pd.DataFrame(self.currMeasurement) new = new.loc[(new["n"] == self.currN) & (new["z"] == self.currZ)] new.loc[:, "m"] = self.measure_n new.loc[:, "file"] = os.path.basename(self.image.file) # new.loc[:, "x"] = new.loc[:, "l"].apply(lambda v: np.arange(start=0, stop=len(v), step=self.image.dl)) self.df = self.df.append(new, ignore_index=True, sort=False) self.measure_n += 1 self.currMeasurement = None self.currN = None self.currZ = None print(self.df) @QtCore.pyqtSlot() def onPlotButton(self): logger.debug('onPlotButton') if self.image.measurements is None: return import matplotlib.pyplot as plt import seaborn as sns from matplotlib.gridspec import GridSpec plt.style.use('bmh') pal = sns.color_palette("Blues", n_colors=len(self.image.measurements)) fig = plt.figure(figsize=(2, 2 * 4), dpi=300) gs = GridSpec(nrows=2, ncols=1, height_ratios=[4, 0.5]) ax1 = plt.subplot(gs[0, 0]) ax2 = plt.subplot(gs[1, 0]) self.image.drawMeasurements(ax1, pal) lw = 1 for me, c in zip(self.image.measurements, pal): x = np.arange(start=0, stop=len(me['l']) * self.image.dl, step=self.image.dl) ax2.plot(x, me['l'], linewidth=lw, linestyle='-', color=c, alpha=1, zorder=10) ax1.xaxis.set_major_locator(ticker.MultipleLocator(20)) ax1.xaxis.set_minor_locator(ticker.MultipleLocator(10)) ax1.yaxis.set_major_locator(ticker.MultipleLocator(20)) ax1.yaxis.set_minor_locator(ticker.MultipleLocator(10)) ax2.xaxis.set_major_locator(ticker.MultipleLocator(1)) ax2.xaxis.set_minor_locator(ticker.MultipleLocator(0.5)) ax2.yaxis.set_major_locator(ticker.MultipleLocator(1e4)) ax2.yaxis.set_minor_locator(ticker.MultipleLocator(5e3)) ax2.yaxis.set_major_formatter(EngFormatter(unit='')) fig.savefig(os.path.basename(self.image.file) + ".pdf") @QtCore.pyqtSlot() def onLinePickedFromGraph(self): logger.debug('onLinePickedFromGraph') self.selectedLine = self.grph.selectedLine if self.grph.selectedLine is not None else None if self.selectedLine is not None: self.image.selectedLine = self.selectedLine self.currMeasurement = self.image.measurements self.currN = self.selectedLine self.currZ = self.image.zstack # self.stk.selectedLineId = self.image.selectedLine if self.image.selectedLine is not None else 0 # self.stk.selectedNucId = self.image.currNucleusId if self.image.currNucleusId is not None else 0 # self.stk.selectedZ = self.currZ try: self.stk.drawMeasurements(erase_bkg=True) except Exception as e: logger.error(e) self.statusbar.showMessage("line %d selected" % self.selectedLine) @QtCore.pyqtSlot() def onLinePickedFromStackGraph(self): logger.debug('onLinePickedFromStackGraph') self.selectedLine = self.stk.selectedLineId if self.stk.selectedLineId is not None else None if self.selectedLine is not None: self.currN = self.stk.selectedLineId # self.stk.selectedLineId = self.image.selectedLine if self.image.selectedLine is not None else 0 # self.stk.selectedNucId = self.image.currNucleusId if self.image.currNucleusId is not None else 0 self.currZ = self.stk.selectedZ self.statusbar.showMessage( f"Line {self.currN} of z-stack {self.currZ} selected.") logger.info(f"Line {self.currN} of z-stack {self.currZ} selected.") @QtCore.pyqtSlot() def onLinePickedFromImage(self): logger.debug('onLinePickedFromImage') self.selectedLine = self.image.selectedLine if self.image.selectedLine is not None else None if self.selectedLine is not None: self.currN = self.selectedLine self.currZ = self.image.zstack # self.stk.selectedLineId = self.image.selectedLine if self.image.selectedLine is not None else 0 # self.stk.selectedNucId = self.image.currNucleusId if self.image.currNucleusId is not None else 0 # self.stk.selectedZ = self.currZ self.statusbar.showMessage("Line %d selected" % self.selectedLine)
def show(self): # For non-modal dialogs, show() is not enough to bring the window at the forefront, we have # to call raise() as well QWidget.showNormal(self) QWidget.raise_(self) QWidget.activateWindow(self)
class BashActionsDialog(QDialog): def __init__(self): super().__init__() self.mainDialog, self.wid0, self.wid1 = QWidget(), QWidget(), QWidget() self.mainDialog.setWindowModality(Qt.NonModal) self.mainLayout = QVBoxLayout(self.mainDialog) self.box0, self.box1 = QHBoxLayout(self.wid0), QHBoxLayout(self.wid1) QLabel(self.mainDialog).setPixmap( QIcon.fromTheme("applications-graphics").pixmap(64)) self.formLayout = QFormLayout() self.refreshButton = QPushButton("Refresh") self.loadButton = QPushButton("Load") self.mode = QComboBox() self.mode.addItems(("Full", "Simple")) self.widgetDocuments = QListWidget() self.widgetDocuments.setSortingEnabled(True) self.widgetDocuments.setToolTip("Choose 1 or more files") self.widgetDocuments.setSelectionMode(QAbstractItemView.MultiSelection) self.widgetDocuments.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContents) self.bashscript, self.preview = QPlainTextEdit(), QPlainTextEdit() self.bashscript.setPlaceholderText(_MSG0) self.bashscript.setToolTip(_MSG0) self.bashscript.setPlainText(Path(__file__ + ".txt").read_text()) self.preview.setPlaceholderText( "Read-Only Preview of your Bash Commands before execution.") self.preview.setToolTip( "This will run exactly as seen here, line-by-line 1 per line.") self.preview.setReadOnly(True) self.log = QPlainTextEdit() self.log.setPlaceholderText( "Read-Only Log of your Bash Commands after execution.") self.log.setToolTip("Standard output, standard error and extra info.") self.log.setReadOnly(True) self.chrt = QCheckBox("Low CPU priority") self.autoquote = QCheckBox( "Auto add quotes if the filename or path has white spaces") self.asave = QCheckBox("Auto save") self.autoquote.setChecked(True) self.asave.setChecked(True) self.mini = QCheckBox("Minimize during execution") self.qq = QCheckBox("Close after execution") self.repeats, self.delay = QSpinBox(), QSpinBox() self.backoff, self.timeout = QSpinBox(), QSpinBox() self.repeats.setRange(1, 99) self.repeats.setPrefix("Repeat ") self.repeats.setSuffix(" times") self.delay.setRange(0, 99) self.delay.setPrefix("Delay ") self.delay.setSuffix(" seconds") self.backoff.setRange(1, 9) self.backoff.setPrefix("Backoff ") self.backoff.setSuffix(" seconds") self.timeout.setRange(0, 999) self.timeout.setValue(999) self.timeout.setPrefix("Timeout at ") self.timeout.setSuffix(" seconds") self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Close | QDialogButtonBox.Help) # self.misteryButton = QPushButton("?", self.buttonBox) # self.misteryButton.setFlat(True) self.refreshButton.clicked.connect(self.loadDocuments) self.loadButton.clicked.connect(self.load_script) self.buttonBox.accepted.connect(self.confirmButton) self.buttonBox.rejected.connect(self.mainDialog.close) self.buttonBox.helpRequested.connect(lambda: open_new_tab(__url__)) self.mode.currentIndexChanged.connect(self.change_mode) self.bashscript.textChanged.connect(self.update_preview) self.kritaInstance = krita.Krita.instance() self.documentsList, self.selected = [], [] self.process = QProcess(self) # self.process.error.connect( # lambda: self.statusBar().showMessage("Info: Process Killed", 5000)) def change_mode(self, index): for item in (self.preview, self.log, self.wid0, self.wid1, self.autoquote): if index == 1: item.hide() self.formLayout.labelForField(item).hide() self.mainDialog.resize(400, 400) else: item.show() self.formLayout.labelForField(item).show() self.mainDialog.resize(800, 800) def load_script(self): self.bashscript.setPlainText(str(Path(QFileDialog.getOpenFileName( self, "Open 1 Bash Script Template", "Bash_Commands_Template.sh", "Bash Script to use as Template (*.sh);;Plain text files (*.txt)" )[0]).read_text())) def update_preview(self): script = str(self.bashscript.toPlainText()).strip() self.selected = [a.text() for a in self.widgetDocuments.selectedItems()] if not len(script): return # Nothing to do yet. elif not len(self.selected): self.preview.setPlainText(script) # We got script but no files yet. else: preview_script = script for index, selected_file in enumerate(self.selected): index, seleted_file = index + 1, str(selected_file) need_quote = self.autoquote.isChecked() and " " in seleted_file preview_script = preview_script.replace( f"FILE{index}", f'"{seleted_file}"' if need_quote else seleted_file) self.preview.setPlainText(preview_script) def initialize(self): self.loadDocuments() self.formLayout.addRow(QLabel("<center><h1>Bash Actions for Krita")) self.formLayout.addRow("Mode", self.mode) self.formLayout.addRow("Opened image files", self.widgetDocuments) self.formLayout.addRow(" ", self.refreshButton) self.formLayout.addRow("Commands template", self.bashscript) self.formLayout.addRow(" ", self.loadButton) self.formLayout.addRow("Preview", self.preview) self.formLayout.addRow("Log", self.log) self.box1.addWidget(self.repeats) self.box1.addWidget(self.delay) self.box1.addWidget(self.backoff) self.box1.addWidget(self.timeout) self.formLayout.addRow("Execution repeats", self.wid1) self.box0.addWidget(self.asave) self.box0.addWidget(self.chrt) self.box0.addWidget(self.mini) self.box0.addWidget(self.qq) self.formLayout.addRow("Execution details", self.wid0) self.formLayout.addRow("Filename correction", self.autoquote) self.mainLayout.addLayout(self.formLayout) self.mainLayout.addWidget(self.buttonBox) self.mainDialog.resize(800, 800) self.mainDialog.setWindowTitle("Bash Actions") self.mainDialog.show() self.mainDialog.activateWindow() self.bashscript.setFocus() def loadDocuments(self): self.widgetDocuments.clear() self.documentsList = [ document for document in self.kritaInstance.documents() if document.fileName()] for document in self.documentsList: self.widgetDocuments.addItem(document.fileName()) def confirmButton(self): start_time, repeat = datetime.now(), int(self.repeats.value()) end_time = int(time.time() + int(self.timeout.value())) delay, backoff = int(self.delay.value()), int(self.backoff.value()) if self.asave.isChecked(): Path(__file__ + ".txt").write_text(str(self.bashscript.toPlainText())) chrt = which("chrt") if self.chrt.isChecked() else None commands = tuple(str(self.preview.toPlainText()).strip().splitlines()) if not len(commands): return QMessageBox.warning(self, __doc__, "Nothing to execute!.") if self.mini.isChecked() and self.mainDialog.isVisible(): self.mainDialog.hide() self.log.clear() self.log.setPlainText(f"""STARTED: {start_time} by user {getuser()}. LINES OF BASH TO EXECUTE: {len(commands)}.\nTIMEOUT: {end_time} Secs. SELECTED FILES: {len(self.selected)} files.\nDELAY: {delay} Secs. BACKOFF: {backoff} Secs.\nREPEATS: {repeat} Times.\nPRIORITY: {chrt}""") while repeat: self.log.appendPlainText(f"REPETITION: {repeat} loop.") for i, cmd in enumerate(commands): cmd = f"""{chrt} -i 0 {cmd.strip()}""" if chrt else cmd.strip() self.log.appendPlainText(f"{i} EXECUTING: {cmd}.") self.process.start(cmd) self.process.waitForFinished(self.timeout.value()) self.log.appendPlainText( bytes(self.process.readAllStandardError()).decode("utf-8")) self.log.appendPlainText( bytes(self.process.readAllStandardOutput()).decode("utf-8")) sleep(delay) repeat -= 1 delay *= backoff if end_time and time.time() > end_time: self.log.appendPlainText(f"TIMEOUT: {time.time()} > {end_time}") return else: self.log.appendPlainText(f"FINISHED: {datetime.now()}.") if self.mini.isChecked() and not self.mainDialog.isVisible(): self.mainDialog.show() if self.qq.isChecked(): self.mainDialog.close() self.close()